@accelint/map-toolkit 1.4.0 → 2.0.0

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 (204) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/catalog-info.yaml +4 -4
  3. package/dist/camera/events.js +1 -1
  4. package/dist/camera/index.d.ts +1 -1
  5. package/dist/camera/index.js +1 -1
  6. package/dist/camera/store.d.ts +1 -1
  7. package/dist/camera/store.js +4 -6
  8. package/dist/camera/store.js.map +1 -1
  9. package/dist/camera/types.d.ts +1 -1
  10. package/dist/camera/types.js +1 -1
  11. package/dist/cursor-coordinates/constants.js +1 -1
  12. package/dist/cursor-coordinates/index.d.ts +1 -1
  13. package/dist/cursor-coordinates/index.js +1 -1
  14. package/dist/cursor-coordinates/store.d.ts +1 -1
  15. package/dist/cursor-coordinates/store.js +1 -1
  16. package/dist/cursor-coordinates/types.d.ts +1 -1
  17. package/dist/cursor-coordinates/types.js +1 -1
  18. package/dist/cursor-coordinates/use-cursor-coordinates.d.ts +1 -1
  19. package/dist/cursor-coordinates/use-cursor-coordinates.js +4 -9
  20. package/dist/cursor-coordinates/use-cursor-coordinates.js.map +1 -1
  21. package/dist/deckgl/base-map/constants.js +1 -1
  22. package/dist/deckgl/base-map/controls.d.ts +1 -1
  23. package/dist/deckgl/base-map/controls.js +1 -1
  24. package/dist/deckgl/base-map/events.js +1 -1
  25. package/dist/deckgl/base-map/index.d.ts +3 -3
  26. package/dist/deckgl/base-map/index.js +15 -7
  27. package/dist/deckgl/base-map/index.js.map +1 -1
  28. package/dist/deckgl/base-map/provider.d.ts +3 -3
  29. package/dist/deckgl/base-map/provider.js +3 -5
  30. package/dist/deckgl/base-map/provider.js.map +1 -1
  31. package/dist/deckgl/base-map/types.d.ts +1 -1
  32. package/dist/deckgl/base-map/types.js +1 -1
  33. package/dist/deckgl/index.d.ts +4 -4
  34. package/dist/deckgl/index.js +4 -4
  35. package/dist/deckgl/saved-viewports/index.d.ts +1 -1
  36. package/dist/deckgl/saved-viewports/index.js +1 -1
  37. package/dist/deckgl/saved-viewports/storage.d.ts +1 -1
  38. package/dist/deckgl/saved-viewports/storage.js +5 -10
  39. package/dist/deckgl/saved-viewports/storage.js.map +1 -1
  40. package/dist/deckgl/shapes/display-shape-layer/constants.js +66 -13
  41. package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -1
  42. package/dist/deckgl/shapes/display-shape-layer/fiber.d.ts +1 -1
  43. package/dist/deckgl/shapes/display-shape-layer/fiber.js +1 -1
  44. package/dist/deckgl/shapes/display-shape-layer/index.d.ts +74 -35
  45. package/dist/deckgl/shapes/display-shape-layer/index.js +381 -154
  46. package/dist/deckgl/shapes/display-shape-layer/index.js.map +1 -1
  47. package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js +1 -1
  48. package/dist/deckgl/shapes/display-shape-layer/store.js +8 -2
  49. package/dist/deckgl/shapes/display-shape-layer/store.js.map +1 -1
  50. package/dist/deckgl/shapes/display-shape-layer/types.d.ts +108 -19
  51. package/dist/deckgl/shapes/display-shape-layer/types.js +1 -1
  52. package/dist/deckgl/shapes/display-shape-layer/use-select-shape.d.ts +1 -1
  53. package/dist/deckgl/shapes/display-shape-layer/use-select-shape.js +1 -1
  54. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +66 -36
  55. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -1
  56. package/dist/deckgl/shapes/display-shape-layer/utils/elevation.js +407 -0
  57. package/dist/deckgl/shapes/display-shape-layer/utils/elevation.js.map +1 -0
  58. package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js +151 -0
  59. package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js.map +1 -0
  60. package/dist/deckgl/shapes/display-shape-layer/utils/interaction.js +50 -0
  61. package/dist/deckgl/shapes/display-shape-layer/utils/interaction.js.map +1 -0
  62. package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +1 -1
  63. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +28 -39
  64. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -1
  65. package/dist/deckgl/shapes/draw-shape-layer/constants.js +1 -1
  66. package/dist/deckgl/shapes/draw-shape-layer/events.d.ts +1 -1
  67. package/dist/deckgl/shapes/draw-shape-layer/events.js +1 -1
  68. package/dist/deckgl/shapes/draw-shape-layer/fiber.js +1 -1
  69. package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +3 -3
  70. package/dist/deckgl/shapes/draw-shape-layer/index.js +7 -17
  71. package/dist/deckgl/shapes/draw-shape-layer/index.js.map +1 -1
  72. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +8 -9
  73. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js.map +1 -1
  74. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js +3 -3
  75. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +4 -21
  76. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js.map +1 -1
  77. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +4 -34
  78. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js.map +1 -1
  79. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +11 -12
  80. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js.map +1 -1
  81. package/dist/deckgl/shapes/draw-shape-layer/modes/index.js +2 -32
  82. package/dist/deckgl/shapes/draw-shape-layer/modes/index.js.map +1 -1
  83. package/dist/deckgl/shapes/draw-shape-layer/store.js +38 -4
  84. package/dist/deckgl/shapes/draw-shape-layer/store.js.map +1 -1
  85. package/dist/deckgl/shapes/draw-shape-layer/types.d.ts +1 -1
  86. package/dist/deckgl/shapes/draw-shape-layer/types.js +1 -1
  87. package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.d.ts +1 -1
  88. package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.js +2 -2
  89. package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js +4 -9
  90. package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js.map +1 -1
  91. package/dist/deckgl/shapes/edit-shape-layer/constants.js +17 -2
  92. package/dist/deckgl/shapes/edit-shape-layer/constants.js.map +1 -1
  93. package/dist/deckgl/shapes/edit-shape-layer/events.d.ts +1 -1
  94. package/dist/deckgl/shapes/edit-shape-layer/events.js +1 -1
  95. package/dist/deckgl/shapes/edit-shape-layer/fiber.d.ts +1 -1
  96. package/dist/deckgl/shapes/edit-shape-layer/fiber.js +1 -1
  97. package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +7 -4
  98. package/dist/deckgl/shapes/edit-shape-layer/index.js +52 -21
  99. package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -1
  100. package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js +4 -1
  101. package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js.map +1 -1
  102. package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +2 -2
  103. package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js.map +1 -1
  104. package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +7 -7
  105. package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js.map +1 -1
  106. package/dist/deckgl/shapes/edit-shape-layer/modes/index.js +2 -2
  107. package/dist/deckgl/shapes/edit-shape-layer/modes/index.js.map +1 -1
  108. package/dist/deckgl/shapes/edit-shape-layer/modes/point-translate-mode.js +1 -1
  109. package/dist/deckgl/shapes/edit-shape-layer/modes/point-translate-mode.js.map +1 -1
  110. package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js +2 -2
  111. package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js.map +1 -1
  112. package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js +1 -1
  113. package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js.map +1 -1
  114. package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js +1 -1
  115. package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js.map +1 -1
  116. package/dist/deckgl/shapes/edit-shape-layer/store.js +78 -14
  117. package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -1
  118. package/dist/deckgl/shapes/edit-shape-layer/types.d.ts +14 -2
  119. package/dist/deckgl/shapes/edit-shape-layer/types.js +1 -1
  120. package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.d.ts +1 -1
  121. package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js +2 -2
  122. package/dist/deckgl/shapes/index.d.ts +4 -4
  123. package/dist/deckgl/shapes/index.js +5 -5
  124. package/dist/deckgl/shapes/shared/constants.d.ts +4 -3
  125. package/dist/deckgl/shapes/shared/constants.js +55 -15
  126. package/dist/deckgl/shapes/shared/constants.js.map +1 -1
  127. package/dist/deckgl/shapes/shared/events.d.ts +5 -1
  128. package/dist/deckgl/shapes/shared/events.js +1 -1
  129. package/dist/deckgl/shapes/shared/events.js.map +1 -1
  130. package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js +19 -16
  131. package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js.map +1 -1
  132. package/dist/deckgl/shapes/shared/types.d.ts +174 -53
  133. package/dist/deckgl/shapes/shared/types.js +155 -2
  134. package/dist/deckgl/shapes/shared/types.js.map +1 -1
  135. package/dist/deckgl/shapes/shared/utils/geometry-measurements.js +29 -24
  136. package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -1
  137. package/dist/deckgl/shapes/shared/utils/layer-config.js +9 -6
  138. package/dist/deckgl/shapes/shared/utils/layer-config.js.map +1 -1
  139. package/dist/deckgl/shapes/shared/utils/mode-utils.js +50 -20
  140. package/dist/deckgl/shapes/shared/utils/mode-utils.js.map +1 -1
  141. package/dist/deckgl/shapes/shared/utils/pick-filtering.js +22 -15
  142. package/dist/deckgl/shapes/shared/utils/pick-filtering.js.map +1 -1
  143. package/dist/deckgl/shapes/shared/utils/style-utils.d.ts +38 -14
  144. package/dist/deckgl/shapes/shared/utils/style-utils.js +43 -32
  145. package/dist/deckgl/shapes/shared/utils/style-utils.js.map +1 -1
  146. package/dist/deckgl/symbol-layer/fiber.d.ts +1 -1
  147. package/dist/deckgl/symbol-layer/fiber.js +1 -1
  148. package/dist/deckgl/symbol-layer/index.d.ts +1 -1
  149. package/dist/deckgl/symbol-layer/index.js +1 -1
  150. package/dist/deckgl/text-layer/character-sets.js +1 -1
  151. package/dist/deckgl/text-layer/default-settings.d.ts +1 -1
  152. package/dist/deckgl/text-layer/default-settings.js +1 -1
  153. package/dist/deckgl/text-layer/fiber.d.ts +1 -1
  154. package/dist/deckgl/text-layer/fiber.js +1 -1
  155. package/dist/deckgl/text-layer/index.d.ts +1 -1
  156. package/dist/deckgl/text-layer/index.js +1 -1
  157. package/dist/deckgl/text-settings.d.ts +3 -3
  158. package/dist/deckgl/text-settings.js +1 -1
  159. package/dist/map-cursor/events.js +1 -1
  160. package/dist/map-cursor/index.d.ts +1 -1
  161. package/dist/map-cursor/index.js +1 -1
  162. package/dist/map-cursor/store.d.ts +1 -1
  163. package/dist/map-cursor/store.js +1 -1
  164. package/dist/map-cursor/types.d.ts +1 -1
  165. package/dist/map-cursor/types.js +1 -1
  166. package/dist/map-cursor/use-map-cursor.d.ts +1 -1
  167. package/dist/map-cursor/use-map-cursor.js +1 -1
  168. package/dist/map-mode/events.js +1 -1
  169. package/dist/map-mode/index.d.ts +1 -1
  170. package/dist/map-mode/index.js +1 -1
  171. package/dist/map-mode/store.d.ts +1 -1
  172. package/dist/map-mode/store.js +3 -8
  173. package/dist/map-mode/store.js.map +1 -1
  174. package/dist/map-mode/types.d.ts +1 -1
  175. package/dist/map-mode/types.js +1 -1
  176. package/dist/map-mode/use-map-mode.d.ts +1 -1
  177. package/dist/map-mode/use-map-mode.js +1 -1
  178. package/dist/maplibre/hooks/use-maplibre.d.ts +1 -1
  179. package/dist/maplibre/hooks/use-maplibre.js +1 -1
  180. package/dist/maplibre/index.d.ts +1 -1
  181. package/dist/maplibre/index.js +1 -1
  182. package/dist/shared/cleanup.d.ts +58 -0
  183. package/dist/shared/cleanup.js +93 -0
  184. package/dist/shared/cleanup.js.map +1 -0
  185. package/dist/shared/constants.js +1 -1
  186. package/dist/shared/create-map-store.d.ts +13 -1
  187. package/dist/shared/create-map-store.js +9 -4
  188. package/dist/shared/create-map-store.js.map +1 -1
  189. package/dist/shared/logger.js +31 -0
  190. package/dist/shared/logger.js.map +1 -0
  191. package/dist/shared/units.js +1 -1
  192. package/dist/viewport/index.d.ts +1 -1
  193. package/dist/viewport/index.js +1 -1
  194. package/dist/viewport/store.d.ts +1 -1
  195. package/dist/viewport/store.js +1 -1
  196. package/dist/viewport/types.d.ts +1 -1
  197. package/dist/viewport/types.js +1 -1
  198. package/dist/viewport/utils.d.ts +1 -1
  199. package/dist/viewport/utils.js +1 -1
  200. package/dist/viewport/viewport-size.d.ts +3 -3
  201. package/dist/viewport/viewport-size.js +1 -1
  202. package/package.json +22 -19
  203. package/dist/hotkey-manager/dist/react/use-hotkey.js +0 -39
  204. package/dist/hotkey-manager/dist/react/use-hotkey.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","names":["DEFAULT_EDITING_STATE: EditingState"],"sources":["../../../../src/deckgl/shapes/edit-shape-layer/store.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\n/**\n * Edit Shape Store\n *\n * Manages editing state for shape modification.\n *\n * @example\n * ```typescript\n * import { editStore } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * function EditControls({ mapId }) {\n * const { state, edit, save, cancel } = editStore.use(mapId);\n *\n * return (\n * <div>\n * <p>Editing: {state.editingShape?.name ?? 'none'}</p>\n * <button onClick={save}>Save</button>\n * <button onClick={cancel}>Cancel</button>\n * </div>\n * );\n * }\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { getLogger } from '@accelint/logger';\nimport { createMapStore } from '@/shared/create-map-store';\nimport { MapEvents } from '../../base-map/events';\nimport {\n isCircleShape,\n isEllipseShape,\n isPointShape,\n isRectangleShape,\n} from '../shared/types';\nimport {\n releaseModeAndCursor,\n requestCursorChange,\n requestModeChange,\n} from '../shared/utils/mode-utils';\nimport {\n EDIT_CURSOR_MAP,\n EDIT_SHAPE_LAYER_ID,\n EDIT_SHAPE_MODE,\n} from './constants';\nimport { EditShapeEvents } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { Feature } from 'geojson';\nimport type { MapEventType } from '../../base-map/types';\nimport type { Shape } from '../shared/types';\nimport type {\n EditShapeEvent,\n ShapeEditCanceledEvent,\n ShapeEditingEvent,\n ShapeUpdatedEvent,\n} from './events';\nimport type {\n EditFunction,\n EditingState,\n EditMode,\n EditShapeOptions,\n} from './types';\n\nconst logger = getLogger({\n enabled:\n process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test',\n level: 'warn',\n prefix: '[EditShapeLayer]',\n pretty: true,\n});\n\n/**\n * Typed event bus instances\n */\nconst editShapeBus = Broadcast.getInstance<EditShapeEvent>();\nconst mapEventBus = Broadcast.getInstance<MapEventType>();\n\n/**\n * Default editing state\n */\nconst DEFAULT_EDITING_STATE: EditingState = {\n editingShape: null,\n editMode: 'view',\n featureBeingEdited: null,\n};\n\n/**\n * Actions for edit shape store\n */\ntype EditShapeActions = {\n /** Start editing a shape */\n edit: EditFunction;\n /** Save the current edits */\n save: () => void;\n /** Cancel editing */\n cancel: () => void;\n};\n\n/**\n * Determine the appropriate edit mode for a shape type\n *\n * @param shape - The shape to determine the edit mode for\n * @returns The edit mode to use for this shape type\n */\nfunction getEditModeForShape(shape: Shape): EditMode {\n if (isPointShape(shape)) {\n return 'point-translate';\n }\n if (isCircleShape(shape)) {\n return 'circle-transform';\n }\n if (isEllipseShape(shape) || isRectangleShape(shape)) {\n return 'bounding-transform';\n }\n return 'vertex-transform';\n}\n\n/**\n * Start editing a shape\n */\nfunction startEditing(\n mapId: UniqueId,\n state: EditingState,\n shape: Shape,\n options: EditShapeOptions | undefined,\n notify: () => void,\n setState: (updates: Partial<EditingState>) => void,\n): void {\n // Prevent editing locked shapes\n if (shape.locked) {\n logger.warn(`Cannot edit locked shape: \"${shape.name}\"`);\n return;\n }\n\n // Already editing - cancel first\n if (state.editingShape) {\n cancelEditingInternal(mapId, state, notify, setState);\n }\n\n // Determine edit mode (can be overridden via options)\n const editMode = options?.mode ?? getEditModeForShape(shape);\n\n // Update state with new object reference\n setState({\n editingShape: shape,\n editMode,\n featureBeingEdited: shape.feature,\n });\n\n // Request map mode and cursor\n requestModeChange(mapId, EDIT_SHAPE_MODE, EDIT_SHAPE_LAYER_ID);\n const cursor = EDIT_CURSOR_MAP[editMode];\n requestCursorChange(mapId, cursor, EDIT_SHAPE_LAYER_ID);\n\n // Disable map panning during editing\n mapEventBus.emit(MapEvents.disablePan, { id: mapId });\n\n // Emit editing started event\n editShapeBus.emit(EditShapeEvents.editing, {\n shape,\n mapId,\n } as unknown as ShapeEditingEvent['payload']);\n\n notify();\n}\n\n/**\n * Save editing and create updated shape\n */\nfunction saveEditingInternal(\n mapId: UniqueId,\n state: EditingState,\n notify: () => void,\n setState: (updates: Partial<EditingState>) => void,\n): Shape | null {\n if (!(state.editingShape && state.featureBeingEdited)) {\n return null;\n }\n\n const originalShape = state.editingShape;\n const updatedFeature = state.featureBeingEdited;\n\n // Create updated shape with new geometry\n const updatedShape = {\n ...originalShape,\n feature: {\n ...updatedFeature,\n properties: {\n ...originalShape.feature.properties,\n ...updatedFeature.properties,\n },\n },\n lastUpdated: Date.now(),\n } as Shape;\n\n // Reset state\n setState({\n editingShape: null,\n editMode: 'view',\n featureBeingEdited: null,\n });\n\n // Return to default mode and cursor\n releaseModeAndCursor(mapId, EDIT_SHAPE_LAYER_ID);\n\n // Re-enable map panning\n mapEventBus.emit(MapEvents.enablePan, { id: mapId });\n\n // Emit shape updated event\n editShapeBus.emit(EditShapeEvents.updated, {\n shape: updatedShape,\n mapId,\n } as unknown as ShapeUpdatedEvent['payload']);\n\n notify();\n\n return updatedShape;\n}\n\n/**\n * Cancel the current editing operation\n */\nfunction cancelEditingInternal(\n mapId: UniqueId,\n state: EditingState,\n notify: () => void,\n setState: (updates: Partial<EditingState>) => void,\n): void {\n if (!state.editingShape) {\n return; // Nothing to cancel\n }\n\n const originalShape = state.editingShape;\n\n // Reset state\n setState({\n editingShape: null,\n editMode: 'view',\n featureBeingEdited: null,\n });\n\n // Return to default mode and cursor\n releaseModeAndCursor(mapId, EDIT_SHAPE_LAYER_ID);\n\n // Re-enable map panning\n mapEventBus.emit(MapEvents.enablePan, { id: mapId });\n\n // Emit canceled event\n editShapeBus.emit(EditShapeEvents.canceled, {\n shape: originalShape,\n mapId,\n } as unknown as ShapeEditCanceledEvent['payload']);\n\n notify();\n}\n\n/**\n * Edit shape store\n */\nexport const editStore = createMapStore<EditingState, EditShapeActions>({\n defaultState: { ...DEFAULT_EDITING_STATE },\n\n actions: (mapId, { get, set, notify }) => ({\n edit: (shape: Shape, options?: EditShapeOptions) => {\n startEditing(mapId, get(), shape, options, notify, set);\n },\n\n save: () => {\n saveEditingInternal(mapId, get(), notify, set);\n },\n\n cancel: () => {\n cancelEditingInternal(mapId, get(), notify, set);\n },\n }),\n\n // Note: EditShapeLayer is \"neutral\" regarding mode change authorization.\n // It doesn't auto-cancel or reject mode changes - those decisions are\n // left to UI components that can prompt the user.\n\n onCleanup: (mapId, state) => {\n // Cancel any active editing before cleanup\n if (state.editingShape) {\n // Return to default mode and cursor\n releaseModeAndCursor(mapId, EDIT_SHAPE_LAYER_ID);\n\n // Re-enable map panning\n mapEventBus.emit(MapEvents.enablePan, { id: mapId });\n\n // Emit canceled event\n editShapeBus.emit(EditShapeEvents.canceled, {\n shape: state.editingShape,\n mapId,\n } as unknown as ShapeEditCanceledEvent['payload']);\n }\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Get the current editing state for a mapId\n * Returns null if no store instance exists\n */\nexport function getEditingState(mapId: UniqueId): EditingState | null {\n if (!editStore.exists(mapId)) {\n return null;\n }\n return editStore.get(mapId);\n}\n\n/**\n * Hook for editing state\n */\nexport function useEditingState(\n mapId: UniqueId,\n): { state: EditingState } & EditShapeActions {\n return editStore.use(mapId);\n}\n\n/**\n * Manually clear editing state for a specific mapId.\n */\nexport function clearEditingState(mapId: UniqueId): void {\n editStore.clear(mapId);\n}\n\n/**\n * Update feature from the layer component (called during drag operations)\n */\nexport function updateFeatureFromLayer(\n mapId: UniqueId,\n feature: Feature,\n): void {\n editStore.set(mapId, { featureBeingEdited: feature });\n}\n\n/**\n * Cancel editing (called by the layer component on ESC)\n */\nexport function cancelEditingFromLayer(mapId: UniqueId): void {\n editStore.actions(mapId).cancel();\n}\n\n/**\n * Save editing (called by the layer component on Enter)\n */\nexport function saveEditingFromLayer(mapId: UniqueId): void {\n editStore.actions(mapId).save();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"store.js","names":["DEFAULT_EDITING_STATE: EditingState"],"sources":["../../../../src/deckgl/shapes/edit-shape-layer/store.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\n/**\n * Edit Shape Store\n *\n * Manages editing state for shape modification.\n *\n * @example\n * ```typescript\n * import { editStore } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * function EditControls({ mapId }) {\n * const { state, edit, save, cancel } = editStore.use(mapId);\n *\n * return (\n * <div>\n * <p>Editing: {state.editingShape?.name ?? 'none'}</p>\n * <button onClick={save}>Save</button>\n * <button onClick={cancel}>Cancel</button>\n * </div>\n * );\n * }\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { createMapStore } from '@/shared/create-map-store';\nimport { createLoggerDomain } from '@/shared/logger';\nimport { MapEvents } from '../../base-map/events';\nimport {\n isCircleShape,\n isEllipseShape,\n isPointShape,\n isRectangleShape,\n} from '../shared/types';\nimport {\n releaseModeAndCursor,\n requestCursorChange,\n requestModeChange,\n} from '../shared/utils/mode-utils';\nimport {\n EDIT_CURSOR_MAP,\n EDIT_SHAPE_LAYER_ID,\n EDIT_SHAPE_MODE,\n} from './constants';\nimport { EditShapeEvents } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { Feature } from 'geojson';\nimport type { MapEventType } from '../../base-map/types';\nimport type { Shape } from '../shared/types';\nimport type {\n EditShapeEvent,\n ShapeEditCanceledEvent,\n ShapeEditingEvent,\n ShapeUpdatedEvent,\n} from './events';\nimport type {\n EditFunction,\n EditingState,\n EditMode,\n EditShapeOptions,\n} from './types';\n\nconst logger = createLoggerDomain('[EditShapeLayer]');\n\n/**\n * Typed event bus instances\n */\nconst editShapeBus = Broadcast.getInstance<EditShapeEvent>();\nconst mapEventBus = Broadcast.getInstance<MapEventType>();\n\n/**\n * Default editing state\n */\nconst DEFAULT_EDITING_STATE: EditingState = {\n editingShape: null,\n editMode: 'view',\n featureBeingEdited: null,\n previousMode: null,\n};\n\n/**\n * Actions for edit shape store\n */\ntype EditShapeActions = {\n /** Start editing a shape */\n edit: EditFunction;\n /** Save the current edits */\n save: () => void;\n /** Cancel editing */\n cancel: () => void;\n};\n\n/**\n * Determine the appropriate edit mode for a shape type\n *\n * @param shape - The shape to determine the edit mode for\n * @returns The edit mode to use for this shape type\n */\nfunction getEditModeForShape(shape: Shape): EditMode {\n if (isPointShape(shape)) {\n return 'point-translate';\n }\n if (isCircleShape(shape)) {\n return 'circle-transform';\n }\n if (isEllipseShape(shape) || isRectangleShape(shape)) {\n return 'bounding-transform';\n }\n return 'vertex-transform';\n}\n\n/**\n * Start editing a shape\n */\nfunction startEditing(\n mapId: UniqueId,\n state: EditingState,\n shape: Shape,\n options: EditShapeOptions | undefined,\n notify: () => void,\n setState: (updates: Partial<EditingState>) => void,\n): void {\n // Prevent editing locked shapes\n if (shape.locked) {\n logger.warn(`Cannot edit locked shape: \"${shape.name}\"`);\n return;\n }\n\n // Already editing - cancel first\n if (state.editingShape) {\n cancelEditingInternal(mapId, state, notify, setState);\n }\n\n // Determine edit mode (can be overridden via options)\n const editMode = options?.mode ?? getEditModeForShape(shape);\n\n // Update state with new object reference\n setState({\n editingShape: shape,\n editMode,\n featureBeingEdited: shape.feature,\n });\n\n // Request map mode and cursor\n requestModeChange(mapId, EDIT_SHAPE_MODE, EDIT_SHAPE_LAYER_ID);\n const cursor = EDIT_CURSOR_MAP[editMode];\n requestCursorChange(mapId, cursor, EDIT_SHAPE_LAYER_ID);\n\n // Disable map panning during editing\n mapEventBus.emit(MapEvents.disablePan, { id: mapId });\n\n // Emit editing started event\n editShapeBus.emit(EditShapeEvents.editing, {\n shape,\n mapId,\n } as unknown as ShapeEditingEvent['payload']);\n\n notify();\n}\n\n/**\n * Save editing and create updated shape\n */\nfunction saveEditingInternal(\n mapId: UniqueId,\n state: EditingState,\n notify: () => void,\n setState: (updates: Partial<EditingState>) => void,\n): Shape | null {\n if (!(state.editingShape && state.featureBeingEdited)) {\n return null;\n }\n\n const originalShape = state.editingShape;\n const updatedFeature = state.featureBeingEdited;\n\n // Create updated shape with new geometry\n const updatedShape = {\n ...originalShape,\n feature: {\n ...updatedFeature,\n properties: {\n ...originalShape.feature.properties,\n ...updatedFeature.properties,\n },\n },\n lastUpdated: Date.now(),\n } as Shape;\n\n // Reset state\n setState({\n editingShape: null,\n editMode: 'view',\n featureBeingEdited: null,\n previousMode: null,\n });\n\n // Return to default mode and cursor\n releaseModeAndCursor(mapId, EDIT_SHAPE_LAYER_ID);\n\n // Re-enable map panning\n mapEventBus.emit(MapEvents.enablePan, { id: mapId });\n\n // Emit shape updated event\n editShapeBus.emit(EditShapeEvents.updated, {\n shape: updatedShape,\n mapId,\n } as unknown as ShapeUpdatedEvent['payload']);\n\n notify();\n\n return updatedShape;\n}\n\n/**\n * Cancel the current editing operation\n */\nfunction cancelEditingInternal(\n mapId: UniqueId,\n state: EditingState,\n notify: () => void,\n setState: (updates: Partial<EditingState>) => void,\n): void {\n if (!state.editingShape) {\n return; // Nothing to cancel\n }\n\n const originalShape = state.editingShape;\n\n // Reset state\n setState({\n editingShape: null,\n editMode: 'view',\n featureBeingEdited: null,\n previousMode: null,\n });\n\n // Return to default mode and cursor\n releaseModeAndCursor(mapId, EDIT_SHAPE_LAYER_ID);\n\n // Re-enable map panning\n mapEventBus.emit(MapEvents.enablePan, { id: mapId });\n\n // Emit canceled event\n editShapeBus.emit(EditShapeEvents.canceled, {\n shape: originalShape,\n mapId,\n } as unknown as ShapeEditCanceledEvent['payload']);\n\n notify();\n}\n\n/**\n * Edit shape store\n */\nexport const editStore = createMapStore<EditingState, EditShapeActions>({\n defaultState: { ...DEFAULT_EDITING_STATE },\n\n actions: (mapId, { get, set, notify }) => ({\n edit: (shape: Shape, options?: EditShapeOptions) => {\n startEditing(mapId, get(), shape, options, notify, set);\n },\n\n save: () => {\n saveEditingInternal(mapId, get(), notify, set);\n },\n\n cancel: () => {\n cancelEditingInternal(mapId, get(), notify, set);\n },\n }),\n\n // Note: EditShapeLayer is \"neutral\" regarding mode change authorization.\n // It doesn't auto-cancel or reject mode changes - those decisions are\n // left to UI components that can prompt the user.\n\n onCleanup: (mapId, state) => {\n // Cancel any active editing before cleanup\n if (state.editingShape) {\n // Return to default mode and cursor\n releaseModeAndCursor(mapId, EDIT_SHAPE_LAYER_ID);\n\n // Re-enable map panning\n mapEventBus.emit(MapEvents.enablePan, { id: mapId });\n\n // Emit canceled event\n editShapeBus.emit(EditShapeEvents.canceled, {\n shape: state.editingShape,\n mapId,\n } as unknown as ShapeEditCanceledEvent['payload']);\n }\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Get the current editing state for a mapId\n * Returns null if no store instance exists\n *\n * @param mapId - The map instance ID.\n * @returns The current editing state, or null if no store instance exists.\n */\nexport function getEditingState(mapId: UniqueId): EditingState | null {\n if (!editStore.exists(mapId)) {\n return null;\n }\n return editStore.get(mapId);\n}\n\n/**\n * Hook for editing state\n * @param mapId - The map instance ID.\n * @returns The current editing state and edit actions.\n *\n * @example\n * ```typescript\n * const { state, edit, save, cancel } = useEditingState(mapId);\n */\nexport function useEditingState(\n mapId: UniqueId,\n): { state: EditingState } & EditShapeActions {\n return editStore.use(mapId);\n}\n\n/**\n * Manually clear editing state for a specific mapId.\n * @param mapId - The map instance ID.\n */\nexport function clearEditingState(mapId: UniqueId): void {\n editStore.clear(mapId);\n}\n\n/**\n * Update feature from the layer component (called during drag operations)\n * @param mapId - The map instance ID.\n * @param feature - The updated GeoJSON feature from the editable layer.\n */\nexport function updateFeatureFromLayer(\n mapId: UniqueId,\n feature: Feature,\n): void {\n editStore.set(mapId, { featureBeingEdited: feature });\n}\n\n/**\n * Cancel editing (called by the layer component on ESC)\n * @param mapId - The map instance ID.\n */\nexport function cancelEditingFromLayer(mapId: UniqueId): void {\n editStore.actions(mapId).cancel();\n}\n\n/**\n * Save editing (called by the layer component on Enter)\n * @param mapId - The map instance ID.\n */\nexport function saveEditingFromLayer(mapId: UniqueId): void {\n editStore.actions(mapId).save();\n}\n\n/**\n * Enables map panning while editing by switching to view mode and storing the\n * current edit mode for restoration.\n *\n * No-op if no shape is currently being edited.\n *\n * @param mapId - The map instance ID.\n *\n * @example\n * ```typescript\n * enableEditPanning(mapId);\n * ```\n */\nexport function enableEditPanning(mapId: UniqueId): void {\n const state = editStore.get(mapId);\n if (state?.previousMode !== null || !state?.editingShape) {\n return;\n }\n\n editStore.set(mapId, { previousMode: state.editMode, editMode: 'view' });\n mapEventBus.emit(MapEvents.enablePan, { id: mapId });\n requestCursorChange(mapId, 'grab', EDIT_SHAPE_LAYER_ID);\n}\n\n/**\n * Disables map panning and restores the previous edit mode.\n *\n * If no shape is being edited, clears the stored `previousMode` and returns.\n * Otherwise, restores the edit mode from `previousMode`, re-disables panning,\n * and sets the cursor back to the shape-appropriate edit cursor.\n *\n * @param mapId - The map instance ID.\n *\n * @example\n * ```typescript\n * disableEditPanning(mapId);\n * ```\n */\nexport function disableEditPanning(mapId: UniqueId): void {\n const state = editStore.get(mapId);\n\n if (!state?.editingShape) {\n editStore.set(mapId, { previousMode: null });\n return;\n }\n\n const previousMode = state?.previousMode;\n if (!previousMode) {\n editStore.set(mapId, { previousMode: null });\n return;\n }\n\n editStore.set(mapId, { editMode: previousMode, previousMode: null });\n mapEventBus.emit(MapEvents.disablePan, { id: mapId });\n requestCursorChange(\n mapId,\n EDIT_CURSOR_MAP[previousMode],\n EDIT_SHAPE_LAYER_ID,\n );\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -13,7 +13,9 @@
13
13
  import { DistanceUnitAbbreviation } from "../../../shared/units.js";
14
14
  import { Shape } from "../shared/types.js";
15
15
  import { UniqueId } from "@accelint/core";
16
+ import { KeyOption } from "@accelint/hotkey-manager";
16
17
  import { Feature } from "geojson";
18
+ import { NonEmptyArray } from "@accelint/hotkey-manager/types/non-empty-array";
17
19
 
18
20
  //#region src/deckgl/shapes/edit-shape-layer/types.d.ts
19
21
  /**
@@ -36,6 +38,8 @@ type EditingState = {
36
38
  editMode: EditMode;
37
39
  /** Live feature being edited (updates in real-time during drag) */
38
40
  featureBeingEdited: Feature | null;
41
+ /** Edit mode to restore after held panning hotkey is released. Null when not panning. */
42
+ previousMode: EditMode | null;
39
43
  };
