mirror of
https://github.com/usememos/memos.git
synced 2024-12-18 16:41:44 +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 PlainText from "./PlainText";
|
||||
import Strikethrough from "./Strikethrough";
|
||||
import Table from "./Table";
|
||||
import Tag from "./Tag";
|
||||
import TodoList from "./TodoList";
|
||||
import UnorderedList from "./UnorderedList";
|
||||
@ -31,6 +32,7 @@ export const blockElementParserList = [
|
||||
Br,
|
||||
CodeBlock,
|
||||
Blockquote,
|
||||
Table,
|
||||
Heading,
|
||||
TodoList,
|
||||
DoneList,
|
||||
|
Loading…
Reference in New Issue
Block a user