@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.
- package/README.md +85 -10
- package/dist/component/organism/EChart/chart/BarChart.d.ts +3 -0
- package/dist/component/organism/EChart/chart/BarChart.js +5 -0
- package/dist/component/organism/EChart/chart/BarChart.js.map +1 -1
- package/dist/component/organism/EChart/chart/CartesianChart.d.ts +4 -0
- package/dist/component/organism/EChart/chart/CartesianChart.js +5 -0
- package/dist/component/organism/EChart/chart/CartesianChart.js.map +1 -1
- package/dist/component/organism/EChart/chart/ChartType.d.ts +3 -0
- package/dist/component/organism/EChart/chart/DonutChart.d.ts +4 -0
- package/dist/component/organism/EChart/chart/DonutChart.js +6 -0
- package/dist/component/organism/EChart/chart/DonutChart.js.map +1 -1
- package/dist/component/organism/EChart/chart/LineChart.d.ts +3 -0
- package/dist/component/organism/EChart/chart/LineChart.js +5 -0
- package/dist/component/organism/EChart/chart/LineChart.js.map +1 -1
- package/dist/component/organism/EChart/chart/StackedBarChart.d.ts +3 -0
- package/dist/component/organism/EChart/chart/StackedBarChart.js +5 -0
- package/dist/component/organism/EChart/chart/StackedBarChart.js.map +1 -1
- package/dist/component/organism/Table/Table.d.ts +9 -9
- package/dist/component/organism/Table/Table.js +21 -24
- package/dist/component/organism/Table/Table.js.map +1 -1
- package/dist/component/organism/Table/TableData.d.ts +24 -0
- package/dist/component/organism/Table/TableData.js +1 -0
- package/dist/component/organism/Table/useClientPagination.d.ts +21 -0
- package/dist/component/organism/Table/useClientPagination.js +43 -0
- package/dist/component/organism/Table/useClientPagination.js.map +1 -0
- package/dist/component/organism/Table/useServerPagination.d.ts +24 -0
- package/dist/component/organism/Table/useServerPagination.js +105 -0
- package/dist/component/organism/Table/useServerPagination.js.map +1 -0
- package/dist/component/organism/ViewTable/DataTableView.d.ts +39 -0
- package/dist/component/organism/ViewTable/DataTableView.js +52 -0
- package/dist/component/organism/ViewTable/DataTableView.js.map +1 -0
- package/dist/component/organism/ViewTable/TableState.d.ts +30 -0
- package/dist/component/organism/ViewTable/TableState.js +19 -0
- package/dist/component/organism/ViewTable/TableState.js.map +1 -0
- package/dist/component/organism/ViewTable/ViewTable.d.ts +11 -33
- package/dist/component/organism/ViewTable/ViewTable.js +16 -101
- package/dist/component/organism/ViewTable/ViewTable.js.map +1 -1
- package/dist/component/organism/ViewTable/useClientTableState.d.ts +25 -0
- package/dist/component/organism/ViewTable/useClientTableState.js +68 -0
- package/dist/component/organism/ViewTable/useClientTableState.js.map +1 -0
- package/dist/component/template/ListPage/ListPage.d.ts +9 -19
- package/dist/component/template/ListPage/ListPage.js +5 -14
- package/dist/component/template/ListPage/ListPage.js.map +1 -1
- package/dist/data/PageDataSource.d.ts +10 -0
- package/dist/data/PageDataSource.js +1 -0
- package/dist/data/PageRequest.d.ts +29 -0
- package/dist/data/PageRequest.js +1 -0
- package/dist/hook/useService.d.ts +3 -3
- package/dist/hook/useService.js +2 -0
- package/dist/hook/useService.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Table.js","names":[],"sources":["../../../../src/component/organism/Table/Table.tsx"],"sourcesContent":["'use client';\n\nimport { ReactElement,
|
|
1
|
+
{"version":3,"file":"Table.js","names":[],"sources":["../../../../src/component/organism/Table/Table.tsx"],"sourcesContent":["'use client';\n\nimport { ReactElement, useMemo } from 'react';\n\nimport { Box } from '@mantine/core';\nimport { DataTableColumn } from 'mantine-datatable';\n\nimport DataTableView from '../ViewTable/DataTableView';\nimport { SetRecords, TableData } from './TableData';\nimport RowActions from './RowActions';\nimport { ListDataSource } from '../../../data/ListDataSource';\nimport { KitMessages } from '../../../i18n/KitMessages';\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useKitConfig } from '../../../provider/useKit';\nimport { FormRouteSegments } from '../../../router/FormRouteSegments';\nimport ErrorAlert from '../../atom/ErrorAlert/ErrorAlert';\nimport { showErrorNotification, showSuccessNotification } from '../../../util/Notifications';\n\n/*\nColor of the row under the mouse. The default color of mantine-datatable (gray-1) is barely distinguishable from the\nzebra stripes (gray-0), so the highlight went unnoticed. The light variant of the primary color adapts to the light and\ndark color schemes.\n*/\nconst ROW_HOVER_COLOR = 'var(--mantine-primary-color-light)';\n\n/** Which actions the table shows for each record. */\nexport type ActionsSettings = {\n /** Whether the actions column is shown. */\n canShowActions: boolean;\n\n /** Whether the view action is shown. */\n canView: boolean;\n\n /** Whether the edit action is shown. */\n canEdit: boolean;\n\n /** Whether the delete action is shown. */\n canDelete: boolean;\n};\n\n/** Every action shown, used for the settings the caller does not give. */\nconst DEFAULT_ACTIONS_SETTINGS: ActionsSettings = {\n canShowActions: true,\n canView: true,\n canEdit: true,\n canDelete: true\n};\n\n/** Props of the {@link Table}. */\ntype TableProps<M extends Identifiable, S extends ListDataSource<M>> = {\n /**\n * Records and their pagination and sort, given by `useClientPagination` (sorted and paginated in the browser) or\n * `useServerPagination` (sorted and paginated by the backend).\n */\n data: TableData<M>;\n\n /** Source of the records, which deletes them (e.g. a service that accesses an API). */\n service: S;\n\n /** Columns of the table. */\n columns: DataTableColumn<M>[];\n\n /** Base path of the pages of the records (e.g. `/region`), used to build the view and edit URLs. */\n basePath: string;\n\n /** Which actions are shown; the ones not given are shown. */\n actionsSettings?: Partial<ActionsSettings>;\n\n /**\n * Called by the edit action instead of opening the edit page of the record.\n * @param itemId id of the record to edit\n */\n customEditFunction?: (itemId: M['id']) => void;\n\n /**\n * Builds the URL opened to view a record (click on the row or on the view icon).\n * If absent, the default route is used: `${basePath}/<view segment>/${item.id}`.\n */\n getViewUrl?: (item: M) => string;\n};\n\n/**\n * Returns the view URL of a record: the one of `getViewUrl`, if given; otherwise, the default view route.\n * @param props props of the table\n * @param item record\n * @param segments URL segments of the form pages\n */\nfunction viewUrl<M extends Identifiable, S extends ListDataSource<M>>(\n props: TableProps<M, S>,\n item: M,\n segments: FormRouteSegments\n): string {\n return props.getViewUrl ? props.getViewUrl(item) : `${props.basePath}/${segments.view}/${item.id}`;\n}\n\n/**\n * Creates the function that deletes a record and reloads the table.\n * @param setData function that sets the records of the table\n * @param service source of the records, which deletes them\n * @param reload fetches the records again after the deletion\n * @param messages texts of the locale\n * @return a function that receives the record to delete and returns a promise resolved when it is deleted\n */\nfunction getDeleteRecordFn<M extends Identifiable, S extends ListDataSource<M>>(\n setData: SetRecords<M>,\n service: S,\n reload: () => Promise<void>,\n messages: KitMessages\n): (item: M) => Promise<void> {\n return async (item: M) => {\n const id = item?.id ?? 0;\n\n // Without an id, the record was added only to the array and was not sent to the backend: just remove it.\n if (!id) {\n setData(prevData => prevData.filter(current => current !== item));\n return;\n }\n\n try {\n await service.delete(id);\n showSuccessNotification(messages.table.deleteSuccess, messages.common.success);\n await reload();\n } catch (e) {\n showErrorNotification(e, undefined, messages.common.error);\n }\n };\n}\n\n/**\n * A table that shows the records of a data source, with the view, edit and delete actions, or an alert when they cannot\n * be fetched. Clicking a row opens the record. Its `data` comes from `useClientPagination` or `useServerPagination`,\n * which define where the records are sorted and paginated.\n * @template M type of the records, shown one per row\n * @template S source of the records (e.g. a service that accesses an API)\n */\nconst Table = <M extends Identifiable, S extends ListDataSource<M>>(props: TableProps<M, S>): ReactElement => {\n const { service, columns, basePath, customEditFunction, data } = props;\n const { setRecords, reload } = data;\n const { router, messages, formRouteSegments } = useKitConfig();\n const navigate = router.useNavigate();\n const settings: ActionsSettings = { ...DEFAULT_ACTIONS_SETTINGS, ...props.actionsSettings };\n\n const deleteRecord = useMemo(\n () => getDeleteRecordFn(setRecords, service, reload, messages),\n [setRecords, service, reload, messages]\n );\n\n const actionsColumn: DataTableColumn<M> = {\n accessor: 'actions',\n title: <Box mr={6}>{messages.table.actions}</Box>,\n textAlign: 'right',\n render: item => (\n <RowActions\n {...settings}\n viewUrl={viewUrl(props, item, formRouteSegments)}\n editUrl={item.id ? `${basePath}/${formRouteSegments.edit}/${item.id}` : undefined}\n onEdit={customEditFunction && item.id ? () => customEditFunction(item.id) : undefined}\n onDelete={() => deleteRecord(item)}\n />\n )\n };\n const effectiveColumns = settings.canShowActions ? [...columns, actionsColumn] : columns;\n\n if (data.error) {\n return <ErrorAlert title={messages.common.loadError} message={String(data.error)} />;\n }\n\n return (\n <DataTableView\n records={data.records}\n state={data.tableState}\n paginated={data.paginated}\n columns={effectiveColumns}\n loading={data.loading}\n // Highlights the row under the mouse, to show that clicking it opens the record, as the view icon does.\n highlightOnHover\n highlightOnHoverColor={ROW_HOVER_COLOR}\n onRowClick={({ record }) => navigate(viewUrl(props, record, formRouteSegments))}\n />\n );\n};\n\nexport default Table;\n"],"mappings":";;;;;;;;;;AAuBA,IAAM,kBAAkB;;AAkBxB,IAAM,2BAA4C;CAC9C,gBAAgB;CAChB,SAAS;CACT,SAAS;CACT,WAAW;AACf;;;;;;;AAyCA,SAAS,QACL,OACA,MACA,UACM;CACN,OAAO,MAAM,aAAa,MAAM,WAAW,IAAI,IAAI,GAAG,MAAM,SAAS,GAAG,SAAS,KAAK,GAAG,KAAK;AAClG;;;;;;;;;AAUA,SAAS,kBACL,SACA,SACA,QACA,UAC0B;CAC1B,OAAO,OAAO,SAAY;EACtB,MAAM,KAAK,MAAM,MAAM;EAGvB,IAAI,CAAC,IAAI;GACL,SAAQ,aAAY,SAAS,QAAO,YAAW,YAAY,IAAI,CAAC;GAChE;EACJ;EAEA,IAAI;GACA,MAAM,QAAQ,OAAO,EAAE;GACvB,wBAAwB,SAAS,MAAM,eAAe,SAAS,OAAO,OAAO;GAC7E,MAAM,OAAO;EACjB,SAAS,GAAG;GACR,sBAAsB,GAAG,KAAA,GAAW,SAAS,OAAO,KAAK;EAC7D;CACJ;AACJ;;;;;;;;AASA,IAAM,SAA8D,UAA0C;CAC1G,MAAM,EAAE,SAAS,SAAS,UAAU,oBAAoB,SAAS;CACjE,MAAM,EAAE,YAAY,WAAW;CAC/B,MAAM,EAAE,QAAQ,UAAU,sBAAsB,aAAa;CAC7D,MAAM,WAAW,OAAO,YAAY;CACpC,MAAM,WAA4B;EAAE,GAAG;EAA0B,GAAG,MAAM;CAAgB;CAE1F,MAAM,eAAe,cACX,kBAAkB,YAAY,SAAS,QAAQ,QAAQ,GAC7D;EAAC;EAAY;EAAS;EAAQ;CAAQ,CAC1C;CAEA,MAAM,gBAAoC;EACtC,UAAU;EACV,OAAO,oBAAC,KAAD;GAAK,IAAI;GAAI,UAAA,SAAS,MAAM;EAAa,CAAA;EAChD,WAAW;EACX,SAAQ,SACJ,oBAAC,YAAD;GACI,GAAI;GACJ,SAAS,QAAQ,OAAO,MAAM,iBAAiB;GAC/C,SAAS,KAAK,KAAK,GAAG,SAAS,GAAG,kBAAkB,KAAK,GAAG,KAAK,OAAO,KAAA;GACxE,QAAQ,sBAAsB,KAAK,WAAW,mBAAmB,KAAK,EAAE,IAAI,KAAA;GAC5E,gBAAgB,aAAa,IAAI;EACpC,CAAA;CAET;CACA,MAAM,mBAAmB,SAAS,iBAAiB,CAAC,GAAG,SAAS,aAAa,IAAI;CAEjF,IAAI,KAAK,OACL,OAAO,oBAAC,YAAD;EAAY,OAAO,SAAS,OAAO;EAAW,SAAS,OAAO,KAAK,KAAK;CAAI,CAAA;CAGvF,OACI,oBAAC,eAAD;EACI,SAAS,KAAK;EACd,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,SAAS;EACT,SAAS,KAAK;EAEd,kBAAA;EACA,uBAAuB;EACvB,aAAa,EAAE,aAAa,SAAS,QAAQ,OAAO,QAAQ,iBAAiB,CAAC;CACjF,CAAA;AAET"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TableState } from '../ViewTable/TableState';
|
|
2
|
+
/** Function that sets the records of a table, as the one of `useState`. */
|
|
3
|
+
export type SetRecords<M> = (value: ((prevState: M[]) => M[]) | M[]) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Records of a {@link Table} and their pagination and sort, given by `useClientPagination` (sorted and paginated in the
|
|
6
|
+
* browser) or `useServerPagination` (sorted and paginated by the backend).
|
|
7
|
+
* @template M type of the records
|
|
8
|
+
*/
|
|
9
|
+
export type TableData<M> = {
|
|
10
|
+
/** Records shown, already sorted and cut to the current page. */
|
|
11
|
+
records: M[];
|
|
12
|
+
/** Changes the records of the table without fetching them (e.g. to remove one that was not saved yet). */
|
|
13
|
+
setRecords: SetRecords<M>;
|
|
14
|
+
/** Fetches the records again (e.g. after a deletion). */
|
|
15
|
+
reload: () => Promise<void>;
|
|
16
|
+
/** Whether the records are being fetched. */
|
|
17
|
+
loading: boolean;
|
|
18
|
+
/** Error of the last fetch; `null` if there is none. */
|
|
19
|
+
error: unknown;
|
|
20
|
+
/** Pagination and sort of the table. */
|
|
21
|
+
tableState: TableState<M>;
|
|
22
|
+
/** Whether the pagination controls are shown. */
|
|
23
|
+
paginated: boolean;
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TableData } from './TableData';
|
|
2
|
+
import { ListDataSource } from '../../../data/ListDataSource';
|
|
3
|
+
import { Identifiable } from '../../../model/Identifiable';
|
|
4
|
+
/** Options of {@link useClientPagination}. */
|
|
5
|
+
export type ClientPaginationOptions = {
|
|
6
|
+
/** Whether the table is paginated; defaults to true. Without it, every record is shown. */
|
|
7
|
+
pagination?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/** Fetches all records of a table. */
|
|
10
|
+
export type FetchAll<M> = () => Promise<M[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Fetches all records of a {@link Table} at once (e.g. from an API), which the browser then sorts (following the locale)
|
|
13
|
+
* and paginates.
|
|
14
|
+
* @template M type of the records
|
|
15
|
+
* @param source an object whose `findAll` fetches all records (e.g. a service), or a function that fetches them (e.g.
|
|
16
|
+
* with a filter). It must be stable (e.g. a module constant, or a function created with `useCallback`), since a
|
|
17
|
+
* new object or function fetches again: a `new Service()` inside the component would fetch on every render
|
|
18
|
+
* @param options whether the table is paginated
|
|
19
|
+
* @return the data to give to the `data` prop of the {@link Table}
|
|
20
|
+
*/
|
|
21
|
+
export declare function useClientPagination<M extends Identifiable>(source: Pick<ListDataSource<M>, 'findAll'> | FetchAll<M>, { pagination }?: ClientPaginationOptions): TableData<M>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useClientTableState } from "../ViewTable/useClientTableState.js";
|
|
3
|
+
import useService from "../../../hook/useService.js";
|
|
4
|
+
import { useKitConfig } from "../../../provider/useKit.js";
|
|
5
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
6
|
+
// src/component/organism/Table/useClientPagination.ts
|
|
7
|
+
/** Initial records of the table, while they are fetched; a single constant, so it is stable across renders. */
|
|
8
|
+
var NO_RECORDS = [];
|
|
9
|
+
/**
|
|
10
|
+
* Fetches all records of a {@link Table} at once (e.g. from an API), which the browser then sorts (following the locale)
|
|
11
|
+
* and paginates.
|
|
12
|
+
* @template M type of the records
|
|
13
|
+
* @param source an object whose `findAll` fetches all records (e.g. a service), or a function that fetches them (e.g.
|
|
14
|
+
* with a filter). It must be stable (e.g. a module constant, or a function created with `useCallback`), since a
|
|
15
|
+
* new object or function fetches again: a `new Service()` inside the component would fetch on every render
|
|
16
|
+
* @param options whether the table is paginated
|
|
17
|
+
* @return the data to give to the `data` prop of the {@link Table}
|
|
18
|
+
*/
|
|
19
|
+
function useClientPagination(source, { pagination = true } = {}) {
|
|
20
|
+
const fetchAll = useMemo(() => typeof source === "function" ? source : () => source.findAll(), [source]);
|
|
21
|
+
const { locale } = useKitConfig();
|
|
22
|
+
const { data: fetchedData, loading, error } = useService(fetchAll, NO_RECORDS);
|
|
23
|
+
const [data, setData] = useState(NO_RECORDS);
|
|
24
|
+
useEffect(() => setData(fetchedData), [fetchedData]);
|
|
25
|
+
const reload = useCallback(async () => setData(await fetchAll()), [fetchAll]);
|
|
26
|
+
const { records, ...tableState } = useClientTableState({
|
|
27
|
+
data,
|
|
28
|
+
pagination,
|
|
29
|
+
locale
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
records,
|
|
33
|
+
setRecords: setData,
|
|
34
|
+
reload,
|
|
35
|
+
loading,
|
|
36
|
+
error,
|
|
37
|
+
tableState,
|
|
38
|
+
paginated: pagination
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export { useClientPagination };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=useClientPagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useClientPagination.js","names":[],"sources":["../../../../src/component/organism/Table/useClientPagination.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { TableData } from './TableData';\nimport { useClientTableState } from '../ViewTable/useClientTableState';\nimport { ListDataSource } from '../../../data/ListDataSource';\nimport useService from '../../../hook/useService';\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useKitConfig } from '../../../provider/useKit';\n\n/** Initial records of the table, while they are fetched; a single constant, so it is stable across renders. */\nconst NO_RECORDS: never[] = [];\n\n/** Options of {@link useClientPagination}. */\nexport type ClientPaginationOptions = {\n /** Whether the table is paginated; defaults to true. Without it, every record is shown. */\n pagination?: boolean;\n};\n\n/** Fetches all records of a table. */\nexport type FetchAll<M> = () => Promise<M[]>;\n\n/**\n * Fetches all records of a {@link Table} at once (e.g. from an API), which the browser then sorts (following the locale)\n * and paginates.\n * @template M type of the records\n * @param source an object whose `findAll` fetches all records (e.g. a service), or a function that fetches them (e.g.\n * with a filter). It must be stable (e.g. a module constant, or a function created with `useCallback`), since a\n * new object or function fetches again: a `new Service()` inside the component would fetch on every render\n * @param options whether the table is paginated\n * @return the data to give to the `data` prop of the {@link Table}\n */\nexport function useClientPagination<M extends Identifiable>(\n source: Pick<ListDataSource<M>, 'findAll'> | FetchAll<M>,\n { pagination = true }: ClientPaginationOptions = {}\n): TableData<M> {\n // Calls the method on the object, so it keeps its `this`.\n const fetchAll = useMemo(() => (typeof source === 'function' ? source : () => source.findAll()), [source]);\n const { locale } = useKitConfig();\n const { data: fetchedData, loading, error } = useService<M[]>(fetchAll, NO_RECORDS);\n const [data, setData] = useState<M[]>(NO_RECORDS);\n\n useEffect(() => setData(fetchedData), [fetchedData]);\n\n const reload = useCallback(async () => setData(await fetchAll()), [fetchAll]);\n const { records, ...tableState } = useClientTableState({ data, pagination, locale });\n\n return { records, setRecords: setData, reload, loading, error, tableState, paginated: pagination };\n}\n"],"mappings":";;;;;;;AAUA,IAAM,aAAsB,CAAC;;;;;;;;;;;AAqB7B,SAAgB,oBACZ,QACA,EAAE,aAAa,SAAkC,CAAC,GACtC;CAEZ,MAAM,WAAW,cAAe,OAAO,WAAW,aAAa,eAAe,OAAO,QAAQ,GAAI,CAAC,MAAM,CAAC;CACzG,MAAM,EAAE,WAAW,aAAa;CAChC,MAAM,EAAE,MAAM,aAAa,SAAS,UAAU,WAAgB,UAAU,UAAU;CAClF,MAAM,CAAC,MAAM,WAAW,SAAc,UAAU;CAEhD,gBAAgB,QAAQ,WAAW,GAAG,CAAC,WAAW,CAAC;CAEnD,MAAM,SAAS,YAAY,YAAY,QAAQ,MAAM,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;CAC5E,MAAM,EAAE,SAAS,GAAG,eAAe,oBAAoB;EAAE;EAAM;EAAY;CAAO,CAAC;CAEnF,OAAO;EAAE;EAAS,YAAY;EAAS;EAAQ;EAAS;EAAO;EAAY,WAAW;CAAW;AACrG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TableData } from './TableData';
|
|
2
|
+
import { PageDataSource } from '../../../data/PageDataSource';
|
|
3
|
+
import { FetchPage } from '../../../data/PageRequest';
|
|
4
|
+
/** Options of {@link useServerPagination}. */
|
|
5
|
+
export type ServerPaginationOptions = {
|
|
6
|
+
/**
|
|
7
|
+
* Number of the first page in the requests to the backend (e.g. 0 for Spring Data); defaults to 1. It only changes
|
|
8
|
+
* the `page` sent to the backend: the table always shows the pages starting at 1.
|
|
9
|
+
*/
|
|
10
|
+
firstPage?: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Fetches one page at a time of a {@link Table}, already sorted and paginated by the backend. It goes back to page 1
|
|
14
|
+
* when the page size, the sort or the filter changes, and to the last existing page when the current one no longer
|
|
15
|
+
* exists (e.g. its last record was deleted).
|
|
16
|
+
* @template M type of the records
|
|
17
|
+
* @param source an object whose `findPage` fetches a page (e.g. a service), or a function that fetches it (e.g. with a
|
|
18
|
+
* filter). A new object or function means that the filter changed, so it goes back to page 1 and fetches again.
|
|
19
|
+
* Keep it stable otherwise (e.g. a module constant, or a function created with `useCallback`): a
|
|
20
|
+
* `new Service()` inside the component would fetch on every render
|
|
21
|
+
* @param options number of the first page for the backend
|
|
22
|
+
* @return the data to give to the `data` prop of the {@link Table}
|
|
23
|
+
*/
|
|
24
|
+
export declare function useServerPagination<M>(source: PageDataSource<M> | FetchPage<M>, { firstPage }?: ServerPaginationOptions): TableData<M>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { DEFAULT_SORT_STATUS, PAGE_SIZES } from "../ViewTable/TableState.js";
|
|
3
|
+
import useService from "../../../hook/useService.js";
|
|
4
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
5
|
+
// src/component/organism/Table/useServerPagination.ts
|
|
6
|
+
/** Result before the first response; a single constant, so it is stable across renders. */
|
|
7
|
+
var NO_PAGE = {
|
|
8
|
+
records: [],
|
|
9
|
+
totalRecords: 0
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Builds what is sent to the backend. The table state keeps the `columnAccessor`, as the header needs it to show the
|
|
13
|
+
* sort indicator, but the backend must receive the `sortKey`, when the column has one.
|
|
14
|
+
* @param request page (starting at {@link FIRST_PAGE}), size and sort chosen in the table
|
|
15
|
+
* @param firstPage number of the first page for the backend
|
|
16
|
+
*/
|
|
17
|
+
function toBackendRequest(request, firstPage) {
|
|
18
|
+
const { page, sort } = request;
|
|
19
|
+
return {
|
|
20
|
+
...request,
|
|
21
|
+
page: page - 1 + firstPage,
|
|
22
|
+
sort: {
|
|
23
|
+
...sort,
|
|
24
|
+
columnAccessor: sort.sortKey ?? sort.columnAccessor
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fetches one page at a time of a {@link Table}, already sorted and paginated by the backend. It goes back to page 1
|
|
30
|
+
* when the page size, the sort or the filter changes, and to the last existing page when the current one no longer
|
|
31
|
+
* exists (e.g. its last record was deleted).
|
|
32
|
+
* @template M type of the records
|
|
33
|
+
* @param source an object whose `findPage` fetches a page (e.g. a service), or a function that fetches it (e.g. with a
|
|
34
|
+
* filter). A new object or function means that the filter changed, so it goes back to page 1 and fetches again.
|
|
35
|
+
* Keep it stable otherwise (e.g. a module constant, or a function created with `useCallback`): a
|
|
36
|
+
* `new Service()` inside the component would fetch on every render
|
|
37
|
+
* @param options number of the first page for the backend
|
|
38
|
+
* @return the data to give to the `data` prop of the {@link Table}
|
|
39
|
+
*/
|
|
40
|
+
function useServerPagination(source, { firstPage = 1 } = {}) {
|
|
41
|
+
const fetchPage = useMemo(() => typeof source === "function" ? source : (request) => source.findPage(request), [source]);
|
|
42
|
+
const [request, setRequest] = useState({
|
|
43
|
+
page: 1,
|
|
44
|
+
size: PAGE_SIZES[0],
|
|
45
|
+
sort: DEFAULT_SORT_STATUS
|
|
46
|
+
});
|
|
47
|
+
const [previousFetchPage, setPreviousFetchPage] = useState(() => fetchPage);
|
|
48
|
+
if (fetchPage !== previousFetchPage) {
|
|
49
|
+
setPreviousFetchPage(() => fetchPage);
|
|
50
|
+
setRequest((current) => ({
|
|
51
|
+
...current,
|
|
52
|
+
page: 1
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
const fetchCurrentPage = useCallback(() => fetchPage(toBackendRequest(request, firstPage)), [
|
|
56
|
+
fetchPage,
|
|
57
|
+
request,
|
|
58
|
+
firstPage
|
|
59
|
+
]);
|
|
60
|
+
const { data, loading, error } = useService(fetchCurrentPage, NO_PAGE);
|
|
61
|
+
const [records, setRecords] = useState([]);
|
|
62
|
+
useEffect(() => setRecords(data.records), [data]);
|
|
63
|
+
const lastPage = Math.max(Math.ceil(data.totalRecords / request.size), 1);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (!loading && request.page > lastPage) setRequest((current) => ({
|
|
66
|
+
...current,
|
|
67
|
+
page: lastPage
|
|
68
|
+
}));
|
|
69
|
+
}, [
|
|
70
|
+
loading,
|
|
71
|
+
request.page,
|
|
72
|
+
lastPage
|
|
73
|
+
]);
|
|
74
|
+
return {
|
|
75
|
+
records,
|
|
76
|
+
setRecords,
|
|
77
|
+
reload: useCallback(async () => setRequest((current) => ({ ...current })), []),
|
|
78
|
+
loading,
|
|
79
|
+
error,
|
|
80
|
+
tableState: {
|
|
81
|
+
page: request.page,
|
|
82
|
+
pageSize: request.size,
|
|
83
|
+
sortStatus: request.sort,
|
|
84
|
+
totalRecords: data.totalRecords,
|
|
85
|
+
onPageChange: (page) => setRequest((current) => ({
|
|
86
|
+
...current,
|
|
87
|
+
page
|
|
88
|
+
})),
|
|
89
|
+
onPageSizeChange: (size) => setRequest((current) => ({
|
|
90
|
+
...current,
|
|
91
|
+
size,
|
|
92
|
+
page: 1
|
|
93
|
+
})),
|
|
94
|
+
onSortStatusChange: (sort) => setRequest((current) => ({
|
|
95
|
+
...current,
|
|
96
|
+
sort,
|
|
97
|
+
page: 1
|
|
98
|
+
}))
|
|
99
|
+
},
|
|
100
|
+
paginated: true
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export { useServerPagination };
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=useServerPagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useServerPagination.js","names":[],"sources":["../../../../src/component/organism/Table/useServerPagination.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { TableData } from './TableData';\nimport { DEFAULT_SORT_STATUS, FIRST_PAGE, PAGE_SIZES } from '../ViewTable/TableState';\nimport { PageDataSource } from '../../../data/PageDataSource';\nimport { FetchPage, PageRequest, PageResult } from '../../../data/PageRequest';\nimport useService from '../../../hook/useService';\n\n/** Result before the first response; a single constant, so it is stable across renders. */\nconst NO_PAGE: PageResult<never> = { records: [], totalRecords: 0 };\n\n/** Options of {@link useServerPagination}. */\nexport type ServerPaginationOptions = {\n /**\n * Number of the first page in the requests to the backend (e.g. 0 for Spring Data); defaults to 1. It only changes\n * the `page` sent to the backend: the table always shows the pages starting at 1.\n */\n firstPage?: number;\n};\n\n/**\n * Builds what is sent to the backend. The table state keeps the `columnAccessor`, as the header needs it to show the\n * sort indicator, but the backend must receive the `sortKey`, when the column has one.\n * @param request page (starting at {@link FIRST_PAGE}), size and sort chosen in the table\n * @param firstPage number of the first page for the backend\n */\nfunction toBackendRequest<M>(request: PageRequest<M>, firstPage: number): PageRequest<M> {\n const { page, sort } = request;\n\n return {\n ...request,\n page: page - FIRST_PAGE + firstPage,\n sort: { ...sort, columnAccessor: sort.sortKey ?? sort.columnAccessor }\n };\n}\n\n/**\n * Fetches one page at a time of a {@link Table}, already sorted and paginated by the backend. It goes back to page 1\n * when the page size, the sort or the filter changes, and to the last existing page when the current one no longer\n * exists (e.g. its last record was deleted).\n * @template M type of the records\n * @param source an object whose `findPage` fetches a page (e.g. a service), or a function that fetches it (e.g. with a\n * filter). A new object or function means that the filter changed, so it goes back to page 1 and fetches again.\n * Keep it stable otherwise (e.g. a module constant, or a function created with `useCallback`): a\n * `new Service()` inside the component would fetch on every render\n * @param options number of the first page for the backend\n * @return the data to give to the `data` prop of the {@link Table}\n */\nexport function useServerPagination<M>(\n source: PageDataSource<M> | FetchPage<M>,\n { firstPage = FIRST_PAGE }: ServerPaginationOptions = {}\n): TableData<M> {\n // Calls the method on the object, so it keeps its `this`.\n const fetchPage = useMemo<FetchPage<M>>(\n () => (typeof source === 'function' ? source : request => source.findPage(request)),\n [source]\n );\n // A single object, so a copy of it fetches the same page again, and a change is always a single fetch.\n const [request, setRequest] = useState<PageRequest<M>>({\n page: FIRST_PAGE,\n size: PAGE_SIZES[0],\n sort: DEFAULT_SORT_STATUS\n });\n const [previousFetchPage, setPreviousFetchPage] = useState(() => fetchPage);\n\n /* Done while rendering, as an effect would first fetch the new filter with the old page.\n See https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes */\n if (fetchPage !== previousFetchPage) {\n setPreviousFetchPage(() => fetchPage);\n setRequest(current => ({ ...current, page: FIRST_PAGE }));\n }\n\n const fetchCurrentPage = useCallback(\n () => fetchPage(toBackendRequest(request, firstPage)),\n [fetchPage, request, firstPage]\n );\n const { data, loading, error } = useService<PageResult<M>>(fetchCurrentPage, NO_PAGE);\n const [records, setRecords] = useState<M[]>([]);\n\n useEffect(() => setRecords(data.records), [data]);\n\n const lastPage = Math.max(Math.ceil(data.totalRecords / request.size), FIRST_PAGE);\n\n // The current page may not exist anymore (e.g. its last record was deleted).\n useEffect(() => {\n if (!loading && request.page > lastPage) setRequest(current => ({ ...current, page: lastPage }));\n }, [loading, request.page, lastPage]);\n\n const reload = useCallback(async () => setRequest(current => ({ ...current })), []);\n\n return {\n records,\n setRecords,\n reload,\n loading,\n error,\n tableState: {\n page: request.page,\n pageSize: request.size,\n sortStatus: request.sort,\n totalRecords: data.totalRecords,\n onPageChange: page => setRequest(current => ({ ...current, page })),\n onPageSizeChange: size => setRequest(current => ({ ...current, size, page: FIRST_PAGE })),\n onSortStatusChange: sort => setRequest(current => ({ ...current, sort, page: FIRST_PAGE }))\n },\n paginated: true\n };\n}\n"],"mappings":";;;;;;AASA,IAAM,UAA6B;CAAE,SAAS,CAAC;CAAG,cAAc;AAAE;;;;;;;AAiBlE,SAAS,iBAAoB,SAAyB,WAAmC;CACrF,MAAM,EAAE,MAAM,SAAS;CAEvB,OAAO;EACH,GAAG;EACH,MAAM,OAAA,IAAoB;EAC1B,MAAM;GAAE,GAAG;GAAM,gBAAgB,KAAK,WAAW,KAAK;EAAe;CACzE;AACJ;;;;;;;;;;;;;AAcA,SAAgB,oBACZ,QACA,EAAE,YAAA,MAAoD,CAAC,GAC3C;CAEZ,MAAM,YAAY,cACP,OAAO,WAAW,aAAa,UAAS,YAAW,OAAO,SAAS,OAAO,GACjF,CAAC,MAAM,CACX;CAEA,MAAM,CAAC,SAAS,cAAc,SAAyB;EACnD,MAAA;EACA,MAAM,WAAW;EACjB,MAAM;CACV,CAAC;CACD,MAAM,CAAC,mBAAmB,wBAAwB,eAAe,SAAS;CAI1E,IAAI,cAAc,mBAAmB;EACjC,2BAA2B,SAAS;EACpC,YAAW,aAAY;GAAE,GAAG;GAAS,MAAA;EAAiB,EAAE;CAC5D;CAEA,MAAM,mBAAmB,kBACf,UAAU,iBAAiB,SAAS,SAAS,CAAC,GACpD;EAAC;EAAW;EAAS;CAAS,CAClC;CACA,MAAM,EAAE,MAAM,SAAS,UAAU,WAA0B,kBAAkB,OAAO;CACpF,MAAM,CAAC,SAAS,cAAc,SAAc,CAAC,CAAC;CAE9C,gBAAgB,WAAW,KAAK,OAAO,GAAG,CAAC,IAAI,CAAC;CAEhD,MAAM,WAAW,KAAK,IAAI,KAAK,KAAK,KAAK,eAAe,QAAQ,IAAI,GAAA,CAAa;CAGjF,gBAAgB;EACZ,IAAI,CAAC,WAAW,QAAQ,OAAO,UAAU,YAAW,aAAY;GAAE,GAAG;GAAS,MAAM;EAAS,EAAE;CACnG,GAAG;EAAC;EAAS,QAAQ;EAAM;CAAQ,CAAC;CAIpC,OAAO;EACH;EACA;EACA,QALW,YAAY,YAAY,YAAW,aAAY,EAAE,GAAG,QAAQ,EAAE,GAAG,CAAC,CAK7E;EACA;EACA;EACA,YAAY;GACR,MAAM,QAAQ;GACd,UAAU,QAAQ;GAClB,YAAY,QAAQ;GACpB,cAAc,KAAK;GACnB,eAAc,SAAQ,YAAW,aAAY;IAAE,GAAG;IAAS;GAAK,EAAE;GAClE,mBAAkB,SAAQ,YAAW,aAAY;IAAE,GAAG;IAAS;IAAM,MAAA;GAAiB,EAAE;GACxF,qBAAoB,SAAQ,YAAW,aAAY;IAAE,GAAG;IAAS;IAAM,MAAA;GAAiB,EAAE;EAC9F;EACA,WAAW;CACf;AACJ"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DataTableColumn, DataTablePaginationProps, DataTableProps } from 'mantine-datatable';
|
|
2
|
+
import { Identifiable } from '../../../model/Identifiable';
|
|
3
|
+
import { TableState } from './TableState';
|
|
4
|
+
/** Function that defines extra attributes (e.g. aria-label) for each pagination control button. */
|
|
5
|
+
type PaginationControlPropsGetter = Extract<DataTablePaginationProps, {
|
|
6
|
+
page: number;
|
|
7
|
+
}>['getPaginationControlProps'];
|
|
8
|
+
/**
|
|
9
|
+
* Props of the {@link DataTable} passed straight through.
|
|
10
|
+
* Only the props actually used by the callers are accepted: using `Partial<DataTableProps<M>>` makes TypeScript combine
|
|
11
|
+
* the variants of the `DataTableProps` union with the ones of `paginationProps`, which exhausts the memory of `tsc`.
|
|
12
|
+
*/
|
|
13
|
+
type DataTablePassThroughProps<M extends Identifiable> = Pick<DataTableProps<M>, 'onRowClick' | 'rowStyle' | 'noRecordsText' | 'highlightOnHover' | 'highlightOnHoverColor' | 'className' | 'minHeight' | 'withColumnBorders' | 'verticalSpacing' | 'horizontalSpacing'>;
|
|
14
|
+
/** Props of the {@link DataTableView} that do not depend on where the table is sorted and paginated. */
|
|
15
|
+
export type DataTableViewBaseProps<M extends Identifiable> = {
|
|
16
|
+
columns: DataTableColumn<M>[];
|
|
17
|
+
/** Whether the records are being loaded. */
|
|
18
|
+
loading?: boolean;
|
|
19
|
+
/** Shows the ID column, automatically added to the given columns. */
|
|
20
|
+
showIdColumn?: boolean;
|
|
21
|
+
/** Extra attributes for the pagination control buttons, when the table is paginated. */
|
|
22
|
+
getPaginationControlProps?: PaginationControlPropsGetter;
|
|
23
|
+
} & DataTablePassThroughProps<M>;
|
|
24
|
+
/** Props of the {@link DataTableView}. */
|
|
25
|
+
type DataTableViewProps<M extends Identifiable> = DataTableViewBaseProps<M> & {
|
|
26
|
+
/** Records shown, already sorted and cut to the current page. */
|
|
27
|
+
records: M[];
|
|
28
|
+
/** Pagination and sort of the table. */
|
|
29
|
+
state: TableState<M>;
|
|
30
|
+
/** Whether the pagination controls are shown. */
|
|
31
|
+
paginated: boolean;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Shows records as received, with the given pagination and sort (e.g. kept by a backend that paginates). It is the
|
|
35
|
+
* visual part of the tables, on top of the mantine-datatable `DataTable`, with the texts of the locale.
|
|
36
|
+
* @template M type of the records, shown one per row
|
|
37
|
+
*/
|
|
38
|
+
declare const DataTableView: <M extends Identifiable>({ records, state, paginated, columns, loading, showIdColumn, getPaginationControlProps, ...rest }: DataTableViewProps<M>) => import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
export default DataTableView;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useKitConfig } from "../../../provider/useKit.js";
|
|
3
|
+
import { PAGE_SIZES } from "./TableState.js";
|
|
4
|
+
import { useRowKey } from "./useRowKey.js";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
import { DataTable } from "mantine-datatable";
|
|
7
|
+
// src/component/organism/ViewTable/DataTableView.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Shows records as received, with the given pagination and sort (e.g. kept by a backend that paginates). It is the
|
|
10
|
+
* visual part of the tables, on top of the mantine-datatable `DataTable`, with the texts of the locale.
|
|
11
|
+
* @template M type of the records, shown one per row
|
|
12
|
+
*/
|
|
13
|
+
var DataTableView = ({ records, state, paginated, columns, loading, showIdColumn = true, getPaginationControlProps, ...rest }) => {
|
|
14
|
+
const { messages } = useKitConfig();
|
|
15
|
+
const rowKey = useRowKey();
|
|
16
|
+
const effectiveColumns = [{
|
|
17
|
+
title: "ID",
|
|
18
|
+
accessor: "id",
|
|
19
|
+
hidden: !showIdColumn,
|
|
20
|
+
sortable: true,
|
|
21
|
+
render: (item) => /* @__PURE__ */ jsx("span", { children: item.id })
|
|
22
|
+
}, ...columns];
|
|
23
|
+
const paginationProps = paginated ? {
|
|
24
|
+
page: state.page,
|
|
25
|
+
onPageChange: state.onPageChange,
|
|
26
|
+
totalRecords: state.totalRecords,
|
|
27
|
+
recordsPerPage: state.pageSize,
|
|
28
|
+
recordsPerPageOptions: PAGE_SIZES,
|
|
29
|
+
onRecordsPerPageChange: state.onPageSizeChange,
|
|
30
|
+
recordsPerPageLabel: messages.table.recordsPerPage,
|
|
31
|
+
paginationText: messages.table.pagination,
|
|
32
|
+
getPaginationControlProps
|
|
33
|
+
} : {};
|
|
34
|
+
return /* @__PURE__ */ jsx(DataTable, {
|
|
35
|
+
minHeight: 200,
|
|
36
|
+
verticalSpacing: "sm",
|
|
37
|
+
striped: true,
|
|
38
|
+
columns: effectiveColumns,
|
|
39
|
+
records,
|
|
40
|
+
idAccessor: rowKey,
|
|
41
|
+
noRecordsText: messages.table.noRecords,
|
|
42
|
+
fetching: loading,
|
|
43
|
+
sortStatus: state.sortStatus,
|
|
44
|
+
onSortStatusChange: state.onSortStatusChange,
|
|
45
|
+
customRowAttributes: (record) => ({ "data-testid": `row-${record.id}` }),
|
|
46
|
+
...paginationProps,
|
|
47
|
+
...rest
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
export { DataTableView as default };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=DataTableView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataTableView.js","names":[],"sources":["../../../../src/component/organism/ViewTable/DataTableView.tsx"],"sourcesContent":["'use client';\n\nimport { DataTable, DataTableColumn, DataTablePaginationProps, DataTableProps } from 'mantine-datatable';\n\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useKitConfig } from '../../../provider/useKit';\nimport { PAGE_SIZES, TableState } from './TableState';\nimport { useRowKey } from './useRowKey';\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.\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/** Props of the {@link DataTableView} that do not depend on where the table is sorted and paginated. */\nexport type DataTableViewBaseProps<M extends Identifiable> = {\n columns: DataTableColumn<M>[];\n\n /** Whether the records are being loaded. */\n loading?: boolean;\n\n /** Shows the ID column, automatically added to the given columns. */\n showIdColumn?: boolean;\n\n /** Extra attributes for the pagination control buttons, when the table is paginated. */\n getPaginationControlProps?: PaginationControlPropsGetter;\n} & DataTablePassThroughProps<M>;\n\n/** Props of the {@link DataTableView}. */\ntype DataTableViewProps<M extends Identifiable> = DataTableViewBaseProps<M> & {\n /** Records shown, already sorted and cut to the current page. */\n records: M[];\n\n /** Pagination and sort of the table. */\n state: TableState<M>;\n\n /** Whether the pagination controls are shown. */\n paginated: boolean;\n};\n\n/**\n * Shows records as received, with the given pagination and sort (e.g. kept by a backend that paginates). It is the\n * visual part of the tables, on top of the mantine-datatable `DataTable`, with the texts of the locale.\n * @template M type of the records, shown one per row\n */\nconst DataTableView = <M extends Identifiable>({\n records,\n state,\n paginated,\n columns,\n loading,\n showIdColumn = true,\n getPaginationControlProps,\n ...rest\n}: DataTableViewProps<M>) => {\n const { messages } = useKitConfig();\n const rowKey = useRowKey<M>();\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 /* 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 = paginated\n ? {\n page: state.page,\n onPageChange: state.onPageChange,\n totalRecords: state.totalRecords,\n recordsPerPage: state.pageSize,\n recordsPerPageOptions: PAGE_SIZES,\n onRecordsPerPageChange: state.onPageSizeChange,\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={state.sortStatus}\n onSortStatusChange={state.onSortStatusChange}\n customRowAttributes={record => ({ 'data-testid': `row-${record.id}` })}\n {...paginationProps}\n {...rest}\n />\n );\n};\n\nexport default DataTableView;\n"],"mappings":";;;;;;;;;;;;AA8DA,IAAM,iBAAyC,EAC3C,SACA,OACA,WACA,SACA,SACA,eAAe,MACf,2BACA,GAAG,WACsB;CACzB,MAAM,EAAE,aAAa,aAAa;CAClC,MAAM,SAAS,UAAa;CAE5B,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;CAIA,MAAM,kBAA4C,YAC5C;EACI,MAAM,MAAM;EACZ,cAAc,MAAM;EACpB,cAAc,MAAM;EACpB,gBAAgB,MAAM;EACtB,uBAAuB;EACvB,wBAAwB,MAAM;EAC9B,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;EACV,YAAY,MAAM;EAClB,oBAAoB,MAAM;EAC1B,sBAAqB,YAAW,EAAE,eAAe,OAAO,OAAO,KAAK;EACpE,GAAI;EACJ,GAAI;CACP,CAAA;AAET"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DataTableSortStatus } from 'mantine-datatable';
|
|
2
|
+
/** Options of records per page. The largest one lets long tables (and end-to-end tests) show every row in one page. */
|
|
3
|
+
export declare const PAGE_SIZES: number[];
|
|
4
|
+
/** Number of the first page of a table. */
|
|
5
|
+
export declare const FIRST_PAGE = 1;
|
|
6
|
+
/** Default sort when neither `defaultSortStatus` nor `compare` is given. */
|
|
7
|
+
export declare const DEFAULT_SORT_STATUS: {
|
|
8
|
+
readonly columnAccessor: 'id';
|
|
9
|
+
readonly direction: 'asc';
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Pagination and sort of a table, with the callbacks that change them.
|
|
13
|
+
* @template M type of the records
|
|
14
|
+
*/
|
|
15
|
+
export type TableState<M> = {
|
|
16
|
+
/** Current page, starting at 1. */
|
|
17
|
+
page: number;
|
|
18
|
+
/** Records per page. */
|
|
19
|
+
pageSize: number;
|
|
20
|
+
/** Current sort. */
|
|
21
|
+
sortStatus: DataTableSortStatus<M>;
|
|
22
|
+
/** Total of records of all pages. */
|
|
23
|
+
totalRecords: number;
|
|
24
|
+
/** Called when the user chooses a page. */
|
|
25
|
+
onPageChange: (page: number) => void;
|
|
26
|
+
/** Called when the user chooses the records per page. */
|
|
27
|
+
onPageSizeChange: (pageSize: number) => void;
|
|
28
|
+
/** Called when the user chooses the sort. */
|
|
29
|
+
onSortStatusChange: (sortStatus: DataTableSortStatus<M>) => void;
|
|
30
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/component/organism/ViewTable/TableState.ts
|
|
3
|
+
/** Options of records per page. The largest one lets long tables (and end-to-end tests) show every row in one page. */
|
|
4
|
+
var PAGE_SIZES = [
|
|
5
|
+
10,
|
|
6
|
+
20,
|
|
7
|
+
50,
|
|
8
|
+
200
|
|
9
|
+
];
|
|
10
|
+
/** Number of the first page of a table. */
|
|
11
|
+
var FIRST_PAGE = 1;
|
|
12
|
+
/** Default sort when neither `defaultSortStatus` nor `compare` is given. */
|
|
13
|
+
var DEFAULT_SORT_STATUS = {
|
|
14
|
+
columnAccessor: "id",
|
|
15
|
+
direction: "asc"
|
|
16
|
+
};
|
|
17
|
+
export { DEFAULT_SORT_STATUS, FIRST_PAGE, PAGE_SIZES };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=TableState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TableState.js","names":[],"sources":["../../../../src/component/organism/ViewTable/TableState.ts"],"sourcesContent":["import type { DataTableSortStatus } from 'mantine-datatable';\n\n/*\nPagination and sort state of a table, and its initial values, shared by the state kept in the browser and the one kept\nfor a backend that paginates.\n*/\n\n/** Options of records per page. The largest one lets long tables (and end-to-end tests) show every row in one page. */\nexport const PAGE_SIZES = [10, 20, 50, 200];\n\n/** Number of the first page of a table. */\nexport const FIRST_PAGE = 1;\n\n/** Default sort when neither `defaultSortStatus` nor `compare` is given. */\nexport const DEFAULT_SORT_STATUS = { columnAccessor: 'id', direction: 'asc' } as const;\n\n/**\n * Pagination and sort of a table, with the callbacks that change them.\n * @template M type of the records\n */\nexport type TableState<M> = {\n /** Current page, starting at 1. */\n page: number;\n\n /** Records per page. */\n pageSize: number;\n\n /** Current sort. */\n sortStatus: DataTableSortStatus<M>;\n\n /** Total of records of all pages. */\n totalRecords: number;\n\n /** Called when the user chooses a page. */\n onPageChange: (page: number) => void;\n\n /** Called when the user chooses the records per page. */\n onPageSizeChange: (pageSize: number) => void;\n\n /** Called when the user chooses the sort. */\n onSortStatusChange: (sortStatus: DataTableSortStatus<M>) => void;\n};\n"],"mappings":";;;AAQA,IAAa,aAAa;CAAC;CAAI;CAAI;CAAI;AAAG;;AAG1C,IAAa,aAAa;;AAG1B,IAAa,sBAAsB;CAAE,gBAAgB;CAAM,WAAW;AAAM"}
|
|
@@ -1,26 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DataTableSortStatus } from 'mantine-datatable';
|
|
2
2
|
import { Identifiable } from '../../../model/Identifiable';
|
|
3
3
|
import { Comparator } from './ColumnComparator';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Props of the {@link DataTable} passed straight through by the ViewTable.
|
|
10
|
-
* Only the props actually used by the callers are accepted: using `Partial<DataTableProps<M>>` makes TypeScript combine
|
|
11
|
-
* the variants of the `DataTableProps` union with the ones of `paginationProps`, which exhausts the memory of `tsc`.
|
|
12
|
-
*/
|
|
13
|
-
type DataTablePassThroughProps<M extends Identifiable> = Pick<DataTableProps<M>, 'onRowClick' | 'rowStyle' | 'noRecordsText' | 'highlightOnHover' | 'highlightOnHoverColor' | 'className' | 'minHeight' | 'withColumnBorders' | 'verticalSpacing' | 'horizontalSpacing'>;
|
|
14
|
-
/**
|
|
15
|
-
* TableProps is a generic type for the ViewTable component props.
|
|
16
|
-
*/
|
|
17
|
-
type TableProps<M extends Identifiable> = {
|
|
4
|
+
import { DataTableViewBaseProps } from './DataTableView';
|
|
5
|
+
/** Props of the {@link ViewTable}. */
|
|
6
|
+
type ViewTableProps<M extends Identifiable> = DataTableViewBaseProps<M> & {
|
|
7
|
+
/** All records of the table. */
|
|
18
8
|
data: M[];
|
|
19
|
-
|
|
20
|
-
loading?: boolean;
|
|
9
|
+
/** Whether the table is paginated; defaults to true. Without it, every record is shown (e.g. a printed report). */
|
|
21
10
|
pagination?: boolean;
|
|
22
|
-
/** Shows the ID column, automatically added to the given columns. */
|
|
23
|
-
showIdColumn?: boolean;
|
|
24
11
|
/**
|
|
25
12
|
* Initial sort of the table. Default: by `id`, ascending.
|
|
26
13
|
* Give another value when the natural order of the data should not be by `id` (e.g. when the backend already
|
|
@@ -34,20 +21,11 @@ type TableProps<M extends Identifiable> = {
|
|
|
34
21
|
* header of a column, the sort of the column applies.
|
|
35
22
|
*/
|
|
36
23
|
compare?: Comparator<M>;
|
|
37
|
-
|
|
38
|
-
getPaginationControlProps?: PaginationControlPropsGetter;
|
|
39
|
-
} & DataTablePassThroughProps<M>;
|
|
24
|
+
};
|
|
40
25
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* @
|
|
44
|
-
* @param {DataTableColumn<M>[]} columns - The columns to be displayed in this ViewTable
|
|
45
|
-
* @param loading Indicates if the data to be displayed is being loaded yet or not
|
|
46
|
-
* @param pagination Indicates whether to show pagination or not.
|
|
47
|
-
* @param getPaginationControlProps extra attributes for the pagination control buttons
|
|
48
|
-
* @param compare comparison function of the initial order, replaced by the sort of the column chosen in the header
|
|
49
|
-
* @param rest remaining props, passed to the DataTable
|
|
50
|
-
* @return the table with the data, sortable and (optionally) paginated
|
|
26
|
+
* Table that sorts (following the locale) and paginates in the browser all the given records, on top of the
|
|
27
|
+
* mantine-datatable `DataTable`.
|
|
28
|
+
* @template M type of the records, shown one per row
|
|
51
29
|
*/
|
|
52
|
-
declare const ViewTable: <M extends Identifiable>({ data,
|
|
30
|
+
declare const ViewTable: <M extends Identifiable>({ data, pagination, defaultSortStatus, compare, ...rest }: ViewTableProps<M>) => import("react/jsx-runtime").JSX.Element;
|
|
53
31
|
export default ViewTable;
|