Usersettings: use a collapsable dropdown, introduce dropdown special visualisation

This commit is contained in:
Pieter Vander Vennet 2024-08-10 12:09:55 +02:00
parent 1f9ef8158b
commit 8e98502960
14 changed files with 319 additions and 257 deletions

View file

@ -34,11 +34,187 @@
"lineRendering": null,
"tagRenderings": [
{
"id": "profile",
"id": "profile-group",
"render": {
"*": "{user_profile()}"
"special": {
"type": "group",
"header": "profile-title",
"labels": "profile-content"
}
}
},
{
"id": "profile-title",
"labels": ["hidden"],
"icon": "user_circle",
"render": {
"*": "<h3>{_name}</h3>"
},
"mappings": [
{
"if": "_img!=",
"#": "ignore-image-in-then",
"then": {
"*": "<div class='flex'><img src={_img} class='w-12 h-12 rounded-full' style='margin-right: 0.75rem'/> <h3>{_name}</h3></div>"
}
}
]
},
{
"id": "profile-description",
"labels": [
"profile-content","hidden"
],
"render": {
"*": "{_description_html}"
},
"mappings": [
{
"if": "_description=",
"then": {
"special": {
"type": "link",
"class": "button link-no-underline",
"icon": "pencil",
"href": "{_backend}/profile/edit",
"text": {
"ca": "Afegeix una descripció del perfil",
"cs": "Přidat popis profilu",
"de": "Profilbeschreibung hinzufügen",
"en": "Add a profile description",
"fi": "Lisää profiilin kuvaus",
"nb_NO": "Legg til profilbeskrivelse",
"nl": "Voeg een profielbeschrijving toe",
"pl": "Dodaj opis profilu",
"pt": "Adicionar uma descrição do perfil",
"zh_Hant": "新增個人檔敘述"
}
}
}
}
]
},
{
"id": "edit-profile",
"labels": [
"profile-content","hidden"
],
"condition": "_description!=",
"render": {
"special": {
"type": "link",
"href": "{_backend}/profile/edit",
"class": "button link-no-underline",
"icon": "pencil",
"text": {
"ca": "Editeu la descripció del vostre perfil",
"cs": "Úprava popisu vašeho profilu",
"da": "Ret din profilbeskrivelse",
"de": "Eigene Profilbeschreibung bearbeiten",
"en": "Edit your profile description",
"fi": "Muokkaa profiilin kuvausta",
"fr": "Modifier ton profil",
"nl": "Pas je profielbeschrijving aan",
"pl": "Edytuj opis swojego profilu",
"pt": "Editar a descrição do seu perfil",
"zh_Hant": "編輯你的個人檔敘述"
}
}
}
},
{
"id": "verified-mastodon",
"labels": [
"profile-content","hidden"
],
"mappings": [
{
"if": "_mastodon_link~*",
"then": {
"en": "A link to your Mastodon-profile has been been found: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"de": "Es wurde ein Link zu deinem Mastodon-Profil gefunden: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"nl": "Een link naar je Mastodon-profiel werd gevonden: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"fr": "Un lien vers votre profil Mastodon a été trouvé : <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"ca": "S'ha trobat un enllaç al vostre perfil de Mastodon: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"cs": "Byl nalezen odkaz na váš profil Mastodon: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>"
},
"icon": "mastodon"
},
{
"if": "_mastodon_candidate~*",
"then": {
"en": "We found a link to what looks to be a mastodon account, but it is unverified. <a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Edit your profile description</a> and place the following there: <span class='code'>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt",
"de": "Wir haben einen Link gefunden, der aussieht wie ein Mastodon-Konto, aber nicht verifiziert ist. <a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Bearbeiten Sie Ihre Profilbeschreibung</a> und fügen Sie dort Folgendes ein: <span class='code'>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt",
"nl": "Je profielbeschrijving bevat een link die vermoedelijk naar je Mastodon gaat, maar deze link is niet verifieerdbaar voor Mastodon.<a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Pas je profielbeschrijving aan</a> en plaats er de volgende code: <span class='code'>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt",
"ca": "Hem trobat un enllaç al que sembla ser un compte de mastodon, però no està verificat. <a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Editeu la descripció del vostre perfil</a> i col·loqueu-hi el següent: <span class='code '>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt",
"cs": "Našli jsme odkaz na to, co vypadá jako účet mastodon, ale je neověřený. <a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Upravte popis svého profilu</a> a umístěte tam následující: <span class='code '>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt"
},
"icon": "invalid"
}
]
},
{
"id": "cscount-thanks",
"labels": [
"profile-content","hidden"
],
"mappings": [
{
"if": "_csCount>0",
"then": {
"en": "You have made changes on {_csCount} different occasions! That is awesome!",
"de": "Sie haben bei {_csCount} verschiedenen Gelegenheiten Änderungen vorgenommen! Das ist großartig!",
"ca": "Has fet {_csCount} en diferents ocasions! Això és sorprenent!",
"fr": "Vous avez fait {_csCount} modifications ! C'est génial !",
"pt": "Você fez alterações em {_csCount} ocasiões diferentes! Isso é incrível!",
"nl": "Je hebt {_csCount} verschillende keren bijgedragen! Dat is indrukwekkend!",
"da": "Du har lavet ændringer ved {_csCount} forskellige begivenheder! Det er fantastisk!",
"es": "Has hecho cambios en {_csCount} ocasiones diferentes. ¡Es alucinante!",
"cs": "Změny jste provedli při {_csCount} různých příležitostech! To je úžasné!"
},
"icon": "party"
}
]
},
{
"id": "translation-thanks",
"labels": [
"profile-content","hidden"
],
"mappings": [
{
"if": "_translation_contributions>0",
"then": {
"en": "You have contributed to translating MapComplete with {_translation_contributions} commits! That's awesome!",
"nl": "Je hebt MapComplete helpen vertalen met {_translation_contributions} commits! Dat is fantastisch! Bedankt hiervoor!",
"de": "Du hast mit {_translation_contributions} Änderungen zur Übersetzung von MapComplete beigetragen! Das ist großartig!"
},
"icon": "party"
}
]
},
{
"id": "contributor-thanks",
"labels": [
"profile-content","hidden"
],
"mappings": [
{
"if": "_code_contributions>0",
"then": {
"en": "You have contributed code to MapComplete with {_code_contributions} commits! That's awesome!",
"de": "Sie haben Code zu MapComplete mit {_code_contributions} Commits beigetragen! Das ist großartig!",
"nl": "Je hebt mee geprogrammeerd aan MapComplete met {_code_contributions} commits! Das supercool van je! Bedankt hiervoor!",
"ca": "Heu aportat codi a MapComplete amb {_code_contributions} commits! Això és increïble!",
"cs": "Přispěli jste do MapComplete kódem s {_code_contributions} revizemi! To je úžasné!",
"da": "Du har bidraget kode til MapComplete med {_code_contributions} commits! Det er fantastisk!"
},
"icon": "party",
"hideInAnswer": true
}
]
},
{
"id": "language_picker",
"render": {
@ -624,7 +800,6 @@
"href": "data:application/json,{mangroveidentity}",
"download": "mangrove_private_key_{_name}",
"class": "button",
"text": {
"en": "Download the private key for your Mangrove Account",
"de": "Laden Sie den privaten Schlüssel für Ihr Mangrove-Konto herunter",
@ -824,86 +999,6 @@
}
}
},
{
"id": "verified-mastodon",
"mappings": [
{
"if": "_mastodon_link~*",
"then": {
"en": "A link to your Mastodon-profile has been been found: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"de": "Es wurde ein Link zu deinem Mastodon-Profil gefunden: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"nl": "Een link naar je Mastodon-profiel werd gevonden: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"fr": "Un lien vers votre profil Mastodon a été trouvé : <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"ca": "S'ha trobat un enllaç al vostre perfil de Mastodon: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>",
"cs": "Byl nalezen odkaz na váš profil Mastodon: <a href='{_mastodon_link}' target='_blank' rel='noopener'>{_mastodon_link}</a>"
},
"icon": "mastodon"
},
{
"if": "_mastodon_candidate~*",
"then": {
"en": "We found a link to what looks to be a mastodon account, but it is unverified. <a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Edit your profile description</a> and place the following there: <span class='code'>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt",
"de": "Wir haben einen Link gefunden, der aussieht wie ein Mastodon-Konto, aber nicht verifiziert ist. <a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Bearbeiten Sie Ihre Profilbeschreibung</a> und fügen Sie dort Folgendes ein: <span class='code'>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt",
"nl": "Je profielbeschrijving bevat een link die vermoedelijk naar je Mastodon gaat, maar deze link is niet verifieerdbaar voor Mastodon.<a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Pas je profielbeschrijving aan</a> en plaats er de volgende code: <span class='code'>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt",
"ca": "Hem trobat un enllaç al que sembla ser un compte de mastodon, però no està verificat. <a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Editeu la descripció del vostre perfil</a> i col·loqueu-hi el següent: <span class='code '>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt",
"cs": "Našli jsme odkaz na to, co vypadá jako účet mastodon, ale je neověřený. <a href='https://www.openstreetmap.org/profile/edit' target='_blank' rel='noopener'>Upravte popis svého profilu</a> a umístěte tam následující: <span class='code '>&lta href=\"{_mastodon_candidate}\" rel=\"me\"&gtMastodon&lt/a&gt"
},
"icon": "invalid"
}
]
},
{
"id": "cscount-thanks",
"mappings": [
{
"if": "_csCount>0",
"then": {
"en": "You have made changes on {_csCount} different occasions! That is awesome!",
"de": "Sie haben bei {_csCount} verschiedenen Gelegenheiten Änderungen vorgenommen! Das ist großartig!",
"ca": "Has fet {_csCount} en diferents ocasions! Això és sorprenent!",
"fr": "Vous avez fait {_csCount} modifications ! C'est génial !",
"pt": "Você fez alterações em {_csCount} ocasiões diferentes! Isso é incrível!",
"nl": "Je hebt {_csCount} verschillende keren bijgedragen! Dat is indrukwekkend!",
"da": "Du har lavet ændringer ved {_csCount} forskellige begivenheder! Det er fantastisk!",
"es": "Has hecho cambios en {_csCount} ocasiones diferentes. ¡Es alucinante!",
"cs": "Změny jste provedli při {_csCount} různých příležitostech! To je úžasné!"
},
"icon": "party"
}
]
},
{
"id": "translation-thanks",
"mappings": [
{
"if": "_translation_contributions>0",
"then": {
"en": "You have contributed to translating MapComplete with {_translation_contributions} commits! That's awesome!",
"nl": "Je hebt MapComplete helpen vertalen met {_translation_contributions} commits! Dat is fantastisch! Bedankt hiervoor!",
"de": "Du hast mit {_translation_contributions} Änderungen zur Übersetzung von MapComplete beigetragen! Das ist großartig!"
},
"icon": "party"
}
]
},
{
"id": "contributor-thanks",
"mappings": [
{
"if": "_code_contributions>0",
"then": {
"en": "You have contributed code to MapComplete with {_code_contributions} commits! That's awesome!",
"de": "Sie haben Code zu MapComplete mit {_code_contributions} Commits beigetragen! Das ist großartig!",
"nl": "Je hebt mee geprogrammeerd aan MapComplete met {_code_contributions} commits! Das supercool van je! Bedankt hiervoor!",
"ca": "Heu aportat codi a MapComplete amb {_code_contributions} commits! Això és increïble!",
"cs": "Přispěli jste do MapComplete kódem s {_code_contributions} revizemi! To je úžasné!",
"da": "Du har bidraget kode til MapComplete med {_code_contributions} commits! Det er fantastisk!"
},
"icon": "party",
"hideInAnswer": true
}
]
},
{
"id": "debug-title",
"render": {

View file

@ -1221,14 +1221,14 @@ video {
height: 6rem;
}
.h-screen {
height: 100vh;
}
.h-full {
height: 100%;
}
.h-screen {
height: 100vh;
}
.h-fit {
height: -webkit-fit-content;
height: -moz-fit-content;
@ -2043,6 +2043,10 @@ video {
column-gap: 0px;
}
.gap-x-4 {
column-gap: 1rem;
}
.gap-y-8 {
row-gap: 2rem;
}
@ -2336,10 +2340,6 @@ video {
border-radius: 1rem;
}
.rounded-md {
border-radius: 0.375rem;
}
.rounded-lg {
border-radius: 0.5rem;
}
@ -2558,11 +2558,6 @@ video {
border-color: rgb(209 213 219 / var(--tw-border-opacity));
}
.border-gray-600 {
--tw-border-opacity: 1;
border-color: rgb(75 85 99 / var(--tw-border-opacity));
}
.border-gray-800 {
--tw-border-opacity: 1;
border-color: rgb(31 41 55 / var(--tw-border-opacity));
@ -4405,10 +4400,12 @@ video {
--interactive-foreground: black;
--interactive-contrast: #ff00ff;
--button-background: #282828;
--button-background-hover: #686868;
--button-background-hover: #484848;
--button-primary-background-hover: #353535;
--button-foreground: white;
--button-border-color: #F7F7F7;
--disabled: #DBDBDB;
--disabled: #B8B8B8;
--disabled-font: #B8B8B8;
/**
* Base colour of interactive elements, mainly the 'subtle button'
* @deprecated
@ -4592,19 +4589,19 @@ button.primary, .button.primary {
}
button.primary:hover:not(.disabled), .button.primary:hover:not(.disabled) {
background-color: var(--button-background-hover);
background-color: var(--button-primary-background-hover);
}
button.disabled {
border-color: var(--disabled);
color: var(--disabled);
border-color: var(--disabled-font);
color: var(--disabled-font);
cursor: unset;
}
button.disabled svg path {
transition: all 200ms;
fill: var(--disabled);
stroke: var(--disabled);
fill: var(--disabled-font);
stroke: var(--disabled-font);
}
button.primary.disabled, .button.primary.disabled {

View file

@ -18,6 +18,7 @@ import Constants from "../../Models/Constants"
import { QueryParameters } from "../Web/QueryParameters"
import { ThemeMetaTagging } from "./UserSettingsMetaTagging"
import { MapProperties } from "../../Models/MapProperties"
import Showdown from "showdown"
/**
* The part of the state which keeps track of user-related stuff, e.g. the OSM-connection,
@ -390,6 +391,13 @@ export default class UserRelatedState {
for (const k in userDetails) {
amendedPrefs.data["_" + k] = "" + userDetails[k]
}
if(userDetails.description){
amendedPrefs.data["_description_html"] = Utils.purify(new Showdown.Converter()
.makeHtml(userDetails.description)
?.replace(/&gt;/g, ">")
?.replace(/&lt;/g, "<")
?.replace(/\n/g, ""))
}
usersettingMetaTagging.metaTaggging_for_usersettings({ properties: amendedPrefs.data })

View file

@ -151,6 +151,7 @@ export default class Constants {
"not_found",
"note",
"party",
"pencil",
"pin",
"resolved",
"ring",

View file

@ -21,7 +21,7 @@
class={$classnames}
>
{#if $icon}
<Icon clss="w-8 h-8" icon={$icon}/>
<Icon clss="w-4 h-4" icon={$icon}/>
{/if}
{@html $text}
</a>

View file

@ -17,31 +17,44 @@
export let highlightedRendering: UIEventSource<string> = undefined
export let tags: UIEventSource<Record<string, string>> = state?.featureProperties?.getStore(
selectedElement.properties.id
selectedElement.properties.id,
)
let isAddNew = tags.mapD(
(t) => t?.id?.startsWith(LastClickFeatureSource.newPointElementId) ?? false
(t) => t?.id?.startsWith(LastClickFeatureSource.newPointElementId) ?? false,
)
export let layer: LayerConfig
export let mustMatchLabels: Set<string> | undefined = undefined
export let dontMatchLabels: Set<string> | undefined = new Set(["hidden"])
let _metatags: Record<string, string>
if (state?.userRelatedState?.preferencesAsTags) {
onDestroy(
state.userRelatedState.preferencesAsTags.addCallbackAndRun((tags) => {
_metatags = tags
})
}),
)
}
let knownTagRenderings: Store<TagRenderingConfig[]> = tags.mapD((tgs) =>
layer?.tagRenderings?.filter(
(config) =>
(config.condition?.matchesProperties(tgs) ?? true) &&
(config.metacondition?.matchesProperties({ ...tgs, ..._metatags }) ?? true) &&
config.IsKnown(tgs)
)
(config) => {
if (mustMatchLabels !== undefined) {
if (!mustMatchLabels.has(config.id) && !config?.labels?.some(l => mustMatchLabels.has(l))) {
return false
}
} else if (dontMatchLabels) {
if (dontMatchLabels.has(config.id) || config?.labels?.some(l => dontMatchLabels.has(l))) {
return false
}
}
if (!config.IsKnown(tgs)) {
return false
}
return (config.condition?.matchesProperties(tgs) ?? true) &&
(config.metacondition?.matchesProperties({ ...tgs, ..._metatags }) ?? true)
},
),
)
</script>

View file

@ -14,8 +14,8 @@
import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini"
import Location_refused from "../../assets/svg/Location_refused.svelte"
import Location from "../../assets/svg/Location.svelte"
import ChevronDoubleLeft from "@babeard/svelte-heroicons/mini/ChevronDoubleLeft"
import Constants from "../../Models/Constants"
import ChevronDoubleLeft from "@babeard/svelte-heroicons/solid/ChevronDoubleLeft"
import GeolocationIndicator from "./GeolocationIndicator.svelte"
/**
* The theme introduction panel
@ -27,9 +27,11 @@
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
let searchEnabled = false
let geopermission: Store<GeolocationPermissionState> =
state.geolocation.geolocationState.permission
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
let geolocation = state.geolocation.geolocationState
let geopermission: Store<GeolocationPermissionState> = geolocation.permission
let currentGPSLocation = geolocation.currentGPSLocation
let gpsExplanation = geolocation.gpsStateExplanation
let gpsAvailable = geolocation.gpsAvailable
function jumpToCurrentLocation() {
const glstate = state.geolocation.geolocationState
@ -75,38 +77,12 @@
<div class="flex w-full flex-wrap sm:flex-nowrap">
<If condition={state.featureSwitches.featureSwitchGeolocation}>
{#if $currentGPSLocation !== undefined || $geopermission === "prompt"}
<button class="flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
<Location class="h-8 w-8" />
<Tr t={Translations.t.general.openTheMapAtGeolocation} />
</button>
<!-- No geolocation granted - we don't show the button -->
{:else if $geopermission === "requested"}
<button
class="disabled flex w-full items-center gap-x-2"
on:click={jumpToCurrentLocation}
>
<!-- Even though disabled, when clicking we request the location again in case the contributor dismissed the location popup -->
<Location
class="h-8 w-8"
style="animation: 3s linear 0s infinite normal none running spin;"
/>
<Tr t={Translations.t.general.waitingForGeopermission} />
</button>
{:else if $geopermission === "denied"}
<button class="disabled flex w-full items-center gap-x-2">
<Location_refused class="h-8 w-8" />
<Tr t={Translations.t.general.geopermissionDenied} />
</button>
{:else}
<button class="disabled flex w-full items-center gap-x-2">
<Location
class="h-8 w-8"
style="animation: 3s linear 0s infinite normal none running spin;"
/>
<Tr t={Translations.t.general.waitingForLocation} />
</button>
{/if}
<button disabled={!$gpsAvailable} class:disabled={!$gpsAvailable} class="flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
<GeolocationIndicator {state} />
<Tr t={$gpsExplanation} />
</button>
</If>
<If condition={state.featureSwitches.featureSwitchSearch}>

View file

@ -1,55 +0,0 @@
<script lang="ts">
import UserDetails, { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { UIEventSource } from "../../Logic/UIEventSource"
import { PencilAltIcon, UserCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
import { onDestroy } from "svelte"
import Showdown from "showdown"
import FromHtml from "../Base/FromHtml.svelte"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations.js"
import Pencil from "@babeard/svelte-heroicons/solid/Pencil"
/**
* This panel shows information about the logged-in user, showing account name, profile pick, description and an edit-button
*/
export let osmConnection: OsmConnection
let userdetails: UIEventSource<UserDetails> = osmConnection.userDetails
let description: string
onDestroy(
userdetails.addCallbackAndRunD((userdetails) => {
description = new Showdown.Converter()
.makeHtml(userdetails.description)
?.replace(/&gt;/g, ">")
?.replace(/&lt;/g, "<")
})
)
</script>
<div class="link-underline m-1 flex rounded-md border border-dashed border-gray-600 p-1">
{#if $userdetails.img}
<img src={$userdetails.img} class="m-4 h-12 w-12 rounded-full" />
{:else}
<UserCircleIcon class="h-12 w-12" />
{/if}
<div class="flex flex-col">
<h3>{$userdetails.name}</h3>
{#if description}
<FromHtml src={description} />
<a
href={osmConnection.Backend() + "/profile/edit"}
target="_blank"
rel="noopener"
class="link-no-underline flex items-center self-end"
>
<PencilAltIcon slot="image" class="h-8 w-8 p-2" />
<Tr t={Translations.t.userinfo.editDescription} />
</a>
{:else}
<Tr t={Translations.t.userinfo.noDescription} />
<a href={osmConnection.Backend() + "/profile/edit"} target="_blank" class="flex items-center">
<Pencil class="h-8 w-8 p-2" />
<Tr t={Translations.t.userinfo.noDescriptionCallToAction} />
</a>
{/if}
</div>
</div>

View file

@ -22,7 +22,7 @@
import Brick_wall_square from "../../assets/svg/Brick_wall_square.svelte"
import Brick_wall_round from "../../assets/svg/Brick_wall_round.svelte"
import Gps_arrow from "../../assets/svg/Gps_arrow.svelte"
import { HeartIcon, WifiIcon } from "@babeard/svelte-heroicons/solid"
import { HeartIcon, PencilIcon, WifiIcon } from "@babeard/svelte-heroicons/solid"
import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline"
import Confirm from "../../assets/svg/Confirm.svelte"
import Not_found from "../../assets/svg/Not_found.svelte"
@ -37,7 +37,7 @@
import Cross_bottom_right from "../../assets/svg/Cross_bottom_right.svelte"
import { Utils } from "../../Utils"
import Gear from "../../assets/svg/Gear.svelte"
import { DesktopComputerIcon } from "@rgossiaux/svelte-heroicons/solid"
import { DesktopComputerIcon, UserCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
import Relocation from "../../assets/svg/Relocation.svelte"
/**
@ -142,9 +142,13 @@
<DesktopComputerIcon class={"m-0 " + clss} {color} />
{:else if icon === "relocation"}
<Relocation class={clss} {color} />
{:else if icon === "pencil"}
<PencilIcon class={clss} {color} />
{:else if icon === "user_circle"}
<UserCircleIcon class={clss} {color} />
{:else if Utils.isEmoji(icon)}
<span style={`font-size: ${emojiHeight}px; line-height: ${emojiHeight}px`}>
{icon}
{icon}
</span>
{:else}
<img class={clss ?? "h-full w-full"} src={icon} aria-hidden="true" alt="" />

View file

@ -0,0 +1,27 @@
<script lang="ts">
import type { SpecialVisualizationState } from "../SpecialVisualization"
import type { Feature } from "geojson"
import { UIEventSource } from "../../Logic/UIEventSource"
import type { OsmTags } from "../../Models/OsmFeature"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
import SelectedElementView from "../BigComponents/SelectedElementView.svelte"
import TagRenderingAnswer from "./TagRendering/TagRenderingAnswer.svelte"
export let state: SpecialVisualizationState
export let selectedElement: Feature
export let tags: UIEventSource<OsmTags>
export let labels: string[]
export let header: string
export let layer: LayerConfig
let headerTr = layer.tagRenderings.find(tr => tr.id === header)
</script>
<AccordionSingle>
<div slot="header">
<TagRenderingAnswer {tags} {layer} config={headerTr} {state} {selectedElement} />
</div>
<SelectedElementView mustMatchLabels={new Set(labels)} {state} {layer} {tags} {selectedElement}/>
</AccordionSingle>

View file

@ -271,6 +271,7 @@
feedback.setData(undefined)
}
tags.ping()
dispatch("saved", { config, applied: selectedTags })
return
}
dispatch("saved", { config, applied: selectedTags })

View file

@ -40,7 +40,6 @@ import { Feature, GeoJsonProperties } from "geojson"
import { GeoOperations } from "../Logic/GeoOperations"
import CreateNewNote from "./Popup/Notes/CreateNewNote.svelte"
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
import UserProfile from "./BigComponents/UserProfile.svelte"
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"
import { ExportAsGpxViz } from "./Popup/ExportAsGpxViz"
@ -102,6 +101,7 @@ import CloseNoteButton from "./Popup/Notes/CloseNoteButton.svelte"
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
import QrCode from "./Popup/QrCode.svelte"
import ClearCaches from "./Popup/ClearCaches.svelte"
import GroupedView from "./Popup/GroupedView.svelte"
class NearbyImageVis implements SpecialVisualization {
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
@ -425,17 +425,6 @@ export default class SpecialVisualizations {
}).SetClass("w-full h-full overflow-auto")
},
},
{
funcName: "user_profile",
args: [],
docs: "A component showing information about the currently logged in user (username, profile description, profile picture + link to edit them). Mostly meant to be used in the 'user-settings'",
constr(state: SpecialVisualizationState): BaseUIElement {
return new SvelteUIElement(UserProfile, {
osmConnection: state.osmConnection,
})
},
},
{
funcName: "language_picker",
args: [],
@ -1340,6 +1329,7 @@ export default class SpecialVisualizations {
download: tagSource.map((tags) => Utils.SubstituteKeys(download, tags)),
ariaLabel: tagSource.map((tags) => Utils.SubstituteKeys(ariaLabel, tags)),
newTab: new ImmutableStore(newTab),
icon: tagSource.map((tags) => Utils.SubstituteKeys(icon, tags))
}).setSpan()
},
},
@ -2006,6 +1996,27 @@ export default class SpecialVisualizations {
],
constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, argument: string[], feature: Feature, layer: LayerConfig): SvelteUIElement {
return new SvelteUIElement<any, any, any>(ClearCaches, {msg: argument[0] ?? "Clear local caches"})
},
},
{
funcName: "group",
docs: "A collapsable group (accordion)",
args: [
{
name: "header",
doc: "The _identifier_ of a single tagRendering. This will be used as header"
},
{
name: "labels",
doc: "A `;`-separated list of either identifiers or label names. All tagRenderings matching this value will be shown in the accordion"
}
],
constr(state: SpecialVisualizationState, tags: UIEventSource<Record<string, string>>, argument: string[], selectedElement: Feature, layer: LayerConfig): SvelteUIElement {
const [header, labelsStr] = argument
const labels = labelsStr.split(";").map(x => x.trim())
return new SvelteUIElement<any, any, any>(GroupedView, {
state, tags, selectedElement, layer, header, labels
})
}
}
]

View file

@ -54,7 +54,7 @@
import ImageOperations from "./Image/ImageOperations.svelte"
import VisualFeedbackPanel from "./BigComponents/VisualFeedbackPanel.svelte"
import { Orientation } from "../Sensors/Orientation"
import GeolocationControl from "./BigComponents/GeolocationControl.svelte"
import GeolocationIndicator from "./BigComponents/GeolocationIndicator.svelte"
import Compass_arrow from "../assets/svg/Compass_arrow.svelte"
import ReverseGeocoding from "./BigComponents/ReverseGeocoding.svelte"
import FilterPanel from "./BigComponents/FilterPanel.svelte"
@ -209,30 +209,7 @@
let addNewFeatureMode = state.userRelatedState.addNewFeatureMode
let gpsAvailable = state.geolocation.geolocationState.gpsAvailable
let gpsButtonAriaLabel = gpsAvailable.map(available => {
if (!available) {
return Translations.t.general.labels.locationNotAvailable
}
if (state.geolocation.geolocationState.permission.data === "denied") {
return Translations.t.general.geopermissionDenied
}
if (state.geolocation.geolocationState.permission.data === "requested") {
return Translations.t.general.waitingForGeopermission
}
if (!state.geolocation.geolocationState.allowMoving.data) {
return Translations.t.general.visualFeedback.islocked
}
if (state.geolocation.geolocationState.currentGPSLocation.data === undefined) {
return Translations.t.general.waitingForLocation
}
return Translations.t.general.labels.jumpToLocation
}, [state.geolocation.geolocationState.allowMoving, state.geolocation.geolocationState.permission])
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation
</script>
<main>
@ -435,7 +412,7 @@
on:click={() => state.geolocationControl.handleClick()}
on:keydown={forwardEventToMap}
>
<GeolocationControl {state} />
<GeolocationIndicator {state} />
<!-- h-8 w-8 + p-0.5 sm:p-1 + 2px border => 9 sm: 10 in total-->
</MapControlButton>
{#if $compassLoaded}
@ -501,14 +478,15 @@
{/if}
{/if}
<!-- Image preview -->
<If condition={state.previewedImage.map((i) => i !== undefined)}>
<FloatOver on:close={() => state.previewedImage.setData(undefined)}>
<ImageOperations image={$previewedImage} />
</FloatOver>
</If>
<!-- big theme menu -->
<If condition={state.guistate.themeIsOpened}>
<!-- Theme menu -->
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
<span slot="close-button"><!-- Disable the close button --></span>
<TabbedGroup
@ -524,7 +502,6 @@
<div class="flex" slot="title0">
<Marker icons={layout.icon} size="h-4 w-4" />
<Tr t={layout.title} />
</div>
@ -561,14 +538,15 @@
</FloatOver>
</If>
<!-- Filterpane -->
<If condition={state.guistate.filtersPanelIsOpened}>
<FloatOver on:close={() => state.guistate.filtersPanelIsOpened.setData(false)}>
<FilterPanel {state} />
</FloatOver>
</If>
<!-- background layer selector -->
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
<!-- background layer selector -->
<FloatOver
on:close={() => {
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
@ -584,8 +562,8 @@
</FloatOver>
</IfHidden>
<!-- Menu page -->
<If condition={state.guistate.menuIsOpened}>
<!-- Menu page -->
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
<span slot="close-button"><!-- Hide the default close button --></span>
<TabbedGroup
@ -656,6 +634,7 @@
</FloatOver>
</If>
<!-- Privacy policy -->
<If condition={state.guistate.privacyPanelIsOpened}>
<FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
<div class="flex h-full flex-col overflow-hidden">
@ -670,6 +649,7 @@
</FloatOver>
</If>
<!-- Attribution, copyright and about MapComplete (no menu case) -->
<If condition={state.guistate.copyrightPanelIsOpened}>
<FloatOver on:close={() => state.guistate.copyrightPanelIsOpened.setData(false)}>
<div class="flex h-full flex-col overflow-hidden">
@ -687,6 +667,7 @@
</FloatOver>
</If>
<!-- Community index -->
<If condition={state.guistate.communityIndexPanelIsOpened}>
<FloatOver on:close={() => state.guistate.communityIndexPanelIsOpened.setData(false)}>
<div class="flex h-full flex-col overflow-hidden">

View file

@ -39,10 +39,13 @@
--interactive-contrast: #ff00ff;
--button-background: #282828;
--button-background-hover: #686868;
--button-background-hover: #484848;
--button-primary-background-hover: #353535;
--button-foreground: white;
--button-border-color: #F7F7F7;
--disabled: #DBDBDB;
--disabled: #B8B8B8;
--disabled-font: #B8B8B8;
/**
* Base colour of interactive elements, mainly the 'subtle button'
@ -232,20 +235,20 @@ button.primary, .button.primary {
}
button.primary:hover:not(.disabled), .button.primary:hover:not(.disabled) {
background-color: var(--button-background-hover);
background-color: var(--button-primary-background-hover);
}
button.disabled {
border-color: var(--disabled);
color: var(--disabled);
border-color: var(--disabled-font);
color: var(--disabled-font);
cursor: unset;
}
button.disabled svg path {
transition: all 200ms;
}
button.disabled svg path {
fill: var(--disabled);
stroke: var(--disabled);
fill: var(--disabled-font);
stroke: var(--disabled-font);
}