maplibre-gl 6.2.0 → 6.4.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/dist/maplibre-gl-dev.mjs +1108 -427
- package/dist/maplibre-gl-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared-dev.mjs +359 -291
- package/dist/maplibre-gl-shared-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared.mjs +2 -2
- package/dist/maplibre-gl-shared.mjs.map +1 -1
- package/dist/maplibre-gl-worker-dev.mjs +23 -49
- package/dist/maplibre-gl-worker-dev.mjs.map +1 -1
- package/dist/maplibre-gl-worker.mjs +2 -2
- package/dist/maplibre-gl-worker.mjs.map +1 -1
- package/dist/maplibre-gl.css +1 -1
- package/dist/maplibre-gl.d.ts +467 -151
- package/dist/maplibre-gl.mjs +49 -49
- package/dist/maplibre-gl.mjs.map +1 -1
- package/package.json +12 -22
- package/src/data/bucket/fill_extrusion_bucket.ts +1 -13
- package/src/data/bucket/round_polygon_corners.test.ts +84 -36
- package/src/data/bucket/round_polygon_corners.ts +90 -39
- package/src/data/extent_bounds.ts +28 -1
- package/src/data/feature_index.bench.ts +21 -0
- package/src/data/segment.test.ts +2 -2
- package/src/geo/edge_insets.test.ts +1 -1
- package/src/geo/lng_lat.test.ts +7 -7
- package/src/geo/lng_lat_bounds.test.ts +4 -4
- package/src/geo/mercator_coordinate.test.ts +2 -2
- package/src/geo/projection/camera_helper.ts +5 -0
- package/src/geo/projection/globe_transform.test.ts +154 -1
- package/src/geo/projection/globe_transform.ts +7 -3
- package/src/geo/projection/globe_utils.ts +189 -2
- package/src/geo/projection/mercator_camera_helper.ts +1 -1
- package/src/geo/projection/mercator_transform.ts +21 -5
- package/src/geo/projection/vertical_perspective_camera_helper.test.ts +24 -0
- package/src/geo/projection/vertical_perspective_camera_helper.ts +71 -41
- package/src/geo/projection/vertical_perspective_transform.ts +7 -1
- package/src/geo/transform_interface.ts +13 -1
- package/src/index.ts +7 -2
- package/src/render/image_atlas.test.ts +180 -0
- package/src/render/image_atlas.ts +52 -6
- package/src/render/image_manager.test.ts +111 -0
- package/src/render/image_manager.ts +143 -144
- package/src/render/painter.ts +6 -14
- package/src/render/pattern_atlas.test.ts +131 -0
- package/src/render/pattern_atlas.ts +144 -0
- package/src/render/update_pattern_positions_in_program.test.ts +1 -1
- package/src/shaders/glsl/_prelude.vertex.glsl +10 -11
- package/src/shaders/glsl/_prelude.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/circle.vertex.glsl +4 -4
- package/src/shaders/glsl/circle.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/color_relief.fragment.glsl +1 -2
- package/src/shaders/glsl/color_relief.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/fill_extrusion.vertex.glsl +3 -3
- package/src/shaders/glsl/fill_extrusion.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/fill_extrusion_pattern.vertex.glsl +5 -5
- package/src/shaders/glsl/fill_extrusion_pattern.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/heatmap.vertex.glsl +4 -4
- package/src/shaders/glsl/heatmap.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/hillshade_prepare.fragment.glsl +12 -12
- package/src/shaders/glsl/hillshade_prepare.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line.vertex.glsl +7 -7
- package/src/shaders/glsl/line.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_gradient.vertex.glsl +6 -6
- package/src/shaders/glsl/line_gradient.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_gradient_sdf.vertex.glsl +7 -7
- package/src/shaders/glsl/line_gradient_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_pattern.vertex.glsl +7 -7
- package/src/shaders/glsl/line_pattern.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_sdf.vertex.glsl +7 -7
- package/src/shaders/glsl/line_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/raster.fragment.glsl +4 -4
- package/src/shaders/glsl/raster.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/raster.vertex.glsl +11 -6
- package/src/shaders/glsl/raster.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/symbol_icon.vertex.glsl +5 -5
- package/src/shaders/glsl/symbol_icon.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/symbol_sdf.vertex.glsl +5 -5
- package/src/shaders/glsl/symbol_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/symbol_text_and_icon.vertex.glsl +6 -6
- package/src/shaders/glsl/symbol_text_and_icon.vertex.glsl.g.ts +1 -1
- package/src/source/canvas_source.test.ts +31 -0
- package/src/source/canvas_source.ts +2 -1
- package/src/source/geojson_worker_source.test.ts +88 -0
- package/src/source/geojson_worker_source.ts +22 -39
- package/src/source/image_source.test.ts +246 -38
- package/src/source/image_source.ts +132 -19
- package/src/source/raster_dem_tile_source.test.ts +37 -3
- package/src/source/raster_dem_tile_source.ts +1 -2
- package/src/source/raster_dem_tile_worker_source.test.ts +1 -1
- package/src/source/raster_tile_source.test.ts +5 -26
- package/src/source/raster_tile_source.ts +4 -3
- package/src/source/vector_tile_worker_source.test.ts +94 -2
- package/src/source/vector_tile_worker_source.ts +18 -27
- package/src/source/video_source.test.ts +12 -8
- package/src/source/worker_tile.bench.ts +10 -0
- package/src/source/worker_tile.ts +0 -3
- package/src/style/create_style_layer.bench.ts +15 -0
- package/src/style/properties.bench.ts +81 -0
- package/src/style/style.test.ts +75 -10
- package/src/style/style.ts +37 -64
- package/src/style/style_image.ts +67 -9
- package/src/style/style_layer.bench.ts +65 -0
- package/src/style/style_layer.test.ts +1 -1
- package/src/style/style_layer.ts +2 -2
- package/src/style/validate_style.bench.ts +11 -0
- package/src/style/validate_style.test.ts +2 -2
- package/src/symbol/anchor.test.ts +2 -2
- package/src/symbol/collision_index.bench.ts +116 -0
- package/src/symbol/cross_tile_symbol_index.bench.ts +49 -0
- package/src/symbol/cross_tile_symbol_index.test.ts +31 -0
- package/src/symbol/cross_tile_symbol_index.ts +67 -37
- package/src/symbol/shaping.test.ts +2 -1
- package/src/symbol/symbol_layout.bench.ts +30 -0
- package/src/tile/tile_id.test.ts +1 -1
- package/src/tile/tile_manager.test.ts +69 -1
- package/src/tile/tile_manager.ts +16 -11
- package/src/ui/control/fullscreen_control.test.ts +10 -10
- package/src/ui/control/geolocate_control.test.ts +3 -5
- package/src/ui/control/globe_control.test.ts +6 -6
- package/src/ui/events.ts +1 -1
- package/src/ui/handler/scroll_zoom.test.ts +50 -0
- package/src/ui/handler/two_fingers_touch.test.ts +17 -6
- package/src/ui/handler_inertia.test.ts +91 -0
- package/src/ui/handler_inertia.ts +65 -14
- package/src/ui/handler_manager.test.ts +243 -3
- package/src/ui/handler_manager.ts +79 -16
- package/src/ui/map.ts +17 -3
- package/src/ui/map_tests/map_cross_window.test.ts +2 -2
- package/src/ui/map_tests/map_events.test.ts +3 -3
- package/src/ui/map_tests/map_images.test.ts +55 -0
- package/src/ui/map_tests/map_style.test.ts +9 -8
- package/src/ui/map_tests/map_webgl.test.ts +30 -0
- package/src/ui/marker.test.ts +177 -42
- package/src/ui/marker.ts +120 -10
- package/src/ui/popup.test.ts +14 -14
- package/src/util/evented.test.ts +33 -28
- package/src/util/evented.ts +69 -43
- package/src/util/image_request.ts +31 -1
- package/src/util/test/null_gl.ts +10 -0
- package/src/util/test/util.ts +2 -1
- package/src/util/util.test.ts +1 -1
- package/src/util/web_worker_transfer.test.ts +5 -5
- package/src/webgl/context.ts +20 -0
- package/src/webgl/draw/draw_background.ts +1 -1
- package/src/webgl/draw/draw_raster.ts +11 -6
- package/src/webgl/program/pattern.ts +3 -3
- package/src/webgl/program/raster_program.ts +11 -0
- package/src/webgl/program.ts +31 -16
- package/src/webgl/texture.test.ts +16 -1
- package/src/webgl/texture.ts +3 -0
- package/src/webgl/vertex_buffer.test.ts +17 -2
- package/src/webgl/vertex_buffer.ts +24 -13
package/src/ui/map.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {defaultLocale} from './default_locale.ts';
|
|
|
31
31
|
import {isAbortError} from '../util/abort_error.ts';
|
|
32
32
|
import {coveringTiles, type CoveringTilesOptions, createCalculateTileZoomFunction} from '../geo/projection/covering_tiles.ts';
|
|
33
33
|
import {CanonicalTileID, type OverscaledTileID} from '../tile/tile_id.ts';
|
|
34
|
+
import {isStyleImageWebGLData} from '../style/style_image.ts';
|
|
34
35
|
|
|
35
36
|
import type {PaddingOptions} from '../geo/edge_insets.ts';
|
|
36
37
|
import type {Source} from '../source/source.ts';
|
|
@@ -3208,9 +3209,11 @@ export class Map extends Evented<MapEventType> {
|
|
|
3208
3209
|
} else {
|
|
3209
3210
|
const {width, height, data} = image as ImageData;
|
|
3210
3211
|
const userImage = (image as any as StyleImageInterface);
|
|
3212
|
+
const isWebGLImage = isStyleImageWebGLData(userImage.data);
|
|
3211
3213
|
|
|
3212
3214
|
return {
|
|
3213
|
-
|
|
3215
|
+
// A WebGL image paints its own slot, so its pixels are only ever transparent padding.
|
|
3216
|
+
data: isWebGLImage ? new RGBAImage({width, height}) : new RGBAImage({width, height}, new Uint8Array(data)),
|
|
3214
3217
|
pixelRatio,
|
|
3215
3218
|
stretchX,
|
|
3216
3219
|
stretchY,
|
|
@@ -3219,6 +3222,7 @@ export class Map extends Evented<MapEventType> {
|
|
|
3219
3222
|
textFitHeight,
|
|
3220
3223
|
sdf,
|
|
3221
3224
|
version,
|
|
3225
|
+
isWebGLImage,
|
|
3222
3226
|
userImage
|
|
3223
3227
|
};
|
|
3224
3228
|
}
|
|
@@ -3267,8 +3271,13 @@ export class Map extends Evented<MapEventType> {
|
|
|
3267
3271
|
'The width and height of the updated image must be that same as the previous version of the image')));
|
|
3268
3272
|
}
|
|
3269
3273
|
|
|
3270
|
-
|
|
3271
|
-
existingImage.
|
|
3274
|
+
existingImage.isWebGLImage = isStyleImageWebGLData(data);
|
|
3275
|
+
if (existingImage.isWebGLImage) {
|
|
3276
|
+
existingImage.userImage = image as StyleImageInterface;
|
|
3277
|
+
} else {
|
|
3278
|
+
const copy = !(image instanceof HTMLImageElement || isImageBitmap(image));
|
|
3279
|
+
existingImage.data.replace(data as Uint8Array | Uint8ClampedArray, copy);
|
|
3280
|
+
}
|
|
3272
3281
|
|
|
3273
3282
|
this.style.updateImage(id, existingImage);
|
|
3274
3283
|
return this;
|
|
@@ -4146,6 +4155,11 @@ export class Map extends Evented<MapEventType> {
|
|
|
4146
4155
|
|
|
4147
4156
|
if (this._lostContextStyle.images && this.style) {
|
|
4148
4157
|
this.style.imageManager.images = this._lostContextStyle.images;
|
|
4158
|
+
// The atlas textures died with the old context, so images that render themselves with WebGL owe every atlas a fresh render.
|
|
4159
|
+
for (const id in this._lostContextStyle.images) {
|
|
4160
|
+
const image = this._lostContextStyle.images[id];
|
|
4161
|
+
if (image.isWebGLImage) this.style.imageManager.updateImage(id, image, false);
|
|
4162
|
+
}
|
|
4149
4163
|
}
|
|
4150
4164
|
|
|
4151
4165
|
this._lostContextStyle = {style: null, images: null};
|
|
@@ -36,8 +36,8 @@ describe('Map cross-window support', () => {
|
|
|
36
36
|
expect(iframeDocument).not.toBe(window.document);
|
|
37
37
|
expect(iframeWindow).not.toBe(window);
|
|
38
38
|
|
|
39
|
-
expect(container
|
|
40
|
-
expect(container
|
|
39
|
+
expect(container).not.toBeInstanceOf( HTMLElement);
|
|
40
|
+
expect(container).toBeInstanceOf(iframeDocument.defaultView.HTMLElement);
|
|
41
41
|
|
|
42
42
|
const map = new Map({
|
|
43
43
|
container,
|
|
@@ -3,9 +3,9 @@ import simulate from '../../../test/unit/lib/simulate_interaction.ts';
|
|
|
3
3
|
import {type StyleLayer} from '../../style/style_layer.ts';
|
|
4
4
|
import {createMap, beforeMapTest, createStyle, sleep, createTerrain} from '../../util/test/util.ts';
|
|
5
5
|
import {type MapGeoJSONFeature} from '../../util/vectortile_to_geojson.ts';
|
|
6
|
-
import {type MapLibreEvent} from '../events.ts';
|
|
6
|
+
import {type MapLibreEvent, MapSourceDataEvent} from '../events.ts';
|
|
7
7
|
import {Map} from '../map.ts';
|
|
8
|
-
import {
|
|
8
|
+
import {ErrorEvent} from '../../util/evented.ts';
|
|
9
9
|
import {type StyleSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
10
10
|
|
|
11
11
|
type IsAny<T> = 0 extends T & 1 ? T : never;
|
|
@@ -980,7 +980,7 @@ describe('map events', () => {
|
|
|
980
980
|
test('fires sourcedataabort event on dataabort event', async () => {
|
|
981
981
|
const map = createMap();
|
|
982
982
|
const sourcePromise = map.once('sourcedataabort');
|
|
983
|
-
map.fire(new
|
|
983
|
+
map.fire(new MapSourceDataEvent('dataabort'));
|
|
984
984
|
await expect(sourcePromise).resolves.toBeDefined();
|
|
985
985
|
});
|
|
986
986
|
|
|
@@ -95,6 +95,32 @@ test('map fires `styleimagemissing` when missing style image resolver returns no
|
|
|
95
95
|
expect(map.hasImage(id)).toBeFalsy();
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
+
test('map preserves resolved images when missing style image resolver rejects another image', async () => {
|
|
99
|
+
const map = createMap();
|
|
100
|
+
|
|
101
|
+
const resolvedId = 'resolved-style-image';
|
|
102
|
+
const rejectedId = 'rejected-style-image';
|
|
103
|
+
const sampleImage = {width: 2, height: 1, data: new Uint8Array(8)};
|
|
104
|
+
const missingImageEventSpy = vi.fn();
|
|
105
|
+
|
|
106
|
+
map.setMissingStyleImageResolver(async (imageId) => {
|
|
107
|
+
if (imageId === rejectedId) {
|
|
108
|
+
throw new Error('Failed to resolve image');
|
|
109
|
+
}
|
|
110
|
+
map.addImage(imageId, sampleImage);
|
|
111
|
+
});
|
|
112
|
+
map.on('styleimagemissing', missingImageEventSpy);
|
|
113
|
+
|
|
114
|
+
const generatedImages = await map.style.imageManager.getImages([resolvedId, rejectedId]);
|
|
115
|
+
|
|
116
|
+
expect(generatedImages[resolvedId].data.width).toEqual(sampleImage.width);
|
|
117
|
+
expect(generatedImages[rejectedId]).toBeUndefined();
|
|
118
|
+
expect(missingImageEventSpy).toHaveBeenCalledTimes(1);
|
|
119
|
+
expect(missingImageEventSpy.mock.calls[0][0].id).toBe(rejectedId);
|
|
120
|
+
expect(map.hasImage(resolvedId)).toBeTruthy();
|
|
121
|
+
expect(map.hasImage(rejectedId)).toBeFalsy();
|
|
122
|
+
});
|
|
123
|
+
|
|
98
124
|
test('map keeps missing style image resolver after replacing the style', async () => {
|
|
99
125
|
const map = createMap();
|
|
100
126
|
|
|
@@ -215,6 +241,35 @@ test('map getImage matches addImage, StyleImageInterface SDF', () => {
|
|
|
215
241
|
expect(gotImage.sdf).toBe(true);
|
|
216
242
|
});
|
|
217
243
|
|
|
244
|
+
test('map addImage packs a placeholder for a WebGL image, which brings its own pixels', () => {
|
|
245
|
+
const map = createMap();
|
|
246
|
+
const id = 'add-get-webgl-style-image';
|
|
247
|
+
const onAdd = vi.fn();
|
|
248
|
+
const inputImage: StyleImageInterface = {width: 3, height: 2, data: {renderWithWebGL: vi.fn()}, onAdd};
|
|
249
|
+
|
|
250
|
+
map.addImage(id, inputImage);
|
|
251
|
+
|
|
252
|
+
const gotImage = map.getImage(id);
|
|
253
|
+
expect(gotImage.data.width).toBe(3);
|
|
254
|
+
expect(gotImage.data.height).toBe(2);
|
|
255
|
+
expect([...gotImage.data.data]).toEqual(new Array(3 * 2 * 4).fill(0));
|
|
256
|
+
expect(gotImage.isWebGLImage).toBe(true);
|
|
257
|
+
expect(gotImage.userImage).toBe(inputImage);
|
|
258
|
+
expect(onAdd).toHaveBeenCalledWith(map, id);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('map updateImage swaps a WebGL image\'s renderWithWebGL callback instead of copying pixels', () => {
|
|
262
|
+
const map = createMap();
|
|
263
|
+
const replacement: StyleImageInterface = {width: 1, height: 1, data: {renderWithWebGL: vi.fn()}};
|
|
264
|
+
|
|
265
|
+
map.addImage('webgl', {width: 1, height: 1, data: {renderWithWebGL: vi.fn()}});
|
|
266
|
+
const version = map.getImage('webgl').version;
|
|
267
|
+
map.updateImage('webgl', replacement);
|
|
268
|
+
|
|
269
|
+
expect(map.getImage('webgl').userImage).toBe(replacement);
|
|
270
|
+
expect(map.getImage('webgl').version).toBe(version + 1);
|
|
271
|
+
});
|
|
272
|
+
|
|
218
273
|
test('map does not fire `styleimagemissing` for empty icon values', async () => {
|
|
219
274
|
const map = createMap();
|
|
220
275
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {describe, beforeEach, afterEach, test, expect, vi} from 'vitest';
|
|
2
2
|
import {Map, type MapOptions} from '../map.ts';
|
|
3
3
|
import {createMap, beforeMapTest, createStyle, createStyleSource, sleep} from '../../util/test/util.ts';
|
|
4
|
-
import {
|
|
4
|
+
import {ErrorEvent} from '../../util/evented.ts';
|
|
5
|
+
import {MapSourceDataEvent, MapStyleDataEvent} from '../events.ts';
|
|
5
6
|
import {fixedLngLat, fixedNum} from '../../../test/unit/lib/fixed.ts';
|
|
6
7
|
import {extend} from '../../util/util.ts';
|
|
7
8
|
import {fakeServer, type FakeServer} from 'nise';
|
|
@@ -42,9 +43,9 @@ describe('setStyle', () => {
|
|
|
42
43
|
map.on('data', recordEvent);
|
|
43
44
|
map.on('dataloading', recordEvent);
|
|
44
45
|
|
|
45
|
-
map.style.fire(new
|
|
46
|
-
map.style.fire(new
|
|
47
|
-
map.style.fire(new
|
|
46
|
+
map.style.fire(new ErrorEvent(new Error('test')));
|
|
47
|
+
map.style.fire(new MapSourceDataEvent('data'));
|
|
48
|
+
map.style.fire(new MapSourceDataEvent('dataloading'));
|
|
48
49
|
|
|
49
50
|
expect(events).toEqual([
|
|
50
51
|
'error',
|
|
@@ -65,10 +66,10 @@ describe('setStyle', () => {
|
|
|
65
66
|
map.on('sourcedata', recordEvent);
|
|
66
67
|
map.on('sourcedataloading', recordEvent);
|
|
67
68
|
|
|
68
|
-
map.style.fire(new
|
|
69
|
-
map.style.fire(new
|
|
70
|
-
map.style.fire(new
|
|
71
|
-
map.style.fire(new
|
|
69
|
+
map.style.fire(new MapStyleDataEvent('data'));
|
|
70
|
+
map.style.fire(new MapStyleDataEvent('dataloading'));
|
|
71
|
+
map.style.fire(new MapSourceDataEvent('data'));
|
|
72
|
+
map.style.fire(new MapSourceDataEvent('dataloading'));
|
|
72
73
|
|
|
73
74
|
expect(events).toEqual([
|
|
74
75
|
'styledata',
|
|
@@ -93,6 +93,36 @@ test('does not fire "webglcontextrestored" after remove has been called', async
|
|
|
93
93
|
expect(spy).not.toHaveBeenCalled();
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
+
test('a WebGL style image is told to release its GPU resources on context loss, and survives it', async () => {
|
|
97
|
+
const map = createMap();
|
|
98
|
+
const canvas = map.getCanvas();
|
|
99
|
+
await map.once('load');
|
|
100
|
+
|
|
101
|
+
const userImage = {
|
|
102
|
+
width: 2, height: 2,
|
|
103
|
+
data: {renderWithWebGL: vi.fn()},
|
|
104
|
+
onRemove: vi.fn()
|
|
105
|
+
};
|
|
106
|
+
map.addImage('gpu-image', userImage);
|
|
107
|
+
const versionBeforeContextLoss = map.getImage('gpu-image').version;
|
|
108
|
+
|
|
109
|
+
const contextLostPromise = map.once('webglcontextlost');
|
|
110
|
+
canvas.dispatchEvent(new window.Event('webglcontextlost'));
|
|
111
|
+
await contextLostPromise;
|
|
112
|
+
|
|
113
|
+
expect(userImage.onRemove).toHaveBeenCalled();
|
|
114
|
+
|
|
115
|
+
const contextRestoredPromise = map.once('webglcontextrestored');
|
|
116
|
+
canvas.dispatchEvent(new window.Event('webglcontextrestored'));
|
|
117
|
+
await contextRestoredPromise;
|
|
118
|
+
|
|
119
|
+
expect(map.hasImage('gpu-image')).toBe(true);
|
|
120
|
+
expect(map.getImage('gpu-image').userImage).toBe(userImage);
|
|
121
|
+
expect(map.getImage('gpu-image').version).toBe(versionBeforeContextLoss + 1);
|
|
122
|
+
|
|
123
|
+
map.remove();
|
|
124
|
+
});
|
|
125
|
+
|
|
96
126
|
test('WebGL2 context creation error fires ErrorEvent with structured GPUInitializationError', () => {
|
|
97
127
|
HTMLCanvasElement.prototype.getContext = function (type: string) {
|
|
98
128
|
if (type === 'webgl2') {
|
package/src/ui/marker.test.ts
CHANGED
|
@@ -96,20 +96,20 @@ describe('marker', () => {
|
|
|
96
96
|
.addTo(map);
|
|
97
97
|
|
|
98
98
|
const markerElement = marker.getElement();
|
|
99
|
-
expect(markerElement.classList.
|
|
100
|
-
expect(markerElement.classList.
|
|
99
|
+
expect(markerElement.classList).toContain('some');
|
|
100
|
+
expect(markerElement.classList).toContain('classes');
|
|
101
101
|
|
|
102
102
|
marker.addClassName('addedClass');
|
|
103
|
-
expect(markerElement.classList.
|
|
103
|
+
expect(markerElement.classList).toContain('addedClass');
|
|
104
104
|
|
|
105
105
|
marker.removeClassName('addedClass');
|
|
106
|
-
expect(
|
|
106
|
+
expect(markerElement.classList).not.toContain('addedClass');
|
|
107
107
|
|
|
108
108
|
marker.toggleClassName('toggle');
|
|
109
|
-
expect(markerElement.classList.
|
|
109
|
+
expect(markerElement.classList).toContain('toggle');
|
|
110
110
|
|
|
111
111
|
marker.toggleClassName('toggle');
|
|
112
|
-
expect(
|
|
112
|
+
expect(markerElement.classList).not.toContain('toggle');
|
|
113
113
|
|
|
114
114
|
expect(() => marker.addClassName('should throw exception')).toThrow(window.DOMException);
|
|
115
115
|
expect(() => marker.removeClassName('should throw exception')).toThrow(window.DOMException);
|
|
@@ -123,19 +123,19 @@ describe('marker', () => {
|
|
|
123
123
|
test('Marker provides LngLat accessors', () => {
|
|
124
124
|
expect(new Marker().getLngLat()).toBeUndefined();
|
|
125
125
|
|
|
126
|
-
expect(new Marker().setLngLat([1, 2]).getLngLat()
|
|
126
|
+
expect(new Marker().setLngLat([1, 2]).getLngLat()).toBeInstanceOf(LngLat);
|
|
127
127
|
expect(new Marker().setLngLat([1, 2]).getLngLat()).toEqual(new LngLat(1, 2));
|
|
128
128
|
|
|
129
|
-
expect(new Marker().setLngLat(new LngLat(1, 2)).getLngLat()
|
|
129
|
+
expect(new Marker().setLngLat(new LngLat(1, 2)).getLngLat()).toBeInstanceOf(LngLat);
|
|
130
130
|
expect(new Marker().setLngLat(new LngLat(1, 2)).getLngLat()).toEqual(new LngLat(1, 2));
|
|
131
131
|
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
test('Marker provides offset accessors', () => {
|
|
135
|
-
expect(new Marker().setOffset([1, 2]).getOffset()
|
|
135
|
+
expect(new Marker().setOffset([1, 2]).getOffset()).toBeInstanceOf(Point);
|
|
136
136
|
expect(new Marker().setOffset([1, 2]).getOffset()).toEqual(new Point(1, 2));
|
|
137
137
|
|
|
138
|
-
expect(new Marker().setOffset(new Point(1, 2)).getOffset()
|
|
138
|
+
expect(new Marker().setOffset(new Point(1, 2)).getOffset()).toBeInstanceOf(Point);
|
|
139
139
|
expect(new Marker().setOffset(new Point(1, 2)).getOffset()).toEqual(new Point(1, 2));
|
|
140
140
|
|
|
141
141
|
});
|
|
@@ -343,7 +343,7 @@ describe('marker', () => {
|
|
|
343
343
|
.setLngLat([0, 0])
|
|
344
344
|
.addTo(map);
|
|
345
345
|
|
|
346
|
-
expect(marker.getElement().classList.
|
|
346
|
+
expect(marker.getElement().classList).toContain('maplibregl-marker-anchor-center');
|
|
347
347
|
expect(marker.getElement().style.transform).toMatch(/translate\(-50%,-50%\)/);
|
|
348
348
|
|
|
349
349
|
map.remove();
|
|
@@ -355,7 +355,7 @@ describe('marker', () => {
|
|
|
355
355
|
.setLngLat([0, 0])
|
|
356
356
|
.addTo(map);
|
|
357
357
|
|
|
358
|
-
expect(marker.getElement().classList.
|
|
358
|
+
expect(marker.getElement().classList).toContain('maplibregl-marker-anchor-top');
|
|
359
359
|
expect(marker.getElement().style.transform).toMatch(/translate\(-50%,0\)/);
|
|
360
360
|
|
|
361
361
|
map.remove();
|
|
@@ -407,51 +407,35 @@ describe('marker', () => {
|
|
|
407
407
|
Object.defineProperty(marker.getPopup()._container, 'offsetHeight', {value: 100});
|
|
408
408
|
|
|
409
409
|
// marker should default to above since it has enough space
|
|
410
|
-
expect(
|
|
411
|
-
marker.getPopup()._container.classList.contains('maplibregl-popup-anchor-bottom')
|
|
412
|
-
).toBeTruthy();
|
|
410
|
+
expect(marker.getPopup()._container.classList).toContain('maplibregl-popup-anchor-bottom');
|
|
413
411
|
|
|
414
412
|
// move marker to the top forcing the popup to below
|
|
415
413
|
marker.setLngLat(map.unproject([mapHeight / 2, markerTop]));
|
|
416
|
-
expect(
|
|
417
|
-
marker.getPopup()._container.classList.contains('maplibregl-popup-anchor-top')
|
|
418
|
-
).toBeTruthy();
|
|
414
|
+
expect(marker.getPopup()._container.classList).toContain('maplibregl-popup-anchor-top');
|
|
419
415
|
|
|
420
416
|
// move marker to the right forcing the popup to the left
|
|
421
417
|
marker.setLngLat(map.unproject([mapHeight - markerRight, mapHeight / 2]));
|
|
422
|
-
expect(
|
|
423
|
-
marker.getPopup()._container.classList.contains('maplibregl-popup-anchor-right')
|
|
424
|
-
).toBeTruthy();
|
|
418
|
+
expect(marker.getPopup()._container.classList).toContain('maplibregl-popup-anchor-right');
|
|
425
419
|
|
|
426
420
|
// move marker to the left forcing the popup to the right
|
|
427
421
|
marker.setLngLat(map.unproject([markerRight, mapHeight / 2]));
|
|
428
|
-
expect(
|
|
429
|
-
marker.getPopup()._container.classList.contains('maplibregl-popup-anchor-left')
|
|
430
|
-
).toBeTruthy();
|
|
422
|
+
expect(marker.getPopup()._container.classList).toContain('maplibregl-popup-anchor-left');
|
|
431
423
|
|
|
432
424
|
// move marker to the top left forcing the popup to the bottom right
|
|
433
425
|
marker.setLngLat(map.unproject([markerRight, markerTop]));
|
|
434
|
-
expect(
|
|
435
|
-
marker.getPopup()._container.classList.contains('maplibregl-popup-anchor-top-left')
|
|
436
|
-
).toBeTruthy();
|
|
426
|
+
expect(marker.getPopup()._container.classList).toContain('maplibregl-popup-anchor-top-left');
|
|
437
427
|
|
|
438
428
|
// move marker to the top right forcing the popup to the bottom left
|
|
439
429
|
marker.setLngLat(map.unproject([mapHeight - markerRight, markerTop]));
|
|
440
|
-
expect(
|
|
441
|
-
marker.getPopup()._container.classList.contains('maplibregl-popup-anchor-top-right')
|
|
442
|
-
).toBeTruthy();
|
|
430
|
+
expect(marker.getPopup()._container.classList).toContain('maplibregl-popup-anchor-top-right');
|
|
443
431
|
|
|
444
432
|
// move marker to the bottom left forcing the popup to the top right
|
|
445
433
|
marker.setLngLat(map.unproject([markerRight, mapHeight]));
|
|
446
|
-
expect(
|
|
447
|
-
marker.getPopup()._container.classList.contains('maplibregl-popup-anchor-bottom-left')
|
|
448
|
-
).toBeTruthy();
|
|
434
|
+
expect(marker.getPopup()._container.classList).toContain('maplibregl-popup-anchor-bottom-left');
|
|
449
435
|
|
|
450
436
|
// move marker to the bottom right forcing the popup to the top left
|
|
451
437
|
marker.setLngLat(map.unproject([mapHeight - markerRight, mapHeight]));
|
|
452
|
-
expect(
|
|
453
|
-
marker.getPopup()._container.classList.contains('maplibregl-popup-anchor-bottom-right')
|
|
454
|
-
).toBeTruthy();
|
|
438
|
+
expect(marker.getPopup()._container.classList).toContain('maplibregl-popup-anchor-bottom-right');
|
|
455
439
|
|
|
456
440
|
map.remove();
|
|
457
441
|
});
|
|
@@ -828,6 +812,157 @@ describe('marker', () => {
|
|
|
828
812
|
map.remove();
|
|
829
813
|
});
|
|
830
814
|
|
|
815
|
+
test('Marker with draggable:true is keyboard-focusable, and stops being focusable when dragging is disabled', () => {
|
|
816
|
+
const map = createMap();
|
|
817
|
+
const marker = new Marker({draggable: true}).setLngLat([0, 0]).addTo(map);
|
|
818
|
+
expect(marker.getElement().getAttribute('tabindex')).toBe('0');
|
|
819
|
+
marker.setDraggable(false);
|
|
820
|
+
expect(marker.getElement().hasAttribute('tabindex')).toBe(false);
|
|
821
|
+
map.remove();
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
test('Marker.setDraggable preserves an application-provided tabindex', () => {
|
|
825
|
+
const marker = new Marker();
|
|
826
|
+
marker.getElement().setAttribute('tabindex', '5');
|
|
827
|
+
|
|
828
|
+
marker.setDraggable(true);
|
|
829
|
+
expect(marker.getElement().getAttribute('tabindex')).toBe('5');
|
|
830
|
+
|
|
831
|
+
marker.setDraggable(false);
|
|
832
|
+
expect(marker.getElement().getAttribute('tabindex')).toBe('5');
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
test('Marker keeps a tabindex the application overrode after the marker managed it', () => {
|
|
836
|
+
const marker = new Marker({draggable: true});
|
|
837
|
+
marker.setDraggable(true);
|
|
838
|
+
expect(marker.getElement().getAttribute('tabindex')).toBe('0');
|
|
839
|
+
|
|
840
|
+
marker.getElement().setAttribute('tabindex', '5');
|
|
841
|
+
marker.setDraggable(false);
|
|
842
|
+
expect(marker.getElement().getAttribute('tabindex')).toBe('5');
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
test('Marker starts a fresh keyboard gesture after being removed mid-drag and re-added', () => {
|
|
846
|
+
const map = createMap();
|
|
847
|
+
const marker = new Marker({draggable: true}).setLngLat([0, 0]).addTo(map);
|
|
848
|
+
const el = marker.getElement();
|
|
849
|
+
const dragstart = vi.fn();
|
|
850
|
+
marker.on('dragstart', dragstart);
|
|
851
|
+
|
|
852
|
+
el.dispatchEvent(new KeyboardEvent('keydown', {key: 'ArrowRight', bubbles: true, cancelable: true}));
|
|
853
|
+
expect(dragstart).toHaveBeenCalledTimes(1);
|
|
854
|
+
|
|
855
|
+
// removed mid-gesture (no keyup), then re-added
|
|
856
|
+
marker.remove();
|
|
857
|
+
marker.addTo(map);
|
|
858
|
+
|
|
859
|
+
el.dispatchEvent(new KeyboardEvent('keydown', {key: 'ArrowRight', bubbles: true, cancelable: true}));
|
|
860
|
+
expect(dragstart).toHaveBeenCalledTimes(2);
|
|
861
|
+
map.remove();
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
test('Marker keeps its tabindex while either dragging or a popup keeps it interactive', () => {
|
|
865
|
+
const marker = new Marker({draggable: true}).setPopup(new Popup());
|
|
866
|
+
|
|
867
|
+
marker.setPopup();
|
|
868
|
+
expect(marker.getElement().getAttribute('tabindex')).toBe('0');
|
|
869
|
+
|
|
870
|
+
marker.setDraggable(false);
|
|
871
|
+
expect(marker.getElement().hasAttribute('tabindex')).toBe(false);
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
test('Marker with a custom element leaves focusability and keyboard handling to the application', () => {
|
|
875
|
+
const map = createMap();
|
|
876
|
+
const element = window.document.createElement('div');
|
|
877
|
+
const marker = new Marker({draggable: true, element}).setLngLat([0, 0]).addTo(map);
|
|
878
|
+
const startLngLat = marker.getLngLat();
|
|
879
|
+
const event = new KeyboardEvent('keydown', {key: 'ArrowRight', bubbles: true, cancelable: true});
|
|
880
|
+
|
|
881
|
+
element.dispatchEvent(event);
|
|
882
|
+
|
|
883
|
+
expect(element.hasAttribute('tabindex')).toBe(false);
|
|
884
|
+
expect(marker.getLngLat()).toEqual(startLngLat);
|
|
885
|
+
expect(event.defaultPrevented).toBe(false);
|
|
886
|
+
map.remove();
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
test('Marker with draggable:true moves with arrow keys and fires drag events', () => {
|
|
890
|
+
const map = createMap();
|
|
891
|
+
const marker = new Marker({draggable: true}).setLngLat([0, 0]).addTo(map);
|
|
892
|
+
const el = marker.getElement();
|
|
893
|
+
const startPos = map.project(marker.getLngLat());
|
|
894
|
+
const events: string[] = [];
|
|
895
|
+
const positionsAtDragstart: Point[] = [];
|
|
896
|
+
marker.on('dragstart', () => {
|
|
897
|
+
events.push('dragstart');
|
|
898
|
+
positionsAtDragstart.push(map.project(marker.getLngLat()));
|
|
899
|
+
});
|
|
900
|
+
marker.on('drag', () => events.push('drag'));
|
|
901
|
+
marker.on('dragend', () => events.push('dragend'));
|
|
902
|
+
|
|
903
|
+
const ignoredEvent = new KeyboardEvent('keydown', {key: 'Enter', bubbles: true, cancelable: true});
|
|
904
|
+
el.dispatchEvent(ignoredEvent);
|
|
905
|
+
expect(map.project(marker.getLngLat())).toEqual(startPos);
|
|
906
|
+
expect(ignoredEvent.defaultPrevented).toBe(false);
|
|
907
|
+
|
|
908
|
+
const nestedElement = window.document.createElement('button');
|
|
909
|
+
el.attachShadow({mode: 'open'}).appendChild(nestedElement);
|
|
910
|
+
const nestedEvent = new KeyboardEvent('keydown', {key: 'ArrowRight', bubbles: true, composed: true, cancelable: true});
|
|
911
|
+
nestedElement.dispatchEvent(nestedEvent);
|
|
912
|
+
expect(map.project(marker.getLngLat())).toEqual(startPos);
|
|
913
|
+
expect(nestedEvent.defaultPrevented).toBe(false);
|
|
914
|
+
|
|
915
|
+
const easeTo = vi.spyOn(map, 'easeTo');
|
|
916
|
+
const containerHandler = vi.fn();
|
|
917
|
+
map.getCanvasContainer().addEventListener('keydown', containerHandler);
|
|
918
|
+
const event = new KeyboardEvent('keydown', {key: 'ArrowRight', bubbles: true, cancelable: true});
|
|
919
|
+
el.dispatchEvent(event);
|
|
920
|
+
|
|
921
|
+
expect(map.project(marker.getLngLat()).x).toBeCloseTo(startPos.x + 1);
|
|
922
|
+
expect(map.project(marker.getLngLat()).y).toBeCloseTo(startPos.y);
|
|
923
|
+
expect(event.defaultPrevented).toBe(true);
|
|
924
|
+
expect(containerHandler).not.toHaveBeenCalled();
|
|
925
|
+
expect(easeTo).not.toHaveBeenCalled();
|
|
926
|
+
expect(events).toEqual(['dragstart', 'drag']);
|
|
927
|
+
expect(positionsAtDragstart[0].x).toBeCloseTo(startPos.x + 1);
|
|
928
|
+
|
|
929
|
+
el.dispatchEvent(new KeyboardEvent('keydown', {key: 'ArrowRight', repeat: true, bubbles: true, cancelable: true}));
|
|
930
|
+
el.dispatchEvent(new KeyboardEvent('keyup', {key: 'ArrowRight', bubbles: true, cancelable: true}));
|
|
931
|
+
expect(events).toEqual(['dragstart', 'drag', 'drag', 'dragend']);
|
|
932
|
+
|
|
933
|
+
el.dispatchEvent(new KeyboardEvent('keydown', {key: 'ArrowRight', bubbles: true, cancelable: true}));
|
|
934
|
+
el.dispatchEvent(new FocusEvent('blur'));
|
|
935
|
+
expect(events).toEqual(['dragstart', 'drag', 'drag', 'dragend', 'dragstart', 'drag', 'dragend']);
|
|
936
|
+
|
|
937
|
+
el.dispatchEvent(new KeyboardEvent('keyup', {key: 'ArrowRight', bubbles: true, cancelable: true}));
|
|
938
|
+
expect(events).toEqual(['dragstart', 'drag', 'drag', 'dragend', 'dragstart', 'drag', 'dragend']);
|
|
939
|
+
map.remove();
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
test('Marker with draggable:true moves ten pixels per Shift+arrow keydown', () => {
|
|
943
|
+
const map = createMap();
|
|
944
|
+
const marker = new Marker({draggable: true}).setLngLat([0, 0]).addTo(map);
|
|
945
|
+
const startPos = map.project(marker.getLngLat());
|
|
946
|
+
|
|
947
|
+
marker.getElement().dispatchEvent(new KeyboardEvent('keydown', {key: 'ArrowDown', shiftKey: true, bubbles: true, cancelable: true}));
|
|
948
|
+
|
|
949
|
+
const endPos = map.project(marker.getLngLat());
|
|
950
|
+
expect(endPos.x).toBeCloseTo(startPos.x);
|
|
951
|
+
expect(endPos.y).toBeCloseTo(startPos.y + 10);
|
|
952
|
+
map.remove();
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
test('Marker stops responding to arrow keys after dragging is disabled', () => {
|
|
956
|
+
const map = createMap();
|
|
957
|
+
const marker = new Marker({draggable: true}).setLngLat([0, 0]).addTo(map).setDraggable(false);
|
|
958
|
+
const startLngLat = marker.getLngLat();
|
|
959
|
+
|
|
960
|
+
marker.getElement().dispatchEvent(new KeyboardEvent('keydown', {key: 'ArrowRight', bubbles: true, cancelable: true}));
|
|
961
|
+
|
|
962
|
+
expect(marker.getLngLat()).toEqual(startLngLat);
|
|
963
|
+
map.remove();
|
|
964
|
+
});
|
|
965
|
+
|
|
831
966
|
test('Marker with draggable:false does not move to new position in response to a mouse-triggered drag', () => {
|
|
832
967
|
const map = createMap();
|
|
833
968
|
const marker = new Marker({})
|
|
@@ -1352,7 +1487,7 @@ describe('marker', () => {
|
|
|
1352
1487
|
map.fire('terrain');
|
|
1353
1488
|
await sleep(100);
|
|
1354
1489
|
|
|
1355
|
-
expect(marker.getElement().classList.
|
|
1490
|
+
expect(marker.getElement().classList).toContain('maplibregl-marker-covered');
|
|
1356
1491
|
map.remove();
|
|
1357
1492
|
});
|
|
1358
1493
|
|
|
@@ -1368,13 +1503,13 @@ describe('marker', () => {
|
|
|
1368
1503
|
map.fire('terrain');
|
|
1369
1504
|
await sleep(100);
|
|
1370
1505
|
|
|
1371
|
-
expect(marker.getElement().classList.
|
|
1506
|
+
expect(marker.getElement().classList).toContain('maplibregl-marker-covered');
|
|
1372
1507
|
|
|
1373
1508
|
map.terrain.depthAtPoint = () => .95; // Terrain no longer blocking marker
|
|
1374
1509
|
map.fire('moveend');
|
|
1375
1510
|
await sleep(100);
|
|
1376
1511
|
|
|
1377
|
-
expect(marker.getElement().classList.
|
|
1512
|
+
expect(marker.getElement().classList).not.toContain('maplibregl-marker-covered');
|
|
1378
1513
|
map.remove();
|
|
1379
1514
|
});
|
|
1380
1515
|
|
|
@@ -1389,7 +1524,7 @@ describe('marker', () => {
|
|
|
1389
1524
|
map.setProjection({type: 'globe'});
|
|
1390
1525
|
await sleep(100);
|
|
1391
1526
|
|
|
1392
|
-
expect(marker.getElement().classList.
|
|
1527
|
+
expect(marker.getElement().classList).toContain('maplibregl-marker-covered');
|
|
1393
1528
|
map.remove();
|
|
1394
1529
|
});
|
|
1395
1530
|
|
|
@@ -1404,12 +1539,12 @@ describe('marker', () => {
|
|
|
1404
1539
|
map.setProjection({type: 'globe'});
|
|
1405
1540
|
await sleep(100);
|
|
1406
1541
|
|
|
1407
|
-
expect(marker.getElement().classList.
|
|
1542
|
+
expect(marker.getElement().classList).toContain('maplibregl-marker-covered');
|
|
1408
1543
|
|
|
1409
1544
|
marker.setLngLat([0, 0]);
|
|
1410
1545
|
await sleep(100);
|
|
1411
1546
|
|
|
1412
|
-
expect(marker.getElement().classList.
|
|
1547
|
+
expect(marker.getElement().classList).not.toContain('maplibregl-marker-covered');
|
|
1413
1548
|
map.remove();
|
|
1414
1549
|
});
|
|
1415
1550
|
});
|