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,
|
"lineRendering": null,
|
||||||
"tagRenderings": [
|
"tagRenderings": [
|
||||||
{
|
{
|
||||||
"id": "profile",
|
"id": "profile-group",
|
||||||
"render": {
|
"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",
|
"id": "language_picker",
|
||||||
"render": {
|
"render": {
|
||||||
|
@ -624,7 +800,6 @@
|
||||||
"href": "data:application/json,{mangroveidentity}",
|
"href": "data:application/json,{mangroveidentity}",
|
||||||
"download": "mangrove_private_key_{_name}",
|
"download": "mangrove_private_key_{_name}",
|
||||||
"class": "button",
|
"class": "button",
|
||||||
|
|
||||||
"text": {
|
"text": {
|
||||||
"en": "Download the private key for your Mangrove Account",
|
"en": "Download the private key for your Mangrove Account",
|
||||||
"de": "Laden Sie den privaten Schlüssel für Ihr Mangrove-Konto herunter",
|
"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",
|
"id": "debug-title",
|
||||||
"render": {
|
"render": {
|
||||||
|
|
|
@ -1221,14 +1221,14 @@ video {
|
||||||
height: 6rem;
|
height: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h-screen {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-full {
|
.h-full {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-screen {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
.h-fit {
|
.h-fit {
|
||||||
height: -webkit-fit-content;
|
height: -webkit-fit-content;
|
||||||
height: -moz-fit-content;
|
height: -moz-fit-content;
|
||||||
|
@ -2043,6 +2043,10 @@ video {
|
||||||
column-gap: 0px;
|
column-gap: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gap-x-4 {
|
||||||
|
column-gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.gap-y-8 {
|
.gap-y-8 {
|
||||||
row-gap: 2rem;
|
row-gap: 2rem;
|
||||||
}
|
}
|
||||||
|
@ -2336,10 +2340,6 @@ video {
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rounded-md {
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rounded-lg {
|
.rounded-lg {
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
@ -2558,11 +2558,6 @@ video {
|
||||||
border-color: rgb(209 213 219 / var(--tw-border-opacity));
|
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 {
|
.border-gray-800 {
|
||||||
--tw-border-opacity: 1;
|
--tw-border-opacity: 1;
|
||||||
border-color: rgb(31 41 55 / var(--tw-border-opacity));
|
border-color: rgb(31 41 55 / var(--tw-border-opacity));
|
||||||
|
@ -4405,10 +4400,12 @@ video {
|
||||||
--interactive-foreground: black;
|
--interactive-foreground: black;
|
||||||
--interactive-contrast: #ff00ff;
|
--interactive-contrast: #ff00ff;
|
||||||
--button-background: #282828;
|
--button-background: #282828;
|
||||||
--button-background-hover: #686868;
|
--button-background-hover: #484848;
|
||||||
|
--button-primary-background-hover: #353535;
|
||||||
--button-foreground: white;
|
--button-foreground: white;
|
||||||
--button-border-color: #F7F7F7;
|
--button-border-color: #F7F7F7;
|
||||||
--disabled: #DBDBDB;
|
--disabled: #B8B8B8;
|
||||||
|
--disabled-font: #B8B8B8;
|
||||||
/**
|
/**
|
||||||
* Base colour of interactive elements, mainly the 'subtle button'
|
* Base colour of interactive elements, mainly the 'subtle button'
|
||||||
* @deprecated
|
* @deprecated
|
||||||
|
@ -4592,19 +4589,19 @@ button.primary, .button.primary {
|
||||||
}
|
}
|
||||||
|
|
||||||
button.primary:hover:not(.disabled), .button.primary:hover:not(.disabled) {
|
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 {
|
button.disabled {
|
||||||
border-color: var(--disabled);
|
border-color: var(--disabled-font);
|
||||||
color: var(--disabled);
|
color: var(--disabled-font);
|
||||||
cursor: unset;
|
cursor: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.disabled svg path {
|
button.disabled svg path {
|
||||||
transition: all 200ms;
|
transition: all 200ms;
|
||||||
fill: var(--disabled);
|
fill: var(--disabled-font);
|
||||||
stroke: var(--disabled);
|
stroke: var(--disabled-font);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.primary.disabled, .button.primary.disabled {
|
button.primary.disabled, .button.primary.disabled {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import Constants from "../../Models/Constants"
|
||||||
import { QueryParameters } from "../Web/QueryParameters"
|
import { QueryParameters } from "../Web/QueryParameters"
|
||||||
import { ThemeMetaTagging } from "./UserSettingsMetaTagging"
|
import { ThemeMetaTagging } from "./UserSettingsMetaTagging"
|
||||||
import { MapProperties } from "../../Models/MapProperties"
|
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,
|
* 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) {
|
for (const k in userDetails) {
|
||||||
amendedPrefs.data["_" + k] = "" + userDetails[k]
|
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 })
|
usersettingMetaTagging.metaTaggging_for_usersettings({ properties: amendedPrefs.data })
|
||||||
|
|
||||||
|
|
|
@ -151,6 +151,7 @@ export default class Constants {
|
||||||
"not_found",
|
"not_found",
|
||||||
"note",
|
"note",
|
||||||
"party",
|
"party",
|
||||||
|
"pencil",
|
||||||
"pin",
|
"pin",
|
||||||
"resolved",
|
"resolved",
|
||||||
"ring",
|
"ring",
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
class={$classnames}
|
class={$classnames}
|
||||||
>
|
>
|
||||||
{#if $icon}
|
{#if $icon}
|
||||||
<Icon clss="w-8 h-8" icon={$icon}/>
|
<Icon clss="w-4 h-4" icon={$icon}/>
|
||||||
{/if}
|
{/if}
|
||||||
{@html $text}
|
{@html $text}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -17,31 +17,44 @@
|
||||||
export let highlightedRendering: UIEventSource<string> = undefined
|
export let highlightedRendering: UIEventSource<string> = undefined
|
||||||
|
|
||||||
export let tags: UIEventSource<Record<string, string>> = state?.featureProperties?.getStore(
|
export let tags: UIEventSource<Record<string, string>> = state?.featureProperties?.getStore(
|
||||||
selectedElement.properties.id
|
selectedElement.properties.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
let isAddNew = tags.mapD(
|
let isAddNew = tags.mapD(
|
||||||
(t) => t?.id?.startsWith(LastClickFeatureSource.newPointElementId) ?? false
|
(t) => t?.id?.startsWith(LastClickFeatureSource.newPointElementId) ?? false,
|
||||||
)
|
)
|
||||||
|
|
||||||
export let layer: LayerConfig
|
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>
|
let _metatags: Record<string, string>
|
||||||
if (state?.userRelatedState?.preferencesAsTags) {
|
if (state?.userRelatedState?.preferencesAsTags) {
|
||||||
onDestroy(
|
onDestroy(
|
||||||
state.userRelatedState.preferencesAsTags.addCallbackAndRun((tags) => {
|
state.userRelatedState.preferencesAsTags.addCallbackAndRun((tags) => {
|
||||||
_metatags = tags
|
_metatags = tags
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let knownTagRenderings: Store<TagRenderingConfig[]> = tags.mapD((tgs) =>
|
let knownTagRenderings: Store<TagRenderingConfig[]> = tags.mapD((tgs) =>
|
||||||
layer?.tagRenderings?.filter(
|
layer?.tagRenderings?.filter(
|
||||||
(config) =>
|
(config) => {
|
||||||
(config.condition?.matchesProperties(tgs) ?? true) &&
|
if (mustMatchLabels !== undefined) {
|
||||||
(config.metacondition?.matchesProperties({ ...tgs, ..._metatags }) ?? true) &&
|
if (!mustMatchLabels.has(config.id) && !config?.labels?.some(l => mustMatchLabels.has(l))) {
|
||||||
config.IsKnown(tgs)
|
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>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini"
|
import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini"
|
||||||
import Location_refused from "../../assets/svg/Location_refused.svelte"
|
import Location_refused from "../../assets/svg/Location_refused.svelte"
|
||||||
import Location from "../../assets/svg/Location.svelte"
|
import Location from "../../assets/svg/Location.svelte"
|
||||||
import ChevronDoubleLeft from "@babeard/svelte-heroicons/mini/ChevronDoubleLeft"
|
import ChevronDoubleLeft from "@babeard/svelte-heroicons/solid/ChevronDoubleLeft"
|
||||||
import Constants from "../../Models/Constants"
|
import GeolocationIndicator from "./GeolocationIndicator.svelte"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The theme introduction panel
|
* The theme introduction panel
|
||||||
|
@ -27,9 +27,11 @@
|
||||||
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
||||||
let searchEnabled = false
|
let searchEnabled = false
|
||||||
|
|
||||||
let geopermission: Store<GeolocationPermissionState> =
|
let geolocation = state.geolocation.geolocationState
|
||||||
state.geolocation.geolocationState.permission
|
let geopermission: Store<GeolocationPermissionState> = geolocation.permission
|
||||||
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
|
let currentGPSLocation = geolocation.currentGPSLocation
|
||||||
|
let gpsExplanation = geolocation.gpsStateExplanation
|
||||||
|
let gpsAvailable = geolocation.gpsAvailable
|
||||||
|
|
||||||
function jumpToCurrentLocation() {
|
function jumpToCurrentLocation() {
|
||||||
const glstate = state.geolocation.geolocationState
|
const glstate = state.geolocation.geolocationState
|
||||||
|
@ -75,38 +77,12 @@
|
||||||
|
|
||||||
<div class="flex w-full flex-wrap sm:flex-nowrap">
|
<div class="flex w-full flex-wrap sm:flex-nowrap">
|
||||||
<If condition={state.featureSwitches.featureSwitchGeolocation}>
|
<If condition={state.featureSwitches.featureSwitchGeolocation}>
|
||||||
{#if $currentGPSLocation !== undefined || $geopermission === "prompt"}
|
|
||||||
<button class="flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
|
<button disabled={!$gpsAvailable} class:disabled={!$gpsAvailable} class="flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
|
||||||
<Location class="h-8 w-8" />
|
|
||||||
<Tr t={Translations.t.general.openTheMapAtGeolocation} />
|
<GeolocationIndicator {state} />
|
||||||
</button>
|
<Tr t={$gpsExplanation} />
|
||||||
<!-- No geolocation granted - we don't show the button -->
|
</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}
|
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
<If condition={state.featureSwitches.featureSwitchSearch}>
|
<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_square from "../../assets/svg/Brick_wall_square.svelte"
|
||||||
import Brick_wall_round from "../../assets/svg/Brick_wall_round.svelte"
|
import Brick_wall_round from "../../assets/svg/Brick_wall_round.svelte"
|
||||||
import Gps_arrow from "../../assets/svg/Gps_arrow.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 { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline"
|
||||||
import Confirm from "../../assets/svg/Confirm.svelte"
|
import Confirm from "../../assets/svg/Confirm.svelte"
|
||||||
import Not_found from "../../assets/svg/Not_found.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 Cross_bottom_right from "../../assets/svg/Cross_bottom_right.svelte"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import Gear from "../../assets/svg/Gear.svelte"
|
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"
|
import Relocation from "../../assets/svg/Relocation.svelte"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,9 +142,13 @@
|
||||||
<DesktopComputerIcon class={"m-0 " + clss} {color} />
|
<DesktopComputerIcon class={"m-0 " + clss} {color} />
|
||||||
{:else if icon === "relocation"}
|
{:else if icon === "relocation"}
|
||||||
<Relocation class={clss} {color} />
|
<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)}
|
{:else if Utils.isEmoji(icon)}
|
||||||
<span style={`font-size: ${emojiHeight}px; line-height: ${emojiHeight}px`}>
|
<span style={`font-size: ${emojiHeight}px; line-height: ${emojiHeight}px`}>
|
||||||
{icon}
|
{icon}
|
||||||
</span>
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
<img class={clss ?? "h-full w-full"} src={icon} aria-hidden="true" alt="" />
|
<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)
|
feedback.setData(undefined)
|
||||||
}
|
}
|
||||||
tags.ping()
|
tags.ping()
|
||||||
|
dispatch("saved", { config, applied: selectedTags })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dispatch("saved", { config, applied: selectedTags })
|
dispatch("saved", { config, applied: selectedTags })
|
||||||
|
|
|
@ -40,7 +40,6 @@ import { Feature, GeoJsonProperties } from "geojson"
|
||||||
import { GeoOperations } from "../Logic/GeoOperations"
|
import { GeoOperations } from "../Logic/GeoOperations"
|
||||||
import CreateNewNote from "./Popup/Notes/CreateNewNote.svelte"
|
import CreateNewNote from "./Popup/Notes/CreateNewNote.svelte"
|
||||||
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
|
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
|
||||||
import UserProfile from "./BigComponents/UserProfile.svelte"
|
|
||||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
|
||||||
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"
|
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"
|
||||||
import { ExportAsGpxViz } from "./Popup/ExportAsGpxViz"
|
import { ExportAsGpxViz } from "./Popup/ExportAsGpxViz"
|
||||||
|
@ -102,6 +101,7 @@ import CloseNoteButton from "./Popup/Notes/CloseNoteButton.svelte"
|
||||||
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
|
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
|
||||||
import QrCode from "./Popup/QrCode.svelte"
|
import QrCode from "./Popup/QrCode.svelte"
|
||||||
import ClearCaches from "./Popup/ClearCaches.svelte"
|
import ClearCaches from "./Popup/ClearCaches.svelte"
|
||||||
|
import GroupedView from "./Popup/GroupedView.svelte"
|
||||||
|
|
||||||
class NearbyImageVis implements SpecialVisualization {
|
class NearbyImageVis implements SpecialVisualization {
|
||||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
// 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")
|
}).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",
|
funcName: "language_picker",
|
||||||
args: [],
|
args: [],
|
||||||
|
@ -1340,6 +1329,7 @@ export default class SpecialVisualizations {
|
||||||
download: tagSource.map((tags) => Utils.SubstituteKeys(download, tags)),
|
download: tagSource.map((tags) => Utils.SubstituteKeys(download, tags)),
|
||||||
ariaLabel: tagSource.map((tags) => Utils.SubstituteKeys(ariaLabel, tags)),
|
ariaLabel: tagSource.map((tags) => Utils.SubstituteKeys(ariaLabel, tags)),
|
||||||
newTab: new ImmutableStore(newTab),
|
newTab: new ImmutableStore(newTab),
|
||||||
|
icon: tagSource.map((tags) => Utils.SubstituteKeys(icon, tags))
|
||||||
}).setSpan()
|
}).setSpan()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2006,6 +1996,27 @@ export default class SpecialVisualizations {
|
||||||
],
|
],
|
||||||
constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, argument: string[], feature: Feature, layer: LayerConfig): SvelteUIElement {
|
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"})
|
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 ImageOperations from "./Image/ImageOperations.svelte"
|
||||||
import VisualFeedbackPanel from "./BigComponents/VisualFeedbackPanel.svelte"
|
import VisualFeedbackPanel from "./BigComponents/VisualFeedbackPanel.svelte"
|
||||||
import { Orientation } from "../Sensors/Orientation"
|
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 Compass_arrow from "../assets/svg/Compass_arrow.svelte"
|
||||||
import ReverseGeocoding from "./BigComponents/ReverseGeocoding.svelte"
|
import ReverseGeocoding from "./BigComponents/ReverseGeocoding.svelte"
|
||||||
import FilterPanel from "./BigComponents/FilterPanel.svelte"
|
import FilterPanel from "./BigComponents/FilterPanel.svelte"
|
||||||
|
@ -209,30 +209,7 @@
|
||||||
let addNewFeatureMode = state.userRelatedState.addNewFeatureMode
|
let addNewFeatureMode = state.userRelatedState.addNewFeatureMode
|
||||||
|
|
||||||
let gpsAvailable = state.geolocation.geolocationState.gpsAvailable
|
let gpsAvailable = state.geolocation.geolocationState.gpsAvailable
|
||||||
let gpsButtonAriaLabel = gpsAvailable.map(available => {
|
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation
|
||||||
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])
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
@ -435,7 +412,7 @@
|
||||||
on:click={() => state.geolocationControl.handleClick()}
|
on:click={() => state.geolocationControl.handleClick()}
|
||||||
on:keydown={forwardEventToMap}
|
on:keydown={forwardEventToMap}
|
||||||
>
|
>
|
||||||
<GeolocationControl {state} />
|
<GeolocationIndicator {state} />
|
||||||
<!-- h-8 w-8 + p-0.5 sm:p-1 + 2px border => 9 sm: 10 in total-->
|
<!-- h-8 w-8 + p-0.5 sm:p-1 + 2px border => 9 sm: 10 in total-->
|
||||||
</MapControlButton>
|
</MapControlButton>
|
||||||
{#if $compassLoaded}
|
{#if $compassLoaded}
|
||||||
|
@ -501,14 +478,15 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<!-- Image preview -->
|
||||||
<If condition={state.previewedImage.map((i) => i !== undefined)}>
|
<If condition={state.previewedImage.map((i) => i !== undefined)}>
|
||||||
<FloatOver on:close={() => state.previewedImage.setData(undefined)}>
|
<FloatOver on:close={() => state.previewedImage.setData(undefined)}>
|
||||||
<ImageOperations image={$previewedImage} />
|
<ImageOperations image={$previewedImage} />
|
||||||
</FloatOver>
|
</FloatOver>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
|
<!-- big theme menu -->
|
||||||
<If condition={state.guistate.themeIsOpened}>
|
<If condition={state.guistate.themeIsOpened}>
|
||||||
<!-- Theme menu -->
|
|
||||||
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
|
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
|
||||||
<span slot="close-button"><!-- Disable the close button --></span>
|
<span slot="close-button"><!-- Disable the close button --></span>
|
||||||
<TabbedGroup
|
<TabbedGroup
|
||||||
|
@ -524,7 +502,6 @@
|
||||||
|
|
||||||
<div class="flex" slot="title0">
|
<div class="flex" slot="title0">
|
||||||
<Marker icons={layout.icon} size="h-4 w-4" />
|
<Marker icons={layout.icon} size="h-4 w-4" />
|
||||||
|
|
||||||
<Tr t={layout.title} />
|
<Tr t={layout.title} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -561,14 +538,15 @@
|
||||||
</FloatOver>
|
</FloatOver>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
|
<!-- Filterpane -->
|
||||||
<If condition={state.guistate.filtersPanelIsOpened}>
|
<If condition={state.guistate.filtersPanelIsOpened}>
|
||||||
<FloatOver on:close={() => state.guistate.filtersPanelIsOpened.setData(false)}>
|
<FloatOver on:close={() => state.guistate.filtersPanelIsOpened.setData(false)}>
|
||||||
<FilterPanel {state} />
|
<FilterPanel {state} />
|
||||||
</FloatOver>
|
</FloatOver>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
|
<!-- background layer selector -->
|
||||||
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
|
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
|
||||||
<!-- background layer selector -->
|
|
||||||
<FloatOver
|
<FloatOver
|
||||||
on:close={() => {
|
on:close={() => {
|
||||||
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
|
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
|
||||||
|
@ -584,8 +562,8 @@
|
||||||
</FloatOver>
|
</FloatOver>
|
||||||
</IfHidden>
|
</IfHidden>
|
||||||
|
|
||||||
|
<!-- Menu page -->
|
||||||
<If condition={state.guistate.menuIsOpened}>
|
<If condition={state.guistate.menuIsOpened}>
|
||||||
<!-- Menu page -->
|
|
||||||
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
|
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
|
||||||
<span slot="close-button"><!-- Hide the default close button --></span>
|
<span slot="close-button"><!-- Hide the default close button --></span>
|
||||||
<TabbedGroup
|
<TabbedGroup
|
||||||
|
@ -656,6 +634,7 @@
|
||||||
</FloatOver>
|
</FloatOver>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
|
<!-- Privacy policy -->
|
||||||
<If condition={state.guistate.privacyPanelIsOpened}>
|
<If condition={state.guistate.privacyPanelIsOpened}>
|
||||||
<FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
|
<FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
|
||||||
<div class="flex h-full flex-col overflow-hidden">
|
<div class="flex h-full flex-col overflow-hidden">
|
||||||
|
@ -670,6 +649,7 @@
|
||||||
</FloatOver>
|
</FloatOver>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
|
<!-- Attribution, copyright and about MapComplete (no menu case) -->
|
||||||
<If condition={state.guistate.copyrightPanelIsOpened}>
|
<If condition={state.guistate.copyrightPanelIsOpened}>
|
||||||
<FloatOver on:close={() => state.guistate.copyrightPanelIsOpened.setData(false)}>
|
<FloatOver on:close={() => state.guistate.copyrightPanelIsOpened.setData(false)}>
|
||||||
<div class="flex h-full flex-col overflow-hidden">
|
<div class="flex h-full flex-col overflow-hidden">
|
||||||
|
@ -687,6 +667,7 @@
|
||||||
</FloatOver>
|
</FloatOver>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
|
<!-- Community index -->
|
||||||
<If condition={state.guistate.communityIndexPanelIsOpened}>
|
<If condition={state.guistate.communityIndexPanelIsOpened}>
|
||||||
<FloatOver on:close={() => state.guistate.communityIndexPanelIsOpened.setData(false)}>
|
<FloatOver on:close={() => state.guistate.communityIndexPanelIsOpened.setData(false)}>
|
||||||
<div class="flex h-full flex-col overflow-hidden">
|
<div class="flex h-full flex-col overflow-hidden">
|
||||||
|
|
|
@ -39,10 +39,13 @@
|
||||||
--interactive-contrast: #ff00ff;
|
--interactive-contrast: #ff00ff;
|
||||||
|
|
||||||
--button-background: #282828;
|
--button-background: #282828;
|
||||||
--button-background-hover: #686868;
|
--button-background-hover: #484848;
|
||||||
|
--button-primary-background-hover: #353535;
|
||||||
|
|
||||||
--button-foreground: white;
|
--button-foreground: white;
|
||||||
--button-border-color: #F7F7F7;
|
--button-border-color: #F7F7F7;
|
||||||
--disabled: #DBDBDB;
|
--disabled: #B8B8B8;
|
||||||
|
--disabled-font: #B8B8B8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base colour of interactive elements, mainly the 'subtle button'
|
* 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) {
|
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 {
|
button.disabled {
|
||||||
border-color: var(--disabled);
|
border-color: var(--disabled-font);
|
||||||
color: var(--disabled);
|
color: var(--disabled-font);
|
||||||
cursor: unset;
|
cursor: unset;
|
||||||
}
|
}
|
||||||
button.disabled svg path {
|
button.disabled svg path {
|
||||||
transition: all 200ms;
|
transition: all 200ms;
|
||||||
}
|
}
|
||||||
button.disabled svg path {
|
button.disabled svg path {
|
||||||
fill: var(--disabled);
|
fill: var(--disabled-font);
|
||||||
stroke: var(--disabled);
|
stroke: var(--disabled-font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue