mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 19:21:54 +00:00
[backport cloud/1.32] Use shared button components in queue overlay (#6855)
Backport of #6793 to `cloud/1.32` Automatically created by backport workflow. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6855-backport-cloud-1-32-Use-shared-button-components-in-queue-overlay-2b46d73d365081d69a6edd5e08e19edd) by [Unito](https://www.unito.io) Co-authored-by: Benjamin Lu <benceruleanlu@proton.me>
This commit is contained in:
@@ -24,7 +24,7 @@ import {
|
|||||||
import { cn } from '@/utils/tailwindUtil'
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
|
|
||||||
interface IconButtonProps extends BaseButtonProps {
|
interface IconButtonProps extends BaseButtonProps {
|
||||||
onClick: (event: Event) => void
|
onClick?: (event: MouseEvent) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const {
|
|||||||
} = defineProps<IconTextButtonProps>()
|
} = defineProps<IconTextButtonProps>()
|
||||||
|
|
||||||
const buttonStyle = computed(() => {
|
const buttonStyle = computed(() => {
|
||||||
const baseClasses = `${getBaseButtonClasses()} justify-start! gap-2`
|
const baseClasses = `${getBaseButtonClasses()} justify-start gap-2`
|
||||||
const sizeClasses = getButtonSizeClasses(size)
|
const sizeClasses = getButtonSizeClasses(size)
|
||||||
const typeClasses = border
|
const typeClasses = border
|
||||||
? getBorderButtonTypeClasses(type)
|
? getBorderButtonTypeClasses(type)
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<button
|
<IconButton
|
||||||
type="button"
|
type="secondary"
|
||||||
class="group flex w-full items-center justify-between gap-3 rounded-lg border-0 bg-secondary-background p-1 text-left transition-colors duration-200 ease-in-out hover:cursor-pointer hover:bg-secondary-background-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-background"
|
size="fit-content"
|
||||||
|
class="group w-full justify-between gap-3 rounded-lg p-1 text-left font-normal hover:cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-background"
|
||||||
:aria-label="props.ariaLabel"
|
:aria-label="props.ariaLabel"
|
||||||
|
@click="emit('click', $event)"
|
||||||
>
|
>
|
||||||
<span class="inline-flex items-center gap-2">
|
<span class="inline-flex items-center gap-2">
|
||||||
<span v-if="props.mode === 'allFailed'" class="inline-flex items-center">
|
<span v-if="props.mode === 'allFailed'" class="inline-flex items-center">
|
||||||
@@ -76,10 +78,11 @@
|
|||||||
>
|
>
|
||||||
<i class="icon-[lucide--chevron-down] block size-4 leading-none" />
|
<i class="icon-[lucide--chevron-down] block size-4 leading-none" />
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</IconButton>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import IconButton from '@/components/button/IconButton.vue'
|
||||||
import type {
|
import type {
|
||||||
CompletionSummary,
|
CompletionSummary,
|
||||||
CompletionSummaryMode
|
CompletionSummaryMode
|
||||||
@@ -96,4 +99,8 @@ type Props = {
|
|||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
thumbnailUrls: () => []
|
thumbnailUrls: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'click', event: MouseEvent): void
|
||||||
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -42,17 +42,19 @@
|
|||||||
t('sideToolbar.queueProgressOverlay.running')
|
t('sideToolbar.queueProgressOverlay.running')
|
||||||
}}</span>
|
}}</span>
|
||||||
</span>
|
</span>
|
||||||
<button
|
<IconButton
|
||||||
v-if="runningCount > 0"
|
v-if="runningCount > 0"
|
||||||
v-tooltip.top="cancelJobTooltip"
|
v-tooltip.top="cancelJobTooltip"
|
||||||
class="inline-flex size-6 cursor-pointer items-center justify-center rounded border-0 bg-secondary-background p-0 transition-colors hover:bg-destructive-background"
|
type="secondary"
|
||||||
|
size="sm"
|
||||||
|
class="size-6 bg-secondary-background hover:bg-destructive-background"
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.interruptAll')"
|
:aria-label="t('sideToolbar.queueProgressOverlay.interruptAll')"
|
||||||
@click="$emit('interruptAll')"
|
@click="$emit('interruptAll')"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="icon-[lucide--x] block size-4 leading-none text-text-primary"
|
class="icon-[lucide--x] block size-4 leading-none text-text-primary"
|
||||||
/>
|
/>
|
||||||
</button>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@@ -62,26 +64,28 @@
|
|||||||
t('sideToolbar.queueProgressOverlay.queuedSuffix')
|
t('sideToolbar.queueProgressOverlay.queuedSuffix')
|
||||||
}}</span>
|
}}</span>
|
||||||
</span>
|
</span>
|
||||||
<button
|
<IconButton
|
||||||
v-if="queuedCount > 0"
|
v-if="queuedCount > 0"
|
||||||
v-tooltip.top="clearQueueTooltip"
|
v-tooltip.top="clearQueueTooltip"
|
||||||
class="inline-flex size-6 cursor-pointer items-center justify-center rounded border-0 bg-secondary-background p-0 transition-colors hover:bg-destructive-background"
|
type="secondary"
|
||||||
|
size="sm"
|
||||||
|
class="size-6 bg-secondary-background hover:bg-destructive-background"
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.clearQueued')"
|
:aria-label="t('sideToolbar.queueProgressOverlay.clearQueued')"
|
||||||
@click="$emit('clearQueued')"
|
@click="$emit('clearQueued')"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="icon-[lucide--list-x] block size-4 leading-none text-text-primary"
|
class="icon-[lucide--list-x] block size-4 leading-none text-text-primary"
|
||||||
/>
|
/>
|
||||||
</button>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<TextButton
|
||||||
class="inline-flex h-6 min-w-[120px] flex-1 cursor-pointer items-center justify-center rounded border-0 bg-secondary-background px-2 py-0 text-[12px] text-text-primary hover:bg-secondary-background-hover hover:opacity-90"
|
class="h-6 min-w-[120px] flex-1 px-2 py-0 text-[12px]"
|
||||||
|
type="secondary"
|
||||||
|
:label="t('sideToolbar.queueProgressOverlay.viewAllJobs')"
|
||||||
@click="$emit('viewAllJobs')"
|
@click="$emit('viewAllJobs')"
|
||||||
>
|
/>
|
||||||
{{ t('sideToolbar.queueProgressOverlay.viewAllJobs') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -90,6 +94,8 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import IconButton from '@/components/button/IconButton.vue'
|
||||||
|
import TextButton from '@/components/button/TextButton.vue'
|
||||||
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|||||||
@@ -8,17 +8,20 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex items-center justify-between px-3">
|
<div class="flex items-center justify-between px-3">
|
||||||
<button
|
<IconTextButton
|
||||||
class="inline-flex grow cursor-pointer items-center justify-center gap-1 rounded border-0 bg-secondary-background p-2 text-center font-inter text-[12px] leading-none text-text-primary hover:bg-secondary-background-hover hover:opacity-90"
|
class="grow gap-1 p-2 text-center font-inter text-[12px] leading-none hover:opacity-90 justify-center"
|
||||||
|
type="secondary"
|
||||||
|
:label="t('sideToolbar.queueProgressOverlay.showAssets')"
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.showAssets')"
|
:aria-label="t('sideToolbar.queueProgressOverlay.showAssets')"
|
||||||
@click="$emit('showAssets')"
|
@click="$emit('showAssets')"
|
||||||
>
|
>
|
||||||
<div
|
<template #icon>
|
||||||
class="pointer-events-none block size-4 shrink-0 leading-none icon-[comfy--image-ai-edit]"
|
<div
|
||||||
aria-hidden="true"
|
class="pointer-events-none block size-4 shrink-0 leading-none icon-[comfy--image-ai-edit]"
|
||||||
/>
|
aria-hidden="true"
|
||||||
<span>{{ t('sideToolbar.queueProgressOverlay.showAssets') }}</span>
|
/>
|
||||||
</button>
|
</template>
|
||||||
|
</IconTextButton>
|
||||||
<div class="ml-4 inline-flex items-center">
|
<div class="ml-4 inline-flex items-center">
|
||||||
<div
|
<div
|
||||||
class="inline-flex h-6 items-center text-[12px] leading-none text-text-primary opacity-90"
|
class="inline-flex h-6 items-center text-[12px] leading-none text-text-primary opacity-90"
|
||||||
@@ -28,16 +31,18 @@
|
|||||||
t('sideToolbar.queueProgressOverlay.queuedSuffix')
|
t('sideToolbar.queueProgressOverlay.queuedSuffix')
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<IconButton
|
||||||
v-if="queuedCount > 0"
|
v-if="queuedCount > 0"
|
||||||
class="group ml-2 inline-flex size-6 cursor-pointer items-center justify-center rounded border-0 bg-secondary-background p-0 transition-colors hover:bg-destructive-background"
|
class="group ml-2 size-6 bg-secondary-background hover:bg-destructive-background"
|
||||||
|
type="secondary"
|
||||||
|
size="sm"
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.clearQueued')"
|
:aria-label="t('sideToolbar.queueProgressOverlay.clearQueued')"
|
||||||
@click="$emit('clearQueued')"
|
@click="$emit('clearQueued')"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="pointer-events-none icon-[lucide--list-x] block size-4 leading-none text-text-primary transition-colors group-hover:text-base-background"
|
class="pointer-events-none icon-[lucide--list-x] block size-4 leading-none text-text-primary transition-colors group-hover:text-base-background"
|
||||||
/>
|
/>
|
||||||
</button>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -75,6 +80,8 @@
|
|||||||
import { ref } from 'vue'
|
import { 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 type {
|
import type {
|
||||||
JobGroup,
|
JobGroup,
|
||||||
JobListItem,
|
JobListItem,
|
||||||
|
|||||||
@@ -18,16 +18,18 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<button
|
<IconButton
|
||||||
v-tooltip.top="moreTooltipConfig"
|
v-tooltip.top="moreTooltipConfig"
|
||||||
class="inline-flex size-6 cursor-pointer items-center justify-center rounded border-0 bg-transparent p-0 hover:bg-secondary-background hover:opacity-100"
|
type="transparent"
|
||||||
|
size="sm"
|
||||||
|
class="size-6 bg-transparent hover:bg-secondary-background hover:opacity-100"
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.moreOptions')"
|
:aria-label="t('sideToolbar.queueProgressOverlay.moreOptions')"
|
||||||
@click="onMoreClick"
|
@click="onMoreClick"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="icon-[lucide--more-horizontal] block size-4 leading-none text-text-secondary"
|
class="icon-[lucide--more-horizontal] block size-4 leading-none text-text-secondary"
|
||||||
/>
|
/>
|
||||||
</button>
|
</IconButton>
|
||||||
<Popover
|
<Popover
|
||||||
ref="morePopoverRef"
|
ref="morePopoverRef"
|
||||||
:dismissable="true"
|
:dismissable="true"
|
||||||
@@ -45,18 +47,19 @@
|
|||||||
<div
|
<div
|
||||||
class="flex flex-col items-stretch rounded-lg border border-interface-stroke bg-interface-panel-surface px-2 py-3 font-inter"
|
class="flex flex-col items-stretch rounded-lg border border-interface-stroke bg-interface-panel-surface px-2 py-3 font-inter"
|
||||||
>
|
>
|
||||||
<button
|
<IconTextButton
|
||||||
class="inline-flex w-full cursor-pointer items-center justify-start gap-2 rounded-lg border-0 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary hover:bg-transparent hover:opacity-90"
|
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')"
|
:aria-label="t('sideToolbar.queueProgressOverlay.clearHistory')"
|
||||||
@click="onClearHistoryFromMenu"
|
@click="onClearHistoryFromMenu"
|
||||||
>
|
>
|
||||||
<i
|
<template #icon>
|
||||||
class="icon-[lucide--file-x-2] block size-4 leading-none text-text-secondary"
|
<i
|
||||||
/>
|
class="icon-[lucide--file-x-2] block size-4 leading-none text-text-secondary"
|
||||||
<span>{{
|
/>
|
||||||
t('sideToolbar.queueProgressOverlay.clearHistory')
|
</template>
|
||||||
}}</span>
|
</IconTextButton>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,6 +72,8 @@ import type { PopoverMethods } from 'primevue/popover'
|
|||||||
import { computed, ref } from 'vue'
|
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'
|
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|||||||
@@ -8,13 +8,15 @@
|
|||||||
<p class="m-0 text-[14px] font-normal leading-none">
|
<p class="m-0 text-[14px] font-normal leading-none">
|
||||||
{{ t('sideToolbar.queueProgressOverlay.clearHistoryDialogTitle') }}
|
{{ t('sideToolbar.queueProgressOverlay.clearHistoryDialogTitle') }}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<IconButton
|
||||||
class="inline-flex size-6 cursor-pointer items-center justify-center rounded border-0 bg-transparent p-0 text-text-secondary transition hover:bg-secondary-background hover:opacity-100"
|
type="transparent"
|
||||||
|
size="sm"
|
||||||
|
class="size-6 bg-transparent text-text-secondary hover:bg-secondary-background hover:opacity-100"
|
||||||
:aria-label="t('g.close')"
|
:aria-label="t('g.close')"
|
||||||
@click="onCancel"
|
@click="onCancel"
|
||||||
>
|
>
|
||||||
<i class="icon-[lucide--x] block size-4 leading-none" />
|
<i class="icon-[lucide--x] block size-4 leading-none" />
|
||||||
</button>
|
</IconButton>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="flex flex-col gap-4 px-4 py-4 text-[14px] text-text-secondary">
|
<div class="flex flex-col gap-4 px-4 py-4 text-[14px] text-text-secondary">
|
||||||
@@ -30,21 +32,19 @@
|
|||||||
|
|
||||||
<footer class="flex items-center justify-end px-4 py-4">
|
<footer class="flex items-center justify-end px-4 py-4">
|
||||||
<div class="flex items-center gap-4 text-[14px] leading-none">
|
<div class="flex items-center gap-4 text-[14px] leading-none">
|
||||||
<button
|
<TextButton
|
||||||
class="inline-flex min-h-[24px] cursor-pointer items-center rounded-md border-0 bg-transparent px-1 py-1 text-[14px] leading-[1] text-text-secondary transition hover:text-text-primary"
|
class="min-h-[24px] px-1 py-1 text-[14px] leading-[1] text-text-secondary hover:text-text-primary"
|
||||||
:aria-label="t('g.cancel')"
|
type="transparent"
|
||||||
|
:label="t('g.cancel')"
|
||||||
@click="onCancel"
|
@click="onCancel"
|
||||||
>
|
/>
|
||||||
{{ t('g.cancel') }}
|
<TextButton
|
||||||
</button>
|
class="min-h-[32px] px-4 py-2 text-[12px] font-normal leading-[1]"
|
||||||
<button
|
type="secondary"
|
||||||
class="inline-flex min-h-[32px] items-center rounded-lg border-0 bg-secondary-background px-4 py-2 text-[12px] font-normal leading-[1] text-text-primary transition hover:bg-secondary-background-hover hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-60"
|
:label="t('g.clear')"
|
||||||
:aria-label="t('g.clear')"
|
|
||||||
:disabled="isClearing"
|
:disabled="isClearing"
|
||||||
@click="onConfirm"
|
@click="onConfirm"
|
||||||
>
|
/>
|
||||||
{{ t('g.clear') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
@@ -54,6 +54,8 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
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 { useErrorHandling } from '@/composables/useErrorHandling'
|
||||||
import { useDialogStore } from '@/stores/dialogStore'
|
import { useDialogStore } from '@/stores/dialogStore'
|
||||||
import { useQueueStore } from '@/stores/queueStore'
|
import { useQueueStore } from '@/stores/queueStore'
|
||||||
|
|||||||
@@ -20,21 +20,24 @@
|
|||||||
<div v-if="entry.kind === 'divider'" class="px-2 py-1">
|
<div v-if="entry.kind === 'divider'" class="px-2 py-1">
|
||||||
<div class="h-px bg-interface-stroke" />
|
<div class="h-px bg-interface-stroke" />
|
||||||
</div>
|
</div>
|
||||||
<button
|
<IconTextButton
|
||||||
v-else
|
v-else
|
||||||
class="inline-flex w-full cursor-pointer items-center justify-start gap-2 rounded-lg border-0 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary transition-colors duration-150 hover:bg-interface-panel-hover-surface"
|
class="w-full justify-start gap-2 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary hover:bg-interface-panel-hover-surface"
|
||||||
|
type="transparent"
|
||||||
|
:label="entry.label"
|
||||||
:aria-label="entry.label"
|
:aria-label="entry.label"
|
||||||
@click="onEntry(entry)"
|
@click="onEntry(entry)"
|
||||||
>
|
>
|
||||||
<i
|
<template #icon>
|
||||||
v-if="entry.icon"
|
<i
|
||||||
:class="[
|
v-if="entry.icon"
|
||||||
entry.icon,
|
:class="[
|
||||||
'block size-4 shrink-0 leading-none text-text-secondary'
|
entry.icon,
|
||||||
]"
|
'block size-4 shrink-0 leading-none text-text-secondary'
|
||||||
/>
|
]"
|
||||||
<span>{{ entry.label }}</span>
|
/>
|
||||||
</button>
|
</template>
|
||||||
|
</IconTextButton>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
@@ -44,6 +47,7 @@
|
|||||||
import Popover from 'primevue/popover'
|
import Popover from 'primevue/popover'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||||
import type { MenuEntry } from '@/composables/queue/useJobMenu'
|
import type { MenuEntry } from '@/composables/queue/useJobMenu'
|
||||||
|
|
||||||
defineProps<{ entries: MenuEntry[] }>()
|
defineProps<{ entries: MenuEntry[] }>()
|
||||||
|
|||||||
@@ -20,17 +20,18 @@
|
|||||||
class="flex min-w-0 items-center text-[0.75rem] leading-normal font-normal text-text-secondary"
|
class="flex min-w-0 items-center text-[0.75rem] leading-normal font-normal text-text-secondary"
|
||||||
>
|
>
|
||||||
<span class="block min-w-0 truncate">{{ row.value }}</span>
|
<span class="block min-w-0 truncate">{{ row.value }}</span>
|
||||||
<button
|
<IconButton
|
||||||
v-if="row.canCopy"
|
v-if="row.canCopy"
|
||||||
type="button"
|
type="transparent"
|
||||||
class="ml-2 inline-flex size-6 items-center justify-center rounded border-0 bg-transparent p-0 hover:opacity-90"
|
size="sm"
|
||||||
|
class="ml-2 size-6 bg-transparent hover:opacity-90"
|
||||||
:aria-label="copyAriaLabel"
|
:aria-label="copyAriaLabel"
|
||||||
@click.stop="copyJobId"
|
@click.stop="copyJobId"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="icon-[lucide--copy] block size-4 leading-none text-text-secondary"
|
class="icon-[lucide--copy] block size-4 leading-none text-text-secondary"
|
||||||
/>
|
/>
|
||||||
</button>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,25 +61,31 @@
|
|||||||
{{ t('queue.jobDetails.errorMessage') }}
|
{{ t('queue.jobDetails.errorMessage') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between gap-4">
|
<div class="flex items-center justify-between gap-4">
|
||||||
<button
|
<IconTextButton
|
||||||
type="button"
|
class="h-6 justify-start gap-2 bg-transparent px-0 text-[0.75rem] leading-none text-text-secondary hover:opacity-90"
|
||||||
class="inline-flex h-6 items-center justify-center gap-2 rounded border-none bg-transparent px-0 text-[0.75rem] leading-none text-text-secondary hover:opacity-90"
|
type="transparent"
|
||||||
|
:label="copyAriaLabel"
|
||||||
:aria-label="copyAriaLabel"
|
:aria-label="copyAriaLabel"
|
||||||
|
icon-position="right"
|
||||||
@click.stop="copyErrorMessage"
|
@click.stop="copyErrorMessage"
|
||||||
>
|
>
|
||||||
<span>{{ copyAriaLabel }}</span>
|
<template #icon>
|
||||||
<i class="icon-[lucide--copy] block size-3.5 leading-none" />
|
<i class="icon-[lucide--copy] block size-3.5 leading-none" />
|
||||||
</button>
|
</template>
|
||||||
<button
|
</IconTextButton>
|
||||||
type="button"
|
<IconTextButton
|
||||||
class="inline-flex h-6 items-center justify-center gap-2 rounded border-none bg-transparent px-0 text-[0.75rem] leading-none text-text-secondary hover:opacity-90"
|
class="h-6 justify-start gap-2 bg-transparent px-0 text-[0.75rem] leading-none text-text-secondary hover:opacity-90"
|
||||||
|
type="transparent"
|
||||||
|
:label="t('queue.jobDetails.report')"
|
||||||
|
icon-position="right"
|
||||||
@click.stop="reportJobError"
|
@click.stop="reportJobError"
|
||||||
>
|
>
|
||||||
<span>{{ t('queue.jobDetails.report') }}</span>
|
<template #icon>
|
||||||
<i
|
<i
|
||||||
class="icon-[lucide--message-circle-warning] block size-3.5 leading-none"
|
class="icon-[lucide--message-circle-warning] block size-3.5 leading-none"
|
||||||
/>
|
/>
|
||||||
</button>
|
</template>
|
||||||
|
</IconTextButton>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="col-span-2 mt-2 rounded bg-interface-panel-hover-surface px-4 py-2 text-[0.75rem] leading-normal text-text-secondary"
|
class="col-span-2 mt-2 rounded bg-interface-panel-hover-surface px-4 py-2 text-[0.75rem] leading-normal text-text-secondary"
|
||||||
@@ -94,6 +101,8 @@
|
|||||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, 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 { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||||
import { t } from '@/i18n'
|
import { t } from '@/i18n'
|
||||||
import { isCloud } from '@/platform/distribution/types'
|
import { isCloud } from '@/platform/distribution/types'
|
||||||
|
|||||||
@@ -2,26 +2,26 @@
|
|||||||
<div class="flex items-center justify-between gap-2 px-3">
|
<div class="flex items-center justify-between gap-2 px-3">
|
||||||
<div class="min-w-0 flex-1 overflow-x-auto">
|
<div class="min-w-0 flex-1 overflow-x-auto">
|
||||||
<div class="inline-flex items-center gap-1 whitespace-nowrap">
|
<div class="inline-flex items-center gap-1 whitespace-nowrap">
|
||||||
<button
|
<TextButton
|
||||||
v-for="tab in visibleJobTabs"
|
v-for="tab in visibleJobTabs"
|
||||||
:key="tab"
|
:key="tab"
|
||||||
class="h-6 cursor-pointer rounded border-0 px-3 py-1 text-[12px] leading-none hover:opacity-90"
|
class="h-6 px-3 py-1 text-[12px] leading-none hover:opacity-90"
|
||||||
|
:type="selectedJobTab === tab ? 'secondary' : 'transparent'"
|
||||||
:class="[
|
:class="[
|
||||||
selectedJobTab === tab
|
selectedJobTab === tab ? 'text-text-primary' : 'text-text-secondary'
|
||||||
? 'bg-secondary-background text-text-primary'
|
|
||||||
: 'bg-transparent text-text-secondary'
|
|
||||||
]"
|
]"
|
||||||
|
:label="tabLabel(tab)"
|
||||||
@click="$emit('update:selectedJobTab', tab)"
|
@click="$emit('update:selectedJobTab', tab)"
|
||||||
>
|
/>
|
||||||
{{ tabLabel(tab) }}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-2 flex shrink-0 items-center gap-2">
|
<div class="ml-2 flex shrink-0 items-center gap-2">
|
||||||
<button
|
<IconButton
|
||||||
v-if="showWorkflowFilter"
|
v-if="showWorkflowFilter"
|
||||||
v-tooltip.top="filterTooltipConfig"
|
v-tooltip.top="filterTooltipConfig"
|
||||||
class="relative inline-flex size-6 cursor-pointer items-center justify-center rounded border-0 bg-secondary-background p-0 hover:bg-secondary-background-hover hover:opacity-90"
|
type="secondary"
|
||||||
|
size="sm"
|
||||||
|
class="relative size-6 bg-secondary-background hover:bg-secondary-background-hover hover:opacity-90"
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.filterJobs')"
|
:aria-label="t('sideToolbar.queueProgressOverlay.filterJobs')"
|
||||||
@click="onFilterClick"
|
@click="onFilterClick"
|
||||||
>
|
>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
v-if="selectedWorkflowFilter !== 'all'"
|
v-if="selectedWorkflowFilter !== 'all'"
|
||||||
class="pointer-events-none absolute -top-1 -right-1 inline-block size-2 rounded-full bg-base-foreground"
|
class="pointer-events-none absolute -top-1 -right-1 inline-block size-2 rounded-full bg-base-foreground"
|
||||||
/>
|
/>
|
||||||
</button>
|
</IconButton>
|
||||||
<Popover
|
<Popover
|
||||||
v-if="showWorkflowFilter"
|
v-if="showWorkflowFilter"
|
||||||
ref="filterPopoverRef"
|
ref="filterPopoverRef"
|
||||||
@@ -51,46 +51,48 @@
|
|||||||
<div
|
<div
|
||||||
class="flex min-w-[12rem] flex-col items-stretch rounded-lg border border-interface-stroke bg-interface-panel-surface px-2 py-3"
|
class="flex min-w-[12rem] flex-col items-stretch rounded-lg border border-interface-stroke bg-interface-panel-surface px-2 py-3"
|
||||||
>
|
>
|
||||||
<button
|
<IconTextButton
|
||||||
class="inline-flex w-full cursor-pointer items-center justify-start gap-1 rounded-lg border-0 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary hover:bg-transparent hover:opacity-90"
|
class="w-full justify-between gap-1 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary hover:bg-transparent hover:opacity-90"
|
||||||
|
type="transparent"
|
||||||
|
icon-position="right"
|
||||||
|
:label="t('sideToolbar.queueProgressOverlay.filterAllWorkflows')"
|
||||||
:aria-label="
|
:aria-label="
|
||||||
t('sideToolbar.queueProgressOverlay.filterAllWorkflows')
|
t('sideToolbar.queueProgressOverlay.filterAllWorkflows')
|
||||||
"
|
"
|
||||||
@click="selectWorkflowFilter('all')"
|
@click="selectWorkflowFilter('all')"
|
||||||
>
|
>
|
||||||
<span>{{
|
<template #icon>
|
||||||
t('sideToolbar.queueProgressOverlay.filterAllWorkflows')
|
|
||||||
}}</span>
|
|
||||||
<span class="ml-auto inline-flex items-center">
|
|
||||||
<i
|
<i
|
||||||
v-if="selectedWorkflowFilter === 'all'"
|
v-if="selectedWorkflowFilter === 'all'"
|
||||||
class="icon-[lucide--check] block size-4 leading-none text-text-secondary"
|
class="icon-[lucide--check] block size-4 leading-none text-text-secondary"
|
||||||
/>
|
/>
|
||||||
</span>
|
</template>
|
||||||
</button>
|
</IconTextButton>
|
||||||
<div class="mx-2 mt-1 h-px" />
|
<div class="mx-2 mt-1 h-px" />
|
||||||
<button
|
<IconTextButton
|
||||||
class="inline-flex w-full cursor-pointer items-center justify-start gap-1 rounded-lg border-0 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary hover:bg-transparent hover:opacity-90"
|
class="w-full justify-between gap-1 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary hover:bg-transparent hover:opacity-90"
|
||||||
|
type="transparent"
|
||||||
|
icon-position="right"
|
||||||
|
:label="t('sideToolbar.queueProgressOverlay.filterCurrentWorkflow')"
|
||||||
:aria-label="
|
:aria-label="
|
||||||
t('sideToolbar.queueProgressOverlay.filterCurrentWorkflow')
|
t('sideToolbar.queueProgressOverlay.filterCurrentWorkflow')
|
||||||
"
|
"
|
||||||
@click="selectWorkflowFilter('current')"
|
@click="selectWorkflowFilter('current')"
|
||||||
>
|
>
|
||||||
<span>{{
|
<template #icon>
|
||||||
t('sideToolbar.queueProgressOverlay.filterCurrentWorkflow')
|
|
||||||
}}</span>
|
|
||||||
<span class="ml-auto inline-flex items-center">
|
|
||||||
<i
|
<i
|
||||||
v-if="selectedWorkflowFilter === 'current'"
|
v-if="selectedWorkflowFilter === 'current'"
|
||||||
class="icon-[lucide--check] block size-4 leading-none text-text-secondary"
|
class="icon-[lucide--check] block size-4 leading-none text-text-secondary"
|
||||||
/>
|
/>
|
||||||
</span>
|
</template>
|
||||||
</button>
|
</IconTextButton>
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
<button
|
<IconButton
|
||||||
v-tooltip.top="sortTooltipConfig"
|
v-tooltip.top="sortTooltipConfig"
|
||||||
class="relative inline-flex size-6 cursor-pointer items-center justify-center rounded border-0 bg-secondary-background p-0 hover:bg-secondary-background-hover hover:opacity-90"
|
type="secondary"
|
||||||
|
size="sm"
|
||||||
|
class="relative size-6 bg-secondary-background hover:bg-secondary-background-hover hover:opacity-90"
|
||||||
:aria-label="t('sideToolbar.queueProgressOverlay.sortJobs')"
|
:aria-label="t('sideToolbar.queueProgressOverlay.sortJobs')"
|
||||||
@click="onSortClick"
|
@click="onSortClick"
|
||||||
>
|
>
|
||||||
@@ -101,7 +103,7 @@
|
|||||||
v-if="selectedSortMode !== 'mostRecent'"
|
v-if="selectedSortMode !== 'mostRecent'"
|
||||||
class="pointer-events-none absolute -top-1 -right-1 inline-block size-2 rounded-full bg-base-foreground"
|
class="pointer-events-none absolute -top-1 -right-1 inline-block size-2 rounded-full bg-base-foreground"
|
||||||
/>
|
/>
|
||||||
</button>
|
</IconButton>
|
||||||
<Popover
|
<Popover
|
||||||
ref="sortPopoverRef"
|
ref="sortPopoverRef"
|
||||||
:dismissable="true"
|
:dismissable="true"
|
||||||
@@ -120,19 +122,21 @@
|
|||||||
class="flex min-w-[12rem] flex-col items-stretch rounded-lg border border-interface-stroke bg-interface-panel-surface px-2 py-3"
|
class="flex min-w-[12rem] flex-col items-stretch rounded-lg border border-interface-stroke bg-interface-panel-surface px-2 py-3"
|
||||||
>
|
>
|
||||||
<template v-for="(mode, index) in jobSortModes" :key="mode">
|
<template v-for="(mode, index) in jobSortModes" :key="mode">
|
||||||
<button
|
<IconTextButton
|
||||||
class="inline-flex w-full cursor-pointer items-center justify-start gap-1 rounded-lg border-0 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary hover:bg-transparent hover:opacity-90"
|
class="w-full justify-between gap-1 bg-transparent p-2 font-inter text-[12px] leading-none text-text-primary hover:bg-transparent hover:opacity-90"
|
||||||
|
type="transparent"
|
||||||
|
icon-position="right"
|
||||||
|
:label="sortLabel(mode)"
|
||||||
:aria-label="sortLabel(mode)"
|
:aria-label="sortLabel(mode)"
|
||||||
@click="selectSortMode(mode)"
|
@click="selectSortMode(mode)"
|
||||||
>
|
>
|
||||||
<span>{{ sortLabel(mode) }}</span>
|
<template #icon>
|
||||||
<span class="ml-auto inline-flex items-center">
|
|
||||||
<i
|
<i
|
||||||
v-if="selectedSortMode === mode"
|
v-if="selectedSortMode === mode"
|
||||||
class="icon-[lucide--check] block size-4 leading-none text-text-secondary"
|
class="icon-[lucide--check] block size-4 leading-none text-text-secondary"
|
||||||
/>
|
/>
|
||||||
</span>
|
</template>
|
||||||
</button>
|
</IconTextButton>
|
||||||
<div
|
<div
|
||||||
v-if="index < jobSortModes.length - 1"
|
v-if="index < jobSortModes.length - 1"
|
||||||
class="mx-2 mt-1 h-px"
|
class="mx-2 mt-1 h-px"
|
||||||
@@ -149,6 +153,9 @@ import Popover from 'primevue/popover'
|
|||||||
import { computed, ref } from 'vue'
|
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 TextButton from '@/components/button/TextButton.vue'
|
||||||
import { jobSortModes, jobTabs } from '@/composables/queue/useJobList'
|
import { jobSortModes, jobTabs } from '@/composables/queue/useJobList'
|
||||||
import type { JobSortMode, JobTab } from '@/composables/queue/useJobList'
|
import type { JobSortMode, JobTab } from '@/composables/queue/useJobList'
|
||||||
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
||||||
|
|||||||
@@ -108,45 +108,47 @@
|
|||||||
key="actions"
|
key="actions"
|
||||||
class="inline-flex items-center gap-2 pr-1"
|
class="inline-flex items-center gap-2 pr-1"
|
||||||
>
|
>
|
||||||
<button
|
<IconButton
|
||||||
v-if="props.state === 'failed' && computedShowClear"
|
v-if="props.state === 'failed' && computedShowClear"
|
||||||
v-tooltip.top="deleteTooltipConfig"
|
v-tooltip.top="deleteTooltipConfig"
|
||||||
type="button"
|
type="transparent"
|
||||||
class="inline-flex h-6 transform cursor-pointer items-center gap-1 rounded border-0 bg-modal-card-button-surface px-1 py-0 text-text-primary transition duration-150 ease-in-out hover:-translate-y-px hover:bg-destructive-background hover:opacity-95"
|
size="sm"
|
||||||
|
class="h-6 transform gap-1 rounded bg-modal-card-button-surface px-1 py-0 text-text-primary transition duration-150 ease-in-out hover:-translate-y-px hover:bg-destructive-background hover:opacity-95"
|
||||||
:aria-label="t('g.delete')"
|
:aria-label="t('g.delete')"
|
||||||
@click.stop="emit('delete')"
|
@click.stop="emit('delete')"
|
||||||
>
|
>
|
||||||
<i class="icon-[lucide--trash-2] size-4" />
|
<i class="icon-[lucide--trash-2] size-4" />
|
||||||
</button>
|
</IconButton>
|
||||||
<button
|
<IconButton
|
||||||
v-else-if="props.state !== 'completed' && computedShowClear"
|
v-else-if="props.state !== 'completed' && computedShowClear"
|
||||||
v-tooltip.top="cancelTooltipConfig"
|
v-tooltip.top="cancelTooltipConfig"
|
||||||
type="button"
|
type="transparent"
|
||||||
class="inline-flex h-6 transform cursor-pointer items-center gap-1 rounded border-0 bg-modal-card-button-surface px-1 py-0 text-text-primary transition duration-150 ease-in-out hover:-translate-y-px hover:bg-destructive-background hover:opacity-95"
|
size="sm"
|
||||||
|
class="h-6 transform gap-1 rounded bg-modal-card-button-surface px-1 py-0 text-text-primary transition duration-150 ease-in-out hover:-translate-y-px hover:bg-destructive-background hover:opacity-95"
|
||||||
:aria-label="t('g.cancel')"
|
:aria-label="t('g.cancel')"
|
||||||
@click.stop="emit('cancel')"
|
@click.stop="emit('cancel')"
|
||||||
>
|
>
|
||||||
<i class="icon-[lucide--x] size-4" />
|
<i class="icon-[lucide--x] size-4" />
|
||||||
</button>
|
</IconButton>
|
||||||
<button
|
<TextButton
|
||||||
v-else-if="props.state === 'completed'"
|
v-else-if="props.state === 'completed'"
|
||||||
type="button"
|
class="h-6 transform gap-1 rounded bg-modal-card-button-surface px-2 py-0 text-text-primary transition duration-150 ease-in-out hover:-translate-y-px hover:opacity-95"
|
||||||
class="inline-flex h-6 transform cursor-pointer items-center gap-1 rounded border-0 bg-modal-card-button-surface px-2 py-0 text-text-primary transition duration-150 ease-in-out hover:-translate-y-px hover:opacity-95"
|
type="transparent"
|
||||||
|
:label="t('menuLabels.View')"
|
||||||
:aria-label="t('menuLabels.View')"
|
:aria-label="t('menuLabels.View')"
|
||||||
@click.stop="emit('view')"
|
@click.stop="emit('view')"
|
||||||
>
|
/>
|
||||||
<span>{{ t('menuLabels.View') }}</span>
|
<IconButton
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="props.showMenu !== undefined ? props.showMenu : true"
|
v-if="props.showMenu !== undefined ? props.showMenu : true"
|
||||||
v-tooltip.top="moreTooltipConfig"
|
v-tooltip.top="moreTooltipConfig"
|
||||||
type="button"
|
type="transparent"
|
||||||
class="inline-flex h-6 transform cursor-pointer items-center gap-1 rounded border-0 bg-modal-card-button-surface px-1 py-0 text-text-primary transition duration-150 ease-in-out hover:-translate-y-px hover:opacity-95"
|
size="sm"
|
||||||
|
class="h-6 transform gap-1 rounded bg-modal-card-button-surface px-1 py-0 text-text-primary transition duration-150 ease-in-out hover:-translate-y-px hover:opacity-95"
|
||||||
:aria-label="t('g.more')"
|
:aria-label="t('g.more')"
|
||||||
@click.stop="emit('menu', $event)"
|
@click.stop="emit('menu', $event)"
|
||||||
>
|
>
|
||||||
<i class="icon-[lucide--more-horizontal] size-4" />
|
<i class="icon-[lucide--more-horizontal] size-4" />
|
||||||
</button>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
<div v-else key="secondary" class="pr-2">
|
<div v-else key="secondary" class="pr-2">
|
||||||
<slot name="secondary">{{ props.rightText }}</slot>
|
<slot name="secondary">{{ props.rightText }}</slot>
|
||||||
@@ -161,6 +163,8 @@
|
|||||||
import { computed, nextTick, ref, watch } from 'vue'
|
import { computed, nextTick, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import IconButton from '@/components/button/IconButton.vue'
|
||||||
|
import TextButton from '@/components/button/TextButton.vue'
|
||||||
import JobDetailsPopover from '@/components/queue/job/JobDetailsPopover.vue'
|
import JobDetailsPopover from '@/components/queue/job/JobDetailsPopover.vue'
|
||||||
import QueueAssetPreview from '@/components/queue/job/QueueAssetPreview.vue'
|
import QueueAssetPreview from '@/components/queue/job/QueueAssetPreview.vue'
|
||||||
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
||||||
|
|||||||
Reference in New Issue
Block a user