Next.js + CETEIcean + React TEI Routerを使ったビューア開発
概要
Next.js、CETEIcean、React TEI Routerを組み合わせたTEI/XMLビューアの開発についての備忘録です。
背景
CETEIceanは、TEI/XML を HTML5 に変換する JavaScript ライブラリです。
そして、React TEI Routerは、CETEIcean をベースに React コンポーネントで TEI/XML を構造化して表示できるライブラリです。以下のように説明されています。
TEI for React using CETEIcean and routes
これらを組み合わせることで、Next.js において TEI/XML をカスタマイズして表示できるビューア を作成しました。
リポジトリ
以下がサンプルリポジトリです。
実際に動作するデモも用意しています。
実装
Next.js のページコンポーネント (page.tsx)
CETEIcean を利用して XML を変換し、カスタムコンポーネントで描画します。
import React from "react";
import Render from "@/components/tei";
export default function App() {
const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0">
<text>
<body>
<div type="original">
私の名前は<persName corresp="#id1">田中太郎</persName>です。
</div>
<div>
<p style="color: green;">こんにちは</p>
<p style="color: green;">こんばんは <seg style="color: blue;">xxx</seg>
</p>
</div>
</body>
</text>
</TEI>`;
return <Render xmlContent={xmlContent} />;
}
TEIレンダリングコンポーネント
- CETEIcean を使って XML を HTML5 に変換。
- TEIRender + TEIRoute を使い、TEI 要素ごとにカスタムコンポーネントを適用。
import { TEIRender, TEIRoute } from "react-teirouter";
を使用した上で、要素毎にコンポーネントを用意しています。
"use client";
import React from "react";
import CETEI from "CETEIcean";
import { TEIRender, TEIRoute } from "react-teirouter";
import { Div } from "@/components/tei/element/div";
import { PersName } from "@/components/tei/element/persname";
import { P } from "@/components/tei/element/p";
import { Seg } from "@/components/tei/element/seg";
export default function Render({ xmlContent }: { xmlContent: string }) {
const [teiDoc, setTeiDoc] = React.useState<Document | null>(null);
React.useEffect(() => {
const fetchData = async () => {
// CETEIceanをインスタンス化
const CETEIcean = new CETEI();
const data = await CETEIcean.makeHTML5(xmlContent);
setTeiDoc(data);
};
fetchData();
}, [xmlContent]);
if (teiDoc) {
const teiElement = teiDoc.querySelector("tei-tei");
if (teiElement) {
return (
<div
className=""
style={{ writingMode: "vertical-rl", height: "calc(100vh - 128px)" }}
>
<TEIRender data={teiElement}>
<TEIRoute el="tei-div" component={Div} />
<TEIRoute el="tei-p" component={P} />
<TEIRoute el="tei-persname" component={PersName} />
<TEIRoute el="tei-seg" component={Seg} />
</TEIRender>
</div>
);
}
}
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "calc(100vh - 128px)",
}}
>
<h1
style={{
fontFamily: "Arial, sans-serif",
color: "#333",
fontSize: "24px",
}}
>
Loading TEI...
</h1>
</div>
);
}
カスタムコンポーネント (p.tsx の例)
pタグの例です。<TEINodes teiNodes={props.teiNode.childNodes} {...props} />
を用いることで、再帰的にタグの処理を行うことができました。
import { TEINodes } from "react-teirouter";
import { getStyle } from "@/utils";
export function P(props: { teiNode: ChildNode }) {
const teiNode = props.teiNode;
const style = getStyle(teiNode);
return (
<p style={style} className="m-1">
<TEINodes teiNodes={props.teiNode.childNodes} {...props} />
</p>
);
}
補足
この方法を利用したものとして、CETEIcean を React コンポーネントとして活用し、カスタム要素を TEI にマッピングする「Astro TEI - React」がありました。
An Astro component for publishing TEI as Custom Elements powered by CETEIcean.
This utility provides the React version of CETEIcean's default behaviors and provides a way of mapping your own React components to TEI elements via React TEI Router.
ただし、サイズが大きいTEI/XMLに対しては、 React TEI Router
でレンダリングすると 再帰的に回す処理が多くなり、パフォーマンスが低下する可能性があります。
その場合、React componentとしては使用せず、CETEIceanのみを使用する方がよいかもしれません。
var CETEIcean = new CETEI()
CETEIcean.getHTML5("URL_TO_YOUR_TEI.xml", function(data) {
document.getElementById("TEI").appendChild(data)
})
まとめ
ReactでのTEI/XMLの利用にあたり、参考になりましたら幸いです。
Discussion