@jwcarman/roux-web 0.0.0 → 0.1.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 +201 -0
- package/README.md +22 -1
- package/dist/api/client.d.ts +17 -0
- package/dist/api/client.js +57 -0
- package/dist/api/config.d.ts +11 -0
- package/dist/api/config.js +15 -0
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +4 -0
- package/dist/api/serverAction.d.ts +23 -0
- package/dist/api/serverAction.js +28 -0
- package/dist/feedback/ConfirmProvider.d.ts +21 -0
- package/dist/feedback/ConfirmProvider.js +50 -0
- package/dist/feedback/ErrorMessage.d.ts +7 -0
- package/dist/feedback/ErrorMessage.js +11 -0
- package/dist/feedback/index.d.ts +4 -0
- package/dist/feedback/index.js +5 -0
- package/dist/feedback/notify.d.ts +16 -0
- package/dist/feedback/notify.js +27 -0
- package/dist/feedback/toaster.d.ts +7 -0
- package/dist/feedback/toaster.js +29 -0
- package/dist/form/FormDialog.d.ts +34 -0
- package/dist/form/FormDialog.js +36 -0
- package/dist/form/errors.d.ts +8 -0
- package/dist/form/errors.js +17 -0
- package/dist/form/fields/CheckboxField.d.ts +4 -0
- package/dist/form/fields/CheckboxField.js +25 -0
- package/dist/form/fields/FormField.d.ts +20 -0
- package/dist/form/fields/FormField.js +24 -0
- package/dist/form/fields/InputField.d.ts +5 -0
- package/dist/form/fields/InputField.js +21 -0
- package/dist/form/fields/NumberField.d.ts +10 -0
- package/dist/form/fields/NumberField.js +26 -0
- package/dist/form/fields/RadioGroupField.d.ts +8 -0
- package/dist/form/fields/RadioGroupField.js +33 -0
- package/dist/form/fields/SelectField.d.ts +12 -0
- package/dist/form/fields/SelectField.js +25 -0
- package/dist/form/fields/TextAreaField.d.ts +5 -0
- package/dist/form/fields/TextAreaField.js +21 -0
- package/dist/form/fields/formComponents.d.ts +18 -0
- package/dist/form/fields/formComponents.js +50 -0
- package/dist/form/form.d.ts +73 -0
- package/dist/form/form.js +54 -0
- package/dist/form/formContext.d.ts +2 -0
- package/dist/form/formContext.js +4 -0
- package/dist/form/index.d.ts +13 -0
- package/dist/form/index.js +14 -0
- package/dist/form/values.d.ts +20 -0
- package/dist/form/values.js +24 -0
- package/dist/paging/index.d.ts +1 -0
- package/dist/paging/index.js +1 -0
- package/dist/paging/paging.d.ts +80 -0
- package/dist/paging/paging.js +71 -0
- package/dist/problem/index.d.ts +1 -0
- package/dist/problem/index.js +1 -0
- package/dist/problem/problem.d.ts +22 -0
- package/dist/problem/problem.js +24 -0
- package/dist/table/FilterChips.d.ts +16 -0
- package/dist/table/FilterChips.js +28 -0
- package/dist/table/QueryTable.d.ts +11 -0
- package/dist/table/QueryTable.js +29 -0
- package/dist/table/SimpleQueryTable.d.ts +17 -0
- package/dist/table/SimpleQueryTable.js +37 -0
- package/dist/table/TablePageSizeSelect.d.ts +9 -0
- package/dist/table/TablePageSizeSelect.js +35 -0
- package/dist/table/TablePagination.d.ts +5 -0
- package/dist/table/TablePagination.js +65 -0
- package/dist/table/TableStatus.d.ts +6 -0
- package/dist/table/TableStatus.js +27 -0
- package/dist/table/TableView.d.ts +7 -0
- package/dist/table/TableView.js +31 -0
- package/dist/table/TanstackTable.d.ts +16 -0
- package/dist/table/TanstackTable.js +61 -0
- package/dist/table/index.d.ts +11 -0
- package/dist/table/index.js +12 -0
- package/dist/table/selectColumn.d.ts +17 -0
- package/dist/table/selectColumn.js +36 -0
- package/dist/table/useDebounce.d.ts +2 -0
- package/dist/table/useDebounce.js +10 -0
- package/dist/table/useQueryTable.d.ts +49 -0
- package/dist/table/useQueryTable.js +127 -0
- package/package.json +78 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ColumnDef } from "@tanstack/react-table";
|
|
2
|
+
export interface SelectColumnOptions<T> {
|
|
3
|
+
/** The selected rows, by key; they may span pages. */
|
|
4
|
+
selected: ReadonlyMap<string, T>;
|
|
5
|
+
onSelectedChange: (selected: Map<string, T>) => void;
|
|
6
|
+
/** A row's stable key, such as its id: rows are new objects on every fetch. */
|
|
7
|
+
keyOf: (row: T) => string;
|
|
8
|
+
/** Names a row for its checkbox: "Select <label>". */
|
|
9
|
+
labelOf: (row: T) => string;
|
|
10
|
+
/** The header checkbox's label. */
|
|
11
|
+
selectAllLabel?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A checkbox column for choosing rows, across pages. The header checkbox selects or unselects
|
|
15
|
+
* the rows on the page shown, leaving other pages' selections alone.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createSelectColumn<T>({ selected, onSelectedChange, keyOf, labelOf, selectAllLabel, }: SelectColumnOptions<T>): ColumnDef<T, unknown>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Checkbox as e } from "@chakra-ui/react";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
import { createColumnHelper as r } from "@tanstack/react-table";
|
|
4
|
+
// src/table/selectColumn.tsx
|
|
5
|
+
function i({ selected: i, onSelectedChange: a, keyOf: o, labelOf: s, selectAllLabel: c = "Select every row on this page" }) {
|
|
6
|
+
return r().display({
|
|
7
|
+
id: "select",
|
|
8
|
+
header: ({ table: r }) => {
|
|
9
|
+
let s = r.getRowModel().rows.map((e) => e.original), l = s.filter((e) => i.has(o(e))).length, u = s.length > 0 && l === s.length || l > 0 && "indeterminate";
|
|
10
|
+
function d(e) {
|
|
11
|
+
let t = new Map(i);
|
|
12
|
+
for (let n of s) e ? t.set(o(n), n) : t.delete(o(n));
|
|
13
|
+
a(t);
|
|
14
|
+
}
|
|
15
|
+
return /* @__PURE__ */ n(e.Root, {
|
|
16
|
+
checked: u,
|
|
17
|
+
disabled: s.length === 0,
|
|
18
|
+
onCheckedChange: () => d(u !== !0),
|
|
19
|
+
children: [/* @__PURE__ */ t(e.HiddenInput, { "aria-label": c }), /* @__PURE__ */ t(e.Control, { children: /* @__PURE__ */ t(e.Indicator, {}) })]
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
cell: ({ row: r }) => {
|
|
23
|
+
let c = o(r.original), l = i.has(c);
|
|
24
|
+
function u(e) {
|
|
25
|
+
let t = new Map(i);
|
|
26
|
+
e ? t.set(c, r.original) : t.delete(c), a(t);
|
|
27
|
+
}
|
|
28
|
+
return /* @__PURE__ */ n(e.Root, {
|
|
29
|
+
checked: l,
|
|
30
|
+
onCheckedChange: (e) => u(e.checked === !0),
|
|
31
|
+
children: [/* @__PURE__ */ t(e.HiddenInput, { "aria-label": `Select ${s(r.original)}` }), /* @__PURE__ */ t(e.Control, { children: /* @__PURE__ */ t(e.Indicator, {}) })]
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export { i as createSelectColumn };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useEffect as e, useState as t } from "react";
|
|
2
|
+
// src/table/useDebounce.ts
|
|
3
|
+
function n(n, r) {
|
|
4
|
+
let [i, a] = t(n);
|
|
5
|
+
return e(() => {
|
|
6
|
+
let e = setTimeout(() => a(n), r);
|
|
7
|
+
return () => clearTimeout(e);
|
|
8
|
+
}, [n, r]), i;
|
|
9
|
+
}
|
|
10
|
+
export { n as useDebounce };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type QueryKey } from "@tanstack/react-query";
|
|
2
|
+
import { type ColumnDef, type ColumnSort, type Table } from "@tanstack/react-table";
|
|
3
|
+
import { type PageDto, type PageParams, type SortDirection, type TableSearch } from "../paging/paging";
|
|
4
|
+
export declare const DEFAULT_PAGE_SIZE = 25;
|
|
5
|
+
export interface UseQueryTableOptions<T> {
|
|
6
|
+
/**
|
|
7
|
+
* Names the list, including any search or filter the page applies (`query` closes over the
|
|
8
|
+
* same values). A different key is a different list, which starts on its first page.
|
|
9
|
+
*/
|
|
10
|
+
queryKey: QueryKey;
|
|
11
|
+
/** Fetches one page; the table supplies the page, page size and sort. */
|
|
12
|
+
query: (params: PageParams) => Promise<PageDto<T>>;
|
|
13
|
+
/** Column ids of sortable columns are the endpoint's sort names (its sort enum's values). */
|
|
14
|
+
columns: ColumnDef<T, unknown>[];
|
|
15
|
+
initialSort?: ColumnSort;
|
|
16
|
+
/** Rows per page, at most {@link MAX_PAGE_SIZE}; {@link DEFAULT_PAGE_SIZE} unless given. */
|
|
17
|
+
initialPageSize?: number;
|
|
18
|
+
/** A stable id per row, such as the entity's id; the row's index on the page otherwise. */
|
|
19
|
+
getRowId?: (row: T) => string;
|
|
20
|
+
/**
|
|
21
|
+
* The page and sort, when the caller keeps them (in the URL, say): the table shows these, with
|
|
22
|
+
* the defaults above for what is absent, and reports every change to {@link onParamsChange}
|
|
23
|
+
* instead of keeping its own. Its `search` is ignored; put the search in `queryKey` and `query`,
|
|
24
|
+
* and set `pageIndex` back to 0 in the same update that changes it.
|
|
25
|
+
*/
|
|
26
|
+
params?: TableSearch;
|
|
27
|
+
onParamsChange?: (params: TableParams) => void;
|
|
28
|
+
}
|
|
29
|
+
/** A table's page and sort, as reported to {@link UseQueryTableOptions.onParamsChange}. */
|
|
30
|
+
export interface TableParams {
|
|
31
|
+
pageIndex: number;
|
|
32
|
+
pageSize: number;
|
|
33
|
+
sortBy?: string;
|
|
34
|
+
sortDirection?: SortDirection;
|
|
35
|
+
}
|
|
36
|
+
export interface UseQueryTableResult<T> {
|
|
37
|
+
table: Table<T>;
|
|
38
|
+
/** The page on screen: the last one loaded, while the next one loads. */
|
|
39
|
+
page: PageDto<T> | undefined;
|
|
40
|
+
isLoading: boolean;
|
|
41
|
+
isError: boolean;
|
|
42
|
+
refetch: () => Promise<unknown>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A TanStack table over a jpa-utils paged endpoint: the server pages and sorts, TanStack Query
|
|
46
|
+
* fetches and caches, and the table holds the page and sort the user chose, or reports them to a
|
|
47
|
+
* caller that keeps them ({@link UseQueryTableOptions.params}).
|
|
48
|
+
*/
|
|
49
|
+
export declare function useQueryTable<T>({ queryKey, query, columns, initialSort, initialPageSize, getRowId, params, onParamsChange, }: UseQueryTableOptions<T>): UseQueryTableResult<T>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { pageParamsOf as e } from "../paging/paging.js";
|
|
2
|
+
import { hashKey as t, keepPreviousData as n, useQuery as r } from "@tanstack/react-query";
|
|
3
|
+
import { functionalUpdate as i, getCoreRowModel as a, useReactTable as o } from "@tanstack/react-table";
|
|
4
|
+
import { useEffect as s, useRef as c, useState as l } from "react";
|
|
5
|
+
// src/table/useQueryTable.ts
|
|
6
|
+
var u = 25, d = [];
|
|
7
|
+
function f(e, t, n) {
|
|
8
|
+
return {
|
|
9
|
+
pagination: {
|
|
10
|
+
pageIndex: g(e.pageIndex ?? 0),
|
|
11
|
+
pageSize: h(e.pageSize ?? n)
|
|
12
|
+
},
|
|
13
|
+
sorting: p(e, t)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function p(e, t) {
|
|
17
|
+
if (e.sortBy !== void 0) return [{
|
|
18
|
+
id: e.sortBy,
|
|
19
|
+
desc: e.sortDirection === "DESC"
|
|
20
|
+
}];
|
|
21
|
+
if (t === void 0) return [];
|
|
22
|
+
let n = e.sortDirection === void 0 ? t.desc : e.sortDirection === "DESC";
|
|
23
|
+
return [{
|
|
24
|
+
id: t.id,
|
|
25
|
+
desc: n
|
|
26
|
+
}];
|
|
27
|
+
}
|
|
28
|
+
function m({ pagination: e, sorting: t }) {
|
|
29
|
+
let { pageIndex: n, pageSize: r } = e, i = t[0];
|
|
30
|
+
return i ? {
|
|
31
|
+
pageIndex: n,
|
|
32
|
+
pageSize: r,
|
|
33
|
+
sortBy: i.id,
|
|
34
|
+
sortDirection: i.desc ? "DESC" : "ASC"
|
|
35
|
+
} : {
|
|
36
|
+
pageIndex: n,
|
|
37
|
+
pageSize: r
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function h(e) {
|
|
41
|
+
return Number.isFinite(e) ? Math.min(Math.max(1, Math.floor(e)), 100) : 25;
|
|
42
|
+
}
|
|
43
|
+
function g(e) {
|
|
44
|
+
return Number.isFinite(e) ? Math.max(0, Math.floor(e)) : 0;
|
|
45
|
+
}
|
|
46
|
+
function _({ queryKey: u, query: p, columns: _, initialSort: v, initialPageSize: y = 25, getRowId: b, params: x, onParamsChange: S }) {
|
|
47
|
+
let C = x !== void 0, [w, T] = l(() => f({}, v, y)), E = C ? f(x, v, y) : w, D = c(S);
|
|
48
|
+
s(() => {
|
|
49
|
+
D.current = S;
|
|
50
|
+
}, [S]);
|
|
51
|
+
function O(e) {
|
|
52
|
+
let t = {
|
|
53
|
+
pagination: {
|
|
54
|
+
pageIndex: g(e.pagination.pageIndex),
|
|
55
|
+
pageSize: h(e.pagination.pageSize)
|
|
56
|
+
},
|
|
57
|
+
sorting: e.sorting
|
|
58
|
+
};
|
|
59
|
+
C ? D.current?.(m(t)) : T(t);
|
|
60
|
+
}
|
|
61
|
+
let k = t(u), [A, j] = l(k);
|
|
62
|
+
A !== k && (j(k), T({
|
|
63
|
+
...w,
|
|
64
|
+
pagination: {
|
|
65
|
+
...w.pagination,
|
|
66
|
+
pageIndex: 0
|
|
67
|
+
}
|
|
68
|
+
}));
|
|
69
|
+
let M = E.pagination, N = e(M, E.sorting), P = r({
|
|
70
|
+
queryKey: [...u, N],
|
|
71
|
+
queryFn: () => p(N),
|
|
72
|
+
placeholderData: n
|
|
73
|
+
}), F = P.data, I = P.isPlaceholderData ? Infinity : F?.pagination.totalPageCount ?? Infinity, L = M.pageIndex > 0 && M.pageIndex >= I ? Math.max(0, I - 1) : void 0, R = M.pageSize, z = E.sorting[0]?.id, B = E.sorting[0]?.desc === !0;
|
|
74
|
+
return s(() => {
|
|
75
|
+
if (L === void 0) return;
|
|
76
|
+
let e = {
|
|
77
|
+
pagination: {
|
|
78
|
+
pageIndex: L,
|
|
79
|
+
pageSize: R
|
|
80
|
+
},
|
|
81
|
+
sorting: z === void 0 ? [] : [{
|
|
82
|
+
id: z,
|
|
83
|
+
desc: B
|
|
84
|
+
}]
|
|
85
|
+
};
|
|
86
|
+
C ? D.current?.(m(e)) : T(e);
|
|
87
|
+
}, [
|
|
88
|
+
L,
|
|
89
|
+
C,
|
|
90
|
+
R,
|
|
91
|
+
z,
|
|
92
|
+
B
|
|
93
|
+
]), {
|
|
94
|
+
table: o({
|
|
95
|
+
data: F?.data ?? d,
|
|
96
|
+
columns: _,
|
|
97
|
+
getRowId: b,
|
|
98
|
+
rowCount: F?.pagination.totalElementCount ?? 0,
|
|
99
|
+
pageCount: F?.pagination.totalPageCount ?? 0,
|
|
100
|
+
state: {
|
|
101
|
+
pagination: M,
|
|
102
|
+
sorting: E.sorting
|
|
103
|
+
},
|
|
104
|
+
onPaginationChange: (e) => O({
|
|
105
|
+
...E,
|
|
106
|
+
pagination: i(e, M)
|
|
107
|
+
}),
|
|
108
|
+
onSortingChange: (e) => O({
|
|
109
|
+
sorting: i(e, E.sorting),
|
|
110
|
+
pagination: {
|
|
111
|
+
...M,
|
|
112
|
+
pageIndex: 0
|
|
113
|
+
}
|
|
114
|
+
}),
|
|
115
|
+
getCoreRowModel: a(),
|
|
116
|
+
autoResetPageIndex: !1,
|
|
117
|
+
enableMultiSort: !1,
|
|
118
|
+
enableSortingRemoval: !1,
|
|
119
|
+
sortDescFirst: !1
|
|
120
|
+
}),
|
|
121
|
+
page: F,
|
|
122
|
+
isLoading: P.isLoading,
|
|
123
|
+
isError: P.isError,
|
|
124
|
+
refetch: P.refetch
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export { u as DEFAULT_PAGE_SIZE, _ as useQueryTable };
|
package/package.json
CHANGED
|
@@ -1,11 +1,87 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jwcarman/roux-web",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/jwcarman/roux.git",
|
|
9
9
|
"directory": "web/roux-web"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/jwcarman/roux#readme",
|
|
12
|
+
"bugs": "https://github.com/jwcarman/roux/issues",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"exports": {
|
|
18
|
+
"./problem": {
|
|
19
|
+
"types": "./dist/problem/index.d.ts",
|
|
20
|
+
"import": "./dist/problem/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./api": {
|
|
23
|
+
"types": "./dist/api/index.d.ts",
|
|
24
|
+
"import": "./dist/api/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./paging": {
|
|
27
|
+
"types": "./dist/paging/index.d.ts",
|
|
28
|
+
"import": "./dist/paging/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./table": {
|
|
31
|
+
"types": "./dist/table/index.d.ts",
|
|
32
|
+
"import": "./dist/table/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./feedback": {
|
|
35
|
+
"types": "./dist/feedback/index.d.ts",
|
|
36
|
+
"import": "./dist/feedback/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./form": {
|
|
39
|
+
"types": "./dist/form/index.d.ts",
|
|
40
|
+
"import": "./dist/form/index.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json --emitDeclarationOnly && vite build",
|
|
45
|
+
"typecheck": "tsc -p tsconfig.test.json",
|
|
46
|
+
"test": "vitest run",
|
|
47
|
+
"coverage": "vitest run --coverage",
|
|
48
|
+
"mutate": "stryker run",
|
|
49
|
+
"format": "../lint-tools/node_modules/.bin/prettier --write .",
|
|
50
|
+
"format:check": "../lint-tools/node_modules/.bin/prettier --check .",
|
|
51
|
+
"lint": "../lint-tools/node_modules/.bin/eslint -c ../lint-tools/eslint.config.mjs ."
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@chakra-ui/react": "^3.38.0",
|
|
55
|
+
"@emotion/react": "^11.14.0",
|
|
56
|
+
"@tanstack/react-form": "^1.33.5",
|
|
57
|
+
"@tanstack/react-query": "^5.107.1",
|
|
58
|
+
"@tanstack/react-table": "^8.21.3",
|
|
59
|
+
"react": "^19.3.1",
|
|
60
|
+
"react-icons": "^5.7.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@chakra-ui/react": "3.38.0",
|
|
64
|
+
"@emotion/react": "11.14.0",
|
|
65
|
+
"@stryker-mutator/core": "10.1.1",
|
|
66
|
+
"@stryker-mutator/vitest-runner": "10.1.1",
|
|
67
|
+
"@tanstack/react-form": "1.33.5",
|
|
68
|
+
"@tanstack/react-query": "5.107.1",
|
|
69
|
+
"@tanstack/react-table": "8.21.3",
|
|
70
|
+
"@testing-library/dom": "10.4.2",
|
|
71
|
+
"@testing-library/jest-dom": "7.0.1",
|
|
72
|
+
"@testing-library/react": "16.3.3",
|
|
73
|
+
"@testing-library/user-event": "14.6.8",
|
|
74
|
+
"@types/node": "26.6.6",
|
|
75
|
+
"@types/react": "19.3.0",
|
|
76
|
+
"@types/react-dom": "19.3.0",
|
|
77
|
+
"@vitest/coverage-v8": "5.1.0",
|
|
78
|
+
"jsdom": "31.0.0",
|
|
79
|
+
"msw": "3.0.5",
|
|
80
|
+
"react": "19.3.1",
|
|
81
|
+
"react-dom": "19.3.1",
|
|
82
|
+
"react-icons": "5.7.0",
|
|
83
|
+
"typescript": "7.0.2",
|
|
84
|
+
"vite": "8.3.6",
|
|
85
|
+
"vitest": "5.1.0"
|
|
10
86
|
}
|
|
11
87
|
}
|