mirror of
				https://github.com/BookStackApp/BookStack.git
				synced 2025-10-29 16:09:29 +03:00 
			
		
		
		
	Lexical: Switched to ts for new editor build
This commit is contained in:
		| @@ -14,7 +14,7 @@ const entryPoints = { | |||||||
|     code: path.join(__dirname, '../../resources/js/code/index.mjs'), |     code: path.join(__dirname, '../../resources/js/code/index.mjs'), | ||||||
|     'legacy-modes': path.join(__dirname, '../../resources/js/code/legacy-modes.mjs'), |     'legacy-modes': path.join(__dirname, '../../resources/js/code/legacy-modes.mjs'), | ||||||
|     markdown: path.join(__dirname, '../../resources/js/markdown/index.mjs'), |     markdown: path.join(__dirname, '../../resources/js/markdown/index.mjs'), | ||||||
|     wysiwyg: path.join(__dirname, '../../resources/js/wysiwyg/index.mjs'), |     wysiwyg: path.join(__dirname, '../../resources/js/wysiwyg/index.ts'), | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // Locate our output directory | // Locate our output directory | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										16
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -43,7 +43,8 @@ | |||||||
|         "eslint-plugin-import": "^2.29.0", |         "eslint-plugin-import": "^2.29.0", | ||||||
|         "livereload": "^0.9.3", |         "livereload": "^0.9.3", | ||||||
|         "npm-run-all": "^4.1.5", |         "npm-run-all": "^4.1.5", | ||||||
|         "sass": "^1.69.5" |         "sass": "^1.69.5", | ||||||
|  |         "typescript": "^5.4.5" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@aashutoshrathi/word-wrap": { |     "node_modules/@aashutoshrathi/word-wrap": { | ||||||
| @@ -4099,6 +4100,19 @@ | |||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/typescript": { | ||||||
|  |       "version": "5.4.5", | ||||||
|  |       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", | ||||||
|  |       "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", | ||||||
|  |       "dev": true, | ||||||
|  |       "bin": { | ||||||
|  |         "tsc": "bin/tsc", | ||||||
|  |         "tsserver": "bin/tsserver" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=14.17" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/uc.micro": { |     "node_modules/uc.micro": { | ||||||
|       "version": "2.1.0", |       "version": "2.1.0", | ||||||
|       "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", |       "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", | ||||||
|   | |||||||
| @@ -25,7 +25,8 @@ | |||||||
|     "eslint-plugin-import": "^2.29.0", |     "eslint-plugin-import": "^2.29.0", | ||||||
|     "livereload": "^0.9.3", |     "livereload": "^0.9.3", | ||||||
|     "npm-run-all": "^4.1.5", |     "npm-run-all": "^4.1.5", | ||||||
|     "sass": "^1.69.5" |     "sass": "^1.69.5", | ||||||
|  |     "typescript": "^5.4.5" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@codemirror/commands": "^6.3.2", |     "@codemirror/commands": "^6.3.2", | ||||||
| @@ -65,7 +66,8 @@ | |||||||
|     }, |     }, | ||||||
|     "extends": "airbnb-base", |     "extends": "airbnb-base", | ||||||
|     "ignorePatterns": [ |     "ignorePatterns": [ | ||||||
|       "resources/**/*-stub.js" |       "resources/**/*-stub.js", | ||||||
|  |       "resources/**/*.ts" | ||||||
|     ], |     ], | ||||||
|     "overrides": [], |     "overrides": [], | ||||||
|     "parserOptions": { |     "parserOptions": { | ||||||
|   | |||||||
| @@ -4,18 +4,18 @@ import { | |||||||
|     $getSelection, |     $getSelection, | ||||||
|     COMMAND_PRIORITY_LOW, |     COMMAND_PRIORITY_LOW, | ||||||
|     createCommand, |     createCommand, | ||||||
|     createEditor |     createEditor, CreateEditorArgs, | ||||||
| } from 'lexical'; | } from 'lexical'; | ||||||
| import {createEmptyHistoryState, registerHistory} from '@lexical/history'; | import {createEmptyHistoryState, registerHistory} from '@lexical/history'; | ||||||
| import {registerRichText} from '@lexical/rich-text'; | import {registerRichText} from '@lexical/rich-text'; | ||||||
| import {$getNearestBlockElementAncestorOrThrow, mergeRegister} from '@lexical/utils'; | import {$getNearestBlockElementAncestorOrThrow, mergeRegister} from '@lexical/utils'; | ||||||
| import {$generateNodesFromDOM} from '@lexical/html'; | import {$generateNodesFromDOM} from '@lexical/html'; | ||||||
| import {getNodesForPageEditor} from "./nodes/index.js"; | import {$setBlocksType} from '@lexical/selection'; | ||||||
| import {$createCalloutNode, $isCalloutNode} from "./nodes/callout.js"; | import {getNodesForPageEditor} from './nodes'; | ||||||
| import {$setBlocksType} from "@lexical/selection"; | import {$createCalloutNode, $isCalloutNode, CalloutCategory} from './nodes/callout'; | ||||||
| 
 | 
 | ||||||
| export function createPageEditorInstance(editArea) { | export function createPageEditorInstance(editArea: HTMLElement) { | ||||||
|     const config = { |     const config: CreateEditorArgs = { | ||||||
|         namespace: 'BookStackPageEditor', |         namespace: 'BookStackPageEditor', | ||||||
|         nodes: getNodesForPageEditor(), |         nodes: getNodesForPageEditor(), | ||||||
|         onError: console.error, |         onError: console.error, | ||||||
| @@ -52,7 +52,7 @@ export function createPageEditorInstance(editArea) { | |||||||
|     // Example of creating, registering and using a custom command
 |     // Example of creating, registering and using a custom command
 | ||||||
| 
 | 
 | ||||||
|     const SET_BLOCK_CALLOUT_COMMAND = createCommand(); |     const SET_BLOCK_CALLOUT_COMMAND = createCommand(); | ||||||
|     editor.registerCommand(SET_BLOCK_CALLOUT_COMMAND, (category = 'info') => { |     editor.registerCommand(SET_BLOCK_CALLOUT_COMMAND, (category: CalloutCategory = 'info') => { | ||||||
|         const selection = $getSelection(); |         const selection = $getSelection(); | ||||||
|         const blockElement = $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]); |         const blockElement = $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]); | ||||||
|         if ($isCalloutNode(blockElement)) { |         if ($isCalloutNode(blockElement)) { | ||||||
| @@ -1,35 +1,51 @@ | |||||||
| import {$createParagraphNode, ElementNode} from 'lexical'; | import { | ||||||
|  |     $createParagraphNode, | ||||||
|  |     DOMConversion, | ||||||
|  |     DOMConversionMap, DOMConversionOutput, | ||||||
|  |     ElementNode, | ||||||
|  |     LexicalEditor, | ||||||
|  |     LexicalNode, | ||||||
|  |     ParagraphNode, SerializedElementNode, Spread | ||||||
|  | } from 'lexical'; | ||||||
|  | import type {EditorConfig} from "lexical/LexicalEditor"; | ||||||
|  | import type {RangeSelection} from "lexical/LexicalSelection"; | ||||||
|  | 
 | ||||||
|  | export type CalloutCategory = 'info' | 'danger' | 'warning' | 'success'; | ||||||
|  | 
 | ||||||
|  | export type SerializedCalloutNode = Spread<{ | ||||||
|  |     category: CalloutCategory; | ||||||
|  | }, SerializedElementNode> | ||||||
| 
 | 
 | ||||||
| export class Callout extends ElementNode { | export class Callout extends ElementNode { | ||||||
| 
 | 
 | ||||||
|     __category = 'info'; |     __category: CalloutCategory = 'info'; | ||||||
| 
 | 
 | ||||||
|     static getType() { |     static getType() { | ||||||
|         return 'callout'; |         return 'callout'; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static clone(node) { |     static clone(node: Callout) { | ||||||
|         return new Callout(node.__category, node.__key); |         return new Callout(node.__category, node.__key); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     constructor(category, key) { |     constructor(category: CalloutCategory, key?: string) { | ||||||
|         super(key); |         super(key); | ||||||
|         this.__category = category; |         this.__category = category; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     createDOM(_config, _editor) { |     createDOM(_config: EditorConfig, _editor: LexicalEditor) { | ||||||
|         const element = document.createElement('p'); |         const element = document.createElement('p'); | ||||||
|         element.classList.add('callout', this.__category || ''); |         element.classList.add('callout', this.__category || ''); | ||||||
|         return element; |         return element; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     updateDOM(prevNode, dom) { |     updateDOM(prevNode: unknown, dom: HTMLElement) { | ||||||
|         // Returning false tells Lexical that this node does not need its
 |         // Returning false tells Lexical that this node does not need its
 | ||||||
|         // DOM element replacing with a new copy from createDOM.
 |         // DOM element replacing with a new copy from createDOM.
 | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     insertNewAfter(selection, restoreSelection) { |     insertNewAfter(selection: RangeSelection, restoreSelection?: boolean): Callout|ParagraphNode { | ||||||
|         const anchorOffset = selection ? selection.anchor.offset : 0; |         const anchorOffset = selection ? selection.anchor.offset : 0; | ||||||
|         const newElement = anchorOffset === this.getTextContentSize() || !selection |         const newElement = anchorOffset === this.getTextContentSize() || !selection | ||||||
|             ? $createParagraphNode() : $createCalloutNode(this.__category); |             ? $createParagraphNode() : $createCalloutNode(this.__category); | ||||||
| @@ -46,14 +62,14 @@ export class Callout extends ElementNode { | |||||||
|         return newElement; |         return newElement; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static importDOM() { |     static importDOM(): DOMConversionMap|null { | ||||||
|         return { |         return { | ||||||
|             p: node => { |             p(node: HTMLElement): DOMConversion|null { | ||||||
|                 if (node.classList.contains('callout')) { |                 if (node.classList.contains('callout')) { | ||||||
|                     return { |                     return { | ||||||
|                         conversion: element => { |                         conversion: (element: HTMLElement): DOMConversionOutput|null => { | ||||||
|                             let category = 'info'; |                             let category: CalloutCategory = 'info'; | ||||||
|                             const categories = ['info', 'success', 'warning', 'danger']; |                             const categories: CalloutCategory[] = ['info', 'success', 'warning', 'danger']; | ||||||
| 
 | 
 | ||||||
|                             for (const c of categories) { |                             for (const c of categories) { | ||||||
|                                 if (element.classList.contains(c)) { |                                 if (element.classList.contains(c)) { | ||||||
| @@ -74,7 +90,7 @@ export class Callout extends ElementNode { | |||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     exportJSON() { |     exportJSON(): SerializedCalloutNode { | ||||||
|         return { |         return { | ||||||
|             ...super.exportJSON(), |             ...super.exportJSON(), | ||||||
|             type: 'callout', |             type: 'callout', | ||||||
| @@ -83,16 +99,16 @@ export class Callout extends ElementNode { | |||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static importJSON(serializedNode) { |     static importJSON(serializedNode: SerializedCalloutNode): Callout { | ||||||
|         return $createCalloutNode(serializedNode.category); |         return $createCalloutNode(serializedNode.category); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function $createCalloutNode(category = 'info') { | export function $createCalloutNode(category: CalloutCategory = 'info') { | ||||||
|     return new Callout(category); |     return new Callout(category); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function $isCalloutNode(node) { | export function $isCalloutNode(node: LexicalNode | null | undefined) { | ||||||
|     return node instanceof Callout; |     return node instanceof Callout; | ||||||
| } | } | ||||||
| @@ -1,11 +1,11 @@ | |||||||
| import {HeadingNode, QuoteNode} from '@lexical/rich-text'; | import {HeadingNode, QuoteNode} from '@lexical/rich-text'; | ||||||
| import {Callout} from './callout'; | import {Callout} from './callout'; | ||||||
|  | import {KlassConstructor, LexicalNode} from "lexical"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Load the nodes for lexical. |  * Load the nodes for lexical. | ||||||
|  * @returns {LexicalNode[]} |  | ||||||
|  */ |  */ | ||||||
| export function getNodesForPageEditor() { | export function getNodesForPageEditor(): KlassConstructor<typeof LexicalNode>[] { | ||||||
|     return [ |     return [ | ||||||
|         Callout, |         Callout, | ||||||
|         HeadingNode, |         HeadingNode, | ||||||
		Reference in New Issue
	
	Block a user