@seedgrid/fe-components 2026.6.7 → 2026.7.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.
Files changed (73) hide show
  1. package/dist/ai/seedgrid-components.manifest.json +8003 -696
  2. package/dist/commons/SgToaster.d.ts +2 -0
  3. package/dist/commons/SgToaster.d.ts.map +1 -1
  4. package/dist/commons/SgToaster.js +2 -2
  5. package/dist/commons/SgToaster.meta.d.ts.map +1 -1
  6. package/dist/commons/SgToaster.meta.js +2 -1
  7. package/dist/commons/SgWhistleHost.d.ts +16 -15
  8. package/dist/commons/SgWhistleHost.d.ts.map +1 -1
  9. package/dist/commons/SgWhistleHost.js +38 -147
  10. package/dist/commons/SgWhistleHost.meta.d.ts.map +1 -1
  11. package/dist/commons/SgWhistleHost.meta.js +17 -15
  12. package/dist/commons/SgWhistler.d.ts +20 -0
  13. package/dist/commons/SgWhistler.d.ts.map +1 -0
  14. package/dist/commons/SgWhistler.js +168 -0
  15. package/dist/commons/SgWhistler.meta.d.ts +4 -0
  16. package/dist/commons/SgWhistler.meta.d.ts.map +1 -0
  17. package/dist/commons/SgWhistler.meta.js +47 -0
  18. package/dist/commons/sgWhistleHostRegistry.d.ts +7 -0
  19. package/dist/commons/sgWhistleHostRegistry.d.ts.map +1 -0
  20. package/dist/commons/sgWhistleHostRegistry.js +42 -0
  21. package/dist/gadgets/clock/themes/SgClockThemePicker.d.ts.map +1 -1
  22. package/dist/gadgets/clock/themes/SgClockThemePicker.js +1 -1
  23. package/dist/i18n/en-US.d.ts.map +1 -1
  24. package/dist/i18n/en-US.js +2 -0
  25. package/dist/i18n/es.d.ts.map +1 -1
  26. package/dist/i18n/es.js +2 -0
  27. package/dist/i18n/fr.d.ts.map +1 -1
  28. package/dist/i18n/fr.js +2 -0
  29. package/dist/i18n/pt-BR.d.ts.map +1 -1
  30. package/dist/i18n/pt-BR.js +2 -0
  31. package/dist/i18n/pt-PT.d.ts.map +1 -1
  32. package/dist/i18n/pt-PT.js +2 -0
  33. package/dist/index.d.ts +10 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +5 -0
  36. package/dist/inputs/SgCombobox.d.ts.map +1 -1
  37. package/dist/inputs/SgCombobox.js +6 -2
  38. package/dist/inputs/SgInputPassword.d.ts +1 -0
  39. package/dist/inputs/SgInputPassword.d.ts.map +1 -1
  40. package/dist/inputs/SgInputTextSearch.d.ts +11 -0
  41. package/dist/inputs/SgInputTextSearch.d.ts.map +1 -0
  42. package/dist/inputs/SgInputTextSearch.js +47 -0
  43. package/dist/inputs/SgInputTextSearch.meta.d.ts +4 -0
  44. package/dist/inputs/SgInputTextSearch.meta.d.ts.map +1 -0
  45. package/dist/inputs/SgInputTextSearch.meta.js +50 -0
  46. package/dist/inputs/SgMultiSelect.d.ts +23 -0
  47. package/dist/inputs/SgMultiSelect.d.ts.map +1 -0
  48. package/dist/inputs/SgMultiSelect.js +117 -0
  49. package/dist/inputs/SgMultiSelect.meta.d.ts +4 -0
  50. package/dist/inputs/SgMultiSelect.meta.d.ts.map +1 -0
  51. package/dist/inputs/SgMultiSelect.meta.js +56 -0
  52. package/dist/inputs/SgMultiSelectChips.d.ts +27 -0
  53. package/dist/inputs/SgMultiSelectChips.d.ts.map +1 -0
  54. package/dist/inputs/SgMultiSelectChips.js +127 -0
  55. package/dist/inputs/SgMultiSelectChips.meta.d.ts +4 -0
  56. package/dist/inputs/SgMultiSelectChips.meta.d.ts.map +1 -0
  57. package/dist/inputs/SgMultiSelectChips.meta.js +55 -0
  58. package/dist/inputs/SgMultiSelectDropdown.d.ts +28 -0
  59. package/dist/inputs/SgMultiSelectDropdown.d.ts.map +1 -0
  60. package/dist/inputs/SgMultiSelectDropdown.js +41 -0
  61. package/dist/inputs/SgPeriodSelector.d.ts +103 -0
  62. package/dist/inputs/SgPeriodSelector.d.ts.map +1 -0
  63. package/dist/inputs/SgPeriodSelector.js +368 -0
  64. package/dist/inputs/SgPeriodSelector.meta.d.ts +4 -0
  65. package/dist/inputs/SgPeriodSelector.meta.d.ts.map +1 -0
  66. package/dist/inputs/SgPeriodSelector.meta.js +55 -0
  67. package/dist/inputs/useSgMultiSelect.d.ts +48 -0
  68. package/dist/inputs/useSgMultiSelect.d.ts.map +1 -0
  69. package/dist/inputs/useSgMultiSelect.js +202 -0
  70. package/dist/layout/SgMenu.d.ts.map +1 -1
  71. package/dist/layout/SgMenu.js +1 -1
  72. package/dist/sandbox.cjs +53 -53
  73. package/package.json +3 -3
