@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.
@@ -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
- test('constructor initializes values correctly with clamping', () => {
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
- test('asArray returns the correct array representation', () => {
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
- test('clone creates a new instance with identical values', () => {
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
- test('asString returns correct RGB/RGBA string', () => {
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
- test('asHex returns correct hexadecimal representation', () => {
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
- test('asHSL converts RGB to HSL correctly', () => {
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
- test('asHSV converts RGB to HSV correctly', () => {
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
- test('asRGB and toRGB return the same instance or clone', () => {
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
- test('handles black correctly', () => {
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
- test('handles white correctly', () => {
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
- test('parses hexadecimal color strings correctly', () => {
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
- test('parses shorthand hexadecimal strings correctly', () => {
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
- test('parses RGB strings correctly', () => {
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
- test('parses RGBA strings correctly', () => {
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
- test('throws an error for invalid strings', () => {
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
- test('adjusts gamma correctly', () => {
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
- test('inverts RGB values correctly', () => {
144
+ it('inverts RGB values correctly', () => {
144
145
  expect(pc(c => c.invert())).toStrictEqual([205, 105, 55, 0.8]);
145
146
  });
146
147
 
147
- test('adjusts contrast correctly', () => {
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
- test('increases brightness correctly', () => {
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
- test('tints color correctly', () => {
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
- test('blends color correctly', () => {
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
- test('lightens the color correctly', () => {
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
- test('darkens the color correctly', () => {
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
- test('fades color correctly', () => {
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
 
@@ -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
- test('returns the number itself if within the range', () => {
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
- test('returns the minimum value if number is below range', () => {
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
- test('returns the maximum value if number is above range', () => {
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
- test('handles edge cases with boundaries', () => {
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
- test('returns the correct modulus for positive numbers', () => {
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
- test('handles negative numbers correctly', () => {
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
- test('returns 0 for numbers that are exact multiples of max', () => {
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
- test('returns 0 for zero input with positive max', () => {
43
+ it('returns 0 for zero input with positive max', () => {
43
44
  expect(mod(0, 5)).toBe(0);
44
45
  });
45
46
 
46
- test('handles edge cases with 1 as max', () => {
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
- test('formats numbers with specified precision correctly', () => {
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
- test('handles trailing zeros correctly', () => {
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
- test('handles edge cases with small numbers and high precision', () => {
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
- test('handles large numbers correctly', () => {
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
- test('handles zero and negative numbers correctly', () => {
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
- test('handles no unnecessary decimal points', () => {
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
  });
@@ -1,4 +1,4 @@
1
-
1
+ import { describe, expect, it } from 'vitest';
2
2
  import type { TileJSONSpecification, VectorLayer } from '../types/index.js';
3
3
  import { guessStyle } from './guess_style.js';
4
4
  import { getShortbreadVectorLayers } from '../shortbread/template.js';
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 { guessStyle, styles } from './index.js';
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
- const style = colorful({
37
- baseUrl: 'https://dev.null',
38
- colors: { commercial: '#f00' },
39
- });
40
- expect(style.glyphs).toBe('https://dev.null/assets/glyphs/{fontstack}/{range}.pbf');
41
- const paint = style.layers.find(l => l.id === 'land-commercial')?.paint;
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
- expect(paint).toBeDefined();
44
- if (paint == null) throw Error();
44
+ expect(paint).toBeDefined();
45
+ if (paint == null) throw Error();
45
46
 
46
- expect(paint).toHaveProperty('fill-color');
47
- if (!('fill-color' in paint)) throw Error();
47
+ expect(paint).toHaveProperty('fill-color');
48
+ if (!('fill-color' in paint)) throw Error();
48
49
 
49
- expect(paint['fill-color']).toBe('rgb(255,0,0)');
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 = VersatilesStyle.colorful();
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-style';
26
- * // OR: const { colorful } = require('versatiles-style');
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';
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { Color } from '../color/index.js';
2
3
  import { deepClone, isSimpleObject, isBasicType, deepMerge, resolveUrl, basename } from './utils.js';
3
4
 
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
- // @ts-expect-error: Too complicated to handle
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
- // @ts-expect-error: Too complicated to handle
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
- // @ts-expect-error: Too complicated to handle
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']).toContain('{name_en}');
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', () => {
@@ -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
- const nameField = language ? '{name_' + language + '}' : '{name}';
9
+ let nameField: DataDrivenPropertyValueSpecification<FormattedSpecification> = '{name}';
10
+ if (language) {
11
+ nameField = ['coalesce', '{name_' + language + '}', '{name}'];
12
+ }
10
13
 
11
14
  return [
12
15
 
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import type { ShortbreadProperty } from './properties.js';
2
3
  import propertyLookup from './properties.js';
3
4
 
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { VectorSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
2
3
  import { getShortbreadTemplate } from './template.js';
3
4
 
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { decorate } from './decorator.js';
2
3
  import { Color } from '../color/index.js';
3
4
  import type { MaplibreLayer } from '../types/maplibre.js';
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { Color } from '../color/index.js';
2
3
  import { CachedRecolor, getDefaultRecolorFlags, recolorArray, recolorObject } from './recolor.js';
3
4
 
@@ -1,4 +1,4 @@
1
-
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
2
  import { Color } from '../color/index.js';
3
3
  import type { StyleRules, StyleRulesOptions } from './types.js';
4
4
  import { StyleBuilder } from './style_builder.js';
@@ -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 = 'de' | 'en' | null;
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
@@ -1,5 +1,4 @@
1
-
2
-
1
+ import { describe, expect, it } from 'vitest';
3
2
  import { isTileJSONSpecification } from './tilejson.js';
4
3
 
5
4
  describe('isTileJSONSpecification', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import type { VectorLayer } from './vector_layer.js';
2
3
  import { isVectorLayer, isVectorLayers } from './vector_layer.js';
3
4