forked from MapComplete/MapComplete
Merge branch 'develop' into feature/nsi
This commit is contained in:
commit
22c2f5166d
116 changed files with 5653 additions and 1538 deletions
55
src/UI/BigComponents/ExtraLinkButton.svelte
Normal file
55
src/UI/BigComponents/ExtraLinkButton.svelte
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<script lang="ts">
|
||||
import ExtraLinkConfig from "../../Models/ThemeConfig/ExtraLinkConfig"
|
||||
import Locale from "../i18n/Locale"
|
||||
import { Utils } from "../../Utils"
|
||||
import Translations from "../i18n/Translations"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import Pop_out from "../../assets/svg/Pop_out.svelte"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Icon from "../Map/Icon.svelte"
|
||||
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
let theme = state.layout?.id ?? ""
|
||||
let config: ExtraLinkConfig = state.layout.extraLink
|
||||
const isIframe = window !== window.top
|
||||
let basepath = window.location.host
|
||||
let showWelcomeMessageSwitch = state.featureSwitches.featureSwitchWelcomeMessage
|
||||
|
||||
const t = Translations.t.general
|
||||
const href = state.mapProperties.location.map(
|
||||
(loc) => {
|
||||
const subs = {
|
||||
...loc,
|
||||
theme: theme,
|
||||
basepath,
|
||||
language: Locale.language.data
|
||||
}
|
||||
return Utils.SubstituteKeys(config.href, subs)
|
||||
},
|
||||
[state.mapProperties.zoom]
|
||||
)
|
||||
</script>
|
||||
|
||||
|
||||
{#if config !== undefined &&
|
||||
!(config.requirements.has("iframe") && !isIframe) &&
|
||||
!(config.requirements.has("no-iframe") && isIframe) &&
|
||||
!(config.requirements.has("welcome-message") && !$showWelcomeMessageSwitch) &&
|
||||
!(config.requirements.has("no-welcome-message") && $showWelcomeMessageSwitch)}
|
||||
<div class="links-as-button">
|
||||
|
||||
<a href={$href} target={config.newTab ? "_blank" : ""} rel="noopener"
|
||||
class="flex pointer-events-auto rounded-full border-black">
|
||||
|
||||
<Icon icon={config.icon} clss="w-6 h-6 m-2"/>
|
||||
|
||||
{#if config.text}
|
||||
<Tr t={config.text} />
|
||||
{:else}
|
||||
<Tr t={t.screenToSmall.Subs({theme: state.layout.title})} />
|
||||
{/if}
|
||||
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
import { UIElement } from "../UIElement"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import ExtraLinkConfig from "../../Models/ThemeConfig/ExtraLinkConfig"
|
||||
import Img from "../Base/Img"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import Toggle from "../Input/Toggle"
|
||||
import Locale from "../i18n/Locale"
|
||||
import { Utils } from "../../Utils"
|
||||
import Svg from "../../Svg"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
|
||||
interface ExtraLinkButtonState {
|
||||
layout: { id: string; title: Translation }
|
||||
featureSwitches: { featureSwitchWelcomeMessage: Store<boolean> }
|
||||
mapProperties: {
|
||||
location: Store<{ lon: number; lat: number }>
|
||||
zoom: Store<number>
|
||||
}
|
||||
}
|
||||
export default class ExtraLinkButton extends UIElement {
|
||||
private readonly _config: ExtraLinkConfig
|
||||
private readonly state: ExtraLinkButtonState
|
||||
|
||||
constructor(state: ExtraLinkButtonState, config: ExtraLinkConfig) {
|
||||
super()
|
||||
this.state = state
|
||||
this._config = config
|
||||
}
|
||||
|
||||
protected InnerRender(): BaseUIElement {
|
||||
if (this._config === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const c = this._config
|
||||
|
||||
const isIframe = window !== window.top
|
||||
if (c.requirements?.has("iframe") && !isIframe) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (c.requirements?.has("no-iframe") && isIframe) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
let link: BaseUIElement
|
||||
const theme = this.state.layout?.id ?? ""
|
||||
const basepath = window.location.host
|
||||
const href = this.state.mapProperties.location.map(
|
||||
(loc) => {
|
||||
const subs = {
|
||||
...loc,
|
||||
theme: theme,
|
||||
basepath,
|
||||
language: Locale.language.data,
|
||||
}
|
||||
return Utils.SubstituteKeys(c.href, subs)
|
||||
},
|
||||
[this.state.mapProperties.zoom]
|
||||
)
|
||||
|
||||
let img: BaseUIElement = Svg.pop_out_svg()
|
||||
if (c.icon !== undefined) {
|
||||
img = new Img(c.icon).SetClass("h-6")
|
||||
}
|
||||
|
||||
let text: Translation
|
||||
if (c.text === undefined) {
|
||||
text = Translations.t.general.screenToSmall.Subs({
|
||||
theme: this.state.layout.title,
|
||||
})
|
||||
} else {
|
||||
text = c.text.Clone()
|
||||
}
|
||||
|
||||
link = new SubtleButton(img, text, {
|
||||
url: href,
|
||||
newTab: c.newTab,
|
||||
})
|
||||
|
||||
if (c.requirements?.has("no-welcome-message")) {
|
||||
link = new Toggle(
|
||||
undefined,
|
||||
link,
|
||||
this.state.featureSwitches.featureSwitchWelcomeMessage
|
||||
)
|
||||
}
|
||||
|
||||
if (c.requirements?.has("welcome-message")) {
|
||||
link = new Toggle(
|
||||
link,
|
||||
undefined,
|
||||
this.state.featureSwitches.featureSwitchWelcomeMessage
|
||||
)
|
||||
}
|
||||
|
||||
return link
|
||||
}
|
||||
}
|
||||
|
|
@ -91,11 +91,6 @@
|
|||
return
|
||||
}
|
||||
|
||||
if (unit !== undefined && isNaN(Number(v))) {
|
||||
value.setData(undefined)
|
||||
return
|
||||
}
|
||||
|
||||
feedback?.setData(undefined)
|
||||
if (selectedUnit.data) {
|
||||
value.setData(unit.toOsm(v, selectedUnit.data))
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
import { LinkIcon } from "@babeard/svelte-heroicons/mini"
|
||||
import Square_rounded from "../../assets/svg/Square_rounded.svelte"
|
||||
import Bug from "../../assets/svg/Bug.svelte"
|
||||
import Pop_out from "../../assets/svg/Pop_out.svelte"
|
||||
|
||||
/**
|
||||
* Renders a single icon.
|
||||
|
|
@ -123,6 +124,9 @@
|
|||
<AddSmall {color} class={clss} />
|
||||
{:else if icon === "link"}
|
||||
<LinkIcon style="--svg-color: {color}" class={twMerge(clss, "apply-fill")} />
|
||||
{:else if icon === "popout"}
|
||||
<LinkIcon style="--svg-color: {color}" />
|
||||
|
||||
{:else}
|
||||
<img class={clss ?? "h-full w-full"} src={icon} aria-hidden="true" alt="" />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
|
|||
return { error: new Translation({ "*": e }) }
|
||||
}
|
||||
|
||||
if (state.mapProperties.zoom.data < 18) {
|
||||
if (state.mapProperties.zoom.data < 16) {
|
||||
return { error: t.zoomInMore }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
|
||||
export let selectedElement: Feature
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig | undefined
|
||||
|
|
@ -71,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") {
|
||||
|
|
@ -139,11 +142,33 @@
|
|||
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) {
|
||||
|
|
@ -180,6 +205,19 @@
|
|||
checkedMappings,
|
||||
tags.data
|
||||
)
|
||||
if (state.featureSwitches.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
|
||||
|
|
@ -210,7 +248,7 @@
|
|||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -64,10 +64,14 @@
|
|||
)
|
||||
</script>
|
||||
|
||||
{#if unit.inverted}
|
||||
<div class="bold px-2">/</div>
|
||||
{/if}
|
||||
|
||||
<select bind:value={$selectedUnit}>
|
||||
{#each unit.denominations as denom (denom.canonical)}
|
||||
<option value={denom.canonical}>
|
||||
{#if $isSingle}
|
||||
{#if $isSingle || unit.inverted}
|
||||
<Tr t={denom.humanSingular} />
|
||||
{:else}
|
||||
<Tr t={denom.human.Subs({ quantity: "" })} />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
import { Utils } from "../Utils"
|
||||
import Hotkeys from "./Base/Hotkeys"
|
||||
import LevelSelector from "./BigComponents/LevelSelector.svelte"
|
||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
|
||||
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"
|
||||
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
|
||||
import type { RasterLayerPolygon } from "../Models/RasterLayers"
|
||||
|
|
@ -73,6 +72,7 @@
|
|||
import PrivacyPolicy from "./BigComponents/PrivacyPolicy.svelte"
|
||||
import { BBox } from "../Logic/BBox"
|
||||
import ReviewsOverview from "./Reviews/ReviewsOverview.svelte"
|
||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton.svelte"
|
||||
|
||||
export let state: ThemeViewState
|
||||
let layout = state.layout
|
||||
|
|
@ -260,9 +260,7 @@
|
|||
/>
|
||||
</MapControlButton>
|
||||
{/if}
|
||||
<ToSvelte
|
||||
construct={() => new ExtraLinkButton(state, layout.extraLink).SetClass("pointer-events-auto")}
|
||||
/>
|
||||
<ExtraLinkButton {state} />
|
||||
<UploadingImageCounter featureId="*" showThankYou={false} {state} />
|
||||
<PendingChangesIndicator {state} />
|
||||
<If condition={state.featureSwitchIsTesting}>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue