22
33import { useTheme } from '@ultraviolet/themes'
44import type {
5+ ChangeEvent ,
56 ComponentProps ,
67 Dispatch ,
78 KeyboardEvent ,
9+ MouseEvent ,
810 ReactNode ,
911 RefObject ,
1012 SetStateAction ,
1113} from 'react'
1214import {
15+ use ,
1316 useCallback ,
14- useContext ,
1517 useEffect ,
18+ useLayoutEffect ,
1619 useMemo ,
1720 useRef ,
1821 useState ,
@@ -219,13 +222,26 @@ const CreateDropdown = ({
219222 )
220223 }
221224
222- const handleClick = ( clickedOption : OptionType , group ?: string ) => {
225+ const handleClick = ( {
226+ clickedOption,
227+ group,
228+ event,
229+ } : {
230+ clickedOption : OptionType
231+ group ?: string
232+ event :
233+ | MouseEvent < HTMLDivElement >
234+ | KeyboardEvent < HTMLDivElement >
235+ | ChangeEvent < HTMLDivElement >
236+ } ) => {
237+ event . stopPropagation ( )
238+
223239 setSelectedData ( { clickedOption, group, type : 'selectOption' } )
224240 if ( multiselect ) {
225241 if ( selectedData . selectedValues . includes ( clickedOption . value ) ) {
226242 onChange ?.(
227243 selectedData . selectedValues . filter (
228- val => val !== clickedOption . value ,
244+ value => value !== clickedOption . value ,
229245 ) ,
230246 )
231247 } else {
@@ -420,16 +436,25 @@ const CreateDropdown = ({
420436 data-testid = { `option-${ option . value } ` }
421437 id = { `option-${ indexOption } ` }
422438 key = { option . value }
423- onClick = { ( ) => {
439+ onClick = { event => {
424440 if ( ! option . disabled ) {
425- handleClick ( option , group )
441+ handleClick ( {
442+ clickedOption : option ,
443+ event,
444+ group,
445+ } )
446+ }
447+ } }
448+ onKeyDown = { event => {
449+ const shouldClick = [ ' ' , 'Enter' ] . includes ( event . key )
450+ if ( shouldClick ) {
451+ handleClick ( {
452+ clickedOption : option ,
453+ event,
454+ group,
455+ } )
426456 }
427457 } }
428- onKeyDown = { event =>
429- [ ' ' , 'Enter' ] . includes ( event . key )
430- ? handleClick ( option , group )
431- : null
432- }
433458 ref = {
434459 option . value === defaultSearchValue ||
435460 option . searchText === defaultSearchValue
@@ -447,9 +472,13 @@ const CreateDropdown = ({
447472 }
448473 className = { dropdownCheckbox }
449474 disabled = { option . disabled }
450- onChange = { ( ) => {
475+ onChange = { event => {
451476 if ( ! option . disabled ) {
452- handleClick ( option , group )
477+ handleClick ( {
478+ clickedOption : option ,
479+ event,
480+ group,
481+ } )
453482 }
454483 } }
455484 tabIndex = { - 1 }
@@ -548,14 +577,23 @@ const CreateDropdown = ({
548577 data-testid = { `option-${ option . value } ` }
549578 id = { `option-${ index } ` }
550579 key = { option . value }
551- onClick = { ( ) => {
580+ onClick = { event => {
552581 if ( ! option . disabled ) {
553- handleClick ( option )
582+ handleClick ( {
583+ clickedOption : option ,
584+ event,
585+ } )
586+ }
587+ } }
588+ onKeyDown = { event => {
589+ const shouldClick = [ ' ' , 'Enter' ] . includes ( event . key )
590+ if ( shouldClick ) {
591+ handleClick ( {
592+ clickedOption : option ,
593+ event,
594+ } )
554595 }
555596 } }
556- onKeyDown = { event =>
557- [ ' ' , 'Enter' ] . includes ( event . key ) ? handleClick ( option ) : null
558- }
559597 ref = {
560598 option . value === defaultSearchValue ||
561599 option . searchText === defaultSearchValue
@@ -573,9 +611,12 @@ const CreateDropdown = ({
573611 }
574612 className = { dropdownCheckbox }
575613 disabled = { option . disabled }
576- onChange = { ( ) => {
614+ onChange = { event => {
577615 if ( ! option . disabled ) {
578- handleClick ( option )
616+ handleClick ( {
617+ clickedOption : option ,
618+ event,
619+ } )
579620 }
580621 } }
581622 tabIndex = { - 1 }
@@ -637,35 +678,41 @@ export const Dropdown = ({
637678 const [ maxWidth , setWidth ] = useState < string | number > (
638679 refSelect . current ?. offsetWidth ?? '100%' ,
639680 )
640- const modalContext = useContext ( ModalContext )
681+ const modalContext = use ( ModalContext )
641682
642- useEffect ( ( ) => {
683+ useLayoutEffect ( ( ) => {
643684 if ( refSelect . current && isDropdownVisible ) {
644685 const position =
645686 refSelect . current . getBoundingClientRect ( ) . bottom +
646687 DROPDOWN_MAX_HEIGHT +
647688 Number ( theme . sizing [ INPUT_SIZE_HEIGHT [ size ] ] . replace ( 'rem' , '' ) ) * 16 +
648689 Number . parseInt ( theme . space [ '5' ] , 10 )
649690 const overflow = position - window . innerHeight + 32
691+
650692 if ( overflow > 0 && modalContext ) {
651693 const currentModal = modalContext . openedModals [ 0 ]
652694 const modalElement = currentModal ?. ref . current
653695
654696 if ( modalElement ) {
655- const parentElement = modalElement . parentNode as HTMLElement
656- if ( parentElement ) {
697+ const parentElement = modalElement . parentNode
698+
699+ if ( parentElement instanceof HTMLElement ) {
657700 parentElement . scrollBy ( {
658701 behavior : 'smooth' ,
659702 top : overflow ,
660703 } )
704+ } else {
705+ modalElement . scrollBy ( {
706+ behavior : 'smooth' ,
707+ top : overflow ,
708+ } )
661709 }
662710 } else {
663711 window . scrollBy ( { behavior : 'smooth' , top : overflow } )
664712 }
665713 }
666714 }
667- // oxlint-disable react/exhaustive-deps
668- } , [ isDropdownVisible , refSelect , size , ref . current ] )
715+ } , [ isDropdownVisible , refSelect , size , modalContext , theme ] )
669716
670717 const resizeDropdown = useCallback ( ( ) => {
671718 if (
@@ -696,33 +743,24 @@ export const Dropdown = ({
696743 setSearch ( '' )
697744 }
698745
699- if ( ! searchable ) {
700- document . addEventListener ( 'keydown' , event =>
701- handleKeyDown (
702- event ,
703- ref ,
704- options ,
705- searchBarActive ,
706- setSearch ,
707- setDefaultSearch ,
708- search ,
709- ) ,
746+ const eventKeydown = ( event : globalThis . KeyboardEvent ) =>
747+ handleKeyDown (
748+ event ,
749+ ref ,
750+ options ,
751+ searchBarActive ,
752+ setSearch ,
753+ setDefaultSearch ,
754+ search ,
710755 )
756+
757+ if ( ! searchable ) {
758+ document . addEventListener ( 'keydown' , eventKeydown )
711759 }
712760
713761 return ( ) => {
714762 if ( ! searchable ) {
715- document . removeEventListener ( 'keydown' , event =>
716- handleKeyDown (
717- event ,
718- ref ,
719- options ,
720- searchBarActive ,
721- setSearch ,
722- setDefaultSearch ,
723- search ,
724- ) ,
725- )
763+ document . removeEventListener ( 'keydown' , eventKeydown )
726764 }
727765 }
728766 } , [
0 commit comments