@accelint/map-toolkit 0.5.0 → 0.6.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 (145) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/catalog-info.yaml +5 -3
  3. package/dist/camera/use-camera-state.js +1 -2
  4. package/dist/camera/use-camera-state.js.map +1 -1
  5. package/dist/deckgl/base-map/constants.d.ts +9 -2
  6. package/dist/deckgl/base-map/constants.js +9 -2
  7. package/dist/deckgl/base-map/constants.js.map +1 -1
  8. package/dist/deckgl/base-map/index.d.ts +4 -2
  9. package/dist/deckgl/base-map/index.js +5 -6
  10. package/dist/deckgl/base-map/index.js.map +1 -1
  11. package/dist/deckgl/base-map/types.d.ts +6 -1
  12. package/dist/deckgl/index.d.ts +8 -2
  13. package/dist/deckgl/index.js +6 -2
  14. package/dist/deckgl/saved-viewports/index.d.ts +1 -1
  15. package/dist/deckgl/saved-viewports/index.js +1 -2
  16. package/dist/deckgl/saved-viewports/index.js.map +1 -1
  17. package/dist/deckgl/shapes/display-shape-layer/constants.d.ts +44 -0
  18. package/dist/deckgl/shapes/display-shape-layer/constants.js +61 -0
  19. package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -0
  20. package/dist/{node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/assert.js → deckgl/shapes/display-shape-layer/fiber.d.ts} +11 -7
  21. package/dist/{packages/hotkey-manager/dist/lib/is-client/index.js → deckgl/shapes/display-shape-layer/fiber.js} +6 -7
  22. package/dist/deckgl/shapes/display-shape-layer/fiber.js.map +1 -0
  23. package/dist/deckgl/shapes/display-shape-layer/index.d.ts +206 -0
  24. package/dist/deckgl/shapes/display-shape-layer/index.js +416 -0
  25. package/dist/deckgl/shapes/display-shape-layer/index.js.map +1 -0
  26. package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.d.ts +66 -0
  27. package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js +116 -0
  28. package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js.map +1 -0
  29. package/dist/deckgl/shapes/display-shape-layer/store.d.ts +87 -0
  30. package/dist/deckgl/shapes/display-shape-layer/store.js +316 -0
  31. package/dist/deckgl/shapes/display-shape-layer/store.js.map +1 -0
  32. package/dist/deckgl/shapes/display-shape-layer/types.d.ts +115 -0
  33. package/dist/{node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fly-to-viewport.js → deckgl/shapes/display-shape-layer/types.js} +0 -2
  34. package/dist/deckgl/shapes/display-shape-layer/use-shape-selection.d.ts +89 -0
  35. package/dist/deckgl/shapes/display-shape-layer/use-shape-selection.js +88 -0
  36. package/dist/deckgl/shapes/display-shape-layer/use-shape-selection.js.map +1 -0
  37. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.d.ts +61 -0
  38. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +111 -0
  39. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -0
  40. package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +196 -0
  41. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +368 -0
  42. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -0
  43. package/dist/deckgl/shapes/index.d.ts +20 -0
  44. package/dist/{node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/math-utils.js → deckgl/shapes/index.js} +6 -11
  45. package/dist/deckgl/shapes/shared/constants.d.ts +78 -0
  46. package/dist/deckgl/shapes/shared/constants.js +109 -0
  47. package/dist/deckgl/shapes/shared/constants.js.map +1 -0
  48. package/dist/deckgl/shapes/shared/events.d.ts +73 -0
  49. package/dist/deckgl/shapes/shared/events.js +58 -0
  50. package/dist/deckgl/shapes/shared/events.js.map +1 -0
  51. package/dist/deckgl/shapes/shared/types.d.ts +158 -0
  52. package/dist/{node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/get-bounds.js → deckgl/shapes/shared/types.js} +13 -4
  53. package/dist/deckgl/shapes/shared/types.js.map +1 -0
  54. package/dist/deckgl/symbol-layer/index.d.ts +1 -1
  55. package/dist/viewport/viewport-size.d.ts +2 -2
  56. package/package.json +36 -20
  57. package/dist/_virtual/rolldown_runtime.js +0 -22
  58. package/dist/decorators/deckgl.d.ts +0 -19
  59. package/dist/decorators/deckgl.js +0 -32
  60. package/dist/decorators/deckgl.js.map +0 -1
  61. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/assert.js.map +0 -1
  62. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fit-bounds.js +0 -63
  63. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fit-bounds.js.map +0 -1
  64. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/get-bounds.js.map +0 -1
  65. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/index.js +0 -19
  66. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/math-utils.js.map +0 -1
  67. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/normalize-viewport-props.js +0 -14
  68. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/web-mercator-utils.js +0 -59
  69. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/web-mercator-utils.js.map +0 -1
  70. package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/web-mercator-viewport.js +0 -16
  71. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/attribution-control.js +0 -29
  72. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/attribution-control.js.map +0 -1
  73. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/fullscreen-control.js +0 -29
  74. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/fullscreen-control.js.map +0 -1
  75. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/geolocate-control.js +0 -54
  76. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/geolocate-control.js.map +0 -1
  77. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/layer.js +0 -15
  78. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/logo-control.js +0 -29
  79. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/logo-control.js.map +0 -1
  80. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/map.js +0 -91
  81. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/map.js.map +0 -1
  82. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/marker.js +0 -88
  83. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/marker.js.map +0 -1
  84. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/navigation-control.js +0 -29
  85. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/navigation-control.js.map +0 -1
  86. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/popup.js +0 -69
  87. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/popup.js.map +0 -1
  88. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/scale-control.js +0 -35
  89. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/scale-control.js.map +0 -1
  90. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/source.js +0 -15
  91. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/terrain-control.js +0 -29
  92. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/terrain-control.js.map +0 -1
  93. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-control.js +0 -40
  94. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-control.js.map +0 -1
  95. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-map.js +0 -23
  96. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-map.js.map +0 -1
  97. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/index.js +0 -27
  98. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/create-ref.js +0 -57
  99. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/create-ref.js.map +0 -1
  100. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/maplibre.js +0 -343
  101. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/maplibre.js.map +0 -1
  102. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/apply-react-style.js +0 -28
  103. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/apply-react-style.js.map +0 -1
  104. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/compare-class-names.js +0 -31
  105. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/compare-class-names.js.map +0 -1
  106. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/deep-equal.js +0 -57
  107. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/deep-equal.js.map +0 -1
  108. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/set-globals.js +0 -30
  109. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/set-globals.js.map +0 -1
  110. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/style-utils.js +0 -53
  111. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/style-utils.js.map +0 -1
  112. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/transform.js +0 -52
  113. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/transform.js.map +0 -1
  114. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/use-isomorphic-layout-effect.js +0 -22
  115. package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/use-isomorphic-layout-effect.js.map +0 -1
  116. package/dist/node_modules/.pnpm/immer@10.2.0/node_modules/immer/dist/immer.js +0 -812
  117. package/dist/node_modules/.pnpm/immer@10.2.0/node_modules/immer/dist/immer.js.map +0 -1
  118. package/dist/node_modules/.pnpm/radashi@12.7.1/node_modules/radashi/dist/radashi.js +0 -35
  119. package/dist/node_modules/.pnpm/radashi@12.7.1/node_modules/radashi/dist/radashi.js.map +0 -1
  120. package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.development.js +0 -195
  121. package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.development.js.map +0 -1
  122. package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.production.js +0 -76
  123. package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.production.js.map +0 -1
  124. package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/index.js +0 -39
  125. package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/index.js.map +0 -1
  126. package/dist/node_modules/.pnpm/react-map-gl@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/react-map-gl/dist/maplibre.js +0 -16
  127. package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/middleware/immer.js +0 -27
  128. package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/middleware/immer.js.map +0 -1
  129. package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/vanilla.js +0 -45
  130. package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/vanilla.js.map +0 -1
  131. package/dist/packages/hotkey-manager/dist/actions/register-hotkey/index.js +0 -78
  132. package/dist/packages/hotkey-manager/dist/actions/register-hotkey/index.js.map +0 -1
  133. package/dist/packages/hotkey-manager/dist/constants.js +0 -47
  134. package/dist/packages/hotkey-manager/dist/constants.js.map +0 -1
  135. package/dist/packages/hotkey-manager/dist/enums/keycode.js +0 -130
  136. package/dist/packages/hotkey-manager/dist/enums/keycode.js.map +0 -1
  137. package/dist/packages/hotkey-manager/dist/lib/is-client/index.js.map +0 -1
  138. package/dist/packages/hotkey-manager/dist/lib/is-mac/index.js +0 -24
  139. package/dist/packages/hotkey-manager/dist/lib/is-mac/index.js.map +0 -1
  140. package/dist/packages/hotkey-manager/dist/lib/key-to-id/index.js +0 -39
  141. package/dist/packages/hotkey-manager/dist/lib/key-to-id/index.js.map +0 -1
  142. package/dist/packages/hotkey-manager/dist/lib/key-to-string/index.js +0 -27
  143. package/dist/packages/hotkey-manager/dist/lib/key-to-string/index.js.map +0 -1
  144. package/dist/packages/hotkey-manager/dist/stores/hotkey-store/index.js +0 -95
  145. package/dist/packages/hotkey-manager/dist/stores/hotkey-store/index.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/deckgl/shapes/display-shape-layer/index.ts"],"sourcesContent":["/*\n * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport { Broadcast } from '@accelint/bus';\nimport { CompositeLayer } from '@deck.gl/core';\nimport { PathStyleExtension } from '@deck.gl/extensions';\nimport { GeoJsonLayer, IconLayer } from '@deck.gl/layers';\nimport { DASH_ARRAYS, SHAPE_LAYER_IDS } from '../shared/constants';\nimport { type ShapeEvent, ShapeEvents } from '../shared/events';\nimport {\n COFFIN_CORNERS,\n DEFAULT_DISPLAY_PROPS,\n MAP_INTERACTION,\n} from './constants';\nimport { createShapeLabelLayer } from './shape-label-layer';\nimport {\n getDashArray,\n getFillColor,\n getHighlightColor,\n getHighlightLineWidth,\n getHoverLineWidth,\n getStrokeColor,\n} from './utils/display-style';\nimport type { Layer, PickingInfo } from '@deck.gl/core';\nimport type { EditableShape, ShapeId } from '../shared/types';\nimport type { DisplayShapeLayerProps } from './types';\n\n/**\n * Typed event bus instance for shape events.\n * Provides type-safe event emission for shape interactions.\n */\nconst shapeBus = Broadcast.getInstance<ShapeEvent>();\n\n/**\n * State interface for DisplayShapeLayer\n */\ninterface DisplayShapeLayerState {\n /** Index of currently hovered shape, undefined when not hovering */\n hoverIndex?: number;\n /** ID of the last hovered shape for event deduplication */\n lastHoveredId?: ShapeId | null;\n /** Allow additional properties from base layer state */\n [key: string]: unknown;\n}\n\n/**\n * Cache for transformed features to avoid recreating objects on every render.\n */\ninterface FeaturesCache {\n /** Reference to the original data array for identity comparison */\n data: EditableShape[];\n /** Transformed features with shapeId added to properties */\n features: EditableShape['feature'][];\n}\n\n/**\n * DisplayShapeLayer - Read-only shapes visualization layer\n *\n * A composite deck.gl layer for displaying geographic shapes with interactive features.\n * Ideal for rendering shapes from external APIs or displaying read-only geographic data.\n *\n * ## Features\n * - **Multiple geometry types**: Point, LineString, Polygon, and Circle\n * - **Icon support**: Custom icons for Point geometries via icon atlases\n * - **Interactive selection**: Click handling with dotted border and optional highlight\n * - **Hover effects**: Line width increases on hover for better UX\n * - **Customizable labels**: Flexible label positioning with per-shape or global options\n * - **Style properties**: Full control over colors, stroke patterns, and opacity\n * - **Event bus integration**: Automatically emits shape events via @accelint/bus\n * - **Multi-map support**: Events include map instance ID for isolation\n *\n * ## Selection Visual Feedback\n * When a shape is selected via `selectedShapeId`:\n * - The shape's stroke pattern changes to dotted\n * - An optional highlight renders underneath (controlled by `showHighlight` prop)\n *\n * ## Layer Structure\n * Renders up to four sublayers (in order, bottom to top):\n * 1. **Highlight layer**: Selection highlight effect for non-icon-Point shapes (if showHighlight=true)\n * 2. **Coffin corners layer**: Selection/hover feedback for Point shapes with icons\n * 3. **Main GeoJsonLayer**: Shape geometries with styling and interaction\n * 4. **Label layer**: Text labels (if showLabels enabled)\n *\n * ## Icon Atlas Constraint\n * When using icons for Point geometries, all shapes in a single layer must share the\n * same icon atlas. The layer uses the first atlas found across all features. If you\n * need icons from different atlases, use separate DisplayShapeLayer instances.\n *\n * ## Event Bus Integration\n * Automatically emits shape events that can be consumed anywhere in your app:\n * - `shapes:selected` - Emitted when a shape is clicked (includes mapId)\n * - `shapes:hovered` - Emitted when the hovered shape changes (deduplicated, includes mapId)\n *\n * For selection with auto-deselection, use the companion `useShapeSelection` hook which handles\n * all the event wiring automatically. See the example below.\n *\n * @example Basic usage with useShapeSelection hook (recommended)\n * ```tsx\n * import '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/fiber';\n * import { useShapeSelection } from '@accelint/map-toolkit/deckgl/shapes';\n * import { uuid } from '@accelint/core';\n *\n * const MAP_ID = uuid();\n *\n * function MapWithShapes() {\n * const { selectedId } = useShapeSelection(MAP_ID);\n *\n * return (\n * <BaseMap id={MAP_ID}>\n * <displayShapeLayer\n * id=\"my-shapes\"\n * mapId={MAP_ID}\n * data={shapes}\n * selectedShapeId={selectedId}\n * showLabels={true}\n * pickable={true}\n * />\n * </BaseMap>\n * );\n * }\n * ```\n *\n * @example With custom label positioning\n * ```tsx\n * <displayShapeLayer\n * id=\"my-shapes\"\n * data={shapes}\n * showLabels={true}\n * labelOptions={{\n * // Position circle labels at the top\n * circleLabelCoordinateAnchor: 'top',\n * circleLabelVerticalAnchor: 'bottom',\n * circleLabelOffset: [0, -10],\n * // Position line labels at the middle\n * lineStringLabelCoordinateAnchor: 'middle',\n * }}\n * />\n * ```\n */\nexport class DisplayShapeLayer extends CompositeLayer<DisplayShapeLayerProps> {\n // State is typed via DisplayShapeLayerState but deck.gl doesn't support generic state\n declare state: DisplayShapeLayerState;\n\n /** Cache for transformed features to avoid recreating objects on every render */\n private featuresCache: FeaturesCache | null = null;\n\n static override layerName = 'DisplayShapeLayer';\n\n static override defaultProps = {\n ...DEFAULT_DISPLAY_PROPS,\n };\n\n /**\n * Clean up state and caches when layer is destroyed\n */\n override finalizeState(): void {\n // Clear hover state to prevent stale references\n if (this.state?.hoverIndex !== undefined) {\n this.setState({ hoverIndex: undefined, lastHoveredId: undefined });\n }\n // Clear features cache\n this.featuresCache = null;\n }\n\n /**\n * Override getPickingInfo to handle events from sublayers\n * This is the correct pattern for CompositeLayer event handling\n */\n override getPickingInfo({\n info,\n mode,\n sourceLayer,\n }: {\n info: PickingInfo;\n mode?: string;\n // biome-ignore lint/suspicious/noExplicitAny: sourceLayer type from deck.gl is not well-typed\n sourceLayer?: any;\n }) {\n // Check if this picking event came from our main shapes layer\n if (sourceLayer?.id === `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}`) {\n // Handle click events (deck.gl uses 'query' mode for clicks)\n if (mode === 'query') {\n this.handleShapeClick(info);\n }\n\n // Handle hover events (including when mode is undefined, which is hover)\n if (mode === 'hover' || !mode) {\n // Update hover state\n if (info.index !== undefined && info.index !== this.state?.hoverIndex) {\n this.setState({ hoverIndex: info.index });\n } else if (\n info.index === undefined &&\n this.state?.hoverIndex !== undefined\n ) {\n this.setState({ hoverIndex: undefined });\n }\n\n // Call hover callback\n this.handleShapeHover(info);\n }\n }\n\n return info;\n }\n\n /**\n * Convert shapes to GeoJSON features with shapeId in properties.\n * Uses caching to avoid recreating objects on every render cycle.\n */\n private getFeaturesWithId(): EditableShape['feature'][] {\n const { data } = this.props;\n\n // Return cached features if data hasn't changed (identity check)\n if (this.featuresCache?.data === data) {\n return this.featuresCache.features;\n }\n\n // Transform features and cache the result\n const features = data.map((shape) => ({\n ...shape.feature,\n properties: {\n ...shape.feature.properties,\n shapeId: shape.id,\n },\n }));\n\n this.featuresCache = { data, features };\n return features;\n }\n\n /**\n * Look up a shape by ID from the data prop.\n * Used by event handlers to get full shape without storing in feature properties.\n */\n private getShapeById(shapeId: ShapeId): EditableShape | undefined {\n return this.props.data.find((shape) => shape.id === shapeId);\n }\n\n /**\n * Handle shape click\n */\n private handleShapeClick = (info: PickingInfo): void => {\n const { onShapeClick, mapId } = this.props;\n\n if (!info.object) {\n return;\n }\n\n // Look up shape from data prop using shapeId stored in feature properties\n const shapeId = info.object.properties?.shapeId as ShapeId | undefined;\n if (!shapeId) {\n return;\n }\n\n const shape = this.getShapeById(shapeId);\n if (!shape) {\n return;\n }\n\n // Emit shape selected event via bus (include mapId for multi-map isolation)\n shapeBus.emit(ShapeEvents.selected, { shapeId: shape.id, mapId });\n\n // Call callback if provided\n if (onShapeClick) {\n onShapeClick(shape);\n }\n };\n\n /**\n * Handle shape hover\n */\n private handleShapeHover = (info: PickingInfo): void => {\n const { onShapeHover, mapId } = this.props;\n\n // Look up shape from data prop using shapeId stored in feature properties\n const shapeId =\n (info.object?.properties?.shapeId as ShapeId | undefined) ?? null;\n const shape = shapeId ? (this.getShapeById(shapeId) ?? null) : null;\n\n // Dedupe hover events - only emit if hovered shape changed\n if (shapeId !== this.state?.lastHoveredId) {\n this.setState({ lastHoveredId: shapeId });\n\n // Emit shape hovered event via bus (include mapId for multi-map isolation)\n shapeBus.emit(ShapeEvents.hovered, {\n shapeId,\n mapId,\n });\n }\n\n // Always call callback if provided (for local state updates)\n if (onShapeHover) {\n onShapeHover(shape);\n }\n };\n\n /**\n * Render highlight sublayer (underneath main layer)\n * Note: Points with icons use coffin corners instead of highlight layer\n */\n private renderHighlightLayer(\n features: EditableShape['feature'][],\n ): GeoJsonLayer | null {\n const { selectedShapeId, showHighlight, highlightColor } = this.props;\n\n if (!selectedShapeId || showHighlight === false) {\n return null;\n }\n\n const selectedFeature = features.find(\n (f) => f.properties?.shapeId === selectedShapeId,\n );\n\n if (!selectedFeature) {\n return null;\n }\n\n // Skip highlight layer for Point geometries with icons - they use coffin corners instead\n // Points without icons should still show the highlight layer\n if (selectedFeature.geometry.type === 'Point') {\n const hasIcon = !!selectedFeature.properties?.styleProperties?.icon;\n if (hasIcon) {\n return null;\n }\n }\n\n return new GeoJsonLayer({\n id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY_HIGHLIGHT}`,\n // biome-ignore lint/suspicious/noExplicitAny: GeoJsonLayer accepts various feature formats\n data: [selectedFeature] as any,\n\n // Styling\n filled: true,\n stroked: true,\n lineWidthUnits: 'pixels',\n lineWidthMinPixels: MAP_INTERACTION.LINE_WIDTH_MIN_PIXELS,\n getFillColor: () => [0, 0, 0, 0], // Transparent fill\n getLineColor: () => highlightColor || getHighlightColor(),\n getLineWidth: getHighlightLineWidth,\n\n // Behavior\n pickable: false,\n updateTriggers: {\n getLineColor: [highlightColor],\n getLineWidth: [selectedShapeId, features],\n },\n });\n }\n\n /**\n * Render coffin corners layer for Point geometries that have icons on hover/select\n * Coffin corners provide visual feedback for points instead of highlight layer\n */\n private renderCoffinCornersLayer(\n features: EditableShape['feature'][],\n ): IconLayer | null {\n const { selectedShapeId } = this.props;\n\n // Find point features that need coffin corners (hovered or selected)\n const pointFeatures = features.filter((f) => {\n if (f.geometry.type !== 'Point') {\n return false;\n }\n const hasIcon = !!f.properties?.styleProperties?.icon;\n if (!hasIcon) {\n return false;\n }\n\n const shapeId = f.properties?.shapeId;\n const isSelected = shapeId === selectedShapeId;\n const isHovered =\n this.state?.hoverIndex !== undefined &&\n features.indexOf(f) === this.state.hoverIndex;\n\n return isSelected || isHovered;\n });\n\n if (pointFeatures.length === 0) {\n return null;\n }\n\n // Get icon atlas from first point feature (all should share the same atlas)\n const firstPointIcon = pointFeatures[0]?.properties?.styleProperties?.icon;\n const iconAtlas = firstPointIcon?.atlas;\n const iconMapping = firstPointIcon?.mapping;\n\n if (!iconAtlas) {\n return null;\n }\n\n if (!iconMapping) {\n return null;\n }\n\n // Add coffin corners icons to the mapping\n const extendedMapping = {\n ...iconMapping,\n [COFFIN_CORNERS.HOVER_ICON]: {\n x: 0,\n y: 0,\n width: 76,\n height: 76,\n mask: false,\n },\n [COFFIN_CORNERS.SELECTED_ICON]: {\n x: 76,\n y: 0,\n width: 76,\n height: 76,\n mask: false,\n },\n [COFFIN_CORNERS.SELECTED_HOVER_ICON]: {\n x: 152,\n y: 0,\n width: 76,\n height: 76,\n mask: false,\n },\n };\n\n return new IconLayer({\n id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}-coffin-corners`,\n data: pointFeatures,\n iconAtlas,\n iconMapping: extendedMapping,\n getIcon: (d: EditableShape['feature']) => {\n const shapeId = d.properties?.shapeId;\n const isSelected = shapeId === selectedShapeId;\n const isHovered =\n this.state?.hoverIndex !== undefined &&\n features.indexOf(d) === this.state.hoverIndex;\n\n if (isSelected && isHovered) {\n return COFFIN_CORNERS.SELECTED_HOVER_ICON;\n }\n if (isSelected) {\n return COFFIN_CORNERS.SELECTED_ICON;\n }\n return COFFIN_CORNERS.HOVER_ICON;\n },\n getSize: COFFIN_CORNERS.SIZE,\n getPosition: (d: EditableShape['feature']) => {\n const coords =\n d.geometry.type === 'Point' ? d.geometry.coordinates : [0, 0];\n return coords as [number, number];\n },\n getPixelOffset: (d: EditableShape['feature']) => {\n const iconSize =\n d.properties?.styleProperties?.icon?.size ??\n MAP_INTERACTION.ICON_SIZE;\n // Center the coffin corners on the point icon\n return [-1, -iconSize / 2];\n },\n billboard: false,\n pickable: false,\n updateTriggers: {\n getIcon: [selectedShapeId, this.state?.hoverIndex],\n data: [features, selectedShapeId, this.state?.hoverIndex],\n },\n });\n }\n\n /**\n * Extract icon configuration from features in a single pass.\n * Returns the first icon's atlas and mapping (all shapes share the same atlas).\n * Uses early return for O(1) best case when first feature has icons.\n */\n private getIconConfig(features: EditableShape['feature'][]): {\n hasIcons: boolean;\n atlas?: string;\n mapping?: Record<\n string,\n { x: number; y: number; width: number; height: number; mask?: boolean }\n >;\n } {\n for (const f of features) {\n const icon = f.properties?.styleProperties?.icon;\n if (icon) {\n return {\n hasIcons: true,\n atlas: icon.atlas,\n mapping: icon.mapping,\n };\n }\n }\n return { hasIcons: false };\n }\n\n /**\n * Render main shapes layer\n */\n private renderMainLayer(features: EditableShape['feature'][]): GeoJsonLayer {\n const { pickable, applyBaseOpacity, selectedShapeId } = this.props;\n\n // Single-pass icon config extraction (O(1) best case with early return)\n const {\n hasIcons,\n atlas: iconAtlas,\n mapping: iconMapping,\n } = this.getIconConfig(features);\n\n return new GeoJsonLayer({\n id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}`,\n // biome-ignore lint/suspicious/noExplicitAny: GeoJsonLayer accepts various feature formats\n data: features as any,\n\n // Styling\n filled: true,\n stroked: true,\n getFillColor: (d: EditableShape['feature']) =>\n getFillColor(d, applyBaseOpacity),\n getLineColor: getStrokeColor,\n getLineWidth: (d, info) => {\n const isHovered = info?.index === this.state?.hoverIndex;\n return getHoverLineWidth(d, isHovered);\n },\n lineWidthUnits: 'pixels',\n lineWidthMinPixels: MAP_INTERACTION.LINE_WIDTH_MIN_PIXELS,\n lineWidthMaxPixels: 20,\n\n // Points - use icons if any feature has icon config, otherwise circles\n pointType: hasIcons ? 'icon' : 'circle',\n getPointRadius: (d) => {\n const iconSize = d.properties?.styleProperties?.icon?.size;\n return iconSize ?? 2;\n },\n pointRadiusUnits: 'pixels',\n\n // Icon configuration (only used if pointType includes 'icon')\n ...(hasIcons && iconAtlas ? { iconAtlas } : {}),\n ...(hasIcons && iconMapping ? { iconMapping } : {}),\n ...(hasIcons\n ? {\n getIcon: (d: EditableShape['feature']) =>\n d.properties?.styleProperties?.icon?.name ?? 'marker',\n getIconSize: (d: EditableShape['feature']) => {\n return (\n d.properties?.styleProperties?.icon?.size ??\n MAP_INTERACTION.ICON_SIZE\n );\n },\n getIconColor: getStrokeColor,\n getIconPixelOffset: (d: EditableShape['feature']) => {\n const iconSize =\n d.properties?.styleProperties?.icon?.size ??\n MAP_INTERACTION.ICON_SIZE;\n return [-1, -iconSize / 2];\n },\n iconBillboard: false,\n }\n : {}),\n\n // Dash pattern support - selected shapes get dotted border\n extensions: [new PathStyleExtension({ dash: true })],\n getDashArray: (d: EditableShape['feature']) => {\n const isSelected = d.properties?.shapeId === selectedShapeId;\n if (isSelected) {\n return DASH_ARRAYS.dotted;\n }\n return getDashArray(d);\n },\n\n // Behavior\n pickable,\n autoHighlight: false, // We handle highlighting manually\n // Note: onClick and onHover are handled via getPickingInfo() override\n\n // Update triggers\n updateTriggers: {\n getFillColor: [features, applyBaseOpacity],\n getLineColor: [features],\n getLineWidth: [features, this.state?.hoverIndex],\n getDashArray: [features, selectedShapeId],\n getPointRadius: [features],\n ...(hasIcons\n ? {\n getIcon: [features],\n getIconSize: [features],\n getIconColor: [features],\n getIconPixelOffset: [features],\n }\n : {}),\n },\n });\n }\n\n /**\n * Render labels layer\n */\n private renderLabelsLayer(): ReturnType<typeof createShapeLabelLayer> | null {\n const { showLabels, data, labelOptions } = this.props;\n\n if (!showLabels) {\n return null;\n }\n\n return createShapeLabelLayer({\n id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY_LABELS}`,\n data,\n labelOptions,\n });\n }\n\n /**\n * Render all sublayers\n */\n renderLayers(): Layer[] {\n // Compute features once per render cycle for performance\n const features = this.getFeaturesWithId();\n\n return [\n this.renderHighlightLayer(features),\n this.renderCoffinCornersLayer(features),\n this.renderMainLayer(features),\n this.renderLabelsLayer(),\n ].filter(Boolean) as Layer[];\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /*
2
+ * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at https://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { EditableShape } from "../shared/types.js";
14
+ import { LabelPositionOptions } from "./utils/labels.js";
15
+ import { TextLayer } from "@deck.gl/layers";
16
+
17
+ //#region src/deckgl/shapes/display-shape-layer/shape-label-layer.d.ts
18
+ /**
19
+ * Props for creating a shape label layer
20
+ */
21
+ interface ShapeLabelLayerProps {
22
+ /** Layer ID (defaults to DISPLAY_LABELS constant) */
23
+ id?: string;
24
+ /** Array of shapes to label */
25
+ data: EditableShape[];
26
+ /**
27
+ * Global label positioning options
28
+ * Per-shape properties in styleProperties take precedence
29
+ */
30
+ labelOptions?: LabelPositionOptions;
31
+ }
32
+ /**
33
+ * Creates a TextLayer for rendering shape labels with intelligent positioning
34
+ *
35
+ * ## Features
36
+ * - **Geometry-aware positioning**: Different defaults for Point, LineString, Polygon, and Circle
37
+ * - **Three-tier priority system**: Per-shape properties > labelOptions > defaults
38
+ * - **Coordinate anchoring**: Position labels at start/middle/end (or edge positions for circles)
39
+ * - **Pixel-based offsets**: Consistent label placement at all zoom levels
40
+ * - **Text-only styling**: White uppercase text with black outline for legibility
41
+ * - **Position caching**: Label positions are computed once per shape using a WeakMap cache
42
+ *
43
+ * ## Label Positioning Priority
44
+ * 1. Per-shape `styleProperties` (highest priority)
45
+ * 2. Global `labelOptions` parameter
46
+ * 3. Geometry-specific defaults (fallback)
47
+ *
48
+ * @param props - Shape label layer configuration
49
+ * @returns Configured TextLayer instance
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * const labelLayer = createShapeLabelLayer({
54
+ * id: 'my-labels',
55
+ * data: shapes,
56
+ * labelOptions: {
57
+ * circleLabelCoordinateAnchor: 'top',
58
+ * pointLabelOffset: [0, -20],
59
+ * },
60
+ * });
61
+ * ```
62
+ */
63
+ declare function createShapeLabelLayer(props: ShapeLabelLayerProps): TextLayer<EditableShape>;
64
+ //#endregion
65
+ export { ShapeLabelLayerProps, createShapeLabelLayer };
66
+ //# sourceMappingURL=shape-label-layer.d.ts.map
@@ -0,0 +1,116 @@
1
+ /*
2
+ * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at https://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+
14
+ 'use client';
15
+
16
+ import { SHAPE_LAYER_IDS } from "../shared/constants.js";
17
+ import { getLabelPosition2d, getLabelText } from "./utils/labels.js";
18
+ import { TextLayer } from "@deck.gl/layers";
19
+
20
+ //#region src/deckgl/shapes/display-shape-layer/shape-label-layer.ts
21
+ /**
22
+ * Creates a cached label position getter to avoid computing position multiple times per shape.
23
+ * Uses WeakMap so positions are garbage collected when shapes are removed.
24
+ */
25
+ function createCachedPositionGetter(labelOptions) {
26
+ const cache = /* @__PURE__ */ new WeakMap();
27
+ const getNullable = (shape) => {
28
+ if (cache.has(shape)) return cache.get(shape) ?? null;
29
+ const position = getLabelPosition2d(shape, labelOptions);
30
+ cache.set(shape, position);
31
+ return position;
32
+ };
33
+ const getRequired = (shape) => {
34
+ const position = getNullable(shape);
35
+ if (!position) throw new Error("Shape has no valid position - should have been filtered");
36
+ return position;
37
+ };
38
+ return {
39
+ getRequired,
40
+ getNullable
41
+ };
42
+ }
43
+ /**
44
+ * Creates a TextLayer for rendering shape labels with intelligent positioning
45
+ *
46
+ * ## Features
47
+ * - **Geometry-aware positioning**: Different defaults for Point, LineString, Polygon, and Circle
48
+ * - **Three-tier priority system**: Per-shape properties > labelOptions > defaults
49
+ * - **Coordinate anchoring**: Position labels at start/middle/end (or edge positions for circles)
50
+ * - **Pixel-based offsets**: Consistent label placement at all zoom levels
51
+ * - **Text-only styling**: White uppercase text with black outline for legibility
52
+ * - **Position caching**: Label positions are computed once per shape using a WeakMap cache
53
+ *
54
+ * ## Label Positioning Priority
55
+ * 1. Per-shape `styleProperties` (highest priority)
56
+ * 2. Global `labelOptions` parameter
57
+ * 3. Geometry-specific defaults (fallback)
58
+ *
59
+ * @param props - Shape label layer configuration
60
+ * @returns Configured TextLayer instance
61
+ *
62
+ * @example
63
+ * ```tsx
64
+ * const labelLayer = createShapeLabelLayer({
65
+ * id: 'my-labels',
66
+ * data: shapes,
67
+ * labelOptions: {
68
+ * circleLabelCoordinateAnchor: 'top',
69
+ * pointLabelOffset: [0, -20],
70
+ * },
71
+ * });
72
+ * ```
73
+ */
74
+ function createShapeLabelLayer(props) {
75
+ const { id = SHAPE_LAYER_IDS.DISPLAY_LABELS, data, labelOptions } = props;
76
+ const { getRequired, getNullable } = createCachedPositionGetter(labelOptions);
77
+ return new TextLayer({
78
+ id,
79
+ data: data.filter((shape) => getNullable(shape) !== null),
80
+ getText: getLabelText,
81
+ getPosition: (d) => getRequired(d).coordinates,
82
+ getPixelOffset: (d) => getRequired(d).pixelOffset,
83
+ getTextAnchor: (d) => getRequired(d).textAnchor,
84
+ getAlignmentBaseline: (d) => getRequired(d).alignmentBaseline,
85
+ getColor: [
86
+ 255,
87
+ 255,
88
+ 255,
89
+ 255
90
+ ],
91
+ getSize: 10,
92
+ getAngle: 0,
93
+ outlineWidth: 2,
94
+ outlineColor: [
95
+ 0,
96
+ 0,
97
+ 0,
98
+ 255
99
+ ],
100
+ background: false,
101
+ fontFamily: "Roboto MonoVariable, monospace",
102
+ fontWeight: "bold",
103
+ fontSettings: { sdf: true },
104
+ updateTriggers: {
105
+ getPosition: [labelOptions],
106
+ getPixelOffset: [labelOptions],
107
+ getTextAnchor: [labelOptions],
108
+ getAlignmentBaseline: [labelOptions]
109
+ },
110
+ pickable: false
111
+ });
112
+ }
113
+
114
+ //#endregion
115
+ export { createShapeLabelLayer };
116
+ //# sourceMappingURL=shape-label-layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape-label-layer.js","names":[],"sources":["../../../../src/deckgl/shapes/display-shape-layer/shape-label-layer.ts"],"sourcesContent":["/*\n * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport { TextLayer } from '@deck.gl/layers';\nimport { SHAPE_LAYER_IDS } from '../shared/constants';\nimport {\n getLabelPosition2d,\n getLabelText,\n type LabelPosition2d,\n type LabelPositionOptions,\n} from './utils/labels';\nimport type { EditableShape } from '../shared/types';\n\n/**\n * Creates a cached label position getter to avoid computing position multiple times per shape.\n * Uses WeakMap so positions are garbage collected when shapes are removed.\n */\nfunction createCachedPositionGetter(\n labelOptions: LabelPositionOptions | undefined,\n) {\n const cache = new WeakMap<EditableShape, LabelPosition2d | null>();\n\n // Returns nullable position for filtering\n const getNullable = (shape: EditableShape): LabelPosition2d | null => {\n if (cache.has(shape)) {\n return cache.get(shape) ?? null;\n }\n const position = getLabelPosition2d(shape, labelOptions);\n cache.set(shape, position);\n return position;\n };\n\n // Returns position, throwing if null (use only after filtering)\n const getRequired = (shape: EditableShape): LabelPosition2d => {\n const position = getNullable(shape);\n if (!position) {\n throw new Error(\n 'Shape has no valid position - should have been filtered',\n );\n }\n return position;\n };\n\n return { getRequired, getNullable };\n}\n\n/**\n * Props for creating a shape label layer\n */\nexport interface ShapeLabelLayerProps {\n /** Layer ID (defaults to DISPLAY_LABELS constant) */\n id?: string;\n /** Array of shapes to label */\n data: EditableShape[];\n /**\n * Global label positioning options\n * Per-shape properties in styleProperties take precedence\n */\n labelOptions?: LabelPositionOptions;\n}\n\n/**\n * Creates a TextLayer for rendering shape labels with intelligent positioning\n *\n * ## Features\n * - **Geometry-aware positioning**: Different defaults for Point, LineString, Polygon, and Circle\n * - **Three-tier priority system**: Per-shape properties > labelOptions > defaults\n * - **Coordinate anchoring**: Position labels at start/middle/end (or edge positions for circles)\n * - **Pixel-based offsets**: Consistent label placement at all zoom levels\n * - **Text-only styling**: White uppercase text with black outline for legibility\n * - **Position caching**: Label positions are computed once per shape using a WeakMap cache\n *\n * ## Label Positioning Priority\n * 1. Per-shape `styleProperties` (highest priority)\n * 2. Global `labelOptions` parameter\n * 3. Geometry-specific defaults (fallback)\n *\n * @param props - Shape label layer configuration\n * @returns Configured TextLayer instance\n *\n * @example\n * ```tsx\n * const labelLayer = createShapeLabelLayer({\n * id: 'my-labels',\n * data: shapes,\n * labelOptions: {\n * circleLabelCoordinateAnchor: 'top',\n * pointLabelOffset: [0, -20],\n * },\n * });\n * ```\n */\nexport function createShapeLabelLayer(\n props: ShapeLabelLayerProps,\n): TextLayer<EditableShape> {\n const { id = SHAPE_LAYER_IDS.DISPLAY_LABELS, data, labelOptions } = props;\n\n // Create cached position getter to avoid computing position 4x per shape\n const { getRequired, getNullable } = createCachedPositionGetter(labelOptions);\n\n // Filter out shapes with invalid positions (null coordinates)\n const validData = data.filter((shape) => getNullable(shape) !== null);\n\n return new TextLayer<EditableShape>({\n id,\n data: validData,\n\n // Text content - uppercase\n getText: getLabelText,\n\n // Position - use cached getter for all position-related properties\n // getRequired is safe because we filtered out null positions above\n getPosition: (d: EditableShape) => getRequired(d).coordinates,\n getPixelOffset: (d: EditableShape) => getRequired(d).pixelOffset,\n getTextAnchor: (d: EditableShape) => getRequired(d).textAnchor,\n getAlignmentBaseline: (d: EditableShape) =>\n getRequired(d).alignmentBaseline,\n\n // Styling - white text with black outline, no background\n getColor: [255, 255, 255, 255], // White text\n getSize: 10,\n getAngle: 0,\n\n // Text outline for legibility (black stroke around white text)\n outlineWidth: 2,\n outlineColor: [0, 0, 0, 255], // Black outline\n\n // No background or border\n background: false,\n\n // Font\n fontFamily: 'Roboto MonoVariable, monospace',\n fontWeight: 'bold',\n fontSettings: {\n sdf: true,\n },\n\n // Update triggers - tell deck.gl to recalculate when labelOptions change\n updateTriggers: {\n getPosition: [labelOptions],\n getPixelOffset: [labelOptions],\n getTextAnchor: [labelOptions],\n getAlignmentBaseline: [labelOptions],\n },\n\n // Behavior\n pickable: false,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAS,2BACP,cACA;CACA,MAAM,wBAAQ,IAAI,SAAgD;CAGlE,MAAM,eAAe,UAAiD;AACpE,MAAI,MAAM,IAAI,MAAM,CAClB,QAAO,MAAM,IAAI,MAAM,IAAI;EAE7B,MAAM,WAAW,mBAAmB,OAAO,aAAa;AACxD,QAAM,IAAI,OAAO,SAAS;AAC1B,SAAO;;CAIT,MAAM,eAAe,UAA0C;EAC7D,MAAM,WAAW,YAAY,MAAM;AACnC,MAAI,CAAC,SACH,OAAM,IAAI,MACR,0DACD;AAEH,SAAO;;AAGT,QAAO;EAAE;EAAa;EAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDrC,SAAgB,sBACd,OAC0B;CAC1B,MAAM,EAAE,KAAK,gBAAgB,gBAAgB,MAAM,iBAAiB;CAGpE,MAAM,EAAE,aAAa,gBAAgB,2BAA2B,aAAa;AAK7E,QAAO,IAAI,UAAyB;EAClC;EACA,MAJgB,KAAK,QAAQ,UAAU,YAAY,MAAM,KAAK,KAAK;EAOnE,SAAS;EAIT,cAAc,MAAqB,YAAY,EAAE,CAAC;EAClD,iBAAiB,MAAqB,YAAY,EAAE,CAAC;EACrD,gBAAgB,MAAqB,YAAY,EAAE,CAAC;EACpD,uBAAuB,MACrB,YAAY,EAAE,CAAC;EAGjB,UAAU;GAAC;GAAK;GAAK;GAAK;GAAI;EAC9B,SAAS;EACT,UAAU;EAGV,cAAc;EACd,cAAc;GAAC;GAAG;GAAG;GAAG;GAAI;EAG5B,YAAY;EAGZ,YAAY;EACZ,YAAY;EACZ,cAAc,EACZ,KAAK,MACN;EAGD,gBAAgB;GACd,aAAa,CAAC,aAAa;GAC3B,gBAAgB,CAAC,aAAa;GAC9B,eAAe,CAAC,aAAa;GAC7B,sBAAsB,CAAC,aAAa;GACrC;EAGD,UAAU;EACX,CAAC"}
@@ -0,0 +1,87 @@
1
+ /*
2
+ * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at https://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { ShapeId } from "../shared/types.js";
14
+ import { UniqueId } from "@accelint/core";
15
+
16
+ //#region src/deckgl/shapes/display-shape-layer/store.d.ts
17
+ /**
18
+ * Creates or retrieves a cached subscription function for a given mapId.
19
+ * Uses a fan-out pattern: 1 bus listener -> N React subscribers.
20
+ * Automatically cleans up selection state when the last subscriber unsubscribes.
21
+ *
22
+ * @param mapId - The unique identifier for the map instance
23
+ * @returns A subscription function for useSyncExternalStore
24
+ */
25
+ declare function getOrCreateSubscription(mapId: UniqueId): (onStoreChange: () => void) => () => void;
26
+ /**
27
+ * Creates or retrieves a cached snapshot function for a given mapId.
28
+ * The selection ID returned gets equality checked, so it needs to be stable or React re-renders unnecessarily.
29
+ *
30
+ * @param mapId - The unique identifier for the map instance
31
+ * @returns A snapshot function for useSyncExternalStore
32
+ */
33
+ declare function getOrCreateSnapshot(mapId: UniqueId): () => ShapeId | undefined;
34
+ /**
35
+ * Creates or retrieves a cached server snapshot function for a given mapId.
36
+ * Server snapshots always return undefined since selection state is client-only.
37
+ * Required for SSR/RSC compatibility with useSyncExternalStore.
38
+ *
39
+ * @param mapId - The unique identifier for the map instance
40
+ * @returns A server snapshot function for useSyncExternalStore
41
+ */
42
+ declare function getOrCreateServerSnapshot(mapId: UniqueId): () => ShapeId | undefined;
43
+ /**
44
+ * Creates or retrieves a cached setSelectedId function for a given mapId.
45
+ * This maintains referential stability for the function reference.
46
+ *
47
+ * @param mapId - The unique identifier for the map instance
48
+ * @returns A setSelectedId function for this instance
49
+ */
50
+ declare function getOrCreateSetSelectedId(mapId: UniqueId): (id: ShapeId | undefined) => void;
51
+ /**
52
+ * Creates or retrieves a cached clearSelection function for a given mapId.
53
+ * This maintains referential stability for the function reference.
54
+ *
55
+ * Note: This always emits a deselected event, even if nothing is currently selected.
56
+ * This matches the original behavior and allows consumers to use clearSelection
57
+ * as a "force deselect" without needing to check selection state first.
58
+ *
59
+ * @param mapId - The unique identifier for the map instance
60
+ * @returns A clearSelection function for this instance
61
+ */
62
+ declare function getOrCreateClearSelection(mapId: UniqueId): () => void;
63
+ /**
64
+ * Get the current selected shape ID for a given map instance (direct access, not reactive).
65
+ * @internal - For internal map-toolkit use only
66
+ *
67
+ * @param mapId - The unique identifier for the map instance
68
+ * @returns The currently selected shape ID, or undefined
69
+ */
70
+ declare function getSelectedShapeId(mapId: UniqueId): ShapeId | undefined;
71
+ /**
72
+ * Manually clear all selection state for a specific mapId.
73
+ * This is typically not needed as cleanup happens automatically when all subscribers unmount.
74
+ * Use this only in advanced scenarios where manual cleanup is required.
75
+ *
76
+ * @param mapId - The unique identifier for the map instance to clear
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * // Manual cleanup (rarely needed)
81
+ * clearSelectionState('my-map-instance');
82
+ * ```
83
+ */
84
+ declare function clearSelectionState(mapId: UniqueId): void;
85
+ //#endregion
86
+ export { clearSelectionState, getOrCreateClearSelection, getOrCreateServerSnapshot, getOrCreateSetSelectedId, getOrCreateSnapshot, getOrCreateSubscription, getSelectedShapeId };
87
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1,316 @@
1
+ /*
2
+ * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at https://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+
14
+ import { MapEvents } from "../../base-map/events.js";
15
+ import { ShapeEvents } from "../shared/events.js";
16
+ import { Broadcast } from "@accelint/bus";
17
+
18
+ //#region src/deckgl/shapes/display-shape-layer/store.ts
19
+ /**
20
+ * Shape Selection Store
21
+ *
22
+ * This module provides a store pattern for managing shape selection state,
23
+ * designed to work with React's `useSyncExternalStore` hook.
24
+ *
25
+ * ## Architecture
26
+ *
27
+ * The store uses a fan-out pattern similar to `map-cursor/store.ts` and `map-mode/store.ts`:
28
+ * - **1 bus listener per mapId** - Regardless of how many React components subscribe
29
+ * - **N React subscribers** - Each component gets notified via `useSyncExternalStore`
30
+ * - **Automatic cleanup** - Bus listeners are removed when the last subscriber unmounts
31
+ *
32
+ * ## Why This Pattern?
33
+ *
34
+ * Without this pattern, using `useOn` directly in each React component would
35
+ * create N bus listeners for N components subscribing to the same events.
36
+ * The fan-out pattern ensures exactly 1 bus listener per mapId, regardless
37
+ * of how many React components need the state. This also provides proper
38
+ * cleanup semantics that prevent listener accumulation during HMR.
39
+ *
40
+ * ## Usage
41
+ *
42
+ * This module is internal to `useShapeSelection`. Use the hook instead:
43
+ *
44
+ * ```tsx
45
+ * import { useShapeSelection } from '@accelint/map-toolkit/deckgl/shapes';
46
+ *
47
+ * const { selectedId, setSelectedId, clearSelection } = useShapeSelection(mapId);
48
+ * ```
49
+ *
50
+ * @module
51
+ */
52
+ /**
53
+ * Typed event bus instances for shape and map events
54
+ */
55
+ const shapeBus = Broadcast.getInstance();
56
+ const mapBus = Broadcast.getInstance();
57
+ /**
58
+ * Store for shape selection state keyed by mapId
59
+ */
60
+ const selectionStore = /* @__PURE__ */ new Map();
61
+ /**
62
+ * Track React component subscribers per mapId (for fan-out notifications).
63
+ * Each Set contains onStoreChange callbacks from useSyncExternalStore.
64
+ */
65
+ const componentSubscribers = /* @__PURE__ */ new Map();
66
+ /**
67
+ * Cache of bus unsubscribe functions (1 per mapId).
68
+ * This ensures we only have one bus listener per map instance, regardless of
69
+ * how many React components subscribe to it.
70
+ */
71
+ const busUnsubscribers = /* @__PURE__ */ new Map();
72
+ /**
73
+ * Cache of subscription functions per mapId to avoid recreating on every render
74
+ */
75
+ const subscriptionCache = /* @__PURE__ */ new Map();
76
+ /**
77
+ * Cache of snapshot functions per mapId to maintain referential stability
78
+ */
79
+ const snapshotCache = /* @__PURE__ */ new Map();
80
+ /**
81
+ * Cache of server snapshot functions per mapId to maintain referential stability.
82
+ * Server snapshots always return undefined since selection state is client-only.
83
+ */
84
+ const serverSnapshotCache = /* @__PURE__ */ new Map();
85
+ /**
86
+ * Cache of setSelectedId functions per mapId to maintain referential stability
87
+ */
88
+ const setSelectedIdCache = /* @__PURE__ */ new Map();
89
+ /**
90
+ * Cache of clearSelection functions per mapId to maintain referential stability
91
+ */
92
+ const clearSelectionCache = /* @__PURE__ */ new Map();
93
+ /**
94
+ * All state caches that need cleanup when an instance is removed
95
+ */
96
+ const stateCaches = [
97
+ selectionStore,
98
+ componentSubscribers,
99
+ subscriptionCache,
100
+ snapshotCache,
101
+ serverSnapshotCache,
102
+ setSelectedIdCache,
103
+ clearSelectionCache
104
+ ];
105
+ /**
106
+ * Clear all cached state for a given mapId
107
+ */
108
+ function clearAllCaches(mapId) {
109
+ for (const cache of stateCaches) cache.delete(mapId);
110
+ }
111
+ /**
112
+ * Get or create selection state for a given mapId
113
+ */
114
+ function getOrCreateState(mapId) {
115
+ if (!selectionStore.has(mapId)) selectionStore.set(mapId, { selectedId: void 0 });
116
+ return selectionStore.get(mapId);
117
+ }
118
+ /**
119
+ * Get current selection snapshot for a given map instance (for useSyncExternalStore)
120
+ */
121
+ function getSnapshot(mapId) {
122
+ return selectionStore.get(mapId)?.selectedId;
123
+ }
124
+ /**
125
+ * Notify all React subscribers for a given mapId
126
+ */
127
+ function notifySubscribers(mapId) {
128
+ const subscribers = componentSubscribers.get(mapId);
129
+ if (subscribers) for (const onStoreChange of subscribers) onStoreChange();
130
+ }
131
+ /**
132
+ * Ensures a single bus listener exists for the given mapId.
133
+ * All React subscribers will be notified via fan-out when the bus events fire.
134
+ * This prevents creating N bus listeners for N React components.
135
+ *
136
+ * @param mapId - The unique identifier for the map instance
137
+ */
138
+ function ensureBusListener(mapId) {
139
+ if (busUnsubscribers.has(mapId)) return;
140
+ const state = getOrCreateState(mapId);
141
+ const unsubSelected = shapeBus.on(ShapeEvents.selected, (event) => {
142
+ if (event.payload.mapId !== mapId) return;
143
+ const previousId = state.selectedId;
144
+ state.selectedId = event.payload.shapeId;
145
+ if (previousId !== state.selectedId) notifySubscribers(mapId);
146
+ });
147
+ const unsubDeselected = shapeBus.on(ShapeEvents.deselected, (event) => {
148
+ if (event.payload.mapId !== mapId) return;
149
+ const previousId = state.selectedId;
150
+ state.selectedId = void 0;
151
+ if (previousId !== state.selectedId) notifySubscribers(mapId);
152
+ });
153
+ const unsubClick = mapBus.on(MapEvents.click, (event) => {
154
+ if (state.selectedId && event.payload.id === mapId && event.payload.info.index === -1) shapeBus.emit(ShapeEvents.deselected, { mapId });
155
+ });
156
+ busUnsubscribers.set(mapId, () => {
157
+ unsubSelected();
158
+ unsubDeselected();
159
+ unsubClick();
160
+ });
161
+ }
162
+ /**
163
+ * Cleans up the bus listener if no React subscribers remain.
164
+ *
165
+ * @param mapId - The unique identifier for the map instance
166
+ */
167
+ function cleanupBusListenerIfNeeded(mapId) {
168
+ const subscribers = componentSubscribers.get(mapId);
169
+ if (!subscribers || subscribers.size === 0) {
170
+ const unsub = busUnsubscribers.get(mapId);
171
+ if (unsub) {
172
+ unsub();
173
+ busUnsubscribers.delete(mapId);
174
+ }
175
+ clearAllCaches(mapId);
176
+ }
177
+ }
178
+ /**
179
+ * Creates or retrieves a cached subscription function for a given mapId.
180
+ * Uses a fan-out pattern: 1 bus listener -> N React subscribers.
181
+ * Automatically cleans up selection state when the last subscriber unsubscribes.
182
+ *
183
+ * @param mapId - The unique identifier for the map instance
184
+ * @returns A subscription function for useSyncExternalStore
185
+ */
186
+ function getOrCreateSubscription(mapId) {
187
+ let subscription = subscriptionCache.get(mapId);
188
+ if (!subscription) {
189
+ subscription = (onStoreChange) => {
190
+ getOrCreateState(mapId);
191
+ ensureBusListener(mapId);
192
+ let subscriberSet = componentSubscribers.get(mapId);
193
+ if (!subscriberSet) {
194
+ subscriberSet = /* @__PURE__ */ new Set();
195
+ componentSubscribers.set(mapId, subscriberSet);
196
+ }
197
+ subscriberSet.add(onStoreChange);
198
+ return () => {
199
+ const currentSubscriberSet = componentSubscribers.get(mapId);
200
+ if (currentSubscriberSet) currentSubscriberSet.delete(onStoreChange);
201
+ cleanupBusListenerIfNeeded(mapId);
202
+ };
203
+ };
204
+ subscriptionCache.set(mapId, subscription);
205
+ }
206
+ return subscription;
207
+ }
208
+ /**
209
+ * Creates or retrieves a cached snapshot function for a given mapId.
210
+ * The selection ID returned gets equality checked, so it needs to be stable or React re-renders unnecessarily.
211
+ *
212
+ * @param mapId - The unique identifier for the map instance
213
+ * @returns A snapshot function for useSyncExternalStore
214
+ */
215
+ function getOrCreateSnapshot(mapId) {
216
+ let snapshot = snapshotCache.get(mapId);
217
+ if (!snapshot) {
218
+ snapshot = () => getSnapshot(mapId);
219
+ snapshotCache.set(mapId, snapshot);
220
+ }
221
+ return snapshot;
222
+ }
223
+ /**
224
+ * Creates or retrieves a cached server snapshot function for a given mapId.
225
+ * Server snapshots always return undefined since selection state is client-only.
226
+ * Required for SSR/RSC compatibility with useSyncExternalStore.
227
+ *
228
+ * @param mapId - The unique identifier for the map instance
229
+ * @returns A server snapshot function for useSyncExternalStore
230
+ */
231
+ function getOrCreateServerSnapshot(mapId) {
232
+ let serverSnapshot = serverSnapshotCache.get(mapId);
233
+ if (!serverSnapshot) {
234
+ serverSnapshot = () => void 0;
235
+ serverSnapshotCache.set(mapId, serverSnapshot);
236
+ }
237
+ return serverSnapshot;
238
+ }
239
+ /**
240
+ * Creates or retrieves a cached setSelectedId function for a given mapId.
241
+ * This maintains referential stability for the function reference.
242
+ *
243
+ * @param mapId - The unique identifier for the map instance
244
+ * @returns A setSelectedId function for this instance
245
+ */
246
+ function getOrCreateSetSelectedId(mapId) {
247
+ let setSelectedId = setSelectedIdCache.get(mapId);
248
+ if (!setSelectedId) {
249
+ setSelectedId = (id) => {
250
+ const previousId = getOrCreateState(mapId).selectedId;
251
+ if (id === void 0 && previousId !== void 0) shapeBus.emit(ShapeEvents.deselected, { mapId });
252
+ else if (id !== void 0 && previousId !== id) shapeBus.emit(ShapeEvents.selected, {
253
+ shapeId: id,
254
+ mapId
255
+ });
256
+ };
257
+ setSelectedIdCache.set(mapId, setSelectedId);
258
+ }
259
+ return setSelectedId;
260
+ }
261
+ /**
262
+ * Creates or retrieves a cached clearSelection function for a given mapId.
263
+ * This maintains referential stability for the function reference.
264
+ *
265
+ * Note: This always emits a deselected event, even if nothing is currently selected.
266
+ * This matches the original behavior and allows consumers to use clearSelection
267
+ * as a "force deselect" without needing to check selection state first.
268
+ *
269
+ * @param mapId - The unique identifier for the map instance
270
+ * @returns A clearSelection function for this instance
271
+ */
272
+ function getOrCreateClearSelection(mapId) {
273
+ let clearSelection = clearSelectionCache.get(mapId);
274
+ if (!clearSelection) {
275
+ clearSelection = () => {
276
+ shapeBus.emit(ShapeEvents.deselected, { mapId });
277
+ };
278
+ clearSelectionCache.set(mapId, clearSelection);
279
+ }
280
+ return clearSelection;
281
+ }
282
+ /**
283
+ * Get the current selected shape ID for a given map instance (direct access, not reactive).
284
+ * @internal - For internal map-toolkit use only
285
+ *
286
+ * @param mapId - The unique identifier for the map instance
287
+ * @returns The currently selected shape ID, or undefined
288
+ */
289
+ function getSelectedShapeId(mapId) {
290
+ return getSnapshot(mapId);
291
+ }
292
+ /**
293
+ * Manually clear all selection state for a specific mapId.
294
+ * This is typically not needed as cleanup happens automatically when all subscribers unmount.
295
+ * Use this only in advanced scenarios where manual cleanup is required.
296
+ *
297
+ * @param mapId - The unique identifier for the map instance to clear
298
+ *
299
+ * @example
300
+ * ```tsx
301
+ * // Manual cleanup (rarely needed)
302
+ * clearSelectionState('my-map-instance');
303
+ * ```
304
+ */
305
+ function clearSelectionState(mapId) {
306
+ const unsub = busUnsubscribers.get(mapId);
307
+ if (unsub) {
308
+ unsub();
309
+ busUnsubscribers.delete(mapId);
310
+ }
311
+ clearAllCaches(mapId);
312
+ }
313
+
314
+ //#endregion
315
+ export { clearSelectionState, getOrCreateClearSelection, getOrCreateServerSnapshot, getOrCreateSetSelectedId, getOrCreateSnapshot, getOrCreateSubscription, getSelectedShapeId };
316
+ //# sourceMappingURL=store.js.map