@minmaps-dev/mm-web-sdk 1.0.0-rc.4 → 1.0.0-rc.40
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 +23 -15
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1873 -10
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.ts +307 -3
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/package.json +35 -17
- package/src/themes/alt3-hybrid-style.json +2074 -812
- package/src/themes/high-contrast-style.json +0 -383
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IControl, ControlPosition,
|
|
1
|
+
import { Map, IControl, ControlPosition, StyleSpecification } from 'maplibre-gl';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Represents a floor in a building
|
|
@@ -6,8 +6,18 @@ 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;
|
|
17
|
+
/** Short display name of the floor (e.g. "L2"). Optional: JACS leaves it
|
|
18
|
+
* unset on plenty of floors, and `JacsDataProvider` maps a blank one to
|
|
19
|
+
* `undefined` rather than inventing a value. */
|
|
20
|
+
shortName?: string;
|
|
11
21
|
/** Floor sequence/order (e.g., -1 for basement, 0 for ground, 1 for first floor) */
|
|
12
22
|
sequence?: number;
|
|
13
23
|
/** Associated map data */
|
|
@@ -23,6 +33,7 @@ interface Floor {
|
|
|
23
33
|
interface FloorMetadata {
|
|
24
34
|
id: string | number;
|
|
25
35
|
name: string;
|
|
36
|
+
shortName?: string;
|
|
26
37
|
sequence?: number;
|
|
27
38
|
isDefault?: boolean;
|
|
28
39
|
isActive?: boolean;
|
|
@@ -32,7 +43,7 @@ interface POI {
|
|
|
32
43
|
/** Unique identifier */
|
|
33
44
|
id: string | number;
|
|
34
45
|
/** POI type */
|
|
35
|
-
type: 'amenity' | 'destination';
|
|
46
|
+
type: 'amenity' | 'destination' | 'kiosk';
|
|
36
47
|
/** Display name */
|
|
37
48
|
name: string;
|
|
38
49
|
/** Geographic coordinates [longitude, latitude] */
|
|
@@ -45,6 +56,17 @@ interface POI {
|
|
|
45
56
|
floorId: string | number;
|
|
46
57
|
/** Category/type of amenity (e.g., 'restroom', 'elevator') */
|
|
47
58
|
amenityType?: string;
|
|
59
|
+
/** Locale-stable classification text (name + type + keywords, frozen at
|
|
60
|
+
* load before any locale patch can translate them). Copied from the
|
|
61
|
+
* source `Amenity.classifyText`; used by `amenityCategoryFor`/rank
|
|
62
|
+
* instead of the live, possibly-translated fields. See `Amenity.classifyText`. */
|
|
63
|
+
classifyText?: string;
|
|
64
|
+
/** Which amenity icon layer this POI renders in — `'connector'`,
|
|
65
|
+
* `'entrance'`, `'parking'`, `'bus'`, `'information'` or `'other'`.
|
|
66
|
+
* Derived from the amenity's connector flag, name, type and keywords;
|
|
67
|
+
* emitted as the `amenityCategory` feature property the theme filters on.
|
|
68
|
+
* Amenities only. See `amenityCategoryFor`. */
|
|
69
|
+
amenityCategory?: 'connector' | 'entrance' | 'parking' | 'bus' | 'information' | 'other';
|
|
48
70
|
/** Search keywords */
|
|
49
71
|
keywords?: string[];
|
|
50
72
|
/** Additional properties */
|
|
@@ -55,6 +77,15 @@ interface POI {
|
|
|
55
77
|
waypoint?: Waypoint;
|
|
56
78
|
/** Marks this POI as the "You are here" kiosk */
|
|
57
79
|
isYouAreHere?: boolean;
|
|
80
|
+
/** For kiosk POIs only: compass heading the device physically faces, in
|
|
81
|
+
* degrees clockwise from north. Sourced from JACS `Device.heading`. */
|
|
82
|
+
heading?: number | null;
|
|
83
|
+
/** Per-instance name for this specific placement of an amenity (e.g.
|
|
84
|
+
* "Parking Lot A" for a "Parking" amenity), resolved from the waypoint's
|
|
85
|
+
* `amenityAssociations`. Distinct from `name`, which is the amenity's
|
|
86
|
+
* type-level name shared by every instance — see `Amenity.name`. Amenities
|
|
87
|
+
* only, and only set when JACS actually returned an instance name. */
|
|
88
|
+
instanceName?: string;
|
|
58
89
|
}
|
|
59
90
|
/**
|
|
60
91
|
* Amenity - a specific type of POI (facilities, services)
|
|
@@ -64,14 +95,42 @@ interface Amenity {
|
|
|
64
95
|
id: string | number;
|
|
65
96
|
/** Display name */
|
|
66
97
|
name: string;
|
|
67
|
-
/** SVG icon
|
|
98
|
+
/** Inline SVG markup (legacy fullcall DTO). When absent, icon is resolved from `uris`. */
|
|
68
99
|
svg?: string;
|
|
100
|
+
/**
|
|
101
|
+
* Hosted icon descriptors from JACS (`uris[].locales[].uriPath`). Used by
|
|
102
|
+
* the SDK's runtime icon fetcher to register a map image at init time.
|
|
103
|
+
*/
|
|
104
|
+
uris?: Array<{
|
|
105
|
+
mimeType?: string;
|
|
106
|
+
resourceType?: string;
|
|
107
|
+
locales?: Array<{
|
|
108
|
+
locale?: string;
|
|
109
|
+
uriPath?: string;
|
|
110
|
+
}>;
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* Map-image id used as `icon-image` in the POI style layer. Decorated
|
|
114
|
+
* in-place by AmenityIcons.register once the SVG is fetched and registered.
|
|
115
|
+
*/
|
|
116
|
+
iconId?: string;
|
|
69
117
|
/** Search keywords */
|
|
70
118
|
keywords?: string[];
|
|
71
119
|
/** Associated waypoints */
|
|
72
120
|
waypoints?: Waypoint[];
|
|
73
121
|
/** Amenity type/category */
|
|
74
122
|
type?: string;
|
|
123
|
+
/**
|
|
124
|
+
* Locale-stable snapshot of `name + type + keywords`, captured once from
|
|
125
|
+
* the `/all` payload before `applyLocale` ever runs. `amenityCategoryFor`
|
|
126
|
+
* and the icon-rank heuristics in `poi.ts` match hard-coded English
|
|
127
|
+
* vocabulary against those fields; `name`/`keywords` are legitimately
|
|
128
|
+
* overwritten with translated text on a language switch
|
|
129
|
+
* (`LOCALIZED_FIELDS.amenity`), which reclassified every translated
|
|
130
|
+
* amenity to `'other'`/tier-3 and dropped its icon until the next zoom
|
|
131
|
+
* recomputed the rank gate. Classification reads this frozen copy instead.
|
|
132
|
+
*/
|
|
133
|
+
classifyText?: string;
|
|
75
134
|
/** Extended properties */
|
|
76
135
|
extensors?: Record<string, unknown>;
|
|
77
136
|
}
|
|
@@ -87,8 +146,34 @@ interface Destination {
|
|
|
87
146
|
waypoints?: Waypoint[];
|
|
88
147
|
/** Category/classification */
|
|
89
148
|
category?: string;
|
|
149
|
+
/** Room number — the CMS's "Room Number" field. A **real JACS column**, not
|
|
150
|
+
* one of the `mm_` stand-in extensors, so it arrives on the entity rather
|
|
151
|
+
* than in `extensors`. Surfaced as `getPOIDetails().roomNumber`. */
|
|
152
|
+
unitNumber?: string | null;
|
|
153
|
+
/** Search keywords */
|
|
154
|
+
keywords?: string[];
|
|
90
155
|
/** Additional properties */
|
|
91
156
|
properties?: Record<string, unknown>;
|
|
157
|
+
/** The CMS "Location Image" — a photo or logo for the popover, not a map
|
|
158
|
+
* marker. JACS `/all` fullcall serves this as
|
|
159
|
+
* `{ items: [{ resourceType, mimeType, path }] }`; the legacy/basic shape is
|
|
160
|
+
* `[{ locales: [{ uriPath }] }]`. `destinationImageSrc` handles both, and
|
|
161
|
+
* surfaces the result on `sdk.getPOIDetails(poi).imageUrl`. */
|
|
162
|
+
uris?: unknown;
|
|
163
|
+
/** Inline SVG for the uploaded image, embedded by JACS `/all`. Preferred over
|
|
164
|
+
* fetching `uris[].path` — no network round-trip. */
|
|
165
|
+
svg?: string;
|
|
166
|
+
/** Free-form CMS metadata. */
|
|
167
|
+
extensors?: Record<string, unknown>;
|
|
168
|
+
/** @deprecated Read by nothing in the SDK as of rc.34.
|
|
169
|
+
*
|
|
170
|
+
* JACS still serves the old map-manager "Label Display" mode
|
|
171
|
+
* (`0` Hidden · `1` Label · `2` Image), but the CMS removed the picker and
|
|
172
|
+
* pins every write to `1`. Rows authored before that keep a stale `0`/`2`
|
|
173
|
+
* the author can no longer change, so honouring the field would hide labels
|
|
174
|
+
* with no way to bring them back. Destinations always render dot + text
|
|
175
|
+
* label; the uploaded image goes to the popover instead. */
|
|
176
|
+
displayMode?: number;
|
|
92
177
|
}
|
|
93
178
|
/**
|
|
94
179
|
* Waypoint - a specific point location
|
|
@@ -102,6 +187,19 @@ interface Waypoint {
|
|
|
102
187
|
floorId?: string | number;
|
|
103
188
|
/** Whether this is the primary/default waypoint */
|
|
104
189
|
isPrimary?: boolean;
|
|
190
|
+
/** CMS zone (department) this waypoint belongs to, if any. The only backend
|
|
191
|
+
* link between a location and its department — read at runtime across the
|
|
192
|
+
* data provider (see the package CLAUDE.md zone landmine). */
|
|
193
|
+
zoneId?: string | number;
|
|
194
|
+
/** Per-instance names JACS attaches to this waypoint's amenities (e.g. a
|
|
195
|
+
* "Parking" amenity placed twice as "Parking Lot A" / "Parking Lot B").
|
|
196
|
+
* `id` matches an `Amenity.id`, not this waypoint — a waypoint can carry
|
|
197
|
+
* more than one amenity, so a consumer must find the entry for the
|
|
198
|
+
* specific amenity it's rendering rather than assuming index 0. */
|
|
199
|
+
amenityAssociations?: Array<{
|
|
200
|
+
id: string | number;
|
|
201
|
+
name: string;
|
|
202
|
+
}>;
|
|
105
203
|
}
|
|
106
204
|
/**
|
|
107
205
|
* Amenity enriched with the floor it belongs to
|
|
@@ -121,6 +219,23 @@ interface POISearchResult {
|
|
|
121
219
|
matchedFields: string[];
|
|
122
220
|
}
|
|
123
221
|
|
|
222
|
+
/**
|
|
223
|
+
* A Zone is a CMS-authored department category — a named, colored grouping
|
|
224
|
+
* (e.g. "Cardiology", "Surgical", "Admin"). Zones are venue-wide (not
|
|
225
|
+
* floor-scoped) in the JACS `/all` payload and carry no geometry of their own;
|
|
226
|
+
* their spatial footprint is derived from the waypoints that reference them
|
|
227
|
+
* (`waypoint.zoneId`), which lets the SDK color-code the room/unit polygons a
|
|
228
|
+
* zone covers. See `map/zones.ts` for the derivation.
|
|
229
|
+
*/
|
|
230
|
+
interface Zone {
|
|
231
|
+
id: number;
|
|
232
|
+
name: string;
|
|
233
|
+
/** Authored hex color (e.g. "#d32f2f"); may be empty when the author
|
|
234
|
+
* hasn't picked one — such zones are skipped for color-coding. */
|
|
235
|
+
color: string;
|
|
236
|
+
description?: string;
|
|
237
|
+
}
|
|
238
|
+
|
|
124
239
|
/** Captured camera state (center, zoom, pitch, bearing) */
|
|
125
240
|
type CameraState = {
|
|
126
241
|
center: [number, number];
|
|
@@ -140,25 +255,341 @@ type ViewOptions = {
|
|
|
140
255
|
/** SW/NE bounding box */
|
|
141
256
|
type Bounds = [[number, number], [number, number]];
|
|
142
257
|
|
|
258
|
+
/** How to center the map after computing a route */
|
|
259
|
+
type WayfindCenterMode = 'none' | 'destination' | 'route';
|
|
260
|
+
/**
|
|
261
|
+
* Per-call routing preferences forwarded to the wayfinding provider.
|
|
262
|
+
*
|
|
263
|
+
* - `avoidStairs` — hard-filter stairs from the graph. Accessibility
|
|
264
|
+
* preference otherwise comes from the CMS's per-path-type `weight`
|
|
265
|
+
* (elevators score low, stairs/escalators score high) — see
|
|
266
|
+
* `data/wayfinding/weights.ts` — not from a caller-supplied flag.
|
|
267
|
+
*
|
|
268
|
+
* The bundled stub provider doesn't compute routes; consumers wire a real
|
|
269
|
+
* provider (see `mm-web-sdk-example/lib/wayfinding`) that reads this
|
|
270
|
+
* flag when building edge weights.
|
|
271
|
+
*/
|
|
272
|
+
type RoutingOptions = {
|
|
273
|
+
avoidStairs?: boolean;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* A single point along the rendered route. `mapId` lets the renderer
|
|
278
|
+
* split the polyline by floor (so other-floor segments don't paint
|
|
279
|
+
* through the visible floor's geometry); `waypointId` and `pathTypeId`
|
|
280
|
+
* let downstream layers (text-directions, step UI) classify
|
|
281
|
+
* transitions — e.g. "Take the elevator" vs "Walk straight". The two
|
|
282
|
+
* endpoint points are the kiosk and destination anchors (`isEndpoint`
|
|
283
|
+
* marks them so they can render endpoint pins). All fields except
|
|
284
|
+
* `coordinates` are optional for backward-compat with the straight-line
|
|
285
|
+
* fallback.
|
|
286
|
+
*/
|
|
287
|
+
type RoutePoint = {
|
|
288
|
+
coordinates: [number, number];
|
|
289
|
+
/** Floor mapId this point sits on. Missing on the straight-line fallback. */
|
|
290
|
+
mapId?: number;
|
|
291
|
+
/** JACS waypoint id this point resolved to, if any. */
|
|
292
|
+
waypointId?: number;
|
|
293
|
+
/** Path type of the edge that LEADS INTO this point (i.e. the edge
|
|
294
|
+
* whose `toKey` is this node). Used to detect elevator / stair /
|
|
295
|
+
* escalator transitions. Missing on the start point and on the
|
|
296
|
+
* straight-line fallback. */
|
|
297
|
+
pathTypeId?: number;
|
|
298
|
+
/** True for the snapped kiosk + destination anchors. */
|
|
299
|
+
isEndpoint?: boolean;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
type WayfindStep = DepartStep | TurnStep | ContinueStep | TransitionStep$1 | ArriveStep;
|
|
303
|
+
type StepBase = {
|
|
304
|
+
/** Indices into the input `points` array that this step spans. */
|
|
305
|
+
pointRange: [startInclusive: number, endInclusive: number];
|
|
306
|
+
/** Floor this step is on. `null` for cross-floor transitions. */
|
|
307
|
+
floorId: number | null;
|
|
308
|
+
/** Localized instruction text. */
|
|
309
|
+
text: string;
|
|
310
|
+
/** Total walking distance within this step, in feet (rounded). */
|
|
311
|
+
distanceFeet?: number;
|
|
312
|
+
/** Landmark name used for anchoring, if any. */
|
|
313
|
+
landmark?: string;
|
|
314
|
+
};
|
|
315
|
+
type DepartStep = StepBase & {
|
|
316
|
+
type: 'depart';
|
|
317
|
+
/** Which way the traveller turns before walking off, relative to the
|
|
318
|
+
* way they're facing at the origin. Absent when `originFacingBearing`
|
|
319
|
+
* wasn't supplied (direction unknowable) or when the route heads
|
|
320
|
+
* straight ahead. Consumers use it to pick the step's icon. */
|
|
321
|
+
initialTurn?: 'left' | 'right' | 'around';
|
|
322
|
+
};
|
|
323
|
+
type TurnStep = StepBase & {
|
|
324
|
+
type: 'turn-left' | 'turn-right' | 'u-turn';
|
|
325
|
+
};
|
|
326
|
+
type ContinueStep = StepBase & {
|
|
327
|
+
type: 'continue';
|
|
328
|
+
};
|
|
329
|
+
type TransitionStep$1 = StepBase & {
|
|
330
|
+
type: 'transition';
|
|
331
|
+
transition: 'elevator' | 'stairs' | 'escalator';
|
|
332
|
+
fromFloorId: number | null;
|
|
333
|
+
toFloorId: number | null;
|
|
334
|
+
};
|
|
335
|
+
type ArriveStep = StepBase & {
|
|
336
|
+
type: 'arrive';
|
|
337
|
+
};
|
|
338
|
+
|
|
143
339
|
interface MapEvent {
|
|
144
340
|
floor?: Floor;
|
|
145
341
|
poi?: POI;
|
|
342
|
+
/** All POIs co-located at a clicked point (overlapping pins / a shared
|
|
343
|
+
* suite). Emitted by `poiSelected`; length ≥ 1, and `poi` mirrors
|
|
344
|
+
* `pois[0]` for single-POI consumers. */
|
|
345
|
+
pois?: POI[];
|
|
146
346
|
coordinates?: [number, number];
|
|
147
347
|
error?: any;
|
|
148
348
|
venue?: any;
|
|
149
349
|
camera?: CameraState;
|
|
350
|
+
/** Emitted by `themeChanged`; `'custom'` when consumer passed a raw style. */
|
|
351
|
+
theme?: 'default' | 'high-contrast' | 'custom';
|
|
352
|
+
/** Emitted by `localeChanged` after `setLocale()` finishes patching the
|
|
353
|
+
* venue model. BCP-47 code (`'en'`, `'es'`, `'es-MX'`). */
|
|
354
|
+
locale?: string;
|
|
355
|
+
/** Emitted by `routeReady` after a successful `navigateFromKioskToPOI`.
|
|
356
|
+
* Carries the raw geometry (`points`) and the human-readable
|
|
357
|
+
* step-by-step directions. Subscribe to drive a turn-by-turn UI. */
|
|
358
|
+
route?: {
|
|
359
|
+
points: RoutePoint[];
|
|
360
|
+
steps: WayfindStep[];
|
|
361
|
+
/** Floor mapId of each segment, in render order. `null` for
|
|
362
|
+
* segments emitted by the straight-line fallback. */
|
|
363
|
+
floorIds: Array<number | null>;
|
|
364
|
+
};
|
|
150
365
|
}
|
|
151
366
|
type EventCallback = (event: MapEvent) => void;
|
|
152
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Padding kept clear inside the viewport when the SDK frames bounds —
|
|
370
|
+
* the initial venue/floor fit and the wayfinding route fit. A plain
|
|
371
|
+
* number pads all sides equally; the object form lets a kiosk reserve
|
|
372
|
+
* space for fixed UI overlays (header, dock, side rails) so framed
|
|
373
|
+
* content is never hidden behind them.
|
|
374
|
+
*/
|
|
375
|
+
type BoundsPadding = number | {
|
|
376
|
+
top: number;
|
|
377
|
+
bottom: number;
|
|
378
|
+
left: number;
|
|
379
|
+
right: number;
|
|
380
|
+
};
|
|
381
|
+
/**
|
|
382
|
+
* Built-in theme names accepted by `MinuteMaps.setTheme()` and the
|
|
383
|
+
* `options.theme` init option. Pass a `StyleSpecification` directly for
|
|
384
|
+
* custom themes.
|
|
385
|
+
*/
|
|
386
|
+
type ThemeName = 'default' | 'high-contrast';
|
|
387
|
+
/**
|
|
388
|
+
* Style of the disc + ring rendered behind each amenity icon. The SDK
|
|
389
|
+
* composites this into the icon bitmap at registration time, so badge +
|
|
390
|
+
* icon participate in symbol collision as one unit.
|
|
391
|
+
*/
|
|
392
|
+
type AmenityBadgeStyle = {
|
|
393
|
+
/** Fill colour of the badge disc. Default `'#fdb81e'`. */
|
|
394
|
+
color?: string;
|
|
395
|
+
/** Stroke colour of the ring around the disc. Default `'#FFFFFF'`. */
|
|
396
|
+
ringColor?: string;
|
|
397
|
+
/** Ring width in logical px. Default `2`. */
|
|
398
|
+
ringWidth?: number;
|
|
399
|
+
/** Extra inset between the icon and the badge's inner edge, in logical px
|
|
400
|
+
* (applied on all sides). Default `0` — the icon fills ~85% of the disc
|
|
401
|
+
* as before. Raise this if an icon's own artwork reaches close to its
|
|
402
|
+
* viewBox edges and ends up touching the ring. */
|
|
403
|
+
padding?: number;
|
|
404
|
+
};
|
|
153
405
|
interface SDKOptions {
|
|
154
406
|
debug?: boolean;
|
|
155
|
-
|
|
407
|
+
/**
|
|
408
|
+
* `window` key the SDK instance is published under while `debug` is on, so
|
|
409
|
+
* a running kiosk can be driven from devtools (`mm.debug.hide('labels')`,
|
|
410
|
+
* `mm.getMap()`). Defaults to `'mm'`. Pass another name to avoid a clash,
|
|
411
|
+
* or `false` to publish nothing even in debug mode. Ignored entirely when
|
|
412
|
+
* `debug` is off.
|
|
413
|
+
*/
|
|
414
|
+
debugGlobal?: string | false;
|
|
415
|
+
/**
|
|
416
|
+
* Initial theme. `'default'` uses the bundled hybrid 3D theme.
|
|
417
|
+
* `'high-contrast'` uses the WCAG-AA tuned theme. Pass a
|
|
418
|
+
* `StyleSpecification` for fully custom styling.
|
|
419
|
+
*/
|
|
420
|
+
theme?: ThemeName | any;
|
|
421
|
+
/**
|
|
422
|
+
* When true, the SDK skips animations on imperative camera calls
|
|
423
|
+
* (wayfinding fits, `setView`, idle re-frames). Consumers should
|
|
424
|
+
* mirror their app's `prefers-reduced-motion` state into this.
|
|
425
|
+
*/
|
|
426
|
+
reducedMotion?: boolean;
|
|
427
|
+
/**
|
|
428
|
+
* Whether a freshly computed route auto-highlights its first step's
|
|
429
|
+
* segment (the brighter `route-line-active` overlay). Defaults to
|
|
430
|
+
* `true`, preserving the turn-by-turn segment highlight that
|
|
431
|
+
* `setActiveStep` drives.
|
|
432
|
+
*
|
|
433
|
+
* Set `false` for kiosk-style "show the whole route at once" UIs:
|
|
434
|
+
* the entire route line is shown without singling out one segment,
|
|
435
|
+
* which reads more clearly at a glance and avoids implying the visitor
|
|
436
|
+
* must step through the route. `setActiveStep` still works when called
|
|
437
|
+
* explicitly (e.g. a paginated QR-handoff / mobile flow) — this only
|
|
438
|
+
* controls the automatic highlight on `routeReady`.
|
|
439
|
+
*/
|
|
440
|
+
routeStepHighlight?: boolean;
|
|
441
|
+
/**
|
|
442
|
+
* Whether restroom pins that are too close together to draw individually
|
|
443
|
+
* collapse into a single Material `wc` badge. Defaults to `true`.
|
|
444
|
+
*
|
|
445
|
+
* Venues author men's and women's restrooms as two amenities a few metres
|
|
446
|
+
* apart. Both are wayfinding-critical (rank 0) and both draw on a layer that
|
|
447
|
+
* doesn't allow icon overlap, so without this MapLibre resolves the collision
|
|
448
|
+
* by hiding one of them outright until the camera is very close in. Grouping
|
|
449
|
+
* shows one badge in their place and hands back *all* the members when it's
|
|
450
|
+
* tapped, so the consumer can offer directions to each.
|
|
451
|
+
*
|
|
452
|
+
* Set `false` to opt out and get the raw collision behaviour back.
|
|
453
|
+
*/
|
|
454
|
+
groupRestroomIcons?: boolean;
|
|
156
455
|
initialFloor?: string | number;
|
|
157
456
|
enableInteractions?: boolean;
|
|
158
457
|
customSprite?: string;
|
|
159
458
|
minIndoorZoom?: number;
|
|
459
|
+
/**
|
|
460
|
+
* How far, in **metres** (default `0.3`, ≈1 ft), extruded interior polygons
|
|
461
|
+
* (restrooms, back-of-house, obstacles, connectors) are inset so their edges
|
|
462
|
+
* don't z-fight the neighbouring room block. A polygon too thin to survive
|
|
463
|
+
* the inset (it would keep under half its area, e.g. a ~0.3 m back-of-house
|
|
464
|
+
* perimeter band) is left at its original size rather than erased. Metres to match the theme's
|
|
465
|
+
* `fill-extrusion-height` values, which MapLibre defines in metres — unlike
|
|
466
|
+
* the distances shown to visitors, which are in feet. Was feet before this
|
|
467
|
+
* option changed units; a value of `1` is now ~3.3× thicker.
|
|
468
|
+
*/
|
|
160
469
|
wallThickness?: number;
|
|
161
|
-
|
|
470
|
+
/**
|
|
471
|
+
* How much costlier a candidate on a **different floor** from the kiosk is
|
|
472
|
+
* treated when choosing the closest instance of an amenity (default `1.5`).
|
|
473
|
+
* Used by `findClosestByWalkTime`, and so by `navigateFromKioskToClosestAmenity`
|
|
474
|
+
* and `highlightAmenity`.
|
|
475
|
+
*
|
|
476
|
+
* A multiplier on the trip's effort — walk time plus the elevator wait — not
|
|
477
|
+
* a distance: `1.5` means another floor's instance has to be a third cheaper
|
|
478
|
+
* to beat one on the kiosk's floor. It is a *preference* for not making the
|
|
479
|
+
* visitor change floors, layered on top of the physical cost of doing so
|
|
480
|
+
* (which is already in the effort). `1` turns it off and ranks by effort
|
|
481
|
+
* alone; a value below `1` is treated as `1`. It only affects which instance
|
|
482
|
+
* is chosen — never a route.
|
|
483
|
+
*
|
|
484
|
+
* For scale: at the Las Vegas VA the nearest other-floor Lactation Pod costs
|
|
485
|
+
* ~400 ft-equivalent against ~530 for the one on the kiosk's floor, so it
|
|
486
|
+
* takes a value above ~1.32 to keep the visitor on their floor.
|
|
487
|
+
*/
|
|
488
|
+
otherFloorPenalty?: number;
|
|
489
|
+
boundsPadding?: BoundsPadding;
|
|
490
|
+
/**
|
|
491
|
+
* Caps how far a visitor can pan/zoom out. By default the SDK derives this
|
|
492
|
+
* from the venue's own bounds, scaled to 3x its width/height around the
|
|
493
|
+
* same center — enough room to pan around the building without drifting
|
|
494
|
+
* into an empty, un-tiled region. The default box is never smaller than
|
|
495
|
+
* ~3.7 km a side, so a small venue isn't forced to zoom in tighter than a
|
|
496
|
+
* large one. Pass a number to use a different
|
|
497
|
+
* multiplier (e.g. `1.5` for a tighter leash), explicit `[[west, south],
|
|
498
|
+
* [east, north]]` bounds to override entirely, or `false` to disable
|
|
499
|
+
* max-bounds clamping.
|
|
500
|
+
*/
|
|
501
|
+
maxBounds?: Bounds | number | false;
|
|
502
|
+
/**
|
|
503
|
+
* Pitch (deg) for the opening view, applied centred on the kiosk ("You
|
|
504
|
+
* are here"). Omit for top-down.
|
|
505
|
+
*/
|
|
506
|
+
initialPitch?: number;
|
|
507
|
+
/**
|
|
508
|
+
* Zoom for the opening view, centred on the kiosk. Set this above the
|
|
509
|
+
* theme's unit-walls breakpoint (~16.5 in the bundled hybrid theme) so the
|
|
510
|
+
* kiosk opens on the building's interior floor plan (its contents) rather
|
|
511
|
+
* than a zoomed-out 3D massing outline. Omit to keep the post-floor-fit
|
|
512
|
+
* zoom.
|
|
513
|
+
*/
|
|
514
|
+
initialZoom?: number;
|
|
515
|
+
/**
|
|
516
|
+
* Clamp zoom-out relative to the opening view. When set, the map's
|
|
517
|
+
* `minZoom` becomes `(initialZoom − this)`, so visitors can nudge out by
|
|
518
|
+
* this many zoom levels but never pull back below the interior into the
|
|
519
|
+
* massing/region. `0` locks zoom-out exactly to the opening view.
|
|
520
|
+
*/
|
|
521
|
+
minZoomBelowInitialFit?: number;
|
|
522
|
+
/**
|
|
523
|
+
* Zoom-out floor expressed as ground scale: the most metres one CSS pixel
|
|
524
|
+
* may span. The SDK converts it to a `minZoom` at the venue's latitude, so
|
|
525
|
+
* every venue shows the same amount of surrounding map when fully zoomed
|
|
526
|
+
* out — a plain zoom number doesn't, because Web Mercator shows less ground
|
|
527
|
+
* per zoom level the further a venue is from the equator. Takes precedence
|
|
528
|
+
* over `minZoomBelowInitialFit`. For scale, `1.207` is zoom 15.8 at Orlando
|
|
529
|
+
* (about 2.3 km x 1.3 km on a 1920x1080 screen).
|
|
530
|
+
*/
|
|
531
|
+
minZoomMetersPerPixel?: number;
|
|
532
|
+
/**
|
|
533
|
+
* Override the colour the SDK recolours every amenity SVG to before it
|
|
534
|
+
* composites the badge. `none` / `transparent` fills are preserved so
|
|
535
|
+
* cut-outs stay. When the badge is enabled (the default), this defaults
|
|
536
|
+
* to navy (`#162e51`) so icons read on gold. Set explicitly for a
|
|
537
|
+
* different look, or set `amenityBadge: false` to disable recolouring
|
|
538
|
+
* altogether and keep the CMS-uploaded colours.
|
|
539
|
+
*/
|
|
540
|
+
amenityIconColor?: string;
|
|
541
|
+
/**
|
|
542
|
+
* Badge composited behind each amenity icon. Pass `false` to render the
|
|
543
|
+
* icon alone (no badge — useful for high-contrast or 2D themes where the
|
|
544
|
+
* gold disc would compete with the floor). Pass an object to tune the
|
|
545
|
+
* disc / ring style. Defaults to a VA-gold disc with a 2px white ring.
|
|
546
|
+
*
|
|
547
|
+
* The badge is baked into the icon bitmap (canvas composite) rather than
|
|
548
|
+
* drawn as a separate circle layer, so badge + icon participate in symbol
|
|
549
|
+
* collision together — overlapping amenities hide as one unit instead of
|
|
550
|
+
* the icon hiding while the disc stays painted.
|
|
551
|
+
*/
|
|
552
|
+
amenityBadge?: AmenityBadgeStyle | false;
|
|
553
|
+
/**
|
|
554
|
+
* Badge for vertical-circulation connectors (elevator / stairs / escalator).
|
|
555
|
+
* These render as their own visual class — a navy disc with a white glyph —
|
|
556
|
+
* so circulation reads distinct from the gold service amenities. Pass `false`
|
|
557
|
+
* to fall them back into the regular gold badge, or an object to tune the
|
|
558
|
+
* disc / ring. When `amenityBadge` is `false`, connectors are badge-less too
|
|
559
|
+
* unless this is set explicitly.
|
|
560
|
+
*/
|
|
561
|
+
connectorBadge?: AmenityBadgeStyle | false;
|
|
562
|
+
/**
|
|
563
|
+
* Badge for information-desk amenities (curated `information`/`info`
|
|
564
|
+
* icon). These render as their own visual class — a green disc with a
|
|
565
|
+
* white glyph by default — so information reads distinct from the gold
|
|
566
|
+
* service amenities. Pass `false` to fall them back into the regular gold
|
|
567
|
+
* badge, or an object to tune the disc / ring. When `amenityBadge` is
|
|
568
|
+
* `false`, information icons are badge-less too unless this is set
|
|
569
|
+
* explicitly.
|
|
570
|
+
*/
|
|
571
|
+
informationBadge?: AmenityBadgeStyle | false;
|
|
572
|
+
/**
|
|
573
|
+
* Glyph colour for information icons on their badge. Default white
|
|
574
|
+
* (matches `informationBadge`'s default green disc).
|
|
575
|
+
*/
|
|
576
|
+
informationIconColor?: string;
|
|
577
|
+
/**
|
|
578
|
+
* Badge for the end-of-route flag (`poi-route-end-flag`) shown in place of a
|
|
579
|
+
* destination's dot while a route to it is displayed. Has its own defaults
|
|
580
|
+
* (green disc, white ring) that don't follow `amenityBadge`. Pass an object
|
|
581
|
+
* to tune the disc / ring, or `false` for the bare flag glyph with no badge.
|
|
582
|
+
* When `amenityBadge` is `false`, the flag is badge-less too unless this is
|
|
583
|
+
* set explicitly. Under the high-contrast theme the disc colour is forced to
|
|
584
|
+
* the high-contrast amenity yellow, like the amenity and connector badges.
|
|
585
|
+
*/
|
|
586
|
+
routeEndBadge?: AmenityBadgeStyle | false;
|
|
587
|
+
/**
|
|
588
|
+
* Flag glyph colour on the end-of-route badge. Default white (contrasts the
|
|
589
|
+
* default green disc); doesn't follow `amenityIconColor`. Ignored under the
|
|
590
|
+
* high-contrast theme, which forces its own glyph colour.
|
|
591
|
+
*/
|
|
592
|
+
routeEndIconColor?: string;
|
|
162
593
|
styleMode?: 'venueStyleUrl' | 'sdkTemplate';
|
|
163
594
|
templateOverrideMode?: 'colorsOnly' | 'colorsAndConstants' | 'all';
|
|
164
595
|
}
|
|
@@ -190,6 +621,13 @@ type SDKConfig = {
|
|
|
190
621
|
venueId: number;
|
|
191
622
|
locale?: string;
|
|
192
623
|
auth?: JMapAuth;
|
|
624
|
+
/**
|
|
625
|
+
* Identifies which physical kiosk this instance is, so the SDK can pin
|
|
626
|
+
* the "You are here" marker. Matched against either the numeric device
|
|
627
|
+
* `id` or the device `uuid` from the venue's `devices`. The device's
|
|
628
|
+
* attached waypoint (`waypoint.deviceIds`) is the kiosk's location.
|
|
629
|
+
*/
|
|
630
|
+
deviceId?: string | number;
|
|
193
631
|
};
|
|
194
632
|
jacs: {
|
|
195
633
|
mode: 'proxy' | 'direct';
|
|
@@ -204,8 +642,229 @@ type SDKConfig = {
|
|
|
204
642
|
options?: SDKOptions;
|
|
205
643
|
};
|
|
206
644
|
|
|
207
|
-
|
|
208
|
-
type
|
|
645
|
+
type Visibility = 'visible' | 'none';
|
|
646
|
+
type DebugLayersDeps = {
|
|
647
|
+
getMap: () => Map | null;
|
|
648
|
+
/**
|
|
649
|
+
* Re-assert the visibility the SDK itself owns — floor layers, the POI
|
|
650
|
+
* category filter, the 2D/3D view mode. Called at the end of `reset()`,
|
|
651
|
+
* because restoring a captured `visibility` can't distinguish "the theme
|
|
652
|
+
* ships this visible" from "the SDK hid it because it belongs to another
|
|
653
|
+
* floor", and the naive restore would reveal every floor at once.
|
|
654
|
+
*/
|
|
655
|
+
reapplySdkState?: () => void;
|
|
656
|
+
log?: (...args: unknown[]) => void;
|
|
657
|
+
};
|
|
658
|
+
type LayerRow = {
|
|
659
|
+
id: string;
|
|
660
|
+
type: string;
|
|
661
|
+
visibility: Visibility;
|
|
662
|
+
/**
|
|
663
|
+
* What this layer's visibility was before we touched it — i.e. what the
|
|
664
|
+
* theme and the SDK want it to be. Equal to `visibility` for any layer we
|
|
665
|
+
* hold no override on. A UI wanting to show "is this group on" should judge
|
|
666
|
+
* only the layers whose `base` is `'visible'`: the rest (the 3D extrusions
|
|
667
|
+
* in flat mode, the 2D outlines outside it) aren't rendering by design, so
|
|
668
|
+
* counting them makes a healthy group look half-off.
|
|
669
|
+
*/
|
|
670
|
+
base: Visibility;
|
|
671
|
+
};
|
|
672
|
+
/**
|
|
673
|
+
* Console-facing layer inspector for demos, screenshots and debugging.
|
|
674
|
+
*
|
|
675
|
+
* Reachable as `sdk.debug`, and as `window.mm.debug` when
|
|
676
|
+
* `options.debug` is on (see `options.debugGlobal`).
|
|
677
|
+
*
|
|
678
|
+
* Two things make it more than a wrapper around `setLayoutProperty`:
|
|
679
|
+
*
|
|
680
|
+
* 1. **Selectors.** `hide('amenities')`, `hide('route-')`, `hide('/^poi-/')`
|
|
681
|
+
* and `hide('type:fill-extrusion')` all work, so you don't have to know
|
|
682
|
+
* the theme's layer ids to start poking at it.
|
|
683
|
+
* 2. **Overrides are sticky.** The SDK re-asserts layer visibility on floor
|
|
684
|
+
* changes, `setPOIFilter`, flat-mode toggles and theme swaps — all of which
|
|
685
|
+
* would otherwise wipe a manual toggle mid-demo. Every override is re-applied
|
|
686
|
+
* on `styledata`, so what you hid stays hidden until `reset()`.
|
|
687
|
+
*/
|
|
688
|
+
declare class DebugLayers {
|
|
689
|
+
private deps;
|
|
690
|
+
/** layer id → visibility this API is asserting. */
|
|
691
|
+
private overrides;
|
|
692
|
+
/** layer id → visibility before we first touched it. */
|
|
693
|
+
private originals;
|
|
694
|
+
/** `${layer}|${prop}` → paint value before we first touched it. */
|
|
695
|
+
private paintOriginals;
|
|
696
|
+
private sticky;
|
|
697
|
+
private attachedTo;
|
|
698
|
+
/** Re-entrancy guard: our own `setLayoutProperty` fires `styledata`. */
|
|
699
|
+
private applying;
|
|
700
|
+
private readonly onStyleData;
|
|
701
|
+
constructor(deps: DebugLayersDeps);
|
|
702
|
+
/** Begin re-asserting overrides across style/floor changes. */
|
|
703
|
+
attach(map: Map): void;
|
|
704
|
+
detach(): void;
|
|
705
|
+
/**
|
|
706
|
+
* Every layer in the active style, newest-on-top last, with its current
|
|
707
|
+
* visibility. Pass a selector to narrow it (same grammar as `hide`).
|
|
708
|
+
*/
|
|
709
|
+
list(selector?: string): LayerRow[];
|
|
710
|
+
/** Just the ids currently rendering — the quickest "what am I looking at". */
|
|
711
|
+
visible(): string[];
|
|
712
|
+
/** Group name → the ids it resolves to *in the active style*. */
|
|
713
|
+
groups(): Record<string, string[]>;
|
|
714
|
+
/** Hide every layer the selectors resolve to. Returns the ids affected. */
|
|
715
|
+
hide(...selectors: string[]): string[];
|
|
716
|
+
/**
|
|
717
|
+
* **Force** every resolved layer visible — including ones the SDK
|
|
718
|
+
* deliberately keeps off: the 3D extrusions while flat mode is on, the 2D
|
|
719
|
+
* outlines while it isn't, every floor but the active one. Forcing those
|
|
720
|
+
* renders two representations of the same geometry at once, or every floor
|
|
721
|
+
* stacked, and the map looks wrong.
|
|
722
|
+
*
|
|
723
|
+
* So this is *not* the way to undo a `hide()` — use `restore()`, which puts
|
|
724
|
+
* each layer back to what it was rather than blanket-revealing the group.
|
|
725
|
+
* Reach for `show()` only when you actually mean "reveal this regardless".
|
|
726
|
+
*/
|
|
727
|
+
show(...selectors: string[]): string[];
|
|
728
|
+
/**
|
|
729
|
+
* Undo our overrides on these layers: each goes back to the visibility it
|
|
730
|
+
* had before we first touched it, and control returns to the SDK (floor
|
|
731
|
+
* layers, POI filter, view mode re-assert afterwards).
|
|
732
|
+
*
|
|
733
|
+
* This is the correct inverse of `hide()`. A group is rarely uniformly
|
|
734
|
+
* visible to begin with — `rooms` hides either the 2D outline or the 3D
|
|
735
|
+
* extrusion depending on view mode, and every floor layer but the active
|
|
736
|
+
* one is off — so `hide()` then `show()` does not round-trip, but `hide()`
|
|
737
|
+
* then `restore()` does.
|
|
738
|
+
*
|
|
739
|
+
* Returns the ids that actually had an override; layers we never touched
|
|
740
|
+
* are left alone.
|
|
741
|
+
*/
|
|
742
|
+
restore(...selectors: string[]): string[];
|
|
743
|
+
/** Flip each resolved layer independently. Returns the ids affected. */
|
|
744
|
+
toggle(...selectors: string[]): string[];
|
|
745
|
+
/**
|
|
746
|
+
* Show only what the selectors resolve to — everything else is hidden
|
|
747
|
+
* (except `background`, which is just the canvas colour). The one-liner for
|
|
748
|
+
* "which layer is drawing that thing".
|
|
749
|
+
*
|
|
750
|
+
* The kept set is *restored*, not forced: anything we'd previously hidden
|
|
751
|
+
* comes back, but a layer the theme ships hidden stays hidden. Soloing must
|
|
752
|
+
* not invent geometry that never renders normally — see `show()`.
|
|
753
|
+
*/
|
|
754
|
+
solo(...selectors: string[]): string[];
|
|
755
|
+
/**
|
|
756
|
+
* Set a paint property — `paint('units-fill', 'fill-color', '#f00')`. The
|
|
757
|
+
* original value is captured so `reset()` puts it back. Not sticky: unlike
|
|
758
|
+
* visibility, a theme swap rebuilds paint from the new style and re-applying
|
|
759
|
+
* a captured expression across themes is exactly the bug `setTheme` had to
|
|
760
|
+
* fix. Re-run it after a swap if you need it back.
|
|
761
|
+
*/
|
|
762
|
+
paint(selector: string, prop: string, value: unknown): string[];
|
|
763
|
+
/**
|
|
764
|
+
* Drop every override and paint change, then let the SDK re-assert the
|
|
765
|
+
* visibility it owns (floor layers, POI filter, view mode). Back to the map
|
|
766
|
+
* you'd have had without touching anything.
|
|
767
|
+
*/
|
|
768
|
+
reset(): void;
|
|
769
|
+
/**
|
|
770
|
+
* Hand the layers we just released back to the SDK, then re-assert whatever
|
|
771
|
+
* overrides are still standing.
|
|
772
|
+
*
|
|
773
|
+
* The second half is not belt-and-braces. The SDK's re-assert passes are
|
|
774
|
+
* all-or-nothing: with no POI filter active, `applyPOIFilter` writes
|
|
775
|
+
* `visible` to *every* POI category layer, and the floor / view-mode passes
|
|
776
|
+
* sweep their whole layer sets the same way. So releasing one group would
|
|
777
|
+
* un-hide every other group you'd hidden — hide labels, destinations and
|
|
778
|
+
* amenities, re-check one, and all three came back.
|
|
779
|
+
*
|
|
780
|
+
* Doing it here rather than waiting for the `styledata` the SDK's writes
|
|
781
|
+
* trigger also keeps it synchronous: MapLibre fires that on a later frame,
|
|
782
|
+
* so a UI reading visibility straight after this call would otherwise see —
|
|
783
|
+
* and render — the clobbered state.
|
|
784
|
+
*/
|
|
785
|
+
private handBackToSdk;
|
|
786
|
+
/**
|
|
787
|
+
* The current override set as a plain object — save it, paste it back with
|
|
788
|
+
* `apply()` to reproduce the exact same framing for a screenshot later.
|
|
789
|
+
*/
|
|
790
|
+
snapshot(): Record<string, Visibility>;
|
|
791
|
+
/** Apply a `snapshot()` (or any layer-id → visibility map). */
|
|
792
|
+
apply(snapshot: Record<string, Visibility>): string[];
|
|
793
|
+
/**
|
|
794
|
+
* Stop (or resume) re-asserting overrides when the style changes. Turn it
|
|
795
|
+
* off if you want a floor switch or `setPOIFilter` to win over what you
|
|
796
|
+
* toggled by hand.
|
|
797
|
+
*/
|
|
798
|
+
setSticky(enabled: boolean): boolean;
|
|
799
|
+
/** Console cheat sheet. */
|
|
800
|
+
help(): Array<{
|
|
801
|
+
call: string;
|
|
802
|
+
does: string;
|
|
803
|
+
}>;
|
|
804
|
+
/**
|
|
805
|
+
* Resolve one selector to layer ids present in the active style, tried in
|
|
806
|
+
* order: group name → exact layer id → `type:<layer type>` → `/regex/` →
|
|
807
|
+
* case-insensitive substring on the id.
|
|
808
|
+
*/
|
|
809
|
+
resolve(selector: string): string[];
|
|
810
|
+
private resolveAll;
|
|
811
|
+
private set;
|
|
812
|
+
/**
|
|
813
|
+
* Put these layers back to their pre-override visibility and forget them.
|
|
814
|
+
* Callers decide whether to follow up with `reapplySdkState` — `restore()`
|
|
815
|
+
* and `reset()` do, `solo()` deliberately doesn't.
|
|
816
|
+
*/
|
|
817
|
+
private restoreVisibility;
|
|
818
|
+
/** Write one layer's visibility and remember it as an override. */
|
|
819
|
+
private write;
|
|
820
|
+
/**
|
|
821
|
+
* Re-assert overrides after something else rewrote layer visibility. Only
|
|
822
|
+
* writes when the current value actually differs, so this doesn't feed
|
|
823
|
+
* itself through the `styledata` it triggers.
|
|
824
|
+
*/
|
|
825
|
+
private reapply;
|
|
826
|
+
private styleLayers;
|
|
827
|
+
private visibilityOf;
|
|
828
|
+
/**
|
|
829
|
+
* MapLibre throws from these getters/setters while a style is mid-swap (the
|
|
830
|
+
* layer exists in `getStyle()` but its owner is being replaced). A debug tool
|
|
831
|
+
* that can break the map it's inspecting is worse than useless.
|
|
832
|
+
*/
|
|
833
|
+
private safe;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
type LayerAudit = {
|
|
837
|
+
/** `featureType` values the CMS permits for this customer. */
|
|
838
|
+
allowed: string[];
|
|
839
|
+
/** `featureType` values actually present in the loaded floors. */
|
|
840
|
+
inData: string[];
|
|
841
|
+
/** `featureType` values some theme layer filters on. */
|
|
842
|
+
inStyle: string[];
|
|
843
|
+
/**
|
|
844
|
+
* Drawn on a map but not in the customer's allowed list — a mis-authored
|
|
845
|
+
* or renamed layer. These render only by accident (if the theme happens to
|
|
846
|
+
* match the string) and are invisible otherwise.
|
|
847
|
+
*/
|
|
848
|
+
unauthorized: string[];
|
|
849
|
+
/**
|
|
850
|
+
* Present in the data and allowed, but no theme layer filters on it. The
|
|
851
|
+
* author drew something the map will never show.
|
|
852
|
+
*/
|
|
853
|
+
unstyled: string[];
|
|
854
|
+
/**
|
|
855
|
+
* The theme filters on it, but no loaded floor contains it. Dead weight in
|
|
856
|
+
* the style *for this venue* — another venue may well use it, so this is a
|
|
857
|
+
* prompt to check, not a delete list. Cross-reference `allowed`: a value
|
|
858
|
+
* that isn't in there either is dead everywhere for this customer.
|
|
859
|
+
*/
|
|
860
|
+
unused: string[];
|
|
861
|
+
/**
|
|
862
|
+
* Allowed by the CMS but no theme rule matches it, whether or not this
|
|
863
|
+
* venue happens to draw it. The SDK's total blind spot — an author can
|
|
864
|
+
* legitimately draw any of these and get nothing.
|
|
865
|
+
*/
|
|
866
|
+
allowedNotStyled: string[];
|
|
867
|
+
};
|
|
209
868
|
|
|
210
869
|
type LoggerFn = (...args: unknown[]) => void;
|
|
211
870
|
type AmenityManagerDeps = {
|
|
@@ -224,41 +883,524 @@ declare class AmenityManager {
|
|
|
224
883
|
private log;
|
|
225
884
|
private loadForFloor;
|
|
226
885
|
getAll(): AmenityWithFloor[];
|
|
886
|
+
/**
|
|
887
|
+
* Venue-wide amenities, one entry per id. `getAll()` returns each amenity
|
|
888
|
+
* once per floor it appears on (and so renders a multi-floor "Bathroom"
|
|
889
|
+
* three times in a venue list); this collapses those into a single record
|
|
890
|
+
* with all its waypoints merged across floors.
|
|
891
|
+
*
|
|
892
|
+
* Also folds any duplicate-id records from the provider into a single entry
|
|
893
|
+
* so React lists don't trip on dup keys. This used to say those duplicates
|
|
894
|
+
* were "a CMS data bug" — they weren't. JACS returns one record per amenity;
|
|
895
|
+
* the SDK's own floor index pushed it once per waypoint, so an amenity with
|
|
896
|
+
* four placements on a floor came back four times. Fixed at the source in
|
|
897
|
+
* `jacsDataProvider.floorIdsForWaypoints`; the fold stays as a cheap guard
|
|
898
|
+
* for a provider that genuinely repeats an id.
|
|
899
|
+
*
|
|
900
|
+
* Use this for venue-wide browse UIs; pair it with `findClosestWaypoint`
|
|
901
|
+
* (on the SDK) to resolve a tap to the nearest physical instance.
|
|
902
|
+
*/
|
|
903
|
+
getDistinct(): Amenity[];
|
|
227
904
|
getByFloorId(floorId: Floor['id']): AmenityWithFloor[];
|
|
228
905
|
getAllKiosks(): AmenityWithFloor[];
|
|
229
906
|
getKioskForFloor(floorId: Floor['id']): AmenityWithFloor | null;
|
|
230
907
|
logKioskForFloor(floorId: Floor['id']): void;
|
|
231
908
|
}
|
|
232
909
|
|
|
910
|
+
/**
|
|
911
|
+
* The `mm_`-prefixed extensor keys the CMS reserves for fields JACS has no
|
|
912
|
+
* column for yet.
|
|
913
|
+
*
|
|
914
|
+
* This file is the read half of a cross-repo table: the write half is
|
|
915
|
+
* `cms/packages/map-manager/app/utils/reserved-extensors.js`, which owns the key
|
|
916
|
+
* names, the value encodings, and the rule that a default value is deleted
|
|
917
|
+
* rather than stored. Change them together.
|
|
918
|
+
*
|
|
919
|
+
* The `mm_` prefix is load-bearing on both sides. Authors can type anything into
|
|
920
|
+
* the CMS's free-form Properties editor, so an unprefixed key like
|
|
921
|
+
* `point_of_interest` could collide with a hand-entered one; the prefix also
|
|
922
|
+
* makes "is this key reserved?" a prefix scan rather than a hand-maintained list
|
|
923
|
+
* once JACS grows real columns and these are migrated away.
|
|
924
|
+
*
|
|
925
|
+
* Two things that look like they belong here and don't:
|
|
926
|
+
*
|
|
927
|
+
* - **Room number.** The CMS labels it "Room Number", but it writes the real
|
|
928
|
+
* `Destination.unitNumber` field, not an extensor. `getPOIDetails().roomNumber`
|
|
929
|
+
* reads that field (see `sdk.ts`).
|
|
930
|
+
* - **`mm_point_of_interest`.** Registered by the CMS and parsed here for
|
|
931
|
+
* completeness, but deliberately not surfaced on `getPOIDetails` — nothing has
|
|
932
|
+
* pinned down whether an unflagged destination should drop out of search, out
|
|
933
|
+
* of the map, or neither, and guessing wrong is worse than omitting it.
|
|
934
|
+
*/
|
|
935
|
+
declare const RESERVED_EXTENSOR_PREFIX = "mm_";
|
|
936
|
+
/** Weekly opening hours. Value shape and parser live in `openHours.ts`. */
|
|
937
|
+
declare const OPEN_HOURS_EXTENSOR_KEY = "mm_open_hours";
|
|
938
|
+
/** Phone number, with the extension folded into the same value. */
|
|
939
|
+
declare const PHONE_EXTENSOR_KEY = "mm_phone";
|
|
940
|
+
/** Alt text for the destination's uploaded Location Image. */
|
|
941
|
+
declare const IMAGE_ALT_EXTENSOR_KEY = "mm_image_alt";
|
|
942
|
+
/** Author-set "this is a point of interest" flag. Parsed, not surfaced. */
|
|
943
|
+
declare const POINT_OF_INTEREST_EXTENSOR_KEY = "mm_point_of_interest";
|
|
944
|
+
/**
|
|
945
|
+
* Which building on the campus the destination stands in.
|
|
946
|
+
*
|
|
947
|
+
* The venue's own `Building` record can't answer this: the CMS binds a venue to
|
|
948
|
+
* a single building and hangs the floor list off it, so a campus with a dozen
|
|
949
|
+
* numbered buildings has one `Building` and no way to say which one a room is
|
|
950
|
+
* in. The author types it as free text, and that text is what a kiosk prints —
|
|
951
|
+
* it is not a reference to anything, so don't try to resolve it against
|
|
952
|
+
* `venue.buildings`.
|
|
953
|
+
*/
|
|
954
|
+
declare const BUILDING_NAME_EXTENSOR_KEY = "mm_building_name";
|
|
955
|
+
interface DestinationPhone {
|
|
956
|
+
/** Exactly as the author typed it — `"(408) 300-9294"`. Deliberately not
|
|
957
|
+
* normalised: doing that properly across international formats needs a real
|
|
958
|
+
* phone library, and the CMS makes the same choice on the way in. */
|
|
959
|
+
number: string;
|
|
960
|
+
/** Internal extension digits, when there is one. */
|
|
961
|
+
ext?: string;
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* `"(408) 300-9294;ext=123"` → `{ number, ext }`. `null` when there's no number
|
|
965
|
+
* — including for the empty string, which is how JACS spells a deleted key.
|
|
966
|
+
*/
|
|
967
|
+
declare function parsePhone(raw: unknown): DestinationPhone | null;
|
|
968
|
+
/** Alt text for the Location Image, or `undefined` when the author set none. */
|
|
969
|
+
declare function parseImageAlt(raw: unknown): string | undefined;
|
|
970
|
+
/** The building name, or `undefined` when the author set none. */
|
|
971
|
+
declare function parseBuildingName(raw: unknown): string | undefined;
|
|
972
|
+
/** The point-of-interest flag. Absence means false — only the checked state is
|
|
973
|
+
* ever stored. */
|
|
974
|
+
declare function parsePointOfInterest(raw: unknown): boolean;
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* Weekly opening hours for a destination.
|
|
978
|
+
*
|
|
979
|
+
* JACS has no hours column, so the CMS ships the whole week in a single
|
|
980
|
+
* destination extensor — key `mm_open_hours`, value a JSON string:
|
|
981
|
+
*
|
|
982
|
+
* {"v":1,"days":{"mon":[["09:00","17:30"]],"sat":[],"sun":[["00:00","24:00"]]}}
|
|
983
|
+
*
|
|
984
|
+
* - Times are 24-hour `"HH:MM"` and **venue-local**; no timezone is recorded.
|
|
985
|
+
* - Each day maps to an array of `[open, close]` ranges. The editor exposes one
|
|
986
|
+
* range per day today, but the array shape leaves room for split shifts (a
|
|
987
|
+
* cafeteria that closes over the afternoon) and extra ranges round-trip.
|
|
988
|
+
* - An empty array — or a missing day — means closed.
|
|
989
|
+
* - `[["00:00","24:00"]]` means open 24 hours.
|
|
990
|
+
*
|
|
991
|
+
* This module owns the read half: parse the value (`parseOpenHours`) and derive
|
|
992
|
+
* the live open/closed state from it (`getOpenStatus`). The write half lives in
|
|
993
|
+
* the CMS (`map-manager`'s `utils/open-hours.js`) — the two must agree on the
|
|
994
|
+
* wire shape above, so change them together. The key itself is registered in
|
|
995
|
+
* `reservedExtensors.ts` alongside the CMS's other stand-in fields, mirroring
|
|
996
|
+
* the same split on the authoring side.
|
|
997
|
+
*/
|
|
998
|
+
|
|
999
|
+
/** Wire-format version this module understands. */
|
|
1000
|
+
declare const OPEN_HOURS_VERSION = 1;
|
|
1001
|
+
type DayKey = 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun';
|
|
1002
|
+
/** Monday-first, matching the wire format's own day order. `getOpenStatus`
|
|
1003
|
+
* reports `OpenHoursTransition.day` as one of these. */
|
|
1004
|
+
declare const DAY_KEYS: readonly DayKey[];
|
|
1005
|
+
/** One open→close stretch within a single day. 24-hour `"HH:MM"`, venue-local.
|
|
1006
|
+
* `close` may be `"24:00"` (end of day); `open` never is. */
|
|
1007
|
+
interface HoursRange {
|
|
1008
|
+
open: string;
|
|
1009
|
+
close: string;
|
|
1010
|
+
}
|
|
1011
|
+
/** A parsed week. Every day is present; a closed day is an empty array. */
|
|
1012
|
+
interface OpenHours {
|
|
1013
|
+
days: Record<DayKey, HoursRange[]>;
|
|
1014
|
+
/** Open every minute of the week (a 24/7 emergency department, say). The
|
|
1015
|
+
* status has no `closesAt` in this case — there is nothing to count down to. */
|
|
1016
|
+
alwaysOpen: boolean;
|
|
1017
|
+
}
|
|
1018
|
+
/** A point in the week the status is counting toward. */
|
|
1019
|
+
interface OpenHoursTransition {
|
|
1020
|
+
/** Day the transition falls on, venue-local. */
|
|
1021
|
+
day: DayKey;
|
|
1022
|
+
/** 24-hour `"HH:MM"`, venue-local. Midnight is always `"00:00"` on the
|
|
1023
|
+
* following day — `"24:00"` never surfaces here. */
|
|
1024
|
+
time: string;
|
|
1025
|
+
/** Whole minutes from `now`. */
|
|
1026
|
+
minutesUntil: number;
|
|
1027
|
+
/** Calendar days ahead: `0` today, `1` tomorrow. */
|
|
1028
|
+
daysAhead: number;
|
|
1029
|
+
}
|
|
1030
|
+
interface OpenStatus {
|
|
1031
|
+
/** Open right now. */
|
|
1032
|
+
open: boolean;
|
|
1033
|
+
/** Open every minute of the week — no closing time to show. */
|
|
1034
|
+
alwaysOpen: boolean;
|
|
1035
|
+
/** Open, and closing within `soonMinutes`. */
|
|
1036
|
+
closingSoon: boolean;
|
|
1037
|
+
/** Closed, and opening within `soonMinutes`. */
|
|
1038
|
+
openingSoon: boolean;
|
|
1039
|
+
/** When open: the end of the current stretch. Absent when `alwaysOpen`. */
|
|
1040
|
+
closesAt?: OpenHoursTransition;
|
|
1041
|
+
/** When closed: the next time it opens. Absent when no day is open. */
|
|
1042
|
+
opensAt?: OpenHoursTransition;
|
|
1043
|
+
}
|
|
1044
|
+
interface OpenStatusOptions {
|
|
1045
|
+
/** How near a transition counts as "soon". Default `30`. */
|
|
1046
|
+
soonMinutes?: number;
|
|
1047
|
+
/** IANA zone the hours are expressed in (`'America/Denver'`). Omit — the
|
|
1048
|
+
* normal case for a kiosk standing in the venue — to read the host clock.
|
|
1049
|
+
* An unrecognised zone falls back to the host clock rather than throwing. */
|
|
1050
|
+
timeZone?: string;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Extensor value → a parsed week, or `null` when there is nothing usable.
|
|
1054
|
+
*
|
|
1055
|
+
* Accepts the stored JSON string or an already-parsed object. Anything
|
|
1056
|
+
* unreadable — absent key, malformed JSON, no open day — is `null` rather than
|
|
1057
|
+
* a throw, so a bad value costs the consumer an omitted section, not a crash.
|
|
1058
|
+
*
|
|
1059
|
+
* An unknown `v` is also `null`, deliberately. A future version could add
|
|
1060
|
+
* holiday overrides or a timezone; reading only the `days` it recognises would
|
|
1061
|
+
* let the SDK announce "Open" on a day the venue marked closed, and a confidently
|
|
1062
|
+
* wrong answer at a kiosk is worse than a missing one.
|
|
1063
|
+
*/
|
|
1064
|
+
declare function parseOpenHours(raw: unknown): OpenHours | null;
|
|
1065
|
+
/**
|
|
1066
|
+
* The live open/closed state for a parsed week.
|
|
1067
|
+
*
|
|
1068
|
+
* Pure and time-explicit: pass the `now` you want evaluated (default: the
|
|
1069
|
+
* moment of the call). Nothing is cached, so a consumer that wants a status
|
|
1070
|
+
* that stays true — a kiosk card sitting open — re-calls this on a tick rather
|
|
1071
|
+
* than holding the result.
|
|
1072
|
+
*
|
|
1073
|
+
* Hours carry no timezone of their own, so they are read against the host clock
|
|
1074
|
+
* unless `options.timeZone` names the venue's zone. The kiosk case needs no
|
|
1075
|
+
* option: the machine stands in the building it is describing.
|
|
1076
|
+
*/
|
|
1077
|
+
declare function getOpenStatus(hours: OpenHours, now?: Date, options?: OpenStatusOptions): OpenStatus;
|
|
1078
|
+
|
|
233
1079
|
declare class MinuteMaps {
|
|
234
1080
|
private config;
|
|
235
1081
|
private map;
|
|
1082
|
+
/**
|
|
1083
|
+
* Generation token for `init()`. Bumped by every `init()` and by `destroy()`,
|
|
1084
|
+
* so an init suspended on a network await can tell it has been superseded and
|
|
1085
|
+
* bail instead of creating a map nobody holds a reference to.
|
|
1086
|
+
*
|
|
1087
|
+
* This is what makes `destroy()` safe *during* init: `destroy()` only removes
|
|
1088
|
+
* `this.map`, which is still null until step 3, so a teardown that lands
|
|
1089
|
+
* mid-fetch has nothing to clean up — and without this token the init would
|
|
1090
|
+
* resume and build a fully live orphan (WebGL context, render loop, map-level
|
|
1091
|
+
* listeners, YAH pulse) that can never be reached to shut down. React
|
|
1092
|
+
* StrictMode's mount → unmount → mount does exactly this on every dev load.
|
|
1093
|
+
*
|
|
1094
|
+
* A counter, not a sticky `destroyed` flag: `destroy()` → `init()` re-init is
|
|
1095
|
+
* a supported lifecycle (see the manager-class note in CLAUDE.md), so the
|
|
1096
|
+
* instance has to stay usable after teardown.
|
|
1097
|
+
*/
|
|
1098
|
+
private initGeneration;
|
|
236
1099
|
private data;
|
|
237
1100
|
private wayfindingProvider;
|
|
238
1101
|
private events;
|
|
239
1102
|
private floorsApi;
|
|
240
1103
|
private venue;
|
|
241
1104
|
private spriteKeys;
|
|
242
|
-
private readonly
|
|
1105
|
+
private readonly debugEnabled;
|
|
243
1106
|
private readonly logger;
|
|
1107
|
+
/**
|
|
1108
|
+
* Console-facing layer inspector — `sdk.debug.hide('labels')`,
|
|
1109
|
+
* `sdk.debug.solo('route')`, `sdk.debug.reset()`. Always present so demo
|
|
1110
|
+
* code can drive it; additionally reachable as `window.mm.debug` when
|
|
1111
|
+
* `options.debug` is on. See `DebugLayers`.
|
|
1112
|
+
*/
|
|
1113
|
+
readonly debug: DebugLayers;
|
|
1114
|
+
/** The `window` key `debug` mode installed this instance under, if any. */
|
|
1115
|
+
private debugGlobalKey;
|
|
244
1116
|
private defaultCamera;
|
|
1117
|
+
/** This venue's opening zoom, captured once after the initial fit resolves.
|
|
1118
|
+
* Anchors the pinned zoom profile's icon/label reveal thresholds (idle
|
|
1119
|
+
* spotlight, active route) to this venue's own opening view rather than an
|
|
1120
|
+
* absolute MapLibre zoom, and the roof fill's fade-in. See `zoomViews`. */
|
|
1121
|
+
private iconZoomBase;
|
|
1122
|
+
/** Which layers show at which zoom: the Campus / Building / Kiosk views, or
|
|
1123
|
+
* the pinned profile while the idle spotlight or a route is up. Re-applied
|
|
1124
|
+
* after every `setTheme()` swap, since `setStyle({ diff: false })`
|
|
1125
|
+
* recreates layers from the pristine theme. */
|
|
1126
|
+
private zoomViews;
|
|
245
1127
|
private viewModes;
|
|
1128
|
+
/** Mirror of `config.options.reducedMotion`, mutable via `setReducedMotion`. */
|
|
1129
|
+
private reducedMotion;
|
|
1130
|
+
/** Whether map labels render in OpenDyslexic, mutable via `setDyslexicFont`.
|
|
1131
|
+
* Re-applied after every `setTheme()` swap, same reason as `iconZoomBase`. */
|
|
1132
|
+
private dyslexicFont;
|
|
1133
|
+
/** Multiplier applied to every theme label's `text-size`, mutable via
|
|
1134
|
+
* `setTextScale`. Re-applied after every `setTheme()` swap. */
|
|
1135
|
+
private textScale;
|
|
1136
|
+
/** Each label layer's unscaled `text-size`, captured by `setTextScale`.
|
|
1137
|
+
* Cleared on `setTheme()` — the recreated layers carry new base values. */
|
|
1138
|
+
private baseTextSizes;
|
|
1139
|
+
/** Tracks the active theme name so `setTheme()` is a no-op when re-requested. */
|
|
1140
|
+
private activeThemeName;
|
|
1141
|
+
/** Tracks the active locale so `setLocale()` is a no-op when re-requested.
|
|
1142
|
+
* Seeded from `config.jmap.locale` at construction; `undefined` means
|
|
1143
|
+
* whatever the customer's default locale is on the JACS side. */
|
|
1144
|
+
private currentLocale;
|
|
1145
|
+
/** The destination of the last successfully computed route — kept so
|
|
1146
|
+
* `setLocale()` can rebuild + re-emit `routeReady` with retranslated
|
|
1147
|
+
* step text without recomputing the path. Cleared on route failure. */
|
|
1148
|
+
private lastRouteDestinationPoi;
|
|
246
1149
|
private amenityManager;
|
|
247
1150
|
private wayfinding;
|
|
1151
|
+
private highlightManager;
|
|
1152
|
+
private selectionManager;
|
|
1153
|
+
private restroomGroups;
|
|
1154
|
+
/** Captured unit paint values to restore when the selection clears, or
|
|
1155
|
+
* `null` when no room is currently focused. See `UNIT_DIM_TARGETS`. */
|
|
1156
|
+
private unitDimRestore;
|
|
1157
|
+
/** Captured destination-layer filters to restore when the selection clears,
|
|
1158
|
+
* or `null` when no destination focus is active. See `setDestinationFocus`. */
|
|
1159
|
+
private destFocusRestore;
|
|
1160
|
+
/** The allow-list currently narrowing the destination layers, or `null` when
|
|
1161
|
+
* no focus is active. Kept alongside `destFocusRestore` (which holds the
|
|
1162
|
+
* *previous* filters, not the ids) so `setTheme` can re-apply the same focus
|
|
1163
|
+
* to the new style's layers after a swap discards the old ones. */
|
|
1164
|
+
private destFocusIds;
|
|
1165
|
+
/** The predicate currently narrowing the POI marker layers to a curated set,
|
|
1166
|
+
* or `null` when no spotlight is active. See `setPOISpotlight`. Held as the
|
|
1167
|
+
* predicate, not a resolved id list, so a floor change or locale redraw can
|
|
1168
|
+
* re-resolve it against the POIs now on screen. */
|
|
1169
|
+
private poiSpotlight;
|
|
1170
|
+
/** Layer filters as they were before the spotlight narrowed them, put back
|
|
1171
|
+
* verbatim when it releases. `null` when no spotlight filter is applied. */
|
|
1172
|
+
private poiSpotlightRestore;
|
|
1173
|
+
/** Coordinates of the current tap/Info selection, or `null` when nothing is
|
|
1174
|
+
* selected. `easeToSelection` reads `getBoundsPadding()` at the moment a
|
|
1175
|
+
* selection is made, which is often *before* the consumer's info card has
|
|
1176
|
+
* mounted and been measured into that padding — so the first pan can land
|
|
1177
|
+
* the POI behind the card. Kept here so `setBoundsPadding` can re-nudge the
|
|
1178
|
+
* camera once the consumer measures its now-mounted chrome and reports
|
|
1179
|
+
* fresh insets, the same way it already re-fits an active route. Cleared by
|
|
1180
|
+
* `clearHighlight`. */
|
|
1181
|
+
private selectedCoordinates;
|
|
1182
|
+
private youAreHerePulse;
|
|
1183
|
+
/** Theme layers whose features represent a tappable destination/amenity.
|
|
1184
|
+
* A click hit-tests these (see `SelectionManager`) → `poiSelected`. The
|
|
1185
|
+
* "You are here" anchor and the transient highlight layers are excluded;
|
|
1186
|
+
* a POI drawn across the circle/label/icon layers is de-duped downstream
|
|
1187
|
+
* in `resolveClickedPOIs`. */
|
|
1188
|
+
private static readonly CLICKABLE_POI_LAYERS;
|
|
1189
|
+
/** Room-body layers that are also tappable: a click on a `Units` polygon
|
|
1190
|
+
* resolves to the destination pin inside it (see `resolvePOIsFromFeatures`).
|
|
1191
|
+
* Queried alongside the pin layers so tapping the room or its dot behaves
|
|
1192
|
+
* identically. Kept separate from the pin list because these carry no POI
|
|
1193
|
+
* identity of their own — they resolve spatially. */
|
|
1194
|
+
private static readonly CLICKABLE_UNIT_LAYERS;
|
|
1195
|
+
/** The kiosk's own "you are here" pin — icon, directional heading wedge,
|
|
1196
|
+
* and the ring drawn behind them. Deliberately **not** part of
|
|
1197
|
+
* `CLICKABLE_POI_LAYERS`: that list feeds `emitPoiSelected`, which opens
|
|
1198
|
+
* a selection card for a destination/amenity, and this pin isn't one —
|
|
1199
|
+
* it's where the kiosk itself is standing. A tap here means "where am
|
|
1200
|
+
* I", so it gets its own delegated listener emitting `youAreHereClicked`
|
|
1201
|
+
* instead, for a consumer to wire to the same recenter action as its
|
|
1202
|
+
* own recenter control. */
|
|
1203
|
+
private static readonly YOU_ARE_HERE_LAYERS;
|
|
1204
|
+
/** While a room is selected, the other units are recolored to a flat, muted
|
|
1205
|
+
* gray so the raised, full-strength highlight block reads as the focus. We
|
|
1206
|
+
* recolor rather than lower opacity on purpose: translucent extrusions blend
|
|
1207
|
+
* through their neighbours and look murky. The blocks stay fully opaque (and
|
|
1208
|
+
* keep their vertical-gradient shading, so they still read as rooms) — just
|
|
1209
|
+
* colorless. The selected room's own base grays too, but its highlight cap
|
|
1210
|
+
* sits opaque and taller on top, so it still stands out. The original paint
|
|
1211
|
+
* value (the data-driven color expression) is captured at mute time and put
|
|
1212
|
+
* back on `clearHighlight`, so department tinting returns intact. */
|
|
1213
|
+
private static readonly UNIT_DIM_TARGETS;
|
|
1214
|
+
/** Destination marker layers hidden down to just the selected destination
|
|
1215
|
+
* while a room is focused, so the map declutters to the one place in view.
|
|
1216
|
+
* Amenity/connector layers (`poiType: 'amenity'`), the "you are here" anchor,
|
|
1217
|
+
* and room name labels (`map-labels`) are deliberately absent — they stay
|
|
1218
|
+
* visible as wayfinding reference. Restored on `clearHighlight`. */
|
|
1219
|
+
private static readonly DESTINATION_FOCUS_LAYERS;
|
|
1220
|
+
/** Categorical POI layers in the bundled theme that `setPOIFilter`
|
|
1221
|
+
* toggles. Layers absent from this list — `poi-you-are-here-*` and the
|
|
1222
|
+
* `poi-highlight-*` layers — are intentionally unaffected; they're
|
|
1223
|
+
* anchors or transient focus state, not categorical content. */
|
|
1224
|
+
private static readonly POI_CATEGORY_LAYERS;
|
|
1225
|
+
/** Marker layers `setPOISpotlight` narrows. Every categorical POI layer
|
|
1226
|
+
* except `poi-other-amenity-icons`, whose filter the restroom-group manager
|
|
1227
|
+
* and the zoom-range pass rewrite as the camera moves — a spotlight
|
|
1228
|
+
* narrowing would be silently overwritten mid-orbit — plus the per-instance
|
|
1229
|
+
* amenity label, which is a marker in every sense but isn't a category. */
|
|
1230
|
+
private static readonly POI_SPOTLIGHT_LAYERS;
|
|
1231
|
+
private poiVisibleTypes;
|
|
1232
|
+
/** Independent show/hide state for destination dots and name labels, set
|
|
1233
|
+
* by `setDestinationCirclesVisible`/`setDestinationLabelsVisible`. Kept
|
|
1234
|
+
* separate from `poiVisibleTypes` so the two controls compose instead of
|
|
1235
|
+
* clobbering each other — `applyPOIFilter` and `applyDestinationDisplay`
|
|
1236
|
+
* each own one paint/layout axis of the same layers. */
|
|
1237
|
+
private destinationCirclesVisible;
|
|
1238
|
+
private destinationLabelsVisible;
|
|
248
1239
|
constructor(config: SDKConfig);
|
|
249
1240
|
init(): Promise<void>;
|
|
1241
|
+
/**
|
|
1242
|
+
* Supply an image the current style has requested but doesn't have.
|
|
1243
|
+
*
|
|
1244
|
+
* Every imperatively-registered image is served from here, because a theme
|
|
1245
|
+
* swap (`setStyle` with `diff: false`) drops all of them and the code that
|
|
1246
|
+
* registered them doesn't run again:
|
|
1247
|
+
*
|
|
1248
|
+
* - the **route arrow** isn't in the sprite sheet at all — it's canvas-drawn
|
|
1249
|
+
* on demand (this was already the case);
|
|
1250
|
+
* - **amenity / destination icons** come back from `iconCache`. Their
|
|
1251
|
+
* registrars run only during `init`, so before this they went missing on
|
|
1252
|
+
* the first theme swap and never returned — the map kept the dots and
|
|
1253
|
+
* labels but lost every badge and logo.
|
|
1254
|
+
*
|
|
1255
|
+
* Serving lazily (rather than re-registering everything on `style.load`)
|
|
1256
|
+
* means only the ids the new style actually asks for are restored, with no
|
|
1257
|
+
* refetch and no re-compositing.
|
|
1258
|
+
*/
|
|
1259
|
+
private serveMissingStyleImage;
|
|
250
1260
|
on(event: string, cb: (e: MapEvent) => void): void;
|
|
251
1261
|
off(event: string, cb?: (e: MapEvent) => void): void;
|
|
252
1262
|
addControl(control: IControl, position?: ControlPosition): void;
|
|
253
1263
|
setView(options: ViewOptions): void;
|
|
1264
|
+
/**
|
|
1265
|
+
* Force a camera move to land instantly while reduced motion is on.
|
|
1266
|
+
*
|
|
1267
|
+
* The single gate every consumer-driven move goes through — zoom
|
|
1268
|
+
* buttons, compass reset, go-home, recenter, route fits — so the one
|
|
1269
|
+
* toggle covers them all instead of each call site remembering to ask.
|
|
1270
|
+
* Returns `opts` untouched when the preference is off, so a caller that
|
|
1271
|
+
* asked for `animate: false` keeps it either way.
|
|
1272
|
+
*/
|
|
1273
|
+
private motionGated;
|
|
254
1274
|
get amenities(): AmenityManager;
|
|
255
1275
|
resetView(opts?: {
|
|
256
1276
|
animate?: boolean;
|
|
257
1277
|
duration?: number;
|
|
258
1278
|
}): void;
|
|
1279
|
+
/**
|
|
1280
|
+
* Content-aware re-frame: fit an active route if one is displayed,
|
|
1281
|
+
* otherwise fit the current floor's geojson. Unlike `resetView()`, this
|
|
1282
|
+
* preserves the user's pitch and bearing — it only adjusts center/zoom
|
|
1283
|
+
* to bring the scene back into view. Intended use is after the map
|
|
1284
|
+
* container resizes (e.g. a side drawer opens beside the map and the
|
|
1285
|
+
* pane gets narrower), so what matters stays framed without a yank.
|
|
1286
|
+
*/
|
|
1287
|
+
refit(opts?: {
|
|
1288
|
+
animate?: boolean;
|
|
1289
|
+
duration?: number;
|
|
1290
|
+
}): void;
|
|
1291
|
+
/**
|
|
1292
|
+
* Update the bounds padding (camera insets) used by route + floor fits.
|
|
1293
|
+
* Consumers measure their on-screen chrome (panels, rails) and pass the
|
|
1294
|
+
* insets so a fit frames the path in the visible, unobscured area rather
|
|
1295
|
+
* than under the UI. Stored; the next fit / `refit()` uses it (clamped so
|
|
1296
|
+
* a fit always lands — see `clampPadding`).
|
|
1297
|
+
*/
|
|
1298
|
+
setBoundsPadding(p: BoundsPadding): void;
|
|
1299
|
+
private fitToBounds;
|
|
259
1300
|
set3dEnabled(enabled: boolean): void;
|
|
260
1301
|
toggle3d(): void;
|
|
261
1302
|
getIs3dEnabled(): boolean;
|
|
1303
|
+
/**
|
|
1304
|
+
* (Re-)register per-amenity icons, per-destination logos, and the kiosk
|
|
1305
|
+
* "you are here" icon. Needed both at init and after every `setTheme()`
|
|
1306
|
+
* call — `map.setStyle(..., { diff: false })` tears down and recreates
|
|
1307
|
+
* the whole `Style` object, which drops every image added at runtime via
|
|
1308
|
+
* `map.addImage`. Only `route-arrow` re-registers itself lazily via
|
|
1309
|
+
* `styleimagemissing`; everything else must be re-run explicitly here or
|
|
1310
|
+
* it silently goes blank after a theme swap.
|
|
1311
|
+
*
|
|
1312
|
+
* `themeName === 'high-contrast'` recolors both the amenity badge disc
|
|
1313
|
+
* and the connector (elevator/stairs/escalator) badge disc to
|
|
1314
|
+
* `HIGH_CONTRAST_AMENITY_BADGE_COLOR`, and forces both the regular and
|
|
1315
|
+
* connector glyph to `HIGH_CONTRAST_AMENITY_ICON_COLOR` so neither
|
|
1316
|
+
* washes out against its now-yellow disc and the two match each other.
|
|
1317
|
+
* Ring colors are left untouched. Failures per icon set are logged and
|
|
1318
|
+
* skipped so one bad icon doesn't block the rest.
|
|
1319
|
+
*/
|
|
1320
|
+
private registerRuntimeIcons;
|
|
1321
|
+
/**
|
|
1322
|
+
* Set the active map theme by name (`'default'` / `'high-contrast'`) or by
|
|
1323
|
+
* passing a custom `StyleSpecification`. The current floor, route, POIs
|
|
1324
|
+
* and camera are preserved across the swap — only layer styling changes.
|
|
1325
|
+
*
|
|
1326
|
+
* Used by kiosk consumers to wire the OS-level / popover high-contrast
|
|
1327
|
+
* toggle to the map. See `KIOSK.md` for the pattern.
|
|
1328
|
+
*/
|
|
1329
|
+
setTheme(theme: ThemeName | StyleSpecification): Promise<void>;
|
|
1330
|
+
/** Currently active theme name (or `'custom'` when a raw style was passed). */
|
|
1331
|
+
getActiveTheme(): 'default' | 'high-contrast' | 'custom';
|
|
1332
|
+
/**
|
|
1333
|
+
* Update the SDK's reduced-motion preference at runtime. When true, every
|
|
1334
|
+
* imperative camera move (`setView`, `resetView`, `refit`, recenter, route
|
|
1335
|
+
* fits, selection pans, floor fits) runs with `duration: 0`, and every
|
|
1336
|
+
* looping animation the SDK drives — the route line-draw, the flowing route
|
|
1337
|
+
* arrows, the "You are here" pulse, the selection ring's ping — is stopped.
|
|
1338
|
+
*
|
|
1339
|
+
* Anything already on screen is switched over here, not just on the next
|
|
1340
|
+
* action: a visitor who flips the toggle mid-route is doing it *because* of
|
|
1341
|
+
* the motion they can see, so waiting for the next event would be the one
|
|
1342
|
+
* moment the preference doesn't work.
|
|
1343
|
+
*/
|
|
1344
|
+
setReducedMotion(enabled: boolean): void;
|
|
1345
|
+
getReducedMotion(): boolean;
|
|
1346
|
+
/**
|
|
1347
|
+
* Swap every map label (street names, POI names, building labels, "You are
|
|
1348
|
+
* here") between the bundled themes' default font and OpenDyslexic. Mirrors
|
|
1349
|
+
* the DOM-side dyslexic-font accommodation onto the canvas, which CSS can't
|
|
1350
|
+
* reach.
|
|
1351
|
+
*
|
|
1352
|
+
* Goes through the same local-glyph fallback the themes already use for
|
|
1353
|
+
* `Material Icons` (`LOCAL_GLYPH_FONTS`): `text-font` values are swapped to
|
|
1354
|
+
* fontstack names intentionally absent from the glyph server
|
|
1355
|
+
* (`OpenDyslexic Bold` etc.), so MapLibre rasterizes them from a
|
|
1356
|
+
* page-loaded `@font-face` instead. The consuming app owns loading that
|
|
1357
|
+
* font — the SDK ships no assets — so this is a no-op glyph-wise (labels
|
|
1358
|
+
* fall back to tofu-free default rendering) if the app never declared it.
|
|
1359
|
+
*
|
|
1360
|
+
* Re-applied after `setTheme()`, since `setStyle({ diff: false })`
|
|
1361
|
+
* recreates every layer from the pristine (non-dyslexic) theme JSON.
|
|
1362
|
+
*/
|
|
1363
|
+
setDyslexicFont(enabled: boolean): Promise<void>;
|
|
1364
|
+
getDyslexicFont(): boolean;
|
|
1365
|
+
private applyDyslexicFontToLabels;
|
|
1366
|
+
/**
|
|
1367
|
+
* Scale every map label's `text-size` by `scale` (1 = theme default; the
|
|
1368
|
+
* example kiosk passes 1.15 for its "large text" setting). Mirrors the
|
|
1369
|
+
* DOM-side text-size accommodation onto the canvas, which CSS can't reach.
|
|
1370
|
+
*
|
|
1371
|
+
* Multiplies the output values inside each layer's `text-size` (MapLibre
|
|
1372
|
+
* requires `zoom` to stay at the top of the expression, so it can't be
|
|
1373
|
+
* wrapped), so zoom interpolation and data-driven sizing keep working. Re-applied after
|
|
1374
|
+
* `setTheme()`, since `setStyle({ diff: false })` recreates every layer from
|
|
1375
|
+
* the pristine theme JSON. Non-finite or non-positive values reset to 1.
|
|
1376
|
+
*/
|
|
1377
|
+
setTextScale(scale: number): void;
|
|
1378
|
+
getTextScale(): number;
|
|
1379
|
+
private applyTextScaleToLabels;
|
|
1380
|
+
/**
|
|
1381
|
+
* Switch the locale used for POI / amenity / destination / floor names at
|
|
1382
|
+
* runtime. The SDK refetches localized names from JACS (per-entity
|
|
1383
|
+
* endpoints — `/all` does not honor `?locale=`), patches the in-memory
|
|
1384
|
+
* venue model in place, then redraws the POI source for the current floor
|
|
1385
|
+
* so map labels render in the new language.
|
|
1386
|
+
*
|
|
1387
|
+
* Structural data (waypoints, coordinates, iconIds) is preserved — only
|
|
1388
|
+
* the localized fields (`name`, `description`) move.
|
|
1389
|
+
*
|
|
1390
|
+
* Emits a `localeChanged` event with the new code after the patch
|
|
1391
|
+
* completes. No-op when the code matches the current locale.
|
|
1392
|
+
*
|
|
1393
|
+
* Throws only on programmer error — a provider without locale support
|
|
1394
|
+
* (only `JacsDataProvider` has it today) or an empty code. A **failed
|
|
1395
|
+
* JACS fetch is not fatal**: the locale still switches and is reported,
|
|
1396
|
+
* it's only the localized names that stay on the prior language. That
|
|
1397
|
+
* split is deliberate — generated directions text ships inside the SDK
|
|
1398
|
+
* and must not be held hostage to a network round-trip.
|
|
1399
|
+
*/
|
|
1400
|
+
setLocale(locale: string): Promise<void>;
|
|
1401
|
+
/** Currently active locale (BCP-47), or `undefined` when no locale has
|
|
1402
|
+
* been set — meaning JACS resolves names to the customer's default. */
|
|
1403
|
+
getActiveLocale(): string | undefined;
|
|
262
1404
|
setUnits2dEnabled(enabled: boolean): void;
|
|
263
1405
|
toggleUnits2d(): void;
|
|
264
1406
|
getIsUnits2dEnabled(): boolean;
|
|
@@ -269,26 +1411,639 @@ declare class MinuteMaps {
|
|
|
269
1411
|
getCurrentFloor(): Floor | null;
|
|
270
1412
|
getDefaultFloor(): Floor | null;
|
|
271
1413
|
getDestinations(floor?: Floor): Destination[];
|
|
1414
|
+
getZones(): Zone[];
|
|
1415
|
+
/** The department (zone) a POI belongs to, resolved via its waypoint's
|
|
1416
|
+
* `zoneId` — the only backend link between a location and a department
|
|
1417
|
+
* (see the zone landmine in the package CLAUDE.md). `null` when the POI
|
|
1418
|
+
* has no waypoint, no zone, or the zone id is unknown. */
|
|
1419
|
+
getZoneForPOI(poi: POI): Zone | null;
|
|
1420
|
+
/** Human-readable detail fields for a POI info popover. Only surfaces
|
|
1421
|
+
* fields that exist on the current data — `category`, `floorName`,
|
|
1422
|
+
* `zoneName`, `keywords`, `description`, and `imageUrl` are each omitted
|
|
1423
|
+
* when absent. Keeps the display-field derivation (incl. the zone lookup)
|
|
1424
|
+
* in the SDK so consumers stay thin. */
|
|
1425
|
+
getPOIDetails(poi: POI): {
|
|
1426
|
+
name: string;
|
|
1427
|
+
type: 'amenity' | 'destination' | 'kiosk';
|
|
1428
|
+
category?: string;
|
|
1429
|
+
floorName?: string;
|
|
1430
|
+
floorShortName?: string;
|
|
1431
|
+
zoneName?: string;
|
|
1432
|
+
zoneDescription?: string;
|
|
1433
|
+
/** The department's authored hex colour (`Zone.color`) — the same value
|
|
1434
|
+
* that tints this room's polygon on the map, so a consumer can print a
|
|
1435
|
+
* swatch beside the department name and connect the two. Absent when the
|
|
1436
|
+
* author never picked one, which is also when the map leaves the room
|
|
1437
|
+
* untinted; treat "no colour" as "this department isn't colour-coded",
|
|
1438
|
+
* not as "fall back to a default swatch". */
|
|
1439
|
+
zoneColor?: string;
|
|
1440
|
+
keywords?: string[];
|
|
1441
|
+
description?: string;
|
|
1442
|
+
/** CMS-authored extras from the entity's `extensors` bag (opening hours,
|
|
1443
|
+
* days, room, building). Free-form — the consumer decides what to render.
|
|
1444
|
+
* Artwork is not in here; see `imageUrl`. */
|
|
1445
|
+
properties?: Record<string, unknown>;
|
|
1446
|
+
/** The destination's uploaded location image, ready for an `<img src>`.
|
|
1447
|
+
* Destinations only — amenities use sprite glyphs, not photos. Inline SVG
|
|
1448
|
+
* from `/all` comes back as a data URI; otherwise it's the uploaded uri
|
|
1449
|
+
* path. Absent when nothing was uploaded in the CMS. */
|
|
1450
|
+
imageUrl?: string;
|
|
1451
|
+
/** Author-written alt text for `imageUrl` (`mm_image_alt`) — a description
|
|
1452
|
+
* of the place, for screen readers and for anyone the image fails to load
|
|
1453
|
+
* for. Absent when the author wrote none, in which case the image is
|
|
1454
|
+
* decorative and belongs in an `<img alt="">`, not an unlabelled one. */
|
|
1455
|
+
imageAlt?: string;
|
|
1456
|
+
/** Room number — the CMS's "Room Number". Reads the real
|
|
1457
|
+
* `Destination.unitNumber` column, falling back to a legacy hand-typed
|
|
1458
|
+
* `room_number` property for venues authored before that field existed. */
|
|
1459
|
+
roomNumber?: string;
|
|
1460
|
+
/** Which building on the campus this destination is in (`mm_building_name`),
|
|
1461
|
+
* as the author typed it — "200", "Ambulatory Care". Free text, not a
|
|
1462
|
+
* reference to a `Building` record; render it, don't resolve it. Falls back
|
|
1463
|
+
* to the legacy hand-typed `building_name` property, which is unprefixed
|
|
1464
|
+
* and therefore a different key. */
|
|
1465
|
+
buildingName?: string;
|
|
1466
|
+
/** Phone number and internal extension (`mm_phone`). The number is exactly
|
|
1467
|
+
* as the author typed it, not normalised — see `parsePhone`. */
|
|
1468
|
+
phone?: DestinationPhone;
|
|
1469
|
+
/** The CMS-authored opening hours (`mm_open_hours`), parsed. Absent when
|
|
1470
|
+
* the author set none, or the stored value is unreadable.
|
|
1471
|
+
*
|
|
1472
|
+
* Static — it describes the week, not this moment. Pass it to
|
|
1473
|
+
* `getOpenStatus()` for the live open/closed state, and re-call that on a
|
|
1474
|
+
* tick if the UI stays on screen. The raw string also remains in
|
|
1475
|
+
* `properties` for consumers that would rather parse it themselves. */
|
|
1476
|
+
openHours?: OpenHours;
|
|
1477
|
+
};
|
|
1478
|
+
/** Resolve the source Destination/Amenity record backing a rendered POI,
|
|
1479
|
+
* for detail fields (category, localized description) that don't live on
|
|
1480
|
+
* the built POI object. */
|
|
1481
|
+
private getSourceEntity;
|
|
272
1482
|
getPolygonLayers(): any[];
|
|
1483
|
+
/**
|
|
1484
|
+
* Cross-reference the venue's drawn layers against the customer's allowed
|
|
1485
|
+
* layer list (JACS `GET /customer/{id}/polygon-layer`) and against what the
|
|
1486
|
+
* active theme can actually render.
|
|
1487
|
+
*
|
|
1488
|
+
* Three failure modes, none of which is visible by looking at the map:
|
|
1489
|
+
* a layer drawn under a name the CMS doesn't list (`unauthorized`), a layer
|
|
1490
|
+
* legitimately drawn that no theme rule matches (`unstyled`), and a theme
|
|
1491
|
+
* rule with nothing to draw at this venue (`unused`). Runs automatically
|
|
1492
|
+
* once after `ready` when `options.debug` is on; call it directly —
|
|
1493
|
+
* `mm.validateMapLayers()` — any time.
|
|
1494
|
+
*
|
|
1495
|
+
* Only counts floors whose geojson has loaded. Init preloads the rest in
|
|
1496
|
+
* the background, so a call in the first moments after `ready` may report
|
|
1497
|
+
* fewer `inData` types than a call a second later.
|
|
1498
|
+
*/
|
|
1499
|
+
validateMapLayers(): Promise<LayerAudit>;
|
|
1500
|
+
/** Debug-mode auto-run: log findings once, stay silent when clean. */
|
|
1501
|
+
private reportLayerAudit;
|
|
273
1502
|
getFloorMapTemplate3d(floorId: string | number): any[];
|
|
274
1503
|
getAllPOIs(floor?: Floor): POI[];
|
|
1504
|
+
/**
|
|
1505
|
+
* Put the map on the kiosk's floor before drawing a route from it.
|
|
1506
|
+
*
|
|
1507
|
+
* **A route from the kiosk always starts on the kiosk's floor**, so that is
|
|
1508
|
+
* the floor the visitor has to be looking at when it appears. The renderer
|
|
1509
|
+
* agrees with that already — `wayfinding` reveals and frames *the active
|
|
1510
|
+
* floor's* slice of the route — which is exactly why this matters: leave the
|
|
1511
|
+
* map where the visitor had wandered to and the route arrives framed on the
|
|
1512
|
+
* wrong leg, or on no leg at all.
|
|
1513
|
+
*
|
|
1514
|
+
* Both halves of that were reported from a real kiosk. Browse up to Level 3,
|
|
1515
|
+
* search, press Directions: if Level 3 isn't on the route the fit falls back
|
|
1516
|
+
* to the union of every floor and nothing reads as a path from here; if it
|
|
1517
|
+
* *is* on the route — because the destination is up there — the visitor is
|
|
1518
|
+
* looking at the last leg of a walk they haven't started, with no indication
|
|
1519
|
+
* that the first one is two floors down.
|
|
1520
|
+
*
|
|
1521
|
+
* Called before the route is computed rather than after it's drawn, so the
|
|
1522
|
+
* camera makes one move instead of fitting the wrong floor and correcting.
|
|
1523
|
+
* No-op when the venue has no kiosk anchor (those routes fail anyway) or the
|
|
1524
|
+
* map is already there.
|
|
1525
|
+
*/
|
|
1526
|
+
private showKioskFloorForRoute;
|
|
1527
|
+
/**
|
|
1528
|
+
* The floor the kiosk is physically anchored to — not necessarily the
|
|
1529
|
+
* floor currently displayed (the visitor may have panned the floor
|
|
1530
|
+
* selector to preview a destination on another level, and the SDK opens
|
|
1531
|
+
* on the venue's configured default floor rather than the kiosk's own —
|
|
1532
|
+
* see `FloorManager.selectInitialFloor`). Anchor resolution (routing,
|
|
1533
|
+
* recentering, closest-waypoint) must resolve against this floor, never
|
|
1534
|
+
* `getCurrentFloor()`, or it silently misses the "you are here" POI
|
|
1535
|
+
* whenever the two diverge. `null` when the venue has no kiosk anchor.
|
|
1536
|
+
*/
|
|
1537
|
+
getKioskFloor(): Floor | null;
|
|
275
1538
|
getYouAreHerePOI(floor?: Floor): POI | null;
|
|
276
1539
|
getYouAreHereCoordinates(floor?: Floor): [number, number] | null;
|
|
1540
|
+
/**
|
|
1541
|
+
* Compass heading the kiosk's physical hardware faces, in degrees
|
|
1542
|
+
* clockwise from north. Sourced from JACS `Device.heading`. `null` when
|
|
1543
|
+
* the venue's device record has no heading set.
|
|
1544
|
+
*
|
|
1545
|
+
* In order to rotate the map so it is oriented towards the user's gaze,
|
|
1546
|
+
* 180 is subtracted from `Device.heading`.
|
|
1547
|
+
*
|
|
1548
|
+
* Drives `recenterOnKiosk({ useHeading: true })` and the directional
|
|
1549
|
+
* cone rendered next to the "You are here" marker.
|
|
1550
|
+
*/
|
|
1551
|
+
getKioskHeading(): number | null;
|
|
1552
|
+
/**
|
|
1553
|
+
* Bearing (degrees clockwise from north) the visitor at the kiosk is
|
|
1554
|
+
* FACING — the device heading rotated 180°, since they stand in front
|
|
1555
|
+
* of the screen looking back at it. `null` when the device record
|
|
1556
|
+
* carries no heading, in which case direction-relative wording ("turn
|
|
1557
|
+
* right and walk 40 ft") is suppressed rather than guessed.
|
|
1558
|
+
*/
|
|
1559
|
+
private getVisitorFacingBearing;
|
|
1560
|
+
/**
|
|
1561
|
+
* Display name of the JACS device the kiosk is anchored to (e.g.
|
|
1562
|
+
* "Main Lobby Kiosk", "Information Desk Kiosk"). `null` when the venue
|
|
1563
|
+
* has no kiosk anchor or the device record carries no `name`.
|
|
1564
|
+
*
|
|
1565
|
+
* Distinct from `getYouAreHerePOI()?.name`, which is always the
|
|
1566
|
+
* literal "You are here" label rendered on the map. Use this for chrome
|
|
1567
|
+
* that needs the kiosk's identity — e.g. the directions overlay's
|
|
1568
|
+
* "From <kiosk>" header.
|
|
1569
|
+
*/
|
|
1570
|
+
getKioskName(): string | null;
|
|
1571
|
+
/**
|
|
1572
|
+
* Distance + walking time from the kiosk's anchor waypoint to a target,
|
|
1573
|
+
* computed over the JACS path graph (the same graph the routing engine
|
|
1574
|
+
* uses). Designed for "Closest: 30 sec walk" subtitles in the consumer
|
|
1575
|
+
* UI — no second graph build, no second Dijkstra implementation.
|
|
1576
|
+
*
|
|
1577
|
+
* Accepts a raw waypoint id, an amenity / destination object (the first
|
|
1578
|
+
* entry of its `waypoints` array is treated as the entry point), or any
|
|
1579
|
+
* `{ id, mapId? }` shape. Returns `null` when the kiosk isn't anchored,
|
|
1580
|
+
* the target waypoint isn't on the graph, or no path resolves.
|
|
1581
|
+
*
|
|
1582
|
+
* Walking speed defaults to 2.953 ft/s (0.9 m/s, average adult walking speed —
|
|
1583
|
+
* PMC2967707). Pass `walkingSpeedFps` to estimate for accessibility (e.g. 2).
|
|
1584
|
+
*
|
|
1585
|
+
* `feet` / `seconds` are the *physical* walk — horizontal legs only, what a
|
|
1586
|
+
* visitor is shown. `effortSeconds` adds the router's charge for each floor
|
|
1587
|
+
* change (the elevator wait) and is what to *rank* candidates by; see
|
|
1588
|
+
* `findClosestByWalkTime`. Pass `avoidStairs` to price the path as a route
|
|
1589
|
+
* with that option would be.
|
|
1590
|
+
*/
|
|
1591
|
+
getWalkTimeFromKiosk(target: number | string | {
|
|
1592
|
+
id?: number | string;
|
|
1593
|
+
mapId?: number;
|
|
1594
|
+
} | {
|
|
1595
|
+
waypoints?: Array<number | string | {
|
|
1596
|
+
id?: number | string;
|
|
1597
|
+
}>;
|
|
1598
|
+
}, opts?: {
|
|
1599
|
+
walkingSpeedFps?: number;
|
|
1600
|
+
avoidStairs?: boolean;
|
|
1601
|
+
}): {
|
|
1602
|
+
feet: number;
|
|
1603
|
+
seconds: number;
|
|
1604
|
+
effortSeconds: number;
|
|
1605
|
+
pathNodeCount: number;
|
|
1606
|
+
} | null;
|
|
1607
|
+
/**
|
|
1608
|
+
* Center the camera on the kiosk's "You are here" position. Use this
|
|
1609
|
+
* (not `resetView`) for a chrome "Recenter" button — `resetView` snaps
|
|
1610
|
+
* to the SDK's captured default camera, which may have been a venue
|
|
1611
|
+
* overview rather than the kiosk's spot.
|
|
1612
|
+
*
|
|
1613
|
+
* When `useHeading` is true (default) and the kiosk device has a
|
|
1614
|
+
* `heading` on file, the camera's bearing is rotated so the direction
|
|
1615
|
+
* the kiosk physically faces ends up at the top of the screen. That
|
|
1616
|
+
* way "what the user sees in front of them" matches "what's up on the
|
|
1617
|
+
* map" — the single largest readability win for stressed indoor users.
|
|
1618
|
+
*/
|
|
1619
|
+
recenterOnKiosk(opts?: {
|
|
1620
|
+
useHeading?: boolean;
|
|
1621
|
+
zoom?: number;
|
|
1622
|
+
pitch?: number;
|
|
1623
|
+
animate?: boolean;
|
|
1624
|
+
duration?: number;
|
|
1625
|
+
}): void;
|
|
277
1626
|
searchPOIs(query: string, floor?: Floor): POI[];
|
|
1627
|
+
/**
|
|
1628
|
+
* Search POIs across every floor in the venue. Returns raw matches —
|
|
1629
|
+
* an amenity that exists on multiple floors appears multiple times,
|
|
1630
|
+
* once per instance. Consumers that need one row per logical amenity
|
|
1631
|
+
* should dedupe by id (and, for "nearest", pick the instance closest
|
|
1632
|
+
* to the kiosk via planar distance on `coordinates`).
|
|
1633
|
+
*/
|
|
1634
|
+
searchAllPOIs(query: string): POI[];
|
|
278
1635
|
wayfindBetweenWaypoints(fromWaypoint: any, toWaypoint: any, options?: {
|
|
279
1636
|
centerMode?: 'none' | 'destination' | 'route';
|
|
280
1637
|
zoom?: number;
|
|
1638
|
+
/** Hard-filter stairs from the graph. */
|
|
1639
|
+
avoidStairs?: boolean;
|
|
281
1640
|
}): Promise<any>;
|
|
282
1641
|
navigateFromKioskToDestination(destination: any): Promise<any>;
|
|
1642
|
+
/**
|
|
1643
|
+
* Route from the kiosk to a specific POI's waypoint. Use this when you
|
|
1644
|
+
* already hold a resolved POI (e.g. from `searchPOIs` / `searchAllPOIs`).
|
|
1645
|
+
* For a venue-wide amenity record with multiple instances, resolve via
|
|
1646
|
+
* `findClosestWaypoint` first — or use `highlightAmenity` instead, if
|
|
1647
|
+
* you want a visual focus rather than a route.
|
|
1648
|
+
*/
|
|
1649
|
+
navigateFromKioskToPOI(poi: POI, options?: {
|
|
1650
|
+
avoidStairs?: boolean;
|
|
1651
|
+
}): Promise<any>;
|
|
1652
|
+
/** Build the landmark / floor-name context the directions module needs.
|
|
1653
|
+
* Pulled out so `setLocale()` or `setActiveStep()` can rebuild it on
|
|
1654
|
+
* demand if we ever surface a locale-aware variant. */
|
|
1655
|
+
/**
|
|
1656
|
+
* Rewrite every source whose features carry venue-authored *text* for a
|
|
1657
|
+
* floor: POI labels and map labels. Both read their names straight out of
|
|
1658
|
+
* the venue model, so anything that mutates that model in place — today
|
|
1659
|
+
* `setLocale`'s translation patch — has to call this or the map keeps
|
|
1660
|
+
* rendering the old strings until the next floor change.
|
|
1661
|
+
*
|
|
1662
|
+
* Map labels ride the same lifecycle as POIs and carry the `allLevels`
|
|
1663
|
+
* flag, which the projector has already honored by duplicating the
|
|
1664
|
+
* instance under every floor's list.
|
|
1665
|
+
*/
|
|
1666
|
+
private redrawFloorText;
|
|
1667
|
+
private buildDirectionsContext;
|
|
1668
|
+
/**
|
|
1669
|
+
* Drive the turn-by-turn UI. Two effects:
|
|
1670
|
+
*
|
|
1671
|
+
* 1. When the step's `floorId` differs from the active floor, switch
|
|
1672
|
+
* floors so the segment for the step's leg becomes visible.
|
|
1673
|
+
* 2. Highlight the step's slice of the route line by writing the
|
|
1674
|
+
* point range to the `route-active-step` source; the bundled
|
|
1675
|
+
* theme's `route-line-active` layer paints it in gold over the
|
|
1676
|
+
* muted base route.
|
|
1677
|
+
*
|
|
1678
|
+
* Transition steps (cross-floor elevator / stair hops) clear the
|
|
1679
|
+
* highlight — the overlay text carries the action, and there's no
|
|
1680
|
+
* meaningful on-floor segment to paint.
|
|
1681
|
+
*
|
|
1682
|
+
* Pass `null` to clear the highlight without changing the floor or
|
|
1683
|
+
* tearing down the route.
|
|
1684
|
+
*/
|
|
1685
|
+
setActiveStep(step: WayfindStep | null): Promise<void>;
|
|
1686
|
+
private writeActiveStepHighlight;
|
|
1687
|
+
private clearActiveStepHighlight;
|
|
283
1688
|
clearRoute(): void;
|
|
1689
|
+
/**
|
|
1690
|
+
* Put the end-of-route marker on `poi`, or take it down (`null`).
|
|
1691
|
+
*
|
|
1692
|
+
* A destination gets a flag in place of its dot; an amenity instance gets an
|
|
1693
|
+
* enlarged copy of its own icon (`map/routeEnd.ts`). Both are drawn from a
|
|
1694
|
+
* source of their own, so this never touches the POI layers — clearing the
|
|
1695
|
+
* route empties the source and the ordinary dot / icon are what remain.
|
|
1696
|
+
*
|
|
1697
|
+
* The routed restroom is also kept out of restroom grouping while the marker
|
|
1698
|
+
* is up; see `RestroomGroupManager.setExcludedKey`.
|
|
1699
|
+
*/
|
|
1700
|
+
private setRouteEnd;
|
|
1701
|
+
/**
|
|
1702
|
+
* Highlight a single POI on the map — a pulsing ring — and bring it into
|
|
1703
|
+
* view. Resolves the POI across all floors, switching the active floor if
|
|
1704
|
+
* it lives on another one. Pass a POI / amenity / destination id.
|
|
1705
|
+
*/
|
|
1706
|
+
highlightPOI(id: string | number): Promise<void>;
|
|
1707
|
+
/**
|
|
1708
|
+
* Select a POI programmatically — the same end state as tapping it on the
|
|
1709
|
+
* map, from a list or any other chrome.
|
|
1710
|
+
*
|
|
1711
|
+
* Switches the active floor first when the POI lives on another one, raises
|
|
1712
|
+
* its room as a highlighted block, mutes the neighbouring units, declutters
|
|
1713
|
+
* to the selected destination(s), pans it into the unobscured map area and
|
|
1714
|
+
* emits **`poiSelected`** carrying every co-located POI. A consumer that
|
|
1715
|
+
* already renders an info card off that event therefore needs no second code
|
|
1716
|
+
* path: a tap and a list pick arrive the same way.
|
|
1717
|
+
*
|
|
1718
|
+
* This is deliberately *not* `highlightPOI`, which is the search-locate
|
|
1719
|
+
* gesture — it zooms and recenters and draws a pulsing ring, and emits
|
|
1720
|
+
* nothing. Selection is the quieter one: current zoom kept, no ring, the
|
|
1721
|
+
* raised room is the indicator.
|
|
1722
|
+
*
|
|
1723
|
+
* Accepts a `POI` (preferred — its `type` and `waypointId` pin down the exact
|
|
1724
|
+
* instance, which matters for an amenity that shares one id across every
|
|
1725
|
+
* physical pin) or a bare id.
|
|
1726
|
+
*
|
|
1727
|
+
* **`includeCoLocated: false` emits only the requested POI.** By default the
|
|
1728
|
+
* payload carries everything sharing the room, which is right when the
|
|
1729
|
+
* gesture was a tap — the visitor pointed at a place on a floor plan and the
|
|
1730
|
+
* consumer has to show them what's there. It's wrong when they picked one
|
|
1731
|
+
* entry out of a named list: they have already disambiguated, and handing
|
|
1732
|
+
* back four places would ask them to do it again, on a card that opened
|
|
1733
|
+
* *because* they were specific. The map treatment is identical either way
|
|
1734
|
+
* (same room raised, same neighbours muted, same pan); only the payload —
|
|
1735
|
+
* and so the declutter's allow-list — narrows.
|
|
1736
|
+
*
|
|
1737
|
+
* Returns the selected POIs, `pois[0]` being the requested one; an empty
|
|
1738
|
+
* array if no POI matched, which is also when nothing on the map changes.
|
|
1739
|
+
*/
|
|
1740
|
+
selectPOI(target: POI | string | number, options?: {
|
|
1741
|
+
includeCoLocated?: boolean;
|
|
1742
|
+
}): Promise<POI[]>;
|
|
1743
|
+
/** Remove the POI highlight set by `highlightPOI`, un-dim the other units,
|
|
1744
|
+
* and bring back the destination markers hidden by a room selection. */
|
|
1745
|
+
clearHighlight(): void;
|
|
1746
|
+
/** Map a click's raw feature hits to the full, de-duplicated POIs at that
|
|
1747
|
+
* point (see `resolvePOIsFromFeatures`). Injected into `SelectionManager`.
|
|
1748
|
+
* `getAllPOIs()` is the current floor's rendered POIs — the only clickable
|
|
1749
|
+
* ones — and carries each POI's `waypoint`, needed for directions. */
|
|
1750
|
+
private resolveClickedPOIs;
|
|
1751
|
+
/** Emit `poiSelected` for the co-located POIs at a clicked point. Focuses the
|
|
1752
|
+
* selection in place (no recenter — the user already sees where they
|
|
1753
|
+
* tapped): rings the tapped dot and, when the tap landed in a room, raises
|
|
1754
|
+
* that room as a highlighted block so the eye goes to the selected unit.
|
|
1755
|
+
* `clearHighlight` removes both. `poi` mirrors `pois[0]` for single-POI
|
|
1756
|
+
* consumers. */
|
|
1757
|
+
private emitPoiSelected;
|
|
1758
|
+
/**
|
|
1759
|
+
* Build the room-highlight payload for a selection. The colour comes from the
|
|
1760
|
+
* *clicked* feature (the rendered truth), but the geometry comes from the
|
|
1761
|
+
* floor's full source polygon — the clicked feature is from
|
|
1762
|
+
* `queryRenderedFeatures`, whose geometry is clipped to on-screen tiles, so
|
|
1763
|
+
* highlighting it directly would leave any off-screen part of the room grey
|
|
1764
|
+
* (its base extrusion is muted). Falls back to the clipped geometry if the
|
|
1765
|
+
* full polygon can't be found. Returns `null` for a pin with no room.
|
|
1766
|
+
*/
|
|
1767
|
+
private unitHighlightFor;
|
|
1768
|
+
/** The full, unclipped `Units` polygon on the current floor that contains
|
|
1769
|
+
* `coord`, straight from the floor source (not the tile-clipped render). */
|
|
1770
|
+
private fullUnitPolygonAt;
|
|
1771
|
+
/**
|
|
1772
|
+
* Hide every destination marker except the given ids (pass `null` to
|
|
1773
|
+
* restore all). Narrows each destination layer's own filter with an id
|
|
1774
|
+
* allow-list; amenity/connector/"you are here" layers are untouched, so they
|
|
1775
|
+
* stay on the map as reference. Captures the original filters once and puts
|
|
1776
|
+
* them back verbatim, so the layers' base rules (poiType, showLabel, iconId)
|
|
1777
|
+
* survive.
|
|
1778
|
+
*/
|
|
1779
|
+
private setDestinationFocus;
|
|
1780
|
+
/**
|
|
1781
|
+
* Select every destination inside a tapped cluster bubble, so a consumer's
|
|
1782
|
+
* multi-place card lists them — the same `poiSelected` a tap on stacked pins
|
|
1783
|
+
* emits.
|
|
1784
|
+
*
|
|
1785
|
+
* Unlike `emitPoiSelected` this raises no room and narrows no markers. A
|
|
1786
|
+
* cluster spans rooms, so there is no one unit to raise or dim around, and
|
|
1787
|
+
* the destination focus would filter out the bubble that was just tapped
|
|
1788
|
+
* (its features carry no `id`) and the members hidden inside it with it.
|
|
1789
|
+
* Any earlier selection is cleared first, so a room left raised by the last
|
|
1790
|
+
* tap doesn't outlive this one. `clearHighlight` afterwards is a harmless
|
|
1791
|
+
* no-op for the consumer to call on dismiss.
|
|
1792
|
+
*
|
|
1793
|
+
* The leaves are read back from the cluster source (`getClusterLeaves`: a
|
|
1794
|
+
* promise on MapLibre v5, a callback on v4 — `peerDependencies` allows both)
|
|
1795
|
+
* and resolved against the floor's POIs, since a source feature only carries
|
|
1796
|
+
* enough identity to look the full POI (with its waypoint) back up.
|
|
1797
|
+
*/
|
|
1798
|
+
private selectCluster;
|
|
1799
|
+
/** Gentle pan so the selection sits in the centre of the map area MapLibre
|
|
1800
|
+
* frames within `boundsPadding` (which the consumer sets to reserve for its
|
|
1801
|
+
* bottom-docked chrome / info card). A straight `easeTo`, current zoom kept,
|
|
1802
|
+
* so it reads as a nudge, not a recenter. */
|
|
1803
|
+
private easeToSelection;
|
|
1804
|
+
/** Dim every unit's paint (or restore it) so a selected room reads as the
|
|
1805
|
+
* focus. Idempotent: capturing only happens on the first activate, and the
|
|
1806
|
+
* captured originals are put back verbatim on deactivate. */
|
|
1807
|
+
private setUnitFocusDim;
|
|
1808
|
+
/**
|
|
1809
|
+
* Show only the named POI categories on the map. Pass `['amenity']` to
|
|
1810
|
+
* emphasize amenities (e.g. while an Amenities drawer is open) — destination
|
|
1811
|
+
* POIs hide. Call `clearPOIFilter()` to restore the default (all categories).
|
|
1812
|
+
*
|
|
1813
|
+
* The "You are here" marker, the wheelchair-accessibility badge, and any
|
|
1814
|
+
* active highlight are not affected and always render.
|
|
1815
|
+
*/
|
|
1816
|
+
setPOIFilter(types: Array<'amenity' | 'destination'>): void;
|
|
1817
|
+
/** Restore the default — every POI category renders. */
|
|
1818
|
+
clearPOIFilter(): void;
|
|
1819
|
+
/**
|
|
1820
|
+
* Narrow the POI markers to a curated set: only POIs for which `match`
|
|
1821
|
+
* returns true keep their icon and label, every other pin on the categorical
|
|
1822
|
+
* marker layers (destinations, entrances, parking, bus, connectors,
|
|
1823
|
+
* information, and the per-instance amenity names) is filtered out. Built for
|
|
1824
|
+
* an attract loop that wants a few chosen places on screen instead of either
|
|
1825
|
+
* every marker or none.
|
|
1826
|
+
*
|
|
1827
|
+
* `match` runs against the current floor's POIs — the same objects
|
|
1828
|
+
* `getAllPOIs()` returns, `instanceName` and `amenityCategory` included — and
|
|
1829
|
+
* is re-run on every floor change, theme swap and locale redraw, so the
|
|
1830
|
+
* spotlight follows the map rather than the floor it was set on. Kiosk
|
|
1831
|
+
* "You are here" is never touched.
|
|
1832
|
+
*
|
|
1833
|
+
* Two things it deliberately leaves alone:
|
|
1834
|
+
* - `poi-other-amenity-icons` and the restroom badge. Their filters are owned
|
|
1835
|
+
* by the restroom-group manager and rewritten as the camera moves, so a
|
|
1836
|
+
* narrowing there would not survive a frame. Hide them with `hideLayers`
|
|
1837
|
+
* if the spotlight should exclude them.
|
|
1838
|
+
* - `setPOIFilter`'s category visibility, which composes: a category the
|
|
1839
|
+
* filter hides stays hidden whatever `match` says.
|
|
1840
|
+
*
|
|
1841
|
+
* Selecting a place (`selectPOI`, a map tap) releases the spotlight. Pass
|
|
1842
|
+
* `null` — or call `clearPOISpotlight()` — to restore every marker.
|
|
1843
|
+
*/
|
|
1844
|
+
setPOISpotlight(match: ((poi: POI) => boolean) | null): void;
|
|
1845
|
+
/** Re-apply the zoom profile if the idle spotlight or a route just changed
|
|
1846
|
+
* which one is in force, and refresh what hangs off it. */
|
|
1847
|
+
private syncZoomViews;
|
|
1848
|
+
/** Restore every POI marker narrowed by `setPOISpotlight`. */
|
|
1849
|
+
clearPOISpotlight(): void;
|
|
1850
|
+
private applyPOISpotlight;
|
|
1851
|
+
private applyPOIFilter;
|
|
1852
|
+
/**
|
|
1853
|
+
* Show or hide destination dots, independently of their name labels and of
|
|
1854
|
+
* `setPOIFilter`'s combined `'destination'` category (which still toggles
|
|
1855
|
+
* both together). Affects every destination dot — both the dot+label
|
|
1856
|
+
* marker on `poi-destination-labels` and the dot-only fallback on
|
|
1857
|
+
* `poi-destination-circles` that a `showLabel: false` POI renders on.
|
|
1858
|
+
*
|
|
1859
|
+
* Implemented as `icon-opacity`, not a layer swap: dot and label are
|
|
1860
|
+
* placed as one collision unit on `poi-destination-labels` (`icon-optional:
|
|
1861
|
+
* false` / `text-optional: true`), which is what guarantees a label never
|
|
1862
|
+
* renders without a dot beneath it. Splitting them into independently
|
|
1863
|
+
* *placed* layers would let MapLibre's collision index resolve each half
|
|
1864
|
+
* on its own, so a label could win placement while its dot lost — the
|
|
1865
|
+
* "floating name" bug this theme's combined layer was built to fix (see
|
|
1866
|
+
* `themes.test.ts` :: "a destination is one marker").
|
|
1867
|
+
*/
|
|
1868
|
+
setDestinationCirclesVisible(visible: boolean): void;
|
|
1869
|
+
/**
|
|
1870
|
+
* Show or hide destination name labels, independently of their dots. See
|
|
1871
|
+
* `setDestinationCirclesVisible` for why this is an opacity toggle on the
|
|
1872
|
+
* shared marker layer rather than a separate label layer.
|
|
1873
|
+
*
|
|
1874
|
+
* `visible: true` (the default) doesn't mean "always on" — labels still
|
|
1875
|
+
* fade in only once the visitor has zoomed in past the venue's opening
|
|
1876
|
+
* view (`DESTINATION_LABEL_REVEAL_OFFSET` in `iconZoomRanges.ts`), so a
|
|
1877
|
+
* zoomed-out visitor sees dots without the screen filling with names. Pass
|
|
1878
|
+
* `false` to suppress labels outright regardless of zoom.
|
|
1879
|
+
*/
|
|
1880
|
+
setDestinationLabelsVisible(visible: boolean): void;
|
|
1881
|
+
private applyDestinationDisplay;
|
|
1882
|
+
/**
|
|
1883
|
+
* Return the waypoint in `waypoints` closest to `from`. Useful for
|
|
1884
|
+
* "route to the nearest X" against a venue-wide amenity record that has
|
|
1885
|
+
* multiple physical instances. Defaults `from` to the kiosk's
|
|
1886
|
+
* "You are here" coordinates.
|
|
1887
|
+
*
|
|
1888
|
+
* Distance is planar Euclidean on lng/lat — sufficient for ordering at
|
|
1889
|
+
* single-venue scale, and avoids a turf dependency on the hot path.
|
|
1890
|
+
*/
|
|
1891
|
+
findClosestWaypoint(waypoints: Waypoint[], from?: [number, number]): Waypoint | null;
|
|
1892
|
+
/**
|
|
1893
|
+
* Pick the candidate cheapest to reach from the kiosk — the one rule for
|
|
1894
|
+
* "closest instance of an amenity". `navigateFromKioskToClosestAmenity` and
|
|
1895
|
+
* `highlightAmenity` use it, and a consumer collapsing a venue-wide amenity
|
|
1896
|
+
* to a single search row should too, so a tap and a search result can never
|
|
1897
|
+
* resolve the same amenity to different floors.
|
|
1898
|
+
*
|
|
1899
|
+
* Each candidate is scored by `getWalkTimeFromKiosk().effortSeconds` — walk
|
|
1900
|
+
* time over the JACS path graph **plus the elevator wait** for any floor
|
|
1901
|
+
* change — and a candidate on a *different floor* from the kiosk has that
|
|
1902
|
+
* score multiplied by `options.otherFloorPenalty` (default `1.5`; `1`
|
|
1903
|
+
* disables it). The wait is the physical cost of leaving the floor; the
|
|
1904
|
+
* multiplier is a preference for not making the visitor do it, and only
|
|
1905
|
+
* applies here — it never changes a route.
|
|
1906
|
+
*
|
|
1907
|
+
* `toWaypoint` maps a candidate to the waypoint it stands for (identity for
|
|
1908
|
+
* a `Waypoint[]`, `poi => poi.waypoint` for a `POI[]`). Candidates whose
|
|
1909
|
+
* waypoint is missing or has no coordinates are ignored, as are waypoints
|
|
1910
|
+
* the graph can't reach from the kiosk. If none resolve a cost, falls back
|
|
1911
|
+
* to the planar `findClosestWaypoint`, **restricted to the kiosk's floor
|
|
1912
|
+
* when any candidate is on it** (planar distance is meaningless across
|
|
1913
|
+
* overlaid floors). Returns `null` for no usable candidate. Pass the same
|
|
1914
|
+
* `avoidStairs` the route will use so the pick and the route agree.
|
|
1915
|
+
*/
|
|
1916
|
+
findClosestByWalkTime<T>(items: readonly T[], toWaypoint: (item: T) => Waypoint | null | undefined, opts?: {
|
|
1917
|
+
avoidStairs?: boolean;
|
|
1918
|
+
}): T | null;
|
|
1919
|
+
/**
|
|
1920
|
+
* Route from the kiosk to the closest physical instance of an amenity.
|
|
1921
|
+
* Resolves the amenity venue-wide, picks the instance with the smallest
|
|
1922
|
+
* path-graph cost from the kiosk (`findClosestByWalkTime`: walk time plus
|
|
1923
|
+
* the elevator wait, with a preference for the kiosk's floor; falling back
|
|
1924
|
+
* to planar Euclidean when the walk-time graph can't resolve any waypoint), switches the
|
|
1925
|
+
* active floor if needed, and routes via the same plumbing as
|
|
1926
|
+
* `navigateFromKioskToPOI` — so step-by-step directions, the
|
|
1927
|
+
* `routeReady` event, and the active-floor camera fit all "just work."
|
|
1928
|
+
*
|
|
1929
|
+
* Pass `avoidStairs` to hard-filter stairs from the *route* (the SDK's
|
|
1930
|
+
* routing engine reads it per call; the CMS's per-path-type weight
|
|
1931
|
+
* otherwise handles accessibility preference automatically). The same
|
|
1932
|
+
* flag is passed to the closest-instance pick, so an instance reachable
|
|
1933
|
+
* only by stairs is skipped in favour of one the route can actually reach.
|
|
1934
|
+
*/
|
|
1935
|
+
navigateFromKioskToClosestAmenity(amenityId: string | number, options?: {
|
|
1936
|
+
avoidStairs?: boolean;
|
|
1937
|
+
}): Promise<any>;
|
|
1938
|
+
/**
|
|
1939
|
+
* Highlight + center on the closest physical instance of an amenity.
|
|
1940
|
+
* The amenity is resolved against `amenities.getDistinct()` (venue-wide,
|
|
1941
|
+
* deduped); the closest waypoint to the kiosk is picked; the active floor
|
|
1942
|
+
* is switched if that instance lives on another one.
|
|
1943
|
+
*
|
|
1944
|
+
* This is the right call from a venue-wide list ("here are all the
|
|
1945
|
+
* bathrooms in the building, take me to the closest one"). For taps on
|
|
1946
|
+
* a specific resolved POI (e.g. a search result that already names one
|
|
1947
|
+
* instance), use `highlightPOI` instead.
|
|
1948
|
+
*/
|
|
1949
|
+
highlightAmenity(amenityId: string | number): Promise<void>;
|
|
1950
|
+
/** Which floor owns this waypoint, by matching mapId against the floor's
|
|
1951
|
+
* `mapId` (or `id` as fallback). Used to switch floors when routing /
|
|
1952
|
+
* highlighting hits an instance on a different one. */
|
|
1953
|
+
private findFloorForWaypoint;
|
|
284
1954
|
getCameraPosition(): CameraState | null;
|
|
285
1955
|
getMap(): Map | null;
|
|
1956
|
+
/**
|
|
1957
|
+
* Hide one or more layers by id — a public, always-live counterpart to
|
|
1958
|
+
* `debug.hide()` for consumers that want to toggle arbitrary layers
|
|
1959
|
+
* without reaching into the console-facing debug API. Sticky: the
|
|
1960
|
+
* override is re-asserted across floor changes, `setPOIFilter`, view-mode
|
|
1961
|
+
* toggles and theme swaps until `showLayers` undoes it. Ids absent from
|
|
1962
|
+
* the active style are skipped silently. Returns the ids actually found
|
|
1963
|
+
* and hidden.
|
|
1964
|
+
*/
|
|
1965
|
+
hideLayers(layerIds: string[]): string[];
|
|
1966
|
+
/**
|
|
1967
|
+
* Undo `hideLayers` for these ids. Each layer returns to whatever
|
|
1968
|
+
* visibility the SDK's own state — current floor, POI filter, view mode —
|
|
1969
|
+
* says it should have; it is **not** forced to `visible`. A layer the SDK
|
|
1970
|
+
* is independently keeping hidden (another floor's layers, an
|
|
1971
|
+
* off-view-mode extrusion) stays hidden rather than being force-revealed.
|
|
1972
|
+
* This is the correct inverse of `hideLayers` (mirrors `debug.restore()` —
|
|
1973
|
+
* see `debugLayers.ts` for why a blanket "show" is usually the wrong
|
|
1974
|
+
* undo). Returns the ids that actually had an override to release.
|
|
1975
|
+
*/
|
|
1976
|
+
showLayers(layerIds: string[]): string[];
|
|
1977
|
+
/**
|
|
1978
|
+
* Whether a layer is currently rendering (`visibility !== 'none'`).
|
|
1979
|
+
* `false` for a layer absent from the active style.
|
|
1980
|
+
*/
|
|
1981
|
+
isLayerVisible(layerId: string): boolean;
|
|
1982
|
+
/**
|
|
1983
|
+
* Put this instance on `window` so a kiosk running in a browser can be
|
|
1984
|
+
* driven from devtools — `mm.debug.help()`, `mm.getMap()`, `mm.setTheme(…)`.
|
|
1985
|
+
* Debug builds only: nothing is exposed unless `options.debug` is on, and
|
|
1986
|
+
* `options.debugGlobal` renames the key (or `false` opts out entirely).
|
|
1987
|
+
*
|
|
1988
|
+
* Refuses to clobber a key that already holds something that isn't a
|
|
1989
|
+
* MinuteMaps — a kiosk shell with its own `window.mm` shouldn't lose it to
|
|
1990
|
+
* a dev-mode convenience.
|
|
1991
|
+
*/
|
|
1992
|
+
private exposeDebugGlobal;
|
|
1993
|
+
/** Drop the `window` handle, but only if it still points at this instance. */
|
|
1994
|
+
private releaseDebugGlobal;
|
|
286
1995
|
destroy(): void;
|
|
287
1996
|
setCurrentFloor(floor: Floor): Promise<void>;
|
|
1997
|
+
/**
|
|
1998
|
+
* Filter the route-line / route-halo layers to only render segments
|
|
1999
|
+
* whose `floorId` matches the active floor (or features that carry no
|
|
2000
|
+
* `floorId` at all — the straight-line fallback, which we want visible
|
|
2001
|
+
* on every floor since it has no floor membership to filter against).
|
|
2002
|
+
*
|
|
2003
|
+
* Features are tagged with the JACS pixel `mapId` (that's what flows
|
|
2004
|
+
* through the route point's `mapId`), so the filter compares against
|
|
2005
|
+
* the active floor's `mapId`, NOT its `id` — those are two different
|
|
2006
|
+
* JACS identifiers (`floor.id` is the building-floor record; `mapId`
|
|
2007
|
+
* is the SVG asset). We accept the floor's `id` here for convenience
|
|
2008
|
+
* and resolve to `mapId` via `getFloors()`.
|
|
2009
|
+
*
|
|
2010
|
+
* Applied imperatively rather than baked into the theme JSON so the
|
|
2011
|
+
* filter tracks runtime floor changes without restyling the map.
|
|
2012
|
+
*/
|
|
2013
|
+
private updateRouteFloorFilter;
|
|
288
2014
|
isReady(): boolean;
|
|
289
2015
|
private setFloorLayerVisibility;
|
|
290
2016
|
private getBoundsPadding;
|
|
2017
|
+
/**
|
|
2018
|
+
* Shrink per-side padding if the requested insets would leave no room.
|
|
2019
|
+
* `cameraForBounds` / `fitBounds` silently fail ("Map cannot fit within
|
|
2020
|
+
* canvas") when an axis's total padding meets or exceeds that axis — cap
|
|
2021
|
+
* each axis's total at 80% so a fit always has space to land.
|
|
2022
|
+
*/
|
|
2023
|
+
private clampPadding;
|
|
291
2024
|
private getVenueBounds;
|
|
2025
|
+
/**
|
|
2026
|
+
* Resolve `options.maxBounds` into the actual pan/zoom-out cap. `false`
|
|
2027
|
+
* disables it, explicit bounds pass through as-is, and a number (or the
|
|
2028
|
+
* unset default of 3) scales the venue's own bounds outward around its
|
|
2029
|
+
* center — see `scaleBounds`.
|
|
2030
|
+
*
|
|
2031
|
+
* The unset default is also floored at `DEFAULT_MIN_MAX_BOUNDS_SPAN_M` per
|
|
2032
|
+
* side. MapLibre won't zoom out past the point where `maxBounds` fills the
|
|
2033
|
+
* viewport, so a small venue's 3x box would otherwise raise the effective
|
|
2034
|
+
* zoom-out floor above `minZoom` and show far less surrounding map than a
|
|
2035
|
+
* large venue does. An explicit number is a deliberate leash and is left as-is.
|
|
2036
|
+
*/
|
|
2037
|
+
private getMaxBounds;
|
|
2038
|
+
/**
|
|
2039
|
+
* Opening-framing policy applied once after the initial floor fit.
|
|
2040
|
+
* - `initialPitch`: tilt to this and re-fit the building footprint so the
|
|
2041
|
+
* kiosk opens on a tilted building instead of a flat top-down plan.
|
|
2042
|
+
* - `minZoomBelowInitialFit`: clamp the map's minZoom to
|
|
2043
|
+
* `(fitZoom − margin)` so visitors can't pull back to the empty region.
|
|
2044
|
+
* Both are no-ops when their option is unset.
|
|
2045
|
+
*/
|
|
2046
|
+
private applyInitialFraming;
|
|
292
2047
|
private applyInitialViewFromVenue;
|
|
293
2048
|
private loadAndPatchVenueStyle;
|
|
294
2049
|
private ensureFloorLayersFromStyle;
|
|
@@ -296,5 +2051,113 @@ declare class MinuteMaps {
|
|
|
296
2051
|
}
|
|
297
2052
|
declare function createMinuteMapsSDK(config: SDKConfig): MinuteMaps;
|
|
298
2053
|
|
|
299
|
-
|
|
300
|
-
|
|
2054
|
+
type TransitionStep = Extract<WayfindStep, {
|
|
2055
|
+
type: 'transition';
|
|
2056
|
+
}>;
|
|
2057
|
+
type RouteFloorSection = {
|
|
2058
|
+
/** Floor (JACS `mapId`) this section is on. `null` only for a degenerate
|
|
2059
|
+
* floorless route (straight-line fallback with no `mapId`). */
|
|
2060
|
+
floorId: number | null;
|
|
2061
|
+
/** This floor's steps, each with its index into the original flat array
|
|
2062
|
+
* so the consumer can call `setActiveStep` / highlight by index. */
|
|
2063
|
+
steps: Array<{
|
|
2064
|
+
step: WayfindStep;
|
|
2065
|
+
index: number;
|
|
2066
|
+
}>;
|
|
2067
|
+
/** Index of this section's first step in the original array — the target
|
|
2068
|
+
* for `setActiveStep` when advancing INTO this section. */
|
|
2069
|
+
firstStepIndex: number;
|
|
2070
|
+
/** The transition step that leaves this floor for the next section, if
|
|
2071
|
+
* any. Absent on the final section. Its `text` ("Take the elevator to
|
|
2072
|
+
* Basement") makes a good "continue" button label. */
|
|
2073
|
+
exit?: {
|
|
2074
|
+
step: TransitionStep;
|
|
2075
|
+
index: number;
|
|
2076
|
+
};
|
|
2077
|
+
};
|
|
2078
|
+
/**
|
|
2079
|
+
* Group steps into per-floor sections. Single-floor routes return one
|
|
2080
|
+
* section (so a consumer can keep its whole-route view for that case).
|
|
2081
|
+
*/
|
|
2082
|
+
declare function groupStepsIntoFloorSections(steps: WayfindStep[]): RouteFloorSection[];
|
|
2083
|
+
/**
|
|
2084
|
+
* Which section is "active" given the active step index — the last section
|
|
2085
|
+
* whose `firstStepIndex` is at or before `activeStepIndex`. Returns 0 when
|
|
2086
|
+
* nothing matches (e.g. index points at the leading transition, which
|
|
2087
|
+
* shouldn't happen since routes start with a depart step).
|
|
2088
|
+
*/
|
|
2089
|
+
declare function activeSectionIndex(sections: RouteFloorSection[], activeStepIndex: number): number;
|
|
2090
|
+
|
|
2091
|
+
/** True when an amenity is a vertical-circulation connector. The provider
|
|
2092
|
+
* stamps `_connector` on every synthetic elevator / stairs / escalator, which
|
|
2093
|
+
* is the reliable signal — `extensors.iconName` is only the *display* glyph
|
|
2094
|
+
* and may be any CMS-assigned icon (we still honour it as a fallback for
|
|
2095
|
+
* amenities authored with a connector icon name directly). */
|
|
2096
|
+
declare function isConnectorAmenity(amenity: AmenityLike): boolean;
|
|
2097
|
+
type LocaleField = {
|
|
2098
|
+
locale?: string;
|
|
2099
|
+
uriPath?: string;
|
|
2100
|
+
};
|
|
2101
|
+
type UriField = {
|
|
2102
|
+
mimeType?: string;
|
|
2103
|
+
resourceType?: string;
|
|
2104
|
+
locales?: LocaleField[];
|
|
2105
|
+
};
|
|
2106
|
+
type AmenityLike = {
|
|
2107
|
+
id: number | string;
|
|
2108
|
+
iconId?: string;
|
|
2109
|
+
uris?: UriField[] | null;
|
|
2110
|
+
/** Inline SVG markup. Used by the SDK for synthetic connector
|
|
2111
|
+
* amenities (elevator / stairs / escalator) where the CMS has no
|
|
2112
|
+
* uploaded icon to fetch. When set, this short-circuits the
|
|
2113
|
+
* `uris[].locales[].uriPath` lookup. */
|
|
2114
|
+
svg?: string;
|
|
2115
|
+
/** Free-form CMS metadata. When `extensors.iconName` names a curated
|
|
2116
|
+
* Material Symbols icon, the SDK renders it from the bundled registry
|
|
2117
|
+
* (no upload, no network) — see `pickMaterialSvg`. */
|
|
2118
|
+
extensors?: Record<string, unknown> | null;
|
|
2119
|
+
/** Set by the JACS provider on synthetic vertical-circulation amenities
|
|
2120
|
+
* (elevator / stairs / escalator built from path types). The reliable
|
|
2121
|
+
* connector signal — `extensors.iconName` is the *display* glyph, which
|
|
2122
|
+
* may be any CMS-assigned icon, not the connector kind. */
|
|
2123
|
+
_connector?: boolean;
|
|
2124
|
+
};
|
|
2125
|
+
type ResolvedIconSource = {
|
|
2126
|
+
/** Map image id to register/reference. Shared across amenities for
|
|
2127
|
+
* connectors (preset) and curated Material icons (`material-<name>`). */
|
|
2128
|
+
iconId: string;
|
|
2129
|
+
/** Source for `composeAmenityBitmap` — exactly one of url / inlineSvg. */
|
|
2130
|
+
source: {
|
|
2131
|
+
url?: string;
|
|
2132
|
+
inlineSvg?: string;
|
|
2133
|
+
};
|
|
2134
|
+
/**
|
|
2135
|
+
* Where the artwork came from. The map doesn't care — it rasterises all
|
|
2136
|
+
* three the same way — but a DOM consumer must, because provenance decides
|
|
2137
|
+
* both trust and styling:
|
|
2138
|
+
*
|
|
2139
|
+
* - `material` — from the SDK's bundled registry. Ours, so it's safe to
|
|
2140
|
+
* inline into the DOM, and safe to recolour (it's a monochrome glyph).
|
|
2141
|
+
* - `inline` — an `svg` string off the CMS record. **Author-supplied
|
|
2142
|
+
* markup**: an inline `<svg>` can carry `<script>`, so render it through
|
|
2143
|
+
* an `<img src="data:…">` (which never executes script), not
|
|
2144
|
+
* `dangerouslySetInnerHTML`.
|
|
2145
|
+
* - `upload` — a URL to a CMS-uploaded file. Also author-supplied, also an
|
|
2146
|
+
* `<img>`; and it arrives in whatever colours the author chose, so don't
|
|
2147
|
+
* assume you can tint it to match a badge.
|
|
2148
|
+
*/
|
|
2149
|
+
kind: 'material' | 'inline' | 'upload';
|
|
2150
|
+
};
|
|
2151
|
+
/**
|
|
2152
|
+
* Pure icon-source resolution by precedence — no map/DOM side effects, so it's
|
|
2153
|
+
* unit-testable:
|
|
2154
|
+
* 1. inline svg — explicit inline-SVG override (rare)
|
|
2155
|
+
* 2. extensors.iconName — curated Material icon (bundled, shared bitmap);
|
|
2156
|
+
* also how synthetic connectors render now
|
|
2157
|
+
* 3. uris[].uriPath — legacy / custom-uploaded SVG (fetched)
|
|
2158
|
+
* Returns null when the amenity has no resolvable icon.
|
|
2159
|
+
*/
|
|
2160
|
+
declare function resolveAmenityIconSource(amenity: AmenityLike, preferredLocale?: string): ResolvedIconSource | null;
|
|
2161
|
+
|
|
2162
|
+
export { BUILDING_NAME_EXTENSOR_KEY, DAY_KEYS, IMAGE_ALT_EXTENSOR_KEY, MinuteMaps, OPEN_HOURS_EXTENSOR_KEY, OPEN_HOURS_VERSION, PHONE_EXTENSOR_KEY, POINT_OF_INTEREST_EXTENSOR_KEY, RESERVED_EXTENSOR_PREFIX, activeSectionIndex, createMinuteMapsSDK, getOpenStatus, groupStepsIntoFloorSections, isConnectorAmenity, parseBuildingName, parseImageAlt, parseOpenHours, parsePhone, parsePointOfInterest, resolveAmenityIconSource };
|
|
2163
|
+
export type { Amenity, AmenityBadgeStyle, AmenityWithFloor, Bounds, BoundsPadding, CameraState, DayKey, Destination, DestinationPhone, EventCallback, Floor, FloorMetadata, HoursRange, JMapAuth, JMapConfig, JacsAuth, JacsConfig, MapEvent, OpenHours, OpenHoursTransition, OpenStatus, OpenStatusOptions, POI, POISearchResult, ResolvedIconSource, RouteFloorSection, RoutePoint, RoutingOptions, SDKConfig, SDKOptions, ThemeName, TransitionStep, ViewOptions, WayfindCenterMode, WayfindStep, Waypoint, Zone };
|