forked from MapComplete/MapComplete
		
	Merge master
This commit is contained in:
		
						commit
						08461f70d3
					
				
					 119 changed files with 2810 additions and 1164 deletions
				
			
		|  | @ -29,7 +29,15 @@ export default class Table extends BaseUIElement { | |||
|         const header = Utils.NoNull(headerMarkdownParts).join(" | ") | ||||
|         const headerSep = headerMarkdownParts.map((part) => "-".repeat(part.length + 2)).join(" | ") | ||||
|         const table = this._contents | ||||
|             .map((row) => row.map((el) => el?.AsMarkdown()?.replaceAll("\\","\\\\")?.replaceAll("|", "\\|") ?? " ").join(" | ")) | ||||
|             .map((row) => | ||||
|                 row | ||||
|                     .map( | ||||
|                         (el) => | ||||
|                             el?.AsMarkdown()?.replaceAll("\\", "\\\\")?.replaceAll("|", "\\|") ?? | ||||
|                             " " | ||||
|                     ) | ||||
|                     .join(" | ") | ||||
|             ) | ||||
|             .join("\n") | ||||
| 
 | ||||
|         return "\n\n" + [header, headerSep, table, ""].join("\n") | ||||
|  |  | |||
|  | @ -35,7 +35,12 @@ | |||
|         src={`https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/community_index/${resource.type}.svg`} | ||||
|       /> | ||||
|       <div class="flex flex-col"> | ||||
|         <a href={resource.resolved.url} target="_blank" rel="noreferrer nofollow noopener" class="font-bold"> | ||||
|         <a | ||||
|           href={resource.resolved.url} | ||||
|           target="_blank" | ||||
|           rel="noreferrer nofollow noopener" | ||||
|           class="font-bold" | ||||
|         > | ||||
|           {resource.resolved.name ?? resource.resolved.url} | ||||
|         </a> | ||||
|         {resource.resolved?.description} | ||||
|  |  | |||
|  | @ -102,7 +102,11 @@ export default class CopyrightPanel extends Combine { | |||
|                         let bgAttr: BaseUIElement | string = undefined | ||||
|                         if (attrText && attrUrl) { | ||||
|                             bgAttr = | ||||
|                                 "<a href='" + attrUrl + "' target='_blank' rel='noopener'>" + attrText + "</a>" | ||||
|                                 "<a href='" + | ||||
|                                 attrUrl + | ||||
|                                 "' target='_blank' rel='noopener'>" + | ||||
|                                 attrText + | ||||
|                                 "</a>" | ||||
|                         } else if (attrUrl) { | ||||
|                             bgAttr = attrUrl | ||||
|                         } else { | ||||
|  |  | |||
|  | @ -72,10 +72,7 @@ export class ImageUploadFlow extends Toggle { | |||
|             labelContent, | ||||
|         ]).SetClass("w-full flex justify-center items-center") | ||||
| 
 | ||||
|         const licenseStore = state?.osmConnection?.GetPreference( | ||||
|             "pictures-license", | ||||
|             "CC0" | ||||
|         ) | ||||
|         const licenseStore = state?.osmConnection?.GetPreference("pictures-license", "CC0") | ||||
| 
 | ||||
|         const fileSelector = new FileSelectorButton(label, { | ||||
|             acceptType: "image/*", | ||||
|  |  | |||
|  | @ -1,28 +1,30 @@ | |||
| <script lang="ts"> | ||||
|   import { Store } from "../../Logic/UIEventSource" | ||||
|   import type { OsmTags } from "../../Models/OsmFeature" | ||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization" | ||||
|   import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch" | ||||
|   import ToSvelte from "../Base/ToSvelte.svelte" | ||||
|   import { AttributedImage } from "../Image/AttributedImage" | ||||
|   import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders" | ||||
|   import LinkPicture from "../../Logic/Osm/Actions/LinkPicture" | ||||
|   import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction" | ||||
|   import { Tag } from "../../Logic/Tags/Tag" | ||||
|   import { GeoOperations } from "../../Logic/GeoOperations" | ||||
|   import type { Feature } from "geojson" | ||||
|   import Translations from "../i18n/Translations" | ||||
|   import SpecialTranslation from "./TagRendering/SpecialTranslation.svelte" | ||||
|   import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| 
 | ||||
|   import { Store } from "../../Logic/UIEventSource"; | ||||
|   import type { OsmTags } from "../../Models/OsmFeature"; | ||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization"; | ||||
|   import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"; | ||||
|   import ToSvelte from "../Base/ToSvelte.svelte"; | ||||
|   import { AttributedImage } from "../Image/AttributedImage"; | ||||
|   import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"; | ||||
|   import LinkPicture from "../../Logic/Osm/Actions/LinkPicture"; | ||||
|   import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"; | ||||
|   import { Tag } from "../../Logic/Tags/Tag"; | ||||
|   import { GeoOperations } from "../../Logic/GeoOperations"; | ||||
|   import type { Feature } from "geojson"; | ||||
|   import Translations from "../i18n/Translations"; | ||||
|   import SpecialTranslation from "./TagRendering/SpecialTranslation.svelte"; | ||||
|   import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; | ||||
|   export let tags: Store<OsmTags> | ||||
|   export let lon: number | ||||
|   export let lat: number | ||||
|   export let state: SpecialVisualizationState | ||||
|   export let image: P4CPicture | ||||
|   export let feature: Feature | ||||
|   export let layer: LayerConfig | ||||
| 
 | ||||
|   export let tags: Store<OsmTags>; | ||||
|   export let lon: number; | ||||
|   export let lat: number; | ||||
|   export let state: SpecialVisualizationState; | ||||
|   export let image: P4CPicture; | ||||
|   export let feature: Feature; | ||||
|   export let layer: LayerConfig; | ||||
|   export let linkable = true | ||||
|   let isLinked = false | ||||
| 
 | ||||
|   export let linkable = true; | ||||
|   let isLinked = Object.values(tags.data).some(v => image.pictureUrl === v); | ||||
|  | @ -37,37 +39,35 @@ | |||
|   let distance = Math.round(GeoOperations.distanceBetween([image.coordinates.lng, image.coordinates.lat], c)); | ||||
|    | ||||
|   $: { | ||||
|     const currentTags = tags.data; | ||||
|     const key = Object.keys(image.osmTags)[0]; | ||||
|     const url = image.osmTags[key]; | ||||
|     const currentTags = tags.data | ||||
|     const key = Object.keys(image.osmTags)[0] | ||||
|     const url = image.osmTags[key] | ||||
|     if (isLinked) { | ||||
|       const action = new LinkPicture( | ||||
|         currentTags.id, | ||||
|         key, | ||||
|         url, | ||||
|         currentTags, | ||||
|         { | ||||
|           theme: state.layout.id, | ||||
|           changeType: "link-image" | ||||
|         } | ||||
|       ); | ||||
|       state.changes.applyAction(action); | ||||
|       const action = new LinkPicture(currentTags.id, key, url, currentTags, { | ||||
|         theme: state.layout.id, | ||||
|         changeType: "link-image", | ||||
|       }) | ||||
|       state.changes.applyAction(action) | ||||
|     } else { | ||||
|       for (const k in currentTags) { | ||||
|         const v = currentTags[k]; | ||||
|         const v = currentTags[k] | ||||
|         if (v === url) { | ||||
|           const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, { theme: state.layout.id, changeType: "remove-image" }); | ||||
|           state.changes.applyAction(action); | ||||
|           const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, { | ||||
|             theme: state.layout.id, | ||||
|             changeType: "remove-image", | ||||
|           }) | ||||
|           state.changes.applyAction(action) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
| <div class="flex flex-col w-fit shrink-0"> | ||||
| 
 | ||||
| <div class="flex w-fit shrink-0 flex-col"> | ||||
|   <ToSvelte construct={attributedImage.SetClass("h-48 w-fit")} /> | ||||
|   {#if linkable} | ||||
|     <label> | ||||
|       <input bind:checked={isLinked} type="checkbox"> | ||||
|       <input bind:checked={isLinked} type="checkbox" /> | ||||
|       <SpecialTranslation t={t.link} {tags} {state} {layer} {feature} /> | ||||
|     </label> | ||||
|   {/if} | ||||
|  |  | |||
|  | @ -1,40 +1,44 @@ | |||
| <script lang="ts">/** | ||||
|  * Show nearby images which can be clicked | ||||
|  */ | ||||
| import type { OsmTags } from "../../Models/OsmFeature"; | ||||
| import { Store, UIEventSource } from "../../Logic/UIEventSource"; | ||||
| import type { SpecialVisualizationState } from "../SpecialVisualization"; | ||||
| import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"; | ||||
| import NearbyImagesSearch from "../../Logic/Web/NearbyImagesSearch"; | ||||
| import LinkableImage from "./LinkableImage.svelte"; | ||||
| import type { Feature } from "geojson"; | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; | ||||
| import Loading from "../Base/Loading.svelte"; | ||||
| import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"; | ||||
| import Tr from "../Base/Tr.svelte"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| <script lang="ts"> | ||||
|   /** | ||||
|    * Show nearby images which can be clicked | ||||
|    */ | ||||
|   import type { OsmTags } from "../../Models/OsmFeature" | ||||
|   import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization" | ||||
|   import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch" | ||||
|   import NearbyImagesSearch from "../../Logic/Web/NearbyImagesSearch" | ||||
|   import LinkableImage from "./LinkableImage.svelte" | ||||
|   import type { Feature } from "geojson" | ||||
|   import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
|   import Loading from "../Base/Loading.svelte" | ||||
|   import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import Translations from "../i18n/Translations" | ||||
| 
 | ||||
| export let tags: Store<OsmTags>; | ||||
| export let state: SpecialVisualizationState; | ||||
| export let lon: number; | ||||
| export let lat: number; | ||||
| export let feature: Feature; | ||||
|   export let tags: Store<OsmTags> | ||||
|   export let state: SpecialVisualizationState | ||||
|   export let lon: number | ||||
|   export let lat: number | ||||
|   export let feature: Feature | ||||
| 
 | ||||
| export let linkable: boolean = true; | ||||
| export let layer: LayerConfig; | ||||
|   export let linkable: boolean = true | ||||
|   export let layer: LayerConfig | ||||
| 
 | ||||
| let imagesProvider = new NearbyImagesSearch({ | ||||
|   lon, lat, allowSpherical: new UIEventSource<boolean>(false), | ||||
|   blacklist: AllImageProviders.LoadImagesFor(tags) | ||||
| }, state.indexedFeatures); | ||||
| 
 | ||||
| let images: Store<P4CPicture[]> = imagesProvider.store.map(images => images.slice(0, 20)); | ||||
|   let imagesProvider = new NearbyImagesSearch( | ||||
|     { | ||||
|       lon, | ||||
|       lat, | ||||
|       allowSpherical: new UIEventSource<boolean>(false), | ||||
|       blacklist: AllImageProviders.LoadImagesFor(tags), | ||||
|     }, | ||||
|     state.indexedFeatures | ||||
|   ) | ||||
| 
 | ||||
|   let images: Store<P4CPicture[]> = imagesProvider.store.map((images) => images.slice(0, 20)) | ||||
| </script> | ||||
| 
 | ||||
| <div class="interactive rounded-2xl border-interactive p-2"> | ||||
| <div class="interactive border-interactive rounded-2xl p-2"> | ||||
|   <div class="flex justify-between"> | ||||
| 
 | ||||
|     <h4> | ||||
|       <Tr t={Translations.t.image.nearby.title} /> | ||||
|     </h4> | ||||
|  | @ -43,7 +47,7 @@ let images: Store<P4CPicture[]> = imagesProvider.store.map(images => images.slic | |||
|   {#if $images.length === 0} | ||||
|     <Loading /> | ||||
|   {:else} | ||||
|     <div class="overflow-x-auto w-full flex space-x-1" style="scroll-snap-type: x proximity"> | ||||
|     <div class="flex w-full space-x-1 overflow-x-auto" style="scroll-snap-type: x proximity"> | ||||
|       {#each $images as image (image.pictureUrl)} | ||||
|         <span class="w-fit shrink-0" style="scroll-snap-align: start"> | ||||
|           <LinkableImage {tags} {image} {state} {lon} {lat} {feature} {layer} {linkable} /> | ||||
|  |  | |||
|  | @ -1,36 +1,48 @@ | |||
| <script lang="ts"> | ||||
|   import { Store } from "../../Logic/UIEventSource"; | ||||
|   import type { OsmTags } from "../../Models/OsmFeature"; | ||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization"; | ||||
|   import type { Feature } from "geojson"; | ||||
|   import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; | ||||
|   import Translations from "../i18n/Translations"; | ||||
|   import Tr from "../Base/Tr.svelte"; | ||||
|   import NearbyImages from "./NearbyImages.svelte"; | ||||
|   import Svg from "../../Svg"; | ||||
|   import ToSvelte from "../Base/ToSvelte.svelte"; | ||||
|   import { XCircleIcon } from "@babeard/svelte-heroicons/solid"; | ||||
|   import exp from "constants"; | ||||
|   import { Store } from "../../Logic/UIEventSource" | ||||
|   import type { OsmTags } from "../../Models/OsmFeature" | ||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization" | ||||
|   import type { Feature } from "geojson" | ||||
|   import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
|   import Translations from "../i18n/Translations" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import NearbyImages from "./NearbyImages.svelte" | ||||
|   import Svg from "../../Svg" | ||||
|   import ToSvelte from "../Base/ToSvelte.svelte" | ||||
|   import { XCircleIcon } from "@babeard/svelte-heroicons/solid" | ||||
|   import exp from "constants" | ||||
| 
 | ||||
|   export let tags: Store<OsmTags>; | ||||
|   export let state: SpecialVisualizationState; | ||||
|   export let lon: number; | ||||
|   export let lat: number; | ||||
|   export let feature: Feature; | ||||
|   export let tags: Store<OsmTags> | ||||
|   export let state: SpecialVisualizationState | ||||
|   export let lon: number | ||||
|   export let lat: number | ||||
|   export let feature: Feature | ||||
| 
 | ||||
|   export let linkable: boolean = true; | ||||
|   export let layer: LayerConfig; | ||||
|   const t = Translations.t.image.nearby; | ||||
|   export let linkable: boolean = true | ||||
|   export let layer: LayerConfig | ||||
|   const t = Translations.t.image.nearby | ||||
| 
 | ||||
|   let expanded = false; | ||||
|   let expanded = false | ||||
| </script> | ||||
| 
 | ||||
| {#if expanded} | ||||
|   <NearbyImages {tags} {state} {lon} {lat} {feature} {linkable}> | ||||
|     <XCircleIcon slot="corner" class="w-6 h-6 cursor-pointer" on:click={() => {expanded = false}}/> | ||||
|     <XCircleIcon | ||||
|       slot="corner" | ||||
|       class="h-6 w-6 cursor-pointer" | ||||
|       on:click={() => { | ||||
|         expanded = false | ||||
|       }} | ||||
|     /> | ||||
|   </NearbyImages> | ||||
| {:else} | ||||
|   <button class="w-full flex items-center" on:click={() => { expanded = true; }}> | ||||
|     <ToSvelte construct={ Svg.camera_plus_svg().SetClass("block w-8 h-8 p-1 mr-2 ")}/> | ||||
|     <Tr t={t.seeNearby}/></button> | ||||
|   <button | ||||
|     class="flex w-full items-center" | ||||
|     on:click={() => { | ||||
|       expanded = true | ||||
|     }} | ||||
|   > | ||||
|     <ToSvelte construct={Svg.camera_plus_svg().SetClass("block w-8 h-8 p-1 mr-2 ")} /> | ||||
|     <Tr t={t.seeNearby} /> | ||||
|   </button> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -1,22 +1,18 @@ | |||
| <script lang="ts"> | ||||
|    | ||||
|   import type { OsmTags } from "../../Models/OsmFeature"; | ||||
|   import Svg from "../../Svg"; | ||||
|   import ToSvelte from "../Base/ToSvelte.svelte"; | ||||
|   import { Utils } from "../../Utils"; | ||||
|   import type { OsmTags } from "../../Models/OsmFeature" | ||||
|   import Svg from "../../Svg" | ||||
|   import ToSvelte from "../Base/ToSvelte.svelte" | ||||
|   import { Utils } from "../../Utils" | ||||
| 
 | ||||
|   export let tags: Store<OsmTags> | ||||
|   export let args: string[] | ||||
| 
 | ||||
|   let [to, subject, body, button_text] = args.map(a => Utils.SubstituteKeys(a, $tags)) | ||||
|   let url = "mailto:" + | ||||
|     to + | ||||
|     "?subject=" + | ||||
|     encodeURIComponent(subject) + | ||||
|     "&body=" + | ||||
|     encodeURIComponent(body) | ||||
|   let [to, subject, body, button_text] = args.map((a) => Utils.SubstituteKeys(a, $tags)) | ||||
|   let url = | ||||
|     "mailto:" + to + "?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body) | ||||
| </script> | ||||
| <a class="button flex items-center w-full" href={url}> | ||||
|   <ToSvelte construct={Svg.envelope_svg().SetClass("w-8 h-8 mr-4 shrink-0")}/> | ||||
| 
 | ||||
| <a class="button flex w-full items-center" href={url}> | ||||
|   <ToSvelte construct={Svg.envelope_svg().SetClass("w-8 h-8 mr-4 shrink-0")} /> | ||||
|   {button_text} | ||||
| </a> | ||||
|  |  | |||
|  | @ -1,43 +1,43 @@ | |||
| <script lang="ts"> | ||||
|   import { UIEventSource } from "../../../Logic/UIEventSource"; | ||||
|   import type { SpecialVisualizationState } from "../../SpecialVisualization"; | ||||
|   import Tr from "../../Base/Tr.svelte"; | ||||
|   import type { Feature } from "geojson"; | ||||
|   import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig"; | ||||
|   import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"; | ||||
|   import { TagsFilter } from "../../../Logic/Tags/TagsFilter"; | ||||
|   import FreeformInput from "./FreeformInput.svelte"; | ||||
|   import Translations from "../../i18n/Translations.js"; | ||||
|   import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"; | ||||
|   import { createEventDispatcher, onDestroy } from "svelte"; | ||||
|   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; | ||||
|   import SpecialTranslation from "./SpecialTranslation.svelte"; | ||||
|   import TagHint from "../TagHint.svelte"; | ||||
|   import LoginToggle from "../../Base/LoginToggle.svelte"; | ||||
|   import SubtleButton from "../../Base/SubtleButton.svelte"; | ||||
|   import Loading from "../../Base/Loading.svelte"; | ||||
|   import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte"; | ||||
|   import { Translation } from "../../i18n/Translation"; | ||||
|   import Constants from "../../../Models/Constants"; | ||||
|   import { Unit } from "../../../Models/Unit"; | ||||
|   import UserRelatedState from "../../../Logic/State/UserRelatedState"; | ||||
|   import { twJoin } from "tailwind-merge"; | ||||
|   import { TagUtils } from "../../../Logic/Tags/TagUtils"; | ||||
|   import { UIEventSource } from "../../../Logic/UIEventSource" | ||||
|   import type { SpecialVisualizationState } from "../../SpecialVisualization" | ||||
|   import Tr from "../../Base/Tr.svelte" | ||||
|   import type { Feature } from "geojson" | ||||
|   import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig" | ||||
|   import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig" | ||||
|   import { TagsFilter } from "../../../Logic/Tags/TagsFilter" | ||||
|   import FreeformInput from "./FreeformInput.svelte" | ||||
|   import Translations from "../../i18n/Translations.js" | ||||
|   import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction" | ||||
|   import { createEventDispatcher, onDestroy } from "svelte" | ||||
|   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" | ||||
|   import SpecialTranslation from "./SpecialTranslation.svelte" | ||||
|   import TagHint from "../TagHint.svelte" | ||||
|   import LoginToggle from "../../Base/LoginToggle.svelte" | ||||
|   import SubtleButton from "../../Base/SubtleButton.svelte" | ||||
|   import Loading from "../../Base/Loading.svelte" | ||||
|   import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte" | ||||
|   import { Translation } from "../../i18n/Translation" | ||||
|   import Constants from "../../../Models/Constants" | ||||
|   import { Unit } from "../../../Models/Unit" | ||||
|   import UserRelatedState from "../../../Logic/State/UserRelatedState" | ||||
|   import { twJoin } from "tailwind-merge" | ||||
|   import { TagUtils } from "../../../Logic/Tags/TagUtils" | ||||
| 
 | ||||
|   export let config: TagRenderingConfig; | ||||
|   export let tags: UIEventSource<Record<string, string>>; | ||||
|   export let selectedElement: Feature; | ||||
|   export let state: SpecialVisualizationState; | ||||
|   export let layer: LayerConfig; | ||||
|   export let config: TagRenderingConfig | ||||
|   export let tags: UIEventSource<Record<string, string>> | ||||
|   export let selectedElement: Feature | ||||
|   export let state: SpecialVisualizationState | ||||
|   export let layer: LayerConfig | ||||
| 
 | ||||
|   let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined); | ||||
|   let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined) | ||||
| 
 | ||||
|   let unit: Unit = layer.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key)); | ||||
|   let unit: Unit = layer.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key)) | ||||
| 
 | ||||
|   // Will be bound if a freeform is available | ||||
|   let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key]); | ||||
|   let selectedMapping: number = undefined; | ||||
|   let checkedMappings: boolean[]; | ||||
|   let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key]) | ||||
|   let selectedMapping: number = undefined | ||||
|   let checkedMappings: boolean[] | ||||
| 
 | ||||
|   /** | ||||
|    * Prepares and fills the checkedMappings | ||||
|  | @ -45,38 +45,39 @@ | |||
|   function initialize(tgs: Record<string, string>, confg: TagRenderingConfig) { | ||||
|     mappings = confg.mappings?.filter((m) => { | ||||
|       if (typeof m.hideInAnswer === "boolean") { | ||||
|         return !m.hideInAnswer; | ||||
|         return !m.hideInAnswer | ||||
|       } | ||||
|       return !m.hideInAnswer.matchesProperties(tgs); | ||||
|     }); | ||||
|       return !m.hideInAnswer.matchesProperties(tgs) | ||||
|     }) | ||||
|     // We received a new config -> reinit | ||||
|     unit = layer.units.find((unit) => unit.appliesToKeys.has(confg.freeform?.key)); | ||||
|     unit = layer.units.find((unit) => unit.appliesToKeys.has(confg.freeform?.key)) | ||||
| 
 | ||||
|     if ( | ||||
|       confg.mappings?.length > 0 && | ||||
|       (checkedMappings === undefined || checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0)) | ||||
|       (checkedMappings === undefined || | ||||
|         checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0)) | ||||
|     ) { | ||||
|       const seenFreeforms = []; | ||||
|       const seenFreeforms = [] | ||||
|       TagUtils.FlattenMultiAnswer() | ||||
|       checkedMappings = [ | ||||
|         ...confg.mappings.map((mapping) => { | ||||
|           const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs)  | ||||
|           const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs) | ||||
|           if (matches && confg.freeform) { | ||||
|             const newProps = TagUtils.changeAsProperties(mapping.if.asChange()); | ||||
|             seenFreeforms.push(newProps[confg.freeform.key]); | ||||
|             const newProps = TagUtils.changeAsProperties(mapping.if.asChange()) | ||||
|             seenFreeforms.push(newProps[confg.freeform.key]) | ||||
|           } | ||||
|           return matches; | ||||
|         }) | ||||
|       ]; | ||||
|        | ||||
|       if(tgs !== undefined && confg.freeform){ | ||||
|           return matches | ||||
|         }), | ||||
|       ] | ||||
| 
 | ||||
|       if (tgs !== undefined && confg.freeform) { | ||||
|         const unseenFreeformValues = tgs[confg.freeform.key]?.split(";") ?? [] | ||||
|         for (const seenFreeform of seenFreeforms) { | ||||
|           if(!seenFreeform){ | ||||
|           if (!seenFreeform) { | ||||
|             continue | ||||
|           } | ||||
|           const index = unseenFreeformValues.indexOf(seenFreeform) | ||||
|           if(index < 0){ | ||||
|           if (index < 0) { | ||||
|             continue | ||||
|           } | ||||
|           unseenFreeformValues.splice(index, 1) | ||||
|  | @ -85,29 +86,27 @@ | |||
|         freeformInput.setData(unseenFreeformValues.join(";")) | ||||
|         checkedMappings.push(unseenFreeformValues.length > 0) | ||||
|       } | ||||
|        | ||||
|        | ||||
|     } | ||||
|     if (confg.freeform?.key) { | ||||
|       if (!confg.multiAnswer) { | ||||
|         // Somehow, setting multi-answer freeform values is broken if this is not set | ||||
|         freeformInput.setData(tgs[confg.freeform.key]); | ||||
|         freeformInput.setData(tgs[confg.freeform.key]) | ||||
|       } | ||||
|     } else { | ||||
|       freeformInput.setData(undefined); | ||||
|       freeformInput.setData(undefined) | ||||
|     } | ||||
|     feedback.setData(undefined); | ||||
|     feedback.setData(undefined) | ||||
|   } | ||||
| 
 | ||||
|   $: { | ||||
|     // Even though 'config' is not declared as a store, Svelte uses it as one to update the component | ||||
|     // We want to (re)-initialize whenever the 'tags' or 'config' change - but not when 'checkedConfig' changes | ||||
|     initialize($tags, config); | ||||
|     initialize($tags, config) | ||||
|   } | ||||
|   export let selectedTags: TagsFilter = undefined; | ||||
|   export let selectedTags: TagsFilter = undefined | ||||
| 
 | ||||
|   let mappings: Mapping[] = config?.mappings; | ||||
|   let searchTerm: UIEventSource<string> = new UIEventSource(""); | ||||
|   let mappings: Mapping[] = config?.mappings | ||||
|   let searchTerm: UIEventSource<string> = new UIEventSource("") | ||||
| 
 | ||||
|   $: { | ||||
|     try { | ||||
|  | @ -116,10 +115,10 @@ | |||
|         selectedMapping, | ||||
|         checkedMappings, | ||||
|         tags.data | ||||
|       ); | ||||
|       ) | ||||
|     } catch (e) { | ||||
|       console.error("Could not calculate changeSpecification:", e); | ||||
|       selectedTags = undefined; | ||||
|       console.error("Could not calculate changeSpecification:", e) | ||||
|       selectedTags = undefined | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | @ -128,53 +127,53 @@ | |||
|       config: TagRenderingConfig | ||||
|       applied: TagsFilter | ||||
|     } | ||||
|   }>(); | ||||
|   }>() | ||||
| 
 | ||||
|   function onSave() { | ||||
|     if (selectedTags === undefined) { | ||||
|       return; | ||||
|       return | ||||
|     } | ||||
|     if (layer.source === null) { | ||||
|       /** | ||||
|        * This is a special, priviliged layer. | ||||
|        * We simply apply the tags onto the records | ||||
|        */ | ||||
|       const kv = selectedTags.asChange(tags.data); | ||||
|       const kv = selectedTags.asChange(tags.data) | ||||
|       for (const { k, v } of kv) { | ||||
|         if (v === undefined) { | ||||
|           delete tags.data[k]; | ||||
|           delete tags.data[k] | ||||
|         } else { | ||||
|           tags.data[k] = v; | ||||
|           tags.data[k] = v | ||||
|         } | ||||
|       } | ||||
|       tags.ping(); | ||||
|       return; | ||||
|       tags.ping() | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     dispatch("saved", { config, applied: selectedTags }); | ||||
|     dispatch("saved", { config, applied: selectedTags }) | ||||
|     const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, { | ||||
|       theme: state.layout.id, | ||||
|       changeType: "answer" | ||||
|     }); | ||||
|     freeformInput.setData(undefined); | ||||
|     selectedMapping = undefined; | ||||
|     selectedTags = undefined; | ||||
|       changeType: "answer", | ||||
|     }) | ||||
|     freeformInput.setData(undefined) | ||||
|     selectedMapping = undefined | ||||
|     selectedTags = undefined | ||||
| 
 | ||||
