forked from MapComplete/MapComplete
Search: similar filters in different layers are now merged, fix #
This commit is contained in:
parent
6ebc0632a3
commit
48186aa530
15 changed files with 156 additions and 63 deletions
|
@ -26,5 +26,5 @@ To validate the 'search with filters', the tester was tasked with searching all
|
||||||
|
|
||||||
## To improve
|
## To improve
|
||||||
|
|
||||||
[ ] Why are there multiple "Open Now" filters?
|
[x] Why are there multiple "Open Now" filters? Because of different layers with a similar filter, they are now shown merged
|
||||||
[x] Special layers (e.g. gps location) are disabled as well (fixed now)
|
[x] Special layers (e.g. gps location) are disabled as well (fixed now)
|
||||||
|
|
|
@ -406,6 +406,7 @@
|
||||||
"error": "Something went wrong…",
|
"error": "Something went wrong…",
|
||||||
"instructions": "Use the search bar above to search for locations, filters or other thematic maps",
|
"instructions": "Use the search bar above to search for locations, filters or other thematic maps",
|
||||||
"locations": "Locations",
|
"locations": "Locations",
|
||||||
|
"nMoreFilters": "{n} more",
|
||||||
"nothing": "Nothing found…",
|
"nothing": "Nothing found…",
|
||||||
"nothingFor": "No results found for {term}",
|
"nothingFor": "No results found for {term}",
|
||||||
"otherMaps": "Other maps",
|
"otherMaps": "Other maps",
|
||||||
|
|
|
@ -7208,7 +7208,7 @@
|
||||||
},
|
},
|
||||||
"description": "Obchod",
|
"description": "Obchod",
|
||||||
"filter": {
|
"filter": {
|
||||||
"1": {
|
"0": {
|
||||||
"options": {
|
"options": {
|
||||||
"0": {
|
"0": {
|
||||||
"question": "Zobrazit pouze obchody prodávající použité zboží"
|
"question": "Zobrazit pouze obchody prodávající použité zboží"
|
||||||
|
|
|
@ -9087,7 +9087,7 @@
|
||||||
},
|
},
|
||||||
"description": "Ein Geschäft",
|
"description": "Ein Geschäft",
|
||||||
"filter": {
|
"filter": {
|
||||||
"1": {
|
"0": {
|
||||||
"options": {
|
"options": {
|
||||||
"0": {
|
"0": {
|
||||||
"question": "Nur Second-Hand-Geschäfte anzeigen"
|
"question": "Nur Second-Hand-Geschäfte anzeigen"
|
||||||
|
|
|
@ -9122,7 +9122,7 @@
|
||||||
},
|
},
|
||||||
"description": "A shop",
|
"description": "A shop",
|
||||||
"filter": {
|
"filter": {
|
||||||
"1": {
|
"0": {
|
||||||
"options": {
|
"options": {
|
||||||
"0": {
|
"0": {
|
||||||
"question": "Only show shops selling second-hand items"
|
"question": "Only show shops selling second-hand items"
|
||||||
|
|
|
@ -1188,14 +1188,14 @@ input[type="range"].range-lg::-moz-range-thumb {
|
||||||
left: 25%;
|
left: 25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-4 {
|
|
||||||
bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-6 {
|
.top-6 {
|
||||||
top: 1.5rem;
|
top: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bottom-4 {
|
||||||
|
bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.bottom-5 {
|
.bottom-5 {
|
||||||
bottom: 1.25rem;
|
bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
@ -2760,6 +2760,10 @@ input[type="range"].range-lg::-moz-range-thumb {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overflow-x-hidden {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.overflow-y-scroll {
|
.overflow-y-scroll {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
@ -4719,6 +4723,11 @@ input[type="range"].range-lg::-moz-range-thumb {
|
||||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shadow-transparent {
|
||||||
|
--tw-shadow-color: transparent;
|
||||||
|
--tw-shadow: var(--tw-shadow-colored);
|
||||||
|
}
|
||||||
|
|
||||||
.shadow-gray-300 {
|
.shadow-gray-300 {
|
||||||
--tw-shadow-color: #D1D5DB;
|
--tw-shadow-color: #D1D5DB;
|
||||||
--tw-shadow: var(--tw-shadow-colored);
|
--tw-shadow: var(--tw-shadow-colored);
|
||||||
|
@ -5353,8 +5362,8 @@ h2.group {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
border-radius: 999rem;
|
border-radius: 999rem;
|
||||||
padding-left: 0.5rem;
|
padding-left: 0.25rem;
|
||||||
padding-right: 0.5rem;
|
padding-right: 0.25rem;
|
||||||
border: 1px solid var(--subtle-detail-color-light-contrast);
|
border: 1px solid var(--subtle-detail-color-light-contrast);
|
||||||
background-color: var(--low-interaction-background);
|
background-color: var(--low-interaction-background);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { SpecialVisualizationState } from "../../UI/SpecialVisualization"
|
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import Locale from "../../UI/i18n/Locale"
|
import Locale from "../../UI/i18n/Locale"
|
||||||
import Constants from "../../Models/Constants"
|
import Constants from "../../Models/Constants"
|
||||||
|
@ -110,4 +109,23 @@ export default class FilterSearch {
|
||||||
Utils.shuffle(result)
|
Utils.shuffle(result)
|
||||||
return result.slice(0, 6)
|
return result.slice(0, 6)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partitions the list of filters in such a way that identically appearing filters will be in the same sublist.
|
||||||
|
*
|
||||||
|
* Note that this depends on the language and the displayed text. For example, two filters {"en": "A", "nl": "B"} and {"en": "X", "nl": "B"} will be joined for dutch but not for English
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static mergeSemiIdenticalLayers<T extends FilterSearchResult = FilterSearchResult>(filters: ReadonlyArray<T>, language: string):T[][] {
|
||||||
|
const results : Record<string, T[]> = {}
|
||||||
|
for (const filter of filters) {
|
||||||
|
const txt = filter.option.question.textFor(language)
|
||||||
|
if(results[txt]){
|
||||||
|
results[txt].push(filter)
|
||||||
|
}else{
|
||||||
|
results[txt] = [filter]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.values(results)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ export default class SearchState {
|
||||||
return !foundMatch
|
return !foundMatch
|
||||||
})
|
})
|
||||||
}, [state.layerState.activeFilters])
|
}, [state.layerState.activeFilters])
|
||||||
this.locationResults =new GeocodingFeatureSource(this.suggestions.stabilized(250))
|
this.locationResults = new GeocodingFeatureSource(this.suggestions.stabilized(250))
|
||||||
|
|
||||||
this.showSearchDrawer = new UIEventSource(false)
|
this.showSearchDrawer = new UIEventSource(false)
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ export default class SearchState {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async apply(result: FilterSearchResult | LayerConfig) {
|
public async apply(result: FilterSearchResult[] | LayerConfig) {
|
||||||
if (result instanceof LayerConfig) {
|
if (result instanceof LayerConfig) {
|
||||||
return this.applyLayer(result)
|
return this.applyLayer(result)
|
||||||
}
|
}
|
||||||
|
@ -105,29 +105,35 @@ export default class SearchState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async applyFilter(payload: FilterSearchResult) {
|
private async applyFilter(payload: FilterSearchResult[]) {
|
||||||
const state = this.state
|
const state = this.state
|
||||||
|
|
||||||
const { layer, filter, index } = payload
|
const layers = payload.map(fsr => fsr.layer.id)
|
||||||
for (const [name, otherLayer] of state.layerState.filteredLayers) {
|
for (const [name, otherLayer] of state.layerState.filteredLayers) {
|
||||||
const layer = otherLayer.layerDef
|
const layer = otherLayer.layerDef
|
||||||
if(!layer.isNormal()){
|
if (!layer.isNormal()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
otherLayer.isDisplayed.setData(payload.layer.id === layer.id)
|
if(otherLayer.layerDef.minzoom > state.mapProperties.minzoom.data) {
|
||||||
|
// Currently not displayed, we don't hide
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
otherLayer.isDisplayed.setData(layers.indexOf(layer.id) > 0)
|
||||||
}
|
}
|
||||||
const flayer = state.layerState.filteredLayers.get(layer.id)
|
for (const { filter, index, layer } of payload) {
|
||||||
flayer.isDisplayed.set(true)
|
const flayer = state.layerState.filteredLayers.get(layer.id)
|
||||||
const filtercontrol = flayer.appliedFilters.get(filter.id)
|
flayer.isDisplayed.set(true)
|
||||||
if (filtercontrol.data === index) {
|
const filtercontrol = flayer.appliedFilters.get(filter.id)
|
||||||
filtercontrol.setData(undefined)
|
if (filtercontrol.data === index) {
|
||||||
} else {
|
filtercontrol.setData(undefined)
|
||||||
filtercontrol.setData(index)
|
} else {
|
||||||
|
filtercontrol.setData(index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closeIfFullscreen() {
|
closeIfFullscreen() {
|
||||||
if(window.innerWidth < 640){
|
if (window.innerWidth < 640) {
|
||||||
this.showSearchDrawer.set(false)
|
this.showSearchDrawer.set(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +141,7 @@ export default class SearchState {
|
||||||
clickedOnMap(feature: Feature) {
|
clickedOnMap(feature: Feature) {
|
||||||
const osmid = feature.properties.osm_id
|
const osmid = feature.properties.osm_id
|
||||||
const localElement = this.state.indexedFeatures.featuresById.data.get(osmid)
|
const localElement = this.state.indexedFeatures.featuresById.data.get(osmid)
|
||||||
if(localElement){
|
if (localElement) {
|
||||||
this.state.selectedElement.set(localElement)
|
this.state.selectedElement.set(localElement)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.sidebar-unit > h3) {
|
:global(.sidebar-unit > h3) {
|
||||||
|
|
|
@ -2,11 +2,16 @@
|
||||||
import { Accordion, AccordionItem } from "flowbite-svelte"
|
import { Accordion, AccordionItem } from "flowbite-svelte"
|
||||||
|
|
||||||
export let expanded = false
|
export let expanded = false
|
||||||
|
export let noBorder = false
|
||||||
|
let defaultClass: string = undefined
|
||||||
|
if(noBorder){
|
||||||
|
defaultClass = "unstyled w-full flex-grow"
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Accordion>
|
<Accordion>
|
||||||
<AccordionItem open={expanded} paddingDefault="p-0" inactiveClass="text-black">
|
<AccordionItem open={expanded} paddingDefault="p-0" inactiveClass="text-black" {defaultClass}>
|
||||||
<span slot="header" class="w-full p-2 text-base">
|
<span slot="header" class={!noBorder ? "w-full p-2 text-base" : "w-full"}>
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
</span>
|
</span>
|
||||||
<div class="low-interaction rounded-b p-2">
|
<div class="low-interaction rounded-b p-2">
|
||||||
|
|
|
@ -3,25 +3,36 @@
|
||||||
import FilterOption from "./FilterOption.svelte"
|
import FilterOption from "./FilterOption.svelte"
|
||||||
import Loading from "../Base/Loading.svelte"
|
import Loading from "../Base/Loading.svelte"
|
||||||
import FilterToggle from "./FilterToggle.svelte"
|
import FilterToggle from "./FilterToggle.svelte"
|
||||||
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
|
|
||||||
|
|
||||||
export let activeFilter: ActiveFilter
|
export let activeFilter: ActiveFilter[]
|
||||||
let { control, filter } = activeFilter
|
let { control, filter } = activeFilter[0]
|
||||||
let option = control.map(c => filter.options[c] ?? filter.options[0])
|
let option = control.map(c => filter.options[c] ?? filter.options[0])
|
||||||
let loading = false
|
let loading = false
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
loading = true
|
loading = true
|
||||||
requestIdleCallback(() => {
|
requestIdleCallback(() => {
|
||||||
control.setData(undefined)
|
for (const af of activeFilter) {
|
||||||
|
af.control.setData(undefined)
|
||||||
|
}
|
||||||
loading = false
|
loading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export let state: SpecialVisualizationState
|
||||||
|
let debug = state.featureSwitches.featureSwitchIsDebugging
|
||||||
</script>
|
</script>
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<Loading />
|
<Loading />
|
||||||
{:else }
|
{:else }
|
||||||
<FilterToggle on:click={() => clear()}>
|
<FilterToggle on:click={() => clear()}>
|
||||||
<FilterOption option={$option} />
|
<FilterOption option={$option} />
|
||||||
|
{#if $debug}
|
||||||
|
<span class="subtle">
|
||||||
|
({activeFilter.map(af => af.layer.id).join(", ")})
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
</FilterToggle>
|
</FilterToggle>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -8,10 +8,17 @@
|
||||||
import FilterToggle from "./FilterToggle.svelte"
|
import FilterToggle from "./FilterToggle.svelte"
|
||||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||||
import Tr from "../Base/Tr.svelte"
|
import Tr from "../Base/Tr.svelte"
|
||||||
import { Store } from "../../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
|
import type { FilterSearchResult } from "../../Logic/Search/FilterSearch"
|
||||||
|
import FilterSearch from "../../Logic/Search/FilterSearch"
|
||||||
|
|
||||||
export let activeFilters: ActiveFilter[]
|
import Locale from "../i18n/Locale"
|
||||||
|
|
||||||
|
export let activeFilters: ( FilterSearchResult & ActiveFilter)[]
|
||||||
|
let language = Locale.language
|
||||||
|
let mergedActiveFilters = FilterSearch.mergeSemiIdenticalLayers(activeFilters, $language)
|
||||||
|
$:mergedActiveFilters = FilterSearch.mergeSemiIdenticalLayers(activeFilters, $language)
|
||||||
export let state: SpecialVisualizationState
|
export let state: SpecialVisualizationState
|
||||||
let loading = false
|
let loading = false
|
||||||
const t =Translations.t.general.search
|
const t =Translations.t.general.search
|
||||||
|
@ -33,8 +40,6 @@
|
||||||
loading = true
|
loading = true
|
||||||
requestIdleCallback(() => {
|
requestIdleCallback(() => {
|
||||||
enableAllLayers()
|
enableAllLayers()
|
||||||
|
|
||||||
|
|
||||||
for (const activeFilter of activeFilters) {
|
for (const activeFilter of activeFilters) {
|
||||||
activeFilter.control.setData(undefined)
|
activeFilter.control.setData(undefined)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +49,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if activeFilters.length > 0 || $nonactiveLayers.length > 0}
|
{#if mergedActiveFilters.length > 0 || $nonactiveLayers.length > 0}
|
||||||
<SidebarUnit>
|
<SidebarUnit>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<h3><Tr t={t.activeFilters}/></h3>
|
<h3><Tr t={t.activeFilters}/></h3>
|
||||||
|
@ -81,9 +86,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
{#each activeFilters as activeFilter (activeFilter)}
|
{#each mergedActiveFilters as activeFilter (activeFilter)}
|
||||||
<div>
|
<div>
|
||||||
<ActiveFilterSvelte {activeFilter} />
|
<ActiveFilterSvelte {activeFilter} {state}/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,13 +8,19 @@
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
import Loading from "../Base/Loading.svelte"
|
import Loading from "../Base/Loading.svelte"
|
||||||
|
|
||||||
export let entry: FilterSearchResult | LayerConfig
|
export let entry: FilterSearchResult[] | LayerConfig
|
||||||
let isLayer = entry instanceof LayerConfig
|
let asFilter: FilterSearchResult[]
|
||||||
let asLayer = <LayerConfig>entry
|
let asLayer: LayerConfig
|
||||||
let asFilter = <FilterSearchResult>entry
|
if(Array.isArray(entry)){
|
||||||
|
asFilter = entry
|
||||||
|
}else{
|
||||||
|
asLayer = <LayerConfig>entry
|
||||||
|
|
||||||
|
}
|
||||||
export let state: SpecialVisualizationState
|
export let state: SpecialVisualizationState
|
||||||
|
|
||||||
let loading = false
|
let loading = false
|
||||||
|
let debug = state.featureSwitches.featureSwitchIsDebugging
|
||||||
|
|
||||||
function apply() {
|
function apply() {
|
||||||
loading = true
|
loading = true
|
||||||
|
@ -34,7 +40,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex flex-col items-start">
|
<div class="flex flex-col items-start">
|
||||||
<div class="flex items-center gap-x-1">
|
<div class="flex items-center gap-x-1">
|
||||||
{#if isLayer}
|
{#if asLayer}
|
||||||
<div class="w-8 h-8 p-1">
|
<div class="w-8 h-8 p-1">
|
||||||
<ToSvelte construct={asLayer.defaultIcon()} />
|
<ToSvelte construct={asLayer.defaultIcon()} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -42,8 +48,11 @@
|
||||||
<Tr t={asLayer.name} />
|
<Tr t={asLayer.name} />
|
||||||
</b>
|
</b>
|
||||||
{:else}
|
{:else}
|
||||||
<Icon icon={asFilter.option.icon ?? asFilter.option.emoji} clss="w-4 h-4" emojiHeight="14px" />
|
<Icon icon={asFilter[0].option.icon ?? asFilter[0].option.emoji} clss="w-4 h-4" emojiHeight="14px" />
|
||||||
<Tr cls="whitespace-nowrap" t={asFilter.option.question} />
|
<Tr cls="whitespace-nowrap" t={asFilter[0].option.question} />
|
||||||
|
{#if $debug}
|
||||||
|
<span class="subtle">({asFilter.map(f => f.layer.id).join(", ")})</span>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,15 +4,21 @@
|
||||||
|
|
||||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
|
import FilterSearch from "../../Logic/Search/FilterSearch"
|
||||||
import type { FilterSearchResult } from "../../Logic/Search/FilterSearch"
|
import type { FilterSearchResult } from "../../Logic/Search/FilterSearch"
|
||||||
|
|
||||||
import Tr from "../Base/Tr.svelte"
|
import Tr from "../Base/Tr.svelte"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
|
import Locale from "../i18n/Locale"
|
||||||
|
import { Store } from "../../Logic/UIEventSource"
|
||||||
|
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
|
||||||
|
|
||||||
export let state: SpecialVisualizationState
|
export let state: SpecialVisualizationState
|
||||||
let searchTerm = state.searchState.searchTerm
|
let searchTerm = state.searchState.searchTerm
|
||||||
let activeLayers = state.layerState.activeLayers
|
let activeLayers = state.layerState.activeLayers
|
||||||
let filterResults = state.searchState.filterSuggestions
|
let filterResults = state.searchState.filterSuggestions
|
||||||
|
|
||||||
|
let filtersMerged = filterResults.map(filters => FilterSearch.mergeSemiIdenticalLayers(filters, Locale.language.data), [Locale.language])
|
||||||
|
|
||||||
let layerResults = state.searchState.layerSuggestions.map(layers => {
|
let layerResults = state.searchState.layerSuggestions.map(layers => {
|
||||||
const nowActive = activeLayers.data.filter(al => al.layerDef.isNormal())
|
const nowActive = activeLayers.data.filter(al => al.layerDef.isNormal())
|
||||||
|
@ -22,30 +28,44 @@
|
||||||
}
|
}
|
||||||
return layers
|
return layers
|
||||||
}, [activeLayers])
|
}, [activeLayers])
|
||||||
let filterResultsClipped = filterResults.mapD(filters => {
|
let filterResultsClipped: Store<{
|
||||||
|
clipped: (FilterSearchResult[] | LayerConfig)[],
|
||||||
|
rest?: (FilterSearchResult[] | LayerConfig)[]
|
||||||
|
}> = filtersMerged.mapD(filters => {
|
||||||
let layers = layerResults.data
|
let layers = layerResults.data
|
||||||
const ls: (FilterSearchResult | LayerConfig)[] = [].concat(layers, filters)
|
const ls: (FilterSearchResult[] | LayerConfig)[] = [].concat(layers, filters)
|
||||||
if (ls.length <= 6) {
|
if (ls.length <= 6) {
|
||||||
return ls
|
return { clipped: ls }
|
||||||
}
|
}
|
||||||
return ls.slice(0, 4)
|
return { clipped: ls.slice(0, 4), rest: ls.slice(4) }
|
||||||
}, [layerResults, activeLayers])
|
}, [layerResults, activeLayers, Locale.language])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $searchTerm.length > 0 && ($filterResults.length > 0 || $layerResults.length > 0)}
|
{#if $searchTerm.length > 0 && ($filterResults.length > 0 || $layerResults.length > 0)}
|
||||||
<SidebarUnit>
|
<SidebarUnit>
|
||||||
|
|
||||||
<h3><Tr t={Translations.t.general.search.pickFilter} /></h3>
|
<h3>
|
||||||
|
<Tr t={Translations.t.general.search.pickFilter} />
|
||||||
|
</h3>
|
||||||
|
|
||||||
<div class="flex flex-wrap">
|
<div class="flex flex-wrap">
|
||||||
{#each $filterResultsClipped as filterResult (filterResult)}
|
{#each $filterResultsClipped.clipped as filterResult (filterResult)}
|
||||||
<FilterResultSvelte {state} entry={filterResult} />
|
<FilterResultSvelte {state} entry={filterResult} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if $filterResults.length + $layerResults.length > $filterResultsClipped.length}
|
{#if $filtersMerged.length + $layerResults.length > $filterResultsClipped.clipped.length}
|
||||||
<div class="flex justify-center">
|
<AccordionSingle noBorder>
|
||||||
... and {$filterResults.length + $layerResults.length - $filterResultsClipped.length} more ...
|
<div class="flex justify-end text-sm subtle" slot="header">
|
||||||
</div>
|
<Tr t={Translations.t.general.search.nMoreFilters.Subs(
|
||||||
|
{n: $filtersMerged.length + $layerResults.length - $filterResultsClipped.clipped.length}
|
||||||
|
)}/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap overflow-y-auto">
|
||||||
|
{#each $filterResultsClipped.rest as filterResult (filterResult)}
|
||||||
|
<FilterResultSvelte {state} entry={filterResult} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</AccordionSingle>
|
||||||
{/if}
|
{/if}
|
||||||
</SidebarUnit>
|
</SidebarUnit>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -4,16 +4,22 @@
|
||||||
import Constants from "../../Models/Constants"
|
import Constants from "../../Models/Constants"
|
||||||
import type { ActiveFilter } from "../../Logic/State/LayerState"
|
import type { ActiveFilter } from "../../Logic/State/LayerState"
|
||||||
import ThemeViewState from "../../Models/ThemeViewState"
|
import ThemeViewState from "../../Models/ThemeViewState"
|
||||||
import ThemeResults from "./ThemeResults.svelte"
|
import ThemeResults from "./ThemeResults.svelte"
|
||||||
import GeocodeResults from "./GeocodeResults.svelte"
|
import GeocodeResults from "./GeocodeResults.svelte"
|
||||||
import FilterResults from "./FilterResults.svelte"
|
import FilterResults from "./FilterResults.svelte"
|
||||||
import Tr from "../Base/Tr.svelte"
|
import Tr from "../Base/Tr.svelte"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
|
import type { FilterSearchResult } from "../../Logic/Search/FilterSearch"
|
||||||
|
|
||||||
export let state: ThemeViewState
|
export let state: ThemeViewState
|
||||||
let activeFilters: Store<ActiveFilter[]> = state.layerState.activeFilters.map(fs => fs.filter(f =>
|
let activeFilters: Store<(ActiveFilter & FilterSearchResult)[]> = state.layerState.activeFilters.map(fs => fs.filter(f =>
|
||||||
(f.filter.options[0].fields.length === 0) &&
|
(f.filter.options[0].fields.length === 0) &&
|
||||||
Constants.priviliged_layers.indexOf(<any>f.layer.id) < 0))
|
Constants.priviliged_layers.indexOf(<any>f.layer.id) < 0)
|
||||||
|
.map(af => {
|
||||||
|
const index = <number> af.control.data
|
||||||
|
const r : FilterSearchResult & ActiveFilter = { ...af, index, option: af.filter.options[index] }
|
||||||
|
return r
|
||||||
|
}))
|
||||||
let allowOtherThemes = state.featureSwitches.featureSwitchBackToThemeOverview
|
let allowOtherThemes = state.featureSwitches.featureSwitchBackToThemeOverview
|
||||||
let searchTerm = state.searchState.searchTerm
|
let searchTerm = state.searchState.searchTerm
|
||||||
</script>
|
</script>
|
||||||
|
@ -23,13 +29,15 @@
|
||||||
|
|
||||||
{#if $searchTerm.length === 0 && $activeFilters.length === 0 }
|
{#if $searchTerm.length === 0 && $activeFilters.length === 0 }
|
||||||
<div class="p-8 items-center text-center">
|
<div class="p-8 items-center text-center">
|
||||||
<b><Tr t={Translations.t.general.search.instructions}/></b>
|
<b>
|
||||||
|
<Tr t={Translations.t.general.search.instructions} />
|
||||||
|
</b>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<FilterResults {state}/>
|
<FilterResults {state} />
|
||||||
|
|
||||||
<GeocodeResults {state}/>
|
<GeocodeResults {state} />
|
||||||
|
|
||||||
{#if $allowOtherThemes}
|
{#if $allowOtherThemes}
|
||||||
<ThemeResults {state} />
|
<ThemeResults {state} />
|
||||||
|
|
Loading…
Reference in a new issue