Merge branches

This commit is contained in:
Pieter Vander Vennet 2024-05-13 18:55:54 +02:00
commit bae90d50bc
304 changed files with 49983 additions and 31589 deletions

View file

@ -100,7 +100,7 @@
<input
autofocus
bind:value={$themeSearchText}
class="mr-4 w-full"
class="mr-4 w-full outline-none"
id="theme-search"
type="search"
use:placeholder={tr.searchForATheme}

View file

@ -0,0 +1,49 @@
<script lang="ts">
import { Store } from "../../Logic/UIEventSource"
import { onDestroy, onMount } from "svelte"
let elem: HTMLElement
let targetOuter: HTMLElement
export let isOpened: Store<boolean>
export let moveTo: Store<HTMLElement>
export let debug : string
function copySizeOf(htmlElem: HTMLElement) {
const target = htmlElem.getBoundingClientRect()
elem.style.left = target.x + "px"
elem.style.top = target.y + "px"
elem.style.width = target.width + "px"
elem.style.height = target.height + "px"
}
function animate(opened: boolean) {
const moveToElem = moveTo.data
console.log("Animating", debug," to", opened)
if (opened) {
copySizeOf(targetOuter)
elem.style.background = "var(--background-color)"
} else if (moveToElem !== undefined) {
copySizeOf(moveToElem)
elem.style.background = "#ffffff00"
} else {
elem.style.left = "0px"
elem.style.top = "0px"
elem.style.background = "#ffffff00"
}
}
onDestroy(isOpened.addCallback(opened => animate(opened)))
onMount(() => requestAnimationFrame(() => animate(isOpened.data)))
</script>
<div class={"absolute bottom-0 right-0 h-full w-screen p-4 md:p-6 pointer-events-none invisible"}>
<div class="content h-full" bind:this={targetOuter} style="background: red" />
</div>
<div bind:this={elem} class="pointer-events-none absolute bottom-0 right-0 low-interaction rounded-2xl"
style="transition: all 0.5s ease-out, background-color 1.4s ease-out; background: var(--background-color);">
<!-- Classes should be the same as the 'floatoaver' -->
</div>

View file

@ -11,7 +11,6 @@
*/
const dispatch = createEventDispatcher<{ close }>()
export let extraClasses = "p-4 md:p-6"
</script>
<!-- Draw the background over the total screen -->
@ -24,7 +23,7 @@
/>
<!-- draw a _second_ absolute div, placed using 'bottom' which will be above the navigation bar on mobile browsers -->
<div
class={twMerge("absolute bottom-0 right-0 h-full w-screen", extraClasses)}
class={"absolute bottom-0 right-0 h-full w-screen p-4 md:p-6"}
style="z-index: 21"
use:trapFocus
>

View file

