[READ-ONLY] Mirror of https://github.com/mrgnw/zenfo.co.
0

Configure Feed

Select the types of activity you want to include in your feed.

zenfo.co / src / lib / components / PhotoViewer.svelte
3.9 kB 196 lines
1<script lang="ts"> 2 import type { Photo } from '$lib/types.js'; 3 import DepthPhoto from './DepthPhoto.svelte'; 4 import DepthSlice from './DepthSlice.svelte'; 5 6 let { 7 photos, 8 currentIndex = $bindable(0), 9 onclose, 10 }: { 11 photos: Photo[]; 12 currentIndex: number; 13 onclose?: () => void; 14 } = $props(); 15 16 let mode = $state<'parallax' | 'focus'>('parallax'); 17 let photo = $derived(photos[currentIndex]); 18 19 function prev() { 20 if (currentIndex > 0) currentIndex--; 21 } 22 23 function next() { 24 if (currentIndex < photos.length - 1) currentIndex++; 25 } 26 27 function onkeydown(e: KeyboardEvent) { 28 if (e.key === 'ArrowLeft') prev(); 29 else if (e.key === 'ArrowRight') next(); 30 else if (e.key === 'Escape') onclose?.(); 31 } 32 33 function stop(fn: (e: MouseEvent) => void) { 34 return (e: MouseEvent) => { 35 e.stopPropagation(); 36 fn(e); 37 }; 38 } 39</script> 40 41<svelte:window {onkeydown} /> 42 43<!-- svelte-ignore a11y_no_static_element_interactions --> 44<div class="overlay" onclick={onclose}> 45 <!-- svelte-ignore a11y_no_static_element_interactions --> 46 <div class="viewer" onclick={stop(() => {})}> 47 {#key `${currentIndex}-${mode}`} 48 <div class="photo-wrap"> 49 {#if mode === 'parallax'} 50 <DepthPhoto 51 src={photo.src} 52 depthSrc={photo.depthSrc} 53 alt={photo.title} 54 intensity={0.02} 55 /> 56 {:else} 57 <DepthSlice 58 src={photo.src} 59 depthSrc={photo.depthSrc} 60 alt={photo.title} 61 /> 62 {/if} 63 </div> 64 {/key} 65 <div class="caption"> 66 <h2>{photo.title}</h2> 67 {#if photo.caption} 68 <p>{photo.caption}</p> 69 {/if} 70 <div class="controls"> 71 <span class="counter">{currentIndex + 1} / {photos.length}</span> 72 <div class="mode-toggle"> 73 <button 74 class="mode-btn" 75 class:active={mode === 'parallax'} 76 onclick={stop(() => { mode = 'parallax'; })} 77 > 78 Parallax 79 </button> 80 <button 81 class="mode-btn" 82 class:active={mode === 'focus'} 83 onclick={stop(() => { mode = 'focus'; })} 84 > 85 Focus 86 </button> 87 </div> 88 </div> 89 </div> 90 </div> 91 92 <button class="nav nav-prev" onclick={stop(prev)} disabled={currentIndex === 0}> 93 &#8592; 94 </button> 95 <button class="nav nav-next" onclick={stop(next)} disabled={currentIndex === photos.length - 1}> 96 &#8594; 97 </button> 98</div> 99 100<style> 101 .overlay { 102 position: fixed; 103 inset: 0; 104 z-index: 100; 105 background: rgba(0, 0, 0, 0.92); 106 display: flex; 107 align-items: center; 108 justify-content: center; 109 } 110 .viewer { 111 max-width: 90vw; 112 max-height: 90vh; 113 display: flex; 114 flex-direction: column; 115 gap: 1rem; 116 } 117 .photo-wrap { 118 width: 90vw; 119 height: 80vh; 120 overflow: hidden; 121 } 122 .caption { 123 text-align: center; 124 padding: 0.5rem; 125 } 126 .caption h2 { 127 font-size: 1.1rem; 128 font-weight: 400; 129 margin: 0; 130 color: var(--color-text); 131 } 132 .caption p { 133 font-size: 0.85rem; 134 color: var(--color-text-muted); 135 margin: 0.25rem 0 0; 136 } 137 .controls { 138 display: flex; 139 align-items: center; 140 justify-content: center; 141 gap: 1rem; 142 margin-top: 0.5rem; 143 } 144 .counter { 145 font-size: 0.75rem; 146 color: var(--color-text-muted); 147 } 148 .mode-toggle { 149 display: flex; 150 gap: 0.25rem; 151 background: rgba(255, 255, 255, 0.06); 152 border-radius: 0.375rem; 153 padding: 0.15rem; 154 } 155 .mode-btn { 156 all: unset; 157 font-size: 0.7rem; 158 padding: 0.25rem 0.6rem; 159 border-radius: 0.25rem; 160 color: var(--color-text-muted); 161 cursor: pointer; 162 transition: all 0.15s; 163 } 164 .mode-btn:hover { 165 color: var(--color-text); 166 } 167 .mode-btn.active { 168 background: rgba(255, 255, 255, 0.12); 169 color: var(--color-text); 170 } 171 .nav { 172 all: unset; 173 position: fixed; 174 top: 50%; 175 transform: translateY(-50%); 176 font-size: 2rem; 177 color: var(--color-text-muted); 178 padding: 1rem; 179 cursor: pointer; 180 transition: color 0.15s; 181 user-select: none; 182 } 183 .nav:hover:not(:disabled) { 184 color: var(--color-text); 185 } 186 .nav:disabled { 187 opacity: 0.2; 188 cursor: default; 189 } 190 .nav-prev { 191 left: 1rem; 192 } 193 .nav-next { 194 right: 1rem; 195 } 196</style>