mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-23 19:49:56 +01:00
editor: improve math block node
* fix exit on arrow up * allow entering the node block via up/down * live math rendering Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
This commit is contained in:
@@ -22,7 +22,7 @@ import { useEffect, useRef } from "react";
|
||||
import { Button } from "../../components/button";
|
||||
import { useTimer } from "../../hooks/use-timer";
|
||||
import { SelectionBasedReactNodeViewProps } from "../react/types";
|
||||
import { MathBlock, MathBlockAttributes } from "./math-block";
|
||||
import { MathBlockAttributes } from "./math-block";
|
||||
import { loadKatex } from "./plugin/renderers/katex";
|
||||
import { useThemeEngineStore } from "@notesnook/theme";
|
||||
import SimpleBar from "simplebar-react";
|
||||
@@ -32,34 +32,37 @@ export function MathBlockComponent(
|
||||
props: SelectionBasedReactNodeViewProps<MathBlockAttributes>
|
||||
) {
|
||||
const { editor, node, forwardRef, getPos } = props;
|
||||
const { indentLength, indentType, caretPosition } = node.attrs;
|
||||
const { caretPosition } = node.attrs;
|
||||
|
||||
const isActive = editor.isActive(MathBlock.name);
|
||||
const elementRef = useRef<HTMLElement>();
|
||||
const codeElementRef = useRef<HTMLElement>();
|
||||
const toolbarRef = useRef<HTMLDivElement>(null);
|
||||
const pos = getPos();
|
||||
const { from, to } = editor.state.selection;
|
||||
const isActive = from >= pos && to < pos + node.nodeSize;
|
||||
const mathRendererRef = useRef<HTMLElement>();
|
||||
const mathInputElementRef = useRef<HTMLElement>();
|
||||
const theme = useThemeEngineStore((store) => store.theme);
|
||||
const { enabled, start } = useTimer(1000);
|
||||
|
||||
console.log("Rerendering MathBlockComponent", isActive);
|
||||
useEffect(() => {
|
||||
if (isActive) return;
|
||||
(async function () {
|
||||
const pos = getPos();
|
||||
const node = editor.state.doc.nodeAt(pos);
|
||||
const text = node?.textContent;
|
||||
|
||||
if (text && elementRef.current) {
|
||||
const katex = await loadKatex();
|
||||
if (mathRendererRef.current) {
|
||||
if (text && text.trim()) {
|
||||
const katex = await loadKatex();
|
||||
|
||||
elementRef.current.innerHTML = katex.renderToString(text, {
|
||||
displayMode: true,
|
||||
globalGroup: true,
|
||||
throwOnError: false
|
||||
});
|
||||
mathRendererRef.current.innerHTML = katex.renderToString(text, {
|
||||
displayMode: true,
|
||||
globalGroup: true,
|
||||
throwOnError: false
|
||||
});
|
||||
} else {
|
||||
mathRendererRef.current.innerHTML = "";
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, [isActive]);
|
||||
}, [isActive, node.textContent]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -68,7 +71,7 @@ export function MathBlockComponent(
|
||||
flexDirection: "column",
|
||||
borderRadius: "default",
|
||||
overflow: "hidden",
|
||||
...(isActive ? {} : { height: "1px", width: 0, visibility: "hidden" })
|
||||
display: isActive ? "flex" : "none"
|
||||
}}
|
||||
>
|
||||
<SimpleBar
|
||||
@@ -79,7 +82,7 @@ export function MathBlockComponent(
|
||||
<div>
|
||||
<Text
|
||||
ref={(ref) => {
|
||||
codeElementRef.current = ref ?? undefined;
|
||||
mathInputElementRef.current = ref ?? undefined;
|
||||
forwardRef?.(ref);
|
||||
}}
|
||||
autoCorrect="off"
|
||||
@@ -89,7 +92,6 @@ export function MathBlockComponent(
|
||||
pre: {
|
||||
fontFamily: "inherit !important",
|
||||
tabSize: "inherit !important",
|
||||
// background: "transparent !important",
|
||||
padding: "10px !important",
|
||||
margin: "0px !important",
|
||||
width: "100%",
|
||||
@@ -105,13 +107,10 @@ export function MathBlockComponent(
|
||||
}
|
||||
},
|
||||
fontFamily: "monospace",
|
||||
whiteSpace: "pre", // TODO !important
|
||||
whiteSpace: "pre",
|
||||
tabSize: 1,
|
||||
position: "relative",
|
||||
lineHeight: "20px",
|
||||
// bg: "var(--background-secondary)",
|
||||
// color: "white",
|
||||
// overflowX: "hidden",
|
||||
display: "flex"
|
||||
}}
|
||||
spellCheck={false}
|
||||
@@ -119,7 +118,6 @@ export function MathBlockComponent(
|
||||
</div>
|
||||
</SimpleBar>
|
||||
<Flex
|
||||
ref={toolbarRef}
|
||||
contentEditable={false}
|
||||
sx={{
|
||||
bg: "var(--background-secondary)",
|
||||
@@ -136,29 +134,6 @@ export function MathBlockComponent(
|
||||
: ""}
|
||||
</Text>
|
||||
) : null}
|
||||
|
||||
<Button
|
||||
variant={"icon"}
|
||||
sx={{
|
||||
p: 1,
|
||||
opacity: "1 !important"
|
||||
}}
|
||||
title={strings.toggleIndentationMode()}
|
||||
disabled={!editor.isEditable}
|
||||
onClick={() => {
|
||||
if (!editor.isEditable) return;
|
||||
editor.commands.changeCodeBlockIndentation({
|
||||
type: indentType === "space" ? "tab" : "space",
|
||||
amount: indentLength
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Text variant={"subBody"}>
|
||||
{indentType === "space" ? strings.spaces() : strings.tabs()}:{" "}
|
||||
{indentLength}
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant={"icon"}
|
||||
sx={{
|
||||
@@ -191,7 +166,7 @@ export function MathBlockComponent(
|
||||
onClick={() => {
|
||||
editor.storage.copyToClipboard?.(
|
||||
node.textContent,
|
||||
codeElementRef?.current?.innerHTML
|
||||
mathInputElementRef?.current?.innerHTML
|
||||
);
|
||||
start();
|
||||
}}
|
||||
@@ -208,7 +183,18 @@ export function MathBlockComponent(
|
||||
) : null}
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Box contentEditable={false} ref={elementRef} />
|
||||
{node.textContent && node.textContent.trim() && (
|
||||
<SimpleBar
|
||||
style={{
|
||||
borderRadius: "5px",
|
||||
paddingInline: "5px",
|
||||
border: isActive ? "1px solid var(--border)" : "none",
|
||||
marginTop: isActive ? "8px" : "0px"
|
||||
}}
|
||||
>
|
||||
<Box contentEditable={false} ref={mathRendererRef} />
|
||||
</SimpleBar>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,9 +53,31 @@ export type MathBlockAttributes = {
|
||||
caretPosition?: CaretPosition;
|
||||
};
|
||||
|
||||
export interface MathBlockOptions {
|
||||
/**
|
||||
* Define whether the node should be exited on triple enter.
|
||||
* Defaults to `true`.
|
||||
*/
|
||||
exitOnTripleEnter: boolean;
|
||||
/**
|
||||
* Define whether the node should be exited on arrow down if there is no node after it.
|
||||
* Defaults to `true`.
|
||||
*/
|
||||
exitOnArrowDown: boolean;
|
||||
/**
|
||||
* Define whether the node should be exited on arrow up if there is no node before it.
|
||||
* Defaults to `true`.
|
||||
*/
|
||||
exitOnArrowUp: boolean;
|
||||
/**
|
||||
* Custom HTML attributes that should be added to the rendered HTML tag.
|
||||
*/
|
||||
HTMLAttributes: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// simple inputrule for block math
|
||||
const REGEX_BLOCK_MATH_DOLLARS = /\$\$\$\s+$/; //new RegExp("\$\$\s+$", "i");
|
||||
export const MathBlock = Node.create({
|
||||
export const MathBlock = Node.create<MathBlockOptions>({
|
||||
name: "mathBlock",
|
||||
group: "block math",
|
||||
content: "text*", // important!
|
||||
@@ -64,6 +86,15 @@ export const MathBlock = Node.create({
|
||||
draggable: false,
|
||||
marks: "",
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
exitOnTripleEnter: true,
|
||||
exitOnArrowDown: true,
|
||||
exitOnArrowUp: true,
|
||||
HTMLAttributes: {}
|
||||
};
|
||||
},
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
language: {
|
||||
@@ -189,7 +220,6 @@ export const MathBlock = Node.create({
|
||||
if (indentation) return indentOnEnter(editor, $from, indentation);
|
||||
return false;
|
||||
},
|
||||
|
||||
// exit node on arrow up
|
||||
ArrowUp: ({ editor }) => {
|
||||
if (!this.options.exitOnArrowUp) {
|
||||
@@ -198,18 +228,29 @@ export const MathBlock = Node.create({
|
||||
|
||||
const { state } = editor;
|
||||
const { selection } = state;
|
||||
const { $anchor, empty } = selection;
|
||||
const { $anchor, empty, $from } = selection;
|
||||
|
||||
if (!empty || $anchor.parent.type !== this.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isAtStart = $anchor.pos === 1;
|
||||
if (!isAtStart) {
|
||||
const isAtStartOfNode = $from.parentOffset === 0;
|
||||
if (!isAtStartOfNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return editor.commands.insertContentAt(0, "<p></p>");
|
||||
const before = $from.before();
|
||||
if (before === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nodeBefore = state.doc.nodeAt(before);
|
||||
if (nodeBefore) {
|
||||
editor.commands.setNodeSelection($from.before());
|
||||
return false;
|
||||
}
|
||||
|
||||
return editor.commands.exitCode();
|
||||
},
|
||||
// exit node on arrow down
|
||||
ArrowDown: ({ editor }) => {
|
||||
@@ -359,7 +400,7 @@ export const MathBlock = Node.create({
|
||||
addNodeView() {
|
||||
return createSelectionBasedNodeView(MathBlockComponent, {
|
||||
contentDOMFactory: () => {
|
||||
const content = document.createElement("div");
|
||||
const content = document.createElement("pre");
|
||||
content.classList.add("node-content-wrapper");
|
||||
content.style.whiteSpace = "pre";
|
||||
// caret is not visible if content element width is 0px
|
||||
|
||||
@@ -347,6 +347,7 @@ const useTiptap = (
|
||||
CodeBlock.name,
|
||||
Table.name,
|
||||
Blockquote.name,
|
||||
MathBlock.name,
|
||||
...LIST_NODE_TYPES
|
||||
]
|
||||
}),
|
||||
|
||||
@@ -132,7 +132,8 @@
|
||||
|
||||
.ProseMirror > div.codeblock-view-content-wrap,
|
||||
.ProseMirror > div.taskList-view-content-wrap,
|
||||
.ProseMirror > div.math-block.math-node {
|
||||
.ProseMirror > div.math-block.math-node,
|
||||
.ProseMirror > div.mathBlock-view-content-wrap {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user