fix: Add ability to convert between checklist and other types of list
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import { NodeType } from "prosemirror-model";
|
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) {
|
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;
|
const { $from, from, to, empty } = state.selection;
|
||||||
|
|
||||||
// if the selection has anything in it then use standard delete behavior
|
// if the selection has anything in it then use standard delete behavior
|
||||||
|
|||||||
49
shared/editor/commands/clearNodes.ts
Normal file
49
shared/editor/commands/clearNodes.ts
Normal file
@@ -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;
|
||||||
@@ -17,13 +17,9 @@ limitations under the License.
|
|||||||
// This file is based on the implementation found here:
|
// 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
|
// https://bitbucket.org/atlassian/design-system-mirror/src/master/editor/editor-core/src/plugins/text-formatting/commands/text-formatting.ts
|
||||||
|
|
||||||
import {
|
import { Selection, EditorState, TextSelection } from "prosemirror-state";
|
||||||
Selection,
|
|
||||||
EditorState,
|
|
||||||
Transaction,
|
|
||||||
TextSelection,
|
|
||||||
} from "prosemirror-state";
|
|
||||||
import isMarkActive from "../queries/isMarkActive";
|
import isMarkActive from "../queries/isMarkActive";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
|
|
||||||
function hasCode(state: EditorState, pos: number) {
|
function hasCode(state: EditorState, pos: number) {
|
||||||
const { code_inline } = state.schema.marks;
|
const { code_inline } = state.schema.marks;
|
||||||
@@ -35,7 +31,7 @@ function hasCode(state: EditorState, pos: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function moveLeft() {
|
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 { code_inline } = state.schema.marks;
|
||||||
const { empty, $cursor } = state.selection as TextSelection;
|
const { empty, $cursor } = state.selection as TextSelection;
|
||||||
if (!empty || !$cursor) {
|
if (!empty || !$cursor) {
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ limitations under the License.
|
|||||||
// This file is based on the implementation found here:
|
// 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
|
// 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 isMarkActive from "../queries/isMarkActive";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
|
|
||||||
export default function moveRight() {
|
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 { code_inline } = state.schema.marks;
|
||||||
const { empty, $cursor } = state.selection as TextSelection;
|
const { empty, $cursor } = state.selection as TextSelection;
|
||||||
if (!empty || !$cursor) {
|
if (!empty || !$cursor) {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { NodeType } from "prosemirror-model";
|
import { NodeType } from "prosemirror-model";
|
||||||
import { EditorState, TextSelection, Transaction } from "prosemirror-state";
|
import { EditorState, TextSelection } from "prosemirror-state";
|
||||||
import { findBlockNodes } from "prosemirror-utils";
|
import { findBlockNodes } from "prosemirror-utils";
|
||||||
import findCollapsedNodes from "../queries/findCollapsedNodes";
|
import findCollapsedNodes from "../queries/findCollapsedNodes";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
|
|
||||||
export default function splitHeading(type: NodeType) {
|
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;
|
const { $from, from, $to, to } = state.selection;
|
||||||
|
|
||||||
// check we're in a matching heading node
|
// check we're in a matching heading node
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { setBlockType } from "prosemirror-commands";
|
import { setBlockType } from "prosemirror-commands";
|
||||||
import { NodeType } from "prosemirror-model";
|
import { NodeType } from "prosemirror-model";
|
||||||
import { EditorState, Transaction } from "prosemirror-state";
|
import { EditorState } from "prosemirror-state";
|
||||||
import isNodeActive from "../queries/isNodeActive";
|
import isNodeActive from "../queries/isNodeActive";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
|
|
||||||
export default function toggleBlockType(
|
export default function toggleBlockType(
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
toggleType: NodeType,
|
toggleType: NodeType,
|
||||||
attrs = {}
|
attrs = {}
|
||||||
) {
|
) {
|
||||||
return (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
return (state: EditorState, dispatch?: Dispatch) => {
|
||||||
const isActive = isNodeActive(type, attrs)(state);
|
const isActive = isNodeActive(type, attrs)(state);
|
||||||
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { NodeType } from "prosemirror-model";
|
import { NodeType } from "prosemirror-model";
|
||||||
import { wrapInList, liftListItem } from "prosemirror-schema-list";
|
import { wrapInList, liftListItem } from "prosemirror-schema-list";
|
||||||
import { EditorState, Transaction } from "prosemirror-state";
|
import { EditorState } from "prosemirror-state";
|
||||||
import { findParentNode } from "prosemirror-utils";
|
import { findParentNode } from "prosemirror-utils";
|
||||||
|
import chainTransactions from "../lib/chainTransactions";
|
||||||
import isList from "../queries/isList";
|
import isList from "../queries/isList";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
|
import clearNodes from "./clearNodes";
|
||||||
|
|
||||||
export default function toggleList(listType: NodeType, itemType: NodeType) {
|
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 { schema, selection } = state;
|
||||||
const { $from, $to } = selection;
|
const { $from, $to } = selection;
|
||||||
const range = $from.blockRange($to);
|
const range = $from.blockRange($to);
|
||||||
|
const { tr } = state;
|
||||||
|
|
||||||
if (!range) {
|
if (!range) {
|
||||||
return false;
|
return false;
|
||||||
@@ -27,7 +31,6 @@ export default function toggleList(listType: NodeType, itemType: NodeType) {
|
|||||||
isList(parentList.node, schema) &&
|
isList(parentList.node, schema) &&
|
||||||
listType.validContent(parentList.node.content)
|
listType.validContent(parentList.node.content)
|
||||||
) {
|
) {
|
||||||
const { tr } = state;
|
|
||||||
tr.setNodeMarkup(parentList.pos, listType);
|
tr.setNodeMarkup(parentList.pos, listType);
|
||||||
|
|
||||||
if (dispatch) {
|
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
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { wrapIn, lift } from "prosemirror-commands";
|
import { wrapIn, lift } from "prosemirror-commands";
|
||||||
import { NodeType } from "prosemirror-model";
|
import { NodeType } from "prosemirror-model";
|
||||||
import { EditorState, Transaction } from "prosemirror-state";
|
import { EditorState } from "prosemirror-state";
|
||||||
import isNodeActive from "../queries/isNodeActive";
|
import isNodeActive from "../queries/isNodeActive";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
|
|
||||||
export default function toggleWrap(
|
export default function toggleWrap(
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
attrs?: Record<string, any>
|
attrs?: Record<string, any>
|
||||||
) {
|
) {
|
||||||
return (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
return (state: EditorState, dispatch?: Dispatch) => {
|
||||||
const isActive = isNodeActive(type)(state);
|
const isActive = isNodeActive(type)(state);
|
||||||
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
|
|||||||
@@ -1,22 +1,16 @@
|
|||||||
import { PluginSimple } from "markdown-it";
|
import { PluginSimple } from "markdown-it";
|
||||||
import { InputRule } from "prosemirror-inputrules";
|
import { InputRule } from "prosemirror-inputrules";
|
||||||
import { NodeType, MarkType, Schema } from "prosemirror-model";
|
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 { EditorView } from "prosemirror-view";
|
||||||
import { Editor } from "../../../app/editor";
|
import { Editor } from "../../../app/editor";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
|
|
||||||
export type Command = (
|
export type Command = (state: EditorState, dispatch: Dispatch) => boolean;
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => boolean;
|
|
||||||
|
|
||||||
export type CommandFactory = (
|
export type CommandFactory = (
|
||||||
attrs?: Record<string, any>
|
attrs?: Record<string, any>
|
||||||
) => (
|
) => (state: EditorState, dispatch: Dispatch, view: EditorView) => boolean;
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void,
|
|
||||||
view: EditorView
|
|
||||||
) => boolean;
|
|
||||||
|
|
||||||
export default class Extension {
|
export default class Extension {
|
||||||
options: any;
|
options: any;
|
||||||
|
|||||||
18
shared/editor/lib/chainTransactions.ts
Normal file
18
shared/editor/lib/chainTransactions.ts
Normal file
@@ -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);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -9,12 +9,13 @@ import {
|
|||||||
Node,
|
Node,
|
||||||
Mark as ProsemirrorMark,
|
Mark as ProsemirrorMark,
|
||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import { Transaction, EditorState, Plugin } from "prosemirror-state";
|
import { EditorState, Plugin } from "prosemirror-state";
|
||||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { isInternalUrl } from "../../utils/urls";
|
import { isInternalUrl } from "../../utils/urls";
|
||||||
import findLinkNodes from "../queries/findLinkNodes";
|
import findLinkNodes from "../queries/findLinkNodes";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
import Mark from "./Mark";
|
import Mark from "./Mark";
|
||||||
|
|
||||||
const LINK_INPUT_REGEX = /\[([^[]+)]\((\S+)\)$/;
|
const LINK_INPUT_REGEX = /\[([^[]+)]\((\S+)\)$/;
|
||||||
@@ -103,7 +104,7 @@ export default class Link extends Mark {
|
|||||||
|
|
||||||
keys({ type }: { type: MarkType }) {
|
keys({ type }: { type: MarkType }) {
|
||||||
return {
|
return {
|
||||||
"Mod-k": (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
"Mod-k": (state: EditorState, dispatch: Dispatch) => {
|
||||||
if (state.selection.empty) {
|
if (state.selection.empty) {
|
||||||
this.options.onKeyboardShortcut();
|
this.options.onKeyboardShortcut();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { wrappingInputRule } from "prosemirror-inputrules";
|
import { wrappingInputRule } from "prosemirror-inputrules";
|
||||||
import { NodeSpec, Node as ProsemirrorNode, NodeType } from "prosemirror-model";
|
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 toggleWrap from "../commands/toggleWrap";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import isNodeActive from "../queries/isNodeActive";
|
import isNodeActive from "../queries/isNodeActive";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class Blockquote extends Node {
|
export default class Blockquote extends Node {
|
||||||
@@ -37,10 +38,7 @@ export default class Blockquote extends Node {
|
|||||||
return {
|
return {
|
||||||
"Ctrl->": toggleWrap(type),
|
"Ctrl->": toggleWrap(type),
|
||||||
"Mod-]": toggleWrap(type),
|
"Mod-]": toggleWrap(type),
|
||||||
"Shift-Enter": (
|
"Shift-Enter": (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
if (!isNodeActive(type)(state)) {
|
if (!isNodeActive(type)(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import toggleBlockType from "../commands/toggleBlockType";
|
|||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import Prism, { LANGUAGES } from "../plugins/Prism";
|
import Prism, { LANGUAGES } from "../plugins/Prism";
|
||||||
import isInCode from "../queries/isInCode";
|
import isInCode from "../queries/isInCode";
|
||||||
import { ToastType } from "../types";
|
import { Dispatch, ToastType } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
const PERSISTENCE_KEY = "rme-code-language";
|
const PERSISTENCE_KEY = "rme-code-language";
|
||||||
@@ -146,10 +146,7 @@ export default class CodeFence extends Node {
|
|||||||
keys({ type, schema }: { type: NodeType; schema: Schema }) {
|
keys({ type, schema }: { type: NodeType; schema: Schema }) {
|
||||||
return {
|
return {
|
||||||
"Shift-Ctrl-\\": toggleBlockType(type, schema.nodes.paragraph),
|
"Shift-Ctrl-\\": toggleBlockType(type, schema.nodes.paragraph),
|
||||||
"Shift-Enter": (
|
"Shift-Enter": (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
if (!isInCode(state)) {
|
if (!isInCode(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -172,7 +169,7 @@ export default class CodeFence extends Node {
|
|||||||
dispatch(tr.insertText(newText, selection.from, selection.to));
|
dispatch(tr.insertText(newText, selection.from, selection.to));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
Tab: (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
Tab: (state: EditorState, dispatch: Dispatch) => {
|
||||||
if (!isInCode(state)) {
|
if (!isInCode(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import Token from "markdown-it/lib/token";
|
import Token from "markdown-it/lib/token";
|
||||||
import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
|
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 * as React from "react";
|
||||||
import DisabledEmbed from "../components/DisabledEmbed";
|
import DisabledEmbed from "../components/DisabledEmbed";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import embedsRule from "../rules/embeds";
|
import embedsRule from "../rules/embeds";
|
||||||
import { ComponentProps } from "../types";
|
import { ComponentProps, Dispatch } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
const cache = {};
|
const cache = {};
|
||||||
@@ -117,7 +117,7 @@ export default class Embed extends Node {
|
|||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return (attrs: Record<string, any>) => (
|
return (attrs: Record<string, any>) => (
|
||||||
state: EditorState,
|
state: EditorState,
|
||||||
dispatch: (tr: Transaction) => void
|
dispatch: Dispatch
|
||||||
) => {
|
) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView()
|
state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView()
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import nameToEmoji from "gemoji/name-to-emoji.json";
|
|||||||
import Token from "markdown-it/lib/token";
|
import Token from "markdown-it/lib/token";
|
||||||
import { InputRule } from "prosemirror-inputrules";
|
import { InputRule } from "prosemirror-inputrules";
|
||||||
import { NodeSpec, Node as ProsemirrorNode, NodeType } from "prosemirror-model";
|
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 { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import emojiRule from "../rules/emoji";
|
import emojiRule from "../rules/emoji";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class Emoji extends Node {
|
export default class Emoji extends Node {
|
||||||
@@ -63,7 +64,7 @@ export default class Emoji extends Node {
|
|||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return (attrs: Record<string, string>) => (
|
return (attrs: Record<string, string>) => (
|
||||||
state: EditorState,
|
state: EditorState,
|
||||||
dispatch: (tr: Transaction) => void
|
dispatch: Dispatch
|
||||||
) => {
|
) => {
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
const position =
|
const position =
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { NodeSpec, NodeType } from "prosemirror-model";
|
import { NodeSpec, NodeType } from "prosemirror-model";
|
||||||
import { EditorState, Transaction } from "prosemirror-state";
|
import { EditorState } from "prosemirror-state";
|
||||||
import { isInTable } from "prosemirror-tables";
|
import { isInTable } from "prosemirror-tables";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import breakRule from "../rules/breaks";
|
import breakRule from "../rules/breaks";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class HardBreak extends Node {
|
export default class HardBreak extends Node {
|
||||||
@@ -27,7 +28,7 @@ export default class HardBreak extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return () => (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
return () => (state: EditorState, dispatch: Dispatch) => {
|
||||||
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -35,10 +36,7 @@ export default class HardBreak extends Node {
|
|||||||
|
|
||||||
keys({ type }: { type: NodeType }) {
|
keys({ type }: { type: NodeType }) {
|
||||||
return {
|
return {
|
||||||
"Shift-Enter": (
|
"Shift-Enter": (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
if (!isInTable(state)) {
|
if (!isInTable(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import Token from "markdown-it/lib/token";
|
import Token from "markdown-it/lib/token";
|
||||||
import { InputRule } from "prosemirror-inputrules";
|
import { InputRule } from "prosemirror-inputrules";
|
||||||
import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
|
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 { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class HorizontalRule extends Node {
|
export default class HorizontalRule extends Node {
|
||||||
@@ -31,7 +32,7 @@ export default class HorizontalRule extends Node {
|
|||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return (attrs: Record<string, any>) => (
|
return (attrs: Record<string, any>) => (
|
||||||
state: EditorState,
|
state: EditorState,
|
||||||
dispatch: (tr: Transaction) => void
|
dispatch: Dispatch
|
||||||
) => {
|
) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView()
|
state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView()
|
||||||
@@ -42,7 +43,7 @@ export default class HorizontalRule extends Node {
|
|||||||
|
|
||||||
keys({ type }: { type: NodeType }) {
|
keys({ type }: { type: NodeType }) {
|
||||||
return {
|
return {
|
||||||
"Mod-_": (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
"Mod-_": (state: EditorState, dispatch: Dispatch) => {
|
||||||
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
TextSelection,
|
TextSelection,
|
||||||
NodeSelection,
|
NodeSelection,
|
||||||
EditorState,
|
EditorState,
|
||||||
Transaction,
|
|
||||||
} from "prosemirror-state";
|
} from "prosemirror-state";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ImageZoom from "react-medium-image-zoom";
|
import ImageZoom from "react-medium-image-zoom";
|
||||||
@@ -16,7 +15,7 @@ import getDataTransferFiles from "../../utils/getDataTransferFiles";
|
|||||||
import insertFiles, { Options } from "../commands/insertFiles";
|
import insertFiles, { Options } from "../commands/insertFiles";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import uploadPlaceholderPlugin from "../lib/uploadPlaceholder";
|
import uploadPlaceholderPlugin from "../lib/uploadPlaceholder";
|
||||||
import { ComponentProps } from "../types";
|
import { ComponentProps, Dispatch } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -366,17 +365,11 @@ export default class Image extends Node {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
deleteImage: () => (
|
deleteImage: () => (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
dispatch(state.tr.deleteSelection());
|
dispatch(state.tr.deleteSelection());
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
alignRight: () => (
|
alignRight: () => (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
if (!(state.selection instanceof NodeSelection)) {
|
if (!(state.selection instanceof NodeSelection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -389,10 +382,7 @@ export default class Image extends Node {
|
|||||||
dispatch(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
dispatch(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
alignLeft: () => (
|
alignLeft: () => (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
if (!(state.selection instanceof NodeSelection)) {
|
if (!(state.selection instanceof NodeSelection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -436,10 +426,7 @@ export default class Image extends Node {
|
|||||||
inputElement.click();
|
inputElement.click();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
alignCenter: () => (
|
alignCenter: () => (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
if (!(state.selection instanceof NodeSelection)) {
|
if (!(state.selection instanceof NodeSelection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -450,7 +437,7 @@ export default class Image extends Node {
|
|||||||
},
|
},
|
||||||
createImage: (attrs: Record<string, any>) => (
|
createImage: (attrs: Record<string, any>) => (
|
||||||
state: EditorState,
|
state: EditorState,
|
||||||
dispatch: (tr: Transaction) => void
|
dispatch: Dispatch
|
||||||
) => {
|
) => {
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
const position =
|
const position =
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
|||||||
import getParentListItem from "../queries/getParentListItem";
|
import getParentListItem from "../queries/getParentListItem";
|
||||||
import isInList from "../queries/isInList";
|
import isInList from "../queries/isInList";
|
||||||
import isList from "../queries/isList";
|
import isList from "../queries/isList";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class ListItem extends Node {
|
export default class ListItem extends Node {
|
||||||
@@ -199,10 +200,7 @@ export default class ListItem extends Node {
|
|||||||
"Shift-Tab": liftListItem(type),
|
"Shift-Tab": liftListItem(type),
|
||||||
"Mod-]": sinkListItem(type),
|
"Mod-]": sinkListItem(type),
|
||||||
"Mod-[": liftListItem(type),
|
"Mod-[": liftListItem(type),
|
||||||
"Shift-Enter": (
|
"Shift-Enter": (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
if (!isInList(state)) {
|
if (!isInList(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -214,10 +212,7 @@ export default class ListItem extends Node {
|
|||||||
dispatch(tr.split(selection.to));
|
dispatch(tr.split(selection.to));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
"Alt-ArrowUp": (
|
"Alt-ArrowUp": (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
if (!state.selection.empty) {
|
if (!state.selection.empty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -248,10 +243,7 @@ export default class ListItem extends Node {
|
|||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
"Alt-ArrowDown": (
|
"Alt-ArrowDown": (state: EditorState, dispatch: Dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: (tr: Transaction) => void
|
|
||||||
) => {
|
|
||||||
if (!state.selection.empty) {
|
if (!state.selection.empty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import { NodeSpec, Node as ProsemirrorNode, Schema } from "prosemirror-model";
|
import { NodeSpec, Node as ProsemirrorNode, Schema } from "prosemirror-model";
|
||||||
import {
|
import { EditorState, Plugin, TextSelection } from "prosemirror-state";
|
||||||
EditorState,
|
|
||||||
Plugin,
|
|
||||||
TextSelection,
|
|
||||||
Transaction,
|
|
||||||
} from "prosemirror-state";
|
|
||||||
import {
|
import {
|
||||||
addColumnAfter,
|
addColumnAfter,
|
||||||
addColumnBefore,
|
addColumnBefore,
|
||||||
@@ -27,6 +22,7 @@ import {
|
|||||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import tablesRule from "../rules/tables";
|
import tablesRule from "../rules/tables";
|
||||||
|
import { Dispatch } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class Table extends Node {
|
export default class Table extends Node {
|
||||||
@@ -67,7 +63,7 @@ export default class Table extends Node {
|
|||||||
}: {
|
}: {
|
||||||
rowsCount: number;
|
rowsCount: number;
|
||||||
colsCount: number;
|
colsCount: number;
|
||||||
}) => (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
}) => (state: EditorState, dispatch: Dispatch) => {
|
||||||
const offset = state.tr.selection.anchor + 1;
|
const offset = state.tr.selection.anchor + 1;
|
||||||
const nodes = createTable(schema, rowsCount, colsCount);
|
const nodes = createTable(schema, rowsCount, colsCount);
|
||||||
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView();
|
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView();
|
||||||
@@ -83,7 +79,7 @@ export default class Table extends Node {
|
|||||||
}: {
|
}: {
|
||||||
index: number;
|
index: number;
|
||||||
alignment: string;
|
alignment: string;
|
||||||
}) => (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
}) => (state: EditorState, dispatch: Dispatch) => {
|
||||||
const cells = getCellsInColumn(index)(state.selection) || [];
|
const cells = getCellsInColumn(index)(state.selection) || [];
|
||||||
let transaction = state.tr;
|
let transaction = state.tr;
|
||||||
cells.forEach(({ pos }) => {
|
cells.forEach(({ pos }) => {
|
||||||
@@ -99,7 +95,7 @@ export default class Table extends Node {
|
|||||||
deleteColumn: () => deleteColumn,
|
deleteColumn: () => deleteColumn,
|
||||||
addRowAfter: ({ index }: { index: number }) => (
|
addRowAfter: ({ index }: { index: number }) => (
|
||||||
state: EditorState,
|
state: EditorState,
|
||||||
dispatch: (tr: Transaction) => void
|
dispatch: Dispatch
|
||||||
) => {
|
) => {
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
// A little hack to avoid cloning the heading row by cloning the row
|
// A little hack to avoid cloning the heading row by cloning the row
|
||||||
@@ -123,7 +119,7 @@ export default class Table extends Node {
|
|||||||
return {
|
return {
|
||||||
Tab: goToNextCell(1),
|
Tab: goToNextCell(1),
|
||||||
"Shift-Tab": goToNextCell(-1),
|
"Shift-Tab": goToNextCell(-1),
|
||||||
Enter: (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
Enter: (state: EditorState, dispatch: Dispatch) => {
|
||||||
if (!isInTable(state)) {
|
if (!isInTable(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Node as ProsemirrorNode } from "prosemirror-model";
|
import { Node as ProsemirrorNode } from "prosemirror-model";
|
||||||
import { EditorState } from "prosemirror-state";
|
import { EditorState, Transaction } from "prosemirror-state";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { DefaultTheme } from "styled-components";
|
import { DefaultTheme } from "styled-components";
|
||||||
|
|
||||||
@@ -34,3 +34,5 @@ export type ComponentProps = {
|
|||||||
isEditable: boolean;
|
isEditable: boolean;
|
||||||
getPos: () => number;
|
getPos: () => number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Dispatch = (tr: Transaction) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user