@jwcarman/roux-web 0.0.0-stage → 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.
Files changed (81) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +21 -2
  3. package/dist/api/client.d.ts +17 -0
  4. package/dist/api/client.js +57 -0
  5. package/dist/api/config.d.ts +11 -0
  6. package/dist/api/config.js +15 -0
  7. package/dist/api/index.d.ts +3 -0
  8. package/dist/api/index.js +4 -0
  9. package/dist/api/serverAction.d.ts +23 -0
  10. package/dist/api/serverAction.js +28 -0
  11. package/dist/feedback/ConfirmProvider.d.ts +21 -0
  12. package/dist/feedback/ConfirmProvider.js +50 -0
  13. package/dist/feedback/ErrorMessage.d.ts +7 -0
  14. package/dist/feedback/ErrorMessage.js +11 -0
  15. package/dist/feedback/index.d.ts +4 -0
  16. package/dist/feedback/index.js +5 -0
  17. package/dist/feedback/notify.d.ts +16 -0
  18. package/dist/feedback/notify.js +27 -0
  19. package/dist/feedback/toaster.d.ts +7 -0
  20. package/dist/feedback/toaster.js +29 -0
  21. package/dist/form/FormDialog.d.ts +34 -0
  22. package/dist/form/FormDialog.js +36 -0
  23. package/dist/form/errors.d.ts +8 -0
  24. package/dist/form/errors.js +17 -0
  25. package/dist/form/fields/CheckboxField.d.ts +4 -0
  26. package/dist/form/fields/CheckboxField.js +25 -0
  27. package/dist/form/fields/FormField.d.ts +20 -0
  28. package/dist/form/fields/FormField.js +24 -0
  29. package/dist/form/fields/InputField.d.ts +5 -0
  30. package/dist/form/fields/InputField.js +21 -0
  31. package/dist/form/fields/NumberField.d.ts +10 -0
  32. package/dist/form/fields/NumberField.js +26 -0
  33. package/dist/form/fields/RadioGroupField.d.ts +8 -0
  34. package/dist/form/fields/RadioGroupField.js +33 -0
  35. package/dist/form/fields/SelectField.d.ts +12 -0
  36. package/dist/form/fields/SelectField.js +25 -0
  37. package/dist/form/fields/TextAreaField.d.ts +5 -0
  38. package/dist/form/fields/TextAreaField.js +21 -0
  39. package/dist/form/fields/formComponents.d.ts +18 -0
  40. package/dist/form/fields/formComponents.js +50 -0
  41. package/dist/form/form.d.ts +73 -0
  42. package/dist/form/form.js +54 -0
  43. package/dist/form/formContext.d.ts +2 -0
  44. package/dist/form/formContext.js +4 -0
  45. package/dist/form/index.d.ts +13 -0
  46. package/dist/form/index.js +14 -0
  47. package/dist/form/values.d.ts +20 -0
  48. package/dist/form/values.js +24 -0
  49. package/dist/paging/index.d.ts +1 -0
  50. package/dist/paging/index.js +1 -0
  51. package/dist/paging/paging.d.ts +80 -0
  52. package/dist/paging/paging.js +71 -0
  53. package/dist/problem/index.d.ts +1 -0
  54. package/dist/problem/index.js +1 -0
  55. package/dist/problem/problem.d.ts +22 -0
  56. package/dist/problem/problem.js +24 -0
  57. package/dist/table/FilterChips.d.ts +16 -0
  58. package/dist/table/FilterChips.js +28 -0
  59. package/dist/table/QueryTable.d.ts +11 -0
  60. package/dist/table/QueryTable.js +29 -0
  61. package/dist/table/SimpleQueryTable.d.ts +17 -0
  62. package/dist/table/SimpleQueryTable.js +37 -0
  63. package/dist/table/TablePageSizeSelect.d.ts +9 -0
  64. package/dist/table/TablePageSizeSelect.js +35 -0
  65. package/dist/table/TablePagination.d.ts +5 -0
  66. package/dist/table/TablePagination.js +65 -0
  67. package/dist/table/TableStatus.d.ts +6 -0
  68. package/dist/table/TableStatus.js +27 -0
  69. package/dist/table/TableView.d.ts +7 -0
  70. package/dist/table/TableView.js +31 -0
  71. package/dist/table/TanstackTable.d.ts +16 -0
  72. package/dist/table/TanstackTable.js +61 -0
  73. package/dist/table/index.d.ts +11 -0
  74. package/dist/table/index.js +12 -0
  75. package/dist/table/selectColumn.d.ts +17 -0
  76. package/dist/table/selectColumn.js +36 -0
  77. package/dist/table/useDebounce.d.ts +2 -0
  78. package/dist/table/useDebounce.js +10 -0
  79. package/dist/table/useQueryTable.d.ts +49 -0
  80. package/dist/table/useQueryTable.js +127 -0
  81. package/package.json +85 -4
