@seedgrid/fe-components 2026.8.8 → 2026.9.1

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.
@@ -16728,6 +16728,12 @@
16728
16728
  "type": "boolean",
16729
16729
  "required": false
16730
16730
  },
16731
+ {
16732
+ "name": "checkableLabel",
16733
+ "type": "string",
16734
+ "required": false,
16735
+ "description": "Cabecalho da coluna do checkbox principal -- so' e' desenhado quando `tracks` tambem esta'\npresente (e' o cabecalho de colunas como um todo que fica ambiguo com 2+ controles por linha,\nnao o checkbox sozinho)."
16736
+ },
16731
16737
  {
16732
16738
  "name": "checkMode",
16733
16739
  "type": "\"live\" | \"confirm\"",
@@ -16832,6 +16838,12 @@
16832
16838
  "name": "tone",
16833
16839
  "type": "SgTone",
16834
16840
  "required": false
16841
+ },
16842
+ {
16843
+ "name": "tracks",
16844
+ "type": "SgTreeTrack[]",
16845
+ "required": false,
16846
+ "description": "Trilhas de selecao extras, uma por coluna de controle na linha -- ver SgTreeTrack."
16835
16847
  }
16836
16848
  ],
16837
16849
  "states": [
@@ -25,6 +25,56 @@ export declare function sgTreeFromJsonWithChecked(json: SgTreeNodeJson[], iconRe
25
25
  nodes: SgTreeNode[];
26
26
  checkedIds: string[];
27
27
  };
28
+ /** O que toda trilha tem, seja ela de selecao ou de conteudo livre. */
29
+ type SgTreeTrackBase = {
30
+ id: string;
31
+ /** Cabecalho da coluna -- some sem ele; com 2+ trilhas na mesma arvore, o usuario nao tem
32
+ * como saber o que cada coluna significa so' pela posicao. */
33
+ label?: string;
34
+ /** Largura da coluna, valor CSS (ex.: "140px", "12rem"). Default: 110px. Colunas com conteudo
35
+ * mais largo que um checkbox -- um select com rotulos longos, um texto com acao ao lado --
36
+ * precisam disso, senao o cabecalho trunca e o controle espreme. */
37
+ width?: string;
38
+ };
39
+ export type SgTreeTrackBoolean = SgTreeTrackBase & {
40
+ kind: "boolean";
41
+ checkedIds: string[];
42
+ onCheckedChange: (ids: string[]) => void;
43
+ disabled?: (node: SgTreeNode) => boolean;
44
+ ariaLabel?: string;
45
+ /** Conteudo livre ao lado do controle desta coluna, por no'. O componente nao sabe (nem
46
+ * precisa saber) o que ele significa -- icone, badge, contador, o que a tela quiser. */
47
+ adornment?: (node: SgTreeNode) => React.ReactNode;
48
+ };
49
+ export type SgTreeTrackEnum = SgTreeTrackBase & {
50
+ kind: "enum";
51
+ options: {
52
+ value: string;
53
+ label: string;
54
+ }[];
55
+ valueByNodeId: Record<string, string | undefined>;
56
+ onChange: (next: Record<string, string | undefined>) => void;
57
+ disabled?: (node: SgTreeNode) => boolean;
58
+ ariaLabel?: string;
59
+ placeholder?: string;
60
+ mixedLabel?: string;
61
+ adornment?: (node: SgTreeNode) => React.ReactNode;
62
+ };
63
+ export type SgTreeTrackCustom = SgTreeTrackBase & {
64
+ kind: "custom";
65
+ /** A arvore so' cede a celula (largura, alinhamento, cabecalho) -- nao ha' estado nem cascata
66
+ * aqui: quem renderiza controla tudo. */
67
+ render: (node: SgTreeNode) => React.ReactNode;
68
+ };
69
+ /**
70
+ * Uma coluna extra por linha, independente do `checkable`/`checkedIds` principal.
71
+ *
72
+ * As trilhas de selecao (`boolean` e `enum`) tem cascata pai->filhos propria e sao controladas de
73
+ * fora (sem defaultValue) -- o estado mora em quem chama, igual ja acontece com `checkedIds`.
74
+ * A trilha `custom` nao tem estado nenhum: e' so' uma celula alinhada com as outras, para a tela
75
+ * desenhar o que precisar.
76
+ */
77
+ export type SgTreeTrack = SgTreeTrackBoolean | SgTreeTrackEnum | SgTreeTrackCustom;
28
78
  export type SgTreeViewRef = {
29
79
  getCheckedIds: () => string[];
30
80
  getCheckedLeafIds: () => string[];
@@ -60,6 +110,12 @@ export type SgTreeViewProps = {
60
110
  checkedIds?: string[];
61
111
  defaultCheckedIds?: string[];
62
112
  onCheckedChange?: (ids: string[]) => void;
113
+ /** Cabecalho da coluna do checkbox principal -- so' e' desenhado quando `tracks` tambem esta'
114
+ * presente (e' o cabecalho de colunas como um todo que fica ambiguo com 2+ controles por linha,
115
+ * nao o checkbox sozinho). */
116
+ checkableLabel?: string;
117
+ /** Trilhas de selecao extras, uma por coluna de controle na linha -- ver SgTreeTrack. */
118
+ tracks?: SgTreeTrack[];
63
119
  expandedIds?: string[];
64
120
  defaultExpandedIds?: string[];
65
121
  onExpandedChange?: (ids: string[]) => void;
@@ -78,4 +134,5 @@ export type SgTreeViewProps = {
78
134
  maxHeightClassName?: string;
79
135
  };
80
136
  export declare const SgTreeView: React.ForwardRefExoticComponent<SgTreeViewProps & React.RefAttributes<SgTreeViewRef>>;
137
+ export {};
81
138
  //# sourceMappingURL=SgTreeView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SgTreeView.d.ts","sourceRoot":"","sources":["../../src/layout/SgTreeView.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SgTreeView.d.ts","sourceRoot":"","sources":["../../src/layout/SgTreeView.tsx"],"names":[],"mappings":"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"}
@@ -141,6 +141,31 @@ function setBranchChecked(id, value, childrenById, checked) {
141
141
  checked.delete(x);
142
142
  }
143
143
  }
144
+ function computeCascadeValue(id, childrenById, valueById) {
145
+ const kids = childrenById.get(id) ?? [];
146
+ if (!kids.length)
147
+ return { mixed: false, value: valueById.get(id) };
148
+ let acc = null;
149
+ for (const k of kids) {
150
+ const r = computeCascadeValue(k, childrenById, valueById);
151
+ if (r.mixed)
152
+ return { mixed: true };
153
+ if (acc === null)
154
+ acc = r;
155
+ else if (acc.mixed || acc.value !== r.value)
156
+ return { mixed: true };
157
+ }
158
+ return acc ?? { mixed: false, value: valueById.get(id) };
159
+ }
160
+ function setBranchValue(id, value, childrenById, valueById) {
161
+ const all = [id, ...collectDescendants(childrenById, id)];
162
+ for (const x of all) {
163
+ if (value === undefined)
164
+ valueById.delete(x);
165
+ else
166
+ valueById.set(x, value);
167
+ }
168
+ }
144
169
  function useControllable(opts, fallback) {
145
170
  const isControlled = "value" in opts && opts.value !== undefined;
146
171
  const [inner, setInner] = React.useState("defaultValue" in opts && opts.defaultValue !== undefined ? opts.defaultValue : fallback);
@@ -176,6 +201,14 @@ const densityMap = {
176
201
  normal: { rowPy: "py-1", rowPx: "px-2", gap: "gap-2", caret: "h-6 w-6", indent: 16 },
177
202
  comfortable: { rowPy: "py-1.5", rowPx: "px-2.5", gap: "gap-2.5", caret: "h-7 w-7", indent: 18 }
178
203
  };
204
+ // Cada coluna de trilha tem largura propria, aplicada igual na linha e no cabecalho -- e' o que
205
+ // deixa o texto do cabecalho alinhado com o controle por baixo dele, em vez de so' o controle
206
+ // estreito sem legenda nenhuma.
207
+ const TRACK_CELL_CLASS = "shrink-0";
208
+ const TRACK_CELL_WIDTH_DEFAULT = "110px";
209
+ function trackCellWidth(track) {
210
+ return { width: track.width ?? TRACK_CELL_WIDTH_DEFAULT };
211
+ }
179
212
  function toneClasses(tone) {
180
213
  if (tone === "muted") {
181
214
  return {
@@ -224,6 +257,19 @@ export const SgTreeView = React.forwardRef(function SgTreeView(props, ref) {
224
257
  const searchId = React.useId();
225
258
  const expandedSet = React.useMemo(() => new Set(expandedIds), [expandedIds]);
226
259
  const checkedSet = React.useMemo(() => new Set(effectiveCheckedIds), [effectiveCheckedIds]);
260
+ // Um Set/Map por trilha de selecao, montado uma vez por render (nao por no) -- mesma logica de
261
+ // `checkedSet` acima, só que uma cópia por track em vez de uma única. Trilha `custom` nao tem
262
+ // estado: passa direto.
263
+ const tracks = props.tracks;
264
+ const hasTracks = !!tracks && tracks.length > 0;
265
+ const trackData = React.useMemo(() => (tracks ?? []).map((track) => {
266
+ if (track.kind === "boolean")
267
+ return { track, checkedSet: new Set(track.checkedIds) };
268
+ if (track.kind === "enum") {
269
+ return { track, valueById: new Map(Object.entries(track.valueByNodeId)) };
270
+ }
271
+ return { track };
272
+ }), [tracks]);
227
273
  const { filtered, matchIds } = React.useMemo(() => filterTreeByQuery(nodes, search), [nodes, search]);
228
274
  React.useEffect(() => {
229
275
  if (!expandMatchesOnSearch)
@@ -321,10 +367,42 @@ export const SgTreeView = React.forwardRef(function SgTreeView(props, ref) {
321
367
  const isOpen = expandedSet.has(node.id);
322
368
  const isDisabled = !!node.disabled;
323
369
  const checkedState = checkable ? computeCheckedState(node.id, maps.childrenById, checkedSet) : "unchecked";
324
- return (_jsxs("div", { children: [_jsxs("div", { className: cn("flex items-center rounded-md", DY.rowPx, DY.rowPy, DY.gap, T.rowHover, isDisabled && "opacity-60"), style: { paddingLeft: 8 + depth * DY.indent }, children: [_jsx("button", { type: "button", className: cn(DY.caret, "shrink-0 rounded-md flex items-center justify-center", "hover:bg-sg-hover2 hover:bg-foreground/10", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30", !hasChildren && "opacity-0 pointer-events-none"), onClick: () => hasChildren && !isDisabled && toggleExpand(node), "aria-label": isOpen ? t(i18n, "components.actions.collapse") : t(i18n, "components.actions.expand"), disabled: isDisabled, children: _jsx("span", { className: cn("transition-transform", isOpen && "rotate-90"), children: ">" }) }), checkable && (_jsx("input", { type: "checkbox", className: cn(SZ.checkbox, "rounded border border-sg-border bg-sg-surface", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30"), disabled: isDisabled, checked: checkedState === "checked", ref: (el) => {
325
- if (el)
326
- el.indeterminate = checkedState === "indeterminate";
327
- }, onChange: () => toggleCheck(node) })), node.icon && (_jsx("div", { className: cn("shrink-0", SZ.icon, iconTone === "primary" ? "text-[rgb(var(--sg-primary-600))]" : ""), children: node.icon })), _jsx("button", { type: "button", className: cn("flex-1 text-left rounded-md", SZ.text, isDisabled ? "cursor-not-allowed" : "cursor-pointer", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30"), onClick: () => {
370
+ return (_jsxs("div", { children: [_jsxs("div", { className: cn("flex items-center rounded-md", DY.rowPx, DY.rowPy, DY.gap, T.rowHover, isDisabled && "opacity-60"), style: { paddingLeft: 8 + depth * DY.indent }, children: [_jsx("button", { type: "button", className: cn(DY.caret, "shrink-0 rounded-md flex items-center justify-center", "hover:bg-sg-hover2 hover:bg-foreground/10", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30", !hasChildren && "opacity-0 pointer-events-none"), onClick: () => hasChildren && !isDisabled && toggleExpand(node), "aria-label": isOpen ? t(i18n, "components.actions.collapse") : t(i18n, "components.actions.expand"), disabled: isDisabled, children: _jsx("span", { className: cn("transition-transform", isOpen && "rotate-90"), children: ">" }) }), checkable &&
371
+ (() => {
372
+ const checkboxEl = (_jsx("input", { type: "checkbox", className: cn(SZ.checkbox, "rounded border border-sg-border bg-sg-surface", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30"), disabled: isDisabled, checked: checkedState === "checked", ref: (el) => {
373
+ if (el)
374
+ el.indeterminate = checkedState === "indeterminate";
375
+ }, onChange: () => toggleCheck(node) }));
376
+ // So' empacota (e reserva a largura da coluna) quando ha' cabecalho pra alinhar --
377
+ // sem `tracks`, o checkbox continua exatamente como sempre foi.
378
+ return hasTracks ? (_jsx("div", { className: cn(TRACK_CELL_CLASS, "flex items-center"), style: trackCellWidth({}), children: checkboxEl })) : (checkboxEl);
379
+ })(), trackData.map(({ track, ...data }) => {
380
+ if (track.kind === "custom") {
381
+ return (_jsx("div", { className: cn(TRACK_CELL_CLASS, "flex items-center gap-1"), style: trackCellWidth(track), children: track.render(node) }, track.id));
382
+ }
383
+ const trackDisabled = isDisabled || !!track.disabled?.(node);
384
+ const adornment = track.adornment?.(node) ?? null;
385
+ if (track.kind === "boolean") {
386
+ const checkedSetTrack = data.checkedSet;
387
+ const state = computeCheckedState(node.id, maps.childrenById, checkedSetTrack);
388
+ return (_jsxs("div", { className: cn(TRACK_CELL_CLASS, "flex items-center gap-1"), style: trackCellWidth(track), children: [_jsx("input", { type: "checkbox", "aria-label": track.ariaLabel, className: cn(SZ.checkbox, "shrink-0 rounded border border-sg-border bg-sg-surface", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30"), disabled: trackDisabled, checked: state === "checked", ref: (el) => {
389
+ if (el)
390
+ el.indeterminate = state === "indeterminate";
391
+ }, onChange: () => {
392
+ const next = new Set(checkedSetTrack);
393
+ setBranchChecked(node.id, state !== "checked", maps.childrenById, next);
394
+ track.onCheckedChange(Array.from(next));
395
+ } }), adornment] }, track.id));
396
+ }
397
+ const valueById = data.valueById;
398
+ const result = computeCascadeValue(node.id, maps.childrenById, valueById);
399
+ return (_jsxs("div", { className: cn(TRACK_CELL_CLASS, "flex items-center gap-1"), style: trackCellWidth(track), children: [_jsxs("select", { "aria-label": track.ariaLabel ?? track.label ?? track.id, disabled: trackDisabled, value: result.mixed ? "" : (result.value ?? ""), className: cn(SZ.text, "w-full rounded-md border border-sg-border bg-sg-surface px-1 py-0.5", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30"), onChange: (e) => {
400
+ const v = e.target.value === "" ? undefined : e.target.value;
401
+ const nextValueById = new Map(valueById);
402
+ setBranchValue(node.id, v, maps.childrenById, nextValueById);
403
+ track.onChange(Object.fromEntries(nextValueById));
404
+ }, children: [_jsx("option", { value: "", children: result.mixed ? (track.mixedLabel ?? "— misto —") : (track.placeholder ?? "—") }), track.options.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }), adornment] }, track.id));
405
+ }), node.icon && (_jsx("div", { className: cn("shrink-0", SZ.icon, iconTone === "primary" ? "text-[rgb(var(--sg-primary-600))]" : ""), children: node.icon })), _jsx("button", { type: "button", className: cn("flex-1 text-left rounded-md", SZ.text, isDisabled ? "cursor-not-allowed" : "cursor-pointer", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30"), onClick: () => {
328
406
  if (isDisabled)
329
407
  return;
330
408
  if (!hasChildren) {
@@ -337,6 +415,6 @@ export const SgTreeView = React.forwardRef(function SgTreeView(props, ref) {
337
415
  }, disabled: isDisabled, children: node.label })] }), hasChildren && isOpen && _jsx("div", { children: node.children.map((c) => renderNode(c, depth + 1)) })] }, node.id));
338
416
  };
339
417
  const hasAny = filtered.length > 0;
340
- return (_jsxs("div", { className: cn("w-full", className), style: style, children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [searchable && (_jsx(SgInputText, { id: searchId, labelText: searchPlaceholder, placeholder: searchPlaceholder, prefixIcon: _jsx(Search, { size: 16 }), clearButton: true, filled: true, inputProps: { value: search }, onChange: (value) => setSearch(value) })), _jsx(SgButton, { severity: "secondary", appearance: "ghost", size: "sm", shape: "rounded", onClick: expandAll, leftIcon: _jsx(ChevronDown, { className: "size-4" }), children: t(i18n, "components.actions.expandAll") }), _jsx(SgButton, { severity: "secondary", appearance: "ghost", size: "sm", shape: "rounded", onClick: collapseAll, leftIcon: _jsx(ChevronUp, { className: "size-4" }), children: t(i18n, "components.actions.collapseAll") }), checkable && (_jsx(SgButton, { severity: "danger", appearance: "outline", size: "sm", shape: "rounded", onClick: clearChecked, title: t(i18n, "components.actions.clearSelection"), children: t(i18n, "components.actions.clear") }))] }), _jsxs("div", { className: cn("rounded-sg border border-sg-border", T.panelBg, "text-sg-text"), children: [_jsx("div", { className: cn("overflow-auto", maxHeightClassName), children: _jsx("div", { className: "p-2", children: !hasAny ? (_jsx("div", { className: cn("p-6 text-center", SZ.text, T.subtle), children: emptyText })) : (filtered.map((n) => renderNode(n, 0))) }) }), confirmEnabled && confirmCfg && (_jsx("div", { className: cn("border-t border-sg-border px-3 py-2", (confirmCfg.sticky ?? true) && "sticky bottom-0 bg-sg-surface/95 bg-background/95 backdrop-blur"), children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("div", { className: cn("text-xs", T.subtle), children: confirmCfg.hint ?? (_jsxs(_Fragment, { children: [t(i18n, "components.tree.selected", { count: confirmSelectedIds.length }), confirmSelection === "leafOnly" ? t(i18n, "components.tree.selectedLeafs") : ""] })) }), _jsxs("div", { className: "flex items-center gap-2", children: [(confirmCfg.showCancel ?? false) && (_jsx("button", { type: "button", onClick: onCancel, className: cn("rounded-md border border-sg-border", T.panelBg, "text-sg-text hover:bg-sg-hover", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30", SZ.btn), children: confirmCfg.cancelLabel ?? t(i18n, "components.actions.cancel") })), _jsx(SgButton, { severity: "secondary", appearance: "outline", size: "md", shape: "rounded", onClick: clearChecked, disabled: !!confirmDisabled, children: t(i18n, "components.actions.clear") }), _jsx(SgButton, { severity: "primary", appearance: "solid", elevation: "sm", size: "md", shape: "rounded", disabled: !!confirmDisabled, onClick: onConfirm, children: confirmCfg.label ?? t(i18n, "components.actions.confirm") })] })] }) }))] })] }));
418
+ return (_jsxs("div", { className: cn("w-full", className), style: style, children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [searchable && (_jsx(SgInputText, { id: searchId, labelText: searchPlaceholder, placeholder: searchPlaceholder, prefixIcon: _jsx(Search, { size: 16 }), clearButton: true, filled: true, inputProps: { value: search }, onChange: (value) => setSearch(value) })), _jsx(SgButton, { severity: "secondary", appearance: "ghost", size: "sm", shape: "rounded", onClick: expandAll, leftIcon: _jsx(ChevronDown, { className: "size-4" }), children: t(i18n, "components.actions.expandAll") }), _jsx(SgButton, { severity: "secondary", appearance: "ghost", size: "sm", shape: "rounded", onClick: collapseAll, leftIcon: _jsx(ChevronUp, { className: "size-4" }), children: t(i18n, "components.actions.collapseAll") }), checkable && (_jsx(SgButton, { severity: "danger", appearance: "outline", size: "sm", shape: "rounded", onClick: clearChecked, title: t(i18n, "components.actions.clearSelection"), children: t(i18n, "components.actions.clear") }))] }), _jsxs("div", { className: cn("rounded-sg border border-sg-border", T.panelBg, "text-sg-text"), children: [hasTracks && (_jsxs("div", { className: cn("flex items-center border-b border-sg-border", DY.rowPx, DY.rowPy, DY.gap, SZ.text, "font-semibold text-sg-muted"), children: [_jsx("span", { className: cn(DY.caret, "shrink-0") }), checkable && (_jsx("div", { className: cn(TRACK_CELL_CLASS, "truncate"), style: trackCellWidth({}), title: props.checkableLabel, children: props.checkableLabel })), (tracks ?? []).map((track) => (_jsx("div", { className: cn(TRACK_CELL_CLASS, "truncate"), style: trackCellWidth(track), title: track.label, children: track.label }, track.id)))] })), _jsx("div", { className: cn("overflow-auto", maxHeightClassName), children: _jsx("div", { className: "p-2", children: !hasAny ? (_jsx("div", { className: cn("p-6 text-center", SZ.text, T.subtle), children: emptyText })) : (filtered.map((n) => renderNode(n, 0))) }) }), confirmEnabled && confirmCfg && (_jsx("div", { className: cn("border-t border-sg-border px-3 py-2", (confirmCfg.sticky ?? true) && "sticky bottom-0 bg-sg-surface/95 bg-background/95 backdrop-blur"), children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("div", { className: cn("text-xs", T.subtle), children: confirmCfg.hint ?? (_jsxs(_Fragment, { children: [t(i18n, "components.tree.selected", { count: confirmSelectedIds.length }), confirmSelection === "leafOnly" ? t(i18n, "components.tree.selectedLeafs") : ""] })) }), _jsxs("div", { className: "flex items-center gap-2", children: [(confirmCfg.showCancel ?? false) && (_jsx("button", { type: "button", onClick: onCancel, className: cn("rounded-md border border-sg-border", T.panelBg, "text-sg-text hover:bg-sg-hover", "focus:outline-none focus:ring-2 focus:ring-sg-focus/30", SZ.btn), children: confirmCfg.cancelLabel ?? t(i18n, "components.actions.cancel") })), _jsx(SgButton, { severity: "secondary", appearance: "outline", size: "md", shape: "rounded", onClick: clearChecked, disabled: !!confirmDisabled, children: t(i18n, "components.actions.clear") }), _jsx(SgButton, { severity: "primary", appearance: "solid", elevation: "sm", size: "md", shape: "rounded", disabled: !!confirmDisabled, onClick: onConfirm, children: confirmCfg.label ?? t(i18n, "components.actions.confirm") })] })] }) }))] })] }));
341
419
  });
342
420
  SgTreeView.displayName = "SgTreeView";