@apliteni/apliteni-ui 0.25.3 → 0.26.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/package.json +1 -1
- package/react/README.md +29 -0
- package/react/dist/index.d.ts +25 -6
- package/react/dist/index.js +33 -18
package/package.json
CHANGED
package/react/README.md
CHANGED
|
@@ -64,3 +64,32 @@ The bare `@apliteni/apliteni-ui` specifier in this source resolves to the kit it
|
|
|
64
64
|
once installed. In the repo there is no copy to resolve to, so `kit-alias.ts` points
|
|
65
65
|
vitest and Storybook straight at `../src/` — which is why the class-name parity tests
|
|
66
66
|
now compare against the working tree rather than the last published release.
|
|
67
|
+
|
|
68
|
+
### Tables with another presentation of the same rows
|
|
69
|
+
|
|
70
|
+
Pass `selectable={false}` to omit selection controls and their callbacks. Existing callers
|
|
71
|
+
that supply selection callbacks retain the checkbox column by default.
|
|
72
|
+
|
|
73
|
+
`sort` and `onSortChange` make sorting controlled. Both presentations can use the exported
|
|
74
|
+
`sortTableRows` helper, so initial order, stable ties and later changes agree:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
const [sort, setSort] = useState<TableSort<Row>>({ key: 'name', dir: -1 });
|
|
78
|
+
const ordered = sortTableRows(rows, sort);
|
|
79
|
+
|
|
80
|
+
<DataTable columns={columns} rows={rows} selectable={false}
|
|
81
|
+
sort={sort} onSortChange={setSort} />
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Render the sibling list from `ordered`. Omit `sort` to keep the table's own state;
|
|
85
|
+
`onSortChange` can also observe that uncontrolled state. Set `key: undefined` to preserve
|
|
86
|
+
input order. Import `TableSort` and `sortTableRows` from `@apliteni/apliteni-ui/react`.
|
|
87
|
+
|
|
88
|
+
Changing the sort returns the table to its first page. The comparator uses JavaScript
|
|
89
|
+
`<` and `>`; use consistently typed, comparable values in sortable columns. Ordering of
|
|
90
|
+
mixed types, missing values and `NaN` is not guaranteed. `sortTableRows` always returns
|
|
91
|
+
a new array, including when `key` is `undefined`.
|
|
92
|
+
|
|
93
|
+
Choose controlled or uncontrolled once per table. Passing `sort` for a while and then
|
|
94
|
+
dropping it is not supported: the table falls back to the sort state it started with, not to
|
|
95
|
+
the one it was last given.
|
package/react/dist/index.d.ts
CHANGED
|
@@ -45,17 +45,36 @@ type Column<T> = {
|
|
|
45
45
|
sortable?: boolean;
|
|
46
46
|
render?: (row: T) => ReactNode;
|
|
47
47
|
};
|
|
48
|
-
type
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
type TableSort<T> = {
|
|
49
|
+
key: (keyof T & string) | undefined;
|
|
50
|
+
dir: 1 | -1;
|
|
51
|
+
};
|
|
52
|
+
type SelectionProps = {
|
|
53
|
+
selectable: false;
|
|
54
|
+
selected?: Set<string>;
|
|
55
|
+
onToggle?: (name: string) => void;
|
|
56
|
+
onTogglePage?: (names: string[]) => void;
|
|
57
|
+
} | {
|
|
58
|
+
selectable?: true;
|
|
52
59
|
selected: Set<string>;
|
|
53
60
|
onToggle: (name: string) => void;
|
|
54
61
|
onTogglePage: (names: string[]) => void;
|
|
55
62
|
};
|
|
63
|
+
type DataTableProps<T> = {
|
|
64
|
+
columns: Column<T>[];
|
|
65
|
+
rows: T[];
|
|
66
|
+
pageSize?: number;
|
|
67
|
+
} & SelectionProps & ({
|
|
68
|
+
sort?: never;
|
|
69
|
+
onSortChange?: (sort: TableSort<T>) => void;
|
|
70
|
+
} | {
|
|
71
|
+
sort: TableSort<T>;
|
|
72
|
+
onSortChange: (sort: TableSort<T>) => void;
|
|
73
|
+
});
|
|
74
|
+
declare function sortTableRows<T>(rows: T[], sort: TableSort<T>): T[];
|
|
56
75
|
declare function DataTable<T extends {
|
|
57
76
|
name: string;
|
|
58
|
-
}>({ columns, rows, pageSize, selected, onToggle, onTogglePage, }: DataTableProps<T>): react.JSX.Element;
|
|
77
|
+
}>({ columns, rows, pageSize, selectable, selected, onToggle, onTogglePage, sort: controlledSort, onSortChange, }: DataTableProps<T>): react.JSX.Element;
|
|
59
78
|
|
|
60
79
|
type SkeletonProps = {
|
|
61
80
|
/** A count of bars, or explicit widths when a ragged prose edge matters. */
|
|
@@ -96,4 +115,4 @@ type DeniedProps = {
|
|
|
96
115
|
};
|
|
97
116
|
declare function Denied({ title, sub, need, icon, className, children, }: DeniedProps): react.JSX.Element;
|
|
98
117
|
|
|
99
|
-
export { Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Card, type Column, DataTable, type DataTableProps, Denied, type DeniedProps, Icon, Modal, type ModalProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps };
|
|
118
|
+
export { Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Card, type Column, DataTable, type DataTableProps, Denied, type DeniedProps, Icon, Modal, type ModalProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, type TableSort, sortTableRows };
|
package/react/dist/index.js
CHANGED
|
@@ -172,36 +172,50 @@ function Modal({ open, title, onClose, footer, children }) {
|
|
|
172
172
|
// src/DataTable.tsx
|
|
173
173
|
import { useMemo, useState } from "react";
|
|
174
174
|
import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
175
|
+
function sortTableRows(rows, sort) {
|
|
176
|
+
if (sort.key === void 0) return [...rows];
|
|
177
|
+
const key = sort.key;
|
|
178
|
+
return [...rows].sort((a, b) => (a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : 0) * sort.dir);
|
|
179
|
+
}
|
|
175
180
|
function DataTable({
|
|
176
181
|
columns,
|
|
177
182
|
rows,
|
|
178
183
|
pageSize = 4,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
selectable = true,
|
|
185
|
+
selected = /* @__PURE__ */ new Set(),
|
|
186
|
+
onToggle = () => {
|
|
187
|
+
},
|
|
188
|
+
onTogglePage = () => {
|
|
189
|
+
},
|
|
190
|
+
sort: controlledSort,
|
|
191
|
+
onSortChange
|
|
182
192
|
}) {
|
|
183
|
-
const [
|
|
193
|
+
const [localSort, setLocalSort] = useState(
|
|
184
194
|
{ key: columns.find((c) => c.sortable)?.key, dir: -1 }
|
|
185
195
|
);
|
|
196
|
+
const sort = controlledSort ?? localSort;
|
|
186
197
|
const [page, setPage] = useState(0);
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
198
|
+
const [pagedSort, setPagedSort] = useState(sort);
|
|
199
|
+
if (pagedSort.key !== sort.key || pagedSort.dir !== sort.dir) {
|
|
200
|
+
setPagedSort(sort);
|
|
201
|
+
setPage(0);
|
|
202
|
+
}
|
|
203
|
+
const sorted = useMemo(() => sortTableRows(rows, sort), [rows, sort.key, sort.dir]);
|
|
192
204
|
const pages = Math.max(1, Math.ceil(sorted.length / pageSize));
|
|
193
205
|
const safePage = Math.min(page, pages - 1);
|
|
194
206
|
const slice = sorted.slice(safePage * pageSize, safePage * pageSize + pageSize);
|
|
195
|
-
const onSort = (
|
|
196
|
-
|
|
207
|
+
const onSort = (key) => {
|
|
208
|
+
const next = sort.key === key ? { key, dir: sort.dir === 1 ? -1 : 1 } : { key, dir: -1 };
|
|
209
|
+
if (controlledSort === void 0) setLocalSort(next);
|
|
210
|
+
onSortChange?.(next);
|
|
197
211
|
setPage(0);
|
|
198
212
|
};
|
|
199
213
|
const caret = (k) => sort.key === k ? sort.dir === 1 ? " \u25B2" : " \u25BC" : " \u2195";
|
|
200
|
-
const pageAllOn = slice.length > 0 && slice.every((r) => selected.has(r.name));
|
|
214
|
+
const pageAllOn = selectable && slice.length > 0 && slice.every((r) => selected.has(r.name));
|
|
201
215
|
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
202
216
|
/* @__PURE__ */ jsxs4("table", { className: "ui-table ui-table--hover ui-table--zebra", children: [
|
|
203
217
|
/* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsxs4("tr", { children: [
|
|
204
|
-
/* @__PURE__ */ jsx6("th", { scope: "col", children: /* @__PURE__ */ jsx6(
|
|
218
|
+
selectable ? /* @__PURE__ */ jsx6("th", { scope: "col", children: /* @__PURE__ */ jsx6(
|
|
205
219
|
"input",
|
|
206
220
|
{
|
|
207
221
|
type: "checkbox",
|
|
@@ -209,7 +223,7 @@ function DataTable({
|
|
|
209
223
|
"aria-label": "Select all rows on this page",
|
|
210
224
|
onChange: () => onTogglePage(slice.map((r) => r.name))
|
|
211
225
|
}
|
|
212
|
-
) }),
|
|
226
|
+
) }) : null,
|
|
213
227
|
columns.map((c) => (
|
|
214
228
|
// The sort control is a real <button> inside the header cell. It used to be
|
|
215
229
|
// role="button" ON the <th>, which threw away the columnheader role and put
|
|
@@ -219,7 +233,7 @@ function DataTable({
|
|
|
219
233
|
{
|
|
220
234
|
scope: "col",
|
|
221
235
|
className: [c.num && "ui-table__num", c.sortable && "rx-sortable"].filter(Boolean).join(" "),
|
|
222
|
-
"aria-sort":
|
|
236
|
+
"aria-sort": sort.key === c.key ? sort.dir === 1 ? "ascending" : "descending" : c.sortable ? "none" : void 0,
|
|
223
237
|
children: c.sortable ? /* @__PURE__ */ jsxs4("button", { type: "button", className: "rx-sort", onClick: () => onSort(c.key), children: [
|
|
224
238
|
c.label,
|
|
225
239
|
/* @__PURE__ */ jsx6("span", { className: "rx-caret", "aria-hidden": "true", children: caret(c.key) })
|
|
@@ -230,7 +244,7 @@ function DataTable({
|
|
|
230
244
|
))
|
|
231
245
|
] }) }),
|
|
232
246
|
/* @__PURE__ */ jsx6("tbody", { children: slice.map((r) => /* @__PURE__ */ jsxs4("tr", { children: [
|
|
233
|
-
/* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsx6(
|
|
247
|
+
selectable ? /* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsx6(
|
|
234
248
|
"input",
|
|
235
249
|
{
|
|
236
250
|
type: "checkbox",
|
|
@@ -238,7 +252,7 @@ function DataTable({
|
|
|
238
252
|
"aria-label": `Select ${r.name}`,
|
|
239
253
|
onChange: () => onToggle(r.name)
|
|
240
254
|
}
|
|
241
|
-
) }),
|
|
255
|
+
) }) : null,
|
|
242
256
|
columns.map((c) => /* @__PURE__ */ jsx6("td", { className: c.num ? "ui-table__num" : void 0, children: c.render ? c.render(r) : String(r[c.key]) }, c.key))
|
|
243
257
|
] }, r.name)) })
|
|
244
258
|
] }),
|
|
@@ -357,5 +371,6 @@ export {
|
|
|
357
371
|
Icon,
|
|
358
372
|
Modal,
|
|
359
373
|
Skeleton,
|
|
360
|
-
SkeletonTable
|
|
374
|
+
SkeletonTable,
|
|
375
|
+
sortTableRows
|
|
361
376
|
};
|