From 14ce4c1846f62055a2237ce4ed55e46d0c47ca3d Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Apr 2022 02:04:25 +0200 Subject: [PATCH] Improvements to index search functionality --- UI/BigComponents/MoreScreen.ts | 77 +++++++++++-------- UI/Input/TextField.ts | 17 +++- .../mapcomplete-changes.json | 53 ++++--------- scripts/generateLayerOverview.ts | 22 ++++-- 4 files changed, 89 insertions(+), 80 deletions(-) diff --git a/UI/BigComponents/MoreScreen.ts b/UI/BigComponents/MoreScreen.ts index 455807f105..7754b02d4a 100644 --- a/UI/BigComponents/MoreScreen.ts +++ b/UI/BigComponents/MoreScreen.ts @@ -36,28 +36,39 @@ export default class MoreScreen extends Combine { } const search = new TextField({ - placeholder: tr.searchForATheme, + placeholder: tr.searchForATheme, }) - search.GetValue().addCallbackAndRun(d => console.log("Search is ", d)) + + if (onMainScreen) { + search.focus() + } + document.addEventListener("keydown", function (event) { + console.log("Got a key event: ", event) + if (event.ctrlKey && event.code === "KeyF") { + search.focus() + event.preventDefault(); + } + }); + const searchBar = new Combine([Svg.search_svg().SetClass("w-8"), search.SetClass("mr-4 w-full")]) - .SetClass("flex rounded-full border-2 border-black w-max items-center my-2 w-1/2") + .SetClass("flex rounded-full border-2 border-black items-center my-2 w-1/2") - super([ - new Combine([searchBar]).SetClass("flex justify-center"), + super([ + new Combine([searchBar]).SetClass("flex justify-center"), MoreScreen.createOfficialThemesList(state, themeButtonStyle, themeListStyle, search.GetValue()), MoreScreen.createPreviouslyVistedHiddenList(state, themeButtonStyle, themeListStyle, search.GetValue()), MoreScreen.createUnofficialThemeList(themeButtonStyle, state, themeListStyle, search.GetValue()), tr.streetcomplete.Clone().SetClass("block text-base mx-10 my-3 mb-10") ]); } - - private static NothingFound(search: UIEventSource): BaseUIElement{ - const t = Translations.t.general.morescreen; + + private static NothingFound(search: UIEventSource): BaseUIElement { + const t = Translations.t.general.morescreen; return new Combine([ new Title(t.noMatchingThemes, 5).SetClass("w-max font-bold"), - new SubtleButton(Svg.search_disable_ui(), t.noSearch,{imgSize: "h-8"}).SetClass("h-12 w-max") - .onClick( () => search.setData("")) + new SubtleButton(Svg.search_disable_ui(), t.noSearch, {imgSize: "h-6"}).SetClass("h-12 w-max") + .onClick(() => search.setData("")) ]).SetClass("flex flex-col items-center w-full") } @@ -173,7 +184,7 @@ export default class MoreScreen extends Combine { } } - private static createUnofficialThemeList(buttonClass: string, state: UserRelatedState, themeListClasses: string, search : UIEventSource): BaseUIElement { + private static createUnofficialThemeList(buttonClass: string, state: UserRelatedState, themeListClasses: string, search: UIEventSource): BaseUIElement { const prefix = "mapcomplete-unofficial-theme-"; var currentIds: UIEventSource = state.osmConnection.preferencesHandler.preferences @@ -192,11 +203,14 @@ export default class MoreScreen extends Combine { var stableIds = UIEventSource.ListStabilized(currentIds) return new VariableUiElement( stableIds.map(ids => { - const allThemes: { element: BaseUIElement, predicate?: (s:string) => boolean }[] = [] + const allThemes: { element: BaseUIElement, predicate?: (s: string) => boolean }[] = [] for (const id of ids) { const link = this.createUnofficialButtonFor(state, id) if (link !== undefined) { - allThemes.push({element: link.SetClass(buttonClass), predicate : s => id.toLowerCase().indexOf(s) >= 0}) + allThemes.push({ + element: link.SetClass(buttonClass), + predicate: s => id.toLowerCase().indexOf(s) >= 0 + }) } } if (allThemes.length <= 0) { @@ -212,7 +226,7 @@ export default class MoreScreen extends Combine { })); } - private static createPreviouslyVistedHiddenList(state: UserRelatedState, buttonClass: string, themeListStyle: string, search: UIEventSource) : BaseUIElement{ + private static createPreviouslyVistedHiddenList(state: UserRelatedState, buttonClass: string, themeListStyle: string, search: UIEventSource): BaseUIElement { const t = Translations.t.general.morescreen const prefix = "mapcomplete-hidden-theme-" const hiddenThemes = themeOverview["default"].filter(layout => layout.hideFromOverview) @@ -230,15 +244,18 @@ export default class MoreScreen extends Combine { } const knownThemeDescriptions = hiddenThemes.filter(theme => knownThemes.has(theme.id)) - .map(theme => ({element: MoreScreen.createLinkButton(state, theme)?.SetClass(buttonClass), - predicate: MoreScreen.MatchesLayoutFunc(theme) + .map(theme => ({ + element: MoreScreen.createLinkButton(state, theme)?.SetClass(buttonClass), + predicate: MoreScreen.MatchesLayoutFunc(theme) })); const knownLayouts = new FilteredCombine(knownThemeDescriptions, search, - {innerClasses: themeListStyle, - onEmpty: MoreScreen.NothingFound(search)} - ) + { + innerClasses: themeListStyle, + onEmpty: MoreScreen.NothingFound(search) + } + ) return new Combine([ new Title(t.previouslyHiddenTitle), @@ -261,36 +278,28 @@ export default class MoreScreen extends Combine { private static MatchesLayoutFunc(layout: { id: string, title: any, - shortDescription: any + shortDescription: any, + keywords?: any[] }) { return (search: string) => { search = search.toLocaleLowerCase() if (layout.id.toLowerCase().indexOf(search) >= 0) { return true; } - for (const lang in layout.shortDescription) { - if (Locale.language.data !== lang) { - continue - } - if (layout.shortDescription[lang].toLowerCase()?.indexOf(search) >= 0) { + const entitiesToSearch = [layout.shortDescription, layout.title, ...layout.keywords] + for (const entity of entitiesToSearch) { + const term = entity["*"] ?? entity[Locale.language.data] + if (term?.toLowerCase()?.indexOf(search) >= 0) { return true } } - for (const lang in layout.title) { - if (Locale.language.data !== lang) { - continue - } - if (layout.title[lang].toLowerCase()?.indexOf(search) >= 0) { - return true - } - } return false; } } - private static createOfficialThemesList(state: { osmConnection: OsmConnection, locationControl?: UIEventSource }, buttonClass: string, themeListStyle: string, search: UIEventSource):BaseUIElement { + private static createOfficialThemesList(state: { osmConnection: OsmConnection, locationControl?: UIEventSource }, buttonClass: string, themeListStyle: string, search: UIEventSource): BaseUIElement { let officialThemes: { id: string, icon: string, diff --git a/UI/Input/TextField.ts b/UI/Input/TextField.ts index 1b8bd39411..6ffbc7a5a3 100644 --- a/UI/Input/TextField.ts +++ b/UI/Input/TextField.ts @@ -7,8 +7,10 @@ export class TextField extends InputElement { public readonly enterPressed = new UIEventSource(undefined); private readonly value: UIEventSource; private _element: HTMLElement; + private _actualField : HTMLElement private readonly _isValid: (s: string) => boolean; private _rawValue: UIEventSource + private _isFocused = false; constructor(options?: { placeholder?: string | BaseUIElement, @@ -60,7 +62,6 @@ export class TextField extends InputElement { const field = inputEl; - this.value.addCallbackAndRunD(value => { // We leave the textfield as is in the case of undefined or null (handled by addCallbackAndRunD) - make sure we do not erase it! field["value"] = value; @@ -99,7 +100,6 @@ export class TextField extends InputElement { ) { newCursorPos--; } - // @ts-ignore TextField.SetCursorPosition(field, newCursorPos); }; @@ -110,6 +110,12 @@ export class TextField extends InputElement { self.enterPressed.setData(field.value); } }); + + if(this._isFocused){ + field.focus() + } + + this._actualField = field; } @@ -147,4 +153,11 @@ export class TextField extends InputElement { return this._element; } + public focus() { + if(this._actualField === undefined){ + this._isFocused = true + }else{ + this._actualField.focus() + } + } } \ No newline at end of file diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 847f62fd61..c90abd4c4d 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -1,19 +1,13 @@ { "id": "mapcomplete-changes", "title": { - "en": "Changes made with MapComplete", - "de": "Änderungen mit MapComplete", - "es": "Cambios hechos con MapComplete" + "en": "Changes made with MapComplete" }, "shortDescription": { - "en": "Shows changes made by MapComplete", - "de": "Zeigt Änderungen von MapComplete", - "es": "Muestra los cambios hechos por MapComplete" + "en": "Shows changes made by MapComplete" }, "description": { - "en": "This maps shows all the changes made with MapComplete", - "de": "Diese Karte zeigt alle Änderungen die mit MapComplete gemacht wurden", - "es": "Este mapa muestra todos los cambios hechos con MapComplete" + "en": "This maps shows all the changes made with MapComplete" }, "maintainer": "", "icon": "./assets/svg/logo.svg", @@ -28,9 +22,7 @@ { "id": "mapcomplete-changes", "name": { - "en": "Changeset centers", - "de": "Schwerpunkte von Änderungssätzen", - "es": "Centros de conjuntos de cambios" + "en": "Changeset centers" }, "minzoom": 0, "source": { @@ -44,45 +36,35 @@ ], "title": { "render": { - "en": "Changeset for {theme}", - "de": "Änderungen für {theme}", - "es": "Conjunto de cambios para {theme}" + "en": "Changeset for {theme}" } }, "description": { - "en": "Shows all MapComplete changes", - "de": "Zeigt alle MapComplete Änderungen", - "es": "Muestra todos los cambios de MapComplete" + "en": "Shows all MapComplete changes" }, "tagRenderings": [ { "id": "render_id", "render": { - "en": "Changeset {id}", - "de": "Änderung {id}", - "es": "Conjunto de cambios {id}" + "en": "Changeset {id}" } }, { "id": "contributor", "render": { - "en": "Change made by {_last_edit:contributor}", - "de": "Änderung wurde von {_last_edit:contributor} gemacht", - "es": "Cambio hecho por {_last_edit:contributor}" + "en": "Change made by {_last_edit:contributor}" } }, { "id": "theme", "render": { - "en": "Change with theme {theme}", - "de": "Änderung mit Thema {theme}" + "en": "Change with theme {theme}" }, "mappings": [ { "if": "theme~http.*", "then": { - "en": "Change with unofficial theme {theme}", - "de": "Änderung mit inoffiziellem Thema {theme}" + "en": "Change with unofficial theme {theme}" } } ] @@ -346,8 +328,7 @@ } ], "question": { - "en": "Themename contains {search}", - "de": "Themenname enthält {search}" + "en": "Themename contains {search}" } } ] @@ -363,9 +344,7 @@ } ], "question": { - "en": "Made by contributor {search}", - "de": "Erstellt von {search}", - "es": "Hecho por contributor/a {search}" + "en": "Made by contributor {search}" } } ] @@ -381,9 +360,7 @@ } ], "question": { - "en": "Not made by contributor {search}", - "de": "Nicht erstellt von {search}", - "es": "No hecho por contributor/a {search}" + "en": "Not made by contributor {search}" } } ] @@ -398,9 +375,7 @@ { "id": "link_to_more", "render": { - "en": "More statistics can be found here", - "de": "Weitere Statistiken finden Sie hier", - "es": "Se pueden encontrar más estadísticas aquí" + "en": "More statistics can be found here" } }, { diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index d9faf2d9ab..c9a17230f8 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -13,22 +13,35 @@ import PointRenderingConfigJson from "../Models/ThemeConfig/Json/PointRenderingC import {PrepareLayer} from "../Models/ThemeConfig/Conversion/PrepareLayer"; import {PrepareTheme} from "../Models/ThemeConfig/Conversion/PrepareTheme"; import {DesugaringContext} from "../Models/ThemeConfig/Conversion/Conversion"; +import LayerConfig from "../Models/ThemeConfig/LayerConfig"; +import LayerConfigJsonJSC from "../Docs/Schemas/LayerConfigJsonJSC"; +import {Utils} from "../Utils"; // This scripts scans 'assets/layers/*.json' for layer definition files and 'assets/themes/*.json' for theme definition files. // It spits out an overview of those to be used to load them class LayerOverviewUtils { - writeSmallOverview(themes: { id: string, title: any, shortDescription: any, icon: string, hideFromOverview: boolean, mustHaveLanguage: boolean }[]) { + writeSmallOverview(themes: { id: string, title: any, shortDescription: any, icon: string, hideFromOverview: boolean, mustHaveLanguage: boolean, layers: (LayerConfigJson | string | {builtin})[] }[]) { const perId = new Map(); for (const theme of themes) { + + const keywords : {}[] = [] + for (const layer of (theme.layers ?? [])) { + const l = layer; + keywords.push({"*": l.id}) + keywords.push(l.title) + keywords.push(l.description) + } + const data = { id: theme.id, title: theme.title, shortDescription: theme.shortDescription, icon: theme.icon, hideFromOverview: theme.hideFromOverview, - mustHaveLanguage: theme.mustHaveLanguage + mustHaveLanguage: theme.mustHaveLanguage, + keywords: Utils.NoNull(keywords) } perId.set(theme.id, data); } @@ -215,13 +228,12 @@ class LayerOverviewUtils { fixed.set(themeFile.id, themeFile) } - this.writeSmallOverview(themeFiles.map(tf => { - const t = tf.parsed; + this.writeSmallOverview(Array.from(fixed.values()).map(t => { return { ...t, hideFromOverview: t.hideFromOverview ?? false, shortDescription: t.shortDescription ?? new Translation(t.description).FirstSentence().translations, - mustHaveLanguage: t.mustHaveLanguage?.length > 0 + mustHaveLanguage: t.mustHaveLanguage?.length > 0, } })); return fixed;