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
|
|
@ -10,6 +10,7 @@
|
|||
background: var(--background-color);
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
:global(.sidebar-unit > h3) {
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue