@osm-editor-kit/street-imagery-react 0.1.0-alpha.5 → 0.1.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +45 -0
  2. package/dist/MapillaryPanel-N45NQY4C.js +2 -0
  3. package/dist/PanoramaxPanel-MFI4L67Z.js +2 -0
  4. package/dist/StreetLevelImagerySelectionOverlay.d.ts +20 -3
  5. package/dist/StreetLevelImagerySourcesAndLayers.d.ts +9 -1
  6. package/dist/StreetLevelImageryViewCone.d.ts +11 -1
  7. package/dist/StreetLevelImageryViewer.d.ts +7 -2
  8. package/dist/{chunk-72HJV6II.js → chunk-53RA4OSJ.js} +6 -2
  9. package/dist/chunk-EQ2JKLGT.js +311 -0
  10. package/dist/chunk-HA4GJWHI.js +120 -0
  11. package/dist/hooks/useAllProviderMapFeatures.d.ts +0 -4
  12. package/dist/i18n/PhotoDate.d.ts +16 -0
  13. package/dist/i18n/StreetImageryLocaleProvider.d.ts +21 -0
  14. package/dist/i18n/messages.d.ts +35 -0
  15. package/dist/index.d.ts +10 -4
  16. package/dist/index.js +470 -167
  17. package/dist/mapillary/MapillaryFeatureBar.d.ts +13 -0
  18. package/dist/openers/LocationPickOnMap.d.ts +10 -0
  19. package/dist/openers/useLocationPickStore.d.ts +9 -0
  20. package/dist/panels/MapillaryPanel.d.ts +7 -1
  21. package/dist/panels/PanoramaxPanel.d.ts +8 -1
  22. package/dist/panels/panoramaxLimitPanning.d.ts +54 -0
  23. package/dist/panels/panoramaxPhotoFromMetadata.d.ts +8 -0
  24. package/dist/photo-details/PhotoDetailsDialog.d.ts +21 -0
  25. package/dist/photo-details/index.d.ts +6 -0
  26. package/dist/photo-details/labels.d.ts +33 -0
  27. package/dist/photo-details.js +235 -0
  28. package/dist/viewpoints/FloatingPhotoViewer.d.ts +25 -2
  29. package/dist/viewpoints/useViewpointSessionStore.d.ts +2 -9
  30. package/dist/viewpoints/viewDirectionLabels.d.ts +4 -4
  31. package/package.json +10 -2
  32. package/dist/MapillaryPanel-I5GKT6YT.js +0 -2
  33. package/dist/PanoramaxPanel-J6ANR6LB.js +0 -2
  34. package/dist/chunk-ZH3TPW4D.js +0 -165
package/dist/index.js CHANGED
@@ -1,67 +1,127 @@
1
- export { MapillaryPanel, mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo } from './chunk-72HJV6II.js';
2
- export { PanoramaxPanel } from './chunk-ZH3TPW4D.js';
1
+ import { useStreetImageryI18n } from './chunk-HA4GJWHI.js';
2
+ export { STREET_IMAGERY_MESSAGES, StreetImageryLocaleProvider, useStreetImageryI18n } from './chunk-HA4GJWHI.js';
3
+ export { MapillaryPanel, mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo } from './chunk-53RA4OSJ.js';
4
+ export { PanoramaxPanel } from './chunk-EQ2JKLGT.js';
3
5
  export { useViewerActions, useViewerBearing, useViewerHfov, useViewerLngLat } from './chunk-25UGGXIE.js';
4
- import { photosToFeatureCollection, emptyPointCollection, sequencesToFeatureCollection, emptyLineCollection, adapterById, viewConeGeoJson, coneRadiusMeters, setStreetImageryConfig, photoLayerId, viewfieldLayerId, featureLayerId, PROVIDER_IDS, mapFeatureMatchesDateRange, isBrowserAvailableProvider, photoMatchesFilters, resolvePhotoPanoramaUrl, photoFullUrlQueryKey, resolvePhotoThumbnailUrl, photoThumbnailQueryKey, mapillaryPhotoSource, buildViewSuggestions, yearsToMs, SIGN_TARGET, fetchMapFeatureImages, fetchMapillaryMapFeaturesInBbox, bboxAround, distanceMeters, fetchImageDetections, mapFeaturesToFeatureCollection, buildMapFeatureLayerFilter, featureSourceId, photosToViewfieldsFeatureCollection, emptyPolygonCollection, buildPhotoLayerFilter, photoSourceId, viewfieldSourceId, sequenceSourceId, sequenceLayerId, viewfieldLineLayerId, VIEWFIELD_MIN_ZOOM } from '@osm-editor-kit/street-imagery';
6
+ import { distanceMeters, sequencesToFeatureCollection, emptyLineCollection, adapterById, viewConeGeoJson, viewShapeRadiusMeters, setStreetImageryConfig, viewfieldLayerId, photoLayerId, featureLayerId, PROVIDER_IDS, mapFeatureMatchesDateRange, isBrowserAvailableProvider, photoMatchesFilters, resolvePhotoPanoramaUrl, photoFullUrlQueryKey, resolvePhotoThumbnailUrl, photoThumbnailQueryKey, formatLocalDateTime, formatMonth, formatRelativeDate, formatDateTooltip, mapillaryPhotoSource, buildViewSuggestions, yearsToMs, SIGN_TARGET, fetchMapFeatureImages, fetchMapillaryMapFeaturesInBbox, bboxAround, fetchImageDetections, findLocationOpener, mapillaryIconUrl, openLocationInNewTab, alignLineToPoints, coneRadiusMeters, mapFeaturesToFeatureCollection, emptyPointCollection, buildMapFeatureLayerFilter, featureSourceId, photosToFeatureCollection, photosToViewfieldsFeatureCollection, emptyPolygonCollection, buildPhotoLayerFilter, photoSourceId, viewfieldSourceId, sequenceSourceId, sequenceLayerId, viewfieldLineLayerId, VIEWFIELD_MIN_ZOOM } from '@osm-editor-kit/street-imagery';
5
7
  import { Source, Layer, useMap } from 'react-map-gl/maplibre';
6
8
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
7
9
  import { lazy, useEffect, Fragment as Fragment$1, Suspense, useState, useRef, useEffectEvent } from 'react';
8
10
  import { useQuery, useQueries } from '@tanstack/react-query';
9
11
  import { create } from 'zustand';
10
12
 
11
- var HIGHLIGHT_SOURCE_ID = "selection-highlight";
12
- var HIGHLIGHT_LAYER_ID = "selection-highlight-layer";
13
+ var POINTS_SOURCE_ID = "selection-highlight";
14
+ var CONNECTOR_SOURCE_ID = "selection-connector";
13
15
  var SEQUENCE_HIGHLIGHT_SOURCE_ID = "sequence-highlight";
