@dashadmin/dash-components 1.3.25 → 1.3.28
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/README.md +35 -0
- package/dist/components/AuditLog/AuditLog.js +345 -1
- package/dist/components/AuditLog/index.js +4 -1
- package/dist/components/AutoCompleteArrayInput/AutoCompleteCheckBoxInput.js +630 -3
- package/dist/components/AutoCompleteArrayInput/AutocompleteCheckBoxArrayInput.js +53 -1
- package/dist/components/AutoCompleteArrayInputAutoSearch/AutoCompleteCheckBoxInput.js +647 -3
- package/dist/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayInput.js +53 -1
- package/dist/components/Json/Json.js +235 -1
- package/dist/components/JsonColorSelector/JsonColorSelector.js +1172 -1
- package/dist/components/JsonColorSelector/JsonColorSelectorEnhanced.js +863 -2
- package/dist/components/JsonColorSelector/JsonCssVarValues.js +738 -1
- package/dist/components/JsonColorSelector/components/ColorEditDialog.js +426 -1
- package/dist/components/JsonColorSelector/components/ColorPaletteItem.js +75 -1
- package/dist/components/JsonColorSelector/components/ImageColorExtractor.js +283 -1
- package/dist/components/JsonColorSelector/helpers/functions.js +101 -1
- package/dist/components/JsonColorSelector/useAsyncColorThief.js +105 -1
- package/dist/components/ListActive/ListActive.js +126 -1
- package/dist/components/ListActive/ListBoolean.js +124 -1
- package/dist/components/NotificationPreferences/NotificationPreferences.js +317 -1
- package/dist/components/NotificationPreferences/index.js +7 -1
- package/dist/components/SearchableSelects/RASearchableSelect.js +236 -1
- package/dist/components/SearchableSelects/RASearchableSelectCheckboxes.js +479 -1
- package/dist/components/SearchableSelects/RASearchableSelectChips.js +465 -1
- package/dist/components/SearchableSelects/RASearchableSelectChipsRecordContext.js +295 -1
- package/dist/components/SearchableSelects/SearchableSelect.js +202 -1
- package/dist/components/SearchableSelects/SearchableSelectCheckboxes.js +290 -1
- package/dist/components/SortableDataGrid/SortableDataGrid.js +72 -1
- package/dist/components/SortableDataGrid/SortableDataGridBody.js +167 -1
- package/dist/components/SortableDataGrid/SortableDataGridHeader.js +109 -1
- package/dist/components/SortableDataGrid/SortableDataGridRow.js +283 -1
- package/dist/components/Upload/SingleImageUploader.js +40 -1
- package/dist/components/custom/PackageCopyMethod.js +78 -1
- package/dist/components/custom/PackageRadioButtonGroup.js +45 -1
- package/dist/components/dialog/Basic.js +8 -1
- package/dist/components/dialog/Correct.js +14 -1
- package/dist/components/dialog/Error.js +14 -1
- package/dist/components/dialog/Info.js +14 -1
- package/dist/components/dialog/InfoV2.js +21 -1
- package/dist/components/dialog/NotFound.js +14 -1
- package/dist/components/dialog/QuickSearch.js +117 -1
- package/dist/components/notifications/NotificationsCenter.js +597 -1
- package/dist/components/notifications/index.js +24 -1
- package/dist/components/tables/PaginationComponent.js +36 -1
- package/dist/components/theme/AppLoadingFallback.js +38 -2
- package/dist/components/theme/ErrorBoundary.js +52 -1
- package/dist/components/theme/InitialLoader.js +13 -1
- package/dist/components/theme/NotFound.js +136 -1
- package/dist/components/theme/NotResults.js +38 -1
- package/dist/hooks/useDraggable.js +6 -1
- package/dist/hooks/useNotifications.js +8 -1
- package/dist/hooks/useQuickSearch.js +26 -1
- package/dist/index.js +66 -5567
- package/dist/utils/setNativeValue.js +17 -1
- package/package.json +148 -132
- package/src/components/AuditLog/AuditLog.tsx +476 -0
- package/src/components/AuditLog/index.ts +2 -0
- package/src/components/AutoCompleteArrayInput/AutoCompleteCheckBoxInput.tsx +917 -0
- package/src/components/AutoCompleteArrayInput/AutocompleteCheckBoxArrayInput.tsx +39 -0
- package/src/components/AutoCompleteArrayInputAutoSearch/AutoCompleteCheckBoxInput.tsx +916 -0
- package/src/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayInput.tsx +44 -0
- package/src/components/Json/Json.tsx +312 -0
- package/src/components/JsonColorSelector/JsonColorSelector.tsx +1435 -0
- package/src/components/JsonColorSelector/JsonColorSelectorEnhanced.tsx +1029 -0
- package/src/components/JsonColorSelector/JsonCssVarValues.tsx +863 -0
- package/src/components/JsonColorSelector/components/ColorEditDialog.tsx +491 -0
- package/src/components/JsonColorSelector/components/ColorPaletteItem.tsx +80 -0
- package/src/components/JsonColorSelector/components/ImageColorExtractor.tsx +328 -0
- package/src/components/JsonColorSelector/helpers/functions.tsx +123 -0
- package/src/components/JsonColorSelector/interfaces/interfaces.tsx +22 -0
- package/src/components/JsonColorSelector/useAsyncColorThief.tsx +97 -0
- package/src/components/ListActive/ListActive.tsx +106 -0
- package/src/components/ListActive/ListBoolean.tsx +96 -0
- package/src/components/NotificationPreferences/NotificationPreferences.tsx +463 -0
- package/src/components/NotificationPreferences/index.tsx +2 -0
- package/src/components/SearchableSelects/RASearchableSelect.tsx +300 -0
- package/src/components/SearchableSelects/RASearchableSelectCheckboxes.tsx +604 -0
- package/src/components/SearchableSelects/RASearchableSelectChips.tsx +558 -0
- package/src/components/SearchableSelects/RASearchableSelectChipsRecordContext.tsx +362 -0
- package/src/components/SearchableSelects/SearchableSelect.tsx +321 -0
- package/src/components/SearchableSelects/SearchableSelectCheckboxes.tsx +355 -0
- package/src/components/SortableDataGrid/SortableDataGrid.tsx +56 -0
- package/src/components/SortableDataGrid/SortableDataGridBody.tsx +175 -0
- package/src/components/SortableDataGrid/SortableDataGridHeader.tsx +171 -0
- package/src/components/SortableDataGrid/SortableDataGridRow.tsx +247 -0
- package/src/components/Upload/SingleImageUploader.tsx +55 -0
- package/src/components/custom/PackageCopyMethod.tsx +109 -0
- package/src/components/custom/PackageRadioButtonGroup.tsx +47 -0
- package/src/components/dialog/Basic.tsx +11 -0
- package/src/components/dialog/Correct.tsx +16 -0
- package/src/components/dialog/Error.tsx +15 -0
- package/src/components/dialog/Info.tsx +15 -0
- package/src/components/dialog/InfoV2.tsx +35 -0
- package/src/components/dialog/NotFound.tsx +18 -0
- package/src/components/dialog/QuickSearch.tsx +115 -0
- package/src/components/notifications/NotificationsCenter.tsx +792 -0
- package/src/components/notifications/index.ts +42 -0
- package/src/components/tables/PaginationComponent.tsx +17 -0
- package/src/components/theme/AppLoadingFallback.tsx +42 -0
- package/src/components/theme/ErrorBoundary.tsx +57 -0
- package/src/components/theme/InitialLoader.tsx +16 -0
- package/src/components/theme/NotFound.tsx +124 -0
- package/src/components/theme/NotResults.tsx +54 -0
- package/src/hooks/useDraggable.ts +11 -0
- package/src/hooks/useNotifications.ts +14 -0
- package/src/hooks/useQuickSearch.tsx +29 -0
- package/src/index.tsx +77 -0
- package/src/utils/setNativeValue.tsx +23 -0
- package/dist/color-thief-CTwGFhOB.js +0 -272
- /package/{dist → src}/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayAutoSearch.tsx.wip +0 -0
- /package/{dist → src}/components/JsonColorSelector/JsonColorSelector.tsx.bkup +0 -0
- /package/{dist → src}/components/SortableDataGrid/README.md +0 -0
- /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;
|