mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 18:52:19 +00:00
Use responsive grid for templates dialog (#2791)
This commit is contained in:
@@ -1,56 +1,75 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex h-96 overflow-y-hidden"
|
||||
class="flex flex-col h-[83vh] w-[90vw] relative"
|
||||
data-testid="template-workflows-content"
|
||||
>
|
||||
<div class="relative">
|
||||
<ProgressSpinner
|
||||
v-if="!workflowTemplatesStore.isLoaded"
|
||||
class="absolute w-8 h-full inset-0"
|
||||
/>
|
||||
<Listbox
|
||||
:model-value="selectedTab"
|
||||
@update:model-value="handleTabSelection"
|
||||
:options="tabs"
|
||||
option-group-label="label"
|
||||
option-label="title"
|
||||
option-group-children="modules"
|
||||
scroll-height="auto"
|
||||
class="overflow-y-auto w-64 h-full"
|
||||
listStyle="max-height:unset"
|
||||
/>
|
||||
</div>
|
||||
<Carousel
|
||||
class="carousel justify-center"
|
||||
:value="selectedTab.templates"
|
||||
:responsive-options="responsiveOptions"
|
||||
:num-visible="4"
|
||||
:num-scroll="3"
|
||||
:key="`${selectedTab.moduleName}${selectedTab.title}`"
|
||||
>
|
||||
<template #item="slotProps">
|
||||
<div class="p-2 justify-items-center">
|
||||
<TemplateWorkflowCard
|
||||
:sourceModule="selectedTab.moduleName"
|
||||
:template="slotProps.data"
|
||||
:loading="slotProps.data.name === workflowLoading"
|
||||
:categoryTitle="selectedTab.title"
|
||||
@loadWorkflow="loadWorkflow"
|
||||
/>
|
||||
<Button
|
||||
v-if="isSmallScreen"
|
||||
:icon="isSideNavOpen ? 'pi pi-chevron-left' : 'pi pi-chevron-right'"
|
||||
text
|
||||
class="absolute top-1/2 -translate-y-1/2 z-10"
|
||||
:class="isSideNavOpen ? 'left-[19rem]' : 'left-2'"
|
||||
@click="toggleSideNav"
|
||||
/>
|
||||
<Divider
|
||||
class="m-0 [&::before]:border-surface-border/70 [&::before]:border-t-2"
|
||||
/>
|
||||
<div class="flex flex-1 relative overflow-hidden">
|
||||
<aside
|
||||
v-if="isSideNavOpen"
|
||||
class="absolute translate-x-0 top-0 left-0 h-full w-80 shadow-md z-5 transition-transform duration-300 ease-in-out"
|
||||
>
|
||||
<ProgressSpinner
|
||||
v-if="!workflowTemplatesStore.isLoaded"
|
||||
class="absolute w-8 h-full inset-0"
|
||||
/>
|
||||
<TemplateWorkflowsSideNav
|
||||
:tabs="tabs"
|
||||
:selected-tab="selectedTab"
|
||||
@update:selected-tab="handleTabSelection"
|
||||
/>
|
||||
</aside>
|
||||
<div
|
||||
class="flex-1 overflow-auto transition-all duration-300"
|
||||
:class="{
|
||||
'pl-80': isSideNavOpen || !isSmallScreen,
|
||||
'pl-8': !isSideNavOpen && isSmallScreen
|
||||
}"
|
||||
>
|
||||
<div v-if="selectedTab" class="flex flex-col px-12 pb-4">
|
||||
<div class="py-3 text-left">
|
||||
<h2 class="text-lg">{{ selectedTab.title }}</h2>
|
||||
</div>
|
||||
<div
|
||||
class="grid grid-cols-[repeat(auto-fill,minmax(16rem,1fr))] gap-8 justify-items-center"
|
||||
>
|
||||
<div v-for="template in selectedTab.templates" :key="template.name">
|
||||
<TemplateWorkflowCard
|
||||
:sourceModule="selectedTab.moduleName"
|
||||
:template="template"
|
||||
:loading="template.name === workflowLoading"
|
||||
:categoryTitle="selectedTab.title"
|
||||
@loadWorkflow="loadWorkflow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Carousel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Carousel from 'primevue/carousel'
|
||||
import Listbox from 'primevue/listbox'
|
||||
import { useBreakpoints } from '@vueuse/core'
|
||||
import { useAsyncState } from '@vueuse/core'
|
||||
import Button from 'primevue/button'
|
||||
import Divider from 'primevue/divider'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import TemplateWorkflowCard from '@/components/templates/TemplateWorkflowCard.vue'
|
||||
import TemplateWorkflowsSideNav from '@/components/templates/TemplateWorkflowsSideNav.vue'
|
||||
import { api } from '@/scripts/api'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
@@ -59,43 +78,46 @@ import type { WorkflowTemplates } from '@/types/workflowTemplateTypes'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const responsiveOptions = ref([
|
||||
{
|
||||
breakpoint: '1660px',
|
||||
numVisible: 3,
|
||||
numScroll: 2
|
||||
},
|
||||
{
|
||||
breakpoint: '1360px',
|
||||
numVisible: 2,
|
||||
numScroll: 1
|
||||
},
|
||||
{
|
||||
breakpoint: '960px',
|
||||
numVisible: 1,
|
||||
numScroll: 1
|
||||
}
|
||||
])
|
||||
const breakpoints = useBreakpoints({
|
||||
mobile: 0,
|
||||
tablet: 768,
|
||||
desktop: 1024
|
||||
})
|
||||
const isSmallScreen = breakpoints.between('mobile', 'desktop')
|
||||
const isSideNavOpen = ref(!isSmallScreen.value)
|
||||
const toggleSideNav = () => {
|
||||
isSideNavOpen.value = !isSideNavOpen.value
|
||||
}
|
||||
watch(isSmallScreen, toggleSideNav)
|
||||
|
||||
const workflowTemplatesStore = useWorkflowTemplatesStore()
|
||||
const { isReady } = useAsyncState(
|
||||
workflowTemplatesStore.loadWorkflowTemplates,
|
||||
null
|
||||
)
|
||||
|
||||
const selectedTab = ref<WorkflowTemplates | null>(
|
||||
workflowTemplatesStore?.defaultTemplate
|
||||
workflowTemplatesStore.defaultTemplate
|
||||
)
|
||||
const workflowLoading = ref<string | null>(null)
|
||||
|
||||
const tabs = computed(() => workflowTemplatesStore.groupedTemplates)
|
||||
|
||||
onMounted(async () => {
|
||||
await workflowTemplatesStore.loadWorkflowTemplates()
|
||||
})
|
||||
|
||||
const handleTabSelection = (selection: WorkflowTemplates | null) => {
|
||||
//Listbox allows deselecting so this special case is ignored here
|
||||
if (selection !== selectedTab.value && selection !== null)
|
||||
if (selection !== selectedTab.value && selection !== null) {
|
||||
selectedTab.value = selection
|
||||
|
||||
// On small screens, close the sidebar when a category is selected
|
||||
if (isSmallScreen.value) {
|
||||
isSideNavOpen.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const loadWorkflow = async (id: string) => {
|
||||
if (!isReady.value) return
|
||||
|
||||
workflowLoading.value = id
|
||||
let json
|
||||
if (selectedTab.value.moduleName === 'default') {
|
||||
@@ -120,9 +142,3 @@ const loadWorkflow = async (id: string) => {
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.carousel {
|
||||
width: 66vw;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user