mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-08-07 23:03:00 +03:00
Lexical: Reorganised custom node code into lexical codebase
Also cleaned up old unused imports.
This commit is contained in:
197
resources/js/wysiwyg/lexical/rich-text/LexicalCodeBlockNode.ts
Normal file
197
resources/js/wysiwyg/lexical/rich-text/LexicalCodeBlockNode.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import {
|
||||
DecoratorNode,
|
||||
DOMConversion,
|
||||
DOMConversionMap,
|
||||
DOMConversionOutput, DOMExportOutput,
|
||||
LexicalEditor, LexicalNode,
|
||||
SerializedLexicalNode,
|
||||
Spread
|
||||
} from "lexical";
|
||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||
import {EditorDecoratorAdapter} from "../../ui/framework/decorator";
|
||||
import {CodeEditor} from "../../../components";
|
||||
import {el} from "../../utils/dom";
|
||||
|
||||
export type SerializedCodeBlockNode = Spread<{
|
||||
language: string;
|
||||
id: string;
|
||||
code: string;
|
||||
}, SerializedLexicalNode>
|
||||
|
||||
const getLanguageFromClassList = (classes: string) => {
|
||||
const langClasses = classes.split(' ').filter(cssClass => cssClass.startsWith('language-'));
|
||||
return (langClasses[0] || '').replace('language-', '');
|
||||
};
|
||||
|
||||
export class CodeBlockNode extends DecoratorNode<EditorDecoratorAdapter> {
|
||||
__id: string = '';
|
||||
__language: string = '';
|
||||
__code: string = '';
|
||||
|
||||
static getType(): string {
|
||||
return 'code-block';
|
||||
}
|
||||
|
||||
static clone(node: CodeBlockNode): CodeBlockNode {
|
||||
const newNode = new CodeBlockNode(node.__language, node.__code, node.__key);
|
||||
newNode.__id = node.__id;
|
||||
return newNode;
|
||||
}
|
||||
|
||||
constructor(language: string = '', code: string = '', key?: string) {
|
||||
super(key);
|
||||
this.__language = language;
|
||||
this.__code = code;
|
||||
}
|
||||
|
||||
setLanguage(language: string): void {
|
||||
const self = this.getWritable();
|
||||
self.__language = language;
|
||||
}
|
||||
|
||||
getLanguage(): string {
|
||||
const self = this.getLatest();
|
||||
return self.__language;
|
||||
}
|
||||
|
||||
setCode(code: string): void {
|
||||
const self = this.getWritable();
|
||||
self.__code = code;
|
||||
}
|
||||
|
||||
getCode(): string {
|
||||
const self = this.getLatest();
|
||||
return self.__code;
|
||||
}
|
||||
|
||||
setId(id: string) {
|
||||
const self = this.getWritable();
|
||||
self.__id = id;
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
const self = this.getLatest();
|
||||
return self.__id;
|
||||
}
|
||||
|
||||
decorate(editor: LexicalEditor, config: EditorConfig): EditorDecoratorAdapter {
|
||||
return {
|
||||
type: 'code',
|
||||
getNode: () => this,
|
||||
};
|
||||
}
|
||||
|
||||
isInline(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
isIsolated() {
|
||||
return true;
|
||||
}
|
||||
|
||||
createDOM(_config: EditorConfig, _editor: LexicalEditor) {
|
||||
const codeBlock = el('pre', {
|
||||
id: this.__id || null,
|
||||
}, [
|
||||
el('code', {
|
||||
class: this.__language ? `language-${this.__language}` : null,
|
||||
}, [this.__code]),
|
||||
]);
|
||||
|
||||
return el('div', {class: 'editor-code-block-wrap'}, [codeBlock]);
|
||||
}
|
||||
|
||||
updateDOM(prevNode: CodeBlockNode, dom: HTMLElement) {
|
||||
const code = dom.querySelector('code');
|
||||
if (!code) return false;
|
||||
|
||||
if (prevNode.__language !== this.__language) {
|
||||
code.className = this.__language ? `language-${this.__language}` : '';
|
||||
}
|
||||
|
||||
if (prevNode.__id !== this.__id) {
|
||||
dom.setAttribute('id', this.__id);
|
||||
}
|
||||
|
||||
if (prevNode.__code !== this.__code) {
|
||||
code.textContent = this.__code;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
exportDOM(editor: LexicalEditor): DOMExportOutput {
|
||||
const dom = this.createDOM(editor._config, editor);
|
||||
return {
|
||||
element: dom.querySelector('pre') as HTMLElement,
|
||||
};
|
||||
}
|
||||
|
||||
static importDOM(): DOMConversionMap|null {
|
||||
return {
|
||||
pre(node: HTMLElement): DOMConversion|null {
|
||||
return {
|
||||
conversion: (element: HTMLElement): DOMConversionOutput|null => {
|
||||
|
||||
const codeEl = element.querySelector('code');
|
||||
const language = getLanguageFromClassList(element.className)
|
||||
|| (codeEl && getLanguageFromClassList(codeEl.className))
|
||||
|| '';
|
||||
|
||||
const code = codeEl ? (codeEl.textContent || '').trim() : (element.textContent || '').trim();
|
||||
const node = $createCodeBlockNode(language, code);
|
||||
|
||||
if (element.id) {
|
||||
node.setId(element.id);
|
||||
}
|
||||
|
||||
return { node };
|
||||
},
|
||||
priority: 3,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
exportJSON(): SerializedCodeBlockNode {
|
||||
return {
|
||||
type: 'code-block',
|
||||
version: 1,
|
||||
id: this.__id,
|
||||
language: this.__language,
|
||||
code: this.__code,
|
||||
};
|
||||
}
|
||||
|
||||
static importJSON(serializedNode: SerializedCodeBlockNode): CodeBlockNode {
|
||||
const node = $createCodeBlockNode(serializedNode.language, serializedNode.code);
|
||||
node.setId(serializedNode.id || '');
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
export function $createCodeBlockNode(language: string = '', code: string = ''): CodeBlockNode {
|
||||
return new CodeBlockNode(language, code);
|
||||
}
|
||||
|
||||
export function $isCodeBlockNode(node: LexicalNode | null | undefined) {
|
||||
return node instanceof CodeBlockNode;
|
||||
}
|
||||
|
||||
export function $openCodeEditorForNode(editor: LexicalEditor, node: CodeBlockNode): void {
|
||||
const code = node.getCode();
|
||||
const language = node.getLanguage();
|
||||
|
||||
// @ts-ignore
|
||||
const codeEditor = window.$components.first('code-editor') as CodeEditor;
|
||||
// TODO - Handle direction
|
||||
codeEditor.open(code, language, 'ltr', (newCode: string, newLang: string) => {
|
||||
editor.update(() => {
|
||||
node.setCode(newCode);
|
||||
node.setLanguage(newLang);
|
||||
});
|
||||
// TODO - Re-focus
|
||||
}, () => {
|
||||
// TODO - Re-focus
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user