From fcdc39b5495fa6c93edfca453474bd484e8cb44e Mon Sep 17 00:00:00 2001 From: Sibiraj <20282546+sibiraj-s@users.noreply.github.com> Date: Fri, 8 May 2020 20:49:32 +0530 Subject: [PATCH] feat: add support for custom plugins --- demo/src/app/app.component.html | 2 +- demo/src/app/app.component.ts | 1 - demo/src/app/app.module.ts | 8 ++- .../plugins.ts => demo/src/app/plugin.ts | 41 ++++++----- src/lib/ngx-editor.component.ts | 23 +++--- src/lib/ngx-editor.module.ts | 27 ++++++- src/lib/ngx-editor.service.ts | 31 ++++++++ src/lib/plugins/menu/i18n.ts | 8 --- src/lib/plugins/menu/index.ts | 20 ------ src/lib/prosemirror/commands/index.ts | 2 + .../commands/toggleBlockType.ts | 4 +- .../commands/toggleList.ts | 4 +- src/lib/prosemirror/helpers/index.ts | 2 + .../helpers/isMarkActive.ts | 2 +- .../helpers/isNodeActive.ts | 2 +- src/lib/prosemirror/plugins/index.ts | 2 + .../plugins/menu/MenuBarView.ts | 10 +-- src/lib/prosemirror/plugins/menu/index.ts | 41 +++++++++++ .../{ => prosemirror}/plugins/menu/menu.ts | 42 ++++++----- .../{ => prosemirror}/plugins/menu/meta.ts | 21 +++++- .../{ => prosemirror}/plugins/placeholder.ts | 4 +- src/lib/types.ts | 28 +++----- src/lib/utils/computeOptions.ts | 49 ------------- src/ng-package.json | 4 -- src/package.json | 4 -- src/public-api.ts | 4 ++ wiki/content/configuartion.md | 51 ++++++------- wiki/content/examples.md | 72 +++++++++++++++++++ wiki/content/shortcuts.md | 12 ---- wiki/summary.json | 4 +- 30 files changed, 312 insertions(+), 213 deletions(-) rename src/lib/utils/plugins.ts => demo/src/app/plugin.ts (62%) create mode 100644 src/lib/ngx-editor.service.ts delete mode 100644 src/lib/plugins/menu/i18n.ts delete mode 100644 src/lib/plugins/menu/index.ts create mode 100644 src/lib/prosemirror/commands/index.ts rename src/lib/{pm-tools => prosemirror}/commands/toggleBlockType.ts (83%) rename src/lib/{pm-tools => prosemirror}/commands/toggleList.ts (86%) create mode 100644 src/lib/prosemirror/helpers/index.ts rename src/lib/{pm-tools => prosemirror}/helpers/isMarkActive.ts (80%) rename src/lib/{pm-tools => prosemirror}/helpers/isNodeActive.ts (87%) create mode 100644 src/lib/prosemirror/plugins/index.ts rename src/lib/{ => prosemirror}/plugins/menu/MenuBarView.ts (75%) create mode 100644 src/lib/prosemirror/plugins/menu/index.ts rename src/lib/{ => prosemirror}/plugins/menu/menu.ts (91%) rename src/lib/{ => prosemirror}/plugins/menu/meta.ts (70%) rename src/lib/{ => prosemirror}/plugins/placeholder.ts (87%) delete mode 100644 src/lib/utils/computeOptions.ts create mode 100644 wiki/content/examples.md delete mode 100644 wiki/content/shortcuts.md diff --git a/demo/src/app/app.component.html b/demo/src/app/app.component.html index 0daa7f29..89766a05 100644 --- a/demo/src/app/app.component.html +++ b/demo/src/app/app.component.html @@ -13,7 +13,7 @@
- +
diff --git a/demo/src/app/app.component.ts b/demo/src/app/app.component.ts index e45142a1..041bda71 100644 --- a/demo/src/app/app.component.ts +++ b/demo/src/app/app.component.ts @@ -8,7 +8,6 @@ import { environment } from '../environments/environment'; }) export class AppComponent { - placeholder = 'Type something here...'; isProdMode = environment.production; editorContent: object = { diff --git a/demo/src/app/app.module.ts b/demo/src/app/app.module.ts index 843e2a9f..2da10f16 100644 --- a/demo/src/app/app.module.ts +++ b/demo/src/app/app.module.ts @@ -1,3 +1,4 @@ +import { CommonModule } from '@angular/common'; import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; @@ -5,14 +6,19 @@ 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] }) diff --git a/src/lib/utils/plugins.ts b/demo/src/app/plugin.ts similarity index 62% rename from src/lib/utils/plugins.ts rename to demo/src/app/plugin.ts index 01014d07..8efe91a0 100644 --- a/src/lib/utils/plugins.ts +++ b/demo/src/app/plugin.ts @@ -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 = {}; @@ -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(); @@ -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; }; diff --git a/src/lib/ngx-editor.component.ts b/src/lib/ngx-editor.component.ts index 7223772c..db6b397a 100644 --- a/src/lib/ngx-editor.component.ts +++ b/src/lib/ngx-editor.component.ts @@ -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', @@ -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) { @@ -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: { @@ -94,11 +92,6 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnDestr } ngOnInit() { - this.options = computeOptions({ - placeholder: this.placeholder, - config: this.config - }); - this.createEditor(); } diff --git a/src/lib/ngx-editor.module.ts b/src/lib/ngx-editor.module.ts index f60a494c..83bd7c17 100644 --- a/src/lib/ngx-editor.module.ts +++ b/src/lib/ngx-editor.module.ts @@ -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'); + @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] + } + ] + }; + } +} diff --git a/src/lib/ngx-editor.service.ts b/src/lib/ngx-editor.service.ts new file mode 100644 index 00000000..ab1d2996 --- /dev/null +++ b/src/lib/ngx-editor.service.ts @@ -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); +} diff --git a/src/lib/plugins/menu/i18n.ts b/src/lib/plugins/menu/i18n.ts deleted file mode 100644 index 4dd1763f..00000000 --- a/src/lib/plugins/menu/i18n.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const labels = { - bold: 'Bold', - italics: 'Italics', - code: 'Code', - ordered_list: 'Ordered List', - bullet_list: 'Bullet List', - heading: 'Heading' -}; diff --git a/src/lib/plugins/menu/index.ts b/src/lib/plugins/menu/index.ts deleted file mode 100644 index a19b9b4d..00000000 --- a/src/lib/plugins/menu/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { EditorView } from 'prosemirror-view'; -import { Plugin, PluginKey } from 'prosemirror-state'; - -import { Toolbar } from '../../types'; -import MenuBarView from './MenuBarView'; - -function menuPlugin(toolbar: Toolbar): Plugin { - return new Plugin({ - key: new PluginKey('menu'), - view(editorView: EditorView): MenuBarView { - return new MenuBarView(toolbar, editorView); - }, - }); -} - -const menu = (toolbarOptions: Toolbar) => { - return menuPlugin(toolbarOptions); -}; - -export default menu; diff --git a/src/lib/prosemirror/commands/index.ts b/src/lib/prosemirror/commands/index.ts new file mode 100644 index 00000000..d572f7a4 --- /dev/null +++ b/src/lib/prosemirror/commands/index.ts @@ -0,0 +1,2 @@ +export * from './toggleBlockType'; +export * from './toggleList'; diff --git a/src/lib/pm-tools/commands/toggleBlockType.ts b/src/lib/prosemirror/commands/toggleBlockType.ts similarity index 83% rename from src/lib/pm-tools/commands/toggleBlockType.ts rename to src/lib/prosemirror/commands/toggleBlockType.ts index 03f6eef4..eb464f93 100644 --- a/src/lib/pm-tools/commands/toggleBlockType.ts +++ b/src/lib/prosemirror/commands/toggleBlockType.ts @@ -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); @@ -14,4 +14,4 @@ export default function toggleBlockType(type: NodeType, toggleType: NodeType, at return setBlockType(type, attrs)(state, dispatch); }; -} +}; diff --git a/src/lib/pm-tools/commands/toggleList.ts b/src/lib/prosemirror/commands/toggleList.ts similarity index 86% rename from src/lib/pm-tools/commands/toggleList.ts rename to src/lib/prosemirror/commands/toggleList.ts index 4db4aeda..73c5f8dd 100644 --- a/src/lib/pm-tools/commands/toggleList.ts +++ b/src/lib/prosemirror/commands/toggleList.ts @@ -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); @@ -14,4 +14,4 @@ export default function toggleList(type: NodeType, itemType: NodeType) { return wrapInList(type)(state, dispatch); }; -} +}; diff --git a/src/lib/prosemirror/helpers/index.ts b/src/lib/prosemirror/helpers/index.ts new file mode 100644 index 00000000..54ea7b32 --- /dev/null +++ b/src/lib/prosemirror/helpers/index.ts @@ -0,0 +1,2 @@ +export * from './isMarkActive'; +export * from './isNodeActive'; diff --git a/src/lib/pm-tools/helpers/isMarkActive.ts b/src/lib/prosemirror/helpers/isMarkActive.ts similarity index 80% rename from src/lib/pm-tools/helpers/isMarkActive.ts rename to src/lib/prosemirror/helpers/isMarkActive.ts index 237952aa..a61fb97d 100644 --- a/src/lib/pm-tools/helpers/isMarkActive.ts +++ b/src/lib/prosemirror/helpers/isMarkActive.ts @@ -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) { diff --git a/src/lib/pm-tools/helpers/isNodeActive.ts b/src/lib/prosemirror/helpers/isNodeActive.ts similarity index 87% rename from src/lib/pm-tools/helpers/isNodeActive.ts rename to src/lib/prosemirror/helpers/isNodeActive.ts index 1e7433d1..fc855a37 100644 --- a/src/lib/pm-tools/helpers/isNodeActive.ts +++ b/src/lib/prosemirror/helpers/isNodeActive.ts @@ -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; diff --git a/src/lib/prosemirror/plugins/index.ts b/src/lib/prosemirror/plugins/index.ts new file mode 100644 index 00000000..2135df8d --- /dev/null +++ b/src/lib/prosemirror/plugins/index.ts @@ -0,0 +1,2 @@ +export { default as placeholder } from './placeholder'; +export { default as menu } from './menu'; diff --git a/src/lib/plugins/menu/MenuBarView.ts b/src/lib/prosemirror/plugins/menu/MenuBarView.ts similarity index 75% rename from src/lib/plugins/menu/MenuBarView.ts rename to src/lib/prosemirror/plugins/menu/MenuBarView.ts index 455d0c9b..ef3dc6bb 100644 --- a/src/lib/plugins/menu/MenuBarView.ts +++ b/src/lib/prosemirror/plugins/menu/MenuBarView.ts @@ -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(); @@ -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); diff --git a/src/lib/prosemirror/plugins/menu/index.ts b/src/lib/prosemirror/plugins/menu/index.ts new file mode 100644 index 00000000..96156d15 --- /dev/null +++ b/src/lib/prosemirror/plugins/menu/index.ts @@ -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; diff --git a/src/lib/plugins/menu/menu.ts b/src/lib/prosemirror/plugins/menu/menu.ts similarity index 91% rename from src/lib/plugins/menu/menu.ts rename to src/lib/prosemirror/plugins/menu/menu.ts index a41d6cc8..6ab4c5b3 100644 --- a/src/lib/plugins/menu/menu.ts +++ b/src/lib/prosemirror/plugins/menu/menu.ts @@ -4,27 +4,23 @@ import { EditorState } from 'prosemirror-state'; import { MarkType, NodeType } from 'prosemirror-model'; import { - Toolbar, - MenuItemMeta, MenuItemViewSpec, ToolbarItem, ToolbarDropdownGroupKeys, - ToolbarDropdownGroupValues -} from '../../types'; + ToolbarDropdownGroupValues, + MenuOptions +} from '../../../types'; -import schema from '../../schema'; +import schema from '../../../schema'; -import isNodeActive from '../../pm-tools/helpers/isNodeActive'; -import isMarkActive from '../../pm-tools/helpers/isMarkActive'; -import toggleList from '../../pm-tools/commands/toggleList'; -import toggleBlockType from '../../pm-tools/commands/toggleBlockType'; +import isNodeActive from '../../helpers/isNodeActive'; +import isMarkActive from '../../helpers/isMarkActive'; +import { toggleList, toggleBlockType } from '../../commands'; -import { getIconSvg } from '../../utils/icons'; -import flatDeep from '../../utils/flatDeep'; +import { getIconSvg } from '../../../utils/icons'; +import flatDeep from '../../../utils/flatDeep'; -import menuItemsMeta from './meta'; - -import { labels } from './i18n'; +import menuItemsMeta, {MenuItemMeta} from './meta'; const MENU_ITEM_CLASSNAME = 'NgxEditor-MenuItem'; @@ -43,6 +39,8 @@ class DropDownView { private dropdownGroup: ToolbarDropdownGroupKeys; private dropdownFields: ToolbarDropdownGroupValues; private editorView: EditorView; + private options: MenuOptions; + dom: HTMLElement; updates = []; @@ -50,17 +48,21 @@ class DropDownView { constructor( dropdownGroup: ToolbarDropdownGroupKeys, dropdownFields: ToolbarDropdownGroupValues, - editorView: EditorView + editorView: EditorView, + options: MenuOptions ) { this.dropdownGroup = dropdownGroup; this.dropdownFields = dropdownFields; this.editorView = editorView; + this.options = options; } getWrapperDom(): HTMLElement { let isDropdownOpen = false; const dropdownWrapper = document.createElement('div'); + const labels = this.options.labels; + dropdownWrapper.classList.add(MENU_ITEM_CLASSNAME); dropdownWrapper.classList.add(`${MENU_ITEM_CLASSNAME}__Dropdown-Wrapper`); @@ -286,9 +288,11 @@ const getSeperatorDom = (): HTMLElement => { return div; }; -export const renderMenu = (toolbar: Toolbar, editorView: EditorView, menuDom: HTMLElement) => { +export const renderMenu = (options: MenuOptions, editorView: EditorView, menuDom: HTMLElement) => { const updates: any[] = []; + const toolbar = options.toolbar; + toolbar.forEach((group: ToolbarItem[], toolbarIndex: number): void => { const isLastMenuGroup = toolbar.length - 1 === toolbarIndex; @@ -301,7 +305,7 @@ export const renderMenu = (toolbar: Toolbar, editorView: EditorView, menuDom: HT if (DROPDOWN_ITEMS.has(dropdownGroup)) { const dropdown: ToolbarDropdownGroupValues = toolbarItem[dropdownGroup]; - const dropdownView = new DropDownView(dropdownGroup, dropdown, editorView); + const dropdownView = new DropDownView(dropdownGroup, dropdown, editorView, options); const rendered = dropdownView.render(); updates.push(rendered.updates); menuDom.appendChild(rendered.dom); @@ -315,6 +319,8 @@ export const renderMenu = (toolbar: Toolbar, editorView: EditorView, menuDom: HT if (typeof toolbarItem === 'string') { const menuItem = menuItemsMeta[toolbarItem]; + const labels = options.labels; + if (menuItem) { const spec: MenuItemViewSpec = { classNames: [ @@ -324,7 +330,7 @@ export const renderMenu = (toolbar: Toolbar, editorView: EditorView, menuDom: HT ], innerHTML: getIconSvg(menuItem.icon), attrs: { - title: menuItem.key + title: labels[menuItem.i18nKey] } }; diff --git a/src/lib/plugins/menu/meta.ts b/src/lib/prosemirror/plugins/menu/meta.ts similarity index 70% rename from src/lib/plugins/menu/meta.ts rename to src/lib/prosemirror/plugins/menu/meta.ts index ee60ea47..fc5c5aac 100644 --- a/src/lib/plugins/menu/meta.ts +++ b/src/lib/prosemirror/plugins/menu/meta.ts @@ -1,33 +1,47 @@ -import { MenuItemMeta } from '../../types'; +export interface MenuItemMeta { + key: string; + i18nKey: string; + icon?: string; + type: 'mark' | 'node'; + attrs?: { + level?: number + }; +} const menuItemsMeta: { [key: string]: MenuItemMeta } = { bold: { key: 'strong', + i18nKey: 'bold', icon: 'bold', type: 'mark', }, italic: { key: 'em', + i18nKey: 'italics', icon: 'italic', type: 'mark', }, code: { key: 'code', + i18nKey: 'code', icon: 'code', type: 'mark', }, ordered_list: { key: 'ordered_list', + i18nKey: 'ordered_list', icon: 'ordered_list', type: 'node', }, bullet_list: { key: 'bullet_list', + i18nKey: 'bullet_list', icon: 'bullet_list', type: 'node', }, h1: { key: 'heading', + i18nKey: 'heading', attrs: { level: 1, }, @@ -35,6 +49,7 @@ const menuItemsMeta: { [key: string]: MenuItemMeta } = { }, h2: { key: 'heading', + i18nKey: 'heading', attrs: { level: 2, }, @@ -42,6 +57,7 @@ const menuItemsMeta: { [key: string]: MenuItemMeta } = { }, h3: { key: 'heading', + i18nKey: 'heading', attrs: { level: 3, }, @@ -49,6 +65,7 @@ const menuItemsMeta: { [key: string]: MenuItemMeta } = { }, h4: { key: 'heading', + i18nKey: 'heading', attrs: { level: 4, }, @@ -56,6 +73,7 @@ const menuItemsMeta: { [key: string]: MenuItemMeta } = { }, h5: { key: 'heading', + i18nKey: 'heading', attrs: { level: 5, }, @@ -63,6 +81,7 @@ const menuItemsMeta: { [key: string]: MenuItemMeta } = { }, h6: { key: 'heading', + i18nKey: 'heading', attrs: { level: 6, }, diff --git a/src/lib/plugins/placeholder.ts b/src/lib/prosemirror/plugins/placeholder.ts similarity index 87% rename from src/lib/plugins/placeholder.ts rename to src/lib/prosemirror/plugins/placeholder.ts index 645c409c..08e385e9 100644 --- a/src/lib/plugins/placeholder.ts +++ b/src/lib/prosemirror/plugins/placeholder.ts @@ -1,7 +1,9 @@ import { Plugin, EditorState, PluginKey } from 'prosemirror-state'; import { DecorationSet, Decoration } from 'prosemirror-view'; -function placeholderPlugin(text: string) { +const DEFAULT_PLACEHOLDER = 'Type Here...'; + +function placeholderPlugin(text: string = DEFAULT_PLACEHOLDER) { return new Plugin({ key: new PluginKey('placeholder'), props: { diff --git a/src/lib/types.ts b/src/lib/types.ts index e6c94dcb..26ef5d06 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,24 +1,11 @@ +import { Plugin } from 'prosemirror-state'; + export type ToolbarDropdown = { heading?: string[] }; export type ToolbarDropdownGroupKeys = keyof ToolbarDropdown; export type ToolbarDropdownGroupValues = ToolbarDropdown[ToolbarDropdownGroupKeys]; export type ToolbarItem = string | ToolbarDropdown; export type Toolbar = Array | null; -export interface Config { - toolbar: boolean | Toolbar; -} - -export interface MenuItemMeta { - key: string; - icon?: string; - type: 'mark' | 'node'; - label?: string; - attrs?: { - level?: number - }; - command?: any; -} - export interface MenuItemViewSpec { classNames?: string[]; innerHTML?: string; @@ -26,9 +13,12 @@ export interface MenuItemViewSpec { attrs?: { [key: string]: string }; } -export interface ComputedOptions extends Config { - placeholder: string; - toolbar: Toolbar; +export interface NgxEditorConfig { + plugins: Plugin[]; } -export type KeyMap = { [key: string]: any }; +export type MenuLabels = { [key: string]: string }; +export interface MenuOptions { + toolbar?: Toolbar; + labels?: MenuLabels; +} diff --git a/src/lib/utils/computeOptions.ts b/src/lib/utils/computeOptions.ts deleted file mode 100644 index 9763c7ab..00000000 --- a/src/lib/utils/computeOptions.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ComputedOptions, Config, Toolbar } from '../types'; - -interface Options { - placeholder: string; - config: Config; -} - -const defaultToolbarOptions: Toolbar = [ - ['bold', 'italic'], - ['code'], - ['ordered_list', 'bullet_list'], - [{ heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] }] -]; - -const defaultConfig: Config = { - toolbar: true -}; - -const getToolbar = (config: Config): Toolbar => { - let toolbar = null; - - // set toolbar options - if (typeof config.toolbar === 'boolean') { - if (config.toolbar) { - toolbar = defaultToolbarOptions; - } - } else { - toolbar = config.toolbar; - } - - return toolbar; -}; - -// prefer attributes to config -const computeOptions = (options: Options): ComputedOptions => { - const computedOptions = {} as ComputedOptions; - - const config = Object.assign({}, defaultConfig, options.config); - - computedOptions.toolbar = getToolbar(config); - - if (options.placeholder) { - computedOptions.placeholder = options.placeholder; - } - - return computedOptions; -}; - -export default computeOptions; diff --git a/src/ng-package.json b/src/ng-package.json index d9259b48..5b16347a 100644 --- a/src/ng-package.json +++ b/src/ng-package.json @@ -9,8 +9,6 @@ "prosemirror-model": "prosemirrorModel", "prosemirror-schema-basic": "prosemirrorSchemaBasic", "prosemirror-schema-list": "prosemirrorSchemaList", - "prosemirror-history": "prosemirrorHistory", - "prosemirror-keymap": "prosemirrorKeymap", "prosemirror-commands": "prosemirrorCommands", "prosemirror-utils": "prosemirrorUtils" } @@ -21,8 +19,6 @@ "prosemirror-model", "prosemirror-schema-basic", "prosemirror-schema-list", - "prosemirror-history", - "prosemirror-keymap", "prosemirror-commands", "prosemirror-utils", "prosemirror-tables" diff --git a/src/package.json b/src/package.json index 3391c2e0..08408b85 100644 --- a/src/package.json +++ b/src/package.json @@ -21,16 +21,12 @@ }, "dependencies": { "prosemirror-commands": "^1.1.0", - "prosemirror-history": "^1.1.0", - "prosemirror-keymap": "^1.1.0", "prosemirror-model": "^1.1.0", "prosemirror-schema-basic": "^1.1.0", "prosemirror-schema-list": "^1.1.0", "prosemirror-state": "^1.3.0", "prosemirror-tables": "^1.1.0", - "prosemirror-utils": "^0.9.6", - "prosemirror-view": "^1.14.0" } } diff --git a/src/public-api.ts b/src/public-api.ts index 3f7b6e74..3103cc11 100644 --- a/src/public-api.ts +++ b/src/public-api.ts @@ -4,4 +4,8 @@ export * from './lib/ngx-editor.component'; export * from './lib/ngx-editor.module'; + export { default as schema } from './lib/schema'; +export * from './lib/prosemirror/plugins'; +export * from './lib/prosemirror/commands'; +export * from './lib/prosemirror/helpers'; diff --git a/wiki/content/configuartion.md b/wiki/content/configuartion.md index 06da46f5..adaa23c4 100644 --- a/wiki/content/configuartion.md +++ b/wiki/content/configuartion.md @@ -4,33 +4,28 @@ The Configuration can be provided using `config` property ## Usage -```html - - -``` - -### Default Configuration - -The config property is a JSON object. Each array is a group which is seperated by sperator. - -```jsonc -{ - "toolbar": [ - ["bold", "italic", "code"], // inline icons - ["ordered_list", "bullet_list"], - [{ "heading": ["h1", "h2", "h3", "h4", "h5", "h6"] }] // dropdown - ] -} -``` - -Configuration Type definition - ```ts -interface Config { - toolbar: boolean | null | string[][]; -} +import { menu, placeholder } from "ngx-editor"; + +NgxEditorModule.forRoot({ + plugins: [ + menu({ + // default options (Optional) + toolbar: [ + ["bold", "italic", "code"], // inline icons + ["ordered_list", "bullet_list"], + [{ heading: ["h1", "h2", "h3", "h4", "h5", "h6"] }], // dropdown + ], + labels: { + bold: "Bold", + italics: "Italics", + code: "Code", + ordered_list: "Ordered List", + bullet_list: "Bullet List", + heading: "Heading", + }, + }), + placholder("Type something here..."), + ], +}); ``` diff --git a/wiki/content/examples.md b/wiki/content/examples.md new file mode 100644 index 00000000..e9de56cc --- /dev/null +++ b/wiki/content/examples.md @@ -0,0 +1,72 @@ +# Examples + +### Implement undo/redo history + +Reference + +- https://prosemirror.net/docs/ref/#history +- https://prosemirror.net/docs/ref/#keymap + +```ts +import { undo, redo, history } from "prosemirror-history"; +import { keymap } from "prosemirror-keymap"; + +NgxEditorModule.forRoot({ + plugins: [ + history(), // enable history support + keymap({ + // configure shortcuts + "Mod-z": undo, + "Shift-Mod-z": redo, + }), + ], +}); +``` + +### Configure shortcts to work with list items + +Reference + +- https://prosemirror.net/docs/ref/#keymap +- https://prosemirror.net/docs/ref/#schema-list + +```ts +import { schema } from "ngx-editor"; +import { splitListItem, liftListItem, sinkListItem } from "prosemirror-schema-list"; +import { keymap } from "prosemirror-keymap"; + +NgxEditorModule.forRoot({ + plugins: [ + keymap({ + Enter: splitListItem(schema.nodes.list_item), + "Mod-[": liftListItem(schema.nodes.list_item), + "Mod-]": sinkListItem(schema.nodes.list_item), + Tab: sinkListItem(schema.nodes.list_item), + }), + ], +}); +``` + +### Extras + +Reference + +- https://prosemirror.net/docs/ref/#commands +- https://prosemirror.net/docs/ref/#keymap + +```ts +import { schema } from "ngx-editor"; +import { keymap } from "prosemirror-keymap"; +import { toggleMark, baseKeymap } from "prosemirror-commands"; + +NgxEditorModule.forRoot({ + plugins: [ + keymap({ + "Mod-b": toggleMark(schema.marks.strong), // toggle strong + "Mod-i": toggleMark(schema.marks.em), // toggle italics + "Mod-`": toggleMark(schema.marks.code), // toggle code + }), + keymap(baseKeymap), + ], +}); +``` diff --git a/wiki/content/shortcuts.md b/wiki/content/shortcuts.md deleted file mode 100644 index c00c410e..00000000 --- a/wiki/content/shortcuts.md +++ /dev/null @@ -1,12 +0,0 @@ -# Shortcuts - -- **Mod+b** - Toggle Strong -- **Mod+i** - Toggle Emphasis -- **Mod+`** - Toggle Code -- **Mod+Z** - Undo -- **Shift-Mod-z or Mod-y** - Redo -- **Mod-[** - Lift the list item around the selection up into a wrapping list -- **Mod-] or Tab** - Sink the list item around the selection down into an inner list - - -Note: **Mod-** is a shorthand for **Cmd-** on Mac and **Ctrl-** on other platforms. diff --git a/wiki/summary.json b/wiki/summary.json index 129a1bfb..c2a7d4e0 100644 --- a/wiki/summary.json +++ b/wiki/summary.json @@ -8,7 +8,7 @@ "file": "content/ng-model.md" }, { - "title": "Shortcuts", - "file": "content/shortcuts.md" + "title": "Examples", + "file": "content/examples.md" } ]