|
@@ -0,0 +1,122 @@
|
|
|
+// const _text_styles_ = ["bold", "underline", "italic", "sup", "sub"];
|
|
|
+
|
|
|
+import { RichTextBlockJSON, RichTextJSON, RichTextSectionJSON } from "@/types";
|
|
|
+
|
|
|
+let _container = document.createElement("div");
|
|
|
+/**
|
|
|
+ * 将富文本 JSON 渲染到指定的元素中
|
|
|
+ *
|
|
|
+ * @param {RichTextJSON} body
|
|
|
+ * @param {HTMLDivElement} container
|
|
|
+ */
|
|
|
+export function renderRichText(body: RichTextJSON, container?: HTMLDivElement) {
|
|
|
+ _container = container || document.createElement("div");
|
|
|
+ let sections = body?.sections || [];
|
|
|
+ let nodes = [] as Array<Node>;
|
|
|
+ sections.forEach((section) => {
|
|
|
+ nodes.push(renderSection(section));
|
|
|
+ });
|
|
|
+ if (_container != undefined) {
|
|
|
+ // container.classList.add("rich-text");
|
|
|
+ while (_container.hasChildNodes()) {
|
|
|
+ _container.removeChild(_container.lastChild as Node);
|
|
|
+ }
|
|
|
+ nodes.forEach((node) => {
|
|
|
+ _container.appendChild(node);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return _container;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @param {RichTextSectionJSON} section
|
|
|
+ * @returns {HTMLDivElement} 返回根据 section 渲染好的 HTMLDivElement
|
|
|
+ */
|
|
|
+function renderSection(section: RichTextSectionJSON) {
|
|
|
+ let blocks = section.blocks || [];
|
|
|
+ let inline = blocks.length > 1;
|
|
|
+ let node = document.createElement("div");
|
|
|
+ // node.style = "display: flex;";
|
|
|
+ blocks.forEach((block) => {
|
|
|
+ node.appendChild(renderBlock(block, inline));
|
|
|
+ });
|
|
|
+ return node;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @param {RichTextBlockJSON} block
|
|
|
+ * @param {Boolean} inline 图片是否以 inline 的样式展示
|
|
|
+ * @returns {HTMLElement} 返回根据 block 渲染好的 HTMLElement
|
|
|
+ */
|
|
|
+function renderBlock(block: RichTextBlockJSON, inline: boolean) {
|
|
|
+ // let node = document.createElement('span')
|
|
|
+ // let classList = node.classList
|
|
|
+ let node;
|
|
|
+ if (block.type === "text") {
|
|
|
+ // classList.add('text')
|
|
|
+ // if (block.param != undefined) {
|
|
|
+ // _text_styles_.forEach(style => {
|
|
|
+ // if (block.param[style] === true) {
|
|
|
+ // classList.add(style)
|
|
|
+ // }
|
|
|
+ // })
|
|
|
+ // }
|
|
|
+ if (
|
|
|
+ block.param &&
|
|
|
+ (block.param.underline || block.param.bold || block.param.italic)
|
|
|
+ ) {
|
|
|
+ let uNode: Node | null = null,
|
|
|
+ bNode: Node | null = null,
|
|
|
+ iNode: Node | null = null;
|
|
|
+ if (block.param.underline) {
|
|
|
+ uNode = document.createElement("u");
|
|
|
+ }
|
|
|
+ if (block.param.bold) {
|
|
|
+ bNode = document.createElement("b");
|
|
|
+ }
|
|
|
+ if (block.param.italic) {
|
|
|
+ iNode = document.createElement("i");
|
|
|
+ }
|
|
|
+ // 将不为空的元素依次append
|
|
|
+ node = ([uNode, bNode, iNode] as Array<Node>)
|
|
|
+ .filter((v) => v)
|
|
|
+ .reduceRight((p, c) => {
|
|
|
+ c.appendChild(p);
|
|
|
+ return c;
|
|
|
+ });
|
|
|
+
|
|
|
+ let childNode = node;
|
|
|
+
|
|
|
+ for (let i = 0; i < 3; i++) {
|
|
|
+ if (childNode && childNode.hasChildNodes()) {
|
|
|
+ childNode = childNode.childNodes[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ childNode.textContent = block.value;
|
|
|
+ } else {
|
|
|
+ node = document.createTextNode(block.value);
|
|
|
+ }
|
|
|
+ } else if (block.type === "image") {
|
|
|
+ node = document.createElement("img");
|
|
|
+ if (inline === true) {
|
|
|
+ node.classList.add("inline");
|
|
|
+ }
|
|
|
+
|
|
|
+ node.src = block.value;
|
|
|
+
|
|
|
+ // param
|
|
|
+ if (block.param) {
|
|
|
+ node.style.width = block.param.width;
|
|
|
+ node.style.height = block.param.height;
|
|
|
+ }
|
|
|
+ } else if (block.type === "audio") {
|
|
|
+ node = document.createElement("audio");
|
|
|
+ node.className = "audio";
|
|
|
+ node.src = block.value;
|
|
|
+ node.controls = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return node as Node;
|
|
|
+}
|