@iclips/ui 2.0.3 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +246 -0
- package/MIGRATION-v3.md +244 -0
- package/README.md +4 -0
- package/dist/alert-theme.css +20 -79
- package/dist/components/ui/charts/donut-chart.cjs +1 -1
- package/dist/components/ui/charts/donut-chart.cjs.map +1 -1
- package/dist/components/ui/charts/donut-chart.js +39 -60
- package/dist/components/ui/charts/donut-chart.js.map +1 -1
- package/dist/components/ui/charts/kpi-card.cjs +1 -1
- package/dist/components/ui/charts/kpi-card.cjs.map +1 -1
- package/dist/components/ui/charts/kpi-card.js +19 -19
- package/dist/components/ui/charts/kpi-card.js.map +1 -1
- package/dist/components/ui/data/comment-system.cjs +1 -1
- package/dist/components/ui/data/comment-system.cjs.map +1 -1
- package/dist/components/ui/data/comment-system.js +67 -67
- package/dist/components/ui/data/comment-system.js.map +1 -1
- package/dist/components/ui/data/data-table.cjs +1 -1
- package/dist/components/ui/data/data-table.cjs.map +1 -1
- package/dist/components/ui/data/data-table.js +112 -101
- package/dist/components/ui/data/data-table.js.map +1 -1
- package/dist/components/ui/data/editorial-calendar.cjs +1 -1
- package/dist/components/ui/data/editorial-calendar.cjs.map +1 -1
- package/dist/components/ui/data/editorial-calendar.js +291 -291
- package/dist/components/ui/data/editorial-calendar.js.map +1 -1
- package/dist/components/ui/data/filter-builder.cjs +1 -1
- package/dist/components/ui/data/filter-builder.cjs.map +1 -1
- package/dist/components/ui/data/filter-builder.js +1 -1
- package/dist/components/ui/data/filter-builder.js.map +1 -1
- package/dist/components/ui/feedback/empty-state.cjs +1 -1
- package/dist/components/ui/feedback/empty-state.cjs.map +1 -1
- package/dist/components/ui/feedback/empty-state.js +14 -14
- package/dist/components/ui/feedback/empty-state.js.map +1 -1
- package/dist/components/ui/feedback/toast.cjs +1 -1
- package/dist/components/ui/feedback/toast.cjs.map +1 -1
- package/dist/components/ui/feedback/toast.js +53 -22
- package/dist/components/ui/feedback/toast.js.map +1 -1
- package/dist/components/ui/forms/calendar.cjs +1 -1
- package/dist/components/ui/forms/calendar.cjs.map +1 -1
- package/dist/components/ui/forms/calendar.js +16 -16
- package/dist/components/ui/forms/calendar.js.map +1 -1
- package/dist/components/ui/forms/combobox.cjs +1 -1
- package/dist/components/ui/forms/combobox.cjs.map +1 -1
- package/dist/components/ui/forms/combobox.js +78 -76
- package/dist/components/ui/forms/combobox.js.map +1 -1
- package/dist/components/ui/forms/file-attachment-list.cjs +1 -1
- package/dist/components/ui/forms/file-attachment-list.cjs.map +1 -1
- package/dist/components/ui/forms/file-attachment-list.js +4 -4
- package/dist/components/ui/forms/file-attachment-list.js.map +1 -1
- package/dist/components/ui/forms/file-upload-zone.cjs +2 -2
- package/dist/components/ui/forms/file-upload-zone.cjs.map +1 -1
- package/dist/components/ui/forms/file-upload-zone.js +55 -49
- package/dist/components/ui/forms/file-upload-zone.js.map +1 -1
- package/dist/components/ui/forms/form.cjs +1 -1
- package/dist/components/ui/forms/form.cjs.map +1 -1
- package/dist/components/ui/forms/form.js +11 -11
- package/dist/components/ui/forms/form.js.map +1 -1
- package/dist/components/ui/forms/input-validation.cjs +1 -1
- package/dist/components/ui/forms/input-validation.cjs.map +1 -1
- package/dist/components/ui/forms/input-validation.js +20 -20
- package/dist/components/ui/forms/input-validation.js.map +1 -1
- package/dist/components/ui/forms/multi-select.cjs +1 -1
- package/dist/components/ui/forms/multi-select.cjs.map +1 -1
- package/dist/components/ui/forms/multi-select.js +69 -67
- package/dist/components/ui/forms/multi-select.js.map +1 -1
- package/dist/components/ui/forms/radio-group.cjs +1 -1
- package/dist/components/ui/forms/radio-group.cjs.map +1 -1
- package/dist/components/ui/forms/radio-group.js +19 -19
- package/dist/components/ui/forms/radio-group.js.map +1 -1
- package/dist/components/ui/forms/social-button.cjs +1 -1
- package/dist/components/ui/forms/social-button.cjs.map +1 -1
- package/dist/components/ui/forms/social-button.js +6 -6
- package/dist/components/ui/forms/social-button.js.map +1 -1
- package/dist/components/ui/forms/toggle.cjs +1 -1
- package/dist/components/ui/forms/toggle.cjs.map +1 -1
- package/dist/components/ui/forms/toggle.js +7 -7
- package/dist/components/ui/forms/toggle.js.map +1 -1
- package/dist/components/ui/navigation/navigation-menu.cjs +1 -1
- package/dist/components/ui/navigation/navigation-menu.cjs.map +1 -1
- package/dist/components/ui/navigation/navigation-menu.js +14 -14
- package/dist/components/ui/navigation/navigation-menu.js.map +1 -1
- package/dist/components/ui/navigation/sidebar.cjs +1 -1
- package/dist/components/ui/navigation/sidebar.cjs.map +1 -1
- package/dist/components/ui/navigation/sidebar.js +44 -44
- package/dist/components/ui/navigation/sidebar.js.map +1 -1
- package/dist/components/ui/overlays/context-menu.cjs +1 -1
- package/dist/components/ui/overlays/context-menu.cjs.map +1 -1
- package/dist/components/ui/overlays/context-menu.js +1 -1
- package/dist/components/ui/overlays/context-menu.js.map +1 -1
- package/dist/components/ui/overlays/dropdown-menu.cjs +1 -1
- package/dist/components/ui/overlays/dropdown-menu.cjs.map +1 -1
- package/dist/components/ui/overlays/dropdown-menu.js +5 -5
- package/dist/components/ui/overlays/dropdown-menu.js.map +1 -1
- package/dist/components/ui/overlays/menubar.cjs +1 -1
- package/dist/components/ui/overlays/menubar.cjs.map +1 -1
- package/dist/components/ui/overlays/menubar.js +29 -29
- package/dist/components/ui/overlays/menubar.js.map +1 -1
- package/dist/components/ui/overlays/tooltip.cjs +1 -1
- package/dist/components/ui/overlays/tooltip.cjs.map +1 -1
- package/dist/components/ui/overlays/tooltip.js +12 -12
- package/dist/components/ui/overlays/tooltip.js.map +1 -1
- package/dist/components/ui/primitives/avatar.cjs +1 -1
- package/dist/components/ui/primitives/avatar.cjs.map +1 -1
- package/dist/components/ui/primitives/avatar.js +20 -27
- package/dist/components/ui/primitives/avatar.js.map +1 -1
- package/dist/components/ui/primitives/badge.cjs +1 -1
- package/dist/components/ui/primitives/badge.cjs.map +1 -1
- package/dist/components/ui/primitives/badge.js +21 -18
- package/dist/components/ui/primitives/badge.js.map +1 -1
- package/dist/components/ui/primitives/button.cjs +1 -1
- package/dist/components/ui/primitives/button.cjs.map +1 -1
- package/dist/components/ui/primitives/button.js +11 -8
- package/dist/components/ui/primitives/button.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +34 -35
- package/dist/index.js.map +1 -1
- package/dist/preset.css +1 -2
- package/dist/toast-theme.css +100 -64
- package/dist/tokens.css +59 -159
- package/docs/README.md +44 -0
- package/docs/acoes.md +103 -0
- package/docs/escolha.md +171 -0
- package/docs/formularios.md +137 -0
- package/docs/mensagens.md +114 -0
- package/docs/overlays.md +192 -0
- package/docs/status.md +71 -0
- package/package.json +7 -3
- package/scripts/check-colors.mjs +208 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-select.cjs","sources":["../../../../src/components/ui/forms/multi-select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Check, ChevronsUpDown, X, Search, Loader2, Plus } from \"lucide-react\";\nimport { cn, mergeRefs, pressableKeyDown } from \"../utils\";\nimport { Button } from \"../primitives/button\";\nimport { Badge } from \"../primitives/badge\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../overlays/popover\";\nimport { Input } from \"./input\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n /** Slot para renderização customizada à direita do label — ex: <Badge>Externo</Badge> */\n badge?: React.ReactNode;\n}\n\nexport interface MultiSelectGroup {\n label: string;\n options: MultiSelectOption[];\n /** Quando presente, o header do grupo torna-se clicável e seleciona/deseleciona o grupo */\n value?: string;\n}\n\nexport interface MultiSelectProps {\n /** Valores atualmente selecionados */\n value: string[];\n /** Callback disparado ao alterar a seleção */\n onValueChange: (values: string[]) => void;\n\n /**\n * Opções em lista plana.\n * Use `options` OU `groups` — nunca os dois ao mesmo tempo.\n */\n options?: MultiSelectOption[];\n /**\n * Opções agrupadas hierarquicamente.\n * Use `options` OU `groups` — nunca os dois ao mesmo tempo.\n */\n groups?: MultiSelectGroup[];\n\n placeholder?: string;\n searchPlaceholder?: string;\n emptyMessage?: string;\n\n /** Rótulo acessível do gatilho quando não há `<Label>` associado */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n\n disabled?: boolean;\n /** Exibe estado de carregamento — substitui a lista por spinner */\n loading?: boolean;\n\n /**\n * Número máximo de chips visíveis no trigger antes de exibir \"+N\".\n * @default 2\n */\n maxVisibleChips?: number;\n\n /**\n * Callback de busca assíncrona — chamado a cada keystroke e ao abrir o dropdown.\n * Quando presente, a filtragem local é desativada (o caller controla `options`/`groups`).\n */\n onSearch?: (query: string) => void;\n\n /**\n * Habilita criação de uma nova opção diretamente no dropdown.\n * Chamado quando o usuário clica em \"Criar '{busca}'\" — o caller é responsável por\n * persistir a opção (ex: chamada de API), atualizar `options`/`value` com o resultado\n * e sinalizar falhas ao usuário (ex: toast). Rejeitar a promise preserva o termo\n * buscado no campo, permitindo corrigir e tentar novamente.\n * Exibido apenas quando não há opção com label idêntico ao termo buscado.\n */\n onCreateOption?: (inputValue: string) => void | Promise<void>;\n /** Customiza o label do botão de criação. @default (query) => `Criar \"${query}\"` */\n createOptionLabel?: (query: string) => string;\n\n className?: string;\n}\n\n/**\n * MultiSelect — Seleção múltipla com chips, busca, grupos hierárquicos e slots de badge por opção.\n *\n * @example\n * ```tsx\n * // Lista plana\n * <MultiSelect\n * value={selectedSquads}\n * onValueChange={setSelectedSquads}\n * options={squads.map(s => ({ value: String(s.id), label: s.name }))}\n * placeholder=\"Selecione os squads\"\n * />\n *\n * // Com badge por opção (ex: funcionários externos)\n * <MultiSelect\n * value={selectedEmployees}\n * onValueChange={setSelectedEmployees}\n * options={employees.map(e => ({\n * value: String(e.id),\n * label: e.name,\n * badge: e.isExternalUser\n * ? <Badge variant=\"secondary\" className=\"text-xs\">Externo</Badge>\n * : undefined,\n * }))}\n * />\n *\n * // Lista hierárquica (grupos de clientes)\n * <MultiSelect\n * value={selectedClients}\n * onValueChange={setSelectedClients}\n * groups={customerGroups.map(g => ({\n * label: g.name,\n * options: g.children.map(c => ({ value: String(c.id), label: c.name })),\n * }))}\n * />\n * ```\n */\nexport const MultiSelect = React.forwardRef<\n React.ElementRef<typeof Button>,\n MultiSelectProps\n>(({\n value,\n onValueChange,\n options,\n groups,\n placeholder = \"Selecione...\",\n searchPlaceholder = \"Buscar...\",\n emptyMessage = \"Nenhum resultado encontrado\",\n disabled = false,\n loading = false,\n maxVisibleChips = 2,\n onSearch,\n onCreateOption,\n createOptionLabel,\n className,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n}, ref) => {\n const [open, setOpen] = React.useState(false);\n const [search, setSearch] = React.useState(\"\");\n const [isCreating, setIsCreating] = React.useState(false);\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n const searchInputRef = React.useRef<HTMLInputElement>(null);\n const mergedTriggerRef = React.useMemo(() => mergeRefs(triggerRef, ref), [ref]);\n\n // Callback ref: chamado imediatamente ao montar o elemento (sem timing issue do useEffect).\n // Para a propagação nativa do wheel antes de chegar ao document, onde o\n // react-remove-scroll do Sheet chama preventDefault() em eventos de Portal.\n const listCallbackRef = React.useCallback((el: HTMLDivElement | null) => {\n if (!el) return;\n el.addEventListener(\"wheel\", (e) => e.stopPropagation(), { passive: true });\n }, []);\n\n const allOptions = React.useMemo((): MultiSelectOption[] => {\n if (options) return options;\n if (groups) {\n const groupHeaders: MultiSelectOption[] = groups\n .filter((g) => g.value != null)\n .map((g) => ({ value: g.value!, label: g.label }));\n return [...groupHeaders, ...groups.flatMap((g) => g.options)];\n }\n return [];\n }, [options, groups]);\n\n const isAsyncSearch = !!onSearch;\n\n const filteredOptions = React.useMemo(() => {\n if (!options) return [];\n if (isAsyncSearch) return options;\n const term = search.toLowerCase().trim();\n if (!term) return options;\n return options.filter(\n (o) =>\n o.label.toLowerCase().includes(term) ||\n o.value.toLowerCase().includes(term)\n );\n }, [options, search, isAsyncSearch]);\n\n const filteredGroups = React.useMemo(() => {\n if (!groups) return undefined;\n if (isAsyncSearch) return groups;\n const term = search.toLowerCase().trim();\n if (!term) return groups;\n return groups\n .map((g) => ({\n ...g,\n options: g.options.filter(\n (o) =>\n o.label.toLowerCase().includes(term) ||\n o.value.toLowerCase().includes(term)\n ),\n }))\n .filter((g) => g.options.length > 0);\n }, [groups, search, isAsyncSearch]);\n\n const selectedOptions = React.useMemo(\n () => allOptions.filter((o) => value.includes(o.value)),\n [allOptions, value]\n );\n\n const hasResults = groups\n ? (filteredGroups?.some((g) => g.options.length > 0) ?? false)\n : filteredOptions.length > 0;\n\n const trimmedSearch = search.trim();\n const hasExactMatch = allOptions.some(\n (o) => o.label.trim().toLowerCase() === trimmedSearch.toLowerCase()\n );\n const canCreate = !!onCreateOption && trimmedSearch.length > 0 && !hasExactMatch;\n\n const handleCreate = async () => {\n if (!onCreateOption || isCreating || !trimmedSearch) return;\n setIsCreating(true);\n try {\n await onCreateOption(trimmedSearch);\n setSearch(\"\");\n } catch {\n // Falha na criação: mantém o termo buscado para o usuário corrigir e tentar\n // novamente. O caller é responsável por sinalizar o erro (ex: toast).\n } finally {\n setIsCreating(false);\n }\n };\n\n const handleToggle = (optionValue: string) => {\n if (value.includes(optionValue)) {\n onValueChange(value.filter((v) => v !== optionValue));\n } else {\n onValueChange([...value, optionValue]);\n }\n };\n\n const handleRemoveChip = (optionValue: string, e: React.MouseEvent) => {\n e.stopPropagation();\n onValueChange(value.filter((v) => v !== optionValue));\n };\n\n const handleOpenChange = (next: boolean) => {\n if (disabled) return;\n setOpen(next);\n if (!next) setSearch(\"\");\n };\n\n React.useEffect(() => {\n if (open) onSearch?.(\"\");\n }, [open]);\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n setSearch(e.target.value);\n onSearch?.(e.target.value);\n };\n\n const visibleChips = selectedOptions.slice(0, maxVisibleChips);\n const overflowCount = selectedOptions.length - maxVisibleChips;\n\n const renderOption = (option: MultiSelectOption) => {\n const selected = value.includes(option.value);\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={selected}\n disabled={option.disabled}\n onClick={() => handleToggle(option.value)}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm\",\n \"hover:bg-accent hover:text-accent-foreground\",\n \"focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n \"transition-colors duration-100\",\n option.disabled && \"pointer-events-none opacity-50\"\n )}\n >\n <Check\n className={cn(\n \"h-4 w-4 shrink-0 transition-opacity duration-100\",\n selected ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n <span className=\"flex-1 text-left truncate\">{option.label}</span>\n {option.badge}\n </button>\n );\n };\n\n return (\n <Popover open={open} onOpenChange={handleOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={mergedTriggerRef}\n type=\"button\"\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n disabled={disabled}\n className={cn(\n \"w-full min-h-10 h-auto justify-between font-normal px-3 py-2\",\n !value.length && \"text-muted-foreground\",\n className\n )}\n >\n <div className=\"flex flex-wrap items-center gap-1 flex-1 min-w-0\">\n {value.length === 0 ? (\n <span className=\"truncate text-sm\">\n {loading ? \"Carregando...\" : placeholder}\n </span>\n ) : (\n <>\n {visibleChips.map((opt) => (\n <Badge\n key={opt.value}\n variant=\"secondary\"\n className=\"rounded-sm h-5 gap-0.5 pr-1 font-normal max-w-[160px]\"\n >\n <span className=\"truncate text-xs\">{opt.label}</span>\n <span\n role=\"button\"\n tabIndex={0}\n aria-label={`Remover ${opt.label}`}\n className=\"inline-flex items-center justify-center rounded-sm hover:bg-muted/80 cursor-pointer shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-ring/60\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => handleRemoveChip(opt.value, e)}\n onKeyDown={pressableKeyDown}\n >\n <X className=\"h-3 w-3\" />\n </span>\n </Badge>\n ))}\n {overflowCount > 0 && (\n <Badge\n variant=\"secondary\"\n className=\"rounded-sm h-5 px-1.5 font-normal text-xs shrink-0\"\n >\n +{overflowCount}\n </Badge>\n )}\n </>\n )}\n </div>\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n\n <PopoverContent\n className=\"p-0 overflow-hidden\"\n style={{\n width: triggerRef.current?.offsetWidth\n ? `${triggerRef.current.offsetWidth}px`\n : \"auto\",\n }}\n align=\"start\"\n onOpenAutoFocus={(e) => {\n // O Radix suprime o autofocus padrão quando este Popover está aninhado em um\n // Dialog/Sheet (o focus-trap do pai disputa o foco com o do Popover). Focamos\n // o input manualmente via ref assim que o conteúdo transiciona para visível.\n e.preventDefault();\n searchInputRef.current?.focus();\n }}\n onWheel={(e) => e.stopPropagation()}\n >\n {/* Busca */}\n <div className=\"flex items-center border-b px-3\">\n <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n <Input\n ref={searchInputRef}\n placeholder={searchPlaceholder}\n value={search}\n onChange={handleSearchChange}\n className=\"h-10 border-0 focus-visible:ring-0 focus-visible:ring-offset-0 px-0\"\n />\n </div>\n\n {/* Criar nova opção */}\n {canCreate && (\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={isCreating}\n className={cn(\n \"flex w-full items-center gap-2 border-b px-3 py-2 text-sm text-primary\",\n \"hover:bg-accent transition-colors duration-100\",\n \"disabled:opacity-50 disabled:pointer-events-none\"\n )}\n >\n {isCreating ? (\n <Loader2 className=\"h-4 w-4 shrink-0 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4 shrink-0\" />\n )}\n <span className=\"truncate\">\n {isCreating\n ? \"Criando...\"\n : (createOptionLabel?.(trimmedSearch) ?? `Criar \"${trimmedSearch}\"`)}\n </span>\n </button>\n )}\n\n {/* Lista */}\n <div ref={listCallbackRef} className=\"max-h-70 overflow-y-auto [&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-primary/30 [&::-webkit-scrollbar-thumb:hover]:bg-primary/50\">\n <div className=\"p-1\" role=\"listbox\" aria-multiselectable>\n {loading ? (\n <div className=\"flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n Carregando...\n </div>\n ) : !hasResults ? (\n <div className=\"py-6 text-center text-sm text-muted-foreground\">\n {emptyMessage}\n </div>\n ) : groups ? (\n filteredGroups?.map((group) => (\n <div key={group.label}>\n {group.value ? (\n <button\n type=\"button\"\n onClick={() => handleToggle(group.value!)}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5\",\n \"hover:bg-accent hover:text-accent-foreground transition-colors duration-100\",\n value.includes(group.value) && \"text-foreground\"\n )}\n >\n <Check\n className={cn(\n \"h-4 w-4 shrink-0 transition-opacity duration-100\",\n value.includes(group.value) ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n <span className=\"text-xs font-semibold uppercase tracking-wide\">\n {group.label}\n </span>\n </button>\n ) : (\n <div className=\"px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wide\">\n {group.label}\n </div>\n )}\n {group.options.map(renderOption)}\n </div>\n ))\n ) : (\n filteredOptions.map(renderOption)\n )}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n});\nMultiSelect.displayName = \"MultiSelect\";\n\nexport default MultiSelect;\n"],"names":["MultiSelect","React","value","onValueChange","options","groups","placeholder","searchPlaceholder","emptyMessage","disabled","loading","maxVisibleChips","onSearch","onCreateOption","createOptionLabel","className","ariaLabel","ariaLabelledby","ref","open","setOpen","search","setSearch","isCreating","setIsCreating","triggerRef","searchInputRef","mergedTriggerRef","mergeRefs","listCallbackRef","el","e","allOptions","g","isAsyncSearch","filteredOptions","term","o","filteredGroups","selectedOptions","hasResults","trimmedSearch","hasExactMatch","canCreate","handleCreate","handleToggle","optionValue","v","handleRemoveChip","handleOpenChange","next","handleSearchChange","visibleChips","overflowCount","renderOption","option","selected","jsxs","cn","jsx","Check","Popover","PopoverTrigger","Button","Fragment","opt","Badge","pressableKeyDown","X","ChevronsUpDown","PopoverContent","_a","Search","Input","Loader2","Plus","group"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"multi-select.cjs","sources":["../../../../src/components/ui/forms/multi-select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Check, ChevronsUpDown, X, Search, Loader2, Plus } from \"lucide-react\";\nimport { cn, mergeRefs, pressableKeyDown } from \"../utils\";\nimport { Button } from \"../primitives/button\";\nimport { Badge } from \"../primitives/badge\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../overlays/popover\";\nimport { Input } from \"./input\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n /** Slot para renderização customizada à direita do label — ex: <Badge>Externo</Badge> */\n badge?: React.ReactNode;\n}\n\nexport interface MultiSelectGroup {\n label: string;\n options: MultiSelectOption[];\n /** Quando presente, o header do grupo torna-se clicável e seleciona/deseleciona o grupo */\n value?: string;\n}\n\nexport interface MultiSelectProps {\n /** Valores atualmente selecionados */\n value: string[];\n /** Callback disparado ao alterar a seleção */\n onValueChange: (values: string[]) => void;\n\n /**\n * Opções em lista plana.\n * Use `options` OU `groups` — nunca os dois ao mesmo tempo.\n */\n options?: MultiSelectOption[];\n /**\n * Opções agrupadas hierarquicamente.\n * Use `options` OU `groups` — nunca os dois ao mesmo tempo.\n */\n groups?: MultiSelectGroup[];\n\n placeholder?: string;\n searchPlaceholder?: string;\n emptyMessage?: string;\n\n /** Rótulo acessível do gatilho quando não há `<Label>` associado */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n\n disabled?: boolean;\n /** Exibe estado de carregamento — substitui a lista por spinner */\n loading?: boolean;\n\n /**\n * Número máximo de chips visíveis no trigger antes de exibir \"+N\".\n * @default 2\n */\n maxVisibleChips?: number;\n\n /**\n * Callback de busca assíncrona — chamado a cada keystroke e ao abrir o dropdown.\n * Quando presente, a filtragem local é desativada (o caller controla `options`/`groups`).\n */\n onSearch?: (query: string) => void;\n\n /**\n * Habilita criação de uma nova opção diretamente no dropdown.\n * Chamado quando o usuário clica em \"Criar '{busca}'\" — o caller é responsável por\n * persistir a opção (ex: chamada de API), atualizar `options`/`value` com o resultado\n * e sinalizar falhas ao usuário (ex: toast). Rejeitar a promise preserva o termo\n * buscado no campo, permitindo corrigir e tentar novamente.\n * Exibido apenas quando não há opção com label idêntico ao termo buscado.\n */\n onCreateOption?: (inputValue: string) => void | Promise<void>;\n /** Customiza o label do botão de criação. @default (query) => `Criar \"${query}\"` */\n createOptionLabel?: (query: string) => string;\n\n className?: string;\n}\n\n/**\n * MultiSelect — Seleção múltipla com chips, busca, grupos hierárquicos e slots de badge por opção.\n *\n * @example\n * ```tsx\n * // Lista plana\n * <MultiSelect\n * value={selectedSquads}\n * onValueChange={setSelectedSquads}\n * options={squads.map(s => ({ value: String(s.id), label: s.name }))}\n * placeholder=\"Selecione os squads\"\n * />\n *\n * // Com badge por opção (ex: funcionários externos)\n * <MultiSelect\n * value={selectedEmployees}\n * onValueChange={setSelectedEmployees}\n * options={employees.map(e => ({\n * value: String(e.id),\n * label: e.name,\n * badge: e.isExternalUser\n * ? <Badge variant=\"secondary\" className=\"text-xs\">Externo</Badge>\n * : undefined,\n * }))}\n * />\n *\n * // Lista hierárquica (grupos de clientes)\n * <MultiSelect\n * value={selectedClients}\n * onValueChange={setSelectedClients}\n * groups={customerGroups.map(g => ({\n * label: g.name,\n * options: g.children.map(c => ({ value: String(c.id), label: c.name })),\n * }))}\n * />\n * ```\n */\nexport const MultiSelect = React.forwardRef<\n React.ElementRef<typeof Button>,\n MultiSelectProps\n>(({\n value,\n onValueChange,\n options,\n groups,\n placeholder = \"Selecione...\",\n searchPlaceholder = \"Buscar...\",\n emptyMessage = \"Nenhum resultado encontrado\",\n disabled = false,\n loading = false,\n maxVisibleChips = 2,\n onSearch,\n onCreateOption,\n createOptionLabel,\n className,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n}, ref) => {\n const [open, setOpen] = React.useState(false);\n const [search, setSearch] = React.useState(\"\");\n const [isCreating, setIsCreating] = React.useState(false);\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n const searchInputRef = React.useRef<HTMLInputElement>(null);\n const mergedTriggerRef = React.useMemo(() => mergeRefs(triggerRef, ref), [ref]);\n\n // Callback ref: chamado imediatamente ao montar o elemento (sem timing issue do useEffect).\n // Para a propagação nativa do wheel antes de chegar ao document, onde o\n // react-remove-scroll do Sheet chama preventDefault() em eventos de Portal.\n const listCallbackRef = React.useCallback((el: HTMLDivElement | null) => {\n if (!el) return;\n el.addEventListener(\"wheel\", (e) => e.stopPropagation(), { passive: true });\n }, []);\n\n const allOptions = React.useMemo((): MultiSelectOption[] => {\n if (options) return options;\n if (groups) {\n const groupHeaders: MultiSelectOption[] = groups\n .filter((g) => g.value != null)\n .map((g) => ({ value: g.value!, label: g.label }));\n return [...groupHeaders, ...groups.flatMap((g) => g.options)];\n }\n return [];\n }, [options, groups]);\n\n const isAsyncSearch = !!onSearch;\n\n const filteredOptions = React.useMemo(() => {\n if (!options) return [];\n if (isAsyncSearch) return options;\n const term = search.toLowerCase().trim();\n if (!term) return options;\n return options.filter(\n (o) =>\n o.label.toLowerCase().includes(term) ||\n o.value.toLowerCase().includes(term)\n );\n }, [options, search, isAsyncSearch]);\n\n const filteredGroups = React.useMemo(() => {\n if (!groups) return undefined;\n if (isAsyncSearch) return groups;\n const term = search.toLowerCase().trim();\n if (!term) return groups;\n return groups\n .map((g) => ({\n ...g,\n options: g.options.filter(\n (o) =>\n o.label.toLowerCase().includes(term) ||\n o.value.toLowerCase().includes(term)\n ),\n }))\n .filter((g) => g.options.length > 0);\n }, [groups, search, isAsyncSearch]);\n\n const selectedOptions = React.useMemo(\n () => allOptions.filter((o) => value.includes(o.value)),\n [allOptions, value]\n );\n\n const hasResults = groups\n ? (filteredGroups?.some((g) => g.options.length > 0) ?? false)\n : filteredOptions.length > 0;\n\n const trimmedSearch = search.trim();\n const hasExactMatch = allOptions.some(\n (o) => o.label.trim().toLowerCase() === trimmedSearch.toLowerCase()\n );\n const canCreate = !!onCreateOption && trimmedSearch.length > 0 && !hasExactMatch;\n\n const handleCreate = async () => {\n if (!onCreateOption || isCreating || !trimmedSearch) return;\n setIsCreating(true);\n try {\n await onCreateOption(trimmedSearch);\n setSearch(\"\");\n } catch {\n // Falha na criação: mantém o termo buscado para o usuário corrigir e tentar\n // novamente. O caller é responsável por sinalizar o erro (ex: toast).\n } finally {\n setIsCreating(false);\n }\n };\n\n const handleToggle = (optionValue: string) => {\n if (value.includes(optionValue)) {\n onValueChange(value.filter((v) => v !== optionValue));\n } else {\n onValueChange([...value, optionValue]);\n }\n };\n\n const handleRemoveChip = (optionValue: string, e: React.MouseEvent) => {\n e.stopPropagation();\n onValueChange(value.filter((v) => v !== optionValue));\n };\n\n const handleOpenChange = (next: boolean) => {\n if (disabled) return;\n setOpen(next);\n if (!next) setSearch(\"\");\n };\n\n React.useEffect(() => {\n if (open) onSearch?.(\"\");\n }, [open]);\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n setSearch(e.target.value);\n onSearch?.(e.target.value);\n };\n\n const visibleChips = selectedOptions.slice(0, maxVisibleChips);\n const overflowCount = selectedOptions.length - maxVisibleChips;\n\n const renderOption = (option: MultiSelectOption) => {\n const selected = value.includes(option.value);\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={selected}\n disabled={option.disabled}\n onClick={() => handleToggle(option.value)}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm\",\n \"aria-selected:bg-secondary aria-selected:text-secondary-foreground aria-selected:font-medium\",\n \"not-aria-selected:hover:bg-accent not-aria-selected:hover:text-accent-foreground\",\n \"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\",\n \"transition-colors duration-100\",\n option.disabled && \"pointer-events-none opacity-50\"\n )}\n >\n <Check\n className={cn(\n \"h-4 w-4 shrink-0 transition-opacity duration-100\",\n selected ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n <span className=\"flex-1 text-left truncate\">{option.label}</span>\n {option.badge}\n </button>\n );\n };\n\n return (\n <Popover open={open} onOpenChange={handleOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={mergedTriggerRef}\n type=\"button\"\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n disabled={disabled}\n className={cn(\n \"w-full min-h-10 h-auto justify-between font-normal px-3 py-2\",\n !value.length && \"text-muted-foreground\",\n className\n )}\n >\n <div className=\"flex flex-wrap items-center gap-1 flex-1 min-w-0\">\n {value.length === 0 ? (\n <span className=\"truncate text-sm\">\n {loading ? \"Carregando...\" : placeholder}\n </span>\n ) : (\n <>\n {visibleChips.map((opt) => (\n <Badge\n key={opt.value}\n variant=\"secondary\"\n className=\"rounded-sm h-5 gap-0.5 pr-1 font-normal max-w-[160px]\"\n >\n <span className=\"truncate text-xs\">{opt.label}</span>\n <span\n role=\"button\"\n tabIndex={0}\n aria-label={`Remover ${opt.label}`}\n className=\"inline-flex items-center justify-center rounded-sm hover:bg-muted/80 cursor-pointer shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-ring/60\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => handleRemoveChip(opt.value, e)}\n onKeyDown={pressableKeyDown}\n >\n <X className=\"h-3 w-3\" />\n </span>\n </Badge>\n ))}\n {overflowCount > 0 && (\n <Badge\n variant=\"secondary\"\n className=\"rounded-sm h-5 px-1.5 font-normal text-xs shrink-0\"\n >\n +{overflowCount}\n </Badge>\n )}\n </>\n )}\n </div>\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n\n <PopoverContent\n className=\"p-0 overflow-hidden\"\n style={{\n width: triggerRef.current?.offsetWidth\n ? `${triggerRef.current.offsetWidth}px`\n : \"auto\",\n }}\n align=\"start\"\n onOpenAutoFocus={(e) => {\n // O Radix suprime o autofocus padrão quando este Popover está aninhado em um\n // Dialog/Sheet (o focus-trap do pai disputa o foco com o do Popover). Focamos\n // o input manualmente via ref assim que o conteúdo transiciona para visível.\n e.preventDefault();\n searchInputRef.current?.focus();\n }}\n onWheel={(e) => e.stopPropagation()}\n >\n {/* Busca */}\n <div className=\"flex items-center border-b px-3\">\n <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n <Input\n ref={searchInputRef}\n placeholder={searchPlaceholder}\n value={search}\n onChange={handleSearchChange}\n className=\"h-10 border-0 focus-visible:ring-0 focus-visible:ring-offset-0 px-0\"\n />\n </div>\n\n {/* Criar nova opção */}\n {canCreate && (\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={isCreating}\n className={cn(\n \"flex w-full items-center gap-2 border-b px-3 py-2 text-sm text-primary\",\n // hover neutro: roxo sobre accent (gray-600) no escuro dá 4,4:1\n \"hover:bg-accent hover:text-accent-foreground transition-colors duration-100\",\n \"disabled:opacity-50 disabled:pointer-events-none\"\n )}\n >\n {isCreating ? (\n <Loader2 className=\"h-4 w-4 shrink-0 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4 shrink-0\" />\n )}\n <span className=\"truncate\">\n {isCreating\n ? \"Criando...\"\n : (createOptionLabel?.(trimmedSearch) ?? `Criar \"${trimmedSearch}\"`)}\n </span>\n </button>\n )}\n\n {/* Lista */}\n <div ref={listCallbackRef} className=\"max-h-70 overflow-y-auto [&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-primary/30 [&::-webkit-scrollbar-thumb:hover]:bg-primary/50\">\n <div className=\"p-1\" role=\"listbox\" aria-multiselectable>\n {loading ? (\n <div className=\"flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n Carregando...\n </div>\n ) : !hasResults ? (\n <div className=\"py-6 text-center text-sm text-muted-foreground\">\n {emptyMessage}\n </div>\n ) : groups ? (\n filteredGroups?.map((group) => (\n <div key={group.label}>\n {group.value ? (\n <button\n type=\"button\"\n onClick={() => handleToggle(group.value!)}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5\",\n \"hover:bg-accent hover:text-accent-foreground transition-colors duration-100\",\n value.includes(group.value) && \"text-foreground\"\n )}\n >\n <Check\n className={cn(\n \"h-4 w-4 shrink-0 transition-opacity duration-100\",\n value.includes(group.value) ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n <span className=\"text-xs font-semibold uppercase tracking-wide\">\n {group.label}\n </span>\n </button>\n ) : (\n <div className=\"px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wide\">\n {group.label}\n </div>\n )}\n {group.options.map(renderOption)}\n </div>\n ))\n ) : (\n filteredOptions.map(renderOption)\n )}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n});\nMultiSelect.displayName = \"MultiSelect\";\n\nexport default MultiSelect;\n"],"names":["MultiSelect","React","value","onValueChange","options","groups","placeholder","searchPlaceholder","emptyMessage","disabled","loading","maxVisibleChips","onSearch","onCreateOption","createOptionLabel","className","ariaLabel","ariaLabelledby","ref","open","setOpen","search","setSearch","isCreating","setIsCreating","triggerRef","searchInputRef","mergedTriggerRef","mergeRefs","listCallbackRef","el","e","allOptions","g","isAsyncSearch","filteredOptions","term","o","filteredGroups","selectedOptions","hasResults","trimmedSearch","hasExactMatch","canCreate","handleCreate","handleToggle","optionValue","v","handleRemoveChip","handleOpenChange","next","handleSearchChange","visibleChips","overflowCount","renderOption","option","selected","jsxs","cn","jsx","Check","Popover","PopoverTrigger","Button","Fragment","opt","Badge","pressableKeyDown","X","ChevronsUpDown","PopoverContent","_a","Search","Input","Loader2","Plus","group"],"mappings":"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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as
|
|
3
|
-
import * as
|
|
4
|
-
import { X as
|
|
5
|
-
import { mergeRefs as le, pressableKeyDown as ie, cn as
|
|
2
|
+
import { jsxs as s, jsx as r, Fragment as te } from "react/jsx-runtime";
|
|
3
|
+
import * as a from "react";
|
|
4
|
+
import { X as re, ChevronsUpDown as ae, Search as se, Loader2 as W, Plus as ne, Check as $ } from "lucide-react";
|
|
5
|
+
import { mergeRefs as le, pressableKeyDown as ie, cn as m } from "../utils.js";
|
|
6
6
|
import { Button as oe } from "../primitives/button.js";
|
|
7
7
|
import { Badge as A } from "../primitives/badge.js";
|
|
8
|
-
import { Popover as ce, PopoverTrigger as de, PopoverContent as
|
|
9
|
-
import { Input as
|
|
10
|
-
const fe =
|
|
11
|
-
value:
|
|
8
|
+
import { Popover as ce, PopoverTrigger as de, PopoverContent as ue } from "../overlays/popover.js";
|
|
9
|
+
import { Input as me } from "./input.js";
|
|
10
|
+
const fe = a.forwardRef(({
|
|
11
|
+
value: n,
|
|
12
12
|
onValueChange: x,
|
|
13
13
|
options: i,
|
|
14
14
|
groups: l,
|
|
@@ -26,55 +26,55 @@ const fe = r.forwardRef(({
|
|
|
26
26
|
"aria-labelledby": U
|
|
27
27
|
}, M) => {
|
|
28
28
|
var T;
|
|
29
|
-
const [f, X] =
|
|
29
|
+
const [f, X] = a.useState(!1), [c, w] = a.useState(""), [h, P] = a.useState(!1), y = a.useRef(null), D = a.useRef(null), q = a.useMemo(() => le(y, M), [M]), z = a.useCallback((e) => {
|
|
30
30
|
e && e.addEventListener("wheel", (t) => t.stopPropagation(), { passive: !0 });
|
|
31
|
-
}, []), N =
|
|
31
|
+
}, []), N = a.useMemo(() => i || (l ? [...l.filter((t) => t.value != null).map((t) => ({ value: t.value, label: t.label })), ...l.flatMap((t) => t.options)] : []), [i, l]), b = !!o, L = a.useMemo(() => {
|
|
32
32
|
if (!i) return [];
|
|
33
|
-
if (
|
|
33
|
+
if (b) return i;
|
|
34
34
|
const e = c.toLowerCase().trim();
|
|
35
35
|
return e ? i.filter(
|
|
36
36
|
(t) => t.label.toLowerCase().includes(e) || t.value.toLowerCase().includes(e)
|
|
37
37
|
) : i;
|
|
38
|
-
}, [i, c,
|
|
38
|
+
}, [i, c, b]), d = a.useMemo(() => {
|
|
39
39
|
if (!l) return;
|
|
40
|
-
if (
|
|
40
|
+
if (b) return l;
|
|
41
41
|
const e = c.toLowerCase().trim();
|
|
42
42
|
return e ? l.map((t) => ({
|
|
43
43
|
...t,
|
|
44
44
|
options: t.options.filter(
|
|
45
|
-
(
|
|
45
|
+
(p) => p.label.toLowerCase().includes(e) || p.value.toLowerCase().includes(e)
|
|
46
46
|
)
|
|
47
47
|
})).filter((t) => t.options.length > 0) : l;
|
|
48
|
-
}, [l, c,
|
|
49
|
-
() => N.filter((e) =>
|
|
50
|
-
[N,
|
|
51
|
-
), J = l ? (d == null ? void 0 : d.some((e) => e.options.length > 0)) ?? !1 : L.length > 0,
|
|
52
|
-
(e) => e.label.trim().toLowerCase() ===
|
|
53
|
-
), Y = !!v &&
|
|
54
|
-
if (!(!v || h || !
|
|
48
|
+
}, [l, c, b]), j = a.useMemo(
|
|
49
|
+
() => N.filter((e) => n.includes(e.value)),
|
|
50
|
+
[N, n]
|
|
51
|
+
), J = l ? (d == null ? void 0 : d.some((e) => e.options.length > 0)) ?? !1 : L.length > 0, u = c.trim(), Q = N.some(
|
|
52
|
+
(e) => e.label.trim().toLowerCase() === u.toLowerCase()
|
|
53
|
+
), Y = !!v && u.length > 0 && !Q, Z = async () => {
|
|
54
|
+
if (!(!v || h || !u)) {
|
|
55
55
|
P(!0);
|
|
56
56
|
try {
|
|
57
|
-
await v(
|
|
57
|
+
await v(u), w("");
|
|
58
58
|
} catch {
|
|
59
59
|
} finally {
|
|
60
60
|
P(!1);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}, I = (e) => {
|
|
64
|
-
|
|
64
|
+
n.includes(e) ? x(n.filter((t) => t !== e)) : x([...n, e]);
|
|
65
65
|
}, _ = (e, t) => {
|
|
66
|
-
t.stopPropagation(), x(
|
|
66
|
+
t.stopPropagation(), x(n.filter((p) => p !== e));
|
|
67
67
|
}, G = (e) => {
|
|
68
68
|
C || (X(e), e || w(""));
|
|
69
69
|
};
|
|
70
|
-
|
|
70
|
+
a.useEffect(() => {
|
|
71
71
|
f && (o == null || o(""));
|
|
72
72
|
}, [f]);
|
|
73
73
|
const V = (e) => {
|
|
74
74
|
w(e.target.value), o == null || o(e.target.value);
|
|
75
75
|
}, ee = j.slice(0, R), B = j.length - R, E = (e) => {
|
|
76
|
-
const t =
|
|
77
|
-
return /* @__PURE__ */
|
|
76
|
+
const t = n.includes(e.value);
|
|
77
|
+
return /* @__PURE__ */ s(
|
|
78
78
|
"button",
|
|
79
79
|
{
|
|
80
80
|
type: "button",
|
|
@@ -82,32 +82,33 @@ const fe = r.forwardRef(({
|
|
|
82
82
|
"aria-selected": t,
|
|
83
83
|
disabled: e.disabled,
|
|
84
84
|
onClick: () => I(e.value),
|
|
85
|
-
className:
|
|
85
|
+
className: m(
|
|
86
86
|
"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm",
|
|
87
|
-
"
|
|
88
|
-
"
|
|
87
|
+
"aria-selected:bg-secondary aria-selected:text-secondary-foreground aria-selected:font-medium",
|
|
88
|
+
"not-aria-selected:hover:bg-accent not-aria-selected:hover:text-accent-foreground",
|
|
89
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
|
|
89
90
|
"transition-colors duration-100",
|
|
90
91
|
e.disabled && "pointer-events-none opacity-50"
|
|
91
92
|
),
|
|
92
93
|
children: [
|
|
93
|
-
/* @__PURE__ */
|
|
94
|
+
/* @__PURE__ */ r(
|
|
94
95
|
$,
|
|
95
96
|
{
|
|
96
|
-
className:
|
|
97
|
+
className: m(
|
|
97
98
|
"h-4 w-4 shrink-0 transition-opacity duration-100",
|
|
98
99
|
t ? "opacity-100" : "opacity-0"
|
|
99
100
|
)
|
|
100
101
|
}
|
|
101
102
|
),
|
|
102
|
-
/* @__PURE__ */
|
|
103
|
+
/* @__PURE__ */ r("span", { className: "flex-1 text-left truncate", children: e.label }),
|
|
103
104
|
e.badge
|
|
104
105
|
]
|
|
105
106
|
},
|
|
106
107
|
e.value
|
|
107
108
|
);
|
|
108
109
|
};
|
|
109
|
-
return /* @__PURE__ */
|
|
110
|
-
/* @__PURE__ */
|
|
110
|
+
return /* @__PURE__ */ s(ce, { open: f, onOpenChange: G, children: [
|
|
111
|
+
/* @__PURE__ */ r(de, { asChild: !0, children: /* @__PURE__ */ s(
|
|
111
112
|
oe,
|
|
112
113
|
{
|
|
113
114
|
ref: q,
|
|
@@ -118,21 +119,21 @@ const fe = r.forwardRef(({
|
|
|
118
119
|
"aria-label": S,
|
|
119
120
|
"aria-labelledby": U,
|
|
120
121
|
disabled: C,
|
|
121
|
-
className:
|
|
122
|
+
className: m(
|
|
122
123
|
"w-full min-h-10 h-auto justify-between font-normal px-3 py-2",
|
|
123
|
-
!
|
|
124
|
+
!n.length && "text-muted-foreground",
|
|
124
125
|
O
|
|
125
126
|
),
|
|
126
127
|
children: [
|
|
127
|
-
/* @__PURE__ */
|
|
128
|
-
ee.map((e) => /* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ r("div", { className: "flex flex-wrap items-center gap-1 flex-1 min-w-0", children: n.length === 0 ? /* @__PURE__ */ r("span", { className: "truncate text-sm", children: k ? "Carregando..." : F }) : /* @__PURE__ */ s(te, { children: [
|
|
129
|
+
ee.map((e) => /* @__PURE__ */ s(
|
|
129
130
|
A,
|
|
130
131
|
{
|
|
131
132
|
variant: "secondary",
|
|
132
133
|
className: "rounded-sm h-5 gap-0.5 pr-1 font-normal max-w-[160px]",
|
|
133
134
|
children: [
|
|
134
|
-
/* @__PURE__ */
|
|
135
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ r("span", { className: "truncate text-xs", children: e.label }),
|
|
136
|
+
/* @__PURE__ */ r(
|
|
136
137
|
"span",
|
|
137
138
|
{
|
|
138
139
|
role: "button",
|
|
@@ -142,14 +143,14 @@ const fe = r.forwardRef(({
|
|
|
142
143
|
onMouseDown: (t) => t.preventDefault(),
|
|
143
144
|
onClick: (t) => _(e.value, t),
|
|
144
145
|
onKeyDown: ie,
|
|
145
|
-
children: /* @__PURE__ */
|
|
146
|
+
children: /* @__PURE__ */ r(re, { className: "h-3 w-3" })
|
|
146
147
|
}
|
|
147
148
|
)
|
|
148
149
|
]
|
|
149
150
|
},
|
|
150
151
|
e.value
|
|
151
152
|
)),
|
|
152
|
-
B > 0 && /* @__PURE__ */
|
|
153
|
+
B > 0 && /* @__PURE__ */ s(
|
|
153
154
|
A,
|
|
154
155
|
{
|
|
155
156
|
variant: "secondary",
|
|
@@ -161,12 +162,12 @@ const fe = r.forwardRef(({
|
|
|
161
162
|
}
|
|
162
163
|
)
|
|
163
164
|
] }) }),
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ r(ae, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })
|
|
165
166
|
]
|
|
166
167
|
}
|
|
167
168
|
) }),
|
|
168
|
-
/* @__PURE__ */
|
|
169
|
-
|
|
169
|
+
/* @__PURE__ */ s(
|
|
170
|
+
ue,
|
|
170
171
|
{
|
|
171
172
|
className: "p-0 overflow-hidden",
|
|
172
173
|
style: {
|
|
@@ -179,10 +180,10 @@ const fe = r.forwardRef(({
|
|
|
179
180
|
},
|
|
180
181
|
onWheel: (e) => e.stopPropagation(),
|
|
181
182
|
children: [
|
|
182
|
-
/* @__PURE__ */
|
|
183
|
-
/* @__PURE__ */
|
|
184
|
-
/* @__PURE__ */
|
|
185
|
-
|
|
183
|
+
/* @__PURE__ */ s("div", { className: "flex items-center border-b px-3", children: [
|
|
184
|
+
/* @__PURE__ */ r(se, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
|
|
185
|
+
/* @__PURE__ */ r(
|
|
186
|
+
me,
|
|
186
187
|
{
|
|
187
188
|
ref: D,
|
|
188
189
|
placeholder: H,
|
|
@@ -192,53 +193,54 @@ const fe = r.forwardRef(({
|
|
|
192
193
|
}
|
|
193
194
|
)
|
|
194
195
|
] }),
|
|
195
|
-
Y && /* @__PURE__ */
|
|
196
|
+
Y && /* @__PURE__ */ s(
|
|
196
197
|
"button",
|
|
197
198
|
{
|
|
198
199
|
type: "button",
|
|
199
200
|
onClick: Z,
|
|
200
201
|
disabled: h,
|
|
201
|
-
className:
|
|
202
|
+
className: m(
|
|
202
203
|
"flex w-full items-center gap-2 border-b px-3 py-2 text-sm text-primary",
|
|
203
|
-
|
|
204
|
+
// hover neutro: roxo sobre accent (gray-600) no escuro dá 4,4:1
|
|
205
|
+
"hover:bg-accent hover:text-accent-foreground transition-colors duration-100",
|
|
204
206
|
"disabled:opacity-50 disabled:pointer-events-none"
|
|
205
207
|
),
|
|
206
208
|
children: [
|
|
207
|
-
h ? /* @__PURE__ */
|
|
208
|
-
/* @__PURE__ */
|
|
209
|
+
h ? /* @__PURE__ */ r(W, { className: "h-4 w-4 shrink-0 animate-spin" }) : /* @__PURE__ */ r(ne, { className: "h-4 w-4 shrink-0" }),
|
|
210
|
+
/* @__PURE__ */ r("span", { className: "truncate", children: h ? "Criando..." : (g == null ? void 0 : g(u)) ?? `Criar "${u}"` })
|
|
209
211
|
]
|
|
210
212
|
}
|
|
211
213
|
),
|
|
212
|
-
/* @__PURE__ */
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
+
/* @__PURE__ */ r("div", { ref: z, className: "max-h-70 overflow-y-auto [&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-primary/30 [&::-webkit-scrollbar-thumb:hover]:bg-primary/50", children: /* @__PURE__ */ r("div", { className: "p-1", role: "listbox", "aria-multiselectable": !0, children: k ? /* @__PURE__ */ s("div", { className: "flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground", children: [
|
|
215
|
+
/* @__PURE__ */ r(W, { className: "h-4 w-4 animate-spin" }),
|
|
214
216
|
"Carregando..."
|
|
215
|
-
] }) : J ? l ? d == null ? void 0 : d.map((e) => /* @__PURE__ */
|
|
216
|
-
e.value ? /* @__PURE__ */
|
|
217
|
+
] }) : J ? l ? d == null ? void 0 : d.map((e) => /* @__PURE__ */ s("div", { children: [
|
|
218
|
+
e.value ? /* @__PURE__ */ s(
|
|
217
219
|
"button",
|
|
218
220
|
{
|
|
219
221
|
type: "button",
|
|
220
222
|
onClick: () => I(e.value),
|
|
221
|
-
className:
|
|
223
|
+
className: m(
|
|
222
224
|
"flex w-full items-center gap-2 rounded-sm px-2 py-1.5",
|
|
223
225
|
"hover:bg-accent hover:text-accent-foreground transition-colors duration-100",
|
|
224
|
-
|
|
226
|
+
n.includes(e.value) && "text-foreground"
|
|
225
227
|
),
|
|
226
228
|
children: [
|
|
227
|
-
/* @__PURE__ */
|
|
229
|
+
/* @__PURE__ */ r(
|
|
228
230
|
$,
|
|
229
231
|
{
|
|
230
|
-
className:
|
|
232
|
+
className: m(
|
|
231
233
|
"h-4 w-4 shrink-0 transition-opacity duration-100",
|
|
232
|
-
|
|
234
|
+
n.includes(e.value) ? "opacity-100" : "opacity-0"
|
|
233
235
|
)
|
|
234
236
|
}
|
|
235
237
|
),
|
|
236
|
-
/* @__PURE__ */
|
|
238
|
+
/* @__PURE__ */ r("span", { className: "text-xs font-semibold uppercase tracking-wide", children: e.label })
|
|
237
239
|
]
|
|
238
240
|
}
|
|
239
|
-
) : /* @__PURE__ */
|
|
241
|
+
) : /* @__PURE__ */ r("div", { className: "px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: e.label }),
|
|
240
242
|
e.options.map(E)
|
|
241
|
-
] }, e.label)) : L.map(E) : /* @__PURE__ */
|
|
243
|
+
] }, e.label)) : L.map(E) : /* @__PURE__ */ r("div", { className: "py-6 text-center text-sm text-muted-foreground", children: K }) }) })
|
|
242
244
|
]
|
|
243
245
|
}
|
|
244
246
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-select.js","sources":["../../../../src/components/ui/forms/multi-select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Check, ChevronsUpDown, X, Search, Loader2, Plus } from \"lucide-react\";\nimport { cn, mergeRefs, pressableKeyDown } from \"../utils\";\nimport { Button } from \"../primitives/button\";\nimport { Badge } from \"../primitives/badge\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../overlays/popover\";\nimport { Input } from \"./input\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n /** Slot para renderização customizada à direita do label — ex: <Badge>Externo</Badge> */\n badge?: React.ReactNode;\n}\n\nexport interface MultiSelectGroup {\n label: string;\n options: MultiSelectOption[];\n /** Quando presente, o header do grupo torna-se clicável e seleciona/deseleciona o grupo */\n value?: string;\n}\n\nexport interface MultiSelectProps {\n /** Valores atualmente selecionados */\n value: string[];\n /** Callback disparado ao alterar a seleção */\n onValueChange: (values: string[]) => void;\n\n /**\n * Opções em lista plana.\n * Use `options` OU `groups` — nunca os dois ao mesmo tempo.\n */\n options?: MultiSelectOption[];\n /**\n * Opções agrupadas hierarquicamente.\n * Use `options` OU `groups` — nunca os dois ao mesmo tempo.\n */\n groups?: MultiSelectGroup[];\n\n placeholder?: string;\n searchPlaceholder?: string;\n emptyMessage?: string;\n\n /** Rótulo acessível do gatilho quando não há `<Label>` associado */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n\n disabled?: boolean;\n /** Exibe estado de carregamento — substitui a lista por spinner */\n loading?: boolean;\n\n /**\n * Número máximo de chips visíveis no trigger antes de exibir \"+N\".\n * @default 2\n */\n maxVisibleChips?: number;\n\n /**\n * Callback de busca assíncrona — chamado a cada keystroke e ao abrir o dropdown.\n * Quando presente, a filtragem local é desativada (o caller controla `options`/`groups`).\n */\n onSearch?: (query: string) => void;\n\n /**\n * Habilita criação de uma nova opção diretamente no dropdown.\n * Chamado quando o usuário clica em \"Criar '{busca}'\" — o caller é responsável por\n * persistir a opção (ex: chamada de API), atualizar `options`/`value` com o resultado\n * e sinalizar falhas ao usuário (ex: toast). Rejeitar a promise preserva o termo\n * buscado no campo, permitindo corrigir e tentar novamente.\n * Exibido apenas quando não há opção com label idêntico ao termo buscado.\n */\n onCreateOption?: (inputValue: string) => void | Promise<void>;\n /** Customiza o label do botão de criação. @default (query) => `Criar \"${query}\"` */\n createOptionLabel?: (query: string) => string;\n\n className?: string;\n}\n\n/**\n * MultiSelect — Seleção múltipla com chips, busca, grupos hierárquicos e slots de badge por opção.\n *\n * @example\n * ```tsx\n * // Lista plana\n * <MultiSelect\n * value={selectedSquads}\n * onValueChange={setSelectedSquads}\n * options={squads.map(s => ({ value: String(s.id), label: s.name }))}\n * placeholder=\"Selecione os squads\"\n * />\n *\n * // Com badge por opção (ex: funcionários externos)\n * <MultiSelect\n * value={selectedEmployees}\n * onValueChange={setSelectedEmployees}\n * options={employees.map(e => ({\n * value: String(e.id),\n * label: e.name,\n * badge: e.isExternalUser\n * ? <Badge variant=\"secondary\" className=\"text-xs\">Externo</Badge>\n * : undefined,\n * }))}\n * />\n *\n * // Lista hierárquica (grupos de clientes)\n * <MultiSelect\n * value={selectedClients}\n * onValueChange={setSelectedClients}\n * groups={customerGroups.map(g => ({\n * label: g.name,\n * options: g.children.map(c => ({ value: String(c.id), label: c.name })),\n * }))}\n * />\n * ```\n */\nexport const MultiSelect = React.forwardRef<\n React.ElementRef<typeof Button>,\n MultiSelectProps\n>(({\n value,\n onValueChange,\n options,\n groups,\n placeholder = \"Selecione...\",\n searchPlaceholder = \"Buscar...\",\n emptyMessage = \"Nenhum resultado encontrado\",\n disabled = false,\n loading = false,\n maxVisibleChips = 2,\n onSearch,\n onCreateOption,\n createOptionLabel,\n className,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n}, ref) => {\n const [open, setOpen] = React.useState(false);\n const [search, setSearch] = React.useState(\"\");\n const [isCreating, setIsCreating] = React.useState(false);\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n const searchInputRef = React.useRef<HTMLInputElement>(null);\n const mergedTriggerRef = React.useMemo(() => mergeRefs(triggerRef, ref), [ref]);\n\n // Callback ref: chamado imediatamente ao montar o elemento (sem timing issue do useEffect).\n // Para a propagação nativa do wheel antes de chegar ao document, onde o\n // react-remove-scroll do Sheet chama preventDefault() em eventos de Portal.\n const listCallbackRef = React.useCallback((el: HTMLDivElement | null) => {\n if (!el) return;\n el.addEventListener(\"wheel\", (e) => e.stopPropagation(), { passive: true });\n }, []);\n\n const allOptions = React.useMemo((): MultiSelectOption[] => {\n if (options) return options;\n if (groups) {\n const groupHeaders: MultiSelectOption[] = groups\n .filter((g) => g.value != null)\n .map((g) => ({ value: g.value!, label: g.label }));\n return [...groupHeaders, ...groups.flatMap((g) => g.options)];\n }\n return [];\n }, [options, groups]);\n\n const isAsyncSearch = !!onSearch;\n\n const filteredOptions = React.useMemo(() => {\n if (!options) return [];\n if (isAsyncSearch) return options;\n const term = search.toLowerCase().trim();\n if (!term) return options;\n return options.filter(\n (o) =>\n o.label.toLowerCase().includes(term) ||\n o.value.toLowerCase().includes(term)\n );\n }, [options, search, isAsyncSearch]);\n\n const filteredGroups = React.useMemo(() => {\n if (!groups) return undefined;\n if (isAsyncSearch) return groups;\n const term = search.toLowerCase().trim();\n if (!term) return groups;\n return groups\n .map((g) => ({\n ...g,\n options: g.options.filter(\n (o) =>\n o.label.toLowerCase().includes(term) ||\n o.value.toLowerCase().includes(term)\n ),\n }))\n .filter((g) => g.options.length > 0);\n }, [groups, search, isAsyncSearch]);\n\n const selectedOptions = React.useMemo(\n () => allOptions.filter((o) => value.includes(o.value)),\n [allOptions, value]\n );\n\n const hasResults = groups\n ? (filteredGroups?.some((g) => g.options.length > 0) ?? false)\n : filteredOptions.length > 0;\n\n const trimmedSearch = search.trim();\n const hasExactMatch = allOptions.some(\n (o) => o.label.trim().toLowerCase() === trimmedSearch.toLowerCase()\n );\n const canCreate = !!onCreateOption && trimmedSearch.length > 0 && !hasExactMatch;\n\n const handleCreate = async () => {\n if (!onCreateOption || isCreating || !trimmedSearch) return;\n setIsCreating(true);\n try {\n await onCreateOption(trimmedSearch);\n setSearch(\"\");\n } catch {\n // Falha na criação: mantém o termo buscado para o usuário corrigir e tentar\n // novamente. O caller é responsável por sinalizar o erro (ex: toast).\n } finally {\n setIsCreating(false);\n }\n };\n\n const handleToggle = (optionValue: string) => {\n if (value.includes(optionValue)) {\n onValueChange(value.filter((v) => v !== optionValue));\n } else {\n onValueChange([...value, optionValue]);\n }\n };\n\n const handleRemoveChip = (optionValue: string, e: React.MouseEvent) => {\n e.stopPropagation();\n onValueChange(value.filter((v) => v !== optionValue));\n };\n\n const handleOpenChange = (next: boolean) => {\n if (disabled) return;\n setOpen(next);\n if (!next) setSearch(\"\");\n };\n\n React.useEffect(() => {\n if (open) onSearch?.(\"\");\n }, [open]);\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n setSearch(e.target.value);\n onSearch?.(e.target.value);\n };\n\n const visibleChips = selectedOptions.slice(0, maxVisibleChips);\n const overflowCount = selectedOptions.length - maxVisibleChips;\n\n const renderOption = (option: MultiSelectOption) => {\n const selected = value.includes(option.value);\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={selected}\n disabled={option.disabled}\n onClick={() => handleToggle(option.value)}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm\",\n \"hover:bg-accent hover:text-accent-foreground\",\n \"focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n \"transition-colors duration-100\",\n option.disabled && \"pointer-events-none opacity-50\"\n )}\n >\n <Check\n className={cn(\n \"h-4 w-4 shrink-0 transition-opacity duration-100\",\n selected ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n <span className=\"flex-1 text-left truncate\">{option.label}</span>\n {option.badge}\n </button>\n );\n };\n\n return (\n <Popover open={open} onOpenChange={handleOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={mergedTriggerRef}\n type=\"button\"\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n disabled={disabled}\n className={cn(\n \"w-full min-h-10 h-auto justify-between font-normal px-3 py-2\",\n !value.length && \"text-muted-foreground\",\n className\n )}\n >\n <div className=\"flex flex-wrap items-center gap-1 flex-1 min-w-0\">\n {value.length === 0 ? (\n <span className=\"truncate text-sm\">\n {loading ? \"Carregando...\" : placeholder}\n </span>\n ) : (\n <>\n {visibleChips.map((opt) => (\n <Badge\n key={opt.value}\n variant=\"secondary\"\n className=\"rounded-sm h-5 gap-0.5 pr-1 font-normal max-w-[160px]\"\n >\n <span className=\"truncate text-xs\">{opt.label}</span>\n <span\n role=\"button\"\n tabIndex={0}\n aria-label={`Remover ${opt.label}`}\n className=\"inline-flex items-center justify-center rounded-sm hover:bg-muted/80 cursor-pointer shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-ring/60\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => handleRemoveChip(opt.value, e)}\n onKeyDown={pressableKeyDown}\n >\n <X className=\"h-3 w-3\" />\n </span>\n </Badge>\n ))}\n {overflowCount > 0 && (\n <Badge\n variant=\"secondary\"\n className=\"rounded-sm h-5 px-1.5 font-normal text-xs shrink-0\"\n >\n +{overflowCount}\n </Badge>\n )}\n </>\n )}\n </div>\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n\n <PopoverContent\n className=\"p-0 overflow-hidden\"\n style={{\n width: triggerRef.current?.offsetWidth\n ? `${triggerRef.current.offsetWidth}px`\n : \"auto\",\n }}\n align=\"start\"\n onOpenAutoFocus={(e) => {\n // O Radix suprime o autofocus padrão quando este Popover está aninhado em um\n // Dialog/Sheet (o focus-trap do pai disputa o foco com o do Popover). Focamos\n // o input manualmente via ref assim que o conteúdo transiciona para visível.\n e.preventDefault();\n searchInputRef.current?.focus();\n }}\n onWheel={(e) => e.stopPropagation()}\n >\n {/* Busca */}\n <div className=\"flex items-center border-b px-3\">\n <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n <Input\n ref={searchInputRef}\n placeholder={searchPlaceholder}\n value={search}\n onChange={handleSearchChange}\n className=\"h-10 border-0 focus-visible:ring-0 focus-visible:ring-offset-0 px-0\"\n />\n </div>\n\n {/* Criar nova opção */}\n {canCreate && (\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={isCreating}\n className={cn(\n \"flex w-full items-center gap-2 border-b px-3 py-2 text-sm text-primary\",\n \"hover:bg-accent transition-colors duration-100\",\n \"disabled:opacity-50 disabled:pointer-events-none\"\n )}\n >\n {isCreating ? (\n <Loader2 className=\"h-4 w-4 shrink-0 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4 shrink-0\" />\n )}\n <span className=\"truncate\">\n {isCreating\n ? \"Criando...\"\n : (createOptionLabel?.(trimmedSearch) ?? `Criar \"${trimmedSearch}\"`)}\n </span>\n </button>\n )}\n\n {/* Lista */}\n <div ref={listCallbackRef} className=\"max-h-70 overflow-y-auto [&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-primary/30 [&::-webkit-scrollbar-thumb:hover]:bg-primary/50\">\n <div className=\"p-1\" role=\"listbox\" aria-multiselectable>\n {loading ? (\n <div className=\"flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n Carregando...\n </div>\n ) : !hasResults ? (\n <div className=\"py-6 text-center text-sm text-muted-foreground\">\n {emptyMessage}\n </div>\n ) : groups ? (\n filteredGroups?.map((group) => (\n <div key={group.label}>\n {group.value ? (\n <button\n type=\"button\"\n onClick={() => handleToggle(group.value!)}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5\",\n \"hover:bg-accent hover:text-accent-foreground transition-colors duration-100\",\n value.includes(group.value) && \"text-foreground\"\n )}\n >\n <Check\n className={cn(\n \"h-4 w-4 shrink-0 transition-opacity duration-100\",\n value.includes(group.value) ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n <span className=\"text-xs font-semibold uppercase tracking-wide\">\n {group.label}\n </span>\n </button>\n ) : (\n <div className=\"px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wide\">\n {group.label}\n </div>\n )}\n {group.options.map(renderOption)}\n </div>\n ))\n ) : (\n filteredOptions.map(renderOption)\n )}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n});\nMultiSelect.displayName = \"MultiSelect\";\n\nexport default MultiSelect;\n"],"names":["MultiSelect","React","value","onValueChange","options","groups","placeholder","searchPlaceholder","emptyMessage","disabled","loading","maxVisibleChips","onSearch","onCreateOption","createOptionLabel","className","ariaLabel","ariaLabelledby","ref","open","setOpen","search","setSearch","isCreating","setIsCreating","triggerRef","searchInputRef","mergedTriggerRef","mergeRefs","listCallbackRef","el","e","allOptions","g","isAsyncSearch","filteredOptions","term","o","filteredGroups","selectedOptions","hasResults","trimmedSearch","hasExactMatch","canCreate","handleCreate","handleToggle","optionValue","v","handleRemoveChip","handleOpenChange","next","handleSearchChange","visibleChips","overflowCount","renderOption","option","selected","jsxs","cn","jsx","Check","Popover","PopoverTrigger","Button","Fragment","opt","Badge","pressableKeyDown","X","ChevronsUpDown","PopoverContent","_a","Search","Input","Loader2","Plus","group"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"multi-select.js","sources":["../../../../src/components/ui/forms/multi-select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Check, ChevronsUpDown, X, Search, Loader2, Plus } from \"lucide-react\";\nimport { cn, mergeRefs, pressableKeyDown } from \"../utils\";\nimport { Button } from \"../primitives/button\";\nimport { Badge } from \"../primitives/badge\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../overlays/popover\";\nimport { Input } from \"./input\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n /** Slot para renderização customizada à direita do label — ex: <Badge>Externo</Badge> */\n badge?: React.ReactNode;\n}\n\nexport interface MultiSelectGroup {\n label: string;\n options: MultiSelectOption[];\n /** Quando presente, o header do grupo torna-se clicável e seleciona/deseleciona o grupo */\n value?: string;\n}\n\nexport interface MultiSelectProps {\n /** Valores atualmente selecionados */\n value: string[];\n /** Callback disparado ao alterar a seleção */\n onValueChange: (values: string[]) => void;\n\n /**\n * Opções em lista plana.\n * Use `options` OU `groups` — nunca os dois ao mesmo tempo.\n */\n options?: MultiSelectOption[];\n /**\n * Opções agrupadas hierarquicamente.\n * Use `options` OU `groups` — nunca os dois ao mesmo tempo.\n */\n groups?: MultiSelectGroup[];\n\n placeholder?: string;\n searchPlaceholder?: string;\n emptyMessage?: string;\n\n /** Rótulo acessível do gatilho quando não há `<Label>` associado */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n\n disabled?: boolean;\n /** Exibe estado de carregamento — substitui a lista por spinner */\n loading?: boolean;\n\n /**\n * Número máximo de chips visíveis no trigger antes de exibir \"+N\".\n * @default 2\n */\n maxVisibleChips?: number;\n\n /**\n * Callback de busca assíncrona — chamado a cada keystroke e ao abrir o dropdown.\n * Quando presente, a filtragem local é desativada (o caller controla `options`/`groups`).\n */\n onSearch?: (query: string) => void;\n\n /**\n * Habilita criação de uma nova opção diretamente no dropdown.\n * Chamado quando o usuário clica em \"Criar '{busca}'\" — o caller é responsável por\n * persistir a opção (ex: chamada de API), atualizar `options`/`value` com o resultado\n * e sinalizar falhas ao usuário (ex: toast). Rejeitar a promise preserva o termo\n * buscado no campo, permitindo corrigir e tentar novamente.\n * Exibido apenas quando não há opção com label idêntico ao termo buscado.\n */\n onCreateOption?: (inputValue: string) => void | Promise<void>;\n /** Customiza o label do botão de criação. @default (query) => `Criar \"${query}\"` */\n createOptionLabel?: (query: string) => string;\n\n className?: string;\n}\n\n/**\n * MultiSelect — Seleção múltipla com chips, busca, grupos hierárquicos e slots de badge por opção.\n *\n * @example\n * ```tsx\n * // Lista plana\n * <MultiSelect\n * value={selectedSquads}\n * onValueChange={setSelectedSquads}\n * options={squads.map(s => ({ value: String(s.id), label: s.name }))}\n * placeholder=\"Selecione os squads\"\n * />\n *\n * // Com badge por opção (ex: funcionários externos)\n * <MultiSelect\n * value={selectedEmployees}\n * onValueChange={setSelectedEmployees}\n * options={employees.map(e => ({\n * value: String(e.id),\n * label: e.name,\n * badge: e.isExternalUser\n * ? <Badge variant=\"secondary\" className=\"text-xs\">Externo</Badge>\n * : undefined,\n * }))}\n * />\n *\n * // Lista hierárquica (grupos de clientes)\n * <MultiSelect\n * value={selectedClients}\n * onValueChange={setSelectedClients}\n * groups={customerGroups.map(g => ({\n * label: g.name,\n * options: g.children.map(c => ({ value: String(c.id), label: c.name })),\n * }))}\n * />\n * ```\n */\nexport const MultiSelect = React.forwardRef<\n React.ElementRef<typeof Button>,\n MultiSelectProps\n>(({\n value,\n onValueChange,\n options,\n groups,\n placeholder = \"Selecione...\",\n searchPlaceholder = \"Buscar...\",\n emptyMessage = \"Nenhum resultado encontrado\",\n disabled = false,\n loading = false,\n maxVisibleChips = 2,\n onSearch,\n onCreateOption,\n createOptionLabel,\n className,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n}, ref) => {\n const [open, setOpen] = React.useState(false);\n const [search, setSearch] = React.useState(\"\");\n const [isCreating, setIsCreating] = React.useState(false);\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n const searchInputRef = React.useRef<HTMLInputElement>(null);\n const mergedTriggerRef = React.useMemo(() => mergeRefs(triggerRef, ref), [ref]);\n\n // Callback ref: chamado imediatamente ao montar o elemento (sem timing issue do useEffect).\n // Para a propagação nativa do wheel antes de chegar ao document, onde o\n // react-remove-scroll do Sheet chama preventDefault() em eventos de Portal.\n const listCallbackRef = React.useCallback((el: HTMLDivElement | null) => {\n if (!el) return;\n el.addEventListener(\"wheel\", (e) => e.stopPropagation(), { passive: true });\n }, []);\n\n const allOptions = React.useMemo((): MultiSelectOption[] => {\n if (options) return options;\n if (groups) {\n const groupHeaders: MultiSelectOption[] = groups\n .filter((g) => g.value != null)\n .map((g) => ({ value: g.value!, label: g.label }));\n return [...groupHeaders, ...groups.flatMap((g) => g.options)];\n }\n return [];\n }, [options, groups]);\n\n const isAsyncSearch = !!onSearch;\n\n const filteredOptions = React.useMemo(() => {\n if (!options) return [];\n if (isAsyncSearch) return options;\n const term = search.toLowerCase().trim();\n if (!term) return options;\n return options.filter(\n (o) =>\n o.label.toLowerCase().includes(term) ||\n o.value.toLowerCase().includes(term)\n );\n }, [options, search, isAsyncSearch]);\n\n const filteredGroups = React.useMemo(() => {\n if (!groups) return undefined;\n if (isAsyncSearch) return groups;\n const term = search.toLowerCase().trim();\n if (!term) return groups;\n return groups\n .map((g) => ({\n ...g,\n options: g.options.filter(\n (o) =>\n o.label.toLowerCase().includes(term) ||\n o.value.toLowerCase().includes(term)\n ),\n }))\n .filter((g) => g.options.length > 0);\n }, [groups, search, isAsyncSearch]);\n\n const selectedOptions = React.useMemo(\n () => allOptions.filter((o) => value.includes(o.value)),\n [allOptions, value]\n );\n\n const hasResults = groups\n ? (filteredGroups?.some((g) => g.options.length > 0) ?? false)\n : filteredOptions.length > 0;\n\n const trimmedSearch = search.trim();\n const hasExactMatch = allOptions.some(\n (o) => o.label.trim().toLowerCase() === trimmedSearch.toLowerCase()\n );\n const canCreate = !!onCreateOption && trimmedSearch.length > 0 && !hasExactMatch;\n\n const handleCreate = async () => {\n if (!onCreateOption || isCreating || !trimmedSearch) return;\n setIsCreating(true);\n try {\n await onCreateOption(trimmedSearch);\n setSearch(\"\");\n } catch {\n // Falha na criação: mantém o termo buscado para o usuário corrigir e tentar\n // novamente. O caller é responsável por sinalizar o erro (ex: toast).\n } finally {\n setIsCreating(false);\n }\n };\n\n const handleToggle = (optionValue: string) => {\n if (value.includes(optionValue)) {\n onValueChange(value.filter((v) => v !== optionValue));\n } else {\n onValueChange([...value, optionValue]);\n }\n };\n\n const handleRemoveChip = (optionValue: string, e: React.MouseEvent) => {\n e.stopPropagation();\n onValueChange(value.filter((v) => v !== optionValue));\n };\n\n const handleOpenChange = (next: boolean) => {\n if (disabled) return;\n setOpen(next);\n if (!next) setSearch(\"\");\n };\n\n React.useEffect(() => {\n if (open) onSearch?.(\"\");\n }, [open]);\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n setSearch(e.target.value);\n onSearch?.(e.target.value);\n };\n\n const visibleChips = selectedOptions.slice(0, maxVisibleChips);\n const overflowCount = selectedOptions.length - maxVisibleChips;\n\n const renderOption = (option: MultiSelectOption) => {\n const selected = value.includes(option.value);\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={selected}\n disabled={option.disabled}\n onClick={() => handleToggle(option.value)}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm\",\n \"aria-selected:bg-secondary aria-selected:text-secondary-foreground aria-selected:font-medium\",\n \"not-aria-selected:hover:bg-accent not-aria-selected:hover:text-accent-foreground\",\n \"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\",\n \"transition-colors duration-100\",\n option.disabled && \"pointer-events-none opacity-50\"\n )}\n >\n <Check\n className={cn(\n \"h-4 w-4 shrink-0 transition-opacity duration-100\",\n selected ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n <span className=\"flex-1 text-left truncate\">{option.label}</span>\n {option.badge}\n </button>\n );\n };\n\n return (\n <Popover open={open} onOpenChange={handleOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={mergedTriggerRef}\n type=\"button\"\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n disabled={disabled}\n className={cn(\n \"w-full min-h-10 h-auto justify-between font-normal px-3 py-2\",\n !value.length && \"text-muted-foreground\",\n className\n )}\n >\n <div className=\"flex flex-wrap items-center gap-1 flex-1 min-w-0\">\n {value.length === 0 ? (\n <span className=\"truncate text-sm\">\n {loading ? \"Carregando...\" : placeholder}\n </span>\n ) : (\n <>\n {visibleChips.map((opt) => (\n <Badge\n key={opt.value}\n variant=\"secondary\"\n className=\"rounded-sm h-5 gap-0.5 pr-1 font-normal max-w-[160px]\"\n >\n <span className=\"truncate text-xs\">{opt.label}</span>\n <span\n role=\"button\"\n tabIndex={0}\n aria-label={`Remover ${opt.label}`}\n className=\"inline-flex items-center justify-center rounded-sm hover:bg-muted/80 cursor-pointer shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-ring/60\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => handleRemoveChip(opt.value, e)}\n onKeyDown={pressableKeyDown}\n >\n <X className=\"h-3 w-3\" />\n </span>\n </Badge>\n ))}\n {overflowCount > 0 && (\n <Badge\n variant=\"secondary\"\n className=\"rounded-sm h-5 px-1.5 font-normal text-xs shrink-0\"\n >\n +{overflowCount}\n </Badge>\n )}\n </>\n )}\n </div>\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n\n <PopoverContent\n className=\"p-0 overflow-hidden\"\n style={{\n width: triggerRef.current?.offsetWidth\n ? `${triggerRef.current.offsetWidth}px`\n : \"auto\",\n }}\n align=\"start\"\n onOpenAutoFocus={(e) => {\n // O Radix suprime o autofocus padrão quando este Popover está aninhado em um\n // Dialog/Sheet (o focus-trap do pai disputa o foco com o do Popover). Focamos\n // o input manualmente via ref assim que o conteúdo transiciona para visível.\n e.preventDefault();\n searchInputRef.current?.focus();\n }}\n onWheel={(e) => e.stopPropagation()}\n >\n {/* Busca */}\n <div className=\"flex items-center border-b px-3\">\n <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n <Input\n ref={searchInputRef}\n placeholder={searchPlaceholder}\n value={search}\n onChange={handleSearchChange}\n className=\"h-10 border-0 focus-visible:ring-0 focus-visible:ring-offset-0 px-0\"\n />\n </div>\n\n {/* Criar nova opção */}\n {canCreate && (\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={isCreating}\n className={cn(\n \"flex w-full items-center gap-2 border-b px-3 py-2 text-sm text-primary\",\n // hover neutro: roxo sobre accent (gray-600) no escuro dá 4,4:1\n \"hover:bg-accent hover:text-accent-foreground transition-colors duration-100\",\n \"disabled:opacity-50 disabled:pointer-events-none\"\n )}\n >\n {isCreating ? (\n <Loader2 className=\"h-4 w-4 shrink-0 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4 shrink-0\" />\n )}\n <span className=\"truncate\">\n {isCreating\n ? \"Criando...\"\n : (createOptionLabel?.(trimmedSearch) ?? `Criar \"${trimmedSearch}\"`)}\n </span>\n </button>\n )}\n\n {/* Lista */}\n <div ref={listCallbackRef} className=\"max-h-70 overflow-y-auto [&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-primary/30 [&::-webkit-scrollbar-thumb:hover]:bg-primary/50\">\n <div className=\"p-1\" role=\"listbox\" aria-multiselectable>\n {loading ? (\n <div className=\"flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n Carregando...\n </div>\n ) : !hasResults ? (\n <div className=\"py-6 text-center text-sm text-muted-foreground\">\n {emptyMessage}\n </div>\n ) : groups ? (\n filteredGroups?.map((group) => (\n <div key={group.label}>\n {group.value ? (\n <button\n type=\"button\"\n onClick={() => handleToggle(group.value!)}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5\",\n \"hover:bg-accent hover:text-accent-foreground transition-colors duration-100\",\n value.includes(group.value) && \"text-foreground\"\n )}\n >\n <Check\n className={cn(\n \"h-4 w-4 shrink-0 transition-opacity duration-100\",\n value.includes(group.value) ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n <span className=\"text-xs font-semibold uppercase tracking-wide\">\n {group.label}\n </span>\n </button>\n ) : (\n <div className=\"px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wide\">\n {group.label}\n </div>\n )}\n {group.options.map(renderOption)}\n </div>\n ))\n ) : (\n filteredOptions.map(renderOption)\n )}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n});\nMultiSelect.displayName = \"MultiSelect\";\n\nexport default MultiSelect;\n"],"names":["MultiSelect","React","value","onValueChange","options","groups","placeholder","searchPlaceholder","emptyMessage","disabled","loading","maxVisibleChips","onSearch","onCreateOption","createOptionLabel","className","ariaLabel","ariaLabelledby","ref","open","setOpen","search","setSearch","isCreating","setIsCreating","triggerRef","searchInputRef","mergedTriggerRef","mergeRefs","listCallbackRef","el","e","allOptions","g","isAsyncSearch","filteredOptions","term","o","filteredGroups","selectedOptions","hasResults","trimmedSearch","hasExactMatch","canCreate","handleCreate","handleToggle","optionValue","v","handleRemoveChip","handleOpenChange","next","handleSearchChange","visibleChips","overflowCount","renderOption","option","selected","jsxs","cn","jsx","Check","Popover","PopoverTrigger","Button","Fragment","opt","Badge","pressableKeyDown","X","ChevronsUpDown","PopoverContent","_a","Search","Input","Loader2","Plus","group"],"mappings":";;;;;;;;;AAsHO,MAAMA,KAAcC,EAAM,WAG/B,CAAC;AAAA,EACD,OAAAC;AAAA,EACA,eAAAC;AAAA,EACA,SAAAC;AAAA,EACA,QAAAC;AAAA,EACA,aAAAC,IAAc;AAAA,EACd,mBAAAC,IAAoB;AAAA,EACpB,cAAAC,IAAe;AAAA,EACf,UAAAC,IAAW;AAAA,EACX,SAAAC,IAAU;AAAA,EACV,iBAAAC,IAAkB;AAAA,EAClB,UAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,mBAAAC;AAAA,EACA,WAAAC;AAAA,EACA,cAAcC;AAAA,EACd,mBAAmBC;AACrB,GAAGC,MAAQ;;AACT,QAAM,CAACC,GAAMC,CAAO,IAAInB,EAAM,SAAS,EAAK,GACtC,CAACoB,GAAQC,CAAS,IAAIrB,EAAM,SAAS,EAAE,GACvC,CAACsB,GAAYC,CAAa,IAAIvB,EAAM,SAAS,EAAK,GAClDwB,IAAaxB,EAAM,OAA0B,IAAI,GACjDyB,IAAiBzB,EAAM,OAAyB,IAAI,GACpD0B,IAAmB1B,EAAM,QAAQ,MAAM2B,GAAUH,GAAYP,CAAG,GAAG,CAACA,CAAG,CAAC,GAKxEW,IAAkB5B,EAAM,YAAY,CAAC6B,MAA8B;AACvE,IAAKA,KACLA,EAAG,iBAAiB,SAAS,CAACC,MAAMA,EAAE,mBAAmB,EAAE,SAAS,IAAM;AAAA,EAC5E,GAAG,CAAA,CAAE,GAECC,IAAa/B,EAAM,QAAQ,MAC3BG,MACAC,IAIK,CAAC,GAHkCA,EACvC,OAAO,CAAC4B,MAAMA,EAAE,SAAS,IAAI,EAC7B,IAAI,CAACA,OAAO,EAAE,OAAOA,EAAE,OAAQ,OAAOA,EAAE,QAAQ,GAC1B,GAAG5B,EAAO,QAAQ,CAAC4B,MAAMA,EAAE,OAAO,CAAC,IAEvD,CAAA,IACN,CAAC7B,GAASC,CAAM,CAAC,GAEd6B,IAAgB,CAAC,CAACtB,GAElBuB,IAAkBlC,EAAM,QAAQ,MAAM;AAC1C,QAAI,CAACG,EAAS,QAAO,CAAA;AACrB,QAAI8B,EAAe,QAAO9B;AAC1B,UAAMgC,IAAOf,EAAO,YAAA,EAAc,KAAA;AAClC,WAAKe,IACEhC,EAAQ;AAAA,MACb,CAACiC,MACCA,EAAE,MAAM,cAAc,SAASD,CAAI,KACnCC,EAAE,MAAM,YAAA,EAAc,SAASD,CAAI;AAAA,IAAA,IAJrBhC;AAAA,EAMpB,GAAG,CAACA,GAASiB,GAAQa,CAAa,CAAC,GAE7BI,IAAiBrC,EAAM,QAAQ,MAAM;AACzC,QAAI,CAACI,EAAQ;AACb,QAAI6B,EAAe,QAAO7B;AAC1B,UAAM+B,IAAOf,EAAO,YAAA,EAAc,KAAA;AAClC,WAAKe,IACE/B,EACJ,IAAI,CAAC4B,OAAO;AAAA,MACX,GAAGA;AAAA,MACH,SAASA,EAAE,QAAQ;AAAA,QACjB,CAACI,MACCA,EAAE,MAAM,cAAc,SAASD,CAAI,KACnCC,EAAE,MAAM,YAAA,EAAc,SAASD,CAAI;AAAA,MAAA;AAAA,IACvC,EACA,EACD,OAAO,CAACH,MAAMA,EAAE,QAAQ,SAAS,CAAC,IAVnB5B;AAAA,EAWpB,GAAG,CAACA,GAAQgB,GAAQa,CAAa,CAAC,GAE5BK,IAAkBtC,EAAM;AAAA,IAC5B,MAAM+B,EAAW,OAAO,CAACK,MAAMnC,EAAM,SAASmC,EAAE,KAAK,CAAC;AAAA,IACtD,CAACL,GAAY9B,CAAK;AAAA,EAAA,GAGdsC,IAAanC,KACdiC,KAAA,gBAAAA,EAAgB,KAAK,CAACL,MAAMA,EAAE,QAAQ,SAAS,OAAM,KACtDE,EAAgB,SAAS,GAEvBM,IAAgBpB,EAAO,KAAA,GACvBqB,IAAgBV,EAAW;AAAA,IAC/B,CAACK,MAAMA,EAAE,MAAM,OAAO,YAAA,MAAkBI,EAAc,YAAA;AAAA,EAAY,GAE9DE,IAAY,CAAC,CAAC9B,KAAkB4B,EAAc,SAAS,KAAK,CAACC,GAE7DE,IAAe,YAAY;AAC/B,QAAI,GAAC/B,KAAkBU,KAAc,CAACkB,IACtC;AAAA,MAAAjB,EAAc,EAAI;AAClB,UAAI;AACF,cAAMX,EAAe4B,CAAa,GAClCnB,EAAU,EAAE;AAAA,MACd,QAAQ;AAAA,MAGR,UAAA;AACE,QAAAE,EAAc,EAAK;AAAA,MACrB;AAAA;AAAA,EACF,GAEMqB,IAAe,CAACC,MAAwB;AAC5C,IAAI5C,EAAM,SAAS4C,CAAW,IAC5B3C,EAAcD,EAAM,OAAO,CAAC6C,MAAMA,MAAMD,CAAW,CAAC,IAEpD3C,EAAc,CAAC,GAAGD,GAAO4C,CAAW,CAAC;AAAA,EAEzC,GAEME,IAAmB,CAACF,GAAqBf,MAAwB;AACrE,IAAAA,EAAE,gBAAA,GACF5B,EAAcD,EAAM,OAAO,CAAC6C,MAAMA,MAAMD,CAAW,CAAC;AAAA,EACtD,GAEMG,IAAmB,CAACC,MAAkB;AAC1C,IAAIzC,MACJW,EAAQ8B,CAAI,GACPA,KAAM5B,EAAU,EAAE;AAAA,EACzB;AAEA,EAAArB,EAAM,UAAU,MAAM;AACpB,IAAIkB,qBAAiB;AAAA,EACvB,GAAG,CAACA,CAAI,CAAC;AAET,QAAMgC,IAAqB,CAAC,MAA2C;AACrE,IAAA7B,EAAU,EAAE,OAAO,KAAK,GACxBV,KAAA,QAAAA,EAAW,EAAE,OAAO;AAAA,EACtB,GAEMwC,KAAeb,EAAgB,MAAM,GAAG5B,CAAe,GACvD0C,IAAgBd,EAAgB,SAAS5B,GAEzC2C,IAAe,CAACC,MAA8B;AAClD,UAAMC,IAAWtD,EAAM,SAASqD,EAAO,KAAK;AAC5C,WACE,gBAAAE;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,iBAAeD;AAAA,QACf,UAAUD,EAAO;AAAA,QACjB,SAAS,MAAMV,EAAaU,EAAO,KAAK;AAAA,QACxC,WAAWG;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACAH,EAAO,YAAY;AAAA,QAAA;AAAA,QAGrB,UAAA;AAAA,UAAA,gBAAAI;AAAA,YAACC;AAAA,YAAA;AAAA,cACC,WAAWF;AAAA,gBACT;AAAA,gBACAF,IAAW,gBAAgB;AAAA,cAAA;AAAA,YAC7B;AAAA,UAAA;AAAA,UAEF,gBAAAG,EAAC,QAAA,EAAK,WAAU,6BAA6B,YAAO,OAAM;AAAA,UACzDJ,EAAO;AAAA,QAAA;AAAA,MAAA;AAAA,MAtBHA,EAAO;AAAA,IAAA;AAAA,EAyBlB;AAEA,SACE,gBAAAE,EAACI,IAAA,EAAQ,MAAA1C,GAAY,cAAc8B,GACjC,UAAA;AAAA,IAAA,gBAAAU,EAACG,IAAA,EAAe,SAAO,IACrB,UAAA,gBAAAL;AAAA,MAACM;AAAA,MAAA;AAAA,QACC,KAAKpC;AAAA,QACL,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,iBAAeR;AAAA,QACf,cAAYH;AAAA,QACZ,mBAAiBC;AAAA,QACjB,UAAAR;AAAA,QACA,WAAWiD;AAAA,UACT;AAAA,UACA,CAACxD,EAAM,UAAU;AAAA,UACjBa;AAAA,QAAA;AAAA,QAGF,UAAA;AAAA,UAAA,gBAAA4C,EAAC,OAAA,EAAI,WAAU,oDACZ,UAAAzD,EAAM,WAAW,IAChB,gBAAAyD,EAAC,QAAA,EAAK,WAAU,oBACb,UAAAjD,IAAU,kBAAkBJ,EAAA,CAC/B,IAEA,gBAAAmD,EAAAO,IAAA,EACG,UAAA;AAAA,YAAAZ,GAAa,IAAI,CAACa,MACjB,gBAAAR;AAAA,cAACS;AAAA,cAAA;AAAA,gBAEC,SAAQ;AAAA,gBACR,WAAU;AAAA,gBAEV,UAAA;AAAA,kBAAA,gBAAAP,EAAC,QAAA,EAAK,WAAU,oBAAoB,UAAAM,EAAI,OAAM;AAAA,kBAC9C,gBAAAN;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBACC,MAAK;AAAA,sBACL,UAAU;AAAA,sBACV,cAAY,WAAWM,EAAI,KAAK;AAAA,sBAChC,WAAU;AAAA,sBACV,aAAa,CAAClC,MAAMA,EAAE,eAAA;AAAA,sBACtB,SAAS,CAACA,MAAMiB,EAAiBiB,EAAI,OAAOlC,CAAC;AAAA,sBAC7C,WAAWoC;AAAA,sBAEX,UAAA,gBAAAR,EAACS,IAAA,EAAE,WAAU,UAAA,CAAU;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBACzB;AAAA,cAAA;AAAA,cAfKH,EAAI;AAAA,YAAA,CAiBZ;AAAA,YACAZ,IAAgB,KACf,gBAAAI;AAAA,cAACS;AAAA,cAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,WAAU;AAAA,gBACX,UAAA;AAAA,kBAAA;AAAA,kBACGb;AAAA,gBAAA;AAAA,cAAA;AAAA,YAAA;AAAA,UACJ,EAAA,CAEJ,EAAA,CAEJ;AAAA,UACA,gBAAAM,EAACU,IAAA,EAAe,WAAU,mCAAA,CAAmC;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,GAEjE;AAAA,IAEA,gBAAAZ;AAAA,MAACa;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO;AAAA,UACL,QAAOC,IAAA9C,EAAW,YAAX,QAAA8C,EAAoB,cACvB,GAAG9C,EAAW,QAAQ,WAAW,OACjC;AAAA,QAAA;AAAA,QAEN,OAAM;AAAA,QACN,iBAAiB,CAAC,MAAM;;AAItB,YAAE,eAAA,IACF8C,IAAA7C,EAAe,YAAf,QAAA6C,EAAwB;AAAA,QAC1B;AAAA,QACA,SAAS,CAAC,MAAM,EAAE,gBAAA;AAAA,QAGlB,UAAA;AAAA,UAAA,gBAAAd,EAAC,OAAA,EAAI,WAAU,mCACb,UAAA;AAAA,YAAA,gBAAAE,EAACa,IAAA,EAAO,WAAU,mCAAA,CAAmC;AAAA,YACrD,gBAAAb;AAAA,cAACc;AAAA,cAAA;AAAA,gBACC,KAAK/C;AAAA,gBACL,aAAanB;AAAA,gBACb,OAAOc;AAAA,gBACP,UAAU8B;AAAA,gBACV,WAAU;AAAA,cAAA;AAAA,YAAA;AAAA,UACZ,GACF;AAAA,UAGCR,KACC,gBAAAc;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAASb;AAAA,cACT,UAAUrB;AAAA,cACV,WAAWmC;AAAA,gBACT;AAAA;AAAA,gBAEA;AAAA,gBACA;AAAA,cAAA;AAAA,cAGD,UAAA;AAAA,gBAAAnC,IACC,gBAAAoC,EAACe,KAAQ,WAAU,gCAAA,CAAgC,IAEnD,gBAAAf,EAACgB,IAAA,EAAK,WAAU,mBAAA,CAAmB;AAAA,gBAErC,gBAAAhB,EAAC,QAAA,EAAK,WAAU,YACb,UAAApC,IACG,gBACCT,KAAA,gBAAAA,EAAoB2B,OAAkB,UAAUA,CAAa,IAAA,CACpE;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,4BAKH,OAAA,EAAI,KAAKZ,GAAiB,WAAU,2OACnC,4BAAC,OAAA,EAAI,WAAU,OAAM,MAAK,WAAU,wBAAoB,IACrD,cACC,gBAAA4B,EAAC,OAAA,EAAI,WAAU,6EACb,UAAA;AAAA,YAAA,gBAAAE,EAACe,GAAA,EAAQ,WAAU,uBAAA,CAAuB;AAAA,YAAE;AAAA,UAAA,GAE9C,IACGlC,IAIDnC,IACFiC,KAAA,gBAAAA,EAAgB,IAAI,CAACsC,wBAClB,OAAA,EACE,UAAA;AAAA,YAAAA,EAAM,QACL,gBAAAnB;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAMZ,EAAa+B,EAAM,KAAM;AAAA,gBACxC,WAAWlB;AAAA,kBACT;AAAA,kBACA;AAAA,kBACAxD,EAAM,SAAS0E,EAAM,KAAK,KAAK;AAAA,gBAAA;AAAA,gBAGjC,UAAA;AAAA,kBAAA,gBAAAjB;AAAA,oBAACC;AAAA,oBAAA;AAAA,sBACC,WAAWF;AAAA,wBACT;AAAA,wBACAxD,EAAM,SAAS0E,EAAM,KAAK,IAAI,gBAAgB;AAAA,sBAAA;AAAA,oBAChD;AAAA,kBAAA;AAAA,kBAEF,gBAAAjB,EAAC,QAAA,EAAK,WAAU,iDACb,YAAM,MAAA,CACT;AAAA,gBAAA;AAAA,cAAA;AAAA,YAAA,IAGF,gBAAAA,EAAC,OAAA,EAAI,WAAU,mFACZ,YAAM,OACT;AAAA,YAEDiB,EAAM,QAAQ,IAAItB,CAAY;AAAA,UAAA,KA1BvBsB,EAAM,KA2BhB,KAGFzC,EAAgB,IAAImB,CAAY,IAnChC,gBAAAK,EAAC,SAAI,WAAU,kDACZ,UAAAnD,EAAA,CACH,GAmCJ,EAAA,CACF;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EACF,GACF;AAEJ,CAAC;AACDR,GAAY,cAAc;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("react/jsx-runtime"),l=require("react"),m=require("@radix-ui/react-radio-group"),s=require("../utils.cjs");function d(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const i=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,i.get?i:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const n=d(l),o=d(m),c=n.forwardRef(({className:e,...t},r)=>a.jsx(o.Root,{ref:r,"data-slot":"radio-group",className:s.cn("grid gap-3",e),...t}));c.displayName=o.Root.displayName;const u=n.forwardRef(({className:e,...t},r)=>a.jsx(o.Item,{ref:r,"data-slot":"radio-group-item",className:s.cn("peer h-5 w-5 shrink-0 rounded-full border border-input bg-background shadow-sm transition-all hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",e),...t,children:a.jsx(o.Indicator,{"data-slot":"radio-group-indicator",className:"flex items-center justify-center",children:a.jsx("div",{className:"h-2 w-2 rounded-full bg-primary-foreground"})})}));u.displayName=o.Item.displayName;exports.RadioGroup=c;exports.RadioGroupItem=u;
|
|
2
2
|
//# sourceMappingURL=radio-group.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-group.cjs","sources":["../../../../src/components/ui/forms/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\n\nimport { cn } from \"../utils\";\n\nconst RadioGroup = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <RadioGroupPrimitive.Root\n ref={ref}\n data-slot=\"radio-group\"\n className={cn(\"grid gap-3\", className)}\n {...props}\n />\n));\nRadioGroup.displayName = RadioGroupPrimitive.Root.displayName;\n\nconst RadioGroupItem = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>\n>(({ className, ...props }, ref) => (\n <RadioGroupPrimitive.Item\n ref={ref}\n data-slot=\"radio-group-item\"\n className={cn(\n \"peer h-5 w-5 shrink-0 rounded-full border border-
|
|
1
|
+
{"version":3,"file":"radio-group.cjs","sources":["../../../../src/components/ui/forms/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\n\nimport { cn } from \"../utils\";\n\nconst RadioGroup = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <RadioGroupPrimitive.Root\n ref={ref}\n data-slot=\"radio-group\"\n className={cn(\"grid gap-3\", className)}\n {...props}\n />\n));\nRadioGroup.displayName = RadioGroupPrimitive.Root.displayName;\n\nconst RadioGroupItem = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>\n>(({ className, ...props }, ref) => (\n <RadioGroupPrimitive.Item\n ref={ref}\n data-slot=\"radio-group-item\"\n className={cn(\n \"peer h-5 w-5 shrink-0 rounded-full border border-input bg-background shadow-sm transition-all hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n className,\n )}\n {...props}\n >\n <RadioGroupPrimitive.Indicator\n data-slot=\"radio-group-indicator\"\n className=\"flex items-center justify-center\"\n >\n <div className=\"h-2 w-2 rounded-full bg-primary-foreground\" />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n));\nRadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;\n\nexport { RadioGroup, RadioGroupItem };\n"],"names":["RadioGroup","React","className","props","ref","jsx","RadioGroupPrimitive","cn","RadioGroupItem"],"mappings":"yfAOMA,EAAaC,EAAM,WAGvB,CAAC,CAAE,UAAAC,EAAW,GAAGC,CAAA,EAASC,IAC1BC,EAAAA,IAACC,EAAoB,KAApB,CACC,IAAAF,EACA,YAAU,cACV,UAAWG,EAAAA,GAAG,aAAcL,CAAS,EACpC,GAAGC,CAAA,CACN,CACD,EACDH,EAAW,YAAcM,EAAoB,KAAK,YAElD,MAAME,EAAiBP,EAAM,WAG3B,CAAC,CAAE,UAAAC,EAAW,GAAGC,CAAA,EAASC,IAC1BC,EAAAA,IAACC,EAAoB,KAApB,CACC,IAAAF,EACA,YAAU,mBACV,UAAWG,EAAAA,GACT,4VACAL,CAAA,EAED,GAAGC,EAEJ,SAAAE,EAAAA,IAACC,EAAoB,UAApB,CACC,YAAU,wBACV,UAAU,mCAEV,SAAAD,EAAAA,IAAC,MAAA,CAAI,UAAU,4CAAA,CAA6C,CAAA,CAAA,CAC9D,CACF,CACD,EACDG,EAAe,YAAcF,EAAoB,KAAK"}
|