@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":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACxC,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;AAC7D,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEzC,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;AAEjE,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,YAAY,CAAC,EAAE,kBAAkB,GAAG,UAAU,EAAE,CAWtG;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,cAAc,EAAE,EACtB,YAAY,CAAC,EAAE,kBAAkB,GAChC;IAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CA4B/C;
|
|
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 &&
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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";
|