@defra/interactive-map 0.0.17-alpha → 0.0.18-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.
Files changed (140) hide show
  1. package/dist/css/index.css +1 -1
  2. package/dist/esm/im-core.js +1 -1
  3. package/dist/esm/im-shell.js +1 -1
  4. package/dist/umd/im-core.js +1 -1
  5. package/dist/umd/index.js +1 -1
  6. package/docs/api/context.md +53 -7
  7. package/docs/api/map-style-config.md +41 -2
  8. package/docs/api/marker-config.md +53 -11
  9. package/docs/api/symbol-config.md +160 -0
  10. package/docs/api/symbol-registry.md +115 -0
  11. package/docs/api.md +22 -19
  12. package/docs/plugins/datasets.md +105 -9
  13. package/docs/plugins/interact.md +68 -43
  14. package/docs/plugins/search.md +15 -3
  15. package/package.json +1 -1
  16. package/plugins/beta/datasets/dist/css/index.css +32 -14
  17. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  18. package/plugins/beta/datasets/dist/esm/index.js +1 -1
  19. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
  20. package/plugins/beta/datasets/dist/umd/index.js +1 -1
  21. package/plugins/beta/datasets/src/DatasetsInit.jsx +9 -4
  22. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +57 -11
  23. package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +14 -8
  24. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +155 -53
  25. package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +27 -0
  26. package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +31 -0
  27. package/plugins/beta/datasets/src/api/addDataset.js +1 -1
  28. package/plugins/beta/datasets/src/api/setData.js +4 -2
  29. package/plugins/beta/datasets/src/api/setStyle.js +2 -2
  30. package/plugins/beta/datasets/src/components/EmptyKey.jsx +7 -0
  31. package/plugins/beta/datasets/src/components/EmptyKey.test.jsx +21 -0
  32. package/plugins/beta/datasets/src/components/KeySvg.jsx +24 -0
  33. package/plugins/beta/datasets/src/components/KeySvgLine.jsx +19 -0
  34. package/plugins/beta/datasets/src/components/KeySvgPattern.jsx +15 -0
  35. package/plugins/beta/datasets/src/components/KeySvgRect.jsx +22 -0
  36. package/plugins/beta/datasets/src/components/KeySvgSymbol.jsx +16 -0
  37. package/plugins/beta/datasets/src/components/svgProperties.js +20 -0
  38. package/plugins/beta/datasets/src/datasets.js +13 -4
  39. package/plugins/beta/datasets/src/defaults.js +4 -2
  40. package/plugins/beta/datasets/src/index.js +2 -1
  41. package/plugins/beta/datasets/src/manifest.js +1 -1
  42. package/plugins/beta/datasets/src/panels/Key.jsx +11 -89
  43. package/plugins/beta/datasets/src/panels/Key.module.scss +24 -13
  44. package/plugins/beta/datasets/src/panels/Layers.module.scss +13 -7
  45. package/plugins/beta/datasets/src/reducer.js +6 -0
  46. package/plugins/beta/datasets/src/reducers/keyReducer.js +34 -0
  47. package/plugins/beta/datasets/src/utils/mergeSublayer.js +8 -0
  48. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  49. package/plugins/beta/draw-es/src/DrawInit.jsx +3 -2
  50. package/plugins/beta/draw-ml/dist/css/index.css +21 -1
  51. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  52. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  53. package/plugins/beta/draw-ml/dist/umd/index.js +1 -1
  54. package/plugins/beta/draw-ml/src/DrawInit.jsx +4 -3
  55. package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -1
  56. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
  57. package/plugins/beta/map-styles/dist/umd/index.js +1 -1
  58. package/plugins/beta/map-styles/src/MapStyles.jsx +5 -4
  59. package/plugins/beta/map-styles/src/MapStylesInit.jsx +5 -4
  60. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  61. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  62. package/plugins/interact/dist/umd/index.js +1 -1
  63. package/plugins/interact/src/InteractInit.jsx +14 -5
  64. package/plugins/interact/src/InteractInit.test.js +26 -6
  65. package/plugins/interact/src/api/enable.test.js +7 -7
  66. package/plugins/interact/src/defaults.js +4 -6
  67. package/plugins/interact/src/events.js +9 -6
  68. package/plugins/interact/src/events.test.js +28 -4
  69. package/plugins/interact/src/hooks/useHighlightSync.js +3 -3
  70. package/plugins/interact/src/hooks/useHighlightSync.test.js +6 -6
  71. package/plugins/interact/src/hooks/useHoverCursor.js +10 -0
  72. package/plugins/interact/src/hooks/useHoverCursor.test.js +44 -0
  73. package/plugins/interact/src/hooks/useInteractionHandlers.js +111 -69
  74. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +147 -32
  75. package/plugins/interact/src/reducer.js +23 -4
  76. package/plugins/interact/src/reducer.test.js +60 -11
  77. package/plugins/interact/src/utils/buildStylesMap.js +17 -4
  78. package/plugins/interact/src/utils/buildStylesMap.test.js +16 -2
  79. package/plugins/interact/src/utils/featureQueries.js +11 -6
  80. package/plugins/interact/src/utils/featureQueries.test.js +8 -1
  81. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  82. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  83. package/plugins/search/src/Search.jsx +3 -1
  84. package/plugins/search/src/events/fetchSuggestions.js +6 -4
  85. package/plugins/search/src/events/fetchSuggestions.test.js +26 -4
  86. package/plugins/search/src/events/formHandlers.js +3 -3
  87. package/plugins/search/src/events/formHandlers.test.js +1 -1
  88. package/plugins/search/src/events/suggestionHandlers.js +2 -2
  89. package/plugins/search/src/events/suggestionHandlers.test.js +1 -1
  90. package/plugins/search/src/utils/updateMap.js +3 -3
  91. package/plugins/search/src/utils/updateMap.test.js +3 -3
  92. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  93. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  94. package/providers/maplibre/dist/umd/index.js +1 -1
  95. package/providers/maplibre/src/appEvents.js +7 -0
  96. package/providers/maplibre/src/appEvents.test.js +18 -4
  97. package/providers/maplibre/src/maplibreProvider.js +52 -0
  98. package/providers/maplibre/src/maplibreProvider.test.js +105 -1
  99. package/providers/maplibre/src/utils/highlightFeatures.js +36 -7
  100. package/providers/maplibre/src/utils/highlightFeatures.test.js +153 -96
  101. package/providers/maplibre/src/utils/hoverCursor.js +61 -0
  102. package/providers/maplibre/src/utils/hoverCursor.test.js +130 -0
  103. package/providers/maplibre/src/utils/patternImages.js +70 -0
  104. package/providers/maplibre/src/utils/patternImages.test.js +180 -0
  105. package/providers/maplibre/src/utils/queryFeatures.js +38 -16
  106. package/providers/maplibre/src/utils/queryFeatures.test.js +20 -3
  107. package/providers/maplibre/src/utils/rasteriseToImageData.js +30 -0
  108. package/providers/maplibre/src/utils/rasteriseToImageData.test.js +69 -0
  109. package/providers/maplibre/src/utils/symbolImages.js +147 -0
  110. package/providers/maplibre/src/utils/symbolImages.test.js +248 -0
  111. package/src/App/components/Markers/Markers.jsx +122 -27
  112. package/src/App/components/Markers/Markers.module.scss +0 -10
  113. package/src/App/components/Markers/Markers.test.jsx +246 -0
  114. package/src/App/hooks/useInterfaceAPI.test.js +156 -0
  115. package/src/App/hooks/useMarkersAPI.js +2 -5
  116. package/src/App/hooks/useMarkersAPI.test.js +4 -4
  117. package/src/App/layout/Layout.jsx +2 -2
  118. package/src/App/layout/Layout.test.jsx +4 -2
  119. package/src/App/store/ServiceProvider.jsx +7 -5
  120. package/src/App/store/mapActionsMap.js +4 -6
  121. package/src/App/store/mapActionsMap.test.js +3 -2
  122. package/src/App/store/mapReducer.js +2 -1
  123. package/src/config/appConfig.js +0 -6
  124. package/src/config/appConfig.test.js +1 -2
  125. package/src/config/defaults.js +0 -2
  126. package/src/config/mapTheme.js +56 -0
  127. package/src/config/patternConfig.js +16 -0
  128. package/src/config/symbolConfig.js +80 -0
  129. package/src/scss/settings/_colors.scss +0 -9
  130. package/src/services/patternRegistry.js +40 -0
  131. package/src/services/patternRegistry.test.js +48 -0
  132. package/src/services/symbolRegistry.js +113 -0
  133. package/src/services/symbolRegistry.test.js +262 -0
  134. package/src/types.js +93 -11
  135. package/src/utils/patternUtils.js +94 -0
  136. package/src/utils/patternUtils.test.js +160 -0
  137. package/src/utils/symbolUtils.js +85 -0
  138. package/src/utils/symbolUtils.test.js +156 -0
  139. package/plugins/beta/datasets/src/adapters/maplibre/patternRegistry.js +0 -48
  140. package/plugins/beta/datasets/src/styles/patterns.js +0 -157