@@ -0,0 +1,56 @@
1
+ export const sgMeta = {
2
+ version: "0.1",
3
+ componentId: "form.input.multiselect",
4
+ package: "@seedgrid/fe-components",
5
+ exportName: "SgMultiSelect",
6
+ slug: "sg-multi-select",
7
+ displayName: "SgMultiSelect",
8
+ category: "input",
9
+ subcategory: "multiselect",
10
+ description: "Campo de selecao multipla no estilo select: trigger com chevron que vira check ao abrir e dropdown com checkboxes que mantem a lista aberta.",
11
+ tags: ["form", "multiselect", "choice", "checkbox", "rhf"],
12
+ capabilities: ["multi-selection", "search", "clearable", "max-selected", "keyboard"],
13
+ fieldSemantics: ["structuredChoice", "multiChoice", "tags", "categories"],
14
+ props: [
15
+ { name: "id", type: "string", required: true, description: "Identificador unico do campo.", semanticRole: "data", bindable: false },
16
+ { name: "label", type: "string", description: "Rotulo exibido ao usuario.", semanticRole: "label", bindable: true },
17
+ { name: "options", type: "{ label: string; value: string | number; disabled?: boolean }[]", description: "Lista de opcoes disponiveis.", semanticRole: "data", bindable: false },
18
+ { name: "value", type: "(string | number)[]", description: "Array de valores selecionados.", semanticRole: "value", bindable: true },
19
+ { name: "onChange", type: "(value: (string | number)[]) => void", description: "Callback com o array atualizado de valores.", semanticRole: "event", bindable: false },
20
+ { name: "searchable", type: "boolean", default: false, description: "Exibe campo de busca no topo do dropdown.", semanticRole: "behavior", bindable: true },
21
+ { name: "clearable", type: "boolean", default: false, description: "Exibe botao para limpar toda a selecao.", semanticRole: "behavior", bindable: true },
22
+ { name: "maxSelected", type: "number", description: "Limite maximo de itens selecionaveis.", semanticRole: "validation", bindable: true },
23
+ { name: "placeholder", type: "string", description: "Texto exibido quando nada esta selecionado.", semanticRole: "label", bindable: true },
24
+ { name: "summaryThreshold", type: "number", default: 2, description: "Quantidade de rotulos antes de mostrar 'N selecionadas'.", semanticRole: "appearance", bindable: true },
25
+ { name: "required", type: "boolean", default: false, description: "Marca o campo como obrigatorio.", semanticRole: "validation", bindable: true },
26
+ { name: "onEnter", type: "() => void", description: "Disparado ao focar o campo.", semanticRole: "event", bindable: false },
27
+ { name: "onExit", type: "() => void", description: "Disparado ao sair do campo.", semanticRole: "event", bindable: false }
28
+ ],
29
+ states: ["default", "focused", "open", "disabled", "error"],
30
+ showcase: { route: "/components/sg-multi-select", hasPlayground: false, hasPropsTable: true },
31
+ sdui: {
32
+ rendererType: "field.multiselect",
33
+ acceptsDataBinding: true,
34
+ defaultProps: { searchable: false, clearable: false, summaryThreshold: 2 }
35
+ }
36
+ };
37
+ export const aiHints = {
38
+ version: "0.1",
39
+ preferredUseCases: [
40
+ "Selecao de varias opcoes a partir de uma lista fechada (ex.: empresas, categorias, tags).",
41
+ "Filtros multi-valor em formularios e relatorios.",
42
+ "Campos relacionais N:N em formularios CRUD."
43
+ ],
44
+ avoidUseCases: [
45
+ "Escolha unica; nesses casos prefira SgCombobox ou SgRadioGroup.",
46
+ "Listas muito grandes com busca remota assincrona; nesses casos prefira SgAutocomplete.",
47
+ "Poucas opcoes sempre visiveis; nesses casos prefira SgCheckboxGroup."
48
+ ],
49
+ synonyms: ["multi select", "multiselect", "selecao multipla", "multiple choice select", "checkbox dropdown"],
50
+ relatedEntityFields: ["empresas", "categorias", "tags", "permissoes", "roles"],
51
+ compositionHints: [
52
+ "Usar dentro de SgPanel ou SgGroupBox em formularios.",
53
+ "Combinar com SgCheckboxGroup quando todas as opcoes precisam ficar sempre visiveis."
54
+ ],
55
+ rankingSignals: { freeText: 0.1, structuredChoice: 0.95, date: 0, number: 0, denseLayout: 0.7 }
56
+ };
@@ -0,0 +1,27 @@
1
+ import { type RhfFieldProps } from "../rhf";
2
+ import type { SgMultiSelectOption, SgMultiSelectOptionValue } from "./useSgMultiSelect";
3
+ export type { SgMultiSelectOption, SgMultiSelectOptionValue } from "./useSgMultiSelect";
4
+ export type SgMultiSelectChipsProps = RhfFieldProps & {
5
+ id: string;
6
+ label?: string;
7
+ labelText?: string;
8
+ className?: string;
9
+ labelClassName?: string;
10
+ width?: number | string;
11
+ borderRadius?: number | string;
12
+ enabled?: boolean;
13
+ required?: boolean;
14
+ options: SgMultiSelectOption[];
15
+ value?: SgMultiSelectOptionValue[];
16
+ onChange?: (value: SgMultiSelectOptionValue[]) => void;
17
+ searchable?: boolean;
18
+ searchPlaceholder?: string;
19
+ clearable?: boolean;
20
+ maxSelected?: number;
21
+ emptyText?: string;
22
+ placeholder?: string;
23
+ onEnter?: () => void;
24
+ onExit?: () => void;
25
+ };
26
+ export declare function SgMultiSelectChips(props: Readonly<SgMultiSelectChipsProps>): import("react/jsx-runtime").JSX.Element;
27
+ //# sourceMappingURL=SgMultiSelectChips.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgMultiSelectChips.d.ts","sourceRoot":"","sources":["../../src/inputs/SgMultiSelectChips.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAI/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAExF,YAAY,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAExF,MAAM,MAAM,uBAAuB,GAAG,aAAa,GAAG;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,KAAK,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,EAAE,KAAK,IAAI,CAAC;IACvD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAwPF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,QAAQ,CAAC,uBAAuB,CAAC,2CAsB1E"}
@@ -0,0 +1,127 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { Check, ChevronDown, X } from "lucide-react";
5
+ import { Controller } from "react-hook-form";
6
+ import { resolveFieldError } from "../rhf";
7
+ import { t, useComponentsI18n } from "../i18n";
8
+ import { SgMultiSelectDropdown } from "./SgMultiSelectDropdown";
9
+ import { useSgMultiSelect } from "./useSgMultiSelect";
10
+ function SgMultiSelectChipsBase(props) {
11
+ const { id, label, labelText, className, labelClassName, width, borderRadius, enabled, required, options, value, onChange, searchable = false, searchPlaceholder, clearable = false, maxSelected, emptyText: emptyTextProp, placeholder, error, onEnter, onExit } = props;
12
+ const i18n = useComponentsI18n();
13
+ const emptyText = emptyTextProp ?? t(i18n, "components.autocomplete.empty");
14
+ const resolvedPlaceholder = placeholder ?? t(i18n, "components.inputs.select.placeholder");
15
+ const isDisabled = enabled === false;
16
+ const resolvedLabel = labelText ?? label ?? "";
17
+ const ms = useSgMultiSelect({
18
+ options,
19
+ value,
20
+ onChange,
21
+ searchable,
22
+ maxSelected,
23
+ isDisabled,
24
+ borderRadius
25
+ });
26
+ const triggerRef = React.useRef(null);
27
+ const hasError = Boolean(error);
28
+ const resolvedBorderRadius = borderRadius === undefined
29
+ ? undefined
30
+ : typeof borderRadius === "number"
31
+ ? `${borderRadius}px`
32
+ : borderRadius;
33
+ const borderClass = hasError
34
+ ? "border border-[hsl(var(--destructive))]"
35
+ : ms.open
36
+ ? "border border-[hsl(var(--primary))] ring-2 ring-[hsl(var(--primary)/0.25)]"
37
+ : "border border-border";
38
+ const triggerClass = [
39
+ "flex min-h-11 w-full flex-wrap items-center gap-1.5 rounded-md px-2 py-1.5 text-sm",
40
+ "bg-[var(--sg-input-bg,hsl(var(--background)))] text-[var(--sg-input-fg,hsl(var(--foreground)))]",
41
+ borderClass,
42
+ isDisabled
43
+ ? "cursor-not-allowed bg-[var(--sg-input-disabled-bg,hsl(var(--muted)))] text-[var(--sg-input-disabled-fg,hsl(var(--muted-foreground)))]"
44
+ : "cursor-text",
45
+ className ?? ""
46
+ ].join(" ");
47
+ const openOrClose = () => {
48
+ if (isDisabled)
49
+ return;
50
+ if (ms.open) {
51
+ ms.closeDropdown();
52
+ }
53
+ else {
54
+ ms.openDropdown();
55
+ }
56
+ };
57
+ return (_jsxs("div", { style: { width: width ?? "100%" }, children: [resolvedLabel ? (_jsxs("label", { htmlFor: id, className: [
58
+ "mb-1 block text-sm font-medium",
59
+ hasError ? "text-[hsl(var(--destructive))]" : "text-foreground/70",
60
+ labelClassName ?? ""
61
+ ].join(" "), children: [_jsx("span", { children: resolvedLabel }), required ? (_jsx("span", { className: "ml-1 text-[hsl(var(--destructive))]", "aria-hidden": "true", children: "*" })) : null] })) : null, _jsxs("div", { className: ms.open ? "relative z-[1100]" : "relative", ref: ms.wrapperRef, children: [_jsxs("div", { ref: triggerRef, id: id, role: "combobox", "aria-expanded": ms.open, "aria-haspopup": "listbox", tabIndex: isDisabled ? -1 : 0, className: triggerClass, style: { borderRadius: resolvedBorderRadius }, onMouseDown: (event) => {
62
+ // Clicks on chip remove buttons handle their own logic.
63
+ if (event.target.closest("[data-sg-chip-remove]"))
64
+ return;
65
+ event.preventDefault();
66
+ ms.ignoreBlurRef.current = true;
67
+ openOrClose();
68
+ triggerRef.current?.focus();
69
+ }, onFocus: () => {
70
+ onEnter?.();
71
+ }, onBlur: (event) => {
72
+ if (event.relatedTarget && ms.wrapperRef.current?.contains(event.relatedTarget)) {
73
+ return;
74
+ }
75
+ if (event.relatedTarget && ms.dropdownRef.current?.contains(event.relatedTarget)) {
76
+ return;
77
+ }
78
+ if (ms.ignoreBlurRef.current) {
79
+ setTimeout(() => {
80
+ ms.ignoreBlurRef.current = false;
81
+ }, 0);
82
+ return;
83
+ }
84
+ onExit?.();
85
+ }, onKeyDown: (event) => {
86
+ if (isDisabled)
87
+ return;
88
+ if (event.key === "Escape") {
89
+ event.preventDefault();
90
+ ms.closeDropdown();
91
+ return;
92
+ }
93
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
94
+ event.preventDefault();
95
+ if (!ms.open) {
96
+ ms.openDropdown();
97
+ return;
98
+ }
99
+ if (!searchable) {
100
+ ms.handleListKeyDown(event);
101
+ }
102
+ }
103
+ }, children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-1.5", children: ms.selectedOptions.length === 0 ? (_jsx("span", { className: "px-1 text-[var(--sg-input-placeholder,hsl(var(--muted-foreground)))]", children: resolvedPlaceholder })) : (ms.selectedOptions.map((option) => (_jsxs("span", { className: "inline-flex max-w-full items-center gap-1 rounded-full border border-border bg-muted/60 py-0.5 pl-2 pr-1 text-xs text-foreground", children: [_jsx("span", { className: "truncate", children: option.label }), !isDisabled ? (_jsx("button", { type: "button", "data-sg-chip-remove": "true", tabIndex: -1, className: "flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-foreground/60 hover:bg-foreground/10 hover:text-foreground", onMouseDown: (event) => {
104
+ event.preventDefault();
105
+ event.stopPropagation();
106
+ ms.ignoreBlurRef.current = true;
107
+ }, onClick: (event) => {
108
+ event.preventDefault();
109
+ event.stopPropagation();
110
+ ms.removeValue(option.value);
111
+ }, "aria-label": t(i18n, "components.inputs.multiSelect.removeChip", { label: option.label }), children: _jsx(X, { size: 12 }) })) : null] }, String(option.value))))) }), _jsxs("span", { className: "ml-auto flex shrink-0 items-center gap-1 self-start pt-0.5", children: [clearable && ms.selectedOptions.length > 0 && !isDisabled ? (_jsx("button", { type: "button", "data-sg-chip-remove": "true", tabIndex: -1, className: "text-foreground/60 hover:text-foreground", onMouseDown: (event) => {
112
+ event.preventDefault();
113
+ event.stopPropagation();
114
+ ms.ignoreBlurRef.current = true;
115
+ }, onClick: (event) => {
116
+ event.preventDefault();
117
+ event.stopPropagation();
118
+ ms.clearSelection();
119
+ }, "aria-label": t(i18n, "components.actions.clearSelection"), children: _jsx(X, { size: 16 }) })) : null, _jsx("span", { className: "text-foreground/60", "aria-hidden": "true", children: ms.open ? _jsx(Check, { size: 16 }) : _jsx(ChevronDown, { size: 16 }) })] })] }), error ? _jsx("p", { "data-sg-error": true, className: "mt-1 text-xs text-red-600", children: error }) : null, ms.open && !isDisabled ? (_jsx(SgMultiSelectDropdown, { dropdownRef: ms.dropdownRef, searchRef: ms.searchRef, ignoreBlurRef: ms.ignoreBlurRef, dropdownStyle: ms.dropdownStyle, searchable: searchable, searchPlaceholder: searchPlaceholder, query: ms.query, setQuery: ms.setQuery, setActiveIndex: ms.setActiveIndex, activeIndex: ms.activeIndex, visibleOptions: ms.visibleOptions, emptyText: emptyText, isSelected: ms.isSelected, resolvedValueLength: ms.resolvedValue.length, maxSelected: maxSelected, toggleOption: ms.toggleOption, onKeyDown: ms.handleListKeyDown })) : null] })] }));
120
+ }
121
+ export function SgMultiSelectChips(props) {
122
+ const { control, name, rules, ...rest } = props;
123
+ if (control && name) {
124
+ return (_jsx(Controller, { name: name, control: control, rules: rules, render: ({ field, fieldState }) => (_jsx(SgMultiSelectChipsBase, { ...rest, error: resolveFieldError(rest.error, fieldState.error?.message), value: field.value ?? [], onChange: (next) => field.onChange(next) })) }));
125
+ }
126
+ return _jsx(SgMultiSelectChipsBase, { ...rest });
127
+ }
@@ -0,0 +1,4 @@
1
+ import type { SgAiHintsV0, SgMetaV0 } from "../ai-meta/types";
2
+ export declare const sgMeta: SgMetaV0;
3
+ export declare const aiHints: SgAiHintsV0;
4
+ //# sourceMappingURL=SgMultiSelectChips.meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgMultiSelectChips.meta.d.ts","sourceRoot":"","sources":["../../src/inputs/SgMultiSelectChips.meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,MAAM,EAAE,QAmCpB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAmBrB,CAAC"}
@@ -0,0 +1,55 @@
1
+ export const sgMeta = {
2
+ version: "0.1",
3
+ componentId: "form.input.multiselectchips",
4
+ package: "@seedgrid/fe-components",
5
+ exportName: "SgMultiSelectChips",
6
+ slug: "sg-multi-select-chips",
7
+ displayName: "SgMultiSelectChips",
8
+ category: "input",
9
+ subcategory: "multiselect",
10
+ description: "Variante do multi-select onde os itens selecionados viram chips removiveis no trigger; o dropdown com checkboxes e a busca sao iguais ao SgMultiSelect.",
11
+ tags: ["form", "multiselect", "chips", "tags", "checkbox", "rhf"],
12
+ capabilities: ["multi-selection", "chips", "search", "clearable", "max-selected", "keyboard"],
13
+ fieldSemantics: ["structuredChoice", "multiChoice", "tags", "categories"],
14
+ props: [
15
+ { name: "id", type: "string", required: true, description: "Identificador unico do campo.", semanticRole: "data", bindable: false },
16
+ { name: "label", type: "string", description: "Rotulo exibido ao usuario.", semanticRole: "label", bindable: true },
17
+ { name: "options", type: "{ label: string; value: string | number; disabled?: boolean }[]", description: "Lista de opcoes disponiveis.", semanticRole: "data", bindable: false },
18
+ { name: "value", type: "(string | number)[]", description: "Array de valores selecionados.", semanticRole: "value", bindable: true },
19
+ { name: "onChange", type: "(value: (string | number)[]) => void", description: "Callback com o array atualizado de valores.", semanticRole: "event", bindable: false },
20
+ { name: "searchable", type: "boolean", default: false, description: "Exibe campo de busca no topo do dropdown.", semanticRole: "behavior", bindable: true },
21
+ { name: "clearable", type: "boolean", default: false, description: "Exibe botao para limpar toda a selecao.", semanticRole: "behavior", bindable: true },
22
+ { name: "maxSelected", type: "number", description: "Limite maximo de itens selecionaveis.", semanticRole: "validation", bindable: true },
23
+ { name: "placeholder", type: "string", description: "Texto exibido quando nada esta selecionado.", semanticRole: "label", bindable: true },
24
+ { name: "required", type: "boolean", default: false, description: "Marca o campo como obrigatorio.", semanticRole: "validation", bindable: true },
25
+ { name: "onEnter", type: "() => void", description: "Disparado ao focar o campo.", semanticRole: "event", bindable: false },
26
+ { name: "onExit", type: "() => void", description: "Disparado ao sair do campo.", semanticRole: "event", bindable: false }
27
+ ],
28
+ states: ["default", "focused", "open", "disabled", "error"],
29
+ showcase: { route: "/components/sg-multi-select-chips", hasPlayground: false, hasPropsTable: true },
30
+ sdui: {
31
+ rendererType: "field.multiselectchips",
32
+ acceptsDataBinding: true,
33
+ defaultProps: { searchable: false, clearable: false }
34
+ }
35
+ };
36
+ export const aiHints = {
37
+ version: "0.1",
38
+ preferredUseCases: [
39
+ "Selecao de varias opcoes onde e util ver e remover cada item escolhido individualmente (ex.: tags, destinatarios, empresas).",
40
+ "Filtros multi-valor em que o usuario precisa enxergar tudo o que ja selecionou.",
41
+ "Campos relacionais N:N em formularios CRUD com poucos itens visiveis por vez."
42
+ ],
43
+ avoidUseCases: [
44
+ "Escolha unica; nesses casos prefira SgCombobox ou SgRadioGroup.",
45
+ "Selecoes muito numerosas onde os chips ocupariam espaco demais; nesses casos prefira SgMultiSelect (resumo 'N selecionadas').",
46
+ "Listas com busca remota assincrona; nesses casos prefira SgAutocomplete."
47
+ ],
48
+ synonyms: ["multi select chips", "multiselect tags", "selecao multipla com chips", "tag input select", "chips dropdown"],
49
+ relatedEntityFields: ["empresas", "categorias", "tags", "permissoes", "roles", "destinatarios"],
50
+ compositionHints: [
51
+ "Usar dentro de SgPanel ou SgGroupBox em formularios.",
52
+ "Preferir a SgMultiSelect quando o numero de selecoes for grande e os chips poluirem o layout."
53
+ ],
54
+ rankingSignals: { freeText: 0.1, structuredChoice: 0.95, date: 0, number: 0, denseLayout: 0.6 }
55
+ };
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ import type { SgMultiSelectOption, SgMultiSelectOptionValue } from "./useSgMultiSelect";
3
+ export type SgMultiSelectDropdownProps = {
4
+ dropdownRef: React.RefObject<HTMLDivElement | null>;
5
+ searchRef: React.RefObject<HTMLInputElement | null>;
6
+ ignoreBlurRef: React.MutableRefObject<boolean>;
7
+ dropdownStyle: React.CSSProperties;
8
+ searchable: boolean;
9
+ searchPlaceholder?: string;
10
+ query: string;
11
+ setQuery: (value: string) => void;
12
+ setActiveIndex: (index: number) => void;
13
+ activeIndex: number;
14
+ visibleOptions: SgMultiSelectOption[];
15
+ emptyText: string;
16
+ isSelected: (value: SgMultiSelectOptionValue) => boolean;
17
+ resolvedValueLength: number;
18
+ maxSelected?: number;
19
+ toggleOption: (option: SgMultiSelectOption) => void;
20
+ onKeyDown: (event: React.KeyboardEvent) => void;
21
+ };
22
+ /**
23
+ * Dropdown compartilhado por SgMultiSelect e SgMultiSelectChips: lista de
24
+ * opcoes com checkbox, busca opcional, scroll e marcacao. Mantem a lista
25
+ * aberta enquanto o usuario marca/desmarca.
26
+ */
27
+ export declare function SgMultiSelectDropdown(props: Readonly<SgMultiSelectDropdownProps>): React.ReactPortal | null;
28
+ //# sourceMappingURL=SgMultiSelectDropdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgMultiSelectDropdown.d.ts","sourceRoot":"","sources":["../../src/inputs/SgMultiSelectDropdown.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAExF,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACpD,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACpD,aAAa,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC/C,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC;IACnC,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,mBAAmB,EAAE,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,OAAO,CAAC;IACzD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACpD,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC;CACjD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,CAAC,0BAA0B,CAAC,4BAiFhF"}
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { createPortal } from "react-dom";
4
+ import { Check } from "lucide-react";
5
+ import { t, useComponentsI18n } from "../i18n";
6
+ /**
7
+ * Dropdown compartilhado por SgMultiSelect e SgMultiSelectChips: lista de
8
+ * opcoes com checkbox, busca opcional, scroll e marcacao. Mantem a lista
9
+ * aberta enquanto o usuario marca/desmarca.
10
+ */
11
+ export function SgMultiSelectDropdown(props) {
12
+ const i18n = useComponentsI18n();
13
+ if (typeof document === "undefined")
14
+ return null;
15
+ return createPortal(_jsxs("div", { ref: props.dropdownRef, className: "z-[1100] overflow-hidden rounded-md border border-border bg-[rgb(var(--sg-surface,var(--sg-bg)))] text-[rgb(var(--sg-text,var(--sg-fg)))] shadow-lg", style: props.dropdownStyle, role: "listbox", "aria-multiselectable": "true", onKeyDown: props.onKeyDown, children: [props.searchable ? (_jsx("div", { className: "border-b border-border p-2", children: _jsx("input", { ref: props.searchRef, type: "text", value: props.query, placeholder: props.searchPlaceholder ?? t(i18n, "components.actions.search"), className: "h-8 w-full rounded border border-border bg-[var(--sg-input-bg,hsl(var(--background)))] px-2 text-sm text-[var(--sg-input-fg,hsl(var(--foreground)))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary)/0.25)]", onChange: (event) => {
16
+ props.setQuery(event.currentTarget.value);
17
+ props.setActiveIndex(0);
18
+ }, onMouseDown: () => {
19
+ props.ignoreBlurRef.current = true;
20
+ } }) })) : null, _jsx("div", { className: "max-h-64 overflow-auto py-1", children: props.visibleOptions.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: props.emptyText })) : (props.visibleOptions.map((option, index) => {
21
+ const checked = props.isSelected(option.value);
22
+ const isActive = props.activeIndex === index;
23
+ const reachedMax = !checked &&
24
+ props.maxSelected !== undefined &&
25
+ props.resolvedValueLength >= props.maxSelected;
26
+ const optionDisabled = option.disabled || reachedMax;
27
+ return (_jsxs("div", { role: "option", "aria-selected": checked, className: [
28
+ "flex cursor-pointer items-center gap-2 px-3 py-2 text-sm",
29
+ isActive ? "bg-muted/60" : "",
30
+ optionDisabled ? "cursor-not-allowed opacity-50" : "hover:bg-muted/40"
31
+ ].join(" "), onMouseEnter: () => props.setActiveIndex(index), onMouseDown: (event) => {
32
+ event.preventDefault();
33
+ props.ignoreBlurRef.current = true;
34
+ }, onClick: () => props.toggleOption(option), children: [_jsx("span", { "aria-hidden": "true", className: [
35
+ "flex h-4 w-4 shrink-0 items-center justify-center rounded border",
36
+ checked
37
+ ? "border-[hsl(var(--primary))] bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]"
38
+ : "border-border bg-[var(--sg-input-bg,hsl(var(--background)))]"
39
+ ].join(" "), children: checked ? _jsx(Check, { size: 12, strokeWidth: 3 }) : null }), _jsx("span", { className: "truncate", children: option.label })] }, String(option.value)));
40
+ })) })] }), document.body);
41
+ }
@@ -0,0 +1,103 @@
1
+ import type { Control, FieldValues, RegisterOptions } from "react-hook-form";
2
+ import type { SgAutocompleteItem } from "./SgAutocomplete";
3
+ /**
4
+ * Presets temporais pre-definidos para filtros de relatorios/dashboards.
5
+ * O valor de cada membro e igual ao seu nome (string) para trafegar bem em
6
+ * fontes de dados, JSON e no id de item do SgCombobox.
7
+ */
8
+ export declare enum PeriodPreset {
9
+ ALL = "ALL",
10
+ CUSTOM = "CUSTOM",
11
+ TODAY = "TODAY",
12
+ YESTERDAY = "YESTERDAY",
13
+ TOMORROW = "TOMORROW",
14
+ THIS_WEEK = "THIS_WEEK",
15
+ THIS_QUINZENA = "THIS_QUINZENA",
16
+ THIS_MONTH = "THIS_MONTH",
17
+ THIS_QUARTER = "THIS_QUARTER",
18
+ THIS_SEMESTER = "THIS_SEMESTER",
19
+ THIS_YEAR = "THIS_YEAR",
20
+ LAST_WEEK = "LAST_WEEK",
21
+ LAST_QUINZENA = "LAST_QUINZENA",
22
+ LAST_MONTH = "LAST_MONTH",
23
+ LAST_QUARTER = "LAST_QUARTER",
24
+ LAST_SEMESTER = "LAST_SEMESTER",
25
+ LAST_YEAR = "LAST_YEAR",
26
+ NEXT_WEEK = "NEXT_WEEK",
27
+ NEXT_QUINZENA = "NEXT_QUINZENA",
28
+ NEXT_MONTH = "NEXT_MONTH",
29
+ NEXT_QUARTER = "NEXT_QUARTER",
30
+ NEXT_SEMESTER = "NEXT_SEMESTER",
31
+ NEXT_YEAR = "NEXT_YEAR"
32
+ }
33
+ /**
34
+ * Intervalo resolvido de um preset.
35
+ * - startDate: meia-noite local do primeiro dia (00:00:00.000).
36
+ * - endDate: ultimo instante local do ultimo dia (23:59:59.999).
37
+ * - ALL e CUSTOM (sem preenchimento da UI) retornam datas nulas.
38
+ */
39
+ export type ResolvedPeriod = {
40
+ preset: PeriodPreset;
41
+ label: string;
42
+ startDate: Date | null;
43
+ endDate: Date | null;
44
+ };
45
+ export type ResolvePeriodOptions = {
46
+ /** Nao permite que startDate seja anterior a esta data (clamp). */
47
+ minDate?: Date;
48
+ /** Nao permite que endDate seja posterior a esta data (clamp). */
49
+ maxDate?: Date;
50
+ /** Locale usado para o label textual. Default "pt-BR". */
51
+ locale?: string;
52
+ };
53
+ export type SgPeriodSelectorProps = {
54
+ id?: string;
55
+ label?: string;
56
+ value?: PeriodPreset;
57
+ onChange?: (period: ResolvedPeriod) => void;
58
+ /** Habilita o grupo de periodos passados (LAST_*). Default true. */
59
+ permitPast?: boolean;
60
+ /** Habilita o grupo de periodos atuais (THIS_*, TODAY, YESTERDAY). Default true. */
61
+ permitCurrent?: boolean;
62
+ /** Habilita o grupo de periodos futuros (TOMORROW, NEXT_*). Default true. */
63
+ permitFuture?: boolean;
64
+ /** Habilita a opcao "Todos" (sem limites de data). Default true. */
65
+ permitAll?: boolean;
66
+ /** Habilita a opcao "Periodo" (intervalo customizado). Default true. */
67
+ allowCustomPeriod?: boolean;
68
+ /** Locale para labels e formatacao. Default "pt-BR". */
69
+ locale?: string;
70
+ disabled?: boolean;
71
+ required?: boolean;
72
+ minDate?: Date;
73
+ maxDate?: Date;
74
+ /** Data de referencia para resolver os presets (default: agora). Util em testes. */
75
+ referenceDate?: Date;
76
+ className?: string;
77
+ error?: string;
78
+ /** Integracao react-hook-form (opcional). */
79
+ control?: Control<FieldValues>;
80
+ name?: string;
81
+ rules?: RegisterOptions;
82
+ };
83
+ export declare function periodPresetLabel(preset: PeriodPreset, locale?: string): string;
84
+ /**
85
+ * Resolve um preset temporal para um intervalo real de datas.
86
+ *
87
+ * Convencoes:
88
+ * - Semana inicia na SEGUNDA-FEIRA.
89
+ * - Quinzena e de calendario (dia 1..15 e 16..fim do mes), nao 15 dias corridos.
90
+ * - startDate = 00:00:00.000 local; endDate = 23:59:59.999 local (inclusivo).
91
+ * - ALL/CUSTOM retornam datas nulas.
92
+ * - minDate/maxDate (options) fazem clamp do intervalo resolvido.
93
+ */
94
+ export declare function resolvePeriod(preset: PeriodPreset, ref?: Date, options?: ResolvePeriodOptions): ResolvedPeriod;
95
+ export declare function buildPeriodOptions(config: {
96
+ permitAll?: boolean;
97
+ allowCustomPeriod?: boolean;
98
+ permitCurrent?: boolean;
99
+ permitFuture?: boolean;
100
+ permitPast?: boolean;
101
+ }, locale?: string): SgAutocompleteItem[];
102
+ export declare function SgPeriodSelector(props: Readonly<SgPeriodSelectorProps>): import("react/jsx-runtime").JSX.Element;
103
+ //# sourceMappingURL=SgPeriodSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgPeriodSelector.d.ts","sourceRoot":"","sources":["../../src/inputs/SgPeriodSelector.tsx"],"names":[],"mappings":"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"}