@espresso-lab/mantine-data-table 2.1.14 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
- # Mantine Data Table 🚀
1
+ # Mantine Data Table
2
2
 
3
- This is a simple wrapper for the [Mantine Datatable](https://icflorescu.github.io/mantine-datatable/) library to work
4
- with a single configuration object.
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](https://img.shields.io/badge/License-MIT-blue)](#license)
7
8
  [![NPM Version](https://img.shields.io/npm/v/@espresso-lab/mantine-data-table.svg?style=flat)](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,5 +1,5 @@
1
1
  import { BaseEntity } from '../Hooks/useApi';
2
- import { Field, StepConfig } from './DataTableInner.tsx';
2
+ import { Field, StepConfig } from './DataTable.tsx';
3
3
  export interface CreateModalProps<T> {
4
4
  fields: Field<T>[];
5
5
  onClose: () => void;
@@ -1,3 +1,88 @@
1
- import { BaseEntity } from '../Hooks/useApi.ts';
2
- import { DataTableProps } from './DataTableInner.tsx';
3
- export declare function DataTable<T extends BaseEntity>(props: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
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<boolean>;
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 './DataTableInner';
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;
@@ -1,5 +1,5 @@
1
1
  import { BaseEntity } from '../Hooks/useApi';
2
- import { Field, StepConfig } from './DataTableInner.tsx';
2
+ import { Field, StepConfig } from './DataTable.tsx';
3
3
  export interface UpdateModalProps<T> {
4
4
  fields: Field<T>[];
5
5
  steps?: StepConfig[];
@@ -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/DataTableInner.tsx';
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';