Skip to content

Commit

Permalink
feat: add support for custom plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
sibiraj-s authored May 8, 2020
1 parent 1cacf15 commit fcdc39b
Show file tree
Hide file tree
Showing 30 changed files with 312 additions and 213 deletions.
2 changes: 1 addition & 1 deletion demo/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</div>
</div>
<div class="editor">
<ngx-editor [placeholder]="placeholder" [ngModel]="editorContent" (ngModelChange)="editorContentChange($event)">
<ngx-editor [ngModel]="editorContent" (ngModelChange)="editorContentChange($event)">
</ngx-editor>
</div>
</div>
Expand Down
1 change: 0 additions & 1 deletion demo/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { environment } from '../environments/environment';
})

export class AppComponent {
placeholder = 'Type something here...';
isProdMode = environment.production;

editorContent: object = {
Expand Down
8 changes: 7 additions & 1 deletion demo/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import { CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { NgxEditorModule } from 'ngx-editor';

import { getPlugins } from './plugin';

@NgModule({
declarations: [
AppComponent
],
imports: [
CommonModule,
BrowserModule,
FormsModule,
NgxEditorModule,
NgxEditorModule.forRoot({
plugins: getPlugins()
}),
],
bootstrap: [AppComponent]
})
Expand Down
41 changes: 25 additions & 16 deletions src/lib/utils/plugins.ts → demo/src/app/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { Plugin } from 'prosemirror-state';
import { history, undo, redo } from 'prosemirror-history';
import { keymap } from 'prosemirror-keymap';
import { baseKeymap, toggleMark } from 'prosemirror-commands';

import schema from '../schema';
import { schema, menu, placeholder } from 'ngx-editor';

import { ComputedOptions, KeyMap } from '../types';

import menu from '../plugins/menu';
import placeholder from '../plugins/placeholder';
import { undo, redo, history } from 'prosemirror-history';
import { splitListItem, liftListItem, sinkListItem } from 'prosemirror-schema-list';
import { keymap } from 'prosemirror-keymap';
import { toggleMark, baseKeymap } from 'prosemirror-commands';
import { Plugin } from 'prosemirror-state';

const isMacOs = /Mac/.test(navigator.platform);

export type KeyMap = { [key: string]: any };

const getHistoryKeyMap = (): KeyMap => {
const historyMap: KeyMap = {};

Expand All @@ -38,7 +35,7 @@ const getListKeyMap = (): KeyMap => {
return listMap;
};

export const getPlugins = (options: ComputedOptions): Plugin[] => {
export const getPlugins = (): Plugin[] => {
const historyKeyMap = getHistoryKeyMap();
const listKeyMap = getListKeyMap();

Expand All @@ -52,12 +49,24 @@ export const getPlugins = (options: ComputedOptions): Plugin[] => {
keymap(historyKeyMap),
keymap(listKeyMap),
keymap(baseKeymap),
placeholder(options.placeholder),
menu({
toolbar: [
['bold', 'italic'],
['code'],
['ordered_list', 'bullet_list'],
[{ heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] }]
],
labels: {
bold: 'Bold',
italics: 'Italics',
code: 'Code',
ordered_list: 'Ordered List',
bullet_list: 'Bullet List',
heading: 'Header'
}
}),
placeholder('Type Something here...')
];

if (options.toolbar) {
plugins.push(menu(options.toolbar));
}

return plugins;
};
23 changes: 8 additions & 15 deletions src/lib/ngx-editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@ import { EditorState, Transaction } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Node as ProsemirrorNode } from 'prosemirror-model';

import schema from './schema';

import { Config, ComputedOptions } from './types';
import { NgxEditorService, NgxEditorServiceConfig } from './ngx-editor.service';

import { getPlugins } from './utils/plugins';
import computeOptions from './utils/computeOptions';
import schema from './schema';

@Component({
selector: 'ngx-editor',
Expand All @@ -30,13 +27,14 @@ import computeOptions from './utils/computeOptions';
export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnDestroy {
@ViewChild('ngxEditor', { static: true }) ngxEditor: ElementRef;

@Input() placeholder = 'Type here...';
@Input() config: Config;

private view: EditorView;
private onChange: (value: object) => void;

private options: ComputedOptions;
private config: NgxEditorServiceConfig;

constructor(ngxEditorService: NgxEditorService) {
this.config = ngxEditorService.config;
}

writeValue(value: object | null) {
if (!value) {
Expand Down Expand Up @@ -84,7 +82,7 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnDestr
this.view = new EditorView(this.ngxEditor.nativeElement, {
state: EditorState.create({
schema,
plugins: getPlugins(this.options),
plugins: this.config.plugins,
}),
dispatchTransaction: this.handleTransactions.bind(this),
attributes: {
Expand All @@ -94,11 +92,6 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnDestr
}

ngOnInit() {
this.options = computeOptions({
placeholder: this.placeholder,
config: this.config
});

this.createEditor();
}

Expand Down
27 changes: 25 additions & 2 deletions src/lib/ngx-editor.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
import { NgModule } from '@angular/core';
import { NgModule, ModuleWithProviders, InjectionToken } from '@angular/core';

import { NgxEditorComponent } from './ngx-editor.component';

import { NgxEditorServiceConfig, provideMyServiceOptions } from './ngx-editor.service';
import { NgxEditorConfig } from './types';

const NGX_EDITOR_CONFIG_TOKEN = new InjectionToken<NgxEditorConfig>('NgxEditorConfig');

@NgModule({
declarations: [NgxEditorComponent],
exports: [NgxEditorComponent],
})

export class NgxEditorModule { }
export class NgxEditorModule {
static forRoot(config: NgxEditorConfig): ModuleWithProviders {

return {
ngModule: NgxEditorModule,
providers: [
{
provide: NGX_EDITOR_CONFIG_TOKEN,
useValue: config
},
{
provide: NgxEditorServiceConfig,
useFactory: provideMyServiceOptions,
deps: [NGX_EDITOR_CONFIG_TOKEN]
}
]
};
}
}
31 changes: 31 additions & 0 deletions src/lib/ngx-editor.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Injectable, Optional } from '@angular/core';

import { NgxEditorConfig } from './types';

import menu from './prosemirror/plugins/menu';
import placeholder from './prosemirror/plugins/placeholder';

@Injectable({
providedIn: 'root'
})
export class NgxEditorServiceConfig {
public plugins = [
menu(),
placeholder()
];
}

@Injectable({
providedIn: 'root'
})
export class NgxEditorService {
config: NgxEditorServiceConfig;

constructor(@Optional() config?: NgxEditorServiceConfig) {
this.config = config;
}
}

export function provideMyServiceOptions(config?: NgxEditorConfig): NgxEditorConfig {
return (config);
}
8 changes: 0 additions & 8 deletions src/lib/plugins/menu/i18n.ts

This file was deleted.

20 changes: 0 additions & 20 deletions src/lib/plugins/menu/index.ts

This file was deleted.

2 changes: 2 additions & 0 deletions src/lib/prosemirror/commands/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './toggleBlockType';
export * from './toggleList';
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { setBlockType } from 'prosemirror-commands';

import isNodeActive from '../helpers/isNodeActive';

export default function toggleBlockType(type: NodeType, toggleType: NodeType, attrs = {}) {
export const toggleBlockType = (type: NodeType, toggleType: NodeType, attrs = {}) => {
return (state: EditorState, dispatch: (tr: Transaction) => void) => {
const isActive = isNodeActive(state, type, attrs);

Expand All @@ -14,4 +14,4 @@ export default function toggleBlockType(type: NodeType, toggleType: NodeType, at

return setBlockType(type, attrs)(state, dispatch);
};
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { wrapInList, liftListItem } from 'prosemirror-schema-list';

import isNodeActive from '../helpers/isNodeActive';

export default function toggleList(type: NodeType, itemType: NodeType) {
export const toggleList = (type: NodeType, itemType: NodeType) => {
return (state: EditorState, dispatch: (tr: Transaction) => void) => {
const isActive = isNodeActive(state, type);

Expand All @@ -14,4 +14,4 @@ export default function toggleList(type: NodeType, itemType: NodeType) {

return wrapInList(type)(state, dispatch);
};
}
};
2 changes: 2 additions & 0 deletions src/lib/prosemirror/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './isMarkActive';
export * from './isNodeActive';
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EditorState } from 'prosemirror-state';
import { MarkType } from 'prosemirror-model';

const isMarkActive = (state: EditorState, type: MarkType): boolean => {
export const isMarkActive = (state: EditorState, type: MarkType): boolean => {
const { from, $from, to, empty } = state.selection;

if (empty) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EditorState } from 'prosemirror-state';
import { NodeType, Node as ProsemirrorNode } from 'prosemirror-model';
import { findSelectedNodeOfType, findParentNode } from 'prosemirror-utils';

const isNodeActive = (state: EditorState, type: NodeType, attrs = {}): boolean => {
export const isNodeActive = (state: EditorState, type: NodeType, attrs = {}): boolean => {
const { $from, to } = state.selection;

const predicate = (n: ProsemirrorNode) => n.type === type;
Expand Down
2 changes: 2 additions & 0 deletions src/lib/prosemirror/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as placeholder } from './placeholder';
export { default as menu } from './menu';
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { EditorView } from 'prosemirror-view';
import { EditorState } from 'prosemirror-state';

import { Toolbar } from '../../types';
import { MenuOptions } from '../../../types';

import { renderMenu } from './menu';

class MenuBarView {
toolbar: Toolbar;
options: MenuOptions;
view: EditorView;

dom: HTMLElement;

updateMenuItems: (state: EditorState) => void;

constructor(toolbar: Toolbar, editorView: EditorView) {
constructor(editorView: EditorView, options: MenuOptions) {
// const menu = getMenu(toolbar);
this.view = editorView;
this.toolbar = toolbar;
this.options = options;

this.render();
this.update();
Expand All @@ -26,7 +26,7 @@ class MenuBarView {
const menuDom = document.createElement('div');
menuDom.className = 'NgxEditor-MenuBar';

const { update } = renderMenu(this.toolbar, this.view, menuDom);
const { update } = renderMenu(this.options, this.view, menuDom);
this.updateMenuItems = update;

this.view.dom.parentNode.insertBefore(menuDom, this.view.dom);
Expand Down
41 changes: 41 additions & 0 deletions src/lib/prosemirror/plugins/menu/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { EditorView } from 'prosemirror-view';
import { Plugin, PluginKey } from 'prosemirror-state';

import { Toolbar, MenuLabels, MenuOptions } from '../../../types';
import MenuBarView from './MenuBarView';

const DEFAULT_TOOLBAR: Toolbar = [
['bold', 'italic'],
['code'],
['ordered_list', 'bullet_list'],
[{ heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] }]
];

const DEFAULT_LABELS: MenuLabels = {
bold: 'Bold',
italics: 'Italics',
code: 'Code',
ordered_list: 'Ordered List',
bullet_list: 'Bullet List',
heading: 'Heading'
};

const DEFAULT_OPTIONS: MenuOptions = {
toolbar: DEFAULT_TOOLBAR,
labels: DEFAULT_LABELS
};

function menuPlugin(options: MenuOptions): Plugin {
return new Plugin({
key: new PluginKey('menu'),
view(editorView: EditorView): MenuBarView {
return new MenuBarView(editorView, options);
},
});
}

const menu = (options: MenuOptions = DEFAULT_OPTIONS) => {
return menuPlugin(options);
};

export default menu;
Loading

0 comments on commit fcdc39b

Please sign in to comment.