forked from MapComplete/MapComplete
Refactoring: new background selector
This commit is contained in:
parent
5427a4cb05
commit
82093ffdf4
25 changed files with 658 additions and 269 deletions
|
@ -10,7 +10,7 @@
|
|||
|
||||
<div class="absolute top-0 right-0 w-screen h-screen p-4 md:p-6" style="background-color: #00000088">
|
||||
<div class="content normal-background">
|
||||
<div class="rounded-xl">
|
||||
<div class="rounded-xl h-full">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="close-button">
|
||||
|
|
33
UI/Base/IfHidden.svelte
Normal file
33
UI/Base/IfHidden.svelte
Normal file
|
@ -0,0 +1,33 @@
|
|||
<script lang="ts">
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {onDestroy} from "svelte";
|
||||
|
||||
/**
|
||||
* Functions as 'If', but uses 'display:hidden' instead.
|
||||
*/
|
||||
export let condition: UIEventSource<boolean>;
|
||||
let _c = condition.data;
|
||||
let hasBeenShownPositive = false
|
||||
let hasBeenShownNegative = false
|
||||
onDestroy(condition.addCallbackAndRun(c => {
|
||||
/* Do _not_ abbreviate this as `.addCallback(c => _c = c)`. This is the same as writing `.addCallback(c => {return _c = c})`,
|
||||
which will _unregister_ the callback if `c = true`! */
|
||||
hasBeenShownPositive = hasBeenShownPositive || c
|
||||
hasBeenShownNegative = hasBeenShownNegative || !c
|
||||
_c = c;
|
||||
return false
|
||||
}))
|
||||
|
||||
</script>
|
||||
|
||||
{#if hasBeenShownPositive}
|
||||
<span class={_c ? "" : "hidden"}>
|
||||
<slot/>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#if hasBeenShownNegative}
|
||||
<span class={_c ? "hidden" : ""}>
|
||||
<slot name="else"/>
|
||||
</span>
|
||||
{/if}
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
{#if context}
|
||||
{#if $linkOnMobile}
|
||||
<a href={LinkToWeblate.hrefToWeblate($language, context)} target="_blank" class="mx-1">
|
||||
<a href={LinkToWeblate.hrefToWeblate($language, context)} target="_blank" class="mx-1 weblate-link">
|
||||
<img src="./assets/svg/translate.svg" class="w-3 h-3 rounded-full font-gray" />
|
||||
</a>
|
||||
{:else if $linkToWeblate}
|
||||
<a href={LinkToWeblate.hrefToWeblate($language, context)} class="hidden-on-mobile mx-1" target="_blank">
|
||||
<a href={LinkToWeblate.hrefToWeblate($language, context)} class="weblate-link hidden-on-mobile mx-1" target="_blank">
|
||||
<img src="./assets/svg/translate.svg" class="w-3 h-3 rounded-full font-gray" />
|
||||
</a>
|
||||
{/if}
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
import type {RasterLayerPolygon} from "../../Models/RasterLayers";
|
||||
import {AvailableRasterLayers} from "../../Models/RasterLayers";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import {onDestroy} from "svelte";
|
||||
import {createEventDispatcher, onDestroy} from "svelte";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Svg from "../../Svg";
|
||||
import {Map as MlMap} from "maplibre-gl"
|
||||
import MaplibreMap from "../Map/MaplibreMap.svelte";
|
||||
import {MapLibreAdaptor} from "../Map/MapLibreAdaptor";
|
||||
import type {MapProperties} from "../../Models/MapProperties";
|
||||
import OverlayMap from "../Map/OverlayMap.svelte";
|
||||
import RasterLayerPicker from "../Map/RasterLayerPicker.svelte";
|
||||
|
||||
export let mapproperties: MapProperties
|
||||
export let normalMap: UIEventSource<MlMap>
|
||||
|
@ -28,63 +28,55 @@
|
|||
*/
|
||||
export let availableRasterLayers: Store<RasterLayerPolygon[]>
|
||||
|
||||
let altmap: UIEventSource<MlMap> = new UIEventSource(undefined)
|
||||
let altproperties = new MapLibreAdaptor(altmap, {zoom: UIEventSource.feedFrom(mapproperties.zoom)})
|
||||
altproperties.allowMoving.setData(false)
|
||||
altproperties.allowZooming.setData(false)
|
||||
let altmap0: UIEventSource<MlMap> = new UIEventSource(undefined)
|
||||
let altproperties0 = new MapLibreAdaptor(altmap0, {zoom: altproperties.zoom})
|
||||
// altproperties0.allowMoving.setData(false)
|
||||
// altproperties0.allowZooming.setData(false)
|
||||
let raster0 = new UIEventSource<RasterLayerPolygon>(undefined)
|
||||
|
||||
let raster1 = new UIEventSource<RasterLayerPolygon>(undefined)
|
||||
|
||||
let currentLayer: RasterLayerPolygon
|
||||
|
||||
function updatedAltLayer() {
|
||||
const available = availableRasterLayers.data
|
||||
const current = rasterLayer.data
|
||||
const defaultLayer = AvailableRasterLayers.maplibre
|
||||
const firstOther = available.find(l => l !== current && l !== defaultLayer)
|
||||
|
||||
altproperties.rasterLayer.setData(firstOther)
|
||||
const secondOther = available.find(l => l !== current && l !== firstOther && l !== defaultLayer)
|
||||
altproperties0.rasterLayer.setData(secondOther)
|
||||
|
||||
const firstOther = available.find(l => l !== defaultLayer)
|
||||
const secondOther = available.find(l => l !== defaultLayer && l !== firstOther)
|
||||
raster0.setData(firstOther === current ? defaultLayer : firstOther)
|
||||
raster1.setData(secondOther === current ? defaultLayer : secondOther)
|
||||
}
|
||||
|
||||
updatedAltLayer()
|
||||
onDestroy(mapproperties.rasterLayer.addCallbackAndRunD(updatedAltLayer))
|
||||
onDestroy(availableRasterLayers.addCallbackAndRunD(updatedAltLayer))
|
||||
onDestroy(rasterLayer.addCallbackAndRunD(updatedAltLayer))
|
||||
|
||||
function pixelCenterOf(map: UIEventSource<MlMap>): [number, number] {
|
||||
const rect = map?.data?.getCanvas()?.getBoundingClientRect()
|
||||
if (!rect) {
|
||||
return undefined
|
||||
function use(rasterLayer: UIEventSource<RasterLayerPolygon>): (() => void) {
|
||||
return () => {
|
||||
currentLayer = undefined
|
||||
mapproperties.rasterLayer.setData(rasterLayer.data)
|
||||
}
|
||||
const x = (rect.left + rect.right) / 2
|
||||
const y = (rect.top + rect.bottom) / 2
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
mapproperties.location.addCallbackAndRunD(({lon, lat}) => {
|
||||
if (!normalMap.data || !altmap.data) {
|
||||
return
|
||||
}
|
||||
const altMapCenter = pixelCenterOf(altmap)
|
||||
const c = normalMap.data.unproject(altMapCenter)
|
||||
altproperties.location.setData({lon: c.lng, lat: c.lat})
|
||||
|
||||
const altMapCenter0 = pixelCenterOf(altmap0)
|
||||
const c0 = normalMap.data.unproject(altMapCenter0)
|
||||
altproperties0.location.setData({lon: c0.lng, lat: c0.lat})
|
||||
})
|
||||
const dispatch = createEventDispatcher<{ copyright_clicked }>()
|
||||
|
||||
</script>
|
||||
<div class="flex">
|
||||
<div class="w-32 h-32 overflow-hidden border-interactive">
|
||||
<MaplibreMap map={altmap}/>
|
||||
<div class="flex items-end opacity-50 hover:opacity-100">
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<button class="w-16 h-12 md:w-16 md:h-16 overflow-hidden m-0 p-0"
|
||||
on:click={use(raster0)}>
|
||||
<OverlayMap placedOverMap={normalMap} placedOverMapProperties={mapproperties} rasterLayer={raster0}/>
|
||||
</button>
|
||||
<button class="w-16 h-12 md:w-16 md:h-16 overflow-hidden m-0 p-0 " on:click={use(raster1)}>
|
||||
<OverlayMap placedOverMap={normalMap} placedOverMapProperties={mapproperties} rasterLayer={raster1}/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-32 h-32 overflow-hidden border-interactive">
|
||||
<MaplibreMap map={altmap0}/>
|
||||
</div>
|
||||
<div class="low-interaction flex flex-col">
|
||||
<b>Current background:</b>
|
||||
<Tr t={Translations.T(name)}/>
|
||||
<div class="text-sm flex flex-col gap-y-1 h-fit ml-1">
|
||||
|
||||
<div class="low-interaction rounded p-1 w-64">
|
||||
<RasterLayerPicker availableLayers={availableRasterLayers} value={mapproperties.rasterLayer}></RasterLayerPicker>
|
||||
</div>
|
||||
|
||||
<button class="small" on:click={() => dispatch("copyright_clicked")}>
|
||||
© OpenStreetMap
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -22,6 +22,7 @@ import ContributorCount from "../../Logic/ContributorCount"
|
|||
import Img from "../Base/Img"
|
||||
import { TypedTranslation } from "../i18n/Translation"
|
||||
import GeoIndexedStore from "../../Logic/FeatureSource/Actors/GeoIndexedStore"
|
||||
import {RasterLayerPolygon} from "../../Models/RasterLayers";
|
||||
|
||||
export class OpenIdEditor extends VariableUiElement {
|
||||
constructor(
|
||||
|
@ -113,7 +114,7 @@ export default class CopyrightPanel extends Combine {
|
|||
|
||||
constructor(state: {
|
||||
layout: LayoutConfig
|
||||
mapProperties: { bounds: Store<BBox> }
|
||||
mapProperties: { readonly bounds: Store<BBox>, readonly rasterLayer: Store<RasterLayerPolygon> }
|
||||
osmConnection: OsmConnection
|
||||
dataIsLoading: Store<boolean>
|
||||
perLayer: ReadonlyMap<string, GeoIndexedStore>
|
||||
|
@ -173,6 +174,29 @@ export default class CopyrightPanel extends Combine {
|
|||
[
|
||||
new Title(t.attributionTitle),
|
||||
t.attributionContent,
|
||||
|
||||
new VariableUiElement(state.mapProperties.rasterLayer.mapD(layer => {
|
||||
const props = layer.properties
|
||||
const attrUrl = props.attribution?.url
|
||||
const attrText = props.attribution?.text
|
||||
|
||||
let bgAttr: BaseUIElement | string = undefined
|
||||
if(attrText && attrUrl){
|
||||
bgAttr = "<a href='"+attrUrl+"' target='_blank'>"+attrText+"</a>"
|
||||
}else if(attrUrl){
|
||||
bgAttr = attrUrl
|
||||
}else{
|
||||
bgAttr = attrText
|
||||
}
|
||||
if(bgAttr){
|
||||
return Translations.t.general.attribution.attributionBackgroundLayerWithCopyright.Subs({
|
||||
name: props.name,
|
||||
copyright: bgAttr
|
||||
})
|
||||
}
|
||||
return Translations.t.general.attribution.attributionBackgroundLayer.Subs(props)
|
||||
})),
|
||||
|
||||
maintainer,
|
||||
dataContributors,
|
||||
CopyrightPanel.CodeContributors(contributors, t.codeContributionsBy),
|
||||
|
|
|
@ -1,100 +1,116 @@
|
|||
<script lang="ts">
|
||||
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import type { Feature } from "geojson";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||
import Svg from "../../Svg.js";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Loading from "../Base/Loading.svelte";
|
||||
import Hotkeys from "../Base/Hotkeys";
|
||||
import { Geocoding } from "../../Logic/Osm/Geocoding";
|
||||
import { BBox } from "../../Logic/BBox";
|
||||
import { GeoIndexedStoreForLayer } from "../../Logic/FeatureSource/Actors/GeoIndexedStore";
|
||||
import {createEventDispatcher} from "svelte";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import type {Feature} from "geojson";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||
import Svg from "../../Svg.js";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Loading from "../Base/Loading.svelte";
|
||||
import Hotkeys from "../Base/Hotkeys";
|
||||
import {Geocoding} from "../../Logic/Osm/Geocoding";
|
||||
import {BBox} from "../../Logic/BBox";
|
||||
import {GeoIndexedStoreForLayer} from "../../Logic/FeatureSource/Actors/GeoIndexedStore";
|
||||
import {createEventDispatcher, onDestroy} from "svelte";
|
||||
|
||||
export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined;
|
||||
export let bounds: UIEventSource<BBox>;
|
||||
export let selectedElement: UIEventSource<Feature> | undefined = undefined;
|
||||
export let selectedLayer: UIEventSource<LayerConfig> | undefined = undefined;
|
||||
export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined;
|
||||
export let bounds: UIEventSource<BBox>;
|
||||
export let selectedElement: UIEventSource<Feature> | undefined = undefined;
|
||||
export let selectedLayer: UIEventSource<LayerConfig> | undefined = undefined;
|
||||
|
||||
let searchContents: string = undefined;
|
||||
let searchContents: string = ""
|
||||
export let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
||||
onDestroy(triggerSearch.addCallback(_ => {
|
||||
console.log("TriggerRun pinged")
|
||||
performSearch()
|
||||
}))
|
||||
|
||||
let isRunning: boolean = false;
|
||||
let isRunning: boolean = false;
|
||||
|
||||
let inputElement: HTMLInputElement;
|
||||
let inputElement: HTMLInputElement;
|
||||
|
||||
let feedback: string = undefined;
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ ctrl: "F" },
|
||||
Translations.t.hotkeyDocumentation.selectSearch,
|
||||
() => {
|
||||
inputElement?.focus();
|
||||
inputElement?.select();
|
||||
}
|
||||
);
|
||||
|
||||
const dispatch = createEventDispatcher<{searchCompleted}>()
|
||||
|
||||
async function performSearch() {
|
||||
try {
|
||||
isRunning = true;
|
||||
searchContents = searchContents?.trim() ?? "";
|
||||
if (searchContents === "") {
|
||||
return;
|
||||
}
|
||||
const result = await Geocoding.Search(searchContents, bounds.data);
|
||||
if (result.length == 0) {
|
||||
feedback = Translations.t.general.search.nothing.txt;
|
||||
return;
|
||||
}
|
||||
const poi = result[0];
|
||||
const [lat0, lat1, lon0, lon1] = poi.boundingbox;
|
||||
bounds.set(new BBox([[lon0, lat0], [lon1, lat1]]).pad(0.01));
|
||||
if (perLayer !== undefined) {
|
||||
const id = poi.osm_type + "/" + poi.osm_id;
|
||||
const layers = Array.from(perLayer?.values() ?? []);
|
||||
for (const layer of layers) {
|
||||
const found = layer.features.data.find(f => f.properties.id === id);
|
||||
selectedElement?.setData(found);
|
||||
selectedLayer?.setData(layer.layer.layerDef);
|
||||
let feedback: string = undefined;
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ctrl: "F"},
|
||||
Translations.t.hotkeyDocumentation.selectSearch,
|
||||
() => {
|
||||
inputElement?.focus();
|
||||
inputElement?.select();
|
||||
}
|
||||
);
|
||||
|
||||
const dispatch = createEventDispatcher<{ searchCompleted, searchIsValid: boolean }>()
|
||||
$: {
|
||||
if (!searchContents?.trim()) {
|
||||
dispatch("searchIsValid", false)
|
||||
}else{
|
||||
dispatch("searchIsValid", true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function performSearch() {
|
||||
try {
|
||||
isRunning = true;
|
||||
searchContents = searchContents?.trim() ?? "";
|
||||
|
||||
if (searchContents === "") {
|
||||
return;
|
||||
}
|
||||
const result = await Geocoding.Search(searchContents, bounds.data);
|
||||
if (result.length == 0) {
|
||||
feedback = Translations.t.general.search.nothing.txt;
|
||||
return;
|
||||
}
|
||||
const poi = result[0];
|
||||
const [lat0, lat1, lon0, lon1] = poi.boundingbox;
|
||||
bounds.set(new BBox([[lon0, lat0], [lon1, lat1]]).pad(0.01));
|
||||
if (perLayer !== undefined) {
|
||||
const id = poi.osm_type + "/" + poi.osm_id;
|
||||
const layers = Array.from(perLayer?.values() ?? []);
|
||||
for (const layer of layers) {
|
||||
const found = layer.features.data.find(f => f.properties.id === id);
|
||||
selectedElement?.setData(found);
|
||||
selectedLayer?.setData(layer.layer.layerDef);
|
||||
|
||||
}
|
||||
}
|
||||
searchContents = ""
|
||||
dispatch("searchIsValid", false)
|
||||
dispatch("searchCompleted")
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
feedback = Translations.t.general.search.error.txt;
|
||||
} finally {
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
dispatch("searchCompleted")
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
feedback = Translations.t.general.search.error.txt;
|
||||
} finally {
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="flex normal-background rounded-full pl-2 justify-between">
|
||||
<form class="w-full">
|
||||
<form class="w-full">
|
||||
|
||||
{#if isRunning}
|
||||
<Loading>{Translations.t.general.search.searching}</Loading>
|
||||
{:else if feedback !== undefined}
|
||||
<div class="alert" on:click={() => feedback = undefined}>
|
||||
{feedback}
|
||||
</div>
|
||||
{:else }
|
||||
<input
|
||||
type="search"
|
||||
class="w-full"
|
||||
bind:this={inputElement}
|
||||
on:keypress={keypr => keypr.key === "Enter" ? performSearch() : undefined}
|
||||
{#if isRunning}
|
||||
<Loading>{Translations.t.general.search.searching}</Loading>
|
||||
{:else if feedback !== undefined}
|
||||
<div class="alert" on:click={() => feedback = undefined}>
|
||||
{feedback}
|
||||
</div>
|
||||
{:else }
|
||||
<input
|
||||
type="search"
|
||||
class="w-full"
|
||||
bind:this={inputElement}
|
||||
on:keypress={keypr => keypr.key === "Enter" ? performSearch() : undefined}
|
||||
|
||||
bind:value={searchContents}
|
||||
placeholder={Translations.t.general.search.search}>
|
||||
{/if}
|
||||
bind:value={searchContents}
|
||||
placeholder={Translations.t.general.search.search}>
|
||||
{/if}
|
||||
|
||||
</form>
|
||||
<div class="w-6 h-6 self-end" on:click={performSearch}>
|
||||
<ToSvelte construct={Svg.search_svg}></ToSvelte>
|
||||
</div>
|
||||
</form>
|
||||
<div class="w-6 h-6 self-end" on:click={performSearch}>
|
||||
<ToSvelte construct={Svg.search_svg}></ToSvelte>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
export let onMainScreen: boolean = true
|
||||
|
||||
const prefix = "mapcomplete-hidden-theme-"
|
||||
const hiddenThemes: LayoutInformation[] = themeOverview["default"].filter(
|
||||
const hiddenThemes: LayoutInformation[] = themeOverview.filter(
|
||||
(layout) => layout.hideFromOverview
|
||||
)
|
||||
const userPreferences = state.osmConnection.preferencesHandler.preferences
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
{layer}></TagRenderingAnswer>
|
||||
</h3>
|
||||
|
||||
<div class="title-icons flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2 gap-x-0.5 p-1 links-as-button">
|
||||
<div class="no-weblate title-icons flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2 gap-x-0.5 p-1 links-as-button">
|
||||
{#each layer.titleIcons as titleIconConfig}
|
||||
{#if (titleIconConfig.condition?.matchesProperties(_tags) ?? true) && (titleIconConfig.metacondition?.matchesProperties({..._metatags, ..._tags}) ?? true) && titleIconConfig.IsKnown(_tags)}
|
||||
<div class="w-8 h-8 flex items-center">
|
||||
|
|
87
UI/BigComponents/ThemeIntroPanel.svelte
Normal file
87
UI/BigComponents/ThemeIntroPanel.svelte
Normal file
|
@ -0,0 +1,87 @@
|
|||
<script lang="ts">
|
||||
import Translations from "../i18n/Translations";
|
||||
import Svg from "../../Svg";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import NextButton from "../Base/NextButton.svelte";
|
||||
import Geosearch from "./Geosearch.svelte";
|
||||
import IfNot from "../Base/IfNot.svelte";
|
||||
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||
import ThemeViewState from "../../Models/ThemeViewState";
|
||||
import If from "../Base/If.svelte";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {SearchIcon} from "@rgossiaux/svelte-heroicons/solid";
|
||||
|
||||
/**
|
||||
* The theme introduction panel
|
||||
*/
|
||||
export let state: ThemeViewState
|
||||
let layout = state.layout
|
||||
let selectedElement = state.selectedElement
|
||||
let selectedLayer = state.selectedLayer
|
||||
|
||||
|
||||
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
||||
let searchEnabled = false
|
||||
|
||||
function jumpToCurrentLocation() {
|
||||
const glstate = state.geolocation.geolocationState
|
||||
if (glstate.currentGPSLocation.data !== undefined) {
|
||||
const c: GeolocationCoordinates = glstate.currentGPSLocation.data
|
||||
state.guistate.themeIsOpened.setData(false)
|
||||
const coor = {lon: c.longitude, lat: c.latitude}
|
||||
state.mapProperties.location.setData(coor)
|
||||
}
|
||||
if (glstate.permission.data !== "granted") {
|
||||
glstate.requestPermission()
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<Tr t={layout.description}></Tr>
|
||||
<Tr t={Translations.t.general.welcomeExplanation.general}/>
|
||||
{#if layout.layers.some((l) => l.presets?.length > 0)}
|
||||
<If condition={state.featureSwitches.featureSwitchAddNew}>
|
||||
<Tr t={Translations.t.general.welcomeExplanation.addNew}/>
|
||||
</If>
|
||||
{/if}
|
||||
|
||||
<!--toTheMap,
|
||||
loginStatus.SetClass("block mt-6 pt-2 md:border-t-2 border-dotted border-gray-400"),
|
||||
-->
|
||||
<Tr t={layout.descriptionTail}></Tr>
|
||||
<NextButton clss="primary w-full" on:click={() => state.guistate.themeIsOpened.setData(false)}>
|
||||
<div class="flex justify-center w-full text-2xl">
|
||||
<Tr t={Translations.t.general.openTheMap}/>
|
||||
</div>
|
||||
</NextButton>
|
||||
|
||||
<div class="flex w-full flex-wrap sm:flex-nowrap">
|
||||
<IfNot condition={state.geolocation.geolocationState.permission.map(p => p === "denied")}>
|
||||
<button class="flex w-full gap-x-2 items-center" on:click={jumpToCurrentLocation}>
|
||||
<ToSvelte construct={Svg.crosshair_svg().SetClass("w-8 h-8")}/>
|
||||
<Tr t={Translations.t.general.openTheMapAtGeolocation}/>
|
||||
</button>
|
||||
</IfNot>
|
||||
|
||||
<div class="flex gap-x-2 items-center w-full border rounded .button p-2 m-1 low-interaction">
|
||||
<div class="w-full">
|
||||
<Geosearch bounds={state.mapProperties.bounds}
|
||||
on:searchCompleted={() => state.guistate.themeIsOpened.setData(false)}
|
||||
on:searchIsValid={isValid => {searchEnabled= isValid}}
|
||||
perLayer={state.perLayer}
|
||||
{selectedElement}
|
||||
{selectedLayer}
|
||||
{triggerSearch}>
|
||||
</Geosearch>
|
||||
</div>
|
||||
<button class={"flex gap-x-2 justify-between items-center "+(searchEnabled ? "" : "disabled")}
|
||||
on:click={() => triggerSearch.ping()}>
|
||||
<Tr t={Translations.t.general.search.searchShort}/>
|
||||
<SearchIcon class="w-6 h-6"></SearchIcon>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -275,7 +275,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
return new Promise<Blob>((resolve) => drawOn.toBlob((data) => resolve(data)))
|
||||
}
|
||||
|
||||
private updateStores() {
|
||||
private updateStores(): void {
|
||||
const map = this._maplibreMap.data
|
||||
if (!map) {
|
||||
return
|
||||
|
@ -293,7 +293,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
this.bounds.setData(bbox)
|
||||
}
|
||||
|
||||
private SetZoom(z: number) {
|
||||
private SetZoom(z: number): void {
|
||||
const map = this._maplibreMap.data
|
||||
if (!map || z === undefined) {
|
||||
return
|
||||
|
@ -303,7 +303,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
}
|
||||
}
|
||||
|
||||
private MoveMapToCurrentLoc(loc: { lat: number; lon: number }) {
|
||||
private MoveMapToCurrentLoc(loc: { lat: number; lon: number }): void {
|
||||
const map = this._maplibreMap.data
|
||||
if (!map || loc === undefined) {
|
||||
return
|
||||
|
@ -325,7 +325,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
}
|
||||
}
|
||||
|
||||
private removeCurrentLayer(map: MLMap) {
|
||||
private removeCurrentLayer(map: MLMap): void {
|
||||
if (this._currentRasterLayer) {
|
||||
// hide the previous layer
|
||||
map.removeLayer(this._currentRasterLayer)
|
||||
|
@ -333,7 +333,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
}
|
||||
}
|
||||
|
||||
private async setBackground() {
|
||||
private async setBackground(): Promise<void> {
|
||||
const map = this._maplibreMap.data
|
||||
if (!map) {
|
||||
return
|
||||
|
@ -363,6 +363,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
|
||||
map.addSource(background.id, MapLibreAdaptor.prepareWmsSource(background))
|
||||
|
||||
map.resize()
|
||||
map.addLayer(
|
||||
{
|
||||
id: background.id,
|
||||
|
|
69
UI/Map/OverlayMap.svelte
Normal file
69
UI/Map/OverlayMap.svelte
Normal file
|
@ -0,0 +1,69 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* The overlay map is a bit a weird map:
|
||||
* it is a HTML-component which is intended to be placed _over_ another map.
|
||||
* It will align itself in order to seamlessly show the same location; but possibly in a different style
|
||||
*/
|
||||
import MaplibreMap from "./MaplibreMap.svelte";
|
||||
import {Store, UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {Map as MlMap} from "maplibre-gl";
|
||||
import {MapLibreAdaptor} from "./MapLibreAdaptor";
|
||||
import type {MapProperties} from "../../Models/MapProperties";
|
||||
import {onDestroy} from "svelte";
|
||||
import type {RasterLayerPolygon} from "../../Models/RasterLayers";
|
||||
|
||||
|
||||
export let placedOverMapProperties: MapProperties
|
||||
export let placedOverMap: UIEventSource<MlMap>
|
||||
|
||||
export let rasterLayer: UIEventSource<RasterLayerPolygon>
|
||||
|
||||
export let visible: Store<boolean> = undefined
|
||||
let altmap: UIEventSource<MlMap> = new UIEventSource(undefined)
|
||||
let altproperties = new MapLibreAdaptor(altmap, {
|
||||
rasterLayer,
|
||||
zoom: UIEventSource.feedFrom(placedOverMapProperties.zoom)
|
||||
})
|
||||
altproperties.allowMoving.setData(false)
|
||||
altproperties.allowZooming.setData(false)
|
||||
|
||||
function pixelCenterOf(map: UIEventSource<MlMap>): [number, number] {
|
||||
const rect = map?.data?.getCanvas()?.getBoundingClientRect()
|
||||
if (!rect) {
|
||||
return undefined
|
||||
}
|
||||
const x = (rect.left + rect.right) / 2
|
||||
const y = (rect.top + rect.bottom) / 2
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
function updateLocation() {
|
||||
if (!placedOverMap.data || !altmap.data) {
|
||||
return
|
||||
}
|
||||
altmap.data.resize()
|
||||
const {lon, lat} = placedOverMapProperties.location.data
|
||||
const altMapCenter = pixelCenterOf(altmap)
|
||||
const c = placedOverMap.data.unproject(altMapCenter)
|
||||
altproperties.location.setData({lon: c.lng, lat: c.lat})
|
||||
}
|
||||
|
||||
onDestroy(placedOverMapProperties.location.addCallbackAndRunD(updateLocation))
|
||||
updateLocation()
|
||||
window.setTimeout(updateLocation, 150)
|
||||
window.setTimeout(updateLocation, 500)
|
||||
|
||||
if (visible) {
|
||||
onDestroy(visible?.addCallbackAndRunD(v => {
|
||||
if (!v) {
|
||||
return
|
||||
}
|
||||
updateLocation()
|
||||
window.setTimeout(updateLocation, 150)
|
||||
window.setTimeout(updateLocation, 500)
|
||||
}))
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<MaplibreMap map={altmap}/>
|
73
UI/Map/RasterLayerOverview.svelte
Normal file
73
UI/Map/RasterLayerOverview.svelte
Normal file
|
@ -0,0 +1,73 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* The RasterLayerOverview shows the available 4 categories of maps with a RasterLayerPicker
|
||||
*/
|
||||
import {Store, UIEventSource} from "../../Logic/UIEventSource";
|
||||
import type {RasterLayerPolygon} from "../../Models/RasterLayers";
|
||||
import type {MapProperties} from "../../Models/MapProperties";
|
||||
import {Map as MlMap} from "maplibre-gl";
|
||||
import RasterLayerPicker from "./RasterLayerPicker.svelte";
|
||||
import type {EliCategory} from "../../Models/RasterLayerProperties";
|
||||
import UserRelatedState from "../../Logic/State/UserRelatedState";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
|
||||
export let availableLayers: Store<RasterLayerPolygon[]>
|
||||
export let mapproperties: MapProperties
|
||||
export let userstate: UserRelatedState
|
||||
export let map: Store<MlMap>
|
||||
/**
|
||||
* Used to toggle the background layers on/off
|
||||
*/
|
||||
export let visible: UIEventSource<boolean> = undefined
|
||||
|
||||
type CategoryType = "photo" | "map" | "other" | "osmbasedmap"
|
||||
const categories: Record<CategoryType, EliCategory[]> = {
|
||||
"photo": ["photo", "historicphoto"],
|
||||
"map": ["map", "historicmap"],
|
||||
"other": ["other", "elevation"],
|
||||
"osmbasedmap": ["osmbasedmap"]
|
||||
}
|
||||
|
||||
function availableForCategory(type: CategoryType): Store<RasterLayerPolygon[]> {
|
||||
const keywords = categories[type]
|
||||
return availableLayers.mapD(available => available.filter(layer =>
|
||||
keywords.indexOf(<EliCategory>layer.properties.category) >= 0
|
||||
))
|
||||
}
|
||||
|
||||
const mapLayers = availableForCategory("map")
|
||||
const osmbasedmapLayers = availableForCategory("osmbasedmap")
|
||||
const photoLayers = availableForCategory("photo")
|
||||
const otherLayers = availableForCategory("other")
|
||||
|
||||
function onApply() {
|
||||
visible.setData(false)
|
||||
}
|
||||
|
||||
function getPref(type: CategoryType): UIEventSource<string> {
|
||||
return userstate.osmConnection.GetPreference("preferred-layer-" + type)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<div class="h-full flex flex-col">
|
||||
<slot name="title">
|
||||
<h2>
|
||||
<Tr t={Translations.t.general.backgroundMap}/>
|
||||
</h2>
|
||||
</slot>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-2 h-full w-full">
|
||||
<RasterLayerPicker availableLayers={photoLayers} favourite={getPref("photo")} {map} {mapproperties}
|
||||
on:appliedLayer={onApply} {visible}/>
|
||||
<RasterLayerPicker availableLayers={mapLayers} favourite={getPref("map")} {map} {mapproperties}
|
||||
on:appliedLayer={onApply} {visible}/>
|
||||
<RasterLayerPicker availableLayers={osmbasedmapLayers} favourite={getPref("osmbasedmap")}
|
||||
{map} {mapproperties} on:appliedLayer={onApply} {visible}/>
|
||||
<RasterLayerPicker availableLayers={otherLayers} favourite={getPref("other")} {map} {mapproperties}
|
||||
on:appliedLayer={onApply} {visible}/>
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -1,18 +1,77 @@
|
|||
<script lang="ts">
|
||||
import type { Readable, Writable } from "svelte/store";
|
||||
import type { RasterLayerPolygon } from "../../Models/RasterLayers";
|
||||
import type {RasterLayerPolygon} from "../../Models/RasterLayers";
|
||||
import OverlayMap from "./OverlayMap.svelte";
|
||||
import type {MapProperties} from "../../Models/MapProperties";
|
||||
import {Store, UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {Map as MlMap} from "maplibre-gl"
|
||||
import {createEventDispatcher, onDestroy} from "svelte";
|
||||
|
||||
/***
|
||||
* Chooses a background-layer out of available options
|
||||
*/
|
||||
export let availableLayers: Readable<RasterLayerPolygon[]>
|
||||
export let value: Writable<RasterLayerPolygon>
|
||||
/***
|
||||
* Chooses a background-layer out of available options
|
||||
*/
|
||||
export let availableLayers: Store<RasterLayerPolygon[]>
|
||||
export let mapproperties: MapProperties
|
||||
export let map: Store<MlMap>
|
||||
|
||||
export let visible: Store<boolean> = undefined
|
||||
|
||||
let dispatch = createEventDispatcher<{appliedLayer}>()
|
||||
|
||||
export let favourite : UIEventSource<string> = undefined
|
||||
|
||||
|
||||
let rasterLayer = new UIEventSource<RasterLayerPolygon>(availableLayers.data?.[0])
|
||||
let hasLayers = true
|
||||
onDestroy(availableLayers.addCallbackAndRun(layers => {
|
||||
if (layers === undefined || layers.length === 0) {
|
||||
hasLayers = false
|
||||
return
|
||||
}
|
||||
hasLayers = true
|
||||
rasterLayer.setData(layers[0])
|
||||
}))
|
||||
|
||||
if(favourite){
|
||||
onDestroy(favourite.addCallbackAndRunD(favourite => {
|
||||
const fav = availableLayers.data?.find(l => l.properties.id === favourite)
|
||||
if(!fav){
|
||||
return
|
||||
}
|
||||
rasterLayer.setData(fav)
|
||||
}))
|
||||
}
|
||||
|
||||
onDestroy(rasterLayer.addCallbackAndRunD(selected => {
|
||||
favourite?.setData(selected.properties.id)
|
||||
}))
|
||||
|
||||
let rasterLayerOnMap = UIEventSource.feedFrom(rasterLayer)
|
||||
|
||||
if (visible) {
|
||||
onDestroy(visible?.addCallbackAndRunD(visible => {
|
||||
if (visible) {
|
||||
rasterLayerOnMap.setData(rasterLayer.data ?? availableLayers.data[0])
|
||||
} else {
|
||||
rasterLayerOnMap.setData(undefined)
|
||||
}
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
|
||||
<select bind:value={$value}>
|
||||
{#each $availableLayers as availableLayer }
|
||||
<option value={availableLayer}>
|
||||
{availableLayer.properties.name}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
{#if hasLayers}
|
||||
<div class="h-full w-full flex flex-col">
|
||||
<button on:click={() => {mapproperties.rasterLayer.setData(rasterLayer.data);
|
||||
dispatch("appliedLayer")
|
||||
}} class="w-full h-full m-0 p-0">
|
||||
<OverlayMap rasterLayer={rasterLayerOnMap} placedOverMap={map} placedOverMapProperties={mapproperties}
|
||||
{visible}/>
|
||||
</button>
|
||||
<select bind:value={$rasterLayer} class="w-full">
|
||||
{#each $availableLayers as availableLayer }
|
||||
<option value={availableLayer}>
|
||||
{availableLayer.properties.name}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -11,12 +11,13 @@
|
|||
import SelectedElementView from "./BigComponents/SelectedElementView.svelte";
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||
import Filterview from "./BigComponents/Filterview.svelte";
|
||||
import RasterLayerPicker from "./Map/RasterLayerPicker.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 {Square3Stack3dIcon} from "@babeard/svelte-heroicons/solid";
|
||||
|
||||
import Tr from "./Base/Tr.svelte";
|
||||
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte";
|
||||
import FloatOver from "./Base/FloatOver.svelte";
|
||||
|
@ -39,9 +40,12 @@
|
|||
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte";
|
||||
import Svg from "../Svg";
|
||||
import {ShareScreen} from "./BigComponents/ShareScreen";
|
||||
import NextButton from "./Base/NextButton.svelte";
|
||||
import IfNot from "./Base/IfNot.svelte";
|
||||
import BackgroundSwitcher from "./BigComponents/BackgroundSwitcher.svelte";
|
||||
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte";
|
||||
import type {Readable} from "svelte/store";
|
||||
import type {RasterLayerPolygon} from "../Models/RasterLayers";
|
||||
import RasterLayerPicker from "./Map/RasterLayerPicker.svelte";
|
||||
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte";
|
||||
import IfHidden from "./Base/IfHidden.svelte";
|
||||
|
||||
export let state: ThemeViewState;
|
||||
let layout = state.layout;
|
||||
|
@ -86,21 +90,7 @@
|
|||
let availableLayers = state.availableLayers;
|
||||
let userdetails = state.osmConnection.userDetails;
|
||||
let currentViewLayer = layout.layers.find(l => l.id === "current_view")
|
||||
|
||||
function jumpToCurrentLocation() {
|
||||
const glstate = state.geolocation.geolocationState
|
||||
if (glstate.currentGPSLocation.data !== undefined) {
|
||||
const c: GeolocationCoordinates = glstate.currentGPSLocation.data
|
||||
state.guistate.themeIsOpened.setData(false)
|
||||
const coor = {lon: c.longitude, lat: c.latitude}
|
||||
state.mapProperties.location.setData(coor)
|
||||
}
|
||||
if (glstate.permission.data !== "granted") {
|
||||
glstate.requestPermission()
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
let rasterLayer: Readable<RasterLayerPolygon> = state.mapProperties.rasterLayer
|
||||
</script>
|
||||
|
||||
|
||||
|
@ -138,35 +128,48 @@
|
|||
<ToSvelte construct={() => new ExtraLinkButton(state, layout.extraLink)}></ToSvelte>
|
||||
<If condition={state.featureSwitchIsTesting}>
|
||||
<div class="alert w-fit">
|
||||
Testmode
|
||||
Testmode
|
||||
</div>
|
||||
</If>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-0 left-0 mb-4 ml-4">
|
||||
<BackgroundSwitcher availableRasterLayers={state.availableLayers} mapproperties={state.mapProperties} normalMap={state.map}/>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-0 right-0 mb-4 mr-4 flex flex-col items-end">
|
||||
<If condition={state.floors.map(f => f.length > 1)}>
|
||||
<div class="mr-0.5">
|
||||
<LevelSelector floors={state.floors} layerState={state.layerState} zoom={state.mapProperties.zoom}/>
|
||||
<div class="absolute bottom-0 left-0 mb-4 w-screen">
|
||||
<div class="w-full flex justify-between px-4 items-end">
|
||||
<div>
|
||||
<!-- bottom left elements -->
|
||||
<MapControlButton on:click={() => state.guistate.backgroundLayerSelectionIsOpened.setData(true)}>
|
||||
<Square3Stack3dIcon class="w-6 h-6"/>
|
||||
</MapControlButton>
|
||||
<a class="opacity-50 hover:opacity-100 text-white cursor-pointer bg-black-transparent px-1 rounded-2xl"
|
||||
on:click={() =>{ state.guistate.themeViewTab.setData("copyright"); state.guistate.themeIsOpened.setData(true)}}>
|
||||
© OpenStreetMap | <span class="w-24">{$rasterLayer.properties.name}</span>
|
||||
</a>
|
||||
</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")}></ToSvelte>
|
||||
</MapControlButton>
|
||||
</If>
|
||||
|
||||
<div class="flex flex-col items-end">
|
||||
<!-- bottom right elements -->
|
||||
<If condition={state.floors.map(f => f.length > 1)}>
|
||||
<div class="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")}></ToSvelte>
|
||||
</MapControlButton>
|
||||
</If>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<If condition={selectedElementView.map(v => v !== undefined && selectedLayer.data !== undefined && !selectedLayer.data.popupInFloatover,[ selectedLayer] )}>
|
||||
|
@ -204,43 +207,7 @@
|
|||
|
||||
<div class="m-4" slot="content0">
|
||||
|
||||
<Tr t={layout.description}></Tr>
|
||||
<Tr t={Translations.t.general.welcomeExplanation.general}/>
|
||||
{#if layout.layers.some((l) => l.presets?.length > 0)}
|
||||
<If condition={state.featureSwitches.featureSwitchAddNew}>
|
||||
<Tr t={Translations.t.general.welcomeExplanation.addNew}/>
|
||||
</If>
|
||||
{/if}
|
||||
|
||||
<!--toTheMap,
|
||||
loginStatus.SetClass("block mt-6 pt-2 md:border-t-2 border-dotted border-gray-400"),
|
||||
-->
|
||||
<Tr t={layout.descriptionTail}></Tr>
|
||||
<NextButton clss="primary w-full" on:click={() => state.guistate.themeIsOpened.setData(false)}>
|
||||
<div class="flex justify-center w-full text-2xl">
|
||||
<Tr t={Translations.t.general.openTheMap}/>
|
||||
</div>
|
||||
</NextButton>
|
||||
|
||||
<div class="flex w-full">
|
||||
<IfNot condition={state.geolocation.geolocationState.permission.map(p => p === "denied")}>
|
||||
<button class="flex w-full gap-x-2 items-center" on:click={jumpToCurrentLocation}>
|
||||
<ToSvelte construct={Svg.crosshair_svg().SetClass("w-8 h-8")}/>
|
||||
<span>
|
||||
Jump to your location
|
||||
</span>
|
||||
</button>
|
||||
</IfNot>
|
||||
|
||||
<div class="flex flex-col w-full border rounded low-interactive">
|
||||
Search for a location:
|
||||
<Geosearch bounds={state.mapProperties.bounds}
|
||||
on:searchCompleted={() => state.guistate.themeIsOpened.setData(false)}
|
||||
perLayer={state.perLayer}
|
||||
{selectedElement}
|
||||
{selectedLayer}/>
|
||||
</div>
|
||||
</div>
|
||||
<ThemeIntroPanel {state}/>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -265,9 +232,6 @@
|
|||
zoomlevel={state.mapProperties.zoom}
|
||||
/>
|
||||
{/each}
|
||||
<If condition={state.featureSwitches.featureSwitchBackgroundSelection}>
|
||||
<RasterLayerPicker {availableLayers} value={mapproperties.rasterLayer}></RasterLayerPicker>
|
||||
</If>
|
||||
</div>
|
||||
<div class="flex" slot="title2">
|
||||
<If condition={state.featureSwitches.featureSwitchEnableExport}>
|
||||
|
@ -288,12 +252,22 @@
|
|||
<div slot="title4">
|
||||
<Tr t={Translations.t.general.sharescreen.title}/>
|
||||
</div>
|
||||
<ToSvelte construct={() => new ShareScreen(state)} slot="content4"/>
|
||||
<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="p-2 h-full">
|
||||
<RasterLayerOverview userstate={state.userRelatedState} mapproperties={state.mapProperties} map={state.map} {availableLayers} visible={state.guistate.backgroundLayerSelectionIsOpened}/>
|
||||
</div>
|
||||
</FloatOver>
|
||||
</IfHidden>
|
||||
|
||||
<If condition={state.guistate.menuIsOpened}>
|
||||
<!-- Menu page -->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue