@swisper.ai/web-map 0.0.2 → 0.0.4

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.
package/dist/index.es.js CHANGED
@@ -1,44 +1,44 @@
1
1
  import { createContext as e, useContext as t, useEffect as n, useMemo as r, useRef as i } from "react";
2
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
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";
4
+ import { Tooltip as f, Typography as p } from "@swisper.ai/ui";
5
+ import { cn as m } from "@swisper.ai/web-core";
6
6
  import "maplibre-gl/dist/maplibre-gl.css";
7
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
7
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
8
8
  //#region src/constants.ts
9
- var v = "https://tiles.openfreemap.org/styles/liberty", y = 48, b = 15;
10
- function x(e) {
9
+ var _ = "https://tiles.openfreemap.org/styles/liberty", v = 48, y = 15;
10
+ function b(e) {
11
11
  return e ?? "https://tiles.openfreemap.org/styles/liberty";
12
12
  }
13
13
  //#endregion
14
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 }]) => {
15
+ var x;
16
+ function S() {
17
+ return x ??= Promise.all([import("maplibre-gl"), import("./maplibre-worker-source-DgoyX01U.js")]).then(([e, { default: t }]) => {
18
18
  let n = new Blob([t], { type: "text/javascript" });
19
19
  return e.setWorkerUrl(URL.createObjectURL(n)), e;
20
- }), S;
20
+ }), x;
21
21
  }
22
22
  //#endregion
23
23
  //#region src/utils/get-bounds-from-points.ts
