@reactorui/datagrid 1.0.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +778 -0
  3. package/dist/components/DataGrid/DataGrid.d.ts +5 -0
  4. package/dist/components/DataGrid/DataGrid.d.ts.map +1 -0
  5. package/dist/components/DataGrid/DataGrid.js +87 -0
  6. package/dist/components/DataGrid/index.d.ts +3 -0
  7. package/dist/components/DataGrid/index.d.ts.map +1 -0
  8. package/dist/components/DataGrid/index.js +1 -0
  9. package/dist/components/Filter/FilterControls.d.ts +11 -0
  10. package/dist/components/Filter/FilterControls.d.ts.map +1 -0
  11. package/dist/components/Filter/FilterControls.js +78 -0
  12. package/dist/components/Filter/index.d.ts +2 -0
  13. package/dist/components/Filter/index.d.ts.map +1 -0
  14. package/dist/components/Filter/index.js +1 -0
  15. package/dist/components/Pagination/Pagination.d.ts +17 -0
  16. package/dist/components/Pagination/Pagination.d.ts.map +1 -0
  17. package/dist/components/Pagination/Pagination.js +12 -0
  18. package/dist/components/Pagination/index.d.ts +2 -0
  19. package/dist/components/Pagination/index.d.ts.map +1 -0
  20. package/dist/components/Pagination/index.js +1 -0
  21. package/dist/components/Search/SearchInput.d.ts +11 -0
  22. package/dist/components/Search/SearchInput.d.ts.map +1 -0
  23. package/dist/components/Search/SearchInput.js +9 -0
  24. package/dist/components/Search/index.d.ts +2 -0
  25. package/dist/components/Search/index.d.ts.map +1 -0
  26. package/dist/components/Search/index.js +1 -0
  27. package/dist/components/Table/TableBody.d.ts +20 -0
  28. package/dist/components/Table/TableBody.d.ts.map +1 -0
  29. package/dist/components/Table/TableBody.js +56 -0
  30. package/dist/components/Table/TableHeader.d.ts +13 -0
  31. package/dist/components/Table/TableHeader.d.ts.map +1 -0
  32. package/dist/components/Table/TableHeader.js +24 -0
  33. package/dist/components/Table/index.d.ts +3 -0
  34. package/dist/components/Table/index.d.ts.map +1 -0
  35. package/dist/components/Table/index.js +2 -0
  36. package/dist/hooks/index.d.ts +2 -0
  37. package/dist/hooks/index.d.ts.map +1 -0
  38. package/dist/hooks/index.js +1 -0
  39. package/dist/hooks/useDataGrid.d.ts +49 -0
  40. package/dist/hooks/useDataGrid.d.ts.map +1 -0
  41. package/dist/hooks/useDataGrid.js +356 -0
  42. package/dist/index.d.ts +7 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +8 -0
  45. package/dist/setupTests.d.ts +12 -0
  46. package/dist/setupTests.d.ts.map +1 -0
  47. package/dist/setupTests.js +1 -0
  48. package/dist/themes/index.d.ts +22 -0
  49. package/dist/themes/index.d.ts.map +1 -0
  50. package/dist/themes/index.js +31 -0
  51. package/dist/types/index.d.ts +108 -0
  52. package/dist/types/index.d.ts.map +1 -0
  53. package/dist/types/index.js +1 -0
  54. package/dist/utils/index.d.ts +12 -0
  55. package/dist/utils/index.d.ts.map +1 -0
  56. package/dist/utils/index.js +209 -0
  57. package/package.json +80 -0
