Skip to content

Commit cc7c309

Browse files
committed
feat: adds math block
1 parent 800f7d9 commit cc7c309

File tree

12 files changed

+7191
-8
lines changed

12 files changed

+7191
-8
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"playground": true,
3+
"docs": false,
4+
"author": "martinrsts",
5+
"tags": ["Blocks", "Custom Schemas"],
6+
"dependencies": {
7+
"katex": "^0.16.18",
8+
"react-icons": "^5.2.1"
9+
}
10+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import React, { useState, useRef, useEffect } from "react";
2+
import { BlockNoteSchema, defaultInlineContentSpecs,
3+
InlineContentFromConfig,
4+
PartialCustomInlineContentFromConfig,
5+
CustomInlineContentConfig, } from "@blocknote/core";
6+
import { useCreateBlockNote,
7+
createReactInlineContentSpec,
8+
useComponentsContext } from "@blocknote/react";
9+
import { BlockNoteView } from "@blocknote/ariakit";
10+
import katex from "katex";
11+
import { TbMathFunction } from "react-icons/tb";
12+
13+
const MathBlockSchema = {
14+
type: "math",
15+
propSchema: {
16+
rawContent: {
17+
default: "a_b = c",
18+
},
19+
},
20+
content: "none",
21+
} as const satisfies CustomInlineContentConfig;
22+
23+
const MathBlockComponent: React.FC<{
24+
inlineContent: InlineContentFromConfig<typeof MathBlockSchema, any>;
25+
updateInlineContent: (
26+
update: PartialCustomInlineContentFromConfig<typeof MathBlockSchema, any>
27+
) => void;
28+
}> = (props) => {
29+
const Components = useComponentsContext()!;
30+
const [isOpen, setIsOpen] = useState(false);
31+
const [inputValue, setInputValue] = useState(
32+
props.inlineContent.props.rawContent
33+
);
34+
const renderedRef = useRef<HTMLSpanElement>(null);
35+
36+
useEffect(() => {
37+
katex.render(props.inlineContent.props.rawContent, renderedRef.current!, {
38+
output: "mathml",
39+
throwOnError: false,
40+
});
41+
}, [props]);
42+
43+
const handleClick = () => {
44+
setIsOpen(!isOpen);
45+
};
46+
47+
const handleSubmit = () => {
48+
props.updateInlineContent({
49+
type: MathBlockSchema.type,
50+
props: { rawContent: inputValue },
51+
});
52+
setIsOpen(false);
53+
};
54+
55+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
56+
if (e.key === "Enter") {
57+
handleSubmit();
58+
} else if (e.key === "Escape") {
59+
setIsOpen(false);
60+
} else if (e.key === "click") {
61+
setIsOpen(false);
62+
}
63+
};
64+
65+
const handleRawContentChange = (e: React.ChangeEvent<HTMLInputElement>) => {
66+
setInputValue(e.target.value);
67+
};
68+
69+
return (
70+
<Components.Generic.Popover.Root opened={isOpen} position="bottom">
71+
<Components.Generic.Popover.Trigger>
72+
<span ref={renderedRef} onClick={handleClick} />
73+
</Components.Generic.Popover.Trigger>
74+
<Components.Generic.Popover.Content variant="form-popover">
75+
<Components.Generic.Form.Root>
76+
<Components.Generic.Form.TextInput
77+
className={"bn-text-input"}
78+
name="rawContent"
79+
icon={<TbMathFunction />}
80+
autoFocus={true}
81+
placeholder="Enter your TeX code here"
82+
value={inputValue}
83+
onKeyDown={handleKeyDown}
84+
onChange={handleRawContentChange}
85+
onSubmit={handleSubmit}
86+
/>
87+
</Components.Generic.Form.Root>
88+
</Components.Generic.Popover.Content>
89+
</Components.Generic.Popover.Root>
90+
);
91+
};
92+
93+
const MathBlock = createReactInlineContentSpec(MathBlockSchema, {
94+
render: MathBlockComponent,
95+
});
96+
97+
const schema = BlockNoteSchema.create({
98+
inlineContentSpecs: {
99+
...defaultInlineContentSpecs,
100+
math: MathBlock,
101+
},
102+
});
103+
104+
export default function App() {
105+
const editor = useCreateBlockNote({
106+
schema,
107+
initialContent: [
108+
{
109+
type: "paragraph",
110+
content: "Welcome to this demo!",
111+
},
112+
{
113+
type: "paragraph",
114+
content: [
115+
"You can add a math block by typing /math and pressing enter.",
116+
{
117+
type: "math",
118+
props: {
119+
rawContent: "a_b = c",
120+
},
121+
},
122+
],
123+
},
124+
],
125+
});
126+
127+
return <BlockNoteView editor={editor} />;
128+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Math Block
2+
3+
In this example, we create a custom `Math` block which is used to render formulas.
4+
5+
**Try it out:** Click on the formula to edit it.
6+
7+
**Relevant Docs:**
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<html lang="en">
2+
<head>
3+
<script>
4+
<!-- AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY -->
5+
</script>
6+
<meta charset="UTF-8" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
<title>Math Block</title>
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
<script type="module" src="./main.tsx"></script>
13+
</body>
14+
</html>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
2+
import React from "react";
3+
import { createRoot } from "react-dom/client";
4+
import App from "./App.jsx";
5+
6+
const root = createRoot(document.getElementById("root")!);
7+
root.render(
8+
<React.StrictMode>
9+
<App />
10+
</React.StrictMode>
11+
);

0 commit comments

Comments
 (0)