feat(SPA): media page T0

This commit is contained in:
fsylvain committed 2026-09-30 19:45:47 +02:00
1 parent 0d99544579
commit 67d456baf6
32 files changed
+4874 -40

No files matched your search

+87
View File
@@ -0,0 +1,87 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import AppLogo from '@/components/AppLogo.vue'
const TONE_COUNT = 4
const HEX_RADIX = 16
const ALPHA_TYPES = ['image/png', 'image/svg+xml']
const SVG_TYPE = 'image/svg+xml'
const props = withDefaults(
defineProps<{
item: { url: string; content_type: string; sha256: string }
size?: 'sm' | 'md' | 'lg' | 'xl'
}>(),
{ size: 'lg' },
)
const loaded = ref(false)
const failed = ref(false)
const hasAlpha = computed(() => ALPHA_TYPES.includes(props.item.content_type))
const isSvg = computed(() => props.item.content_type === SVG_TYPE)
const tone = computed(() => {
const digit = Number.parseInt(props.item.sha256.charAt(0), HEX_RADIX)
return Number.isNaN(digit) ? 0 : digit % TONE_COUNT
})
const iconClass = computed(
() => ({ sm: 'size-4', md: 'size-5', lg: 'size-6', xl: 'size-10' })[props.size],
)
const logoSize = computed(() => ({ sm: 16, md: 20, lg: 28, xl: 40 })[props.size])
</script>
<template>
<div
data-testid="media-thumb"
class="relative flex shrink-0 items-center justify-center overflow-hidden text-muted"
:class="hasAlpha ? 'media-thumb-checker' : ['media-thumb-tone', `media-thumb-tone-${tone}`]"
>
<span v-if="!loaded" aria-hidden="true" class="absolute flex">
<AppLogo v-if="isSvg" :size="logoSize" aria-hidden="true" />
<UIcon v-else name="i-lucide-image" class="opacity-55" :class="iconClass" />
</span>
<img
v-if="!failed"
:src="item.url"
alt=""
loading="lazy"
class="absolute inset-0 size-full object-cover"
@load="loaded = true"
@error="failed = true"
/>
</div>
</template>
<style scoped>
.media-thumb-checker {
background: conic-gradient(
var(--ui-bg-accented) 25%,
var(--ui-bg) 0 50%,
var(--ui-bg-accented) 0 75%,
var(--ui-bg) 0
)
0 0 / 16px 16px;
}
.media-thumb-tone {
background-image: repeating-linear-gradient(
135deg,
color-mix(in srgb, var(--ui-text-muted) 12%, transparent) 0 1px,
transparent 1px 12px
);
}
.media-thumb-tone-0 {
background-color: var(--ui-color-secondary-200);
}
.dark .media-thumb-tone-0 {
background-color: var(--ui-color-secondary-300);
}
.media-thumb-tone-1 {
background-color: var(--ui-bg-accented);
}
.media-thumb-tone-2 {
background-color: color-mix(in srgb, var(--ui-border-accented) 35%, var(--ui-bg));
}
.media-thumb-tone-3 {
background-color: var(--ui-bg-elevated);
}
</style>