@snksergio/design-system 0.67.0 → 0.68.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/dist-lib/ai/componentes/AppShell.md +1 -1
- package/dist-lib/ai/componentes/Chip.md +11 -0
- package/dist-lib/ai/componentes/Combobox.md +23 -0
- package/dist-lib/ai/componentes/DatePicker.md +15 -0
- package/dist-lib/ai/componentes/FileUploadField.md +23 -0
- package/dist-lib/ai/componentes/Header.md +9 -0
- package/dist-lib/ai/componentes/Kpi.md +14 -1
- package/dist-lib/ai/componentes/MenuSidebar.md +3 -3
- package/dist-lib/ai/componentes/PageHeader.md +6 -0
- package/dist-lib/ai/componentes/_primitivos.md +9 -2
- package/dist-lib/ai/global/componentes.md +20 -2
- package/dist-lib/ai/manifest.json +2 -2
- package/dist-lib/shadcn.cjs +1 -0
- package/dist-lib/shadcn.cjs.map +1 -1
- package/dist-lib/shadcn.mjs +2 -1
- package/dist-lib/src/components/shadcn/alert-dialog.cjs +13 -2
- package/dist-lib/src/components/shadcn/alert-dialog.cjs.map +1 -1
- package/dist-lib/src/components/shadcn/alert-dialog.d.ts +21 -1
- package/dist-lib/src/components/shadcn/alert-dialog.d.ts.map +1 -1
- package/dist-lib/src/components/shadcn/alert-dialog.mjs +13 -2
- package/dist-lib/src/components/shadcn/alert-dialog.mjs.map +1 -1
- package/dist-lib/src/components/shadcn/card.cjs +22 -2
- package/dist-lib/src/components/shadcn/card.cjs.map +1 -1
- package/dist-lib/src/components/shadcn/card.d.ts +10 -1
- package/dist-lib/src/components/shadcn/card.d.ts.map +1 -1
- package/dist-lib/src/components/shadcn/card.mjs +22 -2
- package/dist-lib/src/components/shadcn/card.mjs.map +1 -1
- package/dist-lib/src/components/shadcn/clickable-surface.cjs +28 -4
- package/dist-lib/src/components/shadcn/clickable-surface.cjs.map +1 -1
- package/dist-lib/src/components/shadcn/clickable-surface.d.ts +26 -2
- package/dist-lib/src/components/shadcn/clickable-surface.d.ts.map +1 -1
- package/dist-lib/src/components/shadcn/clickable-surface.mjs +28 -4
- package/dist-lib/src/components/shadcn/clickable-surface.mjs.map +1 -1
- package/dist-lib/src/components/shadcn/command.cjs +26 -5
- package/dist-lib/src/components/shadcn/command.cjs.map +1 -1
- package/dist-lib/src/components/shadcn/command.d.ts +49 -2
- package/dist-lib/src/components/shadcn/command.d.ts.map +1 -1
- package/dist-lib/src/components/shadcn/command.mjs +26 -5
- package/dist-lib/src/components/shadcn/command.mjs.map +1 -1
- package/dist-lib/src/components/shadcn/input-group.d.ts +1 -1
- package/dist-lib/src/components/shadcn/input-otp.d.ts +1 -1
- package/dist-lib/src/components/shadcn/input.d.ts +1 -1
- package/dist-lib/src/components/shadcn/sheet.d.ts +1 -1
- package/dist-lib/src/components/ui/AppShell/app-shell.types.d.ts +1 -1
- package/dist-lib/src/components/ui/Chip/chip.cjs +7 -0
- package/dist-lib/src/components/ui/Chip/chip.cjs.map +1 -1
- package/dist-lib/src/components/ui/Chip/chip.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Chip/chip.mjs +7 -0
- package/dist-lib/src/components/ui/Chip/chip.mjs.map +1 -1
- package/dist-lib/src/components/ui/Chip/chip.styles.cjs.map +1 -1
- package/dist-lib/src/components/ui/Chip/chip.styles.mjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.cjs +60 -22
- package/dist-lib/src/components/ui/Combobox/combobox.cjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.d.ts +2 -1
- package/dist-lib/src/components/ui/Combobox/combobox.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.mjs +44 -22
- package/dist-lib/src/components/ui/Combobox/combobox.mjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.cjs +5 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.cjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.d.ts +18 -0
- package/dist-lib/src/components/ui/Combobox/combobox.styles.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.mjs +5 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.mjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.types.d.ts +21 -0
- package/dist-lib/src/components/ui/Combobox/combobox.types.d.ts.map +1 -1
- package/dist-lib/src/components/ui/DatePicker/datepicker.cjs +21 -3
- package/dist-lib/src/components/ui/DatePicker/datepicker.cjs.map +1 -1
- package/dist-lib/src/components/ui/DatePicker/datepicker.d.ts +10 -0
- package/dist-lib/src/components/ui/DatePicker/datepicker.d.ts.map +1 -1
- package/dist-lib/src/components/ui/DatePicker/datepicker.mjs +21 -3
- package/dist-lib/src/components/ui/DatePicker/datepicker.mjs.map +1 -1
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.cjs +1 -4
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.cjs.map +1 -1
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.d.ts.map +1 -1
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.mjs +1 -4
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.mjs.map +1 -1
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.types.d.ts +6 -0
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.types.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Header/header-search.cjs +6 -4
- package/dist-lib/src/components/ui/Header/header-search.cjs.map +1 -1
- package/dist-lib/src/components/ui/Header/header-search.d.ts +14 -3
- package/dist-lib/src/components/ui/Header/header-search.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Header/header-search.mjs +6 -4
- package/dist-lib/src/components/ui/Header/header-search.mjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-delta.cjs +5 -0
- package/dist-lib/src/components/ui/Kpi/kpi-delta.cjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-delta.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-delta.mjs +5 -0
- package/dist-lib/src/components/ui/Kpi/kpi-delta.mjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-group.cjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-group.mjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.cjs +8 -3
- package/dist-lib/src/components/ui/Kpi/kpi.cjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.d.ts +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.mjs +8 -3
- package/dist-lib/src/components/ui/Kpi/kpi.mjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.types.d.ts +9 -0
- package/dist-lib/src/components/ui/Kpi/kpi.types.d.ts.map +1 -1
- package/dist-lib/src/components/ui/MenuSidebar/sidebar.types.d.ts +1 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.cjs +4 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.cjs.map +1 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.d.ts +9 -0
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.d.ts.map +1 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.mjs +4 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.mjs.map +1 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.types.d.ts +11 -4
- package/dist-lib/src/components/ui/PageHeader/page-header.types.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Table/table-span-row.cjs.map +1 -1
- package/dist-lib/src/components/ui/Table/table-span-row.mjs.map +1 -1
- package/dist-lib/src/lib/utils.cjs.map +1 -1
- package/dist-lib/src/lib/utils.mjs.map +1 -1
- package/dist-lib/src/utils/tv.cjs.map +1 -1
- package/dist-lib/src/utils/tv.mjs.map +1 -1
- package/dist-lib/src/utils/tw-merge-config.cjs.map +1 -1
- package/dist-lib/src/utils/tw-merge-config.mjs.map +1 -1
- package/dist-lib/src/utils/use-title-se-truncado.cjs +34 -0
- package/dist-lib/src/utils/use-title-se-truncado.cjs.map +1 -0
- package/dist-lib/src/utils/use-title-se-truncado.d.ts +30 -0
- package/dist-lib/src/utils/use-title-se-truncado.d.ts.map +1 -0
- package/dist-lib/src/utils/use-title-se-truncado.mjs +34 -0
- package/dist-lib/src/utils/use-title-se-truncado.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -6,10 +6,28 @@ const lucideReact = require("lucide-react");
|
|
|
6
6
|
const utils = require("../../../lib/utils.cjs");
|
|
7
7
|
const popover = require("../../shadcn/popover.cjs");
|
|
8
8
|
const command = require("../../shadcn/command.cjs");
|
|
9
|
+
const separator = require("../../shadcn/separator.cjs");
|
|
9
10
|
const chip = require("../Chip/chip.cjs");
|
|
10
11
|
require("../Chip/chip-group.cjs");
|
|
11
12
|
require("../Chip/chip.styles.cjs");
|
|
12
13
|
const combobox_styles = require("./combobox.styles.cjs");
|
|
14
|
+
function _interopNamespaceDefault(e) {
|
|
15
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
16
|
+
if (e) {
|
|
17
|
+
for (const k in e) {
|
|
18
|
+
if (k !== "default") {
|
|
19
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
20
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: () => e[k]
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
n.default = e;
|
|
28
|
+
return Object.freeze(n);
|
|
29
|
+
}
|
|
30
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
13
31
|
const Combobox = React.forwardRef(
|
|
14
32
|
function Combobox2(props, ref) {
|
|
15
33
|
const {
|
|
@@ -30,6 +48,7 @@ const Combobox = React.forwardRef(
|
|
|
30
48
|
onValueChange,
|
|
31
49
|
...rest
|
|
32
50
|
} = props;
|
|
51
|
+
const fechaAoEscolher = props.multiple === true ? false : props.closeOnSelect ?? true;
|
|
33
52
|
const { maxChips = 2, renderSummary } = props.multiple === true ? props : {};
|
|
34
53
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(
|
|
35
54
|
defaultOpen ?? false
|
|
@@ -46,13 +65,22 @@ const Combobox = React.forwardRef(
|
|
|
46
65
|
const escolher = (o) => {
|
|
47
66
|
if (!multiple) {
|
|
48
67
|
onValueChange?.(o.value);
|
|
49
|
-
setOpen(false);
|
|
68
|
+
if (fechaAoEscolher) setOpen(false);
|
|
50
69
|
return;
|
|
51
70
|
}
|
|
52
71
|
const atual = value ?? [];
|
|
53
72
|
const proximo = atual.includes(o.value) ? atual.filter((v) => v !== o.value) : [...atual, o.value];
|
|
54
73
|
onValueChange?.(proximo);
|
|
55
74
|
};
|
|
75
|
+
const grupos = React__namespace.useMemo(() => {
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const o of options) {
|
|
78
|
+
const atual = out.find((g) => g.heading === o.group);
|
|
79
|
+
if (atual) atual.itens.push(o);
|
|
80
|
+
else out.push({ heading: o.group, itens: [o] });
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}, [options]);
|
|
56
84
|
const conteudoTrigger = () => {
|
|
57
85
|
if (selecionados.length === 0) return placeholder;
|
|
58
86
|
if (!multiple) return selecionados[0].label;
|
|
@@ -101,27 +129,37 @@ const Combobox = React.forwardRef(
|
|
|
101
129
|
/* @__PURE__ */ jsxRuntime.jsx(command.CommandInput, { placeholder: searchPlaceholder }),
|
|
102
130
|
/* @__PURE__ */ jsxRuntime.jsxs(command.CommandList, { children: [
|
|
103
131
|
/* @__PURE__ */ jsxRuntime.jsx(command.CommandEmpty, { children: emptyMessage }),
|
|
104
|
-
/* @__PURE__ */ jsxRuntime.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
132
|
+
grupos.map(({ heading, itens }, i) => /* @__PURE__ */ jsxRuntime.jsxs(command.CommandGroup, { heading, children: [
|
|
133
|
+
i > 0 && !heading && /* @__PURE__ */ jsxRuntime.jsx(separator.Separator, { className: "my-pad-xs" }),
|
|
134
|
+
itens.map((option) => {
|
|
135
|
+
const isSelected = estaSelecionada(option);
|
|
136
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
137
|
+
command.CommandItem,
|
|
138
|
+
{
|
|
139
|
+
value: option.value,
|
|
140
|
+
keywords: [
|
|
141
|
+
option.label,
|
|
142
|
+
...option.hint ? [option.hint] : [],
|
|
143
|
+
...option.keywords ?? []
|
|
144
|
+
],
|
|
145
|
+
role: multiple ? "option" : void 0,
|
|
146
|
+
"aria-checked": multiple ? isSelected : void 0,
|
|
147
|
+
onSelect: () => escolher(option),
|
|
148
|
+
className: utils.cn(
|
|
149
|
+
isSelected && "text-fg-default font-medium [&_svg]:text-fg-brand"
|
|
150
|
+
),
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: styles.itemTexto(), children: [
|
|
153
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.itemLabel(), children: option.label }),
|
|
154
|
+
option.hint && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.itemHint(), children: option.hint })
|
|
155
|
+
] }),
|
|
156
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { "aria-hidden": "true" })
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
option.value
|
|
160
|
+
);
|
|
161
|
+
})
|
|
162
|
+
] }, heading ?? "__sem-grupo"))
|
|
125
163
|
] })
|
|
126
164
|
] })
|
|
127
165
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.cjs","sources":["../../../../../src/components/ui/Combobox/combobox.tsx"],"sourcesContent":["import { forwardRef, useState } from \"react\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/components/shadcn/popover\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@/components/shadcn/command\";\nimport { Chip } from \"@/components/ui/Chip\";\nimport { comboboxStyles } from \"./combobox.styles\";\nimport type { ComboboxOption, ComboboxProps } from \"./combobox.types\";\n\n/**\n * Combobox — select com BUSCA (autocomplete) e lista ROLÁVEL, em escolha única\n * (default) ou múltipla (`multiple`).\n *\n * Compõe `Popover` + `Command` (cmdk): o trigger imita o `SelectTrigger` (parear\n * com Selects irmãos) e o dropdown traz `CommandInput` no topo + `CommandList`\n * com `max-h`/scroll. Use no lugar de um `Select` quando a lista é longa e o\n * usuário precisa digitar pra achar a opção (ex.: escolher uma coluna entre 30).\n *\n * Selação robusta: o `onSelect` do cmdk passa um valor normalizado, então o\n * componente NÃO depende dele — fecha via closure sobre `option.value`. A busca\n * casa por `label` + `keywords` (que inclui o `value` da opção).\n */\nexport const Combobox = forwardRef<HTMLButtonElement, ComboboxProps>(\n function Combobox(props, ref) {\n const {\n options,\n placeholder = \"Selecione…\",\n searchPlaceholder = \"Buscar…\",\n emptyMessage = \"Nenhum resultado.\",\n open: openProp,\n defaultOpen,\n onOpenChange,\n align = \"start\",\n className,\n contentClassName,\n disabled,\n \"aria-label\": ariaLabel,\n multiple,\n value,\n onValueChange,\n ...rest\n } = props as ComboboxProps & {\n multiple?: boolean;\n value?: string | string[];\n onValueChange?: (v: never) => void;\n };\n const { maxChips = 2, renderSummary } =\n props.multiple === true ? props : ({} as { maxChips?: number; renderSummary?: never });\n\n const [uncontrolledOpen, setUncontrolledOpen] = useState(\n defaultOpen ?? false,\n );\n const isControlled = openProp !== undefined;\n const open = isControlled ? openProp : uncontrolledOpen;\n const setOpen = (next: boolean) => {\n if (!isControlled) setUncontrolledOpen(next);\n onOpenChange?.(next);\n };\n\n const styles = comboboxStyles();\n\n const selecionados: ComboboxOption[] = multiple\n ? options.filter((o) => (value as string[] | undefined)?.includes(o.value))\n : options.filter((o) => o.value === value);\n\n const estaSelecionada = (o: ComboboxOption) =>\n multiple\n ? Boolean((value as string[] | undefined)?.includes(o.value))\n : o.value === value;\n\n const escolher = (o: ComboboxOption) => {\n if (!multiple) {\n (onValueChange as ((v: string) => void) | undefined)?.(o.value);\n setOpen(false);\n return;\n }\n // Toggle, e o dropdown FICA ABERTO: escolher várias de uma lista longa com o\n // popover fechando a cada clique custa um reabrir + re-buscar por item.\n const atual = (value as string[] | undefined) ?? [];\n const proximo = atual.includes(o.value)\n ? atual.filter((v) => v !== o.value)\n : [...atual, o.value];\n (onValueChange as ((v: string[]) => void) | undefined)?.(proximo);\n };\n\n const conteudoTrigger = () => {\n if (selecionados.length === 0) return placeholder;\n if (!multiple) return selecionados[0].label;\n if (renderSummary) return renderSummary(selecionados);\n const visiveis = selecionados.slice(0, maxChips);\n const resto = selecionados.length - visiveis.length;\n return (\n // Chips SEM × de propósito: o trigger é um <button>, e um botão de remover\n // aqui dentro seria botão aninhado — HTML inválido, e o navegador desaninha,\n // fazendo o × abrir o dropdown. Pra remover com ×, renderize <Chip onRemove>\n // ABAIXO do campo.\n <span className=\"flex min-w-0 items-center gap-gp-xs\">\n {visiveis.map((o) => (\n <Chip key={o.value} size=\"sm\" color=\"neutral\" variant=\"soft\">\n {o.label}\n </Chip>\n ))}\n {resto > 0 && (\n <span className=\"shrink-0 text-fg-muted\">+{resto}</span>\n )}\n </span>\n );\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n ref={ref}\n type=\"button\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n disabled={disabled}\n data-placeholder={selecionados.length > 0 ? undefined : \"\"}\n className={styles.trigger({ className })}\n {...rest}\n >\n <span className={styles.value()}>{conteudoTrigger()}</span>\n <ChevronDown className={styles.icon()} aria-hidden=\"true\" />\n </button>\n </PopoverTrigger>\n <PopoverContent\n align={align}\n className={styles.content({ className: contentClassName })}\n >\n <Command>\n <CommandInput placeholder={searchPlaceholder} />\n <CommandList>\n <CommandEmpty>{emptyMessage}</CommandEmpty>\n <CommandGroup>\n {options.map((option) => {\n const isSelected = estaSelecionada(option);\n return (\n <CommandItem\n key={option.value}\n value={option.label}\n keywords={[option.value, ...(option.keywords ?? [])]}\n // `aria-selected` é do cmdk (item ativo do teclado). Em multi,\n // quem diz \"marcado\" é `aria-checked` + role de opção múltipla.\n role={multiple ? \"option\" : undefined}\n aria-checked={multiple ? isSelected : undefined}\n onSelect={() => escolher(option)}\n className={cn(\n isSelected &&\n \"text-fg-default font-medium [&_svg]:text-fg-brand\",\n )}\n >\n <span className={styles.itemLabel()}>{option.label}</span>\n {isSelected && <Check aria-hidden=\"true\" />}\n </CommandItem>\n );\n })}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n },\n);\nCombobox.displayName = \"Combobox\";\n"],"names":["forwardRef","Combobox","useState","comboboxStyles","jsxs","jsx","Chip","Popover","PopoverTrigger","ChevronDown","PopoverContent","Command","CommandInput","CommandList","CommandEmpty","CommandGroup","CommandItem","cn","Check"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"combobox.cjs","sources":["../../../../../src/components/ui/Combobox/combobox.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport { forwardRef, useState } from \"react\";\r\nimport { Check, ChevronDown } from \"lucide-react\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Popover,\r\n PopoverContent,\r\n PopoverTrigger,\r\n} from \"@/components/shadcn/popover\";\r\nimport {\r\n Command,\r\n CommandEmpty,\r\n CommandGroup,\r\n CommandInput,\r\n CommandItem,\r\n CommandList,\r\n} from \"@/components/shadcn/command\";\r\nimport { Separator } from \"@/components/shadcn/separator\";\r\nimport { Chip } from \"@/components/ui/Chip\";\r\nimport { comboboxStyles } from \"./combobox.styles\";\r\nimport type { ComboboxOption, ComboboxProps } from \"./combobox.types\";\r\n\r\n/**\r\n * Combobox — select com BUSCA (autocomplete) e lista ROLÁVEL, em escolha única\r\n * (default) ou múltipla (`multiple`).\r\n *\r\n * Compõe `Popover` + `Command` (cmdk): o trigger imita o `SelectTrigger` (parear\r\n * com Selects irmãos) e o dropdown traz `CommandInput` no topo + `CommandList`\r\n * com `max-h`/scroll. Use no lugar de um `Select` quando a lista é longa e o\r\n * usuário precisa digitar pra achar a opção (ex.: escolher uma coluna entre 30).\r\n *\r\n * Selação robusta: o `onSelect` do cmdk passa um valor normalizado, então o\r\n * componente NÃO depende dele — fecha via closure sobre `option.value`. A busca\r\n * casa por `label` + `keywords` (que inclui o `value` da opção).\r\n */\r\nexport const Combobox = forwardRef<HTMLButtonElement, ComboboxProps>(\r\n function Combobox(props, ref) {\r\n const {\r\n options,\r\n placeholder = \"Selecione…\",\r\n searchPlaceholder = \"Buscar…\",\r\n emptyMessage = \"Nenhum resultado.\",\r\n open: openProp,\r\n defaultOpen,\r\n onOpenChange,\r\n align = \"start\",\r\n className,\r\n contentClassName,\r\n disabled,\r\n \"aria-label\": ariaLabel,\r\n multiple,\r\n value,\r\n onValueChange,\r\n ...rest\r\n } = props as ComboboxProps & {\r\n multiple?: boolean;\r\n value?: string | string[];\r\n onValueChange?: (v: never) => void;\r\n };\r\n const fechaAoEscolher =\r\n props.multiple === true ? false : (props.closeOnSelect ?? true);\r\n const { maxChips = 2, renderSummary } =\r\n props.multiple === true ? props : ({} as { maxChips?: number; renderSummary?: never });\r\n\r\n const [uncontrolledOpen, setUncontrolledOpen] = useState(\r\n defaultOpen ?? false,\r\n );\r\n const isControlled = openProp !== undefined;\r\n const open = isControlled ? openProp : uncontrolledOpen;\r\n const setOpen = (next: boolean) => {\r\n if (!isControlled) setUncontrolledOpen(next);\r\n onOpenChange?.(next);\r\n };\r\n\r\n const styles = comboboxStyles();\r\n\r\n const selecionados: ComboboxOption[] = multiple\r\n ? options.filter((o) => (value as string[] | undefined)?.includes(o.value))\r\n : options.filter((o) => o.value === value);\r\n\r\n const estaSelecionada = (o: ComboboxOption) =>\r\n multiple\r\n ? Boolean((value as string[] | undefined)?.includes(o.value))\r\n : o.value === value;\r\n\r\n const escolher = (o: ComboboxOption) => {\r\n if (!multiple) {\r\n (onValueChange as ((v: string) => void) | undefined)?.(o.value);\r\n if (fechaAoEscolher) setOpen(false);\r\n return;\r\n }\r\n // Toggle, e o dropdown FICA ABERTO: escolher várias de uma lista longa com o\r\n // popover fechando a cada clique custa um reabrir + re-buscar por item.\r\n const atual = (value as string[] | undefined) ?? [];\r\n const proximo = atual.includes(o.value)\r\n ? atual.filter((v) => v !== o.value)\r\n : [...atual, o.value];\r\n (onValueChange as ((v: string[]) => void) | undefined)?.(proximo);\r\n };\r\n\r\n /**\r\n * Agrupa preservando a ORDEM de `options`: as sem `group` vêm primeiro (sem\r\n * cabeçalho) e cada grupo aparece na posição da sua primeira opção. Ordenar por nome\r\n * aqui seria decidir pelo consumidor — a ordem da lista é dele.\r\n */\r\n const grupos = React.useMemo(() => {\r\n const out: { heading?: string; itens: ComboboxOption[] }[] = [];\r\n for (const o of options) {\r\n const atual = out.find((g) => g.heading === o.group);\r\n if (atual) atual.itens.push(o);\r\n else out.push({ heading: o.group, itens: [o] });\r\n }\r\n return out;\r\n }, [options]);\r\n\r\n const conteudoTrigger = () => {\r\n if (selecionados.length === 0) return placeholder;\r\n if (!multiple) return selecionados[0].label;\r\n if (renderSummary) return renderSummary(selecionados);\r\n const visiveis = selecionados.slice(0, maxChips);\r\n const resto = selecionados.length - visiveis.length;\r\n return (\r\n // Chips SEM × de propósito: o trigger é um <button>, e um botão de remover\r\n // aqui dentro seria botão aninhado — HTML inválido, e o navegador desaninha,\r\n // fazendo o × abrir o dropdown. Pra remover com ×, renderize <Chip onRemove>\r\n // ABAIXO do campo.\r\n <span className=\"flex min-w-0 items-center gap-gp-xs\">\r\n {visiveis.map((o) => (\r\n <Chip key={o.value} size=\"sm\" color=\"neutral\" variant=\"soft\">\r\n {o.label}\r\n </Chip>\r\n ))}\r\n {resto > 0 && (\r\n <span className=\"shrink-0 text-fg-muted\">+{resto}</span>\r\n )}\r\n </span>\r\n );\r\n };\r\n\r\n return (\r\n <Popover open={open} onOpenChange={setOpen}>\r\n <PopoverTrigger asChild>\r\n <button\r\n ref={ref}\r\n type=\"button\"\r\n role=\"combobox\"\r\n aria-expanded={open}\r\n aria-label={ariaLabel}\r\n disabled={disabled}\r\n data-placeholder={selecionados.length > 0 ? undefined : \"\"}\r\n className={styles.trigger({ className })}\r\n {...rest}\r\n >\r\n <span className={styles.value()}>{conteudoTrigger()}</span>\r\n <ChevronDown className={styles.icon()} aria-hidden=\"true\" />\r\n </button>\r\n </PopoverTrigger>\r\n <PopoverContent\r\n align={align}\r\n className={styles.content({ className: contentClassName })}\r\n >\r\n <Command>\r\n <CommandInput placeholder={searchPlaceholder} />\r\n <CommandList>\r\n <CommandEmpty>{emptyMessage}</CommandEmpty>\r\n {grupos.map(({ heading, itens }, i) => (\r\n <CommandGroup key={heading ?? \"__sem-grupo\"} heading={heading}>\r\n {i > 0 && !heading && <Separator className=\"my-pad-xs\" />}\r\n {itens.map((option) => {\r\n const isSelected = estaSelecionada(option);\r\n return (\r\n <CommandItem\r\n key={option.value}\r\n // `value` = o VALUE da opção, não o label. Com o label, duas\r\n // opções de mesmo rótulo (duas \"Matriz\", em unidades\r\n // diferentes) colidem no cmdk: ele casa pelo value, então uma\r\n // some da lista filtrada e a outra recebe o clique das duas.\r\n // O label continua pesquisável por `keywords`.\r\n value={option.value}\r\n keywords={[\r\n option.label,\r\n ...(option.hint ? [option.hint] : []),\r\n ...(option.keywords ?? []),\r\n ]}\r\n // `aria-selected` é do cmdk (item ativo do teclado). Em multi,\r\n // quem diz \"marcado\" é `aria-checked` + role de opção múltipla.\r\n role={multiple ? \"option\" : undefined}\r\n aria-checked={multiple ? isSelected : undefined}\r\n onSelect={() => escolher(option)}\r\n className={cn(\r\n isSelected &&\r\n \"text-fg-default font-medium [&_svg]:text-fg-brand\",\r\n )}\r\n >\r\n <span className={styles.itemTexto()}>\r\n <span className={styles.itemLabel()}>{option.label}</span>\r\n {option.hint && (\r\n <span className={styles.itemHint()}>{option.hint}</span>\r\n )}\r\n </span>\r\n {isSelected && <Check aria-hidden=\"true\" />}\r\n </CommandItem>\r\n );\r\n })}\r\n </CommandGroup>\r\n ))}\r\n </CommandList>\r\n </Command>\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n },\r\n);\r\nCombobox.displayName = \"Combobox\";\r\n"],"names":["forwardRef","Combobox","useState","comboboxStyles","React","jsxs","jsx","Chip","Popover","PopoverTrigger","ChevronDown","PopoverContent","Command","CommandInput","CommandList","CommandEmpty","CommandGroup","Separator","CommandItem","cn","Check"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ComboboxProps } from './combobox.types';
|
|
2
|
+
import * as React from "react";
|
|
2
3
|
/**
|
|
3
4
|
* Combobox — select com BUSCA (autocomplete) e lista ROLÁVEL, em escolha única
|
|
4
5
|
* (default) ou múltipla (`multiple`).
|
|
@@ -12,5 +13,5 @@ import { ComboboxProps } from './combobox.types';
|
|
|
12
13
|
* componente NÃO depende dele — fecha via closure sobre `option.value`. A busca
|
|
13
14
|
* casa por `label` + `keywords` (que inclui o `value` da opção).
|
|
14
15
|
*/
|
|
15
|
-
export declare const Combobox:
|
|
16
|
+
export declare const Combobox: React.ForwardRefExoticComponent<ComboboxProps & React.RefAttributes<HTMLButtonElement>>;
|
|
16
17
|
//# sourceMappingURL=combobox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/Combobox/combobox.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/Combobox/combobox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAoB/B,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ,yFAiLpB,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
2
3
|
import { forwardRef, useState } from "react";
|
|
3
4
|
import { ChevronDown, Check } from "lucide-react";
|
|
4
5
|
import { cn } from "../../../lib/utils.mjs";
|
|
5
6
|
import { Popover, PopoverTrigger, PopoverContent } from "../../shadcn/popover.mjs";
|
|
6
7
|
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "../../shadcn/command.mjs";
|
|
8
|
+
import { Separator } from "../../shadcn/separator.mjs";
|
|
7
9
|
import { Chip } from "../Chip/chip.mjs";
|
|
8
10
|
import "../Chip/chip-group.mjs";
|
|
9
11
|
import "../Chip/chip.styles.mjs";
|
|
@@ -28,6 +30,7 @@ const Combobox = forwardRef(
|
|
|
28
30
|
onValueChange,
|
|
29
31
|
...rest
|
|
30
32
|
} = props;
|
|
33
|
+
const fechaAoEscolher = props.multiple === true ? false : props.closeOnSelect ?? true;
|
|
31
34
|
const { maxChips = 2, renderSummary } = props.multiple === true ? props : {};
|
|
32
35
|
const [uncontrolledOpen, setUncontrolledOpen] = useState(
|
|
33
36
|
defaultOpen ?? false
|
|
@@ -44,13 +47,22 @@ const Combobox = forwardRef(
|
|
|
44
47
|
const escolher = (o) => {
|
|
45
48
|
if (!multiple) {
|
|
46
49
|
onValueChange?.(o.value);
|
|
47
|
-
setOpen(false);
|
|
50
|
+
if (fechaAoEscolher) setOpen(false);
|
|
48
51
|
return;
|
|
49
52
|
}
|
|
50
53
|
const atual = value ?? [];
|
|
51
54
|
const proximo = atual.includes(o.value) ? atual.filter((v) => v !== o.value) : [...atual, o.value];
|
|
52
55
|
onValueChange?.(proximo);
|
|
53
56
|
};
|
|
57
|
+
const grupos = React.useMemo(() => {
|
|
58
|
+
const out = [];
|
|
59
|
+
for (const o of options) {
|
|
60
|
+
const atual = out.find((g) => g.heading === o.group);
|
|
61
|
+
if (atual) atual.itens.push(o);
|
|
62
|
+
else out.push({ heading: o.group, itens: [o] });
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}, [options]);
|
|
54
66
|
const conteudoTrigger = () => {
|
|
55
67
|
if (selecionados.length === 0) return placeholder;
|
|
56
68
|
if (!multiple) return selecionados[0].label;
|
|
@@ -99,27 +111,37 @@ const Combobox = forwardRef(
|
|
|
99
111
|
/* @__PURE__ */ jsx(CommandInput, { placeholder: searchPlaceholder }),
|
|
100
112
|
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
101
113
|
/* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
|
|
102
|
-
/* @__PURE__ */
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
114
|
+
grupos.map(({ heading, itens }, i) => /* @__PURE__ */ jsxs(CommandGroup, { heading, children: [
|
|
115
|
+
i > 0 && !heading && /* @__PURE__ */ jsx(Separator, { className: "my-pad-xs" }),
|
|
116
|
+
itens.map((option) => {
|
|
117
|
+
const isSelected = estaSelecionada(option);
|
|
118
|
+
return /* @__PURE__ */ jsxs(
|
|
119
|
+
CommandItem,
|
|
120
|
+
{
|
|
121
|
+
value: option.value,
|
|
122
|
+
keywords: [
|
|
123
|
+
option.label,
|
|
124
|
+
...option.hint ? [option.hint] : [],
|
|
125
|
+
...option.keywords ?? []
|
|
126
|
+
],
|
|
127
|
+
role: multiple ? "option" : void 0,
|
|
128
|
+
"aria-checked": multiple ? isSelected : void 0,
|
|
129
|
+
onSelect: () => escolher(option),
|
|
130
|
+
className: cn(
|
|
131
|
+
isSelected && "text-fg-default font-medium [&_svg]:text-fg-brand"
|
|
132
|
+
),
|
|
133
|
+
children: [
|
|
134
|
+
/* @__PURE__ */ jsxs("span", { className: styles.itemTexto(), children: [
|
|
135
|
+
/* @__PURE__ */ jsx("span", { className: styles.itemLabel(), children: option.label }),
|
|
136
|
+
option.hint && /* @__PURE__ */ jsx("span", { className: styles.itemHint(), children: option.hint })
|
|
137
|
+
] }),
|
|
138
|
+
isSelected && /* @__PURE__ */ jsx(Check, { "aria-hidden": "true" })
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
option.value
|
|
142
|
+
);
|
|
143
|
+
})
|
|
144
|
+
] }, heading ?? "__sem-grupo"))
|
|
123
145
|
] })
|
|
124
146
|
] })
|
|
125
147
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.mjs","sources":["../../../../../src/components/ui/Combobox/combobox.tsx"],"sourcesContent":["import { forwardRef, useState } from \"react\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/components/shadcn/popover\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@/components/shadcn/command\";\nimport { Chip } from \"@/components/ui/Chip\";\nimport { comboboxStyles } from \"./combobox.styles\";\nimport type { ComboboxOption, ComboboxProps } from \"./combobox.types\";\n\n/**\n * Combobox — select com BUSCA (autocomplete) e lista ROLÁVEL, em escolha única\n * (default) ou múltipla (`multiple`).\n *\n * Compõe `Popover` + `Command` (cmdk): o trigger imita o `SelectTrigger` (parear\n * com Selects irmãos) e o dropdown traz `CommandInput` no topo + `CommandList`\n * com `max-h`/scroll. Use no lugar de um `Select` quando a lista é longa e o\n * usuário precisa digitar pra achar a opção (ex.: escolher uma coluna entre 30).\n *\n * Selação robusta: o `onSelect` do cmdk passa um valor normalizado, então o\n * componente NÃO depende dele — fecha via closure sobre `option.value`. A busca\n * casa por `label` + `keywords` (que inclui o `value` da opção).\n */\nexport const Combobox = forwardRef<HTMLButtonElement, ComboboxProps>(\n function Combobox(props, ref) {\n const {\n options,\n placeholder = \"Selecione…\",\n searchPlaceholder = \"Buscar…\",\n emptyMessage = \"Nenhum resultado.\",\n open: openProp,\n defaultOpen,\n onOpenChange,\n align = \"start\",\n className,\n contentClassName,\n disabled,\n \"aria-label\": ariaLabel,\n multiple,\n value,\n onValueChange,\n ...rest\n } = props as ComboboxProps & {\n multiple?: boolean;\n value?: string | string[];\n onValueChange?: (v: never) => void;\n };\n const { maxChips = 2, renderSummary } =\n props.multiple === true ? props : ({} as { maxChips?: number; renderSummary?: never });\n\n const [uncontrolledOpen, setUncontrolledOpen] = useState(\n defaultOpen ?? false,\n );\n const isControlled = openProp !== undefined;\n const open = isControlled ? openProp : uncontrolledOpen;\n const setOpen = (next: boolean) => {\n if (!isControlled) setUncontrolledOpen(next);\n onOpenChange?.(next);\n };\n\n const styles = comboboxStyles();\n\n const selecionados: ComboboxOption[] = multiple\n ? options.filter((o) => (value as string[] | undefined)?.includes(o.value))\n : options.filter((o) => o.value === value);\n\n const estaSelecionada = (o: ComboboxOption) =>\n multiple\n ? Boolean((value as string[] | undefined)?.includes(o.value))\n : o.value === value;\n\n const escolher = (o: ComboboxOption) => {\n if (!multiple) {\n (onValueChange as ((v: string) => void) | undefined)?.(o.value);\n setOpen(false);\n return;\n }\n // Toggle, e o dropdown FICA ABERTO: escolher várias de uma lista longa com o\n // popover fechando a cada clique custa um reabrir + re-buscar por item.\n const atual = (value as string[] | undefined) ?? [];\n const proximo = atual.includes(o.value)\n ? atual.filter((v) => v !== o.value)\n : [...atual, o.value];\n (onValueChange as ((v: string[]) => void) | undefined)?.(proximo);\n };\n\n const conteudoTrigger = () => {\n if (selecionados.length === 0) return placeholder;\n if (!multiple) return selecionados[0].label;\n if (renderSummary) return renderSummary(selecionados);\n const visiveis = selecionados.slice(0, maxChips);\n const resto = selecionados.length - visiveis.length;\n return (\n // Chips SEM × de propósito: o trigger é um <button>, e um botão de remover\n // aqui dentro seria botão aninhado — HTML inválido, e o navegador desaninha,\n // fazendo o × abrir o dropdown. Pra remover com ×, renderize <Chip onRemove>\n // ABAIXO do campo.\n <span className=\"flex min-w-0 items-center gap-gp-xs\">\n {visiveis.map((o) => (\n <Chip key={o.value} size=\"sm\" color=\"neutral\" variant=\"soft\">\n {o.label}\n </Chip>\n ))}\n {resto > 0 && (\n <span className=\"shrink-0 text-fg-muted\">+{resto}</span>\n )}\n </span>\n );\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n ref={ref}\n type=\"button\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n disabled={disabled}\n data-placeholder={selecionados.length > 0 ? undefined : \"\"}\n className={styles.trigger({ className })}\n {...rest}\n >\n <span className={styles.value()}>{conteudoTrigger()}</span>\n <ChevronDown className={styles.icon()} aria-hidden=\"true\" />\n </button>\n </PopoverTrigger>\n <PopoverContent\n align={align}\n className={styles.content({ className: contentClassName })}\n >\n <Command>\n <CommandInput placeholder={searchPlaceholder} />\n <CommandList>\n <CommandEmpty>{emptyMessage}</CommandEmpty>\n <CommandGroup>\n {options.map((option) => {\n const isSelected = estaSelecionada(option);\n return (\n <CommandItem\n key={option.value}\n value={option.label}\n keywords={[option.value, ...(option.keywords ?? [])]}\n // `aria-selected` é do cmdk (item ativo do teclado). Em multi,\n // quem diz \"marcado\" é `aria-checked` + role de opção múltipla.\n role={multiple ? \"option\" : undefined}\n aria-checked={multiple ? isSelected : undefined}\n onSelect={() => escolher(option)}\n className={cn(\n isSelected &&\n \"text-fg-default font-medium [&_svg]:text-fg-brand\",\n )}\n >\n <span className={styles.itemLabel()}>{option.label}</span>\n {isSelected && <Check aria-hidden=\"true\" />}\n </CommandItem>\n );\n })}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n },\n);\nCombobox.displayName = \"Combobox\";\n"],"names":["Combobox"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"combobox.mjs","sources":["../../../../../src/components/ui/Combobox/combobox.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport { forwardRef, useState } from \"react\";\r\nimport { Check, ChevronDown } from \"lucide-react\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Popover,\r\n PopoverContent,\r\n PopoverTrigger,\r\n} from \"@/components/shadcn/popover\";\r\nimport {\r\n Command,\r\n CommandEmpty,\r\n CommandGroup,\r\n CommandInput,\r\n CommandItem,\r\n CommandList,\r\n} from \"@/components/shadcn/command\";\r\nimport { Separator } from \"@/components/shadcn/separator\";\r\nimport { Chip } from \"@/components/ui/Chip\";\r\nimport { comboboxStyles } from \"./combobox.styles\";\r\nimport type { ComboboxOption, ComboboxProps } from \"./combobox.types\";\r\n\r\n/**\r\n * Combobox — select com BUSCA (autocomplete) e lista ROLÁVEL, em escolha única\r\n * (default) ou múltipla (`multiple`).\r\n *\r\n * Compõe `Popover` + `Command` (cmdk): o trigger imita o `SelectTrigger` (parear\r\n * com Selects irmãos) e o dropdown traz `CommandInput` no topo + `CommandList`\r\n * com `max-h`/scroll. Use no lugar de um `Select` quando a lista é longa e o\r\n * usuário precisa digitar pra achar a opção (ex.: escolher uma coluna entre 30).\r\n *\r\n * Selação robusta: o `onSelect` do cmdk passa um valor normalizado, então o\r\n * componente NÃO depende dele — fecha via closure sobre `option.value`. A busca\r\n * casa por `label` + `keywords` (que inclui o `value` da opção).\r\n */\r\nexport const Combobox = forwardRef<HTMLButtonElement, ComboboxProps>(\r\n function Combobox(props, ref) {\r\n const {\r\n options,\r\n placeholder = \"Selecione…\",\r\n searchPlaceholder = \"Buscar…\",\r\n emptyMessage = \"Nenhum resultado.\",\r\n open: openProp,\r\n defaultOpen,\r\n onOpenChange,\r\n align = \"start\",\r\n className,\r\n contentClassName,\r\n disabled,\r\n \"aria-label\": ariaLabel,\r\n multiple,\r\n value,\r\n onValueChange,\r\n ...rest\r\n } = props as ComboboxProps & {\r\n multiple?: boolean;\r\n value?: string | string[];\r\n onValueChange?: (v: never) => void;\r\n };\r\n const fechaAoEscolher =\r\n props.multiple === true ? false : (props.closeOnSelect ?? true);\r\n const { maxChips = 2, renderSummary } =\r\n props.multiple === true ? props : ({} as { maxChips?: number; renderSummary?: never });\r\n\r\n const [uncontrolledOpen, setUncontrolledOpen] = useState(\r\n defaultOpen ?? false,\r\n );\r\n const isControlled = openProp !== undefined;\r\n const open = isControlled ? openProp : uncontrolledOpen;\r\n const setOpen = (next: boolean) => {\r\n if (!isControlled) setUncontrolledOpen(next);\r\n onOpenChange?.(next);\r\n };\r\n\r\n const styles = comboboxStyles();\r\n\r\n const selecionados: ComboboxOption[] = multiple\r\n ? options.filter((o) => (value as string[] | undefined)?.includes(o.value))\r\n : options.filter((o) => o.value === value);\r\n\r\n const estaSelecionada = (o: ComboboxOption) =>\r\n multiple\r\n ? Boolean((value as string[] | undefined)?.includes(o.value))\r\n : o.value === value;\r\n\r\n const escolher = (o: ComboboxOption) => {\r\n if (!multiple) {\r\n (onValueChange as ((v: string) => void) | undefined)?.(o.value);\r\n if (fechaAoEscolher) setOpen(false);\r\n return;\r\n }\r\n // Toggle, e o dropdown FICA ABERTO: escolher várias de uma lista longa com o\r\n // popover fechando a cada clique custa um reabrir + re-buscar por item.\r\n const atual = (value as string[] | undefined) ?? [];\r\n const proximo = atual.includes(o.value)\r\n ? atual.filter((v) => v !== o.value)\r\n : [...atual, o.value];\r\n (onValueChange as ((v: string[]) => void) | undefined)?.(proximo);\r\n };\r\n\r\n /**\r\n * Agrupa preservando a ORDEM de `options`: as sem `group` vêm primeiro (sem\r\n * cabeçalho) e cada grupo aparece na posição da sua primeira opção. Ordenar por nome\r\n * aqui seria decidir pelo consumidor — a ordem da lista é dele.\r\n */\r\n const grupos = React.useMemo(() => {\r\n const out: { heading?: string; itens: ComboboxOption[] }[] = [];\r\n for (const o of options) {\r\n const atual = out.find((g) => g.heading === o.group);\r\n if (atual) atual.itens.push(o);\r\n else out.push({ heading: o.group, itens: [o] });\r\n }\r\n return out;\r\n }, [options]);\r\n\r\n const conteudoTrigger = () => {\r\n if (selecionados.length === 0) return placeholder;\r\n if (!multiple) return selecionados[0].label;\r\n if (renderSummary) return renderSummary(selecionados);\r\n const visiveis = selecionados.slice(0, maxChips);\r\n const resto = selecionados.length - visiveis.length;\r\n return (\r\n // Chips SEM × de propósito: o trigger é um <button>, e um botão de remover\r\n // aqui dentro seria botão aninhado — HTML inválido, e o navegador desaninha,\r\n // fazendo o × abrir o dropdown. Pra remover com ×, renderize <Chip onRemove>\r\n // ABAIXO do campo.\r\n <span className=\"flex min-w-0 items-center gap-gp-xs\">\r\n {visiveis.map((o) => (\r\n <Chip key={o.value} size=\"sm\" color=\"neutral\" variant=\"soft\">\r\n {o.label}\r\n </Chip>\r\n ))}\r\n {resto > 0 && (\r\n <span className=\"shrink-0 text-fg-muted\">+{resto}</span>\r\n )}\r\n </span>\r\n );\r\n };\r\n\r\n return (\r\n <Popover open={open} onOpenChange={setOpen}>\r\n <PopoverTrigger asChild>\r\n <button\r\n ref={ref}\r\n type=\"button\"\r\n role=\"combobox\"\r\n aria-expanded={open}\r\n aria-label={ariaLabel}\r\n disabled={disabled}\r\n data-placeholder={selecionados.length > 0 ? undefined : \"\"}\r\n className={styles.trigger({ className })}\r\n {...rest}\r\n >\r\n <span className={styles.value()}>{conteudoTrigger()}</span>\r\n <ChevronDown className={styles.icon()} aria-hidden=\"true\" />\r\n </button>\r\n </PopoverTrigger>\r\n <PopoverContent\r\n align={align}\r\n className={styles.content({ className: contentClassName })}\r\n >\r\n <Command>\r\n <CommandInput placeholder={searchPlaceholder} />\r\n <CommandList>\r\n <CommandEmpty>{emptyMessage}</CommandEmpty>\r\n {grupos.map(({ heading, itens }, i) => (\r\n <CommandGroup key={heading ?? \"__sem-grupo\"} heading={heading}>\r\n {i > 0 && !heading && <Separator className=\"my-pad-xs\" />}\r\n {itens.map((option) => {\r\n const isSelected = estaSelecionada(option);\r\n return (\r\n <CommandItem\r\n key={option.value}\r\n // `value` = o VALUE da opção, não o label. Com o label, duas\r\n // opções de mesmo rótulo (duas \"Matriz\", em unidades\r\n // diferentes) colidem no cmdk: ele casa pelo value, então uma\r\n // some da lista filtrada e a outra recebe o clique das duas.\r\n // O label continua pesquisável por `keywords`.\r\n value={option.value}\r\n keywords={[\r\n option.label,\r\n ...(option.hint ? [option.hint] : []),\r\n ...(option.keywords ?? []),\r\n ]}\r\n // `aria-selected` é do cmdk (item ativo do teclado). Em multi,\r\n // quem diz \"marcado\" é `aria-checked` + role de opção múltipla.\r\n role={multiple ? \"option\" : undefined}\r\n aria-checked={multiple ? isSelected : undefined}\r\n onSelect={() => escolher(option)}\r\n className={cn(\r\n isSelected &&\r\n \"text-fg-default font-medium [&_svg]:text-fg-brand\",\r\n )}\r\n >\r\n <span className={styles.itemTexto()}>\r\n <span className={styles.itemLabel()}>{option.label}</span>\r\n {option.hint && (\r\n <span className={styles.itemHint()}>{option.hint}</span>\r\n )}\r\n </span>\r\n {isSelected && <Check aria-hidden=\"true\" />}\r\n </CommandItem>\r\n );\r\n })}\r\n </CommandGroup>\r\n ))}\r\n </CommandList>\r\n </Command>\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n },\r\n);\r\nCombobox.displayName = \"Combobox\";\r\n"],"names":["Combobox"],"mappings":"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"}
|
|
@@ -25,7 +25,11 @@ const comboboxStyles = tv.tv({
|
|
|
25
25
|
// padding e o scroll da lista.
|
|
26
26
|
content: "w-[var(--radix-popover-trigger-width)] overflow-hidden p-0",
|
|
27
27
|
// Item da lista: ocupa a largura e trunca rótulos longos.
|
|
28
|
-
itemLabel: "min-w-0 flex-1 truncate"
|
|
28
|
+
itemLabel: "min-w-0 flex-1 truncate",
|
|
29
|
+
/** Coluna label + hint, pra o check ficar alinhado à direita. */
|
|
30
|
+
itemTexto: "flex min-w-0 flex-1 flex-col gap-gp-2xs",
|
|
31
|
+
/** Linha secundária da opção — código, e-mail, unidade. */
|
|
32
|
+
itemHint: "truncate text-caption-sm text-fg-subtle"
|
|
29
33
|
}
|
|
30
34
|
});
|
|
31
35
|
exports.comboboxStyles = comboboxStyles;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.styles.cjs","sources":["../../../../../src/components/ui/Combobox/combobox.styles.ts"],"sourcesContent":["import { tv } from \"@/utils/tv\";\r\n\r\n/**\r\n * Combobox — select de escolha única com busca (autocomplete) + lista rolável.\r\n *\r\n * O `trigger` espelha 1:1 o `SelectTrigger` do shadcn (mesma altura, borda,\r\n * radius, hover e foco) pra que um Combobox e um Select fiquem visualmente\r\n * idênticos lado a lado (ex.: os 3 campos do filter-popover: Campo/Operador/Valor).\r\n * A lista, o input de busca e os items são do `Command` (cmdk) — já tokenizados.\r\n *\r\n * Foco: padrão \"input\" do DS (mesmo do SelectTrigger) — `shadow-sh-ring` no\r\n * focus-visible e no `data-[state=open]`, NÃO `ring-4` (que é o padrão de botão).\r\n * Assim o anel de foco bate com os Selects irmãos.\r\n */\r\nexport const comboboxStyles = tv({\r\n slots: {\r\n trigger: [\r\n \"flex min-h-form-lg w-full items-center justify-between gap-gp-md\",\r\n \"rounded-radius-lg px-pad-xl\",\r\n \"bg-bg-input dark:bg-bg-muted\",\r\n \"hover:bg-bg-input-hover dark:hover:bg-bg-muted-hover\",\r\n \"border border-border-input\",\r\n \"text-body-sm font-normal text-fg-default text-left\",\r\n \"transition-[color,box-shadow,background-color,border-color]\",\r\n \"focus-visible:outline-none\",\r\n \"data-[placeholder]:text-fg-muted data-[placeholder]:opacity-70\",\r\n \"focus-visible:border-border-brand data-[state=open]:border-border-brand\",\r\n \"focus-visible:shadow-sh-ring data-[state=open]:shadow-sh-ring\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n \"disabled:hover:bg-bg-input dark:disabled:hover:bg-bg-muted\",\r\n ],\r\n value: \"min-w-0 flex-1 truncate text-left\",\r\n icon: \"size-4 shrink-0 text-fg-muted\",\r\n // Dropdown alinhado à largura do trigger (mesmo pattern do Select via\r\n // `--radix-*-trigger-width`). `p-0`/overflow: o Command interno já provê o\r\n // padding e o scroll da lista.\r\n content: \"w-[var(--radix-popover-trigger-width)] overflow-hidden p-0\",\r\n // Item da lista: ocupa a largura e trunca rótulos longos.\r\n itemLabel: \"min-w-0 flex-1 truncate\",\r\n },\r\n});\r\n"],"names":["tv"],"mappings":";;;AAcO,MAAM,iBAAiBA,GAAAA,GAAG;AAAA,EAC/B,OAAO;AAAA,IACL,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEF,OAAO;AAAA,IACP,MAAM;AAAA;AAAA;AAAA;AAAA,IAIN,SAAS;AAAA;AAAA,IAET,WAAW;AAAA,EAAA;
|
|
1
|
+
{"version":3,"file":"combobox.styles.cjs","sources":["../../../../../src/components/ui/Combobox/combobox.styles.ts"],"sourcesContent":["import { tv } from \"@/utils/tv\";\r\n\r\n/**\r\n * Combobox — select de escolha única com busca (autocomplete) + lista rolável.\r\n *\r\n * O `trigger` espelha 1:1 o `SelectTrigger` do shadcn (mesma altura, borda,\r\n * radius, hover e foco) pra que um Combobox e um Select fiquem visualmente\r\n * idênticos lado a lado (ex.: os 3 campos do filter-popover: Campo/Operador/Valor).\r\n * A lista, o input de busca e os items são do `Command` (cmdk) — já tokenizados.\r\n *\r\n * Foco: padrão \"input\" do DS (mesmo do SelectTrigger) — `shadow-sh-ring` no\r\n * focus-visible e no `data-[state=open]`, NÃO `ring-4` (que é o padrão de botão).\r\n * Assim o anel de foco bate com os Selects irmãos.\r\n */\r\nexport const comboboxStyles = tv({\r\n slots: {\r\n trigger: [\r\n \"flex min-h-form-lg w-full items-center justify-between gap-gp-md\",\r\n \"rounded-radius-lg px-pad-xl\",\r\n \"bg-bg-input dark:bg-bg-muted\",\r\n \"hover:bg-bg-input-hover dark:hover:bg-bg-muted-hover\",\r\n \"border border-border-input\",\r\n \"text-body-sm font-normal text-fg-default text-left\",\r\n \"transition-[color,box-shadow,background-color,border-color]\",\r\n \"focus-visible:outline-none\",\r\n \"data-[placeholder]:text-fg-muted data-[placeholder]:opacity-70\",\r\n \"focus-visible:border-border-brand data-[state=open]:border-border-brand\",\r\n \"focus-visible:shadow-sh-ring data-[state=open]:shadow-sh-ring\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n \"disabled:hover:bg-bg-input dark:disabled:hover:bg-bg-muted\",\r\n ],\r\n value: \"min-w-0 flex-1 truncate text-left\",\r\n icon: \"size-4 shrink-0 text-fg-muted\",\r\n // Dropdown alinhado à largura do trigger (mesmo pattern do Select via\r\n // `--radix-*-trigger-width`). `p-0`/overflow: o Command interno já provê o\r\n // padding e o scroll da lista.\r\n content: \"w-[var(--radix-popover-trigger-width)] overflow-hidden p-0\",\r\n // Item da lista: ocupa a largura e trunca rótulos longos.\r\n itemLabel: \"min-w-0 flex-1 truncate\",\r\n /** Coluna label + hint, pra o check ficar alinhado à direita. */\r\n itemTexto: \"flex min-w-0 flex-1 flex-col gap-gp-2xs\",\r\n /** Linha secundária da opção — código, e-mail, unidade. */\r\n itemHint: \"truncate text-caption-sm text-fg-subtle\",\r\n },\r\n});\r\n"],"names":["tv"],"mappings":";;;AAcO,MAAM,iBAAiBA,GAAAA,GAAG;AAAA,EAC/B,OAAO;AAAA,IACL,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEF,OAAO;AAAA,IACP,MAAM;AAAA;AAAA;AAAA;AAAA,IAIN,SAAS;AAAA;AAAA,IAET,WAAW;AAAA;AAAA,IAEX,WAAW;AAAA;AAAA,IAEX,UAAU;AAAA,EAAA;AAEd,CAAC;;"}
|
|
@@ -16,8 +16,10 @@ export declare const comboboxStyles: import('tailwind-variants').TVReturnType<{
|
|
|
16
16
|
content?: import('tailwind-merge').ClassNameValue;
|
|
17
17
|
value?: import('tailwind-merge').ClassNameValue;
|
|
18
18
|
icon?: import('tailwind-merge').ClassNameValue;
|
|
19
|
+
itemTexto?: import('tailwind-merge').ClassNameValue;
|
|
19
20
|
itemLabel?: import('tailwind-merge').ClassNameValue;
|
|
20
21
|
trigger?: import('tailwind-merge').ClassNameValue;
|
|
22
|
+
itemHint?: import('tailwind-merge').ClassNameValue;
|
|
21
23
|
};
|
|
22
24
|
};
|
|
23
25
|
} | {
|
|
@@ -26,8 +28,10 @@ export declare const comboboxStyles: import('tailwind-variants').TVReturnType<{
|
|
|
26
28
|
content?: import('tailwind-merge').ClassNameValue;
|
|
27
29
|
value?: import('tailwind-merge').ClassNameValue;
|
|
28
30
|
icon?: import('tailwind-merge').ClassNameValue;
|
|
31
|
+
itemTexto?: import('tailwind-merge').ClassNameValue;
|
|
29
32
|
itemLabel?: import('tailwind-merge').ClassNameValue;
|
|
30
33
|
trigger?: import('tailwind-merge').ClassNameValue;
|
|
34
|
+
itemHint?: import('tailwind-merge').ClassNameValue;
|
|
31
35
|
};
|
|
32
36
|
};
|
|
33
37
|
} | {}, {
|
|
@@ -36,14 +40,20 @@ export declare const comboboxStyles: import('tailwind-variants').TVReturnType<{
|
|
|
36
40
|
icon: string;
|
|
37
41
|
content: string;
|
|
38
42
|
itemLabel: string;
|
|
43
|
+
/** Coluna label + hint, pra o check ficar alinhado à direita. */
|
|
44
|
+
itemTexto: string;
|
|
45
|
+
/** Linha secundária da opção — código, e-mail, unidade. */
|
|
46
|
+
itemHint: string;
|
|
39
47
|
}, undefined, {
|
|
40
48
|
[key: string]: {
|
|
41
49
|
[key: string]: import('tailwind-merge').ClassNameValue | {
|
|
42
50
|
content?: import('tailwind-merge').ClassNameValue;
|
|
43
51
|
value?: import('tailwind-merge').ClassNameValue;
|
|
44
52
|
icon?: import('tailwind-merge').ClassNameValue;
|
|
53
|
+
itemTexto?: import('tailwind-merge').ClassNameValue;
|
|
45
54
|
itemLabel?: import('tailwind-merge').ClassNameValue;
|
|
46
55
|
trigger?: import('tailwind-merge').ClassNameValue;
|
|
56
|
+
itemHint?: import('tailwind-merge').ClassNameValue;
|
|
47
57
|
};
|
|
48
58
|
};
|
|
49
59
|
} | {}, {
|
|
@@ -52,11 +62,19 @@ export declare const comboboxStyles: import('tailwind-variants').TVReturnType<{
|
|
|
52
62
|
icon: string;
|
|
53
63
|
content: string;
|
|
54
64
|
itemLabel: string;
|
|
65
|
+
/** Coluna label + hint, pra o check ficar alinhado à direita. */
|
|
66
|
+
itemTexto: string;
|
|
67
|
+
/** Linha secundária da opção — código, e-mail, unidade. */
|
|
68
|
+
itemHint: string;
|
|
55
69
|
}, import('tailwind-variants').TVReturnType<unknown, {
|
|
56
70
|
trigger: string[];
|
|
57
71
|
value: string;
|
|
58
72
|
icon: string;
|
|
59
73
|
content: string;
|
|
60
74
|
itemLabel: string;
|
|
75
|
+
/** Coluna label + hint, pra o check ficar alinhado à direita. */
|
|
76
|
+
itemTexto: string;
|
|
77
|
+
/** Linha secundária da opção — código, e-mail, unidade. */
|
|
78
|
+
itemHint: string;
|
|
61
79
|
}, undefined, unknown, unknown, undefined>>;
|
|
62
80
|
//# sourceMappingURL=combobox.styles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.styles.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/Combobox/combobox.styles.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"combobox.styles.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/Combobox/combobox.styles.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyBvB,iEAAiE;;IAEjE,2DAA2D;;;;;;;;;;;;;;;;;;;;IAF3D,iEAAiE;;IAEjE,2DAA2D;;;;;;;;IAF3D,iEAAiE;;IAEjE,2DAA2D;;2CAG7D,CAAC"}
|
|
@@ -23,7 +23,11 @@ const comboboxStyles = tv({
|
|
|
23
23
|
// padding e o scroll da lista.
|
|
24
24
|
content: "w-[var(--radix-popover-trigger-width)] overflow-hidden p-0",
|
|
25
25
|
// Item da lista: ocupa a largura e trunca rótulos longos.
|
|
26
|
-
itemLabel: "min-w-0 flex-1 truncate"
|
|
26
|
+
itemLabel: "min-w-0 flex-1 truncate",
|
|
27
|
+
/** Coluna label + hint, pra o check ficar alinhado à direita. */
|
|
28
|
+
itemTexto: "flex min-w-0 flex-1 flex-col gap-gp-2xs",
|
|
29
|
+
/** Linha secundária da opção — código, e-mail, unidade. */
|
|
30
|
+
itemHint: "truncate text-caption-sm text-fg-subtle"
|
|
27
31
|
}
|
|
28
32
|
});
|
|
29
33
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.styles.mjs","sources":["../../../../../src/components/ui/Combobox/combobox.styles.ts"],"sourcesContent":["import { tv } from \"@/utils/tv\";\r\n\r\n/**\r\n * Combobox — select de escolha única com busca (autocomplete) + lista rolável.\r\n *\r\n * O `trigger` espelha 1:1 o `SelectTrigger` do shadcn (mesma altura, borda,\r\n * radius, hover e foco) pra que um Combobox e um Select fiquem visualmente\r\n * idênticos lado a lado (ex.: os 3 campos do filter-popover: Campo/Operador/Valor).\r\n * A lista, o input de busca e os items são do `Command` (cmdk) — já tokenizados.\r\n *\r\n * Foco: padrão \"input\" do DS (mesmo do SelectTrigger) — `shadow-sh-ring` no\r\n * focus-visible e no `data-[state=open]`, NÃO `ring-4` (que é o padrão de botão).\r\n * Assim o anel de foco bate com os Selects irmãos.\r\n */\r\nexport const comboboxStyles = tv({\r\n slots: {\r\n trigger: [\r\n \"flex min-h-form-lg w-full items-center justify-between gap-gp-md\",\r\n \"rounded-radius-lg px-pad-xl\",\r\n \"bg-bg-input dark:bg-bg-muted\",\r\n \"hover:bg-bg-input-hover dark:hover:bg-bg-muted-hover\",\r\n \"border border-border-input\",\r\n \"text-body-sm font-normal text-fg-default text-left\",\r\n \"transition-[color,box-shadow,background-color,border-color]\",\r\n \"focus-visible:outline-none\",\r\n \"data-[placeholder]:text-fg-muted data-[placeholder]:opacity-70\",\r\n \"focus-visible:border-border-brand data-[state=open]:border-border-brand\",\r\n \"focus-visible:shadow-sh-ring data-[state=open]:shadow-sh-ring\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n \"disabled:hover:bg-bg-input dark:disabled:hover:bg-bg-muted\",\r\n ],\r\n value: \"min-w-0 flex-1 truncate text-left\",\r\n icon: \"size-4 shrink-0 text-fg-muted\",\r\n // Dropdown alinhado à largura do trigger (mesmo pattern do Select via\r\n // `--radix-*-trigger-width`). `p-0`/overflow: o Command interno já provê o\r\n // padding e o scroll da lista.\r\n content: \"w-[var(--radix-popover-trigger-width)] overflow-hidden p-0\",\r\n // Item da lista: ocupa a largura e trunca rótulos longos.\r\n itemLabel: \"min-w-0 flex-1 truncate\",\r\n },\r\n});\r\n"],"names":[],"mappings":";AAcO,MAAM,iBAAiB,GAAG;AAAA,EAC/B,OAAO;AAAA,IACL,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEF,OAAO;AAAA,IACP,MAAM;AAAA;AAAA;AAAA;AAAA,IAIN,SAAS;AAAA;AAAA,IAET,WAAW;AAAA,EAAA;
|
|
1
|
+
{"version":3,"file":"combobox.styles.mjs","sources":["../../../../../src/components/ui/Combobox/combobox.styles.ts"],"sourcesContent":["import { tv } from \"@/utils/tv\";\r\n\r\n/**\r\n * Combobox — select de escolha única com busca (autocomplete) + lista rolável.\r\n *\r\n * O `trigger` espelha 1:1 o `SelectTrigger` do shadcn (mesma altura, borda,\r\n * radius, hover e foco) pra que um Combobox e um Select fiquem visualmente\r\n * idênticos lado a lado (ex.: os 3 campos do filter-popover: Campo/Operador/Valor).\r\n * A lista, o input de busca e os items são do `Command` (cmdk) — já tokenizados.\r\n *\r\n * Foco: padrão \"input\" do DS (mesmo do SelectTrigger) — `shadow-sh-ring` no\r\n * focus-visible e no `data-[state=open]`, NÃO `ring-4` (que é o padrão de botão).\r\n * Assim o anel de foco bate com os Selects irmãos.\r\n */\r\nexport const comboboxStyles = tv({\r\n slots: {\r\n trigger: [\r\n \"flex min-h-form-lg w-full items-center justify-between gap-gp-md\",\r\n \"rounded-radius-lg px-pad-xl\",\r\n \"bg-bg-input dark:bg-bg-muted\",\r\n \"hover:bg-bg-input-hover dark:hover:bg-bg-muted-hover\",\r\n \"border border-border-input\",\r\n \"text-body-sm font-normal text-fg-default text-left\",\r\n \"transition-[color,box-shadow,background-color,border-color]\",\r\n \"focus-visible:outline-none\",\r\n \"data-[placeholder]:text-fg-muted data-[placeholder]:opacity-70\",\r\n \"focus-visible:border-border-brand data-[state=open]:border-border-brand\",\r\n \"focus-visible:shadow-sh-ring data-[state=open]:shadow-sh-ring\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n \"disabled:hover:bg-bg-input dark:disabled:hover:bg-bg-muted\",\r\n ],\r\n value: \"min-w-0 flex-1 truncate text-left\",\r\n icon: \"size-4 shrink-0 text-fg-muted\",\r\n // Dropdown alinhado à largura do trigger (mesmo pattern do Select via\r\n // `--radix-*-trigger-width`). `p-0`/overflow: o Command interno já provê o\r\n // padding e o scroll da lista.\r\n content: \"w-[var(--radix-popover-trigger-width)] overflow-hidden p-0\",\r\n // Item da lista: ocupa a largura e trunca rótulos longos.\r\n itemLabel: \"min-w-0 flex-1 truncate\",\r\n /** Coluna label + hint, pra o check ficar alinhado à direita. */\r\n itemTexto: \"flex min-w-0 flex-1 flex-col gap-gp-2xs\",\r\n /** Linha secundária da opção — código, e-mail, unidade. */\r\n itemHint: \"truncate text-caption-sm text-fg-subtle\",\r\n },\r\n});\r\n"],"names":[],"mappings":";AAcO,MAAM,iBAAiB,GAAG;AAAA,EAC/B,OAAO;AAAA,IACL,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEF,OAAO;AAAA,IACP,MAAM;AAAA;AAAA;AAAA;AAAA,IAIN,SAAS;AAAA;AAAA,IAET,WAAW;AAAA;AAAA,IAEX,WAAW;AAAA;AAAA,IAEX,UAAU;AAAA,EAAA;AAEd,CAAC;"}
|
|
@@ -6,6 +6,20 @@ export type ComboboxOption = {
|
|
|
6
6
|
value: string;
|
|
7
7
|
label: string;
|
|
8
8
|
keywords?: string[];
|
|
9
|
+
/**
|
|
10
|
+
* Linha secundária na opção (código, e-mail, unidade). NÃO entra na busca por si —
|
|
11
|
+
* se quiser que o texto seja pesquisável, repita em `keywords`.
|
|
12
|
+
*/
|
|
13
|
+
hint?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Cabeçalho do grupo a que a opção pertence. Opções com o mesmo `group` são
|
|
16
|
+
* renderizadas juntas, sob esse título.
|
|
17
|
+
*
|
|
18
|
+
* É chave na OPÇÃO, e não uma mudança na forma de `options`, de propósito: quem já
|
|
19
|
+
* passa uma lista plana não muda nada, e agrupar vira acrescentar um campo. Opções
|
|
20
|
+
* sem `group` aparecem primeiro, sem cabeçalho.
|
|
21
|
+
*/
|
|
22
|
+
group?: string;
|
|
9
23
|
};
|
|
10
24
|
export interface ComboboxBaseProps extends Omit<ComponentPropsWithoutRef<"button">, "value" | "defaultValue" | "onChange"> {
|
|
11
25
|
/** Opções selecionáveis (a busca filtra por `label` + `keywords`). */
|
|
@@ -37,6 +51,13 @@ export interface ComboboxSingleProps extends ComboboxBaseProps {
|
|
|
37
51
|
value?: string;
|
|
38
52
|
/** Disparado ao escolher uma opção — recebe o `value` da opção. */
|
|
39
53
|
onValueChange?: (value: string) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Fecha o dropdown ao escolher. Default `true`.
|
|
56
|
+
*
|
|
57
|
+
* `false` mantém aberto — útil quando a escolha alimenta uma prévia ao lado e o
|
|
58
|
+
* usuário compara alternativas antes de sair.
|
|
59
|
+
*/
|
|
60
|
+
closeOnSelect?: boolean;
|
|
40
61
|
}
|
|
41
62
|
/**
|
|
42
63
|
* Multi-seleção com busca.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.types.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/Combobox/combobox.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjE;;2DAE2D;AAC3D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"combobox.types.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/Combobox/combobox.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjE;;2DAE2D;AAC3D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,WAAW,iBACf,SAAQ,IAAI,CACV,wBAAwB,CAAC,QAAQ,CAAC,EAClC,OAAO,GAAG,cAAc,GAAG,UAAU,CACtC;IACD,sEAAsE;IACtE,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,sCAAsC;IACtC,qDAAqD;IACrD,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,wDAAwD;IACxD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,uCAAuC;IACvC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wDAAwD;IACxD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,2DAA2D;AAC3D,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,QAAQ,EAAE,IAAI,CAAC;IACf,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,aAAa,CAAC,EAAE,CAAC,YAAY,EAAE,cAAc,EAAE,KAAK,SAAS,CAAC;CAC/D;AAED,MAAM,MAAM,aAAa,GAAG,mBAAmB,GAAG,qBAAqB,CAAC"}
|
|
@@ -4,6 +4,7 @@ const jsxRuntime = require("react/jsx-runtime");
|
|
|
4
4
|
const React = require("react");
|
|
5
5
|
const lucideReact = require("lucide-react");
|
|
6
6
|
const utils = require("../../../lib/utils.cjs");
|
|
7
|
+
const locale = require("date-fns/locale");
|
|
7
8
|
const calendar = require("../../shadcn/calendar.cjs");
|
|
8
9
|
const popover = require("../../shadcn/popover.cjs");
|
|
9
10
|
function _interopNamespaceDefault(e) {
|
|
@@ -39,6 +40,7 @@ const DatePicker = React__namespace.forwardRef(
|
|
|
39
40
|
placeholder,
|
|
40
41
|
disabled,
|
|
41
42
|
align = "start",
|
|
43
|
+
locale: locale$1 = locale.ptBR,
|
|
42
44
|
minValue,
|
|
43
45
|
maxValue,
|
|
44
46
|
clearable,
|
|
@@ -46,6 +48,7 @@ const DatePicker = React__namespace.forwardRef(
|
|
|
46
48
|
className
|
|
47
49
|
} = props;
|
|
48
50
|
const [open, setOpen] = React__namespace.useState(false);
|
|
51
|
+
const [aguardandoFim, setAguardandoFim] = React__namespace.useState(false);
|
|
49
52
|
const desabilitados = React__namespace.useMemo(() => {
|
|
50
53
|
const m = [];
|
|
51
54
|
if (minValue) m.push({ before: minValue });
|
|
@@ -61,13 +64,22 @@ const DatePicker = React__namespace.forwardRef(
|
|
|
61
64
|
calendar.Calendar,
|
|
62
65
|
{
|
|
63
66
|
mode: "range",
|
|
67
|
+
locale: locale$1,
|
|
64
68
|
disabled: desabilitados,
|
|
65
|
-
min: 1,
|
|
66
69
|
numberOfMonths: props.numberOfMonths ?? 2,
|
|
67
70
|
selected: v,
|
|
68
71
|
onSelect: (range) => {
|
|
69
72
|
props.onValueChange?.(range);
|
|
70
|
-
if (range?.from
|
|
73
|
+
if (!range?.from) {
|
|
74
|
+
setAguardandoFim(false);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!aguardandoFim) {
|
|
78
|
+
setAguardandoFim(true);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
setAguardandoFim(false);
|
|
82
|
+
setOpen(false);
|
|
71
83
|
},
|
|
72
84
|
autoFocus: true
|
|
73
85
|
}
|
|
@@ -79,6 +91,7 @@ const DatePicker = React__namespace.forwardRef(
|
|
|
79
91
|
calendar.Calendar,
|
|
80
92
|
{
|
|
81
93
|
mode: "multiple",
|
|
94
|
+
locale: locale$1,
|
|
82
95
|
disabled: desabilitados,
|
|
83
96
|
numberOfMonths: props.numberOfMonths,
|
|
84
97
|
selected: v,
|
|
@@ -93,6 +106,7 @@ const DatePicker = React__namespace.forwardRef(
|
|
|
93
106
|
calendar.Calendar,
|
|
94
107
|
{
|
|
95
108
|
mode: "single",
|
|
109
|
+
locale: locale$1,
|
|
96
110
|
disabled: desabilitados,
|
|
97
111
|
numberOfMonths: props.numberOfMonths,
|
|
98
112
|
selected: v,
|
|
@@ -103,13 +117,17 @@ const DatePicker = React__namespace.forwardRef(
|
|
|
103
117
|
}
|
|
104
118
|
);
|
|
105
119
|
}
|
|
120
|
+
const aoAbrirFechar = (proximo) => {
|
|
121
|
+
setOpen(proximo);
|
|
122
|
+
if (!proximo) setAguardandoFim(false);
|
|
123
|
+
};
|
|
106
124
|
const limpar = () => {
|
|
107
125
|
if (props.mode === "range") props.onValueChange?.(void 0);
|
|
108
126
|
else if (props.mode === "multiple") props.onValueChange?.(void 0);
|
|
109
127
|
else props.onValueChange?.(void 0);
|
|
110
128
|
};
|
|
111
129
|
const text = label ?? placeholder ?? (props.mode === "range" ? "Selecione o período" : "Selecione a data");
|
|
112
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(popover.Popover, { open, onOpenChange:
|
|
130
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(popover.Popover, { open, onOpenChange: aoAbrirFechar, children: [
|
|
113
131
|
/* @__PURE__ */ jsxRuntime.jsx(popover.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
114
132
|
"button",
|
|
115
133
|
{
|