v1.3 - added an sqlite db and built data structure
This commit is contained in:
parent
b0fba52c0e
commit
5efdafbb97
37 changed files with 6414 additions and 1988 deletions
78
src/lib/sections.tsx
Normal file
78
src/lib/sections.tsx
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { useState } from "react";
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════
|
||||
SECTION MANIFEST
|
||||
|
||||
A page is a list of sections, and a section is a view over data
|
||||
the database already holds, narrowed by a filter. The map of
|
||||
chapters and the vertical list of regions are the same rows seen
|
||||
two ways; the three bands of retreats are one view seen three
|
||||
times with a different filter each.
|
||||
|
||||
So a page declares what it wants and this turns it into what
|
||||
PageShell takes. Every entry looks like:
|
||||
|
||||
{
|
||||
id, title, blurb, accent, background, // the heading
|
||||
Component, // the view
|
||||
props: { ... }, // the filter
|
||||
views: { // optional toggle
|
||||
options: ["carousel", "grid"],
|
||||
default: "carousel",
|
||||
Toggle: EventCardsToggle,
|
||||
},
|
||||
}
|
||||
|
||||
Sections fetch their own data. The page never does, which is why
|
||||
there's no loading or error state here — each view handles its
|
||||
own, and one section failing doesn't blank the page.
|
||||
|
||||
Every Component receives `accent` so its empty and error notices
|
||||
match the heading above them, and `view` when the entry declares
|
||||
a toggle. Both are ignored harmlessly by a section that doesn't
|
||||
want them.
|
||||
═══════════════════════════════════════════════════════════════ */
|
||||
|
||||
export function useSectionManifest(manifest) {
|
||||
// One entry per section that has a toggle, seeded from its
|
||||
// declared default so the control is right before anything loads.
|
||||
const [views, setViews] = useState(() =>
|
||||
Object.fromEntries(
|
||||
manifest
|
||||
.filter(entry => entry.views)
|
||||
.map(entry => [
|
||||
entry.id,
|
||||
entry.views.default ?? entry.views.options?.[0] ?? null,
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
const setView = (id, value) => setViews(prev => ({ ...prev, [id]: value }));
|
||||
|
||||
return manifest.map(entry => {
|
||||
const { Component, props, views: spec, ...heading } = entry;
|
||||
const view = views[entry.id];
|
||||
const Toggle = spec?.Toggle;
|
||||
|
||||
return {
|
||||
...heading,
|
||||
|
||||
actions: Toggle ? (
|
||||
<Toggle
|
||||
view={view}
|
||||
setView={value => setView(entry.id, value)}
|
||||
accent={entry.accent}
|
||||
options={spec.options}
|
||||
/>
|
||||
) : undefined,
|
||||
|
||||
content: (
|
||||
<Component
|
||||
{...(props ?? {})}
|
||||
accent={entry.accent}
|
||||
{...(spec ? { view } : {})}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue