@robr0/design-system 0.6.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/LICENSE +53 -2
- package/README.md +36 -5
- package/components/AgentPlan/AgentPlan.css +231 -0
- package/components/AgentPlan/AgentPlan.d.ts +41 -0
- package/components/AgentPlan/AgentPlan.js +99 -0
- package/components/AgentStatus/AgentStatus.css +26 -11
- package/components/Card/Card.css +9 -0
- package/components/Card/Card.d.ts +6 -0
- package/components/Card/Card.js +2 -1
- package/components/Composer/Composer.css +18 -0
- package/components/Composer/Composer.d.ts +3 -0
- package/components/Composer/Composer.js +12 -1
- package/components/DataTable/DataTable.css +105 -0
- package/components/DataTable/DataTable.d.ts +81 -0
- package/components/DataTable/DataTable.js +235 -0
- package/components/EventCalendar/EventCalendar.css +328 -0
- package/components/EventCalendar/EventCalendar.d.ts +51 -0
- package/components/EventCalendar/EventCalendar.js +213 -0
- package/components/ModelPicker/ModelPicker.css +238 -0
- package/components/ModelPicker/ModelPicker.d.ts +60 -0
- package/components/ModelPicker/ModelPicker.js +217 -0
- package/components/NotificationCenter/NotificationCenter.css +262 -0
- package/components/NotificationCenter/NotificationCenter.d.ts +72 -0
- package/components/NotificationCenter/NotificationCenter.js +128 -0
- package/components/Prose/Prose.css +18 -1
- package/components/Reasoning/Reasoning.css +19 -4
- package/components/ShaderField/ShaderField.css +24 -0
- package/components/ShaderField/ShaderField.d.ts +68 -0
- package/components/ShaderField/ShaderField.js +54 -0
- package/components/ShaderField/field.glsl.d.ts +39 -0
- package/components/ShaderField/field.glsl.js +178 -0
- package/components/ShaderField/useShaderField.d.ts +103 -0
- package/components/ShaderField/useShaderField.js +409 -0
- package/components/Timeline/Timeline.css +17 -0
- package/components/Timeline/Timeline.d.ts +2 -0
- package/components/Timeline/Timeline.js +1 -0
- package/components/registry.json +48 -0
- package/components/registry.json.d.ts +48 -0
- package/components/registry.json.js +1 -1
- package/index.d.ts +6 -0
- package/index.js +19 -0
- package/package.json +5 -1
- package/tokens/registry.json +1 -0
- package/tokens/registry.json.d.ts +1 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +1 -1
- package/tokens/tokens-light.css +2 -2
- package/tokens/tokens-motion.css +2 -0
|
@@ -59,6 +59,9 @@ export interface ComposerProps extends ComposerOwnProps, Omit<React.ComponentPro
|
|
|
59
59
|
* The textarea grows with its content up to `maxRows`, then scrolls
|
|
60
60
|
* internally. Where the browser supports `field-sizing: content` the sizing
|
|
61
61
|
* is fully native; elsewhere a measurement effect keeps the height in step.
|
|
62
|
+
* Either way the text zone glides between the two heights rather than
|
|
63
|
+
* snapping — 75ms, short enough to soften the step without reading as an
|
|
64
|
+
* animation. The action bar's buttons never move relative to the bar.
|
|
62
65
|
*
|
|
63
66
|
* Forwards a ref to the underlying `<textarea>` and spreads unrecognised
|
|
64
67
|
* props onto it; `className` lands on the shell.
|
|
@@ -23,6 +23,7 @@ const Composer = React.forwardRef(
|
|
|
23
23
|
}, ref) => {
|
|
24
24
|
const baseClass = "ds-composer";
|
|
25
25
|
const textareaRef = useRef(null);
|
|
26
|
+
const contentRef = useRef(null);
|
|
26
27
|
const isControlled = value !== void 0;
|
|
27
28
|
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? "");
|
|
28
29
|
const currentValue = isControlled ? value : uncontrolledValue;
|
|
@@ -40,6 +41,16 @@ const Composer = React.forwardRef(
|
|
|
40
41
|
node.style.height = "auto";
|
|
41
42
|
node.style.height = `${node.scrollHeight}px`;
|
|
42
43
|
}, [currentValue]);
|
|
44
|
+
useLayoutEffect(() => {
|
|
45
|
+
const node = textareaRef.current;
|
|
46
|
+
const content = contentRef.current;
|
|
47
|
+
if (!node || !content) return;
|
|
48
|
+
const observer = new ResizeObserver(() => {
|
|
49
|
+
content.style.setProperty("--ds-composer-text-height", `${node.offsetHeight}px`);
|
|
50
|
+
});
|
|
51
|
+
observer.observe(node);
|
|
52
|
+
return () => observer.disconnect();
|
|
53
|
+
}, []);
|
|
43
54
|
const submit = () => {
|
|
44
55
|
if (streaming || !canSend) return;
|
|
45
56
|
onSubmit?.(currentValue);
|
|
@@ -71,7 +82,7 @@ const Composer = React.forwardRef(
|
|
|
71
82
|
onClick: handleShellClick,
|
|
72
83
|
children: [
|
|
73
84
|
attachments && /* @__PURE__ */ jsx("div", { className: `${baseClass}__attachments`, children: attachments }),
|
|
74
|
-
/* @__PURE__ */ jsx("div", { className: `${baseClass}__content`, children: /* @__PURE__ */ jsx(
|
|
85
|
+
/* @__PURE__ */ jsx("div", { className: `${baseClass}__content`, ref: contentRef, children: /* @__PURE__ */ jsx(
|
|
75
86
|
"textarea",
|
|
76
87
|
{
|
|
77
88
|
...rest,
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
DATA TABLE COMPONENT
|
|
3
|
+
The wired table block: toolbar with search
|
|
4
|
+
and filter slot, sortable headers, selection,
|
|
5
|
+
pagination footer. Row and cell styling all
|
|
6
|
+
comes from the underlying Table — this file
|
|
7
|
+
only styles the chrome around it.
|
|
8
|
+
============================================ */
|
|
9
|
+
|
|
10
|
+
.ds-data-table {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
gap: var(--gap-sm);
|
|
14
|
+
width: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* ============================================
|
|
18
|
+
TOOLBAR
|
|
19
|
+
============================================ */
|
|
20
|
+
|
|
21
|
+
.ds-data-table__toolbar {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: space-between;
|
|
25
|
+
gap: var(--gap-sm);
|
|
26
|
+
flex-wrap: wrap;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ds-data-table__filters {
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
gap: var(--gap-sm);
|
|
33
|
+
flex-wrap: wrap;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* The search field sits at the end of the toolbar even with no filters */
|
|
37
|
+
.ds-data-table__search {
|
|
38
|
+
margin-left: auto;
|
|
39
|
+
max-width: 240px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ============================================
|
|
43
|
+
SORTABLE HEADERS
|
|
44
|
+
The button fills the header cell so the whole
|
|
45
|
+
label is the click target.
|
|
46
|
+
============================================ */
|
|
47
|
+
|
|
48
|
+
.ds-data-table__sort {
|
|
49
|
+
display: inline-flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: var(--gap-xxs);
|
|
52
|
+
padding: 0;
|
|
53
|
+
background: none;
|
|
54
|
+
border: none;
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
color: inherit;
|
|
57
|
+
font: inherit;
|
|
58
|
+
letter-spacing: inherit;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.ds-data-table__sort:focus-visible {
|
|
62
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
63
|
+
outline-offset: 2px;
|
|
64
|
+
border-radius: var(--radius-xxs);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.ds-data-table__sort-icon {
|
|
68
|
+
--icon-size: var(--icon-size-sm);
|
|
69
|
+
|
|
70
|
+
color: var(--color-icon-secondary);
|
|
71
|
+
opacity: 0;
|
|
72
|
+
transition: opacity var(--motion-duration-fast) var(--motion-ease-standard);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.ds-data-table__sort:hover .ds-data-table__sort-icon,
|
|
76
|
+
.ds-data-table__sort:focus-visible .ds-data-table__sort-icon,
|
|
77
|
+
.ds-data-table__sort-icon--active {
|
|
78
|
+
opacity: 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.ds-data-table__sort-icon--active {
|
|
82
|
+
color: var(--color-text-primary);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* ============================================
|
|
86
|
+
FOOTER
|
|
87
|
+
============================================ */
|
|
88
|
+
|
|
89
|
+
.ds-data-table__footer {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
gap: var(--gap-sm);
|
|
94
|
+
flex-wrap: wrap;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.ds-data-table__count {
|
|
98
|
+
color: var(--color-text-tertiary);
|
|
99
|
+
font-family: var(--font-paragraph-sm-family);
|
|
100
|
+
font-size: var(--font-paragraph-sm-size);
|
|
101
|
+
font-weight: var(--font-paragraph-sm-weight);
|
|
102
|
+
line-height: var(--font-paragraph-sm-line-height);
|
|
103
|
+
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
104
|
+
font-variant-numeric: tabular-nums;
|
|
105
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export interface DataTableColumn {
|
|
3
|
+
/** Unique column identifier, used as the key into each row's values. */
|
|
4
|
+
key: string;
|
|
5
|
+
/** Header label displayed in the column header. */
|
|
6
|
+
header: React.ReactNode;
|
|
7
|
+
/** Whether clicking this header sorts by the column. */
|
|
8
|
+
sortable?: boolean;
|
|
9
|
+
/** Optional column width (CSS value like '200px', '30%', 'auto'). */
|
|
10
|
+
width?: string;
|
|
11
|
+
/** Text alignment for cells in this column. */
|
|
12
|
+
align?: 'left' | 'center' | 'right';
|
|
13
|
+
/** Render the cell for a row; defaults to the row's raw value for this key. */
|
|
14
|
+
render?: (row: DataTableRow) => React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export interface DataTableRow {
|
|
17
|
+
/** Unique row identifier. */
|
|
18
|
+
id: string;
|
|
19
|
+
/** Raw values keyed by column key — used for sorting, searching, and default rendering. */
|
|
20
|
+
values: Record<string, string | number | null | undefined>;
|
|
21
|
+
}
|
|
22
|
+
export interface DataTableSort {
|
|
23
|
+
/** The column being sorted. */
|
|
24
|
+
key: string;
|
|
25
|
+
/** Sort direction. */
|
|
26
|
+
direction: 'asc' | 'desc';
|
|
27
|
+
}
|
|
28
|
+
/** Props owned by DataTable itself — everything else falls through to the root element. */
|
|
29
|
+
type DataTableOwnProps = {
|
|
30
|
+
/** Column definitions. */
|
|
31
|
+
columns: DataTableColumn[];
|
|
32
|
+
/** Row data, as raw values the table can sort and search. */
|
|
33
|
+
rows: DataTableRow[];
|
|
34
|
+
/** Rows per page. Setting this turns on the built-in pagination. */
|
|
35
|
+
pageSize?: number;
|
|
36
|
+
/** Adds a checkbox column with a select-all header. */
|
|
37
|
+
selectable?: boolean;
|
|
38
|
+
/** Selected row ids for controlled use. Pair with `onSelectionChange`. */
|
|
39
|
+
selectedIds?: string[];
|
|
40
|
+
/** Initially selected row ids for uncontrolled use. */
|
|
41
|
+
defaultSelectedIds?: string[];
|
|
42
|
+
/** Fires with the full list of selected row ids after every change. */
|
|
43
|
+
onSelectionChange?: (ids: string[]) => void;
|
|
44
|
+
/** Sort state for controlled use. Pair with `onSortChange`; `null` means unsorted. */
|
|
45
|
+
sort?: DataTableSort | null;
|
|
46
|
+
/** Initial sort state for uncontrolled use. */
|
|
47
|
+
defaultSort?: DataTableSort;
|
|
48
|
+
/** Fires with the new sort state — `null` when a third click clears the sort. */
|
|
49
|
+
onSortChange?: (sort: DataTableSort | null) => void;
|
|
50
|
+
/** Shows the built-in search field, matching against every column's raw value. */
|
|
51
|
+
searchable?: boolean;
|
|
52
|
+
/** Placeholder for the search field. */
|
|
53
|
+
searchPlaceholder?: string;
|
|
54
|
+
/** Slot beside the search field for consumer-owned filter controls. */
|
|
55
|
+
toolbar?: React.ReactNode;
|
|
56
|
+
/** Visual size, passed through to the underlying Table. */
|
|
57
|
+
size?: 'default' | 'compact';
|
|
58
|
+
/** Alternating row backgrounds, passed through to the underlying Table. */
|
|
59
|
+
striped?: boolean;
|
|
60
|
+
/** Accessible caption for the underlying table (visually hidden). */
|
|
61
|
+
caption?: string;
|
|
62
|
+
/** What to render when no rows match — defaults to a built-in empty state. */
|
|
63
|
+
emptyState?: React.ReactNode;
|
|
64
|
+
/** Additional CSS classes */
|
|
65
|
+
className?: string;
|
|
66
|
+
};
|
|
67
|
+
export interface DataTableProps extends DataTableOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof DataTableOwnProps> {
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* DataTable is the wired version of Table: sorting on header click, a search
|
|
71
|
+
* field, row selection with a select-all header, client-side pagination, and
|
|
72
|
+
* an empty state — assembled from Table, Pagination, Checkbox, Input and
|
|
73
|
+
* EmptyState so a working data view is one component, not an afternoon of
|
|
74
|
+
* plumbing.
|
|
75
|
+
*
|
|
76
|
+
* Rows carry raw values rather than rendered nodes; a column's `render`
|
|
77
|
+
* turns them into cells when text isn't enough. For server-driven data,
|
|
78
|
+
* control `sort` and slot your own filters into `toolbar`.
|
|
79
|
+
*/
|
|
80
|
+
export declare const DataTable: React.ForwardRefExoticComponent<DataTableProps & React.RefAttributes<HTMLDivElement>>;
|
|
81
|
+
export {};
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useMemo } from "react";
|
|
3
|
+
import { Table } from "../Table/Table.js";
|
|
4
|
+
import { Pagination } from "../Pagination/Pagination.js";
|
|
5
|
+
import { Checkbox } from "../Checkbox/Checkbox.js";
|
|
6
|
+
import { Input } from "../Input/Input.js";
|
|
7
|
+
import { EmptyState } from "../EmptyState/EmptyState.js";
|
|
8
|
+
import "./DataTable.css";
|
|
9
|
+
import "../../fonts/material-symbols.css";
|
|
10
|
+
const compareValues = (a, b) => {
|
|
11
|
+
if (a == null && b == null) return 0;
|
|
12
|
+
if (a == null) return -1;
|
|
13
|
+
if (b == null) return 1;
|
|
14
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
15
|
+
return String(a).localeCompare(String(b), void 0, { numeric: true, sensitivity: "base" });
|
|
16
|
+
};
|
|
17
|
+
const DataTable = React.forwardRef(
|
|
18
|
+
({
|
|
19
|
+
columns,
|
|
20
|
+
rows,
|
|
21
|
+
pageSize,
|
|
22
|
+
selectable = false,
|
|
23
|
+
selectedIds,
|
|
24
|
+
defaultSelectedIds,
|
|
25
|
+
onSelectionChange,
|
|
26
|
+
sort,
|
|
27
|
+
defaultSort,
|
|
28
|
+
onSortChange,
|
|
29
|
+
searchable = false,
|
|
30
|
+
searchPlaceholder = "Search",
|
|
31
|
+
toolbar,
|
|
32
|
+
size = "default",
|
|
33
|
+
striped = false,
|
|
34
|
+
caption,
|
|
35
|
+
emptyState,
|
|
36
|
+
className = "",
|
|
37
|
+
...rest
|
|
38
|
+
}, ref) => {
|
|
39
|
+
const baseClass = "ds-data-table";
|
|
40
|
+
const [search, setSearch] = useState("");
|
|
41
|
+
const [page, setPage] = useState(1);
|
|
42
|
+
const isSortControlled = sort !== void 0;
|
|
43
|
+
const [uncontrolledSort, setUncontrolledSort] = useState(
|
|
44
|
+
defaultSort ?? null
|
|
45
|
+
);
|
|
46
|
+
const currentSort = isSortControlled ? sort : uncontrolledSort;
|
|
47
|
+
const isSelectionControlled = selectedIds !== void 0;
|
|
48
|
+
const [uncontrolledSelection, setUncontrolledSelection] = useState(
|
|
49
|
+
defaultSelectedIds ?? []
|
|
50
|
+
);
|
|
51
|
+
const currentSelection = isSelectionControlled ? selectedIds : uncontrolledSelection;
|
|
52
|
+
const setSelection = (ids) => {
|
|
53
|
+
if (!isSelectionControlled) setUncontrolledSelection(ids);
|
|
54
|
+
onSelectionChange?.(ids);
|
|
55
|
+
};
|
|
56
|
+
const cycleSort = (key) => {
|
|
57
|
+
let next;
|
|
58
|
+
if (currentSort?.key !== key) next = { key, direction: "asc" };
|
|
59
|
+
else if (currentSort.direction === "asc") next = { key, direction: "desc" };
|
|
60
|
+
else next = null;
|
|
61
|
+
if (!isSortControlled) setUncontrolledSort(next);
|
|
62
|
+
onSortChange?.(next);
|
|
63
|
+
};
|
|
64
|
+
const filteredRows = useMemo(() => {
|
|
65
|
+
if (!search.trim()) return rows;
|
|
66
|
+
const needle = search.trim().toLowerCase();
|
|
67
|
+
return rows.filter(
|
|
68
|
+
(row) => Object.values(row.values).some(
|
|
69
|
+
(value) => value != null && String(value).toLowerCase().includes(needle)
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
}, [rows, search]);
|
|
73
|
+
const sortedRows = useMemo(() => {
|
|
74
|
+
if (!currentSort) return filteredRows;
|
|
75
|
+
const { key, direction } = currentSort;
|
|
76
|
+
const factor = direction === "asc" ? 1 : -1;
|
|
77
|
+
return [...filteredRows].sort(
|
|
78
|
+
(a, b) => compareValues(a.values[key], b.values[key]) * factor
|
|
79
|
+
);
|
|
80
|
+
}, [filteredRows, currentSort]);
|
|
81
|
+
const pageCount = pageSize ? Math.max(1, Math.ceil(sortedRows.length / pageSize)) : 1;
|
|
82
|
+
const clampedPage = Math.min(page, pageCount);
|
|
83
|
+
const visibleRows = pageSize ? sortedRows.slice((clampedPage - 1) * pageSize, clampedPage * pageSize) : sortedRows;
|
|
84
|
+
const visibleIds = visibleRows.map((row) => row.id);
|
|
85
|
+
const selectedVisible = visibleIds.filter((id) => currentSelection.includes(id));
|
|
86
|
+
const allVisibleSelected = visibleIds.length > 0 && selectedVisible.length === visibleIds.length;
|
|
87
|
+
const someVisibleSelected = selectedVisible.length > 0 && !allVisibleSelected;
|
|
88
|
+
const toggleAllVisible = (checked) => {
|
|
89
|
+
if (checked) {
|
|
90
|
+
setSelection([.../* @__PURE__ */ new Set([...currentSelection, ...visibleIds])]);
|
|
91
|
+
} else {
|
|
92
|
+
setSelection(currentSelection.filter((id) => !visibleIds.includes(id)));
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const toggleRow = (id, checked) => {
|
|
96
|
+
if (checked) setSelection([...currentSelection, id]);
|
|
97
|
+
else setSelection(currentSelection.filter((selected) => selected !== id));
|
|
98
|
+
};
|
|
99
|
+
const sortIcon = (key) => {
|
|
100
|
+
if (currentSort?.key !== key) return "swap_vert";
|
|
101
|
+
return currentSort.direction === "asc" ? "arrow_upward" : "arrow_downward";
|
|
102
|
+
};
|
|
103
|
+
const sortStateText = (key) => {
|
|
104
|
+
if (currentSort?.key !== key) return "not sorted";
|
|
105
|
+
return currentSort.direction === "asc" ? "sorted ascending" : "sorted descending";
|
|
106
|
+
};
|
|
107
|
+
const tableColumns = [
|
|
108
|
+
...selectable ? [
|
|
109
|
+
{
|
|
110
|
+
key: "ds-data-table-select",
|
|
111
|
+
header: /* @__PURE__ */ jsx(
|
|
112
|
+
Checkbox,
|
|
113
|
+
{
|
|
114
|
+
size: "compact",
|
|
115
|
+
checked: allVisibleSelected,
|
|
116
|
+
indeterminate: someVisibleSelected,
|
|
117
|
+
onCheckedChange: toggleAllVisible,
|
|
118
|
+
"aria-label": "Select all rows on this page"
|
|
119
|
+
}
|
|
120
|
+
),
|
|
121
|
+
width: "40px"
|
|
122
|
+
}
|
|
123
|
+
] : [],
|
|
124
|
+
...columns.map(
|
|
125
|
+
(col) => ({
|
|
126
|
+
key: col.key,
|
|
127
|
+
width: col.width,
|
|
128
|
+
align: col.align,
|
|
129
|
+
header: col.sortable ? /* @__PURE__ */ jsxs(
|
|
130
|
+
"button",
|
|
131
|
+
{
|
|
132
|
+
type: "button",
|
|
133
|
+
className: `${baseClass}__sort`,
|
|
134
|
+
"aria-label": `Sort by ${typeof col.header === "string" ? col.header : col.key}, ${sortStateText(col.key)}`,
|
|
135
|
+
onClick: () => cycleSort(col.key),
|
|
136
|
+
children: [
|
|
137
|
+
/* @__PURE__ */ jsx("span", { children: col.header }),
|
|
138
|
+
/* @__PURE__ */ jsx(
|
|
139
|
+
"span",
|
|
140
|
+
{
|
|
141
|
+
className: [
|
|
142
|
+
`${baseClass}__sort-icon`,
|
|
143
|
+
currentSort?.key === col.key ? `${baseClass}__sort-icon--active` : "",
|
|
144
|
+
"material-symbols-rounded"
|
|
145
|
+
].filter(Boolean).join(" "),
|
|
146
|
+
"aria-hidden": "true",
|
|
147
|
+
children: sortIcon(col.key)
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
) : col.header
|
|
153
|
+
})
|
|
154
|
+
)
|
|
155
|
+
];
|
|
156
|
+
const tableRows = visibleRows.map((row) => ({
|
|
157
|
+
id: row.id,
|
|
158
|
+
cells: {
|
|
159
|
+
...selectable ? {
|
|
160
|
+
"ds-data-table-select": /* @__PURE__ */ jsx(
|
|
161
|
+
Checkbox,
|
|
162
|
+
{
|
|
163
|
+
size: "compact",
|
|
164
|
+
checked: currentSelection.includes(row.id),
|
|
165
|
+
onCheckedChange: (checked) => toggleRow(row.id, checked),
|
|
166
|
+
"aria-label": `Select row ${row.id}`
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
} : {},
|
|
170
|
+
...Object.fromEntries(
|
|
171
|
+
columns.map((col) => [col.key, col.render ? col.render(row) : row.values[col.key]])
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
}));
|
|
175
|
+
const hasChrome = searchable || Boolean(toolbar);
|
|
176
|
+
const classes = [baseClass, className].filter(Boolean).join(" ");
|
|
177
|
+
return /* @__PURE__ */ jsxs("div", { ...rest, ref, className: classes, children: [
|
|
178
|
+
hasChrome && /* @__PURE__ */ jsxs("div", { className: `${baseClass}__toolbar`, children: [
|
|
179
|
+
toolbar && /* @__PURE__ */ jsx("div", { className: `${baseClass}__filters`, children: toolbar }),
|
|
180
|
+
searchable && /* @__PURE__ */ jsx(
|
|
181
|
+
Input,
|
|
182
|
+
{
|
|
183
|
+
className: `${baseClass}__search`,
|
|
184
|
+
size: "compact",
|
|
185
|
+
iconLeft: "search",
|
|
186
|
+
placeholder: searchPlaceholder,
|
|
187
|
+
"aria-label": searchPlaceholder,
|
|
188
|
+
value: search,
|
|
189
|
+
onValueChange: (value) => {
|
|
190
|
+
setSearch(value);
|
|
191
|
+
setPage(1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
] }),
|
|
196
|
+
visibleRows.length > 0 ? /* @__PURE__ */ jsx(
|
|
197
|
+
Table,
|
|
198
|
+
{
|
|
199
|
+
columns: tableColumns,
|
|
200
|
+
rows: tableRows,
|
|
201
|
+
size,
|
|
202
|
+
striped,
|
|
203
|
+
bordered: true,
|
|
204
|
+
caption,
|
|
205
|
+
captionHidden: true
|
|
206
|
+
}
|
|
207
|
+
) : emptyState ?? /* @__PURE__ */ jsx(
|
|
208
|
+
EmptyState,
|
|
209
|
+
{
|
|
210
|
+
icon: "search_off",
|
|
211
|
+
title: "No matching rows",
|
|
212
|
+
description: "Adjust the search or filters and try again.",
|
|
213
|
+
variant: "bordered",
|
|
214
|
+
size: "compact"
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
(pageSize !== void 0 || selectable) && /* @__PURE__ */ jsxs("div", { className: `${baseClass}__footer`, children: [
|
|
218
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__count`, "aria-live": "polite", children: selectable && currentSelection.length > 0 ? `${currentSelection.length} selected` : `${sortedRows.length} result${sortedRows.length === 1 ? "" : "s"}` }),
|
|
219
|
+
pageSize !== void 0 && pageCount > 1 && /* @__PURE__ */ jsx(
|
|
220
|
+
Pagination,
|
|
221
|
+
{
|
|
222
|
+
page: clampedPage,
|
|
223
|
+
pageCount,
|
|
224
|
+
onPageChange: setPage,
|
|
225
|
+
size: "compact"
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
] })
|
|
229
|
+
] });
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
DataTable.displayName = "DataTable";
|
|
233
|
+
export {
|
|
234
|
+
DataTable
|
|
235
|
+
};
|