40
44
  /**
41
45
  * Options for starting an edit operation
@@ -84,11 +88,19 @@ type EditShapeLayerProps = {
84
88
  mapId?: UniqueId;
85
89
  /** Distance unit for tooltip measurements (defaults to 'km') */
86
90
  unit?: DistanceUnitAbbreviation;
91
+ /** Configuration for hotkeys in EditShapesLayer */
92
+ hotkeyConfig?: EditShapeHotkeyConfig;
87
93
  };
88
94
  /**
89
95
  * Function type for the edit action
90
96
  */
91
97
  type EditFunction = (shape: Shape, options?: EditShapeOptions) => void;
98
+ /**
99
+ * Type to define hotkey configurations for functions in EditShapeLayer
100
+ */
101
+ type EditShapeHotkeyConfig = {
102
+ panning: KeyOption | NonEmptyArray<KeyOption>;
103
+ };
92
104
  //#endregion
93
- export { EditFunction, EditMode, EditShapeLayerProps, EditShapeOptions, EditingState, UseEditShapeOptions, UseEditShapeReturn };
105
+ export { EditFunction, EditMode, EditShapeHotkeyConfig, EditShapeLayerProps, EditShapeOptions, EditingState, UseEditShapeOptions, UseEditShapeReturn };
94
106
  //# sourceMappingURL=types.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -13,9 +13,9 @@
