131 lines
5.1 KiB
TypeScript
131 lines
5.1 KiB
TypeScript
|
|
/**
|
||
|
|
* gallery.yaml ⇄ the page builder's model (#608).
|
||
|
|
*
|
||
|
|
* This is the code that decides what ends up in gallery.yaml when the
|
||
|
|
* builder saves. It lived inside the 1,200-line PageBuilder component, where
|
||
|
|
* nothing could test it; as pure functions it has a table of cases in
|
||
|
|
* app/admin/__tests__/gallery-yaml.test.ts.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { parseAlbumEntries, serializeAlbumEntries } from './albumEntries';
|
||
|
|
import { seedCoverGrid, type AlbumEntry, type Subpage } from './types';
|
||
|
|
|
||
|
|
export interface GalleryState {
|
||
|
|
hero: string[];
|
||
|
|
albums: AlbumEntry[];
|
||
|
|
subpages: Subpage[];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Accepts both subpage shapes gallery.yaml allows: the list of objects the
|
||
|
|
* builder writes, and the older map of name → albums (or name → object).
|
||
|
|
*/
|
||
|
|
export function parseGalleryYaml(raw: Record<string, unknown>): GalleryState {
|
||
|
|
const hero = Array.isArray(raw.hero) ? raw.hero : raw.hero ? [raw.hero as string] : [];
|
||
|
|
const albums = parseAlbumEntries(
|
||
|
|
raw.albums as Array<string | Record<string, string>> | undefined,
|
||
|
|
);
|
||
|
|
|
||
|
|
let subpages: Subpage[] = [];
|
||
|
|
if (Array.isArray(raw.subpages)) {
|
||
|
|
subpages = (raw.subpages as Array<Record<string, unknown>>).map((sp) => ({
|
||
|
|
name: (sp.name as string) || '',
|
||
|
|
title: sp.title as string | undefined,
|
||
|
|
subtitle: sp.subtitle as string | undefined,
|
||
|
|
password: sp.password as string | undefined,
|
||
|
|
enabled: sp.enabled !== false,
|
||
|
|
hidden: sp.hidden === true,
|
||
|
|
proofing: typeof sp.proofing === 'boolean' ? sp.proofing : undefined,
|
||
|
|
location: sp.location as string | undefined,
|
||
|
|
essayText: sp.essayText as string | undefined,
|
||
|
|
essayFile: sp.essayFile as string | undefined,
|
||
|
|
albums: parseAlbumEntries(sp.albums as Array<string | Record<string, string>> | undefined),
|
||
|
|
sections: sp.sections
|
||
|
|
? (sp.sections as Array<Record<string, unknown>>).map((sec) => ({
|
||
|
|
title: (sec.title as string) || '',
|
||
|
|
description: sec.description as string | undefined,
|
||
|
|
albums: parseAlbumEntries(sec.albums as Array<string | Record<string, string>>),
|
||
|
|
}))
|
||
|
|
: undefined,
|
||
|
|
grid: sp.grid as Subpage['grid'],
|
||
|
|
coverGrid:
|
||
|
|
(sp.coverGrid as Subpage['coverGrid']) ?? seedCoverGrid(sp.grid as Subpage['grid']),
|
||
|
|
}));
|
||
|
|
} else if (raw.subpages && typeof raw.subpages === 'object') {
|
||
|
|
subpages = Object.entries(raw.subpages as Record<string, unknown>).map(([name, value]) => {
|
||
|
|
if (Array.isArray(value)) {
|
||
|
|
return { name, albums: parseAlbumEntries(value), sections: undefined, enabled: true };
|
||
|
|
}
|
||
|
|
const sp = value as Record<string, unknown>;
|
||
|
|
return {
|
||
|
|
name,
|
||
|
|
title: sp.title as string | undefined,
|
||
|
|
subtitle: sp.subtitle as string | undefined,
|
||
|
|
password: sp.password as string | undefined,
|
||
|
|
enabled: sp.enabled !== false,
|
||
|
|
hidden: sp.hidden === true,
|
||
|
|
proofing: typeof sp.proofing === 'boolean' ? sp.proofing : undefined,
|
||
|
|
location: sp.location as string | undefined,
|
||
|
|
essayText: sp.essayText as string | undefined,
|
||
|
|
essayFile: sp.essayFile as string | undefined,
|
||
|
|
albums: parseAlbumEntries(sp.albums as Array<string | Record<string, string>> | undefined),
|
||
|
|
sections: undefined,
|
||
|
|
grid: sp.grid as Subpage['grid'],
|
||
|
|
coverGrid:
|
||
|
|
(sp.coverGrid as Subpage['coverGrid']) ?? seedCoverGrid(sp.grid as Subpage['grid']),
|
||
|
|
};
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return { hero, albums, subpages };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** The gallery.yaml object for a builder state. Empty groups are left out. */
|
||
|
|
export function serializeGallery(gallery: GalleryState): Record<string, unknown> {
|
||
|
|
const yamlData: Record<string, unknown> = {};
|
||
|
|
|
||
|
|
if (gallery.hero.length > 0) {
|
||
|
|
yamlData.hero = gallery.hero;
|
||
|
|
}
|
||
|
|
if (gallery.albums.length > 0) {
|
||
|
|
yamlData.albums = serializeAlbumEntries(gallery.albums);
|
||
|
|
}
|
||
|
|
if (gallery.subpages.length > 0) {
|
||
|
|
yamlData.subpages = gallery.subpages.map((sp) => {
|
||
|
|
const entry: Record<string, unknown> = { name: sp.name };
|
||
|
|
if (sp.title) entry.title = sp.title;
|
||
|
|
if (sp.subtitle) entry.subtitle = sp.subtitle;
|
||
|
|
if (sp.password) entry.password = sp.password;
|
||
|
|
if (sp.enabled === false) entry.enabled = false;
|
||
|
|
if (sp.hidden === true) entry.hidden = true;
|
||
|
|
// Both values mean something: false switches proofing off for this page.
|
||
|
|
if (sp.proofing !== undefined) entry.proofing = sp.proofing;
|
||
|
|
if (sp.essayText) entry.essayText = sp.essayText;
|
||
|
|
if (sp.essayFile) entry.essayFile = sp.essayFile;
|
||
|
|
if (sp.grid) entry.grid = sp.grid;
|
||
|
|
if (sp.coverGrid) entry.coverGrid = sp.coverGrid;
|
||
|
|
if (sp.location) entry.location = sp.location;
|
||
|
|
|
||
|
|
if (sp.sections && sp.sections.length > 0) {
|
||
|
|
entry.sections = sp.sections.map((sec) => {
|
||
|
|
const s: Record<string, unknown> = {
|
||
|
|
title: sec.title,
|
||
|
|
albums: serializeAlbumEntries(sec.albums),
|
||
|
|
};
|
||
|
|
if (sec.description) s.description = sec.description;
|
||
|
|
return s;
|
||
|
|
});
|
||
|
|
if (sp.albums.length > 0) {
|
||
|
|
entry.albums = serializeAlbumEntries(sp.albums);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
entry.albums = serializeAlbumEntries(sp.albums);
|
||
|
|
}
|
||
|
|
|
||
|
|
return entry;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return yamlData;
|
||
|
|
}
|