feat(media): align visual checks and variant UI with merged design

This commit is contained in:
Florian Sylvain committed 2026-10-13 02:35:20 +02:00
1 parent e6746f2be7
commit 160c0a49ce
23 files changed
+606 -99

No files matched your search

@@ -18,6 +18,7 @@ defineProps<{ files: number }>()
root: 'items-center rounded-none px-3.5 py-3',
icon: 'size-4 animate-spin',
title: 'font-semibold',
description: 'mt-2',
}"
>
<template #description>
+10 -1
View File
@@ -9,7 +9,8 @@ const VARIANT_ROWS = 5
const IMAGE_ROWS = 6
const BAR = 'rounded-none bg-accented'
const CARD_UI = {
root: 'min-w-0 rounded-none border border-default bg-default ring-0',
root: 'min-w-0 rounded-none border border-default bg-default ring-0 divide-y-0',
header: 'flex min-h-11 items-center border-b border-default px-4 py-0 sm:px-4 sm:py-0',
body: 'flex flex-col gap-5 p-4 sm:p-4',
}
</script>
@@ -24,12 +25,20 @@ const CARD_UI = {
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-[460px_minmax(0,1fr)]">
<UCard :ui="CARD_UI">
<template #header>
<h2 class="font-semibold text-highlighted">{{ t('By variant') }}</h2>
</template>
<div v-for="row in VARIANT_ROWS" :key="row" class="flex flex-col gap-2">
<USkeleton class="h-3.25 w-[50%]" :class="BAR" />
<USkeleton class="h-2 w-full" :class="BAR" />
</div>
</UCard>
<UCard v-if="desktop" :ui="CARD_UI">
<template #header>
<h2 class="font-semibold text-highlighted">
{{ t('Top 10 images by variant disk usage') }}
</h2>
</template>
<USkeleton
v-for="row in IMAGE_ROWS"
:key="row"
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { SHEET_CONTENT_CLASS, SHEET_FOOTER_CLASS } from '@/components/sheetClasses'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
const props = defineProps<{
@@ -55,13 +56,12 @@ function onConfirm() {
:close="false"
:content="{ role: 'alertdialog' }"
:ui="{
content:
'divide-y-0 shadow-(--shadow-modal) ring-0 sm:top-35 sm:max-w-115 sm:translate-y-0 max-sm:!inset-x-4 max-sm:!top-auto max-sm:!bottom-4 max-sm:!left-4 max-sm:!w-auto max-sm:!max-w-none max-sm:!translate-x-0 max-sm:!translate-y-0',
content: `${SHEET_CONTENT_CLASS} sm:top-35 sm:max-w-115`,
header: 'min-h-0 px-6 pt-5 pb-0 sm:px-6',
title: 'text-lg/6 font-semibold',
description: 'mt-1 text-sm text-muted text-pretty',
body: 'px-6 pt-5 pb-0 sm:px-6 sm:pt-5 sm:pb-0',
footer: 'px-6 py-5 sm:px-6',
footer: SHEET_FOOTER_CLASS,
}"
@update:open="open = $event"
>
@@ -41,6 +41,7 @@ const overlay = useLocalizedOverlay()
const { announceFailure } = useVariantToasts()
const isDesktop = useMediaQuery(DESKTOP_QUERY)
const form = useVariantForm(props.mode, props.initial)
const side = computed(() => (isDesktop.value ? 'right' : 'bottom'))
const saving = ref(false)
const saveFailed = ref(false)
@@ -63,6 +64,29 @@ const discardDescription = computed(() =>
: t("You have unsaved changes. If you close now, they're lost."),
)
const closeButton = computed(() => ({
icon: 'i-lucide-x',
color: 'neutral' as const,
variant: 'ghost' as const,
'aria-label': t('Close'),
class: isDesktop.value
? 'size-7 justify-center text-muted'
: 'top-4.5 end-1.5 size-11 justify-center p-0 text-muted',
ui: { leadingIcon: isDesktop.value ? 'size-4' : 'size-5' },
}))
const overlayUi = computed(() => ({
content: isDesktop.value
? 'w-full max-w-none sm:max-w-240'
: 'h-23/25 max-h-none pt-3 before:absolute before:top-2 before:left-1/2 before:h-1 before:w-9 before:-translate-x-1/2 before:bg-(--ui-border-accented)',
header: 'h-14 min-h-14 gap-3 px-5 py-0 max-sm:gap-1 max-sm:px-1.5',
wrapper: 'min-w-0 flex-1',
title: 'truncate text-lg font-semibold',
description: 'sr-only',
body: 'min-h-0 p-0 sm:overflow-hidden sm:p-0',
footer: 'h-18 gap-2 px-4 py-0 sm:h-16 sm:gap-3 sm:px-5',
}))
function focusField(field: FieldName) {
const target = document.querySelector<HTMLElement>(
`[data-field="${field}"] input:not([type="hidden"])`,
@@ -208,50 +232,19 @@ async function submit() {
<template>
<USlideover
:open="true"
side="right"
:side="side"
:title="title"
:description="t('Define a named size and format for generated images.')"
:close="
isDesktop
? {
icon: 'i-lucide-x',
color: 'neutral',
variant: 'ghost',
'aria-label': t('Close'),
class: 'size-7 justify-center text-muted',
}
: false
"
:close="closeButton"
:content="{ onOpenAutoFocus: focusOnOpen }"
:ui="{
content: 'w-full max-w-none sm:max-w-240',
header: 'h-14 min-h-14 gap-3 px-5 py-0 max-sm:gap-1 max-sm:px-1.5',
wrapper: 'min-w-0 flex-1',
title: 'truncate text-lg font-semibold',
description: 'sr-only',
body: 'min-h-0 p-0 sm:overflow-hidden sm:p-0',
footer: 'h-18 gap-2 px-4 py-0 sm:h-16 sm:gap-3 sm:px-5',
}"
:ui="overlayUi"
@update:open="onOpenChange"
>
<template #actions>
<UButton
v-if="!isDesktop"
icon="i-lucide-arrow-left"
color="neutral"
variant="ghost"
:aria-label="t('Back')"
class="order-first size-11 shrink-0 justify-center"
:ui="{ leadingIcon: 'size-5' }"
@click="requestClose"
/>
</template>
<template #body>
<UForm
:state="form.values"
:validate-on="[]"
class="flex flex-col gap-6 p-4 max-sm:pb-25 sm:grid sm:h-full sm:grid-cols-[440px_minmax(0,1fr)] sm:gap-0 sm:p-0"
class="flex flex-col gap-6 p-4 sm:grid sm:h-full sm:grid-cols-[440px_minmax(0,1fr)] sm:gap-0 sm:p-0"
@submit="submit"
>
<div class="min-w-0 sm:overflow-y-auto sm:p-5">
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { SHEET_CONTENT_CLASS, SHEET_FOOTER_CLASS } from '@/components/sheetClasses'
import { t } from '@/i18n'
import { computed } from 'vue'
@@ -41,14 +42,13 @@ const originalTitle = computed(
:title="t('Preview: {0}', [variant.name])"
:description="t('Both images are shown at actual size. Scroll to pan.')"
:ui="{
content:
'sm:top-20 sm:translate-y-0 sm:max-w-220 max-sm:!inset-x-4 max-sm:!top-auto max-sm:!bottom-4 max-sm:!w-auto max-sm:!max-w-none max-sm:!translate-x-0 max-sm:!translate-y-0 divide-y-0 shadow-(--shadow-modal) ring-0',
content: `${SHEET_CONTENT_CLASS} sm:top-20 sm:max-w-220`,
header: 'min-h-0 px-6 pt-5 pb-0 sm:px-6',
close: 'top-5 end-6',
close: 'top-5 end-6 max-sm:top-8 max-sm:size-11 max-sm:justify-center',
title: 'text-lg/6 font-semibold',
description: 'sr-only',
body: 'px-6 pt-5 pb-0 sm:px-6 sm:pt-5 sm:pb-0',
footer: 'px-6 py-5 sm:px-6',
footer: SHEET_FOOTER_CLASS,
}"
>
<template #body>