13
13
 
14
14
  'use client';
15
15
 
16
- import { MapContext } from "../../base-map/provider.js";
17
16
  import { EditShapeEvents } from "./events.js";
18
17
  import { editStore } from "./store.js";
18
+ import { MapContext } from "../../base-map/provider.js";
19
19
  import { useContext, useMemo } from "react";
20
20
  import { useBus } from "@accelint/bus/react";
21
21
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -10,9 +10,9 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import { CircleFeatureProperties, CircleProperties, CircleRadius, CircleShape, EllipseFeatureProperties, EllipseProperties, EllipseShape, LineStringShape, PointShape, PolygonShape, RectangleShape, Shape, ShapeFeature, ShapeFeatureProperties, ShapeFeatureType, ShapeFeatureTypeValues, ShapeId, StyleProperties, Subscription, isCircleShape, isEllipseShape, isLineStringShape, isPointShape, isPolygonShape, isRectangleShape } from "./shared/types.js";
13
+ import { CircleFeatureProperties, CircleProperties, CircleRadius, CircleShape, EllipseFeatureProperties, EllipseProperties, EllipseShape, LineStringShape, PointShape, PolygonShape, RectangleShape, Shape, ShapeFeature, ShapeFeatureProperties, ShapeFeatureType, ShapeId, StyleProperties, StyledFeature, StyledFeatureProperties, isCircleShape, isEllipseShape, isLineStringShape, isPointShape, isPolygonShape, isRectangleShape } from "./shared/types.js";
14
14
  import { CardinalLabelCoordinateAnchor, LabelHorizontalPosition, LabelPosition2d, LabelPositionOptions, LabelVerticalPosition } from "./display-shape-layer/utils/labels.js";
15
- import { DisplayShapeLayerProps, ShowLabelsMode, StyledFeature, StyledFeatureProperties } from "./display-shape-layer/types.js";
15
+ import { DisplayShapeLayerProps, ShowLabelsMode } from "./display-shape-layer/types.js";
16
16
  import { DisplayShapeLayer } from "./display-shape-layer/index.js";
17
17
  import { UseSelectShapeReturn, useSelectShape } from "./display-shape-layer/use-select-shape.js";
18
18
  import { DrawShapeEvent, DrawShapeEventType, DrawShapeEvents, ShapeDrawCanceledEvent, ShapeDrawingEvent, ShapeDrawnEvent } from "./draw-shape-layer/events.js";
@@ -26,4 +26,4 @@ import { useEditShape } from "./edit-shape-layer/use-edit-shape.js";
26
26
  import { BASE_FILL_OPACITY, DASH_ARRAYS, DEFAULT_COLORS, DEFAULT_EDIT_HANDLE_COLOR, DEFAULT_EDIT_HANDLE_OUTLINE_COLOR, DEFAULT_STYLE_PROPERTIES, LINE_PATTERNS, LINE_WIDTHS, SHAPE_LAYER_IDS } from "./shared/constants.js";
