@versatiles/style 5.6.0 → 5.8.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 +44 -38
- package/dist/index.d.ts +461 -75
- package/dist/index.js +651 -176
- package/dist/index.js.map +1 -1
- package/package.json +35 -33
- package/src/color/abstract.ts +142 -27
- package/src/color/hsl.test.ts +21 -20
- package/src/color/hsl.ts +88 -5
- package/src/color/hsv.test.ts +19 -18
- package/src/color/hsv.ts +78 -5
- package/src/color/index.test.ts +12 -30
- package/src/color/index.ts +0 -3
- package/src/color/random.test.ts +26 -4
- package/src/color/rgb.test.ts +25 -24
- package/src/color/rgb.ts +142 -8
- package/src/color/utils.test.ts +16 -15
- package/src/guess_style/guess_style.test.ts +1 -1
- package/src/guess_style/guess_style.ts +42 -2
- package/src/index.test.ts +43 -16
- package/src/index.ts +88 -3
- package/src/lib/utils.test.ts +1 -0
- package/src/lib/utils.ts +3 -6
- package/src/shortbread/layers.test.ts +1 -0
- package/src/shortbread/properties.test.ts +1 -0
- package/src/shortbread/template.test.ts +1 -0
- package/src/style_builder/decorator.test.ts +1 -0
- package/src/style_builder/recolor.test.ts +1 -0
- package/src/style_builder/recolor.ts +10 -10
- package/src/style_builder/style_builder.test.ts +1 -1
- package/src/style_builder/style_builder.ts +9 -11
- package/src/style_builder/types.ts +23 -58
- package/src/styles/index.ts +2 -0
- package/src/styles/shadow.ts +11 -0
- package/src/types/tilejson.test.ts +1 -2
- package/src/types/vector_layer.test.ts +1 -0
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versatiles/style",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "Generate StyleJSON for MapLibre",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"devEngines": {
|
|
8
|
+
"runtime": {
|
|
9
|
+
"name": "node",
|
|
10
|
+
"version": ">=20.0.0 <24.0.0"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
7
13
|
"scripts": {
|
|
8
14
|
"check": "npm run lint && npm run build && npm run test ",
|
|
9
15
|
"build": "rm -rf release; npm run build-browser && npm run build-node && npm run build-styles && npm run build-sprites && npm run doc",
|
|
10
|
-
"build-browser": "rollup
|
|
11
|
-
"build-node": "rm -rf dist && rollup
|
|
16
|
+
"build-browser": "rollup -c --environment BUILD:browser && $(cd release/versatiles-style; tar -cf - versatiles-style.* | gzip -9 > ../versatiles-style.tar.gz)",
|
|
17
|
+
"build-node": "rm -rf dist && rollup -c --environment BUILD:node && chmod +x dist/index.js && rm -r dist/declaration",
|
|
12
18
|
"build-styles": "tsx scripts/build-styles.ts",
|
|
13
19
|
"build-sprites": "tsx scripts/build-sprites.ts",
|
|
14
20
|
"dev": "tsx scripts/dev.ts",
|
|
@@ -20,8 +26,8 @@
|
|
|
20
26
|
"prepack": "npm run build",
|
|
21
27
|
"release": "vrt release-npm",
|
|
22
28
|
"test": "npm run test-typescript",
|
|
23
|
-
"test-coverage": "
|
|
24
|
-
"test-typescript": "
|
|
29
|
+
"test-coverage": "vitest run --coverage",
|
|
30
|
+
"test-typescript": "vitest run",
|
|
25
31
|
"upgrade": "vrt deps-upgrade"
|
|
26
32
|
},
|
|
27
33
|
"repository": {
|
|
@@ -32,44 +38,40 @@
|
|
|
32
38
|
"license": "MIT",
|
|
33
39
|
"type": "module",
|
|
34
40
|
"dependencies": {
|
|
35
|
-
"brace-expansion": "^4.0.
|
|
41
|
+
"brace-expansion": "^4.0.1"
|
|
36
42
|
},
|
|
37
43
|
"files": [
|
|
38
44
|
"dist/*",
|
|
39
45
|
"src/*"
|
|
40
46
|
],
|
|
41
47
|
"devDependencies": {
|
|
42
|
-
"@maplibre/maplibre-gl-native": "^6.
|
|
43
|
-
"@maplibre/maplibre-gl-style-spec": "^
|
|
44
|
-
"@rollup/plugin-commonjs": "^
|
|
45
|
-
"@rollup/plugin-node-resolve": "^16.0.
|
|
48
|
+
"@maplibre/maplibre-gl-native": "^6.2.0",
|
|
49
|
+
"@maplibre/maplibre-gl-style-spec": "^24.3.1",
|
|
50
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
51
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
46
52
|
"@rollup/plugin-terser": "^0.4.4",
|
|
47
|
-
"@rollup/plugin-typescript": "^12.
|
|
53
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
48
54
|
"@types/bin-pack": "^1.0.3",
|
|
49
55
|
"@types/brace-expansion": "^1.1.2",
|
|
50
|
-
"@types/inquirer": "^9.0.
|
|
51
|
-
"@types/
|
|
52
|
-
"@types/
|
|
53
|
-
"@
|
|
54
|
-
"@typescript-eslint/
|
|
55
|
-
"@
|
|
56
|
-
"@
|
|
56
|
+
"@types/inquirer": "^9.0.9",
|
|
57
|
+
"@types/node": "^24.10.1",
|
|
58
|
+
"@types/tar-stream": "^3.1.4",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
|
60
|
+
"@typescript-eslint/parser": "^8.47.0",
|
|
61
|
+
"@versatiles/release-tool": "^2.4.5",
|
|
62
|
+
"@vitest/coverage-v8": "^4.0.10",
|
|
57
63
|
"bin-pack": "^1.0.2",
|
|
58
|
-
"esbuild": "^0.
|
|
59
|
-
"eslint": "^9.
|
|
60
|
-
"inquirer": "^
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"rollup-plugin-dts": "^6.1.1",
|
|
66
|
-
"rollup-plugin-sourcemaps2": "^0.5.0",
|
|
67
|
-
"sharp": "^0.33.5",
|
|
64
|
+
"esbuild": "^0.27.0",
|
|
65
|
+
"eslint": "^9.39.1",
|
|
66
|
+
"inquirer": "^13.0.1",
|
|
67
|
+
"rollup": "^4.53.3",
|
|
68
|
+
"rollup-plugin-dts": "^6.2.3",
|
|
69
|
+
"rollup-plugin-sourcemaps2": "^0.5.4",
|
|
70
|
+
"sharp": "^0.34.5",
|
|
68
71
|
"tar-stream": "^3.1.7",
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"typescript-eslint": "^8.26.1"
|
|
72
|
+
"tsx": "^4.20.6",
|
|
73
|
+
"typescript": "^5.9.3",
|
|
74
|
+
"typescript-eslint": "^8.47.0",
|
|
75
|
+
"vitest": "^4.0.10"
|
|
74
76
|
}
|
|
75
77
|
}
|
package/src/color/abstract.ts
CHANGED
|
@@ -1,87 +1,202 @@
|
|
|
1
1
|
import type { HSL } from './hsl.js';
|
|
2
2
|
import type { HSV } from './hsv.js';
|
|
3
|
-
import { RandomColorOptions } from './random.js';
|
|
4
3
|
import type { RGB } from './rgb.js';
|
|
5
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The abstract `Color` class provides a blueprint for color manipulation and conversion.
|
|
7
|
+
* It includes methods for converting between different color models ({@link HSL}, {@link HSV}, {@link RGB}),
|
|
8
|
+
* as well as various color transformations such as inversion, rotation, saturation, and blending.
|
|
9
|
+
*
|
|
10
|
+
* @abstract
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Abstract class representing a color.
|
|
14
|
+
*/
|
|
6
15
|
export abstract class Color {
|
|
16
|
+
/**
|
|
17
|
+
* Parses a color from a string or another Color instance.
|
|
18
|
+
* @param input - The input color as a string or Color instance.
|
|
19
|
+
* @returns The parsed Color instance.
|
|
20
|
+
*/
|
|
7
21
|
static parse: (input: Color | string) => Color;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The HSL color model.
|
|
25
|
+
*/
|
|
8
26
|
static HSL: typeof HSL;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The HSV color model.
|
|
30
|
+
*/
|
|
9
31
|
static HSV: typeof HSV;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The RGB color model.
|
|
35
|
+
*/
|
|
10
36
|
static RGB: typeof RGB;
|
|
11
|
-
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a clone of the current color instance.
|
|
40
|
+
* @returns A new Color instance that is a clone of the current instance.
|
|
41
|
+
*/
|
|
12
42
|
abstract clone(): Color;
|
|
13
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Converts the color to a hexadecimal string.
|
|
46
|
+
* @returns The hexadecimal representation of the color.
|
|
47
|
+
*/
|
|
14
48
|
asHex(): string {
|
|
15
|
-
return this.
|
|
49
|
+
return this.asRGB().asHex();
|
|
16
50
|
}
|
|
17
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Converts the color to a string representation.
|
|
54
|
+
* @returns The string representation of the color.
|
|
55
|
+
*/
|
|
18
56
|
abstract asString(): string;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Rounds the color values.
|
|
60
|
+
* @returns A new Color instance with rounded values.
|
|
61
|
+
*/
|
|
19
62
|
abstract round(): Color;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Converts the color to an array of numbers.
|
|
66
|
+
* @returns An array representing the color.
|
|
67
|
+
*/
|
|
20
68
|
abstract asArray(): number[];
|
|
21
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Converts the color to the HSL color model.
|
|
72
|
+
* @returns The HSL representation of the color.
|
|
73
|
+
*/
|
|
22
74
|
abstract asHSL(): HSL;
|
|
23
|
-
abstract asHSV(): HSV;
|
|
24
|
-
abstract asRGB(): RGB;
|
|
25
|
-
|
|
26
|
-
toHSL(): HSL {
|
|
27
|
-
return this.asHSL();
|
|
28
|
-
}
|
|
29
75
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Converts the color to the HSV color model.
|
|
78
|
+
* @returns The HSV representation of the color.
|
|
79
|
+
*/
|
|
80
|
+
abstract asHSV(): HSV;
|
|
33
81
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Converts the color to the RGB color model.
|
|
84
|
+
* @returns The RGB representation of the color.
|
|
85
|
+
*/
|
|
86
|
+
abstract asRGB(): RGB;
|
|
37
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Inverts the luminosity of the color.
|
|
90
|
+
* @returns A new HSL color with inverted luminosity.
|
|
91
|
+
*/
|
|
38
92
|
invertLuminosity(): HSL {
|
|
39
|
-
return this.
|
|
93
|
+
return this.asHSL().invertLuminosity();
|
|
40
94
|
}
|
|
41
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Rotates the hue of the color by a given offset.
|
|
98
|
+
* @param offset - The amount to rotate the hue.
|
|
99
|
+
* @returns A new HSL color with the hue rotated.
|
|
100
|
+
*/
|
|
42
101
|
rotateHue(offset: number): HSL {
|
|
43
|
-
return this.
|
|
102
|
+
return this.asHSL().rotateHue(offset);
|
|
44
103
|
}
|
|
45
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Saturates the color by a given ratio.
|
|
107
|
+
* @param ratio - The ratio to saturate the color.
|
|
108
|
+
* @returns A new HSL color with increased saturation.
|
|
109
|
+
*/
|
|
46
110
|
saturate(ratio: number): HSL {
|
|
47
|
-
return this.
|
|
111
|
+
return this.asHSL().saturate(ratio);
|
|
48
112
|
}
|
|
49
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Applies gamma correction to the color.
|
|
116
|
+
* @param value - The gamma correction value.
|
|
117
|
+
* @returns A new RGB color with gamma correction applied.
|
|
118
|
+
*/
|
|
50
119
|
gamma(value: number): RGB {
|
|
51
|
-
return this.
|
|
120
|
+
return this.asRGB().gamma(value);
|
|
52
121
|
}
|
|
53
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Inverts the color.
|
|
125
|
+
* @returns A new RGB color with inverted values.
|
|
126
|
+
*/
|
|
54
127
|
invert(): RGB {
|
|
55
|
-
return this.
|
|
128
|
+
return this.asRGB().invert();
|
|
56
129
|
}
|
|
57
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Adjusts the contrast of the color.
|
|
133
|
+
* @param value - The contrast adjustment value.
|
|
134
|
+
* @returns A new RGB color with adjusted contrast.
|
|
135
|
+
*/
|
|
58
136
|
contrast(value: number): RGB {
|
|
59
|
-
return this.
|
|
137
|
+
return this.asRGB().contrast(value);
|
|
60
138
|
}
|
|
61
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Adjusts the brightness of the color.
|
|
142
|
+
* @param value - The brightness adjustment value.
|
|
143
|
+
* @returns A new RGB color with adjusted brightness.
|
|
144
|
+
*/
|
|
62
145
|
brightness(value: number): RGB {
|
|
63
|
-
return this.
|
|
146
|
+
return this.asRGB().brightness(value);
|
|
64
147
|
}
|
|
65
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Lightens the color by a given value.
|
|
151
|
+
* @param value - The amount to lighten the color.
|
|
152
|
+
* @returns A new RGB color that is lightened.
|
|
153
|
+
*/
|
|
66
154
|
lighten(value: number): RGB {
|
|
67
|
-
return this.
|
|
155
|
+
return this.asRGB().lighten(value);
|
|
68
156
|
}
|
|
69
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Darkens the color by a given value.
|
|
160
|
+
* @param value - The amount to darken the color.
|
|
161
|
+
* @returns A new RGB color that is darkened.
|
|
162
|
+
*/
|
|
70
163
|
darken(value: number): RGB {
|
|
71
|
-
return this.
|
|
164
|
+
return this.asRGB().darken(value);
|
|
72
165
|
}
|
|
73
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Tints the color by blending it with another color.
|
|
169
|
+
* @param value - The blend ratio.
|
|
170
|
+
* @param tintColor - The color to blend with.
|
|
171
|
+
* @returns A new RGB color that is tinted.
|
|
172
|
+
*/
|
|
74
173
|
tint(value: number, tintColor: Color): RGB {
|
|
75
|
-
return this.
|
|
174
|
+
return this.asRGB().tint(value, tintColor);
|
|
76
175
|
}
|
|
77
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Blends the color with another color.
|
|
179
|
+
* @param value - The blend ratio.
|
|
180
|
+
* @param blendColor - The color to blend with.
|
|
181
|
+
* @returns A new RGB color that is blended.
|
|
182
|
+
*/
|
|
78
183
|
blend(value: number, blendColor: Color): RGB {
|
|
79
|
-
return this.
|
|
184
|
+
return this.asRGB().blend(value, blendColor);
|
|
80
185
|
}
|
|
81
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Sets the hue of the color.
|
|
189
|
+
* @param value - The new hue value.
|
|
190
|
+
* @returns A new HSV color with the hue set.
|
|
191
|
+
*/
|
|
82
192
|
setHue(value: number): HSV {
|
|
83
|
-
return this.
|
|
193
|
+
return this.asHSV().setHue(value);
|
|
84
194
|
}
|
|
85
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Fades the color by a given value.
|
|
198
|
+
* @param value - The fade value.
|
|
199
|
+
* @returns A new Color instance that is faded.
|
|
200
|
+
*/
|
|
86
201
|
abstract fade(value: number): Color;
|
|
87
202
|
}
|
package/src/color/hsl.test.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach } from 'vitest';
|
|
1
2
|
import { HSL } from './hsl.js';
|
|
2
3
|
import { HSV } from './hsv.js';
|
|
3
4
|
import { RGB } from './rgb.js';
|
|
@@ -6,23 +7,23 @@ describe('HSL Class', () => {
|
|
|
6
7
|
|
|
7
8
|
describe('constructor', () => {
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
it('should initialize small HSL values correctly', () => {
|
|
10
11
|
const color = new HSL(10, 20, 30, 0.4);
|
|
11
12
|
expect(color.asArray()).toStrictEqual([10, 20, 30, 0.4]);
|
|
12
13
|
});
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
it('should initialize big HSL values correctly', () => {
|
|
15
16
|
const color = new HSL(400, 120, 120, 2);
|
|
16
17
|
expect(color.asArray()).toStrictEqual([40, 100, 100, 1]);
|
|
17
18
|
});
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
it('should initialize small HSL values correctly', () => {
|
|
20
21
|
const color = new HSL(-60, -10, -10, -1);
|
|
21
22
|
expect(color.asArray()).toStrictEqual([300, 0, 0, 0]);
|
|
22
23
|
});
|
|
23
24
|
})
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
it('clone should return a new HSL instance with identical values', () => {
|
|
26
27
|
const color = new HSL(120, 50, 50, 0.5);
|
|
27
28
|
const clone = color.clone();
|
|
28
29
|
expect(clone).toBeInstanceOf(HSL);
|
|
@@ -32,7 +33,7 @@ describe('HSL Class', () => {
|
|
|
32
33
|
|
|
33
34
|
describe('conversion', () => {
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
it('asString should return correct HSL and HSLA strings', () => {
|
|
36
37
|
const color1 = new HSL(120, 50, 50);
|
|
37
38
|
expect(color1.asString()).toBe('hsl(120,50%,50%)');
|
|
38
39
|
|
|
@@ -40,13 +41,13 @@ describe('HSL Class', () => {
|
|
|
40
41
|
expect(color2.asString()).toBe('hsla(120,50%,50%,0.5)');
|
|
41
42
|
});
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
it('asHSL and toHSL should return the same instance', () => {
|
|
44
45
|
const color = new HSL(120, 50, 50);
|
|
45
46
|
expect(color.asHSL()).toStrictEqual(color);
|
|
46
47
|
expect(color.toHSL()).toStrictEqual(color);
|
|
47
48
|
});
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
it('asHSV should correctly convert HSL to HSV', () => {
|
|
50
51
|
function check(input: [number, number, number], output: [number, number, number]) {
|
|
51
52
|
const hsl = new HSL(...input);
|
|
52
53
|
const hsv = hsl.asHSV();
|
|
@@ -66,7 +67,7 @@ describe('HSL Class', () => {
|
|
|
66
67
|
check([18, 100, 100], [18, 0, 100]);
|
|
67
68
|
});
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
it('asRGB should correctly convert HSL to RGB', () => {
|
|
70
71
|
function check(input: [number, number, number], output: [number, number, number]) {
|
|
71
72
|
const hsl = new HSL(...input)
|
|
72
73
|
const rgb = hsl.asRGB();
|
|
@@ -90,7 +91,7 @@ describe('HSL Class', () => {
|
|
|
90
91
|
|
|
91
92
|
describe('should parse valid HSL and HSLA strings', () => {
|
|
92
93
|
function check(str: string, result: number[]) {
|
|
93
|
-
|
|
94
|
+
it(`parse "${str}"`, () => {
|
|
94
95
|
const color = HSL.parse(str);
|
|
95
96
|
expect(color).toBeInstanceOf(HSL);
|
|
96
97
|
expect(color.asArray()).toStrictEqual(result);
|
|
@@ -101,7 +102,7 @@ describe('HSL Class', () => {
|
|
|
101
102
|
check('hsla(240,100%,50%,0.75)', [240, 100, 50, 0.75]);
|
|
102
103
|
check('hsl(400,50%,50%)', [40, 50, 50, 1]);
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
it('parse should throw an error for invalid strings', () => {
|
|
105
106
|
expect(() => HSL.parse('invalid')).toThrow('Invalid HSL color string');
|
|
106
107
|
});
|
|
107
108
|
});
|
|
@@ -110,11 +111,11 @@ describe('HSL Class', () => {
|
|
|
110
111
|
let color: HSL;
|
|
111
112
|
beforeEach(() => color = new HSL(120, 50, 50, 0.8));
|
|
112
113
|
|
|
113
|
-
|
|
114
|
+
it('inverts luminosity correctly', () => {
|
|
114
115
|
expect(color.invertLuminosity().asArray()).toStrictEqual([120, 50, 50, 0.8]); // Luminosity inverted to 50%
|
|
115
116
|
});
|
|
116
117
|
|
|
117
|
-
|
|
118
|
+
it('handles edge cases for luminosity inversion', () => {
|
|
118
119
|
const black = new HSL(0, 0, 0, 1);
|
|
119
120
|
expect(black.invertLuminosity().asArray()).toStrictEqual([0, 0, 100, 1]); // Black becomes white
|
|
120
121
|
|
|
@@ -127,15 +128,15 @@ describe('HSL Class', () => {
|
|
|
127
128
|
let color: HSL;
|
|
128
129
|
beforeEach(() => color = new HSL(120, 50, 50, 0.8));
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
it('rotates hue correctly within the range of 0-360', () => {
|
|
131
132
|
expect(color.rotateHue(180).asArray()).toStrictEqual([300, 50, 50, 0.8]); // Hue rotated by 180 degrees
|
|
132
133
|
});
|
|
133
134
|
|
|
134
|
-
|
|
135
|
+
it('handles negative rotation correctly', () => {
|
|
135
136
|
expect(color.rotateHue(-270).asArray()).toStrictEqual([210, 50, 50, 0.8]); // Hue rotated negatively
|
|
136
137
|
});
|
|
137
138
|
|
|
138
|
-
|
|
139
|
+
it('handles rotations that exceed 360 degrees', () => {
|
|
139
140
|
expect(color.rotateHue(540).asArray()).toStrictEqual([300, 50, 50, 0.8]); // Hue wrapped around to 300
|
|
140
141
|
});
|
|
141
142
|
});
|
|
@@ -147,16 +148,16 @@ describe('HSL Class', () => {
|
|
|
147
148
|
grey = new HSL(120, 0, 50, 0.8);
|
|
148
149
|
});
|
|
149
150
|
|
|
150
|
-
|
|
151
|
+
it('increases saturation correctly', () => {
|
|
151
152
|
expect(color.saturate(0.5).asArray()).toStrictEqual([120, 75, 50, 0.8]);
|
|
152
153
|
expect(grey.saturate(0.5).asArray()).toStrictEqual([120, 0, 50, 0.8]);
|
|
153
154
|
});
|
|
154
155
|
|
|
155
|
-
|
|
156
|
+
it('decreases saturation correctly', () => {
|
|
156
157
|
expect(color.saturate(-0.5).asArray()).toStrictEqual([120, 25, 50, 0.8]); // Saturation decreased by 50%
|
|
157
158
|
});
|
|
158
159
|
|
|
159
|
-
|
|
160
|
+
it('clamps saturation to the valid range', () => {
|
|
160
161
|
expect(color.saturate(1.5).asArray()).toStrictEqual([120, 100, 50, 0.8]); // Saturation clamped to 100%
|
|
161
162
|
|
|
162
163
|
expect(color.saturate(-2).asArray()).toStrictEqual([120, 0, 50, 0.8]); // Saturation clamped to 0%
|
|
@@ -167,11 +168,11 @@ describe('HSL Class', () => {
|
|
|
167
168
|
let color: HSL;
|
|
168
169
|
beforeEach(() => color = new HSL(120, 50, 50, 0.8));
|
|
169
170
|
|
|
170
|
-
|
|
171
|
+
it('reduces alpha correctly', () => {
|
|
171
172
|
expect(color.fade(0.5).asArray()).toStrictEqual([120, 50, 50, 0.4]); // Alpha reduced by 50%
|
|
172
173
|
});
|
|
173
174
|
|
|
174
|
-
|
|
175
|
+
it('handles edge cases for fading', () => {
|
|
175
176
|
const opaque = new HSL(0, 50, 50, 1);
|
|
176
177
|
expect(opaque.fade(1).asArray()).toStrictEqual([0, 50, 50, 0]); // Fully faded to transparent
|
|
177
178
|
|
package/src/color/hsl.ts
CHANGED
|
@@ -3,12 +3,38 @@ import { HSV } from './hsv.js';
|
|
|
3
3
|
import { RGB } from './rgb.js';
|
|
4
4
|
import { clamp, formatFloat, mod } from './utils.js';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Represents a color in the HSL (Hue, Saturation, Lightness) color space.
|
|
8
|
+
* Extends the base `Color` class.
|
|
9
|
+
*/
|
|
6
10
|
export class HSL extends Color {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
readonly
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The hue component of the color, in the range [0, 360].
|
|
13
|
+
*/
|
|
14
|
+
readonly h: number;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The saturation component of the color, in the range [0, 100].
|
|
18
|
+
*/
|
|
19
|
+
readonly s: number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The lightness component of the color, in the range [0, 100].
|
|
23
|
+
*/
|
|
24
|
+
readonly l: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The alpha (opacity) component of the color, in the range [0, 1].
|
|
28
|
+
*/
|
|
29
|
+
readonly a: number;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new HSL color.
|
|
33
|
+
* @param h - The hue component, in the range [0, 360].
|
|
34
|
+
* @param s - The saturation component, in the range [0, 100].
|
|
35
|
+
* @param l - The lightness component, in the range [0, 100].
|
|
36
|
+
* @param a - The alpha (opacity) component, in the range [0, 1]. Defaults to 1.
|
|
37
|
+
*/
|
|
12
38
|
constructor(h: number, s: number, l: number, a: number = 1) {
|
|
13
39
|
super();
|
|
14
40
|
this.h = mod(h, 360);
|
|
@@ -17,10 +43,18 @@ export class HSL extends Color {
|
|
|
17
43
|
this.a = clamp(a, 0, 1);
|
|
18
44
|
}
|
|
19
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Returns the HSL color as an array of numbers.
|
|
48
|
+
* @returns An array containing the hue, saturation, lightness, and alpha components.
|
|
49
|
+
*/
|
|
20
50
|
asArray(): [number, number, number, number] {
|
|
21
51
|
return [this.h, this.s, this.l, this.a];
|
|
22
52
|
}
|
|
23
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Returns a new HSL color with rounded components.
|
|
56
|
+
* @returns A new HSL color with rounded hue, saturation, lightness, and alpha components.
|
|
57
|
+
*/
|
|
24
58
|
round(): HSL {
|
|
25
59
|
return new HSL(
|
|
26
60
|
Math.round(this.h),
|
|
@@ -30,10 +64,18 @@ export class HSL extends Color {
|
|
|
30
64
|
);
|
|
31
65
|
}
|
|
32
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Creates a copy of the current HSL color.
|
|
69
|
+
* @returns A new HSL color with the same components as the current color.
|
|
70
|
+
*/
|
|
33
71
|
clone(): HSL {
|
|
34
72
|
return new HSL(this.h, this.s, this.l, this.a);
|
|
35
73
|
}
|
|
36
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Returns the HSL color as a CSS-compatible string.
|
|
77
|
+
* @returns A string representing the HSL color in CSS format.
|
|
78
|
+
*/
|
|
37
79
|
asString(): string {
|
|
38
80
|
if (this.a === 1) {
|
|
39
81
|
return `hsl(${this.h.toFixed(0)},${this.s.toFixed(0)}%,${this.l.toFixed(0)}%)`;
|
|
@@ -42,14 +84,26 @@ export class HSL extends Color {
|
|
|
42
84
|
}
|
|
43
85
|
}
|
|
44
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Returns the current HSL color.
|
|
89
|
+
* @returns The current HSL color.
|
|
90
|
+
*/
|
|
45
91
|
asHSL(): HSL {
|
|
46
92
|
return this.clone();
|
|
47
93
|
}
|
|
48
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Returns the current HSL color.
|
|
97
|
+
* @returns The current HSL color.
|
|
98
|
+
*/
|
|
49
99
|
toHSL(): HSL {
|
|
50
100
|
return this;
|
|
51
101
|
}
|
|
52
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Converts the HSL color to an HSV color.
|
|
105
|
+
* @returns A new HSV color representing the same color.
|
|
106
|
+
*/
|
|
53
107
|
asHSV(): HSV {
|
|
54
108
|
const s = this.s / 100, l = this.l / 100;
|
|
55
109
|
const v = l + s * Math.min(l, 1 - l);
|
|
@@ -57,6 +111,10 @@ export class HSL extends Color {
|
|
|
57
111
|
return new HSV(this.h, sv * 100, v * 100, this.a);
|
|
58
112
|
}
|
|
59
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Converts the HSL color to an RGB color.
|
|
116
|
+
* @returns A new RGB color representing the same color.
|
|
117
|
+
*/
|
|
60
118
|
asRGB(): RGB {
|
|
61
119
|
const h = this.h / 360;
|
|
62
120
|
const s = this.s / 100;
|
|
@@ -86,6 +144,12 @@ export class HSL extends Color {
|
|
|
86
144
|
);
|
|
87
145
|
}
|
|
88
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Parses a string or Color object into an HSL color.
|
|
149
|
+
* @param input - The input string or Color object to parse.
|
|
150
|
+
* @returns A new HSL color parsed from the input.
|
|
151
|
+
* @throws Will throw an error if the input string is not a valid HSL color string.
|
|
152
|
+
*/
|
|
89
153
|
static parse(input: string | Color): HSL {
|
|
90
154
|
if (input instanceof Color) return input.asHSL();
|
|
91
155
|
|
|
@@ -104,18 +168,37 @@ export class HSL extends Color {
|
|
|
104
168
|
throw new Error(`Invalid HSL color string: "${input}"`);
|
|
105
169
|
}
|
|
106
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Inverts the lightness component of the HSL color.
|
|
173
|
+
* @returns A new HSL color with the lightness component inverted.
|
|
174
|
+
*/
|
|
107
175
|
invertLuminosity(): HSL {
|
|
108
176
|
return new HSL(this.h, this.s, 100 - this.l, this.a);
|
|
109
177
|
}
|
|
110
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Rotates the hue component of the HSL color by a given offset.
|
|
181
|
+
* @param offset - The amount to rotate the hue by, in degrees.
|
|
182
|
+
* @returns A new HSL color with the hue rotated by the given offset.
|
|
183
|
+
*/
|
|
111
184
|
rotateHue(offset: number): HSL {
|
|
112
185
|
return new HSL(mod(this.h + offset, 360), this.s, this.l, this.a);
|
|
113
186
|
}
|
|
114
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Increases the saturation of the HSL color by a given ratio.
|
|
190
|
+
* @param ratio - The ratio by which to increase the saturation.
|
|
191
|
+
* @returns A new HSL color with increased saturation.
|
|
192
|
+
*/
|
|
115
193
|
saturate(ratio: number): HSL {
|
|
116
194
|
return new HSL(this.h, clamp(this.s * (1 + ratio), 0, 100), this.l, this.a);
|
|
117
195
|
}
|
|
118
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Decreases the alpha (opacity) of the HSL color by a given value.
|
|
199
|
+
* @param value - The value by which to decrease the alpha.
|
|
200
|
+
* @returns A new HSL color with decreased alpha.
|
|
201
|
+
*/
|
|
119
202
|
fade(value: number): HSL {
|
|
120
203
|
return new HSL(this.h, this.s, this.l, this.a * (1 - value));
|
|
121
204
|
}
|