14
- var SEQUENCE_HIGHLIGHT_LAYER_ID = "sequence-highlight-layer";
16
+ var BASE_COLOR = "#171717";
17
+ var SELECTION_COLOR = "#f97316";
18
+ var SAME_POSITION_METERS = 0.75;
19
+ var ACTIVE_LINE_WIDTH = 2.5;
15
20
  var StreetLevelImagerySelectionOverlay = ({
16
21
  selectedPhoto,
17
- selectedSequence
22
+ selectedSequence,
23
+ cameraLngLat,
24
+ sequenceBeforeId
18
25
  }) => {
19
- const highlightCollection = selectedPhoto ? photosToFeatureCollection([selectedPhoto]) : emptyPointCollection();
20
- const sequenceHighlightCollection = selectedSequence ? sequencesToFeatureCollection([selectedSequence]) : emptyLineCollection();
21
- if (!selectedPhoto && sequenceHighlightCollection.features.length === 0) {
26
+ const photoLngLat = selectedPhoto?.lngLat;
27
+ const cameraDiffers = photoLngLat != null && cameraLngLat != null && distanceMeters(photoLngLat, cameraLngLat) > SAME_POSITION_METERS;
28
+ const camera = cameraDiffers ? cameraLngLat : null;
29
+ const dot = photoLngLat && cameraLngLat && !cameraDiffers ? cameraLngLat : photoLngLat;
30
+ const sequenceLines = selectedSequence ? sequencesToFeatureCollection([selectedSequence]) : emptyLineCollection();
31
+ const throughDot = (line) => dot ? alignLineToPoints(line, [dot]) : line;
32
+ const sequenceCollection = {
33
+ ...sequenceLines,
34
+ features: sequenceLines.features.map((feature) => ({
35
+ ...feature,
36
+ geometry: feature.geometry.type === "LineString" ? { ...feature.geometry, coordinates: throughDot(feature.geometry.coordinates) } : { ...feature.geometry, coordinates: feature.geometry.coordinates.map(throughDot) }
37
+ }))
38
+ };
39
+ if (!selectedPhoto && sequenceCollection.features.length === 0) {
22
40
  return null;
23
41
  }
24
- return /* @__PURE__ */ jsxs(Fragment, { children: [
25
- selectedPhoto ? /* @__PURE__ */ jsxs(Fragment, { children: [
26
- /* @__PURE__ */ jsx(Source, { id: HIGHLIGHT_SOURCE_ID, type: "geojson", data: highlightCollection }),
27
- /* @__PURE__ */ jsx(
28
- Layer,
42
+ const points = {
43
+ type: "FeatureCollection",
44
+ features: [
45
+ ...dot ? [
29
46
  {
30
- id: HIGHLIGHT_LAYER_ID,
31
- type: "circle",
32
- source: HIGHLIGHT_SOURCE_ID,
33
- paint: {
34
- "circle-radius": 10,
35
- "circle-color": "#ffffff",
36
- "circle-stroke-width": 3,
37
- "circle-stroke-color": "#0f172a"
38
- }
47
+ type: "Feature",
48
+ properties: { kind: "dot" },
49
+ geometry: { type: "Point", coordinates: dot }
39
50
  }
40
- )
41
- ] }) : null,
42
- sequenceHighlightCollection.features.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
43
- /* @__PURE__ */ jsx(
44
- Source,
51
+ ] : [],
52
+ ...camera ? [
45
53
  {
46
- id: SEQUENCE_HIGHLIGHT_SOURCE_ID,
47
- type: "geojson",
48
- data: sequenceHighlightCollection
54
+ type: "Feature",
55
+ properties: { kind: "camera" },
56
+ geometry: { type: "Point", coordinates: camera }
49
57
  }
50
- ),
51
- /* @__PURE__ */ jsx(
52
- Layer,
53
- {
54
- id: SEQUENCE_HIGHLIGHT_LAYER_ID,
55
- type: "line",
56
- source: SEQUENCE_HIGHLIGHT_SOURCE_ID,
57
- paint: {
58
- "line-color": "#0f172a",
59
- "line-width": 4,
60
- "line-opacity": 0.75
61
- }
58
+ ] : []
59
+ ]
60
+ };
61
+ const connector = {
62
+ type: "FeatureCollection",
63
+ features: dot && camera ? [
64
+ {
65
+ type: "Feature",
66
+ properties: {},
67
+ geometry: { type: "LineString", coordinates: [dot, camera] }
68
+ }
69
+ ] : []
70
+ };
71
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
72
+ /* @__PURE__ */ jsx(Source, { id: SEQUENCE_HIGHLIGHT_SOURCE_ID, type: "geojson", data: sequenceCollection }),
73
+ /* @__PURE__ */ jsx(
74
+ Layer,
75
+ {
76
+ beforeId: sequenceBeforeId,
77
+ id: "sequence-highlight-layer",
78
+ type: "line",
79
+ source: SEQUENCE_HIGHLIGHT_SOURCE_ID,
80
+ layout: { "line-cap": "round", "line-join": "round" },
81
+ paint: { "line-color": BASE_COLOR, "line-width": ACTIVE_LINE_WIDTH }
82
+ }
83
+ ),
84
+ /* @__PURE__ */ jsx(Source, { id: CONNECTOR_SOURCE_ID, type: "geojson", data: connector }),
85
+ /* @__PURE__ */ jsx(
86
+ Layer,
87
+ {
88
+ id: "selection-connector-layer",
89
+ type: "line",
90
+ source: CONNECTOR_SOURCE_ID,
91
+ paint: { "line-color": SELECTION_COLOR, "line-width": 1.5, "line-dasharray": [2, 1.5] }
92
+ }
93
+ ),
94
+ /* @__PURE__ */ jsx(Source, { id: POINTS_SOURCE_ID, type: "geojson", data: points }),
95
+ /* @__PURE__ */ jsx(
96
+ Layer,
97
+ {
98
+ id: "selection-highlight-layer",
99
+ type: "circle",
100
+ source: POINTS_SOURCE_ID,
101
+ filter: ["==", ["get", "kind"], "dot"],
102
+ paint: {
103
+ "circle-radius": 6,
104
+ "circle-color": SELECTION_COLOR,
105
+ "circle-stroke-width": 2,
106
+ "circle-stroke-color": BASE_COLOR
62
107
  }
63
- )
64
- ] }) : null
108
+ }
109
+ ),
110
+ /* @__PURE__ */ jsx(
111
+ Layer,
112
+ {
113
+ id: "selection-camera-layer",
114
+ type: "circle",
115
+ source: POINTS_SOURCE_ID,
116
+ filter: ["==", ["get", "kind"], "camera"],
117
+ paint: {
118
+ "circle-radius": 3.5,
119
+ "circle-color": SELECTION_COLOR,
120
+ "circle-stroke-width": 1.5,
121
+ "circle-stroke-color": BASE_COLOR
122
+ }
123
+ }
124
+ )
65
125
  ] });
66
126
  };
