@versatiles/style 5.7.0 → 5.8.1
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 +15 -14
- package/dist/index.d.ts +12 -3
- package/dist/index.js +141 -137
- package/dist/index.js.map +1 -1
- package/package.json +33 -32
- package/src/color/hsl.test.ts +21 -20
- package/src/color/hsv.test.ts +19 -18
- package/src/color/hsv.ts +5 -0
- package/src/color/index.test.ts +12 -10
- package/src/color/random.test.ts +7 -6
- package/src/color/rgb.test.ts +25 -24
- package/src/color/utils.test.ts +16 -15
- package/src/guess_style/guess_style.test.ts +1 -1
- package/src/index.test.ts +42 -16
- package/src/index.ts +4 -4
- package/src/lib/utils.test.ts +1 -0
- package/src/lib/utils.ts +3 -6
- package/src/shortbread/layers.test.ts +31 -2
- package/src/shortbread/layers.ts +5 -2
- 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/style_builder.test.ts +1 -1
- package/src/style_builder/style_builder.ts +1 -2
- package/src/style_builder/types.ts +2 -2
- package/src/types/tilejson.test.ts +1 -2
- package/src/types/vector_layer.test.ts +1 -0
package/src/color/rgb.test.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
1
2
|
import { RGB } from './rgb.js';
|
|
2
3
|
import { HSL } from './hsl.js';
|
|
3
4
|
import { HSV } from './hsv.js';
|
|
4
5
|
|
|
5
6
|
describe('RGB Class', () => {
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
it('constructor initializes values correctly with clamping', () => {
|
|
8
9
|
const color = new RGB(300, -50, 500, 2);
|
|
9
10
|
expect(color.asArray()).toStrictEqual([255, 0, 255, 1]);
|
|
10
11
|
|
|
@@ -12,12 +13,12 @@ describe('RGB Class', () => {
|
|
|
12
13
|
expect(colorNegative.asArray()).toStrictEqual([0, 0, 0, 0]);
|
|
13
14
|
});
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
it('asArray returns the correct array representation', () => {
|
|
16
17
|
const color = new RGB(128, 64, 255, 0.5);
|
|
17
18
|
expect(color.asArray()).toStrictEqual([128, 64, 255, 0.5]);
|
|
18
19
|
});
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
it('clone creates a new instance with identical values', () => {
|
|
21
22
|
const color = new RGB(128, 255, 64, 0.8);
|
|
22
23
|
const clone = color.clone();
|
|
23
24
|
expect(clone).toBeInstanceOf(RGB);
|
|
@@ -25,7 +26,7 @@ describe('RGB Class', () => {
|
|
|
25
26
|
expect(clone).not.toBe(color);
|
|
26
27
|
});
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
it('asString returns correct RGB/RGBA string', () => {
|
|
29
30
|
const color1 = new RGB(255, 128, 64);
|
|
30
31
|
expect(color1.asString()).toBe('rgb(255,128,64)');
|
|
31
32
|
|
|
@@ -33,7 +34,7 @@ describe('RGB Class', () => {
|
|
|
33
34
|
expect(color2.asString()).toBe('rgba(255,128,64,0.5)');
|
|
34
35
|
});
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
it('asHex returns correct hexadecimal representation', () => {
|
|
37
38
|
const color1 = new RGB(255, 128, 64);
|
|
38
39
|
expect(color1.asHex()).toBe('#FF8040');
|
|
39
40
|
|
|
@@ -42,7 +43,7 @@ describe('RGB Class', () => {
|
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
describe('color conversion', () => {
|
|
45
|
-
|
|
46
|
+
it('asHSL converts RGB to HSL correctly', () => {
|
|
46
47
|
const hsl = new RGB(255, 0, 0).asHSL();
|
|
47
48
|
expect(hsl).toBeInstanceOf(HSL);
|
|
48
49
|
expect(hsl.asArray().map(value => Math.round(value)))
|
|
@@ -64,7 +65,7 @@ describe('RGB Class', () => {
|
|
|
64
65
|
expect(RGB.parse('#FF0080').asHSL().round().asArray()).toStrictEqual([330, 100, 50, 1]);
|
|
65
66
|
});
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
it('asHSV converts RGB to HSV correctly', () => {
|
|
68
69
|
const hsv = new RGB(255, 0, 0).asHSV();
|
|
69
70
|
expect(hsv).toBeInstanceOf(HSV);
|
|
70
71
|
expect(hsv.asArray().map(value => Math.round(value)))
|
|
@@ -86,19 +87,19 @@ describe('RGB Class', () => {
|
|
|
86
87
|
expect(RGB.parse('#FF0080').asHSV().round().asArray()).toStrictEqual([330, 100, 100, 1]);
|
|
87
88
|
});
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
it('asRGB and toRGB return the same instance or clone', () => {
|
|
90
91
|
const color = new RGB(255, 128, 64, 0.5);
|
|
91
92
|
expect(color.asRGB()).toStrictEqual(color);
|
|
92
93
|
expect(color.toRGB()).toStrictEqual(color);
|
|
93
94
|
});
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
it('handles black correctly', () => {
|
|
96
97
|
const color = new RGB(0, 0, 0);
|
|
97
98
|
expect(color.asHSL().asArray().map(value => Math.round(value))).toStrictEqual([0, 0, 0, 1]);
|
|
98
99
|
expect(color.asHSV().asArray().map(value => Math.round(value))).toStrictEqual([0, 0, 0, 1]);
|
|
99
100
|
});
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
it('handles white correctly', () => {
|
|
102
103
|
const color = new RGB(255, 255, 255);
|
|
103
104
|
expect(color.asHSL().asArray().map(value => Math.round(value))).toStrictEqual([0, 0, 100, 1]);
|
|
104
105
|
expect(color.asHSV().asArray().map(value => Math.round(value))).toStrictEqual([0, 0, 100, 1]);
|
|
@@ -106,25 +107,25 @@ describe('RGB Class', () => {
|
|
|
106
107
|
});
|
|
107
108
|
|
|
108
109
|
describe('parse', () => {
|
|
109
|
-
|
|
110
|
+
it('parses hexadecimal color strings correctly', () => {
|
|
110
111
|
expect(RGB.parse('#ff8040').asArray()).toStrictEqual([255, 128, 64, 1]);
|
|
111
112
|
expect(RGB.parse('#ff804066').asArray()).toStrictEqual([255, 128, 64, 0.4]);
|
|
112
113
|
});
|
|
113
114
|
|
|
114
|
-
|
|
115
|
+
it('parses shorthand hexadecimal strings correctly', () => {
|
|
115
116
|
expect(RGB.parse('#fa4').asArray()).toStrictEqual([255, 170, 68, 1]);
|
|
116
117
|
expect(RGB.parse('#fa43').asArray()).toStrictEqual([255, 170, 68, 0.2]);
|
|
117
118
|
});
|
|
118
119
|
|
|
119
|
-
|
|
120
|
+
it('parses RGB strings correctly', () => {
|
|
120
121
|
expect(RGB.parse('rgb(255, 128, 64)').asArray()).toStrictEqual([255, 128, 64, 1]);
|
|
121
122
|
});
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
it('parses RGBA strings correctly', () => {
|
|
124
125
|
expect(RGB.parse('rgba(255, 128, 64, 0.5)').asArray()).toStrictEqual([255, 128, 64, 0.5]);
|
|
125
126
|
});
|
|
126
127
|
|
|
127
|
-
|
|
128
|
+
it('throws an error for invalid strings', () => {
|
|
128
129
|
expect(() => RGB.parse('invalid')).toThrow('Invalid RGB color string: "invalid"');
|
|
129
130
|
});
|
|
130
131
|
});
|
|
@@ -134,37 +135,37 @@ describe('RGB Class', () => {
|
|
|
134
135
|
return cb(new RGB(50, 150, 200, 0.8)).round().asArray();
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
|
|
138
|
+
it('adjusts gamma correctly', () => {
|
|
138
139
|
expect(pc(c => c.gamma(2.2))).toStrictEqual([7, 79, 149, 0.8]);
|
|
139
140
|
expect(pc(c => c.gamma(0.001))).toStrictEqual([255, 255, 255, 0.8]);
|
|
140
141
|
expect(pc(c => c.gamma(1000))).toStrictEqual([0, 0, 0, 0.8]);
|
|
141
142
|
});
|
|
142
143
|
|
|
143
|
-
|
|
144
|
+
it('inverts RGB values correctly', () => {
|
|
144
145
|
expect(pc(c => c.invert())).toStrictEqual([205, 105, 55, 0.8]);
|
|
145
146
|
});
|
|
146
147
|
|
|
147
|
-
|
|
148
|
+
it('adjusts contrast correctly', () => {
|
|
148
149
|
expect(pc(c => c.contrast(1.5))).toStrictEqual([11, 161, 236, 0.8]);
|
|
149
150
|
expect(pc(c => c.contrast(1e6))).toStrictEqual([0, 255, 255, 0.8]);
|
|
150
151
|
expect(pc(c => c.contrast(0))).toStrictEqual([128, 128, 128, 0.8]);
|
|
151
152
|
});
|
|
152
153
|
|
|
153
|
-
|
|
154
|
+
it('increases brightness correctly', () => {
|
|
154
155
|
expect(pc(c => c.brightness(0.5))).toStrictEqual([153, 203, 228, 0.8]);
|
|
155
156
|
expect(pc(c => c.brightness(-0.5))).toStrictEqual([25, 75, 100, 0.8]);
|
|
156
157
|
expect(pc(c => c.brightness(2))).toStrictEqual([255, 255, 255, 0.8]);
|
|
157
158
|
expect(pc(c => c.brightness(-2))).toStrictEqual([0, 0, 0, 0.8]);
|
|
158
159
|
});
|
|
159
160
|
|
|
160
|
-
|
|
161
|
+
it('tints color correctly', () => {
|
|
161
162
|
const tintColor = new RGB(255, 0, 0);
|
|
162
163
|
expect(pc(c => c.tint(0.5, tintColor))).toStrictEqual([125, 100, 125, 0.8]);
|
|
163
164
|
expect(pc(c => c.tint(1, tintColor))).toStrictEqual([200, 50, 50, 0.8]);
|
|
164
165
|
expect(pc(c => c.tint(0, tintColor))).toStrictEqual([50, 150, 200, 0.8]);
|
|
165
166
|
});
|
|
166
167
|
|
|
167
|
-
|
|
168
|
+
it('blends color correctly', () => {
|
|
168
169
|
const blendColor = new RGB(255, 0, 0);
|
|
169
170
|
expect(pc(c => c.blend(0.2, blendColor))).toStrictEqual([91, 120, 160, 0.8]);
|
|
170
171
|
expect(pc(c => c.blend(0.5, blendColor))).toStrictEqual([153, 75, 100, 0.8]);
|
|
@@ -173,18 +174,18 @@ describe('RGB Class', () => {
|
|
|
173
174
|
expect(pc(c => c.blend(0, blendColor))).toStrictEqual([50, 150, 200, 0.8]);
|
|
174
175
|
});
|
|
175
176
|
|
|
176
|
-
|
|
177
|
+
it('lightens the color correctly', () => {
|
|
177
178
|
expect(pc(c => c.lighten(0.5))).toStrictEqual([153, 203, 228, 0.8]);
|
|
178
179
|
expect(pc(c => c.lighten(2))).toStrictEqual([255, 255, 255, 0.8]);
|
|
179
180
|
});
|
|
180
181
|
|
|
181
|
-
|
|
182
|
+
it('darkens the color correctly', () => {
|
|
182
183
|
expect(pc(c => c.darken(0.5))).toStrictEqual([25, 75, 100, 0.8]);
|
|
183
184
|
expect(pc(c => c.darken(1))).toStrictEqual([0, 0, 0, 0.8]);
|
|
184
185
|
expect(pc(c => c.darken(2))).toStrictEqual([0, 0, 0, 0.8]);
|
|
185
186
|
});
|
|
186
187
|
|
|
187
|
-
|
|
188
|
+
it('fades color correctly', () => {
|
|
188
189
|
expect(pc(c => c.fade(0.5))).toStrictEqual([50, 150, 200, 0.4]);
|
|
189
190
|
expect(pc(c => c.fade(1))).toStrictEqual([50, 150, 200, 0]);
|
|
190
191
|
|
package/src/color/utils.test.ts
CHANGED
|
@@ -1,83 +1,84 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
1
2
|
import { clamp, formatFloat, mod } from './utils.js';
|
|
2
3
|
|
|
3
4
|
describe('clamp function', () => {
|
|
4
|
-
|
|
5
|
+
it('returns the number itself if within the range', () => {
|
|
5
6
|
expect(clamp(5, 0, 10)).toBe(5);
|
|
6
7
|
expect(clamp(0, -10, 10)).toBe(0);
|
|
7
8
|
expect(clamp(-5, -10, 0)).toBe(-5);
|
|
8
9
|
});
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
it('returns the minimum value if number is below range', () => {
|
|
11
12
|
expect(clamp(-5, 0, 10)).toBe(0);
|
|
12
13
|
expect(clamp(-15, -10, 10)).toBe(-10);
|
|
13
14
|
});
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
it('returns the maximum value if number is above range', () => {
|
|
16
17
|
expect(clamp(15, 0, 10)).toBe(10);
|
|
17
18
|
expect(clamp(20, -10, 10)).toBe(10);
|
|
18
19
|
});
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
it('handles edge cases with boundaries', () => {
|
|
21
22
|
expect(clamp(0, 0, 10)).toBe(0);
|
|
22
23
|
expect(clamp(10, 0, 10)).toBe(10);
|
|
23
24
|
});
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
describe('mod function', () => {
|
|
27
|
-
|
|
28
|
+
it('returns the correct modulus for positive numbers', () => {
|
|
28
29
|
expect(mod(10, 3)).toBe(1);
|
|
29
30
|
expect(mod(15, 4)).toBe(3);
|
|
30
31
|
});
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
it('handles negative numbers correctly', () => {
|
|
33
34
|
expect(mod(-10, 3)).toBe(2);
|
|
34
35
|
expect(mod(-15, 4)).toBe(1);
|
|
35
36
|
});
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
it('returns 0 for numbers that are exact multiples of max', () => {
|
|
38
39
|
expect(mod(9, 3)).toBe(0);
|
|
39
40
|
expect(mod(20, 5)).toBe(0);
|
|
40
41
|
});
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
it('returns 0 for zero input with positive max', () => {
|
|
43
44
|
expect(mod(0, 5)).toBe(0);
|
|
44
45
|
});
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
it('handles edge cases with 1 as max', () => {
|
|
47
48
|
expect(mod(10, 1)).toBe(0);
|
|
48
49
|
expect(mod(-10, 1)).toBe(0);
|
|
49
50
|
});
|
|
50
51
|
});
|
|
51
52
|
describe('formatFloat function', () => {
|
|
52
|
-
|
|
53
|
+
it('formats numbers with specified precision correctly', () => {
|
|
53
54
|
expect(formatFloat(123.4567, 2)).toBe('123.46');
|
|
54
55
|
expect(formatFloat(123.4, 2)).toBe('123.4');
|
|
55
56
|
expect(formatFloat(123.0, 2)).toBe('123');
|
|
56
57
|
});
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
it('handles trailing zeros correctly', () => {
|
|
59
60
|
expect(formatFloat(0.000123, 6)).toBe('0.000123');
|
|
60
61
|
expect(formatFloat(0.000100, 6)).toBe('0.0001');
|
|
61
62
|
expect(formatFloat(10.0, 1)).toBe('10');
|
|
62
63
|
});
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
it('handles edge cases with small numbers and high precision', () => {
|
|
65
66
|
expect(formatFloat(0.0000001234, 10)).toBe('0.0000001234');
|
|
66
67
|
expect(formatFloat(0.0000001234, 5)).toBe('0');
|
|
67
68
|
});
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
it('handles large numbers correctly', () => {
|
|
70
71
|
expect(formatFloat(123456789.123456, 3)).toBe('123456789.123');
|
|
71
72
|
expect(formatFloat(123456789.0, 5)).toBe('123456789');
|
|
72
73
|
});
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
it('handles zero and negative numbers correctly', () => {
|
|
75
76
|
expect(formatFloat(0, 2)).toBe('0');
|
|
76
77
|
expect(formatFloat(-123.456, 1)).toBe('-123.5');
|
|
77
78
|
expect(formatFloat(-0.000123, 6)).toBe('-0.000123');
|
|
78
79
|
});
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
it('handles no unnecessary decimal points', () => {
|
|
81
82
|
expect(formatFloat(10.000, 3)).toBe('10');
|
|
82
83
|
expect(formatFloat(10.010, 3)).toBe('10.01');
|
|
83
84
|
});
|
package/src/index.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import { VectorSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
|
|
3
3
|
import type { VectorLayer } from './index.js';
|
|
4
|
-
import
|
|
4
|
+
import * as lib from './index.js';
|
|
5
5
|
|
|
6
|
-
const { colorful, eclipse, graybeard, neutrino, shadow } = styles;
|
|
6
|
+
const { colorful, eclipse, graybeard, neutrino, shadow } = lib.styles;
|
|
7
7
|
|
|
8
8
|
describe('styles', () => {
|
|
9
9
|
[
|
|
@@ -33,20 +33,22 @@ describe('styles', () => {
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
describe('Colorful', () => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
it('should allow custom colors and baseUrl', () => {
|
|
37
|
+
const style = colorful({
|
|
38
|
+
baseUrl: 'https://dev.null',
|
|
39
|
+
colors: { commercial: '#f00' },
|
|
40
|
+
});
|
|
41
|
+
expect(style.glyphs).toBe('https://dev.null/assets/glyphs/{fontstack}/{range}.pbf');
|
|
42
|
+
const paint = style.layers.find(l => l.id === 'land-commercial')?.paint;
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
expect(paint).toBeDefined();
|
|
45
|
+
if (paint == null) throw Error();
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
expect(paint).toHaveProperty('fill-color');
|
|
48
|
+
if (!('fill-color' in paint)) throw Error();
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
expect(paint['fill-color']).toBe('rgb(255,0,0)');
|
|
51
|
+
});
|
|
50
52
|
});
|
|
51
53
|
|
|
52
54
|
describe('guessStyle', () => {
|
|
@@ -54,7 +56,7 @@ describe('guessStyle', () => {
|
|
|
54
56
|
const vector_layers: VectorLayer[] = [{ id: 'hallo', fields: { label: 'String' } }];
|
|
55
57
|
|
|
56
58
|
it('should build raster styles', () => {
|
|
57
|
-
const style = guessStyle({ tiles });
|
|
59
|
+
const style = lib.guessStyle({ tiles });
|
|
58
60
|
expect(style).toStrictEqual({
|
|
59
61
|
layers: [{ id: 'raster', source: 'rasterSource', type: 'raster' }],
|
|
60
62
|
sources: { rasterSource: { tiles, type: 'raster' } },
|
|
@@ -63,7 +65,7 @@ describe('guessStyle', () => {
|
|
|
63
65
|
});
|
|
64
66
|
|
|
65
67
|
it('should build vector styles', () => {
|
|
66
|
-
const style = guessStyle({ tiles, vector_layers });
|
|
68
|
+
const style = lib.guessStyle({ tiles, vector_layers });
|
|
67
69
|
expect(style).toStrictEqual({
|
|
68
70
|
layers: [
|
|
69
71
|
{ id: 'background', paint: { 'background-color': '#fff' }, type: 'background' },
|
|
@@ -76,3 +78,27 @@ describe('guessStyle', () => {
|
|
|
76
78
|
});
|
|
77
79
|
});
|
|
78
80
|
});
|
|
81
|
+
|
|
82
|
+
describe('exports', () => {
|
|
83
|
+
it('should export styles', () => {
|
|
84
|
+
type something = Record<string, unknown>;
|
|
85
|
+
expect(typeof lib.styles).toBe('object');
|
|
86
|
+
const styleNames = ['colorful', 'eclipse', 'graybeard', 'neutrino', 'shadow'];
|
|
87
|
+
for (const name of styleNames) {
|
|
88
|
+
expect(typeof (lib as something)[name]).toBe('function');
|
|
89
|
+
expect(typeof (lib.styles as something)[name]).toBe('function');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should export guessStyle', () => {
|
|
94
|
+
expect(typeof lib.guessStyle).toBe('function');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should export Color', () => {
|
|
98
|
+
expect(typeof lib.Color).toBe('function');
|
|
99
|
+
expect(typeof lib.Color.HSL).toBe('function');
|
|
100
|
+
expect(typeof lib.Color.HSV).toBe('function');
|
|
101
|
+
expect(typeof lib.Color.HSV.randomColor).toBe('function');
|
|
102
|
+
expect(typeof lib.Color.RGB).toBe('function');
|
|
103
|
+
});
|
|
104
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* <body>
|
|
11
11
|
* <!-- ... -->
|
|
12
12
|
* <script>
|
|
13
|
-
* const style =
|
|
13
|
+
* const style = VersaTilesStyle.colorful();
|
|
14
14
|
* // ...
|
|
15
15
|
* </script>
|
|
16
16
|
* </body>
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* npm i @versatiles/style
|
|
23
23
|
* ```
|
|
24
24
|
* ```
|
|
25
|
-
* import { colorful } from 'versatiles
|
|
26
|
-
* // OR: const { colorful } = require('versatiles
|
|
25
|
+
* import { colorful } from '@versatiles/style';
|
|
26
|
+
* // OR: const { colorful } = require('@versatiles/style');
|
|
27
27
|
* const style = colorful();
|
|
28
28
|
* ```
|
|
29
29
|
*
|
|
@@ -91,7 +91,7 @@ export const styles = {
|
|
|
91
91
|
};
|
|
92
92
|
|
|
93
93
|
export type { GuessStyleOptions } from './guess_style/index.js';
|
|
94
|
-
export type { RGB, HSL, HSV } from './color/index.js';
|
|
94
|
+
export type { RGB, HSL, HSV, RandomColorOptions } from './color/index.js';
|
|
95
95
|
export type { TileJSONSpecification, TileJSONSpecificationRaster, TileJSONSpecificationVector } from './types/tilejson.js';
|
|
96
96
|
export type { VectorLayer } from './types/index.js';
|
|
97
97
|
export type { StyleBuilderOptions, Language, StyleBuilderColors, StyleBuilderColorKey, StyleBuilderFonts } from './style_builder/types.js';
|
package/src/lib/utils.test.ts
CHANGED
package/src/lib/utils.ts
CHANGED
|
@@ -15,13 +15,11 @@ export function deepClone<T>(obj: T): T {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
if (isSimpleObject(obj)) {
|
|
18
|
-
|
|
19
|
-
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, deepClone(value)]));
|
|
18
|
+
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, deepClone(value)])) as T;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
if (obj instanceof Array) {
|
|
23
|
-
|
|
24
|
-
return obj.map((e: unknown) => deepClone(e));
|
|
22
|
+
return obj.map((e: unknown) => deepClone(e)) as T;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
if (obj instanceof Color) {
|
|
@@ -87,8 +85,7 @@ export function deepMerge<T extends object>(source0: T, ...sources: Partial<T>[]
|
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
if (sourceValue instanceof Color) {
|
|
90
|
-
|
|
91
|
-
target[key] = sourceValue.clone();
|
|
88
|
+
target[key] = sourceValue.clone() as T[typeof key];
|
|
92
89
|
continue;
|
|
93
90
|
}
|
|
94
91
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
1
2
|
import type { FillLayerSpecification, SymbolLayerSpecification } from '@maplibre/maplibre-gl-style-spec';
|
|
2
3
|
import { getShortbreadLayers } from './layers.js';
|
|
3
4
|
import { Language } from '../style_builder/types.js';
|
|
@@ -15,14 +16,42 @@ describe('layers', () => {
|
|
|
15
16
|
});
|
|
16
17
|
});
|
|
17
18
|
|
|
18
|
-
it('should handle language suffix correctly', () => {
|
|
19
|
+
it('should handle no language suffix correctly', () => {
|
|
20
|
+
const language: Language = '';
|
|
21
|
+
const layers = getShortbreadLayers({ language });
|
|
22
|
+
const labelLayer = layers.find((layer) => layer.id === 'label-street-pedestrian') as SymbolLayerSpecification;
|
|
23
|
+
|
|
24
|
+
expect(labelLayer).toBeDefined();
|
|
25
|
+
|
|
26
|
+
expect(labelLayer.layout?.['text-field']).toBe("{name}");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should handle language suffix en correctly', () => {
|
|
19
30
|
const language: Language = 'en';
|
|
20
31
|
const layers = getShortbreadLayers({ language });
|
|
21
32
|
const labelLayer = layers.find((layer) => layer.id === 'label-street-pedestrian') as SymbolLayerSpecification;
|
|
22
33
|
|
|
23
34
|
expect(labelLayer).toBeDefined();
|
|
24
35
|
|
|
25
|
-
expect(labelLayer.layout?.['text-field']).
|
|
36
|
+
expect(labelLayer.layout?.['text-field']).toStrictEqual([
|
|
37
|
+
"coalesce",
|
|
38
|
+
"{name_en}",
|
|
39
|
+
"{name}",
|
|
40
|
+
]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should handle language suffix fr correctly', () => {
|
|
44
|
+
const language: Language = 'fr';
|
|
45
|
+
const layers = getShortbreadLayers({ language });
|
|
46
|
+
const labelLayer = layers.find((layer) => layer.id === 'label-street-pedestrian') as SymbolLayerSpecification;
|
|
47
|
+
|
|
48
|
+
expect(labelLayer).toBeDefined();
|
|
49
|
+
|
|
50
|
+
expect(labelLayer.layout?.['text-field']).toStrictEqual([
|
|
51
|
+
"coalesce",
|
|
52
|
+
"{name_fr}",
|
|
53
|
+
"{name}",
|
|
54
|
+
]);
|
|
26
55
|
});
|
|
27
56
|
|
|
28
57
|
it('should create appropriate filters for land layers', () => {
|
package/src/shortbread/layers.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
import type { LegacyFilterSpecification } from '@maplibre/maplibre-gl-style-spec';
|
|
3
|
+
import type { DataDrivenPropertyValueSpecification, FormattedSpecification, LegacyFilterSpecification } from '@maplibre/maplibre-gl-style-spec';
|
|
4
4
|
import type { MaplibreLayerDefinition } from '../types/index.js';
|
|
5
5
|
import { Language } from '../style_builder/types.js';
|
|
6
6
|
|
|
7
7
|
export function getShortbreadLayers(option: { readonly language: Language }): MaplibreLayerDefinition[] {
|
|
8
8
|
const { language } = option;
|
|
9
|
-
|
|
9
|
+
let nameField: DataDrivenPropertyValueSpecification<FormattedSpecification> = '{name}';
|
|
10
|
+
if (language) {
|
|
11
|
+
nameField = ['coalesce', '{name_' + language + '}', '{name}'];
|
|
12
|
+
}
|
|
10
13
|
|
|
11
14
|
return [
|
|
12
15
|
|
|
@@ -21,10 +21,9 @@ export abstract class StyleBuilder {
|
|
|
21
21
|
public abstract readonly defaultFonts: StyleBuilderFonts;
|
|
22
22
|
|
|
23
23
|
public build(options?: StyleBuilderOptions): StyleSpecification {
|
|
24
|
-
|
|
25
24
|
options ??= {};
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
// @ts-expect-error globalThis may be undefined in some environments
|
|
28
27
|
const baseUrl = options.baseUrl ?? globalThis?.document?.location?.origin ?? 'https://tiles.versatiles.org';
|
|
29
28
|
const glyphs = options.glyphs ?? '/assets/glyphs/{fontstack}/{range}.pbf';
|
|
30
29
|
|
|
@@ -3,7 +3,7 @@ import type { RecolorOptions } from './recolor.js';
|
|
|
3
3
|
import { SpriteSpecification } from '@maplibre/maplibre-gl-style-spec';
|
|
4
4
|
|
|
5
5
|
/** Represents language suffixes used in map styles. */
|
|
6
|
-
export type Language =
|
|
6
|
+
export type Language = string | null;
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Options for configuring the style builder.
|
|
@@ -46,7 +46,7 @@ export interface StyleBuilderOptions {
|
|
|
46
46
|
hideLabels?: boolean;
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Set the language ('en', 'de') of all map labels.
|
|
49
|
+
* Set the language ('en', 'de', ...) of all map labels.
|
|
50
50
|
* A null value means that the language of the country in which the label is drawn will be used.
|
|
51
51
|
* See also: {@link Language}
|
|
52
52
|
* @default null
|