@@ -0,0 +1,356 @@
1
+ import { useState, useEffect, useCallback, useMemo } from 'react';
2
+ import { createApiRequest, compareValues, sortData } from '../utils';
3
+ export const useDataGrid = ({ data: staticData, endpoint, httpConfig, pageSize = 10, serverPageSize = 100, onDataLoad, onDataError, onLoadingStateChange, onPageChange, onPageSizeChange, onSortChange, onFilterChange, onSearchChange, }) => {
4
+ // Core state
5
+ const [loading, setLoading] = useState(false);
6
+ const [error, setError] = useState(null);
7
+ const [serverData, setServerData] = useState([]);
8
+ const [lastServerResponse, setLastServerResponse] = useState(null);
9
+ // UI state
10
+ const [searchTerm, setSearchTerm] = useState('');
11
+ const [activeFilters, setActiveFilters] = useState([]);
12
+ const [sortConfig, setSortConfig] = useState({ column: '', direction: 'asc' });
13
+ const [selectedRows, setSelectedRows] = useState(new Set());
14
+ // Pagination state (simplified)
15
+ const [currentPage, setCurrentPage] = useState(1);
16
+ const [currentPageSize, setCurrentPageSize] = useState(pageSize);
17
+ const [continuationToken, setContinuationToken] = useState();
18
+ const [tokenHistory, setTokenHistory] = useState([]);
19
+ // Determine data source
20
+ const sourceData = staticData || serverData;
21
+ // Internal loading state handler
22
+ const handleLoadingChange = useCallback((newLoading, context) => {
23
+ setLoading(newLoading);
24
+ onLoadingStateChange?.(newLoading, context);
25
+ }, [onLoadingStateChange]);
26
+ // Internal error handler
27
+ const handleError = useCallback((err, context) => {
28
+ const errorMessage = err.message || 'An unknown error occurred';
29
+ setError(errorMessage);
30
+ onDataError?.(err, context);
31
+ }, [onDataError]);
32
+ // Process data (search, filter, sort) - sorting is ALWAYS client-side
33
+ const processedData = useMemo(() => {
34
+ let processed = [...sourceData];
35
+ // Apply search (client-side for static data)
36
+ if (searchTerm && staticData) {
37
+ processed = processed.filter((row) => Object.values(row).some((value) => value?.toString().toLowerCase().includes(searchTerm.toLowerCase())));
38
+ }
39
+ // Apply filters (client-side for static data)
40
+ if (staticData) {
41
+ activeFilters.forEach((filter) => {
42
+ processed = processed.filter((row) => {
43
+ const value = row[filter.column];
44
+ return compareValues(value, filter.value, filter.operator, filter.dataType);
45
+ });
46
+ });
47
+ }
48
+ // Apply sorting (ALWAYS client-side)
49
+ if (sortConfig.column) {
50
+ processed = sortData(processed, sortConfig.column, sortConfig.direction);
51
+ }
52
+ return processed;
53
+ }, [sourceData, searchTerm, activeFilters, sortConfig, staticData]);
54
+ // Paginate data - only for client-side data
55
+ const paginatedData = useMemo(() => {
56
+ if (!staticData)
57
+ return sourceData; // For server-side, return as-is
58
+ const start = (currentPage - 1) * currentPageSize;
59
+ const end = start + currentPageSize;
60
+ return processedData.slice(start, end);
61
+ }, [processedData, currentPage, currentPageSize, staticData, sourceData]);
62
+ // Pagination info
63
+ const paginationInfo = useMemo(() => {
64
+ if (!staticData && lastServerResponse) {
65
+ // Server-side pagination info
66
+ const displayedCount = sourceData.length;
67
+ const start = displayedCount === 0 ? 0 : 1;
68
+ const end = displayedCount;
69
+ return {
70
+ currentPage,
71
+ totalPages: 1, // Not meaningful with continuation tokens
72
+ pageSize: currentPageSize,
73
+ totalRecords: lastServerResponse.Count,
74
+ start,
75
+ end,
76
+ hasNext: lastServerResponse.HasMore,
77
+ hasPrevious: tokenHistory.length > 0,
78
+ continuationToken: lastServerResponse.ContinuationToken,
79
+ };
80
+ }
81
+ // Client-side pagination info
82
+ const totalPages = Math.ceil(processedData.length / currentPageSize);
83
+ const start = processedData.length === 0 ? 0 : (currentPage - 1) * currentPageSize + 1;
84
+ const end = Math.min(currentPage * currentPageSize, processedData.length);
85
+ return {
86
+ currentPage,
87
+ totalPages,
88
+ pageSize: currentPageSize,
89
+ totalRecords: processedData.length,
90
+ start,
91
+ end,
92
+ hasNext: currentPage < totalPages,
93
+ hasPrevious: currentPage > 1,
94
+ };
95
+ }, [
96
+ processedData.length,
97
+ currentPage,
98
+ currentPageSize,
99
+ staticData,
100
+ sourceData,
101
+ lastServerResponse,
102
+ tokenHistory.length,
103
+ ]);
104
+ // Load server data
105
+ const loadServerData = useCallback(async (resetPagination = false, navigationDirection) => {
106
+ if (!endpoint || staticData)
107
+ return;
108
+ handleLoadingChange(true, 'data-load');
109
+ setError(null);
110
+ try {
111
+ let requestToken = continuationToken;
112
+ // Handle navigation
113
+ if (resetPagination) {
114
+ requestToken = undefined;
115
+ setContinuationToken(undefined);
116
+ setTokenHistory([]);
117
+ setCurrentPage(1);
118
+ }
119
+ else if (navigationDirection === 'previous' && tokenHistory.length > 0) {
120
+ // Go back to previous token
121
+ const newHistory = [...tokenHistory];
122
+ requestToken = newHistory.pop();
123
+ setTokenHistory(newHistory);
124
+ setContinuationToken(requestToken);
125
+ }
126
+ const request = {
127
+ page: currentPage,
128
+ pageSize: serverPageSize,
129
+ search: searchTerm,
130
+ filters: activeFilters,
131
+ continuationToken: requestToken,
132
+ };
133
+ const response = await createApiRequest(endpoint, request, httpConfig);
134
+ // Handle response
135
+ setServerData(response.Items);
136
+ setLastServerResponse(response);
137
+ // Update token for next navigation
138
+ if (navigationDirection === 'next' && continuationToken) {
139
+ // Save current token to history
140
+ setTokenHistory((prev) => [...prev, continuationToken]);
141
+ }
142
+ if (response.ContinuationToken) {
143
+ setContinuationToken(response.ContinuationToken);
144
+ }
145
+ onDataLoad?.(response);
146
+ }
147
+ catch (err) {
148
+ handleError(err instanceof Error ? err : new Error('Failed to load data'), 'data-load');
149
+ }
150
+ finally {
151
+ handleLoadingChange(false, 'data-load');
152
+ }
153
+ }, [
154
+ endpoint,
155
+ staticData,
156
+ currentPage,
157
+ serverPageSize,
158
+ searchTerm,
159
+ sortConfig,
160
+ activeFilters,
161
+ continuationToken,
162
+ tokenHistory,
163
+ httpConfig,
164
+ onDataLoad,
165
+ handleLoadingChange,
166
+ handleError,
167
+ ]);
168
+ // Load data on mount and when dependencies change (no sorting here - that's client-side)
169
+ useEffect(() => {
170
+ if (!staticData) {
171
+ loadServerData(true); // Reset pagination on mount
172
+ }
173
+ }, [staticData, searchTerm, activeFilters]); // Note: sortConfig removed - sorting is client-side only
174
+ // Actions
175
+ const setSort = useCallback((column) => {
176
+ const newSortConfig = {
177
+ column,
178
+ direction: sortConfig.column === column && sortConfig.direction === 'asc' ? 'desc' : 'asc',
179
+ };
180
+ setSortConfig(newSortConfig);
181
+ onSortChange?.(newSortConfig);
182
+ // No server reload needed - sorting is client-side only
183
+ if (staticData) {
184
+ setCurrentPage(1);
185
+ }
186
+ }, [sortConfig, onSortChange, staticData]);
187
+ const setPage = useCallback((page) => {
188
+ if (staticData) {
189
+ // Client-side pagination
190
+ setCurrentPage(page);
191
+ onPageChange?.(page, {
192
+ ...paginationInfo,
193
+ currentPage: page,
194
+ });
195
+ }
196
+ else {
197
+ // Server-side with continuation tokens doesn't support arbitrary page jumps
198
+ console.warn('Direct page navigation not supported with continuation tokens. Use navigateNext/navigatePrevious instead.');
199
+ }
200
+ }, [staticData, paginationInfo, onPageChange]);
201
+ const navigateNext = useCallback(() => {
202
+ if (!paginationInfo.hasNext)
203
+ return;
204
+ if (!staticData) {
205
+ // Server-side navigation
206
+ loadServerData(false, 'next');
207
+ }
208
+ else {
209
+ // Client-side navigation
210
+ setPage(currentPage + 1);
211
+ }
212
+ }, [paginationInfo.hasNext, staticData, loadServerData, setPage, currentPage]);
213
+ const navigatePrevious = useCallback(() => {
214
+ if (!paginationInfo.hasPrevious)
215
+ return;
216
+ if (!staticData) {
217
+ // Server-side navigation
218
+ loadServerData(false, 'previous');
219
+ }
220
+ else {
221
+ // Client-side navigation
222
+ setPage(currentPage - 1);
223
+ }
224
+ }, [paginationInfo.hasPrevious, staticData, loadServerData, setPage, currentPage]);
225
+ const setPageSize = useCallback((newPageSize) => {
226
+ setCurrentPageSize(newPageSize);
227
+ setCurrentPage(1);
228
+ const newPaginationInfo = {
229
+ ...paginationInfo,
230
+ pageSize: newPageSize,
231
+ currentPage: 1,
232
+ totalPages: staticData ? Math.ceil(processedData.length / newPageSize) : 1,
233
+ };
234
+ onPageSizeChange?.(newPageSize, newPaginationInfo);
235
+ // Reset pagination for server-side
236
+ if (!staticData) {
237
+ setContinuationToken(undefined);
238
+ setTokenHistory([]);
239
+ loadServerData(true);
240
+ }
241
+ }, [paginationInfo, staticData, processedData.length, onPageSizeChange, loadServerData]);
242
+ const updateSearchTerm = useCallback((term) => {
243
+ setSearchTerm(term);
244
+ onSearchChange?.(term);
245
+ // Reset pagination when search changes
246
+ setCurrentPage(1);
247
+ if (!staticData) {
248
+ setContinuationToken(undefined);
249
+ setTokenHistory([]);
250
+ }
251
+ }, [onSearchChange, staticData]);
252
+ const addFilter = useCallback((filter) => {
253
+ const label = `${filter.column} ${filter.operator} "${filter.value}"`;
254
+ const newFilters = [
255
+ ...activeFilters.filter((f) => f.column !== filter.column),
256
+ { ...filter, label },
257
+ ];
258
+ setActiveFilters(newFilters);
259
+ onFilterChange?.(newFilters);
260
+ // Reset pagination when filters change
261
+ setCurrentPage(1);
262
+ if (!staticData) {
263
+ setContinuationToken(undefined);
264
+ setTokenHistory([]);
265
+ }
266
+ }, [activeFilters, onFilterChange, staticData]);
267
+ const removeFilter = useCallback((index) => {
268
+ const newFilters = activeFilters.filter((_, i) => i !== index);
269
+ setActiveFilters(newFilters);
270
+ onFilterChange?.(newFilters);
271
+ // Reset pagination when filters change
272
+ setCurrentPage(1);
273
+ if (!staticData) {
274
+ setContinuationToken(undefined);
275
+ setTokenHistory([]);
276
+ }
277
+ }, [activeFilters, onFilterChange, staticData]);
278
+ const clearFilters = useCallback(() => {
279
+ setActiveFilters([]);
280
+ onFilterChange?.([]);
281
+ // Reset pagination when filters change
282
+ setCurrentPage(1);
283
+ if (!staticData) {
284
+ setContinuationToken(undefined);
285
+ setTokenHistory([]);
286
+ }
287
+ }, [onFilterChange, staticData]);
288
+ const selectRow = useCallback((rowId, selected) => {
289
+ setSelectedRows((prev) => {
290
+ const newSet = new Set(prev);
291
+ if (selected) {
292
+ newSet.add(rowId);
293
+ }
294
+ else {
295
+ newSet.delete(rowId);
296
+ }
297
+ return newSet;
298
+ });
299
+ }, []);
300
+ const selectAll = useCallback((selected) => {
301
+ if (selected) {
302
+ const currentPageData = staticData ? paginatedData : sourceData;
303
+ const allIds = currentPageData.map((row) => String(row.id)).filter(Boolean);
304
+ setSelectedRows(new Set(allIds));
305
+ }
306
+ else {
307
+ setSelectedRows(new Set());
308
+ }
309
+ }, [staticData, paginatedData, sourceData]);
310
+ const refresh = useCallback(() => {
311
+ if (staticData) {
312
+ setSearchTerm('');
313
+ setActiveFilters([]);
314
+ setSortConfig({ column: '', direction: 'asc' });
315
+ setCurrentPage(1);
316
+ }
317
+ else {
318
+ loadServerData(true);
319
+ }
320
+ }, [staticData, loadServerData]);
321
+ return {
322
+ // Data
323
+ data: sourceData,
324
+ processedData,
325
+ paginatedData: staticData ? paginatedData : sourceData,
326
+ loading,
327
+ error,
328
+ // State
329
+ searchTerm,
330
+ activeFilters,
331
+ sortConfig,
332
+ selectedRows,
333
+ currentPage,
334
+ currentPageSize,
335
+ totalRecords: paginationInfo.totalRecords,
336
+ hasMore: paginationInfo.hasNext,
337
+ continuationToken: paginationInfo.continuationToken,
338
+ // Actions
339
+ setSearchTerm: updateSearchTerm,
340
+ setSort,
341
+ setCurrentPage: setPage,
342
+ setCurrentPageSize: setPageSize,
343
+ navigateNext,
344
+ navigatePrevious,
345
+ addFilter,
346
+ removeFilter,
347
+ clearFilters,
348
+ selectRow,
349
+ selectAll,
350
+ refresh,
351
+ // Computed
352
+ paginationInfo,
353
+ selectedData: sourceData.filter((row) => selectedRows.has(String(row.id))),
354
+ hasSelection: selectedRows.size > 0,
355
+ };
356
+ };
@@ -0,0 +1,7 @@
1
+ export { DataGrid } from './components/DataGrid';
2
+ export { useDataGrid } from './hooks';
3
+ export type { BaseRowData, Column, DataGridProps, ActiveFilter, SortConfig, PaginationInfo, ServerRequest, ServerResponse, HttpConfig, OnDataLoadCallback, OnDataErrorCallback, OnLoadingStateChangeCallback, OnPageChangeCallback, OnPageSizeChangeCallback, OnSortChangeCallback, OnFilterChangeCallback, OnSearchChangeCallback, OnTableRowClickCallback, OnTableRowDoubleClickCallback, OnRowSelectCallback, OnSelectionChangeCallback, OnTableRowHoverCallback, OnCellClickCallback, OnTableRefreshCallback, } from './types';
4
+ export { getTheme, themes } from './themes';
5
+ export type { Theme } from './themes';
6
+ export { formatters, compareValues, createApiRequest } from './utils';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGtC,YAAY,EACV,WAAW,EACX,MAAM,EACN,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,aAAa,EACb,cAAc,EACd,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,4BAA4B,EAC5B,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EACzB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAGtC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // Main component
2
+ export { DataGrid } from './components/DataGrid';
3
+ // Hooks
4
+ export { useDataGrid } from './hooks';
5
+ // Themes
6
+ export { getTheme, themes } from './themes';
7
+ // Utilities
8
+ export { formatters, compareValues, createApiRequest } from './utils';
@@ -0,0 +1,12 @@
1
+ import '@testing-library/jest-dom';
2
+ declare global {
3
+ namespace jest {
4
+ interface Matchers<R> {
5
+ toBeInTheDocument(): R;
6
+ toHaveTextContent(text: string): R;
7
+ toBeVisible(): R;
8
+ toBeDisabled(): R;
9
+ }
10
+ }
11
+ }
12
+ //# sourceMappingURL=setupTests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setupTests.d.ts","sourceRoot":"","sources":["../src/setupTests.ts"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAC;AAGnC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,IAAI,CAAC;QACb,UAAU,QAAQ,CAAC,CAAC;YAClB,iBAAiB,IAAI,CAAC,CAAC;YACvB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC;YACnC,WAAW,IAAI,CAAC,CAAC;YACjB,YAAY,IAAI,CAAC,CAAC;SACnB;KACF;CACF"}
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,22 @@
1
+ export interface Theme {
2
+ container: string;
3
+ table: string;
4
+ header: string;
5
+ headerCell: string;
6
+ row: string;
7
+ cell: string;
8
+ selectedRow: string;
9
+ searchInput: string;
10
+ button: string;
11
+ pagination: string;
12
+ }
13
+ export declare const defaultTheme: Theme;
14
+ export declare const stripedTheme: Theme;
15
+ export declare const borderedTheme: Theme;
16
+ export declare const themes: {
17
+ default: Theme;
18
+ striped: Theme;
19
+ bordered: Theme;
20
+ };
21
+ export declare const getTheme: (variant?: keyof typeof themes) => Theme;
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/themes/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,YAAY,EAAE,KAa1B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAG1B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAO3B,CAAC;AAEF,eAAO,MAAM,MAAM;;;;CAIlB,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,UAAS,MAAM,OAAO,MAAkB,KAAG,KAEnE,CAAC"}
@@ -0,0 +1,31 @@
1
+ export const defaultTheme = {
2
+ container: 'bg-white rounded-lg shadow-sm border border-gray-200',
3
+ table: 'w-full',
4
+ header: 'bg-gray-50 border-b border-gray-200',
5
+ headerCell: 'px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider',
6
+ row: 'hover:bg-gray-50 transition-colors duration-150',
7
+ cell: 'px-4 py-3 text-sm text-gray-900',
8
+ selectedRow: 'bg-blue-50 hover:bg-blue-100',
9
+ searchInput: 'px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500',
10
+ button: 'px-3 py-2 bg-blue-600 text-white text-sm rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500',
11
+ pagination: 'flex items-center justify-between px-4 py-3 bg-white border-t border-gray-200',
12
+ };
13
+ export const stripedTheme = {
14
+ ...defaultTheme,
15
+ row: 'odd:bg-gray-50 even:bg-white hover:bg-gray-100 transition-colors duration-150',
16
+ };
17
+ export const borderedTheme = {
18
+ ...defaultTheme,
19
+ container: 'bg-white rounded-lg shadow-sm border border-gray-200',
20
+ table: 'w-full border-collapse',
21
+ cell: 'px-4 py-3 text-sm text-gray-900 border-r border-gray-200 last:border-r-0',
22
+ headerCell: 'px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-r border-gray-200 last:border-r-0',
23
+ };
24
+ export const themes = {
25
+ default: defaultTheme,
26
+ striped: stripedTheme,
27
+ bordered: borderedTheme,
28
+ };
29
+ export const getTheme = (variant = 'default') => {
30
+ return themes[variant] || themes.default;
31
+ };
@@ -0,0 +1,108 @@
1
+ import { ReactNode, HTMLAttributes } from 'react';
2
+ export interface BaseRowData {
3
+ id?: string | number;
4
+ [key: string]: any;
5
+ }
6
+ export interface Column<T = BaseRowData> {
7
+ key: keyof T | string;
8
+ label: string;
9
+ sortable?: boolean;
10
+ filterable?: boolean;
11
+ dataType?: 'string' | 'number' | 'boolean' | 'date' | 'datetime';
12
+ width?: string | number;
13
+ minWidth?: string | number;
14
+ maxWidth?: string | number;
15
+ align?: 'left' | 'center' | 'right';
16
+ render?: (value: any, row: T, index: number) => ReactNode;
17
+ }
18
+ export interface ActiveFilter {
19
+ column: string;
20
+ operator: string;
21
+ value: any;
22
+ dataType: string;
23
+ label: string;
24
+ }
25
+ export interface SortConfig {
26
+ column: string;
27
+ direction: 'asc' | 'desc';
28
+ }
29
+ export interface PaginationInfo {
30
+ currentPage: number;
31
+ totalPages: number;
32
+ pageSize: number;
33
+ totalRecords: number;
34
+ start: number;
35
+ end: number;
36
+ hasNext: boolean;
37
+ hasPrevious: boolean;
38
+ continuationToken?: string;
39
+ }
40
+ export interface ServerRequest {
41
+ page: number;
42
+ pageSize: number;
43
+ search: string;
44
+ filters: ActiveFilter[];
45
+ continuationToken?: string;
46
+ }
47
+ export interface ServerResponse<T = BaseRowData> {
48
+ Items: T[];
49
+ ContinuationToken?: string;
50
+ HasMore: boolean;
51
+ Count: number;
52
+ }
53
+ export interface HttpConfig {
54
+ bearerToken?: string;
55
+ apiKey?: string;
56
+ customHeaders?: Record<string, string>;
57
+ method?: 'GET' | 'POST';
58
+ postDataFormat?: 'form' | 'json';
59
+ withCredentials?: boolean;
60
+ timeout?: number;
61
+ }
62
+ export type OnDataLoadCallback<T = BaseRowData> = (data: ServerResponse<T>) => void;
63
+ export type OnDataErrorCallback = (error: Error, context: string) => void;
64
+ export type OnLoadingStateChangeCallback = (loading: boolean, context: string) => void;
65
+ export type OnPageChangeCallback = (page: number, paginationInfo: PaginationInfo) => void;
66
+ export type OnPageSizeChangeCallback = (pageSize: number, paginationInfo: PaginationInfo) => void;
67
+ export type OnSortChangeCallback = (sortConfig: SortConfig) => void;
68
+ export type OnFilterChangeCallback = (filters: ActiveFilter[]) => void;
69
+ export type OnSearchChangeCallback = (searchTerm: string) => void;
70
+ export type OnTableRowClickCallback<T = BaseRowData> = (row: T, event: React.MouseEvent) => void;
71
+ export type OnTableRowDoubleClickCallback<T = BaseRowData> = (row: T, event: React.MouseEvent) => boolean | void;
72
+ export type OnRowSelectCallback<T = BaseRowData> = (row: T, isSelected: boolean) => void;
73
+ export type OnSelectionChangeCallback<T = BaseRowData> = (selectedRows: T[]) => void;
74
+ export type OnTableRowHoverCallback<T = BaseRowData> = (row: T | null, event: React.MouseEvent) => void;
75
+ export type OnCellClickCallback<T = BaseRowData> = (value: any, row: T, column: Column<T>, event: React.MouseEvent) => void;
76
+ export type OnTableRefreshCallback = () => void;
77
+ export interface DataGridProps<T = BaseRowData> extends Omit<HTMLAttributes<HTMLDivElement>, 'onError'> {
78
+ data?: T[];
79
+ endpoint?: string;
80
+ columns?: Column<T>[];
81
+ enableSearch?: boolean;
82
+ enableSorting?: boolean;
83
+ enableFilters?: boolean;
84
+ enableSelection?: boolean;
85
+ pageSize?: number;
86
+ serverPageSize?: number;
87
+ pageSizeOptions?: number[];
88
+ httpConfig?: HttpConfig;
89
+ className?: string;
90
+ variant?: 'default' | 'striped' | 'bordered';
91
+ size?: 'sm' | 'md' | 'lg';
92
+ onDataLoad?: OnDataLoadCallback<T>;
93
+ onDataError?: OnDataErrorCallback;
94
+ onLoadingStateChange?: OnLoadingStateChangeCallback;
95
+ onPageChange?: OnPageChangeCallback;
96
+ onPageSizeChange?: OnPageSizeChangeCallback;
97
+ onSortChange?: OnSortChangeCallback;
98
+ onFilterChange?: OnFilterChangeCallback;
99
+ onSearchChange?: OnSearchChangeCallback;
100
+ onTableRefresh?: OnTableRefreshCallback;
101
+ onTableRowClick?: OnTableRowClickCallback<T>;
102
+ onTableRowDoubleClick?: OnTableRowDoubleClickCallback<T>;
103
+ onRowSelect?: OnRowSelectCallback<T>;
104
+ onSelectionChange?: OnSelectionChangeCallback<T>;
105
+ onTableRowHover?: OnTableRowHoverCallback<T>;
106
+ onCellClick?: OnCellClickCallback<T>;
107
+ }
108
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import { HttpConfig, ServerRequest, ServerResponse } from '../types';
2
+ export declare const formatters: {
3
+ date: (value: string, includeTime?: boolean) => string;
4
+ currency: (value: number, currency?: string) => string;
5
+ number: (value: number, decimals?: number) => string;
6
+ truncate: (text: string, length: number) => string;
7
+ };
8
+ export declare const compareValues: (dataValue: any, filterValue: any, operator: string, dataType: string) => boolean;
9
+ export declare const createApiRequest: <T>(endpoint: string, request: ServerRequest, config?: HttpConfig) => Promise<ServerResponse<T>>;
10
+ export declare const mapServerResponse: <T>(data: any) => ServerResponse<T>;
11
+ export declare const sortData: <T>(data: T[], sortColumn: string, direction: "asc" | "desc") => T[];
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAGrE,eAAO,MAAM,UAAU;kBACP,MAAM;sBAMF,MAAM;oBAOR,MAAM;qBAOL,MAAM,UAAU,MAAM;CAIxC,CAAC;AAGF,eAAO,MAAM,aAAa,GACxB,WAAW,GAAG,EACd,aAAa,GAAG,EAChB,UAAU,MAAM,EAChB,UAAU,MAAM,KACf,OAkEF,CAAC;AAGF,eAAO,MAAM,gBAAgB,GAAU,CAAC,EACtC,UAAU,MAAM,EAChB,SAAS,aAAa,EACtB,SAAQ,UAAe,KACtB,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CA2D3B,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,MAAM,GAAG,KAAG,cAAc,CAAC,CAAC,CA6DhE,CAAC;AAGF,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,YAAY,MAAM,EAAE,WAAW,KAAK,GAAG,MAAM,KAAG,CAAC,EAiBvF,CAAC"}