27
27
  import { ShapeEventType, ShapeEvents } from "./shared/events.js";
28
28
  import { getDashArray, getFillColor, getLineColor, getLineWidth, normalizeColor } from "./shared/utils/style-utils.js";
29
- export { BASE_FILL_OPACITY, type CardinalLabelCoordinateAnchor, type CircleFeatureProperties, type CircleProperties, type CircleRadius, type CircleShape, DASH_ARRAYS, DEFAULT_COLORS, DEFAULT_EDIT_HANDLE_COLOR, DEFAULT_EDIT_HANDLE_OUTLINE_COLOR, DEFAULT_STYLE_PROPERTIES, DisplayShapeLayer, type DisplayShapeLayerProps, type DrawShapeEvent, type DrawShapeEventType, DrawShapeEvents, DrawShapeLayer, type DrawShapeLayerProps, type DrawShapeOptions, type DrawingState, type EditMode, type EditShapeEvent, type EditShapeEventType, EditShapeEvents, EditShapeLayer, type EditShapeLayerProps, type EditShapeOptions, type EditingState, type EllipseFeatureProperties, type EllipseProperties, type EllipseShape, LINE_PATTERNS, LINE_WIDTHS, type LabelHorizontalPosition, type LabelPosition2d, type LabelPositionOptions, type LabelVerticalPosition, type LineStringShape, type PointShape, type PolygonShape, type RectangleShape, SHAPE_LAYER_IDS, type Shape, type ShapeDrawCanceledEvent, type ShapeDrawingEvent, type ShapeDrawnEvent, type ShapeEditCanceledEvent, type ShapeEditingEvent, type ShapeEventType, ShapeEvents, type ShapeFeature, type ShapeFeatureProperties, ShapeFeatureType, type ShapeFeatureTypeValues, type ShapeId, type ShapeUpdatedEvent, type ShowLabelsMode, type StyleProperties, type StyledFeature, type StyledFeatureProperties, type Subscription, type UseDrawShapeOptions, type UseDrawShapeReturn, type UseEditShapeOptions, type UseEditShapeReturn, type UseSelectShapeReturn, getDashArray, getFillColor, getLineColor, getLineWidth, isCircleShape, isEllipseShape, isLineStringShape, isPointShape, isPolygonShape, isRectangleShape, normalizeColor, useDrawShape, useEditShape, useSelectShape };
29
+ export { BASE_FILL_OPACITY, type CardinalLabelCoordinateAnchor, type CircleFeatureProperties, type CircleProperties, type CircleRadius, type CircleShape, DASH_ARRAYS, DEFAULT_COLORS, DEFAULT_EDIT_HANDLE_COLOR, DEFAULT_EDIT_HANDLE_OUTLINE_COLOR, DEFAULT_STYLE_PROPERTIES, DisplayShapeLayer, type DisplayShapeLayerProps, type DrawShapeEvent, type DrawShapeEventType, DrawShapeEvents, DrawShapeLayer, type DrawShapeLayerProps, type DrawShapeOptions, type DrawingState, type EditMode, type EditShapeEvent, type EditShapeEventType, EditShapeEvents, EditShapeLayer, type EditShapeLayerProps, type EditShapeOptions, type EditingState, type EllipseFeatureProperties, type EllipseProperties, type EllipseShape, LINE_PATTERNS, LINE_WIDTHS, type LabelHorizontalPosition, type LabelPosition2d, type LabelPositionOptions, type LabelVerticalPosition, type LineStringShape, type PointShape, type PolygonShape, type RectangleShape, SHAPE_LAYER_IDS, type Shape, type ShapeDrawCanceledEvent, type ShapeDrawingEvent, type ShapeDrawnEvent, type ShapeEditCanceledEvent, type ShapeEditingEvent, type ShapeEventType, ShapeEvents, type ShapeFeature, type ShapeFeatureProperties, ShapeFeatureType, type ShapeId, type ShapeUpdatedEvent, type ShowLabelsMode, type StyleProperties, type StyledFeature, type StyledFeatureProperties, type UseDrawShapeOptions, type UseDrawShapeReturn, type UseEditShapeOptions, type UseEditShapeReturn, type UseSelectShapeReturn, getDashArray, getFillColor, getLineColor, getLineWidth, isCircleShape, isEllipseShape, isLineStringShape, isPointShape, isPolygonShape, isRectangleShape, normalizeColor, useDrawShape, useEditShape, useSelectShape };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -11,16 +11,16 @@
11
11
  */
12
12
 
13
13
 
14
- import { BASE_FILL_OPACITY, DASH_ARRAYS, DEFAULT_COLORS, DEFAULT_EDIT_HANDLE_COLOR, DEFAULT_EDIT_HANDLE_OUTLINE_COLOR, DEFAULT_STYLE_PROPERTIES, LINE_PATTERNS, LINE_WIDTHS, SHAPE_LAYER_IDS } from "./shared/constants.js";
15
14
  import { ShapeEvents } from "./shared/events.js";
16
- import { getDashArray, getFillColor, getLineColor, getLineWidth, normalizeColor } from "./shared/utils/style-utils.js";
17
15
  import { ShapeFeatureType, isCircleShape, isEllipseShape, isLineStringShape, isPointShape, isPolygonShape, isRectangleShape } from "./shared/types.js";
16
+ import { DrawShapeEvents } from "./draw-shape-layer/events.js";
17
+ import { BASE_FILL_OPACITY, DASH_ARRAYS, DEFAULT_COLORS, DEFAULT_EDIT_HANDLE_COLOR, DEFAULT_EDIT_HANDLE_OUTLINE_COLOR, DEFAULT_STYLE_PROPERTIES, LINE_PATTERNS, LINE_WIDTHS, SHAPE_LAYER_IDS } from "./shared/constants.js";
18
+ import { EditShapeEvents } from "./edit-shape-layer/events.js";
19
+ import { getDashArray, getFillColor, getLineColor, getLineWidth, normalizeColor } from "./shared/utils/style-utils.js";
18
20
  import { DisplayShapeLayer } from "./display-shape-layer/index.js";
19
21
  import { useSelectShape } from "./display-shape-layer/use-select-shape.js";
20
- import { DrawShapeEvents } from "./draw-shape-layer/events.js";
21
22
  import { DrawShapeLayer } from "./draw-shape-layer/index.js";
22
23
  import { useDrawShape } from "./draw-shape-layer/use-draw-shape.js";
23
- import { EditShapeEvents } from "./edit-shape-layer/events.js";
24
24
  import { EditShapeLayer } from "./edit-shape-layer/index.js";
25
25
  import { useEditShape } from "./edit-shape-layer/use-edit-shape.js";
26
26
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -10,7 +10,7 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import { StyleProperties } from "./types.js";
13
+ import { LinePattern, StyleProperties } from "./types.js";
14
14
  import { Color } from "@deck.gl/core";
15
15
 
16
16
  //#region src/deckgl/shapes/shared/constants.d.ts
@@ -21,6 +21,7 @@ declare const SHAPE_LAYER_IDS: {
21
21
  readonly DISPLAY: "DISPLAY_SHAPES";
22
22
  readonly DISPLAY_HIGHLIGHT: "DISPLAY_SHAPES::Highlight";
23
23
  readonly DISPLAY_LABELS: "DISPLAY_SHAPES::Labels";
24
+ readonly DISPLAY_SELECTION: "DISPLAY_SHAPES::Selection";
24
25
  };
25
26
  /**
26
27
  * Base fill opacity multiplier for standard semi-transparent look.
@@ -59,7 +60,7 @@ declare const LINE_PATTERNS: readonly ["solid", "dashed", "dotted"];
59
60
  /**
60
61
  * Dash array patterns for border/outline rendering
61
62
  */
62
- declare const DASH_ARRAYS: Record<'solid' | 'dashed' | 'dotted', [number, number] | null>;
63
+ declare const DASH_ARRAYS: Record<LinePattern, [number, number] | null>;
63
64
  /**
64
65
  * Default edit handle color (white) - used by both draw and edit layers
65
66
  */
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -22,7 +22,8 @@ import { DEFAULT_TEXT_STYLE } from "../../text-settings.js";
22
22
  const SHAPE_LAYER_IDS = {
23
23
  DISPLAY: "DISPLAY_SHAPES",
24
24
  DISPLAY_HIGHLIGHT: "DISPLAY_SHAPES::Highlight",
25
- DISPLAY_LABELS: "DISPLAY_SHAPES::Labels"
25
+ DISPLAY_LABELS: "DISPLAY_SHAPES::Labels",
26
+ DISPLAY_SELECTION: "DISPLAY_SHAPES::Selection"
26
27
  };
27
28
  /**
28
29
  * Base fill opacity multiplier for standard semi-transparent look.
@@ -92,7 +93,7 @@ const DEFAULT_TENTATIVE_COLORS = {
92
93
  const DEFAULT_STYLE_PROPERTIES = {
93
94
  fillColor: DEFAULT_COLORS.fill,
94
95
  lineColor: DEFAULT_COLORS.line,
95
- lineWidth: 2,
96
+ lineWidth: DEFAULT_LINE_WIDTH,
96
97
  linePattern: "solid"
97
98
  };
98
99
  /**
@@ -141,10 +142,11 @@ const DEFAULT_EDIT_HANDLE_OUTLINE_COLOR = [
141
142
  /**
142
143
  * Empty feature collection for initializing editable layers
143
144
  */
144
- const EMPTY_FEATURE_COLLECTION = {
145
+ const EMPTY_FEATURE_COLLECTION = Object.freeze({
145
146
  type: "FeatureCollection",
146
- features: []
147
- };
147
+ features: Object.freeze([])
148
+ });
149
+ const ASCII_RANGE = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~€";
148
150
  /**
149
151
  * Custom character set for deck.gl TextLayer used by tooltip rendering.
150
152
  *
@@ -153,8 +155,11 @@ const EMPTY_FEATURE_COLLECTION = {
153
155
  * like degree symbol (°) and superscript 2 (²) must be explicitly included
154
156
  * for tooltip text like "100.5 km²" to render correctly.
155
157
  */
156
- const TOOLTIP_CHARACTER_SET = ["°", "²"];
157
- for (let i = 32; i <= 128; i++) TOOLTIP_CHARACTER_SET.push(String.fromCharCode(i));
158
+ const TOOLTIP_CHARACTER_SET = [
159
+ "°",
160
+ "²",
161
+ ...ASCII_RANGE.split("")
162
+ ];
158
163
  /**
159
164
  * Sublayer props for tooltip text rendering.
160
165
  * Used by both draw-shape-layer and edit-shape-layer for area/distance tooltips.
@@ -195,20 +200,41 @@ const EDITABLE_LAYER_SUBLAYER_PROPS = {
195
200
  *
196
201
  * @param value - The distance value to format
197
202
  * @returns The formatted string with 2 decimal places
203
+ * @example
204
+ * ```typescript
205
+ * formatDistance(3.14159); // → "3.14"
206
+ * ```
198
207
  */
199
208
  function formatDistance(value) {
200
209
  return value.toFixed(2);
201
210
  }
202
211
  /**
203
- * Format circle tooltip text showing diameter and area.
212
+ * Format circle tooltip text showing radius and area.
204
213
  *
205
- * @param diameter - Circle diameter in the specified units
214
+ * @param radius - Circle radius in the specified units
206
215
  * @param area - Circle area in the specified units squared
207
216
  * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')
208
- * @returns Formatted tooltip text: "d: {diameter} {unit}\n{area} {unit}²"
217
+ * @returns Formatted tooltip text: "r: {radius} {unit}\n{area} {unit}²"
218
+ * @example
219
+ * ```typescript
220
+ * formatCircleTooltip(12.5, 490.87, 'km');
221
+ * // → "r: 12.50 km\n490.87 km²"
222
+ * ```
223
+ */
224
+ function formatCircleTooltip(radius, area, unitAbbrev) {
225
+ return `r: ${formatDistance(radius)} ${unitAbbrev}\n${formatDistance(area)} ${unitAbbrev}²`;
226
+ }
227
+ /**
228
+ * Format a "{dim1} {unit} x {dim2} {unit}\n{area} {unit}²" tooltip string.
229
+ *
230
+ * @param dim1 - First dimension value
231
+ * @param dim2 - Second dimension value
232
+ * @param area - Area value
233
+ * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')
234
+ * @returns Formatted tooltip text
209
235
  */
