forked from pietervdvn/MapComplete
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathThemeViewGUI.svelte
440 lines (404 loc) · 16.7 KB
/
ThemeViewGUI.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
<script lang="ts">
import { Store, UIEventSource } from "../Logic/UIEventSource"
import { Map as MlMap } from "maplibre-gl"
import MaplibreMap from "./Map/MaplibreMap.svelte"
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
import MapControlButton from "./Base/MapControlButton.svelte"
import ToSvelte from "./Base/ToSvelte.svelte"
import If from "./Base/If.svelte"
import { GeolocationControl } from "./BigComponents/GeolocationControl"
import type { Feature } from "geojson"
import SelectedElementView from "./BigComponents/SelectedElementView.svelte"
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import Filterview from "./BigComponents/Filterview.svelte"
import ThemeViewState from "../Models/ThemeViewState"
import type { MapProperties } from "../Models/MapProperties"
import Geosearch from "./BigComponents/Geosearch.svelte"
import Translations from "./i18n/Translations"
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
import Tr from "./Base/Tr.svelte"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
import FloatOver from "./Base/FloatOver.svelte"
import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
import Constants from "../Models/Constants"
import TabbedGroup from "./Base/TabbedGroup.svelte"
import UserRelatedState from "../Logic/State/UserRelatedState"
import LoginToggle from "./Base/LoginToggle.svelte"
import LoginButton from "./Base/LoginButton.svelte"
import CopyrightPanel from "./BigComponents/CopyrightPanel"
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
import ModalRight from "./Base/ModalRight.svelte"
import { Utils } from "../Utils"
import Hotkeys from "./Base/Hotkeys"
import { VariableUiElement } from "./Base/VariableUIElement"
import SvelteUIElement from "./Base/SvelteUIElement"
import OverlayToggle from "./BigComponents/OverlayToggle.svelte"
import LevelSelector from "./BigComponents/LevelSelector.svelte"
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"
import Svg from "../Svg"
import { ShareScreen } from "./BigComponents/ShareScreen"
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
import type { RasterLayerPolygon } from "../Models/RasterLayers"
import { AvailableRasterLayers } from "../Models/RasterLayers"
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"
import IfHidden from "./Base/IfHidden.svelte"
import { onDestroy } from "svelte"
import { OpenJosm } from "./BigComponents/OpenJosm"
import MapillaryLink from "./BigComponents/MapillaryLink.svelte"
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"
import Loading from "./Base/Loading.svelte"
import StateIndicator from "./BigComponents/StateIndicator.svelte"
export let state: ThemeViewState
let layout = state.layout
let maplibremap: UIEventSource<MlMap> = state.map
let selectedElement: UIEventSource<Feature> = state.selectedElement
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer
const selectedElementView = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data
if (selectedElement === undefined || layer === undefined) {
return undefined
}
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
return undefined
}
const tags = state.featureProperties.getStore(selectedElement.properties.id)
return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags })
},
[selectedLayer]
)
const selectedElementTitle = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data
if (selectedElement === undefined || layer === undefined) {
return undefined
}
const tags = state.featureProperties.getStore(selectedElement.properties.id)
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags })
},
[selectedLayer]
)
let mapproperties: MapProperties = state.mapProperties
let featureSwitches: FeatureSwitchState = state.featureSwitches
let availableLayers = state.availableLayers
let userdetails = state.osmConnection.userDetails
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
let rasterLayerName =
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maplibre.properties.name
onDestroy(
rasterLayer.addCallbackAndRunD((l) => {
rasterLayerName = l.properties.name
})
)
</script>
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
<MaplibreMap map={maplibremap} />
</div>
<div class="pointer-events-none absolute top-0 left-0 w-full">
<!-- Top components -->
<If condition={state.featureSwitches.featureSwitchSearch}>
<div class="pointer-events-auto float-right mt-1 px-1 max-[480px]:w-full sm:m-2">
<Geosearch
bounds={state.mapProperties.bounds}
perLayer={state.perLayer}
{selectedElement}
{selectedLayer}
/>
</div>
</If>
<div class="float-left m-1 flex flex-col sm:mt-2">
<MapControlButton on:click={() => state.guistate.themeIsOpened.setData(true)}>
<div class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2">
<img class="mr-0.5 block h-6 w-6 sm:mr-1 md:mr-2 md:h-8 md:w-8" src={layout.icon} />
<b class="mr-1">
<Tr t={layout.title} />
</b>
</div>
</MapControlButton>
<MapControlButton on:click={() => state.guistate.menuIsOpened.setData(true)}>
<MenuIcon class="h-8 w-8 cursor-pointer" />
</MapControlButton>
{#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()}
<MapControlButton
on:click={() => {
selectedLayer.setData(currentViewLayer)
selectedElement.setData(state.currentView.features?.data?.[0])
}}
>
<ToSvelte
construct={() => currentViewLayer.defaultIcon().SetClass("w-8 h-8 cursor-pointer")}
/>
</MapControlButton>
{/if}
<ToSvelte
construct={() => new ExtraLinkButton(state, layout.extraLink).SetClass("pointer-events-auto")}
/>
<If condition={state.featureSwitchIsTesting}>
<div class="alert w-fit">Testmode</div>
</If>
</div>
<div class="flex w-full justify-center">
<!-- Flex and w-full are needed for the positioning -->
<!-- Centermessage -->
<StateIndicator {state} />
</div>
</div>
<div class="pointer-events-none absolute bottom-0 left-0 mb-4 w-screen">
<!-- bottom controls -->
<div class="flex w-full items-end justify-between px-4">
<div class="flex">
<!-- bottom left elements -->
<OpenBackgroundSelectorButton hideTooltip={true} {state} />
<a
class="bg-black-transparent pointer-events-auto h-fit max-h-12 cursor-pointer self-end overflow-hidden rounded-2xl pl-1 pr-2 text-white opacity-50 hover:opacity-100"
on:click={() => {
state.guistate.themeViewTab.setData("copyright")
state.guistate.themeIsOpened.setData(true)
}}
>
© OpenStreetMap, <span class="w-24">{rasterLayerName}</span>
</a>
</div>
<div class="flex flex-col items-end">
<!-- bottom right elements -->
<If condition={state.floors.map((f) => f.length > 1)}>
<div class="pointer-events-auto mr-0.5">
<LevelSelector
floors={state.floors}
layerState={state.layerState}
zoom={state.mapProperties.zoom}
/>
</div>
</If>
<MapControlButton on:click={() => mapproperties.zoom.update((z) => z + 1)}>
<ToSvelte construct={Svg.plus_svg().SetClass("w-8 h-8")} />
</MapControlButton>
<MapControlButton on:click={() => mapproperties.zoom.update((z) => z - 1)}>
<ToSvelte construct={Svg.min_svg().SetClass("w-8 h-8")} />
</MapControlButton>
<If condition={featureSwitches.featureSwitchGeolocation}>
<MapControlButton>
<ToSvelte
construct={new GeolocationControl(state.geolocation, mapproperties).SetClass(
"block w-8 h-8"
)}
/>
</MapControlButton>
</If>
</div>
</div>
</div>
<If
condition={selectedElementView.map(
(v) =>
v !== undefined && selectedLayer.data !== undefined && !selectedLayer.data.popupInFloatover,
[selectedLayer]
)}
>
<!-- right modal with the selected element view -->
<ModalRight
on:close={() => {
selectedElement.setData(undefined)
}}
>
<div class="normal-background absolute flex h-full w-full flex-col">
<ToSvelte construct={new VariableUiElement(selectedElementTitle)}>
<!-- Title -->
</ToSvelte>
<ToSvelte construct={new VariableUiElement(selectedElementView).SetClass("overflow-auto")}>
<!-- Main view -->
</ToSvelte>
</div>
</ModalRight>
</If>
<If
condition={selectedElementView.map(
(v) =>
v !== undefined && selectedLayer.data !== undefined && selectedLayer.data.popupInFloatover,
[selectedLayer]
)}
>
<!-- Floatover with the selected element, if applicable -->
<FloatOver
on:close={() => {
selectedElement.setData(undefined)
}}
>
<ToSvelte construct={new VariableUiElement(selectedElementView)} />
</FloatOver>
</If>
<If condition={state.guistate.themeIsOpened}>
<!-- Theme menu -->
<FloatOver>
<span slot="close-button"><!-- Disable the close button --></span>
<TabbedGroup tab={state.guistate.themeViewTabIndex}>
<div slot="post-tablist">
<XCircleIcon
class="mr-2 h-8 w-8"
on:click={() => state.guistate.themeIsOpened.setData(false)}
/>
</div>
<div class="flex" slot="title0">
<img class="block h-4 w-4" src={layout.icon} />
<Tr t={layout.title} />
</div>
<div class="m-4 h-full" slot="content0">
<ThemeIntroPanel {state} />
</div>
<div class="flex" slot="title1">
<If condition={state.featureSwitches.featureSwitchFilter}>
<ToSvelte construct={Svg.filter_svg().SetClass("w-4 h-4")} />
<Tr t={Translations.t.general.menu.filter} />
</If>
</div>
<div class="m-2 flex flex-col" slot="content1">
{#each layout.layers as layer}
<Filterview
zoomlevel={state.mapProperties.zoom}
filteredLayer={state.layerState.filteredLayers.get(layer.id)}
highlightedLayer={state.guistate.highlightedLayerInFilters}
/>
{/each}
{#each layout.tileLayerSources as tilesource}
<OverlayToggle
layerproperties={tilesource}
state={state.overlayLayerStates.get(tilesource.id)}
highlightedLayer={state.guistate.highlightedLayerInFilters}
zoomlevel={state.mapProperties.zoom}
/>
{/each}
</div>
<div class="flex" slot="title2">
<If condition={state.featureSwitches.featureSwitchEnableExport}>
<ToSvelte construct={Svg.download_svg().SetClass("w-4 h-4")} />
<Tr t={Translations.t.general.download.title} />
</If>
</div>
<div class="m-4" slot="content2">
<DownloadPanel {state} />
</div>
<div slot="title3">
<Tr t={Translations.t.general.attribution.title} />
</div>
<ToSvelte construct={() => new CopyrightPanel(state)} slot="content3" />
<div slot="title4">
<Tr t={Translations.t.general.sharescreen.title} />
</div>
<div class="m-2" slot="content4">
<ToSvelte construct={() => new ShareScreen(state)} />
</div>
</TabbedGroup>
</FloatOver>
</If>
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
<!-- background layer selector -->
<FloatOver on:close={() => state.guistate.backgroundLayerSelectionIsOpened.setData(false)}>
<div class="h-full p-2">
<RasterLayerOverview
{availableLayers}
map={state.map}
mapproperties={state.mapProperties}
userstate={state.userRelatedState}
visible={state.guistate.backgroundLayerSelectionIsOpened}
/>
</div>
</FloatOver>
</IfHidden>
<If condition={state.guistate.menuIsOpened}>
<!-- Menu page -->
<FloatOver>
<span slot="close-button"><!-- Hide the default close button --></span>
<TabbedGroup tab={state.guistate.menuViewTabIndex}>
<div slot="post-tablist">
<XCircleIcon
class="mr-2 h-8 w-8"
on:click={() => state.guistate.menuIsOpened.setData(false)}
/>
</div>
<div class="flex" slot="title0">
<Tr t={Translations.t.general.menu.aboutMapComplete} />
</div>
<div class="links-as-button links-w-full m-2 flex flex-col gap-y-1" slot="content0">
<Tr t={Translations.t.general.aboutMapComplete.intro} />
<a class="flex" href={Utils.HomepageLink()}>
<img class="h-6 w-6" src="./assets/svg/add.svg" />
<Tr t={Translations.t.general.backToIndex} />
</a>
<a class="flex" href="https://github.com/pietervdvn/MapComplete/issues" target="_blank">
<img class="h-6 w-6" src="./assets/svg/bug.svg" />
<Tr t={Translations.t.general.attribution.openIssueTracker} />
</a>
<a class="flex" href="https://en.osm.town/@MapComplete" target="_blank">
<img class="h-6 w-6" src="./assets/svg/mastodon.svg" />
<Tr t={Translations.t.general.attribution.followOnMastodon} />
</a>
<a class="flex" href="https://liberapay.com/pietervdvn/" target="_blank">
<img class="h-6 w-6" src="./assets/svg/liberapay.svg" />
<Tr t={Translations.t.general.attribution.donate} />
</a>
<a class="flex" href={Utils.OsmChaLinkFor(7)} target="_blank">
<img class="h-6 w-6" src="./assets/svg/statistics.svg" />
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: "MapComplete" })} />
</a>
{Constants.vNumber}
</div>
<div class="flex" slot="title1">
<CogIcon class="h-6 w-6" />
<Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})} />
</div>
<div class="links-as-button" slot="content1">
<!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it -->
<LoginToggle {state}>
<div class="flex flex-col" slot="not-logged-in">
<Tr class="alert" t={Translations.t.userinfo.notLoggedIn} />
<LoginButton clss="primary" osmConnection={state.osmConnection} />
</div>
<SelectedElementView
highlightedRendering={state.guistate.highlightedUserSetting}
layer={UserRelatedState.usersettingsConfig}
selectedElement={{
type: "Feature",
properties: {},
geometry: { type: "Point", coordinates: [0, 0] },
}}
{state}
tags={state.userRelatedState.preferencesAsTags}
/>
</LoginToggle>
</div>
<div class="flex" slot="title2">
<ToSvelte construct={Svg.community_svg().SetClass("w-6 h-6")} />
Get in touch with others
</div>
<div class="m-2" slot="content2">
<CommunityIndexView location={state.mapProperties.location} />
</div>
<div class="flex" slot="title3">
<EyeIcon class="w-6" />
<Tr t={Translations.t.privacy.title} />
</div>
<div class="m-2" slot="content3">
<ToSvelte construct={() => new PrivacyPolicy()} />
</div>
<Tr slot="title4" t={Translations.t.advanced.title} />
<div class="m-2 flex flex-col" slot="content4">
<OpenIdEditor mapProperties={state.mapProperties} />
<ToSvelte
construct={() =>
new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}
/>
<MapillaryLink mapProperties={state.mapProperties} />
<ToSvelte construct={Hotkeys.generateDocumentationDynamic} />
</div>
</TabbedGroup>
</FloatOver>
</If>