({
value,
options,
renderValue,
disabled,
placeholder,
onChange,
}: {
value: string[];
options: { label: string; value: string }[];
renderValue?: (value: string, fallbackLabel?: string) => ReactNode;
disabled?: boolean;
placeholder?: string;
onChange: (value: string[]) => void;
})
| 721 | // ============================================================ |
| 722 | |
| 723 | function MultiCheckSelect({ |
| 724 | value, |
| 725 | options, |
| 726 | renderValue, |
| 727 | disabled, |
| 728 | placeholder, |
| 729 | onChange, |
| 730 | }: { |
| 731 | value: string[]; |
| 732 | options: { label: string; value: string }[]; |
| 733 | renderValue?: (value: string, fallbackLabel?: string) => ReactNode; |
| 734 | disabled?: boolean; |
| 735 | placeholder?: string; |
| 736 | onChange: (value: string[]) => void; |
| 737 | }) { |
| 738 | const { t } = useTranslation(); |
| 739 | const [open, setOpen] = useState(false); |
| 740 | const triggerRef = useRef<HTMLButtonElement>(null); |
| 741 | const dropdownRef = useRef<HTMLDivElement>(null); |
| 742 | |
| 743 | const allValues = options.map((o) => o.value); |
| 744 | const allSelected = |
| 745 | allValues.length > 0 && allValues.every((v) => value.includes(v)); |
| 746 | const anySelected = value.length > 0; |
| 747 | |
| 748 | const getLabelForValue = (v: string) => |
| 749 | options.find((o) => o.value === v)?.label ?? v; |
| 750 | |
| 751 | const toggleValue = (v: string) => { |
| 752 | if (value.includes(v)) { |
| 753 | onChange(value.filter((x) => x !== v)); |
| 754 | } else { |
| 755 | onChange([...value, v]); |
| 756 | } |
| 757 | }; |
| 758 | |
| 759 | const close = useCallback(() => setOpen(false), []); |
| 760 | useClickOutside([triggerRef, dropdownRef], open, close); |
| 761 | |
| 762 | return ( |
| 763 | <div className="min-w-32"> |
| 764 | <button |
| 765 | ref={triggerRef} |
| 766 | type="button" |
| 767 | className={cn( |
| 768 | VALUE_MULTI_FIELD_CLASS, |
| 769 | "inline-flex w-full flex-wrap items-center gap-1 text-left hover:bg-control-bg disabled:pointer-events-none disabled:opacity-50" |
| 770 | )} |
| 771 | disabled={disabled} |
| 772 | onClick={() => setOpen(!open)} |
| 773 | > |
| 774 | {value.length === 0 && ( |
| 775 | <span className="text-control-placeholder">{placeholder}</span> |
| 776 | )} |
| 777 | {value.map((v) => ( |
| 778 | <span |
| 779 | key={v} |
| 780 | className="inline-flex items-center gap-1 bg-control-bg text-xs px-1.5 py-0.5 rounded-xs" |
nothing calls this directly
no test coverage detected