forked from MapComplete/MapComplete
		
	Themes: improve note theme, fix #2088
This commit is contained in:
		
							parent
							
								
									70117ac687
								
							
						
					
					
						commit
						e8099b9081
					
				
					 5 changed files with 266 additions and 189 deletions
				
			
		|  | @ -18,13 +18,16 @@ | |||
|   "calculatedTags": [ | ||||
|     "_total_comments:=get(feat)('comments').length", | ||||
|     "_first_comment:=get(feat)('comments')[0].text", | ||||
|     "_all_comments:=get(feat)('comments').map(c => c.text ?? '').join('\\n')", | ||||
|     "_all_usernames:=get(feat)('comments').map(c => c.user ?? 'Anonymous').join('\\n')", | ||||
|     "_opened_by_anonymous_user:=get(feat)('comments')[0].user === undefined", | ||||
|     "_first_user:=get(feat)('comments')[0].user", | ||||
|     "_last_user:=(() => {const comms = get(feat)('comments'); return comms[comms.length - 1].user})()", | ||||
|     "_last_change_date:=(() => {const comms = get(feat)('comments'); return comms[comms.length - 1].date})()", | ||||
|     "_first_user_id:=get(feat)('comments')[0].uid", | ||||
|     "_is_import_note:=(() => {const lines = feat.properties['_first_comment'].split('\\n'); const matchesMapCompleteURL = lines.map(l => l.match(\".*https://mapcomplete.\\(osm.be|org\\)/\\([a-zA-Z_-]+\\)\\(.html\\).*#import\")); const matchedIndexes = matchesMapCompleteURL.map((doesMatch, i) => [doesMatch !== null, i]).filter(v => v[0]).map(v => v[1]); return matchedIndexes[0] })()" | ||||
|   ], | ||||
|   "minzoom": 10, | ||||
|   "minzoom": 7, | ||||
|   "title": { | ||||
|     "render": { | ||||
|       "en": "Note", | ||||
|  | @ -158,183 +161,6 @@ | |||
|     } | ||||
|   ], | ||||
|   "filter": [ | ||||
|     { | ||||
|       "id": "search", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_first_comment~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Should mention {search} in the first comment", | ||||
|             "nl": "Moet in de eerste opmerking \"{search}\" bevatten", | ||||
|             "de": "Sollte {search} im ersten Kommentar erwähnen", | ||||
|             "es": "Debe mencionar {search} en el primer comentario", | ||||
|             "ca": "Has de mencionar {search} en el primer comentari", | ||||
|             "cs": "Měl by se zmínit {search} v prvním komentáři" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "not", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_first_comment!~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Should <b>not</b> mention {search} in the first comment", | ||||
|             "nl": "Mag in de eerste opmerking <b>niet</b> \"{search}\" bevatten", | ||||
|             "de": "Sollte <b>nicht</b> {search} im ersten Kommentar erwähnen", | ||||
|             "es": "<b>No</b> debe mencionar {search} en el primer comentario", | ||||
|             "ca": "<b>No</b> s'ha de mencionar {search} al primer comentari", | ||||
|             "cs": "V prvním komentáři by jste <b>neměli</b> zmiňovat {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "opened_by", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_first_user~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Opened by contributor {search}", | ||||
|             "nl": "Geopend door bijdrager {search}", | ||||
|             "de": "Erstellt von {search}", | ||||
|             "es": "Abierto por el contributor {search}", | ||||
|             "fr": "Ouverte par {search}", | ||||
|             "ca": "Obert pel contribuïdor {search}", | ||||
|             "cs": "Otevřeno přispěvatelem {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "not_opened_by", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_first_user!~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "<b>Not</b> opened by contributor {search}", | ||||
|             "nl": "<b>Niet</b> geopend door bijdrager {search}", | ||||
|             "de": "<b>Nicht</b> erstellt von {search}", | ||||
|             "es": "<b>No</b> abierto por el contributor {search}", | ||||
|             "ca": "<b>No</b> obert pel contribuïdor {search}", | ||||
|             "cs": "<b>Není</b> otevřeno přispěvatelem {search}", | ||||
|             "fr": "<b>Exclure</b>les notes ouvertes par {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "edited_by", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_last_user~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Last edited by contributor {search}", | ||||
|             "nl": "Laatst bewerkt door bijdrager {search}", | ||||
|             "de": "Zuletzt bearbeitet von {search}", | ||||
|             "es": "Editada por última vez por el contributor {search}", | ||||
|             "ca": "Editat per última vega pel contribuïdor {search}", | ||||
|             "cs": "Naposledy upravil přispěvatel {search}", | ||||
|             "da": "Senest redigeret af bidragsyder {search}", | ||||
|             "fr": "Dernière modification par {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "not_edited_by", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_last_user!~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Opened after {search}", | ||||
|             "nl": "Geopend na {search}", | ||||
|             "de": "Zuletzt bearbeitet nach dem {search}", | ||||
|             "es": "Abierta después de {search}", | ||||
|             "ca": "Oberta després de {search}", | ||||
|             "cs": "Otevřeno po {search}", | ||||
|             "fr": "Ouverte après le {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "opened_before", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "date_created<{search}", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search", | ||||
|               "type": "date" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Created before {search}", | ||||
|             "nl": "Aangemaakt voor {search}", | ||||
|             "de": "Erstellt vor dem {search}", | ||||
|             "es": "Creada antes de {search}", | ||||
|             "ca": "Creada abans de {search}", | ||||
|             "cs": "Vytvořeno před {search}", | ||||
|             "fr": "Créée avant le {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "opened_after", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "date_created>{search}", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search", | ||||
|               "type": "date" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Created after {search}", | ||||
|             "nl": "Aangemaakt na {search}", | ||||
|             "de": "Erstellt nach dem {search}", | ||||
|             "es": "Creada después de {search}", | ||||
|             "ca": "Creada després de {search}", | ||||
|             "cs": "Vytvořeno po {search}", | ||||
|             "fr": "Créée après le {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "anonymous", | ||||
|       "options": [ | ||||
|  | @ -406,6 +232,244 @@ | |||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "search", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_first_comment~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Should mention {search} in the first comment", | ||||
|             "nl": "Moet in de eerste opmerking \"{search}\" bevatten", | ||||
|             "de": "Sollte {search} im ersten Kommentar erwähnen", | ||||
|             "es": "Debe mencionar {search} en el primer comentario", | ||||
|             "ca": "Has de mencionar {search} en el primer comentari", | ||||
|             "cs": "Měl by se zmínit {search} v prvním komentáři" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "search_any", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_all_comments~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Should mention {search} in any comment" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
| 
 | ||||
|     { | ||||
|       "id": "not", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_first_comment!~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Should <b>not</b> mention {search} in the first comment", | ||||
|             "nl": "Mag in de eerste opmerking <b>niet</b> \"{search}\" bevatten", | ||||
|             "de": "Sollte <b>nicht</b> {search} im ersten Kommentar erwähnen", | ||||
|             "es": "<b>No</b> debe mencionar {search} en el primer comentario", | ||||
|             "ca": "<b>No</b> s'ha de mencionar {search} al primer comentari", | ||||
|             "cs": "V prvním komentáři by jste <b>neměli</b> zmiňovat {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "opened_by", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_first_user~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Opened by contributor {search}", | ||||
|             "nl": "Geopend door bijdrager {search}", | ||||
|             "de": "Erstellt von {search}", | ||||
|             "es": "Abierto por el contributor {search}", | ||||
|             "fr": "Ouverte par {search}", | ||||
|             "ca": "Obert pel contribuïdor {search}", | ||||
|             "cs": "Otevřeno přispěvatelem {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "not_opened_by", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_first_user!~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "<b>Not</b> opened by contributor {search}", | ||||
|             "nl": "<b>Niet</b> geopend door bijdrager {search}", | ||||
|             "de": "<b>Nicht</b> erstellt von {search}", | ||||
|             "es": "<b>No</b> abierto por el contributor {search}", | ||||
|             "ca": "<b>No</b> obert pel contribuïdor {search}", | ||||
|             "cs": "<b>Není</b> otevřeno přispěvatelem {search}", | ||||
|             "fr": "<b>Exclure</b>les notes ouvertes par {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "edited_by_any", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_all_usernames~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Edited or commented on by any user with name {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "last_edited_by", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_last_user~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Last edited by contributor {search}", | ||||
|             "nl": "Laatst bewerkt door bijdrager {search}", | ||||
|             "de": "Zuletzt bearbeitet von {search}", | ||||
|             "es": "Editada por última vez por el contributor {search}", | ||||
|             "ca": "Editat per última vega pel contribuïdor {search}", | ||||
|             "cs": "Naposledy upravil přispěvatel {search}", | ||||
|             "da": "Senest redigeret af bidragsyder {search}", | ||||
|             "fr": "Dernière modification par {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "not_last_edited_by", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_last_user!~i~.*{search}.*", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Not edited as last by {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "opened_before", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "date_created<{search}", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search", | ||||
|               "type": "date" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Created before {search}", | ||||
|             "nl": "Aangemaakt voor {search}", | ||||
|             "de": "Erstellt vor dem {search}", | ||||
|             "es": "Creada antes de {search}", | ||||
|             "ca": "Creada abans de {search}", | ||||
|             "cs": "Vytvořeno před {search}", | ||||
|             "fr": "Créée avant le {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "opened_after", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "date_created>{search}", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search", | ||||
|               "type": "date" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Created after {search}", | ||||
|             "nl": "Aangemaakt na {search}", | ||||
|             "de": "Erstellt nach dem {search}", | ||||
|             "es": "Creada después de {search}", | ||||
|             "ca": "Creada després de {search}", | ||||
|             "cs": "Vytvořeno po {search}", | ||||
|             "fr": "Créée après le {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "last_edited_before", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_last_change_date<{search}", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search", | ||||
|               "type": "date" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Last edited before {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "last_edited_after", | ||||
|       "options": [ | ||||
|         { | ||||
|           "osmTags": "_last_change_date>{search}", | ||||
|           "fields": [ | ||||
|             { | ||||
|               "name": "search", | ||||
|               "type": "date" | ||||
|             } | ||||
|           ], | ||||
|           "question": { | ||||
|             "en": "Last edited after {search}" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ], | ||||
|   "allowMove": false | ||||
|  |  | |||
|  | @ -12,12 +12,14 @@ import BaseUIElement from "../../UI/BaseUIElement" | |||
| import Table from "../../UI/Base/Table" | ||||
| import Combine from "../../UI/Base/Combine" | ||||
| import MarkdownUtils from "../../Utils/MarkdownUtils" | ||||
| import Validators, { ValidatorType } from "../../UI/InputElement/Validators" | ||||
| 
 | ||||
| export type FilterConfigOption = { | ||||
|     question: Translation | ||||
|     osmTags: TagsFilter | undefined | ||||
|     /* Only set if fields are present. Used to create `osmTags` (which are used to _actually_ filter) when the field is written*/ | ||||
|     readonly originalTagsSpec: TagConfigJson | ||||
|     fields: { name: string; type: string }[] | ||||
|     fields: { name: string; type: ValidatorType }[] | ||||
| } | ||||
| export default class FilterConfig { | ||||
|     public readonly id: string | ||||
|  | @ -57,8 +59,11 @@ export default class FilterConfig { | |||
|                 throw `Invalid filter: no question given at ${ctx}` | ||||
|             } | ||||
| 
 | ||||
|             const fields: { name: string; type: string }[] = (option.fields ?? []).map((f, i) => { | ||||
|                 const type = f.type ?? "string" | ||||
|             const fields: { name: string; type: ValidatorType }[] = (option.fields ?? []).map((f, i) => { | ||||
|                 const type = <ValidatorType> f.type ?? "string" | ||||
|                 if(Validators.availableTypes.indexOf(type) < 0){ | ||||
|                     throw `Invalid filter: type is not a valid validator. Did you mean one of ${Utils.sortedByLevenshteinDistance(type, <any>Validators.availableTypes, x => x).slice(0, 3)}` | ||||
|                 } | ||||
|                 // Type is validated against 'ValidatedTextField' in Validation.ts, in ValidateFilterConfig
 | ||||
|                 if (f.name === undefined || f.name === "" || f.name.match(/[a-z0-9_-]+/) == null) { | ||||
|                     throw `Invalid filter: a variable name should match [a-z0-9_-]+ at ${ctx}.fields[${i}]` | ||||
|  |  | |||
|  | @ -6,7 +6,8 @@ | |||
|   import { UIEventSource } from "../../Logic/UIEventSource" | ||||
|   import { onDestroy } from "svelte" | ||||
|   import { Utils } from "../../Utils" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import type { ValidatorType } from "../InputElement/Validators" | ||||
|   import InputHelper from "../InputElement/InputHelper.svelte" | ||||
| 
 | ||||
|   export let filteredLayer: FilteredLayer | ||||
|   export let option: FilterConfigOption | ||||
|  | @ -18,7 +19,7 @@ | |||
|     parts = Utils.splitIntoSubstitutionParts(template) | ||||
|   } | ||||
|   let fieldValues: Record<string, UIEventSource<string>> = {} | ||||
|   let fieldTypes: Record<string, string> = {} | ||||
|   let fieldTypes: Record<string, ValidatorType> = {} | ||||
|   let appliedFilter = <UIEventSource<string>>filteredLayer.appliedFilters.get(id) | ||||
|   let initialState: Record<string, string> = JSON.parse(appliedFilter?.data ?? "{}") | ||||
| 
 | ||||
|  | @ -35,25 +36,30 @@ | |||
|     appliedFilter?.setData(FilteredLayer.fieldsToString(properties)) | ||||
|   } | ||||
| 
 | ||||
|   let firstValue : UIEventSource<string> | ||||
|   for (const field of option.fields) { | ||||
|     // A bit of cheating: the 'parts' will have '}' suffixed for fields | ||||
|     const src = new UIEventSource<string>(initialState[field.name] ?? "") | ||||
|     firstValue ??= src | ||||
|     fieldTypes[field.name] = field.type | ||||
|     console.log(field.name, "-->", field.type) | ||||
|     fieldValues[field.name] = src | ||||
|     onDestroy( | ||||
|       src.stabilized(200).addCallback(() => { | ||||
|         setFields() | ||||
|       }) | ||||
|       }), | ||||
|     ) | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <div> | ||||
| <div class="low-interaction p-1 rounded-2xl px-3" class:interactive={$firstValue?.length > 0}> | ||||
|   {#each parts as part, i} | ||||
|     {#if part["subs"]} | ||||
|       <!-- This is a field! --> | ||||
|       <span class="mx-1"> | ||||
|         <ValidatedInput value={fieldValues[part["subs"]]} type={fieldTypes[part["subs"]]} /> | ||||
|         <InputHelper value={fieldValues[part["subs"]]} type={fieldTypes[part["subs"]]}> | ||||
|           <ValidatedInput slot="fallback" value={fieldValues[part["subs"]]} type={fieldTypes[part["subs"]]} /> | ||||
|         </InputHelper> | ||||
|       </span> | ||||
|     {:else} | ||||
|       {@html part["message"]} | ||||
|  |  | |||
|  | @ -23,9 +23,9 @@ | |||
|   export let type: ValidatorType | ||||
|   export let value: UIEventSource<string | object> | ||||
| 
 | ||||
|   export let feature: Feature | ||||
|   export let feature: Feature = undefined | ||||
|   export let args: (string | number | boolean)[] = undefined | ||||
|   export let state: SpecialVisualizationState | ||||
|   export let state: SpecialVisualizationState = undefined | ||||
| </script> | ||||
| 
 | ||||
| {#if type === "translation"} | ||||
|  | @ -51,4 +51,6 @@ | |||
|   <SlopeInput {value} {feature} {state} /> | ||||
| {:else if type === "wikidata"} | ||||
|   <WikidataInputHelper {value} {feature} {state} {args} /> | ||||
| {:else} | ||||
|   <slot name="fallback" /> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -1272,7 +1272,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | |||
| 
 | ||||
|     public static sortedByLevenshteinDistance<T>( | ||||
|         reference: string, | ||||
|         ts: T[], | ||||
|         ts: ReadonlyArray<T>, | ||||
|         getName: (t: T) => string | ||||
|     ): T[] { | ||||
|         const withDistance: [T, number][] = ts.map((t) => [ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue