Merge branch 'main' of github.com:ueberdosis/tiptap-next into main

This commit is contained in:
Hans Pagel
2020-11-18 17:33:51 +01:00
47 changed files with 273 additions and 259 deletions

View File

@@ -1,9 +1,9 @@
<template> <template>
<div v-if="editor"> <div v-if="editor">
<button @click="addLink" :class="{ 'is-active': editor.isActive('link') }"> <button @click="setLink" :class="{ 'is-active': editor.isActive('link') }">
link link
</button> </button>
<button @click="editor.chain().focus().removeLink().run()" v-if="editor.isActive('link')"> <button @click="editor.chain().focus().unsetLink().run()" v-if="editor.isActive('link')">
remove remove
</button> </button>
<editor-content :editor="editor" /> <editor-content :editor="editor" />
@@ -52,10 +52,10 @@ export default {
}, },
methods: { methods: {
addLink() { setLink() {
const url = window.prompt('URL') const url = window.prompt('URL')
this.editor.chain().focus().addLink({ href: url }).run() this.editor.chain().focus().setLink({ href: url }).run()
}, },
}, },

View File

@@ -12,7 +12,7 @@
<button @click="editor.chain().focus().setTextAlign('justify').run()"> <button @click="editor.chain().focus().setTextAlign('justify').run()">
justify justify
</button> </button>
<button @click="editor.chain().focus().resetNodeAttributes(['textAlign']).run()"> <button @click="editor.chain().focus().unsetTextAlign().run()">
set default set default
</button> </button>
<editor-content :editor="editor" /> <editor-content :editor="editor" />

View File

@@ -1,9 +1,9 @@
<template> <template>
<div v-if="editor"> <div v-if="editor">
<button @click="addLink" :class="{ 'is-active': editor.isActive('link') }"> <button @click="setLink" :class="{ 'is-active': editor.isActive('link') }">
link link
</button> </button>
<button @click="editor.chain().focus().removeLink().run()" v-if="editor.isActive('link')"> <button @click="editor.chain().focus().unsetLink().run()" v-if="editor.isActive('link')">
remove remove
</button> </button>
<editor-content :editor="editor" /> <editor-content :editor="editor" />
@@ -49,10 +49,10 @@ export default {
}, },
methods: { methods: {
addLink() { setLink() {
const url = window.prompt('URL') const url = window.prompt('URL')
this.editor.chain().focus().addLink({ href: url }).run() this.editor.chain().focus().setLink({ href: url }).run()
}, },
}, },

View File

@@ -9,7 +9,7 @@ The editor provides a ton of commands to programmtically add or change content o
All available commands are accessible through an editor instance. Lets say you want to make text bold when a user clicks on a button. Thats how that would look like: All available commands are accessible through an editor instance. Lets say you want to make text bold when a user clicks on a button. Thats how that would look like:
```js ```js
editor.commands.addBold() editor.commands.setBold()
``` ```
While thats perfectly fine and does make the selected bold, youd likely want to change multiple commands in one run. Lets have a look at how that works. While thats perfectly fine and does make the selected bold, youd likely want to change multiple commands in one run. Lets have a look at how that works.

View File

