mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-15 01:48:06 +00:00
Compare commits
1 Commits
fix/codera
...
search-imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
436798d23d |
@@ -24,8 +24,8 @@
|
||||
</p>
|
||||
|
||||
<!-- Badges -->
|
||||
<div class="flex flex-wrap gap-2 empty:hidden">
|
||||
<NodePricingBadge :node-def="nodeDef" />
|
||||
<div class="flex flex-wrap gap-2 empty:hidden overflow-hidden">
|
||||
<NodePricingBadge class="max-w-full truncate" :node-def="nodeDef" />
|
||||
<NodeProviderBadge :node-def="nodeDef" />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex min-h-0 flex-col overflow-y-auto py-2.5">
|
||||
<div ref="sidebarRef" class="flex min-h-0 flex-col overflow-y-auto py-2.5">
|
||||
<!-- Preset categories -->
|
||||
<div class="flex flex-col px-1">
|
||||
<button
|
||||
@@ -38,6 +38,7 @@
|
||||
:node="category"
|
||||
:selected-category="selectedCategory"
|
||||
:selected-collapsed="selectedCollapsed"
|
||||
:show-chevrons="isHovered"
|
||||
@select="selectCategory"
|
||||
/>
|
||||
</div>
|
||||
@@ -45,6 +46,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useElementHover } from '@vueuse/core'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
@@ -64,6 +66,9 @@ const selectedCategory = defineModel<string>('selectedCategory', {
|
||||
required: true
|
||||
})
|
||||
|
||||
const sidebarRef = ref<HTMLElement>()
|
||||
const isHovered = useElementHover(sidebarRef)
|
||||
|
||||
const { t } = useI18n()
|
||||
const { flags } = useFeatureFlags()
|
||||
const nodeDefStore = useNodeDefStore()
|
||||
|
||||
@@ -1,32 +1,47 @@
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
:data-testid="`category-${node.key}`"
|
||||
:aria-current="selectedCategory === node.key || undefined"
|
||||
:style="{ paddingLeft: `${0.75 + depth * 1.25}rem` }"
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'w-full cursor-pointer rounded border-none bg-transparent py-2.5 pr-3 text-left text-sm transition-colors',
|
||||
selectedCategory === node.key
|
||||
? CATEGORY_SELECTED_CLASS
|
||||
: CATEGORY_UNSELECTED_CLASS
|
||||
isExpanded &&
|
||||
hasChildren &&
|
||||
'flex flex-col rounded-lg bg-secondary-background'
|
||||
)
|
||||
"
|
||||
@click="$emit('select', node.key)"
|
||||
>
|
||||
{{ node.label }}
|
||||
</button>
|
||||
<template v-if="isExpanded && node.children?.length">
|
||||
<NodeSearchCategoryTreeNode
|
||||
v-for="child in node.children"
|
||||
:key="child.key"
|
||||
:node="child"
|
||||
:depth="depth + 1"
|
||||
:selected-category="selectedCategory"
|
||||
:selected-collapsed="selectedCollapsed"
|
||||
@select="$emit('select', $event)"
|
||||
/>
|
||||
</template>
|
||||
<Button
|
||||
type="button"
|
||||
variant="muted-textonly"
|
||||
size="unset"
|
||||
:data-testid="`category-${node.key}`"
|
||||
:aria-current="selectedCategory === node.key || undefined"
|
||||
:class="categoryButtonClass"
|
||||
:style="{ paddingLeft: paddingLeft }"
|
||||
@click="$emit('select', node.key)"
|
||||
>
|
||||
<i
|
||||
v-if="showChevrons && hasChildren"
|
||||
:class="
|
||||
cn(
|
||||
'pi pi-chevron-down shrink-0 text-[10px] transition-transform',
|
||||
!isExpanded && '-rotate-90'
|
||||
)
|
||||
"
|
||||
/>
|
||||
{{ node.label }}
|
||||
</Button>
|
||||
<template v-if="isExpanded && hasChildren">
|
||||
<NodeSearchCategoryTreeNode
|
||||
v-for="child in node.children"
|
||||
:key="child.key"
|
||||
:node="child"
|
||||
:depth="depth + 1"
|
||||
:selected-category="selectedCategory"
|
||||
:selected-collapsed="selectedCollapsed"
|
||||
:show-chevrons="showChevrons"
|
||||
@select="$emit('select', $event)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -45,26 +60,43 @@ export const CATEGORY_UNSELECTED_CLASS =
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
const {
|
||||
node,
|
||||
depth = 0,
|
||||
selectedCategory,
|
||||
selectedCollapsed = false
|
||||
selectedCollapsed = false,
|
||||
showChevrons = false
|
||||
} = defineProps<{
|
||||
node: CategoryNode
|
||||
depth?: number
|
||||
selectedCategory: string
|
||||
selectedCollapsed?: boolean
|
||||
showChevrons?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
select: [key: string]
|
||||
}>()
|
||||
|
||||
const hasChildren = computed(() => (node.children?.length ?? 0) > 0)
|
||||
|
||||
const isExpanded = computed(() => {
|
||||
if (selectedCategory === node.key) return !selectedCollapsed
|
||||
return selectedCategory.startsWith(node.key + '/')
|
||||
})
|
||||
|
||||
const paddingLeft = computed(() => {
|
||||
if (hasChildren.value && showChevrons) return `${0.5 + depth * 1.25}rem`
|
||||
return `${0.75 + depth * 1.25}rem`
|
||||
})
|
||||
|
||||
const categoryButtonClass = computed(() =>
|
||||
cn(
|
||||
'w-full justify-start rounded py-3 pr-3 font-normal hover:text-foreground',
|
||||
selectedCategory === node.key && CATEGORY_SELECTED_CLASS
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<NodeSearchFilterBar
|
||||
class="flex-1"
|
||||
:active-chip-key="activeFilter?.key"
|
||||
:applied-filters="filters"
|
||||
@select-chip="onSelectFilterChip"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-2 px-2 py-1.5">
|
||||
<button
|
||||
<Button
|
||||
v-for="chip in chips"
|
||||
:key="chip.key"
|
||||
type="button"
|
||||
:variant="chipVariant(chip.key)"
|
||||
size="unset"
|
||||
:aria-pressed="activeChipKey === chip.key"
|
||||
:class="
|
||||
cn(
|
||||
'cursor-pointer rounded-md border px-3 py-1 text-sm transition-colors flex-auto border-secondary-background',
|
||||
activeChipKey === chip.key
|
||||
? 'bg-secondary-background text-foreground'
|
||||
: 'bg-transparent text-muted-foreground hover:border-base-foreground/60 hover:text-base-foreground/60'
|
||||
)
|
||||
"
|
||||
:class="chipExtraClass(chip.key)"
|
||||
@click="emit('selectChip', chip)"
|
||||
>
|
||||
{{ chip.label }}
|
||||
</button>
|
||||
<span
|
||||
v-if="appliedFilterCounts[chip.key]"
|
||||
class="ml-0.5 text-xs opacity-80"
|
||||
>
|
||||
({{ appliedFilterCounts[chip.key] }})
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { FuseFilter } from '@/utils/fuseUtil'
|
||||
import type { FuseFilter, FuseFilterWithValue } from '@/utils/fuseUtil'
|
||||
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
||||
|
||||
export interface FilterChip {
|
||||
@@ -35,11 +36,14 @@ export interface FilterChip {
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type { ButtonVariants } from '@/components/ui/button/button.variants'
|
||||
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
const { activeChipKey = null } = defineProps<{
|
||||
const { activeChipKey = null, appliedFilters = [] } = defineProps<{
|
||||
activeChipKey?: string | null
|
||||
appliedFilters?: FuseFilterWithValue<ComfyNodeDefImpl, string>[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -69,4 +73,29 @@ const chips = computed<FilterChip[]>(() => {
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const appliedFilterCounts = computed(() => {
|
||||
const counts: Record<string, number> = {}
|
||||
for (const filter of appliedFilters) {
|
||||
counts[filter.filterDef.id] = (counts[filter.filterDef.id] ?? 0) + 1
|
||||
}
|
||||
return counts
|
||||
})
|
||||
|
||||
function chipVariant(chipKey: string): ButtonVariants['variant'] {
|
||||
return activeChipKey === chipKey ? 'inverted' : 'outline'
|
||||
}
|
||||
|
||||
function chipExtraClass(chipKey: string) {
|
||||
const isActive = activeChipKey === chipKey
|
||||
const hasApplied = (appliedFilterCounts.value[chipKey] ?? 0) > 0
|
||||
|
||||
return cn(
|
||||
'flex-auto px-3 py-1',
|
||||
isActive && 'border border-solid border-base-foreground',
|
||||
!isActive &&
|
||||
hasApplied &&
|
||||
'bg-base-foreground/10 border-base-foreground text-base-foreground'
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -19,7 +19,9 @@ export const buttonVariants = cva({
|
||||
'text-muted-foreground bg-transparent hover:bg-secondary-background-hover',
|
||||
'destructive-textonly':
|
||||
'text-destructive-background bg-transparent hover:bg-destructive-background/10',
|
||||
'overlay-white': 'bg-white text-gray-600 hover:bg-white/90'
|
||||
'overlay-white': 'bg-white text-gray-600 hover:bg-white/90',
|
||||
outline:
|
||||
'border border-solid border-muted-foreground bg-transparent text-muted-foreground hover:border-base-foreground/60 hover:text-base-foreground/60'
|
||||
},
|
||||
size: {
|
||||
sm: 'h-6 rounded-sm px-2 py-1 text-xs',
|
||||
@@ -47,7 +49,8 @@ const variants = [
|
||||
'textonly',
|
||||
'muted-textonly',
|
||||
'destructive-textonly',
|
||||
'overlay-white'
|
||||
'overlay-white',
|
||||
'outline'
|
||||
] as const satisfies Array<ButtonVariants['variant']>
|
||||
const sizes = ['sm', 'md', 'lg', 'icon', 'icon-sm'] as const satisfies Array<
|
||||
ButtonVariants['size']
|
||||
|
||||
Reference in New Issue
Block a user