forked from MapComplete/MapComplete
Move various tabs into buttons, more work on a11y
This commit is contained in:
parent
cce9b879f2
commit
7e852dd7e3
29 changed files with 10642 additions and 10432 deletions
File diff suppressed because it is too large
Load diff
|
@ -2227,7 +2227,7 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "internet-ssid",
|
||||
"labels": [
|
||||
|
|
|
@ -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",
|
||||
|
|
20244
langs/layers/de.json
20244
langs/layers/de.json
File diff suppressed because it is too large
Load diff
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(/</g, "<")?.replace(/>/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(/</g,'<')?.replace(/>/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'
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue