Skip to content

Commit 9c27968

Browse files
Add table to editor (#507)
1 parent 25f4145 commit 9c27968

File tree

7 files changed

+177
-17
lines changed

7 files changed

+177
-17
lines changed
+11-14
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
1-
import React, { useMemo } from "react";
2-
import DOMPurify, { Config } from "dompurify";
3-
import { generateHTML } from "@tiptap/core";
4-
import { TiptapExtensions } from "./extensions";
1+
import React from "react";
2+
import { CustomCodeBlockReadOnly, TiptapExtensions } from "./extensions";
3+
import { EditorContent, useEditor } from "@tiptap/react";
54

65
interface RenderPostProps {
76
json: string;
87
}
98

10-
const config: Config = {
11-
ADD_TAGS: ["iframe"],
12-
ADD_ATTR: ["allowfullscreen", "target"],
13-
};
14-
159
const RenderPost = ({ json }: RenderPostProps) => {
16-
const sanitizedHTML = useMemo(() => {
17-
const rawHTML = generateHTML(JSON.parse(json), [...TiptapExtensions]);
18-
return DOMPurify.sanitize(rawHTML, config) as string;
19-
}, [json]);
10+
const content = JSON.parse(json);
11+
12+
const editor = useEditor({
13+
editable: false,
14+
extensions: [...TiptapExtensions, CustomCodeBlockReadOnly],
15+
content,
16+
});
2017

21-
return <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />;
18+
return <EditorContent editor={editor} />;
2219
};
2320

2421
export default RenderPost;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* For styling codeblock theme */
2+
.code-block div {
3+
background-color: black;
4+
color: white;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { NodeViewContent, NodeViewProps, NodeViewWrapper } from "@tiptap/react";
2+
import { ChangeEvent, FunctionComponent } from "react";
3+
4+
import styles from "./CodeBlock.module.css";
5+
6+
interface CodeBlockProps {
7+
readOnly: boolean;
8+
}
9+
10+
const CodeBlock: FunctionComponent<NodeViewProps & CodeBlockProps> = ({
11+
node: {
12+
attrs: { language: defaultLanguage },
13+
},
14+
updateAttributes,
15+
extension,
16+
readOnly,
17+
}) => {
18+
return (
19+
<NodeViewWrapper className={styles["code-block"]}>
20+
{/* Read only mode removes selector ability dropdown - more elegant solution potentially possible */}
21+
{!readOnly && (
22+
<select
23+
onClick={(e) => e.stopPropagation()}
24+
onMouseDown={(e) => e.stopPropagation()}
25+
contentEditable={false}
26+
defaultValue={defaultLanguage}
27+
onChange={(event: ChangeEvent<HTMLSelectElement>) => {
28+
updateAttributes({ language: event.target.value });
29+
}}
30+
>
31+
<option value="null">auto</option>
32+
<option disabled></option>
33+
{extension.options.lowlight
34+
.listLanguages()
35+
.map((lang: string, index: number) => (
36+
<option key={index} value={lang}>
37+
{lang}
38+
</option>
39+
))}
40+
</select>
41+
)}
42+
<pre>
43+
<NodeViewContent as="code" />
44+
</pre>
45+
</NodeViewWrapper>
46+
);
47+
};
48+
49+
export default CodeBlock;

components/editor/editor/extensions/index.tsx

+27
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { InputRule } from "@tiptap/core";
1515
// import UploadImagesPlugin from "@/components/editor/editor/plugins/upload-images";
1616
import UpdatedImage from "./updated-image";
1717
import Document from "@tiptap/extension-document";
18+
import Paragraph from "@tiptap/extension-paragraph";
19+
import Text from "@tiptap/extension-text";
1820
import TextAlign from "@tiptap/extension-text-align";
1921
import Subscript from "@tiptap/extension-subscript";
2022
import Superscript from "@tiptap/extension-superscript";
@@ -27,6 +29,14 @@ import TableHeader from "@tiptap/extension-table-header";
2729
import TableRow from "@tiptap/extension-table-row";
2830
import { ReactNodeViewRenderer, NodeViewProps } from "@tiptap/react";
2931
import CustomTableNodeView from "../components/Table/CustomTableNodeView";
32+
import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
33+
34+
import { lowlight } from "lowlight";
35+
36+
// Highlight syntax select your style from here (https://highlightjs.org/examples)
37+
import "highlight.js/styles/monokai-sublime.css";
38+
39+
import CodeBlock from "../components/CodeBlock/CodeBlock";
3040

3141
// const CustomImage = TiptapImage.extend({
3242
// addProseMirrorPlugins() {
@@ -38,6 +48,21 @@ const CustomDocument = Document.extend({
3848
content: "heading block*",
3949
});
4050

51+
export const CustomCodeBlockEdit = CodeBlockLowlight.extend({
52+
addNodeView() {
53+
return ReactNodeViewRenderer(CodeBlock);
54+
},
55+
}).configure({ lowlight });
56+
57+
// Two CodeBlockNodes need to be created to disable selector menu
58+
export const CustomCodeBlockReadOnly = CodeBlockLowlight.extend({
59+
addNodeView() {
60+
return ReactNodeViewRenderer((props: NodeViewProps) => (
61+
<CodeBlock {...props} readOnly />
62+
));
63+
},
64+
}).configure({ lowlight });
65+
4166
export const CustomTable = Table.extend({
4267
addNodeView() {
4368
return ReactNodeViewRenderer((props: NodeViewProps) => (
@@ -70,6 +95,8 @@ export const TiptapExtensions = [
7095
"text-neutral-900 bg-neutral-100 border border-neutral-500 flex-1 flex p-1",
7196
},
7297
}),
98+
Paragraph,
99+
Text,
73100
StarterKit.configure({
74101
document: false,
75102
bulletList: {

components/editor/editor/index.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22

33
import { useEditor, EditorContent } from "@tiptap/react";
44
import { TiptapEditorProps } from "./props";
5-
import { CustomTableRow, TiptapExtensions } from "./extensions";
5+
import { CustomCodeBlockEdit, TiptapExtensions } from "./extensions";
66
import { EditorBubbleMenu } from "./components/bubble-menu";
77
import { MediaResizer } from "./components/image-resizer";
88
import Toolbar from "./components/Toolbar/Toolbar";
9-
import TableMenuBar from "./components/Table/TableMenuBar";
109

1110
interface EditorProps {
1211
initialValue: string;
@@ -15,7 +14,7 @@ interface EditorProps {
1514

1615
export default function Editor({ onChange, initialValue }: EditorProps) {
1716
const editor = useEditor({
18-
extensions: TiptapExtensions,
17+
extensions: [...TiptapExtensions, CustomCodeBlockEdit],
1918
editorProps: TiptapEditorProps,
2019
content: JSON.parse(initialValue),
2120
onUpdate: (e) => {

package-lock.json

+80
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@styled-icons/material": "^10.47.0",
4040
"@tailwindcss/forms": "^0.5.6",
4141
"@tanstack/react-query": "^4.35.0",
42+
"@tiptap/extension-code-block-lowlight": "^2.1.11",
4243
"@tiptap/extension-color": "^2.1.6",
4344
"@tiptap/extension-highlight": "^2.1.6",
4445
"@tiptap/extension-image": "^2.1.11",
@@ -70,6 +71,8 @@
7071
"dompurify": "^3.0.5",
7172
"fathom-client": "^3.5.0",
7273
"framer-motion": "^10.16.1",
74+
"highlight.js": "^11.9.0",
75+
"lowlight": "^2.4.0",
7376
"lucide-react": "^0.292.0",
7477
"nanoid": "^4.0.2",
7578
"next": "^13.4.19",

0 commit comments

Comments
 (0)