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.
Files changed (20) hide show
  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.d.ts +9 -1
  2. 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
  3. package/lib/src/core-components/src/components/Icon/Icon.d.ts +9 -0
  4. package/lib/src/core-components/src/components/Icon/Icon.js +80 -0
  5. package/lib/src/core-components/src/components/Table/Table.js +141 -43
  6. package/lib/src/core-components/src/components/Table/elements.d.ts +3 -0
  7. package/lib/src/core-components/src/components/Table/elements.js +56 -0
  8. package/lib/src/core-components/src/components/Table/types.d.ts +2 -0
  9. package/lib/src/core-components/src/components/ag-grid/AgGrid.js +332 -8
  10. package/lib/src/core-components/src/components/ag-grid/elements.d.ts +136 -0
  11. package/lib/src/core-components/src/components/ag-grid/elements.js +639 -1
  12. package/lib/src/core-components/src/components/ag-grid/hooks.d.ts +80 -0
  13. package/lib/src/core-components/src/components/ag-grid/hooks.js +277 -0
  14. package/lib/src/core-components/src/components/ag-grid/index.d.ts +2 -1
  15. package/lib/src/core-components/src/components/ag-grid/index.js +2 -0
  16. package/lib/src/core-components/src/tc.global.css +28 -2
  17. package/lib/src/core-components/src/tc.module.css +1 -1
  18. package/lib/src/core-components/src/utils/designTokens.d.ts +9 -9
  19. package/lib/src/core-components/src/utils/designTokens.js +13 -10
  20. 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
- import { debounce } from '@techabl/core-utils';
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
- }, [ref, isListOpen, getSelectedGroups, onSelect]);
132
+ }, [
133
+ ref,
134
+ isListOpen,
135
+ getSelectedGroups,
136
+ onSelect,
137
+ groupedOptions,
138
+ getFlatListFromGroupedOptions,
139
+ onUpdate,
140
+ ]);
71
141
  };
72
- const prevGroupedOptionsRef = useRef(groupedOptions);
73
- useEffect(() => {
74
- const prevGroupedOptions = prevGroupedOptionsRef.current;
75
- if (JSON.stringify(prevGroupedOptions) !== JSON.stringify(groupedOptions)) {
76
- // onUpdate &&
77
- // onUpdate(
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
- const debouncedFilter = React.useMemo(() => debounce((search) => {
165
- onFilter && onFilter(search);
166
- }, filterDebounceDelay), [onFilter, filterDebounceDelay]);
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
- const groupNames = Object.keys(groupedOptions).sort();
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 = groupedOptions[groupName] || [];
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 = groupItems.filter((item) => item.selected).length;
385
+ const selectedInGroup = originalGroupItems.filter((item) => item.selected).length;
281
386
  const unselectedInGroup = groupItems.filter((item) => !item.selected);
282
- 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['items-center'], s['justify-between'], s['p-3'], s['bg-gray-50'], s['border-b'], s['border-gray-200'], s['font-semibold'], s['text-sm'], s['text-gray-700'], collapsibleGroups && s['cursor-pointer'], s['hover:bg-gray-100']), 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: {
283
- size: 12,
284
- color: '#666',
285
- } })), _jsx("span", { className: cn(s['font-bold'], s['text-blue-700']), children: getGroupNameDisplay(groupName, groupItems.length) }), _jsxs("span", { className: cn(s['text-xs'], s['bg-blue-100'], s['text-blue-800'], s['px-2'], s['py-1'], s['rounded-full']), children: [selectedInGroup, "/", groupItems.length] })] }), showSelectAllButtons && (_jsxs("div", { className: cn(s['flex'], s['gap-1']), children: [_jsx("button", { type: "button", onClick: (e) => {
286
- e.stopPropagation();
287
- handleGroupSelectAll(groupName, groupItems, true);
288
- }, className: cn(s['text-xs'], s['px-2'], s['py-1'], s['bg-green-500'], s['text-white'], s['rounded'], s['hover:bg-green-600'], s['transition-colors'], selectedInGroup === groupItems.length && s['opacity-50'], selectedInGroup === groupItems.length &&
289
- s['cursor-not-allowed']), title: `Select all in ${groupName}`, disabled: selectedInGroup === groupItems.length, children: "All" }), _jsx("button", { type: "button", onClick: (e) => {
290
- e.stopPropagation();
291
- handleGroupSelectAll(groupName, groupItems, false);
292
- }, className: cn(s['text-xs'], s['px-2'], s['py-1'], s['bg-gray-500'], s['text-white'], s['rounded'], s['hover:bg-gray-600'], s['transition-colors'], selectedInGroup === 0 && s['opacity-50'], selectedInGroup === 0 && s['cursor-not-allowed']), title: `Deselect all in ${groupName}`, disabled: selectedInGroup === 0, children: "None" })] }))] })), (!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: () => {
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;