A11y: add option to disable or always enable a11y features

This commit is contained in:
Pieter Vander Vennet 2024-01-01 03:29:57 +01:00
parent 3059d2ed26
commit 8dd1a0e107
9 changed files with 95 additions and 66 deletions

View file

@ -113,6 +113,33 @@
"*": "{logout()}" "*": "{logout()}"
} }
}, },
{
"id": "a11y-features",
"question": {
"en": "What accessibility features should be applied?"
},
"mappings": [
{
"if": "mapcomplete-a11y=default",
"alsoShowIf": "mapcomplete-a11y=",
"then": {
"en": "Enable accessibility features when arrow keys are used to navigate the map"
}
},
{
"if": "mapcomplete-a11y=always",
"then": {
"en": "Always enable accessibility features"
}
},
{
"if": "mapcomplete-a11y=never",
"then": {
"en": "Never enable accessibility features"
}
}
]
},
{ {
"id": "background-layer-readonly", "id": "background-layer-readonly",
"condition": { "condition": {

View file

@ -1,16 +1,13 @@
{ {
"id": "mapcomplete-changes", "id": "mapcomplete-changes",
"title": { "title": {
"en": "Changes made with MapComplete", "en": "Changes made with MapComplete"
"de": "Mit MapComplete vorgenommene Änderungen"
}, },
"shortDescription": { "shortDescription": {
"en": "Shows changes made by MapComplete", "en": "Shows changes made by MapComplete"
"de": "Zeigt die von MapComplete vorgenommenen Änderungen an"
}, },
"description": { "description": {
"en": "This maps shows all the changes made with MapComplete", "en": "This maps shows all the changes made with MapComplete"
"de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen"
}, },
"icon": "./assets/svg/logo.svg", "icon": "./assets/svg/logo.svg",
"hideFromOverview": true, "hideFromOverview": true,
@ -23,8 +20,7 @@
{ {
"id": "mapcomplete-changes", "id": "mapcomplete-changes",
"name": { "name": {
"en": "Changeset centers", "en": "Changeset centers"
"de": "Zentrum der Änderungssätze"
}, },
"minzoom": 0, "minzoom": 0,
"source": { "source": {
@ -35,48 +31,41 @@
}, },
"title": { "title": {
"render": { "render": {
"en": "Changeset for {theme}", "en": "Changeset for {theme}"
"de": "Änderungssatz für {theme}"
} }
}, },
"description": { "description": {
"en": "Shows all MapComplete changes", "en": "Shows all MapComplete changes"
"de": "Zeigt alle MapComplete-Änderungen"
}, },
"tagRenderings": [ "tagRenderings": [
{ {
"id": "show_changeset_id", "id": "show_changeset_id",
"render": { "render": {
"en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>", "en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
"de": "Änderungssatz <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
} }
}, },
{ {
"id": "contributor", "id": "contributor",
"question": { "question": {
"en": "What contributor did make this change?", "en": "What contributor did make this change?"
"de": "Wer hat diese Änderung vorgenommen?"
}, },
"freeform": { "freeform": {
"key": "user" "key": "user"
}, },
"render": { "render": {
"en": "Change made by <a href='https://openstreetmap.org/user/{user}' target='_blank'>{user}</a>", "en": "Change made by <a href='https://openstreetmap.org/user/{user}' target='_blank'>{user}</a>"
"de": "Änderung von <a href='https://openstreetmap.org/user/{user}' target='_blank'>{user}</a>"
} }
}, },
{ {
"id": "theme-id", "id": "theme-id",
"question": { "question": {
"en": "What theme was used to make this change?", "en": "What theme was used to make this change?"
"de": "Welches Theme wurde für diese Änderung verwendet?"
}, },
"freeform": { "freeform": {
"key": "theme" "key": "theme"
}, },
"render": { "render": {
"en": "Change with theme <a href='https://mapcomplete.org/{theme}'>{theme}</a>", "en": "Change with theme <a href='https://mapcomplete.org/{theme}'>{theme}</a>"
"de": "Geändert mit Thema <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>"
} }
}, },
{ {
@ -85,23 +74,19 @@
"key": "locale" "key": "locale"
}, },
"question": { "question": {
"en": "What locale (language) was this change made in?", "en": "What locale (language) was this change made in?"
"de": "In welcher Benutzersprache wurde diese Änderung vorgenommen?"
}, },
"render": { "render": {
"en": "User locale is {locale}", "en": "User locale is {locale}"
"de": "Benutzersprache {locale}"
} }
}, },
{ {
"id": "host", "id": "host",
"render": { "render": {
"en": "Change with with <a href='{host}'>{host}</a>", "en": "Change with with <a href='{host}'>{host}</a>"
"de": "Geändert über <a href='{host}'>{host}</a>"
}, },
"question": { "question": {
"en": "What host (website) was this change made with?", "en": "What host (website) was this change made with?"
"de": "Über welchen Host (Webseite) wurde diese Änderung vorgenommen?"
}, },
"freeform": { "freeform": {
"key": "host" "key": "host"
@ -122,12 +107,10 @@
{ {
"id": "version", "id": "version",
"question": { "question": {
"en": "What version of MapComplete was used to make this change?", "en": "What version of MapComplete was used to make this change?"
"de": "Welche Version von MapComplete wurde verwendet, um diese Änderung vorzunehmen?"
}, },
"render": { "render": {
"en": "Made with {editor}", "en": "Made with {editor}"
"de": "Erstellt mit {editor}"
}, },
"freeform": { "freeform": {
"key": "editor" "key": "editor"
@ -477,8 +460,7 @@
} }
], ],
"question": { "question": {
"en": "Themename contains {search}", "en": "Themename contains {search}"
"de": "Themename enthält {search}"
} }
} }
] ]
@ -494,8 +476,7 @@
} }
], ],
"question": { "question": {
"en": "Themename does <b>not</b> contain {search}", "en": "Themename does <b>not</b> contain {search}"
"de": "Der Name enthält <b>nicht</b> {search}"
} }
} }
] ]
@ -511,8 +492,7 @@
} }
], ],
"question": { "question": {
"en": "Made by contributor {search}", "en": "Made by contributor {search}"
"de": "Der Name enthält <b>nicht</b> {search}"
} }
} }
] ]
@ -528,8 +508,7 @@
} }
], ],
"question": { "question": {
"en": "<b>Not</b> made by contributor {search}", "en": "<b>Not</b> made by contributor {search}"
"de": "<b>Nicht</b> erstellt von {search}"
} }
} }
] ]
@ -546,8 +525,7 @@
} }
], ],
"question": { "question": {
"en": "Made before {search}", "en": "Made before {search}"
"de": "Erstellt vor {search}"
} }
} }
] ]
@ -564,8 +542,7 @@
} }
], ],
"question": { "question": {
"en": "Made after {search}", "en": "Made after {search}"
"de": "Erstellt nach {search}"
} }
} }
] ]
@ -581,8 +558,7 @@
} }
], ],
"question": { "question": {
"en": "User language (iso-code) {search}", "en": "User language (iso-code) {search}"
"de": "Benutzersprache (ISO-Code) {search}"
} }
} }
] ]
@ -598,8 +574,7 @@
} }
], ],
"question": { "question": {
"en": "Made with host {search}", "en": "Made with host {search}"
"de": "Erstellt mit Host {search}"
} }
} }
] ]
@ -610,8 +585,7 @@
{ {
"osmTags": "add-image>0", "osmTags": "add-image>0",
"question": { "question": {
"en": "Changeset added at least one image", "en": "Changeset added at least one image"
"de": "Änderungssatz hat mindestens ein Bild hinzugefügt"
} }
} }
] ]
@ -622,8 +596,7 @@
{ {
"osmTags": "theme!=grb", "osmTags": "theme!=grb",
"question": { "question": {
"en": "Exclude GRB theme", "en": "Exclude GRB theme"
"de": "GRB-Theme ausschließen"
} }
} }
] ]
@ -634,8 +607,7 @@
{ {
"osmTags": "theme!=etymology", "osmTags": "theme!=etymology",
"question": { "question": {
"en": "Exclude etymology theme", "en": "Exclude etymology theme"
"de": "Etymologie-Thema ausschließen"
} }
} }
] ]
@ -650,8 +622,7 @@
{ {
"id": "link_to_more", "id": "link_to_more",
"render": { "render": {
"en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>", "en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>"
"de": "Mehr Statistiken gibt es <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>hier</a>"
} }
}, },
{ {

View file

@ -376,7 +376,7 @@
"islocked": "Bewegen vergrendeld rond je huidige locatie. Duw op de geolocatie-knop om te ontgrendelen.", "islocked": "Bewegen vergrendeld rond je huidige locatie. Duw op de geolocatie-knop om te ontgrendelen.",
"locked": "Bewegen vergrendeld rond jouw huidige locatie.", "locked": "Bewegen vergrendeld rond jouw huidige locatie.",
"navigation": "Gebruik de pijltjestoetsen om te bewegen. Druk op spatie om het meest centrale punt te selecteren. Druk op een cijfertoets om andere items te selecteren.", "navigation": "Gebruik de pijltjestoetsen om te bewegen. Druk op spatie om het meest centrale punt te selecteren. Druk op een cijfertoets om andere items te selecteren.",
"noCloseFeatures": "Niet in beeld", "noCloseFeatures": "Geen objecten in beeld",
"north": "Naar het noorden", "north": "Naar het noorden",
"oneFeatureInView": "Eén object in beeld.", "oneFeatureInView": "Eén object in beeld.",
"out": "Aan het uitzoomen naar zoomlevel {z}", "out": "Aan het uitzoomen naar zoomlevel {z}",

View file

@ -41,6 +41,7 @@ export default class UserRelatedState {
public readonly showTags: UIEventSource<"no" | undefined | "always" | "yes" | "full"> public readonly showTags: UIEventSource<"no" | undefined | "always" | "yes" | "full">
public readonly showCrosshair: UIEventSource<"yes" | "always" | "no" | undefined> public readonly showCrosshair: UIEventSource<"yes" | "always" | "no" | undefined>
public readonly fixateNorth: UIEventSource<undefined | "yes"> public readonly fixateNorth: UIEventSource<undefined | "yes">
public readonly a11y: UIEventSource<undefined | "always" | "never" | "default">
public readonly homeLocation: FeatureSource public readonly homeLocation: FeatureSource
/** /**
* The language as saved into the preferences of the user, if logged in. * The language as saved into the preferences of the user, if logged in.
@ -109,6 +110,10 @@ export default class UserRelatedState {
this.showTags = <UIEventSource<any>>this.osmConnection.GetPreference("show_tags") this.showTags = <UIEventSource<any>>this.osmConnection.GetPreference("show_tags")
this.showCrosshair = <UIEventSource<any>>this.osmConnection.GetPreference("show_crosshair") this.showCrosshair = <UIEventSource<any>>this.osmConnection.GetPreference("show_crosshair")
this.fixateNorth = <UIEventSource<"yes">>this.osmConnection.GetPreference("fixate-north") this.fixateNorth = <UIEventSource<"yes">>this.osmConnection.GetPreference("fixate-north")
this.a11y = <UIEventSource<"always" | "never" | "default">>(
this.osmConnection.GetPreference("a11y")
)
this.mangroveIdentity = new MangroveIdentity( this.mangroveIdentity = new MangroveIdentity(
this.osmConnection.GetLongPreference("identity", "mangrove") this.osmConnection.GetLongPreference("identity", "mangrove")
) )

View file

@ -452,7 +452,17 @@ export default class ThemeViewState implements SpecialVisualizationState {
* Various small methods that need to be called * Various small methods that need to be called
*/ */
private miscSetup() { private miscSetup() {
this.userRelatedState.a11y.addCallbackAndRunD((a11y) => {
if (a11y === "always") {
this.visualFeedback.setData(true)
} else if (a11y === "never") {
this.visualFeedback.setData(false)
}
})
this.mapProperties.onKeyNavigationEvent((keyEvent) => { this.mapProperties.onKeyNavigationEvent((keyEvent) => {
if (this.userRelatedState.a11y.data === "never") {
return
}
if (["north", "east", "south", "west"].indexOf(keyEvent.key) >= 0) { if (["north", "east", "south", "west"].indexOf(keyEvent.key) >= 0) {
this.visualFeedback.setData(true) this.visualFeedback.setData(true)
return true // Our job is done, unregister return true // Our job is done, unregister
@ -482,7 +492,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
* @private * @private
*/ */
private selectClosestAtCenter(i: number = 0) { private selectClosestAtCenter(i: number = 0) {
if (this.userRelatedState.a11y.data !== "never") {
this.visualFeedback.setData(true) this.visualFeedback.setData(true)
}
const toSelect = this.closestFeatures.features?.data?.[i] const toSelect = this.closestFeatures.features?.data?.[i]
if (!toSelect) { if (!toSelect) {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {

View file

@ -10,6 +10,7 @@
import { createEventDispatcher, onDestroy } from "svelte" import { createEventDispatcher, onDestroy } from "svelte"
import { placeholder } from "../../Utils/placeholder" import { placeholder } from "../../Utils/placeholder"
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid" import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
import { ariaLabel } from "../../Utils/ariaLabel"
export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined
export let bounds: UIEventSource<BBox> export let bounds: UIEventSource<BBox>
@ -116,6 +117,7 @@
}} }}
bind:value={searchContents} bind:value={searchContents}
use:placeholder={Translations.t.general.search.search} use:placeholder={Translations.t.general.search.search}
use:ariaLabel={Translations.t.general.search.search}
/> />
{#if feedback !== undefined} {#if feedback !== undefined}
<!-- The feedback is _always_ shown for screenreaders and to make sure that the searchfield can still be selected by tabbing--> <!-- The feedback is _always_ shown for screenreaders and to make sure that the searchfield can still be selected by tabbing-->

View file

@ -511,15 +511,25 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
await Utils.waitFor(250) await Utils.waitFor(250)
} }
} }
public installCustomKeyboardHandler(viewport: Store<HTMLDivElement>) { public installCustomKeyboardHandler(viewportStore: UIEventSource<HTMLDivElement>) {
viewport.mapD( viewportStore.mapD(
(viewport) => { (viewport) => {
const map = this._maplibreMap.data const map = this._maplibreMap.data
if (!map) { if (!map) {
return return
} }
const oldKeyboard = map.keyboard const oldKeyboard = map.keyboard
oldKeyboard._panStep = viewport.getBoundingClientRect().width const w = viewport.getBoundingClientRect().width
if (w < 10) {
/// this is weird, but definitively wrong!
console.log("Got a very small bound", w, viewport)
// We try again later on
window.requestAnimationFrame(() => {
viewportStore.ping()
})
return
}
oldKeyboard._panStep = w
}, },
[this._maplibreMap] [this._maplibreMap]
) )

View file

@ -29,9 +29,9 @@
</script> </script>
{#if config !== undefined && (config?.condition === undefined || config.condition.matchesProperties($tags))} {#if config !== undefined && (config?.condition === undefined || config.condition.matchesProperties($tags))}
<div {id} class={twMerge("link-underline inline-block w-full", config?.classes ?? "flex items-center", extraClasses)}> <div {id} class={twMerge("link-underline inline-block w-full", config?.classes , extraClasses)}>
{#if $trs.length === 1} {#if $trs.length === 1}
<TagRenderingMapping clss={extraClasses} mapping={$trs[0]} {tags} {state} {selectedElement} {layer} /> <TagRenderingMapping mapping={$trs[0]} {tags} {state} {selectedElement} {layer} />
{/if} {/if}
{#if $trs.length > 1} {#if $trs.length > 1}
<ul> <ul>

View file

@ -79,7 +79,7 @@
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)
selectedElement.addCallbackAndRun(se => console.log("Selected element", se))
let compass = Orientation.singleton.alpha let compass = Orientation.singleton.alpha
let compassLoaded = Orientation.singleton.gotMeasurement let compassLoaded = Orientation.singleton.gotMeasurement
Orientation.singleton.startMeasurements() Orientation.singleton.startMeasurements()
@ -189,9 +189,11 @@
<div class="pointer-events-auto float-right mt-1 flex flex-col px-1 max-[480px]:w-full sm:m-2"> <div class="pointer-events-auto float-right mt-1 flex flex-col px-1 max-[480px]:w-full sm:m-2">
<If condition={state.visualFeedback}> <If condition={state.visualFeedback}>
{#if $selectedElement === undefined}
<div class="w-fit"> <div class="w-fit">
<VisualFeedbackPanel {state} /> <VisualFeedbackPanel {state} />
</div> </div>
{/if}
</If> </If>
<If condition={state.featureSwitches.featureSwitchSearch}> <If condition={state.featureSwitches.featureSwitchSearch}>
<Geosearch <Geosearch