@elabs-ai/components-maps 4.2.0 → 5.1.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 (291) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-2FPB53NG.js +36 -0
  3. package/dist/chunk-2FPB53NG.js.map +1 -0
  4. package/dist/chunk-2VVJRT66.js +37 -0
  5. package/dist/chunk-2VVJRT66.js.map +1 -0
  6. package/dist/chunk-3HXFONRP.js +2 -0
  7. package/dist/chunk-3HXFONRP.js.map +1 -0
  8. package/dist/chunk-3SJN3IYT.js +2 -0
  9. package/dist/chunk-3SJN3IYT.js.map +1 -0
  10. package/dist/chunk-4DANBD5B.js +55 -0
  11. package/dist/chunk-4DANBD5B.js.map +1 -0
  12. package/dist/chunk-5PDVG53B.js +2 -0
  13. package/dist/chunk-5PDVG53B.js.map +1 -0
  14. package/dist/chunk-643R7IJC.js +2 -0
  15. package/dist/chunk-643R7IJC.js.map +1 -0
  16. package/dist/chunk-B3ZDBQ6I.js +164 -0
  17. package/dist/chunk-B3ZDBQ6I.js.map +1 -0
  18. package/dist/chunk-BGCE3RR5.js +35 -0
  19. package/dist/chunk-BGCE3RR5.js.map +1 -0
  20. package/dist/chunk-C4VI37TD.js +485 -0
  21. package/dist/chunk-C4VI37TD.js.map +1 -0
  22. package/dist/chunk-CJJT6AEI.js +179 -0
  23. package/dist/chunk-CJJT6AEI.js.map +1 -0
  24. package/dist/chunk-D2RCPTRO.js +2 -0
  25. package/dist/chunk-D2RCPTRO.js.map +1 -0
  26. package/dist/chunk-DTV7JZVN.js +2 -0
  27. package/dist/chunk-DTV7JZVN.js.map +1 -0
  28. package/dist/chunk-E65Z3MSV.js +80 -0
  29. package/dist/chunk-E65Z3MSV.js.map +1 -0
  30. package/dist/chunk-E7VPNINZ.js +70 -0
  31. package/dist/chunk-E7VPNINZ.js.map +1 -0
  32. package/dist/chunk-FHV5IA6N.js +100 -0
  33. package/dist/chunk-FHV5IA6N.js.map +1 -0
  34. package/dist/chunk-FQ6V7BFZ.js +347 -0
  35. package/dist/chunk-FQ6V7BFZ.js.map +1 -0
  36. package/dist/chunk-G7JDXDJN.js +19 -0
  37. package/dist/chunk-G7JDXDJN.js.map +1 -0
  38. package/dist/chunk-GWULZOMW.js +21 -0
  39. package/dist/chunk-GWULZOMW.js.map +1 -0
  40. package/dist/chunk-GYDTTAVD.js +120 -0
  41. package/dist/chunk-GYDTTAVD.js.map +1 -0
  42. package/dist/chunk-HCOBWIM4.js +48 -0
  43. package/dist/chunk-HCOBWIM4.js.map +1 -0
  44. package/dist/chunk-IAHK5MVT.js +2 -0
  45. package/dist/chunk-IAHK5MVT.js.map +1 -0
  46. package/dist/chunk-JGJ6442I.js +235 -0
  47. package/dist/chunk-JGJ6442I.js.map +1 -0
  48. package/dist/chunk-JYFERXZT.js +18 -0
  49. package/dist/chunk-JYFERXZT.js.map +1 -0
  50. package/dist/chunk-LU2UEKFM.js +53 -0
  51. package/dist/chunk-LU2UEKFM.js.map +1 -0
  52. package/dist/chunk-LVYM6C5C.js +2 -0
  53. package/dist/chunk-LVYM6C5C.js.map +1 -0
  54. package/dist/chunk-MIMDP3EU.js +104 -0
  55. package/dist/chunk-MIMDP3EU.js.map +1 -0
  56. package/dist/chunk-NNBURJHY.js +2 -0
  57. package/dist/chunk-NNBURJHY.js.map +1 -0
  58. package/dist/chunk-NPUC3V37.js +2 -0
  59. package/dist/chunk-NPUC3V37.js.map +1 -0
  60. package/dist/chunk-OEVVNPN3.js +201 -0
  61. package/dist/chunk-OEVVNPN3.js.map +1 -0
  62. package/dist/chunk-PF3R2BXM.js +200 -0
  63. package/dist/chunk-PF3R2BXM.js.map +1 -0
  64. package/dist/chunk-Q2I5YODW.js +2 -0
  65. package/dist/chunk-Q2I5YODW.js.map +1 -0
  66. package/dist/chunk-QDWWP3N7.js +247 -0
  67. package/dist/chunk-QDWWP3N7.js.map +1 -0
  68. package/dist/chunk-QU33KPMP.js +2 -0
  69. package/dist/chunk-QU33KPMP.js.map +1 -0
  70. package/dist/chunk-RK4BUMHS.js +2 -0
  71. package/dist/chunk-RK4BUMHS.js.map +1 -0
  72. package/dist/chunk-RNSLG64F.js +26 -0
  73. package/dist/chunk-RNSLG64F.js.map +1 -0
  74. package/dist/chunk-RV673TB4.js +68 -0
  75. package/dist/chunk-RV673TB4.js.map +1 -0
  76. package/dist/chunk-S7N2WMVT.js +180 -0
  77. package/dist/chunk-S7N2WMVT.js.map +1 -0
  78. package/dist/chunk-T3Z6TCDD.js +2 -0
  79. package/dist/chunk-T3Z6TCDD.js.map +1 -0
  80. package/dist/chunk-T4TJTVVA.js +105 -0
  81. package/dist/chunk-T4TJTVVA.js.map +1 -0
  82. package/dist/chunk-UMOYRCDX.js +103 -0
  83. package/dist/chunk-UMOYRCDX.js.map +1 -0
  84. package/dist/chunk-V2REM6UT.js +185 -0
  85. package/dist/chunk-V2REM6UT.js.map +1 -0
  86. package/dist/chunk-VFBRFGID.js +89 -0
  87. package/dist/chunk-VFBRFGID.js.map +1 -0
  88. package/dist/chunk-W76NLFER.js +103 -0
  89. package/dist/chunk-W76NLFER.js.map +1 -0
  90. package/dist/chunk-WNU4IOGL.js +116 -0
  91. package/dist/chunk-WNU4IOGL.js.map +1 -0
  92. package/dist/chunk-WS4Z7ENS.js +494 -0
  93. package/dist/chunk-WS4Z7ENS.js.map +1 -0
  94. package/dist/chunk-XQT7YLJY.js +497 -0
  95. package/dist/chunk-XQT7YLJY.js.map +1 -0
  96. package/dist/chunk-Y44MEKJV.js +21 -0
  97. package/dist/chunk-Y44MEKJV.js.map +1 -0
  98. package/dist/chunk-Z4EPSOB4.js +53 -0
  99. package/dist/chunk-Z4EPSOB4.js.map +1 -0
  100. package/dist/chunk-Z5OPDBDC.js +2 -0
  101. package/dist/chunk-Z5OPDBDC.js.map +1 -0
  102. package/dist/index.d.ts +1241 -46
  103. package/dist/index.js +189 -1471
  104. package/dist/index.js.map +1 -1
  105. package/dist/lib/arc-math.js +8 -0
  106. package/dist/lib/arc-math.js.map +1 -0
  107. package/dist/lib/map-position.js +11 -0
  108. package/dist/lib/map-position.js.map +1 -0
  109. package/dist/lib/merge-hover-paint.js +10 -0
  110. package/dist/lib/merge-hover-paint.js.map +1 -0
  111. package/dist/lib/plan-crs.js +14 -0
  112. package/dist/lib/plan-crs.js.map +1 -0
  113. package/dist/lib/plan-patterns.js +14 -0
  114. package/dist/lib/plan-patterns.js.map +1 -0
  115. package/dist/lib/plan-regions.js +18 -0
  116. package/dist/lib/plan-regions.js.map +1 -0
  117. package/dist/lib/plan-status.js +14 -0
  118. package/dist/lib/plan-status.js.map +1 -0
  119. package/dist/lib/route-arrow.js +12 -0
  120. package/dist/lib/route-arrow.js.map +1 -0
  121. package/dist/lib/use-map-breakpoint.js +33 -0
  122. package/dist/lib/use-map-breakpoint.js.map +1 -0
  123. package/dist/lib/use-plan-projection.js +9 -0
  124. package/dist/lib/use-plan-projection.js.map +1 -0
  125. package/dist/lib/use-token-color.js +10 -0
  126. package/dist/lib/use-token-color.js.map +1 -0
  127. package/dist/lib/warn-once.js +10 -0
  128. package/dist/lib/warn-once.js.map +1 -0
  129. package/dist/map-annotation/anchor.js +14 -0
  130. package/dist/map-annotation/anchor.js.map +1 -0
  131. package/dist/map-annotation/index.js +21 -0
  132. package/dist/map-annotation/index.js.map +1 -0
  133. package/dist/map-annotation/map-annotation.js +18 -0
  134. package/dist/map-annotation/map-annotation.js.map +1 -0
  135. package/dist/map-arc/index.js +16 -0
  136. package/dist/map-arc/index.js.map +1 -0
  137. package/dist/map-arc/map-arc.js +13 -0
  138. package/dist/map-arc/map-arc.js.map +1 -0
  139. package/dist/map-canvas/index.css +11 -0
  140. package/dist/map-canvas/index.css.map +1 -0
  141. package/dist/map-canvas/index.js +20 -0
  142. package/dist/map-canvas/index.js.map +1 -0
  143. package/dist/map-canvas/map-canvas.css +11 -0
  144. package/dist/map-canvas/map-canvas.css.map +1 -0
  145. package/dist/map-canvas/map-canvas.js +17 -0
  146. package/dist/map-canvas/map-canvas.js.map +1 -0
  147. package/dist/map-canvas/map-context.js +11 -0
  148. package/dist/map-canvas/map-context.js.map +1 -0
  149. package/dist/map-canvas/use-resolved-basemap-theme.js +9 -0
  150. package/dist/map-canvas/use-resolved-basemap-theme.js.map +1 -0
  151. package/dist/map-cluster-layer/index.js +12 -0
  152. package/dist/map-cluster-layer/index.js.map +1 -0
  153. package/dist/map-cluster-layer/map-cluster-layer.js +12 -0
  154. package/dist/map-cluster-layer/map-cluster-layer.js.map +1 -0
  155. package/dist/map-controls/index.js +11 -0
  156. package/dist/map-controls/index.js.map +1 -0
  157. package/dist/map-controls/map-controls.js +11 -0
  158. package/dist/map-controls/map-controls.js.map +1 -0
  159. package/dist/map-geojson/index.js +13 -0
  160. package/dist/map-geojson/index.js.map +1 -0
  161. package/dist/map-geojson/map-geojson.js +13 -0
  162. package/dist/map-geojson/map-geojson.js.map +1 -0
  163. package/dist/map-inset/index.css +11 -0
  164. package/dist/map-inset/index.css.map +1 -0
  165. package/dist/map-inset/index.js +20 -0
  166. package/dist/map-inset/index.js.map +1 -0
  167. package/dist/map-inset/map-inset.css +11 -0
  168. package/dist/map-inset/map-inset.css.map +1 -0
  169. package/dist/map-inset/map-inset.js +20 -0
  170. package/dist/map-inset/map-inset.js.map +1 -0
  171. package/dist/map-legend/index.js +14 -0
  172. package/dist/map-legend/index.js.map +1 -0
  173. package/dist/map-legend/map-corner.js +10 -0
  174. package/dist/map-legend/map-corner.js.map +1 -0
  175. package/dist/map-legend/map-legend.js +11 -0
  176. package/dist/map-legend/map-legend.js.map +1 -0
  177. package/dist/map-marker/index.js +22 -0
  178. package/dist/map-marker/index.js.map +1 -0
  179. package/dist/map-marker/map-marker.js +22 -0
  180. package/dist/map-marker/map-marker.js.map +1 -0
  181. package/dist/map-north-arrow/index.js +11 -0
  182. package/dist/map-north-arrow/index.js.map +1 -0
  183. package/dist/map-north-arrow/map-north-arrow.js +11 -0
  184. package/dist/map-north-arrow/map-north-arrow.js.map +1 -0
  185. package/dist/map-plan/index.js +30 -0
  186. package/dist/map-plan/index.js.map +1 -0
  187. package/dist/map-plan/map-plan-legend.js +10 -0
  188. package/dist/map-plan/map-plan-legend.js.map +1 -0
  189. package/dist/map-plan/map-plan-overlay.js +13 -0
  190. package/dist/map-plan/map-plan-overlay.js.map +1 -0
  191. package/dist/map-plan/map-plan-status.js +9 -0
  192. package/dist/map-plan/map-plan-status.js.map +1 -0
  193. package/dist/map-plan/map-plan-table.js +10 -0
  194. package/dist/map-plan/map-plan-table.js.map +1 -0
  195. package/dist/map-plan/use-plan-selection.js +9 -0
  196. package/dist/map-plan/use-plan-selection.js.map +1 -0
  197. package/dist/map-plan-image/index.js +11 -0
  198. package/dist/map-plan-image/index.js.map +1 -0
  199. package/dist/map-plan-image/map-plan-image.js +11 -0
  200. package/dist/map-plan-image/map-plan-image.js.map +1 -0
  201. package/dist/map-popup/index.js +12 -0
  202. package/dist/map-popup/index.js.map +1 -0
  203. package/dist/map-popup/map-popup.js +12 -0
  204. package/dist/map-popup/map-popup.js.map +1 -0
  205. package/dist/map-route/index.js +12 -0
  206. package/dist/map-route/index.js.map +1 -0
  207. package/dist/map-route/map-route.js +12 -0
  208. package/dist/map-route/map-route.js.map +1 -0
  209. package/dist/map-scale-bar/index.js +18 -0
  210. package/dist/map-scale-bar/index.js.map +1 -0
  211. package/dist/map-scale-bar/map-scale-bar.js +18 -0
  212. package/dist/map-scale-bar/map-scale-bar.js.map +1 -0
  213. package/package.json +5 -5
  214. package/src/index.ts +83 -0
  215. package/src/lib/arc-math.test.ts +19 -0
  216. package/src/lib/arc-math.ts +6 -1
  217. package/src/lib/map-position.ts +77 -0
  218. package/src/lib/merge-hover-paint.ts +44 -13
  219. package/src/lib/plan-crs.parity.test.ts +39 -0
  220. package/src/lib/plan-crs.test.ts +248 -0
  221. package/src/lib/plan-crs.ts +252 -0
  222. package/src/lib/plan-patterns.test.ts +255 -0
  223. package/src/lib/plan-patterns.ts +160 -0
  224. package/src/lib/plan-regions.test.ts +186 -0
  225. package/src/lib/plan-regions.ts +209 -0
  226. package/src/lib/plan-status.test.ts +87 -0
  227. package/src/lib/plan-status.ts +147 -0
  228. package/src/lib/route-arrow.test.ts +88 -0
  229. package/src/lib/route-arrow.ts +59 -0
  230. package/src/lib/use-map-breakpoint.test.ts +83 -0
  231. package/src/lib/use-map-breakpoint.ts +238 -0
  232. package/src/lib/use-plan-projection.ts +157 -0
  233. package/src/lib/warn-once.ts +21 -0
  234. package/src/map-annotation/anchor.ts +92 -0
  235. package/src/map-annotation/index.ts +15 -0
  236. package/src/map-annotation/map-annotation.stories.tsx +113 -0
  237. package/src/map-annotation/map-annotation.test.tsx +106 -0
  238. package/src/map-annotation/map-annotation.tsx +356 -0
  239. package/src/map-arc/map-arc.tsx +26 -5
  240. package/src/map-canvas/map-canvas.stories.tsx +211 -0
  241. package/src/map-canvas/map-canvas.test.tsx +389 -0
  242. package/src/map-canvas/map-canvas.tsx +457 -17
  243. package/src/map-canvas/map-context.ts +11 -0
  244. package/src/map-cluster-layer/map-cluster-layer.tsx +14 -1
  245. package/src/map-controls/map-controls.stories.tsx +16 -0
  246. package/src/map-controls/map-controls.test.tsx +145 -1
  247. package/src/map-controls/map-controls.tsx +57 -12
  248. package/src/map-geojson/map-geojson.stories.tsx +153 -3
  249. package/src/map-geojson/map-geojson.test.tsx +453 -0
  250. package/src/map-geojson/map-geojson.tsx +591 -51
  251. package/src/map-inset/index.ts +1 -0
  252. package/src/map-inset/map-inset.stories.tsx +61 -0
  253. package/src/map-inset/map-inset.test.tsx +117 -0
  254. package/src/map-inset/map-inset.tsx +264 -0
  255. package/src/map-legend/index.ts +10 -0
  256. package/src/map-legend/map-corner.ts +23 -0
  257. package/src/map-legend/map-legend.stories.tsx +171 -0
  258. package/src/map-legend/map-legend.test.tsx +136 -0
  259. package/src/map-legend/map-legend.tsx +264 -0
  260. package/src/map-marker/map-marker.stories.tsx +136 -4
  261. package/src/map-marker/map-marker.test.tsx +328 -3
  262. package/src/map-marker/map-marker.tsx +370 -33
  263. package/src/map-north-arrow/index.ts +1 -0
  264. package/src/map-north-arrow/map-north-arrow.stories.tsx +65 -0
  265. package/src/map-north-arrow/map-north-arrow.test.tsx +39 -0
  266. package/src/map-north-arrow/map-north-arrow.tsx +82 -0
  267. package/src/map-plan/index.ts +13 -0
  268. package/src/map-plan/map-plan-legend.test.tsx +61 -0
  269. package/src/map-plan/map-plan-legend.tsx +83 -0
  270. package/src/map-plan/map-plan-overlay.stories.tsx +260 -0
  271. package/src/map-plan/map-plan-overlay.test.tsx +302 -0
  272. package/src/map-plan/map-plan-overlay.tsx +531 -0
  273. package/src/map-plan/map-plan-status.test.tsx +71 -0
  274. package/src/map-plan/map-plan-status.tsx +77 -0
  275. package/src/map-plan/map-plan-table.test.tsx +86 -0
  276. package/src/map-plan/map-plan-table.tsx +153 -0
  277. package/src/map-plan/use-plan-selection.ts +78 -0
  278. package/src/map-plan-image/index.ts +1 -0
  279. package/src/map-plan-image/map-plan-image.stories.tsx +301 -0
  280. package/src/map-plan-image/map-plan-image.test.tsx +176 -0
  281. package/src/map-plan-image/map-plan-image.tsx +279 -0
  282. package/src/map-popup/map-popup.tsx +15 -10
  283. package/src/map-route/index.ts +1 -1
  284. package/src/map-route/map-route.test.tsx +216 -0
  285. package/src/map-route/map-route.tsx +105 -6
  286. package/src/map-scale-bar/index.ts +9 -0
  287. package/src/map-scale-bar/map-scale-bar.stories.tsx +63 -0
  288. package/src/map-scale-bar/map-scale-bar.test.tsx +65 -0
  289. package/src/map-scale-bar/map-scale-bar.tsx +145 -0
  290. package/src/test-utils/locator-fixture.ts +75 -0
  291. package/src/test-utils/maplibre-mock.ts +256 -19
@@ -0,0 +1,485 @@
1
+ "use client";
2
+ import {
3
+ resolveMapResponsive,
4
+ useMapBreakpoint
5
+ } from "./chunk-W76NLFER.js";
6
+ import {
7
+ useMap
8
+ } from "./chunk-JYFERXZT.js";
9
+ import {
10
+ clampShift,
11
+ mapAnchorGeometry,
12
+ mapAnchorTransform
13
+ } from "./chunk-4DANBD5B.js";
14
+ import {
15
+ resolveMapPosition,
16
+ toPlanPoint
17
+ } from "./chunk-2FPB53NG.js";
18
+
19
+ // src/map-marker/map-marker.tsx
20
+ import MapLibreGL from "maplibre-gl";
21
+ import {
22
+ createContext,
23
+ use,
24
+ useEffect,
25
+ useLayoutEffect,
26
+ useMemo,
27
+ useRef,
28
+ useState
29
+ } from "react";
30
+ import { createPortal } from "react-dom";
31
+ import { X } from "lucide-react";
32
+ import { cn } from "@elabs-ai/components-ui/lib/cn";
33
+ import { useLocale } from "@elabs-ai/components-ui";
34
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
35
+ var MarkerContext = createContext(null);
36
+ function useMarkerContext() {
37
+ const context = use(MarkerContext);
38
+ if (!context) {
39
+ throw new Error("MapMarker sub-components must be used within <MapMarker>");
40
+ }
41
+ return context;
42
+ }
43
+ function MapMarker({
44
+ longitude: longitudeProp,
45
+ latitude: latitudeProp,
46
+ x,
47
+ y,
48
+ children,
49
+ onClick,
50
+ onMouseEnter,
51
+ onMouseLeave,
52
+ onDragStart,
53
+ onDrag,
54
+ onDragEnd,
55
+ draggable = false,
56
+ dragLabel,
57
+ showAt = true,
58
+ label,
59
+ ...markerOptions
60
+ }) {
61
+ const { map, plan } = useMap();
62
+ const { t } = useLocale();
63
+ const visible = resolveMapResponsive(showAt, useMapBreakpoint());
64
+ const [marker, setMarker] = useState(null);
65
+ const [longitude, latitude] = resolveMapPosition(
66
+ { longitude: longitudeProp, latitude: latitudeProp, x, y },
67
+ plan,
68
+ "MapMarker"
69
+ );
70
+ const planRef = useRef(plan);
71
+ planRef.current = plan;
72
+ const callbacksRef = useRef({
73
+ onClick,
74
+ onMouseEnter,
75
+ onMouseLeave,
76
+ onDragStart,
77
+ onDrag,
78
+ onDragEnd
79
+ });
80
+ callbacksRef.current = {
81
+ onClick,
82
+ onMouseEnter,
83
+ onMouseLeave,
84
+ onDragStart,
85
+ onDrag,
86
+ onDragEnd
87
+ };
88
+ const { anchor, className, offset, rotation, rotationAlignment, pitchAlignment } = markerOptions;
89
+ const initialRef = useRef({ longitude, latitude, draggable });
90
+ initialRef.current = { longitude, latitude, draggable };
91
+ useEffect(() => {
92
+ const markerInstance = new MapLibreGL.Marker({
93
+ anchor,
94
+ className,
95
+ element: document.createElement("div"),
96
+ draggable: initialRef.current.draggable
97
+ }).setLngLat([initialRef.current.longitude, initialRef.current.latitude]);
98
+ markerInstance.getElement()?.classList.add("focus-ring");
99
+ const handleClick = (e) => callbacksRef.current.onClick?.(e);
100
+ const handleMouseEnter = (e) => callbacksRef.current.onMouseEnter?.(e);
101
+ const handleMouseLeave = (e) => callbacksRef.current.onMouseLeave?.(e);
102
+ markerInstance.getElement()?.addEventListener("click", handleClick);
103
+ markerInstance.getElement()?.addEventListener("mouseenter", handleMouseEnter);
104
+ markerInstance.getElement()?.addEventListener("mouseleave", handleMouseLeave);
105
+ const handleDragStart = () => {
106
+ const lngLat = markerInstance.getLngLat();
107
+ callbacksRef.current.onDragStart?.(
108
+ { lng: lngLat.lng, lat: lngLat.lat },
109
+ toPlanPoint(planRef.current, lngLat)
110
+ );
111
+ };
112
+ const handleDrag = () => {
113
+ const lngLat = markerInstance.getLngLat();
114
+ callbacksRef.current.onDrag?.(
115
+ { lng: lngLat.lng, lat: lngLat.lat },
116
+ toPlanPoint(planRef.current, lngLat)
117
+ );
118
+ };
119
+ const handleDragEnd = () => {
120
+ const lngLat = markerInstance.getLngLat();
121
+ callbacksRef.current.onDragEnd?.(
122
+ { lng: lngLat.lng, lat: lngLat.lat },
123
+ toPlanPoint(planRef.current, lngLat)
124
+ );
125
+ };
126
+ markerInstance.on("dragstart", handleDragStart);
127
+ markerInstance.on("drag", handleDrag);
128
+ markerInstance.on("dragend", handleDragEnd);
129
+ setMarker(markerInstance);
130
+ }, [anchor, className]);
131
+ useEffect(() => {
132
+ if (!map || !marker || !visible) return;
133
+ marker.addTo(map);
134
+ return () => {
135
+ marker.remove();
136
+ };
137
+ }, [map, marker, visible]);
138
+ useEffect(() => {
139
+ if (!marker) return;
140
+ const current = marker.getLngLat();
141
+ if (current.lng !== longitude || current.lat !== latitude) {
142
+ marker.setLngLat([longitude, latitude]);
143
+ }
144
+ if (marker.isDraggable() !== draggable) {
145
+ marker.setDraggable(draggable);
146
+ }
147
+ const currentOffset = marker.getOffset();
148
+ const newOffset = offset ?? [0, 0];
149
+ const [newOffsetX, newOffsetY] = Array.isArray(newOffset) ? newOffset : [newOffset.x, newOffset.y];
150
+ if (currentOffset.x !== newOffsetX || currentOffset.y !== newOffsetY) {
151
+ marker.setOffset(newOffset);
152
+ }
153
+ if (marker.getRotation() !== (rotation ?? 0)) {
154
+ marker.setRotation(rotation ?? 0);
155
+ }
156
+ if (marker.getRotationAlignment() !== (rotationAlignment ?? "auto")) {
157
+ marker.setRotationAlignment(rotationAlignment ?? "auto");
158
+ }
159
+ if (marker.getPitchAlignment() !== (pitchAlignment ?? "auto")) {
160
+ marker.setPitchAlignment(pitchAlignment ?? "auto");
161
+ }
162
+ }, [marker, longitude, latitude, draggable, offset, rotation, rotationAlignment, pitchAlignment]);
163
+ useEffect(() => {
164
+ const element = marker?.getElement();
165
+ if (!(draggable && marker && map && element)) return;
166
+ const hadTabIndex = element.getAttribute("tabindex");
167
+ const hadRole = element.getAttribute("role");
168
+ const hadLabel = element.getAttribute("aria-label");
169
+ if (hadTabIndex === null) element.setAttribute("tabindex", "0");
170
+ if (hadRole === null) element.setAttribute("role", "button");
171
+ element.setAttribute("aria-label", dragLabel ?? t("maps.marker.drag"));
172
+ let dragging = false;
173
+ const position = () => {
174
+ const lngLat = marker.getLngLat();
175
+ return { lng: lngLat.lng, lat: lngLat.lat };
176
+ };
177
+ const planPoint = () => toPlanPoint(planRef.current, marker.getLngLat());
178
+ const settle = () => {
179
+ if (!dragging) return;
180
+ dragging = false;
181
+ callbacksRef.current.onDragEnd?.(position(), planPoint());
182
+ };
183
+ const handleKeyDown = (event) => {
184
+ const step = event.shiftKey ? KEYBOARD_DRAG_STEP_COARSE_PX : KEYBOARD_DRAG_STEP_PX;
185
+ const delta = KEYBOARD_DRAG_DELTAS[event.key];
186
+ if (!delta) return;
187
+ event.preventDefault();
188
+ event.stopPropagation();
189
+ if (!dragging) {
190
+ dragging = true;
191
+ callbacksRef.current.onDragStart?.(position(), planPoint());
192
+ }
193
+ const point = map.project(marker.getLngLat());
194
+ const next = map.unproject([point.x + delta[0] * step, point.y + delta[1] * step]);
195
+ marker.setLngLat(next);
196
+ callbacksRef.current.onDrag?.(position(), planPoint());
197
+ };
198
+ const handleKeyUp = (event) => {
199
+ if (KEYBOARD_DRAG_DELTAS[event.key]) settle();
200
+ };
201
+ element.addEventListener("keydown", handleKeyDown);
202
+ element.addEventListener("keyup", handleKeyUp);
203
+ element.addEventListener("blur", settle);
204
+ return () => {
205
+ element.removeEventListener("keydown", handleKeyDown);
206
+ element.removeEventListener("keyup", handleKeyUp);
207
+ element.removeEventListener("blur", settle);
208
+ if (hadTabIndex === null) element.removeAttribute("tabindex");
209
+ if (hadRole === null) element.removeAttribute("role");
210
+ if (hadLabel === null) element.removeAttribute("aria-label");
211
+ else element.setAttribute("aria-label", hadLabel);
212
+ };
213
+ }, [draggable, dragLabel, map, marker, t]);
214
+ if (!marker) {
215
+ return null;
216
+ }
217
+ return /* @__PURE__ */ jsxs(MarkerContext.Provider, { value: { marker, map }, children: [
218
+ children,
219
+ label && /* @__PURE__ */ jsx(MarkerOwnLabel, { label })
220
+ ] });
221
+ }
222
+ var KEYBOARD_DRAG_STEP_PX = 8;
223
+ var KEYBOARD_DRAG_STEP_COARSE_PX = 40;
224
+ var KEYBOARD_DRAG_DELTAS = {
225
+ ArrowLeft: [-1, 0],
226
+ ArrowRight: [1, 0],
227
+ ArrowUp: [0, -1],
228
+ ArrowDown: [0, 1]
229
+ };
230
+ var LABEL_GAP = 10;
231
+ var CALLOUT_DISTANCE = 28;
232
+ var LABEL_EDGE_PAD = 4;
233
+ function useLabelInsideMap(element, map, marker, onShift) {
234
+ const onShiftRef = useRef(onShift);
235
+ onShiftRef.current = onShift;
236
+ useLayoutEffect(() => {
237
+ if (!element || !map) return void 0;
238
+ const container = map.getContainer();
239
+ const update = () => {
240
+ const frame = container.getBoundingClientRect();
241
+ if (frame.width === 0 || frame.height === 0) return;
242
+ const point = map.project(marker.getLngLat());
243
+ const inside = point.x >= 0 && point.y >= 0 && point.x <= frame.width && point.y <= frame.height;
244
+ element.style.visibility = inside ? "" : "hidden";
245
+ if (!inside) return;
246
+ element.style.translate = "";
247
+ const box = element.getBoundingClientRect();
248
+ const { sx, sy } = clampShift(
249
+ box.left - frame.left,
250
+ box.top - frame.top,
251
+ box.width,
252
+ box.height,
253
+ frame.width,
254
+ frame.height,
255
+ LABEL_EDGE_PAD
256
+ );
257
+ element.style.translate = sx === 0 && sy === 0 ? "" : `${sx}px ${sy}px`;
258
+ onShiftRef.current?.(sx, sy);
259
+ };
260
+ update();
261
+ map.on("move", update);
262
+ map.on("resize", update);
263
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => update());
264
+ observer?.observe(element);
265
+ return () => {
266
+ map.off("move", update);
267
+ map.off("resize", update);
268
+ observer?.disconnect();
269
+ };
270
+ }, [element, map, marker]);
271
+ }
272
+ function MarkerOwnLabel({ label }) {
273
+ const { marker, map } = useMarkerContext();
274
+ const { text, position = "top", box = false, callout = false } = label;
275
+ const distance = callout ? CALLOUT_DISTANCE : LABEL_GAP;
276
+ const { dx, dy } = mapAnchorGeometry(position, distance);
277
+ const [labelEl, setLabelEl] = useState(null);
278
+ const lineRef = useRef(null);
279
+ useLabelInsideMap(labelEl, map, marker, (sx, sy) => {
280
+ const line = lineRef.current;
281
+ if (!line) return;
282
+ line.setAttribute("x2", String(dx + sx));
283
+ line.setAttribute("y2", String(dy + sy));
284
+ });
285
+ return createPortal(
286
+ /* @__PURE__ */ jsxs(Fragment, { children: [
287
+ callout && /* @__PURE__ */ jsx(
288
+ "svg",
289
+ {
290
+ "aria-hidden": "true",
291
+ "data-slot": "map-marker-callout",
292
+ className: "pointer-events-none absolute overflow-visible text-foreground",
293
+ style: { left: "50%", top: "50%" },
294
+ width: 1,
295
+ height: 1,
296
+ children: /* @__PURE__ */ jsx("line", { ref: lineRef, x1: 0, y1: 0, x2: dx, y2: dy, stroke: "currentColor", strokeWidth: 1 })
297
+ }
298
+ ),
299
+ /* @__PURE__ */ jsx(
300
+ "div",
301
+ {
302
+ ref: setLabelEl,
303
+ "data-slot": "map-marker-label",
304
+ "data-position": position,
305
+ className: cn(
306
+ "pointer-events-none absolute whitespace-nowrap text-meta font-medium text-foreground",
307
+ box && "rounded-sm bg-background/90 px-1 py-px"
308
+ ),
309
+ style: { left: "50%", top: "50%", transform: mapAnchorTransform(position, distance) },
310
+ children: text
311
+ }
312
+ )
313
+ ] }),
314
+ marker.getElement()
315
+ );
316
+ }
317
+ function MapMarkerContent({ children, className }) {
318
+ const { marker } = useMarkerContext();
319
+ return createPortal(
320
+ /* @__PURE__ */ jsx("div", { className: cn("relative cursor-pointer", className), children: children || /* @__PURE__ */ jsx(DefaultMarkerIcon, {}) }),
321
+ marker.getElement()
322
+ );
323
+ }
324
+ function DefaultMarkerIcon() {
325
+ return /* @__PURE__ */ jsx("div", { className: "relative size-4 rounded-full border-2 border-background bg-primary shadow-sm" });
326
+ }
327
+ function PopupCloseButton({ onClick }) {
328
+ const { t } = useLocale();
329
+ return /* @__PURE__ */ jsx(
330
+ "button",
331
+ {
332
+ type: "button",
333
+ onClick,
334
+ "aria-label": t("maps.popup.close"),
335
+ className: "absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset",
336
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5", "aria-hidden": "true" })
337
+ }
338
+ );
339
+ }
340
+ function MapMarkerPopup({
341
+ children,
342
+ className,
343
+ closeButton = false,
344
+ ...popupOptions
345
+ }) {
346
+ const { marker, map } = useMarkerContext();
347
+ const container = useMemo(() => document.createElement("div"), []);
348
+ const { offset, maxWidth } = popupOptions;
349
+ const popup = useMemo(() => {
350
+ return new MapLibreGL.Popup({
351
+ offset: 16,
352
+ ...popupOptions,
353
+ closeButton: false
354
+ }).setMaxWidth("none").setDOMContent(container);
355
+ }, []);
356
+ useEffect(() => {
357
+ if (!map) return;
358
+ popup.setDOMContent(container);
359
+ marker.setPopup(popup);
360
+ return () => {
361
+ marker.setPopup(null);
362
+ };
363
+ }, [map]);
364
+ useEffect(() => {
365
+ popup.setOffset(offset ?? 16);
366
+ if (maxWidth) {
367
+ popup.setMaxWidth(maxWidth);
368
+ }
369
+ }, [popup, offset, maxWidth]);
370
+ const handleClose = () => popup.remove();
371
+ return createPortal(
372
+ /* @__PURE__ */ jsxs(
373
+ "div",
374
+ {
375
+ className: cn(
376
+ "relative max-w-62 rounded-md bg-popover p-3 text-popover-foreground shadow-ring-md",
377
+ "animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance",
378
+ className
379
+ ),
380
+ children: [
381
+ closeButton && /* @__PURE__ */ jsx(PopupCloseButton, { onClick: handleClose }),
382
+ children
383
+ ]
384
+ }
385
+ ),
386
+ container
387
+ );
388
+ }
389
+ function MapMarkerTooltip({ children, className, ...popupOptions }) {
390
+ const { marker, map } = useMarkerContext();
391
+ const container = useMemo(() => document.createElement("div"), []);
392
+ const { offset, maxWidth } = popupOptions;
393
+ const tooltip = useMemo(() => {
394
+ return new MapLibreGL.Popup({
395
+ offset: 16,
396
+ ...popupOptions,
397
+ closeOnClick: true,
398
+ closeButton: false
399
+ }).setMaxWidth("none");
400
+ }, []);
401
+ useEffect(() => {
402
+ if (!map) return;
403
+ tooltip.setDOMContent(container);
404
+ const show = () => {
405
+ tooltip.setLngLat(marker.getLngLat()).addTo(map);
406
+ };
407
+ const hide = () => tooltip.remove();
408
+ const handleKeyDown = (event) => {
409
+ if (event.key === "Escape") hide();
410
+ };
411
+ const element = marker.getElement();
412
+ const hadTabIndex = element?.getAttribute("tabindex") ?? null;
413
+ if (element && hadTabIndex === null) element.setAttribute("tabindex", "0");
414
+ element?.addEventListener("mouseenter", show);
415
+ element?.addEventListener("mouseleave", hide);
416
+ element?.addEventListener("focus", show);
417
+ element?.addEventListener("blur", hide);
418
+ element?.addEventListener("keydown", handleKeyDown);
419
+ return () => {
420
+ element?.removeEventListener("mouseenter", show);
421
+ element?.removeEventListener("mouseleave", hide);
422
+ element?.removeEventListener("focus", show);
423
+ element?.removeEventListener("blur", hide);
424
+ element?.removeEventListener("keydown", handleKeyDown);
425
+ if (element && hadTabIndex === null) element.removeAttribute("tabindex");
426
+ tooltip.remove();
427
+ };
428
+ }, [map]);
429
+ useEffect(() => {
430
+ tooltip.setOffset(offset ?? 16);
431
+ if (maxWidth) {
432
+ tooltip.setMaxWidth(maxWidth);
433
+ }
434
+ }, [tooltip, offset, maxWidth]);
435
+ return createPortal(
436
+ /* @__PURE__ */ jsx(
437
+ "div",
438
+ {
439
+ className: cn(
440
+ "pointer-events-none rounded-md bg-foreground px-2 py-1 text-meta text-balance text-background shadow-md",
441
+ "animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance",
442
+ className
443
+ ),
444
+ children
445
+ }
446
+ ),
447
+ container
448
+ );
449
+ }
450
+ function MapMarkerLabel({ children, className, position = "top" }) {
451
+ const { marker, map } = useMarkerContext();
452
+ const [labelEl, setLabelEl] = useState(null);
453
+ const positionClasses = {
454
+ top: "bottom-full mb-1",
455
+ bottom: "top-full mt-1"
456
+ };
457
+ useLabelInsideMap(labelEl, map, marker);
458
+ return createPortal(
459
+ /* @__PURE__ */ jsx(
460
+ "div",
461
+ {
462
+ ref: setLabelEl,
463
+ "data-slot": "map-marker-label",
464
+ "data-position": position,
465
+ className: cn(
466
+ "absolute left-1/2 -translate-x-1/2 whitespace-nowrap",
467
+ "text-meta font-medium text-foreground",
468
+ positionClasses[position],
469
+ className
470
+ ),
471
+ children
472
+ }
473
+ ),
474
+ marker.getElement()
475
+ );
476
+ }
477
+
478
+ export {
479
+ MapMarker,
480
+ MapMarkerContent,
481
+ MapMarkerPopup,
482
+ MapMarkerTooltip,
483
+ MapMarkerLabel
484
+ };
485
+ //# sourceMappingURL=chunk-C4VI37TD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/map-marker/map-marker.tsx"],"sourcesContent":["\"use client\";\n\nimport MapLibreGL, { type MarkerOptions, type PopupOptions } from \"maplibre-gl\";\nimport {\n createContext,\n use,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { useLocale } from \"@elabs-ai/components-ui\";\n\nimport { useMap } from \"../map-canvas/map-context\";\nimport {\n type MapResponsive,\n useMapBreakpoint,\n resolveMapResponsive,\n} from \"../lib/use-map-breakpoint\";\nimport {\n type MapLabelAnchor,\n clampShift,\n mapAnchorGeometry,\n mapAnchorTransform,\n} from \"../map-annotation/anchor\";\nimport { resolveMapPosition, toPlanPoint, type MapPosition } from \"../lib/map-position\";\nimport type { PlanPoint } from \"../lib/plan-crs\";\n\ntype MarkerContextValue = {\n marker: MapLibreGL.Marker;\n map: MapLibreGL.Map | null;\n};\n\nconst MarkerContext = createContext<MarkerContextValue | null>(null);\n\nfunction useMarkerContext() {\n const context = use(MarkerContext);\n if (!context) {\n throw new Error(\"MapMarker sub-components must be used within <MapMarker>\");\n }\n return context;\n}\n\n/**\n * A marker's own text label — the locator-map label: one of eight positions,\n * optionally on a box, optionally pushed out on a callout line.\n */\nexport interface MapMarkerLabelSpec {\n /** The label text. */\n text: string;\n /** Where the label sits relative to the point (default `\"top\"`). */\n position?: MapLabelAnchor;\n /** Set the label on a background box so it reads over a busy basemap. */\n box?: boolean;\n /** Push the label further out and draw a callout line back to the point. */\n callout?: boolean;\n}\n\nexport type MapMarkerProps = MapPosition & {\n /**\n * Marker sub-components (MapMarkerContent, MapMarkerPopup, MapMarkerTooltip,\n * MapMarkerLabel). Optional when `label` alone marks the place — an inline\n * area label, for example.\n */\n children?: ReactNode;\n /**\n * Whether the marker shows at a tier (default `true` everywhere):\n * `{ base: true, narrow: false }` drops it from a phone-width map. The\n * The editorial advice applies — duplicate a marker with a shorter label and\n * the opposite `showAt` rather than cramming one label into both.\n */\n showAt?: MapResponsive<boolean>;\n /** A text label drawn with the marker (see {@link MapMarkerLabelSpec}). */\n label?: MapMarkerLabelSpec;\n /** Callback when the marker is clicked. */\n onClick?: (e: MouseEvent) => void;\n /** Callback when the mouse enters the marker. */\n onMouseEnter?: (e: MouseEvent) => void;\n /** Callback when the mouse leaves the marker. */\n onMouseLeave?: (e: MouseEvent) => void;\n /** Callback when a drag starts (requires `draggable`). The plan point is `null` off a plan. */\n onDragStart?: (lngLat: { lng: number; lat: number }, plan: PlanPoint | null) => void;\n /** Callback during a drag (requires `draggable`). The plan point is `null` off a plan. */\n onDrag?: (lngLat: { lng: number; lat: number }, plan: PlanPoint | null) => void;\n /** Callback when a drag ends (requires `draggable`). The plan point is `null` off a plan. */\n onDragEnd?: (lngLat: { lng: number; lat: number }, plan: PlanPoint | null) => void;\n /**\n * The accessible name of a `draggable` marker — say what it moves (\"Depot\n * location\"). Defaults to the locale seam's generic name. Ignored when the\n * marker is not draggable.\n */\n dragLabel?: string;\n} & Omit<MarkerOptions, \"element\">;\n\n/**\n * A marker anchored at a lng/lat — or, on a canvas with a `plan` extent, at a\n * plan `x`/`y`. Compose the pieces you need: `MapMarkerContent` (the visual),\n * `MapMarkerLabel`, `MapMarkerPopup` (opens on click) and `MapMarkerTooltip`\n * (shows on hover).\n */\nexport function MapMarker({\n longitude: longitudeProp,\n latitude: latitudeProp,\n x,\n y,\n children,\n onClick,\n onMouseEnter,\n onMouseLeave,\n onDragStart,\n onDrag,\n onDragEnd,\n draggable = false,\n dragLabel,\n showAt = true,\n label,\n ...markerOptions\n}: MapMarkerProps) {\n const { map, plan } = useMap();\n const { t } = useLocale();\n const visible = resolveMapResponsive(showAt, useMapBreakpoint());\n const [marker, setMarker] = useState<MapLibreGL.Marker | null>(null);\n\n // One position prop, two coordinate systems, two scalars out — so every sync\n // effect below keeps primitive dependencies.\n const [longitude, latitude] = resolveMapPosition(\n { longitude: longitudeProp, latitude: latitudeProp, x, y },\n plan,\n \"MapMarker\",\n );\n const planRef = useRef(plan);\n planRef.current = plan;\n\n const callbacksRef = useRef({\n onClick,\n onMouseEnter,\n onMouseLeave,\n onDragStart,\n onDrag,\n onDragEnd,\n });\n callbacksRef.current = {\n onClick,\n onMouseEnter,\n onMouseLeave,\n onDragStart,\n onDrag,\n onDragEnd,\n };\n\n const { anchor, className, offset, rotation, rotationAlignment, pitchAlignment } = markerOptions;\n\n // `longitude`/`latitude`/`draggable` at construction time only — the sync\n // effect below corrects them on the very next commit, and are read from a\n // ref here so they don't force a rebuild on every position change (see the\n // `anchor`/`className` note below for what SHOULD force one).\n const initialRef = useRef({ longitude, latitude, draggable });\n initialRef.current = { longitude, latitude, draggable };\n\n // Builds (and tears down) the actual MapLibre `Marker` instance. This is a\n // real `useEffect`, never `useMemo`: `useMemo`'s initializer runs during\n // RENDER — including on the server, where `document` doesn't exist — and\n // React offers no guarantee it runs only once (it may discard and recompute\n // a memoized value), which would silently leak marker instances/listeners.\n //\n // `anchor` and `className` have no MapLibre setter (`Marker` bakes both into\n // its constructor), so they are the only options that must REBUILD the\n // instance; every other option (position, draggable, offset, rotation,\n // alignment) has a live setter and is kept in sync by the effect further\n // below without ever recreating the marker.\n useEffect(() => {\n const markerInstance = new MapLibreGL.Marker({\n anchor,\n className,\n element: document.createElement(\"div\"),\n draggable: initialRef.current.draggable,\n }).setLngLat([initialRef.current.longitude, initialRef.current.latitude]);\n\n // c-10: MapLibre gives a marker a tab stop of its own (a popup, a\n // keyboard drag), and a raw element shows the BROWSER's focus ring, which\n // does not follow the theme. The house indicator is a class, so the\n // element wears the same `focus-ring` every other control does.\n markerInstance.getElement()?.classList.add(\"focus-ring\");\n\n const handleClick = (e: MouseEvent) => callbacksRef.current.onClick?.(e);\n const handleMouseEnter = (e: MouseEvent) => callbacksRef.current.onMouseEnter?.(e);\n const handleMouseLeave = (e: MouseEvent) => callbacksRef.current.onMouseLeave?.(e);\n\n markerInstance.getElement()?.addEventListener(\"click\", handleClick);\n markerInstance.getElement()?.addEventListener(\"mouseenter\", handleMouseEnter);\n markerInstance.getElement()?.addEventListener(\"mouseleave\", handleMouseLeave);\n\n const handleDragStart = () => {\n const lngLat = markerInstance.getLngLat();\n callbacksRef.current.onDragStart?.(\n { lng: lngLat.lng, lat: lngLat.lat },\n toPlanPoint(planRef.current, lngLat),\n );\n };\n const handleDrag = () => {\n const lngLat = markerInstance.getLngLat();\n callbacksRef.current.onDrag?.(\n { lng: lngLat.lng, lat: lngLat.lat },\n toPlanPoint(planRef.current, lngLat),\n );\n };\n const handleDragEnd = () => {\n const lngLat = markerInstance.getLngLat();\n callbacksRef.current.onDragEnd?.(\n { lng: lngLat.lng, lat: lngLat.lat },\n toPlanPoint(planRef.current, lngLat),\n );\n };\n\n markerInstance.on(\"dragstart\", handleDragStart);\n markerInstance.on(\"drag\", handleDrag);\n markerInstance.on(\"dragend\", handleDragEnd);\n\n setMarker(markerInstance);\n }, [anchor, className]);\n\n // Attaches/detaches the CURRENT marker instance to the map — its own effect\n // so both a `map` change and an `anchor`/`className`-driven rebuild above\n // are handled the same way, without a double `remove()`.\n // A marker hidden at this tier (`showAt`) is simply not on the map.\n useEffect(() => {\n if (!map || !marker || !visible) return;\n\n marker.addTo(map);\n\n return () => {\n marker.remove();\n };\n }, [map, marker, visible]);\n\n useEffect(() => {\n if (!marker) return;\n\n const current = marker.getLngLat();\n if (current.lng !== longitude || current.lat !== latitude) {\n marker.setLngLat([longitude, latitude]);\n }\n\n if (marker.isDraggable() !== draggable) {\n marker.setDraggable(draggable);\n }\n\n const currentOffset = marker.getOffset();\n const newOffset = offset ?? [0, 0];\n const [newOffsetX, newOffsetY] = Array.isArray(newOffset)\n ? newOffset\n : [newOffset.x, newOffset.y];\n if (currentOffset.x !== newOffsetX || currentOffset.y !== newOffsetY) {\n marker.setOffset(newOffset);\n }\n\n if (marker.getRotation() !== (rotation ?? 0)) {\n marker.setRotation(rotation ?? 0);\n }\n if (marker.getRotationAlignment() !== (rotationAlignment ?? \"auto\")) {\n marker.setRotationAlignment(rotationAlignment ?? \"auto\");\n }\n if (marker.getPitchAlignment() !== (pitchAlignment ?? \"auto\")) {\n marker.setPitchAlignment(pitchAlignment ?? \"auto\");\n }\n }, [marker, longitude, latitude, draggable, offset, rotation, rotationAlignment, pitchAlignment]);\n\n // c-4 (WCAG 2.1.1): `marker.setDraggable(true)` buys a MOUSE affordance and\n // nothing else — MapLibre gives a marker element a tab stop and a key\n // handler only from inside `setPopup`, so a popup-less draggable marker is\n // not focusable and answers no key. The drag gets its keyboard equivalent\n // here: a real tab stop, a name that says what moves, and arrow keys that\n // step the marker in PIXELS (Shift = a coarser step) through the same\n // project/unproject round trip the pointer drag uses, firing the identical\n // dragstart/drag/dragend payloads.\n useEffect(() => {\n const element = marker?.getElement();\n if (!(draggable && marker && map && element)) return;\n\n const hadTabIndex = element.getAttribute(\"tabindex\");\n const hadRole = element.getAttribute(\"role\");\n const hadLabel = element.getAttribute(\"aria-label\");\n if (hadTabIndex === null) element.setAttribute(\"tabindex\", \"0\");\n if (hadRole === null) element.setAttribute(\"role\", \"button\");\n // MapLibre's own name is the generic \"Map marker\"; it says neither what\n // this one moves nor that it can be moved at all. A draggable marker is\n // named for its drag, and the original name goes back on cleanup.\n element.setAttribute(\"aria-label\", dragLabel ?? t(\"maps.marker.drag\"));\n\n let dragging = false;\n const position = () => {\n const lngLat = marker.getLngLat();\n return { lng: lngLat.lng, lat: lngLat.lat };\n };\n // The keyboard path reports the plan point exactly as the pointer path\n // does, so a plan consumer cannot tell the two apart.\n const planPoint = () => toPlanPoint(planRef.current, marker.getLngLat());\n const settle = () => {\n if (!dragging) return;\n dragging = false;\n callbacksRef.current.onDragEnd?.(position(), planPoint());\n };\n\n const handleKeyDown = (event: KeyboardEvent) => {\n const step = event.shiftKey ? KEYBOARD_DRAG_STEP_COARSE_PX : KEYBOARD_DRAG_STEP_PX;\n const delta = KEYBOARD_DRAG_DELTAS[event.key];\n if (!delta) return;\n event.preventDefault();\n // MapLibre appends the marker element straight into\n // `map.getCanvasContainer()` — the SAME element its own\n // `HandlerManager` listens on for `keydown` (bubble phase, no\n // `defaultPrevented` check). Without this, every arrow key we handle\n // here also reaches MapLibre's built-in keyboard pan handler, which\n // fires an `easeTo` that moves the CAMERA by 100px the opposite way\n // (its `offset` is negated) and — since that `easeTo` isn't marked\n // `essential` — collapses to an instant jump under reduced motion but\n // animates over 300ms otherwise, so repeated presses compose\n // differently per motion setting. Stopping propagation keeps the key\n // entirely inside this handler: only `marker.setLngLat` moves, the\n // camera never does.\n event.stopPropagation();\n if (!dragging) {\n dragging = true;\n callbacksRef.current.onDragStart?.(position(), planPoint());\n }\n const point = map.project(marker.getLngLat());\n const next = map.unproject([point.x + delta[0] * step, point.y + delta[1] * step]);\n marker.setLngLat(next);\n callbacksRef.current.onDrag?.(position(), planPoint());\n };\n const handleKeyUp = (event: KeyboardEvent) => {\n if (KEYBOARD_DRAG_DELTAS[event.key]) settle();\n };\n\n element.addEventListener(\"keydown\", handleKeyDown);\n element.addEventListener(\"keyup\", handleKeyUp);\n element.addEventListener(\"blur\", settle);\n\n return () => {\n element.removeEventListener(\"keydown\", handleKeyDown);\n element.removeEventListener(\"keyup\", handleKeyUp);\n element.removeEventListener(\"blur\", settle);\n if (hadTabIndex === null) element.removeAttribute(\"tabindex\");\n if (hadRole === null) element.removeAttribute(\"role\");\n if (hadLabel === null) element.removeAttribute(\"aria-label\");\n else element.setAttribute(\"aria-label\", hadLabel);\n };\n }, [draggable, dragLabel, map, marker, t]);\n\n // No marker yet (first client tick after mount, or the moment an\n // `anchor`/`className` rebuild is in flight) — render nothing rather than\n // a sub-component (`MapMarkerContent` et al.) reaching into a null marker,\n // and nothing at all on the server.\n if (!marker) {\n return null;\n }\n\n return (\n <MarkerContext.Provider value={{ marker, map }}>\n {children}\n {label && <MarkerOwnLabel label={label} />}\n </MarkerContext.Provider>\n );\n}\n\n/** One arrow-key press of a keyboard drag, in screen px (c-4). */\nconst KEYBOARD_DRAG_STEP_PX = 8;\n/** The same press with Shift held — a coarser step across the map. */\nconst KEYBOARD_DRAG_STEP_COARSE_PX = 40;\n/** Arrow key → screen-space direction. `y` grows downward, as `map.project` does. */\nconst KEYBOARD_DRAG_DELTAS: Record<string, [number, number] | undefined> = {\n ArrowLeft: [-1, 0],\n ArrowRight: [1, 0],\n ArrowUp: [0, -1],\n ArrowDown: [0, 1],\n};\n\nconst LABEL_GAP = 10;\nconst CALLOUT_DISTANCE = 28;\n/** How close to the map's edge a clamped label may sit, in px. */\nconst LABEL_EDGE_PAD = 4;\n\n/**\n * Keep a marker's label inside the map box (c-7 / c-8).\n *\n * A label is a pure offset from its marker, so at the edge of a static map it\n * was painted half outside the box and clipped — \"bottom-righ\", \"…urg Gate\" —\n * with no way to pan it back into view. This is the pass `MapAnnotation`\n * already makes: measure the painted box, clamp it into the map's rectangle\n * with `clampShift`, and write the correction to the CSS `translate` property,\n * which composes with the label's own `transform` instead of replacing it.\n *\n * A label whose own point has left the box is hidden rather than pinned to the\n * edge: a name parked at the border beside no marker claims a place the reader\n * cannot see. Skipped while the map box measures 0 (jsdom, first paint).\n */\nfunction useLabelInsideMap(\n element: HTMLElement | null,\n map: MapLibreGL.Map | null,\n marker: MapLibreGL.Marker,\n onShift?: (sx: number, sy: number) => void,\n) {\n const onShiftRef = useRef(onShift);\n onShiftRef.current = onShift;\n\n useLayoutEffect(() => {\n if (!element || !map) return undefined;\n const container = map.getContainer();\n\n const update = () => {\n const frame = container.getBoundingClientRect();\n if (frame.width === 0 || frame.height === 0) return;\n\n const point = map.project(marker.getLngLat());\n const inside =\n point.x >= 0 && point.y >= 0 && point.x <= frame.width && point.y <= frame.height;\n element.style.visibility = inside ? \"\" : \"hidden\";\n if (!inside) return;\n\n // Measure the label where it would sit unshifted, then re-apply.\n element.style.translate = \"\";\n const box = element.getBoundingClientRect();\n const { sx, sy } = clampShift(\n box.left - frame.left,\n box.top - frame.top,\n box.width,\n box.height,\n frame.width,\n frame.height,\n LABEL_EDGE_PAD,\n );\n element.style.translate = sx === 0 && sy === 0 ? \"\" : `${sx}px ${sy}px`;\n onShiftRef.current?.(sx, sy);\n };\n\n update();\n map.on(\"move\", update);\n map.on(\"resize\", update);\n const observer =\n typeof ResizeObserver === \"undefined\" ? null : new ResizeObserver(() => update());\n observer?.observe(element);\n return () => {\n map.off(\"move\", update);\n map.off(\"resize\", update);\n observer?.disconnect();\n };\n }, [element, map, marker]);\n}\n\n/** The `label` prop, portaled into the marker element and centred on the point. */\nfunction MarkerOwnLabel({ label }: { label: MapMarkerLabelSpec }) {\n const { marker, map } = useMarkerContext();\n const { text, position = \"top\", box = false, callout = false } = label;\n const distance = callout ? CALLOUT_DISTANCE : LABEL_GAP;\n const { dx, dy } = mapAnchorGeometry(position, distance);\n const [labelEl, setLabelEl] = useState<HTMLDivElement | null>(null);\n const lineRef = useRef<SVGLineElement | null>(null);\n\n // A clamped label pulls its callout line's end along with it, so the line\n // still meets the box it points at.\n useLabelInsideMap(labelEl, map, marker, (sx, sy) => {\n const line = lineRef.current;\n if (!line) return;\n line.setAttribute(\"x2\", String(dx + sx));\n line.setAttribute(\"y2\", String(dy + sy));\n });\n\n return createPortal(\n <>\n {callout && (\n <svg\n aria-hidden=\"true\"\n data-slot=\"map-marker-callout\"\n className=\"pointer-events-none absolute overflow-visible text-foreground\"\n // The marker element is centred on the point; draw from its centre.\n style={{ left: \"50%\", top: \"50%\" }}\n width={1}\n height={1}\n >\n <line ref={lineRef} x1={0} y1={0} x2={dx} y2={dy} stroke=\"currentColor\" strokeWidth={1} />\n </svg>\n )}\n <div\n ref={setLabelEl}\n data-slot=\"map-marker-label\"\n data-position={position}\n className={cn(\n \"pointer-events-none absolute whitespace-nowrap text-meta font-medium text-foreground\",\n box && \"rounded-sm bg-background/90 px-1 py-px\",\n )}\n // Geographic placement: physical offsets from the point in every\n // writing direction (east stays east), like MapLibre's own anchors.\n style={{ left: \"50%\", top: \"50%\", transform: mapAnchorTransform(position, distance) }}\n >\n {text}\n </div>\n </>,\n marker.getElement(),\n );\n}\n\nexport interface MapMarkerContentProps {\n /** Custom marker content. Defaults to a primary-colored dot. */\n children?: ReactNode;\n /** Additional CSS classes for the marker container. */\n className?: string;\n}\n\n/** The marker's visual, portaled into the MapLibre marker element. */\nexport function MapMarkerContent({ children, className }: MapMarkerContentProps) {\n const { marker } = useMarkerContext();\n\n return createPortal(\n <div className={cn(\"relative cursor-pointer\", className)}>\n {children || <DefaultMarkerIcon />}\n </div>,\n marker.getElement(),\n );\n}\n\nfunction DefaultMarkerIcon() {\n return (\n <div className=\"relative size-4 rounded-full border-2 border-background bg-primary shadow-sm\" />\n );\n}\n\nfunction PopupCloseButton({ onClick }: { onClick: () => void }) {\n const { t } = useLocale();\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={t(\"maps.popup.close\")}\n className=\"absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset\"\n >\n <X className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n );\n}\n\nexport type MapMarkerPopupProps = {\n /** Popup content. */\n children: ReactNode;\n /** Additional CSS classes for the popup container. */\n className?: string;\n /** Show a close button in the popup (default: false). */\n closeButton?: boolean;\n} & Omit<PopupOptions, \"className\" | \"closeButton\">;\n\n/** A popup attached to the marker — MapLibre toggles it on marker click. */\nexport function MapMarkerPopup({\n children,\n className,\n closeButton = false,\n ...popupOptions\n}: MapMarkerPopupProps) {\n const { marker, map } = useMarkerContext();\n const container = useMemo(() => document.createElement(\"div\"), []);\n const { offset, maxWidth } = popupOptions;\n\n const popup = useMemo(() => {\n return new MapLibreGL.Popup({\n offset: 16,\n ...popupOptions,\n closeButton: false,\n })\n .setMaxWidth(\"none\")\n .setDOMContent(container);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; options are synced by the effect below\n }, []);\n\n useEffect(() => {\n if (!map) return;\n\n popup.setDOMContent(container);\n marker.setPopup(popup);\n\n return () => {\n marker.setPopup(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- popup/marker/container are stable\n }, [map]);\n\n // Sync popup options when they change.\n useEffect(() => {\n popup.setOffset(offset ?? 16);\n if (maxWidth) {\n popup.setMaxWidth(maxWidth);\n }\n }, [popup, offset, maxWidth]);\n\n const handleClose = () => popup.remove();\n\n return createPortal(\n <div\n className={cn(\n \"relative max-w-62 rounded-md bg-popover p-3 text-popover-foreground shadow-ring-md\",\n \"animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance\",\n className,\n )}\n >\n {closeButton && <PopupCloseButton onClick={handleClose} />}\n {children}\n </div>,\n container,\n );\n}\n\nexport type MapMarkerTooltipProps = {\n /** Tooltip content. */\n children: ReactNode;\n /** Additional CSS classes for the tooltip container. */\n className?: string;\n} & Omit<PopupOptions, \"className\" | \"closeButton\" | \"closeOnClick\">;\n\n/** A hover tooltip attached to the marker. */\nexport function MapMarkerTooltip({ children, className, ...popupOptions }: MapMarkerTooltipProps) {\n const { marker, map } = useMarkerContext();\n const container = useMemo(() => document.createElement(\"div\"), []);\n const { offset, maxWidth } = popupOptions;\n\n const tooltip = useMemo(() => {\n return new MapLibreGL.Popup({\n offset: 16,\n ...popupOptions,\n closeOnClick: true,\n closeButton: false,\n }).setMaxWidth(\"none\");\n // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; options are synced by the effect below\n }, []);\n\n useEffect(() => {\n if (!map) return;\n\n tooltip.setDOMContent(container);\n\n const show = () => {\n tooltip.setLngLat(marker.getLngLat()).addTo(map);\n };\n const hide = () => tooltip.remove();\n // WCAG 1.4.13: a tooltip that only answers a pointer is unreachable, and\n // one that cannot be dismissed without moving the pointer traps the view.\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") hide();\n };\n\n const element = marker.getElement();\n // MapLibre only makes a marker focusable inside `setPopup`, so a\n // tooltip-only marker has to ask for the tab stop itself. The original\n // value is put back on cleanup, so a marker that had one keeps it.\n const hadTabIndex = element?.getAttribute(\"tabindex\") ?? null;\n if (element && hadTabIndex === null) element.setAttribute(\"tabindex\", \"0\");\n\n element?.addEventListener(\"mouseenter\", show);\n element?.addEventListener(\"mouseleave\", hide);\n element?.addEventListener(\"focus\", show);\n element?.addEventListener(\"blur\", hide);\n element?.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n element?.removeEventListener(\"mouseenter\", show);\n element?.removeEventListener(\"mouseleave\", hide);\n element?.removeEventListener(\"focus\", show);\n element?.removeEventListener(\"blur\", hide);\n element?.removeEventListener(\"keydown\", handleKeyDown);\n if (element && hadTabIndex === null) element.removeAttribute(\"tabindex\");\n tooltip.remove();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- tooltip/marker/container are stable\n }, [map]);\n\n // Sync tooltip options when they change.\n useEffect(() => {\n tooltip.setOffset(offset ?? 16);\n if (maxWidth) {\n tooltip.setMaxWidth(maxWidth);\n }\n }, [tooltip, offset, maxWidth]);\n\n return createPortal(\n <div\n className={cn(\n \"pointer-events-none rounded-md bg-foreground px-2 py-1 text-meta text-balance text-background shadow-md\",\n \"animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance\",\n className,\n )}\n >\n {children}\n </div>,\n container,\n );\n}\n\nexport interface MapMarkerLabelProps {\n /** Label text content. */\n children: ReactNode;\n /** Additional CSS classes for the label. */\n className?: string;\n /** Position of the label relative to the marker (default: \"top\"). */\n position?: \"top\" | \"bottom\";\n}\n\n/**\n * A small always-visible text label above or below the marker. Portaled into\n * the marker element so it anchors to the marker whether composed as a sibling\n * of `MapMarkerContent` or nested inside it (a cross-theme sweep caught the\n * sibling composition rendering the label against the map container instead).\n */\nexport function MapMarkerLabel({ children, className, position = \"top\" }: MapMarkerLabelProps) {\n const { marker, map } = useMarkerContext();\n const [labelEl, setLabelEl] = useState<HTMLDivElement | null>(null);\n const positionClasses = {\n top: \"bottom-full mb-1\",\n bottom: \"top-full mt-1\",\n };\n\n // Same edge clamp as the `label` prop's own label (c-7 / c-8): a composed\n // label is a plain offset from the marker too, so at the map's edge it was\n // painted outside the box and clipped.\n useLabelInsideMap(labelEl, map, marker);\n\n return createPortal(\n <div\n ref={setLabelEl}\n data-slot=\"map-marker-label\"\n data-position={position}\n className={cn(\n \"absolute left-1/2 -translate-x-1/2 whitespace-nowrap\",\n \"text-meta font-medium text-foreground\",\n positionClasses[position],\n className,\n )}\n >\n {children}\n </div>,\n marker.getElement(),\n );\n}\n"],"mappings":"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","names":[]}