mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 10:42:44 +00:00
refactor: Create reusable Tab components and update AssetsSidebarTab UI
- Add new Tab and TabList components with provide/inject pattern for state management - Replace TextButton-based tabs with new Tab components in AssetsSidebarTab - Update AssetSidebarTemplate to use semantic color tokens (bg-interface-panel-surface) - Improve tab styling with proper hover and focus states
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex h-full flex-col bg-white dark-theme:bg-zinc-900"
|
class="flex h-full flex-col bg-interface-panel-surface"
|
||||||
:class="props.class"
|
:class="props.class"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
v-if="slots.top"
|
v-if="slots.top"
|
||||||
class="flex min-h-12 items-center border-b border-zinc-200 px-4 py-2 dark-theme:border-zinc-700"
|
class="flex min-h-12 items-center border-b border-interface-stroke px-4 py-2"
|
||||||
>
|
>
|
||||||
<slot name="top" />
|
<slot name="top" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -29,24 +29,10 @@
|
|||||||
</IconTextButton>
|
</IconTextButton>
|
||||||
</div>
|
</div>
|
||||||
<!-- Normal Tab View -->
|
<!-- Normal Tab View -->
|
||||||
<div v-else class="flex items-center gap-2 pt-4 pb-1">
|
<TabList v-else v-model="activeTab" class="pt-4 pb-1">
|
||||||
<TextButton
|
<Tab value="input">{{ $t('sideToolbar.labels.imported') }}</Tab>
|
||||||
:label="$t('sideToolbar.labels.imported')"
|
<Tab value="output">{{ $t('sideToolbar.labels.generated') }}</Tab>
|
||||||
:type="activeTab === 'input' ? 'secondary' : 'transparent'"
|
</TabList>
|
||||||
@click.stop="activeTab = 'input'"
|
|
||||||
/>
|
|
||||||
<TextButton
|
|
||||||
:label="$t('sideToolbar.labels.generated')"
|
|
||||||
:type="activeTab === 'output' ? 'secondary' : 'transparent'"
|
|
||||||
@click.stop="activeTab = 'output'"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- <Tabs v-else v-model:value="activeTab" class="w-full">
|
|
||||||
<TabList class="border-b border-neutral-300">
|
|
||||||
<Tab value="input">{{ $t('sideToolbar.labels.imported') }}</Tab>
|
|
||||||
<Tab value="output">{{ $t('sideToolbar.labels.generated') }}</Tab>
|
|
||||||
</TabList>
|
|
||||||
</Tabs> -->
|
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<VirtualGrid
|
<VirtualGrid
|
||||||
@@ -107,10 +93,11 @@ import { useToast } from 'primevue/usetoast'
|
|||||||
import { computed, onMounted, ref, watch } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
|
||||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||||
import VirtualGrid from '@/components/common/VirtualGrid.vue'
|
import VirtualGrid from '@/components/common/VirtualGrid.vue'
|
||||||
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
|
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
|
||||||
|
import Tab from '@/components/tab/Tab.vue'
|
||||||
|
import TabList from '@/components/tab/TabList.vue'
|
||||||
import MediaAssetCard from '@/platform/assets/components/MediaAssetCard.vue'
|
import MediaAssetCard from '@/platform/assets/components/MediaAssetCard.vue'
|
||||||
import { useMediaAssets } from '@/platform/assets/composables/useMediaAssets'
|
import { useMediaAssets } from '@/platform/assets/composables/useMediaAssets'
|
||||||
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||||
|
|||||||
43
src/components/tab/Tab.vue
Normal file
43
src/components/tab/Tab.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<button
|
||||||
|
:class="tabClasses"
|
||||||
|
role="tab"
|
||||||
|
:aria-selected="isActive"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
import { computed, inject } from 'vue'
|
||||||
|
|
||||||
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
|
|
||||||
|
const { value } = defineProps<{
|
||||||
|
value: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const currentValue = inject<Ref<string>>('tabs-value')
|
||||||
|
const updateValue = inject<(value: string) => void>('tabs-update')
|
||||||
|
|
||||||
|
const isActive = computed(() => currentValue?.value === value)
|
||||||
|
|
||||||
|
const tabClasses = computed(() => {
|
||||||
|
return cn(
|
||||||
|
// Base styles from TextButton
|
||||||
|
'flex items-center justify-center shrink-0',
|
||||||
|
'px-2.5 py-2 text-sm rounded-lg cursor-pointer transition-all duration-200',
|
||||||
|
'outline-hidden border-none',
|
||||||
|
// State styles with semantic tokens
|
||||||
|
isActive.value
|
||||||
|
? 'bg-interface-menu-component-surface-hovered text-text-primary text-bold'
|
||||||
|
: 'bg-transparent text-text-secondary hover:bg-button-hover-surface focus:bg-button-hover-surface'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
updateValue?.(value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
23
src/components/tab/TabList.vue
Normal file
23
src/components/tab/TabList.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="flex items-center gap-2 pb-1">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { provide, toRef } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [value: string]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Provide for child Tab components
|
||||||
|
provide('tabs-value', toRef(props, 'modelValue'))
|
||||||
|
provide('tabs-update', (value: string) => emit('update:modelValue', value))
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user