@dashadmin/dash-components 1.3.25 → 1.3.26

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 (111) hide show
  1. package/dist/components/AuditLog/AuditLog.js +345 -1
  2. package/dist/components/AuditLog/index.js +4 -1
  3. package/dist/components/AutoCompleteArrayInput/AutoCompleteCheckBoxInput.js +630 -3
  4. package/dist/components/AutoCompleteArrayInput/AutocompleteCheckBoxArrayInput.js +53 -1
  5. package/dist/components/AutoCompleteArrayInputAutoSearch/AutoCompleteCheckBoxInput.js +647 -3
  6. package/dist/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayInput.js +53 -1
  7. package/dist/components/Json/Json.js +235 -1
  8. package/dist/components/JsonColorSelector/JsonColorSelector.js +1172 -1
  9. package/dist/components/JsonColorSelector/JsonColorSelectorEnhanced.js +863 -2
  10. package/dist/components/JsonColorSelector/JsonCssVarValues.js +738 -1
  11. package/dist/components/JsonColorSelector/components/ColorEditDialog.js +426 -1
  12. package/dist/components/JsonColorSelector/components/ColorPaletteItem.js +75 -1
  13. package/dist/components/JsonColorSelector/components/ImageColorExtractor.js +283 -1
  14. package/dist/components/JsonColorSelector/helpers/functions.js +101 -1
  15. package/dist/components/JsonColorSelector/useAsyncColorThief.js +105 -1
  16. package/dist/components/ListActive/ListActive.js +126 -1
  17. package/dist/components/ListActive/ListBoolean.js +124 -1
  18. package/dist/components/NotificationPreferences/NotificationPreferences.js +317 -1
  19. package/dist/components/NotificationPreferences/index.js +7 -1
  20. package/dist/components/SearchableSelects/RASearchableSelect.js +236 -1
  21. package/dist/components/SearchableSelects/RASearchableSelectCheckboxes.js +479 -1
  22. package/dist/components/SearchableSelects/RASearchableSelectChips.js +465 -1
  23. package/dist/components/SearchableSelects/RASearchableSelectChipsRecordContext.js +295 -1
  24. package/dist/components/SearchableSelects/SearchableSelect.js +202 -1
  25. package/dist/components/SearchableSelects/SearchableSelectCheckboxes.js +290 -1
  26. package/dist/components/SortableDataGrid/SortableDataGrid.js +72 -1
  27. package/dist/components/SortableDataGrid/SortableDataGridBody.js +167 -1
  28. package/dist/components/SortableDataGrid/SortableDataGridHeader.js +109 -1
  29. package/dist/components/SortableDataGrid/SortableDataGridRow.js +283 -1
  30. package/dist/components/Upload/SingleImageUploader.js +40 -1
  31. package/dist/components/custom/PackageCopyMethod.js +78 -1
  32. package/dist/components/custom/PackageRadioButtonGroup.js +45 -1
  33. package/dist/components/dialog/Basic.js +8 -1
  34. package/dist/components/dialog/Correct.js +14 -1
  35. package/dist/components/dialog/Error.js +14 -1
  36. package/dist/components/dialog/Info.js +14 -1
  37. package/dist/components/dialog/InfoV2.js +21 -1
  38. package/dist/components/dialog/NotFound.js +14 -1
  39. package/dist/components/dialog/QuickSearch.js +117 -1
  40. package/dist/components/notifications/NotificationsCenter.js +597 -1
  41. package/dist/components/notifications/index.js +24 -1
  42. package/dist/components/tables/PaginationComponent.js +36 -1
  43. package/dist/components/theme/AppLoadingFallback.js +38 -2
  44. package/dist/components/theme/ErrorBoundary.js +52 -1
  45. package/dist/components/theme/InitialLoader.js +13 -1
  46. package/dist/components/theme/NotFound.js +136 -1
  47. package/dist/components/theme/NotResults.js +38 -1
  48. package/dist/hooks/useDraggable.js +6 -1
  49. package/dist/hooks/useNotifications.js +8 -1
  50. package/dist/hooks/useQuickSearch.js +26 -1
  51. package/dist/index.js +66 -5567
  52. package/dist/utils/setNativeValue.js +17 -1
  53. package/package.json +149 -132
  54. package/src/components/AuditLog/AuditLog.tsx +476 -0
  55. package/src/components/AuditLog/index.ts +2 -0
  56. package/src/components/AutoCompleteArrayInput/AutoCompleteCheckBoxInput.tsx +917 -0
  57. package/src/components/AutoCompleteArrayInput/AutocompleteCheckBoxArrayInput.tsx +39 -0
  58. package/src/components/AutoCompleteArrayInputAutoSearch/AutoCompleteCheckBoxInput.tsx +916 -0
  59. package/src/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayInput.tsx +44 -0
  60. package/src/components/Json/Json.tsx +312 -0
  61. package/src/components/JsonColorSelector/JsonColorSelector.tsx +1435 -0
  62. package/src/components/JsonColorSelector/JsonColorSelectorEnhanced.tsx +1029 -0
  63. package/src/components/JsonColorSelector/JsonCssVarValues.tsx +863 -0
  64. package/src/components/JsonColorSelector/components/ColorEditDialog.tsx +491 -0
  65. package/src/components/JsonColorSelector/components/ColorPaletteItem.tsx +80 -0
  66. package/src/components/JsonColorSelector/components/ImageColorExtractor.tsx +328 -0
  67. package/src/components/JsonColorSelector/helpers/functions.tsx +123 -0
  68. package/src/components/JsonColorSelector/interfaces/interfaces.tsx +22 -0
  69. package/src/components/JsonColorSelector/useAsyncColorThief.tsx +97 -0
  70. package/src/components/ListActive/ListActive.tsx +106 -0
  71. package/src/components/ListActive/ListBoolean.tsx +96 -0
  72. package/src/components/NotificationPreferences/NotificationPreferences.tsx +463 -0
  73. package/src/components/NotificationPreferences/index.tsx +2 -0
  74. package/src/components/SearchableSelects/RASearchableSelect.tsx +300 -0
  75. package/src/components/SearchableSelects/RASearchableSelectCheckboxes.tsx +604 -0
  76. package/src/components/SearchableSelects/RASearchableSelectChips.tsx +558 -0
  77. package/src/components/SearchableSelects/RASearchableSelectChipsRecordContext.tsx +362 -0
  78. package/src/components/SearchableSelects/SearchableSelect.tsx +321 -0
  79. package/src/components/SearchableSelects/SearchableSelectCheckboxes.tsx +355 -0
  80. package/src/components/SortableDataGrid/SortableDataGrid.tsx +56 -0
  81. package/src/components/SortableDataGrid/SortableDataGridBody.tsx +175 -0
  82. package/src/components/SortableDataGrid/SortableDataGridHeader.tsx +171 -0
  83. package/src/components/SortableDataGrid/SortableDataGridRow.tsx +247 -0
  84. package/src/components/Upload/SingleImageUploader.tsx +55 -0
  85. package/src/components/custom/PackageCopyMethod.tsx +109 -0
  86. package/src/components/custom/PackageRadioButtonGroup.tsx +47 -0
  87. package/src/components/dialog/Basic.tsx +11 -0
  88. package/src/components/dialog/Correct.tsx +16 -0
  89. package/src/components/dialog/Error.tsx +15 -0
  90. package/src/components/dialog/Info.tsx +15 -0
  91. package/src/components/dialog/InfoV2.tsx +35 -0
  92. package/src/components/dialog/NotFound.tsx +18 -0
  93. package/src/components/dialog/QuickSearch.tsx +115 -0
  94. package/src/components/notifications/NotificationsCenter.tsx +792 -0
  95. package/src/components/notifications/index.ts +42 -0
  96. package/src/components/tables/PaginationComponent.tsx +17 -0
  97. package/src/components/theme/AppLoadingFallback.tsx +42 -0
  98. package/src/components/theme/ErrorBoundary.tsx +57 -0
  99. package/src/components/theme/InitialLoader.tsx +16 -0
  100. package/src/components/theme/NotFound.tsx +124 -0
  101. package/src/components/theme/NotResults.tsx +54 -0
  102. package/src/hooks/useDraggable.ts +11 -0
  103. package/src/hooks/useNotifications.ts +14 -0
  104. package/src/hooks/useQuickSearch.tsx +29 -0
  105. package/src/index.tsx +77 -0
  106. package/src/utils/setNativeValue.tsx +23 -0
  107. package/dist/color-thief-CTwGFhOB.js +0 -272
  108. /package/{dist → src}/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayAutoSearch.tsx.wip +0 -0
  109. /package/{dist → src}/components/JsonColorSelector/JsonColorSelector.tsx.bkup +0 -0
  110. /package/{dist → src}/components/SortableDataGrid/README.md +0 -0
  111. /package/{dist → src}/styles/index.less +0 -0
@@ -0,0 +1,491 @@
1
+ import {
2
+ Box,
3
+ Button,
4
+ TextField,
5
+ Typography,
6
+ Drawer,
7
+ InputAdornment,
8
+ FormControl,
9
+ InputLabel,
10
+ Select,
11
+ MenuItem,
12
+ Tabs,
13
+ Tab,
14
+ Avatar,
15
+ IconButton,
16
+ } from '@mui/material';
17
+ import DeleteIcon from '@mui/icons-material/Delete';
18
+ import CloseIcon from '@mui/icons-material/Close';
19
+ import { ChromePicker } from 'react-color';
20
+ import { useState, useEffect, useRef, useMemo } from 'react';
21
+ import { ColorFormat, ColorFormatType, KeyValuePair } from '../interfaces/interfaces';
22
+ import { convertColor, getContrastColor, rgbArrayToHex } from '../helpers/functions';
23
+
24
+
25
+ // Color edit drawer component (right-side panel)
26
+ const ColorEditDialog: React.FC<{
27
+ open: boolean;
28
+ pair: KeyValuePair | null;
29
+ onClose: () => void;
30
+ onSave: (pair: KeyValuePair) => void;
31
+ onDelete: (id: string) => void;
32
+ existingKeys: string[];
33
+ updateSingleDomColor: (key: string, color: string) => void;
34
+ extractedColors?: number[][]; // Add this prop
35
+ }> = ({ open, pair, onClose, onSave, onDelete, existingKeys, updateSingleDomColor, extractedColors = [] }) => {
36
+ const [editedPair, setEditedPair] = useState<KeyValuePair | null>(null);
37
+ const [colorFormat, setColorFormat] = useState<ColorFormatType>('hex');
38
+ const [keyError, setKeyError] = useState<string>('');
39
+ const [tabValue, setTabValue] = useState(0);
40
+ const lastUpdateRef = useRef<number>(0); // Ref for throttling numeric updates
41
+
42
+ // Memoize styles to prevent unnecessary re-renders of ChromePicker
43
+ const pickerStyles = useMemo(() => ({
44
+ default: {
45
+ picker: {
46
+ width: '100%',
47
+ boxShadow: 'none',
48
+ }
49
+ }
50
+ }), []);
51
+ useEffect(() => {
52
+ if (pair) {
53
+ setEditedPair({ ...pair });
54
+ // Detect color format - improved detection
55
+ const value = pair.value.toLowerCase().trim();
56
+ if (value.startsWith('rgba(')) {
57
+ setColorFormat('rgba');
58
+ } else if (value.startsWith('rgb(')) {
59
+ setColorFormat('rgb');
60
+ } else if (value.startsWith('hsla(')) {
61
+ setColorFormat('hsla');
62
+ } else if (value.startsWith('hsl(')) {
63
+ setColorFormat('hsl');
64
+ } else if (value.startsWith('#') && value.length === 9) {
65
+ // 8-digit hex with alpha
66
+ setColorFormat('rgba'); // Treat as rgba for editing
67
+ } else {
68
+ setColorFormat('hex');
69
+ }
70
+ }
71
+ setKeyError('');
72
+ }, [pair]);
73
+
74
+ const handleKeyChange = (newKey: string) => {
75
+ if (!editedPair) return;
76
+
77
+ const isDuplicate = existingKeys.some(key => key === newKey && newKey !== pair?.key);
78
+ setKeyError(isDuplicate ? 'Key already exists' : '');
79
+
80
+ setEditedPair({ ...editedPair, key: newKey });
81
+ };
82
+ // Add this helper function to parse color strings into ChromePicker-compatible format
83
+ const parseColorForPicker = (colorString: string): any => {
84
+ const trimmed = colorString.toLowerCase().trim();
85
+
86
+ if (trimmed.startsWith('rgba(')) {
87
+ const match = trimmed.match(/rgba?\(([^)]+)\)/);
88
+ if (match) {
89
+ const values = match[1].split(',').map(v => parseFloat(v.trim()));
90
+ const [r, g, b, a = 1] = values;
91
+ return {
92
+ r: Math.round(r),
93
+ g: Math.round(g),
94
+ b: Math.round(b),
95
+ a: a
96
+ };
97
+ }
98
+ } else if (trimmed.startsWith('rgb(')) {
99
+ const match = trimmed.match(/rgb\(([^)]+)\)/);
100
+ if (match) {
101
+ const values = match[1].split(',').map(v => parseFloat(v.trim()));
102
+ const [r, g, b] = values;
103
+ return {
104
+ r: Math.round(r),
105
+ g: Math.round(g),
106
+ b: Math.round(b),
107
+ a: 1
108
+ };
109
+ }
110
+ } else if (trimmed.startsWith('hsla(')) {
111
+ // For HSLA, convert to RGBA first or return the string
112
+ return colorString;
113
+ } else if (trimmed.startsWith('hsl(')) {
114
+ // For HSL, convert to RGB first or return the string
115
+ return colorString;
116
+ } else if (trimmed.startsWith('#')) {
117
+ // Hex color
118
+ const hex = trimmed.replace('#', '');
119
+ if (hex.length === 8) {
120
+ // RGBA hex
121
+ const r = parseInt(hex.substr(0, 2), 16);
122
+ const g = parseInt(hex.substr(2, 2), 16);
123
+ const b = parseInt(hex.substr(4, 2), 16);
124
+ const a = parseInt(hex.substr(6, 2), 16) / 255;
125
+ return { r, g, b, a };
126
+ } else if (hex.length === 6) {
127
+ // RGB hex
128
+ const r = parseInt(hex.substr(0, 2), 16);
129
+ const g = parseInt(hex.substr(2, 2), 16);
130
+ const b = parseInt(hex.substr(4, 2), 16);
131
+ return { r, g, b, a: 1 };
132
+ }
133
+ }
134
+
135
+ // Fallback
136
+ return colorString;
137
+ };
138
+
139
+ // Update the handleColorChange function in ColorEditDialog
140
+ const handleColorChange = (color: any) => {
141
+ if (!editedPair) return;
142
+
143
+ // The color object from ChromePicker already has the correct structure
144
+ // color.rgb = { r, g, b, a }
145
+ // color.hsl = { h, s, l, a }
146
+ // color.hex = "#rrggbb"
147
+
148
+ // Create a proper ColorFormat object with preserved alpha
149
+ const colorFormatObj: ColorFormat = {
150
+ hex: color.hex,
151
+ rgb: {
152
+ r: Math.round(color.rgb.r),
153
+ g: Math.round(color.rgb.g),
154
+ b: Math.round(color.rgb.b),
155
+ a: color.rgb.a !== undefined ? color.rgb.a : 1
156
+ },
157
+ hsl: {
158
+ h: Math.round(color.hsl.h || 0),
159
+ s: color.hsl.s || 0,
160
+ l: color.hsl.l || 0,
161
+ a: color.hsl.a !== undefined ? color.hsl.a : (color.rgb.a !== undefined ? color.rgb.a : 1)
162
+ }
163
+ };
164
+
165
+ // Convert to the selected format type
166
+ const newValue = convertColor(colorFormatObj, colorFormat);
167
+ setEditedPair({ ...editedPair, value: newValue });
168
+
169
+ // Throttle DOM updates to prevent layout thrashing (max 60fps)
170
+ const now = Date.now();
171
+ if (now - lastUpdateRef.current >= 16) {
172
+ updateSingleDomColor(editedPair.key, newValue);
173
+ lastUpdateRef.current = now;
174
+ }
175
+ };
176
+
177
+
178
+ const handleExtractedColorSelect = (colorRgb: number[]) => {
179
+ if (!editedPair) return;
180
+
181
+ const hexColor = rgbArrayToHex(colorRgb);
182
+ setEditedPair({ ...editedPair, value: hexColor });
183
+ updateSingleDomColor(editedPair.key, hexColor);
184
+ };
185
+
186
+ const handleSave = () => {
187
+ if (!editedPair || keyError || !editedPair.key.trim()) return;
188
+ onSave(editedPair);
189
+ onClose();
190
+ };
191
+
192
+ const handleDelete = () => {
193
+ if (!editedPair) return;
194
+ onDelete(editedPair.id);
195
+ onClose();
196
+ };
197
+
198
+ if (!editedPair) return null;
199
+
200
+ return (
201
+ <Drawer
202
+ anchor="right"
203
+ open={open}
204
+ onClose={onClose}
205
+ BackdropProps={{ invisible: true }}
206
+ PaperProps={{
207
+ sx: {
208
+ width: { xs: '100%', sm: 460 },
209
+ maxWidth: '100vw',
210
+ display: 'flex',
211
+ flexDirection: 'column',
212
+ }
213
+ }}
214
+ >
215
+ {/* Drawer Header */}
216
+ <Box sx={{
217
+ display: 'flex',
218
+ alignItems: 'center',
219
+ justifyContent: 'space-between',
220
+ px: 2,
221
+ py: 1.5,
222
+ borderBottom: '1px solid',
223
+ borderColor: 'divider',
224
+ flexShrink: 0,
225
+ }}>
226
+ <Typography variant="h6" sx={{ fontWeight: 600 }}>
227
+ {pair?.key ? `Edit: ${pair.key}` : 'Add New Color'}
228
+ </Typography>
229
+ <IconButton onClick={onClose} size="small">
230
+ <CloseIcon />
231
+ </IconButton>
232
+ </Box>
233
+
234
+ {/* Drawer Content - Scrollable */}
235
+ <Box sx={{
236
+ flex: 1,
237
+ overflowY: 'auto',
238
+ px: 2,
239
+ py: 2,
240
+ display: 'flex',
241
+ flexDirection: 'column',
242
+ gap: 3,
243
+ }}>
244
+ {/* Color Name */}
245
+ <TextField
246
+ fullWidth
247
+ label="Color Name"
248
+ value={editedPair.key}
249
+ onChange={(e) => handleKeyChange(e.target.value)}
250
+ error={!!keyError}
251
+ helperText={keyError}
252
+ placeholder="e.g., primary-color--light, background-dark--dark"
253
+ size="small"
254
+ />
255
+
256
+ {/* Color Format Selector */}
257
+ <FormControl fullWidth size="small">
258
+ <InputLabel>Color Format</InputLabel>
259
+ <Select
260
+ value={colorFormat}
261
+ label="Color Format"
262
+ onChange={(e) => setColorFormat(e.target.value as ColorFormatType)}
263
+ >
264
+ <MenuItem value="hex">HEX</MenuItem>
265
+ <MenuItem value="rgb">RGB</MenuItem>
266
+ <MenuItem value="rgba">RGBA (with alpha)</MenuItem>
267
+ <MenuItem value="hsl">HSL</MenuItem>
268
+ <MenuItem value="hsla">HSLA (with alpha)</MenuItem>
269
+ </Select>
270
+ </FormControl>
271
+
272
+ {/* Color Picker */}
273
+ <Box>
274
+ <Typography variant="subtitle2" gutterBottom>
275
+ Color Picker
276
+ </Typography>
277
+
278
+ {/* Color Picker Tabs */}
279
+ <Box>
280
+ <Tabs value={tabValue} onChange={(_, newValue) => setTabValue(newValue)} variant="fullWidth">
281
+ <Tab label="Picker" />
282
+ <Tab label="Manual" />
283
+ </Tabs>
284
+
285
+ {tabValue === 0 && (
286
+ <Box sx={{ mt: 2, display: 'flex', justifyContent: 'center' }}>
287
+ <ChromePicker
288
+ color={editedPair.value}
289
+ onChange={handleColorChange}
290
+ disableAlpha={false}
291
+ styles={pickerStyles}
292
+ />
293
+ </Box>
294
+ )}
295
+
296
+ {tabValue === 1 && (
297
+ <Box sx={{ mt: 2 }}>
298
+ <TextField
299
+ fullWidth
300
+ size="small"
301
+ label={`Color Value (${colorFormat.toUpperCase()})`}
302
+ value={editedPair.value}
303
+ onChange={(e) => {
304
+ const newValue = e.target.value;
305
+ setEditedPair({ ...editedPair, value: newValue });
306
+ // Also update the DOM color for preview
307
+ updateSingleDomColor(editedPair.key, newValue);
308
+ }}
309
+ placeholder={
310
+ colorFormat === 'hex' ? '#ff0000' :
311
+ colorFormat === 'rgb' ? 'rgb(255, 0, 0)' :
312
+ colorFormat === 'rgba' ? 'rgba(255, 0, 0, 0.5)' :
313
+ colorFormat === 'hsl' ? 'hsl(0, 100%, 50%)' :
314
+ 'hsla(0, 100%, 50%, 0.5)'
315
+ }
316
+ helperText={
317
+ colorFormat === 'rgba' ? 'Alpha value should be between 0 and 1 (e.g., 0.1, 0.5, 1)' :
318
+ colorFormat === 'hsla' ? 'Alpha value should be between 0 and 1 (e.g., 0.1, 0.5, 1)' :
319
+ undefined
320
+ }
321
+ InputProps={{
322
+ startAdornment: (
323
+ <InputAdornment position="start">
324
+ <Box
325
+ sx={{
326
+ width: 20,
327
+ height: 20,
328
+ backgroundColor: editedPair.value,
329
+ border: '1px solid #ccc',
330
+ borderRadius: 1,
331
+ // Add a checkerboard pattern background for transparency preview
332
+ backgroundImage: colorFormat === 'rgba' || colorFormat === 'hsla' ?
333
+ 'linear-gradient(45deg, #ccc 25%, transparent 25%), linear-gradient(-45deg, #ccc 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ccc 75%), linear-gradient(-45deg, transparent 75%, #ccc 75%)' :
334
+ 'none',
335
+ backgroundSize: '4px 4px',
336
+ backgroundPosition: '0 0, 0 2px, 2px -2px, -2px 0px',
337
+ }}
338
+ />
339
+ </InputAdornment>
340
+ ),
341
+ }}
342
+ />
343
+ </Box>
344
+ )}
345
+ </Box>
346
+ </Box>
347
+
348
+ {/* Extracted Colors */}
349
+ {extractedColors.length > 0 && (
350
+ <Box>
351
+ <Typography variant="subtitle2" gutterBottom>
352
+ Extracted Colors
353
+ </Typography>
354
+ <Box sx={{
355
+ display: 'flex',
356
+ flexWrap: 'wrap',
357
+ gap: 1,
358
+ p: 2,
359
+ border: '1px solid',
360
+ borderColor: 'divider',
361
+ borderRadius: 1,
362
+ backgroundColor: 'action.hover',
363
+ }}>
364
+ {extractedColors.map((color, index) => {
365
+ const hexColor = rgbArrayToHex(color);
366
+ const isSelected = editedPair.value === hexColor;
367
+
368
+ return (
369
+ <Avatar
370
+ key={index}
371
+ sx={{
372
+ width: 36,
373
+ height: 36,
374
+ backgroundColor: hexColor,
375
+ border: isSelected ? '3px solid #1976d2' : '2px solid #ccc',
376
+ cursor: 'pointer',
377
+ transition: 'all 0.2s',
378
+ '&:hover': {
379
+ transform: 'scale(1.1)',
380
+ boxShadow: 2,
381
+ },
382
+ color: getContrastColor(hexColor),
383
+ fontWeight: 'bold',
384
+ fontSize: '0.7rem',
385
+ }}
386
+ onClick={() => handleExtractedColorSelect(color)}
387
+ title={`Use extracted color: ${hexColor}`}
388
+ >
389
+ {index + 1}
390
+ </Avatar>
391
+ );
392
+ })}
393
+ </Box>
394
+ <Typography variant="caption" color="textSecondary" sx={{ mt: 1, display: 'block' }}>
395
+ Click any color to use it
396
+ </Typography>
397
+ </Box>
398
+ )}
399
+
400
+ {/* Color Preview */}
401
+ <Box>
402
+ <Typography variant="subtitle2" gutterBottom>
403
+ Preview
404
+ </Typography>
405
+ <Box
406
+ sx={{
407
+ width: '100%',
408
+ height: 80,
409
+ backgroundColor: editedPair.value,
410
+ border: '1px solid #ccc',
411
+ borderRadius: 1,
412
+ display: 'flex',
413
+ alignItems: 'center',
414
+ justifyContent: 'center',
415
+ position: 'relative',
416
+ // Add checkerboard background for alpha preview
417
+ backgroundImage: (editedPair.value.includes('rgba') || editedPair.value.includes('hsla')) ?
418
+ 'linear-gradient(45deg, #f0f0f0 25%, transparent 25%), linear-gradient(-45deg, #f0f0f0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #f0f0f0 75%), linear-gradient(-45deg, transparent 75%, #f0f0f0 75%)' :
419
+ 'none',
420
+ backgroundSize: '10px 10px',
421
+ backgroundPosition: '0 0, 0 5px, 5px -5px, -5px 0px',
422
+ }}
423
+ >
424
+ <Box
425
+ sx={{
426
+ width: '100%',
427
+ height: '100%',
428
+ backgroundColor: editedPair.value,
429
+ borderRadius: 1,
430
+ display: 'flex',
431
+ alignItems: 'center',
432
+ justifyContent: 'center',
433
+ }}
434
+ >
435
+ <Typography
436
+ variant="body2"
437
+ sx={{
438
+ color: getContrastColor(editedPair.value),
439
+ fontWeight: 'bold',
440
+ backgroundColor: 'rgba(0,0,0,0.1)',
441
+ padding: '4px 8px',
442
+ borderRadius: 1,
443
+ }}
444
+ >
445
+ {editedPair.key || 'Color Preview'}
446
+ </Typography>
447
+ </Box>
448
+ </Box>
449
+ </Box>
450
+ </Box>
451
+
452
+ {/* Drawer Footer - Fixed at bottom */}
453
+ <Box sx={{
454
+ display: 'flex',
455
+ alignItems: 'center',
456
+ gap: 1,
457
+ px: 2,
458
+ py: 1.5,
459
+ borderTop: '1px solid',
460
+ borderColor: 'divider',
461
+ flexShrink: 0,
462
+ backgroundColor: 'background.paper',
463
+ }}>
464
+ {pair?.key && (
465
+ <Button
466
+ onClick={handleDelete}
467
+ color="error"
468
+ startIcon={<DeleteIcon />}
469
+ size="small"
470
+ >
471
+ Delete
472
+ </Button>
473
+ )}
474
+ <Box sx={{ flexGrow: 1 }} />
475
+ <Button onClick={onClose} size="small">
476
+ Cancel
477
+ </Button>
478
+ <Button
479
+ onClick={handleSave}
480
+ variant="contained"
481
+ disabled={!!keyError || !editedPair.key.trim()}
482
+ size="small"
483
+ >
484
+ Save
485
+ </Button>
486
+ </Box>
487
+ </Drawer>
488
+ );
489
+ };
490
+
491
+ export default ColorEditDialog;
@@ -0,0 +1,80 @@
1
+ import {
2
+ Box,
3
+ Chip,
4
+ } from '@mui/material';
5
+ import { KeyValuePair } from '../interfaces/interfaces';
6
+ import { getContrastColor, getModeIcon, parseColorKey } from '../helpers/functions';
7
+
8
+ const ColorPaletteItem: React.FC<{
9
+ pair: KeyValuePair;
10
+ onEdit: (pair: KeyValuePair) => void;
11
+ onDelete: (id: string) => void;
12
+ }> = ({ pair, onEdit, onDelete }) => {
13
+ const textColor = getContrastColor(pair.value);
14
+ const { baseChips, mode } = parseColorKey(pair.key);
15
+ const modeIcon = getModeIcon(mode);
16
+
17
+ return (
18
+ <Box
19
+ sx={{
20
+ aspectRatio: 'unset',
21
+ height: '30px',
22
+ cursor: 'pointer',
23
+ transition: 'all 0.2s ease-in-out',
24
+ '&:hover': {
25
+ transform: 'translateY(-2px)',
26
+ boxShadow: 3,
27
+ },
28
+ position: 'relative',
29
+ overflow: 'hidden',
30
+ backgroundColor: pair.value,
31
+ display: 'flex',
32
+ flexDirection: 'row',
33
+ alignItems: 'center',
34
+ justifyContent: 'flex-start',
35
+ padding: '0 8px',
36
+ gap: 0.5,
37
+ }}
38
+ onClick={() => onEdit(pair)}
39
+ >
40
+ {/* Base name chips */}
41
+ <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.25, justifyContent: 'center' }}>
42
+ {baseChips.map((chip, index) => (
43
+ <Chip
44
+ key={index}
45
+ label={chip}
46
+ size="small"
47
+ sx={{
48
+ height: 16,
49
+ fontSize: '0.6rem',
50
+ backgroundColor: 'rgba(255,255,255,0.9)',
51
+ color: '#333',
52
+ '& .MuiChip-label': {
53
+ padding: '0 4px',
54
+ },
55
+ }}
56
+ />
57
+ ))}
58
+ </Box>
59
+
60
+ {/* Mode chip/icon */}
61
+ {mode && (
62
+ <Chip
63
+ label={modeIcon}
64
+ size="small"
65
+ sx={{
66
+ height: 16,
67
+ fontSize: '0.6rem',
68
+ backgroundColor: 'rgba(0,0,0,0.7)',
69
+ color: '#fff',
70
+ '& .MuiChip-label': {
71
+ padding: '0 4px',
72
+ },
73
+ }}
74
+ />
75
+ )}
76
+ </Box>
77
+ );
78
+ };
79
+
80
+ export default ColorPaletteItem;