forked from MapComplete/MapComplete
A11y: improve flow with screenreader, some more refactoring to svelte, see #1181
This commit is contained in:
parent
40067e35d4
commit
48ac539272
15 changed files with 188 additions and 226 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "unit",
|
"id": "unit",
|
||||||
"description": {
|
"description": {
|
||||||
"en": "Library layer with all common units"
|
"en": "Library layer with all common units. Units can _only_ be imported from this file."
|
||||||
},
|
},
|
||||||
"source": "special:library",
|
"source": "special:library",
|
||||||
"units": [
|
"units": [
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"nl": "Breng jouw buurtnatuur in kaart"
|
"nl": "Breng jouw buurtnatuur in kaart"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"nl": "<img style='float:right;margin: 1em;width: 10em;height: auto;' src='./assets/themes/buurtnatuur/groen_logo.svg' alt='logo-groen' class='logo/> <br /><b>Natuur maakt gelukkig.</b> Aan de hand van deze website willen we de natuur dicht bij ons beter inventariseren. Met als doel meer mensen te laten genieten van toegankelijke natuur én te strijden voor meer natuur in onze buurten.<ul><li>In welke natuurgebieden kan jij terecht? Hoe toegankelijk zijn ze?</li><li>In welke bossen kan een gezin in jouw gemeente opnieuw op adem komen?</li><li>Op welke onbekende plekjes is het zalig spelen?</li></ul><p>Samen kleuren we heel Vlaanderen en Brussel groen.Blijf op de hoogte van de resultaten van buurtnatuur.be: <a href='https://www.groen.be/buurtnatuur' target='_blank'>meld je aan voor e-mailupdates</a>."
|
"nl": "<img style='float:right;margin: 1em;width: 10em;height: auto;' src='./assets/themes/buurtnatuur/groen_logo.svg' alt='logo-groen' class='logo'/> <br /><b>Natuur maakt gelukkig.</b> Aan de hand van deze website willen we de natuur dicht bij ons beter inventariseren. Met als doel meer mensen te laten genieten van toegankelijke natuur én te strijden voor meer natuur in onze buurten.<ul><li>In welke natuurgebieden kan jij terecht? Hoe toegankelijk zijn ze?</li><li>In welke bossen kan een gezin in jouw gemeente opnieuw op adem komen?</li><li>Op welke onbekende plekjes is het zalig spelen?</li></ul><p>Samen kleuren we heel Vlaanderen en Brussel groen.Blijf op de hoogte van de resultaten van buurtnatuur.be: <a href='https://www.groen.be/buurtnatuur' target='_blank'>meld je aan voor e-mailupdates</a>."
|
||||||
},
|
},
|
||||||
"shortDescription": {
|
"shortDescription": {
|
||||||
"nl": "Met deze tool kan je natuur in je buurt in kaart brengen en meer informatie geven over je favoriete plekje"
|
"nl": "Met deze tool kan je natuur in je buurt in kaart brengen en meer informatie geven over je favoriete plekje"
|
||||||
|
|
|
@ -182,7 +182,8 @@
|
||||||
"backgroundSwitch": "Switch background",
|
"backgroundSwitch": "Switch background",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"customThemeIntro": "<h3>Custom themes</h3>These are previously visited user-generated themes.",
|
"customThemeIntro": "These are previously visited user-generated themes.",
|
||||||
|
"customThemeTitle": "Custom themes",
|
||||||
"download": {
|
"download": {
|
||||||
"downloadAsPdf": "Download a PDF of the current map",
|
"downloadAsPdf": "Download a PDF of the current map",
|
||||||
"downloadAsPdfHelper": "Ideal to print the current map",
|
"downloadAsPdfHelper": "Ideal to print the current map",
|
||||||
|
|
|
@ -10346,4 +10346,4 @@
|
||||||
"render": "wind turbine"
|
"render": "wind turbine"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -876,16 +876,16 @@ video {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx-1 {
|
|
||||||
margin-left: 0.25rem;
|
|
||||||
margin-right: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.my-2 {
|
.my-2 {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx-1 {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
.mx-10 {
|
.mx-10 {
|
||||||
margin-left: 2.5rem;
|
margin-left: 2.5rem;
|
||||||
margin-right: 2.5rem;
|
margin-right: 2.5rem;
|
||||||
|
@ -1262,10 +1262,6 @@ video {
|
||||||
width: 16rem;
|
width: 16rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-1\/2 {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-14 {
|
.w-14 {
|
||||||
width: 3.5rem;
|
width: 3.5rem;
|
||||||
}
|
}
|
||||||
|
@ -1533,6 +1529,10 @@ video {
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.justify-self-center {
|
||||||
|
justify-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
.overflow-auto {
|
.overflow-auto {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
@ -2892,6 +2892,10 @@ a.link-underline {
|
||||||
width: 6rem;
|
width: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sm\:w-1\/2 {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.sm\:flex-nowrap {
|
.sm\:flex-nowrap {
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,18 @@
|
||||||
import Translations from "./i18n/Translations"
|
import Translations from "./i18n/Translations"
|
||||||
import Logo from "../assets/svg/Logo.svelte"
|
import Logo from "../assets/svg/Logo.svelte"
|
||||||
import Tr from "./Base/Tr.svelte"
|
import Tr from "./Base/Tr.svelte"
|
||||||
import ToSvelte from "./Base/ToSvelte.svelte"
|
|
||||||
import MoreScreen from "./BigComponents/MoreScreen"
|
import MoreScreen from "./BigComponents/MoreScreen"
|
||||||
import LoginToggle from "./Base/LoginToggle.svelte"
|
import LoginToggle from "./Base/LoginToggle.svelte"
|
||||||
import Pencil from "../assets/svg/Pencil.svelte"
|
import Pencil from "../assets/svg/Pencil.svelte"
|
||||||
import Login from "../assets/svg/Login.svelte"
|
import Login from "../assets/svg/Login.svelte"
|
||||||
import Constants from "../Models/Constants"
|
import Constants from "../Models/Constants"
|
||||||
|
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||||
|
import { placeholder } from "../Utils/placeholder"
|
||||||
|
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||||
|
import ThemesList from "./BigComponents/ThemesList.svelte"
|
||||||
|
import { LayoutInformation } from "../Models/ThemeConfig/LayoutConfig"
|
||||||
|
import * as themeOverview from "../assets/generated/theme_overview.json"
|
||||||
|
import UnofficialThemeList from "./BigComponents/UnofficialThemeList.svelte"
|
||||||
|
|
||||||
const featureSwitches = new OsmConnectionFeatureSwitches()
|
const featureSwitches = new OsmConnectionFeatureSwitches()
|
||||||
const osmConnection = new OsmConnection({
|
const osmConnection = new OsmConnection({
|
||||||
|
@ -20,17 +26,45 @@
|
||||||
oauth_token: QueryParameters.GetQueryParameter(
|
oauth_token: QueryParameters.GetQueryParameter(
|
||||||
"oauth_token",
|
"oauth_token",
|
||||||
undefined,
|
undefined,
|
||||||
"Used to complete the login"
|
"Used to complete the login",
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
const state = new UserRelatedState(osmConnection)
|
const state = new UserRelatedState(osmConnection)
|
||||||
const t = Translations.t.index
|
const t = Translations.t.index
|
||||||
|
const tr = Translations.t.general.morescreen
|
||||||
|
|
||||||
let userLanguages = osmConnection.userDetails.map((ud) => ud.languages)
|
let userLanguages = osmConnection.userDetails.map((ud) => ud.languages)
|
||||||
|
let themeSearchText: UIEventSource<string | undefined> = new UIEventSource<string>(undefined)
|
||||||
|
|
||||||
|
document.addEventListener("keydown", function(event) {
|
||||||
|
if (event.ctrlKey && event.code === "KeyF") {
|
||||||
|
document.getElementById("theme-search")?.focus()
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let visitedHiddenThemes: Store<LayoutInformation[]>
|
||||||
|
const hiddenThemes: LayoutInformation[] =
|
||||||
|
(themeOverview["default"] ?? themeOverview)?.filter((layout) => layout.hideFromOverview) ?? []
|
||||||
|
{
|
||||||
|
const prefix = "mapcomplete-hidden-theme-"
|
||||||
|
const userPreferences = state.osmConnection.preferencesHandler.preferences
|
||||||
|
visitedHiddenThemes = userPreferences.map(preferences => {
|
||||||
|
const knownIds = new Set<string>(
|
||||||
|
Object.keys(preferences)
|
||||||
|
.filter((key) => key.startsWith(prefix))
|
||||||
|
.map((key) => key.substring(prefix.length, key.length - "-enabled".length)),
|
||||||
|
)
|
||||||
|
return hiddenThemes.filter((theme) => knownIds.has(theme.id))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="m-4 flex flex-col">
|
<div class="m-4 flex flex-col">
|
||||||
<LanguagePicker clss="self-end" assignTo={state.language} availableLanguages={t.title.SupportedLanguages()}
|
<LanguagePicker assignTo={state.language} availableLanguages={t.title.SupportedLanguages()} clss="self-end"
|
||||||
preferredLanguages={userLanguages} />
|
preferredLanguages={userLanguages} />
|
||||||
|
|
||||||
<div class="mt-4 flex">
|
<div class="mt-4 flex">
|
||||||
<div class="m-3 flex-none">
|
<div class="m-3 flex-none">
|
||||||
|
@ -48,9 +82,42 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ToSvelte construct={new MoreScreen(state, true)} />
|
|
||||||
|
<form class="flex justify-center" on:submit|preventDefault={_ => MoreScreen.applySearch(themeSearchText.data)}>
|
||||||
|
<label
|
||||||
|
class="flex rounded-full border-2 border-black items-center my-2 w-full sm:w-1/2 neutral-label">
|
||||||
|
<SearchIcon aria-hidden="true" class="w-8 h-8" />
|
||||||
|
<input autofocus bind:value={$themeSearchText} class="mr-4 w-full" id="theme-search"
|
||||||
|
type="search"
|
||||||
|
use:placeholder={tr.searchForATheme}>
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<ThemesList search={themeSearchText} {state} themes={MoreScreen.officialThemes} />
|
||||||
|
|
||||||
<LoginToggle {state}>
|
<LoginToggle {state}>
|
||||||
|
<ThemesList
|
||||||
|
hideThemes={false}
|
||||||
|
isCustom={false}
|
||||||
|
search={themeSearchText}
|
||||||
|
{state}
|
||||||
|
themes={$visitedHiddenThemes}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="title">
|
||||||
|
<h3>
|
||||||
|
<Tr t={tr.previouslyHiddenTitle} />
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
<Tr t={tr.hiddenExplanation.Subs({
|
||||||
|
hidden_discovered: $visitedHiddenThemes.length.toString(),
|
||||||
|
total_hidden: hiddenThemes.length.toString(),
|
||||||
|
})} />
|
||||||
|
</p>
|
||||||
|
</svelte:fragment>
|
||||||
|
</ThemesList>
|
||||||
|
|
||||||
|
<UnofficialThemeList search={themeSearchText} {state} />
|
||||||
|
|
||||||
<div slot="not-logged-in">
|
<div slot="not-logged-in">
|
||||||
<button class="w-full" on:click={() => osmConnection.AttemptLogin()}>
|
<button class="w-full" on:click={() => osmConnection.AttemptLogin()}>
|
||||||
<Login class="mr-2 h-6 w-6 " />
|
<Login class="mr-2 h-6 w-6 " />
|
||||||
|
@ -64,9 +131,13 @@
|
||||||
<Pencil class="mr-2 h-6 w-6" />
|
<Pencil class="mr-2 h-6 w-6" />
|
||||||
<Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
|
<Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
</LoginToggle>
|
</LoginToggle>
|
||||||
|
|
||||||
<Tr cls="link-underline" t={Translations.t.general.aboutMapComplete.intro} />
|
<Tr cls="link-underline" t={Translations.t.general.aboutMapComplete.intro} />
|
||||||
|
<Tr t={tr.streetcomplete} />
|
||||||
|
|
||||||
<div class="subtle mb-16 self-end">
|
<div class="subtle mb-16 self-end">
|
||||||
v{Constants.vNumber}
|
v{Constants.vNumber}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import { Translation } from "../i18n/Translation"
|
import { Translation } from "../i18n/Translation"
|
||||||
import WeblateLink from "./WeblateLink.svelte"
|
import WeblateLink from "./WeblateLink.svelte"
|
||||||
import { Store } from "../../Logic/UIEventSource"
|
import { Store } from "../../Logic/UIEventSource"
|
||||||
|
import FromHtml from "./FromHtml.svelte"
|
||||||
|
|
||||||
export let t: Translation
|
export let t: Translation
|
||||||
export let cls: string = ""
|
export let cls: string = ""
|
||||||
|
@ -14,9 +15,9 @@
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if txt}
|
{#if $txt}
|
||||||
<span class={cls}>
|
<span class={cls}>
|
||||||
{$txt}
|
<FromHtml src={$txt}/>
|
||||||
<WeblateLink context={t.context} />
|
<WeblateLink context={t.context} />
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
import { BBox } from "../../Logic/BBox"
|
import { BBox } from "../../Logic/BBox"
|
||||||
import { GeoIndexedStoreForLayer } from "../../Logic/FeatureSource/Actors/GeoIndexedStore"
|
import { GeoIndexedStoreForLayer } from "../../Logic/FeatureSource/Actors/GeoIndexedStore"
|
||||||
import { createEventDispatcher, onDestroy } from "svelte"
|
import { createEventDispatcher, onDestroy } from "svelte"
|
||||||
|
import { placeholder } from "../../Utils/placeholder"
|
||||||
|
|
||||||
export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined
|
export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined
|
||||||
export let bounds: UIEventSource<BBox>
|
export let bounds: UIEventSource<BBox>
|
||||||
export let selectedElement: UIEventSource<Feature> | undefined = undefined
|
export let selectedElement: UIEventSource<Feature> | undefined = undefined
|
||||||
|
|
||||||
export let clearAfterView: boolean = true
|
export let clearAfterView: boolean = true
|
||||||
|
|
||||||
let searchContents: string = ""
|
let searchContents: string = ""
|
||||||
export let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
export let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
||||||
onDestroy(
|
onDestroy(
|
||||||
|
@ -31,17 +31,6 @@
|
||||||
|
|
||||||
let feedback: string = undefined
|
let feedback: string = undefined
|
||||||
|
|
||||||
let placeholder = Translations.t.general.search.search.current
|
|
||||||
$:{
|
|
||||||
if(inputElement){
|
|
||||||
inputElement.placeholder = placeholder.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onDestroy(placeholder.addCallbackAndRunD(placeholder => {
|
|
||||||
if(inputElement){
|
|
||||||
inputElement.placeholder = placeholder
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
|
|
||||||
Hotkeys.RegisterHotkey({ ctrl: "F" }, Translations.t.hotkeyDocumentation.selectSearch, () => {
|
Hotkeys.RegisterHotkey({ ctrl: "F" }, Translations.t.hotkeyDocumentation.selectSearch, () => {
|
||||||
|
@ -124,6 +113,7 @@
|
||||||
bind:this={inputElement}
|
bind:this={inputElement}
|
||||||
on:keypress={(keypr) => (keypr.key === "Enter" ? performSearch() : undefined)}
|
on:keypress={(keypr) => (keypr.key === "Enter" ? performSearch() : undefined)}
|
||||||
bind:value={searchContents}
|
bind:value={searchContents}
|
||||||
|
use:placeholder={Translations.t.general.search.search}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
|
||||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
|
||||||
import * as themeOverview from "../../assets/generated/theme_overview.json"
|
|
||||||
import { Utils } from "../../Utils"
|
|
||||||
import ThemesList from "./ThemesList.svelte"
|
|
||||||
import Translations from "../i18n/Translations"
|
|
||||||
import { LayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
|
||||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
|
||||||
|
|
||||||
export let search: UIEventSource<string>
|
|
||||||
export let state: { osmConnection: OsmConnection }
|
|
||||||
export let onMainScreen: boolean = true
|
|
||||||
|
|
||||||
const prefix = "mapcomplete-hidden-theme-"
|
|
||||||
const hiddenThemes: LayoutInformation[] =
|
|
||||||
(themeOverview["default"] ?? themeOverview)?.filter((layout) => layout.hideFromOverview) ?? []
|
|
||||||
const userPreferences = state.osmConnection.preferencesHandler.preferences
|
|
||||||
const t = Translations.t.general.morescreen
|
|
||||||
|
|
||||||
let knownThemesId: string[]
|
|
||||||
$: knownThemesId = Utils.NoNull(
|
|
||||||
Object.keys($userPreferences)
|
|
||||||
.filter((key) => key.startsWith(prefix))
|
|
||||||
.map((key) => key.substring(prefix.length, key.length - "-enabled".length))
|
|
||||||
)
|
|
||||||
$: console.log("Known theme ids:", knownThemesId)
|
|
||||||
$: knownThemes = hiddenThemes.filter((theme) => knownThemesId.includes(theme.id))
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<LoginToggle {state}>
|
|
||||||
<ThemesList
|
|
||||||
hideThemes={false}
|
|
||||||
isCustom={false}
|
|
||||||
{onMainScreen}
|
|
||||||
{search}
|
|
||||||
{state}
|
|
||||||
themes={knownThemes}
|
|
||||||
>
|
|
||||||
<svelte:fragment slot="title">
|
|
||||||
<h3>{t.previouslyHiddenTitle.toString()}</h3>
|
|
||||||
<p>
|
|
||||||
{t.hiddenExplanation.Subs({
|
|
||||||
hidden_discovered: knownThemes.length.toString(),
|
|
||||||
total_hidden: hiddenThemes.length.toString(),
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
</svelte:fragment>
|
|
||||||
</ThemesList>
|
|
||||||
</LoginToggle>
|
|
|
@ -1,109 +1,48 @@
|
||||||
import Svg from "../../Svg"
|
import { LayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||||
import Combine from "../Base/Combine"
|
|
||||||
import Translations from "../i18n/Translations"
|
|
||||||
import LayoutConfig, { LayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
|
||||||
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
|
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
|
||||||
import UserRelatedState from "../../Logic/State/UserRelatedState"
|
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import themeOverview from "../../assets/generated/theme_overview.json"
|
import themeOverview from "../../assets/generated/theme_overview.json"
|
||||||
import { TextField } from "../Input/TextField"
|
|
||||||
import Locale from "../i18n/Locale"
|
import Locale from "../i18n/Locale"
|
||||||
import SvelteUIElement from "../Base/SvelteUIElement"
|
|
||||||
import ThemesList from "./ThemesList.svelte"
|
|
||||||
import HiddenThemeList from "./HiddenThemeList.svelte"
|
|
||||||
import UnofficialThemeList from "./UnofficialThemeList.svelte"
|
|
||||||
|
|
||||||
export default class MoreScreen extends Combine {
|
export default class MoreScreen {
|
||||||
private static readonly officialThemes: LayoutInformation[] = themeOverview
|
public static readonly officialThemes: LayoutInformation[] = themeOverview
|
||||||
|
|
||||||
constructor(
|
public static applySearch(searchTerm: string) {
|
||||||
state: UserRelatedState & {
|
searchTerm = searchTerm.toLowerCase()
|
||||||
layoutToUse?: LayoutConfig
|
if (!searchTerm) {
|
||||||
},
|
return
|
||||||
onMainScreen: boolean = false
|
}
|
||||||
) {
|
if (searchTerm === "personal") {
|
||||||
const tr = Translations.t.general.morescreen
|
window.location.href = MoreScreen.createUrlFor({ id: "personal" }, false).data
|
||||||
|
}
|
||||||
const search = new TextField({
|
if (searchTerm === "bugs" || searchTerm === "issues") {
|
||||||
placeholder: tr.searchForATheme,
|
window.location.href = "https://github.com/pietervdvn/MapComplete/issues"
|
||||||
})
|
}
|
||||||
search.enterPressed.addCallbackD((searchTerm) => {
|
if (searchTerm === "source") {
|
||||||
searchTerm = searchTerm.toLowerCase()
|
window.location.href = "https://github.com/pietervdvn/MapComplete"
|
||||||
if (!searchTerm) {
|
}
|
||||||
return
|
if (searchTerm === "docs") {
|
||||||
}
|
window.location.href = "https://github.com/pietervdvn/MapComplete/tree/develop/Docs"
|
||||||
if (searchTerm === "personal") {
|
}
|
||||||
window.location.href = MoreScreen.createUrlFor(
|
if (searchTerm === "osmcha" || searchTerm === "stats") {
|
||||||
{ id: "personal" },
|
window.location.href = Utils.OsmChaLinkFor(7)
|
||||||
false,
|
}
|
||||||
state
|
// Enter pressed -> search the first _official_ matchin theme and open it
|
||||||
).data
|
const publicTheme = MoreScreen.officialThemes.find(
|
||||||
}
|
(th) =>
|
||||||
if (searchTerm === "bugs" || searchTerm === "issues") {
|
th.hideFromOverview == false &&
|
||||||
window.location.href = "https://github.com/pietervdvn/MapComplete/issues"
|
th.id !== "personal" &&
|
||||||
}
|
MoreScreen.MatchesLayout(th, searchTerm)
|
||||||
if (searchTerm === "source") {
|
)
|
||||||
window.location.href = "https://github.com/pietervdvn/MapComplete"
|
if (publicTheme !== undefined) {
|
||||||
}
|
window.location.href = MoreScreen.createUrlFor(publicTheme, false).data
|
||||||
if (searchTerm === "docs") {
|
}
|
||||||
window.location.href = "https://github.com/pietervdvn/MapComplete/tree/develop/Docs"
|
const hiddenTheme = MoreScreen.officialThemes.find(
|
||||||
}
|
(th) => th.id !== "personal" && MoreScreen.MatchesLayout(th, searchTerm)
|
||||||
if (searchTerm === "osmcha" || searchTerm === "stats") {
|
)
|
||||||
window.location.href = Utils.OsmChaLinkFor(7)
|
if (hiddenTheme !== undefined) {
|
||||||
}
|
window.location.href = MoreScreen.createUrlFor(hiddenTheme, false).data
|
||||||
// Enter pressed -> search the first _official_ matchin theme and open it
|
|
||||||
const publicTheme = MoreScreen.officialThemes.find(
|
|
||||||
(th) =>
|
|
||||||
th.hideFromOverview == false &&
|
|
||||||
th.id !== "personal" &&
|
|
||||||
MoreScreen.MatchesLayout(th, searchTerm)
|
|
||||||
)
|
|
||||||
if (publicTheme !== undefined) {
|
|
||||||
window.location.href = MoreScreen.createUrlFor(publicTheme, false, state).data
|
|
||||||
}
|
|
||||||
const hiddenTheme = MoreScreen.officialThemes.find(
|
|
||||||
(th) => th.id !== "personal" && MoreScreen.MatchesLayout(th, searchTerm)
|
|
||||||
)
|
|
||||||
if (hiddenTheme !== undefined) {
|
|
||||||
window.location.href = MoreScreen.createUrlFor(hiddenTheme, false, state).data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (onMainScreen) {
|
|
||||||
search.focus()
|
|
||||||
document.addEventListener("keydown", function (event) {
|
|
||||||
if (event.ctrlKey && event.code === "KeyF") {
|
|
||||||
search.focus()
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchBar = new Combine([
|
|
||||||
Svg.search_svg().SetClass("w-8"),
|
|
||||||
search.SetClass("mr-4 w-full"),
|
|
||||||
]).SetClass("flex rounded-full border-2 border-black items-center my-2 w-1/2")
|
|
||||||
|
|
||||||
super([
|
|
||||||
new Combine([searchBar]).SetClass("flex justify-center"),
|
|
||||||
new SvelteUIElement(ThemesList, {
|
|
||||||
state,
|
|
||||||
onMainScreen,
|
|
||||||
search: search.GetValue(),
|
|
||||||
themes: MoreScreen.officialThemes,
|
|
||||||
}),
|
|
||||||
new SvelteUIElement(HiddenThemeList, {
|
|
||||||
state,
|
|
||||||
onMainScreen,
|
|
||||||
search: search.GetValue(),
|
|
||||||
}),
|
|
||||||
new SvelteUIElement(UnofficialThemeList, {
|
|
||||||
state,
|
|
||||||
onMainScreen,
|
|
||||||
search: search.GetValue(),
|
|
||||||
}),
|
|
||||||
tr.streetcomplete.Clone().SetClass("block text-base mx-10 my-3 mb-10"),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MatchesLayout(
|
public static MatchesLayout(
|
||||||
|
@ -139,7 +78,7 @@ export default class MoreScreen extends Combine {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createUrlFor(
|
public static createUrlFor(
|
||||||
layout: { id: string; definition?: string },
|
layout: { id: string; definition?: string },
|
||||||
isCustom: boolean,
|
isCustom: boolean,
|
||||||
state?: { layoutToUse?: { id } }
|
state?: { layoutToUse?: { id } }
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import NextButton from "../Base/NextButton.svelte"
|
import NextButton from "../Base/NextButton.svelte"
|
||||||
import Geosearch from "./Geosearch.svelte"
|
import Geosearch from "./Geosearch.svelte"
|
||||||
import ThemeViewState from "../../Models/ThemeViewState"
|
import ThemeViewState from "../../Models/ThemeViewState"
|
||||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
|
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||||
import { twJoin } from "tailwind-merge"
|
import { twJoin } from "tailwind-merge"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
import Add from "../../assets/svg/Add.svelte"
|
import Add from "../../assets/svg/Add.svelte"
|
||||||
import Location_refused from "../../assets/svg/Location_refused.svelte"
|
import Location_refused from "../../assets/svg/Location_refused.svelte"
|
||||||
import Crosshair from "../../assets/svg/Crosshair.svelte"
|
import Crosshair from "../../assets/svg/Crosshair.svelte"
|
||||||
|
import FromHtml from "../Base/FromHtml.svelte"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The theme introduction panel
|
* The theme introduction panel
|
||||||
|
@ -27,12 +28,12 @@
|
||||||
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
||||||
let searchEnabled = false
|
let searchEnabled = false
|
||||||
|
|
||||||
let geopermission: Readable<GeolocationPermissionState> =
|
let geopermission: Store<GeolocationPermissionState> =
|
||||||
state.geolocation.geolocationState.permission
|
state.geolocation.geolocationState.permission
|
||||||
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
|
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
|
||||||
|
|
||||||
geopermission.addCallback((perm) => console.log(">>>> Permission", perm))
|
geopermission.addCallback((perm) => console.log(">>>> Permission", perm))
|
||||||
|
|
||||||
function jumpToCurrentLocation() {
|
function jumpToCurrentLocation() {
|
||||||
const glstate = state.geolocation.geolocationState
|
const glstate = state.geolocation.geolocationState
|
||||||
if (glstate.currentGPSLocation.data !== undefined) {
|
if (glstate.currentGPSLocation.data !== undefined) {
|
||||||
|
@ -57,8 +58,8 @@
|
||||||
<Tr t={Translations.t.general.welcomeExplanation.addNew} />
|
<Tr t={Translations.t.general.welcomeExplanation.addNew} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Tr t={layout.descriptionTail} />
|
<Tr t={layout.descriptionTail}/>
|
||||||
|
|
||||||
<!-- Buttons: open map, go to location, search -->
|
<!-- Buttons: open map, go to location, search -->
|
||||||
<NextButton clss="primary w-full" on:click={() => state.guistate.themeIsOpened.setData(false)}>
|
<NextButton clss="primary w-full" on:click={() => state.guistate.themeIsOpened.setData(false)}>
|
||||||
<div class="flex w-full justify-center text-2xl">
|
<div class="flex w-full justify-center text-2xl">
|
||||||
|
@ -110,8 +111,8 @@
|
||||||
<Geosearch
|
<Geosearch
|
||||||
bounds={state.mapProperties.bounds}
|
bounds={state.mapProperties.bounds}
|
||||||
on:searchCompleted={() => state.guistate.themeIsOpened.setData(false)}
|
on:searchCompleted={() => state.guistate.themeIsOpened.setData(false)}
|
||||||
on:searchIsValid={(isValid) => {
|
on:searchIsValid={(event) => {
|
||||||
searchEnabled = isValid
|
searchEnabled = event.detail
|
||||||
}}
|
}}
|
||||||
perLayer={state.perLayer}
|
perLayer={state.perLayer}
|
||||||
{selectedElement}
|
{selectedElement}
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
export let themes: LayoutInformation[]
|
export let themes: LayoutInformation[]
|
||||||
export let state: { osmConnection: OsmConnection }
|
export let state: { osmConnection: OsmConnection }
|
||||||
export let isCustom: boolean = false
|
export let isCustom: boolean = false
|
||||||
export let onMainScreen: boolean = true
|
|
||||||
export let hideThemes: boolean = true
|
export let hideThemes: boolean = true
|
||||||
|
|
||||||
// Filter theme based on search value
|
// Filter theme based on search value
|
||||||
|
@ -25,34 +24,25 @@
|
||||||
|
|
||||||
<section class="w-full">
|
<section class="w-full">
|
||||||
<slot name="title" />
|
<slot name="title" />
|
||||||
{#if onMainScreen}
|
<div class="gap-4 md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3">
|
||||||
<div class="gap-4 md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3">
|
{#each filteredThemes as theme (theme.id)}
|
||||||
{#each filteredThemes as theme (theme.id)}
|
{#if theme !== undefined && !(hideThemes && theme?.hideFromOverview)}
|
||||||
{#if theme !== undefined && !(hideThemes && theme?.hideFromOverview)}
|
<!-- TODO: doesn't work if first theme is hidden -->
|
||||||
<!-- TODO: doesn't work if first theme is hidden -->
|
{#if theme === firstTheme && !isCustom && $search !== "" && $search !== undefined}
|
||||||
{#if theme === firstTheme && !isCustom && $search !== "" && $search !== undefined}
|
<ThemeButton
|
||||||
<ThemeButton
|
{theme}
|
||||||
{theme}
|
{isCustom}
|
||||||
{isCustom}
|
userDetails={state.osmConnection.userDetails}
|
||||||
userDetails={state.osmConnection.userDetails}
|
{state}
|
||||||
{state}
|
selected={true}
|
||||||
selected={true}
|
/>
|
||||||
/>
|
{:else}
|
||||||
{:else}
|
|
||||||
<ThemeButton {theme} {isCustom} userDetails={state.osmConnection.userDetails} {state} />
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div>
|
|
||||||
{#each filteredThemes as theme (theme.id)}
|
|
||||||
{#if theme !== undefined && !(hideThemes && theme?.hideFromOverview)}
|
|
||||||
<ThemeButton {theme} {isCustom} userDetails={state.osmConnection.userDetails} {state} />
|
<ThemeButton {theme} {isCustom} userDetails={state.osmConnection.userDetails} {state} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/if}
|
||||||
</div>
|
{/each}
|
||||||
{/if}
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{#if filteredThemes.length === 0}
|
{#if filteredThemes.length === 0}
|
||||||
<NoThemeResultButton {search} />
|
<NoThemeResultButton {search} />
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
import ThemesList from "./ThemesList.svelte"
|
import ThemesList from "./ThemesList.svelte"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
import UserRelatedState from "../../Logic/State/UserRelatedState"
|
import UserRelatedState from "../../Logic/State/UserRelatedState"
|
||||||
|
import Tr from "../Base/Tr.svelte"
|
||||||
|
|
||||||
export let search: UIEventSource<string>
|
export let search: UIEventSource<string>
|
||||||
export let state: UserRelatedState & {
|
export let state: UserRelatedState & {
|
||||||
osmConnection: OsmConnection
|
osmConnection: OsmConnection
|
||||||
}
|
}
|
||||||
export let onMainScreen: boolean = true
|
|
||||||
|
|
||||||
const t = Translations.t.general
|
const t = Translations.t.general
|
||||||
const currentIds: Store<string[]> = state.installedUserThemes
|
const currentIds: Store<string[]> = state.installedUserThemes
|
||||||
|
@ -24,14 +24,15 @@
|
||||||
<ThemesList
|
<ThemesList
|
||||||
{search}
|
{search}
|
||||||
{state}
|
{state}
|
||||||
{onMainScreen}
|
|
||||||
themes={customThemes}
|
themes={customThemes}
|
||||||
isCustom={true}
|
isCustom={true}
|
||||||
hideThemes={false}
|
hideThemes={false}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="title">
|
<svelte:fragment slot="title">
|
||||||
<!-- TODO: Change string to exclude html -->
|
<h3>
|
||||||
{@html t.customThemeIntro.toString()}
|
<Tr t={t.customThemeTitle} />
|
||||||
|
</h3>
|
||||||
|
<Tr t={t.customThemeIntro} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</ThemesList>
|
</ThemesList>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
import { Translation } from "../UI/i18n/Translation"
|
import { Translation } from "../UI/i18n/Translation"
|
||||||
|
|
||||||
export function ariaLabel(htmlElement: Element, t: Translation) {
|
export function ariaLabel(htmlElement: Element, t: Translation) {
|
||||||
let onDestroy: () => void = undefined
|
let destroy: () => void = undefined
|
||||||
|
|
||||||
t.current.map(
|
t.current.map(
|
||||||
(label) => {
|
(label) => {
|
||||||
console.log("Setting arialabel", label, "to", htmlElement)
|
|
||||||
htmlElement.setAttribute("aria-label", label)
|
htmlElement.setAttribute("aria-label", label)
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
(f) => {
|
(f) => {
|
||||||
onDestroy = f
|
destroy = f
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return { destroy }
|
||||||
destroy() {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
17
src/Utils/placeholder.ts
Normal file
17
src/Utils/placeholder.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { Translation } from "../UI/i18n/Translation"
|
||||||
|
|
||||||
|
export function placeholder(htmlElement: HTMLInputElement, t: Translation) {
|
||||||
|
let destroy: () => void = undefined
|
||||||
|
|
||||||
|
t.current.map(
|
||||||
|
(label) => {
|
||||||
|
htmlElement.setAttribute("placeholder", label)
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
(f) => {
|
||||||
|
destroy = f
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return { destroy }
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue