@ifto/mantine-kit 0.3.0 → 0.5.1

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 (51) hide show
  1. package/README.md +85 -10
  2. package/dist/component/organism/EChart/chart/BarChart.d.ts +3 -0
  3. package/dist/component/organism/EChart/chart/BarChart.js +5 -0
  4. package/dist/component/organism/EChart/chart/BarChart.js.map +1 -1
  5. package/dist/component/organism/EChart/chart/CartesianChart.d.ts +4 -0
  6. package/dist/component/organism/EChart/chart/CartesianChart.js +5 -0
  7. package/dist/component/organism/EChart/chart/CartesianChart.js.map +1 -1
  8. package/dist/component/organism/EChart/chart/ChartType.d.ts +3 -0
  9. package/dist/component/organism/EChart/chart/DonutChart.d.ts +4 -0
  10. package/dist/component/organism/EChart/chart/DonutChart.js +6 -0
  11. package/dist/component/organism/EChart/chart/DonutChart.js.map +1 -1
  12. package/dist/component/organism/EChart/chart/LineChart.d.ts +3 -0
  13. package/dist/component/organism/EChart/chart/LineChart.js +5 -0
  14. package/dist/component/organism/EChart/chart/LineChart.js.map +1 -1
  15. package/dist/component/organism/EChart/chart/StackedBarChart.d.ts +3 -0
  16. package/dist/component/organism/EChart/chart/StackedBarChart.js +5 -0
  17. package/dist/component/organism/EChart/chart/StackedBarChart.js.map +1 -1
  18. package/dist/component/organism/Table/Table.d.ts +9 -9
  19. package/dist/component/organism/Table/Table.js +21 -24
  20. package/dist/component/organism/Table/Table.js.map +1 -1
  21. package/dist/component/organism/Table/TableData.d.ts +24 -0
  22. package/dist/component/organism/Table/TableData.js +1 -0
  23. package/dist/component/organism/Table/useClientPagination.d.ts +21 -0
  24. package/dist/component/organism/Table/useClientPagination.js +43 -0
  25. package/dist/component/organism/Table/useClientPagination.js.map +1 -0
  26. package/dist/component/organism/Table/useServerPagination.d.ts +24 -0
  27. package/dist/component/organism/Table/useServerPagination.js +105 -0
  28. package/dist/component/organism/Table/useServerPagination.js.map +1 -0
  29. package/dist/component/organism/ViewTable/DataTableView.d.ts +39 -0
  30. package/dist/component/organism/ViewTable/DataTableView.js +52 -0
  31. package/dist/component/organism/ViewTable/DataTableView.js.map +1 -0
  32. package/dist/component/organism/ViewTable/TableState.d.ts +30 -0
  33. package/dist/component/organism/ViewTable/TableState.js +19 -0
  34. package/dist/component/organism/ViewTable/TableState.js.map +1 -0
  35. package/dist/component/organism/ViewTable/ViewTable.d.ts +11 -33
  36. package/dist/component/organism/ViewTable/ViewTable.js +16 -101
  37. package/dist/component/organism/ViewTable/ViewTable.js.map +1 -1
  38. package/dist/component/organism/ViewTable/useClientTableState.d.ts +25 -0
  39. package/dist/component/organism/ViewTable/useClientTableState.js +68 -0
  40. package/dist/component/organism/ViewTable/useClientTableState.js.map +1 -0
  41. package/dist/component/template/ListPage/ListPage.d.ts +9 -19
  42. package/dist/component/template/ListPage/ListPage.js +5 -14
  43. package/dist/component/template/ListPage/ListPage.js.map +1 -1
  44. package/dist/data/PageDataSource.d.ts +10 -0
  45. package/dist/data/PageDataSource.js +1 -0
  46. package/dist/data/PageRequest.d.ts +29 -0
  47. package/dist/data/PageRequest.js +1 -0
  48. package/dist/hook/useService.d.ts +3 -3
  49. package/dist/hook/useService.js +2 -0
  50. package/dist/hook/useService.js.map +1 -1
  51. package/package.json +1 -1
@@ -1,113 +1,28 @@
1
1
  "use client";
2
2
  import { useKitConfig } from "../../../provider/useKit.js";
3
- import { columnComparator } from "./ColumnComparator.js";
4
- import { useRowKey } from "./useRowKey.js";
5
- import { useEffect, useMemo, useState } from "react";
3
+ import DataTableView from "./DataTableView.js";
4
+ import { useClientTableState } from "./useClientTableState.js";
6
5
  import { jsx } from "react/jsx-runtime";
7
- import { DataTable } from "mantine-datatable";
8
6
  // src/component/organism/ViewTable/ViewTable.tsx
9
- /** Options of records per page. The largest one lets long tables (and end-to-end tests) show every row in one page. */
10
- var PAGE_SIZES = [
11
- 10,
12
- 20,
13
- 50,
14
- 200
15
- ];
16
- /** Default sort when neither `defaultSortStatus` nor `compare` is given. */
17
- var DEFAULT_SORT_STATUS = {
18
- columnAccessor: "id",
19
- direction: "asc"
20
- };
21
7
  /**
22
- * Sort used while the `compare` function applies, when `defaultSortStatus` is not given: it matches no column, so no
23
- * header shows the sort indicator, and the first click on any column sorts it in ascending order.
8
+ * Table that sorts (following the locale) and paginates in the browser all the given records, on top of the
9
+ * mantine-datatable `DataTable`.
10
+ * @template M type of the records, shown one per row
24
11
  */
25
- var NO_COLUMN_SORT_STATUS = {
26
- columnAccessor: "",
27
- direction: "asc"
28
- };
29
- /**
30
- * The ViewTable component is a wrapper for the Mantine DataTable component.
31
- * @template M - Model that extends from {@link Identifiable}
32
- * @param data the data to be displayed in this ViewTable
33
- * @param {DataTableColumn<M>[]} columns - The columns to be displayed in this ViewTable
34
- * @param loading Indicates if the data to be displayed is being loaded yet or not
35
- * @param pagination Indicates whether to show pagination or not.
36
- * @param getPaginationControlProps extra attributes for the pagination control buttons
37
- * @param compare comparison function of the initial order, replaced by the sort of the column chosen in the header
38
- * @param rest remaining props, passed to the DataTable
39
- * @return the table with the data, sortable and (optionally) paginated
40
- */
41
- var ViewTable = ({ data, columns, loading, pagination = true, showIdColumn = true, defaultSortStatus, compare, getPaginationControlProps, ...rest }) => {
42
- const { locale, messages } = useKitConfig();
43
- const [page, setPage] = useState(1);
44
- const [pageSize, setPageSize] = useState(PAGE_SIZES[0]);
45
- const [sortStatus, setSortStatus] = useState(defaultSortStatus ?? (compare ? NO_COLUMN_SORT_STATUS : DEFAULT_SORT_STATUS));
46
- const [compareActive, setCompareActive] = useState(compare !== void 0);
47
- const sortedData = useMemo(() => {
48
- const comparator = compareActive && compare ? compare : columnComparator(sortStatus, locale);
49
- return [...data].sort(comparator);
50
- }, [
12
+ var ViewTable = ({ data, pagination = true, defaultSortStatus, compare, ...rest }) => {
13
+ const { locale } = useKitConfig();
14
+ const { records, ...state } = useClientTableState({
51
15
  data,
52
- sortStatus,
53
- compareActive,
54
- compare,
55
- locale
56
- ]);
57
- const records = useMemo(() => {
58
- if (!pagination) return sortedData;
59
- const from = (page - 1) * pageSize;
60
- return sortedData.slice(from, from + pageSize);
61
- }, [
62
- sortedData,
63
16
  pagination,
64
- page,
65
- pageSize
66
- ]);
67
- const rowKey = useRowKey();
68
- function changeSortStatus(status) {
69
- setCompareActive(false);
70
- setSortStatus(status);
71
- }
72
- const effectiveColumns = [{
73
- title: "ID",
74
- accessor: "id",
75
- hidden: !showIdColumn,
76
- sortable: true,
77
- render: (item) => /* @__PURE__ */ jsx("span", { children: item.id })
78
- }, ...columns];
79
- useEffect(() => {
80
- setPage(1);
81
- }, [pageSize]);
82
- useEffect(() => {
83
- const totalPages = Math.ceil(data.length / pageSize);
84
- setPage((currentPage) => totalPages && currentPage > totalPages ? totalPages : currentPage);
85
- }, [data.length, pageSize]);
86
- const paginationProps = pagination ? {
87
- page,
88
- onPageChange: (p) => setPage(p),
89
- totalRecords: data.length,
90
- recordsPerPage: pageSize,
91
- recordsPerPageOptions: PAGE_SIZES,
92
- onRecordsPerPageChange: setPageSize,
93
- recordsPerPageLabel: messages.table.recordsPerPage,
94
- paginationText: messages.table.pagination,
95
- getPaginationControlProps
96
- } : {};
97
- return /* @__PURE__ */ jsx(DataTable, {
98
- minHeight: 200,
99
- verticalSpacing: "sm",
100
- striped: true,
101
- columns: effectiveColumns,
17
+ locale,
18
+ defaultSortStatus,
19
+ compare
20
+ });
21
+ return /* @__PURE__ */ jsx(DataTableView, {
22
+ ...rest,
102
23
  records,
103
- idAccessor: rowKey,
104
- noRecordsText: messages.table.noRecords,
105
- fetching: loading,
106
- sortStatus,
107
- onSortStatusChange: changeSortStatus,
108
- customRowAttributes: (record) => ({ "data-testid": `row-${record.id}` }),
109
- ...paginationProps,
110
- ...rest
24
+ state,
25
+ paginated: pagination
111
26
  });
112
27
  };
113
28
  export { ViewTable as default };
@@ -1 +1 @@
1
- {"version":3,"file":"ViewTable.js","names":[],"sources":["../../../../src/component/organism/ViewTable/ViewTable.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\n\nimport {\n DataTable,\n DataTableColumn,\n DataTablePaginationProps,\n DataTableProps,\n DataTableSortStatus\n} from 'mantine-datatable';\n\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useKitConfig } from '../../../provider/useKit';\nimport { columnComparator, Comparator } from './ColumnComparator';\nimport { useRowKey } from './useRowKey';\n\n/** Options of records per page. The largest one lets long tables (and end-to-end tests) show every row in one page. */\nconst PAGE_SIZES = [10, 20, 50, 200];\n\n/** Default sort when neither `defaultSortStatus` nor `compare` is given. */\nconst DEFAULT_SORT_STATUS = { columnAccessor: 'id', direction: 'asc' } as const;\n\n/**\n * Sort used while the `compare` function applies, when `defaultSortStatus` is not given: it matches no column, so no\n * header shows the sort indicator, and the first click on any column sorts it in ascending order.\n */\nconst NO_COLUMN_SORT_STATUS = { columnAccessor: '', direction: 'asc' } as const;\n\n/** Function that defines extra attributes (e.g. aria-label) for each pagination control button. */\ntype PaginationControlPropsGetter = Extract<DataTablePaginationProps, { page: number }>['getPaginationControlProps'];\n\n/**\n * Props of the {@link DataTable} passed straight through by the ViewTable.\n * Only the props actually used by the callers are accepted: using `Partial<DataTableProps<M>>` makes TypeScript combine\n * the variants of the `DataTableProps` union with the ones of `paginationProps`, which exhausts the memory of `tsc`.\n */\ntype DataTablePassThroughProps<M extends Identifiable> = Pick<\n DataTableProps<M>,\n | 'onRowClick'\n | 'rowStyle'\n | 'noRecordsText'\n | 'highlightOnHover'\n | 'highlightOnHoverColor'\n | 'className'\n | 'minHeight'\n | 'withColumnBorders'\n | 'verticalSpacing'\n | 'horizontalSpacing'\n>;\n\n/**\n * TableProps is a generic type for the ViewTable component props.\n */\ntype TableProps<M extends Identifiable> = {\n data: M[];\n columns: DataTableColumn<M>[];\n loading?: boolean;\n pagination?: boolean;\n /** Shows the ID column, automatically added to the given columns. */\n showIdColumn?: boolean;\n /**\n * Initial sort of the table. Default: by `id`, ascending.\n * Give another value when the natural order of the data should not be by `id` (e.g. when the backend already\n * returns the data in an order to keep). Along with `compare`, it only tells which header shows the sort indicator\n * in the initial order.\n */\n defaultSortStatus?: DataTableSortStatus<M>;\n /**\n * Comparison function that defines the initial order, instead of the column of `defaultSortStatus`. It allows\n * sorting by more than one criterion (e.g. city and, in the same city, year descending). After the user clicks the\n * header of a column, the sort of the column applies.\n */\n compare?: Comparator<M>;\n /** Extra attributes for the pagination control buttons. It only has effect when `pagination` is true. */\n getPaginationControlProps?: PaginationControlPropsGetter;\n} & DataTablePassThroughProps<M>;\n\n/**\n * The ViewTable component is a wrapper for the Mantine DataTable component.\n * @template M - Model that extends from {@link Identifiable}\n * @param data the data to be displayed in this ViewTable\n * @param {DataTableColumn<M>[]} columns - The columns to be displayed in this ViewTable\n * @param loading Indicates if the data to be displayed is being loaded yet or not\n * @param pagination Indicates whether to show pagination or not.\n * @param getPaginationControlProps extra attributes for the pagination control buttons\n * @param compare comparison function of the initial order, replaced by the sort of the column chosen in the header\n * @param rest remaining props, passed to the DataTable\n * @return the table with the data, sortable and (optionally) paginated\n */\nconst ViewTable = <M extends Identifiable>({\n data,\n columns,\n loading,\n pagination = true,\n showIdColumn = true,\n defaultSortStatus,\n compare,\n getPaginationControlProps,\n ...rest\n}: TableProps<M>) => {\n const { locale, messages } = useKitConfig();\n const [page, setPage] = useState(1);\n const [pageSize, setPageSize] = useState(PAGE_SIZES[0]);\n const [sortStatus, setSortStatus] = useState<DataTableSortStatus<M>>(\n defaultSortStatus ?? (compare ? NO_COLUMN_SORT_STATUS : DEFAULT_SORT_STATUS)\n );\n // The `compare` function applies until the user chooses a column in the header.\n const [compareActive, setCompareActive] = useState(compare !== undefined);\n\n /*\n * Sorts all records before cutting the page, so each page (including in descending order) shows its part of the\n * whole set. The copy keeps the order of the given array. It is separate from the page cut, so not everything is\n * sorted again on every change of page or page size.\n */\n const sortedData = useMemo(() => {\n const comparator = compareActive && compare ? compare : columnComparator(sortStatus, locale);\n\n return [...data].sort(comparator);\n }, [data, sortStatus, compareActive, compare, locale]);\n\n // Without pagination, shows all records (e.g. a printed report), not only the ones of the first page.\n const records = useMemo(() => {\n if (!pagination) return sortedData;\n\n const from = (page - 1) * pageSize;\n return sortedData.slice(from, from + pageSize);\n }, [sortedData, pagination, page, pageSize]);\n const rowKey = useRowKey<M>();\n\n function changeSortStatus(status: DataTableSortStatus<M>): void {\n setCompareActive(false);\n setSortStatus(status);\n }\n\n const effectiveColumns: DataTableColumn<M>[] = [\n {\n title: 'ID',\n accessor: 'id',\n hidden: !showIdColumn,\n sortable: true,\n render: item => <span>{item.id}</span>\n },\n ...columns\n ];\n\n useEffect(() => {\n setPage(1);\n }, [pageSize]);\n\n /* If the data shrinks, goes back to the last existing page. The functional update avoids using an outdated `page`\n when the effect above has just gone back to page 1. */\n useEffect(() => {\n const totalPages = Math.ceil(data.length / pageSize);\n setPage(currentPage => (totalPages && currentPage > totalPages ? totalPages : currentPage));\n }, [data.length, pageSize]);\n\n /* Typed only with the pagination props (and not as Partial<DataTableProps>) to not exhaust the memory of tsc.\n See the comment of DataTablePassThroughProps. */\n const paginationProps: DataTablePaginationProps = pagination\n ? {\n page,\n onPageChange: p => setPage(p),\n totalRecords: data.length,\n recordsPerPage: pageSize,\n recordsPerPageOptions: PAGE_SIZES,\n onRecordsPerPageChange: setPageSize,\n recordsPerPageLabel: messages.table.recordsPerPage,\n paginationText: messages.table.pagination,\n getPaginationControlProps\n }\n : {};\n\n // This DataTable is the one of https://www.npmjs.com/package/mantine-datatable (there are other libraries with this name).\n return (\n <DataTable\n minHeight={200}\n verticalSpacing=\"sm\"\n striped\n columns={effectiveColumns}\n records={records}\n idAccessor={rowKey}\n noRecordsText={messages.table.noRecords}\n fetching={loading}\n sortStatus={sortStatus}\n onSortStatusChange={changeSortStatus}\n customRowAttributes={record => ({ 'data-testid': `row-${record.id}` })}\n {...paginationProps}\n {...rest}\n />\n );\n};\n\nexport default ViewTable;\n"],"mappings":";;;;;;;;;AAkBA,IAAM,aAAa;CAAC;CAAI;CAAI;CAAI;AAAG;;AAGnC,IAAM,sBAAsB;CAAE,gBAAgB;CAAM,WAAW;AAAM;;;;;AAMrE,IAAM,wBAAwB;CAAE,gBAAgB;CAAI,WAAW;AAAM;;;;;;;;;;;;;AA+DrE,IAAM,aAAqC,EACvC,MACA,SACA,SACA,aAAa,MACb,eAAe,MACf,mBACA,SACA,2BACA,GAAG,WACc;CACjB,MAAM,EAAE,QAAQ,aAAa,aAAa;CAC1C,MAAM,CAAC,MAAM,WAAW,SAAS,CAAC;CAClC,MAAM,CAAC,UAAU,eAAe,SAAS,WAAW,EAAE;CACtD,MAAM,CAAC,YAAY,iBAAiB,SAChC,sBAAsB,UAAU,wBAAwB,oBAC5D;CAEA,MAAM,CAAC,eAAe,oBAAoB,SAAS,YAAY,KAAA,CAAS;CAOxE,MAAM,aAAa,cAAc;EAC7B,MAAM,aAAa,iBAAiB,UAAU,UAAU,iBAAiB,YAAY,MAAM;EAE3F,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,UAAU;CACpC,GAAG;EAAC;EAAM;EAAY;EAAe;EAAS;CAAM,CAAC;CAGrD,MAAM,UAAU,cAAc;EAC1B,IAAI,CAAC,YAAY,OAAO;EAExB,MAAM,QAAQ,OAAO,KAAK;EAC1B,OAAO,WAAW,MAAM,MAAM,OAAO,QAAQ;CACjD,GAAG;EAAC;EAAY;EAAY;EAAM;CAAQ,CAAC;CAC3C,MAAM,SAAS,UAAa;CAE5B,SAAS,iBAAiB,QAAsC;EAC5D,iBAAiB,KAAK;EACtB,cAAc,MAAM;CACxB;CAEA,MAAM,mBAAyC,CAC3C;EACI,OAAO;EACP,UAAU;EACV,QAAQ,CAAC;EACT,UAAU;EACV,SAAQ,SAAQ,oBAAC,QAAD,EAAA,UAAO,KAAK,GAAS,CAAA;CACzC,GACA,GAAG,OACP;CAEA,gBAAgB;EACZ,QAAQ,CAAC;CACb,GAAG,CAAC,QAAQ,CAAC;CAIb,gBAAgB;EACZ,MAAM,aAAa,KAAK,KAAK,KAAK,SAAS,QAAQ;EACnD,SAAQ,gBAAgB,cAAc,cAAc,aAAa,aAAa,WAAY;CAC9F,GAAG,CAAC,KAAK,QAAQ,QAAQ,CAAC;CAI1B,MAAM,kBAA4C,aAC5C;EACI;EACA,eAAc,MAAK,QAAQ,CAAC;EAC5B,cAAc,KAAK;EACnB,gBAAgB;EAChB,uBAAuB;EACvB,wBAAwB;EACxB,qBAAqB,SAAS,MAAM;EACpC,gBAAgB,SAAS,MAAM;EAC/B;CACJ,IACA,CAAC;CAGP,OACI,oBAAC,WAAD;EACI,WAAW;EACX,iBAAgB;EAChB,SAAA;EACA,SAAS;EACA;EACT,YAAY;EACZ,eAAe,SAAS,MAAM;EAC9B,UAAU;EACE;EACZ,oBAAoB;EACpB,sBAAqB,YAAW,EAAE,eAAe,OAAO,OAAO,KAAK;EACpE,GAAI;EACJ,GAAI;CACP,CAAA;AAET"}
1
+ {"version":3,"file":"ViewTable.js","names":[],"sources":["../../../../src/component/organism/ViewTable/ViewTable.tsx"],"sourcesContent":["'use client';\n\nimport { DataTableSortStatus } from 'mantine-datatable';\n\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useKitConfig } from '../../../provider/useKit';\nimport { Comparator } from './ColumnComparator';\nimport DataTableView, { DataTableViewBaseProps } from './DataTableView';\nimport { useClientTableState } from './useClientTableState';\n\n/** Props of the {@link ViewTable}. */\ntype ViewTableProps<M extends Identifiable> = DataTableViewBaseProps<M> & {\n /** All records of the table. */\n data: M[];\n\n /** Whether the table is paginated; defaults to true. Without it, every record is shown (e.g. a printed report). */\n pagination?: boolean;\n\n /**\n * Initial sort of the table. Default: by `id`, ascending.\n * Give another value when the natural order of the data should not be by `id` (e.g. when the backend already\n * returns the data in an order to keep). Along with `compare`, it only tells which header shows the sort indicator\n * in the initial order.\n */\n defaultSortStatus?: DataTableSortStatus<M>;\n\n /**\n * Comparison function that defines the initial order, instead of the column of `defaultSortStatus`. It allows\n * sorting by more than one criterion (e.g. city and, in the same city, year descending). After the user clicks the\n * header of a column, the sort of the column applies.\n */\n compare?: Comparator<M>;\n};\n\n/**\n * Table that sorts (following the locale) and paginates in the browser all the given records, on top of the\n * mantine-datatable `DataTable`.\n * @template M type of the records, shown one per row\n */\nconst ViewTable = <M extends Identifiable>({\n data,\n pagination = true,\n defaultSortStatus,\n compare,\n ...rest\n}: ViewTableProps<M>) => {\n const { locale } = useKitConfig();\n const { records, ...state } = useClientTableState({ data, pagination, locale, defaultSortStatus, compare });\n\n return <DataTableView {...rest} records={records} state={state} paginated={pagination} />;\n};\n\nexport default ViewTable;\n"],"mappings":";;;;;;;;;;;AAuCA,IAAM,aAAqC,EACvC,MACA,aAAa,MACb,mBACA,SACA,GAAG,WACkB;CACrB,MAAM,EAAE,WAAW,aAAa;CAChC,MAAM,EAAE,SAAS,GAAG,UAAU,oBAAoB;EAAE;EAAM;EAAY;EAAQ;EAAmB;CAAQ,CAAC;CAE1G,OAAO,oBAAC,eAAD;EAAe,GAAI;EAAe;EAAgB;EAAO,WAAW;CAAa,CAAA;AAC5F"}
@@ -0,0 +1,25 @@
1
+ import type { DataTableSortStatus } from 'mantine-datatable';
2
+ import { Comparator } from './ColumnComparator';
3
+ import { TableState } from './TableState';
4
+ /** Options of {@link useClientTableState}. */
5
+ type ClientTableOptions<M> = {
6
+ /** All records of the table. */
7
+ data: M[];
8
+ /** Whether only the records of the current page are returned. */
9
+ pagination: boolean;
10
+ /** Locale of the text comparison. */
11
+ locale: string;
12
+ /** Initial sort; defaults to {@link DEFAULT_SORT_STATUS}, or to no column when `compare` is given. */
13
+ defaultSortStatus?: DataTableSortStatus<M>;
14
+ /** Comparison function of the initial order, replaced by the sort of the column chosen in the header. */
15
+ compare?: Comparator<M>;
16
+ };
17
+ /**
18
+ * Sorts and paginates in the browser all the records of a table.
19
+ * @template M type of the records
20
+ * @return the state of the table and the records to show
21
+ */
22
+ export declare function useClientTableState<M>({ data, pagination, locale, defaultSortStatus, compare }: ClientTableOptions<M>): TableState<M> & {
23
+ records: M[];
24
+ };
25
+ export {};
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { columnComparator } from "./ColumnComparator.js";
3
+ import { DEFAULT_SORT_STATUS, PAGE_SIZES } from "./TableState.js";
4
+ import { useEffect, useMemo, useState } from "react";
5
+ // src/component/organism/ViewTable/useClientTableState.ts
6
+ /**
7
+ * Sort used while the `compare` function applies, when `defaultSortStatus` is not given: it matches no column, so no
8
+ * header shows the sort indicator, and the first click on any column sorts it in ascending order.
9
+ */
10
+ var NO_COLUMN_SORT_STATUS = {
11
+ columnAccessor: "",
12
+ direction: "asc"
13
+ };
14
+ /**
15
+ * Sorts and paginates in the browser all the records of a table.
16
+ * @template M type of the records
17
+ * @return the state of the table and the records to show
18
+ */
19
+ function useClientTableState({ data, pagination, locale, defaultSortStatus, compare }) {
20
+ const [page, setPage] = useState(1);
21
+ const [pageSize, setPageSize] = useState(PAGE_SIZES[0]);
22
+ const [sortStatus, setSortStatus] = useState(defaultSortStatus ?? (compare ? NO_COLUMN_SORT_STATUS : DEFAULT_SORT_STATUS));
23
+ const [compareActive, setCompareActive] = useState(compare !== void 0);
24
+ const sortedData = useMemo(() => {
25
+ const comparator = compareActive && compare ? compare : columnComparator(sortStatus, locale);
26
+ return [...data].sort(comparator);
27
+ }, [
28
+ data,
29
+ sortStatus,
30
+ compareActive,
31
+ compare,
32
+ locale
33
+ ]);
34
+ const records = useMemo(() => {
35
+ if (!pagination) return sortedData;
36
+ const from = (page - 1) * pageSize;
37
+ return sortedData.slice(from, from + pageSize);
38
+ }, [
39
+ sortedData,
40
+ pagination,
41
+ page,
42
+ pageSize
43
+ ]);
44
+ function changeSortStatus(status) {
45
+ setCompareActive(false);
46
+ setSortStatus(status);
47
+ }
48
+ useEffect(() => {
49
+ setPage(1);
50
+ }, [pageSize]);
51
+ useEffect(() => {
52
+ const totalPages = Math.ceil(data.length / pageSize);
53
+ setPage((currentPage) => totalPages && currentPage > totalPages ? totalPages : currentPage);
54
+ }, [data.length, pageSize]);
55
+ return {
56
+ records,
57
+ page,
58
+ pageSize,
59
+ sortStatus,
60
+ totalRecords: data.length,
61
+ onPageChange: setPage,
62
+ onPageSizeChange: setPageSize,
63
+ onSortStatusChange: changeSortStatus
64
+ };
65
+ }
66
+ export { useClientTableState };
67
+
68
+ //# sourceMappingURL=useClientTableState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useClientTableState.js","names":[],"sources":["../../../../src/component/organism/ViewTable/useClientTableState.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\n\nimport type { DataTableSortStatus } from 'mantine-datatable';\n\nimport { columnComparator, Comparator } from './ColumnComparator';\nimport { DEFAULT_SORT_STATUS, FIRST_PAGE, PAGE_SIZES, TableState } from './TableState';\n\n/**\n * Sort used while the `compare` function applies, when `defaultSortStatus` is not given: it matches no column, so no\n * header shows the sort indicator, and the first click on any column sorts it in ascending order.\n */\nconst NO_COLUMN_SORT_STATUS = { columnAccessor: '', direction: 'asc' } as const;\n\n/** Options of {@link useClientTableState}. */\ntype ClientTableOptions<M> = {\n /** All records of the table. */\n data: M[];\n\n /** Whether only the records of the current page are returned. */\n pagination: boolean;\n\n /** Locale of the text comparison. */\n locale: string;\n\n /** Initial sort; defaults to {@link DEFAULT_SORT_STATUS}, or to no column when `compare` is given. */\n defaultSortStatus?: DataTableSortStatus<M>;\n\n /** Comparison function of the initial order, replaced by the sort of the column chosen in the header. */\n compare?: Comparator<M>;\n};\n\n/**\n * Sorts and paginates in the browser all the records of a table.\n * @template M type of the records\n * @return the state of the table and the records to show\n */\nexport function useClientTableState<M>({\n data,\n pagination,\n locale,\n defaultSortStatus,\n compare\n}: ClientTableOptions<M>): TableState<M> & { records: M[] } {\n const [page, setPage] = useState(FIRST_PAGE);\n const [pageSize, setPageSize] = useState(PAGE_SIZES[0]);\n const [sortStatus, setSortStatus] = useState<DataTableSortStatus<M>>(\n defaultSortStatus ?? (compare ? NO_COLUMN_SORT_STATUS : DEFAULT_SORT_STATUS)\n );\n // The `compare` function applies until the user chooses a column in the header.\n const [compareActive, setCompareActive] = useState(compare !== undefined);\n\n /*\n * Sorts all records before cutting the page, so each page (including in descending order) shows its part of the\n * whole set. The copy keeps the order of the given array. It is separate from the page cut, so not everything is\n * sorted again on every change of page or page size.\n */\n const sortedData = useMemo(() => {\n const comparator = compareActive && compare ? compare : columnComparator(sortStatus, locale);\n\n return [...data].sort(comparator);\n }, [data, sortStatus, compareActive, compare, locale]);\n\n // Without pagination, shows all records (e.g. a printed report), not only the ones of the first page.\n const records = useMemo(() => {\n if (!pagination) return sortedData;\n\n const from = (page - 1) * pageSize;\n return sortedData.slice(from, from + pageSize);\n }, [sortedData, pagination, page, pageSize]);\n\n function changeSortStatus(status: DataTableSortStatus<M>): void {\n setCompareActive(false);\n setSortStatus(status);\n }\n\n useEffect(() => {\n setPage(FIRST_PAGE);\n }, [pageSize]);\n\n /* If the data shrinks, goes back to the last existing page. The functional update avoids using an outdated `page`\n when the effect above has just gone back to page 1. */\n useEffect(() => {\n const totalPages = Math.ceil(data.length / pageSize);\n setPage(currentPage => (totalPages && currentPage > totalPages ? totalPages : currentPage));\n }, [data.length, pageSize]);\n\n return {\n records,\n page,\n pageSize,\n sortStatus,\n totalRecords: data.length,\n onPageChange: setPage,\n onPageSizeChange: setPageSize,\n onSortStatusChange: changeSortStatus\n };\n}\n"],"mappings":";;;;;;;;;AAWA,IAAM,wBAAwB;CAAE,gBAAgB;CAAI,WAAW;AAAM;;;;;;AAyBrE,SAAgB,oBAAuB,EACnC,MACA,YACA,QACA,mBACA,WACwD;CACxD,MAAM,CAAC,MAAM,WAAW,SAAA,CAAmB;CAC3C,MAAM,CAAC,UAAU,eAAe,SAAS,WAAW,EAAE;CACtD,MAAM,CAAC,YAAY,iBAAiB,SAChC,sBAAsB,UAAU,wBAAwB,oBAC5D;CAEA,MAAM,CAAC,eAAe,oBAAoB,SAAS,YAAY,KAAA,CAAS;CAOxE,MAAM,aAAa,cAAc;EAC7B,MAAM,aAAa,iBAAiB,UAAU,UAAU,iBAAiB,YAAY,MAAM;EAE3F,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,UAAU;CACpC,GAAG;EAAC;EAAM;EAAY;EAAe;EAAS;CAAM,CAAC;CAGrD,MAAM,UAAU,cAAc;EAC1B,IAAI,CAAC,YAAY,OAAO;EAExB,MAAM,QAAQ,OAAO,KAAK;EAC1B,OAAO,WAAW,MAAM,MAAM,OAAO,QAAQ;CACjD,GAAG;EAAC;EAAY;EAAY;EAAM;CAAQ,CAAC;CAE3C,SAAS,iBAAiB,QAAsC;EAC5D,iBAAiB,KAAK;EACtB,cAAc,MAAM;CACxB;CAEA,gBAAgB;EACZ,QAAA,CAAkB;CACtB,GAAG,CAAC,QAAQ,CAAC;CAIb,gBAAgB;EACZ,MAAM,aAAa,KAAK,KAAK,KAAK,SAAS,QAAQ;EACnD,SAAQ,gBAAgB,cAAc,cAAc,aAAa,aAAa,WAAY;CAC9F,GAAG,CAAC,KAAK,QAAQ,QAAQ,CAAC;CAE1B,OAAO;EACH;EACA;EACA;EACA;EACA,cAAc,KAAK;EACnB,cAAc;EACd,kBAAkB;EAClB,oBAAoB;CACxB;AACJ"}
@@ -1,10 +1,7 @@
1
1
  import { ReactElement, ReactNode } from 'react';
2
- import { DataTableColumn } from 'mantine-datatable';
3
- import { ListDataSource } from '../../../data/ListDataSource';
4
- import { Identifiable } from '../../../model/Identifiable';
5
2
  import { BreadcrumbItem } from '../../molecule/Breadcrumb/Breadcrumb';
6
- import { ActionsSettings } from '../../organism/Table/Table';
7
- interface ListPageProps<T, S> {
3
+ /** Props of the {@link ListPage}. */
4
+ type ListPageProps = {
8
5
  /** Title of the page (e.g. "Regions"). */
9
6
  title: string;
10
7
  /** Plural name of the records, used in the title of the table card. */
@@ -12,21 +9,14 @@ interface ListPageProps<T, S> {
12
9
  breadcrumbItems: BreadcrumbItem[];
13
10
  /** Route of the "Add" button; absent for no button. */
14
11
  addActionRoute?: string;
15
- /** Source of the records and of their deletion. */
16
- service: S;
17
- tableColumns: DataTableColumn<T>[];
18
- /** Base path of the pages of the records (e.g. `/region`). */
19
- resourceBasePath: string;
20
- actionsSettings?: Partial<ActionsSettings>;
21
12
  /** Extra content above the table (e.g. filters). */
22
- children?: ReactNode;
23
- customFetchMethod?: () => Promise<T[]>;
24
- /** Builds the URL opened to view a record. If absent, the default view route is used. */
25
- getViewUrl?: (item: T) => string;
26
- }
13
+ filters?: ReactNode;
14
+ /** Table of the records (e.g. a `Table`). */
15
+ children: ReactNode;
16
+ };
27
17
  /**
28
- * List page of records: header with the "Add" button, optional extra content (e.g. filters) and the table of the
29
- * records, with the view, edit and delete actions.
18
+ * List page of records: header with the "Add" button, optional filters and the table of the records, given as
19
+ * children, inside a card.
30
20
  */
31
- declare function ListPage<M extends Identifiable, S extends ListDataSource<M>>(props: ListPageProps<M, S>): ReactElement;
21
+ declare function ListPage({ title, titlePlural, breadcrumbItems, addActionRoute, filters, children }: ListPageProps): ReactElement;
32
22
  export default ListPage;
@@ -2,16 +2,14 @@
2
2
  import { useDocumentTitle, useKitMessages } from "../../../provider/useKit.js";
3
3
  import PaperCard from "../../molecule/PaperCard/PaperCard.js";
4
4
  import PageHeader from "../../organism/PageHeader/PageHeader.js";
5
- import Table from "../../organism/Table/Table.js";
6
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
6
  import { Group, Stack, Text } from "@mantine/core";
8
7
  // src/component/template/ListPage/ListPage.tsx
9
8
  /**
10
- * List page of records: header with the "Add" button, optional extra content (e.g. filters) and the table of the
11
- * records, with the view, edit and delete actions.
9
+ * List page of records: header with the "Add" button, optional filters and the table of the records, given as
10
+ * children, inside a card.
12
11
  */
13
- function ListPage(props) {
14
- const { title, titlePlural, breadcrumbItems, addActionRoute, service, tableColumns, resourceBasePath, actionsSettings, children, customFetchMethod, getViewUrl } = props;
12
+ function ListPage({ title, titlePlural, breadcrumbItems, addActionRoute, filters, children }) {
15
13
  const documentTitle = useDocumentTitle(title);
16
14
  const messages = useKitMessages();
17
15
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("title", { children: documentTitle }), /* @__PURE__ */ jsxs(Stack, {
@@ -22,7 +20,7 @@ function ListPage(props) {
22
20
  breadcrumbItems,
23
21
  addActionRoute
24
22
  }),
25
- children && /* @__PURE__ */ jsx(Stack, { children }),
23
+ filters && /* @__PURE__ */ jsx(Stack, { children: filters }),
26
24
  /* @__PURE__ */ jsxs(PaperCard, { children: [/* @__PURE__ */ jsx(Group, {
27
25
  justify: "space-between",
28
26
  mb: "md",
@@ -31,14 +29,7 @@ function ListPage(props) {
31
29
  fw: 600,
32
30
  children: messages.table.listTitle(titlePlural)
33
31
  })
34
- }), /* @__PURE__ */ jsx(Table, {
35
- service,
36
- basePath: resourceBasePath,
37
- columns: tableColumns,
38
- customFetchMethod,
39
- actionsSettings,
40
- getViewUrl
41
- })] })
32
+ }), children] })
42
33
  ]
43
34
  })] });
44
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ListPage.js","names":["_Fragment"],"sources":["../../../../src/component/template/ListPage/ListPage.tsx"],"sourcesContent":["import { ReactElement, ReactNode } from 'react';\n\nimport { Group, Stack, Text } from '@mantine/core';\nimport { DataTableColumn } from 'mantine-datatable';\n\nimport { ListDataSource } from '../../../data/ListDataSource';\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useDocumentTitle, useKitMessages } from '../../../provider/useKit';\nimport { BreadcrumbItem } from '../../molecule/Breadcrumb/Breadcrumb';\nimport PaperCard from '../../molecule/PaperCard/PaperCard';\nimport PageHeader from '../../organism/PageHeader/PageHeader';\nimport Table, { ActionsSettings } from '../../organism/Table/Table';\n\ninterface ListPageProps<T, S> {\n /** Title of the page (e.g. \"Regions\"). */\n title: string;\n\n /** Plural name of the records, used in the title of the table card. */\n titlePlural: string;\n breadcrumbItems: BreadcrumbItem[];\n\n /** Route of the \"Add\" button; absent for no button. */\n addActionRoute?: string;\n\n /** Source of the records and of their deletion. */\n service: S;\n tableColumns: DataTableColumn<T>[];\n\n /** Base path of the pages of the records (e.g. `/region`). */\n resourceBasePath: string;\n actionsSettings?: Partial<ActionsSettings>;\n\n /** Extra content above the table (e.g. filters). */\n children?: ReactNode;\n customFetchMethod?: () => Promise<T[]>;\n\n /** Builds the URL opened to view a record. If absent, the default view route is used. */\n getViewUrl?: (item: T) => string;\n}\n\n/**\n * List page of records: header with the \"Add\" button, optional extra content (e.g. filters) and the table of the\n * records, with the view, edit and delete actions.\n */\nfunction ListPage<M extends Identifiable, S extends ListDataSource<M>>(props: ListPageProps<M, S>): ReactElement {\n const {\n title,\n titlePlural,\n breadcrumbItems,\n addActionRoute,\n service,\n tableColumns,\n resourceBasePath,\n actionsSettings,\n children,\n customFetchMethod,\n getViewUrl\n } = props;\n const documentTitle = useDocumentTitle(title);\n const messages = useKitMessages();\n\n return (\n <>\n <title>{documentTitle}</title>\n <Stack gap=\"lg\">\n <PageHeader title={title} breadcrumbItems={breadcrumbItems} addActionRoute={addActionRoute} />\n {children && <Stack>{children}</Stack>}\n <PaperCard>\n <Group justify=\"space-between\" mb=\"md\">\n <Text fz=\"lg\" fw={600}>\n {messages.table.listTitle(titlePlural)}\n </Text>\n </Group>\n <Table<M, S>\n service={service}\n basePath={resourceBasePath}\n columns={tableColumns}\n customFetchMethod={customFetchMethod}\n actionsSettings={actionsSettings}\n getViewUrl={getViewUrl}\n />\n </PaperCard>\n </Stack>\n </>\n );\n}\n\nexport default ListPage;\n"],"mappings":";;;;;;;;;;;;AA4CA,SAAS,SAA8D,OAA0C;CAC7G,MAAM,EACF,OACA,aACA,iBACA,gBACA,SACA,cACA,kBACA,iBACA,UACA,mBACA,eACA;CACJ,MAAM,gBAAgB,iBAAiB,KAAK;CAC5C,MAAM,WAAW,eAAe;CAEhC,OACI,qBAAAA,UAAA,EAAA,UAAA,CACI,oBAAC,SAAD,EAAA,UAAQ,cAAqB,CAAA,GAC7B,qBAAC,OAAD;EAAO,KAAI;EAAX,UAAA;GACI,oBAAC,YAAD;IAAmB;IAAwB;IAAiC;GAAiB,CAAA;GAC5F,YAAY,oBAAC,OAAD,EAAQ,SAAgB,CAAA;GACrC,qBAAC,WAAD,EAAA,UAAA,CACI,oBAAC,OAAD;IAAO,SAAQ;IAAgB,IAAG;IAC9B,UAAA,oBAAC,MAAD;KAAM,IAAG;KAAK,IAAI;KACb,UAAA,SAAS,MAAM,UAAU,WAAW;IACnC,CAAA;GACH,CAAA,GACP,oBAAC,OAAD;IACa;IACT,UAAU;IACV,SAAS;IACU;IACF;IACL;GACf,CAAA,CACM,EAAA,CAAA;EACR;CACT,CAAA,CAAA,EAAA,CAAA;AAEV"}
1
+ {"version":3,"file":"ListPage.js","names":["_Fragment"],"sources":["../../../../src/component/template/ListPage/ListPage.tsx"],"sourcesContent":["import { ReactElement, ReactNode } from 'react';\n\nimport { Group, Stack, Text } from '@mantine/core';\n\nimport { useDocumentTitle, useKitMessages } from '../../../provider/useKit';\nimport { BreadcrumbItem } from '../../molecule/Breadcrumb/Breadcrumb';\nimport PaperCard from '../../molecule/PaperCard/PaperCard';\nimport PageHeader from '../../organism/PageHeader/PageHeader';\n\n/** Props of the {@link ListPage}. */\ntype ListPageProps = {\n /** Title of the page (e.g. \"Regions\"). */\n title: string;\n\n /** Plural name of the records, used in the title of the table card. */\n titlePlural: string;\n breadcrumbItems: BreadcrumbItem[];\n\n /** Route of the \"Add\" button; absent for no button. */\n addActionRoute?: string;\n\n /** Extra content above the table (e.g. filters). */\n filters?: ReactNode;\n\n /** Table of the records (e.g. a `Table`). */\n children: ReactNode;\n};\n\n/**\n * List page of records: header with the \"Add\" button, optional filters and the table of the records, given as\n * children, inside a card.\n */\nfunction ListPage({\n title,\n titlePlural,\n breadcrumbItems,\n addActionRoute,\n filters,\n children\n}: ListPageProps): ReactElement {\n const documentTitle = useDocumentTitle(title);\n const messages = useKitMessages();\n\n return (\n <>\n <title>{documentTitle}</title>\n <Stack gap=\"lg\">\n <PageHeader title={title} breadcrumbItems={breadcrumbItems} addActionRoute={addActionRoute} />\n {filters && <Stack>{filters}</Stack>}\n <PaperCard>\n <Group justify=\"space-between\" mb=\"md\">\n <Text fz=\"lg\" fw={600}>\n {messages.table.listTitle(titlePlural)}\n </Text>\n </Group>\n {children}\n </PaperCard>\n </Stack>\n </>\n );\n}\n\nexport default ListPage;\n"],"mappings":";;;;;;;;;;;AAgCA,SAAS,SAAS,EACd,OACA,aACA,iBACA,gBACA,SACA,YAC4B;CAC5B,MAAM,gBAAgB,iBAAiB,KAAK;CAC5C,MAAM,WAAW,eAAe;CAEhC,OACI,qBAAAA,UAAA,EAAA,UAAA,CACI,oBAAC,SAAD,EAAA,UAAQ,cAAqB,CAAA,GAC7B,qBAAC,OAAD;EAAO,KAAI;EAAX,UAAA;GACI,oBAAC,YAAD;IAAmB;IAAwB;IAAiC;GAAiB,CAAA;GAC5F,WAAW,oBAAC,OAAD,EAAA,UAAQ,QAAe,CAAA;GACnC,qBAAC,WAAD,EAAA,UAAA,CACI,oBAAC,OAAD;IAAO,SAAQ;IAAgB,IAAG;IAC9B,UAAA,oBAAC,MAAD;KAAM,IAAG;KAAK,IAAI;KACb,UAAA,SAAS,MAAM,UAAU,WAAW;IACnC,CAAA;GACH,CAAA,GACN,QACM,EAAA,CAAA;EACR;CACT,CAAA,CAAA,EAAA,CAAA;AAEV"}
@@ -0,0 +1,10 @@
1
+ import type { PageRequest, PageResult } from './PageRequest';
2
+ /**
3
+ * Source of the pages of a table, sorted and paginated by the backend (e.g. a service that accesses a paginated API).
4
+ * Any object with this method works, without extending a class.
5
+ * @template M type of the records
6
+ */
7
+ export interface PageDataSource<M> {
8
+ /** Fetches a page of records. */
9
+ findPage(request: PageRequest<M>): Promise<PageResult<M>>;
10
+ }
@@ -0,0 +1 @@
1
+ "use client";
@@ -0,0 +1,29 @@
1
+ import type { DataTableSortStatus } from 'mantine-datatable';
2
+ /**
3
+ * Page of records requested from the backend.
4
+ * @template M type of the records
5
+ */
6
+ export type PageRequest<M> = {
7
+ /** Number of the page, starting at the `firstPage` option of `useServerPagination` (1 by default). */
8
+ page: number;
9
+ /** Maximum number of records of the page. */
10
+ size: number;
11
+ /** Sort to apply. `columnAccessor` holds the `sortKey` of the column, if it has one, or else its `accessor`. */
12
+ sort: DataTableSortStatus<M>;
13
+ };
14
+ /**
15
+ * Page of records returned by the backend.
16
+ * @template M type of the records
17
+ */
18
+ export type PageResult<M> = {
19
+ /** Records of the page, already sorted by the backend. */
20
+ records: M[];
21
+ /** Total of records of all pages. */
22
+ totalRecords: number;
23
+ };
24
+ /**
25
+ * Fetches a page of records, as the `findPage` of a `PageDataSource`. It must be stable (e.g. with `useCallback`),
26
+ * since a new function means that the filter changed, so the table fetches again from page 1.
27
+ * @template M type of the records
28
+ */
29
+ export type FetchPage<M> = (request: PageRequest<M>) => Promise<PageResult<M>>;
@@ -0,0 +1 @@
1
+ "use client";
@@ -1,10 +1,10 @@
1
1
  /** Values returned by {@link useService}. */
2
2
  export type UseService<D> = {
3
- /** Data of the fetch, or the initial value while it has not finished (or if it failed). */
3
+ /** Data of the last successful fetch, or the initial value if none has finished yet. */
4
4
  data: D;
5
- /** Error of the fetch; `null` if it has not finished yet or succeeded. */
5
+ /** Error of the current fetch; `null` if it has not finished yet or succeeded. */
6
6
  error: unknown | null;
7
- /** Whether the fetch has not finished yet. */
7
+ /** Whether the current fetch has not finished yet. */
8
8
  loading: boolean;
9
9
  };
10
10
  /**
@@ -7,6 +7,8 @@ function useService(fetchMethod, initialData) {
7
7
  const [loading, setLoading] = useState(true);
8
8
  useEffect(() => {
9
9
  let obsolete = false;
10
+ setLoading(true);
11
+ setError(null);
10
12
  fetchMethod().then((responseData) => !obsolete && setData(responseData)).catch((e) => !obsolete && setError(e)).finally(() => !obsolete && setLoading(false));
11
13
  return () => {
12
14
  obsolete = true;
@@ -1 +1 @@
1
- {"version":3,"file":"useService.js","names":[],"sources":["../../src/hook/useService.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\n/** Values returned by {@link useService}. */\nexport type UseService<D> = {\n /** Data of the fetch, or the initial value while it has not finished (or if it failed). */\n data: D;\n\n /** Error of the fetch; `null` if it has not finished yet or succeeded. */\n error: unknown | null;\n\n /** Whether the fetch has not finished yet. */\n loading: boolean;\n};\n\n/**\n * Fetches data when the component mounts and every time `fetchMethod` changes.\n * Without an initial value, `data` is `undefined` while loading (or after a failure), so the caller must handle it.\n * @param fetchMethod function that fetches the data. It must be stable (e.g. with `useCallback`), since a new function\n * triggers a new fetch\n * @return the data (`undefined` until the fetch finishes), the error and whether the fetch is still running\n */\nfunction useService<T>(fetchMethod: () => Promise<T>): UseService<T | undefined>;\n\n/**\n * Fetches data when the component mounts and every time `fetchMethod` changes, starting with the given value (e.g. an\n * empty array for a list), so `data` always has the type of the fetched data.\n * @param fetchMethod function that fetches the data. It must be stable (e.g. with `useCallback`), since a new function\n * triggers a new fetch\n * @param initialData value of `data` until the fetch finishes (and after a failure)\n * @return the data, the error and whether the fetch is still running\n */\nfunction useService<T>(fetchMethod: () => Promise<T>, initialData: T): UseService<T>;\n\nfunction useService<T>(fetchMethod: () => Promise<T>, initialData?: T): UseService<T | undefined> {\n const [data, setData] = useState<T | undefined>(initialData);\n const [error, setError] = useState<unknown | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n /* When fetchMethod changes (e.g. another filter), the previous fetch becomes obsolete. If its response arrives\n after the one of the new fetch, it cannot overwrite the current data. */\n let obsolete = false;\n fetchMethod()\n .then(responseData => !obsolete && setData(responseData))\n .catch(e => !obsolete && setError(e))\n .finally(() => !obsolete && setLoading(false));\n\n return () => {\n obsolete = true;\n };\n }, [fetchMethod]);\n\n return { data, error, loading };\n}\n\nexport default useService;\n"],"mappings":";;;AAiCA,SAAS,WAAc,aAA+B,aAA4C;CAC9F,MAAM,CAAC,MAAM,WAAW,SAAwB,WAAW;CAC3D,MAAM,CAAC,OAAO,YAAY,SAAyB,IAAI;CACvD,MAAM,CAAC,SAAS,cAAc,SAAS,IAAI;CAE3C,gBAAgB;EAGZ,IAAI,WAAW;EACf,YAAY,CAAC,CACR,MAAK,iBAAgB,CAAC,YAAY,QAAQ,YAAY,CAAC,CAAC,CACxD,OAAM,MAAK,CAAC,YAAY,SAAS,CAAC,CAAC,CAAC,CACpC,cAAc,CAAC,YAAY,WAAW,KAAK,CAAC;EAEjD,aAAa;GACT,WAAW;EACf;CACJ,GAAG,CAAC,WAAW,CAAC;CAEhB,OAAO;EAAE;EAAM;EAAO;CAAQ;AAClC"}
1
+ {"version":3,"file":"useService.js","names":[],"sources":["../../src/hook/useService.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\n/** Values returned by {@link useService}. */\nexport type UseService<D> = {\n /** Data of the last successful fetch, or the initial value if none has finished yet. */\n data: D;\n\n /** Error of the current fetch; `null` if it has not finished yet or succeeded. */\n error: unknown | null;\n\n /** Whether the current fetch has not finished yet. */\n loading: boolean;\n};\n\n/**\n * Fetches data when the component mounts and every time `fetchMethod` changes.\n * Without an initial value, `data` is `undefined` while loading (or after a failure), so the caller must handle it.\n * @param fetchMethod function that fetches the data. It must be stable (e.g. with `useCallback`), since a new function\n * triggers a new fetch\n * @return the data (`undefined` until the fetch finishes), the error and whether the fetch is still running\n */\nfunction useService<T>(fetchMethod: () => Promise<T>): UseService<T | undefined>;\n\n/**\n * Fetches data when the component mounts and every time `fetchMethod` changes, starting with the given value (e.g. an\n * empty array for a list), so `data` always has the type of the fetched data.\n * @param fetchMethod function that fetches the data. It must be stable (e.g. with `useCallback`), since a new function\n * triggers a new fetch\n * @param initialData value of `data` until the fetch finishes (and after a failure)\n * @return the data, the error and whether the fetch is still running\n */\nfunction useService<T>(fetchMethod: () => Promise<T>, initialData: T): UseService<T>;\n\nfunction useService<T>(fetchMethod: () => Promise<T>, initialData?: T): UseService<T | undefined> {\n const [data, setData] = useState<T | undefined>(initialData);\n const [error, setError] = useState<unknown | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n /* When fetchMethod changes (e.g. another filter), the previous fetch becomes obsolete. If its response arrives\n after the one of the new fetch, it cannot overwrite the current data. */\n let obsolete = false;\n setLoading(true);\n setError(null);\n fetchMethod()\n .then(responseData => !obsolete && setData(responseData))\n .catch(e => !obsolete && setError(e))\n .finally(() => !obsolete && setLoading(false));\n\n return () => {\n obsolete = true;\n };\n }, [fetchMethod]);\n\n return { data, error, loading };\n}\n\nexport default useService;\n"],"mappings":";;;AAiCA,SAAS,WAAc,aAA+B,aAA4C;CAC9F,MAAM,CAAC,MAAM,WAAW,SAAwB,WAAW;CAC3D,MAAM,CAAC,OAAO,YAAY,SAAyB,IAAI;CACvD,MAAM,CAAC,SAAS,cAAc,SAAS,IAAI;CAE3C,gBAAgB;EAGZ,IAAI,WAAW;EACf,WAAW,IAAI;EACf,SAAS,IAAI;EACb,YAAY,CAAC,CACR,MAAK,iBAAgB,CAAC,YAAY,QAAQ,YAAY,CAAC,CAAC,CACxD,OAAM,MAAK,CAAC,YAAY,SAAS,CAAC,CAAC,CAAC,CACpC,cAAc,CAAC,YAAY,WAAW,KAAK,CAAC;EAEjD,aAAa;GACT,WAAW;EACf;CACJ,GAAG,CAAC,WAAW,CAAC;CAEhB,OAAO;EAAE;EAAM;EAAO;CAAQ;AAClC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ifto/mantine-kit",
3
- "version": "0.3.0",
3
+ "version": "0.5.1",
4
4
  "description": "General purpose Mantine components for React apps: CRUD tables and forms, layout, ECharts charts, Excel/CSV export, print pages and a REST HTTP client, with i18n (en-US and pt-BR).",
5
5
  "license": "MIT",
6
6
  "author": "IFTO - Campus Palmas",