mirror of
https://github.com/usememos/memos.git
synced 2024-12-19 00:51:30 +03:00
feat: tables support (#2573)
* Tables support * Linter fixes * Regex Redos fix * Fix empty row and variables proper naming * Default cell style * Now unncessary * Support rows without a starting pipe char * Striped rows * Fix parsing issues * Support tabs in separators
This commit is contained in:
parent
ed190cd41e
commit
787cf2a9fe
81
web/src/labs/marked/parser/Table.tsx
Normal file
81
web/src/labs/marked/parser/Table.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { CSSProperties } from "react";
|
||||||
|
import { inlineElementParserList } from ".";
|
||||||
|
import { marked } from "..";
|
||||||
|
import { matcher } from "../matcher";
|
||||||
|
|
||||||
|
class TableRegExp extends RegExp {
|
||||||
|
[Symbol.match](str: string): RegExpMatchArray | null {
|
||||||
|
const result = RegExp.prototype[Symbol.match].call(this, str);
|
||||||
|
// regex will only be considered valid if headers and delimiters column count matches
|
||||||
|
if (!result || splitPipeDelimiter(result[1]).length != splitPipeDelimiter(result[2]).length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TABLE_REG = new TableRegExp(/^([^\n|]*\|[^\n]*)\n([ \t:-]*(?<!\\)\|[ \t:|-]*)((?:\n[^\n|]*\|[^\n]*)+)/);
|
||||||
|
|
||||||
|
const splitPipeDelimiter = (rawStr: string) => {
|
||||||
|
// loose pipe delimiter for markdown tables. escaped pipes are supported. some examples:
|
||||||
|
// | aaaa | bbbb | cc\|cc | => ["aaaa", "bbbb", "cc|cc"]
|
||||||
|
// aaaa | bbbb | cc\|cc => ["aaaa", "bbbb", "cc|cc"]
|
||||||
|
// |a|f => ["a", "f"]
|
||||||
|
// ||a|f| => ["", "a", "f"]
|
||||||
|
// |||| => ["", "", ""]
|
||||||
|
// |\||\||\|| => ["|", "|", "|"]
|
||||||
|
return (
|
||||||
|
rawStr
|
||||||
|
.replaceAll(/(?<!\\)\|/g, "| ")
|
||||||
|
.trim()
|
||||||
|
.match(/(?:\\\||[^|])+/g) || []
|
||||||
|
).map((cell) => cell.replaceAll("\\|", "|").trim());
|
||||||
|
// TODO: Need to move backslash escaping (to PlainText ?) for all characters
|
||||||
|
// described in markdown spec (\`*_{}[]()#+-.!), and not just the pipe symbol here
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderer = (rawStr: string) => {
|
||||||
|
const matchResult = matcher(rawStr, TABLE_REG);
|
||||||
|
if (!matchResult) {
|
||||||
|
return rawStr;
|
||||||
|
}
|
||||||
|
const headerContents = splitPipeDelimiter(matchResult[1]);
|
||||||
|
const cellStyles: CSSProperties[] = splitPipeDelimiter(matchResult[2]).map((cell) => {
|
||||||
|
const left = cell.startsWith(":");
|
||||||
|
const right = cell.endsWith(":");
|
||||||
|
// github markdown spec says that by default, content is left aligned
|
||||||
|
return {
|
||||||
|
textAlign: left && right ? "center" : right ? "right" : "left",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const rowContents = matchResult[3].substring(1).split(/\r?\n/).map(splitPipeDelimiter);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{headerContents.map((header, index) => (
|
||||||
|
<th key={index}>{marked(header, [], inlineElementParserList)}</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{rowContents.map((row, rowIndex) => (
|
||||||
|
<tr key={rowIndex} className="dark:even:bg-zinc-600 even:bg-zinc-100">
|
||||||
|
{headerContents.map((_, cellIndex) => (
|
||||||
|
<td key={cellIndex} style={cellStyles[cellIndex]}>
|
||||||
|
{cellIndex < row.length ? marked(row[cellIndex], [], inlineElementParserList) : null}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "table",
|
||||||
|
regexp: TABLE_REG,
|
||||||
|
renderer,
|
||||||
|
};
|
@ -17,6 +17,7 @@ import Paragraph from "./Paragraph";
|
|||||||
import PlainLink from "./PlainLink";
|
import PlainLink from "./PlainLink";
|
||||||
import PlainText from "./PlainText";
|
import PlainText from "./PlainText";
|
||||||
import Strikethrough from "./Strikethrough";
|
import Strikethrough from "./Strikethrough";
|
||||||
|
import Table from "./Table";
|
||||||
import Tag from "./Tag";
|
import Tag from "./Tag";
|
||||||
import TodoList from "./TodoList";
|
import TodoList from "./TodoList";
|
||||||
import UnorderedList from "./UnorderedList";
|
import UnorderedList from "./UnorderedList";
|
||||||
@ -31,6 +32,7 @@ export const blockElementParserList = [
|
|||||||
Br,
|
Br,
|
||||||
CodeBlock,
|
CodeBlock,
|
||||||
Blockquote,
|
Blockquote,
|
||||||
|
Table,
|
||||||
Heading,
|
Heading,
|
||||||
TodoList,
|
TodoList,
|
||||||
DoneList,
|
DoneList,
|
||||||
|
Loading…
Reference in New Issue
Block a user