@osm-editor-kit/street-imagery-react 0.1.0-alpha.8 → 0.1.0-alpha.9

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 CHANGED
@@ -40,6 +40,8 @@ const bbox = useMapViewportBbox('main', map)
40
40
  selectedPhoto,
41
41
  photoCircleColor: '#3b82f6',
42
42
  mapFeatureCircleColor: '#94a3b8',
43
+ beforeId: 'road-label', // optional: draw everything below this layer of your style
44
+ minZoom: 10, // optional: below this zoom nothing is requested or drawn
43
45
  }}
44
46
  />
45
47
 
@@ -50,9 +52,11 @@ const hits = queryStreetImageryFeatures(event)
50
52
  onPhotoSelected={setSelection} onEaseMapToPoint={(lng, lat) => map.easeTo({ center: [lng, lat] })} />
51
53
  ```
52
54
 
55
+ **Before photos are loaded:** where a provider has own vector tiles with its track lines (Mapillary: zoom 6 to 15), MapLibre reads those itself. Same colours and filters; the lines are not clickable. Zoomed far out these tiles are several MB each in dense areas; set `options.minZoom` to skip them.
56
+
53
57
  **Mapillary:** `createStreetImageryConfig({ mapillaryToken })` or `setStreetImageryConfig` at boot (`@osm-editor-kit/street-imagery`). **Panoramax + Vite:** see `app/vite.config.ts` for the consuming-app setup.
54
58
 
55
- Also: `useAllProviderPhotos`, `useProviderPhotos` / `useProviderSequences` / `useProviderMapFeatures`, `usePhotoThumbnails`, `resolveSelectedSequence`, `StreetLevelImageryViewCone`, `useViewerBearing` / `useViewerActions`, `MapillaryPanel`, `PanoramaxPanel`.
59
+ Also: `useAllProviderPhotos`, `useProviderPhotos` / `useProviderSequences` / `useProviderMapFeatures`, `usePhotoThumbnails`, `resolveSelectedSequence`, `StreetLevelImageryViewCone`, `useViewerBearing` / `useViewerActions`. The panels themselves (`MapillaryPanel`, `PanoramaxPanel`) are in `@osm-editor-kit/street-imagery-react/viewer-panels`; `StreetLevelImageryViewer` loads them on demand, so `mapillary-js` and `@panoramax/web-viewer` are only loaded when a photo is shown.
56
60
 
57
61
  ## Viewpoints and floating viewer
58
62
 
@@ -119,7 +123,7 @@ const { data: detections } = useMapillaryImageDetections(photo.photoId, {
119
123
 
120
124
  - Map layers: `filter.mapFeatureValue` limits which signs/objects are drawn: `signGroupFilter(['parking', 'bike'])` for sign groups, or `matchesAnyGroup(…)`.
121
125
  - Suggested views: `useViewSuggestions(viewpoints, { sources: [mapillaryPhotoSource, streetViewPhotoSource] })`.
122
- - `turnMapillaryViewerTo` / `setMapillaryViewerOutlines` work on a mapillary-js `Viewer` you own.
126
+ - `turnMapillaryViewerTo` / `setMapillaryViewerOutlines` (from `@osm-editor-kit/street-imagery-react/viewer-panels`) work on a mapillary-js `Viewer` you own.
123
127
 
124
128
  ## A selected sign: viewer, bar and map highlight
125
129
 
@@ -0,0 +1,2 @@
1
+ export { MapillaryPanel } from './chunk-SGIB7AVC.js';
2
+ import './chunk-25UGGXIE.js';
@@ -0,0 +1,2 @@
1
+ export { PanoramaxPanel } from './chunk-KZTMKQT5.js';
2
+ import './chunk-25UGGXIE.js';
@@ -15,12 +15,14 @@ export type StreetLevelImagerySelectionOverlayProps = {
15
15
  cameraLngLat?: [number, number] | null;
16
16
  /** Layer to draw the sequence line below, so photo dots stay on top of it. */
17
17
  sequenceBeforeId?: string;
18
+ /** Id of a layer of the host's style to draw the whole overlay below. */
19
+ beforeId?: string;
18
20
  };
19
21
  /**
20
22
  * The shown photo on the map, bottom to top: its sequence line (only passed when the provider's
21
23
  * own layers are off; else that layer marks it), a filled marker on its photo dot, and — when the camera position differs from the
22
24
  * dot — a dashed connector and a pin at the camera position.
23
25
  */
24
- export declare const StreetLevelImagerySelectionOverlay: ({ selectedPhoto, selectedSequence, cameraLngLat, sequenceBeforeId, }: StreetLevelImagerySelectionOverlayProps) => import("react").JSX.Element | null;
26
+ export declare const StreetLevelImagerySelectionOverlay: ({ selectedPhoto, selectedSequence, cameraLngLat, sequenceBeforeId, beforeId, }: StreetLevelImagerySelectionOverlayProps) => import("react").JSX.Element | null;
25
27
  /** Resolve sequence geometry for highlight when only photo + fetched sequences are available. */
26
28
  export declare const resolveSelectedSequence: (selectedPhoto: NormalizedPhoto | null | undefined, sequences: NormalizedSequence[], sequenceId: string | null | undefined) => NormalizedSequence | null;
@@ -41,6 +41,16 @@ export type StreetLevelImagerySourcesAndLayersProps = {
41
41
  /** Length of the shown photo's view cone, as a multiple of the per-photo shapes. Default 2.5. */
42
42
  viewConeScale?: number;
43
43
  mapFeatureCircleColor: DataDrivenPropertyValueSpecification<string>;
44
+ /**
45
+ * Id of a layer of the host's style: all layers are drawn below it (e.g. below road names).
46
+ * The layer must exist when this component mounts. Default: on top of the style.
47
+ */
48
+ beforeId?: string;
49
+ /**
50
+ * Below this map zoom nothing is requested or drawn. Each provider also has its own minimum
51
+ * zooms (photos, lines); the higher one counts. Default: only the providers' own.
52
+ */
53
+ minZoom?: number;
44
54
  };
45
55
  };
46
56
  export declare const StreetLevelImagerySourcesAndLayers: ({ providers, filter, bbox, zoom, options, }: StreetLevelImagerySourcesAndLayersProps) => import("react").JSX.Element;
@@ -17,5 +17,7 @@ export type StreetLevelImageryViewConeProps = {
17
17
  color?: DataDrivenPropertyValueSpecification<string>;
18
18
  /** Cone length as a multiple of the per-photo view-direction shapes. Default 2.5. */
19
19
  scale?: number;
20
+ /** Id of a layer of the host's style to draw the cone below. */
21
+ beforeId?: string;
20
22
  };
21
- export declare const StreetLevelImageryViewCone: ({ selectedPhoto, zoom, viewerPov, color, scale, }: StreetLevelImageryViewConeProps) => import("react").JSX.Element | null;
23
+ export declare const StreetLevelImageryViewCone: ({ selectedPhoto, zoom, viewerPov, color, scale, beforeId, }: StreetLevelImageryViewConeProps) => import("react").JSX.Element | null;
@@ -31,7 +31,7 @@ var en = {
31
31
  exactDates: "Exact dates",
32
32
  from: "From",
33
33
  to: "To",
34
- photoCount: (count) => count === 1 ? "1 photo" : `${count} photos`
34
+ photoCount: (count) => count === 1 ? "1 photo" : `${count.toLocaleString("en")} photos`
35
35
  },
36
36
  viewpointRole: {
37
37
  here: "Here",
@@ -87,7 +87,7 @@ var de = {
87
87
  exactDates: "Genaue Daten",
88
88
  from: "Von",
89
89
  to: "Bis",
90
- photoCount: (count) => count === 1 ? "1 Foto" : `${count} Fotos`
90
+ photoCount: (count) => count === 1 ? "1 Foto" : `${count.toLocaleString("de")} Fotos`
91
91
  },
92
92
  viewpointRole: {
93
93
  here: "Hier",
@@ -299,6 +299,7 @@ var PanoramaxPanel = ({
299
299
  className: "block h-full w-full",
300
300
  endpoint: panoramaxApiEndpoint(),
301
301
  "url-parameters": "false",
302
+ widgets: hideLegend ? "false" : void 0,
302
303
  picture: photo.photoId,
303
304
  sequence: photo.sequenceId ?? void 0
304
305
  }
@@ -211,7 +211,12 @@ var MapillaryPanel = ({
211
211
  lngLat: [position.lng, position.lat],
212
212
  ...image.creatorUsername ? { creatorName: image.creatorUsername } : {}
213
213
  });
214
- actions.setPov({ lngLat: [position.lng, position.lat] });
214
+ const isFlat = image.cameraType !== "spherical" && image.cameraType !== "equirectangular";
215
+ const compass = image.computedCompassAngle ?? image.compassAngle;
216
+ actions.setPov({
217
+ lngLat: [position.lng, position.lat],
218
+ ...isFlat && compass != null ? { bearing: compass } : {}
219
+ });
215
220
  emitEaseMapToPoint(position.lng, position.lat);
216
221
  };
217
222
  const flushBearing = () => {
@@ -31,6 +31,11 @@ export type PhotoDateRangeFilterProps = {
31
31
  * or left out.
32
32
  */
33
33
  dateInputs?: 'collapsed' | 'open' | 'none';
34
+ /**
35
+ * The "All dates" action next to the chosen range, which removes both limits. Default true;
36
+ * without it the user moves both handles to the ends.
37
+ */
38
+ allDatesAction?: boolean;
34
39
  className?: string;
35
40
  };
36
41
  /**
@@ -38,4 +43,4 @@ export type PhotoDateRangeFilterProps = {
38
43
  * gives recent years more room, marks that show how many photos there are at each time, lines at
39
44
  * whole years, and optional fixed markers. Controlled: `value` in, `onChange` out.
40
45
  */
41
- export declare const PhotoDateRangeFilter: ({ value, onChange, capturedAt, yearLines, recommendedMaxAgeYears, markers, maxYears, now: nowProp, dateInputs, className, }: PhotoDateRangeFilterProps) => import("react").JSX.Element;
46
+ export declare const PhotoDateRangeFilter: ({ value, onChange, capturedAt, yearLines, recommendedMaxAgeYears, markers, maxYears, now: nowProp, dateInputs, className, allDatesAction, }: PhotoDateRangeFilterProps) => import("react").JSX.Element;
@@ -1,5 +1,10 @@
1
1
  import type { Bbox } from '@osm-editor-kit/street-imagery';
2
2
  import { type ProviderId } from '@osm-editor-kit/street-imagery';
3
- export declare const useProviderPhotos: (providerId: ProviderId, bbox: Bbox | null, zoom: number) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").NormalizedPhoto[]>, Error>;
4
- export declare const useProviderSequences: (providerId: ProviderId, bbox: Bbox | null, zoom: number) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").NormalizedSequence[]>, Error>;
5
- export declare const useProviderMapFeatures: (providerId: ProviderId, bbox: Bbox | null, zoom: number) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").NormalizedMapFeature[]>, Error>;
3
+ /** Host limit on top of the provider's own minimum zooms: below it nothing is requested. */
4
+ type ProviderDataOptions = {
5
+ minZoom?: number;
6
+ };
7
+ export declare const useProviderPhotos: (providerId: ProviderId, bbox: Bbox | null, zoom: number, { minZoom }?: ProviderDataOptions) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").NormalizedPhoto[]>, Error>;
8
+ export declare const useProviderSequences: (providerId: ProviderId, bbox: Bbox | null, zoom: number, { minZoom }?: ProviderDataOptions) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").NormalizedSequence[]>, Error>;
9
+ export declare const useProviderMapFeatures: (providerId: ProviderId, bbox: Bbox | null, zoom: number, { minZoom }?: ProviderDataOptions) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").NormalizedMapFeature[]>, Error>;
10
+ export {};
package/dist/index.d.ts CHANGED
@@ -9,15 +9,13 @@ export * from './hooks/useAllProviderPhotos';
9
9
  export * from './hooks/useMapViewportBbox';
10
10
  export * from './hooks/useProviderData';
11
11
  export * from './hooks/usePhotoThumbnails';
12
- export { MapillaryPanel } from './panels/MapillaryPanel';
13
- export { PanoramaxPanel } from './panels/PanoramaxPanel';
14
12
  export * from './useViewerStore';
15
13
  export { FloatingPhotoViewer, FloatingViewerInfoButton, type FloatingPhotoViewerProps, } from './viewpoints/FloatingPhotoViewer';
16
14
  export { VIEWPOINT_DEFAULT_COLOR, VIEWPOINT_DIRECTION_LAYER_ID, ViewpointLayer, viewDirectionKeyFromFeatures, type ViewpointLayerProps, } from './viewpoints/ViewpointLayer';
17
15
  export { viewSuggestionLabel } from './viewpoints/viewDirectionLabels';
18
16
  export * from './viewpoints/useViewpointSessionStore';
19
17
  export * from './viewpoints/useViewSuggestions';
20
- export { mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo, type MapillaryLookAt, type MapillaryViewerOutline, } from './panels/mapillaryLookAt';
18
+ export type { MapillaryLookAt, MapillaryViewerOutline } from './panels/mapillaryLookAt';
21
19
  export * from './mapillary/useMapillaryFeatures';
22
20
  export { LocationPickOnMap, type LocationPickOnMapProps } from './openers/LocationPickOnMap';
23
21
  export * from './openers/useLocationPickStore';
package/dist/index.js CHANGED
@@ -1,9 +1,7 @@
1
- import { useStreetImageryI18n } from './chunk-ZAIXGYAH.js';
2
- export { STREET_IMAGERY_MESSAGES, StreetImageryLocaleProvider, useStreetImageryI18n } from './chunk-ZAIXGYAH.js';
3
- export { MapillaryPanel, mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo } from './chunk-53RA4OSJ.js';
4
- export { PanoramaxPanel } from './chunk-EQ2JKLGT.js';
1
+ import { useStreetImageryI18n } from './chunk-6EMFFIL2.js';
2
+ export { STREET_IMAGERY_MESSAGES, StreetImageryLocaleProvider, useStreetImageryI18n } from './chunk-6EMFFIL2.js';
5
3
  export { useViewerActions, useViewerBearing, useViewerHfov, useViewerLngLat } from './chunk-25UGGXIE.js';
6
- import { distanceMeters, sequencesToFeatureCollection, emptyLineCollection, adapterById, providerById, providerCoversBbox, 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, bestTargetImage, findLocationOpener, MAP_FEATURE_COLOR, mapillaryValueName, mapillaryIconUrl, dateRangeToSliderPositions, sliderPositionsToDateRange, dateSliderBins, dateSliderPosition, yearsAgoMs, parseIsoDateStartMs, openLocationInNewTab, formatDate, isIsoDate, alignLineToPoints, coneRadiusMeters, mapFeaturesToFeatureCollection, emptyPointCollection, buildMapFeatureLayerFilter, featureSourceId, photosToFeatureCollection, photosToViewfieldsFeatureCollection, emptyPolygonCollection, buildPhotoLayerFilter, photoSourceId, viewfieldSourceId, sequenceSourceId, sequenceLayerId, viewfieldLineLayerId, VIEWFIELD_MIN_ZOOM } from '@osm-editor-kit/street-imagery';
4
+ import { distanceMeters, sequencesToFeatureCollection, emptyLineCollection, adapterById, providerById, providerCoversBbox, fetchedSequencesMinZoom, viewConeGeoJson, viewShapeRadiusMeters, setStreetImageryConfig, viewfieldLayerId, photoLayerId, photoTargetLayerId, featureLayerId, PROVIDER_IDS, mapFeatureMatchesDateRange, isBrowserAvailableProvider, photoMatchesFilters, resolvePhotoPanoramaUrl, photoFullUrlQueryKey, resolvePhotoThumbnailUrl, photoThumbnailQueryKey, formatLocalDateTime, formatMonth, formatRelativeDate, formatDateTooltip, mapillaryPhotoSource, buildViewSuggestions, yearsToMs, SIGN_TARGET, fetchMapFeatureImages, fetchMapillaryMapFeaturesInBbox, bboxAround, fetchImageDetections, bestTargetImage, findLocationOpener, MAP_FEATURE_COLOR, mapillaryValueName, mapillaryIconUrl, dateRangeToSliderPositions, sliderPositionsToDateRange, dateSliderBins, dateSliderPosition, yearsAgoMs, parseIsoDateStartMs, openLocationInNewTab, formatDate, isIsoDate, alignLineToPoints, coneRadiusMeters, mapFeaturesToFeatureCollection, emptyPointCollection, buildMapFeatureLayerFilter, featureSourceId, photosToFeatureCollection, photosToViewfieldsFeatureCollection, emptyPolygonCollection, buildPhotoLayerFilter, photoSourceId, viewfieldSourceId, sequenceTilesSourceId, renameExpressionProperties, sequenceTilesLayerId, sequenceSourceId, sequenceLayerId, viewfieldLineLayerId, VIEWFIELD_MIN_ZOOM } from '@osm-editor-kit/street-imagery';
7
5
  import { Source, Layer, useMap } from 'react-map-gl/maplibre';
8
6
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
9
7
  import { lazy, useEffect, Fragment as Fragment$1, Suspense, useState, useRef, useEffectEvent } from 'react';
@@ -21,7 +19,8 @@ var StreetLevelImagerySelectionOverlay = ({
21
19
  selectedPhoto,
22
20
  selectedSequence,
23
21
  cameraLngLat,
24
- sequenceBeforeId
22
+ sequenceBeforeId,
23
+ beforeId
25
24
  }) => {
26
25
  const photoLngLat = selectedPhoto?.lngLat;
27
26
  const cameraDiffers = photoLngLat != null && cameraLngLat != null && distanceMeters(photoLngLat, cameraLngLat) > SAME_POSITION_METERS;
@@ -73,7 +72,7 @@ var StreetLevelImagerySelectionOverlay = ({
73
72
  /* @__PURE__ */ jsx(
74
73
  Layer,
75
74
  {
76
- beforeId: sequenceBeforeId,
75
+ beforeId: sequenceBeforeId ?? beforeId,
77
76
  id: "sequence-highlight-layer",
78
77
  type: "line",
79
78
  source: SEQUENCE_HIGHLIGHT_SOURCE_ID,
@@ -85,6 +84,7 @@ var StreetLevelImagerySelectionOverlay = ({
85
84
  /* @__PURE__ */ jsx(
86
85
  Layer,
87
86
  {
87
+ beforeId,
88
88
  id: "selection-connector-layer",
89
89
  type: "line",
90
90
  source: CONNECTOR_SOURCE_ID,
@@ -95,6 +95,7 @@ var StreetLevelImagerySelectionOverlay = ({
95
95
  /* @__PURE__ */ jsx(
96
96
  Layer,
97
97
  {
98
+ beforeId,
98
99
  id: "selection-highlight-layer",
99
100
  type: "circle",
100
101
  source: POINTS_SOURCE_ID,
@@ -110,6 +111,7 @@ var StreetLevelImagerySelectionOverlay = ({
110
111
  /* @__PURE__ */ jsx(
111
112
  Layer,
112
113
  {
114
+ beforeId,
113
115
  id: "selection-camera-layer",
114
116
  type: "circle",
115
117
  source: POINTS_SOURCE_ID,
@@ -137,10 +139,10 @@ var withinBbox = (bbox) => (item) => {
137
139
  const [lng, lat] = item.lngLat;
138
140
  return lng >= bbox[0] && lng <= bbox[2] && lat >= bbox[1] && lat <= bbox[3];
139
141
  };
140
- var useProviderPhotos = (providerId, bbox, zoom) => {
142
+ var useProviderPhotos = (providerId, bbox, zoom, { minZoom = 0 } = {}) => {
141
143
  const adapter = adapterById[providerId];
142
144
  const meta = providerById[providerId];
143
- const enabled = meta.kind === "photo" && bbox != null && providerCoversBbox(providerId, bbox) && zoom >= meta.minZoom && adapter?.fetchPhotos != null;
145
+ const enabled = meta.kind === "photo" && bbox != null && providerCoversBbox(providerId, bbox) && zoom >= Math.max(meta.minZoom, minZoom) && adapter?.fetchPhotos != null;
144
146
  return useQuery({
145
147
  queryKey: ["provider-photos", providerId, bboxKey(bbox), zoom],
146
148
  queryFn: async ({ signal }) => {
@@ -151,11 +153,11 @@ var useProviderPhotos = (providerId, bbox, zoom) => {
151
153
  placeholderData: (previous) => previous
152
154
  });
153
155
  };
154
- var useProviderSequences = (providerId, bbox, zoom) => {
156
+ var useProviderSequences = (providerId, bbox, zoom, { minZoom = 0 } = {}) => {
155
157
  const adapter = adapterById[providerId];
156
158
  const meta = providerById[providerId];
157
- const sequencesMinZoom = meta.sequencesMinZoom;
158
- const enabled = meta.kind === "photo" && bbox != null && providerCoversBbox(providerId, bbox) && zoom >= sequencesMinZoom && adapter?.fetchSequences != null;
159
+ const sequencesMinZoom = fetchedSequencesMinZoom(providerId);
160
+ const enabled = meta.kind === "photo" && bbox != null && providerCoversBbox(providerId, bbox) && zoom >= Math.max(sequencesMinZoom, minZoom) && adapter?.fetchSequences != null;
159
161
  return useQuery({
160
162
  queryKey: ["provider-sequences", providerId, bboxKey(bbox), zoom],
161
163
  queryFn: ({ signal }) => adapter?.fetchSequences?.(bbox, zoom, signal) ?? [],
@@ -163,10 +165,10 @@ var useProviderSequences = (providerId, bbox, zoom) => {
163
165
  placeholderData: (previous) => previous
164
166
  });
165
167
  };
166
- var useProviderMapFeatures = (providerId, bbox, zoom) => {
168
+ var useProviderMapFeatures = (providerId, bbox, zoom, { minZoom = 0 } = {}) => {
167
169
  const adapter = adapterById[providerId];
168
170
  const meta = providerById[providerId];
169
- const enabled = meta.kind === "mapFeature" && bbox != null && providerCoversBbox(providerId, bbox) && zoom >= meta.minZoom && adapter?.fetchMapFeatures != null;
171
+ const enabled = meta.kind === "mapFeature" && bbox != null && providerCoversBbox(providerId, bbox) && zoom >= Math.max(meta.minZoom, minZoom) && adapter?.fetchMapFeatures != null;
170
172
  return useQuery({
171
173
  queryKey: ["provider-map-features", providerId, bboxKey(bbox), zoom],
172
174
  queryFn: async ({ signal }) => {
@@ -194,7 +196,8 @@ var StreetLevelImageryViewCone = ({
194
196
  zoom,
195
197
  viewerPov,
196
198
  color = SELECTION_COLOR,
197
- scale = 2.5
199
+ scale = 2.5,
200
+ beforeId
198
201
  }) => {
199
202
  const apex = viewerPov?.lngLat ?? selectedPhoto.lngLat;
200
203
  const isPano = selectedPhoto.isPano === true;
@@ -207,6 +210,9 @@ var StreetLevelImageryViewCone = ({
207
210
  } else if (selectedPhoto.providerId === "panoramax") {
208
211
  bearing = viewerPov?.bearing ?? selectedPhoto.heading;
209
212
  fov = 30;
213
+ } else if (selectedPhoto.providerId === "mapillary") {
214
+ bearing = viewerPov?.bearing ?? selectedPhoto.heading;
215
+ fov = viewerPov?.hfov ?? 30;
210
216
  } else {
211
217
  bearing = selectedPhoto.heading;
212
218
  fov = 30;
@@ -224,6 +230,7 @@ var StreetLevelImageryViewCone = ({
224
230
  /* @__PURE__ */ jsx(
225
231
  Layer,
226
232
  {
233
+ beforeId,
227
234
  id: CONE_FILL_LAYER_ID,
228
235
  type: "fill",
229
236
  source: CONE_SOURCE_ID,
@@ -271,6 +278,7 @@ var AGE_SHADE = [
271
278
  BASE_COLOR
272
279
  ]
273
280
  ];
281
+ var PHOTO_TARGET_RADIUS_PX = 10;
274
282
  var PHOTO_SORT_KEY = ["coalesce", ["get", "capturedAt"], 0];
275
283
  var FEATURE_SORT_KEY = ["coalesce", ["get", "lastSeenAt"], 0];
276
284
  var PhotoProviderLayer = ({
@@ -283,15 +291,20 @@ var PhotoProviderLayer = ({
283
291
  photoCircleColor,
284
292
  activeSequenceId,
285
293
  hasActiveSequence,
286
- shownPhotoId
294
+ shownPhotoId,
295
+ beforeId,
296
+ minZoom
287
297
  }) => {
288
298
  const adapter = adapterById[providerId];
289
299
  const meta = providerById[providerId];
290
- const { data: photos = [] } = useProviderPhotos(providerId, bbox, zoom);
291
- const { data: sequences = [] } = useProviderSequences(providerId, bbox, zoom);
292
- const visiblePhotos = zoom >= meta.minZoom ? photos.filter((photo) => photoMatchesFilters(photo, filter?.photoTypes, filter?.date)) : [];
300
+ const photosMinZoom = Math.max(meta.minZoom, minZoom);
301
+ const fetchedLinesMinZoom = fetchedSequencesMinZoom(providerId);
302
+ const sequencesMinZoom = Math.max(fetchedLinesMinZoom, minZoom);
303
+ const { data: photos = [] } = useProviderPhotos(providerId, bbox, zoom, { minZoom });
304
+ const { data: sequences = [] } = useProviderSequences(providerId, bbox, zoom, { minZoom });
305
+ const visiblePhotos = zoom >= photosMinZoom ? photos.filter((photo) => photoMatchesFilters(photo, filter?.photoTypes, filter?.date)) : [];
293
306
  const drawBbox = bbox ? padBbox(bbox, 0.25) : null;
294
- const photoCollection = zoom >= meta.minZoom ? photosToFeatureCollection(
307
+ const photoCollection = zoom >= photosMinZoom ? photosToFeatureCollection(
295
308
  drawBbox ? visiblePhotos.filter((photo) => lngLatInBbox(photo.lngLat, drawBbox)) : visiblePhotos
296
309
  ) : emptyPointCollection();
297
310
  const viewfieldsActive = showViewfields && zoom >= VIEWFIELD_MIN_ZOOM;
@@ -342,7 +355,7 @@ var PhotoProviderLayer = ({
342
355
  sequenceCapturedAtById.set(photo.sequenceId, photo.capturedAt);
343
356
  }
344
357
  }
345
- const filteredSequences = showSequences && zoom >= meta.sequencesMinZoom ? sequences.map((sequence) => {
358
+ const filteredSequences = showSequences && zoom >= sequencesMinZoom ? sequences.map((sequence) => {
346
359
  if (sequence.capturedAt != null) return sequence;
347
360
  const fromPhotos = sequenceCapturedAtById.get(sequence.sequenceId);
348
361
  return fromPhotos != null ? { ...sequence, capturedAt: fromPhotos } : sequence;
@@ -385,6 +398,8 @@ var PhotoProviderLayer = ({
385
398
  }
386
399
  };
387
400
  });
401
+ const sequenceTiles = showSequences ? adapter?.sequenceTiles?.() : null;
402
+ const sequenceTilesMinZoom = sequenceTiles ? Math.max(sequenceTiles.minZoom, minZoom) : 0;
388
403
  const sequenceCollection = alignedSequences.length > 0 ? sequencesToFeatureCollection(alignedSequences) : emptyLineCollection();
389
404
  return /* @__PURE__ */ jsxs(Fragment, { children: [
390
405
  meta.coverageTiles ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -401,6 +416,7 @@ var PhotoProviderLayer = ({
401
416
  /* @__PURE__ */ jsx(
402
417
  Layer,
403
418
  {
419
+ beforeId,
404
420
  id: `coverage-tiles-${providerId}`,
405
421
  minzoom: meta.coverageTiles.minZoom,
406
422
  paint: { "raster-opacity": 0.7 },
@@ -409,6 +425,48 @@ var PhotoProviderLayer = ({
409
425
  }
410
426
  )
411
427
  ] }) : null,
428
+ sequenceTiles && sequenceTilesMinZoom < fetchedLinesMinZoom ? /* @__PURE__ */ jsxs(Fragment, { children: [
429
+ /* @__PURE__ */ jsx(
430
+ Source,
431
+ {
432
+ id: sequenceTilesSourceId(providerId),
433
+ type: "vector",
434
+ tiles: sequenceTiles.tiles,
435
+ minzoom: sequenceTiles.minZoom,
436
+ maxzoom: sequenceTiles.maxZoom
437
+ },
438
+ sequenceTilesSourceId(providerId)
439
+ ),
440
+ /* @__PURE__ */ jsx(
441
+ Layer,
442
+ {
443
+ beforeId,
444
+ id: sequenceTilesLayerId(providerId),
445
+ type: "line",
446
+ source: sequenceTilesSourceId(providerId),
447
+ "source-layer": sequenceTiles.sourceLayer,
448
+ minzoom: sequenceTilesMinZoom,
449
+ maxzoom: fetchedLinesMinZoom,
450
+ filter: renameExpressionProperties(photoFilter, sequenceTiles.properties),
451
+ layout: {
452
+ "line-cap": "round",
453
+ "line-join": "round",
454
+ "line-sort-key": renameExpressionProperties(
455
+ ["case", inActiveSequence, 1, 0],
456
+ sequenceTiles.properties
457
+ )
458
+ },
459
+ paint: {
460
+ "line-color": renameExpressionProperties(photoCircleColor, sequenceTiles.properties),
461
+ "line-width": renameExpressionProperties(
462
+ ["case", inActiveSequence, ACTIVE_LINE_WIDTH, 1.25],
463
+ sequenceTiles.properties
464
+ ),
465
+ "line-opacity": renameExpressionProperties(lineOpacity, sequenceTiles.properties)
466
+ }
467
+ }
468
+ )
469
+ ] }) : null,
412
470
  adapter?.fetchSequences && showSequences ? /* @__PURE__ */ jsxs(Fragment, { children: [
413
471
  /* @__PURE__ */ jsx(
414
472
  Source,
@@ -422,6 +480,7 @@ var PhotoProviderLayer = ({
422
480
  /* @__PURE__ */ jsx(
423
481
  Layer,
424
482
  {
483
+ beforeId,
425
484
  id: sequenceLayerId(providerId),
426
485
  type: "line",
427
486
  source: sequenceSourceId(providerId),
@@ -451,6 +510,7 @@ var PhotoProviderLayer = ({
451
510
  /* @__PURE__ */ jsx(
452
511
  Layer,
453
512
  {
513
+ beforeId,
454
514
  id: viewfieldLayerId(providerId),
455
515
  type: "fill",
456
516
  source: viewfieldSrcId,
@@ -465,6 +525,7 @@ var PhotoProviderLayer = ({
465
525
  /* @__PURE__ */ jsx(
466
526
  Layer,
467
527
  {
528
+ beforeId,
468
529
  id: viewfieldLineLayerId(providerId),
469
530
  type: "line",
470
531
  source: viewfieldSrcId,
@@ -490,6 +551,19 @@ var PhotoProviderLayer = ({
490
551
  /* @__PURE__ */ jsx(
491
552
  Layer,
492
553
  {
554
+ beforeId,
555
+ id: photoTargetLayerId(providerId),
556
+ type: "circle",
557
+ source: photoSrcId,
558
+ filter: photoFilter,
559
+ layout: { "circle-sort-key": PHOTO_SORT_KEY },
560
+ paint: { "circle-radius": PHOTO_TARGET_RADIUS_PX, "circle-opacity": 0 }
561
+ }
562
+ ),
563
+ /* @__PURE__ */ jsx(
564
+ Layer,
565
+ {
566
+ beforeId,
493
567
  id: photoLayerId(providerId),
494
568
  type: "circle",
495
569
  source: photoSrcId,
@@ -508,12 +582,14 @@ var MapFeatureProviderLayer = ({
508
582
  bbox,
509
583
  zoom,
510
584
  filter,
511
- mapFeatureCircleColor
585
+ mapFeatureCircleColor,
586
+ beforeId,
587
+ minZoom
512
588
  }) => {
513
589
  const meta = providerById[providerId];
514
- const { data: features = [] } = useProviderMapFeatures(providerId, bbox, zoom);
590
+ const { data: features = [] } = useProviderMapFeatures(providerId, bbox, zoom, { minZoom });
515
591
  const valueFilter = filter?.mapFeatureValue;
516
- const featureCollection = zoom >= meta.minZoom ? mapFeaturesToFeatureCollection(
592
+ const featureCollection = zoom >= Math.max(meta.minZoom, minZoom) ? mapFeaturesToFeatureCollection(
517
593
  valueFilter ? features.filter((feature) => valueFilter(feature.value)) : features
518
594
  ) : emptyPointCollection();
519
595
  const featureFilter = buildMapFeatureLayerFilter(filter?.date);
@@ -532,6 +608,7 @@ var MapFeatureProviderLayer = ({
532
608
  /* @__PURE__ */ jsx(
533
609
  Layer,
534
610
  {
611
+ beforeId,
535
612
  id: featureLayerId(providerId),
536
613
  type: "circle",
537
614
  source: featureSrcId,
@@ -567,7 +644,9 @@ var StreetLevelImagerySourcesAndLayers = ({
567
644
  viewConeColor,
568
645
  viewConeScale,
569
646
  photoCircleColor,
570
- mapFeatureCircleColor
647
+ mapFeatureCircleColor,
648
+ beforeId,
649
+ minZoom = 0
571
650
  } = options;
572
651
  useEffect(
573
652
  function applyStreetImageryConfig() {
@@ -594,6 +673,8 @@ var StreetLevelImagerySourcesAndLayers = ({
594
673
  providers.map((providerId) => /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(
595
674
  ProviderLayer,
596
675
  {
676
+ beforeId,
677
+ minZoom,
597
678
  bbox,
598
679
  filter,
599
680
  mapFeatureCircleColor,
@@ -610,6 +691,7 @@ var StreetLevelImagerySourcesAndLayers = ({
610
691
  showViewCone && selectedPhoto ? /* @__PURE__ */ jsx(
611
692
  StreetLevelImageryViewCone,
612
693
  {
694
+ beforeId,
613
695
  color: viewConeColor ?? SELECTION_COLOR,
614
696
  scale: viewConeScale,
615
697
  selectedPhoto,
@@ -620,6 +702,7 @@ var StreetLevelImagerySourcesAndLayers = ({
620
702
  showSelectionHighlight ? /* @__PURE__ */ jsx(
621
703
  StreetLevelImagerySelectionOverlay,
622
704
  {
705
+ beforeId,
623
706
  cameraLngLat: viewerPov?.lngLat,
624
707
  selectedPhoto,
625
708
  selectedSequence: providerDrawsSequences ? null : selectedSequence,
@@ -628,13 +711,13 @@ var StreetLevelImagerySourcesAndLayers = ({
628
711
  ) : null
629
712
  ] });
630
713
  };
631
- var MapillaryPanel2 = lazy(
632
- () => import('./MapillaryPanel-N45NQY4C.js').then((module) => ({
714
+ var MapillaryPanel = lazy(
715
+ () => import('./MapillaryPanel-FAXVZH37.js').then((module) => ({
633
716
  default: module.MapillaryPanel
634
717
  }))
635
718
  );
636
- var PanoramaxPanel2 = lazy(
637
- () => import('./PanoramaxPanel-MFI4L67Z.js').then((module) => ({
719
+ var PanoramaxPanel = lazy(
720
+ () => import('./PanoramaxPanel-BTHEMKYY.js').then((module) => ({
638
721
  default: module.PanoramaxPanel
639
722
  }))
640
723
  );
@@ -655,7 +738,7 @@ var StreetLevelImageryViewer = ({
655
738
  }) => {
656
739
  if (photo.providerId === "mapillary") {
657
740
  return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ViewerPanelPlaceholder, {}), children: /* @__PURE__ */ jsx(
658
- MapillaryPanel2,
741
+ MapillaryPanel,
659
742
  {
660
743
  groupPhotos,
661
744
  hideAttribution,
@@ -671,7 +754,7 @@ var StreetLevelImageryViewer = ({
671
754
  }
672
755
  if (photo.providerId === "panoramax") {
673
756
  return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ViewerPanelPlaceholder, {}), children: /* @__PURE__ */ jsx(
674
- PanoramaxPanel2,
757
+ PanoramaxPanel,
675
758
  {
676
759
  groupPhotos,
677
760
  hideLegend: hideAttribution,
@@ -686,6 +769,7 @@ var StreetLevelImageryViewer = ({
686
769
  };
687
770
  var streetImageryInteractiveLayerIds = (providers) => [
688
771
  ...providers.map((providerId) => photoLayerId(providerId)),
772
+ ...providers.map((providerId) => photoTargetLayerId(providerId)),
689
773
  ...providers.map((providerId) => viewfieldLayerId(providerId)),
690
774
  ...providers.map((providerId) => featureLayerId(providerId))
691
775
  ];
@@ -694,6 +778,10 @@ var parseProviderIdFromLayerId = (layerId) => {
694
778
  if (photoMatch) {
695
779
  return photoMatch[1];
696
780
  }
781
+ const targetMatch = layerId.match(/^photo-targets-(.+)$/);
782
+ if (targetMatch) {
783
+ return targetMatch[1];
784
+ }
697
785
  const viewfieldMatch = layerId.match(/^viewfields-(.+)$/);
698
786
  if (viewfieldMatch) {
699
787
  return viewfieldMatch[1];
@@ -718,7 +806,7 @@ var queryStreetImageryFeatures = (event) => {
718
806
  continue;
719
807
  }
720
808
  const props = feature.properties ?? {};
721
- if (layerId.startsWith("photos-") || layerId.startsWith("viewfields-")) {
809
+ if (layerId.startsWith("photos-") || layerId.startsWith("photo-targets-") || layerId.startsWith("viewfields-")) {
722
810
  const photoId = props.photoId;
723
811
  if (photoId == null) {
724
812
  continue;
@@ -880,6 +968,13 @@ var viewSuggestionLabel = ({ viewpoint, direction }, messages) => messages.viewD
880
968
  var DEFAULT_LAYOUT = { right: 16, bottom: 32, width: 420, minimized: false };
881
969
  var MIN_WIDTH = 280;
882
970
  var EDGE = 8;
971
+ var hasStoredLayout = (storageKey) => {
972
+ try {
973
+ return localStorage.getItem(storageKey) != null;
974
+ } catch {
975
+ return false;
976
+ }
977
+ };
883
978
  var readLayout = (storageKey) => {
884
979
  try {
885
980
  const raw = localStorage.getItem(storageKey);
@@ -1024,8 +1119,8 @@ var FloatingPhotoViewer = ({
1024
1119
  activeDirectionKey,
1025
1120
  onSelectSuggestion,
1026
1121
  shownPhotoId,
1027
- canGoBack,
1028
- canGoForward,
1122
+ canGoBack = false,
1123
+ canGoForward = false,
1029
1124
  onBack,
1030
1125
  onForward,
1031
1126
  onClose,
@@ -1035,12 +1130,16 @@ var FloatingPhotoViewer = ({
1035
1130
  children,
1036
1131
  footer,
1037
1132
  drawer,
1038
- storageKey = "street-imagery:floating-viewer"
1133
+ storageKey = "street-imagery:floating-viewer",
1134
+ defaultPosition
1039
1135
  }) => {
1040
1136
  const { messages } = useStreetImageryI18n();
1041
1137
  const severalViewpoints = new Set(suggestions.map((suggestion) => suggestion.viewpoint.id)).size > 1;
1042
1138
  const [layout, setLayout] = useState(() => readLayout(storageKey));
1043
1139
  const [drawerOpen, setDrawerOpen] = useState(false);
1140
+ const [moved, setMoved] = useState(() => hasStoredLayout(storageKey));
1141
+ const atTopLeft = defaultPosition?.corner === "top-left" && !moved;
1142
+ const position = atTopLeft ? { left: defaultPosition.left ?? EDGE, top: defaultPosition.top ?? EDGE } : { right: layout.right, bottom: layout.bottom };
1044
1143
  const dragRef = useRef(null);
1045
1144
  const updateLayout = (next) => {
1046
1145
  setLayout(next);
@@ -1054,9 +1153,9 @@ var FloatingPhotoViewer = ({
1054
1153
  if (event.key === "Escape") {
1055
1154
  onClose();
1056
1155
  } else if (event.key === "[" && canGoBack) {
1057
- onBack();
1156
+ onBack?.();
1058
1157
  } else if (event.key === "]" && canGoForward) {
1059
- onForward();
1158
+ onForward?.();
1060
1159
  } else if (event.key === "ArrowLeft" && event.altKey) {
1061
1160
  step?.onPrevious?.();
1062
1161
  } else if (event.key === "ArrowRight" && event.altKey) {
@@ -1072,7 +1171,16 @@ var FloatingPhotoViewer = ({
1072
1171
  return;
1073
1172
  }
1074
1173
  event.currentTarget.setPointerCapture(event.pointerId);
1075
- dragRef.current = { x: event.clientX, y: event.clientY, layout, mode };
1174
+ const box = event.currentTarget.closest("section");
1175
+ const container = box?.offsetParent;
1176
+ const start = atTopLeft && box && container ? {
1177
+ ...layout,
1178
+ right: container.clientWidth - box.offsetLeft - box.offsetWidth,
1179
+ bottom: container.clientHeight - box.offsetTop - box.offsetHeight
1180
+ } : layout;
1181
+ setLayout(start);
1182
+ setMoved(true);
1183
+ dragRef.current = { x: event.clientX, y: event.clientY, layout: start, mode };
1076
1184
  };
1077
1185
  const onDrag = (event) => {
1078
1186
  const drag = dragRef.current;
@@ -1102,7 +1210,7 @@ var FloatingPhotoViewer = ({
1102
1210
  {
1103
1211
  className: "absolute z-10 inline-flex items-center gap-2 rounded-full bg-white px-3 py-1.5 text-sm font-medium text-slate-800 shadow-lg ring-1 ring-slate-200 hover:bg-slate-50",
1104
1212
  onClick: () => updateLayout({ ...layout, minimized: false }),
1105
- style: { right: layout.right, bottom: layout.bottom },
1213
+ style: position,
1106
1214
  type: "button",
1107
1215
  children: [
1108
1216
  /* @__PURE__ */ jsx(Icon, { path: ICONS.expand }),
@@ -1117,8 +1225,7 @@ var FloatingPhotoViewer = ({
1117
1225
  "aria-label": messages.viewer.regionLabel,
1118
1226
  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",
1119
1227
  style: {
1120
- right: layout.right,
1121
- bottom: layout.bottom,
1228
+ ...position,
1122
1229
  width: `min(${layout.width}px, calc(100% - 1rem))`
1123
1230
  },
1124
1231
  children: [
@@ -1162,16 +1269,26 @@ var FloatingPhotoViewer = ({
1162
1269
  /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate px-1 text-sm font-semibold text-slate-800", children: title }),
1163
1270
  titleActions
1164
1271
  ] }),
1165
- /* @__PURE__ */ jsx(ToolbarButton, { disabled: !canGoBack, label: messages.viewer.backInHistory, onClick: onBack, children: /* @__PURE__ */ jsx(Icon, { path: ICONS.undo }) }),
1166
- /* @__PURE__ */ jsx(
1167
- ToolbarButton,
1168
- {
1169
- disabled: !canGoForward,
1170
- label: messages.viewer.forwardInHistory,
1171
- onClick: onForward,
1172
- children: /* @__PURE__ */ jsx(Icon, { path: ICONS.redo })
1173
- }
1174
- ),
1272
+ onBack && onForward ? /* @__PURE__ */ jsxs(Fragment, { children: [
1273
+ /* @__PURE__ */ jsx(
1274
+ ToolbarButton,
1275
+ {
1276
+ disabled: !canGoBack,
1277
+ label: messages.viewer.backInHistory,
1278
+ onClick: onBack,
1279
+ children: /* @__PURE__ */ jsx(Icon, { path: ICONS.undo })
1280
+ }
1281
+ ),
1282
+ /* @__PURE__ */ jsx(
1283
+ ToolbarButton,
1284
+ {
1285
+ disabled: !canGoForward,
1286
+ label: messages.viewer.forwardInHistory,
1287
+ onClick: onForward,
1288
+ children: /* @__PURE__ */ jsx(Icon, { path: ICONS.redo })
1289
+ }
1290
+ )
1291
+ ] }) : null,
1175
1292
  /* @__PURE__ */ jsx(
1176
1293
  ToolbarButton,
1177
1294
  {
@@ -1725,7 +1842,8 @@ var PhotoDateRangeFilter = ({
1725
1842
  maxYears,
1726
1843
  now: nowProp,
1727
1844
  dateInputs = "collapsed",
1728
- className
1845
+ className,
1846
+ allDatesAction = true
1729
1847
  }) => {
1730
1848
  const { locale, messages } = useStreetImageryI18n();
1731
1849
  const text = messages.dateFilter;
@@ -1799,7 +1917,7 @@ var PhotoDateRangeFilter = ({
1799
1917
  {
1800
1918
  className: `flex-1 rounded-t-[1px] ${inRange ? "bg-slate-500" : "bg-slate-300"}`,
1801
1919
  style: {
1802
- height: count === 0 ? 0 : Math.max(2, Math.sqrt(count / maxCount) * CHART_HEIGHT_PX)
1920
+ height: count === 0 ? 0 : Math.max(2, Math.log1p(count) / Math.log1p(maxCount) * CHART_HEIGHT_PX)
1803
1921
  },
1804
1922
  title: count > 0 ? text.photoCount(count) : void 0
1805
1923
  },
@@ -1905,7 +2023,7 @@ var PhotoDateRangeFilter = ({
1905
2023
  )) }),
1906
2024
  /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-baseline justify-between gap-2 text-xs", children: [
1907
2025
  /* @__PURE__ */ jsx("span", { className: "font-medium text-slate-700", children: rangeLabel }),
1908
- isAllDates ? null : /* @__PURE__ */ jsx(
2026
+ isAllDates || !allDatesAction ? null : /* @__PURE__ */ jsx(
1909
2027
  "button",
1910
2028
  {
1911
2029
  className: "shrink-0 font-medium text-slate-600 underline decoration-slate-300 underline-offset-2 hover:text-slate-900",
@@ -9,8 +9,9 @@ type PanoramaxPanelProps = {
9
9
  /** The shown picture with creator, licence, local capture time and camera. */
10
10
  onViewerPhoto?: (photo: NormalizedPhoto) => void;
11
11
  /**
12
- * Hide the viewer's own legend (a bottom drawer in narrow containers). The host must then show
13
- * creator and licence itself, from `onViewerPhoto`.
12
+ * Leave out the viewer's own widgets: the legend (a bottom drawer in narrow containers), the
13
+ * player and the zoom buttons. The host must then show creator and licence itself, from
14
+ * `onViewerPhoto`. Stepping through a sequence still works with the arrows in the photo.
14
15
  */
15
16
  hideLegend?: boolean;
16
17
  };
@@ -1,4 +1,4 @@
1
- import { useStreetImageryI18n } from './chunk-ZAIXGYAH.js';
1
+ import { useStreetImageryI18n } from './chunk-6EMFFIL2.js';
2
2
  import { providerById, formatLocalDateTime, formatDate, formatRelativeDate } from '@osm-editor-kit/street-imagery';
3
3
  import { useRef, useEffect } from 'react';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -7,6 +7,10 @@ export type StreetImageryClickFeature = {
7
7
  featureId?: string;
8
8
  sequenceId?: string;
9
9
  };
10
+ /**
11
+ * Layers to pass as `interactiveLayerIds`. A click returns the features of all layers it hits, the
12
+ * top one first: the photo dot itself, then view shapes, then the larger invisible targets.
13
+ */
10
14
  export declare const streetImageryInteractiveLayerIds: (providers: ProviderId[]) => string[];
11
15
  export declare const queryStreetImageryFeatures: (event: {
12
16
  features?: MapGeoJSONFeature[] | null;
@@ -0,0 +1,3 @@
1
+ export { MapillaryPanel } from '../panels/MapillaryPanel';
2
+ export { PanoramaxPanel } from '../panels/PanoramaxPanel';
3
+ export { mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo, } from '../panels/mapillaryLookAt';
@@ -0,0 +1,3 @@
1
+ export { MapillaryPanel, mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo } from './chunk-SGIB7AVC.js';
2
+ export { PanoramaxPanel } from './chunk-KZTMKQT5.js';
3
+ import './chunk-25UGGXIE.js';
@@ -19,10 +19,14 @@ export type FloatingPhotoViewerProps = {
19
19
  onSelectSuggestion: (suggestion: ViewSuggestion, candidate: PhotoCandidate) => void;
20
20
  /** The photo in the viewer, so the active view can show which of its photos that is. */
21
21
  shownPhotoId?: string | null;
22
- canGoBack: boolean;
23
- canGoForward: boolean;
24
- onBack: () => void;
25
- onForward: () => void;
22
+ /**
23
+ * History of shown photos: the back and forward buttons and the keys [ and ]. Leave out
24
+ * `onBack` and `onForward` for a host without a history; the buttons are then not shown.
25
+ */
26
+ canGoBack?: boolean;
27
+ canGoForward?: boolean;
28
+ onBack?: () => void;
29
+ onForward?: () => void;
26
30
  onClose: () => void;
27
31
  /**
28
32
  * Step to the neighbouring photo, e.g. along the clicked street: the `<` `>` buttons at the
@@ -51,10 +55,19 @@ export type FloatingPhotoViewerProps = {
51
55
  };
52
56
  /** localStorage key for position, width and minimized state. */
53
57
  storageKey?: string;
58
+ /**
59
+ * Where the box is until the user moves it: the bottom right corner (default), or the top left
60
+ * one with `left` and `top` in px from the container's edges, e.g. next to a side panel.
61
+ */
62
+ defaultPosition?: {
63
+ corner: 'top-left';
64
+ left?: number;
65
+ top?: number;
66
+ };
54
67
  };
55
68
  /**
56
69
  * Photo viewer box floating over the map. Render inside a positioned (relative) map container.
57
70
  * Drag the header to move, drag the left edge to resize. Keys: Esc closes, [ and ] step history,
58
71
  * Alt + arrow left/right step along (`step`).
59
72
  */
60
- export declare const FloatingPhotoViewer: ({ title, titleActions, suggestions, activeDirectionKey, onSelectSuggestion, shownPhotoId, canGoBack, canGoForward, onBack, onForward, onClose, step, toolbar, status, children, footer, drawer, storageKey, }: FloatingPhotoViewerProps) => import("react").JSX.Element;
73
+ export declare const FloatingPhotoViewer: ({ title, titleActions, suggestions, activeDirectionKey, onSelectSuggestion, shownPhotoId, canGoBack, canGoForward, onBack, onForward, onClose, step, toolbar, status, children, footer, drawer, storageKey, defaultPosition, }: FloatingPhotoViewerProps) => import("react").JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osm-editor-kit/street-imagery-react",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "0.1.0-alpha.9",
4
4
  "description": "React MapLibre layers and photo viewers for street-level imagery providers.",
5
5
  "homepage": "https://github.com/osmberlin/street-space-editor/tree/main/packages/street-imagery-react",
6
6
  "license": "MIT",
@@ -24,6 +24,10 @@
24
24
  "types": "./dist/photo-details/index.d.ts",
25
25
  "import": "./dist/photo-details.js"
26
26
  },
27
+ "./viewer-panels": {
28
+ "types": "./dist/viewer-panels/index.d.ts",
29
+ "import": "./dist/viewer-panels.js"
30
+ },
27
31
  "./package.json": "./package.json"
28
32
  },
29
33
  "publishConfig": {
@@ -56,7 +60,7 @@
56
60
  "zustand": "^5.0.7"
57
61
  },
58
62
  "peerDependencies": {
59
- "@osm-editor-kit/street-imagery": "^0.1.0-alpha.7",
63
+ "@osm-editor-kit/street-imagery": "^0.1.0-alpha.8",
60
64
  "@panoramax/web-viewer": "^5",
61
65
  "@tanstack/react-query": "^5",
62
66
  "mapillary-js": "^4",
@@ -75,6 +79,10 @@
75
79
  "types": "./dist/photo-details/index.d.ts",
76
80
  "import": "./dist/photo-details.js"
77
81
  },
82
+ "./viewer-panels": {
83
+ "types": "./dist/viewer-panels/index.d.ts",
84
+ "import": "./dist/viewer-panels.js"
85
+ },
78
86
  "./package.json": "./package.json"
79
87
  }
80
88
  }
@@ -1,2 +0,0 @@
1
- export { MapillaryPanel } from './chunk-53RA4OSJ.js';
2
- import './chunk-25UGGXIE.js';
@@ -1,2 +0,0 @@
1
- export { PanoramaxPanel } from './chunk-EQ2JKLGT.js';
2
- import './chunk-25UGGXIE.js';