@@ -0,0 +1,80 @@
1
+ /** How the client talks to jpa-utils' paged endpoints: `PageParams` in, `PageDto` out. */
2
+ export type SortDirection = "ASC" | "DESC";
3
+ /** jpa-utils' page of results: one page of `data` and where it sits in the whole. */
4
+ export interface PaginationDto {
5
+ pageIndex: number;
6
+ pageSize: number;
7
+ totalElementCount: number;
8
+ totalPageCount: number;
9
+ hasNext: boolean;
10
+ hasPrevious: boolean;
11
+ }
12
+ export interface PageDto<T> {
13
+ data: T[];
14
+ pagination: PaginationDto;
15
+ }
16
+ /** What a paged list endpoint accepts (jpa-utils' PageParams, plus a search term). */
17
+ export interface PageParams {
18
+ pageIndex?: number;
19
+ pageSize?: number;
20
+ sortBy?: string;
21
+ sortDirection?: SortDirection;
22
+ search?: string;
23
+ }
24
+ /** The most rows a jpa-utils `Paginator` returns per page by default. */
25
+ export declare const MAX_PAGE_SIZE = 100;
26
+ /** The query string for `params`: `""` when there is nothing to send, else `"?a=b&c=d"`. */
27
+ export declare function pageQuery(params: PageParams & Record<string, string | number | undefined>): string;
28
+ /**
29
+ * The request for a table's state. Column ids are the endpoint's sort names (its sort enum's
30
+ * values), so the first sorted column becomes `sortBy`; with none, the server's default applies.
31
+ */
32
+ export declare function pageParamsOf(pagination: {
33
+ pageIndex: number;
34
+ pageSize: number;
35
+ }, sorting: readonly {
36
+ id: string;
37
+ desc: boolean;
38
+ }[]): PageParams;
39
+ /** The term to search for: `text` trimmed, or absent while blank or shorter than `minLength`. */
40
+ export declare function searchTermOf(text: string, minLength: number): string | undefined;
41
+ /** The index of the page after `page`, or absent on the last page. */
42
+ export declare function nextPageIndex(page: PageDto<unknown>): number | undefined;
43
+ /** Which rows a page shows, counting from 1: "Showing `first` to `last` of `total`". */
44
+ export declare function rowRange(pagination: Pick<PaginationDto, "pageIndex" | "pageSize" | "totalElementCount">, rowsOnPage: number): {
45
+ first: number;
46
+ last: number;
47
+ total: number;
48
+ };
49
+ /** How many pages {@link fetchAllPages} fetches before it gives up: 10,000 rows at 100 a page. */
50
+ export declare const MAX_PAGES = 100;
51
+ /**
52
+ * Every row of a paged endpoint, fetched page by page, for a dropdown or an export that needs the
53
+ * whole list. Pages are {@link MAX_PAGE_SIZE} rows unless `params` says otherwise; its other
54
+ * parameters go with every request.
55
+ *
56
+ * @throws Error past `maxPages` pages ({@link MAX_PAGES} by default), or if the server answers
57
+ * with a page other than the one asked for: a list that big wants paging, not one fetch.
58
+ */
59
+ export declare function fetchAllPages<T>(fetcher: (params: PageParams) => Promise<PageDto<T>>, params?: PageParams, { maxPages }?: {
60
+ maxPages?: number;
61
+ }): Promise<T[]>;
62
+ /** A table's page, page size, sort and search, as kept in the URL; absent means the default. */
63
+ export interface TableSearch<S extends string = string> {
64
+ pageIndex?: number;
65
+ pageSize?: number;
66
+ sortBy?: S;
67
+ sortDirection?: SortDirection;
68
+ search?: string;
69
+ }
70
+ /**
71
+ * A table's search params from a URL (a route's `validateSearch`). Anyone can type a URL, so
72
+ * values are checked, not trusted: an unknown sort, a bad page or a blank search is dropped, and
73
+ * the page size is capped at {@link MAX_PAGE_SIZE}.
74
+ */
75
+ export declare function tableSearchOf<S extends string>(raw: Record<string, unknown>, sorts: readonly S[]): TableSearch<S>;
76
+ /**
77
+ * A filter's value from a URL (a route's `validateSearch`): the value if `values` offers it, else
78
+ * absent, so a typed or stale URL never sends the server a value it would refuse.
79
+ */
80
+ export declare function enumSearchOf<T extends string>(raw: unknown, values: readonly T[]): T | undefined;
@@ -0,0 +1,71 @@
1
+ // src/paging/paging.ts
2
+ var e = 100;
3
+ function t(e) {
4
+ let t = new URLSearchParams();
5
+ for (let [n, r] of Object.entries(e)) r !== void 0 && String(r).trim() !== "" && t.set(n, String(r));
6
+ let n = t.toString();
7
+ return n === "" ? "" : `?${n}`;
8
+ }
9
+ function n(e, t) {
10
+ let n = {
11
+ pageIndex: e.pageIndex,
12
+ pageSize: e.pageSize
13
+ }, r = t[0];
14
+ return r && (n.sortBy = r.id, n.sortDirection = r.desc ? "DESC" : "ASC"), n;
15
+ }
16
+ function r(e, t) {
17
+ let n = e.trim();
18
+ return n.length > 0 && n.length >= t ? n : void 0;
19
+ }
20
+ function i(e) {
21
+ return e.pagination.hasNext ? e.pagination.pageIndex + 1 : void 0;
22
+ }
23
+ function a(e, t) {
24
+ if (t === 0) return {
25
+ first: 0,
26
+ last: 0,
27
+ total: e.totalElementCount
28
+ };
29
+ let n = e.pageIndex * e.pageSize + 1;
30
+ return {
31
+ first: n,
32
+ last: n + t - 1,
33
+ total: e.totalElementCount
34
+ };
35
+ }
36
+ var o = 100;
37
+ async function s(e, t = {}, { maxPages: n = 100 } = {}) {
38
+ let r = [], a = t.pageIndex ?? 0;
39
+ for (let o = 0; a !== void 0; o++) {
40
+ if (o === n) throw Error(`fetchAllPages: the list has more than ${n} pages`);
41
+ let s = await e({
42
+ ...t,
43
+ pageIndex: a,
44
+ pageSize: t.pageSize ?? 100
45
+ });
46
+ if (s.pagination.pageIndex !== a) throw Error(`fetchAllPages: asked for page ${a}, got page ${s.pagination.pageIndex} again`);
47
+ r.push(...s.data), a = i(s);
48
+ }
49
+ return r;
50
+ }
51
+ function c(e) {
52
+ return Number.isInteger(e);
53
+ }
54
+ function l(e, t) {
55
+ let n = typeof e == "string" && e.trim() !== "" ? Number(e) : e;
56
+ return c(n) && n >= t ? n : void 0;
57
+ }
58
+ function u(e, t) {
59
+ let n = l(e.pageIndex, 0), i = l(e.pageSize, 1), a = typeof e.sortDirection == "string" ? e.sortDirection.toUpperCase() : void 0;
60
+ return {
61
+ pageIndex: n,
62
+ pageSize: i === void 0 ? void 0 : Math.min(i, 100),
63
+ sortBy: t.find((t) => t === e.sortBy),
64
+ sortDirection: a === "ASC" || a === "DESC" ? a : void 0,
65
+ search: typeof e.search == "string" ? r(e.search, 0) : void 0
66
+ };
67
+ }
68
+ function d(e, t) {
69
+ return t.find((t) => t === e);
70
+ }
71
+ export { o as MAX_PAGES, e as MAX_PAGE_SIZE, d as enumSearchOf, s as fetchAllPages, i as nextPageIndex, n as pageParamsOf, t as pageQuery, a as rowRange, r as searchTermOf, u as tableSearchOf };
@@ -0,0 +1 @@
1
+ export { problemOf, type FieldProblem, type Problem } from "./problem";
@@ -0,0 +1 @@
1
+ export { problemOf } from "./problem.js";
@@ -0,0 +1,22 @@
1
+ /** One field's problem in a 400. */
2
+ export interface FieldProblem {
3
+ field: string;
4
+ message: string;
5
+ }
6
+ /** An RFC 9457 Problem Detail, as the server answers every error. */
7
+ export interface Problem {
8
+ /** `about:blank` when the server sent none, as RFC 9457 defines. */
9
+ type: string;
10
+ title: string;
11
+ status: number;
12
+ detail?: string;
13
+ instance?: string;
14
+ errors?: FieldProblem[];
15
+ /** On a 500 only: the reference the server logged the failure under. */
16
+ errorId?: string;
17
+ }
18
+ /**
19
+ * The problem in a failed response. A response that is not `application/problem+json`, or whose
20
+ * body cannot be read, gives a problem built from its status. Never throws.
21
+ */
22
+ export declare function problemOf(response: Response): Promise<Problem>;
@@ -0,0 +1,24 @@
1
+ // src/problem/problem.ts
2
+ function e(e) {
3
+ let t = e;
4
+ return typeof t?.field == "string" && typeof t.message == "string";
5
+ }
6
+ async function t(t) {
7
+ let n = {
8
+ type: "about:blank",
9
+ title: t.statusText,
10
+ status: t.status
11
+ };
12
+ if (!t.headers.get("Content-Type")?.includes("application/problem+json")) return n;
13
+ try {
14
+ let r = Object(await t.json()), i = {
15
+ type: typeof r.type == "string" ? r.type : "about:blank",
16
+ title: typeof r.title == "string" ? r.title : n.title,
17
+ status: typeof r.status == "number" ? r.status : n.status
18
+ };
19
+ return typeof r.detail == "string" && (i.detail = r.detail), typeof r.instance == "string" && (i.instance = r.instance), typeof r.errorId == "string" && (i.errorId = r.errorId), Array.isArray(r.errors) && r.errors.length > 0 && r.errors.every(e) && (i.errors = r.errors), i;
20
+ } catch {
21
+ return n;
22
+ }
23
+ }
24
+ export { t as problemOf };
@@ -0,0 +1,16 @@
1
+ export interface FilterOption<T extends string> {
2
+ value: T;
3
+ label: string;
4
+ }
5
+ export interface FilterChipsProps<T extends string> {
6
+ /** Names the group for screen readers, such as "Status". */
7
+ label: string;
8
+ options: readonly FilterOption<T>[];
9
+ /** The chosen value; undefined for no filter. */
10
+ value: T | undefined;
11
+ onChange: (value: T | undefined) => void;
12
+ /** Adds a first chip that clears the filter, such as "All". */
13
+ allLabel?: string;
14
+ }
15
+ /** One filter as a row of toggle chips (`aria-pressed`); one chip is chosen at a time. */
16
+ export declare function FilterChips<T extends string>({ label, options, value, onChange, allLabel }: FilterChipsProps<T>): import("react").JSX.Element;
@@ -0,0 +1,28 @@
1
+ import { Button as e, Group as t } from "@chakra-ui/react";
2
+ import { jsx as n } from "react/jsx-runtime";
3
+ // src/table/FilterChips.tsx
4
+ function r({ label: r, options: i, value: a, onChange: o, allLabel: s }) {
5
+ let c = s ? [{
6
+ value: void 0,
7
+ label: s
8
+ }, ...i] : [...i];
9
+ return /* @__PURE__ */ n(t, {
10
+ role: "group",
11
+ "aria-label": r,
12
+ attached: !1,
13
+ gap: "2",
14
+ wrap: "wrap",
15
+ children: c.map((t) => {
16
+ let r = t.value === a;
17
+ return /* @__PURE__ */ n(e, {
18
+ size: "xs",
19
+ borderRadius: "full",
20
+ variant: r ? "solid" : "outline",
21
+ "aria-pressed": r,
22
+ onClick: () => o(t.value),
23
+ children: t.label
24
+ }, t.label);
25
+ })
26
+ });
27
+ }
28
+ export { r as FilterChips };
@@ -0,0 +1,11 @@
1
+ import { type UseQueryTableOptions } from "./useQueryTable";
2
+ import { type TableViewProps } from "./TableView";
3
+ export type QueryTableProps<T> = UseQueryTableOptions<T> & Omit<TableViewProps<T>, "table" | "isLoading" | "isError" | "columns"> & {
4
+ /** Shown above the table when a page cannot be loaded. */
5
+ errorText?: string;
6
+ };
7
+ /**
8
+ * A paged, sortable table over a jpa-utils endpoint. Put any search or filter in `queryKey`
9
+ * (and use it in `query`); a change returns the table to its first page.
10
+ */
11
+ export declare function QueryTable<T>({ queryKey, query, columns, initialSort, initialPageSize, getRowId, params, onParamsChange, errorText, ...props }: QueryTableProps<T>): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { ErrorMessage as e } from "../feedback/ErrorMessage.js";
2
+ import { useQueryTable as t } from "./useQueryTable.js";
3
+ import { TableView as n } from "./TableView.js";
4
+ import { Stack as r } from "@chakra-ui/react";
5
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
6
+ // src/table/QueryTable.tsx
7
+ function o({ queryKey: o, query: s, columns: c, initialSort: l, initialPageSize: u, getRowId: d, params: f, onParamsChange: p, errorText: m = "Could not load the rows.", ...h }) {
8
+ let { table: g, isLoading: _, isError: v } = t({
9
+ queryKey: o,
10
+ query: s,
11
+ columns: c,
12
+ initialSort: l,
13
+ initialPageSize: u,
14
+ getRowId: d,
15
+ params: f,
16
+ onParamsChange: p
17
+ });
18
+ return /* @__PURE__ */ a(r, {
19
+ gap: "2",
20
+ width: "100%",
21
+ children: [v && /* @__PURE__ */ i(e, { children: m }), /* @__PURE__ */ i(n, {
22
+ table: g,
23
+ isLoading: _,
24
+ isError: v,
25
+ ...h
26
+ })]
27
+ });
28
+ }
29
+ export { o as QueryTable };
@@ -0,0 +1,17 @@
1
+ import { type QueryKey } from "@tanstack/react-query";
2
+ import { type ColumnDef, type ColumnSort } from "@tanstack/react-table";
3
+ import { type TanstackTableProps } from "./TanstackTable";
4
+ export type SimpleQueryTableProps<T> = Omit<TanstackTableProps<T>, "table" | "isLoading" | "isError" | "columns"> & {
5
+ queryKey: QueryKey;
6
+ /** Fetches every row at once; for a short list, or with `fetchAllPages`. */
7
+ query: () => Promise<T[]>;
8
+ columns: ColumnDef<T, unknown>[];
9
+ initialSort?: ColumnSort;
10
+ /** False holds the fetch, for example until a filter it needs is chosen. */
11
+ enabled?: boolean;
12
+ getRowId?: (row: T) => string;
13
+ /** Shown above the table when the rows cannot be loaded. */
14
+ errorText?: string;
15
+ };
16
+ /** A table over a list fetched whole and sorted in the browser: no paging, no refetch to sort. */
17
+ export declare function SimpleQueryTable<T>({ queryKey, query, columns, initialSort, enabled, getRowId, errorText, ...props }: SimpleQueryTableProps<T>): import("react").JSX.Element;
@@ -0,0 +1,37 @@
1
+ import { ErrorMessage as e } from "../feedback/ErrorMessage.js";
2
+ import { TanstackTable as t } from "./TanstackTable.js";
3
+ import { useQuery as n } from "@tanstack/react-query";
4
+ import { Stack as r } from "@chakra-ui/react";
5
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
6
+ import { getCoreRowModel as o, getSortedRowModel as s, useReactTable as c } from "@tanstack/react-table";
7
+ import { useState as l } from "react";
8
+ // src/table/SimpleQueryTable.tsx
9
+ var u = [];
10
+ function d({ queryKey: d, query: f, columns: p, initialSort: m, enabled: h = !0, getRowId: g, errorText: _ = "Could not load the rows.", ...v }) {
11
+ let [y, b] = l(m ? [m] : u), { data: x, isLoading: S, isError: C } = n({
12
+ queryKey: d,
13
+ queryFn: f,
14
+ enabled: h
15
+ }), w = c({
16
+ data: x ?? u,
17
+ columns: p,
18
+ getRowId: g,
19
+ state: { sorting: y },
20
+ onSortingChange: b,
21
+ getCoreRowModel: o(),
22
+ getSortedRowModel: s(),
23
+ enableMultiSort: !1,
24
+ enableSortingRemoval: !1
25
+ });
26
+ return /* @__PURE__ */ a(r, {
27
+ gap: "2",
28
+ width: "100%",
29
+ children: [C && /* @__PURE__ */ i(e, { children: _ }), /* @__PURE__ */ i(t, {
30
+ table: w,
31
+ isLoading: S,
32
+ isError: C,
33
+ ...v
34
+ })]
35
+ });
36
+ }
37
+ export { d as SimpleQueryTable };
@@ -0,0 +1,9 @@
1
+ import type { Table } from "@tanstack/react-table";
2
+ export declare const PAGE_SIZES: number[];
3
+ export interface TablePageSizeSelectProps<T> {
4
+ table: Table<T>;
5
+ /** The sizes offered; any above {@link MAX_PAGE_SIZE} are left out, as a Paginator caps them. */
6
+ pageSizes?: number[];
7
+ }
8
+ /** Rows per page. */
9
+ export declare function TablePageSizeSelect<T>({ table, pageSizes }: TablePageSizeSelectProps<T>): import("react").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import "../paging/paging.js";
2
+ import { HStack as e, NativeSelect as t, Text as n } from "@chakra-ui/react";
3
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
+ // src/table/TablePageSizeSelect.tsx
5
+ var a = [
6
+ 10,
7
+ 25,
8
+ 50,
9
+ 100
10
+ ];
11
+ function o({ table: o, pageSizes: s = a }) {
12
+ let c = o.getState().pagination.pageSize, l = [.../* @__PURE__ */ new Set([...s.filter((e) => e > 0 && e <= 100), c])].sort((e, t) => e - t);
13
+ return /* @__PURE__ */ i(e, {
14
+ gap: "2",
15
+ children: [/* @__PURE__ */ i(t.Root, {
16
+ size: "sm",
17
+ width: "80px",
18
+ children: [/* @__PURE__ */ r(t.Field, {
19
+ "aria-label": "Rows per page",
20
+ value: c,
21
+ onChange: (e) => o.setPageSize(Number(e.target.value)),
22
+ children: l.map((e) => /* @__PURE__ */ r("option", {
23
+ value: e,
24
+ children: e
25
+ }, e))
26
+ }), /* @__PURE__ */ r(t.Indicator, {})]
27
+ }), /* @__PURE__ */ r(n, {
28
+ fontSize: "sm",
29
+ color: "fg.muted",
30
+ "aria-hidden": !0,
31
+ children: "per page"
32
+ })]
33
+ });
34
+ }
35
+ export { a as PAGE_SIZES, o as TablePageSizeSelect };
@@ -0,0 +1,5 @@
1
+ import type { Table } from "@tanstack/react-table";
2
+ /** First, previous, a page number field, next and last; a page typed past the end goes to the last. */
3
+ export declare function TablePagination<T>({ table }: {
4
+ table: Table<T>;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,65 @@
1
+ import { HStack as e, IconButton as t, InputGroup as n, NumberInput as r } from "@chakra-ui/react";
2
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
3
+ import { LuChevronLeft as o, LuChevronRight as s, LuChevronsLeft as c, LuChevronsRight as l } from "react-icons/lu";
4
+ // src/table/TablePagination.tsx
5
+ function u({ table: u }) {
6
+ let d = u.getPageCount(), f = u.getState().pagination.pageIndex;
7
+ function p(e) {
8
+ Number.isFinite(e) && u.setPageIndex(Math.min(Math.max(Math.floor(e), 1), d) - 1);
9
+ }
10
+ return /* @__PURE__ */ a(e, {
11
+ gap: "1",
12
+ children: [
13
+ /* @__PURE__ */ i(t, {
14
+ "aria-label": "First page",
15
+ size: "sm",
16
+ variant: "ghost",
17
+ disabled: !u.getCanPreviousPage(),
18
+ onClick: () => u.firstPage(),
19
+ children: /* @__PURE__ */ i(c, {})
20
+ }),
21
+ /* @__PURE__ */ i(t, {
22
+ "aria-label": "Previous page",
23
+ size: "sm",
24
+ variant: "ghost",
25
+ disabled: !u.getCanPreviousPage(),
26
+ onClick: () => u.previousPage(),
27
+ children: /* @__PURE__ */ i(o, {})
28
+ }),
29
+ /* @__PURE__ */ i(n, {
30
+ startAddon: "Page",
31
+ endAddon: `of ${d}`,
32
+ width: "auto",
33
+ children: /* @__PURE__ */ a(r.Root, {
34
+ width: "80px",
35
+ min: 1,
36
+ max: Math.max(d, 1),
37
+ value: String(f + 1),
38
+ onValueChange: ({ valueAsNumber: e }) => p(e),
39
+ disabled: d <= 1,
40
+ children: [/* @__PURE__ */ i(r.Input, {
41
+ "aria-label": "Page",
42
+ onFocus: (e) => e.target.select()
43
+ }), /* @__PURE__ */ a(r.Control, { children: [/* @__PURE__ */ i(r.IncrementTrigger, {}), /* @__PURE__ */ i(r.DecrementTrigger, {})] })]
44
+ })
45
+ }),
46
+ /* @__PURE__ */ i(t, {
47
+ "aria-label": "Next page",
48
+ size: "sm",
49
+ variant: "ghost",
50
+ disabled: !u.getCanNextPage(),
51
+ onClick: () => u.nextPage(),
52
+ children: /* @__PURE__ */ i(s, {})
53
+ }),
54
+ /* @__PURE__ */ i(t, {
55
+ "aria-label": "Last page",
56
+ size: "sm",
57
+ variant: "ghost",
58
+ disabled: !u.getCanNextPage(),
59
+ onClick: () => u.lastPage(),
60
+ children: /* @__PURE__ */ i(l, {})
61
+ })
62
+ ]
63
+ });
64
+ }
65
+ export { u as TablePagination };
@@ -0,0 +1,6 @@
1
+ import { type TextProps } from "@chakra-ui/react";
2
+ import type { Table } from "@tanstack/react-table";
3
+ /** "Showing 26 to 50 of 60 rows", announced politely as it changes. */
4
+ export declare function TableStatus<T>({ table, ...props }: TextProps & {
5
+ table: Table<T>;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,27 @@
1
+ import { rowRange as e } from "../paging/paging.js";
2
+ import { Text as t } from "@chakra-ui/react";
3
+ import { jsxs as n } from "react/jsx-runtime";
4
+ // src/table/TableStatus.tsx
5
+ function r({ table: r, ...i }) {
6
+ let { pageIndex: a, pageSize: o } = r.getState().pagination, { first: s, last: c, total: l } = e({
7
+ pageIndex: a,
8
+ pageSize: o,
9
+ totalElementCount: r.getRowCount()
10
+ }, r.getRowModel().rows.length);
11
+ return /* @__PURE__ */ n(t, {
12
+ "aria-live": "polite",
13
+ color: "fg.muted",
14
+ fontSize: "sm",
15
+ ...i,
16
+ children: [
17
+ "Showing ",
18
+ s,
19
+ " to ",
20
+ c,
21
+ " of ",
22
+ l,
23
+ " rows"
24
+ ]
25
+ });
26
+ }
27
+ export { r as TableStatus };
@@ -0,0 +1,7 @@
1
+ import { type TanstackTableProps } from "./TanstackTable";
2
+ export type TableViewProps<T> = TanstackTableProps<T> & {
3
+ /** The page sizes offered; see {@link TablePageSizeSelect}. */
4
+ pageSizes?: number[];
5
+ };
6
+ /** A paged table: the rows, then the pager, the row count and the page size, once there are rows. */
7
+ export declare function TableView<T>({ table, pageSizes, ...props }: TableViewProps<T>): import("react").JSX.Element;
@@ -0,0 +1,31 @@
1
+ import { TablePageSizeSelect as e } from "./TablePageSizeSelect.js";
2
+ import { TablePagination as t } from "./TablePagination.js";
3
+ import { TableStatus as n } from "./TableStatus.js";
4
+ import { TanstackTable as r } from "./TanstackTable.js";
5
+ import { Flex as i, Stack as a } from "@chakra-ui/react";
6
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ // src/table/TableView.tsx
8
+ function c({ table: c, pageSizes: l, ...u }) {
9
+ return /* @__PURE__ */ s(a, {
10
+ gap: "2",
11
+ width: "100%",
12
+ children: [/* @__PURE__ */ o(r, {
13
+ table: c,
14
+ ...u
15
+ }), c.getRowCount() > 0 && /* @__PURE__ */ s(i, {
16
+ wrap: "wrap",
17
+ gap: "3",
18
+ align: "center",
19
+ justify: "space-between",
20
+ children: [
21
+ /* @__PURE__ */ o(t, { table: c }),
22
+ /* @__PURE__ */ o(n, { table: c }),
23
+ /* @__PURE__ */ o(e, {
24
+ table: c,
25
+ pageSizes: l
26
+ })
27
+ ]
28
+ })]
29
+ });
30
+ }
31
+ export { c as TableView };
@@ -0,0 +1,16 @@
1
+ import { type TableRootProps } from "@chakra-ui/react";
2
+ import { type Table as ReactTable } from "@tanstack/react-table";
3
+ export type TanstackTableProps<T> = Omit<TableRootProps, "children"> & {
4
+ table: ReactTable<T>;
5
+ /** True while the first rows load: shows a spinner instead of {@link emptyText}. */
6
+ isLoading?: boolean;
7
+ /** True when the rows could not be loaded: the error says so, not {@link emptyText}. */
8
+ isError?: boolean;
9
+ /** Shown in place of rows when there are none. */
10
+ emptyText?: string;
11
+ };
12
+ /**
13
+ * Renders a TanStack table. A sortable column's header is a button, so it sorts from the
14
+ * keyboard too, and carries `aria-sort`; a column with `enableSorting: false` gets neither.
15
+ */
16
+ export declare function TanstackTable<T>({ table, isLoading, isError, emptyText, ...props }: TanstackTableProps<T>): import("react").JSX.Element;
@@ -0,0 +1,61 @@
1
+ import { Box as e, Button as t, Center as n, Spinner as r, Table as i } from "@chakra-ui/react";
2
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
3
+ import { flexRender as s } from "@tanstack/react-table";
4
+ import { LuArrowDown as c, LuArrowUp as l, LuArrowUpDown as u } from "react-icons/lu";
5
+ // src/table/TanstackTable.tsx
6
+ function d(e) {
7
+ let t = e.column.getIsSorted();
8
+ return t === "asc" ? "ascending" : t === "desc" ? "descending" : "none";
9
+ }
10
+ function f({ sorted: e }) {
11
+ return e === "asc" ? /* @__PURE__ */ a(l, { "aria-hidden": !0 }) : e === "desc" ? /* @__PURE__ */ a(c, { "aria-hidden": !0 }) : /* @__PURE__ */ a(u, {
12
+ "aria-hidden": !0,
13
+ opacity: .3
14
+ });
15
+ }
16
+ function p({ table: c, isLoading: l = !1, isError: u = !1, emptyText: p = "No rows.", ...m }) {
17
+ let h = c.getRowModel().rows, g = c.getVisibleLeafColumns().length;
18
+ return /* @__PURE__ */ a(e, {
19
+ bg: "bg.panel",
20
+ borderWidth: "1px",
21
+ borderRadius: "lg",
22
+ borderColor: "border",
23
+ width: "100%",
24
+ overflowX: "auto",
25
+ children: /* @__PURE__ */ o(i.Root, {
26
+ striped: !0,
27
+ size: "md",
28
+ width: "100%",
29
+ ...m,
30
+ children: [/* @__PURE__ */ a(i.Header, { children: c.getHeaderGroups().map((e) => /* @__PURE__ */ a(i.Row, {
31
+ bg: "bg.subtle",
32
+ children: e.headers.map((e) => {
33
+ let n = e.isPlaceholder ? null : s(e.column.columnDef.header, e.getContext());
34
+ return e.column.getCanSort() ? /* @__PURE__ */ a(i.ColumnHeader, {
35
+ "aria-sort": d(e),
36
+ children: /* @__PURE__ */ o(t, {
37
+ size: "xs",
38
+ variant: "ghost",
39
+ onClick: e.column.getToggleSortingHandler(),
40
+ children: [n, /* @__PURE__ */ a(f, { sorted: e.column.getIsSorted() })]
41
+ })
42
+ }, e.id) : /* @__PURE__ */ a(i.ColumnHeader, { children: n }, e.id);
43
+ })
44
+ }, e.id)) }), /* @__PURE__ */ o(i.Body, { children: [h.map((e) => /* @__PURE__ */ a(i.Row, { children: e.getVisibleCells().map((e) => /* @__PURE__ */ a(i.Cell, { children: s(e.column.columnDef.cell, e.getContext()) }, e.id)) }, e.id)), h.length === 0 && !u && /* @__PURE__ */ a(i.Row, { children: /* @__PURE__ */ a(i.Cell, {
45
+ colSpan: g,
46
+ children: /* @__PURE__ */ a(n, {
47
+ py: "4",
48
+ children: l ? /* @__PURE__ */ a(e, {
49
+ role: "status",
50
+ "aria-label": "Loading",
51
+ children: /* @__PURE__ */ a(r, { size: "sm" })
52
+ }) : /* @__PURE__ */ a(e, {
53
+ color: "fg.muted",
54
+ children: p
55
+ })
56
+ })
57
+ }) })] })]
58
+ })
59
+ });
60
+ }
61
+ export { p as TanstackTable };
@@ -0,0 +1,11 @@
1
+ export { FilterChips, type FilterChipsProps, type FilterOption } from "./FilterChips";
2
+ export { QueryTable, type QueryTableProps } from "./QueryTable";
3
+ export { createSelectColumn, type SelectColumnOptions } from "./selectColumn";
4
+ export { SimpleQueryTable, type SimpleQueryTableProps } from "./SimpleQueryTable";
5
+ export { TablePageSizeSelect, PAGE_SIZES, type TablePageSizeSelectProps } from "./TablePageSizeSelect";
6
+ export { TablePagination } from "./TablePagination";
7
+ export { TableStatus } from "./TableStatus";
8
+ export { TableView, type TableViewProps } from "./TableView";
9
+ export { TanstackTable, type TanstackTableProps } from "./TanstackTable";
10
+ export { useDebounce } from "./useDebounce";
11
+ export { DEFAULT_PAGE_SIZE, useQueryTable, type TableParams, type UseQueryTableOptions, type UseQueryTableResult, } from "./useQueryTable";
@@ -0,0 +1,12 @@
1
+ import { FilterChips as e } from "./FilterChips.js";
2
+ import { QueryTable as t } from "./QueryTable.js";
3
+ import { createSelectColumn as n } from "./selectColumn.js";
4
+ import { SimpleQueryTable as r } from "./SimpleQueryTable.js";
5
+ import { PAGE_SIZES as i, TablePageSizeSelect as a } from "./TablePageSizeSelect.js";
6
+ import { TablePagination as o } from "./TablePagination.js";
7
+ import { TableStatus as s } from "./TableStatus.js";
8
+ import { TableView as c } from "./TableView.js";
9
+ import { TanstackTable as l } from "./TanstackTable.js";
10
+ import { useDebounce as u } from "./useDebounce.js";
11
+ import { DEFAULT_PAGE_SIZE as d, useQueryTable as f } from "./useQueryTable.js";
12
+ export { d as DEFAULT_PAGE_SIZE, e as FilterChips, i as PAGE_SIZES, t as QueryTable, r as SimpleQueryTable, a as TablePageSizeSelect, o as TablePagination, s as TableStatus, c as TableView, l as TanstackTable, n as createSelectColumn, u as useDebounce, f as useQueryTable };