@defra/interactive-map 0.0.40-alpha → 0.0.42-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/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 +7 -8
- package/docs/api/control-definition.md +17 -1
- package/docs/api/panel-definition.md +5 -0
- package/docs/api/slots.md +50 -6
- package/docs/assets/images/slot-map.svg +61 -262
- package/docs/examples/add-polygons.mdx +8 -2
- package/docs/examples/add-symbols.mdx +8 -2
- package/docs/examples/draw-tools.mdx +3 -3
- package/docs/plugins/datasets.md +23 -15
- package/docs/plugins/draw.md +79 -16
- package/docs/plugins/map-key.md +147 -0
- package/docs/plugins/search.md +1 -1
- package/docs/plugins.md +5 -1
- package/govuk-prototype-kit.config.json +2 -0
- package/jest.setup.js +5 -0
- package/package.json +9 -1
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +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-ol/dist/esm/im-draw-ol-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 -1
- package/plugins/datasets/dist/css/index.css +1 -1
- package/plugins/datasets/dist/esm/esriLayerAdapter.js +1 -1
- package/plugins/datasets/dist/esm/im-datasets-ml-adapter.js +1 -1
- package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-esri-adapter.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-ml-adapter.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/index.js +1 -1
- package/plugins/datasets/src/adapters/esri/esriLayerAdapter.test.js +175 -0
- package/plugins/datasets/src/adapters/esri/registry/esriDataset.test.js +55 -0
- package/plugins/datasets/src/adapters/loadLayerAdapter.test.js +61 -0
- package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +14 -0
- package/plugins/datasets/src/api/setOpacity.test.js +9 -0
- package/plugins/datasets/src/components/LayersMenu/LayersMenu.jsx +10 -25
- package/plugins/datasets/src/components/LayersMenu/LayersMenu.test.jsx +0 -42
- package/plugins/datasets/src/datasets.scss +0 -1
- package/plugins/datasets/src/fetch/createDynamicSource.js +135 -129
- package/plugins/datasets/src/fetch/createDynamicSource.test.js +318 -0
- package/plugins/datasets/src/fetch/fetchGeoJSON.test.js +83 -0
- package/plugins/datasets/src/index.test.js +36 -0
- package/plugins/datasets/src/initialise/DatasetsInit.jsx +17 -7
- package/plugins/datasets/src/initialise/DatasetsInit.test.jsx +262 -0
- package/plugins/datasets/src/initialise/initialiseDatasets.js +16 -4
- package/plugins/datasets/src/initialise/initialiseDatasets.test.js +42 -0
- package/plugins/datasets/src/manifest.js +0 -38
- package/plugins/datasets/src/manifest.test.js +49 -0
- package/plugins/datasets/src/reducers/datasetsToMenu.test.js +54 -1
- package/plugins/datasets/src/reducers/pluginState.js +3 -16
- package/plugins/datasets/src/reducers/pluginState.test.js +23 -0
- package/plugins/datasets/src/registry/dataset.js +13 -1
- package/plugins/datasets/src/registry/dataset.test.js +61 -0
- package/plugins/datasets/src/registry/datasetRegistry.js +12 -6
- package/plugins/datasets/src/registry/datasetRegistry.test.js +16 -4
- package/plugins/datasets/src/registry/isVisibleWhen.js +6 -3
- package/plugins/datasets/src/registry/isVisibleWhen.test.js +11 -1
- package/plugins/datasets/src/utils/bbox.test.js +125 -0
- package/plugins/draw/dist/esm/im-draw-ml-adapter.js +1 -1
- package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/esm/im-draw-plugin.js +1 -1
- package/plugins/draw/dist/esm/index.js +1 -1
- package/plugins/draw/dist/umd/im-draw-ml-adapter.js +2 -1
- package/plugins/draw/dist/umd/im-draw-ml-adapter.js.LICENSE.txt +1 -0
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/umd/im-draw-plugin.js +1 -1
- package/plugins/draw/dist/umd/index.js +1 -1
- package/plugins/draw/src/DrawInit.jsx +10 -2
- package/plugins/draw/src/DrawInit.test.jsx +51 -10
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +91 -11
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.test.js +246 -4
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.js +109 -14
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.test.js +174 -11
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.test.js +12 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.js +9 -1
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.test.js +24 -3
- package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.js +194 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.test.js +457 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/__helpers__/harness.js +102 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.js +82 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.test.js +83 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.js +55 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.test.js +62 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.js +138 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.test.js +197 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.js +92 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.test.js +101 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.js +46 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.test.js +42 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode.js +151 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode.test.js +156 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.js +4 -27
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.test.js +24 -39
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.js +4 -5
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.js +20 -23
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.test.js +25 -5
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.js +6 -36
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.test.js +7 -49
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.js +3 -53
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.test.js +8 -33
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.js +29 -72
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.test.js +10 -3
- package/plugins/draw/src/adapters/maplibre/pointSymbolImages.js +158 -0
- package/plugins/draw/src/adapters/maplibre/pointSymbolImages.test.js +314 -0
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.js +6 -1
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.test.js +21 -0
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.js +6 -2
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.test.js +12 -0
- package/plugins/draw/src/adapters/maplibre/styles.js +38 -11
- package/plugins/draw/src/adapters/maplibre/styles.test.js +39 -0
- package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.js +79 -0
- package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.test.js +135 -0
- package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.js +42 -0
- package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.test.js +85 -0
- package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.js +34 -0
- package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.test.js +78 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.js +16 -3
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.test.js +21 -3
- package/plugins/draw/src/adapters/maplibre/utils/snapMovement.js +59 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapMovement.test.js +73 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.js +33 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.test.js +70 -0
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.js +27 -2
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.test.js +93 -0
- package/plugins/draw/src/adapters/openlayers/__helpers__/harness.js +9 -0
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.js +19 -1
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.test.js +34 -1
- package/plugins/draw/src/adapters/openlayers/core/styles.js +73 -1
- package/plugins/draw/src/adapters/openlayers/core/styles.test.js +122 -3
- package/plugins/draw/src/adapters/openlayers/draw/DrawMode.test.js +12 -0
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +8 -2
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +31 -0
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +9 -5
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +33 -0
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.js +5 -3
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.test.js +11 -0
- package/plugins/draw/src/adapters/openlayers/edit/nudge.js +10 -3
- package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +24 -1
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.js +18 -13
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.test.js +46 -1
- package/plugins/draw/src/adapters/openlayers/olDraw.js +11 -4
- package/plugins/draw/src/adapters/openlayers/olDraw.test.js +48 -6
- package/plugins/draw/src/adapters/openlayers/point/drawPointMode.js +156 -0
- package/plugins/draw/src/adapters/openlayers/point/drawPointMode.test.js +246 -0
- package/plugins/draw/src/adapters/openlayers/point/editPointMode.js +267 -0
- package/plugins/draw/src/adapters/openlayers/point/editPointMode.test.js +327 -0
- package/plugins/draw/src/adapters/openlayers/point/pointDragInteraction.js +71 -0
- package/plugins/draw/src/adapters/openlayers/point/pointOps.js +34 -0
- package/plugins/draw/src/adapters/openlayers/point/pointOps.test.js +32 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.js +82 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.test.js +107 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.js +83 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.test.js +214 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapIndicator.js +3 -1
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.js +6 -9
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.test.js +15 -4
- package/plugins/draw/src/adapters/openlayers/utils/olCoords.js +1 -1
- package/plugins/draw/src/api/addFeature.js +16 -1
- package/plugins/draw/src/api/addFeature.test.js +62 -5
- package/plugins/draw/src/api/createNewShape.js +41 -0
- package/plugins/draw/src/api/createNewShape.test.js +63 -0
- package/plugins/draw/src/api/editFeature.js +7 -4
- package/plugins/draw/src/api/editFeature.test.js +16 -0
- package/plugins/draw/src/api/newLine.js +2 -40
- package/plugins/draw/src/api/newPoint.js +42 -0
- package/plugins/draw/src/api/newPoint.test.js +117 -0
- package/plugins/draw/src/api/newPolygon.js +2 -40
- package/plugins/draw/src/api/setStyle.js +43 -0
- package/plugins/draw/src/api/setStyle.test.js +75 -0
- package/plugins/draw/src/events.js +17 -10
- package/plugins/draw/src/events.test.js +14 -0
- package/plugins/draw/src/manifest.js +18 -6
- package/plugins/draw/src/manifest.test.js +32 -1
- package/plugins/draw/src/reducer.test.js +7 -0
- package/plugins/draw/src/utils/stripInternalProperties.js +19 -0
- package/plugins/draw/src/utils/stripInternalProperties.test.js +50 -0
- package/plugins/draw/src/utils/symbolKeys.js +5 -0
- package/plugins/draw/src/validation/liveDrawChecks.test.js +54 -0
- package/plugins/draw/src/validation/liveStroke.test.js +64 -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/dist/umd/index.js +1 -1
- package/plugins/interact/src/hooks/useHighlightSync.js +33 -12
- package/plugins/interact/src/hooks/useHighlightSync.test.js +96 -25
- package/plugins/interact/src/manifest.js +1 -1
- package/plugins/interact/src/utils/featureQueries.js +18 -0
- package/plugins/interact/src/utils/featureQueries.test.js +15 -0
- package/plugins/interact/src/utils/spatial.js +9 -3
- package/plugins/interact/src/utils/spatial.test.js +12 -1
- package/plugins/map-key/dist/css/index.css +68 -0
- package/plugins/map-key/dist/esm/im-map-key-plugin.js +1 -0
- package/plugins/map-key/dist/esm/index.js +1 -0
- package/plugins/map-key/dist/umd/im-map-key-plugin.js +2 -0
- package/plugins/map-key/dist/umd/im-map-key-plugin.js.LICENSE.txt +1 -0
- package/plugins/map-key/dist/umd/index.js +2 -0
- package/plugins/map-key/dist/umd/index.js.LICENSE.txt +1 -0
- package/plugins/map-key/package.json +28 -0
- package/plugins/map-key/src/components/Key/EmptyKey.jsx +7 -0
- package/plugins/{datasets → map-key}/src/components/Key/EmptyKey.test.jsx +2 -2
- package/plugins/map-key/src/components/Key/Key.jsx +43 -0
- package/plugins/{datasets → map-key}/src/components/Key/Key.module.scss +21 -14
- package/plugins/map-key/src/components/Key/Key.test.jsx +73 -0
- package/plugins/map-key/src/components/Key/KeyGroupItem.jsx +16 -0
- package/plugins/map-key/src/components/Key/KeyGroupItem.test.jsx +53 -0
- package/plugins/map-key/src/components/Key/KeyItem.jsx +26 -0
- package/plugins/map-key/src/components/Key/KeyItem.test.jsx +67 -0
- package/plugins/map-key/src/components/Key/KeySvg.jsx +41 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvg.test.jsx +14 -12
- package/plugins/map-key/src/components/Key/KeySvgLine.jsx +22 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgLine.test.jsx +1 -1
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgPattern.jsx +7 -2
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgPattern.test.jsx +8 -2
- package/plugins/map-key/src/components/Key/KeySvgRect.jsx +26 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgRect.test.jsx +1 -1
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgSymbol.jsx +8 -3
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgSymbol.test.jsx +17 -6
- package/plugins/map-key/src/components/Key/MapKey.jsx +53 -0
- package/plugins/map-key/src/components/Key/MapKey.test.jsx +147 -0
- package/plugins/{datasets → map-key}/src/components/Key/svgProperties.js +2 -2
- package/plugins/{datasets → map-key}/src/components/Key/svgProperties.test.js +2 -2
- package/plugins/map-key/src/index.js +13 -0
- package/plugins/map-key/src/index.test.js +31 -0
- package/plugins/map-key/src/initialise/MapKeyInit.jsx +15 -0
- package/plugins/map-key/src/initialise/MapKeyInit.test.jsx +39 -0
- package/plugins/map-key/src/manifest.js +30 -0
- package/plugins/map-key/src/manifest.test.js +63 -0
- package/plugins/map-key/src/mapKey.scss +4 -0
- package/plugins/map-key/src/registry/getDatasetRegistry.js +6 -0
- package/plugins/map-key/src/registry/getDatasetRegistry.test.js +23 -0
- package/plugins/map-key/src/registry/index.js +3 -0
- package/plugins/map-key/src/registry/index.test.js +15 -0
- package/plugins/menu/dist/css/index.css +80 -0
- package/plugins/menu/dist/esm/im-menu-plugin.js +1 -0
- package/plugins/menu/dist/esm/index.js +1 -0
- package/plugins/menu/dist/umd/im-menu-plugin.js +1 -0
- package/plugins/menu/dist/umd/index.js +2 -0
- package/plugins/menu/dist/umd/index.js.LICENSE.txt +1 -0
- package/plugins/menu/package.json +28 -0
- package/plugins/menu/src/components/Menu/CheckboxGroupWrapper.jsx +21 -0
- package/plugins/menu/src/components/Menu/GroupLegend.jsx +17 -0
- package/plugins/menu/src/components/Menu/GroupLegend.test.jsx +68 -0
- package/plugins/menu/src/components/Menu/Menu.jsx +19 -0
- package/plugins/menu/src/components/Menu/Menu.module.scss +90 -0
- package/plugins/menu/src/components/Menu/Menu.test.jsx +161 -0
- package/plugins/menu/src/components/Menu/MenuCheckbox.jsx +35 -0
- package/plugins/menu/src/components/Menu/MenuCheckbox.test.jsx +93 -0
- package/plugins/menu/src/components/Menu/MenuGroup.jsx +11 -0
- package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.jsx → menu/src/components/Menu/MenuRadio.jsx} +6 -4
- package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.test.jsx → menu/src/components/Menu/MenuRadio.test.jsx} +18 -18
- package/plugins/menu/src/components/Menu/RadioGroupWrapper.jsx +32 -0
- package/plugins/{datasets/src/components/LayersMenu/LayersRadioGroupWrapper.test.jsx → menu/src/components/Menu/RadioGroupWrapper.test.jsx} +21 -21
- package/plugins/menu/src/components/Menu/useIdPrefix.jsx +4 -0
- package/plugins/menu/src/index.js +13 -0
- package/plugins/menu/src/index.test.js +31 -0
- package/plugins/menu/src/initialise/MenuInit.jsx +24 -0
- package/plugins/menu/src/initialise/MenuInit.test.jsx +50 -0
- package/plugins/menu/src/initialise/createMenu.js +10 -0
- package/plugins/menu/src/initialise/createMenu.test.js +37 -0
- package/plugins/menu/src/manifest.js +35 -0
- package/plugins/menu/src/manifest.test.js +63 -0
- package/plugins/menu/src/menu.scss +4 -0
- package/plugins/menu/src/reducers/menuStateReducer.test.js +38 -0
- package/plugins/menu/src/reducers/pluginState.js +35 -0
- package/plugins/menu/src/reducers/pluginState.test.js +57 -0
- package/plugins/menu/src/registry/getDatasetRegistry.js +9 -0
- package/plugins/menu/src/registry/getDatasetRegistry.test.js +23 -0
- package/plugins/menu/src/registry/index.js +3 -0
- package/plugins/menu/src/registry/index.test.js +15 -0
- package/plugins/menu/src/registry/isVisibleWhen.js +43 -0
- package/plugins/menu/src/registry/isVisibleWhen.test.js +48 -0
- 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 -3
- package/plugins/search/src/components/Form/Form.module.scss +9 -20
- package/plugins/search/src/index.js +0 -1
- package/plugins/search/src/index.test.js +2 -4
- package/plugins/search/src/manifest.js +1 -1
- package/plugins/search/src/search.scss +17 -16
- package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/index.js +1 -1
- package/providers/beta/openlayers/src/openlayersProvider.js +4 -1
- package/providers/beta/openlayers/src/openlayersProvider.test.js +18 -0
- package/providers/beta/openlayers/src/utils/highlightFeatures.js +52 -5
- package/providers/beta/openlayers/src/utils/highlightFeatures.test.js +176 -0
- package/providers/beta/openlayers/src/utils/symbolImages.js +40 -0
- package/providers/beta/openlayers/src/utils/symbolImages.test.js +58 -0
- package/providers/maplibre/dist/esm/im-maplibre-provider.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/utils/drawLayerBuckets.js +14 -0
- package/providers/maplibre/src/utils/drawLayerBuckets.test.js +18 -0
- package/providers/maplibre/src/utils/highlightFeatures.js +80 -55
- package/providers/maplibre/src/utils/highlightFeatures.test.js +156 -17
- package/providers/maplibre/src/utils/hoverCursor.js +30 -14
- package/providers/maplibre/src/utils/hoverCursor.test.js +35 -0
- package/providers/maplibre/src/utils/patternImages.js +1 -1
- package/providers/maplibre/src/utils/symbolImages.js +41 -3
- package/providers/maplibre/src/utils/symbolImages.test.js +40 -6
- package/rollup.esm.mjs +11 -1
- package/src/App/components/Actions/Actions.test.jsx +47 -0
- package/src/App/components/Hints/Hints.test.jsx +13 -0
- package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +25 -23
- package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +22 -5
- package/src/App/components/MoveControls/MoveControls.module.scss +41 -10
- package/src/App/components/Panel/Panel.jsx +65 -8
- package/src/App/components/Panel/Panel.module.scss +49 -15
- package/src/App/components/Panel/Panel.test.jsx +114 -1
- package/src/App/components/Tabs/Tabs.jsx +43 -17
- package/src/App/components/Tabs/Tabs.module.scss +32 -5
- package/src/App/components/Tabs/Tabs.test.jsx +40 -10
- package/src/App/components/Tooltip/Tooltip.test.jsx +12 -14
- package/src/App/hooks/useFeatureItems.js +16 -7
- package/src/App/hooks/useFeatureItems.test.js +57 -0
- package/src/App/hooks/useLayoutMeasurements.js +95 -84
- package/src/App/hooks/useLayoutMeasurements.test.js +61 -1
- package/src/App/layout/Layout.jsx +9 -10
- package/src/App/layout/Layout.test.jsx +2 -0
- package/src/App/layout/layout.module.scss +31 -17
- package/src/App/registry/pluginRegistry.js +1 -1
- package/src/App/registry/pluginRegistry.test.js +12 -0
- package/src/App/renderer/HtmlElementHost.jsx +18 -5
- package/src/App/renderer/HtmlElementHost.test.jsx +54 -0
- package/src/App/renderer/groupByKey.js +28 -0
- package/src/App/renderer/groupByKey.test.js +39 -0
- package/src/App/renderer/groupIntoTabs.js +42 -0
- package/src/App/renderer/groupIntoTabs.test.js +95 -0
- package/src/App/renderer/mapButtons.js +97 -129
- package/src/App/renderer/mapButtons.test.js +67 -83
- package/src/App/renderer/mapControls.js +4 -1
- package/src/App/renderer/mapControls.test.js +32 -0
- package/src/App/renderer/mapPanels.js +53 -11
- package/src/App/renderer/mapPanels.test.js +95 -2
- package/src/App/renderer/orderItems.js +28 -0
- package/src/App/renderer/orderItems.test.js +50 -0
- package/src/App/renderer/slotAggregator.js +2 -18
- package/src/App/renderer/slotHelpers.js +3 -1
- package/src/App/renderer/slotHelpers.test.js +5 -0
- package/src/App/renderer/slots.js +3 -0
- package/src/App/store/AppProvider.jsx +1 -0
- package/src/App/store/appDispatchMiddleware.js +1 -1
- package/src/App/store/appDispatchMiddleware.test.js +8 -0
- package/src/config/appConfig.js +2 -2
- package/src/config/events.js +2 -0
- package/src/scss/settings/_colors.scss +1 -0
- package/src/scss/settings/_dimensions.scss +2 -3
- package/src/scss/settings/_transition.scss +1 -1
- package/src/services/eventBus.js +44 -0
- package/src/services/eventBus.test.js +83 -0
- package/src/services/symbolRegistry.js +1 -1
- package/src/types.js +31 -3
- package/webpack.umd.mjs +2 -0
- package/plugins/datasets/src/components/Key/EmptyKey.jsx +0 -7
- package/plugins/datasets/src/components/Key/Key.jsx +0 -50
- package/plugins/datasets/src/components/Key/KeyGroupItem.jsx +0 -18
- package/plugins/datasets/src/components/Key/KeyItem.jsx +0 -23
- package/plugins/datasets/src/components/Key/KeySvg.jsx +0 -22
- package/plugins/datasets/src/components/Key/KeySvgLine.jsx +0 -19
- package/plugins/datasets/src/components/Key/KeySvgRect.jsx +0 -22
- package/plugins/datasets/src/components/LayersMenu/LayersRadioGroupWrapper.jsx +0 -41
- /package/plugins/{datasets → menu}/src/reducers/menuStateReducer.js +0 -0
- /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.js +0 -0
- /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.test.js +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { stringToKebab } from '../../utils/stringToKebab.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Partitions a list of items into buckets, keyed by `keyFn(item)` kebab-cased — two items
|
|
5
|
+
* share a bucket by producing the same key (so 'Map Size' and 'map size' merge). Items for
|
|
6
|
+
* which `keyFn` returns a falsy value all share one `null`-keyed bucket. Bucket iteration
|
|
7
|
+
* order follows first-encountered order, same as `Map`.
|
|
8
|
+
*
|
|
9
|
+
* Shared by `groupIntoTabs` (panel/control tabs) and `mapButtons` (button groups) — the two
|
|
10
|
+
* callers differ only in what happens *after* bucketing (how a bucket's own order/label is
|
|
11
|
+
* derived), not in how items get partitioned.
|
|
12
|
+
*
|
|
13
|
+
* @returns {Map<string|null, object[]>}
|
|
14
|
+
*/
|
|
15
|
+
export function groupByKey ({ items, keyFn }) {
|
|
16
|
+
const buckets = new Map()
|
|
17
|
+
|
|
18
|
+
for (const item of items) {
|
|
19
|
+
const raw = keyFn(item)
|
|
20
|
+
const key = raw ? stringToKebab(raw) : null
|
|
21
|
+
if (!buckets.has(key)) {
|
|
22
|
+
buckets.set(key, [])
|
|
23
|
+
}
|
|
24
|
+
buckets.get(key).push(item)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return buckets
|
|
28
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { groupByKey } from './groupByKey.js'
|
|
2
|
+
|
|
3
|
+
describe('groupByKey', () => {
|
|
4
|
+
it('returns one bucket per distinct key', () => {
|
|
5
|
+
const items = [{ id: 'a', k: 'Styles' }, { id: 'b', k: 'Sizes' }]
|
|
6
|
+
const buckets = groupByKey({ items, keyFn: item => item.k })
|
|
7
|
+
expect([...buckets.keys()].sort()).toEqual(['sizes', 'styles'])
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('lowercases the key, hyphenating only camelCase-style boundaries (stringToKebab)', () => {
|
|
11
|
+
const items = [{ id: 'a', k: 'mapSize' }]
|
|
12
|
+
const buckets = groupByKey({ items, keyFn: item => item.k })
|
|
13
|
+
expect([...buckets.keys()]).toEqual(['map-size'])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('merges items whose key differs only by case/whitespace into one bucket', () => {
|
|
17
|
+
const items = [{ id: 'a', k: 'Map Size' }, { id: 'b', k: 'map size' }]
|
|
18
|
+
const buckets = groupByKey({ items, keyFn: item => item.k })
|
|
19
|
+
expect(buckets.size).toBe(1)
|
|
20
|
+
expect([...buckets.values()][0]).toHaveLength(2)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('puts items with no key into one null-keyed bucket', () => {
|
|
24
|
+
const items = [{ id: 'a' }, { id: 'b' }]
|
|
25
|
+
const buckets = groupByKey({ items, keyFn: () => null })
|
|
26
|
+
expect(buckets.size).toBe(1)
|
|
27
|
+
expect(buckets.get(null)).toHaveLength(2)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('keeps items within a bucket in their original relative order', () => {
|
|
31
|
+
const items = [{ id: 'a', k: 'g' }, { id: 'b', k: 'g' }, { id: 'c', k: 'g' }]
|
|
32
|
+
const buckets = groupByKey({ items, keyFn: item => item.k })
|
|
33
|
+
expect(buckets.get('g').map(i => i.id)).toEqual(['a', 'b', 'c'])
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('returns an empty map for an empty list', () => {
|
|
37
|
+
expect(groupByKey({ items: [], keyFn: () => 'x' }).size).toBe(0)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { orderItems } from './orderItems.js'
|
|
2
|
+
import { groupByKey } from './groupByKey.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Groups a flat list of `{ id, order, tab, element }` items into tabs, when warranted.
|
|
6
|
+
*
|
|
7
|
+
* Items are partitioned by `tab` via `groupByKey` — two items share a tab by producing the
|
|
8
|
+
* same kebab-cased key. Items with no `tab` share one fallback bucket. Returns `null` when
|
|
9
|
+
* there's one bucket or fewer, so the caller can fall back to flat rendering unchanged.
|
|
10
|
+
*
|
|
11
|
+
* Each bucket's members are ordered via `orderItems`; the resulting tab's own `order` and
|
|
12
|
+
* displayed `name` both come from whichever member ends up first in that ordering — the same
|
|
13
|
+
* item that "wins" the position also wins the label, so there's nothing separate to reconcile
|
|
14
|
+
* when members disagree. The tabs themselves are then ordered the same way, via `orderItems`
|
|
15
|
+
* again — no new ordering concept, just the existing one applied twice.
|
|
16
|
+
*
|
|
17
|
+
* This derived-order approach is safe here because tabs are mutually exclusive — only the
|
|
18
|
+
* active tab's content is ever visible, so a "losing" member's own order never has to compete
|
|
19
|
+
* against anything outside its own tab at the same time. `mapButtons`'s button groups don't
|
|
20
|
+
* have that property (every group renders simultaneously), so they don't derive a group's
|
|
21
|
+
* order from its members the same way — see `mapButtons.js`.
|
|
22
|
+
*/
|
|
23
|
+
export function groupIntoTabs ({ items, fallbackLabel }) {
|
|
24
|
+
const buckets = groupByKey({ items, keyFn: item => item.tab })
|
|
25
|
+
|
|
26
|
+
if (buckets.size <= 1) {
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const tabs = Array.from(buckets.entries()).map(([key, members]) => {
|
|
31
|
+
const orderedMembers = orderItems(members)
|
|
32
|
+
const first = orderedMembers[0]
|
|
33
|
+
return {
|
|
34
|
+
id: key ?? 'default',
|
|
35
|
+
order: first.order,
|
|
36
|
+
name: key ? first.tab : fallbackLabel,
|
|
37
|
+
items: orderedMembers
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return orderItems(tabs)
|
|
42
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { groupIntoTabs } from './groupIntoTabs.js'
|
|
2
|
+
|
|
3
|
+
describe('groupIntoTabs', () => {
|
|
4
|
+
it('returns null when there are no items', () => {
|
|
5
|
+
expect(groupIntoTabs({ items: [], fallbackLabel: 'Panel' })).toBeNull()
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
it('returns null when all items share the same tab', () => {
|
|
9
|
+
const items = [
|
|
10
|
+
{ id: 'a', order: 0, tab: 'Styles' },
|
|
11
|
+
{ id: 'b', order: 0, tab: 'Styles' }
|
|
12
|
+
]
|
|
13
|
+
expect(groupIntoTabs({ items, fallbackLabel: 'Panel' })).toBeNull()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('returns null when no items have a tab', () => {
|
|
17
|
+
const items = [{ id: 'a', order: 0 }, { id: 'b', order: 0 }]
|
|
18
|
+
expect(groupIntoTabs({ items, fallbackLabel: 'Panel' })).toBeNull()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('partitions items into distinct tabs', () => {
|
|
22
|
+
const items = [
|
|
23
|
+
{ id: 'a', order: 0, tab: 'Styles' },
|
|
24
|
+
{ id: 'b', order: 0, tab: 'Sizes' }
|
|
25
|
+
]
|
|
26
|
+
const tabs = groupIntoTabs({ items, fallbackLabel: 'Panel' })
|
|
27
|
+
expect(tabs.map(t => t.name).sort()).toEqual(['Sizes', 'Styles'])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('groups untagged items into one fallback bucket named after fallbackLabel', () => {
|
|
31
|
+
const items = [
|
|
32
|
+
{ id: 'a', order: 0, tab: 'Styles' },
|
|
33
|
+
{ id: 'b', order: 0 },
|
|
34
|
+
{ id: 'c', order: 0 }
|
|
35
|
+
]
|
|
36
|
+
const tabs = groupIntoTabs({ items, fallbackLabel: 'Map styles' })
|
|
37
|
+
const fallback = tabs.find(t => t.name === 'Map styles')
|
|
38
|
+
expect(fallback.items.map(i => i.id).sort()).toEqual(['b', 'c'])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('merges items whose tab differs only by case/whitespace into one bucket', () => {
|
|
42
|
+
const items = [
|
|
43
|
+
{ id: 'a', order: 0, tab: 'Map Size' },
|
|
44
|
+
{ id: 'b', order: 1, tab: 'map size' },
|
|
45
|
+
{ id: 'c', order: 0, tab: 'Styles' }
|
|
46
|
+
]
|
|
47
|
+
const tabs = groupIntoTabs({ items, fallbackLabel: 'Panel' })
|
|
48
|
+
expect(tabs).toHaveLength(2)
|
|
49
|
+
const merged = tabs.find(t => t.items.length === 2)
|
|
50
|
+
expect(merged.items.map(i => i.id)).toEqual(expect.arrayContaining(['a', 'b']))
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it("uses the winning (lowest-order) member's raw tab string as the displayed name", () => {
|
|
54
|
+
const items = [
|
|
55
|
+
{ id: 'a', order: 2, tab: 'MAP SIZE' },
|
|
56
|
+
{ id: 'b', order: 1, tab: 'map size' }, // lower order wins the label
|
|
57
|
+
{ id: 'c', order: 0, tab: 'Styles' }
|
|
58
|
+
]
|
|
59
|
+
const tabs = groupIntoTabs({ items, fallbackLabel: 'Panel' })
|
|
60
|
+
const merged = tabs.find(t => t.items.length === 2)
|
|
61
|
+
expect(merged.name).toBe('map size')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it("derives a tab's order from its first ordered member", () => {
|
|
65
|
+
const items = [
|
|
66
|
+
{ id: 'a', order: 5, tab: 'Styles' },
|
|
67
|
+
{ id: 'b', order: 1, tab: 'Styles' },
|
|
68
|
+
{ id: 'c', order: 0, tab: 'Sizes' }
|
|
69
|
+
]
|
|
70
|
+
const tabs = groupIntoTabs({ items, fallbackLabel: 'Panel' })
|
|
71
|
+
const stylesTab = tabs.find(t => t.name === 'Styles')
|
|
72
|
+
expect(stylesTab.order).toBe(1)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('orders tabs among themselves using the derived order', () => {
|
|
76
|
+
const items = [
|
|
77
|
+
{ id: 'a', order: 2, tab: 'Second' },
|
|
78
|
+
{ id: 'b', order: 1, tab: 'First' }
|
|
79
|
+
]
|
|
80
|
+
const tabs = groupIntoTabs({ items, fallbackLabel: 'Panel' })
|
|
81
|
+
expect(tabs.map(t => t.name)).toEqual(['First', 'Second'])
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('orders items within a tab using orderItems', () => {
|
|
85
|
+
const items = [
|
|
86
|
+
{ id: 'a', order: 0, tab: 'Styles' },
|
|
87
|
+
{ id: 'b', order: 1, tab: 'Styles' },
|
|
88
|
+
{ id: 'c', order: 0, tab: 'Sizes' }
|
|
89
|
+
]
|
|
90
|
+
const tabs = groupIntoTabs({ items, fallbackLabel: 'Panel' })
|
|
91
|
+
const stylesTab = tabs.find(t => t.name === 'Styles')
|
|
92
|
+
// b (order 1) is spliced ahead of the unordered item a
|
|
93
|
+
expect(stylesTab.items.map(i => i.id)).toEqual(['b', 'a'])
|
|
94
|
+
})
|
|
95
|
+
})
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/core/renderers/mapButtons.js
|
|
2
2
|
import { MapButton } from '../components/MapButton/MapButton.jsx'
|
|
3
3
|
import { allowedSlots } from './slots.js'
|
|
4
|
+
import { groupByKey } from './groupByKey.js'
|
|
5
|
+
import { orderItems } from './orderItems.js'
|
|
4
6
|
import { logger } from '../../services/logger.js'
|
|
5
7
|
|
|
6
8
|
function getMatchingButtons ({ appState, buttonConfig, slot, evaluateProp }) {
|
|
@@ -49,8 +51,7 @@ function getMatchingButtons ({ appState, buttonConfig, slot, evaluateProp }) {
|
|
|
49
51
|
})
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
function createButtonClickHandler (
|
|
53
|
-
const [, config] = btn
|
|
54
|
+
function createButtonClickHandler (config, appState, evaluateProp) {
|
|
54
55
|
const isPanelOpen = !!(config.panelId && appState.openPanels[config.panelId])
|
|
55
56
|
const isToggle = config.isPressed !== undefined || !!config.pressedWhen
|
|
56
57
|
|
|
@@ -75,47 +76,6 @@ function createButtonClickHandler (btn, appState, evaluateProp) {
|
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
/**
|
|
79
|
-
* Resolves the group name from a button config's group property.
|
|
80
|
-
* Accepts either the new object form `{ name, label?, order? }` or a deprecated plain string.
|
|
81
|
-
* @param {string|{name: string, label?: string, order?: number}|null|undefined} group
|
|
82
|
-
* @returns {string|null}
|
|
83
|
-
*/
|
|
84
|
-
function resolveGroupName (group) {
|
|
85
|
-
if (group == null) {
|
|
86
|
-
return null
|
|
87
|
-
}
|
|
88
|
-
return typeof group === 'string' ? group : (group.name ?? null)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Resolves the accessible label for a group.
|
|
93
|
-
* Uses `label` if provided, otherwise falls back to `name`.
|
|
94
|
-
* @param {string|{name: string, label?: string, order?: number}|null|undefined} group
|
|
95
|
-
* @returns {string}
|
|
96
|
-
*/
|
|
97
|
-
function resolveGroupLabel (group) {
|
|
98
|
-
if (!group) {
|
|
99
|
-
return ''
|
|
100
|
-
}
|
|
101
|
-
if (typeof group === 'string') {
|
|
102
|
-
return group
|
|
103
|
-
}
|
|
104
|
-
return group.label ?? group.name ?? ''
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Resolves the slot-level order for a group.
|
|
109
|
-
* @param {string|{name: string, label?: string, order?: number}|null|undefined} group
|
|
110
|
-
* @returns {number}
|
|
111
|
-
*/
|
|
112
|
-
function resolveGroupOrder (group) {
|
|
113
|
-
if (!group || typeof group === 'string') {
|
|
114
|
-
return 0
|
|
115
|
-
}
|
|
116
|
-
return group.order ?? 0
|
|
117
|
-
}
|
|
118
|
-
|
|
119
79
|
function applySlotExclusivity (matching, appState) {
|
|
120
80
|
let exclusivePluginId = null
|
|
121
81
|
|
|
@@ -134,15 +94,33 @@ function applySlotExclusivity (matching, appState) {
|
|
|
134
94
|
return matching.filter(([_, config]) => config.pluginId === exclusivePluginId)
|
|
135
95
|
}
|
|
136
96
|
|
|
137
|
-
|
|
138
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Builds the props for a <SlotButton>. isHidden/variant are included here (not just derived
|
|
99
|
+
* inside SlotButton) because Actions.jsx also reads them directly off the 'actions' slot's
|
|
100
|
+
* immediate children via React.Children.toArray — they need to be top-level props on whatever
|
|
101
|
+
* element ends up there, not just used internally to build MapButton.
|
|
102
|
+
*/
|
|
103
|
+
const slotButtonProps = ({ buttonId, config, appState, appConfig, evaluateProp }) => ({
|
|
104
|
+
buttonId,
|
|
105
|
+
config,
|
|
106
|
+
appState,
|
|
107
|
+
appConfig,
|
|
108
|
+
evaluateProp,
|
|
109
|
+
isHidden: appState.hiddenButtons.has(buttonId),
|
|
110
|
+
variant: config.variant
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Renders a single button's MapButton element for its slot, computing its click handler
|
|
115
|
+
* and derived state (panel-open, showLabel fallback, etc.) from its config and appState.
|
|
116
|
+
*/
|
|
117
|
+
function SlotButton ({ buttonId, config, appState, appConfig, evaluateProp }) {
|
|
139
118
|
const bpConfig = config[appState.breakpoint] ?? {}
|
|
140
|
-
const handleClick = createButtonClickHandler(
|
|
119
|
+
const handleClick = createButtonClickHandler(config, appState, evaluateProp)
|
|
141
120
|
const isPanelOpen = !!(config.panelId && appState.openPanels[config.panelId])
|
|
142
121
|
|
|
143
122
|
return (
|
|
144
123
|
<MapButton
|
|
145
|
-
key={buttonId}
|
|
146
124
|
buttonId={buttonId}
|
|
147
125
|
iconId={evaluateProp(config.iconId, config.pluginId)}
|
|
148
126
|
iconSvgContent={evaluateProp(config.iconSvgContent, config.pluginId)}
|
|
@@ -164,6 +142,69 @@ function renderButton ({ btn, appState, appConfig, evaluateProp }) {
|
|
|
164
142
|
)
|
|
165
143
|
}
|
|
166
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Ungrouped buttons — one result item per button, ordered by its own breakpoint-level
|
|
147
|
+
* slot position.
|
|
148
|
+
*/
|
|
149
|
+
function buildUngroupedItems (members, ctx) {
|
|
150
|
+
return members.map(([buttonId, config]) => ({
|
|
151
|
+
id: buttonId,
|
|
152
|
+
type: 'button',
|
|
153
|
+
order: config[ctx.breakpoint]?.order ?? 0,
|
|
154
|
+
element: <SlotButton key={buttonId} {...slotButtonProps({ buttonId, config, ...ctx })} />
|
|
155
|
+
}))
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* A named group's own slot order/label come from whichever member is encountered first —
|
|
160
|
+
* unlike panel tabs (see groupIntoTabs.js), this is never derived from members' own order:
|
|
161
|
+
* groups render simultaneously (not one-at-a-time like tabs), so an unspecified order
|
|
162
|
+
* defaults to 0, same as every other slot item, rather than borrowing a member's position.
|
|
163
|
+
* A single-member group still degrades to a plain button (no wrapping container), but keeps
|
|
164
|
+
* the group's own slot order rather than falling back to its own breakpoint-level order.
|
|
165
|
+
*/
|
|
166
|
+
function buildGroupItem (key, members, ctx) {
|
|
167
|
+
const [, firstConfig] = members[0]
|
|
168
|
+
const order = firstConfig.group.slotOrder ?? 0
|
|
169
|
+
|
|
170
|
+
/* istanbul ignore next */
|
|
171
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
172
|
+
const distinctOrders = new Set(members.map(([, config]) => config.group?.slotOrder ?? 0))
|
|
173
|
+
if (distinctOrders.size > 1) {
|
|
174
|
+
logger.warn(`Button group "${firstConfig.group.label}" has inconsistent slotOrder values (${[...distinctOrders].join(', ')}) across its members — using ${order} (the first member's).`)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (members.length < 2) {
|
|
179
|
+
const [buttonId, config] = members[0]
|
|
180
|
+
return {
|
|
181
|
+
id: buttonId,
|
|
182
|
+
type: 'button',
|
|
183
|
+
order,
|
|
184
|
+
element: <SlotButton key={buttonId} {...slotButtonProps({ buttonId, config, ...ctx })} />
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Order members within the group the same way panel/control content orders within a tab
|
|
189
|
+
const sorted = orderItems(members.map(([buttonId, config]) => ({
|
|
190
|
+
id: buttonId,
|
|
191
|
+
order: config[ctx.breakpoint]?.order ?? 0,
|
|
192
|
+
buttonId,
|
|
193
|
+
config
|
|
194
|
+
})))
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
id: `group-${key}`,
|
|
198
|
+
type: 'group',
|
|
199
|
+
order,
|
|
200
|
+
element: (
|
|
201
|
+
<div key={`group-${key}`} role='group' aria-label={firstConfig.group.label} className='im-c-button-group'>{/* NOSONAR - div with role="group" is correct for a button group */}
|
|
202
|
+
{sorted.map(({ buttonId, config }) => <SlotButton key={buttonId} {...slotButtonProps({ buttonId, config, ...ctx })} />)}
|
|
203
|
+
</div>
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
167
208
|
function mapButtons ({ slot, appState, appConfig, evaluateProp }) {
|
|
168
209
|
const { buttonConfig, breakpoint } = appState
|
|
169
210
|
|
|
@@ -174,87 +215,17 @@ function mapButtons ({ slot, appState, appConfig, evaluateProp }) {
|
|
|
174
215
|
return []
|
|
175
216
|
}
|
|
176
217
|
|
|
177
|
-
// Partition
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
matching.forEach(([buttonId, config]) => {
|
|
182
|
-
const { group } = config
|
|
183
|
-
|
|
184
|
-
if (group == null) {
|
|
185
|
-
singletons.push([buttonId, config])
|
|
186
|
-
return
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/* istanbul ignore next */
|
|
190
|
-
if (process.env.NODE_ENV !== 'production' && typeof group === 'string') {
|
|
191
|
-
logger.warn(`Button "${buttonId}": group should be an object { name, label?, order? } — string groups are deprecated.`)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const name = resolveGroupName(group)
|
|
195
|
-
const label = resolveGroupLabel(group)
|
|
196
|
-
const order = resolveGroupOrder(group)
|
|
197
|
-
|
|
198
|
-
if (groupMap.has(name)) {
|
|
199
|
-
const existing = groupMap.get(name)
|
|
200
|
-
/* istanbul ignore next */
|
|
201
|
-
if (process.env.NODE_ENV !== 'production' && existing.order !== order) {
|
|
202
|
-
logger.warn(`Group "${name}" has inconsistent order values (${existing.order} vs ${order}). Using the lower value.`)
|
|
203
|
-
existing.order = Math.min(existing.order, order)
|
|
204
|
-
}
|
|
205
|
-
} else {
|
|
206
|
-
groupMap.set(name, { label, order, members: [] })
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
groupMap.get(name).members.push([buttonId, config])
|
|
210
|
-
})
|
|
218
|
+
// Partition into named groups (keyed by kebab-cased group.label) plus one ungrouped bucket
|
|
219
|
+
const buckets = groupByKey({ items: matching, keyFn: ([, config]) => config.group?.label })
|
|
220
|
+
const ctx = { breakpoint, appState, appConfig, evaluateProp }
|
|
211
221
|
|
|
212
222
|
const result = []
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
result.push({
|
|
219
|
-
id: buttonId,
|
|
220
|
-
type: 'button',
|
|
221
|
-
order,
|
|
222
|
-
element: renderButton({ btn, appState, appConfig, evaluateProp })
|
|
223
|
-
})
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
for (const [groupName, { label, order: groupOrder, members }] of groupMap) {
|
|
227
|
-
if (members.length < 2) {
|
|
228
|
-
// Singleton group: degrade to a regular button using the group's slot order
|
|
229
|
-
const btn = members[0]
|
|
230
|
-
const [buttonId, config] = btn
|
|
231
|
-
const order = groupOrder || config[breakpoint]?.order || 0
|
|
232
|
-
result.push({
|
|
233
|
-
id: buttonId,
|
|
234
|
-
type: 'button',
|
|
235
|
-
order,
|
|
236
|
-
element: renderButton({ btn, appState, appConfig, evaluateProp })
|
|
237
|
-
})
|
|
238
|
-
continue
|
|
223
|
+
for (const [key, members] of buckets) {
|
|
224
|
+
if (key === null) {
|
|
225
|
+
result.push(...buildUngroupedItems(members, ctx))
|
|
226
|
+
} else {
|
|
227
|
+
result.push(buildGroupItem(key, members, ctx))
|
|
239
228
|
}
|
|
240
|
-
|
|
241
|
-
// Sort group members by their intra-group order (breakpoint-level order prop)
|
|
242
|
-
const sorted = [...members].sort((a, b) => {
|
|
243
|
-
const orderA = a[1][breakpoint]?.order ?? 0
|
|
244
|
-
const orderB = b[1][breakpoint]?.order ?? 0
|
|
245
|
-
return orderA - orderB
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
result.push({
|
|
249
|
-
id: `group-${groupName}`,
|
|
250
|
-
type: 'group',
|
|
251
|
-
order: groupOrder,
|
|
252
|
-
element: (
|
|
253
|
-
<div key={`group-${groupName}`} role='group' aria-label={label} className='im-c-button-group'>{/* NOSONAR - div with role="group" is correct for a button group */}
|
|
254
|
-
{sorted.map(btn => renderButton({ btn, appState, appConfig, evaluateProp }))}
|
|
255
|
-
</div>
|
|
256
|
-
)
|
|
257
|
-
})
|
|
258
229
|
}
|
|
259
230
|
|
|
260
231
|
return result
|
|
@@ -264,8 +235,5 @@ export {
|
|
|
264
235
|
mapButtons,
|
|
265
236
|
getMatchingButtons,
|
|
266
237
|
applySlotExclusivity,
|
|
267
|
-
|
|
268
|
-
resolveGroupName,
|
|
269
|
-
resolveGroupLabel,
|
|
270
|
-
resolveGroupOrder
|
|
238
|
+
SlotButton
|
|
271
239
|
}
|