11import EmojiMartPickerImport from '@emoji-mart/react' ;
22import { type ComponentType , useState } from 'react' ;
33import { Button } from 'stream-chat-react' ;
4- import { EmojiPickerPanel , type EmojiSelection } from 'stream-chat-react/emojis' ;
4+ import {
5+ EmojiPickerPanel ,
6+ type EmojiSelection ,
7+ StreamEmojiPicker ,
8+ useEmojiPickerContext ,
9+ } from 'stream-chat-react/emojis' ;
510import {
611 appSettingsStore ,
712 DEFAULT_EMOJI_PICKER_SETTINGS ,
@@ -62,17 +67,62 @@ function Field<T extends string | number | boolean>({
6267 ) ;
6368}
6469
70+ /**
71+ * A custom "paged" grid slot — renders only the active category (a layout the default
72+ * scrolling grid can't do), driven entirely by context: it reads `activeCategoryId`
73+ * (which the built-in Nav sets) and reports selection via `selectEmoji`. Demonstrates
74+ * that a replacement slot only needs the SDK's data + report-back APIs.
75+ */
76+ const PagedGrid = ( ) => {
77+ const {
78+ activeCategoryId,
79+ categories,
80+ isSearching,
81+ resolveNative,
82+ searchResults,
83+ selectEmoji,
84+ } = useEmojiPickerContext ( ) ;
85+ const emojis = isSearching
86+ ? ( searchResults ?? [ ] )
87+ : ( ( categories . find ( ( category ) => category . id === activeCategoryId ) ?? categories [ 0 ] )
88+ ?. emojis ?? [ ] ) ;
89+
90+ return (
91+ < div className = 'str-chat__emoji-picker__body' >
92+ < div className = 'str-chat__emoji-picker__grid' >
93+ < div className = 'str-chat__emoji-picker__category-emojis' >
94+ { emojis . map ( ( emoji ) => (
95+ < button
96+ className = 'str-chat__emoji-picker__emoji'
97+ key = { emoji . id }
98+ onClick = { ( ) => selectEmoji ( emoji ) }
99+ type = 'button'
100+ >
101+ { resolveNative ( emoji ) }
102+ </ button >
103+ ) ) }
104+ </ div >
105+ </ div >
106+ </ div >
107+ ) ;
108+ } ;
109+
65110/**
66111 * Always-open picker wired to the current settings, so tweaks show instantly without
67112 * opening the composer. Skin tone and frequently-used are local to the preview —
68- * selecting an emoji here feeds the "frequently used" row.
113+ * selecting an emoji here feeds the "frequently used" row. The `stream-composed` engine
114+ * shows the same picker assembled from `StreamEmojiPicker.Root` + slots, swapping in the
115+ * custom `PagedGrid` while keeping the built-in nav/search/preview/skin-tone.
69116 */
70117const EmojiPickerPreview = ( { options } : { options : EmojiPickerSettingsState } ) => {
71118 const { mode } = useAppSettingsSelector ( ( state ) => state . theme ) ;
72119 const { autoFocus, engine } = options ;
73120 const [ skinTone , setSkinTone ] = useState ( 0 ) ;
74121 const [ frequentlyUsedIds , setFrequentlyUsedIds ] = useState < string [ ] > ( [ ] ) ;
75122
123+ const recordUse = ( emoji : EmojiSelection ) =>
124+ setFrequentlyUsedIds ( ( ids ) => [ emoji . id , ...ids . filter ( ( id ) => id !== emoji . id ) ] ) ;
125+
76126 // The deprecated emoji-mart picker renders inline too and honors the same option
77127 // names, so the shared controls drive both engines.
78128 if ( engine === 'emoji-mart' ) {
@@ -86,13 +136,30 @@ const EmojiPickerPreview = ({ options }: { options: EmojiPickerSettingsState })
86136 ) ;
87137 }
88138
139+ if ( engine === 'stream-composed' ) {
140+ return (
141+ < StreamEmojiPicker . Root
142+ frequentlyUsedIds = { frequentlyUsedIds }
143+ onEmojiSelect = { recordUse }
144+ onSkinToneChange = { setSkinTone }
145+ skinToneIndex = { skinTone }
146+ >
147+ < StreamEmojiPicker . Nav />
148+ < StreamEmojiPicker . Search autoFocus = { autoFocus } />
149+ < PagedGrid />
150+ < div className = 'str-chat__emoji-picker__footer' >
151+ < StreamEmojiPicker . Preview />
152+ < StreamEmojiPicker . SkinTone />
153+ </ div >
154+ </ StreamEmojiPicker . Root >
155+ ) ;
156+ }
157+
89158 return (
90159 < EmojiPickerPanel
91160 autoFocus = { autoFocus }
92161 frequentlyUsedIds = { frequentlyUsedIds }
93- onEmojiSelect = { ( emoji : EmojiSelection ) =>
94- setFrequentlyUsedIds ( ( ids ) => [ emoji . id , ...ids . filter ( ( id ) => id !== emoji . id ) ] )
95- }
162+ onEmojiSelect = { recordUse }
96163 onSkinToneChange = { setSkinTone }
97164 skinToneIndex = { skinTone }
98165 />
@@ -139,6 +206,7 @@ export const EmojiPickerTab = ({ close }: EmojiPickerTabProps) => {
139206 onSelect = { ( engine ) => update ( { engine } ) }
140207 options = { [
141208 { label : 'Stream (native)' , value : 'stream' } ,
209+ { label : 'Stream (composed)' , value : 'stream-composed' } ,
142210 { label : 'emoji-mart (deprecated)' , value : 'emoji-mart' } ,
143211 ] }
144212 value = { emojiPicker . engine }
0 commit comments