@dashadmin/dash-components 1.3.72 → 1.3.74-srcfree.1

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 (60) hide show
  1. package/package.json +24 -21
  2. package/src/components/AuditLog/AuditLog.tsx +0 -490
  3. package/src/components/AuditLog/index.ts +0 -2
  4. package/src/components/AutoCompleteArrayInput/AutoCompleteCheckBoxInput.tsx +0 -917
  5. package/src/components/AutoCompleteArrayInput/AutocompleteCheckBoxArrayInput.tsx +0 -39
  6. package/src/components/AutoCompleteArrayInputAutoSearch/AutoCompleteCheckBoxInput.tsx +0 -916
  7. package/src/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayAutoSearch.tsx.wip +0 -42
  8. package/src/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayInput.tsx +0 -44
  9. package/src/components/Json/Json.tsx +0 -312
  10. package/src/components/JsonColorSelector/JsonColorSelector.tsx +0 -1443
  11. package/src/components/JsonColorSelector/JsonColorSelector.tsx.bkup +0 -746
  12. package/src/components/JsonColorSelector/JsonColorSelectorEnhanced.tsx +0 -1135
  13. package/src/components/JsonColorSelector/JsonCssVarValues.tsx +0 -867
  14. package/src/components/JsonColorSelector/components/BasicColorSelector.tsx +0 -333
  15. package/src/components/JsonColorSelector/components/ColorEditDialog.tsx +0 -495
  16. package/src/components/JsonColorSelector/components/ColorPaletteItem.tsx +0 -80
  17. package/src/components/JsonColorSelector/components/ImageColorExtractor.tsx +0 -420
  18. package/src/components/JsonColorSelector/helpers/functions.tsx +0 -123
  19. package/src/components/JsonColorSelector/helpers/paletteDerivation.ts +0 -367
  20. package/src/components/JsonColorSelector/interfaces/interfaces.tsx +0 -22
  21. package/src/components/JsonColorSelector/useAsyncColorThief.tsx +0 -97
  22. package/src/components/ListActive/ListActive.tsx +0 -106
  23. package/src/components/ListActive/ListBoolean.tsx +0 -96
  24. package/src/components/NotificationPreferences/NotificationPreferences.tsx +0 -480
  25. package/src/components/NotificationPreferences/index.tsx +0 -2
  26. package/src/components/SearchableSelects/RASearchableSelect.tsx +0 -303
  27. package/src/components/SearchableSelects/RASearchableSelectCheckboxes.tsx +0 -617
  28. package/src/components/SearchableSelects/RASearchableSelectChips.tsx +0 -561
  29. package/src/components/SearchableSelects/RASearchableSelectChipsRecordContext.tsx +0 -366
  30. package/src/components/SearchableSelects/SearchableSelect.tsx +0 -323
  31. package/src/components/SearchableSelects/SearchableSelectCheckboxes.tsx +0 -358
  32. package/src/components/SortableDataGrid/README.md +0 -1
  33. package/src/components/SortableDataGrid/SortableDataGrid.tsx +0 -56
  34. package/src/components/SortableDataGrid/SortableDataGridBody.tsx +0 -175
  35. package/src/components/SortableDataGrid/SortableDataGridHeader.tsx +0 -171
  36. package/src/components/SortableDataGrid/SortableDataGridRow.tsx +0 -247
  37. package/src/components/Upload/SingleImageUploader.tsx +0 -55
  38. package/src/components/custom/PackageCopyMethod.tsx +0 -109
  39. package/src/components/custom/PackageRadioButtonGroup.tsx +0 -49
  40. package/src/components/dialog/Basic.tsx +0 -11
  41. package/src/components/dialog/Correct.tsx +0 -16
  42. package/src/components/dialog/Error.tsx +0 -15
  43. package/src/components/dialog/Info.tsx +0 -15
  44. package/src/components/dialog/InfoV2.tsx +0 -35
  45. package/src/components/dialog/NotFound.tsx +0 -18
  46. package/src/components/dialog/QuickSearch.tsx +0 -115
  47. package/src/components/notifications/NotificationsCenter.tsx +0 -743
  48. package/src/components/notifications/index.ts +0 -42
  49. package/src/components/tables/PaginationComponent.tsx +0 -17
  50. package/src/components/theme/AppLoadingFallback.tsx +0 -42
  51. package/src/components/theme/ErrorBoundary.tsx +0 -57
  52. package/src/components/theme/InitialLoader.tsx +0 -16
  53. package/src/components/theme/NotFound.tsx +0 -124
  54. package/src/components/theme/NotResults.tsx +0 -56
  55. package/src/hooks/useDraggable.ts +0 -11
  56. package/src/hooks/useNotifications.ts +0 -14
  57. package/src/hooks/useQuickSearch.tsx +0 -29
  58. package/src/index.tsx +0 -77
  59. package/src/styles/index.less +0 -0
  60. package/src/utils/setNativeValue.tsx +0 -23
@@ -1,617 +0,0 @@
1
- /**
2
- * Standalone SearchableSelectCheckboxes for filters with checkbox support
3
- * @author Francisco Aranda <francisco.aranda@sudo.cl> <faranda@gmail.com>
4
- */
5
-
6
- import {
7
- Autocomplete,
8
- Box,
9
- Checkbox,
10
- CircularProgress,
11
- TextField,
12
- Typography,
13
- Chip,
14
- FormControlLabel,
15
- List,
16
- ListItem,
17
- ListItemButton,
18
- ListItemIcon,
19
- ListItemText,
20
- Paper,
21
- Popper,
22
- ClickAwayListener,
23
- InputAdornment,
24
- IconButton,
25
- } from "@mui/material";
26
- import React, { useEffect, useState, useRef } from "react";
27
- import { useGetList } from "react-admin";
28
- import { LoadingIndicator } from "react-admin";
29
- import SearchIcon from "@mui/icons-material/Search";
30
- import ClearIcon from "@mui/icons-material/Clear";
31
- import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
32
- import ExpandLessIcon from "@mui/icons-material/ExpandLess";
33
-
34
- interface ISearchableSelectCheckboxes {
35
- name?: string;
36
- resource?: string;
37
- selectLabel?: string;
38
- title?: string;
39
- renderText: (option: any, caller: string) => any;
40
- transformData?: (val: any) => any;
41
- defaultValues?: any;
42
- value?: any;
43
- onChange?: (value: any) => void;
44
- isMultiple?: boolean;
45
- minSearch?: number;
46
- searchResults?: number;
47
- minOptions?: number;
48
- timerSearch?: number;
49
- isEmpty?: boolean;
50
- filter?: { [x: string]: any };
51
- queryFilter?: string;
52
- isOptionEqualToValue: (option: any, value: any) => boolean;
53
- viewAttribute: string;
54
- valueKeyId?: string;
55
- transformOption?: (
56
- option: any,
57
- parsedOptions: any[],
58
- caller?: string
59
- ) => boolean;
60
- disabled?: boolean;
61
- placeholder?: string;
62
- emptyText?: string;
63
- fullWidth?: boolean;
64
- maxHeight?: number;
65
- showSelectAll?: boolean;
66
- showSelectedCount?: boolean;
67
- }
68
-
69
- export const SearchableSelectCheckboxes: React.FC<ISearchableSelectCheckboxes> = ({
70
- isMultiple = true, // Default to true for checkbox version
71
- searchResults = 50,
72
- minSearch = 1,
73
- minOptions = 10,
74
- timerSearch = 300,
75
- resource,
76
- selectLabel,
77
- renderText,
78
- transformData = (val: any) => val,
79
- queryFilter = "q",
80
- defaultValues = undefined,
81
- value,
82
- onChange,
83
- isEmpty = false,
84
- filter,
85
- isOptionEqualToValue,
86
- viewAttribute = null,
87
- valueKeyId = "id",
88
- transformOption,
89
- disabled = false,
90
- placeholder = "",
91
- emptyText = "",
92
- fullWidth = true,
93
- maxHeight = 300,
94
- showSelectAll = true,
95
- showSelectedCount = true,
96
- }) => {
97
- // Component state
98
- const [open, setOpen] = useState(false);
99
- const [fullyLoaded, setFullyLoaded] = useState(false);
100
- const [parsedOptions, setParsedOptions] = useState<any[]>([]);
101
- const [q, setQ] = useState("");
102
- const [selectedOptions, setSelectedOptions] = useState<any[]>([]);
103
- const [timeOutSearch, setTimeOutSearch] = useState<any>(undefined);
104
- const anchorRef = useRef<HTMLDivElement>(null);
105
- const [searchInputValue, setSearchInputValue] = useState("");
106
-
107
- const _transformOption = (option: any, caller: string) => {
108
- if (typeof transformOption === "function") {
109
- return transformOption(option, parsedOptions, caller);
110
- }
111
- return option;
112
- };
113
-
114
- // Get includes for the API call
115
- const getIncludes = () => {
116
- const currentValue = value || defaultValues;
117
- if (!currentValue) return [];
118
-
119
- if (Array.isArray(currentValue)) {
120
- return currentValue.map(item =>
121
- typeof item === 'object' ? item[valueKeyId] : item
122
- ).filter(Boolean);
123
- }
124
-
125
- const id = typeof currentValue === 'object' ? currentValue[valueKeyId] : currentValue;
126
- return id ? [id] : [];
127
- };
128
-
129
- const includes = getIncludes();
130
-
131
- // Build filter parameters for mixed results
132
- const buildFilterParams = () => {
133
- const baseFilter = { ...filter };
134
-
135
- // Add includes if we have selected values
136
- if (includes.length > 0) {
137
- baseFilter.includes = includes;
138
- }
139
-
140
- // If we have a search query, enable mixed results
141
- if (q && q.length >= minSearch) {
142
- baseFilter[queryFilter] = q;
143
- baseFilter.mixed_results = true;
144
- baseFilter.min_options = minOptions;
145
- baseFilter.limit = searchResults;
146
- } else {
147
- // No search, just get default options with minimum count
148
- baseFilter.limit = Math.max(minOptions, searchResults);
149
- }
150
-
151
- return baseFilter;
152
- };
153
-
154
- // React Admin useGetList with dynamic filter
155
- const {
156
- data: resourceSearchResults,
157
- isLoading: isResourceSearchLoading,
158
- error: isResourceSearchErrored,
159
- } = useGetList(resource, {
160
- meta: { removeSortFilters: true },
161
- filter: buildFilterParams(),
162
- });
163
-
164
- // Update parsed options when search results change
165
- useEffect(() => {
166
- if (resourceSearchResults) {
167
- const resultsArray = Array.isArray(resourceSearchResults)
168
- ? resourceSearchResults
169
- : Object.values(resourceSearchResults);
170
-
171
- let processedOptions = resultsArray.map((ele, index) => {
172
- // Determine if this is a search match
173
- const isSearchMatch = q && q.length >= minSearch ?
174
- (ele[viewAttribute] && ele[viewAttribute].toLowerCase().includes(q.toLowerCase())) :
175
- true;
176
-
177
- return {
178
- ...ele,
179
- key: `option-${ele[valueKeyId] || index}`,
180
- isSearchMatch,
181
- priority: isSearchMatch ? 1 : 2,
182
- };
183
- });
184
-
185
- // Sort by priority (search matches first) and then by name
186
- processedOptions.sort((a, b) => {
187
- if (a.priority !== b.priority) {
188
- return a.priority - b.priority;
189
- }
190
- const nameA = a[viewAttribute] || a.name || '';
191
- const nameB = b[viewAttribute] || b.name || '';
192
- return nameA.localeCompare(nameB);
193
- });
194
-
195
- // Limit results
196
- if (processedOptions.length > searchResults) {
197
- processedOptions = processedOptions.slice(0, searchResults);
198
- }
199
-
200
- setParsedOptions(processedOptions);
201
- }
202
- }, [resourceSearchResults, valueKeyId, q, minSearch, viewAttribute, searchResults]);
203
-
204
- // Initialize selected options from value or defaultValues
205
- useEffect(() => {
206
- const currentValue = value !== undefined ? value : defaultValues;
207
-
208
- if (currentValue === null || currentValue === undefined) {
209
- setSelectedOptions([]);
210
- setFullyLoaded(true);
211
- return;
212
- }
213
-
214
- let initialSelectedOptions: any[] = [];
215
-
216
- if (Array.isArray(currentValue)) {
217
- initialSelectedOptions = currentValue;
218
- } else if (currentValue) {
219
- initialSelectedOptions = [currentValue];
220
- }
221
-
222
- setSelectedOptions(initialSelectedOptions);
223
- setFullyLoaded(true);
224
- }, [value, defaultValues]);
225
-
226
- // Update selected options when parsedOptions change and we have IDs but no full objects
227
- useEffect(() => {
228
- if (parsedOptions.length > 0 && selectedOptions.length > 0) {
229
- const updatedOptions = selectedOptions.map(selectedOption => {
230
- // If it's already a full object, keep it
231
- if (typeof selectedOption === 'object' && selectedOption[viewAttribute]) {
232
- return selectedOption;
233
- }
234
-
235
- // If it's just an ID, find the full object
236
- const id = typeof selectedOption === 'object' ? selectedOption[valueKeyId] : selectedOption;
237
- return parsedOptions.find(option => option[valueKeyId] === id) || selectedOption;
238
- });
239
-
240
- setSelectedOptions(updatedOptions);
241
- }
242
- }, [parsedOptions, viewAttribute, valueKeyId]);
243
-
244
- // Debounced search
245
- const debounce = (fn: Function, delay = 300) => {
246
- clearTimeout(timeOutSearch);
247
- setTimeOutSearch(
248
- setTimeout(() => {
249
- fn();
250
- }, delay)
251
- );
252
- };
253
-
254
- // Handle search input change
255
- const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
256
- const searchValue = event.target.value;
257
- setSearchInputValue(searchValue);
258
-
259
- if (searchValue && searchValue !== "" && searchValue.length >= minSearch) {
260
- debounce(() => setQ(searchValue), timerSearch);
261
- } else if (searchValue === "") {
262
- setQ("");
263
- }
264
- };
265
-
266
- // Clear search
267
- const handleClearSearch = () => {
268
- setSearchInputValue("");
269
- setQ("");
270
- };
271
-
272
- // Check if option is selected
273
- const isOptionSelected = (option: any) => {
274
- return selectedOptions.some(selected => {
275
- const selectedId = typeof selected === 'object' ? selected[valueKeyId] : selected;
276
- const optionId = typeof option === 'object' ? option[valueKeyId] : option;
277
- return selectedId === optionId;
278
- });
279
- };
280
-
281
- // Handle option toggle
282
- const handleOptionToggle = (option: any) => {
283
- const isSelected = isOptionSelected(option);
284
- let newSelectedOptions: any[];
285
-
286
- if (isSelected) {
287
- // Remove option
288
- newSelectedOptions = selectedOptions.filter(selected => {
289
- const selectedId = typeof selected === 'object' ? selected[valueKeyId] : selected;
290
- const optionId = typeof option === 'object' ? option[valueKeyId] : option;
291
- return selectedId !== optionId;
292
- });
293
- } else {
294
- // Add option
295
- newSelectedOptions = [...selectedOptions, option];
296
- }
297
-
298
- setSelectedOptions(newSelectedOptions);
299
-
300
- if (onChange) {
301
- onChange(newSelectedOptions);
302
- }
303
- };
304
-
305
- // Handle select all
306
- const handleSelectAll = () => {
307
- const allSelected = parsedOptions.every(option => isOptionSelected(option));
308
-
309
- if (allSelected) {
310
- // Deselect all visible options
311
- const visibleIds = parsedOptions.map(option =>
312
- typeof option === 'object' ? option[valueKeyId] : option
313
- );
314
- const newSelectedOptions = selectedOptions.filter(selected => {
315
- const selectedId = typeof selected === 'object' ? selected[valueKeyId] : selected;
316
- return !visibleIds.includes(selectedId);
317
- });
318
- setSelectedOptions(newSelectedOptions);
319
- if (onChange) {
320
- onChange(newSelectedOptions);
321
- }
322
- } else {
323
- // Select all visible options
324
- const newOptions = parsedOptions.filter(option => !isOptionSelected(option));
325
- const newSelectedOptions = [...selectedOptions, ...newOptions];
326
- setSelectedOptions(newSelectedOptions);
327
- if (onChange) {
328
- onChange(newSelectedOptions);
329
- }
330
- }
331
- };
332
-
333
- // Get display text for selected items
334
- const getSelectedDisplayText = () => {
335
- if (selectedOptions.length === 0) {
336
- return placeholder || "Seleccionar opciones...";
337
- }
338
-
339
- if (showSelectedCount && selectedOptions.length > 2) {
340
- return `${selectedOptions.length} elementos seleccionados`;
341
- }
342
-
343
- return selectedOptions
344
- .slice(0, 2)
345
- .map(option => renderText(_transformOption(option, "display"), "display"))
346
- .join(", ") + (selectedOptions.length > 2 ? `, +${selectedOptions.length - 2} más` : "");
347
- };
348
-
349
- // Check if all visible options are selected
350
- const allVisibleSelected = parsedOptions.length > 0 && parsedOptions.every(option => isOptionSelected(option));
351
- const someVisibleSelected = parsedOptions.some(option => isOptionSelected(option));
352
-
353
- if (!fullyLoaded) return <LoadingIndicator />;
354
-
355
- return (
356
- <Box ref={anchorRef} sx={{ position: 'relative', width: fullWidth ? '100%' : 'auto' }}>
357
- {/* Main Input Field */}
358
- <TextField
359
- fullWidth={fullWidth}
360
- label={selectLabel}
361
- value={getSelectedDisplayText()}
362
- onClick={() => setOpen(!open)}
363
- disabled={disabled}
364
- variant="outlined"
365
- sx={{
366
- cursor: 'pointer',
367
- '& .MuiInputBase-input': {
368
- cursor: 'pointer',
369
- }
370
- }}
371
- slotProps={{
372
- input: {
373
- readOnly: true,
374
- endAdornment: (
375
- <InputAdornment position="end">
376
- <IconButton
377
- onClick={(e) => {
378
- e.stopPropagation();
379
- setOpen(!open);
380
- }}
381
- edge="end"
382
- size="small"
383
- >
384
- {open ? <ExpandLessIcon /> : <ExpandMoreIcon />}
385
- </IconButton>
386
- </InputAdornment>
387
- ),
388
- }
389
- }}
390
- />
391
-
392
- {/* Selected Items Chips */}
393
- {selectedOptions.length > 0 && (
394
- <Box sx={{ mt: 1, display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
395
- {selectedOptions.slice(0, 5).map((option, index) => (
396
- <Chip
397
- key={`chip-${option[valueKeyId] || index}`}
398
- label={renderText(_transformOption(option, "chip"), "chip")}
399
- size="small"
400
- onDelete={() => handleOptionToggle(option)}
401
- disabled={disabled}
402
- />
403
- ))}
404
- {selectedOptions.length > 5 && (
405
- <Chip
406
- label={`+${selectedOptions.length - 5} más`}
407
- size="small"
408
- variant="outlined"
409
- />
410
- )}
411
- </Box>
412
- )}
413
-
414
- {/* Dropdown Popper */}
415
- <Popper
416
- open={open}
417
- anchorEl={anchorRef.current}
418
- placement="bottom-start"
419
- style={{ zIndex: 1300, width: anchorRef.current?.offsetWidth }}
420
- >
421
- <ClickAwayListener onClickAway={() => setOpen(false)}>
422
- <Paper
423
- elevation={8}
424
- sx={{
425
- maxHeight: maxHeight + 100,
426
- width: '100%',
427
- overflow: 'hidden',
428
- display: 'flex',
429
- flexDirection: 'column',
430
- }}
431
- >
432
- {/* Search Input */}
433
- <Box sx={{ p: 2, borderBottom: '1px solid #e0e0e0' }}>
434
- <TextField
435
- fullWidth
436
- size="small"
437
- placeholder="Buscar..."
438
- value={searchInputValue}
439
- onChange={handleSearchChange}
440
- slotProps={{
441
- input: {
442
- startAdornment: (
443
- <InputAdornment position="start">
444
- <SearchIcon fontSize="small" />
445
- </InputAdornment>
446
- ),
447
- endAdornment: searchInputValue && (
448
- <InputAdornment position="end">
449
- <IconButton
450
- size="small"
451
- onClick={handleClearSearch}
452
- >
453
- <ClearIcon fontSize="small" />
454
- </IconButton>
455
- </InputAdornment>
456
- ),
457
- }
458
- }}
459
- />
460
- </Box>
461
-
462
- {/* Select All Option */}
463
- {showSelectAll && parsedOptions.length > 0 && (
464
- <Box sx={{ borderBottom: '1px solid #e0e0e0' }}>
465
- <ListItem disablePadding>
466
- <ListItemButton onClick={handleSelectAll} dense>
467
- <ListItemIcon>
468
- <Checkbox
469
- checked={allVisibleSelected}
470
- indeterminate={someVisibleSelected && !allVisibleSelected}
471
- size="small"
472
- />
473
- </ListItemIcon>
474
- <ListItemText
475
- primary={
476
- <Typography variant="body2" sx={{
477
- fontWeight: "bold"
478
- }}>
479
- {allVisibleSelected ? "Deseleccionar todo" : "Seleccionar todo"}
480
- {q && ` (${parsedOptions.length} resultados)`}
481
- </Typography>
482
- }
483
- />
484
- </ListItemButton>
485
- </ListItem>
486
- </Box>
487
- )}
488
-
489
- {/* Loading State */}
490
- {isResourceSearchLoading && (
491
- <Box sx={{ p: 2, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
492
- <CircularProgress size={20} />
493
- <Typography variant="body2" sx={{ ml: 1 }}>
494
- Cargando opciones...
495
- </Typography>
496
- </Box>
497
- )}
498
-
499
- {/* Options List */}
500
- {!isResourceSearchLoading && (
501
- <List
502
- sx={{
503
- maxHeight: maxHeight,
504
- overflow: 'auto',
505
- py: 0,
506
- }}
507
- >
508
- {parsedOptions.length === 0 ? (
509
- <ListItem>
510
- <ListItemText
511
- primary={
512
- <Typography
513
- variant="body2"
514
- sx={{
515
- color: "text.secondary",
516
- textAlign: "center"
517
- }}>
518
- {q ? "No se encontraron resultados" : "No hay opciones disponibles"}
519
- </Typography>
520
- }
521
- />
522
- </ListItem>
523
- ) : (
524
- parsedOptions.map((option, index) => {
525
- const isSelected = isOptionSelected(option);
526
- const isSearchMatch = option.isSearchMatch;
527
- const hasSearchQuery = q && q.length >= minSearch;
528
-
529
- return (
530
- <ListItem key={option.key || `option-${index}`} disablePadding>
531
- <ListItemButton
532
- onClick={() => handleOptionToggle(option)}
533
- dense
534
- sx={{
535
- // Style search matches differently
536
- backgroundColor: hasSearchQuery && isSearchMatch
537
- ? 'rgba(25, 118, 210, 0.04)'
538
- : 'transparent',
539
- borderLeft: hasSearchQuery && isSearchMatch
540
- ? '3px solid #1976d2'
541
- : 'none',
542
- paddingLeft: hasSearchQuery && isSearchMatch ? 1 : 2,
543
- '&:hover': {
544
- backgroundColor: hasSearchQuery && isSearchMatch
545
- ? 'rgba(25, 118, 210, 0.08)'
546
- : 'rgba(0, 0, 0, 0.04)',
547
- },
548
- }}
549
- >
550
- <ListItemIcon>
551
- <Checkbox
552
- checked={isSelected}
553
- size="small"
554
- tabIndex={-1}
555
- disableRipple
556
- />
557
- </ListItemIcon>
558
- <ListItemText
559
- primary={
560
- <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
561
- <Typography
562
- variant="body2"
563
- sx={{
564
- fontWeight: hasSearchQuery && isSearchMatch ? 'bold' : 'normal',
565
- opacity: hasSearchQuery && !isSearchMatch ? 0.7 : 1,
566
- }}
567
- >
568
- {renderText(_transformOption(option, "option"), "option")}
569
- </Typography>
570
- {/* Search match indicator */}
571
- {hasSearchQuery && isSearchMatch && (
572
- <Typography
573
- variant="caption"
574
- sx={{
575
- color: '#1976d2',
576
- fontWeight: 'bold',
577
- fontSize: '0.75rem',
578
- }}
579
- >
580
- ✓
581
- </Typography>
582
- )}
583
- </Box>
584
- }
585
- />
586
- </ListItemButton>
587
- </ListItem>
588
- );
589
- })
590
- )}
591
- </List>
592
- )}
593
-
594
- {/* Footer with selection count */}
595
- {showSelectedCount && selectedOptions.length > 0 && (
596
- <Box
597
- sx={{
598
- p: 1,
599
- borderTop: '1px solid #e0e0e0',
600
- backgroundColor: '#f5f5f5'
601
- }}
602
- >
603
- <Typography variant="caption" sx={{
604
- color: "text.secondary"
605
- }}>
606
- {selectedOptions.length} elemento{selectedOptions.length !== 1 ? 's' : ''} seleccionado{selectedOptions.length !== 1 ? 's' : ''}
607
- </Typography>
608
- </Box>
609
- )}
610
- </Paper>
611
- </ClickAwayListener>
612
- </Popper>
613
- </Box>
614
- );
615
- };
616
-
617
- export default SearchableSelectCheckboxes;