@ackplus/react-tanstack-data-table 1.0.19-beta-0.6
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/LICENSE +21 -0
- package/README.md +599 -0
- package/index.d.ts.map +1 -0
- package/index.js +42 -0
- package/lib/components/droupdown/menu-dropdown.d.ts.map +1 -0
- package/lib/components/droupdown/menu-dropdown.js +47 -0
- package/lib/components/export-progress-dialog.d.ts.map +1 -0
- package/lib/components/export-progress-dialog.js +30 -0
- package/lib/components/filters/filter-value-input.d.ts.map +1 -0
- package/lib/components/filters/filter-value-input.js +64 -0
- package/lib/components/filters/index.d.ts.map +1 -0
- package/lib/components/filters/index.js +125 -0
- package/lib/components/headers/draggable-header.d.ts.map +1 -0
- package/lib/components/headers/draggable-header.js +226 -0
- package/lib/components/headers/index.d.ts.map +1 -0
- package/lib/components/headers/index.js +5 -0
- package/lib/components/headers/table-header.d.ts.map +1 -0
- package/lib/components/headers/table-header.js +59 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/components/index.js +18 -0
- package/lib/components/pagination/data-table-pagination.d.ts.map +1 -0
- package/lib/components/pagination/data-table-pagination.js +24 -0
- package/lib/components/pagination/index.d.ts.map +1 -0
- package/lib/components/pagination/index.js +4 -0
- package/lib/components/rows/data-table-row.d.ts.map +1 -0
- package/lib/components/rows/data-table-row.js +42 -0
- package/lib/components/rows/empty-data-row.d.ts.map +1 -0
- package/lib/components/rows/empty-data-row.js +8 -0
- package/lib/components/rows/index.d.ts.map +1 -0
- package/lib/components/rows/index.js +6 -0
- package/lib/components/rows/loading-rows.d.ts.map +1 -0
- package/lib/components/rows/loading-rows.js +46 -0
- package/lib/components/table/data-table.d.ts.map +1 -0
- package/lib/components/table/data-table.js +663 -0
- package/lib/components/table/data-table.types.d.ts.map +1 -0
- package/lib/components/table/data-table.types.js +6 -0
- package/lib/components/table/index.d.ts.map +1 -0
- package/lib/components/table/index.js +5 -0
- package/lib/components/toolbar/bulk-actions-toolbar.d.ts.map +1 -0
- package/lib/components/toolbar/bulk-actions-toolbar.js +31 -0
- package/lib/components/toolbar/column-custom-filter-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-custom-filter-control.js +149 -0
- package/lib/components/toolbar/column-custum-filter-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-custum-filter-control.js +150 -0
- package/lib/components/toolbar/column-pinning-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-pinning-control.js +103 -0
- package/lib/components/toolbar/column-reset-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-reset-control.js +13 -0
- package/lib/components/toolbar/column-visibility-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-visibility-control.js +27 -0
- package/lib/components/toolbar/data-table-toolbar.d.ts.map +1 -0
- package/lib/components/toolbar/data-table-toolbar.js +23 -0
- package/lib/components/toolbar/index.d.ts.map +1 -0
- package/lib/components/toolbar/index.js +13 -0
- package/lib/components/toolbar/table-export-control.d.ts.map +1 -0
- package/lib/components/toolbar/table-export-control.js +94 -0
- package/lib/components/toolbar/table-search-control.d.ts.map +1 -0
- package/lib/components/toolbar/table-search-control.js +61 -0
- package/lib/components/toolbar/table-size-control.d.ts.map +1 -0
- package/lib/components/toolbar/table-size-control.js +33 -0
- package/lib/contexts/data-table-context.d.ts.map +1 -0
- package/lib/contexts/data-table-context.js +50 -0
- package/lib/examples/advanced-features-example.d.ts.map +1 -0
- package/lib/examples/advanced-features-example.js +282 -0
- package/lib/examples/basic-example.d.ts.map +1 -0
- package/lib/examples/basic-example.js +323 -0
- package/lib/examples/bulk-actions-test.d.ts.map +1 -0
- package/lib/examples/bulk-actions-test.js +47 -0
- package/lib/examples/crud-api-example.d.ts.map +1 -0
- package/lib/examples/crud-api-example.js +321 -0
- package/lib/examples/custom-column-filter-example.d.ts.map +1 -0
- package/lib/examples/custom-column-filter-example.js +60 -0
- package/lib/examples/custom-selection-example.d.ts.map +1 -0
- package/lib/examples/custom-selection-example.js +184 -0
- package/lib/examples/export-callbacks-example.d.ts.map +1 -0
- package/lib/examples/export-callbacks-example.js +155 -0
- package/lib/examples/improved-export-example.d.ts.map +1 -0
- package/lib/examples/improved-export-example.js +153 -0
- package/lib/examples/improved-server-selection-example.d.ts.map +1 -0
- package/lib/examples/improved-server-selection-example.js +118 -0
- package/lib/examples/index.d.ts.map +1 -0
- package/lib/examples/index.js +5 -0
- package/lib/examples/selection-test-example.d.ts.map +1 -0
- package/lib/examples/selection-test-example.js +111 -0
- package/lib/examples/simple-local-example.d.ts.map +1 -0
- package/lib/examples/simple-local-example.js +101 -0
- package/lib/examples/simple-server-selection-example.d.ts.map +1 -0
- package/lib/examples/simple-server-selection-example.js +178 -0
- package/lib/examples/virtualized-example.d.ts.map +1 -0
- package/lib/examples/virtualized-example.js +119 -0
- package/lib/features/custom-column-filter.feature.d.ts.map +1 -0
- package/lib/features/custom-column-filter.feature.js +306 -0
- package/lib/features/custom-selection.feature.d.ts.map +1 -0
- package/lib/features/custom-selection.feature.js +224 -0
- package/lib/features/index.d.ts.map +1 -0
- package/lib/features/index.js +9 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/index.js +6 -0
- package/lib/hooks/use-data-table-api.d.ts.map +1 -0
- package/lib/hooks/use-data-table-api.js +673 -0
- package/lib/hooks/use-table-state.d.ts.map +1 -0
- package/lib/hooks/use-table-state.js +74 -0
- package/lib/icons/add-icon.d.ts.map +1 -0
- package/lib/icons/add-icon.js +5 -0
- package/lib/icons/csv-icon.d.ts.map +1 -0
- package/lib/icons/csv-icon.js +5 -0
- package/lib/icons/delete-icon.d.ts.map +1 -0
- package/lib/icons/delete-icon.js +5 -0
- package/lib/icons/excel-icon.d.ts.map +1 -0
- package/lib/icons/excel-icon.js +5 -0
- package/lib/icons/index.d.ts.map +1 -0
- package/lib/icons/index.js +7 -0
- package/lib/icons/unpin-icon.d.ts.map +1 -0
- package/lib/icons/unpin-icon.js +5 -0
- package/lib/icons/view-comfortable-icon.d.ts.map +1 -0
- package/lib/icons/view-comfortable-icon.js +5 -0
- package/lib/icons/view-compact-icon.d.ts.map +1 -0
- package/lib/icons/view-compact-icon.js +5 -0
- package/lib/types/column.types.d.ts.map +1 -0
- package/lib/types/column.types.js +2 -0
- package/lib/types/data-table-api.d.ts.map +1 -0
- package/lib/types/data-table-api.js +1 -0
- package/lib/types/export.types.d.ts.map +1 -0
- package/lib/types/export.types.js +5 -0
- package/lib/types/hooks.types.d.ts.map +1 -0
- package/lib/types/hooks.types.js +1 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +14 -0
- package/lib/types/slots.types.d.ts.map +1 -0
- package/lib/types/slots.types.js +1 -0
- package/lib/types/table.types.d.ts.map +1 -0
- package/lib/types/table.types.js +1 -0
- package/lib/utils/column-helpers.d.ts.map +1 -0
- package/lib/utils/column-helpers.js +46 -0
- package/lib/utils/debounced-fetch.utils.d.ts.map +1 -0
- package/lib/utils/debounced-fetch.utils.js +51 -0
- package/lib/utils/export-utils.d.ts.map +1 -0
- package/lib/utils/export-utils.js +181 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +17 -0
- package/lib/utils/selection-helpers.d.ts.map +1 -0
- package/lib/utils/selection-helpers.js +162 -0
- package/lib/utils/slot-helpers.d.ts.map +1 -0
- package/lib/utils/slot-helpers.js +27 -0
- package/lib/utils/special-columns.utils.d.ts.map +1 -0
- package/lib/utils/special-columns.utils.js +77 -0
- package/lib/utils/styling-helpers.d.ts.map +1 -0
- package/lib/utils/styling-helpers.js +97 -0
- package/lib/utils/table-helpers.d.ts.map +1 -0
- package/lib/utils/table-helpers.js +72 -0
- package/lib/utils/value-helpers.d.ts.map +1 -0
- package/lib/utils/value-helpers.js +48 -0
- package/package.json +57 -0
- package/tsconfig.lib.tsbuildinfo +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/table/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk-actions-toolbar.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/toolbar/bulk-actions-toolbar.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,SAAS,EAAW,MAAM,OAAO,CAAC;AAI3C,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAGzE,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,GAAG;IAC5C,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,CAAC,cAAc,EAAE,cAAc,KAAK,SAAS,CAAC;IAC5D,EAAE,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,EACxC,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,EAAE,GACL,EAAE,uBAAuB,CAAC,CAAC,CAAC,2CAwD5B"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Toolbar, Typography, Chip, Fade, alpha, } from '@mui/material';
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
5
|
+
import { getSlotComponent } from '../../utils/slot-helpers';
|
|
6
|
+
export function BulkActionsToolbar({ selectionState, selectedRowCount, bulkActions, sx, }) {
|
|
7
|
+
const { slots, slotProps } = useDataTableContext();
|
|
8
|
+
const ToolbarSlot = getSlotComponent(slots, 'toolbar', Toolbar);
|
|
9
|
+
// Memoize the bulk actions rendering to prevent infinite re-renders
|
|
10
|
+
const renderedBulkActions = useMemo(() => {
|
|
11
|
+
if (!bulkActions)
|
|
12
|
+
return null;
|
|
13
|
+
return bulkActions(selectionState);
|
|
14
|
+
}, [bulkActions, selectionState]);
|
|
15
|
+
return (_jsx(Fade, { in: selectedRowCount > 0, children: _jsxs(ToolbarSlot, { sx: {
|
|
16
|
+
pl: { sm: 2 },
|
|
17
|
+
pr: {
|
|
18
|
+
xs: 1,
|
|
19
|
+
sm: 1,
|
|
20
|
+
},
|
|
21
|
+
bgcolor: (theme) => alpha(theme.palette.primary.main, 0.05),
|
|
22
|
+
mb: 1,
|
|
23
|
+
position: 'relative',
|
|
24
|
+
zIndex: 1,
|
|
25
|
+
...sx,
|
|
26
|
+
}, ...slotProps?.bulkActionsToolbar, children: [_jsx(Box, { sx: { flex: '1 1 100%' }, children: _jsx(Typography, { color: "inherit", variant: "subtitle1", component: "div", children: _jsx(Chip, { label: `${selectedRowCount} selected`, size: "small", color: "primary", variant: "outlined" }) }) }), _jsx(Box, { sx: {
|
|
27
|
+
display: 'flex',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
gap: 1,
|
|
30
|
+
}, children: renderedBulkActions })] }) }));
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-custom-filter-control.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/toolbar/column-custom-filter-control.tsx"],"names":[],"mappings":"AA8BA,wBAAgB,yBAAyB,4CAwYxC"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { FilterList } from '@mui/icons-material';
|
|
3
|
+
import { Box, MenuItem, Select, FormControl, InputLabel, Button, Stack, Typography, Chip, IconButton, Divider, Badge, } from '@mui/material';
|
|
4
|
+
import { useMemo, useCallback, useEffect } from 'react';
|
|
5
|
+
import { MenuDropdown } from '../droupdown/menu-dropdown';
|
|
6
|
+
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
7
|
+
import { AddIcon, DeleteIcon, } from '../../icons';
|
|
8
|
+
import { getColumnType, isColumnFilterable } from '../../utils/column-helpers';
|
|
9
|
+
import { getSlotComponent } from '../../utils/slot-helpers';
|
|
10
|
+
import { FILTER_OPERATORS } from '../filters';
|
|
11
|
+
import { FilterValueInput } from '../filters/filter-value-input';
|
|
12
|
+
export function ColumnCustomFilterControl() {
|
|
13
|
+
const { table, slots, slotProps } = useDataTableContext();
|
|
14
|
+
const FilterIconSlot = getSlotComponent(slots, 'filterIcon', FilterList);
|
|
15
|
+
// Use the custom feature state from the table - now using pending filters for UI
|
|
16
|
+
const customFilterState = table.getCustomColumnFilterState?.() || {
|
|
17
|
+
filters: [],
|
|
18
|
+
logic: 'AND',
|
|
19
|
+
pendingFilters: [],
|
|
20
|
+
pendingLogic: 'AND'
|
|
21
|
+
};
|
|
22
|
+
// Use pending filters for the UI (draft state)
|
|
23
|
+
const filters = customFilterState.pendingFilters;
|
|
24
|
+
const filterLogic = customFilterState.pendingLogic;
|
|
25
|
+
// Active filters are the actual applied filters
|
|
26
|
+
const activeFiltersCount = table.getActiveColumnFilters?.()?.length || 0;
|
|
27
|
+
const filterableColumns = useMemo(() => {
|
|
28
|
+
return table.getAllLeafColumns()
|
|
29
|
+
.filter(column => isColumnFilterable(column));
|
|
30
|
+
}, [table]);
|
|
31
|
+
const addFilter = useCallback((columnId, operator) => {
|
|
32
|
+
// If no column specified, use empty (user will select)
|
|
33
|
+
// If column specified, get its appropriate default operator
|
|
34
|
+
let defaultOperator = operator || '';
|
|
35
|
+
if (columnId && !operator) {
|
|
36
|
+
const column = filterableColumns.find(col => col.id === columnId);
|
|
37
|
+
const columnType = getColumnType(column);
|
|
38
|
+
const operators = FILTER_OPERATORS[columnType] || FILTER_OPERATORS.text;
|
|
39
|
+
defaultOperator = operators[0]?.value || 'contains';
|
|
40
|
+
}
|
|
41
|
+
table.addPendingColumnFilter?.(columnId || '', defaultOperator, '');
|
|
42
|
+
}, [table, filterableColumns]);
|
|
43
|
+
const handleAddFilter = useCallback(() => {
|
|
44
|
+
addFilter();
|
|
45
|
+
}, [addFilter]);
|
|
46
|
+
const updateFilter = useCallback((filterId, updates) => {
|
|
47
|
+
table.updatePendingColumnFilter?.(filterId, updates);
|
|
48
|
+
}, [table]);
|
|
49
|
+
const removeFilter = useCallback((filterId) => {
|
|
50
|
+
table.removePendingColumnFilter?.(filterId);
|
|
51
|
+
}, [table]);
|
|
52
|
+
const clearAllFilters = useCallback((closeDialog) => {
|
|
53
|
+
// Clear all pending filters
|
|
54
|
+
table.clearAllPendingColumnFilters?.();
|
|
55
|
+
// Immediately apply the clear (which will clear active filters too)
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
table.applyPendingColumnFilters?.();
|
|
58
|
+
// Close dialog if callback provided
|
|
59
|
+
if (closeDialog) {
|
|
60
|
+
closeDialog();
|
|
61
|
+
}
|
|
62
|
+
}, 0);
|
|
63
|
+
}, [table]);
|
|
64
|
+
// Handle filter logic change (AND/OR)
|
|
65
|
+
const handleLogicChange = useCallback((newLogic) => {
|
|
66
|
+
table.setPendingFilterLogic?.(newLogic);
|
|
67
|
+
}, [table]);
|
|
68
|
+
// Apply all pending filters
|
|
69
|
+
const applyFilters = useCallback(() => {
|
|
70
|
+
table.applyPendingColumnFilters?.();
|
|
71
|
+
}, [table]);
|
|
72
|
+
// Handle apply button click
|
|
73
|
+
const handleApplyFilters = useCallback((closeDialog) => {
|
|
74
|
+
applyFilters();
|
|
75
|
+
closeDialog();
|
|
76
|
+
}, [applyFilters]);
|
|
77
|
+
const getOperatorsForColumn = useCallback((columnId) => {
|
|
78
|
+
const column = filterableColumns.find(col => col.id === columnId);
|
|
79
|
+
const type = getColumnType(column);
|
|
80
|
+
return FILTER_OPERATORS[type] || FILTER_OPERATORS.text;
|
|
81
|
+
}, [filterableColumns]);
|
|
82
|
+
// Handle column selection change
|
|
83
|
+
const handleColumnChange = useCallback((filterId, newColumnId, currentFilter) => {
|
|
84
|
+
const newColumn = filterableColumns.find(col => col.id === newColumnId);
|
|
85
|
+
const columnType = getColumnType(newColumn);
|
|
86
|
+
const operators = FILTER_OPERATORS[columnType] || FILTER_OPERATORS.text;
|
|
87
|
+
// Only reset operator if current operator is not valid for new column type
|
|
88
|
+
const currentOperatorValid = operators.some(op => op.value === currentFilter.operator);
|
|
89
|
+
const newOperator = currentOperatorValid ? currentFilter.operator : operators[0]?.value || '';
|
|
90
|
+
updateFilter(filterId, {
|
|
91
|
+
columnId: newColumnId,
|
|
92
|
+
operator: newOperator,
|
|
93
|
+
// Keep the current value unless operator is empty/notEmpty
|
|
94
|
+
value: ['isEmpty', 'isNotEmpty'].includes(newOperator) ? '' : currentFilter.value,
|
|
95
|
+
});
|
|
96
|
+
}, [filterableColumns, updateFilter]);
|
|
97
|
+
// Handle operator selection change
|
|
98
|
+
const handleOperatorChange = useCallback((filterId, newOperator, currentFilter) => {
|
|
99
|
+
updateFilter(filterId, {
|
|
100
|
+
operator: newOperator,
|
|
101
|
+
// Only reset value if operator is empty/notEmpty, otherwise preserve it
|
|
102
|
+
value: ['isEmpty', 'isNotEmpty'].includes(newOperator) ? '' : currentFilter.value,
|
|
103
|
+
});
|
|
104
|
+
}, [updateFilter]);
|
|
105
|
+
// Handle filter value change
|
|
106
|
+
const handleFilterValueChange = useCallback((filterId, value) => {
|
|
107
|
+
updateFilter(filterId, { value });
|
|
108
|
+
}, [updateFilter]);
|
|
109
|
+
// Handle filter removal
|
|
110
|
+
const handleRemoveFilter = useCallback((filterId) => {
|
|
111
|
+
removeFilter(filterId);
|
|
112
|
+
}, [removeFilter]);
|
|
113
|
+
// Count pending filters that are ready to apply (have column, operator, and value OR are empty/notEmpty operators)
|
|
114
|
+
const pendingFiltersCount = filters.filter(f => {
|
|
115
|
+
if (!f.columnId || !f.operator)
|
|
116
|
+
return false;
|
|
117
|
+
// For empty/notEmpty operators, no value is needed
|
|
118
|
+
if (['isEmpty', 'isNotEmpty'].includes(f.operator))
|
|
119
|
+
return true;
|
|
120
|
+
// For other operators, value is required
|
|
121
|
+
return f.value && f.value.toString().trim() !== '';
|
|
122
|
+
}).length;
|
|
123
|
+
// Check if we need to show "Clear Applied Filters" button
|
|
124
|
+
const hasAppliedFilters = activeFiltersCount > 0;
|
|
125
|
+
// Determine if there are pending changes that can be applied
|
|
126
|
+
const hasPendingChanges = pendingFiltersCount > 0 || (filters.length === 0 && hasAppliedFilters);
|
|
127
|
+
// Auto-add default filter when opening if no filters exist AND no applied filters
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (filters.length === 0 && filterableColumns.length > 0 && activeFiltersCount === 0) {
|
|
130
|
+
const firstColumn = filterableColumns[0];
|
|
131
|
+
const columnType = getColumnType(firstColumn);
|
|
132
|
+
const operators = FILTER_OPERATORS[columnType] || FILTER_OPERATORS.text;
|
|
133
|
+
const defaultOperator = operators[0]?.value || 'contains';
|
|
134
|
+
// Add default filter with first column and its first operator
|
|
135
|
+
addFilter(firstColumn.id, defaultOperator);
|
|
136
|
+
}
|
|
137
|
+
}, [filters.length, filterableColumns, addFilter, activeFiltersCount]);
|
|
138
|
+
return (_jsx(MenuDropdown, { anchor: (_jsx(Badge, { variant: "dot", color: "primary", invisible: activeFiltersCount === 0, children: _jsx(IconButton, { size: "small", color: activeFiltersCount > 0 ? 'primary' : 'default', sx: {
|
|
139
|
+
flexShrink: 0,
|
|
140
|
+
}, children: _jsx(FilterIconSlot, { ...slotProps?.filterIcon }) }) })), children: ({ handleClose }) => (_jsxs(Box, { sx: {
|
|
141
|
+
p: 2,
|
|
142
|
+
minWidth: 500,
|
|
143
|
+
maxWidth: 650,
|
|
144
|
+
}, children: [_jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", sx: { mb: 2 }, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Typography, { variant: "subtitle1", sx: { fontWeight: 'bold' }, children: "Column Filters" }), activeFiltersCount > 0 && (_jsx(Chip, { size: "small", label: `${activeFiltersCount} active`, color: "primary", variant: "outlined" }))] }), _jsx(Stack, { direction: "row", spacing: 1, children: _jsx(Button, { size: "small", variant: "outlined", startIcon: _jsx(AddIcon, {}), onClick: handleAddFilter, children: "Add Filter" }) })] }), filters.length > 1 && (_jsxs(Box, { sx: { mb: 2 }, children: [_jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "Filter Logic:" }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Button, { size: "small", variant: filterLogic === 'AND' ? 'contained' : 'outlined', onClick: () => handleLogicChange('AND'), children: "AND (All conditions)" }), _jsx(Button, { size: "small", variant: filterLogic === 'OR' ? 'contained' : 'outlined', onClick: () => handleLogicChange('OR'), children: "OR (Any condition)" })] })] })), _jsx(Divider, { sx: { mb: 2 } }), filters.length === 0 ? (_jsx(Typography, { variant: "body2", color: "text.secondary", sx: {
|
|
145
|
+
textAlign: 'center',
|
|
146
|
+
py: 2,
|
|
147
|
+
}, children: "No filters applied. Click \"Add Filter\" to start." })) : (_jsx(Stack, { spacing: 2, children: filters.map((filter) => (_jsx(Box, { children: _jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsxs(FormControl, { size: "small", sx: { minWidth: 140 }, children: [_jsx(InputLabel, { children: "Column" }), _jsx(Select, { value: filter.columnId, label: "Column", onChange: (e) => handleColumnChange(filter.id, e.target.value, filter), children: filterableColumns.map(column => (_jsx(MenuItem, { value: column.id, children: typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id }, column.id))) })] }), _jsxs(FormControl, { size: "small", sx: { minWidth: 160 }, disabled: !filter.columnId, children: [_jsx(InputLabel, { children: "Operator" }), _jsx(Select, { value: filter.operator, label: "Operator", onChange: (e) => handleOperatorChange(filter.id, e.target.value, filter), children: getOperatorsForColumn(filter.columnId).map(op => (_jsx(MenuItem, { value: op.value, children: op.label }, op.value))) })] }), !['isEmpty', 'isNotEmpty'].includes(filter.operator) ? (_jsx(FilterValueInput, { filter: filter, column: filterableColumns.find(col => col.id === filter.columnId), onValueChange: (value) => handleFilterValueChange(filter.id, value) })) : (_jsx(Box, { sx: { flex: 1 } }) /* Spacer when no value input needed */), _jsx(IconButton, { size: "small", onClick: () => handleRemoveFilter(filter.id), color: "error", children: _jsx(DeleteIcon, {}) })] }) }, filter.id))) })), _jsx(Divider, { sx: { my: 2 } }), _jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", spacing: 1, children: [(hasAppliedFilters || filters.length > 0) && (_jsx(Button, { size: "small", variant: "text", color: "warning", onClick: () => clearAllFilters(handleClose), startIcon: _jsx(DeleteIcon, {}), children: "Reset" })), !(hasAppliedFilters || filters.length > 0) && _jsx(Box, {}), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Button, { variant: "outlined", onClick: handleClose, children: "Close" }), _jsx(Button, { variant: "contained", onClick: () => handleApplyFilters(handleClose), disabled: !hasPendingChanges, children: pendingFiltersCount === 0 && hasAppliedFilters ? 'Clear All Filters' :
|
|
148
|
+
`Apply ${pendingFiltersCount} Filter${pendingFiltersCount !== 1 ? 's' : ''}${pendingFiltersCount > 1 ? ` (${filterLogic})` : ''}` })] })] })] })) }));
|
|
149
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-custum-filter-control.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/toolbar/column-custum-filter-control.tsx"],"names":[],"mappings":"AA8BA,wBAAgB,yBAAyB,4CAyYxC"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { FilterList } from '@mui/icons-material';
|
|
3
|
+
import { Box, MenuItem, Select, FormControl, InputLabel, Button, Stack, Typography, Chip, IconButton, Divider, Badge, } from '@mui/material';
|
|
4
|
+
import { useMemo, useCallback, useEffect } from 'react';
|
|
5
|
+
import { MenuDropdown } from '../droupdown/menu-dropdown';
|
|
6
|
+
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
7
|
+
import { AddIcon, DeleteIcon, } from '../../icons';
|
|
8
|
+
import { getColumnType, isColumnFilterable } from '../../utils/column-helpers';
|
|
9
|
+
import { getSlotComponent } from '../../utils/slot-helpers';
|
|
10
|
+
import { FILTER_OPERATORS } from '../filters';
|
|
11
|
+
import { FilterValueInput } from '../filters/filter-value-input';
|
|
12
|
+
export function ColumnCustomFilterControl() {
|
|
13
|
+
const { table, slots, slotProps } = useDataTableContext();
|
|
14
|
+
const FilterIconSlot = getSlotComponent(slots, 'filterIcon', FilterList);
|
|
15
|
+
// Use the custom feature state from the table - now using pending filters for UI
|
|
16
|
+
const customFilterState = table.getCustomColumnFilterState?.() || {
|
|
17
|
+
filters: [],
|
|
18
|
+
logic: 'AND',
|
|
19
|
+
pendingFilters: [],
|
|
20
|
+
pendingLogic: 'AND'
|
|
21
|
+
};
|
|
22
|
+
// Use pending filters for the UI (draft state)
|
|
23
|
+
const filters = customFilterState.pendingFilters;
|
|
24
|
+
const filterLogic = customFilterState.pendingLogic;
|
|
25
|
+
// Active filters are the actual applied filters
|
|
26
|
+
const activeFiltersCount = table.getActiveColumnFilters?.()?.length || 0;
|
|
27
|
+
const filterableColumns = useMemo(() => {
|
|
28
|
+
return table.getAllLeafColumns()
|
|
29
|
+
.filter(column => isColumnFilterable(column));
|
|
30
|
+
}, [table]);
|
|
31
|
+
const addFilter = useCallback((columnId, operator) => {
|
|
32
|
+
// If no column specified, use empty (user will select)
|
|
33
|
+
// If column specified, get its appropriate default operator
|
|
34
|
+
let defaultOperator = operator || '';
|
|
35
|
+
if (columnId && !operator) {
|
|
36
|
+
const column = filterableColumns.find(col => col.id === columnId);
|
|
37
|
+
const columnType = getColumnType(column);
|
|
38
|
+
const operators = FILTER_OPERATORS[columnType] || FILTER_OPERATORS.text;
|
|
39
|
+
defaultOperator = operators[0]?.value || 'contains';
|
|
40
|
+
}
|
|
41
|
+
table.addPendingColumnFilter?.(columnId || '', defaultOperator, '');
|
|
42
|
+
}, [table, filterableColumns]);
|
|
43
|
+
const handleAddFilter = useCallback(() => {
|
|
44
|
+
addFilter();
|
|
45
|
+
}, [addFilter]);
|
|
46
|
+
const updateFilter = useCallback((filterId, updates) => {
|
|
47
|
+
table.updatePendingColumnFilter?.(filterId, updates);
|
|
48
|
+
}, [table]);
|
|
49
|
+
const removeFilter = useCallback((filterId) => {
|
|
50
|
+
table.removePendingColumnFilter?.(filterId);
|
|
51
|
+
}, [table]);
|
|
52
|
+
const clearAllFilters = useCallback((closeDialog) => {
|
|
53
|
+
// Clear all pending filters
|
|
54
|
+
table.clearAllPendingColumnFilters?.();
|
|
55
|
+
// Immediately apply the clear (which will clear active filters too)
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
table.applyPendingColumnFilters?.();
|
|
58
|
+
// Close dialog if callback provided
|
|
59
|
+
if (closeDialog) {
|
|
60
|
+
closeDialog();
|
|
61
|
+
}
|
|
62
|
+
}, 0);
|
|
63
|
+
}, [table]);
|
|
64
|
+
// Handle filter logic change (AND/OR)
|
|
65
|
+
const handleLogicChange = useCallback((newLogic) => {
|
|
66
|
+
table.setPendingFilterLogic?.(newLogic);
|
|
67
|
+
}, [table]);
|
|
68
|
+
// Apply all pending filters
|
|
69
|
+
const applyFilters = useCallback(() => {
|
|
70
|
+
table.applyPendingColumnFilters?.();
|
|
71
|
+
}, [table]);
|
|
72
|
+
// Handle apply button click
|
|
73
|
+
const handleApplyFilters = useCallback((closeDialog) => {
|
|
74
|
+
applyFilters();
|
|
75
|
+
closeDialog();
|
|
76
|
+
}, [applyFilters]);
|
|
77
|
+
const getOperatorsForColumn = useCallback((columnId) => {
|
|
78
|
+
const column = filterableColumns.find(col => col.id === columnId);
|
|
79
|
+
const type = getColumnType(column);
|
|
80
|
+
return FILTER_OPERATORS[type] || FILTER_OPERATORS.text;
|
|
81
|
+
}, [filterableColumns]);
|
|
82
|
+
// Handle column selection change
|
|
83
|
+
const handleColumnChange = useCallback((filterId, newColumnId, currentFilter) => {
|
|
84
|
+
const newColumn = filterableColumns.find(col => col.id === newColumnId);
|
|
85
|
+
const columnType = getColumnType(newColumn);
|
|
86
|
+
const operators = FILTER_OPERATORS[columnType] || FILTER_OPERATORS.text;
|
|
87
|
+
// Only reset operator if current operator is not valid for new column type
|
|
88
|
+
const currentOperatorValid = operators.some(op => op.value === currentFilter.operator);
|
|
89
|
+
const newOperator = currentOperatorValid ? currentFilter.operator : operators[0]?.value || '';
|
|
90
|
+
updateFilter(filterId, {
|
|
91
|
+
columnId: newColumnId,
|
|
92
|
+
operator: newOperator,
|
|
93
|
+
// Keep the current value unless operator is empty/notEmpty
|
|
94
|
+
value: ['isEmpty', 'isNotEmpty'].includes(newOperator) ? '' : currentFilter.value,
|
|
95
|
+
});
|
|
96
|
+
}, [filterableColumns, updateFilter]);
|
|
97
|
+
// Handle operator selection change
|
|
98
|
+
const handleOperatorChange = useCallback((filterId, newOperator, currentFilter) => {
|
|
99
|
+
updateFilter(filterId, {
|
|
100
|
+
operator: newOperator,
|
|
101
|
+
// Only reset value if operator is empty/notEmpty, otherwise preserve it
|
|
102
|
+
value: ['isEmpty', 'isNotEmpty'].includes(newOperator) ? '' : currentFilter.value,
|
|
103
|
+
});
|
|
104
|
+
}, [updateFilter]);
|
|
105
|
+
// Handle filter value change
|
|
106
|
+
const handleFilterValueChange = useCallback((filterId, value) => {
|
|
107
|
+
updateFilter(filterId, { value });
|
|
108
|
+
}, [updateFilter]);
|
|
109
|
+
// Handle filter removal
|
|
110
|
+
const handleRemoveFilter = useCallback((filterId) => {
|
|
111
|
+
removeFilter(filterId);
|
|
112
|
+
}, [removeFilter]);
|
|
113
|
+
// Count pending filters that are ready to apply (have column, operator, and value OR are empty/notEmpty operators)
|
|
114
|
+
const pendingFiltersCount = filters.filter(f => {
|
|
115
|
+
if (!f.columnId || !f.operator)
|
|
116
|
+
return false;
|
|
117
|
+
// For empty/notEmpty operators, no value is needed
|
|
118
|
+
if (['isEmpty', 'isNotEmpty'].includes(f.operator))
|
|
119
|
+
return true;
|
|
120
|
+
// For other operators, value is required
|
|
121
|
+
return f.value && f.value.toString().trim() !== '';
|
|
122
|
+
}).length;
|
|
123
|
+
// Check if we need to show "Clear Applied Filters" button
|
|
124
|
+
const hasAppliedFilters = activeFiltersCount > 0;
|
|
125
|
+
// Determine if there are pending changes that can be applied
|
|
126
|
+
const hasPendingChanges = pendingFiltersCount > 0 || (filters.length === 0 && hasAppliedFilters);
|
|
127
|
+
// Auto-add default filter when opening if no filters exist AND no applied filters
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
console.log('🔍 filters', filters);
|
|
130
|
+
if (filters.length === 0 && filterableColumns.length > 0 && activeFiltersCount === 0) {
|
|
131
|
+
const firstColumn = filterableColumns[0];
|
|
132
|
+
const columnType = getColumnType(firstColumn);
|
|
133
|
+
const operators = FILTER_OPERATORS[columnType] || FILTER_OPERATORS.text;
|
|
134
|
+
const defaultOperator = operators[0]?.value || 'contains';
|
|
135
|
+
// Add default filter with first column and its first operator
|
|
136
|
+
addFilter(firstColumn.id, defaultOperator);
|
|
137
|
+
}
|
|
138
|
+
}, [filters.length, filterableColumns, addFilter, activeFiltersCount]);
|
|
139
|
+
return (_jsx(MenuDropdown, { anchor: (_jsx(Badge, { variant: "dot", color: "primary", invisible: activeFiltersCount === 0, children: _jsx(IconButton, { size: "small", color: activeFiltersCount > 0 ? 'primary' : 'default', sx: {
|
|
140
|
+
flexShrink: 0,
|
|
141
|
+
}, children: _jsx(FilterIconSlot, { ...slotProps?.filterIcon }) }) })), children: ({ handleClose }) => (_jsxs(Box, { sx: {
|
|
142
|
+
p: 2,
|
|
143
|
+
minWidth: 500,
|
|
144
|
+
maxWidth: 650,
|
|
145
|
+
}, children: [_jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", sx: { mb: 2 }, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Typography, { variant: "subtitle1", sx: { fontWeight: 'bold' }, children: "Column Filters" }), activeFiltersCount > 0 && (_jsx(Chip, { size: "small", label: `${activeFiltersCount} active`, color: "primary", variant: "outlined" }))] }), _jsx(Stack, { direction: "row", spacing: 1, children: _jsx(Button, { size: "small", variant: "outlined", startIcon: _jsx(AddIcon, {}), onClick: handleAddFilter, children: "Add Filter" }) })] }), filters.length > 1 && (_jsxs(Box, { sx: { mb: 2 }, children: [_jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "Filter Logic:" }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Button, { size: "small", variant: filterLogic === 'AND' ? 'contained' : 'outlined', onClick: () => handleLogicChange('AND'), children: "AND (All conditions)" }), _jsx(Button, { size: "small", variant: filterLogic === 'OR' ? 'contained' : 'outlined', onClick: () => handleLogicChange('OR'), children: "OR (Any condition)" })] })] })), _jsx(Divider, { sx: { mb: 2 } }), filters.length === 0 ? (_jsx(Typography, { variant: "body2", color: "text.secondary", sx: {
|
|
146
|
+
textAlign: 'center',
|
|
147
|
+
py: 2,
|
|
148
|
+
}, children: "No filters applied. Click \"Add Filter\" to start." })) : (_jsx(Stack, { spacing: 2, children: filters.map((filter) => (_jsx(Box, { children: _jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsxs(FormControl, { size: "small", sx: { minWidth: 140 }, children: [_jsx(InputLabel, { children: "Column" }), _jsx(Select, { value: filter.columnId, label: "Column", onChange: (e) => handleColumnChange(filter.id, e.target.value, filter), children: filterableColumns.map(column => (_jsx(MenuItem, { value: column.id, children: typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id }, column.id))) })] }), _jsxs(FormControl, { size: "small", sx: { minWidth: 160 }, disabled: !filter.columnId, children: [_jsx(InputLabel, { children: "Operator" }), _jsx(Select, { value: filter.operator, label: "Operator", onChange: (e) => handleOperatorChange(filter.id, e.target.value, filter), children: getOperatorsForColumn(filter.columnId).map(op => (_jsx(MenuItem, { value: op.value, children: op.label }, op.value))) })] }), !['isEmpty', 'isNotEmpty'].includes(filter.operator) ? (_jsx(FilterValueInput, { filter: filter, column: filterableColumns.find(col => col.id === filter.columnId), onValueChange: (value) => handleFilterValueChange(filter.id, value) })) : (_jsx(Box, { sx: { flex: 1 } }) /* Spacer when no value input needed */), _jsx(IconButton, { size: "small", onClick: () => handleRemoveFilter(filter.id), color: "error", children: _jsx(DeleteIcon, {}) })] }) }, filter.id))) })), _jsx(Divider, { sx: { my: 2 } }), _jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", spacing: 1, children: [(hasAppliedFilters || filters.length > 0) && (_jsx(Button, { size: "small", variant: "text", color: "warning", onClick: () => clearAllFilters(handleClose), startIcon: _jsx(DeleteIcon, {}), children: "Reset" })), !(hasAppliedFilters || filters.length > 0) && _jsx(Box, {}), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Button, { variant: "outlined", onClick: handleClose, children: "Close" }), _jsx(Button, { variant: "contained", onClick: () => handleApplyFilters(handleClose), disabled: !hasPendingChanges, children: pendingFiltersCount === 0 && hasAppliedFilters ? 'Clear All Filters' :
|
|
149
|
+
`Apply ${pendingFiltersCount} Filter${pendingFiltersCount !== 1 ? 's' : ''}${pendingFiltersCount > 1 ? ` (${filterLogic})` : ''}` })] })] })] })) }));
|
|
150
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-pinning-control.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/toolbar/column-pinning-control.tsx"],"names":[],"mappings":"AAsBA,wBAAgB,oBAAoB,4CA6QnC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ArrowLeftOutlined, ArrowRightOutlined, PushPinOutlined } from '@mui/icons-material';
|
|
3
|
+
import { Box, Typography, Divider, IconButton, Tooltip, List, ListItem, ListItemText, } from '@mui/material';
|
|
4
|
+
import { useCallback, useMemo } from 'react';
|
|
5
|
+
import { MenuDropdown } from '../droupdown/menu-dropdown';
|
|
6
|
+
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
7
|
+
import { UnpinIcon, } from '../../icons';
|
|
8
|
+
import { getSlotComponent } from '../../utils/slot-helpers';
|
|
9
|
+
export function ColumnPinningControl() {
|
|
10
|
+
// Use context if no props provided (MUI DataGrid style)
|
|
11
|
+
const { table, slots, slotProps } = useDataTableContext();
|
|
12
|
+
const PinIconSlot = getSlotComponent(slots, 'pinIcon', PushPinOutlined);
|
|
13
|
+
const UnpinIconSlot = getSlotComponent(slots, 'unpinIcon', UnpinIcon);
|
|
14
|
+
const LeftIconSlot = getSlotComponent(slots, 'leftIcon', ArrowLeftOutlined);
|
|
15
|
+
const RightIconSlot = getSlotComponent(slots, 'rightIcon', ArrowRightOutlined);
|
|
16
|
+
const columnPinning = table.getState().columnPinning;
|
|
17
|
+
const allColumns = useMemo(() => {
|
|
18
|
+
if (slotProps?.columnsPanel?.getPinnableColumns) {
|
|
19
|
+
return slotProps?.columnsPanel?.getPinnableColumns(table.getAllLeafColumns());
|
|
20
|
+
}
|
|
21
|
+
return table.getAllLeafColumns().filter(column => column.getCanPin());
|
|
22
|
+
}, [slotProps?.columnsPanel, table]);
|
|
23
|
+
const handlePinColumn = (columnId, position) => {
|
|
24
|
+
const currentPinning = table.getState().columnPinning;
|
|
25
|
+
const newPinning = { ...currentPinning };
|
|
26
|
+
// Remove from current position
|
|
27
|
+
newPinning.left = (newPinning.left || []).filter(id => id !== columnId);
|
|
28
|
+
newPinning.right = (newPinning.right || []).filter(id => id !== columnId);
|
|
29
|
+
// Add to new position
|
|
30
|
+
if (position === 'left') {
|
|
31
|
+
newPinning.left = [...(newPinning.left || []), columnId];
|
|
32
|
+
}
|
|
33
|
+
else if (position === 'right') {
|
|
34
|
+
newPinning.right = [...(newPinning.right || []), columnId];
|
|
35
|
+
}
|
|
36
|
+
table.setColumnPinning(newPinning);
|
|
37
|
+
};
|
|
38
|
+
const getColumnPinStatus = (columnId) => {
|
|
39
|
+
if (columnPinning.left?.includes(columnId))
|
|
40
|
+
return 'left';
|
|
41
|
+
if (columnPinning.right?.includes(columnId))
|
|
42
|
+
return 'right';
|
|
43
|
+
return 'none';
|
|
44
|
+
};
|
|
45
|
+
const getColumnDisplayName = (column) => {
|
|
46
|
+
if (typeof column.columnDef.header === 'string') {
|
|
47
|
+
return column.columnDef.header;
|
|
48
|
+
}
|
|
49
|
+
return column.id;
|
|
50
|
+
};
|
|
51
|
+
const handleUnpinAll = useCallback(() => {
|
|
52
|
+
table.setColumnPinning(table.initialState.columnPinning || {});
|
|
53
|
+
}, [table]);
|
|
54
|
+
// Count only user-pinned columns (exclude system columns like select and action)
|
|
55
|
+
const userPinnedLeft = (columnPinning.left?.filter((id) => allColumns.some((column) => column.id === id)) || []);
|
|
56
|
+
const userPinnedRight = (columnPinning.right?.filter((id) => allColumns.some((column) => column.id === id)) || []);
|
|
57
|
+
const totalPinned = userPinnedLeft.length + userPinnedRight.length;
|
|
58
|
+
return (_jsx(MenuDropdown, { anchor: (_jsx(Tooltip, { title: "Pin columns", children: _jsxs(IconButton, { size: "small", sx: {
|
|
59
|
+
flexShrink: 0,
|
|
60
|
+
}, children: [_jsx(PinIconSlot, { ...slotProps?.pinIcon }), totalPinned > 0 && (_jsx(Box, { sx: {
|
|
61
|
+
position: 'absolute',
|
|
62
|
+
top: -2,
|
|
63
|
+
right: -2,
|
|
64
|
+
backgroundColor: 'primary.main',
|
|
65
|
+
color: 'white',
|
|
66
|
+
borderRadius: '50%',
|
|
67
|
+
width: 16,
|
|
68
|
+
height: 16,
|
|
69
|
+
fontSize: 10,
|
|
70
|
+
display: 'flex',
|
|
71
|
+
alignItems: 'center',
|
|
72
|
+
justifyContent: 'center',
|
|
73
|
+
}, children: totalPinned }))] }) })), children: ({ handleClose }) => (_jsxs(Box, { sx: {
|
|
74
|
+
minWidth: 300,
|
|
75
|
+
maxHeight: 400,
|
|
76
|
+
overflow: 'auto',
|
|
77
|
+
}, children: [_jsxs(Box, { sx: {
|
|
78
|
+
p: 2,
|
|
79
|
+
pb: 1,
|
|
80
|
+
}, children: [_jsx(Typography, { variant: "subtitle2", sx: { mb: 1 }, children: "Pin Columns" }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: "Pin columns to keep them visible while scrolling" })] }), _jsx(Divider, {}), _jsx(List, { dense: true, sx: { py: 0 }, children: allColumns.map((column) => {
|
|
81
|
+
const pinStatus = getColumnPinStatus(column.id);
|
|
82
|
+
const displayName = getColumnDisplayName(column);
|
|
83
|
+
return (_jsx(ListItem, { sx: { py: 0.5 }, secondaryAction: (_jsxs(Box, { sx: {
|
|
84
|
+
display: 'flex',
|
|
85
|
+
gap: 0.5,
|
|
86
|
+
}, children: [_jsx(Tooltip, { title: "Pin left", children: _jsx(IconButton, { size: "small", onClick: () => handlePinColumn(column.id, pinStatus === 'left' ? 'none' : 'left'), color: pinStatus === 'left' ? 'primary' : 'default', children: _jsx(LeftIconSlot, { fontSize: "small", ...slotProps?.leftIcon }) }) }), _jsx(Tooltip, { title: "Pin right", children: _jsx(IconButton, { size: "small", onClick: () => handlePinColumn(column.id, pinStatus === 'right' ? 'none' : 'right'), color: pinStatus === 'right' ? 'secondary' : 'default', children: _jsx(RightIconSlot, { fontSize: "small", ...slotProps?.rightIcon }) }) }), pinStatus !== 'none' && (_jsx(Tooltip, { title: "Unpin", children: _jsx(IconButton, { size: "small", onClick: () => handlePinColumn(column.id, 'none'), children: _jsx(UnpinIconSlot, { fontSize: "small", ...slotProps?.unpinIcon }) }) }))] })), children: _jsx(ListItemText, { primary: displayName, secondary: pinStatus !== 'none' ? (_jsxs(Box, { sx: {
|
|
87
|
+
display: 'flex',
|
|
88
|
+
alignItems: 'center',
|
|
89
|
+
gap: 0.5,
|
|
90
|
+
mt: 0.5,
|
|
91
|
+
}, children: [pinStatus === 'left' ? (_jsx(LeftIconSlot, { fontSize: "small", ...slotProps?.leftIcon })) : (_jsx(RightIconSlot, { fontSize: "small", ...slotProps?.rightIcon })), _jsxs(Typography, { variant: "caption", color: pinStatus === 'left' ? 'primary' : 'secondary', children: ["Pinned", ' ', pinStatus] })] })) : null }) }, column.id));
|
|
92
|
+
}) }), _jsx(Divider, {}), _jsx(Box, { sx: {
|
|
93
|
+
p: 2,
|
|
94
|
+
pt: 1,
|
|
95
|
+
}, children: _jsxs(Box, { sx: {
|
|
96
|
+
display: 'flex',
|
|
97
|
+
gap: 1,
|
|
98
|
+
justifyContent: 'space-between',
|
|
99
|
+
}, children: [_jsx(Typography, { variant: "caption", color: "text.secondary", children: "Quick actions:" }), _jsx(Box, { sx: {
|
|
100
|
+
display: 'flex',
|
|
101
|
+
gap: 1,
|
|
102
|
+
}, children: _jsx(Tooltip, { title: "Unpin all user columns", children: _jsx(IconButton, { size: "small", onClick: handleUnpinAll, disabled: totalPinned === 0, children: _jsx(UnpinIcon, { fontSize: "small" }) }) }) })] }) })] })) }));
|
|
103
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-reset-control.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/toolbar/column-reset-control.tsx"],"names":[],"mappings":"AAMA,wBAAgB,kBAAkB,4CAmBjC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Autorenew } from '@mui/icons-material';
|
|
3
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
4
|
+
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
5
|
+
export function ColumnResetControl() {
|
|
6
|
+
const { table } = useDataTableContext();
|
|
7
|
+
const handleResetLayout = () => {
|
|
8
|
+
table.resetColumnOrder();
|
|
9
|
+
table.resetColumnPinning();
|
|
10
|
+
table.resetColumnSizing();
|
|
11
|
+
};
|
|
12
|
+
return (_jsx(Tooltip, { title: "Reset layout", children: _jsx(IconButton, { size: "small", onClick: handleResetLayout, children: _jsx(Autorenew, {}) }) }));
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-visibility-control.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/toolbar/column-visibility-control.tsx"],"names":[],"mappings":"AASA,wBAAgB,uBAAuB,4CAqEtC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ViewColumnOutlined } from '@mui/icons-material';
|
|
3
|
+
import { Box, Checkbox, Divider, FormControlLabel, FormGroup, IconButton, Tooltip, Typography } from '@mui/material';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { MenuDropdown } from '../droupdown/menu-dropdown';
|
|
6
|
+
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
7
|
+
import { getSlotComponent } from '../../utils/slot-helpers';
|
|
8
|
+
export function ColumnVisibilityControl() {
|
|
9
|
+
// Use context if no props provided (MUI DataGrid style)
|
|
10
|
+
const { table, slots, slotProps } = useDataTableContext();
|
|
11
|
+
const ColumnIconSlot = getSlotComponent(slots, 'columnIcon', ViewColumnOutlined);
|
|
12
|
+
const columns = useMemo(() => {
|
|
13
|
+
if (slotProps?.columnsPanel?.getTogglableColumns) {
|
|
14
|
+
return slotProps?.columnsPanel?.getTogglableColumns(table.getAllLeafColumns());
|
|
15
|
+
}
|
|
16
|
+
return table.getAllLeafColumns().filter(column => column.getCanHide());
|
|
17
|
+
}, [slotProps?.columnsPanel, table]);
|
|
18
|
+
const handleColumnVisibilityChange = ((columnId, visible) => {
|
|
19
|
+
table.getColumn(columnId)?.toggleVisibility(visible);
|
|
20
|
+
});
|
|
21
|
+
return (_jsx(MenuDropdown, { anchor: (_jsx(Tooltip, { title: "Column visibility", children: _jsx(IconButton, { size: "small", sx: {
|
|
22
|
+
flexShrink: 0,
|
|
23
|
+
}, children: _jsx(ColumnIconSlot, { ...slotProps?.columnIcon }) }) })), children: ({ handleClose }) => (_jsxs(Box, { sx: {
|
|
24
|
+
p: 2,
|
|
25
|
+
minWidth: 200,
|
|
26
|
+
}, children: [_jsx(Typography, { variant: "subtitle2", sx: { mb: 1 }, children: "Show/Hide Columns" }), _jsx(Divider, { sx: { mb: 1 } }), _jsx(FormGroup, { children: columns.map((column) => (_jsx(FormControlLabel, { control: (_jsx(Checkbox, { disabled: !column.getCanHide(), checked: column.getIsVisible(), onChange: (e) => handleColumnVisibilityChange(column.id, e.target.checked), size: "small" })), label: typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id }, column.id))) })] })) }));
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-table-toolbar.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/toolbar/data-table-toolbar.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAalC,MAAM,WAAW,qBAAqB;IAClC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAEjC;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,WAAkB,EAClB,kBAAyB,EACzB,KAAK,EACL,QAAQ,EACR,sBAA6B,EAC7B,YAAmB,EACnB,WAAkB,EAClB,kBAAyB,EACzB,sBAA6B,EAC7B,mBAA0B,GAC7B,EAAE,qBAAqB,2CAqFvB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Stack, Toolbar, Typography, Box, } from '@mui/material';
|
|
3
|
+
import { ColumnCustomFilterControl } from './column-custom-filter-control';
|
|
4
|
+
import { ColumnPinningControl } from './column-pinning-control';
|
|
5
|
+
import { ColumnResetControl } from './column-reset-control';
|
|
6
|
+
import { ColumnVisibilityControl } from './column-visibility-control';
|
|
7
|
+
import { TableExportControl } from './table-export-control';
|
|
8
|
+
import { TableSearchControl } from './table-search-control';
|
|
9
|
+
import { TableSizeControl } from './table-size-control';
|
|
10
|
+
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
11
|
+
import { getSlotComponent } from '../../utils/slot-helpers';
|
|
12
|
+
export function DataTableToolbar({ extraFilter = null, enableGlobalFilter = true, title, subtitle, enableColumnVisibility = true, enableExport = true, enableReset = true, enableColumnFilter = true, enableTableSizeControl = true, enableColumnPinning = true, }) {
|
|
13
|
+
const { table, slots, slotProps = {} } = useDataTableContext();
|
|
14
|
+
const ToolbarSlot = getSlotComponent(slots, 'toolbar', Toolbar);
|
|
15
|
+
const TableSearchControlSlot = getSlotComponent(slots, 'searchInput', TableSearchControl);
|
|
16
|
+
const TableSizeControlSlot = getSlotComponent(slots, 'tableSizeControl', TableSizeControl);
|
|
17
|
+
const ColumnCustomFilterControlSlot = getSlotComponent(slots, 'columnCustomFilterControl', ColumnCustomFilterControl);
|
|
18
|
+
const ColumnPinningControlSlot = getSlotComponent(slots, 'columnPinningControl', ColumnPinningControl);
|
|
19
|
+
const ColumnVisibilityControlSlot = getSlotComponent(slots, 'columnVisibilityControl', ColumnVisibilityControl);
|
|
20
|
+
const ColumnResetControlSlot = getSlotComponent(slots, 'resetButton', ColumnResetControl);
|
|
21
|
+
const TableExportControlSlot = getSlotComponent(slots, 'exportButton', TableExportControl);
|
|
22
|
+
return (_jsx(ToolbarSlot, { table: table, ...slotProps.toolbar, children: _jsxs(Box, { sx: { width: '100%' }, children: [(title || subtitle) ? (_jsxs(Box, { sx: { mb: 2 }, children: [title ? (_jsx(Typography, { variant: "h6", component: "div", children: title })) : null, subtitle ? (_jsx(Typography, { variant: "body2", color: "text.secondary", children: subtitle })) : null] })) : null, _jsxs(Stack, { direction: "row", spacing: 2, justifyContent: "space-between", alignItems: "center", children: [_jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", sx: { flex: 1 }, children: [enableGlobalFilter ? _jsx(TableSearchControlSlot, { ...slotProps.searchInput }) : null, enableTableSizeControl ? _jsx(TableSizeControlSlot, { ...slotProps.tableSizeControl }) : null, enableColumnFilter ? _jsx(ColumnCustomFilterControlSlot, { ...slotProps.columnCustomFilterControl }) : null, enableColumnPinning ? _jsx(ColumnPinningControlSlot, { ...slotProps.columnPinningControl }) : null, enableColumnVisibility ? _jsx(ColumnVisibilityControlSlot, { ...slotProps.columnVisibilityControl }) : null, enableReset ? _jsx(ColumnResetControlSlot, { ...slotProps.resetButton }) : null, enableExport ? _jsx(TableExportControlSlot, { ...slotProps.exportButton }) : null] }), _jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", children: extraFilter })] })] }) }));
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/toolbar/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toolbar components for DataTable
|
|
3
|
+
*/
|
|
4
|
+
// Main toolbar component
|
|
5
|
+
export { DataTableToolbar } from './data-table-toolbar';
|
|
6
|
+
// Individual toolbar building blocks - export for custom toolbars
|
|
7
|
+
export { ColumnVisibilityControl } from './column-visibility-control';
|
|
8
|
+
export { ColumnPinningControl } from './column-pinning-control';
|
|
9
|
+
export { ColumnResetControl } from './column-reset-control';
|
|
10
|
+
export { TableExportControl } from './table-export-control';
|
|
11
|
+
export { TableSizeControl } from './table-size-control';
|
|
12
|
+
// Bulk actions
|
|
13
|
+
export { BulkActionsToolbar } from './bulk-actions-toolbar';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-export-control.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/toolbar/table-export-control.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAG,YAAY,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEzE,UAAU,uBAAuB;IAE7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1H,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACxG,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/F,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACtE;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,cAAc,EAAE,mBAAmB,EACnC,cAAc,EAAE,mBAAmB,EACnC,gBAAgB,EAAE,qBAAqB,EACvC,gBAAgB,EAAE,qBAAqB,EACvC,aAAa,EAAE,kBAAkB,GACpC,GAAE,uBAA4B,2CA+M9B"}
|