diff --git a/web/src/labs/marked/marked.test.ts b/web/src/labs/marked/marked.test.ts index 2f649f16a..7db8477c4 100644 --- a/web/src/labs/marked/marked.test.ts +++ b/web/src/labs/marked/marked.test.ts @@ -3,6 +3,38 @@ import { unescape } from "lodash-es"; import { marked } from "."; describe("test marked parser", () => { + test("test markdown table", () => { + const tests = [ + { + markdown: `| a | b | c | +|---|---|---| +| 1 | 2 | 3 | +| 4 | 5 | 6 |`, + want: ` + + + + + + + + +
abc
123
456
`, + }, + { + markdown: `| a | b | c | +| 1 | 2 | 3 | +| 4 | 5 | 6 |`, + want: `

| a | b | c |

+

| 1 | 2 | 3 |

+

| 4 | 5 | 6 |

`, + }, + ]; + for (const t of tests) { + expect(unescape(marked(t.markdown))).toBe(t.want); + } + }); + test("parse code block", () => { const tests = [ { diff --git a/web/src/labs/marked/parser/Table.ts b/web/src/labs/marked/parser/Table.ts new file mode 100644 index 000000000..050c8446a --- /dev/null +++ b/web/src/labs/marked/parser/Table.ts @@ -0,0 +1,45 @@ +/** + * Match markdown table + * example: + * | a | b | c | + * |---|---|---| + * | 1 | 2 | 3 | + * | 4 | 5 | 6 | + */ +export const TABLE_REG = /^(\|.*\|)(?:(?:\n(?:\|-*)+\|))((?:\n\|.*\|)+)/; + +const renderer = (rawStr: string): string => { + const matchResult = rawStr.match(TABLE_REG); + if (!matchResult) { + return rawStr; + } + const tableHeader = matchResult[1] + .split("|") + .filter((str) => str !== "") + .map((str) => str.trim()); + const tableBody = matchResult[2] + .trim() + .split("\n") + .map((str) => + str + .split("|") + .filter((str) => str !== "") + .map((str) => str.trim()) + ); + return ` + + + ${tableHeader.map((str) => ``).join("")} + + + + ${tableBody.map((row) => `${row.map((str) => ``).join("")}`).join("")} + +
${str}
${str}
`; +}; + +export default { + name: "table", + regex: TABLE_REG, + renderer, +}; diff --git a/web/src/labs/marked/parser/index.ts b/web/src/labs/marked/parser/index.ts index e679f485a..d1eb728bf 100644 --- a/web/src/labs/marked/parser/index.ts +++ b/web/src/labs/marked/parser/index.ts @@ -13,6 +13,7 @@ import Emphasis from "./Emphasis"; import PlainLink from "./PlainLink"; import InlineCode from "./InlineCode"; import PlainText from "./PlainText"; +import Table from "./Table"; export { CODE_BLOCK_REG } from "./CodeBlock"; export { TODO_LIST_REG } from "./TodoList"; @@ -21,8 +22,9 @@ export { TAG_REG } from "./Tag"; export { IMAGE_REG } from "./Image"; export { LINK_REG } from "./Link"; export { MARK_REG } from "./Mark"; +export { TABLE_REG } from "./Table"; // The order determines the order of execution. -export const blockElementParserList = [CodeBlock, TodoList, DoneList, OrderedList, UnorderedList, Paragraph]; +export const blockElementParserList = [Table, CodeBlock, TodoList, DoneList, OrderedList, UnorderedList, Paragraph]; export const inlineElementParserList = [Image, Mark, Bold, Emphasis, Link, InlineCode, PlainLink, Tag, PlainText]; export const parserList = [...blockElementParserList, ...inlineElementParserList]; diff --git a/web/src/less/memo-content.less b/web/src/less/memo-content.less index d5c4b0a1f..19b300f58 100644 --- a/web/src/less/memo-content.less +++ b/web/src/less/memo-content.less @@ -58,6 +58,18 @@ code { @apply bg-gray-100 px-1 rounded text-sm font-mono leading-6 inline-block; } + + table { + @apply my-1 table-auto border-collapse border border-gray-300; + + th { + @apply px-4 py-1 text-center border border-gray-300; + } + + td { + @apply px-4 py-1 text-center border border-gray-300; + } + } } > .expand-btn-container {