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", | ||||
|       "condition": "amenity=restaurant", | ||||
|       "condition": "takeaway=only", | ||||
|       "question": { | ||||
|         "en": "Is a reservation required for this place?", | ||||
|         "nl": "Is reserveren verplicht voor deze zaak?", | ||||
|  | @ -581,7 +581,8 @@ | |||
|             "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": { | ||||
|  | @ -731,6 +732,13 @@ | |||
|             "pl": "Wszystkie dania są wegetariańskie", | ||||
|             "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", | ||||
|  | @ -798,6 +806,13 @@ | |||
|             "pl": "Wszystkie dania są wegańskie", | ||||
|             "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", | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ | |||
|         { | ||||
|           "#": "ignore-image-in-then", | ||||
|           "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", | ||||
|         "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": [ | ||||
|         { | ||||
|           "#": "ignore-image-in-then", | ||||
|           "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": { | ||||
|  | @ -174,12 +194,12 @@ | |||
|         { | ||||
|           "#": "ignore-image-in-then", | ||||
|           "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", | ||||
|           "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": [ | ||||
|         "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": [ | ||||
|         { | ||||
|           "if": "id~.*/-.*", | ||||
|  | @ -215,7 +245,17 @@ | |||
|         { | ||||
|           "#": "ignore-image-in-then", | ||||
|           "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]*" | ||||
|  |  | |||
|  | @ -4721,6 +4721,9 @@ | |||
|                     }, | ||||
|                     "3": { | ||||
|                         "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?" | ||||
|  | @ -4738,6 +4741,9 @@ | |||
|                     }, | ||||
|                     "3": { | ||||
|                         "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?" | ||||
|  | @ -5252,7 +5258,41 @@ | |||
|         } | ||||
|     }, | ||||
|     "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": { | ||||
|         "description": "Basic indoor mapping: shows room outlines", | ||||
|  |  | |||
|  | @ -4190,6 +4190,9 @@ | |||
|                     }, | ||||
|                     "3": { | ||||
|                         "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?" | ||||
|  | @ -4207,6 +4210,9 @@ | |||
|                     }, | ||||
|                     "3": { | ||||
|                         "then": "Enkel vegetarische opties zijn beschikbaar" | ||||
|                     }, | ||||
|                     "4": { | ||||
|                         "then": "Sommige gerechten kunnen op vraag vegetarisch gemaakt worden" | ||||
|                     } | ||||
|                 }, | ||||
|                 "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": { | ||||
|         "description": "Een basis voor indoor-navigatie: toont binnenruimtes", | ||||
|         "name": "Binnenruimtes", | ||||
|  |  | |||
|  | @ -886,16 +886,6 @@ video { | |||
|   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 { | ||||
|   margin-left: 3rem; | ||||
|   margin-right: 3rem; | ||||
|  | @ -969,10 +959,6 @@ video { | |||
|   margin-left: 1rem; | ||||
| } | ||||
| 
 | ||||
| .mb-10 { | ||||
|   margin-bottom: 2.5rem; | ||||
| } | ||||
| 
 | ||||
| .mt-8 { | ||||
|   margin-top: 2rem; | ||||
| } | ||||
|  | @ -1529,10 +1515,6 @@ video { | |||
|   justify-self: end; | ||||
| } | ||||
| 
 | ||||
| .justify-self-center { | ||||
|   justify-self: center; | ||||
| } | ||||
| 
 | ||||
| .overflow-auto { | ||||
|   overflow: auto; | ||||
| } | ||||
|  | @ -1935,6 +1917,11 @@ video { | |||
|   line-height: 2.5rem; | ||||
| } | ||||
| 
 | ||||
| .text-5xl { | ||||
|   font-size: 3rem; | ||||
|   line-height: 1; | ||||
| } | ||||
| 
 | ||||
| .font-extrabold { | ||||
|   font-weight: 800; | ||||
| } | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import ImageProvider, { ProvidedImage } from "./ImageProvider" | ||||
| import BaseUIElement from "../../UI/BaseUIElement" | ||||
| import Svg from "../../Svg" | ||||
| import { Utils } from "../../Utils" | ||||
| import { LicenseInfo } from "./LicenseInfo" | ||||
| 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 { | ||||
|     public static readonly singleton = new Mapillary() | ||||
|  | @ -112,11 +112,11 @@ export class Mapillary extends ImageProvider { | |||
|             lat: number | ||||
|         } | ||||
|     ): BaseUIElement { | ||||
|         const icon = Svg.mapillary_svg() | ||||
|         if (!id) { | ||||
|             return icon | ||||
|         let url: string = undefined | ||||
|         if (id) { | ||||
|             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>[]> { | ||||
|  |  | |||
							
								
								
									
										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 BaseUIElement from "../BaseUIElement" | ||||
| import { Store } from "../../Logic/UIEventSource" | ||||
| import { Utils } from "../../Utils" | ||||
| 
 | ||||
| export default class Link extends BaseUIElement { | ||||
|     private readonly _href: string | Store<string> | ||||
|  |  | |||
|  | @ -1,17 +1,21 @@ | |||
| <script lang="ts"> | ||||
|   import { createEventDispatcher } from "svelte" | ||||
|   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 | ||||
|    */ | ||||
|   const dispatch = createEventDispatcher() | ||||
|   export let cls = "" | ||||
|   export let arialabel: Translation = undefined  | ||||
| </script> | ||||
| 
 | ||||
| <button | ||||
|   on:click={(e) => dispatch("click", e)} | ||||
|   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)} | ||||
| > | ||||
|   <slot /> | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ | |||
|   import ToSvelte from "./ToSvelte.svelte" | ||||
|   import Svg from "../../Svg" | ||||
|   import Share from "../../assets/svg/Share.svelte" | ||||
|   import { ariaLabel } from "../../Utils/ariaLabel" | ||||
|   import Translations from "../i18n/Translations" | ||||
| 
 | ||||
|   export let generateShareData: () => { | ||||
|     text: string | ||||
|  | @ -24,7 +26,7 @@ | |||
|   } | ||||
| </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"> | ||||
|     <Share class="h-7 w-7 p-1" /> | ||||
|   </slot> | ||||
|  |  | |||
|  | @ -13,7 +13,8 @@ | |||
|   export let hideTooltip = false | ||||
| </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" /> | ||||
|   {#if !hideTooltip} | ||||
|     <Tr cls="mx-2" t={Translations.t.general.backgroundSwitch} /> | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
|   import Translations from "../i18n/Translations"; | ||||
|   import Tr from "../Base/Tr.svelte"; | ||||
|   import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import { ariaLabel } from "../../Utils/ariaLabel" | ||||
| 
 | ||||
|   export let state: SpecialVisualizationState; | ||||
|   export let layer: LayerConfig; | ||||
|  | @ -50,8 +51,10 @@ | |||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <button on:click={() => state.selectedElement.setData(undefined)} class="border-none p-0"> | ||||
|       <XCircleIcon class="h-8 w-8" /> | ||||
|     <button on:click={() => state.selectedElement.setData(undefined)}  | ||||
|             use:ariaLabel={Translations.t.general.backToMap} | ||||
|             class="border-none p-0"> | ||||
|       <XCircleIcon aria-hidden={true} class="h-8 w-8" /> | ||||
|     </button> | ||||
|   </div> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -92,7 +92,7 @@ | |||
|       <Tr t={title} /> | ||||
| 
 | ||||
|       {#if selected} | ||||
|         <span class="alert"> | ||||
|         <span class="alert" aria-hidden="true"> | ||||
|           <Tr t={Translations.t.general.morescreen.enterToOpen} /> | ||||
|         </span> | ||||
|       {/if} | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ | |||
|     <div class="flex flex-col"> | ||||
|       {#if $license.title} | ||||
|         {#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} | ||||
|           $license.title | ||||
|         {/if} | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ | |||
|   import DateInput from "./Helpers/DateInput.svelte" | ||||
|   import ColorInput from "./Helpers/ColorInput.svelte" | ||||
|   import OpeningHoursInput from "./Helpers/OpeningHoursInput.svelte" | ||||
|   import SlopeInput from "./Helpers/SlopeInput.svelte" | ||||
| 
 | ||||
|   export let type: ValidatorType | ||||
|   export let value: UIEventSource<string | object> | ||||
|  | @ -47,6 +48,8 @@ | |||
|   <SimpleTagInput {value} {args} on:submit /> | ||||
| {:else if type === "opening_hours"} | ||||
|   <OpeningHoursInput {value} /> | ||||
| {:else if type === "slope"} | ||||
|   <SlopeInput {value} /> | ||||
| {:else if type === "wikidata"} | ||||
|   <ToSvelte construct={() => InputHelpers.constructWikidataHelper(value, properties)} /> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -1,36 +1,39 @@ | |||
| <script lang="ts"> | ||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization"; | ||||
|   import { HeartIcon as HeartSolidIcon } from "@babeard/svelte-heroicons/solid"; | ||||
|   import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline"; | ||||
|   import Translations from "../i18n/Translations"; | ||||
|   import LoginToggle from "../Base/LoginToggle.svelte"; | ||||
|   import type { Feature } from "geojson"; | ||||
|   import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; | ||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization" | ||||
|   import { HeartIcon as HeartSolidIcon } from "@babeard/svelte-heroicons/solid" | ||||
|   import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline" | ||||
|   import Translations from "../i18n/Translations" | ||||
|   import LoginToggle from "../Base/LoginToggle.svelte" | ||||
|   import type { Feature } from "geojson" | ||||
|   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 | ||||
|    */ | ||||
|   export let state: SpecialVisualizationState; | ||||
|   export let feature: Feature; | ||||
|   export let tags: Record<string, string>; | ||||
|   export let layer: LayerConfig; | ||||
|   let isFavourite = tags?.map(tags => tags._favourite === "yes"); | ||||
|   const t = Translations.t.favouritePoi; | ||||
|   export let state: SpecialVisualizationState | ||||
|   export let feature: Feature | ||||
|   export let tags: UIEventSource<Record<string, string>> | ||||
|   export let layer: LayerConfig | ||||
|   let isFavourite = tags?.map(tags => tags._favourite === "yes") | ||||
|   const t = Translations.t.favouritePoi | ||||
| 
 | ||||
|   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> | ||||
| 
 | ||||
| <LoginToggle ignoreLoading={true} {state}> | ||||
|   {#if $isFavourite} | ||||
|     <button class="p-0 m-0 h-8 w-8" on:click={() => markFavourite(false)}> | ||||
|       <HeartSolidIcon/> | ||||
|     <button class="p-0 m-0 h-8 w-8" on:click={() => markFavourite(false)} | ||||
|             use:ariaLabel={Translations.t.favouritePoi.button.isMarkedShort}> | ||||
|       <HeartSolidIcon aria-hidden={true} /> | ||||
|     </button> | ||||
|   {:else} | ||||
|     <button class="p-0 m-0 h-8 w-8 no-image-background"  on:click={() => markFavourite(true)} > | ||||
|       <HeartOutlineIcon/> | ||||
|     <button class="p-0 m-0 h-8 w-8 no-image-background" on:click={() => markFavourite(true)} | ||||
|             use:ariaLabel={Translations.t.favouritePoi.button.isNotMarkedShort}> | ||||
|       <HeartOutlineIcon aria-hidden={true} /> | ||||
|     </button> | ||||
|   {/if} | ||||
| </LoginToggle> | ||||
|  |  | |||
|  | @ -45,7 +45,6 @@ import { GeoOperations } from "../Logic/GeoOperations" | |||
| import CreateNewNote from "./Popup/CreateNewNote.svelte" | ||||
| import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte" | ||||
| import UserProfile from "./BigComponents/UserProfile.svelte" | ||||
| import Link from "./Base/Link" | ||||
| import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||
| import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | ||||
| import { WayId } from "../Models/OsmFeature" | ||||
|  | @ -81,9 +80,9 @@ import MarkAsFavouriteMini from "./Popup/MarkAsFavouriteMini.svelte" | |||
| import NextChangeViz from "./OpeningHours/NextChangeViz.svelte" | ||||
| import NearbyImages from "./Image/NearbyImages.svelte" | ||||
| import NearbyImagesCollapsed from "./Image/NearbyImagesCollapsed.svelte" | ||||
| import { svelte } from "@sveltejs/vite-plugin-svelte" | ||||
| import MoveWizard from "./Popup/MoveWizard.svelte" | ||||
| import { Unit } from "../Models/Unit" | ||||
| import Link from "./Base/Link.svelte" | ||||
| 
 | ||||
| class NearbyImageVis implements SpecialVisualization { | ||||
|     // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
 | ||||
|  | @ -1288,6 +1287,10 @@ export default class SpecialVisualizations { | |||
|                         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", | ||||
|                     }, | ||||
|                     { | ||||
|                         name: "arialabel", | ||||
|                         doc: "If set, this text will be used as aria-label", | ||||
|                     }, | ||||
|                 ], | ||||
|                 needsUrls: [], | ||||
|                 constr( | ||||
|  | @ -1295,15 +1298,19 @@ export default class SpecialVisualizations { | |||
|                     tagSource: UIEventSource<Record<string, string>>, | ||||
|                     args: string[] | ||||
|                 ): BaseUIElement { | ||||
|                     const [text, href, classnames, download] = args | ||||
|                     const [text, href, classnames, download, ariaLabel] = args | ||||
|                     const newTab = download === undefined && !href.startsWith("#") | ||||
|                     return new VariableUiElement( | ||||
|                         tagSource.map((tags) => | ||||
|                             new Link( | ||||
|                                 Utils.SubstituteKeys(text, tags), | ||||
|                                 Utils.SubstituteKeys(href, tags), | ||||
|                                 download === undefined && !href.startsWith("#"), | ||||
|                                 Utils.SubstituteKeys(download, tags) | ||||
|                             ).SetClass(classnames) | ||||
|                         tagSource.map( | ||||
|                             (tags) => | ||||
|                                 new SvelteUIElement(Link, { | ||||
|                                     text: Utils.SubstituteKeys(text, tags), | ||||
|                                     href: Utils.SubstituteKeys(href, tags), | ||||
|                                     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( | ||||
|                                     FediverseValidator.usernameAtServer | ||||
|                                 ) | ||||
| 
 | ||||
|                                 return new Link( | ||||
|                                     fediAccount, | ||||
|                                     "https://" + host + "/@" + username, | ||||
|                                     true | ||||
|                                 ) | ||||
|                                 return new SvelteUIElement(Link, { | ||||
|                                     text: fediAccount, | ||||
|                                     url: "https://" + host + "/@" + username, | ||||
|                                     newTab: true, | ||||
|                                 }) | ||||
|                             }) | ||||
|                     ) | ||||
|                 }, | ||||
|  |  | |||
|  | @ -1,5 +1,11 @@ | |||
| <script lang="ts"> | ||||
|   // Testing grounds | ||||
|   import { UIEventSource } from "../Logic/UIEventSource" | ||||
|   import SlopeInput from "./InputElement/Helpers/SlopeInput.svelte" | ||||
|   let value: UIEventSource<string> = new UIEventSource(undefined) | ||||
| </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 Favourites from "./Favourites/Favourites.svelte" | ||||
|   import ImageOperations from "./Image/ImageOperations.svelte" | ||||
|   import { ariaLabel } from "../Utils/ariaLabel" | ||||
| 
 | ||||
|   export let state: ThemeViewState | ||||
|   let layout = state.layout | ||||
| 
 | ||||
|   let maplibremap: UIEventSource<MlMap> = state.map | ||||
|   let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined) | ||||
| 
 | ||||
|  | @ -142,7 +142,8 @@ | |||
|     </div> | ||||
|   </If> | ||||
|   <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"> | ||||
|         <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"> | ||||
|  | @ -150,15 +151,19 @@ | |||
|         </b> | ||||
|       </div> | ||||
|     </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" /> | ||||
|     </MapControlButton> | ||||
|     {#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()} | ||||
|       <MapControlButton | ||||
|         on:click={() => { | ||||
|           selectedElement.setData(state.currentView.features?.data?.[0]) | ||||
|         }} on:keydown={forwardEventToMap} | ||||
|          | ||||
|         }}  | ||||
|         on:keydown={forwardEventToMap} | ||||
|       > | ||||
|         <ToSvelte | ||||
|           construct={() => currentViewLayer.defaultIcon().SetClass("w-8 h-8 cursor-pointer")} | ||||
|  | @ -206,7 +211,9 @@ | |||
|       <div class="flex"> | ||||
|         <!-- bottom left elements --> | ||||
|         <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" /> | ||||
|           </MapControlButton> | ||||
|         </If> | ||||
|  | @ -246,14 +253,21 @@ | |||
|           /> | ||||
|         </div> | ||||
|       </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" /> | ||||
|       </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" /> | ||||
|       </MapControlButton> | ||||
|       <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 | ||||
|             construct={geolocationControl.SetClass("block w-8 h-8")} | ||||
|           /> | ||||
|  |  | |||
|  | @ -1,6 +1,9 @@ | |||
| import { Translation } from "../UI/i18n/Translation" | ||||
| 
 | ||||
| export function ariaLabel(htmlElement: Element, t: Translation) { | ||||
|     if (!t) { | ||||
|         return | ||||
|     } | ||||
|     let destroy: () => void = undefined | ||||
| 
 | ||||
|     t.current.map( | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue