@swisper.ai/web-map 0.0.2

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 (65) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +134 -0
  3. package/dist/base-map.d.ts +28 -0
  4. package/dist/base-map.d.ts.map +1 -0
  5. package/dist/constants.d.ts +9 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/index.css +2 -0
  8. package/dist/index.d.ts +21 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.es.js +283 -0
  11. package/dist/index.es.js.map +1 -0
  12. package/dist/load-map-lib.d.ts +13 -0
  13. package/dist/load-map-lib.d.ts.map +1 -0
  14. package/dist/map-context.d.ts +14 -0
  15. package/dist/map-context.d.ts.map +1 -0
  16. package/dist/map-marker.d.ts +26 -0
  17. package/dist/map-marker.d.ts.map +1 -0
  18. package/dist/map-pin.d.ts +28 -0
  19. package/dist/map-pin.d.ts.map +1 -0
  20. package/dist/map-user-location.d.ts +11 -0
  21. package/dist/map-user-location.d.ts.map +1 -0
  22. package/dist/map-view.d.ts +30 -0
  23. package/dist/map-view.d.ts.map +1 -0
  24. package/dist/maplibre-worker-source-DgoyX01U.js +4 -0
  25. package/dist/user-location-pin.d.ts +8 -0
  26. package/dist/user-location-pin.d.ts.map +1 -0
  27. package/dist/utils/get-bounds-from-points.d.ts +10 -0
  28. package/dist/utils/get-bounds-from-points.d.ts.map +1 -0
  29. package/dist/worker/maplibre-worker-source.d.ts +2 -0
  30. package/dist/worker/maplibre-worker-source.d.ts.map +1 -0
  31. package/package.json +100 -0
  32. package/src/__storybook__/fixtures.ts +19 -0
  33. package/src/__tests__/load-map-lib.spec.ts +57 -0
  34. package/src/__tests__/map-marker.spec.tsx +153 -0
  35. package/src/__tests__/map-pin.spec.tsx +82 -0
  36. package/src/__tests__/map-user-location.spec.tsx +48 -0
  37. package/src/__tests__/maplibre-worker-source.stub.ts +2 -0
  38. package/src/__tests__/setup.ts +13 -0
  39. package/src/base-map.json +89 -0
  40. package/src/base-map.stories.tsx +123 -0
  41. package/src/base-map.tsx +137 -0
  42. package/src/constants.ts +11 -0
  43. package/src/index.css +1 -0
  44. package/src/index.ts +32 -0
  45. package/src/load-map-lib.ts +25 -0
  46. package/src/map-context.tsx +25 -0
  47. package/src/map-marker.json +84 -0
  48. package/src/map-marker.stories.tsx +228 -0
  49. package/src/map-marker.tsx +97 -0
  50. package/src/map-pin.json +87 -0
  51. package/src/map-pin.stories.tsx +97 -0
  52. package/src/map-pin.tsx +174 -0
  53. package/src/map-user-location.json +70 -0
  54. package/src/map-user-location.stories.tsx +69 -0
  55. package/src/map-user-location.tsx +29 -0
  56. package/src/map-view.json +112 -0
  57. package/src/map-view.stories.tsx +174 -0
  58. package/src/map-view.tsx +86 -0
  59. package/src/user-location-pin.json +58 -0
  60. package/src/user-location-pin.stories.tsx +43 -0
  61. package/src/user-location-pin.tsx +27 -0
  62. package/src/utils/get-bounds-from-points.ts +39 -0
  63. package/src/worker/maplibre-worker-source.ts +2 -0
  64. package/src/worker/virtual-maplibre-worker-source.d.ts +5 -0
  65. package/tailwind.css +9 -0
