Files
pangolin/src/components/multi-select/multi-select-content.tsx

90 lines
3.1 KiB
TypeScript

import type { Ref } from "react";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList
} from "../ui/command";
import { cn } from "@app/lib/cn";
import { CheckIcon } from "lucide-react";
import { useTranslations } from "next-intl";
export type TagValue = { text: string; id: string };
export type MultiSelectTagsProps<T extends TagValue> = {
emptyPlaceholder?: string;
searchPlaceholder?: string;
searchQuery?: string;
options: Array<T>;
value: Array<T>;
onChange: (newValue: Array<T>) => void;
onSearch: (query: string) => void;
ref?: Ref<HTMLButtonElement>;
disabled?: boolean;
lockedIds?: Set<string>;
};
export function MultiSelectContent<T extends TagValue>({
emptyPlaceholder,
searchPlaceholder,
searchQuery,
value,
options,
onSearch,
onChange,
lockedIds
}: MultiSelectTagsProps<T>) {
const t = useTranslations();
const selectedValues = new Set(value.map((v) => v.id));
return (
<Command shouldFilter={false}>
<CommandInput
placeholder={searchPlaceholder ?? t("search")}
value={searchQuery}
onValueChange={onSearch}
/>
<CommandList>
<CommandEmpty className="text-muted-foreground">
{emptyPlaceholder ?? t("noResults")}
</CommandEmpty>
<CommandGroup>
{options.map((option) => {
const isLocked = lockedIds?.has(option.id);
return (
<CommandItem
value={option.id}
key={option.id}
disabled={isLocked}
onSelect={() => {
if (isLocked) return;
let newValues = [];
if (selectedValues.has(option.id)) {
newValues = value.filter(
(v) => v.id !== option.id
);
} else {
newValues = [...value, option];
}
onChange(newValues);
}}
>
<CheckIcon
className={cn(
"mr-2 h-4 w-4",
selectedValues.has(option.id)
? "opacity-100"
: "opacity-0"
)}
/>
{`${option.text}`}
</CommandItem>
);
})}
</CommandGroup>
</CommandList>
</Command>
);
}