From 84eee064b27d25c8c23e2ca0bf72c99480c5e170 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 29 Oct 2022 03:03:51 +0200 Subject: [PATCH] Simplify priority behaviour of PillsSelector, create LanguageChooser-element, update usages --- UI/BigComponents/PdfExportGui.ts | 42 ++--- UI/Input/SearchableMappingsSelector.ts | 155 ++++++++--------- UI/Popup/AllLanguagesSelector.ts | 45 +++++ UI/Popup/EditableTagRendering.ts | 14 +- UI/Popup/LanguageElement.ts | 228 +++++++++++++++++++++++++ UI/Popup/SaveButton.ts | 15 ++ UI/Popup/TagRenderingQuestion.ts | 48 +----- 7 files changed, 385 insertions(+), 162 deletions(-) create mode 100644 UI/Popup/AllLanguagesSelector.ts create mode 100644 UI/Popup/LanguageElement.ts diff --git a/UI/BigComponents/PdfExportGui.ts b/UI/BigComponents/PdfExportGui.ts index a55148160..2dbc0932e 100644 --- a/UI/BigComponents/PdfExportGui.ts +++ b/UI/BigComponents/PdfExportGui.ts @@ -1,20 +1,20 @@ import Combine from "../Base/Combine" -import { FlowPanelFactory, FlowStep } from "../ImportFlow/FlowStep" -import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource" -import { InputElement } from "../Input/InputElement" -import { SvgToPdf, SvgToPdfOptions } from "../../Utils/svgToPdf" -import { FixedInputElement } from "../Input/FixedInputElement" -import { FixedUiElement } from "../Base/FixedUiElement" +import {FlowPanelFactory, FlowStep} from "../ImportFlow/FlowStep" +import {ImmutableStore, Store, UIEventSource} from "../../Logic/UIEventSource" +import {InputElement} from "../Input/InputElement" +import {SvgToPdf, SvgToPdfOptions} from "../../Utils/svgToPdf" +import {FixedInputElement} from "../Input/FixedInputElement" +import {FixedUiElement} from "../Base/FixedUiElement" import FileSelectorButton from "../Input/FileSelectorButton" import InputElementMap from "../Input/InputElementMap" -import { RadioButton } from "../Input/RadioButton" -import { Utils } from "../../Utils" -import { VariableUiElement } from "../Base/VariableUIElement" +import {RadioButton} from "../Input/RadioButton" +import {Utils} from "../../Utils" +import {VariableUiElement} from "../Base/VariableUIElement" import Loading from "../Base/Loading" import BaseUIElement from "../BaseUIElement" import Img from "../Base/Img" import Title from "../Base/Title" -import { CheckBox } from "../Input/Checkboxes" +import {CheckBox} from "../Input/Checkboxes" import Minimap from "../Base/Minimap" import SearchAndGo from "./SearchAndGo" import Toggle from "../Input/Toggle" @@ -25,9 +25,7 @@ import Toggleable from "../Base/Toggleable" import Lazy from "../Base/Lazy" import LinkToWeblate from "../Base/LinkToWeblate" import Link from "../Base/Link" -import { SearchablePillsSelector } from "../Input/SearchableMappingsSelector" -import * as languages from "../../assets/language_translations.json" -import { Translation } from "../i18n/Translation" +import {AllLanguagesSelector} from "../Popup/AllLanguagesSelector"; class SelectTemplate extends Combine implements FlowStep<{ title: string; pages: string[] }> { readonly IsValid: Store @@ -203,23 +201,7 @@ class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf; langu constructor(title: string, pages: string[], options: SvgToPdfOptions) { const svgToPdf = new SvgToPdf(title, pages, options) - const languageOptions = [ - 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 languageSelector = new AllLanguagesSelector( ) const isPrepared = UIEventSource.FromPromiseWithErr(svgToPdf.Prepare()) super([ diff --git a/UI/Input/SearchableMappingsSelector.ts b/UI/Input/SearchableMappingsSelector.ts index e10fe42ab..c1683283e 100644 --- a/UI/Input/SearchableMappingsSelector.ts +++ b/UI/Input/SearchableMappingsSelector.ts @@ -1,23 +1,25 @@ -import { UIElement } from "../UIElement" -import { InputElement } from "./InputElement" +import {UIElement} from "../UIElement" +import {InputElement} from "./InputElement" import BaseUIElement from "../BaseUIElement" -import { Store, UIEventSource } from "../../Logic/UIEventSource" +import {Store, UIEventSource} from "../../Logic/UIEventSource" import Translations from "../i18n/Translations" import Locale from "../i18n/Locale" import Combine from "../Base/Combine" -import { TextField } from "./TextField" +import {TextField} from "./TextField" import Svg from "../../Svg" -import { VariableUiElement } from "../Base/VariableUIElement" +import {VariableUiElement} from "../Base/VariableUIElement" /** * A single 'pill' which can hide itself if the search criteria is not met */ class SelfHidingToggle extends UIElement implements InputElement { - private readonly _shown: BaseUIElement public readonly _selected: UIEventSource public readonly isShown: Store = new UIEventSource(true) + public readonly matchesSearchCriteria: Store public readonly forceSelected: UIEventSource + private readonly _shown: BaseUIElement private readonly _squared: boolean + public constructor( shown: string | BaseUIElement, mainTerm: Record, @@ -26,7 +28,9 @@ class SelfHidingToggle extends UIElement implements InputElement { searchTerms?: Record selected?: UIEventSource forceSelected?: UIEventSource - squared?: boolean + squared?: boolean, + /* Hide, if not selected*/ + hide?: Store } ) { super() @@ -49,24 +53,31 @@ class SelfHidingToggle extends UIElement implements InputElement { const selected = (this._selected = options?.selected ?? new UIEventSource(false)) const forceSelected = (this.forceSelected = options?.forceSelected ?? new UIEventSource(false)) - this.isShown = search.map( - (s) => { - if (s === undefined || s.length === 0) { - return true - } + this.matchesSearchCriteria = search.map(s => { + if (s === undefined || s.length === 0) { + return true + } + + s = s?.trim()?.toLowerCase() + if (searchTerms[Locale.language.data]?.some((t) => t.indexOf(s) >= 0)) { + return true + } + if (searchTerms["*"]?.some((t) => t.indexOf(s) >= 0)) { + return true + } + return false + }) + this.isShown = this.matchesSearchCriteria.map( + (matchesSearch) => { if (selected.data && !forceSelected.data) { return true } - s = s?.trim()?.toLowerCase() - if (searchTerms[Locale.language.data]?.some((t) => t.indexOf(s) >= 0)) { - return true + if (options?.hide?.data) { + return false } - if (searchTerms["*"]?.some((t) => t.indexOf(s) >= 0)) { - return true - } - return false + return matchesSearch }, - [selected, Locale.language] + [selected, Locale.language, options?.hide] ) const self = this @@ -128,13 +139,12 @@ class SelfHidingToggle extends UIElement implements InputElement { * A searchfield can be used to filter the values */ export class SearchablePillsSelector extends Combine implements InputElement { - private readonly selectedElements: UIEventSource - public readonly someMatchFound: Store + private readonly selectedElements: UIEventSource /** * - * @param values + * @param values: the values that can be selected * @param options */ constructor( @@ -142,38 +152,57 @@ export class SearchablePillsSelector extends Combine implements InputElement< show: BaseUIElement value: T mainTerm: Record - searchTerms?: Record + searchTerms?: Record, + /* If there are more then 200 elements, should this element still be shown? */ + hasPriority?: Store }[], 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" + /** + * The values of the selected elements. + * Use this to tie input elements together + */ selectedElements?: UIEventSource + /** + * The search bar. Use this to seed the search value or to tie to another value + */ searchValue?: UIEventSource + /** + * What is shown if the search yielded no results. + * By default: a translated "no search results" + */ onNoMatches?: BaseUIElement + /** + * An element that is shown if no search is entered + * Default behaviour is to show all options + */ 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 - onManyElementsValue?: UIEventSource - selectIfSingle?: false | boolean searchAreaClass?: string hideSearchBar?: false | boolean } ) { - const search = new TextField({ value: options?.searchValue }) + const search = new TextField({value: options?.searchValue}) const searchBar = options?.hideSearchBar ? undefined : new Combine([ - Svg.search_svg().SetClass("w-8 normal-background"), - search.SetClass("w-full"), - ]).SetClass("flex items-center border-2 border-black m-2") + Svg.search_svg().SetClass("w-8 normal-background"), + search.SetClass("w-full"), + ]).SetClass("flex items-center border-2 border-black m-2") const searchValue = search.GetValue().map((s) => s?.trim()?.toLowerCase()) const selectedElements = options?.selectedElements ?? new UIEventSource([]) const mode = options?.mode ?? "select-one" const onEmpty = options?.onNoMatches ?? Translations.t.general.noMatchingMapping - + const forceHide = new UIEventSource(false) const mappedValues: { show: SelfHidingToggle mainTerm: Record @@ -209,6 +238,7 @@ export class SearchablePillsSelector extends Combine implements InputElement< searchTerms: v.searchTerms, selected: vIsSelected, squared: mode === "select-many", + hide: v.hasPriority === undefined ? forceHide : forceHide.map(fh => fh && !v.hasPriority?.data, [v.hasPriority]) }) return { @@ -217,62 +247,18 @@ export class SearchablePillsSelector extends Combine implements InputElement< } }) + // The total number of elements that would be displayed based on the search criteria alone let totalShown: Store - if (options.selectIfSingle) { - let forcedSelection: { value: T; show: SelfHidingToggle } = undefined - totalShown = searchValue.map( - (_) => { - 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] - ) + totalShown = searchValue.map((_) => mappedValues.filter((mv) => mv.show.matchesSearchCriteria.data).length) + const tooMuchElementsCutoff = 40 + totalShown.addCallbackAndRunD(shown => forceHide.setData(tooMuchElementsCutoff < shown)) super([ searchBar, new VariableUiElement( Locale.language.map( (lng) => { - if (totalShown.data >= 200) { - return options?.onManyElements ?? Translations.t.general.useSearch - } + if ( options?.onNoSearchMade !== undefined && (searchValue.data === undefined || searchValue.data.length === 0) @@ -284,9 +270,14 @@ export class SearchablePillsSelector extends Combine implements InputElement< } 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(options?.searchAreaClass ?? "") + + if (totalShown.data >= tooMuchElementsCutoff) { + pills = new Combine([options?.onManyElements ?? Translations.t.general.useSearch, pills]) + } + return pills }, [totalShown, searchValue] ) diff --git a/UI/Popup/AllLanguagesSelector.ts b/UI/Popup/AllLanguagesSelector.ts new file mode 100644 index 000000000..ea379920d --- /dev/null +++ b/UI/Popup/AllLanguagesSelector.ts @@ -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 { + + constructor(options?: { + mode?: "select-many" | "select-one" + currentCountry?: Store, + supportedLanguages?: Record & { _meta?: { countries?: string[] } } + }) { + + const possibleValues: { + show: BaseUIElement + value: string + mainTerm: Record + searchTerms?: Record, + hasPriority?: Store + }[] = [] + + const langs = options?.supportedLanguages ?? all_languages["default"] ?? all_languages + for (const ln in langs) { + let languageInfo: Record & { _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' + }); + } + +} diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 319bba2df..0de1b912f 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -10,6 +10,7 @@ import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" import { Unit } from "../../Models/Unit" import Lazy from "../Base/Lazy" import { FixedUiElement } from "../Base/FixedUiElement" +import {EditButton} from "./SaveButton"; export default class EditableTagRendering extends Toggle { constructor( @@ -70,16 +71,9 @@ export default class EditableTagRendering extends Toggle { // We have a question and editing is enabled const answerWithEditButton = new Combine([ answer, - new Toggle( - 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) - }), - undefined, - state.osmConnection.isLoggedIn - ), + new EditButton(state.osmConnection,() => { + editMode.setData(true) + }), ]).SetClass("flex justify-between w-full") const question = new Lazy( diff --git a/UI/Popup/LanguageElement.ts b/UI/Popup/LanguageElement.ts new file mode 100644 index 000000000..5f774860a --- /dev/null +++ b/UI/Popup/LanguageElement.ts @@ -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, 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(); + mapping.set("language", new Translation(all_languages[ln])) + rendered = new SubstitutedTranslation( + new Translation({"*": single_render}, undefined), + tagSource, state, mapping + ) + } else { + + let mapping = new Map(); + const languagesList = new List( + foundLanguages.map(ln => { + let mappingLn = new Map(); + 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])); + } +} + diff --git a/UI/Popup/SaveButton.ts b/UI/Popup/SaveButton.ts index bd65bbecf..7f53800da 100644 --- a/UI/Popup/SaveButton.ts +++ b/UI/Popup/SaveButton.ts @@ -3,6 +3,21 @@ import Translations from "../i18n/Translations" import { OsmConnection } from "../../Logic/Osm/OsmConnection" import Toggle from "../Input/Toggle" 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 { constructor( diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index c8f3afd91..e5358768b 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -105,7 +105,7 @@ export default class TagRenderingQuestion extends Combine { TagUtils.FlattenAnd(inputElement.GetValue().data, tags.data) ) if (selection) { - ;(state?.changes) + (state?.changes) .applyAction( new ChangeTagAction(tags.data.id, selection, tags.data, { theme: state?.layoutToUse?.id ?? "unkown", @@ -288,14 +288,16 @@ export default class TagRenderingQuestion extends Combine { value: number mainTerm: Record searchTerms?: Record - original: Mapping + original: Mapping, + hasPriority?: Store }[] { const values: { show: BaseUIElement value: number mainTerm: Record searchTerms?: Record - original: Mapping + original: Mapping, + hasPriority?: Store }[] = [] const addIcons = applicableMappings.some((m) => m.icon !== undefined) for (let i = 0; i < applicableMappings.length; i++) { @@ -317,6 +319,7 @@ export default class TagRenderingQuestion extends Combine { mainTerm: tr.translations, searchTerms: mapping.searchTerms, original: mapping, + hasPriority: tagsSource.map(tags => mapping.priorityIf?.matchesProperties(tags)) }) } return values @@ -397,7 +400,7 @@ export default class TagRenderingQuestion extends Combine { const values = TagRenderingQuestion.MappingToPillValue( applicableMappings, tagsSource, - state + state, ) const searchValue: UIEventSource = @@ -411,47 +414,12 @@ export default class TagRenderingQuestion extends Combine { } const mode = configuration.multiAnswer ? "select-many" : "select-one" - const tooMuchElementsValue = new UIEventSource([]) - - let priorityPresets: BaseUIElement = undefined 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(values, { - selectIfSingle: true, mode, searchValue, onNoMatches: onEmpty?.SetClass(classes).SetClass("flex justify-center items-center"), - searchAreaClass: classes, - onManyElementsValue: tooMuchElementsValue, - onManyElements: priorityPresets, + searchAreaClass: classes }) const fallbackTag = searchValue.map((s) => { if (s === undefined || ff?.key === undefined) {