@seedgrid/fe-components 2026.8.7 → 2026.9.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.
@@ -25,6 +25,46 @@ export declare function sgTreeFromJsonWithChecked(json: SgTreeNodeJson[], iconRe
25
25
  nodes: SgTreeNode[];
26
26
  checkedIds: string[];
27
27
  };
28
+ export type SgTreeTrackBoolean = {
29
+ id: string;
30
+ kind: "boolean";
31
+ /** Cabecalho da coluna -- some sem ele; com 2+ trilhas na mesma arvore, o usuario nao tem
32
+ * como saber o que cada checkbox/select significa so' pela posicao. */
33
+ label?: string;
34
+ checkedIds: string[];
35
+ onCheckedChange: (ids: string[]) => void;
36
+ disabled?: (node: SgTreeNode) => boolean;
37
+ ariaLabel?: string;
38
+ /** Ids marcados como sugestao (de IA ou outra fonte) nesta trilha -- ganham um indicador
39
+ * colado no PROPRIO controle desta coluna, nao na descricao do no' (uma sugestao pode acertar
40
+ * uma trilha e nao a outra, entao o indicador precisa ser por trilha). */
41
+ suggestedIds?: string[];
42
+ /** Substitui o icone padrao (Sparkles) do indicador de sugestao. */
43
+ suggestedIcon?: React.ReactNode;
44
+ };
45
+ export type SgTreeTrackEnum = {
46
+ id: string;
47
+ kind: "enum";
48
+ label?: string;
49
+ options: {
50
+ value: string;
51
+ label: string;
52
+ }[];
53
+ valueByNodeId: Record<string, string | undefined>;
54
+ onChange: (next: Record<string, string | undefined>) => void;
55
+ disabled?: (node: SgTreeNode) => boolean;
56
+ placeholder?: string;
57
+ mixedLabel?: string;
58
+ suggestedIds?: string[];
59
+ suggestedIcon?: React.ReactNode;
60
+ };
61
+ /**
62
+ * Uma trilha extra de selecao por linha, independente do `checkable`/`checkedIds` principal.
63
+ * Cada trilha tem sua propria cascata pai->filhos (boolean: check/uncheck; enum: propaga o
64
+ * valor escolhido). Controlada de fora (sem defaultValue) -- o estado mora em quem chama,
65
+ * igual ja acontece hoje com `checkedIds` nas telas que usam SgTreeView.
66
+ */
67
+ export type SgTreeTrack = SgTreeTrackBoolean | SgTreeTrackEnum;
28
68
  export type SgTreeViewRef = {
29
69
  getCheckedIds: () => string[];
30
70
  getCheckedLeafIds: () => string[];
@@ -60,6 +100,12 @@ export type SgTreeViewProps = {
60
100
  checkedIds?: string[];
61
101
  defaultCheckedIds?: string[];
62
102
  onCheckedChange?: (ids: string[]) => void;
103
+ /** Cabecalho da coluna do checkbox principal -- so' e' desenhado quando `tracks` tambem esta'
104
+ * presente (e' o cabecalho de colunas como um todo que fica ambiguo com 2+ controles por linha,
105
+ * nao o checkbox sozinho). */
106
+ checkableLabel?: string;
107
+ /** Trilhas de selecao extras, uma por coluna de controle na linha -- ver SgTreeTrack. */
108
+ tracks?: SgTreeTrack[];
63
109
  expandedIds?: string[];
64
110
  defaultExpandedIds?: string[];
65
111
  onExpandedChange?: (ids: string[]) => void;
@@ -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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"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { ChevronDown, ChevronUp, Search } from "lucide-react";
4
+ import { ChevronDown, ChevronUp, Search, Sparkles } from "lucide-react";
5
5
  import { SgInputText } from "../inputs/SgInputText";
6
6
  import { SgButton } from "../buttons/SgButton";
7
7
  import { t, useComponentsI18n } from "../i18n";
@@ -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,10 @@ 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
+ // Largura fixa de cada coluna de trilha (checkbox ou select), usada tanto na linha quanto no
205
+ // cabecalho -- e' o que deixa o texto do cabecalho ("Prioridade", "Tipo de conta") alinhado com
206
+ // o controle por baixo dele, em vez de so' o controle estreito sem legenda nenhuma.
207
+ const TRACK_CELL_CLASS = "w-[110px] shrink-0";
179
208
  function toneClasses(tone) {
180
209
  if (tone === "muted") {
181
210
  return {
@@ -224,6 +253,13 @@ export const SgTreeView = React.forwardRef(function SgTreeView(props, ref) {
224
253
  const searchId = React.useId();
225
254
  const expandedSet = React.useMemo(() => new Set(expandedIds), [expandedIds]);
226
255
  const checkedSet = React.useMemo(() => new Set(effectiveCheckedIds), [effectiveCheckedIds]);
256
+ // Um Set/Map por trilha, montado uma vez por render (nao por no) -- mesma logica de
257
+ // `checkedSet` acima, só que uma cópia por track em vez de uma única.
258
+ const tracks = props.tracks;
259
+ const hasTracks = !!tracks && tracks.length > 0;
260
+ const trackData = React.useMemo(() => (tracks ?? []).map((track) => track.kind === "boolean"
261
+ ? { track, checkedSet: new Set(track.checkedIds) }
262
+ : { track, valueById: new Map(Object.entries(track.valueByNodeId)) }), [tracks]);
227
263
  const { filtered, matchIds } = React.useMemo(() => filterTreeByQuery(nodes, search), [nodes, search]);
228
264
  React.useEffect(() => {
229
265
  if (!expandMatchesOnSearch)
@@ -321,10 +357,40 @@ export const SgTreeView = React.forwardRef(function SgTreeView(props, ref) {
321
357
  const isOpen = expandedSet.has(node.id);
322
358
  const isDisabled = !!node.disabled;
323
359
  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: () => {
360
+ 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 &&
361
+ (() => {
362
+ 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) => {
363
+ if (el)
364
+ el.indeterminate = checkedState === "indeterminate";
365
+ }, onChange: () => toggleCheck(node) }));
366
+ // So' empacota (e reserva a largura da coluna) quando ha' cabecalho pra alinhar --
367
+ // sem `tracks`, o checkbox continua exatamente como sempre foi.
368
+ return hasTracks ? (_jsx("div", { className: cn(TRACK_CELL_CLASS, "flex items-center"), children: checkboxEl })) : (checkboxEl);
369
+ })(), trackData.map(({ track, ...data }) => {
370
+ const trackDisabled = isDisabled || !!track.disabled?.(node);
371
+ const suggested = !!track.suggestedIds?.includes(node.id);
372
+ const suggestedIndicator = suggested ? (_jsx("span", { title: t(i18n, "components.tree.suggested"), className: "shrink-0", children: track.suggestedIcon ?? (_jsx(Sparkles, { className: cn(SZ.icon, "text-[rgb(var(--sg-primary-600))]") })) })) : null;
373
+ if (track.kind === "boolean") {
374
+ const checkedSetTrack = data.checkedSet;
375
+ const state = computeCheckedState(node.id, maps.childrenById, checkedSetTrack);
376
+ return (_jsxs("div", { className: cn(TRACK_CELL_CLASS, "flex items-center gap-1"), 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) => {
377
+ if (el)
378
+ el.indeterminate = state === "indeterminate";
379
+ }, onChange: () => {
380
+ const next = new Set(checkedSetTrack);
381
+ setBranchChecked(node.id, state !== "checked", maps.childrenById, next);
382
+ track.onCheckedChange(Array.from(next));
383
+ } }), suggestedIndicator] }, track.id));
384
+ }
385
+ const valueById = data.valueById;
386
+ const result = computeCascadeValue(node.id, maps.childrenById, valueById);
387
+ return (_jsxs("div", { className: cn(TRACK_CELL_CLASS, "flex items-center gap-1"), children: [_jsxs("select", { "aria-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) => {
388
+ const v = e.target.value === "" ? undefined : e.target.value;
389
+ const nextValueById = new Map(valueById);
390
+ setBranchValue(node.id, v, maps.childrenById, nextValueById);
391
+ track.onChange(Object.fromEntries(nextValueById));
392
+ }, 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)))] }), suggestedIndicator] }, track.id));
393
+ }), 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
394
  if (isDisabled)
329
395
  return;
330
396
  if (!hasChildren) {
@@ -337,6 +403,6 @@ export const SgTreeView = React.forwardRef(function SgTreeView(props, ref) {
337
403
  }, disabled: isDisabled, children: node.label })] }), hasChildren && isOpen && _jsx("div", { children: node.children.map((c) => renderNode(c, depth + 1)) })] }, node.id));
338
404
  };
339
405
  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") })] })] }) }))] })] }));
406
+ 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"), title: props.checkableLabel, children: props.checkableLabel })), (tracks ?? []).map((track) => (_jsx("div", { className: cn(TRACK_CELL_CLASS, "truncate"), 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
407
  });
342
408
  SgTreeView.displayName = "SgTreeView";