mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-10 18:10:08 +00:00
[Manager] Allowing changing sort field of registry search results (#3409)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
<RegistrySearchBar
|
||||
v-model:searchQuery="searchQuery"
|
||||
v-model:searchMode="searchMode"
|
||||
v-model:sortField="sortField"
|
||||
:search-results="searchResults"
|
||||
:suggestions="suggestions"
|
||||
/>
|
||||
@@ -166,6 +167,7 @@ const {
|
||||
isLoading: isSearchLoading,
|
||||
searchResults,
|
||||
searchMode,
|
||||
sortField,
|
||||
suggestions
|
||||
} = useRegistrySearch()
|
||||
pageNumber.value = 0
|
||||
|
||||
@@ -56,7 +56,10 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import SearchFilterDropdown from '@/components/dialog/content/manager/registrySearchBar/SearchFilterDropdown.vue'
|
||||
import type { NodesIndexSuggestion } from '@/services/algoliaSearchService'
|
||||
import type { PackField, SearchOption } from '@/types/comfyManagerTypes'
|
||||
import {
|
||||
type SearchOption,
|
||||
SortableAlgoliaField
|
||||
} from '@/types/comfyManagerTypes'
|
||||
import { components } from '@/types/comfyRegistryTypes'
|
||||
|
||||
const { searchResults } = defineProps<{
|
||||
@@ -66,7 +69,9 @@ const { searchResults } = defineProps<{
|
||||
|
||||
const searchQuery = defineModel<string>('searchQuery')
|
||||
const searchMode = defineModel<string>('searchMode', { default: 'packs' })
|
||||
const sortField = defineModel<PackField>('sortField', { default: 'downloads' })
|
||||
const sortField = defineModel<SortableAlgoliaField>('sortField', {
|
||||
default: SortableAlgoliaField.Downloads
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -74,11 +79,12 @@ const hasResults = computed(
|
||||
() => searchQuery.value?.trim() && searchResults?.length
|
||||
)
|
||||
|
||||
const sortOptions: SearchOption<PackField>[] = [
|
||||
{ id: 'downloads', label: t('manager.sort.downloads') },
|
||||
{ id: 'name', label: t('g.name') },
|
||||
{ id: 'rating', label: t('manager.sort.rating') },
|
||||
{ id: 'category', label: t('g.category') }
|
||||
const sortOptions: SearchOption<SortableAlgoliaField>[] = [
|
||||
{ id: SortableAlgoliaField.Downloads, label: t('manager.sort.downloads') },
|
||||
{ id: SortableAlgoliaField.Created, label: t('manager.sort.created') },
|
||||
{ id: SortableAlgoliaField.Updated, label: t('manager.sort.updated') },
|
||||
{ id: SortableAlgoliaField.Publisher, label: t('manager.sort.publisher') },
|
||||
{ id: SortableAlgoliaField.Name, label: t('g.name') }
|
||||
]
|
||||
const filterOptions: SearchOption<string>[] = [
|
||||
{ id: 'packs', label: t('manager.filter.nodePack') },
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { watchDebounced } from '@vueuse/core'
|
||||
import { memoize } from 'lodash'
|
||||
import type { Hit } from 'algoliasearch/dist/lite/browser'
|
||||
import { memoize, orderBy } from 'lodash'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import {
|
||||
@@ -8,17 +9,30 @@ import {
|
||||
useAlgoliaSearchService
|
||||
} from '@/services/algoliaSearchService'
|
||||
import type { NodesIndexSuggestion } from '@/services/algoliaSearchService'
|
||||
import { PackField } from '@/types/comfyManagerTypes'
|
||||
import { SortableAlgoliaField } from '@/types/comfyManagerTypes'
|
||||
|
||||
const SEARCH_DEBOUNCE_TIME = 256
|
||||
const DEFAULT_PAGE_SIZE = 64
|
||||
const DEFAULT_SORT_FIELD = SortableAlgoliaField.Downloads // Set in the index configuration
|
||||
|
||||
const SORT_DIRECTIONS: Record<SortableAlgoliaField, 'asc' | 'desc'> = {
|
||||
[SortableAlgoliaField.Downloads]: 'desc',
|
||||
[SortableAlgoliaField.Created]: 'desc',
|
||||
[SortableAlgoliaField.Updated]: 'desc',
|
||||
[SortableAlgoliaField.Publisher]: 'asc',
|
||||
[SortableAlgoliaField.Name]: 'asc'
|
||||
}
|
||||
|
||||
const isDateField = (field: SortableAlgoliaField): boolean =>
|
||||
field === SortableAlgoliaField.Created ||
|
||||
field === SortableAlgoliaField.Updated
|
||||
|
||||
/**
|
||||
* Composable for managing UI state of Comfy Node Registry search.
|
||||
*/
|
||||
export function useRegistrySearch() {
|
||||
const isLoading = ref(false)
|
||||
const sortField = ref<PackField>('downloads')
|
||||
const sortField = ref<SortableAlgoliaField>(SortableAlgoliaField.Downloads)
|
||||
const searchMode = ref<'nodes' | 'packs'>('packs')
|
||||
const pageSize = ref(DEFAULT_PAGE_SIZE)
|
||||
const pageNumber = ref(0)
|
||||
@@ -48,6 +62,15 @@ export function useRegistrySearch() {
|
||||
toRegistryPack,
|
||||
(algoliaNode: AlgoliaNodePack) => algoliaNode.id
|
||||
)
|
||||
const getSortValue = (pack: Hit<AlgoliaNodePack>) => {
|
||||
if (isDateField(sortField.value)) {
|
||||
const value = pack[sortField.value]
|
||||
return value ? new Date(value).getTime() : 0
|
||||
} else {
|
||||
const value = pack[sortField.value]
|
||||
return value ?? 0
|
||||
}
|
||||
}
|
||||
|
||||
const updateSearchResults = async (options: { append?: boolean }) => {
|
||||
isLoading.value = true
|
||||
@@ -62,10 +85,22 @@ export function useRegistrySearch() {
|
||||
restrictSearchableAttributes: searchAttributes.value
|
||||
}
|
||||
)
|
||||
|
||||
let sortedPacks = nodePacks
|
||||
|
||||
// Results are sorted by the default field to begin with -- so don't manually sort again
|
||||
if (sortField.value && sortField.value !== DEFAULT_SORT_FIELD) {
|
||||
sortedPacks = orderBy(
|
||||
nodePacks,
|
||||
[getSortValue],
|
||||
[SORT_DIRECTIONS[sortField.value]]
|
||||
)
|
||||
}
|
||||
|
||||
if (options.append && results.value?.length) {
|
||||
results.value = results.value.concat(nodePacks)
|
||||
results.value = results.value.concat(sortedPacks)
|
||||
} else {
|
||||
results.value = nodePacks
|
||||
results.value = sortedPacks
|
||||
}
|
||||
suggestions.value = querySuggestions
|
||||
isLoading.value = false
|
||||
|
||||
@@ -154,8 +154,10 @@
|
||||
"unknown": "Unknown"
|
||||
},
|
||||
"sort": {
|
||||
"rating": "Rating",
|
||||
"downloads": "Most Popular"
|
||||
"downloads": "Most Popular",
|
||||
"publisher": "Publisher",
|
||||
"created": "Newest",
|
||||
"updated": "Updated Recently"
|
||||
},
|
||||
"filter": {
|
||||
"nodePack": "Node Pack",
|
||||
|
||||
@@ -438,8 +438,10 @@
|
||||
"searchPlaceholder": "Buscar",
|
||||
"selectVersion": "Seleccionar Versión",
|
||||
"sort": {
|
||||
"created": "Más reciente",
|
||||
"downloads": "Más Popular",
|
||||
"rating": "Calificación"
|
||||
"publisher": "Editor",
|
||||
"updated": "Actualizado recientemente"
|
||||
},
|
||||
"status": {
|
||||
"active": "Activo",
|
||||
|
||||
@@ -438,8 +438,10 @@
|
||||
"searchPlaceholder": "Recherche",
|
||||
"selectVersion": "Sélectionner la version",
|
||||
"sort": {
|
||||
"created": "Le plus récent",
|
||||
"downloads": "Le plus populaire",
|
||||
"rating": "Évaluation"
|
||||
"publisher": "Éditeur",
|
||||
"updated": "Mis à jour récemment"
|
||||
},
|
||||
"status": {
|
||||
"active": "Actif",
|
||||
|
||||
@@ -438,8 +438,10 @@
|
||||
"searchPlaceholder": "検索",
|
||||
"selectVersion": "バージョンを選択",
|
||||
"sort": {
|
||||
"created": "最新",
|
||||
"downloads": "最も人気",
|
||||
"rating": "評価"
|
||||
"publisher": "出版社",
|
||||
"updated": "最近更新"
|
||||
},
|
||||
"status": {
|
||||
"active": "アクティブ",
|
||||
|
||||
@@ -438,8 +438,10 @@
|
||||
"searchPlaceholder": "검색",
|
||||
"selectVersion": "버전 선택",
|
||||
"sort": {
|
||||
"created": "최신",
|
||||
"downloads": "가장 인기 있는",
|
||||
"rating": "평점"
|
||||
"publisher": "출판사",
|
||||
"updated": "최근 업데이트"
|
||||
},
|
||||
"status": {
|
||||
"active": "활성",
|
||||
|
||||
@@ -438,8 +438,10 @@
|
||||
"searchPlaceholder": "Поиск",
|
||||
"selectVersion": "Выберите версию",
|
||||
"sort": {
|
||||
"created": "Новейшие",
|
||||
"downloads": "Самые популярные",
|
||||
"rating": "Рейтинг"
|
||||
"publisher": "Издатель",
|
||||
"updated": "Недавно обновленные"
|
||||
},
|
||||
"status": {
|
||||
"active": "Активный",
|
||||
|
||||
@@ -438,8 +438,10 @@
|
||||
"searchPlaceholder": "搜索",
|
||||
"selectVersion": "选择版本",
|
||||
"sort": {
|
||||
"created": "最新",
|
||||
"downloads": "最受欢迎",
|
||||
"rating": "评级"
|
||||
"publisher": "出版商",
|
||||
"updated": "最近更新"
|
||||
},
|
||||
"status": {
|
||||
"active": "活跃",
|
||||
|
||||
@@ -61,6 +61,7 @@ const RETRIEVE_ATTRIBUTES: SearchAttribute[] = [
|
||||
'status',
|
||||
'publisher_id',
|
||||
'total_install',
|
||||
'create_time',
|
||||
'update_time',
|
||||
'license',
|
||||
'repository_url',
|
||||
|
||||
@@ -18,6 +18,14 @@ export enum ManagerTab {
|
||||
UpdateAvailable = 'updateAvailable'
|
||||
}
|
||||
|
||||
export enum SortableAlgoliaField {
|
||||
Downloads = 'total_install',
|
||||
Created = 'create_time',
|
||||
Updated = 'update_time',
|
||||
Publisher = 'publisher_id',
|
||||
Name = 'name'
|
||||
}
|
||||
|
||||
export interface TabItem {
|
||||
id: string
|
||||
label: string
|
||||
|
||||
Reference in New Issue
Block a user