@tensor_1/react 2.10.0 → 2.12.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/README.md +10 -1
- package/dist/ChartCore-Bt1C84ty.d.cts +22 -0
- package/dist/chart-preset.cjs +168 -0
- package/dist/chart-preset.d.cts +9 -0
- package/dist/chart-preset.d.ts +9 -0
- package/dist/chart-preset.js +35 -0
- package/dist/chart.cjs +58 -41
- package/dist/chart.d.cts +6 -21
- package/dist/chart.d.ts +2 -1
- package/dist/components/Alert.js +7 -15
- package/dist/components/AppShell.d.ts +1 -3
- package/dist/components/AppShell.js +1 -3
- package/dist/components/Avatar.js +1 -2
- package/dist/components/Button.js +16 -32
- package/dist/components/Card.d.ts +1 -3
- package/dist/components/Card.js +2 -2
- package/dist/components/Chart.d.ts +6 -21
- package/dist/components/Chart.js +5 -85
- package/dist/components/ChartCore.d.ts +33 -0
- package/dist/components/ChartCore.js +100 -0
- package/dist/components/CommandPalette.d.ts +2 -4
- package/dist/components/CommandPalette.js +13 -66
- package/dist/components/Comments.d.ts +1 -5
- package/dist/components/Comments.js +1 -1
- package/dist/components/Composer.d.ts +1 -8
- package/dist/components/Composer.js +2 -7
- package/dist/components/DetailLayout.js +1 -1
- package/dist/components/Drawer.d.ts +4 -3
- package/dist/components/Drawer.js +4 -6
- package/dist/components/Field.js +3 -6
- package/dist/components/Inbox.d.ts +3 -13
- package/dist/components/Inbox.js +3 -62
- package/dist/components/Input.d.ts +2 -13
- package/dist/components/Input.js +5 -20
- package/dist/components/Kanban.d.ts +3 -15
- package/dist/components/Kanban.js +10 -42
- package/dist/components/Meter.d.ts +1 -3
- package/dist/components/Meter.js +1 -10
- package/dist/components/Modal.d.ts +5 -3
- package/dist/components/Modal.js +4 -6
- package/dist/components/NavFavourites.js +35 -119
- package/dist/components/PageHeader.d.ts +1 -3
- package/dist/components/PageHeader.js +2 -2
- package/dist/components/Peek.d.ts +4 -3
- package/dist/components/Peek.js +6 -9
- package/dist/components/Progress.d.ts +1 -3
- package/dist/components/Progress.js +3 -5
- package/dist/components/Radio.d.ts +1 -3
- package/dist/components/Radio.js +2 -6
- package/dist/components/Segmented.js +3 -2
- package/dist/components/Sidebar.js +2 -4
- package/dist/components/Spinner.d.ts +1 -3
- package/dist/components/Spinner.js +3 -3
- package/dist/components/Stat.js +1 -5
- package/dist/components/States.d.ts +1 -3
- package/dist/components/States.js +2 -8
- package/dist/components/Table.d.ts +2 -9
- package/dist/components/Table.js +8 -11
- package/dist/components/Tabs.d.ts +1 -13
- package/dist/components/Tabs.js +4 -78
- package/dist/components/Timeline.d.ts +3 -3
- package/dist/components/Timeline.js +3 -3
- package/dist/components/Toast.js +5 -42
- package/dist/components/Tooltip.d.ts +1 -3
- package/dist/components/Tooltip.js +5 -44
- package/dist/components/Tree.d.ts +0 -2
- package/dist/components/Tree.js +4 -26
- package/dist/hooks/useDialog.d.ts +1 -1
- package/dist/hooks/useDialog.js +2 -17
- package/dist/hooks/useTable.d.ts +0 -5
- package/dist/hooks/useTable.js +12 -27
- package/dist/hooks/useVirtualRows.d.ts +27 -0
- package/dist/hooks/useVirtualRows.js +50 -0
- package/dist/index.cjs +291 -760
- package/dist/index.d.cts +61 -127
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -3
- package/dist/table-engine.js +8 -6
- package/dist/tree-model.d.ts +0 -4
- package/dist/tree-model.js +0 -8
- package/package.json +16 -2
package/dist/components/Tree.js
CHANGED
|
@@ -152,13 +152,6 @@ function Tree({
|
|
|
152
152
|
setFocusedId(id);
|
|
153
153
|
break;
|
|
154
154
|
default:
|
|
155
|
-
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
156
|
-
const act = treeNav(flat, id, e.key);
|
|
157
|
-
if (act.focusId !== void 0) {
|
|
158
|
-
e.preventDefault();
|
|
159
|
-
focusItem(rootRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(act.focusId)}"]`));
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
155
|
break;
|
|
163
156
|
}
|
|
164
157
|
};
|
|
@@ -195,10 +188,8 @@ function VirtualTreeView({
|
|
|
195
188
|
const scrollRef = useRef(null);
|
|
196
189
|
const [scrollTop, setScrollTop] = useState(0);
|
|
197
190
|
const pendingFocus = useRef(false);
|
|
198
|
-
const hadFocus = useRef(false);
|
|
199
191
|
const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
|
|
200
192
|
const rows = flat.slice(win.start, win.end);
|
|
201
|
-
const windowHasTabbable = tabbableId != null && rows.some((f) => f.node.id === tabbableId);
|
|
202
193
|
useEffect(() => {
|
|
203
194
|
if (!pendingFocus.current || focusedId == null) return;
|
|
204
195
|
const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
|
|
@@ -207,11 +198,6 @@ function VirtualTreeView({
|
|
|
207
198
|
pendingFocus.current = false;
|
|
208
199
|
}
|
|
209
200
|
}, [focusedId, win.start, win.end]);
|
|
210
|
-
useEffect(() => {
|
|
211
|
-
if (!hadFocus.current || pendingFocus.current) return;
|
|
212
|
-
const active = document.activeElement;
|
|
213
|
-
if (active === document.body || active == null) scrollRef.current?.focus({ preventScroll: true });
|
|
214
|
-
});
|
|
215
201
|
const moveTo = (id) => {
|
|
216
202
|
const i = flat.findIndex((f) => f.node.id === id);
|
|
217
203
|
if (i < 0) return;
|
|
@@ -227,7 +213,6 @@ function VirtualTreeView({
|
|
|
227
213
|
setFocusedId(id);
|
|
228
214
|
};
|
|
229
215
|
const onKeyDown = (e) => {
|
|
230
|
-
if (e.key.length === 1 && (e.ctrlKey || e.metaKey || e.altKey)) return;
|
|
231
216
|
const curId = e.target.closest('[role="treeitem"]')?.dataset.id ?? focusedId;
|
|
232
217
|
const act = treeNav(flat, curId, e.key);
|
|
233
218
|
if (act.focusId === void 0 && act.toggleId === void 0 && act.selectId === void 0) return;
|
|
@@ -248,20 +233,13 @@ function VirtualTreeView({
|
|
|
248
233
|
ref: scrollRef,
|
|
249
234
|
className: cx("uix-tree", "uix-tree--virtual", className),
|
|
250
235
|
role: "tree",
|
|
251
|
-
tabIndex: -1,
|
|
252
236
|
style: { maxHeight, overflow: "auto" },
|
|
253
237
|
onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
|
|
254
238
|
onKeyDown,
|
|
255
|
-
onFocus: () => {
|
|
256
|
-
hadFocus.current = true;
|
|
257
|
-
},
|
|
258
|
-
onBlur: (e) => {
|
|
259
|
-
if (!e.currentTarget.contains(e.relatedTarget)) hadFocus.current = false;
|
|
260
|
-
},
|
|
261
239
|
...props,
|
|
262
240
|
children: [
|
|
263
|
-
win.padTop > 0 && /* @__PURE__ */ jsx("li", {
|
|
264
|
-
rows.map((f
|
|
241
|
+
win.padTop > 0 && /* @__PURE__ */ jsx("li", { "aria-hidden": "true", style: { height: win.padTop } }),
|
|
242
|
+
rows.map((f) => {
|
|
265
243
|
const rowStyle = { height: rowHeight, paddingLeft: `calc(${f.level - 1} * var(--uix-space-5))` };
|
|
266
244
|
return /* @__PURE__ */ jsx(
|
|
267
245
|
"li",
|
|
@@ -273,7 +251,7 @@ function VirtualTreeView({
|
|
|
273
251
|
"aria-expanded": f.hasChildren ? f.isExpanded : void 0,
|
|
274
252
|
"aria-selected": selectable ? selected === f.node.id : void 0,
|
|
275
253
|
"data-id": f.node.id,
|
|
276
|
-
tabIndex: tabbableId === f.node.id
|
|
254
|
+
tabIndex: tabbableId === f.node.id ? 0 : -1,
|
|
277
255
|
className: "uix-tree__item",
|
|
278
256
|
onFocus: (e) => {
|
|
279
257
|
if (e.target === e.currentTarget) setFocusedId(f.node.id);
|
|
@@ -293,7 +271,7 @@ function VirtualTreeView({
|
|
|
293
271
|
f.node.id
|
|
294
272
|
);
|
|
295
273
|
}),
|
|
296
|
-
win.padBottom > 0 && /* @__PURE__ */ jsx("li", {
|
|
274
|
+
win.padBottom > 0 && /* @__PURE__ */ jsx("li", { "aria-hidden": "true", style: { height: win.padBottom } })
|
|
297
275
|
]
|
|
298
276
|
}
|
|
299
277
|
);
|
package/dist/hooks/useDialog.js
CHANGED
|
@@ -30,10 +30,8 @@ function unlockBodyScroll() {
|
|
|
30
30
|
saved = null;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
function useDialog(open
|
|
33
|
+
function useDialog(open) {
|
|
34
34
|
const ref = useRef(null);
|
|
35
|
-
const onCloseRef = useRef(onClose);
|
|
36
|
-
onCloseRef.current = onClose;
|
|
37
35
|
useEffect(() => {
|
|
38
36
|
const el = ref.current;
|
|
39
37
|
if (!el || !open) {
|
|
@@ -42,21 +40,8 @@ function useDialog(open, onClose) {
|
|
|
42
40
|
}
|
|
43
41
|
if (!el.open) el.showModal();
|
|
44
42
|
lockBodyScroll();
|
|
45
|
-
let locked = true;
|
|
46
|
-
const handleClose = () => {
|
|
47
|
-
if (locked) {
|
|
48
|
-
locked = false;
|
|
49
|
-
unlockBodyScroll();
|
|
50
|
-
}
|
|
51
|
-
onCloseRef.current?.();
|
|
52
|
-
};
|
|
53
|
-
el.addEventListener("close", handleClose);
|
|
54
43
|
return () => {
|
|
55
|
-
|
|
56
|
-
if (locked) {
|
|
57
|
-
locked = false;
|
|
58
|
-
unlockBodyScroll();
|
|
59
|
-
}
|
|
44
|
+
unlockBodyScroll();
|
|
60
45
|
if (el.open) el.close();
|
|
61
46
|
};
|
|
62
47
|
}, [open]);
|
package/dist/hooks/useTable.d.ts
CHANGED
|
@@ -21,11 +21,6 @@ interface UseTableResult<T> {
|
|
|
21
21
|
pinned: ReadonlySet<string>;
|
|
22
22
|
/** header select-all tri-state over the current derived rows. */
|
|
23
23
|
selectAll: SelectAllState;
|
|
24
|
-
/**
|
|
25
|
-
* Screen-reader status text reflecting the latest change — "Sorted by {field}, {dir}",
|
|
26
|
-
* "{n} results" (filter/search), "{n} selected". Pass to `<TableWrap announcement>` (UIX-A11Y-2).
|
|
27
|
-
*/
|
|
28
|
-
announcement: string;
|
|
29
24
|
/** cycle a column's sort; pass additive (⇧-click) to keep it as a secondary key. */
|
|
30
25
|
toggleSort: (field: string, additive?: boolean) => void;
|
|
31
26
|
setSort: (keys: SortKey[]) => void;
|
package/dist/hooks/useTable.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useCallback,
|
|
2
|
+
import { useCallback, useMemo, useState } from "react";
|
|
3
3
|
import {
|
|
4
4
|
multiSort,
|
|
5
5
|
toggleSort,
|
|
@@ -19,39 +19,25 @@ function useTable(data, options = {}) {
|
|
|
19
19
|
const [query, setQuery] = useState(() => options.initialQuery ?? "");
|
|
20
20
|
const [selected, setSelected] = useState(() => /* @__PURE__ */ new Set());
|
|
21
21
|
const [pinned, setPinned] = useState(() => new Set(options.initialPinned ?? []));
|
|
22
|
-
const
|
|
23
|
-
const filtered = applyFilters(data, filters);
|
|
24
|
-
const searched = searchRows(filtered, query, searchFields);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
const visibleRows = useMemo(() => {
|
|
23
|
+
const filtered = filters.length ? applyFilters(data, filters) : data;
|
|
24
|
+
const searched = query.trim() ? searchRows(filtered, query, searchFields) : filtered;
|
|
25
|
+
return sort.length ? multiSort(searched, sort) : searched;
|
|
26
|
+
}, [data, filters, query, searchFields, sort]);
|
|
27
|
+
const rows = useMemo(
|
|
28
|
+
() => pinned.size ? mergePinned(data, visibleRows, pinned, idField) : visibleRows.slice(),
|
|
29
|
+
[data, visibleRows, pinned, idField]
|
|
30
|
+
);
|
|
28
31
|
const pageIds = useMemo(() => rows.map((r) => String(r[idField])), [rows, idField]);
|
|
29
32
|
const selectAll = selectAllState(selected, pageIds);
|
|
30
33
|
const doToggleSort = useCallback((field, additive = false) => {
|
|
31
34
|
setSort((keys) => toggleSort(keys, field, additive));
|
|
32
35
|
}, []);
|
|
33
36
|
const toggleRow = useCallback((id) => setSelected((s) => toggleId(s, id)), []);
|
|
37
|
+
const isSelected = useCallback((id) => selected.has(id), [selected]);
|
|
34
38
|
const toggleAllRows = useCallback(() => setSelected((s) => togglePage(s, pageIds)), [pageIds]);
|
|
35
39
|
const clearSelection = useCallback(() => setSelected(/* @__PURE__ */ new Set()), []);
|
|
36
40
|
const togglePin = useCallback((id) => setPinned((s) => toggleId(s, id)), []);
|
|
37
|
-
const [announcement, setAnnouncement] = useState("");
|
|
38
|
-
const mounted = useRef(false);
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (!mounted.current) return;
|
|
41
|
-
const key = sort[0];
|
|
42
|
-
setAnnouncement(key ? `Sorted by ${key.field}, ${key.dir}` : "Sort cleared");
|
|
43
|
-
}, [sort]);
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
if (!mounted.current) return;
|
|
46
|
-
setAnnouncement(`${rows.length} result${rows.length === 1 ? "" : "s"}`);
|
|
47
|
-
}, [filters, query]);
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (!mounted.current) return;
|
|
50
|
-
setAnnouncement(`${selected.size} selected`);
|
|
51
|
-
}, [selected]);
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
mounted.current = true;
|
|
54
|
-
}, []);
|
|
55
41
|
const view = useMemo(() => ({ sort, filters, q: query }), [sort, filters, query]);
|
|
56
42
|
const viewQueryString = useMemo(() => serializeView(view), [view]);
|
|
57
43
|
const applyView = useCallback((queryString) => {
|
|
@@ -68,12 +54,11 @@ function useTable(data, options = {}) {
|
|
|
68
54
|
selected,
|
|
69
55
|
pinned,
|
|
70
56
|
selectAll,
|
|
71
|
-
announcement,
|
|
72
57
|
toggleSort: doToggleSort,
|
|
73
58
|
setSort,
|
|
74
59
|
setFilters,
|
|
75
60
|
setQuery,
|
|
76
|
-
isSelected
|
|
61
|
+
isSelected,
|
|
77
62
|
toggleRow,
|
|
78
63
|
toggleAllRows,
|
|
79
64
|
clearSelection,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
|
|
3
|
+
interface UseVirtualRowsOptions {
|
|
4
|
+
/** Fixed rendered row height in pixels. */
|
|
5
|
+
rowHeight: number;
|
|
6
|
+
/** Rows rendered beyond each viewport edge. Default: 6. */
|
|
7
|
+
overscan?: number;
|
|
8
|
+
/** Render all rows at or below this count. Default: 100. */
|
|
9
|
+
threshold?: number;
|
|
10
|
+
}
|
|
11
|
+
interface UseVirtualRowsResult<T> {
|
|
12
|
+
/** Attach to the scrolling TableWrap. */
|
|
13
|
+
containerRef: RefObject<HTMLDivElement>;
|
|
14
|
+
rows: readonly T[];
|
|
15
|
+
startIndex: number;
|
|
16
|
+
padTop: number;
|
|
17
|
+
padBottom: number;
|
|
18
|
+
totalHeight: number;
|
|
19
|
+
virtualized: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Owns scroll/resize observation and exposes the small visible row window.
|
|
23
|
+
* Consumers render padTop/padBottom spacer rows inside their existing tbody.
|
|
24
|
+
*/
|
|
25
|
+
declare function useVirtualRows<T>(rows: readonly T[], { rowHeight, overscan, threshold }: UseVirtualRowsOptions): UseVirtualRowsResult<T>;
|
|
26
|
+
|
|
27
|
+
export { type UseVirtualRowsOptions, type UseVirtualRowsResult, useVirtualRows };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { shouldVirtualize, virtualWindow } from "../table-engine.js";
|
|
4
|
+
function useVirtualRows(rows, { rowHeight, overscan = 6, threshold = 100 }) {
|
|
5
|
+
const containerRef = useRef(null);
|
|
6
|
+
const [viewport, setViewport] = useState({ scrollTop: 0, height: 0 });
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
const element = containerRef.current;
|
|
9
|
+
if (!element) return;
|
|
10
|
+
let frame = 0;
|
|
11
|
+
const commitMeasurement = () => {
|
|
12
|
+
const next = { scrollTop: element.scrollTop, height: element.clientHeight };
|
|
13
|
+
setViewport((current) => current.scrollTop === next.scrollTop && current.height === next.height ? current : next);
|
|
14
|
+
};
|
|
15
|
+
const measure = () => {
|
|
16
|
+
if (frame) return;
|
|
17
|
+
frame = requestAnimationFrame(() => {
|
|
18
|
+
frame = 0;
|
|
19
|
+
commitMeasurement();
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
commitMeasurement();
|
|
23
|
+
element.addEventListener("scroll", measure, { passive: true });
|
|
24
|
+
const observer = new ResizeObserver(measure);
|
|
25
|
+
observer.observe(element);
|
|
26
|
+
return () => {
|
|
27
|
+
element.removeEventListener("scroll", measure);
|
|
28
|
+
observer.disconnect();
|
|
29
|
+
if (frame) cancelAnimationFrame(frame);
|
|
30
|
+
};
|
|
31
|
+
}, []);
|
|
32
|
+
const enabled = viewport.height > 0 && shouldVirtualize(rows.length, threshold);
|
|
33
|
+
const window = useMemo(
|
|
34
|
+
() => enabled ? virtualWindow(viewport.scrollTop, viewport.height, rowHeight, rows.length, overscan) : { start: 0, end: rows.length, padTop: 0, padBottom: 0, total: rows.length * rowHeight },
|
|
35
|
+
[enabled, overscan, rowHeight, rows.length, viewport.height, viewport.scrollTop]
|
|
36
|
+
);
|
|
37
|
+
const visibleRows = useMemo(() => rows.slice(window.start, window.end), [rows, window.start, window.end]);
|
|
38
|
+
return {
|
|
39
|
+
containerRef,
|
|
40
|
+
rows: visibleRows,
|
|
41
|
+
startIndex: window.start,
|
|
42
|
+
padTop: window.padTop,
|
|
43
|
+
padBottom: window.padBottom,
|
|
44
|
+
totalHeight: window.total,
|
|
45
|
+
virtualized: enabled
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
useVirtualRows
|
|
50
|
+
};
|