Last active
August 16, 2019 18:51
-
-
Save twavv/ae63368d8c646f6fadbb1cea53227884 to your computer and use it in GitHub Desktop.
Slate Inline Focus Hack
This file contains 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 { | |
LATEX_INLINE_NODE_TYPE, | |
} from "../schema"; | |
export const LatexInlineBackspacePlugin = () => ({ | |
/** | |
* Handle key down when we're on the right edge of a `latexinline`. | |
* @param event | |
* @param editor | |
* @param next | |
* @returns {*} | |
*/ | |
onKeyDown(event, editor, next) { | |
if ( | |
event.key === "Backspace" | |
&& editor.value.selection.isCollapsed | |
&& editor.value.selection.focus.offset === 0 | |
) { | |
const previousText = editor.value.previousText; | |
const previousInline = previousText && editor.value.document.getClosestInline(previousText.key); | |
if (previousInline && previousInline.type === LATEX_INLINE_NODE_TYPE) { | |
const point = {key: previousInline.key, offset: previousInline.text.length - 1}; | |
event.preventDefault(); | |
return ( | |
editor | |
.select({ | |
focus: point, | |
anchor: point, | |
}) | |
); | |
} | |
} | |
return next(); | |
}, | |
}); |
This file contains 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 {Text} from "slate"; | |
import LatexInline from "../components/LatexInline"; | |
import {NODE_TEXT_INVALID} from "./slate-error-codes"; | |
export const LATEX_INLINE_NODE_TYPE = "latexinline"; | |
/* | |
* CURRENT BUG: | |
* Deleting on a latexinline doesn't work (it deletes the trailing space and | |
* then the normalization rule immediately adds it back). We could just have | |
* this condition be detected and make it equivalent to removing a character | |
* (this is **VERY MUCH** a hack), but this also puts the burden on other | |
* code to ensure they're always inserting LaTeX correctly (via a | |
* createInlineLatex function à la newParagraph). | |
*/ | |
export const LATEX_INLINE_SCHEMA = { | |
/** | |
* Only allow a single text node as a child. | |
*/ | |
nodes: [ | |
{ | |
match: {object: "text"}, | |
min: 1, | |
max: 1, | |
}, | |
], | |
/** | |
* Check validity of text. Ensures that text has a trailing space. | |
* @param text {string} | |
*/ | |
text(text) { | |
return ( | |
text.slice(text.length - 1, text.length).trim() === "" | |
); | |
}, | |
normalize(editor, error) { | |
const {node} = error; | |
if (error.code === NODE_TEXT_INVALID) { | |
// The text node gets merged into the previous one by Slate's sanity processes, | |
// so the ultimate effect of this is to add a space to the end of the existing | |
// text node. | |
return editor.insertNodeByKey(node.key, node.nodes.size, Text.create({text: " "})); | |
} | |
}, | |
}; | |
export const LATEX_INLINE_COMPONENT = LatexInline; |
This file contains 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 * as React from "react"; | |
import {css} from "react-emotion"; | |
import InlineLatex from "@/components/latex/InlineLatex"; | |
// We remove 1ch (1 character width) from the right of the latexinline when it's | |
// in the "editor" mode to attempt to hide the hack where we require there to be | |
// an extra space at the end of the node. | |
const UNHIDDEN_SPAN_STYLE = css` | |
font-size: 110%; | |
margin-right: -1ch; | |
`; | |
const HIDDEN_SPAN_STYLE = css` | |
display: inline-block; | |
height: 0; | |
width: 0; | |
overflow: hidden; | |
color: transparent; | |
`; | |
const LatexInline = (props) => { | |
const {attributes, children, editor, node, isSelected} = props; | |
const onLatexClick = React.useCallback((event) => { | |
event.preventDefault(); | |
// Points should always reference Text nodes. | |
const {key} = node.getFirstText(); | |
editor | |
.select({ | |
anchor: {key, offset: 0}, | |
focus: {key, offset: 0}, | |
}) | |
.focus(); | |
}, [editor, node]); | |
return ( | |
<span | |
{...attributes} | |
> | |
<code | |
className={isSelected ? UNHIDDEN_SPAN_STYLE : HIDDEN_SPAN_STYLE} | |
> | |
{children} | |
</code> | |
{ | |
isSelected | |
? null | |
: ( | |
<InlineLatex | |
contentEditable={false} | |
onClick={onLatexClick} | |
> | |
{node.text} | |
</InlineLatex> | |
) | |
} | |
</span> | |
); | |
}; | |
export default React.memo(LatexInline); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment