From 8aa25fd7d6c8f830ef2fdb8bec3399735db21441 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Wed, 23 Mar 2022 00:27:22 -0700 Subject: [PATCH] fix: Add ability to convert between checklist and other types of list --- .../editor/commands/backspaceToParagraph.ts | 5 +- shared/editor/commands/clearNodes.ts | 49 +++++++++++++++++++ shared/editor/commands/moveLeft.ts | 10 ++-- shared/editor/commands/moveRight.ts | 5 +- shared/editor/commands/splitHeading.ts | 5 +- shared/editor/commands/toggleBlockType.ts | 5 +- shared/editor/commands/toggleList.ts | 20 ++++++-- shared/editor/commands/toggleWrap.ts | 5 +- shared/editor/lib/Extension.ts | 14 ++---- shared/editor/lib/chainTransactions.ts | 18 +++++++ shared/editor/marks/Link.tsx | 5 +- shared/editor/nodes/Blockquote.ts | 8 ++- shared/editor/nodes/CodeFence.ts | 9 ++-- shared/editor/nodes/Embed.tsx | 6 +-- shared/editor/nodes/Emoji.tsx | 5 +- shared/editor/nodes/HardBreak.ts | 10 ++-- shared/editor/nodes/HorizontalRule.ts | 7 +-- shared/editor/nodes/Image.tsx | 25 +++------- shared/editor/nodes/ListItem.ts | 16 ++---- shared/editor/nodes/Table.ts | 16 +++--- shared/editor/types/index.ts | 4 +- 21 files changed, 147 insertions(+), 100 deletions(-) create mode 100644 shared/editor/commands/clearNodes.ts create mode 100644 shared/editor/lib/chainTransactions.ts diff --git a/shared/editor/commands/backspaceToParagraph.ts b/shared/editor/commands/backspaceToParagraph.ts index b6c35fe28..be9bf9e41 100644 --- a/shared/editor/commands/backspaceToParagraph.ts +++ b/shared/editor/commands/backspaceToParagraph.ts @@ -1,8 +1,9 @@ import { NodeType } from "prosemirror-model"; -import { EditorState, Transaction } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; +import { Dispatch } from "../types"; export default function backspaceToParagraph(type: NodeType) { - return (state: EditorState, dispatch: (tr: Transaction) => void) => { + return (state: EditorState, dispatch: Dispatch) => { const { $from, from, to, empty } = state.selection; // if the selection has anything in it then use standard delete behavior diff --git a/shared/editor/commands/clearNodes.ts b/shared/editor/commands/clearNodes.ts new file mode 100644 index 000000000..55a30b92c --- /dev/null +++ b/shared/editor/commands/clearNodes.ts @@ -0,0 +1,49 @@ +import { EditorState } from "prosemirror-state"; +import { liftTarget } from "prosemirror-transform"; +import { Dispatch } from "../types"; + +const clearNodes = () => (state: EditorState, dispatch?: Dispatch) => { + const { tr } = state; + const { selection } = tr; + const { ranges } = selection; + + if (!dispatch) { + return true; + } + + ranges.forEach(({ $from, $to }) => { + state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => { + if (node.type.isText) { + return; + } + + const { doc, mapping } = tr; + const $mappedFrom = doc.resolve(mapping.map(pos)); + const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize)); + const nodeRange = $mappedFrom.blockRange($mappedTo); + + if (!nodeRange) { + return; + } + + const targetLiftDepth = liftTarget(nodeRange); + + if (node.type.isTextblock) { + const { defaultType } = $mappedFrom.parent.contentMatchAt( + $mappedFrom.index() + ); + + tr.setNodeMarkup(nodeRange.start, defaultType); + } + + if (targetLiftDepth || targetLiftDepth === 0) { + tr.lift(nodeRange, targetLiftDepth); + } + }); + }); + + dispatch(tr); + return true; +}; + +export default clearNodes; diff --git a/shared/editor/commands/moveLeft.ts b/shared/editor/commands/moveLeft.ts index b9c2440c6..82c3c4b65 100644 --- a/shared/editor/commands/moveLeft.ts +++ b/shared/editor/commands/moveLeft.ts @@ -17,13 +17,9 @@ limitations under the License. // This file is based on the implementation found here: // https://bitbucket.org/atlassian/design-system-mirror/src/master/editor/editor-core/src/plugins/text-formatting/commands/text-formatting.ts -import { - Selection, - EditorState, - Transaction, - TextSelection, -} from "prosemirror-state"; +import { Selection, EditorState, TextSelection } from "prosemirror-state"; import isMarkActive from "../queries/isMarkActive"; +import { Dispatch } from "../types"; function hasCode(state: EditorState, pos: number) { const { code_inline } = state.schema.marks; @@ -35,7 +31,7 @@ function hasCode(state: EditorState, pos: number) { } export default function moveLeft() { - return (state: EditorState, dispatch: (tr: Transaction) => void): boolean => { + return (state: EditorState, dispatch: Dispatch): boolean => { const { code_inline } = state.schema.marks; const { empty, $cursor } = state.selection as TextSelection; if (!empty || !$cursor) { diff --git a/shared/editor/commands/moveRight.ts b/shared/editor/commands/moveRight.ts index 519f95527..5c2c0ccd9 100644 --- a/shared/editor/commands/moveRight.ts +++ b/shared/editor/commands/moveRight.ts @@ -17,11 +17,12 @@ limitations under the License. // This file is based on the implementation found here: // https://bitbucket.org/atlassian/design-system-mirror/src/master/editor/editor-core/src/plugins/text-formatting/commands/text-formatting.ts -import { EditorState, Transaction, TextSelection } from "prosemirror-state"; +import { EditorState, TextSelection } from "prosemirror-state"; import isMarkActive from "../queries/isMarkActive"; +import { Dispatch } from "../types"; export default function moveRight() { - return (state: EditorState, dispatch: (tr: Transaction) => void): boolean => { + return (state: EditorState, dispatch: Dispatch): boolean => { const { code_inline } = state.schema.marks; const { empty, $cursor } = state.selection as TextSelection; if (!empty || !$cursor) { diff --git a/shared/editor/commands/splitHeading.ts b/shared/editor/commands/splitHeading.ts index 59f52bff6..0a156a59d 100644 --- a/shared/editor/commands/splitHeading.ts +++ b/shared/editor/commands/splitHeading.ts @@ -1,10 +1,11 @@ import { NodeType } from "prosemirror-model"; -import { EditorState, TextSelection, Transaction } from "prosemirror-state"; +import { EditorState, TextSelection } from "prosemirror-state"; import { findBlockNodes } from "prosemirror-utils"; import findCollapsedNodes from "../queries/findCollapsedNodes"; +import { Dispatch } from "../types"; export default function splitHeading(type: NodeType) { - return (state: EditorState, dispatch: (tr: Transaction) => void): boolean => { + return (state: EditorState, dispatch: Dispatch): boolean => { const { $from, from, $to, to } = state.selection; // check we're in a matching heading node diff --git a/shared/editor/commands/toggleBlockType.ts b/shared/editor/commands/toggleBlockType.ts index 162ef1562..e7ed7c32a 100644 --- a/shared/editor/commands/toggleBlockType.ts +++ b/shared/editor/commands/toggleBlockType.ts @@ -1,14 +1,15 @@ import { setBlockType } from "prosemirror-commands"; import { NodeType } from "prosemirror-model"; -import { EditorState, Transaction } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; import isNodeActive from "../queries/isNodeActive"; +import { Dispatch } from "../types"; export default function toggleBlockType( type: NodeType, toggleType: NodeType, attrs = {} ) { - return (state: EditorState, dispatch: (tr: Transaction) => void) => { + return (state: EditorState, dispatch?: Dispatch) => { const isActive = isNodeActive(type, attrs)(state); if (isActive) { diff --git a/shared/editor/commands/toggleList.ts b/shared/editor/commands/toggleList.ts index d7e3d3502..f0019a408 100644 --- a/shared/editor/commands/toggleList.ts +++ b/shared/editor/commands/toggleList.ts @@ -1,14 +1,18 @@ import { NodeType } from "prosemirror-model"; import { wrapInList, liftListItem } from "prosemirror-schema-list"; -import { EditorState, Transaction } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; import { findParentNode } from "prosemirror-utils"; +import chainTransactions from "../lib/chainTransactions"; import isList from "../queries/isList"; +import { Dispatch } from "../types"; +import clearNodes from "./clearNodes"; export default function toggleList(listType: NodeType, itemType: NodeType) { - return (state: EditorState, dispatch: (tr: Transaction) => void) => { + return (state: EditorState, dispatch?: Dispatch) => { const { schema, selection } = state; const { $from, $to } = selection; const range = $from.blockRange($to); + const { tr } = state; if (!range) { return false; @@ -27,7 +31,6 @@ export default function toggleList(listType: NodeType, itemType: NodeType) { isList(parentList.node, schema) && listType.validContent(parentList.node.content) ) { - const { tr } = state; tr.setNodeMarkup(parentList.pos, listType); if (dispatch) { @@ -38,6 +41,15 @@ export default function toggleList(listType: NodeType, itemType: NodeType) { } } - return wrapInList(listType)(state, dispatch); + const canWrapInList = wrapInList(listType)(state); + + if (canWrapInList) { + return wrapInList(listType)(state, dispatch); + } + + return chainTransactions(clearNodes(), wrapInList(listType))( + state, + dispatch + ); }; } diff --git a/shared/editor/commands/toggleWrap.ts b/shared/editor/commands/toggleWrap.ts index bfd6b5168..ede336adc 100644 --- a/shared/editor/commands/toggleWrap.ts +++ b/shared/editor/commands/toggleWrap.ts @@ -1,13 +1,14 @@ import { wrapIn, lift } from "prosemirror-commands"; import { NodeType } from "prosemirror-model"; -import { EditorState, Transaction } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; import isNodeActive from "../queries/isNodeActive"; +import { Dispatch } from "../types"; export default function toggleWrap( type: NodeType, attrs?: Record ) { - return (state: EditorState, dispatch: (tr: Transaction) => void) => { + return (state: EditorState, dispatch?: Dispatch) => { const isActive = isNodeActive(type)(state); if (isActive) { diff --git a/shared/editor/lib/Extension.ts b/shared/editor/lib/Extension.ts index 34f92985a..ade3fb44f 100644 --- a/shared/editor/lib/Extension.ts +++ b/shared/editor/lib/Extension.ts @@ -1,22 +1,16 @@ import { PluginSimple } from "markdown-it"; import { InputRule } from "prosemirror-inputrules"; import { NodeType, MarkType, Schema } from "prosemirror-model"; -import { EditorState, Plugin, Transaction } from "prosemirror-state"; +import { EditorState, Plugin } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Editor } from "../../../app/editor"; +import { Dispatch } from "../types"; -export type Command = ( - state: EditorState, - dispatch: (tr: Transaction) => void -) => boolean; +export type Command = (state: EditorState, dispatch: Dispatch) => boolean; export type CommandFactory = ( attrs?: Record -) => ( - state: EditorState, - dispatch: (tr: Transaction) => void, - view: EditorView -) => boolean; +) => (state: EditorState, dispatch: Dispatch, view: EditorView) => boolean; export default class Extension { options: any; diff --git a/shared/editor/lib/chainTransactions.ts b/shared/editor/lib/chainTransactions.ts new file mode 100644 index 000000000..c328174ad --- /dev/null +++ b/shared/editor/lib/chainTransactions.ts @@ -0,0 +1,18 @@ +import { EditorState, Transaction } from "prosemirror-state"; +import { Dispatch } from "../types"; + +export default function chainTransactions( + ...commands: ((state: EditorState, dispatch?: Dispatch) => boolean)[] +) { + return (state: EditorState, dispatch?: Dispatch): boolean => { + const dispatcher = (tr: Transaction): void => { + state = state.apply(tr); + dispatch?.(tr); + }; + const last = commands.pop(); + const reduced = commands.reduce((result, command) => { + return result || command(state, dispatcher); + }, false); + return reduced && last !== undefined && last(state, dispatch); + }; +} diff --git a/shared/editor/marks/Link.tsx b/shared/editor/marks/Link.tsx index efea8bcdb..f08cc0cc3 100644 --- a/shared/editor/marks/Link.tsx +++ b/shared/editor/marks/Link.tsx @@ -9,12 +9,13 @@ import { Node, Mark as ProsemirrorMark, } from "prosemirror-model"; -import { Transaction, EditorState, Plugin } from "prosemirror-state"; +import { EditorState, Plugin } from "prosemirror-state"; import { Decoration, DecorationSet } from "prosemirror-view"; import * as React from "react"; import ReactDOM from "react-dom"; import { isInternalUrl } from "../../utils/urls"; import findLinkNodes from "../queries/findLinkNodes"; +import { Dispatch } from "../types"; import Mark from "./Mark"; const LINK_INPUT_REGEX = /\[([^[]+)]\((\S+)\)$/; @@ -103,7 +104,7 @@ export default class Link extends Mark { keys({ type }: { type: MarkType }) { return { - "Mod-k": (state: EditorState, dispatch: (tr: Transaction) => void) => { + "Mod-k": (state: EditorState, dispatch: Dispatch) => { if (state.selection.empty) { this.options.onKeyboardShortcut(); return true; diff --git a/shared/editor/nodes/Blockquote.ts b/shared/editor/nodes/Blockquote.ts index 3787ee656..0dde3cedd 100644 --- a/shared/editor/nodes/Blockquote.ts +++ b/shared/editor/nodes/Blockquote.ts @@ -1,9 +1,10 @@ import { wrappingInputRule } from "prosemirror-inputrules"; import { NodeSpec, Node as ProsemirrorNode, NodeType } from "prosemirror-model"; -import { EditorState, Transaction } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; import toggleWrap from "../commands/toggleWrap"; import { MarkdownSerializerState } from "../lib/markdown/serializer"; import isNodeActive from "../queries/isNodeActive"; +import { Dispatch } from "../types"; import Node from "./Node"; export default class Blockquote extends Node { @@ -37,10 +38,7 @@ export default class Blockquote extends Node { return { "Ctrl->": toggleWrap(type), "Mod-]": toggleWrap(type), - "Shift-Enter": ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + "Shift-Enter": (state: EditorState, dispatch: Dispatch) => { if (!isNodeActive(type)(state)) { return false; } diff --git a/shared/editor/nodes/CodeFence.ts b/shared/editor/nodes/CodeFence.ts index 392e4103f..bc89cc287 100644 --- a/shared/editor/nodes/CodeFence.ts +++ b/shared/editor/nodes/CodeFence.ts @@ -38,7 +38,7 @@ import toggleBlockType from "../commands/toggleBlockType"; import { MarkdownSerializerState } from "../lib/markdown/serializer"; import Prism, { LANGUAGES } from "../plugins/Prism"; import isInCode from "../queries/isInCode"; -import { ToastType } from "../types"; +import { Dispatch, ToastType } from "../types"; import Node from "./Node"; const PERSISTENCE_KEY = "rme-code-language"; @@ -146,10 +146,7 @@ export default class CodeFence extends Node { keys({ type, schema }: { type: NodeType; schema: Schema }) { return { "Shift-Ctrl-\\": toggleBlockType(type, schema.nodes.paragraph), - "Shift-Enter": ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + "Shift-Enter": (state: EditorState, dispatch: Dispatch) => { if (!isInCode(state)) { return false; } @@ -172,7 +169,7 @@ export default class CodeFence extends Node { dispatch(tr.insertText(newText, selection.from, selection.to)); return true; }, - Tab: (state: EditorState, dispatch: (tr: Transaction) => void) => { + Tab: (state: EditorState, dispatch: Dispatch) => { if (!isInCode(state)) { return false; } diff --git a/shared/editor/nodes/Embed.tsx b/shared/editor/nodes/Embed.tsx index bdda25f13..8109786f6 100644 --- a/shared/editor/nodes/Embed.tsx +++ b/shared/editor/nodes/Embed.tsx @@ -1,11 +1,11 @@ import Token from "markdown-it/lib/token"; import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model"; -import { EditorState, Transaction } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; import * as React from "react"; import DisabledEmbed from "../components/DisabledEmbed"; import { MarkdownSerializerState } from "../lib/markdown/serializer"; import embedsRule from "../rules/embeds"; -import { ComponentProps } from "../types"; +import { ComponentProps, Dispatch } from "../types"; import Node from "./Node"; const cache = {}; @@ -117,7 +117,7 @@ export default class Embed extends Node { commands({ type }: { type: NodeType }) { return (attrs: Record) => ( state: EditorState, - dispatch: (tr: Transaction) => void + dispatch: Dispatch ) => { dispatch( state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView() diff --git a/shared/editor/nodes/Emoji.tsx b/shared/editor/nodes/Emoji.tsx index cce3bca07..5c13da6db 100644 --- a/shared/editor/nodes/Emoji.tsx +++ b/shared/editor/nodes/Emoji.tsx @@ -2,9 +2,10 @@ import nameToEmoji from "gemoji/name-to-emoji.json"; import Token from "markdown-it/lib/token"; import { InputRule } from "prosemirror-inputrules"; import { NodeSpec, Node as ProsemirrorNode, NodeType } from "prosemirror-model"; -import { EditorState, TextSelection, Transaction } from "prosemirror-state"; +import { EditorState, TextSelection } from "prosemirror-state"; import { MarkdownSerializerState } from "../lib/markdown/serializer"; import emojiRule from "../rules/emoji"; +import { Dispatch } from "../types"; import Node from "./Node"; export default class Emoji extends Node { @@ -63,7 +64,7 @@ export default class Emoji extends Node { commands({ type }: { type: NodeType }) { return (attrs: Record) => ( state: EditorState, - dispatch: (tr: Transaction) => void + dispatch: Dispatch ) => { const { selection } = state; const position = diff --git a/shared/editor/nodes/HardBreak.ts b/shared/editor/nodes/HardBreak.ts index 4a25f3b76..3ad7297e0 100644 --- a/shared/editor/nodes/HardBreak.ts +++ b/shared/editor/nodes/HardBreak.ts @@ -1,8 +1,9 @@ import { NodeSpec, NodeType } from "prosemirror-model"; -import { EditorState, Transaction } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; import { isInTable } from "prosemirror-tables"; import { MarkdownSerializerState } from "../lib/markdown/serializer"; import breakRule from "../rules/breaks"; +import { Dispatch } from "../types"; import Node from "./Node"; export default class HardBreak extends Node { @@ -27,7 +28,7 @@ export default class HardBreak extends Node { } commands({ type }: { type: NodeType }) { - return () => (state: EditorState, dispatch: (tr: Transaction) => void) => { + return () => (state: EditorState, dispatch: Dispatch) => { dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView()); return true; }; @@ -35,10 +36,7 @@ export default class HardBreak extends Node { keys({ type }: { type: NodeType }) { return { - "Shift-Enter": ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + "Shift-Enter": (state: EditorState, dispatch: Dispatch) => { if (!isInTable(state)) { return false; } diff --git a/shared/editor/nodes/HorizontalRule.ts b/shared/editor/nodes/HorizontalRule.ts index 02abcb233..9bcba3dcd 100644 --- a/shared/editor/nodes/HorizontalRule.ts +++ b/shared/editor/nodes/HorizontalRule.ts @@ -1,8 +1,9 @@ import Token from "markdown-it/lib/token"; import { InputRule } from "prosemirror-inputrules"; import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model"; -import { EditorState, Transaction } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; import { MarkdownSerializerState } from "../lib/markdown/serializer"; +import { Dispatch } from "../types"; import Node from "./Node"; export default class HorizontalRule extends Node { @@ -31,7 +32,7 @@ export default class HorizontalRule extends Node { commands({ type }: { type: NodeType }) { return (attrs: Record) => ( state: EditorState, - dispatch: (tr: Transaction) => void + dispatch: Dispatch ) => { dispatch( state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView() @@ -42,7 +43,7 @@ export default class HorizontalRule extends Node { keys({ type }: { type: NodeType }) { return { - "Mod-_": (state: EditorState, dispatch: (tr: Transaction) => void) => { + "Mod-_": (state: EditorState, dispatch: Dispatch) => { dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView()); return true; }, diff --git a/shared/editor/nodes/Image.tsx b/shared/editor/nodes/Image.tsx index 17c1ec9ae..5102cde12 100644 --- a/shared/editor/nodes/Image.tsx +++ b/shared/editor/nodes/Image.tsx @@ -7,7 +7,6 @@ import { TextSelection, NodeSelection, EditorState, - Transaction, } from "prosemirror-state"; import * as React from "react"; import ImageZoom from "react-medium-image-zoom"; @@ -16,7 +15,7 @@ import getDataTransferFiles from "../../utils/getDataTransferFiles"; import insertFiles, { Options } from "../commands/insertFiles"; import { MarkdownSerializerState } from "../lib/markdown/serializer"; import uploadPlaceholderPlugin from "../lib/uploadPlaceholder"; -import { ComponentProps } from "../types"; +import { ComponentProps, Dispatch } from "../types"; import Node from "./Node"; /** @@ -366,17 +365,11 @@ export default class Image extends Node { return true; }, - deleteImage: () => ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + deleteImage: () => (state: EditorState, dispatch: Dispatch) => { dispatch(state.tr.deleteSelection()); return true; }, - alignRight: () => ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + alignRight: () => (state: EditorState, dispatch: Dispatch) => { if (!(state.selection instanceof NodeSelection)) { return false; } @@ -389,10 +382,7 @@ export default class Image extends Node { dispatch(state.tr.setNodeMarkup(selection.from, undefined, attrs)); return true; }, - alignLeft: () => ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + alignLeft: () => (state: EditorState, dispatch: Dispatch) => { if (!(state.selection instanceof NodeSelection)) { return false; } @@ -436,10 +426,7 @@ export default class Image extends Node { inputElement.click(); return true; }, - alignCenter: () => ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + alignCenter: () => (state: EditorState, dispatch: Dispatch) => { if (!(state.selection instanceof NodeSelection)) { return false; } @@ -450,7 +437,7 @@ export default class Image extends Node { }, createImage: (attrs: Record) => ( state: EditorState, - dispatch: (tr: Transaction) => void + dispatch: Dispatch ) => { const { selection } = state; const position = diff --git a/shared/editor/nodes/ListItem.ts b/shared/editor/nodes/ListItem.ts index 9070657a9..02aece86c 100644 --- a/shared/editor/nodes/ListItem.ts +++ b/shared/editor/nodes/ListItem.ts @@ -16,6 +16,7 @@ import { MarkdownSerializerState } from "../lib/markdown/serializer"; import getParentListItem from "../queries/getParentListItem"; import isInList from "../queries/isInList"; import isList from "../queries/isList"; +import { Dispatch } from "../types"; import Node from "./Node"; export default class ListItem extends Node { @@ -199,10 +200,7 @@ export default class ListItem extends Node { "Shift-Tab": liftListItem(type), "Mod-]": sinkListItem(type), "Mod-[": liftListItem(type), - "Shift-Enter": ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + "Shift-Enter": (state: EditorState, dispatch: Dispatch) => { if (!isInList(state)) { return false; } @@ -214,10 +212,7 @@ export default class ListItem extends Node { dispatch(tr.split(selection.to)); return true; }, - "Alt-ArrowUp": ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + "Alt-ArrowUp": (state: EditorState, dispatch: Dispatch) => { if (!state.selection.empty) { return false; } @@ -248,10 +243,7 @@ export default class ListItem extends Node { ); return true; }, - "Alt-ArrowDown": ( - state: EditorState, - dispatch: (tr: Transaction) => void - ) => { + "Alt-ArrowDown": (state: EditorState, dispatch: Dispatch) => { if (!state.selection.empty) { return false; } diff --git a/shared/editor/nodes/Table.ts b/shared/editor/nodes/Table.ts index d2be72656..973f81816 100644 --- a/shared/editor/nodes/Table.ts +++ b/shared/editor/nodes/Table.ts @@ -1,10 +1,5 @@ import { NodeSpec, Node as ProsemirrorNode, Schema } from "prosemirror-model"; -import { - EditorState, - Plugin, - TextSelection, - Transaction, -} from "prosemirror-state"; +import { EditorState, Plugin, TextSelection } from "prosemirror-state"; import { addColumnAfter, addColumnBefore, @@ -27,6 +22,7 @@ import { import { Decoration, DecorationSet } from "prosemirror-view"; import { MarkdownSerializerState } from "../lib/markdown/serializer"; import tablesRule from "../rules/tables"; +import { Dispatch } from "../types"; import Node from "./Node"; export default class Table extends Node { @@ -67,7 +63,7 @@ export default class Table extends Node { }: { rowsCount: number; colsCount: number; - }) => (state: EditorState, dispatch: (tr: Transaction) => void) => { + }) => (state: EditorState, dispatch: Dispatch) => { const offset = state.tr.selection.anchor + 1; const nodes = createTable(schema, rowsCount, colsCount); const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView(); @@ -83,7 +79,7 @@ export default class Table extends Node { }: { index: number; alignment: string; - }) => (state: EditorState, dispatch: (tr: Transaction) => void) => { + }) => (state: EditorState, dispatch: Dispatch) => { const cells = getCellsInColumn(index)(state.selection) || []; let transaction = state.tr; cells.forEach(({ pos }) => { @@ -99,7 +95,7 @@ export default class Table extends Node { deleteColumn: () => deleteColumn, addRowAfter: ({ index }: { index: number }) => ( state: EditorState, - dispatch: (tr: Transaction) => void + dispatch: Dispatch ) => { if (index === 0) { // A little hack to avoid cloning the heading row by cloning the row @@ -123,7 +119,7 @@ export default class Table extends Node { return { Tab: goToNextCell(1), "Shift-Tab": goToNextCell(-1), - Enter: (state: EditorState, dispatch: (tr: Transaction) => void) => { + Enter: (state: EditorState, dispatch: Dispatch) => { if (!isInTable(state)) { return false; } diff --git a/shared/editor/types/index.ts b/shared/editor/types/index.ts index 62867698e..9a1ff1427 100644 --- a/shared/editor/types/index.ts +++ b/shared/editor/types/index.ts @@ -1,5 +1,5 @@ import { Node as ProsemirrorNode } from "prosemirror-model"; -import { EditorState } from "prosemirror-state"; +import { EditorState, Transaction } from "prosemirror-state"; import * as React from "react"; import { DefaultTheme } from "styled-components"; @@ -34,3 +34,5 @@ export type ComponentProps = { isEditable: boolean; getPos: () => number; }; + +export type Dispatch = (tr: Transaction) => void;