Last active
October 30, 2022 04:58
-
-
Save BrianHung/b72126c98fa08cb1c09170b1394771a0 to your computer and use it in GitHub Desktop.
Math NodeView for TipTap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.ProseMirror .Math { | |
display: contents; | |
} | |
.ProseMirror .Math .katex-editor { | |
display: inline; | |
} | |
.ProseMirror .Math .katex-render .katex { | |
font-size: 1em; | |
} | |
.ProseMirror .Math .katex-render .katex-error { | |
font-family: "Inter", sans-serif; | |
padding: 0; | |
} | |
.ProseMirror .Math .decoration-inline-math { | |
color: #8e9297; | |
} | |
.Math .hidden { | |
position: absolute; | |
width: 1px; | |
height: 1px; | |
margin: -1px; | |
border: 0; | |
padding: 0; | |
white-space: nowrap; | |
clip-path: inset(100%); | |
clip: rect(0 0 0 0); | |
overflow: hidden; | |
} | |
.Math .active { | |
position: static; | |
width: auto; | |
height: auto; | |
margin: 0; | |
clip: auto; | |
overflow: visible; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {InputRule } from "prosemirror-inputrules" | |
import {Node } from "tiptap" | |
import katex from "katex" | |
import "katex/dist/katex.min.css" | |
import './Math.css' | |
import { deleteMath } from "./MathKeymaps.js" | |
/* | |
* Defines a ComponentView for Math. | |
*/ | |
export default class Math extends Node { | |
get name() { | |
return "math"; | |
} | |
get schema() { | |
return { | |
code: true, | |
content: "text*", | |
marks: "", | |
group: "inline", | |
inline: true, | |
defining: true, | |
isolating: true, | |
parseDOM: [{tag: "span.Math"}], | |
toDOM: node => ["span", {class: "Math"}, | |
["span", {class: "katex-render", contenteditable: "false"}], | |
["span", {contenteditable: "false"}, "$"], | |
["span", {class: "katex-editor"}, 0], | |
["span", {contenteditable: "false"}, "$"], | |
], | |
}; | |
} | |
get view() { | |
return { | |
name: "Math", | |
props: ["node", "view", "getPos"], | |
computed: { | |
active() { return this.parentHasSelection() ? "active" : "hidden" }, | |
hidden() { return this.parentHasSelection() ? "hidden" : "active" }, | |
}, | |
watch: { | |
"node.textContent": function(textContent) { this.render(textContent); }, | |
}, | |
mounted() { | |
if (this.node && this.node.textContent) this.render(this.node.textContent); | |
}, | |
methods: { | |
// Updates katex-render with node textContent. | |
render(textContent) { | |
katex.render(textContent, this.$refs.render, { | |
throwOnError: false, displayMode: false | |
}); | |
}, | |
// Shows katex-render and hides katex-editor when selection is on parent. | |
parentHasSelection() { | |
const {doc, selection: {from, to, anchor}} = this.view.state; | |
const rpos = doc.resolve(this.getPos()); | |
const parentNodeFrom = this.getPos() - rpos.parentOffset; | |
const parentNodeTo = parentNodeFrom + rpos.parent.nodeSize; | |
const hasAnchor = parentNodeFrom <= anchor && anchor < parentNodeTo; | |
const hasSelect = from < parentNodeTo && parentNodeFrom <= to; | |
return hasAnchor || hasSelect; | |
}, | |
}, | |
template: ` | |
<span class="Math"> | |
<span class="katex-render" v-bind:class="hidden" ref="render" v-show="!parentHasSelection()" contenteditable="false"></span><span :contenteditable="false" class="decoration-inline-math" v-bind:class="active">$</span><span class="katex-editor" v-bind:class="active" ref="content"></span><span :contenteditable="false" class="decoration-inline-math" v-bind:class="active">$</span> | |
</span> | |
` | |
} | |
} | |
inputRules({type, getAttrs}) { | |
return [ | |
new InputRule(/(?:\$)([^\$\s]+(?:\s+[^\$\s]+)*)(?:\$)$/, (state, match, start, end) => { | |
const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs; | |
const [matchedText, content] = match; | |
const {tr, schema} = state; | |
if (matchedText) // Create the new Math node. | |
tr.replaceWith(start, end, type.create(attrs, schema.text(content))); | |
return tr | |
}) | |
]; | |
} | |
keys({ type }) { | |
return { | |
"Backspace": deleteMath, | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Selection } from "prosemirror-state" | |
export const deleteMath = (state, dispatch, view) => { | |
const { tr, selection: {$from: from, $to: to, $cursor}} = state; | |
if (!from.sameParent(to)) | |
return false; | |
// Handle deletion of right $. | |
const rborder = from.parent.type !== state.schema.nodes.math | |
&& from.doc.resolve(from.pos - 1).parent.type === state.schema.nodes.math; | |
if (rborder) { | |
const mathNode = from.doc.resolve(from.pos - 1).parent; | |
const startPos = from.pos; | |
tr.replaceRangeWith(startPos - mathNode.nodeSize, startPos, | |
state.schema.text("$" + mathNode.textContent)); | |
const selection = Selection.near(tr.doc.resolve(startPos), -1); | |
tr.setSelection(selection).scrollIntoView() | |
dispatch(tr); | |
return true; | |
} | |
// Handle deletion of left $. | |
const lborder = from.parent.type === state.schema.nodes.math | |
&& from.doc.resolve(from.pos - 1).parent.type !== state.schema.nodes.math; | |
if (lborder) { | |
const mathNode = from.parent; | |
const startPos = from.pos - 1; | |
tr.replaceRangeWith(startPos, startPos + mathNode.nodeSize, | |
state.schema.text(mathNode.textContent + "$")); | |
const selection = Selection.near(tr.doc.resolve(startPos), 1); | |
tr.setSelection(selection).scrollIntoView() | |
dispatch(tr); | |
return true; | |
} | |
// Prevent default behavior of partial node-deletion of katex editor. | |
const textLength = $cursor ? $cursor.node().textContent.length : 0; | |
if (textLength == 1) | |
{ | |
tr.delete($cursor.pos - 1, $cursor.pos); | |
dispatch(tr); | |
return true; | |
} | |
// Allow default ProseMirror behavior of character- or node-deletion. | |
return false; | |
} | |
function isSelectionEntirelyInsideMath(state) { | |
return state.selection.$from.sameParent(state.selection.$to) && | |
state.selection.$from.parent.type === state.schema.nodes.math; | |
} |
@huntz20 This was written for tiptap v1. You would have to modify it to work with the api of tiptap v2, or use prosemirror-math.
@huntz20 any luck implementing prosemirror-math into tiptap v2 or modifying this gist?
@BrianHung please provide us modified gist for tiptap v2.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sorry for silly question how to use it on my code ??
i already add it into my extensions like this
but it not working