diff options
Diffstat (limited to 'packages/web-util/src/forms/InputSelectMultiple.tsx')
-rw-r--r-- | packages/web-util/src/forms/InputSelectMultiple.tsx | 174 |
1 files changed, 93 insertions, 81 deletions
diff --git a/packages/web-util/src/forms/InputSelectMultiple.tsx b/packages/web-util/src/forms/InputSelectMultiple.tsx index a67eb23b7..1bcf85061 100644 --- a/packages/web-util/src/forms/InputSelectMultiple.tsx +++ b/packages/web-util/src/forms/InputSelectMultiple.tsx @@ -1,6 +1,7 @@ import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { UIFormProps } from "./FormProvider.js"; +import { noHandlerPropsAndNoContextForField } from "./InputArray.js"; import { ChoiceS } from "./InputChoiceStacked.js"; import { LabelWithTooltipMaybeRequired } from "./InputLine.js"; import { useField } from "./useField.js"; @@ -12,23 +13,28 @@ export function InputSelectMultiple<T extends object, K extends keyof T>( max?: number; } & UIFormProps<T, K>, ): VNode { - const { name, label, choices, placeholder, tooltip, required, unique, max } = - props; - const { value, onChange, state } = useField<T, K>(name); + const { converter, label, choices, placeholder, tooltip, required, unique, max } = props; + //FIXME: remove deprecated + const fieldCtx = useField<T, K>(props.name); + const { value, onChange, state } = + props.handler ?? fieldCtx ?? noHandlerPropsAndNoContextForField(props.name); const [filter, setFilter] = useState<string | undefined>(undefined); const regex = new RegExp(`.*${filter}.*`, "i"); - const choiceMap = choices.reduce((prev, curr) => { - return { ...prev, [curr.value as string]: curr.label }; - }, {} as Record<string, string>); + const choiceMap = choices.reduce( + (prev, curr) => { + return { ...prev, [curr.value as string]: curr.label }; + }, + {} as Record<string, string>, + ); const list = (value ?? []) as string[]; const filteredChoices = filter === undefined ? undefined : choices.filter((v) => { - return regex.test(v.label); - }); + return regex.test(v.label); + }); return ( <div class="sm:col-span-6"> <LabelWithTooltipMaybeRequired @@ -38,7 +44,10 @@ export function InputSelectMultiple<T extends object, K extends keyof T>( /> {list.map((v, idx) => { return ( - <span class="inline-flex items-center gap-x-0.5 rounded-md bg-gray-100 p-1 mr-2 text-xs font-medium text-gray-600"> + <span + key={idx} + class="inline-flex items-center gap-x-0.5 rounded-md bg-gray-100 p-1 mr-2 text-xs font-medium text-gray-600" + > {choiceMap[v]} <button type="button" @@ -46,7 +55,7 @@ export function InputSelectMultiple<T extends object, K extends keyof T>( onClick={() => { const newValue = [...list]; newValue.splice(idx, 1); - onChange(newValue as T[K]); + onChange(newValue as any); setFilter(undefined); }} class="group relative h-5 w-5 rounded-sm hover:bg-gray-500/20" @@ -64,91 +73,94 @@ export function InputSelectMultiple<T extends object, K extends keyof T>( ); })} - {!state.disabled && <div class="relative mt-2"> - <input - id="combobox" - type="text" - value={filter ?? ""} - onChange={(e) => { - setFilter(e.currentTarget.value); - }} - placeholder={placeholder} - class="w-full rounded-md border-0 bg-white py-1.5 pl-3 pr-12 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - role="combobox" - aria-controls="options" - aria-expanded="false" - /> - <button - type="button" - disabled={state.disabled} - onClick={() => { - setFilter(filter === undefined ? "" : undefined); - }} - class="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none" - > - <svg - class="h-5 w-5 text-gray-400" - viewBox="0 0 20 20" - fill="currentColor" - aria-hidden="true" + {!state.disabled && ( + <div class="relative mt-2"> + <input + id="combobox" + type="text" + value={filter ?? ""} + onChange={(e) => { + setFilter(e.currentTarget.value); + }} + placeholder={placeholder} + class="w-full rounded-md border-0 bg-white py-1.5 pl-3 pr-12 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + role="combobox" + aria-controls="options" + aria-expanded="false" + /> + <button + type="button" + disabled={state.disabled} + onClick={() => { + setFilter(filter === undefined ? "" : undefined); + }} + class="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none" > - <path - fill-rule="evenodd" - d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" - clip-rule="evenodd" - /> - </svg> - </button> + <svg + class="h-5 w-5 text-gray-400" + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" + > + <path + fill-rule="evenodd" + d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" + clip-rule="evenodd" + /> + </svg> + </button> - {filteredChoices !== undefined && ( - <ul - class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" - id="options" - role="listbox" - > - {filteredChoices.map((v, idx) => { - return ( - <li - class="relative cursor-pointer select-none py-2 pl-3 pr-9 text-gray-900 hover:text-white hover:bg-indigo-600" - id="option-0" - role="option" - onClick={() => { - setFilter(undefined); - if (unique && list.indexOf(v.value as string) !== -1) { - return; - } - if (max !== undefined && list.length >= max) { - return; - } - const newValue = [...list]; - newValue.splice(0, 0, v.value as string); - onChange(newValue as T[K]); - }} + {filteredChoices !== undefined && ( + <ul + class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" + id="options" + role="listbox" + > + {filteredChoices.map((v, idx) => { + return ( + <li + key={idx} + class="relative cursor-pointer select-none py-2 pl-3 pr-9 text-gray-900 hover:text-white hover:bg-indigo-600" + id="option-0" + role="option" + onClick={() => { + setFilter(undefined); + if (unique && list.indexOf(v.value as string) !== -1) { + return; + } + if (max !== undefined && list.length >= max) { + return; + } + const newValue = [...list]; + newValue.splice(0, 0, v.value as string); + onChange(newValue as any); + }} - // tabindex="-1" - > - {/* <!-- Selected: "font-semibold" --> */} - <span class="block truncate">{v.label}</span> + // tabindex="-1" + > + {/* <!-- Selected: "font-semibold" --> */} + <span class="block truncate">{v.label}</span> - {/* <!-- + {/* <!-- Checkmark, only display for selected option. Active: "text-white", Not Active: "text-indigo-600" --> */} - </li> - ); - })} + </li> + ); + })} - {/* <!-- + {/* <!-- Combobox option, manage highlight styles based on mouseenter/mouseleave and keyboard navigation. Active: "text-white bg-indigo-600", Not Active: "text-gray-900" --> */} - {/* <!-- More items... --> */} - </ul> - )} - </div>} + {/* <!-- More items... --> */} + </ul> + )} + </div> + )} </div> ); } |