muse/src/render/html/index.ts

164 lines
4.4 KiB
TypeScript

import type { Playbill } from "@proscenium/playbill";
import sortBy from "lodash-es/sortBy";
import { html } from "#lib";
import { GlossaryTable } from "./component/glossary";
import { PageHeader } from "./component/header";
import { ItemCard } from "./component/item";
import { MethodSection } from "./component/method";
import { ResourceSection } from "./component/resource";
import { RuleSection } from "./component/rule";
import { SpeciesSection } from "./component/species";
interface HTMLRenderOptions {
styles?: string;
}
export async function renderPlaybillToHTML(
playbill: Playbill,
opts: HTMLRenderOptions = {},
): Promise<string> {
// Render various resources
const resources = (
await Promise.all(Object.values(playbill.resources).map(ResourceSection))
).join("\n");
const methods = (
await Promise.all(
Object.values(playbill.methods).map((method) => {
return MethodSection(method, playbill);
}),
)
).join("\n");
const species = (
await Promise.all(
Object.values(playbill.species).map((species) => {
return SpeciesSection(species, playbill);
}),
)
).join("\n");
const items = (
await Promise.all(
Object.values(playbill.items).map((item) => {
return ItemCard(item);
}),
)
).join("\n");
const rulesByOrder = sortBy(Object.values(playbill.rules), "order");
const rules = (await Promise.all(rulesByOrder.map(RuleSection))).join("\n");
const glossaryHTML = await GlossaryTable(playbill.glossary);
// Prepare stylesheet
const css = opts.styles ? html`<style>${opts.styles}</style>` : "";
// Main document
const doc = html`
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Playbill: ${playbill.name}</title>
${css}
<script>
function removeHash() {
history.pushState("", document.title, window.location.pathname);
}
function setHash(hash) {
if (hash.length > 0) {
history.pushState(null, null, "#" + hash);
} else {
removeHash();
}
}
function hideAllViews() {
document.querySelectorAll(".view").forEach((view) => {
view.style.display = "none";
});
}
function showAllViews() {
document.querySelectorAll(".view").forEach((view) => {
view.style.display = "block";
});
setHash("");
}
function showView(viewId) {
document.getElementById(viewId).style.display = "block";
setHash(viewId);
}
function hideAllAndShow(viewId) {
hideAllViews();
showView(viewId);
}
function navigateFromHash() {
const hash = window.location.hash.slice(1);
if (hash.length > 0) {
const view = document.getElementById(hash);
if (view !== null) {
hideAllAndShow(hash);
} else {
showAllViews();
}
} else {
showAllViews();
}
}
document.addEventListener("DOMContentLoaded", navigateFromHash());
window.addEventListener("hashchange", navigateFromHash())
</script>
</head>
<body>
${await PageHeader(playbill)}
<blockquote class="overview"><p>${playbill.description}</p></blockquote>
<nav>
<button onclick="hideAllAndShow('species')" id="species-nav">Species</button>
<button onclick="hideAllAndShow('methods')" id="methods-nav">Methods</button>
<button onclick="hideAllAndShow('items')" id="items-nav">Items</button>
<button onclick="hideAllAndShow('rules')" id="rules-nav">Rulebook</button>
<button onclick="hideAllAndShow('glossary')" id="glossary-nav">Glossary</button>
<button onclick="hideAllAndShow('resources')" id="resources-nav">Resources</button>
<button onclick="showAllViews()" id="all-nav">Show All</button>
</nav>
<article id="species" class="view">
${species}
</article>
<article id="methods" class="view">
${methods}
</article>
<article id="items" class="view">
${items}
</article>
<article id="rules" class="view">
${rules}
</article>
<article id="glossary" class="view">
${glossaryHTML}
</article>
<article id="resources" class="view">
${resources}
</article>
</article>
</body>
</html>
`;
return doc;
}