@superdangerous/app-framework 4.14.0 → 4.15.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/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware/validation.d.ts +12 -12
- package/dist/services/emailService.d.ts +146 -0
- package/dist/services/emailService.d.ts.map +1 -0
- package/dist/services/emailService.js +649 -0
- package/dist/services/emailService.js.map +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +2 -0
- package/dist/services/index.js.map +1 -1
- package/package.json +9 -1
- package/src/index.ts +14 -0
- package/src/services/emailService.ts +812 -0
- package/src/services/index.ts +14 -0
- package/ui/data-table/components/BatchActionsBar.tsx +53 -0
- package/ui/data-table/components/ColumnVisibility.tsx +111 -0
- package/ui/data-table/components/DataTable.tsx +492 -0
- package/ui/data-table/components/DataTablePage.tsx +238 -0
- package/ui/data-table/components/Pagination.tsx +203 -0
- package/ui/data-table/components/PaginationControls.tsx +122 -0
- package/ui/data-table/components/TableFilters.tsx +139 -0
- package/ui/data-table/components/index.ts +41 -0
- package/ui/data-table/components/types.ts +181 -0
- package/ui/data-table/hooks/index.ts +17 -0
- package/ui/data-table/hooks/useColumnOrder.ts +233 -0
- package/ui/data-table/hooks/useColumnVisibility.ts +128 -0
- package/ui/data-table/hooks/usePagination.ts +160 -0
- package/ui/data-table/hooks/useResizableColumns.ts +280 -0
- package/ui/data-table/index.ts +84 -0
- package/ui/dist/index.d.mts +207 -5
- package/ui/dist/index.d.ts +207 -5
- package/ui/dist/index.js +36 -43
- package/ui/dist/index.js.map +1 -1
- package/ui/dist/index.mjs +36 -43
- package/ui/dist/index.mjs.map +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { useState, useMemo, useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
interface UsePaginationOptions<T> {
|
|
4
|
+
data: T[];
|
|
5
|
+
pageSize?: number;
|
|
6
|
+
storageKey?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface PaginationState {
|
|
10
|
+
page: number;
|
|
11
|
+
pageSize: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface UsePaginationResult<T> {
|
|
15
|
+
// Paginated data
|
|
16
|
+
paginatedData: T[];
|
|
17
|
+
|
|
18
|
+
// Current state
|
|
19
|
+
page: number;
|
|
20
|
+
pageSize: number;
|
|
21
|
+
totalPages: number;
|
|
22
|
+
totalItems: number;
|
|
23
|
+
|
|
24
|
+
// Navigation
|
|
25
|
+
setPage: (page: number) => void;
|
|
26
|
+
setPageSize: (size: number) => void;
|
|
27
|
+
nextPage: () => void;
|
|
28
|
+
prevPage: () => void;
|
|
29
|
+
firstPage: () => void;
|
|
30
|
+
lastPage: () => void;
|
|
31
|
+
|
|
32
|
+
// Info
|
|
33
|
+
startIndex: number;
|
|
34
|
+
endIndex: number;
|
|
35
|
+
canGoNext: boolean;
|
|
36
|
+
canGoPrev: boolean;
|
|
37
|
+
|
|
38
|
+
// Page size options
|
|
39
|
+
pageSizeOptions: number[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const DEFAULT_PAGE_SIZE = 25;
|
|
43
|
+
const PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
|
|
44
|
+
|
|
45
|
+
export function usePagination<T>({
|
|
46
|
+
data,
|
|
47
|
+
pageSize: initialPageSize = DEFAULT_PAGE_SIZE,
|
|
48
|
+
storageKey,
|
|
49
|
+
}: UsePaginationOptions<T>): UsePaginationResult<T> {
|
|
50
|
+
// Initialize state from localStorage if available
|
|
51
|
+
const [state, setState] = useState<PaginationState>(() => {
|
|
52
|
+
if (storageKey) {
|
|
53
|
+
try {
|
|
54
|
+
const stored = localStorage.getItem(`pagination-${storageKey}`);
|
|
55
|
+
if (stored) {
|
|
56
|
+
const parsed = JSON.parse(stored);
|
|
57
|
+
return {
|
|
58
|
+
page: 1, // Always start on first page
|
|
59
|
+
pageSize: parsed.pageSize || initialPageSize,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore localStorage errors
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
page: 1,
|
|
68
|
+
pageSize: initialPageSize,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const { page, pageSize } = state;
|
|
73
|
+
|
|
74
|
+
// Calculate total pages
|
|
75
|
+
const totalPages = useMemo(
|
|
76
|
+
() => Math.max(1, Math.ceil(data.length / pageSize)),
|
|
77
|
+
[data.length, pageSize]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Reset to first page if current page is out of bounds
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (page > totalPages) {
|
|
83
|
+
setState((prev) => ({ ...prev, page: Math.max(1, totalPages) }));
|
|
84
|
+
}
|
|
85
|
+
}, [totalPages, page]);
|
|
86
|
+
|
|
87
|
+
// Persist pageSize to localStorage
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (storageKey) {
|
|
90
|
+
try {
|
|
91
|
+
localStorage.setItem(
|
|
92
|
+
`pagination-${storageKey}`,
|
|
93
|
+
JSON.stringify({ pageSize })
|
|
94
|
+
);
|
|
95
|
+
} catch {
|
|
96
|
+
// Ignore localStorage errors
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}, [pageSize, storageKey]);
|
|
100
|
+
|
|
101
|
+
// Calculate start and end indices
|
|
102
|
+
const startIndex = (page - 1) * pageSize;
|
|
103
|
+
const endIndex = Math.min(startIndex + pageSize, data.length);
|
|
104
|
+
|
|
105
|
+
// Get paginated data
|
|
106
|
+
const paginatedData = useMemo(
|
|
107
|
+
() => data.slice(startIndex, endIndex),
|
|
108
|
+
[data, startIndex, endIndex]
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Navigation functions
|
|
112
|
+
const setPage = useCallback((newPage: number) => {
|
|
113
|
+
setState((prev) => ({
|
|
114
|
+
...prev,
|
|
115
|
+
page: Math.max(1, Math.min(newPage, totalPages)),
|
|
116
|
+
}));
|
|
117
|
+
}, [totalPages]);
|
|
118
|
+
|
|
119
|
+
const setPageSize = useCallback((newSize: number) => {
|
|
120
|
+
setState({
|
|
121
|
+
page: 1, // Reset to first page when changing page size
|
|
122
|
+
pageSize: newSize,
|
|
123
|
+
});
|
|
124
|
+
}, []);
|
|
125
|
+
|
|
126
|
+
const nextPage = useCallback(() => {
|
|
127
|
+
setPage(page + 1);
|
|
128
|
+
}, [page, setPage]);
|
|
129
|
+
|
|
130
|
+
const prevPage = useCallback(() => {
|
|
131
|
+
setPage(page - 1);
|
|
132
|
+
}, [page, setPage]);
|
|
133
|
+
|
|
134
|
+
const firstPage = useCallback(() => {
|
|
135
|
+
setPage(1);
|
|
136
|
+
}, [setPage]);
|
|
137
|
+
|
|
138
|
+
const lastPage = useCallback(() => {
|
|
139
|
+
setPage(totalPages);
|
|
140
|
+
}, [totalPages, setPage]);
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
paginatedData,
|
|
144
|
+
page,
|
|
145
|
+
pageSize,
|
|
146
|
+
totalPages,
|
|
147
|
+
totalItems: data.length,
|
|
148
|
+
setPage,
|
|
149
|
+
setPageSize,
|
|
150
|
+
nextPage,
|
|
151
|
+
prevPage,
|
|
152
|
+
firstPage,
|
|
153
|
+
lastPage,
|
|
154
|
+
startIndex: startIndex + 1, // 1-indexed for display
|
|
155
|
+
endIndex,
|
|
156
|
+
canGoNext: page < totalPages,
|
|
157
|
+
canGoPrev: page > 1,
|
|
158
|
+
pageSizeOptions: PAGE_SIZE_OPTIONS,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ColumnConfig {
|
|
4
|
+
key: string;
|
|
5
|
+
minWidth?: number;
|
|
6
|
+
maxWidth?: number;
|
|
7
|
+
defaultWidth?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface UseResizableColumnsOptions {
|
|
11
|
+
tableId: string;
|
|
12
|
+
columns: ColumnConfig[];
|
|
13
|
+
storageKey?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ResizableColumnState {
|
|
17
|
+
widths: Record<string, number>;
|
|
18
|
+
isResizing: boolean;
|
|
19
|
+
resizingColumn: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ResizableColumnResult {
|
|
23
|
+
widths: Record<string, number>;
|
|
24
|
+
isResizing: boolean;
|
|
25
|
+
totalWidth: number;
|
|
26
|
+
getResizeHandleProps: (columnKey: string) => {
|
|
27
|
+
onPointerDown: (e: React.PointerEvent) => void;
|
|
28
|
+
onMouseDown: (e: React.MouseEvent) => void;
|
|
29
|
+
draggable: boolean;
|
|
30
|
+
onDragStart: (e: React.DragEvent) => void;
|
|
31
|
+
className: string;
|
|
32
|
+
'data-resizing': boolean;
|
|
33
|
+
};
|
|
34
|
+
getColumnStyle: (columnKey: string) => React.CSSProperties;
|
|
35
|
+
getTableStyle: () => React.CSSProperties;
|
|
36
|
+
resetToDefaults: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DEFAULT_MIN_WIDTH = 50;
|
|
40
|
+
const DEFAULT_WIDTH = 150;
|
|
41
|
+
const DRAG_THRESHOLD = 3; // Minimum pixels before resize activates
|
|
42
|
+
|
|
43
|
+
export function useResizableColumns({
|
|
44
|
+
tableId,
|
|
45
|
+
columns,
|
|
46
|
+
storageKey,
|
|
47
|
+
}: UseResizableColumnsOptions): ResizableColumnResult {
|
|
48
|
+
const effectiveStorageKey = storageKey || `table-columns-${tableId}`;
|
|
49
|
+
|
|
50
|
+
// Get default widths from column config
|
|
51
|
+
const getDefaultWidths = useCallback(() => {
|
|
52
|
+
return columns.reduce((acc, col) => {
|
|
53
|
+
acc[col.key] = col.defaultWidth || DEFAULT_WIDTH;
|
|
54
|
+
return acc;
|
|
55
|
+
}, {} as Record<string, number>);
|
|
56
|
+
}, [columns]);
|
|
57
|
+
|
|
58
|
+
// Initialize widths from localStorage or defaults
|
|
59
|
+
const [state, setState] = useState<ResizableColumnState>(() => {
|
|
60
|
+
try {
|
|
61
|
+
const stored = localStorage.getItem(effectiveStorageKey);
|
|
62
|
+
if (stored) {
|
|
63
|
+
const parsed = JSON.parse(stored);
|
|
64
|
+
// Merge with defaults in case new columns were added
|
|
65
|
+
const defaults = getDefaultWidths();
|
|
66
|
+
return {
|
|
67
|
+
widths: { ...defaults, ...parsed },
|
|
68
|
+
isResizing: false,
|
|
69
|
+
resizingColumn: null,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
// Ignore localStorage errors
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
widths: getDefaultWidths(),
|
|
77
|
+
isResizing: false,
|
|
78
|
+
resizingColumn: null,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Refs for resize handling - these are stable across renders
|
|
83
|
+
const startXRef = useRef<number>(0);
|
|
84
|
+
const startWidthRef = useRef<number>(0);
|
|
85
|
+
const activeColumnRef = useRef<string | null>(null);
|
|
86
|
+
const hasDraggedRef = useRef<boolean>(false);
|
|
87
|
+
const columnsRef = useRef(columns);
|
|
88
|
+
|
|
89
|
+
// Keep columnsRef up to date
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
columnsRef.current = columns;
|
|
92
|
+
}, [columns]);
|
|
93
|
+
|
|
94
|
+
// Get column config by key - uses ref for stable reference
|
|
95
|
+
const getColumnConfig = useCallback(
|
|
96
|
+
(key: string) => columnsRef.current.find((c) => c.key === key),
|
|
97
|
+
[]
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Persist widths to localStorage
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
try {
|
|
103
|
+
localStorage.setItem(effectiveStorageKey, JSON.stringify(state.widths));
|
|
104
|
+
} catch {
|
|
105
|
+
// Ignore localStorage errors
|
|
106
|
+
}
|
|
107
|
+
}, [state.widths, effectiveStorageKey]);
|
|
108
|
+
|
|
109
|
+
// Stable event handlers using refs - these don't change between renders
|
|
110
|
+
const handlersRef = useRef<{
|
|
111
|
+
onPointerMove: (e: PointerEvent) => void;
|
|
112
|
+
onPointerUp: (e: PointerEvent) => void;
|
|
113
|
+
} | null>(null);
|
|
114
|
+
|
|
115
|
+
// Initialize handlers once
|
|
116
|
+
if (!handlersRef.current) {
|
|
117
|
+
handlersRef.current = {
|
|
118
|
+
onPointerMove: (e: PointerEvent) => {
|
|
119
|
+
if (!activeColumnRef.current) return;
|
|
120
|
+
|
|
121
|
+
const diff = e.clientX - startXRef.current;
|
|
122
|
+
|
|
123
|
+
// Check if we've exceeded the drag threshold
|
|
124
|
+
if (!hasDraggedRef.current) {
|
|
125
|
+
if (Math.abs(diff) < DRAG_THRESHOLD) {
|
|
126
|
+
return; // Not yet dragging
|
|
127
|
+
}
|
|
128
|
+
// Now we're actually dragging - set the resizing state
|
|
129
|
+
hasDraggedRef.current = true;
|
|
130
|
+
setState((prev) => ({
|
|
131
|
+
...prev,
|
|
132
|
+
isResizing: true,
|
|
133
|
+
resizingColumn: activeColumnRef.current,
|
|
134
|
+
}));
|
|
135
|
+
document.body.style.cursor = 'col-resize';
|
|
136
|
+
document.body.style.userSelect = 'none';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const config = getColumnConfig(activeColumnRef.current);
|
|
140
|
+
const minWidth = config?.minWidth || DEFAULT_MIN_WIDTH;
|
|
141
|
+
const maxWidth = config?.maxWidth;
|
|
142
|
+
|
|
143
|
+
let newWidth = Math.max(minWidth, startWidthRef.current + diff);
|
|
144
|
+
if (maxWidth) {
|
|
145
|
+
newWidth = Math.min(maxWidth, newWidth);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
setState((prev) => ({
|
|
149
|
+
...prev,
|
|
150
|
+
widths: {
|
|
151
|
+
...prev.widths,
|
|
152
|
+
[activeColumnRef.current!]: newWidth,
|
|
153
|
+
},
|
|
154
|
+
}));
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
onPointerUp: () => {
|
|
158
|
+
activeColumnRef.current = null;
|
|
159
|
+
hasDraggedRef.current = false;
|
|
160
|
+
setState((prev) => ({
|
|
161
|
+
...prev,
|
|
162
|
+
isResizing: false,
|
|
163
|
+
resizingColumn: null,
|
|
164
|
+
}));
|
|
165
|
+
|
|
166
|
+
// Remove listeners using the same stable references
|
|
167
|
+
document.removeEventListener('pointermove', handlersRef.current!.onPointerMove);
|
|
168
|
+
document.removeEventListener('pointerup', handlersRef.current!.onPointerUp);
|
|
169
|
+
document.removeEventListener('pointercancel', handlersRef.current!.onPointerUp);
|
|
170
|
+
|
|
171
|
+
// Remove cursor override
|
|
172
|
+
document.body.style.cursor = '';
|
|
173
|
+
document.body.style.userSelect = '';
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Cleanup on unmount
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
return () => {
|
|
181
|
+
if (handlersRef.current) {
|
|
182
|
+
document.removeEventListener('pointermove', handlersRef.current.onPointerMove);
|
|
183
|
+
document.removeEventListener('pointerup', handlersRef.current.onPointerUp);
|
|
184
|
+
document.removeEventListener('pointercancel', handlersRef.current.onPointerUp);
|
|
185
|
+
}
|
|
186
|
+
document.body.style.cursor = '';
|
|
187
|
+
document.body.style.userSelect = '';
|
|
188
|
+
};
|
|
189
|
+
}, []);
|
|
190
|
+
|
|
191
|
+
// Ref to access current widths without causing re-renders
|
|
192
|
+
const widthsRef = useRef(state.widths);
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
widthsRef.current = state.widths;
|
|
195
|
+
}, [state.widths]);
|
|
196
|
+
|
|
197
|
+
// Start resize - uses stable handler refs
|
|
198
|
+
const startResize = useCallback(
|
|
199
|
+
(columnKey: string, clientX: number) => {
|
|
200
|
+
startXRef.current = clientX;
|
|
201
|
+
startWidthRef.current = widthsRef.current[columnKey] || DEFAULT_WIDTH;
|
|
202
|
+
activeColumnRef.current = columnKey;
|
|
203
|
+
hasDraggedRef.current = false;
|
|
204
|
+
|
|
205
|
+
// Add listeners using stable references - use pointer events consistently
|
|
206
|
+
document.addEventListener('pointermove', handlersRef.current!.onPointerMove);
|
|
207
|
+
document.addEventListener('pointerup', handlersRef.current!.onPointerUp);
|
|
208
|
+
document.addEventListener('pointercancel', handlersRef.current!.onPointerUp);
|
|
209
|
+
},
|
|
210
|
+
[]
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// Get resize handle props for a column
|
|
214
|
+
const getResizeHandleProps = useCallback(
|
|
215
|
+
(columnKey: string) => ({
|
|
216
|
+
onPointerDown: (e: React.PointerEvent) => {
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
e.stopPropagation();
|
|
219
|
+
startResize(columnKey, e.clientX);
|
|
220
|
+
},
|
|
221
|
+
// Prevent other events from interfering
|
|
222
|
+
onMouseDown: (e: React.MouseEvent) => {
|
|
223
|
+
e.preventDefault();
|
|
224
|
+
e.stopPropagation();
|
|
225
|
+
},
|
|
226
|
+
draggable: false,
|
|
227
|
+
onDragStart: (e: React.DragEvent) => {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
e.stopPropagation();
|
|
230
|
+
},
|
|
231
|
+
className: 'resize-handle',
|
|
232
|
+
'data-resizing': state.resizingColumn === columnKey,
|
|
233
|
+
}),
|
|
234
|
+
[startResize, state.resizingColumn]
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// Get column style with width
|
|
238
|
+
const getColumnStyle = useCallback(
|
|
239
|
+
(columnKey: string): React.CSSProperties => {
|
|
240
|
+
const currentWidth = state.widths[columnKey] || DEFAULT_WIDTH;
|
|
241
|
+
return {
|
|
242
|
+
width: currentWidth,
|
|
243
|
+
minWidth: currentWidth,
|
|
244
|
+
position: 'relative',
|
|
245
|
+
};
|
|
246
|
+
},
|
|
247
|
+
[state.widths]
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// Calculate total table width from all columns
|
|
251
|
+
const totalWidth = columns.reduce((sum, col) => {
|
|
252
|
+
return sum + (state.widths[col.key] || col.defaultWidth || DEFAULT_WIDTH);
|
|
253
|
+
}, 0);
|
|
254
|
+
|
|
255
|
+
// Get table style
|
|
256
|
+
const getTableStyle = useCallback(
|
|
257
|
+
(): React.CSSProperties => ({
|
|
258
|
+
minWidth: totalWidth,
|
|
259
|
+
}),
|
|
260
|
+
[totalWidth]
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
// Reset all columns to default widths
|
|
264
|
+
const resetToDefaults = useCallback(() => {
|
|
265
|
+
setState((prev) => ({
|
|
266
|
+
...prev,
|
|
267
|
+
widths: getDefaultWidths(),
|
|
268
|
+
}));
|
|
269
|
+
}, [getDefaultWidths]);
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
widths: state.widths,
|
|
273
|
+
isResizing: state.isResizing,
|
|
274
|
+
totalWidth,
|
|
275
|
+
getResizeHandleProps,
|
|
276
|
+
getColumnStyle,
|
|
277
|
+
getTableStyle,
|
|
278
|
+
resetToDefaults,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @superdangerous/app-framework/data-table
|
|
3
|
+
*
|
|
4
|
+
* A comprehensive data table module providing hooks and components
|
|
5
|
+
* for building enterprise-grade data tables with:
|
|
6
|
+
*
|
|
7
|
+
* - Pagination with localStorage persistence
|
|
8
|
+
* - Column visibility toggling with locked columns
|
|
9
|
+
* - Column resizing with drag handles
|
|
10
|
+
* - Column reordering with drag-and-drop
|
|
11
|
+
* - Batch selection and actions
|
|
12
|
+
* - Search and filter controls
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import {
|
|
17
|
+
* usePagination,
|
|
18
|
+
* useColumnVisibility,
|
|
19
|
+
* DataTablePage,
|
|
20
|
+
* BatchActionsBar
|
|
21
|
+
* } from '@superdangerous/app-framework/data-table';
|
|
22
|
+
*
|
|
23
|
+
* function MyTable() {
|
|
24
|
+
* const pagination = usePagination({ data: items, storageKey: 'my-table' });
|
|
25
|
+
* const columnVisibility = useColumnVisibility({ columns, storageKey: 'my-table-cols' });
|
|
26
|
+
*
|
|
27
|
+
* return (
|
|
28
|
+
* <DataTablePage
|
|
29
|
+
* title="Items"
|
|
30
|
+
* pagination={pagination}
|
|
31
|
+
* // ...
|
|
32
|
+
* >
|
|
33
|
+
* <table>...</table>
|
|
34
|
+
* </DataTablePage>
|
|
35
|
+
* );
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
// Hooks
|
|
41
|
+
export {
|
|
42
|
+
usePagination,
|
|
43
|
+
useColumnVisibility,
|
|
44
|
+
useResizableColumns,
|
|
45
|
+
useColumnOrder,
|
|
46
|
+
useColumnDragDrop,
|
|
47
|
+
} from './hooks';
|
|
48
|
+
|
|
49
|
+
export type {
|
|
50
|
+
ColumnConfig,
|
|
51
|
+
ColumnVisibilityState,
|
|
52
|
+
ResizableColumnResult,
|
|
53
|
+
ColumnOrderConfig,
|
|
54
|
+
DragState,
|
|
55
|
+
} from './hooks';
|
|
56
|
+
|
|
57
|
+
// Components
|
|
58
|
+
export {
|
|
59
|
+
DataTable,
|
|
60
|
+
DataTablePage,
|
|
61
|
+
PaginationControls,
|
|
62
|
+
Pagination,
|
|
63
|
+
BatchActionsBar,
|
|
64
|
+
ColumnVisibility,
|
|
65
|
+
TableFilters,
|
|
66
|
+
} from './components';
|
|
67
|
+
|
|
68
|
+
export type {
|
|
69
|
+
DataTableProps,
|
|
70
|
+
ColumnDef,
|
|
71
|
+
ColumnWidth,
|
|
72
|
+
ColumnVisibilityConfig,
|
|
73
|
+
HeaderCellProps,
|
|
74
|
+
CellProps,
|
|
75
|
+
ExternalPaginationState,
|
|
76
|
+
ColumnConfigCompat,
|
|
77
|
+
ColumnSizeConfig,
|
|
78
|
+
DataTablePageProps,
|
|
79
|
+
FilterOption,
|
|
80
|
+
PaginationControlsProps,
|
|
81
|
+
BatchActionsBarProps,
|
|
82
|
+
TableFiltersProps,
|
|
83
|
+
TableFilterOption,
|
|
84
|
+
} from './components';
|