forked from MapComplete/MapComplete
		
	Simplify priority behaviour of PillsSelector, create LanguageChooser-element, update usages
This commit is contained in:
		
							parent
							
								
									efb54782ca
								
							
						
					
					
						commit
						84eee064b2
					
				
					 7 changed files with 385 additions and 162 deletions
				
			
		| 
						 | 
					@ -25,9 +25,7 @@ import Toggleable from "../Base/Toggleable"
 | 
				
			||||||
import Lazy from "../Base/Lazy"
 | 
					import Lazy from "../Base/Lazy"
 | 
				
			||||||
import LinkToWeblate from "../Base/LinkToWeblate"
 | 
					import LinkToWeblate from "../Base/LinkToWeblate"
 | 
				
			||||||
import Link from "../Base/Link"
 | 
					import Link from "../Base/Link"
 | 
				
			||||||
import { SearchablePillsSelector } from "../Input/SearchableMappingsSelector"
 | 
					import {AllLanguagesSelector} from "../Popup/AllLanguagesSelector";
 | 
				
			||||||
import * as languages from "../../assets/language_translations.json"
 | 
					 | 
				
			||||||
import { Translation } from "../i18n/Translation"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SelectTemplate extends Combine implements FlowStep<{ title: string; pages: string[] }> {
 | 
					class SelectTemplate extends Combine implements FlowStep<{ title: string; pages: string[] }> {
 | 
				
			||||||
    readonly IsValid: Store<boolean>
 | 
					    readonly IsValid: Store<boolean>
 | 
				
			||||||
| 
						 | 
					@ -203,23 +201,7 @@ class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf; langu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(title: string, pages: string[], options: SvgToPdfOptions) {
 | 
					    constructor(title: string, pages: string[], options: SvgToPdfOptions) {
 | 
				
			||||||
        const svgToPdf = new SvgToPdf(title, pages, options)
 | 
					        const svgToPdf = new SvgToPdf(title, pages, options)
 | 
				
			||||||
        const languageOptions = [
 | 
					        const languageSelector = new AllLanguagesSelector(          )
 | 
				
			||||||
            new FixedInputElement("Nederlands", "nl"),
 | 
					 | 
				
			||||||
            new FixedInputElement("English", "en"),
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
        const langs: string[] = Array.from(Object.keys(languages["default"] ?? languages))
 | 
					 | 
				
			||||||
        console.log("Available languages are:", langs)
 | 
					 | 
				
			||||||
        const languageSelector = new SearchablePillsSelector(
 | 
					 | 
				
			||||||
            langs.map((l) => ({
 | 
					 | 
				
			||||||
                show: new Translation(languages[l]),
 | 
					 | 
				
			||||||
                value: l,
 | 
					 | 
				
			||||||
                mainTerm: languages[l],
 | 
					 | 
				
			||||||
            })),
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                mode: "select-many",
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const isPrepared = UIEventSource.FromPromiseWithErr(svgToPdf.Prepare())
 | 
					        const isPrepared = UIEventSource.FromPromiseWithErr(svgToPdf.Prepare())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        super([
 | 
					        super([
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,11 +13,13 @@ import { VariableUiElement } from "../Base/VariableUIElement"
 | 
				
			||||||
 * A single 'pill' which can hide itself if the search criteria is not met
 | 
					 * A single 'pill' which can hide itself if the search criteria is not met
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class SelfHidingToggle extends UIElement implements InputElement<boolean> {
 | 
					class SelfHidingToggle extends UIElement implements InputElement<boolean> {
 | 
				
			||||||
    private readonly _shown: BaseUIElement
 | 
					 | 
				
			||||||
    public readonly _selected: UIEventSource<boolean>
 | 
					    public readonly _selected: UIEventSource<boolean>
 | 
				
			||||||
    public readonly isShown: Store<boolean> = new UIEventSource<boolean>(true)
 | 
					    public readonly isShown: Store<boolean> = new UIEventSource<boolean>(true)
 | 
				
			||||||
 | 
					    public readonly matchesSearchCriteria: Store<boolean>
 | 
				
			||||||
    public readonly forceSelected: UIEventSource<boolean>
 | 
					    public readonly forceSelected: UIEventSource<boolean>
 | 
				
			||||||
 | 
					    private readonly _shown: BaseUIElement
 | 
				
			||||||
    private readonly _squared: boolean
 | 
					    private readonly _squared: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public constructor(
 | 
					    public constructor(
 | 
				
			||||||
        shown: string | BaseUIElement,
 | 
					        shown: string | BaseUIElement,
 | 
				
			||||||
        mainTerm: Record<string, string>,
 | 
					        mainTerm: Record<string, string>,
 | 
				
			||||||
| 
						 | 
					@ -26,7 +28,9 @@ class SelfHidingToggle extends UIElement implements InputElement<boolean> {
 | 
				
			||||||
            searchTerms?: Record<string, string[]>
 | 
					            searchTerms?: Record<string, string[]>
 | 
				
			||||||
            selected?: UIEventSource<boolean>
 | 
					            selected?: UIEventSource<boolean>
 | 
				
			||||||
            forceSelected?: UIEventSource<boolean>
 | 
					            forceSelected?: UIEventSource<boolean>
 | 
				
			||||||
            squared?: boolean
 | 
					            squared?: boolean,
 | 
				
			||||||
 | 
					            /* Hide, if not selected*/
 | 
				
			||||||
 | 
					            hide?: Store<boolean>
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        super()
 | 
					        super()
 | 
				
			||||||
| 
						 | 
					@ -49,14 +53,11 @@ class SelfHidingToggle extends UIElement implements InputElement<boolean> {
 | 
				
			||||||
        const selected = (this._selected = options?.selected ?? new UIEventSource<boolean>(false))
 | 
					        const selected = (this._selected = options?.selected ?? new UIEventSource<boolean>(false))
 | 
				
			||||||
        const forceSelected = (this.forceSelected =
 | 
					        const forceSelected = (this.forceSelected =
 | 
				
			||||||
            options?.forceSelected ?? new UIEventSource<boolean>(false))
 | 
					            options?.forceSelected ?? new UIEventSource<boolean>(false))
 | 
				
			||||||
        this.isShown = search.map(
 | 
					        this.matchesSearchCriteria = search.map(s => {
 | 
				
			||||||
            (s) => {
 | 
					 | 
				
			||||||
            if (s === undefined || s.length === 0) {
 | 
					            if (s === undefined || s.length === 0) {
 | 
				
			||||||
                return true
 | 
					                return true
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                if (selected.data && !forceSelected.data) {
 | 
					
 | 
				
			||||||
                    return true
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            s = s?.trim()?.toLowerCase()
 | 
					            s = s?.trim()?.toLowerCase()
 | 
				
			||||||
            if (searchTerms[Locale.language.data]?.some((t) => t.indexOf(s) >= 0)) {
 | 
					            if (searchTerms[Locale.language.data]?.some((t) => t.indexOf(s) >= 0)) {
 | 
				
			||||||
                return true
 | 
					                return true
 | 
				
			||||||
| 
						 | 
					@ -65,8 +66,18 @@ class SelfHidingToggle extends UIElement implements InputElement<boolean> {
 | 
				
			||||||
                return true
 | 
					                return true
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return false
 | 
					            return false
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        this.isShown = this.matchesSearchCriteria.map(
 | 
				
			||||||
 | 
					            (matchesSearch) => {
 | 
				
			||||||
 | 
					                if (selected.data && !forceSelected.data) {
 | 
				
			||||||
 | 
					                    return true
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (options?.hide?.data) {
 | 
				
			||||||
 | 
					                    return false
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return matchesSearch
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            [selected, Locale.language]
 | 
					            [selected, Locale.language, options?.hide]
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const self = this
 | 
					        const self = this
 | 
				
			||||||
| 
						 | 
					@ -128,13 +139,12 @@ class SelfHidingToggle extends UIElement implements InputElement<boolean> {
 | 
				
			||||||
 * A searchfield can be used to filter the values
 | 
					 * A searchfield can be used to filter the values
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export class SearchablePillsSelector<T> extends Combine implements InputElement<T[]> {
 | 
					export class SearchablePillsSelector<T> extends Combine implements InputElement<T[]> {
 | 
				
			||||||
    private readonly selectedElements: UIEventSource<T[]>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public readonly someMatchFound: Store<boolean>
 | 
					    public readonly someMatchFound: Store<boolean>
 | 
				
			||||||
 | 
					    private readonly selectedElements: UIEventSource<T[]>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param values
 | 
					     * @param values: the values that can be selected
 | 
				
			||||||
     * @param options
 | 
					     * @param options
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
| 
						 | 
					@ -142,20 +152,39 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement<
 | 
				
			||||||
            show: BaseUIElement
 | 
					            show: BaseUIElement
 | 
				
			||||||
            value: T
 | 
					            value: T
 | 
				
			||||||
            mainTerm: Record<string, string>
 | 
					            mainTerm: Record<string, string>
 | 
				
			||||||
            searchTerms?: Record<string, string[]>
 | 
					            searchTerms?: Record<string, string[]>,
 | 
				
			||||||
 | 
					            /* If there are more then 200 elements, should this element still be shown? */
 | 
				
			||||||
 | 
					            hasPriority?: Store<boolean>
 | 
				
			||||||
        }[],
 | 
					        }[],
 | 
				
			||||||
        options?: {
 | 
					        options?: {
 | 
				
			||||||
 | 
					            /*
 | 
				
			||||||
 | 
					            * If one single value can be selected (like a radio button) or if many values can be selected (like checkboxes)
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
            mode?: "select-one" | "select-many"
 | 
					            mode?: "select-one" | "select-many"
 | 
				
			||||||
 | 
					            /**
 | 
				
			||||||
 | 
					             * The values of the selected elements.
 | 
				
			||||||
 | 
					             * Use this to tie input elements together
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
            selectedElements?: UIEventSource<T[]>
 | 
					            selectedElements?: UIEventSource<T[]>
 | 
				
			||||||
 | 
					            /**
 | 
				
			||||||
 | 
					             * The search bar. Use this to seed the search value or to tie to another value
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
            searchValue?: UIEventSource<string>
 | 
					            searchValue?: UIEventSource<string>
 | 
				
			||||||
 | 
					            /**
 | 
				
			||||||
 | 
					             * What is shown if the search yielded no results.
 | 
				
			||||||
 | 
					             * By default: a translated "no search results"
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
            onNoMatches?: BaseUIElement
 | 
					            onNoMatches?: BaseUIElement
 | 
				
			||||||
 | 
					            /**
 | 
				
			||||||
 | 
					             * An element that is shown if no search is entered
 | 
				
			||||||
 | 
					             * Default behaviour is to show all options
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
            onNoSearchMade?: BaseUIElement
 | 
					            onNoSearchMade?: BaseUIElement
 | 
				
			||||||
            /**
 | 
					            /**
 | 
				
			||||||
             * Shows this if there are many (>200) possible mappings
 | 
					             * Extra element to show if there are many (>200) possible mappings and when non-priority mappings are hidden
 | 
				
			||||||
 | 
					             *
 | 
				
			||||||
             */
 | 
					             */
 | 
				
			||||||
            onManyElements?: BaseUIElement
 | 
					            onManyElements?: BaseUIElement
 | 
				
			||||||
            onManyElementsValue?: UIEventSource<T[]>
 | 
					 | 
				
			||||||
            selectIfSingle?: false | boolean
 | 
					 | 
				
			||||||
            searchAreaClass?: string
 | 
					            searchAreaClass?: string
 | 
				
			||||||
            hideSearchBar?: false | boolean
 | 
					            hideSearchBar?: false | boolean
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -173,7 +202,7 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement<
 | 
				
			||||||
        const selectedElements = options?.selectedElements ?? new UIEventSource<T[]>([])
 | 
					        const selectedElements = options?.selectedElements ?? new UIEventSource<T[]>([])
 | 
				
			||||||
        const mode = options?.mode ?? "select-one"
 | 
					        const mode = options?.mode ?? "select-one"
 | 
				
			||||||
        const onEmpty = options?.onNoMatches ?? Translations.t.general.noMatchingMapping
 | 
					        const onEmpty = options?.onNoMatches ?? Translations.t.general.noMatchingMapping
 | 
				
			||||||
 | 
					        const forceHide = new UIEventSource(false)
 | 
				
			||||||
        const mappedValues: {
 | 
					        const mappedValues: {
 | 
				
			||||||
            show: SelfHidingToggle
 | 
					            show: SelfHidingToggle
 | 
				
			||||||
            mainTerm: Record<string, string>
 | 
					            mainTerm: Record<string, string>
 | 
				
			||||||
| 
						 | 
					@ -209,6 +238,7 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement<
 | 
				
			||||||
                searchTerms: v.searchTerms,
 | 
					                searchTerms: v.searchTerms,
 | 
				
			||||||
                selected: vIsSelected,
 | 
					                selected: vIsSelected,
 | 
				
			||||||
                squared: mode === "select-many",
 | 
					                squared: mode === "select-many",
 | 
				
			||||||
 | 
					                hide: v.hasPriority === undefined ? forceHide : forceHide.map(fh => fh && !v.hasPriority?.data, [v.hasPriority])
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
| 
						 | 
					@ -217,62 +247,18 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement<
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // The total number of elements that would be displayed based on the search criteria alone
 | 
				
			||||||
        let totalShown: Store<number>
 | 
					        let totalShown: Store<number>
 | 
				
			||||||
        if (options.selectIfSingle) {
 | 
					        totalShown = searchValue.map((_) => mappedValues.filter((mv) => mv.show.matchesSearchCriteria.data).length)
 | 
				
			||||||
            let forcedSelection: { value: T; show: SelfHidingToggle } = undefined
 | 
					        const tooMuchElementsCutoff = 40
 | 
				
			||||||
            totalShown = searchValue.map(
 | 
					        totalShown.addCallbackAndRunD(shown => forceHide.setData(tooMuchElementsCutoff < shown))
 | 
				
			||||||
                (_) => {
 | 
					 | 
				
			||||||
                    let totalShown = 0
 | 
					 | 
				
			||||||
                    let lastShownValue: { value: T; show: SelfHidingToggle }
 | 
					 | 
				
			||||||
                    for (const mv of mappedValues) {
 | 
					 | 
				
			||||||
                        const valueIsShown = mv.show.isShown.data
 | 
					 | 
				
			||||||
                        if (valueIsShown) {
 | 
					 | 
				
			||||||
                            totalShown++
 | 
					 | 
				
			||||||
                            lastShownValue = mv
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if (totalShown == 1) {
 | 
					 | 
				
			||||||
                        if (selectedElements.data?.indexOf(lastShownValue.value) < 0) {
 | 
					 | 
				
			||||||
                            selectedElements.setData([lastShownValue.value])
 | 
					 | 
				
			||||||
                            lastShownValue.show.forceSelected.setData(true)
 | 
					 | 
				
			||||||
                            forcedSelection = lastShownValue
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    } else if (forcedSelection != undefined) {
 | 
					 | 
				
			||||||
                        forcedSelection?.show?.forceSelected?.setData(false)
 | 
					 | 
				
			||||||
                        forcedSelection = undefined
 | 
					 | 
				
			||||||
                        selectedElements.setData([])
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    return totalShown
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                mappedValues.map((mv) => mv.show.GetValue())
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            totalShown = searchValue.map(
 | 
					 | 
				
			||||||
                (_) => mappedValues.filter((mv) => mv.show.isShown.data).length,
 | 
					 | 
				
			||||||
                mappedValues.map((mv) => mv.show.GetValue())
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const tooMuchElementsCutoff = 200
 | 
					 | 
				
			||||||
        options?.onManyElementsValue?.map(
 | 
					 | 
				
			||||||
            (value) => {
 | 
					 | 
				
			||||||
                console.log("Installing toMuchElementsValue", value)
 | 
					 | 
				
			||||||
                if (tooMuchElementsCutoff <= totalShown.data) {
 | 
					 | 
				
			||||||
                    selectedElements.setData(value)
 | 
					 | 
				
			||||||
                    selectedElements.ping()
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            [totalShown]
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        super([
 | 
					        super([
 | 
				
			||||||
            searchBar,
 | 
					            searchBar,
 | 
				
			||||||
            new VariableUiElement(
 | 
					            new VariableUiElement(
 | 
				
			||||||
                Locale.language.map(
 | 
					                Locale.language.map(
 | 
				
			||||||
                    (lng) => {
 | 
					                    (lng) => {
 | 
				
			||||||
                        if (totalShown.data >= 200) {
 | 
					
 | 
				
			||||||
                            return options?.onManyElements ?? Translations.t.general.useSearch
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        if (
 | 
					                        if (
 | 
				
			||||||
                            options?.onNoSearchMade !== undefined &&
 | 
					                            options?.onNoSearchMade !== undefined &&
 | 
				
			||||||
                            (searchValue.data === undefined || searchValue.data.length === 0)
 | 
					                            (searchValue.data === undefined || searchValue.data.length === 0)
 | 
				
			||||||
| 
						 | 
					@ -284,9 +270,14 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement<
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        mappedValues.sort((a, b) => (a.mainTerm[lng] < b.mainTerm[lng] ? -1 : 1))
 | 
					                        mappedValues.sort((a, b) => (a.mainTerm[lng] < b.mainTerm[lng] ? -1 : 1))
 | 
				
			||||||
                        return new Combine(mappedValues.map((e) => e.show))
 | 
					                        let pills = new Combine(mappedValues.map((e) => e.show))
 | 
				
			||||||
                            .SetClass("flex flex-wrap w-full content-start")
 | 
					                            .SetClass("flex flex-wrap w-full content-start")
 | 
				
			||||||
                            .SetClass(options?.searchAreaClass ?? "")
 | 
					                            .SetClass(options?.searchAreaClass ?? "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (totalShown.data >= tooMuchElementsCutoff) {
 | 
				
			||||||
 | 
					                            pills = new Combine([options?.onManyElements ?? Translations.t.general.useSearch, pills])
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        return pills
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    [totalShown, searchValue]
 | 
					                    [totalShown, searchValue]
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										45
									
								
								UI/Popup/AllLanguagesSelector.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								UI/Popup/AllLanguagesSelector.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,45 @@
 | 
				
			||||||
 | 
					import {SearchablePillsSelector} from "../Input/SearchableMappingsSelector";
 | 
				
			||||||
 | 
					import {Store} from "../../Logic/UIEventSource";
 | 
				
			||||||
 | 
					import BaseUIElement from "../BaseUIElement";
 | 
				
			||||||
 | 
					import * as all_languages from "../../assets/language_translations.json";
 | 
				
			||||||
 | 
					import {Translation} from "../i18n/Translation";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AllLanguagesSelector extends SearchablePillsSelector <string> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(options?: {
 | 
				
			||||||
 | 
					                    mode?: "select-many" | "select-one"
 | 
				
			||||||
 | 
					                    currentCountry?: Store<string>,
 | 
				
			||||||
 | 
					                    supportedLanguages?: Record<string, string> & { _meta?: { countries?: string[] } }
 | 
				
			||||||
 | 
					                }) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const possibleValues: {
 | 
				
			||||||
 | 
					            show: BaseUIElement
 | 
				
			||||||
 | 
					            value: string
 | 
				
			||||||
 | 
					            mainTerm: Record<string, string>
 | 
				
			||||||
 | 
					            searchTerms?: Record<string, string[]>,
 | 
				
			||||||
 | 
					            hasPriority?: Store<boolean>
 | 
				
			||||||
 | 
					        }[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const langs = options?.supportedLanguages ?? all_languages["default"] ?? all_languages
 | 
				
			||||||
 | 
					        for (const ln in langs) {
 | 
				
			||||||
 | 
					            let languageInfo: Record<string, string> & { _meta?: { countries: string[] } } = all_languages[ln]
 | 
				
			||||||
 | 
					            const countries = languageInfo._meta?.countries?.map(c => c.toLowerCase())
 | 
				
			||||||
 | 
					            languageInfo = {...languageInfo}
 | 
				
			||||||
 | 
					            delete languageInfo._meta
 | 
				
			||||||
 | 
					            const term = {
 | 
				
			||||||
 | 
					                show: new Translation(languageInfo),
 | 
				
			||||||
 | 
					                value: ln,
 | 
				
			||||||
 | 
					                mainTerm: languageInfo,
 | 
				
			||||||
 | 
					                searchTerms: {"*": [ln]},
 | 
				
			||||||
 | 
					                hasPriority: countries === undefined ? undefined : options?.currentCountry?.map(country => countries?.indexOf(country.toLowerCase()) >= 0)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            possibleValues.push(term)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        super(possibleValues,
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                mode: options?.mode ?? 'select-many'
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
 | 
				
			||||||
import { Unit } from "../../Models/Unit"
 | 
					import { Unit } from "../../Models/Unit"
 | 
				
			||||||
import Lazy from "../Base/Lazy"
 | 
					import Lazy from "../Base/Lazy"
 | 
				
			||||||
import { FixedUiElement } from "../Base/FixedUiElement"
 | 
					import { FixedUiElement } from "../Base/FixedUiElement"
 | 
				
			||||||
 | 
					import {EditButton} from "./SaveButton";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class EditableTagRendering extends Toggle {
 | 
					export default class EditableTagRendering extends Toggle {
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
| 
						 | 
					@ -70,16 +71,9 @@ export default class EditableTagRendering extends Toggle {
 | 
				
			||||||
            // We have a question and editing is enabled
 | 
					            // We have a question and editing is enabled
 | 
				
			||||||
            const answerWithEditButton = new Combine([
 | 
					            const answerWithEditButton = new Combine([
 | 
				
			||||||
                answer,
 | 
					                answer,
 | 
				
			||||||
                new Toggle(
 | 
					                new EditButton(state.osmConnection,() => {
 | 
				
			||||||
                    new Combine([Svg.pencil_ui()])
 | 
					 | 
				
			||||||
                        .SetClass("block relative h-10 w-10 p-2 float-right")
 | 
					 | 
				
			||||||
                        .SetStyle("border: 1px solid black; border-radius: 0.7em")
 | 
					 | 
				
			||||||
                        .onClick(() => {
 | 
					 | 
				
			||||||
                    editMode.setData(true)
 | 
					                    editMode.setData(true)
 | 
				
			||||||
                }),
 | 
					                }),
 | 
				
			||||||
                    undefined,
 | 
					 | 
				
			||||||
                    state.osmConnection.isLoggedIn
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            ]).SetClass("flex justify-between w-full")
 | 
					            ]).SetClass("flex justify-between w-full")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const question = new Lazy(
 | 
					            const question = new Lazy(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										228
									
								
								UI/Popup/LanguageElement.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								UI/Popup/LanguageElement.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,228 @@
 | 
				
			||||||
 | 
					import {SpecialVisualization} from "../SpecialVisualization";
 | 
				
			||||||
 | 
					import BaseUIElement from "../BaseUIElement";
 | 
				
			||||||
 | 
					import {UIEventSource} from "../../Logic/UIEventSource";
 | 
				
			||||||
 | 
					import FeaturePipelineState from "../../Logic/State/FeaturePipelineState";
 | 
				
			||||||
 | 
					import {VariableUiElement} from "../Base/VariableUIElement";
 | 
				
			||||||
 | 
					import {OsmTags} from "../../Models/OsmFeature";
 | 
				
			||||||
 | 
					import * as all_languages from "../../assets/language_translations.json"
 | 
				
			||||||
 | 
					import {Translation} from "../i18n/Translation";
 | 
				
			||||||
 | 
					import Combine from "../Base/Combine";
 | 
				
			||||||
 | 
					import Title from "../Base/Title";
 | 
				
			||||||
 | 
					import Lazy from "../Base/Lazy";
 | 
				
			||||||
 | 
					import {SubstitutedTranslation} from "../SubstitutedTranslation";
 | 
				
			||||||
 | 
					import List from "../Base/List";
 | 
				
			||||||
 | 
					import {AllLanguagesSelector} from "./AllLanguagesSelector";
 | 
				
			||||||
 | 
					import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction";
 | 
				
			||||||
 | 
					import {And} from "../../Logic/Tags/And";
 | 
				
			||||||
 | 
					import {Tag} from "../../Logic/Tags/Tag";
 | 
				
			||||||
 | 
					import {EditButton, SaveButton} from "./SaveButton";
 | 
				
			||||||
 | 
					import {FixedUiElement} from "../Base/FixedUiElement";
 | 
				
			||||||
 | 
					import Translations from "../i18n/Translations";
 | 
				
			||||||
 | 
					import Toggle from "../Input/Toggle";
 | 
				
			||||||
 | 
					import {On} from "../../Models/ThemeConfig/Conversion/Conversion";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class LanguageElement implements SpecialVisualization {
 | 
				
			||||||
 | 
					    funcName: string = "language_chooser"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    docs: string | BaseUIElement = "The language element allows to show and pick all known (modern) languages. The key can be set";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] =
 | 
				
			||||||
 | 
					        [{
 | 
				
			||||||
 | 
					            name: "key",
 | 
				
			||||||
 | 
					            required: true,
 | 
				
			||||||
 | 
					            doc: "What key to use, e.g. `language`, `tactile_writing:braille:language`, ... If a language is supported, the language code will be appended to this key, resulting in `language:nl=yes` if nl is picked "
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                name: "question",
 | 
				
			||||||
 | 
					                required: true,
 | 
				
			||||||
 | 
					                doc: "What to ask if no questions are known"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                name: "render_list_item",
 | 
				
			||||||
 | 
					                doc: "How a single language will be shown in the list of languages. Use `{language}` to indicate the language (which it must contain).",
 | 
				
			||||||
 | 
					                defaultValue: "{language()}"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                name: "render_single_language",
 | 
				
			||||||
 | 
					                doc: "What will be shown if the feature only supports a single language",
 | 
				
			||||||
 | 
					                required: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                name: "render_all",
 | 
				
			||||||
 | 
					                doc: "The full rendering. Use `{list}` to show where the list of languages must come. Optional if mode=single",
 | 
				
			||||||
 | 
					                defaultValue: "{list()}"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                name: "no_known_languages",
 | 
				
			||||||
 | 
					                doc: "The text that is shown if no languages are known for this key. If this text is omitted, the languages will be prompted instead"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                name: 'mode',
 | 
				
			||||||
 | 
					                doc: "If one or many languages can be selected. Should be 'multi' or 'single'",
 | 
				
			||||||
 | 
					                defaultValue: 'multi'
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    example: `
 | 
				
			||||||
 | 
					    \`\`\`json
 | 
				
			||||||
 | 
					     {"special":
 | 
				
			||||||
 | 
					       "type": "language_chooser",
 | 
				
			||||||
 | 
					       "key": "school:language",
 | 
				
			||||||
 | 
					       "question": {"en": "What are the main (and administrative) languages spoken in this school?"},
 | 
				
			||||||
 | 
					       "render_single_language": {"en": "{language()} is spoken on this school"},
 | 
				
			||||||
 | 
					       "render_list_item": {"en": "{language()}"},
 | 
				
			||||||
 | 
					       "render_all": {"en": "The following languages are spoken here:{list()}"}
 | 
				
			||||||
 | 
					       "mode":"multi"
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					     \`\`\`
 | 
				
			||||||
 | 
					    `
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constr(state: FeaturePipelineState, tagSource: UIEventSource<OsmTags>, argument: string[]): BaseUIElement {
 | 
				
			||||||
 | 
					        let [key, question, item_render, single_render, all_render, on_no_known_languages, mode] = argument
 | 
				
			||||||
 | 
					        if (mode === undefined || mode.length == 0) {
 | 
				
			||||||
 | 
					            mode = "multi"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (item_render === undefined) {
 | 
				
			||||||
 | 
					            item_render = "{language()}"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (all_render === undefined || all_render.length == 0) {
 | 
				
			||||||
 | 
					            all_render = "{list()}"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (mode !== "single" && mode !== "multi") {
 | 
				
			||||||
 | 
					            throw "Error while calling language_chooser: mode must be either 'single' or 'multi' but it is " + mode
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (single_render.indexOf("{language()") < 0 || item_render.indexOf("{language()") < 0) {
 | 
				
			||||||
 | 
					            throw "Error while calling language_chooser: render_single_language and render_list_item must contain '{language()}'"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (all_render.indexOf("{list()") < 0) {
 | 
				
			||||||
 | 
					            throw "Error while calling language_chooser: render_all must contain '{list()}'"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const prefix = key + ":"
 | 
				
			||||||
 | 
					        const foundLanguages = tagSource
 | 
				
			||||||
 | 
					            .map(tags => {
 | 
				
			||||||
 | 
					                const foundLanguages: string[] = []
 | 
				
			||||||
 | 
					                for (const k in tags) {
 | 
				
			||||||
 | 
					                    const v = tags[k]
 | 
				
			||||||
 | 
					                    if (v !== "yes") {
 | 
				
			||||||
 | 
					                        continue
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (k.startsWith(prefix)) {
 | 
				
			||||||
 | 
					                        foundLanguages.push(k.substring(prefix.length))
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return foundLanguages
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        const forceInputMode = new UIEventSource(false);
 | 
				
			||||||
 | 
					        const inputEl = new Lazy(() => {
 | 
				
			||||||
 | 
					            const selector = new AllLanguagesSelector(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    mode: mode === "single" ? "select-one" : "select-many",
 | 
				
			||||||
 | 
					                    currentCountry: tagSource.map(tgs => tgs["_country"])
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            const cancelButton = Toggle.If(forceInputMode,
 | 
				
			||||||
 | 
					                () => Translations.t.general.cancel
 | 
				
			||||||
 | 
					                    .Clone()
 | 
				
			||||||
 | 
					                    .SetClass("btn btn-secondary").onClick(() => forceInputMode.setData(false)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const saveButton = new SaveButton(
 | 
				
			||||||
 | 
					                selector.GetValue().map(lngs => lngs.length > 0 ? "true" : undefined),
 | 
				
			||||||
 | 
					                state.osmConnection,
 | 
				
			||||||
 | 
					            ).onClick(() => {
 | 
				
			||||||
 | 
					                const selectedLanguages = selector.GetValue().data
 | 
				
			||||||
 | 
					                const currentLanguages = foundLanguages.data
 | 
				
			||||||
 | 
					                const selection: Tag[] = selectedLanguages.map(ln => new Tag(prefix + ln, "yes"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (const currentLanguage of currentLanguages) {
 | 
				
			||||||
 | 
					                    if (selectedLanguages.indexOf(currentLanguage) >= 0) {
 | 
				
			||||||
 | 
					                        continue
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    // Erase language that is not spoken anymore
 | 
				
			||||||
 | 
					                    selection.push(new Tag(prefix + currentLanguage, ""))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (state.featureSwitchIsTesting.data) {
 | 
				
			||||||
 | 
					                    for (const tag of selection) {
 | 
				
			||||||
 | 
					                        tagSource.data[tag.key] = tag.value
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    tagSource.ping()
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    (state?.changes)
 | 
				
			||||||
 | 
					                        .applyAction(
 | 
				
			||||||
 | 
					                            new ChangeTagAction(tagSource.data.id, new And(selection), tagSource.data, {
 | 
				
			||||||
 | 
					                                theme: state?.layoutToUse?.id ?? "unkown",
 | 
				
			||||||
 | 
					                                changeType: "answer",
 | 
				
			||||||
 | 
					                            })
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        .then((_) => {
 | 
				
			||||||
 | 
					                            console.log("Tagchanges applied")
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                forceInputMode.setData(false)
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new Combine([new Title(question), selector,
 | 
				
			||||||
 | 
					                new Combine([cancelButton, saveButton]).SetClass("flex justify-end")
 | 
				
			||||||
 | 
					            ]).SetClass("flex flex-col question disable-links");
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const editButton = new EditButton(state.osmConnection, () => forceInputMode.setData(true))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new VariableUiElement(foundLanguages
 | 
				
			||||||
 | 
					            .map(foundLanguages => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (forceInputMode.data) {
 | 
				
			||||||
 | 
					                    return inputEl
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (foundLanguages.length === 0) {
 | 
				
			||||||
 | 
					                    // No languages found - we show the question and the input element
 | 
				
			||||||
 | 
					                    if (on_no_known_languages !== undefined && on_no_known_languages.length > 0) {
 | 
				
			||||||
 | 
					                        return new Combine([on_no_known_languages, editButton]).SetClass("flex justify-end")
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return inputEl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let rendered: BaseUIElement;
 | 
				
			||||||
 | 
					                if (foundLanguages.length === 1) {
 | 
				
			||||||
 | 
					                    const ln = foundLanguages[0]
 | 
				
			||||||
 | 
					                    let mapping = new Map<string, BaseUIElement>();
 | 
				
			||||||
 | 
					                    mapping.set("language", new Translation(all_languages[ln]))
 | 
				
			||||||
 | 
					                    rendered = new SubstitutedTranslation(
 | 
				
			||||||
 | 
					                        new Translation({"*": single_render}, undefined),
 | 
				
			||||||
 | 
					                        tagSource, state, mapping
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let mapping = new Map<string, BaseUIElement>();
 | 
				
			||||||
 | 
					                    const languagesList = new List(
 | 
				
			||||||
 | 
					                        foundLanguages.map(ln => {
 | 
				
			||||||
 | 
					                            let mappingLn = new Map<string, BaseUIElement>();
 | 
				
			||||||
 | 
					                            mappingLn.set("language", new Translation(all_languages[ln]))
 | 
				
			||||||
 | 
					                            return new SubstitutedTranslation(
 | 
				
			||||||
 | 
					                                new Translation({"*": item_render}, undefined),
 | 
				
			||||||
 | 
					                                tagSource, state, mappingLn
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    mapping.set("list", languagesList)
 | 
				
			||||||
 | 
					                    rendered = new SubstitutedTranslation(
 | 
				
			||||||
 | 
					                        new Translation({'*': all_render}, undefined), tagSource,
 | 
				
			||||||
 | 
					                        state, mapping
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return new Combine([rendered, editButton]).SetClass("flex justify-between")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }, [forceInputMode]));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,21 @@ import Translations from "../i18n/Translations"
 | 
				
			||||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
 | 
					import { OsmConnection } from "../../Logic/Osm/OsmConnection"
 | 
				
			||||||
import Toggle from "../Input/Toggle"
 | 
					import Toggle from "../Input/Toggle"
 | 
				
			||||||
import BaseUIElement from "../BaseUIElement"
 | 
					import BaseUIElement from "../BaseUIElement"
 | 
				
			||||||
 | 
					import Combine from "../Base/Combine";
 | 
				
			||||||
 | 
					import Svg from "../../Svg";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class EditButton extends Toggle {
 | 
				
			||||||
 | 
					    constructor(osmConnection: OsmConnection, onClick: () => void) {
 | 
				
			||||||
 | 
					        super(
 | 
				
			||||||
 | 
					            new Combine([Svg.pencil_ui()])
 | 
				
			||||||
 | 
					                .SetClass("block relative h-10 w-10 p-2 float-right")
 | 
				
			||||||
 | 
					                .SetStyle("border: 1px solid black; border-radius: 0.7em")
 | 
				
			||||||
 | 
					                .onClick(onClick),
 | 
				
			||||||
 | 
					            undefined,
 | 
				
			||||||
 | 
					            osmConnection.isLoggedIn
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SaveButton extends Toggle {
 | 
					export class SaveButton extends Toggle {
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,7 +105,7 @@ export default class TagRenderingQuestion extends Combine {
 | 
				
			||||||
                TagUtils.FlattenAnd(inputElement.GetValue().data, tags.data)
 | 
					                TagUtils.FlattenAnd(inputElement.GetValue().data, tags.data)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            if (selection) {
 | 
					            if (selection) {
 | 
				
			||||||
                ;(state?.changes)
 | 
					                (state?.changes)
 | 
				
			||||||
                    .applyAction(
 | 
					                    .applyAction(
 | 
				
			||||||
                        new ChangeTagAction(tags.data.id, selection, tags.data, {
 | 
					                        new ChangeTagAction(tags.data.id, selection, tags.data, {
 | 
				
			||||||
                            theme: state?.layoutToUse?.id ?? "unkown",
 | 
					                            theme: state?.layoutToUse?.id ?? "unkown",
 | 
				
			||||||
| 
						 | 
					@ -288,14 +288,16 @@ export default class TagRenderingQuestion extends Combine {
 | 
				
			||||||
        value: number
 | 
					        value: number
 | 
				
			||||||
        mainTerm: Record<string, string>
 | 
					        mainTerm: Record<string, string>
 | 
				
			||||||
        searchTerms?: Record<string, string[]>
 | 
					        searchTerms?: Record<string, string[]>
 | 
				
			||||||
        original: Mapping
 | 
					        original: Mapping,
 | 
				
			||||||
 | 
					        hasPriority?: Store<boolean>
 | 
				
			||||||
    }[] {
 | 
					    }[] {
 | 
				
			||||||
        const values: {
 | 
					        const values: {
 | 
				
			||||||
            show: BaseUIElement
 | 
					            show: BaseUIElement
 | 
				
			||||||
            value: number
 | 
					            value: number
 | 
				
			||||||
            mainTerm: Record<string, string>
 | 
					            mainTerm: Record<string, string>
 | 
				
			||||||
            searchTerms?: Record<string, string[]>
 | 
					            searchTerms?: Record<string, string[]>
 | 
				
			||||||
            original: Mapping
 | 
					            original: Mapping,
 | 
				
			||||||
 | 
					            hasPriority?: Store<boolean>
 | 
				
			||||||
        }[] = []
 | 
					        }[] = []
 | 
				
			||||||
        const addIcons = applicableMappings.some((m) => m.icon !== undefined)
 | 
					        const addIcons = applicableMappings.some((m) => m.icon !== undefined)
 | 
				
			||||||
        for (let i = 0; i < applicableMappings.length; i++) {
 | 
					        for (let i = 0; i < applicableMappings.length; i++) {
 | 
				
			||||||
| 
						 | 
					@ -317,6 +319,7 @@ export default class TagRenderingQuestion extends Combine {
 | 
				
			||||||
                mainTerm: tr.translations,
 | 
					                mainTerm: tr.translations,
 | 
				
			||||||
                searchTerms: mapping.searchTerms,
 | 
					                searchTerms: mapping.searchTerms,
 | 
				
			||||||
                original: mapping,
 | 
					                original: mapping,
 | 
				
			||||||
 | 
					                hasPriority: tagsSource.map(tags => mapping.priorityIf?.matchesProperties(tags))
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return values
 | 
					        return values
 | 
				
			||||||
| 
						 | 
					@ -397,7 +400,7 @@ export default class TagRenderingQuestion extends Combine {
 | 
				
			||||||
        const values = TagRenderingQuestion.MappingToPillValue(
 | 
					        const values = TagRenderingQuestion.MappingToPillValue(
 | 
				
			||||||
            applicableMappings,
 | 
					            applicableMappings,
 | 
				
			||||||
            tagsSource,
 | 
					            tagsSource,
 | 
				
			||||||
            state
 | 
					            state,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const searchValue: UIEventSource<string> =
 | 
					        const searchValue: UIEventSource<string> =
 | 
				
			||||||
| 
						 | 
					@ -411,47 +414,12 @@ export default class TagRenderingQuestion extends Combine {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const mode = configuration.multiAnswer ? "select-many" : "select-one"
 | 
					        const mode = configuration.multiAnswer ? "select-many" : "select-one"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const tooMuchElementsValue = new UIEventSource<number[]>([])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let priorityPresets: BaseUIElement = undefined
 | 
					 | 
				
			||||||
        const classes = "h-64 overflow-scroll"
 | 
					        const classes = "h-64 overflow-scroll"
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (applicableMappings.some((m) => m.priorityIf !== undefined)) {
 | 
					 | 
				
			||||||
            const priorityValues = tagsSource.map((tags) =>
 | 
					 | 
				
			||||||
                TagRenderingQuestion.MappingToPillValue(
 | 
					 | 
				
			||||||
                    applicableMappings,
 | 
					 | 
				
			||||||
                    tagsSource,
 | 
					 | 
				
			||||||
                    state
 | 
					 | 
				
			||||||
                ).filter((v) => v.original.priorityIf?.matchesProperties(tags))
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            priorityPresets = new VariableUiElement(
 | 
					 | 
				
			||||||
                priorityValues.map((priority) => {
 | 
					 | 
				
			||||||
                    if (priority.length === 0) {
 | 
					 | 
				
			||||||
                        return Translations.t.general.useSearch
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    return new Combine([
 | 
					 | 
				
			||||||
                        Translations.t.general.useSearchForMore.Subs({
 | 
					 | 
				
			||||||
                            total: applicableMappings.length,
 | 
					 | 
				
			||||||
                        }),
 | 
					 | 
				
			||||||
                        new SearchablePillsSelector(priority, {
 | 
					 | 
				
			||||||
                            selectedElements: tooMuchElementsValue,
 | 
					 | 
				
			||||||
                            hideSearchBar: true,
 | 
					 | 
				
			||||||
                            mode,
 | 
					 | 
				
			||||||
                        }),
 | 
					 | 
				
			||||||
                    ])
 | 
					 | 
				
			||||||
                        .SetClass("flex flex-col items-center ")
 | 
					 | 
				
			||||||
                        .SetClass(classes)
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const presetSearch = new SearchablePillsSelector<number>(values, {
 | 
					        const presetSearch = new SearchablePillsSelector<number>(values, {
 | 
				
			||||||
            selectIfSingle: true,
 | 
					 | 
				
			||||||
            mode,
 | 
					            mode,
 | 
				
			||||||
            searchValue,
 | 
					            searchValue,
 | 
				
			||||||
            onNoMatches: onEmpty?.SetClass(classes).SetClass("flex justify-center items-center"),
 | 
					            onNoMatches: onEmpty?.SetClass(classes).SetClass("flex justify-center items-center"),
 | 
				
			||||||
            searchAreaClass: classes,
 | 
					            searchAreaClass: classes
 | 
				
			||||||
            onManyElementsValue: tooMuchElementsValue,
 | 
					 | 
				
			||||||
            onManyElements: priorityPresets,
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        const fallbackTag = searchValue.map((s) => {
 | 
					        const fallbackTag = searchValue.map((s) => {
 | 
				
			||||||
            if (s === undefined || ff?.key === undefined) {
 | 
					            if (s === undefined || ff?.key === undefined) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue