feat: Add alt text viewer to media

This commit is contained in:
Jesse Wierzbinski 2024-12-07 18:09:34 +01:00
parent 90c5241de0
commit a207dea817
No known key found for this signature in database
5 changed files with 106 additions and 12 deletions

View file

@ -1,24 +1,40 @@
<template> <template>
<Dialog> <Dialog>
<DialogTrigger :as-child="true"> <Card class="w-full h-full overflow-hidden relative">
<Card class="w-full h-full overflow-hidden"> <DialogTrigger v-if="attachment.type === 'image'" :as-child="true">
<img v-if="attachment.type === 'image'" :src="attachment.url" :alt="attachment.description ?? ''" <img :src="attachment.url" :alt="attachment.description ?? undefined"
class="w-full h-full object-contain bg-muted/20" /> class="w-full h-full object-contain bg-muted/20" />
</DialogTrigger>
<video v-else-if="attachment.type === 'video' || attachment.type === 'gifv'" :src="attachment.url" <video v-else-if="attachment.type === 'video' || attachment.type === 'gifv'" :src="attachment.url"
:alt="attachment.description ?? ''" class="w-full h-full object-cover bg-muted/20" controls /> :alt="attachment.description ?? undefined" class="w-full h-full object-cover bg-muted/20" controls />
<audio v-else-if="attachment.type === 'audio'" :src="attachment.url" :alt="attachment.description ?? ''" <audio v-else-if="attachment.type === 'audio'" :src="attachment.url"
class="w-full h-full object-cover bg-muted/20" controls /> :alt="attachment.description ?? undefined" class="w-full h-full object-cover bg-muted/20" controls />
<div v-else class="w-full h-full flex flex-col items-center justify-center bg-muted/20"> <DialogTrigger v-else :as-child="true">
<div class="w-full h-full flex flex-col items-center justify-center bg-muted/20">
<File class="size-12" /> <File class="size-12" />
<span class="text-sm"></span> <span class="text-sm"></span>
</div> </div>
</Card>
</DialogTrigger> </DialogTrigger>
<!-- Alt text viewer -->
<Popover v-if="attachment.description">
<div class="absolute top-0 right-0 p-2">
<PopoverTrigger :as-child="true">
<Button variant="outline" size="icon" class="[&_svg]:size-6">
<Captions />
</Button>
</PopoverTrigger>
</div>
<PopoverContent>
<p class="text-sm">{{ attachment.description }}</p>
</PopoverContent>
</Popover>
</Card>
<DialogContent :hide-close="true" <DialogContent :hide-close="true"
class="fixed inset-0 z-50 w-screen h-screen p-6 duration-200 bg-transparent border-none grid grid-rows-[auto,1fr,auto] overflow-hidden translate-x-0 translate-y-0 max-w-full !animate-none gap-6"> class="fixed inset-0 z-50 w-screen h-screen p-6 duration-200 bg-transparent border-none grid grid-rows-[auto,1fr,auto] overflow-hidden translate-x-0 translate-y-0 max-w-full !animate-none gap-6">
<div class="flex flex-row gap-2 w-full"> <div class="flex flex-row gap-2 w-full">
<DialogTitle class="sr-only">{{ attachment.type }}</DialogTitle> <DialogTitle class="sr-only">{{ attachment.type }}</DialogTitle>
<Button as="a" :href="attachment?.url" target="_blank" :download="true" variant="ghost" size="icon" class="[&_svg]:size-6 ml-auto"> <Button as="a" :href="attachment?.url" target="_blank" :download="true" variant="ghost" size="icon"
class="[&_svg]:size-6 ml-auto">
<Download /> <Download />
</Button> </Button>
<DialogClose :as-child="true"> <DialogClose :as-child="true">
@ -50,7 +66,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Attachment } from "@versia/client/types"; import type { Attachment } from "@versia/client/types";
import { Download, File, X } from "lucide-vue-next"; import { Captions, Download, File, X } from "lucide-vue-next";
import { Card } from "~/components/ui/card"; import { Card } from "~/components/ui/card";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { import {
@ -61,6 +77,7 @@ import {
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "../ui/dialog"; } from "../ui/dialog";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
defineProps<{ defineProps<{
attachment: Attachment; attachment: Attachment;

View file

@ -0,0 +1,15 @@
<script setup lang="ts">
import type { PopoverRootEmits, PopoverRootProps } from "radix-vue";
import { PopoverRoot, useForwardPropsEmits } from "radix-vue";
const props = defineProps<PopoverRootProps>();
const emits = defineEmits<PopoverRootEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<PopoverRoot v-bind="forwarded">
<slot />
</PopoverRoot>
</template>

View file

@ -0,0 +1,48 @@
<script setup lang="ts">
import { cn } from "@/lib/utils";
import {
PopoverContent,
type PopoverContentEmits,
type PopoverContentProps,
PopoverPortal,
useForwardPropsEmits,
} from "radix-vue";
import { type HTMLAttributes, computed } from "vue";
defineOptions({
inheritAttrs: false,
});
const props = withDefaults(
defineProps<PopoverContentProps & { class?: HTMLAttributes["class"] }>(),
{
align: "center",
sideOffset: 4,
},
);
const emits = defineEmits<PopoverContentEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<PopoverPortal>
<PopoverContent
v-bind="{ ...forwarded, ...$attrs }"
:class="
cn(
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
)
"
>
<slot />
</PopoverContent>
</PopoverPortal>
</template>

View file

@ -0,0 +1,11 @@
<script setup lang="ts">
import { PopoverTrigger, type PopoverTriggerProps } from "radix-vue";
const props = defineProps<PopoverTriggerProps>();
</script>
<template>
<PopoverTrigger v-bind="props">
<slot />
</PopoverTrigger>
</template>

View file

@ -0,0 +1,3 @@
export { default as Popover } from "./Popover.vue";
export { default as PopoverContent } from "./PopoverContent.vue";
export { default as PopoverTrigger } from "./PopoverTrigger.vue";