@defra/interactive-map 0.0.22-alpha → 0.0.24-alpha
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/assets/images/light-raster-thumb.jpg +0 -0
- package/assets/images/outdoor-raster-thumb.jpg +0 -0
- package/assets/images/road-raster-thumb.jpg +0 -0
- package/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/im-shell.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api/button-definition.md +39 -0
- package/docs/api/context.md +4 -4
- package/docs/api/map-style-config.md +11 -2
- package/docs/api/marker-config.md +38 -7
- package/docs/api/panel-definition.md +7 -3
- package/docs/api/symbol-config.md +10 -26
- package/docs/api/symbol-registry.md +28 -13
- package/docs/api.md +24 -2
- package/docs/assets/css/{docusaurus.css → docusaurus.scss} +18 -12
- package/docs/assets/images/toggle-marker-label.jpg +0 -0
- package/docs/examples/index.mdx +12 -0
- package/docs/examples/toggle-marker-label.mdx +89 -0
- package/docs/plugins/interact.md +4 -11
- package/docs/plugins/search.md +70 -0
- package/docusaurus.config.cjs +2 -1
- package/package.json +23 -9
- package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +1 -1
- package/plugins/beta/datasets/dist/css/index.css +2 -2
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +1 -1
- package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/maplibre/index.js +1 -1
- package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +5 -5
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +24 -29
- package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +2 -2
- package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +3 -3
- package/plugins/beta/datasets/src/components/KeySvg.jsx +2 -2
- package/plugins/beta/datasets/src/components/KeySvgPattern.jsx +1 -2
- package/plugins/beta/datasets/src/components/svgProperties.js +2 -2
- package/plugins/beta/datasets/src/panels/Key.module.scss +2 -2
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/css/index.css +1 -21
- package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/src/api/editFeature.js +9 -4
- package/plugins/beta/draw-ml/src/modes/createDrawMode.js +11 -3
- package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/index.js +1 -1
- package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -1
- package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -1
- package/plugins/beta/use-location/src/manifest.js +1 -0
- package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/index.js +1 -1
- package/plugins/interact/src/InteractInit.jsx +23 -55
- package/plugins/interact/src/InteractInit.test.js +58 -80
- package/plugins/interact/src/defaults.js +1 -3
- package/plugins/interact/src/hooks/useAttachEvents.js +46 -0
- package/plugins/interact/src/hooks/useAttachEvents.test.js +68 -0
- package/plugins/interact/src/hooks/useCrossHairVisibility.js +47 -0
- package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +54 -0
- package/plugins/interact/src/hooks/useHighlightSync.js +30 -8
- package/plugins/interact/src/hooks/useHighlightSync.test.js +127 -80
- package/plugins/interact/src/hooks/useInteractionHandlers.js +18 -17
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +22 -61
- package/plugins/interact/src/hooks/useMapItemList.js +187 -0
- package/plugins/interact/src/hooks/useMapItemList.test.js +584 -0
- package/plugins/interact/src/index.js +0 -2
- package/plugins/interact/src/index.test.js +0 -2
- package/plugins/interact/src/manifest.js +37 -6
- package/plugins/interact/src/reducer.js +8 -4
- package/plugins/interact/src/reducer.test.js +18 -3
- package/plugins/interact/src/utils/buildStylesMap.js +21 -10
- package/plugins/interact/src/utils/buildStylesMap.test.js +29 -29
- package/plugins/interact/src/utils/spatial.js +0 -15
- package/plugins/interact/src/utils/spatial.test.js +1 -13
- package/plugins/search/dist/css/index.css +1 -1
- package/plugins/search/dist/esm/im-search-plugin.js +1 -1
- package/plugins/search/dist/esm/index.js +1 -1
- package/plugins/search/dist/umd/im-search-plugin.js +1 -1
- package/plugins/search/dist/umd/index.js +1 -1
- package/plugins/search/src/Search.jsx +2 -1
- package/plugins/search/src/components/CloseButton/CloseButton.module.scss +2 -0
- package/plugins/search/src/components/Form/Form.jsx +11 -7
- package/plugins/search/src/components/Form/Form.test.jsx +23 -1
- package/plugins/search/src/components/OpenButton/OpenButton.jsx +1 -1
- package/plugins/search/src/components/OpenButton/OpenButton.module.scss +3 -0
- package/plugins/search/src/datasets.js +2 -2
- package/plugins/search/src/datasets.test.js +1 -1
- package/plugins/search/src/defaults.js +7 -1
- package/plugins/search/src/events/fetchSuggestions.js +13 -10
- package/plugins/search/src/events/fetchSuggestions.test.js +14 -1
- package/plugins/search/src/events/formHandlers.js +2 -4
- package/plugins/search/src/events/formHandlers.test.js +3 -3
- package/plugins/search/src/events/inputHandlers.js +1 -1
- package/plugins/search/src/events/inputHandlers.test.js +1 -1
- package/plugins/search/src/events/suggestionHandlers.js +2 -1
- package/plugins/search/src/events/suggestionHandlers.test.js +7 -1
- package/plugins/search/src/index.js +3 -7
- package/plugins/search/src/reducer.js +10 -5
- package/plugins/search/src/reducer.test.js +2 -1
- package/plugins/search/src/search.scss +1 -0
- package/plugins/search/src/utils/parseOsNamesResults.js +3 -3
- package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
- package/providers/beta/esri/src/esriProvider.js +4 -0
- package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -0
- package/providers/beta/openlayers/dist/esm/index.js +1 -0
- package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +2 -0
- package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js.LICENSE.txt +1 -0
- package/providers/beta/openlayers/dist/umd/index.js +2 -0
- package/providers/beta/openlayers/dist/umd/index.js.LICENSE.txt +1 -0
- package/providers/beta/openlayers/src/appEvents.js +50 -0
- package/providers/beta/openlayers/src/appEvents.test.js +140 -0
- package/providers/beta/openlayers/src/defaults.js +36 -0
- package/providers/beta/openlayers/src/index.js +21 -0
- package/providers/beta/openlayers/src/mapEvents.js +132 -0
- package/providers/beta/openlayers/src/mapEvents.test.js +197 -0
- package/providers/beta/openlayers/src/openlayersProvider.js +234 -0
- package/providers/beta/openlayers/src/openlayersProvider.test.js +331 -0
- package/providers/beta/openlayers/src/utils/spatial.js +142 -0
- package/providers/beta/openlayers/src/utils/spatial.test.js +117 -0
- package/providers/beta/openlayers/src/utils/zoom.js +26 -0
- package/providers/beta/openlayers/src/utils/zoom.test.js +23 -0
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/esm/index.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/index.js +1 -1
- package/providers/maplibre/src/index.js +1 -1
- package/providers/maplibre/src/index.test.js +9 -14
- package/providers/maplibre/src/mapEvents.js +11 -5
- package/providers/maplibre/src/mapEvents.test.js +14 -3
- package/providers/maplibre/src/maplibreProvider.js +18 -11
- package/providers/maplibre/src/maplibreProvider.test.js +43 -36
- package/providers/maplibre/src/utils/highlightFeatures.js +112 -57
- package/providers/maplibre/src/utils/highlightFeatures.test.js +179 -51
- package/providers/maplibre/src/utils/maplibreFixes.js +10 -0
- package/providers/maplibre/src/utils/maplibreFixes.test.js +31 -0
- package/providers/maplibre/src/utils/patternImages.js +22 -19
- package/providers/maplibre/src/utils/patternImages.test.js +146 -167
- package/providers/maplibre/src/utils/rasteriseToImageData.js +1 -4
- package/providers/maplibre/src/utils/rasteriseToImageData.test.js +23 -24
- package/providers/maplibre/src/utils/symbolImages.js +23 -86
- package/providers/maplibre/src/utils/symbolImages.test.js +60 -105
- package/rollup.esm.mjs +14 -3
- package/src/App/components/Actions/Actions.module.scss +1 -0
- package/src/App/components/Actions/Actions.test.jsx +17 -4
- package/src/App/components/Hints/Hints.jsx +52 -0
- package/src/App/components/Hints/Hints.module.scss +37 -0
- package/src/App/components/Hints/Hints.test.jsx +104 -0
- package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +57 -19
- package/src/App/components/KeyboardHelp/KeyboardHelp.module.scss +10 -0
- package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +140 -31
- package/src/App/components/Markers/LabelMarker.jsx +14 -0
- package/src/App/components/Markers/LabelMarker.test.jsx +36 -0
- package/src/App/components/Markers/Markers.jsx +44 -75
- package/src/App/components/Markers/Markers.module.scss +58 -0
- package/src/App/components/Markers/Markers.test.jsx +90 -113
- package/src/App/components/Markers/SymbolLabelMarker.jsx +43 -0
- package/src/App/components/Markers/SymbolLabelMarker.test.jsx +56 -0
- package/src/App/components/Markers/SymbolMarker.jsx +31 -0
- package/src/App/components/Markers/SymbolMarker.test.jsx +72 -0
- package/src/App/components/Panel/Panel.jsx +1 -1
- package/src/App/components/Panel/Panel.module.scss +1 -0
- package/src/App/components/PopupMenu/PopupMenu.test.jsx +117 -1
- package/src/App/components/PopupMenu/usePopupMenu.js +132 -85
- package/src/App/components/Tabs/Tabs.jsx +68 -0
- package/src/App/components/Tabs/Tabs.module.scss +57 -0
- package/src/App/components/Tabs/Tabs.test.jsx +121 -0
- package/src/App/components/Tooltip/Tooltip.module.scss +1 -1
- package/src/App/components/Viewport/Features.jsx +35 -0
- package/src/App/components/Viewport/Features.module.scss +31 -0
- package/src/App/components/Viewport/Features.test.jsx +124 -0
- package/src/App/components/Viewport/MapStatus.jsx +0 -4
- package/src/App/components/Viewport/Viewport.jsx +35 -29
- package/src/App/components/Viewport/Viewport.module.scss +7 -21
- package/src/App/components/Viewport/Viewport.test.jsx +108 -65
- package/src/App/controls/keyboardActions.js +9 -3
- package/src/App/controls/keyboardActions.test.js +122 -87
- package/src/App/controls/keyboardShortcuts.js +1 -14
- package/src/App/hooks/useFeatureFocus.js +199 -0
- package/src/App/hooks/useFeatureFocus.test.js +635 -0
- package/src/App/hooks/useFeatureItems.js +30 -0
- package/src/App/hooks/useFeatureItems.test.js +98 -0
- package/src/App/hooks/useFocusVisible.js +4 -7
- package/src/App/hooks/useFocusVisible.test.js +6 -6
- package/src/App/hooks/useKeyboardHint.js +9 -34
- package/src/App/hooks/useKeyboardHint.test.js +34 -84
- package/src/App/hooks/useKeyboardShortcuts.js +8 -5
- package/src/App/hooks/useKeyboardShortcuts.test.js +16 -12
- package/src/App/hooks/useLayoutMeasurements.js +10 -4
- package/src/App/hooks/useLayoutMeasurements.test.js +30 -1
- package/src/App/hooks/useMarkerCursor.js +33 -0
- package/src/App/hooks/useMarkerCursor.test.js +87 -0
- package/src/App/hooks/useMarkersAPI.js +32 -9
- package/src/App/hooks/useMarkersAPI.test.js +163 -94
- package/src/App/hooks/useModalPanelBehaviour.test.js +2 -1
- package/src/App/layout/Layout.jsx +2 -0
- package/src/App/layout/Layout.test.jsx +3 -0
- package/src/App/registry/keyboardShortcutRegistry.js +3 -2
- package/src/App/registry/keyboardShortcutRegistry.test.js +6 -8
- package/src/App/renderer/mapButtons.js +5 -1
- package/src/App/renderer/mapButtons.test.js +37 -1
- package/src/App/store/AppProvider.test.jsx +5 -0
- package/src/App/store/MapProvider.test.jsx +8 -1
- package/src/App/store/ServiceProvider.jsx +4 -1
- package/src/App/store/ServiceProvider.test.jsx +11 -0
- package/src/App/store/appActionsMap.js +5 -1
- package/src/App/store/appActionsMap.test.js +13 -0
- package/src/App/store/appReducer.js +1 -0
- package/src/App/store/mapActionsMap.js +2 -1
- package/src/InteractiveMap/InteractiveMap.js +12 -0
- package/src/InteractiveMap/InteractiveMap.test.js +2 -0
- package/src/config/appConfig.js +3 -1
- package/src/config/appConfig.test.js +4 -0
- package/src/config/defaults.js +1 -1
- package/src/config/events.js +8 -0
- package/src/config/mapTheme.js +8 -4
- package/src/config/symbolConfig.js +23 -16
- package/src/scss/main.scss +3 -0
- package/src/scss/tools/_border-focus.scss +0 -1
- package/src/services/announcer.test.js +5 -7
- package/src/services/eventBus.js +15 -0
- package/src/services/eventBus.test.js +11 -0
- package/src/services/hints.js +47 -0
- package/src/services/hints.test.js +110 -0
- package/src/services/patternRegistry.js +69 -1
- package/src/services/patternRegistry.test.js +112 -1
- package/src/services/reverseGeocode.js +4 -0
- package/src/services/reverseGeocode.test.js +11 -1
- package/src/services/symbolRegistry.js +152 -17
- package/src/services/symbolRegistry.test.js +141 -31
- package/src/types.js +71 -14
- package/src/utils/constrainKeyboardFocus.js +5 -5
- package/src/utils/detectInterfaceType.js +27 -5
- package/src/utils/detectInterfaceType.test.js +29 -3
- package/src/utils/patternUtils.js +4 -67
- package/src/utils/patternUtils.test.js +1 -123
- package/src/utils/symbolUtils.js +11 -13
- package/src/utils/symbolUtils.test.js +18 -26
- package/webpack.dev.mjs +6 -1
- package/webpack.umd.mjs +1 -0
- package/plugins/interact/dist/css/index.css +0 -1
- package/plugins/interact/src/interact.scss +0 -24
|
@@ -4,9 +4,10 @@ import { attachAppEvents } from './appEvents.js'
|
|
|
4
4
|
import { createMapLabelNavigator } from './utils/labels.js'
|
|
5
5
|
import { updateHighlightedFeatures } from './utils/highlightFeatures.js'
|
|
6
6
|
import { queryFeatures } from './utils/queryFeatures.js'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { addSymbolsToMap } from './utils/symbolImages.js'
|
|
8
|
+
import { addPatternsToMap } from './utils/patternImages.js'
|
|
9
9
|
import { getAreaDimensions, getCardinalMove, getResolution, getPaddedBounds, isGeometryObscured } from './utils/spatial.js'
|
|
10
|
+
import { symbolRegistry } from '../../../src/services/symbolRegistry.js'
|
|
10
11
|
|
|
11
12
|
jest.mock('./defaults.js', () => ({
|
|
12
13
|
DEFAULTS: { animationDuration: 400, coordinatePrecision: 7 },
|
|
@@ -35,8 +36,8 @@ jest.mock('./utils/labels.js', () => ({
|
|
|
35
36
|
}))
|
|
36
37
|
jest.mock('./utils/highlightFeatures.js', () => ({ updateHighlightedFeatures: jest.fn(() => []) }))
|
|
37
38
|
jest.mock('./utils/queryFeatures.js', () => ({ queryFeatures: jest.fn(() => []) }))
|
|
38
|
-
jest.mock('./utils/symbolImages.js', () => ({
|
|
39
|
-
jest.mock('./utils/patternImages.js', () => ({
|
|
39
|
+
jest.mock('./utils/symbolImages.js', () => ({ addSymbolsToMap: jest.fn(() => Promise.resolve()) }))
|
|
40
|
+
jest.mock('./utils/patternImages.js', () => ({ addPatternsToMap: jest.fn(() => Promise.resolve()) }))
|
|
40
41
|
|
|
41
42
|
describe('MapLibreProvider', () => {
|
|
42
43
|
let map, eventBus, maplibreModule, loadCallback
|
|
@@ -244,69 +245,75 @@ describe('MapLibreProvider', () => {
|
|
|
244
245
|
await doInitMap(p)
|
|
245
246
|
p.getFeaturesAtPoint({ x: 10, y: 20 }, { radius: 5 })
|
|
246
247
|
expect(queryFeatures).toHaveBeenCalledWith(map, { x: 10, y: 20 }, { radius: 5 })
|
|
247
|
-
p.updateHighlightedFeatures(['feat'], { style: 1 })
|
|
248
|
+
p.updateHighlightedFeatures(['feat'], null, { style: 1 })
|
|
248
249
|
expect(updateHighlightedFeatures).toHaveBeenCalledWith({
|
|
249
|
-
LngLatBounds: maplibreModule.LngLatBounds, map, selectedFeatures: ['feat'], stylesMap: { style: 1 }
|
|
250
|
+
LngLatBounds: maplibreModule.LngLatBounds, map, selectedFeatures: ['feat'], activeFeatures: null, stylesMap: { style: 1 }
|
|
250
251
|
})
|
|
251
252
|
})
|
|
252
253
|
|
|
253
|
-
test('
|
|
254
|
+
test('getVisibleFeatures returns empty array when no layers are present on the map', async () => {
|
|
255
|
+
const p = makeProvider()
|
|
256
|
+
await doInitMap(p)
|
|
257
|
+
map.getLayer.mockReturnValue(null)
|
|
258
|
+
expect(p.getVisibleFeatures(['l1', 'l2'])).toEqual([])
|
|
259
|
+
expect(map.queryRenderedFeatures).not.toHaveBeenCalled()
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
test('getVisibleFeatures queries only layers present on the map', async () => {
|
|
263
|
+
const p = makeProvider()
|
|
264
|
+
await doInitMap(p)
|
|
265
|
+
map.getLayer.mockImplementation(id => id === 'l1' ? {} : null) // NOSONAR
|
|
266
|
+
const features = [{ id: 'f1' }]
|
|
267
|
+
map.queryRenderedFeatures.mockReturnValue(features)
|
|
268
|
+
expect(p.getVisibleFeatures(['l1', 'l2'])).toEqual(features)
|
|
269
|
+
expect(map.queryRenderedFeatures).toHaveBeenCalledWith(undefined, { layers: ['l1'] })
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
test('addSymbolsToMap delegates to utility with map instance', async () => {
|
|
254
273
|
const p = makeProvider()
|
|
255
274
|
await doInitMap(p)
|
|
256
275
|
const configs = [{ symbol: 'pin' }]
|
|
257
276
|
const mapStyle = { id: 'test', selectedColor: '#0b0c0c' }
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
expect(registerSymbols).toHaveBeenCalledWith(map, configs, mapStyle, registry, expect.any(Number))
|
|
277
|
+
await p.addSymbolsToMap(configs, mapStyle, symbolRegistry)
|
|
278
|
+
expect(addSymbolsToMap).toHaveBeenCalledWith(map, configs, mapStyle, symbolRegistry, expect.any(Number))
|
|
261
279
|
})
|
|
262
280
|
|
|
263
|
-
test('
|
|
281
|
+
test('addSymbolsToMap computes pixelRatio from getPixelRatio and mapSize scale factor', async () => {
|
|
264
282
|
const p = makeProvider()
|
|
265
283
|
await doInitMap(p)
|
|
266
284
|
map.getPixelRatio.mockReturnValue(2)
|
|
267
|
-
p.mapSize = 'medium'
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
expect(registerSymbols).toHaveBeenCalledWith(map, [], { id: 'test' }, registry, 3) // 2 * 1.5
|
|
285
|
+
p.mapSize = 'medium'
|
|
286
|
+
await p.addSymbolsToMap([], { id: 'test' }, symbolRegistry)
|
|
287
|
+
expect(addSymbolsToMap).toHaveBeenCalledWith(map, [], { id: 'test' }, symbolRegistry, 2)
|
|
271
288
|
})
|
|
272
289
|
|
|
273
|
-
test('
|
|
290
|
+
test('addSymbolsToMap falls back to pixelRatio 1 when getPixelRatio returns 0', async () => {
|
|
274
291
|
const p = makeProvider()
|
|
275
292
|
await doInitMap(p)
|
|
276
293
|
map.getPixelRatio.mockReturnValue(0)
|
|
277
294
|
p.mapSize = 'small' // scaleFactor['small'] = 1
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
expect(registerSymbols).toHaveBeenCalledWith(map, [], { id: 'test' }, registry, 1) // (0 || 1) * 1
|
|
295
|
+
await p.addSymbolsToMap([], { id: 'test' }, symbolRegistry)
|
|
296
|
+
expect(addSymbolsToMap).toHaveBeenCalledWith(map, [], { id: 'test' }, symbolRegistry, 1)
|
|
281
297
|
})
|
|
282
298
|
|
|
283
|
-
test('
|
|
299
|
+
test('addPatternsToMap falls back to pixelRatio 1 when getPixelRatio returns 0', async () => {
|
|
284
300
|
const p = makeProvider()
|
|
285
301
|
await doInitMap(p)
|
|
302
|
+
map.getPixelRatio.mockReturnValue(0)
|
|
286
303
|
const configs = [{ fillPattern: 'dot' }]
|
|
287
304
|
const registry = {}
|
|
288
|
-
await p.
|
|
289
|
-
expect(
|
|
305
|
+
await p.addPatternsToMap(configs, 'test', registry)
|
|
306
|
+
expect(addPatternsToMap).toHaveBeenCalledWith(map, configs, 'test', registry, 1)
|
|
290
307
|
})
|
|
291
308
|
|
|
292
|
-
test('
|
|
309
|
+
test('addPatternsToMap is called with pixelRatio from getPixelRatio', async () => {
|
|
293
310
|
const p = makeProvider()
|
|
294
311
|
await doInitMap(p)
|
|
295
312
|
map.getPixelRatio.mockReturnValue(2)
|
|
296
|
-
p.mapSize = 'medium'
|
|
297
|
-
const registry = {}
|
|
298
|
-
await p.registerPatterns([], 'test', registry)
|
|
299
|
-
expect(registerPatterns).toHaveBeenCalledWith(map, [], 'test', registry, 3) // 2 * 1.5
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
test('registerPatterns falls back to pixelRatio 1 when getPixelRatio returns 0', async () => {
|
|
303
|
-
const p = makeProvider()
|
|
304
|
-
await doInitMap(p)
|
|
305
|
-
map.getPixelRatio.mockReturnValue(0)
|
|
306
|
-
p.mapSize = 'small' // scaleFactor['small'] = 1
|
|
313
|
+
p.mapSize = 'medium'
|
|
307
314
|
const registry = {}
|
|
308
|
-
await p.
|
|
309
|
-
expect(
|
|
315
|
+
await p.addPatternsToMap([], 'test', registry)
|
|
316
|
+
expect(addPatternsToMap).toHaveBeenCalledWith(map, [], 'test', registry, 2)
|
|
310
317
|
})
|
|
311
318
|
|
|
312
319
|
describe('setHoverCursor', () => {
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
const ICON_IMAGE = 'icon-image'
|
|
2
|
+
|
|
3
|
+
const ACTIVE_PREFIX = 'active-highlight'
|
|
4
|
+
const ACTIVE_INNER_PREFIX = 'active-highlight-inner'
|
|
5
|
+
const SELECTED_PREFIX = 'selected-highlight'
|
|
6
|
+
|
|
7
|
+
// Inner and selected both use the selected colour/width (black, thin)
|
|
8
|
+
const usesSelectedStyle = (prefix) => prefix === SELECTED_PREFIX || prefix === ACTIVE_INNER_PREFIX
|
|
9
|
+
|
|
10
|
+
const getActiveImageId = (map, imageId) => map._activeSymbolImageMap?.[imageId] ?? null
|
|
11
|
+
const getSelectedImageId = (map, imageId) => map._selectedSymbolImageMap?.[imageId] ?? null
|
|
12
|
+
|
|
1
13
|
const groupFeaturesBySource = (map, selectedFeatures) => {
|
|
2
14
|
const featuresBySource = {}
|
|
3
15
|
|
|
@@ -20,7 +32,7 @@ const groupFeaturesBySource = (map, selectedFeatures) => {
|
|
|
20
32
|
}
|
|
21
33
|
}
|
|
22
34
|
|
|
23
|
-
// Track whether any
|
|
35
|
+
// Track whether any feature on this source is a polygon
|
|
24
36
|
if (geometry && (geometry.type === 'Polygon' || geometry.type === 'MultiPolygon')) {
|
|
25
37
|
featuresBySource[sourceId].hasFillGeometry = true
|
|
26
38
|
featuresBySource[sourceId].fillIds.add(featureId)
|
|
@@ -32,12 +44,11 @@ const groupFeaturesBySource = (map, selectedFeatures) => {
|
|
|
32
44
|
return featuresBySource
|
|
33
45
|
}
|
|
34
46
|
|
|
35
|
-
const cleanupStaleSources = (map, previousSources, currentSources) => {
|
|
47
|
+
const cleanupStaleSources = (map, previousSources, currentSources, prefix) => {
|
|
36
48
|
previousSources.forEach(src => {
|
|
37
49
|
if (!currentSources.has(src)) {
|
|
38
|
-
const base =
|
|
39
|
-
|
|
40
|
-
layers.forEach(id => {
|
|
50
|
+
const base = `${prefix}-${src}`
|
|
51
|
+
;[`${base}-fill`, `${base}-line`, `${base}-symbol`].forEach(id => {
|
|
41
52
|
if (map.getLayer(id)) {
|
|
42
53
|
map.setFilter(id, ['==', 'id', ''])
|
|
43
54
|
}
|
|
@@ -46,6 +57,12 @@ const cleanupStaleSources = (map, previousSources, currentSources) => {
|
|
|
46
57
|
})
|
|
47
58
|
}
|
|
48
59
|
|
|
60
|
+
const clearPrefixSources = (map, prefix) => {
|
|
61
|
+
const key = `_${prefix.replaceAll('-', '')}Sources`
|
|
62
|
+
cleanupStaleSources(map, map[key] || new Set(), new Set(), prefix)
|
|
63
|
+
map[key] = new Set()
|
|
64
|
+
}
|
|
65
|
+
|
|
49
66
|
const applyHighlightLayer = (map, id, type, sourceId, srcLayer, paint, filter) => {
|
|
50
67
|
if (!map.getLayer(id)) {
|
|
51
68
|
map.addLayer({
|
|
@@ -71,13 +88,13 @@ const applySymbolHighlightLayer = (map, id, sourceId, srcLayer, originalLayerId,
|
|
|
71
88
|
source: sourceId,
|
|
72
89
|
...(srcLayer && { 'source-layer': srcLayer }),
|
|
73
90
|
layout: {
|
|
74
|
-
|
|
91
|
+
[ICON_IMAGE]: imageId,
|
|
75
92
|
'icon-anchor': map.getLayoutProperty(originalLayerId, 'icon-anchor') ?? 'center',
|
|
76
93
|
'icon-allow-overlap': true
|
|
77
94
|
}
|
|
78
95
|
})
|
|
79
96
|
}
|
|
80
|
-
map.setLayoutProperty(id,
|
|
97
|
+
map.setLayoutProperty(id, ICON_IMAGE, imageId)
|
|
81
98
|
map.setFilter(id, filter)
|
|
82
99
|
map.moveLayer(id)
|
|
83
100
|
}
|
|
@@ -97,67 +114,105 @@ const calculateBounds = (LngLatBounds, renderedFeatures) => {
|
|
|
97
114
|
return [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()]
|
|
98
115
|
}
|
|
99
116
|
|
|
100
|
-
const
|
|
117
|
+
const applySymbolGeomHighlight = (map, base, sourceId, srcLayer, layerId, filter, getSymbolImageId) => {
|
|
118
|
+
const imageId = map.getLayoutProperty(layerId, ICON_IMAGE)
|
|
119
|
+
const symbolImageId = getSymbolImageId(map, imageId)
|
|
120
|
+
if (symbolImageId) {
|
|
121
|
+
applySymbolHighlightLayer(map, `${base}-symbol`, sourceId, srcLayer, layerId, symbolImageId, filter)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const applyFillGeomHighlight = (map, base, sourceId, srcLayer, { isSelected, idExpression, fillIds, fill, lineColor, lineWidth, filter }) => {
|
|
126
|
+
if (isSelected) {
|
|
127
|
+
const fillFilter = ['in', idExpression, ['literal', [...fillIds]]]
|
|
128
|
+
// Only apply fill highlight to polygon features, not to any co-selected line features
|
|
129
|
+
applyHighlightLayer(map, `${base}-fill`, 'fill', sourceId, srcLayer, { 'fill-color': fill }, fillFilter)
|
|
130
|
+
}
|
|
131
|
+
applyHighlightLayer(map, `${base}-line`, 'line', sourceId, srcLayer, { 'line-color': lineColor, 'line-width': lineWidth }, filter)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const applySourceHighlight = (map, sourceId, featuresBySource, stylesMap, prefix, getSymbolImageId) => {
|
|
135
|
+
const { ids, fillIds, idProperty, layerId, hasFillGeometry } = featuresBySource[sourceId]
|
|
136
|
+
const baseLayer = map.getLayer(layerId)
|
|
137
|
+
const srcLayer = baseLayer.sourceLayer
|
|
138
|
+
const geom = hasFillGeometry ? 'fill' : baseLayer.type
|
|
139
|
+
const base = `${prefix}-${sourceId}`
|
|
140
|
+
const style = stylesMap[layerId]
|
|
141
|
+
|
|
142
|
+
if (!style) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const { stroke, selectionStroke, strokeWidth, activeStrokeWidth, fill } = style
|
|
147
|
+
const isSelected = prefix === SELECTED_PREFIX
|
|
148
|
+
const selectedStyle = usesSelectedStyle(prefix)
|
|
149
|
+
const lineColor = selectedStyle ? selectionStroke : stroke
|
|
150
|
+
const lineWidth = selectedStyle ? strokeWidth : activeStrokeWidth
|
|
151
|
+
const idExpression = idProperty ? ['get', idProperty] : ['id']
|
|
152
|
+
const filter = ['in', idExpression, ['literal', [...ids]]]
|
|
153
|
+
|
|
154
|
+
if (geom === 'fill') {
|
|
155
|
+
applyFillGeomHighlight(map, base, sourceId, srcLayer, { isSelected, idExpression, fillIds, fill, lineColor, lineWidth, filter })
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (geom === 'line') {
|
|
159
|
+
if (map.getLayer(`${base}-fill`)) {
|
|
160
|
+
// Clear any fill highlight from a previous polygon on the same source
|
|
161
|
+
map.setFilter(`${base}-fill`, ['==', 'id', ''])
|
|
162
|
+
}
|
|
163
|
+
applyHighlightLayer(map, `${base}-line`, 'line', sourceId, srcLayer, { 'line-color': lineColor, 'line-width': lineWidth }, filter)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (geom === 'symbol') {
|
|
167
|
+
applySymbolGeomHighlight(map, base, sourceId, srcLayer, layerId, filter, getSymbolImageId)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const applyFeatureHighlights = (map, features, stylesMap, prefix, getSymbolImageId) => {
|
|
172
|
+
const featuresBySource = groupFeaturesBySource(map, features)
|
|
173
|
+
const currentSources = new Set(Object.keys(featuresBySource))
|
|
174
|
+
const storageKey = `_${prefix.replaceAll('-', '')}Sources`
|
|
175
|
+
const previousSources = map[storageKey] || new Set()
|
|
176
|
+
|
|
177
|
+
cleanupStaleSources(map, previousSources, currentSources, prefix)
|
|
178
|
+
map[storageKey] = currentSources
|
|
179
|
+
currentSources.forEach(sourceId => applySourceHighlight(map, sourceId, featuresBySource, stylesMap, prefix, getSymbolImageId))
|
|
180
|
+
|
|
181
|
+
return featuresBySource
|
|
182
|
+
}
|
|
101
183
|
|
|
102
184
|
/**
|
|
103
185
|
* Update highlighted features using pure filters.
|
|
186
|
+
* activeFeatures (keyboard cursor) render with the active ring (yellow) plus a selected ring inner (black).
|
|
187
|
+
* selectedFeatures render with the selected ring (black) only.
|
|
104
188
|
* Supports fill, line and symbol geometry, multi-source, cleanup, and bounds.
|
|
105
189
|
*/
|
|
106
|
-
export function updateHighlightedFeatures ({ LngLatBounds, map, selectedFeatures, stylesMap }) {
|
|
190
|
+
export function updateHighlightedFeatures ({ LngLatBounds, map, selectedFeatures, activeFeatures, stylesMap }) {
|
|
107
191
|
if (!map) {
|
|
108
192
|
return null
|
|
109
193
|
}
|
|
110
194
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// Apply highlights for current sources
|
|
121
|
-
currentSources.forEach(sourceId => {
|
|
122
|
-
const { ids, fillIds, idProperty, layerId, hasFillGeometry } = featuresBySource[sourceId]
|
|
123
|
-
const baseLayer = map.getLayer(layerId)
|
|
124
|
-
const srcLayer = baseLayer.sourceLayer
|
|
125
|
-
|
|
126
|
-
// Use the actual feature geometry to determine highlight type
|
|
127
|
-
const geom = hasFillGeometry ? 'fill' : baseLayer.type
|
|
128
|
-
const base = `highlight-${sourceId}`
|
|
129
|
-
|
|
130
|
-
const idExpression = idProperty ? ['get', idProperty] : ['id']
|
|
131
|
-
const filter = ['in', idExpression, ['literal', [...ids]]]
|
|
132
|
-
|
|
133
|
-
if (geom === 'fill') {
|
|
134
|
-
const { stroke, strokeWidth, fill } = stylesMap[layerId]
|
|
135
|
-
const fillFilter = ['in', idExpression, ['literal', [...fillIds]]]
|
|
136
|
-
const linePaint = { 'line-color': stroke, 'line-width': strokeWidth }
|
|
137
|
-
// Only apply fill highlight to polygon features, not to any co-selected line features
|
|
138
|
-
applyHighlightLayer(map, `${base}-fill`, 'fill', sourceId, srcLayer, { 'fill-color': fill }, fillFilter)
|
|
139
|
-
applyHighlightLayer(map, `${base}-line`, 'line', sourceId, srcLayer, linePaint, filter)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (geom === 'line') {
|
|
143
|
-
const { stroke, strokeWidth } = stylesMap[layerId]
|
|
144
|
-
const linePaint = { 'line-color': stroke, 'line-width': strokeWidth }
|
|
145
|
-
// Clear any fill highlight from a previous polygon selection on the same source
|
|
146
|
-
if (map.getLayer(`${base}-fill`)) {
|
|
147
|
-
map.setFilter(`${base}-fill`, ['==', 'id', ''])
|
|
148
|
-
}
|
|
149
|
-
applyHighlightLayer(map, `${base}-line`, 'line', sourceId, srcLayer, linePaint, filter)
|
|
150
|
-
}
|
|
195
|
+
// Active cursor features — rendered first so selected layers appear on top
|
|
196
|
+
if (activeFeatures?.length) {
|
|
197
|
+
applyFeatureHighlights(map, activeFeatures, stylesMap, ACTIVE_PREFIX, getActiveImageId)
|
|
198
|
+
// Black selected stroke on top of yellow active (mirrors resolveActive for symbols)
|
|
199
|
+
applyFeatureHighlights(map, activeFeatures, stylesMap, ACTIVE_INNER_PREFIX, getSelectedImageId)
|
|
200
|
+
} else {
|
|
201
|
+
clearPrefixSources(map, ACTIVE_PREFIX)
|
|
202
|
+
clearPrefixSources(map, ACTIVE_INNER_PREFIX)
|
|
203
|
+
}
|
|
151
204
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
205
|
+
// Selection features
|
|
206
|
+
let featuresBySource = {}
|
|
207
|
+
if (selectedFeatures?.length) {
|
|
208
|
+
featuresBySource = applyFeatureHighlights(map, selectedFeatures, stylesMap, SELECTED_PREFIX, getSelectedImageId)
|
|
209
|
+
} else {
|
|
210
|
+
clearPrefixSources(map, SELECTED_PREFIX)
|
|
211
|
+
}
|
|
159
212
|
|
|
160
|
-
|
|
213
|
+
// Bounds only from selected features
|
|
214
|
+
const renderedFeatures = []
|
|
215
|
+
Object.entries(featuresBySource).forEach(([, { ids, idProperty, layerId }]) => {
|
|
161
216
|
renderedFeatures.push(
|
|
162
217
|
...map
|
|
163
218
|
.queryRenderedFeatures({ layers: [layerId] })
|