forked from MapComplete/MapComplete
A11y: more screenreader a11y tweaks, UX tweaks
This commit is contained in:
parent
8122826ddc
commit
3059d2ed26
16 changed files with 291 additions and 248 deletions
|
@ -1,13 +1,16 @@
|
||||||
{
|
{
|
||||||
"id": "mapcomplete-changes",
|
"id": "mapcomplete-changes",
|
||||||
"title": {
|
"title": {
|
||||||
"en": "Changes made with MapComplete"
|
"en": "Changes made with MapComplete",
|
||||||
|
"de": "Mit MapComplete vorgenommene Änderungen"
|
||||||
},
|
},
|
||||||
"shortDescription": {
|
"shortDescription": {
|
||||||
"en": "Shows changes made by MapComplete"
|
"en": "Shows changes made by MapComplete",
|
||||||
|
"de": "Zeigt die von MapComplete vorgenommenen Änderungen an"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"en": "This maps shows all the changes made with MapComplete"
|
"en": "This maps shows all the changes made with MapComplete",
|
||||||
|
"de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen"
|
||||||
},
|
},
|
||||||
"icon": "./assets/svg/logo.svg",
|
"icon": "./assets/svg/logo.svg",
|
||||||
"hideFromOverview": true,
|
"hideFromOverview": true,
|
||||||
|
@ -20,7 +23,8 @@
|
||||||
{
|
{
|
||||||
"id": "mapcomplete-changes",
|
"id": "mapcomplete-changes",
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Changeset centers"
|
"en": "Changeset centers",
|
||||||
|
"de": "Zentrum der Änderungssätze"
|
||||||
},
|
},
|
||||||
"minzoom": 0,
|
"minzoom": 0,
|
||||||
"source": {
|
"source": {
|
||||||
|
@ -31,41 +35,48 @@
|
||||||
},
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Changeset for {theme}"
|
"en": "Changeset for {theme}",
|
||||||
|
"de": "Änderungssatz für {theme}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"en": "Shows all MapComplete changes"
|
"en": "Shows all MapComplete changes",
|
||||||
|
"de": "Zeigt alle MapComplete-Änderungen"
|
||||||
},
|
},
|
||||||
"tagRenderings": [
|
"tagRenderings": [
|
||||||
{
|
{
|
||||||
"id": "show_changeset_id",
|
"id": "show_changeset_id",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
|
"en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>",
|
||||||
|
"de": "Änderungssatz <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "contributor",
|
"id": "contributor",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What contributor did make this change?"
|
"en": "What contributor did make this change?",
|
||||||
|
"de": "Wer hat diese Änderung vorgenommen?"
|
||||||
},
|
},
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "user"
|
"key": "user"
|
||||||
},
|
},
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Change made by <a href='https://openstreetmap.org/user/{user}' target='_blank'>{user}</a>"
|
"en": "Change made by <a href='https://openstreetmap.org/user/{user}' target='_blank'>{user}</a>",
|
||||||
|
"de": "Änderung von <a href='https://openstreetmap.org/user/{user}' target='_blank'>{user}</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "theme-id",
|
"id": "theme-id",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What theme was used to make this change?"
|
"en": "What theme was used to make this change?",
|
||||||
|
"de": "Welches Theme wurde für diese Änderung verwendet?"
|
||||||
},
|
},
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "theme"
|
"key": "theme"
|
||||||
},
|
},
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Change with theme <a href='https://mapcomplete.org/{theme}'>{theme}</a>"
|
"en": "Change with theme <a href='https://mapcomplete.org/{theme}'>{theme}</a>",
|
||||||
|
"de": "Geändert mit Thema <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -74,19 +85,23 @@
|
||||||
"key": "locale"
|
"key": "locale"
|
||||||
},
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What locale (language) was this change made in?"
|
"en": "What locale (language) was this change made in?",
|
||||||
|
"de": "In welcher Benutzersprache wurde diese Änderung vorgenommen?"
|
||||||
},
|
},
|
||||||
"render": {
|
"render": {
|
||||||
"en": "User locale is {locale}"
|
"en": "User locale is {locale}",
|
||||||
|
"de": "Benutzersprache {locale}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "host",
|
"id": "host",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Change with with <a href='{host}'>{host}</a>"
|
"en": "Change with with <a href='{host}'>{host}</a>",
|
||||||
|
"de": "Geändert über <a href='{host}'>{host}</a>"
|
||||||
},
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What host (website) was this change made with?"
|
"en": "What host (website) was this change made with?",
|
||||||
|
"de": "Über welchen Host (Webseite) wurde diese Änderung vorgenommen?"
|
||||||
},
|
},
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "host"
|
"key": "host"
|
||||||
|
@ -107,10 +122,12 @@
|
||||||
{
|
{
|
||||||
"id": "version",
|
"id": "version",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What version of MapComplete was used to make this change?"
|
"en": "What version of MapComplete was used to make this change?",
|
||||||
|
"de": "Welche Version von MapComplete wurde verwendet, um diese Änderung vorzunehmen?"
|
||||||
},
|
},
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Made with {editor}"
|
"en": "Made with {editor}",
|
||||||
|
"de": "Erstellt mit {editor}"
|
||||||
},
|
},
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "editor"
|
"key": "editor"
|
||||||
|
@ -460,7 +477,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Themename contains {search}"
|
"en": "Themename contains {search}",
|
||||||
|
"de": "Themename enthält {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -476,7 +494,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Themename does <b>not</b> contain {search}"
|
"en": "Themename does <b>not</b> contain {search}",
|
||||||
|
"de": "Der Name enthält <b>nicht</b> {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -492,7 +511,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Made by contributor {search}"
|
"en": "Made by contributor {search}",
|
||||||
|
"de": "Der Name enthält <b>nicht</b> {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -508,7 +528,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "<b>Not</b> made by contributor {search}"
|
"en": "<b>Not</b> made by contributor {search}",
|
||||||
|
"de": "<b>Nicht</b> erstellt von {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -525,7 +546,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Made before {search}"
|
"en": "Made before {search}",
|
||||||
|
"de": "Erstellt vor {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -542,7 +564,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Made after {search}"
|
"en": "Made after {search}",
|
||||||
|
"de": "Erstellt nach {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -558,7 +581,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "User language (iso-code) {search}"
|
"en": "User language (iso-code) {search}",
|
||||||
|
"de": "Benutzersprache (ISO-Code) {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -574,7 +598,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Made with host {search}"
|
"en": "Made with host {search}",
|
||||||
|
"de": "Erstellt mit Host {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -585,7 +610,8 @@
|
||||||
{
|
{
|
||||||
"osmTags": "add-image>0",
|
"osmTags": "add-image>0",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Changeset added at least one image"
|
"en": "Changeset added at least one image",
|
||||||
|
"de": "Änderungssatz hat mindestens ein Bild hinzugefügt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -596,7 +622,8 @@
|
||||||
{
|
{
|
||||||
"osmTags": "theme!=grb",
|
"osmTags": "theme!=grb",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Exclude GRB theme"
|
"en": "Exclude GRB theme",
|
||||||
|
"de": "GRB-Theme ausschließen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -607,7 +634,8 @@
|
||||||
{
|
{
|
||||||
"osmTags": "theme!=etymology",
|
"osmTags": "theme!=etymology",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Exclude etymology theme"
|
"en": "Exclude etymology theme",
|
||||||
|
"de": "Etymologie-Thema ausschließen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -622,7 +650,8 @@
|
||||||
{
|
{
|
||||||
"id": "link_to_more",
|
"id": "link_to_more",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>"
|
"en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>",
|
||||||
|
"de": "Mehr Statistiken gibt es <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>hier</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -8543,6 +8543,18 @@
|
||||||
},
|
},
|
||||||
"question": "Does this stair have a handrail?"
|
"question": "Does this stair have a handrail?"
|
||||||
},
|
},
|
||||||
|
"incline": {
|
||||||
|
"mappings": {
|
||||||
|
"0": {
|
||||||
|
"then": "The upward direction is {direction_absolute()}"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"then": "The downward direction is {direction_absolute()}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"question": "What is the incline of these stairs?",
|
||||||
|
"render": "These stairs have an incline of {incline}"
|
||||||
|
},
|
||||||
"multilevels": {
|
"multilevels": {
|
||||||
"override": {
|
"override": {
|
||||||
"question": "Between which levels are these stairs?",
|
"question": "Between which levels are these stairs?",
|
||||||
|
|
|
@ -370,7 +370,7 @@
|
||||||
"useSearch": "Gebruik de zoekfunctie hierboven om meer opties te zien",
|
"useSearch": "Gebruik de zoekfunctie hierboven om meer opties te zien",
|
||||||
"useSearchForMore": "Gebruik de zoekfunctie om {total} meer waarden te vinden…",
|
"useSearchForMore": "Gebruik de zoekfunctie om {total} meer waarden te vinden…",
|
||||||
"visualFeedback": {
|
"visualFeedback": {
|
||||||
"closestFeaturesAre": "{n} object in beeld.",
|
"closestFeaturesAre": "{n} objecten in beeld.",
|
||||||
"east": "Naar het oosten",
|
"east": "Naar het oosten",
|
||||||
"in": "Aan het inzoomen naar zoomlevel {z}",
|
"in": "Aan het inzoomen naar zoomlevel {z}",
|
||||||
"islocked": "Bewegen vergrendeld rond je huidige locatie. Duw op de geolocatie-knop om te ontgrendelen.",
|
"islocked": "Bewegen vergrendeld rond je huidige locatie. Duw op de geolocatie-knop om te ontgrendelen.",
|
||||||
|
@ -378,6 +378,7 @@
|
||||||
"navigation": "Gebruik de pijltjestoetsen om te bewegen. Druk op spatie om het meest centrale punt te selecteren. Druk op een cijfertoets om andere items te selecteren.",
|
"navigation": "Gebruik de pijltjestoetsen om te bewegen. Druk op spatie om het meest centrale punt te selecteren. Druk op een cijfertoets om andere items te selecteren.",
|
||||||
"noCloseFeatures": "Niet in beeld",
|
"noCloseFeatures": "Niet in beeld",
|
||||||
"north": "Naar het noorden",
|
"north": "Naar het noorden",
|
||||||
|
"oneFeatureInView": "Eén object in beeld.",
|
||||||
"out": "Aan het uitzoomen naar zoomlevel {z}",
|
"out": "Aan het uitzoomen naar zoomlevel {z}",
|
||||||
"south": "Naar het zuiden",
|
"south": "Naar het zuiden",
|
||||||
"unlocked": "Bewegen ontgrendeld",
|
"unlocked": "Bewegen ontgrendeld",
|
||||||
|
|
|
@ -79,7 +79,7 @@ export default class TagRenderingConfig {
|
||||||
public readonly mappings?: Mapping[]
|
public readonly mappings?: Mapping[]
|
||||||
public readonly editButtonAriaLabel?: Translation
|
public readonly editButtonAriaLabel?: Translation
|
||||||
public readonly labels: string[]
|
public readonly labels: string[]
|
||||||
public readonly classes: string[]
|
public readonly classes: string[] | undefined
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
config: string | TagRenderingConfigJson | QuestionableTagRenderingConfigJson,
|
config: string | TagRenderingConfigJson | QuestionableTagRenderingConfigJson,
|
||||||
|
@ -131,6 +131,9 @@ export default class TagRenderingConfig {
|
||||||
this.classes = json.classes ?? []
|
this.classes = json.classes ?? []
|
||||||
}
|
}
|
||||||
this.classes = [].concat(...this.classes.map((cl) => cl.split(" ")))
|
this.classes = [].concat(...this.classes.map((cl) => cl.split(" ")))
|
||||||
|
if (this.classes.length === 0) {
|
||||||
|
this.classes = undefined
|
||||||
|
}
|
||||||
|
|
||||||
this.render = Translations.T(<any>json.render, translationKey + ".render")
|
this.render = Translations.T(<any>json.render, translationKey + ".render")
|
||||||
this.question = Translations.T(json.question, translationKey + ".question")
|
this.question = Translations.T(json.question, translationKey + ".question")
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const uiElem = typeof construct === "function" ? construct() : construct
|
const uiElem = typeof construct === "function" ? construct() : construct
|
||||||
html = uiElem?.ConstructElement()
|
html = uiElem?.ConstructElement()
|
||||||
|
|
||||||
if (html !== undefined) {
|
if (html !== undefined) {
|
||||||
elem?.replaceWith(html)
|
elem?.replaceWith(html)
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,13 +127,6 @@
|
||||||
<button slot="cancel" class="items-center" on:click={() => (currentState = "start")}>
|
<button slot="cancel" class="items-center" on:click={() => (currentState = "start")}>
|
||||||
<Tr t={t.cancel} />
|
<Tr t={t.cancel} />
|
||||||
</button>
|
</button>
|
||||||
<XCircleIcon
|
|
||||||
slot="upper-right"
|
|
||||||
class="h-8 w-8 cursor-pointer"
|
|
||||||
on:click={() => {
|
|
||||||
currentState = "start"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div slot="under-buttons">
|
<div slot="under-buttons">
|
||||||
{#if selectedTags !== undefined}
|
{#if selectedTags !== undefined}
|
||||||
|
|
|
@ -20,6 +20,7 @@ export class ShareLinkViz implements SpecialVisualization {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
needsUrls = []
|
needsUrls = []
|
||||||
|
svelteBased = true
|
||||||
|
|
||||||
public constr(
|
public constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
|
@ -52,6 +53,8 @@ export class ShareLinkViz implements SpecialVisualization {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SvelteUIElement(ShareButton, { generateShareData, text })
|
return new SvelteUIElement(ShareButton, { generateShareData, text }).SetClass(
|
||||||
|
"w-full h-full"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let skippedQuestions = new UIEventSource<Set<string>>(new Set<string>())
|
let skippedQuestions = new UIEventSource<Set<string>>(new Set<string>())
|
||||||
let questionboxElem: HTMLBaseElement
|
let questionboxElem: HTMLDivElement
|
||||||
let questionsToAsk = tags.map(
|
let questionsToAsk = tags.map(
|
||||||
(tags) => {
|
(tags) => {
|
||||||
const baseQuestions = (layer.tagRenderings ?? [])?.filter(
|
const baseQuestions = (layer.tagRenderings ?? [])?.filter(
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
)
|
)
|
||||||
const questionsToAsk: TagRenderingConfig[] = []
|
const questionsToAsk: TagRenderingConfig[] = []
|
||||||
for (const baseQuestion of baseQuestions) {
|
for (const baseQuestion of baseQuestions) {
|
||||||
if (skippedQuestions.data.has(baseQuestion.id) > 0) {
|
if (skippedQuestions.data.has(baseQuestion.id)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
@ -88,6 +88,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
bind:this={questionboxElem}
|
bind:this={questionboxElem}
|
||||||
|
aria-live="polite"
|
||||||
class="marker-questionbox-root"
|
class="marker-questionbox-root"
|
||||||
class:hidden={$questionsToAsk.length === 0 && skipped === 0 && answered === 0}
|
class:hidden={$questionsToAsk.length === 0 && skipped === 0 && answered === 0}
|
||||||
>
|
>
|
||||||
|
|
|
@ -29,9 +29,9 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if config !== undefined && (config?.condition === undefined || config.condition.matchesProperties($tags))}
|
{#if config !== undefined && (config?.condition === undefined || config.condition.matchesProperties($tags))}
|
||||||
<div {id} class={twMerge("link-underline inline-block w-full", config?.classes, extraClasses)}>
|
<div {id} class={twMerge("link-underline inline-block w-full", config?.classes ?? "flex items-center", extraClasses)}>
|
||||||
{#if $trs.length === 1}
|
{#if $trs.length === 1}
|
||||||
<TagRenderingMapping mapping={$trs[0]} {tags} {state} {selectedElement} {layer} />
|
<TagRenderingMapping clss={extraClasses} mapping={$trs[0]} {tags} {state} {selectedElement} {layer} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if $trs.length > 1}
|
{#if $trs.length > 1}
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -98,16 +98,6 @@
|
||||||
>
|
>
|
||||||
<Tr t={Translations.t.general.cancel} />
|
<Tr t={Translations.t.general.cancel} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
slot="upper-right"
|
|
||||||
class="h-8 w-8 cursor-pointer border-none p-0"
|
|
||||||
use:ariaLabel={Translations.t.general.cancel}
|
|
||||||
on:click={() => {
|
|
||||||
editMode = false
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<XCircleIcon />
|
|
||||||
</button>
|
|
||||||
</TagRenderingQuestion>
|
</TagRenderingQuestion>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="low-interaction flex items-center justify-between overflow-hidden rounded px-2">
|
<div class="low-interaction flex items-center justify-between overflow-hidden rounded px-2">
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
export let tags: UIEventSource<Record<string, string>>
|
export let tags: UIEventSource<Record<string, string>>
|
||||||
export let state: SpecialVisualizationState
|
export let state: SpecialVisualizationState
|
||||||
export let layer: LayerConfig
|
export let layer: LayerConfig
|
||||||
|
|
||||||
export let mapping: {
|
export let mapping: {
|
||||||
readonly then: Translation
|
readonly then: Translation
|
||||||
readonly searchTerms?: Record<string, string[]>
|
readonly searchTerms?: Record<string, string[]>
|
||||||
|
@ -30,7 +29,7 @@
|
||||||
|
|
||||||
{#if mapping.icon !== undefined}
|
{#if mapping.icon !== undefined}
|
||||||
<div class="inline-flex items-center">
|
<div class="inline-flex items-center">
|
||||||
<Icon icon={mapping.icon} clss={twJoin(`mapping-icon-${mapping.iconClass}`, "mx-2")} />
|
<Icon icon={mapping.icon} clss={twJoin(`mapping-icon-${mapping.iconClass}`, "mr-2")} />
|
||||||
<SpecialTranslation t={mapping.then} {tags} {state} {layer} feature={selectedElement} />
|
<SpecialTranslation t={mapping.then} {tags} {state} {layer} feature={selectedElement} />
|
||||||
</div>
|
</div>
|
||||||
{:else if mapping.then !== undefined}
|
{:else if mapping.then !== undefined}
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $matchesTerm && !$mappingIsHidden}
|
{#if $matchesTerm && !$mappingIsHidden}
|
||||||
<label class={twJoin("flex", mappingIsSelected && "checked")}>
|
<label class={twJoin("flex gap-x-1", mappingIsSelected && "checked")}>
|
||||||
<slot />
|
<slot />
|
||||||
<TagRenderingMapping {mapping} {tags} {state} {selectedElement} {layer} />
|
<TagRenderingMapping {mapping} {tags} {state} {selectedElement} {layer} />
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -151,7 +151,7 @@
|
||||||
$freeformInput,
|
$freeformInput,
|
||||||
selectedMapping,
|
selectedMapping,
|
||||||
checkedMappings,
|
checkedMappings,
|
||||||
tags.data
|
tags.data,
|
||||||
)
|
)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Could not calculate changeSpecification:", e)
|
console.error("Could not calculate changeSpecification:", e)
|
||||||
|
@ -213,136 +213,53 @@
|
||||||
onDestroy(
|
onDestroy(
|
||||||
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => {
|
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => {
|
||||||
numberOfCs = ud.csCount
|
numberOfCs = ud.csCount
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if question !== undefined}
|
{#if question !== undefined}
|
||||||
<form
|
<div class="relative">
|
||||||
class="interactive border-interactive relative flex flex-col overflow-y-auto px-2"
|
|
||||||
style="max-height: 75vh"
|
|
||||||
on:submit|preventDefault={() => onSave()}
|
|
||||||
>
|
|
||||||
<label class="neutral-label">
|
|
||||||
<div class="interactive sticky top-0 flex justify-between pt-1" style="z-index: 11">
|
|
||||||
<span class="font-bold">
|
|
||||||
<SpecialTranslation t={question} {tags} {state} {layer} feature={selectedElement} />
|
|
||||||
</span>
|
|
||||||
<slot name="upper-right" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if config.questionhint}
|
<form
|
||||||
<div class="max-h-60 overflow-y-auto">
|
class="interactive border-interactive relative flex flex-col overflow-y-auto px-2"
|
||||||
<SpecialTranslation
|
style="max-height: 75vh"
|
||||||
t={config.questionhint}
|
on:submit|preventDefault={() => onSave()}
|
||||||
{tags}
|
>
|
||||||
{state}
|
<fieldset>
|
||||||
{layer}
|
|
||||||
feature={selectedElement}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if config.mappings?.length >= 8}
|
<legend>
|
||||||
<div class="sticky flex w-full" aria-hidden="true">
|
<div class="interactive sticky top-0 flex justify-between pt-1 font-bold" style="z-index: 11">
|
||||||
<Search class="h-6 w-6" />
|
<SpecialTranslation t={question} {tags} {state} {layer} feature={selectedElement} />
|
||||||
<input
|
</div>
|
||||||
type="text"
|
|
||||||
bind:value={$searchTerm}
|
|
||||||
class="w-full"
|
|
||||||
use:placeholder={Translations.t.general.searchAnswer}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if config.freeform?.key && !(mappings?.length > 0)}
|
{#if config.questionhint}
|
||||||
<!-- There are no options to choose from, simply show the input element: fill out the text field -->
|
<div class="max-h-60 overflow-y-auto">
|
||||||
<FreeformInput
|
<SpecialTranslation
|
||||||
{config}
|
t={config.questionhint}
|
||||||
{tags}
|
{tags}
|
||||||
{feedback}
|
{state}
|
||||||
{unit}
|
{layer}
|
||||||
{state}
|
feature={selectedElement}
|
||||||
feature={selectedElement}
|
/>
|
||||||
value={freeformInput}
|
</div>
|
||||||
on:submit={onSave}
|
{/if}
|
||||||
/>
|
</legend>
|
||||||
{:else if mappings !== undefined && !config.multiAnswer}
|
|
||||||
<!-- Simple radiobuttons as mapping -->
|
{#if config.mappings?.length >= 8}
|
||||||
<div class="flex flex-col">
|
<div class="sticky flex w-full" aria-hidden="true">
|
||||||
{#each config.mappings as mapping, i (mapping.then)}
|
<Search class="h-6 w-6" />
|
||||||
<!-- Even though we have a list of 'mappings' already, we still iterate over the list as to keep the original indices-->
|
<input
|
||||||
<TagRenderingMappingInput
|
type="text"
|
||||||
{mapping}
|
bind:value={$searchTerm}
|
||||||
{tags}
|
class="w-full"
|
||||||
{state}
|
use:placeholder={Translations.t.general.searchAnswer}
|
||||||
{selectedElement}
|
/>
|
||||||
{layer}
|
</div>
|
||||||
{searchTerm}
|
{/if}
|
||||||
mappingIsSelected={selectedMapping === i}
|
|
||||||
>
|
{#if config.freeform?.key && !(mappings?.length > 0)}
|
||||||
<input
|
<!-- There are no options to choose from, simply show the input element: fill out the text field -->
|
||||||
type="radio"
|
|
||||||
bind:group={selectedMapping}
|
|
||||||
name={"mappings-radio-" + config.id}
|
|
||||||
value={i}
|
|
||||||
on:keypress={(e) => onInputKeypress(e)}
|
|
||||||
/>
|
|
||||||
</TagRenderingMappingInput>
|
|
||||||
{/each}
|
|
||||||
{#if config.freeform?.key}
|
|
||||||
<label class="flex">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
bind:group={selectedMapping}
|
|
||||||
name={"mappings-radio-" + config.id}
|
|
||||||
value={config.mappings?.length}
|
|
||||||
on:keypress={(e) => onInputKeypress(e)}
|
|
||||||
/>
|
|
||||||
<FreeformInput
|
|
||||||
{config}
|
|
||||||
{tags}
|
|
||||||
{feedback}
|
|
||||||
{unit}
|
|
||||||
{state}
|
|
||||||
feature={selectedElement}
|
|
||||||
value={freeformInput}
|
|
||||||
on:selected={() => (selectedMapping = config.mappings?.length)}
|
|
||||||
on:submit={onSave}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{:else if mappings !== undefined && config.multiAnswer}
|
|
||||||
<!-- Multiple answers can be chosen: checkboxes -->
|
|
||||||
<div class="flex flex-col">
|
|
||||||
{#each config.mappings as mapping, i (mapping.then)}
|
|
||||||
<TagRenderingMappingInput
|
|
||||||
{mapping}
|
|
||||||
{tags}
|
|
||||||
{state}
|
|
||||||
{selectedElement}
|
|
||||||
{layer}
|
|
||||||
{searchTerm}
|
|
||||||
mappingIsSelected={checkedMappings[i]}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
name={"mappings-checkbox-" + config.id + "-" + i}
|
|
||||||
bind:checked={checkedMappings[i]}
|
|
||||||
on:keypress={(e) => onInputKeypress(e)}
|
|
||||||
/>
|
|
||||||
</TagRenderingMappingInput>
|
|
||||||
{/each}
|
|
||||||
{#if config.freeform?.key}
|
|
||||||
<label class="flex">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
name={"mappings-checkbox-" + config.id + "-" + config.mappings?.length}
|
|
||||||
bind:checked={checkedMappings[config.mappings.length]}
|
|
||||||
on:keypress={(e) => onInputKeypress(e)}
|
|
||||||
/>
|
|
||||||
<FreeformInput
|
<FreeformInput
|
||||||
{config}
|
{config}
|
||||||
{tags}
|
{tags}
|
||||||
|
@ -353,39 +270,122 @@
|
||||||
value={freeformInput}
|
value={freeformInput}
|
||||||
on:submit={onSave}
|
on:submit={onSave}
|
||||||
/>
|
/>
|
||||||
</label>
|
{:else if mappings !== undefined && !config.multiAnswer}
|
||||||
|
<!-- Simple radiobuttons as mapping -->
|
||||||
|
<div class="flex flex-col">
|
||||||
|
{#each config.mappings as mapping, i (mapping.then)}
|
||||||
|
<!-- Even though we have a list of 'mappings' already, we still iterate over the list as to keep the original indices-->
|
||||||
|
<TagRenderingMappingInput
|
||||||
|
{mapping}
|
||||||
|
{tags}
|
||||||
|
{state}
|
||||||
|
{selectedElement}
|
||||||
|
{layer}
|
||||||
|
{searchTerm}
|
||||||
|
mappingIsSelected={selectedMapping === i}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
bind:group={selectedMapping}
|
||||||
|
name={"mappings-radio-" + config.id}
|
||||||
|
value={i}
|
||||||
|
on:keypress={(e) => onInputKeypress(e)}
|
||||||
|
/>
|
||||||
|
</TagRenderingMappingInput>
|
||||||
|
{/each}
|
||||||
|
{#if config.freeform?.key}
|
||||||
|
<label class="flex gap-x-1">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
bind:group={selectedMapping}
|
||||||
|
name={"mappings-radio-" + config.id}
|
||||||
|
value={config.mappings?.length}
|
||||||
|
on:keypress={(e) => onInputKeypress(e)}
|
||||||
|
/>
|
||||||
|
<FreeformInput
|
||||||
|
{config}
|
||||||
|
{tags}
|
||||||
|
{feedback}
|
||||||
|
{unit}
|
||||||
|
{state}
|
||||||
|
feature={selectedElement}
|
||||||
|
value={freeformInput}
|
||||||
|
on:selected={() => (selectedMapping = config.mappings?.length)}
|
||||||
|
on:submit={onSave}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{:else if mappings !== undefined && config.multiAnswer}
|
||||||
|
<!-- Multiple answers can be chosen: checkboxes -->
|
||||||
|
<div class="flex flex-col">
|
||||||
|
{#each config.mappings as mapping, i (mapping.then)}
|
||||||
|
<TagRenderingMappingInput
|
||||||
|
{mapping}
|
||||||
|
{tags}
|
||||||
|
{state}
|
||||||
|
{selectedElement}
|
||||||
|
{layer}
|
||||||
|
{searchTerm}
|
||||||
|
mappingIsSelected={checkedMappings[i]}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name={"mappings-checkbox-" + config.id + "-" + i}
|
||||||
|
bind:checked={checkedMappings[i]}
|
||||||
|
on:keypress={(e) => onInputKeypress(e)}
|
||||||
|
/>
|
||||||
|
</TagRenderingMappingInput>
|
||||||
|
{/each}
|
||||||
|
{#if config.freeform?.key}
|
||||||
|
<label class="flex gap-x-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name={"mappings-checkbox-" + config.id + "-" + config.mappings?.length}
|
||||||
|
bind:checked={checkedMappings[config.mappings.length]}
|
||||||
|
on:keypress={(e) => onInputKeypress(e)}
|
||||||
|
/>
|
||||||
|
<FreeformInput
|
||||||
|
{config}
|
||||||
|
{tags}
|
||||||
|
{feedback}
|
||||||
|
{unit}
|
||||||
|
{state}
|
||||||
|
feature={selectedElement}
|
||||||
|
value={freeformInput}
|
||||||
|
on:submit={onSave}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<LoginToggle {state}>
|
||||||
|
<Loading slot="loading" />
|
||||||
|
<SubtleButton slot="not-logged-in" on:click={() => state?.osmConnection?.AttemptLogin()}>
|
||||||
|
<Login slot="image" class="h-8 w-8" />
|
||||||
|
<Tr t={Translations.t.general.loginToStart} slot="message" />
|
||||||
|
</SubtleButton>
|
||||||
|
{#if $feedback !== undefined}
|
||||||
|
<div class="alert" aria-live="assertive" role="alert">
|
||||||
|
<Tr t={$feedback} />
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
<div
|
||||||
{/if}
|
class="interactive sticky bottom-0 flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap"
|
||||||
</label>
|
style="z-index: 11"
|
||||||
|
|
||||||
<LoginToggle {state}>
|
|
||||||
<Loading slot="loading" />
|
|
||||||
<SubtleButton slot="not-logged-in" on:click={() => state?.osmConnection?.AttemptLogin()}>
|
|
||||||
<Login slot="image" class="h-8 w-8" />
|
|
||||||
<Tr t={Translations.t.general.loginToStart} slot="message" />
|
|
||||||
</SubtleButton>
|
|
||||||
{#if $feedback !== undefined}
|
|
||||||
<div class="alert" aria-live="assertive" role="alert">
|
|
||||||
<Tr t={$feedback} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<div
|
|
||||||
class="interactive sticky bottom-0 flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap"
|
|
||||||
style="z-index: 11"
|
|
||||||
>
|
|
||||||
<!-- TagRenderingQuestion-buttons -->
|
|
||||||
<slot name="cancel" />
|
|
||||||
<slot name="save-button" {selectedTags}>
|
|
||||||
<button
|
|
||||||
on:click={onSave}
|
|
||||||
class={twJoin(selectedTags === undefined ? "disabled" : "button-shadow", "primary")}
|
|
||||||
>
|
>
|
||||||
<Tr t={Translations.t.general.save} />
|
<!-- TagRenderingQuestion-buttons -->
|
||||||
</button>
|
<slot name="cancel" />
|
||||||
</slot>
|
<slot name="save-button" {selectedTags}>
|
||||||
</div>
|
<button
|
||||||
{#if UserRelatedState.SHOW_TAGS_VALUES.indexOf($showTags) >= 0 || ($showTags === "" && numberOfCs >= Constants.userJourney.tagsVisibleAt) || $featureSwitchIsTesting || $featureSwitchIsDebugging}
|
on:click={onSave}
|
||||||
|
class={twJoin(selectedTags === undefined ? "disabled" : "button-shadow", "primary")}
|
||||||
|
>
|
||||||
|
<Tr t={Translations.t.general.save} />
|
||||||
|
</button>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
{#if UserRelatedState.SHOW_TAGS_VALUES.indexOf($showTags) >= 0 || ($showTags === "" && numberOfCs >= Constants.userJourney.tagsVisibleAt) || $featureSwitchIsTesting || $featureSwitchIsDebugging}
|
||||||
<span class="flex flex-wrap justify-between">
|
<span class="flex flex-wrap justify-between">
|
||||||
<TagHint {state} tags={selectedTags} currentProperties={$tags} />
|
<TagHint {state} tags={selectedTags} currentProperties={$tags} />
|
||||||
<span class="flex flex-wrap">
|
<span class="flex flex-wrap">
|
||||||
|
@ -397,8 +397,12 @@
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
<slot name="under-buttons" />
|
<slot name="under-buttons" />
|
||||||
</LoginToggle>
|
</LoginToggle>
|
||||||
</form>
|
</fieldset>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -98,7 +98,7 @@ export interface SpecialVisualization {
|
||||||
readonly funcName: string
|
readonly funcName: string
|
||||||
readonly docs: string | BaseUIElement
|
readonly docs: string | BaseUIElement
|
||||||
readonly example?: string
|
readonly example?: string
|
||||||
readonly needsUrls: string[] | ((args: string[]) => string)
|
readonly needsUrls?: string[] | ((args: string[]) => string)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that this special visualisation will make requests to the 'alLNodesDatabase' and that it thus should be included
|
* Indicates that this special visualisation will make requests to the 'alLNodesDatabase' and that it thus should be included
|
||||||
|
|
|
@ -102,6 +102,7 @@ class NearbyImageVis implements SpecialVisualization {
|
||||||
"A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature"
|
"A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature"
|
||||||
funcName = "nearby_images"
|
funcName = "nearby_images"
|
||||||
needsUrls = NearbyImagesSearch.apiUrls
|
needsUrls = NearbyImagesSearch.apiUrls
|
||||||
|
svelteBased = true
|
||||||
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
|
@ -141,6 +142,7 @@ class StealViz implements SpecialVisualization {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
needsUrls = []
|
needsUrls = []
|
||||||
|
svelteBased = true
|
||||||
|
|
||||||
constr(state: SpecialVisualizationState, featureTags, args) {
|
constr(state: SpecialVisualizationState, featureTags, args) {
|
||||||
const [featureIdKey, layerAndtagRenderingIds] = args
|
const [featureIdKey, layerAndtagRenderingIds] = args
|
||||||
|
@ -213,6 +215,7 @@ export class QuestionViz implements SpecialVisualization {
|
||||||
doc: "One or more ';'-separated labels of questions which should _not_ be included",
|
doc: "One or more ';'-separated labels of questions which should _not_ be included",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
svelteBased = true
|
||||||
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
|
@ -236,7 +239,7 @@ export class QuestionViz implements SpecialVisualization {
|
||||||
state,
|
state,
|
||||||
onlyForLabels: labels,
|
onlyForLabels: labels,
|
||||||
notForLabels: blacklist,
|
notForLabels: blacklist,
|
||||||
})
|
}).SetClass("w-full")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +440,7 @@ export default class SpecialVisualizations {
|
||||||
funcName: "add_new_point",
|
funcName: "add_new_point",
|
||||||
docs: "An element which allows to add a new point on the 'last_click'-location. Only makes sense in the layer `last_click`",
|
docs: "An element which allows to add a new point on the 'last_click'-location. Only makes sense in the layer `last_click`",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
constr(state: SpecialVisualizationState, _, __, feature): BaseUIElement {
|
constr(state: SpecialVisualizationState, _, __, feature): BaseUIElement {
|
||||||
let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
||||||
return new SvelteUIElement(AddNewPoint, {
|
return new SvelteUIElement(AddNewPoint, {
|
||||||
|
@ -449,7 +452,7 @@ export default class SpecialVisualizations {
|
||||||
{
|
{
|
||||||
funcName: "user_profile",
|
funcName: "user_profile",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
docs: "A component showing information about the currently logged in user (username, profile description, profile picture + link to edit them). Mostly meant to be used in the 'user-settings'",
|
docs: "A component showing information about the currently logged in user (username, profile description, profile picture + link to edit them). Mostly meant to be used in the 'user-settings'",
|
||||||
constr(state: SpecialVisualizationState): BaseUIElement {
|
constr(state: SpecialVisualizationState): BaseUIElement {
|
||||||
return new SvelteUIElement(UserProfile, {
|
return new SvelteUIElement(UserProfile, {
|
||||||
|
@ -460,7 +463,6 @@ export default class SpecialVisualizations {
|
||||||
{
|
{
|
||||||
funcName: "language_picker",
|
funcName: "language_picker",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
docs: "A component to set the language of the user interface",
|
docs: "A component to set the language of the user interface",
|
||||||
constr(state: SpecialVisualizationState): BaseUIElement {
|
constr(state: SpecialVisualizationState): BaseUIElement {
|
||||||
return new SvelteUIElement(LanguagePicker, {
|
return new SvelteUIElement(LanguagePicker, {
|
||||||
|
@ -477,6 +479,7 @@ export default class SpecialVisualizations {
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [Constants.osmAuthConfig.url],
|
needsUrls: [Constants.osmAuthConfig.url],
|
||||||
docs: "Shows a button where the user can log out",
|
docs: "Shows a button where the user can log out",
|
||||||
|
|
||||||
constr(state: SpecialVisualizationState): BaseUIElement {
|
constr(state: SpecialVisualizationState): BaseUIElement {
|
||||||
return new SvelteUIElement(LogoutButton, { osmConnection: state.osmConnection })
|
return new SvelteUIElement(LogoutButton, { osmConnection: state.osmConnection })
|
||||||
},
|
},
|
||||||
|
@ -488,7 +491,7 @@ export default class SpecialVisualizations {
|
||||||
funcName: "split_button",
|
funcName: "split_button",
|
||||||
docs: "Adds a button which allows to split a way",
|
docs: "Adds a button which allows to split a way",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
tagSource: UIEventSource<Record<string, string>>
|
tagSource: UIEventSource<Record<string, string>>
|
||||||
|
@ -509,7 +512,7 @@ export default class SpecialVisualizations {
|
||||||
funcName: "move_button",
|
funcName: "move_button",
|
||||||
docs: "Adds a button which allows to move the object to another location. The config will be read from the layer config",
|
docs: "Adds a button which allows to move the object to another location. The config will be read from the layer config",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
tagSource: UIEventSource<Record<string, string>>,
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
@ -532,7 +535,7 @@ export default class SpecialVisualizations {
|
||||||
funcName: "delete_button",
|
funcName: "delete_button",
|
||||||
docs: "Adds a button which allows to delete the object at this location. The config will be read from the layer config",
|
docs: "Adds a button which allows to delete the object at this location. The config will be read from the layer config",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
tagSource: UIEventSource<Record<string, string>>,
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
@ -597,6 +600,7 @@ export default class SpecialVisualizations {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
needsUrls: [...Wikidata.neededUrls, ...Wikipedia.neededUrls],
|
needsUrls: [...Wikidata.neededUrls, ...Wikipedia.neededUrls],
|
||||||
|
|
||||||
example:
|
example:
|
||||||
"`{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height",
|
"`{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height",
|
||||||
constr: (_, tagsSource, args) => {
|
constr: (_, tagsSource, args) => {
|
||||||
|
@ -650,7 +654,6 @@ export default class SpecialVisualizations {
|
||||||
funcName: "all_tags",
|
funcName: "all_tags",
|
||||||
docs: "Prints all key-value pairs of the object - used for debugging",
|
docs: "Prints all key-value pairs of the object - used for debugging",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
constr: (state, tags: UIEventSource<any>) =>
|
constr: (state, tags: UIEventSource<any>) =>
|
||||||
new SvelteUIElement(AllTagsPanel, { tags, state }),
|
new SvelteUIElement(AllTagsPanel, { tags, state }),
|
||||||
},
|
},
|
||||||
|
@ -820,7 +823,7 @@ export default class SpecialVisualizations {
|
||||||
doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__",
|
doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
needsUrls: [],
|
|
||||||
example:
|
example:
|
||||||
"A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}`",
|
"A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}`",
|
||||||
constr: (state, tagSource: UIEventSource<any>, args) => {
|
constr: (state, tagSource: UIEventSource<any>, args) => {
|
||||||
|
@ -848,14 +851,13 @@ export default class SpecialVisualizations {
|
||||||
doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__",
|
doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
needsUrls: [],
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
tags: UIEventSource<Record<string, string>>,
|
tags: UIEventSource<Record<string, string>>,
|
||||||
args: string[],
|
args: string[],
|
||||||
feature: Feature,
|
feature: Feature,
|
||||||
layer: LayerConfig
|
layer: LayerConfig
|
||||||
): BaseUIElement {
|
): SvelteUIElement {
|
||||||
const keyToUse = args[0]
|
const keyToUse = args[0]
|
||||||
const prefix = args[1]
|
const prefix = args[1]
|
||||||
const postfix = args[2]
|
const postfix = args[2]
|
||||||
|
@ -870,7 +872,7 @@ export default class SpecialVisualizations {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
funcName: "canonical",
|
funcName: "canonical",
|
||||||
needsUrls: [],
|
|
||||||
docs: "Converts a short, canonical value into the long, translated text including the unit. This only works if a `unit` is defined for the corresponding value. The unit specification will be included in the text. ",
|
docs: "Converts a short, canonical value into the long, translated text including the unit. This only works if a `unit` is defined for the corresponding value. The unit specification will be included in the text. ",
|
||||||
example:
|
example:
|
||||||
"If the object has `length=42`, then `{canonical(length)}` will be shown as **42 meter** (in english), **42 metre** (in french), ...",
|
"If the object has `length=42`, then `{canonical(length)}` will be shown as **42 meter** (in english), **42 metre** (in french), ...",
|
||||||
|
@ -910,7 +912,7 @@ export default class SpecialVisualizations {
|
||||||
funcName: "export_as_geojson",
|
funcName: "export_as_geojson",
|
||||||
docs: "Exports the selected feature as GeoJson-file",
|
docs: "Exports the selected feature as GeoJson-file",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
constr: (state, tagSource, tagsSource, feature, layer) => {
|
constr: (state, tagSource, tagsSource, feature, layer) => {
|
||||||
const t = Translations.t.general.download
|
const t = Translations.t.general.download
|
||||||
|
|
||||||
|
@ -942,7 +944,7 @@ export default class SpecialVisualizations {
|
||||||
funcName: "open_in_iD",
|
funcName: "open_in_iD",
|
||||||
docs: "Opens the current view in the iD-editor",
|
docs: "Opens the current view in the iD-editor",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
constr: (state, feature) => {
|
constr: (state, feature) => {
|
||||||
return new SvelteUIElement(OpenIdEditor, {
|
return new SvelteUIElement(OpenIdEditor, {
|
||||||
mapProperties: state.mapProperties,
|
mapProperties: state.mapProperties,
|
||||||
|
@ -964,7 +966,7 @@ export default class SpecialVisualizations {
|
||||||
funcName: "clear_location_history",
|
funcName: "clear_location_history",
|
||||||
docs: "A button to remove the travelled track information from the device",
|
docs: "A button to remove the travelled track information from the device",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
constr: (state) => {
|
constr: (state) => {
|
||||||
return new SubtleButton(
|
return new SubtleButton(
|
||||||
Svg.delete_icon_svg().SetStyle("height: 1.5rem"),
|
Svg.delete_icon_svg().SetStyle("height: 1.5rem"),
|
||||||
|
@ -1023,6 +1025,7 @@ export default class SpecialVisualizations {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
needsUrls: [Imgur.apiUrl],
|
needsUrls: [Imgur.apiUrl],
|
||||||
|
|
||||||
constr: (state, tags, args) => {
|
constr: (state, tags, args) => {
|
||||||
const id = tags.data[args[0] ?? "id"]
|
const id = tags.data[args[0] ?? "id"]
|
||||||
tags = state.featureProperties.getStore(id)
|
tags = state.featureProperties.getStore(id)
|
||||||
|
@ -1033,7 +1036,7 @@ export default class SpecialVisualizations {
|
||||||
{
|
{
|
||||||
funcName: "title",
|
funcName: "title",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
docs: "Shows the title of the popup. Useful for some cases, e.g. 'What is phone number of {title()}?'",
|
docs: "Shows the title of the popup. Useful for some cases, e.g. 'What is phone number of {title()}?'",
|
||||||
example:
|
example:
|
||||||
"`What is the phone number of {title()}`, which might automatically become `What is the phone number of XYZ`.",
|
"`What is the phone number of {title()}`, which might automatically become `What is the phone number of XYZ`.",
|
||||||
|
@ -1145,6 +1148,7 @@ export default class SpecialVisualizations {
|
||||||
defaultValue: "mr_taskId",
|
defaultValue: "mr_taskId",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
constr: (state, tagsSource, args) => {
|
constr: (state, tagsSource, args) => {
|
||||||
let [message, image, message_closed, statusToSet, maproulette_id_key] = args
|
let [message, image, message_closed, statusToSet, maproulette_id_key] = args
|
||||||
if (image === "") {
|
if (image === "") {
|
||||||
|
@ -1168,7 +1172,7 @@ export default class SpecialVisualizations {
|
||||||
funcName: "statistics",
|
funcName: "statistics",
|
||||||
docs: "Show general statistics about the elements currently in view. Intended to use on the `current_view`-layer",
|
docs: "Show general statistics about the elements currently in view. Intended to use on the `current_view`-layer",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
constr: (state) => {
|
constr: (state) => {
|
||||||
return new Combine(
|
return new Combine(
|
||||||
state.layout.layers
|
state.layout.layers
|
||||||
|
@ -1211,7 +1215,6 @@ export default class SpecialVisualizations {
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
needsUrls: [],
|
|
||||||
|
|
||||||
constr(__, tags, args) {
|
constr(__, tags, args) {
|
||||||
return new SvelteUIElement(SendEmail, { args, tags })
|
return new SvelteUIElement(SendEmail, { args, tags })
|
||||||
|
@ -1244,7 +1247,7 @@ export default class SpecialVisualizations {
|
||||||
doc: "If set, this text will be used as aria-label",
|
doc: "If set, this text will be used as aria-label",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
needsUrls: [],
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
tagSource: UIEventSource<Record<string, string>>,
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
@ -1273,7 +1276,7 @@ export default class SpecialVisualizations {
|
||||||
{
|
{
|
||||||
funcName: "multi",
|
funcName: "multi",
|
||||||
docs: "Given an embedded tagRendering (read only) and a key, will read the keyname as a JSON-list. Every element of this list will be considered as tags and rendered with the tagRendering",
|
docs: "Given an embedded tagRendering (read only) and a key, will read the keyname as a JSON-list. Every element of this list will be considered as tags and rendered with the tagRendering",
|
||||||
needsUrls: [],
|
|
||||||
example:
|
example:
|
||||||
"```json\n" +
|
"```json\n" +
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
|
@ -1327,7 +1330,7 @@ export default class SpecialVisualizations {
|
||||||
{
|
{
|
||||||
funcName: "translated",
|
funcName: "translated",
|
||||||
docs: "If the given key can be interpreted as a JSON, only show the key containing the current language (or 'en'). This specialRendering is meant to be used by MapComplete studio and is not useful in map themes",
|
docs: "If the given key can be interpreted as a JSON, only show the key containing the current language (or 'en'). This specialRendering is meant to be used by MapComplete studio and is not useful in map themes",
|
||||||
needsUrls: [],
|
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "key",
|
name: "key",
|
||||||
|
@ -1366,7 +1369,7 @@ export default class SpecialVisualizations {
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
needsUrls: [],
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
tagSource: UIEventSource<Record<string, string>>,
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
@ -1396,7 +1399,7 @@ export default class SpecialVisualizations {
|
||||||
{
|
{
|
||||||
funcName: "braced",
|
funcName: "braced",
|
||||||
docs: "Show a literal text within braces",
|
docs: "Show a literal text within braces",
|
||||||
needsUrls: [],
|
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "text",
|
name: "text",
|
||||||
|
@ -1417,7 +1420,7 @@ export default class SpecialVisualizations {
|
||||||
{
|
{
|
||||||
funcName: "tags",
|
funcName: "tags",
|
||||||
docs: "Shows a (json of) tags in a human-readable way + links to the wiki",
|
docs: "Shows a (json of) tags in a human-readable way + links to the wiki",
|
||||||
needsUrls: [],
|
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "key",
|
name: "key",
|
||||||
|
@ -1468,6 +1471,7 @@ export default class SpecialVisualizations {
|
||||||
],
|
],
|
||||||
docs: "Shows events that are happening based on a Giggity URL",
|
docs: "Shows events that are happening based on a Giggity URL",
|
||||||
needsUrls: (args) => args[0],
|
needsUrls: (args) => args[0],
|
||||||
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
tagSource: UIEventSource<Record<string, string>>,
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
@ -1481,7 +1485,7 @@ export default class SpecialVisualizations {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
funcName: "gps_all_tags",
|
funcName: "gps_all_tags",
|
||||||
needsUrls: [],
|
|
||||||
docs: "Shows the current tags of the GPS-representing object, used for debugging",
|
docs: "Shows the current tags of the GPS-representing object, used for debugging",
|
||||||
args: [],
|
args: [],
|
||||||
constr(
|
constr(
|
||||||
|
@ -1507,9 +1511,10 @@ export default class SpecialVisualizations {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
funcName: "favourite_status",
|
funcName: "favourite_status",
|
||||||
needsUrls: [],
|
|
||||||
docs: "A button that allows a (logged in) contributor to mark a location as a favourite location",
|
docs: "A button that allows a (logged in) contributor to mark a location as a favourite location",
|
||||||
args: [],
|
args: [],
|
||||||
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
tagSource: UIEventSource<Record<string, string>>,
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
@ -1527,7 +1532,7 @@ export default class SpecialVisualizations {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
funcName: "favourite_icon",
|
funcName: "favourite_icon",
|
||||||
needsUrls: [],
|
|
||||||
docs: "A small button that allows a (logged in) contributor to mark a location as a favourite location, sized to fit a title-icon",
|
docs: "A small button that allows a (logged in) contributor to mark a location as a favourite location, sized to fit a title-icon",
|
||||||
args: [],
|
args: [],
|
||||||
constr(
|
constr(
|
||||||
|
@ -1542,13 +1547,13 @@ export default class SpecialVisualizations {
|
||||||
state,
|
state,
|
||||||
layer,
|
layer,
|
||||||
feature,
|
feature,
|
||||||
})
|
}).SetClass("w-full h-full")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
funcName: "direction_indicator",
|
funcName: "direction_indicator",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
docs: "Gives a distance indicator and a compass pointing towards the location from your GPS-location. If clicked, centers the map on the object",
|
docs: "Gives a distance indicator and a compass pointing towards the location from your GPS-location. If clicked, centers the map on the object",
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
|
@ -1563,7 +1568,7 @@ export default class SpecialVisualizations {
|
||||||
{
|
{
|
||||||
funcName: "qr_code",
|
funcName: "qr_code",
|
||||||
args: [],
|
args: [],
|
||||||
needsUrls: [],
|
|
||||||
docs: "Generates a QR-code to share the selected object",
|
docs: "Generates a QR-code to share the selected object",
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
|
@ -1608,7 +1613,7 @@ export default class SpecialVisualizations {
|
||||||
defaultValue: "_direction:centerpoint",
|
defaultValue: "_direction:centerpoint",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
needsUrls: [],
|
|
||||||
constr(
|
constr(
|
||||||
state: SpecialVisualizationState,
|
state: SpecialVisualizationState,
|
||||||
tagSource: UIEventSource<Record<string, string>>,
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
|
|
@ -1448,7 +1448,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
d.setUTCMinutes(0)
|
d.setUTCMinutes(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static scrollIntoView(element: HTMLBaseElement) {
|
public static scrollIntoView(element: HTMLBaseElement | HTMLDivElement) {
|
||||||
// Is the element completely in the view?
|
// Is the element completely in the view?
|
||||||
const parentRect = Utils.findParentWithScrolling(element).getBoundingClientRect()
|
const parentRect = Utils.findParentWithScrolling(element).getBoundingClientRect()
|
||||||
const elementRect = element.getBoundingClientRect()
|
const elementRect = element.getBoundingClientRect()
|
||||||
|
@ -1680,7 +1680,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private static findParentWithScrolling(element: HTMLBaseElement): HTMLBaseElement {
|
private static findParentWithScrolling(
|
||||||
|
element: HTMLBaseElement | HTMLDivElement
|
||||||
|
): HTMLBaseElement | HTMLDivElement {
|
||||||
// Check if the element itself has scrolling
|
// Check if the element itself has scrolling
|
||||||
if (element.scrollHeight > element.clientHeight) {
|
if (element.scrollHeight > element.clientHeight) {
|
||||||
return element
|
return element
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue