@mitcsutt/kiln-ui 0.7.0 → 0.8.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.
- package/dist/components/display/TreeTable/TreeTable.d.ts +69 -0
- package/dist/components/display/TreeTable/TreeTable.js +152 -0
- package/dist/components/display/TreeTable/TreeTable.js.map +1 -0
- package/dist/components/display/TreeTable/TreeTable.module.js +12 -0
- package/dist/components/display/TreeTable/TreeTable.module.js.map +1 -0
- package/dist/components/display/TreeTable/index.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +31 -30
- package/dist/styles.css +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode, type RefAttributes } from 'react';
|
|
2
|
+
import { type TableAlign, type TableColumnWidth, type TableProps } from '../Table/index.js';
|
|
3
|
+
import type { VisibilityProps } from '../../../utils/visibility.js';
|
|
4
|
+
/** One column of a {@link TreeTable}. The first column holds the tree: indent, toggle and the row's name. */
|
|
5
|
+
export interface TreeTableColumn extends VisibilityProps {
|
|
6
|
+
/** Stable key, passed back to `renderCell`. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** The header cell's content. */
|
|
9
|
+
header: ReactNode;
|
|
10
|
+
align?: TableAlign;
|
|
11
|
+
/** Numeric column: right-aligned with tabular figures. */
|
|
12
|
+
numeric?: boolean;
|
|
13
|
+
/** Column sizing: `fill` takes the spare width, `min` shrinks to fit. */
|
|
14
|
+
width?: TableColumnWidth;
|
|
15
|
+
}
|
|
16
|
+
export interface TreeTableProps<T> extends Omit<TableProps, 'children' | 'label' | 'role' | 'onChange' | 'defaultValue'> {
|
|
17
|
+
/** Name for the grid, read by assistive technology. */
|
|
18
|
+
label: string;
|
|
19
|
+
/** The columns, in order. The first holds the tree. */
|
|
20
|
+
columns: readonly TreeTableColumn[];
|
|
21
|
+
/** The top-level rows. */
|
|
22
|
+
rows: readonly T[];
|
|
23
|
+
/** A stable, unique id for a row. It is what `expanded` holds and what keeps focus on a row across renders. */
|
|
24
|
+
getRowId: (row: T) => string;
|
|
25
|
+
/** The row's children, or `undefined` when it has none. */
|
|
26
|
+
getChildren: (row: T) => readonly T[] | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Whether the row can be opened. Defaults to `getChildren(row)` having entries. Return `true` for a
|
|
29
|
+
* row whose children load when it opens.
|
|
30
|
+
*/
|
|
31
|
+
hasChildren?: (row: T) => boolean;
|
|
32
|
+
/** The ids of the open rows (controlled). */
|
|
33
|
+
expanded?: readonly string[];
|
|
34
|
+
/** The ids of the rows that start open (uncontrolled). */
|
|
35
|
+
defaultExpanded?: readonly string[];
|
|
36
|
+
/** Called with the ids of the open rows when one opens or closes. */
|
|
37
|
+
onExpandedChange?: (expanded: string[]) => void;
|
|
38
|
+
/** The content of one cell. The first column's content sits beside the toggle. */
|
|
39
|
+
renderCell: (row: T, column: TreeTableColumn) => ReactNode;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A table of rows in a hierarchy, with a toggle on each parent and keyboard navigation like a file
|
|
43
|
+
* tree.
|
|
44
|
+
*
|
|
45
|
+
* @remarks
|
|
46
|
+
* `TreeTable` is a WAI-ARIA `treegrid` set in `Table`'s styles. Hand it the top-level `rows`, how to
|
|
47
|
+
* find a row's `children`, and `renderCell`; it handles the indent, the toggles, and the
|
|
48
|
+
* `aria-level`, `aria-expanded`, `aria-setsize` and `aria-posinset` on every row.
|
|
49
|
+
*
|
|
50
|
+
* ## Keyboard
|
|
51
|
+
*
|
|
52
|
+
* The grid is one tab stop, and the arrow keys move between rows: Up and Down step by row, Right
|
|
53
|
+
* opens a closed row and then steps into its first child, Left closes an open row and then steps
|
|
54
|
+
* out to its parent, Home and End jump to the first and last visible row, and Enter follows the
|
|
55
|
+
* first link in the row. Links and buttons in a row's cells are reached by Enter, not Tab, so put
|
|
56
|
+
* the row's main link in its first column.
|
|
57
|
+
*
|
|
58
|
+
* ## Open rows
|
|
59
|
+
*
|
|
60
|
+
* `expanded` holds the ids of the open rows, as `getRowId` returns them. Pass it with
|
|
61
|
+
* `onExpandedChange` to control them, for example to keep them in the URL, or `defaultExpanded` to
|
|
62
|
+
* choose which start open. For children that load when a row opens, return `true` from
|
|
63
|
+
* `hasChildren` and give the row a placeholder child while they load.
|
|
64
|
+
*
|
|
65
|
+
* @privateRemarks
|
|
66
|
+
* A separate export, not `Table.Tree`, so a plain `import { Table }` never carries the key handling
|
|
67
|
+
* (docs/adr/0044). Ref and native props go to the `<table>`.
|
|
68
|
+
*/
|
|
69
|
+
export declare const TreeTable: <T>(props: TreeTableProps<T> & RefAttributes<HTMLTableElement>) => ReactElement;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { cx as e } from "../../../utils/cx.js";
|
|
2
|
+
import { ChevronRightIcon as t } from "../../../icons/icons.js";
|
|
3
|
+
import { useLatest as n } from "../../inputs/internal/useLatest.js";
|
|
4
|
+
import { Table as r } from "../Table/Table.js";
|
|
5
|
+
import i from "./TreeTable.module.js";
|
|
6
|
+
import { forwardRef as a, useCallback as o, useLayoutEffect as s, useMemo as c, useRef as l, useState as u } from "react";
|
|
7
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/display/TreeTable/TreeTable.tsx
|
|
9
|
+
function p({ label: a, columns: p, rows: m, getRowId: h, getChildren: g, hasChildren: _, expanded: v, defaultExpanded: y, onExpandedChange: b, renderCell: x, className: S, ...C }, w) {
|
|
10
|
+
let [T, E] = u(y ?? []), D = v !== void 0, O = D ? v : T, k = n(b), [A, j] = u(), M = l(/* @__PURE__ */ new Map()), N = l(null), P = c(() => {
|
|
11
|
+
let e = new Set(O), t = [], n = (r, i, a) => {
|
|
12
|
+
r.forEach((o, s) => {
|
|
13
|
+
let c = h(o), l = g(o), u = _ ? _(o) : (l?.length ?? 0) > 0, d = u && e.has(c);
|
|
14
|
+
t.push({
|
|
15
|
+
row: o,
|
|
16
|
+
id: c,
|
|
17
|
+
level: i + 1,
|
|
18
|
+
setSize: r.length,
|
|
19
|
+
posInSet: s + 1,
|
|
20
|
+
parentId: a,
|
|
21
|
+
expandable: u,
|
|
22
|
+
open: d
|
|
23
|
+
}), d && l && n(l, i + 1, c);
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
return n(m, 0, void 0), t;
|
|
27
|
+
}, [
|
|
28
|
+
m,
|
|
29
|
+
O,
|
|
30
|
+
h,
|
|
31
|
+
g,
|
|
32
|
+
_
|
|
33
|
+
]), F = P.some((e) => e.id === A) ? A : P[0]?.id, I = o((e, t) => {
|
|
34
|
+
let n = O.filter((t) => t !== e);
|
|
35
|
+
t && n.push(e), D || E(n), k.current?.(n);
|
|
36
|
+
}, [
|
|
37
|
+
D,
|
|
38
|
+
O,
|
|
39
|
+
k
|
|
40
|
+
]);
|
|
41
|
+
s(() => {
|
|
42
|
+
N.current?.querySelectorAll("a[href], button").forEach((e) => {
|
|
43
|
+
e.setAttribute("tabindex", "-1");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
let L = (e) => {
|
|
47
|
+
e !== void 0 && (j(e), M.current.get(e)?.focus());
|
|
48
|
+
}, R = (e) => {
|
|
49
|
+
let t = e.target.closest("tr");
|
|
50
|
+
if (e.target !== t || e.altKey || e.ctrlKey || e.metaKey) return;
|
|
51
|
+
let n = P.findIndex((e) => M.current.get(e.id) === t), r = P[n];
|
|
52
|
+
if (!r) return;
|
|
53
|
+
let i = (t) => {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
let r = {
|
|
56
|
+
next: P[n + 1],
|
|
57
|
+
prev: P[n - 1],
|
|
58
|
+
first: P[0],
|
|
59
|
+
last: P[P.length - 1]
|
|
60
|
+
}[t];
|
|
61
|
+
L(r?.id);
|
|
62
|
+
};
|
|
63
|
+
switch (e.key) {
|
|
64
|
+
case "ArrowDown":
|
|
65
|
+
i("next");
|
|
66
|
+
return;
|
|
67
|
+
case "ArrowUp":
|
|
68
|
+
i("prev");
|
|
69
|
+
return;
|
|
70
|
+
case "Home":
|
|
71
|
+
i("first");
|
|
72
|
+
return;
|
|
73
|
+
case "End":
|
|
74
|
+
i("last");
|
|
75
|
+
return;
|
|
76
|
+
case "ArrowRight":
|
|
77
|
+
if (!r.expandable) return;
|
|
78
|
+
e.preventDefault(), r.open ? P[n + 1]?.parentId === r.id && L(P[n + 1]?.id) : I(r.id, !0);
|
|
79
|
+
return;
|
|
80
|
+
case "ArrowLeft":
|
|
81
|
+
r.open ? (e.preventDefault(), I(r.id, !1)) : r.parentId !== void 0 && (e.preventDefault(), L(r.parentId));
|
|
82
|
+
return;
|
|
83
|
+
case "Enter": {
|
|
84
|
+
let n = t.querySelector("a[href]");
|
|
85
|
+
if (!n) return;
|
|
86
|
+
e.preventDefault(), n.click();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}, z = (e) => {
|
|
91
|
+
let t = e.target.closest("tr"), n = P.find((e) => M.current.get(e.id) === t);
|
|
92
|
+
n && j(n.id);
|
|
93
|
+
};
|
|
94
|
+
return /* @__PURE__ */ f(r, {
|
|
95
|
+
ref: w,
|
|
96
|
+
role: "treegrid",
|
|
97
|
+
"aria-label": a,
|
|
98
|
+
className: e(i.treeTable, S),
|
|
99
|
+
...C,
|
|
100
|
+
children: [/* @__PURE__ */ d(r.Head, { children: /* @__PURE__ */ d(r.Row, { children: p.map((e) => /* @__PURE__ */ d(r.HeaderCell, {
|
|
101
|
+
align: e.align,
|
|
102
|
+
numeric: e.numeric,
|
|
103
|
+
width: e.width,
|
|
104
|
+
hideBelow: e.hideBelow,
|
|
105
|
+
hideAbove: e.hideAbove,
|
|
106
|
+
children: e.header
|
|
107
|
+
}, e.id)) }) }), /* @__PURE__ */ d(r.Body, {
|
|
108
|
+
ref: N,
|
|
109
|
+
onKeyDown: R,
|
|
110
|
+
onFocus: z,
|
|
111
|
+
children: P.map((e) => /* @__PURE__ */ d(r.Row, {
|
|
112
|
+
ref: (t) => {
|
|
113
|
+
t ? M.current.set(e.id, t) : M.current.delete(e.id);
|
|
114
|
+
},
|
|
115
|
+
className: i.row,
|
|
116
|
+
interactive: !0,
|
|
117
|
+
tabIndex: e.id === F ? 0 : -1,
|
|
118
|
+
"aria-level": e.level,
|
|
119
|
+
"aria-setsize": e.setSize,
|
|
120
|
+
"aria-posinset": e.posInSet,
|
|
121
|
+
"aria-expanded": e.expandable ? e.open : void 0,
|
|
122
|
+
children: p.map((n, a) => /* @__PURE__ */ d(r.Cell, {
|
|
123
|
+
rowHeader: a === 0,
|
|
124
|
+
align: n.align,
|
|
125
|
+
numeric: n.numeric,
|
|
126
|
+
hideBelow: n.hideBelow,
|
|
127
|
+
hideAbove: n.hideAbove,
|
|
128
|
+
children: a === 0 ? /* @__PURE__ */ f("span", {
|
|
129
|
+
className: i.treeCellContent,
|
|
130
|
+
style: { "--_level": e.level - 1 },
|
|
131
|
+
children: [/* @__PURE__ */ d("button", {
|
|
132
|
+
type: "button",
|
|
133
|
+
className: i.toggle,
|
|
134
|
+
tabIndex: -1,
|
|
135
|
+
"aria-hidden": "true",
|
|
136
|
+
"data-leaf": e.expandable ? void 0 : "",
|
|
137
|
+
onClick: () => {
|
|
138
|
+
L(e.id), I(e.id, !e.open);
|
|
139
|
+
},
|
|
140
|
+
children: /* @__PURE__ */ d(t, { className: i.chevron })
|
|
141
|
+
}), x(e.row, n)]
|
|
142
|
+
}) : x(e.row, n)
|
|
143
|
+
}, n.id))
|
|
144
|
+
}, e.id))
|
|
145
|
+
})]
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
var m = a(p);
|
|
149
|
+
//#endregion
|
|
150
|
+
export { m as TreeTable };
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=TreeTable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TreeTable.js","names":["styles","TreeTable_module_default","TreeTableInner","expandedProp","rest","inner","setInner","useState","defaultExpanded","controlled","expandedIds","onChange","useLatest","onExpandedChange","activeId","setActiveId","elements","useRef","body","nodes","useMemo","open","out","walk","id","getRowId","row","children","getChildren","expandable","hasChildren","isOpen","level","siblings","index","rows","tabId","node","setExpanded","useCallback","next","existing","moveTo","onKeyDown","rowElement","event","go","target","to","link","onFocus","found","Table","label","cx","className","columns","column","element","columnIndex","ChevronRightIcon","renderCell","TreeTable","forwardRef"],"sources":["../../../../src/components/display/TreeTable/TreeTable.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type ReactElement,\n type ReactNode,\n type Ref,\n type RefAttributes,\n} from 'react'\nimport {\n Table,\n type TableAlign,\n type TableColumnWidth,\n type TableProps,\n} from '#components/display/Table'\nimport { ChevronRightIcon } from '#icons'\nimport { cx } from '#utils/cx'\nimport type { VisibilityProps } from '#utils/visibility'\nimport { useLatest } from '#components/inputs/internal/useLatest'\nimport styles from './TreeTable.module.css'\n\n/** One column of a {@link TreeTable}. The first column holds the tree: indent, toggle and the row's name. */\nexport interface TreeTableColumn extends VisibilityProps {\n /** Stable key, passed back to `renderCell`. */\n id: string\n /** The header cell's content. */\n header: ReactNode\n align?: TableAlign\n /** Numeric column: right-aligned with tabular figures. */\n numeric?: boolean\n /** Column sizing: `fill` takes the spare width, `min` shrinks to fit. */\n width?: TableColumnWidth\n}\n\nexport interface TreeTableProps<T> extends Omit<\n TableProps,\n 'children' | 'label' | 'role' | 'onChange' | 'defaultValue'\n> {\n /** Name for the grid, read by assistive technology. */\n label: string\n /** The columns, in order. The first holds the tree. */\n columns: readonly TreeTableColumn[]\n /** The top-level rows. */\n rows: readonly T[]\n /** A stable, unique id for a row. It is what `expanded` holds and what keeps focus on a row across renders. */\n getRowId: (row: T) => string\n /** The row's children, or `undefined` when it has none. */\n getChildren: (row: T) => readonly T[] | undefined\n /**\n * Whether the row can be opened. Defaults to `getChildren(row)` having entries. Return `true` for a\n * row whose children load when it opens.\n */\n hasChildren?: (row: T) => boolean\n /** The ids of the open rows (controlled). */\n expanded?: readonly string[]\n /** The ids of the rows that start open (uncontrolled). */\n defaultExpanded?: readonly string[]\n /** Called with the ids of the open rows when one opens or closes. */\n onExpandedChange?: (expanded: string[]) => void\n /** The content of one cell. The first column's content sits beside the toggle. */\n renderCell: (row: T, column: TreeTableColumn) => ReactNode\n}\n\ninterface Node<T> {\n row: T\n id: string\n level: number\n setSize: number\n posInSet: number\n parentId: string | undefined\n expandable: boolean\n open: boolean\n}\n\ntype Dispatch = 'next' | 'prev' | 'first' | 'last'\n\nfunction TreeTableInner<T>(\n {\n label,\n columns,\n rows,\n getRowId,\n getChildren,\n hasChildren,\n expanded: expandedProp,\n defaultExpanded,\n onExpandedChange,\n renderCell,\n className,\n ...rest\n }: TreeTableProps<T>,\n ref: Ref<HTMLTableElement>,\n) {\n const [inner, setInner] = useState<readonly string[]>(defaultExpanded ?? [])\n const controlled = expandedProp !== undefined\n const expandedIds = controlled ? expandedProp : inner\n const onChange = useLatest(onExpandedChange)\n const [activeId, setActiveId] = useState<string>()\n const elements = useRef(new Map<string, HTMLTableRowElement>())\n const body = useRef<HTMLTableSectionElement>(null)\n\n const nodes = useMemo(() => {\n const open = new Set(expandedIds)\n const out: Node<T>[] = []\n const walk = (siblings: readonly T[], level: number, parentId: string | undefined) => {\n siblings.forEach((row, index) => {\n const id = getRowId(row)\n const children = getChildren(row)\n const expandable = hasChildren ? hasChildren(row) : (children?.length ?? 0) > 0\n const isOpen = expandable && open.has(id)\n out.push({\n row,\n id,\n level: level + 1,\n setSize: siblings.length,\n posInSet: index + 1,\n parentId,\n expandable,\n open: isOpen,\n })\n if (isOpen && children) walk(children, level + 1, id)\n })\n }\n walk(rows, 0, undefined)\n return out\n }, [rows, expandedIds, getRowId, getChildren, hasChildren])\n\n const tabId = nodes.some((node) => node.id === activeId) ? activeId : nodes[0]?.id\n\n const setExpanded = useCallback(\n (id: string, open: boolean) => {\n const next = expandedIds.filter((existing) => existing !== id)\n if (open) next.push(id)\n if (!controlled) setInner(next)\n onChange.current?.(next)\n },\n [controlled, expandedIds, onChange],\n )\n\n // Links and buttons inside a row are reached with Enter on the row, so they stay out of the tab\n // order: the grid is one tab stop, however many rows are open.\n useLayoutEffect(() => {\n body.current?.querySelectorAll<HTMLElement>('a[href], button').forEach((element) => {\n element.setAttribute('tabindex', '-1')\n })\n })\n\n const moveTo = (id: string | undefined) => {\n if (id === undefined) return\n setActiveId(id)\n elements.current.get(id)?.focus()\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLTableSectionElement>) => {\n const rowElement = (event.target as HTMLElement).closest('tr')\n if (event.target !== rowElement || event.altKey || event.ctrlKey || event.metaKey) return\n const index = nodes.findIndex((node) => elements.current.get(node.id) === rowElement)\n const node = nodes[index]\n if (!node) return\n const go = (to: Dispatch) => {\n event.preventDefault()\n const target = {\n next: nodes[index + 1],\n prev: nodes[index - 1],\n first: nodes[0],\n last: nodes[nodes.length - 1],\n }[to]\n moveTo(target?.id)\n }\n switch (event.key) {\n case 'ArrowDown':\n go('next')\n return\n case 'ArrowUp':\n go('prev')\n return\n case 'Home':\n go('first')\n return\n case 'End':\n go('last')\n return\n case 'ArrowRight':\n if (!node.expandable) return\n event.preventDefault()\n if (!node.open) setExpanded(node.id, true)\n else if (nodes[index + 1]?.parentId === node.id) moveTo(nodes[index + 1]?.id)\n return\n case 'ArrowLeft':\n if (node.open) {\n event.preventDefault()\n setExpanded(node.id, false)\n } else if (node.parentId !== undefined) {\n event.preventDefault()\n moveTo(node.parentId)\n }\n return\n case 'Enter': {\n const link = rowElement.querySelector<HTMLElement>('a[href]')\n if (!link) return\n event.preventDefault()\n link.click()\n return\n }\n }\n }\n\n // Clicking inside a row (or focusing a link in it) makes that row the one tab stop.\n const onFocus = (event: FocusEvent<HTMLTableSectionElement>) => {\n const rowElement = (event.target as HTMLElement).closest('tr')\n const found = nodes.find((node) => elements.current.get(node.id) === rowElement)\n if (found) setActiveId(found.id)\n }\n\n return (\n <Table\n ref={ref}\n role=\"treegrid\"\n aria-label={label}\n className={cx(styles.treeTable, className)}\n {...rest}\n >\n <Table.Head>\n <Table.Row>\n {columns.map((column) => (\n <Table.HeaderCell\n key={column.id}\n align={column.align}\n numeric={column.numeric}\n width={column.width}\n hideBelow={column.hideBelow}\n hideAbove={column.hideAbove}\n >\n {column.header}\n </Table.HeaderCell>\n ))}\n </Table.Row>\n </Table.Head>\n <Table.Body ref={body} onKeyDown={onKeyDown} onFocus={onFocus}>\n {nodes.map((node) => (\n <Table.Row\n key={node.id}\n ref={(element) => {\n if (element) elements.current.set(node.id, element)\n else elements.current.delete(node.id)\n }}\n className={styles.row}\n interactive\n tabIndex={node.id === tabId ? 0 : -1}\n aria-level={node.level}\n aria-setsize={node.setSize}\n aria-posinset={node.posInSet}\n aria-expanded={node.expandable ? node.open : undefined}\n >\n {columns.map((column, columnIndex) => {\n return (\n <Table.Cell\n key={column.id}\n rowHeader={columnIndex === 0}\n align={column.align}\n numeric={column.numeric}\n hideBelow={column.hideBelow}\n hideAbove={column.hideAbove}\n >\n {columnIndex === 0 ? (\n <span\n className={styles.treeCellContent}\n style={{ '--_level': node.level - 1 } as CSSProperties}\n >\n <button\n type=\"button\"\n className={styles.toggle}\n tabIndex={-1}\n aria-hidden=\"true\"\n data-leaf={node.expandable ? undefined : ''}\n onClick={() => {\n moveTo(node.id)\n setExpanded(node.id, !node.open)\n }}\n >\n <ChevronRightIcon className={styles.chevron} />\n </button>\n {renderCell(node.row, column)}\n </span>\n ) : (\n renderCell(node.row, column)\n )}\n </Table.Cell>\n )\n })}\n </Table.Row>\n ))}\n </Table.Body>\n </Table>\n )\n}\n\n/**\n * A table of rows in a hierarchy, with a toggle on each parent and keyboard navigation like a file\n * tree.\n *\n * @remarks\n * `TreeTable` is a WAI-ARIA `treegrid` set in `Table`'s styles. Hand it the top-level `rows`, how to\n * find a row's `children`, and `renderCell`; it handles the indent, the toggles, and the\n * `aria-level`, `aria-expanded`, `aria-setsize` and `aria-posinset` on every row.\n *\n * ## Keyboard\n *\n * The grid is one tab stop, and the arrow keys move between rows: Up and Down step by row, Right\n * opens a closed row and then steps into its first child, Left closes an open row and then steps\n * out to its parent, Home and End jump to the first and last visible row, and Enter follows the\n * first link in the row. Links and buttons in a row's cells are reached by Enter, not Tab, so put\n * the row's main link in its first column.\n *\n * ## Open rows\n *\n * `expanded` holds the ids of the open rows, as `getRowId` returns them. Pass it with\n * `onExpandedChange` to control them, for example to keep them in the URL, or `defaultExpanded` to\n * choose which start open. For children that load when a row opens, return `true` from\n * `hasChildren` and give the row a placeholder child while they load.\n *\n * @privateRemarks\n * A separate export, not `Table.Tree`, so a plain `import { Table }` never carries the key handling\n * (docs/adr/0044). Ref and native props go to the `<table>`.\n */\nexport const TreeTable = forwardRef(TreeTableInner) as <T>(\n props: TreeTableProps<T> & RefAttributes<HTMLTableElement>,\n) => ReactElement\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/components/display/TreeTable/TreeTable.module.css
|
|
2
|
+
var e = "kiln-TreeTable__treeTable", t = "kiln-TreeTable__row", n = "kiln-TreeTable__treeCellContent", r = "kiln-TreeTable__toggle", i = "kiln-TreeTable__chevron", a = {
|
|
3
|
+
treeTable: e,
|
|
4
|
+
row: t,
|
|
5
|
+
treeCellContent: n,
|
|
6
|
+
toggle: r,
|
|
7
|
+
chevron: i
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { i as chevron, a as default, t as row, r as toggle, n as treeCellContent, e as treeTable };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=TreeTable.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TreeTable.module.js","names":["treeTable","row","treeCellContent","toggle","chevron","TreeTable_module_default"],"sources":["../../../../src/components/display/TreeTable/TreeTable.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --tree-table-indent default var(--space-5) — how far each level steps in\n *\n * The table itself is styled by Table; this file adds the indent, the toggle and the row focus.\n */\n.treeTable {\n --_indent: var(--tree-table-indent, var(--space-5));\n}\n\n.row:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: -2px;\n}\n\n.treeCellContent {\n --_level: 0;\n margin-inline-start: calc(var(--_level) * var(--_indent));\n display: inline-flex;\n align-items: baseline;\n gap: var(--space-1);\n min-inline-size: 0;\n}\n\n.toggle {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n inline-size: 1.25rem;\n block-size: 1.25rem;\n padding: 0;\n border: 0;\n background: none;\n color: var(--color-ink-muted);\n cursor: pointer;\n align-self: center;\n}\n\n.toggle[data-leaf] {\n visibility: hidden;\n}\n\n.chevron {\n inline-size: 1rem;\n block-size: 1rem;\n transition: transform var(--dur-3) var(--ease-out);\n}\n\n.row[aria-expanded='true'] .chevron {\n transform: rotate(90deg);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .chevron {\n transition: none;\n }\n}\n"],"mappings":""}
|
package/dist/index.d.ts
CHANGED
|
@@ -197,6 +197,8 @@ export type { DataListProps, DataListItemProps, DataListOrientation, } from './c
|
|
|
197
197
|
export { List } from './components/display/List/index.js';
|
|
198
198
|
export type { ListProps, ListItemProps, ListDensity, ListElement, ListSurface, } from './components/display/List/index.js';
|
|
199
199
|
export { Table } from './components/display/Table/index.js';
|
|
200
|
+
export { TreeTable } from './components/display/TreeTable/index.js';
|
|
201
|
+
export type { TreeTableProps, TreeTableColumn } from './components/display/TreeTable/index.js';
|
|
200
202
|
export type { TableProps, TableDensity, TableVariant, TableSurface, TableAlign, TableVerticalAlign, TableSort, TableRowProps, TableHeaderCellProps, TableCellProps, TableColumnWidth, } from './components/display/Table/index.js';
|
|
201
203
|
export { Media } from './components/display/Media/index.js';
|
|
202
204
|
export type { MediaProps, MediaRatio, MediaFit, MediaRadius, MediaSize, } from './components/display/Media/index.js';
|
package/dist/index.js
CHANGED
|
@@ -111,33 +111,34 @@ import { Stat as Xt } from "./components/display/Stat/Stat.js";
|
|
|
111
111
|
import { DataList as Zt } from "./components/display/DataList/DataList.js";
|
|
112
112
|
import { List as Qt } from "./components/display/List/List.js";
|
|
113
113
|
import { Table as $t } from "./components/display/Table/Table.js";
|
|
114
|
-
import {
|
|
115
|
-
import {
|
|
116
|
-
import {
|
|
117
|
-
import {
|
|
118
|
-
import {
|
|
119
|
-
import {
|
|
120
|
-
import {
|
|
121
|
-
import {
|
|
122
|
-
import {
|
|
123
|
-
import {
|
|
124
|
-
import {
|
|
125
|
-
import {
|
|
126
|
-
import {
|
|
127
|
-
import {
|
|
128
|
-
import {
|
|
129
|
-
import {
|
|
130
|
-
import {
|
|
131
|
-
import {
|
|
132
|
-
import {
|
|
133
|
-
import {
|
|
134
|
-
import {
|
|
135
|
-
import {
|
|
136
|
-
import {
|
|
137
|
-
import {
|
|
138
|
-
import {
|
|
139
|
-
import {
|
|
140
|
-
import {
|
|
141
|
-
import {
|
|
142
|
-
import {
|
|
143
|
-
|
|
114
|
+
import { TreeTable as en } from "./components/display/TreeTable/TreeTable.js";
|
|
115
|
+
import { Media as tn } from "./components/display/Media/Media.js";
|
|
116
|
+
import { Marquee as nn } from "./components/display/Marquee/Marquee.js";
|
|
117
|
+
import { CodeBlock as rn } from "./components/display/CodeBlock/CodeBlock.js";
|
|
118
|
+
import { Stamp as an } from "./components/display/Stamp/Stamp.js";
|
|
119
|
+
import { Progress as on } from "./components/display/Progress/Progress.js";
|
|
120
|
+
import { meterTone as sn } from "./components/display/Meter/meterTone.js";
|
|
121
|
+
import { Meter as cn } from "./components/display/Meter/Meter.js";
|
|
122
|
+
import { Skeleton as ln } from "./components/display/Skeleton/Skeleton.js";
|
|
123
|
+
import { EmptyState as un } from "./components/display/EmptyState/EmptyState.js";
|
|
124
|
+
import { Tabs as dn } from "./components/navigation/Tabs/Tabs.js";
|
|
125
|
+
import { NavLinks as fn } from "./components/navigation/NavLinks/NavLinks.js";
|
|
126
|
+
import { NavLinksGroup as pn } from "./components/navigation/NavLinks/NavLinksGroup.js";
|
|
127
|
+
import { BottomNav as mn } from "./components/navigation/BottomNav/BottomNav.js";
|
|
128
|
+
import { Accordion as hn } from "./components/navigation/Accordion/Accordion.js";
|
|
129
|
+
import { Collapsible as gn } from "./components/navigation/Collapsible/Collapsible.js";
|
|
130
|
+
import { Stepper as _n } from "./components/navigation/Stepper/Stepper.js";
|
|
131
|
+
import { Alert as vn } from "./components/feedback/Alert/Alert.js";
|
|
132
|
+
import { toast as yn } from "./components/feedback/Toaster/toast.js";
|
|
133
|
+
import { Toaster as bn } from "./components/feedback/Toaster/Toaster.js";
|
|
134
|
+
import { LiveIndicator as xn } from "./components/feedback/LiveIndicator/LiveIndicator.js";
|
|
135
|
+
import { Flash as Sn } from "./components/feedback/Flash/Flash.js";
|
|
136
|
+
import { StatusDot as Cn } from "./components/feedback/StatusDot/StatusDot.js";
|
|
137
|
+
import { Indicator as wn } from "./components/feedback/Indicator/Indicator.js";
|
|
138
|
+
import { formatAbsoluteTime as Tn, formatRelativeTime as En } from "./components/feedback/RelativeTime/format.js";
|
|
139
|
+
import { RelativeTime as Dn } from "./components/feedback/RelativeTime/RelativeTime.js";
|
|
140
|
+
import { Dialog as On } from "./components/overlays/Dialog/Dialog.js";
|
|
141
|
+
import { Sheet as kn } from "./components/overlays/Sheet/Sheet.js";
|
|
142
|
+
import { DropdownMenu as An } from "./components/overlays/DropdownMenu/DropdownMenu.js";
|
|
143
|
+
import { CommandPalette as jn } from "./components/overlays/CommandPalette/CommandPalette.js";
|
|
144
|
+
export { hn as Accordion, oe as ActionBar, vn as Alert, Se as Amount, et as AmountField, $e as AmountInput, ie as AppShell, u as ArrowLeftIcon, d as ArrowRightIcon, f as ArrowUpRightIcon, ne as AspectRatio, qt as Avatar, Jt as AvatarGroup, Wt as Badge, mn as BottomNav, X as Box, Te as Button, ct as Calendar, p as CalendarIcon, Ut as Card, m as CheckIcon, Ve as Checkbox, Je as CheckboxField, gt as CheckboxGroup, _t as CheckboxGroupField, h as ChevronDownIcon, g as ChevronLeftIcon, _ as ChevronRightIcon, v as ChevronUpIcon, Pe as ChipGroup, bt as ChipGroupField, xt as ChoiceCards, St as ChoiceCardsField, y as CircleCheckIcon, b as ClockIcon, x as CloseIcon, me as Code, rn as CodeBlock, se as Collapse, gn as Collapsible, it as ColorField, rt as ColorInput, jt as Combobox, Mt as ComboboxField, jn as CommandPalette, $ as Container, Ae as CopyButton, S as CopyIcon, ue as DEFAULT_HEADING_SIZE, e as DEFAULT_THEME, Zt as DataList, pt as DateField, lt as DateInput, ot as DateRangeField, ft as DateRangePickerField, ht as DateTimeField, dt as DateTimeInput, Yt as Delta, On as Dialog, te as Divider, An as DropdownMenu, un as EmptyState, C as ErrorIcon, w as EyeIcon, T as EyeOffIcon, Le as Field, We as Fieldset, Ft as FileDrop, It as FileField, Sn as Flash, Z as Grid, de as Heading, E as HelpIcon, Ee as IconButton, wn as Indicator, D as InfoIcon, Y as Inline, Re as Input, Vt as JsonField, zt as JsonInput, he as Kbd, pe as Link, Qt as List, xn as LiveIndicator, t as MODES, nn as Marquee, tn as Media, O as MenuIcon, k as MessageIcon, cn as Meter, A as MinusIcon, Ie as ModeToggle, j as MoonIcon, M as MoreIcon, fn as NavLinks, pn as NavLinksGroup, Qe as NumberField, Ze as NumberInput, xe as Numeral, nt as OneTimeCodeField, tt as OneTimeCodeInput, Xe as PasswordField, Ye as PasswordInput, N as PlusIcon, st as Popover, on as Progress, ge as Prose, _e as Quote, He as RadioGroup, vt as RadioGroupField, P as RadioIcon, Tt as RangeSlider, Et as RangeSliderField, Dt as Rating, Ot as RatingField, Dn as RelativeTime, ae as ScrollArea, F as SearchIcon, ee as Section, Ce as SectionHeader, Fe as SegmentedControl, yt as SegmentedField, Be as Select, qe as SelectField, kn as Sheet, I as ShieldIcon, le as ShowMore, ln as Skeleton, Ct as Slider, wt as SliderField, we as Spinner, Q as Split, J as Stack, an as Stamp, L as StarIcon, Xt as Stat, Cn as StatusDot, _n as Stepper, R as SunIcon, je as Swatch, Ue as Switch, at as SwitchField, z as SwordsIcon, B as SystemIcon, n as THEMES, r as THEME_META, $t as Table, dn as Tabs, kt as Tag, At as TagList, Pt as TagsField, Nt as TagsInput, V as TargetIcon, fe as Text, Ge as TextField, ze as Textarea, Ke as TextareaField, o as ThemeProvider, s as ThemeScope, mt as TimeField, ut as TimeInput, bn as Toaster, Me as ToggleChip, De as Tooltip, Oe as TooltipProvider, ce as Transition, en as TreeTable, H as TrendingUpIcon, U as TrophyIcon, W as UploadIcon, G as UsersIcon, re as VisuallyHidden, K as WarningIcon, q as ZapIcon, Gt as avatarColor, l as createIcon, c as cx, Ht as describeJsonError, Tn as formatAbsoluteTime, Lt as formatJson, ve as formatNumeral, ye as formatNumeralParts, En as formatRelativeTime, Kt as getInitials, sn as meterTone, Rt as minifyJson, be as signOf, a as themeScript, yn as toast, ke as useClipboard, Ne as useFieldControl, i as useTheme, Bt as validateJson };
|