@@ -30,7 +30,9 @@ export type DropdownData = { label?: string, value: string };
3030
3131export type DropdownStates = {
3232 //the current selected option/s
33- active : boolean
33+ selected : boolean ,
34+ //ie. for dropdown or for the control
35+ target : string
3436} ;
3537
3638export type DropdownContextProps = {
@@ -44,12 +46,12 @@ export type DropdownContextProps = {
4446 option ?: CallableSlotStyleProp < DropdownStates > ,
4547 //toggle handler for dropdown
4648 open : ( show : boolean ) => void ,
47- //options count
48- options : number ,
4949 //select handler
50- select : ( option : string ) => void ,
50+ select : ( option : string , close ?: boolean ) => void ,
5151 //the current selected option/s
52- selected : string [ ]
52+ selected : string [ ] ,
53+ //ie. for dropdown or for the control
54+ target : string
5355} ;
5456
5557export type DropdownConfig = {
@@ -76,6 +78,8 @@ export type DropdownConfig = {
7678 value ?: string | string [ ]
7779} ;
7880
81+ export type DropdownOptionProp = string [ ] | DropdownData [ ] | Record < string , string > ;
82+
7983export type DropdownOptionProps = CallableClassStyleProps < DropdownStates >
8084 & CallableChildrenProps < DropdownStates >
8185 & { value : string } ;
@@ -101,7 +105,7 @@ export type DropdownProps = ClassStyleProps & ChildrenProps & DropdownConfig & {
101105 //slot: style to apply to the select control
102106 option ?: CallableSlotStyleProp < DropdownStates > ,
103107 //serialized list of options as array or object
104- options ?: DropdownData [ ] | Record < string , string >
108+ options ?: DropdownOptionProp
105109} ;
106110
107111//--------------------------------------------------------------------//
@@ -112,7 +116,7 @@ export type DropdownProps = ClassStyleProps & ChildrenProps & DropdownConfig & {
112116 */
113117export function buildOptions (
114118 children ?: ReactNode ,
115- data ?: DropdownData [ ] | Record < string , string > ,
119+ data ?: DropdownOptionProp ,
116120 selected : string [ ] = [ ] ,
117121 multiple ?: boolean
118122) {
@@ -189,10 +193,17 @@ export function getComponent(
189193 for ( const child of nodes ) {
190194 //skip null/undefined child
191195 if ( ! child ) continue ;
192- //if child is a DropdownHead
193- if ( child . type === component || child . props ?. [ propName ] ) {
196+ //if child is a [DropdownHead]
197+ if ( child . type === component
198+ || child . props ?. [ propName ]
199+ || child [ propName ]
200+ ) {
194201 return child ;
195202 }
203+ if ( Array . isArray ( child ) ) {
204+ const nested = getComponent ( component , propName , child ) ;
205+ if ( nested ) return nested ;
206+ }
196207 }
197208 return null ;
198209} ;
@@ -202,7 +213,7 @@ export function getComponent(
202213 */
203214export function getComponents (
204215 children ?: ReactNode ,
205- data ?: DropdownData [ ] | Record < string , string > ,
216+ data ?: DropdownOptionProp ,
206217 selected : string [ ] = [ ] ,
207218 multiple ?: boolean
208219) {
@@ -219,7 +230,7 @@ export function getComponents(
219230export function getControl ( children ?: ReactNode ) {
220231 return getComponent (
221232 DropdownControl ,
222- 'selectDropdownControl ' ,
233+ 'dropdownControl ' ,
223234 children
224235 ) ;
225236} ;
@@ -230,7 +241,7 @@ export function getControl(children?: ReactNode) {
230241export function getFooter ( children ?: ReactNode ) {
231242 return getComponent (
232243 DropdownFoot ,
233- 'selectDropdownFoot ' ,
244+ 'dropdownFoot ' ,
234245 children
235246 ) ;
236247} ;
@@ -241,7 +252,7 @@ export function getFooter(children?: ReactNode) {
241252export function getHeader ( children ?: ReactNode ) {
242253 return getComponent (
243254 DropdownHead ,
244- 'selectDropdownHead ' ,
255+ 'dropdownHead ' ,
245256 children
246257 ) ;
247258} ;
@@ -259,6 +270,7 @@ export function getOptions<T = unknown>(
259270 : children ;
260271 const options : ReactNode [ ] = [ ] ;
261272 const selected : ReactNode [ ] = [ ] ;
273+ const unselected : ReactNode [ ] = [ ] ;
262274 const others : ReactNode [ ] = [ ] ;
263275 for ( const child of nodes ) {
264276 //skip null/undefined child
@@ -271,38 +283,42 @@ export function getOptions<T = unknown>(
271283 const nested = getOptions ( child , values , multiple ) ;
272284 options . push ( ...nested . options ) ;
273285 selected . push ( ...nested . selected ) ;
286+ unselected . push ( ...nested . unselected ) ;
274287 others . push ( ...nested . others ) ;
275288 continue ;
276289 }
277290 //if child is a DropdownOption
278- if ( child . type === DropdownOption || child . props ?. selectOption ) {
291+ if ( child . type === DropdownOption
292+ || child . props ?. dropdownOption
293+ || child . dropdownOption
294+ ) {
295+ //either way, add to options list
296+ options . push ( child ) ;
279297 //if multiple selections allowed
280298 if ( multiple ) {
281299 //check if option is selected
282- if ( values . includes ( child . props . value ) ) {
283- //add to selected
284- selected . push ( child ) ;
285- } else {
286- //add to options
287- options . push ( child ) ;
288- }
300+ values . includes ( child . props . value )
301+ //add to selected if so
302+ ? selected . push ( child )
303+ //add to unselected otherwise
304+ : unselected . push ( child ) ;
289305 //single selection
290306 } else if ( values . includes ( child . props . value ) ) {
291- //only add first match
292- if ( selected . length === 0 ) {
293- //add to selected
294- selected . push ( child ) ;
295- }
296- //dont add to options
307+ //if no selection yet
308+ selected . length === 0
309+ //add to selected if so (only first match)
310+ ? selected . push ( child )
311+ //add to unselected otherwise
312+ : unselected . push ( child ) ;
297313 } else {
298- //add to options
299- options . push ( child ) ;
314+ //add to unselected
315+ unselected . push ( child ) ;
300316 }
301317 continue ;
302318 }
303319 others . push ( child ) ;
304320 }
305- return { options, selected, others } ;
321+ return { options, selected, unselected , others } ;
306322} ;
307323
308324/**
@@ -350,7 +366,7 @@ export function getRelativePosition(
350366 * Make options from data (array or object)
351367 */
352368export function makeOptions (
353- data : DropdownData [ ] | Record < string , string > ,
369+ data : DropdownOptionProp ,
354370 selected : string [ ] ,
355371 multiple ?: boolean
356372) {
@@ -367,7 +383,10 @@ export function makeOptions(
367383 const style = { padding : '2px 8px' } ;
368384 const options : ReactNode [ ] = [ ] ;
369385 for ( let i = 0 ; i < data . length ; i ++ ) {
370- const { label, value } = data [ i ] ;
386+ const option = data [ i ] ;
387+ const { label, value } = typeof option === 'string'
388+ ? { label : option , value : option }
389+ : option ;
371390 options . push (
372391 < DropdownOption key = { i } value = { value } style = { style } >
373392 { label || value }
@@ -432,7 +451,7 @@ export function useDropdown(config: DropdownConfig) {
432451 // whether the dropdown is open
433452 const [ opened , open ] = useState ( false ) ;
434453 // the current selected option/s
435- const [ selected , setDropdowned ] = useState ( defaultValues ) ;
454+ const [ selected , setSelected ] = useState ( defaultValues ) ;
436455 // position of the dropdown (applicable for both relative and absolute dropdowns)
437456 const [ position , setPosition ] = useState < [ number , number ] > ( [ 0 , 0 ] ) ;
438457 // max width of the dropdown (should be applicable for absolute dropdowns)
@@ -445,7 +464,7 @@ export function useDropdown(config: DropdownConfig) {
445464 const absolute = Boolean ( append ) ;
446465 //handlers
447466 const handlers = {
448- clear : ( ) => setDropdowned ( [ ] ) ,
467+ clear : ( ) => setSelected ( [ ] ) ,
449468 open,
450469 portal ( dropdown : JSX . Element ) {
451470 //if no append selector
@@ -457,26 +476,26 @@ export function useDropdown(config: DropdownConfig) {
457476 if ( ! container ) return null ;
458477 return createPortal ( dropdown , container ) ;
459478 } ,
460- select : ( option : string ) => {
479+ select : ( option : string , close = true ) => {
461480 //if multiple selections allowed
462481 if ( multiple ) {
463482 //if option is already selected, remove it
464483 if ( selected . includes ( option ) ) {
465- const values = selected . filter ( s => s !== option ) ;
466- setDropdowned ( values ) ;
484+ const values = selected . filter ( value => value !== option ) ;
485+ setSelected ( values ) ;
467486 onUpdate && onUpdate ( values ) ;
468487 //else add it
469488 } else {
470489 const values = [ ...selected , option ] ;
471- setDropdowned ( values ) ;
490+ setSelected ( values ) ;
472491 onUpdate && onUpdate ( values ) ;
473492 }
474493 //single selection
475494 } else {
476- setDropdowned ( [ option ] ) ;
495+ setSelected ( [ option ] ) ;
477496 onUpdate && onUpdate ( option ) ;
478497 //close the dropdown
479- open ( false ) ;
498+ close && open ( false ) ;
480499 }
481500 }
482501 } ;
@@ -489,7 +508,7 @@ export function useDropdown(config: DropdownConfig) {
489508 handlers . select ( value ) ;
490509 } else if ( multiple && Array . isArray ( value ) ) {
491510 //update selected values
492- setDropdowned ( value ) ;
511+ setSelected ( value ) ;
493512 }
494513 //trigger update handler
495514 value && onUpdate && onUpdate ( value ) ;
@@ -602,12 +621,12 @@ export const DropdownContext = createContext<DropdownContextProps>({
602621 open : ( ) => { } ,
603622 //whether the dropdown is open
604623 opened : false ,
605- //options count
606- options : 0 ,
607624 //select an option
608625 select : ( ) => { } ,
609626 //the current selected option/s
610- selected : [ ]
627+ selected : [ ] ,
628+ //ie. for dropdown or for the control
629+ target : 'dropdown'
611630} ) ;
612631
613632/**
@@ -618,11 +637,11 @@ export function DropdownOption(props: DropdownOptionProps) {
618637 //props
619638 const { value, className, style, children } = props ;
620639 //hooks
621- const { select, selected, option } = useDropdownContext ( ) ;
640+ const { target , select, selected, option } = useDropdownContext ( ) ;
622641 //variables
623642 const active = selected . includes ( value ) ;
624643 // get slot styles
625- const slot = option ? getSlotStyles ( option , { active } ) : { } ;
644+ const slot = option ? getSlotStyles ( option , { target , selected : active } ) : { } ;
626645 //get final classes and styles
627646 const { classes, styles } = getClassStyles ( {
628647 //default classes to apply
@@ -638,14 +657,17 @@ export function DropdownOption(props: DropdownOptionProps) {
638657 style : style || slot . style
639658 } ,
640659 //state to pass to callable props
641- state : { active }
660+ state : { target , selected : active }
642661 } ) ;
643662 //handlers
644663 const onClick = ( ) => select ( value ) ;
645664 //render
646665 return (
647666 < div className = { classes . join ( ' ' ) } style = { styles } onClick = { onClick } >
648- { typeof children === 'function' ? children ( { active } ) : ( children || value ) }
667+ { typeof children === 'function'
668+ ? children ( { target, selected : active } )
669+ : ( children || value )
670+ }
649671 </ div >
650672 ) ;
651673} ;
@@ -803,13 +825,16 @@ export function Dropdown(props: DropdownProps) {
803825 top : `${ position [ 1 ] } px`
804826 } ;
805827 // get slot styles
806- const containerStyles = container
807- ? getSlotStyles ( container , { } )
808- : { } ;
809- containerStyles . className = [
810- 'dropdown-container' ,
811- containerStyles . className || ''
812- ] . filter ( Boolean ) . join ( ' ' ) ;
828+ const containerStyles = getClassStyles ( {
829+ //default classes to apply
830+ classes : [ 'dropdown-container' ] ,
831+ //style props
832+ props : container
833+ ? getSlotStyles ( container , { } )
834+ : { } ,
835+ //state to pass to callable props
836+ state : { }
837+ } ) ;
813838 // determine provider
814839 const components = getComponents (
815840 children ,
@@ -821,19 +846,19 @@ export function Dropdown(props: DropdownProps) {
821846 multiple,
822847 opened,
823848 option,
824- options : components . options . length ,
825849 clear : handlers . clear ,
826850 open : handlers . open ,
827851 select : handlers . select ,
828- selected
852+ selected,
853+ target : 'dropdown'
829854 } ;
830855 //render
831856 return (
832857 < DropdownContext . Provider value = { provider } >
833858 < div
834859 ref = { refs . container }
835- className = { containerStyles . className }
836- style = { containerStyles . style }
860+ className = { containerStyles . classes . join ( ' ' ) }
861+ style = { containerStyles . styles }
837862 >
838863 { components . control }
839864 { opened && components . options . length > 0 && handlers . portal (
0 commit comments