@accelint/map-toolkit 0.6.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/catalog-info.yaml +5 -4
  3. package/dist/camera/index.d.ts +2 -2
  4. package/dist/camera/index.js +2 -2
  5. package/dist/camera/store.d.ts +120 -0
  6. package/dist/camera/store.js +279 -0
  7. package/dist/camera/store.js.map +1 -0
  8. package/dist/deckgl/base-map/constants.d.ts +1 -6
  9. package/dist/deckgl/base-map/constants.js +1 -6
  10. package/dist/deckgl/base-map/constants.js.map +1 -1
  11. package/dist/deckgl/base-map/controls.js +2 -0
  12. package/dist/deckgl/base-map/controls.js.map +1 -1
  13. package/dist/deckgl/base-map/index.d.ts +2 -2
  14. package/dist/deckgl/base-map/index.js +10 -11
  15. package/dist/deckgl/base-map/index.js.map +1 -1
  16. package/dist/deckgl/base-map/provider.js +1 -1
  17. package/dist/deckgl/index.d.ts +4 -4
  18. package/dist/deckgl/index.js +4 -4
  19. package/dist/deckgl/saved-viewports/storage.js +10 -2
  20. package/dist/deckgl/saved-viewports/storage.js.map +1 -1
  21. package/dist/deckgl/shapes/display-shape-layer/constants.js +5 -8
  22. package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -1
  23. package/dist/deckgl/shapes/display-shape-layer/index.d.ts +18 -14
  24. package/dist/deckgl/shapes/display-shape-layer/index.js +63 -30
  25. package/dist/deckgl/shapes/display-shape-layer/index.js.map +1 -1
  26. package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js +2 -16
  27. package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js.map +1 -1
  28. package/dist/deckgl/shapes/display-shape-layer/store.js +58 -272
  29. package/dist/deckgl/shapes/display-shape-layer/store.js.map +1 -1
  30. package/dist/deckgl/shapes/display-shape-layer/types.d.ts +22 -11
  31. package/dist/deckgl/shapes/display-shape-layer/{use-shape-selection.d.ts → use-select-shape.d.ts} +9 -9
  32. package/dist/deckgl/shapes/display-shape-layer/{use-shape-selection.js → use-select-shape.js} +12 -12
  33. package/dist/deckgl/shapes/display-shape-layer/use-select-shape.js.map +1 -0
  34. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +5 -66
  35. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -1
  36. package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +2 -65
  37. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +3 -121
  38. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -1
  39. package/dist/deckgl/shapes/draw-shape-layer/constants.js +46 -0
  40. package/dist/deckgl/shapes/draw-shape-layer/constants.js.map +1 -0
  41. package/dist/deckgl/shapes/draw-shape-layer/events.d.ts +92 -0
  42. package/dist/deckgl/shapes/draw-shape-layer/events.js +56 -0
  43. package/dist/deckgl/shapes/draw-shape-layer/events.js.map +1 -0
  44. package/dist/deckgl/shapes/draw-shape-layer/fiber.d.ts +11 -0
  45. package/dist/{maplibre/constants.js → deckgl/shapes/draw-shape-layer/fiber.js} +6 -12
  46. package/dist/deckgl/shapes/draw-shape-layer/fiber.js.map +1 -0
  47. package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +53 -0
  48. package/dist/deckgl/shapes/draw-shape-layer/index.js +95 -0
  49. package/dist/deckgl/shapes/draw-shape-layer/index.js.map +1 -0
  50. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +51 -0
  51. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js.map +1 -0
  52. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js +73 -0
  53. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js.map +1 -0
  54. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +87 -0
  55. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js.map +1 -0
  56. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +88 -0
  57. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js.map +1 -0
  58. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +77 -0
  59. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js.map +1 -0
  60. package/dist/deckgl/shapes/draw-shape-layer/modes/index.js +64 -0
  61. package/dist/deckgl/shapes/draw-shape-layer/modes/index.js.map +1 -0
  62. package/dist/deckgl/shapes/draw-shape-layer/store.js +175 -0
  63. package/dist/deckgl/shapes/draw-shape-layer/store.js.map +1 -0
  64. package/dist/deckgl/shapes/draw-shape-layer/types.d.ts +86 -0
  65. package/dist/{viewport/constants.js → deckgl/shapes/draw-shape-layer/types.js} +1 -12
  66. package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.d.ts +82 -0
  67. package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.js +112 -0
  68. package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.js.map +1 -0
  69. package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js +147 -0
  70. package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js.map +1 -0
  71. package/dist/deckgl/shapes/edit-shape-layer/constants.js +41 -0
  72. package/dist/deckgl/shapes/edit-shape-layer/constants.js.map +1 -0
  73. package/dist/deckgl/shapes/edit-shape-layer/events.d.ts +92 -0
  74. package/dist/deckgl/shapes/edit-shape-layer/events.js +56 -0
  75. package/dist/deckgl/shapes/edit-shape-layer/events.js.map +1 -0
  76. package/dist/deckgl/shapes/edit-shape-layer/fiber.d.ts +13 -0
  77. package/dist/deckgl/shapes/edit-shape-layer/fiber.js +14 -0
  78. package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +63 -0
  79. package/dist/deckgl/shapes/edit-shape-layer/index.js +162 -0
  80. package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -0
  81. package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js +154 -0
  82. package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js.map +1 -0
  83. package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +147 -0
  84. package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js.map +1 -0
  85. package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +87 -0
  86. package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js.map +1 -0
  87. package/dist/deckgl/shapes/edit-shape-layer/modes/index.js +61 -0
  88. package/dist/deckgl/shapes/edit-shape-layer/modes/index.js.map +1 -0
  89. package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js +109 -0
  90. package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js.map +1 -0
  91. package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js +289 -0
  92. package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js.map +1 -0
  93. package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js +121 -0
  94. package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js.map +1 -0
  95. package/dist/deckgl/shapes/edit-shape-layer/store.js +194 -0
  96. package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -0
  97. package/dist/deckgl/shapes/edit-shape-layer/types.d.ts +93 -0
  98. package/dist/deckgl/shapes/edit-shape-layer/types.js +14 -0
  99. package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.d.ts +82 -0
  100. package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js +114 -0
  101. package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js.map +1 -0
  102. package/dist/deckgl/shapes/index.d.ts +15 -6
  103. package/dist/deckgl/shapes/index.js +12 -5
  104. package/dist/deckgl/shapes/shared/constants.d.ts +27 -32
  105. package/dist/deckgl/shapes/shared/constants.js +189 -25
  106. package/dist/deckgl/shapes/shared/constants.js.map +1 -1
  107. package/dist/deckgl/shapes/shared/events.d.ts +1 -20
  108. package/dist/deckgl/shapes/shared/events.js +1 -31
  109. package/dist/deckgl/shapes/shared/events.js.map +1 -1
  110. package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js +84 -0
  111. package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js.map +1 -0
  112. package/dist/deckgl/shapes/shared/types.d.ts +187 -28
  113. package/dist/deckgl/shapes/shared/types.js +55 -1
  114. package/dist/deckgl/shapes/shared/types.js.map +1 -1
  115. package/dist/deckgl/shapes/shared/utils/geometry-measurements.js +128 -0
  116. package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -0
  117. package/dist/deckgl/shapes/shared/utils/layer-config.js +50 -0
  118. package/dist/deckgl/shapes/shared/utils/layer-config.js.map +1 -0
  119. package/dist/deckgl/shapes/shared/utils/mode-utils.js +113 -0
  120. package/dist/deckgl/shapes/shared/utils/mode-utils.js.map +1 -0
  121. package/dist/deckgl/shapes/shared/utils/pick-filtering.js +57 -0
  122. package/dist/deckgl/shapes/shared/utils/pick-filtering.js.map +1 -0
  123. package/dist/deckgl/shapes/shared/utils/style-utils.d.ts +64 -0
  124. package/dist/deckgl/shapes/shared/utils/style-utils.js +101 -0
  125. package/dist/deckgl/shapes/shared/utils/style-utils.js.map +1 -0
  126. package/dist/deckgl/text-layer/default-settings.js +4 -24
  127. package/dist/deckgl/text-layer/default-settings.js.map +1 -1
  128. package/dist/deckgl/text-settings.d.ts +77 -0
  129. package/dist/deckgl/text-settings.js +83 -0
  130. package/dist/deckgl/text-settings.js.map +1 -0
  131. package/dist/map-cursor/index.d.ts +2 -2
  132. package/dist/map-cursor/index.js +2 -2
  133. package/dist/map-cursor/store.d.ts +32 -61
  134. package/dist/map-cursor/store.js +165 -294
  135. package/dist/map-cursor/store.js.map +1 -1
  136. package/dist/map-cursor/use-map-cursor.d.ts +5 -2
  137. package/dist/map-cursor/use-map-cursor.js +33 -15
  138. package/dist/map-cursor/use-map-cursor.js.map +1 -1
  139. package/dist/map-mode/index.d.ts +2 -2
  140. package/dist/map-mode/index.js +2 -2
  141. package/dist/map-mode/store.d.ts +36 -37
  142. package/dist/map-mode/store.js +131 -237
  143. package/dist/map-mode/store.js.map +1 -1
  144. package/dist/map-mode/use-map-mode.js +6 -5
  145. package/dist/map-mode/use-map-mode.js.map +1 -1
  146. package/dist/maplibre/index.d.ts +2 -2
  147. package/dist/maplibre/index.js +2 -2
  148. package/dist/shared/constants.d.ts +19 -0
  149. package/dist/shared/constants.js +33 -0
  150. package/dist/shared/constants.js.map +1 -0
  151. package/dist/shared/create-map-store.d.ts +202 -0
  152. package/dist/shared/create-map-store.js +223 -0
  153. package/dist/shared/create-map-store.js.map +1 -0
  154. package/dist/shared/units.d.ts +39 -0
  155. package/dist/shared/units.js +49 -0
  156. package/dist/shared/units.js.map +1 -0
  157. package/dist/viewport/index.d.ts +3 -3
  158. package/dist/viewport/index.js +3 -3
  159. package/dist/viewport/store.d.ts +69 -0
  160. package/dist/viewport/store.js +125 -0
  161. package/dist/viewport/store.js.map +1 -0
  162. package/dist/viewport/types.d.ts +2 -2
  163. package/dist/viewport/utils.js +2 -2
  164. package/dist/viewport/utils.js.map +1 -1
  165. package/dist/viewport/viewport-size.d.ts +2 -2
  166. package/dist/viewport/viewport-size.js +2 -2
  167. package/dist/viewport/viewport-size.js.map +1 -1
  168. package/package.json +36 -18
  169. package/dist/camera/use-camera-state.d.ts +0 -153
  170. package/dist/camera/use-camera-state.js +0 -418
  171. package/dist/camera/use-camera-state.js.map +0 -1
  172. package/dist/deckgl/shapes/display-shape-layer/constants.d.ts +0 -44
  173. package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.d.ts +0 -66
  174. package/dist/deckgl/shapes/display-shape-layer/store.d.ts +0 -87
  175. package/dist/deckgl/shapes/display-shape-layer/use-shape-selection.js.map +0 -1
  176. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.d.ts +0 -61
  177. package/dist/maplibre/constants.d.ts +0 -13
  178. package/dist/maplibre/constants.js.map +0 -1
  179. package/dist/viewport/constants.d.ts +0 -11
  180. package/dist/viewport/constants.js.map +0 -1
  181. package/dist/viewport/use-viewport-state.d.ts +0 -100
  182. package/dist/viewport/use-viewport-state.js +0 -222
  183. package/dist/viewport/use-viewport-state.js.map +0 -1
@@ -10,84 +10,55 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
+ import { MapStore } from "../shared/create-map-store.js";
13
14
  import { CSSCursorType } from "./types.js";
14
15
  import { UniqueId } from "@accelint/core";
15
16
 
16
17
  //#region src/map-cursor/store.d.ts
17
18
  /**
18
- * Creates or retrieves a cached subscription function for a given instanceId.
19
- * Uses a fan-out pattern: 1 bus listener -> N React subscribers.
20
- * Automatically cleans up cursor state when the last subscriber unsubscribes.
21
- *
22
- * @param instanceId - The unique identifier for the map cursor instance
23
- * @returns A subscription function for useSyncExternalStore
19
+ * State shape for map cursor
24
20
  */
25
- declare function getOrCreateSubscription(instanceId: UniqueId): (onStoreChange: () => void) => () => void;
21
+ type CursorState = {
22
+ /** Map of owner -> cursor */
23
+ cursorOwners: Map<string, CSSCursorType>;
24
+ /** Current active cursor (for priority tracking) */
25
+ currentCursor: CSSCursorType | null;
26
+ /** Current cursor owner (for priority tracking) */
27
+ currentOwner: string | null;
28
+ };
26
29
  /**
27
- * Creates or retrieves a cached snapshot function for a given instanceId.
28
- * The cursor returned gets equality checked, so it needs to be stable or React re-renders unnecessarily.
29
- *
30
- * @param instanceId - The unique identifier for the map cursor instance
31
- * @returns A snapshot function for useSyncExternalStore
30
+ * Actions for cursor management
32
31
  */
