diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index d5d4551f7..ae1289d55 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -569,7 +569,7 @@ }, { "if": "dog=no", - "icon": "./assets/layers/questions/no_dogs.svg", + "icon": "\uD83D\uDC15 ⃠", "then": { "en": "Dogs are not allowed", "nl": "honden zijn niet toegelaten", diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index af486d593..bdf5b3801 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -257,6 +257,7 @@ { "builtin": "id_presets.shop_types", "override": { + "id": "shop_types", "labels": [ "description" ], @@ -1174,51 +1175,7 @@ ], "filter": [ "open_now", - { - "id": "shop-type", - "options": [ - { - "fields": [ - { - "name": "search", - "type": "string" - } - ], - "osmTags": "shop~i~.*{search}.*", - "question": { - "en": "Only show shops selling {search}", - "de": "Nur Geschäfte, die {search} verkaufen", - "nl": "Toon enkel winkels die {search} verkopen", - "es": "Solo mostrar tiendas que vendan {search}", - "fr": "N'afficher que les magasins vendant {search}", - "ca": "Sols mostrar botigues que venen {search}", - "cs": "Zobrazit pouze obchody prodávající {search}" - } - } - ] - }, - { - "id": "shop-name", - "options": [ - { - "fields": [ - { - "name": "search", - "type": "string" - } - ], - "osmTags": "name~i~.*{search}.*", - "question": { - "en": "Only show shops with name {search}", - "de": "Nur Geschäfte mit dem Namen {search} anzeigen", - "nl": "Toon enkel winkels met naam {search}", - "es": "Solo mostrar tiendas con nombre {search}", - "fr": "N'afficher que les magasins portant le nom {search}", - "cs": "Zobrazit pouze obchody s názvem {search}" - } - } - ] - }, + "shop_types", { "id": "second_hand", "options": [ diff --git a/langs/en.json b/langs/en.json index a3764877c..d250d93e7 100644 --- a/langs/en.json +++ b/langs/en.json @@ -260,6 +260,7 @@ "examples": "Examples", "fewChangesBefore": "Please, answer a few questions of existing features before adding a new feature.", "filterPanel": { + "allTypes": "All types", "disableAll": "Disable all", "enableAll": "Enable all" }, diff --git a/langs/layers/ca.json b/langs/layers/ca.json index 29a0fcd88..ad76e0800 100644 --- a/langs/layers/ca.json +++ b/langs/layers/ca.json @@ -6700,15 +6700,6 @@ } }, "description": "Una botiga", - "filter": { - "1": { - "options": { - "0": { - "question": "Sols mostrar botigues que venen {search}" - } - } - } - }, "name": "Botiga", "presets": { "0": { diff --git a/langs/layers/cs.json b/langs/layers/cs.json index 2a531d791..dbd41ec89 100644 --- a/langs/layers/cs.json +++ b/langs/layers/cs.json @@ -6979,21 +6979,7 @@ }, "description": "Obchod", "filter": { - "1": { - "options": { - "0": { - "question": "Zobrazit pouze obchody prodávající {search}" - } - } - }, "2": { - "options": { - "0": { - "question": "Zobrazit pouze obchody s názvem {search}" - } - } - }, - "3": { "options": { "0": { "question": "Zobrazit pouze obchody prodávající použité zboží" diff --git a/langs/layers/da.json b/langs/layers/da.json index 25009056b..f6766577d 100644 --- a/langs/layers/da.json +++ b/langs/layers/da.json @@ -1419,6 +1419,13 @@ "arialabel": "Åbn på openstreetmap.org" } } + }, + "2": { + "then": { + "special": { + "arialabel": "Åbn på openstreetmap.org" + } + } } }, "render": { diff --git a/langs/layers/de.json b/langs/layers/de.json index 3ffaf7776..e87d187ed 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -5673,6 +5673,13 @@ "arialabel": "Auf openstreetmap.org öffnen" } } + }, + "2": { + "then": { + "special": { + "arialabel": "Auf openstreetmap.org öffnen" + } + } } }, "render": { @@ -8855,21 +8862,7 @@ }, "description": "Ein Geschäft", "filter": { - "1": { - "options": { - "0": { - "question": "Nur Geschäfte, die {search} verkaufen" - } - } - }, "2": { - "options": { - "0": { - "question": "Nur Geschäfte mit dem Namen {search} anzeigen" - } - } - }, - "3": { "options": { "0": { "question": "Nur Second-Hand-Geschäfte anzeigen" diff --git a/langs/layers/en.json b/langs/layers/en.json index da09950a7..d353a30a7 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -5673,6 +5673,13 @@ "arialabel": "Open on openstreetmap.org" } } + }, + "2": { + "then": { + "special": { + "arialabel": "Open on openstreetmap.org" + } + } } }, "render": { @@ -8809,6 +8816,14 @@ "render": "School {name}" } }, + "search": { + "description": "Priviliged layer showing the search results", + "tagRenderings": { + "intro": { + "render": "Search result" + } + } + }, "selected_element": { "description": "Highlights the currently selected element. Override this layer to have different colors" }, @@ -8855,21 +8870,7 @@ }, "description": "A shop", "filter": { - "1": { - "options": { - "0": { - "question": "Only show shops selling {search}" - } - } - }, "2": { - "options": { - "0": { - "question": "Only show shops with name {search}" - } - } - }, - "3": { "options": { "0": { "question": "Only show shops selling second-hand items" diff --git a/langs/layers/es.json b/langs/layers/es.json index 01e2be9ce..c6d08f8a4 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -3796,22 +3796,6 @@ } }, "description": "Una tienda", - "filter": { - "1": { - "options": { - "0": { - "question": "Solo mostrar tiendas que vendan {search}" - } - } - }, - "2": { - "options": { - "0": { - "question": "Solo mostrar tiendas con nombre {search}" - } - } - } - }, "name": "Tienda", "presets": { "0": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 6909872ea..b1c30e751 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -5618,22 +5618,6 @@ } }, "description": "Un magasin", - "filter": { - "1": { - "options": { - "0": { - "question": "N'afficher que les magasins vendant {search}" - } - } - }, - "2": { - "options": { - "0": { - "question": "N'afficher que les magasins portant le nom {search}" - } - } - } - }, "name": "Magasin", "presets": { "0": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 6ef1acbfd..503cd865e 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4664,6 +4664,13 @@ "arialabel": "Bekijk op openstreetmap.org" } } + }, + "2": { + "then": { + "special": { + "arialabel": "Bekijk op openstreetmap.org" + } + } } }, "render": { @@ -7120,22 +7127,6 @@ } }, "description": "Een winkel", - "filter": { - "1": { - "options": { - "0": { - "question": "Toon enkel winkels die {search} verkopen" - } - } - }, - "2": { - "options": { - "0": { - "question": "Toon enkel winkels met naam {search}" - } - } - } - }, "name": "Winkel", "presets": { "0": { diff --git a/langs/layers/pl.json b/langs/layers/pl.json index fc4e53d16..a49574f29 100644 --- a/langs/layers/pl.json +++ b/langs/layers/pl.json @@ -1948,6 +1948,13 @@ "arialabel": "Otwórz na openstreetmap.org" } } + }, + "2": { + "then": { + "special": { + "arialabel": "Otwórz na openstreetmap.org" + } + } } }, "render": { diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index f84b175b8..2546818a4 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -953,6 +953,11 @@ video { margin-right: 1rem; } +.my-8 { + margin-top: 2rem; + margin-bottom: 2rem; +} + .-my-1\.5 { margin-top: -0.375rem; margin-bottom: -0.375rem; @@ -978,11 +983,6 @@ video { margin-right: -0.25rem; } -.my-8 { - margin-top: 2rem; - margin-bottom: 2rem; -} - .mx-12 { margin-left: 3rem; margin-right: 3rem; @@ -993,10 +993,6 @@ video { margin-right: 4rem; } -.mb-4 { - margin-bottom: 1rem; -} - .mt-4 { margin-top: 1rem; } @@ -1029,6 +1025,10 @@ video { margin-right: 0.25rem; } +.mb-4 { + margin-bottom: 1rem; +} + .ml-1 { margin-left: 0.25rem; } @@ -1440,10 +1440,6 @@ video { max-height: 3rem; } -.max-h-96 { - max-height: 24rem; -} - .max-h-24 { max-height: 6rem; } @@ -1452,6 +1448,10 @@ video { max-height: 16rem; } +.max-h-96 { + max-height: 24rem; +} + .max-h-full { max-height: 100%; } @@ -4623,12 +4623,12 @@ button.as-link { padding: 0; } -button.unstyled { +button.unstyled, .button-unstyled button { background-color: unset; display: inline-flex; justify-content: start; border: none; - box-shadow: none; + box-shadow: none !important; margin: 0; padding: 0; } @@ -7234,14 +7234,14 @@ svg.apply-fill path { width: 50%; } - .sm\:w-96 { - width: 24rem; - } - .sm\:w-11 { width: 2.75rem; } + .sm\:w-96 { + width: 24rem; + } + .sm\:w-auto { width: auto; } diff --git a/src/Logic/Geocoding/CombinedSearcher.ts b/src/Logic/Geocoding/CombinedSearcher.ts index 3ee38e016..5bc364f51 100644 --- a/src/Logic/Geocoding/CombinedSearcher.ts +++ b/src/Logic/Geocoding/CombinedSearcher.ts @@ -1,4 +1,4 @@ -import GeocodingProvider, { GeoCodeResult, GeocodingOptions } from "./GeocodingProvider" +import GeocodingProvider, { SearchResult, GeocodingOptions } from "./GeocodingProvider" import { Utils } from "../../Utils" import { Store, Stores } from "../UIEventSource" @@ -17,12 +17,17 @@ export default class CombinedSearcher implements GeocodingProvider { * @param geocoded * @private */ - private merge(geocoded: GeoCodeResult[][]): GeoCodeResult[] { - const results: GeoCodeResult[] = [] + private merge(geocoded: SearchResult[][]): SearchResult[] { + const results: SearchResult[] = [] const seenIds = new Set() for (const geocodedElement of geocoded) { for (const entry of geocodedElement) { - const id = entry.osm_type + entry.osm_id + + + if (entry.osm_id === undefined) { + throw "Invalid search result: a search result always must have an osm_id to be able to merge results from different sources" + } + const id = (entry["osm_type"] ?? "") + entry.osm_id if (seenIds.has(id)) { continue } @@ -33,13 +38,14 @@ export default class CombinedSearcher implements GeocodingProvider { return results } - async search(query: string, options?: GeocodingOptions): Promise { + async search(query: string, options?: GeocodingOptions): Promise { const results = (await Promise.all(this._providers.map(pr => pr.search(query, options)))) - return results.flatMap(x => x) + return this.merge(results) } - suggest(query: string, options?: GeocodingOptions): Store { - return Stores.concat(this._providersWithSuggest.map(pr => pr.suggest(query, options))) + suggest(query: string, options?: GeocodingOptions): Store { + return Stores.concat( + this._providersWithSuggest.map(pr => pr.suggest(query, options))) .map(gcrss => this.merge(gcrss)) } diff --git a/src/Logic/Geocoding/CoordinateSearch.ts b/src/Logic/Geocoding/CoordinateSearch.ts index 7aa34ac95..839669130 100644 --- a/src/Logic/Geocoding/CoordinateSearch.ts +++ b/src/Logic/Geocoding/CoordinateSearch.ts @@ -1,4 +1,4 @@ -import GeocodingProvider, { GeoCodeResult } from "./GeocodingProvider" +import GeocodingProvider, { SearchResult } from "./GeocodingProvider" import { Utils } from "../../Utils" import { ImmutableStore, Store } from "../UIEventSource" @@ -52,9 +52,9 @@ export default class CoordinateSearch implements GeocodingProvider { * results.length // => 1 * results[0] // => {lat: -57.5802905, lon: -12.7202538, display_name: "lon: -12.7202538, lat: -57.5802905", "category": "coordinate", "source": "coordinate:latlon"} */ - private directSearch(query: string): GeoCodeResult[] { + private directSearch(query: string): SearchResult[] { - const matches = Utils.NoNull(CoordinateSearch.latLonRegexes.map(r => query.match(r))).map(m => { + const matches = Utils.NoNull(CoordinateSearch.latLonRegexes.map(r => query.match(r))).map(m => { lat: Number(m[1]), lon: Number(m[2]), display_name: "lon: " + m[2] + ", lat: " + m[1], @@ -64,7 +64,7 @@ export default class CoordinateSearch implements GeocodingProvider { const matchesLonLat = Utils.NoNull(CoordinateSearch.lonLatRegexes.map(r => query.match(r))) - .map(m => { + .map(m => { lat: Number(m[2]), lon: Number(m[1]), display_name: "lon: " + m[1] + ", lat: " + m[2], @@ -74,11 +74,11 @@ export default class CoordinateSearch implements GeocodingProvider { return matches.concat(matchesLonLat) } - suggest(query: string): Store { + suggest(query: string): Store { return new ImmutableStore(this.directSearch(query)) } - async search (query: string): Promise { + async search (query: string): Promise { return this.directSearch(query) } diff --git a/src/Logic/Geocoding/FilterSearch.ts b/src/Logic/Geocoding/FilterSearch.ts new file mode 100644 index 000000000..c116052f7 --- /dev/null +++ b/src/Logic/Geocoding/FilterSearch.ts @@ -0,0 +1,60 @@ +import { ImmutableStore, Store } from "../UIEventSource" +import GeocodingProvider, { GeocodingOptions, SearchResult } from "./GeocodingProvider" +import { SpecialVisualizationState } from "../../UI/SpecialVisualization" +import { Utils } from "../../Utils" +import Locale from "../../UI/i18n/Locale" + +export default class FilterSearch implements GeocodingProvider { + private readonly _state: SpecialVisualizationState + + constructor(state: SpecialVisualizationState) { + this._state = state + + } + + async search(query: string, options?: GeocodingOptions): Promise { + return [] + } + + private searchDirectly(query: string): SearchResult[] { + const possibleFilters: SearchResult[] = [] + for (const layer of this._state.layout.layers) { + if (!Array.isArray(layer.filters)) { + continue + } + for (const filter of layer.filters ?? []) { + for (let i = 0; i < filter.options.length; i++) { + const option = filter.options[i] + if (option === undefined) { + console.log("No options for", filter) + continue + } + const terms = [option.question.txt, + ...(option.searchTerms?.[Locale.language.data] ?? option.searchTerms?.["en"] ?? [])].flatMap(term => term.split(" ")) + const levehnsteinD = Math.min(... + terms.map(entry => { + const simplified = Utils.simplifyStringForSearch(entry) + return Utils.levenshteinDistance(query, simplified.slice(0, query.length)) + })) + if (levehnsteinD / query.length > 0.25) { + continue + } + possibleFilters.push({ + payload: { option, layer, filter, index: i }, + category: "filter", + osm_id: layer.id + "/" + filter.id + "/" + option.osmTags?.asHumanString() ?? "none", + }) + } + } + } + return possibleFilters + } + + suggest(query: string, options?: GeocodingOptions): Store { + query = Utils.simplifyStringForSearch(query) + + return new ImmutableStore(this.searchDirectly(query)) + } + + +} diff --git a/src/Logic/Geocoding/GeocodingFeatureSource.ts b/src/Logic/Geocoding/GeocodingFeatureSource.ts index 2278edff7..65a449638 100644 --- a/src/Logic/Geocoding/GeocodingFeatureSource.ts +++ b/src/Logic/Geocoding/GeocodingFeatureSource.ts @@ -1,4 +1,4 @@ -import { GeoCodeResult } from "./GeocodingProvider" +import { SearchResult } from "./GeocodingProvider" import { Store } from "../UIEventSource" import { FeatureSource } from "../FeatureSource/FeatureSource" import { Feature, Geometry } from "geojson" @@ -6,7 +6,7 @@ import { Feature, Geometry } from "geojson" export default class GeocodingFeatureSource implements FeatureSource { public features: Store>[]> - constructor(provider: Store) { + constructor(provider: Store) { this.features = provider.map(geocoded => { if(geocoded === undefined){ return [] diff --git a/src/Logic/Geocoding/GeocodingProvider.ts b/src/Logic/Geocoding/GeocodingProvider.ts index 2fac696d6..639bc973b 100644 --- a/src/Logic/Geocoding/GeocodingProvider.ts +++ b/src/Logic/Geocoding/GeocodingProvider.ts @@ -5,9 +5,22 @@ import { Store } from "../UIEventSource" import * as search from "../../assets/generated/layers/search.json" import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" -export type GeocodingCategory = "coordinate" | "city" | "house" | "street" | "locality" | "country" | "train_station" | "county" | "airport" | "shop" +import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/FilterConfig" +import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" -export type GeoCodeResult = { +export type GeocodingCategory = + "coordinate" + | "city" + | "house" + | "street" + | "locality" + | "country" + | "train_station" + | "county" + | "airport" + | "shop" + +export type GeocodeResult = { /** * The name of the feature being displayed */ @@ -25,11 +38,16 @@ export type GeoCodeResult = { */ boundingbox?: number[] osm_type?: "node" | "way" | "relation" - osm_id?: string, + osm_id: string, category?: GeocodingCategory, payload?: object, source?: string } +export type FilterPayload = { option: FilterConfigOption, filter: FilterConfig, layer: LayerConfig, index: number } +export type SearchResult = + | { category: "filter", osm_id: string, payload: FilterPayload } + | { category: "theme", osm_id: string, payload: MinimalLayoutInformation } + | GeocodeResult export interface GeocodingOptions { bbox?: BBox, @@ -40,16 +58,16 @@ export interface GeocodingOptions { export default interface GeocodingProvider { - search(query: string, options?: GeocodingOptions): Promise + search(query: string, options?: GeocodingOptions): Promise /** * @param query * @param options */ - suggest?(query: string, options?: GeocodingOptions): Store + suggest?(query: string, options?: GeocodingOptions): Store } -export type ReverseGeocodingResult = Feature +}> export interface ReverseGeocodingProvider { reverseSearch( coordinate: { lon: number; lat: number }, zoom: number, - language?: string - ): Promise ; + language?: string, + ): Promise; } export class GeocodingUtils { - public static searchLayer = GeocodingUtils.initSearchLayer() - private static initSearchLayer():LayerConfig{ - if(search["id"] === undefined){ + public static searchLayer = GeocodingUtils.initSearchLayer() + + private static initSearchLayer(): LayerConfig { + if (search["id"] === undefined) { // We are resetting the layeroverview; trying to parse is useless return undefined } - return new LayerConfig( search, "search") + return new LayerConfig(search, "search") } public static categoryToZoomLevel: Record = { @@ -88,7 +107,7 @@ export class GeocodingUtils { street: 15, train_station: 14, airport: 13, - shop:16 + shop: 16, } @@ -103,7 +122,7 @@ export class GeocodingUtils { train_station: "train", county: "building_office_2", airport: "airport", - shop: "building_storefront" + shop: "building_storefront", } diff --git a/src/Logic/Geocoding/LocalElementSearch.ts b/src/Logic/Geocoding/LocalElementSearch.ts index 16661dbc2..38b8ee991 100644 --- a/src/Logic/Geocoding/LocalElementSearch.ts +++ b/src/Logic/Geocoding/LocalElementSearch.ts @@ -1,4 +1,4 @@ -import GeocodingProvider, { GeoCodeResult, GeocodingOptions } from "./GeocodingProvider" +import GeocodingProvider, { SearchResult, GeocodingOptions } from "./GeocodingProvider" import ThemeViewState from "../../Models/ThemeViewState" import { Utils } from "../../Utils" import { Feature } from "geojson" @@ -27,7 +27,7 @@ export default class LocalElementSearch implements GeocodingProvider { } - async search(query: string, options?: GeocodingOptions): Promise { + async search(query: string, options?: GeocodingOptions): Promise { return this.searchEntries(query, options, false).data } @@ -41,7 +41,6 @@ export default class LocalElementSearch implements GeocodingProvider { props["addr:street"] + props["addr:number"] : undefined]) let levehnsteinD: number - console.log("Comparing nearby:", candidateId, props.id) if (candidateId === props.id) { levehnsteinD = 0 } else { @@ -54,7 +53,7 @@ export default class LocalElementSearch implements GeocodingProvider { })) } const center = GeoOperations.centerpointCoordinates(feature) - if (levehnsteinD <= 2) { + if ((levehnsteinD / query.length) <= 0.3) { let description = "" if (feature.properties["addr:street"]) { @@ -76,7 +75,7 @@ export default class LocalElementSearch implements GeocodingProvider { return results } - searchEntries(query: string, options?: GeocodingOptions, matchStart?: boolean): Store { + searchEntries(query: string, options?: GeocodingOptions, matchStart?: boolean): Store { if (query.length < 3) { return new ImmutableStore([]) } @@ -101,7 +100,7 @@ export default class LocalElementSearch implements GeocodingProvider { } return results.map(entry => { const [osm_type, osm_id] = entry.feature.properties.id.split("/") - return { + return { lon: entry.center[0], lat: entry.center[1], osm_type, @@ -118,7 +117,7 @@ export default class LocalElementSearch implements GeocodingProvider { } - suggest(query: string, options?: GeocodingOptions): Store { + suggest(query: string, options?: GeocodingOptions): Store { return this.searchEntries(query, options, true) } diff --git a/src/Logic/Geocoding/NominatimGeocoding.ts b/src/Logic/Geocoding/NominatimGeocoding.ts index 3b5c4fd3f..6248579c6 100644 --- a/src/Logic/Geocoding/NominatimGeocoding.ts +++ b/src/Logic/Geocoding/NominatimGeocoding.ts @@ -3,7 +3,7 @@ import { BBox } from "../BBox" import Constants from "../../Models/Constants" import { FeatureCollection } from "geojson" import Locale from "../../UI/i18n/Locale" -import GeocodingProvider, { GeoCodeResult } from "./GeocodingProvider" +import GeocodingProvider, { SearchResult } from "./GeocodingProvider" export class NominatimGeocoding implements GeocodingProvider { @@ -13,7 +13,7 @@ export class NominatimGeocoding implements GeocodingProvider { this._host = host } - public search(query: string, options?: { bbox?: BBox; limit?: number }): Promise { + public search(query: string, options?: { bbox?: BBox; limit?: number }): Promise { const b = options?.bbox ?? BBox.global const url = `${ this._host diff --git a/src/Logic/Geocoding/OpenStreetMapIdSearch.ts b/src/Logic/Geocoding/OpenStreetMapIdSearch.ts index ac1196ae7..168ecca84 100644 --- a/src/Logic/Geocoding/OpenStreetMapIdSearch.ts +++ b/src/Logic/Geocoding/OpenStreetMapIdSearch.ts @@ -1,5 +1,5 @@ import { Store, UIEventSource } from "../UIEventSource" -import GeocodingProvider, { GeoCodeResult, GeocodingOptions } from "./GeocodingProvider" +import GeocodingProvider, { SearchResult, GeocodingOptions } from "./GeocodingProvider" import { OsmId } from "../../Models/OsmFeature" import { SpecialVisualizationState } from "../../UI/SpecialVisualization" @@ -30,7 +30,7 @@ export default class OpenStreetMapIdSearch implements GeocodingProvider { return undefined } - async search(query: string, options?: GeocodingOptions): Promise { + async search(query: string, options?: GeocodingOptions): Promise { const id = OpenStreetMapIdSearch.extractId(query) if (!id) { return [] @@ -59,7 +59,7 @@ export default class OpenStreetMapIdSearch implements GeocodingProvider { }] } - suggest?(query: string, options?: GeocodingOptions): Store { + suggest?(query: string, options?: GeocodingOptions): Store { return UIEventSource.FromPromise(this.search(query, options)) } diff --git a/src/Logic/Geocoding/PhotonSearch.ts b/src/Logic/Geocoding/PhotonSearch.ts index c14e4a4ee..ff5eb4bfb 100644 --- a/src/Logic/Geocoding/PhotonSearch.ts +++ b/src/Logic/Geocoding/PhotonSearch.ts @@ -1,10 +1,10 @@ import Constants from "../../Models/Constants" import GeocodingProvider, { - GeoCodeResult, + GeocodeResult, GeocodingCategory, GeocodingOptions, ReverseGeocodingProvider, - ReverseGeocodingResult + ReverseGeocodingResult, } from "./GeocodingProvider" import { Utils } from "../../Utils" import { Feature, FeatureCollection } from "geojson" @@ -18,7 +18,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding private static readonly types = { "R": "relation", "W": "way", - "N": "node" + "N": "node", } @@ -54,7 +54,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding } - suggest(query: string, options?: GeocodingOptions): Store { + suggest(query: string, options?: GeocodingOptions): Store { return Stores.FromPromise(this.search(query, options)) } @@ -95,7 +95,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding private getCategory(entry: Feature) { const p = entry.properties - if(p.osm_key === "shop"){ + if (p.osm_key === "shop") { return "shop" } if (p.osm_value === "train_station" || p.osm_key === "railway") { @@ -107,7 +107,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding return p.type } - async search(query: string, options?: GeocodingOptions): Promise { + async search(query: string, options?: GeocodingOptions): Promise { if (query.length < 3) { return [] } @@ -126,7 +126,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding const [lon0, lat0, lon1, lat1] = f.properties.extent boundingbox = [lat0, lat1, lon0, lon1] } - return { + return { feature: f, osm_id: f.properties.osm_id, display_name: f.properties.name, @@ -135,7 +135,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding category: this.getCategory(f), boundingbox, lon, lat, - source: this._endpoint + source: this._endpoint, } }) } diff --git a/src/Logic/Geocoding/RecentSearch.ts b/src/Logic/Geocoding/RecentSearch.ts index d3830691c..dce6133c4 100644 --- a/src/Logic/Geocoding/RecentSearch.ts +++ b/src/Logic/Geocoding/RecentSearch.ts @@ -1,36 +1,41 @@ import { Store, UIEventSource } from "../UIEventSource" import { Feature } from "geojson" import { OsmConnection } from "../Osm/OsmConnection" -import { GeoCodeResult } from "./GeocodingProvider" +import { GeocodeResult } from "./GeocodingProvider" import { GeoOperations } from "../GeoOperations" import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" export class RecentSearch { - private readonly _seenThisSession: UIEventSource - public readonly seenThisSession: Store + private readonly _seenThisSession: UIEventSource + public readonly seenThisSession: Store constructor(state: { layout: LayoutConfig, osmConnection: OsmConnection, selectedElement: Store }) { const prefs = state.osmConnection.preferencesHandler.GetLongPreference("previous-searches") - prefs.addCallbackAndRunD(prev => console.trace("Previous searches are:", prev)) prefs.set(null) - this._seenThisSession = new UIEventSource([])//UIEventSource.asObject(prefs, []) + this._seenThisSession = new UIEventSource([])//UIEventSource.asObject(prefs, []) this.seenThisSession = this._seenThisSession - prefs.addCallbackAndRunD(prefs => { - if(prefs === ""){ + prefs.addCallbackAndRunD(pref => { + if (pref === "") { return } - const simpleArr = JSON.parse(prefs) - if(simpleArr.length > 0){ - this._seenThisSession.set(simpleArr) - return true + try { + + const simpleArr = JSON.parse(pref) + if (simpleArr.length > 0) { + this._seenThisSession.set(simpleArr) + return true + } + } catch (e) { + console.error(e, pref) + prefs.setData("") } }) this.seenThisSession.stabilized(2500).addCallbackAndRunD(seen => { - const results= [] + const results = [] for (let i = 0; i < Math.min(3, seen.length); i++) { const gc = seen[i] const simple = { @@ -39,7 +44,7 @@ export class RecentSearch { display_name: gc.display_name, lat: gc.lat, lon: gc.lon, osm_id: gc.osm_id, - osm_type: gc.osm_type + osm_type: gc.osm_type, } results.push(simple) } @@ -51,25 +56,25 @@ export class RecentSearch { state.selectedElement.addCallbackAndRunD(selected => { const [osm_type, osm_id] = selected.properties.id.split("/") - if(!osm_id){ + if (!osm_id) { return } console.log("Selected element is", selected) - if(["node","way","relation"].indexOf(osm_type) < 0){ + if (["node", "way", "relation"].indexOf(osm_type) < 0) { return } const [lon, lat] = GeoOperations.centerpointCoordinates(selected) - const entry = { + const entry = { feature: selected, osm_id, osm_type, - lon, lat + lon, lat, } this.addSelected(entry) }) } - addSelected(entry: GeoCodeResult) { + addSelected(entry: GeocodeResult) { const arr = [...(this.seenThisSession.data ?? []).slice(0, 20), entry] const seenIds = new Set() @@ -81,7 +86,7 @@ export class RecentSearch { seenIds.add(id) } } - console.log(">>>",arr) + console.log(">>>", arr) this._seenThisSession.set(arr) } } diff --git a/src/Logic/Geocoding/ThemeSearch.ts b/src/Logic/Geocoding/ThemeSearch.ts index 4b7331382..ca43041d8 100644 --- a/src/Logic/Geocoding/ThemeSearch.ts +++ b/src/Logic/Geocoding/ThemeSearch.ts @@ -1,4 +1,4 @@ -import GeocodingProvider, { GeoCodeResult, GeocodingOptions } from "./GeocodingProvider" +import GeocodingProvider, { SearchResult, GeocodingOptions } from "./GeocodingProvider" import * as themeOverview from "../../assets/generated/theme_overview.json" import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" import { SpecialVisualizationState } from "../../UI/SpecialVisualization" @@ -17,15 +17,15 @@ export default class ThemeSearch implements GeocodingProvider { this._knownHiddenThemes = MoreScreen.knownHiddenThemes(this._state.osmConnection) } - async search(query: string, options?: GeocodingOptions): Promise { + async search(query: string, options?: GeocodingOptions): Promise { return this.searchDirect(query, options) } - suggest(query: string, options?: GeocodingOptions): Store { + suggest(query: string, options?: GeocodingOptions): Store { return new ImmutableStore(this.searchDirect(query, options)) } - private searchDirect(query: string, options?: GeocodingOptions): GeoCodeResult[] { + private searchDirect(query: string, options?: GeocodingOptions): SearchResult[] { if(query.length < 1){ return [] } @@ -37,8 +37,9 @@ export default class ThemeSearch implements GeocodingProvider { .filter(th => MoreScreen.MatchesLayout(th, query)) .slice(0, limit + 1) - return withMatch.map(match => { + return withMatch.map(match => { payload: match, + category: "theme", osm_id: match.id }) } diff --git a/src/Logic/State/LayerState.ts b/src/Logic/State/LayerState.ts index a9ddba4bb..346a5251d 100644 --- a/src/Logic/State/LayerState.ts +++ b/src/Logic/State/LayerState.ts @@ -7,7 +7,13 @@ import { Tag } from "../Tags/Tag" import Translations from "../../UI/i18n/Translations" import { RegexTag } from "../Tags/RegexTag" import { Or } from "../Tags/Or" +import FilterConfig from "../../Models/ThemeConfig/FilterConfig" +export type ActiveFilter = { + layer: LayerConfig, + filter: FilterConfig, + control: UIEventSource +} /** * The layer state keeps track of: * - Which layers are enabled @@ -26,6 +32,9 @@ export default class LayerState { * Which layers are enabled in the current theme and what filters are applied onto them */ public readonly filteredLayers: ReadonlyMap + private readonly _activeFilters: UIEventSource = new UIEventSource([]) + + public readonly activeFilters: Store = this._activeFilters private readonly osmConnection: OsmConnection /** @@ -56,6 +65,41 @@ export default class LayerState { } this.filteredLayers = filteredLayers layers.forEach((l) => LayerState.linkFilterStates(l, filteredLayers)) + + this.filteredLayers.forEach(fl => { + fl.isDisplayed.addCallback(() => this.updateActiveFilters()) + for (const [_, appliedFilter] of fl.appliedFilters) { + appliedFilter.addCallback(() => this.updateActiveFilters()) + } + }) + this.updateActiveFilters() + } + + private updateActiveFilters(){ + const filters: ActiveFilter[] = [] + this.filteredLayers.forEach(fl => { + if(!fl.isDisplayed.data){ + return + } + for (const [filtername, appliedFilter] of fl.appliedFilters) { + if (appliedFilter.data === undefined) { + continue + } + const filter = fl.layerDef.filters.find(f => f.id === filtername) + if(typeof appliedFilter.data === "number"){ + if(filter.options[appliedFilter.data].osmTags === undefined){ + // This is probably the first, generic option which doesn't _actually_ filter + continue + } + } + filters.push({ + layer: fl.layerDef, + control: appliedFilter, + filter, + }) + } + }) + this._activeFilters.set(filters) } /** diff --git a/src/Logic/State/UserRelatedState.ts b/src/Logic/State/UserRelatedState.ts index 3ad5b523e..37aa8c043 100644 --- a/src/Logic/State/UserRelatedState.ts +++ b/src/Logic/State/UserRelatedState.ts @@ -207,7 +207,6 @@ export default class UserRelatedState { isOfficial: boolean } | undefined { - console.log("GETTING UNOFFICIAL THEME") const pref = this.osmConnection.GetLongPreference("unofficial-theme-" + id) const str = pref.data @@ -351,10 +350,8 @@ export default class UserRelatedState { const key = k.substring(0, k.length - "length".length) let combined = "" for (let i = 0; i < l; i++) { - console.log("Building preference:",key,i,">>>", newPrefs[key + i], "<<<", newPrefs, ) combined += (newPrefs[key + i]) } - console.log("Combined",key,">>>",combined) amendedPrefs.data[key.substring(0, key.length - "-combined-".length)] = combined } else { amendedPrefs.data[k] = newPrefs[k] diff --git a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts index 7c604dbf8..fba6240ba 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -33,6 +33,7 @@ import LineRenderingConfigJson from "../Json/LineRenderingConfigJson" import { ConversionContext } from "./ConversionContext" import { ExpandRewrite } from "./ExpandRewrite" import { TagUtils } from "../../../Logic/Tags/TagUtils" +import { Translatable } from "../Json/Translatable" class ExpandFilter extends DesugaringStep { private static readonly predefinedFilters = ExpandFilter.load_filters() @@ -40,7 +41,7 @@ class ExpandFilter extends DesugaringStep { constructor(state: DesugaringContext) { super( - "Expands filters: replaces a shorthand by the value found in 'filters.json'. If the string is formatted 'layername.filtername, it will be looked up into that layer instead", + "Expands filters: replaces a shorthand by the value found in 'filters.json'. If the string is formatted 'layername.filtername, it will be looked up into that layer instead. If a tagRendering sets 'filter', this filter will also be included", ["filter"], "ExpandFilter", ) @@ -67,6 +68,9 @@ class ExpandFilter extends DesugaringStep { const newFilters: FilterConfigJson[] = [] const filters = <(FilterConfigJson | string)[]>json.filter + /** + * Checks all tagRendering. If a tagrendering has 'filter' set, add this filter to the layer config + */ for (let i = 0; i < json.tagRenderings?.length; i++) { const tagRendering = json.tagRenderings[i] if (!tagRendering?.filter) { @@ -94,6 +98,9 @@ class ExpandFilter extends DesugaringStep { } } + /** + * Create filters based on builtin filters + */ for (let i = 0; i < filters.length; i++) { const filter = filters[i] if (filter === undefined) { @@ -115,15 +122,16 @@ class ExpandFilter extends DesugaringStep { "Found a matching tagRendering to base a filter on, but this tagRendering does not contain any mappings", ) } - const options = matchingTr.mappings.map((mapping) => ({ + const options = ( matchingTr).mappings.map((mapping) => ({ question: mapping.then, osmTags: mapping.if, + searchTerms: mapping.searchTerms + })) options.unshift({ - question: matchingTr["question"] ?? { - en: "All types", - }, + question: matchingTr["question"] ?? Translations.t.general.filterPanel.allTypes, osmTags: undefined, + searchTerms: undefined }) newFilters.push({ id: filter, diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index 85cbb8527..f1827f2c2 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -103,6 +103,10 @@ export class DoesImageExist extends DesugaringStep { return image } + if(Utils.isEmoji(image)){ + return image + } + if (!this._knownImagePaths.has(image)) { if (this.doesPathExist === undefined) { context.err( diff --git a/src/Models/ThemeConfig/FilterConfig.ts b/src/Models/ThemeConfig/FilterConfig.ts index eff90cb2c..deb67fc03 100644 --- a/src/Models/ThemeConfig/FilterConfig.ts +++ b/src/Models/ThemeConfig/FilterConfig.ts @@ -8,12 +8,12 @@ import { UIEventSource } from "../../Logic/UIEventSource" import { QueryParameters } from "../../Logic/Web/QueryParameters" import { Utils } from "../../Utils" import { RegexTag } from "../../Logic/Tags/RegexTag" -import BaseUIElement from "../../UI/BaseUIElement" -import Table from "../../UI/Base/Table" -import Combine from "../../UI/Base/Combine" import MarkdownUtils from "../../Utils/MarkdownUtils" + export type FilterConfigOption = { question: Translation + searchTerms: Record + icon?: string osmTags: TagsFilter | undefined /* Only set if fields are present. Used to create `osmTags` (which are used to _actually_ filter) when the field is written*/ readonly originalTagsSpec: TagConfigJson @@ -105,8 +105,10 @@ export default class FilterConfig { return { question: question, osmTags: osmTags, + searchTerms: option.searchTerms, fields, originalTagsSpec: option.osmTags, + icon: option.icon } }) @@ -151,7 +153,7 @@ export default class FilterConfig { } public initState(layerId: string): UIEventSource { - let defaultValue = "" + let defaultValue: string if (this.options.length > 1) { defaultValue = "" + (this.defaultSelection ?? 0) } else if (this.options[0].fields?.length > 0) { diff --git a/src/Models/ThemeConfig/Json/FilterConfigJson.ts b/src/Models/ThemeConfig/Json/FilterConfigJson.ts index 4b0e137e3..04a800edd 100644 --- a/src/Models/ThemeConfig/Json/FilterConfigJson.ts +++ b/src/Models/ThemeConfig/Json/FilterConfigJson.ts @@ -1,4 +1,5 @@ import { TagConfigJson } from "./TagConfigJson" +import { Translatable } from "./Translatable" export default interface FilterConfigJson { /** @@ -34,7 +35,9 @@ export default interface FilterConfigJson { * ``` */ options: { - question: string | any + question: Translatable + searchTerms?: Record + icon?: string osmTags?: TagConfigJson default?: boolean fields?: { diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 6e2f71d19..d6f315637 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -76,6 +76,7 @@ import { RecentSearch } from "../Logic/Geocoding/RecentSearch" import PhotonSearch from "../Logic/Geocoding/PhotonSearch" import ThemeSearch from "../Logic/Geocoding/ThemeSearch" import OpenStreetMapIdSearch from "../Logic/Geocoding/OpenStreetMapIdSearch" +import FilterSearch from "../Logic/Geocoding/FilterSearch" /** * @@ -385,9 +386,10 @@ export default class ThemeViewState implements SpecialVisualizationState { this.geosearch = new CombinedSearcher( new CoordinateSearch(), - new LocalElementSearch(this, 5), - new OpenStreetMapIdSearch(this), - new PhotonSearch(), // new NominatimGeocoding(), + new FilterSearch(this), + //new LocalElementSearch(this, 5), + //new OpenStreetMapIdSearch(this), + // new PhotonSearch(), // new NominatimGeocoding(), this.featureSwitches.featureSwitchBackToThemeOverview.data ? new ThemeSearch(this) : undefined ) diff --git a/src/UI/Base/Dropdown.svelte b/src/UI/Base/Dropdown.svelte index 7b8f7e78e..5511e415d 100644 --- a/src/UI/Base/Dropdown.svelte +++ b/src/UI/Base/Dropdown.svelte @@ -8,7 +8,7 @@ return } const v = value.data - for (let option of htmlElement.getElementsByTagName("option")) { + for (let option of Array.from(htmlElement.getElementsByTagName("option"))) { if (option.value === v) { option.selected = true return diff --git a/src/UI/BigComponents/Filterview.svelte b/src/UI/BigComponents/Filterview.svelte index e669b6d66..098e72007 100644 --- a/src/UI/BigComponents/Filterview.svelte +++ b/src/UI/BigComponents/Filterview.svelte @@ -13,6 +13,7 @@ import FilterviewWithFields from "./FilterviewWithFields.svelte" import Tr from "../Base/Tr.svelte" import Translations from "../i18n/Translations" + import { Utils } from "../../Utils" export let filteredLayer: FilteredLayer export let highlightedLayer: Store = new ImmutableStore(undefined) @@ -28,7 +29,7 @@ return state.sync( (f) => f === 0, [], - (b) => (b ? 0 : undefined) + (b) => (b ? 0 : undefined), ) } @@ -75,6 +76,9 @@ {#each filter.options as option, i} {/each} diff --git a/src/UI/BigComponents/SearchResult.svelte b/src/UI/BigComponents/SearchResult.svelte deleted file mode 100644 index ab010f68d..000000000 --- a/src/UI/BigComponents/SearchResult.svelte +++ /dev/null @@ -1,121 +0,0 @@ - - -{#if otherTheme} - - - -
- - - - -
-
- -{:else} - - -{/if} diff --git a/src/UI/BigComponents/SearchResults.svelte b/src/UI/BigComponents/SearchResults.svelte deleted file mode 100644 index 5a54d4699..000000000 --- a/src/UI/BigComponents/SearchResults.svelte +++ /dev/null @@ -1,100 +0,0 @@ - - -
- - - -
- - diff --git a/src/UI/BigComponents/ThemeIntroPanel.svelte b/src/UI/BigComponents/ThemeIntroPanel.svelte index 504285095..247cecb63 100644 --- a/src/UI/BigComponents/ThemeIntroPanel.svelte +++ b/src/UI/BigComponents/ThemeIntroPanel.svelte @@ -2,7 +2,7 @@ import Translations from "../i18n/Translations" import Tr from "../Base/Tr.svelte" import NextButton from "../Base/NextButton.svelte" - import Geosearch from "./Geosearch.svelte" + import Geosearch from "../Search/Geosearch.svelte" import ThemeViewState from "../../Models/ThemeViewState" import { Store, UIEventSource } from "../../Logic/UIEventSource" import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid" diff --git a/src/UI/Popup/MoveWizard.svelte b/src/UI/Popup/MoveWizard.svelte index 11818325c..7d716f466 100644 --- a/src/UI/Popup/MoveWizard.svelte +++ b/src/UI/Popup/MoveWizard.svelte @@ -12,7 +12,7 @@ import { GeoOperations } from "../../Logic/GeoOperations" import LocationInput from "../InputElement/Helpers/LocationInput.svelte" import OpenBackgroundSelectorButton from "../BigComponents/OpenBackgroundSelectorButton.svelte" - import Geosearch from "../BigComponents/Geosearch.svelte" + import Geosearch from "../Search/Geosearch.svelte" import If from "../Base/If.svelte" import Constants from "../../Models/Constants" import LoginToggle from "../Base/LoginToggle.svelte" diff --git a/src/UI/Search/ActiveFilter.svelte b/src/UI/Search/ActiveFilter.svelte new file mode 100644 index 000000000..4787974b4 --- /dev/null +++ b/src/UI/Search/ActiveFilter.svelte @@ -0,0 +1,20 @@ + +{ console.log( "dismiss"); return control.setData(undefined) }}> + + diff --git a/src/UI/Search/ActiveFilters.svelte b/src/UI/Search/ActiveFilters.svelte new file mode 100644 index 000000000..57a95e4c4 --- /dev/null +++ b/src/UI/Search/ActiveFilters.svelte @@ -0,0 +1,17 @@ + +
+ + {#each $activeFilters as activeFilter (activeFilter)} + + {/each} +
diff --git a/src/UI/Search/FilterResult.svelte b/src/UI/Search/FilterResult.svelte new file mode 100644 index 000000000..8696a0db4 --- /dev/null +++ b/src/UI/Search/FilterResult.svelte @@ -0,0 +1,55 @@ + + diff --git a/src/UI/Search/GeocodeResult.svelte b/src/UI/Search/GeocodeResult.svelte new file mode 100644 index 000000000..da1c24b0b --- /dev/null +++ b/src/UI/Search/GeocodeResult.svelte @@ -0,0 +1,100 @@ + + + diff --git a/src/UI/BigComponents/Geosearch.svelte b/src/UI/Search/Geosearch.svelte similarity index 94% rename from src/UI/BigComponents/Geosearch.svelte rename to src/UI/Search/Geosearch.svelte index c32c6212c..e1199cd28 100644 --- a/src/UI/BigComponents/Geosearch.svelte +++ b/src/UI/Search/Geosearch.svelte @@ -13,18 +13,16 @@ import { GeoLocationState } from "../../Logic/State/GeoLocationState" import { NominatimGeocoding } from "../../Logic/Geocoding/NominatimGeocoding" import { GeocodingUtils } from "../../Logic/Geocoding/GeocodingProvider" - import type { GeoCodeResult } from "../../Logic/Geocoding/GeocodingProvider" + import type { SearchResult } from "../../Logic/Geocoding/GeocodingProvider" import type GeocodingProvider from "../../Logic/Geocoding/GeocodingProvider" import SearchResults from "./SearchResults.svelte" - import MoreScreen from "./MoreScreen" import type { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" import { focusWithArrows } from "../../Utils/focusWithArrows" import ShowDataLayer from "../Map/ShowDataLayer" import ThemeViewState from "../../Models/ThemeViewState" - import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import GeocodingFeatureSource from "../../Logic/Geocoding/GeocodingFeatureSource" - import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson.js" + import MoreScreen from "../BigComponents/MoreScreen" export let perLayer: ReadonlyMap | undefined = undefined export let bounds: UIEventSource @@ -90,8 +88,7 @@ return } const poi = result[0] - if (poi.payload !== undefined) { - // This is a theme + if (poi.category === "theme") { const theme = poi.payload const url = MoreScreen.createUrlFor(theme, false) console.log("Found a theme, going to", url) @@ -99,6 +96,9 @@ window.location = url return } + if(poi.category === "filter"){ + return // Should not happen + } if (poi.boundingbox) { const [lat0, lat1, lon0, lon1] = poi.boundingbox @@ -139,13 +139,14 @@ } } - let suggestions: Store = searchContents.stabilized(250).bindD(search => { + let suggestions: Store = searchContents.stabilized(250).bindD(search => { if (search.length === 0) { return undefined } return Stores.holdDefined(bounds.bindD(bbox => searcher.suggest(search, { bbox, limit: 15 }))) } ) + suggestions.addCallbackAndRun(suggestions => console.log(">>> suggestions are", suggestions)) let geocededFeatures= new GeocodingFeatureSource(suggestions.stabilized(250)) state.featureProperties.trackFeatureSource(geocededFeatures) diff --git a/src/UI/Search/SearchResult.svelte b/src/UI/Search/SearchResult.svelte new file mode 100644 index 000000000..db14788e7 --- /dev/null +++ b/src/UI/Search/SearchResult.svelte @@ -0,0 +1,20 @@ + + +{#if entry.category === "theme"} + +{:else if entry.category === "filter"} + +{:else} + + +{/if} diff --git a/src/UI/Search/SearchResults.svelte b/src/UI/Search/SearchResults.svelte new file mode 100644 index 000000000..2a71ca11a --- /dev/null +++ b/src/UI/Search/SearchResults.svelte @@ -0,0 +1,102 @@ + + +
+ +
+ + diff --git a/src/UI/Search/ThemeResult.svelte b/src/UI/Search/ThemeResult.svelte new file mode 100644 index 000000000..522e379ef --- /dev/null +++ b/src/UI/Search/ThemeResult.svelte @@ -0,0 +1,22 @@ + + + + + +
+ + + + +
+
diff --git a/src/UI/ThemeViewGUI.svelte b/src/UI/ThemeViewGUI.svelte index 75d0c2401..bb67d7768 100644 --- a/src/UI/ThemeViewGUI.svelte +++ b/src/UI/ThemeViewGUI.svelte @@ -11,7 +11,7 @@ import LayerConfig from "../Models/ThemeConfig/LayerConfig" import ThemeViewState from "../Models/ThemeViewState" import type { MapProperties } from "../Models/MapProperties" - import Geosearch from "./BigComponents/Geosearch.svelte" + import Geosearch from "./Search/Geosearch.svelte" import Translations from "./i18n/Translations" import usersettings from "../assets/generated/layers/usersettings.json" import { diff --git a/src/Utils.ts b/src/Utils.ts index 819b72b15..e70fe43b5 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1,5 +1,4 @@ import DOMPurify from "dompurify" - export class Utils { /** * In the 'deploy'-step, some code needs to be run by ts-node. diff --git a/src/index.css b/src/index.css index 457b85028..463b0c00e 100644 --- a/src/index.css +++ b/src/index.css @@ -279,12 +279,12 @@ button.as-link { padding: 0; } -button.unstyled { +button.unstyled, .button-unstyled button { background-color: unset; display: inline-flex; justify-content: start; border: none; - box-shadow: none; + box-shadow: none !important; margin: 0; padding: 0; }