wallet-core/packages/exchange-backoffice-ui/src/handlers/InputSelectMultiple.tsx
2023-05-15 11:45:23 -03:00

148 lines
5.0 KiB
TypeScript

import { Fragment, VNode, h } from "preact";
import { Choice } from "./InputChoice.js";
import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js";
import { useField } from "./useField.js";
import { useState } from "preact/hooks";
export function InputSelectMultiple(
props: {
choices: Choice[];
unique?: boolean;
} & UIFormProps<Array<string>>,
): VNode {
const { name, label, choices, placeholder, tooltip, required, unique } =
props;
const { value, onChange } = useField<{ [s: string]: Array<string> }>(name);
const [filter, setFilter] = useState<string | undefined>(undefined);
const regex = new RegExp(`.*${filter}.*`, "i");
const choiceMap = choices.reduce((prev, curr) => {
return { ...prev, [curr.value]: curr.label };
}, {} as Record<string, string>);
const list = value ?? [];
const filteredChoices =
filter === undefined
? undefined
: choices.filter((v) => {
return regex.test(v.label);
});
return (
<div class="sm:col-span-6">
<LabelWithTooltipMaybeRequired
label={label}
required={required}
tooltip={tooltip}
/>
{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">
{choiceMap[v]}
<button
type="button"
onClick={() => {
const newValue = [...list];
newValue.splice(idx, 1);
onChange(newValue);
setFilter(undefined);
}}
class="group relative h-5 w-5 rounded-sm hover:bg-gray-500/20"
>
<span class="sr-only">Remove</span>
<svg
viewBox="0 0 14 14"
class="h-5 w-5 stroke-gray-700/50 group-hover:stroke-gray-700/75"
>
<path d="M4 4l6 6m0-6l-6 6" />
</svg>
<span class="absolute -inset-1"></span>
</button>
</span>
);
})}
<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"
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"
>
<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) !== -1) {
return;
}
const newValue = [...list];
newValue.splice(0, 0, v.value);
onChange(newValue);
}}
// 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>
);
})}
{/* <!--
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>
</div>
);
}