Improvements to index search functionality

This commit is contained in:
Pieter Vander Vennet 2022-04-28 02:04:25 +02:00
parent 1da799bb5f
commit 14ce4c1846
4 changed files with 89 additions and 80 deletions

View file

@ -36,28 +36,39 @@ export default class MoreScreen extends Combine {
} }
const search = new TextField({ 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")]) 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([ super([
new Combine([searchBar]).SetClass("flex justify-center"), new Combine([searchBar]).SetClass("flex justify-center"),
MoreScreen.createOfficialThemesList(state, themeButtonStyle, themeListStyle, search.GetValue()), MoreScreen.createOfficialThemesList(state, themeButtonStyle, themeListStyle, search.GetValue()),
MoreScreen.createPreviouslyVistedHiddenList(state, themeButtonStyle, themeListStyle, search.GetValue()), MoreScreen.createPreviouslyVistedHiddenList(state, themeButtonStyle, themeListStyle, search.GetValue()),
MoreScreen.createUnofficialThemeList(themeButtonStyle, state, themeListStyle, search.GetValue()), MoreScreen.createUnofficialThemeList(themeButtonStyle, state, themeListStyle, search.GetValue()),
tr.streetcomplete.Clone().SetClass("block text-base mx-10 my-3 mb-10") tr.streetcomplete.Clone().SetClass("block text-base mx-10 my-3 mb-10")
]); ]);
} }
private static NothingFound(search: UIEventSource<string>): BaseUIElement{ private static NothingFound(search: UIEventSource<string>): BaseUIElement {
const t = Translations.t.general.morescreen; const t = Translations.t.general.morescreen;
return new Combine([ return new Combine([
new Title(t.noMatchingThemes, 5).SetClass("w-max font-bold"), 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") new SubtleButton(Svg.search_disable_ui(), t.noSearch, {imgSize: "h-6"}).SetClass("h-12 w-max")
.onClick( () => search.setData("")) .onClick(() => search.setData(""))
]).SetClass("flex flex-col items-center w-full") ]).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<string>): BaseUIElement { private static createUnofficialThemeList(buttonClass: string, state: UserRelatedState, themeListClasses: string, search: UIEventSource<string>): BaseUIElement {
const prefix = "mapcomplete-unofficial-theme-"; const prefix = "mapcomplete-unofficial-theme-";
var currentIds: UIEventSource<string[]> = state.osmConnection.preferencesHandler.preferences var currentIds: UIEventSource<string[]> = state.osmConnection.preferencesHandler.preferences
@ -192,11 +203,14 @@ export default class MoreScreen extends Combine {
var stableIds = UIEventSource.ListStabilized<string>(currentIds) var stableIds = UIEventSource.ListStabilized<string>(currentIds)
return new VariableUiElement( return new VariableUiElement(
stableIds.map(ids => { stableIds.map(ids => {
const allThemes: { element: BaseUIElement, predicate?: (s:string) => boolean }[] = [] const allThemes: { element: BaseUIElement, predicate?: (s: string) => boolean }[] = []
for (const id of ids) { for (const id of ids) {
const link = this.createUnofficialButtonFor(state, id) const link = this.createUnofficialButtonFor(state, id)
if (link !== undefined) { 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) { 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<string>) : BaseUIElement{ private static createPreviouslyVistedHiddenList(state: UserRelatedState, buttonClass: string, themeListStyle: string, search: UIEventSource<string>): BaseUIElement {
const t = Translations.t.general.morescreen const t = Translations.t.general.morescreen
const prefix = "mapcomplete-hidden-theme-" const prefix = "mapcomplete-hidden-theme-"
const hiddenThemes = themeOverview["default"].filter(layout => layout.hideFromOverview) 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)) const knownThemeDescriptions = hiddenThemes.filter(theme => knownThemes.has(theme.id))
.map(theme => ({element: MoreScreen.createLinkButton(state, theme)?.SetClass(buttonClass), .map(theme => ({
predicate: MoreScreen.MatchesLayoutFunc(theme) element: MoreScreen.createLinkButton(state, theme)?.SetClass(buttonClass),
predicate: MoreScreen.MatchesLayoutFunc(theme)
})); }));
const knownLayouts = new FilteredCombine(knownThemeDescriptions, const knownLayouts = new FilteredCombine(knownThemeDescriptions,
search, search,
{innerClasses: themeListStyle, {
onEmpty: MoreScreen.NothingFound(search)} innerClasses: themeListStyle,
) onEmpty: MoreScreen.NothingFound(search)
}
)
return new Combine([ return new Combine([
new Title(t.previouslyHiddenTitle), new Title(t.previouslyHiddenTitle),
@ -261,36 +278,28 @@ export default class MoreScreen extends Combine {
private static MatchesLayoutFunc(layout: { private static MatchesLayoutFunc(layout: {
id: string, id: string,
title: any, title: any,
shortDescription: any shortDescription: any,
keywords?: any[]
}) { }) {
return (search: string) => { return (search: string) => {
search = search.toLocaleLowerCase() search = search.toLocaleLowerCase()
if (layout.id.toLowerCase().indexOf(search) >= 0) { if (layout.id.toLowerCase().indexOf(search) >= 0) {
return true; return true;
} }
for (const lang in layout.shortDescription) { const entitiesToSearch = [layout.shortDescription, layout.title, ...layout.keywords]
if (Locale.language.data !== lang) { for (const entity of entitiesToSearch) {
continue const term = entity["*"] ?? entity[Locale.language.data]
} if (term?.toLowerCase()?.indexOf(search) >= 0) {
if (layout.shortDescription[lang].toLowerCase()?.indexOf(search) >= 0) {
return true 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; return false;
} }
} }
private static createOfficialThemesList(state: { osmConnection: OsmConnection, locationControl?: UIEventSource<Loc> }, buttonClass: string, themeListStyle: string, search: UIEventSource<string>):BaseUIElement { private static createOfficialThemesList(state: { osmConnection: OsmConnection, locationControl?: UIEventSource<Loc> }, buttonClass: string, themeListStyle: string, search: UIEventSource<string>): BaseUIElement {
let officialThemes: { let officialThemes: {
id: string, id: string,
icon: string, icon: string,

View file

@ -7,8 +7,10 @@ export class TextField extends InputElement<string> {
public readonly enterPressed = new UIEventSource<string>(undefined); public readonly enterPressed = new UIEventSource<string>(undefined);
private readonly value: UIEventSource<string>; private readonly value: UIEventSource<string>;
private _element: HTMLElement; private _element: HTMLElement;
private _actualField : HTMLElement
private readonly _isValid: (s: string) => boolean; private readonly _isValid: (s: string) => boolean;
private _rawValue: UIEventSource<string> private _rawValue: UIEventSource<string>
private _isFocused = false;
constructor(options?: { constructor(options?: {
placeholder?: string | BaseUIElement, placeholder?: string | BaseUIElement,
@ -60,7 +62,6 @@ export class TextField extends InputElement<string> {
const field = inputEl; const field = inputEl;
this.value.addCallbackAndRunD(value => { 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! // 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; field["value"] = value;
@ -99,7 +100,6 @@ export class TextField extends InputElement<string> {
) { ) {
newCursorPos--; newCursorPos--;
} }
// @ts-ignore
TextField.SetCursorPosition(field, newCursorPos); TextField.SetCursorPosition(field, newCursorPos);
}; };
@ -110,6 +110,12 @@ export class TextField extends InputElement<string> {
self.enterPressed.setData(field.value); self.enterPressed.setData(field.value);
} }
}); });
if(this._isFocused){
field.focus()
}
this._actualField = field;
} }
@ -147,4 +153,11 @@ export class TextField extends InputElement<string> {
return this._element; return this._element;
} }
public focus() {
if(this._actualField === undefined){
this._isFocused = true
}else{
this._actualField.focus()
}
}
} }

View file

@ -1,19 +1,13 @@
{ {
"id": "mapcomplete-changes", "id": "mapcomplete-changes",
"title": { "title": {
"en": "Changes made with MapComplete", "en": "Changes made with MapComplete"
"de": "Änderungen mit MapComplete",
"es": "Cambios hechos con MapComplete"
}, },
"shortDescription": { "shortDescription": {
"en": "Shows changes made by MapComplete", "en": "Shows changes made by MapComplete"
"de": "Zeigt Änderungen von MapComplete",
"es": "Muestra los cambios hechos por MapComplete"
}, },
"description": { "description": {
"en": "This maps shows all the changes made with MapComplete", "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"
}, },
"maintainer": "", "maintainer": "",
"icon": "./assets/svg/logo.svg", "icon": "./assets/svg/logo.svg",
@ -28,9 +22,7 @@
{ {
"id": "mapcomplete-changes", "id": "mapcomplete-changes",
"name": { "name": {
"en": "Changeset centers", "en": "Changeset centers"
"de": "Schwerpunkte von Änderungssätzen",
"es": "Centros de conjuntos de cambios"
}, },
"minzoom": 0, "minzoom": 0,
"source": { "source": {
@ -44,45 +36,35 @@
], ],
"title": { "title": {
"render": { "render": {
"en": "Changeset for {theme}", "en": "Changeset for {theme}"
"de": "Änderungen für {theme}",
"es": "Conjunto de cambios para {theme}"
} }
}, },
"description": { "description": {
"en": "Shows all MapComplete changes", "en": "Shows all MapComplete changes"
"de": "Zeigt alle MapComplete Änderungen",
"es": "Muestra todos los cambios de MapComplete"
}, },
"tagRenderings": [ "tagRenderings": [
{ {
"id": "render_id", "id": "render_id",
"render": { "render": {
"en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>", "en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
"de": "Änderung <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>",
"es": "Conjunto de cambios <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
} }
}, },
{ {
"id": "contributor", "id": "contributor",
"render": { "render": {
"en": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>", "en": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>"
"de": "Änderung wurde von <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a> gemacht",
"es": "Cambio hecho por <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>"
} }
}, },
{ {
"id": "theme", "id": "theme",
"render": { "render": {
"en": "Change with theme <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>", "en": "Change with theme <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>"
"de": "Änderung mit Thema <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>"
}, },
"mappings": [ "mappings": [
{ {
"if": "theme~http.*", "if": "theme~http.*",
"then": { "then": {
"en": "Change with <b>unofficial</b> theme <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>", "en": "Change with <b>unofficial</b> theme <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>"
"de": "Änderung mit <b>inoffiziellem</b> Thema <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>"
} }
} }
] ]
@ -346,8 +328,7 @@
} }
], ],
"question": { "question": {
"en": "Themename contains {search}", "en": "Themename contains {search}"
"de": "Themenname enthält {search}"
} }
} }
] ]
@ -363,9 +344,7 @@
} }
], ],
"question": { "question": {
"en": "Made by contributor {search}", "en": "Made by contributor {search}"
"de": "Erstellt von {search}",
"es": "Hecho por contributor/a {search}"
} }
} }
] ]
@ -381,9 +360,7 @@
} }
], ],
"question": { "question": {
"en": "<b>Not</b> made by contributor {search}", "en": "<b>Not</b> made by contributor {search}"
"de": "<b>Nicht</b> erstellt von {search}",
"es": "<b>No</b> hecho por contributor/a {search}"
} }
} }
] ]
@ -398,9 +375,7 @@
{ {
"id": "link_to_more", "id": "link_to_more",
"render": { "render": {
"en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>", "en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>"
"de": "Weitere Statistiken finden Sie <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>hier</a>",
"es": "Se pueden encontrar más estadísticas <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>aquí</a>"
} }
}, },
{ {

View file

@ -13,22 +13,35 @@ import PointRenderingConfigJson from "../Models/ThemeConfig/Json/PointRenderingC
import {PrepareLayer} from "../Models/ThemeConfig/Conversion/PrepareLayer"; import {PrepareLayer} from "../Models/ThemeConfig/Conversion/PrepareLayer";
import {PrepareTheme} from "../Models/ThemeConfig/Conversion/PrepareTheme"; import {PrepareTheme} from "../Models/ThemeConfig/Conversion/PrepareTheme";
import {DesugaringContext} from "../Models/ThemeConfig/Conversion/Conversion"; 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. // 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 // It spits out an overview of those to be used to load them
class LayerOverviewUtils { 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<string, any>(); const perId = new Map<string, any>();
for (const theme of themes) { for (const theme of themes) {
const keywords : {}[] = []
for (const layer of (theme.layers ?? [])) {
const l = <LayerConfigJson> layer;
keywords.push({"*": l.id})
keywords.push(l.title)
keywords.push(l.description)
}
const data = { const data = {
id: theme.id, id: theme.id,
title: theme.title, title: theme.title,
shortDescription: theme.shortDescription, shortDescription: theme.shortDescription,
icon: theme.icon, icon: theme.icon,
hideFromOverview: theme.hideFromOverview, hideFromOverview: theme.hideFromOverview,
mustHaveLanguage: theme.mustHaveLanguage mustHaveLanguage: theme.mustHaveLanguage,
keywords: Utils.NoNull(keywords)
} }
perId.set(theme.id, data); perId.set(theme.id, data);
} }
@ -215,13 +228,12 @@ class LayerOverviewUtils {
fixed.set(themeFile.id, themeFile) fixed.set(themeFile.id, themeFile)
} }
this.writeSmallOverview(themeFiles.map(tf => { this.writeSmallOverview(Array.from(fixed.values()).map(t => {
const t = tf.parsed;
return { return {
...t, ...t,
hideFromOverview: t.hideFromOverview ?? false, hideFromOverview: t.hideFromOverview ?? false,
shortDescription: t.shortDescription ?? new Translation(t.description).FirstSentence().translations, shortDescription: t.shortDescription ?? new Translation(t.description).FirstSentence().translations,
mustHaveLanguage: t.mustHaveLanguage?.length > 0 mustHaveLanguage: t.mustHaveLanguage?.length > 0,
} }
})); }));
return fixed; return fixed;