Improved markdown rendering, allow hiding components

This commit is contained in:
Endeavorance 2025-03-20 16:51:34 -04:00
parent d200d48903
commit a9a979c5f8
10 changed files with 598 additions and 100 deletions

View file

@ -1,6 +1,6 @@
import { Playbill, type TaggedComponent } from "@proscenium/playbill";
import sortBy from "lodash-es/sortBy";
import { renderMarkdown } from "#lib";
import { createMarkdownRenderer, type BoundPlaybill } from "#lib";
import { GlossaryTable } from "./component/glossary";
import { PageHeader } from "./component/header";
import { ItemCard } from "./component/item";
@ -10,61 +10,64 @@ import { RuleSection } from "./component/rule";
import { SpeciesSection } from "./component/species";
import { renderToString } from "react-dom/server";
interface HTMLRenderOptions {
styles?: string;
}
async function processMarkdownInComponent(entry: TaggedComponent) {
entry.component.description = await renderMarkdown(entry.component.description);
if (entry.type === "resource" && entry.component.type === "table") {
const newData: string[][] = [];
for (const row of entry.component.data) {
const newRow: string[] = [];
for (const cell of row) {
newRow.push(await renderMarkdown(cell));
}
newData.push(newRow);
}
entry.component.data = newData;
}
}
export async function renderPlaybillToHTML(
playbillToRender: Playbill,
opts: HTMLRenderOptions = {},
boundPlaybill: BoundPlaybill,
): Promise<string> {
// Make a copy of the playbill to avoid modifying the original
// and compile all description markdown
const playbill = Playbill.fromSerialized(playbillToRender.serialize());
const playbill = Playbill.fromSerialized(boundPlaybill.playbill.serialize());
// Prepare a rendering function for markdown that is aware of the playbill binding
const renderMarkdown = createMarkdownRenderer(boundPlaybill);
// Define a processor function to iterate over all components and process their markdown
const processMarkdownInComponent = async (entry: TaggedComponent) => {
entry.component.description = await renderMarkdown(entry.component.description);
if (entry.type === "resource" && entry.component.type === "table") {
const newData: string[][] = [];
for (const row of entry.component.data) {
const newRow: string[] = [];
for (const cell of row) {
newRow.push(await renderMarkdown(cell));
}
newData.push(newRow);
}
entry.component.data = newData;
}
}
// Process all components
await playbill.processComponents(processMarkdownInComponent);
// Soprt rules by their order prop
const rulesByOrder = sortBy(Object.values(playbill.rules), "order");
// Prepare stylesheet
const css = opts.styles ? `<style>${opts.styles}</style>` : "";
const css = `<style>${boundPlaybill.styles}</style>`;
const body = renderToString(<>
<article id="species" className="view">
{Object.values(playbill.species).map((species) => <SpeciesSection key={species.id} species={species} playbill={playbill} />)}
</article>
<article id="methods" className="view">
<article id="methods" className="view" style={{ display: "none" }}>
{Object.values(playbill.methods).map((method) => <MethodSection key={method.id} method={method} playbill={playbill} />)}
</article>
<article id="items" className="view">
<article id="items" className="view" style={{ display: "none" }}>
{Object.values(playbill.items).map((item) => <ItemCard key={item.id} item={item} />)}
</article>
<article id="rules" className="view">
<article id="rules" className="view" style={{ display: "none" }}>
{rulesByOrder.map((rule) => <RuleSection key={rule.id} rule={rule} />)}
</article>
<article id="glossary" className="view">
<article id="glossary" className="view" style={{ display: "none" }}>
{<GlossaryTable glossary={playbill.glossary} />}
</article>
<article id="resources" className="view">
<article id="resources" className="view" style={{ display: "none" }}>
{Object.values(playbill.resources).map((resource) => <ResourceSection key={resource.id} resource={resource} />)}
</article>
</>);