33
- declare function getOrCreateSnapshot(instanceId: UniqueId): () => CSSCursorType;
32
+ type CursorActions = {
33
+ /** Request a cursor change */
34
+ requestCursorChange: (cursor: CSSCursorType, owner: string) => void;
35
+ /** Clear cursor for an owner */
36
+ clearCursor: (owner: string) => void;
37
+ };
34
38
  /**
35
- * Creates or retrieves a cached server snapshot function for a given instanceId.
36
- * Server snapshots always return the default cursor since cursor state is client-only.
37
- * Required for SSR/RSC compatibility with useSyncExternalStore.
38
- *
39
- * @param instanceId - The unique identifier for the map cursor instance
40
- * @returns A server snapshot function for useSyncExternalStore
39
+ * Cursor store
41
40
  */
42
- declare function getOrCreateServerSnapshot(instanceId: UniqueId): () => CSSCursorType;
41
+ declare const cursorStore: MapStore<CursorState, CursorActions>;
43
42
  /**
44
- * Creates or retrieves a cached requestCursorChange function for a given instanceId.
45
- * This maintains referential stability for the function reference.
46
- *
47
- * @param instanceId - The unique identifier for the map cursor instance
48
- * @returns A requestCursorChange function for this instance
43
+ * Get effective cursor (computed from state)
49
44
  */
50
- declare function getOrCreateRequestCursorChange(instanceId: UniqueId): (cursor: CSSCursorType, owner: string) => void;
45
+ declare function getCursor(mapId: UniqueId): CSSCursorType;
51
46
  /**
52
- * Creates or retrieves a cached clearCursor function for a given instanceId.
53
- * This maintains referential stability for the function reference.
47
+ * Hook for effective cursor value.
54
48
  *
55
- * @param instanceId - The unique identifier for the map cursor instance
56
- * @returns A clearCursor function for this instance
57
- */
58
- declare function getOrCreateClearCursor(instanceId: UniqueId): (owner: string) => void;
59
- /**
60
- * Get the current cursor for a given instance (direct access, not reactive).
61
- * @internal - For internal map-toolkit use only
49
+ * **Internal use only** - not exported from the public API.
50
+ * Use `useMapCursor` instead, which provides:
51
+ * - MapContext integration (auto-resolves mapId inside MapProvider)
52
+ * - Actions (requestCursorChange, clearCursor)
53
+ * - Better ergonomics for consumers
62
54
  *
63
- * @param instanceId - The unique identifier for the map instance
64
- * @returns The current cursor
55
+ * This hook exists for internal composition (used by useMapCursor).
65
56
  */
66
- declare function getCursor(instanceId: UniqueId): CSSCursorType;
57
+ declare function useCursor(mapId: UniqueId): CSSCursorType;
67
58
  /**
68
- * Manually clear all cursor state for a specific instanceId.
69
- * This is typically not needed as cleanup happens automatically when all subscribers unmount.
70
- * Use this only in advanced scenarios where manual cleanup is required.
71
- *
72
- * @param instanceId - The unique identifier for the map cursor instance to clear
73
- *
74
- * @example
75
- * ```tsx
76
- * // Manual cleanup (rarely needed)
77
- * clearCursorState('my-map-instance');
78
- * ```
59
+ * Clear cursor state
79
60
  */
80
- declare function clearCursorState(instanceId: UniqueId): void;
81
- /** @deprecated Use module functions directly */
82
- declare function getOrCreateStore(id: UniqueId): {
83
- getSnapshot: () => CSSCursorType;
84
- };
85
- /** @deprecated Use clearCursorState instead */
86
- declare function destroyStore(id: UniqueId): void;
87
- /** @deprecated Use direct function calls instead */
88
- declare function getStore(id: UniqueId): {
89
- getSnapshot: () => CSSCursorType;
90
- } | undefined;
61
+ declare function clearCursorState(mapId: UniqueId): void;
91
62
  //#endregion
92
- export { clearCursorState, destroyStore, getCursor, getOrCreateClearCursor, getOrCreateRequestCursorChange, getOrCreateServerSnapshot, getOrCreateSnapshot, getOrCreateStore, getOrCreateSubscription, getStore };
63
+ export { clearCursorState, cursorStore, getCursor, useCursor };
93
64
  //# sourceMappingURL=store.d.ts.map
@@ -11,98 +11,72 @@
11
11
  */
12
12
 
13
13
 
14
+ import { createMapStore, mapDelete, mapSet } from "../shared/create-map-store.js";
14
15
  import { MapModeEvents } from "../map-mode/events.js";
15
- import { getCurrentModeOwner } from "../map-mode/store.js";
16
16
  import { MapCursorEvents } from "./events.js";
17
17
  import { Broadcast } from "@accelint/bus";
18
18
 
19
19
  //#region src/map-cursor/store.ts
20
20
  /**
21
- * Default cursor value
21
+ * Map Cursor Store
22
+ *
23
+ * Manages cursor state with ownership-based priority.
24
+ *
25
+ * Priority order:
26
+ * 1. Mode owner's cursor (if mode is owned)
27
+ * 2. Most recent cursor request
28
+ * 3. Default cursor
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * import { cursorStore } from '@accelint/map-toolkit/map-cursor';
33
+ *
34
+ * function MapContainer({ mapId }) {
35
+ * const cursor = cursorStore.useSelector(mapId, (s) => getEffectiveCursor(mapId, s));
36
+ * return <div style={{ cursor }}>...</div>;
37
+ * }
38
+ *
39
+ * // Request cursor change from a layer:
40
+ * cursorStore.actions(mapId).requestCursorChange('crosshair', 'draw-layer');
41
+ * ```
22
42
  */
23
43
  const DEFAULT_CURSOR = "default";
