Skip to content

Commit

Permalink
feat: expose commands
Browse files Browse the repository at this point in the history
  • Loading branch information
sibiraj-s committed Jan 11, 2021
1 parent edff1f9 commit dc5c8d1
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 1 deletion.
7 changes: 6 additions & 1 deletion src/lib/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -89,7 +94,7 @@ class Editor {

this.onUpdate.next();

if (!tr.docChanged) {
if (!tr.docChanged && !tr.getMeta('FORCE_EMIT')) {
return;
}

Expand Down
207 changes: 207 additions & 0 deletions src/lib/EditorCommands.ts
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit dc5c8d1

Please sign in to comment.