@zuilib/data-grid 0.3.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 +129 -0
- package/dist/cells.d.ts +61 -0
- package/dist/cells.js +60 -0
- package/dist/columns-Def2RYjD.d.ts +51 -0
- package/dist/data-grid.d.ts +309 -0
- package/dist/data-grid.js +1573 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1630 -0
- package/dist/server-adapter.d.ts +65 -0
- package/dist/server-adapter.js +105 -0
- package/dist/state-9r5WYKo_.d.ts +63 -0
- package/dist/types-B00kY3c9.d.ts +97 -0
- package/dist/types-Br4lyM03.d.ts +192 -0
- package/dist/types-D4DDIl3w.d.ts +73 -0
- package/dist/types-D8c-NBWy.d.ts +117 -0
- package/dist/types-DJh1--C1.d.ts +188 -0
- package/dist/types-j91R0mPE.d.ts +75 -0
- package/dist/use-data-grid.d.ts +71 -0
- package/dist/use-data-grid.js +206 -0
- package/package.json +73 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { ColumnDef, PaginationState, SortingState, ColumnFiltersState, VisibilityState, ColumnOrderState, ColumnPinningState, RowSelectionState, ExpandedState, ColumnSizingState } from '@tanstack/react-table';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Everything the grid can change. The consumer owns it: every interaction
|
|
5
|
+
* calls `onStateChange` with the whole next state, and the grid renders
|
|
6
|
+
* whatever comes back (a server round-trip in between is the point).
|
|
7
|
+
*/
|
|
8
|
+
interface DataGridState {
|
|
9
|
+
pagination: PaginationState;
|
|
10
|
+
sorting: SortingState;
|
|
11
|
+
columnFilters: ColumnFiltersState;
|
|
12
|
+
globalFilter: string;
|
|
13
|
+
columnVisibility: VisibilityState;
|
|
14
|
+
columnOrder: ColumnOrderState;
|
|
15
|
+
columnPinning: ColumnPinningState;
|
|
16
|
+
rowSelection: RowSelectionState;
|
|
17
|
+
/** Expanded rows, keyed by row id. UI-only: `toQuery` leaves it out. */
|
|
18
|
+
expanded: ExpandedState;
|
|
19
|
+
/**
|
|
20
|
+
* "Select all N": every row matching the current query is selected, not
|
|
21
|
+
* only the ones on this page. Set by the bulk bar; cleared by any other
|
|
22
|
+
* selection change. `toQuery` passes it on, the server acts on it.
|
|
23
|
+
*/
|
|
24
|
+
allMatching?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Column widths in px, keyed by column id. Optional: left out, the grid
|
|
27
|
+
* keeps the widths itself; passed (even `{}`), resizes go out through
|
|
28
|
+
* `onStateChange` so they can be persisted. UI-only: `toQuery` leaves it out.
|
|
29
|
+
*/
|
|
30
|
+
columnSizing?: ColumnSizingState;
|
|
31
|
+
}
|
|
32
|
+
declare const DEFAULT_PAGE_SIZE = 25;
|
|
33
|
+
declare const defaultDataGridState: DataGridState;
|
|
34
|
+
/** Fills the keys a consumer left out, so the grid always sees a full state. */
|
|
35
|
+
declare function resolveDataGridState(state: Partial<DataGridState> | undefined): DataGridState;
|
|
36
|
+
type DataGridAlign = 'start' | 'center' | 'end';
|
|
37
|
+
/** A text filter: one string, matched however the server matches it. */
|
|
38
|
+
interface DataGridTextFilter {
|
|
39
|
+
type: 'text';
|
|
40
|
+
placeholder?: string;
|
|
41
|
+
}
|
|
42
|
+
/** A facet filter: a list of options, of which any number may be picked. */
|
|
43
|
+
interface DataGridSelectFilter {
|
|
44
|
+
type: 'select';
|
|
45
|
+
options: readonly {
|
|
46
|
+
value: string;
|
|
47
|
+
label?: string;
|
|
48
|
+
}[];
|
|
49
|
+
}
|
|
50
|
+
/** A date range: ISO `YYYY-MM-DD` bounds, either side optional. */
|
|
51
|
+
interface DataGridDateRangeFilter {
|
|
52
|
+
type: 'date-range';
|
|
53
|
+
}
|
|
54
|
+
type DataGridFilter = DataGridTextFilter | DataGridSelectFilter | DataGridDateRangeFilter;
|
|
55
|
+
interface DataGridDateRange {
|
|
56
|
+
from?: string;
|
|
57
|
+
to?: string;
|
|
58
|
+
}
|
|
59
|
+
/** The ZUI extras on a TanStack column definition. */
|
|
60
|
+
interface DataGridColumnExtras {
|
|
61
|
+
/** Double-click or Enter on a cell turns it into an input; see `onCellEdit`. */
|
|
62
|
+
enableInlineEdit?: boolean;
|
|
63
|
+
/** Horizontal alignment of the head and the cells; `end` for numbers. */
|
|
64
|
+
align?: DataGridAlign;
|
|
65
|
+
/** Initial width in px (TanStack `size`); the user can resize from there. */
|
|
66
|
+
width?: number;
|
|
67
|
+
/** Declares the column's filter popover. */
|
|
68
|
+
filter?: DataGridFilter;
|
|
69
|
+
}
|
|
70
|
+
type DataGridColumnDef<TData, TValue = unknown> = ColumnDef<TData, TValue> & DataGridColumnExtras;
|
|
71
|
+
/** The extras may also be declared on `meta` (the TanStack way of extending a column). */
|
|
72
|
+
interface DataGridColumnMeta extends DataGridColumnExtras {
|
|
73
|
+
/** The column's name in the column manager, the filter chips and the pin labels, when `header` is not a string. */
|
|
74
|
+
label?: string;
|
|
75
|
+
}
|
|
76
|
+
/** The grid's own strings, overridable for another language. */
|
|
77
|
+
interface DataGridLabels {
|
|
78
|
+
/** @default 'Pin left' */
|
|
79
|
+
pinLeft: string;
|
|
80
|
+
/** @default 'Pin right' */
|
|
81
|
+
pinRight: string;
|
|
82
|
+
/** @default 'Unpin' */
|
|
83
|
+
unpin: string;
|
|
84
|
+
/** @default 'Expand row' */
|
|
85
|
+
expandRow: string;
|
|
86
|
+
/** @default 'Collapse row' */
|
|
87
|
+
collapseRow: string;
|
|
88
|
+
/** Bulk bar: the count of selected rows. `{count}` is replaced. @default '{count} selected' */
|
|
89
|
+
selectedCount: string;
|
|
90
|
+
/** Bulk bar: the count when every matching row is selected. `{count}` is replaced. @default 'All {count} selected' */
|
|
91
|
+
allMatchingSelected: string;
|
|
92
|
+
/** Bulk bar: the button that selects every matching row. `{count}` is replaced. @default 'Select all {count}' */
|
|
93
|
+
selectAllMatching: string;
|
|
94
|
+
/** @default 'Clear selection' */
|
|
95
|
+
clearSelection: string;
|
|
96
|
+
}
|
|
97
|
+
declare const defaultDataGridLabels: DataGridLabels;
|
|
98
|
+
interface DataGridCellEdit {
|
|
99
|
+
rowId: string;
|
|
100
|
+
columnId: string;
|
|
101
|
+
value: string;
|
|
102
|
+
}
|
|
103
|
+
interface DataGridBulkActionContext {
|
|
104
|
+
/** `true` when "Select all N" is on: act on every row matching the query, not only `selectedIds`. */
|
|
105
|
+
allMatching: boolean;
|
|
106
|
+
/** The rows matching the query (`rowCount`), the size of the set when `allMatching` is on. */
|
|
107
|
+
rowCount: number;
|
|
108
|
+
}
|
|
109
|
+
interface DataGridBulkAction {
|
|
110
|
+
id: string;
|
|
111
|
+
label: string;
|
|
112
|
+
variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost';
|
|
113
|
+
onAction: (selectedIds: string[], context: DataGridBulkActionContext) => void;
|
|
114
|
+
}
|
|
115
|
+
type DataGridDensity = 'compact' | 'comfortable';
|
|
116
|
+
|
|
117
|
+
export { DEFAULT_PAGE_SIZE as D, type DataGridAlign as a, type DataGridBulkAction as b, type DataGridBulkActionContext as c, type DataGridCellEdit as d, type DataGridColumnDef as e, type DataGridColumnExtras as f, type DataGridColumnMeta as g, type DataGridDateRange as h, type DataGridDateRangeFilter as i, type DataGridDensity as j, type DataGridFilter as k, type DataGridLabels as l, type DataGridSelectFilter as m, type DataGridState as n, type DataGridTextFilter as o, defaultDataGridLabels as p, defaultDataGridState as q, resolveDataGridState as r };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { ColumnDef, PaginationState, SortingState, ColumnFiltersState, VisibilityState, ColumnOrderState, ColumnPinningState, RowSelectionState, ExpandedState, ColumnSizingState } from '@tanstack/react-table';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Everything the grid can change. The consumer owns it: every interaction
|
|
5
|
+
* calls `onStateChange` with the whole next state, and the grid renders
|
|
6
|
+
* whatever comes back (a server round-trip in between is the point).
|
|
7
|
+
*/
|
|
8
|
+
interface DataGridState {
|
|
9
|
+
pagination: PaginationState;
|
|
10
|
+
sorting: SortingState;
|
|
11
|
+
columnFilters: ColumnFiltersState;
|
|
12
|
+
globalFilter: string;
|
|
13
|
+
columnVisibility: VisibilityState;
|
|
14
|
+
columnOrder: ColumnOrderState;
|
|
15
|
+
columnPinning: ColumnPinningState;
|
|
16
|
+
rowSelection: RowSelectionState;
|
|
17
|
+
/** Expanded rows, keyed by row id. UI-only: `toQuery` leaves it out. */
|
|
18
|
+
expanded: ExpandedState;
|
|
19
|
+
/**
|
|
20
|
+
* "Select all N": every row matching the current query is selected, not
|
|
21
|
+
* only the ones on this page. Set by the bulk bar; cleared by any other
|
|
22
|
+
* selection change. `toQuery` passes it on, the server acts on it.
|
|
23
|
+
*/
|
|
24
|
+
allMatching?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Column widths in px, keyed by column id. Optional: left out, the grid
|
|
27
|
+
* keeps the widths itself; passed (even `{}`), resizes go out through
|
|
28
|
+
* `onStateChange` so they can be persisted. UI-only: `toQuery` leaves it out.
|
|
29
|
+
*/
|
|
30
|
+
columnSizing?: ColumnSizingState;
|
|
31
|
+
}
|
|
32
|
+
declare const DEFAULT_PAGE_SIZE = 25;
|
|
33
|
+
declare const defaultDataGridState: DataGridState;
|
|
34
|
+
/** Fills the keys a consumer left out, so the grid always sees a full state. */
|
|
35
|
+
declare function resolveDataGridState(state: Partial<DataGridState> | undefined): DataGridState;
|
|
36
|
+
type DataGridAlign = 'start' | 'center' | 'end';
|
|
37
|
+
/** A text filter: one string, matched however the server matches it. */
|
|
38
|
+
interface DataGridTextFilter {
|
|
39
|
+
type: 'text';
|
|
40
|
+
placeholder?: string;
|
|
41
|
+
}
|
|
42
|
+
/** A facet filter: a list of options, of which any number may be picked. */
|
|
43
|
+
interface DataGridSelectFilter {
|
|
44
|
+
type: 'select';
|
|
45
|
+
options: readonly {
|
|
46
|
+
value: string;
|
|
47
|
+
label?: string;
|
|
48
|
+
}[];
|
|
49
|
+
}
|
|
50
|
+
/** A date range: ISO `YYYY-MM-DD` bounds, either side optional. */
|
|
51
|
+
interface DataGridDateRangeFilter {
|
|
52
|
+
type: 'date-range';
|
|
53
|
+
}
|
|
54
|
+
type DataGridFilter = DataGridTextFilter | DataGridSelectFilter | DataGridDateRangeFilter;
|
|
55
|
+
interface DataGridDateRange {
|
|
56
|
+
from?: string;
|
|
57
|
+
to?: string;
|
|
58
|
+
}
|
|
59
|
+
/** The ZUI extras on a TanStack column definition. */
|
|
60
|
+
interface DataGridColumnExtras {
|
|
61
|
+
/** Double-click or Enter on a cell turns it into an input; see `onCellEdit`. */
|
|
62
|
+
enableInlineEdit?: boolean;
|
|
63
|
+
/** Horizontal alignment of the head and the cells; `end` for numbers. */
|
|
64
|
+
align?: DataGridAlign;
|
|
65
|
+
/** Initial width in px (TanStack `size`); the user can resize from there. */
|
|
66
|
+
width?: number;
|
|
67
|
+
/** Declares the column's filter popover. */
|
|
68
|
+
filter?: DataGridFilter;
|
|
69
|
+
}
|
|
70
|
+
type DataGridColumnDef<TData, TValue = unknown> = ColumnDef<TData, TValue> & DataGridColumnExtras;
|
|
71
|
+
/** The extras may also be declared on `meta` (the TanStack way of extending a column). */
|
|
72
|
+
interface DataGridColumnMeta extends DataGridColumnExtras {
|
|
73
|
+
/** The column's name in the column manager, the filter chips and the pin labels, when `header` is not a string. */
|
|
74
|
+
label?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Every string the grid renders or announces, overridable for another
|
|
78
|
+
* language. A `{name}` placeholder is replaced with the value named.
|
|
79
|
+
*/
|
|
80
|
+
interface DataGridLabels {
|
|
81
|
+
/** @default 'Pin left' */
|
|
82
|
+
pinLeft: string;
|
|
83
|
+
/** @default 'Pin right' */
|
|
84
|
+
pinRight: string;
|
|
85
|
+
/** @default 'Unpin' */
|
|
86
|
+
unpin: string;
|
|
87
|
+
/** @default 'Expand row' */
|
|
88
|
+
expandRow: string;
|
|
89
|
+
/** @default 'Collapse row' */
|
|
90
|
+
collapseRow: string;
|
|
91
|
+
/** The sr-only head of the expand column. @default 'Expand' */
|
|
92
|
+
expandColumn: string;
|
|
93
|
+
/** Bulk bar: the count of selected rows. `{count}` is replaced. @default '{count} selected' */
|
|
94
|
+
selectedCount: string;
|
|
95
|
+
/** Bulk bar: the count when every matching row is selected. `{count}` is replaced. @default 'All {count} selected' */
|
|
96
|
+
allMatchingSelected: string;
|
|
97
|
+
/** Bulk bar: the button that selects every matching row. `{count}` is replaced. @default 'Select all {count}' */
|
|
98
|
+
selectAllMatching: string;
|
|
99
|
+
/** @default 'Clear selection' */
|
|
100
|
+
clearSelection: string;
|
|
101
|
+
/** The bulk bar's region name. @default 'Bulk actions' */
|
|
102
|
+
bulkActions: string;
|
|
103
|
+
/** The head checkbox. @default 'Select all rows on this page' */
|
|
104
|
+
selectAllOnPage: string;
|
|
105
|
+
/** A row checkbox. @default 'Select row' */
|
|
106
|
+
selectRow: string;
|
|
107
|
+
/** The footer's `<nav>` name. @default 'Pagination' */
|
|
108
|
+
pagination: string;
|
|
109
|
+
/** @default 'Rows per page' */
|
|
110
|
+
rowsPerPage: string;
|
|
111
|
+
/** The range text while loading. @default 'Loading…' */
|
|
112
|
+
loading: string;
|
|
113
|
+
/** The range text. `{first}`, `{last}` and `{total}` are replaced. @default '{first}–{last} of {total}' */
|
|
114
|
+
range: string;
|
|
115
|
+
/** @default 'First page' */
|
|
116
|
+
firstPage: string;
|
|
117
|
+
/** @default 'Previous page' */
|
|
118
|
+
previousPage: string;
|
|
119
|
+
/** @default 'Next page' */
|
|
120
|
+
nextPage: string;
|
|
121
|
+
/** @default 'Last page' */
|
|
122
|
+
lastPage: string;
|
|
123
|
+
/** The toolbar search field's name. @default 'Search' */
|
|
124
|
+
search: string;
|
|
125
|
+
/** The toolbar search field's placeholder. @default 'Search…' */
|
|
126
|
+
searchPlaceholder: string;
|
|
127
|
+
/** The filter chip list's name. @default 'Active filters' */
|
|
128
|
+
activeFilters: string;
|
|
129
|
+
/** A chip's remove button. `{column}` is replaced. @default 'Remove {column} filter' */
|
|
130
|
+
removeFilter: string;
|
|
131
|
+
/** The column manager button and its list. @default 'Columns' */
|
|
132
|
+
columns: string;
|
|
133
|
+
/** `{column}` is replaced. @default 'Move {column} up' */
|
|
134
|
+
moveUp: string;
|
|
135
|
+
/** `{column}` is replaced. @default 'Move {column} down' */
|
|
136
|
+
moveDown: string;
|
|
137
|
+
/** The funnel button. `{column}` is replaced. @default 'Filter {column}' */
|
|
138
|
+
filterColumn: string;
|
|
139
|
+
/** The funnel button while a filter is set. `{column}` is replaced. @default 'Filter {column} (active)' */
|
|
140
|
+
filterColumnActive: string;
|
|
141
|
+
/** The text filter input. @default 'Filter value' */
|
|
142
|
+
filterValue: string;
|
|
143
|
+
/** The text filter placeholder when the column declares none. @default 'Contains…' */
|
|
144
|
+
filterPlaceholder: string;
|
|
145
|
+
/** The facet filter's checkbox group. @default 'Options' */
|
|
146
|
+
filterOptions: string;
|
|
147
|
+
/** @default 'From' */
|
|
148
|
+
from: string;
|
|
149
|
+
/** @default 'To' */
|
|
150
|
+
to: string;
|
|
151
|
+
/** @default 'Clear' */
|
|
152
|
+
clear: string;
|
|
153
|
+
/** @default 'Apply' */
|
|
154
|
+
apply: string;
|
|
155
|
+
/** A date-range chip with only a lower bound. `{date}` is replaced. @default 'from {date}' */
|
|
156
|
+
rangeFrom: string;
|
|
157
|
+
/** A date-range chip with only an upper bound. `{date}` is replaced. @default 'until {date}' */
|
|
158
|
+
rangeUntil: string;
|
|
159
|
+
/** The resize handle. `{column}` is replaced. @default 'Resize {column}' */
|
|
160
|
+
resizeColumn: string;
|
|
161
|
+
/** The inline edit input. `{column}` is replaced. @default 'Edit {column}' */
|
|
162
|
+
editColumn: string;
|
|
163
|
+
/** The default empty state's title. @default 'No results' */
|
|
164
|
+
noResults: string;
|
|
165
|
+
/** The default empty state's description. @default 'Try a different search or clear the filters.' */
|
|
166
|
+
noResultsDescription: string;
|
|
167
|
+
}
|
|
168
|
+
declare const defaultDataGridLabels: DataGridLabels;
|
|
169
|
+
interface DataGridCellEdit {
|
|
170
|
+
rowId: string;
|
|
171
|
+
columnId: string;
|
|
172
|
+
value: string;
|
|
173
|
+
}
|
|
174
|
+
interface DataGridBulkActionContext {
|
|
175
|
+
/** `true` when "Select all N" is on: act on every row matching the query, not only `selectedIds`. */
|
|
176
|
+
allMatching: boolean;
|
|
177
|
+
/** The rows matching the query (`rowCount`), the size of the set when `allMatching` is on. */
|
|
178
|
+
rowCount: number;
|
|
179
|
+
}
|
|
180
|
+
interface DataGridBulkAction {
|
|
181
|
+
id: string;
|
|
182
|
+
label: string;
|
|
183
|
+
variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost';
|
|
184
|
+
onAction: (selectedIds: string[], context: DataGridBulkActionContext) => void;
|
|
185
|
+
}
|
|
186
|
+
type DataGridDensity = 'compact' | 'comfortable';
|
|
187
|
+
|
|
188
|
+
export { DEFAULT_PAGE_SIZE as D, type DataGridAlign as a, type DataGridBulkAction as b, type DataGridBulkActionContext as c, type DataGridCellEdit as d, type DataGridColumnDef as e, type DataGridColumnExtras as f, type DataGridColumnMeta as g, type DataGridDateRange as h, type DataGridDateRangeFilter as i, type DataGridDensity as j, type DataGridFilter as k, type DataGridLabels as l, type DataGridSelectFilter as m, type DataGridState as n, type DataGridTextFilter as o, defaultDataGridLabels as p, defaultDataGridState as q, resolveDataGridState as r };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ColumnDef, PaginationState, SortingState, ColumnFiltersState, VisibilityState, ColumnOrderState, ColumnPinningState, RowSelectionState, ExpandedState } from '@tanstack/react-table';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Everything the grid can change. The consumer owns it: every interaction
|
|
5
|
+
* calls `onStateChange` with the whole next state, and the grid renders
|
|
6
|
+
* whatever comes back (a server round-trip in between is the point).
|
|
7
|
+
*/
|
|
8
|
+
interface DataGridState {
|
|
9
|
+
pagination: PaginationState;
|
|
10
|
+
sorting: SortingState;
|
|
11
|
+
columnFilters: ColumnFiltersState;
|
|
12
|
+
globalFilter: string;
|
|
13
|
+
columnVisibility: VisibilityState;
|
|
14
|
+
columnOrder: ColumnOrderState;
|
|
15
|
+
columnPinning: ColumnPinningState;
|
|
16
|
+
rowSelection: RowSelectionState;
|
|
17
|
+
/** Expanded rows, keyed by row id. UI-only: `toQuery` leaves it out. */
|
|
18
|
+
expanded: ExpandedState;
|
|
19
|
+
}
|
|
20
|
+
declare const DEFAULT_PAGE_SIZE = 25;
|
|
21
|
+
declare const defaultDataGridState: DataGridState;
|
|
22
|
+
/** Fills the keys a consumer left out, so the grid always sees a full state. */
|
|
23
|
+
declare function resolveDataGridState(state: Partial<DataGridState> | undefined): DataGridState;
|
|
24
|
+
type DataGridAlign = 'start' | 'center' | 'end';
|
|
25
|
+
/** A text filter: one string, matched however the server matches it. */
|
|
26
|
+
interface DataGridTextFilter {
|
|
27
|
+
type: 'text';
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
}
|
|
30
|
+
/** A facet filter: a list of options, of which any number may be picked. */
|
|
31
|
+
interface DataGridSelectFilter {
|
|
32
|
+
type: 'select';
|
|
33
|
+
options: readonly {
|
|
34
|
+
value: string;
|
|
35
|
+
label?: string;
|
|
36
|
+
}[];
|
|
37
|
+
}
|
|
38
|
+
/** A date range: ISO `YYYY-MM-DD` bounds, either side optional. */
|
|
39
|
+
interface DataGridDateRangeFilter {
|
|
40
|
+
type: 'date-range';
|
|
41
|
+
}
|
|
42
|
+
type DataGridFilter = DataGridTextFilter | DataGridSelectFilter | DataGridDateRangeFilter;
|
|
43
|
+
interface DataGridDateRange {
|
|
44
|
+
from?: string;
|
|
45
|
+
to?: string;
|
|
46
|
+
}
|
|
47
|
+
/** The ZUI extras on a TanStack column definition. */
|
|
48
|
+
interface DataGridColumnExtras {
|
|
49
|
+
/** Double-click or Enter on a cell turns it into an input; see `onCellEdit`. */
|
|
50
|
+
enableInlineEdit?: boolean;
|
|
51
|
+
/** Horizontal alignment of the head and the cells; `end` for numbers. */
|
|
52
|
+
align?: DataGridAlign;
|
|
53
|
+
/** Initial width in px (TanStack `size`); the user can resize from there. */
|
|
54
|
+
width?: number;
|
|
55
|
+
/** Declares the column's filter popover. */
|
|
56
|
+
filter?: DataGridFilter;
|
|
57
|
+
}
|
|
58
|
+
type DataGridColumnDef<TData, TValue = unknown> = ColumnDef<TData, TValue> & DataGridColumnExtras;
|
|
59
|
+
/** The extras may also be declared on `meta` (the TanStack way of extending a column). */
|
|
60
|
+
interface DataGridColumnMeta extends DataGridColumnExtras {
|
|
61
|
+
}
|
|
62
|
+
interface DataGridCellEdit {
|
|
63
|
+
rowId: string;
|
|
64
|
+
columnId: string;
|
|
65
|
+
value: string;
|
|
66
|
+
}
|
|
67
|
+
interface DataGridBulkAction {
|
|
68
|
+
id: string;
|
|
69
|
+
label: string;
|
|
70
|
+
variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost';
|
|
71
|
+
onAction: (selectedIds: string[]) => void;
|
|
72
|
+
}
|
|
73
|
+
type DataGridDensity = 'compact' | 'comfortable';
|
|
74
|
+
|
|
75
|
+
export { type DataGridState as D, type DataGridDensity as a, type DataGridCellEdit as b, type DataGridBulkAction as c, DEFAULT_PAGE_SIZE as d, type DataGridAlign as e, type DataGridColumnDef as f, type DataGridColumnExtras as g, type DataGridColumnMeta as h, type DataGridDateRange as i, type DataGridDateRangeFilter as j, type DataGridFilter as k, type DataGridSelectFilter as l, type DataGridTextFilter as m, defaultDataGridState as n, resolveDataGridState as r };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Row, TableOptions, Table, Updater } from '@tanstack/react-table';
|
|
2
|
+
import { b as DataGridColumnDef } from './columns-Def2RYjD.js';
|
|
3
|
+
import { e as DataGridState } from './state-9r5WYKo_.js';
|
|
4
|
+
import '@zuilib/components/button';
|
|
5
|
+
|
|
6
|
+
/** Called with the whole next state on every interaction; see `onStateChange`. */
|
|
7
|
+
type DataGridStateChangeHandler = (state: DataGridState) => void;
|
|
8
|
+
interface UseDataGridOptions<TData> {
|
|
9
|
+
columns: DataGridColumnDef<TData, any>[];
|
|
10
|
+
/** The current page of rows, rendered as given. */
|
|
11
|
+
rows: TData[];
|
|
12
|
+
/** Total rows on the server (all pages). Drives the page count and the range text. Ignored with `manualPagination={false}`: the rows that pass the filters are counted instead. */
|
|
13
|
+
rowCount?: number;
|
|
14
|
+
/**
|
|
15
|
+
* The controlled state. Left out, the hook keeps the state itself
|
|
16
|
+
* (starting from `defaultState`) and still reports it through `onStateChange`.
|
|
17
|
+
*/
|
|
18
|
+
state?: Partial<DataGridState>;
|
|
19
|
+
/** The initial state when `state` is left out. Read once. */
|
|
20
|
+
defaultState?: Partial<DataGridState>;
|
|
21
|
+
/**
|
|
22
|
+
* Called with the whole next state on every interaction: the current
|
|
23
|
+
* `state` prop plus that one change. An emission the consumer does not
|
|
24
|
+
* apply is not carried into the next one, so ignoring it rejects it.
|
|
25
|
+
*/
|
|
26
|
+
onStateChange?: DataGridStateChangeHandler;
|
|
27
|
+
/**
|
|
28
|
+
* Column ids the grid owns (the selection and expand columns): always
|
|
29
|
+
* first in `columnOrder`, always first among the start-pinned columns.
|
|
30
|
+
*/
|
|
31
|
+
fixedColumnIds?: readonly string[];
|
|
32
|
+
/** @default true */
|
|
33
|
+
manualPagination?: boolean;
|
|
34
|
+
/** @default true */
|
|
35
|
+
manualSorting?: boolean;
|
|
36
|
+
/** @default true */
|
|
37
|
+
manualFiltering?: boolean;
|
|
38
|
+
getRowId?: (row: TData, index: number, parent?: Row<TData>) => string;
|
|
39
|
+
/** @default true */
|
|
40
|
+
enableRowSelection?: boolean | ((row: Row<TData>) => boolean);
|
|
41
|
+
/** @default true */
|
|
42
|
+
enableColumnResizing?: boolean;
|
|
43
|
+
/** Which rows may expand; every row when `renderExpanded` is set and this is left out. */
|
|
44
|
+
getRowCanExpand?: (row: Row<TData>) => boolean;
|
|
45
|
+
/** Anything else TanStack accepts (`defaultColumn`, `meta`, `debugTable`, ...). */
|
|
46
|
+
tableOptions?: Partial<Omit<TableOptions<TData>, 'data' | 'columns' | 'state' | 'getCoreRowModel'>>;
|
|
47
|
+
}
|
|
48
|
+
interface UseDataGridResult<TData> {
|
|
49
|
+
table: Table<TData>;
|
|
50
|
+
/** The state the grid renders (the consumer's, with the defaults filled in). */
|
|
51
|
+
state: DataGridState;
|
|
52
|
+
/** Replaces one key and emits the whole next state. */
|
|
53
|
+
setState: <K extends keyof DataGridState>(key: K, updater: Updater<DataGridState[K]>) => void;
|
|
54
|
+
/** Emits a whole next state (a reset, a restore from the URL). */
|
|
55
|
+
replaceState: (next: Partial<DataGridState>) => void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The fixed columns first in `columnOrder` and first among the start-pinned
|
|
59
|
+
* columns, whatever order the consumer handed in. Returns the same object
|
|
60
|
+
* when nothing needs to move, so a normalised state stays referentially equal.
|
|
61
|
+
*/
|
|
62
|
+
declare function normalizeColumnState(state: DataGridState, fixedColumnIds: readonly string[]): DataGridState;
|
|
63
|
+
/**
|
|
64
|
+
* A TanStack table wired for a server-driven grid: every piece of state is
|
|
65
|
+
* controlled, every change goes out through `onStateChange` as one object.
|
|
66
|
+
* `DataGrid` calls this for you; call it yourself to render the parts
|
|
67
|
+
* (`DataGrid.Body`, `DataGrid.Pagination`, ...) in your own layout.
|
|
68
|
+
*/
|
|
69
|
+
declare function useDataGrid<TData>(options: UseDataGridOptions<TData>): UseDataGridResult<TData>;
|
|
70
|
+
|
|
71
|
+
export { type DataGridStateChangeHandler, type UseDataGridOptions, type UseDataGridResult, useDataGrid as default, normalizeColumnState, useDataGrid };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/use-data-grid.ts
|
|
4
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
5
|
+
import {
|
|
6
|
+
getCoreRowModel,
|
|
7
|
+
getFilteredRowModel,
|
|
8
|
+
getPaginationRowModel,
|
|
9
|
+
getSortedRowModel,
|
|
10
|
+
useReactTable
|
|
11
|
+
} from "@tanstack/react-table";
|
|
12
|
+
|
|
13
|
+
// src/state.ts
|
|
14
|
+
var DEFAULT_PAGE_SIZE = 25;
|
|
15
|
+
var defaultDataGridState = {
|
|
16
|
+
pagination: { pageIndex: 0, pageSize: DEFAULT_PAGE_SIZE },
|
|
17
|
+
sorting: [],
|
|
18
|
+
columnFilters: [],
|
|
19
|
+
search: "",
|
|
20
|
+
columnVisibility: {},
|
|
21
|
+
columnOrder: [],
|
|
22
|
+
columnPinning: { start: [], end: [] },
|
|
23
|
+
rowSelection: {},
|
|
24
|
+
expanded: {},
|
|
25
|
+
allMatching: false
|
|
26
|
+
};
|
|
27
|
+
function resolveDataGridState(state) {
|
|
28
|
+
return { ...defaultDataGridState, ...state };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/use-data-grid.ts
|
|
32
|
+
function resolve(updater, previous) {
|
|
33
|
+
return typeof updater === "function" ? updater(previous) : updater;
|
|
34
|
+
}
|
|
35
|
+
var NO_FIXED_COLUMNS = [];
|
|
36
|
+
var sameList = (a, b) => a.length === b.length && a.every((id, i) => id === b[i]);
|
|
37
|
+
function toTanstackPinning(pinning) {
|
|
38
|
+
return { left: pinning.start, right: pinning.end };
|
|
39
|
+
}
|
|
40
|
+
function fromTanstackPinning(pinning) {
|
|
41
|
+
return { start: pinning.left ?? [], end: pinning.right ?? [] };
|
|
42
|
+
}
|
|
43
|
+
function normalizeColumnState(state, fixedColumnIds) {
|
|
44
|
+
if (fixedColumnIds.length === 0) return state;
|
|
45
|
+
const fixed = (ids) => fixedColumnIds.filter((id) => ids.includes(id));
|
|
46
|
+
const rest = (ids) => ids.filter((id) => !fixedColumnIds.includes(id));
|
|
47
|
+
const order = state.columnOrder.length === 0 ? state.columnOrder : [...fixed(state.columnOrder), ...rest(state.columnOrder)];
|
|
48
|
+
const start = [...fixedColumnIds, ...rest(state.columnPinning.start ?? [])];
|
|
49
|
+
const end = rest(state.columnPinning.end ?? []);
|
|
50
|
+
const pinningChanged = !sameList(start, state.columnPinning.start ?? []) || !sameList(end, state.columnPinning.end ?? []);
|
|
51
|
+
const orderChanged = !sameList(order, state.columnOrder);
|
|
52
|
+
if (!pinningChanged && !orderChanged) return state;
|
|
53
|
+
return {
|
|
54
|
+
...state,
|
|
55
|
+
columnOrder: orderChanged ? order : state.columnOrder,
|
|
56
|
+
columnPinning: pinningChanged ? { start, end } : state.columnPinning
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
60
|
+
var inDateRange = (row, columnId, value) => {
|
|
61
|
+
const raw = row.getValue(columnId);
|
|
62
|
+
if (!value || !value.from && !value.to) return true;
|
|
63
|
+
if (raw === void 0 || raw === null || raw === "") return false;
|
|
64
|
+
const day = (raw instanceof Date ? raw.toISOString() : String(raw)).slice(0, 10);
|
|
65
|
+
if (value.from && day < value.from.slice(0, 10)) return false;
|
|
66
|
+
if (value.to && day > value.to.slice(0, 10)) return false;
|
|
67
|
+
return true;
|
|
68
|
+
};
|
|
69
|
+
inDateRange.autoRemove = (value) => !value || !value.from && !value.to;
|
|
70
|
+
var filterFnByControl = { text: "includesString", select: "arrIncludesSome", "date-range": inDateRange };
|
|
71
|
+
function toTanstackColumns(columns) {
|
|
72
|
+
return columns.map((column) => {
|
|
73
|
+
const filter = column.filter ?? column.meta?.filter;
|
|
74
|
+
const next = { ...column };
|
|
75
|
+
if (column.width !== void 0 && column.size === void 0) next.size = column.width;
|
|
76
|
+
if (filter && column.filterFn === void 0) next.filterFn = filterFnByControl[filter.control];
|
|
77
|
+
return next;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function useDataGrid(options) {
|
|
81
|
+
const {
|
|
82
|
+
columns,
|
|
83
|
+
rows,
|
|
84
|
+
rowCount,
|
|
85
|
+
state: partialState,
|
|
86
|
+
defaultState,
|
|
87
|
+
onStateChange,
|
|
88
|
+
fixedColumnIds,
|
|
89
|
+
manualPagination = true,
|
|
90
|
+
manualSorting = true,
|
|
91
|
+
manualFiltering = true,
|
|
92
|
+
getRowId,
|
|
93
|
+
enableRowSelection = true,
|
|
94
|
+
enableColumnResizing = true,
|
|
95
|
+
getRowCanExpand,
|
|
96
|
+
tableOptions
|
|
97
|
+
} = options;
|
|
98
|
+
const controlled = partialState !== void 0;
|
|
99
|
+
const [internalState, setInternalState] = useState(() => resolveDataGridState(defaultState));
|
|
100
|
+
const fixedIds = fixedColumnIds ?? NO_FIXED_COLUMNS;
|
|
101
|
+
const state = useMemo(
|
|
102
|
+
() => normalizeColumnState(controlled ? resolveDataGridState(partialState) : internalState, fixedIds),
|
|
103
|
+
[controlled, partialState, internalState, fixedIds]
|
|
104
|
+
);
|
|
105
|
+
const latest = useRef({ state, onStateChange, pending: null });
|
|
106
|
+
useIsomorphicLayoutEffect(() => {
|
|
107
|
+
latest.current.state = state;
|
|
108
|
+
latest.current.onStateChange = onStateChange;
|
|
109
|
+
latest.current.pending = null;
|
|
110
|
+
});
|
|
111
|
+
const emit = useCallback((next) => {
|
|
112
|
+
if (!controlled) {
|
|
113
|
+
latest.current.pending = next;
|
|
114
|
+
setInternalState(next);
|
|
115
|
+
}
|
|
116
|
+
latest.current.onStateChange?.(next);
|
|
117
|
+
}, [controlled]);
|
|
118
|
+
const current = () => latest.current.pending ?? latest.current.state;
|
|
119
|
+
const [internalSizing, setInternalSizing] = useState({});
|
|
120
|
+
const tableState = useMemo(
|
|
121
|
+
() => ({
|
|
122
|
+
...state,
|
|
123
|
+
globalFilter: state.search,
|
|
124
|
+
columnPinning: toTanstackPinning(state.columnPinning),
|
|
125
|
+
columnSizing: state.columnSizing ?? internalSizing
|
|
126
|
+
}),
|
|
127
|
+
[state, internalSizing]
|
|
128
|
+
);
|
|
129
|
+
const setState = useCallback((key, updater) => {
|
|
130
|
+
const previous = current();
|
|
131
|
+
const value = resolve(updater, previous[key]);
|
|
132
|
+
if (Object.is(value, previous[key])) return;
|
|
133
|
+
emit({ ...previous, [key]: value });
|
|
134
|
+
}, [emit]);
|
|
135
|
+
const replaceState = useCallback((next) => {
|
|
136
|
+
emit({ ...current(), ...next });
|
|
137
|
+
}, [emit]);
|
|
138
|
+
const tanstackColumns = useMemo(() => toTanstackColumns(columns), [columns]);
|
|
139
|
+
const table = useReactTable({
|
|
140
|
+
...tableOptions,
|
|
141
|
+
data: rows,
|
|
142
|
+
columns: tanstackColumns,
|
|
143
|
+
state: tableState,
|
|
144
|
+
/* Client-side paging counts the rows that pass the filters; a server total would be wrong. */
|
|
145
|
+
rowCount: manualPagination ? rowCount : void 0,
|
|
146
|
+
manualPagination,
|
|
147
|
+
manualSorting,
|
|
148
|
+
manualFiltering,
|
|
149
|
+
enableRowSelection,
|
|
150
|
+
enableColumnResizing,
|
|
151
|
+
columnResizeMode: "onChange",
|
|
152
|
+
getRowId,
|
|
153
|
+
getRowCanExpand,
|
|
154
|
+
getCoreRowModel: getCoreRowModel(),
|
|
155
|
+
getSortedRowModel: manualSorting ? void 0 : getSortedRowModel(),
|
|
156
|
+
getFilteredRowModel: manualFiltering ? void 0 : getFilteredRowModel(),
|
|
157
|
+
getPaginationRowModel: manualPagination ? void 0 : getPaginationRowModel(),
|
|
158
|
+
onPaginationChange: (updater) => setState("pagination", updater),
|
|
159
|
+
/* A new sort or filter starts from the first page: the old page index has
|
|
160
|
+
no meaning against a different ordering. */
|
|
161
|
+
onSortingChange: (updater) => {
|
|
162
|
+
const previous = current();
|
|
163
|
+
const sorting = resolve(updater, previous.sorting);
|
|
164
|
+
emit({ ...previous, sorting, pagination: { ...previous.pagination, pageIndex: 0 }, allMatching: false });
|
|
165
|
+
},
|
|
166
|
+
onColumnFiltersChange: (updater) => {
|
|
167
|
+
const previous = current();
|
|
168
|
+
const columnFilters = resolve(updater, previous.columnFilters);
|
|
169
|
+
emit({ ...previous, columnFilters, pagination: { ...previous.pagination, pageIndex: 0 }, allMatching: false });
|
|
170
|
+
},
|
|
171
|
+
onGlobalFilterChange: (updater) => {
|
|
172
|
+
const previous = current();
|
|
173
|
+
const search = resolve(updater, previous.search) ?? "";
|
|
174
|
+
if (search === previous.search) return;
|
|
175
|
+
emit({ ...previous, search, pagination: { ...previous.pagination, pageIndex: 0 }, allMatching: false });
|
|
176
|
+
},
|
|
177
|
+
onColumnVisibilityChange: (updater) => setState("columnVisibility", updater),
|
|
178
|
+
onColumnOrderChange: (updater) => setState("columnOrder", updater),
|
|
179
|
+
onColumnPinningChange: (updater) => {
|
|
180
|
+
const previous = current();
|
|
181
|
+
const pinning = resolve(updater, toTanstackPinning(previous.columnPinning));
|
|
182
|
+
setState("columnPinning", fromTanstackPinning(pinning));
|
|
183
|
+
},
|
|
184
|
+
/* "Select all N" describes the query's whole result set; any change to
|
|
185
|
+
the explicit selection (or to the query, above) is narrower than that. */
|
|
186
|
+
onRowSelectionChange: (updater) => {
|
|
187
|
+
const previous = current();
|
|
188
|
+
const rowSelection = resolve(updater, previous.rowSelection);
|
|
189
|
+
if (Object.is(rowSelection, previous.rowSelection)) return;
|
|
190
|
+
emit({ ...previous, rowSelection, allMatching: false });
|
|
191
|
+
},
|
|
192
|
+
onExpandedChange: (updater) => setState("expanded", updater),
|
|
193
|
+
onColumnSizingChange: (updater) => {
|
|
194
|
+
const previous = current();
|
|
195
|
+
if (previous.columnSizing === void 0) return setInternalSizing(updater);
|
|
196
|
+
const columnSizing = resolve(updater, previous.columnSizing);
|
|
197
|
+
if (columnSizing !== previous.columnSizing) emit({ ...previous, columnSizing });
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
return { table, state, setState, replaceState };
|
|
201
|
+
}
|
|
202
|
+
export {
|
|
203
|
+
useDataGrid as default,
|
|
204
|
+
normalizeColumnState,
|
|
205
|
+
useDataGrid
|
|
206
|
+
};
|