feat: allow subpages to override title and add subtitle in gallery.yaml
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -31,6 +31,7 @@ npm-debug.log*
|
|||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
server.log
|
||||||
|
|
||||||
# env files (can opt-in for committing if needed)
|
# env files (can opt-in for committing if needed)
|
||||||
.env*
|
.env*
|
||||||
|
|||||||
@@ -17,46 +17,56 @@ interface Placeholder {
|
|||||||
|
|
||||||
interface SubpageGridViewProps {
|
interface SubpageGridViewProps {
|
||||||
slug: string;
|
slug: string;
|
||||||
|
title?: string;
|
||||||
|
subtitle?: string;
|
||||||
albums: SubpageAlbum[];
|
albums: SubpageAlbum[];
|
||||||
coverPlaceholders: (Placeholder | null)[];
|
coverPlaceholders: (Placeholder | null)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SubpageGridView({ slug, albums, coverPlaceholders }: SubpageGridViewProps) {
|
export function SubpageGridView({ slug, title, subtitle, albums, coverPlaceholders }: SubpageGridViewProps) {
|
||||||
return (
|
return (
|
||||||
<div className="subpage-grid">
|
<div className="subpage-container">
|
||||||
{albums.map((album, i) => {
|
{(title || subtitle) && (
|
||||||
const ph = coverPlaceholders[i];
|
<header className="subpage-header">
|
||||||
return (
|
{title && <h1 className="subpage-title">{title}</h1>}
|
||||||
<Link
|
{subtitle && <p className="subpage-subtitle">{subtitle}</p>}
|
||||||
key={album.id}
|
</header>
|
||||||
href={`/${slug}/${album.slug}`}
|
)}
|
||||||
className="subpage-grid__item"
|
<div className="subpage-grid">
|
||||||
style={ph?.dominantColor ? { backgroundColor: ph.dominantColor } : undefined}
|
{albums.map((album, i) => {
|
||||||
>
|
const ph = coverPlaceholders[i];
|
||||||
{album.albumThumbnailAssetId ? (
|
return (
|
||||||
<Image
|
<Link
|
||||||
src={imageUrl(album.albumThumbnailAssetId, 'preview')}
|
key={album.id}
|
||||||
alt={album.albumName}
|
href={`/${slug}/${album.slug}`}
|
||||||
fill
|
className="subpage-grid__item"
|
||||||
sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 33vw"
|
style={ph?.dominantColor ? { backgroundColor: ph.dominantColor } : undefined}
|
||||||
loading="lazy"
|
>
|
||||||
{...(ph ? { placeholder: 'blur' as const, blurDataURL: ph.blurDataURL } : {})}
|
{album.albumThumbnailAssetId ? (
|
||||||
/>
|
<Image
|
||||||
) : (
|
src={imageUrl(album.albumThumbnailAssetId, 'preview')}
|
||||||
<div className="skeleton" style={{ width: '100%', height: '100%' }} />
|
alt={album.albumName}
|
||||||
)}
|
fill
|
||||||
<span className="subpage-grid__item-badge">
|
sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 33vw"
|
||||||
{album.assetCount} {album.assetCount === 1 ? 'photo' : 'photos'}
|
loading="lazy"
|
||||||
</span>
|
{...(ph ? { placeholder: 'blur' as const, blurDataURL: ph.blurDataURL } : {})}
|
||||||
<div className="subpage-grid__item-overlay">
|
/>
|
||||||
<span className="subpage-grid__item-title">{album.albumName}</span>
|
) : (
|
||||||
<span className="subpage-grid__item-count">
|
<div className="skeleton" style={{ width: '100%', height: '100%' }} />
|
||||||
|
)}
|
||||||
|
<span className="subpage-grid__item-badge">
|
||||||
{album.assetCount} {album.assetCount === 1 ? 'photo' : 'photos'}
|
{album.assetCount} {album.assetCount === 1 ? 'photo' : 'photos'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<div className="subpage-grid__item-overlay">
|
||||||
</Link>
|
<span className="subpage-grid__item-title">{album.albumName}</span>
|
||||||
);
|
<span className="subpage-grid__item-count">
|
||||||
})}
|
{album.assetCount} {album.assetCount === 1 ? 'photo' : 'photos'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,6 +222,8 @@ export default async function PathPage({ params }: PathPageProps) {
|
|||||||
return (
|
return (
|
||||||
<SubpageGridView
|
<SubpageGridView
|
||||||
slug={slug}
|
slug={slug}
|
||||||
|
title={result.subpage.title || result.subpage.name}
|
||||||
|
subtitle={result.subpage.subtitle}
|
||||||
albums={albums}
|
albums={albums}
|
||||||
coverPlaceholders={coverPlaceholders}
|
coverPlaceholders={coverPlaceholders}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ subpages:
|
|||||||
- "album-uuid-3"
|
- "album-uuid-3"
|
||||||
- "album-uuid-4"
|
- "album-uuid-4"
|
||||||
"Projects":
|
"Projects":
|
||||||
|
title: "My Creative Projects" # Optional: Header title, defaults to Navigation key ("Projects")
|
||||||
|
subtitle: "A collection of 2026 highlights in 35mm film." # Optional: Sub-text for headers
|
||||||
password: "your-password" # Optional protection
|
password: "your-password" # Optional protection
|
||||||
albums:
|
albums:
|
||||||
- "album-uuid-5"
|
- "album-uuid-5"
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ export function getConfig(): AppConfig {
|
|||||||
return {
|
return {
|
||||||
name: sp.name,
|
name: sp.name,
|
||||||
slug: slugify(sp.name),
|
slug: slugify(sp.name),
|
||||||
|
title: sp.title,
|
||||||
|
subtitle: sp.subtitle,
|
||||||
albumIds: sp.albums.map((entry) => processAlbumEntry(entry, `subpage "${sp.name}"`)),
|
albumIds: sp.albums.map((entry) => processAlbumEntry(entry, `subpage "${sp.name}"`)),
|
||||||
password: sp.password,
|
password: sp.password,
|
||||||
...(sp.grid ? {
|
...(sp.grid ? {
|
||||||
@@ -95,12 +97,14 @@ export function getConfig(): AppConfig {
|
|||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return { name, slug: slugify(name), albumIds: value.map((entry) => processAlbumEntry(entry, `subpage "${name}"`)) };
|
return { name, slug: slugify(name), albumIds: value.map((entry) => processAlbumEntry(entry, `subpage "${name}"`)) };
|
||||||
}
|
}
|
||||||
interface SubpageObjectValue { albums?: Array<string | Record<string, string>>; password?: string; grid?: { columns?: number; gap?: number; aspectRatio?: string; layout?: string; }; }
|
interface SubpageObjectValue { title?: string; subtitle?: string; albums?: Array<string | Record<string, string>>; password?: string; grid?: { columns?: number; gap?: number; aspectRatio?: string; layout?: string; }; }
|
||||||
const sp = value as SubpageObjectValue;
|
const sp = value as SubpageObjectValue;
|
||||||
const albumEntries = sp.albums || [];
|
const albumEntries = sp.albums || [];
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
slug: slugify(name),
|
slug: slugify(name),
|
||||||
|
title: sp.title,
|
||||||
|
subtitle: sp.subtitle,
|
||||||
albumIds: albumEntries.map((entry) => processAlbumEntry(entry, `subpage "${name}"`)),
|
albumIds: albumEntries.map((entry) => processAlbumEntry(entry, `subpage "${name}"`)),
|
||||||
password: sp.password,
|
password: sp.password,
|
||||||
...(sp.grid ? {
|
...(sp.grid ? {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
export interface SubpageConfig {
|
export interface SubpageConfig {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
title?: string;
|
||||||
|
subtitle?: string;
|
||||||
albumIds: string[];
|
albumIds: string[];
|
||||||
password?: string;
|
password?: string;
|
||||||
grid?: Partial<GridConfig>;
|
grid?: Partial<GridConfig>;
|
||||||
@@ -79,6 +81,8 @@ export interface GalleryYaml {
|
|||||||
| Record<string, string[] | Array<string | Record<string, string>>>
|
| Record<string, string[] | Array<string | Record<string, string>>>
|
||||||
| Array<{
|
| Array<{
|
||||||
name: string;
|
name: string;
|
||||||
|
title?: string;
|
||||||
|
subtitle?: string;
|
||||||
albums: Array<string | Record<string, string>>;
|
albums: Array<string | Record<string, string>>;
|
||||||
password?: string;
|
password?: string;
|
||||||
grid?: {
|
grid?: {
|
||||||
|
|||||||
Reference in New Issue
Block a user