@sjvair/monitor-map 3.2.2 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/MonitorMapLayout.svelte +20 -88
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/map/MapShell.svelte +146 -0
- package/dist/lib/map/MapShell.svelte.d.ts +48 -0
- package/package.json +1 -1
- package/src/lib/MonitorMapLayout.svelte +20 -88
- package/src/lib/index.ts +1 -0
- package/src/lib/map/MapShell.svelte +146 -0
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import { onDestroy } from "svelte";
|
|
4
|
-
import
|
|
5
|
-
import Map from "./map/Map.svelte";
|
|
6
|
-
import Menu from "./map/Menu.svelte";
|
|
4
|
+
import MapShell from "./map/MapShell.svelte";
|
|
7
5
|
import MonitorsDisplayOptions from "./monitors/components/MonitorsDisplayOptions.svelte";
|
|
8
6
|
import MapLayersDisplayOptions from "./components/MapLayersDisplayOptions.svelte";
|
|
9
7
|
import MapStyleDisplayOptions from "./map/MapStyleDisplayOptions.svelte";
|
|
10
|
-
import { mapManager } from "./map/map.svelte";
|
|
11
8
|
import { monitorsManager } from "./monitors/monitors.svelte";
|
|
12
9
|
import { monitorsMapIntegration } from "./monitors/monitors-map-integration.svelte";
|
|
13
10
|
import { windMapIntegration } from "./wind/wind.svelte";
|
|
@@ -33,8 +30,6 @@
|
|
|
33
30
|
|
|
34
31
|
const { route, navigate, basePath } = useMonitorMapRouter();
|
|
35
32
|
|
|
36
|
-
const TRANSITION_MS = 300;
|
|
37
|
-
|
|
38
33
|
const integrations: Array<SomeMapIntegration> = [
|
|
39
34
|
baseLayerSeperator,
|
|
40
35
|
collocationSitesMapIntegration,
|
|
@@ -76,22 +71,6 @@
|
|
|
76
71
|
}
|
|
77
72
|
});
|
|
78
73
|
|
|
79
|
-
$effect(() => {
|
|
80
|
-
if (mapManager.map && monitorsManager.initialized) {
|
|
81
|
-
mapManager.map.once("idle", () => disableLoadScreen());
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
$effect(() => {
|
|
86
|
-
if (panelOpen) {
|
|
87
|
-
// Container width snapped to open; resize after layout settles
|
|
88
|
-
requestAnimationFrame(() => mapManager.map?.resize());
|
|
89
|
-
} else {
|
|
90
|
-
// Container width snaps back after the transform transition ends
|
|
91
|
-
setTimeout(() => mapManager.map?.resize(), TRANSITION_MS);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
74
|
// Clear selected icon scale when the detail panel closes
|
|
96
75
|
$effect(() => {
|
|
97
76
|
if (panelOpen) return;
|
|
@@ -101,75 +80,28 @@
|
|
|
101
80
|
onDestroy(() => {
|
|
102
81
|
monitorsManager.autoUpdate.stop();
|
|
103
82
|
});
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* HACK: Fix for escaping sv-router and allowing navigation to other pages,
|
|
107
|
-
* as well as navigating back
|
|
108
|
-
*/
|
|
109
|
-
const rootPath = basePath || "/";
|
|
110
|
-
const knownRoutes = [`${basePath}/monitor/`];
|
|
111
|
-
|
|
112
|
-
window.addEventListener("pageshow", (e) => {
|
|
113
|
-
if (e.persisted) window.location.reload();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
document.addEventListener(
|
|
117
|
-
"click",
|
|
118
|
-
(e) => {
|
|
119
|
-
const anchor = e
|
|
120
|
-
.composedPath()
|
|
121
|
-
.find((el) => el instanceof HTMLAnchorElement) as HTMLAnchorElement;
|
|
122
|
-
if (!anchor) return;
|
|
123
|
-
const { pathname } = new URL(anchor.href);
|
|
124
|
-
const isKnown = pathname === rootPath || knownRoutes.some((r) => pathname.startsWith(r));
|
|
125
|
-
if (!isKnown) {
|
|
126
|
-
e.stopImmediatePropagation();
|
|
127
|
-
window.location.href = anchor.href;
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
{ capture: true }
|
|
131
|
-
);
|
|
132
83
|
</script>
|
|
133
84
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
85
|
+
<MapShell
|
|
86
|
+
{integrations}
|
|
87
|
+
ready={monitorsManager.initialized}
|
|
88
|
+
{panelOpen}
|
|
89
|
+
knownRoutes={[`${basePath}/monitor/`]}
|
|
90
|
+
{basePath}
|
|
91
|
+
>
|
|
92
|
+
{#snippet menu()}
|
|
93
|
+
<MonitorsDisplayOptions />
|
|
94
|
+
<EvStationsDisplayOptions />
|
|
95
|
+
<MapLayersDisplayOptions />
|
|
96
|
+
<MapStyleDisplayOptions />
|
|
97
|
+
{/snippet}
|
|
98
|
+
{#snippet overlays()}
|
|
141
99
|
<div class="pointer-events-none absolute bottom-0 left-0 z-10">
|
|
142
100
|
<MapLegend />
|
|
143
101
|
</div>
|
|
144
|
-
<div class="absolute top-4 left-
|
|
145
|
-
<
|
|
146
|
-
<Menu>
|
|
147
|
-
<MonitorsDisplayOptions />
|
|
148
|
-
<EvStationsDisplayOptions />
|
|
149
|
-
<MapLayersDisplayOptions />
|
|
150
|
-
<MapStyleDisplayOptions />
|
|
151
|
-
</Menu>
|
|
152
|
-
</div>
|
|
153
|
-
<div class="absolute left-16">
|
|
154
|
-
<Search />
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
158
|
-
<div
|
|
159
|
-
class={[
|
|
160
|
-
"panel-containr w-full shrink-0 overflow-hidden",
|
|
161
|
-
panelOpen ? "h-1/2 md:h-full md:w-1/3" : "md:w-0"
|
|
162
|
-
]}
|
|
163
|
-
>
|
|
164
|
-
<div
|
|
165
|
-
class={[
|
|
166
|
-
"panel-contet h-full duration-300 ease-in-out",
|
|
167
|
-
panelOpen
|
|
168
|
-
? "translate-y-0 md:translate-x-0"
|
|
169
|
-
: "translate-y-full md:translate-x-full md:translate-y-0"
|
|
170
|
-
]}
|
|
171
|
-
>
|
|
172
|
-
{@render children()}
|
|
102
|
+
<div class="absolute top-4 left-20 z-10">
|
|
103
|
+
<Search />
|
|
173
104
|
</div>
|
|
174
|
-
|
|
175
|
-
|
|
105
|
+
{/snippet}
|
|
106
|
+
{@render children()}
|
|
107
|
+
</MapShell>
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export { baseLayerSeperator } from "./map/integrations/base-layer-seperator";
|
|
|
44
44
|
export { hmsFireMapIntegration } from "./hms/hms-fire-map-integration.svelte";
|
|
45
45
|
export { hmsSmokeMapIntegration } from "./hms/hms-smoke-map-integration.svelte";
|
|
46
46
|
export { default as Map } from "./map/Map.svelte";
|
|
47
|
+
export { default as MapShell, type MapShellProps } from "./map/MapShell.svelte";
|
|
47
48
|
export { default as Menu } from "./map/Menu.svelte";
|
|
48
49
|
export { default as LoadScreen, loadScreenState, enable as enableLoadScreen, disable as disableLoadScreen } from "./LoadScreen.svelte";
|
|
49
50
|
export type { LoadScreenState } from "./LoadScreen.svelte";
|
package/dist/lib/index.js
CHANGED
|
@@ -42,6 +42,7 @@ export { hmsFireMapIntegration } from "./hms/hms-fire-map-integration.svelte";
|
|
|
42
42
|
export { hmsSmokeMapIntegration } from "./hms/hms-smoke-map-integration.svelte";
|
|
43
43
|
// Components
|
|
44
44
|
export { default as Map } from "./map/Map.svelte";
|
|
45
|
+
export { default as MapShell } from "./map/MapShell.svelte";
|
|
45
46
|
export { default as Menu } from "./map/Menu.svelte";
|
|
46
47
|
export { default as LoadScreen, loadScreenState, enable as enableLoadScreen, disable as disableLoadScreen } from "./LoadScreen.svelte";
|
|
47
48
|
export { default as MonitorsDisplayOptions } from "./monitors/components/MonitorsDisplayOptions.svelte";
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { SomeMapIntegration } from "./integrations/types";
|
|
4
|
+
|
|
5
|
+
export interface MapShellProps {
|
|
6
|
+
/** Map integrations to register, e.g. [monitorsMapIntegration] */
|
|
7
|
+
integrations: Array<SomeMapIntegration>;
|
|
8
|
+
/**
|
|
9
|
+
* True once the caller's own data managers have finished their initial
|
|
10
|
+
* load. The shell hides the load screen once the map is idle AND this
|
|
11
|
+
* is true.
|
|
12
|
+
*/
|
|
13
|
+
ready: boolean;
|
|
14
|
+
/** True when the routed detail panel (`children`) should be visible */
|
|
15
|
+
panelOpen: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Path prefixes that are part of this map's own routed panel (e.g.
|
|
18
|
+
* [`${basePath}/monitor/`]) — internal navigation to these should not
|
|
19
|
+
* trigger the escape-hatch full-page navigation below.
|
|
20
|
+
*/
|
|
21
|
+
knownRoutes?: Array<string>;
|
|
22
|
+
/** Rendered inside the display-options menu toggle */
|
|
23
|
+
menu?: Snippet;
|
|
24
|
+
/** Freeform absolutely-positioned content over the map (legend, search, etc.) */
|
|
25
|
+
overlays?: Snippet;
|
|
26
|
+
/** The routed detail panel content */
|
|
27
|
+
children?: Snippet;
|
|
28
|
+
/**
|
|
29
|
+
* Base path to use for the sv-router escape-hatch's "known" root route.
|
|
30
|
+
* If provided, used directly instead of reading `basePath` from
|
|
31
|
+
* `useMonitorMapRouter()`. Only relevant when `routerEscapeHatch` is
|
|
32
|
+
* enabled.
|
|
33
|
+
*/
|
|
34
|
+
basePath?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Whether to install the sv-router escape-hatch (`pageshow`/`click`
|
|
37
|
+
* listeners that force a full-page navigation for links outside this
|
|
38
|
+
* map's own routes). Defaults to `true`, preserving the existing
|
|
39
|
+
* full-page-map behavior. When `true` and no `basePath` prop is given,
|
|
40
|
+
* `MapShell` falls back to reading `basePath` from
|
|
41
|
+
* `useMonitorMapRouter()`. Set to `false` to embed `MapShell` inside a
|
|
42
|
+
* host app with its own routing — this also skips calling
|
|
43
|
+
* `useMonitorMapRouter()` entirely, so no `provideMonitorMapRouter(...)`
|
|
44
|
+
* ancestor is required.
|
|
45
|
+
*/
|
|
46
|
+
routerEscapeHatch?: boolean;
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<script lang="ts">
|
|
51
|
+
import LoadScreen, { disable as disableLoadScreen } from "../LoadScreen.svelte";
|
|
52
|
+
import Map from "./Map.svelte";
|
|
53
|
+
import Menu from "./Menu.svelte";
|
|
54
|
+
import { mapManager } from "./map.svelte";
|
|
55
|
+
import { useMonitorMapRouter } from "../router-context";
|
|
56
|
+
|
|
57
|
+
let {
|
|
58
|
+
integrations,
|
|
59
|
+
ready,
|
|
60
|
+
panelOpen,
|
|
61
|
+
knownRoutes = [],
|
|
62
|
+
menu,
|
|
63
|
+
overlays,
|
|
64
|
+
children,
|
|
65
|
+
basePath: providedBasePath,
|
|
66
|
+
routerEscapeHatch = true
|
|
67
|
+
}: MapShellProps = $props();
|
|
68
|
+
|
|
69
|
+
const basePath = providedBasePath ?? (routerEscapeHatch ? useMonitorMapRouter().basePath : "");
|
|
70
|
+
const TRANSITION_MS = 300;
|
|
71
|
+
|
|
72
|
+
$effect(() => {
|
|
73
|
+
if (mapManager.map && ready) {
|
|
74
|
+
mapManager.map.once("idle", () => disableLoadScreen());
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
$effect(() => {
|
|
79
|
+
if (panelOpen) {
|
|
80
|
+
// Container width snapped to open; resize after layout settles
|
|
81
|
+
requestAnimationFrame(() => mapManager.map?.resize());
|
|
82
|
+
} else {
|
|
83
|
+
// Container width snaps back after the transform transition ends
|
|
84
|
+
setTimeout(() => mapManager.map?.resize(), TRANSITION_MS);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* HACK: Fix for escaping sv-router and allowing navigation to other pages,
|
|
90
|
+
* as well as navigating back
|
|
91
|
+
*/
|
|
92
|
+
if (routerEscapeHatch) {
|
|
93
|
+
const rootPath = basePath || "/";
|
|
94
|
+
|
|
95
|
+
window.addEventListener("pageshow", (e) => {
|
|
96
|
+
if (e.persisted) window.location.reload();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
document.addEventListener(
|
|
100
|
+
"click",
|
|
101
|
+
(e) => {
|
|
102
|
+
const anchor = e
|
|
103
|
+
.composedPath()
|
|
104
|
+
.find((el) => el instanceof HTMLAnchorElement) as HTMLAnchorElement;
|
|
105
|
+
if (!anchor) return;
|
|
106
|
+
const { pathname } = new URL(anchor.href);
|
|
107
|
+
const isKnown = pathname === rootPath || knownRoutes.some((r) => pathname.startsWith(r));
|
|
108
|
+
if (!isKnown) {
|
|
109
|
+
e.stopImmediatePropagation();
|
|
110
|
+
window.location.href = anchor.href;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{ capture: true }
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<div class="relative flex h-full w-full flex-col md:flex-row">
|
|
119
|
+
<LoadScreen />
|
|
120
|
+
<div class="relative flex-1 overflow-hidden">
|
|
121
|
+
<Map {integrations} />
|
|
122
|
+
{@render overlays?.()}
|
|
123
|
+
{#if menu}
|
|
124
|
+
<div class="absolute top-4 left-4 z-10">
|
|
125
|
+
<Menu>{@render menu()}</Menu>
|
|
126
|
+
</div>
|
|
127
|
+
{/if}
|
|
128
|
+
</div>
|
|
129
|
+
<div
|
|
130
|
+
class={[
|
|
131
|
+
"panel-containr w-full shrink-0 overflow-hidden",
|
|
132
|
+
panelOpen ? "h-1/2 md:h-full md:w-1/3" : "md:w-0"
|
|
133
|
+
]}
|
|
134
|
+
>
|
|
135
|
+
<div
|
|
136
|
+
class={[
|
|
137
|
+
"panel-contet h-full duration-300 ease-in-out",
|
|
138
|
+
panelOpen
|
|
139
|
+
? "translate-y-0 md:translate-x-0"
|
|
140
|
+
: "translate-y-full md:translate-x-full md:translate-y-0"
|
|
141
|
+
]}
|
|
142
|
+
>
|
|
143
|
+
{@render children?.()}
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { SomeMapIntegration } from "./integrations/types";
|
|
3
|
+
export interface MapShellProps {
|
|
4
|
+
/** Map integrations to register, e.g. [monitorsMapIntegration] */
|
|
5
|
+
integrations: Array<SomeMapIntegration>;
|
|
6
|
+
/**
|
|
7
|
+
* True once the caller's own data managers have finished their initial
|
|
8
|
+
* load. The shell hides the load screen once the map is idle AND this
|
|
9
|
+
* is true.
|
|
10
|
+
*/
|
|
11
|
+
ready: boolean;
|
|
12
|
+
/** True when the routed detail panel (`children`) should be visible */
|
|
13
|
+
panelOpen: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Path prefixes that are part of this map's own routed panel (e.g.
|
|
16
|
+
* [`${basePath}/monitor/`]) — internal navigation to these should not
|
|
17
|
+
* trigger the escape-hatch full-page navigation below.
|
|
18
|
+
*/
|
|
19
|
+
knownRoutes?: Array<string>;
|
|
20
|
+
/** Rendered inside the display-options menu toggle */
|
|
21
|
+
menu?: Snippet;
|
|
22
|
+
/** Freeform absolutely-positioned content over the map (legend, search, etc.) */
|
|
23
|
+
overlays?: Snippet;
|
|
24
|
+
/** The routed detail panel content */
|
|
25
|
+
children?: Snippet;
|
|
26
|
+
/**
|
|
27
|
+
* Base path to use for the sv-router escape-hatch's "known" root route.
|
|
28
|
+
* If provided, used directly instead of reading `basePath` from
|
|
29
|
+
* `useMonitorMapRouter()`. Only relevant when `routerEscapeHatch` is
|
|
30
|
+
* enabled.
|
|
31
|
+
*/
|
|
32
|
+
basePath?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Whether to install the sv-router escape-hatch (`pageshow`/`click`
|
|
35
|
+
* listeners that force a full-page navigation for links outside this
|
|
36
|
+
* map's own routes). Defaults to `true`, preserving the existing
|
|
37
|
+
* full-page-map behavior. When `true` and no `basePath` prop is given,
|
|
38
|
+
* `MapShell` falls back to reading `basePath` from
|
|
39
|
+
* `useMonitorMapRouter()`. Set to `false` to embed `MapShell` inside a
|
|
40
|
+
* host app with its own routing — this also skips calling
|
|
41
|
+
* `useMonitorMapRouter()` entirely, so no `provideMonitorMapRouter(...)`
|
|
42
|
+
* ancestor is required.
|
|
43
|
+
*/
|
|
44
|
+
routerEscapeHatch?: boolean;
|
|
45
|
+
}
|
|
46
|
+
declare const MapShell: import("svelte").Component<MapShellProps, {}, "">;
|
|
47
|
+
type MapShell = ReturnType<typeof MapShell>;
|
|
48
|
+
export default MapShell;
|
package/package.json
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import { onDestroy } from "svelte";
|
|
4
|
-
import
|
|
5
|
-
import Map from "$lib/map/Map.svelte";
|
|
6
|
-
import Menu from "$lib/map/Menu.svelte";
|
|
4
|
+
import MapShell from "$lib/map/MapShell.svelte";
|
|
7
5
|
import MonitorsDisplayOptions from "$lib/monitors/components/MonitorsDisplayOptions.svelte";
|
|
8
6
|
import MapLayersDisplayOptions from "$lib/components/MapLayersDisplayOptions.svelte";
|
|
9
7
|
import MapStyleDisplayOptions from "$lib/map/MapStyleDisplayOptions.svelte";
|
|
10
|
-
import { mapManager } from "$lib/map/map.svelte";
|
|
11
8
|
import { monitorsManager } from "$lib/monitors/monitors.svelte";
|
|
12
9
|
import { monitorsMapIntegration } from "$lib/monitors/monitors-map-integration.svelte";
|
|
13
10
|
import { windMapIntegration } from "$lib/wind/wind.svelte";
|
|
@@ -33,8 +30,6 @@
|
|
|
33
30
|
|
|
34
31
|
const { route, navigate, basePath } = useMonitorMapRouter();
|
|
35
32
|
|
|
36
|
-
const TRANSITION_MS = 300;
|
|
37
|
-
|
|
38
33
|
const integrations: Array<SomeMapIntegration> = [
|
|
39
34
|
baseLayerSeperator,
|
|
40
35
|
collocationSitesMapIntegration,
|
|
@@ -76,22 +71,6 @@
|
|
|
76
71
|
}
|
|
77
72
|
});
|
|
78
73
|
|
|
79
|
-
$effect(() => {
|
|
80
|
-
if (mapManager.map && monitorsManager.initialized) {
|
|
81
|
-
mapManager.map.once("idle", () => disableLoadScreen());
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
$effect(() => {
|
|
86
|
-
if (panelOpen) {
|
|
87
|
-
// Container width snapped to open; resize after layout settles
|
|
88
|
-
requestAnimationFrame(() => mapManager.map?.resize());
|
|
89
|
-
} else {
|
|
90
|
-
// Container width snaps back after the transform transition ends
|
|
91
|
-
setTimeout(() => mapManager.map?.resize(), TRANSITION_MS);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
74
|
// Clear selected icon scale when the detail panel closes
|
|
96
75
|
$effect(() => {
|
|
97
76
|
if (panelOpen) return;
|
|
@@ -101,75 +80,28 @@
|
|
|
101
80
|
onDestroy(() => {
|
|
102
81
|
monitorsManager.autoUpdate.stop();
|
|
103
82
|
});
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* HACK: Fix for escaping sv-router and allowing navigation to other pages,
|
|
107
|
-
* as well as navigating back
|
|
108
|
-
*/
|
|
109
|
-
const rootPath = basePath || "/";
|
|
110
|
-
const knownRoutes = [`${basePath}/monitor/`];
|
|
111
|
-
|
|
112
|
-
window.addEventListener("pageshow", (e) => {
|
|
113
|
-
if (e.persisted) window.location.reload();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
document.addEventListener(
|
|
117
|
-
"click",
|
|
118
|
-
(e) => {
|
|
119
|
-
const anchor = e
|
|
120
|
-
.composedPath()
|
|
121
|
-
.find((el) => el instanceof HTMLAnchorElement) as HTMLAnchorElement;
|
|
122
|
-
if (!anchor) return;
|
|
123
|
-
const { pathname } = new URL(anchor.href);
|
|
124
|
-
const isKnown = pathname === rootPath || knownRoutes.some((r) => pathname.startsWith(r));
|
|
125
|
-
if (!isKnown) {
|
|
126
|
-
e.stopImmediatePropagation();
|
|
127
|
-
window.location.href = anchor.href;
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
{ capture: true }
|
|
131
|
-
);
|
|
132
83
|
</script>
|
|
133
84
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
85
|
+
<MapShell
|
|
86
|
+
{integrations}
|
|
87
|
+
ready={monitorsManager.initialized}
|
|
88
|
+
{panelOpen}
|
|
89
|
+
knownRoutes={[`${basePath}/monitor/`]}
|
|
90
|
+
{basePath}
|
|
91
|
+
>
|
|
92
|
+
{#snippet menu()}
|
|
93
|
+
<MonitorsDisplayOptions />
|
|
94
|
+
<EvStationsDisplayOptions />
|
|
95
|
+
<MapLayersDisplayOptions />
|
|
96
|
+
<MapStyleDisplayOptions />
|
|
97
|
+
{/snippet}
|
|
98
|
+
{#snippet overlays()}
|
|
141
99
|
<div class="pointer-events-none absolute bottom-0 left-0 z-10">
|
|
142
100
|
<MapLegend />
|
|
143
101
|
</div>
|
|
144
|
-
<div class="absolute top-4 left-
|
|
145
|
-
<
|
|
146
|
-
<Menu>
|
|
147
|
-
<MonitorsDisplayOptions />
|
|
148
|
-
<EvStationsDisplayOptions />
|
|
149
|
-
<MapLayersDisplayOptions />
|
|
150
|
-
<MapStyleDisplayOptions />
|
|
151
|
-
</Menu>
|
|
152
|
-
</div>
|
|
153
|
-
<div class="absolute left-16">
|
|
154
|
-
<Search />
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
158
|
-
<div
|
|
159
|
-
class={[
|
|
160
|
-
"panel-containr w-full shrink-0 overflow-hidden",
|
|
161
|
-
panelOpen ? "h-1/2 md:h-full md:w-1/3" : "md:w-0"
|
|
162
|
-
]}
|
|
163
|
-
>
|
|
164
|
-
<div
|
|
165
|
-
class={[
|
|
166
|
-
"panel-contet h-full duration-300 ease-in-out",
|
|
167
|
-
panelOpen
|
|
168
|
-
? "translate-y-0 md:translate-x-0"
|
|
169
|
-
: "translate-y-full md:translate-x-full md:translate-y-0"
|
|
170
|
-
]}
|
|
171
|
-
>
|
|
172
|
-
{@render children()}
|
|
102
|
+
<div class="absolute top-4 left-20 z-10">
|
|
103
|
+
<Search />
|
|
173
104
|
</div>
|
|
174
|
-
|
|
175
|
-
|
|
105
|
+
{/snippet}
|
|
106
|
+
{@render children()}
|
|
107
|
+
</MapShell>
|
package/src/lib/index.ts
CHANGED
|
@@ -55,6 +55,7 @@ export { hmsSmokeMapIntegration } from "./hms/hms-smoke-map-integration.svelte";
|
|
|
55
55
|
|
|
56
56
|
// Components
|
|
57
57
|
export { default as Map } from "./map/Map.svelte";
|
|
58
|
+
export { default as MapShell, type MapShellProps } from "./map/MapShell.svelte";
|
|
58
59
|
export { default as Menu } from "./map/Menu.svelte";
|
|
59
60
|
export {
|
|
60
61
|
default as LoadScreen,
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { SomeMapIntegration } from "$lib/map/integrations/types";
|
|
4
|
+
|
|
5
|
+
export interface MapShellProps {
|
|
6
|
+
/** Map integrations to register, e.g. [monitorsMapIntegration] */
|
|
7
|
+
integrations: Array<SomeMapIntegration>;
|
|
8
|
+
/**
|
|
9
|
+
* True once the caller's own data managers have finished their initial
|
|
10
|
+
* load. The shell hides the load screen once the map is idle AND this
|
|
11
|
+
* is true.
|
|
12
|
+
*/
|
|
13
|
+
ready: boolean;
|
|
14
|
+
/** True when the routed detail panel (`children`) should be visible */
|
|
15
|
+
panelOpen: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Path prefixes that are part of this map's own routed panel (e.g.
|
|
18
|
+
* [`${basePath}/monitor/`]) — internal navigation to these should not
|
|
19
|
+
* trigger the escape-hatch full-page navigation below.
|
|
20
|
+
*/
|
|
21
|
+
knownRoutes?: Array<string>;
|
|
22
|
+
/** Rendered inside the display-options menu toggle */
|
|
23
|
+
menu?: Snippet;
|
|
24
|
+
/** Freeform absolutely-positioned content over the map (legend, search, etc.) */
|
|
25
|
+
overlays?: Snippet;
|
|
26
|
+
/** The routed detail panel content */
|
|
27
|
+
children?: Snippet;
|
|
28
|
+
/**
|
|
29
|
+
* Base path to use for the sv-router escape-hatch's "known" root route.
|
|
30
|
+
* If provided, used directly instead of reading `basePath` from
|
|
31
|
+
* `useMonitorMapRouter()`. Only relevant when `routerEscapeHatch` is
|
|
32
|
+
* enabled.
|
|
33
|
+
*/
|
|
34
|
+
basePath?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Whether to install the sv-router escape-hatch (`pageshow`/`click`
|
|
37
|
+
* listeners that force a full-page navigation for links outside this
|
|
38
|
+
* map's own routes). Defaults to `true`, preserving the existing
|
|
39
|
+
* full-page-map behavior. When `true` and no `basePath` prop is given,
|
|
40
|
+
* `MapShell` falls back to reading `basePath` from
|
|
41
|
+
* `useMonitorMapRouter()`. Set to `false` to embed `MapShell` inside a
|
|
42
|
+
* host app with its own routing — this also skips calling
|
|
43
|
+
* `useMonitorMapRouter()` entirely, so no `provideMonitorMapRouter(...)`
|
|
44
|
+
* ancestor is required.
|
|
45
|
+
*/
|
|
46
|
+
routerEscapeHatch?: boolean;
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<script lang="ts">
|
|
51
|
+
import LoadScreen, { disable as disableLoadScreen } from "$lib/LoadScreen.svelte";
|
|
52
|
+
import Map from "$lib/map/Map.svelte";
|
|
53
|
+
import Menu from "$lib/map/Menu.svelte";
|
|
54
|
+
import { mapManager } from "$lib/map/map.svelte";
|
|
55
|
+
import { useMonitorMapRouter } from "$lib/router-context";
|
|
56
|
+
|
|
57
|
+
let {
|
|
58
|
+
integrations,
|
|
59
|
+
ready,
|
|
60
|
+
panelOpen,
|
|
61
|
+
knownRoutes = [],
|
|
62
|
+
menu,
|
|
63
|
+
overlays,
|
|
64
|
+
children,
|
|
65
|
+
basePath: providedBasePath,
|
|
66
|
+
routerEscapeHatch = true
|
|
67
|
+
}: MapShellProps = $props();
|
|
68
|
+
|
|
69
|
+
const basePath = providedBasePath ?? (routerEscapeHatch ? useMonitorMapRouter().basePath : "");
|
|
70
|
+
const TRANSITION_MS = 300;
|
|
71
|
+
|
|
72
|
+
$effect(() => {
|
|
73
|
+
if (mapManager.map && ready) {
|
|
74
|
+
mapManager.map.once("idle", () => disableLoadScreen());
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
$effect(() => {
|
|
79
|
+
if (panelOpen) {
|
|
80
|
+
// Container width snapped to open; resize after layout settles
|
|
81
|
+
requestAnimationFrame(() => mapManager.map?.resize());
|
|
82
|
+
} else {
|
|
83
|
+
// Container width snaps back after the transform transition ends
|
|
84
|
+
setTimeout(() => mapManager.map?.resize(), TRANSITION_MS);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* HACK: Fix for escaping sv-router and allowing navigation to other pages,
|
|
90
|
+
* as well as navigating back
|
|
91
|
+
*/
|
|
92
|
+
if (routerEscapeHatch) {
|
|
93
|
+
const rootPath = basePath || "/";
|
|
94
|
+
|
|
95
|
+
window.addEventListener("pageshow", (e) => {
|
|
96
|
+
if (e.persisted) window.location.reload();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
document.addEventListener(
|
|
100
|
+
"click",
|
|
101
|
+
(e) => {
|
|
102
|
+
const anchor = e
|
|
103
|
+
.composedPath()
|
|
104
|
+
.find((el) => el instanceof HTMLAnchorElement) as HTMLAnchorElement;
|
|
105
|
+
if (!anchor) return;
|
|
106
|
+
const { pathname } = new URL(anchor.href);
|
|
107
|
+
const isKnown = pathname === rootPath || knownRoutes.some((r) => pathname.startsWith(r));
|
|
108
|
+
if (!isKnown) {
|
|
109
|
+
e.stopImmediatePropagation();
|
|
110
|
+
window.location.href = anchor.href;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{ capture: true }
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<div class="relative flex h-full w-full flex-col md:flex-row">
|
|
119
|
+
<LoadScreen />
|
|
120
|
+
<div class="relative flex-1 overflow-hidden">
|
|
121
|
+
<Map {integrations} />
|
|
122
|
+
{@render overlays?.()}
|
|
123
|
+
{#if menu}
|
|
124
|
+
<div class="absolute top-4 left-4 z-10">
|
|
125
|
+
<Menu>{@render menu()}</Menu>
|
|
126
|
+
</div>
|
|
127
|
+
{/if}
|
|
128
|
+
</div>
|
|
129
|
+
<div
|
|
130
|
+
class={[
|
|
131
|
+
"panel-containr w-full shrink-0 overflow-hidden",
|
|
132
|
+
panelOpen ? "h-1/2 md:h-full md:w-1/3" : "md:w-0"
|
|
133
|
+
]}
|
|
134
|
+
>
|
|
135
|
+
<div
|
|
136
|
+
class={[
|
|
137
|
+
"panel-contet h-full duration-300 ease-in-out",
|
|
138
|
+
panelOpen
|
|
139
|
+
? "translate-y-0 md:translate-x-0"
|
|
140
|
+
: "translate-y-full md:translate-x-full md:translate-y-0"
|
|
141
|
+
]}
|
|
142
|
+
>
|
|
143
|
+
{@render children?.()}
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|