@reactorui/datagrid 1.0.20 → 1.0.21
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 +273 -556
- package/dist/components/DataGrid/DataGrid.d.ts +1 -1
- package/dist/components/DataGrid/DataGrid.d.ts.map +1 -1
- package/dist/components/DataGrid/DataGrid.js +80 -47
- package/dist/components/Filter/FilterControls.d.ts +6 -2
- package/dist/components/Filter/FilterControls.d.ts.map +1 -1
- package/dist/components/Filter/FilterControls.js +114 -62
- package/dist/components/Table/TableHeader.d.ts +2 -1
- package/dist/components/Table/TableHeader.d.ts.map +1 -1
- package/dist/components/Table/TableHeader.js +4 -2
- package/dist/hooks/useDataGrid.d.ts +16 -18
- package/dist/hooks/useDataGrid.d.ts.map +1 -1
- package/dist/hooks/useDataGrid.js +139 -299
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/types/index.d.ts +76 -38
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/utils/index.d.ts +14 -5
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +166 -156
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DataGridProps } from '../../types';
|
|
2
2
|
export declare const DataGrid: <T extends {
|
|
3
3
|
[key: string]: any;
|
|
4
|
-
} = any>({ data,
|
|
4
|
+
} = any>({ data, columns: columnsProp, loading: simpleLoading, loadingState: externalLoadingState, totalRecords: externalTotalRecords, error: externalError, currentPage: externalCurrentPage, enableSearch, enableSorting, enableFilters, enableSelection, enableDelete, enableRefresh, deleteConfirmation, maxHeight, stickyHeader, pageSize, pageSizeOptions, variant, size, className, onPageChange, onPageSizeChange, onSortChange, onSearchChange, onApplyFilter, onRemoveFilter, onClearFilters, onFilterChange, onTableRowClick, onTableRowDoubleClick, onRowSelect, onSelectionChange, onTableRowHover, onCellClick, onTableRefresh, onBulkDelete, ...rest }: DataGridProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
//# sourceMappingURL=DataGrid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataGrid.d.ts","sourceRoot":"","sources":["../../../src/components/DataGrid/DataGrid.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DataGrid.d.ts","sourceRoot":"","sources":["../../../src/components/DataGrid/DataGrid.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAwB,MAAM,aAAa,CAAC;AAgClE,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,GAAG,GAAG,EAAE,8nBA+D9D,aAAa,CAAC,CAAC,CAAC,4CAyVlB,CAAC"}
|
|
@@ -1,37 +1,85 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// File: src/components/DataGrid/DataGrid.tsx
|
|
2
3
|
import React, { useMemo, useCallback } from 'react';
|
|
3
4
|
import { useDataGrid } from '../../hooks';
|
|
4
5
|
import { SearchInput } from '../Search';
|
|
5
6
|
import { FilterControls } from '../Filter';
|
|
6
7
|
import { TableHeader, TableBody } from '../Table';
|
|
7
8
|
import { getTheme } from '../../themes';
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
9
|
+
import { inferDataType } from '../../utils';
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Spinner Component (reusable)
|
|
12
|
+
// ============================================================================
|
|
13
|
+
const Spinner = ({ className = 'w-4 h-4' }) => (_jsxs("svg", { className: `animate-spin ${className}`, xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }));
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Main Component
|
|
16
|
+
// ============================================================================
|
|
17
|
+
export const DataGrid = ({
|
|
18
|
+
// Data
|
|
19
|
+
data, columns: columnsProp = [],
|
|
20
|
+
// Loading states
|
|
21
|
+
loading: simpleLoading = false, loadingState: externalLoadingState,
|
|
22
|
+
// External state (controlled mode)
|
|
23
|
+
totalRecords: externalTotalRecords, error: externalError, currentPage: externalCurrentPage,
|
|
24
|
+
// Features
|
|
25
|
+
enableSearch = true, enableSorting = true, enableFilters = true, enableSelection = true, enableDelete = false, enableRefresh = false, deleteConfirmation = false,
|
|
26
|
+
// Layout
|
|
27
|
+
maxHeight, stickyHeader = false,
|
|
28
|
+
// Pagination
|
|
29
|
+
pageSize = 10, pageSizeOptions = [5, 10, 25, 50, 100],
|
|
30
|
+
// Styling
|
|
31
|
+
variant = 'default', size = 'md', className = '',
|
|
32
|
+
// Pagination Events
|
|
33
|
+
onPageChange, onPageSizeChange,
|
|
34
|
+
// Sort & Search Events
|
|
35
|
+
onSortChange, onSearchChange,
|
|
36
|
+
// Filter Events
|
|
37
|
+
onApplyFilter, onRemoveFilter, onClearFilters, onFilterChange,
|
|
38
|
+
// Row & Cell Events
|
|
39
|
+
onTableRowClick, onTableRowDoubleClick, onRowSelect, onSelectionChange, onTableRowHover, onCellClick,
|
|
40
|
+
// Action Events
|
|
41
|
+
onTableRefresh, onBulkDelete, ...rest }) => {
|
|
11
42
|
const theme = getTheme(variant);
|
|
43
|
+
// ===== Normalize Loading State =====
|
|
44
|
+
// If simple `loading` is passed, convert to loadingState.data
|
|
45
|
+
// If loadingState is passed, use it directly
|
|
46
|
+
const loadingState = useMemo(() => {
|
|
47
|
+
if (externalLoadingState)
|
|
48
|
+
return externalLoadingState;
|
|
49
|
+
if (simpleLoading)
|
|
50
|
+
return { data: true };
|
|
51
|
+
return {};
|
|
52
|
+
}, [externalLoadingState, simpleLoading]);
|
|
53
|
+
// Destructure for convenience
|
|
54
|
+
const isDataLoading = loadingState.data ?? false;
|
|
55
|
+
const isFilterLoading = loadingState.filter ?? false;
|
|
56
|
+
const isSearchLoading = loadingState.search ?? false;
|
|
57
|
+
const isRefreshLoading = loadingState.refresh ?? false;
|
|
58
|
+
const isDeleteLoading = loadingState.delete ?? false;
|
|
59
|
+
// Any loading state disables controls
|
|
60
|
+
const isAnyLoading = isDataLoading || isFilterLoading || isSearchLoading || isRefreshLoading || isDeleteLoading;
|
|
12
61
|
// Use the data grid hook
|
|
13
|
-
const {
|
|
14
|
-
} = useDataGrid({
|
|
62
|
+
const { processedData, paginatedData, searchTerm, activeFilters, sortConfig, selectedRows, currentPage, currentPageSize, setSearchTerm, setSort, setCurrentPage, setCurrentPageSize, navigateNext, navigatePrevious, addFilter, removeFilter, clearFilters, selectRow, selectAll, paginationInfo, selectedData, refresh, getRowId, } = useDataGrid({
|
|
15
63
|
data,
|
|
16
|
-
endpoint,
|
|
17
|
-
httpConfig,
|
|
18
64
|
pageSize,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
onLoadingStateChange,
|
|
65
|
+
totalRecords: externalTotalRecords,
|
|
66
|
+
currentPage: externalCurrentPage,
|
|
67
|
+
loading: isDataLoading,
|
|
23
68
|
onPageChange,
|
|
24
69
|
onPageSizeChange,
|
|
25
70
|
onSortChange,
|
|
26
|
-
onFilterChange,
|
|
27
71
|
onSearchChange,
|
|
72
|
+
onApplyFilter,
|
|
73
|
+
onRemoveFilter,
|
|
74
|
+
onClearFilters,
|
|
75
|
+
onFilterChange,
|
|
28
76
|
});
|
|
29
77
|
// Auto-detect columns if not provided
|
|
30
78
|
const columns = useMemo(() => {
|
|
31
79
|
if (columnsProp.length > 0)
|
|
32
80
|
return columnsProp;
|
|
33
|
-
if (
|
|
34
|
-
const firstRow =
|
|
81
|
+
if (data.length > 0) {
|
|
82
|
+
const firstRow = data[0];
|
|
35
83
|
return Object.keys(firstRow).map((key) => ({
|
|
36
84
|
key,
|
|
37
85
|
label: key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1'),
|
|
@@ -41,32 +89,27 @@ onDataLoad, onDataError, onLoadingStateChange, onPageChange, onPageSizeChange, o
|
|
|
41
89
|
}));
|
|
42
90
|
}
|
|
43
91
|
return [];
|
|
44
|
-
}, [columnsProp,
|
|
45
|
-
//
|
|
46
|
-
const handleSelectionChange = useCallback((newSelectedData) => {
|
|
47
|
-
onSelectionChange?.(newSelectedData);
|
|
48
|
-
}, [onSelectionChange]);
|
|
49
|
-
// Use a ref to track previous selectedData to avoid unnecessary calls
|
|
92
|
+
}, [columnsProp, data]);
|
|
93
|
+
// Selection change effect
|
|
50
94
|
const previousSelectedData = React.useRef([]);
|
|
51
|
-
// Only call onSelectionChange when selectedData actually changes
|
|
52
95
|
React.useLayoutEffect(() => {
|
|
53
96
|
const hasChanged = selectedData.length !== previousSelectedData.current.length ||
|
|
54
97
|
selectedData.some((item, index) => item !== previousSelectedData.current[index]);
|
|
55
98
|
if (hasChanged && onSelectionChange) {
|
|
56
99
|
previousSelectedData.current = selectedData;
|
|
57
|
-
|
|
100
|
+
onSelectionChange(selectedData);
|
|
58
101
|
}
|
|
59
|
-
}, [selectedData,
|
|
102
|
+
}, [selectedData, onSelectionChange]);
|
|
60
103
|
// Handle row selection with callback
|
|
61
104
|
const handleRowSelect = useCallback((rowId, selected) => {
|
|
62
105
|
selectRow(rowId, selected);
|
|
63
106
|
if (onRowSelect) {
|
|
64
|
-
const row =
|
|
107
|
+
const row = data.find((r) => getRowId(r) === rowId);
|
|
65
108
|
if (row) {
|
|
66
109
|
onRowSelect(row, selected);
|
|
67
110
|
}
|
|
68
111
|
}
|
|
69
|
-
}, [selectRow, onRowSelect,
|
|
112
|
+
}, [selectRow, onRowSelect, data, getRowId]);
|
|
70
113
|
// Handle delete action
|
|
71
114
|
const handleDelete = useCallback(() => {
|
|
72
115
|
if (selectedRows.size === 0)
|
|
@@ -92,29 +135,19 @@ onDataLoad, onDataError, onLoadingStateChange, onPageChange, onPageSizeChange, o
|
|
|
92
135
|
refresh();
|
|
93
136
|
onTableRefresh?.();
|
|
94
137
|
}, [refresh, onTableRefresh]);
|
|
95
|
-
if
|
|
96
|
-
|
|
138
|
+
// Determine if fixed layout is enabled
|
|
139
|
+
const hasFixedLayout = maxHeight !== undefined || stickyHeader;
|
|
140
|
+
// Error state
|
|
141
|
+
if (externalError) {
|
|
142
|
+
return (_jsx("div", { className: `${theme.container} ${className}`, ...rest, children: _jsxs("div", { className: "px-4 py-8 text-center", children: [_jsx("div", { className: "text-red-600 dark:text-red-400 mb-2", children: "Error loading data" }), _jsx("div", { className: "text-sm text-gray-600 dark:text-gray-400 mb-4", children: externalError }), _jsx("button", { onClick: handleRefresh, className: theme.button, children: "Try Again" })] }) }));
|
|
97
143
|
}
|
|
98
|
-
return (_jsxs("div", { className: `${theme.container} ${className}
|
|
144
|
+
return (_jsxs("div", { className: `${theme.container} ${className} ${hasFixedLayout ? 'flex flex-col' : ''}`, style: hasFixedLayout && maxHeight ? { height: maxHeight } : undefined, ...rest, children: [enableFilters && (_jsx("div", { className: "p-4 pb-2 flex-shrink-0", children: _jsx(FilterControls, { columns: columns, activeFilters: activeFilters, onApplyFilter: addFilter, onRemoveFilter: removeFilter, onClearFilters: clearFilters, disabled: isDataLoading, filterLoading: isFilterLoading }) })), _jsx("div", { className: "px-4 pb-4 flex-shrink-0", children: _jsxs("div", { className: "flex justify-between items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [_jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: "Show" }), _jsx("select", { value: currentPageSize, onChange: (e) => setCurrentPageSize(parseInt(e.target.value)), disabled: isAnyLoading, className: "px-2 py-1 border border-gray-300 dark:border-gray-600 rounded text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:opacity-50", children: pageSizeOptions.map((size) => (_jsx("option", { value: size, children: size }, size))) }), _jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: "entries" })] }), _jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [enableSearch && (_jsxs("div", { className: "w-64 relative", children: [_jsx(SearchInput, { value: searchTerm, onChange: setSearchTerm, placeholder: "Search...", disabled: isAnyLoading, className: theme.searchInput }), isSearchLoading && (_jsx("div", { className: "absolute right-3 top-1/2 -translate-y-1/2", children: _jsx(Spinner, { className: "w-4 h-4 text-gray-400" }) }))] })), enableRefresh && (_jsx("button", { onClick: handleRefresh, disabled: isAnyLoading, title: isRefreshLoading ? 'Refreshing...' : 'Refresh data', className: "px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-200 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 transition-colors duration-150 flex items-center justify-center", children: _jsx("svg", { className: `w-4 h-4 ${isRefreshLoading ? 'animate-spin' : ''}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) }) })), enableDelete && enableSelection && (_jsxs("button", { onClick: handleDelete, disabled: selectedRows.size === 0 || isAnyLoading, title: selectedRows.size === 0
|
|
99
145
|
? 'Select rows to delete'
|
|
100
|
-
:
|
|
146
|
+
: isDeleteLoading
|
|
147
|
+
? 'Deleting...'
|
|
148
|
+
: `Delete ${selectedRows.size} selected item${selectedRows.size === 1 ? '' : 's'}`, className: `px-3 py-2 rounded focus:outline-none focus:ring-2 focus:ring-gray-500 dark:focus:ring-gray-400 transition-colors duration-150 flex items-center gap-1 ${selectedRows.size === 0 || isAnyLoading
|
|
101
149
|
? 'bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 cursor-not-allowed'
|
|
102
|
-
: 'bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600 cursor-pointer'}`, children: [_jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24",
|
|
150
|
+
: 'bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600 cursor-pointer'}`, children: [isDeleteLoading ? (_jsx(Spinner, { className: "w-4 h-4" })) : (_jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) })), selectedRows.size > 0 && (_jsx("span", { className: "text-sm", children: isDeleteLoading ? 'Deleting...' : `(${selectedRows.size} selected)` }))] }))] })] }) }), _jsx("div", { className: `
|
|
151
|
+
${hasFixedLayout ? 'flex-1 overflow-auto min-h-0' : 'overflow-x-auto'}
|
|
152
|
+
`, children: _jsxs("table", { className: theme.table, children: [_jsx(TableHeader, { columns: columns, sortConfig: sortConfig, onSort: enableSorting ? setSort : undefined, enableSelection: enableSelection, selectedCount: selectedRows.size, totalCount: paginatedData.length, onSelectAll: enableSelection ? selectAll : undefined, theme: theme, sticky: hasFixedLayout }), _jsx(TableBody, { columns: columns, data: paginatedData, selectedRows: selectedRows, onSelectRow: enableSelection ? handleRowSelect : undefined, onRowClick: onTableRowClick, onRowDoubleClick: onTableRowDoubleClick, onRowHover: onTableRowHover, onCellClick: onCellClick, enableSelection: enableSelection, loading: isDataLoading, theme: theme, getRowId: getRowId })] }) }), _jsxs("div", { className: `${theme.pagination} flex-shrink-0`, children: [_jsxs("div", { className: "text-sm text-gray-700 dark:text-gray-300 flex-shrink-0", children: ["Showing ", paginationInfo.start, "-", paginationInfo.end, " of", ' ', paginationInfo.totalRecords.toLocaleString(), " records"] }), _jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [_jsx("button", { onClick: navigatePrevious, disabled: !paginationInfo.hasPrevious || isAnyLoading, className: "px-3 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-150", children: "Previous" }), _jsxs("span", { className: "text-sm text-gray-700 dark:text-gray-300 px-2", children: ["Page ", currentPage, " ", paginationInfo.totalPages > 0 && `of ${paginationInfo.totalPages}`] }), _jsx("button", { onClick: navigateNext, disabled: !paginationInfo.hasNext || isAnyLoading, className: "px-3 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-150", children: "Next" })] })] })] }));
|
|
103
153
|
};
|
|
104
|
-
// Helper function to infer data type
|
|
105
|
-
function inferDataType(value) {
|
|
106
|
-
if (value == null)
|
|
107
|
-
return 'string';
|
|
108
|
-
if (typeof value === 'boolean')
|
|
109
|
-
return 'boolean';
|
|
110
|
-
if (typeof value === 'number')
|
|
111
|
-
return 'number';
|
|
112
|
-
if (typeof value === 'string') {
|
|
113
|
-
// Try to detect dates
|
|
114
|
-
const dateValue = new Date(value);
|
|
115
|
-
if (!isNaN(dateValue.getTime()) && dateValue.getFullYear() > 1900) {
|
|
116
|
-
return value.includes('T') || value.includes(' ') ? 'datetime' : 'date';
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return 'string';
|
|
120
|
-
}
|
|
@@ -2,10 +2,14 @@ import { Column, ActiveFilter } from '../../types';
|
|
|
2
2
|
interface FilterControlsProps<T> {
|
|
3
3
|
columns: Column<T>[];
|
|
4
4
|
activeFilters: ActiveFilter[];
|
|
5
|
-
|
|
5
|
+
onApplyFilter: (filter: Omit<ActiveFilter, 'label'>) => void;
|
|
6
6
|
onRemoveFilter: (index: number) => void;
|
|
7
7
|
onClearFilters: () => void;
|
|
8
|
+
/** Disables all controls (e.g., when data is loading) */
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
/** Shows spinner specifically on Apply Filter button */
|
|
11
|
+
filterLoading?: boolean;
|
|
8
12
|
}
|
|
9
|
-
export declare const FilterControls: <T>({ columns, activeFilters,
|
|
13
|
+
export declare const FilterControls: <T>({ columns, activeFilters, onApplyFilter, onRemoveFilter, onClearFilters, disabled, filterLoading, }: FilterControlsProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
10
14
|
export {};
|
|
11
15
|
//# sourceMappingURL=FilterControls.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterControls.d.ts","sourceRoot":"","sources":["../../../src/components/Filter/FilterControls.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FilterControls.d.ts","sourceRoot":"","sources":["../../../src/components/Filter/FilterControls.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAkB,MAAM,aAAa,CAAC;AAMnE,UAAU,mBAAmB,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,aAAa,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7D,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA4FD,eAAO,MAAM,cAAc,GAAI,CAAC,EAAG,qGAQhC,mBAAmB,CAAC,CAAC,CAAC,4CAiMxB,CAAC"}
|
|
@@ -1,81 +1,133 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// File: src/components/Filter/FilterControls.tsx
|
|
3
|
+
import { useState, useCallback, useMemo } from 'react';
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Operator Configuration
|
|
6
|
+
// ============================================================================
|
|
7
|
+
const OPERATORS = {
|
|
8
|
+
string: [
|
|
9
|
+
{ value: 'eq', label: 'equals' },
|
|
10
|
+
{ value: 'neq', label: 'not equals' },
|
|
11
|
+
{ value: 'contains', label: 'contains' },
|
|
12
|
+
{ value: 'startsWith', label: 'starts with' },
|
|
13
|
+
{ value: 'endsWith', label: 'ends with' },
|
|
14
|
+
],
|
|
15
|
+
number: [
|
|
16
|
+
{ value: 'eq', label: 'equals' },
|
|
17
|
+
{ value: 'neq', label: 'not equals' },
|
|
18
|
+
{ value: 'gt', label: 'greater than' },
|
|
19
|
+
{ value: 'gte', label: 'greater or equal' },
|
|
20
|
+
{ value: 'lt', label: 'less than' },
|
|
21
|
+
{ value: 'lte', label: 'less or equal' },
|
|
22
|
+
],
|
|
23
|
+
date: [
|
|
24
|
+
{ value: 'eq', label: 'equals' },
|
|
25
|
+
{ value: 'gt', label: 'after' },
|
|
26
|
+
{ value: 'gte', label: 'on or after' },
|
|
27
|
+
{ value: 'lt', label: 'before' },
|
|
28
|
+
{ value: 'lte', label: 'on or before' },
|
|
29
|
+
],
|
|
30
|
+
datetime: [
|
|
31
|
+
{ value: 'eq', label: 'equals' },
|
|
32
|
+
{ value: 'gt', label: 'after' },
|
|
33
|
+
{ value: 'gte', label: 'on or after' },
|
|
34
|
+
{ value: 'lt', label: 'before' },
|
|
35
|
+
{ value: 'lte', label: 'on or before' },
|
|
36
|
+
],
|
|
37
|
+
boolean: [{ value: 'eq', label: 'equals' }],
|
|
38
|
+
};
|
|
39
|
+
const DEFAULT_OPERATORS = [{ value: 'eq', label: 'equals' }];
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Spinner Component
|
|
42
|
+
// ============================================================================
|
|
43
|
+
const Spinner = ({ className = 'w-4 h-4' }) => (_jsxs("svg", { className: `animate-spin ${className}`, xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }));
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Styles (centralized for DRY)
|
|
46
|
+
// ============================================================================
|
|
47
|
+
const styles = {
|
|
48
|
+
select: 'w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-50 dark:disabled:bg-gray-700 disabled:text-gray-500 dark:disabled:text-gray-400 disabled:cursor-not-allowed',
|
|
49
|
+
input: 'px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-50 dark:disabled:bg-gray-700 disabled:cursor-not-allowed',
|
|
50
|
+
inputDisabled: 'px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-gray-50 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed',
|
|
51
|
+
buttonPrimary: 'px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white text-sm rounded-md hover:bg-blue-700 dark:hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-300 dark:disabled:bg-gray-600 disabled:text-gray-500 dark:disabled:text-gray-400 disabled:cursor-not-allowed transition-colors duration-150 inline-flex items-center gap-2',
|
|
52
|
+
buttonSecondary: 'px-4 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-sm rounded-md hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500 dark:focus:ring-gray-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-150',
|
|
53
|
+
filterTag: 'inline-flex items-center gap-1 px-3 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 rounded-full text-sm',
|
|
54
|
+
filterTagRemove: 'ml-1 text-blue-600 dark:text-blue-300 hover:text-blue-800 dark:hover:text-blue-100 focus:outline-none transition-colors duration-150',
|
|
55
|
+
label: 'block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1',
|
|
56
|
+
};
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Component
|
|
59
|
+
// ============================================================================
|
|
60
|
+
export const FilterControls = ({ columns, activeFilters, onApplyFilter, onRemoveFilter, onClearFilters, disabled = false, filterLoading = false, }) => {
|
|
61
|
+
// Form state
|
|
4
62
|
const [filterColumn, setFilterColumn] = useState('');
|
|
5
63
|
const [filterOperator, setFilterOperator] = useState('eq');
|
|
6
64
|
const [filterValue, setFilterValue] = useState('');
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
65
|
+
// Combined disabled state - disabled OR filterLoading
|
|
66
|
+
const isDisabled = disabled || filterLoading;
|
|
67
|
+
// Derived state
|
|
68
|
+
const filterableColumns = useMemo(() => columns.filter((col) => col.filterable !== false), [columns]);
|
|
69
|
+
const selectedColumn = useMemo(() => filterableColumns.find((col) => col.key === filterColumn), [filterableColumns, filterColumn]);
|
|
70
|
+
const operatorOptions = useMemo(() => OPERATORS[selectedColumn?.dataType ?? 'string'] ?? DEFAULT_OPERATORS, [selectedColumn]);
|
|
71
|
+
const canApply = filterColumn && filterValue.trim();
|
|
72
|
+
// Handlers
|
|
73
|
+
const handleColumnChange = useCallback((columnKey) => {
|
|
74
|
+
setFilterColumn(columnKey);
|
|
75
|
+
setFilterOperator('eq');
|
|
76
|
+
setFilterValue('');
|
|
77
|
+
}, []);
|
|
78
|
+
const handleApply = useCallback(() => {
|
|
79
|
+
if (!canApply || !selectedColumn || isDisabled)
|
|
80
|
+
return;
|
|
81
|
+
onApplyFilter({
|
|
82
|
+
column: filterColumn,
|
|
83
|
+
operator: filterOperator,
|
|
84
|
+
value: filterValue.trim(),
|
|
85
|
+
dataType: selectedColumn.dataType ?? 'string',
|
|
86
|
+
});
|
|
87
|
+
}, [
|
|
88
|
+
canApply,
|
|
89
|
+
filterColumn,
|
|
90
|
+
filterOperator,
|
|
91
|
+
filterValue,
|
|
92
|
+
selectedColumn,
|
|
93
|
+
onApplyFilter,
|
|
94
|
+
isDisabled,
|
|
95
|
+
]);
|
|
96
|
+
const handleClear = useCallback(() => {
|
|
97
|
+
setFilterColumn('');
|
|
98
|
+
setFilterOperator('eq');
|
|
99
|
+
setFilterValue('');
|
|
100
|
+
onClearFilters();
|
|
101
|
+
}, [onClearFilters]);
|
|
102
|
+
const handleKeyDown = useCallback((e) => {
|
|
103
|
+
if (e.key === 'Enter' && canApply && !isDisabled) {
|
|
104
|
+
handleApply();
|
|
34
105
|
}
|
|
35
|
-
};
|
|
36
|
-
|
|
106
|
+
}, [canApply, handleApply, isDisabled]);
|
|
107
|
+
// Render value input based on data type
|
|
108
|
+
const renderValueInput = () => {
|
|
37
109
|
if (!selectedColumn) {
|
|
38
|
-
return (_jsx("input", { type: "text", disabled: true, placeholder: "Select column first", className:
|
|
110
|
+
return (_jsx("input", { type: "text", disabled: true, placeholder: "Select column first", className: styles.inputDisabled }));
|
|
39
111
|
}
|
|
40
|
-
const
|
|
112
|
+
const commonProps = {
|
|
41
113
|
value: filterValue,
|
|
42
114
|
onChange: (e) => setFilterValue(e.target.value),
|
|
43
|
-
|
|
115
|
+
onKeyDown: handleKeyDown,
|
|
116
|
+
disabled: isDisabled,
|
|
117
|
+
className: styles.input,
|
|
44
118
|
};
|
|
45
119
|
switch (selectedColumn.dataType) {
|
|
46
120
|
case 'boolean':
|
|
47
|
-
return (_jsxs("select", { ...
|
|
121
|
+
return (_jsxs("select", { ...commonProps, className: styles.select, children: [_jsx("option", { value: "", children: "Select value" }), _jsx("option", { value: "true", children: "True" }), _jsx("option", { value: "false", children: "False" })] }));
|
|
48
122
|
case 'date':
|
|
49
|
-
return _jsx("input", { ...
|
|
123
|
+
return _jsx("input", { ...commonProps, type: "date" });
|
|
50
124
|
case 'datetime':
|
|
51
|
-
return _jsx("input", { ...
|
|
125
|
+
return _jsx("input", { ...commonProps, type: "datetime-local" });
|
|
52
126
|
case 'number':
|
|
53
|
-
return _jsx("input", { ...
|
|
127
|
+
return _jsx("input", { ...commonProps, type: "number", placeholder: "Enter number" });
|
|
54
128
|
default:
|
|
55
|
-
return _jsx("input", { ...
|
|
129
|
+
return _jsx("input", { ...commonProps, type: "text", placeholder: "Enter value" });
|
|
56
130
|
}
|
|
57
131
|
};
|
|
58
|
-
|
|
59
|
-
if (!filterColumn || !filterValue.trim())
|
|
60
|
-
return;
|
|
61
|
-
onAddFilter({
|
|
62
|
-
column: filterColumn,
|
|
63
|
-
operator: filterOperator,
|
|
64
|
-
value: filterValue.trim(),
|
|
65
|
-
dataType: selectedColumn?.dataType || 'string',
|
|
66
|
-
});
|
|
67
|
-
// DON'T reset the form - keep the values so user can modify and reapply
|
|
68
|
-
};
|
|
69
|
-
const handleClearfilter = () => {
|
|
70
|
-
setFilterColumn('');
|
|
71
|
-
setFilterOperator('eq');
|
|
72
|
-
setFilterValue('');
|
|
73
|
-
onClearFilters();
|
|
74
|
-
};
|
|
75
|
-
const canApplyFilter = filterColumn && filterValue.trim();
|
|
76
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap gap-4 items-end", children: [_jsxs("div", { className: "min-w-40", children: [_jsx("label", { className: "block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1", children: "Column" }), _jsxs("select", { value: filterColumn, onChange: (e) => {
|
|
77
|
-
setFilterColumn(e.target.value);
|
|
78
|
-
setFilterOperator('eq');
|
|
79
|
-
setFilterValue('');
|
|
80
|
-
}, className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400", children: [_jsx("option", { value: "", children: "Select Column" }), filterableColumns.map((col) => (_jsx("option", { value: String(col.key), children: col.label }, String(col.key))))] })] }), _jsxs("div", { className: "min-w-32", children: [_jsx("label", { className: "block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1", children: "Operator" }), _jsx("select", { value: filterOperator, onChange: (e) => setFilterOperator(e.target.value), disabled: !filterColumn, className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-50 dark:disabled:bg-gray-700 disabled:text-gray-500 dark:disabled:text-gray-400 disabled:cursor-not-allowed", children: getOperatorOptions().map((op) => (_jsx("option", { value: op.value, children: op.label }, op.value))) })] }), _jsxs("div", { className: "min-w-40", children: [_jsx("label", { className: "block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1", children: "Value" }), renderFilterInput()] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("button", { onClick: handleApplyFilter, disabled: !canApplyFilter, className: "px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white text-sm rounded-md hover:bg-blue-700 dark:hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-300 dark:disabled:bg-gray-600 disabled:text-gray-500 dark:disabled:text-gray-400 disabled:cursor-not-allowed transition-colors duration-150", children: "Apply Filter" }), _jsx("button", { onClick: handleClearfilter, className: "px-4 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-sm rounded-md hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500 dark:focus:ring-gray-400 transition-colors duration-150", children: "Clear All" })] })] }), activeFilters.length > 0 && (_jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [_jsx("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: "Active filters:" }), activeFilters.map((filter, index) => (_jsxs("span", { className: "inline-flex items-center gap-1 px-3 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 rounded-full text-sm", children: [filter.label, _jsx("button", { onClick: () => onRemoveFilter(index), className: "ml-1 text-blue-600 dark:text-blue-300 hover:text-blue-800 dark:hover:text-blue-100 focus:outline-none transition-colors duration-150", children: "\u00D7" })] }, index)))] }))] }));
|
|
132
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap gap-4 items-end", children: [_jsxs("div", { className: "min-w-40", children: [_jsx("label", { className: styles.label, children: "Column" }), _jsxs("select", { value: filterColumn, onChange: (e) => handleColumnChange(e.target.value), disabled: isDisabled, className: styles.select, children: [_jsx("option", { value: "", children: "Select Column" }), filterableColumns.map((col) => (_jsx("option", { value: String(col.key), children: col.label }, String(col.key))))] })] }), _jsxs("div", { className: "min-w-32", children: [_jsx("label", { className: styles.label, children: "Operator" }), _jsx("select", { value: filterOperator, onChange: (e) => setFilterOperator(e.target.value), disabled: isDisabled || !filterColumn, className: styles.select, children: operatorOptions.map((op) => (_jsx("option", { value: op.value, children: op.label }, op.value))) })] }), _jsxs("div", { className: "min-w-40", children: [_jsx("label", { className: styles.label, children: "Value" }), renderValueInput()] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs("button", { onClick: handleApply, disabled: isDisabled || !canApply, className: styles.buttonPrimary, children: [filterLoading && _jsx(Spinner, { className: "w-4 h-4" }), filterLoading ? 'Applying...' : 'Apply Filter'] }), _jsx("button", { onClick: handleClear, disabled: isDisabled, className: styles.buttonSecondary, children: "Clear All" })] })] }), activeFilters.length > 0 && (_jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [_jsx("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: "Active filters:" }), activeFilters.map((filter, index) => (_jsxs("span", { className: styles.filterTag, children: [filter.label, _jsx("button", { onClick: () => onRemoveFilter(index), disabled: isDisabled, className: styles.filterTagRemove, "aria-label": `Remove filter: ${filter.label}`, children: "\u00D7" })] }, `${filter.column}-${index}`)))] }))] }));
|
|
81
133
|
};
|
|
@@ -9,7 +9,8 @@ interface TableHeaderProps<T> {
|
|
|
9
9
|
totalCount: number;
|
|
10
10
|
onSelectAll?: (selected: boolean) => void;
|
|
11
11
|
theme: Theme;
|
|
12
|
+
sticky?: boolean;
|
|
12
13
|
}
|
|
13
|
-
export declare const TableHeader: <T>({ columns, sortConfig, onSort, enableSelection, selectedCount, totalCount, onSelectAll, theme, }: TableHeaderProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare const TableHeader: <T>({ columns, sortConfig, onSort, enableSelection, selectedCount, totalCount, onSelectAll, theme, sticky, }: TableHeaderProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
14
15
|
export {};
|
|
15
16
|
//# sourceMappingURL=TableHeader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableHeader.d.ts","sourceRoot":"","sources":["../../../src/components/Table/TableHeader.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TableHeader.d.ts","sourceRoot":"","sources":["../../../src/components/Table/TableHeader.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAErC,UAAU,gBAAgB,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,WAAW,GAAI,CAAC,EAAG,0GAU7B,gBAAgB,CAAC,CAAC,CAAC,4CAyGrB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
export const TableHeader = ({ columns, sortConfig, onSort, enableSelection, selectedCount, totalCount, onSelectAll, theme, }) => {
|
|
2
|
+
export const TableHeader = ({ columns, sortConfig, onSort, enableSelection, selectedCount, totalCount, onSelectAll, theme, sticky = false, }) => {
|
|
3
3
|
const getSortIcon = (columnKey) => {
|
|
4
4
|
if (sortConfig.column !== columnKey) {
|
|
5
5
|
return (_jsx("svg", { className: "w-4 h-4 text-gray-400 dark:text-gray-500", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" }) }));
|
|
@@ -9,7 +9,9 @@ export const TableHeader = ({ columns, sortConfig, onSort, enableSelection, sele
|
|
|
9
9
|
}
|
|
10
10
|
return (_jsx("svg", { className: "w-4 h-4 text-blue-500 dark:text-blue-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) }));
|
|
11
11
|
};
|
|
12
|
-
|
|
12
|
+
// Sticky styles
|
|
13
|
+
const stickyClass = sticky ? 'sticky top-0 z-10' : '';
|
|
14
|
+
return (_jsx("thead", { className: `${theme.header} ${stickyClass}`, children: _jsxs("tr", { children: [enableSelection && (_jsx("th", { className: `w-12 ${theme.headerCell}`, role: "columnheader", "aria-label": "Select all rows", children: _jsx("input", { type: "checkbox", checked: selectedCount > 0 && selectedCount === totalCount, ref: (el) => {
|
|
13
15
|
if (el) {
|
|
14
16
|
el.indeterminate = selectedCount > 0 && selectedCount < totalCount;
|
|
15
17
|
}
|
|
@@ -1,38 +1,34 @@
|
|
|
1
|
-
import { BaseRowData,
|
|
1
|
+
import { BaseRowData, ActiveFilter, SortConfig, PaginationInfo } from '../types';
|
|
2
2
|
interface UseDataGridProps<T> {
|
|
3
|
-
data
|
|
4
|
-
endpoint?: string;
|
|
5
|
-
httpConfig?: HttpConfig;
|
|
3
|
+
data: T[];
|
|
6
4
|
pageSize?: number;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
totalRecords?: number;
|
|
6
|
+
currentPage?: number;
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
onPageChange?: (page: number, info: PaginationInfo) => void;
|
|
9
|
+
onPageSizeChange?: (size: number) => void;
|
|
10
|
+
onSortChange?: (config: SortConfig) => void;
|
|
11
|
+
onSearchChange?: (term: string) => void;
|
|
12
|
+
onApplyFilter?: (filter: ActiveFilter, all: ActiveFilter[]) => void;
|
|
13
|
+
onRemoveFilter?: (removed: ActiveFilter, remaining: ActiveFilter[]) => void;
|
|
14
|
+
onClearFilters?: () => void;
|
|
14
15
|
onFilterChange?: (filters: ActiveFilter[]) => void;
|
|
15
|
-
onSearchChange?: (searchTerm: string) => void;
|
|
16
16
|
}
|
|
17
|
-
export declare const useDataGrid: <T extends BaseRowData>({ data:
|
|
17
|
+
export declare const useDataGrid: <T extends BaseRowData>({ data, pageSize: initialPageSize, totalRecords: externalTotalRecords, currentPage: externalCurrentPage, loading: externalLoading, onPageChange, onPageSizeChange, onSortChange, onSearchChange, onApplyFilter, onRemoveFilter, onClearFilters, onFilterChange, }: UseDataGridProps<T>) => {
|
|
18
18
|
data: T[];
|
|
19
19
|
processedData: T[];
|
|
20
20
|
paginatedData: T[];
|
|
21
21
|
loading: boolean;
|
|
22
|
-
error: string | null;
|
|
23
22
|
searchTerm: string;
|
|
24
23
|
activeFilters: ActiveFilter[];
|
|
25
24
|
sortConfig: SortConfig;
|
|
26
25
|
selectedRows: Set<string>;
|
|
27
26
|
currentPage: number;
|
|
28
27
|
currentPageSize: number;
|
|
29
|
-
totalRecords: number;
|
|
30
|
-
hasMore: boolean;
|
|
31
|
-
continuationToken: string | undefined;
|
|
32
28
|
setSearchTerm: (term: string) => void;
|
|
33
29
|
setSort: (column: string) => void;
|
|
34
30
|
setCurrentPage: (page: number) => void;
|
|
35
|
-
setCurrentPageSize: (
|
|
31
|
+
setCurrentPageSize: (size: number) => void;
|
|
36
32
|
navigateNext: () => void;
|
|
37
33
|
navigatePrevious: () => void;
|
|
38
34
|
addFilter: (filter: Omit<ActiveFilter, "label">) => void;
|
|
@@ -40,11 +36,13 @@ export declare const useDataGrid: <T extends BaseRowData>({ data: staticData, en
|
|
|
40
36
|
clearFilters: () => void;
|
|
41
37
|
selectRow: (rowId: string, selected: boolean) => void;
|
|
42
38
|
selectAll: (selected: boolean) => void;
|
|
39
|
+
clearSelection: () => void;
|
|
43
40
|
refresh: () => void;
|
|
44
41
|
paginationInfo: PaginationInfo;
|
|
45
42
|
selectedData: T[];
|
|
46
43
|
hasSelection: boolean;
|
|
47
44
|
getRowId: (row: T) => string;
|
|
45
|
+
isControlled: boolean;
|
|
48
46
|
};
|
|
49
47
|
export {};
|
|
50
48
|
//# sourceMappingURL=useDataGrid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDataGrid.d.ts","sourceRoot":"","sources":["../../src/hooks/useDataGrid.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useDataGrid.d.ts","sourceRoot":"","sources":["../../src/hooks/useDataGrid.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAOjF,UAAU,gBAAgB,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC5D,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IAC5C,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IACpE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAC5E,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;CACpD;AAMD,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,WAAW,EAAE,mQAchD,gBAAgB,CAAC,CAAC,CAAC;;;;;;;;;;;0BAsGX,MAAM;sBASJ,MAAM;2BAaR,MAAM;+BAQN,MAAM;;;wBAsBJ,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC;0BAkB5B,MAAM;;uBAwBsB,MAAM,YAAY,OAAO;0BASlD,OAAO;;;;;;oBA/KZ,CAAC,KAAG,MAAM;;CA+OnB,CAAC"}
|