Coves frontend - a photon fork
at main 98 lines 3.0 kB view raw
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>