11import { mdiCheck , mdiClose , mdiPlus } from "@mdi/js"
22import { Key } from "@solid-primitives/keyed"
3+ import type { Accessor } from "solid-js"
34import { mergeProps } from "solid-js"
45import { ct0 , ct1 } from "~ui/i18n/ct0"
56import { t4multiselect } from "~ui/input/select/t4multiselect"
67import { buttonVariant } from "~ui/interactive/button/buttonCva"
78import { ButtonIcon } from "~ui/interactive/button/ButtonIcon"
8- import { CorvuPopover , type CorvuPopoverProps } from "~ui/interactive/popover/CorvuPopover"
9+ import type { CorvuPopoverProps } from "~ui/interactive/popover/CorvuPopover"
10+ import { CorvuPopover } from "~ui/interactive/popover/CorvuPopover"
911import { classArr } from "~ui/utils/classArr"
1012import { classMerge } from "~ui/utils/classMerge"
1113import type { SignalObject } from "~ui/utils/createSignalObject"
12- import type { HasGetOptions } from "~ui/utils/HasGetOptions"
1314import type { MayHaveChildren } from "~ui/utils/MayHaveChildren"
1415import type { MayHaveClass } from "~ui/utils/MayHaveClass"
15- import type { SelectionItem } from "~ui/utils/SelectionItem"
1616
1717/**
1818 * https://github.com/radix-ui/primitives/blob/main/packages/react/checkbox/src/Checkbox.tsx
1919 */
20- export interface MultiselectProps < T extends string = string > extends MultiselectState < T > , MayHaveClass , MayHaveChildren {
20+ export interface Multiselect2Props extends MayHaveClass , MayHaveChildren {
2121 buttonProps : CorvuPopoverProps
2222 textNoEntries ?: string
2323 textAddEntry ?: string
24+ getOptions : Accessor < string [ ] >
25+ valueSignal : SignalObject < string [ ] >
26+ valueDisplay ?: ( value : string ) => string
2427}
2528
26- export interface MultiselectState < T extends string = string >
27- extends MultiselectStateValue < T > ,
28- MultiselectStateOptions < T > { }
29-
30- export type MultiselectStateValue < T extends string = string > = {
31- valueSignal : SignalObject < SelectionItem < T > [ ] >
32- }
33-
34- export type MultiselectStateOptions < T extends string = string > = HasGetOptions < T >
35-
36- export function Multiselect < T extends string = string > ( p : MultiselectProps < T > ) {
29+ export function Multiselect ( p : Multiselect2Props ) {
3730 const buttonProps = mergeProps (
3831 {
3932 icon : mdiPlus ,
@@ -53,102 +46,112 @@ export function Multiselect<T extends string = string>(p: MultiselectProps<T>) {
5346 p . class ,
5447 ) }
5548 >
56- < SelectedValues valueSignal = { p . valueSignal } />
49+ < SelectedValues2 valueSignal = { p . valueSignal } valueDisplay = { p . valueDisplay } />
5750 < CorvuPopover { ...buttonProps } >
5851 < div class = { classArr ( "bg-white dark:bg-black" , "max-h-dvh" , "grid grid-cols-3 gap-x-2 gap-y-1" ) } >
59- < OptionList valueSignal = { p . valueSignal } getOptions = { p . getOptions } />
52+ < OptionList2 valueSignal = { p . valueSignal } getOptions = { p . getOptions } valueDisplay = { p . valueDisplay } />
6053 </ div >
6154 </ CorvuPopover >
6255 </ div >
6356 )
6457}
6558
66- type MultiselectOptionState < T extends string = string > = { option : SelectionItem < T > } & MultiselectStateValue < T >
59+ interface Multiselect2OptionState {
60+ option : string
61+ }
62+
63+ interface Multiselect2OptionState {
64+ valueSignal : SignalObject < string [ ] >
65+ valueDisplay ?: ( value : string ) => string
66+ }
6767
68- function SelectedValues < T extends string = string > ( p : MultiselectStateValue < T > ) {
69- // items-center justify-center
68+ function SelectedValues2 ( p : { valueSignal : SignalObject < string [ ] > ; valueDisplay ?: ( value : string ) => string } ) {
7069 return (
7170 < div class = { "flex flex-wrap gap-1" } >
72- < Key each = { p . valueSignal . get ( ) } by = { ( item ) => item . value } fallback = { < NoItems /> } >
73- { ( item ) => < SelectedValue option = { item ( ) } valueSignal = { p . valueSignal } /> }
71+ < Key each = { p . valueSignal . get ( ) } by = { ( item ) => item } fallback = { < NoItems2 /> } >
72+ { ( item ) => < SelectedValue2 option = { item ( ) } valueSignal = { p . valueSignal } valueDisplay = { p . valueDisplay } /> }
7473 </ Key >
7574 </ div >
7675 )
7776}
7877
79- function SelectedValue < T extends string = string > ( p : MultiselectOptionState < T > ) {
78+ function SelectedValue2 ( p : Multiselect2OptionState ) {
79+ const label = ( ) => ( p . valueDisplay ? p . valueDisplay ( p . option ) : p . option )
8080 return (
8181 < ButtonIcon
8282 variant = { buttonVariant . outline }
8383 iconRight = { mdiClose }
8484 class = { "text-sm px-2 py-1" }
85- data-value = { p . option . value }
86- onMouseDown = { ( e ) => optionRemove ( p ) }
87- onClick = { ( e ) => optionRemove ( p ) }
88- title = { ct1 ( t4multiselect . Remove_x , p . option . label ) }
85+ data-value = { p . option }
86+ onMouseDown = { ( e ) => optionRemove2 ( p ) }
87+ onClick = { ( e ) => optionRemove2 ( p ) }
88+ title = { ct1 ( t4multiselect . Remove_x , label ( ) ) }
8989 >
90- { p . option . label }
90+ { label ( ) }
9191 </ ButtonIcon >
9292 )
9393}
9494
95- function OptionList < T extends string = string > ( p : MultiselectState < T > ) {
95+ function OptionList2 ( p : {
96+ valueSignal : SignalObject < string [ ] >
97+ getOptions : Accessor < string [ ] >
98+ valueDisplay ?: ( value : string ) => string
99+ } ) {
96100 return (
97101 < >
98- < Key each = { p . getOptions ( ) } by = { ( item ) => item . value } fallback = { < NoItems /> } >
99- { ( item ) => < ListOption option = { item ( ) } valueSignal = { p . valueSignal } /> }
102+ < Key each = { p . getOptions ( ) } by = { ( item ) => item } fallback = { < NoItems2 /> } >
103+ { ( item ) => < ListOption2 option = { item ( ) } valueSignal = { p . valueSignal } valueDisplay = { p . valueDisplay } /> }
100104 </ Key >
101105 </ >
102106 )
103107}
104108
105- function ListOption < T extends string = string > ( p : MultiselectOptionState < T > ) {
109+ function ListOption2 ( p : Multiselect2OptionState ) {
110+ const label = ( ) => ( p . valueDisplay ? p . valueDisplay ( p . option ) : p . option )
106111 return (
107112 < >
108113 < ButtonIcon
109114 type = "button"
110115 role = "checkbox"
111- aria-checked = { optionIsSelected ( p ) }
112- data-state = { optionIsSelected ( p ) }
113- iconRight = { optionIsSelected ( p ) ? mdiCheck : undefined }
116+ aria-checked = { optionIsSelected2 ( p ) }
117+ data-state = { optionIsSelected2 ( p ) }
118+ iconRight = { optionIsSelected2 ( p ) ? mdiCheck : undefined }
114119 onClick = { ( e ) => {
115- // console.log("onchange", e.currentTarget.id, e.currentTarget.checked)
116- toggleOption ( p )
120+ toggleOption2 ( p )
117121 } }
118122 variant = { buttonVariant . ghost }
119- // class={optionIsSelected(p.option, p.valueSignal) ? "" : "pl-8"}
120123 class = { "justify-start" }
121124 >
122- { p . option . label }
125+ { label ( ) }
123126 </ ButtonIcon >
124127 </ >
125128 )
126129}
127130
128- function toggleOption < T extends string = string > ( p : MultiselectOptionState < T > ) {
129- const hasOption = optionIsSelected ( p )
131+ function toggleOption2 ( p : Multiselect2OptionState ) {
132+ const hasOption = optionIsSelected2 ( p )
130133 if ( hasOption ) {
131- return optionRemove ( p )
134+ return optionRemove2 ( p )
132135 }
133- return optionAdd ( p )
136+ return optionAdd2 ( p )
134137}
135138
136- function optionRemove < T extends string = string > ( p : MultiselectOptionState < T > ) {
137- const newValues = p . valueSignal . get ( ) . filter ( ( v ) => v . value !== p . option . value )
139+ function optionRemove2 ( p : Multiselect2OptionState ) {
140+ const newValues = p . valueSignal . get ( ) . filter ( ( v ) => v !== p . option )
138141 p . valueSignal . set ( newValues )
139142}
140143
141- function optionAdd < T extends string = string > ( p : MultiselectOptionState < T > ) {
144+ function optionAdd2 ( p : Multiselect2OptionState ) {
142145 const newValues = [ ...p . valueSignal . get ( ) , p . option ]
143- newValues . sort ( ( a , b ) => a . label . localeCompare ( b . label ) )
146+ newValues . sort ( ( a , b ) => a . localeCompare ( b ) )
144147 p . valueSignal . set ( newValues )
145148}
146149
147- function optionIsSelected < T extends string = string > ( p : MultiselectOptionState < T > ) {
148- return p . valueSignal . get ( ) . some ( ( v ) => v . value === p . option . value )
150+ function optionIsSelected2 ( p : Multiselect2OptionState ) {
151+ return p . valueSignal . get ( ) . includes ( p . option )
149152}
150153
151- function NoItems ( p : MayHaveClass ) {
154+ function NoItems2 ( p : MayHaveClass = { } ) {
152155 return (
153156 < div
154157 class = { classMerge (
0 commit comments