@tensor_1/react 2.5.0 → 2.6.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/AppShell.d.ts +16 -2
- package/dist/components/AppShell.js +39 -6
- package/dist/components/Composer.d.ts +21 -0
- package/dist/components/Composer.js +12 -0
- package/dist/components/Segmented.d.ts +29 -0
- package/dist/components/Segmented.js +36 -0
- package/dist/components/Table.d.ts +45 -4
- package/dist/components/Table.js +63 -3
- package/dist/components/Timeline.d.ts +7 -1
- package/dist/components/Timeline.js +2 -2
- package/dist/hooks/useTable.d.ts +50 -0
- package/dist/hooks/useTable.js +69 -0
- package/dist/index.cjs +505 -96
- package/dist/index.d.cts +244 -6
- package/dist/index.d.ts +6 -2
- package/dist/index.js +33 -2
- package/dist/table-engine.d.ts +89 -0
- package/dist/table-engine.js +185 -0
- package/package.json +3 -2
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
+
type ShellNav = 'full' | 'rail' | 'hidden';
|
|
4
5
|
interface AppShellProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
/** Sidebar width tier: 'full' (default) → 'rail' (icon only) → 'hidden' (0). */
|
|
7
|
+
nav?: ShellNav;
|
|
8
|
+
/** Back-compat alias for nav="rail". Ignored when `nav` is set. */
|
|
5
9
|
collapsed?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Immersive focus mode: hides the sidebar AND the topbar so a wide, many-column
|
|
12
|
+
* table uses the whole frame. Provide `onExitFocus` to enable Esc-to-exit and
|
|
13
|
+
* the floating exit control.
|
|
14
|
+
*/
|
|
15
|
+
focus?: boolean;
|
|
16
|
+
/** Called when the user presses Esc (or the exit control) while in focus mode. */
|
|
17
|
+
onExitFocus?: () => void;
|
|
18
|
+
/** Drop the content gutter so a genuinely wide table runs edge-to-edge. */
|
|
19
|
+
mainBleed?: boolean;
|
|
6
20
|
sidebar?: ReactNode;
|
|
7
21
|
topbar?: ReactNode;
|
|
8
22
|
children?: ReactNode;
|
|
9
23
|
}
|
|
10
|
-
declare function AppShell({ collapsed, sidebar, topbar, children, className, ...props }: AppShellProps): react.JSX.Element;
|
|
24
|
+
declare function AppShell({ nav, collapsed, focus, onExitFocus, mainBleed, sidebar, topbar, children, className, ...props }: AppShellProps): react.JSX.Element;
|
|
11
25
|
|
|
12
|
-
export { AppShell, type AppShellProps };
|
|
26
|
+
export { AppShell, type AppShellProps, type ShellNav };
|
|
@@ -1,11 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect } from "react";
|
|
2
4
|
import { cx } from "../cx.js";
|
|
3
|
-
function AppShell({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
function AppShell({
|
|
6
|
+
nav,
|
|
7
|
+
collapsed,
|
|
8
|
+
focus,
|
|
9
|
+
onExitFocus,
|
|
10
|
+
mainBleed,
|
|
11
|
+
sidebar,
|
|
12
|
+
topbar,
|
|
13
|
+
children,
|
|
14
|
+
className,
|
|
15
|
+
...props
|
|
16
|
+
}) {
|
|
17
|
+
const navState = nav ?? (collapsed ? "rail" : "full");
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!focus || !onExitFocus) return;
|
|
20
|
+
const onKey = (e) => {
|
|
21
|
+
if (e.key === "Escape") onExitFocus();
|
|
22
|
+
};
|
|
23
|
+
document.addEventListener("keydown", onKey);
|
|
24
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
25
|
+
}, [focus, onExitFocus]);
|
|
26
|
+
return /* @__PURE__ */ jsxs(
|
|
27
|
+
"div",
|
|
28
|
+
{
|
|
29
|
+
className: cx("uix-shell", className),
|
|
30
|
+
"data-nav": navState !== "full" ? navState : void 0,
|
|
31
|
+
"data-collapsed": collapsed && nav == null ? "" : void 0,
|
|
32
|
+
"data-focus": focus || void 0,
|
|
33
|
+
...props,
|
|
34
|
+
children: [
|
|
35
|
+
sidebar && /* @__PURE__ */ jsx("div", { className: "uix-shell__sidebar", children: sidebar }),
|
|
36
|
+
topbar && /* @__PURE__ */ jsx("div", { className: "uix-shell__topbar", children: topbar }),
|
|
37
|
+
/* @__PURE__ */ jsx("main", { className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
|
|
38
|
+
focus && onExitFocus && /* @__PURE__ */ jsx("div", { className: "uix-shell__focus-exit", children: /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: onExitFocus, children: "Esc \xB7 Exit focus" }) })
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
);
|
|
9
42
|
}
|
|
10
43
|
export {
|
|
11
44
|
AppShell
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface ComposerProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Composer surface over `.uix-composer` — a single box that owns the
|
|
9
|
+
* `:focus-within` accent ring. Drop a borderless `<textarea>` and a
|
|
10
|
+
* `<ComposerBar>` inside; the descendant `textarea` picks up the composer's
|
|
11
|
+
* padding/typography automatically, so the input never reads as a second
|
|
12
|
+
* nested box.
|
|
13
|
+
*/
|
|
14
|
+
declare function Composer({ children, className, ...props }: ComposerProps): react.JSX.Element;
|
|
15
|
+
interface ComposerBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
16
|
+
children?: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
/** Action footer for a `<Composer>` over `.uix-composer__bar` (top-bordered, right-aligned). */
|
|
19
|
+
declare function ComposerBar({ children, className, ...props }: ComposerBarProps): react.JSX.Element;
|
|
20
|
+
|
|
21
|
+
export { Composer, ComposerBar, type ComposerBarProps, type ComposerProps };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from "../cx.js";
|
|
3
|
+
function Composer({ children, className, ...props }) {
|
|
4
|
+
return /* @__PURE__ */ jsx("div", { className: cx("uix-composer", className), ...props, children });
|
|
5
|
+
}
|
|
6
|
+
function ComposerBar({ children, className, ...props }) {
|
|
7
|
+
return /* @__PURE__ */ jsx("div", { className: cx("uix-composer__bar", className), ...props, children });
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
Composer,
|
|
11
|
+
ComposerBar
|
|
12
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode, ButtonHTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
interface SegmentedProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
5
|
+
/** The currently-selected option value. */
|
|
6
|
+
value?: string;
|
|
7
|
+
/** Fires with the option's value when a different option is chosen. */
|
|
8
|
+
onChange?: (value: string) => void;
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Segmented single-select over `.uix-segmented` — the compact 2–3 way toggle
|
|
13
|
+
* (density, audience, view mode …) that reads as one pill with a lifted active
|
|
14
|
+
* segment. Selection state is shared with `<SegmentedOption>` via context, so
|
|
15
|
+
* the caller only wires `value` + `onChange` once.
|
|
16
|
+
*/
|
|
17
|
+
declare function Segmented({ value, onChange, children, className, ...props }: SegmentedProps): react.JSX.Element;
|
|
18
|
+
interface SegmentedOptionProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'value' | 'onChange'> {
|
|
19
|
+
value: string;
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* One option in a `<Segmented>`. Active state is derived from the parent's
|
|
24
|
+
* `value`; the pressed segment lifts to the surface via
|
|
25
|
+
* `.uix-segmented__option[aria-pressed="true"]`.
|
|
26
|
+
*/
|
|
27
|
+
declare function SegmentedOption({ value, children, className, onClick, ...props }: SegmentedOptionProps): react.JSX.Element;
|
|
28
|
+
|
|
29
|
+
export { Segmented, SegmentedOption, type SegmentedOptionProps, type SegmentedProps };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
import { cx } from "../cx.js";
|
|
5
|
+
const SegmentedCtx = createContext({});
|
|
6
|
+
function Segmented({ value, onChange, children, className, ...props }) {
|
|
7
|
+
return /* @__PURE__ */ jsx(SegmentedCtx.Provider, { value: { value, onChange }, children: /* @__PURE__ */ jsx("div", { role: "group", className: cx("uix-segmented", className), ...props, children }) });
|
|
8
|
+
}
|
|
9
|
+
function SegmentedOption({
|
|
10
|
+
value,
|
|
11
|
+
children,
|
|
12
|
+
className,
|
|
13
|
+
onClick,
|
|
14
|
+
...props
|
|
15
|
+
}) {
|
|
16
|
+
const ctx = useContext(SegmentedCtx);
|
|
17
|
+
const selected = ctx.value === value;
|
|
18
|
+
return /* @__PURE__ */ jsx(
|
|
19
|
+
"button",
|
|
20
|
+
{
|
|
21
|
+
type: "button",
|
|
22
|
+
className: cx("uix-segmented__option", className),
|
|
23
|
+
"aria-pressed": selected,
|
|
24
|
+
onClick: (e) => {
|
|
25
|
+
ctx.onChange?.(value);
|
|
26
|
+
onClick?.(e);
|
|
27
|
+
},
|
|
28
|
+
...props,
|
|
29
|
+
children
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
Segmented,
|
|
35
|
+
SegmentedOption
|
|
36
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { HTMLAttributes, ReactNode, ButtonHTMLAttributes, TdHTMLAttributes, TableHTMLAttributes, ThHTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
interface TableWrapProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
5
|
children?: ReactNode;
|
|
@@ -11,16 +11,20 @@ interface TableProps extends TableHTMLAttributes<HTMLTableElement> {
|
|
|
11
11
|
density?: TableDensity;
|
|
12
12
|
zebra?: boolean;
|
|
13
13
|
pinnedCol?: boolean;
|
|
14
|
+
/** table-layout: fixed — required for reliable column widths + primary-column truncation */
|
|
15
|
+
fixed?: boolean;
|
|
14
16
|
rowPeek?: boolean;
|
|
15
17
|
v2?: boolean;
|
|
16
18
|
}
|
|
17
|
-
declare function Table({ density, zebra, pinnedCol, rowPeek, v2, className, children, ...props }: TableProps): react.JSX.Element;
|
|
19
|
+
declare function Table({ density, zebra, pinnedCol, fixed, rowPeek, v2, className, children, ...props }: TableProps): react.JSX.Element;
|
|
18
20
|
interface ThProps extends ThHTMLAttributes<HTMLTableCellElement> {
|
|
19
21
|
sortable?: boolean;
|
|
20
22
|
sortDirection?: SortDirection;
|
|
23
|
+
/** 1-based ordinal shown when this column is a secondary key in a multi-sort */
|
|
24
|
+
sortOrder?: number;
|
|
21
25
|
onSort?: () => void;
|
|
22
26
|
}
|
|
23
|
-
declare function Th({ sortable, sortDirection, onSort, className, children, onClick, ...props }: ThProps): react.JSX.Element;
|
|
27
|
+
declare function Th({ sortable, sortDirection, sortOrder, onSort, className, children, onClick, ...props }: ThProps): react.JSX.Element;
|
|
24
28
|
interface TdProps extends TdHTMLAttributes<HTMLTableCellElement> {
|
|
25
29
|
pinned?: boolean;
|
|
26
30
|
}
|
|
@@ -30,5 +34,42 @@ interface TrProps extends HTMLAttributes<HTMLTableRowElement> {
|
|
|
30
34
|
pinned?: boolean;
|
|
31
35
|
}
|
|
32
36
|
declare function Tr({ selected, pinned, className, children, ...props }: TrProps): react.JSX.Element;
|
|
37
|
+
interface BulkBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
38
|
+
/** number of selected rows, rendered as "N selected" */
|
|
39
|
+
count?: number;
|
|
40
|
+
children?: ReactNode;
|
|
41
|
+
}
|
|
42
|
+
/** Contextual action bar shown above the table when ≥1 row is selected. */
|
|
43
|
+
declare function BulkBar({ count, children, className, ...props }: BulkBarProps): react.JSX.Element;
|
|
44
|
+
interface RowActionsProps extends TdHTMLAttributes<HTMLTableCellElement> {
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
}
|
|
47
|
+
/** Trailing cell that holds the row's hover actions (kebab, quick actions). */
|
|
48
|
+
declare function RowActions({ children, className, ...props }: RowActionsProps): react.JSX.Element;
|
|
49
|
+
interface RowActionProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
50
|
+
children?: ReactNode;
|
|
51
|
+
}
|
|
52
|
+
/** A single hover-revealed row action button (icon inside). */
|
|
53
|
+
declare function RowAction({ children, className, type, ...props }: RowActionProps): react.JSX.Element;
|
|
54
|
+
interface ExpandToggleProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
55
|
+
expanded?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/** Chevron button that expands/collapses an inline detail row. */
|
|
58
|
+
declare function ExpandToggle({ expanded, className, type, ...props }: ExpandToggleProps): react.JSX.Element;
|
|
59
|
+
/** Emphasised primary cell text. */
|
|
60
|
+
declare function CellStrong({ children, className, ...props }: HTMLAttributes<HTMLSpanElement>): react.JSX.Element;
|
|
61
|
+
/** Secondary line under a primary cell value (muted, meta size). */
|
|
62
|
+
declare function CellSub({ children, className, ...props }: HTMLAttributes<HTMLSpanElement>): react.JSX.Element;
|
|
63
|
+
interface MarkProps extends HTMLAttributes<HTMLElement> {
|
|
64
|
+
children?: ReactNode;
|
|
65
|
+
}
|
|
66
|
+
/** A search-match highlight span. */
|
|
67
|
+
declare function Mark({ children, className, ...props }: MarkProps): react.JSX.Element;
|
|
68
|
+
interface HighlightedProps {
|
|
69
|
+
text: string;
|
|
70
|
+
query: string;
|
|
71
|
+
}
|
|
72
|
+
/** Render `text` with case-insensitive matches of `query` wrapped in <mark>. */
|
|
73
|
+
declare function Highlighted({ text, query }: HighlightedProps): react.JSX.Element;
|
|
33
74
|
|
|
34
|
-
export { type SortDirection, Table, type TableDensity, type TableProps, TableWrap, type TableWrapProps, Td, type TdProps, Th, type ThProps, Tr, type TrProps };
|
|
75
|
+
export { BulkBar, type BulkBarProps, CellStrong, CellSub, ExpandToggle, type ExpandToggleProps, Highlighted, type HighlightedProps, Mark, type MarkProps, RowAction, type RowActionProps, RowActions, type RowActionsProps, type SortDirection, Table, type TableDensity, type TableProps, TableWrap, type TableWrapProps, Td, type TdProps, Th, type ThProps, Tr, type TrProps };
|
package/dist/components/Table.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from "../cx.js";
|
|
3
|
+
import { highlightSegments } from "../table-engine.js";
|
|
3
4
|
function TableWrap({ children, className, ...props }) {
|
|
4
5
|
return /* @__PURE__ */ jsx("div", { className: cx("uix-table-wrap", className), ...props, children });
|
|
5
6
|
}
|
|
@@ -7,6 +8,7 @@ function Table({
|
|
|
7
8
|
density = "standard",
|
|
8
9
|
zebra = true,
|
|
9
10
|
pinnedCol,
|
|
11
|
+
fixed,
|
|
10
12
|
rowPeek,
|
|
11
13
|
v2,
|
|
12
14
|
className,
|
|
@@ -16,7 +18,13 @@ function Table({
|
|
|
16
18
|
return /* @__PURE__ */ jsx(
|
|
17
19
|
"table",
|
|
18
20
|
{
|
|
19
|
-
className: cx(
|
|
21
|
+
className: cx(
|
|
22
|
+
"uix-table",
|
|
23
|
+
!zebra && "uix-table--no-zebra",
|
|
24
|
+
pinnedCol && "uix-table--pinned-col",
|
|
25
|
+
fixed && "uix-table--fixed",
|
|
26
|
+
className
|
|
27
|
+
),
|
|
20
28
|
"data-density": density !== "standard" ? density : void 0,
|
|
21
29
|
"data-uix-rowpeek": rowPeek || void 0,
|
|
22
30
|
"data-uix-table-v2": v2 || void 0,
|
|
@@ -25,12 +33,13 @@ function Table({
|
|
|
25
33
|
}
|
|
26
34
|
);
|
|
27
35
|
}
|
|
28
|
-
function Th({ sortable, sortDirection, onSort, className, children, onClick, ...props }) {
|
|
36
|
+
function Th({ sortable, sortDirection, sortOrder, onSort, className, children, onClick, ...props }) {
|
|
29
37
|
return /* @__PURE__ */ jsx(
|
|
30
38
|
"th",
|
|
31
39
|
{
|
|
32
40
|
className,
|
|
33
41
|
"data-sort": sortable || void 0,
|
|
42
|
+
"data-sort-order": sortOrder != null && sortOrder > 0 ? sortOrder : void 0,
|
|
34
43
|
"aria-sort": sortDirection && sortDirection !== "none" ? sortDirection : void 0,
|
|
35
44
|
onClick: sortable ? onSort ?? onClick : onClick,
|
|
36
45
|
...props,
|
|
@@ -53,7 +62,58 @@ function Tr({ selected, pinned, className, children, ...props }) {
|
|
|
53
62
|
}
|
|
54
63
|
);
|
|
55
64
|
}
|
|
65
|
+
function BulkBar({ count, children, className, ...props }) {
|
|
66
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("uix-bulkbar", className), role: "toolbar", "aria-label": "Bulk actions", ...props, children: [
|
|
67
|
+
count != null && /* @__PURE__ */ jsxs("span", { className: "uix-bulkbar__count", children: [
|
|
68
|
+
count,
|
|
69
|
+
" selected"
|
|
70
|
+
] }),
|
|
71
|
+
/* @__PURE__ */ jsx("span", { className: "uix-bulkbar__spacer" }),
|
|
72
|
+
children
|
|
73
|
+
] });
|
|
74
|
+
}
|
|
75
|
+
function RowActions({ children, className, ...props }) {
|
|
76
|
+
return /* @__PURE__ */ jsx("td", { className: cx("uix-table__actions", className), ...props, children });
|
|
77
|
+
}
|
|
78
|
+
function RowAction({ children, className, type = "button", ...props }) {
|
|
79
|
+
return /* @__PURE__ */ jsx("button", { type, className: cx("uix-rowact", className), ...props, children });
|
|
80
|
+
}
|
|
81
|
+
function ExpandToggle({ expanded, className, type = "button", ...props }) {
|
|
82
|
+
return /* @__PURE__ */ jsx(
|
|
83
|
+
"button",
|
|
84
|
+
{
|
|
85
|
+
type,
|
|
86
|
+
className: cx("uix-table__expand", className),
|
|
87
|
+
"aria-expanded": expanded,
|
|
88
|
+
"aria-label": expanded ? "Collapse row" : "Expand row",
|
|
89
|
+
...props,
|
|
90
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 4l4 4-4 4" }) })
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
function CellStrong({ children, className, ...props }) {
|
|
95
|
+
return /* @__PURE__ */ jsx("span", { className: cx("uix-cell-strong", className), ...props, children });
|
|
96
|
+
}
|
|
97
|
+
function CellSub({ children, className, ...props }) {
|
|
98
|
+
return /* @__PURE__ */ jsx("span", { className: cx("uix-cell-sub", className), ...props, children });
|
|
99
|
+
}
|
|
100
|
+
function Mark({ children, className, ...props }) {
|
|
101
|
+
return /* @__PURE__ */ jsx("mark", { className: cx("uix-mark", className), ...props, children });
|
|
102
|
+
}
|
|
103
|
+
function Highlighted({ text, query }) {
|
|
104
|
+
return /* @__PURE__ */ jsx(Fragment, { children: highlightSegments(text, query).map(
|
|
105
|
+
(seg, i) => seg.match ? /* @__PURE__ */ jsx("mark", { className: "uix-mark", children: seg.text }, i) : /* @__PURE__ */ jsx("span", { children: seg.text }, i)
|
|
106
|
+
) });
|
|
107
|
+
}
|
|
56
108
|
export {
|
|
109
|
+
BulkBar,
|
|
110
|
+
CellStrong,
|
|
111
|
+
CellSub,
|
|
112
|
+
ExpandToggle,
|
|
113
|
+
Highlighted,
|
|
114
|
+
Mark,
|
|
115
|
+
RowAction,
|
|
116
|
+
RowActions,
|
|
57
117
|
Table,
|
|
58
118
|
TableWrap,
|
|
59
119
|
Td,
|
|
@@ -7,10 +7,16 @@ interface TimelineProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
7
7
|
/** Activity timeline container over `.uix-timeline`. */
|
|
8
8
|
declare function Timeline({ children, className, ...props }: TimelineProps): react.JSX.Element;
|
|
9
9
|
interface TimelineItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
|
+
/**
|
|
11
|
+
* Custom rail node — an icon puck, an avatar, a tinted dot. Defaults to the
|
|
12
|
+
* accent `.uix-timeline__node` dot. Supply this to make a heterogeneous feed
|
|
13
|
+
* (comments + audit events) read on one rail with per-row node semantics.
|
|
14
|
+
*/
|
|
15
|
+
node?: ReactNode;
|
|
10
16
|
meta?: ReactNode;
|
|
11
17
|
children?: ReactNode;
|
|
12
18
|
}
|
|
13
19
|
/** A timeline entry over `.uix-timeline__item` (node + connecting line + body). */
|
|
14
|
-
declare function TimelineItem({ meta, children, className, ...props }: TimelineItemProps): react.JSX.Element;
|
|
20
|
+
declare function TimelineItem({ node, meta, children, className, ...props }: TimelineItemProps): react.JSX.Element;
|
|
15
21
|
|
|
16
22
|
export { Timeline, TimelineItem, type TimelineItemProps, type TimelineProps };
|
|
@@ -3,10 +3,10 @@ import { cx } from "../cx.js";
|
|
|
3
3
|
function Timeline({ children, className, ...props }) {
|
|
4
4
|
return /* @__PURE__ */ jsx("div", { className: cx("uix-timeline", className), ...props, children });
|
|
5
5
|
}
|
|
6
|
-
function TimelineItem({ meta, children, className, ...props }) {
|
|
6
|
+
function TimelineItem({ node, meta, children, className, ...props }) {
|
|
7
7
|
return /* @__PURE__ */ jsxs("div", { className: cx("uix-timeline__item", className), ...props, children: [
|
|
8
8
|
/* @__PURE__ */ jsxs("div", { className: "uix-timeline__rail", children: [
|
|
9
|
-
/* @__PURE__ */ jsx("span", { className: "uix-timeline__node" }),
|
|
9
|
+
node ?? /* @__PURE__ */ jsx("span", { className: "uix-timeline__node" }),
|
|
10
10
|
/* @__PURE__ */ jsx("span", { className: "uix-timeline__line" })
|
|
11
11
|
] }),
|
|
12
12
|
/* @__PURE__ */ jsxs("div", { className: "uix-timeline__body", children: [
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { SortKey, ColumnFilter, SelectAllState, ViewState, Row } from '../table-engine.js';
|
|
2
|
+
|
|
3
|
+
interface UseTableOptions {
|
|
4
|
+
/** row field used as the stable id (selection / pinning). Default: 'id'. */
|
|
5
|
+
idField?: string;
|
|
6
|
+
initialSort?: SortKey[];
|
|
7
|
+
initialFilters?: ColumnFilter[];
|
|
8
|
+
initialQuery?: string;
|
|
9
|
+
/** restrict full-text search to these fields (default: all values). */
|
|
10
|
+
searchFields?: readonly string[];
|
|
11
|
+
/** ids kept present + hoisted regardless of filter/sort. */
|
|
12
|
+
initialPinned?: Iterable<string>;
|
|
13
|
+
}
|
|
14
|
+
interface UseTableResult<T> {
|
|
15
|
+
/** derived view: filter → search → sort, then pinned rows hoisted to the top. */
|
|
16
|
+
rows: T[];
|
|
17
|
+
sort: SortKey[];
|
|
18
|
+
filters: ColumnFilter[];
|
|
19
|
+
query: string;
|
|
20
|
+
selected: ReadonlySet<string>;
|
|
21
|
+
pinned: ReadonlySet<string>;
|
|
22
|
+
/** header select-all tri-state over the current derived rows. */
|
|
23
|
+
selectAll: SelectAllState;
|
|
24
|
+
/** cycle a column's sort; pass additive (⇧-click) to keep it as a secondary key. */
|
|
25
|
+
toggleSort: (field: string, additive?: boolean) => void;
|
|
26
|
+
setSort: (keys: SortKey[]) => void;
|
|
27
|
+
setFilters: (filters: ColumnFilter[]) => void;
|
|
28
|
+
setQuery: (query: string) => void;
|
|
29
|
+
isSelected: (id: string) => boolean;
|
|
30
|
+
toggleRow: (id: string) => void;
|
|
31
|
+
/** toggle every currently-visible row from the header checkbox. */
|
|
32
|
+
toggleAllRows: () => void;
|
|
33
|
+
clearSelection: () => void;
|
|
34
|
+
togglePin: (id: string) => void;
|
|
35
|
+
/** current state as a ViewState + its linkable query string. */
|
|
36
|
+
view: ViewState;
|
|
37
|
+
viewQueryString: string;
|
|
38
|
+
/** hydrate sort/filters/query from a saved-view query string. */
|
|
39
|
+
applyView: (queryString: string) => void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Compose the framework-agnostic table-engine into React state: sorting, typed
|
|
43
|
+
* filtering, full-text search, page-scoped selection, pinning, and linkable saved
|
|
44
|
+
* views. Presentation stays in the `<Table>` primitives + table.css; this only owns
|
|
45
|
+
* the state and the derived row list. The vanilla styleguide (`guide/app.js`) runs
|
|
46
|
+
* the same engine functions, so both layers behave identically.
|
|
47
|
+
*/
|
|
48
|
+
declare function useTable<T extends Row>(data: readonly T[], options?: UseTableOptions): UseTableResult<T>;
|
|
49
|
+
|
|
50
|
+
export { type UseTableOptions, type UseTableResult, useTable };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useMemo, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
multiSort,
|
|
5
|
+
toggleSort,
|
|
6
|
+
applyFilters,
|
|
7
|
+
searchRows,
|
|
8
|
+
serializeView,
|
|
9
|
+
parseView,
|
|
10
|
+
mergePinned,
|
|
11
|
+
toggleId,
|
|
12
|
+
togglePage,
|
|
13
|
+
selectAllState
|
|
14
|
+
} from "../table-engine.js";
|
|
15
|
+
function useTable(data, options = {}) {
|
|
16
|
+
const { idField = "id", searchFields } = options;
|
|
17
|
+
const [sort, setSort] = useState(() => options.initialSort ?? []);
|
|
18
|
+
const [filters, setFilters] = useState(() => options.initialFilters ?? []);
|
|
19
|
+
const [query, setQuery] = useState(() => options.initialQuery ?? "");
|
|
20
|
+
const [selected, setSelected] = useState(() => /* @__PURE__ */ new Set());
|
|
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]);
|
|
28
|
+
const pageIds = useMemo(() => rows.map((r) => String(r[idField])), [rows, idField]);
|
|
29
|
+
const selectAll = selectAllState(selected, pageIds);
|
|
30
|
+
const doToggleSort = useCallback((field, additive = false) => {
|
|
31
|
+
setSort((keys) => toggleSort(keys, field, additive));
|
|
32
|
+
}, []);
|
|
33
|
+
const toggleRow = useCallback((id) => setSelected((s) => toggleId(s, id)), []);
|
|
34
|
+
const toggleAllRows = useCallback(() => setSelected((s) => togglePage(s, pageIds)), [pageIds]);
|
|
35
|
+
const clearSelection = useCallback(() => setSelected(/* @__PURE__ */ new Set()), []);
|
|
36
|
+
const togglePin = useCallback((id) => setPinned((s) => toggleId(s, id)), []);
|
|
37
|
+
const view = useMemo(() => ({ sort, filters, q: query }), [sort, filters, query]);
|
|
38
|
+
const viewQueryString = useMemo(() => serializeView(view), [view]);
|
|
39
|
+
const applyView = useCallback((queryString) => {
|
|
40
|
+
const v = parseView(queryString);
|
|
41
|
+
setSort(v.sort ?? []);
|
|
42
|
+
setFilters(v.filters ?? []);
|
|
43
|
+
setQuery(v.q ?? "");
|
|
44
|
+
}, []);
|
|
45
|
+
return {
|
|
46
|
+
rows,
|
|
47
|
+
sort,
|
|
48
|
+
filters,
|
|
49
|
+
query,
|
|
50
|
+
selected,
|
|
51
|
+
pinned,
|
|
52
|
+
selectAll,
|
|
53
|
+
toggleSort: doToggleSort,
|
|
54
|
+
setSort,
|
|
55
|
+
setFilters,
|
|
56
|
+
setQuery,
|
|
57
|
+
isSelected: (id) => selected.has(id),
|
|
58
|
+
toggleRow,
|
|
59
|
+
toggleAllRows,
|
|
60
|
+
clearSelection,
|
|
61
|
+
togglePin,
|
|
62
|
+
view,
|
|
63
|
+
viewQueryString,
|
|
64
|
+
applyView
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
useTable
|
|
69
|
+
};
|