@defra/interactive-map 0.0.15-alpha → 0.0.17-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/css/docusaurus.css +104 -0
- package/assets/images/favicon.svg +1 -0
- package/assets/images/hero.png +0 -0
- package/assets/images/slot-map.svg +264 -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/slots.md +90 -6
- package/docs/api.md +4 -4
- package/docs/architecture.md +3 -1
- package/docs/{demo.mdx → examples.mdx} +1 -1
- package/docs/getting-started.md +5 -4
- package/docs/index.mdx +42 -0
- package/docs/plugins/datasets.md +561 -0
- package/docs/plugins/interact.md +176 -55
- package/docs/plugins/map-styles.md +64 -7
- package/docs/plugins/search.md +207 -63
- package/docs/plugins.md +8 -16
- package/docusaurus.config.cjs +34 -34
- package/jest.setup.js +1 -1
- package/package.json +6 -5
- package/plugins/beta/datasets/dist/css/index.css +85 -15
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/index.js +1 -1
- package/plugins/beta/datasets/src/DatasetsInit.jsx +24 -9
- package/plugins/beta/datasets/src/adapters/maplibre/index.js +18 -0
- package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +113 -0
- package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +69 -0
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +338 -0
- package/plugins/beta/datasets/src/adapters/maplibre/patternRegistry.js +48 -0
- package/plugins/beta/datasets/src/api/addDataset.js +3 -9
- package/plugins/beta/datasets/src/api/getOpacity.js +17 -0
- package/plugins/beta/datasets/src/api/getStyle.js +13 -0
- package/plugins/beta/datasets/src/api/removeDataset.js +3 -45
- package/plugins/beta/datasets/src/api/setData.js +8 -0
- package/plugins/beta/datasets/src/api/setDatasetVisibility.js +37 -0
- package/plugins/beta/datasets/src/api/setFeatureVisibility.js +22 -0
- package/plugins/beta/datasets/src/api/setOpacity.js +29 -0
- package/plugins/beta/datasets/src/api/setStyle.js +22 -0
- package/plugins/beta/datasets/src/datasets.js +33 -59
- package/plugins/beta/datasets/src/defaults.js +43 -9
- package/plugins/beta/datasets/src/fetch/createDynamicSource.js +39 -30
- package/plugins/beta/datasets/src/fetch/fetchGeoJSON.js +2 -2
- package/plugins/beta/datasets/src/manifest.js +27 -19
- package/plugins/beta/datasets/src/panels/Key.jsx +129 -49
- package/plugins/beta/datasets/src/panels/Key.module.scss +48 -9
- package/plugins/beta/datasets/src/panels/Layers.jsx +131 -29
- package/plugins/beta/datasets/src/panels/Layers.module.scss +50 -8
- package/plugins/beta/datasets/src/reducer.js +128 -9
- package/plugins/beta/datasets/src/styles/patterns.js +157 -0
- package/plugins/beta/datasets/src/utils/bbox.js +8 -6
- package/plugins/beta/datasets/src/utils/filters.js +5 -2
- package/plugins/beta/datasets/src/utils/mergeSublayer.js +78 -0
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
- package/plugins/beta/draw-es/src/DrawInit.jsx +16 -16
- package/plugins/beta/draw-es/src/api/addFeature.js +3 -3
- package/plugins/beta/draw-es/src/api/deleteFeature.js +3 -3
- package/plugins/beta/draw-es/src/api/editFeature.js +3 -3
- package/plugins/beta/draw-es/src/api/newPolygon.js +3 -3
- package/plugins/beta/draw-es/src/events.js +52 -20
- package/plugins/beta/draw-es/src/events.test.js +301 -0
- package/plugins/beta/draw-es/src/graphic.js +1 -1
- package/plugins/beta/draw-es/src/manifest.js +4 -4
- package/plugins/beta/draw-es/src/reducer.js +1 -1
- package/plugins/beta/draw-es/src/sketchViewModel.js +1 -1
- package/plugins/beta/draw-ml/dist/css/index.css +1 -1
- 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/DrawInit.jsx +49 -52
- package/plugins/beta/draw-ml/src/api/deleteFeature.js +1 -1
- package/plugins/beta/draw-ml/src/api/editFeature.js +8 -5
- package/plugins/beta/draw-ml/src/api/newLine.js +0 -1
- package/plugins/beta/draw-ml/src/api/newPolygon.js +0 -1
- package/plugins/beta/draw-ml/src/api/split.js +4 -4
- package/plugins/beta/draw-ml/src/defaults.js +1 -1
- package/plugins/beta/draw-ml/src/draw.scss +0 -7
- package/plugins/beta/draw-ml/src/events.js +8 -6
- package/plugins/beta/draw-ml/src/manifest.js +29 -29
- package/plugins/beta/draw-ml/src/mapboxDraw.js +1 -1
- package/plugins/beta/draw-ml/src/mapboxSnap.js +17 -18
- package/plugins/beta/draw-ml/src/modes/createDrawMode.js +31 -31
- package/plugins/beta/draw-ml/src/modes/disabledMode.js +1 -1
- package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +11 -11
- package/plugins/beta/draw-ml/src/modes/editVertex/undoHandlers.js +7 -7
- package/plugins/beta/draw-ml/src/modes/editVertex/vertexOperations.js +8 -8
- package/plugins/beta/draw-ml/src/modes/editVertex/vertexQueries.js +7 -7
- package/plugins/beta/draw-ml/src/modes/editVertexMode.js +32 -24
- package/plugins/beta/draw-ml/src/reducer.js +1 -1
- package/plugins/beta/draw-ml/src/undoStack.js +4 -4
- package/plugins/beta/draw-ml/src/utils/snapHelpers.js +12 -12
- package/plugins/beta/draw-ml/src/utils/spatial.js +11 -11
- package/plugins/beta/frame/dist/esm/im-frame-plugin.js +1 -1
- package/plugins/beta/frame/dist/umd/im-frame-plugin.js +1 -1
- package/plugins/beta/frame/src/Frame.jsx +9 -9
- package/plugins/beta/frame/src/FrameInit.jsx +4 -4
- package/plugins/beta/frame/src/api/addFrame.js +1 -1
- package/plugins/beta/frame/src/api/editFeature.js +1 -1
- package/plugins/beta/frame/src/config.js +1 -1
- package/plugins/beta/frame/src/manifest.js +3 -3
- package/plugins/beta/frame/src/reducer.js +1 -1
- package/plugins/beta/frame/src/utils.js +1 -1
- 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/src/MapStyles.jsx +18 -18
- package/plugins/beta/map-styles/src/manifest.js +1 -1
- package/plugins/beta/scale-bar/dist/css/index.css +1 -1
- package/plugins/beta/scale-bar/dist/esm/im-scale-bar-plugin.js +1 -1
- package/plugins/beta/scale-bar/dist/umd/im-scale-bar-plugin.js +1 -1
- package/plugins/beta/scale-bar/src/ScaleBar.jsx +5 -5
- package/plugins/beta/scale-bar/src/index.test.js +3 -3
- package/plugins/beta/scale-bar/src/manifest.js +3 -3
- package/plugins/beta/scale-bar/src/scaleBar.scss +2 -1
- package/plugins/beta/use-location/src/UseLocation.jsx +1 -1
- package/plugins/beta/use-location/src/defaults.js +1 -1
- package/plugins/beta/use-location/src/events.js +3 -3
- package/plugins/interact/dist/css/index.css +1 -1
- 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/src/InteractInit.jsx +1 -2
- package/plugins/interact/src/api/enable.js +8 -5
- package/plugins/interact/src/api/enable.test.js +2 -2
- package/plugins/interact/src/api/selectFeature.js +4 -4
- package/plugins/interact/src/api/unselectFeature.js +5 -5
- package/plugins/interact/src/defaults.js +0 -1
- package/plugins/interact/src/events.test.js +15 -15
- package/plugins/interact/src/hooks/useHighlightSync.js +1 -1
- package/plugins/interact/src/hooks/useInteractionHandlers.js +2 -2
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +5 -5
- package/plugins/interact/src/interact.scss +0 -7
- package/plugins/interact/src/manifest.js +15 -19
- package/plugins/interact/src/manifest.test.js +6 -5
- package/plugins/interact/src/reducer.js +3 -3
- package/plugins/interact/src/reducer.test.js +0 -1
- package/plugins/interact/src/utils/spatial.js +10 -10
- package/plugins/interact/src/utils/spatial.test.js +14 -14
- 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 +7 -6
- package/plugins/search/src/Search.test.jsx +23 -23
- package/plugins/search/src/components/CloseButton/CloseButton.jsx +15 -15
- package/plugins/search/src/components/CloseButton/CloseButton.test.jsx +2 -2
- package/plugins/search/src/components/Form/Form.jsx +14 -14
- package/plugins/search/src/components/Form/Form.module.scss +2 -1
- package/plugins/search/src/components/Form/Form.test.jsx +11 -11
- package/plugins/search/src/components/OpenButton/OpenButton.jsx +16 -15
- package/plugins/search/src/components/OpenButton/OpenButton.test.jsx +6 -2
- package/plugins/search/src/components/SubmitButton/SubmitButton.jsx +15 -15
- package/plugins/search/src/components/Suggestions/Suggestions.jsx +6 -6
- package/plugins/search/src/components/Suggestions/Suggestions.test.jsx +4 -4
- package/plugins/search/src/datasets.js +12 -13
- package/plugins/search/src/datasets.test.js +1 -1
- package/plugins/search/src/defaults.js +1 -1
- package/plugins/search/src/events/fetchSuggestions.js +3 -3
- package/plugins/search/src/events/fetchSuggestions.test.js +1 -1
- package/plugins/search/src/events/formHandlers.js +3 -3
- package/plugins/search/src/events/formHandlers.test.js +1 -1
- package/plugins/search/src/events/index.js +2 -2
- package/plugins/search/src/events/index.test.js +2 -2
- package/plugins/search/src/events/inputHandlers.js +4 -4
- package/plugins/search/src/events/inputHandlers.test.js +1 -1
- package/plugins/search/src/events/suggestionHandlers.js +2 -2
- package/plugins/search/src/events/suggestionHandlers.test.js +1 -1
- package/plugins/search/src/index.js +2 -1
- package/plugins/search/src/index.test.js +3 -3
- package/plugins/search/src/manifest.js +6 -4
- package/plugins/search/src/reducer.js +1 -2
- package/plugins/search/src/reducer.test.js +2 -2
- package/plugins/search/src/search.scss +10 -3
- package/plugins/search/src/utils/parseOsNamesResults.js +1 -2
- package/plugins/search/src/utils/parseOsNamesResults.test.js +2 -2
- package/plugins/search/src/utils/updateMap.js +1 -1
- package/plugins/search/src/utils/updateMap.test.js +5 -5
- package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
- package/providers/beta/esri/src/esriProvider.js +5 -5
- package/providers/beta/esri/src/utils/coords.js +1 -1
- package/providers/beta/esri/src/utils/esriFixes.js +1 -1
- package/providers/beta/esri/src/utils/query.js +4 -4
- package/providers/beta/esri/src/utils/spatial.js +1 -2
- package/providers/beta/esri/src/utils/spatial.test.js +4 -1
- package/providers/beta/open-names/src/utils/mapToLocationModel.test.js +1 -1
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-framework.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-framework.js.LICENSE.txt +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/src/appEvents.test.js +1 -1
- package/providers/maplibre/src/index.js +1 -1
- package/providers/maplibre/src/index.test.js +3 -5
- package/providers/maplibre/src/mapEvents.test.js +15 -5
- package/providers/maplibre/src/maplibreProvider.test.js +6 -2
- package/providers/maplibre/src/utils/calculateLinearTextSize.js +4 -4
- package/providers/maplibre/src/utils/calculateLinearTextSize.test.js +3 -3
- package/providers/maplibre/src/utils/detectWebgl.test.js +1 -1
- package/providers/maplibre/src/utils/highlightFeatures.js +3 -2
- package/providers/maplibre/src/utils/highlightFeatures.test.js +13 -6
- package/providers/maplibre/src/utils/labels.js +19 -20
- package/providers/maplibre/src/utils/labels.test.js +15 -13
- package/providers/maplibre/src/utils/maplibreFixes.test.js +1 -1
- package/providers/maplibre/src/utils/queryFeatures.js +6 -6
- package/providers/maplibre/src/utils/queryFeatures.test.js +13 -13
- package/providers/maplibre/src/utils/spatial.js +0 -1
- package/providers/maplibre/src/utils/spatial.test.js +26 -27
- package/src/App/components/Actions/Actions.jsx +2 -2
- package/src/App/components/Actions/Actions.module.scss +0 -7
- package/src/App/components/Actions/Actions.test.jsx +1 -1
- package/src/App/components/Icon/Icon.jsx +3 -2
- package/src/App/components/Icon/Icon.module.scss +4 -0
- package/src/App/components/Icon/Icon.test.jsx +43 -4
- package/src/App/components/MapButton/MapButton.jsx +42 -17
- package/src/App/components/MapButton/MapButton.module.scss +4 -13
- package/src/App/components/MapButton/MapButton.test.jsx +27 -3
- package/src/App/components/PopupMenu/PopupMenu.jsx +51 -274
- package/src/App/components/PopupMenu/PopupMenu.module.scss +14 -7
- package/src/App/components/PopupMenu/PopupMenu.test.jsx +70 -1
- package/src/App/components/PopupMenu/usePopupMenu.js +258 -0
- package/src/App/hooks/useButtonStateEvaluator.js +12 -2
- package/src/App/hooks/useButtonStateEvaluator.test.js +38 -4
- package/src/App/hooks/useInterfaceAPI.js +6 -0
- package/src/App/hooks/useLayoutMeasurements.js +84 -18
- package/src/App/hooks/useLayoutMeasurements.test.js +124 -17
- package/src/App/layout/Layout.jsx +12 -7
- package/src/App/layout/Layout.test.jsx +2 -2
- package/src/App/layout/layout.module.scss +67 -29
- package/src/App/registry/pluginRegistry.js +17 -0
- package/src/App/registry/pluginRegistry.test.js +33 -0
- package/src/App/renderer/HtmlElementHost.jsx +2 -1
- package/src/App/renderer/HtmlElementHost.test.jsx +7 -7
- package/src/App/renderer/mapButtons.js +3 -2
- package/src/App/renderer/mapPanels.test.js +2 -2
- package/src/App/renderer/slotHelpers.js +2 -2
- package/src/App/renderer/slotHelpers.test.js +5 -5
- package/src/App/renderer/slots.js +9 -5
- package/src/App/store/AppProvider.jsx +3 -1
- package/src/App/store/AppProvider.test.jsx +1 -1
- package/src/App/store/ServiceProvider.jsx +3 -1
- package/src/App/store/appActionsMap.js +16 -0
- package/src/App/store/appActionsMap.test.js +27 -0
- package/src/App/store/appDispatchMiddleware.js +33 -1
- package/src/App/store/appDispatchMiddleware.test.js +250 -222
- package/src/App/store/appReducer.js +2 -0
- package/src/InteractiveMap/InteractiveMap.js +4 -0
- package/src/config/appConfig.js +7 -4
- package/src/config/events.js +28 -0
- package/src/scss/main.scss +1 -0
- package/src/scss/settings/_dimensions.scss +0 -1
- package/src/services/logger.js +6 -0
- package/src/services/logger.test.js +32 -0
- package/src/utils/getSafeZoneInset.js +9 -7
- package/src/utils/getSafeZoneInset.test.js +10 -10
- package/webpack.dev.mjs +23 -19
- package/docs/govuk-prototype.md +0 -23
- package/docs/index.md +0 -19
- package/plugins/beta/datasets/src/api/hideDataset.js +0 -14
- package/plugins/beta/datasets/src/api/hideFeatures.js +0 -41
- package/plugins/beta/datasets/src/api/showDataset.js +0 -14
- package/plugins/beta/datasets/src/api/showFeatures.js +0 -44
- package/plugins/beta/datasets/src/handleSetMapStyle.js +0 -54
- package/plugins/beta/datasets/src/mapLayers.js +0 -165
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* @param {React.RefObject} refs.leftRef - Left button column.
|
|
28
28
|
* @param {React.RefObject} refs.rightRef - Right button column.
|
|
29
29
|
* @param {React.RefObject} refs.actionsRef - Bottom action bar.
|
|
30
|
-
* @param {React.RefObject} refs.
|
|
30
|
+
* @param {React.RefObject} refs.bottomRef - Bottom row (logo, copyright, etc).
|
|
31
31
|
* @param {React.RefObject} [refs.leftTopRef] - Top-left panel slot.
|
|
32
32
|
* @param {React.RefObject} [refs.leftBottomRef] - Bottom-left panel slot.
|
|
33
33
|
* @param {React.RefObject} [refs.rightTopRef] - Top-right panel slot.
|
|
@@ -107,15 +107,15 @@ const computeRow = (leftW, rightW, leftH, rightH, wThreshold, baseInset, gap) =>
|
|
|
107
107
|
leftW + rightW > wThreshold ? baseInset + Math.max(leftH, rightH) + gap : 0
|
|
108
108
|
|
|
109
109
|
export const getSafeZoneInset = ({
|
|
110
|
-
mainRef, leftRef, rightRef, actionsRef,
|
|
110
|
+
mainRef, leftRef, rightRef, actionsRef, bottomRef,
|
|
111
111
|
leftTopRef, leftBottomRef, rightTopRef, rightBottomRef
|
|
112
112
|
}) => {
|
|
113
|
-
if ([mainRef, leftRef, rightRef, actionsRef,
|
|
113
|
+
if ([mainRef, leftRef, rightRef, actionsRef, bottomRef].some(ref => !ref?.current)) {
|
|
114
114
|
return undefined
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
const main = mainRef.current; const left = leftRef.current
|
|
118
|
-
const actions = actionsRef.current; const
|
|
118
|
+
const actions = actionsRef.current; const bottom = bottomRef.current
|
|
119
119
|
|
|
120
120
|
const gap = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--divider-gap'), 10)
|
|
121
121
|
|
|
@@ -127,8 +127,10 @@ export const getSafeZoneInset = ({
|
|
|
127
127
|
const baseLeft = main.offsetLeft + left.offsetLeft + colWidth + gap
|
|
128
128
|
const baseRight = left.offsetLeft + colWidth + gap
|
|
129
129
|
const baseTop = left.offsetTop
|
|
130
|
-
const
|
|
131
|
-
|
|
130
|
+
const bottomContainerPad = main.offsetHeight - bottom.offsetTop - bottom.offsetHeight
|
|
131
|
+
// Minimum: primary-gap above the bottom edge. Normally: divider-gap above the top of the bottom container.
|
|
132
|
+
const bottomInset = Math.max(bottomContainerPad, main.offsetHeight - bottom.offsetTop + gap)
|
|
133
|
+
const baseBottom = Math.max(main.offsetHeight - actions.offsetTop + gap, bottomInset)
|
|
132
134
|
|
|
133
135
|
const availableH = main.offsetHeight - baseTop - baseBottom
|
|
134
136
|
const availableW = main.offsetWidth - (baseLeft - main.offsetLeft) - baseRight
|
|
@@ -139,7 +141,7 @@ export const getSafeZoneInset = ({
|
|
|
139
141
|
const leftPanelInset = leftCol.panelInset
|
|
140
142
|
const rightPanelInset = rightCol.panelInset
|
|
141
143
|
const topPanelInset = computeRow(leftCol.rowA.w, rightCol.rowA.w, leftCol.rowA.h, rightCol.rowA.h, availableW / RATIO, baseTop, gap)
|
|
142
|
-
const bottomPanelInset = computeRow(leftCol.rowB.w, rightCol.rowB.w, leftCol.rowB.h, rightCol.rowB.h, availableW / RATIO,
|
|
144
|
+
const bottomPanelInset = computeRow(leftCol.rowB.w, rightCol.rowB.w, leftCol.rowB.h, rightCol.rowB.h, availableW / RATIO, bottomInset, gap)
|
|
143
145
|
|
|
144
146
|
const usableW = main.offsetWidth - 2 * gap
|
|
145
147
|
const usableH = main.offsetHeight - 2 * gap
|
|
@@ -7,7 +7,7 @@ const LEFT_WIDTH = 40
|
|
|
7
7
|
const LEFT_TOP = 60
|
|
8
8
|
const RIGHT_WIDTH = 40
|
|
9
9
|
const ACTIONS_TOP = 540
|
|
10
|
-
const
|
|
10
|
+
const BOTTOM_TOP = 560
|
|
11
11
|
const EARLY_ACTIONS_TOP = 500
|
|
12
12
|
const GAP = 8
|
|
13
13
|
|
|
@@ -31,7 +31,7 @@ const PANEL_H_TALL = 150
|
|
|
31
31
|
const PANEL_H_SHORT = 100
|
|
32
32
|
|
|
33
33
|
const ABOVE_CAP_TOP = 330 // 60+330+8=398 > CAP_HEIGHT ≈ 389.3 → capped
|
|
34
|
-
const
|
|
34
|
+
const BOTTOM_INSET = MAIN_HEIGHT - BOTTOM_TOP + GAP // 48: divider-gap above top of bottom container
|
|
35
35
|
const ABOVE_CAP_BOTTOM = 342 // 48+342+8=398 > CAP_HEIGHT → capped
|
|
36
36
|
|
|
37
37
|
const PANEL_W_STANDARD = 200
|
|
@@ -49,7 +49,7 @@ const CAP_HEIGHT = (MAIN_HEIGHT - 2 * GAP) * (MAX_RATIO - 1) / MAX_RATIO
|
|
|
49
49
|
|
|
50
50
|
// ─── Setup ──────────────────────────────────────────────────────────────────
|
|
51
51
|
|
|
52
|
-
let mainRef, leftRef, rightRef, actionsRef,
|
|
52
|
+
let mainRef, leftRef, rightRef, actionsRef, bottomRef
|
|
53
53
|
|
|
54
54
|
beforeAll(() => {
|
|
55
55
|
globalThis.getComputedStyle = jest.fn().mockReturnValue({ getPropertyValue: () => String(GAP) })
|
|
@@ -72,10 +72,10 @@ beforeEach(() => {
|
|
|
72
72
|
leftRef = colRef(LEFT_WIDTH, LEFT_LEFT, LEFT_TOP)
|
|
73
73
|
rightRef = colRef(RIGHT_WIDTH)
|
|
74
74
|
actionsRef = { current: { offsetTop: ACTIONS_TOP } }
|
|
75
|
-
|
|
75
|
+
bottomRef = { current: { offsetTop: BOTTOM_TOP, offsetHeight: 0 } }
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
const base = () => ({ mainRef, leftRef, rightRef, actionsRef,
|
|
78
|
+
const base = () => ({ mainRef, leftRef, rightRef, actionsRef, bottomRef })
|
|
79
79
|
|
|
80
80
|
// Slot container where an .im-c-panel is the first element child.
|
|
81
81
|
const panel = (offsetWidth, offsetHeight) => {
|
|
@@ -96,7 +96,7 @@ describe('getSafeZoneInset — missing refs', () => {
|
|
|
96
96
|
expect(getSafeZoneInset({ ...base(), leftRef: { current: null } })).toBeUndefined()
|
|
97
97
|
})
|
|
98
98
|
it('returns undefined when actionsRef is undefined', () => {
|
|
99
|
-
expect(getSafeZoneInset({ mainRef, leftRef, rightRef,
|
|
99
|
+
expect(getSafeZoneInset({ mainRef, leftRef, rightRef, bottomRef })).toBeUndefined()
|
|
100
100
|
})
|
|
101
101
|
})
|
|
102
102
|
|
|
@@ -120,7 +120,7 @@ describe('getSafeZoneInset — base structural insets', () => {
|
|
|
120
120
|
it('uses zero button width when column ref has no button group', () => {
|
|
121
121
|
const noGroupLeft = { current: { offsetWidth: 0, offsetLeft: LEFT_LEFT, offsetTop: LEFT_TOP, querySelector: () => null } }
|
|
122
122
|
const noGroupRight = { current: { offsetWidth: 0, offsetLeft: 0, offsetTop: 0, querySelector: () => null } }
|
|
123
|
-
expect(getSafeZoneInset({ mainRef, leftRef: noGroupLeft, rightRef: noGroupRight, actionsRef,
|
|
123
|
+
expect(getSafeZoneInset({ mainRef, leftRef: noGroupLeft, rightRef: noGroupRight, actionsRef, bottomRef })).toEqual({
|
|
124
124
|
left: LEFT_LEFT + GAP, right: LEFT_LEFT + GAP, top: LEFT_TOP, bottom: BASE_BOTTOM
|
|
125
125
|
})
|
|
126
126
|
})
|
|
@@ -133,7 +133,7 @@ describe('getSafeZoneInset — base structural insets', () => {
|
|
|
133
133
|
rightBottomRef: panel(PANEL_W_STANDARD, 0)
|
|
134
134
|
})).toEqual({ left: BASE_LEFT, right: BASE_RIGHT, top: BASE_TOP, bottom: BASE_BOTTOM })
|
|
135
135
|
})
|
|
136
|
-
it('uses max of actions and
|
|
136
|
+
it('uses max of actions and bottom for base bottom', () => {
|
|
137
137
|
actionsRef.current.offsetTop = EARLY_ACTIONS_TOP
|
|
138
138
|
expect(getSafeZoneInset(base()).bottom).toBe(MAIN_HEIGHT - EARLY_ACTIONS_TOP + GAP)
|
|
139
139
|
})
|
|
@@ -244,7 +244,7 @@ describe('getSafeZoneInset — bottom edge', () => {
|
|
|
244
244
|
})
|
|
245
245
|
it('triggers when a bottom panel width exceeds threshold', () => {
|
|
246
246
|
expect(getSafeZoneInset({ ...base(), leftBottomRef: panel(ABOVE_W_THRESHOLD, ABOVE_THRESHOLD) }).bottom)
|
|
247
|
-
.toBe(Math.min(
|
|
247
|
+
.toBe(Math.min(BOTTOM_INSET + ABOVE_THRESHOLD + GAP, CAP_HEIGHT))
|
|
248
248
|
})
|
|
249
249
|
it('column-primary wide-and-tall bottom panel triggers left inset, not bottom', () => {
|
|
250
250
|
// panel(400,342): h/availableH≈0.724 > w/availableW≈0.510 → column-primary
|
|
@@ -257,7 +257,7 @@ describe('getSafeZoneInset — bottom edge', () => {
|
|
|
257
257
|
...base(),
|
|
258
258
|
leftBottomRef: panel(COMBINED_ABOVE_W, PANEL_H_TALL),
|
|
259
259
|
rightBottomRef: panel(COMBINED_ABOVE_W, PANEL_H_SHORT)
|
|
260
|
-
}).bottom).toBe(Math.min(
|
|
260
|
+
}).bottom).toBe(Math.min(BOTTOM_INSET + PANEL_H_TALL + GAP, CAP_HEIGHT))
|
|
261
261
|
})
|
|
262
262
|
it('does not trigger when both bottom panels are below combined width threshold', () => {
|
|
263
263
|
expect(getSafeZoneInset({
|
package/webpack.dev.mjs
CHANGED
|
@@ -12,16 +12,20 @@ dotenv.config({ path: path.join(__dirname, './.env'), quiet: true })
|
|
|
12
12
|
export default {
|
|
13
13
|
mode: 'development',
|
|
14
14
|
target: ['web', 'es5'],
|
|
15
|
+
devtool: [
|
|
16
|
+
// produce a source-map to aid debugging
|
|
17
|
+
{ type: 'javascript', use: 'source-map' }
|
|
18
|
+
],
|
|
15
19
|
entry: {
|
|
16
20
|
index: path.join(__dirname, 'demo/js/index.js'),
|
|
17
|
-
|
|
21
|
+
draw: path.join(__dirname, 'demo/js/draw.js'),
|
|
18
22
|
farming: path.join(__dirname, 'demo/js/farming.js'),
|
|
19
23
|
planning: path.join(__dirname, 'demo/js/planning.js')
|
|
20
24
|
},
|
|
21
25
|
output: {
|
|
22
26
|
path: path.resolve(__dirname, 'public'),
|
|
23
27
|
filename: '[name].js',
|
|
24
|
-
clean: true
|
|
28
|
+
clean: true
|
|
25
29
|
},
|
|
26
30
|
resolve: {
|
|
27
31
|
extensions: ['.tsx', '.ts', '.jsx', '.js'],
|
|
@@ -84,28 +88,28 @@ export default {
|
|
|
84
88
|
test: /\.jsx?$/,
|
|
85
89
|
loader: 'babel-loader',
|
|
86
90
|
exclude: /node_modules/
|
|
87
|
-
},{
|
|
91
|
+
}, {
|
|
88
92
|
test: /\.s[ac]ss$/i,
|
|
89
|
-
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
|
|
90
|
-
},{
|
|
93
|
+
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
|
|
94
|
+
}, {
|
|
91
95
|
test: /\.css$/i,
|
|
92
|
-
use: [MiniCssExtractPlugin.loader, 'css-loader']
|
|
96
|
+
use: [MiniCssExtractPlugin.loader, 'css-loader']
|
|
93
97
|
}
|
|
94
|
-
]
|
|
98
|
+
]
|
|
95
99
|
},
|
|
96
100
|
devServer: {
|
|
97
101
|
static: [
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
{
|
|
103
|
+
directory: path.join(__dirname, 'demo')
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
directory: path.join(__dirname, 'public')
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
directory: path.join(__dirname, 'assets'),
|
|
110
|
+
publicPath: '/assets' // Images served from here as used in both demo and prototype kit plugin
|
|
111
|
+
}
|
|
112
|
+
],
|
|
109
113
|
compress: true,
|
|
110
114
|
port: 8080,
|
|
111
115
|
open: true,
|
|
@@ -120,4 +124,4 @@ export default {
|
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
|
-
}
|
|
127
|
+
}
|
package/docs/govuk-prototype.md
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# GOV.UK Prototype Kit
|
|
2
|
-
|
|
3
|
-
This guide explains how to use the Interactive Map component in a GOV.UK Prototype Kit project.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
Install the package in your prototype kit project:
|
|
8
|
-
|
|
9
|
-
```shell
|
|
10
|
-
npm install @defra/interactive-map
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
The InteractiveMap plugin will be automatically added to your prototype.
|
|
14
|
-
|
|
15
|
-
## Usage
|
|
16
|
-
|
|
17
|
-
For detailed usage instructions, see the [GOV.UK Prototype Kit documentation](https://prototype-kit.service.gov.uk/docs/install-and-use-plugins).
|
|
18
|
-
|
|
19
|
-
## Next Steps
|
|
20
|
-
|
|
21
|
-
- [Getting Started](./getting-started.md) - Learn the basics
|
|
22
|
-
- [API Reference](./api.md) - Explore the full API
|
|
23
|
-
- [Plugins](./plugins.md) - Extend functionality
|
package/docs/index.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
## What's Inside
|
|
2
|
-
|
|
3
|
-
- **[Getting Started](getting-started)** - Installation and basic usage
|
|
4
|
-
- **[API Reference](api)** - Complete API documentation
|
|
5
|
-
- **[Plugins](plugins)** - Extend functionality with plugins
|
|
6
|
-
- **[Architecture](architecture)** - Learn about the design and structure
|
|
7
|
-
- **[GOV.UK Prototype](govuk-prototype)** - Use with GOV.UK Prototype Kit
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- ✅ Accessible and keyboard navigable
|
|
12
|
-
- 🗺️ Multiple map provider support (MapLibre, ESRI)
|
|
13
|
-
- 🔌 Extensible plugin system
|
|
14
|
-
- 🎨 Customizable styling and behaviors
|
|
15
|
-
- 📱 Responsive design
|
|
16
|
-
|
|
17
|
-
## Support
|
|
18
|
-
|
|
19
|
-
For issues and feature requests, visit our [GitHub repository](https://github.com/DEFRA/interactive-map).
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export const hideDataset = ({ mapProvider, pluginState }, datasetId) => {
|
|
2
|
-
const map = mapProvider.map
|
|
3
|
-
|
|
4
|
-
// Update map layer visibility
|
|
5
|
-
if (map.getLayer(datasetId)) {
|
|
6
|
-
map.setLayoutProperty(datasetId, 'visibility', 'none')
|
|
7
|
-
}
|
|
8
|
-
if (map.getLayer(`${datasetId}-stroke`)) {
|
|
9
|
-
map.setLayoutProperty(`${datasetId}-stroke`, 'visibility', 'none')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Update state
|
|
13
|
-
pluginState.dispatch({ type: 'SET_DATASET_VISIBILITY', payload: { id: datasetId, visibility: 'hidden' } })
|
|
14
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { applyExclusionFilter } from '../utils/filters.js'
|
|
2
|
-
|
|
3
|
-
export const hideFeatures = ({ mapProvider, pluginState }, { featureIds, idProperty, datasetId }) => {
|
|
4
|
-
const map = mapProvider.map
|
|
5
|
-
|
|
6
|
-
// Get dataset to access original filter and determine layer IDs
|
|
7
|
-
const dataset = pluginState.datasets?.find(d => d.id === datasetId)
|
|
8
|
-
if (!dataset) {
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const originalFilter = dataset.filter || null
|
|
13
|
-
const hasFill = !!dataset.fill
|
|
14
|
-
const hasStroke = !!dataset.stroke
|
|
15
|
-
const fillLayerId = hasFill ? datasetId : null
|
|
16
|
-
|
|
17
|
-
let strokeLayerId = null
|
|
18
|
-
if (hasStroke) {
|
|
19
|
-
strokeLayerId = hasFill ? `${datasetId}-stroke` : datasetId
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Get current hidden state and calculate all hidden IDs
|
|
23
|
-
const existingHidden = pluginState.hiddenFeatures[datasetId]
|
|
24
|
-
const allHiddenIds = existingHidden
|
|
25
|
-
? [...new Set([...existingHidden.ids, ...featureIds])]
|
|
26
|
-
: featureIds
|
|
27
|
-
|
|
28
|
-
// Update state (store by datasetId, not individual layer IDs)
|
|
29
|
-
pluginState.dispatch({
|
|
30
|
-
type: 'HIDE_FEATURES',
|
|
31
|
-
payload: { layerId: datasetId, idProperty, featureIds }
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
// Apply filter to both layers
|
|
35
|
-
if (fillLayerId) {
|
|
36
|
-
applyExclusionFilter(map, fillLayerId, originalFilter, idProperty, allHiddenIds)
|
|
37
|
-
}
|
|
38
|
-
if (strokeLayerId) {
|
|
39
|
-
applyExclusionFilter(map, strokeLayerId, originalFilter, idProperty, allHiddenIds)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export const showDataset = ({ mapProvider, pluginState }, datasetId) => {
|
|
2
|
-
const map = mapProvider.map
|
|
3
|
-
|
|
4
|
-
// Update map layer visibility
|
|
5
|
-
if (map.getLayer(datasetId)) {
|
|
6
|
-
map.setLayoutProperty(datasetId, 'visibility', 'visible')
|
|
7
|
-
}
|
|
8
|
-
if (map.getLayer(`${datasetId}-stroke`)) {
|
|
9
|
-
map.setLayoutProperty(`${datasetId}-stroke`, 'visibility', 'visible')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Update state
|
|
13
|
-
pluginState.dispatch({ type: 'SET_DATASET_VISIBILITY', payload: { id: datasetId, visibility: 'visible' } })
|
|
14
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { applyExclusionFilter } from '../utils/filters.js'
|
|
2
|
-
|
|
3
|
-
export const showFeatures = ({ mapProvider, pluginState }, { featureIds, idProperty, datasetId }) => {
|
|
4
|
-
const map = mapProvider.map
|
|
5
|
-
|
|
6
|
-
// Get current hidden state before update
|
|
7
|
-
const existingHidden = pluginState.hiddenFeatures[datasetId]
|
|
8
|
-
if (!existingHidden) {
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Get dataset to access original filter and determine layer IDs
|
|
13
|
-
const dataset = pluginState.datasets?.find(d => d.id === datasetId)
|
|
14
|
-
if (!dataset) {
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const originalFilter = dataset.filter || null
|
|
19
|
-
const hasFill = !!dataset.fill
|
|
20
|
-
const hasStroke = !!dataset.stroke
|
|
21
|
-
const fillLayerId = hasFill ? datasetId : null
|
|
22
|
-
|
|
23
|
-
let strokeLayerId = null
|
|
24
|
-
if (hasStroke) {
|
|
25
|
-
strokeLayerId = hasFill ? `${datasetId}-stroke` : datasetId
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Calculate remaining hidden IDs
|
|
29
|
-
const remainingHiddenIds = existingHidden.ids.filter(id => !featureIds.includes(id))
|
|
30
|
-
|
|
31
|
-
// Update state
|
|
32
|
-
pluginState.dispatch({
|
|
33
|
-
type: 'SHOW_FEATURES',
|
|
34
|
-
payload: { layerId: datasetId, featureIds }
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
// Apply filter to both layers (or restore original if nothing hidden)
|
|
38
|
-
if (fillLayerId) {
|
|
39
|
-
applyExclusionFilter(map, fillLayerId, originalFilter, idProperty, remainingHiddenIds)
|
|
40
|
-
}
|
|
41
|
-
if (strokeLayerId) {
|
|
42
|
-
applyExclusionFilter(map, strokeLayerId, originalFilter, idProperty, remainingHiddenIds)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { addMapLayers } from './mapLayers.js'
|
|
2
|
-
import { applyExclusionFilter } from './utils/filters.js'
|
|
3
|
-
|
|
4
|
-
export const handleSetMapStyle = ({
|
|
5
|
-
map,
|
|
6
|
-
events,
|
|
7
|
-
eventBus,
|
|
8
|
-
getDatasets,
|
|
9
|
-
getHiddenFeatures,
|
|
10
|
-
getDynamicSources
|
|
11
|
-
}) => {
|
|
12
|
-
const onSetStyle = (e) => {
|
|
13
|
-
map.once('idle', () => {
|
|
14
|
-
const newStyleId = e.id
|
|
15
|
-
const datasets = getDatasets()
|
|
16
|
-
const hiddenFeatures = getHiddenFeatures()
|
|
17
|
-
const dynamicSources = getDynamicSources ? getDynamicSources() : new Map()
|
|
18
|
-
|
|
19
|
-
// Re-add all layers with correct colors for new style
|
|
20
|
-
datasets.forEach(dataset => {
|
|
21
|
-
addMapLayers(map, newStyleId, dataset)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
// Reapply cached data for dynamic sources
|
|
25
|
-
dynamicSources.forEach(source => {
|
|
26
|
-
source.reapply()
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
// Reapply hidden features filters
|
|
30
|
-
Object.entries(hiddenFeatures).forEach(([datasetId, { idProperty, ids }]) => {
|
|
31
|
-
const dataset = datasets.find(d => d.id === datasetId)
|
|
32
|
-
if (!dataset) {
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const originalFilter = dataset.filter || null
|
|
37
|
-
const hasFill = !!dataset.fill
|
|
38
|
-
const hasStroke = !!dataset.stroke
|
|
39
|
-
const fillLayerId = hasFill ? datasetId : null
|
|
40
|
-
const strokeLayerId = hasStroke ? (hasFill ? `${datasetId}-stroke` : datasetId) : null
|
|
41
|
-
|
|
42
|
-
if (fillLayerId) {
|
|
43
|
-
applyExclusionFilter(map, fillLayerId, originalFilter, idProperty, ids)
|
|
44
|
-
}
|
|
45
|
-
if (strokeLayerId) {
|
|
46
|
-
applyExclusionFilter(map, strokeLayerId, originalFilter, idProperty, ids)
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
eventBus.on(events.MAP_SET_STYLE, onSetStyle)
|
|
53
|
-
return onSetStyle
|
|
54
|
-
}
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { getValueForStyle } from '../../../../src/utils/getValueForStyle.js'
|
|
2
|
-
|
|
3
|
-
// Generate a hash for consistent source ID generation
|
|
4
|
-
const hashString = (str) => {
|
|
5
|
-
const HASH_BASE = 36
|
|
6
|
-
let hash = 0
|
|
7
|
-
for (const ch of str) {
|
|
8
|
-
hash = ((hash << 5) - hash) + ch.codePointAt(0)
|
|
9
|
-
hash = hash & hash
|
|
10
|
-
}
|
|
11
|
-
return Math.abs(hash).toString(HASH_BASE)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Generate a consistent source ID for source sharing
|
|
15
|
-
const getSourceId = (dataset) => {
|
|
16
|
-
if (dataset.tiles) {
|
|
17
|
-
const tilesKey = Array.isArray(dataset.tiles) ? dataset.tiles.join(',') : dataset.tiles
|
|
18
|
-
return `tiles-${hashString(tilesKey)}`
|
|
19
|
-
}
|
|
20
|
-
if (dataset.geojson) {
|
|
21
|
-
// Dynamic sources get unique IDs per dataset (no sharing)
|
|
22
|
-
if (isDynamicSource(dataset)) {
|
|
23
|
-
return `geojson-dynamic-${dataset.id}`
|
|
24
|
-
}
|
|
25
|
-
// URL strings can be shared, inline GeoJSON objects get unique IDs per dataset
|
|
26
|
-
if (typeof dataset.geojson === 'string') {
|
|
27
|
-
return `geojson-${hashString(dataset.geojson)}`
|
|
28
|
-
}
|
|
29
|
-
// Inline GeoJSON - use dataset ID since object identity can't be shared
|
|
30
|
-
return `geojson-${dataset.id}`
|
|
31
|
-
}
|
|
32
|
-
// Fallback to dataset ID
|
|
33
|
-
return `source-${dataset.id}`
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Check if a dataset uses dynamic fetching (bbox-based)
|
|
38
|
-
* Dynamic sources require: URL geojson, idProperty for deduplication, and transformRequest for URL building
|
|
39
|
-
* @param {Object} dataset
|
|
40
|
-
* @returns {boolean}
|
|
41
|
-
*/
|
|
42
|
-
const isDynamicSource = (dataset) => {
|
|
43
|
-
return (
|
|
44
|
-
typeof dataset.geojson === 'string' &&
|
|
45
|
-
!!dataset.idProperty &&
|
|
46
|
-
typeof dataset.transformRequest === 'function'
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Update the data for a GeoJSON source
|
|
52
|
-
* @param {Object} map - Map instance
|
|
53
|
-
* @param {string} sourceId - Source ID
|
|
54
|
-
* @param {Object} geojson - GeoJSON FeatureCollection
|
|
55
|
-
*/
|
|
56
|
-
const updateSourceData = (map, sourceId, geojson) => {
|
|
57
|
-
const source = map.getSource(sourceId)
|
|
58
|
-
if (source && typeof source.setData === 'function') {
|
|
59
|
-
source.setData(geojson)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Get all layer IDs that use a given source
|
|
65
|
-
* @param {string} sourceId
|
|
66
|
-
* @returns {string[]} Array of layer IDs using the source
|
|
67
|
-
*/
|
|
68
|
-
const getLayersUsingSource = (map, sourceId) => {
|
|
69
|
-
const style = map.getStyle()
|
|
70
|
-
if (!style?.layers) {
|
|
71
|
-
return []
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return style.layers
|
|
75
|
-
.filter(layer => layer.source === sourceId)
|
|
76
|
-
.map(layer => layer.id)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const addMapLayers = (map, mapStyleId, dataset) => {
|
|
80
|
-
const sourceId = getSourceId(dataset)
|
|
81
|
-
|
|
82
|
-
// --- Add source (shared across datasets with same tiles/data URL) ---
|
|
83
|
-
if (!map.getSource(sourceId)) {
|
|
84
|
-
|
|
85
|
-
if (dataset.tiles) {
|
|
86
|
-
// Tiles
|
|
87
|
-
map.addSource(sourceId, {
|
|
88
|
-
type: 'vector',
|
|
89
|
-
tiles: dataset.tiles,
|
|
90
|
-
minzoom: dataset.minZoom || 0,
|
|
91
|
-
maxzoom: dataset.maxZoom || 22
|
|
92
|
-
})
|
|
93
|
-
} else if (dataset.geojson) {
|
|
94
|
-
// Dynamic source - start with empty FeatureCollection, will be populated by createDynamicSource
|
|
95
|
-
// Static source - use URL or inline GeoJSON directly
|
|
96
|
-
const initialData = isDynamicSource(dataset)
|
|
97
|
-
? { type: 'FeatureCollection', features: [] }
|
|
98
|
-
: dataset.geojson
|
|
99
|
-
|
|
100
|
-
map.addSource(sourceId, {
|
|
101
|
-
type: 'geojson',
|
|
102
|
-
data: initialData
|
|
103
|
-
})
|
|
104
|
-
} else {
|
|
105
|
-
// No action
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// --- Determine layer IDs ---
|
|
110
|
-
const hasFill = !!dataset.fill
|
|
111
|
-
const hasStroke = !!dataset.stroke
|
|
112
|
-
const fillLayerId = hasFill ? dataset.id : null
|
|
113
|
-
const strokeLayerId = hasStroke ? (hasFill ? `${dataset.id}-stroke` : dataset.id) : null
|
|
114
|
-
|
|
115
|
-
// --- Determie visiblity ---
|
|
116
|
-
const visibility = dataset.visibility === 'hidden' ? 'none' : 'visible'
|
|
117
|
-
|
|
118
|
-
// --- Add fill layer ---
|
|
119
|
-
if (hasFill && !map.getLayer(fillLayerId)) {
|
|
120
|
-
const fillColor = getValueForStyle(dataset.fill, mapStyleId)
|
|
121
|
-
map.addLayer({
|
|
122
|
-
id: fillLayerId,
|
|
123
|
-
type: 'fill',
|
|
124
|
-
source: sourceId,
|
|
125
|
-
'source-layer': dataset?.tiles?.length ? dataset.sourceLayer : undefined,
|
|
126
|
-
layout: {
|
|
127
|
-
visibility
|
|
128
|
-
},
|
|
129
|
-
paint: {
|
|
130
|
-
'fill-color': fillColor,
|
|
131
|
-
'fill-opacity': dataset.opacity || 1
|
|
132
|
-
},
|
|
133
|
-
...(dataset.filter ? { filter: dataset.filter } : {})
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// --- Add stroke layer ---
|
|
138
|
-
if (hasStroke && !map.getLayer(strokeLayerId)) {
|
|
139
|
-
const strokeColor = getValueForStyle(dataset.stroke, mapStyleId)
|
|
140
|
-
map.addLayer({
|
|
141
|
-
id: strokeLayerId,
|
|
142
|
-
type: 'line',
|
|
143
|
-
source: sourceId,
|
|
144
|
-
'source-layer': dataset?.tiles?.length ? dataset.sourceLayer : undefined,
|
|
145
|
-
layout: {
|
|
146
|
-
visibility
|
|
147
|
-
},
|
|
148
|
-
paint: {
|
|
149
|
-
'line-color': strokeColor,
|
|
150
|
-
'line-width': dataset.strokeWidth || 1,
|
|
151
|
-
'line-opacity': dataset.opacity || 1,
|
|
152
|
-
...(dataset.strokeDashArray ? { 'line-dasharray': dataset.strokeDashArray } : {})
|
|
153
|
-
},
|
|
154
|
-
...(dataset.filter ? { filter: dataset.filter } : {})
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export {
|
|
160
|
-
getSourceId,
|
|
161
|
-
getLayersUsingSource,
|
|
162
|
-
addMapLayers,
|
|
163
|
-
isDynamicSource,
|
|
164
|
-
updateSourceData
|
|
165
|
-
}
|