@elabs-ai/components-maps 4.2.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunk-2FPB53NG.js +36 -0
- package/dist/chunk-2FPB53NG.js.map +1 -0
- package/dist/chunk-2VVJRT66.js +37 -0
- package/dist/chunk-2VVJRT66.js.map +1 -0
- package/dist/chunk-3HXFONRP.js +2 -0
- package/dist/chunk-3HXFONRP.js.map +1 -0
- package/dist/chunk-3SJN3IYT.js +2 -0
- package/dist/chunk-3SJN3IYT.js.map +1 -0
- package/dist/chunk-4DANBD5B.js +55 -0
- package/dist/chunk-4DANBD5B.js.map +1 -0
- package/dist/chunk-5PDVG53B.js +2 -0
- package/dist/chunk-5PDVG53B.js.map +1 -0
- package/dist/chunk-643R7IJC.js +2 -0
- package/dist/chunk-643R7IJC.js.map +1 -0
- package/dist/chunk-B3ZDBQ6I.js +164 -0
- package/dist/chunk-B3ZDBQ6I.js.map +1 -0
- package/dist/chunk-BGCE3RR5.js +35 -0
- package/dist/chunk-BGCE3RR5.js.map +1 -0
- package/dist/chunk-C4VI37TD.js +485 -0
- package/dist/chunk-C4VI37TD.js.map +1 -0
- package/dist/chunk-CJJT6AEI.js +179 -0
- package/dist/chunk-CJJT6AEI.js.map +1 -0
- package/dist/chunk-D2RCPTRO.js +2 -0
- package/dist/chunk-D2RCPTRO.js.map +1 -0
- package/dist/chunk-DTV7JZVN.js +2 -0
- package/dist/chunk-DTV7JZVN.js.map +1 -0
- package/dist/chunk-E65Z3MSV.js +80 -0
- package/dist/chunk-E65Z3MSV.js.map +1 -0
- package/dist/chunk-E7VPNINZ.js +70 -0
- package/dist/chunk-E7VPNINZ.js.map +1 -0
- package/dist/chunk-FHV5IA6N.js +100 -0
- package/dist/chunk-FHV5IA6N.js.map +1 -0
- package/dist/chunk-FQ6V7BFZ.js +347 -0
- package/dist/chunk-FQ6V7BFZ.js.map +1 -0
- package/dist/chunk-G7JDXDJN.js +19 -0
- package/dist/chunk-G7JDXDJN.js.map +1 -0
- package/dist/chunk-GWULZOMW.js +21 -0
- package/dist/chunk-GWULZOMW.js.map +1 -0
- package/dist/chunk-GYDTTAVD.js +120 -0
- package/dist/chunk-GYDTTAVD.js.map +1 -0
- package/dist/chunk-HCOBWIM4.js +48 -0
- package/dist/chunk-HCOBWIM4.js.map +1 -0
- package/dist/chunk-IAHK5MVT.js +2 -0
- package/dist/chunk-IAHK5MVT.js.map +1 -0
- package/dist/chunk-JGJ6442I.js +235 -0
- package/dist/chunk-JGJ6442I.js.map +1 -0
- package/dist/chunk-JYFERXZT.js +18 -0
- package/dist/chunk-JYFERXZT.js.map +1 -0
- package/dist/chunk-LU2UEKFM.js +53 -0
- package/dist/chunk-LU2UEKFM.js.map +1 -0
- package/dist/chunk-LVYM6C5C.js +2 -0
- package/dist/chunk-LVYM6C5C.js.map +1 -0
- package/dist/chunk-MIMDP3EU.js +104 -0
- package/dist/chunk-MIMDP3EU.js.map +1 -0
- package/dist/chunk-NNBURJHY.js +2 -0
- package/dist/chunk-NNBURJHY.js.map +1 -0
- package/dist/chunk-NPUC3V37.js +2 -0
- package/dist/chunk-NPUC3V37.js.map +1 -0
- package/dist/chunk-OEVVNPN3.js +201 -0
- package/dist/chunk-OEVVNPN3.js.map +1 -0
- package/dist/chunk-PF3R2BXM.js +200 -0
- package/dist/chunk-PF3R2BXM.js.map +1 -0
- package/dist/chunk-Q2I5YODW.js +2 -0
- package/dist/chunk-Q2I5YODW.js.map +1 -0
- package/dist/chunk-QDWWP3N7.js +247 -0
- package/dist/chunk-QDWWP3N7.js.map +1 -0
- package/dist/chunk-QU33KPMP.js +2 -0
- package/dist/chunk-QU33KPMP.js.map +1 -0
- package/dist/chunk-RK4BUMHS.js +2 -0
- package/dist/chunk-RK4BUMHS.js.map +1 -0
- package/dist/chunk-RNSLG64F.js +26 -0
- package/dist/chunk-RNSLG64F.js.map +1 -0
- package/dist/chunk-RV673TB4.js +68 -0
- package/dist/chunk-RV673TB4.js.map +1 -0
- package/dist/chunk-S7N2WMVT.js +180 -0
- package/dist/chunk-S7N2WMVT.js.map +1 -0
- package/dist/chunk-T3Z6TCDD.js +2 -0
- package/dist/chunk-T3Z6TCDD.js.map +1 -0
- package/dist/chunk-T4TJTVVA.js +105 -0
- package/dist/chunk-T4TJTVVA.js.map +1 -0
- package/dist/chunk-UMOYRCDX.js +103 -0
- package/dist/chunk-UMOYRCDX.js.map +1 -0
- package/dist/chunk-V2REM6UT.js +185 -0
- package/dist/chunk-V2REM6UT.js.map +1 -0
- package/dist/chunk-VFBRFGID.js +89 -0
- package/dist/chunk-VFBRFGID.js.map +1 -0
- package/dist/chunk-W76NLFER.js +103 -0
- package/dist/chunk-W76NLFER.js.map +1 -0
- package/dist/chunk-WNU4IOGL.js +116 -0
- package/dist/chunk-WNU4IOGL.js.map +1 -0
- package/dist/chunk-WS4Z7ENS.js +494 -0
- package/dist/chunk-WS4Z7ENS.js.map +1 -0
- package/dist/chunk-XQT7YLJY.js +497 -0
- package/dist/chunk-XQT7YLJY.js.map +1 -0
- package/dist/chunk-Y44MEKJV.js +21 -0
- package/dist/chunk-Y44MEKJV.js.map +1 -0
- package/dist/chunk-Z4EPSOB4.js +53 -0
- package/dist/chunk-Z4EPSOB4.js.map +1 -0
- package/dist/chunk-Z5OPDBDC.js +2 -0
- package/dist/chunk-Z5OPDBDC.js.map +1 -0
- package/dist/index.d.ts +1241 -46
- package/dist/index.js +189 -1471
- package/dist/index.js.map +1 -1
- package/dist/lib/arc-math.js +8 -0
- package/dist/lib/arc-math.js.map +1 -0
- package/dist/lib/map-position.js +11 -0
- package/dist/lib/map-position.js.map +1 -0
- package/dist/lib/merge-hover-paint.js +10 -0
- package/dist/lib/merge-hover-paint.js.map +1 -0
- package/dist/lib/plan-crs.js +14 -0
- package/dist/lib/plan-crs.js.map +1 -0
- package/dist/lib/plan-patterns.js +14 -0
- package/dist/lib/plan-patterns.js.map +1 -0
- package/dist/lib/plan-regions.js +18 -0
- package/dist/lib/plan-regions.js.map +1 -0
- package/dist/lib/plan-status.js +14 -0
- package/dist/lib/plan-status.js.map +1 -0
- package/dist/lib/route-arrow.js +12 -0
- package/dist/lib/route-arrow.js.map +1 -0
- package/dist/lib/use-map-breakpoint.js +33 -0
- package/dist/lib/use-map-breakpoint.js.map +1 -0
- package/dist/lib/use-plan-projection.js +9 -0
- package/dist/lib/use-plan-projection.js.map +1 -0
- package/dist/lib/use-token-color.js +10 -0
- package/dist/lib/use-token-color.js.map +1 -0
- package/dist/lib/warn-once.js +10 -0
- package/dist/lib/warn-once.js.map +1 -0
- package/dist/map-annotation/anchor.js +14 -0
- package/dist/map-annotation/anchor.js.map +1 -0
- package/dist/map-annotation/index.js +21 -0
- package/dist/map-annotation/index.js.map +1 -0
- package/dist/map-annotation/map-annotation.js +18 -0
- package/dist/map-annotation/map-annotation.js.map +1 -0
- package/dist/map-arc/index.js +16 -0
- package/dist/map-arc/index.js.map +1 -0
- package/dist/map-arc/map-arc.js +13 -0
- package/dist/map-arc/map-arc.js.map +1 -0
- package/dist/map-canvas/index.css +11 -0
- package/dist/map-canvas/index.css.map +1 -0
- package/dist/map-canvas/index.js +20 -0
- package/dist/map-canvas/index.js.map +1 -0
- package/dist/map-canvas/map-canvas.css +11 -0
- package/dist/map-canvas/map-canvas.css.map +1 -0
- package/dist/map-canvas/map-canvas.js +17 -0
- package/dist/map-canvas/map-canvas.js.map +1 -0
- package/dist/map-canvas/map-context.js +11 -0
- package/dist/map-canvas/map-context.js.map +1 -0
- package/dist/map-canvas/use-resolved-basemap-theme.js +9 -0
- package/dist/map-canvas/use-resolved-basemap-theme.js.map +1 -0
- package/dist/map-cluster-layer/index.js +12 -0
- package/dist/map-cluster-layer/index.js.map +1 -0
- package/dist/map-cluster-layer/map-cluster-layer.js +12 -0
- package/dist/map-cluster-layer/map-cluster-layer.js.map +1 -0
- package/dist/map-controls/index.js +11 -0
- package/dist/map-controls/index.js.map +1 -0
- package/dist/map-controls/map-controls.js +11 -0
- package/dist/map-controls/map-controls.js.map +1 -0
- package/dist/map-geojson/index.js +13 -0
- package/dist/map-geojson/index.js.map +1 -0
- package/dist/map-geojson/map-geojson.js +13 -0
- package/dist/map-geojson/map-geojson.js.map +1 -0
- package/dist/map-inset/index.css +11 -0
- package/dist/map-inset/index.css.map +1 -0
- package/dist/map-inset/index.js +20 -0
- package/dist/map-inset/index.js.map +1 -0
- package/dist/map-inset/map-inset.css +11 -0
- package/dist/map-inset/map-inset.css.map +1 -0
- package/dist/map-inset/map-inset.js +20 -0
- package/dist/map-inset/map-inset.js.map +1 -0
- package/dist/map-legend/index.js +14 -0
- package/dist/map-legend/index.js.map +1 -0
- package/dist/map-legend/map-corner.js +10 -0
- package/dist/map-legend/map-corner.js.map +1 -0
- package/dist/map-legend/map-legend.js +11 -0
- package/dist/map-legend/map-legend.js.map +1 -0
- package/dist/map-marker/index.js +22 -0
- package/dist/map-marker/index.js.map +1 -0
- package/dist/map-marker/map-marker.js +22 -0
- package/dist/map-marker/map-marker.js.map +1 -0
- package/dist/map-north-arrow/index.js +11 -0
- package/dist/map-north-arrow/index.js.map +1 -0
- package/dist/map-north-arrow/map-north-arrow.js +11 -0
- package/dist/map-north-arrow/map-north-arrow.js.map +1 -0
- package/dist/map-plan/index.js +30 -0
- package/dist/map-plan/index.js.map +1 -0
- package/dist/map-plan/map-plan-legend.js +10 -0
- package/dist/map-plan/map-plan-legend.js.map +1 -0
- package/dist/map-plan/map-plan-overlay.js +13 -0
- package/dist/map-plan/map-plan-overlay.js.map +1 -0
- package/dist/map-plan/map-plan-status.js +9 -0
- package/dist/map-plan/map-plan-status.js.map +1 -0
- package/dist/map-plan/map-plan-table.js +10 -0
- package/dist/map-plan/map-plan-table.js.map +1 -0
- package/dist/map-plan/use-plan-selection.js +9 -0
- package/dist/map-plan/use-plan-selection.js.map +1 -0
- package/dist/map-plan-image/index.js +11 -0
- package/dist/map-plan-image/index.js.map +1 -0
- package/dist/map-plan-image/map-plan-image.js +11 -0
- package/dist/map-plan-image/map-plan-image.js.map +1 -0
- package/dist/map-popup/index.js +12 -0
- package/dist/map-popup/index.js.map +1 -0
- package/dist/map-popup/map-popup.js +12 -0
- package/dist/map-popup/map-popup.js.map +1 -0
- package/dist/map-route/index.js +12 -0
- package/dist/map-route/index.js.map +1 -0
- package/dist/map-route/map-route.js +12 -0
- package/dist/map-route/map-route.js.map +1 -0
- package/dist/map-scale-bar/index.js +18 -0
- package/dist/map-scale-bar/index.js.map +1 -0
- package/dist/map-scale-bar/map-scale-bar.js +18 -0
- package/dist/map-scale-bar/map-scale-bar.js.map +1 -0
- package/package.json +5 -5
- package/src/index.ts +83 -0
- package/src/lib/arc-math.test.ts +19 -0
- package/src/lib/arc-math.ts +6 -1
- package/src/lib/map-position.ts +77 -0
- package/src/lib/merge-hover-paint.ts +44 -13
- package/src/lib/plan-crs.parity.test.ts +39 -0
- package/src/lib/plan-crs.test.ts +248 -0
- package/src/lib/plan-crs.ts +252 -0
- package/src/lib/plan-patterns.test.ts +255 -0
- package/src/lib/plan-patterns.ts +160 -0
- package/src/lib/plan-regions.test.ts +186 -0
- package/src/lib/plan-regions.ts +209 -0
- package/src/lib/plan-status.test.ts +87 -0
- package/src/lib/plan-status.ts +147 -0
- package/src/lib/route-arrow.test.ts +88 -0
- package/src/lib/route-arrow.ts +59 -0
- package/src/lib/use-map-breakpoint.test.ts +83 -0
- package/src/lib/use-map-breakpoint.ts +238 -0
- package/src/lib/use-plan-projection.ts +157 -0
- package/src/lib/warn-once.ts +21 -0
- package/src/map-annotation/anchor.ts +92 -0
- package/src/map-annotation/index.ts +15 -0
- package/src/map-annotation/map-annotation.stories.tsx +113 -0
- package/src/map-annotation/map-annotation.test.tsx +106 -0
- package/src/map-annotation/map-annotation.tsx +356 -0
- package/src/map-arc/map-arc.tsx +26 -5
- package/src/map-canvas/map-canvas.stories.tsx +211 -0
- package/src/map-canvas/map-canvas.test.tsx +389 -0
- package/src/map-canvas/map-canvas.tsx +457 -17
- package/src/map-canvas/map-context.ts +11 -0
- package/src/map-cluster-layer/map-cluster-layer.tsx +14 -1
- package/src/map-controls/map-controls.stories.tsx +16 -0
- package/src/map-controls/map-controls.test.tsx +145 -1
- package/src/map-controls/map-controls.tsx +57 -12
- package/src/map-geojson/map-geojson.stories.tsx +153 -3
- package/src/map-geojson/map-geojson.test.tsx +453 -0
- package/src/map-geojson/map-geojson.tsx +591 -51
- package/src/map-inset/index.ts +1 -0
- package/src/map-inset/map-inset.stories.tsx +61 -0
- package/src/map-inset/map-inset.test.tsx +117 -0
- package/src/map-inset/map-inset.tsx +264 -0
- package/src/map-legend/index.ts +10 -0
- package/src/map-legend/map-corner.ts +23 -0
- package/src/map-legend/map-legend.stories.tsx +171 -0
- package/src/map-legend/map-legend.test.tsx +136 -0
- package/src/map-legend/map-legend.tsx +264 -0
- package/src/map-marker/map-marker.stories.tsx +136 -4
- package/src/map-marker/map-marker.test.tsx +328 -3
- package/src/map-marker/map-marker.tsx +370 -33
- package/src/map-north-arrow/index.ts +1 -0
- package/src/map-north-arrow/map-north-arrow.stories.tsx +65 -0
- package/src/map-north-arrow/map-north-arrow.test.tsx +39 -0
- package/src/map-north-arrow/map-north-arrow.tsx +82 -0
- package/src/map-plan/index.ts +13 -0
- package/src/map-plan/map-plan-legend.test.tsx +61 -0
- package/src/map-plan/map-plan-legend.tsx +83 -0
- package/src/map-plan/map-plan-overlay.stories.tsx +260 -0
- package/src/map-plan/map-plan-overlay.test.tsx +302 -0
- package/src/map-plan/map-plan-overlay.tsx +531 -0
- package/src/map-plan/map-plan-status.test.tsx +71 -0
- package/src/map-plan/map-plan-status.tsx +77 -0
- package/src/map-plan/map-plan-table.test.tsx +86 -0
- package/src/map-plan/map-plan-table.tsx +153 -0
- package/src/map-plan/use-plan-selection.ts +78 -0
- package/src/map-plan-image/index.ts +1 -0
- package/src/map-plan-image/map-plan-image.stories.tsx +301 -0
- package/src/map-plan-image/map-plan-image.test.tsx +176 -0
- package/src/map-plan-image/map-plan-image.tsx +279 -0
- package/src/map-popup/map-popup.tsx +15 -10
- package/src/map-route/index.ts +1 -1
- package/src/map-route/map-route.test.tsx +216 -0
- package/src/map-route/map-route.tsx +105 -6
- package/src/map-scale-bar/index.ts +9 -0
- package/src/map-scale-bar/map-scale-bar.stories.tsx +63 -0
- package/src/map-scale-bar/map-scale-bar.test.tsx +65 -0
- package/src/map-scale-bar/map-scale-bar.tsx +145 -0
- package/src/test-utils/locator-fixture.ts +75 -0
- package/src/test-utils/maplibre-mock.ts +256 -19
package/src/map-arc/map-arc.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { useEffect, useId, useMemo, useRef } from "react";
|
|
|
5
5
|
|
|
6
6
|
import { useMap } from "../map-canvas/map-context";
|
|
7
7
|
import { buildArcCoordinates } from "../lib/arc-math";
|
|
8
|
+
import type { PlanPoint } from "../lib/plan-crs";
|
|
8
9
|
import { mergeHoverPaint } from "../lib/merge-hover-paint";
|
|
9
10
|
import { useTokenColor } from "../lib/use-token-color";
|
|
10
11
|
|
|
@@ -12,9 +13,12 @@ import { useTokenColor } from "../lib/use-token-color";
|
|
|
12
13
|
export type MapArcDatum = {
|
|
13
14
|
/** Unique identifier for this arc. Required for hover state tracking and event payloads. */
|
|
14
15
|
id: string | number;
|
|
15
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Start coordinate as [longitude, latitude] — or, inside a `<MapCanvas plan>`,
|
|
18
|
+
* as [x, y] in plan units.
|
|
19
|
+
*/
|
|
16
20
|
from: [number, number];
|
|
17
|
-
/** End coordinate as
|
|
21
|
+
/** End coordinate, in the same units as `from`. */
|
|
18
22
|
to: [number, number];
|
|
19
23
|
};
|
|
20
24
|
|
|
@@ -26,6 +30,8 @@ export type MapArcEvent<T extends MapArcDatum = MapArcDatum> = {
|
|
|
26
30
|
longitude: number;
|
|
27
31
|
/** Latitude of the cursor at the time of the event. */
|
|
28
32
|
latitude: number;
|
|
33
|
+
/** The cursor in plan units, on a plan map; `null` on a geographic one. */
|
|
34
|
+
plan: PlanPoint | null;
|
|
29
35
|
/** The underlying MapLibre mouse event for advanced use cases. */
|
|
30
36
|
originalEvent: MapLibreGL.MapMouseEvent;
|
|
31
37
|
};
|
|
@@ -89,6 +95,11 @@ const DEFAULT_ARC_LAYOUT: MapArcLineLayout = {
|
|
|
89
95
|
/**
|
|
90
96
|
* Curved great-circle-style arcs between coordinate pairs (flight paths,
|
|
91
97
|
* network links). Pairs well with `<MapCanvas blank projection={{ type: "globe" }}>`.
|
|
98
|
+
*
|
|
99
|
+
* Inside a `<MapCanvas plan>` the same component draws plan-space curves — a
|
|
100
|
+
* material flow between two machine cells, a walking route across a floor. The
|
|
101
|
+
* curve is built in PLAN units and each sample is then converted, because a
|
|
102
|
+
* straight lng/lat line bows once Mercator has had its say.
|
|
92
103
|
*/
|
|
93
104
|
export function MapArc<T extends MapArcDatum = MapArcDatum>({
|
|
94
105
|
data,
|
|
@@ -103,7 +114,7 @@ export function MapArc<T extends MapArcDatum = MapArcDatum>({
|
|
|
103
114
|
interactive = true,
|
|
104
115
|
beforeId,
|
|
105
116
|
}: MapArcProps<T>) {
|
|
106
|
-
const { map, isLoaded } = useMap();
|
|
117
|
+
const { map, isLoaded, plan } = useMap();
|
|
107
118
|
const autoId = useId();
|
|
108
119
|
const id = propId ?? autoId;
|
|
109
120
|
const sourceId = `arc-source-${id}`;
|
|
@@ -133,21 +144,29 @@ export function MapArc<T extends MapArcDatum = MapArcDatum>({
|
|
|
133
144
|
type: "FeatureCollection",
|
|
134
145
|
features: data.map((arc) => {
|
|
135
146
|
const { from, to, ...properties } = arc;
|
|
147
|
+
// In plan mode the curve is sampled in plan units with the antimeridian
|
|
148
|
+
// unwrap OFF — x is a position on a floor, not a longitude — and each
|
|
149
|
+
// sample is converted afterwards.
|
|
150
|
+
const sampled = buildArcCoordinates(from, to, curvature, samples, plan == null);
|
|
136
151
|
return {
|
|
137
152
|
type: "Feature",
|
|
138
153
|
properties,
|
|
139
154
|
geometry: {
|
|
140
155
|
type: "LineString",
|
|
141
|
-
coordinates:
|
|
156
|
+
coordinates: plan ? sampled.map((point) => plan.toLngLat(point)) : sampled,
|
|
142
157
|
},
|
|
143
158
|
};
|
|
144
159
|
}),
|
|
145
160
|
}),
|
|
146
|
-
[data, curvature, samples],
|
|
161
|
+
[data, curvature, samples, plan],
|
|
147
162
|
);
|
|
148
163
|
|
|
149
164
|
const latestRef = useRef({ data, onClick, onHover });
|
|
150
165
|
latestRef.current = { data, onClick, onHover };
|
|
166
|
+
// The interaction effect is bound once per map; the plan is read through a ref
|
|
167
|
+
// so a plan arriving later does not rebind every listener.
|
|
168
|
+
const planRef = useRef(plan);
|
|
169
|
+
planRef.current = plan;
|
|
151
170
|
|
|
152
171
|
// Add source and layers on mount. The invisible hit layer widens the
|
|
153
172
|
// pointer target so thin arcs stay hoverable/clickable.
|
|
@@ -255,6 +274,7 @@ export function MapArc<T extends MapArcDatum = MapArcDatum>({
|
|
|
255
274
|
arc: arc as T,
|
|
256
275
|
longitude: e.lngLat.lng,
|
|
257
276
|
latitude: e.lngLat.lat,
|
|
277
|
+
plan: planRef.current ? planRef.current.toPlan([e.lngLat.lng, e.lngLat.lat]) : null,
|
|
258
278
|
originalEvent: e,
|
|
259
279
|
});
|
|
260
280
|
}
|
|
@@ -273,6 +293,7 @@ export function MapArc<T extends MapArcDatum = MapArcDatum>({
|
|
|
273
293
|
arc: arc as T,
|
|
274
294
|
longitude: e.lngLat.lng,
|
|
275
295
|
latitude: e.lngLat.lat,
|
|
296
|
+
plan: planRef.current ? planRef.current.toPlan([e.lngLat.lng, e.lngLat.lat]) : null,
|
|
276
297
|
originalEvent: e,
|
|
277
298
|
});
|
|
278
299
|
};
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
3
|
+
import { expect, waitFor } from "storybook/test";
|
|
4
|
+
import { resolveTokenColor } from "@elabs-ai/components-tokens";
|
|
2
5
|
|
|
3
6
|
import { MapCanvas } from "./map-canvas";
|
|
4
7
|
import { MapControls } from "../map-controls";
|
|
8
|
+
import { MapGeoJSON } from "../map-geojson";
|
|
9
|
+
import { MapInset } from "../map-inset";
|
|
10
|
+
import { MapLegend } from "../map-legend";
|
|
11
|
+
import { MapMarker, MapMarkerContent, MapMarkerTooltip } from "../map-marker";
|
|
12
|
+
import { MapScaleBar } from "../map-scale-bar";
|
|
13
|
+
import {
|
|
14
|
+
GREATER_TORONTO,
|
|
15
|
+
LAKE_ONTARIO,
|
|
16
|
+
LOCATOR_BOUNDS,
|
|
17
|
+
LOCATOR_PLACES,
|
|
18
|
+
} from "../test-utils/locator-fixture";
|
|
5
19
|
|
|
6
20
|
const meta = {
|
|
7
21
|
title: "Maps/MapCanvas",
|
|
@@ -52,3 +66,200 @@ export const Globe: Story = {
|
|
|
52
66
|
</div>
|
|
53
67
|
),
|
|
54
68
|
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Consumer-side paint needs concrete colours (WebGL cannot read CSS
|
|
72
|
+
* variables): resolve a token and re-resolve when the theme flips.
|
|
73
|
+
*/
|
|
74
|
+
function useThemedTokenColor(name: string): string {
|
|
75
|
+
const [color, setColor] = useState("#000000");
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const update = () => setColor(resolveTokenColor(name));
|
|
78
|
+
update();
|
|
79
|
+
const observer = new MutationObserver(update);
|
|
80
|
+
observer.observe(document.documentElement, {
|
|
81
|
+
attributes: true,
|
|
82
|
+
attributeFilter: ["data-theme", "class"],
|
|
83
|
+
});
|
|
84
|
+
return () => observer.disconnect();
|
|
85
|
+
}, [name]);
|
|
86
|
+
return color;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function LakeOntarioLocator() {
|
|
90
|
+
const lake = useThemedTokenColor("--chart-1");
|
|
91
|
+
const region = useThemedTokenColor("--chart-2");
|
|
92
|
+
return (
|
|
93
|
+
<div className="w-full p-4">
|
|
94
|
+
<MapCanvas
|
|
95
|
+
interactive={false}
|
|
96
|
+
// c-11: the basemap printed "Toronto" 16 px from the marker that
|
|
97
|
+
// names it, with the marker dot over the basemap word's last letters.
|
|
98
|
+
// A locator's editorial labels are the only labels on it.
|
|
99
|
+
basemapLabels={false}
|
|
100
|
+
bounds={LOCATOR_BOUNDS}
|
|
101
|
+
// Room at the top for the inset, at the bottom for the scale bar.
|
|
102
|
+
fitBoundsOptions={{ padding: { top: 152, right: 24, bottom: 56, left: 24 } }}
|
|
103
|
+
>
|
|
104
|
+
<MapGeoJSON
|
|
105
|
+
id="gta"
|
|
106
|
+
data={GREATER_TORONTO}
|
|
107
|
+
fillPaint={{ "fill-color": region }}
|
|
108
|
+
fillOpacity={0.3}
|
|
109
|
+
vignette={{ width: 10, opacity: 0.4 }}
|
|
110
|
+
linePaint={false}
|
|
111
|
+
/>
|
|
112
|
+
<MapGeoJSON
|
|
113
|
+
id="lake"
|
|
114
|
+
data={LAKE_ONTARIO}
|
|
115
|
+
fillPaint={{ "fill-color": lake }}
|
|
116
|
+
pattern={{ kind: "stripes", width: 2, gap: 4 }}
|
|
117
|
+
vignette={{ width: 14, opacity: 0.35 }}
|
|
118
|
+
linePaint={{ "line-color": lake, "line-width": 1 }}
|
|
119
|
+
/>
|
|
120
|
+
<MapMarker {...LOCATOR_PLACES.lakeLabel} label={{ text: "Lake Ontario", box: true }} />
|
|
121
|
+
<MapMarker {...LOCATOR_PLACES.gtaLabel} label={{ text: "Greater Toronto Area" }} />
|
|
122
|
+
<MapMarker {...LOCATOR_PLACES.toronto} label={{ text: "Toronto", position: "bottom-left" }}>
|
|
123
|
+
<MapMarkerContent />
|
|
124
|
+
<MapMarkerTooltip>Toronto — Ontario’s capital</MapMarkerTooltip>
|
|
125
|
+
</MapMarker>
|
|
126
|
+
<MapMarker
|
|
127
|
+
{...LOCATOR_PLACES.rochester}
|
|
128
|
+
showAt={{ base: true, narrow: false }}
|
|
129
|
+
label={{ text: "Rochester, NY", position: "right" }}
|
|
130
|
+
>
|
|
131
|
+
<MapMarkerContent />
|
|
132
|
+
</MapMarker>
|
|
133
|
+
<MapInset kind="globe" position="top-right" />
|
|
134
|
+
<MapScaleBar unit="km" position="bottom-left" />
|
|
135
|
+
<MapLegend
|
|
136
|
+
position={{ base: "top-left", narrow: "below" }}
|
|
137
|
+
items={[
|
|
138
|
+
{ label: "Lake Ontario", color: "var(--chart-1)", shape: "square" },
|
|
139
|
+
{ label: "Greater Toronto Area", color: "var(--chart-2)", shape: "square" },
|
|
140
|
+
]}
|
|
141
|
+
/>
|
|
142
|
+
</MapCanvas>
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* An editorial locator: static (no zoom or pan; tooltips still open),
|
|
149
|
+
* two area markers with a stripe pattern and a vignette glow, inline labels,
|
|
150
|
+
* a globe inset, a scale bar and a key. The map sets no height of its own:
|
|
151
|
+
* it takes 1.6 : 1, and turns square at `narrow` so a phone still gets a map
|
|
152
|
+
* worth reading; there the key moves under the map and Rochester's label
|
|
153
|
+
* (`showAt: { narrow: false }`) steps aside.
|
|
154
|
+
*/
|
|
155
|
+
export const StaticLocator: Story = {
|
|
156
|
+
parameters: { layout: "fullscreen" },
|
|
157
|
+
render: () => <LakeOntarioLocator />,
|
|
158
|
+
play: async ({ canvasElement }) => {
|
|
159
|
+
// Without WebGL the canvas degrades to an error panel and no furniture mounts.
|
|
160
|
+
const map = await waitFor(
|
|
161
|
+
() => {
|
|
162
|
+
const el = canvasElement.querySelector<HTMLElement>('[data-slot="map-canvas"]');
|
|
163
|
+
expect(el ?? canvasElement.querySelector('[role="alert"]')).not.toBeNull();
|
|
164
|
+
return el;
|
|
165
|
+
},
|
|
166
|
+
{ timeout: 5000 },
|
|
167
|
+
);
|
|
168
|
+
if (!map) return;
|
|
169
|
+
await expect(map).toHaveAttribute("data-interactive", "false");
|
|
170
|
+
const tier = map.getAttribute("data-map-breakpoint");
|
|
171
|
+
await waitFor(() =>
|
|
172
|
+
expect(canvasElement.querySelector('[data-slot="map-scale-bar"]')).not.toBeNull(),
|
|
173
|
+
);
|
|
174
|
+
const legend = canvasElement.querySelector('[data-slot="map-legend"]');
|
|
175
|
+
if (tier === "narrow") {
|
|
176
|
+
// Square at narrow; the key sits under the map. The tier is measured
|
|
177
|
+
// after mount, so wait for the box rather than reading it once.
|
|
178
|
+
await waitFor(() => {
|
|
179
|
+
const box = map.getBoundingClientRect();
|
|
180
|
+
expect(box.height).toBeGreaterThanOrEqual(box.width - 1);
|
|
181
|
+
});
|
|
182
|
+
await expect(canvasElement.querySelector('[data-slot="map-canvas-below"]')).not.toBeNull();
|
|
183
|
+
await expect(map.contains(legend)).toBe(false);
|
|
184
|
+
} else {
|
|
185
|
+
await expect(map.contains(legend)).toBe(true);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
function ViewportReadout() {
|
|
191
|
+
const [viewport, setViewport] = useState("not moved");
|
|
192
|
+
return (
|
|
193
|
+
<div className="flex w-full flex-col gap-2 p-4">
|
|
194
|
+
<div className="h-[360px]">
|
|
195
|
+
<MapCanvas
|
|
196
|
+
interactive={false}
|
|
197
|
+
center={[-79.38, 43.65]}
|
|
198
|
+
zoom={9}
|
|
199
|
+
onViewportChange={(v) =>
|
|
200
|
+
setViewport(
|
|
201
|
+
`${v.center[0].toFixed(3)}, ${v.center[1].toFixed(3)} @ ${v.zoom.toFixed(2)}`,
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
>
|
|
205
|
+
<MapMarker {...LOCATOR_PLACES.toronto}>
|
|
206
|
+
<MapMarkerContent />
|
|
207
|
+
<MapMarkerTooltip>Toronto</MapMarkerTooltip>
|
|
208
|
+
</MapMarker>
|
|
209
|
+
</MapCanvas>
|
|
210
|
+
</div>
|
|
211
|
+
<p className="text-caption text-muted-foreground">
|
|
212
|
+
Viewport: <output data-testid="viewport">{viewport}</output>
|
|
213
|
+
</p>
|
|
214
|
+
</div>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* `interactive={false}`: wheel, drag, double-click and keys leave the viewport
|
|
220
|
+
* alone and the cursor stays an arrow, but hovering the marker still opens
|
|
221
|
+
* its tooltip. The readout only changes when the viewport does.
|
|
222
|
+
*/
|
|
223
|
+
export const StaticMode: Story = {
|
|
224
|
+
parameters: { layout: "fullscreen" },
|
|
225
|
+
render: () => <ViewportReadout />,
|
|
226
|
+
play: async ({ canvasElement }) => {
|
|
227
|
+
const canvas = await waitFor(
|
|
228
|
+
() => {
|
|
229
|
+
const el = canvasElement.querySelector<HTMLCanvasElement>(".maplibregl-canvas");
|
|
230
|
+
const fallback = canvasElement.querySelector('[role="alert"]');
|
|
231
|
+
expect(el ?? fallback).not.toBeNull();
|
|
232
|
+
return el;
|
|
233
|
+
},
|
|
234
|
+
{ timeout: 5000 },
|
|
235
|
+
);
|
|
236
|
+
if (!canvas) return;
|
|
237
|
+
const readout = canvasElement.querySelector('[data-testid="viewport"]')!;
|
|
238
|
+
const before = readout.textContent;
|
|
239
|
+
canvas.dispatchEvent(
|
|
240
|
+
new WheelEvent("wheel", {
|
|
241
|
+
deltaY: -600,
|
|
242
|
+
bubbles: true,
|
|
243
|
+
cancelable: true,
|
|
244
|
+
clientX: 100,
|
|
245
|
+
clientY: 100,
|
|
246
|
+
}),
|
|
247
|
+
);
|
|
248
|
+
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
249
|
+
await expect(readout.textContent).toBe(before);
|
|
250
|
+
await expect(canvas.tabIndex).toBe(-1);
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* A map in a parent with no height of its own: `height` resolves per tier —
|
|
256
|
+
* here 320 px, and a square at `narrow`.
|
|
257
|
+
*/
|
|
258
|
+
export const ResponsiveHeight: Story = {
|
|
259
|
+
parameters: { layout: "fullscreen" },
|
|
260
|
+
render: () => (
|
|
261
|
+
<div className="w-full p-4">
|
|
262
|
+
<MapCanvas center={[-79.38, 43.65]} zoom={8} height={{ base: 320, narrow: { aspect: 1 } }} />
|
|
263
|
+
</div>
|
|
264
|
+
),
|
|
265
|
+
};
|