@accelint/map-toolkit 1.5.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.
- package/CHANGELOG.md +33 -0
- package/catalog-info.yaml +4 -4
- package/dist/camera/events.js +1 -1
- package/dist/camera/index.d.ts +1 -1
- package/dist/camera/index.js +1 -1
- package/dist/camera/store.d.ts +1 -1
- package/dist/camera/store.js +3 -5
- package/dist/camera/store.js.map +1 -1
- package/dist/camera/types.d.ts +1 -1
- package/dist/camera/types.js +1 -1
- package/dist/cursor-coordinates/constants.js +1 -1
- package/dist/cursor-coordinates/index.d.ts +1 -1
- package/dist/cursor-coordinates/index.js +1 -1
- package/dist/cursor-coordinates/store.d.ts +1 -1
- package/dist/cursor-coordinates/store.js +1 -1
- package/dist/cursor-coordinates/types.d.ts +1 -1
- package/dist/cursor-coordinates/types.js +1 -1
- package/dist/cursor-coordinates/use-cursor-coordinates.d.ts +1 -1
- package/dist/cursor-coordinates/use-cursor-coordinates.js +4 -9
- package/dist/cursor-coordinates/use-cursor-coordinates.js.map +1 -1
- package/dist/deckgl/base-map/constants.js +1 -1
- package/dist/deckgl/base-map/controls.d.ts +1 -1
- package/dist/deckgl/base-map/controls.js +1 -1
- package/dist/deckgl/base-map/events.js +1 -1
- package/dist/deckgl/base-map/index.d.ts +1 -1
- package/dist/deckgl/base-map/index.js +1 -1
- package/dist/deckgl/base-map/provider.d.ts +3 -3
- package/dist/deckgl/base-map/provider.js +1 -1
- package/dist/deckgl/base-map/types.d.ts +1 -1
- package/dist/deckgl/base-map/types.js +1 -1
- package/dist/deckgl/index.d.ts +4 -4
- package/dist/deckgl/index.js +1 -1
- package/dist/deckgl/saved-viewports/index.d.ts +1 -1
- package/dist/deckgl/saved-viewports/index.js +1 -1
- package/dist/deckgl/saved-viewports/storage.d.ts +1 -1
- package/dist/deckgl/saved-viewports/storage.js +5 -10
- package/dist/deckgl/saved-viewports/storage.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/constants.js +66 -13
- package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/fiber.d.ts +1 -1
- package/dist/deckgl/shapes/display-shape-layer/fiber.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/index.d.ts +74 -35
- package/dist/deckgl/shapes/display-shape-layer/index.js +381 -154
- package/dist/deckgl/shapes/display-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/store.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/types.d.ts +108 -19
- package/dist/deckgl/shapes/display-shape-layer/types.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/use-select-shape.d.ts +1 -1
- package/dist/deckgl/shapes/display-shape-layer/use-select-shape.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +66 -36
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/elevation.js +407 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/elevation.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js +151 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/interaction.js +50 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/interaction.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +28 -39
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/constants.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/events.d.ts +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/events.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/fiber.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/index.js +4 -14
- package/dist/deckgl/shapes/draw-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +2 -3
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +2 -19
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +2 -32
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +9 -10
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/index.js +2 -32
- package/dist/deckgl/shapes/draw-shape-layer/modes/index.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/store.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/types.d.ts +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/types.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.d.ts +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js +3 -8
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/constants.js +17 -2
- package/dist/deckgl/shapes/edit-shape-layer/constants.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/events.d.ts +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/events.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/fiber.d.ts +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/fiber.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +5 -2
- package/dist/deckgl/shapes/edit-shape-layer/index.js +51 -20
- package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js +4 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/index.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/point-translate-mode.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/point-translate-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/store.js +70 -12
- package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/types.d.ts +14 -2
- package/dist/deckgl/shapes/edit-shape-layer/types.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.d.ts +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js +1 -1
- package/dist/deckgl/shapes/index.d.ts +4 -4
- package/dist/deckgl/shapes/index.js +1 -1
- package/dist/deckgl/shapes/shared/constants.d.ts +4 -3
- package/dist/deckgl/shapes/shared/constants.js +50 -10
- package/dist/deckgl/shapes/shared/constants.js.map +1 -1
- package/dist/deckgl/shapes/shared/events.d.ts +5 -1
- package/dist/deckgl/shapes/shared/events.js +1 -1
- package/dist/deckgl/shapes/shared/events.js.map +1 -1
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js +19 -16
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js.map +1 -1
- package/dist/deckgl/shapes/shared/types.d.ts +174 -53
- package/dist/deckgl/shapes/shared/types.js +155 -2
- package/dist/deckgl/shapes/shared/types.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js +29 -24
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/layer-config.js +8 -5
- package/dist/deckgl/shapes/shared/utils/layer-config.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/mode-utils.js +50 -20
- package/dist/deckgl/shapes/shared/utils/mode-utils.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js +22 -15
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/style-utils.d.ts +38 -14
- package/dist/deckgl/shapes/shared/utils/style-utils.js +43 -32
- package/dist/deckgl/shapes/shared/utils/style-utils.js.map +1 -1
- package/dist/deckgl/symbol-layer/fiber.d.ts +1 -1
- package/dist/deckgl/symbol-layer/fiber.js +1 -1
- package/dist/deckgl/symbol-layer/index.d.ts +1 -1
- package/dist/deckgl/symbol-layer/index.js +1 -1
- package/dist/deckgl/text-layer/character-sets.js +1 -1
- package/dist/deckgl/text-layer/default-settings.d.ts +1 -1
- package/dist/deckgl/text-layer/default-settings.js +1 -1
- package/dist/deckgl/text-layer/fiber.d.ts +1 -1
- package/dist/deckgl/text-layer/fiber.js +1 -1
- package/dist/deckgl/text-layer/index.d.ts +1 -1
- package/dist/deckgl/text-layer/index.js +1 -1
- package/dist/deckgl/text-settings.d.ts +3 -3
- package/dist/deckgl/text-settings.js +1 -1
- package/dist/map-cursor/events.js +1 -1
- package/dist/map-cursor/index.d.ts +1 -1
- package/dist/map-cursor/index.js +1 -1
- package/dist/map-cursor/store.d.ts +1 -1
- package/dist/map-cursor/store.js +1 -1
- package/dist/map-cursor/types.d.ts +1 -1
- package/dist/map-cursor/types.js +1 -1
- package/dist/map-cursor/use-map-cursor.d.ts +1 -1
- package/dist/map-cursor/use-map-cursor.js +1 -1
- package/dist/map-mode/events.js +1 -1
- package/dist/map-mode/index.d.ts +1 -1
- package/dist/map-mode/index.js +1 -1
- package/dist/map-mode/store.d.ts +1 -1
- package/dist/map-mode/store.js +3 -8
- package/dist/map-mode/store.js.map +1 -1
- package/dist/map-mode/types.d.ts +1 -1
- package/dist/map-mode/types.js +1 -1
- package/dist/map-mode/use-map-mode.d.ts +1 -1
- package/dist/map-mode/use-map-mode.js +1 -1
- package/dist/maplibre/hooks/use-maplibre.d.ts +1 -1
- package/dist/maplibre/hooks/use-maplibre.js +1 -1
- package/dist/maplibre/index.d.ts +1 -1
- package/dist/maplibre/index.js +1 -1
- package/dist/shared/cleanup.d.ts +1 -1
- package/dist/shared/cleanup.js +1 -1
- package/dist/shared/constants.js +1 -1
- package/dist/shared/create-map-store.d.ts +1 -1
- package/dist/shared/create-map-store.js +1 -1
- package/dist/shared/logger.js +31 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/units.js +1 -1
- package/dist/viewport/index.d.ts +1 -1
- package/dist/viewport/index.js +1 -1
- package/dist/viewport/store.d.ts +1 -1
- package/dist/viewport/store.js +1 -1
- package/dist/viewport/types.d.ts +1 -1
- package/dist/viewport/types.js +1 -1
- package/dist/viewport/utils.d.ts +1 -1
- package/dist/viewport/utils.js +1 -1
- package/dist/viewport/viewport-size.d.ts +3 -3
- package/dist/viewport/viewport-size.js +1 -1
- package/package.json +22 -20
- package/dist/hotkey-manager/dist/react/use-hotkey.js +0 -39
- package/dist/hotkey-manager/dist/react/use-hotkey.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vertex-transform-mode.js","names":["guidesToFilterOut: string[]"],"sources":["../../../../../src/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.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\nimport {\n type FeatureCollection,\n type GeoJsonEditMode,\n type GuideFeatureCollection,\n type ModeProps,\n ModifyMode,\n TranslateMode,\n} from '@deck.gl-community/editable-layers';\nimport { featureCollection } from '@turf/helpers';\nimport { BaseTransformMode, type HandleMatcher } from './base-transform-mode';\nimport { RotateModeWithSnap } from './rotate-mode-with-snap';\nimport { ScaleModeWithFreeTransform } from './scale-mode-with-free-transform';\n\n/**\n * Transform mode for shapes that support vertex editing (polygons and lines).\n *\n * Use this mode for shapes where individual vertices can be dragged to reshape\n * the geometry. This provides the most flexibility for freeform shape editing.\n *\n * ## Capabilities\n * This composite mode provides:\n * - **Vertex editing** (ModifyMode): Drag vertices to reshape the geometry\n * - **Translation** (TranslateMode): Drag the shape to move it\n * - **Scaling** (ScaleModeWithFreeTransform): Drag corner handles to resize\n * - Default: Non-uniform scaling (can stretch/squish)\n * - With Shift: Uniform scaling (maintains aspect ratio)\n * - **Rotation** (RotateModeWithSnap): Drag top handle to rotate\n * - Default: Free rotation\n * - With Shift: Snap to 45° intervals\n *\n * ## Handle Priority Logic\n * When drag starts, modes are evaluated in this priority order:\n * 1. If hovering over a vertex (edit handle) → vertex editing takes priority\n * 2. If hovering over a scale handle → scaling takes priority\n * 3. If hovering over the rotate handle → rotation takes priority\n * 4. Otherwise → dragging the shape translates it\n *\n * ## Visual Behavior\n * The guides from all modes are combined, showing both vertex handles (white circles\n * on existing points) and transform handles (corner/rotation handles on bounding box).\n *\n * ## Tooltips\n * This mode does not show live measurement tooltips during editing because arbitrary\n * polygons don't have well-defined dimensions. Use BoundingTransformMode for shapes\n * like rectangles and ellipses where dimension tooltips are useful.\n *\n * ## Rectangle Special Handling\n * For rectangles, vertex handles are hidden to preserve rotation and right angles.\n * Only scale/rotate/translate handles are shown. Consider using BoundingTransformMode\n * directly for rectangles if vertex editing should never be available.\n *\n * @example\n * ```typescript\n * import { VertexTransformMode } from '@accelint/map-toolkit/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode';\n * import { EditableGeoJsonLayer } from '@deck.gl-community/editable-layers';\n *\n * // Used internally by EditShapeLayer for polygons and lines\n * const mode = new VertexTransformMode();\n *\n * const layer = new EditableGeoJsonLayer({\n * mode,\n * data: polygonFeatureCollection,\n * selectedFeatureIndexes: [0],\n * onEdit: handleEdit,\n * // ... other props\n * });\n * ```\n */\nexport class VertexTransformMode extends BaseTransformMode {\n private modifyMode: ModifyMode;\n private translateMode: TranslateMode;\n private scaleMode: ScaleModeWithFreeTransform;\n private rotateMode: RotateModeWithSnap;\n\n constructor() {\n const modifyMode = new ModifyMode();\n const translateMode = new TranslateMode();\n const scaleMode = new ScaleModeWithFreeTransform();\n const rotateMode = new RotateModeWithSnap();\n\n // Order matters: first mode to handle the event wins\n // We put modify first so vertex handles take priority over translate\n super([modifyMode, scaleMode, rotateMode, translateMode]);\n\n this.modifyMode = modifyMode;\n this.translateMode = translateMode;\n this.scaleMode = scaleMode;\n this.rotateMode = rotateMode;\n }\n\n protected override getHandleMatchers(): HandleMatcher[] {\n return [\n {\n // Vertex handle: existing point on polygon/line\n match: (pick) =>\n Boolean(\n pick.isGuide &&\n pick.object?.properties?.guideType === 'editHandle' &&\n pick.object?.properties?.editHandleType === 'existing',\n ),\n mode: this.modifyMode,\n // No shift config - vertex editing doesn't have modifiers\n },\n {\n // Scale handle: corner handles on bounding box\n match: (pick) =>\n Boolean(\n pick.isGuide && pick.object?.properties?.editHandleType === 'scale',\n ),\n mode: this.scaleMode,\n shiftConfig: { configKey: 'lockScaling' },\n },\n {\n // Rotate handle: top handle on bounding box\n match: (pick) =>\n Boolean(\n pick.isGuide &&\n pick.object?.properties?.editHandleType === 'rotate',\n ),\n mode: this.rotateMode,\n shiftConfig: { configKey: 'snapRotation' },\n },\n ];\n }\n\n protected override getDefaultMode(): GeoJsonEditMode {\n return this.translateMode;\n }\n\n /**\n * Override getGuides to filter duplicate envelope guides and handle rectangles.\n *\n * Both ScaleMode and RotateMode render the same bounding box envelope.\n * We keep scale's envelope and filter rotate's duplicate.\n * We also hide scale handles while rotating to avoid visual clutter.\n *\n * For rectangles, we hide vertex handles because vertex editing would distort\n * the shape or force axis-alignment. Rectangles should use scale handles only.\n */\n override getGuides(\n props: ModeProps<FeatureCollection>,\n ): GuideFeatureCollection {\n // Get guides from all modes (base class handles pick filtering)\n const allGuides = super.getGuides(props);\n\n // Check if we're editing a rectangle - rectangles have shape: 'Rectangle' property\n const isRectangle =\n props.data.features[0]?.properties?.shape === 'Rectangle';\n\n // biome-ignore lint/suspicious/noExplicitAny: Guide properties vary by mode, safely accessing with optional chaining\n const filteredGuides = allGuides.features.filter((guide: any) => {\n const properties = guide.properties || {};\n const editHandleType = properties.editHandleType;\n const guideType = properties.guideType;\n const mode = properties.mode;\n\n // Both scale and rotate modes have the same enveloping box as a guide - only need one\n const guidesToFilterOut: string[] = [mode as string];\n\n // Do not render scaling edit handles if rotating\n if (this.rotateMode.getIsRotating()) {\n guidesToFilterOut.push(editHandleType as string);\n }\n\n // For rectangles, hide ModifyMode vertex handles (editHandleType: 'existing')\n // Rectangles should only use scale handles for resizing to preserve rotation\n // Vertex editing would either distort the shape or force axis-alignment\n if (\n isRectangle &&\n guideType === 'editHandle' &&\n editHandleType === 'existing'\n ) {\n return false;\n }\n\n return !guidesToFilterOut.includes('scale');\n });\n\n // biome-ignore lint/suspicious/noExplicitAny: turf types mismatch with editable-layers GeoJSON types\n return featureCollection(filteredGuides as any) as any;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgFA,IAAa,sBAAb,cAAyC,kBAAkB;CACzD,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,cAAc;EACZ,MAAM,aAAa,IAAI,YAAY;EACnC,MAAM,gBAAgB,IAAI,eAAe;EACzC,MAAM,YAAY,IAAI,4BAA4B;EAClD,MAAM,aAAa,IAAI,oBAAoB;AAI3C,QAAM;GAAC;GAAY;GAAW;GAAY;GAAc,CAAC;AAEzD,OAAK,aAAa;AAClB,OAAK,gBAAgB;AACrB,OAAK,YAAY;AACjB,OAAK,aAAa;;CAGpB,AAAmB,oBAAqC;AACtD,SAAO;GACL;IAEE,QAAQ,SACN,QACE,KAAK,WACH,KAAK,QAAQ,YAAY,cAAc,gBACvC,KAAK,QAAQ,YAAY,mBAAmB,WAC/C;IACH,MAAM,KAAK;IAEZ;GACD;IAEE,QAAQ,SACN,QACE,KAAK,WAAW,KAAK,QAAQ,YAAY,mBAAmB,QAC7D;IACH,MAAM,KAAK;IACX,aAAa,EAAE,WAAW,eAAe;IAC1C;GACD;IAEE,QAAQ,SACN,QACE,KAAK,WACH,KAAK,QAAQ,YAAY,mBAAmB,SAC/C;IACH,MAAM,KAAK;IACX,aAAa,EAAE,WAAW,gBAAgB;IAC3C;GACF;;CAGH,AAAmB,iBAAkC;
|
|
1
|
+
{"version":3,"file":"vertex-transform-mode.js","names":["guidesToFilterOut: string[]"],"sources":["../../../../../src/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.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\nimport {\n type FeatureCollection,\n type GeoJsonEditMode,\n type GuideFeatureCollection,\n type ModeProps,\n ModifyMode,\n TranslateMode,\n} from '@deck.gl-community/editable-layers';\nimport { featureCollection } from '@turf/helpers';\nimport { BaseTransformMode, type HandleMatcher } from './base-transform-mode';\nimport { RotateModeWithSnap } from './rotate-mode-with-snap';\nimport { ScaleModeWithFreeTransform } from './scale-mode-with-free-transform';\n\n/**\n * Transform mode for shapes that support vertex editing (polygons and lines).\n *\n * Use this mode for shapes where individual vertices can be dragged to reshape\n * the geometry. This provides the most flexibility for freeform shape editing.\n *\n * ## Capabilities\n * This composite mode provides:\n * - **Vertex editing** (ModifyMode): Drag vertices to reshape the geometry\n * - **Translation** (TranslateMode): Drag the shape to move it\n * - **Scaling** (ScaleModeWithFreeTransform): Drag corner handles to resize\n * - Default: Non-uniform scaling (can stretch/squish)\n * - With Shift: Uniform scaling (maintains aspect ratio)\n * - **Rotation** (RotateModeWithSnap): Drag top handle to rotate\n * - Default: Free rotation\n * - With Shift: Snap to 45° intervals\n *\n * ## Handle Priority Logic\n * When drag starts, modes are evaluated in this priority order:\n * 1. If hovering over a vertex (edit handle) → vertex editing takes priority\n * 2. If hovering over a scale handle → scaling takes priority\n * 3. If hovering over the rotate handle → rotation takes priority\n * 4. Otherwise → dragging the shape translates it\n *\n * ## Visual Behavior\n * The guides from all modes are combined, showing both vertex handles (white circles\n * on existing points) and transform handles (corner/rotation handles on bounding box).\n *\n * ## Tooltips\n * This mode does not show live measurement tooltips during editing because arbitrary\n * polygons don't have well-defined dimensions. Use BoundingTransformMode for shapes\n * like rectangles and ellipses where dimension tooltips are useful.\n *\n * ## Rectangle Special Handling\n * For rectangles, vertex handles are hidden to preserve rotation and right angles.\n * Only scale/rotate/translate handles are shown. Consider using BoundingTransformMode\n * directly for rectangles if vertex editing should never be available.\n *\n * @example\n * ```typescript\n * import { VertexTransformMode } from '@accelint/map-toolkit/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode';\n * import { EditableGeoJsonLayer } from '@deck.gl-community/editable-layers';\n *\n * // Used internally by EditShapeLayer for polygons and lines\n * const mode = new VertexTransformMode();\n *\n * const layer = new EditableGeoJsonLayer({\n * mode,\n * data: polygonFeatureCollection,\n * selectedFeatureIndexes: [0],\n * onEdit: handleEdit,\n * // ... other props\n * });\n * ```\n */\nexport class VertexTransformMode extends BaseTransformMode {\n private modifyMode: ModifyMode;\n private translateMode: TranslateMode;\n private scaleMode: ScaleModeWithFreeTransform;\n private rotateMode: RotateModeWithSnap;\n\n constructor() {\n const modifyMode = new ModifyMode();\n const translateMode = new TranslateMode();\n const scaleMode = new ScaleModeWithFreeTransform();\n const rotateMode = new RotateModeWithSnap();\n\n // Order matters: first mode to handle the event wins\n // We put modify first so vertex handles take priority over translate\n super([modifyMode, scaleMode, rotateMode, translateMode]);\n\n this.modifyMode = modifyMode;\n this.translateMode = translateMode;\n this.scaleMode = scaleMode;\n this.rotateMode = rotateMode;\n }\n\n protected override getHandleMatchers(): HandleMatcher[] {\n return [\n {\n // Vertex handle: existing point on polygon/line\n match: (pick) =>\n Boolean(\n pick.isGuide &&\n pick.object?.properties?.guideType === 'editHandle' &&\n pick.object?.properties?.editHandleType === 'existing',\n ),\n mode: this.modifyMode,\n // No shift config - vertex editing doesn't have modifiers\n },\n {\n // Scale handle: corner handles on bounding box\n match: (pick) =>\n Boolean(\n pick.isGuide && pick.object?.properties?.editHandleType === 'scale',\n ),\n mode: this.scaleMode,\n shiftConfig: { configKey: 'lockScaling' },\n },\n {\n // Rotate handle: top handle on bounding box\n match: (pick) =>\n Boolean(\n pick.isGuide &&\n pick.object?.properties?.editHandleType === 'rotate',\n ),\n mode: this.rotateMode,\n shiftConfig: { configKey: 'snapRotation' },\n },\n ];\n }\n\n protected override getDefaultMode(): GeoJsonEditMode {\n // biome-ignore lint/suspicious/noExplicitAny: Library type inconsistency — see HandleMatcher JSDoc in base-transform-mode\n return this.translateMode as any;\n }\n\n /**\n * Override getGuides to filter duplicate envelope guides and handle rectangles.\n *\n * Both ScaleMode and RotateMode render the same bounding box envelope.\n * We keep scale's envelope and filter rotate's duplicate.\n * We also hide scale handles while rotating to avoid visual clutter.\n *\n * For rectangles, we hide vertex handles because vertex editing would distort\n * the shape or force axis-alignment. Rectangles should use scale handles only.\n */\n override getGuides(\n props: ModeProps<FeatureCollection>,\n ): GuideFeatureCollection {\n // Get guides from all modes (base class handles pick filtering)\n const allGuides = super.getGuides(props);\n\n // Check if we're editing a rectangle - rectangles have shape: 'Rectangle' property\n const isRectangle =\n props.data.features[0]?.properties?.shape === 'Rectangle';\n\n // biome-ignore lint/suspicious/noExplicitAny: Guide properties vary by mode, safely accessing with optional chaining\n const filteredGuides = allGuides.features.filter((guide: any) => {\n const properties = guide.properties || {};\n const editHandleType = properties.editHandleType;\n const guideType = properties.guideType;\n const mode = properties.mode;\n\n // Both scale and rotate modes have the same enveloping box as a guide - only need one\n const guidesToFilterOut: string[] = [mode as string];\n\n // Do not render scaling edit handles if rotating\n if (this.rotateMode.getIsRotating()) {\n guidesToFilterOut.push(editHandleType as string);\n }\n\n // For rectangles, hide ModifyMode vertex handles (editHandleType: 'existing')\n // Rectangles should only use scale handles for resizing to preserve rotation\n // Vertex editing would either distort the shape or force axis-alignment\n if (\n isRectangle &&\n guideType === 'editHandle' &&\n editHandleType === 'existing'\n ) {\n return false;\n }\n\n return !guidesToFilterOut.includes('scale');\n });\n\n // biome-ignore lint/suspicious/noExplicitAny: turf types mismatch with editable-layers GeoJSON types\n return featureCollection(filteredGuides as any) as any;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgFA,IAAa,sBAAb,cAAyC,kBAAkB;CACzD,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,cAAc;EACZ,MAAM,aAAa,IAAI,YAAY;EACnC,MAAM,gBAAgB,IAAI,eAAe;EACzC,MAAM,YAAY,IAAI,4BAA4B;EAClD,MAAM,aAAa,IAAI,oBAAoB;AAI3C,QAAM;GAAC;GAAY;GAAW;GAAY;GAAc,CAAC;AAEzD,OAAK,aAAa;AAClB,OAAK,gBAAgB;AACrB,OAAK,YAAY;AACjB,OAAK,aAAa;;CAGpB,AAAmB,oBAAqC;AACtD,SAAO;GACL;IAEE,QAAQ,SACN,QACE,KAAK,WACH,KAAK,QAAQ,YAAY,cAAc,gBACvC,KAAK,QAAQ,YAAY,mBAAmB,WAC/C;IACH,MAAM,KAAK;IAEZ;GACD;IAEE,QAAQ,SACN,QACE,KAAK,WAAW,KAAK,QAAQ,YAAY,mBAAmB,QAC7D;IACH,MAAM,KAAK;IACX,aAAa,EAAE,WAAW,eAAe;IAC1C;GACD;IAEE,QAAQ,SACN,QACE,KAAK,WACH,KAAK,QAAQ,YAAY,mBAAmB,SAC/C;IACH,MAAM,KAAK;IACX,aAAa,EAAE,WAAW,gBAAgB;IAC3C;GACF;;CAGH,AAAmB,iBAAkC;AAEnD,SAAO,KAAK;;;;;;;;;;;;CAad,AAAS,UACP,OACwB;EAExB,MAAM,YAAY,MAAM,UAAU,MAAM;EAGxC,MAAM,cACJ,MAAM,KAAK,SAAS,IAAI,YAAY,UAAU;AAgChD,SAAO,kBA7BgB,UAAU,SAAS,QAAQ,UAAe;GAC/D,MAAM,aAAa,MAAM,cAAc,EAAE;GACzC,MAAM,iBAAiB,WAAW;GAClC,MAAM,YAAY,WAAW;GAI7B,MAAMA,oBAA8B,CAHvB,WAAW,KAG4B;AAGpD,OAAI,KAAK,WAAW,eAAe,CACjC,mBAAkB,KAAK,eAAyB;AAMlD,OACE,eACA,cAAc,gBACd,mBAAmB,WAEnB,QAAO;AAGT,UAAO,CAAC,kBAAkB,SAAS,QAAQ;IAC3C,CAG6C"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
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
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
|
|
16
16
|
import { createMapStore } from "../../../shared/create-map-store.js";
|
|
17
17
|
import { MapEvents } from "../../base-map/events.js";
|
|
18
|
+
import { createLoggerDomain } from "../../../shared/logger.js";
|
|
18
19
|
import { releaseModeAndCursor, requestCursorChange, requestModeChange } from "../shared/utils/mode-utils.js";
|
|
19
20
|
import { isCircleShape, isEllipseShape, isPointShape, isRectangleShape } from "../shared/types.js";
|
|
20
21
|
import { EDIT_CURSOR_MAP, EDIT_SHAPE_LAYER_ID, EDIT_SHAPE_MODE } from "./constants.js";
|
|
21
22
|
import { EditShapeEvents } from "./events.js";
|
|
22
23
|
import { Broadcast } from "@accelint/bus";
|
|
23
|
-
import { getLogger } from "@accelint/logger";
|
|
24
24
|
|
|
25
25
|
//#region src/deckgl/shapes/edit-shape-layer/store.ts
|
|
26
26
|
/**
|
|
@@ -45,12 +45,7 @@ import { getLogger } from "@accelint/logger";
|
|
|
45
45
|
* }
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
|
-
const logger =
|
|
49
|
-
enabled: process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "test",
|
|
50
|
-
level: "warn",
|
|
51
|
-
prefix: "[EditShapeLayer]",
|
|
52
|
-
pretty: true
|
|
53
|
-
});
|
|
48
|
+
const logger = createLoggerDomain("[EditShapeLayer]");
|
|
54
49
|
/**
|
|
55
50
|
* Typed event bus instances
|
|
56
51
|
*/
|
|
@@ -62,7 +57,8 @@ const mapEventBus = Broadcast.getInstance();
|
|
|
62
57
|
const DEFAULT_EDITING_STATE = {
|
|
63
58
|
editingShape: null,
|
|
64
59
|
editMode: "view",
|
|
65
|
-
featureBeingEdited: null
|
|
60
|
+
featureBeingEdited: null,
|
|
61
|
+
previousMode: null
|
|
66
62
|
};
|
|
67
63
|
/**
|
|
68
64
|
* Determine the appropriate edit mode for a shape type
|
|
@@ -122,7 +118,8 @@ function saveEditingInternal(mapId, state, notify, setState) {
|
|
|
122
118
|
setState({
|
|
123
119
|
editingShape: null,
|
|
124
120
|
editMode: "view",
|
|
125
|
-
featureBeingEdited: null
|
|
121
|
+
featureBeingEdited: null,
|
|
122
|
+
previousMode: null
|
|
126
123
|
});
|
|
127
124
|
releaseModeAndCursor(mapId, EDIT_SHAPE_LAYER_ID);
|
|
128
125
|
mapEventBus.emit(MapEvents.enablePan, { id: mapId });
|
|
@@ -142,7 +139,8 @@ function cancelEditingInternal(mapId, state, notify, setState) {
|
|
|
142
139
|
setState({
|
|
143
140
|
editingShape: null,
|
|
144
141
|
editMode: "view",
|
|
145
|
-
featureBeingEdited: null
|
|
142
|
+
featureBeingEdited: null,
|
|
143
|
+
previousMode: null
|
|
146
144
|
});
|
|
147
145
|
releaseModeAndCursor(mapId, EDIT_SHAPE_LAYER_ID);
|
|
148
146
|
mapEventBus.emit(MapEvents.enablePan, { id: mapId });
|
|
@@ -181,29 +179,89 @@ const editStore = createMapStore({
|
|
|
181
179
|
});
|
|
182
180
|
/**
|
|
183
181
|
* Manually clear editing state for a specific mapId.
|
|
182
|
+
* @param mapId - The map instance ID.
|
|
184
183
|
*/
|
|
185
184
|
function clearEditingState(mapId) {
|
|
186
185
|
editStore.clear(mapId);
|
|
187
186
|
}
|
|
188
187
|
/**
|
|
189
188
|
* Update feature from the layer component (called during drag operations)
|
|
189
|
+
* @param mapId - The map instance ID.
|
|
190
|
+
* @param feature - The updated GeoJSON feature from the editable layer.
|
|
190
191
|
*/
|
|
191
192
|
function updateFeatureFromLayer(mapId, feature) {
|
|
192
193
|
editStore.set(mapId, { featureBeingEdited: feature });
|
|
193
194
|
}
|
|
194
195
|
/**
|
|
195
196
|
* Cancel editing (called by the layer component on ESC)
|
|
197
|
+
* @param mapId - The map instance ID.
|
|
196
198
|
*/
|
|
197
199
|
function cancelEditingFromLayer(mapId) {
|
|
198
200
|
editStore.actions(mapId).cancel();
|
|
199
201
|
}
|
|
200
202
|
/**
|
|
201
203
|
* Save editing (called by the layer component on Enter)
|
|
204
|
+
* @param mapId - The map instance ID.
|
|
202
205
|
*/
|
|
203
206
|
function saveEditingFromLayer(mapId) {
|
|
204
207
|
editStore.actions(mapId).save();
|
|
205
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Enables map panning while editing by switching to view mode and storing the
|
|
211
|
+
* current edit mode for restoration.
|
|
212
|
+
*
|
|
213
|
+
* No-op if no shape is currently being edited.
|
|
214
|
+
*
|
|
215
|
+
* @param mapId - The map instance ID.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* enableEditPanning(mapId);
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
function enableEditPanning(mapId) {
|
|
223
|
+
const state = editStore.get(mapId);
|
|
224
|
+
if (state?.previousMode !== null || !state?.editingShape) return;
|
|
225
|
+
editStore.set(mapId, {
|
|
226
|
+
previousMode: state.editMode,
|
|
227
|
+
editMode: "view"
|
|
228
|
+
});
|
|
229
|
+
mapEventBus.emit(MapEvents.enablePan, { id: mapId });
|
|
230
|
+
requestCursorChange(mapId, "grab", EDIT_SHAPE_LAYER_ID);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Disables map panning and restores the previous edit mode.
|
|
234
|
+
*
|
|
235
|
+
* If no shape is being edited, clears the stored `previousMode` and returns.
|
|
236
|
+
* Otherwise, restores the edit mode from `previousMode`, re-disables panning,
|
|
237
|
+
* and sets the cursor back to the shape-appropriate edit cursor.
|
|
238
|
+
*
|
|
239
|
+
* @param mapId - The map instance ID.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* disableEditPanning(mapId);
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
function disableEditPanning(mapId) {
|
|
247
|
+
const state = editStore.get(mapId);
|
|
248
|
+
if (!state?.editingShape) {
|
|
249
|
+
editStore.set(mapId, { previousMode: null });
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const previousMode = state?.previousMode;
|
|
253
|
+
if (!previousMode) {
|
|
254
|
+
editStore.set(mapId, { previousMode: null });
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
editStore.set(mapId, {
|
|
258
|
+
editMode: previousMode,
|
|
259
|
+
previousMode: null
|
|
260
|
+
});
|
|
261
|
+
mapEventBus.emit(MapEvents.disablePan, { id: mapId });
|
|
262
|
+
requestCursorChange(mapId, EDIT_CURSOR_MAP[previousMode], EDIT_SHAPE_LAYER_ID);
|
|
263
|
+
}
|
|
206
264
|
|
|
207
265
|
//#endregion
|
|
208
|
-
export { cancelEditingFromLayer, clearEditingState, editStore, saveEditingFromLayer, updateFeatureFromLayer };
|
|
266
|
+
export { cancelEditingFromLayer, clearEditingState, disableEditPanning, editStore, enableEditPanning, saveEditingFromLayer, updateFeatureFromLayer };
|
|
209
267
|
//# sourceMappingURL=store.js.map
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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<
|
|
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
|
|
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:
|
|
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
|
-
|
|
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,6 +200,10 @@ 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);
|
|
@@ -206,11 +215,28 @@ function formatDistance(value) {
|
|
|
206
215
|
* @param area - Circle area in the specified units squared
|
|
207
216
|
* @param unitAbbrev - Unit abbreviation (e.g., 'km', 'mi')
|
|
208
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
|
+
* ```
|
|
209
223
|
*/
|
|
210
224
|
function formatCircleTooltip(radius, area, unitAbbrev) {
|
|
211
225
|
return `r: ${formatDistance(radius)} ${unitAbbrev}\n${formatDistance(area)} ${unitAbbrev}²`;
|
|
212
226
|
}
|
|
213
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
|
|
235
|
+
*/
|
|
236
|
+
function formatDimensionsTooltip(dim1, dim2, area, unitAbbrev) {
|
|
237
|
+
return `${formatDistance(dim1)} ${unitAbbrev} x ${formatDistance(dim2)} ${unitAbbrev}\n${formatDistance(area)} ${unitAbbrev}²`;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
214
240
|
* Format rectangle tooltip text showing dimensions and area.
|
|
215
241
|
*
|
|
216
242
|
* @param width - Rectangle width in the specified units
|
|
@@ -218,9 +244,14 @@ function formatCircleTooltip(radius, 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
|
|
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
|
|
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 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 */\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 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,QACA,MACA,YACQ;AACR,QAAO,MAAM,eAAe,OAAO,CAAC,GAAG,WAAW,IAAI,eAAe,KAAK,CAAC,GAAG,WAAW;;;;;;;;;;;AAY3F,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"}
|