@defra/interactive-map 0.0.22-alpha → 0.0.23-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 (190) 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/button-definition.md +39 -0
  7. package/docs/api/context.md +4 -4
  8. package/docs/api/map-style-config.md +11 -2
  9. package/docs/api/marker-config.md +38 -7
  10. package/docs/api/panel-definition.md +7 -3
  11. package/docs/api/symbol-config.md +10 -26
  12. package/docs/api/symbol-registry.md +28 -13
  13. package/docs/api.md +24 -2
  14. package/docs/assets/css/{docusaurus.css → docusaurus.scss} +18 -12
  15. package/docs/assets/images/toggle-marker-label.jpg +0 -0
  16. package/docs/examples/index.mdx +12 -0
  17. package/docs/examples/toggle-marker-label.mdx +89 -0
  18. package/docs/plugins/interact.md +4 -11
  19. package/docusaurus.config.cjs +2 -1
  20. package/package.json +8 -8
  21. package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +1 -1
  22. package/plugins/beta/datasets/dist/css/index.css +2 -2
  23. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  24. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +1 -1
  25. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +1 -1
  26. package/plugins/beta/datasets/dist/umd/maplibre/index.js +1 -1
  27. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +5 -5
  28. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +24 -29
  29. package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +2 -2
  30. package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +3 -3
  31. package/plugins/beta/datasets/src/components/KeySvg.jsx +2 -2
  32. package/plugins/beta/datasets/src/components/KeySvgPattern.jsx +1 -2
  33. package/plugins/beta/datasets/src/components/svgProperties.js +2 -2
  34. package/plugins/beta/datasets/src/panels/Key.module.scss +2 -2
  35. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  36. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  37. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  38. package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -1
  39. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
  40. package/plugins/beta/map-styles/dist/umd/index.js +1 -1
  41. package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -1
  42. package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -1
  43. package/plugins/beta/use-location/src/manifest.js +1 -0
  44. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  45. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  46. package/plugins/interact/dist/umd/index.js +1 -1
  47. package/plugins/interact/src/InteractInit.jsx +23 -55
  48. package/plugins/interact/src/InteractInit.test.js +58 -80
  49. package/plugins/interact/src/defaults.js +1 -3
  50. package/plugins/interact/src/hooks/useAttachEvents.js +46 -0
  51. package/plugins/interact/src/hooks/useAttachEvents.test.js +65 -0
  52. package/plugins/interact/src/hooks/useCrossHairVisibility.js +47 -0
  53. package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +54 -0
  54. package/plugins/interact/src/hooks/useHighlightSync.js +30 -8
  55. package/plugins/interact/src/hooks/useHighlightSync.test.js +127 -80
  56. package/plugins/interact/src/hooks/useInteractionHandlers.js +18 -17
  57. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +22 -61
  58. package/plugins/interact/src/hooks/useMapItemList.js +187 -0
  59. package/plugins/interact/src/hooks/useMapItemList.test.js +584 -0
  60. package/plugins/interact/src/index.js +0 -2
  61. package/plugins/interact/src/index.test.js +0 -2
  62. package/plugins/interact/src/manifest.js +37 -6
  63. package/plugins/interact/src/reducer.js +8 -4
  64. package/plugins/interact/src/reducer.test.js +18 -3
  65. package/plugins/interact/src/utils/buildStylesMap.js +21 -10
  66. package/plugins/interact/src/utils/buildStylesMap.test.js +29 -29
  67. package/plugins/interact/src/utils/spatial.js +0 -15
  68. package/plugins/interact/src/utils/spatial.test.js +1 -13
  69. package/plugins/search/dist/css/index.css +1 -1
  70. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  71. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  72. package/plugins/search/src/components/CloseButton/CloseButton.module.scss +2 -0
  73. package/plugins/search/src/components/OpenButton/OpenButton.jsx +1 -1
  74. package/plugins/search/src/components/OpenButton/OpenButton.module.scss +3 -0
  75. package/plugins/search/src/search.scss +1 -0
  76. package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
  77. package/providers/beta/esri/src/esriProvider.js +4 -0
  78. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  79. package/providers/maplibre/dist/esm/index.js +1 -1
  80. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  81. package/providers/maplibre/dist/umd/index.js +1 -1
  82. package/providers/maplibre/src/index.js +1 -1
  83. package/providers/maplibre/src/index.test.js +9 -14
  84. package/providers/maplibre/src/mapEvents.js +11 -5
  85. package/providers/maplibre/src/mapEvents.test.js +14 -3
  86. package/providers/maplibre/src/maplibreProvider.js +18 -11
  87. package/providers/maplibre/src/maplibreProvider.test.js +43 -36
  88. package/providers/maplibre/src/utils/highlightFeatures.js +102 -57
  89. package/providers/maplibre/src/utils/highlightFeatures.test.js +162 -51
  90. package/providers/maplibre/src/utils/maplibreFixes.js +10 -0
  91. package/providers/maplibre/src/utils/maplibreFixes.test.js +31 -0
  92. package/providers/maplibre/src/utils/patternImages.js +10 -12
  93. package/providers/maplibre/src/utils/patternImages.test.js +145 -167
  94. package/providers/maplibre/src/utils/rasteriseToImageData.js +1 -4
  95. package/providers/maplibre/src/utils/rasteriseToImageData.test.js +23 -24
  96. package/providers/maplibre/src/utils/symbolImages.js +23 -86
  97. package/providers/maplibre/src/utils/symbolImages.test.js +60 -105
  98. package/src/App/components/Actions/Actions.module.scss +1 -0
  99. package/src/App/components/Actions/Actions.test.jsx +17 -4
  100. package/src/App/components/Hints/Hints.jsx +52 -0
  101. package/src/App/components/Hints/Hints.module.scss +37 -0
  102. package/src/App/components/Hints/Hints.test.jsx +104 -0
  103. package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +57 -19
  104. package/src/App/components/KeyboardHelp/KeyboardHelp.module.scss +10 -0
  105. package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +140 -31
  106. package/src/App/components/Markers/LabelMarker.jsx +14 -0
  107. package/src/App/components/Markers/LabelMarker.test.jsx +36 -0
  108. package/src/App/components/Markers/Markers.jsx +44 -75
  109. package/src/App/components/Markers/Markers.module.scss +58 -0
  110. package/src/App/components/Markers/Markers.test.jsx +90 -113
  111. package/src/App/components/Markers/SymbolLabelMarker.jsx +43 -0
  112. package/src/App/components/Markers/SymbolLabelMarker.test.jsx +56 -0
  113. package/src/App/components/Markers/SymbolMarker.jsx +31 -0
  114. package/src/App/components/Markers/SymbolMarker.test.jsx +72 -0
  115. package/src/App/components/Panel/Panel.jsx +1 -1
  116. package/src/App/components/Panel/Panel.module.scss +1 -0
  117. package/src/App/components/PopupMenu/PopupMenu.test.jsx +117 -1
  118. package/src/App/components/PopupMenu/usePopupMenu.js +132 -85
  119. package/src/App/components/Tabs/Tabs.jsx +68 -0
  120. package/src/App/components/Tabs/Tabs.module.scss +57 -0
  121. package/src/App/components/Tabs/Tabs.test.jsx +121 -0
  122. package/src/App/components/Tooltip/Tooltip.module.scss +1 -1
  123. package/src/App/components/Viewport/Features.jsx +35 -0
  124. package/src/App/components/Viewport/Features.module.scss +31 -0
  125. package/src/App/components/Viewport/Features.test.jsx +124 -0
  126. package/src/App/components/Viewport/MapStatus.jsx +0 -4
  127. package/src/App/components/Viewport/Viewport.jsx +35 -29
  128. package/src/App/components/Viewport/Viewport.module.scss +7 -21
  129. package/src/App/components/Viewport/Viewport.test.jsx +108 -65
  130. package/src/App/controls/keyboardActions.js +9 -3
  131. package/src/App/controls/keyboardActions.test.js +122 -87
  132. package/src/App/controls/keyboardShortcuts.js +1 -14
  133. package/src/App/hooks/useFeatureFocus.js +199 -0
  134. package/src/App/hooks/useFeatureFocus.test.js +635 -0
  135. package/src/App/hooks/useFeatureItems.js +30 -0
  136. package/src/App/hooks/useFeatureItems.test.js +97 -0
  137. package/src/App/hooks/useFocusVisible.js +4 -7
  138. package/src/App/hooks/useFocusVisible.test.js +6 -6
  139. package/src/App/hooks/useKeyboardHint.js +9 -34
  140. package/src/App/hooks/useKeyboardHint.test.js +34 -84
  141. package/src/App/hooks/useKeyboardShortcuts.js +8 -5
  142. package/src/App/hooks/useKeyboardShortcuts.test.js +16 -12
  143. package/src/App/hooks/useLayoutMeasurements.js +10 -4
  144. package/src/App/hooks/useLayoutMeasurements.test.js +30 -0
  145. package/src/App/hooks/useMarkerCursor.js +33 -0
  146. package/src/App/hooks/useMarkerCursor.test.js +87 -0
  147. package/src/App/hooks/useMarkersAPI.js +32 -9
  148. package/src/App/hooks/useMarkersAPI.test.js +162 -94
  149. package/src/App/layout/Layout.jsx +2 -0
  150. package/src/App/layout/Layout.test.jsx +3 -0
  151. package/src/App/registry/keyboardShortcutRegistry.js +3 -2
  152. package/src/App/registry/keyboardShortcutRegistry.test.js +6 -8
  153. package/src/App/renderer/mapButtons.js +5 -1
  154. package/src/App/renderer/mapButtons.test.js +37 -1
  155. package/src/App/store/ServiceProvider.jsx +4 -1
  156. package/src/App/store/ServiceProvider.test.jsx +11 -0
  157. package/src/App/store/appActionsMap.js +5 -1
  158. package/src/App/store/appActionsMap.test.js +13 -0
  159. package/src/App/store/appReducer.js +1 -0
  160. package/src/App/store/mapActionsMap.js +2 -1
  161. package/src/InteractiveMap/InteractiveMap.js +12 -0
  162. package/src/InteractiveMap/InteractiveMap.test.js +2 -0
  163. package/src/config/appConfig.js +3 -1
  164. package/src/config/appConfig.test.js +4 -0
  165. package/src/config/defaults.js +1 -1
  166. package/src/config/events.js +8 -0
  167. package/src/config/mapTheme.js +8 -4
  168. package/src/config/symbolConfig.js +23 -16
  169. package/src/scss/main.scss +3 -0
  170. package/src/scss/tools/_border-focus.scss +0 -1
  171. package/src/services/eventBus.js +15 -0
  172. package/src/services/eventBus.test.js +11 -0
  173. package/src/services/hints.js +47 -0
  174. package/src/services/hints.test.js +110 -0
  175. package/src/services/patternRegistry.js +69 -1
  176. package/src/services/patternRegistry.test.js +112 -1
  177. package/src/services/reverseGeocode.js +4 -0
  178. package/src/services/reverseGeocode.test.js +11 -1
  179. package/src/services/symbolRegistry.js +152 -17
  180. package/src/services/symbolRegistry.test.js +141 -31
  181. package/src/types.js +71 -14
  182. package/src/utils/constrainKeyboardFocus.js +5 -5
  183. package/src/utils/detectInterfaceType.js +27 -5
  184. package/src/utils/detectInterfaceType.test.js +29 -3
  185. package/src/utils/patternUtils.js +4 -67
  186. package/src/utils/patternUtils.test.js +1 -123
  187. package/src/utils/symbolUtils.js +11 -13
  188. package/src/utils/symbolUtils.test.js +18 -26
  189. package/plugins/interact/dist/css/index.css +0 -1
  190. package/plugins/interact/src/interact.scss +0 -24