@@ -0,0 +1,21 @@
1
+ export { MapView } from './map-view';
2
+ export { MapMarker, useMapMarker } from './map-marker';
3
+ export { MapPin } from './map-pin';
4
+ export { MapUserLocation } from './map-user-location';
5
+ export { UserLocationPin } from './user-location-pin';
6
+ export { useMapView } from './map-context';
7
+ export { BaseMap } from './base-map';
8
+ export { loadMapLib } from './load-map-lib';
9
+ export { Layer, Marker, Source, useMap } from '@vis.gl/react-maplibre';
10
+ export { DEFAULT_FIT_BOUNDS_PADDING, DEFAULT_MAP_STYLE_URL, SINGLE_POINT_ZOOM, resolveMapStyleUrl, } from './constants';
11
+ export { getBoundsCentroid, getBoundsFromPoints, isDegenerateBounds, } from './utils/get-bounds-from-points';
12
+ export type { MapViewProps } from './map-view';
13
+ export type { MapMarkerProps, MapMarkerState } from './map-marker';
14
+ export type { MapPinProps, MapPinClassNames } from './map-pin';
15
+ export type { MapUserLocationProps } from './map-user-location';
16
+ export type { UserLocationPinProps } from './user-location-pin';
17
+ export type { MapItemId, MapViewContextValue } from './map-context';
18
+ export type { MapBounds, MapLngLat } from './utils/get-bounds-from-points';
19
+ export type { LayerProps, MapRef, MarkerProps, SourceProps } from '@vis.gl/react-maplibre';
20
+ export type { ExpressionSpecification } from 'maplibre-gl';
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG5C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,gCAAgC,CAAC;AAExC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC/D,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,YAAY,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3E,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC3F,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,283 @@
1
+ import { createContext as e, useContext as t, useEffect as n, useMemo as r, useRef as i } from "react";
2
+ import { Layer as a, Map as o, Marker as s, Marker as c, Source as l, useMap as u } from "@vis.gl/react-maplibre";
3
+ import { ErrorIcon as d } from "@swisper.ai/icons";
4
+ import { Tooltip as f, Typography as p, cn as m } from "@swisper.ai/ui";
5
+ import { cn as h } from "@swisper.ai/web-core";
6
+ import "maplibre-gl/dist/maplibre-gl.css";
7
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
8
+ //#region src/constants.ts
9
+ var v = "https://tiles.openfreemap.org/styles/liberty", y = 48, b = 15;
10
+ function x(e) {
11
+ return e ?? "https://tiles.openfreemap.org/styles/liberty";
12
+ }
13
+ //#endregion
14
+ //#region src/load-map-lib.ts
15
+ var S;
16
+ function C() {
17
+ return S ??= Promise.all([import("maplibre-gl"), import("./maplibre-worker-source-DgoyX01U.js")]).then(([e, { default: t }]) => {
18
+ let n = new Blob([t], { type: "text/javascript" });
19
+ return e.setWorkerUrl(URL.createObjectURL(n)), e;
20
+ }), S;
21
+ }
22
+ //#endregion
23
+ //#region src/utils/get-bounds-from-points.ts
24
+ function w(e) {
25
+ if (e.length === 0) return null;
26
+ let t = e[0][0], n = e[0][1], r = e[0][0], i = e[0][1];
27
+ for (let [a, o] of e) t = Math.min(t, a), n = Math.min(n, o), r = Math.max(r, a), i = Math.max(i, o);
28
+ return [[t, n], [r, i]];
29
+ }
30
+ function T(e) {
31
+ let [[t, n], [r, i]] = e;
32
+ return [(t + r) / 2, (n + i) / 2];
33
+ }
34
+ function E(e) {
35
+ let [[t, n], [r, i]] = e;
36
+ return t === r && n === i;
37
+ }
38
+ //#endregion
39
+ //#region src/base-map.tsx
40
+ function D(e, t, n) {
41
+ if (E(t)) {
42
+ let [[n, r]] = t;
43
+ e.setCenter([n, r]), e.setZoom(15);
44
+ return;
45
+ }
46
+ e.fitBounds(t, {
47
+ padding: n,
48
+ duration: 0
49
+ });
50
+ }
51
+ function O({ children: e, className: t, testId: r, ariaLabel: a, hasError: s, isLoading: c, initialViewState: l, bounds: u = null, fitBoundsPadding: d = 48, mapStyleUrl: f, mapRef: p, onLoad: m, errorLabel: v = "Could not load map. Try again later." }) {
52
+ let y = i(null), b = p ?? y, S = x(f);
53
+ return n(() => {
54
+ if (!u) return;
55
+ let e = b.current?.getMap();
56
+ e && D(e, u, d);
57
+ }, [
58
+ u,
59
+ d,
60
+ b
61
+ ]), /* @__PURE__ */ _("div", {
62
+ "data-testid": r,
63
+ "data-rounded": !0,
64
+ "data-loading": c,
65
+ "aria-invalid": s,
66
+ "data-error": s,
67
+ className: h("relative size-full", "bg-[#f8f4f1]", "data-[error=true]:bg-[#EB0000]/10", "data-[rounded=true]:rounded-lg overflow-hidden", t),
68
+ role: "application",
69
+ "aria-label": a,
70
+ children: [s && /* @__PURE__ */ g(k, { label: v }), /* @__PURE__ */ g(o, {
71
+ ref: b,
72
+ mapLib: C(),
73
+ initialViewState: l,
74
+ mapStyle: S,
75
+ style: {
76
+ width: "100%",
77
+ height: "100%",
78
+ opacity: c || s ? 0 : 1,
79
+ pointerEvents: s ? "none" : "auto"
80
+ },
81
+ attributionControl: !1,
82
+ onLoad: () => {
83
+ if (u) {
84
+ let e = b.current?.getMap();
85
+ e && D(e, u, d);
86
+ }
87
+ m?.();
88
+ },
89
+ children: e
90
+ })]
91
+ });
92
+ }
93
+ function k({ label: e }) {
94
+ return /* @__PURE__ */ g("div", {
95
+ className: "absolute inset-0 flex items-center justify-center",
96
+ children: /* @__PURE__ */ _("div", {
97
+ className: "flex flex-col items-center gap-8",
98
+ children: [/* @__PURE__ */ g(d, { fontSize: "large" }), /* @__PURE__ */ g(p, {
99
+ variant: "body-sm",
100
+ className: "text-text-error",
101
+ children: e
102
+ })]
103
+ })
104
+ });
105
+ }
106
+ //#endregion
107
+ //#region src/map-context.tsx
108
+ var A = e(null);
109
+ function j() {
110
+ let e = t(A);
111
+ if (!e) throw Error("useMapView must be used within a MapView component");
112
+ return e;
113
+ }
114
+ //#endregion
115
+ //#region src/map-view.tsx
116
+ function M({ fitPoints: e, selectedId: t = null, hoveredId: n = null, onSelect: a, onHover: o, flyToSelected: s = !0, children: c, ...l }) {
117
+ let u = i(null), d = r(() => w(e), [e]), f = r(() => d ? {
118
+ center: T(d),
119
+ zoom: 12
120
+ } : {
121
+ center: [0, 0],
122
+ zoom: 2
123
+ }, [d]), p = r(() => ({
124
+ mapRef: u,
125
+ selectedId: t,
126
+ hoveredId: n,
127
+ flyToSelected: s,
128
+ onSelect: a,
129
+ onHover: o
130
+ }), [
131
+ t,
132
+ n,
133
+ s,
134
+ a,
135
+ o
136
+ ]);
137
+ return /* @__PURE__ */ g(A.Provider, {
138
+ value: p,
139
+ children: /* @__PURE__ */ g(O, {
140
+ ...l,
141
+ initialViewState: f,
142
+ bounds: d,
143
+ mapRef: u,
144
+ children: c
145
+ })
146
+ });
147
+ }
148
+ //#endregion
149
+ //#region src/map-marker.tsx
150
+ var N = e(null);
151
+ function P() {
152
+ return t(N);
153
+ }
154
+ function F({ isHovered: e, isSelected: t }) {
155
+ return e ? 3 : t ? 2 : 1;
156
+ }
157
+ function I({ id: e, longitude: t, latitude: i, label: a, anchor: o = "bottom", flyToOnSelect: s, className: l, children: u }) {
158
+ let { mapRef: d, selectedId: f, hoveredId: p, flyToSelected: h, onSelect: _, onHover: v } = j(), y = e === f, b = e === p, x = r(() => ({
159
+ isSelected: y,
160
+ isHovered: b
161
+ }), [y, b]), S = s ?? h;
162
+ return n(() => {
163
+ if (!y || !S) return;
164
+ let e = d.current?.getMap();
165
+ e && e.easeTo({
166
+ center: [t, i],
167
+ duration: 400
168
+ });
169
+ }, [
170
+ y,
171
+ S,
172
+ t,
173
+ i,
174
+ d
175
+ ]), /* @__PURE__ */ g(c, {
176
+ longitude: t,
177
+ latitude: i,
178
+ anchor: o,
179
+ style: { zIndex: F(x) },
180
+ onClick: (t) => {
181
+ t.originalEvent.stopPropagation(), _?.(e);
182
+ },
183
+ children: /* @__PURE__ */ g("button", {
184
+ type: "button",
185
+ "aria-label": a,
186
+ "aria-pressed": y,
187
+ "data-selected": y || void 0,
188
+ "data-hovered": b || void 0,
189
+ onMouseEnter: () => v?.(e),
190
+ onMouseLeave: () => v?.(null),
191
+ className: m("cursor-pointer transition-[filter] duration-150 ease-out", l),
192
+ children: /* @__PURE__ */ g(N.Provider, {
193
+ value: x,
194
+ children: typeof u == "function" ? u(x) : u
195
+ })
196
+ })
197
+ });
198
+ }
199
+ //#endregion
200
+ //#region src/map-pin.tsx
201
+ var L = m("[--map-pin-bg:#252931]", "[--map-pin-bg-selected:#0D212B]", "[--map-pin-overlay-hover:rgba(187,195,204,0.06)]", "[--map-pin-overlay-selected:rgba(187,195,204,0.12)]", "[--map-pin-fg:#BBC3CC]", "[--map-pin-fg-expanded:#E4E8EB]", "[--map-pin-fg-selected:var(--color-primary-main)]", "[--map-pin-divider:rgba(187,195,204,0.6)]", "[--map-pin-divider-selected:color-mix(in_srgb,var(--color-primary-main)_40%,transparent)]");
202
+ function R(e, t) {
203
+ return `linear-gradient(${e}, ${e}), ${t}`;
204
+ }
205
+ function z({ label: e, value: t, isSelected: n, isHovered: r, className: i, classNames: a, "data-testid": o = "map-pin" }) {
206
+ let s = P(), c = n ?? s?.isSelected ?? !1, l = r ?? s?.isHovered ?? !1, u = c || l, d = t != null && t !== "", f = u || !d, h = c ? R("var(--map-pin-overlay-selected)", "var(--map-pin-bg-selected)") : l ? R("var(--map-pin-overlay-hover)", "var(--map-pin-bg)") : "var(--map-pin-bg)", v = c ? "text-[color:var(--map-pin-fg-selected)]" : u ? "text-[color:var(--map-pin-fg-expanded)]" : "text-[color:var(--map-pin-fg)]";
207
+ return /* @__PURE__ */ _("div", {
208
+ "data-testid": o,
209
+ "data-selected": c || void 0,
210
+ "data-hovered": l || void 0,
211
+ className: m(L, "flex flex-col items-center isolate drop-shadow-[0px_1px_2px_rgba(228,232,235,0.3)]", i, a?.root),
212
+ children: [/* @__PURE__ */ g("div", {
213
+ className: m("relative z-2 flex h-[20px] w-full items-center overflow-hidden rounded-[8px] px-4 -mb-1.25", a?.pill),
214
+ style: { background: h },
215
+ children: /* @__PURE__ */ _("div", {
216
+ className: "flex items-center px-4",
217
+ children: [d && /* @__PURE__ */ g(p, {
218
+ variant: "caption",
219
+ className: m("shrink-0 whitespace-nowrap leading-[1.3]", v, a?.value),
220
+ children: t
221
+ }), /* @__PURE__ */ g("div", {
222
+ className: m("grid self-stretch transition-[grid-template-columns] duration-200 ease-out motion-reduce:transition-none", f ? "grid-cols-[1fr]" : "grid-cols-[0fr]"),
223
+ children: /* @__PURE__ */ g("div", {
224
+ className: "flex min-w-0 items-center overflow-hidden",
225
+ children: /* @__PURE__ */ _("div", {
226
+ className: m("flex w-max items-center gap-4", d && "pl-4"),
227
+ children: [d && /* @__PURE__ */ g("span", {
228
+ "aria-hidden": "true",
229
+ className: m("w-px self-stretch shrink-0", c ? "bg-[color:var(--map-pin-divider-selected)]" : "bg-[color:var(--map-pin-divider)]", a?.divider)
230
+ }), /* @__PURE__ */ g(p, {
231
+ variant: "caption",
232
+ className: m("max-w-[84px] truncate whitespace-nowrap leading-[1.3] transition-opacity duration-200 ease-out motion-reduce:transition-none", f ? "opacity-100" : "opacity-0", v, a?.label),
233
+ children: e
234
+ })]
235
+ })
236
+ })
237
+ })]
238
+ })
239
+ }), /* @__PURE__ */ g("div", {
240
+ className: "relative z-1 flex w-full items-center justify-center",
241
+ "aria-hidden": "true",
242
+ children: /* @__PURE__ */ g("svg", {
243
+ width: 8,
244
+ height: 8,
245
+ viewBox: "0 0 8 8",
246
+ fill: "none",
247
+ className: m("size-8 shrink-0", a?.caret),
248
+ children: /* @__PURE__ */ g("path", {
249
+ d: "M4 8L0 0H8L4 8Z",
250
+ fill: c ? "var(--map-pin-bg-selected)" : "var(--map-pin-bg)"
251
+ })
252
+ })
253
+ })]
254
+ });
255
+ }
256
+ //#endregion
257
+ //#region src/user-location-pin.tsx
258
+ function B({ className: e, label: t = "My location" }) {
259
+ return /* @__PURE__ */ g(f, {
260
+ tooltip: t,
261
+ children: /* @__PURE__ */ g("div", {
262
+ "data-testid": "user-location-pin",
263
+ role: "img",
264
+ "aria-label": t,
265
+ className: m("size-3.5 rounded-full bg-blue-500 ring-2 ring-white", "shadow-[0px_1px_3px_rgba(0,0,0,0.35)]", e)
266
+ })
267
+ });
268
+ }
269
+ //#endregion
270
+ //#region src/map-user-location.tsx
271
+ function V({ coordinates: e, children: t, label: n }) {
272
+ return e ? /* @__PURE__ */ g(c, {
273
+ longitude: e.longitude,
274
+ latitude: e.latitude,
275
+ anchor: "center",
276
+ style: { zIndex: 3 },
277
+ children: t ?? /* @__PURE__ */ g(B, { label: n })
278
+ }) : null;
279
+ }
280
+ //#endregion
281
+ export { O as BaseMap, y as DEFAULT_FIT_BOUNDS_PADDING, v as DEFAULT_MAP_STYLE_URL, a as Layer, I as MapMarker, z as MapPin, V as MapUserLocation, M as MapView, s as Marker, b as SINGLE_POINT_ZOOM, l as Source, B as UserLocationPin, T as getBoundsCentroid, w as getBoundsFromPoints, E as isDegenerateBounds, C as loadMapLib, x as resolveMapStyleUrl, u as useMap, P as useMapMarker, j as useMapView };
282
+
283
+ //# sourceMappingURL=index.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.es.js","names":["cn","MapGL","Marker","Marker","Marker$1","cn$1","DEFAULT_MAP_STYLE_URL","DEFAULT_FIT_BOUNDS_PADDING","SINGLE_POINT_ZOOM","resolveMapStyleUrl","mapStyleUrl","mapLib","loadMapLib","lib","workerSource","blob","getBoundsFromPoints","points","minLng","minLat","maxLng","maxLat","lng","lat","getBoundsCentroid","bounds","isDegenerateBounds","fitMapToBounds","map","BaseMap","externalMapRef","internalMapRef","useRef","mapRef","resolvedMapStyleUrl","useEffect","fitBoundsPadding","testId","isLoading","hasError","className","ariaLabel","BaseMapErrorLayer","errorLabel","Map","ErrorIcon","Typography","label","MapViewContext","createContext","useMapView","context","useContext","MapView","baseMapProps","useMemo","fitPoints","initialViewState","value","selectedId","hoveredId","flyToSelected","onSelect","onHover","MapMarkerContext","useMapMarker","getMarkerZIndex","isHovered","isSelected","MapMarker","id","state","shouldFlyTo","flyToOnSelect","longitude","latitude","e","cn","children","PIN_PALETTE","overlay","color","base","MapPin","isSelectedProp","isHoveredProp","dataTestId","marker","isExpanded","hasValue","showLabel","pillBg","textClassName","classNames","UserLocationPin","Tooltip","MapUserLocation","coordinates","Layer","Marker","Source","useMap"],"sources":["../src/constants.ts","../src/load-map-lib.ts","../src/utils/get-bounds-from-points.ts","../src/base-map.tsx","../src/map-context.tsx","../src/map-view.tsx","../src/map-marker.tsx","../src/map-pin.tsx","../src/user-location-pin.tsx","../src/map-user-location.tsx"],"sourcesContent":["export const DEFAULT_MAP_STYLE_URL = 'https://tiles.openfreemap.org/styles/liberty';\nexport const DEFAULT_FIT_BOUNDS_PADDING = 48;\nexport const SINGLE_POINT_ZOOM = 15;\n\n/**\n * No `import.meta.env` fallback: Vite replaces it when this library is built, so a\n * consumer's `VITE_MAP_STYLE_URL` would never be read. Pass `mapStyleUrl` instead.\n */\nexport function resolveMapStyleUrl(mapStyleUrl?: string): string {\n return mapStyleUrl ?? DEFAULT_MAP_STYLE_URL;\n}\n","import type * as maplibre from 'maplibre-gl';\n\ntype MapLib = typeof maplibre;\n\nlet mapLib: Promise<MapLib> | undefined;\n\n/**\n * maplibre-gl, with its worker pointed at a blob URL built from the source this\n * package ships (see `vite-plugin-maplibre-worker.ts`). Pass it to `<Map mapLib>`.\n *\n * Both halves are dynamic imports, so the ~500 kB worker source is fetched with\n * the first map rather than with every import of the package. Memoised: every\n * map shares one worker URL.\n */\nexport function loadMapLib(): Promise<MapLib> {\n mapLib ??= Promise.all([import('maplibre-gl'), import('./worker/maplibre-worker-source')]).then(\n ([lib, { default: workerSource }]) => {\n const blob = new Blob([workerSource], { type: 'text/javascript' });\n lib.setWorkerUrl(URL.createObjectURL(blob));\n return lib;\n },\n );\n\n return mapLib;\n}\n","export type MapLngLat = [number, number];\nexport type MapBounds = [MapLngLat, MapLngLat];\n\n/**\n * Computes a southwest/northeast bounding box from lng/lat points.\n * Returns null when there are no points.\n */\nexport function getBoundsFromPoints(points: MapLngLat[]): MapBounds | null {\n if (points.length === 0) {\n return null;\n }\n\n let minLng = points[0][0];\n let minLat = points[0][1];\n let maxLng = points[0][0];\n let maxLat = points[0][1];\n\n for (const [lng, lat] of points) {\n minLng = Math.min(minLng, lng);\n minLat = Math.min(minLat, lat);\n maxLng = Math.max(maxLng, lng);\n maxLat = Math.max(maxLat, lat);\n }\n\n return [\n [minLng, minLat],\n [maxLng, maxLat],\n ];\n}\n\nexport function getBoundsCentroid(bounds: MapBounds): MapLngLat {\n const [[minLng, minLat], [maxLng, maxLat]] = bounds;\n return [(minLng + maxLng) / 2, (minLat + maxLat) / 2];\n}\n\nexport function isDegenerateBounds(bounds: MapBounds): boolean {\n const [[minLng, minLat], [maxLng, maxLat]] = bounds;\n return minLng === maxLng && minLat === maxLat;\n}\n","import { Map as MapGL } from '@vis.gl/react-maplibre';\nimport type { MapRef } from '@vis.gl/react-maplibre';\nimport type { Map as MapLibreMap } from 'maplibre-gl';\nimport { useEffect, useRef, type ReactNode, type RefObject } from 'react';\n\nimport { ErrorIcon } from '@swisper.ai/icons';\nimport { Typography } from '@swisper.ai/ui';\nimport { cn } from '@swisper.ai/web-core';\n\nimport { DEFAULT_FIT_BOUNDS_PADDING, resolveMapStyleUrl, SINGLE_POINT_ZOOM } from './constants';\nimport { loadMapLib } from './load-map-lib';\nimport { isDegenerateBounds, type MapBounds, type MapLngLat } from './utils/get-bounds-from-points';\n\nimport 'maplibre-gl/dist/maplibre-gl.css';\n\ninterface BaseMapProps {\n children?: ReactNode;\n className?: string;\n testId: string;\n ariaLabel: string;\n hasError?: boolean;\n isLoading?: boolean;\n initialViewState: { center: MapLngLat | null; zoom: number };\n bounds?: MapBounds | null;\n fitBoundsPadding?: number;\n mapStyleUrl?: string;\n mapRef?: RefObject<MapRef>;\n onLoad?: () => void;\n /**\n * Shown when `hasError` is set. A prop rather than a translation lookup, so the\n * package does not tie consumers to one i18n library or namespace.\n */\n errorLabel?: string;\n}\n\nfunction fitMapToBounds(map: MapLibreMap, bounds: MapBounds, padding: number) {\n if (isDegenerateBounds(bounds)) {\n const [[lng, lat]] = bounds;\n map.setCenter([lng, lat]);\n map.setZoom(SINGLE_POINT_ZOOM);\n return;\n }\n\n map.fitBounds(bounds, { padding, duration: 0 });\n}\n\nexport function BaseMap({\n children,\n className,\n testId,\n ariaLabel,\n hasError,\n isLoading,\n initialViewState,\n bounds = null,\n fitBoundsPadding = DEFAULT_FIT_BOUNDS_PADDING,\n mapStyleUrl,\n mapRef: externalMapRef,\n onLoad,\n errorLabel = 'Could not load map. Try again later.',\n}: BaseMapProps) {\n const internalMapRef = useRef<MapRef>(null);\n const mapRef = externalMapRef ?? internalMapRef;\n const resolvedMapStyleUrl = resolveMapStyleUrl(mapStyleUrl);\n\n useEffect(() => {\n if (!bounds) {\n return;\n }\n\n const map = mapRef.current?.getMap();\n if (!map) {\n return;\n }\n\n fitMapToBounds(map, bounds, fitBoundsPadding);\n }, [bounds, fitBoundsPadding, mapRef]);\n\n return (\n <div\n data-testid={testId}\n data-rounded={true}\n data-loading={isLoading}\n aria-invalid={hasError}\n data-error={hasError}\n className={cn(\n 'relative size-full',\n 'bg-[#f8f4f1]',\n 'data-[error=true]:bg-[#EB0000]/10',\n 'data-[rounded=true]:rounded-lg overflow-hidden',\n className,\n )}\n role=\"application\"\n aria-label={ariaLabel}\n >\n {hasError && <BaseMapErrorLayer label={errorLabel} />}\n\n <MapGL\n ref={mapRef}\n mapLib={loadMapLib()}\n initialViewState={initialViewState}\n mapStyle={resolvedMapStyleUrl}\n style={{\n width: '100%',\n height: '100%',\n opacity: isLoading || hasError ? 0 : 1,\n pointerEvents: hasError ? 'none' : 'auto',\n }}\n attributionControl={false}\n onLoad={() => {\n if (bounds) {\n const map = mapRef.current?.getMap();\n if (map) {\n fitMapToBounds(map, bounds, fitBoundsPadding);\n }\n }\n onLoad?.();\n }}\n >\n {children}\n </MapGL>\n </div>\n );\n}\n\nfunction BaseMapErrorLayer({ label }: { label: string }) {\n return (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"flex flex-col items-center gap-8\">\n <ErrorIcon fontSize=\"large\" />\n <Typography variant=\"body-sm\" className=\"text-text-error\">\n {label}\n </Typography>\n </div>\n </div>\n );\n}\n","import type { MapRef } from '@vis.gl/react-maplibre';\nimport { createContext, useContext, type RefObject } from 'react';\n\nexport type MapItemId = string | number;\n\nexport interface MapViewContextValue {\n mapRef: RefObject<MapRef>;\n selectedId: MapItemId | null;\n hoveredId: MapItemId | null;\n flyToSelected: boolean;\n onSelect?: (id: MapItemId) => void;\n onHover?: (id: MapItemId | null) => void;\n}\n\nexport const MapViewContext = createContext<MapViewContextValue | null>(null);\n\nexport function useMapView() {\n const context = useContext(MapViewContext);\n\n if (!context) {\n throw new Error('useMapView must be used within a MapView component');\n }\n\n return context;\n}\n","import type { MapRef } from '@vis.gl/react-maplibre';\nimport { useMemo, useRef, type ReactNode } from 'react';\n\nimport { BaseMap } from './base-map';\nimport { MapViewContext, type MapItemId, type MapViewContextValue } from './map-context';\nimport {\n getBoundsCentroid,\n getBoundsFromPoints,\n type MapLngLat,\n} from './utils/get-bounds-from-points';\n\nexport interface MapViewProps<TId extends MapItemId = MapItemId> {\n /** Points the map frames on load and whenever they change. */\n fitPoints: MapLngLat[];\n selectedId?: TId | null;\n hoveredId?: TId | null;\n onSelect?: (id: TId) => void;\n onHover?: (id: TId | null) => void;\n /** Ease to a marker when it becomes selected, so it is never off screen. */\n flyToSelected?: boolean;\n children?: ReactNode;\n testId: string;\n ariaLabel: string;\n className?: string;\n hasError?: boolean;\n isLoading?: boolean;\n fitBoundsPadding?: number;\n mapStyleUrl?: string;\n onLoad?: () => void;\n /** Shown when `hasError` is set; pass a translated string. */\n errorLabel?: string;\n}\n\n/**\n * Interactive map root: frames `fitPoints` and shares selection / hover state\n * with the `MapMarker`s composed inside it.\n */\nexport function MapView<TId extends MapItemId = MapItemId>({\n fitPoints,\n selectedId = null,\n hoveredId = null,\n onSelect,\n onHover,\n flyToSelected = true,\n children,\n ...baseMapProps\n}: MapViewProps<TId>) {\n const mapRef = useRef<MapRef>(null);\n\n const bounds = useMemo(() => getBoundsFromPoints(fitPoints), [fitPoints]);\n\n const initialViewState = useMemo(() => {\n if (bounds) {\n // With a bounds fit called on load/change we still need an initial\n // center or MapGL defaults to null-island. Use the geographic centroid.\n return { center: getBoundsCentroid(bounds), zoom: 12 };\n }\n return { center: [0, 0] as MapLngLat, zoom: 2 };\n }, [bounds]);\n\n const value = useMemo<MapViewContextValue>(\n () => ({\n mapRef,\n selectedId,\n hoveredId,\n flyToSelected,\n // Markers deal in the widened id type; the caller only ever gets back ids it passed in.\n onSelect: onSelect as MapViewContextValue['onSelect'],\n onHover: onHover as MapViewContextValue['onHover'],\n }),\n [selectedId, hoveredId, flyToSelected, onSelect, onHover],\n );\n\n return (\n <MapViewContext.Provider value={value}>\n <BaseMap\n {...baseMapProps}\n initialViewState={initialViewState}\n bounds={bounds}\n mapRef={mapRef}\n >\n {children}\n </BaseMap>\n </MapViewContext.Provider>\n );\n}\n","import { Marker } from '@vis.gl/react-maplibre';\nimport { createContext, useContext, useEffect, useMemo, type ReactNode } from 'react';\n\nimport { cn } from '@swisper.ai/ui';\n\nimport { useMapView, type MapItemId } from './map-context';\n\nexport interface MapMarkerState {\n isSelected: boolean;\n isHovered: boolean;\n}\n\nconst MapMarkerContext = createContext<MapMarkerState | null>(null);\n\n/** Selection state of the enclosing `MapMarker`, or null outside of one. */\nexport function useMapMarker() {\n return useContext(MapMarkerContext);\n}\n\nexport interface MapMarkerProps {\n id: MapItemId;\n longitude: number;\n latitude: number;\n /** Accessible name of the marker button. */\n label: string;\n anchor?: 'center' | 'top' | 'bottom' | 'left' | 'right';\n /** Ease to this marker when selected; defaults to the `MapView`'s `flyToSelected`. */\n flyToOnSelect?: boolean;\n className?: string;\n children: ReactNode | ((state: MapMarkerState) => ReactNode);\n}\n\nfunction getMarkerZIndex({ isHovered, isSelected }: MapMarkerState) {\n if (isHovered) return 3;\n if (isSelected) return 2;\n return 1;\n}\n\n/**\n * A selectable, hoverable point on a `MapView`. Renders any pin as its child —\n * `MapPin` by default, or a render function receiving the marker state.\n */\nexport function MapMarker({\n id,\n longitude,\n latitude,\n label,\n anchor = 'bottom',\n flyToOnSelect,\n className,\n children,\n}: MapMarkerProps) {\n const { mapRef, selectedId, hoveredId, flyToSelected, onSelect, onHover } = useMapView();\n\n const isSelected = id === selectedId;\n const isHovered = id === hoveredId;\n const state = useMemo(() => ({ isSelected, isHovered }), [isSelected, isHovered]);\n\n // Fly to the selected pin so it's always in view when chosen from outside the map.\n const shouldFlyTo = flyToOnSelect ?? flyToSelected;\n\n useEffect(() => {\n if (!isSelected || !shouldFlyTo) return;\n const map = mapRef.current?.getMap();\n if (!map) return;\n map.easeTo({ center: [longitude, latitude], duration: 400 });\n }, [isSelected, shouldFlyTo, longitude, latitude, mapRef]);\n\n return (\n <Marker\n longitude={longitude}\n latitude={latitude}\n anchor={anchor}\n style={{ zIndex: getMarkerZIndex(state) }}\n onClick={(e) => {\n // Prevent the map's click handler from firing and deselecting.\n e.originalEvent.stopPropagation();\n onSelect?.(id);\n }}\n >\n <button\n type=\"button\"\n aria-label={label}\n aria-pressed={isSelected}\n data-selected={isSelected || undefined}\n data-hovered={isHovered || undefined}\n onMouseEnter={() => onHover?.(id)}\n onMouseLeave={() => onHover?.(null)}\n className={cn('cursor-pointer transition-[filter] duration-150 ease-out', className)}\n >\n <MapMarkerContext.Provider value={state}>\n {typeof children === 'function' ? children(state) : children}\n </MapMarkerContext.Provider>\n </button>\n </Marker>\n );\n}\n","import type { ReactNode } from 'react';\n\nimport { Typography, cn } from '@swisper.ai/ui';\n\nimport { useMapMarker } from './map-marker';\n\n/**\n * Pin palette as CSS custom properties so a caller can restyle it with a class,\n * e.g. `className=\"[--map-pin-bg:#fff] [--map-pin-fg:#000]\"`.\n * Defaults are the Figma PlacePointer values (brand/secondary/700, selected\n * Pill #0D212B + 12% secondary overlay, hover 6% overlay).\n */\nconst PIN_PALETTE = cn(\n '[--map-pin-bg:#252931]',\n '[--map-pin-bg-selected:#0D212B]',\n '[--map-pin-overlay-hover:rgba(187,195,204,0.06)]',\n '[--map-pin-overlay-selected:rgba(187,195,204,0.12)]',\n '[--map-pin-fg:#BBC3CC]',\n '[--map-pin-fg-expanded:#E4E8EB]',\n '[--map-pin-fg-selected:var(--color-primary-main)]',\n '[--map-pin-divider:rgba(187,195,204,0.6)]',\n '[--map-pin-divider-selected:color-mix(in_srgb,var(--color-primary-main)_40%,transparent)]',\n);\n\nfunction overlay(color: string, base: string) {\n return `linear-gradient(${color}, ${color}), ${base}`;\n}\n\nexport interface MapPinClassNames {\n root?: string;\n pill?: string;\n value?: string;\n divider?: string;\n label?: string;\n caret?: string;\n}\n\nexport interface MapPinProps {\n /** Shown on hover / selection, or always when there is no `value`. */\n label: string;\n /** Short lead text always shown in the pill, e.g. a rating or a price. */\n value?: ReactNode;\n /** Defaults to the enclosing `MapMarker`'s state. */\n isSelected?: boolean;\n /** Defaults to the enclosing `MapMarker`'s state. */\n isHovered?: boolean;\n className?: string;\n classNames?: MapPinClassNames;\n 'data-testid'?: string;\n}\n\n/**\n * Map callout pin: `value`-only by default; expands to `value | label` on\n * hover/selection (Figma PlacePointer Default / Hover / selected Pill).\n */\nexport function MapPin({\n label,\n value,\n isSelected: isSelectedProp,\n isHovered: isHoveredProp,\n className,\n classNames,\n 'data-testid': dataTestId = 'map-pin',\n}: MapPinProps) {\n const marker = useMapMarker();\n const isSelected = isSelectedProp ?? marker?.isSelected ?? false;\n const isHovered = isHoveredProp ?? marker?.isHovered ?? false;\n\n const isExpanded = isSelected || isHovered;\n const hasValue = value != null && value !== '';\n const showLabel = isExpanded || !hasValue;\n\n const pillBg = isSelected\n ? overlay('var(--map-pin-overlay-selected)', 'var(--map-pin-bg-selected)')\n : isHovered\n ? overlay('var(--map-pin-overlay-hover)', 'var(--map-pin-bg)')\n : 'var(--map-pin-bg)';\n\n const textClassName = isSelected\n ? 'text-[color:var(--map-pin-fg-selected)]'\n : isExpanded\n ? 'text-[color:var(--map-pin-fg-expanded)]'\n : 'text-[color:var(--map-pin-fg)]';\n\n return (\n <div\n data-testid={dataTestId}\n data-selected={isSelected || undefined}\n data-hovered={isHovered || undefined}\n className={cn(\n PIN_PALETTE,\n 'flex flex-col items-center isolate drop-shadow-[0px_1px_2px_rgba(228,232,235,0.3)]',\n className,\n classNames?.root,\n )}\n >\n <div\n className={cn(\n 'relative z-2 flex h-[20px] w-full items-center overflow-hidden rounded-[8px] px-4 -mb-1.25',\n classNames?.pill,\n )}\n style={{ background: pillBg }}\n >\n <div className=\"flex items-center px-4\">\n {hasValue && (\n <Typography\n variant=\"caption\"\n className={cn(\n 'shrink-0 whitespace-nowrap leading-[1.3]',\n textClassName,\n classNames?.value,\n )}\n >\n {value}\n </Typography>\n )}\n\n {/* 0fr → 1fr animates to the label's intrinsic width; the inner\n `w-max` keeps it from reflowing while the column collapses. */}\n <div\n className={cn(\n 'grid self-stretch transition-[grid-template-columns] duration-200 ease-out motion-reduce:transition-none',\n showLabel ? 'grid-cols-[1fr]' : 'grid-cols-[0fr]',\n )}\n >\n <div className=\"flex min-w-0 items-center overflow-hidden\">\n <div className={cn('flex w-max items-center gap-4', hasValue && 'pl-4')}>\n {hasValue && (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'w-px self-stretch shrink-0',\n isSelected\n ? 'bg-[color:var(--map-pin-divider-selected)]'\n : 'bg-[color:var(--map-pin-divider)]',\n classNames?.divider,\n )}\n />\n )}\n\n <Typography\n variant=\"caption\"\n className={cn(\n 'max-w-[84px] truncate whitespace-nowrap leading-[1.3] transition-opacity duration-200 ease-out motion-reduce:transition-none',\n showLabel ? 'opacity-100' : 'opacity-0',\n textClassName,\n classNames?.label,\n )}\n >\n {label}\n </Typography>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"relative z-1 flex w-full items-center justify-center\" aria-hidden=\"true\">\n <svg\n width={8}\n height={8}\n viewBox=\"0 0 8 8\"\n fill=\"none\"\n className={cn('size-8 shrink-0', classNames?.caret)}\n >\n <path\n d=\"M4 8L0 0H8L4 8Z\"\n fill={isSelected ? 'var(--map-pin-bg-selected)' : 'var(--map-pin-bg)'}\n />\n </svg>\n </div>\n </div>\n );\n}\n","import { Tooltip, cn } from '@swisper.ai/ui';\n\nexport interface UserLocationPinProps {\n className?: string;\n /** Tooltip and accessible name; pass a translated string. */\n label?: string;\n}\n\n/** Map marker for the user's own position: a blue dot with a white ring. */\nexport function UserLocationPin({ className, label = 'My location' }: UserLocationPinProps) {\n return (\n <Tooltip tooltip={label}>\n <div\n data-testid=\"user-location-pin\"\n // The dot is a bare `div`, so without a label it is invisible to\n // screen readers — the tooltip only covers pointer users.\n role=\"img\"\n aria-label={label}\n className={cn(\n 'size-3.5 rounded-full bg-blue-500 ring-2 ring-white',\n 'shadow-[0px_1px_3px_rgba(0,0,0,0.35)]',\n className,\n )}\n />\n </Tooltip>\n );\n}\n","import { Marker } from '@vis.gl/react-maplibre';\nimport type { ReactNode } from 'react';\n\nimport { UserLocationPin } from './user-location-pin';\n\nexport interface MapUserLocationProps {\n coordinates: Pick<GeolocationCoordinates, 'longitude' | 'latitude'> | null | undefined;\n /** Custom marker; defaults to `UserLocationPin`. */\n children?: ReactNode;\n /** Label of the default `UserLocationPin`; ignored when `children` is set. */\n label?: string;\n}\n\n/** The user's own position on a `MapView`; renders nothing until it is known. */\nexport function MapUserLocation({ coordinates, children, label }: MapUserLocationProps) {\n if (!coordinates) return null;\n\n return (\n <Marker\n longitude={coordinates.longitude}\n latitude={coordinates.latitude}\n anchor=\"center\"\n // At the same level as the hovered and selected pins.\n style={{ zIndex: 3 }}\n >\n {children ?? <UserLocationPin label={label} />}\n </Marker>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type * as maplibre from 'maplibre-gl';
2
+ type MapLib = typeof maplibre;
3
+ /**
4
+ * maplibre-gl, with its worker pointed at a blob URL built from the source this
5
+ * package ships (see `vite-plugin-maplibre-worker.ts`). Pass it to `<Map mapLib>`.
6
+ *
7
+ * Both halves are dynamic imports, so the ~500 kB worker source is fetched with
8
+ * the first map rather than with every import of the package. Memoised: every
9
+ * map shares one worker URL.
10
+ */
11
+ export declare function loadMapLib(): Promise<MapLib>;
12
+ export {};
13
+ //# sourceMappingURL=load-map-lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-map-lib.d.ts","sourceRoot":"","sources":["../src/load-map-lib.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,QAAQ,MAAM,aAAa,CAAC;AAE7C,KAAK,MAAM,GAAG,OAAO,QAAQ,CAAC;AAI9B;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAU5C"}
@@ -0,0 +1,14 @@
1
+ import { MapRef } from '@vis.gl/react-maplibre';
2
+ import { RefObject } from 'react';
3
+ export type MapItemId = string | number;
4
+ export interface MapViewContextValue {
5
+ mapRef: RefObject<MapRef>;
6
+ selectedId: MapItemId | null;
7
+ hoveredId: MapItemId | null;
8
+ flyToSelected: boolean;
9
+ onSelect?: (id: MapItemId) => void;
10
+ onHover?: (id: MapItemId | null) => void;
11
+ }
12
+ export declare const MapViewContext: import('react').Context<MapViewContextValue | null>;
13
+ export declare function useMapView(): MapViewContextValue;
14
+ //# sourceMappingURL=map-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-context.d.ts","sourceRoot":"","sources":["../src/map-context.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAElE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAExC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC1B,UAAU,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;CAC1C;AAED,eAAO,MAAM,cAAc,qDAAkD,CAAC;AAE9E,wBAAgB,UAAU,wBAQzB"}
@@ -0,0 +1,26 @@
1
+ import { ReactNode } from 'react';
2
+ import { MapItemId } from './map-context';
3
+ export interface MapMarkerState {
4
+ isSelected: boolean;
5
+ isHovered: boolean;
6
+ }
7
+ /** Selection state of the enclosing `MapMarker`, or null outside of one. */
8
+ export declare function useMapMarker(): MapMarkerState | null;
9
+ export interface MapMarkerProps {
10
+ id: MapItemId;
11
+ longitude: number;
12
+ latitude: number;
13
+ /** Accessible name of the marker button. */
14
+ label: string;
15
+ anchor?: 'center' | 'top' | 'bottom' | 'left' | 'right';
16
+ /** Ease to this marker when selected; defaults to the `MapView`'s `flyToSelected`. */
17
+ flyToOnSelect?: boolean;
18
+ className?: string;
19
+ children: ReactNode | ((state: MapMarkerState) => ReactNode);
20
+ }
21
+ /**
22
+ * A selectable, hoverable point on a `MapView`. Renders any pin as its child —
23
+ * `MapPin` by default, or a render function receiving the marker state.
24
+ */
25
+ export declare function MapMarker({ id, longitude, latitude, label, anchor, flyToOnSelect, className, children, }: MapMarkerProps): import('react').JSX.Element;
26
+ //# sourceMappingURL=map-marker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-marker.d.ts","sourceRoot":"","sources":["../src/map-marker.tsx"],"names":[],"mappings":"AACA,OAAO,EAAiD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAItF,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAID,4EAA4E;AAC5E,wBAAgB,YAAY,0BAE3B;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,SAAS,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACxD,sFAAsF;IACtF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,cAAc,KAAK,SAAS,CAAC,CAAC;CAC9D;AAQD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EACxB,EAAE,EACF,SAAS,EACT,QAAQ,EACR,KAAK,EACL,MAAiB,EACjB,aAAa,EACb,SAAS,EACT,QAAQ,GACT,EAAE,cAAc,+BA6ChB"}
@@ -0,0 +1,28 @@
1
+ import { ReactNode } from 'react';
2
+ export interface MapPinClassNames {
3
+ root?: string;
4
+ pill?: string;
5
+ value?: string;
6
+ divider?: string;
7
+ label?: string;
8
+ caret?: string;
9
+ }
10
+ export interface MapPinProps {
11
+ /** Shown on hover / selection, or always when there is no `value`. */
12
+ label: string;
13
+ /** Short lead text always shown in the pill, e.g. a rating or a price. */
14
+ value?: ReactNode;
15
+ /** Defaults to the enclosing `MapMarker`'s state. */
16
+ isSelected?: boolean;
17
+ /** Defaults to the enclosing `MapMarker`'s state. */
18
+ isHovered?: boolean;
19
+ className?: string;
20
+ classNames?: MapPinClassNames;
21
+ 'data-testid'?: string;
22
+ }
23
+ /**
24
+ * Map callout pin: `value`-only by default; expands to `value | label` on
25
+ * hover/selection (Figma PlacePointer Default / Hover / selected Pill).
26
+ */
27
+ export declare function MapPin({ label, value, isSelected: isSelectedProp, isHovered: isHoveredProp, className, classNames, 'data-testid': dataTestId, }: MapPinProps): import('react').JSX.Element;
28
+ //# sourceMappingURL=map-pin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-pin.d.ts","sourceRoot":"","sources":["../src/map-pin.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AA4BvC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qDAAqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,KAAK,EACL,UAAU,EAAE,cAAc,EAC1B,SAAS,EAAE,aAAa,EACxB,SAAS,EACT,UAAU,EACV,aAAa,EAAE,UAAsB,GACtC,EAAE,WAAW,+BA8Gb"}
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+ export interface MapUserLocationProps {
3
+ coordinates: Pick<GeolocationCoordinates, 'longitude' | 'latitude'> | null | undefined;
4
+ /** Custom marker; defaults to `UserLocationPin`. */
5
+ children?: ReactNode;
6
+ /** Label of the default `UserLocationPin`; ignored when `children` is set. */
7
+ label?: string;
8
+ }
9
+ /** The user's own position on a `MapView`; renders nothing until it is known. */
10
+ export declare function MapUserLocation({ coordinates, children, label }: MapUserLocationProps): import('react').JSX.Element | null;
11
+ //# sourceMappingURL=map-user-location.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-user-location.d.ts","sourceRoot":"","sources":["../src/map-user-location.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,IAAI,CAAC,sBAAsB,EAAE,WAAW,GAAG,UAAU,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACvF,oDAAoD;IACpD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,oBAAoB,sCAcrF"}
@@ -0,0 +1,30 @@
1
+ import { ReactNode } from 'react';
2
+ import { MapItemId } from './map-context';
3
+ import { MapLngLat } from './utils/get-bounds-from-points';
4
+ export interface MapViewProps<TId extends MapItemId = MapItemId> {
5
+ /** Points the map frames on load and whenever they change. */
6
+ fitPoints: MapLngLat[];
7
+ selectedId?: TId | null;
8
+ hoveredId?: TId | null;
9
+ onSelect?: (id: TId) => void;
10
+ onHover?: (id: TId | null) => void;
11
+ /** Ease to a marker when it becomes selected, so it is never off screen. */
12
+ flyToSelected?: boolean;
13
+ children?: ReactNode;
14
+ testId: string;
15
+ ariaLabel: string;
16
+ className?: string;
17
+ hasError?: boolean;
18
+ isLoading?: boolean;
19
+ fitBoundsPadding?: number;
20
+ mapStyleUrl?: string;
21
+ onLoad?: () => void;
22
+ /** Shown when `hasError` is set; pass a translated string. */
23
+ errorLabel?: string;
24
+ }
25
+ /**
26
+ * Interactive map root: frames `fitPoints` and shares selection / hover state
27
+ * with the `MapMarker`s composed inside it.
28
+ */
29
+ export declare function MapView<TId extends MapItemId = MapItemId>({ fitPoints, selectedId, hoveredId, onSelect, onHover, flyToSelected, children, ...baseMapProps }: MapViewProps<TId>): import('react').JSX.Element;
30
+ //# sourceMappingURL=map-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-view.d.ts","sourceRoot":"","sources":["../src/map-view.tsx"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGxD,OAAO,EAAkB,KAAK,SAAS,EAA4B,MAAM,eAAe,CAAC;AACzF,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,gCAAgC,CAAC;AAExC,MAAM,WAAW,YAAY,CAAC,GAAG,SAAS,SAAS,GAAG,SAAS;IAC7D,8DAA8D;IAC9D,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,IAAI,KAAK,IAAI,CAAC;IACnC,4EAA4E;IAC5E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,SAAS,SAAS,GAAG,SAAS,EAAE,EACzD,SAAS,EACT,UAAiB,EACjB,SAAgB,EAChB,QAAQ,EACR,OAAO,EACP,aAAoB,EACpB,QAAQ,EACR,GAAG,YAAY,EAChB,EAAE,YAAY,CAAC,GAAG,CAAC,+BAuCnB"}