|     change | ||||
|       .CreateChangeDescriptions() | ||||
|       .then((changes) => state.changes.applyChanges(changes)) | ||||
|       .catch(console.error); | ||||
|       .catch(console.error) | ||||
|   } | ||||
| 
 | ||||
|   let featureSwitchIsTesting = state.featureSwitchIsTesting; | ||||
|   let featureSwitchIsDebugging = state.featureSwitches.featureSwitchIsDebugging; | ||||
|   let showTags = state.userRelatedState.showTags; | ||||
|   let numberOfCs = state.osmConnection.userDetails.data.csCount; | ||||
|   let featureSwitchIsTesting = state.featureSwitchIsTesting | ||||
|   let featureSwitchIsDebugging = state.featureSwitches.featureSwitchIsDebugging | ||||
|   let showTags = state.userRelatedState.showTags | ||||
|   let numberOfCs = state.osmConnection.userDetails.data.csCount | ||||
|   onDestroy( | ||||
|     state.osmConnection.userDetails.addCallbackAndRun((ud) => { | ||||
|       numberOfCs = ud.csCount; | ||||
|       numberOfCs = ud.csCount | ||||
|     }) | ||||
|   ); | ||||
|   ) | ||||
| </script> | ||||
| 
 | ||||
| {#if config.question !== undefined} | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| <script lang="ts"> | ||||
|   import Svg from "../Svg"; | ||||
|   import Loading from "./Base/Loading.svelte"; | ||||
|   import ToSvelte from "./Base/ToSvelte.svelte"; | ||||
|   import Svg from "../Svg" | ||||
|   import Loading from "./Base/Loading.svelte" | ||||
|   import ToSvelte from "./Base/ToSvelte.svelte" | ||||
| </script> | ||||
| 
 | ||||
| <div> | ||||
|  | @ -43,12 +43,8 @@ | |||
|         Main action (disabled) | ||||
|       </button> | ||||
| 
 | ||||
|       <button class="small primary"> | ||||
|         Small button | ||||
|       </button> | ||||
|       <button class="small primary disabled"> | ||||
|         Small, disabled button | ||||
|       </button> | ||||
|       <button class="small primary">Small button</button> | ||||
|       <button class="small primary disabled">Small, disabled button</button> | ||||
|     </div> | ||||
|     <div class="flex"> | ||||
|       <button> | ||||
|  |  | |||
|  | @ -1,23 +1,22 @@ | |||
| <script lang="ts"> | ||||
|   import type { FullWikipediaDetails } from "../../Logic/Web/Wikipedia"; | ||||
|   import { Store } from "../../Logic/UIEventSource"; | ||||
|   import FromHtml from "../Base/FromHtml.svelte"; | ||||
|   import Loading from "../Base/Loading.svelte"; | ||||
|   import { Disclosure, DisclosureButton, DisclosurePanel } from "@rgossiaux/svelte-headlessui"; | ||||
|   import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import ToSvelte from "../Base/ToSvelte.svelte"; | ||||
|   import WikidataPreviewBox from "./WikidataPreviewBox"; | ||||
|   import Tr from "../Base/Tr.svelte"; | ||||
|   import Translations from "../i18n/Translations"; | ||||
|   import type { FullWikipediaDetails } from "../../Logic/Web/Wikipedia" | ||||
|   import { Store } from "../../Logic/UIEventSource" | ||||
|   import FromHtml from "../Base/FromHtml.svelte" | ||||
|   import Loading from "../Base/Loading.svelte" | ||||
|   import { Disclosure, DisclosureButton, DisclosurePanel } from "@rgossiaux/svelte-headlessui" | ||||
|   import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid" | ||||
|   import ToSvelte from "../Base/ToSvelte.svelte" | ||||
|   import WikidataPreviewBox from "./WikidataPreviewBox" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import Translations from "../i18n/Translations" | ||||
| 
 | ||||
|   /** | ||||
|    * Shows a wikipedia-article + wikidata preview for the given item | ||||
|    */ | ||||
|   export let wikipediaDetails: Store<FullWikipediaDetails>; | ||||
|   export let wikipediaDetails: Store<FullWikipediaDetails> | ||||
| </script> | ||||
| 
 | ||||
| {#if $wikipediaDetails.articleUrl} | ||||
| 
 | ||||
|   <a class="flex" href={$wikipediaDetails.articleUrl} rel="noreferrer" target="_blank"> | ||||
|     <img class="h-6 w-6" src="./assets/svg/wikipedia.svg" /> | ||||
|     <Tr t={Translations.t.general.wikipedia.fromWikipedia} /> | ||||
|  | @ -29,7 +28,6 @@ | |||
| {/if} | ||||
| 
 | ||||
| {#if $wikipediaDetails.articleUrl} | ||||
| 
 | ||||
|   {#if $wikipediaDetails.firstParagraph === "" || $wikipediaDetails.firstParagraph === undefined} | ||||
|     <Loading> | ||||
|       <Tr t={Translations.t.general.wikipedia.loading} /> | ||||
|  |  | |||
|  | @ -2,28 +2,28 @@ | |||
|   /** | ||||
|    * Shows one or more wikidata info boxes or wikipedia articles in a tabbed component. | ||||
|    */ | ||||
|   import type { FullWikipediaDetails } from "../../Logic/Web/Wikipedia"; | ||||
|   import Wikipedia from "../../Logic/Web/Wikipedia"; | ||||
|   import Locale from "../i18n/Locale"; | ||||
|   import { Store } from "../../Logic/UIEventSource"; | ||||
|   import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui"; | ||||
|   import WikipediaTitle from "./WikipediaTitle.svelte"; | ||||
|   import WikipediaArticle from "./WikipediaArticle.svelte"; | ||||
|   import { onDestroy } from "svelte"; | ||||
|   import type { FullWikipediaDetails } from "../../Logic/Web/Wikipedia" | ||||
|   import Wikipedia from "../../Logic/Web/Wikipedia" | ||||
|   import Locale from "../i18n/Locale" | ||||
|   import { Store } from "../../Logic/UIEventSource" | ||||
|   import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui" | ||||
|   import WikipediaTitle from "./WikipediaTitle.svelte" | ||||
|   import WikipediaArticle from "./WikipediaArticle.svelte" | ||||
|   import { onDestroy } from "svelte" | ||||
| 
 | ||||
|   /** | ||||
|    * Either a wikidata item or a '<language>:<article>' link | ||||
|    */ | ||||
|   export let wikiIds: Store<string[]>; | ||||
|   export let wikiIds: Store<string[]> | ||||
|   let wikipediaStores: Store<Store<FullWikipediaDetails>[]> = Locale.language.bind((language) => | ||||
|     wikiIds?.map((wikiIds) => wikiIds?.map((id) => Wikipedia.fetchArticleAndWikidata(id, language))) | ||||
|   ); | ||||
|   let _wikipediaStores; | ||||
|   let _wikipediaStores | ||||
|   onDestroy( | ||||
|     wikipediaStores.addCallbackAndRunD((wikipediaStores) => { | ||||
|       _wikipediaStores = wikipediaStores; | ||||
|       _wikipediaStores = wikipediaStores | ||||
|     }) | ||||
|   ); | ||||
|   ) | ||||
| </script> | ||||
| 
 | ||||
| {#if _wikipediaStores !== undefined} | ||||
|  | @ -50,15 +50,15 @@ | |||
| {/if} | ||||
| 
 | ||||
| <style> | ||||
|     /* Actually used, don't remove*/ | ||||
|     .tab-selected { | ||||
|         background-color: rgb(59 130 246); | ||||
|         color: rgb(255 255 255); | ||||
|     } | ||||
|   /* Actually used, don't remove*/ | ||||
|   .tab-selected { | ||||
|     background-color: rgb(59 130 246); | ||||
|     color: rgb(255 255 255); | ||||
|   } | ||||
| 
 | ||||
|     /* Actually used, don't remove*/ | ||||
|     .tab-unselected { | ||||
|         background-color: rgb(255 255 255); | ||||
|         color: rgb(0 0 0); | ||||
|     } | ||||
|   /* Actually used, don't remove*/ | ||||
|   .tab-unselected { | ||||
|     background-color: rgb(255 255 255); | ||||
|     color: rgb(0 0 0); | ||||
|   } | ||||
| </style> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue