@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.
Files changed (81) hide show
  1. package/README.md +10 -1
  2. package/dist/ChartCore-Bt1C84ty.d.cts +22 -0
  3. package/dist/chart-preset.cjs +168 -0
  4. package/dist/chart-preset.d.cts +9 -0
  5. package/dist/chart-preset.d.ts +9 -0
  6. package/dist/chart-preset.js +35 -0
  7. package/dist/chart.cjs +58 -41
  8. package/dist/chart.d.cts +6 -21
  9. package/dist/chart.d.ts +2 -1
  10. package/dist/components/Alert.js +7 -15
  11. package/dist/components/AppShell.d.ts +1 -3
  12. package/dist/components/AppShell.js +1 -3
  13. package/dist/components/Avatar.js +1 -2
  14. package/dist/components/Button.js +16 -32
  15. package/dist/components/Card.d.ts +1 -3
  16. package/dist/components/Card.js +2 -2
  17. package/dist/components/Chart.d.ts +6 -21
  18. package/dist/components/Chart.js +5 -85
  19. package/dist/components/ChartCore.d.ts +33 -0
  20. package/dist/components/ChartCore.js +100 -0
  21. package/dist/components/CommandPalette.d.ts +2 -4
  22. package/dist/components/CommandPalette.js +13 -66
  23. package/dist/components/Comments.d.ts +1 -5
  24. package/dist/components/Comments.js +1 -1
  25. package/dist/components/Composer.d.ts +1 -8
  26. package/dist/components/Composer.js +2 -7
  27. package/dist/components/DetailLayout.js +1 -1
  28. package/dist/components/Drawer.d.ts +4 -3
  29. package/dist/components/Drawer.js +4 -6
  30. package/dist/components/Field.js +3 -6
  31. package/dist/components/Inbox.d.ts +3 -13
  32. package/dist/components/Inbox.js +3 -62
  33. package/dist/components/Input.d.ts +2 -13
  34. package/dist/components/Input.js +5 -20
  35. package/dist/components/Kanban.d.ts +3 -15
  36. package/dist/components/Kanban.js +10 -42
  37. package/dist/components/Meter.d.ts +1 -3
  38. package/dist/components/Meter.js +1 -10
  39. package/dist/components/Modal.d.ts +5 -3
  40. package/dist/components/Modal.js +4 -6
  41. package/dist/components/NavFavourites.js +35 -119
  42. package/dist/components/PageHeader.d.ts +1 -3
  43. package/dist/components/PageHeader.js +2 -2
  44. package/dist/components/Peek.d.ts +4 -3
  45. package/dist/components/Peek.js +6 -9
  46. package/dist/components/Progress.d.ts +1 -3
  47. package/dist/components/Progress.js +3 -5
  48. package/dist/components/Radio.d.ts +1 -3
  49. package/dist/components/Radio.js +2 -6
  50. package/dist/components/Segmented.js +3 -2
  51. package/dist/components/Sidebar.js +2 -4
  52. package/dist/components/Spinner.d.ts +1 -3
  53. package/dist/components/Spinner.js +3 -3
  54. package/dist/components/Stat.js +1 -5
  55. package/dist/components/States.d.ts +1 -3
  56. package/dist/components/States.js +2 -8
  57. package/dist/components/Table.d.ts +2 -9
  58. package/dist/components/Table.js +8 -11
  59. package/dist/components/Tabs.d.ts +1 -13
  60. package/dist/components/Tabs.js +4 -78
  61. package/dist/components/Timeline.d.ts +3 -3
  62. package/dist/components/Timeline.js +3 -3
  63. package/dist/components/Toast.js +5 -42
  64. package/dist/components/Tooltip.d.ts +1 -3
  65. package/dist/components/Tooltip.js +5 -44
  66. package/dist/components/Tree.d.ts +0 -2
  67. package/dist/components/Tree.js +4 -26
  68. package/dist/hooks/useDialog.d.ts +1 -1
  69. package/dist/hooks/useDialog.js +2 -17
  70. package/dist/hooks/useTable.d.ts +0 -5
  71. package/dist/hooks/useTable.js +12 -27
  72. package/dist/hooks/useVirtualRows.d.ts +27 -0
  73. package/dist/hooks/useVirtualRows.js +50 -0
  74. package/dist/index.cjs +291 -760
  75. package/dist/index.d.cts +61 -127
  76. package/dist/index.d.ts +3 -2
  77. package/dist/index.js +4 -3
  78. package/dist/table-engine.js +8 -6
  79. package/dist/tree-model.d.ts +0 -4
  80. package/dist/tree-model.js +0 -8
  81. package/package.json +16 -2
@@ -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", { role: "presentation", "aria-hidden": "true", style: { height: win.padTop } }),
264
- rows.map((f, i) => {
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 || !windowHasTabbable && i === 0 ? 0 : -1,
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", { role: "presentation", "aria-hidden": "true", style: { height: win.padBottom } })
274
+ win.padBottom > 0 && /* @__PURE__ */ jsx("li", { "aria-hidden": "true", style: { height: win.padBottom } })
297
275
  ]
298
276
  }
299
277
  );
@@ -1,3 +1,3 @@
1
- declare function useDialog(open: boolean, onClose?: () => void): React.RefObject<HTMLDialogElement>;
1
+ declare function useDialog(open: boolean): React.RefObject<HTMLDialogElement>;
2
2
 
3
3
  export { useDialog };
@@ -30,10 +30,8 @@ function unlockBodyScroll() {
30
30
  saved = null;
31
31
  }
32
32
  }
33
- function useDialog(open, onClose) {
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
- el.removeEventListener("close", handleClose);
56
- if (locked) {
57
- locked = false;
58
- unlockBodyScroll();
59
- }
44
+ unlockBodyScroll();
60
45
  if (el.open) el.close();
61
46
  };
62
47
  }, [open]);
@@ -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;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
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 rows = useMemo(() => {
23
- const filtered = applyFilters(data, filters);
24
- const searched = searchRows(filtered, query, searchFields);
25
- const sorted = multiSort(searched, sort);
26
- return mergePinned(data, sorted, pinned, idField);
27
- }, [data, filters, query, searchFields, sort, pinned, idField]);
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: (id) => selected.has(id),
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
+ };