A11y: add labels to previously unlabeled buttons, fix order
This commit is contained in:
parent
736ab13ceb
commit
fdde0aaeb3
21 changed files with 287 additions and 86 deletions
|
@ -524,7 +524,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "Reservation",
|
"id": "Reservation",
|
||||||
"condition": "amenity=restaurant",
|
"condition": "takeaway=only",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Is a reservation required for this place?",
|
"en": "Is a reservation required for this place?",
|
||||||
"nl": "Is reserveren verplicht voor deze zaak?",
|
"nl": "Is reserveren verplicht voor deze zaak?",
|
||||||
|
@ -581,7 +581,8 @@
|
||||||
"cs": "Rezervace na tomto místě není možná"
|
"cs": "Rezervace na tomto místě není možná"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"#condition": "If one can only do takeaway or deliveries, it is nonsensical to ask if a 'reservation' is possible"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -731,6 +732,13 @@
|
||||||
"pl": "Wszystkie dania są wegetariańskie",
|
"pl": "Wszystkie dania są wegetariańskie",
|
||||||
"cs": "Všechna jídla jsou vegetariánská"
|
"cs": "Všechna jídla jsou vegetariánská"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": "diet:vegetarian=on_demand",
|
||||||
|
"then": {
|
||||||
|
"en": "Some dishes might be adapted to a vegetarian version, but this should be demanded",
|
||||||
|
"nl": "Sommige gerechten kunnen op vraag vegetarisch gemaakt worden"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"condition": "cuisine!=friture",
|
"condition": "cuisine!=friture",
|
||||||
|
@ -798,6 +806,13 @@
|
||||||
"pl": "Wszystkie dania są wegańskie",
|
"pl": "Wszystkie dania są wegańskie",
|
||||||
"cs": "Všechna jídla jsou veganská"
|
"cs": "Všechna jídla jsou veganská"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": "diet:vegan=on_demand",
|
||||||
|
"then": {
|
||||||
|
"en": "Some dishes might be adapted to a vegan version if asked for",
|
||||||
|
"nl": "Op vraag kan een veganistische variant van een gerecht gemaakt worden"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"condition": "cuisine!=friture",
|
"condition": "cuisine!=friture",
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
{
|
{
|
||||||
"#": "ignore-image-in-then",
|
"#": "ignore-image-in-then",
|
||||||
"if": "wikipedia=",
|
"if": "wikipedia=",
|
||||||
"then": "<a class='h-8' href='https://www.wikidata.org/wiki/{wikidata}' target='_blank' rel='noopener'><img src='./assets/svg/wikidata.svg' alt='WD'/></a>"
|
"then": "<a class='h-8' href='https://www.wikidata.org/wiki/{wikidata}' target='_blank' rel='noopener'><img src='./assets/svg/wikidata.svg' alt='Wikidata'/></a>"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -119,12 +119,32 @@
|
||||||
"defaults",
|
"defaults",
|
||||||
"in_favourite"
|
"in_favourite"
|
||||||
],
|
],
|
||||||
"render": "<a href='tel:{phone}'><img textmode='📞' alt='phone' src='./assets/layers/questions/phone.svg'/></a>",
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"type": "link",
|
||||||
|
"href": "tel:{phone}",
|
||||||
|
"text": "<img textmode='📞' alt='phone' src='./assets/layers/questions/phone.svg'/>",
|
||||||
|
"arialabel": {
|
||||||
|
"en": "phone",
|
||||||
|
"nl": "Telefoneer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"mappings": [
|
"mappings": [
|
||||||
{
|
{
|
||||||
"#": "ignore-image-in-then",
|
"#": "ignore-image-in-then",
|
||||||
"if": "contact:phone~*",
|
"if": "contact:phone~*",
|
||||||
"then": "<a href='tel:{contact:phone}'><img textmode='📞' alt='phone' src='./assets/layers/questions/phone.svg'/></a>"
|
"then": {
|
||||||
|
"special": {
|
||||||
|
"type": "link",
|
||||||
|
"href": "tel:{contact:phone}",
|
||||||
|
"text": "<img textmode='📞' alt='phone' src='./assets/layers/questions/phone.svg'/>",
|
||||||
|
"arialabel": {
|
||||||
|
"en": "phone",
|
||||||
|
"nl": "Telefoneer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"condition": {
|
"condition": {
|
||||||
|
@ -174,12 +194,12 @@
|
||||||
{
|
{
|
||||||
"#": "ignore-image-in-then",
|
"#": "ignore-image-in-then",
|
||||||
"if": "smoking=no",
|
"if": "smoking=no",
|
||||||
"then": "<img textmode='🚭️' alt='no-smoking' src='./assets/layers/questions/no_smoking.svg'/>"
|
"then": "<img textmode='🚭️' alt='no smoking' src='./assets/layers/questions/no_smoking.svg'/>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"#": "ignore-image-in-then",
|
"#": "ignore-image-in-then",
|
||||||
"if": "smoking=yes",
|
"if": "smoking=yes",
|
||||||
"then": "<img textmode='🚬️' alt='smoking-allowed' src='./assets/layers/questions/smoking.svg'/>"
|
"then": "<img textmode='🚬️' alt='smoking allowed' src='./assets/layers/questions/smoking.svg'/>"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -206,7 +226,17 @@
|
||||||
"labels": [
|
"labels": [
|
||||||
"defaults"
|
"defaults"
|
||||||
],
|
],
|
||||||
"render": "<a href='https://openstreetmap.org/{id}' target='_blank' rel='noopener'><img alt='on osm' textmode='🗺️' src='./assets/svg/osm-logo-us.svg'/></a>",
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"type": "link",
|
||||||
|
"text": "<img alt='on osm' textmode='🗺️' src='./assets/svg/osm-logo-us.svg'/>",
|
||||||
|
"href": "https://openstreetmap.org/{id}",
|
||||||
|
"arialabel": {
|
||||||
|
"en": "Open on openstreetmap.org",
|
||||||
|
"nl": "Bekijk op openstreetmap.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"mappings": [
|
"mappings": [
|
||||||
{
|
{
|
||||||
"if": "id~.*/-.*",
|
"if": "id~.*/-.*",
|
||||||
|
@ -215,7 +245,17 @@
|
||||||
{
|
{
|
||||||
"#": "ignore-image-in-then",
|
"#": "ignore-image-in-then",
|
||||||
"if": "_backend~*",
|
"if": "_backend~*",
|
||||||
"then": "<a href='{_backend}/{id}' target='_blank' rel='noopener'><img src='./assets/svg/osm-logo-us.svg'/></a>"
|
"then": {
|
||||||
|
"special": {
|
||||||
|
"type": "link",
|
||||||
|
"text": "<img alt='on osm' textmode='🗺️' src='./assets/svg/osm-logo-us.svg'/>",
|
||||||
|
"href": "{_backend}/{id}",
|
||||||
|
"arialabel": {
|
||||||
|
"en": "Open on openstreetmap.org",
|
||||||
|
"nl": "Bekijk op openstreetmap.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"condition": "id~(node|way|relation)/[0-9]*"
|
"condition": "id~(node|way|relation)/[0-9]*"
|
||||||
|
|
|
@ -4721,6 +4721,9 @@
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"then": "All dishes are vegan"
|
"then": "All dishes are vegan"
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"then": "Some dishes might be adapted to a vegan version if asked for"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"question": "Does this business serve vegan meals?"
|
"question": "Does this business serve vegan meals?"
|
||||||
|
@ -4738,6 +4741,9 @@
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"then": "All dishes are vegetarian"
|
"then": "All dishes are vegetarian"
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"then": "Some dishes might be adapted to a vegetarian version, but this should be demanded"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"question": "Does this restaurant have a vegetarian option?"
|
"question": "Does this restaurant have a vegetarian option?"
|
||||||
|
@ -5252,7 +5258,41 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"icons": {
|
"icons": {
|
||||||
"description": "A layer acting as library for icon-tagrenderings, especially to show as badge next to a POI"
|
"description": "A layer acting as library for icon-tagrenderings, especially to show as badge next to a POI",
|
||||||
|
"tagRenderings": {
|
||||||
|
"osmlink": {
|
||||||
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": {
|
||||||
|
"special": {
|
||||||
|
"arialabel": "Open on openstreetmap.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"arialabel": "Open on openstreetmap.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"phonelink": {
|
||||||
|
"mappings": {
|
||||||
|
"0": {
|
||||||
|
"then": {
|
||||||
|
"special": {
|
||||||
|
"arialabel": "phone"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"arialabel": "phone"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"indoors": {
|
"indoors": {
|
||||||
"description": "Basic indoor mapping: shows room outlines",
|
"description": "Basic indoor mapping: shows room outlines",
|
||||||
|
|
|
@ -4190,6 +4190,9 @@
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"then": "Enkel veganistische opties zijn beschikbaar"
|
"then": "Enkel veganistische opties zijn beschikbaar"
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"then": "Op vraag kan een veganistische variant van een gerecht gemaakt worden"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"question": "Heeft deze eetgelegenheid een veganistische optie?"
|
"question": "Heeft deze eetgelegenheid een veganistische optie?"
|
||||||
|
@ -4207,6 +4210,9 @@
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"then": "Enkel vegetarische opties zijn beschikbaar"
|
"then": "Enkel vegetarische opties zijn beschikbaar"
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"then": "Sommige gerechten kunnen op vraag vegetarisch gemaakt worden"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"question": "Heeft deze eetgelegenheid een vegetarische optie?"
|
"question": "Heeft deze eetgelegenheid een vegetarische optie?"
|
||||||
|
@ -4654,6 +4660,42 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"icons": {
|
||||||
|
"tagRenderings": {
|
||||||
|
"osmlink": {
|
||||||
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": {
|
||||||
|
"special": {
|
||||||
|
"arialabel": "Bekijk op openstreetmap.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"arialabel": "Bekijk op openstreetmap.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"phonelink": {
|
||||||
|
"mappings": {
|
||||||
|
"0": {
|
||||||
|
"then": {
|
||||||
|
"special": {
|
||||||
|
"arialabel": "Telefoneer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"arialabel": "Telefoneer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"indoors": {
|
"indoors": {
|
||||||
"description": "Een basis voor indoor-navigatie: toont binnenruimtes",
|
"description": "Een basis voor indoor-navigatie: toont binnenruimtes",
|
||||||
"name": "Binnenruimtes",
|
"name": "Binnenruimtes",
|
||||||
|
|
|
@ -886,16 +886,6 @@ video {
|
||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx-10 {
|
|
||||||
margin-left: 2.5rem;
|
|
||||||
margin-right: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.my-3 {
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx-12 {
|
.mx-12 {
|
||||||
margin-left: 3rem;
|
margin-left: 3rem;
|
||||||
margin-right: 3rem;
|
margin-right: 3rem;
|
||||||
|
@ -969,10 +959,6 @@ video {
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mb-10 {
|
|
||||||
margin-bottom: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt-8 {
|
.mt-8 {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
@ -1529,10 +1515,6 @@ video {
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.justify-self-center {
|
|
||||||
justify-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overflow-auto {
|
.overflow-auto {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
@ -1935,6 +1917,11 @@ video {
|
||||||
line-height: 2.5rem;
|
line-height: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-5xl {
|
||||||
|
font-size: 3rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.font-extrabold {
|
.font-extrabold {
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
||||||
import BaseUIElement from "../../UI/BaseUIElement"
|
import BaseUIElement from "../../UI/BaseUIElement"
|
||||||
import Svg from "../../Svg"
|
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import { LicenseInfo } from "./LicenseInfo"
|
import { LicenseInfo } from "./LicenseInfo"
|
||||||
import Constants from "../../Models/Constants"
|
import Constants from "../../Models/Constants"
|
||||||
import Link from "../../UI/Base/Link"
|
import SvelteUIElement from "../../UI/Base/SvelteUIElement"
|
||||||
|
import MapillaryIcon from "./MapillaryIcon.svelte"
|
||||||
|
|
||||||
export class Mapillary extends ImageProvider {
|
export class Mapillary extends ImageProvider {
|
||||||
public static readonly singleton = new Mapillary()
|
public static readonly singleton = new Mapillary()
|
||||||
|
@ -112,11 +112,11 @@ export class Mapillary extends ImageProvider {
|
||||||
lat: number
|
lat: number
|
||||||
}
|
}
|
||||||
): BaseUIElement {
|
): BaseUIElement {
|
||||||
const icon = Svg.mapillary_svg()
|
let url: string = undefined
|
||||||
if (!id) {
|
if (id) {
|
||||||
return icon
|
url = Mapillary.createLink(location, 16, "" + id)
|
||||||
}
|
}
|
||||||
return new Link(icon, Mapillary.createLink(location, 16, "" + id), true)
|
return new SvelteUIElement(MapillaryIcon, { url })
|
||||||
}
|
}
|
||||||
|
|
||||||
async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
||||||
|
|
21
src/Logic/ImageProviders/MapillaryIcon.svelte
Normal file
21
src/Logic/ImageProviders/MapillaryIcon.svelte
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Mapillary from "../../assets/svg/Mapillary.svelte"
|
||||||
|
import { ariaLabel } from "../../Utils/ariaLabel"
|
||||||
|
import Translations from "../../UI/i18n/Translations"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessible, linked mapillary icon
|
||||||
|
*/
|
||||||
|
export let url: string = undefined
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if url}
|
||||||
|
<a href={url}
|
||||||
|
use:ariaLabel={Translations.t.general.attribution.seeOnMapillary}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener nofollower" >
|
||||||
|
<Mapillary />
|
||||||
|
</a>
|
||||||
|
{:else}
|
||||||
|
<Mapillary />
|
||||||
|
{/if}
|
12
src/UI/Base/Link.svelte
Normal file
12
src/UI/Base/Link.svelte
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let text : string
|
||||||
|
export let href : string
|
||||||
|
export let classnames : string = undefined
|
||||||
|
export let download : string = undefined
|
||||||
|
export let ariaLabel : string = undefined
|
||||||
|
|
||||||
|
export let newTab: boolean = false
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a {href} aria-label={ariaLabel} target={newTab ? "_blank" : undefined} {download} class={classnames}>
|
||||||
|
{@html text}</a>
|
|
@ -1,7 +1,6 @@
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
import BaseUIElement from "../BaseUIElement"
|
import BaseUIElement from "../BaseUIElement"
|
||||||
import { Store } from "../../Logic/UIEventSource"
|
import { Store } from "../../Logic/UIEventSource"
|
||||||
import { Utils } from "../../Utils"
|
|
||||||
|
|
||||||
export default class Link extends BaseUIElement {
|
export default class Link extends BaseUIElement {
|
||||||
private readonly _href: string | Store<string>
|
private readonly _href: string | Store<string>
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { twJoin } from "tailwind-merge"
|
import { twJoin } from "tailwind-merge"
|
||||||
|
import { Translation } from "../i18n/Translation"
|
||||||
|
import { ariaLabel } from "../../Utils/ariaLabel"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A round button with an icon and possible a small text, which hovers above the map
|
* A round button with an icon and possible a small text, which hovers above the map
|
||||||
*/
|
*/
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
export let cls = ""
|
export let cls = ""
|
||||||
|
export let arialabel: Translation = undefined
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
on:click={(e) => dispatch("click", e)}
|
on:click={(e) => dispatch("click", e)}
|
||||||
on:keydown
|
on:keydown
|
||||||
|
use:ariaLabel={arialabel}
|
||||||
class={twJoin("pointer-events-auto m-0.5 h-fit w-fit rounded-full p-0.5 sm:p-1 md:m-1", cls)}
|
class={twJoin("pointer-events-auto m-0.5 h-fit w-fit rounded-full p-0.5 sm:p-1 md:m-1", cls)}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
import ToSvelte from "./ToSvelte.svelte"
|
import ToSvelte from "./ToSvelte.svelte"
|
||||||
import Svg from "../../Svg"
|
import Svg from "../../Svg"
|
||||||
import Share from "../../assets/svg/Share.svelte"
|
import Share from "../../assets/svg/Share.svelte"
|
||||||
|
import { ariaLabel } from "../../Utils/ariaLabel"
|
||||||
|
import Translations from "../i18n/Translations"
|
||||||
|
|
||||||
export let generateShareData: () => {
|
export let generateShareData: () => {
|
||||||
text: string
|
text: string
|
||||||
|
@ -24,7 +26,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button on:click={share} class="secondary m-0 h-8 w-8 p-0">
|
<button on:click={share} class="secondary m-0 h-8 w-8 p-0" use:ariaLabel={Translations.t.general.share}>
|
||||||
<slot name="content">
|
<slot name="content">
|
||||||
<Share class="h-7 w-7 p-1" />
|
<Share class="h-7 w-7 p-1" />
|
||||||
</slot>
|
</slot>
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
export let hideTooltip = false
|
export let hideTooltip = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<MapControlButton on:click={() => state.guistate.backgroundLayerSelectionIsOpened.setData(true)}>
|
<MapControlButton arialabel={Translations.t.general.labels.background}
|
||||||
|
on:click={() => state.guistate.backgroundLayerSelectionIsOpened.setData(true)}>
|
||||||
<Square3Stack3dIcon class="h-6 w-6" />
|
<Square3Stack3dIcon class="h-6 w-6" />
|
||||||
{#if !hideTooltip}
|
{#if !hideTooltip}
|
||||||
<Tr cls="mx-2" t={Translations.t.general.backgroundSwitch} />
|
<Tr cls="mx-2" t={Translations.t.general.backgroundSwitch} />
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
import Translations from "../i18n/Translations";
|
import Translations from "../i18n/Translations";
|
||||||
import Tr from "../Base/Tr.svelte";
|
import Tr from "../Base/Tr.svelte";
|
||||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||||
|
import { ariaLabel } from "../../Utils/ariaLabel"
|
||||||
|
|
||||||
export let state: SpecialVisualizationState;
|
export let state: SpecialVisualizationState;
|
||||||
export let layer: LayerConfig;
|
export let layer: LayerConfig;
|
||||||
|
@ -50,8 +51,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button on:click={() => state.selectedElement.setData(undefined)} class="border-none p-0">
|
<button on:click={() => state.selectedElement.setData(undefined)}
|
||||||
<XCircleIcon class="h-8 w-8" />
|
use:ariaLabel={Translations.t.general.backToMap}
|
||||||
|
class="border-none p-0">
|
||||||
|
<XCircleIcon aria-hidden={true} class="h-8 w-8" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
<Tr t={title} />
|
<Tr t={title} />
|
||||||
|
|
||||||
{#if selected}
|
{#if selected}
|
||||||
<span class="alert">
|
<span class="alert" aria-hidden="true">
|
||||||
<Tr t={Translations.t.general.morescreen.enterToOpen} />
|
<Tr t={Translations.t.general.morescreen.enterToOpen} />
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
{#if $license.title}
|
{#if $license.title}
|
||||||
{#if $license.informationLocation}
|
{#if $license.informationLocation}
|
||||||
<a href={$license.informationLocation} target="_blank" rel="noopener nofollower">{$license.title}</a>
|
<a href={$license.informationLocation.href} target="_blank" rel="noopener nofollower">{$license.title}</a>
|
||||||
{:else}
|
{:else}
|
||||||
$license.title
|
$license.title
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
import DateInput from "./Helpers/DateInput.svelte"
|
import DateInput from "./Helpers/DateInput.svelte"
|
||||||
import ColorInput from "./Helpers/ColorInput.svelte"
|
import ColorInput from "./Helpers/ColorInput.svelte"
|
||||||
import OpeningHoursInput from "./Helpers/OpeningHoursInput.svelte"
|
import OpeningHoursInput from "./Helpers/OpeningHoursInput.svelte"
|
||||||
|
import SlopeInput from "./Helpers/SlopeInput.svelte"
|
||||||
|
|
||||||
export let type: ValidatorType
|
export let type: ValidatorType
|
||||||
export let value: UIEventSource<string | object>
|
export let value: UIEventSource<string | object>
|
||||||
|
@ -47,6 +48,8 @@
|
||||||
<SimpleTagInput {value} {args} on:submit />
|
<SimpleTagInput {value} {args} on:submit />
|
||||||
{:else if type === "opening_hours"}
|
{:else if type === "opening_hours"}
|
||||||
<OpeningHoursInput {value} />
|
<OpeningHoursInput {value} />
|
||||||
|
{:else if type === "slope"}
|
||||||
|
<SlopeInput {value} />
|
||||||
{:else if type === "wikidata"}
|
{:else if type === "wikidata"}
|
||||||
<ToSvelte construct={() => InputHelpers.constructWikidataHelper(value, properties)} />
|
<ToSvelte construct={() => InputHelpers.constructWikidataHelper(value, properties)} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,36 +1,39 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
import { HeartIcon as HeartSolidIcon } from "@babeard/svelte-heroicons/solid";
|
import { HeartIcon as HeartSolidIcon } from "@babeard/svelte-heroicons/solid"
|
||||||
import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline";
|
import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline"
|
||||||
import Translations from "../i18n/Translations";
|
import Translations from "../i18n/Translations"
|
||||||
import LoginToggle from "../Base/LoginToggle.svelte";
|
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||||
import type { Feature } from "geojson";
|
import type { Feature } from "geojson"
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
|
import { ariaLabel } from "../../Utils/ariaLabel"
|
||||||
|
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A small 'mark as favourite'-button to serve as title-icon
|
* A small 'mark as favourite'-button to serve as title-icon
|
||||||
*/
|
*/
|
||||||
export let state: SpecialVisualizationState;
|
export let state: SpecialVisualizationState
|
||||||
export let feature: Feature;
|
export let feature: Feature
|
||||||
export let tags: Record<string, string>;
|
export let tags: UIEventSource<Record<string, string>>
|
||||||
export let layer: LayerConfig;
|
export let layer: LayerConfig
|
||||||
let isFavourite = tags?.map(tags => tags._favourite === "yes");
|
let isFavourite = tags?.map(tags => tags._favourite === "yes")
|
||||||
const t = Translations.t.favouritePoi;
|
const t = Translations.t.favouritePoi
|
||||||
|
|
||||||
function markFavourite(isFavourite: boolean) {
|
function markFavourite(isFavourite: boolean) {
|
||||||
state.favourites.markAsFavourite(feature, layer.id, state.layout.id, tags, isFavourite);
|
state.favourites.markAsFavourite(feature, layer.id, state.layout.id, tags, isFavourite)
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LoginToggle ignoreLoading={true} {state}>
|
<LoginToggle ignoreLoading={true} {state}>
|
||||||
{#if $isFavourite}
|
{#if $isFavourite}
|
||||||
<button class="p-0 m-0 h-8 w-8" on:click={() => markFavourite(false)}>
|
<button class="p-0 m-0 h-8 w-8" on:click={() => markFavourite(false)}
|
||||||
<HeartSolidIcon/>
|
use:ariaLabel={Translations.t.favouritePoi.button.isMarkedShort}>
|
||||||
|
<HeartSolidIcon aria-hidden={true} />
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<button class="p-0 m-0 h-8 w-8 no-image-background" on:click={() => markFavourite(true)} >
|
<button class="p-0 m-0 h-8 w-8 no-image-background" on:click={() => markFavourite(true)}
|
||||||
<HeartOutlineIcon/>
|
use:ariaLabel={Translations.t.favouritePoi.button.isNotMarkedShort}>
|
||||||
|
<HeartOutlineIcon aria-hidden={true} />
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</LoginToggle>
|
</LoginToggle>
|
||||||
|
|
|
@ -45,7 +45,6 @@ import { GeoOperations } from "../Logic/GeoOperations"
|
||||||
import CreateNewNote from "./Popup/CreateNewNote.svelte"
|
import CreateNewNote from "./Popup/CreateNewNote.svelte"
|
||||||
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
|
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
|
||||||
import UserProfile from "./BigComponents/UserProfile.svelte"
|
import UserProfile from "./BigComponents/UserProfile.svelte"
|
||||||
import Link from "./Base/Link"
|
|
||||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
|
||||||
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"
|
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"
|
||||||
import { WayId } from "../Models/OsmFeature"
|
import { WayId } from "../Models/OsmFeature"
|
||||||
|
@ -81,9 +80,9 @@ import MarkAsFavouriteMini from "./Popup/MarkAsFavouriteMini.svelte"
|
||||||
import NextChangeViz from "./OpeningHours/NextChangeViz.svelte"
|
import NextChangeViz from "./OpeningHours/NextChangeViz.svelte"
|
||||||
import NearbyImages from "./Image/NearbyImages.svelte"
|
import NearbyImages from "./Image/NearbyImages.svelte"
|
||||||
import NearbyImagesCollapsed from "./Image/NearbyImagesCollapsed.svelte"
|
import NearbyImagesCollapsed from "./Image/NearbyImagesCollapsed.svelte"
|
||||||
import { svelte } from "@sveltejs/vite-plugin-svelte"
|
|
||||||
import MoveWizard from "./Popup/MoveWizard.svelte"
|
import MoveWizard from "./Popup/MoveWizard.svelte"
|
||||||
import { Unit } from "../Models/Unit"
|
import { Unit } from "../Models/Unit"
|
||||||
|
import Link from "./Base/Link.svelte"
|
||||||
|
|
||||||
class NearbyImageVis implements SpecialVisualization {
|
class NearbyImageVis implements SpecialVisualization {
|
||||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
||||||
|
@ -1288,6 +1287,10 @@ export default class SpecialVisualizations {
|
||||||
name: "download",
|
name: "download",
|
||||||
doc: "If set, this link will act as a download-button. The contents of `href` will be offered for download; this parameter will act as the proposed filename",
|
doc: "If set, this link will act as a download-button. The contents of `href` will be offered for download; this parameter will act as the proposed filename",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "arialabel",
|
||||||
|
doc: "If set, this text will be used as aria-label",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
needsUrls: [],
|
needsUrls: [],
|
||||||
constr(
|
constr(
|
||||||
|
@ -1295,15 +1298,19 @@ export default class SpecialVisualizations {
|
||||||
tagSource: UIEventSource<Record<string, string>>,
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
args: string[]
|
args: string[]
|
||||||
): BaseUIElement {
|
): BaseUIElement {
|
||||||
const [text, href, classnames, download] = args
|
const [text, href, classnames, download, ariaLabel] = args
|
||||||
|
const newTab = download === undefined && !href.startsWith("#")
|
||||||
return new VariableUiElement(
|
return new VariableUiElement(
|
||||||
tagSource.map((tags) =>
|
tagSource.map(
|
||||||
new Link(
|
(tags) =>
|
||||||
Utils.SubstituteKeys(text, tags),
|
new SvelteUIElement(Link, {
|
||||||
Utils.SubstituteKeys(href, tags),
|
text: Utils.SubstituteKeys(text, tags),
|
||||||
download === undefined && !href.startsWith("#"),
|
href: Utils.SubstituteKeys(href, tags),
|
||||||
Utils.SubstituteKeys(download, tags)
|
classnames,
|
||||||
).SetClass(classnames)
|
download: Utils.SubstituteKeys(download, tags),
|
||||||
|
ariaLabel: Utils.SubstituteKeys(ariaLabel, tags),
|
||||||
|
newTab,
|
||||||
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1422,12 +1429,11 @@ export default class SpecialVisualizations {
|
||||||
const [_, username, host] = fediAccount.match(
|
const [_, username, host] = fediAccount.match(
|
||||||
FediverseValidator.usernameAtServer
|
FediverseValidator.usernameAtServer
|
||||||
)
|
)
|
||||||
|
return new SvelteUIElement(Link, {
|
||||||
return new Link(
|
text: fediAccount,
|
||||||
fediAccount,
|
url: "https://" + host + "/@" + username,
|
||||||
"https://" + host + "/@" + username,
|
newTab: true,
|
||||||
true
|
})
|
||||||
)
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// Testing grounds
|
// Testing grounds
|
||||||
|
import { UIEventSource } from "../Logic/UIEventSource"
|
||||||
|
import SlopeInput from "./InputElement/Helpers/SlopeInput.svelte"
|
||||||
|
let value: UIEventSource<string> = new UIEventSource(undefined)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full">No tests</div>
|
<div class="w-full flex flex-col">
|
||||||
|
<div>Value: {$value}</div>
|
||||||
|
</div>
|
||||||
|
<SlopeInput {value}></SlopeInput>
|
||||||
|
|
|
@ -64,10 +64,10 @@
|
||||||
import Share from "../assets/svg/Share.svelte"
|
import Share from "../assets/svg/Share.svelte"
|
||||||
import Favourites from "./Favourites/Favourites.svelte"
|
import Favourites from "./Favourites/Favourites.svelte"
|
||||||
import ImageOperations from "./Image/ImageOperations.svelte"
|
import ImageOperations from "./Image/ImageOperations.svelte"
|
||||||
|
import { ariaLabel } from "../Utils/ariaLabel"
|
||||||
|
|
||||||
export let state: ThemeViewState
|
export let state: ThemeViewState
|
||||||
let layout = state.layout
|
let layout = state.layout
|
||||||
|
|
||||||
let maplibremap: UIEventSource<MlMap> = state.map
|
let maplibremap: UIEventSource<MlMap> = state.map
|
||||||
let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined)
|
let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined)
|
||||||
|
|
||||||
|
@ -142,7 +142,8 @@
|
||||||
</div>
|
</div>
|
||||||
</If>
|
</If>
|
||||||
<div class="float-left m-1 flex flex-col sm:mt-2">
|
<div class="float-left m-1 flex flex-col sm:mt-2">
|
||||||
<MapControlButton on:click={() => state.guistate.themeIsOpened.setData(true)} on:keydown={forwardEventToMap}>
|
<MapControlButton on:click={() => state.guistate.themeIsOpened.setData(true)}
|
||||||
|
on:keydown={forwardEventToMap}>
|
||||||
<div class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2">
|
<div class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2">
|
||||||
<img class="mr-0.5 block h-6 w-6 sm:mr-1 md:mr-2 md:h-8 md:w-8" src={layout.icon} />
|
<img class="mr-0.5 block h-6 w-6 sm:mr-1 md:mr-2 md:h-8 md:w-8" src={layout.icon} />
|
||||||
<b class="mr-1">
|
<b class="mr-1">
|
||||||
|
@ -150,15 +151,19 @@
|
||||||
</b>
|
</b>
|
||||||
</div>
|
</div>
|
||||||
</MapControlButton>
|
</MapControlButton>
|
||||||
<MapControlButton on:click={() => state.guistate.menuIsOpened.setData(true)} on:keydown={forwardEventToMap}>
|
<MapControlButton
|
||||||
|
on:click={() => state.guistate.menuIsOpened.setData(true)}
|
||||||
|
on:keydown={forwardEventToMap}
|
||||||
|
arialabel={Translations.t.general.labels.menu}
|
||||||
|
>
|
||||||
<MenuIcon class="h-8 w-8 cursor-pointer" />
|
<MenuIcon class="h-8 w-8 cursor-pointer" />
|
||||||
</MapControlButton>
|
</MapControlButton>
|
||||||
{#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()}
|
{#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()}
|
||||||
<MapControlButton
|
<MapControlButton
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedElement.setData(state.currentView.features?.data?.[0])
|
selectedElement.setData(state.currentView.features?.data?.[0])
|
||||||
}} on:keydown={forwardEventToMap}
|
}}
|
||||||
|
on:keydown={forwardEventToMap}
|
||||||
>
|
>
|
||||||
<ToSvelte
|
<ToSvelte
|
||||||
construct={() => currentViewLayer.defaultIcon().SetClass("w-8 h-8 cursor-pointer")}
|
construct={() => currentViewLayer.defaultIcon().SetClass("w-8 h-8 cursor-pointer")}
|
||||||
|
@ -206,7 +211,9 @@
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<!-- bottom left elements -->
|
<!-- bottom left elements -->
|
||||||
<If condition={state.featureSwitches.featureSwitchFilter}>
|
<If condition={state.featureSwitches.featureSwitchFilter}>
|
||||||
<MapControlButton on:click={() => state.guistate.openFilterView()} on:keydown={forwardEventToMap}>
|
<MapControlButton on:click={() => state.guistate.openFilterView()} on:keydown={forwardEventToMap}
|
||||||
|
arialabel={Translations.t.general.labels.filter}
|
||||||
|
>
|
||||||
<Filter class="h-6 w-6" />
|
<Filter class="h-6 w-6" />
|
||||||
</MapControlButton>
|
</MapControlButton>
|
||||||
</If>
|
</If>
|
||||||
|
@ -246,14 +253,21 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</If>
|
</If>
|
||||||
<MapControlButton on:click={() => mapproperties.zoom.update((z) => z + 1)} on:keydown={forwardEventToMap}>
|
<MapControlButton on:click={() => mapproperties.zoom.update((z) => z + 1)}
|
||||||
|
on:keydown={forwardEventToMap}
|
||||||
|
arialabel={Translations.t.general.labels.zoomIn}
|
||||||
|
>
|
||||||
<Plus class="h-8 w-8" />
|
<Plus class="h-8 w-8" />
|
||||||
</MapControlButton>
|
</MapControlButton>
|
||||||
<MapControlButton on:click={() => mapproperties.zoom.update((z) => z - 1)} on:keydown={forwardEventToMap}>
|
<MapControlButton on:click={() => mapproperties.zoom.update((z) => z - 1)} on:keydown={forwardEventToMap}
|
||||||
|
arialabel={Translations.t.general.labels.zoomOut}
|
||||||
|
>
|
||||||
<Min class="h-8 w-8" />
|
<Min class="h-8 w-8" />
|
||||||
</MapControlButton>
|
</MapControlButton>
|
||||||
<If condition={featureSwitches.featureSwitchGeolocation}>
|
<If condition={featureSwitches.featureSwitchGeolocation}>
|
||||||
<MapControlButton on:keydown={forwardEventToMap} on:click={() => geolocationControl.handleClick()}>
|
<MapControlButton on:keydown={forwardEventToMap} on:click={() => geolocationControl.handleClick()}
|
||||||
|
arialabel={Translations.t.general.labels.jumpToLocation}
|
||||||
|
>
|
||||||
<ToSvelte
|
<ToSvelte
|
||||||
construct={geolocationControl.SetClass("block w-8 h-8")}
|
construct={geolocationControl.SetClass("block w-8 h-8")}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { Translation } from "../UI/i18n/Translation"
|
import { Translation } from "../UI/i18n/Translation"
|
||||||
|
|
||||||
export function ariaLabel(htmlElement: Element, t: Translation) {
|
export function ariaLabel(htmlElement: Element, t: Translation) {
|
||||||
|
if (!t) {
|
||||||
|
return
|
||||||
|
}
|
||||||
let destroy: () => void = undefined
|
let destroy: () => void = undefined
|
||||||
|
|
||||||
t.current.map(
|
t.current.map(
|
||||||
|
|
Loading…
Reference in a new issue