@ -3,7 +3,7 @@
import { twJoin } from "tailwind-merge"
import { Translation } from "../i18n/Translation"
import { ariaLabel } from "../../Utils/ariaLabel"
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
/**
* A round button with an icon and possible a small text, which hovers above the map
@ -12,9 +12,15 @@
export let cls = "m-0.5 p-0.5 sm:p-1 md:m-1"
export let enabled: Store<boolean> = new ImmutableStore(true)
export let arialabel: Translation = undefined
export let htmlElem: UIEventSource<HTMLElement> = undefined
let _htmlElem : HTMLElement
$: {
htmlElem?.setData(_htmlElem)
}
</script>
<button
bind:this={_htmlElem}
on:click={(e) => dispatch("click", e)}
on:keydown
use:ariaLabel={arialabel}

View file

@ -1,11 +1,8 @@
import Combine from "./Combine"
import BaseUIElement from "../BaseUIElement"
import Title from "./Title"
import List from "./List"
import Link from "./Link"
import { marked } from "marked"
import { parse as parse_html } from "node-html-parser"
import {default as turndown} from "turndown"
import { default as turndown } from "turndown"
import { Utils } from "../../Utils"
export default class TableOfContents {
@ -56,7 +53,7 @@ export default class TableOfContents {
const htmlSource = <string>marked.parse(md)
const el = parse_html(htmlSource)
const structure = TableOfContents.generateStructure(<any>el)
let firstTitle = structure[1]
const firstTitle = structure[1]
let minDepth = undefined
do {
minDepth = Math.min(...structure.map(s => s.depth))
@ -81,7 +78,7 @@ export default class TableOfContents {
let topLevelCount = 0
for (const el of structure) {
const depthDiff = el.depth - minDepth
let link = `[${el.title}](#${TableOfContents.asLinkableId(el.title)})`
const link = `[${el.title}](#${TableOfContents.asLinkableId(el.title)})`
if (depthDiff === 0) {
topLevelCount++
toc += `${topLevelCount}. ${link}\n`
@ -91,16 +88,14 @@ export default class TableOfContents {
}
const heading = Utils.Times(() => "#", firstTitle.depth)
toc = heading +" Table of contents\n\n"+toc
toc = heading + " Table of contents\n\n" + toc
const original = el.outerHTML
const firstTitleIndex = original.indexOf(firstTitle.el.outerHTML)
const tocHtml = (<string>marked.parse(toc))
const withToc = original.substring(0, firstTitleIndex) + tocHtml + original.substring(firstTitleIndex)
const firstTitleIndex = md.indexOf(firstTitle.title)
const htmlToMd = new turndown()
return htmlToMd.turndown(withToc)
const intro = md.substring(0, firstTitleIndex)
const splitPoint = intro.lastIndexOf("\n")
return md.substring(0, splitPoint) + toc + md.substring(splitPoint)
}

View file

@ -12,7 +12,6 @@
export let state: SpecialVisualizationState
let theme = state.layout?.id ?? ""
let config: ExtraLinkConfig = state.layout.extraLink
console.log(">>>",config)
const isIframe = window !== window.top
let basepath = window.location.host
let showWelcomeMessageSwitch = state.featureSwitches.featureSwitchWelcomeMessage

View file

@ -113,7 +113,7 @@
{:else}
<input
type="search"
class="w-full"
class="w-full outline-none"
bind:this={inputElement}
on:keypress={(keypr) => {
feedback = undefined

View file

@ -4,7 +4,6 @@
* Even though the component is very small, it gets its own class as it is often reused
*/
import { Square3Stack3dIcon } from "@babeard/svelte-heroicons/solid"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import Translations from "../i18n/Translations"
import MapControlButton from "../Base/MapControlButton.svelte"
import Tr from "../Base/Tr.svelte"
@ -16,11 +15,13 @@
export let state: ThemeViewState
export let map: Store<MlMap> = undefined
export let hideTooltip = false
export let htmlElem : UIEventSource<HTMLElement> = undefined
</script>
<MapControlButton
arialabel={Translations.t.general.labels.background}
on:click={() => state.guistate.backgroundLayerSelectionIsOpened.setData(true)}
{htmlElem}
>
<StyleLoadingIndicator map={map ?? state.map} rasterLayer={state.mapProperties.rasterLayer}>
<Square3Stack3dIcon class="h-6 w-6" />

View file

@ -1,8 +1,15 @@
<script lang="ts">
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import UserRelatedState from "../../Logic/State/UserRelatedState"
const t = Translations.t.privacy
export let state: SpecialVisualizationState
const usersettings = UserRelatedState.usersettingsConfig
const editPrivacy = usersettings.tagRenderings.find(tr => tr.id === "more_privacy")
const isLoggedIn = state.osmConnection.isLoggedIn
</script>
<div class="link-underline flex flex-col">
@ -20,7 +27,41 @@
<h3>
<Tr t={t.editingTitle} />
</h3>
<Tr t={t.editing} />
<Tr t={t.editingIntro} />
<Tr t={t.editingIntro} />
<ul>
<li>
<Tr t={t.items.changesYouMake} />
</li>
<li>
<Tr t={t.items.username} />
</li>
<li>
<Tr t={t.items.date} />
</li>
<li>
<Tr t={t.items.theme} />
</li>
<li>
<Tr t={t.items.language} />
</li>
<li>
{#if $isLoggedIn}
<TagRenderingEditable config={editPrivacy} selectedElement={{
type: "Feature",
properties: { id: "settings" },
geometry: { type: "Point", coordinates: [0, 0] },
}}
{state}
tags={state.userRelatedState.preferencesAsTags} />
{:else}
<Tr t={t.items.distanceIndicator} />
{/if}
</li>
</ul>
<Tr t={t.editingOutro} />
<h3>
<Tr t={t.miscCookiesTitle} />

View file

@ -15,6 +15,7 @@
import Add from "../../assets/svg/Add.svelte"
import Location_refused from "../../assets/svg/Location_refused.svelte"
import Location from "../../assets/svg/Location.svelte"
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
/**
* The theme introduction panel
@ -48,6 +49,7 @@
<div class="flex h-full flex-col justify-between">
<div>
<!-- Intro, description, ... -->
<Tr t={layout.description} />
<Tr t={Translations.t.general.welcomeExplanation.general} />
{#if layout.layers.some((l) => l.presets?.length > 0)}

View file

@ -109,16 +109,17 @@
<div class="low-interaction border-interactive p-1">
{#if !readonly}
<Tr t={t.loadedFrom.Subs({ url: sourceUrl, source: sourceUrl })} />
<h3>
<Tr t={t.conflicting.title} />
</h3>
{/if}
<div class="flex flex-col" class:gap-y-8={!readonly}>
{#if !readonly}
<Tr t={t.conflicting.intro} />
{/if}
{#if $different.length > 0}
{#if !readonly}
<h3>
<Tr t={t.conflicting.title} />
</h3>
<Tr t={t.conflicting.intro} />
{/if}
{#each $different as key (key)}
<div class="mx-2 rounded-2xl">
<ComparisonAction
@ -135,6 +136,13 @@
{/if}
{#if $missing.length > 0}
{#if !readonly}
<h3 class="m-0">
<Tr t={t.missing.title} />
</h3>
<Tr t={t.missing.intro} />
{/if}
{#if currentStep === "init"}
{#each $missing as key (key)}
<div class:glowing-shadow={applyAllHovered} class="mx-2 rounded-2xl">

View file

@ -27,9 +27,9 @@
addExtraTags = helperArgs[1].split(";")
}
const path = `${key}s/${maintag.split("=")[0]}/${maintag.split("=")[1]}`
const [k, v] = maintag.split("=")
let items: NSIItem[] = NameSuggestionIndex.getSuggestionsFor(path, feature.properties["_country"], GeoOperations.centerpointCoordinates(feature))
let items: NSIItem[] = NameSuggestionIndex.getSuggestionsFor(key, k, v, feature.properties["_country"], GeoOperations.centerpointCoordinates(feature))
let selectedItem: NSIItem = items.find((item) => item.tags[key] === value.data)
@ -113,10 +113,10 @@
})
</script>
{#if items?.length >= 0}
<div>
<div class="normal-background my-2 flex w-5/6 justify-between rounded-full pl-2">
<!-- TODO translate placeholder -->
<input type="text" placeholder="Filter entries" bind:value={filter} class="outline-none" />
<SearchIcon aria-hidden="true" class="h-6 w-6 self-end" />
</div>

View file

@ -19,6 +19,7 @@
import OpeningHoursInput from "./Helpers/OpeningHoursInput.svelte"
import SlopeInput from "./Helpers/SlopeInput.svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import NameSuggestionIndexInput from "./Helpers/NameSuggestionIndexInput.svelte"
export let type: ValidatorType
export let value: UIEventSource<string | object>
@ -26,6 +27,9 @@
export let feature: Feature
export let args: (string | number | boolean)[] = undefined
export let state: SpecialVisualizationState
export let helperArgs: (string | number | boolean)[]
export let key: string
export let extraTags: UIEventSource<Record<string, string>>
let properties = { feature, args: args ?? [] }
</script>
@ -50,4 +54,6 @@
<SlopeInput {value} {feature} {state} />
{:else if type === "wikidata"}
<ToSvelte construct={() => InputHelpers.constructWikidataHelper(value, properties)} />
{:else if type === "nsi"}
<NameSuggestionIndexInput {value} {feature} {helperArgs} {key} {extraTags} />
{/if}

View file

@ -28,6 +28,7 @@ import TagValidator from "./Validators/TagValidator"
import IdValidator from "./Validators/IdValidator"
import SlopeValidator from "./Validators/SlopeValidator"
import VeloparkValidator from "./Validators/VeloparkValidator"
import NameSuggestionIndexValidator from "./Validators/NameSuggestionIndexValidator"
import CurrencyValidator from "./Validators/CurrencyValidator"
export type ValidatorType = (typeof Validators.availableTypes)[number]
@ -61,6 +62,7 @@ export default class Validators {
"id",
"slope",
"velopark",
"nsi",
"currency"
] as const
@ -91,6 +93,7 @@ export default class Validators {
new IdValidator(),
new SlopeValidator(),
new VeloparkValidator(),
new NameSuggestionIndexValidator(),
new CurrencyValidator()
]

View file

@ -0,0 +1,40 @@
import Title from "../../Base/Title"
import Combine from "../../Base/Combine"
import { Validator } from "../Validator"
import Table from "../../Base/Table"
export default class NameSuggestionIndexValidator extends Validator {
constructor() {
super(
"nsi",
new Combine([
"Gives a list of possible suggestions for a brand or operator tag.",
new Title("Helper arguments"),
new Table(
["name", "doc"],
[
[
"options",
new Combine([
"A JSON-object of type `{ main: string, key: string }`. ",
new Table(
["subarg", "doc"],
[
[
"main",
"The main tag to give suggestions for, e.g. `amenity=restaurant`.",
],
[
"addExtraTags",
"Extra tags to add to the suggestions, e.g. `nobrand=yes`.",
],
]
),
]),
],
]
),
])
)
}
}

View file

@ -1,11 +1,11 @@
<script lang="ts">
import Loading from "../Base/Loading.svelte"
import { Stores, UIEventSource } from "../../Logic/UIEventSource"
import { Store, Stores, UIEventSource } from "../../Logic/UIEventSource"
import { Map as MlMap } from "maplibre-gl"
import { onDestroy } from "svelte"
let isLoading = false
export let map: UIEventSource<MlMap>
export let map: Store<MlMap>
/**
* Optional. Only used for the 'global' change indicator so that it won't spin on pan/zoom but only when a change _actually_ occured
*/

View file

@ -216,7 +216,7 @@ class ApplyButton extends UIElement {
}
export default class AutoApplyButton implements SpecialVisualization {
public readonly docs: BaseUIElement
public readonly docs: string
public readonly funcName: string = "auto_apply"
public readonly needsUrls = []
@ -273,7 +273,7 @@ export default class AutoApplyButton implements SpecialVisualization {
"Then, use a calculated tag on the host feature to determine the overlapping object ids",
"At last, add this component",
]),
])
]).AsMarkdown()
}
constr(

View file

@ -15,6 +15,7 @@
export let unvalidatedText: UIEventSource<string> = new UIEventSource<string>(value.data)
export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>>
export let extraTags: UIEventSource<Record<string, string>>
export let feature: Feature = undefined
export let state: SpecialVisualizationState
@ -27,6 +28,8 @@
inline = false
inline = config.freeform?.inline
}
let helperArgs = config.freeform?.helperArgs
let key = config.freeform?.key
const dispatch = createEventDispatcher<{ selected }>()
export let feedback: UIEventSource<Translation>
@ -75,6 +78,9 @@
type={config.freeform.type}
{value}
{state}
{helperArgs}
{key}
{extraTags}
on:submit
/>
</div>

View file

@ -79,7 +79,7 @@
console.log("Applying questions to ask")
const qta = questionsToAsk.data
firstQuestion.setData(undefined)
allQuestionsToAsk.setData([])
//allQuestionsToAsk.setData([])
await Utils.awaitAnimationFrame()
firstQuestion.setData(qta[0])
allQuestionsToAsk.setData(qta)

View file

@ -30,15 +30,18 @@
import { placeholder } from "../../../Utils/placeholder"
import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
import { Tag } from "../../../Logic/Tags/Tag"
import { And } from "../../../Logic/Tags/And"
import { get } from "svelte/store"
import Markdown from "../../Base/Markdown.svelte"
export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>>
export let selectedElement: Feature
export let state: SpecialVisualizationState
export let layer: LayerConfig | undefined
export let selectedTags: UploadableTag = undefined
export let extraTags: UIEventSource<Record<string, string>> = new UIEventSource({})
export let allowDeleteOfFreeform: boolean = false
@ -69,6 +72,8 @@
/**
* Prepares and fills the checkedMappings
*/
console.log("Initing ", config.id)
function initialize(tgs: Record<string, string>, confg: TagRenderingConfig): void {
mappings = confg.mappings?.filter((m) => {
if (typeof m.hideInAnswer === "boolean") {
@ -76,8 +81,10 @@
}
return !m.hideInAnswer.matchesProperties(tgs)
})
selectedMapping = mappings?.findIndex(mapping => mapping.if.matchesProperties(tgs) || mapping.alsoShowIf?.matchesProperties(tgs))
if(selectedMapping < 0){
selectedMapping = mappings?.findIndex(
(mapping) => mapping.if.matchesProperties(tgs) || mapping.alsoShowIf?.matchesProperties(tgs)
)
if (selectedMapping < 0) {
selectedMapping = undefined
}
// We received a new config -> reinit
@ -102,7 +109,7 @@
seenFreeforms.push(newProps[confg.freeform.key])
}
return matches
})
}),
]
if (tgs !== undefined && confg.freeform) {
@ -133,15 +140,35 @@
freeformInput.set(undefined)
}
feedback.setData(undefined)
}
$: {
// Even though 'config' is not declared as a store, Svelte uses it as one to update the component
// We want to (re)-initialize whenever the 'tags' or 'config' change - but not when 'checkedConfig' changes
initialize($tags, config)
}
let usedKeys: string[] = config.usedTags().flatMap((t) => t.usedKeys())
/**
* The 'minimalTags' is a subset of the tags of the feature, only containing the values relevant for this object.
* The main goal is to be stable and only 'ping' when an actual change is relevant
*/
let minimalTags = new UIEventSource<Record<string, string>>(undefined)
tags.addCallbackAndRunD((tags) => {
const previousMinimal = minimalTags.data
const newMinimal: Record<string, string> = {}
let somethingChanged = previousMinimal === undefined
for (const key of usedKeys) {
const newValue = tags[key]
somethingChanged ||= previousMinimal?.[key] !== newValue
if (newValue !== undefined && newValue !== null) {
newMinimal[key] = newValue
}
}
if (somethingChanged) {
console.log("Updating minimal tags to", newMinimal, "of", config.id)
minimalTags.setData(newMinimal)
}
})
minimalTags.addCallbackAndRunD((tgs) => {
initialize(tgs, config)
})
onDestroy(
freeformInput.subscribe((freeformValue) => {
if (!mappings || mappings?.length == 0 || config.freeform?.key === undefined) {
@ -178,23 +205,50 @@
checkedMappings,
tags.data
)
if(state.featureSwitches.featureSwitchIsDebugging.data){
console.log("Constructing change spec from", {freeform: $freeformInput, selectedMapping, checkedMappings, currentTags: tags.data}, " --> ", selectedTags)
if (featureSwitchIsDebugging?.data) {
console.log(
"Constructing change spec from",
{
freeform: $freeformInput,
selectedMapping,
checkedMappings,
currentTags: tags.data,
},
" --> ",
selectedTags
)
}
} catch (e) {
console.error("Could not calculate changeSpecification:", e)
selectedTags = undefined
}
}
if (extraTags.data) {
// Map the extraTags into an array of Tag objects
const extraTagsArray = Object.entries(extraTags.data).map(([k, v]) => new Tag(k, v))
// Check the type of selectedTags
if (selectedTags instanceof Tag) {
// Re-define selectedTags as an And
selectedTags = new And([selectedTags, ...extraTagsArray])
} else if (selectedTags instanceof And) {
// Add the extraTags to the existing And
selectedTags = new And([...selectedTags.and, ...extraTagsArray])
} else {
console.error("selectedTags is not of type Tag or And")
}
}
}
function onSave(_ = undefined) {
if (selectedTags === undefined) {
return
}
if (layer === undefined || (layer?.source === null && layer.id !== "favourite")) {
/**
* This is a special, priviliged layer.
* This is a special, privileged layer.
* We simply apply the tags onto the records
*/
const kv = selectedTags.asChange(tags.data)
@ -213,7 +267,7 @@
dispatch("saved", { config, applied: selectedTags })
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, {
theme: tags.data["_orig_theme"] ?? state.layout.id,
changeType: "answer"
changeType: "answer",
})
freeformInput.set(undefined)
selectedMapping = undefined
@ -266,7 +320,7 @@
{#if config.questionhint}
{#if config.questionHintIsMd}
<Markdown srcWritable={ config.questionhint.current} />
<Markdown srcWritable={config.questionhint.current} />
{:else}
<div class="max-h-60 overflow-y-auto">
<SpecialTranslation
@ -277,7 +331,7 @@
feature={selectedElement}
/>
</div>
{/if}
{/if}
{/if}
</legend>
@ -301,6 +355,7 @@
{feedback}
{unit}
{state}
{extraTags}
feature={selectedElement}
value={freeformInput}
unvalidatedText={freeformInputUnvalidated}
@ -344,6 +399,7 @@
{feedback}
{unit}
{state}
{extraTags}
feature={selectedElement}
value={freeformInput}
unvalidatedText={freeformInputUnvalidated}
@ -388,6 +444,7 @@
{feedback}
{unit}
{state}
{extraTags}
feature={selectedElement}
value={freeformInput}
unvalidatedText={freeformInputUnvalidated}

View file

@ -6,6 +6,15 @@
import { Utils } from "../Utils"
import Add from "../assets/svg/Add.svelte"
import LanguagePicker from "./InputElement/LanguagePicker.svelte"
import type { SpecialVisualizationState } from "./SpecialVisualization"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
import UserRelatedState from "../Logic/State/UserRelatedState"
const osmConnection = new OsmConnection()
let state: SpecialVisualizationState = {
osmConnection,
userRelatedState: new UserRelatedState(osmConnection)
}
</script>
<div class="flex h-screen flex-col overflow-hidden px-4">
@ -17,7 +26,7 @@
<LanguagePicker availableLanguages={Translations.t.privacy.intro.SupportedLanguages()} />
</div>
<div class="h-full overflow-auto border border-gray-500 p-4">
<PrivacyPolicy />
<PrivacyPolicy {state} />
</div>
<a class="button flex" href={Utils.HomepageLink()}>
<Add class="h-6 w-6" />

View file

@ -1,12 +1,12 @@
import { Store, UIEventSource } from "../Logic/UIEventSource"
import BaseUIElement from "./BaseUIElement"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import { IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
import { Changes } from "../Logic/Osm/Changes"
import { ExportableMap, MapProperties } from "../Models/MapProperties"
import LayerState from "../Logic/State/LayerState"
import { Feature, Geometry, Point } from "geojson"
import { Feature, Geometry, Point, Polygon } from "geojson"
import FullNodeDatabaseSource from "../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
import { MangroveIdentity } from "../Logic/Web/MangroveReviews"
import { GeoIndexedStoreForLayer } from "../Logic/FeatureSource/Actors/GeoIndexedStore"
@ -61,8 +61,10 @@ export interface SpecialVisualizationState {
readonly selectedElement: UIEventSource<Feature>
readonly currentView: FeatureSource<Feature<Polygon>>
readonly favourites: FavouritesFeatureSource
/**
* If data is currently being fetched from external sources
*/

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@
export let configs: ConfigMeta[]
export let title: string | undefined = undefined
export let path: (string | number)[] = []
export let path: readonly (string | number)[] = []
let expertMode = state.expertMode
let configsNoHidden = configs.filter((schema) => schema.hints?.group !== "hidden")
@ -21,9 +21,9 @@
</script>
{#if configs === undefined}
Bug: 'Region' received 'undefined'
Bug: 'Region' received 'undefined' at {path.join(".")}
{:else if configs.length === 0}
Bug: Region received empty list as configuration
Bug: Region received empty list as configuration at {path.join(".")}
{:else if title}
<div class="flex w-full flex-col">
<h3>{title}</h3>

View file

@ -7,7 +7,7 @@
import type { ConfigMeta } from "./configMeta"
import type {
MappingConfigJson,
QuestionableTagRenderingConfigJson,
QuestionableTagRenderingConfigJson
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
@ -59,8 +59,8 @@
labelMapping = {
if: "value=" + label,
then: {
en: "Builtin collection <b>" + label + "</b>:",
},
en: "Builtin collection <b>" + label + "</b>:"
}
}
perLabel[label] = labelMapping
mappingsBuiltin.push(labelMapping)
@ -72,14 +72,14 @@
mappingsBuiltin.push({
if: "value=" + tr["id"],
then: {
en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>",
},
en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>"
}
})
}
const configBuiltin = new TagRenderingConfig(<QuestionableTagRenderingConfigJson>{
question: "Which builtin element should be shown?",
mappings: mappingsBuiltin,
mappings: mappingsBuiltin
})
const tags = new UIEventSource({ value })
@ -112,7 +112,7 @@
"condition",
"metacondition",
"mappings",
"icon",
"icon"
])
const ignored = new Set(["labels", "description", "classes"])
@ -196,7 +196,10 @@
<h3>Text field and input element configuration</h3>
<div class="border-l border-dashed border-gray-800 pl-2">
<SchemaBasedField {state} path={[...path, "render"]} schema={topLevelItems["render"]} />
<Region {state} {path} configs={freeformSchema} />
{#if freeformSchema?.length > 0}
<!-- In read-only cases, (e.g. popup title) there will be no freeform-schema to set and thus freeformSchema will be undefined -->
<Region {state} {path} configs={freeformSchema} />
{/if}
<SchemaBasedField {state} path={[...path, "icon"]} schema={topLevelItems["icon"]} />
</div>

View file

@ -1,2 +1,3 @@
<script lang="ts">
</script>

View file

@ -18,7 +18,7 @@
EyeIcon,
HeartIcon,
MenuIcon,
XCircleIcon,
XCircleIcon
} from "@rgossiaux/svelte-heroicons/solid"
import Tr from "./Base/Tr.svelte"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
@ -73,6 +73,8 @@
import { BBox } from "../Logic/BBox"
import ReviewsOverview from "./Reviews/ReviewsOverview.svelte"
import ExtraLinkButton from "./BigComponents/ExtraLinkButton.svelte"
import CloseAnimation from "./Base/CloseAnimation.svelte"
import { LastClickFeatureSource } from "../Logic/FeatureSource/Sources/LastClickFeatureSource"
export let state: ThemeViewState
let layout = state.layout
@ -138,7 +140,7 @@
const bottomRight = mlmap.unproject([rect.right, rect.bottom])
const bbox = new BBox([
[topLeft.lng, topLeft.lat],
[bottomRight.lng, bottomRight.lat],
[bottomRight.lng, bottomRight.lat]
])
state.visualFeedbackViewportBounds.setData(bbox)
}
@ -151,7 +153,7 @@
})
let featureSwitches: FeatureSwitchState = state.featureSwitches
let availableLayers = state.availableLayers
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
let currentViewLayer: LayerConfig = layout.layers.find((l) => l.id === "current_view")
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
let rasterLayerName =
rasterLayer.data?.properties?.name ??
@ -183,6 +185,22 @@
const animation = mlmap.keyboard?.keydown(e)
animation?.cameraAnimation(mlmap)
}
/**
* Needed for the animations
*/
let openMapButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openMenuButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openCurrentViewLayerButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let _openNewElementButton: HTMLButtonElement
let openNewElementButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
$: {
openNewElementButton.setData(_openNewElementButton)
}
let openFilterButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openBackgroundButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
</script>
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
@ -228,6 +246,7 @@
<MapControlButton
on:click={() => state.guistate.themeIsOpened.setData(true)}
on:keydown={forwardEventToMap}
htmlElem={openMapButton}
>
<div class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2">
<img
@ -245,15 +264,17 @@
arialabel={Translations.t.general.labels.menu}
on:click={() => state.guistate.menuIsOpened.setData(true)}
on:keydown={forwardEventToMap}
htmlElem={openMenuButton}
>
<MenuIcon class="h-8 w-8 cursor-pointer" />
</MapControlButton>
{#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()}
<MapControlButton
on:click={() => {
state.selectedElement.setData(state.currentView.features?.data?.[0])
state.selectCurrentView()
}}
on:keydown={forwardEventToMap}
htmlElem={openCurrentViewLayerButton}
>
<ToSvelte
construct={() => currentViewLayer.defaultIcon().SetClass("w-8 h-8 cursor-pointer")}
@ -287,6 +308,7 @@
<button
class="pointer-events-auto w-fit low-interaction"
class:disabled={$currentZoom < Constants.minZoomLevelToAddNewPoint}
bind:this={_openNewElementButton}
on:click={() => {
state.openNewDialog()
}}
@ -310,12 +332,13 @@
arialabel={Translations.t.general.labels.filter}
on:click={() => state.guistate.openFilterView()}
on:keydown={forwardEventToMap}
htmlElem={openFilterButton}
>
<Filter class="h-6 w-6" />
</MapControlButton>
</If>
<If condition={state.featureSwitches.featureSwitchBackgroundSelection}>
<OpenBackgroundSelectorButton hideTooltip={true} {state} />
<OpenBackgroundSelectorButton hideTooltip={true} {state} htmlElem={openBackgroundButton} />
</If>
<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"
@ -638,7 +661,7 @@
<Tr t={Translations.t.privacy.title} />
</h2>
<div class="overflow-auto p-4">
<PrivacyPolicy />
<PrivacyPolicy {state}/>
</div>
</div>
</FloatOver>
@ -657,3 +680,11 @@
</div>
</FloatOver>
</If>
<CloseAnimation isOpened={state.guistate.themeIsOpened} moveTo={openMapButton} debug="theme"/>
<CloseAnimation isOpened={state.guistate.menuIsOpened} moveTo={openMenuButton} debug="menu"/>
<CloseAnimation isOpened={selectedLayer.map(sl => (sl !== undefined && sl === currentViewLayer))} moveTo={openCurrentViewLayerButton} debug="currentViewLayer"/>
<CloseAnimation isOpened={selectedElement.map(sl =>{ console.log("SE is", sl); return sl !== undefined && sl?.properties?.id === LastClickFeatureSource.newPointElementId })} moveTo={openNewElementButton} debug="newElement"/>
<CloseAnimation isOpened={state.guistate.filtersPanelIsOpened} moveTo={openFilterButton} debug="filter"/>
<CloseAnimation isOpened={state.guistate.backgroundLayerSelectionIsOpened} moveTo={openBackgroundButton} debug="bg"/>

View file

@ -70,8 +70,7 @@ export default class Locale {
}
if (!Utils.runningFromConsole) {
// @ts-ignore
window.setLanguage = function (language: string) {
window["setLanguage"] = function (language: string) {
source.setData(language)
}
}