add core extensions

This commit is contained in:
Philipp Kühn
2020-10-23 10:44:30 +02:00
parent 73285aadce
commit fd298b645b
48 changed files with 577 additions and 501 deletions

View File

@@ -16,7 +16,7 @@ import ExtensionManager from './ExtensionManager'
import EventEmitter from './EventEmitter' import EventEmitter from './EventEmitter'
import { Extensions, UnionToIntersection, PickValue } from './types' import { Extensions, UnionToIntersection, PickValue } from './types'
import defaultPlugins from './plugins' import defaultPlugins from './plugins'
import * as coreCommands from './commands' import * as extensions from './extensions'
import style from './style' import style from './style'
export type Command = (props: { export type Command = (props: {
@@ -56,7 +56,7 @@ interface HTMLElement {
interface EditorOptions { interface EditorOptions {
element: Element, element: Element,
content: EditorContent, content: EditorContent,
extensions: Extensions[], extensions: Extensions,
injectCSS: boolean, injectCSS: boolean,
autoFocus: 'start' | 'end' | number | boolean | null, autoFocus: 'start' | 'end' | number | boolean | null,
editable: boolean, editable: boolean,
@@ -110,7 +110,7 @@ export class Editor extends EventEmitter {
this.createExtensionManager() this.createExtensionManager()
this.createSchema() this.createSchema()
this.createView() this.createView()
this.registerCommands(coreCommands) // this.registerCommands(coreCommands)
this.injectCSS() this.injectCSS()
window.setTimeout(() => this.proxy.focus(this.options.autoFocus), 0) window.setTimeout(() => this.proxy.focus(this.options.autoFocus), 0)
@@ -226,7 +226,10 @@ export class Editor extends EventEmitter {
* Creates an extension manager. * Creates an extension manager.
*/ */
private createExtensionManager() { 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)
} }
/** /**

View File

@@ -4,11 +4,6 @@ import { Editor } from './Editor'
import { GlobalAttributes } from './types' import { GlobalAttributes } from './types'
export interface ExtensionSpec<Options = {}, Commands = {}> { export interface ExtensionSpec<Options = {}, Commands = {}> {
/**
* The name of your extension
*/
name: string,
/** /**
* Default options * Default options
*/ */
@@ -84,7 +79,6 @@ export type Extension = Required<Omit<ExtensionSpec, 'defaultOptions'> & {
*/ */
export const defaultExtension: Extension = { export const defaultExtension: Extension = {
type: 'extension', type: 'extension',
name: 'extension',
options: {}, options: {},
addGlobalAttributes: () => [], addGlobalAttributes: () => [],
addCommands: () => ({}), addCommands: () => ({}),

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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'

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -1,15 +1,9 @@
import { TextSelection } from 'prosemirror-state' import { TextSelection } from 'prosemirror-state'
import { Editor, Command } from '../Editor' import { Editor, Command } from '../Editor'
import { createExtension } from '../Extension'
import minMax from '../utils/minMax' import minMax from '../utils/minMax'
type Position = 'start' | 'end' | number | boolean | null type Position = 'start' | 'end' | number | boolean | null
type FocusCommand = (position?: Position) => Command
declare module '../Editor' {
interface Commands {
focus: FocusCommand
}
}
interface ResolvedSelection { interface ResolvedSelection {
from: number, from: number,
@@ -43,19 +37,31 @@ function resolveSelection(editor: Editor, position: Position = null): ResolvedSe
} }
} }
export const focus: FocusCommand = (position = null) => ({ editor, view, tr }) => { export const Focus = createExtension({
if ((view.hasFocus() && position === null) || position === false) { addCommands() {
return true 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
} }

View File

@@ -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'

View File

@@ -1,16 +1,9 @@
import { DOMParser } from 'prosemirror-model' import { DOMParser } from 'prosemirror-model'
import { Selection, Transaction } from 'prosemirror-state' import { Selection, Transaction } from 'prosemirror-state'
import { ReplaceStep, ReplaceAroundStep } from 'prosemirror-transform' import { ReplaceStep, ReplaceAroundStep } from 'prosemirror-transform'
import { Command } from '../Editor'
import elementFromString from '../utils/elementFromString' import elementFromString from '../utils/elementFromString'
import { Command } from '../Editor'
type InsertHTMLCommand = (value: string) => Command import { createExtension } from '../Extension'
declare module '../Editor' {
interface Commands {
insertHTML: InsertHTMLCommand,
}
}
// TODO: move to utils // TODO: move to utils
// https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.js#L466 // 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)) tr.setSelection(Selection.near(tr.doc.resolve(end as unknown as number), bias))
} }
export const insertHTML: InsertHTMLCommand = value => ({ tr, state }) => { export const InsertHTML = createExtension({
const { selection } = tr addCommands() {
const element = elementFromString(value) return {
const slice = DOMParser.fromSchema(state.schema).parseSlice(element) 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) tr.insert(selection.anchor, slice.content)
selectionToInsertionEnd(tr, tr.steps.length - 1, -1) selectionToInsertionEnd(tr, tr.steps.length - 1, -1)
return true return true
},
}
},
})
declare module '../Editor' {
interface AllExtensions {
InsertHTML: typeof InsertHTML,
}
} }

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -9,8 +9,6 @@ export interface CollaborationCursorOptions {
} }
const CollaborationCursor = createExtension({ const CollaborationCursor = createExtension({
name: 'collaboration_cursor',
defaultOptions: <CollaborationCursorOptions>{ defaultOptions: <CollaborationCursorOptions>{
provider: null, provider: null,
name: 'Someone', name: 'Someone',

View File

@@ -9,8 +9,6 @@ export interface CollaborationOptions {
} }
const Collaboration = createExtension({ const Collaboration = createExtension({
name: 'collaboration',
defaultOptions: <CollaborationOptions>{ defaultOptions: <CollaborationOptions>{
provider: null, provider: null,
type: null, type: null,

View File

@@ -8,8 +8,6 @@ export interface FocusOptions {
} }
const Focus = createExtension({ const Focus = createExtension({
name: 'focus',
defaultOptions: <FocusOptions>{ defaultOptions: <FocusOptions>{
className: 'has-focus', className: 'has-focus',
nested: false, nested: false,

View File

@@ -7,8 +7,6 @@ export interface HistoryOptions {
} }
const History = createExtension({ const History = createExtension({
name: 'history',
defaultOptions: <HistoryOptions>{ defaultOptions: <HistoryOptions>{
depth: 100, depth: 100,
newGroupDelay: 500, newGroupDelay: 500,