Concept jsx usage

This commit is contained in:
Endeavorance 2025-03-17 20:48:37 -04:00
parent bf444ccb1a
commit d769e298b1
7 changed files with 92 additions and 20 deletions

View file

@ -6,8 +6,11 @@
"dependencies": {
"@proscenium/playbill": "link:@proscenium/playbill",
"chalk": "^5.4.1",
"classnames": "^2.5.1",
"lodash-es": "^4.17.21",
"marked": "^15.0.7",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"slugify": "^1.6.6",
"yaml": "^2.7.0",
"zod": "^3.24.1",
@ -16,6 +19,8 @@
"@biomejs/biome": "^1.9.4",
"@types/bun": "latest",
"@types/lodash-es": "^4.17.12",
"@types/react": "^19.0.11",
"@types/react-dom": "^19.0.4",
},
"peerDependencies": {
"typescript": "^5.0.0",
@ -51,16 +56,30 @@
"@types/node": ["@types/node@22.13.10", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw=="],
"@types/react": ["@types/react@19.0.11", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-vrdxRZfo9ALXth6yPfV16PYTLZwsUWhVjjC+DkfE5t1suNSbBrWC9YqSuuxJZ8Ps6z1o2ycRpIqzZJIgklq4Tw=="],
"@types/react-dom": ["@types/react-dom@19.0.4", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg=="],
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
"bun-types": ["bun-types@1.2.5", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg=="],
"chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
"classnames": ["classnames@2.5.1", "", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
"marked": ["marked@15.0.7", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg=="],
"react": ["react@19.0.0", "", {}, "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="],
"react-dom": ["react-dom@19.0.0", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ=="],
"scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="],
"slugify": ["slugify@1.6.6", "", {}, "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="],
"typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="],

View file

@ -6,7 +6,9 @@
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@types/bun": "latest",
"@types/lodash-es": "^4.17.12"
"@types/lodash-es": "^4.17.12",
"@types/react": "^19.0.11",
"@types/react-dom": "^19.0.4"
},
"peerDependencies": {
"typescript": "^5.0.0"
@ -14,8 +16,11 @@
"dependencies": {
"@proscenium/playbill": "link:@proscenium/playbill",
"chalk": "^5.4.1",
"classnames": "^2.5.1",
"lodash-es": "^4.17.21",
"marked": "^15.0.7",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"slugify": "^1.6.6",
"yaml": "^2.7.0",
"zod": "^3.24.1"

View file

@ -226,6 +226,11 @@ export async function loadFromBinding(
playbill.addBlueprint(blueprint);
}
// Add rules
for (const rule of rules) {
playbill.addRule(rule);
}
// Add all definitions
for (const glossary of glossaries) {
for (const [term, definitions] of Object.entries(glossary)) {

View file

@ -0,0 +1,40 @@
import classNames from "classnames";
import React from "react";
interface SectionProps {
type: string;
title: string;
content: string;
preInfo?: React.ReactNode;
info?: React.ReactNode;
componentId?: string;
leftCornerTag?: React.ReactNode;
rightCornerTag?: React.ReactNode;
}
export function Section({
type,
title,
content,
preInfo,
info,
componentId,
leftCornerTag,
rightCornerTag,
}: SectionProps) {
const sectionClasses = classNames("section", type);
const headerClasses = classNames("section-header", `${type}-header`);
const contentClasses = classNames("section-content", `${type}-content`);
return <section className={sectionClasses} data-component-id={componentId}>
<div className={headerClasses}>
{preInfo}
<h2>{title}</h2>
{info}
</div>
<div className="section-tag section-tag--left">{leftCornerTag}</div>
<div className="section-tag section-tag--right">{rightCornerTag}</div>
<div className={contentClasses} dangerouslySetInnerHTML={{ __html: content }} />
</section>
}

View file

@ -1,14 +0,0 @@
import type { Rule } from "@proscenium/playbill";
import { renderMarkdown } from "#lib";
import { Section } from "./base/section";
export async function RuleSection(rule: Rule): Promise<string> {
const renderedMarkdown = await renderMarkdown(rule.description);
return Section({
title: rule.name,
componentId: rule.id,
content: renderedMarkdown,
type: "rule",
});
}

View file

@ -0,0 +1,11 @@
import type { Rule } from "@proscenium/playbill";
import { Section } from "./base/react-section";
export function RuleSection(rule: Rule) {
return <Section
title={rule.name}
componentId={rule.id}
content={rule.description}
type="rule"
/>;
}

View file

@ -1,6 +1,6 @@
import type { Playbill } from "@proscenium/playbill";
import { Playbill } from "@proscenium/playbill";
import sortBy from "lodash-es/sortBy";
import { html } from "#lib";
import { html, renderMarkdown } from "#lib";
import { GlossaryTable } from "./component/glossary";
import { PageHeader } from "./component/header";
import { ItemCard } from "./component/item";
@ -8,15 +8,21 @@ import { MethodSection } from "./component/method";
import { ResourceSection } from "./component/resource";
import { RuleSection } from "./component/rule";
import { SpeciesSection } from "./component/species";
import { renderToString } from "react-dom/server";
interface HTMLRenderOptions {
styles?: string;
}
export async function renderPlaybillToHTML(
playbill: Playbill,
playbillToRender: Playbill,
opts: HTMLRenderOptions = {},
): Promise<string> {
// Make a copy of the playbill to avoid modifying the original
// and compile all description markdown
const playbill = Playbill.fromSerialized(playbillToRender.serialize());
await playbill.processComponents(renderMarkdown);
// Render various resources
const resources = (
await Promise.all(Object.values(playbill.resources).map(ResourceSection))
@ -47,7 +53,7 @@ export async function renderPlaybillToHTML(
).join("\n");
const rulesByOrder = sortBy(Object.values(playbill.rules), "order");
const rules = (await Promise.all(rulesByOrder.map(RuleSection))).join("\n");
const rules = rulesByOrder.map(RuleSection);
const glossaryHTML = await GlossaryTable(playbill.glossary);
// Prepare stylesheet
@ -144,7 +150,7 @@ export async function renderPlaybillToHTML(
</article>
<article id="rules" class="view">
${rules}
${renderToString(rules)}
</article>
<article id="glossary" class="view">