@accelint/map-toolkit 1.4.0 → 1.5.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 +12 -0
- package/catalog-info.yaml +3 -3
- package/dist/camera/store.js +1 -1
- package/dist/camera/store.js.map +1 -1
- package/dist/cursor-coordinates/use-cursor-coordinates.js.map +1 -1
- package/dist/deckgl/base-map/index.d.ts +2 -2
- package/dist/deckgl/base-map/index.js +14 -6
- package/dist/deckgl/base-map/index.js.map +1 -1
- package/dist/deckgl/base-map/provider.d.ts +2 -2
- package/dist/deckgl/base-map/provider.js +2 -4
- package/dist/deckgl/base-map/provider.js.map +1 -1
- package/dist/deckgl/index.js +3 -3
- package/dist/deckgl/shapes/display-shape-layer/index.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/store.js +7 -1
- package/dist/deckgl/shapes/display-shape-layer/store.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +2 -2
- package/dist/deckgl/shapes/draw-shape-layer/index.js +3 -3
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +7 -7
- 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 +2 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +2 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +2 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +2 -2
- package/dist/deckgl/shapes/draw-shape-layer/store.js +37 -3
- package/dist/deckgl/shapes/draw-shape-layer/store.js.map +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 +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +2 -2
- package/dist/deckgl/shapes/edit-shape-layer/index.js +4 -4
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +6 -6
- 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/index.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/store.js +9 -3
- package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js +1 -1
- package/dist/deckgl/shapes/index.js +4 -4
- package/dist/deckgl/shapes/shared/constants.js +5 -5
- package/dist/deckgl/shapes/shared/constants.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/layer-config.js +1 -1
- package/dist/shared/cleanup.d.ts +58 -0
- package/dist/shared/cleanup.js +93 -0
- package/dist/shared/cleanup.js.map +1 -0
- package/dist/shared/create-map-store.d.ts +12 -0
- package/dist/shared/create-map-store.js +8 -3
- package/dist/shared/create-map-store.js.map +1 -1
- package/package.json +6 -5
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { formatCircleTooltip } from "../../shared/constants.js";
|
|
15
14
|
import { DEFAULT_DISTANCE_UNITS, getDistanceUnitAbbreviation } from "../../../../shared/units.js";
|
|
15
|
+
import { formatCircleTooltip } from "../../shared/constants.js";
|
|
16
16
|
import { computeCircleMeasurements } from "../../shared/utils/geometry-measurements.js";
|
|
17
17
|
import { DrawCircleFromCenterMode } from "@deck.gl-community/editable-layers";
|
|
18
18
|
|
|
19
19
|
//#region src/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.ts
|
|
20
20
|
/**
|
|
21
|
-
* Extends DrawCircleFromCenterMode to display
|
|
21
|
+
* Extends DrawCircleFromCenterMode to display radius and area tooltip.
|
|
22
22
|
*
|
|
23
|
-
* Shows the
|
|
23
|
+
* Shows the radius and area of the circle being drawn based on the radius
|
|
24
24
|
* from center point to cursor position. The tooltip updates in real-time as
|
|
25
25
|
* the cursor moves, displaying measurements in the configured distance units.
|
|
26
26
|
*
|
|
@@ -30,7 +30,7 @@ import { DrawCircleFromCenterMode } from "@deck.gl-community/editable-layers";
|
|
|
30
30
|
*
|
|
31
31
|
* ## Drawing Flow
|
|
32
32
|
* 1. Click to set center point
|
|
33
|
-
* 2. Move cursor to set radius (tooltip shows
|
|
33
|
+
* 2. Move cursor to set radius (tooltip shows radius and area)
|
|
34
34
|
* 3. Click to finish the circle
|
|
35
35
|
*
|
|
36
36
|
* @example
|
|
@@ -46,7 +46,7 @@ var DrawCircleModeWithTooltip = class extends DrawCircleFromCenterMode {
|
|
|
46
46
|
tooltip = null;
|
|
47
47
|
/**
|
|
48
48
|
* Handle pointer move events to update the tooltip with circle measurements.
|
|
49
|
-
* Calculates
|
|
49
|
+
* Calculates radius and area based on the distance from center to cursor.
|
|
50
50
|
*
|
|
51
51
|
* @param event - Pointer move event with cursor position
|
|
52
52
|
* @param props - Mode properties including distance units configuration
|
|
@@ -61,10 +61,10 @@ var DrawCircleModeWithTooltip = class extends DrawCircleFromCenterMode {
|
|
|
61
61
|
const { mapCoords } = event;
|
|
62
62
|
const distanceUnits = props.modeConfig?.distanceUnits ?? DEFAULT_DISTANCE_UNITS;
|
|
63
63
|
const centerPoint = clickSequence[clickSequence.length - 1];
|
|
64
|
-
const {
|
|
64
|
+
const { radius, area } = computeCircleMeasurements(centerPoint, mapCoords, distanceUnits);
|
|
65
65
|
this.tooltip = {
|
|
66
66
|
position: mapCoords,
|
|
67
|
-
text: formatCircleTooltip(
|
|
67
|
+
text: formatCircleTooltip(radius, area, getDistanceUnitAbbreviation(distanceUnits))
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draw-circle-mode-with-tooltip.js","names":[],"sources":["../../../../../src/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.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 DrawCircleFromCenterMode,\n type FeatureCollection,\n type ModeProps,\n type PointerMoveEvent,\n type Tooltip,\n} from '@deck.gl-community/editable-layers';\nimport {\n DEFAULT_DISTANCE_UNITS,\n getDistanceUnitAbbreviation,\n} from '@/shared/units';\nimport { formatCircleTooltip } from '../../shared/constants';\nimport { computeCircleMeasurements } from '../../shared/utils/geometry-measurements';\n\n/**\n * Extends DrawCircleFromCenterMode to display
|
|
1
|
+
{"version":3,"file":"draw-circle-mode-with-tooltip.js","names":[],"sources":["../../../../../src/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.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 DrawCircleFromCenterMode,\n type FeatureCollection,\n type ModeProps,\n type PointerMoveEvent,\n type Tooltip,\n} from '@deck.gl-community/editable-layers';\nimport {\n DEFAULT_DISTANCE_UNITS,\n getDistanceUnitAbbreviation,\n} from '@/shared/units';\nimport { formatCircleTooltip } from '../../shared/constants';\nimport { computeCircleMeasurements } from '../../shared/utils/geometry-measurements';\n\n/**\n * Extends DrawCircleFromCenterMode to display radius and area tooltip.\n *\n * Shows the radius and area of the circle being drawn based on the radius\n * from center point to cursor position. The tooltip updates in real-time as\n * the cursor moves, displaying measurements in the configured distance units.\n *\n * ## Usage\n * This mode is automatically used by DrawShapeLayer when drawing circles.\n * The mode is cached at module level to prevent deck.gl assertion failures.\n *\n * ## Drawing Flow\n * 1. Click to set center point\n * 2. Move cursor to set radius (tooltip shows radius and area)\n * 3. Click to finish the circle\n *\n * @example\n * ```typescript\n * import { DrawCircleModeWithTooltip } from '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/modes';\n *\n * // Used internally by DrawShapeLayer\n * const mode = new DrawCircleModeWithTooltip();\n * ```\n */\nexport class DrawCircleModeWithTooltip extends DrawCircleFromCenterMode {\n /** Current tooltip state (null when not drawing) */\n private tooltip: Tooltip | null = null;\n\n /**\n * Handle pointer move events to update the tooltip with circle measurements.\n * Calculates radius and area based on the distance from center to cursor.\n *\n * @param event - Pointer move event with cursor position\n * @param props - Mode properties including distance units configuration\n */\n override handlePointerMove(\n event: PointerMoveEvent,\n props: ModeProps<FeatureCollection>,\n ) {\n super.handlePointerMove(event, props);\n\n const clickSequence = this.getClickSequence();\n if (!clickSequence.length) {\n this.tooltip = null;\n return;\n }\n\n const { mapCoords } = event;\n const distanceUnits =\n props.modeConfig?.distanceUnits ?? DEFAULT_DISTANCE_UNITS;\n\n const centerPoint = clickSequence[clickSequence.length - 1] as [\n number,\n number,\n ];\n const edgePoint = mapCoords as [number, number];\n\n const { radius, area } = computeCircleMeasurements(\n centerPoint,\n edgePoint,\n distanceUnits,\n );\n const unitAbbrev = getDistanceUnitAbbreviation(distanceUnits);\n\n this.tooltip = {\n position: mapCoords,\n text: formatCircleTooltip(radius, area, unitAbbrev),\n };\n }\n\n /**\n * Get the current tooltip array for rendering.\n *\n * @returns Array containing the tooltip if one is active, empty array otherwise\n */\n override getTooltips(): Tooltip[] {\n return this.tooltip ? [this.tooltip] : [];\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,IAAa,4BAAb,cAA+C,yBAAyB;;CAEtE,AAAQ,UAA0B;;;;;;;;CASlC,AAAS,kBACP,OACA,OACA;AACA,QAAM,kBAAkB,OAAO,MAAM;EAErC,MAAM,gBAAgB,KAAK,kBAAkB;AAC7C,MAAI,CAAC,cAAc,QAAQ;AACzB,QAAK,UAAU;AACf;;EAGF,MAAM,EAAE,cAAc;EACtB,MAAM,gBACJ,MAAM,YAAY,iBAAiB;EAErC,MAAM,cAAc,cAAc,cAAc,SAAS;EAMzD,MAAM,EAAE,QAAQ,SAAS,0BACvB,aAHgB,WAKhB,cACD;AAGD,OAAK,UAAU;GACb,UAAU;GACV,MAAM,oBAAoB,QAAQ,MAJjB,4BAA4B,cAAc,CAIR;GACpD;;;;;;;CAQH,AAAS,cAAyB;AAChC,SAAO,KAAK,UAAU,CAAC,KAAK,QAAQ,GAAG,EAAE"}
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { formatDistanceTooltip, formatEllipseTooltip } from "../../shared/constants.js";
|
|
15
14
|
import { DEFAULT_DISTANCE_UNITS, getDistanceUnitAbbreviation } from "../../../../shared/units.js";
|
|
16
|
-
import {
|
|
15
|
+
import { formatDistanceTooltip, formatEllipseTooltip } from "../../shared/constants.js";
|
|
17
16
|
import { distance } from "@turf/turf";
|
|
17
|
+
import { DrawEllipseUsingThreePointsMode } from "@deck.gl-community/editable-layers";
|
|
18
18
|
|
|
19
19
|
//#region src/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.ts
|
|
20
20
|
/**
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { formatDistanceTooltip } from "../../shared/constants.js";
|
|
15
14
|
import { DEFAULT_DISTANCE_UNITS, getDistanceUnitAbbreviation } from "../../../../shared/units.js";
|
|
16
|
-
import {
|
|
15
|
+
import { formatDistanceTooltip } from "../../shared/constants.js";
|
|
17
16
|
import { distance } from "@turf/turf";
|
|
17
|
+
import { DrawLineStringMode } from "@deck.gl-community/editable-layers";
|
|
18
18
|
|
|
19
19
|
//#region src/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.ts
|
|
20
20
|
/**
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { formatDistanceTooltip } from "../../shared/constants.js";
|
|
15
14
|
import { DEFAULT_DISTANCE_UNITS, getDistanceUnitAbbreviation } from "../../../../shared/units.js";
|
|
16
|
-
import {
|
|
15
|
+
import { formatDistanceTooltip } from "../../shared/constants.js";
|
|
17
16
|
import { distance } from "@turf/turf";
|
|
17
|
+
import { DrawPolygonMode } from "@deck.gl-community/editable-layers";
|
|
18
18
|
|
|
19
19
|
//#region src/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.ts
|
|
20
20
|
/**
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { formatRectangleTooltip } from "../../shared/constants.js";
|
|
15
14
|
import { DEFAULT_DISTANCE_UNITS, getDistanceUnitAbbreviation } from "../../../../shared/units.js";
|
|
16
|
-
import {
|
|
15
|
+
import { formatRectangleTooltip } from "../../shared/constants.js";
|
|
17
16
|
import { area, bbox, bboxPolygon, convertArea, destination, distance } from "@turf/turf";
|
|
17
|
+
import { DrawRectangleMode } from "@deck.gl-community/editable-layers";
|
|
18
18
|
import { featureCollection, point as point$1 } from "@turf/helpers";
|
|
19
19
|
|
|
20
20
|
//#region src/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.ts
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
import { createMapStore } from "../../../shared/create-map-store.js";
|
|
17
17
|
import { MapModeEvents } from "../../../map-mode/events.js";
|
|
18
|
-
import { DrawShapeEvents } from "./events.js";
|
|
19
|
-
import { DRAW_CURSOR_MAP, DRAW_SHAPE_LAYER_ID, DRAW_SHAPE_MODE } from "./constants.js";
|
|
20
18
|
import { releaseModeAndCursor, requestModeAndCursor } from "../shared/utils/mode-utils.js";
|
|
19
|
+
import { DRAW_CURSOR_MAP, DRAW_SHAPE_LAYER_ID, DRAW_SHAPE_MODE } from "./constants.js";
|
|
20
|
+
import { DrawShapeEvents } from "./events.js";
|
|
21
21
|
import { convertFeatureToShape } from "./utils/feature-conversion.js";
|
|
22
22
|
import { Broadcast } from "@accelint/bus";
|
|
23
23
|
|
|
@@ -158,6 +158,40 @@ const drawStore = createMapStore({
|
|
|
158
158
|
}
|
|
159
159
|
});
|
|
160
160
|
/**
|
|
161
|
+
* Manually clear the drawing state for a specific map instance.
|
|
162
|
+
*
|
|
163
|
+
* Removes the drawing store instance for the given map ID, canceling any
|
|
164
|
+
* active drawing operation and releasing mode/cursor ownership. This is
|
|
165
|
+
* typically called automatically during cleanup, but can be used manually
|
|
166
|
+
* when needed.
|
|
167
|
+
*
|
|
168
|
+
* ## When to Use
|
|
169
|
+
* - Cleanup after programmatically managing drawing state
|
|
170
|
+
* - Force-reset drawing state in error conditions
|
|
171
|
+
* - Testing and debugging
|
|
172
|
+
*
|
|
173
|
+
* ## Side Effects
|
|
174
|
+
* - Cancels active drawing (if any)
|
|
175
|
+
* - Releases map mode and cursor
|
|
176
|
+
* - Emits 'shapes:draw-canceled' event
|
|
177
|
+
* - Removes store instance from memory
|
|
178
|
+
*
|
|
179
|
+
* @param mapId - Unique identifier for the map instance
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* import { clearDrawingState } from '@accelint/map-toolkit/deckgl/shapes';
|
|
184
|
+
*
|
|
185
|
+
* // Clear drawing state when unmounting a map
|
|
186
|
+
* function cleanup(mapId: UniqueId) {
|
|
187
|
+
* clearDrawingState(mapId);
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
function clearDrawingState(mapId) {
|
|
192
|
+
drawStore.clear(mapId);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
161
195
|
* Complete the drawing operation with a GeoJSON feature.
|
|
162
196
|
*
|
|
163
197
|
* Called internally by the DrawShapeLayer component when the user finishes
|
|
@@ -219,5 +253,5 @@ function cancelDrawingFromLayer(mapId) {
|
|
|
219
253
|
}
|
|
220
254
|
|
|
221
255
|
//#endregion
|
|
222
|
-
export { cancelDrawingFromLayer, completeDrawingFromLayer, drawStore };
|
|
256
|
+
export { cancelDrawingFromLayer, clearDrawingState, completeDrawingFromLayer, drawStore };
|
|
223
257
|
//# sourceMappingURL=store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","names":["DEFAULT_DRAWING_STATE: DrawingState"],"sources":["../../../../src/deckgl/shapes/draw-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 * Draw Shape Store\n *\n * Manages drawing state for shape creation.\n *\n * @example\n * ```tsx\n * import { drawStore } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * function DrawControls({ mapId }) {\n * const { state, draw, cancel } = drawStore.use(mapId);\n *\n * return (\n * <div>\n * <p>Drawing: {state.activeShapeType ?? 'none'}</p>\n * <button onClick={() => draw('Polygon')}>Draw Polygon</button>\n * <button onClick={cancel}>Cancel</button>\n * </div>\n * );\n * }\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { MapModeEvents } from '@/map-mode/events';\nimport { createMapStore } from '@/shared/create-map-store';\nimport {\n releaseModeAndCursor,\n requestModeAndCursor,\n} from '../shared/utils/mode-utils';\nimport {\n DRAW_CURSOR_MAP,\n DRAW_SHAPE_LAYER_ID,\n DRAW_SHAPE_MODE,\n} from './constants';\nimport { DrawShapeEvents } from './events';\nimport { convertFeatureToShape } from './utils/feature-conversion';\nimport type { UniqueId } from '@accelint/core';\nimport type { Feature } from 'geojson';\nimport type { MapModeEventType } from '@/map-mode/types';\nimport type { Shape, ShapeFeatureType } from '../shared/types';\nimport type { DrawShapeEvent, ShapeDrawnEvent } from './events';\nimport type { DrawFunction, DrawingState, DrawShapeOptions } from './types';\n\n/**\n * Typed event bus instances\n */\nconst drawShapeBus = Broadcast.getInstance<DrawShapeEvent>();\nconst mapModeBus = Broadcast.getInstance<MapModeEventType>();\n\n/**\n * Default drawing state\n */\nconst DEFAULT_DRAWING_STATE: DrawingState = {\n activeShapeType: null,\n tentativeFeature: null,\n styleDefaults: null,\n circleDefaults: null,\n};\n\n/**\n * Actions for draw shape store\n */\ntype DrawShapeActions = {\n /** Start drawing a shape of the specified type */\n draw: DrawFunction;\n /** Cancel the current drawing operation */\n cancel: () => void;\n};\n\n/**\n * Start drawing a shape\n */\nfunction startDrawing(\n mapId: UniqueId,\n state: DrawingState,\n shapeType: ShapeFeatureType,\n options: DrawShapeOptions | undefined,\n notify: () => void,\n setState: (updates: Partial<DrawingState>) => void,\n): void {\n // Already drawing - cancel first\n if (state.activeShapeType) {\n cancelDrawingInternal(mapId, state, notify, setState);\n }\n\n // Update state with new object reference\n setState({\n activeShapeType: shapeType,\n tentativeFeature: null,\n styleDefaults: options?.styleDefaults ?? null,\n circleDefaults: options?.circleDefaults ?? null,\n });\n\n // Request map mode and cursor using shared utilities\n const cursor = DRAW_CURSOR_MAP[shapeType];\n requestModeAndCursor(mapId, DRAW_SHAPE_MODE, cursor, DRAW_SHAPE_LAYER_ID);\n\n // Emit drawing started event\n drawShapeBus.emit(DrawShapeEvents.drawing, {\n shapeType,\n mapId,\n });\n\n notify();\n}\n\n/**\n * Complete drawing and create a shape\n */\nfunction completeDrawingInternal(\n mapId: UniqueId,\n state: DrawingState,\n feature: Feature,\n notify: () => void,\n setState: (updates: Partial<DrawingState>) => void,\n): Shape {\n if (!state.activeShapeType) {\n throw new Error('Cannot complete drawing - not currently drawing');\n }\n\n const shapeType = state.activeShapeType;\n const styleDefaults = state.styleDefaults;\n\n // Convert feature to Shape\n const shape = convertFeatureToShape(feature, shapeType, styleDefaults);\n\n // Reset state with new object reference\n setState({\n activeShapeType: null,\n tentativeFeature: null,\n styleDefaults: null,\n circleDefaults: null,\n });\n\n // Release mode and cursor using shared utilities\n releaseModeAndCursor(mapId, DRAW_SHAPE_LAYER_ID);\n\n // Emit shape drawn event\n drawShapeBus.emit(DrawShapeEvents.drawn, {\n shape,\n mapId,\n } as unknown as ShapeDrawnEvent['payload']);\n\n notify();\n\n return shape;\n}\n\n/**\n * Cancel the current drawing operation\n */\nfunction cancelDrawingInternal(\n mapId: UniqueId,\n state: DrawingState,\n notify: () => void,\n setState: (updates: Partial<DrawingState>) => void,\n): void {\n if (!state.activeShapeType) {\n return; // Nothing to cancel\n }\n\n const shapeType = state.activeShapeType;\n\n // Reset state with new object reference\n setState({\n activeShapeType: null,\n tentativeFeature: null,\n styleDefaults: null,\n circleDefaults: null,\n });\n\n // Release mode and cursor using shared utilities\n releaseModeAndCursor(mapId, DRAW_SHAPE_LAYER_ID);\n\n // Emit canceled event\n drawShapeBus.emit(DrawShapeEvents.canceled, {\n shapeType,\n mapId,\n });\n\n notify();\n}\n\n/**\n * Draw shape store\n */\nexport const drawStore = createMapStore<DrawingState, DrawShapeActions>({\n defaultState: { ...DEFAULT_DRAWING_STATE },\n\n actions: (mapId, { get, set, notify }) => ({\n draw: (shapeType: ShapeFeatureType, options?: DrawShapeOptions) => {\n startDrawing(mapId, get(), shapeType, options, notify, set);\n },\n\n cancel: () => {\n cancelDrawingInternal(mapId, get(), notify, set);\n },\n }),\n\n bus: (mapId, { get }) => {\n // Listen for mode authorization requests - REJECT when drawing (protected mode)\n const unsubAuth = mapModeBus.on(\n MapModeEvents.changeAuthorization,\n (event) => {\n const { authId, id } = event.payload;\n\n // Filter: only handle if targeted at this map\n if (id !== mapId) {\n return;\n }\n\n // If we're actively drawing, reject the mode change request\n if (get().activeShapeType) {\n mapModeBus.emit(MapModeEvents.changeDecision, {\n authId,\n approved: false,\n owner: DRAW_SHAPE_LAYER_ID,\n reason: 'Drawing in progress - cancel drawing first',\n id: mapId,\n });\n }\n },\n );\n\n return () => {\n unsubAuth();\n };\n },\n\n onCleanup: (mapId, state) => {\n // Cancel any active drawing before cleanup\n if (state.activeShapeType) {\n // Release mode and cursor using shared utilities\n releaseModeAndCursor(mapId, DRAW_SHAPE_LAYER_ID);\n\n // Emit canceled event\n drawShapeBus.emit(DrawShapeEvents.canceled, {\n shapeType: state.activeShapeType,\n mapId,\n });\n }\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Get the current drawing state for a map instance.\n *\n * Returns the drawing state (active shape type, style defaults, etc.) for the\n * specified map ID. Returns null if no drawing store instance exists for that map.\n *\n * ## Use Cases\n * - Check if a map is currently in drawing mode\n * - Access drawing state outside of React components\n * - Inspect state for debugging purposes\n *\n * @param mapId - Unique identifier for the map instance\n * @returns The drawing state, or null if no store instance exists\n *\n * @example\n * ```typescript\n * import { getDrawingState } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * const state = getDrawingState('map-1');\n * if (state?.activeShapeType) {\n * console.log(`Currently drawing: ${state.activeShapeType}`);\n * }\n * ```\n */\nexport function getDrawingState(mapId: UniqueId): DrawingState | null {\n if (!drawStore.exists(mapId)) {\n return null;\n }\n return drawStore.get(mapId);\n}\n\n/**\n * React hook for accessing drawing state and actions.\n *\n * Provides access to the drawing store for a specific map instance, including\n * the current state and draw/cancel actions. Uses `useSyncExternalStore` for\n * concurrent-safe React subscriptions.\n *\n * ## Comparison with useDrawShape\n * - `useDrawingState`: Low-level store access without event callbacks\n * - `useDrawShape`: High-level API with onCreate/onCancel callbacks\n *\n * Use `useDrawingState` when you need direct store access without event handling.\n * Use `useDrawShape` (recommended) for most drawing interactions.\n *\n * @param mapId - Unique identifier for the map instance\n * @returns Object containing drawing state and actions (draw, cancel)\n *\n * @example\n * ```tsx\n * import { useDrawingState } from '@accelint/map-toolkit/deckgl/shapes';\n * import { ShapeFeatureType } from '@accelint/map-toolkit/deckgl/shapes/shared/types';\n *\n * function DrawingStatus({ mapId }: { mapId: UniqueId }) {\n * const { state, draw, cancel } = useDrawingState(mapId);\n *\n * return (\n * <div>\n * <p>Status: {state.activeShapeType ?? 'Not drawing'}</p>\n * <button onClick={() => draw(ShapeFeatureType.Polygon)}>\n * Start Drawing\n * </button>\n * {state.activeShapeType && (\n * <button onClick={cancel}>Cancel</button>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useDrawingState(\n mapId: UniqueId,\n): { state: DrawingState } & DrawShapeActions {\n return drawStore.use(mapId);\n}\n\n/**\n * Manually clear the drawing state for a specific map instance.\n *\n * Removes the drawing store instance for the given map ID, canceling any\n * active drawing operation and releasing mode/cursor ownership. This is\n * typically called automatically during cleanup, but can be used manually\n * when needed.\n *\n * ## When to Use\n * - Cleanup after programmatically managing drawing state\n * - Force-reset drawing state in error conditions\n * - Testing and debugging\n *\n * ## Side Effects\n * - Cancels active drawing (if any)\n * - Releases map mode and cursor\n * - Emits 'shapes:draw-canceled' event\n * - Removes store instance from memory\n *\n * @param mapId - Unique identifier for the map instance\n *\n * @example\n * ```typescript\n * import { clearDrawingState } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * // Clear drawing state when unmounting a map\n * function cleanup(mapId: UniqueId) {\n * clearDrawingState(mapId);\n * }\n * ```\n */\nexport function clearDrawingState(mapId: UniqueId): void {\n drawStore.clear(mapId);\n}\n\n/**\n * Complete the drawing operation with a GeoJSON feature.\n *\n * Called internally by the DrawShapeLayer component when the user finishes\n * drawing a shape. Converts the raw EditableGeoJsonLayer feature to a Shape\n * object, resets drawing state, releases mode/cursor, and emits the drawn event.\n *\n * ## Internal API\n * This function is exported for use by the DrawShapeLayer component and should\n * not be called directly from application code. Use the `draw` action from\n * `useDrawShape` or `useDrawingState` instead.\n *\n * @param mapId - Unique identifier for the map instance\n * @param feature - The completed GeoJSON feature from EditableGeoJsonLayer\n * @returns The newly created Shape object\n * @throws Error if not currently drawing\n *\n * @example\n * ```typescript\n * // Internal usage in DrawShapeLayer\n * const handleEdit = ({ updatedData, editType }: EditAction) => {\n * if (editType === 'addFeature') {\n * const feature = updatedData.features[updatedData.features.length - 1];\n * if (feature) {\n * completeDrawingFromLayer(mapId, feature);\n * }\n * }\n * };\n * ```\n */\nexport function completeDrawingFromLayer(\n mapId: UniqueId,\n feature: Feature,\n): Shape {\n const state = drawStore.get(mapId);\n return completeDrawingInternal(\n mapId,\n state,\n feature,\n () => {\n /* notify handled by set */\n },\n (updates) => drawStore.set(mapId, updates),\n );\n}\n\n/**\n * Cancel the current drawing operation from the layer.\n *\n * Called internally by the DrawShapeLayer component when the user presses ESC\n * or the drawing is otherwise canceled. Resets drawing state, releases mode/cursor,\n * and emits the canceled event.\n *\n * ## Internal API\n * This function is exported for use by the DrawShapeLayer component and should\n * not be called directly from application code. Use the `cancel` action from\n * `useDrawShape` or `useDrawingState` instead.\n *\n * @param mapId - Unique identifier for the map instance\n *\n * @example\n * ```typescript\n * // Internal usage in DrawShapeLayer\n * const handleEdit = ({ editType }: EditAction) => {\n * if (editType === 'cancelFeature') {\n * cancelDrawingFromLayer(mapId);\n * }\n * };\n * ```\n */\nexport function cancelDrawingFromLayer(mapId: UniqueId): void {\n drawStore.actions(mapId).cancel();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,MAAM,eAAe,UAAU,aAA6B;AAC5D,MAAM,aAAa,UAAU,aAA+B;;;;AAK5D,MAAMA,wBAAsC;CAC1C,iBAAiB;CACjB,kBAAkB;CAClB,eAAe;CACf,gBAAgB;CACjB;;;;AAeD,SAAS,aACP,OACA,OACA,WACA,SACA,QACA,UACM;AAEN,KAAI,MAAM,gBACR,uBAAsB,OAAO,OAAO,QAAQ,SAAS;AAIvD,UAAS;EACP,iBAAiB;EACjB,kBAAkB;EAClB,eAAe,SAAS,iBAAiB;EACzC,gBAAgB,SAAS,kBAAkB;EAC5C,CAAC;CAGF,MAAM,SAAS,gBAAgB;AAC/B,sBAAqB,OAAO,iBAAiB,QAAQ,oBAAoB;AAGzE,cAAa,KAAK,gBAAgB,SAAS;EACzC;EACA;EACD,CAAC;AAEF,SAAQ;;;;;AAMV,SAAS,wBACP,OACA,OACA,SACA,QACA,UACO;AACP,KAAI,CAAC,MAAM,gBACT,OAAM,IAAI,MAAM,kDAAkD;CAGpE,MAAM,YAAY,MAAM;CACxB,MAAM,gBAAgB,MAAM;CAG5B,MAAM,QAAQ,sBAAsB,SAAS,WAAW,cAAc;AAGtE,UAAS;EACP,iBAAiB;EACjB,kBAAkB;EAClB,eAAe;EACf,gBAAgB;EACjB,CAAC;AAGF,sBAAqB,OAAO,oBAAoB;AAGhD,cAAa,KAAK,gBAAgB,OAAO;EACvC;EACA;EACD,CAA0C;AAE3C,SAAQ;AAER,QAAO;;;;;AAMT,SAAS,sBACP,OACA,OACA,QACA,UACM;AACN,KAAI,CAAC,MAAM,gBACT;CAGF,MAAM,YAAY,MAAM;AAGxB,UAAS;EACP,iBAAiB;EACjB,kBAAkB;EAClB,eAAe;EACf,gBAAgB;EACjB,CAAC;AAGF,sBAAqB,OAAO,oBAAoB;AAGhD,cAAa,KAAK,gBAAgB,UAAU;EAC1C;EACA;EACD,CAAC;AAEF,SAAQ;;;;;AAMV,MAAa,YAAY,eAA+C;CACtE,cAAc,EAAE,GAAG,uBAAuB;CAE1C,UAAU,OAAO,EAAE,KAAK,KAAK,cAAc;EACzC,OAAO,WAA6B,YAA+B;AACjE,gBAAa,OAAO,KAAK,EAAE,WAAW,SAAS,QAAQ,IAAI;;EAG7D,cAAc;AACZ,yBAAsB,OAAO,KAAK,EAAE,QAAQ,IAAI;;EAEnD;CAED,MAAM,OAAO,EAAE,UAAU;EAEvB,MAAM,YAAY,WAAW,GAC3B,cAAc,sBACb,UAAU;GACT,MAAM,EAAE,QAAQ,OAAO,MAAM;AAG7B,OAAI,OAAO,MACT;AAIF,OAAI,KAAK,CAAC,gBACR,YAAW,KAAK,cAAc,gBAAgB;IAC5C;IACA,UAAU;IACV,OAAO;IACP,QAAQ;IACR,IAAI;IACL,CAAC;IAGP;AAED,eAAa;AACX,cAAW;;;CAIf,YAAY,OAAO,UAAU;AAE3B,MAAI,MAAM,iBAAiB;AAEzB,wBAAqB,OAAO,oBAAoB;AAGhD,gBAAa,KAAK,gBAAgB,UAAU;IAC1C,WAAW,MAAM;IACjB;IACD,CAAC;;;CAGP,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmJF,SAAgB,yBACd,OACA,SACO;AAEP,QAAO,wBACL,OAFY,UAAU,IAAI,MAAM,EAIhC,eACM,KAGL,YAAY,UAAU,IAAI,OAAO,QAAQ,CAC3C;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BH,SAAgB,uBAAuB,OAAuB;AAC5D,WAAU,QAAQ,MAAM,CAAC,QAAQ"}
|
|
1
|
+
{"version":3,"file":"store.js","names":["DEFAULT_DRAWING_STATE: DrawingState"],"sources":["../../../../src/deckgl/shapes/draw-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 * Draw Shape Store\n *\n * Manages drawing state for shape creation.\n *\n * @example\n * ```tsx\n * import { drawStore } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * function DrawControls({ mapId }) {\n * const { state, draw, cancel } = drawStore.use(mapId);\n *\n * return (\n * <div>\n * <p>Drawing: {state.activeShapeType ?? 'none'}</p>\n * <button onClick={() => draw('Polygon')}>Draw Polygon</button>\n * <button onClick={cancel}>Cancel</button>\n * </div>\n * );\n * }\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { MapModeEvents } from '@/map-mode/events';\nimport { createMapStore } from '@/shared/create-map-store';\nimport {\n releaseModeAndCursor,\n requestModeAndCursor,\n} from '../shared/utils/mode-utils';\nimport {\n DRAW_CURSOR_MAP,\n DRAW_SHAPE_LAYER_ID,\n DRAW_SHAPE_MODE,\n} from './constants';\nimport { DrawShapeEvents } from './events';\nimport { convertFeatureToShape } from './utils/feature-conversion';\nimport type { UniqueId } from '@accelint/core';\nimport type { Feature } from 'geojson';\nimport type { MapModeEventType } from '@/map-mode/types';\nimport type { Shape, ShapeFeatureType } from '../shared/types';\nimport type { DrawShapeEvent, ShapeDrawnEvent } from './events';\nimport type { DrawFunction, DrawingState, DrawShapeOptions } from './types';\n\n/**\n * Typed event bus instances\n */\nconst drawShapeBus = Broadcast.getInstance<DrawShapeEvent>();\nconst mapModeBus = Broadcast.getInstance<MapModeEventType>();\n\n/**\n * Default drawing state\n */\nconst DEFAULT_DRAWING_STATE: DrawingState = {\n activeShapeType: null,\n tentativeFeature: null,\n styleDefaults: null,\n circleDefaults: null,\n};\n\n/**\n * Actions for draw shape store\n */\ntype DrawShapeActions = {\n /** Start drawing a shape of the specified type */\n draw: DrawFunction;\n /** Cancel the current drawing operation */\n cancel: () => void;\n};\n\n/**\n * Start drawing a shape\n */\nfunction startDrawing(\n mapId: UniqueId,\n state: DrawingState,\n shapeType: ShapeFeatureType,\n options: DrawShapeOptions | undefined,\n notify: () => void,\n setState: (updates: Partial<DrawingState>) => void,\n): void {\n // Already drawing - cancel first\n if (state.activeShapeType) {\n cancelDrawingInternal(mapId, state, notify, setState);\n }\n\n // Update state with new object reference\n setState({\n activeShapeType: shapeType,\n tentativeFeature: null,\n styleDefaults: options?.styleDefaults ?? null,\n circleDefaults: options?.circleDefaults ?? null,\n });\n\n // Request map mode and cursor using shared utilities\n const cursor = DRAW_CURSOR_MAP[shapeType];\n requestModeAndCursor(mapId, DRAW_SHAPE_MODE, cursor, DRAW_SHAPE_LAYER_ID);\n\n // Emit drawing started event\n drawShapeBus.emit(DrawShapeEvents.drawing, {\n shapeType,\n mapId,\n });\n\n notify();\n}\n\n/**\n * Complete drawing and create a shape\n */\nfunction completeDrawingInternal(\n mapId: UniqueId,\n state: DrawingState,\n feature: Feature,\n notify: () => void,\n setState: (updates: Partial<DrawingState>) => void,\n): Shape {\n if (!state.activeShapeType) {\n throw new Error('Cannot complete drawing - not currently drawing');\n }\n\n const shapeType = state.activeShapeType;\n const styleDefaults = state.styleDefaults;\n\n // Convert feature to Shape\n const shape = convertFeatureToShape(feature, shapeType, styleDefaults);\n\n // Reset state with new object reference\n setState({\n activeShapeType: null,\n tentativeFeature: null,\n styleDefaults: null,\n circleDefaults: null,\n });\n\n // Release mode and cursor using shared utilities\n releaseModeAndCursor(mapId, DRAW_SHAPE_LAYER_ID);\n\n // Emit shape drawn event\n drawShapeBus.emit(DrawShapeEvents.drawn, {\n shape,\n mapId,\n } as unknown as ShapeDrawnEvent['payload']);\n\n notify();\n\n return shape;\n}\n\n/**\n * Cancel the current drawing operation\n */\nfunction cancelDrawingInternal(\n mapId: UniqueId,\n state: DrawingState,\n notify: () => void,\n setState: (updates: Partial<DrawingState>) => void,\n): void {\n if (!state.activeShapeType) {\n return; // Nothing to cancel\n }\n\n const shapeType = state.activeShapeType;\n\n // Reset state with new object reference\n setState({\n activeShapeType: null,\n tentativeFeature: null,\n styleDefaults: null,\n circleDefaults: null,\n });\n\n // Release mode and cursor using shared utilities\n releaseModeAndCursor(mapId, DRAW_SHAPE_LAYER_ID);\n\n // Emit canceled event\n drawShapeBus.emit(DrawShapeEvents.canceled, {\n shapeType,\n mapId,\n });\n\n notify();\n}\n\n/**\n * Draw shape store\n */\nexport const drawStore = createMapStore<DrawingState, DrawShapeActions>({\n defaultState: { ...DEFAULT_DRAWING_STATE },\n\n actions: (mapId, { get, set, notify }) => ({\n draw: (shapeType: ShapeFeatureType, options?: DrawShapeOptions) => {\n startDrawing(mapId, get(), shapeType, options, notify, set);\n },\n\n cancel: () => {\n cancelDrawingInternal(mapId, get(), notify, set);\n },\n }),\n\n bus: (mapId, { get }) => {\n // Listen for mode authorization requests - REJECT when drawing (protected mode)\n const unsubAuth = mapModeBus.on(\n MapModeEvents.changeAuthorization,\n (event) => {\n const { authId, id } = event.payload;\n\n // Filter: only handle if targeted at this map\n if (id !== mapId) {\n return;\n }\n\n // If we're actively drawing, reject the mode change request\n if (get().activeShapeType) {\n mapModeBus.emit(MapModeEvents.changeDecision, {\n authId,\n approved: false,\n owner: DRAW_SHAPE_LAYER_ID,\n reason: 'Drawing in progress - cancel drawing first',\n id: mapId,\n });\n }\n },\n );\n\n return () => {\n unsubAuth();\n };\n },\n\n onCleanup: (mapId, state) => {\n // Cancel any active drawing before cleanup\n if (state.activeShapeType) {\n // Release mode and cursor using shared utilities\n releaseModeAndCursor(mapId, DRAW_SHAPE_LAYER_ID);\n\n // Emit canceled event\n drawShapeBus.emit(DrawShapeEvents.canceled, {\n shapeType: state.activeShapeType,\n mapId,\n });\n }\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Get the current drawing state for a map instance.\n *\n * Returns the drawing state (active shape type, style defaults, etc.) for the\n * specified map ID. Returns null if no drawing store instance exists for that map.\n *\n * ## Use Cases\n * - Check if a map is currently in drawing mode\n * - Access drawing state outside of React components\n * - Inspect state for debugging purposes\n *\n * @param mapId - Unique identifier for the map instance\n * @returns The drawing state, or null if no store instance exists\n *\n * @example\n * ```typescript\n * import { getDrawingState } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * const state = getDrawingState('map-1');\n * if (state?.activeShapeType) {\n * console.log(`Currently drawing: ${state.activeShapeType}`);\n * }\n * ```\n */\nexport function getDrawingState(mapId: UniqueId): DrawingState | null {\n if (!drawStore.exists(mapId)) {\n return null;\n }\n return drawStore.get(mapId);\n}\n\n/**\n * React hook for accessing drawing state and actions.\n *\n * Provides access to the drawing store for a specific map instance, including\n * the current state and draw/cancel actions. Uses `useSyncExternalStore` for\n * concurrent-safe React subscriptions.\n *\n * ## Comparison with useDrawShape\n * - `useDrawingState`: Low-level store access without event callbacks\n * - `useDrawShape`: High-level API with onCreate/onCancel callbacks\n *\n * Use `useDrawingState` when you need direct store access without event handling.\n * Use `useDrawShape` (recommended) for most drawing interactions.\n *\n * @param mapId - Unique identifier for the map instance\n * @returns Object containing drawing state and actions (draw, cancel)\n *\n * @example\n * ```tsx\n * import { useDrawingState } from '@accelint/map-toolkit/deckgl/shapes';\n * import { ShapeFeatureType } from '@accelint/map-toolkit/deckgl/shapes/shared/types';\n *\n * function DrawingStatus({ mapId }: { mapId: UniqueId }) {\n * const { state, draw, cancel } = useDrawingState(mapId);\n *\n * return (\n * <div>\n * <p>Status: {state.activeShapeType ?? 'Not drawing'}</p>\n * <button onClick={() => draw(ShapeFeatureType.Polygon)}>\n * Start Drawing\n * </button>\n * {state.activeShapeType && (\n * <button onClick={cancel}>Cancel</button>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useDrawingState(\n mapId: UniqueId,\n): { state: DrawingState } & DrawShapeActions {\n return drawStore.use(mapId);\n}\n\n/**\n * Manually clear the drawing state for a specific map instance.\n *\n * Removes the drawing store instance for the given map ID, canceling any\n * active drawing operation and releasing mode/cursor ownership. This is\n * typically called automatically during cleanup, but can be used manually\n * when needed.\n *\n * ## When to Use\n * - Cleanup after programmatically managing drawing state\n * - Force-reset drawing state in error conditions\n * - Testing and debugging\n *\n * ## Side Effects\n * - Cancels active drawing (if any)\n * - Releases map mode and cursor\n * - Emits 'shapes:draw-canceled' event\n * - Removes store instance from memory\n *\n * @param mapId - Unique identifier for the map instance\n *\n * @example\n * ```typescript\n * import { clearDrawingState } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * // Clear drawing state when unmounting a map\n * function cleanup(mapId: UniqueId) {\n * clearDrawingState(mapId);\n * }\n * ```\n */\nexport function clearDrawingState(mapId: UniqueId): void {\n drawStore.clear(mapId);\n}\n\n/**\n * Complete the drawing operation with a GeoJSON feature.\n *\n * Called internally by the DrawShapeLayer component when the user finishes\n * drawing a shape. Converts the raw EditableGeoJsonLayer feature to a Shape\n * object, resets drawing state, releases mode/cursor, and emits the drawn event.\n *\n * ## Internal API\n * This function is exported for use by the DrawShapeLayer component and should\n * not be called directly from application code. Use the `draw` action from\n * `useDrawShape` or `useDrawingState` instead.\n *\n * @param mapId - Unique identifier for the map instance\n * @param feature - The completed GeoJSON feature from EditableGeoJsonLayer\n * @returns The newly created Shape object\n * @throws Error if not currently drawing\n *\n * @example\n * ```typescript\n * // Internal usage in DrawShapeLayer\n * const handleEdit = ({ updatedData, editType }: EditAction) => {\n * if (editType === 'addFeature') {\n * const feature = updatedData.features[updatedData.features.length - 1];\n * if (feature) {\n * completeDrawingFromLayer(mapId, feature);\n * }\n * }\n * };\n * ```\n */\nexport function completeDrawingFromLayer(\n mapId: UniqueId,\n feature: Feature,\n): Shape {\n const state = drawStore.get(mapId);\n return completeDrawingInternal(\n mapId,\n state,\n feature,\n () => {\n /* notify handled by set */\n },\n (updates) => drawStore.set(mapId, updates),\n );\n}\n\n/**\n * Cancel the current drawing operation from the layer.\n *\n * Called internally by the DrawShapeLayer component when the user presses ESC\n * or the drawing is otherwise canceled. Resets drawing state, releases mode/cursor,\n * and emits the canceled event.\n *\n * ## Internal API\n * This function is exported for use by the DrawShapeLayer component and should\n * not be called directly from application code. Use the `cancel` action from\n * `useDrawShape` or `useDrawingState` instead.\n *\n * @param mapId - Unique identifier for the map instance\n *\n * @example\n * ```typescript\n * // Internal usage in DrawShapeLayer\n * const handleEdit = ({ editType }: EditAction) => {\n * if (editType === 'cancelFeature') {\n * cancelDrawingFromLayer(mapId);\n * }\n * };\n * ```\n */\nexport function cancelDrawingFromLayer(mapId: UniqueId): void {\n drawStore.actions(mapId).cancel();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,MAAM,eAAe,UAAU,aAA6B;AAC5D,MAAM,aAAa,UAAU,aAA+B;;;;AAK5D,MAAMA,wBAAsC;CAC1C,iBAAiB;CACjB,kBAAkB;CAClB,eAAe;CACf,gBAAgB;CACjB;;;;AAeD,SAAS,aACP,OACA,OACA,WACA,SACA,QACA,UACM;AAEN,KAAI,MAAM,gBACR,uBAAsB,OAAO,OAAO,QAAQ,SAAS;AAIvD,UAAS;EACP,iBAAiB;EACjB,kBAAkB;EAClB,eAAe,SAAS,iBAAiB;EACzC,gBAAgB,SAAS,kBAAkB;EAC5C,CAAC;CAGF,MAAM,SAAS,gBAAgB;AAC/B,sBAAqB,OAAO,iBAAiB,QAAQ,oBAAoB;AAGzE,cAAa,KAAK,gBAAgB,SAAS;EACzC;EACA;EACD,CAAC;AAEF,SAAQ;;;;;AAMV,SAAS,wBACP,OACA,OACA,SACA,QACA,UACO;AACP,KAAI,CAAC,MAAM,gBACT,OAAM,IAAI,MAAM,kDAAkD;CAGpE,MAAM,YAAY,MAAM;CACxB,MAAM,gBAAgB,MAAM;CAG5B,MAAM,QAAQ,sBAAsB,SAAS,WAAW,cAAc;AAGtE,UAAS;EACP,iBAAiB;EACjB,kBAAkB;EAClB,eAAe;EACf,gBAAgB;EACjB,CAAC;AAGF,sBAAqB,OAAO,oBAAoB;AAGhD,cAAa,KAAK,gBAAgB,OAAO;EACvC;EACA;EACD,CAA0C;AAE3C,SAAQ;AAER,QAAO;;;;;AAMT,SAAS,sBACP,OACA,OACA,QACA,UACM;AACN,KAAI,CAAC,MAAM,gBACT;CAGF,MAAM,YAAY,MAAM;AAGxB,UAAS;EACP,iBAAiB;EACjB,kBAAkB;EAClB,eAAe;EACf,gBAAgB;EACjB,CAAC;AAGF,sBAAqB,OAAO,oBAAoB;AAGhD,cAAa,KAAK,gBAAgB,UAAU;EAC1C;EACA;EACD,CAAC;AAEF,SAAQ;;;;;AAMV,MAAa,YAAY,eAA+C;CACtE,cAAc,EAAE,GAAG,uBAAuB;CAE1C,UAAU,OAAO,EAAE,KAAK,KAAK,cAAc;EACzC,OAAO,WAA6B,YAA+B;AACjE,gBAAa,OAAO,KAAK,EAAE,WAAW,SAAS,QAAQ,IAAI;;EAG7D,cAAc;AACZ,yBAAsB,OAAO,KAAK,EAAE,QAAQ,IAAI;;EAEnD;CAED,MAAM,OAAO,EAAE,UAAU;EAEvB,MAAM,YAAY,WAAW,GAC3B,cAAc,sBACb,UAAU;GACT,MAAM,EAAE,QAAQ,OAAO,MAAM;AAG7B,OAAI,OAAO,MACT;AAIF,OAAI,KAAK,CAAC,gBACR,YAAW,KAAK,cAAc,gBAAgB;IAC5C;IACA,UAAU;IACV,OAAO;IACP,QAAQ;IACR,IAAI;IACL,CAAC;IAGP;AAED,eAAa;AACX,cAAW;;;CAIf,YAAY,OAAO,UAAU;AAE3B,MAAI,MAAM,iBAAiB;AAEzB,wBAAqB,OAAO,oBAAoB;AAGhD,gBAAa,KAAK,gBAAgB,UAAU;IAC1C,WAAW,MAAM;IACjB;IACD,CAAC;;;CAGP,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiHF,SAAgB,kBAAkB,OAAuB;AACvD,WAAU,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCxB,SAAgB,yBACd,OACA,SACO;AAEP,QAAO,wBACL,OAFY,UAAU,IAAI,MAAM,EAIhC,eACM,KAGL,YAAY,UAAU,IAAI,OAAO,QAAQ,CAC3C;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BH,SAAgB,uBAAuB,OAAuB;AAC5D,WAAU,QAAQ,MAAM,CAAC,QAAQ"}
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
'use client';
|
|
15
15
|
|
|
16
|
-
import { MapContext } from "../../base-map/provider.js";
|
|
17
16
|
import { DrawShapeEvents } from "./events.js";
|
|
18
17
|
import { drawStore } from "./store.js";
|
|
18
|
+
import { MapContext } from "../../base-map/provider.js";
|
|
19
19
|
import { useContext, useMemo } from "react";
|
|
20
20
|
import { useBus } from "@accelint/bus/react";
|
|
21
21
|
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
'use client';
|
|
15
15
|
|
|
16
|
-
import { DEFAULT_STYLE_PROPERTIES } from "../../shared/constants.js";
|
|
17
16
|
import { ShapeFeatureType } from "../../shared/types.js";
|
|
18
17
|
import { DEFAULT_DISTANCE_UNITS } from "../../../../shared/units.js";
|
|
18
|
+
import { DEFAULT_STYLE_PROPERTIES } from "../../shared/constants.js";
|
|
19
19
|
import { getLogger } from "@accelint/logger";
|
|
20
20
|
import { uuid } from "@accelint/core";
|
|
21
21
|
import { centroid, distance } from "@turf/turf";
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { EditShapeLayerProps } from "./types.js";
|
|
14
|
-
import * as
|
|
14
|
+
import * as react_jsx_runtime3 from "react/jsx-runtime";
|
|
15
15
|
|
|
16
16
|
//#region src/deckgl/shapes/edit-shape-layer/index.d.ts
|
|
17
17
|
|
|
@@ -57,7 +57,7 @@ declare function EditShapeLayer({
|
|
|
57
57
|
id,
|
|
58
58
|
mapId,
|
|
59
59
|
unit
|
|
60
|
-
}: EditShapeLayerProps):
|
|
60
|
+
}: EditShapeLayerProps): react_jsx_runtime3.JSX.Element | null;
|
|
61
61
|
//#endregion
|
|
62
62
|
export { EditShapeLayer };
|
|
63
63
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
|
|
14
14
|
'use client';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { ShapeFeatureType } from "../shared/types.js";
|
|
17
17
|
import { COMPLETION_EDIT_TYPES, CONTINUOUS_EDIT_TYPES } from "../shared/constants.js";
|
|
18
|
+
import { EDIT_SHAPE_LAYER_ID } from "./constants.js";
|
|
19
|
+
import { cancelEditingFromLayer, editStore, saveEditingFromLayer, updateFeatureFromLayer } from "./store.js";
|
|
20
|
+
import { MapContext } from "../../base-map/provider.js";
|
|
18
21
|
import { getFillColor, getLineColor } from "../shared/utils/style-utils.js";
|
|
19
|
-
import { ShapeFeatureType } from "../shared/types.js";
|
|
20
22
|
import { useShiftZoomDisable } from "../shared/hooks/use-shift-zoom-disable.js";
|
|
21
23
|
import { getDefaultEditableLayerProps } from "../shared/utils/layer-config.js";
|
|
22
24
|
import { useHotkey } from "../../../hotkey-manager/dist/react/use-hotkey.js";
|
|
23
|
-
import { EDIT_SHAPE_LAYER_ID } from "./constants.js";
|
|
24
25
|
import { getEditModeInstance } from "./modes/index.js";
|
|
25
|
-
import { cancelEditingFromLayer, editStore, saveEditingFromLayer, updateFeatureFromLayer } from "./store.js";
|
|
26
26
|
import { useCallback, useContext, useEffect, useMemo, useRef } from "react";
|
|
27
27
|
import { jsx } from "react/jsx-runtime";
|
|
28
28
|
import { Keycode, globalBind, registerHotkey } from "@accelint/hotkey-manager";
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { formatEllipseTooltip, formatRectangleTooltip } from "../../shared/constants.js";
|
|
15
14
|
import { DEFAULT_DISTANCE_UNITS, getDistanceUnitAbbreviation } from "../../../../shared/units.js";
|
|
15
|
+
import { formatEllipseTooltip, formatRectangleTooltip } from "../../shared/constants.js";
|
|
16
16
|
import { computeEllipseMeasurementsFromPolygon, computeRectangleMeasurementsFromCorners } from "../../shared/utils/geometry-measurements.js";
|
|
17
17
|
import { BaseTransformMode } from "./base-transform-mode.js";
|
|
18
18
|
import { RotateModeWithSnap } from "./rotate-mode-with-snap.js";
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { formatCircleTooltip } from "../../shared/constants.js";
|
|
15
14
|
import { DEFAULT_DISTANCE_UNITS, getDistanceUnitAbbreviation } from "../../../../shared/units.js";
|
|
15
|
+
import { formatCircleTooltip } from "../../shared/constants.js";
|
|
16
16
|
import { computeCircleMeasurements } from "../../shared/utils/geometry-measurements.js";
|
|
17
17
|
import { BaseTransformMode } from "./base-transform-mode.js";
|
|
18
|
-
import { ResizeCircleMode, TranslateMode } from "@deck.gl-community/editable-layers";
|
|
19
18
|
import { centroid } from "@turf/turf";
|
|
19
|
+
import { ResizeCircleMode, TranslateMode } from "@deck.gl-community/editable-layers";
|
|
20
20
|
|
|
21
21
|
//#region src/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.ts
|
|
22
22
|
/**
|
|
@@ -26,7 +26,7 @@ import { centroid } from "@turf/turf";
|
|
|
26
26
|
* This composite mode provides:
|
|
27
27
|
* - **Resize** (ResizeCircleMode): Drag edge to resize from center
|
|
28
28
|
* - **Translation** (TranslateMode): Drag the circle body to move it
|
|
29
|
-
* - **Live tooltip**: Shows
|
|
29
|
+
* - **Live tooltip**: Shows radius and area during resize
|
|
30
30
|
*
|
|
31
31
|
* ## Handle Priority Logic
|
|
32
32
|
* When drag starts, modes are evaluated in this priority order:
|
|
@@ -76,7 +76,7 @@ var CircleTransformMode = class extends BaseTransformMode {
|
|
|
76
76
|
return this.translateMode;
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
|
-
* Update the tooltip with circle
|
|
79
|
+
* Update the tooltip with circle radius and area during resize.
|
|
80
80
|
*/
|
|
81
81
|
onDragging(event, props) {
|
|
82
82
|
if (this.activeDragMode !== this.resizeMode) return;
|
|
@@ -99,10 +99,10 @@ var CircleTransformMode = class extends BaseTransformMode {
|
|
|
99
99
|
}
|
|
100
100
|
const center = centroid(feature).geometry.coordinates;
|
|
101
101
|
const firstPoint = coordinates[0];
|
|
102
|
-
const {
|
|
102
|
+
const { radius, area: area$1 } = computeCircleMeasurements(center, firstPoint, distanceUnits);
|
|
103
103
|
this.tooltip = {
|
|
104
104
|
position: mapCoords,
|
|
105
|
-
text: formatCircleTooltip(
|
|
105
|
+
text: formatCircleTooltip(radius, area$1, getDistanceUnitAbbreviation(distanceUnits))
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circle-transform-mode.js","names":["area"],"sources":["../../../../../src/deckgl/shapes/edit-shape-layer/modes/circle-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 DraggingEvent,\n type FeatureCollection,\n type GeoJsonEditMode,\n type ModeProps,\n ResizeCircleMode,\n TranslateMode,\n} from '@deck.gl-community/editable-layers';\nimport { centroid } from '@turf/turf';\nimport {\n DEFAULT_DISTANCE_UNITS,\n getDistanceUnitAbbreviation,\n} from '@/shared/units';\nimport { formatCircleTooltip } from '../../shared/constants';\nimport { computeCircleMeasurements } from '../../shared/utils/geometry-measurements';\nimport { BaseTransformMode, type HandleMatcher } from './base-transform-mode';\nimport type { Feature, Polygon } from 'geojson';\n\n/**\n * Transform mode for circles combining resize and translate.\n *\n * ## Capabilities\n * This composite mode provides:\n * - **Resize** (ResizeCircleMode): Drag edge to resize from center\n * - **Translation** (TranslateMode): Drag the circle body to move it\n * - **Live tooltip**: Shows
|
|
1
|
+
{"version":3,"file":"circle-transform-mode.js","names":["area"],"sources":["../../../../../src/deckgl/shapes/edit-shape-layer/modes/circle-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 DraggingEvent,\n type FeatureCollection,\n type GeoJsonEditMode,\n type ModeProps,\n ResizeCircleMode,\n TranslateMode,\n} from '@deck.gl-community/editable-layers';\nimport { centroid } from '@turf/turf';\nimport {\n DEFAULT_DISTANCE_UNITS,\n getDistanceUnitAbbreviation,\n} from '@/shared/units';\nimport { formatCircleTooltip } from '../../shared/constants';\nimport { computeCircleMeasurements } from '../../shared/utils/geometry-measurements';\nimport { BaseTransformMode, type HandleMatcher } from './base-transform-mode';\nimport type { Feature, Polygon } from 'geojson';\n\n/**\n * Transform mode for circles combining resize and translate.\n *\n * ## Capabilities\n * This composite mode provides:\n * - **Resize** (ResizeCircleMode): Drag edge to resize from center\n * - **Translation** (TranslateMode): Drag the circle body to move it\n * - **Live tooltip**: Shows radius and area during resize\n *\n * ## Handle Priority Logic\n * When drag starts, modes are evaluated in this priority order:\n * 1. If dragging on the edge/handle → resize takes priority\n * 2. Otherwise → dragging the circle body translates it\n *\n * ## Resize Behavior\n * Unlike scale operations in BoundingTransformMode, circle resize maintains\n * the shape's circular geometry by resizing from the center point. The center\n * remains fixed while the radius changes based on cursor distance.\n *\n * @example\n * ```typescript\n * import { CircleTransformMode } from '@accelint/map-toolkit/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode';\n * import { EditableGeoJsonLayer } from '@deck.gl-community/editable-layers';\n *\n * // Used internally by EditShapeLayer for circles\n * const mode = new CircleTransformMode();\n *\n * const layer = new EditableGeoJsonLayer({\n * mode,\n * data: circleFeatureCollection,\n * selectedFeatureIndexes: [0],\n * onEdit: handleEdit,\n * modeConfig: { distanceUnits: 'kilometers' },\n * // ... other props\n * });\n * ```\n */\nexport class CircleTransformMode extends BaseTransformMode {\n private resizeMode: ResizeCircleMode;\n private translateMode: TranslateMode;\n\n constructor() {\n const resizeMode = new ResizeCircleMode();\n const translateMode = new TranslateMode();\n\n // Order matters: resize first so edge handles take priority\n super([resizeMode, translateMode]);\n\n this.resizeMode = resizeMode;\n this.translateMode = translateMode;\n }\n\n protected override getHandleMatchers(): HandleMatcher[] {\n return [\n {\n // Resize handle: intermediate point on circle edge\n match: (pick) =>\n Boolean(\n pick.isGuide &&\n pick.object?.properties?.guideType === 'editHandle' &&\n pick.object?.properties?.editHandleType === 'intermediate',\n ),\n mode: this.resizeMode,\n // No shift config - resize doesn't have modifiers\n },\n ];\n }\n\n protected override getDefaultMode(): GeoJsonEditMode {\n return this.translateMode;\n }\n\n /**\n * Update the tooltip with circle radius and area during resize.\n */\n protected override onDragging(\n event: DraggingEvent,\n props: ModeProps<FeatureCollection>,\n ): void {\n // Only show tooltip when resizing\n if (this.activeDragMode !== this.resizeMode) {\n return;\n }\n\n const { mapCoords } = event;\n const distanceUnits =\n props.modeConfig?.distanceUnits ?? DEFAULT_DISTANCE_UNITS;\n\n // Get the selected feature to calculate radius from its geometry\n const selectedIndexes = props.selectedIndexes;\n const selectedIndex = selectedIndexes?.[0];\n if (selectedIndex === undefined) {\n this.tooltip = null;\n return;\n }\n\n const feature = props.data.features[selectedIndex] as\n | Feature<Polygon>\n | undefined;\n if (!feature || feature.geometry.type !== 'Polygon') {\n this.tooltip = null;\n return;\n }\n\n // Calculate center and radius from the polygon geometry\n const coordinates = feature.geometry.coordinates[0];\n if (!coordinates || coordinates.length < 3) {\n this.tooltip = null;\n return;\n }\n\n const centerFeature = centroid(feature);\n const center = centerFeature.geometry.coordinates as [number, number];\n const firstPoint = coordinates[0] as [number, number];\n const { radius, area } = computeCircleMeasurements(\n center,\n firstPoint,\n distanceUnits,\n );\n const unitAbbrev = getDistanceUnitAbbreviation(distanceUnits);\n\n // Position tooltip at cursor - offset is applied via getPixelOffset in sublayer props\n this.tooltip = {\n position: mapCoords,\n text: formatCircleTooltip(radius, area, unitAbbrev),\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,IAAa,sBAAb,cAAyC,kBAAkB;CACzD,AAAQ;CACR,AAAQ;CAER,cAAc;EACZ,MAAM,aAAa,IAAI,kBAAkB;EACzC,MAAM,gBAAgB,IAAI,eAAe;AAGzC,QAAM,CAAC,YAAY,cAAc,CAAC;AAElC,OAAK,aAAa;AAClB,OAAK,gBAAgB;;CAGvB,AAAmB,oBAAqC;AACtD,SAAO,CACL;GAEE,QAAQ,SACN,QACE,KAAK,WACH,KAAK,QAAQ,YAAY,cAAc,gBACvC,KAAK,QAAQ,YAAY,mBAAmB,eAC/C;GACH,MAAM,KAAK;GAEZ,CACF;;CAGH,AAAmB,iBAAkC;AACnD,SAAO,KAAK;;;;;CAMd,AAAmB,WACjB,OACA,OACM;AAEN,MAAI,KAAK,mBAAmB,KAAK,WAC/B;EAGF,MAAM,EAAE,cAAc;EACtB,MAAM,gBACJ,MAAM,YAAY,iBAAiB;EAIrC,MAAM,gBADkB,MAAM,kBACU;AACxC,MAAI,kBAAkB,QAAW;AAC/B,QAAK,UAAU;AACf;;EAGF,MAAM,UAAU,MAAM,KAAK,SAAS;AAGpC,MAAI,CAAC,WAAW,QAAQ,SAAS,SAAS,WAAW;AACnD,QAAK,UAAU;AACf;;EAIF,MAAM,cAAc,QAAQ,SAAS,YAAY;AACjD,MAAI,CAAC,eAAe,YAAY,SAAS,GAAG;AAC1C,QAAK,UAAU;AACf;;EAIF,MAAM,SADgB,SAAS,QAAQ,CACV,SAAS;EACtC,MAAM,aAAa,YAAY;EAC/B,MAAM,EAAE,QAAQ,iBAAS,0BACvB,QACA,YACA,cACD;AAID,OAAK,UAAU;GACb,UAAU;GACV,MAAM,oBAAoB,QAAQA,QALjB,4BAA4B,cAAc,CAKR;GACpD"}
|
|
@@ -36,7 +36,7 @@ import { TranslateMode, ViewMode } from "@deck.gl-community/editable-layers";
|
|
|
36
36
|
*
|
|
37
37
|
* CircleTransformMode combines ResizeCircleMode with TranslateMode
|
|
38
38
|
* for circles, allowing resize from edge plus drag to translate.
|
|
39
|
-
* Shows live
|
|
39
|
+
* Shows live radius/area tooltips during resize.
|
|
40
40
|
*
|
|
41
41
|
* PointTranslateMode allows clicking anywhere on the map to reposition
|
|
42
42
|
* a point, or dragging the point for traditional translation behavior.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../../src/deckgl/shapes/edit-shape-layer/modes/index.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 { TranslateMode, ViewMode } from '@deck.gl-community/editable-layers';\nimport { BoundingTransformMode } from './bounding-transform-mode';\nimport { CircleTransformMode } from './circle-transform-mode';\nimport { PointTranslateMode } from './point-translate-mode';\nimport { VertexTransformMode } from './vertex-transform-mode';\nimport type { EditMode } from '../types';\n\n/**\n * Cached edit mode instances.\n *\n * CRITICAL: Mode instances must be cached at module level to prevent\n * deck.gl assertion failures. Creating new mode instances on each render\n * causes the EditableGeoJsonLayer to fail with assertion errors.\n *\n * BoundingTransformMode combines ScaleModeWithFreeTransform, RotateMode, and\n * TranslateMode for shapes without vertex editing (ellipses, rectangles),\n * allowing non-uniform scaling plus rotate/translate via bounding box handles.\n * Shows live dimension tooltips during scaling.\n *\n * VertexTransformMode combines ModifyMode with ScaleModeWithFreeTransform,\n * RotateMode, and TranslateMode for shapes that support vertex editing\n * (polygons, lines), allowing vertex manipulation plus scale/rotate/translate.\n *\n * CircleTransformMode combines ResizeCircleMode with TranslateMode\n * for circles, allowing resize from edge plus drag to translate.\n * Shows live
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../../src/deckgl/shapes/edit-shape-layer/modes/index.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 { TranslateMode, ViewMode } from '@deck.gl-community/editable-layers';\nimport { BoundingTransformMode } from './bounding-transform-mode';\nimport { CircleTransformMode } from './circle-transform-mode';\nimport { PointTranslateMode } from './point-translate-mode';\nimport { VertexTransformMode } from './vertex-transform-mode';\nimport type { EditMode } from '../types';\n\n/**\n * Cached edit mode instances.\n *\n * CRITICAL: Mode instances must be cached at module level to prevent\n * deck.gl assertion failures. Creating new mode instances on each render\n * causes the EditableGeoJsonLayer to fail with assertion errors.\n *\n * BoundingTransformMode combines ScaleModeWithFreeTransform, RotateMode, and\n * TranslateMode for shapes without vertex editing (ellipses, rectangles),\n * allowing non-uniform scaling plus rotate/translate via bounding box handles.\n * Shows live dimension tooltips during scaling.\n *\n * VertexTransformMode combines ModifyMode with ScaleModeWithFreeTransform,\n * RotateMode, and TranslateMode for shapes that support vertex editing\n * (polygons, lines), allowing vertex manipulation plus scale/rotate/translate.\n *\n * CircleTransformMode combines ResizeCircleMode with TranslateMode\n * for circles, allowing resize from edge plus drag to translate.\n * Shows live radius/area tooltips during resize.\n *\n * PointTranslateMode allows clicking anywhere on the map to reposition\n * a point, or dragging the point for traditional translation behavior.\n *\n * TranslateMode allows dragging to move the shape (generic translation).\n */\nconst EDIT_MODE_INSTANCES = {\n view: new ViewMode(),\n 'bounding-transform': new BoundingTransformMode(),\n 'vertex-transform': new VertexTransformMode(),\n 'circle-transform': new CircleTransformMode(),\n translate: new TranslateMode(),\n 'point-translate': new PointTranslateMode(),\n} as const;\n\n/**\n * Get the cached mode instance for an edit mode.\n *\n * Returns the pre-instantiated edit mode for the specified mode type.\n * Modes are cached at module level to prevent deck.gl assertion failures\n * that occur when creating new mode instances on each render.\n *\n * ## Available Edit Modes\n * - `'bounding-transform'`: For shapes without vertex editing (rectangles, ellipses)\n * - `'vertex-transform'`: For shapes with vertex editing (polygons, lines)\n * - `'circle-transform'`: For circles (resize from edge + translate)\n * - `'point-translate'`: For points (click to place + drag to move)\n * - `'translate'`: Generic translation (drag to move)\n *\n * @param mode - The edit mode to get the instance for\n * @returns The cached mode instance\n *\n * @example\n * ```typescript\n * import { getEditModeInstance } from '@accelint/map-toolkit/deckgl/shapes/edit-shape-layer/modes';\n *\n * // Get the bounding transform mode for editing rectangles/ellipses\n * const boundingMode = getEditModeInstance('bounding-transform');\n *\n * // Use with EditableGeoJsonLayer\n * const layer = new EditableGeoJsonLayer({\n * mode: boundingMode,\n * // ... other props\n * });\n * ```\n */\nexport function getEditModeInstance(\n mode: EditMode,\n): (typeof EDIT_MODE_INSTANCES)[EditMode] {\n return EDIT_MODE_INSTANCES[mode];\n}\n\n/**\n * Get the ViewMode instance (for when not editing).\n *\n * Returns the pre-instantiated ViewMode which is the default mode when\n * no editing operation is active. This mode allows viewing and interacting\n * with the map without editing shapes.\n *\n * @returns The cached ViewMode instance\n *\n * @example\n * ```typescript\n * import { getViewModeInstance } from '@accelint/map-toolkit/deckgl/shapes/edit-shape-layer/modes';\n *\n * // Get the view mode (default when not editing)\n * const viewMode = getViewModeInstance();\n *\n * // Use with EditableGeoJsonLayer\n * const layer = new EditableGeoJsonLayer({\n * mode: viewMode,\n * // ... other props\n * });\n * ```\n */\nexport function getViewModeInstance(): ViewMode {\n return EDIT_MODE_INSTANCES.view;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,MAAM,sBAAsB;CAC1B,MAAM,IAAI,UAAU;CACpB,sBAAsB,IAAI,uBAAuB;CACjD,oBAAoB,IAAI,qBAAqB;CAC7C,oBAAoB,IAAI,qBAAqB;CAC7C,WAAW,IAAI,eAAe;CAC9B,mBAAmB,IAAI,oBAAoB;CAC5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCD,SAAgB,oBACd,MACwC;AACxC,QAAO,oBAAoB"}
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { ImmutableFeatureCollection, RotateMode } from "@deck.gl-community/editable-layers";
|
|
15
14
|
import { bearing, centroid, transformRotate } from "@turf/turf";
|
|
15
|
+
import { ImmutableFeatureCollection, RotateMode } from "@deck.gl-community/editable-layers";
|
|
16
16
|
|
|
17
17
|
//#region src/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.ts
|
|
18
18
|
/** Snap interval in degrees (45° = 8 positions around the circle) */
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
|
|
16
16
|
import { createMapStore } from "../../../shared/create-map-store.js";
|
|
17
17
|
import { MapEvents } from "../../base-map/events.js";
|
|
18
|
-
import { isCircleShape, isEllipseShape, isPointShape, isRectangleShape } from "../shared/types.js";
|
|
19
18
|
import { releaseModeAndCursor, requestCursorChange, requestModeChange } from "../shared/utils/mode-utils.js";
|
|
20
|
-
import {
|
|
19
|
+
import { isCircleShape, isEllipseShape, isPointShape, isRectangleShape } from "../shared/types.js";
|
|
21
20
|
import { EDIT_CURSOR_MAP, EDIT_SHAPE_LAYER_ID, EDIT_SHAPE_MODE } from "./constants.js";
|
|
21
|
+
import { EditShapeEvents } from "./events.js";
|
|
22
22
|
import { Broadcast } from "@accelint/bus";
|
|
23
23
|
import { getLogger } from "@accelint/logger";
|
|
24
24
|
|
|
@@ -180,6 +180,12 @@ const editStore = createMapStore({
|
|
|
180
180
|
}
|
|
181
181
|
});
|
|
182
182
|
/**
|
|
183
|
+
* Manually clear editing state for a specific mapId.
|
|
184
|
+
*/
|
|
185
|
+
function clearEditingState(mapId) {
|
|
186
|
+
editStore.clear(mapId);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
183
189
|
* Update feature from the layer component (called during drag operations)
|
|
184
190
|
*/
|
|
185
191
|
function updateFeatureFromLayer(mapId, feature) {
|
|
@@ -199,5 +205,5 @@ function saveEditingFromLayer(mapId) {
|
|
|
199
205
|
}
|
|
200
206
|
|
|
201
207
|
//#endregion
|
|
202
|
-
export { cancelEditingFromLayer, editStore, saveEditingFromLayer, updateFeatureFromLayer };
|
|
208
|
+
export { cancelEditingFromLayer, clearEditingState, editStore, saveEditingFromLayer, updateFeatureFromLayer };
|
|
203
209
|
//# 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 { 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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"}
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
'use client';
|
|
15
15
|
|
|
16
|
-
import { MapContext } from "../../base-map/provider.js";
|
|
17
16
|
import { EditShapeEvents } from "./events.js";
|
|
18
17
|
import { editStore } from "./store.js";
|
|
18
|
+
import { MapContext } from "../../base-map/provider.js";
|
|
19
19
|
import { useContext, useMemo } from "react";
|
|
20
20
|
import { useBus } from "@accelint/bus/react";
|
|
21
21
|
|
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { BASE_FILL_OPACITY, DASH_ARRAYS, DEFAULT_COLORS, DEFAULT_EDIT_HANDLE_COLOR, DEFAULT_EDIT_HANDLE_OUTLINE_COLOR, DEFAULT_STYLE_PROPERTIES, LINE_PATTERNS, LINE_WIDTHS, SHAPE_LAYER_IDS } from "./shared/constants.js";
|
|
15
14
|
import { ShapeEvents } from "./shared/events.js";
|
|
16
|
-
import { getDashArray, getFillColor, getLineColor, getLineWidth, normalizeColor } from "./shared/utils/style-utils.js";
|
|
17
15
|
import { ShapeFeatureType, isCircleShape, isEllipseShape, isLineStringShape, isPointShape, isPolygonShape, isRectangleShape } from "./shared/types.js";
|
|
16
|
+
import { DrawShapeEvents } from "./draw-shape-layer/events.js";
|
|
17
|
+
import { BASE_FILL_OPACITY, DASH_ARRAYS, DEFAULT_COLORS, DEFAULT_EDIT_HANDLE_COLOR, DEFAULT_EDIT_HANDLE_OUTLINE_COLOR, DEFAULT_STYLE_PROPERTIES, LINE_PATTERNS, LINE_WIDTHS, SHAPE_LAYER_IDS } from "./shared/constants.js";
|
|
18
|
+
import { EditShapeEvents } from "./edit-shape-layer/events.js";
|
|
19
|
+
import { getDashArray, getFillColor, getLineColor, getLineWidth, normalizeColor } from "./shared/utils/style-utils.js";
|
|
18
20
|
import { DisplayShapeLayer } from "./display-shape-layer/index.js";
|
|
19
21
|
import { useSelectShape } from "./display-shape-layer/use-select-shape.js";
|
|
20
|
-
import { DrawShapeEvents } from "./draw-shape-layer/events.js";
|
|
21
22
|
import { DrawShapeLayer } from "./draw-shape-layer/index.js";
|
|
22
23
|
import { useDrawShape } from "./draw-shape-layer/use-draw-shape.js";
|
|
23
|
-
import { EditShapeEvents } from "./edit-shape-layer/events.js";
|
|
24
24
|
import { EditShapeLayer } from "./edit-shape-layer/index.js";
|
|
25
25
|
import { useEditShape } from "./edit-shape-layer/use-edit-shape.js";
|
|
26
26
|
|