@espresso-lab/mantine-data-table 2.1.14 → 2.2.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 +161 -4
- package/dist/DataTable/CreateModal.d.ts +1 -1
- package/dist/DataTable/DataTable.d.ts +88 -3
- package/dist/DataTable/EntityForm.d.ts +13 -0
- package/dist/DataTable/FieldCard.d.ts +11 -0
- package/dist/DataTable/MobileCardList.d.ts +2 -1
- package/dist/DataTable/SubTable.d.ts +9 -0
- package/dist/DataTable/UpdateModal.d.ts +1 -1
- package/dist/Hooks/useApi.d.ts +10 -0
- package/dist/Hooks/usePersistentState.d.ts +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +982 -913
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/utils/filter.d.ts +19 -0
- package/package.json +2 -3
- package/dist/DataTable/DataTableInner.d.ts +0 -104
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
# Mantine Data Table
|
|
1
|
+
# Mantine Data Table
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
A config-driven wrapper around [mantine-datatable](https://icflorescu.github.io/mantine-datatable/).
|
|
4
|
+
Describe a table once as a list of fields and get sorting, pagination, filtering, CRUD modals,
|
|
5
|
+
row expansion and a responsive mobile card layout — backed by [TanStack Query](https://tanstack.com/query).
|
|
5
6
|
|
|
6
7
|
[](#license)
|
|
7
8
|
[](https://www.npmjs.com/package/@espresso-lab/mantine-data-table)
|
|
@@ -11,4 +12,160 @@ with a single configuration object.
|
|
|
11
12
|
|
|
12
13
|
```bash
|
|
13
14
|
npm i @espresso-lab/mantine-data-table
|
|
14
|
-
```
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Peer dependencies you need to install: `@mantine/core`, `@mantine/dates`, `@mantine/form`,
|
|
18
|
+
`@mantine/hooks`, `@tabler/icons-react`, `react` and `react-dom`.
|
|
19
|
+
(`mantine-datatable` and `@tanstack/react-query` come bundled.)
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
Wrap your app in `DataTableProvider`. It provides the base URL, a TanStack Query client and the
|
|
24
|
+
headers sent with every request.
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import "@mantine/core/styles.css";
|
|
28
|
+
import "@mantine/dates/styles.css";
|
|
29
|
+
import "mantine-datatable/styles.css";
|
|
30
|
+
|
|
31
|
+
import { MantineProvider } from "@mantine/core";
|
|
32
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
33
|
+
import { DataTableProvider } from "@espresso-lab/mantine-data-table";
|
|
34
|
+
|
|
35
|
+
const queryClient = new QueryClient();
|
|
36
|
+
|
|
37
|
+
export function Root() {
|
|
38
|
+
return (
|
|
39
|
+
<MantineProvider>
|
|
40
|
+
<DataTableProvider
|
|
41
|
+
baseUrl="https://api.example.com"
|
|
42
|
+
queryClient={queryClient}
|
|
43
|
+
getHeaders={async () => ({ Authorization: `Bearer ${await getToken()}` })}
|
|
44
|
+
>
|
|
45
|
+
<App />
|
|
46
|
+
</DataTableProvider>
|
|
47
|
+
</MantineProvider>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
A table is described by a `queryKey`, an `apiPath` and a list of `fields`. The component fetches
|
|
55
|
+
`GET {apiPath}`, renders the rows and wires create/update/delete against the same path.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { DataTable, Field } from "@espresso-lab/mantine-data-table";
|
|
59
|
+
|
|
60
|
+
interface User {
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
email: string;
|
|
64
|
+
active: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const fields: Field<User>[] = [
|
|
68
|
+
{
|
|
69
|
+
id: "name",
|
|
70
|
+
list: true, create: true, update: true, delete: true,
|
|
71
|
+
required: true,
|
|
72
|
+
column: { accessor: "name", title: "Name", sortable: true },
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "email",
|
|
76
|
+
list: true, create: true, update: true, delete: true,
|
|
77
|
+
column: { accessor: "email", title: "Email" },
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "active",
|
|
81
|
+
list: true, create: true, update: true, delete: true,
|
|
82
|
+
type: "boolean",
|
|
83
|
+
column: { accessor: "active", title: "Active" },
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
export function Users() {
|
|
88
|
+
return (
|
|
89
|
+
<DataTable<User>
|
|
90
|
+
title="Users"
|
|
91
|
+
queryKey={["users"]}
|
|
92
|
+
apiPath="/users"
|
|
93
|
+
fields={fields}
|
|
94
|
+
selection
|
|
95
|
+
pagination
|
|
96
|
+
mobileCards
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Fields
|
|
103
|
+
|
|
104
|
+
A field describes both a table column and a form input.
|
|
105
|
+
|
|
106
|
+
| Key | Description |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `id` | Unique key; used as the form field name and column accessor fallback. |
|
|
109
|
+
| `list` / `create` / `update` / `delete` | Whether the field shows in the table, the create form, the edit form, and is editable. |
|
|
110
|
+
| `type` | `text` (default), `number`, `date`, `boolean`, `textarea` or `custom`. |
|
|
111
|
+
| `required` | `boolean` or `(values) => boolean`. |
|
|
112
|
+
| `column` | A [mantine-datatable column](https://icflorescu.github.io/mantine-datatable/) — `accessor`, `title`, `render`, `sortable`, `textAlign`, `filter`, `footer`, `hidden`. |
|
|
113
|
+
| `render` | For `type: "custom"` — render your own input. |
|
|
114
|
+
| `defaultValue`, `placeholder`, `step`, `conditional` | Optional. |
|
|
115
|
+
|
|
116
|
+
### Common props
|
|
117
|
+
|
|
118
|
+
| Prop | Description |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| `selection` | Row checkboxes with bulk actions. |
|
|
121
|
+
| `pagination` | Client-side pagination. |
|
|
122
|
+
| `mobileCards` | Render a card list instead of the table below the `sm` breakpoint. |
|
|
123
|
+
| `tabs` | Switch between datasets, each with its own query params and api path. |
|
|
124
|
+
| `actions` | Custom bulk actions on the selected rows. |
|
|
125
|
+
| `rowExpansion` | Expandable rows (see below). |
|
|
126
|
+
| `defaultSort`, `queryParams`, `onRowClick`, `canUpdate`, `canDelete` | Optional. |
|
|
127
|
+
|
|
128
|
+
## Row expansion
|
|
129
|
+
|
|
130
|
+
Set `rowExpansion` to render content under a row. A chevron is added to the first column
|
|
131
|
+
automatically; `expandable` controls which rows can open.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { SubTable } from "@espresso-lab/mantine-data-table";
|
|
135
|
+
|
|
136
|
+
<DataTable<Account>
|
|
137
|
+
title="Accounts"
|
|
138
|
+
queryKey={["accounts"]}
|
|
139
|
+
apiPath="/accounts"
|
|
140
|
+
fields={fields}
|
|
141
|
+
mobileCards
|
|
142
|
+
rowExpansion={{
|
|
143
|
+
expandable: (account) => account.entries.length > 0,
|
|
144
|
+
content: (account, isMobile) => (
|
|
145
|
+
<SubTable
|
|
146
|
+
mobile={isMobile}
|
|
147
|
+
records={account.entries}
|
|
148
|
+
idAccessor="id"
|
|
149
|
+
columns={[
|
|
150
|
+
{ accessor: "date", title: "Date", render: (e) => formatDate(e.date) },
|
|
151
|
+
{
|
|
152
|
+
accessor: "amount",
|
|
153
|
+
title: "Amount",
|
|
154
|
+
textAlign: "right",
|
|
155
|
+
render: (e) => formatAmount(e.amount),
|
|
156
|
+
footer: formatAmount(account.total),
|
|
157
|
+
},
|
|
158
|
+
]}
|
|
159
|
+
/>
|
|
160
|
+
),
|
|
161
|
+
}}
|
|
162
|
+
/>;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`SubTable` uses one set of columns for both layouts: a full mantine-datatable on desktop (sorting,
|
|
166
|
+
column filters, footer totals) and a labelled card list on mobile. Add `hideOnMobile: (record) => boolean`
|
|
167
|
+
to a column to drop low-value cells from the mobile cards.
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
|
@@ -1,3 +1,88 @@
|
|
|
1
|
-
import { BaseEntity } from '../Hooks/useApi
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { BaseEntity } from '../Hooks/useApi';
|
|
2
|
+
import { default as React } from 'react';
|
|
3
|
+
import { DataTableColumn } from 'mantine-datatable';
|
|
4
|
+
import { Filter } from '../utils/filter';
|
|
5
|
+
export type FieldType = "text" | "number" | "boolean" | "custom" | "date" | "textarea";
|
|
6
|
+
export interface Field<T> {
|
|
7
|
+
id: string;
|
|
8
|
+
defaultValue?: T[keyof T];
|
|
9
|
+
required?: boolean | ((values: Partial<T>) => boolean);
|
|
10
|
+
step?: number;
|
|
11
|
+
list: boolean;
|
|
12
|
+
create: boolean;
|
|
13
|
+
update: boolean;
|
|
14
|
+
delete: boolean;
|
|
15
|
+
type?: FieldType;
|
|
16
|
+
placeholder?: string;
|
|
17
|
+
conditional?: (values: Partial<T>) => boolean;
|
|
18
|
+
render?: (values: T, setValues: (values: Partial<T>) => void, hideButtons: (value: boolean) => void, validationProps?: {
|
|
19
|
+
error?: string;
|
|
20
|
+
required?: boolean;
|
|
21
|
+
}) => React.ReactNode;
|
|
22
|
+
column: DataTableColumn<T>;
|
|
23
|
+
}
|
|
24
|
+
export interface Action<T extends BaseEntity> {
|
|
25
|
+
icon?: React.ReactNode;
|
|
26
|
+
label: string;
|
|
27
|
+
onClick: (records: T[]) => void;
|
|
28
|
+
disabled?: (records: T[]) => boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface TabOption {
|
|
31
|
+
value: string;
|
|
32
|
+
label: string;
|
|
33
|
+
icon?: React.ReactNode;
|
|
34
|
+
queryParams?: Record<string, string | number | boolean | null>;
|
|
35
|
+
apiPath?: string;
|
|
36
|
+
mutationApiPath?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface StepConfig {
|
|
39
|
+
label: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface DataTableProps<T extends BaseEntity> {
|
|
43
|
+
title?: string | React.ReactNode;
|
|
44
|
+
queryKey: (string | number)[];
|
|
45
|
+
connectedQueryKeys?: (string | number)[][];
|
|
46
|
+
apiPath: string;
|
|
47
|
+
mutationApiPath?: string;
|
|
48
|
+
queryParams?: Record<string, string | number | boolean | null>;
|
|
49
|
+
filters?: Filter[];
|
|
50
|
+
buttons?: React.ReactNode[];
|
|
51
|
+
createButtonText?: string;
|
|
52
|
+
actions?: Action<T>[];
|
|
53
|
+
selection?: boolean;
|
|
54
|
+
pagination?: boolean;
|
|
55
|
+
steps?: StepConfig[];
|
|
56
|
+
fields: Field<T>[];
|
|
57
|
+
defaultSort?: {
|
|
58
|
+
field: string;
|
|
59
|
+
direction: "asc" | "desc";
|
|
60
|
+
};
|
|
61
|
+
onSortChange?: (field: string, direction: "asc" | "desc") => void;
|
|
62
|
+
tabs?: TabOption[];
|
|
63
|
+
defaultTab?: string;
|
|
64
|
+
activeTab?: string | null;
|
|
65
|
+
onActiveTabChange?: (tabValue: string | null) => void;
|
|
66
|
+
canUpdate?: (record: T) => boolean;
|
|
67
|
+
canDelete?: (record: T) => boolean;
|
|
68
|
+
showRefresh?: boolean;
|
|
69
|
+
rowExpansion?: {
|
|
70
|
+
allowMultiple?: boolean;
|
|
71
|
+
expandable?: (record: T) => boolean;
|
|
72
|
+
content: (record: T, isMobile: boolean) => React.ReactNode;
|
|
73
|
+
expanded?: {
|
|
74
|
+
recordIds: unknown[];
|
|
75
|
+
onRecordIdsChange: (recordIds: unknown[]) => void;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
onRowClick?: (params: {
|
|
79
|
+
record: T;
|
|
80
|
+
index: number;
|
|
81
|
+
event: React.MouseEvent;
|
|
82
|
+
}) => void;
|
|
83
|
+
mobileCards?: boolean;
|
|
84
|
+
deleteConfirmMessage?: (records: T[]) => React.ReactNode;
|
|
85
|
+
editRecordId?: string | null;
|
|
86
|
+
onEditRecordIdChange?: (id: string | null) => void;
|
|
87
|
+
}
|
|
88
|
+
export declare function DataTable<T extends BaseEntity>({ title, queryKey, connectedQueryKeys, apiPath, mutationApiPath, buttons, fields, selection, pagination, filters, actions, steps, defaultSort, onSortChange, createButtonText, queryParams, tabs, defaultTab, activeTab: controlledActiveTab, onActiveTabChange, canUpdate, canDelete, showRefresh, rowExpansion, onRowClick, mobileCards, deleteConfirmMessage, editRecordId, onEditRecordIdChange, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseEntity } from '../Hooks/useApi';
|
|
2
|
+
import { Field, StepConfig } from './DataTable.tsx';
|
|
3
|
+
export interface EntityFormProps<T extends BaseEntity> {
|
|
4
|
+
fields: Field<T>[];
|
|
5
|
+
steps?: StepConfig[];
|
|
6
|
+
record?: T;
|
|
7
|
+
recordId?: string | number;
|
|
8
|
+
submitting: boolean;
|
|
9
|
+
error?: Error | null;
|
|
10
|
+
onPersist: (values: T) => Promise<void>;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function EntityForm<T extends BaseEntity>({ fields, steps, record, recordId, submitting, error, onPersist, onClose, }: EntityFormProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export interface FieldRow {
|
|
3
|
+
label: React.ReactNode;
|
|
4
|
+
value: React.ReactNode;
|
|
5
|
+
}
|
|
6
|
+
export declare function FieldCardRows({ rows }: {
|
|
7
|
+
readonly rows: readonly FieldRow[];
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function FieldCard({ rows }: {
|
|
10
|
+
readonly rows: readonly FieldRow[];
|
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseEntity } from '../Hooks/useApi';
|
|
2
|
-
import { Action, Field } from './
|
|
2
|
+
import { Action, Field } from './DataTable';
|
|
3
3
|
import { default as React } from 'react';
|
|
4
4
|
interface SortConfig {
|
|
5
5
|
field: string;
|
|
@@ -27,6 +27,7 @@ interface MobileCardListProps<T extends BaseEntity> {
|
|
|
27
27
|
};
|
|
28
28
|
sort?: SortConfig;
|
|
29
29
|
rowExpansion?: {
|
|
30
|
+
expandable?: (record: T) => boolean;
|
|
30
31
|
content: (record: T, isMobile: boolean) => React.ReactNode;
|
|
31
32
|
expanded?: {
|
|
32
33
|
recordIds: unknown[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DataTableColumn, DataTableProps as MantineDataTableProps } from 'mantine-datatable';
|
|
2
|
+
export type SubTableColumn<T> = DataTableColumn<T> & {
|
|
3
|
+
hideOnMobile?: (record: T) => boolean;
|
|
4
|
+
};
|
|
5
|
+
export type SubTableProps<T> = Omit<MantineDataTableProps<T>, "columns"> & {
|
|
6
|
+
mobile: boolean;
|
|
7
|
+
columns: SubTableColumn<T>[];
|
|
8
|
+
};
|
|
9
|
+
export declare function SubTable<T>({ mobile, columns, ...props }: SubTableProps<T>): import("react/jsx-runtime").JSX.Element;
|
package/dist/Hooks/useApi.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { GetHeaders } from '../Context/DataTableContext.tsx';
|
|
2
|
+
export interface FieldViolation {
|
|
3
|
+
field: string;
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class ApiError extends Error {
|
|
7
|
+
readonly status: number;
|
|
8
|
+
readonly violations?: FieldViolation[];
|
|
9
|
+
constructor(message: string, status: number, violations?: FieldViolation[]);
|
|
10
|
+
}
|
|
11
|
+
export declare function getFieldViolations(error: unknown): FieldViolation[] | undefined;
|
|
2
12
|
export declare function parseApiError(error: unknown): {
|
|
3
13
|
message: string;
|
|
4
14
|
code?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function usePersistentState<T>(initialValue: T, key: string): [T, (value: T) => void];
|
|
1
|
+
export declare function usePersistentState<T>(initialValue: T, key: string): [T, (value: T | ((prev: T) => T)) => void];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { BaseEntity } from './Hooks/useApi';
|
|
2
2
|
export { parseApiError, api, useGetOne, useDeleteOne, useGetAll, useUpdateOne, deleteOne, createOne, getAll, updateOne, useAddOne, getOne, } from './Hooks/useApi';
|
|
3
|
-
export type { DataTableProps, FieldType, Field, StepConfig, TabOption, Action, } from './DataTable/
|
|
3
|
+
export type { DataTableProps, FieldType, Field, StepConfig, TabOption, Action, } from './DataTable/DataTable.tsx';
|
|
4
4
|
export { DataTable } from './DataTable/DataTable.tsx';
|
|
5
5
|
export { CreateModal } from './DataTable/CreateModal.tsx';
|
|
6
6
|
export type { CreateModalProps } from './DataTable/CreateModal.tsx';
|
|
@@ -9,6 +9,10 @@ export type { UpdateModalProps } from './DataTable/UpdateModal.tsx';
|
|
|
9
9
|
export { DeleteModal } from './DataTable/DeleteModal.tsx';
|
|
10
10
|
export type { DeleteModalProps } from './DataTable/DeleteModal.tsx';
|
|
11
11
|
export { MobileCardList } from './DataTable/MobileCardList.tsx';
|
|
12
|
+
export { FieldCard } from './DataTable/FieldCard.tsx';
|
|
13
|
+
export type { FieldRow } from './DataTable/FieldCard.tsx';
|
|
14
|
+
export { SubTable } from './DataTable/SubTable.tsx';
|
|
15
|
+
export type { SubTableColumn, SubTableProps } from './DataTable/SubTable.tsx';
|
|
12
16
|
export { usePersistentState } from './Hooks/usePersistentState.ts';
|
|
13
17
|
export { useDataTable } from './Hooks/useDataTable.ts';
|
|
14
18
|
export { DataTableProvider } from './Context/DataTableContext.tsx';
|