Usersettings: use a collapsable dropdown, introduce dropdown special visualisation
This commit is contained in:
parent
1f9ef8158b
commit
8e98502960
14 changed files with 319 additions and 257 deletions
|
@ -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'><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>",
|
||||
"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'><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>",
|
||||
"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'><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>",
|
||||
"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 '><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>",
|
||||
"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 '><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>"
|
||||
},
|
||||
"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'><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>",
|
||||
"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'><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>",
|
||||
"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'><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>",
|
||||
"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 '><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>",
|
||||
"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 '><a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>"
|
||||
},
|
||||
"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": {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(/>/g, ">")
|
||||
?.replace(/</g, "<")
|
||||
?.replace(/\n/g, ""))
|
||||
}
|
||||
|
||||
usersettingMetaTagging.metaTaggging_for_usersettings({ properties: amendedPrefs.data })
|
||||
|
||||
|
|
|
@ -151,6 +151,7 @@ export default class Constants {
|
|||
"not_found",
|
||||
"note",
|
||||
"party",
|
||||
"pencil",
|
||||
"pin",
|
||||
"resolved",
|
||||
"ring",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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(/>/g, ">")
|
||||
?.replace(/</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>
|
|
@ -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="" />
|
||||
|
|
27
src/UI/Popup/GroupedView.svelte
Normal file
27
src/UI/Popup/GroupedView.svelte
Normal 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>
|
|
@ -271,6 +271,7 @@
|
|||
feedback.setData(undefined)
|
||||
}
|
||||
tags.ping()
|
||||
dispatch("saved", { config, applied: selectedTags })
|
||||
return
|
||||
}
|
||||
dispatch("saved", { config, applied: selectedTags })
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue