summaryrefslogtreecommitdiff
path: root/packages/web-util/src/forms/InputSelectMultiple.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/web-util/src/forms/InputSelectMultiple.tsx')
-rw-r--r--packages/web-util/src/forms/InputSelectMultiple.tsx174
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>
);
}