24
- function w(e) {
24
+ function C(e) {
25
25
  if (e.length === 0) return null;
26
26
  let t = e[0][0], n = e[0][1], r = e[0][0], i = e[0][1];
27
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
28
  return [[t, n], [r, i]];
29
29
  }
30
- function T(e) {
30
+ function w(e) {
31
31
  let [[t, n], [r, i]] = e;
32
32
  return [(t + r) / 2, (n + i) / 2];
33
33
  }
34
- function E(e) {
34
+ function T(e) {
35
35
  let [[t, n], [r, i]] = e;
36
36
  return t === r && n === i;
37
37
  }
38
38
  //#endregion
39
39
  //#region src/base-map.tsx
40
- function D(e, t, n) {
41
- if (E(t)) {
40
+ function E(e, t, n) {
41
+ if (T(t)) {
42
42
  let [[n, r]] = t;
43
43
  e.setCenter([n, r]), e.setZoom(15);
44
44
  return;
@@ -48,30 +48,30 @@ function D(e, t, n) {
48
48
  duration: 0
49
49
  });
50
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);
51
+ function D({ 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: _, errorLabel: v = "Could not load map. Try again later." }) {
52
+ let y = i(null), x = p ?? y, C = b(f);
53
53
  return n(() => {
54
54
  if (!u) return;
55
- let e = b.current?.getMap();
56
- e && D(e, u, d);
55
+ let e = x.current?.getMap();
56
+ e && E(e, u, d);
57
57
  }, [
58
58
  u,
59
59
  d,
60
- b
61
- ]), /* @__PURE__ */ _("div", {
60
+ x
61
+ ]), /* @__PURE__ */ g("div", {
62
62
  "data-testid": r,
63
63
  "data-rounded": !0,
64
64
  "data-loading": c,
65
65
  "aria-invalid": s,
66
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),
67
+ className: m("relative size-full", "bg-[#f8f4f1]", "data-[error=true]:bg-[#EB0000]/10", "data-[rounded=true]:rounded-lg overflow-hidden", t),
68
68
  role: "application",
69
69
  "aria-label": a,
70
- children: [s && /* @__PURE__ */ g(k, { label: v }), /* @__PURE__ */ g(o, {
71
- ref: b,
72
- mapLib: C(),
70
+ children: [s && /* @__PURE__ */ h(O, { label: v }), /* @__PURE__ */ h(o, {
71
+ ref: x,
72
+ mapLib: S(),
73
73
  initialViewState: l,
74
- mapStyle: S,
74
+ mapStyle: C,
75
75
  style: {
76
76
  width: "100%",
77
77
  height: "100%",
@@ -81,21 +81,21 @@ function O({ children: e, className: t, testId: r, ariaLabel: a, hasError: s, is
81
81
  attributionControl: !1,
82
82
  onLoad: () => {
83
83
  if (u) {
84
- let e = b.current?.getMap();
85
- e && D(e, u, d);
84
+ let e = x.current?.getMap();
85
+ e && E(e, u, d);
86
86
  }
87
- m?.();
87
+ _?.();
88
88
  },
89
89
  children: e
90
90
  })]
91
91
  });
92
92
  }
93
- function k({ label: e }) {
94
- return /* @__PURE__ */ g("div", {
93
+ function O({ label: e }) {
94
+ return /* @__PURE__ */ h("div", {
95
95
  className: "absolute inset-0 flex items-center justify-center",
96
- children: /* @__PURE__ */ _("div", {
96
+ children: /* @__PURE__ */ g("div", {
97
97
  className: "flex flex-col items-center gap-8",
98
- children: [/* @__PURE__ */ g(d, { fontSize: "large" }), /* @__PURE__ */ g(p, {
98
+ children: [/* @__PURE__ */ h(d, { fontSize: "large" }), /* @__PURE__ */ h(p, {
99
99
  variant: "body-sm",
100
100
  className: "text-text-error",
101
101
  children: e
@@ -105,17 +105,17 @@ function k({ label: e }) {
105
105
  }
106
106
  //#endregion
107
107
  //#region src/map-context.tsx
108
- var A = e(null);
109
- function j() {
110
- let e = t(A);
108
+ var k = e(null);
109
+ function A() {
110
+ let e = t(k);
111
111
  if (!e) throw Error("useMapView must be used within a MapView component");
112
112
  return e;
113
113
  }
114
114
  //#endregion
115
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),
116
+ function j({ 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(() => C(e), [e]), f = r(() => d ? {
118
+ center: w(d),
119
119
  zoom: 12
120
120
  } : {
121
121
  center: [0, 0],
@@ -134,9 +134,9 @@ function M({ fitPoints: e, selectedId: t = null, hoveredId: n = null, onSelect:
134
134
  a,
135
135
  o
136
136
  ]);
137
- return /* @__PURE__ */ g(A.Provider, {
137
+ return /* @__PURE__ */ h(k.Provider, {
138
138
  value: p,
139
- children: /* @__PURE__ */ g(O, {
139
+ children: /* @__PURE__ */ h(D, {
140
140
  ...l,
141
141
  initialViewState: f,
142
142
  bounds: d,
@@ -147,18 +147,18 @@ function M({ fitPoints: e, selectedId: t = null, hoveredId: n = null, onSelect:
147
147
  }
148
148
  //#endregion
149
149
  //#region src/map-marker.tsx
150
- var N = e(null);
151
- function P() {
152
- return t(N);
150
+ var M = e(null);
151
+ function N() {
152
+ return t(M);
153
153
  }
154
- function F({ isHovered: e, isSelected: t }) {
154
+ function P({ isHovered: e, isSelected: t }) {
155
155
  return e ? 3 : t ? 2 : 1;
156
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(() => ({
157
+ function F({ 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: g, onSelect: _, onHover: v } = A(), y = e === f, b = e === p, x = r(() => ({
159
159
  isSelected: y,
160
160
  isHovered: b
161
- }), [y, b]), S = s ?? h;
161
+ }), [y, b]), S = s ?? g;
162
162
  return n(() => {
163
163
  if (!y || !S) return;
164
164
  let e = d.current?.getMap();
@@ -172,15 +172,15 @@ function I({ id: e, longitude: t, latitude: i, label: a, anchor: o = "bottom", f
172
172
  t,
173
173
  i,
174
174
  d
175
- ]), /* @__PURE__ */ g(c, {
175
+ ]), /* @__PURE__ */ h(c, {
176
176
  longitude: t,
177
177
  latitude: i,
178
178
  anchor: o,
179
- style: { zIndex: F(x) },
179
+ style: { zIndex: P(x) },
180
180
  onClick: (t) => {
181
181
  t.originalEvent.stopPropagation(), _?.(e);
182
182
  },
183
- children: /* @__PURE__ */ g("button", {
183
+ children: /* @__PURE__ */ h("button", {
184
184
  type: "button",
185
185
  "aria-label": a,
186
186
  "aria-pressed": y,
@@ -189,7 +189,7 @@ function I({ id: e, longitude: t, latitude: i, label: a, anchor: o = "bottom", f
189
189
  onMouseEnter: () => v?.(e),
190
190
  onMouseLeave: () => v?.(null),
191
191
  className: m("cursor-pointer transition-[filter] duration-150 ease-out", l),
192
- children: /* @__PURE__ */ g(N.Provider, {
192
+ children: /* @__PURE__ */ h(M.Provider, {
193
193
  value: x,
194
194
  children: typeof u == "function" ? u(x) : u
195
195
  })
@@ -198,36 +198,36 @@ function I({ id: e, longitude: t, latitude: i, label: a, anchor: o = "bottom", f
198
198
  }
199
199
  //#endregion
200
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) {
201
+ var I = 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 L(e, t) {
203
203
  return `linear-gradient(${e}, ${e}), ${t}`;
204
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", {
205
+ function R({ label: e, value: t, isSelected: n, isHovered: r, className: i, classNames: a, "data-testid": o = "map-pin" }) {
206
+ let s = N(), c = n ?? s?.isSelected ?? !1, l = r ?? s?.isHovered ?? !1, u = c || l, d = t != null && t !== "", f = u || !d, _ = c ? L("var(--map-pin-overlay-selected)", "var(--map-pin-bg-selected)") : l ? L("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__ */ g("div", {
208
208
  "data-testid": o,
209
209
  "data-selected": c || void 0,
210
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", {
211
+ className: m(I, "flex flex-col items-center isolate drop-shadow-[0px_1px_2px_rgba(228,232,235,0.3)]", i, a?.root),
212
+ children: [/* @__PURE__ */ h("div", {
213
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", {
214
+ style: { background: _ },
215
+ children: /* @__PURE__ */ g("div", {
216
216
  className: "flex items-center px-4",
217
- children: [d && /* @__PURE__ */ g(p, {
217
+ children: [d && /* @__PURE__ */ h(p, {
218
218
  variant: "caption",
219
219
  className: m("shrink-0 whitespace-nowrap leading-[1.3]", v, a?.value),
220
220
  children: t
221
- }), /* @__PURE__ */ g("div", {
221
+ }), /* @__PURE__ */ h("div", {
222
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", {
223
+ children: /* @__PURE__ */ h("div", {
224
224
  className: "flex min-w-0 items-center overflow-hidden",
225
- children: /* @__PURE__ */ _("div", {
225
+ children: /* @__PURE__ */ g("div", {
226
226
  className: m("flex w-max items-center gap-4", d && "pl-4"),
227
- children: [d && /* @__PURE__ */ g("span", {
227
+ children: [d && /* @__PURE__ */ h("span", {
228
228
  "aria-hidden": "true",
229
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, {
230
+ }), /* @__PURE__ */ h(p, {
231
231
  variant: "caption",
232
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
233
  children: e
@@ -236,16 +236,16 @@ function z({ label: e, value: t, isSelected: n, isHovered: r, className: i, clas
236
236
  })
237
237
  })]
238
238
  })
239
- }), /* @__PURE__ */ g("div", {
239
+ }), /* @__PURE__ */ h("div", {
240
240
  className: "relative z-1 flex w-full items-center justify-center",
241
241
  "aria-hidden": "true",
242
- children: /* @__PURE__ */ g("svg", {
242
+ children: /* @__PURE__ */ h("svg", {
243
243
  width: 8,
244
244
  height: 8,
245
245
  viewBox: "0 0 8 8",
246
246
  fill: "none",
247
247
  className: m("size-8 shrink-0", a?.caret),
248
- children: /* @__PURE__ */ g("path", {
248
+ children: /* @__PURE__ */ h("path", {
249
249
  d: "M4 8L0 0H8L4 8Z",
250
250
  fill: c ? "var(--map-pin-bg-selected)" : "var(--map-pin-bg)"
251
251
  })
@@ -255,10 +255,10 @@ function z({ label: e, value: t, isSelected: n, isHovered: r, className: i, clas
255
255
  }
256
256
  //#endregion
257
257
  //#region src/user-location-pin.tsx
258
- function B({ className: e, label: t = "My location" }) {
259
- return /* @__PURE__ */ g(f, {
258
+ function z({ className: e, label: t = "My location" }) {
259
+ return /* @__PURE__ */ h(f, {
260
260
  tooltip: t,
261
- children: /* @__PURE__ */ g("div", {
261
+ children: /* @__PURE__ */ h("div", {
262
262
  "data-testid": "user-location-pin",
263
263
  role: "img",
264
264
  "aria-label": t,
@@ -268,16 +268,16 @@ function B({ className: e, label: t = "My location" }) {
268
268
  }
269
269
  //#endregion
270
270
  //#region src/map-user-location.tsx
271
- function V({ coordinates: e, children: t, label: n }) {
272
- return e ? /* @__PURE__ */ g(c, {
271
+ function B({ coordinates: e, children: t, label: n }) {
272
+ return e ? /* @__PURE__ */ h(c, {
273
273
  longitude: e.longitude,
274
274
  latitude: e.latitude,
275
275
  anchor: "center",
276
276
  style: { zIndex: 3 },
277
- children: t ?? /* @__PURE__ */ g(B, { label: n })
277
+ children: t ?? /* @__PURE__ */ h(z, { label: n })
278
278
  }) : null;
279
279
  }
280
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 };
281
+ export { D as BaseMap, v as DEFAULT_FIT_BOUNDS_PADDING, _ as DEFAULT_MAP_STYLE_URL, a as Layer, F as MapMarker, R as MapPin, B as MapUserLocation, j as MapView, s as Marker, y as SINGLE_POINT_ZOOM, l as Source, z as UserLocationPin, w as getBoundsCentroid, C as getBoundsFromPoints, T as isDegenerateBounds, S as loadMapLib, b as resolveMapStyleUrl, u as useMap, N as useMapMarker, A as useMapView };
282
282
 
283
283
  //# sourceMappingURL=index.es.js.map
@@ -1 +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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"}
1
+ {"version":3,"file":"index.es.js","names":["MapGL","Marker","Marker","Marker$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","cn","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","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/web-core';\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 } from '@swisper.ai/ui';\nimport { cn } from '@swisper.ai/web-core';\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 } from '@swisper.ai/ui';\nimport { cn } from '@swisper.ai/web-core';\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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"}
@@ -1 +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"}
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;AA6BvC,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"user-location-pin.d.ts","sourceRoot":"","sources":["../src/user-location-pin.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,KAAqB,EAAE,EAAE,oBAAoB,+BAiBzF"}
1
+ {"version":3,"file":"user-location-pin.d.ts","sourceRoot":"","sources":["../src/user-location-pin.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,KAAqB,EAAE,EAAE,oBAAoB,+BAiBzF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swisper.ai/web-map",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Interactive MapLibre maps with selectable markers for Swisper web apps",
5
5
  "keywords": [
6
6
  "react",
@@ -42,16 +42,16 @@
42
42
  "tailwind.css"
43
43
  ],
44
44
  "dependencies": {
45
- "@swisper.ai/icons": "0.0.2",
46
- "@swisper.ai/tailwind-config": "0.0.2",
47
- "@swisper.ai/ui": "0.0.2",
45
+ "@swisper.ai/icons": "0.0.3",
46
+ "@swisper.ai/tailwind-config": "0.0.3",
47
+ "@swisper.ai/ui": "0.1.1",
48
48
  "@swisper.ai/web-core": "0.0.2",
49
49
  "@vis.gl/react-maplibre": "^8.1.3",
50
50
  "maplibre-gl": "^6.4.1"
51
51
  },
52
52
  "devDependencies": {
53
- "@fintama/vite-config": "0.1.1",
54
- "@fintama/vitest-config": "0.1.1",
53
+ "@fintama/swisper-int-vite-config": "0.0.1",
54
+ "@fintama/swisper-int-vitest-config": "0.0.1",
55
55
  "@storybook/react-vite": "10.6.0",
56
56
  "@swisper.ai/int-eslint-config": "0.0.1",
57
57
  "@swisper.ai/int-typescript-config": "0.0.1",
@@ -2,7 +2,7 @@ import { cleanup, render, screen } from '@testing-library/react';
2
2
  import type { ReactNode } from 'react';
3
3
  import { afterEach, describe, expect, test, vi } from 'vitest';
4
4
 
5
- import { TooltipProvider } from '@swisper.ai/ui';
5
+ import { withTooltip } from '@swisper.ai/ui';
6
6
 
7
7
  import { MapUserLocation } from '../map-user-location';
8
8
 
@@ -26,11 +26,7 @@ describe('MapUserLocation', () => {
26
26
  });
27
27
 
28
28
  test('shows the default dot once the position is known', () => {
29
- render(
30
- <TooltipProvider>
31
- <MapUserLocation coordinates={ZURICH} />
32
- </TooltipProvider>,
33
- );
29
+ render(withTooltip(() => <MapUserLocation coordinates={ZURICH} />));
34
30
 
35
31
  expect(screen.getByRole('img')).not.toBeNull();
36
32
  });
package/src/base-map.json CHANGED
@@ -2,6 +2,7 @@
2
2
  "$schema": "../../int-eslint-config/schemas/component.schema.json",
3
3
  "component": "base-map",
4
4
  "purpose": "The bare MapLibre map surface with loading and error states, framing and style URL, but no selection context.",
5
+ "anatomy": ["import { BaseMap } from '@swisper.ai/web-map';", "", "<BaseMap />"],
5
6
  "useWhen": [
6
7
  "Drawing custom sources, layers or plain markers that need no shared selection or hover",
7
8
  "Building a new map component that manages its own view state through mapRef",
@@ -2,6 +2,7 @@
2
2
  "$schema": "../../int-eslint-config/schemas/component.schema.json",
3
3
  "component": "map-marker",
4
4
  "purpose": "A selectable, hoverable point on a MapView that wraps its pin in a button and passes the marker's selection and hover state down to it.",
5
+ "anatomy": ["import { MapMarker } from '@swisper.ai/web-map';", "", "<MapMarker />"],
5
6
  "useWhen": [
6
7
  "Placing a place, result or entity on a web-map/map-view that the user can select",
7
8
  "A pin must change look when its place is selected or hovered, in the map or in a list beside it"
@@ -1,7 +1,8 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { useState, type ReactNode } from 'react';
3
3
 
4
- import { TooltipProvider, cn } from '@swisper.ai/ui';
4
+ import { withTooltip } from '@swisper.ai/ui';
5
+ import { cn } from '@swisper.ai/web-core';
5
6
 
6
7
  import { PLACE_POINTS, PLACES } from './__storybook__/fixtures';
7
8
  import { MapMarker, type MapMarkerProps, type MapMarkerState } from './map-marker';
@@ -59,12 +60,11 @@ const meta: Meta<MapMarkerStoryArgs> = {
59
60
  tags: ['autodocs'],
60
61
  decorators: [
61
62
  (Story) => (
62
- <TooltipProvider>
63
- <div className="h-[600px] w-[900px]">
64
- <Story />
65
- </div>
66
- </TooltipProvider>
63
+ <div className="h-[600px] w-[900px]">
64
+ <Story />
65
+ </div>
67
66
  ),
67
+ withTooltip,
68
68
  ],
69
69
  args: {
70
70
  anchor: 'bottom',
@@ -1,7 +1,7 @@
1
1
  import { Marker } from '@vis.gl/react-maplibre';
2
2
  import { createContext, useContext, useEffect, useMemo, type ReactNode } from 'react';
3
3
 
4
- import { cn } from '@swisper.ai/ui';
4
+ import { cn } from '@swisper.ai/web-core';
5
5
 
6
6
  import { useMapView, type MapItemId } from './map-context';
7
7
 
package/src/map-pin.json CHANGED
@@ -2,6 +2,7 @@
2
2
  "$schema": "../../int-eslint-config/schemas/component.schema.json",
3
3
  "component": "map-pin",
4
4
  "purpose": "The default callout pin for a map marker: a pill showing a short value that expands to value and place name on hover or selection.",
5
+ "anatomy": ["import { MapPin } from '@swisper.ai/web-map';", "", "<MapPin />"],
5
6
  "useWhen": [
6
7
  "Marking a place on a web-map/map-view with a short value the user compares at a glance, such as a rating or a price",
7
8
  "Marking named places where the name should appear only on hover or selection to keep the map uncluttered",
package/src/map-pin.tsx CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ReactNode } from 'react';
2
2
 
3
- import { Typography, cn } from '@swisper.ai/ui';
3
+ import { Typography } from '@swisper.ai/ui';
4
+ import { cn } from '@swisper.ai/web-core';
4
5
 
5
6
  import { useMapMarker } from './map-marker';
6
7
 
@@ -2,6 +2,7 @@
2
2
  "$schema": "../../int-eslint-config/schemas/component.schema.json",
3
3
  "component": "map-user-location",
4
4
  "purpose": "Draws the user's own position on a map once it is known, with UserLocationPin as the default marker.",
5
+ "anatomy": ["import { MapUserLocation } from '@swisper.ai/web-map';", "", "<MapUserLocation />"],
5
6
  "useWhen": [
6
7
  "Showing where the user is relative to the places on a web-map/map-view",
7
8
  "The position comes from navigator.geolocation or another source that may resolve late or not at all"