"use client" import * as React from "react" import { cn } from "@/lib/utils" export type DualListItem = { label: React.ReactNode value: string disabled?: boolean } export type DualListPickerProps = React.ComponentProps<"div"> & { items: DualListItem[] picked?: string[] defaultPicked?: string[] onPickedChange?: (value: string[]) => void availableTitle?: React.ReactNode pickedTitle?: React.ReactNode searchPlaceholder?: string emptyMessage?: React.ReactNode maxPicked?: number transferAll?: boolean } function DualListPicker({ items, picked, defaultPicked = [], onPickedChange, availableTitle = "Available", pickedTitle = "Selected", searchPlaceholder = "Search items...", emptyMessage = "Nothing to show.", maxPicked, transferAll = true, className, ...props }: DualListPickerProps) { const [internalPicked, setInternalPicked] = React.useState(defaultPicked) const [query, setQuery] = React.useState("") const currentPicked = picked ?? internalPicked const pickedSet = React.useMemo(() => new Set(currentPicked), [currentPicked]) const normalizedQuery = query.trim().toLowerCase() const matchesQuery = React.useCallback( (item: DualListItem) => { if (!normalizedQuery) return true const label = typeof item.label === "string" ? item.label : String(item.value) return label.toLowerCase().includes(normalizedQuery) || item.value.toLowerCase().includes(normalizedQuery) }, [normalizedQuery] ) const availableItems = React.useMemo( () => items.filter((item) => !pickedSet.has(item.value) && matchesQuery(item)), [items, matchesQuery, pickedSet] ) const pickedItems = React.useMemo( () => items.filter((item) => pickedSet.has(item.value) && matchesQuery(item)), [items, matchesQuery, pickedSet] ) const maxReached = maxPicked !== undefined && currentPicked.length >= maxPicked const setPickedValues = React.useCallback( (next: string[]) => { if (picked === undefined) { setInternalPicked(next) } onPickedChange?.(next) }, [onPickedChange, picked] ) function toggle(value: string) { if (pickedSet.has(value)) { setPickedValues(currentPicked.filter((item) => item !== value)) return } if (maxReached) return setPickedValues([...currentPicked, value]) } function addAll() { const next = [...currentPicked] for (const item of items) { if (item.disabled || pickedSet.has(item.value)) continue if (maxPicked !== undefined && next.length >= maxPicked) break next.push(item.value) } setPickedValues(next) } function removeAll() { setPickedValues([]) } const renderColumn = (title: React.ReactNode, columnItems: DualListItem[]) => (