codex-team / codex-team/editor.js

Feature Request : Inline Block Tool

Open
#2,568 4 comments 1 reaction 0 assignees View on GitHub
nice to have
Dominant language
TypeScript
Stars
31.9k
Forks
2.2k
Avg merge
1d 1h
Merged PRs (30d)
1

Description

## My Use Case & Problem
I am using https://katex.org to render math expressions into HTML. It's quite easy to create a block tool to render math expressions.

However, I needed a function to render math expressions inline. I have created an in-line tool somehow but it's not feasible to use.
Here is a video demo of how I have implemented the Katex inline tool. This tool is live on my website and you can use it here https://dsabyte.com/editor.

https://github.com/codex-team/editor.js/assets/54087826/b2b646ca-410e-4c0c-9c87-989a7647eb4d

Just to render `\sum_{i=0}^n A[i] = S` i have to save below data into my database.
> This is output of paragraph block
```html
"This is inline math ∑i=0n\sum_{i=0}^ni=0n "
```
Can we implement a feature in editorjs where inline tool can be treated as inline blocks and we can get output for every paragraph as an array. For ex:
```js
// expected paragraph block content
[
{type:"text",value:"This is inline math"},
{type:"math",value:"\sum_{i=0}^n A[i] = S"}
]
```

Now, it's up to the user to render however they want.

I am sharing the code of the inline math tool which is implemented here https://dsabyte.com/editor

```js
const katex = require("katex");
const getNodeTextContent = require("./lib/getNodeTextContent");

// https://github.com/hata6502/editorjs-style?tab=readme-ov-file
class InlineMath {
constructor({ api }) {
this.api = api;
this.inlineMathTag = "SPAN";
this.inlineMathTagClass = "inline-math";
this.inlineMathNode = null;
this.tex = "";
this.texAttribute = "data-tex";
}

render() {
const button = document.createElement("button");

button.classList.add(this.api.styles.inlineToolButton);
button.type = "button";

button.innerHTML = `

`;

return button;
}

wrap(range) {
const selectedDocFragement = range.extractContents();
const tex = getNodeTextContent(selectedDocFragement);

const inlineMathNode = document.createElement(this.inlineMathTag);
inlineMathNode.classList.add(this.inlineMathTagClass);
inlineMathNode.setAttribute(this.texAttribute, tex);

// contentEditable = false is required so that we can directly remove the entire inline math node
inlineMathNode.contentEditable = "false";

inlineMathNode.innerHTML = this.renderKatexToString(tex);

// Insert new element
range.insertNode(inlineMathNode);
this.state = true;
this.inlineMathNode = inlineMathNode;
this.tex = tex;
}

unwrap(range) {
// FIXME: this.inlineMathNode check should not be required here
// this check is required due to an issue with the editor
// it won't work when we click inline button multiple times within the same session
if (this.inlineMathNode) {
this.inlineMathNode.remove();
const textNode = document.createTextNode(this.tex);
range.insertNode(textNode);
} else {
const mark = this.api.selection.findParentTag(
this.inlineMathTag,
this.inlineMathTagClass
);

const text = range.extractContents();

mark.remove();

range.insertNode(text);
}

this.state = false;
}

surround(range) {
if (this.state) {
this.unwrap(range);
return;
}
this.wrap(range);
}

checkState(selection) {
const textNode = selection.anchorNode;

if (!textNode) {
return;
}

if (this.state === undefined) {
// it is required to find the parent tag and set it to this.inlineMathNode and tex
// render action input will be empty for already rendered math elements otherwise
const parentNode = this.api.selection.findParentTag(
this.inlineMathTag,
this.inlineMathTagClass
);
this.state = !!parentNode;

if (this.state) {
this.tex = parentNode.getAttribute(this.texAttribute);
this.inlineMathNode = parentNode;
}

this.state = !!this.api.selection.findParentTag(
this.inlineMathTag,
this.inlineMathTagClass
);
}

if (this.state) {
this.showActions();
} else {
this.hideActions();
}
}
static get isInline() {
return true;
}
static get title() {
return "Inline Math";
}
static get shortcut() {
return "CMD+SHIFT+M";
}
static get sanitize() {
return {
span: function () {
return true;
},
};
}

showActions() {
this.inlineMathTextInput.style.display = "block";
this.inlineMathTextInput.value = this.tex;
}

hideActions() {
this.inlineMathTextInput.style.display = "none";
}

renderActions() {
this.inlineMathTextInput = document.createElement("input");
this.inlineMathTextInput.type = "text";
this.inlineMathTextInput.classList.add("cdx-input");
this.inlineMathTextInput.placeholder = "Math is Like e=mc^2";
this.inlineMathTextInput.value = this.tex;
this.inlineMathTextInput.onkeyup = (e) => {
if (e.key.toLowerCase() === "enter") {
this.tex = e.target.value;
this.inlineMathNode.innerHTML = this.renderKatexToString(this.tex);
this.inlineMathNode.setAttribute(this.texAttribute, this.tex);
}
};
return this.inlineMathTextInput;
}

renderKatexToString(tex) {
let html = "";
try {
html = katex.renderToString(tex, { displayMode: false });
} catch (error) {
html = error;
}
return `${html}`;
}
}

module.exports = InlineMath;
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.