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
											
										
									
								
							| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2000,19 +2000,6 @@
 | 
			
		|||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "tagRenderings": {
 | 
			
		||||
      "rewritten-questions": {
 | 
			
		||||
        "renderings": {
 | 
			
		||||
          "0": {
 | 
			
		||||
            "question": "Wie viele Stecker vom Typ {{description}} sind hier vorhanden?"
 | 
			
		||||
          },
 | 
			
		||||
          "1": {
 | 
			
		||||
            "mappings": {
 | 
			
		||||
              "then": "{{description}} liefert {{commonVoltages}} Volt"
 | 
			
		||||
            },
 | 
			
		||||
            "question": "Welche Spannung liefern die {{description}}?"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
            "Authentication": {
 | 
			
		||||
                "mappings": {
 | 
			
		||||
                    "1": {
 | 
			
		||||
| 
						 | 
				
			
			@ -2293,6 +2280,19 @@
 | 
			
		|||
                "question": "Welche Kennnummer hat die Ladestation?",
 | 
			
		||||
                "render": "Die Kennziffer ist <b>{ref}</b>"
 | 
			
		||||
            },
 | 
			
		||||
            "rewritten-questions": {
 | 
			
		||||
                "renderings": {
 | 
			
		||||
                    "0": {
 | 
			
		||||
                        "question": "Wie viele Stecker vom Typ {{description}} sind hier vorhanden?"
 | 
			
		||||
                    },
 | 
			
		||||
                    "1": {
 | 
			
		||||
                        "mappings": {
 | 
			
		||||
                            "then": "{{description}} liefert {{commonVoltages}} Volt"
 | 
			
		||||
                        },
 | 
			
		||||
                        "question": "Welche Spannung liefern die {{description}}?"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            "website": {
 | 
			
		||||
                "question": "Auf welcher Webseite kann man weitere Informationen über diese Ladestation finden?",
 | 
			
		||||
                "render": "Weitere Informationen unter <a href='{website}'>{website}</a>"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,39 +4,11 @@ export class ThemeMetaTagging {
 | 
			
		|||
   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"
 | 
			
		||||
      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,10 +487,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    private initHotkeys() {
 | 
			
		||||
        Hotkeys.RegisterHotkey(
 | 
			
		||||
            { nomod: "Escape", onUp: true },
 | 
			
		||||
            Translations.t.hotkeyDocumentation.closeSidebar,
 | 
			
		||||
            () => {
 | 
			
		||||
        const docs = Translations.t.hotkeyDocumentation
 | 
			
		||||
        Hotkeys.RegisterHotkey({ nomod: "Escape", onUp: true }, docs.closeSidebar, () => {
 | 
			
		||||
            if (this.previewedImage.data !== undefined) {
 | 
			
		||||
                this.previewedImage.setData(undefined)
 | 
			
		||||
                return
 | 
			
		||||
| 
						 | 
				
			
			@ -498,24 +496,19 @@ export default class ThemeViewState implements SpecialVisualizationState {
 | 
			
		|||
            this.selectedElement.setData(undefined)
 | 
			
		||||
            this.guistate.closeAll()
 | 
			
		||||
            this.focusOnMap()
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        Hotkeys.RegisterHotkey(
 | 
			
		||||
            { nomod: "f" },
 | 
			
		||||
            Translations.t.hotkeyDocumentation.selectFavourites,
 | 
			
		||||
            () => {
 | 
			
		||||
        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" on:click={openJosm}>
 | 
			
		||||
    <Josm_logo class="h-12 w-12 p-2 pr-4" />
 | 
			
		||||
    <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>
 | 
			
		||||
 | 
			
		||||
{/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" />
 | 
			
		||||
<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} />
 | 
			
		||||
  {/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()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
Motion.singleton.startListening()
 | 
			
		||||
mapProperties.location.stabilized(500).addCallbackAndRun(loc => {
 | 
			
		||||
Hotkeys.RegisterHotkey({ nomod: "q" },
 | 
			
		||||
  Translations.t.hotkeyDocumentation.queryCurrentLocation,
 | 
			
		||||
  () => {
 | 
			
		||||
    displayLocation()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Motion.singleton.startListening()
 | 
			
		||||
</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,7 +939,9 @@ export class ToTextualDescription {
 | 
			
		|||
            "saturday",
 | 
			
		||||
            "sunday",
 | 
			
		||||
        ]
 | 
			
		||||
        for (let i = 0; i < weekdays.length; 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(
 | 
			
		||||
| 
						 | 
				
			
			@ -961,6 +949,23 @@ export class ToTextualDescription {
 | 
			
		|||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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