mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-21 07:14:11 +00:00
Simplify filter UI
Simplify filter UI by using SearchFilterDropdown for all filters - Remove complex template logic with Dropdown and MultiSelect components - Use SearchFilterDropdown component for all filters (consistent with sort/mode dropdowns) - Remove multi-select support to simplify implementation - Add placeholder and clear functionality to SearchFilterDropdown
This commit is contained in:
@@ -47,56 +47,13 @@
|
||||
</div>
|
||||
<!-- Add search refinement dropdowns if provider supports them -->
|
||||
<div v-if="filterOptions?.length" class="flex gap-3 ml-1 text-sm">
|
||||
<template v-for="filterOption in filterOptions" :key="filterOption.id">
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="text-muted">{{ filterOption.label }}:</span>
|
||||
<Dropdown
|
||||
v-if="filterOption.type === 'single-select'"
|
||||
:model-value="selectedFilters[filterOption.id] as string"
|
||||
:options="filterOption.options || []"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
placeholder="Any"
|
||||
:show-clear="true"
|
||||
class="min-w-[6rem] border-none bg-transparent shadow-none"
|
||||
:pt="{
|
||||
input: { class: 'py-0 px-1 border-none' },
|
||||
trigger: { class: 'hidden' },
|
||||
panel: { class: 'shadow-md' },
|
||||
item: { class: 'py-2 px-3 text-sm' }
|
||||
}"
|
||||
@update:model-value="
|
||||
$event
|
||||
? (selectedFilters[filterOption.id] = $event)
|
||||
: delete selectedFilters[filterOption.id]
|
||||
"
|
||||
/>
|
||||
<MultiSelect
|
||||
v-else-if="filterOption.type === 'multi-select'"
|
||||
:model-value="selectedFilters[filterOption.id] as string[]"
|
||||
:options="filterOption.options || []"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
display="chip"
|
||||
class="min-w-[6rem] border-none bg-transparent shadow-none"
|
||||
:pt="{
|
||||
input: { class: 'py-0 px-1 border-none' },
|
||||
trigger: { class: 'hidden' },
|
||||
panel: { class: 'shadow-md' },
|
||||
item: { class: 'py-2 px-3 text-sm' },
|
||||
label: { class: 'py-0 px-1 text-sm' },
|
||||
header: { class: 'p-2' },
|
||||
filterInput: { class: 'text-sm' },
|
||||
emptyMessage: { class: 'text-sm text-muted p-3' }
|
||||
}"
|
||||
@update:model-value="
|
||||
$event?.length > 0
|
||||
? (selectedFilters[filterOption.id] = $event)
|
||||
: delete selectedFilters[filterOption.id]
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<SearchFilterDropdown
|
||||
v-for="filterOption in filterOptions"
|
||||
:key="filterOption.id"
|
||||
v-model:modelValue="selectedFilters[filterOption.id]"
|
||||
:options="availableFilterOptions(filterOption)"
|
||||
:label="filterOption.label"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,8 +64,6 @@ import { stubTrue } from 'lodash'
|
||||
import AutoComplete, {
|
||||
AutoCompleteOptionSelectEvent
|
||||
} from 'primevue/autocomplete'
|
||||
import Dropdown from 'primevue/dropdown'
|
||||
import MultiSelect from 'primevue/multiselect'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
@@ -157,6 +112,17 @@ const searchModeOptions: SearchOption<SearchMode>[] = [
|
||||
{ id: 'nodes', label: t('g.nodes') }
|
||||
]
|
||||
|
||||
// Convert filter options to SearchOption format for SearchFilterDropdown
|
||||
const availableFilterOptions = (
|
||||
filter: SearchFilter
|
||||
): SearchOption<string>[] => {
|
||||
if (!filter.options) return []
|
||||
return filter.options.map((option) => ({
|
||||
id: option.value,
|
||||
label: option.label
|
||||
}))
|
||||
}
|
||||
|
||||
// When a dropdown query suggestion is selected, update the search query
|
||||
const onOptionSelect = (event: AutoCompleteOptionSelectEvent) => {
|
||||
searchQuery.value = event.value.query
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
:options="options"
|
||||
option-label="label"
|
||||
option-value="id"
|
||||
placeholder="Any"
|
||||
class="min-w-[6rem] border-none bg-transparent shadow-none"
|
||||
:pt="{
|
||||
input: { class: 'py-0 px-1 border-none' },
|
||||
|
||||
@@ -108,25 +108,6 @@ export function useRegistrySearch(
|
||||
return getFilterableFields()
|
||||
})
|
||||
|
||||
// Initialize filters with default values when they become available
|
||||
const filterOptionsInitialized = ref(false)
|
||||
watch(
|
||||
filterOptions,
|
||||
(newOptions) => {
|
||||
if (!filterOptionsInitialized.value && newOptions.length > 0) {
|
||||
const defaultFilters: ActiveFilters = {}
|
||||
for (const option of newOptions) {
|
||||
if (option.defaultValue !== undefined) {
|
||||
defaultFilters[option.id] = option.defaultValue
|
||||
}
|
||||
}
|
||||
activeFilters.value = { ...activeFilters.value, ...defaultFilters }
|
||||
filterOptionsInitialized.value = true
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
pageNumber,
|
||||
|
||||
@@ -16,9 +16,9 @@ export type QuerySuggestion = {
|
||||
export interface SearchFilter {
|
||||
id: string
|
||||
label: string
|
||||
type: 'multi-select' | 'single-select' | 'boolean'
|
||||
type: 'single-select' | 'boolean'
|
||||
options?: FilterOption[]
|
||||
defaultValue?: string | string[] | boolean
|
||||
defaultValue?: string | boolean
|
||||
}
|
||||
|
||||
export interface FilterOption {
|
||||
@@ -27,7 +27,7 @@ export interface FilterOption {
|
||||
icon?: string
|
||||
}
|
||||
|
||||
export type ActiveFilters = Record<string, string | string[] | boolean>
|
||||
export type ActiveFilters = Record<string, string | boolean>
|
||||
|
||||
export interface SearchPacksResult {
|
||||
nodePacks: RegistryNodePack[]
|
||||
|
||||
Reference in New Issue
Block a user