@seedgrid/fe-components 2026.9.0 → 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.
- package/dist/generated/compiledStyles.d.ts +1 -1
- package/dist/generated/compiledStyles.d.ts.map +1 -1
- package/dist/generated/compiledStyles.js +1 -1
- package/dist/i18n/en-US.d.ts.map +1 -1
- package/dist/i18n/en-US.js +0 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +0 -1
- package/dist/i18n/fr.d.ts.map +1 -1
- package/dist/i18n/fr.js +0 -1
- package/dist/i18n/pt-BR.d.ts.map +1 -1
- package/dist/i18n/pt-BR.js +0 -1
- package/dist/i18n/pt-PT.d.ts.map +1 -1
- package/dist/i18n/pt-PT.js +0 -1
- package/dist/layout/SgTreeView.d.ts +30 -19
- package/dist/layout/SgTreeView.d.ts.map +1 -1
- package/dist/layout/SgTreeView.js +30 -18
- package/dist/sandbox.cjs +15 -15
- package/dist/style.css +1 -1
- package/package.json +2 -2
|
@@ -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
|
|
4
|
+
import { ChevronDown, ChevronUp, Search } from "lucide-react";
|
|
5
5
|
import { SgInputText } from "../inputs/SgInputText";
|
|
6
6
|
import { SgButton } from "../buttons/SgButton";
|
|
7
7
|
import { t, useComponentsI18n } from "../i18n";
|
|
@@ -201,10 +201,14 @@ const densityMap = {
|
|
|
201
201
|
normal: { rowPy: "py-1", rowPx: "px-2", gap: "gap-2", caret: "h-6 w-6", indent: 16 },
|
|
202
202
|
comfortable: { rowPy: "py-1.5", rowPx: "px-2.5", gap: "gap-2.5", caret: "h-7 w-7", indent: 18 }
|
|
203
203
|
};
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
const TRACK_CELL_CLASS = "
|
|
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
|
+
}
|
|
208
212
|
function toneClasses(tone) {
|
|
209
213
|
if (tone === "muted") {
|
|
210
214
|
return {
|
|
@@ -253,13 +257,19 @@ export const SgTreeView = React.forwardRef(function SgTreeView(props, ref) {
|
|
|
253
257
|
const searchId = React.useId();
|
|
254
258
|
const expandedSet = React.useMemo(() => new Set(expandedIds), [expandedIds]);
|
|
255
259
|
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.
|
|
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.
|
|
258
263
|
const tracks = props.tracks;
|
|
259
264
|
const hasTracks = !!tracks && tracks.length > 0;
|
|
260
|
-
const trackData = React.useMemo(() => (tracks ?? []).map((track) =>
|
|
261
|
-
|
|
262
|
-
|
|
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]);
|
|
263
273
|
const { filtered, matchIds } = React.useMemo(() => filterTreeByQuery(nodes, search), [nodes, search]);
|
|
264
274
|
React.useEffect(() => {
|
|
265
275
|
if (!expandMatchesOnSearch)
|
|
@@ -365,31 +375,33 @@ export const SgTreeView = React.forwardRef(function SgTreeView(props, ref) {
|
|
|
365
375
|
}, onChange: () => toggleCheck(node) }));
|
|
366
376
|
// So' empacota (e reserva a largura da coluna) quando ha' cabecalho pra alinhar --
|
|
367
377
|
// 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);
|
|
378
|
+
return hasTracks ? (_jsx("div", { className: cn(TRACK_CELL_CLASS, "flex items-center"), style: trackCellWidth({}), children: checkboxEl })) : (checkboxEl);
|
|
369
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
|
+
}
|
|
370
383
|
const trackDisabled = isDisabled || !!track.disabled?.(node);
|
|
371
|
-
const
|
|
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;
|
|
384
|
+
const adornment = track.adornment?.(node) ?? null;
|
|
373
385
|
if (track.kind === "boolean") {
|
|
374
386
|
const checkedSetTrack = data.checkedSet;
|
|
375
387
|
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) => {
|
|
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) => {
|
|
377
389
|
if (el)
|
|
378
390
|
el.indeterminate = state === "indeterminate";
|
|
379
391
|
}, onChange: () => {
|
|
380
392
|
const next = new Set(checkedSetTrack);
|
|
381
393
|
setBranchChecked(node.id, state !== "checked", maps.childrenById, next);
|
|
382
394
|
track.onCheckedChange(Array.from(next));
|
|
383
|
-
} }),
|
|
395
|
+
} }), adornment] }, track.id));
|
|
384
396
|
}
|
|
385
397
|
const valueById = data.valueById;
|
|
386
398
|
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) => {
|
|
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) => {
|
|
388
400
|
const v = e.target.value === "" ? undefined : e.target.value;
|
|
389
401
|
const nextValueById = new Map(valueById);
|
|
390
402
|
setBranchValue(node.id, v, maps.childrenById, nextValueById);
|
|
391
403
|
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)))] }),
|
|
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));
|
|
393
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: () => {
|
|
394
406
|
if (isDisabled)
|
|
395
407
|
return;
|
|
@@ -403,6 +415,6 @@ export const SgTreeView = React.forwardRef(function SgTreeView(props, ref) {
|
|
|
403
415
|
}, disabled: isDisabled, children: node.label })] }), hasChildren && isOpen && _jsx("div", { children: node.children.map((c) => renderNode(c, depth + 1)) })] }, node.id));
|
|
404
416
|
};
|
|
405
417
|
const hasAny = filtered.length > 0;
|
|
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") })] })] }) }))] })] }));
|
|
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") })] })] }) }))] })] }));
|
|
407
419
|
});
|
|
408
420
|
SgTreeView.displayName = "SgTreeView";
|