@accelint/map-toolkit 0.4.0 → 0.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 +52 -24
- package/README.md +3 -0
- package/catalog-info.yaml +6 -7
- package/dist/_virtual/rolldown_runtime.js +22 -0
- package/dist/camera/events.d.ts +15 -0
- package/dist/camera/events.js +29 -0
- package/dist/camera/events.js.map +1 -0
- package/dist/camera/index.d.ts +16 -0
- package/dist/camera/index.js +17 -0
- package/dist/camera/types.d.ts +84 -0
- package/dist/camera/types.js +12 -0
- package/dist/camera/use-camera-state.d.ts +153 -0
- package/dist/camera/use-camera-state.js +419 -0
- package/dist/camera/use-camera-state.js.map +1 -0
- package/dist/cursor-coordinates/use-cursor-coordinates.js +1 -1
- package/dist/deckgl/base-map/constants.d.ts +6 -1
- package/dist/deckgl/base-map/constants.js +6 -1
- package/dist/deckgl/base-map/constants.js.map +1 -1
- package/dist/deckgl/base-map/controls.d.ts +34 -0
- package/dist/deckgl/base-map/controls.js +50 -0
- package/dist/deckgl/base-map/controls.js.map +1 -0
- package/dist/deckgl/base-map/events.d.ts +4 -0
- package/dist/deckgl/base-map/events.js +5 -1
- package/dist/deckgl/base-map/events.js.map +1 -1
- package/dist/deckgl/base-map/index.d.ts +8 -23
- package/dist/deckgl/base-map/index.js +82 -42
- 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/types.d.ts +43 -2
- package/dist/deckgl/index.d.ts +5 -4
- package/dist/deckgl/index.js +2 -1
- package/dist/deckgl/saved-viewports/index.d.ts +32 -0
- package/dist/deckgl/saved-viewports/index.js +52 -0
- package/dist/deckgl/saved-viewports/index.js.map +1 -0
- package/dist/deckgl/saved-viewports/storage.d.ts +21 -0
- package/dist/deckgl/saved-viewports/storage.js +39 -0
- package/dist/deckgl/saved-viewports/storage.js.map +1 -0
- package/dist/maplibre/hooks/use-maplibre.d.ts +2 -2
- package/dist/maplibre/hooks/use-maplibre.js +2 -2
- package/dist/maplibre/hooks/use-maplibre.js.map +1 -1
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/assert.js +21 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/assert.js.map +1 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fit-bounds.js +63 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fit-bounds.js.map +1 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fly-to-viewport.js +14 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/get-bounds.js +20 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/get-bounds.js.map +1 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/index.js +19 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/math-utils.js +25 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/math-utils.js.map +1 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/normalize-viewport-props.js +14 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/web-mercator-utils.js +59 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/web-mercator-utils.js.map +1 -0
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/web-mercator-viewport.js +16 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/attribution-control.js +29 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/attribution-control.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/fullscreen-control.js +29 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/fullscreen-control.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/geolocate-control.js +54 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/geolocate-control.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/layer.js +15 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/logo-control.js +29 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/logo-control.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/map.js +91 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/map.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/marker.js +88 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/marker.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/navigation-control.js +29 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/navigation-control.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/popup.js +69 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/popup.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/scale-control.js +35 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/scale-control.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/source.js +15 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/terrain-control.js +29 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/terrain-control.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-control.js +40 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-control.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-map.js +23 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-map.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/index.js +27 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/create-ref.js +57 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/create-ref.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/maplibre.js +343 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/maplibre.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/apply-react-style.js +28 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/apply-react-style.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/compare-class-names.js +31 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/compare-class-names.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/deep-equal.js +57 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/deep-equal.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/set-globals.js +30 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/set-globals.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/style-utils.js +53 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/style-utils.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/transform.js +52 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/transform.js.map +1 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/use-isomorphic-layout-effect.js +22 -0
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/use-isomorphic-layout-effect.js.map +1 -0
- package/dist/node_modules/.pnpm/immer@10.2.0/node_modules/immer/dist/immer.js +812 -0
- package/dist/node_modules/.pnpm/immer@10.2.0/node_modules/immer/dist/immer.js.map +1 -0
- package/dist/node_modules/.pnpm/radashi@12.7.1/node_modules/radashi/dist/radashi.js +35 -0
- package/dist/node_modules/.pnpm/radashi@12.7.1/node_modules/radashi/dist/radashi.js.map +1 -0
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.development.js +195 -0
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.development.js.map +1 -0
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.production.js +76 -0
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.production.js.map +1 -0
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/index.js +39 -0
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/index.js.map +1 -0
- package/dist/node_modules/.pnpm/react-map-gl@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/react-map-gl/dist/maplibre.js +16 -0
- package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/middleware/immer.js +27 -0
- package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/middleware/immer.js.map +1 -0
- package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/vanilla.js +45 -0
- package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/vanilla.js.map +1 -0
- package/dist/packages/hotkey-manager/dist/actions/register-hotkey/index.js +78 -0
- package/dist/packages/hotkey-manager/dist/actions/register-hotkey/index.js.map +1 -0
- package/dist/packages/hotkey-manager/dist/constants.js +47 -0
- package/dist/packages/hotkey-manager/dist/constants.js.map +1 -0
- package/dist/packages/hotkey-manager/dist/enums/keycode.js +130 -0
- package/dist/packages/hotkey-manager/dist/enums/keycode.js.map +1 -0
- package/dist/packages/hotkey-manager/dist/lib/is-client/index.js +22 -0
- package/dist/packages/hotkey-manager/dist/lib/is-client/index.js.map +1 -0
- package/dist/packages/hotkey-manager/dist/lib/is-mac/index.js +24 -0
- package/dist/packages/hotkey-manager/dist/lib/is-mac/index.js.map +1 -0
- package/dist/packages/hotkey-manager/dist/lib/key-to-id/index.js +39 -0
- package/dist/packages/hotkey-manager/dist/lib/key-to-id/index.js.map +1 -0
- package/dist/packages/hotkey-manager/dist/lib/key-to-string/index.js +27 -0
- package/dist/packages/hotkey-manager/dist/lib/key-to-string/index.js.map +1 -0
- package/dist/packages/hotkey-manager/dist/stores/hotkey-store/index.js +95 -0
- package/dist/packages/hotkey-manager/dist/stores/hotkey-store/index.js.map +1 -0
- package/package.json +98 -85
|
@@ -0,0 +1,419 @@
|
|
|
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
|
+
import { CameraEventTypes } from "./events.js";
|
|
15
|
+
import { fitBounds } from "../node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fit-bounds.js";
|
|
16
|
+
import "../node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/index.js";
|
|
17
|
+
import { Broadcast } from "@accelint/bus";
|
|
18
|
+
import { useMemo, useSyncExternalStore } from "react";
|
|
19
|
+
|
|
20
|
+
//#region src/camera/use-camera-state.ts
|
|
21
|
+
/**
|
|
22
|
+
* Typed event bus instance for camera events.
|
|
23
|
+
* Provides type-safe event emission and listening for all camera state changes.
|
|
24
|
+
*/
|
|
25
|
+
const cameraBus = Broadcast.getInstance();
|
|
26
|
+
/**
|
|
27
|
+
* Store for camera state keyed by instanceId
|
|
28
|
+
*/
|
|
29
|
+
const cameraStore = /* @__PURE__ */ new Map();
|
|
30
|
+
/**
|
|
31
|
+
* Track React component subscribers per instanceId (for fan-out notifications).
|
|
32
|
+
* Each Set contains onStoreChange callbacks from useSyncExternalStore.
|
|
33
|
+
*/
|
|
34
|
+
const componentSubscribers = /* @__PURE__ */ new Map();
|
|
35
|
+
/**
|
|
36
|
+
* Cache of bus unsubscribe functions (1 per instanceId).
|
|
37
|
+
* This ensures we only have one bus listener per camera, regardless of
|
|
38
|
+
* how many React components subscribe to it.
|
|
39
|
+
*/
|
|
40
|
+
const busUnsubscribers = /* @__PURE__ */ new Map();
|
|
41
|
+
/**
|
|
42
|
+
* Cache of subscription functions per instanceId to avoid recreating on every render
|
|
43
|
+
*/
|
|
44
|
+
const subscriptionCache = /* @__PURE__ */ new Map();
|
|
45
|
+
/**
|
|
46
|
+
* Cache of snapshot functions per instanceId to maintain referential stability
|
|
47
|
+
*/
|
|
48
|
+
const snapshotCache = /* @__PURE__ */ new Map();
|
|
49
|
+
/**
|
|
50
|
+
* Cache of fallback snapshots per instanceId to maintain referential stability.
|
|
51
|
+
* This prevents unnecessary re-renders when no camera data exists yet.
|
|
52
|
+
*/
|
|
53
|
+
const fallbackCache = /* @__PURE__ */ new Map();
|
|
54
|
+
function buildInitialCameraState(initialCameraState) {
|
|
55
|
+
if (!initialCameraState) return {
|
|
56
|
+
latitude: 0,
|
|
57
|
+
longitude: 0,
|
|
58
|
+
zoom: 0,
|
|
59
|
+
pitch: 0,
|
|
60
|
+
rotation: 0,
|
|
61
|
+
projection: "mercator",
|
|
62
|
+
view: "2D"
|
|
63
|
+
};
|
|
64
|
+
const is2Point5D = initialCameraState.view === "2.5D";
|
|
65
|
+
const is3D = initialCameraState.view === "3D" || initialCameraState.projection === "globe";
|
|
66
|
+
let projection;
|
|
67
|
+
let view;
|
|
68
|
+
if (is3D) {
|
|
69
|
+
projection = "globe";
|
|
70
|
+
view = "3D";
|
|
71
|
+
} else if (is2Point5D) {
|
|
72
|
+
projection = "mercator";
|
|
73
|
+
view = "2.5D";
|
|
74
|
+
} else {
|
|
75
|
+
projection = initialCameraState.projection ?? "mercator";
|
|
76
|
+
view = initialCameraState.view ?? "2D";
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
latitude: initialCameraState.latitude ?? 0,
|
|
80
|
+
longitude: initialCameraState.longitude ?? 0,
|
|
81
|
+
zoom: initialCameraState.zoom ?? 0,
|
|
82
|
+
pitch: is2Point5D ? initialCameraState.pitch ?? 45 : 0,
|
|
83
|
+
rotation: is3D ? 0 : initialCameraState.rotation ?? 0,
|
|
84
|
+
projection,
|
|
85
|
+
view
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function getOrCreateState(instanceId, initialCameraState) {
|
|
89
|
+
if (!cameraStore.has(instanceId)) cameraStore.set(instanceId, buildInitialCameraState(initialCameraState));
|
|
90
|
+
return cameraStore.get(instanceId);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Notify all React subscribers for a given instanceId
|
|
94
|
+
*/
|
|
95
|
+
function notifySubscribers(instanceId) {
|
|
96
|
+
const subscribers = componentSubscribers.get(instanceId);
|
|
97
|
+
if (subscribers) for (const onStoreChange of subscribers) onStoreChange();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Ensures a single bus listener exists for the given instanceId.
|
|
101
|
+
* All React subscribers will be notified via fan-out when the bus event fires.
|
|
102
|
+
* This prevents creating N bus listeners for N React components.
|
|
103
|
+
*
|
|
104
|
+
* @param instanceId - The unique identifier for the camera
|
|
105
|
+
* @param initialCameraState - Optional initial camera state to set when creating the listener
|
|
106
|
+
*/
|
|
107
|
+
function ensureBusListener(instanceId, initialCameraState) {
|
|
108
|
+
if (busUnsubscribers.has(instanceId)) return;
|
|
109
|
+
const unsubResetCamera = cameraBus.on(CameraEventTypes.reset, ({ payload }) => {
|
|
110
|
+
if (instanceId === payload.id) {
|
|
111
|
+
const state = getOrCreateState(instanceId, initialCameraState);
|
|
112
|
+
const newState = { ...buildInitialCameraState({
|
|
113
|
+
...state,
|
|
114
|
+
zoom: payload.zoom === false ? state.zoom : initialCameraState?.zoom,
|
|
115
|
+
pitch: payload.pitch === false ? state.pitch : initialCameraState?.pitch,
|
|
116
|
+
rotation: payload.rotation === false ? state.rotation : initialCameraState?.rotation
|
|
117
|
+
}) };
|
|
118
|
+
cameraStore.set(instanceId, newState);
|
|
119
|
+
notifySubscribers(instanceId);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
const unsubSetCenter = cameraBus.on(CameraEventTypes.setCenter, ({ payload }) => {
|
|
123
|
+
if (instanceId === payload.id) {
|
|
124
|
+
const state = getOrCreateState(instanceId, initialCameraState);
|
|
125
|
+
const newState = {
|
|
126
|
+
...state,
|
|
127
|
+
latitude: payload.latitude,
|
|
128
|
+
longitude: payload.longitude,
|
|
129
|
+
zoom: payload.zoom ?? state.zoom,
|
|
130
|
+
rotation: payload.heading ?? state.rotation,
|
|
131
|
+
pitch: payload.pitch ?? state.pitch
|
|
132
|
+
};
|
|
133
|
+
cameraStore.set(instanceId, newState);
|
|
134
|
+
notifySubscribers(instanceId);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
const unsubFitBounds = cameraBus.on(CameraEventTypes.fitBounds, ({ payload }) => {
|
|
138
|
+
if (instanceId === payload.id) {
|
|
139
|
+
const state = getOrCreateState(instanceId, initialCameraState);
|
|
140
|
+
const { longitude, latitude, zoom } = fitBounds({
|
|
141
|
+
width: payload.width,
|
|
142
|
+
height: payload.height,
|
|
143
|
+
bounds: [[payload.bounds[0], payload.bounds[1]], [payload.bounds[2], payload.bounds[3]]],
|
|
144
|
+
padding: payload.padding
|
|
145
|
+
});
|
|
146
|
+
const newState = {
|
|
147
|
+
...state,
|
|
148
|
+
latitude,
|
|
149
|
+
longitude,
|
|
150
|
+
zoom,
|
|
151
|
+
rotation: payload.heading ?? state.rotation,
|
|
152
|
+
pitch: payload.pitch ?? state.pitch
|
|
153
|
+
};
|
|
154
|
+
cameraStore.set(instanceId, newState);
|
|
155
|
+
notifySubscribers(instanceId);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
const unsubSetProjection = cameraBus.on(CameraEventTypes.setProjection, ({ payload }) => {
|
|
159
|
+
if (instanceId === payload.id) {
|
|
160
|
+
const newState = { ...getOrCreateState(instanceId, initialCameraState) };
|
|
161
|
+
newState.projection = payload.projection;
|
|
162
|
+
if (payload.projection === "globe") newState.view = "3D";
|
|
163
|
+
else {
|
|
164
|
+
newState.view = "2D";
|
|
165
|
+
newState.pitch = 0;
|
|
166
|
+
}
|
|
167
|
+
cameraStore.set(instanceId, newState);
|
|
168
|
+
notifySubscribers(instanceId);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
const unsubSetView = cameraBus.on(CameraEventTypes.setView, ({ payload }) => {
|
|
172
|
+
if (instanceId === payload.id) {
|
|
173
|
+
const newState = { ...getOrCreateState(instanceId, initialCameraState) };
|
|
174
|
+
newState.view = payload.view;
|
|
175
|
+
if (payload.view === "3D") {
|
|
176
|
+
newState.projection = "globe";
|
|
177
|
+
newState.pitch = 0;
|
|
178
|
+
} else newState.projection = "mercator";
|
|
179
|
+
if (payload.view === "2.5D") newState.pitch = 45;
|
|
180
|
+
cameraStore.set(instanceId, newState);
|
|
181
|
+
notifySubscribers(instanceId);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
const unsubSetZoom = cameraBus.on(CameraEventTypes.setZoom, ({ payload }) => {
|
|
185
|
+
if (instanceId === payload.id) {
|
|
186
|
+
const newState = { ...getOrCreateState(instanceId, initialCameraState) };
|
|
187
|
+
newState.zoom = payload.zoom;
|
|
188
|
+
cameraStore.set(instanceId, newState);
|
|
189
|
+
notifySubscribers(instanceId);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
const unsubSetRotation = cameraBus.on(CameraEventTypes.setRotation, ({ payload }) => {
|
|
193
|
+
const state = getOrCreateState(instanceId, initialCameraState);
|
|
194
|
+
if (instanceId === payload.id && state.view !== "3D") {
|
|
195
|
+
const newState = { ...state };
|
|
196
|
+
newState.rotation = payload.rotation;
|
|
197
|
+
cameraStore.set(instanceId, newState);
|
|
198
|
+
notifySubscribers(instanceId);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
const unsubSetPitch = cameraBus.on(CameraEventTypes.setPitch, ({ payload }) => {
|
|
202
|
+
const state = getOrCreateState(instanceId, initialCameraState);
|
|
203
|
+
if (instanceId === payload.id && state.view === "2.5D") {
|
|
204
|
+
const newState = { ...state };
|
|
205
|
+
newState.pitch = payload.pitch;
|
|
206
|
+
cameraStore.set(instanceId, newState);
|
|
207
|
+
notifySubscribers(instanceId);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
busUnsubscribers.set(instanceId, () => {
|
|
211
|
+
unsubResetCamera();
|
|
212
|
+
unsubSetCenter();
|
|
213
|
+
unsubFitBounds();
|
|
214
|
+
unsubSetProjection();
|
|
215
|
+
unsubSetView();
|
|
216
|
+
unsubSetZoom();
|
|
217
|
+
unsubSetRotation();
|
|
218
|
+
unsubSetPitch();
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Cleans up the bus listener if no React subscribers remain.
|
|
223
|
+
*
|
|
224
|
+
* @param instanceId - The unique identifier for the camera
|
|
225
|
+
*/
|
|
226
|
+
function cleanupBusListenerIfNeeded(instanceId) {
|
|
227
|
+
const subscribers = componentSubscribers.get(instanceId);
|
|
228
|
+
if (!subscribers || subscribers.size === 0) {
|
|
229
|
+
const unsub = busUnsubscribers.get(instanceId);
|
|
230
|
+
if (unsub) {
|
|
231
|
+
unsub();
|
|
232
|
+
busUnsubscribers.delete(instanceId);
|
|
233
|
+
}
|
|
234
|
+
[
|
|
235
|
+
cameraStore,
|
|
236
|
+
componentSubscribers,
|
|
237
|
+
subscriptionCache,
|
|
238
|
+
snapshotCache,
|
|
239
|
+
fallbackCache
|
|
240
|
+
].forEach((map) => {
|
|
241
|
+
map.delete(instanceId);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Creates or retrieves a cached subscription function for a given instanceId.
|
|
247
|
+
* Uses a fan-out pattern: 1 bus listener -> N React subscribers.
|
|
248
|
+
* Automatically cleans up camera state when the last subscriber unsubscribes.
|
|
249
|
+
*
|
|
250
|
+
* @param instanceId - The unique identifier for the camera
|
|
251
|
+
* @param initialCameraState - Optional initial camera state to set when creating the listener
|
|
252
|
+
* @returns A subscription function for useSyncExternalStore
|
|
253
|
+
*/
|
|
254
|
+
function getOrCreateSubscription(instanceId, initialCameraState) {
|
|
255
|
+
const subscription = subscriptionCache.get(instanceId) ?? ((onStoreChange) => {
|
|
256
|
+
ensureBusListener(instanceId, initialCameraState);
|
|
257
|
+
let subscriberSet = componentSubscribers.get(instanceId);
|
|
258
|
+
if (!subscriberSet) {
|
|
259
|
+
subscriberSet = /* @__PURE__ */ new Set();
|
|
260
|
+
componentSubscribers.set(instanceId, subscriberSet);
|
|
261
|
+
}
|
|
262
|
+
subscriberSet.add(onStoreChange);
|
|
263
|
+
return () => {
|
|
264
|
+
const currentSubscriberSet = componentSubscribers.get(instanceId);
|
|
265
|
+
if (currentSubscriberSet) currentSubscriberSet.delete(onStoreChange);
|
|
266
|
+
cleanupBusListenerIfNeeded(instanceId);
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
subscriptionCache.set(instanceId, subscription);
|
|
270
|
+
return subscription;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Creates or retrieves a cached snapshot function for a given instanceId.
|
|
274
|
+
* The object returned gets equality checked, so it needs to be stable or React re-renders unnecessarily.
|
|
275
|
+
*
|
|
276
|
+
* @param instanceId - The unique identifier for the camera
|
|
277
|
+
* @param initialCameraState - Initialize the snapshot with camera view information
|
|
278
|
+
* @returns A snapshot function for useSyncExternalStore
|
|
279
|
+
*/
|
|
280
|
+
function getOrCreateSnapshot(instanceId, initialCameraState) {
|
|
281
|
+
const fallback = fallbackCache.get(instanceId) ?? (() => {
|
|
282
|
+
const newFallback = {
|
|
283
|
+
latitude: initialCameraState?.latitude ?? 0,
|
|
284
|
+
longitude: initialCameraState?.longitude ?? 0,
|
|
285
|
+
zoom: initialCameraState?.zoom ?? 0,
|
|
286
|
+
pitch: 0,
|
|
287
|
+
rotation: 0,
|
|
288
|
+
projection: "mercator",
|
|
289
|
+
view: "2D"
|
|
290
|
+
};
|
|
291
|
+
fallbackCache.set(instanceId, newFallback);
|
|
292
|
+
return newFallback;
|
|
293
|
+
})();
|
|
294
|
+
const snapshot = snapshotCache.get(instanceId) ?? (() => cameraStore.get(instanceId) ?? fallback);
|
|
295
|
+
snapshotCache.set(instanceId, snapshot);
|
|
296
|
+
return snapshot;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Updates the camera state for a given map instance and notifies subscribers.
|
|
300
|
+
*
|
|
301
|
+
* @param instanceId - The unique identifier for the map
|
|
302
|
+
* @param state - The new state to set, will be merged with existing state
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```tsx
|
|
306
|
+
* // Update camera state manually
|
|
307
|
+
* setCameraState('my-map-instance', {
|
|
308
|
+
* latitude: 37.7749,
|
|
309
|
+
* longitude: -122.4194,
|
|
310
|
+
* zoom: 10,
|
|
311
|
+
* pitch: 30,
|
|
312
|
+
* rotation: 0,
|
|
313
|
+
* projection: 'mercator',
|
|
314
|
+
* });
|
|
315
|
+
* ```
|
|
316
|
+
*/
|
|
317
|
+
function setCameraState(instanceId, state) {
|
|
318
|
+
const currentState = getOrCreateState(instanceId);
|
|
319
|
+
cameraStore.set(instanceId, {
|
|
320
|
+
...currentState,
|
|
321
|
+
...state
|
|
322
|
+
});
|
|
323
|
+
notifySubscribers(instanceId);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Hook to access camera state actions.
|
|
327
|
+
*
|
|
328
|
+
* This hook uses `useSyncExternalStore` to subscribe to camera state changes,
|
|
329
|
+
* providing concurrent-safe mode state updates. Uses a fan-out pattern where
|
|
330
|
+
* a single bus listener per map instance notifies N React component subscribers.
|
|
331
|
+
*
|
|
332
|
+
* A thin wrapper around [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore).
|
|
333
|
+
*
|
|
334
|
+
* @param instanceId - Unique identifier for the camera to track
|
|
335
|
+
* @param initialCameraState - Optional initial camera state to set
|
|
336
|
+
* @param subscribe - Optional custom subscription function
|
|
337
|
+
* @param getSnapshot - Optional custom snapshot getter
|
|
338
|
+
* @param getServerSnapshot - Optional server-side snapshot getter
|
|
339
|
+
* @returns Current camera state including latitude, longitude, zoom, pitch, rotation, projection
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```tsx
|
|
343
|
+
* function MapInfo({ instanceId }) {
|
|
344
|
+
* const { latitude, longitude, zoom } = useCameraState({
|
|
345
|
+
* instanceId
|
|
346
|
+
* });
|
|
347
|
+
*
|
|
348
|
+
* return (
|
|
349
|
+
* <div>
|
|
350
|
+
* Lat: {latitude?.toFixed(2)}, Lon: {longitude?.toFixed(2)}, Zoom: {zoom}
|
|
351
|
+
* </div>
|
|
352
|
+
* );
|
|
353
|
+
* }
|
|
354
|
+
* ```
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```tsx
|
|
358
|
+
* // With custom subscribe/getSnapshot for advanced use cases
|
|
359
|
+
* function CustomMapInfo() {
|
|
360
|
+
* const customSubscribe = (onStoreChange) => {
|
|
361
|
+
* // Custom subscription logic
|
|
362
|
+
* return () => { // cleanup }
|
|
363
|
+
* }
|
|
364
|
+
*
|
|
365
|
+
* const customGetSnapshot = () => {
|
|
366
|
+
* // Custom snapshot logic
|
|
367
|
+
* return { latitude: 0, longitude: 0, zoom: 1 };
|
|
368
|
+
* };
|
|
369
|
+
*
|
|
370
|
+
* const viewState = useCameraState({
|
|
371
|
+
* instanceId: 'some-uuid',
|
|
372
|
+
* subscribe: customSubscribe,
|
|
373
|
+
* getSnapshot: customGetSnapshot,
|
|
374
|
+
* });
|
|
375
|
+
*
|
|
376
|
+
* return <div>Custom camera state</div>;
|
|
377
|
+
* }
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
function useCameraState({ instanceId, initialCameraState, subscribe, getSnapshot, getServerSnapshot }) {
|
|
381
|
+
const cameraState = useSyncExternalStore(subscribe ?? getOrCreateSubscription(instanceId, initialCameraState), getSnapshot ?? getOrCreateSnapshot(instanceId, initialCameraState), getServerSnapshot);
|
|
382
|
+
return useMemo(() => ({
|
|
383
|
+
cameraState,
|
|
384
|
+
setCameraState
|
|
385
|
+
}), [cameraState]);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Manually clear camera state for a specific instanceId.
|
|
389
|
+
* This is typically not needed as cleanup happens automatically when all subscribers unmount.
|
|
390
|
+
* Use this only in advanced scenarios where manual cleanup is required.
|
|
391
|
+
*
|
|
392
|
+
* @param instanceId - The unique identifier for the camera to clear
|
|
393
|
+
*
|
|
394
|
+
* @example
|
|
395
|
+
* ```tsx
|
|
396
|
+
* // Manual cleanup (rarely needed)
|
|
397
|
+
* clearCameraState('my-map-instance');
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
function clearCameraState(instanceId) {
|
|
401
|
+
const unsub = busUnsubscribers.get(instanceId);
|
|
402
|
+
if (unsub) {
|
|
403
|
+
unsub();
|
|
404
|
+
busUnsubscribers.delete(instanceId);
|
|
405
|
+
}
|
|
406
|
+
[
|
|
407
|
+
cameraStore,
|
|
408
|
+
componentSubscribers,
|
|
409
|
+
subscriptionCache,
|
|
410
|
+
snapshotCache,
|
|
411
|
+
fallbackCache
|
|
412
|
+
].forEach((map) => {
|
|
413
|
+
map.delete(instanceId);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
//#endregion
|
|
418
|
+
export { clearCameraState, useCameraState };
|
|
419
|
+
//# sourceMappingURL=use-camera-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-camera-state.js","names":["projection: ProjectionType","view: ViewType","newFallback: CameraState"],"sources":["../../src/camera/use-camera-state.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { Broadcast } from '@accelint/bus';\nimport { fitBounds } from '@math.gl/web-mercator';\nimport { useMemo, useSyncExternalStore } from 'react';\nimport { CameraEventTypes } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { CameraEvent, ProjectionType, ViewType } from './types';\n\n/**\n * Typed event bus instance for camera events.\n * Provides type-safe event emission and listening for all camera state changes.\n */\nconst cameraBus = Broadcast.getInstance<CameraEvent>();\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\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\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\nexport type CameraState = CameraState2D | CameraState3D | CameraState2Point5D;\n\nexport type UseCameraStateProps = {\n instanceId: UniqueId;\n initialCameraState?: Partial<CameraState>;\n subscribe?: Parameters<typeof useSyncExternalStore<CameraState>>[0];\n getSnapshot?: Parameters<typeof useSyncExternalStore<CameraState>>[1];\n getServerSnapshot?: Parameters<typeof useSyncExternalStore<CameraState>>[2];\n};\n\n/**\n * Store for camera state keyed by instanceId\n */\nconst cameraStore = new Map<UniqueId, CameraState>();\n\n/**\n * Track React component subscribers per instanceId (for fan-out notifications).\n * Each Set contains onStoreChange callbacks from useSyncExternalStore.\n */\nconst componentSubscribers = new Map<UniqueId, Set<() => void>>();\n\n/**\n * Cache of bus unsubscribe functions (1 per instanceId).\n * This ensures we only have one bus listener per camera, regardless of\n * how many React components subscribe to it.\n */\nconst busUnsubscribers = new Map<UniqueId, () => void>();\n\ntype Subscription = (onStoreChange: () => void) => () => void;\n/**\n * Cache of subscription functions per instanceId to avoid recreating on every render\n */\nconst subscriptionCache = new Map<UniqueId, Subscription>();\n\n/**\n * Cache of snapshot functions per instanceId to maintain referential stability\n */\nconst snapshotCache = new Map<UniqueId, () => CameraState>();\n\n/**\n * Cache of fallback snapshots per instanceId to maintain referential stability.\n * This prevents unnecessary re-renders when no camera data exists yet.\n */\nconst fallbackCache = new Map<UniqueId, CameraState>();\n\n/*\n * Helper to build initial camera state from partial input\n */\nfunction buildInitialCameraState(\n initialCameraState?: Partial<CameraState>,\n): CameraState {\n if (!initialCameraState) {\n return {\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 const is2Point5D = initialCameraState.view === '2.5D';\n const is3D =\n initialCameraState.view === '3D' ||\n initialCameraState.projection === 'globe';\n\n let projection: ProjectionType;\n let view: ViewType;\n\n if (is3D) {\n projection = 'globe';\n view = '3D';\n } else if (is2Point5D) {\n projection = 'mercator';\n view = '2.5D';\n } else {\n projection = initialCameraState.projection ?? 'mercator';\n view = initialCameraState.view ?? '2D';\n }\n\n return {\n latitude: initialCameraState.latitude ?? 0,\n longitude: initialCameraState.longitude ?? 0,\n zoom: initialCameraState.zoom ?? 0,\n pitch: is2Point5D ? (initialCameraState.pitch ?? 45) : 0,\n rotation: is3D ? 0 : (initialCameraState.rotation ?? 0),\n projection,\n view,\n } as CameraState;\n}\n\n/*\n * Get or create camera state for a given instanceId\n */\nfunction getOrCreateState(\n instanceId: UniqueId,\n initialCameraState?: Partial<CameraState>,\n): CameraState {\n if (!cameraStore.has(instanceId)) {\n cameraStore.set(instanceId, buildInitialCameraState(initialCameraState));\n }\n // biome-ignore lint/style/noNonNullAssertion: State guaranteed to exist after has() check above\n return cameraStore.get(instanceId)!;\n}\n\n/**\n * Notify all React subscribers for a given instanceId\n */\nfunction notifySubscribers(instanceId: UniqueId): void {\n const subscribers = componentSubscribers.get(instanceId);\n\n if (subscribers) {\n for (const onStoreChange of subscribers) {\n onStoreChange();\n }\n }\n}\n\n/**\n * Ensures a single bus listener exists for the given instanceId.\n * All React subscribers will be notified via fan-out when the bus event fires.\n * This prevents creating N bus listeners for N React components.\n *\n * @param instanceId - The unique identifier for the camera\n * @param initialCameraState - Optional initial camera state to set when creating the listener\n */\nfunction ensureBusListener(\n instanceId: UniqueId,\n initialCameraState?: Partial<CameraState>,\n): void {\n if (busUnsubscribers.has(instanceId)) {\n return; // Already listening\n }\n\n const unsubResetCamera = cameraBus.on(\n CameraEventTypes.reset,\n ({ payload }) => {\n if (instanceId === payload.id) {\n const state = getOrCreateState(instanceId, initialCameraState);\n const newState = {\n ...buildInitialCameraState({\n ...state,\n zoom:\n payload.zoom === false ? state.zoom : initialCameraState?.zoom,\n pitch:\n payload.pitch === false ? state.pitch : initialCameraState?.pitch,\n rotation:\n payload.rotation === false\n ? state.rotation\n : initialCameraState?.rotation,\n } as Partial<CameraState>),\n };\n\n cameraStore.set(instanceId, newState);\n\n notifySubscribers(instanceId);\n }\n },\n );\n\n const unsubSetCenter = cameraBus.on(\n CameraEventTypes.setCenter,\n ({ payload }) => {\n if (instanceId === payload.id) {\n const state = getOrCreateState(instanceId, initialCameraState);\n const newState = {\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 } as CameraState;\n cameraStore.set(instanceId, newState);\n notifySubscribers(instanceId);\n }\n },\n );\n\n const unsubFitBounds = cameraBus.on(\n CameraEventTypes.fitBounds,\n ({ payload }) => {\n if (instanceId === payload.id) {\n const state = getOrCreateState(instanceId, initialCameraState);\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 const newState = {\n ...state,\n latitude: latitude,\n longitude: longitude,\n zoom: zoom,\n rotation: payload.heading ?? state.rotation,\n pitch: payload.pitch ?? state.pitch,\n } as CameraState;\n cameraStore.set(instanceId, newState);\n notifySubscribers(instanceId);\n }\n },\n );\n\n const unsubSetProjection = cameraBus.on(\n CameraEventTypes.setProjection,\n ({ payload }) => {\n if (instanceId === payload.id) {\n const state = getOrCreateState(instanceId, initialCameraState);\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 cameraStore.set(instanceId, newState);\n notifySubscribers(instanceId);\n }\n },\n );\n\n const unsubSetView = cameraBus.on(CameraEventTypes.setView, ({ payload }) => {\n if (instanceId === payload.id) {\n const state = getOrCreateState(instanceId, initialCameraState);\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 cameraStore.set(instanceId, newState);\n notifySubscribers(instanceId);\n }\n });\n\n const unsubSetZoom = cameraBus.on(CameraEventTypes.setZoom, ({ payload }) => {\n if (instanceId === payload.id) {\n const state = getOrCreateState(instanceId, initialCameraState);\n const newState = { ...state };\n newState.zoom = payload.zoom;\n cameraStore.set(instanceId, newState);\n notifySubscribers(instanceId);\n }\n });\n\n const unsubSetRotation = cameraBus.on(\n CameraEventTypes.setRotation,\n ({ payload }) => {\n const state = getOrCreateState(instanceId, initialCameraState);\n if (instanceId === payload.id && state.view !== '3D') {\n const newState = { ...state };\n newState.rotation = payload.rotation;\n cameraStore.set(instanceId, newState);\n notifySubscribers(instanceId);\n }\n },\n );\n\n const unsubSetPitch = cameraBus.on(\n CameraEventTypes.setPitch,\n ({ payload }) => {\n const state = getOrCreateState(instanceId, initialCameraState);\n if (instanceId === payload.id && state.view === '2.5D') {\n const newState = { ...state };\n newState.pitch = payload.pitch;\n cameraStore.set(instanceId, newState);\n notifySubscribers(instanceId);\n }\n },\n );\n\n busUnsubscribers.set(instanceId, () => {\n unsubResetCamera();\n unsubSetCenter();\n unsubFitBounds();\n unsubSetProjection();\n unsubSetView();\n unsubSetZoom();\n unsubSetRotation();\n unsubSetPitch();\n });\n}\n\n/**\n * Cleans up the bus listener if no React subscribers remain.\n *\n * @param instanceId - The unique identifier for the camera\n */\nfunction cleanupBusListenerIfNeeded(instanceId: UniqueId): void {\n const subscribers = componentSubscribers.get(instanceId);\n\n if (!subscribers || subscribers.size === 0) {\n // No more React subscribers - clean up bus listener\n const unsub = busUnsubscribers.get(instanceId);\n if (unsub) {\n unsub();\n busUnsubscribers.delete(instanceId);\n }\n\n // Clean up all state\n [\n cameraStore,\n componentSubscribers,\n subscriptionCache,\n snapshotCache,\n fallbackCache,\n ].forEach((map) => {\n map.delete(instanceId);\n });\n }\n}\n\n/**\n * Creates or retrieves a cached subscription function for a given instanceId.\n * Uses a fan-out pattern: 1 bus listener -> N React subscribers.\n * Automatically cleans up camera state when the last subscriber unsubscribes.\n *\n * @param instanceId - The unique identifier for the camera\n * @param initialCameraState - Optional initial camera state to set when creating the listener\n * @returns A subscription function for useSyncExternalStore\n */\nfunction getOrCreateSubscription(\n instanceId: UniqueId,\n initialCameraState?: Partial<CameraState>,\n): (onStoreChange: () => void) => () => void {\n const subscription =\n subscriptionCache.get(instanceId) ??\n ((onStoreChange: () => void) => {\n // Ensure single bus listener exists for this instanceId\n ensureBusListener(instanceId, initialCameraState);\n\n // Get or create the subscriber set for this map instance, then add this component's callback\n let subscriberSet = componentSubscribers.get(instanceId);\n if (!subscriberSet) {\n subscriberSet = new Set();\n componentSubscribers.set(instanceId, subscriberSet);\n }\n subscriberSet.add(onStoreChange);\n\n // Return cleanup function to remove this component's subscription\n return () => {\n const currentSubscriberSet = componentSubscribers.get(instanceId);\n if (currentSubscriberSet) {\n currentSubscriberSet.delete(onStoreChange);\n }\n\n // Clean up bus listener if this was the last React subscriber\n cleanupBusListenerIfNeeded(instanceId);\n };\n });\n\n subscriptionCache.set(instanceId, subscription);\n\n return subscription;\n}\n\n/**\n * Creates or retrieves a cached snapshot function for a given instanceId.\n * The object returned gets equality checked, so it needs to be stable or React re-renders unnecessarily.\n *\n * @param instanceId - The unique identifier for the camera\n * @param initialCameraState - Initialize the snapshot with camera view information\n * @returns A snapshot function for useSyncExternalStore\n */\nfunction getOrCreateSnapshot(\n instanceId: UniqueId,\n initialCameraState?: Partial<CameraState>,\n): () => CameraState {\n // Get or create stable fallback reference for this instanceId\n const fallback =\n fallbackCache.get(instanceId) ??\n (() => {\n const newFallback: CameraState = {\n latitude: initialCameraState?.latitude ?? 0,\n longitude: initialCameraState?.longitude ?? 0,\n zoom: initialCameraState?.zoom ?? 0,\n pitch: 0,\n rotation: 0,\n projection: 'mercator',\n view: '2D',\n };\n fallbackCache.set(instanceId, newFallback);\n return newFallback;\n })();\n\n const snapshot =\n snapshotCache.get(instanceId) ??\n (() => cameraStore.get(instanceId) ?? fallback);\n\n snapshotCache.set(instanceId, snapshot);\n\n return snapshot;\n}\n\n/**\n * Updates the camera state for a given map instance and notifies subscribers.\n *\n * @param instanceId - The unique identifier for the map\n * @param state - The new state to set, will be merged with existing state\n *\n * @example\n * ```tsx\n * // Update camera state manually\n * setCameraState('my-map-instance', {\n * latitude: 37.7749,\n * longitude: -122.4194,\n * zoom: 10,\n * pitch: 30,\n * rotation: 0,\n * projection: 'mercator',\n * });\n * ```\n */\nfunction setCameraState(\n instanceId: UniqueId,\n state: Partial<CameraState>,\n): void {\n const currentState = getOrCreateState(instanceId);\n cameraStore.set(instanceId, { ...currentState, ...state } as CameraState);\n notifySubscribers(instanceId);\n}\n\n/**\n * Hook to access camera state actions.\n *\n * This hook uses `useSyncExternalStore` to subscribe to camera state changes,\n * providing concurrent-safe mode state updates. Uses a fan-out pattern where\n * a single bus listener per map instance notifies N React component subscribers.\n *\n * A thin wrapper around [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore).\n *\n * @param instanceId - Unique identifier for the camera to track\n * @param initialCameraState - Optional initial camera state to set\n * @param subscribe - Optional custom subscription function\n * @param getSnapshot - Optional custom snapshot getter\n * @param getServerSnapshot - Optional server-side snapshot getter\n * @returns Current camera state including latitude, longitude, zoom, pitch, rotation, projection\n *\n * @example\n * ```tsx\n * function MapInfo({ instanceId }) {\n * const { latitude, longitude, zoom } = useCameraState({\n * instanceId\n * });\n *\n * return (\n * <div>\n * Lat: {latitude?.toFixed(2)}, Lon: {longitude?.toFixed(2)}, Zoom: {zoom}\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With custom subscribe/getSnapshot for advanced use cases\n * function CustomMapInfo() {\n * const customSubscribe = (onStoreChange) => {\n * // Custom subscription logic\n * return () => { // cleanup }\n * }\n *\n * const customGetSnapshot = () => {\n * // Custom snapshot logic\n * return { latitude: 0, longitude: 0, zoom: 1 };\n * };\n *\n * const viewState = useCameraState({\n * instanceId: 'some-uuid',\n * subscribe: customSubscribe,\n * getSnapshot: customGetSnapshot,\n * });\n *\n * return <div>Custom camera state</div>;\n * }\n * ```\n */\nexport function useCameraState({\n instanceId,\n initialCameraState,\n subscribe,\n getSnapshot,\n getServerSnapshot,\n}: UseCameraStateProps) {\n const cameraState = useSyncExternalStore<CameraState>(\n subscribe ?? getOrCreateSubscription(instanceId, initialCameraState),\n getSnapshot ?? getOrCreateSnapshot(instanceId, initialCameraState),\n getServerSnapshot,\n );\n\n return useMemo(\n () => ({\n cameraState,\n setCameraState,\n }),\n [cameraState],\n );\n}\n\n/**\n * Manually clear camera state for a specific instanceId.\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 camera to clear\n *\n * @example\n * ```tsx\n * // Manual cleanup (rarely needed)\n * clearCameraState('my-map-instance');\n * ```\n */\nexport function clearCameraState(instanceId: UniqueId): void {\n // Unsubscribe from bus if listening\n const unsub = busUnsubscribers.get(instanceId);\n if (unsub) {\n unsub();\n busUnsubscribers.delete(instanceId);\n }\n\n // Clear all state\n [\n cameraStore,\n componentSubscribers,\n subscriptionCache,\n snapshotCache,\n fallbackCache,\n ].forEach((map) => {\n map.delete(instanceId);\n });\n}\n"],"mappings":"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"}
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
import { MapEvents } from "../deckgl/base-map/events.js";
|
|
17
17
|
import { MapContext } from "../deckgl/base-map/provider.js";
|
|
18
18
|
import { Broadcast } from "@accelint/bus";
|
|
19
|
-
import { coordinateSystems, createCoordinate } from "@accelint/geo";
|
|
20
19
|
import { useContext, useMemo, useSyncExternalStore } from "react";
|
|
20
|
+
import { coordinateSystems, createCoordinate } from "@accelint/geo";
|
|
21
21
|
|
|
22
22
|
//#region src/cursor-coordinates/use-cursor-coordinates.ts
|
|
23
23
|
const bus = Broadcast.getInstance();
|
|
@@ -13,6 +13,11 @@ declare const PARAMETERS: {
|
|
|
13
13
|
blendColorOperation: string;
|
|
14
14
|
blendAlphaOperation: string;
|
|
15
15
|
};
|
|
16
|
+
declare const DEFAULT_VIEW_STATE: {
|
|
17
|
+
longitude: number;
|
|
18
|
+
latitude: number;
|
|
19
|
+
zoom: number;
|
|
20
|
+
};
|
|
16
21
|
//#endregion
|
|
17
|
-
export { BASE_MAP_STYLE, PARAMETERS };
|
|
22
|
+
export { BASE_MAP_STYLE, DEFAULT_VIEW_STATE, PARAMETERS };
|
|
18
23
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -26,7 +26,12 @@ const PARAMETERS = {
|
|
|
26
26
|
blendColorOperation: "add",
|
|
27
27
|
blendAlphaOperation: "add"
|
|
28
28
|
};
|
|
29
|
+
const DEFAULT_VIEW_STATE = {
|
|
30
|
+
longitude: -77.0369,
|
|
31
|
+
latitude: 38.9072,
|
|
32
|
+
zoom: 4
|
|
33
|
+
};
|
|
29
34
|
|
|
30
35
|
//#endregion
|
|
31
|
-
export { BASE_MAP_STYLE, PARAMETERS };
|
|
36
|
+
export { BASE_MAP_STYLE, DEFAULT_VIEW_STATE, PARAMETERS };
|
|
32
37
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":[],"sources":["../../../src/deckgl/base-map/constants.ts"],"sourcesContent":["// __private-exports\n/*\n * Copyright 2025 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 BASE_MAP_STYLE =\n 'https://tiles.basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json';\n\nexport const PARAMETERS = {\n depthWriteEnabled: true,\n depthCompare: 'always',\n depthBias: 0,\n blend: true,\n depthTest: false,\n blendColorSrcFactor: 'src-alpha',\n blendColorDstFactor: 'one-minus-src-alpha',\n blendAlphaSrcFactor: 'one',\n blendAlphaDstFactor: 'one-minus-src-alpha',\n blendColorOperation: 'add',\n blendAlphaOperation: 'add',\n};\n"],"mappings":";;;;;;;;;;;;;;AAaA,MAAa,iBACX;AAEF,MAAa,aAAa;CACxB,mBAAmB;CACnB,cAAc;CACd,WAAW;CACX,OAAO;CACP,WAAW;CACX,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CACtB"}
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../../src/deckgl/base-map/constants.ts"],"sourcesContent":["// __private-exports\n/*\n * Copyright 2025 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 BASE_MAP_STYLE =\n 'https://tiles.basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json';\n\nexport const PARAMETERS = {\n depthWriteEnabled: true,\n depthCompare: 'always',\n depthBias: 0,\n blend: true,\n depthTest: false,\n blendColorSrcFactor: 'src-alpha',\n blendColorDstFactor: 'one-minus-src-alpha',\n blendAlphaSrcFactor: 'one',\n blendAlphaDstFactor: 'one-minus-src-alpha',\n blendColorOperation: 'add',\n blendAlphaOperation: 'add',\n};\n\nexport const DEFAULT_VIEW_STATE = {\n longitude: -77.0369,\n latitude: 38.9072,\n zoom: 4,\n};\n"],"mappings":";;;;;;;;;;;;;;AAaA,MAAa,iBACX;AAEF,MAAa,aAAa;CACxB,mBAAmB;CACnB,cAAc;CACd,WAAW;CACX,OAAO;CACP,WAAW;CACX,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CACtB;AAED,MAAa,qBAAqB;CAChC,WAAW;CACX,UAAU;CACV,MAAM;CACP"}
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
import { RefObject } from "react";
|
|
14
|
+
import { UniqueId } from "@accelint/core";
|
|
15
|
+
import { MapRef } from "react-map-gl/maplibre";
|
|
16
|
+
|
|
17
|
+
//#region src/deckgl/base-map/controls.d.ts
|
|
18
|
+
type MapControlsProps = {
|
|
19
|
+
id: UniqueId;
|
|
20
|
+
mapRef: RefObject<MapRef | null>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Headless component that listens for map control events and applies them to the MapLibre instance.
|
|
24
|
+
*
|
|
25
|
+
* This component is rendered inside BaseMap to wire up event listeners
|
|
26
|
+
* for pan and zoom control events.
|
|
27
|
+
*/
|
|
28
|
+
declare function MapControls({
|
|
29
|
+
id,
|
|
30
|
+
mapRef
|
|
31
|
+
}: MapControlsProps): null;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { MapControls };
|
|
34
|
+
//# sourceMappingURL=controls.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
'use client';
|
|
15
|
+
|
|
16
|
+
import { MapEvents } from "./events.js";
|
|
17
|
+
import { useOn } from "@accelint/bus/react";
|
|
18
|
+
|
|
19
|
+
//#region src/deckgl/base-map/controls.tsx
|
|
20
|
+
/**
|
|
21
|
+
* Headless component that listens for map control events and applies them to the MapLibre instance.
|
|
22
|
+
*
|
|
23
|
+
* This component is rendered inside BaseMap to wire up event listeners
|
|
24
|
+
* for pan and zoom control events.
|
|
25
|
+
*/
|
|
26
|
+
function MapControls({ id, mapRef }) {
|
|
27
|
+
useOn(MapEvents.enablePan, (event) => {
|
|
28
|
+
if (event.payload.id === id) mapRef.current?.getMap().dragPan.enable();
|
|
29
|
+
});
|
|
30
|
+
useOn(MapEvents.disablePan, (event) => {
|
|
31
|
+
if (event.payload.id === id) mapRef.current?.getMap().dragPan.disable();
|
|
32
|
+
});
|
|
33
|
+
useOn(MapEvents.enableZoom, (event) => {
|
|
34
|
+
if (event.payload.id === id) {
|
|
35
|
+
mapRef.current?.getMap().scrollZoom.enable();
|
|
36
|
+
mapRef.current?.getMap().doubleClickZoom.enable();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
useOn(MapEvents.disableZoom, (event) => {
|
|
40
|
+
if (event.payload.id === id) {
|
|
41
|
+
mapRef.current?.getMap().scrollZoom.disable();
|
|
42
|
+
mapRef.current?.getMap().doubleClickZoom.disable();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { MapControls };
|
|
50
|
+
//# sourceMappingURL=controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controls.js","names":[],"sources":["../../../src/deckgl/base-map/controls.tsx"],"sourcesContent":["/*\n * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport 'client-only';\nimport { useOn } from '@accelint/bus/react';\nimport { MapEvents } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { RefObject } from 'react';\nimport type { MapRef } from 'react-map-gl/maplibre';\nimport type {\n MapDisablePanEvent,\n MapDisableZoomEvent,\n MapEnablePanEvent,\n MapEnableZoomEvent,\n} from './types';\n\ntype MapControlsProps = {\n id: UniqueId;\n mapRef: RefObject<MapRef | null>;\n};\n\n/**\n * Headless component that listens for map control events and applies them to the MapLibre instance.\n *\n * This component is rendered inside BaseMap to wire up event listeners\n * for pan and zoom control events.\n */\nexport function MapControls({ id, mapRef }: MapControlsProps) {\n useOn<MapEnablePanEvent>(MapEvents.enablePan, (event) => {\n if (event.payload.id === id) {\n mapRef.current?.getMap().dragPan.enable();\n }\n });\n\n useOn<MapDisablePanEvent>(MapEvents.disablePan, (event) => {\n if (event.payload.id === id) {\n mapRef.current?.getMap().dragPan.disable();\n }\n });\n\n useOn<MapEnableZoomEvent>(MapEvents.enableZoom, (event) => {\n if (event.payload.id === id) {\n mapRef.current?.getMap().scrollZoom.enable();\n mapRef.current?.getMap().doubleClickZoom.enable();\n }\n });\n\n useOn<MapDisableZoomEvent>(MapEvents.disableZoom, (event) => {\n if (event.payload.id === id) {\n mapRef.current?.getMap().scrollZoom.disable();\n mapRef.current?.getMap().doubleClickZoom.disable();\n }\n });\n\n return null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,YAAY,EAAE,IAAI,UAA4B;AAC5D,OAAyB,UAAU,YAAY,UAAU;AACvD,MAAI,MAAM,QAAQ,OAAO,GACvB,QAAO,SAAS,QAAQ,CAAC,QAAQ,QAAQ;GAE3C;AAEF,OAA0B,UAAU,aAAa,UAAU;AACzD,MAAI,MAAM,QAAQ,OAAO,GACvB,QAAO,SAAS,QAAQ,CAAC,QAAQ,SAAS;GAE5C;AAEF,OAA0B,UAAU,aAAa,UAAU;AACzD,MAAI,MAAM,QAAQ,OAAO,IAAI;AAC3B,UAAO,SAAS,QAAQ,CAAC,WAAW,QAAQ;AAC5C,UAAO,SAAS,QAAQ,CAAC,gBAAgB,QAAQ;;GAEnD;AAEF,OAA2B,UAAU,cAAc,UAAU;AAC3D,MAAI,MAAM,QAAQ,OAAO,IAAI;AAC3B,UAAO,SAAS,QAAQ,CAAC,WAAW,SAAS;AAC7C,UAAO,SAAS,QAAQ,CAAC,gBAAgB,SAAS;;GAEpD;AAEF,QAAO"}
|
|
@@ -4,6 +4,10 @@ declare const MapEvents: {
|
|
|
4
4
|
readonly click: "map:click";
|
|
5
5
|
readonly hover: "map:hover";
|
|
6
6
|
readonly viewport: "map:viewport";
|
|
7
|
+
readonly enablePan: "map:enablePan";
|
|
8
|
+
readonly disablePan: "map:disablePan";
|
|
9
|
+
readonly enableZoom: "map:enableZoom";
|
|
10
|
+
readonly disableZoom: "map:disableZoom";
|
|
7
11
|
};
|
|
8
12
|
//#endregion
|
|
9
13
|
export { MapEvents, MapEventsNamespace };
|
|
@@ -16,7 +16,11 @@ const MapEventsNamespace = "map";
|
|
|
16
16
|
const MapEvents = {
|
|
17
17
|
click: `${MapEventsNamespace}:click`,
|
|
18
18
|
hover: `${MapEventsNamespace}:hover`,
|
|
19
|
-
viewport: `${MapEventsNamespace}:viewport
|
|
19
|
+
viewport: `${MapEventsNamespace}:viewport`,
|
|
20
|
+
enablePan: `${MapEventsNamespace}:enablePan`,
|
|
21
|
+
disablePan: `${MapEventsNamespace}:disablePan`,
|
|
22
|
+
enableZoom: `${MapEventsNamespace}:enableZoom`,
|
|
23
|
+
disableZoom: `${MapEventsNamespace}:disableZoom`
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","names":[],"sources":["../../../src/deckgl/base-map/events.ts"],"sourcesContent":["/*\n * Copyright 2025 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 MapEventsNamespace = 'map';\n\nexport const MapEvents = {\n click: `${MapEventsNamespace}:click`,\n hover: `${MapEventsNamespace}:hover`,\n viewport: `${MapEventsNamespace}:viewport`,\n} as const;\n"],"mappings":";;;;;;;;;;;;;;AAYA,MAAa,qBAAqB;AAElC,MAAa,YAAY;CACvB,OAAO,GAAG,mBAAmB;CAC7B,OAAO,GAAG,mBAAmB;CAC7B,UAAU,GAAG,mBAAmB;CACjC"}
|
|
1
|
+
{"version":3,"file":"events.js","names":[],"sources":["../../../src/deckgl/base-map/events.ts"],"sourcesContent":["/*\n * Copyright 2025 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 MapEventsNamespace = 'map';\n\nexport const MapEvents = {\n click: `${MapEventsNamespace}:click`,\n hover: `${MapEventsNamespace}:hover`,\n viewport: `${MapEventsNamespace}:viewport`,\n // Control events\n enablePan: `${MapEventsNamespace}:enablePan`,\n disablePan: `${MapEventsNamespace}:disablePan`,\n enableZoom: `${MapEventsNamespace}:enableZoom`,\n disableZoom: `${MapEventsNamespace}:disableZoom`,\n} as const;\n"],"mappings":";;;;;;;;;;;;;;AAYA,MAAa,qBAAqB;AAElC,MAAa,YAAY;CACvB,OAAO,GAAG,mBAAmB;CAC7B,OAAO,GAAG,mBAAmB;CAC7B,UAAU,GAAG,mBAAmB;CAEhC,WAAW,GAAG,mBAAmB;CACjC,YAAY,GAAG,mBAAmB;CAClC,YAAY,GAAG,mBAAmB;CAClC,aAAa,GAAG,mBAAmB;CACpC"}
|