forked from MapComplete/MapComplete
		
	Allow to delete freeform keys again, partial fix of #2008
This commit is contained in:
		
							parent
							
								
									4df2d34f02
								
							
						
					
					
						commit
						d8da61ec07
					
				
					 5 changed files with 56 additions and 17 deletions
				
			
		| 
						 | 
					@ -133,7 +133,16 @@ export interface MappingConfigJson {
 | 
				
			||||||
     * question: What extra tags should be added to the object if this object is chosen?
 | 
					     * question: What extra tags should be added to the object if this object is chosen?
 | 
				
			||||||
     * type: simple_tag
 | 
					     * type: simple_tag
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * If chosen as answer, these tags will be applied onto the object, together with the tags from the `if`
 | 
					     * If chosen as answer, these tags will be applied onto the object, together with the tags from the `if`.
 | 
				
			||||||
 | 
					     * Note that if the contributor picks this mapping, saves and then changes their mind and uses a different mapping,
 | 
				
			||||||
 | 
					     * the extraTags will reside.
 | 
				
			||||||
 | 
					     * E.g. when picking `memorial:type=bench`, then `amenity=bench` will also be applied.
 | 
				
			||||||
 | 
					     * If someone later on changes the type to `memorial:statue`, `amenity=bench` will stay onto the object
 | 
				
			||||||
 | 
					     * (which is the desired behaviour, see e.g. for https://www.openstreetmap.org/node/5620038478)
 | 
				
			||||||
 | 
					     * Use 'ifNot' to explicitly remove an tag if this is important
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * If someone marks the question as 'unknown', the extra tags will not be erased
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
     * Not compatible with multiAnswer.
 | 
					     * Not compatible with multiAnswer.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * This can be used e.g. to erase other keys which indicate the 'not' value:
 | 
					     * This can be used e.g. to erase other keys which indicate the 'not' value:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -911,6 +911,24 @@ export default class TagRenderingConfig {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Utils.NoNull(tags)
 | 
					        return Utils.NoNull(tags)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The keys that should be erased if one has to revert to 'unknown'.
 | 
				
			||||||
 | 
					     * Might give undefined
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public settableKeys(): string[] | undefined {
 | 
				
			||||||
 | 
					        const toDelete = new Set<string>()
 | 
				
			||||||
 | 
					        if(this.freeform){
 | 
				
			||||||
 | 
					            toDelete.add(this.freeform.key)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (const mapping of this.mappings) {
 | 
				
			||||||
 | 
					            for (const usedKey of mapping.if.usedKeys()) {
 | 
				
			||||||
 | 
					                toDelete.add(usedKey)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Array.from(toDelete)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class TagRenderingConfigUtils {
 | 
					export class TagRenderingConfigUtils {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,7 @@
 | 
				
			||||||
  import { And } from "../../../Logic/Tags/And"
 | 
					  import { And } from "../../../Logic/Tags/And"
 | 
				
			||||||
  import { get } from "svelte/store"
 | 
					  import { get } from "svelte/store"
 | 
				
			||||||
  import Markdown from "../../Base/Markdown.svelte"
 | 
					  import Markdown from "../../Base/Markdown.svelte"
 | 
				
			||||||
 | 
					  import { Utils } from "../../../Utils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let config: TagRenderingConfig
 | 
					  export let config: TagRenderingConfig
 | 
				
			||||||
  export let tags: UIEventSource<Record<string, string>>
 | 
					  export let tags: UIEventSource<Record<string, string>>
 | 
				
			||||||
| 
						 | 
					@ -42,7 +43,7 @@
 | 
				
			||||||
  export let selectedTags: UploadableTag = undefined
 | 
					  export let selectedTags: UploadableTag = undefined
 | 
				
			||||||
  export let extraTags: UIEventSource<Record<string, string>> = new UIEventSource({})
 | 
					  export let extraTags: UIEventSource<Record<string, string>> = new UIEventSource({})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let allowDeleteOfFreeform: boolean = false
 | 
					  export let allowDeleteOfFreeform: boolean = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let clss = "interactive border-interactive"
 | 
					  export let clss = "interactive border-interactive"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -141,7 +142,9 @@
 | 
				
			||||||
    feedback.setData(undefined)
 | 
					    feedback.setData(undefined)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let usedKeys: string[] = config.usedTags().flatMap((t) => t.usedKeys())
 | 
					  let usedKeys: string[] = Utils.Dedup(config.usedTags().flatMap((t) => t.usedKeys()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let keysToDeleteOnUnknown = config.settableKeys()
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * The 'minimalTags' is a subset of the tags of the feature, only containing the values relevant for this object.
 | 
					   * The 'minimalTags' is a subset of the tags of the feature, only containing the values relevant for this object.
 | 
				
			||||||
   * The main goal is to be stable and only 'ping' when an actual change is relevant
 | 
					   * The main goal is to be stable and only 'ping' when an actual change is relevant
 | 
				
			||||||
| 
						 | 
					@ -193,10 +196,12 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $: {
 | 
					  $: {
 | 
				
			||||||
    if (
 | 
					    if (
 | 
				
			||||||
 | 
					      config.freeform?.key &&
 | 
				
			||||||
      allowDeleteOfFreeform &&
 | 
					      allowDeleteOfFreeform &&
 | 
				
			||||||
      $freeformInput === undefined &&
 | 
					      !$freeformInput &&
 | 
				
			||||||
      $freeformInputUnvalidated === "" &&
 | 
					      !$freeformInputUnvalidated &&
 | 
				
			||||||
      (config?.mappings?.length ?? 0) === 0
 | 
					      !checkedMappings?.some(m => m) &&
 | 
				
			||||||
 | 
					      $tags[config.freeform.key] // We need to have a current value in order to delete it
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
      selectedTags = new Tag(config.freeform.key, "")
 | 
					      selectedTags = new Tag(config.freeform.key, "")
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
| 
						 | 
					@ -360,7 +365,7 @@
 | 
				
			||||||
          {/if}
 | 
					          {/if}
 | 
				
			||||||
        {/if}
 | 
					        {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {#if config.freeform?.key && !(config?.mappings?.filter((m) => m.hideInAnswer != true)?.length > 0)}
 | 
					        {#if config?.freeform?.key && !(config?.mappings?.filter((m) => m.hideInAnswer != true)?.length > 0)}
 | 
				
			||||||
          <!-- There are no options to choose from, simply show the input element: fill out the text field -->
 | 
					          <!-- There are no options to choose from, simply show the input element: fill out the text field -->
 | 
				
			||||||
          <FreeformInput
 | 
					          <FreeformInput
 | 
				
			||||||
            {config}
 | 
					            {config}
 | 
				
			||||||
| 
						 | 
					@ -480,6 +485,9 @@
 | 
				
			||||||
              <Tr t={$feedback} />
 | 
					              <Tr t={$feedback} />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          {/if}
 | 
					          {/if}
 | 
				
			||||||
 | 
					          <!--{#if keysToDeleteOnUnknown?.some(k => !! $tags[k])}
 | 
				
			||||||
 | 
					            Mark as unknown (delete {keysToDeleteOnUnknown?.filter(k => !! $tags[k]).join(";")})
 | 
				
			||||||
 | 
					            {/if}-->
 | 
				
			||||||
          <div
 | 
					          <div
 | 
				
			||||||
            class="sticky bottom-0 flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap"
 | 
					            class="sticky bottom-0 flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap"
 | 
				
			||||||
            style="z-index: 11"
 | 
					            style="z-index: 11"
 | 
				
			||||||
| 
						 | 
					@ -487,7 +495,7 @@
 | 
				
			||||||
            <!-- TagRenderingQuestion-buttons -->
 | 
					            <!-- TagRenderingQuestion-buttons -->
 | 
				
			||||||
            <slot name="cancel" />
 | 
					            <slot name="cancel" />
 | 
				
			||||||
            <slot name="save-button" {selectedTags}>
 | 
					            <slot name="save-button" {selectedTags}>
 | 
				
			||||||
              {#if allowDeleteOfFreeform && (config?.mappings?.length ?? 0) === 0 && $freeformInput === undefined && $freeformInputUnvalidated === ""}
 | 
					              {#if config.freeform?.key && allowDeleteOfFreeform && !checkedMappings?.some(m => m) && !$freeformInput && !$freeformInputUnvalidated && $tags[config.freeform.key]}
 | 
				
			||||||
                <button
 | 
					                <button
 | 
				
			||||||
                  class="primary flex"
 | 
					                  class="primary flex"
 | 
				
			||||||
                  on:click|stopPropagation|preventDefault={() => onSave()}
 | 
					                  on:click|stopPropagation|preventDefault={() => onSave()}
 | 
				
			||||||
| 
						 | 
					@ -513,12 +521,14 @@
 | 
				
			||||||
              <TagHint {state} tags={selectedTags} currentProperties={$tags} />
 | 
					              <TagHint {state} tags={selectedTags} currentProperties={$tags} />
 | 
				
			||||||
              <span class="flex flex-wrap">
 | 
					              <span class="flex flex-wrap">
 | 
				
			||||||
                {#if $featureSwitchIsTesting}
 | 
					                {#if $featureSwitchIsTesting}
 | 
				
			||||||
                  <button class="small" on:click={() => console.log("Configuration is ", config)}>
 | 
					                  <div class="alert">
 | 
				
			||||||
                    Testmode  
 | 
					                    Testmode  
 | 
				
			||||||
                  </button>
 | 
					                  </div>
 | 
				
			||||||
                {/if}
 | 
					                {/if}
 | 
				
			||||||
                {#if $featureSwitchIsTesting || $featureSwitchIsDebugging}
 | 
					                {#if $featureSwitchIsTesting || $featureSwitchIsDebugging}
 | 
				
			||||||
                  <span class="subtle">{config.id}</span>
 | 
					                  <a class="small" on:click={() => console.log("Configuration is ", config)}>
 | 
				
			||||||
 | 
					                  {config.id}
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
                {/if}
 | 
					                {/if}
 | 
				
			||||||
              </span>
 | 
					              </span>
 | 
				
			||||||
            </span>
 | 
					            </span>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,7 @@
 | 
				
			||||||
  export let selectedTags: UploadableTag = undefined
 | 
					  export let selectedTags: UploadableTag = undefined
 | 
				
			||||||
  export let extraTags: UIEventSource<Record<string, string>> = new UIEventSource({})
 | 
					  export let extraTags: UIEventSource<Record<string, string>> = new UIEventSource({})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let allowDeleteOfFreeform: boolean = false
 | 
					  export let allowDeleteOfFreeform: boolean = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement)
 | 
					  let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement)
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/Utils.ts
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								src/Utils.ts
									
										
									
									
									
								
							| 
						 | 
					@ -382,13 +382,15 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Creates a new array with all elements from 'arr' in such a way that every element will be kept only once
 | 
					     * Creates a new array with all elements from 'arr' in such a way that every element will be kept only once
 | 
				
			||||||
     * Elements are returned in the same order as they appear in the lists
 | 
					     * Elements are returned in the same order as they appear in the lists.
 | 
				
			||||||
     * @param arr
 | 
					     * Null/Undefined is returned as is. If an emtpy array is given, a new empty array will be returned
 | 
				
			||||||
     * @constructor
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					    public static Dedup(arr: NonNullable<string[]>): NonNullable<string[]>
 | 
				
			||||||
 | 
					    public static Dedup(arr: undefined):undefined
 | 
				
			||||||
 | 
					    public static Dedup(arr: string[] | undefined): string[] | undefined
 | 
				
			||||||
    public static Dedup(arr: string[]): string[] {
 | 
					    public static Dedup(arr: string[]): string[] {
 | 
				
			||||||
        if (arr === undefined) {
 | 
					        if (arr === undefined || arr === null) {
 | 
				
			||||||
            return undefined
 | 
					            return arr
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const newArr = []
 | 
					        const newArr = []
 | 
				
			||||||
        for (const string of arr) {
 | 
					        for (const string of arr) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue