forked from MapComplete/MapComplete
		
	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…
	
	Add table
		Add a link
		
	
		Reference in a new issue