Move various tabs into buttons, more work on a11y

This commit is contained in:
Pieter Vander Vennet 2023-12-20 02:50:08 +01:00
parent cce9b879f2
commit 7e852dd7e3
29 changed files with 10642 additions and 10432 deletions

File diff suppressed because it is too large Load diff

View file

@ -2227,7 +2227,7 @@
}
}
]
},
},
{
"id": "internet-ssid",
"labels": [

View file

@ -463,6 +463,7 @@
"key": "Key combination",
"openFilterPanel": "Opens the POI-layers and filter panel",
"openLayersPanel": "Opens the background layers panel",
"queryCurrentLocation": "Display the address which is nearest the map center",
"selectAerial": "Set the background to aerial or satellite imagery. Toggles between the two best, available layers",
"selectFavourites": "Open the favourites page",
"selectItem": "Select the POI which is closest to the map center (crosshair). Only when in keyboard navigation is used",

File diff suppressed because it is too large Load diff

View file

@ -7154,6 +7154,7 @@
"question": "Are dogs allowed in this business?"
},
"email": {
"editButtonAriaLabel": "Edit email address",
"question": "What is the email address of {title()}?"
},
"gluten_free": {
@ -7352,6 +7353,7 @@
}
},
"phone": {
"editButtonAriaLabel": "Edit phone number",
"question": "What is the phone number of {title()}?"
},
"repeated": {
@ -7454,6 +7456,7 @@
"question": "Does this place offer a vegan option?"
},
"website": {
"editButtonAriaLabel": "Edit website",
"question": "What is the website of {title()}?"
},
"wheelchair-access": {

View file

@ -6338,6 +6338,7 @@
"question": "Zijn honden toegelaten in deze zaak?"
},
"email": {
"editButtonAriaLabel": "Pas emailadres aan",
"question": "Wat is het e-mailadres van {title()}?"
},
"induction-loop": {
@ -6482,6 +6483,7 @@
}
},
"phone": {
"editButtonAriaLabel": "Pas telefoonnummer aan",
"question": "Wat is het telefoonnummer van {title()}?"
},
"repeated": {
@ -6540,6 +6542,7 @@
"question": "Is roken toegestaan bij {title()}?"
},
"website": {
"editButtonAriaLabel": "Pas website aan",
"question": "Wat is de website van {title()}?"
},
"wheelchair-access": {

View file

@ -1,6 +1,6 @@
{
"name": "mapcomplete",
"version": "0.36.6",
"version": "0.36.7",
"repository": "https://github.com/pietervdvn/MapComplete",
"description": "A small website to edit OSM easily",
"bugs": "https://github.com/pietervdvn/MapComplete/issues",
@ -32,7 +32,7 @@
"https://overpass.openstreetmap.ru/cgi/interpreter"
],
"country_coder_host": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/latlon2country",
"nominatimEndpoint": "https://nominatim.openstreetmap.org/"
"nominatimEndpoint": "https://geocoding.geofabrik.de/b75350b1cfc34962ac49824fe5b582dc/"
},
"scripts": {
"start": "npm run generate:layeroverview && npm run strt",
@ -75,7 +75,7 @@
"lint:prettier": "prettier --check '**/*.ts' '**/*.svelte'",
"format": "prettier --write '**/*.ts' '**/*.svelte'",
"clean:tests": "find . -type f -name \"*.doctest.ts\" | xargs -r rm",
"clean": "rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|test\\|studio\\|theme\\|style_test\\|statistics\\|leaderboard\\).html\" | xargs -r rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs -r rm)",
"clean": "rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|privacy\\|test\\|studio\\|theme\\|style_test\\|statistics\\|leaderboard\\).html\" | xargs -r rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs -r rm)",
"generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot",
"weblate-add-upstream": "git remote add weblate-github git@github.com:weblate/MapComplete.git && git remote add weblate-hosted-core https://hosted.weblate.org/git/mapcomplete/core/ && git remote add weblate-hosted-layers https://hosted.weblate.org/git/mapcomplete/layers/",
"weblate-merge": "git remote update weblate-github; git merge weblate-github/weblate-mapcomplete-core weblate-github/weblate-mapcomplete-layers weblate-github/weblate-mapcomplete-layer-translations",

View file

@ -1071,14 +1071,14 @@ video {
height: 6rem;
}
.h-screen {
height: 100vh;
}
.h-full {
height: 100%;
}
.h-screen {
height: 100vh;
}
.h-32 {
height: 8rem;
}
@ -1684,6 +1684,11 @@ video {
border-color: rgb(0 0 0 / var(--tw-border-opacity));
}
.border-gray-500 {
--tw-border-opacity: 1;
border-color: rgb(107 114 128 / var(--tw-border-opacity));
}
.border-subtle {
--tw-border-opacity: 1;
border-color: rgb(219 234 254 / var(--tw-border-opacity));
@ -1709,11 +1714,6 @@ video {
border-color: rgb(156 163 175 / var(--tw-border-opacity));
}
.border-gray-500 {
--tw-border-opacity: 1;
border-color: rgb(107 114 128 / var(--tw-border-opacity));
}
.border-gray-800 {
--tw-border-opacity: 1;
border-color: rgb(31 41 55 / var(--tw-border-opacity));
@ -1770,14 +1770,14 @@ video {
padding: 2rem;
}
.p-2 {
padding: 0.5rem;
}
.p-4 {
padding: 1rem;
}
.p-2 {
padding: 0.5rem;
}
.p-1 {
padding: 0.25rem;
}
@ -1814,6 +1814,10 @@ video {
padding-right: 0.75rem;
}
.pr-2 {
padding-right: 0.5rem;
}
.pr-12 {
padding-right: 3rem;
}
@ -1822,8 +1826,8 @@ video {
padding-left: 0.25rem;
}
.pr-2 {
padding-right: 0.5rem;
.pr-1 {
padding-right: 0.25rem;
}
.pl-2 {
@ -1854,10 +1858,6 @@ video {
padding-left: 1rem;
}
.pr-1 {
padding-right: 0.25rem;
}
.pl-3 {
padding-left: 0.75rem;
}
@ -2509,6 +2509,11 @@ label.checked:not(.neutral-label) {
width: 100%;
}
button.soft, .button.soft {
border: 2px solid var(--interactive-background);
margin: 0;
}
.links-as-button a {
/*
* Let a 'link' mimick a button, but not entirely

View file

@ -17,7 +17,7 @@ export class BBox {
* Coordinates should be [[lon, lat],[lon, lat]]
* @param coordinates
*/
constructor(coordinates) {
constructor(coordinates: [number,number][]) {
this.maxLat = -90
this.maxLon = -180
this.minLat = 90

View file

@ -29,13 +29,14 @@ export class Geocoding {
static async reverse(
coordinate: { lon: number; lat: number },
zoom: number = 18
zoom: number = 17,
language?: string
): Promise<FeatureCollection> {
// https://nominatim.org/release-docs/develop/api/Reverse/
// IF the zoom is low, it'll only return a country instead of an address
const url = `${Geocoding.host}reverse?format=geojson&lat=${coordinate.lat}&lon=${
coordinate.lon
}&zoom=${Math.round(zoom)}`
}&zoom=${Math.ceil(zoom) + 1}&accept-language=${language}`
return Utils.downloadJson(url)
}
}

View file

@ -1,42 +1,14 @@
import { Utils } from "../../Utils"
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
export class ThemeMetaTagging {
public static readonly themeName = "usersettings"
public static readonly themeName = "usersettings"
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
feat.properties._description
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
?.at(1)
)
Utils.AddLazyProperty(
feat.properties,
"_d",
() => feat.properties._description?.replace(/&lt;/g, "<")?.replace(/&gt;/g, ">") ?? ""
)
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
((feat) => {
const e = document.createElement("div")
e.innerHTML = feat.properties._d
return Array.from(e.getElementsByTagName("a")).filter(
(a) => a.href.match(/mastodon|en.osm.town/) !== null
)[0]?.href
})(feat)
)
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
((feat) => {
const e = document.createElement("div")
e.innerHTML = feat.properties._d
return Array.from(e.getElementsByTagName("a")).filter(
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
)[0]?.href
})(feat)
)
Utils.AddLazyProperty(
feat.properties,
"_mastodon_candidate",
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
)
feat.properties["__current_backgroun"] = "initial_value"
}
}
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/&lt;/g,'<')?.replace(/&gt;/g,'>') ?? '' )
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
feat.properties['__current_backgroun'] = 'initial_value'
}
}

View file

@ -34,7 +34,8 @@ export class MenuState {
new UIEventSource<boolean>(false)
public readonly filtersPanelIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(false)
public readonly privacyPanelIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(false)
public readonly communityIndexPanelIsOpened: UIEventSource<boolean> = new UIEventSource(false)
public readonly allToggles: {
toggle: UIEventSource<boolean>
name: string
@ -151,13 +152,21 @@ export class MenuState {
*/
public closeAll(): boolean {
const toggles = [
this.menuIsOpened,
this.themeIsOpened,
this.communityIndexPanelIsOpened,
this.privacyPanelIsOpened,
this.backgroundLayerSelectionIsOpened,
this.filtersPanelIsOpened,
this.menuIsOpened,
this.themeIsOpened,
]
const somethingIsOpen = toggles.some((t) => t.data)
toggles.forEach((t) => t.setData(false))
let somethingIsOpen = false
for (const t of toggles) {
somethingIsOpen = t.data
t.setData(false)
if (somethingIsOpen) {
break
}
}
return somethingIsOpen
}
}

View file

@ -487,35 +487,28 @@ export default class ThemeViewState implements SpecialVisualizationState {
}
private initHotkeys() {
Hotkeys.RegisterHotkey(
{ nomod: "Escape", onUp: true },
Translations.t.hotkeyDocumentation.closeSidebar,
() => {
if (this.previewedImage.data !== undefined) {
this.previewedImage.setData(undefined)
return
}
this.selectedElement.setData(undefined)
this.guistate.closeAll()
this.focusOnMap()
const docs = Translations.t.hotkeyDocumentation
Hotkeys.RegisterHotkey({ nomod: "Escape", onUp: true }, docs.closeSidebar, () => {
if (this.previewedImage.data !== undefined) {
this.previewedImage.setData(undefined)
return
}
)
this.selectedElement.setData(undefined)
this.guistate.closeAll()
this.focusOnMap()
})
Hotkeys.RegisterHotkey(
{ nomod: "f" },
Translations.t.hotkeyDocumentation.selectFavourites,
() => {
this.guistate.menuViewTab.setData("favourites")
this.guistate.menuIsOpened.setData(true)
}
)
Hotkeys.RegisterHotkey({ nomod: "f" }, docs.selectFavourites, () => {
this.guistate.menuViewTab.setData("favourites")
this.guistate.menuIsOpened.setData(true)
})
Hotkeys.RegisterHotkey(
{
nomod: " ",
onUp: true,
},
Translations.t.hotkeyDocumentation.selectItem,
docs.selectItem,
() => {
if (this.selectedElement.data !== undefined) {
return false
@ -537,7 +530,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
nomod: "" + i,
onUp: true,
},
Translations.t.hotkeyDocumentation.selectItem,
docs.selectItem,
() => this.selectClosestAtCenter(i - 1)
)
}
@ -550,7 +543,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
{
nomod: "b",
},
Translations.t.hotkeyDocumentation.openLayersPanel,
docs.openLayersPanel,
() => {
if (this.featureSwitches.featureSwitchBackgroundSelection.data) {
this.guistate.backgroundLayerSelectionIsOpened.setData(true)
@ -563,6 +556,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
},
Translations.t.hotkeyDocumentation.openFilterPanel,
() => {
console.log("S pressed")
if (this.featureSwitches.featureSwitchFilter.data) {
this.guistate.openFilterView()
}
@ -646,7 +640,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.closestFeatures.registerSource(specialLayers.favourite, "favourite")
if (this.layout?.lockLocation) {
const bbox = new BBox(this.layout.lockLocation)
const bbox = new BBox(<any>this.layout.lockLocation)
this.mapProperties.maxbounds.setData(bbox)
ShowDataLayer.showRange(
this.map,

View file

@ -19,6 +19,7 @@
import { LayoutInformation } from "../Models/ThemeConfig/LayoutConfig"
import * as themeOverview from "../assets/generated/theme_overview.json"
import UnofficialThemeList from "./BigComponents/UnofficialThemeList.svelte"
import Eye from "@babeard/svelte-heroicons/mini/Eye"
const featureSwitches = new OsmConnectionFeatureSwitches()
const osmConnection = new OsmConnection({
@ -136,6 +137,14 @@
<Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
</a>
<a
class="button h-fit w-full"
href={window.location.protocol + "//" + window.location.host + "/privacy.html"}
>
<Eye class="mr-2 h-6 w-6" />
<Tr t={Translations.t.privacy.title} />
</a>
</LoginToggle>

View file

@ -37,7 +37,7 @@
<slot name="close-button">
<!-- The close button is placed _after_ the default slot in order to always paint it on top -->
<button
class="absolute right-10 top-10 h-8 w-8 cursor-pointer border-none bg-white p-0"
class="absolute right-10 top-10 h-8 w-8 cursor-pointer border-none bg-white rounded-full p-0"
on:click={() => dispatch("close")}
>
<XCircleIcon />

View file

@ -20,7 +20,7 @@
>
<slot name="close-button">
<button
class="absolute right-10 top-10 h-8 w-8 cursor-pointer"
class="absolute right-10 top-10 h-8 w-8 cursor-pointer rounded-full"
on:click={() => dispatch("close")}
>
<XCircleIcon />

View file

@ -5,6 +5,7 @@
import Josm_logo from "../../assets/svg/Josm_logo.svelte"
import Constants from "../../Models/Constants"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Utils } from "../../Utils"
export let state: SpecialVisualizationState
const t = Translations.t.general.attribution
@ -13,7 +14,7 @@
josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined))
const showButton = state.osmConnection.userDetails.map(
(ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible
(ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible,
)
function openJosm() {
@ -33,16 +34,20 @@
</script>
{#if $showButton}
{#if $josmState === undefined}
<!-- empty -->
{:else if state === "OK"}
<Tr cls="thanks" t={t.josmOpened} />
{:else}
<Tr cls="alert" t={t.josmNotOpened} />
{/if}
<div class="flex">
<button class="flex items-center small soft grow" on:click={openJosm}>
<Josm_logo class="h-6 w-6 pr-2" />
<Tr t={t.editJosm} />
</button>
{#if $josmState === undefined}
<!-- empty -->
{:else if $josmState === "OK"}
<Tr cls="thanks shrink-0 w-fit" t={t.josmOpened} />
{:else}
<Tr cls="alert shrink-0 w-fit" t={t.josmNotOpened} />
{/if}
</div>
<button class="flex items-center" on:click={openJosm}>
<Josm_logo class="h-12 w-12 p-2 pr-4" />
<Tr t={t.editJosm} />
</button>
{/if}

View file

@ -26,7 +26,7 @@
}
</script>
<button on:click={share} class="secondary m-0 h-8 w-8 p-0" use:ariaLabel={Translations.t.general.share}>
<button on:click={share} class="soft 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>

View file

@ -4,6 +4,7 @@
import Tr from "../Base/Tr.svelte"
import Mapillary_black from "../../assets/svg/Mapillary_black.svelte"
import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
import { twMerge } from "tailwind-merge"
/*
A subtleButton which opens mapillary in a new tab at the current location
@ -16,12 +17,17 @@
let location = mapProperties.location
let zoom = mapProperties.zoom
let mapillaryLink = Mapillary.createLink($location, $zoom)
export let large: boolean = true
</script>
<a class="button flex items-center" href={mapillaryLink} target="_blank">
<Mapillary_black class="m-2 mr-4 h-12 w-12 shrink-0" />
<div class="flex flex-col">
<a class="flex items-center" href={mapillaryLink} target="_blank">
<Mapillary_black class={twMerge("shrink-0",large ? "m-2 mr-4 h-12 w-12" : "w-6 h-6 pr-2")} />
{#if large}
<div class="flex flex-col">
<Tr t={Translations.t.general.attribution.openMapillary} />
<Tr cls="subtle" t={Translations.t.general.attribution.mapillaryHelp} />
</div>
{:else}
<Tr t={Translations.t.general.attribution.openMapillary} />
<Tr cls="subtle" t={Translations.t.general.attribution.mapillaryHelp} />
</div>
{/if}
</a>

View file

@ -26,7 +26,7 @@
}/${$location?.lon ?? 0}`
</script>
<a class="button flex items-center" target="_blank" href={idLink}>
<PencilIcon class="h-12 w-12 p-2 pr-4" />
<a class="flex items-center" target="_blank" href={idLink}>
<PencilIcon class="h-6 w-6 pr-2" />
<Tr t={Translations.t.general.attribution.editId} />
</a>

View file

@ -4,6 +4,9 @@
import Motion from "../../Sensors/Motion"
import { Geocoding } from "../../Logic/Osm/Geocoding"
import type { MapProperties } from "../../Models/MapProperties"
import Hotkeys from "../Base/Hotkeys"
import Translations from "../i18n/Translations"
import Locale from "../i18n/Locale"
export let mapProperties: MapProperties
let lastDisplayed: Date = undefined
@ -14,32 +17,36 @@ async function displayLocation() {
let result = await Geocoding.reverse(
mapProperties.location.data,
mapProperties.zoom.data,
Locale.language.data
)
console.log("Got result", result)
let properties = result.features[0].properties
currentLocation = properties.display_name
window.setTimeout(() => {
if(properties.display_name !== currentLocation){
return
}
currentLocation = undefined
}, 5000)
}
Motion.singleton.lastShakeEvent.addCallbackD(shaken => {
console.log("Got a shaken event")
if (shaken.getTime() - lastDisplayed.getTime() < 1000) {
console.log("To soon:",shaken.getTime() - lastDisplayed.getTime())
// return
if (lastDisplayed !== undefined && shaken.getTime() - lastDisplayed.getTime() < 2000) {
return
}
displayLocation()
})
Hotkeys.RegisterHotkey({ nomod: "q" },
Translations.t.hotkeyDocumentation.queryCurrentLocation,
() => {
displayLocation()
})
Motion.singleton.startListening()
mapProperties.location.stabilized(500).addCallbackAndRun(loc => {
displayLocation()
})
</script>
{#if currentLocation}
<div role="alert" aria-live="assertive" class="normal-background">
<div role="alert" aria-live="assertive" class="normal-background rounded-full border-interactive px-2">
{currentLocation}
</div>
{/if}

View file

@ -55,7 +55,7 @@
<button on:click={() => state.selectedElement.setData(undefined)}
use:ariaLabel={Translations.t.general.backToMap}
class="border-none p-0">
class="border-none p-0 rounded-full">
<XCircleIcon aria-hidden={true} class="h-8 w-8" />
</button>
</div>

View file

@ -7,10 +7,14 @@
import ThemeViewState from "../../Models/ThemeViewState"
import Summary from "./Summary.svelte"
import Tr from "../Base/Tr.svelte"
import { UIEventSource } from "../../Logic/UIEventSource"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import type { KeyNavigationEvent } from "../../Models/MapProperties"
import type { Feature } from "geojson"
export let state: ThemeViewState
export let featuresInViewPort: Store<Feature[]>
console.log("Visual feedback panel:", featuresInViewPort)
const t = Translations.t.general.visualFeedback
let centerFeatures = state.closestFeatures.features
let lastAction: UIEventSource<KeyNavigationEvent> = new UIEventSource<KeyNavigationEvent>(undefined)
@ -22,12 +26,12 @@
<div aria-live="assertive" class="p-1" role="alert">
{#if $lastAction !== undefined}
<Tr t={Translations.t.general.visualFeedback[$lastAction.key]} />
<Tr t={t[$lastAction.key]} />
{:else if $centerFeatures.length === 0}
<Tr t={Translations.t.general.visualFeedback.noCloseFeatures} />
<Tr t={t.noCloseFeatures} />
{:else}
<div class="pointer-events-auto">
<Tr t={Translations.t.general.visualFeedback.closestFeaturesAre} />
<Tr t={t.closestFeaturesAre.Subs({n: $featuresInViewPort?.length})} />
<ol class="list-none">
{#each $centerFeatures as feat, i (feat.properties.id)}
<li class="flex">

View file

@ -41,7 +41,6 @@
return
}
altmap.data.resize()
const { lon, lat } = placedOverMapProperties.location.data
const altMapCenter = pixelCenterOf(altmap)
const c = placedOverMap.data.unproject(altMapCenter)
altproperties.location.setData({ lon: c.lng, lat: c.lat })

View file

@ -429,7 +429,7 @@ class LineRenderingLayer {
}
export default class ShowDataLayer {
private static rangeLayer = new LayerConfig(<any>range_layer, "ShowDataLayer.ts:range.json")
public static rangeLayer = new LayerConfig(<any>range_layer, "ShowDataLayer.ts:range.json")
private readonly _options: ShowDataLayerOptions & {
layer: LayerConfig
drawMarkers?: true | boolean

View file

@ -677,6 +677,7 @@ This list will be sorted
/* We calculate the ranges when it is opened! */
return { startingMonday: lastMonday, ranges: OH.GetRanges(oh, lastMonday, nextSunday) }
}
public static weekdaysIdentical(openingRanges: OpeningRange[][], startday = 0, endday = 4) {
console.log("Checking identical:", openingRanges)
const monday = openingRanges[startday]
@ -928,21 +929,6 @@ export class ToTextualDescription {
return t.all_days_from.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[0]) })
}
if (OH.weekdaysIdentical(ranges, 0, 4) && OH.weekdaysIdentical(ranges, 5, 6)) {
let result = []
if (ranges[0].length > 0) {
result.push(
t.on_weekdays.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[0]) })
)
}
if (ranges[6].length > 0) {
result.push(
t.on_weekdays.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[5]) })
)
}
return ToTextualDescription.chain(result)
}
const result: Translation[] = []
const weekdays = [
"monday",
@ -953,14 +939,33 @@ export class ToTextualDescription {
"saturday",
"sunday",
]
for (let i = 0; i < weekdays.length; i++) {
const day = weekdays[i]
if (ranges[i]?.length > 0) {
result.push(
t[day].Subs({ ranges: ToTextualDescription.createRangesFor(ranges[i]) })
)
function addRange(start: number, end: number) {
for (let i = start; i <= end; i++) {
const day = weekdays[i]
if (ranges[i]?.length > 0) {
result.push(
t[day].Subs({ ranges: ToTextualDescription.createRangesFor(ranges[i]) })
)
}
}
}
if (OH.weekdaysIdentical(ranges, 0, 4)) {
result.push(
t.on_weekdays.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[0]) })
)
} else {
addRange(0, 4)
}
if (OH.weekdaysIdentical(ranges, 5, 6)) {
result.push(
t.on_weekdays.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[5]) })
)
} else {
addRange(5, 6)
}
return ToTextualDescription.chain(result)
}
@ -972,6 +977,7 @@ export class ToTextualDescription {
}
return tr
}
private static timeString(date: Date) {
return OH.hhmm(date.getHours(), date.getMinutes())
}

View file

@ -31,7 +31,7 @@
<HeartSolidIcon aria-hidden={true} />
</button>
{: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 soft" on:click={() => markFavourite(true)}
use:ariaLabel={Translations.t.favouritePoi.button.isNotMarkedShort}>
<HeartOutlineIcon aria-hidden={true} />
</button>

View file

@ -17,7 +17,6 @@
import Tr from "./Base/Tr.svelte"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
import FloatOver from "./Base/FloatOver.svelte"
import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
import Constants from "../Models/Constants"
import TabbedGroup from "./Base/TabbedGroup.svelte"
import UserRelatedState from "../Logic/State/UserRelatedState"
@ -54,7 +53,6 @@
import Plus from "../assets/svg/Plus.svelte"
import Filter from "../assets/svg/Filter.svelte"
import Add from "../assets/svg/Add.svelte"
import Statistics from "../assets/svg/Statistics.svelte"
import Community from "../assets/svg/Community.svelte"
import Download from "../assets/svg/Download.svelte"
import Share from "../assets/svg/Share.svelte"
@ -66,6 +64,11 @@
import Compass_arrow from "../assets/svg/Compass_arrow.svelte"
import ReverseGeocoding from "./BigComponents/ReverseGeocoding.svelte"
import FilterPanel from "./BigComponents/FilterPanel.svelte"
import PrivacyPolicy from "./BigComponents/PrivacyPolicy.svelte"
import { BBox } from "../Logic/BBox"
import { GeoOperations } from "../Logic/GeoOperations"
import ShowDataLayer from "./Map/ShowDataLayer"
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"
export let state: ThemeViewState
let layout = state.layout
@ -100,8 +103,30 @@
)
let currentZoom = state.mapProperties.zoom
let showCrosshair = state.userRelatedState.showCrosshair
let arrowKeysWereUsed = state.visualFeedback
let centerFeatures = state.closestFeatures.features
let visualFeedback = state.visualFeedback
let viewport: UIEventSource<HTMLDivElement> = new UIEventSource<HTMLDivElement>(undefined)
let featuresInViewPort: UIEventSource<Feature[]> = new UIEventSource<Feature[]>(undefined)
viewport.addCallbackAndRunD(viewport => {
state.featuresInView.features.addCallbackAndRunD((features: Feature[]) => {
const rect = viewport.getBoundingClientRect()
const mlmap = state.map.data
if (!mlmap) {
return undefined
}
const topLeft = mlmap.unproject([rect.left, rect.top])
const bottomRight = mlmap.unproject([rect.right, rect.bottom])
const bbox = new BBox([[topLeft.lng, topLeft.lat], [bottomRight.lng, bottomRight.lat]])
const bboxGeo = bbox.asGeoJson({})
console.log("BBOX:", bboxGeo)
const filtered = features.filter((f: Feature) => {
console.log(f, bboxGeo)
return GeoOperations.calculateOverlap(bboxGeo, [f]).length > 0
})
featuresInViewPort.setData(filtered)
})
})
let mapproperties: MapProperties = state.mapProperties
let featureSwitches: FeatureSwitchState = state.featureSwitches
let availableLayers = state.availableLayers
@ -133,6 +158,13 @@
<MaplibreMap map={maplibremap} />
</div>
{#if $visualFeedback}
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden flex items-center justify-center">
<div bind:this={$viewport} style="border: 2px solid #ff000044; width: 300px; height: 300px"></div>
</div>
{/if}
<div class="pointer-events-none absolute top-0 left-0 w-full">
<!-- Top components -->
<If condition={state.featureSwitches.featureSwitchSearch}>
@ -183,7 +215,7 @@
<div class="alert w-fit">Testmode</div>
</If>
</div>
<div class="flex w-full justify-center">
<div class="flex flex-col w-full justify-center items-center">
<!-- Flex and w-full are needed for the positioning -->
<!-- Centermessage -->
<StateIndicator {state} />
@ -238,7 +270,7 @@
</div>
</div>
<If condition={state.visualFeedback}>
<VisualFeedbackPanel {state} />
<VisualFeedbackPanel {state} {featuresInViewPort} />
</If>
<div class="flex flex-col items-end">
@ -265,7 +297,7 @@
<Min class="h-8 w-8" />
</MapControlButton>
<If condition={featureSwitches.featureSwitchGeolocation}>
<div class="relative m-0.5 md:m-1">
<div class="relative m-0">
<MapControlButton arialabel={Translations.t.general.labels.jumpToLocation}
on:click={() => state.geolocationControl.handleClick()}
on:keydown={forwardEventToMap}
@ -288,7 +320,7 @@
<LoginToggle ignoreLoading={true} {state}>
{#if ($showCrosshair === "yes" && $currentZoom >= 17) || $showCrosshair === "always" || $arrowKeysWereUsed}
{#if ($showCrosshair === "yes" && $currentZoom >= 17) || $showCrosshair === "always" || $visualFeedback}
<div
class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center justify-center"
>
@ -416,6 +448,7 @@
</FloatOver>
</IfHidden>
<If condition={state.guistate.menuIsOpened}>
<!-- Menu page -->
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
@ -458,10 +491,27 @@
<Tr t={Translations.t.general.attribution.donate} />
</a>
<a class="flex" href={Utils.OsmChaLinkFor(7)} target="_blank">
<Statistics class="h-6 w-6" />
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: "MapComplete" })} />
</a>
<button class="small soft flex" on:click={() => state.guistate.communityIndexPanelIsOpened.setData(true)}>
<Community class="h-6 w-6" />
<Tr t={Translations.t.communityIndex.title} />
</button>
<If condition={featureSwitches.featureSwitchEnableLogin}>
<OpenIdEditor mapProperties={state.mapProperties} />
<OpenJosm {state} />
<MapillaryLink large={false} mapProperties={state.mapProperties} />
</If>
<button class="small soft flex"
on:click={() => state.guistate.privacyPanelIsOpened.setData(true)}
>
<EyeIcon class="w-6 h-6 pr-1" />
<Tr t={Translations.t.privacy.title} />
</button>
<div class="m-2 flex flex-col">
<ToSvelte construct={Hotkeys.generateDocumentationDynamic} />
</div>
{Constants.vNumber}
</div>
@ -503,31 +553,40 @@
</h3>
<Favourites {state} />
</div>
<div class="flex" slot="title3">
<Community class="h-6 w-6" />
<Tr t={Translations.t.communityIndex.title} />
</div>
<div class="m-2" slot="content3">
<CommunityIndexView location={state.mapProperties.location} />
</div>
<div class="flex" slot="title4">
<EyeIcon class="w-6" />
<Tr t={Translations.t.privacy.title} />
</div>
<div class="m-2" slot="content4">
<ToSvelte construct={() => new PrivacyPolicy()} />
</div>
<Tr slot="title5" t={Translations.t.advanced.title} />
<div class="m-2 flex flex-col" slot="content5">
<If condition={featureSwitches.featureSwitchEnableLogin}>
<OpenIdEditor mapProperties={state.mapProperties} />
<OpenJosm {state} />
<MapillaryLink mapProperties={state.mapProperties} />
</If>
<ToSvelte construct={Hotkeys.generateDocumentationDynamic} />
</div>
</TabbedGroup>
</FloatOver>
</If>
<If condition={state.guistate.privacyPanelIsOpened}>
<FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
<div class="h-full flex flex-col overflow-hidden">
<h2 class="flex items-center low-interaction p-4 m-0">
<EyeIcon class="w-6 pr-2" />
<Tr t={Translations.t.privacy.title} />
</h2>
<div class="overflow-auto p-4">
<PrivacyPolicy />
</div>
</div>
</FloatOver>
</If>
<If condition={state.guistate.communityIndexPanelIsOpened}>
<FloatOver on:close={() => state.guistate.communityIndexPanelIsOpened.setData(false)}>
<div class="h-full flex flex-col overflow-hidden">
<h2 class="flex items-center low-interaction p-4 m-0">
<Community class="h-6 w-6" />
<Tr t={Translations.t.communityIndex.title} />
</h2>
<div class="overflow-auto p-4">
<CommunityIndexView location={state.mapProperties.location} />
</div>
</div>
</FloatOver>
</If>

View file

@ -350,6 +350,11 @@ label.checked:not(.neutral-label) {
width: 100%;
}
button.soft, .button.soft {
border: 2px solid var(--interactive-background);
margin: 0;
}
.links-as-button a {
/*
* Let a 'link' mimick a button, but not entirely