@proyecto-viviana/solid-stately 0.2.7 → 0.3.0
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 +30 -283
- package/dist/autocomplete/createAutocompleteState.d.ts +2 -2
- package/dist/autocomplete/index.d.ts +1 -1
- package/dist/calendar/createCalendarState.d.ts +22 -7
- package/dist/calendar/createDateFieldState.d.ts +30 -7
- package/dist/calendar/createDatePickerState.d.ts +60 -0
- package/dist/calendar/createRangeCalendarState.d.ts +27 -8
- package/dist/calendar/createTimeFieldState.d.ts +34 -7
- package/dist/calendar/index.d.ts +7 -6
- package/dist/checkbox/createCheckboxGroupState.d.ts +6 -6
- package/dist/checkbox/index.d.ts +1 -1
- package/dist/collections/ListCollection.d.ts +1 -1
- package/dist/collections/createListState.d.ts +8 -8
- package/dist/collections/createMenuState.d.ts +7 -7
- package/dist/collections/createSelectionState.d.ts +5 -5
- package/dist/collections/index.d.ts +5 -5
- package/dist/collections/types.d.ts +12 -12
- package/dist/color/Color.d.ts +1 -1
- package/dist/color/createColorAreaState.d.ts +4 -2
- package/dist/color/createColorFieldState.d.ts +38 -12
- package/dist/color/createColorSliderState.d.ts +9 -2
- package/dist/color/createColorWheelState.d.ts +13 -2
- package/dist/color/getColorChannels.d.ts +1 -1
- package/dist/color/index.d.ts +6 -6
- package/dist/color/types.d.ts +4 -4
- package/dist/combobox/createComboBoxState.d.ts +27 -13
- package/dist/combobox/index.d.ts +1 -1
- package/dist/data/createAsyncList.d.ts +4 -4
- package/dist/data/createListData.d.ts +2 -2
- package/dist/data/createTreeData.d.ts +6 -2
- package/dist/data/index.d.ts +3 -3
- package/dist/datepicker/index.d.ts +2 -4
- package/dist/disclosure/createDisclosureState.d.ts +2 -2
- package/dist/disclosure/index.d.ts +1 -1
- package/dist/dnd/createDragState.d.ts +2 -2
- package/dist/dnd/createDraggableCollectionState.d.ts +2 -2
- package/dist/dnd/createDropState.d.ts +2 -2
- package/dist/dnd/createDroppableCollectionState.d.ts +3 -3
- package/dist/dnd/index.d.ts +5 -5
- package/dist/dnd/types.d.ts +16 -16
- package/dist/form/createFormValidationState.d.ts +3 -3
- package/dist/form/index.d.ts +1 -1
- package/dist/grid/createGridState.d.ts +2 -2
- package/dist/grid/index.d.ts +2 -2
- package/dist/grid/types.d.ts +13 -11
- package/dist/index.d.ts +31 -30
- package/dist/index.js +7924 -7238
- package/dist/index.js.map +1 -1
- package/dist/menu/index.d.ts +2 -2
- package/dist/numberfield/createNumberFieldState.d.ts +2 -2
- package/dist/numberfield/index.d.ts +1 -1
- package/dist/overlays/createOverlayTriggerState.d.ts +2 -2
- package/dist/overlays/index.d.ts +1 -1
- package/dist/radio/createRadioGroupState.d.ts +7 -7
- package/dist/radio/index.d.ts +1 -1
- package/dist/searchfield/createSearchFieldState.d.ts +2 -2
- package/dist/searchfield/index.d.ts +2 -2
- package/dist/select/createSelectState.d.ts +8 -8
- package/dist/select/index.d.ts +1 -1
- package/dist/selection/index.d.ts +5 -5
- package/dist/slider/createSliderState.d.ts +3 -3
- package/dist/slider/index.d.ts +2 -2
- package/dist/steplist/createStepListState.d.ts +48 -0
- package/dist/steplist/index.d.ts +1 -0
- package/dist/table/TableCollection.d.ts +3 -3
- package/dist/table/createTableColumnResizeState.d.ts +54 -0
- package/dist/table/createTableState.d.ts +2 -2
- package/dist/table/index.d.ts +5 -3
- package/dist/table/types.d.ts +10 -10
- package/dist/tabs/createTabListState.d.ts +5 -5
- package/dist/tabs/index.d.ts +1 -1
- package/dist/textfield/createTextFieldState.d.ts +2 -2
- package/dist/textfield/index.d.ts +1 -1
- package/dist/toast/createToastState.d.ts +8 -2
- package/dist/toast/index.d.ts +1 -1
- package/dist/toggle/createToggleGroupState.d.ts +4 -4
- package/dist/toggle/createToggleState.d.ts +2 -2
- package/dist/toggle/index.d.ts +2 -2
- package/dist/tooltip/createTooltipTriggerState.d.ts +2 -2
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tree/TreeCollection.d.ts +2 -2
- package/dist/tree/createTreeState.d.ts +2 -2
- package/dist/tree/index.d.ts +3 -3
- package/dist/tree/types.d.ts +19 -13
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/reactivity.d.ts +1 -1
- package/package.json +29 -30
- package/src/autocomplete/createAutocompleteState.d.ts +19 -17
- package/src/autocomplete/createAutocompleteState.ts +24 -30
- package/src/autocomplete/index.d.ts +6 -2
- package/src/autocomplete/index.ts +1 -1
- package/src/calendar/createCalendarState.d.ts +139 -117
- package/src/calendar/createCalendarState.ts +221 -74
- package/src/calendar/createDateFieldState.d.ts +130 -95
- package/src/calendar/createDateFieldState.ts +263 -151
- package/src/calendar/createDatePickerState.ts +347 -0
- package/src/calendar/createRangeCalendarState.d.ts +154 -129
- package/src/calendar/createRangeCalendarState.ts +293 -79
- package/src/calendar/createTimeFieldState.d.ts +81 -79
- package/src/calendar/createTimeFieldState.ts +187 -108
- package/src/calendar/index.d.ts +80 -7
- package/src/calendar/index.ts +21 -12
- package/src/checkbox/createCheckboxGroupState.d.ts +57 -55
- package/src/checkbox/createCheckboxGroupState.ts +20 -29
- package/src/checkbox/index.d.ts +6 -2
- package/src/checkbox/index.ts +1 -1
- package/src/collections/ListCollection.d.ts +25 -22
- package/src/collections/ListCollection.ts +6 -6
- package/src/collections/createListState.d.ts +73 -58
- package/src/collections/createListState.ts +19 -18
- package/src/collections/createMenuState.d.ts +36 -31
- package/src/collections/createMenuState.ts +18 -12
- package/src/collections/createSelectionState.d.ts +78 -65
- package/src/collections/createSelectionState.ts +36 -32
- package/src/collections/index.d.ts +38 -6
- package/src/collections/index.ts +5 -10
- package/src/collections/types.d.ts +101 -101
- package/src/collections/types.ts +12 -12
- package/src/color/Color.d.ts +20 -5
- package/src/color/Color.ts +357 -254
- package/src/color/createColorAreaState.d.ts +67 -65
- package/src/color/createColorAreaState.ts +21 -13
- package/src/color/createColorFieldState.d.ts +46 -44
- package/src/color/createColorFieldState.ts +285 -149
- package/src/color/createColorSliderState.d.ts +58 -56
- package/src/color/createColorSliderState.ts +45 -14
- package/src/color/createColorWheelState.d.ts +42 -40
- package/src/color/createColorWheelState.ts +101 -64
- package/src/color/getColorChannels.ts +7 -5
- package/src/color/index.d.ts +36 -7
- package/src/color/index.ts +6 -9
- package/src/color/types.d.ts +68 -63
- package/src/color/types.ts +11 -19
- package/src/combobox/createComboBoxState.d.ts +107 -105
- package/src/combobox/createComboBoxState.ts +176 -89
- package/src/combobox/index.d.ts +10 -2
- package/src/combobox/index.ts +1 -1
- package/src/data/createAsyncList.ts +89 -63
- package/src/data/createListData.ts +67 -67
- package/src/data/createTreeData.ts +304 -91
- package/src/data/index.ts +3 -3
- package/src/datepicker/index.ts +6 -10
- package/src/disclosure/createDisclosureState.d.ts +47 -43
- package/src/disclosure/createDisclosureState.ts +7 -21
- package/src/disclosure/index.d.ts +10 -2
- package/src/disclosure/index.ts +1 -1
- package/src/dnd/createDragState.d.ts +51 -42
- package/src/dnd/createDragState.ts +12 -14
- package/src/dnd/createDraggableCollectionState.d.ts +52 -41
- package/src/dnd/createDraggableCollectionState.ts +14 -21
- package/src/dnd/createDropState.d.ts +64 -45
- package/src/dnd/createDropState.ts +22 -32
- package/src/dnd/createDroppableCollectionState.d.ts +88 -59
- package/src/dnd/createDroppableCollectionState.ts +40 -43
- package/src/dnd/index.d.ts +54 -6
- package/src/dnd/index.ts +5 -18
- package/src/dnd/types.d.ts +167 -156
- package/src/dnd/types.ts +20 -21
- package/src/form/createFormValidationState.d.ts +47 -45
- package/src/form/createFormValidationState.ts +30 -63
- package/src/form/index.d.ts +16 -2
- package/src/form/index.ts +1 -1
- package/src/grid/createGridState.d.ts +7 -4
- package/src/grid/createGridState.ts +44 -51
- package/src/grid/index.d.ts +3 -3
- package/src/grid/index.ts +2 -8
- package/src/grid/types.d.ts +136 -129
- package/src/grid/types.ts +20 -18
- package/src/index.d.ts +310 -26
- package/src/index.ts +48 -89
- package/src/menu/index.ts +2 -4
- package/src/numberfield/createNumberFieldState.d.ts +56 -54
- package/src/numberfield/createNumberFieldState.ts +26 -31
- package/src/numberfield/index.d.ts +6 -2
- package/src/numberfield/index.ts +1 -1
- package/src/overlays/createOverlayTriggerState.d.ts +22 -20
- package/src/overlays/createOverlayTriggerState.ts +3 -4
- package/src/overlays/index.d.ts +6 -2
- package/src/overlays/index.ts +1 -1
- package/src/radio/createRadioGroupState.d.ts +54 -52
- package/src/radio/createRadioGroupState.ts +27 -29
- package/src/radio/index.d.ts +7 -2
- package/src/radio/index.ts +1 -1
- package/src/searchfield/createSearchFieldState.d.ts +16 -14
- package/src/searchfield/createSearchFieldState.ts +5 -11
- package/src/searchfield/index.d.ts +3 -3
- package/src/searchfield/index.ts +2 -5
- package/src/select/createSelectState.d.ts +63 -61
- package/src/select/createSelectState.ts +27 -27
- package/src/select/index.d.ts +2 -2
- package/src/select/index.ts +1 -5
- package/src/selection/index.ts +6 -7
- package/src/slider/createSliderState.d.ts +60 -60
- package/src/slider/createSliderState.ts +55 -33
- package/src/slider/index.d.ts +3 -3
- package/src/slider/index.ts +2 -6
- package/src/ssr/index.d.ts +1 -1
- package/src/ssr/index.ts +2 -2
- package/src/steplist/createStepListState.ts +191 -0
- package/src/steplist/index.ts +5 -0
- package/src/table/TableCollection.d.ts +42 -40
- package/src/table/TableCollection.ts +61 -64
- package/src/table/createTableColumnResizeState.ts +242 -0
- package/src/table/createTableState.d.ts +7 -4
- package/src/table/createTableState.ts +10 -7
- package/src/table/index.d.ts +14 -4
- package/src/table/index.ts +10 -3
- package/src/table/types.d.ts +98 -92
- package/src/table/types.ts +17 -13
- package/src/tabs/createTabListState.d.ts +59 -57
- package/src/tabs/createTabListState.ts +24 -31
- package/src/tabs/index.d.ts +8 -2
- package/src/tabs/index.ts +1 -1
- package/src/textfield/createTextFieldState.d.ts +16 -14
- package/src/textfield/createTextFieldState.ts +7 -18
- package/src/textfield/index.d.ts +6 -2
- package/src/textfield/index.ts +1 -1
- package/src/toast/createToastState.d.ts +92 -86
- package/src/toast/createToastState.ts +48 -43
- package/src/toast/index.d.ts +12 -2
- package/src/toast/index.ts +1 -1
- package/src/toggle/createToggleGroupState.ts +11 -21
- package/src/toggle/createToggleState.d.ts +19 -19
- package/src/toggle/createToggleState.ts +2 -16
- package/src/toggle/index.d.ts +2 -2
- package/src/toggle/index.ts +2 -6
- package/src/tooltip/createTooltipTriggerState.d.ts +17 -17
- package/src/tooltip/createTooltipTriggerState.ts +12 -11
- package/src/tooltip/index.d.ts +7 -2
- package/src/tooltip/index.ts +1 -1
- package/src/tree/TreeCollection.d.ts +26 -23
- package/src/tree/TreeCollection.ts +29 -22
- package/src/tree/createTreeState.d.ts +7 -4
- package/src/tree/createTreeState.ts +39 -41
- package/src/tree/index.d.ts +10 -4
- package/src/tree/index.ts +3 -3
- package/src/tree/types.d.ts +131 -126
- package/src/tree/types.ts +22 -13
- package/src/utils/index.d.ts +2 -2
- package/src/utils/index.ts +1 -1
- package/src/utils/reactivity.d.ts +2 -2
- package/src/utils/reactivity.ts +3 -3
package/src/color/Color.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
RGBColor,
|
|
16
16
|
HSLColor,
|
|
17
17
|
HSBColor,
|
|
18
|
-
} from
|
|
18
|
+
} from "./types";
|
|
19
19
|
|
|
20
20
|
// Channel ranges
|
|
21
21
|
const RGB_CHANNEL_RANGE: ColorChannelRange = {
|
|
@@ -48,28 +48,33 @@ const PERCENT_CHANNEL_RANGE: ColorChannelRange = {
|
|
|
48
48
|
|
|
49
49
|
// Channel names (English only for now)
|
|
50
50
|
const CHANNEL_NAMES: Record<ColorChannel, string> = {
|
|
51
|
-
hue:
|
|
52
|
-
saturation:
|
|
53
|
-
brightness:
|
|
54
|
-
lightness:
|
|
55
|
-
red:
|
|
56
|
-
green:
|
|
57
|
-
blue:
|
|
58
|
-
alpha:
|
|
51
|
+
hue: "Hue",
|
|
52
|
+
saturation: "Saturation",
|
|
53
|
+
brightness: "Brightness",
|
|
54
|
+
lightness: "Lightness",
|
|
55
|
+
red: "Red",
|
|
56
|
+
green: "Green",
|
|
57
|
+
blue: "Blue",
|
|
58
|
+
alpha: "Alpha",
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
// React Stately uses OKLCH for color names so that lightness is perceptually
|
|
62
|
+
// consistent across hues.
|
|
63
|
+
const ORANGE_LIGHTNESS_THRESHOLD = 0.68;
|
|
64
|
+
const YELLOW_GREEN_LIGHTNESS_THRESHOLD = 0.85;
|
|
65
|
+
const MAX_DARK_LIGHTNESS = 0.55;
|
|
66
|
+
const GRAY_THRESHOLD = 0.001;
|
|
67
|
+
const OKLCH_HUES: Array<[number, string]> = [
|
|
68
|
+
[0, "pink"],
|
|
69
|
+
[15, "red"],
|
|
70
|
+
[48, "orange"],
|
|
71
|
+
[94, "yellow"],
|
|
72
|
+
[135, "green"],
|
|
73
|
+
[175, "cyan"],
|
|
74
|
+
[264, "blue"],
|
|
75
|
+
[284, "purple"],
|
|
76
|
+
[320, "magenta"],
|
|
77
|
+
[349, "pink"],
|
|
73
78
|
];
|
|
74
79
|
|
|
75
80
|
/**
|
|
@@ -252,79 +257,157 @@ function hsbToRgb(h: number, s: number, b: number): { r: number; g: number; b: n
|
|
|
252
257
|
};
|
|
253
258
|
}
|
|
254
259
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
function getOklchHueName(l: number, c: number, h: number): [string, number] {
|
|
261
|
+
if (c < GRAY_THRESHOLD) {
|
|
262
|
+
return ["gray", l];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
for (let i = 0; i < OKLCH_HUES.length; i += 1) {
|
|
266
|
+
let [hue, hueName] = OKLCH_HUES[i];
|
|
267
|
+
const [nextHue, nextHueName] = OKLCH_HUES[i + 1] || [360, "pink"];
|
|
268
|
+
if (h >= hue && h < nextHue) {
|
|
269
|
+
if (hueName === "orange") {
|
|
270
|
+
if (l < ORANGE_LIGHTNESS_THRESHOLD) {
|
|
271
|
+
hueName = "brown";
|
|
272
|
+
} else {
|
|
273
|
+
l = l - ORANGE_LIGHTNESS_THRESHOLD + MAX_DARK_LIGHTNESS;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (h > hue + (nextHue - hue) / 2 && hueName !== nextHueName) {
|
|
278
|
+
hueName = `${hueName} ${nextHueName}`;
|
|
279
|
+
} else if (hueName === "yellow" && l < YELLOW_GREEN_LIGHTNESS_THRESHOLD) {
|
|
280
|
+
hueName = "yellow green";
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return [hueName.toLocaleLowerCase(), l];
|
|
262
284
|
}
|
|
263
285
|
}
|
|
264
|
-
|
|
286
|
+
|
|
287
|
+
return ["pink", l];
|
|
265
288
|
}
|
|
266
289
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
function getColorNameFromRgb(r: number, g: number, b: number, alpha: number): string {
|
|
271
|
-
// Convert to HSL for analysis
|
|
272
|
-
const { h, s, l } = rgbToHsl(r, g, b);
|
|
290
|
+
function getColorNameFromColor(color: Color): string {
|
|
291
|
+
let [l, c, h] = toOKLCH(color);
|
|
292
|
+
const alpha = color.getChannelValue("alpha");
|
|
273
293
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return alpha < 1 ? `white ${Math.round(alpha * 100)}% transparent` : 'white';
|
|
294
|
+
if (l > 0.999) {
|
|
295
|
+
return alpha < 1 ? `white ${Math.round((1 - alpha) * 100)}% transparent` : "white";
|
|
277
296
|
}
|
|
278
|
-
|
|
279
|
-
|
|
297
|
+
|
|
298
|
+
if (l < 0.001) {
|
|
299
|
+
return alpha < 1 ? `black ${Math.round((1 - alpha) * 100)}% transparent` : "black";
|
|
280
300
|
}
|
|
281
301
|
|
|
282
|
-
|
|
283
|
-
|
|
302
|
+
let hue: string;
|
|
303
|
+
[hue, l] = getOklchHueName(l, c, h);
|
|
284
304
|
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
|
|
288
|
-
} else if (
|
|
289
|
-
|
|
290
|
-
} else if (l > 85) {
|
|
291
|
-
parts.push('very light');
|
|
292
|
-
} else if (l > 70) {
|
|
293
|
-
parts.push('light');
|
|
305
|
+
let chroma = "";
|
|
306
|
+
if (c <= 0.1 && c >= GRAY_THRESHOLD) {
|
|
307
|
+
chroma = l >= 0.7 ? "pale" : "grayish";
|
|
308
|
+
} else if (c >= 0.15) {
|
|
309
|
+
chroma = "vibrant";
|
|
294
310
|
}
|
|
295
311
|
|
|
296
|
-
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
} else if (
|
|
300
|
-
|
|
301
|
-
} else if (
|
|
302
|
-
|
|
312
|
+
let lightness = "";
|
|
313
|
+
if (l < 0.3) {
|
|
314
|
+
lightness = "very dark";
|
|
315
|
+
} else if (l < MAX_DARK_LIGHTNESS) {
|
|
316
|
+
lightness = "dark";
|
|
317
|
+
} else if (l < 0.7) {
|
|
318
|
+
lightness = "";
|
|
319
|
+
} else if (l < 0.85) {
|
|
320
|
+
lightness = "light";
|
|
321
|
+
} else {
|
|
322
|
+
lightness = "very light";
|
|
303
323
|
}
|
|
304
324
|
|
|
305
|
-
|
|
306
|
-
if (
|
|
307
|
-
|
|
325
|
+
const name = [lightness, chroma, hue].filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
|
|
326
|
+
if (alpha < 1) {
|
|
327
|
+
return `${Math.round((1 - alpha) * 100)}% transparent ${name}`.trim();
|
|
328
|
+
}
|
|
308
329
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
hueName = 'brown';
|
|
312
|
-
}
|
|
313
|
-
if (hueName === 'yellow' && l < 85 && s > 50) {
|
|
314
|
-
hueName = 'yellow green';
|
|
315
|
-
}
|
|
330
|
+
return name;
|
|
331
|
+
}
|
|
316
332
|
|
|
317
|
-
|
|
318
|
-
|
|
333
|
+
function getHueNameFromColor(color: Color): string {
|
|
334
|
+
const [l, c, h] = toOKLCH(color);
|
|
335
|
+
const [name] = getOklchHueName(l, c, h);
|
|
336
|
+
return name;
|
|
337
|
+
}
|
|
319
338
|
|
|
320
|
-
|
|
339
|
+
function toOKLCH(color: Color): [number, number, number] {
|
|
340
|
+
const rgb = color.toFormat("rgb");
|
|
341
|
+
let red = rgb.getChannelValue("red") / 255;
|
|
342
|
+
let green = rgb.getChannelValue("green") / 255;
|
|
343
|
+
let blue = rgb.getChannelValue("blue") / 255;
|
|
344
|
+
[red, green, blue] = linSRGB(red, green, blue);
|
|
345
|
+
const [x, y, z] = linSRGBToXYZ(red, green, blue);
|
|
346
|
+
const [l, a, b] = xyzToOKLab(x, y, z);
|
|
347
|
+
return okLabToOKLCH(l, a, b);
|
|
348
|
+
}
|
|
321
349
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
350
|
+
function okLabToOKLCH(l: number, a: number, b: number): [number, number, number] {
|
|
351
|
+
const hue = (Math.atan2(b, a) * 180) / Math.PI;
|
|
352
|
+
return [l, Math.sqrt(a ** 2 + b ** 2), hue >= 0 ? hue : hue + 360];
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function linSRGB(r: number, g: number, b: number): [number, number, number] {
|
|
356
|
+
return [linSRGBComponent(r), linSRGBComponent(g), linSRGBComponent(b)];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function linSRGBComponent(value: number) {
|
|
360
|
+
const sign = value < 0 ? -1 : 1;
|
|
361
|
+
const abs = Math.abs(value);
|
|
362
|
+
|
|
363
|
+
if (abs <= 0.04045) {
|
|
364
|
+
return value / 12.92;
|
|
325
365
|
}
|
|
326
366
|
|
|
327
|
-
return
|
|
367
|
+
return sign * Math.pow((abs + 0.055) / 1.055, 2.4);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function linSRGBToXYZ(r: number, g: number, b: number): [number, number, number] {
|
|
371
|
+
const matrix = [
|
|
372
|
+
506752 / 1228815,
|
|
373
|
+
87881 / 245763,
|
|
374
|
+
12673 / 70218,
|
|
375
|
+
87098 / 409605,
|
|
376
|
+
175762 / 245763,
|
|
377
|
+
12673 / 175545,
|
|
378
|
+
7918 / 409605,
|
|
379
|
+
87881 / 737289,
|
|
380
|
+
1001167 / 1053270,
|
|
381
|
+
];
|
|
382
|
+
return multiplyMatrix(matrix, r, g, b);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function xyzToOKLab(x: number, y: number, z: number): [number, number, number] {
|
|
386
|
+
const xyzToLMS = [
|
|
387
|
+
0.819022437996703, 0.3619062600528904, -0.1288737815209879, 0.0329836539323885,
|
|
388
|
+
0.9292868615863434, 0.0361446663506424, 0.0481771893596242, 0.2642395317527308,
|
|
389
|
+
0.6335478284694309,
|
|
390
|
+
];
|
|
391
|
+
const lmsToOKLab = [
|
|
392
|
+
0.210454268309314, 0.7936177747023054, -0.0040720430116193, 1.9779985324311684,
|
|
393
|
+
-2.4285922420485799, 0.450593709617411, 0.0259040424655478, 0.7827717124575296,
|
|
394
|
+
-0.8086757549230774,
|
|
395
|
+
];
|
|
396
|
+
|
|
397
|
+
const [a, b, c] = multiplyMatrix(xyzToLMS, x, y, z);
|
|
398
|
+
return multiplyMatrix(lmsToOKLab, Math.cbrt(a), Math.cbrt(b), Math.cbrt(c));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function multiplyMatrix(
|
|
402
|
+
matrix: number[],
|
|
403
|
+
x: number,
|
|
404
|
+
y: number,
|
|
405
|
+
z: number,
|
|
406
|
+
): [number, number, number] {
|
|
407
|
+
const a = matrix[0] * x + matrix[1] * y + matrix[2] * z;
|
|
408
|
+
const b = matrix[3] * x + matrix[4] * y + matrix[5] * z;
|
|
409
|
+
const c = matrix[6] * x + matrix[7] * y + matrix[8] * z;
|
|
410
|
+
return [a, b, c];
|
|
328
411
|
}
|
|
329
412
|
|
|
330
413
|
/**
|
|
@@ -345,18 +428,18 @@ class RGBColorImpl implements Color {
|
|
|
345
428
|
|
|
346
429
|
toFormat(format: ColorFormat): Color {
|
|
347
430
|
switch (format) {
|
|
348
|
-
case
|
|
349
|
-
case
|
|
350
|
-
case
|
|
351
|
-
case
|
|
431
|
+
case "hex":
|
|
432
|
+
case "hexa":
|
|
433
|
+
case "rgb":
|
|
434
|
+
case "rgba":
|
|
352
435
|
return this.clone();
|
|
353
|
-
case
|
|
354
|
-
case
|
|
436
|
+
case "hsl":
|
|
437
|
+
case "hsla": {
|
|
355
438
|
const { h, s, l } = rgbToHsl(this.red, this.green, this.blue);
|
|
356
439
|
return new HSLColorImpl(h, s, l, this.alpha);
|
|
357
440
|
}
|
|
358
|
-
case
|
|
359
|
-
case
|
|
441
|
+
case "hsb":
|
|
442
|
+
case "hsba": {
|
|
360
443
|
const { h, s, b } = rgbToHsb(this.red, this.green, this.blue);
|
|
361
444
|
return new HSBColorImpl(h, s, b, this.alpha);
|
|
362
445
|
}
|
|
@@ -365,18 +448,22 @@ class RGBColorImpl implements Color {
|
|
|
365
448
|
}
|
|
366
449
|
}
|
|
367
450
|
|
|
368
|
-
toString(format?: ColorFormat |
|
|
369
|
-
const f = format ??
|
|
451
|
+
toString(format?: ColorFormat | "css"): string {
|
|
452
|
+
const f = format ?? "css";
|
|
370
453
|
|
|
371
454
|
switch (f) {
|
|
372
|
-
case
|
|
373
|
-
return `#${this.red.toString(16).padStart(2,
|
|
374
|
-
case
|
|
375
|
-
return `#${this.red.toString(16).padStart(2,
|
|
376
|
-
|
|
455
|
+
case "hex":
|
|
456
|
+
return `#${this.red.toString(16).padStart(2, "0")}${this.green.toString(16).padStart(2, "0")}${this.blue.toString(16).padStart(2, "0")}`;
|
|
457
|
+
case "hexa":
|
|
458
|
+
return `#${this.red.toString(16).padStart(2, "0")}${this.green.toString(16).padStart(2, "0")}${this.blue.toString(16).padStart(2, "0")}${Math.round(
|
|
459
|
+
this.alpha * 255,
|
|
460
|
+
)
|
|
461
|
+
.toString(16)
|
|
462
|
+
.padStart(2, "0")}`;
|
|
463
|
+
case "rgb":
|
|
377
464
|
return `rgb(${this.red}, ${this.green}, ${this.blue})`;
|
|
378
|
-
case
|
|
379
|
-
case
|
|
465
|
+
case "rgba":
|
|
466
|
+
case "css":
|
|
380
467
|
return this.alpha === 1
|
|
381
468
|
? `rgb(${this.red}, ${this.green}, ${this.blue})`
|
|
382
469
|
: `rgba(${this.red}, ${this.green}, ${this.blue}, ${this.alpha})`;
|
|
@@ -395,21 +482,21 @@ class RGBColorImpl implements Color {
|
|
|
395
482
|
|
|
396
483
|
getChannelValue(channel: ColorChannel): number {
|
|
397
484
|
switch (channel) {
|
|
398
|
-
case
|
|
485
|
+
case "red":
|
|
399
486
|
return this.red;
|
|
400
|
-
case
|
|
487
|
+
case "green":
|
|
401
488
|
return this.green;
|
|
402
|
-
case
|
|
489
|
+
case "blue":
|
|
403
490
|
return this.blue;
|
|
404
|
-
case
|
|
491
|
+
case "alpha":
|
|
405
492
|
return this.alpha;
|
|
406
493
|
// Cross-color-space channels - convert to HSB
|
|
407
|
-
case
|
|
408
|
-
case
|
|
409
|
-
case
|
|
410
|
-
return this.toFormat(
|
|
411
|
-
case
|
|
412
|
-
return this.toFormat(
|
|
494
|
+
case "hue":
|
|
495
|
+
case "saturation":
|
|
496
|
+
case "brightness":
|
|
497
|
+
return this.toFormat("hsb").getChannelValue(channel);
|
|
498
|
+
case "lightness":
|
|
499
|
+
return this.toFormat("hsl").getChannelValue(channel);
|
|
413
500
|
default:
|
|
414
501
|
throw new Error(`Invalid channel: ${channel}`);
|
|
415
502
|
}
|
|
@@ -417,21 +504,21 @@ class RGBColorImpl implements Color {
|
|
|
417
504
|
|
|
418
505
|
withChannelValue(channel: ColorChannel, value: number): Color {
|
|
419
506
|
switch (channel) {
|
|
420
|
-
case
|
|
507
|
+
case "red":
|
|
421
508
|
return new RGBColorImpl(value, this.green, this.blue, this.alpha);
|
|
422
|
-
case
|
|
509
|
+
case "green":
|
|
423
510
|
return new RGBColorImpl(this.red, value, this.blue, this.alpha);
|
|
424
|
-
case
|
|
511
|
+
case "blue":
|
|
425
512
|
return new RGBColorImpl(this.red, this.green, value, this.alpha);
|
|
426
|
-
case
|
|
513
|
+
case "alpha":
|
|
427
514
|
return new RGBColorImpl(this.red, this.green, this.blue, value);
|
|
428
515
|
// Cross-color-space channels - convert, update, convert back
|
|
429
|
-
case
|
|
430
|
-
case
|
|
431
|
-
case
|
|
432
|
-
return this.toFormat(
|
|
433
|
-
case
|
|
434
|
-
return this.toFormat(
|
|
516
|
+
case "hue":
|
|
517
|
+
case "saturation":
|
|
518
|
+
case "brightness":
|
|
519
|
+
return this.toFormat("hsb").withChannelValue(channel, value).toFormat("rgb");
|
|
520
|
+
case "lightness":
|
|
521
|
+
return this.toFormat("hsl").withChannelValue(channel, value).toFormat("rgb");
|
|
435
522
|
default:
|
|
436
523
|
throw new Error(`Invalid channel: ${channel}`);
|
|
437
524
|
}
|
|
@@ -439,17 +526,17 @@ class RGBColorImpl implements Color {
|
|
|
439
526
|
|
|
440
527
|
getChannelRange(channel: ColorChannel): ColorChannelRange {
|
|
441
528
|
switch (channel) {
|
|
442
|
-
case
|
|
443
|
-
case
|
|
444
|
-
case
|
|
529
|
+
case "red":
|
|
530
|
+
case "green":
|
|
531
|
+
case "blue":
|
|
445
532
|
return RGB_CHANNEL_RANGE;
|
|
446
|
-
case
|
|
533
|
+
case "alpha":
|
|
447
534
|
return ALPHA_CHANNEL_RANGE;
|
|
448
|
-
case
|
|
535
|
+
case "hue":
|
|
449
536
|
return HUE_CHANNEL_RANGE;
|
|
450
|
-
case
|
|
451
|
-
case
|
|
452
|
-
case
|
|
537
|
+
case "saturation":
|
|
538
|
+
case "brightness":
|
|
539
|
+
case "lightness":
|
|
453
540
|
return PERCENT_CHANNEL_RANGE;
|
|
454
541
|
default:
|
|
455
542
|
throw new Error(`Invalid channel: ${channel}`);
|
|
@@ -461,8 +548,8 @@ class RGBColorImpl implements Color {
|
|
|
461
548
|
}
|
|
462
549
|
|
|
463
550
|
getChannelFormatOptions(channel: ColorChannel): Intl.NumberFormatOptions {
|
|
464
|
-
if (channel ===
|
|
465
|
-
return { style:
|
|
551
|
+
if (channel === "alpha") {
|
|
552
|
+
return { style: "percent" };
|
|
466
553
|
}
|
|
467
554
|
return { maximumFractionDigits: 0 };
|
|
468
555
|
}
|
|
@@ -474,28 +561,27 @@ class RGBColorImpl implements Color {
|
|
|
474
561
|
}
|
|
475
562
|
|
|
476
563
|
getColorSpace(): ColorSpace {
|
|
477
|
-
return
|
|
564
|
+
return "rgb";
|
|
478
565
|
}
|
|
479
566
|
|
|
480
567
|
getColorSpaceAxes(xyChannels?: { xChannel?: ColorChannel; yChannel?: ColorChannel }): ColorAxes {
|
|
481
|
-
const xChannel = xyChannels?.xChannel ??
|
|
482
|
-
const yChannel = xyChannels?.yChannel ??
|
|
483
|
-
const channels: ColorChannel[] = [
|
|
484
|
-
const zChannel = channels.find((c) => c !== xChannel && c !== yChannel) ??
|
|
568
|
+
const xChannel = xyChannels?.xChannel ?? "red";
|
|
569
|
+
const yChannel = xyChannels?.yChannel ?? "green";
|
|
570
|
+
const channels: ColorChannel[] = ["red", "green", "blue"];
|
|
571
|
+
const zChannel = channels.find((c) => c !== xChannel && c !== yChannel) ?? "blue";
|
|
485
572
|
return { xChannel, yChannel, zChannel };
|
|
486
573
|
}
|
|
487
574
|
|
|
488
575
|
getColorChannels(): [ColorChannel, ColorChannel, ColorChannel] {
|
|
489
|
-
return [
|
|
576
|
+
return ["red", "green", "blue"];
|
|
490
577
|
}
|
|
491
578
|
|
|
492
|
-
getColorName(
|
|
493
|
-
return
|
|
579
|
+
getColorName(_locale: string): string {
|
|
580
|
+
return getColorNameFromColor(this);
|
|
494
581
|
}
|
|
495
582
|
|
|
496
583
|
getHueName(_locale: string): string {
|
|
497
|
-
|
|
498
|
-
return getHueNameFromAngle(h);
|
|
584
|
+
return getHueNameFromColor(this);
|
|
499
585
|
}
|
|
500
586
|
}
|
|
501
587
|
|
|
@@ -517,18 +603,18 @@ class HSLColorImpl implements Color {
|
|
|
517
603
|
|
|
518
604
|
toFormat(format: ColorFormat): Color {
|
|
519
605
|
switch (format) {
|
|
520
|
-
case
|
|
521
|
-
case
|
|
606
|
+
case "hsl":
|
|
607
|
+
case "hsla":
|
|
522
608
|
return this.clone();
|
|
523
|
-
case
|
|
524
|
-
case
|
|
525
|
-
case
|
|
526
|
-
case
|
|
609
|
+
case "hex":
|
|
610
|
+
case "hexa":
|
|
611
|
+
case "rgb":
|
|
612
|
+
case "rgba": {
|
|
527
613
|
const { r, g, b } = hslToRgb(this.hue, this.saturation, this.lightness);
|
|
528
614
|
return new RGBColorImpl(r, g, b, this.alpha);
|
|
529
615
|
}
|
|
530
|
-
case
|
|
531
|
-
case
|
|
616
|
+
case "hsb":
|
|
617
|
+
case "hsba": {
|
|
532
618
|
const { r, g, b } = hslToRgb(this.hue, this.saturation, this.lightness);
|
|
533
619
|
const hsb = rgbToHsb(r, g, b);
|
|
534
620
|
return new HSBColorImpl(hsb.h, hsb.s, hsb.b, this.alpha);
|
|
@@ -538,14 +624,14 @@ class HSLColorImpl implements Color {
|
|
|
538
624
|
}
|
|
539
625
|
}
|
|
540
626
|
|
|
541
|
-
toString(format?: ColorFormat |
|
|
542
|
-
const f = format ??
|
|
627
|
+
toString(format?: ColorFormat | "css"): string {
|
|
628
|
+
const f = format ?? "css";
|
|
543
629
|
|
|
544
630
|
switch (f) {
|
|
545
|
-
case
|
|
631
|
+
case "hsl":
|
|
546
632
|
return `hsl(${this.hue}, ${this.saturation}%, ${this.lightness}%)`;
|
|
547
|
-
case
|
|
548
|
-
case
|
|
633
|
+
case "hsla":
|
|
634
|
+
case "css":
|
|
549
635
|
return this.alpha === 1
|
|
550
636
|
? `hsl(${this.hue}, ${this.saturation}%, ${this.lightness}%)`
|
|
551
637
|
: `hsla(${this.hue}, ${this.saturation}%, ${this.lightness}%, ${this.alpha})`;
|
|
@@ -559,26 +645,26 @@ class HSLColorImpl implements Color {
|
|
|
559
645
|
}
|
|
560
646
|
|
|
561
647
|
toHexInt(): number {
|
|
562
|
-
return this.toFormat(
|
|
648
|
+
return this.toFormat("rgb").toHexInt();
|
|
563
649
|
}
|
|
564
650
|
|
|
565
651
|
getChannelValue(channel: ColorChannel): number {
|
|
566
652
|
switch (channel) {
|
|
567
|
-
case
|
|
653
|
+
case "hue":
|
|
568
654
|
return this.hue;
|
|
569
|
-
case
|
|
655
|
+
case "saturation":
|
|
570
656
|
return this.saturation;
|
|
571
|
-
case
|
|
657
|
+
case "lightness":
|
|
572
658
|
return this.lightness;
|
|
573
|
-
case
|
|
659
|
+
case "alpha":
|
|
574
660
|
return this.alpha;
|
|
575
661
|
// Cross-color-space channels
|
|
576
|
-
case
|
|
577
|
-
case
|
|
578
|
-
case
|
|
579
|
-
return this.toFormat(
|
|
580
|
-
case
|
|
581
|
-
return this.toFormat(
|
|
662
|
+
case "red":
|
|
663
|
+
case "green":
|
|
664
|
+
case "blue":
|
|
665
|
+
return this.toFormat("rgb").getChannelValue(channel);
|
|
666
|
+
case "brightness":
|
|
667
|
+
return this.toFormat("hsb").getChannelValue(channel);
|
|
582
668
|
default:
|
|
583
669
|
throw new Error(`Invalid channel: ${channel}`);
|
|
584
670
|
}
|
|
@@ -586,21 +672,21 @@ class HSLColorImpl implements Color {
|
|
|
586
672
|
|
|
587
673
|
withChannelValue(channel: ColorChannel, value: number): Color {
|
|
588
674
|
switch (channel) {
|
|
589
|
-
case
|
|
675
|
+
case "hue":
|
|
590
676
|
return new HSLColorImpl(value, this.saturation, this.lightness, this.alpha);
|
|
591
|
-
case
|
|
677
|
+
case "saturation":
|
|
592
678
|
return new HSLColorImpl(this.hue, value, this.lightness, this.alpha);
|
|
593
|
-
case
|
|
679
|
+
case "lightness":
|
|
594
680
|
return new HSLColorImpl(this.hue, this.saturation, value, this.alpha);
|
|
595
|
-
case
|
|
681
|
+
case "alpha":
|
|
596
682
|
return new HSLColorImpl(this.hue, this.saturation, this.lightness, value);
|
|
597
683
|
// Cross-color-space channels
|
|
598
|
-
case
|
|
599
|
-
case
|
|
600
|
-
case
|
|
601
|
-
return this.toFormat(
|
|
602
|
-
case
|
|
603
|
-
return this.toFormat(
|
|
684
|
+
case "red":
|
|
685
|
+
case "green":
|
|
686
|
+
case "blue":
|
|
687
|
+
return this.toFormat("rgb").withChannelValue(channel, value).toFormat("hsl");
|
|
688
|
+
case "brightness":
|
|
689
|
+
return this.toFormat("hsb").withChannelValue(channel, value).toFormat("hsl");
|
|
604
690
|
default:
|
|
605
691
|
throw new Error(`Invalid channel: ${channel}`);
|
|
606
692
|
}
|
|
@@ -608,17 +694,17 @@ class HSLColorImpl implements Color {
|
|
|
608
694
|
|
|
609
695
|
getChannelRange(channel: ColorChannel): ColorChannelRange {
|
|
610
696
|
switch (channel) {
|
|
611
|
-
case
|
|
697
|
+
case "hue":
|
|
612
698
|
return HUE_CHANNEL_RANGE;
|
|
613
|
-
case
|
|
614
|
-
case
|
|
615
|
-
case
|
|
699
|
+
case "saturation":
|
|
700
|
+
case "lightness":
|
|
701
|
+
case "brightness":
|
|
616
702
|
return PERCENT_CHANNEL_RANGE;
|
|
617
|
-
case
|
|
703
|
+
case "alpha":
|
|
618
704
|
return ALPHA_CHANNEL_RANGE;
|
|
619
|
-
case
|
|
620
|
-
case
|
|
621
|
-
case
|
|
705
|
+
case "red":
|
|
706
|
+
case "green":
|
|
707
|
+
case "blue":
|
|
622
708
|
return RGB_CHANNEL_RANGE;
|
|
623
709
|
default:
|
|
624
710
|
throw new Error(`Invalid channel: ${channel}`);
|
|
@@ -630,47 +716,46 @@ class HSLColorImpl implements Color {
|
|
|
630
716
|
}
|
|
631
717
|
|
|
632
718
|
getChannelFormatOptions(channel: ColorChannel): Intl.NumberFormatOptions {
|
|
633
|
-
if (channel ===
|
|
634
|
-
return { style:
|
|
719
|
+
if (channel === "alpha") {
|
|
720
|
+
return { style: "percent" };
|
|
635
721
|
}
|
|
636
|
-
if (channel ===
|
|
637
|
-
return { maximumFractionDigits: 0 };
|
|
722
|
+
if (channel === "hue") {
|
|
723
|
+
return { style: "unit", unit: "degree", unitDisplay: "narrow", maximumFractionDigits: 0 };
|
|
638
724
|
}
|
|
639
|
-
return { style:
|
|
725
|
+
return { style: "percent", maximumFractionDigits: 0 };
|
|
640
726
|
}
|
|
641
727
|
|
|
642
728
|
formatChannelValue(channel: ColorChannel, locale: string): string {
|
|
643
729
|
const value = this.getChannelValue(channel);
|
|
644
730
|
const options = this.getChannelFormatOptions(channel);
|
|
645
|
-
if (channel ===
|
|
731
|
+
if (channel === "saturation" || channel === "lightness") {
|
|
646
732
|
return new Intl.NumberFormat(locale, options).format(value / 100);
|
|
647
733
|
}
|
|
648
734
|
return new Intl.NumberFormat(locale, options).format(value);
|
|
649
735
|
}
|
|
650
736
|
|
|
651
737
|
getColorSpace(): ColorSpace {
|
|
652
|
-
return
|
|
738
|
+
return "hsl";
|
|
653
739
|
}
|
|
654
740
|
|
|
655
741
|
getColorSpaceAxes(xyChannels?: { xChannel?: ColorChannel; yChannel?: ColorChannel }): ColorAxes {
|
|
656
|
-
const xChannel = xyChannels?.xChannel ??
|
|
657
|
-
const yChannel = xyChannels?.yChannel ??
|
|
658
|
-
const channels: ColorChannel[] = [
|
|
659
|
-
const zChannel = channels.find((c) => c !== xChannel && c !== yChannel) ??
|
|
742
|
+
const xChannel = xyChannels?.xChannel ?? "saturation";
|
|
743
|
+
const yChannel = xyChannels?.yChannel ?? "lightness";
|
|
744
|
+
const channels: ColorChannel[] = ["hue", "saturation", "lightness"];
|
|
745
|
+
const zChannel = channels.find((c) => c !== xChannel && c !== yChannel) ?? "hue";
|
|
660
746
|
return { xChannel, yChannel, zChannel };
|
|
661
747
|
}
|
|
662
748
|
|
|
663
749
|
getColorChannels(): [ColorChannel, ColorChannel, ColorChannel] {
|
|
664
|
-
return [
|
|
750
|
+
return ["hue", "saturation", "lightness"];
|
|
665
751
|
}
|
|
666
752
|
|
|
667
753
|
getColorName(_locale: string): string {
|
|
668
|
-
|
|
669
|
-
return getColorNameFromRgb(r, g, b, this.alpha);
|
|
754
|
+
return getColorNameFromColor(this);
|
|
670
755
|
}
|
|
671
756
|
|
|
672
757
|
getHueName(_locale: string): string {
|
|
673
|
-
return
|
|
758
|
+
return getHueNameFromColor(this);
|
|
674
759
|
}
|
|
675
760
|
}
|
|
676
761
|
|
|
@@ -692,18 +777,18 @@ class HSBColorImpl implements Color {
|
|
|
692
777
|
|
|
693
778
|
toFormat(format: ColorFormat): Color {
|
|
694
779
|
switch (format) {
|
|
695
|
-
case
|
|
696
|
-
case
|
|
780
|
+
case "hsb":
|
|
781
|
+
case "hsba":
|
|
697
782
|
return this.clone();
|
|
698
|
-
case
|
|
699
|
-
case
|
|
700
|
-
case
|
|
701
|
-
case
|
|
783
|
+
case "hex":
|
|
784
|
+
case "hexa":
|
|
785
|
+
case "rgb":
|
|
786
|
+
case "rgba": {
|
|
702
787
|
const { r, g, b } = hsbToRgb(this.hue, this.saturation, this.brightness);
|
|
703
788
|
return new RGBColorImpl(r, g, b, this.alpha);
|
|
704
789
|
}
|
|
705
|
-
case
|
|
706
|
-
case
|
|
790
|
+
case "hsl":
|
|
791
|
+
case "hsla": {
|
|
707
792
|
const { r, g, b } = hsbToRgb(this.hue, this.saturation, this.brightness);
|
|
708
793
|
const hsl = rgbToHsl(r, g, b);
|
|
709
794
|
return new HSLColorImpl(hsl.h, hsl.s, hsl.l, this.alpha);
|
|
@@ -713,16 +798,16 @@ class HSBColorImpl implements Color {
|
|
|
713
798
|
}
|
|
714
799
|
}
|
|
715
800
|
|
|
716
|
-
toString(format?: ColorFormat |
|
|
717
|
-
const f = format ??
|
|
801
|
+
toString(format?: ColorFormat | "css"): string {
|
|
802
|
+
const f = format ?? "css";
|
|
718
803
|
|
|
719
804
|
switch (f) {
|
|
720
|
-
case
|
|
805
|
+
case "hsb":
|
|
721
806
|
return `hsb(${this.hue}, ${this.saturation}%, ${this.brightness}%)`;
|
|
722
|
-
case
|
|
723
|
-
case
|
|
807
|
+
case "hsba":
|
|
808
|
+
case "css":
|
|
724
809
|
// HSB is not a standard CSS format, convert to RGB
|
|
725
|
-
return this.toFormat(
|
|
810
|
+
return this.toFormat("rgba").toString("css");
|
|
726
811
|
default:
|
|
727
812
|
return this.toFormat(f as ColorFormat).toString(f);
|
|
728
813
|
}
|
|
@@ -733,26 +818,26 @@ class HSBColorImpl implements Color {
|
|
|
733
818
|
}
|
|
734
819
|
|
|
735
820
|
toHexInt(): number {
|
|
736
|
-
return this.toFormat(
|
|
821
|
+
return this.toFormat("rgb").toHexInt();
|
|
737
822
|
}
|
|
738
823
|
|
|
739
824
|
getChannelValue(channel: ColorChannel): number {
|
|
740
825
|
switch (channel) {
|
|
741
|
-
case
|
|
826
|
+
case "hue":
|
|
742
827
|
return this.hue;
|
|
743
|
-
case
|
|
828
|
+
case "saturation":
|
|
744
829
|
return this.saturation;
|
|
745
|
-
case
|
|
830
|
+
case "brightness":
|
|
746
831
|
return this.brightness;
|
|
747
|
-
case
|
|
832
|
+
case "alpha":
|
|
748
833
|
return this.alpha;
|
|
749
834
|
// Cross-color-space channels
|
|
750
|
-
case
|
|
751
|
-
case
|
|
752
|
-
case
|
|
753
|
-
return this.toFormat(
|
|
754
|
-
case
|
|
755
|
-
return this.toFormat(
|
|
835
|
+
case "red":
|
|
836
|
+
case "green":
|
|
837
|
+
case "blue":
|
|
838
|
+
return this.toFormat("rgb").getChannelValue(channel);
|
|
839
|
+
case "lightness":
|
|
840
|
+
return this.toFormat("hsl").getChannelValue(channel);
|
|
756
841
|
default:
|
|
757
842
|
throw new Error(`Invalid channel: ${channel}`);
|
|
758
843
|
}
|
|
@@ -760,21 +845,21 @@ class HSBColorImpl implements Color {
|
|
|
760
845
|
|
|
761
846
|
withChannelValue(channel: ColorChannel, value: number): Color {
|
|
762
847
|
switch (channel) {
|
|
763
|
-
case
|
|
848
|
+
case "hue":
|
|
764
849
|
return new HSBColorImpl(value, this.saturation, this.brightness, this.alpha);
|
|
765
|
-
case
|
|
850
|
+
case "saturation":
|
|
766
851
|
return new HSBColorImpl(this.hue, value, this.brightness, this.alpha);
|
|
767
|
-
case
|
|
852
|
+
case "brightness":
|
|
768
853
|
return new HSBColorImpl(this.hue, this.saturation, value, this.alpha);
|
|
769
|
-
case
|
|
854
|
+
case "alpha":
|
|
770
855
|
return new HSBColorImpl(this.hue, this.saturation, this.brightness, value);
|
|
771
856
|
// Cross-color-space channels
|
|
772
|
-
case
|
|
773
|
-
case
|
|
774
|
-
case
|
|
775
|
-
return this.toFormat(
|
|
776
|
-
case
|
|
777
|
-
return this.toFormat(
|
|
857
|
+
case "red":
|
|
858
|
+
case "green":
|
|
859
|
+
case "blue":
|
|
860
|
+
return this.toFormat("rgb").withChannelValue(channel, value).toFormat("hsb");
|
|
861
|
+
case "lightness":
|
|
862
|
+
return this.toFormat("hsl").withChannelValue(channel, value).toFormat("hsb");
|
|
778
863
|
default:
|
|
779
864
|
throw new Error(`Invalid channel: ${channel}`);
|
|
780
865
|
}
|
|
@@ -782,17 +867,17 @@ class HSBColorImpl implements Color {
|
|
|
782
867
|
|
|
783
868
|
getChannelRange(channel: ColorChannel): ColorChannelRange {
|
|
784
869
|
switch (channel) {
|
|
785
|
-
case
|
|
870
|
+
case "hue":
|
|
786
871
|
return HUE_CHANNEL_RANGE;
|
|
787
|
-
case
|
|
788
|
-
case
|
|
789
|
-
case
|
|
872
|
+
case "saturation":
|
|
873
|
+
case "brightness":
|
|
874
|
+
case "lightness":
|
|
790
875
|
return PERCENT_CHANNEL_RANGE;
|
|
791
|
-
case
|
|
876
|
+
case "alpha":
|
|
792
877
|
return ALPHA_CHANNEL_RANGE;
|
|
793
|
-
case
|
|
794
|
-
case
|
|
795
|
-
case
|
|
878
|
+
case "red":
|
|
879
|
+
case "green":
|
|
880
|
+
case "blue":
|
|
796
881
|
return RGB_CHANNEL_RANGE;
|
|
797
882
|
default:
|
|
798
883
|
throw new Error(`Invalid channel: ${channel}`);
|
|
@@ -804,47 +889,46 @@ class HSBColorImpl implements Color {
|
|
|
804
889
|
}
|
|
805
890
|
|
|
806
891
|
getChannelFormatOptions(channel: ColorChannel): Intl.NumberFormatOptions {
|
|
807
|
-
if (channel ===
|
|
808
|
-
return { style:
|
|
892
|
+
if (channel === "alpha") {
|
|
893
|
+
return { style: "percent" };
|
|
809
894
|
}
|
|
810
|
-
if (channel ===
|
|
811
|
-
return { maximumFractionDigits: 0 };
|
|
895
|
+
if (channel === "hue") {
|
|
896
|
+
return { style: "unit", unit: "degree", unitDisplay: "narrow", maximumFractionDigits: 0 };
|
|
812
897
|
}
|
|
813
|
-
return { style:
|
|
898
|
+
return { style: "percent", maximumFractionDigits: 0 };
|
|
814
899
|
}
|
|
815
900
|
|
|
816
901
|
formatChannelValue(channel: ColorChannel, locale: string): string {
|
|
817
902
|
const value = this.getChannelValue(channel);
|
|
818
903
|
const options = this.getChannelFormatOptions(channel);
|
|
819
|
-
if (channel ===
|
|
904
|
+
if (channel === "saturation" || channel === "brightness") {
|
|
820
905
|
return new Intl.NumberFormat(locale, options).format(value / 100);
|
|
821
906
|
}
|
|
822
907
|
return new Intl.NumberFormat(locale, options).format(value);
|
|
823
908
|
}
|
|
824
909
|
|
|
825
910
|
getColorSpace(): ColorSpace {
|
|
826
|
-
return
|
|
911
|
+
return "hsb";
|
|
827
912
|
}
|
|
828
913
|
|
|
829
914
|
getColorSpaceAxes(xyChannels?: { xChannel?: ColorChannel; yChannel?: ColorChannel }): ColorAxes {
|
|
830
|
-
const xChannel = xyChannels?.xChannel ??
|
|
831
|
-
const yChannel = xyChannels?.yChannel ??
|
|
832
|
-
const channels: ColorChannel[] = [
|
|
833
|
-
const zChannel = channels.find((c) => c !== xChannel && c !== yChannel) ??
|
|
915
|
+
const xChannel = xyChannels?.xChannel ?? "saturation";
|
|
916
|
+
const yChannel = xyChannels?.yChannel ?? "brightness";
|
|
917
|
+
const channels: ColorChannel[] = ["hue", "saturation", "brightness"];
|
|
918
|
+
const zChannel = channels.find((c) => c !== xChannel && c !== yChannel) ?? "hue";
|
|
834
919
|
return { xChannel, yChannel, zChannel };
|
|
835
920
|
}
|
|
836
921
|
|
|
837
922
|
getColorChannels(): [ColorChannel, ColorChannel, ColorChannel] {
|
|
838
|
-
return [
|
|
923
|
+
return ["hue", "saturation", "brightness"];
|
|
839
924
|
}
|
|
840
925
|
|
|
841
926
|
getColorName(_locale: string): string {
|
|
842
|
-
|
|
843
|
-
return getColorNameFromRgb(r, g, b, this.alpha);
|
|
927
|
+
return getColorNameFromColor(this);
|
|
844
928
|
}
|
|
845
929
|
|
|
846
930
|
getHueName(_locale: string): string {
|
|
847
|
-
return
|
|
931
|
+
return getHueNameFromColor(this);
|
|
848
932
|
}
|
|
849
933
|
}
|
|
850
934
|
|
|
@@ -855,8 +939,11 @@ export function parseColor(value: string): Color {
|
|
|
855
939
|
const trimmed = value.trim().toLowerCase();
|
|
856
940
|
|
|
857
941
|
// Hex format
|
|
858
|
-
if (trimmed.startsWith(
|
|
942
|
+
if (trimmed.startsWith("#")) {
|
|
859
943
|
const hex = trimmed.slice(1);
|
|
944
|
+
if (!/^[0-9a-f]+$/i.test(hex)) {
|
|
945
|
+
throw new Error(`Invalid hex color: ${value}`);
|
|
946
|
+
}
|
|
860
947
|
if (hex.length === 3) {
|
|
861
948
|
const r = parseInt(hex[0] + hex[0], 16);
|
|
862
949
|
const g = parseInt(hex[1] + hex[1], 16);
|
|
@@ -887,7 +974,9 @@ export function parseColor(value: string): Color {
|
|
|
887
974
|
}
|
|
888
975
|
|
|
889
976
|
// RGB/RGBA format
|
|
890
|
-
const rgbMatch = trimmed.match(
|
|
977
|
+
const rgbMatch = trimmed.match(
|
|
978
|
+
/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)$/,
|
|
979
|
+
);
|
|
891
980
|
if (rgbMatch) {
|
|
892
981
|
const r = parseInt(rgbMatch[1], 10);
|
|
893
982
|
const g = parseInt(rgbMatch[2], 10);
|
|
@@ -897,7 +986,9 @@ export function parseColor(value: string): Color {
|
|
|
897
986
|
}
|
|
898
987
|
|
|
899
988
|
// HSL/HSLA format
|
|
900
|
-
const hslMatch = trimmed.match(
|
|
989
|
+
const hslMatch = trimmed.match(
|
|
990
|
+
/^hsla?\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(?:,\s*([\d.]+))?\s*\)$/,
|
|
991
|
+
);
|
|
901
992
|
if (hslMatch) {
|
|
902
993
|
const h = parseInt(hslMatch[1], 10);
|
|
903
994
|
const s = parseInt(hslMatch[2], 10);
|
|
@@ -907,7 +998,9 @@ export function parseColor(value: string): Color {
|
|
|
907
998
|
}
|
|
908
999
|
|
|
909
1000
|
// HSB/HSBA format
|
|
910
|
-
const hsbMatch = trimmed.match(
|
|
1001
|
+
const hsbMatch = trimmed.match(
|
|
1002
|
+
/^hsba?\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(?:,\s*([\d.]+))?\s*\)$/,
|
|
1003
|
+
);
|
|
911
1004
|
if (hsbMatch) {
|
|
912
1005
|
const h = parseInt(hsbMatch[1], 10);
|
|
913
1006
|
const s = parseInt(hsbMatch[2], 10);
|
|
@@ -929,14 +1022,24 @@ export function createRGBColor(red: number, green: number, blue: number, alpha:
|
|
|
929
1022
|
/**
|
|
930
1023
|
* Create an HSL color.
|
|
931
1024
|
*/
|
|
932
|
-
export function createHSLColor(
|
|
1025
|
+
export function createHSLColor(
|
|
1026
|
+
hue: number,
|
|
1027
|
+
saturation: number,
|
|
1028
|
+
lightness: number,
|
|
1029
|
+
alpha: number = 1,
|
|
1030
|
+
): Color {
|
|
933
1031
|
return new HSLColorImpl(hue, saturation, lightness, alpha);
|
|
934
1032
|
}
|
|
935
1033
|
|
|
936
1034
|
/**
|
|
937
1035
|
* Create an HSB color.
|
|
938
1036
|
*/
|
|
939
|
-
export function createHSBColor(
|
|
1037
|
+
export function createHSBColor(
|
|
1038
|
+
hue: number,
|
|
1039
|
+
saturation: number,
|
|
1040
|
+
brightness: number,
|
|
1041
|
+
alpha: number = 1,
|
|
1042
|
+
): Color {
|
|
940
1043
|
return new HSBColorImpl(hue, saturation, brightness, alpha);
|
|
941
1044
|
}
|
|
942
1045
|
|
|
@@ -944,7 +1047,7 @@ export function createHSBColor(hue: number, saturation: number, brightness: numb
|
|
|
944
1047
|
* Normalize a color value (string or Color) to a Color object.
|
|
945
1048
|
*/
|
|
946
1049
|
export function normalizeColor(value: string | Color): Color {
|
|
947
|
-
if (typeof value ===
|
|
1050
|
+
if (typeof value === "string") {
|
|
948
1051
|
return parseColor(value);
|
|
949
1052
|
}
|
|
950
1053
|
return value;
|