@accelint/map-toolkit 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +92 -16
  3. package/catalog-info.yaml +44 -0
  4. package/dist/deckgl/base-map/constants.d.ts +16 -0
  5. package/dist/deckgl/base-map/constants.js +18 -0
  6. package/dist/deckgl/base-map/constants.js.map +1 -0
  7. package/dist/deckgl/base-map/events.d.ts +7 -0
  8. package/dist/deckgl/base-map/events.js +9 -0
  9. package/dist/deckgl/base-map/events.js.map +1 -0
  10. package/dist/deckgl/base-map/index.d.ts +98 -0
  11. package/dist/deckgl/base-map/index.js +99 -0
  12. package/dist/deckgl/base-map/index.js.map +1 -0
  13. package/dist/deckgl/base-map/provider.d.ts +133 -0
  14. package/dist/deckgl/base-map/provider.js +22 -0
  15. package/dist/deckgl/base-map/provider.js.map +1 -0
  16. package/dist/deckgl/base-map/types.d.ts +61 -0
  17. package/dist/deckgl/base-map/types.js +3 -0
  18. package/dist/deckgl/base-map/types.js.map +1 -0
  19. package/dist/deckgl/index.d.ts +13 -0
  20. package/dist/deckgl/index.js +6 -0
  21. package/dist/{index.js.map → deckgl/index.js.map} +1 -1
  22. package/dist/deckgl/symbol-layer/fiber.d.ts +14 -0
  23. package/dist/deckgl/symbol-layer/fiber.js +6 -0
  24. package/dist/deckgl/symbol-layer/fiber.js.map +1 -0
  25. package/dist/deckgl/symbol-layer/index.d.ts +63 -0
  26. package/dist/deckgl/symbol-layer/index.js +92 -0
  27. package/dist/deckgl/symbol-layer/index.js.map +1 -0
  28. package/dist/deckgl/text-layer/character-sets.d.ts +20 -0
  29. package/dist/deckgl/text-layer/character-sets.js +36 -0
  30. package/dist/deckgl/text-layer/character-sets.js.map +1 -0
  31. package/dist/deckgl/text-layer/default-settings.d.ts +5 -0
  32. package/dist/deckgl/text-layer/default-settings.js +23 -0
  33. package/dist/deckgl/text-layer/default-settings.js.map +1 -0
  34. package/dist/deckgl/text-layer/fiber.d.ts +31 -0
  35. package/dist/deckgl/text-layer/fiber.js +6 -0
  36. package/dist/deckgl/text-layer/fiber.js.map +1 -0
  37. package/dist/deckgl/text-layer/index.d.ts +43 -0
  38. package/dist/deckgl/text-layer/index.js +33 -0
  39. package/dist/deckgl/text-layer/index.js.map +1 -0
  40. package/dist/decorators/deckgl.d.ts +5 -0
  41. package/dist/decorators/deckgl.js +14 -0
  42. package/dist/decorators/deckgl.js.map +1 -0
  43. package/dist/map-mode/events.d.ts +37 -0
  44. package/dist/map-mode/events.js +15 -0
  45. package/dist/map-mode/events.js.map +1 -0
  46. package/dist/map-mode/index.d.ts +6 -0
  47. package/dist/map-mode/index.js +5 -0
  48. package/dist/map-mode/index.js.map +1 -0
  49. package/dist/map-mode/store.d.ts +122 -0
  50. package/dist/map-mode/store.js +327 -0
  51. package/dist/map-mode/store.js.map +1 -0
  52. package/dist/map-mode/types.d.ts +83 -0
  53. package/dist/map-mode/types.js +3 -0
  54. package/dist/map-mode/types.js.map +1 -0
  55. package/dist/map-mode/use-map-mode.d.ts +54 -0
  56. package/dist/map-mode/use-map-mode.js +31 -0
  57. package/dist/map-mode/use-map-mode.js.map +1 -0
  58. package/dist/maplibre/constants.d.ts +11 -0
  59. package/dist/maplibre/constants.js +13 -0
  60. package/dist/maplibre/constants.js.map +1 -0
  61. package/dist/maplibre/hooks/use-maplibre.d.ts +39 -0
  62. package/dist/maplibre/hooks/use-maplibre.js +37 -0
  63. package/dist/maplibre/hooks/use-maplibre.js.map +1 -0
  64. package/dist/maplibre/index.d.ts +3 -0
  65. package/dist/maplibre/index.js +4 -0
  66. package/dist/maplibre/index.js.map +1 -0
  67. package/dist/metafile-esm.json +1 -1
  68. package/package.json +91 -17
  69. package/dist/index.d.ts +0 -2
  70. package/dist/index.js +0 -3
  71. package/dist/test/setup.d.ts +0 -2
  72. package/dist/test/setup.js +0 -10
  73. package/dist/test/setup.js.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,51 @@
