@synerise/ds-rich-text 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/LICENSE.md +21 -0
- package/README.md +173 -0
- package/dist/RichText.d.ts +4 -0
- package/dist/RichText.js +179 -0
- package/dist/RichText.styles.d.ts +31 -0
- package/dist/RichText.styles.js +87 -0
- package/dist/RichText.types.d.ts +139 -0
- package/dist/RichText.types.js +44 -0
- package/dist/blockModel/blockModel.types.d.ts +130 -0
- package/dist/blockModel/blockModel.types.js +1 -0
- package/dist/blockModel/index.d.ts +2 -0
- package/dist/blockModel/index.js +14 -0
- package/dist/blockModel/serializers/documentToHtml.d.ts +2 -0
- package/dist/blockModel/serializers/documentToHtml.js +101 -0
- package/dist/blockModel/serializers/documentToMarkdown.d.ts +2 -0
- package/dist/blockModel/serializers/documentToMarkdown.js +84 -0
- package/dist/blockModel/serializers/documentToTiptap.d.ts +2 -0
- package/dist/blockModel/serializers/documentToTiptap.js +133 -0
- package/dist/blockModel/serializers/htmlToDocument.d.ts +2 -0
- package/dist/blockModel/serializers/htmlToDocument.js +242 -0
- package/dist/blockModel/serializers/index.d.ts +6 -0
- package/dist/blockModel/serializers/index.js +14 -0
- package/dist/blockModel/serializers/markdownToDocument.d.ts +10 -0
- package/dist/blockModel/serializers/markdownToDocument.js +11 -0
- package/dist/blockModel/serializers/tiptapToDocument.d.ts +2 -0
- package/dist/blockModel/serializers/tiptapToDocument.js +189 -0
- package/dist/components/ImagePopover.d.ts +3 -0
- package/dist/components/ImagePopover.js +89 -0
- package/dist/components/LinkPopover.d.ts +3 -0
- package/dist/components/LinkPopover.js +57 -0
- package/dist/components/Toolbar.d.ts +3 -0
- package/dist/components/Toolbar.js +436 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +8 -0
- package/dist/extensions/CodeSnippet.d.ts +19 -0
- package/dist/extensions/CodeSnippet.js +76 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +52 -0
- package/dist/modules.d.js +1 -0
- package/dist/modules.d.ts +4 -0
- package/dist/utils/computeVisibleCount.d.ts +6 -0
- package/dist/utils/computeVisibleCount.js +19 -0
- package/dist/utils/getExtensions.d.ts +13 -0
- package/dist/utils/getExtensions.js +80 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/normalizeUrl.d.ts +7 -0
- package/dist/utils/normalizeUrl.js +13 -0
- package/dist/utils/sanitizeUrl.d.ts +9 -0
- package/dist/utils/sanitizeUrl.js +30 -0
- package/package.json +75 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const ALL_TOOLBAR_FEATURES = ["bold", "italic", "underline", "strikethrough", "heading", "bulletList", "orderedList", "link", "textAlign", "codeSnippet", "table", "image"];
|
|
2
|
+
const DEFAULT_HEADING_LEVELS = [1, 2, 3];
|
|
3
|
+
const DEFAULT_TEXTS = {
|
|
4
|
+
paragraph: "Paragraph",
|
|
5
|
+
bold: "Bold",
|
|
6
|
+
italic: "Italic",
|
|
7
|
+
underline: "Underline",
|
|
8
|
+
strikethrough: "Strikethrough",
|
|
9
|
+
heading: "Heading",
|
|
10
|
+
bulletList: "Bullet list",
|
|
11
|
+
orderedList: "Ordered list",
|
|
12
|
+
link: "Link",
|
|
13
|
+
align: "Align",
|
|
14
|
+
alignLeft: "Align left",
|
|
15
|
+
alignCenter: "Align center",
|
|
16
|
+
alignRight: "Align right",
|
|
17
|
+
codeBlock: "Code block",
|
|
18
|
+
codeSnippet: "Code snippet",
|
|
19
|
+
copySnippet: "Copy",
|
|
20
|
+
copiedSnippet: "Copied",
|
|
21
|
+
editWithAI: "Edit with AI",
|
|
22
|
+
more: "More",
|
|
23
|
+
table: "Table",
|
|
24
|
+
insertTable: "Insert table",
|
|
25
|
+
addRowBefore: "Add row above",
|
|
26
|
+
addRowAfter: "Add row below",
|
|
27
|
+
addColumnBefore: "Add column left",
|
|
28
|
+
addColumnAfter: "Add column right",
|
|
29
|
+
deleteRow: "Delete row",
|
|
30
|
+
deleteColumn: "Delete column",
|
|
31
|
+
deleteTable: "Delete table",
|
|
32
|
+
linkPlaceholder: "Enter URL...",
|
|
33
|
+
linkConfirm: "Apply",
|
|
34
|
+
linkRemove: "Remove link",
|
|
35
|
+
image: "Image",
|
|
36
|
+
imagePlaceholder: "Enter image URL...",
|
|
37
|
+
imageConfirm: "Insert",
|
|
38
|
+
imageUpload: "Upload file"
|
|
39
|
+
};
|
|
40
|
+
export {
|
|
41
|
+
ALL_TOOLBAR_FEATURES,
|
|
42
|
+
DEFAULT_HEADING_LEVELS,
|
|
43
|
+
DEFAULT_TEXTS
|
|
44
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export type TextAlign = 'left' | 'center' | 'right';
|
|
2
|
+
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
3
|
+
export type BoldMark = {
|
|
4
|
+
type: 'bold';
|
|
5
|
+
};
|
|
6
|
+
export type ItalicMark = {
|
|
7
|
+
type: 'italic';
|
|
8
|
+
};
|
|
9
|
+
export type UnderlineMark = {
|
|
10
|
+
type: 'underline';
|
|
11
|
+
};
|
|
12
|
+
export type StrikeMark = {
|
|
13
|
+
type: 'strike';
|
|
14
|
+
};
|
|
15
|
+
export type CodeMark = {
|
|
16
|
+
type: 'code';
|
|
17
|
+
};
|
|
18
|
+
export type LinkMark = {
|
|
19
|
+
type: 'link';
|
|
20
|
+
href: string;
|
|
21
|
+
target?: string;
|
|
22
|
+
rel?: string;
|
|
23
|
+
};
|
|
24
|
+
export type Mark = BoldMark | ItalicMark | UnderlineMark | StrikeMark | CodeMark | LinkMark;
|
|
25
|
+
export type TextNode = {
|
|
26
|
+
type: 'text';
|
|
27
|
+
text: string;
|
|
28
|
+
marks?: Mark[];
|
|
29
|
+
};
|
|
30
|
+
export type HardBreakNode = {
|
|
31
|
+
type: 'hardBreak';
|
|
32
|
+
};
|
|
33
|
+
export type InlineNode = TextNode | HardBreakNode;
|
|
34
|
+
export type ParagraphBlock = {
|
|
35
|
+
type: 'paragraph';
|
|
36
|
+
attrs?: {
|
|
37
|
+
textAlign?: TextAlign;
|
|
38
|
+
};
|
|
39
|
+
children: InlineNode[];
|
|
40
|
+
};
|
|
41
|
+
export type HeadingBlock = {
|
|
42
|
+
type: 'heading';
|
|
43
|
+
attrs: {
|
|
44
|
+
level: HeadingLevel;
|
|
45
|
+
textAlign?: TextAlign;
|
|
46
|
+
};
|
|
47
|
+
children: InlineNode[];
|
|
48
|
+
};
|
|
49
|
+
export type CodeBlockBlock = {
|
|
50
|
+
type: 'codeBlock';
|
|
51
|
+
attrs?: {
|
|
52
|
+
language?: string;
|
|
53
|
+
};
|
|
54
|
+
children: InlineNode[];
|
|
55
|
+
};
|
|
56
|
+
export type CodeSnippetBlock = {
|
|
57
|
+
type: 'codeSnippet';
|
|
58
|
+
children: InlineNode[];
|
|
59
|
+
};
|
|
60
|
+
export type ImageBlock = {
|
|
61
|
+
type: 'image';
|
|
62
|
+
attrs: {
|
|
63
|
+
src: string;
|
|
64
|
+
alt?: string;
|
|
65
|
+
title?: string;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
export type ListItemBlock = {
|
|
69
|
+
type: 'listItem';
|
|
70
|
+
children: Block[];
|
|
71
|
+
};
|
|
72
|
+
export type BulletListBlock = {
|
|
73
|
+
type: 'bulletList';
|
|
74
|
+
children: ListItemBlock[];
|
|
75
|
+
};
|
|
76
|
+
export type OrderedListBlock = {
|
|
77
|
+
type: 'orderedList';
|
|
78
|
+
attrs?: {
|
|
79
|
+
start?: number;
|
|
80
|
+
};
|
|
81
|
+
children: ListItemBlock[];
|
|
82
|
+
};
|
|
83
|
+
export type BlockquoteBlock = {
|
|
84
|
+
type: 'blockquote';
|
|
85
|
+
children: Block[];
|
|
86
|
+
};
|
|
87
|
+
export type HorizontalRuleBlock = {
|
|
88
|
+
type: 'horizontalRule';
|
|
89
|
+
};
|
|
90
|
+
export type TableCellBlock = {
|
|
91
|
+
type: 'tableCell';
|
|
92
|
+
attrs?: {
|
|
93
|
+
colspan?: number;
|
|
94
|
+
rowspan?: number;
|
|
95
|
+
};
|
|
96
|
+
children: Block[];
|
|
97
|
+
};
|
|
98
|
+
export type TableHeaderBlock = {
|
|
99
|
+
type: 'tableHeader';
|
|
100
|
+
attrs?: {
|
|
101
|
+
colspan?: number;
|
|
102
|
+
rowspan?: number;
|
|
103
|
+
};
|
|
104
|
+
children: Block[];
|
|
105
|
+
};
|
|
106
|
+
export type TableRowBlock = {
|
|
107
|
+
type: 'tableRow';
|
|
108
|
+
children: (TableCellBlock | TableHeaderBlock)[];
|
|
109
|
+
};
|
|
110
|
+
export type TableBlock = {
|
|
111
|
+
type: 'table';
|
|
112
|
+
children: TableRowBlock[];
|
|
113
|
+
};
|
|
114
|
+
export type Block = ParagraphBlock | HeadingBlock | CodeBlockBlock | CodeSnippetBlock | ImageBlock | BulletListBlock | OrderedListBlock | BlockquoteBlock | HorizontalRuleBlock | TableBlock;
|
|
115
|
+
export type RichTextDocument = {
|
|
116
|
+
type: 'doc';
|
|
117
|
+
version: 1;
|
|
118
|
+
children: Block[];
|
|
119
|
+
};
|
|
120
|
+
export type PMNode = {
|
|
121
|
+
type: string;
|
|
122
|
+
attrs?: Record<string, unknown>;
|
|
123
|
+
content?: PMNode[];
|
|
124
|
+
marks?: PMMark[];
|
|
125
|
+
text?: string;
|
|
126
|
+
};
|
|
127
|
+
export type PMMark = {
|
|
128
|
+
type: string;
|
|
129
|
+
attrs?: Record<string, unknown>;
|
|
130
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export type { TextAlign, HeadingLevel, BoldMark, ItalicMark, UnderlineMark, StrikeMark, CodeMark, LinkMark, Mark, TextNode, HardBreakNode, InlineNode, ParagraphBlock, HeadingBlock, CodeBlockBlock, ImageBlock, ListItemBlock, BulletListBlock, OrderedListBlock, BlockquoteBlock, HorizontalRuleBlock, TableCellBlock, TableHeaderBlock, TableRowBlock, TableBlock, Block, RichTextDocument, PMNode, PMMark, } from './blockModel.types';
|
|
2
|
+
export { tiptapJsonToDocument, documentToTiptapJson, documentToHtml, documentToMarkdown, markdownToDocument, htmlToDocument, } from './serializers';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { tiptapJsonToDocument } from "./serializers/tiptapToDocument.js";
|
|
2
|
+
import { documentToTiptapJson } from "./serializers/documentToTiptap.js";
|
|
3
|
+
import { documentToHtml } from "./serializers/documentToHtml.js";
|
|
4
|
+
import { documentToMarkdown } from "./serializers/documentToMarkdown.js";
|
|
5
|
+
import { markdownToDocument } from "./serializers/markdownToDocument.js";
|
|
6
|
+
import { htmlToDocument } from "./serializers/htmlToDocument.js";
|
|
7
|
+
export {
|
|
8
|
+
documentToHtml,
|
|
9
|
+
documentToMarkdown,
|
|
10
|
+
documentToTiptapJson,
|
|
11
|
+
htmlToDocument,
|
|
12
|
+
markdownToDocument,
|
|
13
|
+
tiptapJsonToDocument
|
|
14
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { sanitizeUrl } from "../../utils/sanitizeUrl.js";
|
|
2
|
+
const escapeHtml = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3
|
+
const serializeMark = (mark, inner) => {
|
|
4
|
+
switch (mark.type) {
|
|
5
|
+
case "bold":
|
|
6
|
+
return `<strong>${inner}</strong>`;
|
|
7
|
+
case "italic":
|
|
8
|
+
return `<em>${inner}</em>`;
|
|
9
|
+
case "underline":
|
|
10
|
+
return `<u>${inner}</u>`;
|
|
11
|
+
case "strike":
|
|
12
|
+
return `<s>${inner}</s>`;
|
|
13
|
+
case "code":
|
|
14
|
+
return `<code>${inner}</code>`;
|
|
15
|
+
case "link": {
|
|
16
|
+
const attrs = [`href="${escapeHtml(sanitizeUrl(mark.href))}"`];
|
|
17
|
+
if (mark.target) {
|
|
18
|
+
attrs.push(`target="${escapeHtml(mark.target)}"`);
|
|
19
|
+
}
|
|
20
|
+
if (mark.rel) {
|
|
21
|
+
attrs.push(`rel="${escapeHtml(mark.rel)}"`);
|
|
22
|
+
}
|
|
23
|
+
return `<a ${attrs.join(" ")}>${inner}</a>`;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const serializeInline = (node) => {
|
|
28
|
+
if (node.type === "hardBreak") {
|
|
29
|
+
return "<br>";
|
|
30
|
+
}
|
|
31
|
+
let html = escapeHtml(node.text);
|
|
32
|
+
if (node.marks) {
|
|
33
|
+
for (const mark of node.marks) {
|
|
34
|
+
html = serializeMark(mark, html);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return html;
|
|
38
|
+
};
|
|
39
|
+
const alignAttr = (textAlign) => textAlign ? ` style="text-align: ${textAlign}"` : "";
|
|
40
|
+
const serializeBlock = (block) => {
|
|
41
|
+
switch (block.type) {
|
|
42
|
+
case "paragraph": {
|
|
43
|
+
const inner = block.children.map(serializeInline).join("");
|
|
44
|
+
return `<p${alignAttr(block.attrs?.textAlign)}>${inner}</p>`;
|
|
45
|
+
}
|
|
46
|
+
case "heading": {
|
|
47
|
+
const tag = `h${block.attrs.level}`;
|
|
48
|
+
const inner = block.children.map(serializeInline).join("");
|
|
49
|
+
return `<${tag}${alignAttr(block.attrs.textAlign)}>${inner}</${tag}>`;
|
|
50
|
+
}
|
|
51
|
+
case "codeBlock": {
|
|
52
|
+
const inner = block.children.map(serializeInline).join("");
|
|
53
|
+
return `<pre><code>${inner}</code></pre>`;
|
|
54
|
+
}
|
|
55
|
+
case "codeSnippet": {
|
|
56
|
+
const inner = block.children.map(serializeInline).join("");
|
|
57
|
+
return `<pre data-type="code-snippet"><code>${inner}</code></pre>`;
|
|
58
|
+
}
|
|
59
|
+
case "image": {
|
|
60
|
+
const attrs = [`src="${escapeHtml(block.attrs.src)}"`];
|
|
61
|
+
if (block.attrs.alt) {
|
|
62
|
+
attrs.push(`alt="${escapeHtml(block.attrs.alt)}"`);
|
|
63
|
+
}
|
|
64
|
+
if (block.attrs.title) {
|
|
65
|
+
attrs.push(`title="${escapeHtml(block.attrs.title)}"`);
|
|
66
|
+
}
|
|
67
|
+
return `<img ${attrs.join(" ")}>`;
|
|
68
|
+
}
|
|
69
|
+
case "bulletList":
|
|
70
|
+
return `<ul>${block.children.map((li) => `<li>${li.children.map(serializeBlock).join("")}</li>`).join("")}</ul>`;
|
|
71
|
+
case "orderedList": {
|
|
72
|
+
const startAttr = block.attrs?.start && block.attrs.start !== 1 ? ` start="${block.attrs.start}"` : "";
|
|
73
|
+
return `<ol${startAttr}>${block.children.map((li) => `<li>${li.children.map(serializeBlock).join("")}</li>`).join("")}</ol>`;
|
|
74
|
+
}
|
|
75
|
+
case "blockquote":
|
|
76
|
+
return `<blockquote>${block.children.map(serializeBlock).join("")}</blockquote>`;
|
|
77
|
+
case "horizontalRule":
|
|
78
|
+
return "<hr>";
|
|
79
|
+
case "table":
|
|
80
|
+
return `<table><tbody>${block.children.map(serializeRow).join("")}</tbody></table>`;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const spanAttrs = (cell) => {
|
|
84
|
+
const attrs = [];
|
|
85
|
+
if (cell.attrs?.colspan && cell.attrs.colspan !== 1) {
|
|
86
|
+
attrs.push(`colspan="${cell.attrs.colspan}"`);
|
|
87
|
+
}
|
|
88
|
+
if (cell.attrs?.rowspan && cell.attrs.rowspan !== 1) {
|
|
89
|
+
attrs.push(`rowspan="${cell.attrs.rowspan}"`);
|
|
90
|
+
}
|
|
91
|
+
return attrs.length ? ` ${attrs.join(" ")}` : "";
|
|
92
|
+
};
|
|
93
|
+
const serializeCell = (cell) => {
|
|
94
|
+
const tag = cell.type === "tableHeader" ? "th" : "td";
|
|
95
|
+
return `<${tag}${spanAttrs(cell)}>${cell.children.map(serializeBlock).join("")}</${tag}>`;
|
|
96
|
+
};
|
|
97
|
+
const serializeRow = (row) => `<tr>${row.children.map(serializeCell).join("")}</tr>`;
|
|
98
|
+
const documentToHtml = (doc) => doc.children.map(serializeBlock).join("");
|
|
99
|
+
export {
|
|
100
|
+
documentToHtml
|
|
101
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const serializeMark = (mark, inner) => {
|
|
2
|
+
switch (mark.type) {
|
|
3
|
+
case "bold":
|
|
4
|
+
return `**${inner}**`;
|
|
5
|
+
case "italic":
|
|
6
|
+
return `*${inner}*`;
|
|
7
|
+
case "underline":
|
|
8
|
+
return `<u>${inner}</u>`;
|
|
9
|
+
case "strike":
|
|
10
|
+
return `~~${inner}~~`;
|
|
11
|
+
case "code":
|
|
12
|
+
return `\`${inner}\``;
|
|
13
|
+
case "link":
|
|
14
|
+
return `[${inner}](${mark.href})`;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const serializeInline = (node) => {
|
|
18
|
+
if (node.type === "hardBreak") {
|
|
19
|
+
return " \n";
|
|
20
|
+
}
|
|
21
|
+
let text = node.text;
|
|
22
|
+
if (node.marks) {
|
|
23
|
+
for (const mark of node.marks) {
|
|
24
|
+
text = serializeMark(mark, text);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return text;
|
|
28
|
+
};
|
|
29
|
+
const serializeInlines = (children) => children.map(serializeInline).join("");
|
|
30
|
+
const indentLines = (text, indent) => text.split("\n").map((line) => line ? indent + line : line).join("\n");
|
|
31
|
+
const serializeListItem = (item, marker, indent) => {
|
|
32
|
+
const [first, ...rest] = item.children;
|
|
33
|
+
const firstText = first ? serializeBlock(first) : "";
|
|
34
|
+
const continuation = indent + " ".repeat(marker.length);
|
|
35
|
+
let line = `${indent}${marker}${firstText}`;
|
|
36
|
+
for (const child of rest) {
|
|
37
|
+
line += `
|
|
38
|
+
${indentLines(serializeBlock(child), continuation)}`;
|
|
39
|
+
}
|
|
40
|
+
return line;
|
|
41
|
+
};
|
|
42
|
+
const serializeBlock = (block, indent = "") => {
|
|
43
|
+
switch (block.type) {
|
|
44
|
+
case "paragraph":
|
|
45
|
+
return serializeInlines(block.children);
|
|
46
|
+
case "heading":
|
|
47
|
+
return `${"#".repeat(block.attrs.level)} ${serializeInlines(block.children)}`;
|
|
48
|
+
case "codeBlock":
|
|
49
|
+
return `\`\`\`
|
|
50
|
+
${serializeInlines(block.children)}
|
|
51
|
+
\`\`\``;
|
|
52
|
+
case "codeSnippet":
|
|
53
|
+
return `\`${serializeInlines(block.children)}\``;
|
|
54
|
+
case "image":
|
|
55
|
+
return ``;
|
|
56
|
+
case "bulletList":
|
|
57
|
+
return block.children.map((li) => serializeListItem(li, "- ", indent)).join("\n");
|
|
58
|
+
case "orderedList": {
|
|
59
|
+
const start = block.attrs?.start ?? 1;
|
|
60
|
+
return block.children.map((li, i) => serializeListItem(li, `${start + i}. `, indent)).join("\n");
|
|
61
|
+
}
|
|
62
|
+
case "blockquote":
|
|
63
|
+
return block.children.map((child) => indentLines(serializeBlock(child), "> ")).join("\n>\n");
|
|
64
|
+
case "horizontalRule":
|
|
65
|
+
return "---";
|
|
66
|
+
case "table": {
|
|
67
|
+
const rows = block.children;
|
|
68
|
+
if (rows.length === 0) {
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
const renderRow = (cells) => `| ${cells.map(cellToMarkdown).join(" | ")} |`;
|
|
72
|
+
const colCount = rows[0].children.length;
|
|
73
|
+
const header = renderRow(rows[0].children);
|
|
74
|
+
const separator = `| ${Array(colCount).fill("---").join(" | ")} |`;
|
|
75
|
+
const body = rows.slice(1).map((row) => renderRow(row.children));
|
|
76
|
+
return [header, separator, ...body].join("\n");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const cellToMarkdown = (cell) => cell.children.map((child) => serializeBlock(child)).join(" ").replace(/\n+/g, " ").replace(/\|/g, "\\|").trim();
|
|
81
|
+
const documentToMarkdown = (doc) => doc.children.map((block) => serializeBlock(block)).join("\n\n");
|
|
82
|
+
export {
|
|
83
|
+
documentToMarkdown
|
|
84
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const convertMark = (mark) => {
|
|
2
|
+
if (mark.type === "link") {
|
|
3
|
+
return {
|
|
4
|
+
type: "link",
|
|
5
|
+
attrs: {
|
|
6
|
+
href: mark.href,
|
|
7
|
+
...mark.target && {
|
|
8
|
+
target: mark.target
|
|
9
|
+
},
|
|
10
|
+
...mark.rel && {
|
|
11
|
+
rel: mark.rel
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
type: mark.type
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const convertInlineNode = (node) => {
|
|
21
|
+
if (node.type === "hardBreak") {
|
|
22
|
+
return {
|
|
23
|
+
type: "hardBreak"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
type: "text",
|
|
28
|
+
text: node.text,
|
|
29
|
+
...node.marks?.length && {
|
|
30
|
+
marks: node.marks.map(convertMark)
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
const convertBlock = (block) => {
|
|
35
|
+
switch (block.type) {
|
|
36
|
+
case "paragraph":
|
|
37
|
+
return {
|
|
38
|
+
type: "paragraph",
|
|
39
|
+
...block.attrs?.textAlign && {
|
|
40
|
+
attrs: {
|
|
41
|
+
textAlign: block.attrs.textAlign
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
content: block.children.map(convertInlineNode)
|
|
45
|
+
};
|
|
46
|
+
case "heading":
|
|
47
|
+
return {
|
|
48
|
+
type: "heading",
|
|
49
|
+
attrs: {
|
|
50
|
+
level: block.attrs.level,
|
|
51
|
+
...block.attrs.textAlign && {
|
|
52
|
+
textAlign: block.attrs.textAlign
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
content: block.children.map(convertInlineNode)
|
|
56
|
+
};
|
|
57
|
+
case "codeBlock":
|
|
58
|
+
return {
|
|
59
|
+
type: "codeBlock",
|
|
60
|
+
...block.attrs?.language && {
|
|
61
|
+
attrs: {
|
|
62
|
+
language: block.attrs.language
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
content: block.children.map(convertInlineNode)
|
|
66
|
+
};
|
|
67
|
+
case "codeSnippet":
|
|
68
|
+
return {
|
|
69
|
+
type: "codeSnippet",
|
|
70
|
+
content: block.children.map(convertInlineNode)
|
|
71
|
+
};
|
|
72
|
+
case "image":
|
|
73
|
+
return {
|
|
74
|
+
type: "image",
|
|
75
|
+
attrs: {
|
|
76
|
+
...block.attrs
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
case "bulletList":
|
|
80
|
+
return {
|
|
81
|
+
type: "bulletList",
|
|
82
|
+
content: block.children.map((li) => ({
|
|
83
|
+
type: "listItem",
|
|
84
|
+
content: li.children.map(convertBlock)
|
|
85
|
+
}))
|
|
86
|
+
};
|
|
87
|
+
case "orderedList":
|
|
88
|
+
return {
|
|
89
|
+
type: "orderedList",
|
|
90
|
+
...block.attrs?.start && {
|
|
91
|
+
attrs: {
|
|
92
|
+
start: block.attrs.start
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
content: block.children.map((li) => ({
|
|
96
|
+
type: "listItem",
|
|
97
|
+
content: li.children.map(convertBlock)
|
|
98
|
+
}))
|
|
99
|
+
};
|
|
100
|
+
case "blockquote":
|
|
101
|
+
return {
|
|
102
|
+
type: "blockquote",
|
|
103
|
+
content: block.children.map(convertBlock)
|
|
104
|
+
};
|
|
105
|
+
case "horizontalRule":
|
|
106
|
+
return {
|
|
107
|
+
type: "horizontalRule"
|
|
108
|
+
};
|
|
109
|
+
case "table":
|
|
110
|
+
return {
|
|
111
|
+
type: "table",
|
|
112
|
+
content: block.children.map((row) => ({
|
|
113
|
+
type: "tableRow",
|
|
114
|
+
content: row.children.map((cell) => ({
|
|
115
|
+
type: cell.type,
|
|
116
|
+
...cell.attrs && {
|
|
117
|
+
attrs: {
|
|
118
|
+
...cell.attrs
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
content: cell.children.map(convertBlock)
|
|
122
|
+
}))
|
|
123
|
+
}))
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const documentToTiptapJson = (doc) => ({
|
|
128
|
+
type: "doc",
|
|
129
|
+
content: doc.children.map(convertBlock)
|
|
130
|
+
});
|
|
131
|
+
export {
|
|
132
|
+
documentToTiptapJson
|
|
133
|
+
};
|