@accelint/map-toolkit 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/catalog-info.yaml +3 -3
  3. package/dist/camera/events.d.ts +45 -0
  4. package/dist/camera/events.js +45 -0
  5. package/dist/camera/events.js.map +1 -1
  6. package/dist/camera/store.d.ts +47 -0
  7. package/dist/camera/store.js +81 -0
  8. package/dist/camera/store.js.map +1 -1
  9. package/dist/camera/types.d.ts +81 -0
  10. package/dist/cursor-coordinates/constants.d.ts +8 -0
  11. package/dist/cursor-coordinates/constants.js +22 -0
  12. package/dist/cursor-coordinates/constants.js.map +1 -0
  13. package/dist/cursor-coordinates/store.d.ts +1 -0
  14. package/dist/cursor-coordinates/store.js +1 -0
  15. package/dist/cursor-coordinates/store.js.map +1 -1
  16. package/dist/cursor-coordinates/use-cursor-coordinates.d.ts +5 -0
  17. package/dist/cursor-coordinates/use-cursor-coordinates.js +23 -8
  18. package/dist/cursor-coordinates/use-cursor-coordinates.js.map +1 -1
  19. package/dist/deckgl/base-map/constants.d.ts +12 -0
  20. package/dist/deckgl/base-map/constants.js +12 -0
  21. package/dist/deckgl/base-map/constants.js.map +1 -1
  22. package/dist/deckgl/base-map/controls.d.ts +11 -1
  23. package/dist/deckgl/base-map/controls.js +5 -0
  24. package/dist/deckgl/base-map/controls.js.map +1 -1
  25. package/dist/deckgl/base-map/events.d.ts +30 -0
  26. package/dist/deckgl/base-map/events.js +30 -0
  27. package/dist/deckgl/base-map/events.js.map +1 -1
  28. package/dist/deckgl/base-map/index.d.ts +2 -2
  29. package/dist/deckgl/base-map/index.js +33 -3
  30. package/dist/deckgl/base-map/index.js.map +1 -1
  31. package/dist/deckgl/base-map/provider.d.ts +2 -2
  32. package/dist/deckgl/index.js +1 -1
  33. package/dist/deckgl/saved-viewports/index.d.ts +75 -0
  34. package/dist/deckgl/saved-viewports/index.js +58 -0
  35. package/dist/deckgl/saved-viewports/index.js.map +1 -1
  36. package/dist/deckgl/saved-viewports/storage.d.ts +51 -0
  37. package/dist/deckgl/saved-viewports/storage.js +64 -0
  38. package/dist/deckgl/saved-viewports/storage.js.map +1 -1
  39. package/dist/deckgl/shapes/display-shape-layer/constants.js +18 -6
  40. package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -1
  41. package/dist/deckgl/shapes/display-shape-layer/fiber.d.ts +7 -0
  42. package/dist/deckgl/shapes/display-shape-layer/fiber.js.map +1 -1
  43. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +61 -4
  44. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -1
  45. package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +22 -8
  46. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +75 -4
  47. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -1
  48. package/dist/deckgl/shapes/draw-shape-layer/constants.js +30 -0
  49. package/dist/deckgl/shapes/draw-shape-layer/constants.js.map +1 -1
  50. package/dist/deckgl/shapes/draw-shape-layer/fiber.js +36 -0
  51. package/dist/deckgl/shapes/draw-shape-layer/fiber.js.map +1 -1
  52. package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +2 -2
  53. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +32 -1
  54. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js.map +1 -1
  55. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js +37 -8
  56. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js.map +1 -1
  57. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +43 -1
  58. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js.map +1 -1
  59. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +44 -1
  60. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js.map +1 -1
  61. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +46 -3
  62. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js.map +1 -1
  63. package/dist/deckgl/shapes/draw-shape-layer/modes/index.js +37 -1
  64. package/dist/deckgl/shapes/draw-shape-layer/modes/index.js.map +1 -1
  65. package/dist/deckgl/shapes/draw-shape-layer/store.js +50 -2
  66. package/dist/deckgl/shapes/draw-shape-layer/store.js.map +1 -1
  67. package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js +138 -17
  68. package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js.map +1 -1
  69. package/dist/deckgl/shapes/edit-shape-layer/events.js +1 -1
  70. package/dist/deckgl/shapes/edit-shape-layer/events.js.map +1 -1
  71. package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +2 -2
  72. package/dist/deckgl/shapes/edit-shape-layer/index.js +14 -0
  73. package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -1
  74. package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js +56 -8
  75. package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js.map +1 -1
  76. package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +26 -4
  77. package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js.map +1 -1
  78. package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +28 -3
  79. package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js.map +1 -1
  80. package/dist/deckgl/shapes/edit-shape-layer/modes/index.js +24 -0
  81. package/dist/deckgl/shapes/edit-shape-layer/modes/index.js.map +1 -1
  82. package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js +33 -4
  83. package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js.map +1 -1
  84. package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js +21 -2
  85. package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js.map +1 -1
  86. package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js +35 -11
  87. package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js.map +1 -1
  88. package/dist/deckgl/shapes/edit-shape-layer/store.js +1 -1
  89. package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -1
  90. package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js +12 -0
  91. package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js.map +1 -1
  92. package/dist/deckgl/shapes/shared/types.d.ts +3 -3
  93. package/dist/deckgl/shapes/shared/types.js +2 -2
  94. package/dist/deckgl/shapes/shared/types.js.map +1 -1
  95. package/dist/deckgl/shapes/shared/utils/geometry-measurements.js +3 -3
  96. package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -1
  97. package/dist/deckgl/shapes/shared/utils/pick-filtering.js +1 -1
  98. package/dist/deckgl/shapes/shared/utils/pick-filtering.js.map +1 -1
  99. package/dist/deckgl/symbol-layer/fiber.d.ts +18 -0
  100. package/dist/deckgl/symbol-layer/fiber.js.map +1 -1
  101. package/dist/deckgl/symbol-layer/index.d.ts +79 -1
  102. package/dist/deckgl/symbol-layer/index.js +72 -1
  103. package/dist/deckgl/symbol-layer/index.js.map +1 -1
  104. package/dist/deckgl/text-layer/character-sets.d.ts +30 -0
  105. package/dist/deckgl/text-layer/character-sets.js +26 -0
  106. package/dist/deckgl/text-layer/character-sets.js.map +1 -1
  107. package/dist/deckgl/text-layer/default-settings.d.ts +29 -0
  108. package/dist/deckgl/text-layer/default-settings.js +28 -0
  109. package/dist/deckgl/text-layer/default-settings.js.map +1 -1
  110. package/dist/deckgl/text-layer/index.d.ts +65 -0
  111. package/dist/deckgl/text-layer/index.js +56 -0
  112. package/dist/deckgl/text-layer/index.js.map +1 -1
  113. package/dist/map-cursor/events.d.ts +19 -0
  114. package/dist/map-cursor/events.js +19 -0
  115. package/dist/map-cursor/events.js.map +1 -1
  116. package/dist/map-cursor/store.d.ts +34 -2
  117. package/dist/map-cursor/store.js +44 -3
  118. package/dist/map-cursor/store.js.map +1 -1
  119. package/dist/map-mode/store.d.ts +43 -4
  120. package/dist/map-mode/store.js +56 -6
  121. package/dist/map-mode/store.js.map +1 -1
  122. package/dist/shared/create-map-store.d.ts +14 -0
  123. package/dist/shared/create-map-store.js +26 -2
  124. package/dist/shared/create-map-store.js.map +1 -1
  125. package/dist/shared/units.d.ts +24 -0
  126. package/dist/shared/units.js +24 -0
  127. package/dist/shared/units.js.map +1 -1
  128. package/dist/viewport/store.d.ts +1 -0
  129. package/dist/viewport/store.js +4 -0
  130. package/dist/viewport/store.js.map +1 -1
  131. package/package.json +3 -3
@@ -15,7 +15,16 @@ import { TextLayer as TextLayer$1, TextLayerProps as TextLayerProps$1 } from "@d
15
15
  import { LiteralUnion } from "type-fest";
16
16
 
17
17
  //#region src/deckgl/text-layer/index.d.ts
18
+ /**
19
+ * Props for TextLayer component.
20
+ * Extends Deck.gl's TextLayerProps with enhanced character set support.
21
+ */
18
22
  interface TextLayerProps<TData = unknown> extends TextLayerProps$1<TData> {
23
+ /**
24
+ * Character set to use for text rendering.
25
+ * Can be a predefined CHARACTER_SETS key or a custom string of characters.
26
+ * Defaults to CHARACTER_SETS.EXPANDED for international text support.
27
+ */
19
28
  characterSet?: LiteralUnion<CharacterSetsKeys, string>;
20
29
  }
21
30
  /**
@@ -30,6 +39,62 @@ interface TextLayerProps<TData = unknown> extends TextLayerProps$1<TData> {
30
39
  * Can be used directly with Deck.gl or as a JSX element with React Fiber:
31
40
  * - React Fiber: `<textLayer id="text" data={[...]} ... />`
32
41
  * - Direct: `new TextLayer({ id: 'text', data: [...], ... })`
42
+ *
43
+ * @example
44
+ * Direct Deck.gl usage:
45
+ * ```typescript
46
+ * import { Deck } from '@deck.gl/core';
47
+ * import { TextLayer } from '@accelint/map-toolkit/deckgl/text-layer';
48
+ *
49
+ * const layer = new TextLayer({
50
+ * id: 'text-labels',
51
+ * data: [
52
+ * { position: [-122.4, 37.74], text: 'San Francisco' },
53
+ * { position: [-118.2, 34.05], text: 'Los Angeles' },
54
+ * ],
55
+ * getText: d => d.text,
56
+ * getPosition: d => d.position,
57
+ * getSize: 14,
58
+ * fontWeight: 600,
59
+ * outlineWidth: 2,
60
+ * });
61
+ *
62
+ * new Deck({
63
+ * initialViewState: { longitude: -120, latitude: 36, zoom: 6 },
64
+ * controller: true,
65
+ * layers: [layer],
66
+ * });
67
+ * ```
68
+ *
69
+ * @example
70
+ * React Fiber usage:
71
+ * ```tsx
72
+ * import '@accelint/map-toolkit/deckgl/text-layer/fiber';
73
+ * import { BaseMap } from '@accelint/map-toolkit/deckgl';
74
+ * import { View } from '@deckgl-fiber-renderer/dom';
75
+ *
76
+ * function MapWithLabels() {
77
+ * const cities = [
78
+ * { position: [-122.4, 37.74], text: 'San Francisco' },
79
+ * { position: [-118.2, 34.05], text: 'Los Angeles' },
80
+ * ];
81
+ *
82
+ * return (
83
+ * <BaseMap id="map" className="w-full h-full">
84
+ * <View id="main" controller />
85
+ * <textLayer
86
+ * id="city-labels"
87
+ * data={cities}
88
+ * getText={d => d.text}
89
+ * getPosition={d => d.position}
90
+ * getSize={14}
91
+ * fontWeight={600}
92
+ * outlineWidth={2}
93
+ * />
94
+ * </BaseMap>
95
+ * );
96
+ * }
97
+ * ```
33
98
  */
34
99
  declare class TextLayer<TData = unknown> extends TextLayer$1<TData> {
35
100
  static CHARACTER_SETS: Readonly<{
@@ -28,6 +28,62 @@ import { TextLayer as TextLayer$1 } from "@deck.gl/layers";
28
28
  * Can be used directly with Deck.gl or as a JSX element with React Fiber:
29
29
  * - React Fiber: `<textLayer id="text" data={[...]} ... />`
30
30
  * - Direct: `new TextLayer({ id: 'text', data: [...], ... })`
31
+ *
32
+ * @example
33
+ * Direct Deck.gl usage:
34
+ * ```typescript
35
+ * import { Deck } from '@deck.gl/core';
36
+ * import { TextLayer } from '@accelint/map-toolkit/deckgl/text-layer';
37
+ *
38
+ * const layer = new TextLayer({
39
+ * id: 'text-labels',
40
+ * data: [
41
+ * { position: [-122.4, 37.74], text: 'San Francisco' },
42
+ * { position: [-118.2, 34.05], text: 'Los Angeles' },
43
+ * ],
44
+ * getText: d => d.text,
45
+ * getPosition: d => d.position,
46
+ * getSize: 14,
47
+ * fontWeight: 600,
48
+ * outlineWidth: 2,
49
+ * });
50
+ *
51
+ * new Deck({
52
+ * initialViewState: { longitude: -120, latitude: 36, zoom: 6 },
53
+ * controller: true,
54
+ * layers: [layer],
55
+ * });
56
+ * ```
57
+ *
58
+ * @example
59
+ * React Fiber usage:
60
+ * ```tsx
61
+ * import '@accelint/map-toolkit/deckgl/text-layer/fiber';
62
+ * import { BaseMap } from '@accelint/map-toolkit/deckgl';
63
+ * import { View } from '@deckgl-fiber-renderer/dom';
64
+ *
65
+ * function MapWithLabels() {
66
+ * const cities = [
67
+ * { position: [-122.4, 37.74], text: 'San Francisco' },
68
+ * { position: [-118.2, 34.05], text: 'Los Angeles' },
69
+ * ];
70
+ *
71
+ * return (
72
+ * <BaseMap id="map" className="w-full h-full">
73
+ * <View id="main" controller />
74
+ * <textLayer
75
+ * id="city-labels"
76
+ * data={cities}
77
+ * getText={d => d.text}
78
+ * getPosition={d => d.position}
79
+ * getSize={14}
80
+ * fontWeight={600}
81
+ * outlineWidth={2}
82
+ * />
83
+ * </BaseMap>
84
+ * );
85
+ * }
86
+ * ```
31
87
  */
32
88
  var TextLayer = class extends TextLayer$1 {
33
89
  static CHARACTER_SETS = CHARACTER_SETS;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["DglTextLayer"],"sources":["../../../src/deckgl/text-layer/index.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {\n TextLayer as DglTextLayer,\n type TextLayerProps as DglTextLayerProps,\n} from '@deck.gl/layers';\nimport { CHARACTER_SETS, type CharacterSetsKeys } from './character-sets.js';\nimport { defaultSettings } from './default-settings.js';\nimport type { LiteralUnion } from 'type-fest';\n\nexport interface TextLayerProps<TData = unknown>\n extends DglTextLayerProps<TData> {\n // A union type that preserves autocompletion for CharacterSetsKeys while allowing any string.\n characterSet?: LiteralUnion<CharacterSetsKeys, string>;\n}\n\n/**\n * A styled text layer that extends Deck.gl's TextLayer with enhanced styling capabilities.\n *\n * This layer provides:\n * - Customizable font styling (size, weight, family, line height)\n * - Text outline support\n * - Extended character set support\n * - Consistent styling based on design specifications\n *\n * Can be used directly with Deck.gl or as a JSX element with React Fiber:\n * - React Fiber: `<textLayer id=\"text\" data={[...]} ... />`\n * - Direct: `new TextLayer({ id: 'text', data: [...], ... })`\n */\nexport class TextLayer<TData = unknown> extends DglTextLayer<TData> {\n static CHARACTER_SETS = CHARACTER_SETS;\n\n static override layerName = 'textLayer';\n\n constructor(props: TextLayerProps<TData>) {\n const {\n characterSet = CHARACTER_SETS.EXPANDED,\n fontSettings,\n ...rest\n } = props;\n\n super({\n // set opinionated defaults\n ...defaultSettings,\n\n // user props override defaults\n ...rest,\n\n // handle special characterSet logic\n characterSet:\n CHARACTER_SETS[characterSet as CharacterSetsKeys] ?? characterSet,\n\n fontSettings: {\n // merge fontSettings\n ...defaultSettings.fontSettings,\n\n // user props override defaults\n ...fontSettings,\n },\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,IAAa,YAAb,cAAgDA,YAAoB;CAClE,OAAO,iBAAiB;CAExB,OAAgB,YAAY;CAE5B,YAAY,OAA8B;EACxC,MAAM,EACJ,eAAe,eAAe,UAC9B,cACA,GAAG,SACD;AAEJ,QAAM;GAEJ,GAAG;GAGH,GAAG;GAGH,cACE,eAAe,iBAAsC;GAEvD,cAAc;IAEZ,GAAG,gBAAgB;IAGnB,GAAG;IACJ;GACF,CAAC"}
1
+ {"version":3,"file":"index.js","names":["DglTextLayer"],"sources":["../../../src/deckgl/text-layer/index.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {\n TextLayer as DglTextLayer,\n type TextLayerProps as DglTextLayerProps,\n} from '@deck.gl/layers';\nimport { CHARACTER_SETS, type CharacterSetsKeys } from './character-sets.js';\nimport { defaultSettings } from './default-settings.js';\nimport type { LiteralUnion } from 'type-fest';\n\n/**\n * Props for TextLayer component.\n * Extends Deck.gl's TextLayerProps with enhanced character set support.\n */\nexport interface TextLayerProps<TData = unknown>\n extends DglTextLayerProps<TData> {\n /**\n * Character set to use for text rendering.\n * Can be a predefined CHARACTER_SETS key or a custom string of characters.\n * Defaults to CHARACTER_SETS.EXPANDED for international text support.\n */\n characterSet?: LiteralUnion<CharacterSetsKeys, string>;\n}\n\n/**\n * A styled text layer that extends Deck.gl's TextLayer with enhanced styling capabilities.\n *\n * This layer provides:\n * - Customizable font styling (size, weight, family, line height)\n * - Text outline support\n * - Extended character set support\n * - Consistent styling based on design specifications\n *\n * Can be used directly with Deck.gl or as a JSX element with React Fiber:\n * - React Fiber: `<textLayer id=\"text\" data={[...]} ... />`\n * - Direct: `new TextLayer({ id: 'text', data: [...], ... })`\n *\n * @example\n * Direct Deck.gl usage:\n * ```typescript\n * import { Deck } from '@deck.gl/core';\n * import { TextLayer } from '@accelint/map-toolkit/deckgl/text-layer';\n *\n * const layer = new TextLayer({\n * id: 'text-labels',\n * data: [\n * { position: [-122.4, 37.74], text: 'San Francisco' },\n * { position: [-118.2, 34.05], text: 'Los Angeles' },\n * ],\n * getText: d => d.text,\n * getPosition: d => d.position,\n * getSize: 14,\n * fontWeight: 600,\n * outlineWidth: 2,\n * });\n *\n * new Deck({\n * initialViewState: { longitude: -120, latitude: 36, zoom: 6 },\n * controller: true,\n * layers: [layer],\n * });\n * ```\n *\n * @example\n * React Fiber usage:\n * ```tsx\n * import '@accelint/map-toolkit/deckgl/text-layer/fiber';\n * import { BaseMap } from '@accelint/map-toolkit/deckgl';\n * import { View } from '@deckgl-fiber-renderer/dom';\n *\n * function MapWithLabels() {\n * const cities = [\n * { position: [-122.4, 37.74], text: 'San Francisco' },\n * { position: [-118.2, 34.05], text: 'Los Angeles' },\n * ];\n *\n * return (\n * <BaseMap id=\"map\" className=\"w-full h-full\">\n * <View id=\"main\" controller />\n * <textLayer\n * id=\"city-labels\"\n * data={cities}\n * getText={d => d.text}\n * getPosition={d => d.position}\n * getSize={14}\n * fontWeight={600}\n * outlineWidth={2}\n * />\n * </BaseMap>\n * );\n * }\n * ```\n */\nexport class TextLayer<TData = unknown> extends DglTextLayer<TData> {\n static CHARACTER_SETS = CHARACTER_SETS;\n\n static override layerName = 'textLayer';\n\n constructor(props: TextLayerProps<TData>) {\n const {\n characterSet = CHARACTER_SETS.EXPANDED,\n fontSettings,\n ...rest\n } = props;\n\n super({\n // set opinionated defaults\n ...defaultSettings,\n\n // user props override defaults\n ...rest,\n\n // handle special characterSet logic\n characterSet:\n CHARACTER_SETS[characterSet as CharacterSetsKeys] ?? characterSet,\n\n fontSettings: {\n // merge fontSettings\n ...defaultSettings.fontSettings,\n\n // user props override defaults\n ...fontSettings,\n },\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuGA,IAAa,YAAb,cAAgDA,YAAoB;CAClE,OAAO,iBAAiB;CAExB,OAAgB,YAAY;CAE5B,YAAY,OAA8B;EACxC,MAAM,EACJ,eAAe,eAAe,UAC9B,cACA,GAAG,SACD;AAEJ,QAAM;GAEJ,GAAG;GAGH,GAAG;GAGH,cACE,eAAe,iBAAsC;GAEvD,cAAc;IAEZ,GAAG,gBAAgB;IAGnB,GAAG;IACJ;GACF,CAAC"}
@@ -2,6 +2,25 @@
2
2
  /**
3
3
  * Event keys for map cursor state changes.
4
4
  * These events are used for communication between cursor stores and consumers.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { Broadcast } from '@accelint/bus';
9
+ * import { MapCursorEvents } from '@accelint/map-toolkit/map-cursor';
10
+ * import type { MapCursorEventType } from '@accelint/map-toolkit/map-cursor';
11
+ *
12
+ * const bus = Broadcast.getInstance<MapCursorEventType>();
13
+ *
14
+ * // Listen for cursor changes
15
+ * bus.on(MapCursorEvents.changed, (event) => {
16
+ * console.log('Cursor changed to:', event.payload.currentCursor);
17
+ * });
18
+ *
19
+ * // Listen for rejections
20
+ * bus.on(MapCursorEvents.rejected, (event) => {
21
+ * console.warn('Cursor change rejected:', event.payload.reason);
22
+ * });
23
+ * ```
5
24
  */
6
25
  declare const MapCursorEvents: {
7
26
  /** Emitted when a component requests a cursor change */
@@ -15,6 +15,25 @@
15
15
  /**
16
16
  * Event keys for map cursor state changes.
17
17
  * These events are used for communication between cursor stores and consumers.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { Broadcast } from '@accelint/bus';
22
+ * import { MapCursorEvents } from '@accelint/map-toolkit/map-cursor';
23
+ * import type { MapCursorEventType } from '@accelint/map-toolkit/map-cursor';
24
+ *
25
+ * const bus = Broadcast.getInstance<MapCursorEventType>();
26
+ *
27
+ * // Listen for cursor changes
28
+ * bus.on(MapCursorEvents.changed, (event) => {
29
+ * console.log('Cursor changed to:', event.payload.currentCursor);
30
+ * });
31
+ *
32
+ * // Listen for rejections
33
+ * bus.on(MapCursorEvents.rejected, (event) => {
34
+ * console.warn('Cursor change rejected:', event.payload.reason);
35
+ * });
36
+ * ```
18
37
  */
19
38
  const MapCursorEvents = {
20
39
  changeRequest: "cursor:change-request",
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","names":[],"sources":["../../src/map-cursor/events.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n/**\n * Event keys for map cursor state changes.\n * These events are used for communication between cursor stores and consumers.\n */\nexport const MapCursorEvents = {\n /** Emitted when a component requests a cursor change */\n changeRequest: 'cursor:change-request',\n /** Emitted when the cursor has been changed */\n changed: 'cursor:changed',\n /** Emitted when a cursor change request is rejected */\n rejected: 'cursor:rejected',\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgBA,MAAa,kBAAkB;CAE7B,eAAe;CAEf,SAAS;CAET,UAAU;CACX"}
1
+ {"version":3,"file":"events.js","names":[],"sources":["../../src/map-cursor/events.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n/**\n * Event keys for map cursor state changes.\n * These events are used for communication between cursor stores and consumers.\n *\n * @example\n * ```typescript\n * import { Broadcast } from '@accelint/bus';\n * import { MapCursorEvents } from '@accelint/map-toolkit/map-cursor';\n * import type { MapCursorEventType } from '@accelint/map-toolkit/map-cursor';\n *\n * const bus = Broadcast.getInstance<MapCursorEventType>();\n *\n * // Listen for cursor changes\n * bus.on(MapCursorEvents.changed, (event) => {\n * console.log('Cursor changed to:', event.payload.currentCursor);\n * });\n *\n * // Listen for rejections\n * bus.on(MapCursorEvents.rejected, (event) => {\n * console.warn('Cursor change rejected:', event.payload.reason);\n * });\n * ```\n */\nexport const MapCursorEvents = {\n /** Emitted when a component requests a cursor change */\n changeRequest: 'cursor:change-request',\n /** Emitted when the cursor has been changed */\n changed: 'cursor:changed',\n /** Emitted when a cursor change request is rejected */\n rejected: 'cursor:rejected',\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,kBAAkB;CAE7B,eAAe;CAEf,SAAS;CAET,UAAU;CACX"}
@@ -40,7 +40,21 @@ type CursorActions = {
40
40
  */
41
41
  declare const cursorStore: MapStore<CursorState, CursorActions>;
42
42
  /**
43
- * Get effective cursor (computed from state)
43
+ * Get effective cursor value for a map instance.
44
+ *
45
+ * Computes the effective cursor from the current state by applying
46
+ * the priority system (mode owner > most recent > default).
47
+ *
48
+ * @param mapId - Unique identifier for the map instance
49
+ * @returns The effective cursor to display
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * import { getCursor } from '@accelint/map-toolkit/map-cursor';
54
+ *
55
+ * const currentCursor = getCursor(mapId);
56
+ * console.log('Current cursor:', currentCursor); // 'default', 'pointer', etc.
57
+ * ```
44
58
  */
45
59
  declare function getCursor(mapId: UniqueId): CSSCursorType;
46
60
  /**
@@ -53,10 +67,28 @@ declare function getCursor(mapId: UniqueId): CSSCursorType;
53
67
  * - Better ergonomics for consumers
54
68
  *
55
69
  * This hook exists for internal composition (used by useMapCursor).
70
+ *
71
+ * @param mapId - Unique identifier for the map instance
72
+ * @returns The effective cursor to display
56
73
  */
57
74
  declare function useCursor(mapId: UniqueId): CSSCursorType;
58
75
  /**
59
- * Clear cursor state
76
+ * Clear cursor state for a specific map instance.
77
+ *
78
+ * Removes all cursor ownership data and resets to default state.
79
+ * This is typically not needed as cleanup happens automatically.
80
+ * Use only in advanced scenarios where manual cleanup is required.
81
+ *
82
+ * @param mapId - Unique identifier for the map instance to clear
83
+ * @returns void
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * import { clearCursorState } from '@accelint/map-toolkit/map-cursor';
88
+ *
89
+ * // Manual cleanup when destroying a map
90
+ * clearCursorState(mapId);
91
+ * ```
60
92
  */
61
93
  declare function clearCursorState(mapId: UniqueId): void;
62
94
  //#endregion
@@ -73,7 +73,16 @@ function isRegisteredOwner(mapId, owner) {
73
73
  return isRegisteredModeOwnerFn?.(mapId, owner) ?? false;
74
74
  }
75
75
  /**
76
- * Calculate effective cursor based on priority
76
+ * Calculate effective cursor based on priority.
77
+ *
78
+ * Priority order:
79
+ * 1. Mode owner's cursor (if mode is owned)
80
+ * 2. Most recent cursor request (if owner still has entry)
81
+ * 3. Default cursor
82
+ *
83
+ * @param mapId - Unique identifier for the map instance
84
+ * @param state - Current cursor state
85
+ * @returns The effective cursor to display
77
86
  */
78
87
  function getEffectiveCursor(mapId, state) {
79
88
  const modeOwner = getModeOwner?.(mapId);
@@ -191,7 +200,21 @@ const cursorStore = createMapStore({
191
200
  }
192
201
  });
193
202
  /**
194
- * Get effective cursor (computed from state)
203
+ * Get effective cursor value for a map instance.
204
+ *
205
+ * Computes the effective cursor from the current state by applying
206
+ * the priority system (mode owner > most recent > default).
207
+ *
208
+ * @param mapId - Unique identifier for the map instance
209
+ * @returns The effective cursor to display
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * import { getCursor } from '@accelint/map-toolkit/map-cursor';
214
+ *
215
+ * const currentCursor = getCursor(mapId);
216
+ * console.log('Current cursor:', currentCursor); // 'default', 'pointer', etc.
217
+ * ```
195
218
  */
196
219
  function getCursor(mapId) {
197
220
  return getEffectiveCursor(mapId, cursorStore.get(mapId));
@@ -206,12 +229,30 @@ function getCursor(mapId) {
206
229
  * - Better ergonomics for consumers
207
230
  *
208
231
  * This hook exists for internal composition (used by useMapCursor).
232
+ *
233
+ * @param mapId - Unique identifier for the map instance
234
+ * @returns The effective cursor to display
209
235
  */
210
236
  function useCursor(mapId) {
211
237
  return cursorStore.useSelector(mapId, (state) => getEffectiveCursor(mapId, state));
212
238
  }
213
239
  /**
214
- * Clear cursor state
240
+ * Clear cursor state for a specific map instance.
241
+ *
242
+ * Removes all cursor ownership data and resets to default state.
243
+ * This is typically not needed as cleanup happens automatically.
244
+ * Use only in advanced scenarios where manual cleanup is required.
245
+ *
246
+ * @param mapId - Unique identifier for the map instance to clear
247
+ * @returns void
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * import { clearCursorState } from '@accelint/map-toolkit/map-cursor';
252
+ *
253
+ * // Manual cleanup when destroying a map
254
+ * clearCursorState(mapId);
255
+ * ```
215
256
  */
216
257
  function clearCursorState(mapId) {
217
258
  cursorStore.clear(mapId);
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","names":["DEFAULT_CURSOR: CSSCursorType","getModeOwnerFn: ((mapId: UniqueId) => string | undefined) | null","isRegisteredModeOwnerFn:\n | ((mapId: UniqueId, owner: string) => boolean)\n | null","importPromise: Promise<void> | null","updates: Partial<CursorState>","newState: CursorState"],"sources":["../../src/map-cursor/store.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n/**\n * Map Cursor Store\n *\n * Manages cursor state with ownership-based priority.\n *\n * Priority order:\n * 1. Mode owner's cursor (if mode is owned)\n * 2. Most recent cursor request\n * 3. Default cursor\n *\n * @example\n * ```tsx\n * import { cursorStore } from '@accelint/map-toolkit/map-cursor';\n *\n * function MapContainer({ mapId }) {\n * const cursor = cursorStore.useSelector(mapId, (s) => getEffectiveCursor(mapId, s));\n * return <div style={{ cursor }}>...</div>;\n * }\n *\n * // Request cursor change from a layer:\n * cursorStore.actions(mapId).requestCursorChange('crosshair', 'draw-layer');\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { MapModeEvents } from '../map-mode/events';\nimport { createMapStore, mapDelete, mapSet } from '../shared/create-map-store';\nimport { MapCursorEvents } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { ModeChangedEvent } from '../map-mode/types';\nimport type { CSSCursorType, MapCursorEventType } from './types';\n\nconst DEFAULT_CURSOR: CSSCursorType = 'default';\n\n/**\n * State shape for map cursor\n */\ntype CursorState = {\n /** Map of owner -> cursor */\n cursorOwners: Map<string, CSSCursorType>;\n /** Current active cursor (for priority tracking) */\n currentCursor: CSSCursorType | null;\n /** Current cursor owner (for priority tracking) */\n currentOwner: string | null;\n};\n\n/**\n * Actions for cursor management\n */\ntype CursorActions = {\n /** Request a cursor change */\n requestCursorChange: (cursor: CSSCursorType, owner: string) => void;\n /** Clear cursor for an owner */\n clearCursor: (owner: string) => void;\n};\n\nconst cursorBus = Broadcast.getInstance<MapCursorEventType>();\nconst modeBus = Broadcast.getInstance<ModeChangedEvent>();\n\n/**\n * Lazy import to avoid circular dependency between cursor and mode stores.\n * The mode store doesn't depend on cursor store, but importing it synchronously\n * at module load time can cause initialization order issues.\n *\n * This pattern ensures the import is resolved before first use (bus listeners\n * are set up on first subscriber, not at module load time).\n */\nlet getModeOwnerFn: ((mapId: UniqueId) => string | undefined) | null = null;\nlet isRegisteredModeOwnerFn:\n | ((mapId: UniqueId, owner: string) => boolean)\n | null = null;\nlet importPromise: Promise<void> | null = null;\nlet importFailed = false;\n\nfunction ensureModeStoreImported(): void {\n if (getModeOwnerFn !== null || importFailed) {\n return;\n }\n if (importPromise === null) {\n importPromise = import('../map-mode/store')\n .then((mod) => {\n getModeOwnerFn = mod.getCurrentModeOwner;\n isRegisteredModeOwnerFn = mod.isRegisteredModeOwner;\n })\n .catch((error) => {\n importFailed = true;\n // Log error in development only - in production this is a silent fallback\n if (process.env.NODE_ENV !== 'production') {\n console.error('[MapCursor] Failed to import mode store:', error);\n }\n });\n }\n}\n\n// Start the import immediately so it's likely resolved by first use\nensureModeStoreImported();\n\nfunction getModeOwner(mapId: UniqueId): string | undefined {\n // getModeOwnerFn will be available by the time bus listeners are set up\n // (which happens on first React subscriber, not at module load)\n return getModeOwnerFn?.(mapId);\n}\n\nfunction isRegisteredOwner(mapId: UniqueId, owner: string): boolean {\n return isRegisteredModeOwnerFn?.(mapId, owner) ?? false;\n}\n\n/**\n * Calculate effective cursor based on priority\n */\nfunction getEffectiveCursor(\n mapId: UniqueId,\n state: CursorState,\n): CSSCursorType {\n // Priority 1: Mode owner's cursor\n const modeOwner = getModeOwner?.(mapId);\n if (modeOwner) {\n const modeOwnerCursor = state.cursorOwners.get(modeOwner);\n if (modeOwnerCursor) {\n return modeOwnerCursor;\n }\n }\n\n // Priority 2: Current cursor (if owner still has entry)\n if (state.currentCursor && state.currentOwner) {\n if (state.cursorOwners.has(state.currentOwner)) {\n return state.currentCursor;\n }\n }\n\n // Priority 3: Default\n return DEFAULT_CURSOR;\n}\n\n/**\n * Cursor store\n */\nexport const cursorStore = createMapStore<CursorState, CursorActions>({\n defaultState: {\n cursorOwners: new Map(),\n currentCursor: null,\n currentOwner: null,\n },\n\n actions: (mapId, { get, set }) => ({\n requestCursorChange: (cursor: CSSCursorType, owner: string) => {\n const trimmedCursor = cursor.trim() as CSSCursorType;\n const trimmedOwner = owner.trim();\n\n if (!trimmedCursor) {\n throw new Error('requestCursorChange requires non-empty cursor');\n }\n if (!trimmedOwner) {\n throw new Error('requestCursorChange requires non-empty owner');\n }\n\n cursorBus.emit(MapCursorEvents.changeRequest, {\n cursor: trimmedCursor,\n owner: trimmedOwner,\n id: mapId,\n });\n },\n\n clearCursor: (owner: string) => {\n const state = get();\n const hadCursor = state.cursorOwners.has(owner);\n\n if (hadCursor) {\n // Clear current tracking if this was the owner\n const updates: Partial<CursorState> = {\n cursorOwners: mapDelete(state.cursorOwners, owner),\n };\n if (state.currentOwner === owner) {\n updates.currentCursor = null;\n updates.currentOwner = null;\n }\n\n set(updates);\n }\n },\n }),\n\n bus: (mapId, { get, set }) => {\n // Handle cursor change requests\n const unsubRequest = cursorBus.on(\n MapCursorEvents.changeRequest,\n (event) => {\n const { cursor, owner: requestOwner, id } = event.payload;\n if (id !== mapId) {\n return;\n }\n\n const state = get();\n const previousCursor = getEffectiveCursor(mapId, state);\n\n // Skip if same cursor already stored for this owner\n if (state.cursorOwners.get(requestOwner) === cursor) {\n return;\n }\n\n // Create new Map for immutable update\n const newCursorOwners = mapSet(\n state.cursorOwners,\n requestOwner,\n cursor,\n );\n\n // Check ownership\n const currentModeOwner = getModeOwner?.(mapId);\n const isOwnerless = !currentModeOwner;\n const isCurrentModeOwner = requestOwner === currentModeOwner;\n const isAnyModeOwner = isRegisteredOwner(mapId, requestOwner);\n\n if (isOwnerless || isCurrentModeOwner) {\n // Accept: apply cursor with immutable update\n const newState: CursorState = {\n cursorOwners: newCursorOwners,\n currentCursor: cursor,\n currentOwner: requestOwner,\n };\n\n // Calculate new cursor with updated state\n const newCursor = getEffectiveCursor(mapId, newState);\n\n if (previousCursor !== newCursor) {\n set(newState);\n cursorBus.emit(MapCursorEvents.changed, {\n previousCursor,\n currentCursor: newCursor,\n owner: requestOwner,\n id: mapId,\n });\n } else {\n // Still need to update state even if cursor didn't change visually\n set(newState);\n }\n } else if (isAnyModeOwner) {\n // Store but don't apply: requester owns a different mode (pending or not current).\n // When their mode becomes active, getEffectiveCursor will find their cursor.\n set({ cursorOwners: newCursorOwners });\n\n cursorBus.emit(MapCursorEvents.rejected, {\n rejectedCursor: cursor,\n rejectedOwner: requestOwner,\n currentOwner: state.currentOwner || currentModeOwner || 'unknown',\n reason: 'not-current-owner',\n id: mapId,\n });\n } else {\n // Reject: don't store. Non-owners should only set cursor in default mode.\n cursorBus.emit(MapCursorEvents.rejected, {\n rejectedCursor: cursor,\n rejectedOwner: requestOwner,\n currentOwner: state.currentOwner || currentModeOwner || 'unknown',\n reason: 'not-owner',\n id: mapId,\n });\n }\n },\n );\n\n // Handle mode changes\n const unsubMode = modeBus.on(MapModeEvents.changed, (event) => {\n if (event.payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const previousCursor = getEffectiveCursor(mapId, state);\n\n // Clear current tracking on mode change with immutable update\n if (\n event.payload.currentMode === 'default' ||\n event.payload.previousMode !== event.payload.currentMode\n ) {\n set({\n currentCursor: null,\n currentOwner: null,\n });\n }\n\n // Defer check until new mode owner is registered\n queueMicrotask(() => {\n // Re-get state after microtask since it may have changed\n const currentState = get();\n const newCursor = getEffectiveCursor(mapId, currentState);\n if (previousCursor !== newCursor) {\n const newModeOwner = getModeOwner?.(mapId) || 'system';\n cursorBus.emit(MapCursorEvents.changed, {\n previousCursor,\n currentCursor: newCursor,\n owner: newModeOwner,\n id: mapId,\n });\n }\n });\n });\n\n return () => {\n unsubRequest();\n unsubMode();\n };\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Get effective cursor (computed from state)\n */\nexport function getCursor(mapId: UniqueId): CSSCursorType {\n return getEffectiveCursor(mapId, cursorStore.get(mapId));\n}\n\n/**\n * Hook for effective cursor value.\n *\n * **Internal use only** - not exported from the public API.\n * Use `useMapCursor` instead, which provides:\n * - MapContext integration (auto-resolves mapId inside MapProvider)\n * - Actions (requestCursorChange, clearCursor)\n * - Better ergonomics for consumers\n *\n * This hook exists for internal composition (used by useMapCursor).\n */\nexport function useCursor(mapId: UniqueId): CSSCursorType {\n return cursorStore.useSelector(mapId, (state) =>\n getEffectiveCursor(mapId, state),\n );\n}\n\n/**\n * Clear cursor state\n */\nexport function clearCursorState(mapId: UniqueId): void {\n cursorStore.clear(mapId);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,MAAMA,iBAAgC;AAwBtC,MAAM,YAAY,UAAU,aAAiC;AAC7D,MAAM,UAAU,UAAU,aAA+B;;;;;;;;;AAUzD,IAAIC,iBAAmE;AACvE,IAAIC,0BAEO;AACX,IAAIC,gBAAsC;AAC1C,IAAI,eAAe;AAEnB,SAAS,0BAAgC;AACvC,KAAI,mBAAmB,QAAQ,aAC7B;AAEF,KAAI,kBAAkB,KACpB,iBAAgB,OAAO,wBACpB,MAAM,QAAQ;AACb,mBAAiB,IAAI;AACrB,4BAA0B,IAAI;GAC9B,CACD,OAAO,UAAU;AAChB,iBAAe;AAEf,MAAI,QAAQ,IAAI,aAAa,aAC3B,SAAQ,MAAM,4CAA4C,MAAM;GAElE;;AAKR,yBAAyB;AAEzB,SAAS,aAAa,OAAqC;AAGzD,QAAO,iBAAiB,MAAM;;AAGhC,SAAS,kBAAkB,OAAiB,OAAwB;AAClE,QAAO,0BAA0B,OAAO,MAAM,IAAI;;;;;AAMpD,SAAS,mBACP,OACA,OACe;CAEf,MAAM,YAAY,eAAe,MAAM;AACvC,KAAI,WAAW;EACb,MAAM,kBAAkB,MAAM,aAAa,IAAI,UAAU;AACzD,MAAI,gBACF,QAAO;;AAKX,KAAI,MAAM,iBAAiB,MAAM,cAC/B;MAAI,MAAM,aAAa,IAAI,MAAM,aAAa,CAC5C,QAAO,MAAM;;AAKjB,QAAO;;;;;AAMT,MAAa,cAAc,eAA2C;CACpE,cAAc;EACZ,8BAAc,IAAI,KAAK;EACvB,eAAe;EACf,cAAc;EACf;CAED,UAAU,OAAO,EAAE,KAAK,WAAW;EACjC,sBAAsB,QAAuB,UAAkB;GAC7D,MAAM,gBAAgB,OAAO,MAAM;GACnC,MAAM,eAAe,MAAM,MAAM;AAEjC,OAAI,CAAC,cACH,OAAM,IAAI,MAAM,gDAAgD;AAElE,OAAI,CAAC,aACH,OAAM,IAAI,MAAM,+CAA+C;AAGjE,aAAU,KAAK,gBAAgB,eAAe;IAC5C,QAAQ;IACR,OAAO;IACP,IAAI;IACL,CAAC;;EAGJ,cAAc,UAAkB;GAC9B,MAAM,QAAQ,KAAK;AAGnB,OAFkB,MAAM,aAAa,IAAI,MAAM,EAEhC;IAEb,MAAMC,UAAgC,EACpC,cAAc,UAAU,MAAM,cAAc,MAAM,EACnD;AACD,QAAI,MAAM,iBAAiB,OAAO;AAChC,aAAQ,gBAAgB;AACxB,aAAQ,eAAe;;AAGzB,QAAI,QAAQ;;;EAGjB;CAED,MAAM,OAAO,EAAE,KAAK,UAAU;EAE5B,MAAM,eAAe,UAAU,GAC7B,gBAAgB,gBACf,UAAU;GACT,MAAM,EAAE,QAAQ,OAAO,cAAc,OAAO,MAAM;AAClD,OAAI,OAAO,MACT;GAGF,MAAM,QAAQ,KAAK;GACnB,MAAM,iBAAiB,mBAAmB,OAAO,MAAM;AAGvD,OAAI,MAAM,aAAa,IAAI,aAAa,KAAK,OAC3C;GAIF,MAAM,kBAAkB,OACtB,MAAM,cACN,cACA,OACD;GAGD,MAAM,mBAAmB,eAAe,MAAM;GAC9C,MAAM,cAAc,CAAC;GACrB,MAAM,qBAAqB,iBAAiB;GAC5C,MAAM,iBAAiB,kBAAkB,OAAO,aAAa;AAE7D,OAAI,eAAe,oBAAoB;IAErC,MAAMC,WAAwB;KAC5B,cAAc;KACd,eAAe;KACf,cAAc;KACf;IAGD,MAAM,YAAY,mBAAmB,OAAO,SAAS;AAErD,QAAI,mBAAmB,WAAW;AAChC,SAAI,SAAS;AACb,eAAU,KAAK,gBAAgB,SAAS;MACtC;MACA,eAAe;MACf,OAAO;MACP,IAAI;MACL,CAAC;UAGF,KAAI,SAAS;cAEN,gBAAgB;AAGzB,QAAI,EAAE,cAAc,iBAAiB,CAAC;AAEtC,cAAU,KAAK,gBAAgB,UAAU;KACvC,gBAAgB;KAChB,eAAe;KACf,cAAc,MAAM,gBAAgB,oBAAoB;KACxD,QAAQ;KACR,IAAI;KACL,CAAC;SAGF,WAAU,KAAK,gBAAgB,UAAU;IACvC,gBAAgB;IAChB,eAAe;IACf,cAAc,MAAM,gBAAgB,oBAAoB;IACxD,QAAQ;IACR,IAAI;IACL,CAAC;IAGP;EAGD,MAAM,YAAY,QAAQ,GAAG,cAAc,UAAU,UAAU;AAC7D,OAAI,MAAM,QAAQ,OAAO,MACvB;GAIF,MAAM,iBAAiB,mBAAmB,OAD5B,KAAK,CACoC;AAGvD,OACE,MAAM,QAAQ,gBAAgB,aAC9B,MAAM,QAAQ,iBAAiB,MAAM,QAAQ,YAE7C,KAAI;IACF,eAAe;IACf,cAAc;IACf,CAAC;AAIJ,wBAAqB;IAGnB,MAAM,YAAY,mBAAmB,OADhB,KAAK,CAC+B;AACzD,QAAI,mBAAmB,WAAW;KAChC,MAAM,eAAe,eAAe,MAAM,IAAI;AAC9C,eAAU,KAAK,gBAAgB,SAAS;MACtC;MACA,eAAe;MACf,OAAO;MACP,IAAI;MACL,CAAC;;KAEJ;IACF;AAEF,eAAa;AACX,iBAAc;AACd,cAAW;;;CAGhB,CAAC;;;;AASF,SAAgB,UAAU,OAAgC;AACxD,QAAO,mBAAmB,OAAO,YAAY,IAAI,MAAM,CAAC;;;;;;;;;;;;;AAc1D,SAAgB,UAAU,OAAgC;AACxD,QAAO,YAAY,YAAY,QAAQ,UACrC,mBAAmB,OAAO,MAAM,CACjC;;;;;AAMH,SAAgB,iBAAiB,OAAuB;AACtD,aAAY,MAAM,MAAM"}
1
+ {"version":3,"file":"store.js","names":["DEFAULT_CURSOR: CSSCursorType","getModeOwnerFn: ((mapId: UniqueId) => string | undefined) | null","isRegisteredModeOwnerFn:\n | ((mapId: UniqueId, owner: string) => boolean)\n | null","importPromise: Promise<void> | null","updates: Partial<CursorState>","newState: CursorState"],"sources":["../../src/map-cursor/store.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n/**\n * Map Cursor Store\n *\n * Manages cursor state with ownership-based priority.\n *\n * Priority order:\n * 1. Mode owner's cursor (if mode is owned)\n * 2. Most recent cursor request\n * 3. Default cursor\n *\n * @example\n * ```tsx\n * import { cursorStore } from '@accelint/map-toolkit/map-cursor';\n *\n * function MapContainer({ mapId }) {\n * const cursor = cursorStore.useSelector(mapId, (s) => getEffectiveCursor(mapId, s));\n * return <div style={{ cursor }}>...</div>;\n * }\n *\n * // Request cursor change from a layer:\n * cursorStore.actions(mapId).requestCursorChange('crosshair', 'draw-layer');\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { MapModeEvents } from '../map-mode/events';\nimport { createMapStore, mapDelete, mapSet } from '../shared/create-map-store';\nimport { MapCursorEvents } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { ModeChangedEvent } from '../map-mode/types';\nimport type { CSSCursorType, MapCursorEventType } from './types';\n\nconst DEFAULT_CURSOR: CSSCursorType = 'default';\n\n/**\n * State shape for map cursor\n */\ntype CursorState = {\n /** Map of owner -> cursor */\n cursorOwners: Map<string, CSSCursorType>;\n /** Current active cursor (for priority tracking) */\n currentCursor: CSSCursorType | null;\n /** Current cursor owner (for priority tracking) */\n currentOwner: string | null;\n};\n\n/**\n * Actions for cursor management\n */\ntype CursorActions = {\n /** Request a cursor change */\n requestCursorChange: (cursor: CSSCursorType, owner: string) => void;\n /** Clear cursor for an owner */\n clearCursor: (owner: string) => void;\n};\n\nconst cursorBus = Broadcast.getInstance<MapCursorEventType>();\nconst modeBus = Broadcast.getInstance<ModeChangedEvent>();\n\n/**\n * Lazy import to avoid circular dependency between cursor and mode stores.\n * The mode store doesn't depend on cursor store, but importing it synchronously\n * at module load time can cause initialization order issues.\n *\n * This pattern ensures the import is resolved before first use (bus listeners\n * are set up on first subscriber, not at module load time).\n */\nlet getModeOwnerFn: ((mapId: UniqueId) => string | undefined) | null = null;\nlet isRegisteredModeOwnerFn:\n | ((mapId: UniqueId, owner: string) => boolean)\n | null = null;\nlet importPromise: Promise<void> | null = null;\nlet importFailed = false;\n\nfunction ensureModeStoreImported(): void {\n if (getModeOwnerFn !== null || importFailed) {\n return;\n }\n if (importPromise === null) {\n importPromise = import('../map-mode/store')\n .then((mod) => {\n getModeOwnerFn = mod.getCurrentModeOwner;\n isRegisteredModeOwnerFn = mod.isRegisteredModeOwner;\n })\n .catch((error) => {\n importFailed = true;\n // Log error in development only - in production this is a silent fallback\n if (process.env.NODE_ENV !== 'production') {\n console.error('[MapCursor] Failed to import mode store:', error);\n }\n });\n }\n}\n\n// Start the import immediately so it's likely resolved by first use\nensureModeStoreImported();\n\nfunction getModeOwner(mapId: UniqueId): string | undefined {\n // getModeOwnerFn will be available by the time bus listeners are set up\n // (which happens on first React subscriber, not at module load)\n return getModeOwnerFn?.(mapId);\n}\n\nfunction isRegisteredOwner(mapId: UniqueId, owner: string): boolean {\n return isRegisteredModeOwnerFn?.(mapId, owner) ?? false;\n}\n\n/**\n * Calculate effective cursor based on priority.\n *\n * Priority order:\n * 1. Mode owner's cursor (if mode is owned)\n * 2. Most recent cursor request (if owner still has entry)\n * 3. Default cursor\n *\n * @param mapId - Unique identifier for the map instance\n * @param state - Current cursor state\n * @returns The effective cursor to display\n */\nfunction getEffectiveCursor(\n mapId: UniqueId,\n state: CursorState,\n): CSSCursorType {\n // Priority 1: Mode owner's cursor\n const modeOwner = getModeOwner?.(mapId);\n if (modeOwner) {\n const modeOwnerCursor = state.cursorOwners.get(modeOwner);\n if (modeOwnerCursor) {\n return modeOwnerCursor;\n }\n }\n\n // Priority 2: Current cursor (if owner still has entry)\n if (state.currentCursor && state.currentOwner) {\n if (state.cursorOwners.has(state.currentOwner)) {\n return state.currentCursor;\n }\n }\n\n // Priority 3: Default\n return DEFAULT_CURSOR;\n}\n\n/**\n * Cursor store\n */\nexport const cursorStore = createMapStore<CursorState, CursorActions>({\n defaultState: {\n cursorOwners: new Map(),\n currentCursor: null,\n currentOwner: null,\n },\n\n actions: (mapId, { get, set }) => ({\n requestCursorChange: (cursor: CSSCursorType, owner: string) => {\n const trimmedCursor = cursor.trim() as CSSCursorType;\n const trimmedOwner = owner.trim();\n\n if (!trimmedCursor) {\n throw new Error('requestCursorChange requires non-empty cursor');\n }\n if (!trimmedOwner) {\n throw new Error('requestCursorChange requires non-empty owner');\n }\n\n cursorBus.emit(MapCursorEvents.changeRequest, {\n cursor: trimmedCursor,\n owner: trimmedOwner,\n id: mapId,\n });\n },\n\n clearCursor: (owner: string) => {\n const state = get();\n const hadCursor = state.cursorOwners.has(owner);\n\n if (hadCursor) {\n // Clear current tracking if this was the owner\n const updates: Partial<CursorState> = {\n cursorOwners: mapDelete(state.cursorOwners, owner),\n };\n if (state.currentOwner === owner) {\n updates.currentCursor = null;\n updates.currentOwner = null;\n }\n\n set(updates);\n }\n },\n }),\n\n bus: (mapId, { get, set }) => {\n // Handle cursor change requests\n const unsubRequest = cursorBus.on(\n MapCursorEvents.changeRequest,\n (event) => {\n const { cursor, owner: requestOwner, id } = event.payload;\n if (id !== mapId) {\n return;\n }\n\n const state = get();\n const previousCursor = getEffectiveCursor(mapId, state);\n\n // Skip if same cursor already stored for this owner\n if (state.cursorOwners.get(requestOwner) === cursor) {\n return;\n }\n\n // Create new Map for immutable update\n const newCursorOwners = mapSet(\n state.cursorOwners,\n requestOwner,\n cursor,\n );\n\n // Check ownership\n const currentModeOwner = getModeOwner?.(mapId);\n const isOwnerless = !currentModeOwner;\n const isCurrentModeOwner = requestOwner === currentModeOwner;\n const isAnyModeOwner = isRegisteredOwner(mapId, requestOwner);\n\n if (isOwnerless || isCurrentModeOwner) {\n // Accept: apply cursor with immutable update\n const newState: CursorState = {\n cursorOwners: newCursorOwners,\n currentCursor: cursor,\n currentOwner: requestOwner,\n };\n\n // Calculate new cursor with updated state\n const newCursor = getEffectiveCursor(mapId, newState);\n\n if (previousCursor !== newCursor) {\n set(newState);\n cursorBus.emit(MapCursorEvents.changed, {\n previousCursor,\n currentCursor: newCursor,\n owner: requestOwner,\n id: mapId,\n });\n } else {\n // Still need to update state even if cursor didn't change visually\n set(newState);\n }\n } else if (isAnyModeOwner) {\n // Store but don't apply: requester owns a different mode (pending or not current).\n // When their mode becomes active, getEffectiveCursor will find their cursor.\n set({ cursorOwners: newCursorOwners });\n\n cursorBus.emit(MapCursorEvents.rejected, {\n rejectedCursor: cursor,\n rejectedOwner: requestOwner,\n currentOwner: state.currentOwner || currentModeOwner || 'unknown',\n reason: 'not-current-owner',\n id: mapId,\n });\n } else {\n // Reject: don't store. Non-owners should only set cursor in default mode.\n cursorBus.emit(MapCursorEvents.rejected, {\n rejectedCursor: cursor,\n rejectedOwner: requestOwner,\n currentOwner: state.currentOwner || currentModeOwner || 'unknown',\n reason: 'not-owner',\n id: mapId,\n });\n }\n },\n );\n\n // Handle mode changes\n const unsubMode = modeBus.on(MapModeEvents.changed, (event) => {\n if (event.payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const previousCursor = getEffectiveCursor(mapId, state);\n\n // Clear current tracking on mode change with immutable update\n if (\n event.payload.currentMode === 'default' ||\n event.payload.previousMode !== event.payload.currentMode\n ) {\n set({\n currentCursor: null,\n currentOwner: null,\n });\n }\n\n // Defer check until new mode owner is registered\n queueMicrotask(() => {\n // Re-get state after microtask since it may have changed\n const currentState = get();\n const newCursor = getEffectiveCursor(mapId, currentState);\n if (previousCursor !== newCursor) {\n const newModeOwner = getModeOwner?.(mapId) || 'system';\n cursorBus.emit(MapCursorEvents.changed, {\n previousCursor,\n currentCursor: newCursor,\n owner: newModeOwner,\n id: mapId,\n });\n }\n });\n });\n\n return () => {\n unsubRequest();\n unsubMode();\n };\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Get effective cursor value for a map instance.\n *\n * Computes the effective cursor from the current state by applying\n * the priority system (mode owner > most recent > default).\n *\n * @param mapId - Unique identifier for the map instance\n * @returns The effective cursor to display\n *\n * @example\n * ```typescript\n * import { getCursor } from '@accelint/map-toolkit/map-cursor';\n *\n * const currentCursor = getCursor(mapId);\n * console.log('Current cursor:', currentCursor); // 'default', 'pointer', etc.\n * ```\n */\nexport function getCursor(mapId: UniqueId): CSSCursorType {\n return getEffectiveCursor(mapId, cursorStore.get(mapId));\n}\n\n/**\n * Hook for effective cursor value.\n *\n * **Internal use only** - not exported from the public API.\n * Use `useMapCursor` instead, which provides:\n * - MapContext integration (auto-resolves mapId inside MapProvider)\n * - Actions (requestCursorChange, clearCursor)\n * - Better ergonomics for consumers\n *\n * This hook exists for internal composition (used by useMapCursor).\n *\n * @param mapId - Unique identifier for the map instance\n * @returns The effective cursor to display\n */\nexport function useCursor(mapId: UniqueId): CSSCursorType {\n return cursorStore.useSelector(mapId, (state) =>\n getEffectiveCursor(mapId, state),\n );\n}\n\n/**\n * Clear cursor state for a specific map instance.\n *\n * Removes all cursor ownership data and resets to default state.\n * This is typically not needed as cleanup happens automatically.\n * Use only in advanced scenarios where manual cleanup is required.\n *\n * @param mapId - Unique identifier for the map instance to clear\n * @returns void\n *\n * @example\n * ```typescript\n * import { clearCursorState } from '@accelint/map-toolkit/map-cursor';\n *\n * // Manual cleanup when destroying a map\n * clearCursorState(mapId);\n * ```\n */\nexport function clearCursorState(mapId: UniqueId): void {\n cursorStore.clear(mapId);\n}\n"],"mappings":"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"}
@@ -43,26 +43,65 @@ type MapModeActions = {
43
43
  */
44
44
  declare const modeStore: MapStore<MapModeState, MapModeActions>;
45
45
  /**
46
- * Get the current mode for a map instance
46
+ * Get the current mode for a map instance.
47
+ *
48
+ * @param mapId - Unique identifier for the map instance
49
+ * @returns The current active mode string
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * import { getMode } from '@accelint/map-toolkit/map-mode';
54
+ *
55
+ * const currentMode = getMode(mapId);
56
+ * console.log('Current mode:', currentMode); // 'default', 'drawing', etc.
57
+ * ```
47
58
  */
48
59
  declare function getMode(mapId: UniqueId): string;
49
60
  /**
50
- * Hook for current mode value
61
+ * Hook for current mode value.
62
+ *
63
+ * **Internal use only** - not exported from the public API.
64
+ * Use `useMapMode` instead for better ergonomics and MapContext integration.
65
+ *
66
+ * @param mapId - Unique identifier for the map instance
67
+ * @returns The current active mode string
51
68
  */
52
69
  declare function useMode(mapId: UniqueId): string;
53
70
  /**
54
- * Get the owner of the current mode for a given map instance
71
+ * Get the owner of the current mode for a given map instance.
72
+ *
55
73
  * @internal - For internal map-toolkit use only
74
+ * @param instanceId - Unique identifier for the map instance
75
+ * @returns The owner ID of the current mode, or undefined if unowned
56
76
  */
57
77
  declare function getCurrentModeOwner(instanceId: UniqueId): string | undefined;
58
78
  /**
59
79
  * Check if a given owner is registered as the owner of any mode.
60
80
  * This includes both active mode owners and pending mode requests.
81
+ *
61
82
  * @internal - For internal map-toolkit use only
83
+ * @param instanceId - Unique identifier for the map instance
84
+ * @param owner - The owner ID to check
85
+ * @returns True if the owner is registered for any mode
62
86
  */
63
87
  declare function isRegisteredModeOwner(instanceId: UniqueId, owner: string): boolean;
64
88
  /**
65
- * Manually clear map mode state for a specific instanceId.
89
+ * Manually clear map mode state for a specific map instance.
90
+ *
91
+ * Removes all mode ownership data, pending requests, and resets to default state.
92
+ * This is typically not needed as cleanup happens automatically.
93
+ * Use only in advanced scenarios where manual cleanup is required.
94
+ *
95
+ * @param instanceId - Unique identifier for the map instance to clear
96
+ * @returns void
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * import { clearMapModeState } from '@accelint/map-toolkit/map-mode';
101
+ *
102
+ * // Manual cleanup when destroying a map
103
+ * clearMapModeState(mapId);
104
+ * ```
66
105
  */
67
106
  declare function clearMapModeState(instanceId: UniqueId): void;
68
107
  //#endregion
@@ -14,8 +14,8 @@
14
14
  import { createMapStore, mapClear, mapDelete, mapSet } from "../shared/create-map-store.js";
15
15
  import { MapModeEvents } from "./events.js";
16
16
  import { Broadcast } from "@accelint/bus";
17
- import { uuid } from "@accelint/core";
18
17
  import { getLogger } from "@accelint/logger";
18
+ import { uuid } from "@accelint/core";
19
19
 
20
20
  //#region src/map-mode/store.ts
21
21
  /**
@@ -53,7 +53,18 @@ const DEFAULT_MODE = "default";
53
53
  */
54
54
  const mapModeBus = Broadcast.getInstance();
55
55
  /**
56
- * Determine if a mode change request should be auto-accepted without authorization
56
+ * Determine if a mode change request should be auto-accepted without authorization.
57
+ *
58
+ * Auto-accept conditions:
59
+ * - Owner returning to default mode
60
+ * - Owner switching between their own modes
61
+ * - No ownership conflicts exist
62
+ * - Entering an owned mode from default mode
63
+ *
64
+ * @param state - Current mode state
65
+ * @param desiredMode - The mode being requested
66
+ * @param requestOwner - The component requesting the mode change
67
+ * @returns True if the request should be auto-accepted
57
68
  */
58
69
  function shouldAutoAcceptRequest(state, desiredMode, requestOwner) {
59
70
  const currentModeOwner = state.modeOwners.get(state.mode);
@@ -222,20 +233,40 @@ const modeStore = createMapStore({
222
233
  }
223
234
  });
224
235
  /**
225
- * Get the current mode for a map instance
236
+ * Get the current mode for a map instance.
237
+ *
238
+ * @param mapId - Unique identifier for the map instance
239
+ * @returns The current active mode string
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * import { getMode } from '@accelint/map-toolkit/map-mode';
244
+ *
245
+ * const currentMode = getMode(mapId);
246
+ * console.log('Current mode:', currentMode); // 'default', 'drawing', etc.
247
+ * ```
226
248
  */
227
249
  function getMode(mapId) {
228
250
  return modeStore.get(mapId).mode;
229
251
  }
230
252
  /**
231
- * Hook for current mode value
253
+ * Hook for current mode value.
254
+ *
255
+ * **Internal use only** - not exported from the public API.
256
+ * Use `useMapMode` instead for better ergonomics and MapContext integration.
257
+ *
258
+ * @param mapId - Unique identifier for the map instance
259
+ * @returns The current active mode string
232
260
  */
233
261
  function useMode(mapId) {
234
262
  return modeStore.useSelector(mapId, (state) => state.mode);
235
263
  }
236
264
  /**
237
- * Get the owner of the current mode for a given map instance
265
+ * Get the owner of the current mode for a given map instance.
266
+ *
238
267
  * @internal - For internal map-toolkit use only
268
+ * @param instanceId - Unique identifier for the map instance
269
+ * @returns The owner ID of the current mode, or undefined if unowned
239
270
  */
240
271
  function getCurrentModeOwner(instanceId) {
241
272
  const state = modeStore.get(instanceId);
@@ -244,7 +275,11 @@ function getCurrentModeOwner(instanceId) {
244
275
  /**
245
276
  * Check if a given owner is registered as the owner of any mode.
246
277
  * This includes both active mode owners and pending mode requests.
278
+ *
247
279
  * @internal - For internal map-toolkit use only
280
+ * @param instanceId - Unique identifier for the map instance
281
+ * @param owner - The owner ID to check
282
+ * @returns True if the owner is registered for any mode
248
283
  */
249
284
  function isRegisteredModeOwner(instanceId, owner) {
250
285
  const state = modeStore.get(instanceId);
@@ -253,7 +288,22 @@ function isRegisteredModeOwner(instanceId, owner) {
253
288
  return false;
254
289
  }
255
290
  /**
256
- * Manually clear map mode state for a specific instanceId.
291
+ * Manually clear map mode state for a specific map instance.
292
+ *
293
+ * Removes all mode ownership data, pending requests, and resets to default state.
294
+ * This is typically not needed as cleanup happens automatically.
295
+ * Use only in advanced scenarios where manual cleanup is required.
296
+ *
297
+ * @param instanceId - Unique identifier for the map instance to clear
298
+ * @returns void
299
+ *
300
+ * @example
301
+ * ```typescript
302
+ * import { clearMapModeState } from '@accelint/map-toolkit/map-mode';
303
+ *
304
+ * // Manual cleanup when destroying a map
305
+ * clearMapModeState(mapId);
306
+ * ```
257
307
  */
258
308
  function clearMapModeState(instanceId) {
259
309
  modeStore.clear(instanceId);