1
+ # @accelint/map-toolkit
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b4d1b9b: Add map mode system with authorization flow for managing interaction modes.
8
+
9
+ **New Features:**
10
+ - `MapProvider` component for managing map mode state with instance isolation
11
+ - `useMapMode()` hook for accessing and requesting mode changes
12
+ - Authorization flow for resolving ownership conflicts when switching modes
13
+ - Event bus integration for decoupled mode change communication
14
+ - Support for multiple independent map instances with isolated state
15
+
16
+ **API:**
17
+ - `MapProvider` component (internal to `BaseMap`)
18
+ - `useMapMode(id)` hook for UI components
19
+ - `BaseMap` now requires `id` prop for instance identification
20
+ - Observable store pattern using React's `useSyncExternalStore`
21
+
22
+ - 998dee6: add deckgl-layer-text for default text styling
23
+
24
+ ### Patch Changes
25
+
26
+ - 303b61f: Pins the dependency version of Deck.gl to 9.1.14. We are not yet able to support 9.2.
27
+ - 0d697fa: Fixed definitions in package files for longhand repository definitions, while disabling the option in syncpack that changed it.
28
+ - a8b8de2: Update README content in map-toolkit
29
+ - f99f294: Updated syncpack and realigned all packages for dependency versions
30
+ - 935b8e5: Updated the package names in the Constellation configuration file.
31
+
32
+ ## 0.1.0
33
+
34
+ ### Minor Changes
35
+
36
+ - 24e2def: Open-source the deckgl-layer-symbol package. Adds support for rendering MIL-STD-2525 symbologies as a Deck.gl layer.
37
+ - 5f45f43: Adds base-map component and DeckGL onClick and onHover event emitters. The example story shows how to use the @accelint/bus/react useOn hook to subscribe to the emitted events.
38
+ - 405d875: Introduced basic implementation for storybook for map-toolkit, including a decorator for deckGL for new stories.
39
+
40
+ ### Patch Changes
41
+
42
+ - 64280a7: - Released `@accelint/constellation-tracker` - A tool that helps maintain catalog-info.yaml files for Constellation integration
43
+ - Ensures all packages include catalog-info.yaml in their published files for better discoverability and integration with Constellation
44
+ - Provides automated tracking and updating of component metadata across the project
45
+ - Enhanced package metadata to support better integration with internal tooling
46
+
47
+ ## 0.0.2
48
+
49
+ ### Patch Changes
50
+
51
+ - 56d5af8: Initialization of Map Toolkit (MapTK) library
package/README.md CHANGED
@@ -1,32 +1,108 @@
1
1
  # @accelint/map-toolkit
2
2
 
3
+ A collection of components and utilities to simplify visualizing and working with geospatial data. This toolkit provides ready-to-use map components and layers for deck.gl and MapLibre, making it easier to build interactive mapping applications.
4
+
5
+ ## What is Map Toolkit?
6
+
7
+ Map Toolkit is a comprehensive library that provides:
8
+
9
+ - **Deck.gl Components**: Pre-configured layers and components for rendering geospatial data, including symbol layers for military symbology (MIL-STD-2525)
10
+ - **MapLibre Integration**: Components and utilities for working with MapLibre GL JS
11
+ - **React Support**: React-friendly components with hooks and utilities via `@deckgl-fiber-renderer`
12
+ - **Geospatial Utilities**: Helper functions and decorators for common mapping tasks
13
+
14
+ The package is organized by technology (e.g., `deckgl/`, `maplibre/`) with feature-specific exports, allowing you to import only what you need.
15
+
3
16
  ## Installation
4
17
 
5
18
  ```sh
6
19
  npm install @accelint/map-toolkit
7
20
  ```
8
21
 
9
- ## Structure
22
+ ### Optional Dependencies
10
23
 
11
- - standard-toolkit/packages/map-toolkit
12
- - src
13
- - deckgl
14
- - [feature folders]
15
- - index.ts (generated by indexer)
24
+ To minimize bundle size, Map Toolkit uses optional dependencies. Install only the technologies you need:
16
25
 
17
- - [other technology folders, ex: maplibre]
26
+ ```sh
27
+ # For deck.gl features
28
+ pnpm i --save-optional @deck.gl/core @deck.gl/layers @deck.gl/extensions
18
29
 
19
- - ~~index.ts~~ (do not create a root level barrel, there should only be tech level barrels, but also support individual feature exports in package.json)
30
+ # For React/deck.gl features
31
+ pnpm i --save-optional react @deckgl-fiber-renderer/dom
20
32
 
21
- - [misc files, ex: package.json, etc]
33
+ # For MapLibre features
34
+ pnpm i --save-optional maplibre-gl
22
35
 
23
- ## Installing New Packages
24
- Similar to DesignTK, all features within a technology MUST work together, to help guarantee this we will not publish them as individual packages, but as part of a single package.
36
+ # For military symbology
37
+ pnpm i --save-optional milsymbol
38
+ ```
25
39
 
26
- In an effort to reduce the number of unnecessary dependencies being installed by consumers, we are opting to use `optionalDependencies`, instead of `peerDependencies`. This allows us for more flexibility, but also requires us to be more mindful of how these packages are being used.
40
+ ## Usage
27
41
 
28
- ### Example
29
- `pnpm i --save-optional deckgl@latest`
42
+ Import components from their technology-specific paths:
30
43
 
31
- ## Usage
32
- - Under Construction 🚧
44
+ ```ts
45
+ // Deck.gl components
46
+ import { SymbolLayer } from '@accelint/map-toolkit/deckgl';
47
+
48
+ // MapLibre components
49
+ import { /* components */ } from '@accelint/map-toolkit/maplibre';
50
+
51
+ // React/Fiber components
52
+ import { /* components */ } from '@accelint/map-toolkit/deckgl/fiber';
53
+ ```
54
+
55
+ For detailed examples and interactive demos, see the [Storybook documentation](https://map-toolkit.accelint.io/?path=/docs/deckgl-symbol-layer--docs).
56
+
57
+ ## Running Locally
58
+
59
+ To work on Map Toolkit locally:
60
+
61
+ ```bash
62
+ # Install dependencies
63
+ pnpm i
64
+
65
+ # Run Storybook development server
66
+ pnpm --filter=@accelint/map-toolkit preview
67
+
68
+ # Build the package
69
+ pnpm --filter=@accelint/map-toolkit build
70
+ ```
71
+
72
+ If you encounter errors, try running `pnpm build` after installing dependencies, then run the preview command again.
73
+
74
+ ## Testing
75
+
76
+ Run the test suite:
77
+
78
+ ```bash
79
+ # Run tests once
80
+ pnpm --filter=@accelint/map-toolkit test
81
+
82
+ # Run tests in watch mode
83
+ pnpm --filter=@accelint/map-toolkit test:watch
84
+
85
+ # Run benchmarks
86
+ pnpm --filter=@accelint/map-toolkit bench
87
+ ```
88
+
89
+ ## Documentation
90
+
91
+ - **[Storybook](https://map-toolkit.accelint.io/?path=/docs/deckgl-symbol-layer--docs)** - Interactive component documentation and examples
92
+ - **[Changelog](./CHANGELOG.md)** - Release notes and version history
93
+
94
+ ## Package Structure
95
+
96
+ ```
97
+ packages/map-toolkit/
98
+ src/
99
+ deckgl/ # Deck.gl layers and components
100
+ maplibre/ # MapLibre utilities and components
101
+ decorators/ # Shared decorators and utilities
102
+ ```
103
+
104
+ Each technology folder exports its components via barrel files, and individual features are also exported via `package.json` for granular imports.
105
+
106
+ ## License
107
+
108
+ Apache-2.0
@@ -0,0 +1,44 @@
1
+ ---
2
+ apiVersion: backstage.io/v1alpha1
3
+ kind: Component
4
+ metadata:
5
+ name: accelint_map-toolkit
6
+ title: Accelint Map Toolkit
7
+ description: >-
8
+ A collection of components and utilities to simplify visualizing and working
9
+ with geospatial data.
10
+
11
+
12
+ Dependencies:
13
+
14
+ accelint_biome-config@1.0.2, accelint_bus@2.0.0,
15
+ accelint_constellation-tracker@1.0.1, accelint_core@0.5.0,
16
+ accelint_design-toolkit@7.0.0, accelint_typescript-config@0.1.4,
17
+ accelint_vitest-config@0.1.5
18
+ annotations:
19
+ backstage.io/edit-url: https://github.com/gohypergiant/standard-toolkit/blob/main/packages/map-toolkit/catalog-info.yaml
20
+ backstage.io/techdocs-ref: dir:.
21
+ package/version: 0.2.0
22
+ github.com/project-slug: gohypergiant/standard-toolkit
23
+ links:
24
+ - url: https://github.com/gohypergiant/standard-toolkit/tree/main/packages/map-toolkit
25
+ title: Documentation
26
+ icon: docs
27
+ type: documentation
28
+ tags:
29
+ - deckgl
30
+ - geospatial
31
+ - map-tk
32
+ - maplibre
33
+ spec:
34
+ type: library
35
+ lifecycle: production
36
+ owner: group:default/pathfinder
37
+ dependsOn:
38
+ - component:accelint_biome-config
39
+ - component:accelint_bus
40
+ - component:accelint_constellation-tracker
41
+ - component:accelint_core
42
+ - component:accelint_design-toolkit
43
+ - component:accelint_typescript-config
44
+ - component:accelint_vitest-config
@@ -0,0 +1,16 @@
1
+ declare const BASE_MAP_STYLE = "https://tiles.basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json";
2
+ declare const PARAMETERS: {
3
+ depthWriteEnabled: boolean;
4
+ depthCompare: string;
5
+ depthBias: number;
6
+ blend: boolean;
7
+ depthTest: boolean;
8
+ blendColorSrcFactor: string;
9
+ blendColorDstFactor: string;
10
+ blendAlphaSrcFactor: string;
11
+ blendAlphaDstFactor: string;
12
+ blendColorOperation: string;
13
+ blendAlphaOperation: string;
14
+ };
15
+
16
+ export { BASE_MAP_STYLE, PARAMETERS };
@@ -0,0 +1,18 @@
1
+ const BASE_MAP_STYLE = "https://tiles.basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json";
2
+ const PARAMETERS = {
3
+ depthWriteEnabled: true,
4
+ depthCompare: "always",
5
+ depthBias: 0,
6
+ blend: true,
7
+ depthTest: false,
8
+ blendColorSrcFactor: "src-alpha",
9
+ blendColorDstFactor: "one-minus-src-alpha",
10
+ blendAlphaSrcFactor: "one",
11
+ blendAlphaDstFactor: "one-minus-src-alpha",
12
+ blendColorOperation: "add",
13
+ blendAlphaOperation: "add"
14
+ };
15
+
16
+ export { BASE_MAP_STYLE, PARAMETERS };
17
+ //# sourceMappingURL=constants.js.map
18
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/deckgl/base-map/constants.ts"],"names":[],"mappings":"AAaO,MAAM,cAAA,GACX;AAEK,MAAM,UAAA,GAAa;AAAA,EACxB,iBAAA,EAAmB,IAAA;AAAA,EACnB,YAAA,EAAc,QAAA;AAAA,EACd,SAAA,EAAW,CAAA;AAAA,EACX,KAAA,EAAO,IAAA;AAAA,EACP,SAAA,EAAW,KAAA;AAAA,EACX,mBAAA,EAAqB,WAAA;AAAA,EACrB,mBAAA,EAAqB,qBAAA;AAAA,EACrB,mBAAA,EAAqB,KAAA;AAAA,EACrB,mBAAA,EAAqB,qBAAA;AAAA,EACrB,mBAAA,EAAqB,KAAA;AAAA,EACrB,mBAAA,EAAqB;AACvB","file":"constants.js","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"]}
@@ -0,0 +1,7 @@
1
+ declare const MapEventsNamespace = "map";
2
+ declare const MapEvents: {
3
+ readonly click: "map:click";
4
+ readonly hover: "map:hover";
5
+ };
6
+
7
+ export { MapEvents, MapEventsNamespace };
@@ -0,0 +1,9 @@
1
+ const MapEventsNamespace = "map";
2
+ const MapEvents = {
3
+ click: `${MapEventsNamespace}:click`,
4
+ hover: `${MapEventsNamespace}:hover`
5
+ };
6
+
7
+ export { MapEvents, MapEventsNamespace };
8
+ //# sourceMappingURL=events.js.map
9
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/deckgl/base-map/events.ts"],"names":[],"mappings":"AAYO,MAAM,kBAAA,GAAqB;AAE3B,MAAM,SAAA,GAAY;AAAA,EACvB,KAAA,EAAO,GAAG,kBAAkB,CAAA,MAAA,CAAA;AAAA,EAC5B,KAAA,EAAO,GAAG,kBAAkB,CAAA,MAAA;AAC9B","file":"events.js","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} as const;\n"]}
@@ -0,0 +1,98 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { UniqueId } from '@accelint/core';
3
+ import { DeckglProps } from '@deckgl-fiber-renderer/types';
4
+
5
+ /**
6
+ * Props for the BaseMap component.
7
+ * Extends all Deck.gl props and adds additional map-specific properties.
8
+ */
9
+ type BaseMapProps = DeckglProps & {
10
+ /** Optional CSS class name to apply to the map container element */
11
+ className?: string;
12
+ /**
13
+ * Unique identifier for this map instance (required).
14
+ *
15
+ * Used to isolate map mode state between multiple map instances (e.g., main map vs minimap).
16
+ * This should be a UUID generated using `uuid()` from `@accelint/core`.
17
+ *
18
+ * The same id should be passed to `useMapMode()` when accessing map mode state
19
+ * from components rendered outside of the BaseMap's children (i.e., as siblings).
20
+ */
21
+ id: UniqueId;
22
+ };
23
+ /**
24
+ * A React component that provides a Deck.gl-powered base map with MapLibre GL integration.
25
+ *
26
+ * This component serves as the foundation for building interactive map applications with
27
+ * support for click and hover events through a centralized event bus. It integrates
28
+ * Deck.gl for 3D visualizations with MapLibre GL for the base map tiles.
29
+ *
30
+ * **Map Mode Integration**: BaseMap automatically creates a `MapProvider` internally,
31
+ * which sets up the map mode state management for this instance.
32
+ * - **Children**: Only Deck.gl layer components can be rendered as children. Custom Deck.gl
33
+ * layers can use `useMapMode()` without parameters to access context.
34
+ * - **Siblings**: UI components (buttons, toolbars, etc.) must be rendered as siblings
35
+ * and pass `id` to `useMapMode(id)`.
36
+ *
37
+ * **Event Bus**: Click and hover events are emitted through the event bus with the `id`
38
+ * included in the payload, allowing multiple map instances to coexist without interference.
39
+ *
40
+ * @param props - Component props including id (required), className, onClick, onHover, and all Deck.gl props
41
+ * @returns A map component with Deck.gl and MapLibre GL integration
42
+ *
43
+ * @example
44
+ * Basic usage with id (recommended: module-level constant):
45
+ * ```tsx
46
+ * import { BaseMap } from '@accelint/map-toolkit/deckgl';
47
+ * import { View } from '@deckgl-fiber-renderer/dom';
48
+ * import { uuid } from '@accelint/core';
49
+ *
50
+ * // Create id at module level for stability and easy sharing
51
+ * const MAIN_MAP_ID = uuid();
52
+ *
53
+ * export function MapView() {
54
+ * return (
55
+ * <BaseMap className="w-full h-full" id={MAIN_MAP_ID}>
56
+ * <View id="main" controller />
57
+ * </BaseMap>
58
+ * );
59
+ * }
60
+ * ```
61
+ *
62
+ * @example
63
+ * With map mode and event handlers (module-level constant for sharing):
64
+ * ```tsx
65
+ * import { BaseMap } from '@accelint/map-toolkit/deckgl';
66
+ * import { useMapMode } from '@accelint/map-toolkit/map-mode';
67
+ * import { uuid } from '@accelint/core';
68
+ * import type { PickingInfo } from '@deck.gl/core';
69
+ * import type { MjolnirGestureEvent } from 'mjolnir.js';
70
+ *
71
+ * // Module-level constant - stable and shareable across all components
72
+ * const MAIN_MAP_ID = uuid();
73
+ *
74
+ * function Toolbar() {
75
+ * // Access map mode using the shared id
76
+ * const { mode, requestModeChange } = useMapMode(MAIN_MAP_ID);
77
+ * return <div>Current mode: {mode}</div>;
78
+ * }
79
+ *
80
+ * export function InteractiveMap() {
81
+ * const handleClick = (info: PickingInfo, event: MjolnirGestureEvent) => {
82
+ * console.log('Clicked:', info.object);
83
+ * };
84
+ *
85
+ * return (
86
+ * <div className="relative w-full h-full">
87
+ * <BaseMap className="absolute inset-0" id={MAIN_MAP_ID} onClick={handleClick}>
88
+ * <View id="main" controller />
89
+ * </BaseMap>
90
+ * <Toolbar />
91
+ * </div>
92
+ * );
93
+ * }
94
+ * ```
95
+ */
96
+ declare function BaseMap({ className, children, id, onClick, onHover, parameters, ...rest }: BaseMapProps): react_jsx_runtime.JSX.Element;
97
+
98
+ export { BaseMap, type BaseMapProps };
@@ -0,0 +1,99 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import 'client-only';
3
+ import { useEmit } from '@accelint/bus/react';
4
+ import { useDeckgl, Deckgl } from '@deckgl-fiber-renderer/dom';
5
+ import { useId, useMemo, useCallback } from 'react';
6
+ import { INITIAL_VIEW_STATE } from '../../maplibre/constants.js';
7
+ import { useMapLibre } from '../../maplibre/hooks/use-maplibre.js';
8
+ import { BASE_MAP_STYLE, PARAMETERS } from './constants.js';
9
+ import { MapEvents } from './events.js';
10
+ import { MapProvider } from './provider.js';
11
+
12
+ function BaseMap({
13
+ className,
14
+ children,
15
+ id,
16
+ onClick,
17
+ onHover,
18
+ parameters,
19
+ ...rest
20
+ }) {
21
+ const deckglInstance = useDeckgl();
22
+ const container = useId();
23
+ const mapOptions = useMemo(
24
+ () => ({
25
+ container,
26
+ center: [INITIAL_VIEW_STATE.longitude, INITIAL_VIEW_STATE.latitude],
27
+ zoom: INITIAL_VIEW_STATE.zoom,
28
+ doubleClickZoom: false,
29
+ dragRotate: false,
30
+ pitchWithRotate: false,
31
+ rollEnabled: false
32
+ }),
33
+ [container]
34
+ );
35
+ useMapLibre(deckglInstance, BASE_MAP_STYLE, mapOptions);
36
+ const emitClick = useEmit(MapEvents.click);
37
+ const emitHover = useEmit(MapEvents.hover);
38
+ const handleMapClick = useCallback(
39
+ (info, event) => {
40
+ onClick?.(info, event);
41
+ const { viewport, ...infoRest } = info;
42
+ const {
43
+ stopImmediatePropagation,
44
+ stopPropagation,
45
+ preventDefault,
46
+ srcEvent,
47
+ rootElement,
48
+ target,
49
+ changedPointers,
50
+ pointers,
51
+ ...eventRest
52
+ } = event;
53
+ emitClick({
54
+ info: infoRest,
55
+ event: eventRest,
56
+ id
57
+ });
58
+ },
59
+ [emitClick, id, onClick]
60
+ );
61
+ const handleMapHover = useCallback(
62
+ (info, event) => {
63
+ onHover?.(info, event);
64
+ const { viewport, ...infoRest } = info;
65
+ const {
66
+ stopImmediatePropagation,
67
+ stopPropagation,
68
+ preventDefault,
69
+ srcEvent,
70
+ rootElement,
71
+ target,
72
+ ...eventRest
73
+ } = event;
74
+ emitHover({
75
+ info: infoRest,
76
+ event: eventRest,
77
+ id
78
+ });
79
+ },
80
+ [emitHover, id, onHover]
81
+ );
82
+ return /* @__PURE__ */ jsx("div", { id: container, className, children: /* @__PURE__ */ jsx(MapProvider, { id, children: /* @__PURE__ */ jsx(
83
+ Deckgl,
84
+ {
85
+ ...rest,
86
+ controller: true,
87
+ interleaved: true,
88
+ useDevicePixels: false,
89
+ onHover: handleMapHover,
90
+ onClick: handleMapClick,
91
+ parameters: { ...PARAMETERS, ...parameters },
92
+ children
93
+ }
94
+ ) }) });
95
+ }
96
+
97
+ export { BaseMap };
98
+ //# sourceMappingURL=index.js.map
99
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/deckgl/base-map/index.tsx"],"names":[],"mappings":"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","file":"index.js","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 { useEmit } from '@accelint/bus/react';\nimport { Deckgl, useDeckgl } from '@deckgl-fiber-renderer/dom';\nimport { useCallback, useId, useMemo } from 'react';\nimport { INITIAL_VIEW_STATE } from '../../maplibre/constants';\nimport { useMapLibre } from '../../maplibre/hooks/use-maplibre';\nimport { BASE_MAP_STYLE, PARAMETERS } from './constants';\nimport { MapEvents } from './events';\nimport { MapProvider } from './provider';\nimport type { UniqueId } from '@accelint/core';\nimport type { PickingInfo } from '@deck.gl/core';\nimport type { DeckglProps } from '@deckgl-fiber-renderer/types';\nimport type { IControl } from 'maplibre-gl';\nimport type { MjolnirGestureEvent, MjolnirPointerEvent } from 'mjolnir.js';\nimport type { MapClickEvent, MapHoverEvent } from './types';\n\n/**\n * Props for the BaseMap component.\n * Extends all Deck.gl props and adds additional map-specific properties.\n */\nexport type BaseMapProps = DeckglProps & {\n /** Optional CSS class name to apply to the map container element */\n className?: string;\n /**\n * Unique identifier for this map instance (required).\n *\n * Used to isolate map mode state between multiple map instances (e.g., main map vs minimap).\n * This should be a UUID generated using `uuid()` from `@accelint/core`.\n *\n * The same id should be passed to `useMapMode()` when accessing map mode state\n * from components rendered outside of the BaseMap's children (i.e., as siblings).\n */\n id: UniqueId;\n};\n\n/**\n * A React component that provides a Deck.gl-powered base map with MapLibre GL integration.\n *\n * This component serves as the foundation for building interactive map applications with\n * support for click and hover events through a centralized event bus. It integrates\n * Deck.gl for 3D visualizations with MapLibre GL for the base map tiles.\n *\n * **Map Mode Integration**: BaseMap automatically creates a `MapProvider` internally,\n * which sets up the map mode state management for this instance.\n * - **Children**: Only Deck.gl layer components can be rendered as children. Custom Deck.gl\n * layers can use `useMapMode()` without parameters to access context.\n * - **Siblings**: UI components (buttons, toolbars, etc.) must be rendered as siblings\n * and pass `id` to `useMapMode(id)`.\n *\n * **Event Bus**: Click and hover events are emitted through the event bus with the `id`\n * included in the payload, allowing multiple map instances to coexist without interference.\n *\n * @param props - Component props including id (required), className, onClick, onHover, and all Deck.gl props\n * @returns A map component with Deck.gl and MapLibre GL integration\n *\n * @example\n * Basic usage with id (recommended: module-level constant):\n * ```tsx\n * import { BaseMap } from '@accelint/map-toolkit/deckgl';\n * import { View } from '@deckgl-fiber-renderer/dom';\n * import { uuid } from '@accelint/core';\n *\n * // Create id at module level for stability and easy sharing\n * const MAIN_MAP_ID = uuid();\n *\n * export function MapView() {\n * return (\n * <BaseMap className=\"w-full h-full\" id={MAIN_MAP_ID}>\n * <View id=\"main\" controller />\n * </BaseMap>\n * );\n * }\n * ```\n *\n * @example\n * With map mode and event handlers (module-level constant for sharing):\n * ```tsx\n * import { BaseMap } from '@accelint/map-toolkit/deckgl';\n * import { useMapMode } from '@accelint/map-toolkit/map-mode';\n * import { uuid } from '@accelint/core';\n * import type { PickingInfo } from '@deck.gl/core';\n * import type { MjolnirGestureEvent } from 'mjolnir.js';\n *\n * // Module-level constant - stable and shareable across all components\n * const MAIN_MAP_ID = uuid();\n *\n * function Toolbar() {\n * // Access map mode using the shared id\n * const { mode, requestModeChange } = useMapMode(MAIN_MAP_ID);\n * return <div>Current mode: {mode}</div>;\n * }\n *\n * export function InteractiveMap() {\n * const handleClick = (info: PickingInfo, event: MjolnirGestureEvent) => {\n * console.log('Clicked:', info.object);\n * };\n *\n * return (\n * <div className=\"relative w-full h-full\">\n * <BaseMap className=\"absolute inset-0\" id={MAIN_MAP_ID} onClick={handleClick}>\n * <View id=\"main\" controller />\n * </BaseMap>\n * <Toolbar />\n * </div>\n * );\n * }\n * ```\n */\nexport function BaseMap({\n className,\n children,\n id,\n onClick,\n onHover,\n parameters,\n ...rest\n}: BaseMapProps) {\n const deckglInstance = useDeckgl();\n const container = useId();\n\n // Memoize MapLibre options to avoid creating new object on every render\n const mapOptions = useMemo(\n () => ({\n container,\n center: [INITIAL_VIEW_STATE.longitude, INITIAL_VIEW_STATE.latitude] as [\n number,\n number,\n ],\n zoom: INITIAL_VIEW_STATE.zoom,\n doubleClickZoom: false,\n dragRotate: false,\n pitchWithRotate: false,\n rollEnabled: false,\n }),\n [container],\n );\n\n // Use the custom hook to handle MapLibre\n useMapLibre(deckglInstance as IControl, BASE_MAP_STYLE, mapOptions);\n\n const emitClick = useEmit<MapClickEvent>(MapEvents.click);\n const emitHover = useEmit<MapHoverEvent>(MapEvents.hover);\n\n const handleMapClick = useCallback(\n (info: PickingInfo, event: MjolnirGestureEvent) => {\n // send full pickingInfo and event to user-defined onClick\n onClick?.(info, event);\n\n // the bus cannot serialize functions, so we omit them from the event payloads\n const { viewport, ...infoRest } = info;\n const {\n stopImmediatePropagation,\n stopPropagation,\n preventDefault,\n srcEvent,\n rootElement,\n target,\n changedPointers,\n pointers,\n ...eventRest\n } = event;\n\n emitClick({\n info: infoRest,\n event: eventRest,\n id,\n });\n },\n [emitClick, id, onClick],\n );\n\n const handleMapHover = useCallback(\n (info: PickingInfo, event: MjolnirPointerEvent) => {\n // send full pickingInfo and event to user-defined onHover\n onHover?.(info, event);\n\n // the bus cannot serialize functions, so we omit them from the event payloads\n const { viewport, ...infoRest } = info;\n const {\n stopImmediatePropagation,\n stopPropagation,\n preventDefault,\n srcEvent,\n rootElement,\n target,\n ...eventRest\n } = event;\n\n emitHover({\n info: infoRest,\n event: eventRest,\n id,\n });\n },\n [emitHover, id, onHover],\n );\n\n return (\n <div id={container} className={className}>\n <MapProvider id={id}>\n <Deckgl\n {...rest}\n controller\n interleaved\n useDevicePixels={false}\n onHover={handleMapHover}\n onClick={handleMapClick}\n // @ts-expect-error - DeckglProps parameters type is overly strict for WebGL parameter spreading.\n // The merged object is valid at runtime but TypeScript cannot verify all possible parameter combinations.\n parameters={{ ...PARAMETERS, ...parameters }}\n >\n {children}\n </Deckgl>\n </MapProvider>\n </div>\n );\n}\n"]}
@@ -0,0 +1,133 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { UniqueId } from '@accelint/core';
4
+
5
+ /**
6
+ * React context for map ID.
7
+ * Use the `useMapMode` hook to access the map mode state.
8
+ */
9
+ declare const MapContext: React.Context<UniqueId | null>;
10
+ /**
11
+ * Props for the MapProvider component.
12
+ */
13
+ type MapProviderProps = {
14
+ /** Child components that will have access to map mode context */
15
+ children: ReactNode;
16
+ /**
17
+ * Unique identifier for this map instance.
18
+ *
19
+ * Used to isolate mode changes between different map instances (e.g., main map vs minimap).
20
+ * This is required and should be provided by the parent component (typically BaseMap).
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * // Multiple independent map instances
25
+ * const mainMapId = uuid();
26
+ * const minimapId = uuid();
27
+ *
28
+ * <MapProvider id={mainMapId}>
29
+ * // Map layers and components
30
+ * </MapProvider>
31
+ *
32
+ * <MapProvider id={minimapId}>
33
+ * // Minimap layers and components
34
+ * </MapProvider>
35
+ * ```
36
+ */
37
+ id: UniqueId;
38
+ };
39
+ /**
40
+ * Provider component for managing map modes with ownership and authorization.
41
+ *
42
+ * **Note**: This provider is used internally by `BaseMap` and should not be used directly.
43
+ * Consumers should pass the `id` prop to `BaseMap`, which will create this provider automatically.
44
+ *
45
+ * This component uses a hybrid architecture combining React Context (for map instance identity)
46
+ * with an external observable store (for state management). The provider:
47
+ * - Provides a unique `id` via Context
48
+ * - Creates an isolated MapModeStore instance for this map
49
+ * - Allows components to subscribe to mode changes via `useMapMode` hook (which uses `useSyncExternalStore`)
50
+ *
51
+ * The external store manages a state machine for map modes where components can request
52
+ * mode changes with ownership. When a mode is owned by a component, other components
53
+ * must request authorization to change to a different mode. The store handles:
54
+ *
55
+ * - Automatic mode changes when no ownership conflicts exist
56
+ * - Authorization flow when switching between owned modes
57
+ * - Per-mode ownership tracking that persists throughout the session
58
+ * - Pending request management (one pending request per requester)
59
+ * - Auto-acceptance of first pending request when mode owner returns to default
60
+ * - Auto-rejection of other pending requests when one is approved
61
+ * - Event emission through a centralized event bus
62
+ * - Instance isolation for multiple map scenarios (main map + minimap)
63
+ * - Always initializes in 'default' mode
64
+ *
65
+ * ## Instance Isolation
66
+ *
67
+ * Each MapProvider instance operates independently. Mode changes in one instance
68
+ * do not affect other instances, even when multiple maps are rendered on the same page.
69
+ * This enables scenarios like:
70
+ * - Main map in "drawing" mode while minimap stays in "view" mode
71
+ * - Multiple independent map views with different interaction modes
72
+ *
73
+ * Events are scoped to specific instances using the `id` prop. The event bus
74
+ * filters events to ensure each provider only responds to events for its own instance.
75
+ *
76
+ * ## Pending Request Behavior
77
+ *
78
+ * - Pending requests are stored by requester ID (not mode owner)
79
+ * - Each requester can have only one pending request at a time
80
+ * - New requests from the same requester auto-replace previous requests
81
+ * - Pending requests persist when mode owner switches between their own modes
82
+ * - When any request is approved, all other pending requests are auto-rejected
83
+ * - When mode owner returns to default mode:
84
+ * - If first pending request is for default mode, all pending requests are rejected (already in requested mode)
85
+ * - If first pending request is for a different mode, that request is auto-approved and others are rejected
86
+ *
87
+ * ## Instance ID Stability and Lifecycle
88
+ *
89
+ * The provider uses React's `key` prop to force a complete remount when the `id` changes.
90
+ * This ensures:
91
+ * - Clean cleanup of the old store (no memory leaks)
92
+ * - Fresh initialization for the new ID
93
+ * - All child components remount with the new context
94
+ *
95
+ * While the `id` prop should typically remain stable (created as a module-level constant
96
+ * or with `useState`), changing it will work correctly due to the remount behavior.
97
+ *
98
+ * @param props - Provider props including children and required id
99
+ * @returns Provider component that wraps children with map instance identity context
100
+ *
101
+ * @example
102
+ * Internal usage within BaseMap:
103
+ * ```tsx
104
+ * // BaseMap automatically creates the provider
105
+ * function BaseMap({ id, children, ...props }: BaseMapProps) {
106
+ * return (
107
+ * <div>
108
+ * <MapProvider id={id}>
109
+ * <Deckgl {...props}>
110
+ * {children}
111
+ * </Deckgl>
112
+ * </MapProvider>
113
+ * </div>
114
+ * );
115
+ * }
116
+ * ```
117
+ *
118
+ * @example
119
+ * With authorization handling - use id in event payloads:
120
+ * ```tsx
121
+ * useOn(MapModeEvents.changeAuthorization, (event) => {
122
+ * const { authId, id } = event.payload;
123
+ * emitDecision({ authId, approved: true, owner: 'tool', id });
124
+ * });
125
+ * ```
126
+ */
127
+ /**
128
+ * Public wrapper that forces remount when id changes.
129
+ * This ensures clean lifecycle management and prevents memory leaks.
130
+ */
131
+ declare function MapProvider({ children, id }: MapProviderProps): react_jsx_runtime.JSX.Element;
132
+
133
+ export { MapContext, MapProvider, type MapProviderProps };
@@ -0,0 +1,22 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import 'client-only';
3
+ import { createContext, useEffect } from 'react';
4
+ import { getOrCreateStore, destroyStore } from '../../map-mode/store.js';
5
+
6
+ const MapContext = createContext(null);
7
+ function MapProvider({ children, id }) {
8
+ return /* @__PURE__ */ jsx(MapProviderInternal, { id, children }, id);
9
+ }
10
+ function MapProviderInternal({ children, id }) {
11
+ getOrCreateStore(id);
12
+ useEffect(() => {
13
+ return () => {
14
+ destroyStore(id);
15
+ };
16
+ }, [id]);
17
+ return /* @__PURE__ */ jsx(MapContext.Provider, { value: id, children });
18
+ }
19
+
20
+ export { MapContext, MapProvider };
21
+ //# sourceMappingURL=provider.js.map
22
+ //# sourceMappingURL=provider.js.map