mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-13 17:10:06 +00:00
refactor: improve modal layout and add class prop to PropertiesAccordionItem
This commit is contained in:
@@ -5,25 +5,32 @@ import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import TransitionCollapse from './TransitionCollapse.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
const {
|
||||
disabled,
|
||||
label,
|
||||
enableEmptyState,
|
||||
tooltip,
|
||||
class: className
|
||||
} = defineProps<{
|
||||
disabled?: boolean
|
||||
label?: string
|
||||
enableEmptyState?: boolean
|
||||
tooltip?: string
|
||||
class?: string
|
||||
}>()
|
||||
|
||||
const isCollapse = defineModel<boolean>('collapse', { default: false })
|
||||
|
||||
const isExpanded = computed(() => !isCollapse.value && !props.disabled)
|
||||
const isExpanded = computed(() => !isCollapse.value && !disabled)
|
||||
|
||||
const tooltipConfig = computed(() => {
|
||||
if (!props.tooltip) return undefined
|
||||
return { value: props.tooltip, showDelay: 1000 }
|
||||
if (!tooltip) return undefined
|
||||
return { value: tooltip, showDelay: 1000 }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col bg-comfy-menu-bg">
|
||||
<div :class="cn('flex flex-col bg-comfy-menu-bg', className)">
|
||||
<div
|
||||
class="sticky top-0 z-10 flex items-center justify-between backdrop-blur-xl bg-inherit"
|
||||
>
|
||||
|
||||
@@ -1,25 +1,5 @@
|
||||
<template>
|
||||
<div class="base-widget-layout rounded-2xl overflow-hidden relative">
|
||||
<Button
|
||||
v-show="!isRightPanelOpen && showRightPanelButton"
|
||||
size="icon"
|
||||
:class="
|
||||
cn('absolute top-4 right-18 z-10', 'transition-opacity duration-200', {
|
||||
'opacity-0 pointer-events-none':
|
||||
isRightPanelOpen || !showRightPanelButton
|
||||
})
|
||||
"
|
||||
@click="toggleRightPanel"
|
||||
>
|
||||
<i class="icon-[lucide--panel-right] text-sm" />
|
||||
</Button>
|
||||
<Button
|
||||
size="lg"
|
||||
class="absolute top-4 right-6 z-10 transition-opacity duration-200 w-10"
|
||||
@click="closeDialog"
|
||||
>
|
||||
<i class="pi pi-times" />
|
||||
</Button>
|
||||
<div class="flex h-full w-full">
|
||||
<Transition name="slide-panel">
|
||||
<nav
|
||||
@@ -55,24 +35,18 @@
|
||||
<slot name="header"></slot>
|
||||
</div>
|
||||
<slot name="header-right-area"></slot>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'flex justify-end gap-2 w-0',
|
||||
showRightPanelButton && !isRightPanelOpen
|
||||
? 'min-w-22'
|
||||
: 'min-w-10'
|
||||
)
|
||||
"
|
||||
>
|
||||
<template v-if="!isRightPanelOpen">
|
||||
<Button
|
||||
v-if="isRightPanelOpen && showRightPanelButton"
|
||||
v-if="showRightPanelButton"
|
||||
size="icon"
|
||||
@click="toggleRightPanel"
|
||||
>
|
||||
<i class="icon-[lucide--panel-right-close]" />
|
||||
<i class="icon-[lucide--panel-right] text-sm" />
|
||||
</Button>
|
||||
</div>
|
||||
<Button size="lg" class="w-10" @click="closeDialog">
|
||||
<i class="pi pi-times" />
|
||||
</Button>
|
||||
</template>
|
||||
</header>
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col">
|
||||
@@ -93,9 +67,33 @@
|
||||
</div>
|
||||
<aside
|
||||
v-if="hasRightPanel && isRightPanelOpen"
|
||||
class="w-1/4 min-w-40 max-w-80"
|
||||
class="flex w-72 shrink-0 bg-modal-panel-background flex-col border-l border-border-default"
|
||||
>
|
||||
<slot name="rightPanel" />
|
||||
<header
|
||||
data-component-id="RightPanelHeader"
|
||||
class="flex h-16 shrink-0 items-center gap-2 border-b border-border-default px-4"
|
||||
>
|
||||
<h2 v-if="rightPanelTitle" class="flex-1 text-lg font-semibold">
|
||||
{{ rightPanelTitle }}
|
||||
</h2>
|
||||
<div v-else class="flex-1">
|
||||
<slot name="rightPanelHeaderTitle" />
|
||||
</div>
|
||||
<slot name="rightPanelHeaderActions" />
|
||||
<Button
|
||||
v-if="showRightPanelButton"
|
||||
size="icon"
|
||||
@click="toggleRightPanel"
|
||||
>
|
||||
<i class="icon-[lucide--panel-right-close] text-sm" />
|
||||
</Button>
|
||||
<Button size="icon" @click="closeDialog">
|
||||
<i class="pi pi-times" />
|
||||
</Button>
|
||||
</header>
|
||||
<div class="min-h-0 flex-1 overflow-y-auto">
|
||||
<slot name="rightPanel" />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,8 +108,9 @@ import Button from '@/components/ui/button/Button.vue'
|
||||
import { OnCloseKey } from '@/types/widgetTypes'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
const { contentTitle } = defineProps<{
|
||||
const { contentTitle, rightPanelTitle } = defineProps<{
|
||||
contentTitle: string
|
||||
rightPanelTitle?: string
|
||||
}>()
|
||||
|
||||
const isRightPanelOpen = defineModel<boolean>('rightPanelOpen', {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
data-component-id="AssetBrowserModal"
|
||||
class="size-full max-h-full max-w-full min-w-0"
|
||||
:content-title="displayTitle"
|
||||
:right-panel-title="$t('assetBrowser.modelInfo.title')"
|
||||
@close="handleClose"
|
||||
>
|
||||
<template v-if="shouldShowLeftPanel" #leftPanel>
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onClickOutside, useImage } from '@vueuse/core'
|
||||
import { computed, ref, toValue, useId, useTemplateRef, watch } from 'vue'
|
||||
import { computed, ref, toValue, useId, useTemplateRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import IconGroup from '@/components/button/IconGroup.vue'
|
||||
@@ -160,21 +160,19 @@ const dropdownMenuButton = useTemplateRef<InstanceType<typeof MoreButton>>(
|
||||
'dropdown-menu-button'
|
||||
)
|
||||
|
||||
const stopClickOutside = ref<(() => void) | null>(null)
|
||||
|
||||
watch(
|
||||
() => focused,
|
||||
(isFocused) => {
|
||||
stopClickOutside.value?.()
|
||||
stopClickOutside.value = null
|
||||
|
||||
if (isFocused && cardRef.value) {
|
||||
stopClickOutside.value = onClickOutside(cardRef, () => {
|
||||
emit('blur')
|
||||
})
|
||||
onClickOutside(
|
||||
cardRef,
|
||||
() => {
|
||||
if (focused) {
|
||||
emit('blur')
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{
|
||||
ignore: [
|
||||
'[data-component-id="ModelInfoPanel"]',
|
||||
'[data-component-id="RightPanelHeader"]'
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
const titleId = useId()
|
||||
|
||||
@@ -1,103 +1,98 @@
|
||||
<template>
|
||||
<div class="flex h-full flex-col bg-comfy-menu-bg">
|
||||
<div class="flex h-18 items-center border-b border-divider px-4">
|
||||
<h2 class="text-lg font-semibold">
|
||||
{{ $t('assetBrowser.modelInfo.title') }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto scrollbar-custom">
|
||||
<PropertiesAccordionItem>
|
||||
<template #label>
|
||||
<span class="text-xs uppercase">
|
||||
{{ $t('assetBrowser.modelInfo.basicInfo') }}
|
||||
</span>
|
||||
</template>
|
||||
<ModelInfoField :label="$t('assetBrowser.modelInfo.displayName')">
|
||||
<span class="text-sm">{{ displayName }}</span>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField :label="$t('assetBrowser.modelInfo.fileName')">
|
||||
<span class="text-sm">{{ asset.name }}</span>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField
|
||||
v-if="sourceUrl"
|
||||
:label="$t('assetBrowser.modelInfo.source')"
|
||||
<div
|
||||
data-component-id="ModelInfoPanel"
|
||||
class="flex h-full flex-col scrollbar-custom"
|
||||
>
|
||||
<PropertiesAccordionItem class="bg-transparent">
|
||||
<template #label>
|
||||
<span class="text-xs uppercase font-inter">
|
||||
{{ $t('assetBrowser.modelInfo.basicInfo') }}
|
||||
</span>
|
||||
</template>
|
||||
<ModelInfoField :label="$t('assetBrowser.modelInfo.displayName')">
|
||||
<span class="text-sm break-all">{{ displayName }}</span>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField :label="$t('assetBrowser.modelInfo.fileName')">
|
||||
<span class="text-sm break-all">{{ asset.name }}</span>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField
|
||||
v-if="sourceUrl"
|
||||
:label="$t('assetBrowser.modelInfo.source')"
|
||||
>
|
||||
<a
|
||||
:href="sourceUrl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-sm text-link hover:underline"
|
||||
>
|
||||
<a
|
||||
:href="sourceUrl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-sm text-link hover:underline"
|
||||
{{
|
||||
$t('assetBrowser.modelInfo.viewOnSource', { source: sourceName })
|
||||
}}
|
||||
</a>
|
||||
</ModelInfoField>
|
||||
</PropertiesAccordionItem>
|
||||
|
||||
<PropertiesAccordionItem class="bg-transparent">
|
||||
<template #label>
|
||||
<span class="text-xs uppercase font-inter">
|
||||
{{ $t('assetBrowser.modelInfo.modelTagging') }}
|
||||
</span>
|
||||
</template>
|
||||
<ModelInfoField
|
||||
v-if="modelType"
|
||||
:label="$t('assetBrowser.modelInfo.modelType')"
|
||||
>
|
||||
<span class="text-sm">{{ modelType }}</span>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField
|
||||
v-if="baseModel"
|
||||
:label="$t('assetBrowser.modelInfo.compatibleBaseModels')"
|
||||
>
|
||||
<span class="text-sm">{{ baseModel }}</span>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField
|
||||
v-if="additionalTags.length > 0"
|
||||
:label="$t('assetBrowser.modelInfo.additionalTags')"
|
||||
>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span
|
||||
v-for="tag in additionalTags"
|
||||
:key="tag"
|
||||
class="rounded px-2 py-0.5 text-xs"
|
||||
>
|
||||
{{
|
||||
$t('assetBrowser.modelInfo.viewOnSource', { source: sourceName })
|
||||
}}
|
||||
</a>
|
||||
</ModelInfoField>
|
||||
</PropertiesAccordionItem>
|
||||
|
||||
<PropertiesAccordionItem>
|
||||
<template #label>
|
||||
<span class="text-xs uppercase">
|
||||
{{ $t('assetBrowser.modelInfo.modelTagging') }}
|
||||
{{ tag }}
|
||||
</span>
|
||||
</template>
|
||||
<ModelInfoField
|
||||
v-if="modelType"
|
||||
:label="$t('assetBrowser.modelInfo.modelType')"
|
||||
>
|
||||
<span class="text-sm">{{ modelType }}</span>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField
|
||||
v-if="baseModel"
|
||||
:label="$t('assetBrowser.modelInfo.compatibleBaseModels')"
|
||||
>
|
||||
<span class="text-sm">{{ baseModel }}</span>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField
|
||||
v-if="additionalTags.length > 0"
|
||||
:label="$t('assetBrowser.modelInfo.additionalTags')"
|
||||
>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span
|
||||
v-for="tag in additionalTags"
|
||||
:key="tag"
|
||||
class="rounded bg-surface-container px-2 py-0.5 text-xs"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
</ModelInfoField>
|
||||
</PropertiesAccordionItem>
|
||||
</div>
|
||||
</ModelInfoField>
|
||||
</PropertiesAccordionItem>
|
||||
|
||||
<PropertiesAccordionItem>
|
||||
<template #label>
|
||||
<span class="text-xs uppercase">
|
||||
{{ $t('assetBrowser.modelInfo.modelDescription') }}
|
||||
<PropertiesAccordionItem class="bg-transparent">
|
||||
<template #label>
|
||||
<span class="text-xs uppercase font-inter">
|
||||
{{ $t('assetBrowser.modelInfo.modelDescription') }}
|
||||
</span>
|
||||
</template>
|
||||
<ModelInfoField
|
||||
v-if="triggerPhrases.length > 0"
|
||||
:label="$t('assetBrowser.modelInfo.triggerPhrases')"
|
||||
>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span
|
||||
v-for="phrase in triggerPhrases"
|
||||
:key="phrase"
|
||||
class="rounded px-2 py-0.5 text-xs"
|
||||
>
|
||||
{{ phrase }}
|
||||
</span>
|
||||
</template>
|
||||
<ModelInfoField
|
||||
v-if="triggerPhrases.length > 0"
|
||||
:label="$t('assetBrowser.modelInfo.triggerPhrases')"
|
||||
>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span
|
||||
v-for="phrase in triggerPhrases"
|
||||
:key="phrase"
|
||||
class="rounded bg-surface-container px-2 py-0.5 text-xs"
|
||||
>
|
||||
{{ phrase }}
|
||||
</span>
|
||||
</div>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField
|
||||
v-if="description"
|
||||
:label="$t('assetBrowser.modelInfo.description')"
|
||||
>
|
||||
<p class="text-sm whitespace-pre-wrap">{{ description }}</p>
|
||||
</ModelInfoField>
|
||||
</PropertiesAccordionItem>
|
||||
</div>
|
||||
</div>
|
||||
</ModelInfoField>
|
||||
<ModelInfoField
|
||||
v-if="description"
|
||||
:label="$t('assetBrowser.modelInfo.description')"
|
||||
>
|
||||
<p class="text-sm whitespace-pre-wrap">{{ description }}</p>
|
||||
</ModelInfoField>
|
||||
</PropertiesAccordionItem>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user