1- import React , { useEffect , useRef , useState } from 'react' ;
1+ import React , { useEffect , useRef , useState , useCallback } from 'react' ;
22import classNames from 'classnames' ;
33import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
44import { Button , ThemeExport } from '@deephaven/components' ;
@@ -29,11 +29,15 @@ export default function VisibilityOrderingGroup(
2929 const groupRef = useRef ( group ) ;
3030 const nameInputRef = useRef < HTMLInputElement > ( null ) ;
3131 const colorInputRef = useRef < HTMLInputElement > ( null ) ;
32+ const [ isColorInputOpen , setIsColorInputOpen ] = useState ( false ) ;
3233 const [ name , setName ] = useState ( isNew ? '' : group . name ) ;
3334 const [ isEditing , setIsEditing ] = useState ( isNew ) ;
34- const [ hasTyped , setHasTyped ] = useState ( false ) ;
35+ const [ shouldValidate , setShouldValidate ] = useState ( false ) ;
3536 const nameValidationError = name !== group . name ? validateName ( name ) : '' ;
36- const isValid = ( isNew && ! hasTyped ) || nameValidationError === '' ;
37+ const isValid = ( isNew && ! shouldValidate ) || nameValidationError === '' ;
38+ const colorInputBlurHandler = useCallback ( ( ) => {
39+ setIsColorInputOpen ( false ) ;
40+ } , [ ] ) ;
3741
3842 useEffect (
3943 function focusEditInput ( ) {
@@ -59,6 +63,35 @@ export default function VisibilityOrderingGroup(
5963 [ onDelete ]
6064 ) ;
6165
66+ useEffect (
67+ function openColorInput ( ) {
68+ if ( isColorInputOpen ) {
69+ colorInputRef . current ?. click ( ) ;
70+ // Mostly for testing. Chrome seems to not give the hidden input focus
71+ // Really would only affect screen readers
72+ colorInputRef . current ?. focus ( ) ;
73+
74+ /**
75+ * Adding this event handler is for Firefox on Mac
76+ * There seems to be buggy behavior when multiple color inputs are on the same page
77+ * Clicking between the inputs without closing the previous causes a bad state
78+ * The user gets to a point where they can't open most of the pickers
79+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1618418
80+ * https://bugzilla.mozilla.org/show_bug.cgi?id=975468
81+ * Instead, we remove the color input when any focus is returned to the window
82+ * This causes Firefox on Mac to mostly operate correctly
83+ * Firefox seems to ignore the first click back into the window and emit no event
84+ * So opening a color picker when another is open requires 2 clicks in Firefox
85+ */
86+ window . addEventListener ( 'click' , colorInputBlurHandler , true ) ;
87+ }
88+
89+ return ( ) =>
90+ window . removeEventListener ( 'click' , colorInputBlurHandler , true ) ;
91+ } ,
92+ [ isColorInputOpen , colorInputBlurHandler ]
93+ ) ;
94+
6295 const handleConfirm = ( ) => {
6396 if ( isValid ) {
6497 onNameChange ( group , name ) ;
@@ -76,7 +109,7 @@ export default function VisibilityOrderingGroup(
76109 } ;
77110
78111 const handleEditKeyDown = ( e : React . KeyboardEvent ) : void => {
79- setHasTyped ( true ) ;
112+ setShouldValidate ( true ) ;
80113 if ( e . key === 'Enter' ) {
81114 e . stopPropagation ( ) ;
82115 handleConfirm ( ) ;
@@ -104,6 +137,7 @@ export default function VisibilityOrderingGroup(
104137 placeholder = "Group Name"
105138 onChange = { e => setName ( e . target . value ) }
106139 onKeyDown = { handleEditKeyDown }
140+ onBlur = { ( ) => setShouldValidate ( true ) }
107141 />
108142 < Button
109143 kind = "ghost"
@@ -171,43 +205,47 @@ export default function VisibilityOrderingGroup(
171205 )
172206 }
173207 tooltip = "Set color"
174- onClick = { ( ) => {
175- colorInputRef . current ?. click ( ) ;
176- // Mostly for testing. Chrome seems to not give the hidden input focus
177- // Really would only affect screen readers
178- colorInputRef . current ?. focus ( ) ;
179- } }
208+ /**
209+ * Toggle to close the picker on Chrome
210+ * Prevents Firefox on Mac from getting into a stuck state
211+ * Does not close on Firefox b/c the picker stays open when the element is removed
212+ */
213+ onClick = { ( ) => setIsColorInputOpen ( val => ! val ) }
180214 />
181- < input
182- aria-label = "Color input"
183- ref = { colorInputRef }
184- type = "color"
185- list = "presetColors"
186- value = { group . color ?? ThemeExport [ 'content-bg' ] }
187- style = { {
188- visibility : 'hidden' ,
189- width : 0 ,
190- height : 0 ,
191- padding : 0 ,
192- border : 0 ,
193- } }
194- onChange = { e => {
195- group . color = e . target . value ;
196- onColorChange ( group , e . target . value ) ;
197- } }
198- />
199- < datalist id = "presetColors" >
200- < option > { ThemeExport [ 'content-bg' ] } </ option >
201- < option > { ThemeExport . primary } </ option >
202- < option > { ThemeExport . foreground } </ option >
203- < option > { ThemeExport . green } </ option >
204- < option > { ThemeExport . yellow } </ option >
205- < option > { ThemeExport . orange } </ option >
206- < option > { ThemeExport . red } </ option >
207- < option > { ThemeExport . purple } </ option >
208- < option > { ThemeExport . blue } </ option >
209- < option > { ThemeExport [ 'gray-400' ] } </ option >
210- </ datalist >
215+ { isColorInputOpen && (
216+ < >
217+ < input
218+ aria-label = "Color input"
219+ ref = { colorInputRef }
220+ type = "color"
221+ list = "presetColors"
222+ value = { group . color ?? ThemeExport [ 'content-bg' ] }
223+ style = { {
224+ visibility : 'hidden' ,
225+ width : 0 ,
226+ height : 0 ,
227+ padding : 0 ,
228+ border : 0 ,
229+ } }
230+ onChange = { e => {
231+ group . color = e . target . value ;
232+ onColorChange ( group , e . target . value ) ;
233+ } }
234+ />
235+ < datalist id = "presetColors" >
236+ < option > { ThemeExport [ 'content-bg' ] } </ option >
237+ < option > { ThemeExport . primary } </ option >
238+ < option > { ThemeExport . foreground } </ option >
239+ < option > { ThemeExport . green } </ option >
240+ < option > { ThemeExport . yellow } </ option >
241+ < option > { ThemeExport . orange } </ option >
242+ < option > { ThemeExport . red } </ option >
243+ < option > { ThemeExport . purple } </ option >
244+ < option > { ThemeExport . blue } </ option >
245+ < option > { ThemeExport [ 'gray-400' ] } </ option >
246+ </ datalist >
247+ </ >
248+ ) }
211249 </ span >
212250 </ div >
213251 ) ;
0 commit comments