@@ -80,12 +80,14 @@ export type Group<T extends object> = {
80
80
[ uniqueGroupKey ] : true ;
81
81
} ;
82
82
83
+ type OptionOrGroup < T extends object > = T | Group < T > ;
84
+
83
85
export const buildGroup = < T extends object > ( group : Omit < Group < T > , typeof uniqueGroupKey > ) : Group < T > => ( {
84
86
...group ,
85
87
[ uniqueGroupKey ] : true ,
86
88
} ) ;
87
89
88
- const isGroup = < T extends object > ( optionOrGroup : T | Group < T > ) : optionOrGroup is Group < T > => {
90
+ const isGroup = < T extends object > ( optionOrGroup : OptionOrGroup < T > ) : optionOrGroup is Group < T > => {
89
91
return uniqueGroupKey in optionOrGroup && optionOrGroup [ uniqueGroupKey ] === true ;
90
92
} ;
91
93
@@ -141,7 +143,7 @@ type PickerProps<T extends object> = {
141
143
/**
142
144
* The options to display in the picker. This can be a flat array of options or an array of groups.
143
145
*/
144
- optionsOrGroups : T [ ] | Group < T > [ ] ;
146
+ optionsOrGroups : OptionOrGroup < T > [ ] ;
145
147
/**
146
148
* A function that returns the id of an option.
147
149
*/
@@ -199,11 +201,11 @@ type PickerProps<T extends object> = {
199
201
} ;
200
202
201
203
export type PickerContextState < T extends object > = {
202
- $optionsOrGroups : WritableAtom < T [ ] | Group < T > [ ] > ;
204
+ $optionsOrGroups : WritableAtom < OptionOrGroup < T > [ ] > ;
203
205
$groupStatusMap : WritableAtom < GroupStatusMap > ;
204
206
$compactView : WritableAtom < boolean > ;
205
207
$activeOptionId : WritableAtom < string | undefined > ;
206
- $filteredOptions : WritableAtom < T [ ] | Group < T > [ ] > ;
208
+ $filteredOptions : WritableAtom < OptionOrGroup < T > [ ] > ;
207
209
$flattenedFilteredOptions : ReadableAtom < T [ ] > ;
208
210
$totalOptionCount : ReadableAtom < number > ;
209
211
$areAllGroupsDisabled : ReadableAtom < boolean > ;
@@ -250,7 +252,7 @@ export const getRegex = (searchTerm: string) => {
250
252
return new RegExp ( `${ pattern } .+` , 'i' ) ;
251
253
} ;
252
254
253
- const getFirstOption = < T extends object > ( options : T [ ] | Group < T > [ ] ) : T | undefined => {
255
+ const getFirstOption = < T extends object > ( options : OptionOrGroup < T > [ ] ) : T | undefined => {
254
256
const firstOptionOrGroup = options [ 0 ] ;
255
257
if ( ! firstOptionOrGroup ) {
256
258
return ;
@@ -263,7 +265,7 @@ const getFirstOption = <T extends object>(options: T[] | Group<T>[]): T | undefi
263
265
} ;
264
266
265
267
const getFirstOptionId = < T extends object > (
266
- options : T [ ] | Group < T > [ ] ,
268
+ options : OptionOrGroup < T > [ ] ,
267
269
getOptionId : ( item : T ) => string
268
270
) : string | undefined => {
269
271
const firstOptionOrGroup = getFirstOption ( options ) ;
@@ -275,7 +277,7 @@ const getFirstOptionId = <T extends object>(
275
277
} ;
276
278
277
279
const findOption = < T extends object > (
278
- options : T [ ] | Group < T > [ ] ,
280
+ options : OptionOrGroup < T > [ ] ,
279
281
id : string ,
280
282
getOptionId : ( item : T ) => string
281
283
) : T | undefined => {
@@ -293,7 +295,7 @@ const findOption = <T extends object>(
293
295
}
294
296
} ;
295
297
296
- const flattenOptions = < T extends object > ( options : ( T | Group < T > ) [ ] ) : T [ ] => {
298
+ const flattenOptions = < T extends object > ( options : OptionOrGroup < T > [ ] ) : T [ ] => {
297
299
const flattened : T [ ] = [ ] ;
298
300
for ( const optionOrGroup of options ) {
299
301
if ( isGroup ( optionOrGroup ) ) {
@@ -307,7 +309,7 @@ const flattenOptions = <T extends object>(options: (T | Group<T>)[]): T[] => {
307
309
308
310
type GroupStatusMap = Record < string , boolean > ;
309
311
310
- const useTogglableGroups = < T extends object > ( options : ( T | Group < T > ) [ ] ) => {
312
+ const useTogglableGroups = < T extends object > ( options : OptionOrGroup < T > [ ] ) => {
311
313
const groupsWithOptions = useMemo ( ( ) => {
312
314
const ids : string [ ] = [ ] ;
313
315
for ( const optionOrGroup of options ) {
@@ -478,6 +480,18 @@ const useComputed = <Value, OriginStores extends AnyStore[]>(
478
480
return useState ( ( ) => computed ( stores , cb ) ) [ 0 ] ;
479
481
} ;
480
482
483
+ const countOptions = < T extends object > ( optionsOrGroups : OptionOrGroup < T > [ ] ) => {
484
+ let count = 0 ;
485
+ for ( const optionOrGroup of optionsOrGroups ) {
486
+ if ( isGroup ( optionOrGroup ) ) {
487
+ count += optionOrGroup . options . length ;
488
+ } else {
489
+ count ++ ;
490
+ }
491
+ }
492
+ return count ;
493
+ } ;
494
+
481
495
export const Picker = typedMemo ( < T extends object > ( props : PickerProps < T > ) => {
482
496
const {
483
497
getOptionId,
@@ -501,24 +515,9 @@ export const Picker = typedMemo(<T extends object>(props: PickerProps<T>) => {
501
515
const $activeOptionId = useAtom ( getFirstOptionId ( optionsOrGroups , getOptionId ) ) ;
502
516
const $compactView = useAtom ( true ) ;
503
517
const $optionsOrGroups = useAtom ( optionsOrGroups ) ;
504
- useEffect ( ( ) => {
505
- $optionsOrGroups . set ( optionsOrGroups ) ;
506
- } , [ optionsOrGroups , $optionsOrGroups ] ) ;
507
- const $totalOptionCount = useComputed ( [ $optionsOrGroups ] , ( optionsOrGroups ) => {
508
- let count = 0 ;
509
- for ( const optionOrGroup of optionsOrGroups ) {
510
- if ( isGroup ( optionOrGroup ) ) {
511
- count += optionOrGroup . options . length ;
512
- } else {
513
- count ++ ;
514
- }
515
- }
516
- return count ;
517
- } ) ;
518
- const $filteredOptions = useAtom < T [ ] | Group < T > [ ] > ( [ ] ) ;
519
- const $flattenedFilteredOptions = useComputed ( [ $filteredOptions ] , ( filteredOptions ) =>
520
- flattenOptions ( filteredOptions )
521
- ) ;
518
+ const $totalOptionCount = useComputed ( [ $optionsOrGroups ] , countOptions ) ;
519
+ const $filteredOptions = useAtom < OptionOrGroup < T > [ ] > ( [ ] ) ;
520
+ const $flattenedFilteredOptions = useComputed ( [ $filteredOptions ] , flattenOptions ) ;
522
521
const $hasOptions = useComputed ( [ $totalOptionCount ] , ( count ) => count > 0 ) ;
523
522
const $filteredOptionsCount = useComputed ( [ $flattenedFilteredOptions ] , ( options ) => options . length ) ;
524
523
const $hasFilteredOptions = useComputed ( [ $filteredOptionsCount ] , ( count ) => count > 0 ) ;
@@ -542,10 +541,15 @@ export const Picker = typedMemo(<T extends object>(props: PickerProps<T>) => {
542
541
[ $filteredOptions , getOptionId , onSelect ]
543
542
) ;
544
543
544
+ // Sync the picker's nanostores when props change
545
545
useEffect ( ( ) => {
546
546
$selectedItem . set ( selectedOption ) ;
547
547
} , [ $selectedItem , selectedOption ] ) ;
548
548
549
+ useEffect ( ( ) => {
550
+ $optionsOrGroups . set ( optionsOrGroups ) ;
551
+ } , [ optionsOrGroups , $optionsOrGroups ] ) ;
552
+
549
553
const ctx = useMemo (
550
554
( ) =>
551
555
( {
@@ -647,8 +651,8 @@ const PickerSyncer = typedMemo(<T extends object>() => {
647
651
return true ;
648
652
}
649
653
} ) ;
650
- $filteredOptions . set ( filtered as T [ ] | Group < T > [ ] ) ;
651
- $activeOptionId . set ( getFirstOptionId ( filtered as T [ ] | Group < T > [ ] , getOptionId ) ) ;
654
+ $filteredOptions . set ( filtered ) ;
655
+ $activeOptionId . set ( getFirstOptionId ( filtered , getOptionId ) ) ;
652
656
} else {
653
657
const lowercasedSearchTerm = debouncedSearchTerm . toLowerCase ( ) ;
654
658
const filtered = [ ] ;
@@ -667,8 +671,8 @@ const PickerSyncer = typedMemo(<T extends object>() => {
667
671
}
668
672
}
669
673
}
670
- $filteredOptions . set ( filtered as T [ ] | Group < T > [ ] ) ;
671
- $activeOptionId . set ( getFirstOptionId ( filtered as T [ ] | Group < T > [ ] , getOptionId ) ) ;
674
+ $filteredOptions . set ( filtered ) ;
675
+ $activeOptionId . set ( getFirstOptionId ( filtered , getOptionId ) ) ;
672
676
}
673
677
} , [
674
678
debouncedSearchTerm ,
0 commit comments