mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-08 06:30:04 +00:00
[feat] Replace view mode toggle with settings dropdown menu (#8950)
This commit is contained in:
@@ -751,7 +751,8 @@
|
|||||||
"filterVideo": "Video",
|
"filterVideo": "Video",
|
||||||
"filterAudio": "Audio",
|
"filterAudio": "Audio",
|
||||||
"filter3D": "3D",
|
"filter3D": "3D",
|
||||||
"filterText": "Text"
|
"filterText": "Text",
|
||||||
|
"viewSettings": "View settings"
|
||||||
},
|
},
|
||||||
"backToAssets": "Back to all assets",
|
"backToAssets": "Back to all assets",
|
||||||
"folderView": {
|
"folderView": {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
<MediaAssetFilterButton
|
<MediaAssetFilterButton
|
||||||
v-if="isCloud"
|
v-if="isCloud"
|
||||||
v-tooltip.top="{ value: $t('assetBrowser.filterBy') }"
|
v-tooltip.top="{ value: $t('assetBrowser.filterBy') }"
|
||||||
size="md"
|
|
||||||
>
|
>
|
||||||
<template #default="{ close }">
|
<template #default="{ close }">
|
||||||
<MediaAssetFilterMenu
|
<MediaAssetFilterMenu
|
||||||
@@ -24,7 +23,6 @@
|
|||||||
<AssetSortButton
|
<AssetSortButton
|
||||||
v-if="isCloud"
|
v-if="isCloud"
|
||||||
v-tooltip.top="{ value: $t('assetBrowser.sortBy') }"
|
v-tooltip.top="{ value: $t('assetBrowser.sortBy') }"
|
||||||
size="md"
|
|
||||||
>
|
>
|
||||||
<template #default="{ close }">
|
<template #default="{ close }">
|
||||||
<MediaAssetSortMenu
|
<MediaAssetSortMenu
|
||||||
@@ -34,7 +32,13 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</AssetSortButton>
|
</AssetSortButton>
|
||||||
<MediaAssetViewModeToggle v-model:view-mode="viewMode" />
|
<MediaAssetSettingsButton
|
||||||
|
v-tooltip.top="{ value: $t('sideToolbar.mediaAssets.viewSettings') }"
|
||||||
|
>
|
||||||
|
<template #default="{ close }">
|
||||||
|
<MediaAssetSettingsMenu v-model:view-mode="viewMode" :close />
|
||||||
|
</template>
|
||||||
|
</MediaAssetSettingsButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -48,7 +52,8 @@ import MediaAssetFilterMenu from './MediaAssetFilterMenu.vue'
|
|||||||
import AssetSortButton from './MediaAssetSortButton.vue'
|
import AssetSortButton from './MediaAssetSortButton.vue'
|
||||||
import MediaAssetSortMenu from './MediaAssetSortMenu.vue'
|
import MediaAssetSortMenu from './MediaAssetSortMenu.vue'
|
||||||
import type { SortBy } from './MediaAssetSortMenu.vue'
|
import type { SortBy } from './MediaAssetSortMenu.vue'
|
||||||
import MediaAssetViewModeToggle from './MediaAssetViewModeToggle.vue'
|
import MediaAssetSettingsButton from './MediaAssetSettingsButton.vue'
|
||||||
|
import MediaAssetSettingsMenu from './MediaAssetSettingsMenu.vue'
|
||||||
|
|
||||||
const { showGenerationTimeSort = false } = defineProps<{
|
const { showGenerationTimeSort = false } = defineProps<{
|
||||||
searchQuery: string
|
searchQuery: string
|
||||||
|
|||||||
@@ -1,58 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative inline-flex items-center">
|
<div class="inline-flex items-center">
|
||||||
<Button variant="secondary" size="icon" @click="toggle">
|
<Popover>
|
||||||
<i class="icon-[lucide--list-filter]" />
|
<template #button>
|
||||||
</Button>
|
<Button variant="secondary" size="icon">
|
||||||
|
<i class="icon-[lucide--list-filter]" />
|
||||||
<Popover
|
</Button>
|
||||||
ref="popover"
|
</template>
|
||||||
append-to="#vue-app"
|
<template #default="{ close }">
|
||||||
auto-z-index
|
<slot :close />
|
||||||
:base-z-index="1000"
|
</template>
|
||||||
dismissable
|
|
||||||
close-on-escape
|
|
||||||
unstyled
|
|
||||||
:pt="pt"
|
|
||||||
@show="$emit('menuOpened')"
|
|
||||||
@hide="$emit('menuClosed')"
|
|
||||||
>
|
|
||||||
<div class="flex min-w-40 flex-col gap-2 p-2">
|
|
||||||
<slot :close="hide" />
|
|
||||||
</div>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Popover from 'primevue/popover'
|
|
||||||
import { computed, ref } from 'vue'
|
|
||||||
|
|
||||||
import Button from '@/components/ui/button/Button.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
import Popover from '@/components/ui/Popover.vue'
|
||||||
|
|
||||||
const popover = ref<InstanceType<typeof Popover>>()
|
|
||||||
|
|
||||||
defineEmits<{
|
|
||||||
menuOpened: []
|
|
||||||
menuClosed: []
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const toggle = (event: Event) => {
|
|
||||||
popover.value?.toggle(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
const hide = () => {
|
|
||||||
popover.value?.hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
const pt = computed(() => ({
|
|
||||||
root: {
|
|
||||||
class: cn('absolute z-50')
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
class: cn(
|
|
||||||
'mt-1 rounded-lg bg-base-background text-base-foreground border border-border-default shadow-lg'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ TODO: Extract checkbox pattern into reusable Checkbox component
|
|||||||
<div
|
<div
|
||||||
v-for="filter in filters"
|
v-for="filter in filters"
|
||||||
:key="filter.type"
|
:key="filter.type"
|
||||||
class="flex h-10 cursor-pointer items-center gap-2 rounded-lg px-2 hover:bg-secondary-background-hover"
|
class="flex h-10 min-w-32 cursor-pointer items-center gap-2 rounded-lg px-2 hover:bg-secondary-background-hover"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="checkbox"
|
role="checkbox"
|
||||||
:aria-checked="mediaTypeFilters.includes(filter.type)"
|
:aria-checked="mediaTypeFilters.includes(filter.type)"
|
||||||
|
|||||||
23
src/platform/assets/components/MediaAssetSettingsButton.vue
Normal file
23
src/platform/assets/components/MediaAssetSettingsButton.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
<Popover>
|
||||||
|
<template #button>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="icon"
|
||||||
|
:aria-label="$t('sideToolbar.mediaAssets.viewSettings')"
|
||||||
|
>
|
||||||
|
<i class="icon-[lucide--settings-2]" />
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #default="{ close }">
|
||||||
|
<slot :close />
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
|
import Popover from '@/components/ui/Popover.vue'
|
||||||
|
</script>
|
||||||
48
src/platform/assets/components/MediaAssetSettingsMenu.vue
Normal file
48
src/platform/assets/components/MediaAssetSettingsMenu.vue
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<Button
|
||||||
|
variant="textonly"
|
||||||
|
class="w-full"
|
||||||
|
@click="handleViewModeChange('list')"
|
||||||
|
>
|
||||||
|
<span class="flex items-center gap-2">
|
||||||
|
<i class="icon-[lucide--table-of-contents] size-4" />
|
||||||
|
<span>{{ $t('sideToolbar.queueProgressOverlay.viewList') }}</span>
|
||||||
|
</span>
|
||||||
|
<i
|
||||||
|
class="icon-[lucide--check] ml-auto size-4"
|
||||||
|
:class="viewMode !== 'list' && 'opacity-0'"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="textonly"
|
||||||
|
class="w-full"
|
||||||
|
@click="handleViewModeChange('grid')"
|
||||||
|
>
|
||||||
|
<span class="flex items-center gap-2">
|
||||||
|
<i class="icon-[lucide--layout-grid] size-4" />
|
||||||
|
<span>{{ $t('sideToolbar.queueProgressOverlay.viewGrid') }}</span>
|
||||||
|
</span>
|
||||||
|
<i
|
||||||
|
class="icon-[lucide--check] ml-auto size-4"
|
||||||
|
:class="viewMode !== 'grid' && 'opacity-0'"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
|
|
||||||
|
const { close } = defineProps<{
|
||||||
|
close: () => void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const viewMode = defineModel<'list' | 'grid'>('viewMode', { required: true })
|
||||||
|
|
||||||
|
function handleViewModeChange(value: 'list' | 'grid') {
|
||||||
|
viewMode.value = value
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,60 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative inline-flex items-center">
|
<div class="inline-flex items-center">
|
||||||
<Button variant="secondary" size="icon" @click="toggle">
|
<Popover>
|
||||||
<i class="icon-[lucide--arrow-up-down]" />
|
<template #button>
|
||||||
</Button>
|
<Button variant="secondary" size="icon">
|
||||||
|
<i class="icon-[lucide--arrow-up-down]" />
|
||||||
<Popover
|
</Button>
|
||||||
ref="popover"
|
</template>
|
||||||
append-to="body"
|
<template #default="{ close }">
|
||||||
:auto-z-index="true"
|
<slot :close />
|
||||||
:base-z-index="1000"
|
</template>
|
||||||
:dismissable="true"
|
|
||||||
:close-on-escape="true"
|
|
||||||
unstyled
|
|
||||||
:pt="pt"
|
|
||||||
@show="$emit('menuOpened')"
|
|
||||||
@hide="$emit('menuClosed')"
|
|
||||||
>
|
|
||||||
<div class="flex min-w-40 flex-col gap-2 p-2">
|
|
||||||
<slot :close="hide" />
|
|
||||||
</div>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Popover from 'primevue/popover'
|
|
||||||
import { computed, ref } from 'vue'
|
|
||||||
|
|
||||||
import Button from '@/components/ui/button/Button.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
import Popover from '@/components/ui/Popover.vue'
|
||||||
|
|
||||||
const popover = ref<InstanceType<typeof Popover>>()
|
|
||||||
|
|
||||||
defineEmits<{
|
|
||||||
menuOpened: []
|
|
||||||
menuClosed: []
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const toggle = (event: Event) => {
|
|
||||||
popover.value?.toggle(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
const hide = () => {
|
|
||||||
popover.value?.hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
const pt = computed(() => ({
|
|
||||||
root: {
|
|
||||||
class: cn('absolute z-50')
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
class: cn(
|
|
||||||
'mt-1 rounded-lg',
|
|
||||||
'bg-base-background text-base-foreground border border-border-default',
|
|
||||||
'shadow-lg'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,39 +2,51 @@
|
|||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<Button
|
<Button
|
||||||
variant="textonly"
|
variant="textonly"
|
||||||
class="justify-start"
|
class="w-full"
|
||||||
@click="handleSortChange('newest')"
|
@click="handleSortChange('newest')"
|
||||||
>
|
>
|
||||||
<span>{{ $t('sideToolbar.mediaAssets.sortNewestFirst') }}</span>
|
<span>{{ $t('sideToolbar.mediaAssets.sortNewestFirst') }}</span>
|
||||||
<i v-if="sortBy === 'newest'" class="icon-[lucide--check] size-4" />
|
<i
|
||||||
|
class="icon-[lucide--check] ml-auto size-4"
|
||||||
|
:class="sortBy !== 'newest' && 'opacity-0'"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="textonly"
|
variant="textonly"
|
||||||
class="justify-start"
|
class="w-full"
|
||||||
@click="handleSortChange('oldest')"
|
@click="handleSortChange('oldest')"
|
||||||
>
|
>
|
||||||
<span>{{ $t('sideToolbar.mediaAssets.sortOldestFirst') }}</span>
|
<span>{{ $t('sideToolbar.mediaAssets.sortOldestFirst') }}</span>
|
||||||
<i v-if="sortBy === 'oldest'" class="icon-[lucide--check] size-4" />
|
<i
|
||||||
|
class="icon-[lucide--check] ml-auto size-4"
|
||||||
|
:class="sortBy !== 'oldest' && 'opacity-0'"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<template v-if="showGenerationTimeSort">
|
<template v-if="showGenerationTimeSort">
|
||||||
<Button
|
<Button
|
||||||
variant="textonly"
|
variant="textonly"
|
||||||
class="justify-start"
|
class="w-full"
|
||||||
@click="handleSortChange('longest')"
|
@click="handleSortChange('longest')"
|
||||||
>
|
>
|
||||||
<span>{{ $t('sideToolbar.mediaAssets.sortLongestFirst') }}</span>
|
<span>{{ $t('sideToolbar.mediaAssets.sortLongestFirst') }}</span>
|
||||||
<i v-if="sortBy === 'longest'" class="icon-[lucide--check] size-4" />
|
<i
|
||||||
|
class="icon-[lucide--check] ml-auto size-4"
|
||||||
|
:class="sortBy !== 'longest' && 'opacity-0'"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="textonly"
|
variant="textonly"
|
||||||
class="justify-start"
|
class="w-full"
|
||||||
@click="handleSortChange('fastest')"
|
@click="handleSortChange('fastest')"
|
||||||
>
|
>
|
||||||
<span>{{ $t('sideToolbar.mediaAssets.sortFastestFirst') }}</span>
|
<span>{{ $t('sideToolbar.mediaAssets.sortFastestFirst') }}</span>
|
||||||
<i v-if="sortBy === 'fastest'" class="icon-[lucide--check] size-4" />
|
<i
|
||||||
|
class="icon-[lucide--check] ml-auto size-4"
|
||||||
|
:class="sortBy !== 'fastest' && 'opacity-0'"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
class="inline-flex items-center gap-1 rounded-lg bg-secondary-background p-1"
|
|
||||||
role="group"
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="muted-textonly"
|
|
||||||
size="icon"
|
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.viewList')"
|
|
||||||
:aria-pressed="viewMode === 'list'"
|
|
||||||
:class="
|
|
||||||
cn(
|
|
||||||
'rounded-lg',
|
|
||||||
viewMode === 'list'
|
|
||||||
? 'bg-secondary-background-selected text-text-primary hover:bg-secondary-background-selected'
|
|
||||||
: 'text-text-secondary hover:bg-secondary-background-hover'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@click="viewMode = 'list'"
|
|
||||||
>
|
|
||||||
<i class="icon-[lucide--table-of-contents] size-4" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="muted-textonly"
|
|
||||||
size="icon"
|
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.viewGrid')"
|
|
||||||
:aria-pressed="viewMode === 'grid'"
|
|
||||||
:class="
|
|
||||||
cn(
|
|
||||||
'rounded-lg',
|
|
||||||
viewMode === 'grid'
|
|
||||||
? 'bg-secondary-background-selected text-text-primary hover:bg-secondary-background-selected'
|
|
||||||
: 'text-text-secondary hover:bg-secondary-background-hover'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@click="viewMode = 'grid'"
|
|
||||||
>
|
|
||||||
<i class="icon-[lucide--layout-grid] size-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
import Button from '@/components/ui/button/Button.vue'
|
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
|
||||||
|
|
||||||
const viewMode = defineModel<'list' | 'grid'>('viewMode', { required: true })
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
</script>
|
|
||||||
Reference in New Issue
Block a user