@@ -3,7 +3,10 @@ import { Command } from '../types'
import getMarkType from '../utils/getMarkType' import getMarkType from '../utils/getMarkType'
import getMarkAttributes from '../utils/getMarkAttributes' import getMarkAttributes from '../utils/getMarkAttributes'
export default (typeOrName: string | MarkType, attributes?: {}): Command => ({ tr, state, dispatch }) => { /**
* Add a mark with new attributes.
*/
export const addMark = (typeOrName: string | MarkType, attributes?: {}): Command => ({ tr, state, dispatch }) => {
const { selection } = tr const { selection } = tr
const { from, to, empty } = selection const { from, to, empty } = selection
const type = getMarkType(typeOrName, state.schema) const type = getMarkType(typeOrName, state.schema)

View File

@@ -1,6 +1,9 @@
import { Command } from '../types' import { Command } from '../types'
export default (): Command => ({ view }) => { /**
* Removes focus from the editor.
*/
export const blur = (): Command => ({ view }) => {
const element = view.dom as HTMLElement const element = view.dom as HTMLElement
element.blur() element.blur()

View File

@@ -1,5 +1,8 @@
import { Command } from '../types' import { Command } from '../types'
export default (emitUpdate: Boolean = false): Command => ({ commands }) => { /**
* Clear the whole document.
*/
export const clearContent = (emitUpdate: Boolean = false): Command => ({ commands }) => {
return commands.setContent('', emitUpdate) return commands.setContent('', emitUpdate)
} }

View File

@@ -1,7 +1,10 @@
import { liftTarget } from 'prosemirror-transform' import { liftTarget } from 'prosemirror-transform'
import { Command } from '../types' import { Command } from '../types'
export default (): Command => ({ state, tr, dispatch }) => { /**
* Normalize nodes to a simple paragraph.
*/
export const clearNodes = (): Command => ({ state, tr, dispatch }) => {
const { selection } = tr const { selection } = tr
const { from, to } = selection const { from, to } = selection

View File

@@ -1,5 +1,8 @@
import { Command } from '../types' import { Command } from '../types'
export default (fn: (props: Parameters<Command>[0]) => boolean): Command => props => { /**
* Define a command inline.
*/
export const command = (fn: (props: Parameters<Command>[0]) => boolean): Command => props => {
return fn(props) return fn(props)
} }

View File

@@ -1,6 +1,9 @@
import { deleteSelection } from 'prosemirror-commands' import { deleteSelection as originalDeleteSelection } from 'prosemirror-commands'
import { Command } from '../types' import { Command } from '../types'
export default (): Command => ({ state, dispatch }) => { /**
return deleteSelection(state, dispatch) * Delete the selection, if there is one.
*/
export const deleteSelection = (): Command => ({ state, dispatch }) => {
return originalDeleteSelection(state, dispatch)
} }

View File

@@ -4,7 +4,10 @@ import { Command } from '../types'
import getMarkType from '../utils/getMarkType' import getMarkType from '../utils/getMarkType'
import getMarkRange from '../utils/getMarkRange' import getMarkRange from '../utils/getMarkRange'
export default (typeOrName: string | MarkType): Command => ({ tr, state, dispatch }) => { /**
* Extends the text selection to the current mark.
*/
export const extendMarkRange = (typeOrName: string | MarkType): Command => ({ tr, state, dispatch }) => {
const type = getMarkType(typeOrName, state.schema) const type = getMarkType(typeOrName, state.schema)
const { doc, selection } = tr const { doc, selection } = tr
const { $from, empty } = selection const { $from, empty } = selection

View File

@@ -1,6 +1,9 @@
import { Command } from '../types' import { Command } from '../types'
export default (commands: Command[] | ((props: Parameters<Command>[0]) => Command[])): Command => props => { /**
* Runs one command after the other and stops at the first which returns true.
*/
export const first = (commands: Command[] | ((props: Parameters<Command>[0]) => Command[])): Command => props => {
const items = typeof commands === 'function' const items = typeof commands === 'function'
? commands(props) ? commands(props)
: commands : commands

View File

@@ -35,7 +35,10 @@ function resolveSelection(editor: Editor, position: FocusPosition = null): Resol
} }
} }
export default (position: FocusPosition = null): Command => ({ /**
* Focus the editor at the given position.
*/
export const focus = (position: FocusPosition = null): Command => ({
editor, editor,
view, view,
tr, tr,

View File

@@ -17,7 +17,10 @@ function selectionToInsertionEnd(tr: Transaction, startLen: number, bias: number
tr.setSelection(Selection.near(tr.doc.resolve(end as unknown as number), bias)) tr.setSelection(Selection.near(tr.doc.resolve(end as unknown as number), bias))
} }
export default (value: string): Command => ({ tr, state, dispatch }) => { /**
* Insert a string of HTML at the current position.
*/
export const insertHTML = (value: string): Command => ({ tr, state, dispatch }) => {
const { selection } = tr const { selection } = tr
const element = elementFromString(value) const element = elementFromString(value)
const slice = DOMParser.fromSchema(state.schema).parseSlice(element) const slice = DOMParser.fromSchema(state.schema).parseSlice(element)

View File

@@ -1,6 +1,9 @@
import { Command } from '../types' import { Command } from '../types'
export default (value: string): Command => ({ tr, dispatch }) => { /**
* Insert a string of text at the current position.
*/
export const insertText = (value: string): Command => ({ tr, dispatch }) => {
if (dispatch) { if (dispatch) {
tr.insertText(value) tr.insertText(value)
} }

View File

@@ -1,10 +1,13 @@
import { lift } from 'prosemirror-commands' import { lift as originalLift } from 'prosemirror-commands'
import { NodeType } from 'prosemirror-model' import { NodeType } from 'prosemirror-model'
import { Command } from '../types' import { Command } from '../types'
import nodeIsActive from '../utils/nodeIsActive' import nodeIsActive from '../utils/nodeIsActive'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => { /**
* Removes an existing wrap.
*/
export const lift = (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
const isActive = nodeIsActive(state, type, attributes) const isActive = nodeIsActive(state, type, attributes)
@@ -12,5 +15,5 @@ export default (typeOrName: string | NodeType, attributes = {}): Command => ({ s
return false return false
} }
return lift(state, dispatch) return originalLift(state, dispatch)
} }

View File

@@ -1,10 +1,13 @@
import { liftListItem } from 'prosemirror-schema-list' import { liftListItem as originalLiftListItem } from 'prosemirror-schema-list'
import { NodeType } from 'prosemirror-model' import { NodeType } from 'prosemirror-model'
import { Command } from '../types' import { Command } from '../types'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType): Command => ({ state, dispatch }) => { /**
* Lift the list item into a wrapping list.
*/
export const liftListItem = (typeOrName: string | NodeType): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
return liftListItem(type)(state, dispatch) return originalLiftListItem(type)(state, dispatch)
} }

View File

@@ -3,7 +3,10 @@ import { Command } from '../types'
import getMarkType from '../utils/getMarkType' import getMarkType from '../utils/getMarkType'
import getMarkRange from '../utils/getMarkRange' import getMarkRange from '../utils/getMarkRange'
export default (typeOrName: string | MarkType): Command => ({ tr, state, dispatch }) => { /**
* Remove all marks in the current selection.
*/
export const removeMark = (typeOrName: string | MarkType): Command => ({ tr, state, dispatch }) => {
const { selection } = tr const { selection } = tr
const type = getMarkType(typeOrName, state.schema) const type = getMarkType(typeOrName, state.schema)
let { from, to } = selection let { from, to } = selection

View File

@@ -1,6 +1,9 @@
import { Command } from '../types' import { Command } from '../types'
export default (): Command => ({ tr, state, dispatch }) => { /**
* Remove all marks in the current selection.
*/
export const removeMarks = (): Command => ({ tr, state, dispatch }) => {
const { selection } = tr const { selection } = tr
const { from, to, empty } = selection const { from, to, empty } = selection

View File

@@ -1,22 +1,19 @@
import { NodeType } from 'prosemirror-model'
import getNodeType from '../utils/getNodeType'
import deleteProps from '../utils/deleteProps'
import { Command } from '../types' import { Command } from '../types'
export default (attributeNames: string[] = []): Command => ({ tr, state, dispatch }) => { /**
* Resets node attributes to the default value.
*/
export const resetNodeAttributes = (typeOrName: string | NodeType, attributes: string | string[]): Command => ({ tr, state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
const { selection } = tr const { selection } = tr
const { from, to } = selection const { from, to } = selection
state.doc.nodesBetween(from, to, (node, pos) => { state.doc.nodesBetween(from, to, (node, pos) => {
if (!node.type.isText) { if (node.type === type && dispatch) {
attributeNames.forEach(name => { tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes))
const attribute = node.type.spec.attrs?.[name]
const defaultValue = attribute?.default
if (attribute && defaultValue !== undefined && dispatch) {
tr.setNodeMarkup(pos, undefined, {
...node.attrs,
[name]: defaultValue,
})
}
})
} }
}) })

View File

@@ -1,6 +1,9 @@
import { Command } from '../types' import { Command } from '../types'
export default (): Command => ({ tr, dispatch }) => { /**
* Scroll the selection into view.
*/
export const scrollIntoView = (): Command => ({ tr, dispatch }) => {
if (dispatch) { if (dispatch) {
tr.scrollIntoView() tr.scrollIntoView()
} }

View File

@@ -1,6 +1,9 @@
import { selectAll } from 'prosemirror-commands' import { selectAll as originalSelectAll } from 'prosemirror-commands'
import { Command } from '../types' import { Command } from '../types'
export default (): Command => ({ state, dispatch }) => { /**
return selectAll(state, dispatch) * Select the whole document.
*/
export const selectAll = (): Command => ({ state, dispatch }) => {
return originalSelectAll(state, dispatch)
} }

View File

@@ -1,6 +1,9 @@
import { selectParentNode } from 'prosemirror-commands' import { selectParentNode as originalSelectParentNode } from 'prosemirror-commands'
import { Command } from '../types' import { Command } from '../types'
export default (): Command => ({ state, dispatch }) => { /**
return selectParentNode(state, dispatch) * Select the parent node.
*/
export const selectParentNode = (): Command => ({ state, dispatch }) => {
return originalSelectParentNode(state, dispatch)
} }

View File

@@ -1,10 +1,13 @@
import { NodeType } from 'prosemirror-model' import { NodeType } from 'prosemirror-model'
import { setBlockType } from 'prosemirror-commands' import { setBlockType as originalSetBlockType } from 'prosemirror-commands'
import { Command } from '../types' import { Command } from '../types'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attrs = {}): Command => ({ state, dispatch }) => { /**
* Replace a given range with a node.
*/
export const setBlockType = (typeOrName: string | NodeType, attrs = {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
return setBlockType(type, attrs)(state, dispatch) return originalSetBlockType(type, attrs)(state, dispatch)
} }

View File

@@ -1,7 +1,10 @@
import { TextSelection } from 'prosemirror-state' import { TextSelection } from 'prosemirror-state'
import { Command } from '../types' import { Command } from '../types'
export default (content: string, emitUpdate: Boolean = false, parseOptions = {}): Command => ({ tr, editor, dispatch }) => { /**
* Replace the whole document with new content.
*/
export const setContent = (content: string, emitUpdate: Boolean = false, parseOptions = {}): Command => ({ tr, editor, dispatch }) => {
const { createDocument } = editor const { createDocument } = editor
const { doc } = tr const { doc } = tr
const document = createDocument(content, parseOptions) const document = createDocument(content, parseOptions)

View File

@@ -3,7 +3,10 @@ import { NodeType } from 'prosemirror-model'
import { Command } from '../types' import { Command } from '../types'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType): Command => ({ state, dispatch }) => { /**
* Sink the list item down into an inner list.
*/
export const sinkListItem = (typeOrName: string | NodeType): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
return originalSinkListItem(type)(state, dispatch) return originalSinkListItem(type)(state, dispatch)

View File

@@ -28,7 +28,10 @@ function keepMarks(state: EditorState) {
} }
} }
export default (options: Partial<SplitBlockOptions> = {}): Command => ({ tr, state, dispatch }) => { /**
* Forks a new node from an existing node.
*/
export const splitBlock = (options: Partial<SplitBlockOptions> = {}): Command => ({ tr, state, dispatch }) => {
const defaultOptions: SplitBlockOptions = { const defaultOptions: SplitBlockOptions = {
withAttributes: false, withAttributes: false,
withMarks: true, withMarks: true,

View File

@@ -1,10 +1,13 @@
import { splitListItem } from 'prosemirror-schema-list' import { splitListItem as originalSplitListItem } from 'prosemirror-schema-list'
import { NodeType } from 'prosemirror-model' import { NodeType } from 'prosemirror-model'
import { Command } from '../types' import { Command } from '../types'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType): Command => ({ state, dispatch }) => { /**
* Splits one list item into two list items.
*/
export const splitListItem = (typeOrName: string | NodeType): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
return splitListItem(type)(state, dispatch) return originalSplitListItem(type)(state, dispatch)
} }

View File

@@ -3,7 +3,10 @@ import { Command } from '../types'
import nodeIsActive from '../utils/nodeIsActive' import nodeIsActive from '../utils/nodeIsActive'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, toggleTypeOrName: string | NodeType, attrs = {}): Command => ({ state, commands }) => { /**
* Toggle a node with another node.
*/
export const toggleBlockType = (typeOrName: string | NodeType, toggleTypeOrName: string | NodeType, attrs = {}): Command => ({ state, commands }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
const toggleType = getNodeType(toggleTypeOrName, state.schema) const toggleType = getNodeType(toggleTypeOrName, state.schema)
const isActive = nodeIsActive(state, type, attrs) const isActive = nodeIsActive(state, type, attrs)

View File

@@ -4,7 +4,10 @@ import { Command } from '../types'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
import isList from '../utils/isList' import isList from '../utils/isList'
export default (listTypeOrName: string | NodeType, itemTypeOrName: string | NodeType): Command => ({ /**
* Toggle between different list types.
*/
export const toggleList = (listTypeOrName: string | NodeType, itemTypeOrName: string | NodeType): Command => ({
editor, tr, state, dispatch, chain, commands, can, editor, tr, state, dispatch, chain, commands, can,
}) => { }) => {
const { extensions } = editor.options const { extensions } = editor.options

View File

@@ -1,10 +1,13 @@
import { toggleMark } from 'prosemirror-commands' import { toggleMark as originalToggleMark } from 'prosemirror-commands'
import { MarkType } from 'prosemirror-model' import { MarkType } from 'prosemirror-model'
import { Command } from '../types' import { Command } from '../types'
import getMarkType from '../utils/getMarkType' import getMarkType from '../utils/getMarkType'
import markIsActive from '../utils/markIsActive' import markIsActive from '../utils/markIsActive'
export default (typeOrName: string | MarkType, attributes?: {}): Command => ({ state, dispatch, commands }) => { /**
* Toggle a mark on and off.
*/
export const toggleMark = (typeOrName: string | MarkType, attributes?: {}): Command => ({ state, dispatch, commands }) => {
const type = getMarkType(typeOrName, state.schema) const type = getMarkType(typeOrName, state.schema)
const hasMarkWithDifferentAttributes = attributes const hasMarkWithDifferentAttributes = attributes
@@ -15,5 +18,5 @@ export default (typeOrName: string | MarkType, attributes?: {}): Command => ({ s
return commands.addMark(type, attributes) return commands.addMark(type, attributes)
} }
return toggleMark(type, attributes)(state, dispatch) return originalToggleMark(type, attributes)(state, dispatch)
} }

View File

@@ -4,7 +4,10 @@ import { Command } from '../types'
import nodeIsActive from '../utils/nodeIsActive' import nodeIsActive from '../utils/nodeIsActive'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => { /**
* Wraps nodes in another node, or removes an existing wrap.
*/
export const toggleWrap = (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
const isActive = nodeIsActive(state, type, attributes) const isActive = nodeIsActive(state, type, attributes)

View File

@@ -2,7 +2,10 @@ import { NodeType } from 'prosemirror-model'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
import { Command } from '../types' import { Command } from '../types'
export default (typeOrName: string | NodeType, attributes: {}): Command => ({ tr, state, dispatch }) => { /**
* Update attributes of a node.
*/
export const updateNodeAttributes = (typeOrName: string | NodeType, attributes: {}): Command => ({ tr, state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
const { selection } = tr const { selection } = tr
const { from, to } = selection const { from, to } = selection

View File

@@ -1,10 +1,13 @@
import { wrapIn } from 'prosemirror-commands' import { wrapIn as originalWrapIn } from 'prosemirror-commands'
import { NodeType } from 'prosemirror-model' import { NodeType } from 'prosemirror-model'
import { Command } from '../types' import { Command } from '../types'
import nodeIsActive from '../utils/nodeIsActive' import nodeIsActive from '../utils/nodeIsActive'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => { /**
* Wraps nodes in another node.
*/
export const wrapIn = (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
const isActive = nodeIsActive(state, type, attributes) const isActive = nodeIsActive(state, type, attributes)
@@ -12,5 +15,5 @@ export default (typeOrName: string | NodeType, attributes = {}): Command => ({ s
return false return false
} }
return wrapIn(type, attributes)(state, dispatch) return originalWrapIn(type, attributes)(state, dispatch)
} }

View File

@@ -1,10 +1,13 @@
import { wrapInList } from 'prosemirror-schema-list' import { wrapInList as originalWrapInList } from 'prosemirror-schema-list'
import { NodeType } from 'prosemirror-model' import { NodeType } from 'prosemirror-model'
import { Command } from '../types' import { Command } from '../types'
import getNodeType from '../utils/getNodeType' import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attrs?: {}): Command => ({ state, dispatch }) => { /**
* Wrap a node in a list.
*/
export const wrapInList = (typeOrName: string | NodeType, attrs?: {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema) const type = getNodeType(typeOrName, state.schema)
return wrapInList(type, attrs)(state, dispatch) return originalWrapInList(type, attrs)(state, dispatch)
} }

View File

@@ -1,163 +1,70 @@
import { Extension } from '../Extension' import { Extension } from '../Extension'
import addMark from '../commands/addMark' import * as addMark from '../commands/addMark'
import blur from '../commands/blur' import * as blur from '../commands/blur'
import clearContent from '../commands/clearContent' import * as clearContent from '../commands/clearContent'
import command from '../commands/command' import * as command from '../commands/command'
import clearNodes from '../commands/clearNodes' import * as clearNodes from '../commands/clearNodes'
import deleteSelection from '../commands/deleteSelection' import * as deleteSelection from '../commands/deleteSelection'
import extendMarkRange from '../commands/extendMarkRange' import * as extendMarkRange from '../commands/extendMarkRange'
import focus from '../commands/focus' import * as first from '../commands/first'
import insertHTML from '../commands/insertHTML' import * as focus from '../commands/focus'
import insertText from '../commands/insertText' import * as insertHTML from '../commands/insertHTML'
import lift from '../commands/lift' import * as insertText from '../commands/insertText'
import liftListItem from '../commands/liftListItem' import * as lift from '../commands/lift'
import removeMark from '../commands/removeMark' import * as liftListItem from '../commands/liftListItem'
import removeMarks from '../commands/removeMarks' import * as removeMark from '../commands/removeMark'
import resetNodeAttributes from '../commands/resetNodeAttributes' import * as removeMarks from '../commands/removeMarks'
import scrollIntoView from '../commands/scrollIntoView' import * as resetNodeAttributes from '../commands/resetNodeAttributes'
import selectAll from '../commands/selectAll' import * as scrollIntoView from '../commands/scrollIntoView'
import selectParentNode from '../commands/selectParentNode' import * as selectAll from '../commands/selectAll'
import setBlockType from '../commands/setBlockType' import * as selectParentNode from '../commands/selectParentNode'
import setContent from '../commands/setContent' import * as setBlockType from '../commands/setBlockType'
import sinkListItem from '../commands/sinkListItem' import * as setContent from '../commands/setContent'
import splitBlock from '../commands/splitBlock' import * as sinkListItem from '../commands/sinkListItem'
import splitListItem from '../commands/splitListItem' import * as splitBlock from '../commands/splitBlock'
import toggleBlockType from '../commands/toggleBlockType' import * as splitListItem from '../commands/splitListItem'
import toggleList from '../commands/toggleList' import * as toggleBlockType from '../commands/toggleBlockType'
import toggleMark from '../commands/toggleMark' import * as toggleList from '../commands/toggleList'
import toggleWrap from '../commands/toggleWrap' import * as toggleMark from '../commands/toggleMark'
import tryCommand from '../commands/try' import * as toggleWrap from '../commands/toggleWrap'
import updateNodeAttributes from '../commands/updateNodeAttributes' import * as updateNodeAttributes from '../commands/updateNodeAttributes'
import wrapIn from '../commands/wrapIn' import * as wrapIn from '../commands/wrapIn'
import wrapInList from '../commands/wrapInList' import * as wrapInList from '../commands/wrapInList'
export const Commands = Extension.create({ export const Commands = Extension.create({
addCommands() { addCommands() {
return { return {
/** ...addMark,
* Add a mark with new attributes. ...blur,
*/ ...clearContent,
addMark, ...clearNodes,
/** ...command,
* Removes focus from the editor. ...deleteSelection,
*/ ...extendMarkRange,
blur, ...first,
/** ...focus,
* Clear the whole document. ...insertHTML,
*/ ...insertText,
clearContent, ...lift,
/** ...liftListItem,
* Normalize nodes to a simple paragraph. ...removeMark,
*/ ...removeMarks,
clearNodes, ...resetNodeAttributes,
/** ...scrollIntoView,
* Define a command inline. ...selectAll,
*/ ...selectParentNode,
command, ...setBlockType,
/** ...setContent,
* Delete the selection, if there is one. ...sinkListItem,
*/ ...splitBlock,
deleteSelection, ...splitListItem,
/** ...toggleBlockType,
* Extends the text selection to the current mark. ...toggleList,
*/ ...toggleMark,
extendMarkRange, ...toggleWrap,
/** ...updateNodeAttributes,
* Focus the editor at the given position. ...wrapIn,
*/ ...wrapInList,
focus,
/**
* Insert a string of HTML at the current position.
*/
insertHTML,
/**
* Insert a string of text at the current position.
*/
insertText,
/**
* Removes an existing wrap.
*/
lift,
/**
* Lift the list item into a wrapping list.
*/
liftListItem,
/**
* Remove all marks in the current selection.
*/
removeMark,
/**
* Remove all marks in the current selection.
*/
removeMarks,
/**
* Resets all node attributes to the default value.
*/
resetNodeAttributes,
/**
* Scroll the selection into view.
*/
scrollIntoView,
/**
* Select the whole document.
*/
selectAll,
/**
* Select the parent node.
*/
selectParentNode,
/**
* Replace a given range with a node.
*/
setBlockType,
/**
* Replace the whole document with new content.
*/
setContent,
/**
* Sink the list item down into an inner list.
*/
sinkListItem,
/**
* Forks a new node from an existing node.
*/
splitBlock,
/**
* Splits one list item into two list items.
*/
splitListItem,
/**
* Toggle a node with another node.
*/
toggleBlockType,
/**
* Toggle between different list types.
*/
toggleList,
/**
* Toggle a mark on and off.
*/
toggleMark,
/**
* Wraps nodes in another node, or removes an existing wrap.
*/
toggleWrap,
/**
* Runs one command after the other and stops at the first which returns true.
*/
try: tryCommand,
/**
* Update attributes of a node.
*/
updateNodeAttributes,
/**
* Wraps nodes in another node.
*/
wrapIn,
/**
* Wrap a node in a list.
*/
wrapInList,
} }
}, },
}) })

View File

@@ -14,21 +14,21 @@ import { Extension } from '../Extension'
export const Keymap = Extension.create({ export const Keymap = Extension.create({
addKeyboardShortcuts() { addKeyboardShortcuts() {
const handleBackspace = () => this.editor.commands.try(({ state, dispatch }) => [ const handleBackspace = () => this.editor.commands.first(({ state, dispatch }) => [
() => undoInputRule(state, dispatch), () => undoInputRule(state, dispatch),
() => deleteSelection(state, dispatch), () => deleteSelection(state, dispatch),
() => joinBackward(state, dispatch), () => joinBackward(state, dispatch),
() => selectNodeBackward(state, dispatch), () => selectNodeBackward(state, dispatch),
]) ])
const handleDelete = () => this.editor.commands.try(({ state, dispatch }) => [ const handleDelete = () => this.editor.commands.first(({ state, dispatch }) => [
() => deleteSelection(state, dispatch), () => deleteSelection(state, dispatch),
() => joinForward(state, dispatch), () => joinForward(state, dispatch),
() => selectNodeForward(state, dispatch), () => selectNodeForward(state, dispatch),
]) ])
return { return {
Enter: () => this.editor.commands.try(({ commands, state, dispatch }) => [ Enter: () => this.editor.commands.first(({ commands, state, dispatch }) => [
() => newlineInCode(state, dispatch), () => newlineInCode(state, dispatch),
() => createParagraphNear(state, dispatch), () => createParagraphNear(state, dispatch),
() => liftEmptyBlock(state, dispatch), () => liftEmptyBlock(state, dispatch),

View File

@@ -0,0 +1,20 @@
/**
* Remove a property or an array of properties from an object
* @param obj Object
* @param key Key to remove
*/
export default function deleteProps(obj: { [key: string ]: any }, propOrProps: string | string[]) {
const props = typeof propOrProps === 'string'
? [propOrProps]
: propOrProps
return Object
.keys(obj)
.reduce((newObj: { [key: string ]: any }, prop) => {
if (!props.includes(prop)) {
newObj[prop] = obj[prop]
}
return newObj
}, {})
}

View File

@@ -46,9 +46,9 @@ const Bold = Mark.create({
addCommands() { addCommands() {
return { return {
/** /**
* Add a bold mark * Set a bold mark
*/ */
addBold: (): Command => ({ commands }) => { setBold: (): Command => ({ commands }) => {
return commands.addMark('bold') return commands.addMark('bold')
}, },
/** /**
@@ -58,9 +58,9 @@ const Bold = Mark.create({
return commands.toggleMark('bold') return commands.toggleMark('bold')
}, },
/** /**
* Remove a bold mark * Unset a bold mark
*/ */
removeBold: (): Command => ({ commands }) => { unsetBold: (): Command => ({ commands }) => {
return commands.removeMark('bold') return commands.removeMark('bold')
}, },
} }

View File

@@ -36,9 +36,9 @@ const Code = Mark.create({
addCommands() { addCommands() {
return { return {
/** /**
* Add a code mark * Set a code mark
*/ */
addCode: (): Command => ({ commands }) => { setCode: (): Command => ({ commands }) => {
return commands.addMark('code') return commands.addMark('code')
}, },
/** /**
@@ -48,9 +48,9 @@ const Code = Mark.create({
return commands.toggleMark('code') return commands.toggleMark('code')
}, },
/** /**
* Remove a code mark * Unset a code mark
*/ */
removeCode: (): Command => ({ commands }) => { unsetCode: (): Command => ({ commands }) => {
return commands.addMark('code') return commands.addMark('code')
}, },
} }

View File

@@ -26,7 +26,7 @@ const HardBreak = Node.create({
* Add a hard break * Add a hard break
*/ */
setHardBreak: (): Command => ({ commands, state, dispatch }) => { setHardBreak: (): Command => ({ commands, state, dispatch }) => {
return commands.try([ return commands.first([
() => exitCode(state, dispatch), () => exitCode(state, dispatch),
() => { () => {
if (dispatch) { if (dispatch) {

View File

@@ -59,9 +59,9 @@ const Highlight = Mark.create({
addCommands() { addCommands() {
return { return {
/** /**
* Add a highlight mark * Set a highlight mark
*/ */
addHighlight: (attributes?: { color: string }): Command => ({ commands }) => { setHighlight: (attributes?: { color: string }): Command => ({ commands }) => {
return commands.addMark('highlight', attributes) return commands.addMark('highlight', attributes)
}, },
/** /**
@@ -71,9 +71,9 @@ const Highlight = Mark.create({
return commands.toggleMark('highlight', attributes) return commands.toggleMark('highlight', attributes)
}, },
/** /**
* Remove a highlight mark * Unset a highlight mark
*/ */
removeHighlight: (): Command => ({ commands }) => { unsetHighlight: (): Command => ({ commands }) => {
return commands.removeMark('highlight') return commands.removeMark('highlight')
}, },
} }

View File

@@ -45,9 +45,9 @@ const Italic = Mark.create({
addCommands() { addCommands() {
return { return {
/** /**
* Add an italic mark * Set an italic mark
*/ */
addItalic: (): Command => ({ commands }) => { setItalic: (): Command => ({ commands }) => {
return commands.addMark('italic') return commands.addMark('italic')
}, },
/** /**
@@ -57,9 +57,9 @@ const Italic = Mark.create({
return commands.toggleMark('italic') return commands.toggleMark('italic')
}, },
/** /**
* Remove an italic mark * Unset an italic mark
*/ */
removeItalic: (): Command => ({ commands }) => { unsetItalic: (): Command => ({ commands }) => {
return commands.addMark('italic') return commands.addMark('italic')
}, },
} }

View File

@@ -47,9 +47,9 @@ const Link = Mark.create({
addCommands() { addCommands() {
return { return {
/** /**
* Add a link mark * Set a link mark
*/ */
addLink: (attributes: { href?: string, target?: string } = {}): Command => ({ commands }) => { setLink: (attributes: { href?: string, target?: string } = {}): Command => ({ commands }) => {
return commands.addMark('link', attributes) return commands.addMark('link', attributes)
}, },
/** /**
@@ -59,9 +59,9 @@ const Link = Mark.create({
return commands.toggleMark('link', attributes) return commands.toggleMark('link', attributes)
}, },
/** /**
* Remove a link mark * Unset a link mark
*/ */
removeLink: (): Command => ({ commands }) => { unsetLink: (): Command => ({ commands }) => {
return commands.removeMark('link') return commands.removeMark('link')
}, },
} }

View File

@@ -45,9 +45,9 @@ const Strike = Mark.create({
addCommands() { addCommands() {
return { return {
/** /**
* Add a strike mark * Set a strike mark
*/ */
addStrike: (): Command => ({ commands }) => { setStrike: (): Command => ({ commands }) => {
return commands.addMark('strike') return commands.addMark('strike')
}, },
/** /**
@@ -57,9 +57,9 @@ const Strike = Mark.create({
return commands.toggleMark('strike') return commands.toggleMark('strike')
}, },
/** /**
* Remove a strike mark * Unset a strike mark
*/ */
removeStrike: (): Command => ({ commands }) => { unsetStrike: (): Command => ({ commands }) => {
return commands.addMark('strike') return commands.addMark('strike')
}, },
} }

View File

@@ -48,7 +48,7 @@ const TextAlign = Extension.create({
* Unset the text align attribute * Unset the text align attribute
*/ */
unsetTextAlign: (): Command => ({ commands }) => { unsetTextAlign: (): Command => ({ commands }) => {
return this.options.types.every(type => commands.updateNodeAttributes(type, { textAlign: null })) return this.options.types.every(type => commands.resetNodeAttributes(type, 'textAlign'))
}, },
} }
}, },

View File

@@ -33,7 +33,7 @@ const Underline = Mark.create({
/** /**
* Set an underline mark * Set an underline mark
*/ */
addUnderline: (): Command => ({ commands }) => { setUnderline: (): Command => ({ commands }) => {
return commands.addMark('underline') return commands.addMark('underline')
}, },
/** /**
@@ -45,7 +45,7 @@ const Underline = Mark.create({
/** /**
* Unset an underline mark * Unset an underline mark
*/ */
removeUnderline: (): Command => ({ commands }) => { unsetUnderline: (): Command => ({ commands }) => {
return commands.addMark('underline') return commands.addMark('underline')
}, },
} }