forked from MapComplete/MapComplete
Add level selector and global filters
This commit is contained in:
parent
5504d49d59
commit
7fd7a3722e
19 changed files with 401 additions and 253 deletions
29
UI/BigComponents/LevelSelector.svelte
Normal file
29
UI/BigComponents/LevelSelector.svelte
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Shows a 'floorSelector' and maps the selected floor onto a global filter
|
||||
*/
|
||||
import LayerState from "../../Logic/State/LayerState";
|
||||
import FloorSelector from "../InputElement/Helpers/FloorSelector.svelte";
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
|
||||
export let layerState: LayerState;
|
||||
export let floors: Store<string[]>;
|
||||
export let zoom: Store<number>;
|
||||
const maxZoom = 16
|
||||
|
||||
let selectedFloor: UIEventSource<string> = new UIEventSource<string>(undefined);
|
||||
|
||||
selectedFloor.stabilized(5).map(floor => {
|
||||
if(floors.data === undefined || floors.data.length <= 1 || zoom.data < maxZoom){
|
||||
// Only a single floor is visible -> disable the 'level' global filter
|
||||
// OR we might have zoomed out to much ant want to show all
|
||||
layerState.setLevelFilter(undefined)
|
||||
}else{
|
||||
layerState.setLevelFilter(floor)
|
||||
}
|
||||
}, [floors, zoom])
|
||||
|
||||
</script>
|
||||
{#if $zoom >= maxZoom}
|
||||
<FloorSelector {floors} value={selectedFloor} />
|
||||
{/if}
|
|
@ -1,151 +0,0 @@
|
|||
import FloorLevelInputElement from "../Input/FloorLevelInputElement"
|
||||
import MapState from "../../Logic/State/MapState"
|
||||
import { TagsFilter } from "../../Logic/Tags/TagsFilter"
|
||||
import { RegexTag } from "../../Logic/Tags/RegexTag"
|
||||
import { Or } from "../../Logic/Tags/Or"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Combine from "../Base/Combine"
|
||||
import { OsmFeature } from "../../Models/OsmFeature"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import { TagUtils } from "../../Logic/Tags/TagUtils"
|
||||
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import { GlobalFilter } from "../../Logic/State/GlobalFilter"
|
||||
|
||||
/***
|
||||
* The element responsible for the level input element and picking the right level, showing and hiding at the right time, ...
|
||||
*/
|
||||
export default class LevelSelector extends Combine {
|
||||
constructor(state: MapState & { featurePipeline: FeaturePipeline }) {
|
||||
const levelsInView: Store<Record<string, number>> = state.currentBounds.map((bbox) => {
|
||||
if (bbox === undefined) {
|
||||
return {}
|
||||
}
|
||||
const allElementsUnfiltered: OsmFeature[] = [].concat(
|
||||
...state.featurePipeline.GetAllFeaturesAndMetaWithin(bbox).map((ff) => ff.features)
|
||||
)
|
||||
const allElements = allElementsUnfiltered.filter((f) => BBox.get(f).overlapsWith(bbox))
|
||||
const allLevelsRaw: string[] = allElements.map((f) => f.properties["level"])
|
||||
|
||||
const levels: Record<string, number> = { "0": 0 }
|
||||
for (const levelDescription of allLevelsRaw) {
|
||||
if (levelDescription === undefined) {
|
||||
levels["0"]++
|
||||
}
|
||||
for (const level of TagUtils.LevelsParser(levelDescription)) {
|
||||
levels[level] = (levels[level] ?? 0) + 1
|
||||
}
|
||||
}
|
||||
|
||||
return levels
|
||||
})
|
||||
|
||||
const levelSelect = new FloorLevelInputElement(levelsInView)
|
||||
|
||||
state.globalFilters.data.push({
|
||||
filter: {
|
||||
currentFilter: undefined,
|
||||
state: undefined,
|
||||
},
|
||||
id: "level",
|
||||
onNewPoint: undefined,
|
||||
})
|
||||
const isShown = levelsInView.map(
|
||||
(levelsInView) => {
|
||||
if (state.locationControl.data.zoom <= 16) {
|
||||
return false
|
||||
}
|
||||
if (Object.keys(levelsInView).length == 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
[state.locationControl]
|
||||
)
|
||||
|
||||
function setLevelFilter() {
|
||||
console.log(
|
||||
"Updating levels filter to ",
|
||||
levelSelect.GetValue().data,
|
||||
" is shown:",
|
||||
isShown.data
|
||||
)
|
||||
const filter: GlobalFilter = state.globalFilters.data.find((gf) => gf.id === "level")
|
||||
if (!isShown.data) {
|
||||
filter.filter = {
|
||||
state: "*",
|
||||
currentFilter: undefined,
|
||||
}
|
||||
filter.onNewPoint = undefined
|
||||
state.globalFilters.ping()
|
||||
return
|
||||
}
|
||||
|
||||
const l = levelSelect.GetValue().data
|
||||
if (l === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
let neededLevel: TagsFilter = new RegexTag("level", new RegExp("(^|;)" + l + "(;|$)"))
|
||||
if (l === "0") {
|
||||
neededLevel = new Or([neededLevel, new Tag("level", "")])
|
||||
}
|
||||
filter.filter = {
|
||||
state: l,
|
||||
currentFilter: neededLevel,
|
||||
}
|
||||
const t = Translations.t.general.levelSelection
|
||||
filter.onNewPoint = {
|
||||
confirmAddNew: t.confirmLevel.PartialSubs({ level: l }),
|
||||
safetyCheck: t.addNewOnLevel.Subs({ level: l }),
|
||||
tags: [new Tag("level", l)],
|
||||
}
|
||||
state.globalFilters.ping()
|
||||
return
|
||||
}
|
||||
|
||||
isShown.addCallbackAndRun((shown) => {
|
||||
console.log("Is level selector shown?", shown)
|
||||
setLevelFilter()
|
||||
if (shown) {
|
||||
levelSelect.RemoveClass("invisible")
|
||||
} else {
|
||||
levelSelect.SetClass("invisible")
|
||||
}
|
||||
})
|
||||
|
||||
levelsInView.addCallbackAndRun((levels) => {
|
||||
if (!isShown.data) {
|
||||
return
|
||||
}
|
||||
const value = levelSelect.GetValue()
|
||||
if (!(levels[value.data] === undefined || levels[value.data] === 0)) {
|
||||
return
|
||||
}
|
||||
// Nothing in view. Lets switch to a different level (the level with the most features)
|
||||
let mostElements = 0
|
||||
let mostElementsLevel = undefined
|
||||
for (const level in levels) {
|
||||
const count = levels[level]
|
||||
if (mostElementsLevel === undefined || mostElements < count) {
|
||||
mostElementsLevel = level
|
||||
mostElements = count
|
||||
}
|
||||
}
|
||||
console.log(
|
||||
"Force switching to a different level:",
|
||||
mostElementsLevel,
|
||||
"as it has",
|
||||
mostElements,
|
||||
"elements on that floor",
|
||||
levels,
|
||||
"(old level: " + value.data + ")"
|
||||
)
|
||||
value.setData(mostElementsLevel)
|
||||
})
|
||||
levelSelect.GetValue().addCallback((_) => setLevelFilter())
|
||||
super([levelSelect])
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import Combine from "../Base/Combine"
|
||||
import MapState from "../../Logic/State/MapState"
|
||||
import LevelSelector from "./LevelSelector"
|
||||
|
||||
export default class RightControls extends Combine {
|
||||
constructor(state: MapState & { featurePipeline: FeaturePipeline }) {
|
||||
const levelSelector = new LevelSelector(state)
|
||||
super([levelSelector].map((el) => el.SetClass("m-0.5 md:m-1")))
|
||||
this.SetClass("flex flex-col items-center")
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import Toggle from "./Input/Toggle"
|
||||
import LeftControls from "./BigComponents/LeftControls"
|
||||
import RightControls from "./BigComponents/RightControls"
|
||||
import CenterMessageBox from "./CenterMessageBox"
|
||||
import { DefaultGuiState } from "./DefaultGuiState"
|
||||
import Combine from "./Base/Combine"
|
||||
|
@ -42,7 +41,6 @@ export default class DefaultGUI {
|
|||
|
||||
const guiState = this.guiState
|
||||
new LeftControls(state, guiState).AttachTo("bottom-left")
|
||||
new RightControls(state, this.geolocationHandler).AttachTo("bottom-right")
|
||||
|
||||
new CenterMessageBox(state).AttachTo("centermessage")
|
||||
document?.getElementById("centermessage")?.classList?.add("pointer-events-none")
|
||||
|
|
140
UI/InputElement/Helpers/FloorSelector.svelte
Normal file
140
UI/InputElement/Helpers/FloorSelector.svelte
Normal file
|
@ -0,0 +1,140 @@
|
|||
<script lang="ts">
|
||||
import { Store, Stores, UIEventSource } from "../../../Logic/UIEventSource";
|
||||
|
||||
/**
|
||||
* Given the available floors, shows an elevator to pick a single one
|
||||
*
|
||||
* This is but the input element, the logic of handling the filter is in 'LevelSelector'
|
||||
*/
|
||||
export let floors: Store<string[]>;
|
||||
export let value: UIEventSource<string>;
|
||||
|
||||
const HEIGHT = 40;
|
||||
|
||||
let initialIndex = Math.max(0, floors?.data?.findIndex(f => f === value?.data) ?? 0);
|
||||
let index: UIEventSource<number> = new UIEventSource<number>(initialIndex);
|
||||
let forceIndex: number | undefined = undefined;
|
||||
let top = Math.max(0, initialIndex) * HEIGHT;
|
||||
let elevator: HTMLImageElement;
|
||||
|
||||
let mouseDown = false;
|
||||
|
||||
let container: HTMLElement;
|
||||
|
||||
$:{
|
||||
if (top > 0 || forceIndex !== undefined) {
|
||||
index.setData(closestFloorIndex());
|
||||
value.setData(floors.data[forceIndex ?? closestFloorIndex()]);
|
||||
}
|
||||
}
|
||||
|
||||
function unclick() {
|
||||
mouseDown = false;
|
||||
}
|
||||
|
||||
function click() {
|
||||
mouseDown = true;
|
||||
}
|
||||
|
||||
function closestFloorIndex() {
|
||||
return Math.min(floors.data.length - 1, Math.max(0, Math.round(top / HEIGHT)));
|
||||
}
|
||||
|
||||
function onMove(e: { movementY: number }) {
|
||||
if (mouseDown) {
|
||||
forceIndex = undefined;
|
||||
const containerY = container.clientTop;
|
||||
const containerMax = containerY + (floors.data.length - 1) * HEIGHT;
|
||||
top = Math.min(Math.max(0, top + e.movementY), containerMax);
|
||||
}
|
||||
}
|
||||
|
||||
let momentum = 0;
|
||||
|
||||
function stabilize() {
|
||||
// Automatically move the elevator to the closes floor
|
||||
if (mouseDown) {
|
||||
return;
|
||||
}
|
||||
const target = (forceIndex ?? index.data) * HEIGHT;
|
||||
let diff = target - top;
|
||||
if (diff > 1) {
|
||||
diff /= 3;
|
||||
}
|
||||
const sign = Math.sign(diff);
|
||||
momentum = momentum + sign;
|
||||
let diffR = Math.min(Math.abs(momentum), forceIndex !== undefined ? 9 : 3, Math.abs(diff));
|
||||
momentum = Math.sign(momentum) * Math.min(diffR, Math.abs(momentum));
|
||||
top += sign * diffR;
|
||||
if (index.data === forceIndex) {
|
||||
forceIndex = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Stores.Chronic(50).addCallback(_ => stabilize());
|
||||
|
||||
let image: HTMLImageElement;
|
||||
$:{
|
||||
if (image) {
|
||||
let lastY = 0;
|
||||
image.ontouchstart = (e: TouchEvent) => {
|
||||
mouseDown = true;
|
||||
lastY = e.changedTouches[0].clientY;
|
||||
};
|
||||
image.ontouchmove = e => {
|
||||
const y = e.changedTouches[0].clientY;
|
||||
console.log(y)
|
||||
const movementY = y - lastY;
|
||||
lastY = y;
|
||||
onMove({ movementY });
|
||||
};
|
||||
image.ontouchend = unclick;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div bind:this={container} class="relative"
|
||||
style={`height: calc(${HEIGHT}px * ${$floors.length}); width: 96px`}>
|
||||
<div class="h-full absolute w-min right-0">
|
||||
{#each $floors as floor, i}
|
||||
<button style={`height: ${HEIGHT}px; width: ${HEIGHT}px`}
|
||||
class={"border-2 border-gray-300 flex content-box justify-center items-center "+(i === (forceIndex ?? $index) ? "selected": "normal-background" )
|
||||
}
|
||||
on:click={() => {forceIndex = i}}
|
||||
> {floor}</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div style={`width: ${HEIGHT}px`}>
|
||||
<img bind:this={image} class="draggable" draggable="false" on:mousedown={click} src="./assets/svg/elevator.svg"
|
||||
style={" top: "+top+"px;"} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svelte:window on:mousemove={onMove} on:mouseup={unclick} />
|
||||
|
||||
<style>
|
||||
.selected {
|
||||
background: var(--subtle-detail-color);
|
||||
font-weight: bold;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.draggable {
|
||||
user-select: none;
|
||||
cursor: move;
|
||||
position: absolute;
|
||||
user-drag: none;
|
||||
|
||||
height: 72px;
|
||||
margin-top: -15px;
|
||||
margin-bottom: -15px;
|
||||
margin-left: -18px;
|
||||
-webkit-user-drag: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
</style>
|
|
@ -25,22 +25,36 @@
|
|||
import { Tag } from "../../../Logic/Tags/Tag";
|
||||
import type { WayId } from "../../../Models/OsmFeature";
|
||||
import Loading from "../../Base/Loading.svelte";
|
||||
import type { GlobalFilter } from "../../../Models/GlobalFilter";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
export let coordinate: { lon: number, lat: number };
|
||||
export let state: SpecialVisualizationState;
|
||||
|
||||
let selectedPreset: { preset: PresetConfig, layer: LayerConfig, icon: string, tags: Record<string, string> } = undefined;
|
||||
|
||||
let selectedPreset: {
|
||||
preset: PresetConfig,
|
||||
layer: LayerConfig,
|
||||
icon: string,
|
||||
tags: Record<string, string>
|
||||
} = undefined;
|
||||
let checkedOfGlobalFilters : number = 0
|
||||
let confirmedCategory = false;
|
||||
$: if (selectedPreset === undefined) {
|
||||
confirmedCategory = false;
|
||||
creating = false;
|
||||
checkedOfGlobalFilters = 0
|
||||
|
||||
}
|
||||
|
||||
let flayer: FilteredLayer = undefined;
|
||||
let layerIsDisplayed: UIEventSource<boolean> | undefined = undefined;
|
||||
let layerHasFilters: Store<boolean> | undefined = undefined;
|
||||
|
||||
let globalFilter: UIEventSource<GlobalFilter[]> = state.layerState.globalFilters;
|
||||
let _globalFilter: GlobalFilter[];
|
||||
onDestroy(globalFilter.addCallbackAndRun(globalFilter => {
|
||||
console.log("Global filters are", globalFilter);
|
||||
_globalFilter = globalFilter ?? [];
|
||||
}));
|
||||
$:{
|
||||
flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id);
|
||||
layerIsDisplayed = flayer?.isDisplayed;
|
||||
|
@ -71,38 +85,38 @@
|
|||
creating = true;
|
||||
const location: { lon: number; lat: number } = preciseCoordinate.data;
|
||||
const snapTo: WayId | undefined = <WayId>snappedToObject.data;
|
||||
const tags: Tag[] = selectedPreset.preset.tags;
|
||||
const tags: Tag[] = selectedPreset.preset.tags.concat(..._globalFilter.map(f => f.onNewPoint.tags));
|
||||
console.log("Creating new point at", location, "snapped to", snapTo, "with tags", tags);
|
||||
|
||||
let snapToWay: undefined | OsmWay = undefined
|
||||
if(snapTo !== undefined){
|
||||
let snapToWay: undefined | OsmWay = undefined;
|
||||
if (snapTo !== undefined) {
|
||||
const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0);
|
||||
if(downloaded !== "deleted"){
|
||||
snapToWay = downloaded
|
||||
if (downloaded !== "deleted") {
|
||||
snapToWay = downloaded;
|
||||
}
|
||||
}
|
||||
|
||||
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon,
|
||||
{
|
||||
theme: state.layout?.id ?? "unkown",
|
||||
changeType: "create",
|
||||
snapOnto: snapToWay
|
||||
});
|
||||
await state.changes.applyAction(newElementAction)
|
||||
state.newFeatures.features.ping()
|
||||
theme: state.layout?.id ?? "unkown",
|
||||
changeType: "create",
|
||||
snapOnto: snapToWay
|
||||
});
|
||||
await state.changes.applyAction(newElementAction);
|
||||
state.newFeatures.features.ping();
|
||||
// The 'changes' should have created a new point, which added this into the 'featureProperties'
|
||||
const newId = newElementAction.newElementId;
|
||||
console.log("Applied pending changes, fetching store for", newId)
|
||||
console.log("Applied pending changes, fetching store for", newId);
|
||||
const tagsStore = state.featureProperties.getStore(newId);
|
||||
{
|
||||
// Set some metainfo
|
||||
const properties = tagsStore.data;
|
||||
if (snapTo) {
|
||||
// metatags (starting with underscore) are not uploaded, so we can safely mark this
|
||||
delete properties["_referencing_ways"]
|
||||
delete properties["_referencing_ways"];
|
||||
properties["_referencing_ways"] = `["${snapTo}"]`;
|
||||
}
|
||||
properties["_backend"] = state.osmConnection.Backend()
|
||||
properties["_backend"] = state.osmConnection.Backend();
|
||||
properties["_last_edit:timestamp"] = new Date().toISOString();
|
||||
const userdetails = state.osmConnection.userDetails.data;
|
||||
properties["_last_edit:contributor"] = userdetails.name;
|
||||
|
@ -113,13 +127,17 @@
|
|||
abort();
|
||||
state.selectedLayer.setData(selectedPreset.layer);
|
||||
state.selectedElement.setData(feature);
|
||||
tagsStore.ping()
|
||||
tagsStore.ping();
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<LoginToggle ignoreLoading={true} {state}>
|
||||
<!-- This component is basically one big if/then/else flow checking for many conditions and edge cases that (in some cases) have to be handled;
|
||||
1. the first (and outermost) is of course: are we logged in?
|
||||
2. What do we want to add?
|
||||
3. Are all elements of this category visible? (i.e. there are no filters possibly hiding this, is the data still loading, ...) -->
|
||||
<LoginButton osmConnection={state.osmConnection} slot="not-logged-in">
|
||||
<Tr slot="message" t={Translations.t.general.add.pleaseLogin} />
|
||||
</LoginButton>
|
||||
|
@ -163,7 +181,7 @@
|
|||
|
||||
|
||||
{:else if $layerHasFilters}
|
||||
<!-- Some filters are enabled. The feature to add might already be mapped, but hiddne -->
|
||||
<!-- Some filters are enabled. The feature to add might already be mapped, but hidden -->
|
||||
<div class="alert flex justify-center items-center">
|
||||
<EyeOffIcon class="w-8" />
|
||||
<Tr t={Translations.t.general.add.disableFiltersExplanation} />
|
||||
|
@ -231,6 +249,16 @@
|
|||
<Tr t={t.backToSelect} />
|
||||
</div>
|
||||
</SubtleButton>
|
||||
{:else if _globalFilter.length > checkedOfGlobalFilters}
|
||||
<Tr t={_globalFilter[checkedOfGlobalFilters].onNewPoint?.safetyCheck} />
|
||||
<SubtleButton on:click={() => {checkedOfGlobalFilters = checkedOfGlobalFilters + 1}}>
|
||||
<img slot="image" src={_globalFilter[checkedOfGlobalFilters].onNewPoint?.icon ?? "./assets/svg/confirm.svg"} class="w-12 h-12">
|
||||
<Tr slot="message" t={_globalFilter[checkedOfGlobalFilters].onNewPoint?.confirmAddNew.Subs({preset: selectedPreset.preset})} />
|
||||
</SubtleButton>
|
||||
<SubtleButton on:click={() => {globalFilter.setData([]); abort()}}>
|
||||
<img slot="image" src="./assets/svg/close.svg" class="w-8 h-8"/>
|
||||
<Tr slot="message" t={Translations.t.general.cancel}/>
|
||||
</SubtleButton>
|
||||
{:else if !creating}
|
||||
<NewPointLocationInput value={preciseCoordinate} snappedTo={snappedToObject} {state} {coordinate}
|
||||
targetLayer={selectedPreset.layer}
|
||||
|
|
|
@ -13,9 +13,6 @@ import { OsmServiceState } from "../../Logic/Osm/OsmConnection"
|
|||
* Generates all the questions, one by one
|
||||
*/
|
||||
export default class QuestionBox extends VariableUiElement {
|
||||
public readonly skippedQuestions: UIEventSource<number[]>
|
||||
public readonly restingQuestions: Store<BaseUIElement[]>
|
||||
|
||||
constructor(
|
||||
state,
|
||||
options: {
|
||||
|
@ -29,10 +26,6 @@ export default class QuestionBox extends VariableUiElement {
|
|||
|
||||
const tagsSource = options.tagsSource
|
||||
const units = options.units
|
||||
options.showAllQuestionsAtOnce = options.showAllQuestionsAtOnce ?? false
|
||||
const tagRenderings = options.tagRenderings
|
||||
.filter((tr) => tr.question !== undefined)
|
||||
.filter((tr) => tr.question !== null)
|
||||
|
||||
let focus: () => void = () => {}
|
||||
|
||||
|
@ -59,9 +52,6 @@ export default class QuestionBox extends VariableUiElement {
|
|||
)
|
||||
)
|
||||
|
||||
const skippedQuestionsButton = Translations.t.general.skippedQuestions.onClick(() => {
|
||||
skippedQuestions.setData([])
|
||||
})
|
||||
tagsSource.map(
|
||||
(tags) => {
|
||||
if (tags === undefined) {
|
||||
|
@ -136,18 +126,12 @@ export default class QuestionBox extends VariableUiElement {
|
|||
els.push(allQuestions[0])
|
||||
}
|
||||
|
||||
if (skippedQuestions.data.length > 0) {
|
||||
els.push(skippedQuestionsButton)
|
||||
}
|
||||
|
||||
return new Combine(els).SetClass("block mb-8")
|
||||
},
|
||||
[state.osmConnection.apiIsOnline]
|
||||
)
|
||||
)
|
||||
|
||||
this.skippedQuestions = skippedQuestions
|
||||
this.restingQuestions = questionsToAsk
|
||||
focus = () => this.ScrollIntoView()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,9 @@
|
|||
let answered: number = 0;
|
||||
let skipped: number = 0;
|
||||
|
||||
function focus(){
|
||||
|
||||
}
|
||||
function skip(question: TagRenderingConfig, didAnswer: boolean = false) {
|
||||
skippedQuestions.data.add(question.id);
|
||||
skippedQuestions.ping();
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState";
|
||||
import MapControlButton from "./Base/MapControlButton.svelte";
|
||||
import ToSvelte from "./Base/ToSvelte.svelte";
|
||||
import Svg from "../Svg";
|
||||
import If from "./Base/If.svelte";
|
||||
import { GeolocationControl } from "./BigComponents/GeolocationControl";
|
||||
import type { Feature } from "geojson";
|
||||
|
@ -35,6 +34,7 @@
|
|||
import { VariableUiElement } from "./Base/VariableUIElement";
|
||||
import SvelteUIElement from "./Base/SvelteUIElement";
|
||||
import OverlayToggle from "./BigComponents/OverlayToggle.svelte";
|
||||
import LevelSelector from "./BigComponents/LevelSelector.svelte";
|
||||
|
||||
export let state: ThemeViewState;
|
||||
let layout = state.layout;
|
||||
|
@ -71,14 +71,14 @@
|
|||
<div class="absolute top-0 left-0 w-full ">
|
||||
<!-- Top components -->
|
||||
<If condition={state.featureSwitches.featureSwitchSearch}>
|
||||
<div class="sm:w-min float-right mt-1 px-1 sm:m-2 max-[320px]:w-full">
|
||||
<div class="max-[480px]:w-full float-right mt-1 px-1 sm:m-2">
|
||||
<Geosearch bounds={state.mapProperties.bounds} perLayer={state.perLayer} {selectedElement}
|
||||
{selectedLayer}></Geosearch>
|
||||
</div>
|
||||
</If>
|
||||
<div class="float-left m-1 sm:mt-2">
|
||||
<MapControlButton on:click={() => state.guistate.themeIsOpened.setData(true)}>
|
||||
<div class="flex m-0.5 mx-1 sm:mx-1 md:mx-2 items-center cursor-pointer">
|
||||
<div class="flex m-0.5 mx-1 sm:mx-1 md:mx-2 items-center cursor-pointer max-[480px]:w-full">
|
||||
<img class="w-4 h-4 sm:w-6 sm:h-6 md:w-8 md:h-8 block mr-0.5 sm:mr-1 md:mr-2" src={layout.icon}>
|
||||
<b class="mr-1">
|
||||
<Tr t={layout.title}></Tr>
|
||||
|
@ -101,7 +101,12 @@
|
|||
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-0 right-0 mb-4 mr-4">
|
||||
<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>
|
||||
</If>
|
||||
<MapControlButton on:click={() => mapproperties.zoom.update(z => z+1)}>
|
||||
<img src="./assets/svg/plus.svg" class="w-6 h-6 md:w-8 md:h-8"/>
|
||||
</MapControlButton>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue