Coves frontend - a photon fork
1<script lang="ts">
2 import type { PostEmbed } from '$lib/api/coves/types'
3 import { settings } from '$lib/app/settings.svelte'
4 import { showImage } from '$lib/ui/generic/ExpandableImage.svelte'
5 import { Button, modal } from 'mono-svelte'
6 import { onMount } from 'svelte'
7 import { bestImageURL, extractEmbedAlt } from '../helpers'
8
9 interface Props {
10 embed: PostEmbed
11 blur?: boolean
12 }
13
14 let { embed, blur = false }: Props = $props()
15
16 let imageLoaded: boolean | null = $state(null)
17 onMount(() => {
18 imageLoaded = false
19 })
20
21 let altText = $derived(extractEmbedAlt(embed))
22 let fullImageUrl = $derived(bestImageURL(embed, false, 'fullsize'))
23</script>
24
25<!--disabled preloads here since most people will hover over every image while scrolling-->
26<svelte:element
27 this={settings.expandImages ? 'button' : 'div'}
28 class={[
29 'container/a z-10 rounded-2xl cursor-pointer relative overflow-hidden',
30 'bg-slate-100 dark:bg-zinc-900 transition-colors',
31 'border border-slate-200 dark:border-zinc-800 group',
32 ]}
33 data-sveltekit-preload-data="off"
34 aria-label={altText ?? 'Image'}
35 onclick={() => showImage(fullImageUrl)}
36 role="button"
37 tabindex="0"
38>
39 <!-- svelte-ignore a11y_missing_attribute -->
40 <div class="inset-0 absolute -z-10 rounded-xl overflow-hidden">
41 <img
42 loading="lazy"
43 fetchpriority="auto"
44 src={bestImageURL(embed, false, 'thumb')}
45 class=" object-cover w-full h-full opacity-50 blur-lg"
46 />
47 </div>
48 <picture class="max-h-[60vh]">
49 <source
50 srcset={bestImageURL(embed, false, 'thumb')}
51 media="(max-width: 800px)"
52 />
53 <source
54 srcset={bestImageURL(embed, false, 'fullsize')}
55 media="(min-width: 801px)"
56 />
57 <img
58 src={blur ? '' : fullImageUrl}
59 loading="lazy"
60 class={[
61 'max-w-full rounded-xl z-30 transition-all max-h-[60vh] duration-500 object-contain mx-auto group-hover:scale-98 group-active:scale-95',
62 'duration-200 ease-cubic',
63 imageLoaded === false ? 'opacity-0' : 'opacity-100',
64 blur && 'blur-3xl',
65 ]}
66 width={512}
67 height={300}
68 alt={altText ?? ''}
69 onload={() => (imageLoaded = true)}
70 onerror={() => (imageLoaded = true)}
71 />
72 </picture>
73 <!-- svelte-ignore a11y_click_events_have_key_events -->
74 <!-- svelte-ignore a11y_no_static_element_interactions -->
75 <div
76 class="absolute bottom-0 left-0 right-0 flex justify-between items-center
77 rounded-full ml-auto w-max m-2 p-0 gap-1
78 *:bg-white *:border *:border-slate-200 dark:*:border-zinc-800 dark:*:bg-zinc-900"
79 onclick={(e) => e.stopPropagation()}
80 >
81 {#if altText}
82 <Button
83 onclick={(e) => {
84 e.stopPropagation()
85 modal({
86 title: 'Alt',
87 body: altText ?? '',
88 })
89 }}
90 color="tertiary"
91 size="md"
92 rounding="pill"
93 >
94 ALT
95 </Button>
96 {/if}
97 </div>
98</svelte:element>