@electrovir/color 0.0.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/LICENSE-CC0 +121 -0
- package/LICENSE-MIT +21 -0
- package/README.md +50 -0
- package/dist/color-formats.d.ts +254 -0
- package/dist/color-formats.js +175 -0
- package/dist/color-name-length.d.ts +176 -0
- package/dist/color-name-length.js +75 -0
- package/dist/color.d.ts +171 -0
- package/dist/color.js +243 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/package.json +109 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The longest CSS color name. Used for color name padding calculations.
|
|
3
|
+
*
|
|
4
|
+
* @category Internal
|
|
5
|
+
*/
|
|
6
|
+
export declare const longestColorName: string;
|
|
7
|
+
/**
|
|
8
|
+
* All CSS color names with identical value matches to other CSS color names. Used for color name
|
|
9
|
+
* padding calculations.
|
|
10
|
+
*
|
|
11
|
+
* @category Internal
|
|
12
|
+
*/
|
|
13
|
+
export declare const identicalColorNames: Partial<{
|
|
14
|
+
aliceblue: string[];
|
|
15
|
+
antiquewhite: string[];
|
|
16
|
+
aqua: string[];
|
|
17
|
+
aquamarine: string[];
|
|
18
|
+
azure: string[];
|
|
19
|
+
beige: string[];
|
|
20
|
+
bisque: string[];
|
|
21
|
+
black: string[];
|
|
22
|
+
blanchedalmond: string[];
|
|
23
|
+
blue: string[];
|
|
24
|
+
blueviolet: string[];
|
|
25
|
+
brown: string[];
|
|
26
|
+
burlywood: string[];
|
|
27
|
+
cadetblue: string[];
|
|
28
|
+
chartreuse: string[];
|
|
29
|
+
chocolate: string[];
|
|
30
|
+
coral: string[];
|
|
31
|
+
cornflowerblue: string[];
|
|
32
|
+
cornsilk: string[];
|
|
33
|
+
crimson: string[];
|
|
34
|
+
cyan: string[];
|
|
35
|
+
darkblue: string[];
|
|
36
|
+
darkcyan: string[];
|
|
37
|
+
darkgoldenrod: string[];
|
|
38
|
+
darkgray: string[];
|
|
39
|
+
darkgreen: string[];
|
|
40
|
+
darkgrey: string[];
|
|
41
|
+
darkkhaki: string[];
|
|
42
|
+
darkmagenta: string[];
|
|
43
|
+
darkolivegreen: string[];
|
|
44
|
+
darkorange: string[];
|
|
45
|
+
darkorchid: string[];
|
|
46
|
+
darkred: string[];
|
|
47
|
+
darksalmon: string[];
|
|
48
|
+
darkseagreen: string[];
|
|
49
|
+
darkslateblue: string[];
|
|
50
|
+
darkslategray: string[];
|
|
51
|
+
darkslategrey: string[];
|
|
52
|
+
darkturquoise: string[];
|
|
53
|
+
darkviolet: string[];
|
|
54
|
+
deeppink: string[];
|
|
55
|
+
deepskyblue: string[];
|
|
56
|
+
dimgray: string[];
|
|
57
|
+
dimgrey: string[];
|
|
58
|
+
dodgerblue: string[];
|
|
59
|
+
firebrick: string[];
|
|
60
|
+
floralwhite: string[];
|
|
61
|
+
forestgreen: string[];
|
|
62
|
+
fuchsia: string[];
|
|
63
|
+
gainsboro: string[];
|
|
64
|
+
ghostwhite: string[];
|
|
65
|
+
gold: string[];
|
|
66
|
+
goldenrod: string[];
|
|
67
|
+
gray: string[];
|
|
68
|
+
green: string[];
|
|
69
|
+
greenyellow: string[];
|
|
70
|
+
grey: string[];
|
|
71
|
+
honeydew: string[];
|
|
72
|
+
hotpink: string[];
|
|
73
|
+
indianred: string[];
|
|
74
|
+
indigo: string[];
|
|
75
|
+
ivory: string[];
|
|
76
|
+
khaki: string[];
|
|
77
|
+
lavender: string[];
|
|
78
|
+
lavenderblush: string[];
|
|
79
|
+
lawngreen: string[];
|
|
80
|
+
lemonchiffon: string[];
|
|
81
|
+
lightblue: string[];
|
|
82
|
+
lightcoral: string[];
|
|
83
|
+
lightcyan: string[];
|
|
84
|
+
lightgoldenrodyellow: string[];
|
|
85
|
+
lightgray: string[];
|
|
86
|
+
lightgreen: string[];
|
|
87
|
+
lightgrey: string[];
|
|
88
|
+
lightpink: string[];
|
|
89
|
+
lightsalmon: string[];
|
|
90
|
+
lightseagreen: string[];
|
|
91
|
+
lightskyblue: string[];
|
|
92
|
+
lightslategray: string[];
|
|
93
|
+
lightslategrey: string[];
|
|
94
|
+
lightsteelblue: string[];
|
|
95
|
+
lightyellow: string[];
|
|
96
|
+
lime: string[];
|
|
97
|
+
limegreen: string[];
|
|
98
|
+
linen: string[];
|
|
99
|
+
magenta: string[];
|
|
100
|
+
maroon: string[];
|
|
101
|
+
mediumaquamarine: string[];
|
|
102
|
+
mediumblue: string[];
|
|
103
|
+
mediumorchid: string[];
|
|
104
|
+
mediumpurple: string[];
|
|
105
|
+
mediumseagreen: string[];
|
|
106
|
+
mediumslateblue: string[];
|
|
107
|
+
mediumspringgreen: string[];
|
|
108
|
+
mediumturquoise: string[];
|
|
109
|
+
mediumvioletred: string[];
|
|
110
|
+
midnightblue: string[];
|
|
111
|
+
mintcream: string[];
|
|
112
|
+
mistyrose: string[];
|
|
113
|
+
moccasin: string[];
|
|
114
|
+
navajowhite: string[];
|
|
115
|
+
navy: string[];
|
|
116
|
+
oldlace: string[];
|
|
117
|
+
olive: string[];
|
|
118
|
+
olivedrab: string[];
|
|
119
|
+
orange: string[];
|
|
120
|
+
orangered: string[];
|
|
121
|
+
orchid: string[];
|
|
122
|
+
palegoldenrod: string[];
|
|
123
|
+
palegreen: string[];
|
|
124
|
+
paleturquoise: string[];
|
|
125
|
+
palevioletred: string[];
|
|
126
|
+
papayawhip: string[];
|
|
127
|
+
peachpuff: string[];
|
|
128
|
+
peru: string[];
|
|
129
|
+
pink: string[];
|
|
130
|
+
plum: string[];
|
|
131
|
+
powderblue: string[];
|
|
132
|
+
purple: string[];
|
|
133
|
+
rebeccapurple: string[];
|
|
134
|
+
red: string[];
|
|
135
|
+
rosybrown: string[];
|
|
136
|
+
royalblue: string[];
|
|
137
|
+
saddlebrown: string[];
|
|
138
|
+
salmon: string[];
|
|
139
|
+
sandybrown: string[];
|
|
140
|
+
seagreen: string[];
|
|
141
|
+
seashell: string[];
|
|
142
|
+
sienna: string[];
|
|
143
|
+
silver: string[];
|
|
144
|
+
skyblue: string[];
|
|
145
|
+
slateblue: string[];
|
|
146
|
+
slategray: string[];
|
|
147
|
+
slategrey: string[];
|
|
148
|
+
snow: string[];
|
|
149
|
+
springgreen: string[];
|
|
150
|
+
steelblue: string[];
|
|
151
|
+
tan: string[];
|
|
152
|
+
teal: string[];
|
|
153
|
+
thistle: string[];
|
|
154
|
+
tomato: string[];
|
|
155
|
+
turquoise: string[];
|
|
156
|
+
violet: string[];
|
|
157
|
+
wheat: string[];
|
|
158
|
+
white: string[];
|
|
159
|
+
whitesmoke: string[];
|
|
160
|
+
yellow: string[];
|
|
161
|
+
yellowgreen: string[];
|
|
162
|
+
}>;
|
|
163
|
+
/**
|
|
164
|
+
* The longest collection of CSS color names with identical values. Used for color name padding
|
|
165
|
+
* calculations.
|
|
166
|
+
*
|
|
167
|
+
* @category Internal
|
|
168
|
+
*/
|
|
169
|
+
export declare const longestIdenticalColorNames: string[];
|
|
170
|
+
/**
|
|
171
|
+
* The maximum length that a single-value collection of matching CSS color names can be (when joined
|
|
172
|
+
* with `', '`). Used for color name padding calculations.
|
|
173
|
+
*
|
|
174
|
+
* @category Internal
|
|
175
|
+
*/
|
|
176
|
+
export declare const maxColorNameLength: number;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { check } from '@augment-vir/assert';
|
|
2
|
+
import { filterMap, filterObject, mapObjectValues } from '@augment-vir/common';
|
|
3
|
+
import colorNames from 'color-name';
|
|
4
|
+
/**
|
|
5
|
+
* The longest CSS color name. Used for color name padding calculations.
|
|
6
|
+
*
|
|
7
|
+
* @category Internal
|
|
8
|
+
*/
|
|
9
|
+
export const longestColorName = Object.keys(colorNames).reduce((longest, current) => {
|
|
10
|
+
if (current.length > longest.length) {
|
|
11
|
+
return current;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
return longest;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* All CSS color names with identical value matches to other CSS color names. Used for color name
|
|
19
|
+
* padding calculations.
|
|
20
|
+
*
|
|
21
|
+
* @category Internal
|
|
22
|
+
*/
|
|
23
|
+
export const identicalColorNames = filterObject(mapObjectValues(colorNames, (colorName, rgbValues) => {
|
|
24
|
+
const matches = filterMap(Object.entries(colorNames), ([colorName]) => colorName, (innerColorName, [, innerRgbValues,]) => {
|
|
25
|
+
if (innerColorName === colorName) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return check.deepEquals(innerRgbValues, rgbValues);
|
|
29
|
+
});
|
|
30
|
+
return matches;
|
|
31
|
+
}), (colorName, matches) => !!matches.length);
|
|
32
|
+
/**
|
|
33
|
+
* The longest collection of CSS color names with identical values. Used for color name padding
|
|
34
|
+
* calculations.
|
|
35
|
+
*
|
|
36
|
+
* @category Internal
|
|
37
|
+
*/
|
|
38
|
+
export const longestIdenticalColorNames = Object.entries(identicalColorNames)
|
|
39
|
+
.reduce((longest, current) => {
|
|
40
|
+
const longestString = [
|
|
41
|
+
longest[0],
|
|
42
|
+
...longest[1],
|
|
43
|
+
].join(', ');
|
|
44
|
+
const currentString = [
|
|
45
|
+
current[0],
|
|
46
|
+
...current[1],
|
|
47
|
+
].join(', ');
|
|
48
|
+
if (currentString.length > longestString.length) {
|
|
49
|
+
return current;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
return longest;
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
.reduce((combined, current) => {
|
|
56
|
+
if (check.isArray(current)) {
|
|
57
|
+
return [
|
|
58
|
+
...combined,
|
|
59
|
+
...current,
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
return [
|
|
64
|
+
...combined,
|
|
65
|
+
current,
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
}, []);
|
|
69
|
+
/**
|
|
70
|
+
* The maximum length that a single-value collection of matching CSS color names can be (when joined
|
|
71
|
+
* with `', '`). Used for color name padding calculations.
|
|
72
|
+
*
|
|
73
|
+
* @category Internal
|
|
74
|
+
*/
|
|
75
|
+
export const maxColorNameLength = Math.max(longestColorName.length, longestIdenticalColorNames.length + (longestIdenticalColorNames.length - 1) * ', '.length);
|
package/dist/color.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { type PartialWithUndefined } from '@augment-vir/common';
|
|
2
|
+
import { type RequireExactlyOne } from 'type-fest';
|
|
3
|
+
import { type ColorCoordsByFormat, type ColorFormatName, type ColorValues, type HexColor } from './color-formats.js';
|
|
4
|
+
/**
|
|
5
|
+
* An update to an existing {@link Color} instance. Used in {@link Color.set}.
|
|
6
|
+
*
|
|
7
|
+
* @category Internal
|
|
8
|
+
*/
|
|
9
|
+
export type ColorUpdate = RequireExactlyOne<{
|
|
10
|
+
[FormatName in ColorFormatName]: PartialWithUndefined<Record<ColorCoordsByFormat[FormatName], number>>;
|
|
11
|
+
} & {
|
|
12
|
+
hex: HexColor;
|
|
13
|
+
name: string;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* The values of all supported color formats for a given {@link Color} instance. Accessed via
|
|
17
|
+
* {@link Color.allColors}.
|
|
18
|
+
*
|
|
19
|
+
* @category Internal
|
|
20
|
+
*/
|
|
21
|
+
export type AllColorsValues = ColorValues & {
|
|
22
|
+
hex: HexColor;
|
|
23
|
+
names: string[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A `Color` class with state and the following features:
|
|
27
|
+
*
|
|
28
|
+
* - Color coordinates do not change when they become `'none'`, they stay at their previous value.
|
|
29
|
+
* This makes for a much smoother UI experience.
|
|
30
|
+
* - All relevant color spaces and formats are always set (no extra conversions necessary).
|
|
31
|
+
* - Matching CSS color names are tracked.
|
|
32
|
+
* - This class is exported correctly and the types aren't a mess.
|
|
33
|
+
*
|
|
34
|
+
* @category Color
|
|
35
|
+
*/
|
|
36
|
+
export declare class Color {
|
|
37
|
+
#private;
|
|
38
|
+
/**
|
|
39
|
+
* Create a new {@link Color} instance by parsing the output of another instance's
|
|
40
|
+
* {@link Color.serialize} method.
|
|
41
|
+
*/
|
|
42
|
+
static deserialize(input: string): Color;
|
|
43
|
+
/** All current color values. These are updated whenever {@link Color.set} is called. */
|
|
44
|
+
protected readonly _allColors: {
|
|
45
|
+
names: string[];
|
|
46
|
+
hex: HexColor;
|
|
47
|
+
rgb: {
|
|
48
|
+
r: number;
|
|
49
|
+
g: number;
|
|
50
|
+
b: number;
|
|
51
|
+
};
|
|
52
|
+
hsl: {
|
|
53
|
+
h: number;
|
|
54
|
+
s: number;
|
|
55
|
+
l: number;
|
|
56
|
+
};
|
|
57
|
+
hwb: {
|
|
58
|
+
h: number;
|
|
59
|
+
w: number;
|
|
60
|
+
b: number;
|
|
61
|
+
};
|
|
62
|
+
lab: {
|
|
63
|
+
l: number;
|
|
64
|
+
a: number;
|
|
65
|
+
b: number;
|
|
66
|
+
};
|
|
67
|
+
lch: {
|
|
68
|
+
l: number;
|
|
69
|
+
c: number;
|
|
70
|
+
h: number;
|
|
71
|
+
};
|
|
72
|
+
oklab: {
|
|
73
|
+
l: number;
|
|
74
|
+
a: number;
|
|
75
|
+
b: number;
|
|
76
|
+
};
|
|
77
|
+
oklch: {
|
|
78
|
+
l: number;
|
|
79
|
+
c: number;
|
|
80
|
+
h: number;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
constructor(
|
|
84
|
+
/** Any valid CSS color string or an object of color coordinate values. */
|
|
85
|
+
initValue: string | Readonly<ColorUpdate>);
|
|
86
|
+
/** Create a new {@link Color} instance that matches this one exactly. */
|
|
87
|
+
clone(): Color;
|
|
88
|
+
/**
|
|
89
|
+
* Update the color to match the given string.
|
|
90
|
+
*
|
|
91
|
+
* @throws Error if `cssColorString` is not able to be parsed.
|
|
92
|
+
*/
|
|
93
|
+
protected setByString(cssColorString: string): void;
|
|
94
|
+
/**
|
|
95
|
+
* Update the current color by setting a whole new color, a single coordinate in a single color
|
|
96
|
+
* format, or multiple coordinates in a single color format. This mutates the current
|
|
97
|
+
* {@link Color} instance.
|
|
98
|
+
*/
|
|
99
|
+
set(newValue: Readonly<ColorUpdate> | string): void;
|
|
100
|
+
/**
|
|
101
|
+
* Update all internally stored color format values ({@link Color.allColors}) from the updated
|
|
102
|
+
* internal color object.
|
|
103
|
+
*/
|
|
104
|
+
protected pullFromInternalColor(): void;
|
|
105
|
+
/**
|
|
106
|
+
* Create a string that can be serialized into a new {@link Color} instance which will exactly
|
|
107
|
+
* match the current {@link Color} instance.
|
|
108
|
+
*/
|
|
109
|
+
serialize(): string;
|
|
110
|
+
/** This individual color expressed in all the supported color formats. */
|
|
111
|
+
get allColors(): AllColorsValues;
|
|
112
|
+
/**
|
|
113
|
+
* Converts the values for each supported color format into a padded string for easy display
|
|
114
|
+
* purposes.
|
|
115
|
+
*/
|
|
116
|
+
toFormattedStrings(): Record<ColorFormatName | 'hex' | 'names', string>;
|
|
117
|
+
/**
|
|
118
|
+
* Converts the values for each supported color format in a CSS string that can be directly used
|
|
119
|
+
* in any modern CSS code.
|
|
120
|
+
*/
|
|
121
|
+
toCss(): Record<ColorFormatName | 'hex' | 'name', string>;
|
|
122
|
+
/**
|
|
123
|
+
* The current color expressed as hardcoded CSS color keywords. If no CSS color keywords match
|
|
124
|
+
* the current color, this array will be empty.
|
|
125
|
+
*/
|
|
126
|
+
get names(): string[];
|
|
127
|
+
/** The current color expressed as an RGB hex string. */
|
|
128
|
+
get hex(): HexColor;
|
|
129
|
+
/** The current color expressed as its RGB coordinate values. */
|
|
130
|
+
get rgb(): {
|
|
131
|
+
r: number;
|
|
132
|
+
g: number;
|
|
133
|
+
b: number;
|
|
134
|
+
};
|
|
135
|
+
/** The current color expressed as its HSL coordinate values. */
|
|
136
|
+
get hsl(): {
|
|
137
|
+
h: number;
|
|
138
|
+
s: number;
|
|
139
|
+
l: number;
|
|
140
|
+
};
|
|
141
|
+
/** The current color expressed as its HWB coordinate values. */
|
|
142
|
+
get hwb(): {
|
|
143
|
+
b: number;
|
|
144
|
+
h: number;
|
|
145
|
+
w: number;
|
|
146
|
+
};
|
|
147
|
+
/** The current color expressed as its LAB coordinate values. */
|
|
148
|
+
get lab(): {
|
|
149
|
+
b: number;
|
|
150
|
+
l: number;
|
|
151
|
+
a: number;
|
|
152
|
+
};
|
|
153
|
+
/** The current color expressed as its LCH coordinate values. */
|
|
154
|
+
get lch(): {
|
|
155
|
+
h: number;
|
|
156
|
+
l: number;
|
|
157
|
+
c: number;
|
|
158
|
+
};
|
|
159
|
+
/** The current color expressed as its Oklab coordinate values. */
|
|
160
|
+
get oklab(): {
|
|
161
|
+
b: number;
|
|
162
|
+
l: number;
|
|
163
|
+
a: number;
|
|
164
|
+
};
|
|
165
|
+
/** The current color expressed as its Oklch coordinate values. */
|
|
166
|
+
get oklch(): {
|
|
167
|
+
h: number;
|
|
168
|
+
l: number;
|
|
169
|
+
c: number;
|
|
170
|
+
};
|
|
171
|
+
}
|
package/dist/color.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { assert, assertWrap, check } from '@augment-vir/assert';
|
|
2
|
+
import { copyThroughJson, filterMap, getObjectTypedEntries, joinWithFinalConjunction, mapObjectValues, round, } from '@augment-vir/common';
|
|
3
|
+
import colorNames from 'color-name';
|
|
4
|
+
import { clampGamut, converter, formatHex, parse } from 'culori';
|
|
5
|
+
import { colorFormatNames, colorFormats, } from './color-formats.js';
|
|
6
|
+
import { maxColorNameLength } from './color-name-length.js';
|
|
7
|
+
/**
|
|
8
|
+
* A `Color` class with state and the following features:
|
|
9
|
+
*
|
|
10
|
+
* - Color coordinates do not change when they become `'none'`, they stay at their previous value.
|
|
11
|
+
* This makes for a much smoother UI experience.
|
|
12
|
+
* - All relevant color spaces and formats are always set (no extra conversions necessary).
|
|
13
|
+
* - Matching CSS color names are tracked.
|
|
14
|
+
* - This class is exported correctly and the types aren't a mess.
|
|
15
|
+
*
|
|
16
|
+
* @category Color
|
|
17
|
+
*/
|
|
18
|
+
export class Color {
|
|
19
|
+
/**
|
|
20
|
+
* Create a new {@link Color} instance by parsing the output of another instance's
|
|
21
|
+
* {@link Color.serialize} method.
|
|
22
|
+
*/
|
|
23
|
+
static deserialize(input) {
|
|
24
|
+
const parsed = JSON.parse(input);
|
|
25
|
+
const newColor = new Color('black');
|
|
26
|
+
getObjectTypedEntries(parsed).forEach(([key, value,]) => {
|
|
27
|
+
newColor._allColors[key] = value;
|
|
28
|
+
});
|
|
29
|
+
return newColor;
|
|
30
|
+
}
|
|
31
|
+
#internalColor = assertWrap.isDefined(parse('black'));
|
|
32
|
+
/** All current color values. These are updated whenever {@link Color.set} is called. */
|
|
33
|
+
_allColors = {
|
|
34
|
+
names: ['black'],
|
|
35
|
+
hex: '#000000',
|
|
36
|
+
rgb: {
|
|
37
|
+
r: 0,
|
|
38
|
+
g: 0,
|
|
39
|
+
b: 0,
|
|
40
|
+
},
|
|
41
|
+
hsl: {
|
|
42
|
+
h: 0,
|
|
43
|
+
s: 0,
|
|
44
|
+
l: 0,
|
|
45
|
+
},
|
|
46
|
+
hwb: {
|
|
47
|
+
h: 0,
|
|
48
|
+
w: 0,
|
|
49
|
+
b: 0,
|
|
50
|
+
},
|
|
51
|
+
lab: {
|
|
52
|
+
l: 0,
|
|
53
|
+
a: 0,
|
|
54
|
+
b: 0,
|
|
55
|
+
},
|
|
56
|
+
lch: {
|
|
57
|
+
l: 0,
|
|
58
|
+
c: 0,
|
|
59
|
+
h: 0,
|
|
60
|
+
},
|
|
61
|
+
oklab: {
|
|
62
|
+
l: 0,
|
|
63
|
+
a: 0,
|
|
64
|
+
b: 0,
|
|
65
|
+
},
|
|
66
|
+
oklch: {
|
|
67
|
+
l: 0,
|
|
68
|
+
c: 0,
|
|
69
|
+
h: 0,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
constructor(
|
|
73
|
+
/** Any valid CSS color string or an object of color coordinate values. */
|
|
74
|
+
initValue) {
|
|
75
|
+
this.set(initValue);
|
|
76
|
+
}
|
|
77
|
+
/** Create a new {@link Color} instance that matches this one exactly. */
|
|
78
|
+
clone() {
|
|
79
|
+
return Color.deserialize(this.serialize());
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Update the color to match the given string.
|
|
83
|
+
*
|
|
84
|
+
* @throws Error if `cssColorString` is not able to be parsed.
|
|
85
|
+
*/
|
|
86
|
+
setByString(cssColorString) {
|
|
87
|
+
const newColor = parse(cssColorString);
|
|
88
|
+
if (!newColor) {
|
|
89
|
+
throw new Error(`Unable to parse invalid color string: '${cssColorString}'`);
|
|
90
|
+
}
|
|
91
|
+
this.#internalColor = newColor;
|
|
92
|
+
this.pullFromInternalColor();
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Update the current color by setting a whole new color, a single coordinate in a single color
|
|
96
|
+
* format, or multiple coordinates in a single color format. This mutates the current
|
|
97
|
+
* {@link Color} instance.
|
|
98
|
+
*/
|
|
99
|
+
set(newValue) {
|
|
100
|
+
if (check.isString(newValue)) {
|
|
101
|
+
return this.setByString(newValue);
|
|
102
|
+
}
|
|
103
|
+
assert.isLengthExactly(Object.keys(newValue), 1, `Cannot set multiple color formats at once: got '${joinWithFinalConjunction(Object.keys(newValue))}'`);
|
|
104
|
+
if (newValue.hex || newValue.name) {
|
|
105
|
+
this.setByString(newValue.hex || newValue.name);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const [colorFormatName, colorValues,] = assertWrap.isDefined(getObjectTypedEntries(newValue)[0]);
|
|
109
|
+
const colorFormatDefinition = colorFormats[colorFormatName];
|
|
110
|
+
const orderedColorCoords = Object.values(mapObjectValues(colorFormatDefinition.coords, (coordName) => {
|
|
111
|
+
const coordValue = colorValues[coordName];
|
|
112
|
+
const coordDefinition = assertWrap.isDefined(colorFormatDefinition.coords[coordName]);
|
|
113
|
+
const rawCoordValue = coordValue != undefined &&
|
|
114
|
+
coordValue >= coordDefinition.min &&
|
|
115
|
+
coordValue <= coordDefinition.max
|
|
116
|
+
? colorValues[coordName]
|
|
117
|
+
: this[colorFormatName][coordName];
|
|
118
|
+
return assertWrap.isDefined(rawCoordValue);
|
|
119
|
+
}));
|
|
120
|
+
this.setByString(`${colorFormatName}(${orderedColorCoords.join(' ')})`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Update all internally stored color format values ({@link Color.allColors}) from the updated
|
|
125
|
+
* internal color object.
|
|
126
|
+
*/
|
|
127
|
+
pullFromInternalColor() {
|
|
128
|
+
colorFormatNames.forEach((colorFormatName) => {
|
|
129
|
+
const colorFormatDefinition = colorFormats[colorFormatName];
|
|
130
|
+
const originalColorDefinition = check.isKeyOf(this.#internalColor.mode, colorFormats)
|
|
131
|
+
? colorFormats[this.#internalColor.mode]
|
|
132
|
+
: undefined;
|
|
133
|
+
const converted = clampGamut(colorFormatDefinition.colorSpace === originalColorDefinition?.colorSpace
|
|
134
|
+
? colorFormatName
|
|
135
|
+
: 'rgb')(converter(colorFormatName)(this.#internalColor));
|
|
136
|
+
/* node:coverage ignore next 5: technically this shouldn't happen, idk how to manually trigger it. */
|
|
137
|
+
if (!converted) {
|
|
138
|
+
assert.never(`Failed to convert color '${JSON.stringify(this.#internalColor)}' to '${colorFormatName}'.`);
|
|
139
|
+
}
|
|
140
|
+
Object.keys(this[colorFormatName]).forEach((coordName) => {
|
|
141
|
+
const coordValue = converted[coordName];
|
|
142
|
+
if (coordValue != undefined) {
|
|
143
|
+
this._allColors[colorFormatName][coordName] = round((coordValue || 0) * (colorFormatDefinition.coords[coordName]?.factor || 1), {
|
|
144
|
+
digits: colorFormatDefinition.coords[coordName]?.digits || 0,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
this._allColors.hex = formatHex(this.#internalColor);
|
|
150
|
+
this._allColors.names = findMatchingColorNames(this.rgb);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create a string that can be serialized into a new {@link Color} instance which will exactly
|
|
154
|
+
* match the current {@link Color} instance.
|
|
155
|
+
*/
|
|
156
|
+
serialize() {
|
|
157
|
+
return JSON.stringify(this.allColors);
|
|
158
|
+
}
|
|
159
|
+
/** This individual color expressed in all the supported color formats. */
|
|
160
|
+
get allColors() {
|
|
161
|
+
return copyThroughJson(this._allColors);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Converts the values for each supported color format into a padded string for easy display
|
|
165
|
+
* purposes.
|
|
166
|
+
*/
|
|
167
|
+
toFormattedStrings() {
|
|
168
|
+
const colorFormatStrings = mapObjectValues(colorFormats, (colorFormatName) => {
|
|
169
|
+
const coordValues = Object.values(this[colorFormatName]);
|
|
170
|
+
return coordValues.map((coordValue) => String(coordValue).padStart(6, ' ')).join(' ');
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
hex: this.hex,
|
|
174
|
+
...colorFormatStrings,
|
|
175
|
+
names: this.names.join(', ').padEnd(maxColorNameLength, ' '),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Converts the values for each supported color format in a CSS string that can be directly used
|
|
180
|
+
* in any modern CSS code.
|
|
181
|
+
*/
|
|
182
|
+
toCss() {
|
|
183
|
+
const colorFormatStrings = mapObjectValues(colorFormats, (colorFormatName) => {
|
|
184
|
+
const coordValues = Object.values(this[colorFormatName]);
|
|
185
|
+
return `${colorFormatName}(${coordValues.join(' ')})`;
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
hex: this.hex,
|
|
189
|
+
...colorFormatStrings,
|
|
190
|
+
name: this.names[0] || '',
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The current color expressed as hardcoded CSS color keywords. If no CSS color keywords match
|
|
195
|
+
* the current color, this array will be empty.
|
|
196
|
+
*/
|
|
197
|
+
get names() {
|
|
198
|
+
return copyThroughJson(this._allColors.names);
|
|
199
|
+
}
|
|
200
|
+
/** The current color expressed as an RGB hex string. */
|
|
201
|
+
get hex() {
|
|
202
|
+
return copyThroughJson(this._allColors.hex);
|
|
203
|
+
}
|
|
204
|
+
/** The current color expressed as its RGB coordinate values. */
|
|
205
|
+
get rgb() {
|
|
206
|
+
return copyThroughJson(this._allColors.rgb);
|
|
207
|
+
}
|
|
208
|
+
/** The current color expressed as its HSL coordinate values. */
|
|
209
|
+
get hsl() {
|
|
210
|
+
return copyThroughJson(this._allColors.hsl);
|
|
211
|
+
}
|
|
212
|
+
/** The current color expressed as its HWB coordinate values. */
|
|
213
|
+
get hwb() {
|
|
214
|
+
return copyThroughJson(this._allColors.hwb);
|
|
215
|
+
}
|
|
216
|
+
/** The current color expressed as its LAB coordinate values. */
|
|
217
|
+
get lab() {
|
|
218
|
+
return copyThroughJson(this._allColors.lab);
|
|
219
|
+
}
|
|
220
|
+
/** The current color expressed as its LCH coordinate values. */
|
|
221
|
+
get lch() {
|
|
222
|
+
return copyThroughJson(this._allColors.lch);
|
|
223
|
+
}
|
|
224
|
+
/** The current color expressed as its Oklab coordinate values. */
|
|
225
|
+
get oklab() {
|
|
226
|
+
return copyThroughJson(this._allColors.oklab);
|
|
227
|
+
}
|
|
228
|
+
/** The current color expressed as its Oklch coordinate values. */
|
|
229
|
+
get oklch() {
|
|
230
|
+
return copyThroughJson(this._allColors.oklch);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function findMatchingColorNames(rgb) {
|
|
234
|
+
return filterMap(getObjectTypedEntries(colorNames), ([colorName,]) => {
|
|
235
|
+
return colorName;
|
|
236
|
+
}, (colorName, [, colorValues,]) => {
|
|
237
|
+
return check.deepEquals(colorValues, [
|
|
238
|
+
rgb.r,
|
|
239
|
+
rgb.g,
|
|
240
|
+
rgb.b,
|
|
241
|
+
]);
|
|
242
|
+
});
|
|
243
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED