@mapbox/mapbox-gl-style-spec 14.11.0-beta.1 → 14.11.0-beta.2
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/dist/index.cjs +215 -124
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +49 -36
- package/dist/index.es.js +215 -124
- package/dist/index.es.js.map +1 -1
- package/expression/definitions/image.ts +125 -71
- package/expression/evaluation_context.ts +2 -1
- package/expression/index.ts +11 -10
- package/expression/types/formatted.ts +7 -3
- package/expression/types/image_id.ts +61 -0
- package/expression/types/image_variant.ts +79 -0
- package/expression/types/resolved_image.ts +44 -53
- package/package.json +1 -1
- package/reference/v8.json +1 -1
- package/types/brand.ts +4 -0
- package/expression/types/image_id_with_options.ts +0 -58
|
@@ -1,46 +1,62 @@
|
|
|
1
|
-
import {ColorType, ResolvedImageType, StringType} from '../types';
|
|
2
1
|
import ResolvedImage from '../types/resolved_image';
|
|
2
|
+
import {ImageId} from '../types/image_id';
|
|
3
|
+
import {ColorType, ResolvedImageType, StringType} from '../types';
|
|
3
4
|
|
|
4
5
|
import type Color from '../../util/color';
|
|
5
|
-
import type {Expression, SerializedExpression} from '../expression';
|
|
6
|
-
import type EvaluationContext from '../evaluation_context';
|
|
7
6
|
import type ParsingContext from '../parsing_context';
|
|
7
|
+
import type EvaluationContext from '../evaluation_context';
|
|
8
8
|
import type {Type} from '../types';
|
|
9
|
+
import type {Expression, SerializedExpression} from '../expression';
|
|
9
10
|
|
|
10
11
|
export type ImageParams = Record<string, Expression>;
|
|
12
|
+
export type IconsetParams = {id: string};
|
|
11
13
|
|
|
12
14
|
export type ImageOptions = {
|
|
13
|
-
params
|
|
15
|
+
params?: ImageParams;
|
|
16
|
+
iconset?: IconsetParams;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
type SerializedImageOptions = {
|
|
20
|
+
params?: Record<string, SerializedExpression>;
|
|
21
|
+
iconset?: IconsetParams;
|
|
22
|
+
};
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
function isImageOptions(value: unknown): value is ImageOptions {
|
|
25
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
export default class ImageExpression implements Expression {
|
|
25
29
|
type: Type;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
namePrimary: Expression;
|
|
32
|
+
paramsPrimary?: ImageParams;
|
|
33
|
+
iconsetIdPrimary?: string;
|
|
34
|
+
|
|
35
|
+
nameSecondary?: Expression;
|
|
36
|
+
paramsSecondary?: ImageParams;
|
|
37
|
+
iconsetIdSecondary?: string;
|
|
30
38
|
|
|
31
39
|
_imageWarnHistory: Record<string, boolean> = {};
|
|
32
40
|
|
|
33
41
|
constructor(
|
|
34
42
|
inputPrimary: Expression,
|
|
35
43
|
inputSecondary?: Expression | null,
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
inputPrimaryOptions?: ImageOptions,
|
|
45
|
+
inputSecondaryOptions?: ImageOptions
|
|
38
46
|
) {
|
|
39
47
|
this.type = ResolvedImageType;
|
|
40
|
-
this.
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
this.namePrimary = inputPrimary;
|
|
49
|
+
this.nameSecondary = inputSecondary;
|
|
50
|
+
|
|
51
|
+
if (inputPrimaryOptions) {
|
|
52
|
+
this.paramsPrimary = inputPrimaryOptions.params;
|
|
53
|
+
this.iconsetIdPrimary = inputPrimaryOptions.iconset ? inputPrimaryOptions.iconset.id : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (inputSecondaryOptions) {
|
|
57
|
+
this.paramsSecondary = inputSecondaryOptions.params;
|
|
58
|
+
this.iconsetIdSecondary = inputSecondaryOptions.iconset ? inputSecondaryOptions.iconset.id : undefined;
|
|
59
|
+
}
|
|
44
60
|
}
|
|
45
61
|
|
|
46
62
|
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Expression | null | void {
|
|
@@ -49,7 +65,7 @@ export default class ImageExpression implements Expression {
|
|
|
49
65
|
}
|
|
50
66
|
|
|
51
67
|
let nextArgId = 1;
|
|
52
|
-
const imageExpression: Array<{image: Expression, options
|
|
68
|
+
const imageExpression: Array<{image: Expression, options?: ImageOptions}> = [];
|
|
53
69
|
|
|
54
70
|
function tryParseImage() {
|
|
55
71
|
if (nextArgId < args.length) {
|
|
@@ -59,7 +75,7 @@ export default class ImageExpression implements Expression {
|
|
|
59
75
|
return false;
|
|
60
76
|
}
|
|
61
77
|
|
|
62
|
-
imageExpression.push({image: imageName, options:
|
|
78
|
+
imageExpression.push({image: imageName, options: {}});
|
|
63
79
|
return true;
|
|
64
80
|
}
|
|
65
81
|
|
|
@@ -68,45 +84,62 @@ export default class ImageExpression implements Expression {
|
|
|
68
84
|
|
|
69
85
|
function tryParseOptions() {
|
|
70
86
|
if (nextArgId < args.length) {
|
|
71
|
-
|
|
87
|
+
const options = args[nextArgId];
|
|
88
|
+
if (!isImageOptions(options)) {
|
|
72
89
|
return true;
|
|
73
90
|
}
|
|
74
91
|
|
|
75
|
-
const params =
|
|
76
|
-
|
|
92
|
+
const params = options.params;
|
|
93
|
+
const iconset = options.iconset;
|
|
77
94
|
const optionsContext = context.concat(nextArgId);
|
|
78
95
|
|
|
79
|
-
if (!params) {
|
|
96
|
+
if (!params && !iconset) {
|
|
80
97
|
nextArgId++;
|
|
81
98
|
return true;
|
|
82
99
|
}
|
|
83
100
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
// Parse the image options params as expressions
|
|
102
|
+
if (params) {
|
|
103
|
+
if (typeof params !== 'object' || params.constructor !== Object) {
|
|
104
|
+
optionsContext.error(`Image options \"params\" should be an object`);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
88
107
|
|
|
89
|
-
|
|
108
|
+
const parsedParams: ImageParams = {};
|
|
109
|
+
const childContext = optionsContext.concat(undefined, 'params');
|
|
110
|
+
for (const key in params) {
|
|
111
|
+
if (!key) {
|
|
112
|
+
childContext.error(`Image parameter name should be non-empty`);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
90
115
|
|
|
91
|
-
|
|
116
|
+
const value = childContext.concat(undefined, key).parse(params[key], undefined, ColorType, undefined, {typeAnnotation: 'coerce'});
|
|
117
|
+
if (!value) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
92
120
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
121
|
+
parsedParams[key] = value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
imageExpression[imageExpression.length - 1].options.params = parsedParams;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Validate the iconset image options
|
|
128
|
+
if (iconset) {
|
|
129
|
+
if (typeof iconset !== 'object' || iconset.constructor !== Object) {
|
|
130
|
+
optionsContext.error(`Image options \"iconset\" should be an object`);
|
|
96
131
|
return false;
|
|
97
132
|
}
|
|
98
133
|
|
|
99
|
-
|
|
100
|
-
|
|
134
|
+
if (!iconset.id) {
|
|
135
|
+
optionsContext.error(`Image options \"iconset\" should have an \"id\" property`);
|
|
101
136
|
return false;
|
|
102
137
|
}
|
|
103
138
|
|
|
104
|
-
|
|
139
|
+
imageExpression[imageExpression.length - 1].options.iconset = iconset;
|
|
105
140
|
}
|
|
106
141
|
|
|
107
|
-
imageExpression[imageExpression.length - 1].options = parsed;
|
|
108
142
|
nextArgId++;
|
|
109
|
-
|
|
110
143
|
return true;
|
|
111
144
|
}
|
|
112
145
|
|
|
@@ -162,17 +195,30 @@ export default class ImageExpression implements Expression {
|
|
|
162
195
|
}
|
|
163
196
|
|
|
164
197
|
evaluate(ctx: EvaluationContext): null | ResolvedImage {
|
|
198
|
+
const primaryId = {
|
|
199
|
+
name: this.namePrimary.evaluate(ctx),
|
|
200
|
+
iconsetId: this.iconsetIdPrimary
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const secondaryId = this.nameSecondary ? {
|
|
204
|
+
name: this.nameSecondary.evaluate(ctx),
|
|
205
|
+
iconsetId: this.iconsetIdSecondary
|
|
206
|
+
} : undefined;
|
|
207
|
+
|
|
165
208
|
const value = ResolvedImage.build(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
this.
|
|
169
|
-
this.
|
|
209
|
+
primaryId,
|
|
210
|
+
secondaryId,
|
|
211
|
+
this.paramsPrimary ? this.evaluateParams(ctx, this.paramsPrimary) : undefined,
|
|
212
|
+
this.paramsSecondary ? this.evaluateParams(ctx, this.paramsSecondary) : undefined
|
|
170
213
|
);
|
|
214
|
+
|
|
171
215
|
if (value && ctx.availableImages) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (value.
|
|
175
|
-
|
|
216
|
+
const primaryId = value.getPrimary().id;
|
|
217
|
+
value.available = ctx.availableImages.some((id) => ImageId.isEqual(id, primaryId));
|
|
218
|
+
if (value.available) {
|
|
219
|
+
// If there's a secondary variant, only mark it available if both are present
|
|
220
|
+
const secondaryId = value.getSecondary() ? value.getSecondary().id : null;
|
|
221
|
+
if (secondaryId) value.available = ctx.availableImages.some((id) => ImageId.isEqual(id, secondaryId));
|
|
176
222
|
}
|
|
177
223
|
}
|
|
178
224
|
|
|
@@ -180,20 +226,22 @@ export default class ImageExpression implements Expression {
|
|
|
180
226
|
}
|
|
181
227
|
|
|
182
228
|
eachChild(fn: (_: Expression) => void) {
|
|
183
|
-
fn(this.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
229
|
+
fn(this.namePrimary);
|
|
230
|
+
|
|
231
|
+
if (this.paramsPrimary) {
|
|
232
|
+
for (const key in this.paramsPrimary) {
|
|
233
|
+
if (this.paramsPrimary[key]) {
|
|
234
|
+
fn(this.paramsPrimary[key]);
|
|
188
235
|
}
|
|
189
236
|
}
|
|
190
237
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
238
|
+
|
|
239
|
+
if (this.nameSecondary) {
|
|
240
|
+
fn(this.nameSecondary);
|
|
241
|
+
if (this.paramsSecondary) {
|
|
242
|
+
for (const key in this.paramsSecondary) {
|
|
243
|
+
if (this.paramsSecondary[key]) {
|
|
244
|
+
fn(this.paramsSecondary[key]);
|
|
197
245
|
}
|
|
198
246
|
}
|
|
199
247
|
}
|
|
@@ -205,33 +253,39 @@ export default class ImageExpression implements Expression {
|
|
|
205
253
|
return false;
|
|
206
254
|
}
|
|
207
255
|
|
|
208
|
-
|
|
209
|
-
const result:
|
|
256
|
+
serializeOptions(params: ImageParams, iconsetId: string): SerializedImageOptions | undefined {
|
|
257
|
+
const result: SerializedImageOptions = {};
|
|
258
|
+
|
|
259
|
+
if (iconsetId) {
|
|
260
|
+
result.iconset = {id: iconsetId};
|
|
261
|
+
}
|
|
262
|
+
|
|
210
263
|
if (params) {
|
|
264
|
+
result.params = {};
|
|
211
265
|
for (const key in params) {
|
|
212
266
|
if (params[key]) {
|
|
213
|
-
result[key] = params[key].serialize();
|
|
267
|
+
result.params[key] = params[key].serialize();
|
|
214
268
|
}
|
|
215
269
|
}
|
|
216
|
-
} else {
|
|
217
|
-
return undefined;
|
|
218
270
|
}
|
|
219
271
|
|
|
220
|
-
return
|
|
272
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
221
273
|
}
|
|
222
274
|
|
|
223
275
|
serialize(): SerializedExpression {
|
|
224
|
-
const serialized: SerializedExpression = [
|
|
276
|
+
const serialized: SerializedExpression = ['image', this.namePrimary.serialize()];
|
|
225
277
|
|
|
226
|
-
if (this.
|
|
227
|
-
|
|
278
|
+
if (this.paramsPrimary || this.iconsetIdPrimary) {
|
|
279
|
+
const options = this.serializeOptions(this.paramsPrimary, this.iconsetIdPrimary);
|
|
280
|
+
if (options) serialized.push(options);
|
|
228
281
|
}
|
|
229
282
|
|
|
230
|
-
if (this.
|
|
231
|
-
serialized.push(this.
|
|
283
|
+
if (this.nameSecondary) {
|
|
284
|
+
serialized.push(this.nameSecondary.serialize());
|
|
232
285
|
|
|
233
|
-
if (this.
|
|
234
|
-
|
|
286
|
+
if (this.paramsSecondary || this.iconsetIdSecondary) {
|
|
287
|
+
const options = this.serializeOptions(this.paramsSecondary, this.iconsetIdSecondary);
|
|
288
|
+
if (options) serialized.push(options);
|
|
235
289
|
}
|
|
236
290
|
}
|
|
237
291
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {Color} from './values';
|
|
2
2
|
|
|
3
3
|
import type Point from '@mapbox/point-geometry';
|
|
4
|
+
import type {ImageId} from './types/image_id';
|
|
4
5
|
import type {FormattedSection} from './types/formatted';
|
|
5
6
|
import type {GlobalProperties, Feature, FeatureState} from './index';
|
|
6
7
|
import type {CanonicalTileID} from '../types/tile_id';
|
|
@@ -14,7 +15,7 @@ class EvaluationContext {
|
|
|
14
15
|
feature: Feature | null | undefined;
|
|
15
16
|
featureState: FeatureState | null | undefined;
|
|
16
17
|
formattedSection: FormattedSection | null | undefined;
|
|
17
|
-
availableImages:
|
|
18
|
+
availableImages: ImageId[] | null | undefined;
|
|
18
19
|
canonical: null | CanonicalTileID;
|
|
19
20
|
featureTileCoord: Point | null | undefined;
|
|
20
21
|
featureDistanceData: FeatureDistanceData | null | undefined;
|
package/expression/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ import type Point from '@mapbox/point-geometry';
|
|
|
35
35
|
import type {CanonicalTileID} from '../types/tile_id';
|
|
36
36
|
import type {FeatureDistanceData} from '../feature_filter/index';
|
|
37
37
|
import type {ConfigOptions} from '../types/config_options';
|
|
38
|
+
import type {ImageId} from './types/image_id';
|
|
38
39
|
|
|
39
40
|
export interface Feature {
|
|
40
41
|
readonly type: 0 | 1 | 2 | 3 | 'Unknown' | 'Point' | 'LineString' | 'Polygon';
|
|
@@ -88,7 +89,7 @@ export class StyleExpression {
|
|
|
88
89
|
feature?: Feature,
|
|
89
90
|
featureState?: FeatureState,
|
|
90
91
|
canonical?: CanonicalTileID,
|
|
91
|
-
availableImages?:
|
|
92
|
+
availableImages?: ImageId[],
|
|
92
93
|
formattedSection?: FormattedSection,
|
|
93
94
|
featureTileCoord?: Point,
|
|
94
95
|
featureDistanceData?: FeatureDistanceData,
|
|
@@ -110,7 +111,7 @@ export class StyleExpression {
|
|
|
110
111
|
feature?: Feature,
|
|
111
112
|
featureState?: FeatureState,
|
|
112
113
|
canonical?: CanonicalTileID,
|
|
113
|
-
availableImages?:
|
|
114
|
+
availableImages?: ImageId[],
|
|
114
115
|
formattedSection?: FormattedSection,
|
|
115
116
|
featureTileCoord?: Point,
|
|
116
117
|
featureDistanceData?: FeatureDistanceData,
|
|
@@ -202,7 +203,7 @@ export class ZoomConstantExpression<Kind extends EvaluationKind> {
|
|
|
202
203
|
feature?: Feature,
|
|
203
204
|
featureState?: FeatureState,
|
|
204
205
|
canonical?: CanonicalTileID,
|
|
205
|
-
availableImages?:
|
|
206
|
+
availableImages?: ImageId[],
|
|
206
207
|
formattedSection?: FormattedSection,
|
|
207
208
|
): any {
|
|
208
209
|
return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection);
|
|
@@ -213,7 +214,7 @@ export class ZoomConstantExpression<Kind extends EvaluationKind> {
|
|
|
213
214
|
feature?: Feature,
|
|
214
215
|
featureState?: FeatureState,
|
|
215
216
|
canonical?: CanonicalTileID,
|
|
216
|
-
availableImages?:
|
|
217
|
+
availableImages?: ImageId[],
|
|
217
218
|
formattedSection?: FormattedSection,
|
|
218
219
|
): any {
|
|
219
220
|
return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection);
|
|
@@ -247,7 +248,7 @@ export class ZoomDependentExpression<Kind extends EvaluationKind> {
|
|
|
247
248
|
feature?: Feature,
|
|
248
249
|
featureState?: FeatureState,
|
|
249
250
|
canonical?: CanonicalTileID,
|
|
250
|
-
availableImages?:
|
|
251
|
+
availableImages?: ImageId[],
|
|
251
252
|
formattedSection?: FormattedSection,
|
|
252
253
|
): any {
|
|
253
254
|
return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection);
|
|
@@ -258,7 +259,7 @@ export class ZoomDependentExpression<Kind extends EvaluationKind> {
|
|
|
258
259
|
feature?: Feature,
|
|
259
260
|
featureState?: FeatureState,
|
|
260
261
|
canonical?: CanonicalTileID,
|
|
261
|
-
availableImages?:
|
|
262
|
+
availableImages?: ImageId[],
|
|
262
263
|
formattedSection?: FormattedSection,
|
|
263
264
|
): any {
|
|
264
265
|
return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection);
|
|
@@ -281,7 +282,7 @@ export type ConstantExpression = {
|
|
|
281
282
|
feature?: Feature,
|
|
282
283
|
featureState?: FeatureState,
|
|
283
284
|
canonical?: CanonicalTileID,
|
|
284
|
-
availableImages?:
|
|
285
|
+
availableImages?: ImageId[],
|
|
285
286
|
) => any;
|
|
286
287
|
};
|
|
287
288
|
|
|
@@ -296,7 +297,7 @@ export type SourceExpression = {
|
|
|
296
297
|
feature?: Feature,
|
|
297
298
|
featureState?: FeatureState,
|
|
298
299
|
canonical?: CanonicalTileID,
|
|
299
|
-
availableImages?:
|
|
300
|
+
availableImages?: ImageId[],
|
|
300
301
|
formattedSection?: FormattedSection,
|
|
301
302
|
) => any;
|
|
302
303
|
};
|
|
@@ -310,7 +311,7 @@ export type CameraExpression = {
|
|
|
310
311
|
feature?: Feature,
|
|
311
312
|
featureState?: FeatureState,
|
|
312
313
|
canonical?: CanonicalTileID,
|
|
313
|
-
availableImages?:
|
|
314
|
+
availableImages?: ImageId[],
|
|
314
315
|
) => any;
|
|
315
316
|
readonly interpolationFactor: (input: number, lower: number, upper: number) => number;
|
|
316
317
|
zoomStops: Array<number>;
|
|
@@ -328,7 +329,7 @@ export interface CompositeExpression {
|
|
|
328
329
|
feature?: Feature,
|
|
329
330
|
featureState?: FeatureState,
|
|
330
331
|
canonical?: CanonicalTileID,
|
|
331
|
-
availableImages?:
|
|
332
|
+
availableImages?: ImageId[],
|
|
332
333
|
formattedSection?: FormattedSection,
|
|
333
334
|
) => any;
|
|
334
335
|
readonly interpolationFactor: (input: number, lower: number, upper: number) => number;
|
|
@@ -31,8 +31,11 @@ export default class Formatted {
|
|
|
31
31
|
|
|
32
32
|
isEmpty(): boolean {
|
|
33
33
|
if (this.sections.length === 0) return true;
|
|
34
|
-
return !this.sections.some(section =>
|
|
35
|
-
|
|
34
|
+
return !this.sections.some(section => {
|
|
35
|
+
if (section.text.length !== 0) return true;
|
|
36
|
+
if (!section.image) return false;
|
|
37
|
+
return section.image.hasPrimary();
|
|
38
|
+
});
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
static factory(text: Formatted | string): Formatted {
|
|
@@ -52,7 +55,8 @@ export default class Formatted {
|
|
|
52
55
|
const serialized: Array<unknown> = ["format"];
|
|
53
56
|
for (const section of this.sections) {
|
|
54
57
|
if (section.image) {
|
|
55
|
-
|
|
58
|
+
const primaryId = section.image.getPrimary().id.toString();
|
|
59
|
+
serialized.push(['image', primaryId]);
|
|
56
60
|
continue;
|
|
57
61
|
}
|
|
58
62
|
serialized.push(section.text);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type {Brand} from '../../types/brand';
|
|
2
|
+
|
|
3
|
+
export type ImageIdSpec = {
|
|
4
|
+
name: string;
|
|
5
|
+
iconsetId?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `StringifiedImageId` is a stringified version of the `ImageId`.
|
|
10
|
+
*
|
|
11
|
+
* @private
|
|
12
|
+
*/
|
|
13
|
+
export type StringifiedImageId = Brand<string, 'ImageId'>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `ImageId` is a reference to an {@link ImageVariant} in the sprite or iconset.
|
|
17
|
+
*
|
|
18
|
+
* @private
|
|
19
|
+
*/
|
|
20
|
+
export class ImageId {
|
|
21
|
+
name: string;
|
|
22
|
+
iconsetId?: string;
|
|
23
|
+
|
|
24
|
+
constructor(id: string | ImageId | ImageIdSpec) {
|
|
25
|
+
if (typeof id === 'string') {
|
|
26
|
+
this.name = id;
|
|
27
|
+
} else {
|
|
28
|
+
this.name = id.name;
|
|
29
|
+
this.iconsetId = id.iconsetId;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static from(id: string | ImageId | ImageIdSpec): ImageId {
|
|
34
|
+
return new ImageId(id);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static toString(id: ImageId | ImageIdSpec): StringifiedImageId {
|
|
38
|
+
return JSON.stringify({name: id.name, iconsetId: id.iconsetId}) as StringifiedImageId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static parse(str: StringifiedImageId): ImageId | null {
|
|
42
|
+
try {
|
|
43
|
+
const {name, iconsetId} = JSON.parse(str);
|
|
44
|
+
return new ImageId({name, iconsetId});
|
|
45
|
+
} catch (e) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static isEqual(a: ImageId | ImageIdSpec, b: ImageId | ImageIdSpec): boolean {
|
|
51
|
+
return a.name === b.name && a.iconsetId === b.iconsetId;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
toString(): StringifiedImageId {
|
|
55
|
+
return JSON.stringify({name: this.name, iconsetId: this.iconsetId}) as StringifiedImageId;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
serialize(): ImageIdSpec {
|
|
59
|
+
return {name: this.name, iconsetId: this.iconsetId};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {ImageId} from './image_id';
|
|
2
|
+
|
|
3
|
+
import type Color from '../../util/color';
|
|
4
|
+
import type {Brand} from '../../types/brand';
|
|
5
|
+
import type {ImageIdSpec} from './image_id';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `StringifiedImageVariant` is a stringified version of the `ImageVariant`.
|
|
9
|
+
*
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
export type StringifiedImageVariant = Brand<string, 'ImageVariant'>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* {@link ImageVariant} rasterization options.
|
|
16
|
+
*
|
|
17
|
+
* @private
|
|
18
|
+
*/
|
|
19
|
+
export type RasterizationOptions = {
|
|
20
|
+
params?: Record<string, Color>;
|
|
21
|
+
transform?: DOMMatrix;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* `ImageVariant` is a component of {@link ResolvedImage}
|
|
26
|
+
* that represents either the primary or secondary image
|
|
27
|
+
* along with its rendering configuration.
|
|
28
|
+
*
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
export class ImageVariant {
|
|
32
|
+
id: ImageId;
|
|
33
|
+
options: RasterizationOptions;
|
|
34
|
+
|
|
35
|
+
constructor(id: string | ImageIdSpec, options: RasterizationOptions = {}) {
|
|
36
|
+
this.id = ImageId.from(id);
|
|
37
|
+
this.options = Object.assign({}, options);
|
|
38
|
+
|
|
39
|
+
if (!options.transform) {
|
|
40
|
+
this.options.transform = new DOMMatrix([1, 0, 0, 1, 0, 0]);
|
|
41
|
+
} else {
|
|
42
|
+
const {a, b, c, d, e, f} = options.transform;
|
|
43
|
+
this.options.transform = new DOMMatrix([a, b, c, d, e, f]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
toString(): StringifiedImageVariant {
|
|
48
|
+
const {a, b, c, d, e, f} = this.options.transform;
|
|
49
|
+
|
|
50
|
+
const serialized = {
|
|
51
|
+
name: this.id.name,
|
|
52
|
+
iconsetId: this.id.iconsetId,
|
|
53
|
+
params: this.options.params,
|
|
54
|
+
transform: {a, b, c, d, e, f},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return JSON.stringify(serialized) as StringifiedImageVariant;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static parse(str: StringifiedImageVariant): ImageVariant | null {
|
|
61
|
+
let name, iconsetId, params, transform;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
({name, iconsetId, params, transform} = JSON.parse(str) || {});
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!name) return null;
|
|
70
|
+
|
|
71
|
+
const {a, b, c, d, e, f} = transform || {};
|
|
72
|
+
return new ImageVariant({name, iconsetId}, {params, transform: new DOMMatrix([a, b, c, d, e, f])});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
scaleSelf(factor: number): this {
|
|
76
|
+
this.options.transform.scaleSelf(factor);
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
}
|