From dc5c8d11eadc26079fe9beadeae16378a929597d Mon Sep 17 00:00:00 2001 From: sibiraj-s Date: Mon, 11 Jan 2021 13:04:35 +0530 Subject: [PATCH] feat: expose commands --- src/lib/Editor.ts | 7 +- src/lib/EditorCommands.ts | 207 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 src/lib/EditorCommands.ts diff --git a/src/lib/Editor.ts b/src/lib/Editor.ts index df9e853a..83ab7722 100644 --- a/src/lib/Editor.ts +++ b/src/lib/Editor.ts @@ -8,6 +8,7 @@ import { placeholder as placeholderPlugin } from 'ngx-editor/plugins'; +import EditorCommands from './EditorCommands'; import defautlSchema from './schema'; import { parseContent } from './parsers'; import isNil from './utils/isNil'; @@ -59,6 +60,10 @@ class Editor { return this.options.schema || defautlSchema; } + get commands(): EditorCommands { + return new EditorCommands(this.view); + } + setContent(content: Content): void { if (isNil(content)) { return; @@ -89,7 +94,7 @@ class Editor { this.onUpdate.next(); - if (!tr.docChanged) { + if (!tr.docChanged && !tr.getMeta('FORCE_EMIT')) { return; } diff --git a/src/lib/EditorCommands.ts b/src/lib/EditorCommands.ts new file mode 100644 index 00000000..10987e7d --- /dev/null +++ b/src/lib/EditorCommands.ts @@ -0,0 +1,207 @@ +import { EditorState, Transaction } from 'prosemirror-state'; +import { EditorView } from 'prosemirror-view'; +import { + chainCommands, createParagraphNear, liftEmptyBlock, + newlineInCode, splitBlock +} from 'prosemirror-commands'; + +import MarkCommand from './commands/Mark'; +import ListCommand from './commands/ListItem'; +import LinkCommand, { LinkAttrs } from './commands/Link'; +import HeadingCommand, { HeadingLevels } from './commands/Heading'; +import ImageCommand, { ImageAttrs } from './commands/Image'; +import TextColorCommand from './commands/TextColor'; +import TextAlignCommand, { Align } from './commands/TextAlign'; + +const execMark = (name: string, toggle = false) => { + return (state: EditorState, dispatch: (tr: Transaction) => void) => { + const command = new MarkCommand(name); + + if (!toggle) { + return command.apply()(state, dispatch); + } + + return command.toggle()(state, dispatch); + }; +}; + +class EditorCommands { + private view: EditorView; + private state: EditorState; + private tr: Transaction; + + constructor(view: EditorView) { + if (!view) { + throw Error('NgxEditor: Required view to initialize commands.'); + } + + this.view = view; + this.state = view.state; + this.tr = this.view.state.tr; + } + + private applyTrx = (tr?: Transaction) => { + this.state = this.state.apply(tr ?? this.tr); + this.tr = this.state.tr; + this.tr.setMeta('APPLIED_TRX', true); + } + + private dispatch = (tr: Transaction): void => { + this.applyTrx(tr); + } + + exec(): boolean { + // No changes applied + if (!this.tr.getMeta('APPLIED_TRX')) { + return false; + } + + const forceEmit = !this.view.state.doc.eq(this.state.doc); + this.view.updateState(this.state); + + const tr = this.tr + .setMeta('FORCE_EMIT', forceEmit); + + this.view.dispatch(tr); + return true; + } + + focus(): this { + this.view.focus(); + return this; + } + + scrollIntoView(): this { + this.tr.scrollIntoView(); + this.applyTrx(); + return this; + } + + insertText(text: string): this { + this.tr.insertText(text); + this.applyTrx(); + return this; + } + + insertNewLine(): this { + const newLineCommands = [newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock]; + chainCommands(...newLineCommands)(this.state, this.dispatch); + return this; + } + + applyMark(name: string): this { + execMark(name, false)(this.state, this.dispatch); + return this; + } + + toggleMark(name: string): this { + execMark(name, true)(this.state, this.dispatch); + return this; + } + + bold(): this { + execMark('strong')(this.state, this.dispatch); + return this; + } + + toggleBold(): this { + execMark('strong', true)(this.state, this.dispatch); + return this; + } + + italics(): this { + execMark('em')(this.state, this.dispatch); + return this; + } + + toggleItalics(): this { + execMark('em', true)(this.state, this.dispatch); + return this; + } + + underline(): this { + execMark('u')(this.state, this.dispatch); + return this; + } + + toggleUnderline(): this { + execMark('u', true)(this.state, this.dispatch); + return this; + } + + strike(): this { + execMark('s')(this.state, this.dispatch); + return this; + } + + toggleStrike(): this { + execMark('s', true)(this.state, this.dispatch); + return this; + } + + code(): this { + execMark('code')(this.state, this.dispatch); + return this; + } + + toggleCode(): this { + execMark('code', true)(this.state, this.dispatch); + return this; + } + + toggleOrderedList(): this { + const command = new ListCommand(false); + command.toggle()(this.state, this.dispatch); + return this; + } + + toggleBulletList(): this { + const command = new ListCommand(true); + command.toggle()(this.state, this.dispatch); + return this; + } + + toggleHeading(level: HeadingLevels): this { + const command = new HeadingCommand(level); + command.toggle()(this.state, this.dispatch); + return this; + } + + insertLink(text: string, attrs: LinkAttrs): this { + const command = new LinkCommand(); + command.insert(text, attrs)(this.state, this.dispatch); + return this; + } + + updateLink(attrs: LinkAttrs): this { + const command = new LinkCommand(); + command.update(attrs)(this.state, this.dispatch); + return this; + } + + insertImage(src: string, attrs: ImageAttrs = {}): this { + const command = new ImageCommand(); + command.insert(src, attrs)(this.state, this.dispatch); + return this; + } + + textColor(color: string): this { + const command = new TextColorCommand('text_color'); + command.apply({ color })(this.state, this.dispatch); + return this; + } + + backgroundColor(color: string): this { + const command = new TextColorCommand('text_background_color'); + command.apply({ backgroundColor: color })(this.state, this.dispatch); + return this; + } + + align(p: Align): this { + const command = new TextAlignCommand(p); + command.apply()(this.state, this.dispatch); + return this; + } +} + +export default EditorCommands;