@@ -1,7 +1,9 @@
1
- import { getValueForStyle } from '../utils/getValueForStyle.js'
2
-
3
1
  // Border path rendered behind the pattern content in Key panel symbols (20×20 coordinate space).
4
2
  export const KEY_BORDER_PATH = '<path d="M19 2.862v14.275c0 1.028-.835 1.862-1.862 1.862H2.863c-1.028 0-1.862-.835-1.862-1.862V2.862C1.001 1.834 1.836 1 2.863 1h14.275C18.166 1 19 1.835 19 2.862z" fill="{{backgroundColor}}" stroke="{{foregroundColor}}" stroke-width="2"/>'
3
+ // Minimum oversampling — keeps 16×16 physical pixels as the floor so patterns remain crisp.
4
+ const PATTERN_MIN_PIXEL_RATIO = 2
5
+
6
+ export const getEffectivePixelRatio = (pixelRatio) => Math.max(PATTERN_MIN_PIXEL_RATIO, pixelRatio * 2)
5
7
 
6
8
  export const hashString = (str) => {
7
9
  let hash = 0
@@ -32,68 +34,3 @@ export const injectColors = (content, foregroundColor, backgroundColor) =>
32
34
  * @returns {boolean}
33
35
  */
34
36
  export const hasPattern = (dataset) => !!(dataset.fillPattern || dataset.fillPatternSvgContent)
35
-
36
- /**
37
- * Returns the raw (un-coloured) inner SVG content for a dataset's pattern.
38
- * Precedence: inline fillPatternSvgContent → named fillPattern from registry.
39
- *
40
- * @param {Object} dataset
41
- * @param {Object} patternRegistry
42
- * @returns {string|null}
43
- */
44
- export const getPatternInnerContent = (dataset, patternRegistry) => {
45
- if (dataset.fillPatternSvgContent) {
46
- return dataset.fillPatternSvgContent
47
- }
48
- if (dataset.fillPattern) {
49
- return patternRegistry?.get(dataset.fillPattern)?.svgContent ?? null
50
- }
51
- return null
52
- }
53
-
54
- /**
55
- * Returns a deterministic image ID for a pattern + resolved colour + pixel ratio combination.
56
- *
57
- * @param {Object} dataset
58
- * @param {string} mapStyleId
59
- * @param {Object} patternRegistry
60
- * @param {number} [pixelRatio=1]
61
- * @returns {string|null}
62
- */
63
- // Minimum oversampling — keeps 16×16 physical pixels as the floor so patterns remain crisp.
64
- export const PATTERN_MIN_PIXEL_RATIO = 2
65
-
66
- export const getPatternImageId = (dataset, mapStyleId, patternRegistry, pixelRatio = 1) => {
67
- const innerContent = getPatternInnerContent(dataset, patternRegistry)
68
- if (!innerContent) {
69
- return null
70
- }
71
- const fg = getValueForStyle(dataset.fillPatternForegroundColor, mapStyleId) || 'black'
72
- const bg = getValueForStyle(dataset.fillPatternBackgroundColor, mapStyleId) || 'transparent'
73
- const effectiveRatio = Math.max(PATTERN_MIN_PIXEL_RATIO, pixelRatio)
74
- return `pattern-${hashString(innerContent + fg + bg)}-${effectiveRatio}x`
75
- }
76
-
77
- /**
78
- * Returns colour-injected SVG path content for use in Key panel pattern symbols.
79
- * Returns { border, content } where border is the rounded-rect outline and content
80
- * is the pattern fill. Returns null if the dataset has no pattern.
81
- *
82
- * @param {Object} dataset
83
- * @param {string} mapStyleId
84
- * @param {Object} patternRegistry
85
- * @returns {{ border: string, content: string }|null}
86
- */
87
- export const getKeyPatternPaths = (dataset, mapStyleId, patternRegistry) => {
88
- const innerContent = getPatternInnerContent(dataset, patternRegistry)
89
- if (!innerContent) {
90
- return null
91
- }
92
- const fg = getValueForStyle(dataset.fillPatternForegroundColor, mapStyleId) || 'black'
93
- const bg = getValueForStyle(dataset.fillPatternBackgroundColor, mapStyleId) || 'transparent'
94
- const borderStroke = getValueForStyle(dataset.stroke, mapStyleId) || fg
95
- return {
96
- border: injectColors(KEY_BORDER_PATH, borderStroke, bg),
97
- content: injectColors(innerContent, fg, bg)
98
- }
99
- }
@@ -1,17 +1,9 @@
1
1
  import {
2
2
  hashString,
3
3
  injectColors,
4
- hasPattern,
5
- getPatternInnerContent,
6
- getPatternImageId,
7
- getKeyPatternPaths,
8
- KEY_BORDER_PATH
4
+ hasPattern
9
5
  } from './patternUtils.js'
10
6
 
11
- const mockRegistry = {
12
- get: (id) => id === 'dot' ? { id: 'dot', svgContent: '<path d="M4 4" fill="{{foregroundColor}}"/>' } : undefined
13
- }
14
-
15
7
  describe('hashString', () => {
16
8
  test('returns a non-empty string', () => {
17
9
  expect(typeof hashString('hello')).toBe('string')
@@ -61,117 +53,3 @@ describe('hasPattern', () => {
61
53
  expect(hasPattern({ fill: 'red' })).toBe(false)
62
54
  })
63
55
  })
64
-
65
- describe('getPatternInnerContent', () => {
66
- test('returns fillPatternSvgContent when set (inline SVG takes precedence)', () => {
67
- const dataset = { fillPatternSvgContent: '<path d="custom"/>', fillPattern: 'dot' }
68
- expect(getPatternInnerContent(dataset, mockRegistry)).toBe('<path d="custom"/>')
69
- })
70
-
71
- test('returns svgContent from registry for a named fillPattern', () => {
72
- const dataset = { fillPattern: 'dot' }
73
- expect(getPatternInnerContent(dataset, mockRegistry)).toBe('<path d="M4 4" fill="{{foregroundColor}}"/>')
74
- })
75
-
76
- test('returns null for an unregistered fillPattern name', () => {
77
- const dataset = { fillPattern: 'unknown-pattern' }
78
- expect(getPatternInnerContent(dataset, mockRegistry)).toBeNull()
79
- })
80
-
81
- test('returns null when no pattern is configured', () => {
82
- expect(getPatternInnerContent({}, mockRegistry)).toBeNull()
83
- })
84
- })
85
-
86
- describe('getPatternImageId', () => {
87
- test('returns a deterministic string id', () => {
88
- const dataset = { fillPattern: 'dot', fillPatternForegroundColor: 'red', fillPatternBackgroundColor: 'blue' }
89
- const id = getPatternImageId(dataset, 'style-a', mockRegistry)
90
- expect(typeof id).toBe('string')
91
- expect(id).toMatch(/^pattern-/)
92
- expect(id).toBe(getPatternImageId(dataset, 'style-a', mockRegistry))
93
- })
94
-
95
- test('returns null when no pattern content is found', () => {
96
- expect(getPatternImageId({ fillPattern: 'unknown' }, 'style-a', mockRegistry)).toBeNull()
97
- })
98
-
99
- test('produces different ids for different colours', () => {
100
- const base = { fillPattern: 'dot' }
101
- const idA = getPatternImageId({ ...base, fillPatternForegroundColor: 'red' }, 'style-a', mockRegistry)
102
- const idB = getPatternImageId({ ...base, fillPatternForegroundColor: 'blue' }, 'style-a', mockRegistry)
103
- expect(idA).not.toBe(idB)
104
- })
105
-
106
- test('floors effective ratio at PATTERN_MIN_PIXEL_RATIO so low-DPI ids match 2x', () => {
107
- const dataset = { fillPattern: 'dot' }
108
- const id1x = getPatternImageId(dataset, 'style-a', mockRegistry, 1)
109
- const id2x = getPatternImageId(dataset, 'style-a', mockRegistry, 2)
110
- expect(id1x).toBe(id2x)
111
- expect(id1x).toMatch(/-2x$/)
112
- })
113
-
114
- test('produces different ids for pixelRatios above the floor', () => {
115
- const dataset = { fillPattern: 'dot' }
116
- const id2x = getPatternImageId(dataset, 'style-a', mockRegistry, 2)
117
- const id3x = getPatternImageId(dataset, 'style-a', mockRegistry, 3)
118
- expect(id2x).not.toBe(id3x)
119
- expect(id2x).toMatch(/-2x$/)
120
- expect(id3x).toMatch(/-3x$/)
121
- })
122
-
123
- test('falls back to "black" foreground and "transparent" background when colours are absent', () => {
124
- const id = getPatternImageId({ fillPattern: 'dot' }, 'style-a', mockRegistry)
125
- const idExplicit = getPatternImageId(
126
- { fillPattern: 'dot', fillPatternForegroundColor: 'black', fillPatternBackgroundColor: 'transparent' },
127
- 'style-a',
128
- mockRegistry
129
- )
130
- expect(id).toBe(idExplicit)
131
- })
132
- })
133
-
134
- describe('getKeyPatternPaths', () => {
135
- test('returns border and content strings with colours injected', () => {
136
- const dataset = {
137
- fillPattern: 'dot',
138
- fillPatternForegroundColor: 'red',
139
- fillPatternBackgroundColor: 'white',
140
- stroke: 'black'
141
- }
142
- const result = getKeyPatternPaths(dataset, 'style-a', mockRegistry)
143
- expect(result).not.toBeNull()
144
- expect(result.border).toContain('black') // stroke colour
145
- expect(result.border).toContain('white') // background colour
146
- expect(result.content).toContain('red') // foreground colour
147
- expect(result.border).not.toContain('{{foregroundColor}}')
148
- expect(result.content).not.toContain('{{foregroundColor}}')
149
- })
150
-
151
- test('returns null when no pattern content is found', () => {
152
- expect(getKeyPatternPaths({ fillPattern: 'unknown' }, 'style-a', mockRegistry)).toBeNull()
153
- })
154
-
155
- test('falls back to "black" fg and "transparent" bg when colour properties are absent', () => {
156
- const result = getKeyPatternPaths({ fillPattern: 'dot' }, 'style-a', mockRegistry)
157
- expect(result).not.toBeNull()
158
- expect(result.content).toContain('black')
159
- expect(result.border).toContain('transparent')
160
- })
161
-
162
- test('border stroke falls back to foreground colour when stroke is absent', () => {
163
- const result = getKeyPatternPaths(
164
- { fillPattern: 'dot', fillPatternForegroundColor: 'green' },
165
- 'style-a',
166
- mockRegistry
167
- )
168
- expect(result).not.toBeNull()
169
- // borderStroke falls back to fg ('green'), so the border uses green for both stroke and background
170
- expect(result.border).toContain('green')
171
- })
172
-
173
- test('KEY_BORDER_PATH contains foregroundColor and backgroundColor tokens', () => {
174
- expect(KEY_BORDER_PATH).toContain('{{foregroundColor}}')
175
- expect(KEY_BORDER_PATH).toContain('{{backgroundColor}}')
176
- })
177
- })
@@ -14,23 +14,21 @@ const SYMBOL_STYLE_PROPS = new Set([
14
14
  export const hasSymbol = (dataset) => !!(dataset.symbol || dataset.symbolSvgContent)
15
15
 
16
16
  /**
17
- * Resolves the symbolDef for a dataset's symbol config.
17
+ * Returns true if a marker item should render as a standalone label with no symbol.
18
+ * Requires label content and at least one of symbol/symbolSvgContent to be explicitly null.
19
+ * Undefined values fall through to render-time default symbol resolution.
18
20
  *
19
- * dataset.symbol is a string symbol ID (e.g. 'pin').
20
- * dataset.symbolSvgContent is inline SVG content for a custom symbol.
21
- *
22
- * @param {Object} dataset
23
- * @param {Object} symbolRegistry
24
- * @returns {Object|undefined}
21
+ * @param {Object} marker
22
+ * @returns {boolean}
25
23
  */
26
- export const getSymbolDef = (dataset, symbolRegistry) => {
27
- if (dataset.symbolSvgContent) {
28
- return { svg: dataset.symbolSvgContent }
24
+ export const isStandaloneLabel = (marker) => {
25
+ if (!marker.label) {
26
+ return false
29
27
  }
30
- if (dataset.symbol) {
31
- return symbolRegistry.get(dataset.symbol)
28
+ if (marker.symbol || marker.symbolSvgContent) {
29
+ return false
32
30
  }
33
- return undefined
31
+ return marker.symbol === null || marker.symbolSvgContent === null
34
32
  }
35
33
 
36
34
  /**
@@ -1,15 +1,11 @@
1
1
  import {
2
2
  hasSymbol,
3
- getSymbolDef,
3
+ isStandaloneLabel,
4
4
  getSymbolStyleColors,
5
5
  getSymbolViewBox,
6
6
  getSymbolAnchor
7
7
  } from './symbolUtils.js'
8
8
 
9
- const mockRegistry = (defs = {}) => ({
10
- get: jest.fn((id) => defs[id])
11
- })
12
-
13
9
  // ─── hasSymbol ────────────────────────────────────────────────────────────────
14
10
 
15
11
  describe('hasSymbol', () => {
@@ -30,34 +26,32 @@ describe('hasSymbol', () => {
30
26
  })
31
27
  })
32
28
 
33
- // ─── getSymbolDef ─────────────────────────────────────────────────────────────
29
+ // ─── isStandaloneLabel ────────────────────────────────────────────────────────
30
+
31
+ describe('isStandaloneLabel', () => {
32
+ it('returns false when marker has no label', () => {
33
+ expect(isStandaloneLabel({})).toBe(false)
34
+ expect(isStandaloneLabel({ symbol: null })).toBe(false)
35
+ })
34
36
 
35
- describe('getSymbolDef', () => {
36
- it('returns undefined when dataset has no symbol', () => {
37
- expect(getSymbolDef({}, mockRegistry())).toBeUndefined()
37
+ it('returns false when marker has a truthy symbol string (line 28)', () => {
38
+ expect(isStandaloneLabel({ label: 'My label', symbol: 'pin' })).toBe(false)
38
39
  })
39
40
 
40
- it('looks up string symbol id in the registry', () => {
41
- const pinDef = { id: 'pin', svg: '<g/>' }
42
- const registry = mockRegistry({ pin: pinDef })
43
- expect(getSymbolDef({ symbol: 'pin' }, registry)).toBe(pinDef)
41
+ it('returns false when marker has symbolSvgContent (line 28)', () => {
42
+ expect(isStandaloneLabel({ label: 'My label', symbolSvgContent: '<circle/>' })).toBe(false)
44
43
  })
45
44
 
46
- it('returns undefined for an unregistered string symbol', () => {
47
- expect(getSymbolDef({ symbol: 'missing' }, mockRegistry())).toBeUndefined()
45
+ it('returns false when label is present but both symbol and symbolSvgContent are undefined (line 31)', () => {
46
+ expect(isStandaloneLabel({ label: 'My label' })).toBe(false)
48
47
  })
49
48
 
50
- it('returns inline def from symbolSvgContent with svg key', () => {
51
- const dataset = { symbolSvgContent: '<circle/>', symbolViewBox: '0 0 10 10' }
52
- const result = getSymbolDef(dataset, mockRegistry())
53
- expect(result.svg).toBe('<circle/>')
49
+ it('returns true when label is present and symbol is explicitly null (line 31)', () => {
50
+ expect(isStandaloneLabel({ label: 'My label', symbol: null })).toBe(true)
54
51
  })
55
52
 
56
- it('symbolSvgContent takes precedence over symbol id', () => {
57
- const pinDef = { id: 'pin', svg: '<g/>' }
58
- const registry = mockRegistry({ pin: pinDef })
59
- const result = getSymbolDef({ symbol: 'pin', symbolSvgContent: '<circle/>' }, registry)
60
- expect(result.svg).toBe('<circle/>')
53
+ it('returns true when label is present and symbolSvgContent is explicitly null (line 31)', () => {
54
+ expect(isStandaloneLabel({ label: 'My label', symbolSvgContent: null })).toBe(true)
61
55
  })
62
56
  })
63
57
 
@@ -77,13 +71,11 @@ describe('getSymbolStyleColors', () => {
77
71
  symbol: 'pin',
78
72
  symbolBackgroundColor: '#ff0000',
79
73
  symbolForegroundColor: '#ffffff',
80
- symbolHaloWidth: '2',
81
74
  symbolGraphic: 'cross'
82
75
  }
83
76
  expect(getSymbolStyleColors(dataset)).toEqual({
84
77
  backgroundColor: '#ff0000',
85
78
  foregroundColor: '#ffffff',
86
- haloWidth: '2',
87
79
  graphic: 'cross'
88
80
  })
89
81
  })
@@ -1 +0,0 @@
1
- .im-c-mark-location{display:flex;justify-content:center;gap:var(--divider-gap)}.im-o-app--mobile .im-c-mark-location .im-c-button-wrapper,.im-o-app--mobile .im-c-mark-location .im-c-map-button{flex:1 1 auto;min-width:fit-content}.im-o-app--mouse.im-o-app--tablet .im-c-mark-location .im-c-map-button,.im-o-app--mouse.im-o-app--desktop .im-c-mark-location .im-c-map-button{min-width:150px}.im-o-app--mobile .im-c-mark-location .im-c-map-button{flex:1 1 auto;min-width:fit-content}
@@ -1,24 +0,0 @@
1
- .im-c-mark-location {
2
- display: flex;
3
- justify-content: center;
4
- gap: var(--divider-gap);
5
- }
6
-
7
- .im-o-app--mobile .im-c-mark-location {
8
- .im-c-button-wrapper, .im-c-map-button {
9
- flex: 1 1 auto;
10
- min-width: fit-content;
11
- }
12
- }
13
-
14
- .im-o-app--mouse.im-o-app--tablet,
15
- .im-o-app--mouse.im-o-app--desktop {
16
- .im-c-mark-location .im-c-map-button {
17
- min-width: 150px;
18
- }
19
- }
20
-
21
- .im-o-app--mobile .im-c-mark-location .im-c-map-button {
22
- flex: 1 1 auto;
23
- min-width: fit-content;
24
- }