@defra/interactive-map 0.0.43-alpha → 0.0.44-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 (159) 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.md +16 -8
  7. package/docs/plugins/draw.md +1 -1
  8. package/package.json +1 -1
  9. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  10. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  11. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  12. package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
  13. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
  14. package/plugins/beta/map-styles/dist/umd/index.js +1 -1
  15. package/plugins/datasets/dist/css/index.css +1 -1
  16. package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
  17. package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
  18. package/plugins/datasets/src/components/LayersMenu/Layers.module.scss +1 -0
  19. package/plugins/datasets/src/initialise/DatasetsInit.jsx +2 -2
  20. package/plugins/datasets/src/initialise/DatasetsInit.test.jsx +6 -5
  21. package/plugins/draw/dist/esm/im-draw-ml-adapter.js +1 -1
  22. package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -1
  23. package/plugins/draw/dist/esm/im-draw-plugin.js +1 -1
  24. package/plugins/draw/dist/esm/index.js +1 -1
  25. package/plugins/draw/dist/umd/im-draw-ml-adapter.js +1 -1
  26. package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
  27. package/plugins/draw/dist/umd/im-draw-plugin.js +1 -1
  28. package/plugins/draw/dist/umd/index.js +1 -1
  29. package/plugins/draw/src/DrawInit.jsx +31 -26
  30. package/plugins/draw/src/DrawInit.test.jsx +55 -12
  31. package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +113 -114
  32. package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.test.js +82 -9
  33. package/plugins/draw/src/adapters/maplibre/drawEvents.js +3 -1
  34. package/plugins/draw/src/adapters/maplibre/modes/createDrawMode.test.js +1 -1
  35. package/plugins/draw/src/adapters/maplibre/modes/drawMode/__helpers__/harness.js +18 -9
  36. package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.js +9 -1
  37. package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.test.js +11 -1
  38. package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.js +8 -2
  39. package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.test.js +35 -0
  40. package/plugins/draw/src/adapters/maplibre/modes/drawMode/pointerHandlers.js +29 -19
  41. package/plugins/draw/src/adapters/maplibre/modes/drawMode/pointerHandlers.test.js +17 -2
  42. package/plugins/draw/src/adapters/maplibre/modes/drawMode/undoHandlers.test.js +1 -1
  43. package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.js +35 -29
  44. package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.test.js +24 -6
  45. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.js +22 -20
  46. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.js +1 -1
  47. package/plugins/draw/src/adapters/maplibre/modes/editPointMode.js +2 -2
  48. package/plugins/draw/src/adapters/maplibre/modes/editPointMode.test.js +3 -3
  49. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/geometryHelpers.js +31 -0
  50. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/geometryHelpers.test.js +8 -1
  51. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.js +38 -30
  52. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.test.js +20 -3
  53. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.js +5 -5
  54. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.js +2 -2
  55. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.test.js +2 -2
  56. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.js +3 -3
  57. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.test.js +7 -7
  58. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.js +4 -4
  59. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.test.js +14 -14
  60. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexQueries.js +12 -32
  61. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexQueries.test.js +10 -10
  62. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.js +48 -22
  63. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.test.js +74 -6
  64. package/plugins/draw/src/adapters/maplibre/utils/snapMovement.js +2 -2
  65. package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.js +6 -0
  66. package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.test.js +9 -0
  67. package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.js +15 -2
  68. package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.test.js +27 -1
  69. package/plugins/draw/src/adapters/openlayers/draw/DrawMode.test.js +1 -1
  70. package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +31 -17
  71. package/plugins/draw/src/adapters/openlayers/draw/drawInput.test.js +55 -1
  72. package/plugins/draw/src/adapters/openlayers/draw/vertexPlacement.js +13 -7
  73. package/plugins/draw/src/adapters/openlayers/draw/vertexPlacement.test.js +7 -15
  74. package/plugins/draw/src/adapters/openlayers/edit/EditMode.js +36 -4
  75. package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +41 -1
  76. package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +35 -25
  77. package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +40 -2
  78. package/plugins/draw/src/adapters/openlayers/edit/nudge.js +2 -2
  79. package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +2 -2
  80. package/plugins/draw/src/adapters/openlayers/point/drawPointMode.js +32 -57
  81. package/plugins/draw/src/adapters/openlayers/point/drawPointMode.test.js +14 -13
  82. package/plugins/draw/src/adapters/openlayers/point/editPointMode.js +1 -1
  83. package/plugins/draw/src/adapters/openlayers/point/editPointMode.test.js +17 -1
  84. package/plugins/draw/src/events.js +4 -4
  85. package/plugins/draw/src/events.test.js +1 -1
  86. package/plugins/draw/src/hooks/useSpatialList.js +180 -0
  87. package/plugins/draw/src/hooks/useSpatialList.test.js +339 -0
  88. package/plugins/draw/src/utils/spatial.js +1 -31
  89. package/plugins/draw/src/utils/spatial.test.js +1 -24
  90. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  91. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  92. package/plugins/interact/dist/umd/index.js +1 -1
  93. package/plugins/interact/src/InteractInit.jsx +2 -2
  94. package/plugins/interact/src/InteractInit.test.js +3 -3
  95. package/plugins/interact/src/events.js +5 -0
  96. package/plugins/interact/src/hooks/useAttachEvents.js +20 -0
  97. package/plugins/interact/src/hooks/useAttachEvents.test.js +44 -1
  98. package/plugins/interact/src/hooks/useCrossHairVisibility.js +4 -4
  99. package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +7 -7
  100. package/plugins/interact/src/hooks/{useMapItemList.js → useSpatialList.js} +87 -52
  101. package/plugins/interact/src/hooks/{useMapItemList.test.js → useSpatialList.test.js} +152 -71
  102. package/plugins/interact/src/manifest.js +12 -1
  103. package/plugins/interact/src/manifest.test.js +20 -0
  104. package/plugins/interact/src/utils/spatial.js +1 -1
  105. package/providers/beta/openlayers/src/utils/queryFeatures.js +1 -1
  106. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  107. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  108. package/providers/maplibre/dist/umd/index.js +1 -1
  109. package/providers/maplibre/src/utils/labels.js +2 -2
  110. package/providers/maplibre/src/utils/labels.test.js +5 -5
  111. package/providers/maplibre/src/utils/spatial.js +0 -57
  112. package/providers/maplibre/src/utils/spatial.test.js +0 -27
  113. package/src/App/components/CrossHair/CrossHair.jsx +10 -4
  114. package/src/App/components/CrossHair/CrossHair.module.scss +16 -12
  115. package/src/App/components/CrossHair/CrossHair.test.jsx +9 -4
  116. package/src/App/components/Hints/Hints.jsx +3 -3
  117. package/src/App/components/Hints/Hints.test.jsx +3 -3
  118. package/src/App/components/MapButton/MapButton.module.scss +1 -2
  119. package/src/App/components/{MoveControls/MoveControls.jsx → MapControls/MapControls.jsx} +10 -10
  120. package/src/App/components/{MoveControls/MoveControls.module.scss → MapControls/MapControls.module.scss} +25 -25
  121. package/src/App/components/{MoveControls/MoveControls.test.jsx → MapControls/MapControls.test.jsx} +48 -48
  122. package/src/App/components/Markers/Markers.jsx +2 -2
  123. package/src/App/components/Markers/Markers.test.jsx +3 -3
  124. package/src/App/components/Markers/SymbolMarker.test.jsx +2 -2
  125. package/src/App/components/Viewport/MapStatus.jsx +1 -1
  126. package/src/App/components/Viewport/SpatialList.jsx +47 -0
  127. package/src/App/components/Viewport/{Features.module.scss → SpatialList.module.scss} +3 -3
  128. package/src/App/components/Viewport/{Features.test.jsx → SpatialList.test.jsx} +60 -43
  129. package/src/App/components/Viewport/Viewport.jsx +20 -10
  130. package/src/App/components/Viewport/Viewport.test.jsx +18 -2
  131. package/src/App/hooks/useCrossHairAPI.js +5 -5
  132. package/src/App/hooks/useCrossHairAPI.test.js +5 -5
  133. package/src/App/hooks/useHintsAPI.js +5 -5
  134. package/src/App/hooks/useSpatialListFocus.js +296 -0
  135. package/src/App/hooks/{useFeatureFocus.test.js → useSpatialListFocus.test.js} +318 -163
  136. package/src/App/hooks/useSpatialListItems.js +51 -0
  137. package/src/App/hooks/useSpatialListItems.test.js +217 -0
  138. package/src/App/initialiseApp.js +6 -3
  139. package/src/App/initialiseApp.test.js +11 -0
  140. package/src/App/registry/spatialListRegistry.js +104 -0
  141. package/src/App/registry/spatialListRegistry.test.js +218 -0
  142. package/src/App/store/AppProvider.jsx +3 -2
  143. package/src/config/appConfig.js +14 -14
  144. package/src/config/appConfig.test.js +19 -19
  145. package/src/config/defaults.js +2 -2
  146. package/src/config/events.js +8 -8
  147. package/src/scss/main.scss +2 -2
  148. package/src/test-utils.js +8 -0
  149. package/src/types.js +11 -9
  150. package/src/utils/findNearestItemInDirection.js +29 -0
  151. package/src/utils/findNearestItemInDirection.test.js +67 -0
  152. package/src/utils/globalAltShortcuts.js +12 -0
  153. package/src/utils/globalAltShortcuts.test.js +27 -0
  154. package/src/utils/spatialNavigate.js +49 -0
  155. package/src/utils/spatialNavigate.test.js +42 -0
  156. package/src/App/components/Viewport/Features.jsx +0 -49
  157. package/src/App/hooks/useFeatureFocus.js +0 -257
  158. package/src/App/hooks/useFeatureItems.js +0 -39
  159. package/src/App/hooks/useFeatureItems.test.js +0 -155
@@ -1,6 +1,7 @@
1
1
  import { getSnapInstance, clearSnapIndicator } from '../../utils/snapHelpers.js'
2
2
  import { ARROW_KEYS, ARROW_OFFSETS, isInteractiveElementFocused, isUndoShortcut, sharedKeyboardHandlers } from '../../utils/keyboardShortcuts.js'
3
3
  import { getCoords } from './geometryHelpers.js'
4
+ import { stopIfGlobalAltKey } from '../../../../../../../src/utils/globalAltShortcuts.js'
4
5
 
5
6
  /**
6
7
  * Keyboard interaction for the vertex-edit mode: arrow-key vertex movement/insertion,
@@ -9,7 +10,7 @@ import { getCoords } from './geometryHelpers.js'
9
10
  export const keyboardHandlers = {
10
11
  ...sharedKeyboardHandlers,
11
12
 
12
- onKeydown (state, e) {
13
+ onKeydown (state, event) {
13
14
  if (isInteractiveElementFocused(state)) {
14
15
  return
15
16
  }
@@ -17,40 +18,40 @@ export const keyboardHandlers = {
17
18
  state.interfaceType = 'keyboard'
18
19
  this.hideTouchVertexIndicator(state)
19
20
 
20
- if (e.key === ' ') {
21
- this.handleSpace(state, e)
21
+ if (event.key === ' ') {
22
+ this.handleSpace(state, event)
22
23
  return
23
24
  }
24
- if (ARROW_KEYS.has(e.key) && state.selectedVertexIndex >= 0) {
25
- this.handleArrowKey(state, e)
25
+ if (ARROW_KEYS.has(event.key) && state.selectedVertexIndex >= 0) {
26
+ this.handleArrowKey(state, event)
26
27
  return
27
28
  }
28
- if (e.key === 'Escape') {
29
+ if (event.key === 'Escape') {
29
30
  this.changeMode(state, { isPanEnabled: true, selectedVertexIndex: -1, selectedVertexType: null })
30
31
  return
31
32
  }
32
- if (isUndoShortcut(e)) {
33
- this.handleUndoShortcut(state, e)
33
+ if (isUndoShortcut(event)) {
34
+ this.handleUndoShortcut(state, event)
34
35
  }
35
36
  },
36
37
 
37
38
  // Space always cancels the default; with no active selection it starts keyboard editing.
38
- handleSpace (state, e) {
39
- e.preventDefault()
39
+ handleSpace (state, event) {
40
+ event.preventDefault()
40
41
  if (state.selectedVertexIndex < 0) {
41
42
  this.startKeyboardSelection(state)
42
43
  }
43
44
  },
44
45
 
45
46
  // Alt+arrow steps to the next vertex/midpoint; a plain arrow nudges the selected vertex.
46
- handleArrowKey (state, e) {
47
- e.preventDefault()
48
- e.stopPropagation()
49
- if (e.altKey) {
50
- this.updateVertex(state, e.key)
47
+ handleArrowKey (state, event) {
48
+ event.preventDefault()
49
+ event.stopPropagation()
50
+ if (event.altKey) {
51
+ this.updateVertex(state, event.key)
51
52
  return
52
53
  }
53
- this.moveVertexByKey(state, e)
54
+ this.moveVertexByKey(state, event)
54
55
  },
55
56
 
56
57
  // Space with no active selection: select the first vertex for keyboard editing.
@@ -59,11 +60,11 @@ export const keyboardHandlers = {
59
60
  if (snap) {
60
61
  clearSnapIndicator(snap, this.map)
61
62
  }
62
- if (!state.vertecies?.length) {
63
- state.vertecies = this.getVerticies(state.featureId)
63
+ if (!state.vertices?.length) {
64
+ state.vertices = this.getVertices(state.featureId)
64
65
  state.midpoints = this.getMidpoints(state.featureId)
65
66
  }
66
- if (!state.vertecies?.length) {
67
+ if (!state.vertices?.length) {
67
68
  return
68
69
  }
69
70
  state.isPanEnabled = false
@@ -71,9 +72,9 @@ export const keyboardHandlers = {
71
72
  },
72
73
 
73
74
  // Arrow key with a selected vertex: insert (midpoint) or nudge the vertex, honouring snap.
74
- moveVertexByKey (state, e) {
75
+ moveVertexByKey (state, event) {
75
76
  if (state.selectedVertexType === 'midpoint') {
76
- this.insertVertex(state, e)
77
+ this.insertVertex(state, event)
77
78
  return
78
79
  }
79
80
 
@@ -89,25 +90,32 @@ export const keyboardHandlers = {
89
90
  state._keyboardMoveStartIndex = state.selectedVertexIndex
90
91
  }
91
92
 
92
- this.moveVertex(state, this._keyboardMoveTarget(state, e, currentCoord))
93
+ this.moveVertex(state, this._keyboardMoveTarget(state, event, currentCoord))
93
94
  },
94
95
 
95
96
  // Resolve the destination coordinate for a keyboard nudge, applying or breaking
96
97
  // snap — delegates to the shared resolver (utils/snapMovement.js) also used by
97
- // MoveControls' nudgeVertexByDelta, so both snap identically.
98
- _keyboardMoveTarget (state, e, currentCoord) {
99
- const [dx, dy] = ARROW_OFFSETS[e.key]
100
- return this.resolveSnapTarget(state, dx, dy, currentCoord, () => this.getNewCoord(state, e))
98
+ // MapControls' nudgeVertexByDelta, so both snap identically.
99
+ _keyboardMoveTarget (state, event, currentCoord) {
100
+ const [dx, dy] = ARROW_OFFSETS[event.key]
101
+ return this.resolveSnapTarget(state, dx, dy, currentCoord, () => this.getNewCoord(state, event))
101
102
  },
102
103
 
103
- onKeyup (state, e) {
104
+ onKeyup (state, event) {
104
105
  if (isInteractiveElementFocused(state)) {
105
106
  return
106
107
  }
107
108
 
108
109
  state.interfaceType = 'keyboard'
109
- if (ARROW_KEYS.has(e.key) && state.selectedVertexIndex >= 0) {
110
- e.stopPropagation()
110
+ // Only shadow while a vertex/midpoint is actually selected — that's the same condition
111
+ // gating this mode's own local Alt+Arrow handling below (and there's no local Enter
112
+ // meaning here at all, selected or not), so with nothing selected there's no local
113
+ // conflict to protect and the global map-label shortcuts should keep working.
114
+ if (state.selectedVertexIndex >= 0) {
115
+ stopIfGlobalAltKey(event)
116
+ }
117
+ if (ARROW_KEYS.has(event.key) && state.selectedVertexIndex >= 0) {
118
+ event.stopPropagation()
111
119
 
112
120
  // Push undo for keyboard move sequence
113
121
  if (state._keyboardMoveStartPosition && state._keyboardMoveStartIndex != null) {
@@ -121,7 +129,7 @@ export const keyboardHandlers = {
121
129
  state._keyboardMoveStartIndex = null
122
130
  }
123
131
  }
124
- if (e.key === 'Delete') {
132
+ if (event.key === 'Delete') {
125
133
  this.deleteVertex(state)
126
134
  }
127
135
  }
@@ -4,6 +4,23 @@ describe('keyboardHandlers', () => {
4
4
  const keydown = (ctx, state, key, extra = {}) => ctx.onKeydown(state, { key, preventDefault: jest.fn(), stopPropagation: jest.fn(), ...extra })
5
5
  const keyup = (ctx, state, key) => ctx.onKeyup(state, { key, stopPropagation: jest.fn() })
6
6
 
7
+ // stopIfGlobalAltKey's own branches are covered by src/utils/globalAltShortcuts.test.js —
8
+ // this just checks onKeyup only calls it once a vertex/midpoint is actually selected, same
9
+ // condition its own local Alt+Arrow handling already requires; with nothing selected there's
10
+ // no local meaning to protect, so the app-wide map-label shortcuts should keep working.
11
+ test('shadows Alt+Enter/Alt+Arrow from the app-wide label shortcuts only while something is selected', () => {
12
+ const { ctx, state } = createHarness()
13
+ const altEnter = (extra) => ({ key: 'Enter', altKey: true, stopPropagation: jest.fn(), ...extra })
14
+
15
+ const whileSelected = altEnter()
16
+ ctx.onKeyup({ ...state, selectedVertexIndex: 0 }, whileSelected)
17
+ expect(whileSelected.stopPropagation).toHaveBeenCalled()
18
+
19
+ const whileUnselected = altEnter()
20
+ ctx.onKeyup({ ...state, selectedVertexIndex: -1 }, whileUnselected)
21
+ expect(whileUnselected.stopPropagation).not.toHaveBeenCalled()
22
+ })
23
+
7
24
  // isInteractiveElementFocused's own branches are covered by utils/keyboardShortcuts.test.js
8
25
  // — this just checks onKeydown/onKeyup actually consult it.
9
26
  test('shortcuts are ignored while a form control outside the viewport has focus', () => {
@@ -28,7 +45,7 @@ describe('keyboardHandlers', () => {
28
45
  expect(updateSpy).toHaveBeenCalledTimes(1)
29
46
 
30
47
  map._snapInstance = null // no snap indicator to clear
31
- const s = { ...state, featureId: 'missing', vertecies: [], selectedVertexIndex: -1, isPanEnabled: true }
48
+ const s = { ...state, featureId: 'missing', vertices: [], selectedVertexIndex: -1, isPanEnabled: true }
32
49
  keydown(ctx, s, ' ')
33
50
  expect(s.isPanEnabled).toBe(true)
34
51
  })
@@ -43,7 +60,7 @@ describe('keyboardHandlers', () => {
43
60
  keydown(ctx, state, 'ArrowRight')
44
61
  expect(state._keyboardMoveStartIndex).toBe(1)
45
62
  expect(state._keyboardMoveStartPosition).toEqual([10, 0])
46
- expect(state.vertecies[1]).not.toEqual([10, 0])
63
+ expect(state.vertices[1]).not.toEqual([10, 0])
47
64
 
48
65
  // Still held → start position isn't reset to the intermediate (already-moved) position
49
66
  keydown(ctx, state, 'ArrowRight')
@@ -53,7 +70,7 @@ describe('keyboardHandlers', () => {
53
70
  test('an arrow key on a midpoint inserts a vertex; guards a missing feature or out-of-range vertex', () => {
54
71
  const { ctx, state } = createHarness()
55
72
  const insertSpy = jest.spyOn(ctx, 'insertVertex').mockImplementation(() => {})
56
- keydown(ctx, { ...state, selectedVertexIndex: state.vertecies.length, selectedVertexType: 'midpoint' }, 'ArrowRight')
73
+ keydown(ctx, { ...state, selectedVertexIndex: state.vertices.length, selectedVertexType: 'midpoint' }, 'ArrowRight')
57
74
  expect(insertSpy).toHaveBeenCalled()
58
75
 
59
76
  keydown(ctx, { ...state, featureId: 'missing', selectedVertexIndex: 0 }, 'ArrowRight')
@@ -28,7 +28,7 @@ export const pointerHandlers = {
28
28
  state.selectedVertexIndex = vertexIndex
29
29
  state.selectedVertexType = 'vertex'
30
30
  state.coordPath = coordPath
31
- const vertex = state.vertecies?.[vertexIndex]
31
+ const vertex = state.vertices?.[vertexIndex]
32
32
  if (vertex) {
33
33
  state._moveStartPosition = [...vertex]
34
34
  state._moveStartIndex = vertexIndex
@@ -47,7 +47,7 @@ export const pointerHandlers = {
47
47
  state.selectedVertexIndex = this.getVertexIndexFromMidpoint(state, coordPath)
48
48
  state.selectedVertexType = 'vertex'
49
49
  state.coordPath = null // Clear coordPath for midpoints
50
- this.map.fire(CUSTOM_DRAW_EVENTS.VERTEX_SELECTION, { index: state.selectedVertexIndex, numVertecies: state.vertecies.length })
50
+ this.map.fire(CUSTOM_DRAW_EVENTS.VERTEX_SELECTION, { index: state.selectedVertexIndex, numVertices: state.vertices.length })
51
51
  }
52
52
  },
53
53
 
@@ -60,7 +60,7 @@ export const pointerHandlers = {
60
60
  state.selectedVertexType = 'vertex'
61
61
  state._isInsertingVertex = false
62
62
  state._insertedVertexIndex = null
63
- this.map.fire(CUSTOM_DRAW_EVENTS.VERTEX_SELECTION, { index: insertedIndex, numVertecies: state.vertecies.length })
63
+ this.map.fire(CUSTOM_DRAW_EVENTS.VERTEX_SELECTION, { index: insertedIndex, numVertices: state.vertices.length })
64
64
  return
65
65
  }
66
66
  DirectSelect.onClick.call(this, state, e)
@@ -91,7 +91,7 @@ export const pointerHandlers = {
91
91
  },
92
92
 
93
93
  // Did the selected vertex actually change position during this interaction?
94
- // Reads the live feature (not the cached state.vertecies) for reliability.
94
+ // Reads the live feature (not the cached state.vertices) for reliability.
95
95
  _didVertexMove (state) {
96
96
  if (!state._moveStartPosition || state._moveStartIndex == null) {
97
97
  return false
@@ -115,7 +115,7 @@ export const pointerHandlers = {
115
115
  state._isInsertingVertex = false
116
116
  state._insertedVertexIndex = null
117
117
  // DirectSelect.onMouseUp fires draw.update but not draw.selectionchange, so broadcast the count here
118
- this.map.fire(CUSTOM_DRAW_EVENTS.VERTEX_SELECTION, { index: insertedIndex, numVertecies: state.vertecies.length })
118
+ this.map.fire(CUSTOM_DRAW_EVENTS.VERTEX_SELECTION, { index: insertedIndex, numVertices: state.vertices.length })
119
119
  },
120
120
 
121
121
  _recordMoveUndo (state) {
@@ -66,7 +66,7 @@ export const touchHandlers = {
66
66
  // alignment. Left uncorrected, the next onTouchstart's delta math re-anchors off that
67
67
  // drifted position, compounding a little further with every snapped drag (same fix as
68
68
  // editPointMode's own onTouchend, for the same reason).
69
- const vertex = state.vertecies[state.selectedVertexIndex]
69
+ const vertex = state.vertices[state.selectedVertexIndex]
70
70
  if (vertex) {
71
71
  this.updateTouchVertexTarget(state, scalePoint(this.map.project(vertex), state.scale))
72
72
  }
@@ -101,7 +101,7 @@ export const touchHandlers = {
101
101
  onTouchstart (state, e) {
102
102
  clearSnapState(getSnapInstance(this.map))
103
103
  // Always get fresh vertex data in case coordinates changed during previous edits
104
- const freshVertices = this.getVerticies(state.featureId)
104
+ const freshVertices = this.getVertices(state.featureId)
105
105
  const vertex = freshVertices?.[state.selectedVertexIndex]
106
106
  if (!vertex || !isOnSVG(e.target.parentNode)) {
107
107
  return
@@ -96,14 +96,14 @@ describe('touchHandlers', () => {
96
96
  ctx.onTouchstart(state, { target: svgTarget(state), touches: [{ clientX: 20, clientY: 20 }] })
97
97
 
98
98
  ctx.onTouchmove(state, { target: { parentNode: document.createElement('div') }, touches: [{ clientX: 5, clientY: 5 }] })
99
- expect(state.vertecies[1]).toEqual([10, 0]) // off-target move ignored
99
+ expect(state.vertices[1]).toEqual([10, 0]) // off-target move ignored
100
100
  ctx.onTouchmove(state, { target: svgTarget(state), touches: [{ clientX: 30, clientY: 40 }] })
101
101
  expect(state._touchMoved).toBe(true)
102
102
 
103
103
  state.getSnapEnabled = () => true
104
104
  map._snapInstance = { status: true, snapStatus: true, snapCoords: [7, 8], snapToClosestPoint: jest.fn() }
105
105
  ctx.onTouchmove(state, { target: svgTarget(state), touches: [{ clientX: 50, clientY: 60 }] })
106
- expect(state.vertecies[1]).toEqual([7, 8])
106
+ expect(state.vertices[1]).toEqual([7, 8])
107
107
  })
108
108
 
109
109
  test('onTap clears the snap indicator, then selects a vertex, inserts on a midpoint, or clears with no target', () => {
@@ -64,7 +64,7 @@ export const undoHandlers = {
64
64
  this._applyUndoAndSync(state, geojson, featureId)
65
65
 
66
66
  // Update touch vertex target position
67
- const vertex = state.vertecies[state.selectedVertexIndex]
67
+ const vertex = state.vertices[state.selectedVertexIndex]
68
68
  if (vertex) {
69
69
  this.updateTouchVertexTarget(state, scalePoint(this.map.project(vertex), state.scale))
70
70
  }
@@ -122,13 +122,13 @@ export const undoHandlers = {
122
122
  this._applyUndoAndSync(state, geojson, featureId)
123
123
 
124
124
  // Re-insertion always lands the vertex back at vertexIndex, so it is guaranteed present
125
- this.updateTouchVertexTarget(state, scalePoint(this.map.project(state.vertecies[vertexIndex]), state.scale))
125
+ this.updateTouchVertexTarget(state, scalePoint(this.map.project(state.vertices[vertexIndex]), state.scale))
126
126
  this.changeMode(state, { selectedVertexIndex: vertexIndex, selectedVertexType: 'vertex', coordPath: this.getCoordPath(state, vertexIndex) })
127
127
  },
128
128
 
129
129
  _applyUndoAndSync (state, geojson, featureId) {
130
130
  this._ctx.api.add(geojson)
131
- state.vertecies = this.getVerticies(featureId)
131
+ state.vertices = this.getVertices(featureId)
132
132
  state.midpoints = this.getMidpoints(featureId)
133
133
  this._ctx.store.render()
134
134
  this.fireGeometryChange(state)
@@ -55,23 +55,23 @@ describe('undoHandlers', () => {
55
55
  ctx.undoMoveVertex(state, { vertexIndex: 99, previousPosition: [0, 0], featureId: 'feat-1' })
56
56
  state.selectedVertexIndex = 0
57
57
  ctx.undoMoveVertex(state, { vertexIndex: 0, previousPosition: [3, 4], featureId: 'feat-1' })
58
- expect(ctx.getVerticies('feat-1')[0]).toEqual([3, 4])
58
+ expect(ctx.getVertices('feat-1')[0]).toEqual([3, 4])
59
59
 
60
60
  // Skips the touch target update when the selected vertex is gone
61
61
  ctx.undoMoveVertex({ ...state, selectedVertexIndex: 99 }, { vertexIndex: 0, previousPosition: [1, 1], featureId: 'feat-1' })
62
- expect(ctx.getVerticies('feat-1')[0]).toEqual([1, 1])
62
+ expect(ctx.getVertices('feat-1')[0]).toEqual([1, 1])
63
63
  })
64
64
 
65
65
  test('undoInsertVertex removes the inserted vertex and clears the selection', () => {
66
66
  const { ctx, state, api } = createHarness()
67
67
  ctx.undoInsertVertex(state, { vertexIndex: 1, featureId: 'feat-1' })
68
- expect(ctx.getVerticies('feat-1')).toHaveLength(3)
68
+ expect(ctx.getVertices('feat-1')).toHaveLength(3)
69
69
  expect(api.changeMode).toHaveBeenCalledWith('edit_vertex', expect.objectContaining({ selectedVertexIndex: -1 }))
70
70
 
71
71
  // Guards a missing feature and an out-of-range index
72
72
  ctx.undoInsertVertex({ ...state, featureId: 'missing' }, { vertexIndex: 0, featureId: 'missing' })
73
73
  ctx.undoInsertVertex(state, { vertexIndex: 999, featureId: 'feat-1' })
74
- expect(ctx.getVerticies('feat-1')).toHaveLength(3)
74
+ expect(ctx.getVertices('feat-1')).toHaveLength(3)
75
75
  })
76
76
 
77
77
  test('undoDeleteVertex re-inserts, handling boundary, fallback and missing feature', () => {
@@ -79,13 +79,13 @@ describe('undoHandlers', () => {
79
79
  ctx.undoDeleteVertex({ ...state, featureId: 'missing' }, { vertexIndex: 0, position: [1, 1], featureId: 'missing' })
80
80
  // Fallback: index far beyond any segment boundary → no result → no-op
81
81
  ctx.undoDeleteVertex(state, { vertexIndex: 999, position: [1, 1], featureId: 'feat-1' })
82
- expect(ctx.getVerticies('feat-1')).toHaveLength(4)
82
+ expect(ctx.getVertices('feat-1')).toHaveLength(4)
83
83
  // Success: re-insert at index 1
84
84
  ctx.undoDeleteVertex(state, { vertexIndex: 1, position: [1, 1], featureId: 'feat-1' })
85
- expect(ctx.getVerticies('feat-1')).toHaveLength(5)
85
+ expect(ctx.getVertices('feat-1')).toHaveLength(5)
86
86
  // Boundary: index === segment.start + segment.length
87
87
  const seg = getRingSegments(ctx.getFeature('feat-1'))[0]
88
88
  ctx.undoDeleteVertex(state, { vertexIndex: seg.start + seg.length, position: [2, 2], featureId: 'feat-1' })
89
- expect(ctx.getVerticies('feat-1')).toHaveLength(6)
89
+ expect(ctx.getVertices('feat-1')).toHaveLength(6)
90
90
  })
91
91
  })
@@ -33,7 +33,7 @@ export const vertexOperations = {
33
33
  },
34
34
 
35
35
  // Moves the selected vertex by an explicit (dx, dy) unit direction — the entry
36
- // point for MoveControls' D-pad (see mapProvider.activeMoveTarget in events.js),
36
+ // point for MapControls' D-pad (see mapProvider.activeMoveTarget in events.js),
37
37
  // as opposed to moveVertexByKey's KeyboardEvent-driven path. Each call is treated
38
38
  // as one complete, undoable action (no held-key sequencing, since a button click
39
39
  // has no "held" state to batch the way arrow keys do) — but still honours snap
@@ -55,7 +55,7 @@ export const vertexOperations = {
55
55
  },
56
56
 
57
57
  insertVertex (state, e) {
58
- const midIdx = state.selectedVertexIndex - state.vertecies.length
58
+ const midIdx = state.selectedVertexIndex - state.vertices.length
59
59
  const newCoord = this.getOffset(state.midpoints[midIdx], e)
60
60
  const feature = this.getFeature(state.featureId)
61
61
  const geojson = feature.toGeoJSON()
@@ -107,7 +107,7 @@ export const vertexOperations = {
107
107
  const coords = getModifiableCoords(geojson, result.segment.path)
108
108
  coords[result.localIdx] = [coord.lng, coord.lat]
109
109
  this._ctx.api.add(geojson)
110
- state.vertecies = this.getVerticies(state.featureId)
110
+ state.vertices = this.getVertices(state.featureId)
111
111
 
112
112
  this.map.fire('draw.geometrychange', state.feature)
113
113
  },
@@ -134,7 +134,7 @@ export const vertexOperations = {
134
134
  }
135
135
 
136
136
  // Save position for undo before deletion
137
- const deletedPosition = [...state.vertecies[state.selectedVertexIndex]]
137
+ const deletedPosition = [...state.vertices[state.selectedVertexIndex]]
138
138
  const deletedIndex = state.selectedVertexIndex
139
139
 
140
140
  // Remove the coordinate directly rather than via this._ctx.api.trash(), which routes through
@@ -21,7 +21,7 @@ describe('vertexOperations', () => {
21
21
  }))
22
22
 
23
23
  api.changeMode.mockClear()
24
- ctx.updateVertex({ featureId: 'missing', vertecies: [], selectedVertexIndex: -1 }, 'ArrowRight')
24
+ ctx.updateVertex({ featureId: 'missing', vertices: [], selectedVertexIndex: -1 }, 'ArrowRight')
25
25
  expect(api.changeMode).not.toHaveBeenCalled()
26
26
 
27
27
  // A vertex target carries a coordPath; a midpoint target does not
@@ -43,9 +43,9 @@ describe('vertexOperations', () => {
43
43
  const { ctx, state, map } = createHarness()
44
44
  state.selectedVertexIndex = 0
45
45
  state.selectedVertexType = 'vertex'
46
- const before = [...state.vertecies[0]]
46
+ const before = [...state.vertices[0]]
47
47
  ctx.nudgeVertexByDelta(state, 1, 0, true)
48
- expect(state.vertecies[0]).not.toEqual(before)
48
+ expect(state.vertices[0]).not.toEqual(before)
49
49
  expect(map._undoStack.pop()).toMatchObject({ type: 'move_vertex', vertexIndex: 0, previousPosition: before })
50
50
 
51
51
  map._undoStack.clear()
@@ -60,7 +60,7 @@ describe('vertexOperations', () => {
60
60
 
61
61
  // resolveSnapTarget's own snap/break-out-of-snap branches are covered by
62
62
  // utils/snapMovement.test.js — this just checks nudgeVertexByDelta wires a snap hit
63
- // through to the moved vertex, same as keyboard nudging (regression: MoveControls
63
+ // through to the moved vertex, same as keyboard nudging (regression: MapControls
64
64
  // bypassed snap entirely before resolveSnapTarget was shared).
65
65
  test('nudgeVertexByDelta honours an active snap', () => {
66
66
  const { ctx, state, map } = createHarness()
@@ -70,12 +70,12 @@ describe('vertexOperations', () => {
70
70
  map._snapInstance = { status: true, snapStatus: true, snapCoords: [7, 8], snapToClosestPoint: jest.fn() }
71
71
  ctx.nudgeVertexByDelta(state, 0, -1, true)
72
72
  expect(state._isSnapped).toBe(true)
73
- expect(state.vertecies[1]).toEqual([7, 8])
73
+ expect(state.vertices[1]).toEqual([7, 8])
74
74
  })
75
75
 
76
76
  test('insertVertex splits a midpoint into a new vertex, records undo and selects it', () => {
77
77
  const { ctx, state, api } = createHarness()
78
- ctx.insertVertex({ ...state, selectedVertexIndex: state.vertecies.length, selectedVertexType: 'midpoint' }, { key: 'ArrowRight', shiftKey: false })
78
+ ctx.insertVertex({ ...state, selectedVertexIndex: state.vertices.length, selectedVertexType: 'midpoint' }, { key: 'ArrowRight', shiftKey: false })
79
79
  expect(api.add).toHaveBeenCalled()
80
80
  expect(state.map ?? ctx.map._undoStack.length).toBeGreaterThan(0)
81
81
  expect(api.changeMode).toHaveBeenCalledWith('edit_vertex', expect.objectContaining({
@@ -84,14 +84,14 @@ describe('vertexOperations', () => {
84
84
 
85
85
  // Also works for an open line segment
86
86
  const line = createHarness(LINE())
87
- line.ctx.insertVertex({ ...line.state, selectedVertexIndex: line.state.vertecies.length, selectedVertexType: 'midpoint' }, { key: 'ArrowRight', shiftKey: false })
87
+ line.ctx.insertVertex({ ...line.state, selectedVertexIndex: line.state.vertices.length, selectedVertexType: 'midpoint' }, { key: 'ArrowRight', shiftKey: false })
88
88
  expect(line.api.add).toHaveBeenCalled()
89
89
  })
90
90
 
91
91
  test('insertVertex bails out when the midpoint maps to no segment', () => {
92
92
  const { ctx, state, api } = createHarness()
93
93
  // midIdx beyond the segment's midpoint count, but with a defined midpoint coord
94
- const badState = { ...state, selectedVertexIndex: state.vertecies.length + 4, midpoints: [...state.midpoints, [5, 5]] }
94
+ const badState = { ...state, selectedVertexIndex: state.vertices.length + 4, midpoints: [...state.midpoints, [5, 5]] }
95
95
  api.add.mockClear()
96
96
  ctx.insertVertex(badState, { key: 'ArrowRight', shiftKey: false })
97
97
  expect(api.add).not.toHaveBeenCalled()
@@ -101,20 +101,20 @@ describe('vertexOperations', () => {
101
101
  const { ctx, state, map } = createHarness()
102
102
  state.selectedVertexIndex = 0
103
103
  ctx.moveVertex(state, { lng: 1, lat: 2 })
104
- expect(state.vertecies[0]).toEqual([1, 2])
104
+ expect(state.vertices[0]).toEqual([1, 2])
105
105
 
106
106
  map._snapInstance = { snapStatus: true, snapCoords: [7, 8] }
107
107
  ctx.moveVertex(state, { lng: 1, lat: 2 }, { checkSnap: true })
108
- expect(state.vertecies[0]).toEqual([7, 8])
108
+ expect(state.vertices[0]).toEqual([7, 8])
109
109
 
110
- const before = [...state.vertecies]
110
+ const before = [...state.vertices]
111
111
  ctx.moveVertex({ ...state, selectedVertexIndex: 99 }, { lng: 3, lat: 3 })
112
- expect(ctx.getVerticies('feat-1')).toEqual(before)
112
+ expect(ctx.getVertices('feat-1')).toEqual(before)
113
113
 
114
114
  // checkSnap requested but no active snap → coordinate used as-is
115
115
  const h = createHarness()
116
116
  h.ctx.moveVertex({ ...h.state, selectedVertexIndex: 0 }, { lng: 1, lat: 2 }, { checkSnap: true })
117
- expect(h.ctx.getVerticies('feat-1')[0]).toEqual([1, 2])
117
+ expect(h.ctx.getVertices('feat-1')[0]).toEqual([1, 2])
118
118
  })
119
119
 
120
120
  test('deleteVertex removes a vertex, or no-ops for missing feature / bad index / minimum size', () => {
@@ -137,6 +137,6 @@ describe('vertexOperations', () => {
137
137
  const line = createHarness(LINE())
138
138
  line.state.selectedVertexIndex = 1
139
139
  line.ctx.deleteVertex(line.state)
140
- expect(line.ctx.getVerticies('feat-1')).toHaveLength(2)
140
+ expect(line.ctx.getVertices('feat-1')).toHaveLength(2)
141
141
  })
142
142
  })
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  getCoords,
3
+ getMidpointCoords,
3
4
  getRingSegments,
4
5
  getSegmentForIndex
5
6
  } from './geometryHelpers.js'
6
- import { spatialNavigate } from '../../../../utils/spatial.js'
7
+ import { spatialNavigate } from '../../../../../../../src/utils/spatialNavigate.js'
7
8
 
8
9
  export const vertexQueries = {
9
10
  findVertexIndex (coords, targetCoord, currentIdx) {
@@ -40,56 +41,35 @@ export const vertexQueries = {
40
41
  },
41
42
 
42
43
  syncVertices (state) {
43
- state.vertecies = this.getVerticies(state.featureId)
44
+ state.vertices = this.getVertices(state.featureId)
44
45
  state.midpoints = this.getMidpoints(state.featureId)
45
46
  },
46
47
 
47
- getVerticies (featureId) {
48
+ getVertices (featureId) {
48
49
  return getCoords(this.getFeature(featureId))
49
50
  },
50
51
 
51
52
  getMidpoints (featureId) {
52
- const feature = this.getFeature(featureId)
53
- const coords = getCoords(feature)
54
- const segments = getRingSegments(feature)
55
- if (!coords?.length || !segments.length) {
56
- return []
57
- }
58
-
59
- const midpoints = []
60
- // Create midpoints within each segment, respecting boundaries
61
- for (const seg of segments) {
62
- // For closed rings, create midpoint between every vertex including last→first
63
- // For open lines, create midpoints only between consecutive vertices (no wrap-around)
64
- const count = seg.closed ? seg.length : seg.length - 1
65
- for (let i = 0; i < count; i++) {
66
- const idx = seg.start + i
67
- const nextIdx = seg.start + ((i + 1) % seg.length)
68
- const [x1, y1] = coords[idx]
69
- const [x2, y2] = coords[nextIdx]
70
- midpoints.push([(x1 + x2) / 2, (y1 + y2) / 2])
71
- }
72
- }
73
- return midpoints
53
+ return getMidpointCoords(this.getFeature(featureId))
74
54
  },
75
55
 
76
56
  getVertexOrMidpoint (state, direction) {
77
57
  // Ensure vertices and midpoints are populated
78
- if (!state.vertecies?.length) {
79
- state.vertecies = this.getVerticies(state.featureId)
58
+ if (!state.vertices?.length) {
59
+ state.vertices = this.getVertices(state.featureId)
80
60
  state.midpoints = this.getMidpoints(state.featureId)
81
61
  }
82
- if (!state.vertecies?.length) {
62
+ if (!state.vertices?.length) {
83
63
  return [-1, null]
84
64
  }
85
65
  const project = (p) => p ? Object.values(this.map.project(p)) : null
86
- const pixels = [...state.vertecies.map(project), ...state.midpoints.map(project)].filter(Boolean)
66
+ const pixels = [...state.vertices.map(project), ...state.midpoints.map(project)].filter(Boolean)
87
67
  if (!pixels.length) {
88
68
  return [-1, null]
89
69
  }
90
70
  const start = pixels[state.selectedVertexIndex] || Object.values(this.map.project(this.map.getCenter()))
91
71
  const idx = spatialNavigate(start, pixels, direction)
92
- return [idx, idx < state.vertecies.length ? 'vertex' : 'midpoint']
72
+ return [idx, idx < state.vertices.length ? 'vertex' : 'midpoint']
93
73
  },
94
74
 
95
75
  getVertexIndexFromMidpoint (state, coordPath) {
@@ -108,7 +88,7 @@ export const vertexQueries = {
108
88
  const insertionIdx = parts[parts.length - 1]
109
89
  const localMidpointIdx = insertionIdx > 0 ? insertionIdx - 1 : seg.length - 2
110
90
  // Midpoints are indexed after all vertices
111
- return state.vertecies.length + midpointOffset + localMidpointIdx
91
+ return state.vertices.length + midpointOffset + localMidpointIdx
112
92
  }
113
93
  // Count midpoints in this segment (must match getMidpoints calculation)
114
94
  const segMidpoints = seg.closed ? seg.length : seg.length - 1
@@ -116,6 +96,6 @@ export const vertexQueries = {
116
96
  }
117
97
 
118
98
  // Fallback
119
- return state.vertecies.length
99
+ return state.vertices.length
120
100
  }
121
101
  }
@@ -17,9 +17,9 @@ describe('vertexQueries', () => {
17
17
  expect(ctx.getCoordPath({ featureId: 'missing' }, 0)).toBe('0')
18
18
  })
19
19
 
20
- test('getVerticies / getMidpoints for closed rings, open lines and missing features', () => {
20
+ test('getVertices / getMidpoints for closed rings, open lines and missing features', () => {
21
21
  const { ctx } = createHarness()
22
- expect(ctx.getVerticies('feat-1')).toHaveLength(4)
22
+ expect(ctx.getVertices('feat-1')).toHaveLength(4)
23
23
  expect(ctx.getMidpoints('feat-1')).toHaveLength(4)
24
24
  expect(ctx.getMidpoints('missing')).toEqual([])
25
25
  const line = createHarness(LINE())
@@ -28,29 +28,29 @@ describe('vertexQueries', () => {
28
28
 
29
29
  test('syncVertices refreshes the cached vertex and midpoint lists', () => {
30
30
  const { ctx, state } = createHarness()
31
- state.vertecies = []
31
+ state.vertices = []
32
32
  state.midpoints = []
33
33
  ctx.syncVertices(state)
34
- expect(state.vertecies).toHaveLength(4)
34
+ expect(state.vertices).toHaveLength(4)
35
35
  expect(state.midpoints).toHaveLength(4)
36
36
  })
37
37
 
38
38
  test('getVertexOrMidpoint repopulates empty lists, navigates, and guards empty features', () => {
39
39
  const { ctx, state } = createHarness()
40
- state.vertecies = []
40
+ state.vertices = []
41
41
  state.selectedVertexIndex = 0
42
42
  const [idx, type] = ctx.getVertexOrMidpoint(state, 'ArrowRight')
43
43
  expect(idx).toBeGreaterThanOrEqual(0)
44
44
  expect(['vertex', 'midpoint']).toContain(type)
45
45
 
46
- expect(ctx.getVertexOrMidpoint({ featureId: 'missing', vertecies: [] }, 'ArrowRight')).toEqual([-1, null])
47
- expect(ctx.getVertexOrMidpoint({ ...state, vertecies: [null], midpoints: [], selectedVertexIndex: 0 }, 'ArrowRight')).toEqual([-1, null])
46
+ expect(ctx.getVertexOrMidpoint({ featureId: 'missing', vertices: [] }, 'ArrowRight')).toEqual([-1, null])
47
+ expect(ctx.getVertexOrMidpoint({ ...state, vertices: [null], midpoints: [], selectedVertexIndex: 0 }, 'ArrowRight')).toEqual([-1, null])
48
48
 
49
49
  // Falls back to the map centre when the current index has no pixel, and can resolve a vertex target
50
50
  const fresh = createHarness()
51
51
  expect(fresh.ctx.getVertexOrMidpoint({ ...fresh.state, selectedVertexIndex: -1 }, 'ArrowRight')[0]).toBeGreaterThanOrEqual(0)
52
52
  const types = new Set()
53
- for (let s = 0; s < fresh.state.vertecies.length + fresh.state.midpoints.length; s++) {
53
+ for (let s = 0; s < fresh.state.vertices.length + fresh.state.midpoints.length; s++) {
54
54
  for (const d of ['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown']) {
55
55
  types.add(fresh.ctx.getVertexOrMidpoint({ ...fresh.state, selectedVertexIndex: s }, d)[1])
56
56
  }
@@ -66,8 +66,8 @@ describe('vertexQueries', () => {
66
66
 
67
67
  // Counts earlier segments for multi-part geometry (closed and open)
68
68
  features.set('multipoly', { type: 'MultiPolygon', coordinates: [[[[0, 0], [1, 0], [1, 1], [0, 0]]], [[[5, 5], [6, 5], [6, 6], [5, 5]]]] })
69
- expect(ctx.getVertexIndexFromMidpoint({ ...state, featureId: 'multipoly', vertecies: new Array(8).fill([0, 0]) }, '1.0.1')).toBeGreaterThan(8)
69
+ expect(ctx.getVertexIndexFromMidpoint({ ...state, featureId: 'multipoly', vertices: new Array(8).fill([0, 0]) }, '1.0.1')).toBeGreaterThan(8)
70
70
  features.set('multiline', { type: 'MultiLineString', coordinates: [[[0, 0], [1, 1]], [[5, 5], [6, 6]]] })
71
- expect(ctx.getVertexIndexFromMidpoint({ ...state, featureId: 'multiline', vertecies: new Array(4).fill([0, 0]) }, '1.1')).toBeGreaterThan(4)
71
+ expect(ctx.getVertexIndexFromMidpoint({ ...state, featureId: 'multiline', vertices: new Array(4).fill([0, 0]) }, '1.1')).toBeGreaterThan(4)
72
72
  })
73
73
  })