component renderer

This commit is contained in:
polv committed 2026-02-23 06:24:10 +07:00
1 parent 098ee45b81
commit d96346bfea
2 files changed
+155 -5

No files matched your search

+130
View File
@@ -0,0 +1,130 @@
import { createElement as h } from "react";
export default function Glossary({
glossary,
onTermClicked,
}: {
glossary: any;
onTermClicked: (t: string) => void;
}) {
// Plain string
if (typeof glossary === "string") {
return glossary;
}
// GlossaryText
if ("type" in glossary && glossary.type === "text") {
return glossary.text;
}
// GlossaryImage
if ("type" in glossary && glossary.type === "image") {
return <GlossaryImage {...glossary} />;
}
// GlossaryStructuredContent
if ("type" in glossary && glossary.type === "structured-content") {
const sc = glossary;
return (
<StructuredContent node={sc.content} onTermClicked={onTermClicked} />
);
}
// GlossaryDeinflection
if (Array.isArray(glossary) && glossary.length === 2) {
const [term, rules] = glossary;
return <small>{`${term} (${rules.join(", ")})`}</small>;
}
return null;
}
function GlossaryImage(img: any) {
return (
<img
src={img.path}
width={img.width}
height={img.height}
title={img.title}
alt={img.alt}
style={{
imageRendering: img.imageRendering || "auto",
width: `${img.width}em`,
height: `${img.height}em`,
}}
/>
);
}
function StructuredContent({
node,
onTermClicked,
}: {
node: any;
onTermClicked: (t: string) => void;
}): any {
if (typeof node === "string") {
return node;
}
if (Array.isArray(node)) {
return (
<>
{...node.map((n) => (
<StructuredContent node={n} onTermClicked={onTermClicked} />
))}
</>
);
}
// Line break
if ("tag" in node && node.tag === "br") {
return <br />;
}
// Image
if ("tag" in node && node.tag === "img") {
return <GlossaryImage {...node} />;
}
// Link
if ("tag" in node && node.tag === "a") {
const link = node as any;
const { href, content } = link;
const isWebLink = /^https?:\/\//.test(href);
return (
<a
href={href}
target={isWebLink ? "_blank" : ""}
onClick={(ev) => {
if (isWebLink) return true;
ev.preventDefault();
const querySearch = "?query=";
if (href.startsWith(querySearch)) {
onTermClicked(href.substring(querySearch.length));
}
return false;
}}
>
{content ? (
<StructuredContent node={content} onTermClicked={onTermClicked} />
) : null}
</a>
);
}
// Block/container elements (span, div, ol, ul, li, ruby, table, etc.)
if ("tag" in node) {
const block = node as any;
return h(
block.tag,
{ style: block.style, title: block.title, open: block.open },
StructuredContent({ node: block.content, onTermClicked }),
);
}
return null;
}