From fd298b645bfbde1cc7def700e19bfb836590010e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Fri, 23 Oct 2020 10:44:30 +0200 Subject: [PATCH] add core extensions --- packages/core/src/Editor.ts | 11 ++-- packages/core/src/Extension.ts | 6 --- packages/core/src/commands/blur.ts | 17 ------ packages/core/src/commands/clearContent.ts | 13 ----- packages/core/src/commands/deleteSelection.ts | 14 ----- packages/core/src/commands/index.ts | 21 -------- packages/core/src/commands/insertText.ts | 15 ------ packages/core/src/commands/liftListItem.ts | 18 ------- packages/core/src/commands/removeMark.ts | 32 ------------ packages/core/src/commands/removeMarks.ts | 26 ---------- packages/core/src/commands/scrollIntoView.ts | 15 ------ packages/core/src/commands/selectAll.ts | 14 ----- .../core/src/commands/selectParentNode.ts | 14 ----- packages/core/src/commands/setBlockType.ts | 21 -------- packages/core/src/commands/setContent.ts | 27 ---------- packages/core/src/commands/sinkListItem.ts | 18 ------- packages/core/src/commands/splitListItem.ts | 18 ------- packages/core/src/commands/toggleBlockType.ts | 28 ---------- packages/core/src/commands/toggleList.ts | 50 ------------------ packages/core/src/commands/toggleMark.ts | 18 ------- packages/core/src/commands/toggleWrap.ts | 26 ---------- packages/core/src/commands/updateMark.ts | 41 --------------- packages/core/src/extensions/blur.ts | 22 ++++++++ packages/core/src/extensions/clearContent.ts | 18 +++++++ .../core/src/extensions/deleteSelection.ts | 19 +++++++ .../src/{commands => extensions}/focus.ts | 48 +++++++++-------- packages/core/src/extensions/index.ts | 21 ++++++++ .../{commands => extensions}/insertHTML.ts | 37 +++++++------ packages/core/src/extensions/insertText.ts | 20 +++++++ packages/core/src/extensions/liftListItem.ts | 23 ++++++++ packages/core/src/extensions/removeMark.ts | 37 +++++++++++++ packages/core/src/extensions/removeMarks.ts | 31 +++++++++++ .../core/src/extensions/scrollIntoView.ts | 20 +++++++ packages/core/src/extensions/selectAll.ts | 19 +++++++ .../core/src/extensions/selectParentNode.ts | 19 +++++++ packages/core/src/extensions/setBlockType.ts | 23 ++++++++ packages/core/src/extensions/setContent.ts | 28 ++++++++++ packages/core/src/extensions/sinkListItem.ts | 23 ++++++++ packages/core/src/extensions/splitListItem.ts | 23 ++++++++ .../core/src/extensions/toggleBlockType.ts | 29 +++++++++++ packages/core/src/extensions/toggleList.ts | 52 +++++++++++++++++++ packages/core/src/extensions/toggleMark.ts | 23 ++++++++ packages/core/src/extensions/toggleWrap.ts | 29 +++++++++++ packages/core/src/extensions/updateMark.ts | 43 +++++++++++++++ .../extension-collaboration-cursor/index.ts | 2 - packages/extension-collaboration/index.ts | 2 - packages/extension-focus/index.ts | 2 - packages/extension-history/index.ts | 2 - 48 files changed, 577 insertions(+), 501 deletions(-) delete mode 100644 packages/core/src/commands/blur.ts delete mode 100644 packages/core/src/commands/clearContent.ts delete mode 100644 packages/core/src/commands/deleteSelection.ts delete mode 100644 packages/core/src/commands/index.ts delete mode 100644 packages/core/src/commands/insertText.ts delete mode 100644 packages/core/src/commands/liftListItem.ts delete mode 100644 packages/core/src/commands/removeMark.ts delete mode 100644 packages/core/src/commands/removeMarks.ts delete mode 100644 packages/core/src/commands/scrollIntoView.ts delete mode 100644 packages/core/src/commands/selectAll.ts delete mode 100644 packages/core/src/commands/selectParentNode.ts delete mode 100644 packages/core/src/commands/setBlockType.ts delete mode 100644 packages/core/src/commands/setContent.ts delete mode 100644 packages/core/src/commands/sinkListItem.ts delete mode 100644 packages/core/src/commands/splitListItem.ts delete mode 100644 packages/core/src/commands/toggleBlockType.ts delete mode 100644 packages/core/src/commands/toggleList.ts delete mode 100644 packages/core/src/commands/toggleMark.ts delete mode 100644 packages/core/src/commands/toggleWrap.ts delete mode 100644 packages/core/src/commands/updateMark.ts create mode 100644 packages/core/src/extensions/blur.ts create mode 100644 packages/core/src/extensions/clearContent.ts create mode 100644 packages/core/src/extensions/deleteSelection.ts rename packages/core/src/{commands => extensions}/focus.ts (51%) create mode 100644 packages/core/src/extensions/index.ts rename packages/core/src/{commands => extensions}/insertHTML.ts (61%) create mode 100644 packages/core/src/extensions/insertText.ts create mode 100644 packages/core/src/extensions/liftListItem.ts create mode 100644 packages/core/src/extensions/removeMark.ts create mode 100644 packages/core/src/extensions/removeMarks.ts create mode 100644 packages/core/src/extensions/scrollIntoView.ts create mode 100644 packages/core/src/extensions/selectAll.ts create mode 100644 packages/core/src/extensions/selectParentNode.ts create mode 100644 packages/core/src/extensions/setBlockType.ts create mode 100644 packages/core/src/extensions/setContent.ts create mode 100644 packages/core/src/extensions/sinkListItem.ts create mode 100644 packages/core/src/extensions/splitListItem.ts create mode 100644 packages/core/src/extensions/toggleBlockType.ts create mode 100644 packages/core/src/extensions/toggleList.ts create mode 100644 packages/core/src/extensions/toggleMark.ts create mode 100644 packages/core/src/extensions/toggleWrap.ts create mode 100644 packages/core/src/extensions/updateMark.ts diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index 5843a5e1..844fec16 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -16,7 +16,7 @@ import ExtensionManager from './ExtensionManager' import EventEmitter from './EventEmitter' import { Extensions, UnionToIntersection, PickValue } from './types' import defaultPlugins from './plugins' -import * as coreCommands from './commands' +import * as extensions from './extensions' import style from './style' export type Command = (props: { @@ -56,7 +56,7 @@ interface HTMLElement { interface EditorOptions { element: Element, content: EditorContent, - extensions: Extensions[], + extensions: Extensions, injectCSS: boolean, autoFocus: 'start' | 'end' | number | boolean | null, editable: boolean, @@ -110,7 +110,7 @@ export class Editor extends EventEmitter { this.createExtensionManager() this.createSchema() this.createView() - this.registerCommands(coreCommands) + // this.registerCommands(coreCommands) this.injectCSS() window.setTimeout(() => this.proxy.focus(this.options.autoFocus), 0) @@ -226,7 +226,10 @@ export class Editor extends EventEmitter { * Creates an extension manager. */ private createExtensionManager() { - this.extensionManager = new ExtensionManager(this.options.extensions, this.proxy) + const coreExtensions = Object.entries(extensions).map(([, extension]) => extension()) + const allExtensions = [...coreExtensions, ...this.options.extensions] + + this.extensionManager = new ExtensionManager(allExtensions, this.proxy) } /** diff --git a/packages/core/src/Extension.ts b/packages/core/src/Extension.ts index e06520b2..78e1d79f 100644 --- a/packages/core/src/Extension.ts +++ b/packages/core/src/Extension.ts @@ -4,11 +4,6 @@ import { Editor } from './Editor' import { GlobalAttributes } from './types' export interface ExtensionSpec { - /** - * The name of your extension - */ - name: string, - /** * Default options */ @@ -84,7 +79,6 @@ export type Extension = Required & { */ export const defaultExtension: Extension = { type: 'extension', - name: 'extension', options: {}, addGlobalAttributes: () => [], addCommands: () => ({}), diff --git a/packages/core/src/commands/blur.ts b/packages/core/src/commands/blur.ts deleted file mode 100644 index ec46a8ba..00000000 --- a/packages/core/src/commands/blur.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Command } from '../Editor' - -type BlurCommand = () => Command - -declare module '../Editor' { - interface Commands { - blur: BlurCommand, - } -} - -export const blur: BlurCommand = () => ({ view }) => { - const element = view.dom as HTMLElement - - element.blur() - - return true -} diff --git a/packages/core/src/commands/clearContent.ts b/packages/core/src/commands/clearContent.ts deleted file mode 100644 index beb87c55..00000000 --- a/packages/core/src/commands/clearContent.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Command } from '../Editor' - -type ClearContentCommand = (emitUpdate?: Boolean) => Command - -declare module '../Editor' { - interface Commands { - clearContent: ClearContentCommand, - } -} - -export const clearContent: ClearContentCommand = (emitUpdate = false) => ({ commands }) => { - return commands.setContent('', emitUpdate) -} diff --git a/packages/core/src/commands/deleteSelection.ts b/packages/core/src/commands/deleteSelection.ts deleted file mode 100644 index 067cdebd..00000000 --- a/packages/core/src/commands/deleteSelection.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { deleteSelection as originalDeleteSelection } from 'prosemirror-commands' -import { Command } from '../Editor' - -type DeleteSelectionCommand = () => Command - -declare module '../Editor' { - interface Commands { - deleteSelection: DeleteSelectionCommand, - } -} - -export const deleteSelection: DeleteSelectionCommand = () => ({ state, dispatch }) => { - return originalDeleteSelection(state, dispatch) -} diff --git a/packages/core/src/commands/index.ts b/packages/core/src/commands/index.ts deleted file mode 100644 index cd182285..00000000 --- a/packages/core/src/commands/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -export { blur } from './blur' -export { clearContent } from './clearContent' -export { deleteSelection } from './deleteSelection' -export { focus } from './focus' -export { insertHTML } from './insertHTML' -export { insertText } from './insertText' -export { liftListItem } from './liftListItem' -export { removeMark } from './removeMark' -export { removeMarks } from './removeMarks' -export { scrollIntoView } from './scrollIntoView' -export { selectAll } from './selectAll' -export { selectParentNode } from './selectParentNode' -export { setBlockType } from './setBlockType' -export { setContent } from './setContent' -export { sinkListItem } from './sinkListItem' -export { splitListItem } from './splitListItem' -export { toggleBlockType } from './toggleBlockType' -export { toggleList } from './toggleList' -export { toggleMark } from './toggleMark' -export { updateMark } from './updateMark' -export { toggleWrap } from './toggleWrap' diff --git a/packages/core/src/commands/insertText.ts b/packages/core/src/commands/insertText.ts deleted file mode 100644 index e48609e9..00000000 --- a/packages/core/src/commands/insertText.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Command } from '../Editor' - -type InsertTextCommand = (value: string) => Command - -declare module '../Editor' { - interface Commands { - insertText: InsertTextCommand, - } -} - -export const insertText: InsertTextCommand = value => ({ tr }) => { - tr.insertText(value) - - return true -} diff --git a/packages/core/src/commands/liftListItem.ts b/packages/core/src/commands/liftListItem.ts deleted file mode 100644 index ad0db3ad..00000000 --- a/packages/core/src/commands/liftListItem.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { liftListItem as originalLiftListItem } from 'prosemirror-schema-list' -import { NodeType } from 'prosemirror-model' -import { Command } from '../Editor' -import getNodeType from '../utils/getNodeType' - -type LiftListItem = (typeOrName: string | NodeType) => Command - -declare module '../Editor' { - interface Commands { - liftListItem: LiftListItem, - } -} - -export const liftListItem: LiftListItem = typeOrName => ({ state, dispatch }) => { - const type = getNodeType(typeOrName, state.schema) - - return originalLiftListItem(type)(state, dispatch) -} diff --git a/packages/core/src/commands/removeMark.ts b/packages/core/src/commands/removeMark.ts deleted file mode 100644 index 00971361..00000000 --- a/packages/core/src/commands/removeMark.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MarkType } from 'prosemirror-model' -import { Command } from '../Editor' -import getMarkType from '../utils/getMarkType' -import getMarkRange from '../utils/getMarkRange' - -type RemoveMarkCommand = (typeOrName: string | MarkType) => Command - -declare module '../Editor' { - interface Commands { - removeMark: RemoveMarkCommand, - } -} - -export const removeMark: RemoveMarkCommand = typeOrName => ({ tr, state }) => { - const { selection } = tr - const type = getMarkType(typeOrName, state.schema) - let { from, to } = selection - const { $from, empty } = selection - - if (empty) { - const range = getMarkRange($from, type) - - if (range) { - from = range.from - to = range.to - } - } - - tr.removeMark(from, to, type) - - return true -} diff --git a/packages/core/src/commands/removeMarks.ts b/packages/core/src/commands/removeMarks.ts deleted file mode 100644 index d2eb17c4..00000000 --- a/packages/core/src/commands/removeMarks.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Command } from '../Editor' - -type RemoveMarksCommand = () => Command - -declare module '../Editor' { - interface Commands { - removeMarks: RemoveMarksCommand, - } -} - -export const removeMarks: RemoveMarksCommand = () => ({ tr, state }) => { - const { selection } = tr - const { from, to, empty } = selection - - if (empty) { - return true - } - - Object - .entries(state.schema.marks) - .forEach(([, mark]) => { - tr.removeMark(from, to, mark as any) - }) - - return true -} diff --git a/packages/core/src/commands/scrollIntoView.ts b/packages/core/src/commands/scrollIntoView.ts deleted file mode 100644 index 142dbee3..00000000 --- a/packages/core/src/commands/scrollIntoView.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Command } from '../Editor' - -type ScrollIntoViewCommand = () => Command - -declare module '../Editor' { - interface Commands { - scrollIntoView: ScrollIntoViewCommand, - } -} - -export const scrollIntoView: ScrollIntoViewCommand = () => ({ tr }) => { - tr.scrollIntoView() - - return true -} diff --git a/packages/core/src/commands/selectAll.ts b/packages/core/src/commands/selectAll.ts deleted file mode 100644 index 6aca7b86..00000000 --- a/packages/core/src/commands/selectAll.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { selectAll as originalSelectAll } from 'prosemirror-commands' -import { Command } from '../Editor' - -type SelectAllCommand = () => Command - -declare module '../Editor' { - interface Commands { - selectAll: SelectAllCommand, - } -} - -export const selectAll: SelectAllCommand = () => ({ state, dispatch }) => { - return originalSelectAll(state, dispatch) -} diff --git a/packages/core/src/commands/selectParentNode.ts b/packages/core/src/commands/selectParentNode.ts deleted file mode 100644 index 7e51e7c9..00000000 --- a/packages/core/src/commands/selectParentNode.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { selectParentNode as originalSelectParentNode } from 'prosemirror-commands' -import { Command } from '../Editor' - -type SelectParentNodeCommand = () => Command - -declare module '../Editor' { - interface Commands { - selectParentNode: SelectParentNodeCommand, - } -} - -export const selectParentNode: SelectParentNodeCommand = () => ({ state, dispatch }) => { - return originalSelectParentNode(state, dispatch) -} diff --git a/packages/core/src/commands/setBlockType.ts b/packages/core/src/commands/setBlockType.ts deleted file mode 100644 index af1fc866..00000000 --- a/packages/core/src/commands/setBlockType.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NodeType } from 'prosemirror-model' -import { setBlockType as originalSetBlockType } from 'prosemirror-commands' -import { Command } from '../Editor' -import getNodeType from '../utils/getNodeType' - -type SetBlockTypeCommand = ( - typeOrName: string | NodeType, - attrs?: {}, -) => Command - -declare module '../Editor' { - interface Commands { - setBlockType: SetBlockTypeCommand, - } -} - -export const setBlockType: SetBlockTypeCommand = (typeOrName, attrs = {}) => ({ state, dispatch }) => { - const type = getNodeType(typeOrName, state.schema) - - return originalSetBlockType(type, attrs)(state, dispatch) -} diff --git a/packages/core/src/commands/setContent.ts b/packages/core/src/commands/setContent.ts deleted file mode 100644 index fb1c5d1e..00000000 --- a/packages/core/src/commands/setContent.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { TextSelection } from 'prosemirror-state' -import { Command } from '../Editor' - -type SetContentCommand = ( - content: string, - emitUpdate?: Boolean, - parseOptions?: any, -) => Command - -declare module '../Editor' { - interface Commands { - setContent: SetContentCommand, - } -} - -export const setContent: SetContentCommand = (content = '', emitUpdate = false, parseOptions = {}) => ({ tr, editor }) => { - const { createDocument } = editor - const { doc } = tr - const document = createDocument(content, parseOptions) - const selection = TextSelection.create(doc, 0, doc.content.size) - - tr.setSelection(selection) - .replaceSelectionWith(document, false) - .setMeta('preventUpdate', !emitUpdate) - - return true -} diff --git a/packages/core/src/commands/sinkListItem.ts b/packages/core/src/commands/sinkListItem.ts deleted file mode 100644 index 04dea8b9..00000000 --- a/packages/core/src/commands/sinkListItem.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { sinkListItem as originalSinkListItem } from 'prosemirror-schema-list' -import { NodeType } from 'prosemirror-model' -import { Command } from '../Editor' -import getNodeType from '../utils/getNodeType' - -type SinkListItem = (typeOrName: string | NodeType) => Command - -declare module '../Editor' { - interface Commands { - sinkListItem: SinkListItem, - } -} - -export const sinkListItem: SinkListItem = typeOrName => ({ state, dispatch }) => { - const type = getNodeType(typeOrName, state.schema) - - return originalSinkListItem(type)(state, dispatch) -} diff --git a/packages/core/src/commands/splitListItem.ts b/packages/core/src/commands/splitListItem.ts deleted file mode 100644 index 807eee36..00000000 --- a/packages/core/src/commands/splitListItem.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { splitListItem as originalSplitListItem } from 'prosemirror-schema-list' -import { NodeType } from 'prosemirror-model' -import { Command } from '../Editor' -import getNodeType from '../utils/getNodeType' - -type SplitListItem = (typeOrName: string | NodeType) => Command - -declare module '../Editor' { - interface Commands { - splitListItem: SplitListItem, - } -} - -export const splitListItem: SplitListItem = typeOrName => ({ state, dispatch }) => { - const type = getNodeType(typeOrName, state.schema) - - return originalSplitListItem(type)(state, dispatch) -} diff --git a/packages/core/src/commands/toggleBlockType.ts b/packages/core/src/commands/toggleBlockType.ts deleted file mode 100644 index abf47426..00000000 --- a/packages/core/src/commands/toggleBlockType.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NodeType } from 'prosemirror-model' -import { Command } from '../Editor' -import nodeIsActive from '../utils/nodeIsActive' -import getNodeType from '../utils/getNodeType' - -type ToggleBlockTypeCommand = ( - typeOrName: string | NodeType, - toggleType: string | NodeType, - attrs?: {} -) => Command - -declare module '../Editor' { - interface Commands { - toggleBlockType: ToggleBlockTypeCommand, - } -} - -export const toggleBlockType: ToggleBlockTypeCommand = (typeOrName, toggleTypeOrName, attrs = {}) => ({ state, commands }) => { - const type = getNodeType(typeOrName, state.schema) - const toggleType = getNodeType(toggleTypeOrName, state.schema) - const isActive = nodeIsActive(state, type, attrs) - - if (isActive) { - return commands.setBlockType(toggleType) - } - - return commands.setBlockType(type, attrs) -} diff --git a/packages/core/src/commands/toggleList.ts b/packages/core/src/commands/toggleList.ts deleted file mode 100644 index 2a2238cd..00000000 --- a/packages/core/src/commands/toggleList.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { wrapInList, liftListItem } from 'prosemirror-schema-list' -import { findParentNode } from 'prosemirror-utils' -import { Node, NodeType, Schema } from 'prosemirror-model' -import { Command } from '../Editor' -import getNodeType from '../utils/getNodeType' - -type ToggleListCommand = ( - listType: string | NodeType, - itemType: string | NodeType, -) => Command - -declare module '../Editor' { - interface Commands { - toggleList: ToggleListCommand, - } -} - -function isList(node: Node, schema: Schema) { - return (node.type === schema.nodes.bullet_list - || node.type === schema.nodes.ordered_list - || node.type === schema.nodes.todo_list) -} - -export const toggleList: ToggleListCommand = (listTypeOrName, itemTypeOrName) => ({ tr, state, dispatch }) => { - const listType = getNodeType(listTypeOrName, state.schema) - const itemType = getNodeType(itemTypeOrName, state.schema) - const { schema, selection } = state - const { $from, $to } = selection - const range = $from.blockRange($to) - - if (!range) { - return false - } - - const parentList = findParentNode(node => isList(node, schema))(selection) - - if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) { - if (parentList.node.type === listType) { - return liftListItem(itemType)(state, dispatch) - } - - if (isList(parentList.node, schema) && listType.validContent(parentList.node.content)) { - tr.setNodeMarkup(parentList.pos, listType) - - return false - } - } - - return wrapInList(listType)(state, dispatch) -} diff --git a/packages/core/src/commands/toggleMark.ts b/packages/core/src/commands/toggleMark.ts deleted file mode 100644 index 65aacab7..00000000 --- a/packages/core/src/commands/toggleMark.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { toggleMark as originalToggleMark } from 'prosemirror-commands' -import { MarkType } from 'prosemirror-model' -import { Command } from '../Editor' -import getMarkType from '../utils/getMarkType' - -type ToggleMarkCommand = (typeOrName: string | MarkType) => Command - -declare module '../Editor' { - interface Commands { - toggleMark: ToggleMarkCommand, - } -} - -export const toggleMark: ToggleMarkCommand = typeOrName => ({ state, dispatch }) => { - const type = getMarkType(typeOrName, state.schema) - - return originalToggleMark(type)(state, dispatch) -} diff --git a/packages/core/src/commands/toggleWrap.ts b/packages/core/src/commands/toggleWrap.ts deleted file mode 100644 index e3a17095..00000000 --- a/packages/core/src/commands/toggleWrap.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { wrapIn, lift } from 'prosemirror-commands' -import { NodeType } from 'prosemirror-model' -import { Command } from '../Editor' -import nodeIsActive from '../utils/nodeIsActive' -import getNodeType from '../utils/getNodeType' - -type ToggleWrapCommand = (typeOrName: string | NodeType, attrs?: {}) => Command - -declare module '../Editor' { - interface Commands { - toggleWrap: ToggleWrapCommand, - } -} - -export const toggleWrap: ToggleWrapCommand = (typeOrName, attrs) => ({ - state, dispatch, -}) => { - const type = getNodeType(typeOrName, state.schema) - const isActive = nodeIsActive(state, type, attrs) - - if (isActive) { - return lift(state, dispatch) - } - - return wrapIn(type, attrs)(state, dispatch) -} diff --git a/packages/core/src/commands/updateMark.ts b/packages/core/src/commands/updateMark.ts deleted file mode 100644 index 4e09e46c..00000000 --- a/packages/core/src/commands/updateMark.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { MarkType } from 'prosemirror-model' -import { Command } from '../Editor' -import getMarkType from '../utils/getMarkType' -import getMarkRange from '../utils/getMarkRange' - -type UpdateMarkCommand = ( - typeOrName: string | MarkType, - attrs: {}, -) => Command - -declare module '../Editor' { - interface Commands { - updateMark: UpdateMarkCommand, - } -} - -export const updateMark: UpdateMarkCommand = (typeOrName, attrs = {}) => ({ tr, state }) => { - const { selection, doc } = tr - let { from, to } = selection - const { $from, empty } = selection - const type = getMarkType(typeOrName, state.schema) - - if (empty) { - const range = getMarkRange($from, type) - - if (range) { - from = range.from - to = range.to - } - } - - const hasMark = doc.rangeHasMark(from, to, type) - - if (hasMark) { - tr.removeMark(from, to, type) - } - - tr.addMark(from, to, type.create(attrs)) - - return true -} diff --git a/packages/core/src/extensions/blur.ts b/packages/core/src/extensions/blur.ts new file mode 100644 index 00000000..14dd93e0 --- /dev/null +++ b/packages/core/src/extensions/blur.ts @@ -0,0 +1,22 @@ +import { Command } from '../Editor' +import { createExtension } from '../Extension' + +export const Blur = createExtension({ + addCommands() { + return { + blur: (): Command => ({ view }) => { + const element = view.dom as HTMLElement + + element.blur() + + return true + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + Blur: typeof Blur, + } +} diff --git a/packages/core/src/extensions/clearContent.ts b/packages/core/src/extensions/clearContent.ts new file mode 100644 index 00000000..2efc9463 --- /dev/null +++ b/packages/core/src/extensions/clearContent.ts @@ -0,0 +1,18 @@ +import { Command } from '../Editor' +import { createExtension } from '../Extension' + +export const ClearContent = createExtension({ + addCommands() { + return { + clearContent: (emitUpdate: Boolean = false): Command => ({ commands }) => { + return commands.setContent('', emitUpdate) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + ClearContent: typeof ClearContent, + } +} diff --git a/packages/core/src/extensions/deleteSelection.ts b/packages/core/src/extensions/deleteSelection.ts new file mode 100644 index 00000000..818aeaf2 --- /dev/null +++ b/packages/core/src/extensions/deleteSelection.ts @@ -0,0 +1,19 @@ +import { deleteSelection as originalDeleteSelection } from 'prosemirror-commands' +import { Command } from '../Editor' +import { createExtension } from '../Extension' + +export const DeleteSelection = createExtension({ + addCommands() { + return { + deleteSelection: (): Command => ({ state, dispatch }) => { + return originalDeleteSelection(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + DeleteSelection: typeof DeleteSelection, + } +} diff --git a/packages/core/src/commands/focus.ts b/packages/core/src/extensions/focus.ts similarity index 51% rename from packages/core/src/commands/focus.ts rename to packages/core/src/extensions/focus.ts index 1042852e..8f9ffdb3 100644 --- a/packages/core/src/commands/focus.ts +++ b/packages/core/src/extensions/focus.ts @@ -1,15 +1,9 @@ import { TextSelection } from 'prosemirror-state' import { Editor, Command } from '../Editor' +import { createExtension } from '../Extension' import minMax from '../utils/minMax' type Position = 'start' | 'end' | number | boolean | null -type FocusCommand = (position?: Position) => Command - -declare module '../Editor' { - interface Commands { - focus: FocusCommand - } -} interface ResolvedSelection { from: number, @@ -43,19 +37,31 @@ function resolveSelection(editor: Editor, position: Position = null): ResolvedSe } } -export const focus: FocusCommand = (position = null) => ({ editor, view, tr }) => { - if ((view.hasFocus() && position === null) || position === false) { - return true +export const Focus = createExtension({ + addCommands() { + return { + focus: (position: Position = null): Command => ({ editor, view, tr }) => { + if ((view.hasFocus() && position === null) || position === false) { + return true + } + + const { from, to } = resolveSelection(editor, position) + const { doc } = tr + const resolvedFrom = minMax(from, 0, doc.content.size) + const resolvedEnd = minMax(to, 0, doc.content.size) + const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd) + + tr.setSelection(selection) + view.focus() + + return true + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + Focus: typeof Focus, } - - const { from, to } = resolveSelection(editor, position) - const { doc } = tr - const resolvedFrom = minMax(from, 0, doc.content.size) - const resolvedEnd = minMax(to, 0, doc.content.size) - const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd) - - tr.setSelection(selection) - view.focus() - - return true } diff --git a/packages/core/src/extensions/index.ts b/packages/core/src/extensions/index.ts new file mode 100644 index 00000000..45f4b5a9 --- /dev/null +++ b/packages/core/src/extensions/index.ts @@ -0,0 +1,21 @@ +export { Blur } from './blur' +export { ClearContent } from './clearContent' +export { DeleteSelection } from './deleteSelection' +export { Focus } from './focus' +export { InsertHTML } from './insertHTML' +export { InsertText } from './insertText' +export { LiftListItem } from './liftListItem' +export { RemoveMark } from './removeMark' +export { RemoveMarks } from './removeMarks' +export { ScrollIntoView } from './scrollIntoView' +export { SelectAll } from './selectAll' +export { SelectParentNode } from './selectParentNode' +export { SetBlockType } from './setBlockType' +export { SetContent } from './setContent' +export { SinkListItem } from './sinkListItem' +export { SplitListItem } from './splitListItem' +export { ToggleBlockType } from './toggleBlockType' +export { ToggleList } from './toggleList' +export { ToggleMark } from './toggleMark' +export { UpdateMark } from './updateMark' +export { ToggleWrap } from './toggleWrap' diff --git a/packages/core/src/commands/insertHTML.ts b/packages/core/src/extensions/insertHTML.ts similarity index 61% rename from packages/core/src/commands/insertHTML.ts rename to packages/core/src/extensions/insertHTML.ts index 9d06dddf..2a567a40 100644 --- a/packages/core/src/commands/insertHTML.ts +++ b/packages/core/src/extensions/insertHTML.ts @@ -1,16 +1,9 @@ import { DOMParser } from 'prosemirror-model' import { Selection, Transaction } from 'prosemirror-state' import { ReplaceStep, ReplaceAroundStep } from 'prosemirror-transform' -import { Command } from '../Editor' import elementFromString from '../utils/elementFromString' - -type InsertHTMLCommand = (value: string) => Command - -declare module '../Editor' { - interface Commands { - insertHTML: InsertHTMLCommand, - } -} +import { Command } from '../Editor' +import { createExtension } from '../Extension' // TODO: move to utils // https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.js#L466 @@ -25,13 +18,25 @@ function selectionToInsertionEnd(tr: Transaction, startLen: number, bias: number tr.setSelection(Selection.near(tr.doc.resolve(end as unknown as number), bias)) } -export const insertHTML: InsertHTMLCommand = value => ({ tr, state }) => { - const { selection } = tr - const element = elementFromString(value) - const slice = DOMParser.fromSchema(state.schema).parseSlice(element) +export const InsertHTML = createExtension({ + addCommands() { + return { + insertHTML: (value: string): Command => ({ tr, state }) => { + const { selection } = tr + const element = elementFromString(value) + const slice = DOMParser.fromSchema(state.schema).parseSlice(element) - tr.insert(selection.anchor, slice.content) - selectionToInsertionEnd(tr, tr.steps.length - 1, -1) + tr.insert(selection.anchor, slice.content) + selectionToInsertionEnd(tr, tr.steps.length - 1, -1) - return true + return true + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + InsertHTML: typeof InsertHTML, + } } diff --git a/packages/core/src/extensions/insertText.ts b/packages/core/src/extensions/insertText.ts new file mode 100644 index 00000000..cdef3263 --- /dev/null +++ b/packages/core/src/extensions/insertText.ts @@ -0,0 +1,20 @@ +import { Command } from '../Editor' +import { createExtension } from '../Extension' + +export const InsertText = createExtension({ + addCommands() { + return { + insertText: (value: string): Command => ({ tr }) => { + tr.insertText(value) + + return true + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + InsertText: typeof InsertText, + } +} diff --git a/packages/core/src/extensions/liftListItem.ts b/packages/core/src/extensions/liftListItem.ts new file mode 100644 index 00000000..53043622 --- /dev/null +++ b/packages/core/src/extensions/liftListItem.ts @@ -0,0 +1,23 @@ +import { liftListItem as originalLiftListItem } from 'prosemirror-schema-list' +import { NodeType } from 'prosemirror-model' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import getNodeType from '../utils/getNodeType' + +export const LiftListItem = createExtension({ + addCommands() { + return { + liftListItem: (typeOrName: string | NodeType): Command => ({ state, dispatch }) => { + const type = getNodeType(typeOrName, state.schema) + + return originalLiftListItem(type)(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + LiftListItem: typeof LiftListItem, + } +} diff --git a/packages/core/src/extensions/removeMark.ts b/packages/core/src/extensions/removeMark.ts new file mode 100644 index 00000000..f4463341 --- /dev/null +++ b/packages/core/src/extensions/removeMark.ts @@ -0,0 +1,37 @@ +import { MarkType } from 'prosemirror-model' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import getMarkType from '../utils/getMarkType' +import getMarkRange from '../utils/getMarkRange' + +export const RemoveMark = createExtension({ + addCommands() { + return { + removeMark: (typeOrName: string | MarkType): Command => ({ tr, state }) => { + const { selection } = tr + const type = getMarkType(typeOrName, state.schema) + let { from, to } = selection + const { $from, empty } = selection + + if (empty) { + const range = getMarkRange($from, type) + + if (range) { + from = range.from + to = range.to + } + } + + tr.removeMark(from, to, type) + + return true + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + RemoveMark: typeof RemoveMark, + } +} diff --git a/packages/core/src/extensions/removeMarks.ts b/packages/core/src/extensions/removeMarks.ts new file mode 100644 index 00000000..af527062 --- /dev/null +++ b/packages/core/src/extensions/removeMarks.ts @@ -0,0 +1,31 @@ +import { Command } from '../Editor' +import { createExtension } from '../Extension' + +export const RemoveMarks = createExtension({ + addCommands() { + return { + removeMarks: (): Command => ({ tr, state }) => { + const { selection } = tr + const { from, to, empty } = selection + + if (empty) { + return true + } + + Object + .entries(state.schema.marks) + .forEach(([, mark]) => { + tr.removeMark(from, to, mark as any) + }) + + return true + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + RemoveMarks: typeof RemoveMarks, + } +} diff --git a/packages/core/src/extensions/scrollIntoView.ts b/packages/core/src/extensions/scrollIntoView.ts new file mode 100644 index 00000000..44fee958 --- /dev/null +++ b/packages/core/src/extensions/scrollIntoView.ts @@ -0,0 +1,20 @@ +import { Command } from '../Editor' +import { createExtension } from '../Extension' + +export const ScrollIntoView = createExtension({ + addCommands() { + return { + scrollIntoView: (): Command => ({ tr }) => { + tr.scrollIntoView() + + return true + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + ScrollIntoView: typeof ScrollIntoView, + } +} diff --git a/packages/core/src/extensions/selectAll.ts b/packages/core/src/extensions/selectAll.ts new file mode 100644 index 00000000..0e8b1ccc --- /dev/null +++ b/packages/core/src/extensions/selectAll.ts @@ -0,0 +1,19 @@ +import { selectAll as originalSelectAll } from 'prosemirror-commands' +import { Command } from '../Editor' +import { createExtension } from '../Extension' + +export const SelectAll = createExtension({ + addCommands() { + return { + selectAll: (): Command => ({ state, dispatch }) => { + return originalSelectAll(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + SelectAll: typeof SelectAll, + } +} diff --git a/packages/core/src/extensions/selectParentNode.ts b/packages/core/src/extensions/selectParentNode.ts new file mode 100644 index 00000000..78c39d72 --- /dev/null +++ b/packages/core/src/extensions/selectParentNode.ts @@ -0,0 +1,19 @@ +import { selectParentNode as originalSelectParentNode } from 'prosemirror-commands' +import { Command } from '../Editor' +import { createExtension } from '../Extension' + +export const SelectParentNode = createExtension({ + addCommands() { + return { + selectParentNode: (): Command => ({ state, dispatch }) => { + return originalSelectParentNode(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + SelectParentNode: typeof SelectParentNode, + } +} diff --git a/packages/core/src/extensions/setBlockType.ts b/packages/core/src/extensions/setBlockType.ts new file mode 100644 index 00000000..365f7382 --- /dev/null +++ b/packages/core/src/extensions/setBlockType.ts @@ -0,0 +1,23 @@ +import { NodeType } from 'prosemirror-model' +import { setBlockType as originalSetBlockType } from 'prosemirror-commands' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import getNodeType from '../utils/getNodeType' + +export const SetBlockType = createExtension({ + addCommands() { + return { + setBlockType: (typeOrName: string | NodeType, attrs = {}): Command => ({ state, dispatch }) => { + const type = getNodeType(typeOrName, state.schema) + + return originalSetBlockType(type, attrs)(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + SetBlockType: typeof SetBlockType, + } +} diff --git a/packages/core/src/extensions/setContent.ts b/packages/core/src/extensions/setContent.ts new file mode 100644 index 00000000..f19fb906 --- /dev/null +++ b/packages/core/src/extensions/setContent.ts @@ -0,0 +1,28 @@ +import { TextSelection } from 'prosemirror-state' +import { Command } from '../Editor' +import { createExtension } from '../Extension' + +export const SetContent = createExtension({ + addCommands() { + return { + setContent: (content: string, emitUpdate: Boolean = false, parseOptions = {}): Command => ({ tr, editor }) => { + const { createDocument } = editor + const { doc } = tr + const document = createDocument(content, parseOptions) + const selection = TextSelection.create(doc, 0, doc.content.size) + + tr.setSelection(selection) + .replaceSelectionWith(document, false) + .setMeta('preventUpdate', !emitUpdate) + + return true + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + SetContent: typeof SetContent, + } +} diff --git a/packages/core/src/extensions/sinkListItem.ts b/packages/core/src/extensions/sinkListItem.ts new file mode 100644 index 00000000..e6832a08 --- /dev/null +++ b/packages/core/src/extensions/sinkListItem.ts @@ -0,0 +1,23 @@ +import { sinkListItem as originalSinkListItem } from 'prosemirror-schema-list' +import { NodeType } from 'prosemirror-model' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import getNodeType from '../utils/getNodeType' + +export const SinkListItem = createExtension({ + addCommands() { + return { + sinkListItem: (typeOrName: string | NodeType): Command => ({ state, dispatch }) => { + const type = getNodeType(typeOrName, state.schema) + + return originalSinkListItem(type)(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + SinkListItem: typeof SinkListItem, + } +} diff --git a/packages/core/src/extensions/splitListItem.ts b/packages/core/src/extensions/splitListItem.ts new file mode 100644 index 00000000..4b608d0a --- /dev/null +++ b/packages/core/src/extensions/splitListItem.ts @@ -0,0 +1,23 @@ +import { splitListItem as originalSplitListItem } from 'prosemirror-schema-list' +import { NodeType } from 'prosemirror-model' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import getNodeType from '../utils/getNodeType' + +export const SplitListItem = createExtension({ + addCommands() { + return { + splitListItem: (typeOrName: string | NodeType): Command => ({ state, dispatch }) => { + const type = getNodeType(typeOrName, state.schema) + + return originalSplitListItem(type)(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + SplitListItem: typeof SplitListItem, + } +} diff --git a/packages/core/src/extensions/toggleBlockType.ts b/packages/core/src/extensions/toggleBlockType.ts new file mode 100644 index 00000000..f04838d5 --- /dev/null +++ b/packages/core/src/extensions/toggleBlockType.ts @@ -0,0 +1,29 @@ +import { NodeType } from 'prosemirror-model' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import nodeIsActive from '../utils/nodeIsActive' +import getNodeType from '../utils/getNodeType' + +export const ToggleBlockType = createExtension({ + addCommands() { + return { + toggleBlockType: (typeOrName: string | NodeType, toggleTypeOrName: string | NodeType, attrs = {}): Command => ({ state, commands }) => { + const type = getNodeType(typeOrName, state.schema) + const toggleType = getNodeType(toggleTypeOrName, state.schema) + const isActive = nodeIsActive(state, type, attrs) + + if (isActive) { + return commands.setBlockType(toggleType) + } + + return commands.setBlockType(type, attrs) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + ToggleBlockType: typeof ToggleBlockType, + } +} diff --git a/packages/core/src/extensions/toggleList.ts b/packages/core/src/extensions/toggleList.ts new file mode 100644 index 00000000..2f7a15d3 --- /dev/null +++ b/packages/core/src/extensions/toggleList.ts @@ -0,0 +1,52 @@ +import { wrapInList, liftListItem } from 'prosemirror-schema-list' +import { findParentNode } from 'prosemirror-utils' +import { Node, NodeType, Schema } from 'prosemirror-model' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import getNodeType from '../utils/getNodeType' + +function isList(node: Node, schema: Schema) { + return (node.type === schema.nodes.bullet_list + || node.type === schema.nodes.ordered_list + || node.type === schema.nodes.todo_list) +} + +export const ToggleList = createExtension({ + addCommands() { + return { + toggleList: (listTypeOrName: string | NodeType, itemTypeOrName: string | NodeType): Command => ({ tr, state, dispatch }) => { + const listType = getNodeType(listTypeOrName, state.schema) + const itemType = getNodeType(itemTypeOrName, state.schema) + const { schema, selection } = state + const { $from, $to } = selection + const range = $from.blockRange($to) + + if (!range) { + return false + } + + const parentList = findParentNode(node => isList(node, schema))(selection) + + if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) { + if (parentList.node.type === listType) { + return liftListItem(itemType)(state, dispatch) + } + + if (isList(parentList.node, schema) && listType.validContent(parentList.node.content)) { + tr.setNodeMarkup(parentList.pos, listType) + + return false + } + } + + return wrapInList(listType)(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + ToggleList: typeof ToggleList, + } +} diff --git a/packages/core/src/extensions/toggleMark.ts b/packages/core/src/extensions/toggleMark.ts new file mode 100644 index 00000000..e3de9adb --- /dev/null +++ b/packages/core/src/extensions/toggleMark.ts @@ -0,0 +1,23 @@ +import { toggleMark as originalToggleMark } from 'prosemirror-commands' +import { MarkType } from 'prosemirror-model' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import getMarkType from '../utils/getMarkType' + +export const ToggleMark = createExtension({ + addCommands() { + return { + toggleMark: (typeOrName: string | MarkType): Command => ({ state, dispatch }) => { + const type = getMarkType(typeOrName, state.schema) + + return originalToggleMark(type)(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + ToggleMark: typeof ToggleMark, + } +} diff --git a/packages/core/src/extensions/toggleWrap.ts b/packages/core/src/extensions/toggleWrap.ts new file mode 100644 index 00000000..1ba1558d --- /dev/null +++ b/packages/core/src/extensions/toggleWrap.ts @@ -0,0 +1,29 @@ +import { wrapIn, lift } from 'prosemirror-commands' +import { NodeType } from 'prosemirror-model' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import nodeIsActive from '../utils/nodeIsActive' +import getNodeType from '../utils/getNodeType' + +export const ToggleWrap = createExtension({ + addCommands() { + return { + toggleWrap: (typeOrName: string | NodeType, attrs = {}): Command => ({ state, dispatch }) => { + const type = getNodeType(typeOrName, state.schema) + const isActive = nodeIsActive(state, type, attrs) + + if (isActive) { + return lift(state, dispatch) + } + + return wrapIn(type, attrs)(state, dispatch) + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + ToggleWrap: typeof ToggleWrap, + } +} diff --git a/packages/core/src/extensions/updateMark.ts b/packages/core/src/extensions/updateMark.ts new file mode 100644 index 00000000..0200cf0d --- /dev/null +++ b/packages/core/src/extensions/updateMark.ts @@ -0,0 +1,43 @@ +import { MarkType } from 'prosemirror-model' +import { Command } from '../Editor' +import { createExtension } from '../Extension' +import getMarkType from '../utils/getMarkType' +import getMarkRange from '../utils/getMarkRange' + +export const UpdateMark = createExtension({ + addCommands() { + return { + updateMark: (typeOrName: string | MarkType, attrs: {}): Command => ({ tr, state }) => { + const { selection, doc } = tr + let { from, to } = selection + const { $from, empty } = selection + const type = getMarkType(typeOrName, state.schema) + + if (empty) { + const range = getMarkRange($from, type) + + if (range) { + from = range.from + to = range.to + } + } + + const hasMark = doc.rangeHasMark(from, to, type) + + if (hasMark) { + tr.removeMark(from, to, type) + } + + tr.addMark(from, to, type.create(attrs)) + + return true + }, + } + }, +}) + +declare module '../Editor' { + interface AllExtensions { + UpdateMark: typeof UpdateMark, + } +} diff --git a/packages/extension-collaboration-cursor/index.ts b/packages/extension-collaboration-cursor/index.ts index 1fa870bd..c969fa95 100644 --- a/packages/extension-collaboration-cursor/index.ts +++ b/packages/extension-collaboration-cursor/index.ts @@ -9,8 +9,6 @@ export interface CollaborationCursorOptions { } const CollaborationCursor = createExtension({ - name: 'collaboration_cursor', - defaultOptions: { provider: null, name: 'Someone', diff --git a/packages/extension-collaboration/index.ts b/packages/extension-collaboration/index.ts index a4570fb5..d4427f14 100644 --- a/packages/extension-collaboration/index.ts +++ b/packages/extension-collaboration/index.ts @@ -9,8 +9,6 @@ export interface CollaborationOptions { } const Collaboration = createExtension({ - name: 'collaboration', - defaultOptions: { provider: null, type: null, diff --git a/packages/extension-focus/index.ts b/packages/extension-focus/index.ts index 4fe3a899..ea4e2232 100644 --- a/packages/extension-focus/index.ts +++ b/packages/extension-focus/index.ts @@ -8,8 +8,6 @@ export interface FocusOptions { } const Focus = createExtension({ - name: 'focus', - defaultOptions: { className: 'has-focus', nested: false, diff --git a/packages/extension-history/index.ts b/packages/extension-history/index.ts index 51d4492f..b9ff706b 100644 --- a/packages/extension-history/index.ts +++ b/packages/extension-history/index.ts @@ -7,8 +7,6 @@ export interface HistoryOptions { } const History = createExtension({ - name: 'history', - defaultOptions: { depth: 100, newGroupDelay: 500,