210
- function formatCircleTooltip(diameter, area, unitAbbrev) {
211
- return `d: ${formatDistance(diameter)} ${unitAbbrev}\n${formatDistance(area)} ${unitAbbrev}²`;
236
+ function formatDimensionsTooltip(dim1, dim2, area, unitAbbrev) {
237
+ return `${formatDistance(dim1)} ${unitAbbrev} x ${formatDistance(dim2)} ${unitAbbrev}\n${formatDistance(area)} ${unitAbbrev}²`;
212
238
  }
213
239
  /**
214
240
  * Format rectangle tooltip text showing dimensions and area.
@@ -218,9 +244,14 @@ function formatCircleTooltip(diameter, area, unitAbbrev) {
218
244
  * @param area - Rectangle area in the specified units squared
219
245
  * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')
220
246
  * @returns Formatted tooltip text: "{width} {unit} x {height} {unit}\n{area} {unit}²"
247
+ * @example
248
+ * ```typescript
249
+ * formatRectangleTooltip(5.0, 3.2, 16.0, 'km');
250
+ * // → "5.00 km x 3.20 km\n16.00 km²"
251
+ * ```
221
252
  */
222
253
  function formatRectangleTooltip(width, height, area, unitAbbrev) {
223
- return `${formatDistance(width)} ${unitAbbrev} x ${formatDistance(height)} ${unitAbbrev}\n${formatDistance(area)} ${unitAbbrev}²`;
254
+ return formatDimensionsTooltip(width, height, area, unitAbbrev);
224
255
  }
225
256
  /**
226
257
  * Format ellipse tooltip text showing axes and area.
@@ -230,9 +261,14 @@ function formatRectangleTooltip(width, height, area, unitAbbrev) {
230
261
  * @param area - Ellipse area in the specified units squared
231
262
  * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')
232
263
  * @returns Formatted tooltip text: "{major} {unit} x {minor} {unit}\n{area} {unit}²"
264
+ * @example
265
+ * ```typescript
266
+ * formatEllipseTooltip(10.0, 6.0, 47.12, 'km');
267
+ * // → "10.00 km x 6.00 km\n47.12 km²"
268
+ * ```
233
269
  */
234
270
  function formatEllipseTooltip(majorAxis, minorAxis, area, unitAbbrev) {
235
- return `${formatDistance(majorAxis)} ${unitAbbrev} x ${formatDistance(minorAxis)} ${unitAbbrev}\n${formatDistance(area)} ${unitAbbrev}²`;
271
+ return formatDimensionsTooltip(majorAxis, minorAxis, area, unitAbbrev);
236
272
  }
237
273
  /**
238
274
  * Format simple distance tooltip text.
@@ -240,6 +276,10 @@ function formatEllipseTooltip(majorAxis, minorAxis, area, unitAbbrev) {
240
276
  * @param distance - Distance value in the specified units
241
277
  * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')
242
278
  * @returns Formatted tooltip text: "{distance} {unit}"
279
+ * @example
280
+ * ```typescript
281
+ * formatDistanceTooltip(42.5, 'km'); // → "42.50 km"
282
+ * ```
243
283
  */
244
284
  function formatDistanceTooltip(distance, unitAbbrev) {
245
285
  return `${formatDistance(distance)} ${unitAbbrev}`;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","names":["DEFAULT_STYLE_PROPERTIES: StyleProperties","DASH_ARRAYS: Record<\n 'solid' | 'dashed' | 'dotted',\n [number, number] | null\n>","DEFAULT_EDIT_HANDLE_COLOR: Color","DEFAULT_EDIT_HANDLE_OUTLINE_COLOR: Color","EMPTY_FEATURE_COLLECTION: import('geojson').FeatureCollection","TOOLTIP_CHARACTER_SET: string[]"],"sources":["../../../../src/deckgl/shapes/shared/constants.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport { DEFAULT_TEXT_STYLE } from '../../text-settings';\nimport type { Color } from '@deck.gl/core';\nimport type { StyleProperties } from './types';\n\n/**\n * Layer IDs for shape layers\n */\nexport const SHAPE_LAYER_IDS = {\n DISPLAY: 'DISPLAY_SHAPES',\n DISPLAY_HIGHLIGHT: 'DISPLAY_SHAPES::Highlight',\n DISPLAY_LABELS: 'DISPLAY_SHAPES::Labels',\n} as const;\n\n/**\n * Base fill opacity multiplier for standard semi-transparent look.\n * Multiplies alpha by 0.2 (reduces to 20% of original opacity).\n */\nexport const BASE_FILL_OPACITY = 0.2;\n\n/**\n * Default border/outline width in pixels when not specified in styleProperties\n */\nexport const DEFAULT_LINE_WIDTH = 2;\n\n/**\n * Additional pixels added to border/outline width on hover\n */\nexport const HOVER_WIDTH_INCREASE = 2;\n\n/**\n * Additional pixels added to border/outline width for selection highlight\n */\nexport const HIGHLIGHT_WIDTH_INCREASE = 5;\n\n/**\n * Fixed opacity for label background (0-255)\n */\nexport const LABEL_BACKGROUND_OPACITY = 200;\n\n/**\n * Fixed opacity for label border (0-255)\n */\nexport const LABEL_BORDER_OPACITY = 255;\n\n/**\n * Default colors as RGBA arrays for DeckGL layers.\n *\n * These are the canonical color values used throughout the shapes system.\n * All other color constants should derive from these to maintain consistency.\n */\nexport const DEFAULT_COLORS = {\n /** Default fill color (white at full alpha) */\n fill: [255, 255, 255, 255] as Color,\n /** Default border/outline color (outline-interactive-hover: #888a8f) */\n line: [136, 138, 143, 255] as Color,\n /** Highlight/selection color (turquoise at ~39% alpha) */\n highlight: [40, 245, 190, 100] as Color,\n} as const;\n\n/**\n * Tentative (during-drawing) colors.\n *\n * These colors are used for the shape preview while drawing.\n * Fill is semi-transparent (8% opacity) to not obscure underlying features.\n * Border/outline uses the same color as saved shapes for consistency.\n */\nexport const DEFAULT_TENTATIVE_COLORS = {\n /** Tentative fill color (white at 8% opacity: 0.08 * 255 ≈ 20) */\n fill: [255, 255, 255, 20] as Color,\n /** Tentative border/outline color (same as saved shapes for consistency) */\n line: DEFAULT_COLORS.line,\n} as const;\n\n/**\n * Default style properties for saved shapes.\n *\n * These are applied when a shape is completed/saved.\n * Can be overridden via styleDefaults in draw options.\n */\nexport const DEFAULT_STYLE_PROPERTIES: StyleProperties = {\n fillColor: DEFAULT_COLORS.fill,\n lineColor: DEFAULT_COLORS.line,\n lineWidth: 2,\n linePattern: 'solid',\n};\n\n/**\n * Border/outline width options (in pixels)\n */\nexport const LINE_WIDTHS = [1, 2, 4, 8] as const;\n\n/**\n * Border/outline pattern options\n */\nexport const LINE_PATTERNS = ['solid', 'dashed', 'dotted'] as const;\n\n/**\n * Dash array patterns for border/outline rendering\n */\nexport const DASH_ARRAYS: Record<\n 'solid' | 'dashed' | 'dotted',\n [number, number] | null\n> = {\n solid: null,\n dashed: [8, 4],\n dotted: [2, 4],\n};\n\n/**\n * Default tentative fill color (white at 8% opacity - rgba(255, 255, 255, 0.08))\n * Used when drawing new shapes before they're completed.\n * 0.08 * 255 ≈ 20\n */\nexport const DEFAULT_TENTATIVE_FILL_COLOR: Color = [255, 255, 255, 20];\n\n/**\n * Default tentative border/outline color (outline-interactive-hover: #888a8f)\n * Used when drawing new shapes before they're completed.\n */\nexport const DEFAULT_TENTATIVE_LINE_COLOR: Color = [136, 138, 143, 255];\n\n/**\n * Default edit handle color (white) - used by both draw and edit layers\n */\nexport const DEFAULT_EDIT_HANDLE_COLOR: Color = [255, 255, 255, 255];\n\n/**\n * Edit handle outline color (dark for contrast)\n */\nexport const DEFAULT_EDIT_HANDLE_OUTLINE_COLOR: Color = [0, 0, 0, 200];\n\n/**\n * Empty feature collection for initializing editable layers\n */\nexport const EMPTY_FEATURE_COLLECTION: import('geojson').FeatureCollection = {\n type: 'FeatureCollection',\n features: [],\n};\n\n/**\n * Custom character set for deck.gl TextLayer used by tooltip rendering.\n *\n * deck.gl's TextLayer uses SDF (Signed Distance Field) font rendering which\n * by default only supports basic ASCII characters (32-128). Special characters\n * like degree symbol (°) and superscript 2 (²) must be explicitly included\n * for tooltip text like \"100.5 km²\" to render correctly.\n */\nexport const TOOLTIP_CHARACTER_SET: string[] = ['°', '²'];\n\n// Add standard ASCII characters (space through tilde + DEL)\nfor (let i = 32; i <= 128; i++) {\n TOOLTIP_CHARACTER_SET.push(String.fromCharCode(i));\n}\n\n/**\n * Sublayer props for tooltip text rendering.\n * Used by both draw-shape-layer and edit-shape-layer for area/distance tooltips.\n */\nexport const TOOLTIP_SUBLAYER_PROPS = {\n tooltips: {\n ...DEFAULT_TEXT_STYLE,\n fontFamily: 'Roboto MonoVariable, monospace',\n characterSet: TOOLTIP_CHARACTER_SET,\n getTextAnchor: 'start',\n getAlignmentBaseline: 'bottom',\n getPixelOffset: [8, 0],\n },\n};\n\n/**\n * Shared edit handle sublayer props for EditableGeoJsonLayer.\n * Used by both draw-shape-layer and edit-shape-layer.\n */\nexport const EDIT_HANDLE_SUBLAYER_PROPS = {\n editHandlePointOutline: {\n getFillColor: DEFAULT_EDIT_HANDLE_COLOR,\n getRadius: 6,\n },\n editHandlePoint: {\n getFillColor: DEFAULT_EDIT_HANDLE_COLOR,\n getRadius: 4,\n },\n};\n\n/**\n * Combined sublayer props for EditableGeoJsonLayer with tooltips and edit handles.\n * Used by both draw-shape-layer and edit-shape-layer.\n */\nexport const EDITABLE_LAYER_SUBLAYER_PROPS = {\n ...TOOLTIP_SUBLAYER_PROPS,\n ...EDIT_HANDLE_SUBLAYER_PROPS,\n};\n\n/**\n * Format a distance value for tooltip display.\n * Used by draw and edit mode tooltips for consistent formatting.\n *\n * @param value - The distance value to format\n * @returns The formatted string with 2 decimal places\n */\nexport function formatDistance(value: number): string {\n return value.toFixed(2);\n}\n\n// =============================================================================\n// Tooltip Text Formatters\n// =============================================================================\n// These functions generate consistent tooltip text for both draw and edit modes.\n\n/**\n * Format circle tooltip text showing diameter and area.\n *\n * @param diameter - Circle diameter in the specified units\n * @param area - Circle area in the specified units squared\n * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')\n * @returns Formatted tooltip text: \"d: {diameter} {unit}\\n{area} {unit}²\"\n */\nexport function formatCircleTooltip(\n diameter: number,\n area: number,\n unitAbbrev: string,\n): string {\n return `d: ${formatDistance(diameter)} ${unitAbbrev}\\n${formatDistance(area)} ${unitAbbrev}²`;\n}\n\n/**\n * Format rectangle tooltip text showing dimensions and area.\n *\n * @param width - Rectangle width in the specified units\n * @param height - Rectangle height in the specified units\n * @param area - Rectangle area in the specified units squared\n * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')\n * @returns Formatted tooltip text: \"{width} {unit} x {height} {unit}\\n{area} {unit}²\"\n */\nexport function formatRectangleTooltip(\n width: number,\n height: number,\n area: number,\n unitAbbrev: string,\n): string {\n return `${formatDistance(width)} ${unitAbbrev} x ${formatDistance(height)} ${unitAbbrev}\\n${formatDistance(area)} ${unitAbbrev}²`;\n}\n\n/**\n * Format ellipse tooltip text showing axes and area.\n *\n * @param majorAxis - Ellipse major axis (full length) in the specified units\n * @param minorAxis - Ellipse minor axis (full length) in the specified units\n * @param area - Ellipse area in the specified units squared\n * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')\n * @returns Formatted tooltip text: \"{major} {unit} x {minor} {unit}\\n{area} {unit}²\"\n */\nexport function formatEllipseTooltip(\n majorAxis: number,\n minorAxis: number,\n area: number,\n unitAbbrev: string,\n): string {\n return `${formatDistance(majorAxis)} ${unitAbbrev} x ${formatDistance(minorAxis)} ${unitAbbrev}\\n${formatDistance(area)} ${unitAbbrev}²`;\n}\n\n/**\n * Format simple distance tooltip text.\n *\n * @param distance - Distance value in the specified units\n * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')\n * @returns Formatted tooltip text: \"{distance} {unit}\"\n */\nexport function formatDistanceTooltip(\n distance: number,\n unitAbbrev: string,\n): string {\n return `${formatDistance(distance)} ${unitAbbrev}`;\n}\n\n// =============================================================================\n// Edit Event Type Classification\n// =============================================================================\n\n/**\n * Continuous edit event types that fire during dragging.\n * These are emitted repeatedly while the user drags during an edit operation.\n */\nexport const CONTINUOUS_EDIT_TYPES = new Set([\n 'movePosition',\n 'unionGeometry',\n 'scaling',\n 'rotating',\n 'translating',\n]);\n\n/**\n * Completion edit event types that fire when dragging ends.\n * These are emitted once when the user finishes an edit action.\n */\nexport const COMPLETION_EDIT_TYPES = new Set([\n 'finishMovePosition',\n 'addPosition',\n 'removePosition',\n 'scaled',\n 'rotated',\n 'translated',\n]);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,MAAa,kBAAkB;CAC7B,SAAS;CACT,mBAAmB;CACnB,gBAAgB;CACjB;;;;;AAMD,MAAa,oBAAoB;;;;AAKjC,MAAa,qBAAqB;;;;AAKlC,MAAa,uBAAuB;;;;AAKpC,MAAa,2BAA2B;;;;;;;AAkBxC,MAAa,iBAAiB;CAE5B,MAAM;EAAC;EAAK;EAAK;EAAK;EAAI;CAE1B,MAAM;EAAC;EAAK;EAAK;EAAK;EAAI;CAE1B,WAAW;EAAC;EAAI;EAAK;EAAK;EAAI;CAC/B;;;;;;;;AASD,MAAa,2BAA2B;CAEtC,MAAM;EAAC;EAAK;EAAK;EAAK;EAAG;CAEzB,MAAM,eAAe;CACtB;;;;;;;AAQD,MAAaA,2BAA4C;CACvD,WAAW,eAAe;CAC1B,WAAW,eAAe;CAC1B,WAAW;CACX,aAAa;CACd;;;;AAKD,MAAa,cAAc;CAAC;CAAG;CAAG;CAAG;CAAE;;;;AAKvC,MAAa,gBAAgB;CAAC;CAAS;CAAU;CAAS;;;;AAK1D,MAAaC,cAGT;CACF,OAAO;CACP,QAAQ,CAAC,GAAG,EAAE;CACd,QAAQ,CAAC,GAAG,EAAE;CACf;;;;AAkBD,MAAaC,4BAAmC;CAAC;CAAK;CAAK;CAAK;CAAI;;;;AAKpE,MAAaC,oCAA2C;CAAC;CAAG;CAAG;CAAG;CAAI;;;;AAKtE,MAAaC,2BAAgE;CAC3E,MAAM;CACN,UAAU,EAAE;CACb;;;;;;;;;AAUD,MAAaC,wBAAkC,CAAC,KAAK,IAAI;AAGzD,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,IACzB,uBAAsB,KAAK,OAAO,aAAa,EAAE,CAAC;;;;;AAOpD,MAAa,yBAAyB,EACpC,UAAU;CACR,GAAG;CACH,YAAY;CACZ,cAAc;CACd,eAAe;CACf,sBAAsB;CACtB,gBAAgB,CAAC,GAAG,EAAE;CACvB,EACF;;;;;AAMD,MAAa,6BAA6B;CACxC,wBAAwB;EACtB,cAAc;EACd,WAAW;EACZ;CACD,iBAAiB;EACf,cAAc;EACd,WAAW;EACZ;CACF;;;;;AAMD,MAAa,gCAAgC;CAC3C,GAAG;CACH,GAAG;CACJ;;;;;;;;AASD,SAAgB,eAAe,OAAuB;AACpD,QAAO,MAAM,QAAQ,EAAE;;;;;;;;;;AAgBzB,SAAgB,oBACd,UACA,MACA,YACQ;AACR,QAAO,MAAM,eAAe,SAAS,CAAC,GAAG,WAAW,IAAI,eAAe,KAAK,CAAC,GAAG,WAAW;;;;;;;;;;;AAY7F,SAAgB,uBACd,OACA,QACA,MACA,YACQ;AACR,QAAO,GAAG,eAAe,MAAM,CAAC,GAAG,WAAW,KAAK,eAAe,OAAO,CAAC,GAAG,WAAW,IAAI,eAAe,KAAK,CAAC,GAAG,WAAW;;;;;;;;;;;AAYjI,SAAgB,qBACd,WACA,WACA,MACA,YACQ;AACR,QAAO,GAAG,eAAe,UAAU,CAAC,GAAG,WAAW,KAAK,eAAe,UAAU,CAAC,GAAG,WAAW,IAAI,eAAe,KAAK,CAAC,GAAG,WAAW;;;;;;;;;AAUxI,SAAgB,sBACd,UACA,YACQ;AACR,QAAO,GAAG,eAAe,SAAS,CAAC,GAAG;;;;;;AAWxC,MAAa,wBAAwB,IAAI,IAAI;CAC3C;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;AAMF,MAAa,wBAAwB,IAAI,IAAI;CAC3C;CACA;CACA;CACA;CACA;CACA;CACD,CAAC"}
1
+ {"version":3,"file":"constants.js","names":["DEFAULT_STYLE_PROPERTIES: StyleProperties","DASH_ARRAYS: Record<LinePattern, [number, number] | null>","DEFAULT_EDIT_HANDLE_COLOR: Color","DEFAULT_EDIT_HANDLE_OUTLINE_COLOR: Color","EMPTY_FEATURE_COLLECTION: FeatureCollection","TOOLTIP_CHARACTER_SET: string[]"],"sources":["../../../../src/deckgl/shapes/shared/constants.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport { DEFAULT_TEXT_STYLE } from '../../text-settings';\nimport type { Color } from '@deck.gl/core';\nimport type { FeatureCollection } from 'geojson';\nimport type { LinePattern, StyleProperties } from './types';\n\n/**\n * Layer IDs for shape layers\n */\nexport const SHAPE_LAYER_IDS = {\n DISPLAY: 'DISPLAY_SHAPES',\n DISPLAY_HIGHLIGHT: 'DISPLAY_SHAPES::Highlight',\n DISPLAY_LABELS: 'DISPLAY_SHAPES::Labels',\n DISPLAY_SELECTION: 'DISPLAY_SHAPES::Selection',\n} as const;\n\n/**\n * Base fill opacity multiplier for standard semi-transparent look.\n * Multiplies alpha by 0.2 (reduces to 20% of original opacity).\n */\nexport const BASE_FILL_OPACITY = 0.2;\n\n/**\n * Default border/outline width in pixels when not specified in styleProperties\n */\nexport const DEFAULT_LINE_WIDTH = 2;\n\n/**\n * Additional pixels added to border/outline width on hover\n */\nexport const HOVER_WIDTH_INCREASE = 2;\n\n/**\n * Additional pixels added to border/outline width for selection highlight\n */\nexport const HIGHLIGHT_WIDTH_INCREASE = 5;\n\n/**\n * Fixed opacity for label background (0-255)\n */\nexport const LABEL_BACKGROUND_OPACITY = 200;\n\n/**\n * Fixed opacity for label border (0-255)\n */\nexport const LABEL_BORDER_OPACITY = 255;\n\n/**\n * Default colors as RGBA arrays for DeckGL layers.\n *\n * These are the canonical color values used throughout the shapes system.\n * All other color constants should derive from these to maintain consistency.\n */\nexport const DEFAULT_COLORS = {\n /** Default fill color (white at full alpha) */\n fill: [255, 255, 255, 255] as Color,\n /** Default border/outline color (outline-interactive-hover: #888a8f) */\n line: [136, 138, 143, 255] as Color,\n /** Highlight/selection color (turquoise at ~39% alpha) */\n highlight: [40, 245, 190, 100] as Color,\n} as const;\n\n/**\n * Tentative (during-drawing) colors.\n *\n * These colors are used for the shape preview while drawing.\n * Fill is semi-transparent (8% opacity) to not obscure underlying features.\n * Border/outline uses the same color as saved shapes for consistency.\n */\nexport const DEFAULT_TENTATIVE_COLORS = {\n /** Tentative fill color (white at 8% opacity: 0.08 * 255 ≈ 20) */\n fill: [255, 255, 255, 20] as Color,\n /** Tentative border/outline color (same as saved shapes for consistency) */\n line: DEFAULT_COLORS.line,\n} as const;\n\n/**\n * Default style properties for saved shapes.\n *\n * These are applied when a shape is completed/saved.\n * Can be overridden via styleDefaults in draw options.\n */\nexport const DEFAULT_STYLE_PROPERTIES: StyleProperties = {\n fillColor: DEFAULT_COLORS.fill,\n lineColor: DEFAULT_COLORS.line,\n lineWidth: DEFAULT_LINE_WIDTH,\n linePattern: 'solid',\n};\n\n/**\n * Border/outline width options (in pixels)\n */\nexport const LINE_WIDTHS = [1, 2, 4, 8] as const;\n\n/**\n * Border/outline pattern options\n */\nexport const LINE_PATTERNS = ['solid', 'dashed', 'dotted'] as const;\n\n/**\n * Dash array patterns for border/outline rendering\n */\nexport const DASH_ARRAYS: Record<LinePattern, [number, number] | null> = {\n solid: null,\n dashed: [8, 4],\n dotted: [2, 4],\n};\n\n/**\n * Default edit handle color (white) - used by both draw and edit layers\n */\nexport const DEFAULT_EDIT_HANDLE_COLOR: Color = [255, 255, 255, 255];\n\n/**\n * Edit handle outline color (dark for contrast)\n */\nexport const DEFAULT_EDIT_HANDLE_OUTLINE_COLOR: Color = [0, 0, 0, 200];\n\n/**\n * Empty feature collection for initializing editable layers\n */\nexport const EMPTY_FEATURE_COLLECTION: FeatureCollection = Object.freeze({\n type: 'FeatureCollection',\n features: Object.freeze([]),\n}) as unknown as FeatureCollection;\n\nconst ASCII_RANGE =\n ' !\"#$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80';\n\n/**\n * Custom character set for deck.gl TextLayer used by tooltip rendering.\n *\n * deck.gl's TextLayer uses SDF (Signed Distance Field) font rendering which\n * by default only supports basic ASCII characters (32-128). Special characters\n * like degree symbol (°) and superscript 2 (²) must be explicitly included\n * for tooltip text like \"100.5 km²\" to render correctly.\n */\nexport const TOOLTIP_CHARACTER_SET: string[] = [\n '°',\n '²',\n ...ASCII_RANGE.split(''),\n];\n\n/**\n * Sublayer props for tooltip text rendering.\n * Used by both draw-shape-layer and edit-shape-layer for area/distance tooltips.\n */\nexport const TOOLTIP_SUBLAYER_PROPS = {\n tooltips: {\n ...DEFAULT_TEXT_STYLE,\n fontFamily: 'Roboto MonoVariable, monospace',\n characterSet: TOOLTIP_CHARACTER_SET,\n getTextAnchor: 'start',\n getAlignmentBaseline: 'bottom',\n getPixelOffset: [8, 0],\n },\n};\n\n/**\n * Shared edit handle sublayer props for EditableGeoJsonLayer.\n * Used by both draw-shape-layer and edit-shape-layer.\n */\nexport const EDIT_HANDLE_SUBLAYER_PROPS = {\n editHandlePointOutline: {\n getFillColor: DEFAULT_EDIT_HANDLE_COLOR,\n getRadius: 6,\n },\n editHandlePoint: {\n getFillColor: DEFAULT_EDIT_HANDLE_COLOR,\n getRadius: 4,\n },\n};\n\n/**\n * Combined sublayer props for EditableGeoJsonLayer with tooltips and edit handles.\n * Used by both draw-shape-layer and edit-shape-layer.\n */\nexport const EDITABLE_LAYER_SUBLAYER_PROPS = {\n ...TOOLTIP_SUBLAYER_PROPS,\n ...EDIT_HANDLE_SUBLAYER_PROPS,\n};\n\n/**\n * Format a distance value for tooltip display.\n * Used by draw and edit mode tooltips for consistent formatting.\n *\n * @param value - The distance value to format\n * @returns The formatted string with 2 decimal places\n * @example\n * ```typescript\n * formatDistance(3.14159); // → \"3.14\"\n * ```\n */\nexport function formatDistance(value: number): string {\n return value.toFixed(2);\n}\n\n// =============================================================================\n// Tooltip Text Formatters\n// =============================================================================\n// These functions generate consistent tooltip text for both draw and edit modes.\n\n/**\n * Format circle tooltip text showing radius and area.\n *\n * @param radius - Circle radius in the specified units\n * @param area - Circle area in the specified units squared\n * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')\n * @returns Formatted tooltip text: \"r: {radius} {unit}\\n{area} {unit}²\"\n * @example\n * ```typescript\n * formatCircleTooltip(12.5, 490.87, 'km');\n * // → \"r: 12.50 km\\n490.87 km²\"\n * ```\n */\nexport function formatCircleTooltip(\n radius: number,\n area: number,\n unitAbbrev: string,\n): string {\n return `r: ${formatDistance(radius)} ${unitAbbrev}\\n${formatDistance(area)} ${unitAbbrev}²`;\n}\n\n/**\n * Format a \"{dim1} {unit} x {dim2} {unit}\\n{area} {unit}²\" tooltip string.\n *\n * @param dim1 - First dimension value\n * @param dim2 - Second dimension value\n * @param area - Area value\n * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')\n * @returns Formatted tooltip text\n */\nfunction formatDimensionsTooltip(\n dim1: number,\n dim2: number,\n area: number,\n unitAbbrev: string,\n): string {\n return `${formatDistance(dim1)} ${unitAbbrev} x ${formatDistance(dim2)} ${unitAbbrev}\\n${formatDistance(area)} ${unitAbbrev}²`;\n}\n\n/**\n * Format rectangle tooltip text showing dimensions and area.\n *\n * @param width - Rectangle width in the specified units\n * @param height - Rectangle height in the specified units\n * @param area - Rectangle area in the specified units squared\n * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')\n * @returns Formatted tooltip text: \"{width} {unit} x {height} {unit}\\n{area} {unit}²\"\n * @example\n * ```typescript\n * formatRectangleTooltip(5.0, 3.2, 16.0, 'km');\n * // → \"5.00 km x 3.20 km\\n16.00 km²\"\n * ```\n */\nexport function formatRectangleTooltip(\n width: number,\n height: number,\n area: number,\n unitAbbrev: string,\n): string {\n return formatDimensionsTooltip(width, height, area, unitAbbrev);\n}\n\n/**\n * Format ellipse tooltip text showing axes and area.\n *\n * @param majorAxis - Ellipse major axis (full length) in the specified units\n * @param minorAxis - Ellipse minor axis (full length) in the specified units\n * @param area - Ellipse area in the specified units squared\n * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')\n * @returns Formatted tooltip text: \"{major} {unit} x {minor} {unit}\\n{area} {unit}²\"\n * @example\n * ```typescript\n * formatEllipseTooltip(10.0, 6.0, 47.12, 'km');\n * // → \"10.00 km x 6.00 km\\n47.12 km²\"\n * ```\n */\nexport function formatEllipseTooltip(\n majorAxis: number,\n minorAxis: number,\n area: number,\n unitAbbrev: string,\n): string {\n return formatDimensionsTooltip(majorAxis, minorAxis, area, unitAbbrev);\n}\n\n/**\n * Format simple distance tooltip text.\n *\n * @param distance - Distance value in the specified units\n * @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')\n * @returns Formatted tooltip text: \"{distance} {unit}\"\n * @example\n * ```typescript\n * formatDistanceTooltip(42.5, 'km'); // → \"42.50 km\"\n * ```\n */\nexport function formatDistanceTooltip(\n distance: number,\n unitAbbrev: string,\n): string {\n return `${formatDistance(distance)} ${unitAbbrev}`;\n}\n\n// =============================================================================\n// Edit Event Type Classification\n// =============================================================================\n\n/**\n * Continuous edit event types that fire during dragging.\n * These are emitted repeatedly while the user drags during an edit operation.\n */\nexport const CONTINUOUS_EDIT_TYPES = new Set([\n 'movePosition',\n 'unionGeometry',\n 'scaling',\n 'rotating',\n 'translating',\n]);\n\n/**\n * Completion edit event types that fire when dragging ends.\n * These are emitted once when the user finishes an edit action.\n */\nexport const COMPLETION_EDIT_TYPES = new Set([\n 'finishMovePosition',\n 'addPosition',\n 'removePosition',\n 'scaled',\n 'rotated',\n 'translated',\n]);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,kBAAkB;CAC7B,SAAS;CACT,mBAAmB;CACnB,gBAAgB;CAChB,mBAAmB;CACpB;;;;;AAMD,MAAa,oBAAoB;;;;AAKjC,MAAa,qBAAqB;;;;AAKlC,MAAa,uBAAuB;;;;AAKpC,MAAa,2BAA2B;;;;;;;AAkBxC,MAAa,iBAAiB;CAE5B,MAAM;EAAC;EAAK;EAAK;EAAK;EAAI;CAE1B,MAAM;EAAC;EAAK;EAAK;EAAK;EAAI;CAE1B,WAAW;EAAC;EAAI;EAAK;EAAK;EAAI;CAC/B;;;;;;;;AASD,MAAa,2BAA2B;CAEtC,MAAM;EAAC;EAAK;EAAK;EAAK;EAAG;CAEzB,MAAM,eAAe;CACtB;;;;;;;AAQD,MAAaA,2BAA4C;CACvD,WAAW,eAAe;CAC1B,WAAW,eAAe;CAC1B,WAAW;CACX,aAAa;CACd;;;;AAKD,MAAa,cAAc;CAAC;CAAG;CAAG;CAAG;CAAE;;;;AAKvC,MAAa,gBAAgB;CAAC;CAAS;CAAU;CAAS;;;;AAK1D,MAAaC,cAA4D;CACvE,OAAO;CACP,QAAQ,CAAC,GAAG,EAAE;CACd,QAAQ,CAAC,GAAG,EAAE;CACf;;;;AAKD,MAAaC,4BAAmC;CAAC;CAAK;CAAK;CAAK;CAAI;;;;AAKpE,MAAaC,oCAA2C;CAAC;CAAG;CAAG;CAAG;CAAI;;;;AAKtE,MAAaC,2BAA8C,OAAO,OAAO;CACvE,MAAM;CACN,UAAU,OAAO,OAAO,EAAE,CAAC;CAC5B,CAAC;AAEF,MAAM,cACJ;;;;;;;;;AAUF,MAAaC,wBAAkC;CAC7C;CACA;CACA,GAAG,YAAY,MAAM,GAAG;CACzB;;;;;AAMD,MAAa,yBAAyB,EACpC,UAAU;CACR,GAAG;CACH,YAAY;CACZ,cAAc;CACd,eAAe;CACf,sBAAsB;CACtB,gBAAgB,CAAC,GAAG,EAAE;CACvB,EACF;;;;;AAMD,MAAa,6BAA6B;CACxC,wBAAwB;EACtB,cAAc;EACd,WAAW;EACZ;CACD,iBAAiB;EACf,cAAc;EACd,WAAW;EACZ;CACF;;;;;AAMD,MAAa,gCAAgC;CAC3C,GAAG;CACH,GAAG;CACJ;;;;;;;;;;;;AAaD,SAAgB,eAAe,OAAuB;AACpD,QAAO,MAAM,QAAQ,EAAE;;;;;;;;;;;;;;;AAqBzB,SAAgB,oBACd,QACA,MACA,YACQ;AACR,QAAO,MAAM,eAAe,OAAO,CAAC,GAAG,WAAW,IAAI,eAAe,KAAK,CAAC,GAAG,WAAW;;;;;;;;;;;AAY3F,SAAS,wBACP,MACA,MACA,MACA,YACQ;AACR,QAAO,GAAG,eAAe,KAAK,CAAC,GAAG,WAAW,KAAK,eAAe,KAAK,CAAC,GAAG,WAAW,IAAI,eAAe,KAAK,CAAC,GAAG,WAAW;;;;;;;;;;;;;;;;AAiB9H,SAAgB,uBACd,OACA,QACA,MACA,YACQ;AACR,QAAO,wBAAwB,OAAO,QAAQ,MAAM,WAAW;;;;;;;;;;;;;;;;AAiBjE,SAAgB,qBACd,WACA,WACA,MACA,YACQ;AACR,QAAO,wBAAwB,WAAW,WAAW,MAAM,WAAW;;;;;;;;;;;;;AAcxE,SAAgB,sBACd,UACA,YACQ;AACR,QAAO,GAAG,eAAe,SAAS,CAAC,GAAG;;;;;;AAWxC,MAAa,wBAAwB,IAAI,IAAI;CAC3C;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;AAMF,MAAa,wBAAwB,IAAI,IAAI;CAC3C;CACA;CACA;CACA;CACA;CACA;CACD,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -26,19 +26,23 @@ declare const ShapeEvents: {
26
26
  /** Shape hovered (for cursor changes) */
27
27
  readonly hovered: "shapes:hovered";
28
28
  };
29
+ /** Union of all shape event string literals */
29
30
  type ShapeEventType = (typeof ShapeEvents)[keyof typeof ShapeEvents];
30
31
  /**
31
32
  * Event payload types (all payloads are serializable)
32
33
  */
34
+ /** Event payload emitted when a shape is selected */
33
35
  type ShapeSelectedEvent = Payload<'shapes:selected', {
34
36
  shapeId: ShapeId;
35
37
  /** Map instance ID for multi-map event isolation */
36
38
  mapId: UniqueId;
37
39
  }>;
40
+ /** Event payload emitted when a shape selection is cleared */
38
41
  type ShapeDeselectedEvent = Payload<'shapes:deselected', {
39
42
  /** Map instance ID for multi-map event isolation */
40
43
  mapId: UniqueId;
41
44
  }>;
45
+ /** Event payload emitted when a shape is hovered or hover ends */
42
46
  type ShapeHoveredEvent = Payload<'shapes:hovered', {
43
47
  /** Shape ID being hovered, or null when hover ends */
44
48
  shapeId: ShapeId | null;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","names":[],"sources":["../../../../src/deckgl/shapes/shared/events.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport type { Payload } from '@accelint/bus';\nimport type { UniqueId } from '@accelint/core';\nimport type { ShapeId } from './types';\n\n/**\n * Shape interaction events for DisplayShapeLayer\n */\nexport const ShapeEvents = {\n /** Shape selected */\n selected: 'shapes:selected',\n /** Selection cleared */\n deselected: 'shapes:deselected',\n /** Shape hovered (for cursor changes) */\n hovered: 'shapes:hovered',\n} as const;\n\nexport type ShapeEventType = (typeof ShapeEvents)[keyof typeof ShapeEvents];\n\n/**\n * Event payload types (all payloads are serializable)\n */\n\nexport type ShapeSelectedEvent = Payload<\n 'shapes:selected',\n {\n shapeId: ShapeId;\n /** Map instance ID for multi-map event isolation */\n mapId: UniqueId;\n }\n>;\n\nexport type ShapeDeselectedEvent = Payload<\n 'shapes:deselected',\n {\n /** Map instance ID for multi-map event isolation */\n mapId: UniqueId;\n }\n>;\n\nexport type ShapeHoveredEvent = Payload<\n 'shapes:hovered',\n {\n /** Shape ID being hovered, or null when hover ends */\n shapeId: ShapeId | null;\n /** Map instance ID for multi-map event isolation */\n mapId: UniqueId;\n }\n>;\n\n/**\n * Union of all shape event types\n */\nexport type ShapeEvent =\n | ShapeSelectedEvent\n | ShapeDeselectedEvent\n | ShapeHoveredEvent;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqBA,MAAa,cAAc;CAEzB,UAAU;CAEV,YAAY;CAEZ,SAAS;CACV"}
1
+ {"version":3,"file":"events.js","names":[],"sources":["../../../../src/deckgl/shapes/shared/events.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport type { Payload } from '@accelint/bus';\nimport type { UniqueId } from '@accelint/core';\nimport type { ShapeId } from './types';\n\n/**\n * Shape interaction events for DisplayShapeLayer\n */\nexport const ShapeEvents = {\n /** Shape selected */\n selected: 'shapes:selected',\n /** Selection cleared */\n deselected: 'shapes:deselected',\n /** Shape hovered (for cursor changes) */\n hovered: 'shapes:hovered',\n} as const;\n\n/** Union of all shape event string literals */\nexport type ShapeEventType = (typeof ShapeEvents)[keyof typeof ShapeEvents];\n\n/**\n * Event payload types (all payloads are serializable)\n */\n\n/** Event payload emitted when a shape is selected */\nexport type ShapeSelectedEvent = Payload<\n 'shapes:selected',\n {\n shapeId: ShapeId;\n /** Map instance ID for multi-map event isolation */\n mapId: UniqueId;\n }\n>;\n\n/** Event payload emitted when a shape selection is cleared */\nexport type ShapeDeselectedEvent = Payload<\n 'shapes:deselected',\n {\n /** Map instance ID for multi-map event isolation */\n mapId: UniqueId;\n }\n>;\n\n/** Event payload emitted when a shape is hovered or hover ends */\nexport type ShapeHoveredEvent = Payload<\n 'shapes:hovered',\n {\n /** Shape ID being hovered, or null when hover ends */\n shapeId: ShapeId | null;\n /** Map instance ID for multi-map event isolation */\n mapId: UniqueId;\n }\n>;\n\n/**\n * Union of all shape event types\n */\nexport type ShapeEvent =\n | ShapeSelectedEvent\n | ShapeDeselectedEvent\n | ShapeHoveredEvent;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqBA,MAAa,cAAc;CAEzB,UAAU;CAEV,YAAY;CAEZ,SAAS;CACV"}
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
2
+ * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at https://www.apache.org/licenses/LICENSE-2.0
@@ -27,8 +27,8 @@ import { useEmit } from "@accelint/bus/react";
27
27
  * - Shift for rotation snap during edit
28
28
  * - Shift for square constraint during rectangle drawing
29
29
  *
30
- * @param mapId - The map instance ID
31
- * @param isActive - Whether the hook should be active (e.g., when editing/drawing)
30
+ * @param mapId - The map instance ID.
31
+ * @param isActive - Whether the hook should be active (e.g., when editing/drawing).
32
32
  *
33
33
  * @example
34
34
  * ```typescript
@@ -47,31 +47,34 @@ function useShiftZoomDisable(mapId, isActive) {
47
47
  const emitEnableZoom = useEmit(MapEvents.enableZoom);
48
48
  const isZoomDisabledRef = useRef(false);
49
49
  useEffect(() => {
50
- if (!isActive) return;
51
- const disableZoom = () => {
50
+ if (!isActive) {
51
+ isZoomDisabledRef.current = false;
52
+ return;
53
+ }
54
+ function disableZoom() {
52
55
  if (!isZoomDisabledRef.current) {
53
56
  isZoomDisabledRef.current = true;
54
57
  emitDisableZoom({ id: mapId });
55
58
  }
56
- };
57
- const enableZoom = () => {
59
+ }
60
+ function enableZoom() {
58
61
  if (isZoomDisabledRef.current) {
59
62
  isZoomDisabledRef.current = false;
60
63
  emitEnableZoom({ id: mapId });
61
64
  }
62
- };
63
- const handleKeyDown = (event) => {
65
+ }
66
+ function handleKeyDown(event) {
64
67
  if (event.key === "Shift") disableZoom();
65
- };
66
- const handleKeyUp = (event) => {
68
+ }
69
+ function handleKeyUp(event) {
67
70
  if (event.key === "Shift") enableZoom();
68
- };
69
- const handleMouseDown = (event) => {
71
+ }
72
+ function handleMouseDown(event) {
70
73
  if (event.shiftKey) disableZoom();
71
- };
72
- const handleBlur = () => {
74
+ }
75
+ function handleBlur() {
73
76
  enableZoom();
74
- };
77
+ }
75
78
  document.addEventListener("keydown", handleKeyDown);
76
79
  document.addEventListener("keyup", handleKeyUp);
77
80
  document.addEventListener("mousedown", handleMouseDown, { capture: true });
@@ -1 +1 @@
1
- {"version":3,"file":"use-shift-zoom-disable.js","names":[],"sources":["../../../../../src/deckgl/shapes/shared/hooks/use-shift-zoom-disable.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport { useEmit } from '@accelint/bus/react';\nimport { useEffect, useRef } from 'react';\nimport { MapEvents } from '@/deckgl/base-map/events';\nimport type { UniqueId } from '@accelint/core';\nimport type {\n MapDisableZoomEvent,\n MapEnableZoomEvent,\n} from '@/deckgl/base-map/types';\n\n/**\n * Hook to disable map zoom while Shift key is held during shape operations.\n *\n * This prevents MapLibre's boxZoom (Shift+drag) from interfering with\n * Shift modifier constraints like:\n * - Shift for uniform scaling during edit\n * - Shift for rotation snap during edit\n * - Shift for square constraint during rectangle drawing\n *\n * @param mapId - The map instance ID\n * @param isActive - Whether the hook should be active (e.g., when editing/drawing)\n *\n * @example\n * ```typescript\n * function EditShapeLayer({ mapId }) {\n * const isEditing = editStore.use(mapId).state?.editingShape != null;\n *\n * // Disable zoom when editing to allow Shift modifiers to work\n * useShiftZoomDisable(mapId, isEditing);\n *\n * // ... rest of component\n * }\n * ```\n */\nexport function useShiftZoomDisable(mapId: UniqueId, isActive: boolean): void {\n const emitDisableZoom = useEmit<MapDisableZoomEvent>(MapEvents.disableZoom);\n const emitEnableZoom = useEmit<MapEnableZoomEvent>(MapEvents.enableZoom);\n const isZoomDisabledRef = useRef(false);\n\n useEffect(() => {\n if (!isActive) {\n return;\n }\n\n const disableZoom = () => {\n if (!isZoomDisabledRef.current) {\n isZoomDisabledRef.current = true;\n emitDisableZoom({ id: mapId });\n }\n };\n\n const enableZoom = () => {\n if (isZoomDisabledRef.current) {\n isZoomDisabledRef.current = false;\n emitEnableZoom({ id: mapId });\n }\n };\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Shift') {\n disableZoom();\n }\n };\n\n const handleKeyUp = (event: KeyboardEvent) => {\n if (event.key === 'Shift') {\n enableZoom();\n }\n };\n\n // Also catch Shift state on mousedown to handle edge cases where\n // keydown might have been missed (e.g., focus issues)\n const handleMouseDown = (event: MouseEvent) => {\n if (event.shiftKey) {\n disableZoom();\n }\n };\n\n // Re-enable zoom if the window loses focus while Shift is held\n const handleBlur = () => {\n enableZoom();\n };\n\n document.addEventListener('keydown', handleKeyDown);\n document.addEventListener('keyup', handleKeyUp);\n document.addEventListener('mousedown', handleMouseDown, { capture: true });\n window.addEventListener('blur', handleBlur);\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n document.removeEventListener('keyup', handleKeyUp);\n document.removeEventListener('mousedown', handleMouseDown, {\n capture: true,\n });\n window.removeEventListener('blur', handleBlur);\n\n // Ensure zoom is re-enabled when unmounting\n enableZoom();\n };\n }, [isActive, mapId, emitDisableZoom, emitEnableZoom]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,SAAgB,oBAAoB,OAAiB,UAAyB;CAC5E,MAAM,kBAAkB,QAA6B,UAAU,YAAY;CAC3E,MAAM,iBAAiB,QAA4B,UAAU,WAAW;CACxE,MAAM,oBAAoB,OAAO,MAAM;AAEvC,iBAAgB;AACd,MAAI,CAAC,SACH;EAGF,MAAM,oBAAoB;AACxB,OAAI,CAAC,kBAAkB,SAAS;AAC9B,sBAAkB,UAAU;AAC5B,oBAAgB,EAAE,IAAI,OAAO,CAAC;;;EAIlC,MAAM,mBAAmB;AACvB,OAAI,kBAAkB,SAAS;AAC7B,sBAAkB,UAAU;AAC5B,mBAAe,EAAE,IAAI,OAAO,CAAC;;;EAIjC,MAAM,iBAAiB,UAAyB;AAC9C,OAAI,MAAM,QAAQ,QAChB,cAAa;;EAIjB,MAAM,eAAe,UAAyB;AAC5C,OAAI,MAAM,QAAQ,QAChB,aAAY;;EAMhB,MAAM,mBAAmB,UAAsB;AAC7C,OAAI,MAAM,SACR,cAAa;;EAKjB,MAAM,mBAAmB;AACvB,eAAY;;AAGd,WAAS,iBAAiB,WAAW,cAAc;AACnD,WAAS,iBAAiB,SAAS,YAAY;AAC/C,WAAS,iBAAiB,aAAa,iBAAiB,EAAE,SAAS,MAAM,CAAC;AAC1E,SAAO,iBAAiB,QAAQ,WAAW;AAE3C,eAAa;AACX,YAAS,oBAAoB,WAAW,cAAc;AACtD,YAAS,oBAAoB,SAAS,YAAY;AAClD,YAAS,oBAAoB,aAAa,iBAAiB,EACzD,SAAS,MACV,CAAC;AACF,UAAO,oBAAoB,QAAQ,WAAW;AAG9C,eAAY;;IAEb;EAAC;EAAU;EAAO;EAAiB;EAAe,CAAC"}
1
+ {"version":3,"file":"use-shift-zoom-disable.js","names":[],"sources":["../../../../../src/deckgl/shapes/shared/hooks/use-shift-zoom-disable.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport { useEmit } from '@accelint/bus/react';\nimport { useEffect, useRef } from 'react';\nimport { MapEvents } from '@/deckgl/base-map/events';\nimport type { UniqueId } from '@accelint/core';\nimport type {\n MapDisableZoomEvent,\n MapEnableZoomEvent,\n} from '@/deckgl/base-map/types';\n\n/**\n * Hook to disable map zoom while Shift key is held during shape operations.\n *\n * This prevents MapLibre's boxZoom (Shift+drag) from interfering with\n * Shift modifier constraints like:\n * - Shift for uniform scaling during edit\n * - Shift for rotation snap during edit\n * - Shift for square constraint during rectangle drawing\n *\n * @param mapId - The map instance ID.\n * @param isActive - Whether the hook should be active (e.g., when editing/drawing).\n *\n * @example\n * ```typescript\n * function EditShapeLayer({ mapId }) {\n * const isEditing = editStore.use(mapId).state?.editingShape != null;\n *\n * // Disable zoom when editing to allow Shift modifiers to work\n * useShiftZoomDisable(mapId, isEditing);\n *\n * // ... rest of component\n * }\n * ```\n */\nexport function useShiftZoomDisable(mapId: UniqueId, isActive: boolean): void {\n const emitDisableZoom = useEmit<MapDisableZoomEvent>(MapEvents.disableZoom);\n const emitEnableZoom = useEmit<MapEnableZoomEvent>(MapEvents.enableZoom);\n const isZoomDisabledRef = useRef(false);\n\n useEffect(() => {\n if (!isActive) {\n isZoomDisabledRef.current = false;\n\n return;\n }\n\n function disableZoom(): void {\n if (!isZoomDisabledRef.current) {\n isZoomDisabledRef.current = true;\n emitDisableZoom({ id: mapId });\n }\n }\n\n function enableZoom(): void {\n if (isZoomDisabledRef.current) {\n isZoomDisabledRef.current = false;\n emitEnableZoom({ id: mapId });\n }\n }\n\n function handleKeyDown(event: KeyboardEvent): void {\n if (event.key === 'Shift') {\n disableZoom();\n }\n }\n\n function handleKeyUp(event: KeyboardEvent): void {\n if (event.key === 'Shift') {\n enableZoom();\n }\n }\n\n // Also catch Shift state on mousedown to handle edge cases where\n // keydown might have been missed (e.g., focus issues)\n function handleMouseDown(event: MouseEvent): void {\n if (event.shiftKey) {\n disableZoom();\n }\n }\n\n // Re-enable zoom if the window loses focus while Shift is held\n function handleBlur(): void {\n enableZoom();\n }\n\n document.addEventListener('keydown', handleKeyDown);\n document.addEventListener('keyup', handleKeyUp);\n document.addEventListener('mousedown', handleMouseDown, { capture: true });\n window.addEventListener('blur', handleBlur);\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n document.removeEventListener('keyup', handleKeyUp);\n document.removeEventListener('mousedown', handleMouseDown, {\n capture: true,\n });\n window.removeEventListener('blur', handleBlur);\n\n // Ensure zoom is re-enabled when unmounting\n enableZoom();\n };\n }, [isActive, mapId, emitDisableZoom, emitEnableZoom]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,SAAgB,oBAAoB,OAAiB,UAAyB;CAC5E,MAAM,kBAAkB,QAA6B,UAAU,YAAY;CAC3E,MAAM,iBAAiB,QAA4B,UAAU,WAAW;CACxE,MAAM,oBAAoB,OAAO,MAAM;AAEvC,iBAAgB;AACd,MAAI,CAAC,UAAU;AACb,qBAAkB,UAAU;AAE5B;;EAGF,SAAS,cAAoB;AAC3B,OAAI,CAAC,kBAAkB,SAAS;AAC9B,sBAAkB,UAAU;AAC5B,oBAAgB,EAAE,IAAI,OAAO,CAAC;;;EAIlC,SAAS,aAAmB;AAC1B,OAAI,kBAAkB,SAAS;AAC7B,sBAAkB,UAAU;AAC5B,mBAAe,EAAE,IAAI,OAAO,CAAC;;;EAIjC,SAAS,cAAc,OAA4B;AACjD,OAAI,MAAM,QAAQ,QAChB,cAAa;;EAIjB,SAAS,YAAY,OAA4B;AAC/C,OAAI,MAAM,QAAQ,QAChB,aAAY;;EAMhB,SAAS,gBAAgB,OAAyB;AAChD,OAAI,MAAM,SACR,cAAa;;EAKjB,SAAS,aAAmB;AAC1B,eAAY;;AAGd,WAAS,iBAAiB,WAAW,cAAc;AACnD,WAAS,iBAAiB,SAAS,YAAY;AAC/C,WAAS,iBAAiB,aAAa,iBAAiB,EAAE,SAAS,MAAM,CAAC;AAC1E,SAAO,iBAAiB,QAAQ,WAAW;AAE3C,eAAa;AACX,YAAS,oBAAoB,WAAW,cAAc;AACtD,YAAS,oBAAoB,SAAS,YAAY;AAClD,YAAS,oBAAoB,aAAa,iBAAiB,EACzD,SAAS,MACV,CAAC;AACF,UAAO,oBAAoB,QAAQ,WAAW;AAG9C,eAAY;;IAEb;EAAC;EAAU;EAAO;EAAiB;EAAe,CAAC"}