react-restyle-components 0.4.41 → 0.4.43
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/lib/src/core-components/src/components/AutoComplete/auto-complete-filter-group-by-multiple-select-multiple-fields-display/auto-complete-filter-group-by-multiple-select-multiple-fields-display.component.d.ts +9 -1
- package/lib/src/core-components/src/components/AutoComplete/auto-complete-filter-group-by-multiple-select-multiple-fields-display/auto-complete-filter-group-by-multiple-select-multiple-fields-display.component.js +165 -48
- package/lib/src/core-components/src/components/Icon/Icon.d.ts +9 -0
- package/lib/src/core-components/src/components/Icon/Icon.js +80 -0
- package/lib/src/core-components/src/components/Table/Table.js +141 -43
- package/lib/src/core-components/src/components/Table/elements.d.ts +3 -0
- package/lib/src/core-components/src/components/Table/elements.js +56 -0
- package/lib/src/core-components/src/components/Table/types.d.ts +2 -0
- package/lib/src/core-components/src/components/ag-grid/AgGrid.js +332 -8
- package/lib/src/core-components/src/components/ag-grid/elements.d.ts +136 -0
- package/lib/src/core-components/src/components/ag-grid/elements.js +639 -1
- package/lib/src/core-components/src/components/ag-grid/hooks.d.ts +80 -0
- package/lib/src/core-components/src/components/ag-grid/hooks.js +277 -0
- package/lib/src/core-components/src/components/ag-grid/index.d.ts +2 -1
- package/lib/src/core-components/src/components/ag-grid/index.js +2 -0
- package/lib/src/core-components/src/tc.global.css +28 -2
- package/lib/src/core-components/src/tc.module.css +1 -1
- package/lib/src/core-components/src/utils/designTokens.d.ts +9 -9
- package/lib/src/core-components/src/utils/designTokens.js +13 -10
- package/package.json +1 -1
|
@@ -64,6 +64,12 @@ export interface UIConfig {
|
|
|
64
64
|
emptyStateMessage?: string;
|
|
65
65
|
/** Empty state description */
|
|
66
66
|
emptyStateDescription?: string;
|
|
67
|
+
/** Enable internal filtering (filters data locally without external callback) */
|
|
68
|
+
enableInternalFilter?: boolean;
|
|
69
|
+
/** Show search input for each group separately */
|
|
70
|
+
showGroupSearch?: boolean;
|
|
71
|
+
/** Minimum items in group to show group search input */
|
|
72
|
+
groupSearchMinItems?: number;
|
|
67
73
|
}
|
|
68
74
|
export interface AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplayProps {
|
|
69
75
|
/** Unique identifier field name for items */
|
|
@@ -106,5 +112,7 @@ export interface AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplayPro
|
|
|
106
112
|
style?: React.CSSProperties;
|
|
107
113
|
/** Debounce delay for filter (ms) */
|
|
108
114
|
filterDebounceDelay?: number;
|
|
115
|
+
/** Key to re-render options list without remounting whole component */
|
|
116
|
+
optionsRenderKey?: string | number;
|
|
109
117
|
}
|
|
110
|
-
export declare const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay: ({ uniqueField, groupByField, selectionConfig, displayConfig, uiConfig, loader, placeholder, data, hasError, disable, isUpperCase, name, groupByDetails, onFilter, onUpdate, onSelect, onBlur, className, style, filterDebounceDelay, }: AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplayProps) => import("react/jsx-runtime").JSX.Element;
|
|
118
|
+
export declare const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay: ({ uniqueField, groupByField, selectionConfig, displayConfig, uiConfig, loader, placeholder, data, hasError, disable, isUpperCase, name, groupByDetails, onFilter, onUpdate, onSelect, onBlur, className, style, filterDebounceDelay, optionsRenderKey, }: AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplayProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/* eslint-disable */
|
|
3
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
3
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
4
4
|
import { Icon } from '../../Icon/Icon';
|
|
5
5
|
import s from '../../../tc.module.css';
|
|
6
6
|
import { cn } from '../../../utils';
|
|
7
|
-
|
|
8
|
-
export const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay = ({ uniqueField = '_id', groupByField, selectionConfig = {}, displayConfig = {}, uiConfig = {}, loader = false, placeholder = 'Search...', data, hasError = false, disable = false, isUpperCase = false, name, groupByDetails, onFilter, onUpdate, onSelect, onBlur, className, style, filterDebounceDelay = 300, }) => {
|
|
7
|
+
export const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay = ({ uniqueField = '_id', groupByField, selectionConfig = {}, displayConfig = {}, uiConfig = {}, loader = false, placeholder = 'Search...', data, hasError = false, disable = false, isUpperCase = false, name, groupByDetails, onFilter, onUpdate, onSelect, onBlur, className, style, filterDebounceDelay = 500, optionsRenderKey, }) => {
|
|
9
8
|
// Merge configs with defaults
|
|
10
9
|
const { isSelectedStringArray = false, maxSelection, onSelectionChange, } = selectionConfig;
|
|
11
10
|
const { displayKeys, displaySeparator = ' - ', fallbackKeys = ['name', 'code'], renderItem, renderGroupName, } = displayConfig;
|
|
12
|
-
const { showGroupHeaders = true, collapsibleGroups = false, showSelectAllButtons = true, showSelectedSection = true, selectedPlaceholder, emptyStateMessage = 'No results found', emptyStateDescription = 'Try adjusting your search terms', } = uiConfig;
|
|
11
|
+
const { showGroupHeaders = true, collapsibleGroups = false, showSelectAllButtons = true, showSelectedSection = true, selectedPlaceholder, emptyStateMessage = 'No results found', emptyStateDescription = 'Try adjusting your search terms', enableInternalFilter = false, showGroupSearch = false, groupSearchMinItems = 5, } = uiConfig;
|
|
13
12
|
// Get groupByField from props or legacy groupByDetails
|
|
14
13
|
const groupField = groupByField || groupByDetails?.groupByName || 'lab';
|
|
15
14
|
const defaultGroupName = groupByDetails?.defaultGroupName || 'Default';
|
|
@@ -41,6 +40,66 @@ export const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay = ({ u
|
|
|
41
40
|
const [collapsedGroups, setCollapsedGroups] = useState(new Set());
|
|
42
41
|
const [isListOpen, setIsListOpen] = useState(false);
|
|
43
42
|
const [totalItems, setTotalItems] = useState(0);
|
|
43
|
+
// Internal filter state
|
|
44
|
+
const [internalFilterText, setInternalFilterText] = useState('');
|
|
45
|
+
const [groupFilters, setGroupFilters] = useState({});
|
|
46
|
+
// Filter items based on display text
|
|
47
|
+
const filterItems = useCallback((items, searchText) => {
|
|
48
|
+
if (!searchText || searchText.trim() === '')
|
|
49
|
+
return items;
|
|
50
|
+
const lowerSearch = searchText.toLowerCase().trim();
|
|
51
|
+
const keys = displayKeys || data.displayKey || fallbackKeys;
|
|
52
|
+
return items.filter((item) => {
|
|
53
|
+
// Search across all display keys
|
|
54
|
+
return keys.some((key) => {
|
|
55
|
+
const value = item[key];
|
|
56
|
+
if (typeof value === 'string') {
|
|
57
|
+
return value.toLowerCase().includes(lowerSearch);
|
|
58
|
+
}
|
|
59
|
+
if (typeof value === 'number') {
|
|
60
|
+
return value.toString().includes(lowerSearch);
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}, [displayKeys, data.displayKey, fallbackKeys]);
|
|
66
|
+
// Get filtered options based on internal filter and group filters
|
|
67
|
+
const getFilteredGroupedOptions = useCallback(() => {
|
|
68
|
+
const result = {};
|
|
69
|
+
const searchText = internalFilterText.trim();
|
|
70
|
+
Object.keys(groupedOptions).forEach((groupKey) => {
|
|
71
|
+
const groupItems = groupedOptions[groupKey] || [];
|
|
72
|
+
// Apply global filter first
|
|
73
|
+
let filteredItems = searchText
|
|
74
|
+
? filterItems(groupItems, searchText)
|
|
75
|
+
: groupItems;
|
|
76
|
+
// Apply group-specific filter if exists
|
|
77
|
+
const groupSearch = groupFilters[groupKey]?.trim();
|
|
78
|
+
if (groupSearch) {
|
|
79
|
+
filteredItems = filterItems(filteredItems, groupSearch);
|
|
80
|
+
}
|
|
81
|
+
// Only include groups with matching items
|
|
82
|
+
if (filteredItems.length > 0) {
|
|
83
|
+
result[groupKey] = filteredItems;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return result;
|
|
87
|
+
}, [groupedOptions, internalFilterText, groupFilters, filterItems]);
|
|
88
|
+
// Handle group-specific search
|
|
89
|
+
const handleGroupSearch = (groupName, searchValue) => {
|
|
90
|
+
setGroupFilters((prev) => ({
|
|
91
|
+
...prev,
|
|
92
|
+
[groupName]: searchValue,
|
|
93
|
+
}));
|
|
94
|
+
};
|
|
95
|
+
// Clear group filter
|
|
96
|
+
const clearGroupFilter = (groupName) => {
|
|
97
|
+
setGroupFilters((prev) => {
|
|
98
|
+
const newFilters = { ...prev };
|
|
99
|
+
delete newFilters[groupName];
|
|
100
|
+
return newFilters;
|
|
101
|
+
});
|
|
102
|
+
};
|
|
44
103
|
// Helper to get selected groups
|
|
45
104
|
const getSelectedGroups = React.useCallback(() => {
|
|
46
105
|
return Object.keys(originalGroupedOptions).reduce((acc, groupKey) => {
|
|
@@ -59,6 +118,9 @@ export const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay = ({ u
|
|
|
59
118
|
isListOpen) {
|
|
60
119
|
const selectedGroups = getSelectedGroups();
|
|
61
120
|
onSelect && onSelect(selectedGroups);
|
|
121
|
+
// Call onUpdate when user clicks outside
|
|
122
|
+
const flatList = getFlatListFromGroupedOptions(groupedOptions);
|
|
123
|
+
onUpdate && onUpdate(flatList);
|
|
62
124
|
setIsListOpen(false);
|
|
63
125
|
setValue('');
|
|
64
126
|
}
|
|
@@ -67,31 +129,22 @@ export const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay = ({ u
|
|
|
67
129
|
return () => {
|
|
68
130
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
69
131
|
};
|
|
70
|
-
}, [
|
|
132
|
+
}, [
|
|
133
|
+
ref,
|
|
134
|
+
isListOpen,
|
|
135
|
+
getSelectedGroups,
|
|
136
|
+
onSelect,
|
|
137
|
+
groupedOptions,
|
|
138
|
+
getFlatListFromGroupedOptions,
|
|
139
|
+
onUpdate,
|
|
140
|
+
]);
|
|
71
141
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Object.keys(groupedOptions).reduce((acc: any, groupKey: string) => {
|
|
79
|
-
// const selectedItems = (groupedOptions[groupKey] || []).filter(
|
|
80
|
-
// (item) => item.selected
|
|
81
|
-
// );
|
|
82
|
-
// if (selectedItems.length > 0) {
|
|
83
|
-
// acc[groupKey] = selectedItems;
|
|
84
|
-
// }
|
|
85
|
-
// return acc;
|
|
86
|
-
// }, {})
|
|
87
|
-
// );
|
|
88
|
-
const list = Array.isArray(groupedOptions)
|
|
89
|
-
? groupedOptions.flat(Number.POSITIVE_INFINITY)
|
|
90
|
-
: Object.values(groupedOptions).flat(Number.POSITIVE_INFINITY);
|
|
91
|
-
onUpdate && onUpdate(list);
|
|
92
|
-
prevGroupedOptionsRef.current = groupedOptions;
|
|
93
|
-
}
|
|
94
|
-
}, [groupedOptions, onUpdate]);
|
|
142
|
+
// Helper to get flat list from grouped options for onUpdate callback
|
|
143
|
+
const getFlatListFromGroupedOptions = useCallback((grouped) => {
|
|
144
|
+
return Array.isArray(grouped)
|
|
145
|
+
? grouped.flat(Number.POSITIVE_INFINITY)
|
|
146
|
+
: Object.values(grouped).flat(Number.POSITIVE_INFINITY);
|
|
147
|
+
}, []);
|
|
95
148
|
const wrapperRef = useRef(null);
|
|
96
149
|
useOutsideAlerter(wrapperRef);
|
|
97
150
|
// Process grouped data and mark selected items
|
|
@@ -122,6 +175,19 @@ export const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay = ({ u
|
|
|
122
175
|
setTotalItems(totalCount);
|
|
123
176
|
return processedGroups;
|
|
124
177
|
};
|
|
178
|
+
// Compute a key from data.list to detect changes (helps with MobX observables)
|
|
179
|
+
const dataListKey = React.useMemo(() => {
|
|
180
|
+
if (!data.list)
|
|
181
|
+
return '';
|
|
182
|
+
if (Array.isArray(data.list)) {
|
|
183
|
+
return data.list.length.toString();
|
|
184
|
+
}
|
|
185
|
+
// For grouped data, create a key from group names and item counts
|
|
186
|
+
const groups = Object.keys(data.list);
|
|
187
|
+
return groups
|
|
188
|
+
.map((g) => `${g}:${data.list[g]?.length || 0}`)
|
|
189
|
+
.join(',');
|
|
190
|
+
}, [data.list]);
|
|
125
191
|
useEffect(() => {
|
|
126
192
|
// Handle both grouped data structure and regular list structure
|
|
127
193
|
let processedData = {};
|
|
@@ -154,25 +220,57 @@ export const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay = ({ u
|
|
|
154
220
|
? data.selected
|
|
155
221
|
: allSelected);
|
|
156
222
|
}, [
|
|
157
|
-
data,
|
|
223
|
+
data.list,
|
|
158
224
|
data.selected,
|
|
159
225
|
isSelectedStringArray,
|
|
160
226
|
uniqueField,
|
|
161
227
|
groupField,
|
|
162
228
|
defaultGroupName,
|
|
229
|
+
dataListKey, // Computed key to detect data.list changes
|
|
163
230
|
]);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
231
|
+
// Store onFilter in ref to avoid recreating debounce when callback changes
|
|
232
|
+
const onFilterRef = useRef(onFilter);
|
|
233
|
+
onFilterRef.current = onFilter;
|
|
234
|
+
// Store timeout ref for proper cleanup
|
|
235
|
+
const debounceTimeoutRef = useRef(null);
|
|
236
|
+
// Cleanup timeout on unmount
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
return () => {
|
|
239
|
+
if (debounceTimeoutRef.current) {
|
|
240
|
+
clearTimeout(debounceTimeoutRef.current);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}, []);
|
|
244
|
+
// Debounced filter using useCallback with stable reference
|
|
245
|
+
const debouncedFilter = useCallback((search) => {
|
|
246
|
+
if (!onFilterRef.current)
|
|
247
|
+
return;
|
|
248
|
+
// Clear any existing timeout
|
|
249
|
+
if (debounceTimeoutRef.current) {
|
|
250
|
+
clearTimeout(debounceTimeoutRef.current);
|
|
251
|
+
}
|
|
252
|
+
// Set new timeout
|
|
253
|
+
debounceTimeoutRef.current = setTimeout(() => {
|
|
254
|
+
onFilterRef.current?.(search);
|
|
255
|
+
}, filterDebounceDelay);
|
|
256
|
+
}, [filterDebounceDelay]);
|
|
167
257
|
const onChange = (e) => {
|
|
168
258
|
const search = e.target.value;
|
|
169
259
|
setValue(search);
|
|
260
|
+
// Use internal filtering if enabled
|
|
261
|
+
if (enableInternalFilter) {
|
|
262
|
+
setInternalFilterText(search);
|
|
263
|
+
}
|
|
264
|
+
// Also call external filter if provided
|
|
170
265
|
debouncedFilter(search);
|
|
171
266
|
};
|
|
172
267
|
const onKeyUp = (e) => {
|
|
173
268
|
const charCode = e.which ? e.which : e.keyCode;
|
|
174
269
|
if (charCode === 8) {
|
|
175
270
|
const search = e.currentTarget.value;
|
|
271
|
+
if (enableInternalFilter) {
|
|
272
|
+
setInternalFilterText(search);
|
|
273
|
+
}
|
|
176
274
|
debouncedFilter(search);
|
|
177
275
|
}
|
|
178
276
|
};
|
|
@@ -267,29 +365,45 @@ export const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay = ({ u
|
|
|
267
365
|
});
|
|
268
366
|
};
|
|
269
367
|
const renderGroupedOptions = () => {
|
|
270
|
-
|
|
368
|
+
// Use filtered options instead of raw groupedOptions
|
|
369
|
+
const filteredOptions = getFilteredGroupedOptions();
|
|
370
|
+
const groupNames = Object.keys(filteredOptions).sort();
|
|
271
371
|
if (groupNames.length === 0) {
|
|
272
372
|
return (_jsxs("div", { className: cn(s['p-4'], s['text-center'], s['text-gray-500']), children: [_jsx(Icon, { nameIcon: "FaSearch", propsIcon: {
|
|
273
373
|
size: 24,
|
|
274
374
|
color: '#ccc',
|
|
275
|
-
} }), _jsx("p", { className: cn(s['mt-2'], s['text-sm']), children: emptyStateMessage }), _jsx("p", { className: cn(s['text-xs']), children: emptyStateDescription })
|
|
375
|
+
} }), _jsx("p", { className: cn(s['mt-2'], s['text-sm']), children: emptyStateMessage }), _jsx("p", { className: cn(s['text-xs']), children: emptyStateDescription }), internalFilterText && (_jsx("button", { type: "button", onClick: () => {
|
|
376
|
+
setInternalFilterText('');
|
|
377
|
+
setValue('');
|
|
378
|
+
}, className: cn(s['mt-3'], s['text-xs'], s['px-3'], s['py-1'], s['bg-blue-500'], s['text-white'], s['rounded'], s['hover:bg-blue-600']), children: "Clear Search" }))] }));
|
|
276
379
|
}
|
|
277
380
|
return groupNames.map((groupName) => {
|
|
278
|
-
const groupItems =
|
|
381
|
+
const groupItems = filteredOptions[groupName] || [];
|
|
382
|
+
// Get original group items for accurate counts
|
|
383
|
+
const originalGroupItems = groupedOptions[groupName] || [];
|
|
279
384
|
const isCollapsed = collapsedGroups.has(groupName);
|
|
280
|
-
const selectedInGroup =
|
|
385
|
+
const selectedInGroup = originalGroupItems.filter((item) => item.selected).length;
|
|
281
386
|
const unselectedInGroup = groupItems.filter((item) => !item.selected);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
387
|
+
const hasGroupFilter = !!groupFilters[groupName];
|
|
388
|
+
const showGroupSearchInput = showGroupSearch && originalGroupItems.length >= groupSearchMinItems;
|
|
389
|
+
return (_jsxs("div", { className: cn(s['border-b'], s['border-gray-200'], s['last:border-b-0']), children: [showGroupHeaders && (_jsxs("div", { className: cn(s['flex'], s['flex-col'], s['bg-gradient-to-r'], s['from-blue-50'], s['to-indigo-50'], s['border-b'], s['border-blue-200']), children: [_jsxs("div", { className: cn(s['flex'], s['items-center'], s['justify-between'], s['p-3'], s['font-semibold'], s['text-sm'], s['text-gray-700'], collapsibleGroups && s['cursor-pointer'], s['hover:bg-blue-100'], s['transition-colors']), onClick: () => collapsibleGroups && toggleGroupCollapse(groupName), children: [_jsxs("div", { className: cn(s['flex'], s['items-center'], s['gap-2']), children: [collapsibleGroups && (_jsx(Icon, { nameIcon: isCollapsed ? 'FaChevronRight' : 'FaChevronDown', propsIcon: {
|
|
390
|
+
size: 12,
|
|
391
|
+
color: '#4F46E5',
|
|
392
|
+
} })), _jsx("span", { className: cn(s['font-bold'], s['text-indigo-700'], s['text-base']), children: getGroupNameDisplay(groupName, originalGroupItems.length) }), _jsxs("span", { className: cn(s['text-xs'], s['bg-indigo-100'], s['text-indigo-800'], s['px-2'], s['py-1'], s['rounded-full'], s['font-medium']), children: [selectedInGroup, "/", originalGroupItems.length] }), groupItems.length !== originalGroupItems.length && (_jsxs("span", { className: cn(s['text-xs'], s['bg-amber-100'], s['text-amber-800'], s['px-2'], s['py-1'], s['rounded-full']), children: [groupItems.length, " shown"] }))] }), showSelectAllButtons && (_jsxs("div", { className: cn(s['flex'], s['gap-1']), children: [_jsx("button", { type: "button", onClick: (e) => {
|
|
393
|
+
e.stopPropagation();
|
|
394
|
+
handleGroupSelectAll(groupName, originalGroupItems, true);
|
|
395
|
+
}, className: cn(s['text-xs'], s['px-2'], s['py-1'], s['bg-emerald-500'], s['text-white'], s['rounded'], s['hover:bg-emerald-600'], s['transition-colors'], s['shadow-sm'], selectedInGroup === originalGroupItems.length &&
|
|
396
|
+
s['opacity-50'], selectedInGroup === originalGroupItems.length &&
|
|
397
|
+
s['cursor-not-allowed']), title: `Select all in ${groupName}`, disabled: selectedInGroup === originalGroupItems.length, children: "All" }), _jsx("button", { type: "button", onClick: (e) => {
|
|
398
|
+
e.stopPropagation();
|
|
399
|
+
handleGroupSelectAll(groupName, originalGroupItems, false);
|
|
400
|
+
}, className: cn(s['text-xs'], s['px-2'], s['py-1'], s['bg-slate-500'], s['text-white'], s['rounded'], s['hover:bg-slate-600'], s['transition-colors'], s['shadow-sm'], selectedInGroup === 0 && s['opacity-50'], selectedInGroup === 0 && s['cursor-not-allowed']), title: `Deselect all in ${groupName}`, disabled: selectedInGroup === 0, children: "None" })] }))] }), showGroupSearchInput && !isCollapsed && (_jsx("div", { className: cn(s['px-3'], s['pb-2']), children: _jsxs("div", { className: cn(s['relative']), children: [_jsx("input", { type: "text", placeholder: `Search in ${groupName}...`, value: groupFilters[groupName] || '', onChange: (e) => handleGroupSearch(groupName, e.target.value), onClick: (e) => e.stopPropagation(), className: cn(s['w-full'], s['px-3'], s['py-1.5'], s['text-sm'], s['border'], s['border-indigo-200'], s['rounded-md'], s['focus:outline-none'], s['focus:ring-2'], s['focus:ring-indigo-300'], s['focus:border-indigo-400'], s['bg-white'], s['placeholder-gray-400']) }), hasGroupFilter && (_jsx("button", { type: "button", onClick: (e) => {
|
|
401
|
+
e.stopPropagation();
|
|
402
|
+
clearGroupFilter(groupName);
|
|
403
|
+
}, className: cn(s['absolute'], s['right-2'], s['top-1/2'], s['-translate-y-1/2'], s['text-gray-400'], s['hover:text-gray-600']), children: _jsx(Icon, { nameIcon: "FaTimes", propsIcon: {
|
|
404
|
+
size: 12,
|
|
405
|
+
color: 'currentColor',
|
|
406
|
+
} }) }))] }) }))] })), (!collapsibleGroups || !isCollapsed) && (_jsxs("div", { className: cn(s['max-h-60'], s['overflow-y-auto']), children: [unselectedInGroup.map((item, index) => (_jsx("div", { className: cn(s['flex'], s['items-center'], s['gap-3'], s['p-3'], s['hover:bg-blue-50'], s['border-l-4'], s['border-transparent'], s['transition-colors']), children: _jsxs("label", { className: cn(s['flex'], s['items-center'], s['cursor-pointer'], s['w-full']), children: [_jsx("input", { id: `${groupName}-${index}`, className: cn(s['h-4'], s['w-4'], s['text-blue-600'], s['border-gray-300'], s['rounded'], s['focus:ring-blue-500'], s['cursor-pointer']), type: "checkbox", checked: false, onChange: () => {
|
|
293
407
|
const itemIndex = groupedOptions[groupName].findIndex((grpItem) => grpItem[uniqueField] === item[uniqueField]);
|
|
294
408
|
handleGroupSelectToggle(groupName, false, itemIndex);
|
|
295
409
|
} }), _jsx("div", { className: cn(s['ml-3'], s['flex'], s['flex-col']), children: renderItem ? (renderItem(item)) : (_jsx("span", { className: cn(s['text-sm'], s['font-medium'], s['text-gray-700']), children: getItemDisplayText(item) })) })] }) }, `${groupName}-${item[uniqueField] || index}`))), unselectedInGroup.length === 0 && groupItems.length > 0 && (_jsx("div", { className: cn(s['p-3'], s['text-center'], s['text-gray-500'], s['text-sm']), children: "All items in this group are selected" }))] }))] }, groupName));
|
|
@@ -325,10 +439,13 @@ export const AutoCompleteFilterGroupByMultipleSelectMultipleFieldsDisplay = ({ u
|
|
|
325
439
|
} }))] }), isListOpen && (_jsxs("div", { className: cn(s['mt-1'], s['absolute'], s['rounded-md'], s['bg-white'], s['border'], s['border-gray-300'], s['shadow-lg'], s['z-50'], s['w-full'], s['max-h-96'], s['overflow-hidden']), style: { zIndex: 1000 }, children: [_jsx("div", { className: cn(s['p-3'], s['border-b'], s['border-gray-200'], s['bg-gray-50'], s['text-sm'], s['font-medium'], s['text-gray-700']), children: _jsxs("div", { className: cn(s['flex'], s['justify-between'], s['items-center']), children: [_jsxs("span", { children: [Object.keys(groupedOptions).length, ' ', groupField?.toUpperCase(), ", ", totalItems, ' ', totalItemName?.toUpperCase()] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: cn(s['text-blue-600']), children: [getSelectedCount(), " Selected"] }), _jsx("div", { className: cn(s['flex'], s['gap-2']), children: _jsx("button", { type: "button", className: cn(s['text-xs'], s['px-3'], s['py-1'], s['bg-blue-600'], s['text-white'], s['rounded'], s['hover:bg-blue-700'], s['transition-colors']), onClick: () => {
|
|
326
440
|
const selectedGroups = getSelectedGroups();
|
|
327
441
|
onSelect(selectedGroups);
|
|
442
|
+
// Call onUpdate when user clicks OK
|
|
443
|
+
const flatList = getFlatListFromGroupedOptions(groupedOptions);
|
|
444
|
+
onUpdate && onUpdate(flatList);
|
|
328
445
|
setIsListOpen(false);
|
|
329
446
|
setValue('');
|
|
330
447
|
}, children: "OK" }) })] })] }) }), _jsxs("div", { className: cn(s['max-h-80'], s['overflow-y-auto']), children: [showSelectedSection && getSelectedCount() > 0 && (_jsxs("div", { className: cn(s['border-b'], s['border-gray-200']), children: [_jsx("div", { className: cn(s['p-3'], s['bg-blue-50'], s['border-b'], s['border-blue-200'], s['font-semibold'], s['text-sm'], s['text-blue-700']), children: _jsxs("div", { className: cn(s['flex'], s['items-center'], s['gap-2']), children: [_jsx(Icon, { nameIcon: "FaCheck", propsIcon: {
|
|
331
448
|
size: 12,
|
|
332
449
|
color: '#1976d2',
|
|
333
|
-
} }), _jsxs("span", { children: ["Selected Items (", getSelectedCount(), ")"] })] }) }), _jsx("div", { className: cn(s['max-h-48'], s['overflow-y-auto']), children: selectedItems?.map((item, index) => (_jsx("div", { className: cn(s['flex'], s['items-center'], s['gap-3'], s['p-3'], s['hover:bg-blue-50'], s['border-l-4'], s['border-blue-500'], s['bg-blue-25'], s['transition-colors']), children: _jsxs("label", { className: cn(s['flex'], s['items-center'], s['cursor-pointer'], s['w-full']), children: [_jsx("input", { type: "checkbox", checked: true, onChange: () => handleDeselectItem(item), className: cn(s['h-4'], s['w-4'], s['text-blue-600'], s['border-gray-300'], s['rounded'], s['focus:ring-blue-500'], s['cursor-pointer']) }), _jsx("div", { className: cn(s['ml-3'], s['flex'], s['flex-col']), children: renderItem ? (renderItem(item)) : (_jsxs(_Fragment, { children: [_jsx("span", { className: cn(s['text-sm'], s['font-medium'], s['text-blue-700']), children: getItemDisplayText(item) }), _jsx("span", { className: cn(s['text-xs'], s['text-gray-500']), children: item[groupField] || defaultGroupName })] })) })] }) }, `selected-${item[uniqueField] || index}`))) })] })), _jsx("div", { children: renderGroupedOptions() })] })] }))] }) }));
|
|
450
|
+
} }), _jsxs("span", { children: ["Selected Items (", getSelectedCount(), ")"] })] }) }), _jsx("div", { className: cn(s['max-h-48'], s['overflow-y-auto']), children: selectedItems?.map((item, index) => (_jsx("div", { className: cn(s['flex'], s['items-center'], s['gap-3'], s['p-3'], s['hover:bg-blue-50'], s['border-l-4'], s['border-blue-500'], s['bg-blue-25'], s['transition-colors']), children: _jsxs("label", { className: cn(s['flex'], s['items-center'], s['cursor-pointer'], s['w-full']), children: [_jsx("input", { type: "checkbox", checked: true, onChange: () => handleDeselectItem(item), className: cn(s['h-4'], s['w-4'], s['text-blue-600'], s['border-gray-300'], s['rounded'], s['focus:ring-blue-500'], s['cursor-pointer']) }), _jsx("div", { className: cn(s['ml-3'], s['flex'], s['flex-col']), children: renderItem ? (renderItem(item)) : (_jsxs(_Fragment, { children: [_jsx("span", { className: cn(s['text-sm'], s['font-medium'], s['text-blue-700']), children: getItemDisplayText(item) }), _jsx("span", { className: cn(s['text-xs'], s['text-gray-500']), children: item[groupField] || defaultGroupName })] })) })] }) }, `selected-${item[uniqueField] || index}`))) })] })), _jsx("div", { children: renderGroupedOptions() })] })] }, optionsRenderKey))] }) }));
|
|
334
451
|
};
|
|
@@ -30,6 +30,14 @@ export interface IconProps {
|
|
|
30
30
|
/** Custom styles for internal elements */
|
|
31
31
|
styles?: IconStyles;
|
|
32
32
|
}
|
|
33
|
+
export interface IconContextProps {
|
|
34
|
+
isDisable?: boolean;
|
|
35
|
+
color?: string;
|
|
36
|
+
size?: string;
|
|
37
|
+
onClick?: () => void;
|
|
38
|
+
children?: React.ReactNode;
|
|
39
|
+
style?: CSSProperties;
|
|
40
|
+
}
|
|
33
41
|
export interface CompatIconProps {
|
|
34
42
|
src: string | React.ReactNode;
|
|
35
43
|
className?: string;
|
|
@@ -41,3 +49,4 @@ export declare const Icon: {
|
|
|
41
49
|
SSR: ({ nameIcon, classNames, styles, ...props }: IconProps) => React.JSX.Element;
|
|
42
50
|
Compat: ({ src, className, style, ...props }: CompatIconProps) => React.JSX.Element;
|
|
43
51
|
};
|
|
52
|
+
export declare const IconContext: React.FunctionComponent<IconContextProps>;
|
|
@@ -3,8 +3,30 @@ import { Suspense } from 'react';
|
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import { Tooltip } from '../Tooltip';
|
|
5
5
|
import loadable from '@loadable/component';
|
|
6
|
+
import { IconContext as Context } from 'react-icons';
|
|
6
7
|
import s from '../../tc.module.css';
|
|
7
8
|
import { cn } from '../../utils';
|
|
9
|
+
// Static imports for better reliability
|
|
10
|
+
import * as IconRi from 'react-icons/ri';
|
|
11
|
+
import * as IconIm from 'react-icons/im';
|
|
12
|
+
import * as IconFa from 'react-icons/fa';
|
|
13
|
+
import * as IconFc from 'react-icons/fc';
|
|
14
|
+
import * as IconGi from 'react-icons/gi';
|
|
15
|
+
import * as IconCg from 'react-icons/cg';
|
|
16
|
+
import * as IconGr from 'react-icons/gr';
|
|
17
|
+
import * as IconBs from 'react-icons/bs';
|
|
18
|
+
import * as IconFi from 'react-icons/fi';
|
|
19
|
+
import * as Iconmd from 'react-icons/md';
|
|
20
|
+
import * as Iconio from 'react-icons/io';
|
|
21
|
+
import * as Iconio5 from 'react-icons/io5';
|
|
22
|
+
import * as Iconsi from 'react-icons/si';
|
|
23
|
+
import * as Iconai from 'react-icons/ai';
|
|
24
|
+
import * as Iconvsc from 'react-icons/vsc';
|
|
25
|
+
import * as Iconhi from 'react-icons/hi';
|
|
26
|
+
import * as IconBi from 'react-icons/bi';
|
|
27
|
+
import * as IconTb from 'react-icons/tb';
|
|
28
|
+
import * as IconsCi from 'react-icons/ci';
|
|
29
|
+
import * as IconAi from 'react-icons/ai';
|
|
8
30
|
const CompatIcon = ({ src, className, style, ...props }) => {
|
|
9
31
|
if (typeof src === 'string') {
|
|
10
32
|
return (_jsx("img", { src: src, className: cn(s.icon, className), style: style, ...props, alt: "" }));
|
|
@@ -20,6 +42,54 @@ export const Icon = ({ nameIcon, propsIcon, className, tooltip = '', isDisable =
|
|
|
20
42
|
...propsIcon,
|
|
21
43
|
color: isDisable ? '#808080' : propsIcon?.color || 'rgb(36 48 63)',
|
|
22
44
|
};
|
|
45
|
+
// Try to use static imports first for better reliability
|
|
46
|
+
const getStaticIcon = () => {
|
|
47
|
+
const lib = nameIcon.startsWith('IoIo')
|
|
48
|
+
? 'io'
|
|
49
|
+
: nameIcon.startsWith('Io')
|
|
50
|
+
? 'io5'
|
|
51
|
+
: nameIcon
|
|
52
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
53
|
+
.split(' ')[0]
|
|
54
|
+
.toLowerCase();
|
|
55
|
+
// Map to static imports
|
|
56
|
+
const iconMap = {
|
|
57
|
+
ai: IconAi || Iconai,
|
|
58
|
+
ri: IconRi,
|
|
59
|
+
im: IconIm,
|
|
60
|
+
fa: IconFa,
|
|
61
|
+
fc: IconFc,
|
|
62
|
+
gi: IconGi,
|
|
63
|
+
cg: IconCg,
|
|
64
|
+
gr: IconGr,
|
|
65
|
+
bs: IconBs,
|
|
66
|
+
fi: IconFi,
|
|
67
|
+
md: Iconmd,
|
|
68
|
+
io: Iconio,
|
|
69
|
+
io5: Iconio5,
|
|
70
|
+
si: Iconsi,
|
|
71
|
+
vsc: Iconvsc,
|
|
72
|
+
hi: Iconhi,
|
|
73
|
+
bi: IconBi,
|
|
74
|
+
tb: IconTb,
|
|
75
|
+
ci: IconsCi,
|
|
76
|
+
};
|
|
77
|
+
const iconLib = iconMap[lib];
|
|
78
|
+
if (iconLib && iconLib[nameIcon]) {
|
|
79
|
+
return iconLib[nameIcon];
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
};
|
|
83
|
+
const StaticIcon = getStaticIcon();
|
|
84
|
+
if (StaticIcon) {
|
|
85
|
+
// Use static import
|
|
86
|
+
const IconComponent = StaticIcon;
|
|
87
|
+
return (_jsx("div", { className: cn(s.iconContainer, className, classNames.container), style: styles.container, onClick: () => {
|
|
88
|
+
if (!isDisable)
|
|
89
|
+
onClick && onClick();
|
|
90
|
+
}, children: !_.isEmpty(tooltip) ? (_jsx(Tooltip, { content: tooltip, children: _jsx(IconComponent, { ...iconProps, className: cn(s.icon, classNames.icon), style: styles.icon }) })) : (_jsx(IconComponent, { ...iconProps, className: cn(s.icon, classNames.icon), style: styles.icon })) }));
|
|
91
|
+
}
|
|
92
|
+
// Fallback to dynamic import
|
|
23
93
|
const lib = nameIcon.startsWith('IoIo')
|
|
24
94
|
? 'io'
|
|
25
95
|
: nameIcon.startsWith('Io')
|
|
@@ -74,6 +144,16 @@ export const Icon = ({ nameIcon, propsIcon, className, tooltip = '', isDisable =
|
|
|
74
144
|
return _jsx(_Fragment, {});
|
|
75
145
|
}
|
|
76
146
|
};
|
|
147
|
+
export const IconContext = (props) => {
|
|
148
|
+
return (_jsx(Context.Provider, { value: {
|
|
149
|
+
color: props?.isDisable ? '#808080' : props?.color || '#ffffff',
|
|
150
|
+
size: props?.size || '20',
|
|
151
|
+
}, children: _jsx("div", { onClick: () => {
|
|
152
|
+
if (props?.isDisable)
|
|
153
|
+
return;
|
|
154
|
+
props.onClick && props.onClick();
|
|
155
|
+
}, style: props?.style, children: props?.children }) }));
|
|
156
|
+
};
|
|
77
157
|
// Attach sub-components
|
|
78
158
|
Icon.SSR = SSRIcon;
|
|
79
159
|
Icon.Compat = CompatIcon;
|