@minmaps-dev/mm-web-sdk 1.0.0-rc.3 → 1.0.0-rc.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { IControl, ControlPosition, Map } from 'maplibre-gl';
1
+ import { IControl, ControlPosition, StyleSpecification, Map } from 'maplibre-gl';
2
2
 
3
3
  /**
4
4
  * Represents a floor in a building
@@ -6,6 +6,12 @@ import { IControl, ControlPosition, Map } from 'maplibre-gl';
6
6
  interface Floor {
7
7
  /** Unique floor identifier */
8
8
  id: string | number;
9
+ /**
10
+ * Underlying map entity id. Separate from `id` (the floor entity id) — used
11
+ * by waypoints (`wp.mapId`) and floor-geojson endpoints. Optional because
12
+ * not all providers populate it.
13
+ */
14
+ mapId?: string | number;
9
15
  /** Display name of the floor */
10
16
  name: string;
11
17
  /** Floor sequence/order (e.g., -1 for basement, 0 for ground, 1 for first floor) */
@@ -32,7 +38,7 @@ interface POI {
32
38
  /** Unique identifier */
33
39
  id: string | number;
34
40
  /** POI type */
35
- type: 'amenity' | 'destination';
41
+ type: 'amenity' | 'destination' | 'kiosk';
36
42
  /** Display name */
37
43
  name: string;
38
44
  /** Geographic coordinates [longitude, latitude] */
@@ -55,6 +61,9 @@ interface POI {
55
61
  waypoint?: Waypoint;
56
62
  /** Marks this POI as the "You are here" kiosk */
57
63
  isYouAreHere?: boolean;
64
+ /** For kiosk POIs only: compass heading the device physically faces, in
65
+ * degrees clockwise from north. Sourced from JACS `Device.heading`. */
66
+ heading?: number | null;
58
67
  }
59
68
  /**
60
69
  * Amenity - a specific type of POI (facilities, services)
@@ -64,8 +73,25 @@ interface Amenity {
64
73
  id: string | number;
65
74
  /** Display name */
66
75
  name: string;
67
- /** SVG icon data */
76
+ /** Inline SVG markup (legacy fullcall DTO). When absent, icon is resolved from `uris`. */
68
77
  svg?: string;
78
+ /**
79
+ * Hosted icon descriptors from JACS (`uris[].locales[].uriPath`). Used by
80
+ * the SDK's runtime icon fetcher to register a map image at init time.
81
+ */
82
+ uris?: Array<{
83
+ mimeType?: string;
84
+ resourceType?: string;
85
+ locales?: Array<{
86
+ locale?: string;
87
+ uriPath?: string;
88
+ }>;
89
+ }>;
90
+ /**
91
+ * Map-image id used as `icon-image` in the POI style layer. Decorated
92
+ * in-place by AmenityIcons.register once the SVG is fetched and registered.
93
+ */
94
+ iconId?: string;
69
95
  /** Search keywords */
70
96
  keywords?: string[];
71
97
  /** Associated waypoints */
@@ -89,6 +115,20 @@ interface Destination {
89
115
  category?: string;
90
116
  /** Additional properties */
91
117
  properties?: Record<string, unknown>;
118
+ /** Uploaded image (e.g. brand/store logo). JACS `/all` fullcall serves this
119
+ * as `{ items: [{ resourceType, mimeType, path }] }`; the legacy/basic shape
120
+ * is `[{ locales: [{ uriPath }] }]`. `DestinationIcons.register` handles both. */
121
+ uris?: unknown;
122
+ /** Free-form CMS metadata (reserved for future destination icon options). */
123
+ extensors?: Record<string, unknown>;
124
+ /** Map-manager "Label Display" mode (JACS `displayMode`):
125
+ * `0` Hidden (dot only) · `1` Label (dot + text) · `2` Image (uploaded logo).
126
+ * Undefined → treated as Label (backwards-compatible default). */
127
+ displayMode?: number;
128
+ /** Runtime-assigned map-image id, decorated in place by
129
+ * `DestinationIcons.register` (only for `displayMode === 2`).
130
+ * Read by the POI builder + theme layer. */
131
+ iconId?: string;
92
132
  }
93
133
  /**
94
134
  * Waypoint - a specific point location
@@ -121,6 +161,23 @@ interface POISearchResult {
121
161
  matchedFields: string[];
122
162
  }
123
163
 
164
+ /**
165
+ * A Zone is a CMS-authored department category — a named, colored grouping
166
+ * (e.g. "Cardiology", "Surgical", "Admin"). Zones are venue-wide (not
167
+ * floor-scoped) in the JACS `/all` payload and carry no geometry of their own;
168
+ * their spatial footprint is derived from the waypoints that reference them
169
+ * (`waypoint.zoneId`), which lets the SDK color-code the room/unit polygons a
170
+ * zone covers. See `map/zones.ts` for the derivation.
171
+ */
172
+ interface Zone {
173
+ id: number;
174
+ name: string;
175
+ /** Authored hex color (e.g. "#d32f2f"); may be empty when the author
176
+ * hasn't picked one — such zones are skipped for color-coding. */
177
+ color: string;
178
+ description?: string;
179
+ }
180
+
124
181
  /** Captured camera state (center, zoom, pitch, bearing) */
125
182
  type CameraState = {
126
183
  center: [number, number];
@@ -140,6 +197,83 @@ type ViewOptions = {
140
197
  /** SW/NE bounding box */
141
198
  type Bounds = [[number, number], [number, number]];
142
199
 
200
+ /** How to center the map after computing a route */
201
+ type WayfindCenterMode = 'none' | 'destination' | 'route';
202
+ /**
203
+ * Per-call routing preferences forwarded to the wayfinding provider.
204
+ *
205
+ * - `accessible` — prefer accessible paths; providers should drop or heavily
206
+ * penalize edges marked inaccessible (stairs by default, anything else
207
+ * the provider's data flags).
208
+ * - `avoidStairs` — hard-filter stairs from the graph.
209
+ *
210
+ * The bundled stub provider doesn't compute routes; consumers wire a real
211
+ * provider (see `mm-web-sdk-example/lib/wayfinding`) that reads these
212
+ * flags when building edge weights.
213
+ */
214
+ type RoutingOptions = {
215
+ accessible?: boolean;
216
+ avoidStairs?: boolean;
217
+ };
218
+
219
+ /**
220
+ * A single point along the rendered route. `mapId` lets the renderer
221
+ * split the polyline by floor (so other-floor segments don't paint
222
+ * through the visible floor's geometry); `waypointId` and `pathTypeId`
223
+ * let downstream layers (text-directions, step UI) classify
224
+ * transitions — e.g. "Take the elevator" vs "Walk straight". The two
225
+ * endpoint points are the kiosk and destination anchors (`isEndpoint`
226
+ * marks them so they can render endpoint pins). All fields except
227
+ * `coordinates` are optional for backward-compat with the straight-line
228
+ * fallback.
229
+ */
230
+ type RoutePoint = {
231
+ coordinates: [number, number];
232
+ /** Floor mapId this point sits on. Missing on the straight-line fallback. */
233
+ mapId?: number;
234
+ /** JACS waypoint id this point resolved to, if any. */
235
+ waypointId?: number;
236
+ /** Path type of the edge that LEADS INTO this point (i.e. the edge
237
+ * whose `toKey` is this node). Used to detect elevator / stair /
238
+ * escalator transitions. Missing on the start point and on the
239
+ * straight-line fallback. */
240
+ pathTypeId?: number;
241
+ /** True for the snapped kiosk + destination anchors. */
242
+ isEndpoint?: boolean;
243
+ };
244
+
245
+ type WayfindStep = DepartStep | TurnStep | ContinueStep | TransitionStep$1 | ArriveStep;
246
+ type StepBase = {
247
+ /** Indices into the input `points` array that this step spans. */
248
+ pointRange: [startInclusive: number, endInclusive: number];
249
+ /** Floor this step is on. `null` for cross-floor transitions. */
250
+ floorId: number | null;
251
+ /** Localized instruction text. */
252
+ text: string;
253
+ /** Total walking distance within this step, in meters (rounded). */
254
+ distanceMeters?: number;
255
+ /** Landmark name used for anchoring, if any. */
256
+ landmark?: string;
257
+ };
258
+ type DepartStep = StepBase & {
259
+ type: 'depart';
260
+ };
261
+ type TurnStep = StepBase & {
262
+ type: 'turn-left' | 'turn-right' | 'u-turn';
263
+ };
264
+ type ContinueStep = StepBase & {
265
+ type: 'continue';
266
+ };
267
+ type TransitionStep$1 = StepBase & {
268
+ type: 'transition';
269
+ transition: 'elevator' | 'stairs' | 'escalator';
270
+ fromFloorId: number | null;
271
+ toFloorId: number | null;
272
+ };
273
+ type ArriveStep = StepBase & {
274
+ type: 'arrive';
275
+ };
276
+
143
277
  interface MapEvent {
144
278
  floor?: Floor;
145
279
  poi?: POI;
@@ -147,18 +281,162 @@ interface MapEvent {
147
281
  error?: any;
148
282
  venue?: any;
149
283
  camera?: CameraState;
284
+ /** Emitted by `themeChanged`; `'custom'` when consumer passed a raw style. */
285
+ theme?: 'default' | 'high-contrast' | 'custom';
286
+ /** Emitted by `localeChanged` after `setLocale()` finishes patching the
287
+ * venue model. BCP-47 code (`'en'`, `'es'`, `'es-MX'`). */
288
+ locale?: string;
289
+ /** Emitted by `routeReady` after a successful `navigateFromKioskToPOI`.
290
+ * Carries the raw geometry (`points`) and the human-readable
291
+ * step-by-step directions. Subscribe to drive a turn-by-turn UI. */
292
+ route?: {
293
+ points: RoutePoint[];
294
+ steps: WayfindStep[];
295
+ /** Floor mapId of each segment, in render order. `null` for
296
+ * segments emitted by the straight-line fallback. */
297
+ floorIds: Array<number | null>;
298
+ };
150
299
  }
151
300
  type EventCallback = (event: MapEvent) => void;
152
301
 
302
+ /**
303
+ * Padding kept clear inside the viewport when the SDK frames bounds —
304
+ * the initial venue/floor fit and the wayfinding route fit. A plain
305
+ * number pads all sides equally; the object form lets a kiosk reserve
306
+ * space for fixed UI overlays (header, dock, side rails) so framed
307
+ * content is never hidden behind them.
308
+ */
309
+ type BoundsPadding = number | {
310
+ top: number;
311
+ bottom: number;
312
+ left: number;
313
+ right: number;
314
+ };
315
+ /**
316
+ * Built-in theme names accepted by `MinuteMaps.setTheme()` and the
317
+ * `options.theme` init option. Pass a `StyleSpecification` directly for
318
+ * custom themes.
319
+ */
320
+ type ThemeName = 'default' | 'high-contrast';
321
+ /**
322
+ * Style of the disc + ring rendered behind each amenity icon. The SDK
323
+ * composites this into the icon bitmap at registration time, so badge +
324
+ * icon participate in symbol collision as one unit.
325
+ */
326
+ type AmenityBadgeStyle = {
327
+ /** Fill colour of the badge disc. Default `'#fdb81e'`. */
328
+ color?: string;
329
+ /** Stroke colour of the ring around the disc. Default `'#FFFFFF'`. */
330
+ ringColor?: string;
331
+ /** Ring width in logical px. Default `2`. */
332
+ ringWidth?: number;
333
+ };
334
+ type DestinationChipStyle = {
335
+ /** Chip fill colour. Default `'#FFFFFF'`. */
336
+ color?: string;
337
+ /** Chip border colour. Default navy `'#162e51'`. */
338
+ borderColor?: string;
339
+ /** Chip border width in logical px. Default `2`. */
340
+ borderWidth?: number;
341
+ };
153
342
  interface SDKOptions {
154
343
  debug?: boolean;
155
- theme?: 'light' | 'dark' | 'hybrid' | any;
344
+ /**
345
+ * Initial theme. `'default'` uses the bundled hybrid 3D theme.
346
+ * `'high-contrast'` uses the WCAG-AA tuned theme. Pass a
347
+ * `StyleSpecification` for fully custom styling.
348
+ */
349
+ theme?: ThemeName | any;
350
+ /**
351
+ * When true, the SDK skips animations on imperative camera calls
352
+ * (wayfinding fits, `setView`, idle re-frames). Consumers should
353
+ * mirror their app's `prefers-reduced-motion` state into this.
354
+ */
355
+ reducedMotion?: boolean;
356
+ /**
357
+ * Whether a freshly computed route auto-highlights its first step's
358
+ * segment (the brighter `route-line-active` overlay). Defaults to
359
+ * `true`, preserving the turn-by-turn segment highlight that
360
+ * `setActiveStep` drives.
361
+ *
362
+ * Set `false` for kiosk-style "show the whole route at once" UIs:
363
+ * the entire route line is shown without singling out one segment,
364
+ * which reads more clearly at a glance and avoids implying the visitor
365
+ * must step through the route. `setActiveStep` still works when called
366
+ * explicitly (e.g. a paginated QR-handoff / mobile flow) — this only
367
+ * controls the automatic highlight on `routeReady`.
368
+ */
369
+ routeStepHighlight?: boolean;
156
370
  initialFloor?: string | number;
157
371
  enableInteractions?: boolean;
158
372
  customSprite?: string;
159
373
  minIndoorZoom?: number;
374
+ /**
375
+ * Interior unit-wall thickness in feet (default 1). Each wall is built by
376
+ * insetting a ring into each unit by `wallThickness / 2`, so adjacent units
377
+ * share a wall (their rings abut at the common edge). Floored at 0.5 — a
378
+ * thinner value collapses the negative buffer and drops the wall.
379
+ */
160
380
  wallThickness?: number;
161
- boundsPadding?: number;
381
+ boundsPadding?: BoundsPadding;
382
+ /**
383
+ * Pitch (deg) for the opening view, applied centred on the kiosk ("You
384
+ * are here"). Omit for top-down.
385
+ */
386
+ initialPitch?: number;
387
+ /**
388
+ * Zoom for the opening view, centred on the kiosk. Set this above the
389
+ * theme's unit-walls breakpoint (~16.5 in the bundled hybrid theme) so the
390
+ * kiosk opens on the building's interior floor plan (its contents) rather
391
+ * than a zoomed-out 3D massing outline. Omit to keep the post-floor-fit
392
+ * zoom.
393
+ */
394
+ initialZoom?: number;
395
+ /**
396
+ * Clamp zoom-out relative to the opening view. When set, the map's
397
+ * `minZoom` becomes `(initialZoom − this)`, so visitors can nudge out by
398
+ * this many zoom levels but never pull back below the interior into the
399
+ * massing/region. `0` locks zoom-out exactly to the opening view.
400
+ */
401
+ minZoomBelowInitialFit?: number;
402
+ /**
403
+ * Override the colour the SDK recolours every amenity SVG to before it
404
+ * composites the badge. `none` / `transparent` fills are preserved so
405
+ * cut-outs stay. When the badge is enabled (the default), this defaults
406
+ * to navy (`#162e51`) so icons read on gold. Set explicitly for a
407
+ * different look, or set `amenityBadge: false` to disable recolouring
408
+ * altogether and keep the CMS-uploaded colours.
409
+ */
410
+ amenityIconColor?: string;
411
+ /**
412
+ * Badge composited behind each amenity icon. Pass `false` to render the
413
+ * icon alone (no badge — useful for high-contrast or 2D themes where the
414
+ * gold disc would compete with the floor). Pass an object to tune the
415
+ * disc / ring style. Defaults to a VA-gold disc with a 2px white ring.
416
+ *
417
+ * The badge is baked into the icon bitmap (canvas composite) rather than
418
+ * drawn as a separate circle layer, so badge + icon participate in symbol
419
+ * collision together — overlapping amenities hide as one unit instead of
420
+ * the icon hiding while the disc stays painted.
421
+ */
422
+ amenityBadge?: AmenityBadgeStyle | false;
423
+ /**
424
+ * Badge for vertical-circulation connectors (elevator / stairs / escalator).
425
+ * These render as their own visual class — a navy disc with a white glyph —
426
+ * so circulation reads distinct from the gold service amenities. Pass `false`
427
+ * to fall them back into the regular gold badge, or an object to tune the
428
+ * disc / ring. When `amenityBadge` is `false`, connectors are badge-less too
429
+ * unless this is set explicitly.
430
+ */
431
+ connectorBadge?: AmenityBadgeStyle | false;
432
+ /**
433
+ * Chip composited behind each destination's uploaded logo. Destinations are
434
+ * full-colour brand images (not recoloured), so they get a neutral white
435
+ * rounded chip for legibility instead of the amenity gold badge. Pass `false`
436
+ * to render the logo bare, or an object to tune the chip fill / border.
437
+ * Defaults to a white chip with a 2px navy border.
438
+ */
439
+ destinationChip?: DestinationChipStyle | false;
162
440
  styleMode?: 'venueStyleUrl' | 'sdkTemplate';
163
441
  templateOverrideMode?: 'colorsOnly' | 'colorsAndConstants' | 'all';
164
442
  }
@@ -190,6 +468,13 @@ type SDKConfig = {
190
468
  venueId: number;
191
469
  locale?: string;
192
470
  auth?: JMapAuth;
471
+ /**
472
+ * Identifies which physical kiosk this instance is, so the SDK can pin
473
+ * the "You are here" marker. Matched against either the numeric device
474
+ * `id` or the device `uuid` from the venue's `devices`. The device's
475
+ * attached waypoint (`waypoint.deviceIds`) is the kiosk's location.
476
+ */
477
+ deviceId?: string | number;
193
478
  };
194
479
  jacs: {
195
480
  mode: 'proxy' | 'direct';
@@ -204,9 +489,6 @@ type SDKConfig = {
204
489
  options?: SDKOptions;
205
490
  };
206
491
 
207
- /** How to center the map after computing a route */
208
- type WayfindCenterMode = 'none' | 'destination' | 'route';
209
-
210
492
  type LoggerFn = (...args: unknown[]) => void;
211
493
  type AmenityManagerDeps = {
212
494
  getVenue: () => any;
@@ -224,6 +506,19 @@ declare class AmenityManager {
224
506
  private log;
225
507
  private loadForFloor;
226
508
  getAll(): AmenityWithFloor[];
509
+ /**
510
+ * Venue-wide amenities, one entry per id. `getAll()` returns each amenity
511
+ * once per floor it appears on (and so renders a multi-floor "Bathroom"
512
+ * three times in a venue list); this collapses those into a single record
513
+ * with all its waypoints merged across floors.
514
+ *
515
+ * Also folds any duplicate-id records from the provider (a CMS data bug)
516
+ * into a single entry so React lists don't trip on dup keys.
517
+ *
518
+ * Use this for venue-wide browse UIs; pair it with `findClosestWaypoint`
519
+ * (on the SDK) to resolve a tap to the nearest physical instance.
520
+ */
521
+ getDistinct(): Amenity[];
227
522
  getByFloorId(floorId: Floor['id']): AmenityWithFloor[];
228
523
  getAllKiosks(): AmenityWithFloor[];
229
524
  getKioskForFloor(floorId: Floor['id']): AmenityWithFloor | null;
@@ -243,8 +538,25 @@ declare class MinuteMaps {
243
538
  private readonly logger;
244
539
  private defaultCamera;
245
540
  private viewModes;
541
+ /** Mirror of `config.options.reducedMotion`, mutable via `setReducedMotion`. */
542
+ private reducedMotion;
543
+ /** Tracks the active theme name so `setTheme()` is a no-op when re-requested. */
544
+ private activeThemeName;
545
+ /** Tracks the active locale so `setLocale()` is a no-op when re-requested.
546
+ * Seeded from `config.jmap.locale` at construction; `undefined` means
547
+ * whatever the customer's default locale is on the JACS side. */
548
+ private currentLocale;
246
549
  private amenityManager;
247
550
  private wayfinding;
551
+ private highlightManager;
552
+ private youAreHerePulse;
553
+ /** Categorical POI layers in the bundled theme that `setPOIFilter`
554
+ * toggles. Layers absent from this list — `poi-you-are-here-*`, the
555
+ * `poi-accessibility-icons` badge, and the `poi-highlight-*` layers —
556
+ * are intentionally unaffected; they're either anchors, status
557
+ * indicators, or transient focus state, not categorical content. */
558
+ private static readonly POI_CATEGORY_LAYERS;
559
+ private poiVisibleTypes;
248
560
  constructor(config: SDKConfig);
249
561
  init(): Promise<void>;
250
562
  on(event: string, cb: (e: MapEvent) => void): void;
@@ -256,9 +568,69 @@ declare class MinuteMaps {
256
568
  animate?: boolean;
257
569
  duration?: number;
258
570
  }): void;
571
+ /**
572
+ * Content-aware re-frame: fit an active route if one is displayed,
573
+ * otherwise fit the current floor's geojson. Unlike `resetView()`, this
574
+ * preserves the user's pitch and bearing — it only adjusts center/zoom
575
+ * to bring the scene back into view. Intended use is after the map
576
+ * container resizes (e.g. a side drawer opens beside the map and the
577
+ * pane gets narrower), so what matters stays framed without a yank.
578
+ */
579
+ refit(opts?: {
580
+ animate?: boolean;
581
+ duration?: number;
582
+ }): void;
583
+ /**
584
+ * Update the bounds padding (camera insets) used by route + floor fits.
585
+ * Consumers measure their on-screen chrome (panels, rails) and pass the
586
+ * insets so a fit frames the path in the visible, unobscured area rather
587
+ * than under the UI. Stored; the next fit / `refit()` uses it (clamped so
588
+ * a fit always lands — see `clampPadding`).
589
+ */
590
+ setBoundsPadding(p: BoundsPadding): void;
591
+ private fitToBounds;
259
592
  set3dEnabled(enabled: boolean): void;
260
593
  toggle3d(): void;
261
594
  getIs3dEnabled(): boolean;
595
+ /**
596
+ * Set the active map theme by name (`'default'` / `'high-contrast'`) or by
597
+ * passing a custom `StyleSpecification`. The current floor, route, POIs
598
+ * and camera are preserved across the swap — only layer styling changes.
599
+ *
600
+ * Used by kiosk consumers to wire the OS-level / popover high-contrast
601
+ * toggle to the map. See `KIOSK.md` for the pattern.
602
+ */
603
+ setTheme(theme: ThemeName | StyleSpecification): Promise<void>;
604
+ /** Currently active theme name (or `'custom'` when a raw style was passed). */
605
+ getActiveTheme(): 'default' | 'high-contrast' | 'custom';
606
+ /**
607
+ * Update the SDK's reduced-motion preference at runtime. When true,
608
+ * imperative camera animations (route fits, idle re-frames) and the
609
+ * route line-draw animation run with `duration: 0`.
610
+ */
611
+ setReducedMotion(enabled: boolean): void;
612
+ getReducedMotion(): boolean;
613
+ /**
614
+ * Switch the locale used for POI / amenity / destination / floor names at
615
+ * runtime. The SDK refetches localized names from JACS (per-entity
616
+ * endpoints — `/all` does not honor `?locale=`), patches the in-memory
617
+ * venue model in place, then redraws the POI source for the current floor
618
+ * so map labels render in the new language.
619
+ *
620
+ * Structural data (waypoints, coordinates, iconIds) is preserved — only
621
+ * the localized fields (`name`, `description`) move.
622
+ *
623
+ * Emits a `localeChanged` event with the new code after the patch
624
+ * completes. No-op when the code matches the current locale.
625
+ *
626
+ * Throws if the underlying data provider doesn't implement locale support
627
+ * (only `JacsDataProvider` does today), or if the JACS fetch fails — the
628
+ * prior locale is left intact in that case.
629
+ */
630
+ setLocale(locale: string): Promise<void>;
631
+ /** Currently active locale (BCP-47), or `undefined` when no locale has
632
+ * been set — meaning JACS resolves names to the customer's default. */
633
+ getActiveLocale(): string | undefined;
262
634
  setUnits2dEnabled(enabled: boolean): void;
263
635
  toggleUnits2d(): void;
264
636
  getIsUnits2dEnabled(): boolean;
@@ -269,26 +641,245 @@ declare class MinuteMaps {
269
641
  getCurrentFloor(): Floor | null;
270
642
  getDefaultFloor(): Floor | null;
271
643
  getDestinations(floor?: Floor): Destination[];
644
+ getZones(): Zone[];
272
645
  getPolygonLayers(): any[];
273
646
  getFloorMapTemplate3d(floorId: string | number): any[];
274
647
  getAllPOIs(floor?: Floor): POI[];
275
648
  getYouAreHerePOI(floor?: Floor): POI | null;
276
649
  getYouAreHereCoordinates(floor?: Floor): [number, number] | null;
650
+ /**
651
+ * Compass heading the kiosk's physical hardware faces, in degrees
652
+ * clockwise from north. Sourced from JACS `Device.heading`. `null` when
653
+ * the venue's device record has no heading set.
654
+ *
655
+ * In order to rotate the map so it is oriented towards the user's gaze,
656
+ * 180 is subtracted from `Device.heading`.
657
+ *
658
+ * Drives `recenterOnKiosk({ useHeading: true })` and the directional
659
+ * cone rendered next to the "You are here" marker.
660
+ */
661
+ getKioskHeading(): number | null;
662
+ /**
663
+ * Display name of the JACS device the kiosk is anchored to (e.g.
664
+ * "Main Lobby Kiosk", "Information Desk Kiosk"). `null` when the venue
665
+ * has no kiosk anchor or the device record carries no `name`.
666
+ *
667
+ * Distinct from `getYouAreHerePOI()?.name`, which is always the
668
+ * literal "You are here" label rendered on the map. Use this for chrome
669
+ * that needs the kiosk's identity — e.g. the directions overlay's
670
+ * "From <kiosk>" header.
671
+ */
672
+ getKioskName(): string | null;
673
+ /**
674
+ * Distance + walking time from the kiosk's anchor waypoint to a target,
675
+ * computed over the JACS path graph (the same graph the routing engine
676
+ * uses). Designed for "Closest: 30 sec walk" subtitles in the consumer
677
+ * UI — no second graph build, no second Dijkstra implementation.
678
+ *
679
+ * Accepts a raw waypoint id, an amenity / destination object (the first
680
+ * entry of its `waypoints` array is treated as the entry point), or any
681
+ * `{ id, mapId? }` shape. Returns `null` when the kiosk isn't anchored,
682
+ * the target waypoint isn't on the graph, or no path resolves.
683
+ *
684
+ * Walking speed defaults to 1.2 m/s (indoor wayfinding norm). Pass
685
+ * `walkingSpeedMps` to estimate for accessibility (e.g. 0.9).
686
+ */
687
+ getWalkTimeFromKiosk(target: number | string | {
688
+ id?: number | string;
689
+ mapId?: number;
690
+ } | {
691
+ waypoints?: Array<number | string | {
692
+ id?: number | string;
693
+ }>;
694
+ }, opts?: {
695
+ walkingSpeedMps?: number;
696
+ }): {
697
+ meters: number;
698
+ seconds: number;
699
+ pathNodeCount: number;
700
+ } | null;
701
+ /**
702
+ * Center the camera on the kiosk's "You are here" position. Use this
703
+ * (not `resetView`) for a chrome "Recenter" button — `resetView` snaps
704
+ * to the SDK's captured default camera, which may have been a venue
705
+ * overview rather than the kiosk's spot.
706
+ *
707
+ * When `useHeading` is true (default) and the kiosk device has a
708
+ * `heading` on file, the camera's bearing is rotated so the direction
709
+ * the kiosk physically faces ends up at the top of the screen. That
710
+ * way "what the user sees in front of them" matches "what's up on the
711
+ * map" — the single largest readability win for stressed indoor users.
712
+ */
713
+ recenterOnKiosk(opts?: {
714
+ useHeading?: boolean;
715
+ zoom?: number;
716
+ pitch?: number;
717
+ animate?: boolean;
718
+ duration?: number;
719
+ }): void;
277
720
  searchPOIs(query: string, floor?: Floor): POI[];
721
+ /**
722
+ * Search POIs across every floor in the venue. Returns raw matches —
723
+ * an amenity that exists on multiple floors appears multiple times,
724
+ * once per instance. Consumers that need one row per logical amenity
725
+ * should dedupe by id (and, for "nearest", pick the instance closest
726
+ * to the kiosk via planar distance on `coordinates`).
727
+ */
728
+ searchAllPOIs(query: string): POI[];
278
729
  wayfindBetweenWaypoints(fromWaypoint: any, toWaypoint: any, options?: {
279
730
  centerMode?: 'none' | 'destination' | 'route';
280
731
  zoom?: number;
732
+ /** Prefer accessible paths (drops or penalizes stairs / inaccessible edges). */
733
+ accessible?: boolean;
734
+ /** Hard-filter stairs from the graph. */
735
+ avoidStairs?: boolean;
281
736
  }): Promise<any>;
282
737
  navigateFromKioskToDestination(destination: any): Promise<any>;
738
+ /**
739
+ * Route from the kiosk to a specific POI's waypoint. Use this when you
740
+ * already hold a resolved POI (e.g. from `searchPOIs` / `searchAllPOIs`).
741
+ * For a venue-wide amenity record with multiple instances, resolve via
742
+ * `findClosestWaypoint` first — or use `highlightAmenity` instead, if
743
+ * you want a visual focus rather than a route.
744
+ */
745
+ navigateFromKioskToPOI(poi: POI, options?: {
746
+ accessible?: boolean;
747
+ avoidStairs?: boolean;
748
+ }): Promise<any>;
749
+ /** Build the landmark / floor-name context the directions module needs.
750
+ * Pulled out so `setLocale()` or `setActiveStep()` can rebuild it on
751
+ * demand if we ever surface a locale-aware variant. */
752
+ private buildDirectionsContext;
753
+ /**
754
+ * Drive the turn-by-turn UI. Two effects:
755
+ *
756
+ * 1. When the step's `floorId` differs from the active floor, switch
757
+ * floors so the segment for the step's leg becomes visible.
758
+ * 2. Highlight the step's slice of the route line by writing the
759
+ * point range to the `route-active-step` source; the bundled
760
+ * theme's `route-line-active` layer paints it in gold over the
761
+ * muted base route.
762
+ *
763
+ * Transition steps (cross-floor elevator / stair hops) clear the
764
+ * highlight — the overlay text carries the action, and there's no
765
+ * meaningful on-floor segment to paint.
766
+ *
767
+ * Pass `null` to clear the highlight without changing the floor or
768
+ * tearing down the route.
769
+ */
770
+ setActiveStep(step: WayfindStep | null): Promise<void>;
771
+ private writeActiveStepHighlight;
772
+ private clearActiveStepHighlight;
283
773
  clearRoute(): void;
774
+ /**
775
+ * Highlight a single POI on the map — a pulsing ring — and bring it into
776
+ * view. Resolves the POI across all floors, switching the active floor if
777
+ * it lives on another one. Pass a POI / amenity / destination id.
778
+ */
779
+ highlightPOI(id: string | number): Promise<void>;
780
+ /** Remove the POI highlight set by `highlightPOI`. */
781
+ clearHighlight(): void;
782
+ /**
783
+ * Show only the named POI categories on the map. Pass `['amenity']` to
784
+ * emphasize amenities (e.g. while an Amenities drawer is open) — destination
785
+ * POIs hide. Call `clearPOIFilter()` to restore the default (all categories).
786
+ *
787
+ * The "You are here" marker, the wheelchair-accessibility badge, and any
788
+ * active highlight are not affected and always render.
789
+ */
790
+ setPOIFilter(types: Array<'amenity' | 'destination'>): void;
791
+ /** Restore the default — every POI category renders. */
792
+ clearPOIFilter(): void;
793
+ private applyPOIFilter;
794
+ /**
795
+ * Return the waypoint in `waypoints` closest to `from`. Useful for
796
+ * "route to the nearest X" against a venue-wide amenity record that has
797
+ * multiple physical instances. Defaults `from` to the kiosk's
798
+ * "You are here" coordinates.
799
+ *
800
+ * Distance is planar Euclidean on lng/lat — sufficient for ordering at
801
+ * single-venue scale, and avoids a turf dependency on the hot path.
802
+ */
803
+ findClosestWaypoint(waypoints: Waypoint[], from?: [number, number]): Waypoint | null;
804
+ /**
805
+ * Route from the kiosk to the closest physical instance of an amenity.
806
+ * Resolves the amenity venue-wide, picks the instance with the smallest
807
+ * path-graph walk time from the kiosk (falling back to planar Euclidean
808
+ * when the walk-time graph can't resolve any waypoint), switches the
809
+ * active floor if needed, and routes via the same plumbing as
810
+ * `navigateFromKioskToPOI` — so step-by-step directions, the
811
+ * `routeReady` event, and the active-floor camera fit all "just work."
812
+ *
813
+ * Pass `accessible` / `avoidStairs` to weight the *route* (the SDK's
814
+ * routing engine reads these per call). Note: closest-instance picking
815
+ * itself is currently distance-based and does not yet honor those
816
+ * weights — the underlying walk-time graph applies pixel-length only.
817
+ * In practice this matters when an amenity has multiple instances and
818
+ * the geometrically closest is reachable only via stairs; the picked
819
+ * instance won't change today, but the *route* to it will avoid stairs
820
+ * (or fail gracefully) if `accessible`/`avoidStairs` is set.
821
+ */
822
+ navigateFromKioskToClosestAmenity(amenityId: string | number, options?: {
823
+ accessible?: boolean;
824
+ avoidStairs?: boolean;
825
+ }): Promise<any>;
826
+ /**
827
+ * Highlight + center on the closest physical instance of an amenity.
828
+ * The amenity is resolved against `amenities.getDistinct()` (venue-wide,
829
+ * deduped); the closest waypoint to the kiosk is picked; the active floor
830
+ * is switched if that instance lives on another one.
831
+ *
832
+ * This is the right call from a venue-wide list ("here are all the
833
+ * bathrooms in the building, take me to the closest one"). For taps on
834
+ * a specific resolved POI (e.g. a search result that already names one
835
+ * instance), use `highlightPOI` instead.
836
+ */
837
+ highlightAmenity(amenityId: string | number): Promise<void>;
838
+ /** Which floor owns this waypoint, by matching mapId against the floor's
839
+ * `mapId` (or `id` as fallback). Used to switch floors when routing /
840
+ * highlighting hits an instance on a different one. */
841
+ private findFloorForWaypoint;
284
842
  getCameraPosition(): CameraState | null;
285
843
  getMap(): Map | null;
286
844
  destroy(): void;
287
845
  setCurrentFloor(floor: Floor): Promise<void>;
846
+ /**
847
+ * Filter the route-line / route-halo layers to only render segments
848
+ * whose `floorId` matches the active floor (or features that carry no
849
+ * `floorId` at all — the straight-line fallback, which we want visible
850
+ * on every floor since it has no floor membership to filter against).
851
+ *
852
+ * Features are tagged with the JACS pixel `mapId` (that's what flows
853
+ * through the route point's `mapId`), so the filter compares against
854
+ * the active floor's `mapId`, NOT its `id` — those are two different
855
+ * JACS identifiers (`floor.id` is the building-floor record; `mapId`
856
+ * is the SVG asset). We accept the floor's `id` here for convenience
857
+ * and resolve to `mapId` via `getFloors()`.
858
+ *
859
+ * Applied imperatively rather than baked into the theme JSON so the
860
+ * filter tracks runtime floor changes without restyling the map.
861
+ */
862
+ private updateRouteFloorFilter;
288
863
  isReady(): boolean;
289
864
  private setFloorLayerVisibility;
290
865
  private getBoundsPadding;
866
+ /**
867
+ * Shrink per-side padding if the requested insets would leave no room.
868
+ * `cameraForBounds` / `fitBounds` silently fail ("Map cannot fit within
869
+ * canvas") when an axis's total padding meets or exceeds that axis — cap
870
+ * each axis's total at 80% so a fit always has space to land.
871
+ */
872
+ private clampPadding;
291
873
  private getVenueBounds;
874
+ /**
875
+ * Opening-framing policy applied once after the initial floor fit.
876
+ * - `initialPitch`: tilt to this and re-fit the building footprint so the
877
+ * kiosk opens on a tilted building instead of a flat top-down plan.
878
+ * - `minZoomBelowInitialFit`: clamp the map's minZoom to
879
+ * `(fitZoom − margin)` so visitors can't pull back to the empty region.
880
+ * Both are no-ops when their option is unset.
881
+ */
882
+ private applyInitialFraming;
292
883
  private applyInitialViewFromVenue;
293
884
  private loadAndPatchVenueStyle;
294
885
  private ensureFloorLayersFromStyle;
@@ -296,5 +887,42 @@ declare class MinuteMaps {
296
887
  }
297
888
  declare function createMinuteMapsSDK(config: SDKConfig): MinuteMaps;
298
889
 
299
- export { MinuteMaps, createMinuteMapsSDK };
300
- export type { Amenity, AmenityWithFloor, Bounds, CameraState, Destination, EventCallback, Floor, FloorMetadata, JMapAuth, JMapConfig, JacsAuth, JacsConfig, MapEvent, POI, POISearchResult, SDKConfig, SDKOptions, ViewOptions, WayfindCenterMode, Waypoint };
890
+ type TransitionStep = Extract<WayfindStep, {
891
+ type: 'transition';
892
+ }>;
893
+ type RouteFloorSection = {
894
+ /** Floor (JACS `mapId`) this section is on. `null` only for a degenerate
895
+ * floorless route (straight-line fallback with no `mapId`). */
896
+ floorId: number | null;
897
+ /** This floor's steps, each with its index into the original flat array
898
+ * so the consumer can call `setActiveStep` / highlight by index. */
899
+ steps: Array<{
900
+ step: WayfindStep;
901
+ index: number;
902
+ }>;
903
+ /** Index of this section's first step in the original array — the target
904
+ * for `setActiveStep` when advancing INTO this section. */
905
+ firstStepIndex: number;
906
+ /** The transition step that leaves this floor for the next section, if
907
+ * any. Absent on the final section. Its `text` ("Take the elevator to
908
+ * Basement") makes a good "continue" button label. */
909
+ exit?: {
910
+ step: TransitionStep;
911
+ index: number;
912
+ };
913
+ };
914
+ /**
915
+ * Group steps into per-floor sections. Single-floor routes return one
916
+ * section (so a consumer can keep its whole-route view for that case).
917
+ */
918
+ declare function groupStepsIntoFloorSections(steps: WayfindStep[]): RouteFloorSection[];
919
+ /**
920
+ * Which section is "active" given the active step index — the last section
921
+ * whose `firstStepIndex` is at or before `activeStepIndex`. Returns 0 when
922
+ * nothing matches (e.g. index points at the leading transition, which
923
+ * shouldn't happen since routes start with a depart step).
924
+ */
925
+ declare function activeSectionIndex(sections: RouteFloorSection[], activeStepIndex: number): number;
926
+
927
+ export { MinuteMaps, activeSectionIndex, createMinuteMapsSDK, groupStepsIntoFloorSections };
928
+ export type { Amenity, AmenityBadgeStyle, AmenityWithFloor, Bounds, BoundsPadding, CameraState, Destination, DestinationChipStyle, EventCallback, Floor, FloorMetadata, JMapAuth, JMapConfig, JacsAuth, JacsConfig, MapEvent, POI, POISearchResult, RouteFloorSection, RoutePoint, RoutingOptions, SDKConfig, SDKOptions, ThemeName, TransitionStep, ViewOptions, WayfindCenterMode, WayfindStep, Waypoint, Zone };