44
+ const cursorBus = Broadcast.getInstance();
45
+ const modeBus = Broadcast.getInstance();
24
46
  /**
25
- * Typed event bus instance for map cursor events.
26
- * Provides type-safe event emission and listening for all map cursor state changes.
27
- */
28
- const mapCursorBus = Broadcast.getInstance();
29
- /**
30
- * Store for map cursor state keyed by instanceId
31
- */
32
- const cursorStore = /* @__PURE__ */ new Map();
33
- /**
34
- * Track React component subscribers per instanceId (for fan-out notifications).
35
- * Each Set contains onStoreChange callbacks from useSyncExternalStore.
36
- */
37
- const componentSubscribers = /* @__PURE__ */ new Map();
38
- /**
39
- * Cache of bus unsubscribe functions (1 per instanceId).
40
- * This ensures we only have one bus listener per map cursor instance, regardless of
41
- * how many React components subscribe to it.
42
- */
43
- const busUnsubscribers = /* @__PURE__ */ new Map();
44
- /**
45
- * Cache of subscription functions per instanceId to avoid recreating on every render
46
- */
47
- const subscriptionCache = /* @__PURE__ */ new Map();
48
- /**
49
- * Cache of snapshot functions per instanceId to maintain referential stability
50
- */
51
- const snapshotCache = /* @__PURE__ */ new Map();
52
- /**
53
- * Cache of server snapshot functions per instanceId to maintain referential stability.
54
- * Server snapshots always return default cursor since cursor state is client-only.
55
- */
56
- const serverSnapshotCache = /* @__PURE__ */ new Map();
57
- /**
58
- * Cache of requestCursorChange functions per instanceId to maintain referential stability
59
- */
60
- const requestCursorChangeCache = /* @__PURE__ */ new Map();
61
- /**
62
- * Cache of clearCursor functions per instanceId to maintain referential stability
63
- */
64
- const clearCursorCache = /* @__PURE__ */ new Map();
65
- /**
66
- * All state caches that need cleanup when an instance is removed
67
- */
68
- const stateCaches = [
69
- cursorStore,
70
- componentSubscribers,
71
- subscriptionCache,
72
- snapshotCache,
73
- serverSnapshotCache,
74
- requestCursorChangeCache,
75
- clearCursorCache
76
- ];
77
- /**
78
- * Clear all cached state for a given instanceId
79
- */
80
- function clearAllCaches(instanceId) {
81
- stateCaches.map((cache) => cache.delete(instanceId));
82
- }
83
- /**
84
- * Get or create cursor state for a given instanceId
85
- */
86
- function getOrCreateState(instanceId) {
87
- if (!cursorStore.has(instanceId)) cursorStore.set(instanceId, {
88
- cursorOwners: /* @__PURE__ */ new Map(),
89
- currentCursor: null,
90
- currentOwner: null
47
+ * Lazy import to avoid circular dependency between cursor and mode stores.
48
+ * The mode store doesn't depend on cursor store, but importing it synchronously
49
+ * at module load time can cause initialization order issues.
50
+ *
51
+ * This pattern ensures the import is resolved before first use (bus listeners
52
+ * are set up on first subscriber, not at module load time).
53
+ */
54
+ let getModeOwnerFn = null;
55
+ let isRegisteredModeOwnerFn = null;
56
+ let importPromise = null;
57
+ let importFailed = false;
58
+ function ensureModeStoreImported() {
59
+ if (getModeOwnerFn !== null || importFailed) return;
60
+ if (importPromise === null) importPromise = import("../map-mode/store.js").then((mod) => {
61
+ getModeOwnerFn = mod.getCurrentModeOwner;
62
+ isRegisteredModeOwnerFn = mod.isRegisteredModeOwner;
63
+ }).catch((error) => {
64
+ importFailed = true;
65
+ if (process.env.NODE_ENV !== "production") console.error("[MapCursor] Failed to import mode store:", error);
91
66
  });
92
- return cursorStore.get(instanceId);
67
+ }
68
+ ensureModeStoreImported();
69
+ function getModeOwner(mapId) {
70
+ return getModeOwnerFn?.(mapId);
71
+ }
72
+ function isRegisteredOwner(mapId, owner) {
73
+ return isRegisteredModeOwnerFn?.(mapId, owner) ?? false;
93
74
  }
94
75
  /**
95
- * Get current cursor snapshot for a given instance (for useSyncExternalStore)
96
- *
97
- * Priority order:
98
- * 1. Mode owner's cursor (if exists and mode is owned)
99
- * 2. Most recent cursor (only if currentOwner still has an entry in storage)
100
- * 3. Default cursor
76
+ * Calculate effective cursor based on priority
101
77
  */
102
- function getSnapshot(instanceId) {
103
- const state = cursorStore.get(instanceId);
104
- if (!state) return DEFAULT_CURSOR;
105
- const modeOwner = getCurrentModeOwner(instanceId);
78
+ function getEffectiveCursor(mapId, state) {
79
+ const modeOwner = getModeOwner?.(mapId);
106
80
  if (modeOwner) {
107
81
  const modeOwnerCursor = state.cursorOwners.get(modeOwner);
108
82
  if (modeOwnerCursor) return modeOwnerCursor;
@@ -113,239 +87,136 @@ function getSnapshot(instanceId) {
113
87
  return DEFAULT_CURSOR;
114
88
  }
115
89
  /**
116
- * Notify all React subscribers for a given instanceId
117
- */
118
- function notifySubscribers(instanceId) {
119
- const subscribers = componentSubscribers.get(instanceId);
120
- if (subscribers) for (const onStoreChange of subscribers) onStoreChange();
121
- }
122
- /**
123
- * Handle cursor change request for a given instance
90
+ * Cursor store
124
91
  */
125
- function handleCursorChangeRequest(instanceId, state, cursor, requestOwner) {
126
- const previousCursor = getSnapshot(instanceId);
127
- if (state.cursorOwners.get(requestOwner) === cursor) return;
128
- state.cursorOwners.set(requestOwner, cursor);
129
- const modeOwner = getCurrentModeOwner(instanceId);
130
- if (!modeOwner || requestOwner === modeOwner) {
131
- state.currentCursor = cursor;
132
- state.currentOwner = requestOwner;
133
- const newCursor = getSnapshot(instanceId);
134
- if (previousCursor !== newCursor) {
135
- mapCursorBus.emit(MapCursorEvents.changed, {
136
- previousCursor,
137
- currentCursor: newCursor,
138
- owner: requestOwner,
139
- id: instanceId
92
+ const cursorStore = createMapStore({
93
+ defaultState: {
94
+ cursorOwners: /* @__PURE__ */ new Map(),
95
+ currentCursor: null,
96
+ currentOwner: null
97
+ },
98
+ actions: (mapId, { get, set }) => ({
99
+ requestCursorChange: (cursor, owner) => {
100
+ const trimmedCursor = cursor.trim();
101
+ const trimmedOwner = owner.trim();
102
+ if (!trimmedCursor) throw new Error("requestCursorChange requires non-empty cursor");
103
+ if (!trimmedOwner) throw new Error("requestCursorChange requires non-empty owner");
104
+ cursorBus.emit(MapCursorEvents.changeRequest, {
105
+ cursor: trimmedCursor,
106
+ owner: trimmedOwner,
107
+ id: mapId
140
108
  });
141
- notifySubscribers(instanceId);
142
- }
143
- } else mapCursorBus.emit(MapCursorEvents.rejected, {
144
- rejectedCursor: cursor,
145
- rejectedOwner: requestOwner,
146
- currentOwner: state.currentOwner || modeOwner || "unknown",
147
- reason: "not-owner",
148
- id: instanceId
149
- });
150
- }
151
- /**
152
- * Ensures a single bus listener exists for the given instanceId.
153
- * All React subscribers will be notified via fan-out when the bus events fire.
154
- * This prevents creating N bus listeners for N React components.
155
- *
156
- * @param instanceId - The unique identifier for the map cursor instance
157
- */
158
- function ensureBusListener(instanceId) {
159
- if (busUnsubscribers.has(instanceId)) return;
160
- const state = getOrCreateState(instanceId);
161
- const unsubRequest = mapCursorBus.on(MapCursorEvents.changeRequest, (event) => {
162
- const { cursor, owner: requestOwner, id } = event.payload;
163
- if (id !== instanceId) return;
164
- handleCursorChangeRequest(instanceId, state, cursor, requestOwner);
165
- });
166
- const unsubModeChange = Broadcast.getInstance().on(MapModeEvents.changed, (event) => {
167
- if (event.payload.id !== instanceId) return;
168
- const previousCursor = getSnapshot(instanceId);
169
- if (event.payload.currentMode === "default" || event.payload.previousMode !== event.payload.currentMode) {
170
- state.currentCursor = null;
171
- state.currentOwner = null;
172
- notifySubscribers(instanceId);
109
+ },
110
+ clearCursor: (owner) => {
111
+ const state = get();
112
+ if (state.cursorOwners.has(owner)) {
113
+ const updates = { cursorOwners: mapDelete(state.cursorOwners, owner) };
114
+ if (state.currentOwner === owner) {
115
+ updates.currentCursor = null;
116
+ updates.currentOwner = null;
117
+ }
118
+ set(updates);
119
+ }
173
120
  }
174
- queueMicrotask(() => {
175
- const newCursor = getSnapshot(instanceId);
176
- if (previousCursor !== newCursor) {
177
- const newModeOwner = getCurrentModeOwner(instanceId) || "system";
178
- mapCursorBus.emit(MapCursorEvents.changed, {
179
- previousCursor,
180
- currentCursor: newCursor,
181
- owner: newModeOwner,
182
- id: instanceId
121
+ }),
122
+ bus: (mapId, { get, set }) => {
123
+ const unsubRequest = cursorBus.on(MapCursorEvents.changeRequest, (event) => {
124
+ const { cursor, owner: requestOwner, id } = event.payload;
125
+ if (id !== mapId) return;
126
+ const state = get();
127
+ const previousCursor = getEffectiveCursor(mapId, state);
128
+ if (state.cursorOwners.get(requestOwner) === cursor) return;
129
+ const newCursorOwners = mapSet(state.cursorOwners, requestOwner, cursor);
130
+ const currentModeOwner = getModeOwner?.(mapId);
131
+ const isOwnerless = !currentModeOwner;
132
+ const isCurrentModeOwner = requestOwner === currentModeOwner;
133
+ const isAnyModeOwner = isRegisteredOwner(mapId, requestOwner);
134
+ if (isOwnerless || isCurrentModeOwner) {
135
+ const newState = {
136
+ cursorOwners: newCursorOwners,
137
+ currentCursor: cursor,
138
+ currentOwner: requestOwner
139
+ };
140
+ const newCursor = getEffectiveCursor(mapId, newState);
141
+ if (previousCursor !== newCursor) {
142
+ set(newState);
143
+ cursorBus.emit(MapCursorEvents.changed, {
144
+ previousCursor,
145
+ currentCursor: newCursor,
146
+ owner: requestOwner,
147
+ id: mapId
148
+ });
149
+ } else set(newState);
150
+ } else if (isAnyModeOwner) {
151
+ set({ cursorOwners: newCursorOwners });
152
+ cursorBus.emit(MapCursorEvents.rejected, {
153
+ rejectedCursor: cursor,
154
+ rejectedOwner: requestOwner,
155
+ currentOwner: state.currentOwner || currentModeOwner || "unknown",
156
+ reason: "not-current-owner",
157
+ id: mapId
183
158
  });
184
- notifySubscribers(instanceId);
185
- }
159
+ } else cursorBus.emit(MapCursorEvents.rejected, {
160
+ rejectedCursor: cursor,
161
+ rejectedOwner: requestOwner,
162
+ currentOwner: state.currentOwner || currentModeOwner || "unknown",
163
+ reason: "not-owner",
164
+ id: mapId
165
+ });
166
+ });
167
+ const unsubMode = modeBus.on(MapModeEvents.changed, (event) => {
168
+ if (event.payload.id !== mapId) return;
169
+ const previousCursor = getEffectiveCursor(mapId, get());
170
+ if (event.payload.currentMode === "default" || event.payload.previousMode !== event.payload.currentMode) set({
171
+ currentCursor: null,
172
+ currentOwner: null
173
+ });
174
+ queueMicrotask(() => {
175
+ const newCursor = getEffectiveCursor(mapId, get());
176
+ if (previousCursor !== newCursor) {
177
+ const newModeOwner = getModeOwner?.(mapId) || "system";
178
+ cursorBus.emit(MapCursorEvents.changed, {
179
+ previousCursor,
180
+ currentCursor: newCursor,
181
+ owner: newModeOwner,
182
+ id: mapId
183
+ });
184
+ }
185
+ });
186
186
  });
187
- });
188
- busUnsubscribers.set(instanceId, () => {
189
- unsubRequest();
190
- unsubModeChange();
191
- });
192
- }
193
- /**
194
- * Cleans up the bus listener if no React subscribers remain.
195
- *
196
- * @param instanceId - The unique identifier for the map cursor instance
197
- */
198
- function cleanupBusListenerIfNeeded(instanceId) {
199
- const subscribers = componentSubscribers.get(instanceId);
200
- if (!subscribers || subscribers.size === 0) {
201
- const unsub = busUnsubscribers.get(instanceId);
202
- if (unsub) {
203
- unsub();
204
- busUnsubscribers.delete(instanceId);
205
- }
206
- clearAllCaches(instanceId);
207
- }
208
- }
209
- /**
210
- * Creates or retrieves a cached subscription function for a given instanceId.
211
- * Uses a fan-out pattern: 1 bus listener -> N React subscribers.
212
- * Automatically cleans up cursor state when the last subscriber unsubscribes.
213
- *
214
- * @param instanceId - The unique identifier for the map cursor instance
215
- * @returns A subscription function for useSyncExternalStore
216
- */
217
- function getOrCreateSubscription(instanceId) {
218
- const subscription = subscriptionCache.get(instanceId) ?? ((onStoreChange) => {
219
- getOrCreateState(instanceId);
220
- ensureBusListener(instanceId);
221
- let subscriberSet = componentSubscribers.get(instanceId);
222
- if (!subscriberSet) {
223
- subscriberSet = /* @__PURE__ */ new Set();
224
- componentSubscribers.set(instanceId, subscriberSet);
225
- }
226
- subscriberSet.add(onStoreChange);
227
187
  return () => {
228
- const currentSubscriberSet = componentSubscribers.get(instanceId);
229
- if (currentSubscriberSet) currentSubscriberSet.delete(onStoreChange);
230
- cleanupBusListenerIfNeeded(instanceId);
188
+ unsubRequest();
189
+ unsubMode();
231
190
  };
232
- });
233
- subscriptionCache.set(instanceId, subscription);
234
- return subscription;
235
- }
236
- /**
237
- * Creates or retrieves a cached snapshot function for a given instanceId.
238
- * The cursor returned gets equality checked, so it needs to be stable or React re-renders unnecessarily.
239
- *
240
- * @param instanceId - The unique identifier for the map cursor instance
241
- * @returns A snapshot function for useSyncExternalStore
242
- */
243
- function getOrCreateSnapshot(instanceId) {
244
- const snapshot = snapshotCache.get(instanceId) ?? (() => getSnapshot(instanceId));
245
- snapshotCache.set(instanceId, snapshot);
246
- return snapshot;
247
- }
191
+ }
192
+ });
248
193
  /**
249
- * Creates or retrieves a cached server snapshot function for a given instanceId.
250
- * Server snapshots always return the default cursor since cursor state is client-only.
251
- * Required for SSR/RSC compatibility with useSyncExternalStore.
252
- *
253
- * @param instanceId - The unique identifier for the map cursor instance
254
- * @returns A server snapshot function for useSyncExternalStore
194
+ * Get effective cursor (computed from state)
255
195
  */
256
- function getOrCreateServerSnapshot(instanceId) {
257
- const serverSnapshot = serverSnapshotCache.get(instanceId) ?? (() => DEFAULT_CURSOR);
258
- serverSnapshotCache.set(instanceId, serverSnapshot);
259
- return serverSnapshot;
196
+ function getCursor(mapId) {
197
+ return getEffectiveCursor(mapId, cursorStore.get(mapId));
260
198
  }
261
199
  /**
262
- * Creates or retrieves a cached requestCursorChange function for a given instanceId.
263
- * This maintains referential stability for the function reference.
200
+ * Hook for effective cursor value.
264
201
  *
265
- * @param instanceId - The unique identifier for the map cursor instance
266
- * @returns A requestCursorChange function for this instance
267
- */
268
- function getOrCreateRequestCursorChange(instanceId) {
269
- const requestCursorChange = requestCursorChangeCache.get(instanceId) ?? ((cursor, owner) => {
270
- const trimmedCursor = cursor.trim();
271
- const trimmedOwner = owner.trim();
272
- if (!trimmedCursor) throw new Error("requestCursorChange requires non-empty cursor");
273
- if (!trimmedOwner) throw new Error("requestCursorChange requires non-empty owner");
274
- mapCursorBus.emit(MapCursorEvents.changeRequest, {
275
- cursor: trimmedCursor,
276
- owner: trimmedOwner,
277
- id: instanceId
278
- });
279
- });
280
- requestCursorChangeCache.set(instanceId, requestCursorChange);
281
- return requestCursorChange;
282
- }
283
- /**
284
- * Creates or retrieves a cached clearCursor function for a given instanceId.
285
- * This maintains referential stability for the function reference.
202
+ * **Internal use only** - not exported from the public API.
203
+ * Use `useMapCursor` instead, which provides:
204
+ * - MapContext integration (auto-resolves mapId inside MapProvider)
205
+ * - Actions (requestCursorChange, clearCursor)
206
+ * - Better ergonomics for consumers
286
207
  *
287
- * @param instanceId - The unique identifier for the map cursor instance
288
- * @returns A clearCursor function for this instance
208
+ * This hook exists for internal composition (used by useMapCursor).
289
209
  */
290
- function getOrCreateClearCursor(instanceId) {
291
- const clearCursor = clearCursorCache.get(instanceId) ?? ((owner) => {
292
- const state = cursorStore.get(instanceId);
293
- if (!state) return;
294
- const hadCursor = state.cursorOwners.has(owner);
295
- state.cursorOwners.delete(owner);
296
- if (state.currentOwner === owner) {
297
- state.currentCursor = null;
298
- state.currentOwner = null;
299
- }
300
- if (hadCursor) notifySubscribers(instanceId);
301
- });
302
- clearCursorCache.set(instanceId, clearCursor);
303
- return clearCursor;
210
+ function useCursor(mapId) {
211
+ return cursorStore.useSelector(mapId, (state) => getEffectiveCursor(mapId, state));
304
212
  }
305
213
  /**
306
- * Get the current cursor for a given instance (direct access, not reactive).
307
- * @internal - For internal map-toolkit use only
308
- *
309
- * @param instanceId - The unique identifier for the map instance
310
- * @returns The current cursor
214
+ * Clear cursor state
311
215
  */
312
- function getCursor(instanceId) {
313
- return getSnapshot(instanceId);
314
- }
315
- /**
316
- * Manually clear all cursor state for a specific instanceId.
317
- * This is typically not needed as cleanup happens automatically when all subscribers unmount.
318
- * Use this only in advanced scenarios where manual cleanup is required.
319
- *
320
- * @param instanceId - The unique identifier for the map cursor instance to clear
321
- *
322
- * @example
323
- * ```tsx
324
- * // Manual cleanup (rarely needed)
325
- * clearCursorState('my-map-instance');
326
- * ```
327
- */
328
- function clearCursorState(instanceId) {
329
- const unsub = busUnsubscribers.get(instanceId);
330
- if (unsub) {
331
- unsub();
332
- busUnsubscribers.delete(instanceId);
333
- }
334
- clearAllCaches(instanceId);
335
- }
336
- /** @deprecated Use module functions directly */
337
- function getOrCreateStore(id) {
338
- return { getSnapshot: () => getSnapshot(id) };
339
- }
340
- /** @deprecated Use clearCursorState instead */
341
- function destroyStore(id) {
342
- clearCursorState(id);
343
- }
344
- /** @deprecated Use direct function calls instead */
345
- function getStore(id) {
346
- return { getSnapshot: () => getSnapshot(id) };
216
+ function clearCursorState(mapId) {
217
+ cursorStore.clear(mapId);
347
218
  }
348
219
 
349
220
  //#endregion
350
- export { clearCursorState, destroyStore, getCursor, getOrCreateClearCursor, getOrCreateRequestCursorChange, getOrCreateServerSnapshot, getOrCreateSnapshot, getOrCreateStore, getOrCreateSubscription, getStore };
221
+ export { clearCursorState, cursorStore, getCursor, useCursor };
351
222
  //# sourceMappingURL=store.js.map