From 27c8a46e401ab4b5b54ec5273db08d3e3a2e9bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Tue, 21 Apr 2020 22:11:57 +0200 Subject: [PATCH 01/37] fix package alias --- docs/gridsome.server.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/gridsome.server.js b/docs/gridsome.server.js index e87a4c54a..e8dae9386 100644 --- a/docs/gridsome.server.js +++ b/docs/gridsome.server.js @@ -18,12 +18,12 @@ module.exports = function (api) { .test(/\.jsx?$/) .use() .loader('babel-loader') - - globby.sync('./packages/*', { onlyDirectories: true }) - .map(name => name.replace('./packages/', '')) + + globby.sync('../packages/*', { onlyDirectories: true }) + .map(name => name.replace('../packages/', '')) .forEach(name => { config.resolve.alias - .set(`@tiptap/${name}`, path.resolve(`./packages/${name}`)) + .set(`@tiptap/${name}`, path.resolve(`../packages/${name}`)) }) }) } From a057755e429aa5ea800fff5b612c6fc57ba7f5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Tue, 21 Apr 2020 22:16:26 +0200 Subject: [PATCH 02/37] add basic heading extension --- docs/src/demos/HandleExtensions/index.vue | 4 +- packages/extension-heading/index.ts | 55 +++++++++++++++++++++++ packages/extension-heading/package.json | 17 +++++++ packages/starter-kit/index.ts | 2 + 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 packages/extension-heading/index.ts create mode 100644 packages/extension-heading/package.json diff --git a/docs/src/demos/HandleExtensions/index.vue b/docs/src/demos/HandleExtensions/index.vue index 6698f7a11..f233722df 100644 --- a/docs/src/demos/HandleExtensions/index.vue +++ b/docs/src/demos/HandleExtensions/index.vue @@ -32,6 +32,7 @@ import Bold from '@tiptap/extension-bold' import Italic from '@tiptap/extension-italic' import Code from '@tiptap/extension-code' import CodeBlock from '@tiptap/extension-codeblock' +import Heading from '@tiptap/extension-heading' export default { components: { @@ -46,7 +47,7 @@ export default { mounted() { this.editor = new Editor({ - content: '

This editor is based on Prosemirror, fully extendable and renderless. You can easily add custom nodes as Vue components.

', + content: '

Hey there!

This editor is based on Prosemirror, fully extendable and renderless. You can easily add custom nodes as Vue components.

', extensions: [ new Document(), new Paragraph(), @@ -56,6 +57,7 @@ export default { new Bold(), new Italic(), new Code(), + new Heading(), ], }) }, diff --git a/packages/extension-heading/index.ts b/packages/extension-heading/index.ts new file mode 100644 index 000000000..ca5a663f4 --- /dev/null +++ b/packages/extension-heading/index.ts @@ -0,0 +1,55 @@ +import { Node, CommandSpec } from '@tiptap/core' +import { NodeSpec } from 'prosemirror-model' + +type Level = 1 | 2 | 3 | 4 | 5 | 6 + +interface HeadingOptions { + levels?: Level[], +} + +declare module '@tiptap/core/src/Editor' { + interface Editor { + heading(level: Level): Editor, + } +} + +export default class Heading extends Node { + + name = 'heading' + + defaultOptions(): HeadingOptions { + return { + levels: [1, 2, 3, 4, 5, 6], + } + } + + schema(): NodeSpec { + return { + attrs: { + level: { + default: 1, + }, + }, + content: 'inline*', + group: 'block', + defining: true, + draggable: false, + parseDOM: this.options.levels + .map((level: Level) => ({ + tag: `h${level}`, + attrs: { level }, + })), + toDOM: node => [`h${node.attrs.level}`, 0], + } + } + + // commands(): CommandSpec { + // return { + // heading: (next, { view }) => { + // toggleBlockType(this.type)(view.state, view.dispatch) + // next() + // }, + // } + // } + +} \ No newline at end of file diff --git a/packages/extension-heading/package.json b/packages/extension-heading/package.json new file mode 100644 index 000000000..3c6f6f190 --- /dev/null +++ b/packages/extension-heading/package.json @@ -0,0 +1,17 @@ +{ + "name": "@tiptap/extension-heading", + "version": "1.0.0", + "source": "index.ts", + "main": "dist/tiptap-extension-heading.js", + "umd:main": "dist/tiptap-extension-heading.umd.js", + "module": "dist/tiptap-extension-heading.mjs", + "unpkg": "dist/tiptap-extension-heading.js", + "jsdelivr": "dist/tiptap-extension-heading.js", + "files": [ + "src", + "dist" + ], + "peerDependencies": { + "@tiptap/core": "2.x" + } +} diff --git a/packages/starter-kit/index.ts b/packages/starter-kit/index.ts index fdbff7b30..0f695426c 100644 --- a/packages/starter-kit/index.ts +++ b/packages/starter-kit/index.ts @@ -6,6 +6,7 @@ import Bold from '@tiptap/extension-bold' import Italic from '@tiptap/extension-italic' import Code from '@tiptap/extension-code' import CodeBlock from '@tiptap/extension-codeblock' +import Heading from '@tiptap/extension-heading' export default function extensions() { return [ @@ -17,5 +18,6 @@ export default function extensions() { new Italic(), new Code(), new CodeBlock(), + new Heading(), ] } \ No newline at end of file From 6b5b30f3fc164c05895a39ecd3a6f69469477afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Tue, 21 Apr 2020 22:36:31 +0200 Subject: [PATCH 03/37] add heading command --- docs/src/demos/HandleExtensions/index.vue | 6 ++++ packages/core/src/Editor.ts | 1 + packages/core/src/commands/toggleBlockType.ts | 30 +++++++++++++++++++ packages/extension-heading/index.ts | 16 +++++----- 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 packages/core/src/commands/toggleBlockType.ts diff --git a/docs/src/demos/HandleExtensions/index.vue b/docs/src/demos/HandleExtensions/index.vue index f233722df..605340077 100644 --- a/docs/src/demos/HandleExtensions/index.vue +++ b/docs/src/demos/HandleExtensions/index.vue @@ -16,6 +16,12 @@ + + diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index 6c804217b..bd27c1866 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -71,6 +71,7 @@ export class Editor extends EventEmitter { this.registerCommand('setContent', require('./commands/setContent').default) this.registerCommand('clearContent', require('./commands/clearContent').default) this.registerCommand('removeMarks', require('./commands/removeMarks').default) + this.registerCommand('toggleBlockType', require('./commands/toggleBlockType').default) if (this.options.injectCSS) { require('./style.css') diff --git a/packages/core/src/commands/toggleBlockType.ts b/packages/core/src/commands/toggleBlockType.ts new file mode 100644 index 000000000..7181e2bc6 --- /dev/null +++ b/packages/core/src/commands/toggleBlockType.ts @@ -0,0 +1,30 @@ +import { NodeType } from 'prosemirror-model' +import { setBlockType } from 'prosemirror-commands' +import { Editor } from '../Editor' +import nodeIsActive from '../utils/nodeIsActive' + +declare module '../Editor' { + interface Editor { + toggleBlockType(type: NodeType, toggleType: NodeType, attrs?: {}): Editor, + } +} + +export default function toggleBlockType( + next: Function, + editor: Editor, + type: NodeType, + toggleType: NodeType, + attrs?: {}, +): void { + const { view, state } = editor + const isActive = nodeIsActive(state, type, attrs) + + if (isActive) { + setBlockType(toggleType)(view.state, view.dispatch) + next() + return + } + + setBlockType(type, attrs)(view.state, view.dispatch) + next() +} diff --git a/packages/extension-heading/index.ts b/packages/extension-heading/index.ts index ca5a663f4..577af280b 100644 --- a/packages/extension-heading/index.ts +++ b/packages/extension-heading/index.ts @@ -43,13 +43,13 @@ export default class Heading extends Node { } } - // commands(): CommandSpec { - // return { - // heading: (next, { view }) => { - // toggleBlockType(this.type)(view.state, view.dispatch) - // next() - // }, - // } - // } + commands(): CommandSpec { + return { + heading: (next, editor, attrs) => { + editor.toggleBlockType(this.type, editor.schema.nodes.paragraph, attrs) + next() + }, + } + } } \ No newline at end of file From 3deae61a15bf05f1b36f1575f5521c78c4646736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Tue, 21 Apr 2020 22:48:27 +0200 Subject: [PATCH 04/37] add heading input rule --- packages/extension-heading/index.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/extension-heading/index.ts b/packages/extension-heading/index.ts index 577af280b..3594b326e 100644 --- a/packages/extension-heading/index.ts +++ b/packages/extension-heading/index.ts @@ -1,5 +1,7 @@ import { Node, CommandSpec } from '@tiptap/core' import { NodeSpec } from 'prosemirror-model' +import VerEx from 'verbal-expressions' +import { textblockTypeInputRule } from 'prosemirror-inputrules' type Level = 1 | 2 | 3 | 4 | 5 | 6 @@ -52,4 +54,17 @@ export default class Heading extends Node { } } + inputRules() { + return this.options.levels.map((level: Level) => { + const regex = VerEx() + .startOfLine() + .find('#') + .repeatPrevious(level) + .whitespace() + .endOfLine() + + return textblockTypeInputRule(regex, this.type, { level }) + }) + } + } \ No newline at end of file From 91d4aa7e399693c72cf1c9e7994e1dec6c0a3746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Tue, 21 Apr 2020 23:22:27 +0200 Subject: [PATCH 05/37] improve type handling for commands --- packages/core/src/Editor.ts | 4 ++-- packages/core/src/commands/clearContent.ts | 6 ++++-- packages/core/src/commands/focus.ts | 6 ++++-- packages/core/src/commands/insertHTML.ts | 6 ++++-- packages/core/src/commands/insertText.ts | 6 ++++-- packages/core/src/commands/removeMarks.ts | 6 ++++-- packages/core/src/commands/setContent.ts | 16 ++++++++-------- packages/core/src/commands/toggleBlockType.ts | 16 ++++++++-------- packages/extension-bold/index.ts | 2 +- packages/extension-code/index.ts | 2 +- packages/extension-heading/index.ts | 4 ++-- packages/extension-history/index.ts | 4 ++-- packages/extension-italic/index.ts | 2 +- 13 files changed, 45 insertions(+), 35 deletions(-) diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index bd27c1866..e9754f5a3 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -21,7 +21,7 @@ import Node from './Node' import Mark from './Mark' import EventEmitter from './EventEmitter' -export type Command = (next: Function, editor: Editor, ...args: any) => any +export type Command = (next: Function, editor: Editor) => (...args: any) => any export interface CommandSpec { [key: string]: Command @@ -104,7 +104,7 @@ export class Editor extends EventEmitter { } this.commands[name] = this.chainCommand((...args: any) => { - return new Promise(resolve => callback(resolve, this.proxy, ...args)) + return new Promise(resolve => callback(resolve, this.proxy)(...args)) }) return this.proxy diff --git a/packages/core/src/commands/clearContent.ts b/packages/core/src/commands/clearContent.ts index db8c2a6aa..59abd230b 100644 --- a/packages/core/src/commands/clearContent.ts +++ b/packages/core/src/commands/clearContent.ts @@ -1,13 +1,15 @@ import { Editor } from '../Editor' import { TextSelection } from 'prosemirror-state' +type ClearContent = (emitUpdate?: Boolean) => any + declare module '../Editor' { interface Editor { - clearContent(emitUpdate?: Boolean): Editor, + clearContent: ClearContent, } } -export default function clearContent(next: Function, editor: Editor, emitUpdate = false): void { +export default (next: Function, editor: Editor, emitUpdate = false): ClearContent => emitUpdate => { editor.setContent('', emitUpdate) next() } diff --git a/packages/core/src/commands/focus.ts b/packages/core/src/commands/focus.ts index 00f8b3ee7..af93f1eaa 100644 --- a/packages/core/src/commands/focus.ts +++ b/packages/core/src/commands/focus.ts @@ -3,9 +3,11 @@ import { TextSelection } from 'prosemirror-state' import sleep from '../utils/sleep' import minMax from '../utils/minMax' +type Focus = (position?: Position) => any + declare module '../Editor' { interface Editor { - focus(position?: Position): Editor + focus: Focus, } } @@ -43,7 +45,7 @@ function resolveSelection(editor: Editor, position: Position = null): ResolvedSe } } -export default async function focus(next: Function, editor: Editor, position: Position = null): Promise { +export default (next: Function, editor: Editor, position: Position = null): Focus => async () => { const { view, state } = editor if ((view.hasFocus() && position === null)) { diff --git a/packages/core/src/commands/insertHTML.ts b/packages/core/src/commands/insertHTML.ts index f043ec31e..3597a052c 100644 --- a/packages/core/src/commands/insertHTML.ts +++ b/packages/core/src/commands/insertHTML.ts @@ -2,13 +2,15 @@ import { DOMParser } from 'prosemirror-model' import { Editor } from '../Editor' import elementFromString from '../utils/elementFromString' +type InsertHTML = (value: string) => any + declare module '../Editor' { interface Editor { - insertHTML(value: string): Editor, + insertHTML: InsertHTML, } } -export default function insertHTML(next: Function, editor: Editor, value: string): void { +export default (next: Function, editor: Editor, value: string): InsertHTML => () => { const { view, state } = editor const { selection } = state const element = elementFromString(value) diff --git a/packages/core/src/commands/insertText.ts b/packages/core/src/commands/insertText.ts index 9647fac04..2715c1bf5 100644 --- a/packages/core/src/commands/insertText.ts +++ b/packages/core/src/commands/insertText.ts @@ -1,12 +1,14 @@ import { Editor } from '../Editor' +type InsertText = (value: string) => any + declare module '../Editor' { interface Editor { - insertText(value: string): Editor, + insertText: InsertText, } } -export default function insertText(next: Function, editor: Editor, value: string): void { +export default (next: Function, editor: Editor, value: string): InsertText => () => { const { view, state } = editor const transaction = state.tr.insertText(value) diff --git a/packages/core/src/commands/removeMarks.ts b/packages/core/src/commands/removeMarks.ts index a002c641d..5514a60d0 100644 --- a/packages/core/src/commands/removeMarks.ts +++ b/packages/core/src/commands/removeMarks.ts @@ -1,12 +1,14 @@ import { Editor } from '../Editor' +type RemoveMarks = () => any + declare module '../Editor' { interface Editor { - removeMarks(): Editor, + removeMarks: RemoveMarks, } } -export default function removeMarks(next: Function, editor: Editor): void { +export default (next: Function, editor: Editor): RemoveMarks => () => { const { state, view, schema } = editor const { selection, tr } = state const { from, to, empty } = selection diff --git a/packages/core/src/commands/setContent.ts b/packages/core/src/commands/setContent.ts index 472c7ae54..4daf4260b 100644 --- a/packages/core/src/commands/setContent.ts +++ b/packages/core/src/commands/setContent.ts @@ -1,19 +1,19 @@ import { Editor } from '../Editor' import { TextSelection } from 'prosemirror-state' +type SetContent = ( + content: string, + emitUpdate?: Boolean, + parseOptions?: any, +) => any + declare module '../Editor' { interface Editor { - setContent(content: string, emitUpdate?: Boolean, parseOptions?: any): Editor, + setContent: SetContent, } } -export default function setContent( - next: Function, - editor: Editor, - content = null, - emitUpdate = false, - parseOptions = {}, -): void { +export default (next: Function, editor: Editor): SetContent => (content, emitUpdate, parseOptions) => { if (content === null) { next() return diff --git a/packages/core/src/commands/toggleBlockType.ts b/packages/core/src/commands/toggleBlockType.ts index 7181e2bc6..fb7945808 100644 --- a/packages/core/src/commands/toggleBlockType.ts +++ b/packages/core/src/commands/toggleBlockType.ts @@ -3,19 +3,19 @@ import { setBlockType } from 'prosemirror-commands' import { Editor } from '../Editor' import nodeIsActive from '../utils/nodeIsActive' +type ToggleBlockType = ( + type: NodeType, + toggleType: NodeType, + attrs?: {} +) => any + declare module '../Editor' { interface Editor { - toggleBlockType(type: NodeType, toggleType: NodeType, attrs?: {}): Editor, + toggleBlockType: ToggleBlockType, } } -export default function toggleBlockType( - next: Function, - editor: Editor, - type: NodeType, - toggleType: NodeType, - attrs?: {}, -): void { +export default (next: Function, editor: Editor): ToggleBlockType => (type, toggleType, attrs) => { const { view, state } = editor const isActive = nodeIsActive(state, type, attrs) diff --git a/packages/extension-bold/index.ts b/packages/extension-bold/index.ts index 88d633f8e..37f812ffb 100644 --- a/packages/extension-bold/index.ts +++ b/packages/extension-bold/index.ts @@ -34,7 +34,7 @@ export default class Bold extends Mark { commands(): CommandSpec { return { - bold: (next, { view }) => { + bold: (next, { view }) => () => { toggleMark(this.type)(view.state, view.dispatch) next() }, diff --git a/packages/extension-code/index.ts b/packages/extension-code/index.ts index 821eccfc5..28a365f68 100644 --- a/packages/extension-code/index.ts +++ b/packages/extension-code/index.ts @@ -25,7 +25,7 @@ export default class Code extends Mark { commands(): CommandSpec { return { - code: (next, { view }) => { + code: (next, { view }) => () => { toggleMark(this.type)(view.state, view.dispatch) next() }, diff --git a/packages/extension-heading/index.ts b/packages/extension-heading/index.ts index 3594b326e..b5e200d9d 100644 --- a/packages/extension-heading/index.ts +++ b/packages/extension-heading/index.ts @@ -47,8 +47,8 @@ export default class Heading extends Node { commands(): CommandSpec { return { - heading: (next, editor, attrs) => { - editor.toggleBlockType(this.type, editor.schema.nodes.paragraph, attrs) + heading: next => attrs => { + this.editor.toggleBlockType(this.type, this.editor.schema.nodes.paragraph, attrs) next() }, } diff --git a/packages/extension-history/index.ts b/packages/extension-history/index.ts index 57366277a..911e0e1fe 100644 --- a/packages/extension-history/index.ts +++ b/packages/extension-history/index.ts @@ -34,11 +34,11 @@ export default class History extends Extension { commands(): CommandSpec { return { - undo: (next, { view }) => { + undo: (next, { view }) => () => { undo(view.state, view.dispatch) next() }, - redo: (next, { view }) => { + redo: (next, { view }) => () => { redo(view.state, view.dispatch) next() }, diff --git a/packages/extension-italic/index.ts b/packages/extension-italic/index.ts index fd1acdf22..3e80965ae 100644 --- a/packages/extension-italic/index.ts +++ b/packages/extension-italic/index.ts @@ -26,7 +26,7 @@ export default class Italic extends Mark { commands(): CommandSpec { return { - italic: (next, { view }) => { + italic: (next, { view }) => () => { toggleMark(this.type)(view.state, view.dispatch) next() }, From 023c16a4f57f8ae58fe24745df577cdb6ae0f2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Tue, 21 Apr 2020 23:48:50 +0200 Subject: [PATCH 06/37] some fixes --- packages/core/src/commands/clearContent.ts | 2 +- packages/core/src/commands/focus.ts | 2 +- packages/core/src/commands/insertHTML.ts | 2 +- packages/core/src/commands/insertText.ts | 2 +- packages/core/src/commands/setContent.ts | 2 +- packages/core/src/commands/toggleBlockType.ts | 5 ++--- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/core/src/commands/clearContent.ts b/packages/core/src/commands/clearContent.ts index 59abd230b..5a38fe8e6 100644 --- a/packages/core/src/commands/clearContent.ts +++ b/packages/core/src/commands/clearContent.ts @@ -9,7 +9,7 @@ declare module '../Editor' { } } -export default (next: Function, editor: Editor, emitUpdate = false): ClearContent => emitUpdate => { +export default (next: Function, editor: Editor): ClearContent => (emitUpdate = false) => { editor.setContent('', emitUpdate) next() } diff --git a/packages/core/src/commands/focus.ts b/packages/core/src/commands/focus.ts index af93f1eaa..01312b4a5 100644 --- a/packages/core/src/commands/focus.ts +++ b/packages/core/src/commands/focus.ts @@ -45,7 +45,7 @@ function resolveSelection(editor: Editor, position: Position = null): ResolvedSe } } -export default (next: Function, editor: Editor, position: Position = null): Focus => async () => { +export default (next: Function, editor: Editor): Focus => async (position = null) => { const { view, state } = editor if ((view.hasFocus() && position === null)) { diff --git a/packages/core/src/commands/insertHTML.ts b/packages/core/src/commands/insertHTML.ts index 3597a052c..a09a33efd 100644 --- a/packages/core/src/commands/insertHTML.ts +++ b/packages/core/src/commands/insertHTML.ts @@ -10,7 +10,7 @@ declare module '../Editor' { } } -export default (next: Function, editor: Editor, value: string): InsertHTML => () => { +export default (next: Function, editor: Editor): InsertHTML => value => { const { view, state } = editor const { selection } = state const element = elementFromString(value) diff --git a/packages/core/src/commands/insertText.ts b/packages/core/src/commands/insertText.ts index 2715c1bf5..4877ca2da 100644 --- a/packages/core/src/commands/insertText.ts +++ b/packages/core/src/commands/insertText.ts @@ -8,7 +8,7 @@ declare module '../Editor' { } } -export default (next: Function, editor: Editor, value: string): InsertText => () => { +export default (next: Function, editor: Editor): InsertText => value => { const { view, state } = editor const transaction = state.tr.insertText(value) diff --git a/packages/core/src/commands/setContent.ts b/packages/core/src/commands/setContent.ts index 4daf4260b..f2e9d5419 100644 --- a/packages/core/src/commands/setContent.ts +++ b/packages/core/src/commands/setContent.ts @@ -13,7 +13,7 @@ declare module '../Editor' { } } -export default (next: Function, editor: Editor): SetContent => (content, emitUpdate, parseOptions) => { +export default (next: Function, editor: Editor): SetContent => (content, emitUpdate = false, parseOptions = {}) => { if (content === null) { next() return diff --git a/packages/core/src/commands/toggleBlockType.ts b/packages/core/src/commands/toggleBlockType.ts index fb7945808..35277d6ac 100644 --- a/packages/core/src/commands/toggleBlockType.ts +++ b/packages/core/src/commands/toggleBlockType.ts @@ -21,10 +21,9 @@ export default (next: Function, editor: Editor): ToggleBlockType => (type, toggl if (isActive) { setBlockType(toggleType)(view.state, view.dispatch) - next() - return + } else { + setBlockType(type, attrs)(view.state, view.dispatch) } - setBlockType(type, attrs)(view.state, view.dispatch) next() } From 3d02da20fffe8e897fcdda50411e43b37e6365fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 00:09:31 +0200 Subject: [PATCH 07/37] add getNodeType --- packages/core/src/commands/toggleBlockType.ts | 11 +++++++---- packages/core/src/utils/getNodeType.ts | 9 +++++++++ packages/extension-heading/index.ts | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 packages/core/src/utils/getNodeType.ts diff --git a/packages/core/src/commands/toggleBlockType.ts b/packages/core/src/commands/toggleBlockType.ts index 35277d6ac..fdbc2015b 100644 --- a/packages/core/src/commands/toggleBlockType.ts +++ b/packages/core/src/commands/toggleBlockType.ts @@ -2,10 +2,11 @@ import { NodeType } from 'prosemirror-model' import { setBlockType } from 'prosemirror-commands' import { Editor } from '../Editor' import nodeIsActive from '../utils/nodeIsActive' +import getNodeType from '../utils/getNodeType' type ToggleBlockType = ( - type: NodeType, - toggleType: NodeType, + type: string | NodeType, + toggleType: string | NodeType, attrs?: {} ) => any @@ -15,8 +16,10 @@ declare module '../Editor' { } } -export default (next: Function, editor: Editor): ToggleBlockType => (type, toggleType, attrs) => { - const { view, state } = editor +export default (next: Function, editor: Editor): ToggleBlockType => (typeOrName, toggleTypeOrName, attrs) => { + const { view, state, schema } = editor + const type = getNodeType(typeOrName, schema) + const toggleType = getNodeType(toggleTypeOrName, schema) const isActive = nodeIsActive(state, type, attrs) if (isActive) { diff --git a/packages/core/src/utils/getNodeType.ts b/packages/core/src/utils/getNodeType.ts new file mode 100644 index 000000000..58da4f70b --- /dev/null +++ b/packages/core/src/utils/getNodeType.ts @@ -0,0 +1,9 @@ +import { NodeType, Schema } from 'prosemirror-model' + +export default function getNodeType(nameOrType: string | NodeType, schema: Schema): NodeType { + if (typeof nameOrType === 'string') { + return schema.nodes[nameOrType] + } + + return nameOrType +} diff --git a/packages/extension-heading/index.ts b/packages/extension-heading/index.ts index b5e200d9d..8471a7b69 100644 --- a/packages/extension-heading/index.ts +++ b/packages/extension-heading/index.ts @@ -48,7 +48,7 @@ export default class Heading extends Node { commands(): CommandSpec { return { heading: next => attrs => { - this.editor.toggleBlockType(this.type, this.editor.schema.nodes.paragraph, attrs) + this.editor.toggleBlockType(this.name, 'paragraph', attrs) next() }, } From adb44f317c6265bf7dbb17379266d594e5740686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 00:17:36 +0200 Subject: [PATCH 08/37] add selectall command --- packages/core/src/Editor.ts | 1 + packages/core/src/commands/selectAll.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 packages/core/src/commands/selectAll.ts diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index e9754f5a3..584ea9b55 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -72,6 +72,7 @@ export class Editor extends EventEmitter { this.registerCommand('clearContent', require('./commands/clearContent').default) this.registerCommand('removeMarks', require('./commands/removeMarks').default) this.registerCommand('toggleBlockType', require('./commands/toggleBlockType').default) + this.registerCommand('selectAll', require('./commands/selectAll').default) if (this.options.injectCSS) { require('./style.css') diff --git a/packages/core/src/commands/selectAll.ts b/packages/core/src/commands/selectAll.ts new file mode 100644 index 000000000..6d08a2c58 --- /dev/null +++ b/packages/core/src/commands/selectAll.ts @@ -0,0 +1,15 @@ +import { Editor } from '../Editor' +import { selectAll } from 'prosemirror-commands' + +type SelectAll = () => any + +declare module '../Editor' { + interface Editor { + selectAll: SelectAll, + } +} + +export default (next: Function, { state, view }: Editor): SelectAll => () => { + selectAll(state, view.dispatch) + next() +} From 0e18e67c93e23d7e421e0142781a49260e9180c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 09:23:53 +0200 Subject: [PATCH 09/37] add list of current commands --- docs/src/data/posts/commands.md | 34 ++++++++++++++++++- packages/core/src/Editor.ts | 2 +- .../{toggleBlockType.ts => toggleNode.ts} | 6 ++-- packages/extension-heading/index.ts | 2 +- 4 files changed, 38 insertions(+), 6 deletions(-) rename packages/core/src/commands/{toggleBlockType.ts => toggleNode.ts} (80%) diff --git a/docs/src/data/posts/commands.md b/docs/src/data/posts/commands.md index e6827429d..7f8f65315 100644 --- a/docs/src/data/posts/commands.md +++ b/docs/src/data/posts/commands.md @@ -1 +1,33 @@ -# Commands \ No newline at end of file +# Commands + +## .clearContent() + +Clear the whole document. + +## .focus() + +Focus the editor at the given position. + +## .insertHTML() + +Insert a string of HTML at the currently selected position. + +## .insertText() + +Insert a string of text at the currently selected position. + +## .removeMarks() + +Remove all marks in the current selection. + +## .selectAll() + +Select the whole document. + +## .setContent() + +Replace the whole document with new content. + +## .toggleNode() + +Toggle a node with another node. \ No newline at end of file diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index 584ea9b55..8b2a3005d 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -71,7 +71,7 @@ export class Editor extends EventEmitter { this.registerCommand('setContent', require('./commands/setContent').default) this.registerCommand('clearContent', require('./commands/clearContent').default) this.registerCommand('removeMarks', require('./commands/removeMarks').default) - this.registerCommand('toggleBlockType', require('./commands/toggleBlockType').default) + this.registerCommand('toggleNode', require('./commands/toggleNode').default) this.registerCommand('selectAll', require('./commands/selectAll').default) if (this.options.injectCSS) { diff --git a/packages/core/src/commands/toggleBlockType.ts b/packages/core/src/commands/toggleNode.ts similarity index 80% rename from packages/core/src/commands/toggleBlockType.ts rename to packages/core/src/commands/toggleNode.ts index fdbc2015b..32ab13d04 100644 --- a/packages/core/src/commands/toggleBlockType.ts +++ b/packages/core/src/commands/toggleNode.ts @@ -4,7 +4,7 @@ import { Editor } from '../Editor' import nodeIsActive from '../utils/nodeIsActive' import getNodeType from '../utils/getNodeType' -type ToggleBlockType = ( +type ToggleNode = ( type: string | NodeType, toggleType: string | NodeType, attrs?: {} @@ -12,11 +12,11 @@ type ToggleBlockType = ( declare module '../Editor' { interface Editor { - toggleBlockType: ToggleBlockType, + toggleNode: ToggleNode, } } -export default (next: Function, editor: Editor): ToggleBlockType => (typeOrName, toggleTypeOrName, attrs) => { +export default (next: Function, editor: Editor): ToggleNode => (typeOrName, toggleTypeOrName, attrs) => { const { view, state, schema } = editor const type = getNodeType(typeOrName, schema) const toggleType = getNodeType(toggleTypeOrName, schema) diff --git a/packages/extension-heading/index.ts b/packages/extension-heading/index.ts index 8471a7b69..c8d72ea12 100644 --- a/packages/extension-heading/index.ts +++ b/packages/extension-heading/index.ts @@ -48,7 +48,7 @@ export default class Heading extends Node { commands(): CommandSpec { return { heading: next => attrs => { - this.editor.toggleBlockType(this.name, 'paragraph', attrs) + this.editor.toggleNode(this.name, 'paragraph', attrs) next() }, } From 4e6404a404802b7a4bd94a7865ec3a9d4000aea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 14:06:15 +0200 Subject: [PATCH 10/37] add toggleMark command --- packages/core/src/Editor.ts | 12 ++++++----- .../core/src/commands/selectParentNode.ts | 15 ++++++++++++++ packages/core/src/commands/toggleMark.ts | 20 +++++++++++++++++++ packages/core/src/utils/getMarkType.ts | 9 +++++++++ packages/extension-bold/index.ts | 5 ++--- packages/extension-code/index.ts | 5 ++--- packages/extension-italic/index.ts | 5 ++--- 7 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 packages/core/src/commands/selectParentNode.ts create mode 100644 packages/core/src/commands/toggleMark.ts create mode 100644 packages/core/src/utils/getMarkType.ts diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index 8b2a3005d..6ea4fe49b 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -65,14 +65,16 @@ export class Editor extends EventEmitter { this.createExtensionManager() this.createSchema() this.createView() - this.registerCommand('focus', require('./commands/focus').default) - this.registerCommand('insertText', require('./commands/insertText').default) - this.registerCommand('insertHTML', require('./commands/insertHTML').default) - this.registerCommand('setContent', require('./commands/setContent').default) this.registerCommand('clearContent', require('./commands/clearContent').default) + this.registerCommand('focus', require('./commands/focus').default) + this.registerCommand('insertHTML', require('./commands/insertHTML').default) + this.registerCommand('insertText', require('./commands/insertText').default) this.registerCommand('removeMarks', require('./commands/removeMarks').default) - this.registerCommand('toggleNode', require('./commands/toggleNode').default) this.registerCommand('selectAll', require('./commands/selectAll').default) + this.registerCommand('selectParentNode', require('./commands/selectParentNode').default) + this.registerCommand('setContent', require('./commands/setContent').default) + this.registerCommand('toggleMark', require('./commands/toggleMark').default) + this.registerCommand('toggleNode', require('./commands/toggleNode').default) if (this.options.injectCSS) { require('./style.css') diff --git a/packages/core/src/commands/selectParentNode.ts b/packages/core/src/commands/selectParentNode.ts new file mode 100644 index 000000000..0d22582c6 --- /dev/null +++ b/packages/core/src/commands/selectParentNode.ts @@ -0,0 +1,15 @@ +import { Editor } from '../Editor' +import { selectParentNode } from 'prosemirror-commands' + +type SelectParentNode = () => any + +declare module '../Editor' { + interface Editor { + selectParentNode: SelectParentNode, + } +} + +export default (next: Function, { state, view }: Editor): SelectParentNode => () => { + selectParentNode(state, view.dispatch) + next() +} diff --git a/packages/core/src/commands/toggleMark.ts b/packages/core/src/commands/toggleMark.ts new file mode 100644 index 000000000..8441a2723 --- /dev/null +++ b/packages/core/src/commands/toggleMark.ts @@ -0,0 +1,20 @@ +import { Editor } from '../Editor' +import { toggleMark } from 'prosemirror-commands' +import { MarkType } from 'prosemirror-model' +import getMarkType from '../utils/getMarkType' + +type ToggleMark = (type: string | MarkType) => any + +declare module '../Editor' { + interface Editor { + toggleMark: ToggleMark, + } +} + +export default (next: Function, editor: Editor): ToggleMark => (typeOrName) => { + const { view, state, schema } = editor + const type = getMarkType(typeOrName, schema) + + toggleMark(type)(state, view.dispatch) + next() +} diff --git a/packages/core/src/utils/getMarkType.ts b/packages/core/src/utils/getMarkType.ts new file mode 100644 index 000000000..c65100b21 --- /dev/null +++ b/packages/core/src/utils/getMarkType.ts @@ -0,0 +1,9 @@ +import { MarkType, Schema } from 'prosemirror-model' + +export default function getMarkType(nameOrType: string | MarkType, schema: Schema): MarkType { + if (typeof nameOrType === 'string') { + return schema.marks[nameOrType] + } + + return nameOrType +} diff --git a/packages/extension-bold/index.ts b/packages/extension-bold/index.ts index 37f812ffb..2daffc539 100644 --- a/packages/extension-bold/index.ts +++ b/packages/extension-bold/index.ts @@ -1,5 +1,4 @@ import { Mark, CommandSpec, markInputRule, markPasteRule } from '@tiptap/core' -import { toggleMark } from 'prosemirror-commands' import { MarkSpec } from 'prosemirror-model' import VerEx from 'verbal-expressions' @@ -34,8 +33,8 @@ export default class Bold extends Mark { commands(): CommandSpec { return { - bold: (next, { view }) => () => { - toggleMark(this.type)(view.state, view.dispatch) + bold: next => () => { + this.editor.toggleMark(this.name) next() }, } diff --git a/packages/extension-code/index.ts b/packages/extension-code/index.ts index 28a365f68..815b2aee9 100644 --- a/packages/extension-code/index.ts +++ b/packages/extension-code/index.ts @@ -1,5 +1,4 @@ import { Mark, markInputRule, markPasteRule, CommandSpec } from '@tiptap/core' -import { toggleMark } from 'prosemirror-commands' import { MarkSpec } from 'prosemirror-model' import VerEx from 'verbal-expressions' @@ -25,8 +24,8 @@ export default class Code extends Mark { commands(): CommandSpec { return { - code: (next, { view }) => () => { - toggleMark(this.type)(view.state, view.dispatch) + code: next => () => { + this.editor.toggleMark(this.name) next() }, } diff --git a/packages/extension-italic/index.ts b/packages/extension-italic/index.ts index 3e80965ae..d0492e4b8 100644 --- a/packages/extension-italic/index.ts +++ b/packages/extension-italic/index.ts @@ -1,5 +1,4 @@ import { Mark, markInputRule, markPasteRule, CommandSpec } from '@tiptap/core' -import { toggleMark } from 'prosemirror-commands' import { MarkSpec } from 'prosemirror-model' import VerEx from 'verbal-expressions' @@ -26,8 +25,8 @@ export default class Italic extends Mark { commands(): CommandSpec { return { - italic: (next, { view }) => () => { - toggleMark(this.type)(view.state, view.dispatch) + italic: next => () => { + this.editor.toggleMark(this.name) next() }, } From fd4453f7c5dda888401dce57c9ba9e9467c3e77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 14:08:25 +0200 Subject: [PATCH 11/37] update docs --- docs/src/data/posts/commands.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/src/data/posts/commands.md b/docs/src/data/posts/commands.md index 7f8f65315..e6d808c9f 100644 --- a/docs/src/data/posts/commands.md +++ b/docs/src/data/posts/commands.md @@ -24,10 +24,18 @@ Remove all marks in the current selection. Select the whole document. +## .selectParentNode() + +Select the parent node. + ## .setContent() Replace the whole document with new content. +## .toggleMark() + +Toggle a mark on and off. + ## .toggleNode() Toggle a node with another node. \ No newline at end of file From fd8f5de375a72553edec0145745ec514f4a388c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 14:22:31 +0200 Subject: [PATCH 12/37] add removeMark command --- docs/src/data/posts/commands.md | 4 +++ packages/core/src/Editor.ts | 1 + packages/core/src/commands/removeMark.ts | 32 +++++++++++++++++++ packages/core/src/utils/getMarkRange.ts | 40 ++++++++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 packages/core/src/commands/removeMark.ts create mode 100644 packages/core/src/utils/getMarkRange.ts diff --git a/docs/src/data/posts/commands.md b/docs/src/data/posts/commands.md index e6d808c9f..38b48276a 100644 --- a/docs/src/data/posts/commands.md +++ b/docs/src/data/posts/commands.md @@ -16,6 +16,10 @@ Insert a string of HTML at the currently selected position. Insert a string of text at the currently selected position. +## .removeMark() + +Remove a mark in the current selection. + ## .removeMarks() Remove all marks in the current selection. diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index 6ea4fe49b..79b0373eb 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -69,6 +69,7 @@ export class Editor extends EventEmitter { this.registerCommand('focus', require('./commands/focus').default) this.registerCommand('insertHTML', require('./commands/insertHTML').default) this.registerCommand('insertText', require('./commands/insertText').default) + this.registerCommand('removeMark', require('./commands/removeMark').default) this.registerCommand('removeMarks', require('./commands/removeMarks').default) this.registerCommand('selectAll', require('./commands/selectAll').default) this.registerCommand('selectParentNode', require('./commands/selectParentNode').default) diff --git a/packages/core/src/commands/removeMark.ts b/packages/core/src/commands/removeMark.ts new file mode 100644 index 000000000..d9a8a47af --- /dev/null +++ b/packages/core/src/commands/removeMark.ts @@ -0,0 +1,32 @@ +import { Editor } from '../Editor' +import { MarkType } from 'prosemirror-model' +import getMarkType from '../utils/getMarkType' +import getMarkRange from '../utils/getMarkRange' + +type RemoveMark = (type: string | MarkType) => any + +declare module '../Editor' { + interface Editor { + toggleMark: RemoveMark, + } +} + +export default (next: Function, editor: Editor): RemoveMark => typeOrName => { + const { view, state, schema } = editor + const { tr, selection } = state + const type = getMarkType(typeOrName, schema) + let { from, to, $from, empty } = selection + + if (empty) { + const range = getMarkRange($from, type) + + if (range) { + from = range.from + to = range.to + } + } + + tr.removeMark(from, to, type) + view.dispatch(tr) + next() +} diff --git a/packages/core/src/utils/getMarkRange.ts b/packages/core/src/utils/getMarkRange.ts new file mode 100644 index 000000000..8498a2de3 --- /dev/null +++ b/packages/core/src/utils/getMarkRange.ts @@ -0,0 +1,40 @@ +import { MarkType, ResolvedPos } from 'prosemirror-model' + +interface Range { + from: number, + to: number, +} + +export default function getMarkRange($pos: ResolvedPos, type: MarkType): Range | void { + if (!$pos || !type) { + return + } + + const start = $pos.parent.childAfter($pos.parentOffset) + + if (!start.node) { + return + } + + const link = start.node.marks.find(mark => mark.type === type) + if (!link) { + return + } + + let startIndex = $pos.index() + let startPos = $pos.start() + start.offset + let endIndex = startIndex + 1 + let endPos = startPos + start.node.nodeSize + + while (startIndex > 0 && link.isInSet($pos.parent.child(startIndex - 1).marks)) { + startIndex -= 1 + startPos -= $pos.parent.child(startIndex).nodeSize + } + + while (endIndex < $pos.parent.childCount && link.isInSet($pos.parent.child(endIndex).marks)) { + endPos += $pos.parent.child(endIndex).nodeSize + endIndex += 1 + } + + return { from: startPos, to: endPos } +} From 9f53481e016ce23d325524e1b9081eaad1c753e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 14:38:15 +0200 Subject: [PATCH 13/37] add replaceWithNode command --- docs/src/data/posts/commands.md | 4 +++ packages/core/src/Editor.ts | 1 + packages/core/src/commands/replaceWithNode.ts | 35 +++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 packages/core/src/commands/replaceWithNode.ts diff --git a/docs/src/data/posts/commands.md b/docs/src/data/posts/commands.md index 38b48276a..118d5ceb2 100644 --- a/docs/src/data/posts/commands.md +++ b/docs/src/data/posts/commands.md @@ -24,6 +24,10 @@ Remove a mark in the current selection. Remove all marks in the current selection. +## .replaceWithNode() + +Replace a given range with a node. + ## .selectAll() Select the whole document. diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index 79b0373eb..66b0364ae 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -71,6 +71,7 @@ export class Editor extends EventEmitter { this.registerCommand('insertText', require('./commands/insertText').default) this.registerCommand('removeMark', require('./commands/removeMark').default) this.registerCommand('removeMarks', require('./commands/removeMarks').default) + this.registerCommand('replaceWithNode', require('./commands/replaceWithNode').default) this.registerCommand('selectAll', require('./commands/selectAll').default) this.registerCommand('selectParentNode', require('./commands/selectParentNode').default) this.registerCommand('setContent', require('./commands/setContent').default) diff --git a/packages/core/src/commands/replaceWithNode.ts b/packages/core/src/commands/replaceWithNode.ts new file mode 100644 index 000000000..b29d38824 --- /dev/null +++ b/packages/core/src/commands/replaceWithNode.ts @@ -0,0 +1,35 @@ +import { Editor } from '../Editor' +import { NodeType } from 'prosemirror-model' +import getNodeType from '../utils/getNodeType' + +interface Range { + from: number, + to: number, +} + +type ReplaceWithNode = ( + range: Range, + type: NodeType, + attrs: {}, +) => any + +declare module '../Editor' { + interface Editor { + replaceText: ReplaceWithNode, + } +} + +export default (next: Function, editor: Editor): ReplaceWithNode => (range, typeOrName, attrs) => { + const { view, state, schema } = editor + const { $from, $to } = state.selection + const type = getNodeType(typeOrName, schema) + const index = $from.index() + const from = range ? range.from : $from.pos + const to = range ? range.to : $to.pos + + if ($from.parent.canReplaceWith(index, index, type)) { + view.dispatch(state.tr.replaceWith(from, to, type.create(attrs))) + } + + next() +} From bddd5b3da74d8db21d61205e2651659d69ef85c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 14:41:43 +0200 Subject: [PATCH 14/37] change order --- packages/core/src/commands/replaceWithNode.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/commands/replaceWithNode.ts b/packages/core/src/commands/replaceWithNode.ts index b29d38824..3d6245950 100644 --- a/packages/core/src/commands/replaceWithNode.ts +++ b/packages/core/src/commands/replaceWithNode.ts @@ -8,9 +8,9 @@ interface Range { } type ReplaceWithNode = ( - range: Range, type: NodeType, attrs: {}, + range?: Range, ) => any declare module '../Editor' { @@ -19,7 +19,7 @@ declare module '../Editor' { } } -export default (next: Function, editor: Editor): ReplaceWithNode => (range, typeOrName, attrs) => { +export default (next: Function, editor: Editor): ReplaceWithNode => (typeOrName, attrs, range) => { const { view, state, schema } = editor const { $from, $to } = state.selection const type = getNodeType(typeOrName, schema) From 4cfadf7d7f9a04fb6082b5358f6ec93500b2b242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 14:50:02 +0200 Subject: [PATCH 15/37] add updateMark command --- docs/src/data/posts/commands.md | 6 +++- packages/core/src/Editor.ts | 1 + packages/core/src/commands/updateMark.ts | 41 ++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/commands/updateMark.ts diff --git a/docs/src/data/posts/commands.md b/docs/src/data/posts/commands.md index 118d5ceb2..8e6f27da9 100644 --- a/docs/src/data/posts/commands.md +++ b/docs/src/data/posts/commands.md @@ -46,4 +46,8 @@ Toggle a mark on and off. ## .toggleNode() -Toggle a node with another node. \ No newline at end of file +Toggle a node with another node. + +## .updateMark() + +Update a mark with new attributes. \ No newline at end of file diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index 66b0364ae..8b7a0b3fb 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -77,6 +77,7 @@ export class Editor extends EventEmitter { this.registerCommand('setContent', require('./commands/setContent').default) this.registerCommand('toggleMark', require('./commands/toggleMark').default) this.registerCommand('toggleNode', require('./commands/toggleNode').default) + this.registerCommand('updateMark', require('./commands/updateMark').default) if (this.options.injectCSS) { require('./style.css') diff --git a/packages/core/src/commands/updateMark.ts b/packages/core/src/commands/updateMark.ts new file mode 100644 index 000000000..e5d81eef2 --- /dev/null +++ b/packages/core/src/commands/updateMark.ts @@ -0,0 +1,41 @@ +import { Editor } from '../Editor' +import { MarkType } from 'prosemirror-model' +import getMarkType from '../utils/getMarkType' +import getMarkRange from '../utils/getMarkRange' + +type UpdateMark = ( + type: string | MarkType, + attrs: {}, +) => any + +declare module '../Editor' { + interface Editor { + updateMark: UpdateMark, + } +} + +export default (next: Function, editor: Editor): UpdateMark => (typeOrName, attrs) => { + const { view, state, schema } = editor + const { tr, selection, doc } = state + let { from, to, $from, empty } = selection + const type = getMarkType(typeOrName, 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)) + view.dispatch(tr) + next() +} From 8aca2852ba2f5a69517512a732c33e4c4c026e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 14:59:34 +0200 Subject: [PATCH 16/37] add deleteSelection command --- docs/src/data/posts/commands.md | 4 ++++ packages/core/src/Editor.ts | 1 + packages/core/src/commands/deleteSelection.ts | 15 +++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 packages/core/src/commands/deleteSelection.ts diff --git a/docs/src/data/posts/commands.md b/docs/src/data/posts/commands.md index 8e6f27da9..59492f46b 100644 --- a/docs/src/data/posts/commands.md +++ b/docs/src/data/posts/commands.md @@ -4,6 +4,10 @@ Clear the whole document. +## .deleteSelection() + +Delete the selection, if there is one. + ## .focus() Focus the editor at the given position. diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index 8b7a0b3fb..add815854 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -66,6 +66,7 @@ export class Editor extends EventEmitter { this.createSchema() this.createView() this.registerCommand('clearContent', require('./commands/clearContent').default) + this.registerCommand('deleteSelection', require('./commands/deleteSelection').default) this.registerCommand('focus', require('./commands/focus').default) this.registerCommand('insertHTML', require('./commands/insertHTML').default) this.registerCommand('insertText', require('./commands/insertText').default) diff --git a/packages/core/src/commands/deleteSelection.ts b/packages/core/src/commands/deleteSelection.ts new file mode 100644 index 000000000..1a67e0860 --- /dev/null +++ b/packages/core/src/commands/deleteSelection.ts @@ -0,0 +1,15 @@ +import { Editor } from '../Editor' +import { deleteSelection } from 'prosemirror-commands' + +type DeleteSelection = () => any + +declare module '../Editor' { + interface Editor { + deleteSelection: DeleteSelection, + } +} + +export default (next: Function, { state, view }: Editor): DeleteSelection => () => { + deleteSelection(state, view.dispatch) + next() +} From 7e0c0b7a282814f1f30d11df5b38130c2b2a1aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 19:01:39 +0200 Subject: [PATCH 17/37] fix a bug --- packages/core/src/utils/nodeIsActive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/utils/nodeIsActive.ts b/packages/core/src/utils/nodeIsActive.ts index e75a15785..9f8dd83fe 100644 --- a/packages/core/src/utils/nodeIsActive.ts +++ b/packages/core/src/utils/nodeIsActive.ts @@ -11,5 +11,5 @@ export default function nodeIsActive(state: EditorState, type: NodeType, attrs = return !!node } - return node.node.hasMarkup(type, attrs) + return node.node.hasMarkup(type, { ...node.node.attrs, ...attrs }) } From d8f041bc9ca8f47a1d00c8a9b663c4c229a9c888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 21:23:24 +0200 Subject: [PATCH 18/37] add scrollable sidebar --- docs/src/layouts/App/base.scss | 8 ++++++-- docs/src/layouts/App/index.vue | 28 +++++++++++++++------------- docs/src/layouts/App/style.scss | 20 +++++++++++++++++++- docs/src/variables.scss | 1 + 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/docs/src/layouts/App/base.scss b/docs/src/layouts/App/base.scss index f47ac757a..0cecf87f4 100644 --- a/docs/src/layouts/App/base.scss +++ b/docs/src/layouts/App/base.scss @@ -24,7 +24,11 @@ border: 4px solid rgba(0, 0, 0, 0); background-clip: padding-box; border-radius: 8px; - background-color: $colorBlack; + background-color: rgba($colorBlack, 0); +} + +:hover::-webkit-scrollbar-thumb { + background-color: rgba($colorBlack, 0.1); } ::-webkit-scrollbar-button { @@ -48,7 +52,7 @@ body { -moz-osx-font-smoothing: grayscale; line-height: 1.7; font-feature-settings: 'cv05' 1; - background-color: rgba($colorBlack, 0.02); + background-color: $colorBackground; height: 100%; } diff --git a/docs/src/layouts/App/index.vue b/docs/src/layouts/App/index.vue index a1a2b6581..a4154f3bb 100644 --- a/docs/src/layouts/App/index.vue +++ b/docs/src/layouts/App/index.vue @@ -11,20 +11,22 @@ />
- + +
diff --git a/docs/src/layouts/App/style.scss b/docs/src/layouts/App/style.scss index 86705888b..6939d4121 100644 --- a/docs/src/layouts/App/style.scss +++ b/docs/src/layouts/App/style.scss @@ -51,26 +51,44 @@ } &__header { + align-self: center; + position: fixed; + z-index: 2; + width: 100%; + top: 0; flex: 0 0 auto; align-items: center; justify-content: space-between; padding-top: 2rem; padding-bottom: 2rem; + background-color: rgba($colorBackground, 0.8); + backdrop-filter: blur(10px); } &__content { flex: 1 1 auto; } - &__sidebar { + &__sidebar-wrapper { flex: 0 0 auto; + align-self: flex-start; + position: sticky; + top: 0; width: 18rem; + height: 100vh; padding-right: 3rem; + padding-top: 100px; + } + + &__sidebar { + overflow: auto; + height: 100%; } &__main { flex: 1 1 auto; min-width: 0; + padding-top: 100px; } &__inner { diff --git a/docs/src/variables.scss b/docs/src/variables.scss index bbb10d0ba..8badbfb93 100644 --- a/docs/src/variables.scss +++ b/docs/src/variables.scss @@ -1,5 +1,6 @@ $colorWhite: #FFF; $colorBlack: #000; +$colorBackground: mix($colorBlack, $colorWhite, 2%); /* Default Equations */ $linear: cubic-bezier(0.250, 0.250, 0.750, 0.750); From 1705ca8fb287e697f33ec0e7505bf3e8c3b81302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 21:26:03 +0200 Subject: [PATCH 19/37] hide menu for now --- docs/src/layouts/App/style.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/src/layouts/App/style.scss b/docs/src/layouts/App/style.scss index 6939d4121..c1dd037b3 100644 --- a/docs/src/layouts/App/style.scss +++ b/docs/src/layouts/App/style.scss @@ -78,6 +78,10 @@ height: 100vh; padding-right: 3rem; padding-top: 100px; + + @media (max-width: 750px) { + display: none; + } } &__sidebar { From 1537f4df3debe95f1756fc6a0712f416797c7237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 22 Apr 2020 21:57:26 +0200 Subject: [PATCH 20/37] improve layout --- docs/src/layouts/App/index.vue | 18 +++++----- docs/src/layouts/App/style.scss | 63 ++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/docs/src/layouts/App/index.vue b/docs/src/layouts/App/index.vue index a4154f3bb..c3bad835b 100644 --- a/docs/src/layouts/App/index.vue +++ b/docs/src/layouts/App/index.vue @@ -1,14 +1,16 @@ diff --git a/docs/src/demos/React/components/Editor.js b/docs/src/demos/React/components/Editor.jsx similarity index 100% rename from docs/src/demos/React/components/Editor.js rename to docs/src/demos/React/components/Editor.jsx diff --git a/yarn.lock b/yarn.lock index 179ba6bfd..89568b950 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3845,7 +3845,7 @@ collapse-white-space@^1.0.0, collapse-white-space@^1.0.2: resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== -collect.js@^4.23.0: +collect.js@^4.23.0, collect.js@^4.25.0: version "4.25.0" resolved "https://registry.yarnpkg.com/collect.js/-/collect.js-4.25.0.tgz#3cc9580935997263ab283488d760f9956c420e8b" integrity sha512-Wk+cWM9iQouzCe2RulakcE6BKweADOHYcz3pVcO2e6jRPfTuZWiLmAjJ2+lI3K9ldFyp77GZVheKjaGnoTAofw== @@ -4718,7 +4718,7 @@ debug@4.1.1, debug@^4.1.0: dependencies: ms "^2.1.1" -debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -4936,7 +4936,7 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== -detect-libc@^1.0.2, detect-libc@^1.0.3: +detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= @@ -7062,7 +7062,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -9094,15 +9094,6 @@ napi-build-utils@^1.0.1: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== -needle@^2.2.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a" - integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -9223,22 +9214,6 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - node-releases@^1.1.53: version "1.1.53" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4" @@ -9387,7 +9362,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.6, npm-packlist@^1.4.4: +npm-packlist@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== @@ -9419,7 +9394,7 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.1, npmlog@^4.0.2, npmlog@^4.1.2: +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.1, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -11781,7 +11756,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -12055,7 +12030,7 @@ sass-loader@^8.0.2: schema-utils "^2.6.1" semver "^6.3.0" -sax@^1.2.4, sax@~1.2.1, sax@~1.2.4: +sax@~1.2.1, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -13052,7 +13027,7 @@ tar@^2.0.0: fstream "^1.0.12" inherits "2" -tar@^4.4.10, tar@^4.4.12, tar@^4.4.2, tar@^4.4.8: +tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==