feat(05-02): add Columns/Details/Video/Badge MDC components + full showcase article

This commit is contained in:
2026-04-21 15:31:00 +02:00
parent 36cd7f11aa
commit 9848338619
5 changed files with 386 additions and 23 deletions
+77
View File
@@ -0,0 +1,77 @@
<script setup lang="ts">
interface Props {
src: string
title?: string
aspect?: '16/9' | '4/3' | '1/1'
autoplay?: boolean
loop?: boolean
muted?: boolean
}
const props = withDefaults(defineProps<Props>(), {
title: '',
aspect: '16/9',
autoplay: false,
loop: false,
muted: false,
})
const isYoutube = computed(() =>
/youtube\.com|youtu\.be/.test(props.src)
)
const youtubeId = computed(() => {
const match = props.src.match(
/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
)
return match?.[1] ?? ''
})
const youtubeUrl = computed(() => {
const params = new URLSearchParams({
...(props.autoplay ? { autoplay: '1' } : {}),
...(props.loop ? { loop: '1', playlist: youtubeId.value } : {}),
modestbranding: '1',
rel: '0',
})
return `https://www.youtube.com/embed/${youtubeId.value}?${params}`
})
const aspectClass = computed(() => ({
'16/9': 'aspect-video',
'4/3': 'aspect-[4/3]',
'1/1': 'aspect-square',
}[props.aspect]))
</script>
<template>
<figure class="not-prose my-6 w-full overflow-hidden rounded-lg bg-black">
<!-- YouTube embed -->
<iframe
v-if="isYoutube"
:src="youtubeUrl"
:title="props.title"
:class="['w-full', aspectClass]"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
loading="lazy"
/>
<!-- Local video -->
<video
v-else
:src="props.src"
:title="props.title"
:class="['w-full', aspectClass]"
:autoplay="props.autoplay"
:loop="props.loop"
:muted="props.muted || props.autoplay"
controls
playsinline
/>
<figcaption
v-if="props.title"
class="bg-neutral-900 px-4 py-2 text-center text-xs text-neutral-400 italic"
>
{{ props.title }}
</figcaption>
</figure>
</template>