Fine-tuning slideshow
This commit is contained in:
parent
7894750d2b
commit
0354b798d3
6 changed files with 419 additions and 252 deletions
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import LinkCard from './LinkCard.svelte'
|
||||
import Slideshow from './Slideshow.svelte'
|
||||
|
||||
let { post }: { post: any } = $props()
|
||||
|
||||
|
|
@ -127,19 +128,40 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
{#if post.attachments && Array.isArray(post.attachments) && post.attachments.length > 0}
|
||||
<div class="post-attachments">
|
||||
<h3>Attachments</h3>
|
||||
<div class="attachments-grid">
|
||||
{#each post.attachments as attachment}
|
||||
<div class="attachment-item">
|
||||
<img src={attachment.url} alt={attachment.caption || 'Attachment'} loading="lazy" />
|
||||
{#if attachment.caption}
|
||||
<p class="attachment-caption">{attachment.caption}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{#if post.album && post.album.photos && post.album.photos.length > 0}
|
||||
<!-- Album slideshow -->
|
||||
<div class="post-album">
|
||||
<div class="album-header">
|
||||
<h3>{post.album.title}</h3>
|
||||
{#if post.album.description}
|
||||
<p class="album-description">{post.album.description}</p>
|
||||
{/if}
|
||||
</div>
|
||||
<Slideshow
|
||||
items={post.album.photos.map(photo => ({
|
||||
url: photo.url,
|
||||
thumbnailUrl: photo.thumbnailUrl,
|
||||
caption: photo.caption,
|
||||
alt: photo.caption || post.album.title
|
||||
}))}
|
||||
alt={post.album.title}
|
||||
aspectRatio="4/3"
|
||||
/>
|
||||
</div>
|
||||
{:else if post.attachments && Array.isArray(post.attachments) && post.attachments.length > 0}
|
||||
<!-- Regular attachments -->
|
||||
<div class="post-attachments">
|
||||
<h3>Photos</h3>
|
||||
<Slideshow
|
||||
items={post.attachments.map(attachment => ({
|
||||
url: attachment.url,
|
||||
thumbnailUrl: attachment.thumbnailUrl,
|
||||
caption: attachment.caption,
|
||||
alt: attachment.caption || 'Photo'
|
||||
}))}
|
||||
alt="Post photos"
|
||||
aspectRatio="4/3"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
|
@ -228,6 +250,7 @@
|
|||
max-width: 600px;
|
||||
}
|
||||
|
||||
.post-album,
|
||||
.post-attachments {
|
||||
margin-bottom: $unit-4x;
|
||||
|
||||
|
|
@ -237,26 +260,20 @@
|
|||
margin: 0 0 $unit-2x;
|
||||
color: $grey-20;
|
||||
}
|
||||
}
|
||||
|
||||
.attachments-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: $unit-2x;
|
||||
.album-header {
|
||||
margin-bottom: $unit-3x;
|
||||
|
||||
h3 {
|
||||
margin-bottom: $unit;
|
||||
}
|
||||
|
||||
.attachment-item {
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: $unit;
|
||||
}
|
||||
|
||||
.attachment-caption {
|
||||
margin: $unit 0 0;
|
||||
font-size: 0.875rem;
|
||||
color: $grey-40;
|
||||
font-style: italic;
|
||||
}
|
||||
.album-description {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
color: $grey-40;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import Lightbox from './Lightbox.svelte'
|
||||
import TiltCard from './TiltCard.svelte'
|
||||
import Slideshow from './Slideshow.svelte'
|
||||
|
||||
let {
|
||||
images = [],
|
||||
|
|
@ -10,191 +9,9 @@
|
|||
alt?: string
|
||||
} = $props()
|
||||
|
||||
let selectedIndex = $state(0)
|
||||
let lightboxOpen = $state(false)
|
||||
let windowWidth = $state(0)
|
||||
|
||||
// Calculate columns based on breakpoints
|
||||
const columnsPerRow = $derived(windowWidth <= 400 ? 3 : windowWidth <= 600 ? 4 : 6)
|
||||
const totalSlots = $derived(Math.ceil(images.length / columnsPerRow) * columnsPerRow)
|
||||
|
||||
const selectImage = (index: number) => {
|
||||
selectedIndex = index
|
||||
}
|
||||
|
||||
const openLightbox = (index?: number) => {
|
||||
if (index !== undefined) {
|
||||
selectedIndex = index
|
||||
}
|
||||
lightboxOpen = true
|
||||
}
|
||||
|
||||
// Track window width for responsive columns
|
||||
$effect(() => {
|
||||
windowWidth = window.innerWidth
|
||||
|
||||
const handleResize = () => {
|
||||
windowWidth = window.innerWidth
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
}
|
||||
})
|
||||
// Convert string array to slideshow items
|
||||
const slideshowItems = $derived(images.map(url => ({ url, alt })))
|
||||
</script>
|
||||
|
||||
{#if images.length === 1}
|
||||
<!-- Single image -->
|
||||
<TiltCard>
|
||||
<button class="single-image image-button" onclick={() => openLightbox()}>
|
||||
<img src={images[0]} {alt} />
|
||||
</button>
|
||||
</TiltCard>
|
||||
{:else if images.length > 1}
|
||||
<!-- Slideshow -->
|
||||
<div class="slideshow">
|
||||
<TiltCard>
|
||||
<button class="main-image image-button" onclick={() => openLightbox()}>
|
||||
<img src={images[selectedIndex]} alt="{alt} {selectedIndex + 1}" />
|
||||
</button>
|
||||
</TiltCard>
|
||||
<div class="thumbnails">
|
||||
{#each Array(totalSlots) as _, index}
|
||||
{#if index < images.length}
|
||||
<button
|
||||
class="thumbnail"
|
||||
class:active={index === selectedIndex}
|
||||
onclick={() => selectImage(index)}
|
||||
aria-label="View image {index + 1}"
|
||||
>
|
||||
<img src={images[index]} alt="{alt} thumbnail {index + 1}" />
|
||||
</button>
|
||||
{:else}
|
||||
<div class="thumbnail placeholder" aria-hidden="true"></div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<Slideshow items={slideshowItems} {alt} aspectRatio="4/3" />
|
||||
|
||||
<Lightbox {images} bind:selectedIndex bind:isOpen={lightboxOpen} {alt} />
|
||||
|
||||
<style lang="scss">
|
||||
.image-button {
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid $red-60;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.single-image,
|
||||
.main-image {
|
||||
aspect-ratio: 4 / 3;
|
||||
border-radius: $image-corner-radius;
|
||||
overflow: hidden;
|
||||
// Force GPU acceleration and proper clipping
|
||||
transform: translateZ(0);
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.slideshow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-2x;
|
||||
}
|
||||
|
||||
.thumbnails {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: $unit-2x;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
border-radius: $image-corner-radius;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: $image-corner-radius;
|
||||
border: 4px solid transparent;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 4px;
|
||||
border-radius: calc($image-corner-radius - 4px);
|
||||
border: 4px solid transparent;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&.active {
|
||||
&::before {
|
||||
border-color: $red-60;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border-color: $grey-100;
|
||||
}
|
||||
}
|
||||
|
||||
&.placeholder {
|
||||
background: $grey-90;
|
||||
cursor: default;
|
||||
|
||||
&:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
319
src/lib/components/Slideshow.svelte
Normal file
319
src/lib/components/Slideshow.svelte
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
<script lang="ts">
|
||||
import Lightbox from './Lightbox.svelte'
|
||||
import TiltCard from './TiltCard.svelte'
|
||||
|
||||
interface SlideItem {
|
||||
url: string
|
||||
thumbnailUrl?: string
|
||||
caption?: string
|
||||
alt?: string
|
||||
}
|
||||
|
||||
let {
|
||||
items = [],
|
||||
alt = 'Image',
|
||||
showThumbnails = true,
|
||||
aspectRatio = '4/3',
|
||||
maxThumbnails,
|
||||
totalCount,
|
||||
showMoreLink
|
||||
}: {
|
||||
items: SlideItem[]
|
||||
alt?: string
|
||||
showThumbnails?: boolean
|
||||
aspectRatio?: string
|
||||
maxThumbnails?: number
|
||||
totalCount?: number
|
||||
showMoreLink?: string
|
||||
} = $props()
|
||||
|
||||
let selectedIndex = $state(0)
|
||||
let lightboxOpen = $state(false)
|
||||
let windowWidth = $state(0)
|
||||
|
||||
// Calculate columns based on breakpoints
|
||||
const columnsPerRow = $derived(windowWidth <= 400 ? 3 : windowWidth <= 600 ? 4 : 6)
|
||||
|
||||
const showMoreThumbnail = $derived(maxThumbnails && totalCount && totalCount > maxThumbnails - 1)
|
||||
|
||||
// Determine how many thumbnails to show
|
||||
const displayItems = $derived(
|
||||
!maxThumbnails || !showMoreThumbnail
|
||||
? items
|
||||
: items.slice(0, maxThumbnails - 1) // Show actual thumbnails, leave last slot for "+N"
|
||||
)
|
||||
const remainingCount = $derived(
|
||||
showMoreThumbnail ? (totalCount || items.length) - (maxThumbnails - 1) : 0
|
||||
)
|
||||
const totalSlots = $derived(
|
||||
maxThumbnails
|
||||
? maxThumbnails
|
||||
: Math.ceil((displayItems.length + (showMoreThumbnail ? 1 : 0)) / columnsPerRow) * columnsPerRow
|
||||
)
|
||||
|
||||
// Convert items to image URLs for lightbox
|
||||
const lightboxImages = $derived(items.map(item => item.url))
|
||||
|
||||
const selectImage = (index: number) => {
|
||||
selectedIndex = index
|
||||
}
|
||||
|
||||
const openLightbox = (index?: number) => {
|
||||
if (index !== undefined) {
|
||||
selectedIndex = index
|
||||
}
|
||||
lightboxOpen = true
|
||||
}
|
||||
|
||||
// Track window width for responsive columns
|
||||
$effect(() => {
|
||||
windowWidth = window.innerWidth
|
||||
|
||||
const handleResize = () => {
|
||||
windowWidth = window.innerWidth
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if items.length === 1}
|
||||
<!-- Single image -->
|
||||
<TiltCard>
|
||||
<div class="single-image image-container" onclick={() => openLightbox()}>
|
||||
<img src={items[0].url} alt={items[0].alt || alt} />
|
||||
{#if items[0].caption}
|
||||
<div class="image-caption">{items[0].caption}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</TiltCard>
|
||||
{:else if items.length > 1}
|
||||
<!-- Slideshow -->
|
||||
<div class="slideshow">
|
||||
<TiltCard>
|
||||
<div class="main-image image-container" onclick={() => openLightbox()}>
|
||||
<img
|
||||
src={items[selectedIndex].url}
|
||||
alt={items[selectedIndex].alt || `${alt} ${selectedIndex + 1}`}
|
||||
/>
|
||||
{#if items[selectedIndex].caption}
|
||||
<div class="image-caption">{items[selectedIndex].caption}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</TiltCard>
|
||||
|
||||
{#if showThumbnails}
|
||||
<div class="thumbnails">
|
||||
{#each Array(totalSlots) as _, index}
|
||||
{#if index < displayItems.length}
|
||||
<button
|
||||
class="thumbnail"
|
||||
class:active={index === selectedIndex}
|
||||
onclick={() => selectImage(index)}
|
||||
aria-label="View image {index + 1}"
|
||||
>
|
||||
<img
|
||||
src={displayItems[index].thumbnailUrl || displayItems[index].url}
|
||||
alt="{displayItems[index].alt || alt} thumbnail {index + 1}"
|
||||
/>
|
||||
</button>
|
||||
{:else if index === displayItems.length && showMoreThumbnail}
|
||||
<a
|
||||
href={showMoreLink}
|
||||
class="thumbnail show-more"
|
||||
aria-label="View all {totalCount || items.length} photos"
|
||||
>
|
||||
{#if items[displayItems.length]}
|
||||
<img
|
||||
src={items[displayItems.length].thumbnailUrl || items[displayItems.length].url}
|
||||
alt="View all photos"
|
||||
class="blurred-bg"
|
||||
/>
|
||||
{:else if items[items.length - 1]}
|
||||
<img
|
||||
src={items[items.length - 1].thumbnailUrl || items[items.length - 1].url}
|
||||
alt="View all photos"
|
||||
class="blurred-bg"
|
||||
/>
|
||||
{/if}
|
||||
<div class="show-more-overlay">
|
||||
+{remainingCount}
|
||||
</div>
|
||||
</a>
|
||||
{:else}
|
||||
<div class="thumbnail placeholder" aria-hidden="true"></div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Lightbox images={lightboxImages} bind:selectedIndex bind:isOpen={lightboxOpen} alt={alt} />
|
||||
|
||||
<style lang="scss">
|
||||
.image-container {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid $red-60;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.single-image,
|
||||
.main-image {
|
||||
width: 100%;
|
||||
aspect-ratio: v-bind(aspectRatio);
|
||||
border-radius: $image-corner-radius;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
// Force GPU acceleration and proper clipping
|
||||
transform: translateZ(0);
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.image-caption {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
|
||||
color: white;
|
||||
padding: $unit-3x $unit-2x $unit-2x;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.slideshow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-2x;
|
||||
}
|
||||
|
||||
.thumbnails {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: $unit-2x;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
border-radius: $image-corner-radius;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: $image-corner-radius;
|
||||
border: 4px solid transparent;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 4px;
|
||||
border-radius: calc($image-corner-radius - 4px);
|
||||
border: 4px solid transparent;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&.active {
|
||||
&::before {
|
||||
border-color: $red-60;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border-color: $grey-100;
|
||||
}
|
||||
}
|
||||
|
||||
&.placeholder {
|
||||
background: $grey-90;
|
||||
cursor: default;
|
||||
|
||||
&:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.show-more {
|
||||
position: relative;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
.blurred-bg {
|
||||
filter: blur(3px);
|
||||
transform: scale(1.1); // Slightly scale to hide blur edges
|
||||
}
|
||||
|
||||
.show-more-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
border-radius: $image-corner-radius;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.show-more-overlay {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import UniverseIcon from '$icons/universe.svg'
|
||||
import Slideshow from './Slideshow.svelte'
|
||||
import type { UniverseItem } from '../../routes/api/universe/+server'
|
||||
|
||||
let { album }: { album: UniverseItem } = $props()
|
||||
|
|
@ -12,6 +13,25 @@
|
|||
year: 'numeric'
|
||||
})
|
||||
}
|
||||
|
||||
// Convert photos to slideshow items
|
||||
const slideshowItems = $derived(
|
||||
album.photos && album.photos.length > 0
|
||||
? album.photos.map(photo => ({
|
||||
url: photo.url,
|
||||
thumbnailUrl: photo.thumbnailUrl,
|
||||
caption: photo.caption,
|
||||
alt: photo.caption || album.title
|
||||
}))
|
||||
: album.coverPhoto
|
||||
? [{
|
||||
url: album.coverPhoto.url,
|
||||
thumbnailUrl: album.coverPhoto.thumbnailUrl,
|
||||
caption: album.coverPhoto.caption,
|
||||
alt: album.coverPhoto.caption || album.title
|
||||
}]
|
||||
: []
|
||||
)
|
||||
</script>
|
||||
|
||||
<article class="universe-album-card">
|
||||
|
|
@ -23,16 +43,17 @@
|
|||
</time>
|
||||
</div>
|
||||
|
||||
{#if album.coverPhoto}
|
||||
<div class="album-cover">
|
||||
<img
|
||||
src={album.coverPhoto.thumbnailUrl || album.coverPhoto.url}
|
||||
alt={album.coverPhoto.caption || album.title}
|
||||
loading="lazy"
|
||||
{#if slideshowItems.length > 0}
|
||||
<div class="album-slideshow">
|
||||
<Slideshow
|
||||
items={slideshowItems}
|
||||
alt={album.title}
|
||||
aspectRatio="3/2"
|
||||
showThumbnails={slideshowItems.length > 1}
|
||||
maxThumbnails={6}
|
||||
totalCount={album.photosCount}
|
||||
showMoreLink="/photos/{album.slug}"
|
||||
/>
|
||||
<div class="photo-count-overlay">
|
||||
{album.photosCount || 0} photo{(album.photosCount || 0) !== 1 ? 's' : ''}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
|
@ -108,32 +129,10 @@
|
|||
font-weight: 400;
|
||||
}
|
||||
|
||||
.album-cover {
|
||||
.album-slideshow {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
border-radius: $unit;
|
||||
overflow: hidden;
|
||||
margin-bottom: $unit-3x;
|
||||
background: $grey-95;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.photo-count-overlay {
|
||||
position: absolute;
|
||||
bottom: $unit;
|
||||
right: $unit;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: $unit-half $unit-2x;
|
||||
border-radius: 50px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.album-info {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,18 @@ export const GET: RequestHandler = async (event) => {
|
|||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
description: true
|
||||
description: true,
|
||||
photos: {
|
||||
orderBy: { displayOrder: 'asc' },
|
||||
select: {
|
||||
id: true,
|
||||
url: true,
|
||||
thumbnailUrl: true,
|
||||
caption: true,
|
||||
width: true,
|
||||
height: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
photo: {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export interface UniverseItem {
|
|||
date?: string
|
||||
photosCount?: number
|
||||
coverPhoto?: any
|
||||
photos?: any[]
|
||||
}
|
||||
|
||||
// GET /api/universe - Get mixed feed of published posts and albums
|
||||
|
|
@ -74,13 +75,15 @@ export const GET: RequestHandler = async (event) => {
|
|||
select: { photos: true }
|
||||
},
|
||||
photos: {
|
||||
take: 1,
|
||||
take: 6, // Fetch enough for 5 thumbnails + 1 background
|
||||
orderBy: { displayOrder: 'asc' },
|
||||
select: {
|
||||
id: true,
|
||||
url: true,
|
||||
thumbnailUrl: true,
|
||||
caption: true
|
||||
caption: true,
|
||||
width: true,
|
||||
height: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -114,7 +117,8 @@ export const GET: RequestHandler = async (event) => {
|
|||
location: album.location || undefined,
|
||||
date: album.date?.toISOString(),
|
||||
photosCount: album._count.photos,
|
||||
coverPhoto: album.photos[0] || null,
|
||||
coverPhoto: album.photos[0] || null, // Keep for backward compatibility
|
||||
photos: album.photos, // Add all photos for slideshow
|
||||
publishedAt: album.createdAt.toISOString(), // Albums use createdAt as publishedAt
|
||||
createdAt: album.createdAt.toISOString()
|
||||
}))
|
||||
|
|
|
|||
Loading…
Reference in a new issue