67
127
  var resolveSelectedSequence = (selectedPhoto, sequences, sequenceId) => {
@@ -116,7 +176,8 @@ var useProviderMapFeatures = (providerId, bbox, zoom) => {
116
176
  };
117
177
  var CONE_SOURCE_ID = "view-direction-cone";
118
178
  var CONE_FILL_LAYER_ID = "view-direction-cone-fill";
119
- var CONE_LINE_LAYER_ID = "view-direction-cone-line";
179
+ var MIN_CONE_FOV_DEG = 45;
180
+ var VIEW_SHAPE_FILL_OPACITY = 0.25;
120
181
  var INTERACTIVE_PANO_PROVIDERS = /* @__PURE__ */ new Set([
121
182
  "mapillary",
122
183
  "panoramax",
@@ -128,7 +189,9 @@ var INTERACTIVE_PANO_PROVIDERS = /* @__PURE__ */ new Set([
128
189
  var StreetLevelImageryViewCone = ({
129
190
  selectedPhoto,
130
191
  zoom,
131
- viewerPov
192
+ viewerPov,
193
+ color = SELECTION_COLOR,
194
+ scale = 2.5
132
195
  }) => {
133
196
  const apex = viewerPov?.lngLat ?? selectedPhoto.lngLat;
134
197
  const isPano = selectedPhoto.isPano === true;
@@ -148,7 +211,11 @@ var StreetLevelImageryViewCone = ({
148
211
  if (bearing == null) {
149
212
  return null;
150
213
  }
151
- const coneFeature = viewConeGeoJson(apex, bearing, fov, coneRadiusMeters(zoom));
214
+ fov = Math.max(fov, MIN_CONE_FOV_DEG);
215
+ const coneFeature = {
216
+ ...viewConeGeoJson(apex, bearing, fov, viewShapeRadiusMeters(zoom) * scale),
217
+ properties: { isPano: selectedPhoto.isPano, capturedAt: selectedPhoto.capturedAt }
218
+ };
152
219
  return /* @__PURE__ */ jsxs(Fragment, { children: [
153
220
  /* @__PURE__ */ jsx(Source, { id: CONE_SOURCE_ID, type: "geojson", data: coneFeature }),
154
221
  /* @__PURE__ */ jsx(
@@ -158,37 +225,14 @@ var StreetLevelImageryViewCone = ({
158
225
  type: "fill",
159
226
  source: CONE_SOURCE_ID,
160
227
  paint: {
161
- "fill-color": "#0f172a",
162
- "fill-opacity": 0.15
163
- }
164
- }
165
- ),
166
- /* @__PURE__ */ jsx(
167
- Layer,
168
- {
169
- id: CONE_LINE_LAYER_ID,
170
- type: "line",
171
- source: CONE_SOURCE_ID,
172
- paint: {
173
- "line-color": "#0f172a",
174
- "line-width": 1.5,
175
- "line-opacity": 0.5
228
+ "fill-color": color,
229
+ "fill-opacity": VIEW_SHAPE_FILL_OPACITY,
230
+ "fill-outline-color": "rgba(0, 0, 0, 0)"
176
231
  }
177
232
  }
178
233
  )
179
234
  ] });
180
235
  };
181
- var CIRCLE_RADIUS = [
182
- "interpolate",
183
- ["linear"],
184
- ["zoom"],
185
- 10,
186
- 2,
187
- 14,
188
- 4,
189
- 18,
190
- 6
191
- ];
192
236
  var FEATURE_CIRCLE_RADIUS = [
193
237
  "interpolate",
194
238
  ["linear"],
@@ -206,6 +250,24 @@ var padBbox = ([west, south, east, north], factor) => {
206
250
  return [west - dx, south - dy, east + dx, north + dy];
207
251
  };
208
252
  var lngLatInBbox = ([lng, lat], [west, south, east, north]) => lng >= west && lng <= east && lat >= south && lat <= north;
253
+ var YEAR_MS = 365.25 * 24 * 60 * 60 * 1e3;
254
+ var LOADED_AT = Date.now();
255
+ var AGE_SHADE = [
256
+ "case",
257
+ ["==", ["get", "capturedAt"], null],
258
+ BASE_COLOR,
259
+ [
260
+ "step",
261
+ ["get", "capturedAt"],
262
+ "#909090",
263
+ LOADED_AT - 3 * YEAR_MS,
264
+ "#686868",
265
+ LOADED_AT - 2 * YEAR_MS,
266
+ "#404040",
267
+ LOADED_AT - YEAR_MS,
268
+ BASE_COLOR
269
+ ]
270
+ ];
209
271
  var PHOTO_SORT_KEY = ["coalesce", ["get", "capturedAt"], 0];
210
272
  var FEATURE_SORT_KEY = ["coalesce", ["get", "lastSeenAt"], 0];
211
273
  var PhotoProviderLayer = ({
@@ -215,7 +277,10 @@ var PhotoProviderLayer = ({
215
277
  filter,
216
278
  showSequences,
217
279
  showViewfields,
218
- photoCircleColor
280
+ photoCircleColor,
281
+ activeSequenceId,
282
+ hasActiveSequence,
283
+ shownPhotoId
219
284
  }) => {
220
285
  const adapter = adapterById[providerId];
221
286
  const { data: photos = [] } = useProviderPhotos(providerId, bbox, zoom);
@@ -227,6 +292,41 @@ var PhotoProviderLayer = ({
227
292
  ) : emptyPointCollection();
228
293
  const viewfieldsActive = showViewfields && zoom >= VIEWFIELD_MIN_ZOOM;
229
294
  const viewfieldCollection = viewfieldsActive ? photosToViewfieldsFeatureCollection(visiblePhotos, zoom, { bbox }) : emptyPolygonCollection();
295
+ const inActiveSequence = [
296
+ "==",
297
+ ["get", "sequenceId"],
298
+ activeSequenceId ?? ""
299
+ ];
300
+ const lineOpacity = [
301
+ "case",
302
+ inActiveSequence,
303
+ 1,
304
+ hasActiveSequence ? 0.45 : 1
305
+ ];
306
+ const dotRadius = [
307
+ "interpolate",
308
+ ["linear"],
309
+ ["zoom"],
310
+ 10,
311
+ ["case", inActiveSequence, 2, 1.2],
312
+ 14,
313
+ ["case", inActiveSequence, 4, 2],
314
+ // Up to here there are no view shapes and the dots are 20 % smaller.
315
+ VIEWFIELD_MIN_ZOOM - 0.01,
316
+ ["case", inActiveSequence, 5.5, 2.9],
317
+ VIEWFIELD_MIN_ZOOM,
318
+ ["case", inActiveSequence, 5.5, 3.6],
319
+ 18,
320
+ ["case", inActiveSequence, 6, 4]
321
+ ];
322
+ const lineAndDotColor = viewfieldsActive ? ["case", inActiveSequence, BASE_COLOR, AGE_SHADE] : photoCircleColor;
323
+ const viewShapeOpacity = (full) => [
324
+ "case",
325
+ inActiveSequence,
326
+ full,
327
+ hasActiveSequence ? full * 0.4 : full
328
+ ];
329
+ const notShownPhoto = ["!=", ["get", "photoId"], shownPhotoId ?? ""];
230
330
  const photoFilter = buildPhotoLayerFilter(filter?.photoTypes, filter?.date);
231
331
  const photoSrcId = photoSourceId(providerId);
232
332
  const viewfieldSrcId = viewfieldSourceId(providerId);
@@ -254,7 +354,34 @@ var PhotoProviderLayer = ({
254
354
  };
255
355
  return photoMatchesFilters(asPhoto, filter?.photoTypes, filter?.date);
256
356
  }) : [];
257
- const sequenceCollection = filteredSequences.length > 0 ? sequencesToFeatureCollection(filteredSequences) : emptyLineCollection();
357
+ const photoLngLatsBySequence = /* @__PURE__ */ new Map();
358
+ for (const photo of photos) {
359
+ if (photo.sequenceId == null) continue;
360
+ const list = photoLngLatsBySequence.get(photo.sequenceId);
361
+ if (list) {
362
+ list.push(photo.lngLat);
363
+ } else {
364
+ photoLngLatsBySequence.set(photo.sequenceId, [photo.lngLat]);
365
+ }
366
+ }
367
+ const alignedSequences = filteredSequences.map((sequence) => {
368
+ const points = photoLngLatsBySequence.get(sequence.sequenceId);
369
+ if (!points) return sequence;
370
+ const { geometry } = sequence;
371
+ return {
372
+ ...sequence,
373
+ geometry: geometry.type === "LineString" ? {
374
+ ...geometry,
375
+ coordinates: alignLineToPoints(geometry.coordinates, points)
376
+ } : {
377
+ ...geometry,
378
+ coordinates: geometry.coordinates.map(
379
+ (line) => alignLineToPoints(line, points)
380
+ )
381
+ }
382
+ };
383
+ });
384
+ const sequenceCollection = alignedSequences.length > 0 ? sequencesToFeatureCollection(alignedSequences) : emptyLineCollection();
258
385
  return /* @__PURE__ */ jsxs(Fragment, { children: [
259
386
  adapter.fetchSequences && showSequences ? /* @__PURE__ */ jsxs(Fragment, { children: [
260
387
  /* @__PURE__ */ jsx(
@@ -272,10 +399,15 @@ var PhotoProviderLayer = ({
272
399
  id: sequenceLayerId(providerId),
273
400
  type: "line",
274
401
  source: sequenceSourceId(providerId),
402
+ layout: {
403
+ "line-cap": "round",
404
+ "line-join": "round",
405
+ "line-sort-key": ["case", inActiveSequence, 1, 0]
406
+ },
275
407
  paint: {
276
- "line-color": adapter.color,
277
- "line-width": 2,
278
- "line-opacity": 0.35
408
+ "line-color": lineAndDotColor,
409
+ "line-width": ["case", inActiveSequence, ACTIVE_LINE_WIDTH, 1.25],
410
+ "line-opacity": lineOpacity
279
411
  }
280
412
  }
281
413
  )
@@ -296,9 +428,11 @@ var PhotoProviderLayer = ({
296
428
  id: viewfieldLayerId(providerId),
297
429
  type: "fill",
298
430
  source: viewfieldSrcId,
431
+ filter: notShownPhoto,
299
432
  paint: {
300
433
  "fill-color": photoCircleColor,
301
- "fill-opacity": 0.22
434
+ "fill-opacity": viewShapeOpacity(VIEW_SHAPE_FILL_OPACITY),
435
+ "fill-outline-color": "rgba(0, 0, 0, 0)"
302
436
  }
303
437
  }
304
438
  ),
@@ -308,10 +442,11 @@ var PhotoProviderLayer = ({
308
442
  id: viewfieldLineLayerId(providerId),
309
443
  type: "line",
310
444
  source: viewfieldSrcId,
445
+ filter: notShownPhoto,
311
446
  paint: {
312
447
  "line-color": photoCircleColor,
313
- "line-width": 1,
314
- "line-opacity": 0.45
448
+ "line-width": 0.5,
449
+ "line-opacity": viewShapeOpacity(0.3)
315
450
  }
316
451
  }
317
452
  )
@@ -335,10 +470,8 @@ var PhotoProviderLayer = ({
335
470
  filter: photoFilter,
336
471
  layout: { "circle-sort-key": PHOTO_SORT_KEY },
337
472
  paint: {
338
- "circle-radius": CIRCLE_RADIUS,
339
- "circle-color": photoCircleColor,
340
- "circle-stroke-width": 1,
341
- "circle-stroke-color": "#ffffff"
473
+ "circle-radius": dotRadius,
474
+ "circle-color": lineAndDotColor
342
475
  }
343
476
  }
344
477
  )
@@ -380,9 +513,7 @@ var MapFeatureProviderLayer = ({
380
513
  layout: { "circle-sort-key": FEATURE_SORT_KEY },
381
514
  paint: {
382
515
  "circle-radius": FEATURE_CIRCLE_RADIUS,
383
- "circle-color": mapFeatureCircleColor,
384
- "circle-stroke-width": 1,
385
- "circle-stroke-color": "#ffffff"
516
+ "circle-color": mapFeatureCircleColor
386
517
  }
387
518
  }
388
519
  )
@@ -408,6 +539,8 @@ var StreetLevelImagerySourcesAndLayers = ({
408
539
  selectedPhoto,
409
540
  selectedSequenceId,
410
541
  viewerPov,
542
+ viewConeColor,
543
+ viewConeScale,
411
544
  photoCircleColor,
412
545
  mapFeatureCircleColor
413
546
  } = options;
@@ -425,11 +558,13 @@ var StreetLevelImagerySourcesAndLayers = ({
425
558
  showSelectionHighlight && selectedProviderId ? bbox : null,
426
559
  zoom
427
560
  );
428
- const selectedSequence = resolveSelectedSequence(
429
- selectedPhoto,
430
- sequences,
431
- selectedSequenceId ?? selectedPhoto?.sequenceId
561
+ const activeSequenceId = selectedSequenceId ?? selectedPhoto?.sequenceId;
562
+ const selectedSequence = resolveSelectedSequence(selectedPhoto, sequences, activeSequenceId);
563
+ const providerDrawsSequences = selectedProviderId != null && showSequences && providers.includes(selectedProviderId);
564
+ const firstPhotoProvider = providers.find(
565
+ (id) => adapterById[id].kind === "photo" && adapterById[id].fetchPhotos
432
566
  );
567
+ const sequenceBeforeId = firstPhotoProvider ? showViewfields ? viewfieldLayerId(firstPhotoProvider) : photoLayerId(firstPhotoProvider) : void 0;
433
568
  return /* @__PURE__ */ jsxs(Fragment, { children: [
434
569
  providers.map((providerId) => /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(
435
570
  ProviderLayer,
@@ -437,6 +572,9 @@ var StreetLevelImagerySourcesAndLayers = ({
437
572
  bbox,
438
573
  filter,
439
574
  mapFeatureCircleColor,
575
+ activeSequenceId: providerId === selectedProviderId ? activeSequenceId : null,
576
+ hasActiveSequence: activeSequenceId != null,
577
+ shownPhotoId: showViewCone && providerId === selectedProviderId ? selectedPhoto?.photoId : null,
440
578
  photoCircleColor,
441
579
  providerId,
442
580
  showSequences,
@@ -447,6 +585,8 @@ var StreetLevelImagerySourcesAndLayers = ({
447
585
  showViewCone && selectedPhoto ? /* @__PURE__ */ jsx(
448
586
  StreetLevelImageryViewCone,
449
587
  {
588
+ color: viewConeColor ?? SELECTION_COLOR,
589
+ scale: viewConeScale,
450
590
  selectedPhoto,
451
591
  viewerPov,
452
592
  zoom
@@ -455,23 +595,28 @@ var StreetLevelImagerySourcesAndLayers = ({
455
595
  showSelectionHighlight ? /* @__PURE__ */ jsx(
456
596
  StreetLevelImagerySelectionOverlay,
457
597
  {
598
+ cameraLngLat: viewerPov?.lngLat,
458
599
  selectedPhoto,
459
- selectedSequence
600
+ selectedSequence: providerDrawsSequences ? null : selectedSequence,
601
+ sequenceBeforeId
460
602
  }
461
603
  ) : null
462
604
  ] });
463
605
  };
464
606
  var MapillaryPanel2 = lazy(
465
- () => import('./MapillaryPanel-I5GKT6YT.js').then((module) => ({
607
+ () => import('./MapillaryPanel-N45NQY4C.js').then((module) => ({
466
608
  default: module.MapillaryPanel
467
609
  }))
468
610
  );
469
611
  var PanoramaxPanel2 = lazy(
470
- () => import('./PanoramaxPanel-J6ANR6LB.js').then((module) => ({
612
+ () => import('./PanoramaxPanel-MFI4L67Z.js').then((module) => ({
471
613
  default: module.PanoramaxPanel
472
614
  }))
473
615
  );
474
- var ViewerPanelPlaceholder = () => /* @__PURE__ */ jsx("div", { className: "flex min-h-48 animate-pulse items-center justify-center rounded-lg border border-slate-200 bg-slate-100", children: /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: "Loading viewer\u2026" }) });
616
+ var ViewerPanelPlaceholder = () => {
617
+ const { messages } = useStreetImageryI18n();
618
+ return /* @__PURE__ */ jsx("div", { className: "flex min-h-48 animate-pulse items-center justify-center rounded-lg border border-slate-200 bg-slate-100", children: /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: messages.viewer.loading }) });
619
+ };
475
620
  var StreetLevelImageryViewer = ({
476
621
  photo,
477
622
  groupPhotos,
@@ -480,13 +625,15 @@ var StreetLevelImageryViewer = ({
480
625
  onViewerPhoto,
481
626
  lookAtBearing,
482
627
  lookAt,
483
- outlines
628
+ outlines,
629
+ hideAttribution
484
630
  }) => {
485
631
  if (photo.providerId === "mapillary") {
486
632
  return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ViewerPanelPlaceholder, {}), children: /* @__PURE__ */ jsx(
487
633
  MapillaryPanel2,
488
634
  {
489
635
  groupPhotos,
636
+ hideAttribution,
490
637
  onEaseMapToPoint,
491
638
  onPhotoSelected,
492
639
  lookAt,
@@ -502,6 +649,8 @@ var StreetLevelImageryViewer = ({
502
649
  PanoramaxPanel2,
503
650
  {
504
651
  groupPhotos,
652
+ hideLegend: hideAttribution,
653
+ onViewerPhoto,
505
654
  onEaseMapToPoint,
506
655
  onPhotoSelected,
507
656
  photo
@@ -589,18 +738,6 @@ var useAllProviderMapFeatures = (providerIds, bbox, zoom, date) => {
589
738
  return data.filter((feature) => mapFeatureMatchesDateRange(feature, date));
590
739
  });
591
740
  };
592
- var useAllProviderMapFeaturesLoading = (providerIds, bbox, zoom) => {
593
- const enabled = new Set(providerIds);
594
- const queries = PROVIDER_IDS.map((providerId) => ({
595
- providerId,
596
- query: useProviderMapFeaturesMaybe(providerId, enabled.has(providerId), bbox, zoom)
597
- }));
598
- const activeQueries = queries.filter(({ providerId }) => enabled.has(providerId));
599
- return {
600
- isLoading: activeQueries.some(({ query }) => query.isLoading),
601
- isFetching: activeQueries.some(({ query }) => query.isFetching)
602
- };
603
- };
604
741
  var useProviderPhotosMaybe = (providerId, enabled, bbox, zoom) => useProviderPhotos(providerId, enabled ? bbox : null, zoom);
605
742
  var useAllProviderPhotos = (providerIds, bbox, zoom, photoTypes, date) => {
606
743
  const enabled = new Set(providerIds);
@@ -677,32 +814,26 @@ var usePhotoThumbnail = (photo) => useQuery({
677
814
  enabled: photo != null,
678
815
  staleTime: 24 * 60 * 60 * 1e3
679
816
  });
680
-
681
- // src/viewpoints/viewDirectionLabels.ts
682
- var ROLE_LABELS = {
683
- here: "Here",
684
- "line-start": "Start",
685
- "line-end": "End",
686
- "into-node": "Junction",
687
- custom: "View"
688
- };
689
- var viewpointRoleLabel = (role) => ROLE_LABELS[role];
690
- var viewSuggestionLabel = ({ viewpoint, direction }) => {
691
- const role = viewpoint.label ?? viewpointRoleLabel(viewpoint.role);
692
- switch (direction.kind) {
693
- case "forward":
694
- return `${role}, looking along the street`;
695
- case "back":
696
- return `${role}, looking back along the street`;
697
- case "into":
698
- return `${role}, looking into the junction`;
699
- case "N":
700
- case "E":
701
- case "S":
702
- case "W":
703
- return `${role}, looking ${direction.kind}`;
817
+ var PhotoDate = ({ timestamp, className, localDateTime }) => {
818
+ const { locale, messages } = useStreetImageryI18n();
819
+ if (timestamp == null) {
820
+ return /* @__PURE__ */ jsx("span", { className, children: messages.unknownDate });
704
821
  }
822
+ const precise = localDateTime ? formatLocalDateTime(localDateTime, locale) : null;
823
+ return /* @__PURE__ */ jsx(
824
+ "time",
825
+ {
826
+ className,
827
+ dateTime: new Date(timestamp).toISOString(),
828
+ title: precise ? `${precise}
829
+ ${formatRelativeDate(timestamp, locale)}` : formatDateTooltip(timestamp, locale),
830
+ children: formatMonth(timestamp, locale)
831
+ }
832
+ );
705
833
  };
834
+
835
+ // src/viewpoints/viewDirectionLabels.ts
836
+ var viewSuggestionLabel = ({ viewpoint, direction }, messages) => messages.viewDirection(viewpoint.label ?? messages.viewpointRole[viewpoint.role], direction.kind);
706
837
  var DEFAULT_LAYOUT = { right: 16, bottom: 32, width: 420, minimized: false };
707
838
  var MIN_WIDTH = 280;
708
839
  var EDGE = 8;
@@ -736,11 +867,14 @@ var Icon = ({ path, className = "size-4" }) => /* @__PURE__ */ jsx(
736
867
  );
737
868
  var ICONS = {
738
869
  back: "M15 18l-6-6 6-6",
870
+ undo: "M9 14 4 9l5-5M4 9h10.5a5.5 5.5 0 0 1 0 11H11",
871
+ redo: "m15 14 5-5-5-5M20 9H9.5a5.5 5.5 0 0 0 0 11H13",
739
872
  forward: "M9 18l6-6-6-6",
740
873
  close: "M18 6 6 18M6 6l12 12",
741
874
  minimize: "M5 12h14",
742
875
  expand: "M4 14v6h6M20 10V4h-6M14 10l6-6M10 14l-6 6",
743
- arrow: "M12 20V5M6 11l6-6 6 6"
876
+ arrow: "M12 20V5M6 11l6-6 6 6",
877
+ info: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18ZM12 11v5M12 8h.01"
744
878
  };
745
879
  var ToolbarButton = ({
746
880
  label,
@@ -759,18 +893,18 @@ var ToolbarButton = ({
759
893
  children
760
894
  }
761
895
  );
762
- var formatShortDate = (capturedAt) => capturedAt == null ? "\u2014" : new Intl.DateTimeFormat("en", { year: "2-digit", month: "short" }).format(capturedAt);
763
896
  var SuggestionChip = ({
764
897
  suggestion,
765
898
  active,
766
899
  onSelect
767
900
  }) => {
901
+ const { messages } = useStreetImageryI18n();
768
902
  const best = suggestion.candidates[0];
769
- const label = viewSuggestionLabel(suggestion);
903
+ const label = viewSuggestionLabel(suggestion, messages);
770
904
  return /* @__PURE__ */ jsxs(
771
905
  "button",
772
906
  {
773
- "aria-label": best ? label : `${label} (no photo)`,
907
+ "aria-label": best ? label : messages.viewer.noPhoto(label),
774
908
  "aria-pressed": active,
775
909
  className: [
776
910
  "inline-flex shrink-0 items-center gap-1 rounded-full border px-2 py-0.5 text-xs whitespace-nowrap",
@@ -778,10 +912,10 @@ var SuggestionChip = ({
778
912
  ].join(" "),
779
913
  disabled: !best,
780
914
  onClick: onSelect,
781
- title: best ? label : `${label}: no matching photo`,
915
+ title: best ? label : messages.viewer.noMatchingPhoto(label),
782
916
  type: "button",
783
917
  children: [
784
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: viewpointRoleLabel(suggestion.viewpoint.role) }),
918
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: suggestion.viewpoint.label ?? messages.viewpointRole[suggestion.viewpoint.role] }),
785
919
  /* @__PURE__ */ jsx(
786
920
  "span",
787
921
  {
@@ -791,13 +925,24 @@ var SuggestionChip = ({
791
925
  children: /* @__PURE__ */ jsx(Icon, { className: "size-3", path: ICONS.arrow })
792
926
  }
793
927
  ),
794
- /* @__PURE__ */ jsx("span", { className: active ? "text-fuchsia-100" : "text-slate-400", children: formatShortDate(best?.photo.capturedAt) })
928
+ best ? /* @__PURE__ */ jsx(
929
+ PhotoDate,
930
+ {
931
+ className: active ? "text-fuchsia-100" : "text-slate-400",
932
+ timestamp: best.photo.capturedAt
933
+ }
934
+ ) : /* @__PURE__ */ jsx("span", { className: "text-slate-400", children: "\u2014" })
795
935
  ]
796
936
  }
797
937
  );
798
938
  };
939
+ var FloatingViewerInfoButton = ({
940
+ label,
941
+ onClick
942
+ }) => /* @__PURE__ */ jsx(ToolbarButton, { label, onClick, children: /* @__PURE__ */ jsx(Icon, { path: ICONS.info }) });
799
943
  var FloatingPhotoViewer = ({
800
944
  title,
945
+ titleActions,
801
946
  suggestions,
802
947
  activeDirectionKey,
803
948
  onSelectSuggestion,
@@ -806,17 +951,18 @@ var FloatingPhotoViewer = ({
806
951
  onBack,
807
952
  onForward,
808
953
  onClose,
954
+ step,
955
+ toolbar,
809
956
  status,
810
957
  children,
811
958
  footer,
812
959
  drawer,
813
960
  storageKey = "street-imagery:floating-viewer"
814
961
  }) => {
962
+ const { messages } = useStreetImageryI18n();
815
963
  const [layout, setLayout] = useState(() => readLayout(storageKey));
816
964
  const [drawerOpen, setDrawerOpen] = useState(false);
817
- const dragRef = useRef(
818
- null
819
- );
965
+ const dragRef = useRef(null);
820
966
  const updateLayout = (next) => {
821
967
  setLayout(next);
822
968
  writeLayout(storageKey, next);
@@ -832,6 +978,10 @@ var FloatingPhotoViewer = ({
832
978
  onBack();
833
979
  } else if (event.key === "]" && canGoForward) {
834
980
  onForward();
981
+ } else if (event.key === "ArrowLeft" && event.altKey) {
982
+ step?.onPrevious?.();
983
+ } else if (event.key === "ArrowRight" && event.altKey) {
984
+ step?.onNext?.();
835
985
  }
836
986
  });
837
987
  useEffect(function subscribeKeyboardShortcuts() {
@@ -885,7 +1035,7 @@ var FloatingPhotoViewer = ({
885
1035
  return /* @__PURE__ */ jsxs(
886
1036
  "section",
887
1037
  {
888
- "aria-label": "Photo viewer",
1038
+ "aria-label": messages.viewer.regionLabel,
889
1039
  className: "absolute z-10 flex max-h-[calc(100%-1rem)] flex-col overflow-hidden rounded-xl bg-white shadow-xl ring-1 ring-slate-200",
890
1040
  style: {
891
1041
  right: layout.right,
@@ -909,43 +1059,70 @@ var FloatingPhotoViewer = ({
909
1059
  onPointerDown: (event) => startDrag(event, "move"),
910
1060
  ...dragHandlers,
911
1061
  children: [
912
- /* @__PURE__ */ jsx(ToolbarButton, { disabled: !canGoBack, label: "Previous photo in history ([)", onClick: onBack, children: /* @__PURE__ */ jsx(Icon, { path: ICONS.back }) }),
1062
+ step ? /* @__PURE__ */ jsxs(Fragment, { children: [
1063
+ /* @__PURE__ */ jsx(
1064
+ ToolbarButton,
1065
+ {
1066
+ disabled: !step.onPrevious,
1067
+ label: step.previousLabel,
1068
+ onClick: () => step.onPrevious?.(),
1069
+ children: /* @__PURE__ */ jsx(Icon, { path: ICONS.back })
1070
+ }
1071
+ ),
1072
+ /* @__PURE__ */ jsx(
1073
+ ToolbarButton,
1074
+ {
1075
+ disabled: !step.onNext,
1076
+ label: step.nextLabel,
1077
+ onClick: () => step.onNext?.(),
1078
+ children: /* @__PURE__ */ jsx(Icon, { path: ICONS.forward })
1079
+ }
1080
+ )
1081
+ ] }) : null,
1082
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-0.5", children: [
1083
+ /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate px-1 text-sm font-semibold text-slate-800", children: title }),
1084
+ titleActions
1085
+ ] }),
1086
+ /* @__PURE__ */ jsx(ToolbarButton, { disabled: !canGoBack, label: messages.viewer.backInHistory, onClick: onBack, children: /* @__PURE__ */ jsx(Icon, { path: ICONS.undo }) }),
913
1087
  /* @__PURE__ */ jsx(
914
1088
  ToolbarButton,
915
1089
  {
916
1090
  disabled: !canGoForward,
917
- label: "Next photo in history (])",
1091
+ label: messages.viewer.forwardInHistory,
918
1092
  onClick: onForward,
919
- children: /* @__PURE__ */ jsx(Icon, { path: ICONS.forward })
1093
+ children: /* @__PURE__ */ jsx(Icon, { path: ICONS.redo })
920
1094
  }
921
1095
  ),
922
- /* @__PURE__ */ jsx("h2", { className: "min-w-0 flex-1 truncate px-1 text-sm font-semibold text-slate-800", children: title }),
923
1096
  /* @__PURE__ */ jsx(
924
1097
  ToolbarButton,
925
1098
  {
926
- label: "Minimize",
1099
+ label: messages.viewer.minimize,
927
1100
  onClick: () => updateLayout({ ...layout, minimized: true }),
928
1101
  children: /* @__PURE__ */ jsx(Icon, { path: ICONS.minimize })
929
1102
  }
930
1103
  ),
931
- /* @__PURE__ */ jsx(ToolbarButton, { label: "Close (Esc)", onClick: onClose, children: /* @__PURE__ */ jsx(Icon, { path: ICONS.close }) })
1104
+ /* @__PURE__ */ jsx(ToolbarButton, { label: messages.viewer.close, onClick: onClose, children: /* @__PURE__ */ jsx(Icon, { path: ICONS.close }) })
932
1105
  ]
933
1106
  }
934
1107
  ),
935
- suggestions.length > 0 ? /* @__PURE__ */ jsx(
1108
+ toolbar ? /* @__PURE__ */ jsx("div", { className: "border-b border-slate-100 px-2 py-1.5", children: toolbar }) : suggestions.length > 0 ? /* @__PURE__ */ jsxs(
936
1109
  "nav",
937
1110
  {
938
- "aria-label": "Suggested views",
1111
+ "aria-label": messages.viewer.suggestedViews,
939
1112
  className: "flex gap-1 overflow-x-auto border-b border-slate-100 px-2 py-1.5",
940
- children: suggestions.map((suggestion) => /* @__PURE__ */ jsx(
941
- SuggestionChip,
942
- {
943
- active: suggestion.direction.key === activeDirectionKey,
944
- onSelect: () => onSelectSuggestion(suggestion),
945
- suggestion
946
- },
947
- suggestion.direction.key
948
- ))
1113
+ title: messages.viewer.suggestedViewsHint,
1114
+ children: [
1115
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 self-center pr-0.5 text-xs text-slate-500", children: messages.viewer.views }),
1116
+ suggestions.map((suggestion) => /* @__PURE__ */ jsx(
1117
+ SuggestionChip,
1118
+ {
1119
+ active: suggestion.direction.key === activeDirectionKey,
1120
+ onSelect: () => onSelectSuggestion(suggestion),
1121
+ suggestion
1122
+ },
1123
+ suggestion.direction.key
1124
+ ))
1125
+ ]
949
1126
  }
950
1127
  ) : null,
951
1128
  /* @__PURE__ */ jsxs("div", { className: "min-h-0 overflow-y-auto", children: [
@@ -1121,17 +1298,22 @@ var useViewpointSessionStore = create()((set, get) => ({
1121
1298
  set({ historyIndex: historyIndex + 1, activeDirectionKey: entry.directionKey });
1122
1299
  return entry;
1123
1300
  },
1124
- close: () => set({ viewpoints: [], line: null, activeDirectionKey: null })
1301
+ close: () => set({ viewpoints: [], line: null, activeDirectionKey: null }),
1302
+ reset: () => set({
1303
+ viewpoints: [],
1304
+ line: null,
1305
+ activeDirectionKey: null,
1306
+ history: [],
1307
+ historyIndex: -1
1308
+ })
1125
1309
  }
1126
1310
  }));
1127
- var useViewpointSessionId = () => useViewpointSessionStore((state) => state.sessionId);
1128
1311
  var useViewpoints = () => useViewpointSessionStore((state) => state.viewpoints);
1129
1312
  var useViewpointLine = () => useViewpointSessionStore((state) => state.line);
1130
1313
  var useActiveDirectionKey = () => useViewpointSessionStore((state) => state.activeDirectionKey);
1131
1314
  var useCurrentHistoryEntry = () => useViewpointSessionStore((state) => state.history[state.historyIndex] ?? null);
1132
1315
  var useCanGoBack = () => useViewpointSessionStore((state) => state.historyIndex > 0);
1133
1316
  var useCanGoForward = () => useViewpointSessionStore((state) => state.historyIndex < state.history.length - 1);
1134
- var useViewpointSessionActions = () => useViewpointSessionStore((state) => state.actions);
1135
1317
  var getViewpointSession = () => {
1136
1318
  const { actions, history, historyIndex } = useViewpointSessionStore.getState();
1137
1319
  return { actions, current: history[historyIndex] ?? null };
@@ -1219,5 +1401,126 @@ var useMapillaryImageDetections = (imageId, { filter, filterKey } = {}) => useQu
1219
1401
  enabled: !!imageId,
1220
1402
  staleTime: STALE_TIME
1221
1403
  });
1404
+ var useLocationPickStore = create()((set) => ({
1405
+ armedOpenerId: null,
1406
+ actions: {
1407
+ arm: (openerId) => set({ armedOpenerId: openerId }),
1408
+ disarm: () => set((state) => state.armedOpenerId == null ? state : { armedOpenerId: null }),
1409
+ toggle: (openerId) => set((state) => ({ armedOpenerId: state.armedOpenerId === openerId ? null : openerId }))
1410
+ }
1411
+ }));
1412
+ var useArmedLocationOpenerId = () => useLocationPickStore((state) => state.armedOpenerId);
1413
+ var useLocationPickActions = () => useLocationPickStore((state) => state.actions);
1414
+
1415
+ // src/openers/LocationPickOnMap.tsx
1416
+ var roundCoordinate = (value) => Number(value.toFixed(7));
1417
+ var LocationPickOnMap = ({ onPick }) => {
1418
+ const { current: map } = useMap();
1419
+ const armedOpenerId = useArmedLocationOpenerId();
1420
+ const { disarm } = useLocationPickActions();
1421
+ useEffect(
1422
+ function openArmedOpenerOnNextMapClick() {
1423
+ if (!map || !armedOpenerId) {
1424
+ return;
1425
+ }
1426
+ const opener = findLocationOpener(armedOpenerId);
1427
+ if (!opener) {
1428
+ return;
1429
+ }
1430
+ const onClick = (event) => {
1431
+ const target = {
1432
+ lngLat: [roundCoordinate(event.lngLat.lng), roundCoordinate(event.lngLat.lat)],
1433
+ zoom: map.getZoom()
1434
+ };
1435
+ openLocationInNewTab(opener, target);
1436
+ disarm();
1437
+ onPick?.(target, opener);
1438
+ };
1439
+ const onKeyDown = (event) => {
1440
+ if (event.key === "Escape") {
1441
+ disarm();
1442
+ }
1443
+ };
1444
+ map.on("click", onClick);
1445
+ window.addEventListener("keydown", onKeyDown);
1446
+ return function stopListeningForPick() {
1447
+ map.off("click", onClick);
1448
+ window.removeEventListener("keydown", onKeyDown);
1449
+ };
1450
+ },
1451
+ [armedOpenerId, disarm, map, onPick]
1452
+ );
1453
+ return null;
1454
+ };
1455
+ var MapillaryFeatureBar = ({ data, shownImage, onShow }) => {
1456
+ const { locale, messages } = useStreetImageryI18n();
1457
+ const labels = messages.feature;
1458
+ const { feature, days, images } = data;
1459
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1460
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", title: feature.value, children: [
1461
+ /* @__PURE__ */ jsx(
1462
+ "img",
1463
+ {
1464
+ alt: "",
1465
+ className: "size-7 shrink-0",
1466
+ onError: (event) => {
1467
+ event.currentTarget.style.display = "none";
1468
+ },
1469
+ src: mapillaryIconUrl(feature.value)
1470
+ },
1471
+ feature.value
1472
+ ),
1473
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1474
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm leading-tight font-medium text-slate-900", children: labels.name(feature.value) }),
1475
+ /* @__PURE__ */ jsxs("p", { className: "truncate text-xs leading-tight text-slate-500", children: [
1476
+ labels.seen,
1477
+ " ",
1478
+ /* @__PURE__ */ jsx(PhotoDate, { timestamp: feature.firstSeenAt }),
1479
+ " \u2013",
1480
+ " ",
1481
+ /* @__PURE__ */ jsx(PhotoDate, { timestamp: feature.lastSeenAt })
1482
+ ] })
1483
+ ] })
1484
+ ] }),
1485
+ days.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-slate-500", children: labels.noPhotos }) : /* @__PURE__ */ jsx(
1486
+ "div",
1487
+ {
1488
+ "aria-label": labels.daysSummary(images.length, days.length),
1489
+ className: "flex gap-1 overflow-x-auto",
1490
+ role: "group",
1491
+ children: days.map((day) => {
1492
+ const noon = /* @__PURE__ */ new Date(`${day.day}T12:00:00Z`);
1493
+ const shownIndex = shownImage ? day.images.findIndex((image) => image.id === shownImage.id) : -1;
1494
+ const active = shownIndex >= 0;
1495
+ return /* @__PURE__ */ jsxs(
1496
+ "button",
1497
+ {
1498
+ "aria-pressed": active,
1499
+ className: `shrink-0 rounded-md border px-2 py-1 text-xs whitespace-nowrap ${active ? "border-amber-500 bg-amber-100 text-amber-950" : "border-slate-300 bg-white text-slate-700 hover:border-slate-400"}`,
1500
+ onClick: () => {
1501
+ const next = active ? day.images[(shownIndex + 1) % day.images.length] : day.best;
1502
+ if (next) {
1503
+ onShow(next);
1504
+ }
1505
+ },
1506
+ title: `${formatDateTooltip(noon, locale)}
1507
+ ${labels.dayPhotos(day.images.length, active && day.images.length > 1)}`,
1508
+ type: "button",
1509
+ children: [
1510
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: formatMonth(noon, locale) }),
1511
+ " ",
1512
+ /* @__PURE__ */ jsxs("span", { className: active ? "text-amber-800" : "text-slate-500", children: [
1513
+ active ? `${shownIndex + 1}/` : "",
1514
+ day.images.length
1515
+ ] })
1516
+ ]
1517
+ },
1518
+ day.day
1519
+ );
1520
+ })
1521
+ }
1522
+ )
1523
+ ] });
1524
+ };
1222
1525
 
1223
- export { FloatingPhotoViewer, StreetLevelImagerySelectionOverlay, StreetLevelImagerySourcesAndLayers, StreetLevelImageryViewCone, StreetLevelImageryViewer, VIEWPOINT_DEFAULT_COLOR, VIEWPOINT_DIRECTION_LAYER_ID, ViewpointLayer, getViewpointSession, queryStreetImageryFeatures, resolveSelectedSequence, streetImageryInteractiveLayerIds, useActiveDirectionKey, useAllProviderMapFeatures, useAllProviderMapFeaturesLoading, useAllProviderPhotos, useCanGoBack, useCanGoForward, useCurrentHistoryEntry, useMapViewportBbox, useMapillaryImageDetections, useMapillaryMapFeatureImages, useMapillaryMapFeaturesNear, usePhotoFullUrl, usePhotoThumbnail, useProviderMapFeatures, useProviderPhotos, useProviderSequences, useViewSuggestions, useViewpointLine, useViewpointSessionActions, useViewpointSessionId, useViewpoints, viewDirectionKeyFromFeatures, viewSuggestionLabel, viewpointRoleLabel };
1526
+ export { BASE_COLOR, FloatingPhotoViewer, FloatingViewerInfoButton, LocationPickOnMap, MapillaryFeatureBar, PhotoDate, SELECTION_COLOR, StreetLevelImagerySelectionOverlay, StreetLevelImagerySourcesAndLayers, StreetLevelImageryViewCone, StreetLevelImageryViewer, VIEWPOINT_DEFAULT_COLOR, VIEWPOINT_DIRECTION_LAYER_ID, VIEW_SHAPE_FILL_OPACITY, ViewpointLayer, getViewpointSession, queryStreetImageryFeatures, resolveSelectedSequence, streetImageryInteractiveLayerIds, useActiveDirectionKey, useAllProviderMapFeatures, useAllProviderPhotos, useArmedLocationOpenerId, useCanGoBack, useCanGoForward, useCurrentHistoryEntry, useLocationPickActions, useMapViewportBbox, useMapillaryImageDetections, useMapillaryMapFeatureImages, useMapillaryMapFeaturesNear, usePhotoFullUrl, usePhotoThumbnail, useProviderMapFeatures, useProviderPhotos, useProviderSequences, useViewSuggestions, useViewpointLine, useViewpoints, viewDirectionKeyFromFeatures, viewSuggestionLabel };