From 11180b66477f99ed5cbb4e88bf60baabe75038bd Mon Sep 17 00:00:00 2001 From: p208p2002 Date: Sat, 31 Aug 2024 12:55:32 +0800 Subject: [PATCH] feat: render md table --- src/modules/MdRender/index.css | 8 ++ src/modules/MdRender/index.jsx | 29 +++-- src/modules/MdRender/md-table-to-html.js | 133 +++++++++++++++++++++++ src/tailwindcss.css | 4 + 4 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 src/modules/MdRender/md-table-to-html.js diff --git a/src/modules/MdRender/index.css b/src/modules/MdRender/index.css index 926ecaf..6ab1a8f 100644 --- a/src/modules/MdRender/index.css +++ b/src/modules/MdRender/index.css @@ -239,4 +239,12 @@ padding-right: 5px; margin-left: 6px; color: var(--text-2); +} +#MD tr:first-child{ + border-width: 0px 0px 2px 0px; + border-style: solid; + border-color: var(--main-1);; +} +#MD td{ + padding: 5px; } \ No newline at end of file diff --git a/src/modules/MdRender/index.jsx b/src/modules/MdRender/index.jsx index 735f2ae..bd8438a 100644 --- a/src/modules/MdRender/index.jsx +++ b/src/modules/MdRender/index.jsx @@ -16,7 +16,7 @@ import Gitalk from 'gitalk' import Darkmode from 'drkmd-js' import ExifReader from 'exifreader'; import { siNikon, siLens, siApple } from 'simple-icons' - +import { mdTableToHTML } from './md-table-to-html' const axios = require('axios'); @@ -169,7 +169,7 @@ export default function MdRender({ doc_id }) { setPageTitle(`${gistTitle} - ${BLOG_NAME}`) setPageDescription(gistContent.replaceAll("#", " ").slice(0, 500)) }) - .catch(()=>{ + .catch(() => { window.location.href = "/?page=code-404" }) // eslint-disable-next-line @@ -188,7 +188,7 @@ export default function MdRender({ doc_id }) { }) gitalk.render("comments") } - }, [doc_id,postTitle]) + }, [doc_id, postTitle]) let { date = "", tags = [] } = documentInfo @@ -286,8 +286,21 @@ export default function MdRender({ doc_id }) { return
{children}
} }, + p({ node, inline, className, children, ...props }) { + if (Array.isArray(children)) { + children = children.map((child, childIdx) => { + if (typeof (child) === "string" && child.length > 1 && child[0] === "|") { + return
+ } + return child + }) + } + return

{children}

+ }, span({ node, inline, className, children, ...props }) { - + if (className === "math math-inline") { let math_tex = children[0] || ""; let math_html = katex.renderToString(math_tex, { @@ -297,7 +310,7 @@ export default function MdRender({ doc_id }) { }); return } - + else { return {children} } @@ -343,9 +356,9 @@ export default function MdRender({ doc_id }) { }) .catch(() => { // image without exif info - let exifText = document.getElementById(img_url+"EXIF-Text") + let exifText = document.getElementById(img_url + "EXIF-Text") try { - exifText.parentNode.removeChild(exifText) + exifText.parentNode.removeChild(exifText) } catch (error) { // pass } @@ -358,7 +371,7 @@ export default function MdRender({ doc_id }) { -  |  +  |  diff --git a/src/modules/MdRender/md-table-to-html.js b/src/modules/MdRender/md-table-to-html.js new file mode 100644 index 0000000..6feadc7 --- /dev/null +++ b/src/modules/MdRender/md-table-to-html.js @@ -0,0 +1,133 @@ +const alignments = ['left', 'center', 'right']; + +/** + * Converts a markdown table string into HTML. + * @param {string} md Markdown table as a string. + */ +export function mdTableToHTML(md) { + var lines = getLines(md); + var isHeader = true; + var table = new HTMLTable(); + var colAlignments = []; + lines.forEach(function(line) { + if (isHeaderSeparation(line)) { + isHeader = false; + colAlignments = getAlignment(line); + return; + } + var vals = splitLine(line); + table.addRow(vals, isHeader); + }); + table.setAlignments(colAlignments); + return { + html: table.getHTML(), + htmlString: table.getHTMLString() + } +} + +/** + * Converts a document into an array of string, where each element corresponds to one row. + * @param {string} doc Document + * @returns {string[]} The lines of the document. + */ +function getLines(doc) { + return doc.replace(/\r\n/g,"\n").split("\n") +} + +/** + * Checks whether a line contains the table syntax that separates table header from body. + * Example for a separation line: "| --- |:---:| ---:|" + * @param {string} line A line of a markdown document. + * @returns {boolean} True if the line is a separation line. + */ +function isHeaderSeparation(line) { + let match = line.match(/(\|\s*(:)?\s*-{3,}\s*(:)?\s*)+\|/g); + if (!Array.isArray(match)) return false; + return match.length > 0; +} + +/** + * Reads the column alignment information from a separation line. + * E.g. "| --- |:---:| ---:|" is ['left', 'center', 'right'] + * @param {string} headerLine A line for which isHeaderSeparation returns true. + * @returns {string[]} The alignment of each column, entries are in ['left', 'center', 'right']. + */ +function getAlignment(headerLine) { + let parts = splitLine(headerLine); + + return parts.map(col => { + if (col.length === 0) return alignments[0]; + let firstChar = col.charAt(0), + lastChar = col.slice(-1); + if (firstChar === ':' && lastChar === ':') return alignments[1]; + if (lastChar === ':') return alignments[2]; + return alignments[0]; + }) +} + +/** + * Converts an alignment into the corresponding style tag. + * Empty string for alignment: left (default). + * @param {string} alignment Alignment, i.e. either of ['left', 'center', 'right']. + * @returns {string} The style attribute with text-alignment. + */ +function getStyleAttribute(alignment) { + if (alignment === alignments[0]) return ''; + return ' style="text-align: ' + alignment + ';"'; +} + +/** + * Splits a Markdown table line into its trimmed column values. + * @param {string} line Markdown table line. + * @returns {string[]} The trimmed column values. + */ +function splitLine(line) { + return line.split('|').map(x => x.trim()).filter((x, i, a) => { return x.length > 0 || [0, a.length].indexOf(i) === -1}) +} + +/** + * HTML table creation helper class. + * Constructs the table and returns the HTML code. + */ +function HTMLTable() { + this.ths = []; + this.tds = []; + this.alignments = []; + + this.getHTMLString = function() { + let newline = '\n'; + return "" + newline + + this.ths.map((x) => { return this.getRow(x, 'th'); }, this).join(newline) + newline + + this.tds.map((x) => { return this.getRow(x, 'td'); }, this).join(newline) + newline + + "
" + }; + + this.getHTML = function() { + return '' + + this.ths.map((x, index) => { return this.getRow(x, 'th'); }, this).join('') + + this.tds.map((x, index) => { return this.getRow(x, 'td'); }, this).join('') + + '
' + }; + + this.getRow = function(vals, tag) { + return "" + vals.map((x, index) => { return "<" + tag + getStyleAttribute(this.getAlignment(index)) + ">" + x + ""}).join('') + ""; + }; + + this.addRow = function(vals, isHeader) { + if (isHeader) { + this.ths.push(vals); + } + else { + this.tds.push(vals); + } + }; + + this.setAlignments = function(alignments) { + this.alignments = alignments; + }; + + this.getAlignment = function(colIndex) { + if (this.alignments.length <= colIndex) return alignments[0]; + return this.alignments[colIndex]; + } +} \ No newline at end of file diff --git a/src/tailwindcss.css b/src/tailwindcss.css index 81af1d3..4bf782d 100644 --- a/src/tailwindcss.css +++ b/src/tailwindcss.css @@ -627,6 +627,10 @@ video { display: flex; } +.table { + display: table; +} + .hidden { display: none; }