@accelint/map-toolkit 1.2.0 → 1.3.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 +11 -0
- package/catalog-info.yaml +3 -3
- package/dist/camera/events.d.ts +45 -0
- package/dist/camera/events.js +45 -0
- package/dist/camera/events.js.map +1 -1
- package/dist/camera/store.d.ts +47 -0
- package/dist/camera/store.js +81 -0
- package/dist/camera/store.js.map +1 -1
- package/dist/camera/types.d.ts +81 -0
- package/dist/cursor-coordinates/constants.d.ts +8 -0
- package/dist/cursor-coordinates/constants.js +22 -0
- package/dist/cursor-coordinates/constants.js.map +1 -0
- package/dist/cursor-coordinates/store.d.ts +1 -0
- package/dist/cursor-coordinates/store.js +1 -0
- package/dist/cursor-coordinates/store.js.map +1 -1
- package/dist/cursor-coordinates/use-cursor-coordinates.d.ts +5 -0
- package/dist/cursor-coordinates/use-cursor-coordinates.js +23 -8
- package/dist/cursor-coordinates/use-cursor-coordinates.js.map +1 -1
- package/dist/deckgl/base-map/constants.d.ts +12 -0
- package/dist/deckgl/base-map/constants.js +12 -0
- package/dist/deckgl/base-map/constants.js.map +1 -1
- package/dist/deckgl/base-map/controls.d.ts +11 -1
- package/dist/deckgl/base-map/controls.js +5 -0
- package/dist/deckgl/base-map/controls.js.map +1 -1
- package/dist/deckgl/base-map/events.d.ts +30 -0
- package/dist/deckgl/base-map/events.js +30 -0
- package/dist/deckgl/base-map/events.js.map +1 -1
- package/dist/deckgl/base-map/index.d.ts +2 -2
- package/dist/deckgl/base-map/index.js +33 -3
- package/dist/deckgl/base-map/index.js.map +1 -1
- package/dist/deckgl/base-map/provider.d.ts +2 -2
- package/dist/deckgl/index.js +1 -1
- package/dist/deckgl/saved-viewports/index.d.ts +75 -0
- package/dist/deckgl/saved-viewports/index.js +58 -0
- package/dist/deckgl/saved-viewports/index.js.map +1 -1
- package/dist/deckgl/saved-viewports/storage.d.ts +51 -0
- package/dist/deckgl/saved-viewports/storage.js +64 -0
- package/dist/deckgl/saved-viewports/storage.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/constants.js +18 -6
- package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/fiber.d.ts +7 -0
- package/dist/deckgl/shapes/display-shape-layer/fiber.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +61 -4
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +22 -8
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +75 -4
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/constants.js +30 -0
- package/dist/deckgl/shapes/draw-shape-layer/constants.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/fiber.js +36 -0
- package/dist/deckgl/shapes/draw-shape-layer/fiber.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +2 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +32 -1
- 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 +37 -8
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +43 -1
- 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 +44 -1
- 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 +46 -3
- 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 +37 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/index.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/store.js +50 -2
- package/dist/deckgl/shapes/draw-shape-layer/store.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js +138 -17
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/events.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/events.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +2 -2
- package/dist/deckgl/shapes/edit-shape-layer/index.js +14 -0
- package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js +56 -8
- 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 +26 -4
- 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 +28 -3
- 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 +24 -0
- 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 +33 -4
- 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 +21 -2
- 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 +35 -11
- package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/store.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -1
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js +12 -0
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js.map +1 -1
- package/dist/deckgl/shapes/shared/types.d.ts +3 -3
- package/dist/deckgl/shapes/shared/types.js +2 -2
- package/dist/deckgl/shapes/shared/types.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js +3 -3
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js +1 -1
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js.map +1 -1
- package/dist/deckgl/symbol-layer/fiber.d.ts +18 -0
- package/dist/deckgl/symbol-layer/fiber.js.map +1 -1
- package/dist/deckgl/symbol-layer/index.d.ts +79 -1
- package/dist/deckgl/symbol-layer/index.js +72 -1
- package/dist/deckgl/symbol-layer/index.js.map +1 -1
- package/dist/deckgl/text-layer/character-sets.d.ts +30 -0
- package/dist/deckgl/text-layer/character-sets.js +26 -0
- package/dist/deckgl/text-layer/character-sets.js.map +1 -1
- package/dist/deckgl/text-layer/default-settings.d.ts +29 -0
- package/dist/deckgl/text-layer/default-settings.js +28 -0
- package/dist/deckgl/text-layer/default-settings.js.map +1 -1
- package/dist/deckgl/text-layer/index.d.ts +65 -0
- package/dist/deckgl/text-layer/index.js +56 -0
- package/dist/deckgl/text-layer/index.js.map +1 -1
- package/dist/map-cursor/events.d.ts +19 -0
- package/dist/map-cursor/events.js +19 -0
- package/dist/map-cursor/events.js.map +1 -1
- package/dist/map-cursor/store.d.ts +34 -2
- package/dist/map-cursor/store.js +44 -3
- package/dist/map-cursor/store.js.map +1 -1
- package/dist/map-mode/store.d.ts +43 -4
- package/dist/map-mode/store.js +56 -6
- package/dist/map-mode/store.js.map +1 -1
- package/dist/shared/create-map-store.d.ts +14 -0
- package/dist/shared/create-map-store.js +26 -2
- package/dist/shared/create-map-store.js.map +1 -1
- package/dist/shared/units.d.ts +24 -0
- package/dist/shared/units.js +24 -0
- package/dist/shared/units.js.map +1 -1
- package/dist/viewport/store.d.ts +1 -0
- package/dist/viewport/store.js +4 -0
- package/dist/viewport/store.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @accelint/map-toolkit
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 58199e5: Updates use-cursor-coordinates to format MGRS and UTM default coordinates to follow a more expected format.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 03c1e39: Add a tweak for the Viewport Resize debounce timing, 500 -> 200, for better visual feedback on updates.
|
|
12
|
+
- 0265877: Fixed error handling in `useCursorCoordinates` hook for coordinates outside UTM/MGRS valid range. UTM and MGRS coordinate systems are only valid between 80°S and 84°N. The hook now returns the default placeholder (`--, --`) when attempting to format polar coordinates in these formats.
|
|
13
|
+
|
|
3
14
|
## 1.2.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/catalog-info.yaml
CHANGED
|
@@ -12,14 +12,14 @@ metadata:
|
|
|
12
12
|
Dependencies:
|
|
13
13
|
|
|
14
14
|
accelint_biome-config@1.1.0, accelint_bus@3.0.2, accelint_core@0.5.2,
|
|
15
|
-
accelint_design-foundation@2.1.0, accelint_design-toolkit@9.
|
|
15
|
+
accelint_design-foundation@2.1.0, accelint_design-toolkit@9.5.0,
|
|
16
16
|
accelint_geo@0.5.1, accelint_logger@0.1.5,
|
|
17
|
-
accelint_postcss-tailwind-css-modules@1.0.1, accelint_smeegl@0.3.
|
|
17
|
+
accelint_postcss-tailwind-css-modules@1.0.1, accelint_smeegl@0.3.5,
|
|
18
18
|
accelint_typescript-config@0.1.4, accelint_vitest-config@0.1.6
|
|
19
19
|
annotations:
|
|
20
20
|
backstage.io/edit-url: https://github.com/gohypergiant/standard-toolkit/blob/main/packages/map-toolkit/catalog-info.yaml
|
|
21
21
|
backstage.io/techdocs-ref: dir:.
|
|
22
|
-
package/version: 1.
|
|
22
|
+
package/version: 1.3.0
|
|
23
23
|
github.com/project-slug: gohypergiant/standard-toolkit
|
|
24
24
|
links:
|
|
25
25
|
- url: https://github.com/gohypergiant/standard-toolkit/tree/main/packages/map-toolkit
|
package/dist/camera/events.d.ts
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
//#region src/camera/events.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Namespace prefix for all camera-related events.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { CameraEventNamespace } from '@accelint/map-toolkit/camera';
|
|
8
|
+
*
|
|
9
|
+
* const customEvent = `${CameraEventNamespace}:customAction`;
|
|
10
|
+
* // Result: 'camera:customAction'
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
2
13
|
declare const CameraEventNamespace = "camera";
|
|
14
|
+
/**
|
|
15
|
+
* Event type constants for camera operations.
|
|
16
|
+
*
|
|
17
|
+
* Use these constants with the broadcast bus to control camera behavior across
|
|
18
|
+
* your application. Each event type corresponds to a specific camera operation.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { Broadcast } from '@accelint/bus';
|
|
23
|
+
* import { CameraEventTypes } from '@accelint/map-toolkit/camera';
|
|
24
|
+
* import type { CameraEvent } from '@accelint/map-toolkit/camera';
|
|
25
|
+
*
|
|
26
|
+
* const bus = Broadcast.getInstance<CameraEvent>();
|
|
27
|
+
*
|
|
28
|
+
* // Set camera center
|
|
29
|
+
* bus.emit(CameraEventTypes.setCenter, {
|
|
30
|
+
* id: 'map-1',
|
|
31
|
+
* latitude: 37.7749,
|
|
32
|
+
* longitude: -122.4194,
|
|
33
|
+
* zoom: 10,
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Change view mode
|
|
37
|
+
* bus.emit(CameraEventTypes.setView, {
|
|
38
|
+
* id: 'map-1',
|
|
39
|
+
* view: '2.5D',
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* // Reset camera to initial state
|
|
43
|
+
* bus.emit(CameraEventTypes.reset, {
|
|
44
|
+
* id: 'map-1',
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
3
48
|
declare const CameraEventTypes: {
|
|
4
49
|
readonly setView: "camera:setView";
|
|
5
50
|
readonly setProjection: "camera:setProjection";
|
package/dist/camera/events.js
CHANGED
|
@@ -12,7 +12,52 @@
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
//#region src/camera/events.ts
|
|
15
|
+
/**
|
|
16
|
+
* Namespace prefix for all camera-related events.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { CameraEventNamespace } from '@accelint/map-toolkit/camera';
|
|
21
|
+
*
|
|
22
|
+
* const customEvent = `${CameraEventNamespace}:customAction`;
|
|
23
|
+
* // Result: 'camera:customAction'
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
15
26
|
const CameraEventNamespace = "camera";
|
|
27
|
+
/**
|
|
28
|
+
* Event type constants for camera operations.
|
|
29
|
+
*
|
|
30
|
+
* Use these constants with the broadcast bus to control camera behavior across
|
|
31
|
+
* your application. Each event type corresponds to a specific camera operation.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import { Broadcast } from '@accelint/bus';
|
|
36
|
+
* import { CameraEventTypes } from '@accelint/map-toolkit/camera';
|
|
37
|
+
* import type { CameraEvent } from '@accelint/map-toolkit/camera';
|
|
38
|
+
*
|
|
39
|
+
* const bus = Broadcast.getInstance<CameraEvent>();
|
|
40
|
+
*
|
|
41
|
+
* // Set camera center
|
|
42
|
+
* bus.emit(CameraEventTypes.setCenter, {
|
|
43
|
+
* id: 'map-1',
|
|
44
|
+
* latitude: 37.7749,
|
|
45
|
+
* longitude: -122.4194,
|
|
46
|
+
* zoom: 10,
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* // Change view mode
|
|
50
|
+
* bus.emit(CameraEventTypes.setView, {
|
|
51
|
+
* id: 'map-1',
|
|
52
|
+
* view: '2.5D',
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // Reset camera to initial state
|
|
56
|
+
* bus.emit(CameraEventTypes.reset, {
|
|
57
|
+
* id: 'map-1',
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
16
61
|
const CameraEventTypes = {
|
|
17
62
|
setView: `${CameraEventNamespace}:setView`,
|
|
18
63
|
setProjection: `${CameraEventNamespace}:setProjection`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","names":[],"sources":["../../src/camera/events.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport const CameraEventNamespace = 'camera';\n\nexport const CameraEventTypes = {\n setView: `${CameraEventNamespace}:setView`,\n setProjection: `${CameraEventNamespace}:setProjection`,\n setZoom: `${CameraEventNamespace}:setZoom`,\n setRotation: `${CameraEventNamespace}:setRotation`,\n setPitch: `${CameraEventNamespace}:setPitch`,\n setCenter: `${CameraEventNamespace}:setCenter`,\n fitBounds: `${CameraEventNamespace}:fitBounds`,\n reset: `${CameraEventNamespace}:reset`,\n} as const;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"events.js","names":[],"sources":["../../src/camera/events.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n/**\n * Namespace prefix for all camera-related events.\n *\n * @example\n * ```typescript\n * import { CameraEventNamespace } from '@accelint/map-toolkit/camera';\n *\n * const customEvent = `${CameraEventNamespace}:customAction`;\n * // Result: 'camera:customAction'\n * ```\n */\nexport const CameraEventNamespace = 'camera';\n\n/**\n * Event type constants for camera operations.\n *\n * Use these constants with the broadcast bus to control camera behavior across\n * your application. Each event type corresponds to a specific camera operation.\n *\n * @example\n * ```typescript\n * import { Broadcast } from '@accelint/bus';\n * import { CameraEventTypes } from '@accelint/map-toolkit/camera';\n * import type { CameraEvent } from '@accelint/map-toolkit/camera';\n *\n * const bus = Broadcast.getInstance<CameraEvent>();\n *\n * // Set camera center\n * bus.emit(CameraEventTypes.setCenter, {\n * id: 'map-1',\n * latitude: 37.7749,\n * longitude: -122.4194,\n * zoom: 10,\n * });\n *\n * // Change view mode\n * bus.emit(CameraEventTypes.setView, {\n * id: 'map-1',\n * view: '2.5D',\n * });\n *\n * // Reset camera to initial state\n * bus.emit(CameraEventTypes.reset, {\n * id: 'map-1',\n * });\n * ```\n */\nexport const CameraEventTypes = {\n setView: `${CameraEventNamespace}:setView`,\n setProjection: `${CameraEventNamespace}:setProjection`,\n setZoom: `${CameraEventNamespace}:setZoom`,\n setRotation: `${CameraEventNamespace}:setRotation`,\n setPitch: `${CameraEventNamespace}:setPitch`,\n setCenter: `${CameraEventNamespace}:setCenter`,\n fitBounds: `${CameraEventNamespace}:fitBounds`,\n reset: `${CameraEventNamespace}:reset`,\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCpC,MAAa,mBAAmB;CAC9B,SAAS,GAAG,qBAAqB;CACjC,eAAe,GAAG,qBAAqB;CACvC,SAAS,GAAG,qBAAqB;CACjC,aAAa,GAAG,qBAAqB;CACrC,UAAU,GAAG,qBAAqB;CAClC,WAAW,GAAG,qBAAqB;CACnC,WAAW,GAAG,qBAAqB;CACnC,OAAO,GAAG,qBAAqB;CAChC"}
|
package/dist/camera/store.d.ts
CHANGED
|
@@ -84,6 +84,27 @@ declare const cameraStore: MapStore<CameraState, CameraActions>;
|
|
|
84
84
|
*
|
|
85
85
|
* @param mapId - Unique identifier for the map instance
|
|
86
86
|
* @param initialState - Optional initial camera state
|
|
87
|
+
* @returns void
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* import { uuid } from '@accelint/core';
|
|
92
|
+
* import { initializeCameraState } from '@accelint/map-toolkit/camera';
|
|
93
|
+
*
|
|
94
|
+
* const mapId = uuid();
|
|
95
|
+
*
|
|
96
|
+
* // Initialize with default state
|
|
97
|
+
* initializeCameraState(mapId);
|
|
98
|
+
*
|
|
99
|
+
* // Initialize with custom state
|
|
100
|
+
* initializeCameraState(mapId, {
|
|
101
|
+
* latitude: 37.7749,
|
|
102
|
+
* longitude: -122.4194,
|
|
103
|
+
* zoom: 10,
|
|
104
|
+
* view: '2.5D',
|
|
105
|
+
* pitch: 45,
|
|
106
|
+
* });
|
|
107
|
+
* ```
|
|
87
108
|
*/
|
|
88
109
|
declare function initializeCameraState(mapId: UniqueId, initialState?: CameraStateInput): void;
|
|
89
110
|
/**
|
|
@@ -112,7 +133,33 @@ declare function useMapCamera(mapId: UniqueId, initialCameraState?: CameraStateI
|
|
|
112
133
|
/**
|
|
113
134
|
* Manually clear camera state for a specific map instance.
|
|
114
135
|
*
|
|
136
|
+
* Removes all cached state including initial values and subscription tracking.
|
|
137
|
+
* Useful for cleanup when dynamically destroying map instances.
|
|
138
|
+
*
|
|
115
139
|
* @param mapId - The unique identifier for the map instance to clear
|
|
140
|
+
* @returns void
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* import { clearCameraState } from '@accelint/map-toolkit/camera';
|
|
145
|
+
*
|
|
146
|
+
* // Clean up when removing a map
|
|
147
|
+
* function removeMap(mapId: UniqueId) {
|
|
148
|
+
* clearCameraState(mapId);
|
|
149
|
+
* // ... remove map component
|
|
150
|
+
* }
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* import { clearCameraState } from '@accelint/map-toolkit/camera';
|
|
156
|
+
* import { afterEach } from 'vitest';
|
|
157
|
+
*
|
|
158
|
+
* // Clean up in tests
|
|
159
|
+
* afterEach(() => {
|
|
160
|
+
* clearCameraState('test-map-id');
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
116
163
|
*/
|
|
117
164
|
declare function clearCameraState(mapId: UniqueId): void;
|
|
118
165
|
//#endregion
|
package/dist/camera/store.js
CHANGED
|
@@ -57,6 +57,40 @@ const initializedInstances = /* @__PURE__ */ new Set();
|
|
|
57
57
|
/**
|
|
58
58
|
* Build a complete camera state from partial input.
|
|
59
59
|
* Returns the appropriate discriminated union variant based on view/projection.
|
|
60
|
+
*
|
|
61
|
+
* This function ensures type-safe camera states by constructing the correct
|
|
62
|
+
* discriminated union variant (2D, 2.5D, or 3D) based on view and projection
|
|
63
|
+
* settings, applying appropriate defaults and constraints.
|
|
64
|
+
*
|
|
65
|
+
* @param partial - Optional partial camera state input
|
|
66
|
+
* @returns Complete camera state matching one of the discriminated union variants
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* // Build 2D state (default)
|
|
71
|
+
* const state2D = buildCameraState({
|
|
72
|
+
* latitude: 40.7128,
|
|
73
|
+
* longitude: -74.0060,
|
|
74
|
+
* zoom: 10,
|
|
75
|
+
* });
|
|
76
|
+
* // Result: { ..., view: '2D', projection: 'mercator', pitch: 0 }
|
|
77
|
+
*
|
|
78
|
+
* // Build 2.5D state
|
|
79
|
+
* const state2Point5D = buildCameraState({
|
|
80
|
+
* latitude: 37.7749,
|
|
81
|
+
* longitude: -122.4194,
|
|
82
|
+
* zoom: 12,
|
|
83
|
+
* view: '2.5D',
|
|
84
|
+
* });
|
|
85
|
+
* // Result: { ..., view: '2.5D', projection: 'mercator', pitch: 45 }
|
|
86
|
+
*
|
|
87
|
+
* // Build 3D state
|
|
88
|
+
* const state3D = buildCameraState({
|
|
89
|
+
* view: '3D',
|
|
90
|
+
* zoom: 2,
|
|
91
|
+
* });
|
|
92
|
+
* // Result: { ..., view: '3D', projection: 'globe', pitch: 0, rotation: 0 }
|
|
93
|
+
* ```
|
|
60
94
|
*/
|
|
61
95
|
function buildCameraState(partial) {
|
|
62
96
|
const latitude = partial?.latitude ?? 0;
|
|
@@ -228,6 +262,27 @@ const cameraStore = createMapStore({
|
|
|
228
262
|
*
|
|
229
263
|
* @param mapId - Unique identifier for the map instance
|
|
230
264
|
* @param initialState - Optional initial camera state
|
|
265
|
+
* @returns void
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```typescript
|
|
269
|
+
* import { uuid } from '@accelint/core';
|
|
270
|
+
* import { initializeCameraState } from '@accelint/map-toolkit/camera';
|
|
271
|
+
*
|
|
272
|
+
* const mapId = uuid();
|
|
273
|
+
*
|
|
274
|
+
* // Initialize with default state
|
|
275
|
+
* initializeCameraState(mapId);
|
|
276
|
+
*
|
|
277
|
+
* // Initialize with custom state
|
|
278
|
+
* initializeCameraState(mapId, {
|
|
279
|
+
* latitude: 37.7749,
|
|
280
|
+
* longitude: -122.4194,
|
|
281
|
+
* zoom: 10,
|
|
282
|
+
* view: '2.5D',
|
|
283
|
+
* pitch: 45,
|
|
284
|
+
* });
|
|
285
|
+
* ```
|
|
231
286
|
*/
|
|
232
287
|
function initializeCameraState(mapId, initialState) {
|
|
233
288
|
if (initializedInstances.has(mapId)) return;
|
|
@@ -266,7 +321,33 @@ function useMapCamera(mapId, initialCameraState) {
|
|
|
266
321
|
/**
|
|
267
322
|
* Manually clear camera state for a specific map instance.
|
|
268
323
|
*
|
|
324
|
+
* Removes all cached state including initial values and subscription tracking.
|
|
325
|
+
* Useful for cleanup when dynamically destroying map instances.
|
|
326
|
+
*
|
|
269
327
|
* @param mapId - The unique identifier for the map instance to clear
|
|
328
|
+
* @returns void
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```typescript
|
|
332
|
+
* import { clearCameraState } from '@accelint/map-toolkit/camera';
|
|
333
|
+
*
|
|
334
|
+
* // Clean up when removing a map
|
|
335
|
+
* function removeMap(mapId: UniqueId) {
|
|
336
|
+
* clearCameraState(mapId);
|
|
337
|
+
* // ... remove map component
|
|
338
|
+
* }
|
|
339
|
+
* ```
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* import { clearCameraState } from '@accelint/map-toolkit/camera';
|
|
344
|
+
* import { afterEach } from 'vitest';
|
|
345
|
+
*
|
|
346
|
+
* // Clean up in tests
|
|
347
|
+
* afterEach(() => {
|
|
348
|
+
* clearCameraState('test-map-id');
|
|
349
|
+
* });
|
|
350
|
+
* ```
|
|
270
351
|
*/
|
|
271
352
|
function clearCameraState(mapId) {
|
|
272
353
|
initializedInstances.delete(mapId);
|
package/dist/camera/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","names":["DEFAULT_CAMERA_STATE: CameraState"],"sources":["../../src/camera/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/**\n * Camera Store\n *\n * Manages camera state (position, zoom, pitch, rotation, projection, view) per map instance.\n * State is updated via bus events or direct actions.\n *\n * @example\n * ```tsx\n * import { cameraStore } from '@accelint/map-toolkit/camera';\n *\n * function MapInfo({ mapId }) {\n * const { state } = cameraStore.use(mapId);\n * return (\n * <div>\n * Lat: {state.latitude.toFixed(2)}, Lon: {state.longitude.toFixed(2)}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { fitBounds } from '@math.gl/web-mercator';\nimport { createMapStore } from '../shared/create-map-store';\nimport { CameraEventTypes } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { CameraEvent, ProjectionType, ViewType } from './types';\n\nconst cameraBus = Broadcast.getInstance<CameraEvent>();\n\n/**\n * Camera state for 2D view\n */\ntype CameraState2D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: 0;\n rotation: number;\n projection: 'mercator';\n view: '2D';\n};\n\n/**\n * Camera state for 3D view\n */\ntype CameraState3D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: 0;\n rotation: number;\n projection: 'globe';\n view: '3D';\n};\n\n/**\n * Camera state for 2.5D view\n */\ntype CameraState2Point5D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: number;\n rotation: number;\n projection: 'mercator';\n view: '2.5D';\n};\n\n/**\n * Union type for all camera states\n */\nexport type CameraState = CameraState2D | CameraState3D | CameraState2Point5D;\n\n/**\n * Actions for camera management\n */\ntype CameraActions = {\n /** Update camera state directly */\n setCameraState: (state: Partial<CameraState>) => void;\n};\n\n/**\n * Storage for initial camera state per instance.\n * Used for reset operations to restore to initial values.\n *\n * @internal These caches live outside the store factory because reset operations\n * need access to the original initial values. They are cleaned up via `onCleanup`\n * hook when the store instance is destroyed, and via `clearCameraState()` for\n * manual cleanup. Do NOT use `cameraStore.clear()` directly in tests - use\n * `clearCameraState()` instead to ensure proper cleanup.\n */\nconst initialStateCache = new Map<UniqueId, CameraStateInput>();\n\n/**\n * Track which instances have been initialized.\n * @internal See note on initialStateCache about cleanup.\n */\nconst initializedInstances = new Set<UniqueId>();\n\n/**\n * Input type for building camera state - simpler than union type\n */\ntype CameraStateInput = {\n latitude?: number;\n longitude?: number;\n zoom?: number;\n pitch?: number;\n rotation?: number;\n projection?: ProjectionType;\n view?: ViewType;\n};\n\n/**\n * Build a complete camera state from partial input.\n * Returns the appropriate discriminated union variant based on view/projection.\n */\nfunction buildCameraState(partial?: CameraStateInput): CameraState {\n const latitude = partial?.latitude ?? 0;\n const longitude = partial?.longitude ?? 0;\n const zoom = partial?.zoom ?? 0;\n const rotation = partial?.rotation ?? 0;\n\n // Determine which variant to build based on view/projection\n const is3D = partial?.view === '3D' || partial?.projection === 'globe';\n const is2Point5D = partial?.view === '2.5D';\n\n if (is3D) {\n // 3D view: globe projection, no pitch, no rotation\n return {\n latitude,\n longitude,\n zoom,\n pitch: 0,\n rotation: 0,\n projection: 'globe',\n view: '3D',\n } satisfies CameraState3D;\n }\n\n if (is2Point5D) {\n // 2.5D view: mercator projection, variable pitch\n return {\n latitude,\n longitude,\n zoom,\n pitch: partial?.pitch ?? 45,\n rotation,\n projection: 'mercator',\n view: '2.5D',\n } satisfies CameraState2Point5D;\n }\n\n // Default: 2D view, mercator projection, no pitch\n return {\n latitude,\n longitude,\n zoom,\n pitch: 0,\n rotation,\n projection: 'mercator',\n view: '2D',\n } satisfies CameraState2D;\n}\n\n/**\n * Default camera state\n */\nconst DEFAULT_CAMERA_STATE: CameraState = {\n latitude: 0,\n longitude: 0,\n zoom: 0,\n pitch: 0,\n rotation: 0,\n projection: 'mercator',\n view: '2D',\n};\n\n/**\n * Camera store instance\n */\nexport const cameraStore = createMapStore<CameraState, CameraActions>({\n defaultState: DEFAULT_CAMERA_STATE,\n\n actions: (_mapId, { get, replace }) => ({\n setCameraState: (updates: Partial<CameraState>) => {\n const currentState = get();\n // Use buildCameraState to ensure proper discriminated union type\n replace(buildCameraState({ ...currentState, ...updates }));\n },\n }),\n\n bus: (mapId, { get, replace }) => {\n const unsubReset = cameraBus.on(CameraEventTypes.reset, ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const initialState = initialStateCache.get(mapId);\n const newState = buildCameraState({\n latitude: state.latitude,\n longitude: state.longitude,\n projection: state.projection,\n view: state.view,\n zoom: payload.zoom === false ? state.zoom : (initialState?.zoom ?? 0),\n pitch:\n payload.pitch === false ? state.pitch : (initialState?.pitch ?? 0),\n rotation:\n payload.rotation === false\n ? state.rotation\n : (initialState?.rotation ?? 0),\n });\n\n replace(newState);\n });\n\n const unsubSetCenter = cameraBus.on(\n CameraEventTypes.setCenter,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n replace(\n buildCameraState({\n ...state,\n latitude: payload.latitude,\n longitude: payload.longitude,\n zoom: payload.zoom ?? state.zoom,\n rotation: payload.heading ?? state.rotation,\n pitch: payload.pitch ?? state.pitch,\n }),\n );\n },\n );\n\n const unsubFitBounds = cameraBus.on(\n CameraEventTypes.fitBounds,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const { longitude, latitude, zoom } = fitBounds({\n width: payload.width,\n height: payload.height,\n bounds: [\n [payload.bounds[0], payload.bounds[1]],\n [payload.bounds[2], payload.bounds[3]],\n ],\n padding: payload.padding,\n });\n\n replace(\n buildCameraState({\n ...state,\n latitude,\n longitude,\n zoom,\n rotation: payload.heading ?? state.rotation,\n pitch: payload.pitch ?? state.pitch,\n }),\n );\n },\n );\n\n const unsubSetProjection = cameraBus.on(\n CameraEventTypes.setProjection,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const newState = { ...state };\n newState.projection = payload.projection;\n if (payload.projection === 'globe') {\n newState.view = '3D';\n } else {\n newState.view = '2D';\n newState.pitch = 0;\n }\n replace(newState);\n },\n );\n\n const unsubSetView = cameraBus.on(\n CameraEventTypes.setView,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const newState = { ...state };\n newState.view = payload.view;\n if (payload.view === '3D') {\n newState.projection = 'globe';\n newState.pitch = 0;\n } else {\n newState.projection = 'mercator';\n }\n\n if (payload.view === '2.5D') {\n newState.pitch = 45;\n }\n replace(newState);\n },\n );\n\n const unsubSetZoom = cameraBus.on(\n CameraEventTypes.setZoom,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n replace({ ...state, zoom: payload.zoom });\n },\n );\n\n const unsubSetRotation = cameraBus.on(\n CameraEventTypes.setRotation,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n if (state.view !== '3D') {\n replace({ ...state, rotation: payload.rotation });\n }\n },\n );\n\n const unsubSetPitch = cameraBus.on(\n CameraEventTypes.setPitch,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n if (state.view === '2.5D') {\n replace({ ...state, pitch: payload.pitch });\n }\n },\n );\n\n return () => {\n unsubReset();\n unsubSetCenter();\n unsubFitBounds();\n unsubSetProjection();\n unsubSetView();\n unsubSetZoom();\n unsubSetRotation();\n unsubSetPitch();\n };\n },\n\n onCleanup: (mapId) => {\n initializedInstances.delete(mapId);\n initialStateCache.delete(mapId);\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Initialize camera state for a map instance with optional initial values.\n * Should be called before using the camera store for a given mapId.\n *\n * @param mapId - Unique identifier for the map instance\n * @param initialState - Optional initial camera state\n */\nexport function initializeCameraState(\n mapId: UniqueId,\n initialState?: CameraStateInput,\n): void {\n if (initializedInstances.has(mapId)) {\n return; // Already initialized\n }\n\n initializedInstances.add(mapId);\n if (initialState) {\n initialStateCache.set(mapId, initialState);\n }\n const builtState = buildCameraState(initialState);\n cameraStore.set(mapId, builtState);\n}\n\n/**\n * Hook to subscribe to camera state changes for a specific map.\n *\n * @param mapId - Unique identifier for the map instance\n * @param initialCameraState - Optional initial camera state (only used on first call)\n * @returns Camera state and setCameraState action\n *\n * @example\n * ```tsx\n * function MapInfo({ mapId }) {\n * const { cameraState, setCameraState } = useMapCamera(mapId);\n * return (\n * <div>\n * Lat: {cameraState.latitude.toFixed(2)}, Lon: {cameraState.longitude.toFixed(2)}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMapCamera(\n mapId: UniqueId,\n initialCameraState?: CameraStateInput,\n): {\n cameraState: CameraState;\n setCameraState: (state: Partial<CameraState>) => void;\n} {\n // Initialize on first use if initial state provided\n if (initialCameraState && !initializedInstances.has(mapId)) {\n initializeCameraState(mapId, initialCameraState);\n }\n\n const { state, setCameraState } = cameraStore.use(mapId);\n\n return { cameraState: state, setCameraState };\n}\n\n/**\n * Manually clear camera state for a specific map instance.\n *\n * @param mapId - The unique identifier for the map instance to clear\n */\nexport function clearCameraState(mapId: UniqueId): void {\n initializedInstances.delete(mapId);\n initialStateCache.delete(mapId);\n cameraStore.clear(mapId);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"store.js","names":["DEFAULT_CAMERA_STATE: CameraState"],"sources":["../../src/camera/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/**\n * Camera Store\n *\n * Manages camera state (position, zoom, pitch, rotation, projection, view) per map instance.\n * State is updated via bus events or direct actions.\n *\n * @example\n * ```tsx\n * import { cameraStore } from '@accelint/map-toolkit/camera';\n *\n * function MapInfo({ mapId }) {\n * const { state } = cameraStore.use(mapId);\n * return (\n * <div>\n * Lat: {state.latitude.toFixed(2)}, Lon: {state.longitude.toFixed(2)}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { fitBounds } from '@math.gl/web-mercator';\nimport { createMapStore } from '../shared/create-map-store';\nimport { CameraEventTypes } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { CameraEvent, ProjectionType, ViewType } from './types';\n\nconst cameraBus = Broadcast.getInstance<CameraEvent>();\n\n/**\n * Camera state for 2D view\n */\ntype CameraState2D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: 0;\n rotation: number;\n projection: 'mercator';\n view: '2D';\n};\n\n/**\n * Camera state for 3D view\n */\ntype CameraState3D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: 0;\n rotation: number;\n projection: 'globe';\n view: '3D';\n};\n\n/**\n * Camera state for 2.5D view\n */\ntype CameraState2Point5D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: number;\n rotation: number;\n projection: 'mercator';\n view: '2.5D';\n};\n\n/**\n * Union type for all camera states\n */\nexport type CameraState = CameraState2D | CameraState3D | CameraState2Point5D;\n\n/**\n * Actions for camera management\n */\ntype CameraActions = {\n /** Update camera state directly */\n setCameraState: (state: Partial<CameraState>) => void;\n};\n\n/**\n * Storage for initial camera state per instance.\n * Used for reset operations to restore to initial values.\n *\n * @internal These caches live outside the store factory because reset operations\n * need access to the original initial values. They are cleaned up via `onCleanup`\n * hook when the store instance is destroyed, and via `clearCameraState()` for\n * manual cleanup. Do NOT use `cameraStore.clear()` directly in tests - use\n * `clearCameraState()` instead to ensure proper cleanup.\n */\nconst initialStateCache = new Map<UniqueId, CameraStateInput>();\n\n/**\n * Track which instances have been initialized.\n * @internal See note on initialStateCache about cleanup.\n */\nconst initializedInstances = new Set<UniqueId>();\n\n/**\n * Input type for building camera state - simpler than union type\n */\ntype CameraStateInput = {\n latitude?: number;\n longitude?: number;\n zoom?: number;\n pitch?: number;\n rotation?: number;\n projection?: ProjectionType;\n view?: ViewType;\n};\n\n/**\n * Build a complete camera state from partial input.\n * Returns the appropriate discriminated union variant based on view/projection.\n *\n * This function ensures type-safe camera states by constructing the correct\n * discriminated union variant (2D, 2.5D, or 3D) based on view and projection\n * settings, applying appropriate defaults and constraints.\n *\n * @param partial - Optional partial camera state input\n * @returns Complete camera state matching one of the discriminated union variants\n *\n * @example\n * ```typescript\n * // Build 2D state (default)\n * const state2D = buildCameraState({\n * latitude: 40.7128,\n * longitude: -74.0060,\n * zoom: 10,\n * });\n * // Result: { ..., view: '2D', projection: 'mercator', pitch: 0 }\n *\n * // Build 2.5D state\n * const state2Point5D = buildCameraState({\n * latitude: 37.7749,\n * longitude: -122.4194,\n * zoom: 12,\n * view: '2.5D',\n * });\n * // Result: { ..., view: '2.5D', projection: 'mercator', pitch: 45 }\n *\n * // Build 3D state\n * const state3D = buildCameraState({\n * view: '3D',\n * zoom: 2,\n * });\n * // Result: { ..., view: '3D', projection: 'globe', pitch: 0, rotation: 0 }\n * ```\n */\nfunction buildCameraState(partial?: CameraStateInput): CameraState {\n const latitude = partial?.latitude ?? 0;\n const longitude = partial?.longitude ?? 0;\n const zoom = partial?.zoom ?? 0;\n const rotation = partial?.rotation ?? 0;\n\n // Determine which variant to build based on view/projection\n const is3D = partial?.view === '3D' || partial?.projection === 'globe';\n const is2Point5D = partial?.view === '2.5D';\n\n if (is3D) {\n // 3D view: globe projection, no pitch, no rotation\n return {\n latitude,\n longitude,\n zoom,\n pitch: 0,\n rotation: 0,\n projection: 'globe',\n view: '3D',\n } satisfies CameraState3D;\n }\n\n if (is2Point5D) {\n // 2.5D view: mercator projection, variable pitch\n return {\n latitude,\n longitude,\n zoom,\n pitch: partial?.pitch ?? 45,\n rotation,\n projection: 'mercator',\n view: '2.5D',\n } satisfies CameraState2Point5D;\n }\n\n // Default: 2D view, mercator projection, no pitch\n return {\n latitude,\n longitude,\n zoom,\n pitch: 0,\n rotation,\n projection: 'mercator',\n view: '2D',\n } satisfies CameraState2D;\n}\n\n/**\n * Default camera state\n */\nconst DEFAULT_CAMERA_STATE: CameraState = {\n latitude: 0,\n longitude: 0,\n zoom: 0,\n pitch: 0,\n rotation: 0,\n projection: 'mercator',\n view: '2D',\n};\n\n/**\n * Camera store instance\n */\nexport const cameraStore = createMapStore<CameraState, CameraActions>({\n defaultState: DEFAULT_CAMERA_STATE,\n\n actions: (_mapId, { get, replace }) => ({\n setCameraState: (updates: Partial<CameraState>) => {\n const currentState = get();\n // Use buildCameraState to ensure proper discriminated union type\n replace(buildCameraState({ ...currentState, ...updates }));\n },\n }),\n\n bus: (mapId, { get, replace }) => {\n const unsubReset = cameraBus.on(CameraEventTypes.reset, ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const initialState = initialStateCache.get(mapId);\n const newState = buildCameraState({\n latitude: state.latitude,\n longitude: state.longitude,\n projection: state.projection,\n view: state.view,\n zoom: payload.zoom === false ? state.zoom : (initialState?.zoom ?? 0),\n pitch:\n payload.pitch === false ? state.pitch : (initialState?.pitch ?? 0),\n rotation:\n payload.rotation === false\n ? state.rotation\n : (initialState?.rotation ?? 0),\n });\n\n replace(newState);\n });\n\n const unsubSetCenter = cameraBus.on(\n CameraEventTypes.setCenter,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n replace(\n buildCameraState({\n ...state,\n latitude: payload.latitude,\n longitude: payload.longitude,\n zoom: payload.zoom ?? state.zoom,\n rotation: payload.heading ?? state.rotation,\n pitch: payload.pitch ?? state.pitch,\n }),\n );\n },\n );\n\n const unsubFitBounds = cameraBus.on(\n CameraEventTypes.fitBounds,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const { longitude, latitude, zoom } = fitBounds({\n width: payload.width,\n height: payload.height,\n bounds: [\n [payload.bounds[0], payload.bounds[1]],\n [payload.bounds[2], payload.bounds[3]],\n ],\n padding: payload.padding,\n });\n\n replace(\n buildCameraState({\n ...state,\n latitude,\n longitude,\n zoom,\n rotation: payload.heading ?? state.rotation,\n pitch: payload.pitch ?? state.pitch,\n }),\n );\n },\n );\n\n const unsubSetProjection = cameraBus.on(\n CameraEventTypes.setProjection,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const newState = { ...state };\n newState.projection = payload.projection;\n if (payload.projection === 'globe') {\n newState.view = '3D';\n } else {\n newState.view = '2D';\n newState.pitch = 0;\n }\n replace(newState);\n },\n );\n\n const unsubSetView = cameraBus.on(\n CameraEventTypes.setView,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const newState = { ...state };\n newState.view = payload.view;\n if (payload.view === '3D') {\n newState.projection = 'globe';\n newState.pitch = 0;\n } else {\n newState.projection = 'mercator';\n }\n\n if (payload.view === '2.5D') {\n newState.pitch = 45;\n }\n replace(newState);\n },\n );\n\n const unsubSetZoom = cameraBus.on(\n CameraEventTypes.setZoom,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n replace({ ...state, zoom: payload.zoom });\n },\n );\n\n const unsubSetRotation = cameraBus.on(\n CameraEventTypes.setRotation,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n if (state.view !== '3D') {\n replace({ ...state, rotation: payload.rotation });\n }\n },\n );\n\n const unsubSetPitch = cameraBus.on(\n CameraEventTypes.setPitch,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n if (state.view === '2.5D') {\n replace({ ...state, pitch: payload.pitch });\n }\n },\n );\n\n return () => {\n unsubReset();\n unsubSetCenter();\n unsubFitBounds();\n unsubSetProjection();\n unsubSetView();\n unsubSetZoom();\n unsubSetRotation();\n unsubSetPitch();\n };\n },\n\n onCleanup: (mapId) => {\n initializedInstances.delete(mapId);\n initialStateCache.delete(mapId);\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Initialize camera state for a map instance with optional initial values.\n * Should be called before using the camera store for a given mapId.\n *\n * @param mapId - Unique identifier for the map instance\n * @param initialState - Optional initial camera state\n * @returns void\n *\n * @example\n * ```typescript\n * import { uuid } from '@accelint/core';\n * import { initializeCameraState } from '@accelint/map-toolkit/camera';\n *\n * const mapId = uuid();\n *\n * // Initialize with default state\n * initializeCameraState(mapId);\n *\n * // Initialize with custom state\n * initializeCameraState(mapId, {\n * latitude: 37.7749,\n * longitude: -122.4194,\n * zoom: 10,\n * view: '2.5D',\n * pitch: 45,\n * });\n * ```\n */\nexport function initializeCameraState(\n mapId: UniqueId,\n initialState?: CameraStateInput,\n): void {\n if (initializedInstances.has(mapId)) {\n return; // Already initialized\n }\n\n initializedInstances.add(mapId);\n if (initialState) {\n initialStateCache.set(mapId, initialState);\n }\n const builtState = buildCameraState(initialState);\n cameraStore.set(mapId, builtState);\n}\n\n/**\n * Hook to subscribe to camera state changes for a specific map.\n *\n * @param mapId - Unique identifier for the map instance\n * @param initialCameraState - Optional initial camera state (only used on first call)\n * @returns Camera state and setCameraState action\n *\n * @example\n * ```tsx\n * function MapInfo({ mapId }) {\n * const { cameraState, setCameraState } = useMapCamera(mapId);\n * return (\n * <div>\n * Lat: {cameraState.latitude.toFixed(2)}, Lon: {cameraState.longitude.toFixed(2)}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMapCamera(\n mapId: UniqueId,\n initialCameraState?: CameraStateInput,\n): {\n cameraState: CameraState;\n setCameraState: (state: Partial<CameraState>) => void;\n} {\n // Initialize on first use if initial state provided\n if (initialCameraState && !initializedInstances.has(mapId)) {\n initializeCameraState(mapId, initialCameraState);\n }\n\n const { state, setCameraState } = cameraStore.use(mapId);\n\n return { cameraState: state, setCameraState };\n}\n\n/**\n * Manually clear camera state for a specific map instance.\n *\n * Removes all cached state including initial values and subscription tracking.\n * Useful for cleanup when dynamically destroying map instances.\n *\n * @param mapId - The unique identifier for the map instance to clear\n * @returns void\n *\n * @example\n * ```typescript\n * import { clearCameraState } from '@accelint/map-toolkit/camera';\n *\n * // Clean up when removing a map\n * function removeMap(mapId: UniqueId) {\n * clearCameraState(mapId);\n * // ... remove map component\n * }\n * ```\n *\n * @example\n * ```typescript\n * import { clearCameraState } from '@accelint/map-toolkit/camera';\n * import { afterEach } from 'vitest';\n *\n * // Clean up in tests\n * afterEach(() => {\n * clearCameraState('test-map-id');\n * });\n * ```\n */\nexport function clearCameraState(mapId: UniqueId): void {\n initializedInstances.delete(mapId);\n initialStateCache.delete(mapId);\n cameraStore.clear(mapId);\n}\n"],"mappings":"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"}
|
package/dist/camera/types.d.ts
CHANGED
|
@@ -16,8 +16,24 @@ import { Payload } from "@accelint/bus";
|
|
|
16
16
|
import { UniqueId } from "@accelint/core/utility/uuid";
|
|
17
17
|
|
|
18
18
|
//#region src/camera/types.d.ts
|
|
19
|
+
/**
|
|
20
|
+
* Map projection types supported by the camera system.
|
|
21
|
+
*
|
|
22
|
+
* - `mercator`: Web Mercator projection for 2D and 2.5D views
|
|
23
|
+
* - `globe`: Spherical globe projection for 3D views
|
|
24
|
+
*/
|
|
19
25
|
type ProjectionType = 'mercator' | 'globe';
|
|
26
|
+
/**
|
|
27
|
+
* Camera view modes defining perspective and interaction capabilities.
|
|
28
|
+
*
|
|
29
|
+
* - `2D`: Traditional flat map view with rotation
|
|
30
|
+
* - `2.5D`: Tilted perspective view with pitch and rotation
|
|
31
|
+
* - `3D`: Globe view with fixed orientation
|
|
32
|
+
*/
|
|
20
33
|
type ViewType = '2D' | '2.5D' | '3D';
|
|
34
|
+
/**
|
|
35
|
+
* Payload for setting camera center position.
|
|
36
|
+
*/
|
|
21
37
|
type CameraSetCenterPayload = {
|
|
22
38
|
/** Identifier of the camera */
|
|
23
39
|
id: UniqueId;
|
|
@@ -32,6 +48,9 @@ type CameraSetCenterPayload = {
|
|
|
32
48
|
/** Optional pitch angle */
|
|
33
49
|
pitch?: number;
|
|
34
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Payload for fitting camera to show a bounding box.
|
|
53
|
+
*/
|
|
35
54
|
type CameraFitBoundsPayload = {
|
|
36
55
|
/** Identifier of the camera */
|
|
37
56
|
id: UniqueId;
|
|
@@ -48,36 +67,98 @@ type CameraFitBoundsPayload = {
|
|
|
48
67
|
/** Optional pitch angle */
|
|
49
68
|
pitch?: number;
|
|
50
69
|
};
|
|
70
|
+
/**
|
|
71
|
+
* Payload for resetting camera to initial state.
|
|
72
|
+
*
|
|
73
|
+
* Set property to `false` to preserve that property during reset.
|
|
74
|
+
* By default, all properties are reset to initial values.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* import type { CameraResetPayload } from '@accelint/map-toolkit/camera';
|
|
79
|
+
*
|
|
80
|
+
* // Full reset
|
|
81
|
+
* const fullReset: CameraResetPayload = {
|
|
82
|
+
* id: 'map-1',
|
|
83
|
+
* };
|
|
84
|
+
*
|
|
85
|
+
* // Reset but keep current zoom
|
|
86
|
+
* const keepZoom: CameraResetPayload = {
|
|
87
|
+
* id: 'map-1',
|
|
88
|
+
* zoom: false,
|
|
89
|
+
* };
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
51
92
|
type CameraResetPayload = {
|
|
52
93
|
/** Identifier of the camera */
|
|
53
94
|
id: UniqueId;
|
|
95
|
+
/** Set to false to preserve current zoom during reset */
|
|
54
96
|
zoom?: boolean;
|
|
97
|
+
/** Set to false to preserve current pitch during reset */
|
|
55
98
|
pitch?: boolean;
|
|
99
|
+
/** Set to false to preserve current rotation during reset */
|
|
56
100
|
rotation?: boolean;
|
|
57
101
|
};
|
|
102
|
+
/**
|
|
103
|
+
* Event type for setting camera center position.
|
|
104
|
+
*/
|
|
58
105
|
type CameraSetCenterEvent = Payload<typeof CameraEventTypes.setCenter, CameraSetCenterPayload>;
|
|
106
|
+
/**
|
|
107
|
+
* Event type for fitting camera to bounds.
|
|
108
|
+
*/
|
|
59
109
|
type CameraFitBoundsEvent = Payload<typeof CameraEventTypes.fitBounds, CameraFitBoundsPayload>;
|
|
110
|
+
/**
|
|
111
|
+
* Event type for setting camera projection.
|
|
112
|
+
*/
|
|
60
113
|
type CameraSetProjectionEvent = Payload<typeof CameraEventTypes.setProjection, {
|
|
61
114
|
id: UniqueId;
|
|
62
115
|
projection: ProjectionType;
|
|
63
116
|
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Event type for setting camera view mode.
|
|
119
|
+
*/
|
|
64
120
|
type CameraSetViewEvent = Payload<typeof CameraEventTypes.setView, {
|
|
65
121
|
id: UniqueId;
|
|
66
122
|
view: ViewType;
|
|
67
123
|
}>;
|
|
124
|
+
/**
|
|
125
|
+
* Event type for setting camera zoom level.
|
|
126
|
+
*/
|
|
68
127
|
type CameraSetZoomEvent = Payload<typeof CameraEventTypes.setZoom, {
|
|
69
128
|
id: UniqueId;
|
|
70
129
|
zoom: number;
|
|
71
130
|
}>;
|
|
131
|
+
/**
|
|
132
|
+
* Event type for setting camera rotation angle.
|
|
133
|
+
*/
|
|
72
134
|
type CameraSetRotationEvent = Payload<typeof CameraEventTypes.setRotation, {
|
|
73
135
|
id: UniqueId;
|
|
74
136
|
rotation: number;
|
|
75
137
|
}>;
|
|
138
|
+
/**
|
|
139
|
+
* Event type for setting camera pitch angle.
|
|
140
|
+
*/
|
|
76
141
|
type CameraSetPitchEvent = Payload<typeof CameraEventTypes.setPitch, {
|
|
77
142
|
id: UniqueId;
|
|
78
143
|
pitch: number;
|
|
79
144
|
}>;
|
|
145
|
+
/**
|
|
146
|
+
* Event type for resetting camera to initial state.
|
|
147
|
+
*/
|
|
80
148
|
type CameraResetEvent = Payload<typeof CameraEventTypes.reset, CameraResetPayload>;
|
|
149
|
+
/**
|
|
150
|
+
* Union type of all camera events.
|
|
151
|
+
*
|
|
152
|
+
* Use this type when registering bus listeners that handle multiple camera events.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* import { Broadcast } from '@accelint/bus';
|
|
157
|
+
* import type { CameraEvent } from '@accelint/map-toolkit/camera';
|
|
158
|
+
*
|
|
159
|
+
* const bus = Broadcast.getInstance<CameraEvent>();
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
81
162
|
type CameraEvent = CameraSetCenterEvent | CameraFitBoundsEvent | CameraSetProjectionEvent | CameraSetViewEvent | CameraSetZoomEvent | CameraSetRotationEvent | CameraSetPitchEvent | CameraResetEvent;
|
|
82
163
|
//#endregion
|
|
83
164
|
export { CameraEvent, CameraFitBoundsEvent, CameraFitBoundsPayload, CameraResetEvent, CameraResetPayload, CameraSetCenterEvent, CameraSetCenterPayload, CameraSetPitchEvent, CameraSetProjectionEvent, CameraSetRotationEvent, CameraSetViewEvent, CameraSetZoomEvent, ProjectionType, ViewType };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/cursor-coordinates/constants.d.ts
|
|
2
|
+
declare const MAX_LONGITUDE = 180;
|
|
3
|
+
declare const LONGITUDE_RANGE = 360;
|
|
4
|
+
declare const DEFAULT_LATLON_COORDS = "--, --";
|
|
5
|
+
declare const DEFAULT_MGRS_UTM_COORDS = "--- -- ---- ----";
|
|
6
|
+
//#endregion
|
|
7
|
+
export { DEFAULT_LATLON_COORDS, DEFAULT_MGRS_UTM_COORDS, LONGITUDE_RANGE, MAX_LONGITUDE };
|
|
8
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
//#region src/cursor-coordinates/constants.ts
|
|
15
|
+
const MAX_LONGITUDE = 180;
|
|
16
|
+
const LONGITUDE_RANGE = 360;
|
|
17
|
+
const DEFAULT_LATLON_COORDS = "--, --";
|
|
18
|
+
const DEFAULT_MGRS_UTM_COORDS = "--- -- ---- ----";
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { DEFAULT_LATLON_COORDS, DEFAULT_MGRS_UTM_COORDS, LONGITUDE_RANGE, MAX_LONGITUDE };
|
|
22
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../src/cursor-coordinates/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\nexport const MAX_LONGITUDE = 180;\nexport const LONGITUDE_RANGE = 360;\nexport const DEFAULT_LATLON_COORDS = '--, --';\nexport const DEFAULT_MGRS_UTM_COORDS = '--- -- ---- ----';\n"],"mappings":";;;;;;;;;;;;;;AAYA,MAAa,gBAAgB;AAC7B,MAAa,kBAAkB;AAC/B,MAAa,wBAAwB;AACrC,MAAa,0BAA0B"}
|
|
@@ -35,6 +35,7 @@ declare const cursorCoordinateStore: MapStore<CursorCoordinateState, CursorCoord
|
|
|
35
35
|
* Use this only in advanced scenarios where manual cleanup is required.
|
|
36
36
|
*
|
|
37
37
|
* @param instanceId - The unique identifier for the map to clear
|
|
38
|
+
* @returns void
|
|
38
39
|
*
|
|
39
40
|
* @example
|
|
40
41
|
* ```tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","names":[],"sources":["../../src/cursor-coordinates/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/**\n * Cursor Coordinates Store\n *\n * Manages cursor coordinate state for map instances using the shared store factory.\n * Subscribes to hover events from the map and stores the current cursor position.\n *\n * @example\n * ```tsx\n * import { cursorCoordinateStore } from './store';\n *\n * // Use in a component\n * function CoordinateDisplay({ mapId }) {\n * const { state, setFormat } = cursorCoordinateStore.use(mapId);\n * return <div>{state.coordinate ? `${state.coordinate[0]}, ${state.coordinate[1]}` : '--'}</div>;\n * }\n *\n * // Or use the selector for specific values\n * const coord = cursorCoordinateStore.useSelector(mapId, (s) => s.coordinate);\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { MapEvents } from '../deckgl/base-map/events';\nimport { createMapStore } from '../shared/create-map-store';\nimport type { UniqueId } from '@accelint/core';\nimport type { MapEventType, MapHoverEvent } from '../deckgl/base-map/types';\nimport type { CoordinateFormatTypes, CursorCoordinateState } from './types';\n\nconst bus = Broadcast.getInstance<MapEventType>();\n\n/**\n * Type guard to validate that a value is a proper coordinate tuple.\n * Checks that the value is an array with exactly two finite numbers.\n *\n * @param value - Value to validate as a coordinate\n * @returns True if value is a valid [longitude, latitude] tuple\n */\nfunction isValidCoordinate(value?: number[]): value is [number, number] {\n return (\n Array.isArray(value) && value.length === 2 && value.every(Number.isFinite)\n );\n}\n\n/**\n * Actions for cursor coordinate management\n */\ntype CursorCoordinateActions = {\n /** Set the coordinate display format */\n setFormat: (format: CoordinateFormatTypes) => void;\n};\n\n/**\n * Cursor coordinates store\n *\n * Stores the current cursor position and display format for each map instance.\n * Automatically subscribes to hover events when first React subscriber mounts.\n */\nexport const cursorCoordinateStore = createMapStore<\n CursorCoordinateState,\n CursorCoordinateActions\n>({\n defaultState: {\n coordinate: null,\n format: 'dd',\n },\n\n actions: (_mapId, { set }) => ({\n setFormat: (format: CoordinateFormatTypes) => {\n set({ format });\n },\n }),\n\n bus: (mapId, { set }) => {\n return bus.on(MapEvents.hover, (data: MapHoverEvent) => {\n const eventId = data.payload.id;\n\n // Ignore hover events from other map instances\n if (mapId !== eventId) {\n return;\n }\n\n const coords = data.payload.info.coordinate;\n\n // Update coordinate if valid, or clear if invalid\n if (isValidCoordinate(coords)) {\n set({ coordinate: coords as [number, number] });\n } else {\n set({ coordinate: null });\n }\n });\n },\n});\n\n/**\n * Clear cursor coordinate state for a specific map instance.\n * This is typically not needed as cleanup happens automatically when all subscribers unmount.\n * Use this only in advanced scenarios where manual cleanup is required.\n *\n * @param instanceId - The unique identifier for the map to clear\n *\n * @example\n * ```tsx\n * // Manual cleanup (rarely needed)\n * clearCursorCoordinateState('my-map-instance');\n * ```\n */\nexport function clearCursorCoordinateState(instanceId: UniqueId): void {\n cursorCoordinateStore.clear(instanceId);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAM,MAAM,UAAU,aAA2B;;;;;;;;AASjD,SAAS,kBAAkB,OAA6C;AACtE,QACE,MAAM,QAAQ,MAAM,IAAI,MAAM,WAAW,KAAK,MAAM,MAAM,OAAO,SAAS;;;;;;;;AAkB9E,MAAa,wBAAwB,eAGnC;CACA,cAAc;EACZ,YAAY;EACZ,QAAQ;EACT;CAED,UAAU,QAAQ,EAAE,WAAW,EAC7B,YAAY,WAAkC;AAC5C,MAAI,EAAE,QAAQ,CAAC;IAElB;CAED,MAAM,OAAO,EAAE,UAAU;AACvB,SAAO,IAAI,GAAG,UAAU,QAAQ,SAAwB;AAItD,OAAI,UAHY,KAAK,QAAQ,GAI3B;GAGF,MAAM,SAAS,KAAK,QAAQ,KAAK;AAGjC,OAAI,kBAAkB,OAAO,CAC3B,KAAI,EAAE,YAAY,QAA4B,CAAC;OAE/C,KAAI,EAAE,YAAY,MAAM,CAAC;IAE3B;;CAEL,CAAC
|
|
1
|
+
{"version":3,"file":"store.js","names":[],"sources":["../../src/cursor-coordinates/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/**\n * Cursor Coordinates Store\n *\n * Manages cursor coordinate state for map instances using the shared store factory.\n * Subscribes to hover events from the map and stores the current cursor position.\n *\n * @example\n * ```tsx\n * import { cursorCoordinateStore } from './store';\n *\n * // Use in a component\n * function CoordinateDisplay({ mapId }) {\n * const { state, setFormat } = cursorCoordinateStore.use(mapId);\n * return <div>{state.coordinate ? `${state.coordinate[0]}, ${state.coordinate[1]}` : '--'}</div>;\n * }\n *\n * // Or use the selector for specific values\n * const coord = cursorCoordinateStore.useSelector(mapId, (s) => s.coordinate);\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { MapEvents } from '../deckgl/base-map/events';\nimport { createMapStore } from '../shared/create-map-store';\nimport type { UniqueId } from '@accelint/core';\nimport type { MapEventType, MapHoverEvent } from '../deckgl/base-map/types';\nimport type { CoordinateFormatTypes, CursorCoordinateState } from './types';\n\nconst bus = Broadcast.getInstance<MapEventType>();\n\n/**\n * Type guard to validate that a value is a proper coordinate tuple.\n * Checks that the value is an array with exactly two finite numbers.\n *\n * @param value - Value to validate as a coordinate\n * @returns True if value is a valid [longitude, latitude] tuple\n */\nfunction isValidCoordinate(value?: number[]): value is [number, number] {\n return (\n Array.isArray(value) && value.length === 2 && value.every(Number.isFinite)\n );\n}\n\n/**\n * Actions for cursor coordinate management\n */\ntype CursorCoordinateActions = {\n /** Set the coordinate display format */\n setFormat: (format: CoordinateFormatTypes) => void;\n};\n\n/**\n * Cursor coordinates store\n *\n * Stores the current cursor position and display format for each map instance.\n * Automatically subscribes to hover events when first React subscriber mounts.\n */\nexport const cursorCoordinateStore = createMapStore<\n CursorCoordinateState,\n CursorCoordinateActions\n>({\n defaultState: {\n coordinate: null,\n format: 'dd',\n },\n\n actions: (_mapId, { set }) => ({\n setFormat: (format: CoordinateFormatTypes) => {\n set({ format });\n },\n }),\n\n bus: (mapId, { set }) => {\n return bus.on(MapEvents.hover, (data: MapHoverEvent) => {\n const eventId = data.payload.id;\n\n // Ignore hover events from other map instances\n if (mapId !== eventId) {\n return;\n }\n\n const coords = data.payload.info.coordinate;\n\n // Update coordinate if valid, or clear if invalid\n if (isValidCoordinate(coords)) {\n set({ coordinate: coords as [number, number] });\n } else {\n set({ coordinate: null });\n }\n });\n },\n});\n\n/**\n * Clear cursor coordinate state for a specific map instance.\n * This is typically not needed as cleanup happens automatically when all subscribers unmount.\n * Use this only in advanced scenarios where manual cleanup is required.\n *\n * @param instanceId - The unique identifier for the map to clear\n * @returns void\n *\n * @example\n * ```tsx\n * // Manual cleanup (rarely needed)\n * clearCursorCoordinateState('my-map-instance');\n * ```\n */\nexport function clearCursorCoordinateState(instanceId: UniqueId): void {\n cursorCoordinateStore.clear(instanceId);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAM,MAAM,UAAU,aAA2B;;;;;;;;AASjD,SAAS,kBAAkB,OAA6C;AACtE,QACE,MAAM,QAAQ,MAAM,IAAI,MAAM,WAAW,KAAK,MAAM,MAAM,OAAO,SAAS;;;;;;;;AAkB9E,MAAa,wBAAwB,eAGnC;CACA,cAAc;EACZ,YAAY;EACZ,QAAQ;EACT;CAED,UAAU,QAAQ,EAAE,WAAW,EAC7B,YAAY,WAAkC;AAC5C,MAAI,EAAE,QAAQ,CAAC;IAElB;CAED,MAAM,OAAO,EAAE,UAAU;AACvB,SAAO,IAAI,GAAG,UAAU,QAAQ,SAAwB;AAItD,OAAI,UAHY,KAAK,QAAQ,GAI3B;GAGF,MAAM,SAAS,KAAK,QAAQ,KAAK;AAGjC,OAAI,kBAAkB,OAAO,CAC3B,KAAI,EAAE,YAAY,QAA4B,CAAC;OAE/C,KAAI,EAAE,YAAY,MAAM,CAAC;IAE3B;;CAEL,CAAC;;;;;;;;;;;;;;;AAgBF,SAAgB,2BAA2B,YAA4B;AACrE,uBAAsB,MAAM,WAAW"}
|
|
@@ -29,6 +29,11 @@ import { UniqueId } from "@accelint/core";
|
|
|
29
29
|
* @returns Object containing the formatted coordinate string, raw coordinate, format setter, and current format
|
|
30
30
|
* @throws {Error} When no id is provided and hook is used outside MapProvider context
|
|
31
31
|
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* **UTM/MGRS Limitations:** UTM and MGRS coordinate systems are only valid between 80°S and 84°N.
|
|
34
|
+
* Coordinates outside this range (e.g., polar regions) will display the default placeholder `--, --`.
|
|
35
|
+
* Other formats (DD, DDM, DMS) work correctly at all latitudes.
|
|
36
|
+
*
|
|
32
37
|
* @example
|
|
33
38
|
* Basic usage:
|
|
34
39
|
* ```tsx
|