Search: similar filters in different layers are now merged, fix #

This commit is contained in:
Pieter Vander Vennet 2024-09-17 02:16:25 +02:00
parent 6ebc0632a3
commit 48186aa530
15 changed files with 156 additions and 63 deletions

View file

@ -10,6 +10,7 @@
background: var(--background-color);
padding: 0.5rem;
border-radius: 0.5rem;
overflow-y: auto;
}
:global(.sidebar-unit > h3) {

View file

@ -2,11 +2,16 @@
import { Accordion, AccordionItem } from "flowbite-svelte"
export let expanded = false
export let noBorder = false
let defaultClass: string = undefined
if(noBorder){
defaultClass = "unstyled w-full flex-grow"
}
</script>
<Accordion>
<AccordionItem open={expanded} paddingDefault="p-0" inactiveClass="text-black">
<span slot="header" class="w-full p-2 text-base">
<AccordionItem open={expanded} paddingDefault="p-0" inactiveClass="text-black" {defaultClass}>
<span slot="header" class={!noBorder ? "w-full p-2 text-base" : "w-full"}>
<slot name="header" />
</span>
<div class="low-interaction rounded-b p-2">

View file

@ -3,25 +3,36 @@
import FilterOption from "./FilterOption.svelte"
import Loading from "../Base/Loading.svelte"
import FilterToggle from "./FilterToggle.svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
export let activeFilter: ActiveFilter
let { control, filter } = activeFilter
export let activeFilter: ActiveFilter[]
let { control, filter } = activeFilter[0]
let option = control.map(c => filter.options[c] ?? filter.options[0])
let loading = false
function clear() {
loading = true
requestIdleCallback(() => {
control.setData(undefined)
for (const af of activeFilter) {
af.control.setData(undefined)
}
loading = false
})
}
export let state: SpecialVisualizationState
let debug = state.featureSwitches.featureSwitchIsDebugging
</script>
{#if loading}
<Loading />
{:else }
<FilterToggle on:click={() => clear()}>
<FilterToggle on:click={() => clear()}>
<FilterOption option={$option} />
{#if $debug}
<span class="subtle">
({activeFilter.map(af => af.layer.id).join(", ")})
</span>
{/if}
</FilterToggle>
{/if}

View file

@ -8,10 +8,17 @@
import FilterToggle from "./FilterToggle.svelte"
import ToSvelte from "../Base/ToSvelte.svelte"
import Tr from "../Base/Tr.svelte"
import { Store } from "../../Logic/UIEventSource"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
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
let loading = false
const t =Translations.t.general.search
@ -33,8 +40,6 @@
loading = true
requestIdleCallback(() => {
enableAllLayers()
for (const activeFilter of activeFilters) {
activeFilter.control.setData(undefined)
}
@ -44,7 +49,7 @@
}
</script>
{#if activeFilters.length > 0 || $nonactiveLayers.length > 0}
{#if mergedActiveFilters.length > 0 || $nonactiveLayers.length > 0}
<SidebarUnit>
<div class="flex justify-between">
<h3><Tr t={t.activeFilters}/></h3>
@ -81,9 +86,9 @@
{/if}
{#each activeFilters as activeFilter (activeFilter)}
{#each mergedActiveFilters as activeFilter (activeFilter)}
<div>
<ActiveFilterSvelte {activeFilter} />
<ActiveFilterSvelte {activeFilter} {state}/>
</div>
{/each}
</div>

View file

@ -8,13 +8,19 @@
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import Loading from "../Base/Loading.svelte"
export let entry: FilterSearchResult | LayerConfig
let isLayer = entry instanceof LayerConfig
let asLayer = <LayerConfig>entry
let asFilter = <FilterSearchResult>entry
export let entry: FilterSearchResult[] | LayerConfig
let asFilter: FilterSearchResult[]
let asLayer: LayerConfig
if(Array.isArray(entry)){
asFilter = entry
}else{
asLayer = <LayerConfig>entry
}
export let state: SpecialVisualizationState
let loading = false
let debug = state.featureSwitches.featureSwitchIsDebugging
function apply() {
loading = true
@ -34,7 +40,7 @@
{/if}
<div class="flex flex-col items-start">
<div class="flex items-center gap-x-1">
{#if isLayer}
{#if asLayer}
<div class="w-8 h-8 p-1">
<ToSvelte construct={asLayer.defaultIcon()} />
</div>
@ -42,8 +48,11 @@
<Tr t={asLayer.name} />
</b>
{:else}
<Icon icon={asFilter.option.icon ?? asFilter.option.emoji} clss="w-4 h-4" emojiHeight="14px" />
<Tr cls="whitespace-nowrap" t={asFilter.option.question} />
<Icon icon={asFilter[0].option.icon ?? asFilter[0].option.emoji} clss="w-4 h-4" emojiHeight="14px" />
<Tr cls="whitespace-nowrap" t={asFilter[0].option.question} />
{#if $debug}
<span class="subtle">({asFilter.map(f => f.layer.id).join(", ")})</span>
{/if}
{/if}
</div>
</div>

View file

@ -4,15 +4,21 @@
import type { SpecialVisualizationState } from "../SpecialVisualization"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import FilterSearch from "../../Logic/Search/FilterSearch"
import type { FilterSearchResult } from "../../Logic/Search/FilterSearch"
import Tr from "../Base/Tr.svelte"
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
let searchTerm = state.searchState.searchTerm
let activeLayers = state.layerState.activeLayers
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 => {
const nowActive = activeLayers.data.filter(al => al.layerDef.isNormal())
@ -22,30 +28,44 @@
}
return layers
}, [activeLayers])
let filterResultsClipped = filterResults.mapD(filters => {
let filterResultsClipped: Store<{
clipped: (FilterSearchResult[] | LayerConfig)[],
rest?: (FilterSearchResult[] | LayerConfig)[]
}> = filtersMerged.mapD(filters => {
let layers = layerResults.data
const ls: (FilterSearchResult | LayerConfig)[] = [].concat(layers, filters)
const ls: (FilterSearchResult[] | LayerConfig)[] = [].concat(layers, filters)
if (ls.length <= 6) {
return ls
return { clipped: ls }
}
return ls.slice(0, 4)
}, [layerResults, activeLayers])
return { clipped: ls.slice(0, 4), rest: ls.slice(4) }
}, [layerResults, activeLayers, Locale.language])
</script>
{#if $searchTerm.length > 0 && ($filterResults.length > 0 || $layerResults.length > 0)}
<SidebarUnit>
<h3><Tr t={Translations.t.general.search.pickFilter} /></h3>
<h3>
<Tr t={Translations.t.general.search.pickFilter} />
</h3>
<div class="flex flex-wrap">
{#each $filterResultsClipped as filterResult (filterResult)}
{#each $filterResultsClipped.clipped as filterResult (filterResult)}
<FilterResultSvelte {state} entry={filterResult} />
{/each}
</div>
{#if $filterResults.length + $layerResults.length > $filterResultsClipped.length}
<div class="flex justify-center">
... and {$filterResults.length + $layerResults.length - $filterResultsClipped.length} more ...
</div>
{#if $filtersMerged.length + $layerResults.length > $filterResultsClipped.clipped.length}
<AccordionSingle noBorder>
<div class="flex justify-end text-sm subtle" slot="header">
<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}
</SidebarUnit>
{/if}

View file

@ -4,16 +4,22 @@
import Constants from "../../Models/Constants"
import type { ActiveFilter } from "../../Logic/State/LayerState"
import ThemeViewState from "../../Models/ThemeViewState"
import ThemeResults from "./ThemeResults.svelte"
import ThemeResults from "./ThemeResults.svelte"
import GeocodeResults from "./GeocodeResults.svelte"
import FilterResults from "./FilterResults.svelte"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
import type { FilterSearchResult } from "../../Logic/Search/FilterSearch"
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) &&
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 searchTerm = state.searchState.searchTerm
</script>
@ -23,13 +29,15 @@
{#if $searchTerm.length === 0 && $activeFilters.length === 0 }
<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>
{/if}
<FilterResults {state}/>
<FilterResults {state} />
<GeocodeResults {state}/>
<GeocodeResults {state} />
{#if $allowOtherThemes}
<ThemeResults {state} />