@finsweet/webflow-apps-utils 1.0.17 → 1.0.18
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/dist/ui/components/color-picker/ColorPicker.stories.svelte +16 -15
- package/dist/ui/components/color-picker/ColorPicker.svelte +93 -31
- package/dist/ui/components/color-picker/ColorPicker.svelte.d.ts +8 -0
- package/dist/ui/components/color-picker/ColorPickerWrapper.svelte +14 -0
- package/dist/ui/components/color-picker/ColorPickerWrapper.svelte.d.ts +6 -0
- package/dist/ui/components/color-picker/ColorSelect.svelte +100 -32
- package/dist/ui/utils/color-utils.d.ts +12 -0
- package/dist/ui/utils/color-utils.js +50 -0
- package/dist/ui/utils/index.d.ts +1 -0
- package/dist/ui/utils/index.js +1 -0
- package/package.json +1 -1
|
@@ -39,19 +39,28 @@
|
|
|
39
39
|
});
|
|
40
40
|
</script>
|
|
41
41
|
|
|
42
|
-
<Story name="Default" args={{}} />
|
|
42
|
+
<Story name="Default" args={{ oncolorchange: handleFullColorChange }} />
|
|
43
43
|
|
|
44
|
-
<Story
|
|
44
|
+
<Story
|
|
45
|
+
name="With Initial Color"
|
|
46
|
+
args={{ color: '#00ff00', oncolorchange: handleFullColorChange }}
|
|
47
|
+
/>
|
|
45
48
|
|
|
46
|
-
<Story
|
|
49
|
+
<Story
|
|
50
|
+
name="Edge: Invalid Color"
|
|
51
|
+
args={{ color: '#xyzxyz', oncolorchange: handleFullColorChange }}
|
|
52
|
+
/>
|
|
47
53
|
|
|
48
|
-
<Story name="Edge: No Color" args={{ color: undefined }} />
|
|
54
|
+
<Story name="Edge: No Color" args={{ color: undefined, oncolorchange: handleFullColorChange }} />
|
|
49
55
|
|
|
50
|
-
<Story
|
|
56
|
+
<Story
|
|
57
|
+
name="Disabled Picker"
|
|
58
|
+
args={{ color: '#ebebeb', disabled: true, oncolorchange: handleFullColorChange }}
|
|
59
|
+
/>
|
|
51
60
|
|
|
52
61
|
<Story
|
|
53
62
|
name="Full Color Object Demo"
|
|
54
|
-
args={{ color: '#ff6600' }}
|
|
63
|
+
args={{ color: '#ff6600', oncolorchange: handleFullColorChange }}
|
|
55
64
|
story={{
|
|
56
65
|
parameters: {
|
|
57
66
|
docs: {
|
|
@@ -68,15 +77,7 @@
|
|
|
68
77
|
name="Alpha Channel Demo"
|
|
69
78
|
args={{
|
|
70
79
|
color: '#ff0000',
|
|
71
|
-
oncolorchange:
|
|
72
|
-
hex: string;
|
|
73
|
-
rgb: { r: number; g: number; b: number; value: string };
|
|
74
|
-
rgba: { r: number; g: number; b: number; a: number; value: string };
|
|
75
|
-
hsb: { h: number; s: number; b: number; value: string };
|
|
76
|
-
alpha: number;
|
|
77
|
-
}) => {
|
|
78
|
-
console.log('Full color object:', fullColor);
|
|
79
|
-
}
|
|
80
|
+
oncolorchange: handleFullColorChange
|
|
80
81
|
}}
|
|
81
82
|
story={{
|
|
82
83
|
parameters: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { normalizeHex } from '../../utils';
|
|
2
3
|
import Tooltip from '../tooltip/Tooltip.svelte';
|
|
3
4
|
import ColorSelect from './ColorSelect.svelte';
|
|
4
5
|
|
|
@@ -28,23 +29,48 @@
|
|
|
28
29
|
* Whether the picker is disabled.
|
|
29
30
|
*/
|
|
30
31
|
disabled?: boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The id of the picker.
|
|
35
|
+
*/
|
|
36
|
+
id?: string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Whether the color select should be shown by default.
|
|
40
|
+
*/
|
|
41
|
+
defaultShowColorSelect?: boolean;
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
let {
|
|
34
45
|
color = $bindable('#fff'),
|
|
35
46
|
oncolorchange,
|
|
36
47
|
width = '80px',
|
|
37
|
-
disabled = false
|
|
48
|
+
disabled = false,
|
|
49
|
+
defaultShowColorSelect = false,
|
|
50
|
+
id
|
|
38
51
|
}: Props = $props();
|
|
39
52
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
// Remove local inputValue state
|
|
54
|
+
// let inputValue = $state(color);
|
|
55
|
+
|
|
56
|
+
// Helper: normalize hex to 6-digit uppercase
|
|
57
|
+
// function normalizeHex(value: string): string {
|
|
58
|
+
// if (/^#[A-Fa-f0-9]{3}$/.test(value)) {
|
|
59
|
+
// return (
|
|
60
|
+
// '#' +
|
|
61
|
+
// value
|
|
62
|
+
// .slice(1)
|
|
63
|
+
// .split('')
|
|
64
|
+
// .map((c) => c + c)
|
|
65
|
+
// .join('')
|
|
66
|
+
// .toUpperCase()
|
|
67
|
+
// );
|
|
68
|
+
// }
|
|
69
|
+
// if (/^#[A-Fa-f0-9]{6}$/.test(value)) {
|
|
70
|
+
// return value.toUpperCase();
|
|
71
|
+
// }
|
|
72
|
+
// return value;
|
|
73
|
+
// }
|
|
48
74
|
|
|
49
75
|
function isValidColor(value: string): boolean {
|
|
50
76
|
const hexRegex = /^#?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/;
|
|
@@ -107,6 +133,41 @@
|
|
|
107
133
|
};
|
|
108
134
|
}
|
|
109
135
|
|
|
136
|
+
function normalizeHexTo6Upper(value: string): string {
|
|
137
|
+
const hex = value.startsWith('#') ? value : `#${value}`;
|
|
138
|
+
if (/^#[A-Fa-f0-9]{3}$/.test(hex)) {
|
|
139
|
+
return (
|
|
140
|
+
'#' +
|
|
141
|
+
hex
|
|
142
|
+
.slice(1)
|
|
143
|
+
.split('')
|
|
144
|
+
.map((c) => c + c)
|
|
145
|
+
.join('')
|
|
146
|
+
.toUpperCase()
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (/^#[A-Fa-f0-9]{6}$/.test(hex)) {
|
|
150
|
+
return hex.toUpperCase();
|
|
151
|
+
}
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
$effect(() => {
|
|
156
|
+
// Only update inputValue if color changes externally (not from input)
|
|
157
|
+
// if (
|
|
158
|
+
// color !== inputValue &&
|
|
159
|
+
// document.activeElement !== null &&
|
|
160
|
+
// document.activeElement !== inputEl
|
|
161
|
+
// ) {
|
|
162
|
+
// inputValue = color;
|
|
163
|
+
// }
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
function handleInput(event: Event) {
|
|
167
|
+
const target = event.target as HTMLInputElement;
|
|
168
|
+
color = target.value;
|
|
169
|
+
}
|
|
170
|
+
|
|
110
171
|
function handleInputPaste(event: ClipboardEvent) {
|
|
111
172
|
event.preventDefault();
|
|
112
173
|
|
|
@@ -116,23 +177,9 @@
|
|
|
116
177
|
|
|
117
178
|
cleanText = cleanText.substring(0, 9);
|
|
118
179
|
|
|
119
|
-
|
|
120
|
-
|
|
180
|
+
const normalizedValue = normalizeHexTo6Upper(cleanText);
|
|
181
|
+
if (/^#[A-F0-9]{6}$/.test(normalizedValue)) {
|
|
121
182
|
color = normalizedValue;
|
|
122
|
-
// Call oncolorchange with the new color
|
|
123
|
-
const colorObject = createColorObject(normalizedValue);
|
|
124
|
-
oncolorchange?.(colorObject);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function handleInputChange(event: Event) {
|
|
129
|
-
const target = event.target as HTMLInputElement;
|
|
130
|
-
const value = target.value.trim();
|
|
131
|
-
|
|
132
|
-
if (isValidColor(value)) {
|
|
133
|
-
const normalizedValue = normalizeHex(value);
|
|
134
|
-
color = normalizedValue;
|
|
135
|
-
// Call oncolorchange with the new color
|
|
136
183
|
const colorObject = createColorObject(normalizedValue);
|
|
137
184
|
oncolorchange?.(colorObject);
|
|
138
185
|
}
|
|
@@ -140,18 +187,27 @@
|
|
|
140
187
|
|
|
141
188
|
function handleInputKeydown(event: KeyboardEvent) {
|
|
142
189
|
if (event.key === 'Enter') {
|
|
143
|
-
(event.target as HTMLInputElement).blur();
|
|
190
|
+
// (event.target as HTMLInputElement).blur(); // Removed local inputValue update
|
|
144
191
|
}
|
|
145
192
|
}
|
|
146
193
|
|
|
147
|
-
function
|
|
148
|
-
const
|
|
149
|
-
|
|
194
|
+
function handleBlur(event: Event) {
|
|
195
|
+
const target = event.target as HTMLInputElement;
|
|
196
|
+
const normalized = normalizeHex(target.value);
|
|
197
|
+
if (/^#[A-F0-9]{6}$/.test(normalized)) {
|
|
198
|
+
color = normalized;
|
|
199
|
+
const colorObject = createColorObject(normalized);
|
|
200
|
+
oncolorchange?.(colorObject);
|
|
201
|
+
}
|
|
150
202
|
}
|
|
151
203
|
|
|
152
204
|
function handleFullColorChange(fullColor: ColorObject) {
|
|
153
205
|
oncolorchange?.(fullColor);
|
|
206
|
+
const normalizedValue = normalizeHex(fullColor.hex);
|
|
207
|
+
color = normalizedValue;
|
|
154
208
|
}
|
|
209
|
+
|
|
210
|
+
let showColorSelect = $state(defaultShowColorSelect);
|
|
155
211
|
</script>
|
|
156
212
|
|
|
157
213
|
<div class="color-picker">
|
|
@@ -164,6 +220,8 @@
|
|
|
164
220
|
stopPropagation={true}
|
|
165
221
|
width="241px"
|
|
166
222
|
placement="bottom"
|
|
223
|
+
onshow={() => (showColorSelect = true)}
|
|
224
|
+
onclose={() => (showColorSelect = false)}
|
|
167
225
|
fallbackPlacements={['top-end', 'top', 'bottom-end', 'bottom', 'top-start', 'bottom-start']}
|
|
168
226
|
>
|
|
169
227
|
{#snippet target()}
|
|
@@ -172,7 +230,9 @@
|
|
|
172
230
|
</div>
|
|
173
231
|
{/snippet}
|
|
174
232
|
{#snippet tooltip()}
|
|
175
|
-
|
|
233
|
+
{#if showColorSelect}
|
|
234
|
+
<ColorSelect bind:color oncolorchange={handleFullColorChange} />
|
|
235
|
+
{/if}
|
|
176
236
|
{/snippet}
|
|
177
237
|
</Tooltip>
|
|
178
238
|
|
|
@@ -183,12 +243,14 @@
|
|
|
183
243
|
bind:value={color}
|
|
184
244
|
{disabled}
|
|
185
245
|
readonly={disabled}
|
|
186
|
-
oninput={
|
|
246
|
+
oninput={handleInput}
|
|
187
247
|
onkeydown={handleInputKeydown}
|
|
188
248
|
onpaste={handleInputPaste}
|
|
249
|
+
onblur={handleBlur}
|
|
189
250
|
placeholder="#ffffff"
|
|
190
251
|
aria-label="Color hex value"
|
|
191
252
|
style="width: {width}"
|
|
253
|
+
{id}
|
|
192
254
|
/>
|
|
193
255
|
</div>
|
|
194
256
|
|
|
@@ -38,6 +38,14 @@ interface Props {
|
|
|
38
38
|
* Whether the picker is disabled.
|
|
39
39
|
*/
|
|
40
40
|
disabled?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* The id of the picker.
|
|
43
|
+
*/
|
|
44
|
+
id?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the color select should be shown by default.
|
|
47
|
+
*/
|
|
48
|
+
defaultShowColorSelect?: boolean;
|
|
41
49
|
}
|
|
42
50
|
declare const ColorPicker: import("svelte").Component<Props, {}, "color">;
|
|
43
51
|
type ColorPicker = ReturnType<typeof ColorPicker>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ColorPicker from './ColorPicker.svelte';
|
|
3
|
+
let { initialColor = '#000000', oncolorchange } = $props();
|
|
4
|
+
let color = $state(initialColor);
|
|
5
|
+
|
|
6
|
+
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
function handleColorChange(fullColor: any) {
|
|
8
|
+
color = fullColor.hex;
|
|
9
|
+
oncolorchange?.(fullColor);
|
|
10
|
+
}
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<label for="color-input">Color hex value</label>
|
|
14
|
+
<ColorPicker id="color-input" bind:color oncolorchange={handleColorChange} />
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { onMount } from 'svelte';
|
|
2
|
+
import { onMount, tick } from 'svelte';
|
|
3
|
+
|
|
4
|
+
import { normalizeHex } from '../../utils/color-utils';
|
|
3
5
|
|
|
4
6
|
// Color object type definition
|
|
5
7
|
interface ColorObject {
|
|
@@ -83,6 +85,29 @@
|
|
|
83
85
|
let dragStartPosition = $state<{ x: number; y: number } | null>(null);
|
|
84
86
|
const DRAG_THRESHOLD = 3; // pixels of movement before considering it a drag
|
|
85
87
|
|
|
88
|
+
// Helper function to normalize color input to hex
|
|
89
|
+
function normalizeColorToHex(colorInput: string): string {
|
|
90
|
+
// If it's already a valid hex, normalize it
|
|
91
|
+
if (colorInput.startsWith('#')) {
|
|
92
|
+
return normalizeHex(colorInput);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// For named colors, use a canvas to convert to hex
|
|
96
|
+
const canvas = document.createElement('canvas');
|
|
97
|
+
const ctx = canvas.getContext('2d');
|
|
98
|
+
if (ctx) {
|
|
99
|
+
ctx.fillStyle = colorInput;
|
|
100
|
+
const computedColor = ctx.fillStyle;
|
|
101
|
+
// ctx.fillStyle returns hex format for valid colors
|
|
102
|
+
if (computedColor.startsWith('#')) {
|
|
103
|
+
return computedColor.toUpperCase();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Fallback to original value if conversion fails
|
|
108
|
+
return normalizeHex(colorInput);
|
|
109
|
+
}
|
|
110
|
+
|
|
86
111
|
// Color conversion utilities
|
|
87
112
|
function hsbToRgb(h: number, s: number, b: number): [number, number, number] {
|
|
88
113
|
h = h / 360;
|
|
@@ -211,10 +236,26 @@
|
|
|
211
236
|
return result;
|
|
212
237
|
}
|
|
213
238
|
|
|
214
|
-
// For non-hex colors,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
239
|
+
// For non-hex colors, try to validate using canvas
|
|
240
|
+
try {
|
|
241
|
+
const canvas = document.createElement('canvas');
|
|
242
|
+
const ctx = canvas.getContext('2d');
|
|
243
|
+
if (ctx) {
|
|
244
|
+
ctx.fillStyle = value;
|
|
245
|
+
return (
|
|
246
|
+
ctx.fillStyle !== '#000000' ||
|
|
247
|
+
value === 'black' ||
|
|
248
|
+
value === '#000000' ||
|
|
249
|
+
value === '#000'
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
} catch (e) {
|
|
253
|
+
// Fallback to regex for hex without #
|
|
254
|
+
const hexRegex = /^([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
|
255
|
+
return hexRegex.test(value);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return false;
|
|
218
259
|
}
|
|
219
260
|
|
|
220
261
|
function rgbToHsb(r: number, g: number, b: number): [number, number, number] {
|
|
@@ -266,8 +307,16 @@
|
|
|
266
307
|
const rect = colorWell.getBoundingClientRect();
|
|
267
308
|
const x = Math.max(0, Math.min(rect.width, event.clientX - rect.left));
|
|
268
309
|
const y = Math.max(0, Math.min(rect.height, event.clientY - rect.top));
|
|
310
|
+
|
|
311
|
+
// Always update HSB values first
|
|
269
312
|
saturation = Math.round((x / rect.width) * 100);
|
|
270
313
|
brightness = Math.round(100 - (y / rect.height) * 100);
|
|
314
|
+
|
|
315
|
+
// If in RGB mode, sync the RGB values with the new HSB values
|
|
316
|
+
if (mode === 'RGB') {
|
|
317
|
+
[rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
|
|
318
|
+
}
|
|
319
|
+
|
|
271
320
|
updateColor();
|
|
272
321
|
updateColorPickerPosition();
|
|
273
322
|
}
|
|
@@ -276,16 +325,31 @@
|
|
|
276
325
|
if (!hueBar) return;
|
|
277
326
|
const rect = hueBar.getBoundingClientRect();
|
|
278
327
|
const x = Math.max(0, Math.min(rect.width, event.clientX - rect.left));
|
|
328
|
+
|
|
329
|
+
// Always update HSB hue first
|
|
279
330
|
hue = Math.round((x / rect.width) * 360);
|
|
331
|
+
|
|
332
|
+
// If in RGB mode, sync the RGB values with the new HSB values
|
|
333
|
+
if (mode === 'RGB') {
|
|
334
|
+
[rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
|
|
335
|
+
}
|
|
336
|
+
|
|
280
337
|
updateColor();
|
|
281
338
|
}
|
|
282
339
|
|
|
283
340
|
function handleAlphaBarInteraction(event: MouseEvent) {
|
|
284
341
|
if (!alphaBar) return;
|
|
342
|
+
|
|
285
343
|
const rect = alphaBar.getBoundingClientRect();
|
|
286
344
|
const x = Math.max(0, Math.min(rect.width, event.clientX - rect.left));
|
|
287
|
-
|
|
288
|
-
|
|
345
|
+
|
|
346
|
+
let newAlpha = Math.round((x / rect.width) * 100);
|
|
347
|
+
|
|
348
|
+
if (isNaN(newAlpha) || typeof newAlpha !== 'number') newAlpha = 100;
|
|
349
|
+
|
|
350
|
+
alpha = newAlpha;
|
|
351
|
+
|
|
352
|
+
updateColor();
|
|
289
353
|
}
|
|
290
354
|
|
|
291
355
|
function handleHexChange(event: Event) {
|
|
@@ -442,18 +506,18 @@
|
|
|
442
506
|
return min + (value * (max - min)) / range;
|
|
443
507
|
}
|
|
444
508
|
|
|
445
|
-
// Watch for prop changes
|
|
509
|
+
// Watch for prop changes - FIXED: Better color normalization
|
|
446
510
|
$effect(() => {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
511
|
+
const normalized = normalizeColorToHex(color);
|
|
512
|
+
if (normalized !== hexValue) {
|
|
513
|
+
hexValue = normalized;
|
|
514
|
+
const [h, s, b] = hexToHsb(normalized);
|
|
515
|
+
hue = h;
|
|
516
|
+
saturation = s;
|
|
517
|
+
brightness = b;
|
|
518
|
+
[rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
|
|
519
|
+
// Force position update after state changes
|
|
520
|
+
setTimeout(() => updateColorPickerPosition(), 0);
|
|
457
521
|
}
|
|
458
522
|
});
|
|
459
523
|
|
|
@@ -462,18 +526,19 @@
|
|
|
462
526
|
);
|
|
463
527
|
|
|
464
528
|
onMount(() => {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
529
|
+
const normalized = normalizeColorToHex(color);
|
|
530
|
+
hexValue = normalized;
|
|
531
|
+
const [h, s, b] = hexToHsb(normalized);
|
|
532
|
+
hue = h;
|
|
533
|
+
saturation = s;
|
|
534
|
+
brightness = b;
|
|
535
|
+
[rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
|
|
536
|
+
|
|
537
|
+
console.log('ColorSelect mounted', { color });
|
|
475
538
|
updateColor();
|
|
476
|
-
|
|
539
|
+
// Delay position update to ensure DOM is ready
|
|
540
|
+
|
|
541
|
+
setTimeout(() => updateColorPickerPosition(), 5);
|
|
477
542
|
|
|
478
543
|
document.addEventListener('mousemove', handleMouseMove);
|
|
479
544
|
document.addEventListener('mouseup', handleMouseUp);
|
|
@@ -511,6 +576,7 @@
|
|
|
511
576
|
}
|
|
512
577
|
}
|
|
513
578
|
|
|
579
|
+
// Add an $effect to always update the color well and picker position after state changes
|
|
514
580
|
$effect(() => {
|
|
515
581
|
updateColorPickerPosition();
|
|
516
582
|
});
|
|
@@ -744,7 +810,9 @@
|
|
|
744
810
|
oninput={(e) => {
|
|
745
811
|
const target = e.target as HTMLInputElement;
|
|
746
812
|
if (!target) return;
|
|
747
|
-
|
|
813
|
+
let newAlpha = +target.value;
|
|
814
|
+
if (isNaN(newAlpha) || typeof newAlpha !== 'number') newAlpha = 100;
|
|
815
|
+
alpha = newAlpha;
|
|
748
816
|
emitColorChange(hexValue, alpha);
|
|
749
817
|
}}
|
|
750
818
|
/>
|
|
@@ -928,7 +996,7 @@
|
|
|
928
996
|
/* Controls */
|
|
929
997
|
.controls {
|
|
930
998
|
display: grid;
|
|
931
|
-
grid-template-columns:
|
|
999
|
+
grid-template-columns: 80px 1fr 30px;
|
|
932
1000
|
gap: 7px;
|
|
933
1001
|
width: 100%;
|
|
934
1002
|
}
|
|
@@ -988,7 +1056,7 @@
|
|
|
988
1056
|
}
|
|
989
1057
|
|
|
990
1058
|
.input--hex {
|
|
991
|
-
width:
|
|
1059
|
+
width: 80px;
|
|
992
1060
|
text-align: left;
|
|
993
1061
|
}
|
|
994
1062
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a color name to a hex value
|
|
3
|
+
* @param colorName - The color name to convert
|
|
4
|
+
* @returns The hex value of the color
|
|
5
|
+
*/
|
|
6
|
+
export declare function colorNameToHex(colorName: string): string | null;
|
|
7
|
+
/**
|
|
8
|
+
* Normalize a hex value
|
|
9
|
+
* @param value - The hex value to normalize
|
|
10
|
+
* @returns The normalized hex value
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeHex(value: string): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a color name to a hex value
|
|
3
|
+
* @param colorName - The color name to convert
|
|
4
|
+
* @returns The hex value of the color
|
|
5
|
+
*/
|
|
6
|
+
export function colorNameToHex(colorName) {
|
|
7
|
+
const tempDiv = document.createElement('div');
|
|
8
|
+
tempDiv.style.color = colorName;
|
|
9
|
+
document.body.appendChild(tempDiv);
|
|
10
|
+
const rgbColor = window.getComputedStyle(tempDiv).color;
|
|
11
|
+
document.body.removeChild(tempDiv);
|
|
12
|
+
const match = rgbColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
|
|
13
|
+
if (match) {
|
|
14
|
+
const r = parseInt(match[1]);
|
|
15
|
+
const g = parseInt(match[2]);
|
|
16
|
+
const b = parseInt(match[3]);
|
|
17
|
+
const toHex = (c) => {
|
|
18
|
+
const hex = c.toString(16);
|
|
19
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
20
|
+
};
|
|
21
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Normalize a hex value
|
|
29
|
+
* @param value - The hex value to normalize
|
|
30
|
+
* @returns The normalized hex value
|
|
31
|
+
*/
|
|
32
|
+
export function normalizeHex(value) {
|
|
33
|
+
let v = value.trim();
|
|
34
|
+
if (!v.startsWith('#')) {
|
|
35
|
+
const hex = colorNameToHex(v);
|
|
36
|
+
if (hex)
|
|
37
|
+
v = hex;
|
|
38
|
+
else
|
|
39
|
+
v = `#${v}`;
|
|
40
|
+
}
|
|
41
|
+
// Expand 3-digit hex to 6-digit
|
|
42
|
+
if (/^#[A-Fa-f0-9]{3}$/.test(v)) {
|
|
43
|
+
v = '#' + v[1] + v[1] + v[2] + v[2] + v[3] + v[3];
|
|
44
|
+
}
|
|
45
|
+
// Only accept valid 6-digit hex
|
|
46
|
+
if (/^#[A-Fa-f0-9]{6}$/.test(v)) {
|
|
47
|
+
return v.toUpperCase();
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
package/dist/ui/utils/index.d.ts
CHANGED
package/dist/ui/utils/index.js
CHANGED