mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 11:11:53 +00:00
Remove queue overlay header more menu
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
:header-title="headerTitle"
|
:header-title="headerTitle"
|
||||||
:show-concurrent-indicator="showConcurrentIndicator"
|
:show-concurrent-indicator="showConcurrentIndicator"
|
||||||
:concurrent-workflow-count="concurrentWorkflowCount"
|
:concurrent-workflow-count="concurrentWorkflowCount"
|
||||||
@clear-history="$emit('clearHistory')"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex items-center justify-between px-3">
|
<div class="flex items-center justify-between px-3">
|
||||||
@@ -110,7 +109,6 @@ defineProps<{
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'showAssets'): void
|
(e: 'showAssets'): void
|
||||||
(e: 'clearHistory'): void
|
|
||||||
(e: 'clearQueued'): void
|
(e: 'clearQueued'): void
|
||||||
(e: 'update:selectedJobTab', value: JobTab): void
|
(e: 'update:selectedJobTab', value: JobTab): void
|
||||||
(e: 'update:selectedWorkflowFilter', value: 'all' | 'current'): void
|
(e: 'update:selectedWorkflowFilter', value: 'all' | 'current'): void
|
||||||
|
|||||||
@@ -1,47 +1,17 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
const popoverToggleSpy = vi.fn()
|
|
||||||
const popoverHideSpy = vi.fn()
|
|
||||||
|
|
||||||
vi.mock('primevue/popover', () => {
|
|
||||||
const PopoverStub = defineComponent({
|
|
||||||
name: 'Popover',
|
|
||||||
setup(_, { slots, expose }) {
|
|
||||||
const toggle = (event: Event) => {
|
|
||||||
popoverToggleSpy(event)
|
|
||||||
}
|
|
||||||
const hide = () => {
|
|
||||||
popoverHideSpy()
|
|
||||||
}
|
|
||||||
expose({ toggle, hide })
|
|
||||||
return () => slots.default?.()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return { default: PopoverStub }
|
|
||||||
})
|
|
||||||
|
|
||||||
import QueueOverlayHeader from './QueueOverlayHeader.vue'
|
import QueueOverlayHeader from './QueueOverlayHeader.vue'
|
||||||
import * as tooltipConfig from '@/composables/useTooltipConfig'
|
|
||||||
|
|
||||||
const tooltipDirectiveStub = {
|
|
||||||
mounted: vi.fn(),
|
|
||||||
updated: vi.fn()
|
|
||||||
}
|
|
||||||
|
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
legacy: false,
|
legacy: false,
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
messages: {
|
messages: {
|
||||||
en: {
|
en: {
|
||||||
g: { more: 'More' },
|
|
||||||
sideToolbar: {
|
sideToolbar: {
|
||||||
queueProgressOverlay: {
|
queueProgressOverlay: {
|
||||||
running: 'running',
|
running: 'running'
|
||||||
moreOptions: 'More options',
|
|
||||||
clearHistory: 'Clear history'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,8 +27,7 @@ const mountHeader = (props = {}) =>
|
|||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
global: {
|
global: {
|
||||||
plugins: [i18n],
|
plugins: [i18n]
|
||||||
directives: { tooltip: tooltipDirectiveStub }
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -79,20 +48,4 @@ describe('QueueOverlayHeader', () => {
|
|||||||
expect(wrapper.text()).toContain('Job queue')
|
expect(wrapper.text()).toContain('Job queue')
|
||||||
expect(wrapper.find('.inline-flex.items-center.gap-1').exists()).toBe(false)
|
expect(wrapper.find('.inline-flex.items-center.gap-1').exists()).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('toggles popover and emits clear history', async () => {
|
|
||||||
const spy = vi.spyOn(tooltipConfig, 'buildTooltipConfig')
|
|
||||||
|
|
||||||
const wrapper = mountHeader()
|
|
||||||
|
|
||||||
const moreButton = wrapper.get('button[aria-label="More options"]')
|
|
||||||
await moreButton.trigger('click')
|
|
||||||
expect(popoverToggleSpy).toHaveBeenCalledTimes(1)
|
|
||||||
expect(spy).toHaveBeenCalledWith('More')
|
|
||||||
|
|
||||||
const clearHistoryButton = wrapper.get('button[aria-label="Clear history"]')
|
|
||||||
await clearHistoryButton.trigger('click')
|
|
||||||
expect(popoverHideSpy).toHaveBeenCalledTimes(1)
|
|
||||||
expect(wrapper.emitted('clearHistory')).toHaveLength(1)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -17,85 +17,17 @@
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1">
|
|
||||||
<IconButton
|
|
||||||
v-tooltip.top="moreTooltipConfig"
|
|
||||||
type="transparent"
|
|
||||||
size="sm"
|
|
||||||
class="size-6 bg-transparent hover:bg-secondary-background hover:opacity-100"
|
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.moreOptions')"
|
|
||||||
@click="onMoreClick"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="icon-[lucide--more-horizontal] block size-4 leading-none text-text-secondary"
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
<Popover
|
|
||||||
ref="morePopoverRef"
|
|
||||||
:dismissable="true"
|
|
||||||
:close-on-escape="true"
|
|
||||||
unstyled
|
|
||||||
:pt="{
|
|
||||||
root: { class: 'absolute z-50' },
|
|
||||||
content: {
|
|
||||||
class: [
|
|
||||||
'bg-transparent border-none p-0 pt-2 rounded-lg shadow-lg font-inter'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="flex flex-col items-stretch rounded-lg border border-interface-stroke bg-interface-panel-surface px-2 py-3 font-inter"
|
|
||||||
>
|
|
||||||
<IconTextButton
|
|
||||||
class="w-full justify-start gap-2 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary hover:bg-transparent hover:opacity-90"
|
|
||||||
type="transparent"
|
|
||||||
:label="t('sideToolbar.queueProgressOverlay.clearHistory')"
|
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.clearHistory')"
|
|
||||||
@click="onClearHistoryFromMenu"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<i
|
|
||||||
class="icon-[lucide--file-x-2] block size-4 leading-none text-text-secondary"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</IconTextButton>
|
|
||||||
</div>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Popover from 'primevue/popover'
|
|
||||||
import type { PopoverMethods } from 'primevue/popover'
|
|
||||||
import { computed, ref } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import IconButton from '@/components/button/IconButton.vue'
|
|
||||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
|
||||||
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
headerTitle: string
|
headerTitle: string
|
||||||
showConcurrentIndicator: boolean
|
showConcurrentIndicator: boolean
|
||||||
concurrentWorkflowCount: number
|
concurrentWorkflowCount: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'clearHistory'): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const morePopoverRef = ref<PopoverMethods | null>(null)
|
|
||||||
const moreTooltipConfig = computed(() => buildTooltipConfig(t('g.more')))
|
|
||||||
|
|
||||||
const onMoreClick = (event: MouseEvent) => {
|
|
||||||
morePopoverRef.value?.toggle(event)
|
|
||||||
}
|
|
||||||
const onClearHistoryFromMenu = () => {
|
|
||||||
morePopoverRef.value?.hide()
|
|
||||||
emit('clearHistory')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
:displayed-job-groups="displayedJobGroups"
|
:displayed-job-groups="displayedJobGroups"
|
||||||
:has-failed-jobs="hasFailedJobs"
|
:has-failed-jobs="hasFailedJobs"
|
||||||
@show-assets="openAssetsSidebar"
|
@show-assets="openAssetsSidebar"
|
||||||
@clear-history="onClearHistoryFromMenu"
|
|
||||||
@clear-queued="cancelQueuedWorkflows"
|
@clear-queued="cancelQueuedWorkflows"
|
||||||
@cancel-item="onCancelItem"
|
@cancel-item="onCancelItem"
|
||||||
@delete-item="onDeleteItem"
|
@delete-item="onDeleteItem"
|
||||||
@@ -66,7 +65,6 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import QueueOverlayActive from '@/components/queue/QueueOverlayActive.vue'
|
import QueueOverlayActive from '@/components/queue/QueueOverlayActive.vue'
|
||||||
import QueueOverlayEmpty from '@/components/queue/QueueOverlayEmpty.vue'
|
import QueueOverlayEmpty from '@/components/queue/QueueOverlayEmpty.vue'
|
||||||
import QueueOverlayExpanded from '@/components/queue/QueueOverlayExpanded.vue'
|
import QueueOverlayExpanded from '@/components/queue/QueueOverlayExpanded.vue'
|
||||||
import QueueClearHistoryDialog from '@/components/queue/dialogs/QueueClearHistoryDialog.vue'
|
|
||||||
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
|
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
|
||||||
import { useCompletionSummary } from '@/composables/queue/useCompletionSummary'
|
import { useCompletionSummary } from '@/composables/queue/useCompletionSummary'
|
||||||
import { useJobList } from '@/composables/queue/useJobList'
|
import { useJobList } from '@/composables/queue/useJobList'
|
||||||
@@ -79,7 +77,6 @@ import { isCloud } from '@/platform/distribution/types'
|
|||||||
import { api } from '@/scripts/api'
|
import { api } from '@/scripts/api'
|
||||||
import { useAssetsStore } from '@/stores/assetsStore'
|
import { useAssetsStore } from '@/stores/assetsStore'
|
||||||
import { useCommandStore } from '@/stores/commandStore'
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
import { useDialogStore } from '@/stores/dialogStore'
|
|
||||||
import { useExecutionStore } from '@/stores/executionStore'
|
import { useExecutionStore } from '@/stores/executionStore'
|
||||||
import { useQueueStore } from '@/stores/queueStore'
|
import { useQueueStore } from '@/stores/queueStore'
|
||||||
import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore'
|
import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore'
|
||||||
@@ -105,7 +102,6 @@ const queueStore = useQueueStore()
|
|||||||
const commandStore = useCommandStore()
|
const commandStore = useCommandStore()
|
||||||
const executionStore = useExecutionStore()
|
const executionStore = useExecutionStore()
|
||||||
const sidebarTabStore = useSidebarTabStore()
|
const sidebarTabStore = useSidebarTabStore()
|
||||||
const dialogStore = useDialogStore()
|
|
||||||
const assetsStore = useAssetsStore()
|
const assetsStore = useAssetsStore()
|
||||||
const assetSelectionStore = useAssetSelectionStore()
|
const assetSelectionStore = useAssetSelectionStore()
|
||||||
const { wrapWithErrorHandlingAsync } = useErrorHandling()
|
const { wrapWithErrorHandlingAsync } = useErrorHandling()
|
||||||
@@ -280,29 +276,4 @@ const interruptAll = wrapWithErrorHandlingAsync(async () => {
|
|||||||
|
|
||||||
await Promise.all(promptIds.map((id) => api.interrupt(id)))
|
await Promise.all(promptIds.map((id) => api.interrupt(id)))
|
||||||
})
|
})
|
||||||
|
|
||||||
const showClearHistoryDialog = () => {
|
|
||||||
dialogStore.showDialog({
|
|
||||||
key: 'queue-clear-history',
|
|
||||||
component: QueueClearHistoryDialog,
|
|
||||||
dialogComponentProps: {
|
|
||||||
headless: true,
|
|
||||||
closable: false,
|
|
||||||
closeOnEscape: true,
|
|
||||||
dismissableMask: true,
|
|
||||||
pt: {
|
|
||||||
root: {
|
|
||||||
class: 'max-w-[360px] w-auto bg-transparent border-none shadow-none'
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
class: '!p-0 bg-transparent'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClearHistoryFromMenu = () => {
|
|
||||||
showClearHistoryDialog()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
<template>
|
|
||||||
<section
|
|
||||||
class="w-[360px] rounded-2xl border border-interface-stroke bg-interface-panel-surface text-text-primary shadow-interface font-inter"
|
|
||||||
>
|
|
||||||
<header
|
|
||||||
class="flex items-center justify-between border-b border-interface-stroke px-4 py-4"
|
|
||||||
>
|
|
||||||
<p class="m-0 text-[14px] font-normal leading-none">
|
|
||||||
{{ t('sideToolbar.queueProgressOverlay.clearHistoryDialogTitle') }}
|
|
||||||
</p>
|
|
||||||
<IconButton
|
|
||||||
type="transparent"
|
|
||||||
size="sm"
|
|
||||||
class="size-6 bg-transparent text-text-secondary hover:bg-secondary-background hover:opacity-100"
|
|
||||||
:aria-label="t('g.close')"
|
|
||||||
@click="onCancel"
|
|
||||||
>
|
|
||||||
<i class="icon-[lucide--x] block size-4 leading-none" />
|
|
||||||
</IconButton>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-4 px-4 py-4 text-[14px] text-text-secondary">
|
|
||||||
<p class="m-0">
|
|
||||||
{{
|
|
||||||
t('sideToolbar.queueProgressOverlay.clearHistoryDialogDescription')
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
<p class="m-0">
|
|
||||||
{{ t('sideToolbar.queueProgressOverlay.clearHistoryDialogAssetsNote') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer class="flex items-center justify-end px-4 py-4">
|
|
||||||
<div class="flex items-center gap-4 text-[14px] leading-none">
|
|
||||||
<TextButton
|
|
||||||
class="min-h-[24px] px-1 py-1 text-[14px] leading-[1] text-text-secondary hover:text-text-primary"
|
|
||||||
type="transparent"
|
|
||||||
:label="t('g.cancel')"
|
|
||||||
@click="onCancel"
|
|
||||||
/>
|
|
||||||
<TextButton
|
|
||||||
class="min-h-[32px] px-4 py-2 text-[12px] font-normal leading-[1]"
|
|
||||||
type="secondary"
|
|
||||||
:label="t('g.clear')"
|
|
||||||
:disabled="isClearing"
|
|
||||||
@click="onConfirm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
import IconButton from '@/components/button/IconButton.vue'
|
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
|
||||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
|
||||||
import { useDialogStore } from '@/stores/dialogStore'
|
|
||||||
import { useQueueStore } from '@/stores/queueStore'
|
|
||||||
|
|
||||||
const dialogStore = useDialogStore()
|
|
||||||
const queueStore = useQueueStore()
|
|
||||||
const { t } = useI18n()
|
|
||||||
const { wrapWithErrorHandlingAsync } = useErrorHandling()
|
|
||||||
|
|
||||||
const isClearing = ref(false)
|
|
||||||
|
|
||||||
const clearHistory = wrapWithErrorHandlingAsync(
|
|
||||||
async () => {
|
|
||||||
await queueStore.clear(['history'])
|
|
||||||
dialogStore.closeDialog()
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
() => {
|
|
||||||
isClearing.value = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const onConfirm = async () => {
|
|
||||||
if (isClearing.value) return
|
|
||||||
isClearing.value = true
|
|
||||||
await clearHistory()
|
|
||||||
}
|
|
||||||
|
|
||||||
const onCancel = () => {
|
|
||||||
dialogStore.closeDialog()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
Reference in New Issue
Block a user