mirror of
https://github.com/patarapolw/cjdic2.git
synced 2026-11-09 17:39:35 +00:00
component renderer
This commit is contained in:
1 parent
098ee45b81
commit
d96346bfea
2 files changed
+155
-5
No files matched your search
+25
-5
@@ -2,9 +2,10 @@ import "./App.css";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { Button, Group, Input, Stack } from "@chakra-ui/react";
|
||||
import { Box, Button, Card, Group, Input, Stack } from "@chakra-ui/react";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
import Glossary from "./components/Glossary";
|
||||
import { Provider } from "./components/ui/provider";
|
||||
|
||||
function App() {
|
||||
@@ -50,16 +51,35 @@ function App() {
|
||||
value={q}
|
||||
onChange={(e) => setQ(e.currentTarget.value)}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
<Button type="submit">Search</Button>
|
||||
</Group>
|
||||
</form>
|
||||
<ol>
|
||||
{entries.map((it, i) => (
|
||||
<li key={i}>{JSON.stringify(it)}</li>
|
||||
<Box as={"ol"} listStyleType={"number"} style={{ margin: "1em" }}>
|
||||
{entries.map(({ term, reading, glossary_json, ...it }, i) => (
|
||||
<li key={i}>
|
||||
<details>
|
||||
<summary>
|
||||
{term} ({reading})
|
||||
</summary>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>{JSON.stringify(it)}</Card.Header>
|
||||
<Card.Body>
|
||||
{JSON.parse(glossary_json).map((g: any) => (
|
||||
<Glossary
|
||||
glossary={g}
|
||||
onTermClicked={(t) => setQ(t + " ")}
|
||||
/>
|
||||
))}
|
||||
</ol>
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
</details>
|
||||
</li>
|
||||
))}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user