@@ -0,0 +1,7 @@
1
+ export const EmptyKey = ({ text }) => {
2
+ return (
3
+ <div className='im-c-datasets-key'>
4
+ <p className='im-c-datasets-key__empty-message'>{text}</p>
5
+ </div>
6
+ )
7
+ }
@@ -0,0 +1,21 @@
1
+ import { render, screen } from '@testing-library/react'
2
+ import { EmptyKey } from './EmptyKey'
3
+
4
+ describe('EmptyKey', () => {
5
+ const text = 'No features available'
6
+
7
+ it('renders the wrapper div with the correct class', async () => {
8
+ const { container } = render(<EmptyKey text={text} />)
9
+ expect(container.querySelector('.im-c-datasets-key')).toBeTruthy()
10
+ })
11
+
12
+ it('renders the empty message paragraph with the correct class', async () => {
13
+ const { container } = render(<EmptyKey text={text} />)
14
+ expect(container.querySelector('.im-c-datasets-key__empty-message')).toBeTruthy()
15
+ })
16
+
17
+ it('renders the provided text', async () => {
18
+ render(<EmptyKey text={text} />)
19
+ expect(screen.getByText(text)).toBeTruthy()
20
+ })
21
+ })
@@ -0,0 +1,24 @@
1
+ import { hasSymbol, getSymbolDef } from '../../../../../src/utils/symbolUtils.js'
2
+ import { hasPattern } from '../../../../../src/utils/patternUtils.js'
3
+ import { KeySvgPattern } from './KeySvgPattern.jsx'
4
+ import { KeySvgSymbol } from './KeySvgSymbol.jsx'
5
+ import { KeySvgLine } from './KeySvgLine.jsx'
6
+ import { KeySvgRect } from './KeySvgRect.jsx'
7
+
8
+ export const KeySvg = (props) => {
9
+ const { symbolRegistry } = props
10
+ const symbolDef = hasSymbol(props) && getSymbolDef(props, symbolRegistry)
11
+ if (symbolDef) {
12
+ return <KeySvgSymbol {...props} symbolDef={symbolDef} />
13
+ }
14
+
15
+ if (hasPattern(props)) {
16
+ return <KeySvgPattern {...props} />
17
+ }
18
+
19
+ if (props.keySymbolShape === 'line') {
20
+ return <KeySvgLine {...props} />
21
+ }
22
+
23
+ return <KeySvgRect {...props} />
24
+ }
@@ -0,0 +1,19 @@
1
+ import { svgProps, SVG_SIZE, SVG_CENTER } from './svgProperties.js'
2
+ import { getValueForStyle } from '../../../../../src/utils/getValueForStyle'
3
+
4
+ export const KeySvgLine = (props) => {
5
+ const { mapStyle } = props
6
+ return (
7
+ <svg {...svgProps}>
8
+ <line
9
+ x1={props.strokeWidth / 2}
10
+ y1={SVG_CENTER}
11
+ x2={SVG_SIZE - props.strokeWidth / 2}
12
+ y2={SVG_CENTER}
13
+ stroke={getValueForStyle(props.stroke, mapStyle.id)}
14
+ strokeWidth={props.strokeWidth}
15
+ strokeLinecap='round'
16
+ />
17
+ </svg>
18
+ )
19
+ }
@@ -0,0 +1,15 @@
1
+ import { getKeyPatternPaths } from '../../../../../src/utils/patternUtils.js'
2
+ import { svgProps } from './svgProperties.js'
3
+ const PATTERN_INSET = 2
4
+
5
+ export const KeySvgPattern = (props) => {
6
+ const { patternRegistry, mapStyle } = props
7
+
8
+ const paths = getKeyPatternPaths(props, mapStyle.id, patternRegistry)
9
+ return (
10
+ <svg {...svgProps}>
11
+ <g dangerouslySetInnerHTML={{ __html: paths.border }} />
12
+ <g transform={`translate(${PATTERN_INSET}, ${PATTERN_INSET})`} dangerouslySetInnerHTML={{ __html: paths.content }} />
13
+ </svg>
14
+ )
15
+ }
@@ -0,0 +1,22 @@
1
+ import { getValueForStyle } from '../../../../../src/utils/getValueForStyle'
2
+ import { svgProps, SVG_SIZE } from './svgProperties.js'
3
+
4
+ export const KeySvgRect = (props) => {
5
+ const { mapStyle } = props
6
+ return (
7
+ <svg {...svgProps}>
8
+ <rect
9
+ x={props.strokeWidth / 2}
10
+ y={props.strokeWidth / 2}
11
+ width={SVG_SIZE - props.strokeWidth}
12
+ height={SVG_SIZE - props.strokeWidth}
13
+ rx={props.strokeWidth}
14
+ ry={props.strokeWidth}
15
+ fill={getValueForStyle(props.fill, mapStyle.id)}
16
+ stroke={getValueForStyle(props.stroke, mapStyle.id)}
17
+ strokeWidth={props.strokeWidth}
18
+ strokeLinejoin='round'
19
+ />
20
+ </svg>
21
+ )
22
+ }
@@ -0,0 +1,16 @@
1
+ import { getSymbolStyleColors, getSymbolViewBox } from '../../../../../src/utils/symbolUtils.js'
2
+ import { svgSymbolProps } from './svgProperties.js'
3
+
4
+ export const KeySvgSymbol = (props) => {
5
+ const { symbolRegistry, mapStyle, symbolDef } = props
6
+ const mapColorScheme = mapStyle?.appColorScheme ?? 'light'
7
+ const keyMapStyle = { ...mapStyle, mapColorScheme }
8
+
9
+ const resolvedSvg = symbolRegistry.resolve(symbolDef, getSymbolStyleColors(props), keyMapStyle)
10
+ const viewBox = getSymbolViewBox(props, symbolDef)
11
+ return (
12
+ <svg {...svgSymbolProps} viewBox={viewBox}>
13
+ <g dangerouslySetInnerHTML={{ __html: resolvedSvg }} />
14
+ </svg>
15
+ )
16
+ }
@@ -0,0 +1,20 @@
1
+ export const SVG_SIZE = 20
2
+ export const SVG_CENTER = SVG_SIZE / 2
3
+ const SVG_SYMBOL_SIZE = 38
4
+
5
+ export const svgProps = {
6
+ xmlns: 'http://www.w3.org/2000/svg',
7
+ width: SVG_SIZE,
8
+ height: SVG_SIZE,
9
+ viewBox: `0 0 ${SVG_SIZE} ${SVG_SIZE}`,
10
+ className: 'am-c-datasets-key-symbol',
11
+ 'aria-hidden': 'true',
12
+ focusable: 'false'
13
+ }
14
+
15
+ export const svgSymbolProps = {
16
+ ...svgProps,
17
+ width: SVG_SYMBOL_SIZE,
18
+ height: SVG_SYMBOL_SIZE,
19
+ className: 'am-c-datasets-key-symbol am-c-datasets-key-symbol--point'
20
+ }
@@ -11,7 +11,7 @@ export const createDatasets = ({
11
11
  adapter,
12
12
  pluginConfig,
13
13
  pluginStateRef,
14
- mapStyleId,
14
+ mapStyle,
15
15
  mapProvider,
16
16
  events,
17
17
  eventBus
@@ -25,7 +25,7 @@ export const createDatasets = ({
25
25
 
26
26
  // Initialise all datasets via the adapter, then set up dynamic sources
27
27
  const processedDatasets = datasets.map(d => applyDatasetDefaults(d, datasetDefaults))
28
- adapter.init(processedDatasets, mapStyleId).then(() => {
28
+ adapter.init(processedDatasets, mapStyle).then(() => {
29
29
  processedDatasets.forEach(dataset => {
30
30
  if (!isDynamicSource(dataset)) {
31
31
  return
@@ -42,16 +42,25 @@ export const createDatasets = ({
42
42
  eventBus.emit('datasets:ready')
43
43
  })
44
44
 
45
+ let currentMapStyle = mapStyle
46
+
45
47
  // Handle basemap style changes — delegate entirely to the adapter
46
- const onSetStyle = (e) => {
47
- adapter.onStyleChange(getDatasets(), e.id, getHiddenFeatures(), dynamicSources)
48
+ const onSetStyle = (newMapStyle) => {
49
+ currentMapStyle = newMapStyle
50
+ adapter.onStyleChange(getDatasets(), newMapStyle, getHiddenFeatures(), dynamicSources)
51
+ }
52
+
53
+ const onSizeChange = () => {
54
+ adapter.onSizeChange(getDatasets(), currentMapStyle)
48
55
  }
49
56
 
50
57
  eventBus.on(events.MAP_SET_STYLE, onSetStyle)
58
+ eventBus.on(events.MAP_SIZE_CHANGE, onSizeChange)
51
59
 
52
60
  return {
53
61
  remove () {
54
62
  eventBus.off(events.MAP_SET_STYLE, onSetStyle)
63
+ eventBus.off(events.MAP_SIZE_CHANGE, onSizeChange)
55
64
 
56
65
  // Clean up dynamic sources
57
66
  dynamicSources.forEach(source => source.destroy())
@@ -16,12 +16,14 @@ const datasetDefaults = {
16
16
  const STYLE_PROPS = [
17
17
  'stroke', 'strokeWidth', 'strokeDashArray',
18
18
  'fill', 'fillPattern', 'fillPatternSvgContent', 'fillPatternForegroundColor', 'fillPatternBackgroundColor',
19
- 'opacity', 'symbolDescription', 'keySymbolShape'
19
+ 'opacity', 'symbolDescription', 'keySymbolShape',
20
+ 'symbol', 'symbolSvgContent', 'symbolViewBox', 'symbolAnchor',
21
+ 'symbolBackgroundColor', 'symbolForegroundColor', 'symbolHaloWidth', 'symbolGraphic'
20
22
  ]
21
23
 
22
24
  // Props whose presence in a style object indicates a custom visual style.
23
25
  // When any are set, the default symbolDescription is not appropriate.
24
- const VISUAL_STYLE_PROPS = ['stroke', 'fill', 'fillPattern', 'fillPatternSvgContent']
26
+ const VISUAL_STYLE_PROPS = ['stroke', 'fill', 'fillPattern', 'fillPatternSvgContent', 'symbol', 'symbolSvgContent']
25
27
 
26
28
  const hasCustomVisualStyle = (style) =>
27
29
  VISUAL_STYLE_PROPS.some(prop => prop in style)
@@ -1,8 +1,9 @@
1
1
  // /plugins/datasets/index.js
2
2
  import './datasets.scss'
3
3
 
4
- export default function createPlugin (options = {}) {
4
+ export default function createPlugin (options = { }) {
5
5
  const plugin = {
6
+ noKeyItemText: 'No features displayed',
6
7
  ...options,
7
8
  id: 'datasets',
8
9
  load: async () => {
@@ -103,7 +103,7 @@ export const manifest = {
103
103
 
104
104
  icons: [{
105
105
  id: 'layers',
106
- svgContent: '<path d="M13 13.74a2 2 0 0 1-2 0L2.5 8.87a1 1 0 0 1 0-1.74L11 2.26a2 2 0 0 1 2 0l8.5 4.87a1 1 0 0 1 0 1.74z"></path><path d="m20 14.285 1.5.845a1 1 0 0 1 0 1.74L13 21.74a2 2 0 0 1-2 0l-8.5-4.87a1 1 0 0 1 0-1.74l1.5-.845"></path>'
106
+ svgContent: '<path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"></path><path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"></path><path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"></path>'
107
107
  }, {
108
108
  id: 'key',
109
109
  svgContent: '<path d="M3 5h.01"/><path d="M3 12h.01"/><path d="M3 19h.01"/><path d="M8 5h13"/><path d="M8 12h13"/><path d="M8 19h13"/>'
@@ -1,95 +1,21 @@
1
1
  import React from 'react'
2
2
  import { getValueForStyle } from '../../../../../src/utils/getValueForStyle'
3
- import { hasPattern, getKeyPatternPaths } from '../styles/patterns.js'
4
3
  import { mergeSublayer } from '../utils/mergeSublayer.js'
4
+ import { EmptyKey } from '../components/EmptyKey.jsx'
5
+ import { KeySvg } from '../components/KeySvg.jsx'
5
6
 
6
- const SVG_SIZE = 20
7
- const SVG_CENTER = SVG_SIZE / 2
8
- const PATTERN_INSET = 2
9
-
10
- const buildKeyGroups = (datasets) => {
11
- const seenGroups = new Set()
12
- const items = []
13
- datasets.forEach(dataset => {
14
- if (dataset.sublayers?.length) {
15
- items.push({ type: 'sublayers', dataset })
16
- return
17
- }
18
- if (dataset.groupLabel) {
19
- if (seenGroups.has(dataset.groupLabel)) {
20
- return
21
- }
22
- seenGroups.add(dataset.groupLabel)
23
- items.push({
24
- type: 'group',
25
- groupLabel: dataset.groupLabel,
26
- datasets: datasets.filter(d => !d.sublayers?.length && d.groupLabel === dataset.groupLabel)
27
- })
28
- return
29
- }
30
- items.push({ type: 'flat', dataset })
31
- })
32
- return items
33
- }
34
-
35
- export const Key = ({ mapState, pluginState }) => {
36
- const { mapStyle } = mapState
37
-
38
- const itemSymbol = (config) => {
39
- const svgProps = {
40
- xmlns: 'http://www.w3.org/2000/svg',
41
- width: SVG_SIZE,
42
- height: SVG_SIZE,
43
- viewBox: `0 0 ${SVG_SIZE} ${SVG_SIZE}`,
44
- 'aria-hidden': 'true',
45
- focusable: 'false'
46
- }
47
-
48
- if (hasPattern(config)) {
49
- const paths = getKeyPatternPaths(config, mapStyle.id)
50
- return (
51
- <svg {...svgProps}>
52
- <g dangerouslySetInnerHTML={{ __html: paths.border }} />
53
- <g transform={`translate(${PATTERN_INSET}, ${PATTERN_INSET})`} dangerouslySetInnerHTML={{ __html: paths.content }} />
54
- </svg>
55
- )
56
- }
57
-
58
- return (
59
- <svg {...svgProps}>
60
- {config.keySymbolShape === 'line'
61
- ? (
62
- <line
63
- x1={config.strokeWidth / 2}
64
- y1={SVG_CENTER}
65
- x2={SVG_SIZE - config.strokeWidth / 2}
66
- y2={SVG_CENTER}
67
- stroke={getValueForStyle(config.stroke, mapStyle.id)}
68
- strokeWidth={config.strokeWidth}
69
- strokeLinecap='round'
70
- />
71
- )
72
- : (
73
- <rect
74
- x={config.strokeWidth / 2}
75
- y={config.strokeWidth / 2}
76
- width={SVG_SIZE - config.strokeWidth}
77
- height={SVG_SIZE - config.strokeWidth}
78
- rx={config.strokeWidth}
79
- ry={config.strokeWidth}
80
- fill={getValueForStyle(config.fill, mapStyle.id)}
81
- stroke={getValueForStyle(config.stroke, mapStyle.id)}
82
- strokeWidth={config.strokeWidth}
83
- strokeLinejoin='round'
84
- />
85
- )}
86
- </svg>
87
- )
7
+ export const Key = ({ pluginConfig, mapState, pluginState, services }) => {
8
+ if (!pluginState?.key?.items?.length) {
9
+ return (<EmptyKey text={pluginConfig.noKeyItemText} />)
88
10
  }
11
+ const { mapStyle } = mapState
12
+ const { symbolRegistry, patternRegistry } = services
89
13
 
90
14
  const renderEntry = (key, config) => (
91
15
  <dl key={key} className='im-c-datasets-key__item'>
92
- <dt className='im-c-datasets-key__item-symbol'>{itemSymbol(config)}</dt>
16
+ <dt className='im-c-datasets-key__item-symbol'>
17
+ <KeySvg {...config} symbolRegistry={symbolRegistry} patternRegistry={patternRegistry} mapStyle={mapStyle} />
18
+ </dt>
93
19
  <dd className='im-c-datasets-key__item-label'>
94
20
  {config.label}
95
21
  {config.symbolDescription && (
@@ -101,11 +27,7 @@ export const Key = ({ mapState, pluginState }) => {
101
27
  </dl>
102
28
  )
103
29
 
104
- const visibleDatasets = (pluginState.datasets || [])
105
- .filter(dataset => dataset.showInKey && dataset.visibility !== 'hidden')
106
-
107
- const keyGroups = buildKeyGroups(visibleDatasets)
108
- const hasGroups = keyGroups.some(item => item.type === 'sublayers' || item.type === 'group')
30
+ const { items: keyGroups, hasGroups } = pluginState.key
109
31
  const containerClass = `im-c-datasets-key${hasGroups ? ' im-c-datasets-key--has-groups' : ''}`
110
32
 
111
33
  return (
@@ -1,10 +1,13 @@
1
1
  // When groups are present, every direct child gets a border-top
2
- .im-c-datasets-key--has-groups > * {
3
- border-top: 1px solid var(--button-hover-color);
2
+ .im-c-datasets-key > *:first-child {
3
+ padding-top: 5px;
4
+
5
+ .im-c-datasets-key__group-heading {
6
+ padding-top: 0;
7
+ }
4
8
  }
5
9
 
6
- // When no groups, only the first child gets a border-top
7
- .im-c-datasets-key:not(.im-c-datasets-key--has-groups) > *:first-child {
10
+ .im-c-datasets-key--has-groups > *:not(:first-child) {
8
11
  border-top: 1px solid var(--button-hover-color);
9
12
  }
10
13
 
@@ -28,31 +31,39 @@
28
31
  padding-bottom: 10px;
29
32
  font-size: 1rem;
30
33
 
31
- // When mixed with groups, flat items match group heading spacing
32
- .im-c-datasets-key--has-groups > & {
33
- padding-top: 15px;
34
- padding-bottom: 15px;
35
- }
36
-
37
34
  // First item inside a group — heading already provides top spacing
38
35
  .im-c-datasets-key__group &:first-child {
39
36
  padding-top: 0;
40
37
  }
41
38
  }
42
39
 
43
- // No-groups: first item needs 15px below the single border
44
- .im-c-datasets-key:not(.im-c-datasets-key--has-groups) > .im-c-datasets-key__item:first-child {
40
+ .im-c-datasets-key--has-groups > .im-c-datasets-key__item:not(:first-child) {
45
41
  padding-top: 15px;
46
42
  }
47
43
 
44
+ .im-c-datasets-key--has-groups > .im-c-datasets-key__item {
45
+ padding-bottom: 15px;
46
+ }
47
+
48
+ .im-c-datasets-key__group .im-c-datasets-key__item {
49
+ padding-top: 10px;
50
+ padding-bottom: 10px;
51
+ }
52
+
48
53
  // Last item in all scenarios — flat or inside the last group
49
54
  .im-c-datasets-key > .im-c-datasets-key__item:last-child,
50
55
  .im-c-datasets-key__group:last-child .im-c-datasets-key__item:last-child {
51
56
  padding-bottom: 5px;
52
57
  }
53
58
 
54
- .im-c-datasets-key__item svg {
59
+ // Symbol
60
+
61
+ .am-c-datasets-key-symbol {
55
62
  position: relative;
56
63
  flex-shrink: 0;
57
64
  margin: 0 13px 0 2px;
58
65
  }
66
+
67
+ .am-c-datasets-key-symbol--point {
68
+ margin: -9px 3px -9px -7px;
69
+ }
@@ -1,12 +1,10 @@
1
- // When groups are present, every direct child (groups and flat items) gets a border-top
2
- .im-c-datasets-layers--has-groups > * {
1
+ // When groups are present, every direct child except the first gets a border-top
2
+ .im-c-datasets-layers--has-groups > *:not(:first-child) {
3
3
  border-top: 1px solid var(--button-hover-color);
4
4
  }
5
5
 
6
- // When no groups, only the first child gets a border-top with 15px padding
7
- .im-c-datasets-layers:not(.im-c-datasets-layers--has-groups) > *:first-child {
8
- border-top: 1px solid var(--button-hover-color);
9
- padding-top: 10px;
6
+ .im-c-datasets-layers > *:first-child {
7
+ padding-top: 5px;
10
8
  }
11
9
 
12
10
  .im-c-datasets-layers > *:last-child {
@@ -20,7 +18,11 @@
20
18
  .im-c-datasets-layers__item {
21
19
  padding-bottom: 5px;
22
20
 
23
- .im-c-datasets-layers--has-groups > & {
21
+ &:first-child {
22
+ padding-top: 0;
23
+ }
24
+
25
+ .im-c-datasets-layers--has-groups > &:not(:first-child) {
24
26
  padding-top: 5px;
25
27
  }
26
28
 
@@ -73,3 +75,7 @@
73
75
  font-weight: bold;
74
76
  color: var(--foreground-color);
75
77
  }
78
+
79
+ .im-c-datasets-layers-group:first-child .im-c-datasets-layers-group__legend {
80
+ padding-top: 0;
81
+ }
@@ -1,7 +1,12 @@
1
1
  import { applyDatasetDefaults } from './defaults.js'
2
+ import { keyReducer } from './reducers/keyReducer.js'
2
3
 
3
4
  const initialState = {
4
5
  datasets: null,
6
+ key: {
7
+ items: [],
8
+ hasGroups: false
9
+ },
5
10
  hiddenFeatures: {}, // { [layerId]: { idProperty: string, ids: string[] } }
6
11
  layerAdapter: null
7
12
  }
@@ -191,6 +196,7 @@ const setSublayerOpacity = (state, payload) => {
191
196
  const setLayerAdapter = (state, payload) => ({ ...state, layerAdapter: payload })
192
197
 
193
198
  const actions = {
199
+ BUILD_KEY_GROUPS: keyReducer,
194
200
  SET_DATASETS: setDatasets,
195
201
  ADD_DATASET: addDataset,
196
202
  REMOVE_DATASET: removeDataset,
@@ -0,0 +1,34 @@
1
+ export const keyReducer = (state) => {
2
+ const datasets = state.datasets || []
3
+ const seenGroups = new Set()
4
+ const items = []
5
+ let hasGroups = false
6
+ datasets.forEach(dataset => {
7
+ if (!dataset.showInKey || dataset.visibility === 'hidden') {
8
+ return
9
+ }
10
+ if (dataset.sublayers?.length) {
11
+ const { sublayerVisibility = [] } = dataset
12
+ if (Object.values(sublayerVisibility).some((value) => value !== 'hidden')) {
13
+ hasGroups = true
14
+ items.push({ type: 'sublayers', dataset })
15
+ }
16
+ return
17
+ }
18
+ if (dataset.groupLabel) {
19
+ if (seenGroups.has(dataset.groupLabel)) {
20
+ return
21
+ }
22
+ seenGroups.add(dataset.groupLabel)
23
+ hasGroups = true
24
+ items.push({
25
+ type: 'group',
26
+ groupLabel: dataset.groupLabel,
27
+ datasets: datasets.filter(d => !d.sublayers?.length && d.groupLabel === dataset.groupLabel)
28
+ })
29
+ return
30
+ }
31
+ items.push({ type: 'flat', dataset })
32
+ })
33
+ return { ...state, key: { items, hasGroups } }
34
+ }
@@ -73,6 +73,14 @@ export const mergeSublayer = (dataset, sublayer) => {
73
73
  filter: combinedFilter,
74
74
  minZoom: dataset.minZoom,
75
75
  maxZoom: dataset.maxZoom,
76
+ symbol: sublayerStyle.symbol ?? dataset.symbol,
77
+ symbolSvgContent: sublayerStyle.symbolSvgContent ?? dataset.symbolSvgContent,
78
+ symbolViewBox: sublayerStyle.symbolViewBox ?? dataset.symbolViewBox,
79
+ symbolAnchor: sublayerStyle.symbolAnchor ?? dataset.symbolAnchor,
80
+ symbolBackgroundColor: sublayerStyle.symbolBackgroundColor ?? dataset.symbolBackgroundColor,
81
+ symbolForegroundColor: sublayerStyle.symbolForegroundColor ?? dataset.symbolForegroundColor,
82
+ symbolHaloWidth: sublayerStyle.symbolHaloWidth ?? dataset.symbolHaloWidth,
83
+ symbolGraphic: sublayerStyle.symbolGraphic ?? dataset.symbolGraphic,
76
84
  ...getFillProps(dataset, sublayerStyle)
77
85
  }
78
86
  }
@@ -1 +1 @@
1
- import e from"@babel/runtime/helpers/defineProperty";import{useEffect as t}from"preact/compat";import r from"@arcgis/core/widgets/Sketch/SketchViewModel.js";import o from"@arcgis/core/layers/GraphicsLayer.js";import a from"@babel/runtime/helpers/asyncToGenerator";import*as n from"@arcgis/core/geometry/operators/simplifyOperator.js";import i from"@arcgis/core/Graphic.js";function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,o)}return r}function p(t){for(var r=1;r<arguments.length;r++){var o=null!=arguments[r]?arguments[r]:{};r%2?l(Object(o),!0).forEach(function(r){e(t,r,o[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(o)):l(Object(o)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(o,e))})}return t}function c(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,o)}return r}function u(t){for(var r=1;r<arguments.length;r++){var o=null!=arguments[r]?arguments[r]:{};r%2?c(Object(o),!0).forEach(function(r){e(t,r,o[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(o)):c(Object(o)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(o,e))})}return t}function s(e){return{type:"simple-fill",color:[0,120,255,.2],outline:{color:"dark"===e?"#ffffff":"#d4351c",width:2}}}function d(e,t,r){return new i({geometry:{type:"polygon",rings:t,spatialReference:27700},attributes:{id:e},symbol:s(r)})}function y(e){if(null==e||!e.geometry)throw new Error("Invalid graphic");var{geometry:t,attributes:r={}}=e;switch(t.type){case"point":return{type:"Feature",geometry:{type:"Point",coordinates:[t.x,t.y]},properties:u({},r)};case"polyline":return{type:"Feature",geometry:{type:"LineString",coordinates:t.paths[0]},properties:u({},r)};case"polygon":return{type:"Feature",geometry:{type:"Polygon",coordinates:t.rings},properties:u({},r)};default:throw new Error("Unsupported geometry type: ".concat(t.type))}}function m(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,o)}return r}function v(t){for(var r=1;r<arguments.length;r++){var o=null!=arguments[r]?arguments[r]:{};r%2?m(Object(o),!0).forEach(function(r){e(t,r,o[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(o)):m(Object(o)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(o,e))})}return t}var f={mobile:{slot:"actions",showLabel:!0},tablet:{slot:"actions",showLabel:!0},desktop:{slot:"actions",showLabel:!0}},h={reducer:{initialState:{mode:null,feature:null,tempFeature:null},actions:{SET_MODE:(e,t)=>p(p({},e),{},{mode:t}),SET_FEATURE:(e,t)=>p(p({},e),{},{feature:void 0===t.feature?e.feature:t.feature,tempFeature:void 0===t.tempFeature?e.tempFeature:t.tempFeature})}},InitComponent:e=>{var i,l,p,c,{appState:u,mapState:m,pluginConfig:v,pluginState:f,services:h,mapProvider:g,buttonConfig:b}=e,{events:O,eventBus:w}=h,{mapColorScheme:S}=m.mapStyle||{},E=null===(i=null===(l=v.includeModes)||void 0===l?void 0:l.includes(u.mode))||void 0===i||i,k=null!==(p=null===(c=v.excludeModes)||void 0===c?void 0:c.includes(u.mode))&&void 0!==p&&p,P=m.isMapReady&&E&&!k;t(()=>{if(P&&!g.sketchViewModel){var{sketchViewModel:e,sketchLayer:t,emptySketchLayer:a}=(e=>{var{mapProvider:t}=e,{view:a}=t,n=new o({id:"sketchLayer"});a.map.add(n);var i=new o({id:"emptySketchLayer"});return a.map.add(i),{sketchViewModel:new r({view:a,layer:i,defaultUpdateOptions:{tool:"reshape",updateOnGraphicClick:!1,multipleSelectionEnabled:!1,toggleToolOnClick:!1,highlightOptions:{enabled:!1}}}),emptySketchLayer:i,sketchLayer:n}})({mapProvider:g});return g.sketchViewModel=e,g.sketchLayer=t,g.emptySketchLayer=a,w.emit("draw:ready"),()=>{g.sketchViewModel=null,g.sketchLayer=null,g.emptySketchLayer=null}}},[m.isMapReady,u.mode]),t(()=>{if(P&&g.sketchViewModel){var e=function(e){var{pluginState:t,mapProvider:r,events:o,eventBus:i,buttonConfig:l,mapColorScheme:p}=e,{view:c,sketchViewModel:u,sketchLayer:m,emptySketchLayer:v}=r;if(!u)return null;var{drawDone:f,drawCancel:h}=l,{dispatch:g,mode:b,feature:O}=t,w=function(){var e=a(function*(){var e,r=null===(e=t.feature)||void 0===e||null===(e=e.properties)||void 0===e?void 0:e.id,o=null,a="active"===u.state&&!r;if("active"===u.state&&r&&(u.cancel(),yield new Promise(e=>setTimeout(e,50))),u.polygonSymbol=s(p),null==m||m.graphics.items.forEach(e=>{var t=d(e.attributes.id,e.geometry.rings,p);e.symbol=t.symbol,r===e.attributes.id&&(o=e)}),o&&!a&&u.layer===m)try{yield u.update(o,{tool:"reshape",toggleToolOnClick:!1})}catch(e){"AbortError"!==e.name&&console.error("Error updating sketch:",e)}});return function(){return e.apply(this,arguments)}}(),S=()=>{var e,t,r=null!==(e=null===(t=m.graphics)||void 0===t||null===(t=t.items)||void 0===t?void 0:t[0])&&void 0!==e?e:null;r&&setTimeout(()=>u.update(r),0)},E=()=>w(),k=e=>{if(e){var t=y(e);i.emit("draw:updated",t),g({type:"SET_FEATURE",payload:{tempFeature:t}})}},P=function(){var e=a(function*(){b&&((u.updateGraphics||[]).length||S())});return function(){return e.apply(this,arguments)}}(),j=function(){var e=a(function*(e){var{toolEventInfo:t}=e,r=null==e?void 0:e.graphic;if(r&&"vertex-add"===(null==t?void 0:t.type)){var o,a,n=null===(o=r.geometry)||void 0===o?void 0:o.rings;(null==n?void 0:n.length)>1?setTimeout(()=>u.undo(),0):(null==n||null===(a=n[0])||void 0===a?void 0:a.length)>3&&k(r)}});return function(t){return e.apply(this,arguments)}}(),T=function(){var e=a(function*(e){var t,r=null==e||null===(t=e.graphics)||void 0===t?void 0:t[0];k(r)});return function(t){return e.apply(this,arguments)}}();i.on(o.MAP_STYLE_CHANGE,E);var C=u.on("update",e=>{var t,r=null===(t=e.toolEventInfo)||void 0===t?void 0:t.type,o=e.graphics[0];"move-start"===r&&(u.cancel(),S()),"reshape"===r&&(n.isSimple(o.geometry)||u.undo()),"reshape-stop"!==r&&"vertex-remove"!==r||k(o)}),F=c.on("click",P),L=u.on("create",j),M=u.on("undo",T),D=f.onClick,_=h.onClick;return f.onClick=()=>{u.cancel(),u.layer=v,g({type:"SET_MODE",payload:null}),g({type:"SET_FEATURE",payload:{feature:null,tempFeature:null}}),i.emit("draw:done",{newFeature:t.tempFeature})},h.onClick=()=>{if(u.cancel(),m.removeAll(),O){var e=d(O.id||O.properties.id,O.geometry.coordinates,p);m.add(e)}u.layer=v,g({type:"SET_MODE",payload:null}),i.emit("draw:cancelled")},()=>{i.off(o.MAP_STYLE_CHANGE,E),C.remove(),F.remove(),L.remove(),M.remove(),f.onClick=D,h.onClick=_}}({pluginState:f,mapProvider:g,events:O,eventBus:w,buttonConfig:b,mapColorScheme:S});return()=>{e()}}},[P,S,f])},buttons:[v({id:"drawDone",label:"Done",variant:"primary",hiddenWhen:e=>{var{pluginState:t}=e;return!t.mode},enableWhen:e=>{var{pluginState:t}=e;return!!t.tempFeature}},f),v({id:"drawCancel",label:"Cancel",variant:"tertiary",hiddenWhen:e=>{var{pluginState:t}=e;return!t.mode}},f)],api:{newPolygon:(e,t)=>{var{mapState:r,pluginState:o,mapProvider:a,services:n}=e,{dispatch:i}=o,{sketchViewModel:l,sketchLayer:p}=a,{eventBus:c}=n;l.layer=p;var u=l.on("create",e=>{if("complete"===e.state){e.graphic.attributes={id:t},requestAnimationFrame(()=>{l.update(e.graphic,{tool:"reshape",toggleToolOnClick:!1})});var r=y(e.graphic);c.emit("draw:created",r),i({type:"SET_FEATURE",payload:{tempFeature:r}}),u.remove()}});l.polygonSymbol=s(r.mapStyle.mapColorScheme),l.create("polygon"),i({type:"SET_MODE",payload:"new-polygon"})},editFeature:(e,t)=>{var{pluginState:r,mapProvider:o}=e,{dispatch:a}=r,{sketchViewModel:n,sketchLayer:i}=o,l=i.graphics.items.find(e=>e.attributes.id===t),p=l.geometry.extent,c=[p.xmin,p.ymin,p.xmax,p.ymax];o.fitToBounds(c),n.layer=i,n.update(l,{tool:"reshape",toggleToolOnClick:!1,enableRotation:!1,enableScaling:!1}),a({type:"SET_FEATURE",payload:{feature:y(l)}}),a({type:"SET_MODE",payload:"edit-feature"})},addFeature:(e,t)=>{var{pluginState:r,mapState:o,mapProvider:a,services:n}=e,{dispatch:i}=r,{mapStyle:l}=o,{sketchViewModel:p,sketchLayer:c,emptySketchLayer:u}=a,{eventBus:s}=n,y=d(t.id,t.geometry.coordinates,l.mapColorScheme);c.add(y),p.layer=u,i({type:"SET_FEATURE",payload:{feature:t}}),s.emit("draw:add",t)},deleteFeature:(e,t)=>{var{pluginState:r,mapProvider:o,services:a}=e,{dispatch:n}=r,{sketchViewModel:i,sketchLayer:l,emptySketchLayer:p}=o,{eventBus:c}=a,u=l.graphics.items.find(e=>e.attributes.id===t);i.cancel(),l.remove(u),i.layer=p,n({type:"SET_FEATURE",payload:{feature:null,tempFeature:null}}),c.emit("draw:delete",{featureId:t}),n({type:"SET_MODE",payload:null})}}};export{h as manifest};
1
+ import e from"@babel/runtime/helpers/defineProperty";import{useEffect as t}from"preact/compat";import r from"@arcgis/core/widgets/Sketch/SketchViewModel.js";import a from"@arcgis/core/layers/GraphicsLayer.js";import o from"@babel/runtime/helpers/asyncToGenerator";import*as n from"@arcgis/core/geometry/operators/simplifyOperator.js";import i from"@arcgis/core/Graphic.js";function p(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,a)}return r}function l(t){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?p(Object(a),!0).forEach(function(r){e(t,r,a[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(a)):p(Object(a)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(a,e))})}return t}var c={APP_ADD_MARKER:"app:addmarker",APP_REMOVE_MARKER:"app:removemarker",APP_SET_MODE:"app:setmode",APP_REVERT_MODE:"app:revertmode",APP_ADD_BUTTON:"app:addbutton",APP_TOGGLE_BUTTON_STATE:"app:togglebuttonstate",APP_ADD_PANEL:"app:addpanel",APP_REMOVE_PANEL:"app:removepanel",APP_SHOW_PANEL:"app:showpanel",APP_HIDE_PANEL:"app:hidepanel",APP_ADD_CONTROL:"app:addcontrol",APP_READY:"app:ready",APP_VISIBLE:"app:visible",APP_HIDDEN:"app:hidden",APP_PANEL_OPENED:"app:panelopened",APP_PANEL_CLOSED:"app:panelclosed",MAP_SET_STYLE:"map:setstyle",MAP_SET_SIZE:"map:setsize",MAP_SET_PIXEL_RATIO:"map:setpixelratio",MAP_FIT_TO_BOUNDS:"map:fittobounds",MAP_SET_VIEW:"map:setview",MAP_INIT_MAP_STYLES:"map:initmapstyles",MAP_STYLE_CHANGE:"map:stylechange",MAP_LOADED:"map:loaded",MAP_READY:"map:ready",MAP_SIZE_CHANGE:"map:sizechange",MAP_FIRST_IDLE:"map:firstidle",MAP_MOVE_START:"map:movestart",MAP_MOVE:"map:move",MAP_MOVE_END:"map:moveend",MAP_STATE_UPDATED:"map:stateupdated",MAP_DATA_CHANGE:"map:datachange",MAP_RENDER:"map:render",MAP_CLICK:"map:click",MAP_EXIT:"map:exit",MAP_DESTROY:"map:destroy"};function d(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,a)}return r}function s(t){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?d(Object(a),!0).forEach(function(r){e(t,r,a[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(a)):d(Object(a)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(a,e))})}return t}function u(e){return{type:"simple-fill",color:[0,120,255,.2],outline:{color:"dark"===e?"#ffffff":"#d4351c",width:2}}}function m(e,t,r){return new i({geometry:{type:"polygon",rings:t,spatialReference:27700},attributes:{id:e},symbol:u(r)})}function y(e){if(null==e||!e.geometry)throw new Error("Invalid graphic");var{geometry:t,attributes:r={}}=e;switch(t.type){case"point":return{type:"Feature",geometry:{type:"Point",coordinates:[t.x,t.y]},properties:s({},r)};case"polyline":return{type:"Feature",geometry:{type:"LineString",coordinates:t.paths[0]},properties:s({},r)};case"polygon":return{type:"Feature",geometry:{type:"Polygon",coordinates:t.rings},properties:s({},r)};default:throw new Error("Unsupported geometry type: ".concat(t.type))}}function v(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,a)}return r}function h(t){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?v(Object(a),!0).forEach(function(r){e(t,r,a[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(a)):v(Object(a)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(a,e))})}return t}var f={mobile:{slot:"actions",showLabel:!0},tablet:{slot:"actions",showLabel:!0},desktop:{slot:"actions",showLabel:!0}},E={reducer:{initialState:{mode:null,feature:null,tempFeature:null},actions:{SET_MODE:(e,t)=>l(l({},e),{},{mode:t}),SET_FEATURE:(e,t)=>l(l({},e),{},{feature:void 0===t.feature?e.feature:t.feature,tempFeature:void 0===t.tempFeature?e.tempFeature:t.tempFeature})}},InitComponent:e=>{var i,p,l,d,{appState:s,mapState:v,pluginConfig:h,pluginState:f,services:E,mapProvider:P,buttonConfig:g}=e,{eventBus:O}=E,{mapColorScheme:_}=v.mapStyle||{},S=null===(i=null===(p=h.includeModes)||void 0===p?void 0:p.includes(s.mode))||void 0===i||i,b=null!==(l=null===(d=h.excludeModes)||void 0===d?void 0:d.includes(s.mode))&&void 0!==l&&l,A=v.isMapReady&&S&&!b;t(()=>{if(A&&!P.sketchViewModel){var{sketchViewModel:e,sketchLayer:t,emptySketchLayer:o}=(e=>{var{mapProvider:t}=e,{view:o}=t,n=new a({id:"sketchLayer"});o.map.add(n);var i=new a({id:"emptySketchLayer"});return o.map.add(i),{sketchViewModel:new r({view:o,layer:i,defaultUpdateOptions:{tool:"reshape",updateOnGraphicClick:!1,multipleSelectionEnabled:!1,toggleToolOnClick:!1,highlightOptions:{enabled:!1}}}),emptySketchLayer:i,sketchLayer:n}})({mapProvider:P});return P.sketchViewModel=e,P.sketchLayer=t,P.emptySketchLayer=o,O.emit("draw:ready"),()=>{P.sketchViewModel=null,P.sketchLayer=null,P.emptySketchLayer=null}}},[v.isMapReady,s.mode]),t(()=>{if(A&&P.sketchViewModel){var e=function(e){var{pluginState:t,mapProvider:r,events:a,eventBus:i,buttonConfig:p,mapColorScheme:l}=e,{view:c,sketchViewModel:d,sketchLayer:s,emptySketchLayer:v}=r;if(!d)return null;var{drawDone:h,drawCancel:f}=p,{dispatch:E,mode:P,feature:g}=t,O=function(){var e=o(function*(){var e,r=null===(e=t.feature)||void 0===e||null===(e=e.properties)||void 0===e?void 0:e.id,a=null,o="active"===d.state&&!r;if("active"===d.state&&r&&(d.cancel(),yield new Promise(e=>setTimeout(e,50))),d.polygonSymbol=u(l),null==s||s.graphics.items.forEach(e=>{var t=m(e.attributes.id,e.geometry.rings,l);e.symbol=t.symbol,r===e.attributes.id&&(a=e)}),a&&!o&&d.layer===s)try{yield d.update(a,{tool:"reshape",toggleToolOnClick:!1})}catch(e){"AbortError"!==e.name&&console.error("Error updating sketch:",e)}});return function(){return e.apply(this,arguments)}}(),_=()=>{var e,t,r=null!==(e=null===(t=s.graphics)||void 0===t||null===(t=t.items)||void 0===t?void 0:t[0])&&void 0!==e?e:null;r&&setTimeout(()=>d.update(r),0)},S=()=>O(),b=e=>{if(e){var t=y(e);i.emit("draw:updated",t),E({type:"SET_FEATURE",payload:{tempFeature:t}})}},A=function(){var e=o(function*(){P&&((d.updateGraphics||[]).length||_())});return function(){return e.apply(this,arguments)}}(),w=function(){var e=o(function*(e){var{toolEventInfo:t}=e,r=null==e?void 0:e.graphic;if(r&&"vertex-add"===(null==t?void 0:t.type)){var a,o,n=null===(a=r.geometry)||void 0===a?void 0:a.rings;(null==n?void 0:n.length)>1?setTimeout(()=>d.undo(),0):(null==n||null===(o=n[0])||void 0===o?void 0:o.length)>3&&b(r)}});return function(t){return e.apply(this,arguments)}}(),T=function(){var e=o(function*(e){var t,r=null==e||null===(t=e.graphics)||void 0===t?void 0:t[0];b(r)});return function(t){return e.apply(this,arguments)}}();i.on(a.MAP_STYLE_CHANGE,S);var M=d.on("update",e=>{var t,r=null===(t=e.toolEventInfo)||void 0===t?void 0:t.type,a=e.graphics[0];"move-start"===r&&(d.cancel(),_()),"reshape"===r&&(n.isSimple(a.geometry)||d.undo()),"reshape-stop"!==r&&"vertex-remove"!==r||b(a)}),k=c.on("click",A),D=d.on("create",w),L=d.on("undo",T),j=h.onClick,C=f.onClick;return h.onClick=()=>{d.cancel(),d.layer=v,E({type:"SET_MODE",payload:null}),E({type:"SET_FEATURE",payload:{feature:null,tempFeature:null}}),i.emit("draw:done",{newFeature:t.tempFeature})},f.onClick=()=>{if(d.cancel(),s.removeAll(),g){var e=m(g.id||g.properties.id,g.geometry.coordinates,l);s.add(e)}d.layer=v,E({type:"SET_MODE",payload:null}),i.emit("draw:cancelled")},()=>{i.off(a.MAP_STYLE_CHANGE,S),M.remove(),k.remove(),D.remove(),L.remove(),h.onClick=j,f.onClick=C}}({pluginState:f,mapProvider:P,events:c,eventBus:O,buttonConfig:g,mapColorScheme:_});return()=>{e()}}},[A,_,f])},buttons:[h({id:"drawDone",label:"Done",variant:"primary",hiddenWhen:e=>{var{pluginState:t}=e;return!t.mode},enableWhen:e=>{var{pluginState:t}=e;return!!t.tempFeature}},f),h({id:"drawCancel",label:"Cancel",variant:"tertiary",hiddenWhen:e=>{var{pluginState:t}=e;return!t.mode}},f)],api:{newPolygon:(e,t)=>{var{mapState:r,pluginState:a,mapProvider:o,services:n}=e,{dispatch:i}=a,{sketchViewModel:p,sketchLayer:l}=o,{eventBus:c}=n;p.layer=l;var d=p.on("create",e=>{if("complete"===e.state){e.graphic.attributes={id:t},requestAnimationFrame(()=>{p.update(e.graphic,{tool:"reshape",toggleToolOnClick:!1})});var r=y(e.graphic);c.emit("draw:created",r),i({type:"SET_FEATURE",payload:{tempFeature:r}}),d.remove()}});p.polygonSymbol=u(r.mapStyle.mapColorScheme),p.create("polygon"),i({type:"SET_MODE",payload:"new-polygon"})},editFeature:(e,t)=>{var{pluginState:r,mapProvider:a}=e,{dispatch:o}=r,{sketchViewModel:n,sketchLayer:i}=a,p=i.graphics.items.find(e=>e.attributes.id===t),l=p.geometry.extent,c=[l.xmin,l.ymin,l.xmax,l.ymax];a.fitToBounds(c),n.layer=i,n.update(p,{tool:"reshape",toggleToolOnClick:!1,enableRotation:!1,enableScaling:!1}),o({type:"SET_FEATURE",payload:{feature:y(p)}}),o({type:"SET_MODE",payload:"edit-feature"})},addFeature:(e,t)=>{var{pluginState:r,mapState:a,mapProvider:o,services:n}=e,{dispatch:i}=r,{mapStyle:p}=a,{sketchViewModel:l,sketchLayer:c,emptySketchLayer:d}=o,{eventBus:s}=n,u=m(t.id,t.geometry.coordinates,p.mapColorScheme);c.add(u),l.layer=d,i({type:"SET_FEATURE",payload:{feature:t}}),s.emit("draw:add",t)},deleteFeature:(e,t)=>{var{pluginState:r,mapProvider:a,services:o}=e,{dispatch:n}=r,{sketchViewModel:i,sketchLayer:p,emptySketchLayer:l}=a,{eventBus:c}=o,d=p.graphics.items.find(e=>e.attributes.id===t);i.cancel(),p.remove(d),i.layer=l,n({type:"SET_FEATURE",payload:{feature:null,tempFeature:null}}),c.emit("draw:delete",{featureId:t}),n({type:"SET_MODE",payload:null})}}};export{E as manifest};
@@ -1,4 +1,5 @@
1
1
  import { useEffect } from 'react'
2
+ import { EVENTS } from '../../../../src/config/events.js'
2
3
  import { createSketchViewModel } from './sketchViewModel.js'
3
4
  import { attachEvents } from './events.js'
4
5
 
@@ -11,7 +12,7 @@ export const DrawInit = ({
11
12
  mapProvider,
12
13
  buttonConfig
13
14
  }) => {
14
- const { events, eventBus } = services
15
+ const { eventBus } = services
15
16
  const { mapColorScheme } = mapState.mapStyle || {}
16
17
 
17
18
  // Check if plugin should be active
@@ -52,7 +53,7 @@ export const DrawInit = ({
52
53
  const cleanup = attachEvents({
53
54
  pluginState,
54
55
  mapProvider,
55
- events,
56
+ events: EVENTS,
56
57
  eventBus,
57
58
  buttonConfig,
58
59
  mapColorScheme
@@ -1 +1,21 @@
1
- .touch-vertex-target circle{fill:var(--map-overlay-foreground-color)}.touch-vertex-target path{fill:var(--map-overlay-halo-color)}.im-c-actions .im-c-button-wrapper--draw-done,.im-c-actions .im-c-button-wrapper--draw-menu,.im-c-actions .im-c-button-wrapper--draw-cancel{width:33.33%}.im-o-app--tablet .im-c-actions .im-c-button-wrapper--draw-done,.im-o-app--tablet .im-c-actions .im-c-button-wrapper--draw-menu,.im-o-app--tablet .im-c-actions .im-c-button-wrapper--draw-cancel,.im-o-app--desktop .im-c-actions .im-c-button-wrapper--draw-done,.im-o-app--desktop .im-c-actions .im-c-button-wrapper--draw-menu,.im-o-app--desktop .im-c-actions .im-c-button-wrapper--draw-cancel{width:100px}
1
+ .touch-vertex-target circle {
2
+ fill: var(--map-overlay-foreground-color);
3
+ }
4
+ .touch-vertex-target path {
5
+ fill: var(--map-overlay-halo-color);
6
+ }
7
+
8
+ .im-c-actions .im-c-button-wrapper--draw-done,
9
+ .im-c-actions .im-c-button-wrapper--draw-menu,
10
+ .im-c-actions .im-c-button-wrapper--draw-cancel {
11
+ width: 33.33%;
12
+ }
13
+
14
+ .im-o-app--tablet .im-c-actions .im-c-button-wrapper--draw-done,
15
+ .im-o-app--tablet .im-c-actions .im-c-button-wrapper--draw-menu,
16
+ .im-o-app--tablet .im-c-actions .im-c-button-wrapper--draw-cancel,
17
+ .im-o-app--desktop .im-c-actions .im-c-button-wrapper--draw-done,
18
+ .im-o-app--desktop .im-c-actions .im-c-button-wrapper--draw-menu,
19
+ .im-o-app--desktop .im-c-actions .im-c-button-wrapper--draw-cancel {
20
+ width: 100px;
21
+ }