Restructure the chapter shell around a visible outline

This commit is contained in:
pat
2026-09-01 03:44:06 +00:00
parent 8937061c49
commit b645d1bec2
+82 -35
View File
@@ -1,16 +1,18 @@
import { useEffect } from "react"; import { useEffect, useMemo } from "react";
import Head from "next/head"; import Head from "next/head";
import ReaderNav from "../../components/reader/ReaderNav"; import ReaderNav from "../../components/reader/ReaderNav";
import ReaderFooter from "../../components/reader/ReaderFooter"; import ReaderFooter from "../../components/reader/ReaderFooter";
import WorkflowBlock from "../../components/reader/WorkflowBlock"; import WorkflowBlock from "../../components/reader/WorkflowBlock";
import ChapterOutline from "../../components/reader/ChapterOutline";
import useActivation from "../../components/reader/useActivation"; import useActivation from "../../components/reader/useActivation";
import { CHAPTER, RECIPES } from "../../data/sample-chapter"; import { CHAPTER, RECIPES, buildOutline } from "../../data/sample-chapter";
const RECIPE_IDS = RECIPES.map((r) => r.id); const RECIPE_IDS = RECIPES.map((r) => r.id);
const ESSAY_KEYS = ["stakes", "constraints", "practice"];
function Essay({ heading, paragraphs }) { function Essay({ id, heading, paragraphs }) {
return ( return (
<section className="essay"> <section className="essay" id={id}>
<h2>{heading}</h2> <h2>{heading}</h2>
{paragraphs.map((p) => ( {paragraphs.map((p) => (
<p key={p}>{p}</p> <p key={p}>{p}</p>
@@ -22,6 +24,9 @@ function Essay({ heading, paragraphs }) {
export default function RealEstateChapter() { export default function RealEstateChapter() {
const { complete, toggle, count, total } = useActivation(RECIPE_IDS); const { complete, toggle, count, total } = useActivation(RECIPE_IDS);
const [first, second] = RECIPES; const [first, second] = RECIPES;
const outline = useMemo(() => buildOutline(CHAPTER, RECIPES), []);
const essayGroup = outline.find((g) => g.label === "Essay");
const workflowGroup = outline.find((g) => g.label === "Workflows");
useEffect(() => { useEffect(() => {
const hash = window.location.hash; const hash = window.location.hash;
@@ -33,32 +38,23 @@ export default function RealEstateChapter() {
return ( return (
<> <>
<Head> <Head>
<title>{CHAPTER.title} — AIBook.fun reading demo</title> <title>{CHAPTER.title} · AIBook.fun reading demo</title>
<meta name="description" content={CHAPTER.promise} /> <meta name="description" content={CHAPTER.promise} />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
</Head> </Head>
<ReaderNav chapterName={CHAPTER.vertical} /> <ReaderNav chapterName={CHAPTER.vertical} />
<div className="chapter-progress" role="status" aria-live="polite">
<div className="wrap chapter-progress-in">
<span>
{count}/{total} Try-its complete
</span>
<span className="chapter-progress-links">
{RECIPES.map((r) => (
<a key={r.id} href={`#${r.anchor}`}>
{r.id}
{complete[r.id] ? " · done" : ""}
</a>
))}
</span>
</div>
</div>
<main> <main>
<article className="chapter-page"> <article className="chapter-page">
<div className="wrap"> <div className="wrap chapter-shell">
<ChapterOutline
groups={outline}
complete={complete}
count={count}
total={total}
/>
<div className="chapter-col"> <div className="chapter-col">
<span className="spec demo-chip">Reading demo · not the live book</span> <span className="spec demo-chip">Reading demo · not the live book</span>
<p className="spec chapter-kicker"> <p className="spec chapter-kicker">
@@ -67,40 +63,90 @@ export default function RealEstateChapter() {
<h1 className="chapter-title">{CHAPTER.title}</h1> <h1 className="chapter-title">{CHAPTER.title}</h1>
<p className="chapter-by">{CHAPTER.authors}</p> <p className="chapter-by">{CHAPTER.authors}</p>
<ChapterOutline
variant="mobile"
groups={outline}
complete={complete}
count={count}
total={total}
/>
<p className="chapter-promise">{CHAPTER.promise}</p> <p className="chapter-promise">{CHAPTER.promise}</p>
<section className="chapter-map" aria-label="What's in this chapter">
<h2 className="chapter-map-title">What's in this chapter</h2>
<div className="chapter-map-block">
<p className="spec">Essay</p>
<ul>
{essayGroup.items.map((item) => (
<li key={item.id}>
<a href={`#${item.id}`}>{item.label}</a>
</li>
))}
</ul>
</div>
<div className="chapter-map-block">
<p className="spec">Workflows</p>
<ul>
{workflowGroup.items.map((item) => (
<li key={item.id}>
<a href={`#${item.id}`}>
{item.recipeId} · {item.shortTitle} · {item.time}
</a>
</li>
))}
</ul>
</div>
</section>
<div className="skip-row"> <div className="skip-row">
<span className="skip-label">Skip to a Try-it</span> <span className="skip-label">Skip to a Try-it</span>
{CHAPTER.skip.map((s) => ( {RECIPES.map((r) => (
<a key={s.id} className="skip-chip" href={s.href}> <a key={r.id} className="skip-chip" href={`#${r.anchor}`}>
{s.id} {r.id}
<span className="skip-chip-job">{s.title}</span> <span className="skip-chip-job">{r.shortTitle}</span>
</a> </a>
))} ))}
</div> </div>
<Essay heading={CHAPTER.stakes.heading} paragraphs={CHAPTER.stakes.paragraphs} /> {ESSAY_KEYS.map((key) => (
<Essay <Essay
heading={CHAPTER.constraints.heading} key={key}
paragraphs={CHAPTER.constraints.paragraphs} id={CHAPTER[key].id}
/> heading={CHAPTER[key].heading}
<Essay heading={CHAPTER.practice.heading} paragraphs={CHAPTER.practice.paragraphs} /> paragraphs={CHAPTER[key].paragraphs}
/>
))}
<p className="spec workflow-kicker">
Workflow 1 of {RECIPES.length} in this chapter
</p>
<WorkflowBlock <WorkflowBlock
recipe={first} recipe={first}
index={1}
total={RECIPES.length}
complete={!!complete[first.id]} complete={!!complete[first.id]}
onToggleComplete={toggle} onToggleComplete={toggle}
/> />
<Essay heading={CHAPTER.between.heading} paragraphs={CHAPTER.between.paragraphs} /> <Essay
id={CHAPTER.between.id}
heading={CHAPTER.between.heading}
paragraphs={CHAPTER.between.paragraphs}
/>
<p className="spec workflow-kicker">
Workflow 2 of {RECIPES.length} in this chapter
</p>
<WorkflowBlock <WorkflowBlock
recipe={second} recipe={second}
index={2}
total={RECIPES.length}
complete={!!complete[second.id]} complete={!!complete[second.id]}
onToggleComplete={toggle} onToggleComplete={toggle}
/> />
<section className="end-matter"> <section className="end-matter" id="further">
<h2>Further workflows in this vertical</h2> <h2>Further workflows in this vertical</h2>
<ul className="further-list"> <ul className="further-list">
{CHAPTER.further.map((item) => ( {CHAPTER.further.map((item) => (
@@ -125,14 +171,15 @@ export default function RealEstateChapter() {
</li> </li>
))} ))}
</ul> </ul>
</section>
<section className="end-sources" id="sources">
<h2>Sources</h2> <h2>Sources</h2>
<ul className="further-list"> <ul className="further-list">
{CHAPTER.sources.map((s) => ( {CHAPTER.sources.map((s) => (
<li key={s}>{s}</li> <li key={s}>{s}</li>
))} ))}
</ul> </ul>
<p className="chapter-updated">Last updated: {CHAPTER.lastUpdated}</p> <p className="chapter-updated">Last updated: {CHAPTER.lastUpdated}</p>
</section> </section>
</div> </div>