11import './icons/icon-loading' ;
2- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3- //@ts -ignore
42import en from '@emoji-mart/data/i18n/en.json' ;
5- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
6- //@ts -ignore
73import es from '@emoji-mart/data/i18n/es.json' ;
8- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
9- //@ts -ignore
104import zh from '@emoji-mart/data/i18n/zh.json' ;
115import {
126 autoUpdate ,
@@ -31,18 +25,79 @@ const localeMap = {
3125 es : es ,
3226} ;
3327
28+ const sharedEmojiPanel = {
29+ wrapper : null as HTMLDivElement | null ,
30+ picker : null as Picker | null ,
31+ activeButton : null as EmojiButton | null ,
32+
33+ init ( ) {
34+ if ( this . wrapper ) return this . wrapper ;
35+
36+ const styleElement = document . createElement ( 'style' ) ;
37+ styleElement . textContent = `
38+ .form__emoji-panel {
39+ box-shadow: 0 0.5em 1em rgba(0, 0, 0, 0.1);
40+ border-radius: 0.875em;
41+ overflow: hidden;
42+ display: none;
43+ }
44+
45+ .form__emoji-panel.visible {
46+ display: block;
47+ animation: fadeInUp 0.3s both;
48+ }
49+
50+ @keyframes fadeInUp {
51+ from {
52+ opacity: 0;
53+ transform: translate3d(0, 5%, 0);
54+ }
55+ to {
56+ opacity: 1;
57+ transform: translate3d(0, 0, 0);
58+ }
59+ }
60+
61+ em-emoji-picker {
62+ --rgb-color: var(--halo-cw-emoji-picker-rgb-color, 34, 36, 39);
63+ --rgb-accent: var(--halo-cw-emoji-picker-rgb-accent, 34, 102, 237);
64+ --rgb-background: var(--halo-cw-emoji-picker-rgb-background, 255, 255, 255);
65+ --rgb-input: var(--halo-cw-emoji-picker-rgb-input, 255, 255, 255);
66+ --color-border: var(--halo-cw-emoji-picker-color-border, rgba(0, 0, 0, .05));
67+ --color-border-over: var(--halo-cw-emoji-picker-color-border-over, rgba(0, 0, 0, .1));
68+ }
69+ ` ;
70+ document . head . appendChild ( styleElement ) ;
71+
72+ this . wrapper = document . createElement ( 'div' ) ;
73+ this . wrapper . className = 'form__emoji-panel' ;
74+ this . wrapper . style . position = 'absolute' ;
75+ this . wrapper . style . zIndex = '9999' ;
76+ document . body . appendChild ( this . wrapper ) ;
77+
78+ return this . wrapper ;
79+ } ,
80+
81+ show ( ) {
82+ if ( this . wrapper ) {
83+ this . wrapper . classList . add ( 'visible' ) ;
84+ }
85+ } ,
86+
87+ hide ( ) {
88+ if ( this . wrapper ) {
89+ this . wrapper . classList . remove ( 'visible' ) ;
90+ }
91+ } ,
92+ } ;
93+
3494export class EmojiButton extends LitElement {
3595 @state ( )
3696 emojiPickerVisible = false ;
3797
3898 @state ( )
3999 emojiLoading = false ;
40100
41- @state ( )
42- emojiPicker : Picker | null = null ;
43-
44- emojiPickerWrapperRef : Ref < HTMLDivElement > = createRef < HTMLDivElement > ( ) ;
45-
46101 buttonRef : Ref < HTMLDivElement > = createRef < HTMLDivElement > ( ) ;
47102
48103 cleanupAutoUpdate : ( ( ) => void ) | null = null ;
@@ -58,11 +113,16 @@ export class EmojiButton extends LitElement {
58113 override connectedCallback ( ) {
59114 super . connectedCallback ( ) ;
60115 document . addEventListener ( 'click' , this . handleClickOutside , true ) ;
116+ sharedEmojiPanel . init ( ) ;
61117 }
62118
63119 override disconnectedCallback ( ) {
64120 document . removeEventListener ( 'click' , this . handleClickOutside , true ) ;
65121 this . cleanupFloating ( ) ;
122+ if ( sharedEmojiPanel . activeButton === this ) {
123+ sharedEmojiPanel . activeButton = null ;
124+ sharedEmojiPanel . hide ( ) ;
125+ }
66126 super . disconnectedCallback ( ) ;
67127 }
68128
@@ -74,8 +134,22 @@ export class EmojiButton extends LitElement {
74134 }
75135
76136 handleClickOutside ( event : Event ) {
77- if ( this . emojiPickerVisible && ! event . composedPath ( ) . includes ( this ) ) {
78- this . emojiPickerVisible = false ;
137+ if (
138+ this . emojiPickerVisible &&
139+ ! event . composedPath ( ) . includes ( this ) &&
140+ sharedEmojiPanel . wrapper &&
141+ ! event . composedPath ( ) . includes ( sharedEmojiPanel . wrapper )
142+ ) {
143+ this . closeEmojiPicker ( ) ;
144+ }
145+ }
146+
147+ closeEmojiPicker ( ) {
148+ this . emojiPickerVisible = false ;
149+ this . cleanupFloating ( ) ;
150+ sharedEmojiPanel . hide ( ) ;
151+ if ( sharedEmojiPanel . activeButton === this ) {
152+ sharedEmojiPanel . activeButton = null ;
79153 }
80154 }
81155
@@ -84,58 +158,65 @@ export class EmojiButton extends LitElement {
84158 e . preventDefault ( ) ;
85159
86160 if ( this . emojiPickerVisible ) {
87- this . emojiPickerVisible = false ;
88- this . cleanupFloating ( ) ;
161+ this . closeEmojiPicker ( ) ;
89162 return ;
90163 }
91164
92- if ( this . emojiPickerWrapperRef . value ?. children . length ) {
165+ if (
166+ sharedEmojiPanel . activeButton &&
167+ sharedEmojiPanel . activeButton !== this
168+ ) {
169+ sharedEmojiPanel . activeButton . closeEmojiPicker ( ) ;
170+ }
171+
172+ sharedEmojiPanel . activeButton = this ;
173+
174+ if ( sharedEmojiPanel . wrapper ?. children . length ) {
93175 this . emojiPickerVisible = true ;
176+ sharedEmojiPanel . show ( ) ;
94177 this . setupFloating ( ) ;
95178 return ;
96179 }
97180
98181 this . emojiLoading = true ;
99182
100- const { Picker } = await import (
101- /* webpackChunkName: "emoji-mart" */ 'emoji-mart'
102- ) ;
183+ const { Picker } = await import ( 'emoji-mart' ) ;
184+ const { default : data } = await import ( '@emoji-mart/data' ) ;
103185
104- const { default : data } = await import (
105- /* webpackChunkName: "emoji-mart-data" */ '@emoji-mart/data'
106- ) ;
107-
108- const emojiPicker = new Picker ( {
186+ sharedEmojiPanel . picker = new Picker ( {
109187 data,
110188 onEmojiSelect : ( { native } : { native : string } ) => {
111- this . dispatchEvent (
112- new CustomEvent ( 'emoji-select' , { detail : { native } } )
113- ) ;
189+ const activeButton = sharedEmojiPanel . activeButton ;
190+ if ( activeButton ) {
191+ activeButton . dispatchEvent (
192+ new CustomEvent ( 'emoji-select' , { detail : { native } } )
193+ ) ;
194+ activeButton . closeEmojiPicker ( ) ;
195+ }
114196 } ,
115197 i18n : localeMap [ getLocale ( ) ] ,
116198 } ) ;
117199
118- this . emojiPickerWrapperRef . value ?. appendChild (
119- emojiPicker as unknown as Node
120- ) ;
200+ if ( sharedEmojiPanel . wrapper ) {
201+ sharedEmojiPanel . wrapper . appendChild (
202+ sharedEmojiPanel . picker as unknown as Node
203+ ) ;
204+ sharedEmojiPanel . show ( ) ;
205+ }
121206
122207 this . emojiPickerVisible = true ;
123208 this . emojiLoading = false ;
124209 this . setupFloating ( ) ;
125210 }
126211
127212 setupFloating ( ) {
128- if ( ! this . buttonRef . value || ! this . emojiPickerWrapperRef . value ) return ;
213+ if ( ! this . buttonRef . value || ! sharedEmojiPanel . wrapper ) return ;
129214
130215 this . cleanupFloating ( ) ;
131216
132- if ( this . emojiPickerWrapperRef . value ) {
133- this . emojiPickerWrapperRef . value . style . zIndex = '1000' ;
134- }
135-
136217 this . cleanupAutoUpdate = autoUpdate (
137218 this . buttonRef . value ,
138- this . emojiPickerWrapperRef . value ,
219+ sharedEmojiPanel . wrapper ,
139220 ( ) => this . updatePosition ( )
140221 ) ;
141222
@@ -144,7 +225,7 @@ export class EmojiButton extends LitElement {
144225
145226 async updatePosition ( ) {
146227 const reference = this . buttonRef . value ;
147- const floating = this . emojiPickerWrapperRef . value ;
228+ const floating = sharedEmojiPanel . wrapper ;
148229
149230 if ( ! reference || ! floating ) return ;
150231
@@ -182,12 +263,7 @@ export class EmojiButton extends LitElement {
182263 @click = ${ this . handleOpenEmojiPicker }
183264 > </ div> `
184265 }
185- </ div>
186- <div
187- class= "form__emoji-panel"
188- ?hidden= ${ ! this . emojiPickerVisible }
189- ${ ref ( this . emojiPickerWrapperRef ) }
190- > </ div> ` ;
266+ </ div> ` ;
191267 }
192268
193269 static override styles = [
@@ -196,36 +272,6 @@ export class EmojiButton extends LitElement {
196272 : host {
197273 display : inline-flex;
198274 }
199-
200- em-emoji-picker {
201- --rgb-color : var (--halo-cw-emoji-picker-rgb-color , 34 , 36 , 39 );
202- --rgb-accent : var (--halo-cw-emoji-picker-rgb-accent , 34 , 102 , 237 );
203- --rgb-background : var (--halo-cw-emoji-picker-rgb-background , 255 , 255 , 255 );
204- --rgb-input : var (--halo-cw-emoji-picker-rgb-input , 255 , 255 , 255 );
205- --color-border : var (--halo-cw-emoji-picker-color-border , rgba (0 , 0 , 0 , .05 ));
206- --color-border-over : var (--halo-cw-emoji-picker-color-border-over , rgba (0 , 0 , 0 , .1 ));
207- }
208-
209- .form__emoji-panel {
210- position : absolute;
211- box-shadow : 0 0.5em 1em rgba (0 , 0 , 0 , 0.1 );
212- border-radius : 0.875em ;
213- overflow : hidden;
214- animation : fadeInUp 0.3s both;
215- z-index : 1000 ;
216- }
217-
218- @keyframes fadeInUp {
219- from {
220- opacity : 0 ;
221- transform : translate3d (0 , 5% , 0 );
222- }
223-
224- to {
225- opacity : 1 ;
226- transform : translate3d (0 , 0 , 0 );
227- }
228- }
229275 @unocss-placeholder ;
230276 ` ,
231277 ] ;
0 commit comments