@expofp/floorplan 3.44.8 → 3.45.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/browser/{Button-C1iQgU3z.js → Button-Dcwu_Erq.js} +5 -5
  2. package/dist/browser/{CookieConsent-Dv1LCPG3.js → CookieConsent-DGTw9yBm.js} +7 -7
  3. package/dist/browser/{Debug-DPy0A0cw.js → Debug-JZ9md0eP.js} +7 -7
  4. package/dist/browser/{DebugTools-Dp8d4gPZ.js → DebugTools-CratPuV3.js} +6 -6
  5. package/dist/browser/{Demo-CHH4a7sZ.js → Demo-CUmjH8Ho.js} +9 -9
  6. package/dist/browser/{Free-BidfuRjG.js → Free-DsJz0sgq.js} +10 -10
  7. package/dist/browser/{GalleryContainer-7IYWWKkK.js → GalleryContainer-CEdKWbFL.js} +11 -11
  8. package/dist/browser/{GpsLoader-CrO61Fpj.js → GpsLoader-CpihsOIR.js} +13 -13
  9. package/dist/browser/{KioskStore-BE_V60tp.js → KioskStore-C_LBovdc.js} +14 -20
  10. package/dist/browser/{LayerStore-BM7GOQ3J.js → LayerStore-91e6BAzl.js} +3726 -3220
  11. package/dist/browser/{MaplibreAdapter-DmrCTw3Q.js → MaplibreAdapter-9qou3kuu.js} +2386 -2396
  12. package/dist/browser/{ModalContainer-DqsSRv60.js → ModalContainer-BXfF2Q_j.js} +10 -10
  13. package/dist/browser/{PermissionNotice--Vn_MkaQ.js → PermissionNotice-Cs-hDB1d.js} +6 -6
  14. package/dist/browser/{RouteStore-D7G9YROM.js → RouteStore-CDPMgE4a.js} +28 -28
  15. package/dist/browser/{SetKioskMode-CMndo5d3.js → SetKioskMode-3qeyrZoQ.js} +12 -12
  16. package/dist/browser/{ShowKiosk-CUiprdS9.js → ShowKiosk-kOOPVQEC.js} +10 -10
  17. package/dist/browser/{UIState-D0GHKRf4.js → UIState-CruLIa-8.js} +230 -230
  18. package/dist/browser/{ViewerMenuPanel-B7corw44.js → ViewerMenuPanel-KKVazBcQ.js} +11 -11
  19. package/dist/browser/{add-debug-secret-listener-uMLIuPW5.js → add-debug-secret-listener-Dk6I0moa.js} +1 -1
  20. package/dist/browser/{adopt-global-settings-BVVdz-YX.js → adopt-global-settings-y2ubRt8B.js} +9 -9
  21. package/dist/browser/{boolean-editor-Dg3cfoLy.js → boolean-editor-DkLLh76y.js} +9 -9
  22. package/dist/browser/{bootIntercom-7FTJ0piX.js → bootIntercom-5AKlon7N.js} +2 -2
  23. package/dist/browser/bundle.json +87 -87
  24. package/dist/browser/{camera-events-DbYubA01.js → camera-events-DbMaLp1j.js} +1 -1
  25. package/dist/browser/{classnames-BryLpfgQ.js → classnames-Cjhuw82Y.js} +2 -2
  26. package/dist/browser/{client-DnnJr0Ks.js → client-DEugfaqe.js} +5 -5
  27. package/dist/browser/{components-DHNMQbqJ.js → components-BOe9R3hC.js} +997 -1011
  28. package/dist/browser/{create-live-plan-BgCjK-Kh.js → create-live-plan-643NFN4Y.js} +263 -245
  29. package/dist/browser/{debug-context-BEABO7OL.js → debug-context-Ctd_2DsZ.js} +5 -5
  30. package/dist/browser/{debug-overlay-Bv-C1rmT.js → debug-overlay-CNtn-y2A.js} +10 -10
  31. package/dist/browser/{debug-ui-FirQ0EQb.js → debug-ui-BFrAXZTt.js} +6 -6
  32. package/dist/browser/{dist-sup8YcEL.js → dist-BeUUWH0c.js} +5 -5
  33. package/dist/browser/{dist-DN5ATV5p.js → dist-CwAXjSKp.js} +3 -3
  34. package/dist/browser/{dist-p14a9te8.js → dist-oubL2fXt.js} +6 -6
  35. package/dist/browser/{efp-debug-init-DUPmVtYg.js → efp-debug-init-DxqSbETg.js} +3 -3
  36. package/dist/browser/{enum-editor-g46R4Zq7.js → enum-editor-px6vgaqK.js} +4 -4
  37. package/dist/browser/{es5-D_WEVRPo.js → es5-CH_bA74p.js} +2 -2
  38. package/dist/browser/{event-head-DBemDpob.js → event-head-CDMutSDx.js} +1 -1
  39. package/dist/browser/{event-not-found-m_QUX3nl.js → event-not-found-kZi9Tm3H.js} +3 -3
  40. package/dist/browser/{event-scoped-cache-BpjbCC59.js → event-scoped-cache-Dn2rQ3T_.js} +2 -2
  41. package/dist/browser/{expofpUrl-CAOzqFzt.js → expofpUrl-CAzeD2_k.js} +1 -1
  42. package/dist/browser/{exports-svWN4mLB.js → exports-7gqjmj0R.js} +1 -1
  43. package/dist/browser/{favicon-hR5BaEm4.js → favicon-wPU50D_T.js} +1 -1
  44. package/dist/browser/{fetch-retry.umd-DS-5XmpN.js → fetch-retry.umd-DvG22Oyz.js} +2 -2
  45. package/dist/browser/{flex-BAz1Zlx4.js → flex-BBkw01oJ.js} +5 -5
  46. package/dist/browser/{floorplan.loader-DPHmWNvd.js → floorplan.loader-DHlIL2Jh.js} +12 -12
  47. package/dist/browser/{floorplan.ready-clG372Lo.js → floorplan.ready-D6sP5Dzi.js} +344 -335
  48. package/dist/browser/framing-rect-BTvtbCvd.js +1458 -0
  49. package/dist/browser/{freeBanner-D0nU1aT0.js → freeBanner-BLieMmCe.js} +2 -2
  50. package/dist/browser/{fuse-DFf-hk6f.js → fuse-BYZURCHm.js} +2 -2
  51. package/dist/browser/{i18n-CPJmxFQX.js → i18n-UxIFjlzb.js} +6 -6
  52. package/dist/browser/index.js +17 -17
  53. package/dist/browser/{index.module-COSPv-bu.js → index.module-gZhGxKYh.js} +4 -4
  54. package/dist/browser/{init-ui-Cj38w8tb.js → init-ui-DqN9Ga2O.js} +16 -16
  55. package/dist/browser/{integrateKiosk-Du5HThkw.js → integrateKiosk-D-BA3s4g.js} +5 -5
  56. package/dist/browser/{is-debug-CmeNjnvW.js → is-debug-Bm0SoIKk.js} +4 -4
  57. package/dist/browser/{is-own-domain-z0V4JpKp.js → is-own-domain-BeUqGWxA.js} +1 -1
  58. package/dist/browser/{is-webview-JHA9dtnx.js → is-webview-D8PFcU-t.js} +1 -1
  59. package/dist/browser/{jsx-runtime-D-d_UIhH.js → jsx-runtime--7WN7ch2.js} +2 -2
  60. package/dist/browser/{kiosk-mode-ZBMSIWbK.js → kiosk-mode-XRBIqAOG.js} +3 -3
  61. package/dist/browser/{kiosk-record-Djd0ZbEF.js → kiosk-record-lQMkL50s.js} +1 -1
  62. package/dist/browser/{kioskApi-CXqDoN71.js → kioskApi-D-yj1FWd.js} +7 -7
  63. package/dist/browser/{lib-GyGKAKXf.js → lib-Bga7_H1q.js} +3 -3
  64. package/dist/browser/{loadImage-cnbZD9uN.js → loadImage-CmdsQylv.js} +1 -1
  65. package/dist/browser/{logDeviceInfo-BSf9sLzS.js → logDeviceInfo-CY7UomaF.js} +9 -9
  66. package/dist/browser/{mobx-D3NQFhEq.js → mobx-Bx4ZGHQ6.js} +4 -4
  67. package/dist/browser/{mobx.module-CAKsDOYI.js → mobx.module-Ct-ywxj5.js} +1 -1
  68. package/dist/browser/{optional-boolean-editor-IR9THgLK.js → optional-boolean-editor-DJnmDRzX.js} +3 -3
  69. package/dist/browser/{optional-enum-editor-PRQn6yzK.js → optional-enum-editor--vIuPZNm.js} +4 -4
  70. package/dist/browser/{optional-string-editor-bZCuZ1tn.js → optional-string-editor-BRFjphR8.js} +4 -4
  71. package/dist/browser/{particles.min-CwWuVwyg.js → particles.min-BnHTt56K.js} +2 -2
  72. package/dist/browser/{react-DQVTc7xG.js → react-Bjt3-m5k.js} +2 -2
  73. package/dist/browser/{react-dom-BsqqKfoG.js → react-dom-vr2-i-5R.js} +3 -3
  74. package/dist/browser/{rolldown-runtime-Cyn7iuGp.js → rolldown-runtime-Cscki9eS.js} +1 -1
  75. package/dist/browser/{scene-CqJCEbSr.js → scene-Db6sOX9h.js} +12 -12
  76. package/dist/browser/{scheduler-CVY86sNW.js → scheduler-CFHtYDYI.js} +2 -2
  77. package/dist/browser/{select-Du3T8Mpj.js → select-C9rxTFpi.js} +10 -10
  78. package/dist/browser/{settings-NB72PTux.js → settings-D7YmKLIx.js} +2 -2
  79. package/dist/browser/{settings-item-BIPeCHmD.js → settings-item-BNXaf4NN.js} +6 -6
  80. package/dist/browser/{store-BvnDl4Sc.js → store-BFM5M0Uq.js} +2 -2
  81. package/dist/browser/{string-editor-o-TMbaK8.js → string-editor-D2J_ipcW.js} +4 -4
  82. package/dist/browser/{text-field-BZ5346fV.js → text-field-DjQ_BeUk.js} +5 -5
  83. package/dist/browser/{theme-CPcxdYz7.js → theme-C5g_OQXn.js} +8 -8
  84. package/dist/browser/{ui-CesJTtUW.js → ui-642bT-_c.js} +8 -8
  85. package/dist/browser/{useRenderTarget-DpLNpx1f.js → useRenderTarget-BAEuBxcJ.js} +4 -4
  86. package/dist/browser/{viewport-QrHclV_L.js → viewport-DmBpectu.js} +3 -3
  87. package/dist/browser/{wayfinding-v9-TVwHq.js → wayfinding-FHTu4Wgf.js} +416 -383
  88. package/dist/esm/components/Kiosk/KioskStore.d.ts +5 -0
  89. package/dist/esm/components/Kiosk/KioskStore.js +1 -1
  90. package/dist/esm/components/Map/MapCamera.js +1 -1
  91. package/dist/esm/components/Map/drawing/config/TextFitter.d.ts +2 -2
  92. package/dist/esm/components/Map/drawing/config/TextFitter.js +1 -1
  93. package/dist/esm/components/Map/drawing/config/canvases.d.ts +90 -2
  94. package/dist/esm/components/Map/drawing/config/canvases.js +1 -1
  95. package/dist/esm/components/Map/drawing/config/config-all.js +1 -1
  96. package/dist/esm/components/Map/drawing/config/config-booth-badge.d.ts +3 -3
  97. package/dist/esm/components/Map/drawing/config/config-booth-bookmark.d.ts +1 -1
  98. package/dist/esm/components/Map/drawing/config/config-booth-bookmark.js +1 -1
  99. package/dist/esm/components/Map/drawing/config/config-booth-labels-special.d.ts +3 -4
  100. package/dist/esm/components/Map/drawing/config/config-booth-labels-special.js +2 -2
  101. package/dist/esm/components/Map/drawing/config/config-booth-labels.d.ts +2 -2
  102. package/dist/esm/components/Map/drawing/config/config-booth-labels.js +1 -1
  103. package/dist/esm/components/Map/drawing/config/config-markers.d.ts +17 -1
  104. package/dist/esm/components/Map/drawing/config/config-markers.js +1 -1
  105. package/dist/esm/components/Map/drawing/config/config-pins.d.ts +40 -0
  106. package/dist/esm/components/Map/drawing/config/config-pins.js +1 -0
  107. package/dist/esm/components/Map/drawing/config/pin-geometry.d.ts +65 -0
  108. package/dist/esm/components/Map/drawing/config/pin-geometry.js +1 -0
  109. package/dist/esm/components/Map/framing-rect.d.ts +16 -0
  110. package/dist/esm/components/Map/framing-rect.js +1 -0
  111. package/dist/esm/components/Map/traffic/useBuildRoute.d.ts +1 -1
  112. package/dist/esm/components/Map/traffic/useBuildRoute.js +4 -4
  113. package/dist/esm/components/Map/traffic/useManageTraffic.js +1 -1
  114. package/dist/esm/components/Map/with-kiosk.d.ts +11 -0
  115. package/dist/esm/components/Map/with-kiosk.js +1 -0
  116. package/dist/esm/components/Maplibre/useCameraReactions.js +1 -1
  117. package/dist/esm/components/Maplibre/utils/geo-config.d.ts +4 -7
  118. package/dist/esm/components/Maplibre/utils/geo-config.js +1 -1
  119. package/dist/esm/engine/defs.d.ts +5 -0
  120. package/dist/esm/engine/defs.js +3 -3
  121. package/dist/esm/engine/update-def.d.ts +13 -1
  122. package/dist/esm/engine/update-def.js +1 -1
  123. package/dist/esm/services/url-dispatch.js +1 -1
  124. package/dist/esm/store/UIState.d.ts +5 -5
  125. package/dist/esm/store/init/init-heatmap.d.ts +1 -1
  126. package/dist/esm/store/init/init-heatmap.js +1 -1
  127. package/dist/esm/store/search-buttons/SearchButtonsActions.d.ts +9 -0
  128. package/dist/esm/store/search-buttons/SearchButtonsActions.js +1 -1
  129. package/dist/esm/store/search-buttons/SearchButtonsStore.d.ts +15 -0
  130. package/dist/esm/store/search-buttons/SearchButtonsStore.js +1 -1
  131. package/dist/esm/test/store-harness.d.ts +13 -0
  132. package/dist/esm/test/store-harness.js +1 -1
  133. package/dist/esm/utils/dimming.js +1 -1
  134. package/dist/esm/wayfinding/adapters/canvasIconProvider.js +1 -1
  135. package/dist/esm/wayfinding/handlers/kioskHandler.js +1 -1
  136. package/dist/esm/wayfinding/handlers/yahHandler.js +1 -1
  137. package/package.json +10 -10
  138. package/dist/browser/dimming-B7lpw_UN.js +0 -1214
  139. package/dist/esm/engine/canvases.d.ts +0 -5
  140. package/dist/esm/engine/canvases.js +0 -1
@@ -0,0 +1,65 @@
1
+ /**
2
+ * How a count is written on a cluster marker: the count itself up to 99, and `99+` above that.
3
+ *
4
+ * Three characters is the most the counter circle holds, and the font size is chosen for three, so
5
+ * a longer label would have to be drawn smaller or clipped.
6
+ * @param count - How many points the cluster holds.
7
+ * @returns The label.
8
+ */
9
+ export declare function clusterCountText(count: number): string;
10
+ /**
11
+ * The counter circle inside a cluster pin, in CSS pixels from the pin's top-left corner. The pin
12
+ * fills its canvas, so this is also the canvas's CSS coordinate space.
13
+ * @param widthPx - The pin's width in CSS pixels.
14
+ * @returns The circle's center and radius.
15
+ */
16
+ export declare function clusterCounterCircle(widthPx: number): {
17
+ cx: number;
18
+ cy: number;
19
+ r: number;
20
+ };
21
+ /**
22
+ * The count's em height, chosen once so that `99+` — the widest label — fits inside the circle, and
23
+ * used for every count so a marker does not change size as its membership changes.
24
+ *
25
+ * The label's half-diagonal is what has to fit, not its half-width: a box inscribed in a circle
26
+ * touches at its corners.
27
+ * @param radiusPx - The counter circle's radius in CSS pixels.
28
+ * @returns The font size in CSS pixels.
29
+ */
30
+ export declare function clusterCountFontPx(radiusPx: number): number;
31
+ /**
32
+ * The teardrop's height as a fraction of its width, for every pin drawn from `createPinCanvas`.
33
+ * Here rather than in `canvases.ts`, so a pin's height is computable without loading a module that
34
+ * builds a canvas.
35
+ */
36
+ export declare const PIN_ASPECT: number;
37
+ /** The pin head's radius as a fraction of the pin's width, for the PoI pin and the cluster pin. */
38
+ export declare const PIN_HEAD_FACTOR = 0.37;
39
+ /** The edge stroke's width in CSS pixels, which `createPinCanvas` also takes as its inset. */
40
+ export declare const PIN_EDGE_PX = 1.4;
41
+ /** The counter circle's radius as a fraction of the pin head's radius. */
42
+ export declare const CLUSTER_CIRCLE_FACTOR = 0.78;
43
+ /**
44
+ * One character's advance width as a fraction of the font size, for the SDK's condensed face.
45
+ *
46
+ * An estimate, like {@link CLUSTER_COUNT_CAP_EM} and {@link CLUSTER_COUNT_FIT}, rather than a
47
+ * computation: the count is laid out by the renderer's text engine and not by a 2D context, so
48
+ * there is nothing here to call `measureText` on. Nothing automated checks the three against the
49
+ * shipped font. The renderer's `ClusterCounts` baselines draw a fixture font, so they check the
50
+ * layout, not these numbers. The real check is visual: run the app and look at a `99+` marker.
51
+ */
52
+ export declare const CLUSTER_COUNT_ADVANCE_EM = 0.58;
53
+ /**
54
+ * How far below a `top`-aligned line's top edge the digits' ink center sits, in em. Troika anchors
55
+ * the line at the block's top, ascender space included, so this lifts the glyphs from that edge onto
56
+ * the circle's center. Measured in the running app with the shipped font (device ratios 1 and 2, one
57
+ * to three characters), not half the block, which was the 0.5 it replaced. The name keeps the old
58
+ * "half block" wording. The renderer story `ClusterCounts` keeps its own value for its fixture font.
59
+ */
60
+ export declare const COUNT_BLOCK_HALF_EM = 0.61;
61
+ /** A digit's cap height as a fraction of the font size, measured the same way. */
62
+ export declare const CLUSTER_COUNT_CAP_EM = 0.72;
63
+ /** How much of the circle the label claims, leaving a visible ring of white around it. */
64
+ export declare const CLUSTER_COUNT_FIT = 0.88;
65
+ //# sourceMappingURL=pin-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ export function clusterCountText(t){return t>99?"99+":String(t)}export function clusterCounterCircle(t){const o=t*PIN_HEAD_FACTOR;return{cx:t/2,cy:o+PIN_EDGE_PX,r:o*CLUSTER_CIRCLE_FACTOR}}export function clusterCountFontPx(t){const o=3*CLUSTER_COUNT_ADVANCE_EM/2,n=CLUSTER_COUNT_CAP_EM/2;return CLUSTER_COUNT_FIT*t/Math.hypot(o,n)}export const PIN_ASPECT=93/81,PIN_HEAD_FACTOR=.37,PIN_EDGE_PX=1.4,CLUSTER_CIRCLE_FACTOR=.78,CLUSTER_COUNT_ADVANCE_EM=.58,COUNT_BLOCK_HALF_EM=.61,CLUSTER_COUNT_CAP_EM=.72,CLUSTER_COUNT_FIT=.88;
@@ -0,0 +1,16 @@
1
+ import { Box } from '@expofp/geometry';
2
+ /**
3
+ * The part of the canvas a command should frame into: what the user can see of the plan, narrowed by
4
+ * an overlay sat over it.
5
+ *
6
+ * Composed here rather than kept on the store, because nothing observes it — it is read once, by the
7
+ * command about to run. `canvasVisibleRectPx` is where the plan is *drawn* and does have observers;
8
+ * widening that would slide the plan every time a details panel opened, where this only moves where
9
+ * a `zoomTo` puts its target. The imperative renderer got the same effect by shifting the visible
10
+ * rect around each call and putting it back in a `finally`.
11
+ *
12
+ * Shared by both views, so a command frames into the same region with and without MapLibre.
13
+ * @returns The region, in css pixels from the canvas's top-left corner.
14
+ */
15
+ export declare function framingRect(): Box;
16
+ //# sourceMappingURL=framing-rect.d.ts.map
@@ -0,0 +1 @@
1
+ import{Box as r}from"@expofp/geometry";import{uiState as i}from"../../store";export function framingRect(){const t=i.canvasVisibleRectPx;return i.shouldShiftMapRect?i.rtl?r.fromMinMax(t.min,{x:t.max.x-i.overlayWidthPx,y:t.max.y}):r.fromMinMax({x:t.min.x+i.overlayWidthPx,y:t.min.y},t.max):t}
@@ -1,4 +1,4 @@
1
- import { SceneDef } from '@expofp/renderer';
1
+ import { type SceneDef } from '@expofp/renderer';
2
2
  export interface BuildRouteParams {
3
3
  enabled: boolean;
4
4
  /** The scene whose build-route layers this draws into. */
@@ -1,7 +1,7 @@
1
- import{lineIntersection as U}from"@expofp/geometry";import{update as O}from"@expofp/renderer";import{useCallback as x,useEffect as $,useMemo as S,useRef as P}from"react";import{createCircleCanvas as v,getBounds as b}from"../../../engine/canvases";import{mapCamera as K}from"../camera";import{setPlanClickListener as E}from"../pointer";import{BUILD_ROUTE_DOTS as T,BUILD_ROUTE_LINES as H}from"./trafficLayers";import{UpdateQueue as B}from"./UpdateQueue";const C=49,R=`hsl(257, 76%, ${C}%)`,Y=`hsl(339.903, 82%, ${C}%)`,J=`hsl(257, 76%, ${Math.round(C*1.82)}%)`,I=4,j=I,D="U",A="R",N="S",M="P";class q{constructor(){this._state=[[]],this.index=0,this.pointHashSet=new Set,this.locked=!1,this.storageKey="expofp_build_route_state",this._selected=0}get points(){return this._state[this.index]}set points(e){this._state.push(e),this.setIndex(),this.save()}setIndex(){this.index=Math.max(this._state.length-1,0)}genPointHash(e){return`${e.x},${e.y}`}add(e){const l=this.genPointHash(e);this.pointHashSet.has(l)||(this.pointHashSet.add(l),this.points=[...this.points,e])}undo(){this.index&&(this._state.pop(),this.setIndex(),this.save(),this.select(this.points.length-1))}execute(e){try{if(this.locked)return;this.locked=!0,e()}finally{requestAnimationFrame(()=>{this.locked=!1})}}log(e,l=12){console.log(`%c${e}`,`color: black; font-size: ${l}px; background: LemonChiffon;`)}instruct(){this.log(`How to Build a Route:
1
+ import{lineIntersection as T}from"@expofp/geometry";import{update as v}from"@expofp/renderer";import{useCallback as x,useEffect as U,useMemo as S,useRef as I}from"react";import{mapCamera as $}from"../camera";import{circleCanvasCssSize as K,createCircleCanvas as w,getBounds as b}from"../drawing/config/canvases";import{setPlanClickListener as E}from"../pointer";import{BUILD_ROUTE_DOTS as H,BUILD_ROUTE_LINES as B}from"./trafficLayers";import{UpdateQueue as Y}from"./UpdateQueue";const O=49,R=`hsl(257, 76%, ${O}%)`,z=`hsl(339.903, 82%, ${O}%)`,J=`hsl(257, 76%, ${Math.round(O*1.82)}%)`,_=4,L=K(_),Q=_,D="U",N="R",A="S",M="P";class j{constructor(){this._state=[[]],this.index=0,this.pointHashSet=new Set,this.locked=!1,this.storageKey="expofp_build_route_state",this._selected=0}get points(){return this._state[this.index]}set points(e){this._state.push(e),this.setIndex(),this.save()}setIndex(){this.index=Math.max(this._state.length-1,0)}genPointHash(e){return`${e.x},${e.y}`}add(e){const l=this.genPointHash(e);this.pointHashSet.has(l)||(this.pointHashSet.add(l),this.points=[...this.points,e])}undo(){this.index&&(this._state.pop(),this.setIndex(),this.save(),this.select(this.points.length-1))}execute(e){try{if(this.locked)return;this.locked=!0,e()}finally{requestAnimationFrame(()=>{this.locked=!1})}}log(e,l=12){console.log(`%c${e}`,`color: black; font-size: ${l}px; background: LemonChiffon;`)}instruct(){this.log(`How to Build a Route:
2
2
  Add Point: Click on the map.
3
- Apply Smoothing: Press ${A}.
4
- Apply Simplification: Press ${N}.
3
+ Apply Smoothing: Press ${N}.
4
+ Apply Simplification: Press ${A}.
5
5
  Undo: Press ${D}.
6
6
  Enable/Disable Preview: Press ${M}.
7
- `,16)}applyChaikinSmoothing(e,l,p){let o;if(p>=1&&e.length>=2){o=[e[0]];for(let h=1;h<e.length;h++){const t=e[h-1],u=e[h];for(let n=1;n<=p;n++){const r=n/(p+1);o.push({x:t.x+(u.x-t.x)*r,y:t.y+(u.y-t.y)*r})}o.push(u)}}else o=e.slice();for(let h=0;h<l&&!(o.length<2);h++){const t=[{...o[0]}];for(let u=1;u<o.length;u++){const n=o[u-1],r=o[u];t.push({x:.75*n.x+.25*r.x,y:.75*n.y+.25*r.y}),t.push({x:.25*n.x+.75*r.x,y:.25*n.y+.75*r.y})}t.push({...o[o.length-1]}),o=t}return o}applySmoothing(){this.points.length<3||(this.points=this.applyChaikinSmoothing(this.points,1,0),this.select(this.points.length-1))}save(){try{this.points.length?sessionStorage.setItem(this.storageKey,JSON.stringify(this.points)):sessionStorage.removeItem(this.storageKey)}catch(e){console.warn(e)}}restore(){try{const e=sessionStorage.getItem(this.storageKey);if(!e)return;const l=JSON.parse(e);if(!Array.isArray(l))return;l.forEach(p=>this.add(p)),this.select(this.points.length-1)}catch(e){console.warn(e)}}topologyPreservingSimplify(e,l){if(e.length<=2)return e.map(n=>({...n}));const p=(n,r,y)=>{const g=n.x,f=n.y,s=r.x,i=r.y,c=y.x,a=y.y,d=Math.abs((a-i)*g-(c-s)*f+c*i-a*s),m=Math.sqrt((a-i)**2+(c-s)**2);return d/(m||1e-10)},o=(n,r,y,g)=>{const f=U({p0:n,p1:r},{p0:y,p1:g});return f!==null&&f.onLine1&&f.onLine2},h=new Array(e.length).fill(!1);h[0]=h[h.length-1]=!0;const t=[],u=(n,r)=>{if(r<=n+1)return;const y=e[n],g=e[r];let f=0,s=0;for(let i=n+1;i<r;i++){const c=p(e[i],y,g);c>f&&(f=c,s=i)}if(f>l){const i=e[s];let c=!0;for(let a=0;a<t.length-1;a++)if(o(t[a],t[a+1],y,i)||o(t[a],t[a+1],i,g)){c=!1;break}c&&(h[s]=!0,u(n,s),u(s,r))}};return u(0,e.length-1),e.filter((n,r)=>h[r])}applySimplification(){this.points=this.topologyPreservingSimplify(this.points,.5),this.select(this.points.length-1)}get selected(){return this._selected}select(e){this._selected=Math.max(0,Math.min(e,this.points.length-1))}}export function useBuildRoute({enabled:_,scene:e}){const l=P(!1),p=P(),o=S(()=>v(I,R),[]),h=S(()=>v(I,Y),[]),t=S(()=>new q,[]),u=P(new B),n=S(()=>{if(!e)return{};const s=e.rootLayer.children,i=s.find(a=>a.name===H),c=s.find(a=>a.name===T);return{linesLayer:i,pointsLayer:c}},[e]),r=x(()=>{if(!n||!o||!h)return;const{linesLayer:s,pointsLayer:i}=n;if(!s||!i)return;const c=t.points,a=l.current;a?i.children.length=0:i.children=c.map(({x:m,y:L},k)=>{const w=k==t.selected?h:o;return{source:w,bounds:b(m,L,w.width,w.height,p.current)}});const d=[];for(let m=1;m<c.length;m++){const L=c[m-1],k=c[m];d.push({points:{p0:L,p1:k},color:a?R:J,width:j})}d.length?s.children=d:s.children.length=0,u.current.add(()=>O(i,s)),t.instruct(),t.log(JSON.stringify(t.points))},[t,n,o,h]),y=x(s=>{const i=s.key.toUpperCase();t.execute(()=>{switch(i){case D:s.preventDefault(),t.undo(),l.current=!1,r();break;case A:s.preventDefault(),t.applySmoothing(),l.current=!1,r();break;case N:s.preventDefault(),t.applySimplification(),l.current=!1,r();break;case M:s.preventDefault(),l.current=!l.current,r();break}})},[t,r]),g=x(s=>{p.current=s;const{pointsLayer:i}=n;if(!i)return[];const c=[];return t.execute(()=>{const a=i.children;for(const d of a)d.bounds=b(d.bounds.center.x,d.bounds.center.y,d.source.width,d.source.height,p.current),c.push(d)}),c},[n,t]),f=x(s=>{t.execute(()=>{t.add({x:s.x,y:s.y}),t.select(t.points.length-1),r()})},[t,r]);$(()=>{if(!_||!n?.pointsLayer)return;const{pointsLayer:s}=n;return p.current=K()?.state().pixelSize??1,t.execute(()=>{t.restore(),r()}),s.onScale=({pixelSize:i})=>g(i),u.current.add(()=>O(s)),E(f),window.addEventListener("keyup",y),()=>{window.removeEventListener("keyup",y),E(),s.onScale=void 0,u.current.destroy()}},[_,n,t,r,f,g,y])}
7
+ `,16)}applyChaikinSmoothing(e,l,p){let c;if(p>=1&&e.length>=2){c=[e[0]];for(let h=1;h<e.length;h++){const t=e[h-1],u=e[h];for(let n=1;n<=p;n++){const r=n/(p+1);c.push({x:t.x+(u.x-t.x)*r,y:t.y+(u.y-t.y)*r})}c.push(u)}}else c=e.slice();for(let h=0;h<l&&!(c.length<2);h++){const t=[{...c[0]}];for(let u=1;u<c.length;u++){const n=c[u-1],r=c[u];t.push({x:.75*n.x+.25*r.x,y:.75*n.y+.25*r.y}),t.push({x:.25*n.x+.75*r.x,y:.25*n.y+.75*r.y})}t.push({...c[c.length-1]}),c=t}return c}applySmoothing(){this.points.length<3||(this.points=this.applyChaikinSmoothing(this.points,1,0),this.select(this.points.length-1))}save(){try{this.points.length?sessionStorage.setItem(this.storageKey,JSON.stringify(this.points)):sessionStorage.removeItem(this.storageKey)}catch(e){console.warn(e)}}restore(){try{const e=sessionStorage.getItem(this.storageKey);if(!e)return;const l=JSON.parse(e);if(!Array.isArray(l))return;l.forEach(p=>this.add(p)),this.select(this.points.length-1)}catch(e){console.warn(e)}}topologyPreservingSimplify(e,l){if(e.length<=2)return e.map(n=>({...n}));const p=(n,r,d)=>{const g=n.x,f=n.y,s=r.x,i=r.y,o=d.x,a=d.y,y=Math.abs((a-i)*g-(o-s)*f+o*i-a*s),m=Math.sqrt((a-i)**2+(o-s)**2);return y/(m||1e-10)},c=(n,r,d,g)=>{const f=T({p0:n,p1:r},{p0:d,p1:g});return f!==null&&f.onLine1&&f.onLine2},h=new Array(e.length).fill(!1);h[0]=h[h.length-1]=!0;const t=[],u=(n,r)=>{if(r<=n+1)return;const d=e[n],g=e[r];let f=0,s=0;for(let i=n+1;i<r;i++){const o=p(e[i],d,g);o>f&&(f=o,s=i)}if(f>l){const i=e[s];let o=!0;for(let a=0;a<t.length-1;a++)if(c(t[a],t[a+1],d,i)||c(t[a],t[a+1],i,g)){o=!1;break}o&&(h[s]=!0,u(n,s),u(s,r))}};return u(0,e.length-1),e.filter((n,r)=>h[r])}applySimplification(){this.points=this.topologyPreservingSimplify(this.points,.5),this.select(this.points.length-1)}get selected(){return this._selected}select(e){this._selected=Math.max(0,Math.min(e,this.points.length-1))}}export function useBuildRoute({enabled:k,scene:e}){const l=I(!1),p=I(1),c=S(()=>w(_,R),[]),h=S(()=>w(_,z),[]),t=S(()=>new j,[]),u=I(new Y),n=S(()=>{if(!e)return{};const s=e.rootLayer.children,i=s.find(a=>a.name===B),o=s.find(a=>a.name===H);return{linesLayer:i,pointsLayer:o}},[e]),r=x(()=>{if(!n||!c||!h)return;const{linesLayer:s,pointsLayer:i}=n;if(!s||!i)return;const o=t.points,a=l.current;a?i.children.length=0:i.children=o.map(({x:m,y:C},P)=>({source:P==t.selected?h:c,bounds:b(m,C,L,L,p.current)}));const y=[];for(let m=1;m<o.length;m++){const C=o[m-1],P=o[m];y.push({points:{p0:C,p1:P},color:a?R:J,width:Q})}y.length?s.children=y:s.children.length=0,u.current.add(()=>v(i,s)),t.instruct(),t.log(JSON.stringify(t.points))},[t,n,c,h]),d=x(s=>{const i=s.key.toUpperCase();t.execute(()=>{switch(i){case D:s.preventDefault(),t.undo(),l.current=!1,r();break;case N:s.preventDefault(),t.applySmoothing(),l.current=!1,r();break;case A:s.preventDefault(),t.applySimplification(),l.current=!1,r();break;case M:s.preventDefault(),l.current=!l.current,r();break}})},[t,r]),g=x(s=>{p.current=s;const{pointsLayer:i}=n;if(!i)return[];const o=[];return t.execute(()=>{const a=i.children;for(const y of a)y.bounds=b(y.bounds.center.x,y.bounds.center.y,L,L,p.current),o.push(y)}),o},[n,t]),f=x(s=>{t.execute(()=>{t.add({x:s.x,y:s.y}),t.select(t.points.length-1),r()})},[t,r]);U(()=>{if(!k||!n?.pointsLayer)return;const{pointsLayer:s}=n;p.current=$()?.state().pixelSize??1,t.execute(()=>{t.restore(),r()}),s.onScale=({pixelSize:o})=>g(o);const i=u.current;return i.add(()=>v(s)),E(f),window.addEventListener("keyup",d),()=>{window.removeEventListener("keyup",d),E(),s.onScale=void 0,i.destroy()}},[k,n,t,r,f,g,d])}
@@ -1 +1 @@
1
- import{degToRad as Me,lineAngle as ae,pointDistance as le}from"@expofp/geometry";import{update as Y}from"@expofp/renderer";import{useEffect as ve,useRef as v}from"react";import{getBounds as z}from"../../../engine/canvases";import Le from"../../../tools/base-runtime-url";import{loadImage as Re}from"../../../utils/loadImage";import{mapCamera as ue}from"../camera";import{createImageCanvas as Se}from"../drawing/config/canvases";import{getRouteLayerRegex as be,getRouteStopLayerRegex as Te,TRAFFIC_VEHICLES as te}from"./trafficLayers";import{UpdateQueue as Ae}from"./UpdateQueue";const ne=2,Fe=ne*3.5,Z=ne*12,de=Z*.7,Pe=8,Ce="transport_locations",_e=150,Ne="#a4aab6",fe=1e3,De=2e4,he=500;export function useManageTraffic({permission:t,scene:r,dataJsonUrl:n,websocketUrl:l}){const e=v(window.devicePixelRatio),s=v(new Map),o=v(new Map),c=v(null),f=v(null),d=v(0),M=v(1),T=v(new Ae),L=v(new Map),x=v(fe),h=v(new Map),p=v(void 0);ve(()=>{if(!t||!r)return;let a=!1,C;const R=r.rootLayer.children,g=o.current,y=s.current,I=T.current;async function ye(){try{let u;try{const i=await fetch(n,{method:"GET",headers:{"Content-Type":"application/json"}});if(!i.ok||i.status===204)return;u=await i.json()}catch(i){console.warn("useManageTraffic failed to get JSON",i)}if(a||!u||u.disabled||u.error)return;e.current=ge(ue()?.state().pixelSize??window.devicePixelRatio);const $=new Map(u.routes.map(({id:i,color:m})=>[i,m])),A=new Map;$.forEach(i=>{A.set(i,Ve({radius:Fe,color:i,scale:window.devicePixelRatio}))});const U=be(),j=R.filter(i=>U.test(i.name)),Q=Te(),q=R.filter(i=>Q.test(i.name));u.routes.slice(0,j.length).forEach(({id:i,color:m,path:w,stops:_,hiddenPoints:S},V)=>{L.current.set(i,w);const E=j[V];if(E){const H=new Map(S?.map(N=>[ce(N),N])??[]);for(let N=1;N<w.length;N++){const b=w[N-1],k=w[N];H.has(ce(b))||H.has(ce(k))||E.children.push({points:{p0:b,p1:k},color:m,width:ne})}E.children.length&&I.add(()=>Y(E))}const F=q[V],P=A.get(m);F&&P&&_?.length&&(_.forEach(H=>{F.children.push({source:P,bounds:z(H.x,H.y,P.width,P.height,e.current)})}),F.children.length&&I.add(()=>Y(F)))});const O=R.find(i=>i.name===te);if(!O){console.warn(`useManageTraffic: the scene has no "${te}" layer`);return}const B=new Map;O.onScale=({pixelSize:i})=>{e.current=ge(i);const m=[];for(const w of q){const _=w.children;for(const S of _){const V=S.source;S.bounds=z(S.bounds.center.x,S.bounds.center.y,V.width,V.height,e.current,S.bounds.rotation),m.push(S)}}for(const w of s.current.values()){const _=w.source;w.bounds=z(w.bounds.center.x,w.bounds.center.y,_.width,_.height,e.current,w.bounds.rotation),m.push(w)}return m},I.add(()=>Y(O)),$.forEach((i,m)=>{B.set(m,me({id:m,width:Z,height:Z,color:i,scale:window.devicePixelRatio}))});const xe=me({id:"orphan",width:Z,height:Z,color:Ne,scale:window.devicePixelRatio});let G=null;try{const i=await Re(`${Le}icons/bus-white.svg`);if(a)return;G=Se(i,de,de,window.devicePixelRatio)}catch(i){console.error(i)}f.current=i=>{try{const m=JSON.parse(i.data);if(m.type!==Ce)return;const w=i.timeStamp||performance.now();M.current=Math.round(w-d.current),d.current=w;const _=Math.max(M.current??he,he),S=O.children;let V=!1;m.data.forEach(({id:E,x:F,y:P,route_id:H,hidden:N})=>{g.has(E)&&(g.get(E)?.(),g.delete(E));const b=y.get(E),k=y.get(re(E))??null;if(N&&b&&k){b.hidden=!0,k.hidden=!0,I.add(()=>Y(b,k));return}if(h.current.set(E,performance.now()),!b){const W=B.get(H)??xe,D={source:W,bounds:z(F,P,W.width,W.height,e.current,0)};if(y.set(E,D),G){const X=re(E),ie={source:G,bounds:z(F,P,G.width,G.height,e.current,0)};G.setAttribute("id",X),y.set(X,ie),S.push(D,ie)}else S.push(D);V=!0;return}const Ee=b.bounds.center,ee=ke(Ee,{x:F,y:P},L.current.get(H));if(ee.reduce((W,D,X)=>X===0?W:W+oe(ee[X-1],D),0)>=_e||document.hidden)we({updateQueue:I,vehicle:b,vehicleFinery:k,x:F,y:P,scale:e.current,angle:Oe(b.bounds.center,{x:F,y:P})});else{const W=We(ee,_,D=>{we({updateQueue:I,vehicle:b,vehicleFinery:k,x:D.x,y:D.y,scale:e.current,angle:D.angle})});g.set(E,W)}}),V&&I.add(()=>Y(O))}catch(m){console.error("handleSocketMessage",m)}},d.current=performance.now(),K(),window.addEventListener("online",se),Ie(u.config?(Number(u.config.vehicle_inactive_interval)||20)*1e3:De)}catch(u){console.warn(u)}}ye();function K(){a||c.current?.readyState===WebSocket.OPEN||c.current?.readyState===WebSocket.CONNECTING||(c.current=new WebSocket(l),c.current.onmessage=f.current,c.current.onopen=()=>{x.current=fe},c.current.onclose=()=>{if(a||!navigator.onLine)return;C=window.setTimeout(K,x.current);const u=3e4;x.current=Math.min(x.current*2,u)})}function se(){K()}function Ie(u){clearInterval(p.current),!a&&(p.current=window.setInterval(()=>{const $=performance.now(),A=[],U=[];h.current.forEach((j,Q)=>{if($-j<u)return;const q=y.get(Q);q&&(q.hidden=!0,U.push(q),A.push(Q));const O=re(Q),B=y.get(O);B&&(B.hidden=!0,U.push(B),A.push(O))}),U.length&&I.add(()=>Y(...U)),A.forEach(j=>h.current.delete(j))},u))}return()=>{a=!0,clearTimeout(C);const u=c.current;u&&(u.onopen=null,u.onmessage=null,u.onclose=null,u.close(),c.current=null),f.current=null;const $=R.find(A=>A.name===te);$&&($.onScale=void 0),g.forEach(A=>A?.()),g.clear(),y.clear(),I.destroy(),window.removeEventListener("online",se),clearInterval(p.current)}},[t,r,n,l])}function ge(t){return Math.min(t||window.devicePixelRatio,Pe)}function Oe(t,r){return ae(t,r)}function Ve({radius:t,scale:r,color:n}){const l=document.createElement("canvas"),e=l.getContext("2d");e.imageSmoothingEnabled=!1;const s=Math.ceil(t*2*r),o=s/2,c=t*.78,f=t*.33;return l.width=s,l.height=s,e.translate(o,o),e.scale(r,r),e.beginPath(),e.arc(0,0,t,0,2*Math.PI),e.fillStyle="#FFFFFF",e.fill(),e.beginPath(),e.arc(0,0,c,0,2*Math.PI),e.fillStyle=n,e.fill(),e.beginPath(),e.arc(0,0,f,0,2*Math.PI),e.fillStyle="#FFFFFF",e.fill(),l}function me({id:t,width:r,height:n,color:l,scale:e}){const s=2*e,o=r/2*e,c=n*e,f=.6,d=.01,M=c*f,T=c,L=8*e,x=T+s+M/2+L*2,h=o*2+M+s+L*2,p=document.createElement("canvas");p.setAttribute("id",t),p.width=x,p.height=h;const a=p.getContext("2d"),C=x/2,R=h/2;a.save(),a.translate(C,R),a.rotate(-Math.PI/2),a.translate(-C,-R);const g=C,y=R-T/2+o,I=y+M;return a.shadowColor="rgba(16, 24, 40, 0.2)",a.shadowBlur=L/2,a.beginPath(),a.arc(g,y,o,Math.PI,0,!1),a.bezierCurveTo(g+o,y+c*.3,g+d*o,I,g,I),a.bezierCurveTo(g-d*o,I,g-o,y+c*.3,g-o,y),a.closePath(),a.fillStyle=l,a.fill(),a.strokeStyle="#FFFFFF",a.lineWidth=s,a.stroke(),a.restore(),p}function oe(t,r){return le(t,r)}function re(t){return`${t}_finery`}function we({updateQueue:t,vehicle:r,vehicleFinery:n,x:l,y:e,scale:s,angle:o}){const c=r.source;if(r.bounds=z(l,e,c.width,c.height,s,o),r.hidden=!1,n){const f=ue()?.state().roll??0,d=n.source;n.bounds=z(l,e,d.width,d.height,s,Me(f)),n.hidden=!1,t.add(()=>Y(n,r))}else t.add(()=>Y(r))}function J(t,r,n){return{x:t.x+(r.x-t.x)*n,y:t.y+(r.y-t.y)*n}}function pe(t,r){let n=1/0,l={index:0,t:0};for(let e=0;e<r.length-1;e++){const s=r[e],o=r[e+1],c=o.x-s.x,f=o.y-s.y,d=c*c+f*f,M=d?((t.x-s.x)*c+(t.y-s.y)*f)/d:0,T=Math.max(0,Math.min(1,M)),L=J(s,o,T),x=oe(t,L);x<n&&(n=x,l={index:e,t:T})}return l}function He(t,r,n){const l=oe(t,r),e=Math.max(2,Math.ceil(l/n)+1),s=[];for(let o=0;o<e;o++){const c=o/(e-1),f=J(t,r,c);s.push(f)}return s}function ke(t,r,n){if(!n||!n.length)return He(t,r,10);const l=pe(t,n),e=pe(r,n),s=[],o=c=>s.push(c);l.index+1<n.length?o(J(n[l.index],n[l.index+1],l.t)):o(n[l.index]);for(let c=l.index+1;c<=e.index&&c<n.length;c++)o(n[c]);return e.index+1<n.length?o(J(n[e.index],n[e.index+1],e.t)):o(n[e.index]),s}function We(t,r,n,l){if(t.length<2)return()=>{};let e=null,s;const o=[];let c=0;for(let d=0;d<t.length-1;d++){const M=le(t[d],t[d+1]);o.push(M),c+=M}const f=d=>{e||(e=d);const M=d-e,T=Math.min(M/r,1),L=T*c;let x=0,h=0;for(;h<o.length&&x+o[h]<L;)x+=o[h],h++;h>=o.length&&(h=o.length-1);const p=t[h],a=t[Math.min(h+1,t.length-1)],C=o[h]||0,R=C===0?0:(L-x)/C,g=p.x+(a.x-p.x)*R,y=p.y+(a.y-p.y)*R,I=ae(p,a);n({x:g,y,angle:I}),T<1?s=requestAnimationFrame(f):l&&l()};return s=requestAnimationFrame(f),()=>{s&&cancelAnimationFrame(s)}}function ce(t){return`${t.x},${t.y}`}
1
+ import{degToRad as Ce,lineAngle as fe,pointDistance as he}from"@expofp/geometry";import{update as W}from"@expofp/renderer";import{useEffect as ve,useRef as S}from"react";import _e from"../../../tools/base-runtime-url";import{loadImage as Fe}from"../../../utils/loadImage";import{mapCamera as ge}from"../camera";import{createCanvas as me,createImageCanvas as Pe,getBounds as Y,imageCanvasCssSize as Ne}from"../drawing/config/canvases";import{getRouteLayerRegex as De,getRouteStopLayerRegex as Oe,TRAFFIC_VEHICLES as re}from"./trafficLayers";import{UpdateQueue as He}from"./UpdateQueue";const ce=2,Ve=ce*3.5,J=ce*12,K=J*.7,We=8,se=2,pe=.6,we=8,ze="transport_locations",ke=150,Ge="#a4aab6",Ie=1e3,Ye=2e4,ye=500;export function useManageTraffic({permission:e,scene:n,dataJsonUrl:r,websocketUrl:a}){const t=S(1),o=S(new Map),c=S(new Map),s=S(null),f=S(null),d=S(0),M=S(1),b=S(new He),T=S(new Map),y=S(Ie),m=S(new Map),E=S(void 0);ve(()=>{if(!e||!n)return;let l=!1,_;const R=n.rootLayer.children,p=c.current,w=o.current,I=b.current;async function Re(){try{let u;try{const i=await fetch(r,{method:"GET",headers:{"Content-Type":"application/json"}});if(!i.ok||i.status===204)return;u=await i.json()}catch(i){console.warn("useManageTraffic failed to get JSON",i)}if(l||!u||u.disabled||u.error)return;t.current=xe(ge()?.state().pixelSize??1);const z=new Map(u.routes.map(({id:i,color:h})=>[i,h])),C=new Map;z.forEach(i=>{C.set(i,je({radius:Ve,color:i,scale:window.devicePixelRatio}))});const U=De(),j=R.filter(i=>U.test(i.name)),X=Oe(),q=R.filter(i=>X.test(i.name));u.routes.slice(0,j.length).forEach(({id:i,color:h,path:g,stops:O,hiddenPoints:L},k)=>{T.current.set(i,g);const x=j[k];if(x){const H=new Map(L?.map(P=>[le(P),P])??[]);for(let P=1;P<g.length;P++){const A=g[P-1],V=g[P];H.has(le(A))||H.has(le(V))||x.children.push({points:{p0:A,p1:V},color:h,width:ce})}x.children.length&&I.add(()=>W(x))}const v=q[k],F=C.get(h);v&&F&&O?.length&&(O.forEach(H=>{v.children.push({source:F,bounds:Y(H.x,H.y,...$(F),t.current)})}),v.children.length&&I.add(()=>W(v)))});const D=R.find(i=>i.name===re);if(!D){console.warn(`useManageTraffic: the scene has no "${re}" layer`);return}const B=new Map;D.onScale=({pixelSize:i})=>{t.current=xe(i);const h=[];for(const g of q){const O=g.children;for(const L of O){const k=L.source;L.bounds=Y(L.bounds.center.x,L.bounds.center.y,...$(k),t.current,L.bounds.rotation),h.push(L)}}for(const g of o.current.values()){const O=g.source;g.bounds=Y(g.bounds.center.x,g.bounds.center.y,...$(O),t.current,g.bounds.rotation),h.push(g)}return h},I.add(()=>W(D)),z.forEach((i,h)=>{B.set(h,Me({id:h,width:J,height:J,color:i,scale:window.devicePixelRatio}))});const Ae=Me({id:"orphan",width:J,height:J,color:Ge,scale:window.devicePixelRatio});let Q=null;try{const i=await Fe(`${_e}icons/bus-white.svg`);if(l)return;Q=Pe(i,K,K,window.devicePixelRatio);const{width:h,height:g}=Ne(i,K,K);ne.set(Q,[h,g])}catch(i){console.error(i)}f.current=i=>{try{const h=JSON.parse(i.data);if(h.type!==ze)return;const g=i.timeStamp||performance.now();M.current=Math.round(g-d.current),d.current=g;const O=Math.max(M.current??ye,ye),L=D.children;let k=!1;h.data.forEach(({id:x,x:v,y:F,route_id:H,hidden:P})=>{p.has(x)&&(p.get(x)?.(),p.delete(x));const A=w.get(x),V=w.get(ae(x))??null;if(P&&A&&V){A.hidden=!0,V.hidden=!0,I.add(()=>W(A,V));return}if(m.current.set(x,performance.now()),!A){const G=B.get(H)??Ae,N={source:G,bounds:Y(v,F,...$(G),t.current,0)};if(w.set(x,N),Q){const Z=ae(x),de={source:Q,bounds:Y(v,F,...$(Q),t.current,0)};Q.setAttribute("id",Z),w.set(Z,de),L.push(N,de)}else L.push(N);k=!0;return}const be=A.bounds.center,oe=Be(be,{x:v,y:F},T.current.get(H));if(oe.reduce((G,N,Z)=>Z===0?G:G+ie(oe[Z-1],N),0)>=ke||document.hidden)Se({updateQueue:I,vehicle:A,vehicleFinery:V,x:v,y:F,scale:t.current,angle:Ue(A.bounds.center,{x:v,y:F})});else{const G=Qe(oe,O,N=>{Se({updateQueue:I,vehicle:A,vehicleFinery:V,x:N.x,y:N.y,scale:t.current,angle:N.angle})});p.set(x,G)}}),k&&I.add(()=>W(D))}catch(h){console.error("handleSocketMessage",h)}},d.current=performance.now(),te(),window.addEventListener("online",ue),Le(u.config?(Number(u.config.vehicle_inactive_interval)||20)*1e3:Ye)}catch(u){console.warn(u)}}Re();function te(){l||s.current?.readyState===WebSocket.OPEN||s.current?.readyState===WebSocket.CONNECTING||(s.current=new WebSocket(a),s.current.onmessage=f.current,s.current.onopen=()=>{y.current=Ie},s.current.onclose=()=>{if(l||!navigator.onLine)return;_=window.setTimeout(te,y.current);const u=3e4;y.current=Math.min(y.current*2,u)})}function ue(){te()}function Le(u){clearInterval(E.current),!l&&(E.current=window.setInterval(()=>{const z=performance.now(),C=[],U=[];m.current.forEach((j,X)=>{if(z-j<u)return;const q=w.get(X);q&&(q.hidden=!0,U.push(q),C.push(X));const D=ae(X),B=w.get(D);B&&(B.hidden=!0,U.push(B),C.push(D))}),U.length&&I.add(()=>W(...U)),C.forEach(j=>m.current.delete(j))},u))}return()=>{l=!0,clearTimeout(_);const u=s.current;u&&(u.onopen=null,u.onmessage=null,u.onclose=null,u.close(),s.current=null),f.current=null;const z=R.find(C=>C.name===re);z&&(z.onScale=void 0),p.forEach(C=>C?.()),p.clear(),w.clear(),I.destroy(),window.removeEventListener("online",ue),clearInterval(E.current)}},[e,n,r,a])}function $(e){const n=ne.get(e);if(!n)throw new Error("useManageTraffic: an icon was drawn without a recorded CSS size");return n}function Ee(e){return e*2}function $e(e,n){const r=n*pe,a=we*2;return[n+se+r/2+a,e+r+se+a]}function xe(e){return Math.min(e||1,We)}function Ue(e,n){return fe(e,n)}function je({radius:e,scale:n,color:r}){const a=Math.ceil(e*2*n),t=me(a,a,n);ne.set(t,[Ee(e),Ee(e)]);const o=t.getContext("2d");o.imageSmoothingEnabled=!1;const c=a/2,s=e*.78,f=e*.33;return o.translate(c,c),o.scale(n,n),o.beginPath(),o.arc(0,0,e,0,2*Math.PI),o.fillStyle="#FFFFFF",o.fill(),o.beginPath(),o.arc(0,0,s,0,2*Math.PI),o.fillStyle=r,o.fill(),o.beginPath(),o.arc(0,0,f,0,2*Math.PI),o.fillStyle="#FFFFFF",o.fill(),t}function Me({id:e,width:n,height:r,color:a,scale:t}){const o=se*t,c=n/2*t,s=r*t,f=pe,d=.01,M=s*f,b=s,T=we*t,y=b+o+M/2+T*2,m=c*2+M+o+T*2,E=me(y,m,t);ne.set(E,$e(n,r)),E.setAttribute("id",e);const l=E.getContext("2d"),_=y/2,R=m/2;l.save(),l.translate(_,R),l.rotate(-Math.PI/2),l.translate(-_,-R);const p=_,w=R-b/2+c,I=w+M;return l.shadowColor="rgba(16, 24, 40, 0.2)",l.shadowBlur=T/2,l.beginPath(),l.arc(p,w,c,Math.PI,0,!1),l.bezierCurveTo(p+c,w+s*.3,p+d*c,I,p,I),l.bezierCurveTo(p-d*c,I,p-c,w+s*.3,p-c,w),l.closePath(),l.fillStyle=a,l.fill(),l.strokeStyle="#FFFFFF",l.lineWidth=o,l.stroke(),l.restore(),E}function ie(e,n){return he(e,n)}function ae(e){return`${e}_finery`}function Se({updateQueue:e,vehicle:n,vehicleFinery:r,x:a,y:t,scale:o,angle:c}){const s=n.source;if(n.bounds=Y(a,t,...$(s),o,c),n.hidden=!1,r){const f=ge()?.state().roll??0,d=r.source;r.bounds=Y(a,t,...$(d),o,Ce(f)),r.hidden=!1,e.add(()=>W(r,n))}else e.add(()=>W(n))}function ee(e,n,r){return{x:e.x+(n.x-e.x)*r,y:e.y+(n.y-e.y)*r}}function Te(e,n){let r=1/0,a={index:0,t:0};for(let t=0;t<n.length-1;t++){const o=n[t],c=n[t+1],s=c.x-o.x,f=c.y-o.y,d=s*s+f*f,M=d?((e.x-o.x)*s+(e.y-o.y)*f)/d:0,b=Math.max(0,Math.min(1,M)),T=ee(o,c,b),y=ie(e,T);y<r&&(r=y,a={index:t,t:b})}return a}function qe(e,n,r){const a=ie(e,n),t=Math.max(2,Math.ceil(a/r)+1),o=[];for(let c=0;c<t;c++){const s=c/(t-1),f=ee(e,n,s);o.push(f)}return o}function Be(e,n,r){if(!r||!r.length)return qe(e,n,10);const a=Te(e,r),t=Te(n,r),o=[],c=s=>o.push(s);a.index+1<r.length?c(ee(r[a.index],r[a.index+1],a.t)):c(r[a.index]);for(let s=a.index+1;s<=t.index&&s<r.length;s++)c(r[s]);return t.index+1<r.length?c(ee(r[t.index],r[t.index+1],t.t)):c(r[t.index]),o}function Qe(e,n,r,a){if(e.length<2)return()=>{};let t=null,o;const c=[];let s=0;for(let d=0;d<e.length-1;d++){const M=he(e[d],e[d+1]);c.push(M),s+=M}const f=d=>{t||(t=d);const M=d-t,b=Math.min(M/n,1),T=b*s;let y=0,m=0;for(;m<c.length&&y+c[m]<T;)y+=c[m],m++;m>=c.length&&(m=c.length-1);const E=e[m],l=e[Math.min(m+1,e.length-1)],_=c[m]||0,R=_===0?0:(T-y)/_,p=E.x+(l.x-E.x)*R,w=E.y+(l.y-E.y)*R,I=fe(E,l);r({x:p,y:w,angle:I}),b<1?o=requestAnimationFrame(f):a&&a()};return o=requestAnimationFrame(f),()=>{o&&cancelAnimationFrame(o)}}function le(e){return`${e.x},${e.y}`}const ne=new WeakMap;
@@ -0,0 +1,11 @@
1
+ import { Box } from '@expofp/geometry';
2
+ /**
3
+ * The box to frame, widened to keep the kiosk in view when it stands on the floor being shown.
4
+ *
5
+ * Shared by both views, like `framingRect()`: the route's own bounds stop at the walkway nearest
6
+ * the kiosk, so without this the kiosk and the trail leading from it fall outside the frame.
7
+ * @param rect - What was asked for, in plan units.
8
+ * @returns The box to frame.
9
+ */
10
+ export declare function withKiosk(rect: Box): Box;
11
+ //# sourceMappingURL=with-kiosk.d.ts.map
@@ -0,0 +1 @@
1
+ import{Box as t}from"@expofp/geometry";import e,{uiState as n}from"../../store";import{areLayersEnabled as i}from"../../utils/areLayersEnabled.js";export function withKiosk(r){const o=n.kioskData;return!o||!(i()?o.z===e.routeStore.currentRouteLayer?.name:!0)?r:t.fromUnion(r,new t(o.x,o.y,1,1))}
@@ -1 +1 @@
1
- import{Box as h}from"@expofp/geometry";import{getSvgArea as f,isSvgAreaExplicit as v}from"../../data/svg.js";import u,{uiState as s}from"../../store";import{convertLocalToGps as T}from"../../utils/gps";import{useReaction as l}from"../../utils/mobx";import{expandRectByPercent as y,getVenueFitMarginPercent as b,resolveVenueFitRect as S}from"../../utils/scene-bounds";import{fitMapToLocalRect as d,getVisibleRectFitPadding as x,isLngLatInVisibleViewport as O}from"./utils/geo-config";const a=s,g=15,M=24,L=20;function R(e){return y(e,b(e,f())).expand(L)}function B(e,r){return Math.min(Math.max(r,e.getMinZoom()),e.getMaxZoom())}function I(e,r,m){if(m.length!==1)return!1;const o=m[0]?.id,t=u.poiMarkerStore.markers.find(c=>c.boothId===o);if(!t)return!1;const n=T(t.center.x,t.center.y,r.gpsConfig),i=x(M);return O(e,n,i)||e.easeTo({center:n,zoom:B(e,g),bearing:r.bearing,duration:500,essential:!0}),!0}export function useCameraReactions(e,r,m){l(()=>a.moveToBooths,o=>{const t=e.current;if(!o||!t||!r)return;const n=o.filter(c=>c.rect);if(n.length===0)return;if(I(t,r,n)){a.moveToBooths=null;return}const i=h.fromUnion(...n.map(c=>c.rect));d(t,R(i),r),a.moveToBooths=null}),l(()=>a.moveToRect,o=>{const t=e.current;!o||!t||!r||(d(t,o,r,{paddingPercent:a.moveToRectPaddingPercent}),a.moveToRect=null,a.moveToRectPaddingPercent=null)}),l(()=>a.zoomBy,o=>{const t=e.current;!o||!t||(t.flyTo({zoom:t.getZoom()+(o>1?.5:-.5),animate:!0,duration:500}),a.zoomBy=null)}),l(()=>[s.moveToLocation,u.maplibreStore.cameraPort,u.routeStore.currentPosition],([o])=>{if(!o)return;const t=e.current,n=u.routeStore.currentPosition;if(!n){s.moveToLocation=!1;return}if(!t)return;const{lng:i,lat:c}=n,P=typeof i=="number"&&Number.isFinite(i)&&typeof c=="number"&&Number.isFinite(c);s.moveToLocation=!1;let p;if(P)p=[i,c];else{if(!r)return;p=T(n.x,n.y,r.gpsConfig)}t.flyTo({center:p,animate:!0,duration:1e3})}),l(()=>s.centerMap,o=>{const t=e.current;if(!o||!t||!r)return;if(!(s.list.type==="search"&&!s.list.text)){const{rect:i}=S({layerViewbox:u.layerStore.viewboxRectangle,explicitArea:v()?f():null,layerRectangle:u.layerStore.rectangle,sceneDef:m,svgArea:f()});d(t,i,r)}s.centerMap=!1})}
1
+ import{Box as b}from"@expofp/geometry";import{getSvgArea as d,isSvgAreaExplicit as g}from"../../data/svg.js";import u,{uiState as s}from"../../store";import{convertLocalToGps as P}from"../../utils/gps";import{useReaction as m}from"../../utils/mobx";import{expandRectByPercent as y,getVenueFitMarginPercent as T,resolveVenueFitRect as S}from"../../utils/scene-bounds";import{withKiosk as x}from"../Map/with-kiosk.js";import{fitMapToLocalRect as h,getVisibleRectFitPadding as O,isLngLatInVisibleViewport as v}from"./utils/geo-config";const a=s,M=15,L=24,B=20;function I(r){return y(r,T(r,d())).expand(B)}function R(r,o){return Math.min(Math.max(o,r.getMinZoom()),r.getMaxZoom())}function _(r,o,f){if(f.length!==1)return!1;const e=f[0]?.id,t=u.poiMarkerStore.markers.find(c=>c.boothId===e);if(!t)return!1;const n=P(t.center.x,t.center.y,o.gpsConfig),i=O(L);return v(r,n,i)||r.easeTo({center:n,zoom:R(r,M),bearing:o.bearing,duration:500,essential:!0}),!0}export function useCameraReactions(r,o,f){m(()=>a.moveToBooths,e=>{const t=r.current;if(!e||!t||!o)return;const n=e.filter(c=>c.rect);if(n.length===0)return;if(_(t,o,n)){a.moveToBooths=null;return}const i=b.fromUnion(...n.map(c=>c.rect));h(t,I(i),o),a.moveToBooths=null}),m(()=>a.moveToRect,e=>{const t=r.current;if(!e||!t||!o)return;const{width:n,height:i}=e.size,c=n>0&&i>0?x(e):e,p=a.moveToRectPaddingPercent??T(e,d()),l=y(c,p/(1-2*p));h(t,l,o,{padding:0}),a.moveToRect=null,a.moveToRectPaddingPercent=null}),m(()=>a.zoomBy,e=>{const t=r.current;!e||!t||(t.flyTo({zoom:t.getZoom()+(e>1?.5:-.5),animate:!0,duration:500}),a.zoomBy=null)}),m(()=>[s.moveToLocation,u.maplibreStore.cameraPort,u.routeStore.currentPosition],([e])=>{if(!e)return;const t=r.current,n=u.routeStore.currentPosition;if(!n){s.moveToLocation=!1;return}if(!t)return;const{lng:i,lat:c}=n,p=typeof i=="number"&&Number.isFinite(i)&&typeof c=="number"&&Number.isFinite(c);s.moveToLocation=!1;let l;if(p)l=[i,c];else{if(!o)return;l=P(n.x,n.y,o.gpsConfig)}t.flyTo({center:l,animate:!0,duration:1e3})}),m(()=>s.centerMap,e=>{const t=r.current;if(!e||!t||!o)return;if(!(s.list.type==="search"&&!s.list.text)){const{rect:i}=S({layerViewbox:u.layerStore.viewboxRectangle,explicitArea:g()?d():null,layerRectangle:u.layerStore.rectangle,sceneDef:f,svgArea:d()});h(t,i,o)}s.centerMap=!1})}
@@ -13,14 +13,11 @@ export interface CameraPaddingOptions {
13
13
  }
14
14
  export interface FitMapOptions {
15
15
  duration?: number;
16
- /** Breathing room around the rect, in CSS pixels per side. */
17
- padding?: number;
18
16
  /**
19
- * Breathing room as a fraction of the canvas per side, overriding `padding`
20
- * when set — the form Fit-to-screen speaks, so the legacy renderer and the map
21
- * pad a venue fit by the same proportion rather than by the same pixel count.
17
+ * Breathing room around the rect, in CSS pixels per side. A margin meant as a share of the
18
+ * framing region is grown into the rect by the caller instead (the `moveToRect` reaction).
22
19
  */
23
- paddingPercent?: number | null;
20
+ padding?: number;
24
21
  }
25
22
  export declare const MAP_BOUNDS_PADDING_FACTOR = 3;
26
23
  export declare const MAP_BOUNDS_MIN_PADDING_DEGREES = 0.02;
@@ -52,7 +49,7 @@ export declare function cameraForLocalRect(rect: Box, config: TransformGeoConfig
52
49
  center: [number, number];
53
50
  zoom: number;
54
51
  } | null;
55
- export declare function getVisibleRectFitPadding(extraPadding?: number): CameraPaddingOptions;
52
+ export declare function getVisibleRectFitPadding(extraPadding?: number, visibleRect?: Box): CameraPaddingOptions;
56
53
  /** Whether a lng/lat projects inside the visible viewport, inset by `padding`. */
57
54
  export declare function isLngLatInVisibleViewport(map: MaplibreMap, lngLat: [number, number], padding: CameraPaddingOptions): boolean;
58
55
  /** Geographic configuration from fpGeo data; null when unavailable. */
@@ -1 +1 @@
1
- import{LngLatBounds as S,MercatorCoordinate as R}from"maplibre-gl";import{getFpGeo as z}from"../../../data/fpGeo.js";import{getSvgArea as A}from"../../../data/svg.js";import{uiState as E}from"../../../store";import{convertLocalToGps as x}from"../../../utils/gps";import{planBearing as C}from"../../../utils/planBearing";export const MAP_BOUNDS_PADDING_FACTOR=3,MAP_BOUNDS_MIN_PADDING_DEGREES=.02,INITIAL_FIT_ZOOM_ADJUSTMENT=.15;const _=1.42,v=.35,D=.9,N=.85,P=8,B=.05;export function deriveVenueMapBounds(t){const n=A(),e=[[n.min.x,n.min.y],[n.max.x,n.min.y],[n.max.x,n.max.y],[n.min.x,n.max.y]].map(([c,u])=>x(c,u,t));if(!e.every(([c,u])=>Number.isFinite(c)&&Number.isFinite(u)))return;const o=e.map(([c])=>c),i=e.map(([,c])=>c),r=Math.min(...o),s=Math.max(...o),a=Math.min(...i),m=Math.max(...i),h=Math.max((s-r)*MAP_BOUNDS_PADDING_FACTOR,MAP_BOUNDS_MIN_PADDING_DEGREES),l=Math.max((m-a)*MAP_BOUNDS_PADDING_FACTOR,MAP_BOUNDS_MIN_PADDING_DEGREES);return[[r-h,a-l],[s+h,m+l]]}function F(t,n,e,o){const i=Math.min(t.getZoom(),n),r=(e-i)*Math.min(1,o/_);return i+r>=t.getMinZoom()-B}function w(t,n,e){if(F(t,n,e,_))return _;if(!F(t,n,e,v))return null;let o=v,i=_;for(let r=0;r<P;r++){const s=(o+i)/2;F(t,n,e,s)?o=s:i=s}return o}export function getLocalRectGeoBounds(t,n){const e=[x(t.min.x,t.min.y,n),x(t.max.x,t.min.y,n),x(t.max.x,t.max.y,n),x(t.min.x,t.max.y,n)],o=e.map(([r])=>r),i=e.map(([,r])=>r);return[[Math.min(...o),Math.min(...i)],[Math.max(...o),Math.max(...i)]]}const O=512;export function cameraForLocalRect(t,n,e,o,i){const r=[x(t.min.x,t.min.y,n),x(t.max.x,t.min.y,n),x(t.max.x,t.max.y,n),x(t.min.x,t.max.y,n)].map(([M,p])=>R.fromLngLat({lng:M,lat:p}));if(!r.every(({x:M,y:p})=>Number.isFinite(M)&&Number.isFinite(p)))return null;const s=-e*Math.PI/180,a=Math.cos(s),m=Math.sin(s),h=r.map(({x:M,y:p})=>M*a-p*m),l=r.map(({x:M,y:p})=>M*m+p*a),c=Math.max(...h)-Math.min(...h),u=Math.max(...l)-Math.min(...l);if(c<=0||u<=0)return null;const y=o.width-i.left-i.right,f=o.height-i.top-i.bottom;if(y<=0||f<=0)return null;const b=Math.min(y/(c*O),f/(u*O));if(!(b>0))return null;const g=O*b,d=(Math.min(...h)+Math.max(...h))/2-(i.left-i.right)/2/g,L=(Math.min(...l)+Math.max(...l))/2-(i.top-i.bottom)/2/g,{lng:T,lat:I}=new R(d*a+L*m,-d*m+L*a).toLngLat();return{center:[T,I],zoom:Math.log2(b)}}function U(t,n){return{top:t.top+n.top,bottom:t.bottom+n.bottom,left:t.left+n.left,right:t.right+n.right}}export function getVisibleRectFitPadding(t=0){const n=E.canvasVisibleRectPx,e=E.screenSize;return n?{top:Math.max(0,n.min.y)+t,bottom:Math.max(0,e.height-n.max.y)+t,left:Math.max(0,n.min.x)+t,right:Math.max(0,e.width-n.max.x)+t}:{top:t,bottom:t,left:t,right:t}}export function isLngLatInVisibleViewport(t,n,e){const o=t.project(n),i=t.getCanvas();return o.x>=e.left&&o.x<=i.clientWidth-e.right&&o.y>=e.top&&o.y<=i.clientHeight-e.bottom}export function deriveGeoConfig(){const t=z()?.properties?.config;if(!t)return null;const n=(t.p0.lng+t.p2.lng)/2,e=(t.p0.lat+t.p2.lat)/2,o=C(t.p0,t.p2),i=Number.isFinite(o)?o:t.bearing??0;return{center:[n,e],bearing:i,zoom:16,gpsConfig:t}}export function fitMapToLocalRect(t,n,e,o={}){if(!t||!n||!e||n.size.height===1/0||n.size.width===1/0||n.size.height<=0||n.size.width<=0)return;const i=getLocalRectGeoBounds(n,e.gpsConfig),r=t.getCanvas(),s=o.paddingPercent==null?getVisibleRectFitPadding(o.padding??50):U(getVisibleRectFitPadding(0),{top:r.clientHeight*o.paddingPercent,bottom:r.clientHeight*o.paddingPercent,left:r.clientWidth*o.paddingPercent,right:r.clientWidth*o.paddingPercent}),a=cameraForLocalRect(n,e.gpsConfig,e.bearing,{width:r.clientWidth,height:r.clientHeight},s);if(!a){t.fitBounds(i,{padding:s,bearing:e.bearing,essential:!0});return}const m=t.project(t.getCenter()),h=t.project(a.center),l=Math.max(t.getCanvas().clientWidth,t.getCanvas().clientHeight,1),c=Math.hypot(h.x-m.x,h.y-m.y)/l,u=Math.abs(t.getZoom()-a.zoom),y=o.duration??Math.min(2800,500+c*450+u*150),f={center:a.center,zoom:a.zoom,bearing:e.bearing,duration:y,essential:!0};if(c<=1.2&&u<=1.5){t.easeTo(f);return}const b=t.getBounds().extend(S.convert(i)),g=t.cameraForBounds(b,{padding:s,bearing:e.bearing});if(!g?.center||typeof g.zoom!="number"||g.zoom>=t.getZoom()-.2){t.easeTo(f);return}const d=w(t,a.zoom,g.zoom);if(d!==null){const L=Math.max(v,d*D),T=Math.round(y*N);t.flyTo({...f,curve:L,duration:T});return}t.easeTo(f)}
1
+ import{LngLatBounds as A,MercatorCoordinate as S}from"maplibre-gl";import{getFpGeo as C}from"../../../data/fpGeo.js";import{getSvgArea as D}from"../../../data/svg.js";import{uiState as v}from"../../../store";import{convertLocalToGps as x}from"../../../utils/gps";import{planBearing as N}from"../../../utils/planBearing";import{framingRect as R}from"../../Map/framing-rect.js";export const MAP_BOUNDS_PADDING_FACTOR=3,MAP_BOUNDS_MIN_PADDING_DEGREES=.02,INITIAL_FIT_ZOOM_ADJUSTMENT=.15;const b=1.42,O=.35,d=.9,B=.85,w=8,U=.05;export function deriveVenueMapBounds(t){const n=D(),o=[[n.min.x,n.min.y],[n.max.x,n.min.y],[n.max.x,n.max.y],[n.min.x,n.max.y]].map(([c,l])=>x(c,l,t));if(!o.every(([c,l])=>Number.isFinite(c)&&Number.isFinite(l)))return;const i=o.map(([c])=>c),e=o.map(([,c])=>c),s=Math.min(...i),r=Math.max(...i),a=Math.min(...e),m=Math.max(...e),h=Math.max((r-s)*MAP_BOUNDS_PADDING_FACTOR,MAP_BOUNDS_MIN_PADDING_DEGREES),u=Math.max((m-a)*MAP_BOUNDS_PADDING_FACTOR,MAP_BOUNDS_MIN_PADDING_DEGREES);return[[s-h,a-u],[r+h,m+u]]}function E(t,n,o,i){const e=Math.min(t.getZoom(),n),s=(o-e)*Math.min(1,i/b);return e+s>=t.getMinZoom()-U}function V(t,n,o){if(E(t,n,o,b))return b;if(!E(t,n,o,O))return null;let i=O,e=b;for(let s=0;s<w;s++){const r=(i+e)/2;E(t,n,o,r)?i=r:e=r}return i}export function getLocalRectGeoBounds(t,n){const o=[x(t.min.x,t.min.y,n),x(t.max.x,t.min.y,n),x(t.max.x,t.max.y,n),x(t.min.x,t.max.y,n)],i=o.map(([s])=>s),e=o.map(([,s])=>s);return[[Math.min(...i),Math.min(...e)],[Math.max(...i),Math.max(...e)]]}const I=512;export function cameraForLocalRect(t,n,o,i,e){const s=[x(t.min.x,t.min.y,n),x(t.max.x,t.min.y,n),x(t.max.x,t.max.y,n),x(t.min.x,t.max.y,n)].map(([g,p])=>S.fromLngLat({lng:g,lat:p}));if(!s.every(({x:g,y:p})=>Number.isFinite(g)&&Number.isFinite(p)))return null;const r=-o*Math.PI/180,a=Math.cos(r),m=Math.sin(r),h=s.map(({x:g,y:p})=>g*a-p*m),u=s.map(({x:g,y:p})=>g*m+p*a),c=Math.max(...h)-Math.min(...h),l=Math.max(...u)-Math.min(...u);if(c<=0||l<=0)return null;const y=i.width-e.left-e.right,f=i.height-e.top-e.bottom;if(y<=0||f<=0)return null;const L=Math.min(y/(c*I),f/(l*I));if(!(L>0))return null;const M=I*L,_=(Math.min(...h)+Math.max(...h))/2-(e.left-e.right)/2/M,T=(Math.min(...u)+Math.max(...u))/2-(e.top-e.bottom)/2/M,{lng:F,lat:z}=new S(_*a+T*m,-_*m+T*a).toLngLat();return{center:[F,z],zoom:Math.log2(L)}}export function getVisibleRectFitPadding(t=0,n=v.canvasVisibleRectPx){const o=v.screenSize;return n?{top:Math.max(0,n.min.y)+t,bottom:Math.max(0,o.height-n.max.y)+t,left:Math.max(0,n.min.x)+t,right:Math.max(0,o.width-n.max.x)+t}:{top:t,bottom:t,left:t,right:t}}export function isLngLatInVisibleViewport(t,n,o){const i=t.project(n),e=t.getCanvas();return i.x>=o.left&&i.x<=e.clientWidth-o.right&&i.y>=o.top&&i.y<=e.clientHeight-o.bottom}export function deriveGeoConfig(){const t=C()?.properties?.config;if(!t)return null;const n=(t.p0.lng+t.p2.lng)/2,o=(t.p0.lat+t.p2.lat)/2,i=N(t.p0,t.p2),e=Number.isFinite(i)?i:t.bearing??0;return{center:[n,o],bearing:e,zoom:16,gpsConfig:t}}export function fitMapToLocalRect(t,n,o,i={}){if(!t||!n||!o||n.size.height===1/0||n.size.width===1/0||n.size.height<=0||n.size.width<=0)return;const e=getLocalRectGeoBounds(n,o.gpsConfig),s=t.getCanvas(),r=getVisibleRectFitPadding(i.padding??50,R()),a=cameraForLocalRect(n,o.gpsConfig,o.bearing,{width:s.clientWidth,height:s.clientHeight},r);if(!a){t.fitBounds(e,{padding:r,bearing:o.bearing,essential:!0});return}const m=t.project(t.getCenter()),h=t.project(a.center),u=Math.max(t.getCanvas().clientWidth,t.getCanvas().clientHeight,1),c=Math.hypot(h.x-m.x,h.y-m.y)/u,l=Math.abs(t.getZoom()-a.zoom),y=i.duration??Math.min(2800,500+c*450+l*150),f={center:a.center,zoom:a.zoom,bearing:o.bearing,duration:y,essential:!0};if(c<=1.2&&l<=1.5){t.easeTo(f);return}const L=t.getBounds().extend(A.convert(e)),M=t.cameraForBounds(L,{padding:r,bearing:o.bearing});if(!M?.center||typeof M.zoom!="number"||M.zoom>=t.getZoom()-.2){t.easeTo(f);return}const _=V(t,a.zoom,M.zoom);if(_!==null){const T=Math.max(O,_*d),F=Math.round(y*B);t.flyTo({...f,curve:T,duration:F});return}t.easeTo(f)}
@@ -30,4 +30,9 @@ export declare function createSceneDef(layers: LayerDef[], viewboxRect: Box | SV
30
30
  export declare function createShapeDot(rect: Rect, color?: string, radius?: number): ImageDef & {
31
31
  source: HTMLCanvasElement;
32
32
  };
33
+ /**
34
+ * The CSS size a booth's dot is drawn at. For the drawers that receive a dot rather than create
35
+ * one, and so cannot read the radius it was made from.
36
+ */
37
+ export declare const SHAPE_DOT_CSS_PX: number;
33
38
  //# sourceMappingURL=defs.d.ts.map
@@ -1,3 +1,3 @@
1
- import{Box as i,isBox as c,isMesh as a,isPolygon as u,isRect as p,Mesh as x,Rect as d}from"@expofp/geometry";import{createCircleCanvas as h}from"./canvases";import{DEFAULT_FONT_LIGHT_URL as m,DEFAULT_FONT_URL as w}from"./fonts";export function rectToShapeDef(e,r){return createShapeDef(i.fromSvg(e),r)}export function meshToShapeDef(e,r){if(!e)throw new Error("Create ShapeDef: mesh is not provided");const t=e.positions.map(o=>({x:o[0],y:Math.abs(o[1]),z:o[2]??0})),n=e.cells;return createShapeDef(new x(t,n),r)}export function createShapeDef(e,r="#000"){if(!e||!(c(e)||p(e)||u(e)||a(e)))throw new Error("Invalid shape definition");return{shape:e,color:r}}export function createImageDef(e,r,t){if(!e)throw new Error("Image source is not provided");return{source:e,bounds:r,...t}}export function createTextDef(e,r,t,n,o){if(e.length===0)throw new Error("Invalid text lines");return{lines:e,bounds:r,padding:t,alignment:n,...o}}export function createTextDefLines(e,r,t,n=[void 0,void 0]){const[o,f]=e,s=[{text:o.join(`
2
- `),fontUrl:n[0]||w,color:r,fontSize:t}];return f.length&&s.push({text:f.join(`
3
- `),fontUrl:n[1]||m,color:r,fontSize:t}),s}export function createLayerDef(e,r,t){if(!e)throw new Error("Layer name is required");return r||(r=[]),{name:e,children:r,...t}}export function createSceneDef(e,r,t){const n=r instanceof i?r:i.fromSvg(r);return{rootLayer:createLayerDef("root",e),background:t,viewbox:n}}export function createShapeDot(e,r="#000",t=1.5){const n=h(t,r),o=new d(e.center,{x:n.width,y:n.height},e.rotation,e.elevation);return createImageDef(n,o)}
1
+ import{Box as s,isBox as u,isMesh as p,isPolygon as x,isRect as D,Mesh as S,Rect as d}from"@expofp/geometry";import{circleCanvasCssSize as f,createCircleCanvas as l}from"../components/Map/drawing/config/canvases";import{DEFAULT_FONT_LIGHT_URL as m,DEFAULT_FONT_URL as w}from"./fonts";export function rectToShapeDef(e,t){return createShapeDef(s.fromSvg(e),t)}export function meshToShapeDef(e,t){if(!e)throw new Error("Create ShapeDef: mesh is not provided");const n=e.positions.map(o=>({x:o[0],y:Math.abs(o[1]),z:o[2]??0})),r=e.cells;return createShapeDef(new S(n,r),t)}export function createShapeDef(e,t="#000"){if(!e||!(u(e)||D(e)||x(e)||p(e)))throw new Error("Invalid shape definition");return{shape:e,color:t}}export function createImageDef(e,t,n){if(!e)throw new Error("Image source is not provided");return{source:e,bounds:t,...n}}export function createTextDef(e,t,n,r,o){if(e.length===0)throw new Error("Invalid text lines");return{lines:e,bounds:t,padding:n,alignment:r,...o}}export function createTextDefLines(e,t,n,r=[void 0,void 0]){const[o,i]=e,c=[{text:o.join(`
2
+ `),fontUrl:r[0]||w,color:t,fontSize:n}];return i.length&&c.push({text:i.join(`
3
+ `),fontUrl:r[1]||m,color:t,fontSize:n}),c}export function createLayerDef(e,t,n){if(!e)throw new Error("Layer name is required");return t||(t=[]),{name:e,children:t,...n}}export function createSceneDef(e,t,n){const r=t instanceof s?t:s.fromSvg(t);return{rootLayer:createLayerDef("root",e),background:n,viewbox:r}}export function createShapeDot(e,t="#000",n=a){const r=l(n,t),o=f(n),i=new d(e.center,{x:o,y:o},e.rotation,e.elevation);return createImageDef(r,i)}const a=1.5;export const SHAPE_DOT_CSS_PX=f(a);
@@ -33,7 +33,19 @@ export declare function rotateRectByQuarters(rect: Rect, turns: number): Rect;
33
33
  export declare function getQuarterTurns(newAngle: number, currentRotation: number): number;
34
34
  export declare function shouldFlip(newAngle: number, currentRotation: number): boolean;
35
35
  export declare function flipAngle(angle: number): number;
36
- export declare function getClampedBoundsCanvas(rect: Rect, canvas: HTMLCanvasElement, padding: number, scale: number): Rect;
36
+ /**
37
+ * Bounds for an image pinned to a booth's top-right corner, clamped to fit inside the booth.
38
+ * @param rect - The booth.
39
+ * @param size - The image's size at a scale of 1, in CSS pixels. Not a canvas's backing store: that
40
+ * is in the device pixels it was rasterized at, which says how crisp the image is, not how big.
41
+ * @param padding - The booth's inset, in plan units.
42
+ * @param scale - Plan units per CSS pixel.
43
+ * @returns The bounds.
44
+ */
45
+ export declare function getClampedBoundsCanvas(rect: Rect, size: {
46
+ readonly width: number;
47
+ readonly height: number;
48
+ }, padding: number, scale: number): Rect;
37
49
  /**
38
50
  * Reads the `roll` map setting — `1`/`0`, from the URL or the venue — as a boolean, refusing
39
51
  * anything else. The label configs consult it before declaring their `onRoll` rules: with roll
@@ -1 +1 @@
1
- import{angleDiff as p,Rect as k}from"@expofp/geometry";import{getBounds as w}from"./canvases";export function updateLinesScale(t,o,n){let e=!1;return t.forEach((r,i)=>{let s=0;n[i].forEach(({value:a,scaleFactor:c})=>{o<c&&(s=a)}),r.fontSize!==s*o&&(e=!0),r.fontSize=s*o}),e}export function rotateTextDefByQuarters(t,o){const{center:n,size:e,rotation:r,elevation:i}=t.bounds,s=o%2!==0;return t.bounds.set(n.x,n.y,s?e.y:e.x,s?e.x:e.y,r+o*Math.PI/2,i),t}export function rotateRectByQuarters(t,o){const{size:n,center:e,rotation:r,elevation:i}=t,s=o%2!==0?{x:n.y,y:n.x}:n;return new k(e,s,r+o*Math.PI/2,i)}export function getQuarterTurns(t,o){if(!Number.isFinite(t)||!Number.isFinite(o))return 0;const n=p(t,o);return Math.abs(n)<=Math.PI/4?0:Math.round(n/(Math.PI/2))}export function shouldFlip(t,o){const n=p(t,o),e=Math.PI/2;return Math.abs(n)>e+.1}export function flipAngle(t){return(t+Math.PI+Math.PI)%(2*Math.PI)-Math.PI}export function getClampedBoundsCanvas(t,o,n,e){const r=o.width,i=o.height,s=t.size,a=t.center,c=t.rotation,d=s.x/r,M=s.x-n*2,b=(s.y-n*2)*.8,y=0,g=M/r,I=b/i,S=Math.min(g,I,d);e=Math.min(Math.max(e,y),S);const P=r*e,z=i*e,h=Math.sin(c),f=Math.cos(c),u=s.x/2-n,x=-s.y/2+n,B=a.x+u*f-x*h,R=a.y+u*h+x*f,l=P/2,m=-z/2,W=l*f-m*h,H=l*h+m*f,X=B-W,Y=R-H;return w(X,Y,r,i,e,c,t.elevation)}export function getRollValue(t){if(!t)return!1;if(!Number.isInteger(+t)||+t!=1&&+t!=0)throw new Error("Map roll: setting value is incorrect");return!!+t}
1
+ import{angleDiff as p,Rect as k}from"@expofp/geometry";import{getBounds as w}from"../components/Map/drawing/config/canvases";export function updateLinesScale(t,o,n){let e=!1;return t.forEach((r,i)=>{let s=0;n[i].forEach(({value:a,scaleFactor:c})=>{o<c&&(s=a)}),r.fontSize!==s*o&&(e=!0),r.fontSize=s*o}),e}export function rotateTextDefByQuarters(t,o){const{center:n,size:e,rotation:r,elevation:i}=t.bounds,s=o%2!==0;return t.bounds.set(n.x,n.y,s?e.y:e.x,s?e.x:e.y,r+o*Math.PI/2,i),t}export function rotateRectByQuarters(t,o){const{size:n,center:e,rotation:r,elevation:i}=t,s=o%2!==0?{x:n.y,y:n.x}:n;return new k(e,s,r+o*Math.PI/2,i)}export function getQuarterTurns(t,o){if(!Number.isFinite(t)||!Number.isFinite(o))return 0;const n=p(t,o);return Math.abs(n)<=Math.PI/4?0:Math.round(n/(Math.PI/2))}export function shouldFlip(t,o){const n=p(t,o),e=Math.PI/2;return Math.abs(n)>e+.1}export function flipAngle(t){return(t+Math.PI+Math.PI)%(2*Math.PI)-Math.PI}export function getClampedBoundsCanvas(t,o,n,e){const r=o.width,i=o.height,s=t.size,a=t.center,c=t.rotation,d=s.x/r,M=s.x-n*2,b=(s.y-n*2)*.8,y=0,g=M/r,I=b/i,S=Math.min(g,I,d);e=Math.min(Math.max(e,y),S);const P=r*e,B=i*e,h=Math.sin(c),f=Math.cos(c),u=s.x/2-n,x=-s.y/2+n,z=a.x+u*f-x*h,R=a.y+u*h+x*f,l=P/2,m=-B/2,W=l*f-m*h,H=l*h+m*f,X=z-W,Y=R-H;return w(X,Y,r,i,e,c,t.elevation)}export function getRollValue(t){if(!t)return!1;if(!Number.isInteger(+t)||+t!=1&&+t!=0)throw new Error("Map roll: setting value is incorrect");return!!+t}
@@ -1 +1 @@
1
- import{applyIntents as v,getConfig as h,parseDeprecatedUrlParams as Y,parseFromUrlTolerant as W,parseIntentsFromUrl as k,parseLegacyQuery as I,serializeIntentToUrl as z,serializeSelection as Q}from"@expofp/config";import{Box as j}from"@expofp/geometry";import{UrlConfigSchema as H}from"@expofp/schema";import{safeDecode as p}from"@expofp/utils";import{runInAction as N}from"mobx";import{applyKioskMode as $}from"../components/Kiosk/kiosk-mode";import{recoverKioskFromUrlPrams as q}from"../components/Kiosk/recoverKioskFromUrlPrams";import{LEGACY_PREVIEW_MODE_STORAGE_KEY as J,LEGACY_VIEWER_MODE_STORAGE_KEY as X,PREVIEW_MODE_STORAGE_KEY as w,VIEWER_MODE_STORAGE_KEY as E}from"../constants";import Z from"../data";import n,{uiState as i}from"../store";import{BoothBase as x}from"../store/BoothStore";import{CurrentPosition as ee,extractRoute as te}from"../store/RouteStore";import{eventScopedValue as oe}from"../tools/event-scoped-cache.js";import{setConsentSettings as re}from"../tools/gtag";import M from"../tools/logger";import{areLayersEnabled as ie}from"../utils/areLayersEnabled";import{brand as F}from"../utils/brand";import{cameraToMapSettings as O}from"../utils/camera-map-settings";import{handleCustomCommand as se}from"../utils/customCommands";import{formatEventTitle as ne,formatPlaceholderTitle as ae}from"../utils/event-title";import{getMostFrequent as le}from"../utils/getMostFrequent";import{heatmapEnabled as ce}from"../utils/heatmap-enabled";import ue from"../utils/is-own-domain";import{isLocalStorageAvailable as B}from"../utils/localStorage";import{planAutorun as L,planWhen as d}from"../utils/plan-reactions.js";import{stripTrackingParams as fe}from"../utils/trackingParams";import{rewriteMultiSlugQuery as me}from"./multi-slug";import{currentSearch as f,disableUrlWrites as pe,initUrlSync as de,pushQuery as he,replaceQuery as m}from"./url-sync";let y=!1;export function applyDeclarativeConfig(){S(h()),d(()=>n.layerStore.layersLoaded,()=>{const e=h().camera;Object.keys(e).length&&G(O(e));const t=h().visibility;Object.keys(t).length&&i.setVisibility(t)})}export function initUrlRouting(e){de({offHistory:e,onNavigate:()=>g({backForward:!0})}),R(),b(),d(()=>n.layerStore.layersLoaded,()=>{n.urlFilterManager&&!n.urlFilterManager.isInitialized&&n.urlFilterManager.initialize(),g(),L(V),L(_),i.viewerMode&&(i.menu=!0)})}export function applyParameters(e=""){m("?"+p(e.toString())),n.layerStore.layersLoaded?P(e):(b(),d(()=>n.layerStore.layersLoaded,()=>P(e)))}function P(e){const t="?"+p(e),o=Se(t),s=[...o.intents??[],...k(t).intents],{intents:l=[],...c}=Y(t,{visibility:o.visibility,consent:o.consent,intents:s});S(o),S(c);const a=o.consent??c.consent;a&&re(a),o.camera&&Object.keys(o.camera).length&&G(O(o.camera)),o.visibility&&Object.keys(o.visibility).length&&i.setVisibility(o.visibility),R(),g();const r=[...s,...l];r.length&&v(n.fp,r)}function b(){const e=f(),t=e.length>1?p(e.substring(1)):"";return se(t)}function g(e={}){y=!0;try{const t=new URLSearchParams(p(window.location.search));q(t);const{primary:o}=I(f()),s=o.type==="select"&&(!o.slug||/^t=[^&]*$/.test(o.slug))&&(i.heatmap||i.heatmapYah);!b()&&!s&&(e.backForward?ye(o):U(o))}finally{y=!1}if(e.backForward){const{exhibitor:t,booth:o,eventItem:s}=D(),l=C();l.exhibitor=t,l.booth=o,l.eventItem=s}_(),V()}function ye(e){e.type==="list"&&e.list==="sessions"?n.fp.showSessions():U(e);const{intents:t}=k(f());v(n.fp,t.filter(o=>o.name==="showSessions"))}function U(e){const t=n.fp;switch(e.type){case"kiosk":e.enabled!==null&&$(i,e.enabled);return;case"list":t.showList(e.list);return;case"tour":t.selectTour(e.tourId);return;case"planner":t.openPlanner({items:e.items,from:e.from,fromBookmarks:e.fromBookmarks});return;case"route":{n.routeStore.setOnlyAccessible(e.accessible),n.routeStore.selectRoute(te(e.from??"",e.to??"",e.waypoints)),n.uiState.setIsMultiPointRoute(e.waypoints.length>0),e.title&&n.uiState.setRouteTitle(e.title);return}case"selectExhibitors":setTimeout(()=>t.selectExhibitor(e.values),250);return;case"ownedByFilters":M.log("Skipping filter parameters in dispatchFromUrl (owned by URLFilterManager)");return;case"ownedByConfig":return;case"select":t.select(e.slug);return}}export function applyPathway(e){const{pathwayId:t,boothIds:o,exhibitorIds:s}=e,l=n.boothStore.booths.filter(r=>o.includes(r.externalId)),c=n.exhibitorStore.exhibitors.filter(r=>s.includes(r.externalId)).flatMap(r=>r.booths),a=[...l,...c];if(ie()){const r=le(a.map(u=>u.layer.name));r&&n.fp.activateFloor({name:r})}(o.length>0||s.length>0)&&n.fp.highlightEntities([...o,...s]),n.layerStore.showPathWay(t),a.length>0&&N(()=>{n.uiState.moveToRect=j.fromUnion(...a.map(r=>r.rect).filter(Boolean)),n.uiState.rollReset=!0})}function R(){const{oneShots:e}=I(f());for(const o of e)be(o);const t=fe(f());t!==null&&m("?"+t),i.previewExhibitor&&m("?"+i.previewExhibitor.slug),ge()}function be(e){switch(e.type){case"heatmap":T(e.yah);return;case"blueDot":{const t=new ee(e.x,e.y,n.layerStore.findLayer(e.layer??"")?.name,void 0,e.lat,e.lng);d(()=>n.layerStore.layersLoaded,()=>n.routeStore.selectCurrentPosition(t,!0,1));return}case"viewerMode":B&&(e.enabled?localStorage.setItem(E,"true"):(localStorage.removeItem(E),localStorage.removeItem(X)));return;case"previewMode":B&&(e.enabled?localStorage.setItem(w,"true"):(localStorage.removeItem(w),localStorage.removeItem(J)));return;case"uiScale":e.scale!==null?n.uiState.setUIScale(e.scale,!0):M.warn(`Invalid ui-scale parameter: ${e.raw}. Must be a number between 0.8 and 1.4.`);return;case"resetUiScale":n.uiState.resetUIScale();return}}function T(e){pe(),e?(i.heatmapYah=!0,i.monochrome=!0,i.hideLogoInBooth=!0,i.hideHeaderLogo=!0,i.disableBookmarked=!0):i.heatmap=!0}function ge(){const e=me(f(),t=>n.exhibitorStore.exhibitors.find(s=>s.slug===t||s.externalId===t)?.name);e!==null&&m(e)}function S(e){e.noOverlay!==void 0&&(i.hideOverlay=e.noOverlay),e.monochrome&&(i.monochrome=!0),e.hideHeaderLogo&&(i.hideHeaderLogo=!0),e.hideLogoInBooth&&(i.hideLogoInBooth=!0),e.disableFeatured&&n.exhibitorStore.exhibitors.forEach(t=>t.featured=!1),e.disableBookmarked&&(i.disableBookmarked=!0),e.disableGps&&n.routeStore.setGpsTrackingEnabled(!1),e.heatmap&&ce(e.heatmap)&&T(e.heatmap.variant==="yah")}function Se(e){return W(H,e).value}const C=oe(()=>({exhibitor:null,booth:null,eventItem:null}));function _(){const{selection:e,exhibitor:t,booth:o,eventItem:s}=D(),l=n.urlFilterManager.filterSearch(),c=ve(e);if(y)return;const a=c||Q(e)||l;if(f()===a)return;const r=C();t!==r.exhibitor||o!==r.booth||s!==r.eventItem?he(a):m(a),r.exhibitor=t,r.booth=o,r.eventItem=s}function ve(e){if(e.type!=="list"||e.list!=="sessions")return"";const{selectedTracks:t,selectedVenues:o,date:s,timeOfDay:l,showBookmarksOnly:c,showLiveOnly:a}=n.sessionsFilterStore.state.filters,r={};return t.value.length&&(r.tracks=A(t.value)),o.value.length&&(r.venues=A(o.value)),s.value!=="all"&&(r.date=s.value),s.value==="today"&&l.value!=="all"&&(r.time=l.value),c.value&&(r.bookmarked=!0),a.value&&(r.live=!0),Object.keys(r).length?"?"+z({name:"showSessions",args:[r]}):""}function A(e){return[...e].sort((t,o)=>t-o).map(String)}function D(){const e=i.selectedExhibitor,t=i.selectedBooth,o=i.selectedEventItem,s=i.selectedRoute,l=i.selectedSpeaker,c=i.setKioskModeEnabled,a=i.list;let r;if(a.type==="route-planner"&&!e&&!t&&!o){const u=n.routePlannerStore;r={type:"planner",fromBookmarks:u.source==="bookmarks",items:u.items.map(K=>K.entity.slug).filter(Boolean),from:u.startPoint?.slug||void 0}}else if(s&&!i.isMultiPointRoute)r={type:"route",to:s.to instanceof x?s.to.slug:void 0,from:s.from instanceof x?s.from.slug:void 0,accessible:n.routeStore.onlyAccessible,waypoints:s.waypoints?.map(u=>u.slug)};else if(e)r={type:"slug",slug:e.slug};else if(t)r={type:"slug",slug:t.slug};else if(o)r={type:"slug",slug:o.slug};else if(l)r={type:"slug",slug:l.slug};else if(c)r={type:"none"};else switch(a.type){case"bookmarks":case"visited":case"sessions":case"exhibitors":case"speakers":case"tours":case"language":r={type:"list",list:a.type};break;case"category":r={type:"slug",slug:i.selectedCategory?.slug??""};break;case"search":r={type:"slug",slug:a.text||""};break;case"filter":r={type:"filter",key:a.query.key,value:a.query.value};break;case"multi-point-tour":r={type:"tour",tourId:a.tourId};break;default:throw new Error("Unkown list.type")}return{selection:r,exhibitor:e,booth:t,eventItem:o}}function V(){if(!ue)return;const e=i.selectedExhibitor,t=i.selectedEventItem,o=i.selectedSpeaker;let s="";e?s=e.name:t?s=t.name:o?s=o.name:i.list.type==="search"&&i.list.text?s="`"+(i.list.text||"")+"`":i.list.type==="multi-point-tour"&&i.tourTitle&&(s=i.tourTitle),s.length&&(s+=" \u2013 "),s+=ne(Z,F)??ae(window.location.hostname,F),document.title=s}function G(e){try{if(i.setMapSettings(e),e.z){const t=n.layerStore.layers.find(o=>o.name===e.z);t&&n.layerStore.updateVisibility(t,!0)}}catch(t){console.error("Failed to process map settings.",t)}}
1
+ import{applyIntents as k,getConfig as y,parseDeprecatedUrlParams as W,parseFromUrlTolerant as z,parseIntentsFromUrl as I,parseLegacyQuery as w,serializeIntentToUrl as N,serializeSelection as Q}from"@expofp/config";import{Box as j}from"@expofp/geometry";import{UrlConfigSchema as H}from"@expofp/schema";import{safeDecode as p}from"@expofp/utils";import{runInAction as $}from"mobx";import{applyKioskMode as q}from"../components/Kiosk/kiosk-mode";import{recoverKioskFromUrlPrams as J}from"../components/Kiosk/recoverKioskFromUrlPrams";import{LEGACY_PREVIEW_MODE_STORAGE_KEY as X,LEGACY_VIEWER_MODE_STORAGE_KEY as Z,PREVIEW_MODE_STORAGE_KEY as x,VIEWER_MODE_STORAGE_KEY as E}from"../constants";import ee from"../data";import s,{uiState as i}from"../store";import{BoothBase as F}from"../store/BoothStore";import{CurrentPosition as te,extractRoute as oe}from"../store/RouteStore";import{eventScopedValue as re}from"../tools/event-scoped-cache.js";import{setConsentSettings as ie}from"../tools/gtag";import M from"../tools/logger";import{areLayersEnabled as se}from"../utils/areLayersEnabled";import{brand as O}from"../utils/brand";import{cameraToMapSettings as B}from"../utils/camera-map-settings";import{handleCustomCommand as ne}from"../utils/customCommands";import{formatEventTitle as ae,formatPlaceholderTitle as le}from"../utils/event-title";import{getMostFrequent as ce}from"../utils/getMostFrequent";import{heatmapEnabled as ue}from"../utils/heatmap-enabled";import fe from"../utils/is-own-domain";import{isLocalStorageAvailable as L}from"../utils/localStorage";import{planAutorun as P,planWhen as d}from"../utils/plan-reactions.js";import{stripTrackingParams as me}from"../utils/trackingParams";import{rewriteMultiSlugQuery as pe}from"./multi-slug";import{currentSearch as f,disableUrlWrites as de,initUrlSync as he,pushQuery as ye,replaceQuery as m}from"./url-sync";let b=!1,h=!1;export function applyDeclarativeConfig(){v(y()),d(()=>s.layerStore.layersLoaded,()=>{const e=y().camera;Object.keys(e).length&&K(B(e));const t=y().visibility;Object.keys(t).length&&i.setVisibility(t)})}export function initUrlRouting(e){he({offHistory:e,onNavigate:()=>S({backForward:!0})}),T(),g(),d(()=>s.layerStore.layersLoaded,()=>{s.urlFilterManager&&!s.urlFilterManager.isInitialized&&s.urlFilterManager.initialize(),h=!0;try{S()}finally{h=!1}P(G),P(A),i.viewerMode&&(i.menu=!0)})}export function applyParameters(e=""){m("?"+p(e.toString())),s.layerStore.layersLoaded?U(e):(g(),d(()=>s.layerStore.layersLoaded,()=>U(e)))}function U(e){const t="?"+p(e),o=ve(t),n=[...o.intents??[],...I(t).intents],{intents:l=[],...c}=W(t,{visibility:o.visibility,consent:o.consent,intents:n});v(o),v(c);const a=o.consent??c.consent;a&&ie(a),o.camera&&Object.keys(o.camera).length&&K(B(o.camera)),o.visibility&&Object.keys(o.visibility).length&&i.setVisibility(o.visibility),T(),S();const r=[...n,...l];r.length&&k(s.fp,r)}function g(){const e=f(),t=e.length>1?p(e.substring(1)):"";return ne(t)}function S(e={}){b=!0;try{const t=new URLSearchParams(p(window.location.search));J(t);const{primary:o}=w(f()),n=o.type==="select"&&(!o.slug||/^t=[^&]*$/.test(o.slug))&&(i.heatmap||i.heatmapYah);!g()&&!n&&(e.backForward?be(o):R(o))}finally{b=!1}if(e.backForward){const{exhibitor:t,booth:o,eventItem:n}=V(),l=_();l.exhibitor=t,l.booth=o,l.eventItem=n}A(),G()}function be(e){e.type==="list"&&e.list==="sessions"?s.fp.showSessions():R(e);const{intents:t}=I(f());k(s.fp,t.filter(o=>o.name==="showSessions"))}function R(e){const t=s.fp;switch(e.type){case"kiosk":e.enabled!==null&&q(i,e.enabled);return;case"list":t.showList(e.list);return;case"tour":t.selectTour(e.tourId);return;case"planner":h&&s.searchButtonsStore.keepFiltersOnNextNavigation(),t.openPlanner({items:e.items,from:e.from,fromBookmarks:e.fromBookmarks});return;case"route":{s.routeStore.setOnlyAccessible(e.accessible);const o=oe(e.from??"",e.to??"",e.waypoints);h&&!(o.from&&o.from===o.to)&&s.searchButtonsStore.keepFiltersOnNextNavigation(),s.routeStore.selectRoute(o),s.uiState.setIsMultiPointRoute(e.waypoints.length>0),e.title&&s.uiState.setRouteTitle(e.title);return}case"selectExhibitors":setTimeout(()=>t.selectExhibitor(e.values),250);return;case"ownedByFilters":M.log("Skipping filter parameters in dispatchFromUrl (owned by URLFilterManager)");return;case"ownedByConfig":return;case"select":t.select(e.slug);return}}export function applyPathway(e){const{pathwayId:t,boothIds:o,exhibitorIds:n}=e,l=s.boothStore.booths.filter(r=>o.includes(r.externalId)),c=s.exhibitorStore.exhibitors.filter(r=>n.includes(r.externalId)).flatMap(r=>r.booths),a=[...l,...c];if(se()){const r=ce(a.map(u=>u.layer.name));r&&s.fp.activateFloor({name:r})}(o.length>0||n.length>0)&&s.fp.highlightEntities([...o,...n]),s.layerStore.showPathWay(t),a.length>0&&$(()=>{s.uiState.moveToRect=j.fromUnion(...a.map(r=>r.rect).filter(Boolean)),s.uiState.rollReset=!0})}function T(){const{oneShots:e}=w(f());for(const o of e)ge(o);const t=me(f());t!==null&&m("?"+t),i.previewExhibitor&&m("?"+i.previewExhibitor.slug),Se()}function ge(e){switch(e.type){case"heatmap":C(e.yah);return;case"blueDot":{const t=new te(e.x,e.y,s.layerStore.findLayer(e.layer??"")?.name,void 0,e.lat,e.lng);d(()=>s.layerStore.layersLoaded,()=>s.routeStore.selectCurrentPosition(t,!0,1));return}case"viewerMode":L&&(e.enabled?localStorage.setItem(E,"true"):(localStorage.removeItem(E),localStorage.removeItem(Z)));return;case"previewMode":L&&(e.enabled?localStorage.setItem(x,"true"):(localStorage.removeItem(x),localStorage.removeItem(X)));return;case"uiScale":e.scale!==null?s.uiState.setUIScale(e.scale,!0):M.warn(`Invalid ui-scale parameter: ${e.raw}. Must be a number between 0.8 and 1.4.`);return;case"resetUiScale":s.uiState.resetUIScale();return}}function C(e){de(),e?(i.heatmapYah=!0,i.monochrome=!0,i.hideLogoInBooth=!0,i.hideHeaderLogo=!0,i.disableBookmarked=!0):i.heatmap=!0}function Se(){const e=pe(f(),t=>s.exhibitorStore.exhibitors.find(n=>n.slug===t||n.externalId===t)?.name);e!==null&&m(e)}function v(e){e.noOverlay!==void 0&&(i.hideOverlay=e.noOverlay),e.monochrome&&(i.monochrome=!0),e.hideHeaderLogo&&(i.hideHeaderLogo=!0),e.hideLogoInBooth&&(i.hideLogoInBooth=!0),e.disableFeatured&&s.exhibitorStore.exhibitors.forEach(t=>t.featured=!1),e.disableBookmarked&&(i.disableBookmarked=!0),e.disableGps&&s.routeStore.setGpsTrackingEnabled(!1),e.heatmap&&ue(e.heatmap)&&C(e.heatmap.variant==="yah")}function ve(e){return z(H,e).value}const _=re(()=>({exhibitor:null,booth:null,eventItem:null}));function A(){const{selection:e,exhibitor:t,booth:o,eventItem:n}=V(),l=s.urlFilterManager.filterSearch(),c=ke(e);if(b)return;const a=c||Q(e)||l;if(f()===a)return;const r=_();t!==r.exhibitor||o!==r.booth||n!==r.eventItem?ye(a):m(a),r.exhibitor=t,r.booth=o,r.eventItem=n}function ke(e){if(e.type!=="list"||e.list!=="sessions")return"";const{selectedTracks:t,selectedVenues:o,date:n,timeOfDay:l,showBookmarksOnly:c,showLiveOnly:a}=s.sessionsFilterStore.state.filters,r={};return t.value.length&&(r.tracks=D(t.value)),o.value.length&&(r.venues=D(o.value)),n.value!=="all"&&(r.date=n.value),n.value==="today"&&l.value!=="all"&&(r.time=l.value),c.value&&(r.bookmarked=!0),a.value&&(r.live=!0),Object.keys(r).length?"?"+N({name:"showSessions",args:[r]}):""}function D(e){return[...e].sort((t,o)=>t-o).map(String)}function V(){const e=i.selectedExhibitor,t=i.selectedBooth,o=i.selectedEventItem,n=i.selectedRoute,l=i.selectedSpeaker,c=i.setKioskModeEnabled,a=i.list;let r;if(a.type==="route-planner"&&!e&&!t&&!o){const u=s.routePlannerStore;r={type:"planner",fromBookmarks:u.source==="bookmarks",items:u.items.map(Y=>Y.entity.slug).filter(Boolean),from:u.startPoint?.slug||void 0}}else if(n&&!i.isMultiPointRoute)r={type:"route",to:n.to instanceof F?n.to.slug:void 0,from:n.from instanceof F?n.from.slug:void 0,accessible:s.routeStore.onlyAccessible,waypoints:n.waypoints?.map(u=>u.slug)};else if(e)r={type:"slug",slug:e.slug};else if(t)r={type:"slug",slug:t.slug};else if(o)r={type:"slug",slug:o.slug};else if(l)r={type:"slug",slug:l.slug};else if(c)r={type:"none"};else switch(a.type){case"bookmarks":case"visited":case"sessions":case"exhibitors":case"speakers":case"tours":case"language":r={type:"list",list:a.type};break;case"category":r={type:"slug",slug:i.selectedCategory?.slug??""};break;case"search":r={type:"slug",slug:a.text||""};break;case"filter":r={type:"filter",key:a.query.key,value:a.query.value};break;case"multi-point-tour":r={type:"tour",tourId:a.tourId};break;default:throw new Error("Unkown list.type")}return{selection:r,exhibitor:e,booth:t,eventItem:o}}function G(){if(!fe)return;const e=i.selectedExhibitor,t=i.selectedEventItem,o=i.selectedSpeaker;let n="";e?n=e.name:t?n=t.name:o?n=o.name:i.list.type==="search"&&i.list.text?n="`"+(i.list.text||"")+"`":i.list.type==="multi-point-tour"&&i.tourTitle&&(n=i.tourTitle),n.length&&(n+=" \u2013 "),n+=ae(ee,O)??le(window.location.hostname,O),document.title=n}function K(e){try{if(i.setMapSettings(e),e.z){const t=s.layerStore.layers.find(o=>o.name===e.z);t&&s.layerStore.updateVisibility(t,!0)}}catch(t){console.error("Failed to process map settings.",t)}}
@@ -42,11 +42,11 @@ export default class UIState {
42
42
  moveToBooths: Booth[] | null;
43
43
  moveToRect: Box | null;
44
44
  /**
45
- * Breathing room the pending `moveToRect` wants, as a fraction of the viewport
46
- * per side. `null` leaves each renderer on its own default — the adaptive
47
- * 15%/25% for the legacy one, a fixed inset for the map. Fit-to-screen sets 0:
48
- * its rect is the framing itself, so padding it would zoom past the authored
49
- * viewbox.
45
+ * Breathing room the pending `moveToRect` wants, as a fraction of the framing
46
+ * region per side — the visible map less the side panel, in both renderers.
47
+ * `null` leaves it to the adaptive 15%/25% rule, which both renderers apply.
48
+ * Fit-to-screen sets 0: its rect is the framing itself, so padding it would
49
+ * zoom past the authored viewbox.
50
50
  */
51
51
  moveToRectPaddingPercent: number | null;
52
52
  moveToLocation: boolean;
@@ -1,3 +1,3 @@
1
- import RootStore from '../RootStore';
1
+ import type RootStore from '../RootStore';
2
2
  export declare function initHeatmap(store: RootStore): void;
3
3
  //# sourceMappingURL=init-heatmap.d.ts.map
@@ -1 +1 @@
1
- import{createCurrentCanvas as c}from"../../components/Map/drawing/config/canvases";import{HeatmapYah as h}from"../HeatmapStore";export function initHeatmap(e){if(window.__heatmapData)e.heatmapStore.heatmapData=window.__heatmapData;else if(window.__heatmapDataYah){e.heatmapStore.heatmapData.yah=window.__heatmapDataYah.yah.map(a=>{const t=new h;return t.id=a.id,t.name=a.name,t.viewCount=a.viewCount,t.x=a.x,t.y=a.y,t.z=a.z,t});const o=e.heatmapStore.heatmapData.yah.map(a=>{const t=e.heatmapStore.getColorByClicks(a.viewCount),r=e.uiState.devicePixelRatio,n=c(r,t,void 0,95,120,a.viewCount.toString()),p=n.toDataURL();return{name:a.id.toString(),scale:1,width:n.width,height:n.height,content:p}}),i=e.heatmapStore.heatmapData.yah.map(a=>({id:a.id.toString(),x:a.x,y:a.y,z:a.z,position:"lefttop",icon:a.id.toString(),selectedIcon:a.id.toString()}));e.routeStore.setMarkers({markers:i,icons:o}),e.selectSearch(" ")}}
1
+ import{createCurrentCanvas as c,cssSizeOf as m}from"../../components/Map/drawing/config/canvases";import{HeatmapYah as d}from"../HeatmapStore";export function initHeatmap(t){if(window.__heatmapData)t.heatmapStore.heatmapData=window.__heatmapData;else if(window.__heatmapDataYah){t.heatmapStore.heatmapData.yah=window.__heatmapDataYah.yah.map(a=>new d(a.id,a.name,a.viewCount,a.x,a.y,a.z));const o=t.heatmapStore.heatmapData.yah.map(a=>{const i=t.heatmapStore.getColorByClicks(a.viewCount),p=t.uiState.devicePixelRatio,e=c(p,i,void 0,95,120,a.viewCount.toString()),r=e.toDataURL();return{name:a.id.toString(),scale:1,...m(e),content:r}}),n=t.heatmapStore.heatmapData.yah.map(a=>({id:a.id.toString(),x:a.x,y:a.y,z:a.z,position:"lefttop",icon:a.id.toString(),selectedIcon:a.id.toString()}));t.routeStore.setMarkers({markers:n,icons:o}),t.selectSearch(" ")}}
@@ -25,6 +25,13 @@ export declare class SearchButtonsActions {
25
25
  handleSessionsClick(caption?: string): void;
26
26
  togglePoiType(poiTypeId: number): void;
27
27
  clearAllFilters(): void;
28
+ /**
29
+ * Turns the active search button off when navigation opens, so its pins do
30
+ * not crowd the route. Unlike `clearAllFilters` it leaves the panel, the
31
+ * list and the details alone (they now show the navigation) and keeps
32
+ * pathways, which belong to tours, not to search buttons.
33
+ */
34
+ deactivateForNavigation(): void;
28
35
  showSubButtons(parentButtonId: string): void;
29
36
  hideSubButtons(): void;
30
37
  handleSubButtonClick(subButtonId: string): void;
@@ -40,6 +47,8 @@ export declare class SearchButtonsActions {
40
47
  private preserveSearchText;
41
48
  private switchToSearch;
42
49
  private resetAllFilters;
50
+ /** Turns off the active search button and every filter it applied, URL included. */
51
+ private clearActiveFilters;
43
52
  private setPoiFilterFromString;
44
53
  private get isMobile();
45
54
  }
@@ -1 +1 @@
1
- import{__decorate as r}from"tslib";import{action as l}from"mobx";import{MOBILE_BREAKPOINT as n}from"../../constants";import c from"../../tools/track-event";import{PREFIXES as a,SearchButtonType as o}from"./constants";import{extractPoiTypeId as u,getButtonConfig as S}from"./utils";export class SearchButtonsActions{constructor(e,t,i){this.rootStore=e,this.state=t,this.filters=i}reportActivation(e){e&&c("sbtnview",e)}handleSearchButtonClick(e,t){const s={[o.BOOKMARKS]:()=>this.handleBookmarksClick(t),[o.VISITED]:()=>this.handleVisitedClick(t),[o.SESSIONS]:()=>this.handleSessionsClick(t),[o.TOURS]:()=>this.handleToursClick(t),[o.CATEGORIES]:()=>this.handleCategoriesClick(t),[o.EXHIBITORS]:()=>this.handleExhibitorsClick(t)}[e];if(s){s();return}e.startsWith(a.POI_TYPE)&&this.handlePoiTypeClick(e,t)}handleExtendedSearchButtonClick(e){const t=S(e);if(!t)return;const i=t.name;if(t.type==="link"){this.handleLinkClick(t,i);return}const s=t.children?.filter(h=>h.visible)||[];if(s.length===1&&s[0].type==="poi_types"&&s[0].name===t.name){this.handleSearchButtonClick(e,i);return}t.children&&s.length>0?this.handleParentButton(e,t,i):this.handleSearchButtonClick(e,i)}handleMainButtonClick(e){this.handleSearchButtonClick(e)}handleSessionsClick(e){const{uiState:t}=this.rootStore;t.list.type==="sessions"?this.switchToSearch():(this.reportActivation(e),this.resetAllFilters(),t.details=null,t.menu=!1,t.list={type:"sessions",text:"",focused:!1},this.state.setSubButton(null),t.selectedTrack=null,this.state.subButtonsEnabled&&this.showSubButtons("sessions"),this.isMobile&&(t.desiredOverlaySize="full"))}togglePoiType(e){this.handleSearchButtonClick(`${a.POI_TYPE}${e}`)}clearAllFilters(){this.switchToSearch()}showSubButtons(e){this.state.subButtonsEnabled&&(this.state.setSubButtonParent(e),this.state.setSubButton(null),this.rootStore.uiState.subButtonsVisible=!0)}hideSubButtons(){this.state.setSubButtonParent(null),this.state.setSubButton(null),this.rootStore.uiState.subButtonsVisible=!1}handleSubButtonClick(e){this.state.activeSubButtonId!==e&&this.state.setSubButton(e)}toggleListView(e,t,i){this.rootStore.uiState.list.type===e?this.switchToSearch():(this.reportActivation(i),this.resetAllFilters(),t())}handleBookmarksClick(e){this.toggleListView("bookmarks",()=>{this.rootStore.clickBookmarks()},e)}handleVisitedClick(e){this.toggleListView("visited",()=>{this.rootStore.clickVisited()},e)}handleToursClick(e){const{uiState:t}=this.rootStore;t.list.type==="tours"?this.switchToSearch():(this.reportActivation(e),this.resetAllFilters(),t.details=null,t.menu=!1,t.list={type:"tours",text:"",focused:!1},this.isMobile&&(t.desiredOverlaySize="full"))}handleCategoriesClick(e){const{uiState:t}=this.rootStore;this.reportActivation(e),t.list.type!=="search"?this.switchToSearch():this.state.activePoiFilterId&&this.filters.clearPoiTypeFilters(),this.state.searchShortcutState.activeParentId&&(this.state.setActiveParentId(null),this.state.clearChildren(),this.state.showShortcuts(!1),this.filters.clearAllChildFilters()),this.rootStore.categoryFilterStore.openFilter()}handleExhibitorsClick(e){const{uiState:t}=this.rootStore;t.list.type==="exhibitors"?this.switchToSearch():(this.reportActivation(e),this.resetAllFilters(),t.details=null,t.menu=!1,t.list={type:"exhibitors",text:"",focused:!1},this.isMobile&&(t.desiredOverlaySize="full"))}handlePoiTypeClick(e,t){const{uiState:i}=this.rootStore,s=this.state.activePoiFilterId!==e;s&&this.reportActivation(t);const h=i.list.type==="search"&&i.list.text||"";i.list.type!=="search"?this.switchToSearch(!0):i.selectedCategoryFilters.length>0&&this.filters.clearCategoryFilters(),this.state.searchShortcutState.activeParentId&&(this.state.setActiveParentId(null),this.state.clearChildren(),this.state.showShortcuts(!1),this.filters.clearAllChildFilters()),s?this.setPoiFilterFromString(e):this.filters.clearPoiTypeFilters(),h&&i.list.type==="search"&&(i.list={type:"search",text:h,focused:!1}),this.rootStore.searchStore.selectSearchAndFocus(h)}handleLinkClick(e,t){const i=e.children?.find(s=>s.type==="link");i&&"linkUrl"in i&&(this.reportActivation(t),window.open(i.linkUrl,"_blank"))}handleParentButton(e,t,i){const{uiState:s}=this.rootStore;if(this.state.searchShortcutState.activeParentId===e){this.state.setActiveParentId(null),this.state.clearChildren(),this.state.showShortcuts(!1),this.filters.clearAllChildFilters(),this.rootStore.urlFilterManager.clearAllFilters(),s.centerMap=!0,s.overlaySize==="full"&&s.searchFocused&&this.rootStore.searchStore.setFocused(!1);return}this.reportActivation(i),s.list.type!=="search"&&this.switchToSearch(),this.state.activePoiFilterId&&this.state.setPoiFilter(null),this.state.setActiveParentId(e),this.state.showShortcuts(!0),this.state.clearChildren(),this.filters.applyAllChildrenFilters(t),this.rootStore.searchStore.selectSearchAndFocus()}preserveSearchText(e){const{uiState:t}=this.rootStore,i=t.list.type==="search"&&t.list.text||"";e(),t.list.type==="search"&&i&&(t.list={type:"search",text:i,focused:!1})}switchToSearch(e=!1){const t=()=>{this.resetAllFilters(),this.rootStore.uiState.details=null,this.rootStore.uiState.menu=!1,this.rootStore.uiState.list={type:"search",text:"",focused:!1}};e?this.preserveSearchText(t):t()}resetAllFilters(){this.state.reset(),this.rootStore.uiState.selectedPoiTypeFilters=[],this.rootStore.uiState.selectedCategoryFilters=[],this.rootStore.uiState.categoryFilterOpen=!1,this.rootStore.categoryFilterStore.state.selectedItems=[],this.rootStore.categoryFilterStore.state.pendingItems=[],this.rootStore.urlFilterManager.clearAllFilters(),this.rootStore.fp.hidePathways()}setPoiFilterFromString(e){this.state.setPoiFilter(e);const t=u(e);if(t!==null){const i=this.rootStore.poiTypeStore.poiTypes.find(s=>s.id===t);i&&(this.rootStore.uiState.selectedPoiTypeFilters=[i],this.rootStore.urlFilterManager.setPoiType(i.name))}}get isMobile(){return typeof window<"u"&&window.innerWidth<=n}}r([l],SearchButtonsActions.prototype,"handleSearchButtonClick",null),r([l],SearchButtonsActions.prototype,"handleExtendedSearchButtonClick",null),r([l],SearchButtonsActions.prototype,"handleMainButtonClick",null),r([l],SearchButtonsActions.prototype,"handleSessionsClick",null),r([l],SearchButtonsActions.prototype,"togglePoiType",null),r([l],SearchButtonsActions.prototype,"clearAllFilters",null),r([l],SearchButtonsActions.prototype,"showSubButtons",null),r([l],SearchButtonsActions.prototype,"hideSubButtons",null),r([l],SearchButtonsActions.prototype,"handleSubButtonClick",null);
1
+ import{__decorate as r}from"tslib";import{action as l}from"mobx";import{MOBILE_BREAKPOINT as n}from"../../constants";import c from"../../tools/track-event";import{PREFIXES as h,SearchButtonType as o}from"./constants";import{extractPoiTypeId as u,getButtonConfig as S}from"./utils";export class SearchButtonsActions{constructor(e,t,i){this.rootStore=e,this.state=t,this.filters=i}reportActivation(e){e&&c("sbtnview",e)}handleSearchButtonClick(e,t){const s={[o.BOOKMARKS]:()=>this.handleBookmarksClick(t),[o.VISITED]:()=>this.handleVisitedClick(t),[o.SESSIONS]:()=>this.handleSessionsClick(t),[o.TOURS]:()=>this.handleToursClick(t),[o.CATEGORIES]:()=>this.handleCategoriesClick(t),[o.EXHIBITORS]:()=>this.handleExhibitorsClick(t)}[e];if(s){s();return}e.startsWith(h.POI_TYPE)&&this.handlePoiTypeClick(e,t)}handleExtendedSearchButtonClick(e){const t=S(e);if(!t)return;const i=t.name;if(t.type==="link"){this.handleLinkClick(t,i);return}const s=t.children?.filter(a=>a.visible)||[];if(s.length===1&&s[0].type==="poi_types"&&s[0].name===t.name){this.handleSearchButtonClick(e,i);return}t.children&&s.length>0?this.handleParentButton(e,t,i):this.handleSearchButtonClick(e,i)}handleMainButtonClick(e){this.handleSearchButtonClick(e)}handleSessionsClick(e){const{uiState:t}=this.rootStore;t.list.type==="sessions"?this.switchToSearch():(this.reportActivation(e),this.resetAllFilters(),t.details=null,t.menu=!1,t.list={type:"sessions",text:"",focused:!1},this.state.setSubButton(null),t.selectedTrack=null,this.state.subButtonsEnabled&&this.showSubButtons("sessions"),this.isMobile&&(t.desiredOverlaySize="full"))}togglePoiType(e){this.handleSearchButtonClick(`${h.POI_TYPE}${e}`)}clearAllFilters(){this.switchToSearch()}deactivateForNavigation(){const{uiState:e}=this.rootStore;(this.state.activePoiFilterId!==null||this.state.searchShortcutState.activeParentId!==null||e.hasAppliedFilters)&&this.clearActiveFilters()}showSubButtons(e){this.state.subButtonsEnabled&&(this.state.setSubButtonParent(e),this.state.setSubButton(null),this.rootStore.uiState.subButtonsVisible=!0)}hideSubButtons(){this.state.setSubButtonParent(null),this.state.setSubButton(null),this.rootStore.uiState.subButtonsVisible=!1}handleSubButtonClick(e){this.state.activeSubButtonId!==e&&this.state.setSubButton(e)}toggleListView(e,t,i){this.rootStore.uiState.list.type===e?this.switchToSearch():(this.reportActivation(i),this.resetAllFilters(),t())}handleBookmarksClick(e){this.toggleListView("bookmarks",()=>{this.rootStore.clickBookmarks()},e)}handleVisitedClick(e){this.toggleListView("visited",()=>{this.rootStore.clickVisited()},e)}handleToursClick(e){const{uiState:t}=this.rootStore;t.list.type==="tours"?this.switchToSearch():(this.reportActivation(e),this.resetAllFilters(),t.details=null,t.menu=!1,t.list={type:"tours",text:"",focused:!1},this.isMobile&&(t.desiredOverlaySize="full"))}handleCategoriesClick(e){const{uiState:t}=this.rootStore;this.reportActivation(e),t.list.type!=="search"?this.switchToSearch():this.state.activePoiFilterId&&this.filters.clearPoiTypeFilters(),this.state.searchShortcutState.activeParentId&&(this.state.setActiveParentId(null),this.state.clearChildren(),this.state.showShortcuts(!1),this.filters.clearAllChildFilters()),this.rootStore.categoryFilterStore.openFilter()}handleExhibitorsClick(e){const{uiState:t}=this.rootStore;t.list.type==="exhibitors"?this.switchToSearch():(this.reportActivation(e),this.resetAllFilters(),t.details=null,t.menu=!1,t.list={type:"exhibitors",text:"",focused:!1},this.isMobile&&(t.desiredOverlaySize="full"))}handlePoiTypeClick(e,t){const{uiState:i}=this.rootStore,s=this.state.activePoiFilterId!==e;s&&this.reportActivation(t);const a=i.list.type==="search"&&i.list.text||"";i.list.type!=="search"?this.switchToSearch(!0):i.selectedCategoryFilters.length>0&&this.filters.clearCategoryFilters(),this.state.searchShortcutState.activeParentId&&(this.state.setActiveParentId(null),this.state.clearChildren(),this.state.showShortcuts(!1),this.filters.clearAllChildFilters()),s?this.setPoiFilterFromString(e):this.filters.clearPoiTypeFilters(),a&&i.list.type==="search"&&(i.list={type:"search",text:a,focused:!1}),this.rootStore.searchStore.selectSearchAndFocus(a)}handleLinkClick(e,t){const i=e.children?.find(s=>s.type==="link");i&&"linkUrl"in i&&(this.reportActivation(t),window.open(i.linkUrl,"_blank"))}handleParentButton(e,t,i){const{uiState:s}=this.rootStore;if(this.state.searchShortcutState.activeParentId===e){this.state.setActiveParentId(null),this.state.clearChildren(),this.state.showShortcuts(!1),this.filters.clearAllChildFilters(),this.rootStore.urlFilterManager.clearAllFilters(),s.centerMap=!0,s.overlaySize==="full"&&s.searchFocused&&this.rootStore.searchStore.setFocused(!1);return}this.reportActivation(i),s.list.type!=="search"&&this.switchToSearch(),this.state.activePoiFilterId&&this.state.setPoiFilter(null),this.state.setActiveParentId(e),this.state.showShortcuts(!0),this.state.clearChildren(),this.filters.applyAllChildrenFilters(t),this.rootStore.searchStore.selectSearchAndFocus()}preserveSearchText(e){const{uiState:t}=this.rootStore,i=t.list.type==="search"&&t.list.text||"";e(),t.list.type==="search"&&i&&(t.list={type:"search",text:i,focused:!1})}switchToSearch(e=!1){const t=()=>{this.resetAllFilters(),this.rootStore.uiState.details=null,this.rootStore.uiState.menu=!1,this.rootStore.uiState.list={type:"search",text:"",focused:!1}};e?this.preserveSearchText(t):t()}resetAllFilters(){this.clearActiveFilters(),this.rootStore.fp.hidePathways()}clearActiveFilters(){this.state.reset(),this.rootStore.uiState.selectedPoiTypeFilters=[],this.rootStore.uiState.selectedCategoryFilters=[],this.rootStore.uiState.categoryFilterOpen=!1,this.rootStore.categoryFilterStore.state.selectedItems=[],this.rootStore.categoryFilterStore.state.pendingItems=[],this.rootStore.urlFilterManager.clearAllFilters()}setPoiFilterFromString(e){this.state.setPoiFilter(e);const t=u(e);if(t!==null){const i=this.rootStore.poiTypeStore.poiTypes.find(s=>s.id===t);i&&(this.rootStore.uiState.selectedPoiTypeFilters=[i],this.rootStore.urlFilterManager.setPoiType(i.name))}}get isMobile(){return typeof window<"u"&&window.innerWidth<=n}}r([l],SearchButtonsActions.prototype,"handleSearchButtonClick",null),r([l],SearchButtonsActions.prototype,"handleExtendedSearchButtonClick",null),r([l],SearchButtonsActions.prototype,"handleMainButtonClick",null),r([l],SearchButtonsActions.prototype,"handleSessionsClick",null),r([l],SearchButtonsActions.prototype,"togglePoiType",null),r([l],SearchButtonsActions.prototype,"clearAllFilters",null),r([l],SearchButtonsActions.prototype,"deactivateForNavigation",null),r([l],SearchButtonsActions.prototype,"showSubButtons",null),r([l],SearchButtonsActions.prototype,"hideSubButtons",null),r([l],SearchButtonsActions.prototype,"handleSubButtonClick",null);
@@ -8,9 +8,24 @@ export default class SearchButtonsStore {
8
8
  private readonly actions;
9
9
  private readonly factory;
10
10
  private poiTypeCountsCache;
11
+ /** Set by `keepFiltersOnNextNavigation`, spent by the next entry into navigation. */
12
+ private keepFiltersOnNextEntry;
11
13
  private lastPoiTypeCountUpdate;
12
14
  constructor(rootStore: RootStore);
13
15
  private setupReactions;
16
+ /**
17
+ * Lets the next entry into navigation keep the active filter. For a deep
18
+ * link that opens with both a filter and a route or planner: that is not a
19
+ * switch into navigation, so its filter stays. Call it only right before
20
+ * opening navigation that is sure to land, or the hold outlives it and
21
+ * spares the user's own next entry.
22
+ */
23
+ keepFiltersOnNextNavigation(): void;
24
+ /**
25
+ * Navigation is the route planner or the directions panel. The planner opens
26
+ * before its route is drawn, so a planner session counts as one entry.
27
+ */
28
+ private get isNavigationOpen();
14
29
  private get subButtonRegistry();
15
30
  private registerSubButtonProviders;
16
31
  private invalidatePoiTypeCounts;