chore: Upgrade all of prosemirror (#5366)
Co-authored-by: Apoorv Mishra <apoorvmishra101092@gmail.com>
This commit is contained in:
@@ -4,9 +4,10 @@ import useBoolean from "~/hooks/useBoolean";
|
|||||||
import Initials from "./Initials";
|
import Initials from "./Initials";
|
||||||
|
|
||||||
export enum AvatarSize {
|
export enum AvatarSize {
|
||||||
Small = 18,
|
Small = 16,
|
||||||
Medium = 24,
|
Medium = 24,
|
||||||
Large = 32,
|
Large = 32,
|
||||||
|
XLarge = 48,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAvatar {
|
export interface IAvatar {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { findParentNode } from "prosemirror-utils";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import useDictionary from "~/hooks/useDictionary";
|
import useDictionary from "~/hooks/useDictionary";
|
||||||
import getMenuItems from "../menus/block";
|
import getMenuItems from "../menus/block";
|
||||||
import { useEditor } from "./EditorContext";
|
|
||||||
import SuggestionsMenu, {
|
import SuggestionsMenu, {
|
||||||
Props as SuggestionsMenuProps,
|
Props as SuggestionsMenuProps,
|
||||||
} from "./SuggestionsMenu";
|
} from "./SuggestionsMenu";
|
||||||
@@ -10,28 +8,18 @@ import SuggestionsMenuItem from "./SuggestionsMenuItem";
|
|||||||
|
|
||||||
type Props = Omit<
|
type Props = Omit<
|
||||||
SuggestionsMenuProps,
|
SuggestionsMenuProps,
|
||||||
"renderMenuItem" | "items" | "onClearSearch"
|
"renderMenuItem" | "items" | "trigger"
|
||||||
> &
|
> &
|
||||||
Required<Pick<SuggestionsMenuProps, "onLinkToolbarOpen" | "embeds">>;
|
Required<Pick<SuggestionsMenuProps, "onLinkToolbarOpen" | "embeds">>;
|
||||||
|
|
||||||
function BlockMenu(props: Props) {
|
function BlockMenu(props: Props) {
|
||||||
const { view } = useEditor();
|
|
||||||
const dictionary = useDictionary();
|
const dictionary = useDictionary();
|
||||||
|
|
||||||
const clearSearch = React.useCallback(() => {
|
|
||||||
const { state, dispatch } = view;
|
|
||||||
const parent = findParentNode((node) => !!node)(state.selection);
|
|
||||||
|
|
||||||
if (parent) {
|
|
||||||
dispatch(state.tr.insertText("", parent.pos, state.selection.to));
|
|
||||||
}
|
|
||||||
}, [view]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SuggestionsMenu
|
<SuggestionsMenu
|
||||||
{...props}
|
{...props}
|
||||||
filterable
|
filterable
|
||||||
onClearSearch={clearSearch}
|
trigger="/"
|
||||||
renderMenuItem={(item, _index, options) => (
|
renderMenuItem={(item, _index, options) => (
|
||||||
<SuggestionsMenuItem
|
<SuggestionsMenuItem
|
||||||
onClick={options.onClick}
|
onClick={options.onClick}
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ export default class ComponentView {
|
|||||||
node: ProsemirrorNode;
|
node: ProsemirrorNode;
|
||||||
view: EditorView;
|
view: EditorView;
|
||||||
getPos: () => number;
|
getPos: () => number;
|
||||||
decorations: Decoration<{
|
decorations: Decoration[];
|
||||||
[key: string]: any;
|
|
||||||
}>[];
|
|
||||||
|
|
||||||
isSelected = false;
|
isSelected = false;
|
||||||
dom: HTMLElement | null;
|
dom: HTMLElement | null;
|
||||||
@@ -39,9 +37,7 @@ export default class ComponentView {
|
|||||||
node: ProsemirrorNode;
|
node: ProsemirrorNode;
|
||||||
view: EditorView;
|
view: EditorView;
|
||||||
getPos: () => number;
|
getPos: () => number;
|
||||||
decorations: Decoration<{
|
decorations: Decoration[];
|
||||||
[key: string]: any;
|
|
||||||
}>[];
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
this.component = component;
|
this.component = component;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import FuzzySearch from "fuzzy-search";
|
import FuzzySearch from "fuzzy-search";
|
||||||
import gemojies from "gemoji";
|
import gemojies from "gemoji";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useEditor } from "./EditorContext";
|
|
||||||
import EmojiMenuItem from "./EmojiMenuItem";
|
import EmojiMenuItem from "./EmojiMenuItem";
|
||||||
import SuggestionsMenu, {
|
import SuggestionsMenu, {
|
||||||
Props as SuggestionsMenuProps,
|
Props as SuggestionsMenuProps,
|
||||||
@@ -26,12 +25,11 @@ const searcher = new FuzzySearch<{
|
|||||||
|
|
||||||
type Props = Omit<
|
type Props = Omit<
|
||||||
SuggestionsMenuProps<Emoji>,
|
SuggestionsMenuProps<Emoji>,
|
||||||
"renderMenuItem" | "items" | "onLinkToolbarOpen" | "embeds" | "onClearSearch"
|
"renderMenuItem" | "items" | "onLinkToolbarOpen" | "embeds" | "trigger"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const EmojiMenu = (props: Props) => {
|
const EmojiMenu = (props: Props) => {
|
||||||
const { search = "" } = props;
|
const { search = "" } = props;
|
||||||
const { view } = useEditor();
|
|
||||||
|
|
||||||
const items = React.useMemo(() => {
|
const items = React.useMemo(() => {
|
||||||
const n = search.toLowerCase();
|
const n = search.toLowerCase();
|
||||||
@@ -50,24 +48,11 @@ const EmojiMenu = (props: Props) => {
|
|||||||
return result.slice(0, 10);
|
return result.slice(0, 10);
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
||||||
const clearSearch = React.useCallback(() => {
|
|
||||||
const { state, dispatch } = view;
|
|
||||||
|
|
||||||
// clear search input
|
|
||||||
dispatch(
|
|
||||||
state.tr.insertText(
|
|
||||||
"",
|
|
||||||
state.selection.$from.pos - (props.search ?? "").length - 1,
|
|
||||||
state.selection.to
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}, [view, props.search]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SuggestionsMenu
|
<SuggestionsMenu
|
||||||
{...props}
|
{...props}
|
||||||
|
trigger=":"
|
||||||
filterable={false}
|
filterable={false}
|
||||||
onClearSearch={clearSearch}
|
|
||||||
renderMenuItem={(item, _index, options) => (
|
renderMenuItem={(item, _index, options) => (
|
||||||
<EmojiMenuItem
|
<EmojiMenuItem
|
||||||
onClick={options.onClick}
|
onClick={options.onClick}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NodeSelection } from "prosemirror-state";
|
import { NodeSelection } from "prosemirror-state";
|
||||||
import { CellSelection } from "prosemirror-tables";
|
import { CellSelection, selectedRect } from "prosemirror-tables";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { depths, s } from "@shared/styles";
|
import { depths, s } from "@shared/styles";
|
||||||
@@ -87,23 +87,37 @@ function usePosition({
|
|||||||
|
|
||||||
// tables are an oddity, and need their own positioning logic
|
// tables are an oddity, and need their own positioning logic
|
||||||
const isColSelection =
|
const isColSelection =
|
||||||
selection instanceof CellSelection &&
|
selection instanceof CellSelection && selection.isColSelection();
|
||||||
selection.isColSelection &&
|
|
||||||
selection.isColSelection();
|
|
||||||
const isRowSelection =
|
const isRowSelection =
|
||||||
selection instanceof CellSelection &&
|
selection instanceof CellSelection && selection.isRowSelection();
|
||||||
selection.isRowSelection &&
|
|
||||||
selection.isRowSelection();
|
|
||||||
|
|
||||||
if (isColSelection) {
|
if (isColSelection && isRowSelection) {
|
||||||
const { node: element } = view.domAtPos(selection.from);
|
const rect = selectedRect(view.state);
|
||||||
const { width } = (element as HTMLElement).getBoundingClientRect();
|
const table = view.domAtPos(rect.tableStart);
|
||||||
selectionBounds.top -= 20;
|
const bounds = (table.node as HTMLElement).getBoundingClientRect();
|
||||||
selectionBounds.right = selectionBounds.left + width;
|
selectionBounds.top = bounds.top - 16;
|
||||||
}
|
selectionBounds.left = bounds.left - 10;
|
||||||
|
selectionBounds.right = bounds.left - 10;
|
||||||
if (isRowSelection) {
|
} else if (isColSelection) {
|
||||||
selectionBounds.right = selectionBounds.left = selectionBounds.left - 18;
|
const rect = selectedRect(view.state);
|
||||||
|
const table = view.domAtPos(rect.tableStart);
|
||||||
|
const element = (table.node as HTMLElement).querySelector(
|
||||||
|
`tr > *:nth-child(${rect.left + 1})`
|
||||||
|
);
|
||||||
|
const bounds = (element as HTMLElement).getBoundingClientRect();
|
||||||
|
selectionBounds.top = bounds.top - 16;
|
||||||
|
selectionBounds.left = bounds.left;
|
||||||
|
selectionBounds.right = bounds.right;
|
||||||
|
} else if (isRowSelection) {
|
||||||
|
const rect = selectedRect(view.state);
|
||||||
|
const table = view.domAtPos(rect.tableStart);
|
||||||
|
const element = (table.node as HTMLElement).querySelector(
|
||||||
|
`tr:nth-child(${rect.top + 1}) > *`
|
||||||
|
);
|
||||||
|
const bounds = (element as HTMLElement).getBoundingClientRect();
|
||||||
|
selectionBounds.top = bounds.top;
|
||||||
|
selectionBounds.left = bounds.left - 10;
|
||||||
|
selectionBounds.right = bounds.left - 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isImageSelection =
|
const isImageSelection =
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
OpenIcon,
|
OpenIcon,
|
||||||
} from "outline-icons";
|
} from "outline-icons";
|
||||||
import { Mark } from "prosemirror-model";
|
import { Mark } from "prosemirror-model";
|
||||||
import { setTextSelection } from "prosemirror-utils";
|
import { Selection } from "prosemirror-state";
|
||||||
import { EditorView } from "prosemirror-view";
|
import { EditorView } from "prosemirror-view";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
@@ -285,7 +285,10 @@ class LinkEditor extends React.Component<Props, State> {
|
|||||||
moveSelectionToEnd = () => {
|
moveSelectionToEnd = () => {
|
||||||
const { to, view } = this.props;
|
const { to, view } = this.props;
|
||||||
const { state, dispatch } = view;
|
const { state, dispatch } = view;
|
||||||
dispatch(setTextSelection(to)(state.tr));
|
const nextSelection = Selection.findFrom(state.tr.doc.resolve(to), 1, true);
|
||||||
|
if (nextSelection) {
|
||||||
|
dispatch(state.tr.setSelection(nextSelection));
|
||||||
|
}
|
||||||
view.focus();
|
view.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import { MentionType } from "@shared/types";
|
|||||||
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
|
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
|
||||||
import User from "~/models/User";
|
import User from "~/models/User";
|
||||||
import Avatar from "~/components/Avatar";
|
import Avatar from "~/components/Avatar";
|
||||||
|
import { AvatarSize } from "~/components/Avatar/Avatar";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import useRequest from "~/hooks/useRequest";
|
import useRequest from "~/hooks/useRequest";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import { useEditor } from "./EditorContext";
|
|
||||||
import MentionMenuItem from "./MentionMenuItem";
|
import MentionMenuItem from "./MentionMenuItem";
|
||||||
import SuggestionsMenu, {
|
import SuggestionsMenu, {
|
||||||
Props as SuggestionsMenuProps,
|
Props as SuggestionsMenuProps,
|
||||||
@@ -32,7 +32,7 @@ interface MentionItem extends MenuItem {
|
|||||||
|
|
||||||
type Props = Omit<
|
type Props = Omit<
|
||||||
SuggestionsMenuProps<MentionItem>,
|
SuggestionsMenuProps<MentionItem>,
|
||||||
"renderMenuItem" | "items" | "onLinkToolbarOpen" | "embeds" | "onClearSearch"
|
"renderMenuItem" | "items" | "onLinkToolbarOpen" | "embeds" | "trigger"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
function MentionMenu({ search, isActive, ...rest }: Props) {
|
function MentionMenu({ search, isActive, ...rest }: Props) {
|
||||||
@@ -42,7 +42,6 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
|||||||
const { users, auth } = useStores();
|
const { users, auth } = useStores();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const documentId = parseDocumentSlug(location.pathname);
|
const documentId = parseDocumentSlug(location.pathname);
|
||||||
const { view } = useEditor();
|
|
||||||
const { data, loading, request } = useRequest(
|
const { data, loading, request } = useRequest(
|
||||||
React.useCallback(
|
React.useCallback(
|
||||||
() =>
|
() =>
|
||||||
@@ -80,19 +79,6 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
|||||||
}
|
}
|
||||||
}, [auth.user?.id, loading, data]);
|
}, [auth.user?.id, loading, data]);
|
||||||
|
|
||||||
const clearSearch = () => {
|
|
||||||
const { state, dispatch } = view;
|
|
||||||
|
|
||||||
// clear search input
|
|
||||||
dispatch(
|
|
||||||
state.tr.insertText(
|
|
||||||
"",
|
|
||||||
state.selection.$from.pos - (search ?? "").length - 1,
|
|
||||||
state.selection.to
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prevent showing the menu until we have data otherwise it will be positioned
|
// Prevent showing the menu until we have data otherwise it will be positioned
|
||||||
// incorrectly due to the height being unknown.
|
// incorrectly due to the height being unknown.
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
@@ -104,7 +90,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
|||||||
{...rest}
|
{...rest}
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
filterable={false}
|
filterable={false}
|
||||||
onClearSearch={clearSearch}
|
trigger="@"
|
||||||
search={search}
|
search={search}
|
||||||
renderMenuItem={(item, _index, options) => (
|
renderMenuItem={(item, _index, options) => (
|
||||||
<MentionMenuItem
|
<MentionMenuItem
|
||||||
@@ -122,7 +108,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
|||||||
model={item.user}
|
model={item.user}
|
||||||
showBorder={false}
|
showBorder={false}
|
||||||
alt={t("Profile picture")}
|
alt={t("Profile picture")}
|
||||||
size={16}
|
size={AvatarSize.Small}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { some } from "lodash";
|
import { some } from "lodash";
|
||||||
import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
|
import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
|
||||||
import { CellSelection } from "prosemirror-tables";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import createAndInsertLink from "@shared/editor/commands/createAndInsertLink";
|
import createAndInsertLink from "@shared/editor/commands/createAndInsertLink";
|
||||||
import filterExcessSeparators from "@shared/editor/lib/filterExcessSeparators";
|
import filterExcessSeparators from "@shared/editor/lib/filterExcessSeparators";
|
||||||
import getColumnIndex from "@shared/editor/queries/getColumnIndex";
|
|
||||||
import getMarkRange from "@shared/editor/queries/getMarkRange";
|
import getMarkRange from "@shared/editor/queries/getMarkRange";
|
||||||
import getRowIndex from "@shared/editor/queries/getRowIndex";
|
|
||||||
import isMarkActive from "@shared/editor/queries/isMarkActive";
|
import isMarkActive from "@shared/editor/queries/isMarkActive";
|
||||||
import isNodeActive from "@shared/editor/queries/isNodeActive";
|
import isNodeActive from "@shared/editor/queries/isNodeActive";
|
||||||
|
import { getColumnIndex, getRowIndex } from "@shared/editor/queries/table";
|
||||||
import { MenuItem } from "@shared/editor/types";
|
import { MenuItem } from "@shared/editor/types";
|
||||||
import { creatingUrlPrefix } from "@shared/utils/urls";
|
import { creatingUrlPrefix } from "@shared/utils/urls";
|
||||||
import useBoolean from "~/hooks/useBoolean";
|
import useBoolean from "~/hooks/useBoolean";
|
||||||
@@ -197,8 +195,8 @@ export default function SelectionToolbar(props: Props) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const colIndex = getColumnIndex(state.selection as unknown as CellSelection);
|
const colIndex = getColumnIndex(state);
|
||||||
const rowIndex = getRowIndex(state.selection as unknown as CellSelection);
|
const rowIndex = getRowIndex(state);
|
||||||
const isTableSelection = colIndex !== undefined && rowIndex !== undefined;
|
const isTableSelection = colIndex !== undefined && rowIndex !== undefined;
|
||||||
const link = isMarkActive(state.schema.marks.link)(state);
|
const link = isMarkActive(state.schema.marks.link)(state);
|
||||||
const range = getMarkRange(selection.$from, state.schema.marks.link);
|
const range = getMarkRange(selection.$from, state.schema.marks.link);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import commandScore from "command-score";
|
import commandScore from "command-score";
|
||||||
import { capitalize } from "lodash";
|
import { capitalize } from "lodash";
|
||||||
import { findParentNode } from "prosemirror-utils";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans } from "react-i18next";
|
import { Trans } from "react-i18next";
|
||||||
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
||||||
@@ -8,6 +7,7 @@ import styled from "styled-components";
|
|||||||
import insertFiles from "@shared/editor/commands/insertFiles";
|
import insertFiles from "@shared/editor/commands/insertFiles";
|
||||||
import { EmbedDescriptor } from "@shared/editor/embeds";
|
import { EmbedDescriptor } from "@shared/editor/embeds";
|
||||||
import filterExcessSeparators from "@shared/editor/lib/filterExcessSeparators";
|
import filterExcessSeparators from "@shared/editor/lib/filterExcessSeparators";
|
||||||
|
import { findParentNode } from "@shared/editor/queries/findParentNode";
|
||||||
import { MenuItem } from "@shared/editor/types";
|
import { MenuItem } from "@shared/editor/types";
|
||||||
import { depths, s } from "@shared/styles";
|
import { depths, s } from "@shared/styles";
|
||||||
import { getEventFiles } from "@shared/utils/files";
|
import { getEventFiles } from "@shared/utils/files";
|
||||||
@@ -56,12 +56,12 @@ export type Props<T extends MenuItem = MenuItem> = {
|
|||||||
rtl: boolean;
|
rtl: boolean;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
search: string;
|
search: string;
|
||||||
|
trigger: string;
|
||||||
uploadFile?: (file: File) => Promise<string>;
|
uploadFile?: (file: File) => Promise<string>;
|
||||||
onFileUploadStart?: () => void;
|
onFileUploadStart?: () => void;
|
||||||
onFileUploadStop?: () => void;
|
onFileUploadStop?: () => void;
|
||||||
onLinkToolbarOpen?: () => void;
|
onLinkToolbarOpen?: () => void;
|
||||||
onClose: (insertNewLine?: boolean) => void;
|
onClose: (insertNewLine?: boolean) => void;
|
||||||
onClearSearch: () => void;
|
|
||||||
embeds?: EmbedDescriptor[];
|
embeds?: EmbedDescriptor[];
|
||||||
renderMenuItem: (
|
renderMenuItem: (
|
||||||
item: T,
|
item: T,
|
||||||
@@ -163,6 +163,30 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
|||||||
[view]
|
[view]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleClearSearch = React.useCallback(() => {
|
||||||
|
const { state, dispatch } = view;
|
||||||
|
const poss = state.doc.cut(
|
||||||
|
state.selection.from - (props.search ?? "").length - 1,
|
||||||
|
state.selection.from
|
||||||
|
);
|
||||||
|
const trimTrigger = poss.textContent.startsWith(props.trigger);
|
||||||
|
|
||||||
|
if (!props.search && !trimTrigger) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear search input
|
||||||
|
dispatch(
|
||||||
|
state.tr.insertText(
|
||||||
|
"",
|
||||||
|
state.selection.from -
|
||||||
|
(props.search ?? "").length -
|
||||||
|
(trimTrigger ? 1 : 0),
|
||||||
|
state.selection.to
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [props.search, props.trigger, view]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!props.isActive) {
|
if (!props.isActive) {
|
||||||
return;
|
return;
|
||||||
@@ -185,7 +209,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
|||||||
|
|
||||||
const insertNode = React.useCallback(
|
const insertNode = React.useCallback(
|
||||||
(item: MenuItem | EmbedDescriptor) => {
|
(item: MenuItem | EmbedDescriptor) => {
|
||||||
props.onClearSearch();
|
handleClearSearch();
|
||||||
|
|
||||||
const command = item.name ? commands[item.name] : undefined;
|
const command = item.name ? commands[item.name] : undefined;
|
||||||
|
|
||||||
@@ -201,7 +225,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
|||||||
|
|
||||||
props.onClose();
|
props.onClose();
|
||||||
},
|
},
|
||||||
[commands, props, view]
|
[commands, handleClearSearch, props, view]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClickItem = React.useCallback(
|
const handleClickItem = React.useCallback(
|
||||||
@@ -216,7 +240,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
|||||||
case "embed":
|
case "embed":
|
||||||
return triggerLinkInput(item);
|
return triggerLinkInput(item);
|
||||||
case "link": {
|
case "link": {
|
||||||
props.onClearSearch();
|
handleClearSearch();
|
||||||
props.onClose();
|
props.onClose();
|
||||||
props.onLinkToolbarOpen?.();
|
props.onLinkToolbarOpen?.();
|
||||||
return;
|
return;
|
||||||
@@ -225,7 +249,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
|||||||
insertNode(item);
|
insertNode(item);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[insertNode, props]
|
[insertNode, handleClearSearch, props]
|
||||||
);
|
);
|
||||||
|
|
||||||
const close = React.useCallback(() => {
|
const close = React.useCallback(() => {
|
||||||
@@ -313,7 +337,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
|||||||
const files = getEventFiles(event);
|
const files = getEventFiles(event);
|
||||||
const parent = findParentNode((node) => !!node)(view.state.selection);
|
const parent = findParentNode((node) => !!node)(view.state.selection);
|
||||||
|
|
||||||
props.onClearSearch();
|
handleClearSearch();
|
||||||
|
|
||||||
if (!uploadFile) {
|
if (!uploadFile) {
|
||||||
throw new Error("uploadFile prop is required to replace files");
|
throw new Error("uploadFile prop is required to replace files");
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
Node as ProsemirrorNode,
|
Node as ProsemirrorNode,
|
||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import { EditorState, Selection, Plugin, Transaction } from "prosemirror-state";
|
import { EditorState, Selection, Plugin, Transaction } from "prosemirror-state";
|
||||||
import { Decoration, EditorView } from "prosemirror-view";
|
import { Decoration, EditorView, NodeViewConstructor } from "prosemirror-view";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import styled, { css, DefaultTheme, ThemeProps } from "styled-components";
|
import styled, { css, DefaultTheme, ThemeProps } from "styled-components";
|
||||||
import Styles from "@shared/editor/components/Styles";
|
import Styles from "@shared/editor/components/Styles";
|
||||||
@@ -193,14 +193,7 @@ export class Editor extends React.PureComponent<
|
|||||||
keymaps: Plugin[];
|
keymaps: Plugin[];
|
||||||
inputRules: InputRule[];
|
inputRules: InputRule[];
|
||||||
nodeViews: {
|
nodeViews: {
|
||||||
[name: string]: (
|
[name: string]: NodeViewConstructor;
|
||||||
node: ProsemirrorNode,
|
|
||||||
view: EditorView,
|
|
||||||
getPos: () => number,
|
|
||||||
decorations: Decoration<{
|
|
||||||
[key: string]: any;
|
|
||||||
}>[]
|
|
||||||
) => ComponentView;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
nodes: { [name: string]: NodeSpec };
|
nodes: { [name: string]: NodeSpec };
|
||||||
@@ -350,9 +343,7 @@ export class Editor extends React.PureComponent<
|
|||||||
node: ProsemirrorNode,
|
node: ProsemirrorNode,
|
||||||
view: EditorView,
|
view: EditorView,
|
||||||
getPos: () => number,
|
getPos: () => number,
|
||||||
decorations: Decoration<{
|
decorations: Decoration[]
|
||||||
[key: string]: any;
|
|
||||||
}>[]
|
|
||||||
) =>
|
) =>
|
||||||
new ComponentView(extension.component, {
|
new ComponentView(extension.component, {
|
||||||
editor: this,
|
editor: this,
|
||||||
@@ -435,7 +426,7 @@ export class Editor extends React.PureComponent<
|
|||||||
private createDocument(content: string | object) {
|
private createDocument(content: string | object) {
|
||||||
// Looks like Markdown
|
// Looks like Markdown
|
||||||
if (typeof content === "string") {
|
if (typeof content === "string") {
|
||||||
return this.parser.parse(content);
|
return this.parser.parse(content) || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProsemirrorNode.fromJSON(this.schema, content);
|
return ProsemirrorNode.fromJSON(this.schema, content);
|
||||||
@@ -464,8 +455,9 @@ export class Editor extends React.PureComponent<
|
|||||||
nodeViews: this.nodeViews,
|
nodeViews: this.nodeViews,
|
||||||
dispatchTransaction(transaction) {
|
dispatchTransaction(transaction) {
|
||||||
// callback is bound to have the view instance as its this binding
|
// callback is bound to have the view instance as its this binding
|
||||||
const { state, transactions } =
|
const { state, transactions } = (
|
||||||
this.state.applyTransaction(transaction);
|
this.state as EditorState
|
||||||
|
).applyTransaction(transaction);
|
||||||
|
|
||||||
this.updateState(state);
|
this.updateState(state);
|
||||||
|
|
||||||
|
|||||||
@@ -140,11 +140,15 @@ class DocumentScene extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { view, parser } = editorRef;
|
const { view, parser } = editorRef;
|
||||||
view.dispatch(
|
const doc = parser.parse(template.text);
|
||||||
view.state.tr
|
|
||||||
.setSelection(new AllSelection(view.state.doc))
|
if (doc) {
|
||||||
.replaceSelectionWith(parser.parse(template.text))
|
view.dispatch(
|
||||||
);
|
view.state.tr
|
||||||
|
.setSelection(new AllSelection(view.state.doc))
|
||||||
|
.replaceSelectionWith(doc)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.isEditorDirty = true;
|
this.isEditorDirty = true;
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,9 @@ const EditableTitle = React.forwardRef(
|
|||||||
|
|
||||||
if (isMarkdown(text)) {
|
if (isMarkdown(text)) {
|
||||||
const paste = pasteParser.parse(normalizePastedMarkdown(content));
|
const paste = pasteParser.parse(normalizePastedMarkdown(content));
|
||||||
slice = paste.slice(0);
|
if (paste) {
|
||||||
|
slice = paste.slice(0);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const defaultSlice = __parseFromClipboard(
|
const defaultSlice = __parseFromClipboard(
|
||||||
view,
|
view,
|
||||||
@@ -165,11 +167,13 @@ const EditableTitle = React.forwardRef(
|
|||||||
: defaultSlice;
|
: defaultSlice;
|
||||||
}
|
}
|
||||||
|
|
||||||
view.dispatch(
|
if (slice) {
|
||||||
view.state.tr
|
view.dispatch(
|
||||||
.setSelection(Selection.atStart(view.state.doc))
|
view.state.tr
|
||||||
.replaceSelection(slice)
|
.setSelection(Selection.atStart(view.state.doc))
|
||||||
);
|
.replaceSelection(slice)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[editor]
|
[editor]
|
||||||
|
|||||||
14
app/typings/prosemirror-view.d.ts
vendored
Normal file
14
app/typings/prosemirror-view.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import "prosemirror-view";
|
||||||
|
|
||||||
|
declare module "prosemirror-view" {
|
||||||
|
import { ResolvedPos } from "prosemirror-model";
|
||||||
|
import { EditorView } from "prosemirror-view";
|
||||||
|
|
||||||
|
export function __parseFromClipboard(
|
||||||
|
view: EditorView,
|
||||||
|
text: string,
|
||||||
|
html: string | null,
|
||||||
|
plainText: boolean,
|
||||||
|
$context: ResolvedPos
|
||||||
|
);
|
||||||
|
}
|
||||||
41
package.json
41
package.json
@@ -15,7 +15,7 @@
|
|||||||
"dev:watch": "NODE_ENV=development yarn concurrently -n backend,frontend \"yarn dev:backend\" \"yarn vite:dev\"",
|
"dev:watch": "NODE_ENV=development yarn concurrently -n backend,frontend \"yarn dev:backend\" \"yarn vite:dev\"",
|
||||||
"lint": "eslint app server shared plugins",
|
"lint": "eslint app server shared plugins",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"postinstall": "rimraf node_modules/prosemirror-view/dist/index.d.ts && yarn patch-package",
|
"postinstall": "yarn patch-package",
|
||||||
"heroku-postbuild": "yarn build && yarn db:migrate",
|
"heroku-postbuild": "yarn build && yarn db:migrate",
|
||||||
"db:create-migration": "sequelize migration:create",
|
"db:create-migration": "sequelize migration:create",
|
||||||
"db:create": "sequelize db:create",
|
"db:create": "sequelize db:create",
|
||||||
@@ -149,20 +149,19 @@
|
|||||||
"pg": "^8.8.0",
|
"pg": "^8.8.0",
|
||||||
"pg-tsquery": "^8.4.0",
|
"pg-tsquery": "^8.4.0",
|
||||||
"polished": "^4.2.2",
|
"polished": "^4.2.2",
|
||||||
"prosemirror-commands": "1.2.2",
|
"prosemirror-commands": "^1.5.2",
|
||||||
"prosemirror-dropcursor": "^1.6.1",
|
"prosemirror-dropcursor": "^1.8.1",
|
||||||
"prosemirror-gapcursor": "^1.3.1",
|
"prosemirror-gapcursor": "^1.3.2",
|
||||||
"prosemirror-history": "^1.2.0",
|
"prosemirror-history": "^1.3.2",
|
||||||
"prosemirror-inputrules": "^1.1.3",
|
"prosemirror-inputrules": "^1.2.1",
|
||||||
"prosemirror-keymap": "^1.1.5",
|
"prosemirror-keymap": "^1.2.2",
|
||||||
"prosemirror-markdown": "^1.9.3",
|
"prosemirror-markdown": "^1.11.0",
|
||||||
"prosemirror-model": "1.16.1",
|
"prosemirror-model": "^1.19.1",
|
||||||
"prosemirror-schema-list": "1.1.4",
|
"prosemirror-schema-list": "^1.2.3",
|
||||||
"prosemirror-state": "1.3.4",
|
"prosemirror-state": "^1.4.3",
|
||||||
"prosemirror-tables": "^1.1.1",
|
"prosemirror-tables": "^1.3.2",
|
||||||
"prosemirror-transform": "1.2.5",
|
"prosemirror-transform": "^1.7.2",
|
||||||
"prosemirror-utils": "^0.9.6",
|
"prosemirror-view": "^1.31.3",
|
||||||
"prosemirror-view": "1.26.5",
|
|
||||||
"query-string": "^7.1.1",
|
"query-string": "^7.1.1",
|
||||||
"quoted-printable": "^1.0.1",
|
"quoted-printable": "^1.0.1",
|
||||||
"randomstring": "1.2.3",
|
"randomstring": "1.2.3",
|
||||||
@@ -263,17 +262,6 @@
|
|||||||
"@types/node-fetch": "^2.6.2",
|
"@types/node-fetch": "^2.6.2",
|
||||||
"@types/nodemailer": "^6.4.7",
|
"@types/nodemailer": "^6.4.7",
|
||||||
"@types/passport-oauth2": "^1.4.11",
|
"@types/passport-oauth2": "^1.4.11",
|
||||||
"@types/prosemirror-commands": "^1.0.4",
|
|
||||||
"@types/prosemirror-dropcursor": "^1.5.0",
|
|
||||||
"@types/prosemirror-gapcursor": "^1.3.0",
|
|
||||||
"@types/prosemirror-history": "^1.0.1",
|
|
||||||
"@types/prosemirror-inputrules": "^1.0.2",
|
|
||||||
"@types/prosemirror-keymap": "^1.0.1",
|
|
||||||
"@types/prosemirror-markdown": "^1.0.3",
|
|
||||||
"@types/prosemirror-model": "^1.7.2",
|
|
||||||
"@types/prosemirror-schema-list": "^1.0.3",
|
|
||||||
"@types/prosemirror-state": "^1.2.4",
|
|
||||||
"@types/prosemirror-view": "^1.11.4",
|
|
||||||
"@types/quoted-printable": "^1.0.0",
|
"@types/quoted-printable": "^1.0.0",
|
||||||
"@types/randomstring": "^1.1.8",
|
"@types/randomstring": "^1.1.8",
|
||||||
"@types/react": "^17.0.34",
|
"@types/react": "^17.0.34",
|
||||||
@@ -341,7 +329,6 @@
|
|||||||
"body-scroll-lock": "^4.0.0-beta.0",
|
"body-scroll-lock": "^4.0.0-beta.0",
|
||||||
"d3": "^7.0.0",
|
"d3": "^7.0.0",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"prosemirror-transform": "1.2.5",
|
|
||||||
"dot-prop": "^5.2.0",
|
"dot-prop": "^5.2.0",
|
||||||
"js-yaml": "^3.14.1",
|
"js-yaml": "^3.14.1",
|
||||||
"jpeg-js": "0.4.4",
|
"jpeg-js": "0.4.4",
|
||||||
|
|||||||
@@ -13,13 +13,12 @@ export default function markdownToYDoc(
|
|||||||
// in the editor embeds were created at runtime by converting links
|
// in the editor embeds were created at runtime by converting links
|
||||||
// into embeds where they match. Because we're converting to a CRDT structure
|
// into embeds where they match. Because we're converting to a CRDT structure
|
||||||
// on the server we need to mimic this behavior.
|
// on the server we need to mimic this behavior.
|
||||||
function urlsToEmbeds(node: Node): Node {
|
function urlsToEmbeds(node: Node): Node | null {
|
||||||
if (node.type.name === "paragraph") {
|
if (node.type.name === "paragraph") {
|
||||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'content' does not exist on type 'Fragmen... Remove this comment to see the full error message
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'content' does not exist on type 'Fragmen... Remove this comment to see the full error message
|
||||||
for (const textNode of node.content.content) {
|
for (const textNode of node.content.content) {
|
||||||
for (const embed of embeds) {
|
for (const embed of embeds) {
|
||||||
if (textNode.text && embed.matcher(textNode.text)) {
|
if (textNode.text && embed.matcher(textNode.text)) {
|
||||||
// @ts-expect-error ts-migrate(2322) FIXME: Type 'ProsemirrorNode<Schema<never, never>> | null... Remove this comment to see the full error message
|
|
||||||
return schema.nodes.embed.createAndFill({
|
return schema.nodes.embed.createAndFill({
|
||||||
href: textNode.text,
|
href: textNode.text,
|
||||||
});
|
});
|
||||||
@@ -30,14 +29,19 @@ export default function markdownToYDoc(
|
|||||||
|
|
||||||
if (node.content) {
|
if (node.content) {
|
||||||
const contentAsArray =
|
const contentAsArray =
|
||||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'content' does not exist on type 'Fragmen... Remove this comment to see the full error message
|
// @ts-expect-error content
|
||||||
node.content instanceof Fragment ? node.content.content : node.content;
|
node.content instanceof Fragment ? node.content.content : node.content;
|
||||||
|
// @ts-expect-error content
|
||||||
node.content = Fragment.fromArray(contentAsArray.map(urlsToEmbeds));
|
node.content = Fragment.fromArray(contentAsArray.map(urlsToEmbeds));
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = urlsToEmbeds(node);
|
if (node) {
|
||||||
|
node = urlsToEmbeds(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error null node
|
||||||
return prosemirrorToYDoc(node, fieldName);
|
return prosemirrorToYDoc(node, fieldName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { parser } from ".";
|
|||||||
test("renders an empty doc", () => {
|
test("renders an empty doc", () => {
|
||||||
const ast = parser.parse("");
|
const ast = parser.parse("");
|
||||||
|
|
||||||
expect(ast.toJSON()).toEqual({
|
expect(ast?.toJSON()).toEqual({
|
||||||
content: [{ type: "paragraph" }],
|
content: [{ type: "paragraph" }],
|
||||||
type: "doc",
|
type: "doc",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default class DocumentHelper {
|
|||||||
Y.applyUpdate(ydoc, document.state);
|
Y.applyUpdate(ydoc, document.state);
|
||||||
return Node.fromJSON(schema, yDocToProsemirrorJSON(ydoc, "default"));
|
return Node.fromJSON(schema, yDocToProsemirrorJSON(ydoc, "default"));
|
||||||
}
|
}
|
||||||
return parser.parse(document.text);
|
return parser.parse(document.text) || Node.fromJSON(schema, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import { parser } from "@server/editor";
|
|||||||
export default function parseDocumentIds(text: string): string[] {
|
export default function parseDocumentIds(text: string): string[] {
|
||||||
const doc = parser.parse(text);
|
const doc = parser.parse(text);
|
||||||
const identifiers: string[] = [];
|
const identifiers: string[] = [];
|
||||||
|
if (!doc) {
|
||||||
|
return identifiers;
|
||||||
|
}
|
||||||
|
|
||||||
doc.descendants((node: Node) => {
|
doc.descendants((node: Node) => {
|
||||||
// get text nodes
|
// get text nodes
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { parser } from "@server/editor";
|
|||||||
export default function parseImages(text: string): string[] {
|
export default function parseImages(text: string): string[] {
|
||||||
const doc = parser.parse(text);
|
const doc = parser.parse(text);
|
||||||
const images: string[] = [];
|
const images: string[] = [];
|
||||||
|
if (!doc) {
|
||||||
|
return images;
|
||||||
|
}
|
||||||
|
|
||||||
doc.descendants((node: Node) => {
|
doc.descendants((node: Node) => {
|
||||||
if (node.type.name === "image") {
|
if (node.type.name === "image") {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { NodeType } from "prosemirror-model";
|
import { NodeType } from "prosemirror-model";
|
||||||
import { EditorState } from "prosemirror-state";
|
import { Command } from "prosemirror-state";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the current node to a paragraph when pressing backspace at the
|
* Converts the current node to a paragraph when pressing backspace at the
|
||||||
@@ -9,28 +8,28 @@ import { Dispatch } from "../types";
|
|||||||
* @param type The node type
|
* @param type The node type
|
||||||
* @returns A prosemirror command.
|
* @returns A prosemirror command.
|
||||||
*/
|
*/
|
||||||
export default function backspaceToParagraph(type: NodeType) {
|
export default function backspaceToParagraph(type: NodeType): Command {
|
||||||
return (state: EditorState, dispatch: Dispatch) => {
|
return (state, 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
|
||||||
if (!empty) {
|
if (!empty) {
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check we're in a matching node
|
// check we're in a matching node
|
||||||
if ($from.parent.type !== type) {
|
if ($from.parent.type !== type) {
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we're at the beginning of the heading
|
// check if we're at the beginning of the heading
|
||||||
const $pos = state.doc.resolve(from - 1);
|
const $pos = state.doc.resolve(from - 1);
|
||||||
if ($pos.parent === $from.parent) {
|
if ($pos.parent === $from.parent) {
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// okay, replace it with a paragraph
|
// okay, replace it with a paragraph
|
||||||
dispatch(
|
dispatch?.(
|
||||||
state.tr
|
state.tr
|
||||||
.setBlockType(from, to, type.schema.nodes.paragraph)
|
.setBlockType(from, to, type.schema.nodes.paragraph)
|
||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { EditorState } from "prosemirror-state";
|
import { Command } from "prosemirror-state";
|
||||||
import { liftTarget } from "prosemirror-transform";
|
import { liftTarget } from "prosemirror-transform";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
|
|
||||||
const clearNodes = () => (state: EditorState, dispatch?: Dispatch) => {
|
const clearNodes = (): Command => (state, dispatch) => {
|
||||||
const { tr } = state;
|
const { tr } = state;
|
||||||
const { selection } = tr;
|
const { selection } = tr;
|
||||||
const { ranges } = selection;
|
const { ranges } = selection;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { EditorState, TextSelection } from "prosemirror-state";
|
import { Command, TextSelection } from "prosemirror-state";
|
||||||
import isInCode from "../queries/isInCode";
|
import isInCode from "../queries/isInCode";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the current selection to the previous newline, this is used inside
|
* Moves the current selection to the previous newline, this is used inside
|
||||||
@@ -8,10 +7,7 @@ import { Dispatch } from "../types";
|
|||||||
*
|
*
|
||||||
* @returns A prosemirror command.
|
* @returns A prosemirror command.
|
||||||
*/
|
*/
|
||||||
export const moveToPreviousNewline = (
|
export const moveToPreviousNewline: Command = (state, dispatch) => {
|
||||||
state: EditorState,
|
|
||||||
dispatch: Dispatch
|
|
||||||
) => {
|
|
||||||
if (!isInCode(state)) {
|
if (!isInCode(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -34,7 +30,7 @@ export const moveToPreviousNewline = (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch?.(
|
||||||
state.tr.setSelection(
|
state.tr.setSelection(
|
||||||
TextSelection.create(
|
TextSelection.create(
|
||||||
state.doc,
|
state.doc,
|
||||||
@@ -52,7 +48,7 @@ export const moveToPreviousNewline = (
|
|||||||
*
|
*
|
||||||
* @returns A prosemirror command.
|
* @returns A prosemirror command.
|
||||||
*/
|
*/
|
||||||
export const moveToNextNewline = (state: EditorState, dispatch: Dispatch) => {
|
export const moveToNextNewline: Command = (state, dispatch) => {
|
||||||
if (!isInCode(state)) {
|
if (!isInCode(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -69,7 +65,7 @@ export const moveToNextNewline = (state: EditorState, dispatch: Dispatch) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch?.(
|
||||||
state.tr.setSelection(
|
state.tr.setSelection(
|
||||||
TextSelection.create(state.doc, beginningOfNode + endOfLine)
|
TextSelection.create(state.doc, beginningOfNode + endOfLine)
|
||||||
)
|
)
|
||||||
@@ -85,7 +81,7 @@ export const moveToNextNewline = (state: EditorState, dispatch: Dispatch) => {
|
|||||||
*
|
*
|
||||||
* @returns A prosemirror command
|
* @returns A prosemirror command
|
||||||
*/
|
*/
|
||||||
export const newlineInCode = (state: EditorState, dispatch: Dispatch) => {
|
export const newlineInCode: Command = (state, dispatch) => {
|
||||||
if (!isInCode(state)) {
|
if (!isInCode(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -101,7 +97,7 @@ export const newlineInCode = (state: EditorState, dispatch: Dispatch) => {
|
|||||||
newText += " ".repeat(numOfSpaces);
|
newText += " ".repeat(numOfSpaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(tr.insertText(newText, selection.from, selection.to));
|
dispatch?.(tr.insertText(newText, selection.from, selection.to));
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -110,12 +106,12 @@ export const newlineInCode = (state: EditorState, dispatch: Dispatch) => {
|
|||||||
*
|
*
|
||||||
* @returns A prosemirror command
|
* @returns A prosemirror command
|
||||||
*/
|
*/
|
||||||
export const insertSpaceTab = (state: EditorState, dispatch: Dispatch) => {
|
export const insertSpaceTab: Command = (state, dispatch) => {
|
||||||
if (!isInCode(state)) {
|
if (!isInCode(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tr, selection } = state;
|
const { tr, selection } = state;
|
||||||
dispatch(tr.insertText(" ", selection.from, selection.to));
|
dispatch?.(tr.insertText(" ", selection.from, selection.to));
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { EditorState, TextSelection } from "prosemirror-state";
|
import { Command, TextSelection } from "prosemirror-state";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
|
|
||||||
const collapseSelection = () => (state: EditorState, dispatch?: Dispatch) => {
|
const collapseSelection = (): Command => (state, dispatch) => {
|
||||||
dispatch?.(
|
dispatch?.(
|
||||||
state.tr.setSelection(
|
state.tr.setSelection(
|
||||||
TextSelection.create(state.doc, state.tr.selection.from)
|
TextSelection.create(state.doc, state.tr.selection.from)
|
||||||
|
|||||||
31
shared/editor/commands/deleteEmptyFirstParagraph.ts
Normal file
31
shared/editor/commands/deleteEmptyFirstParagraph.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Command } from "prosemirror-state";
|
||||||
|
import isNodeActive from "../queries/isNodeActive";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the first paragraph node if it is empty and the cursor is at the
|
||||||
|
* beginning of the document.
|
||||||
|
*/
|
||||||
|
const deleteEmptyFirstParagraph: Command = (state, dispatch) => {
|
||||||
|
if (!isNodeActive(state.schema.nodes.paragraph)(state)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.selection.from !== 1 || state.selection.to !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = state.selection.$from.parent;
|
||||||
|
if (parent.textContent !== "" || parent.childCount > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the empty paragraph node
|
||||||
|
dispatch?.(
|
||||||
|
state.tr
|
||||||
|
.delete(state.selection.from - 1, state.selection.from)
|
||||||
|
.scrollIntoView()
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default deleteEmptyFirstParagraph;
|
||||||
@@ -17,9 +17,13 @@ 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 { Selection, EditorState, TextSelection } from "prosemirror-state";
|
import {
|
||||||
|
Selection,
|
||||||
|
EditorState,
|
||||||
|
TextSelection,
|
||||||
|
Command,
|
||||||
|
} 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;
|
||||||
@@ -30,8 +34,8 @@ function hasCode(state: EditorState, pos: number) {
|
|||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function moveLeft() {
|
export default function moveLeft(): Command {
|
||||||
return (state: EditorState, dispatch: Dispatch): boolean => {
|
return (state, 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) {
|
||||||
@@ -70,14 +74,14 @@ export default function moveLeft() {
|
|||||||
Selection.near(state.doc.resolve($cursor.pos - 1))
|
Selection.near(state.doc.resolve($cursor.pos - 1))
|
||||||
);
|
);
|
||||||
|
|
||||||
dispatch(tr.removeStoredMark(code_inline));
|
dispatch?.(tr.removeStoredMark(code_inline));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// entering code mark (from right edge): don't move the cursor, just add the mark
|
// entering code mark (from right edge): don't move the cursor, just add the mark
|
||||||
if (!insideCode && enteringCode) {
|
if (!insideCode && enteringCode) {
|
||||||
dispatch(state.tr.addStoredMark(code_inline.create()));
|
dispatch?.(state.tr.addStoredMark(code_inline.create()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +91,7 @@ export default function moveLeft() {
|
|||||||
Selection.near(state.doc.resolve($cursor.pos - 1))
|
Selection.near(state.doc.resolve($cursor.pos - 1))
|
||||||
);
|
);
|
||||||
|
|
||||||
dispatch(tr.addStoredMark(code_inline.create()));
|
dispatch?.(tr.addStoredMark(code_inline.create()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +101,7 @@ export default function moveLeft() {
|
|||||||
insideCode &&
|
insideCode &&
|
||||||
(exitingCode || (!$cursor.nodeBefore && isFirstChild))
|
(exitingCode || (!$cursor.nodeBefore && isFirstChild))
|
||||||
) {
|
) {
|
||||||
dispatch(state.tr.removeStoredMark(code_inline));
|
dispatch?.(state.tr.removeStoredMark(code_inline));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,11 @@ 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, TextSelection } from "prosemirror-state";
|
import { Command, 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(): Command {
|
||||||
return (state: EditorState, dispatch: Dispatch): boolean => {
|
return (state, 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) {
|
||||||
@@ -54,14 +53,14 @@ export default function moveRight() {
|
|||||||
|
|
||||||
// entering code mark (from the left edge): don't move the cursor, just add the mark
|
// entering code mark (from the left edge): don't move the cursor, just add the mark
|
||||||
if (!insideCode && enteringCode) {
|
if (!insideCode && enteringCode) {
|
||||||
dispatch(state.tr.addStoredMark(code_inline.create()));
|
dispatch?.(state.tr.addStoredMark(code_inline.create()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// exiting code mark: don't move the cursor, just remove the mark
|
// exiting code mark: don't move the cursor, just remove the mark
|
||||||
if (insideCode && exitingCode) {
|
if (insideCode && exitingCode) {
|
||||||
dispatch(state.tr.removeStoredMark(code_inline));
|
dispatch?.(state.tr.removeStoredMark(code_inline));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { NodeType } from "prosemirror-model";
|
import { NodeType } from "prosemirror-model";
|
||||||
import { EditorState, TextSelection } from "prosemirror-state";
|
import { Command, TextSelection } from "prosemirror-state";
|
||||||
import { findBlockNodes } from "prosemirror-utils";
|
import { findBlockNodes } from "../queries/findChildren";
|
||||||
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): Command {
|
||||||
return (state: EditorState, dispatch: Dispatch): boolean => {
|
return (state, 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
|
||||||
@@ -33,7 +32,7 @@ export default function splitHeading(type: NodeType) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Move the selection into the new heading node and make sure it's on screen
|
// Move the selection into the new heading node and make sure it's on screen
|
||||||
dispatch(
|
dispatch?.(
|
||||||
transaction
|
transaction
|
||||||
.setSelection(
|
.setSelection(
|
||||||
TextSelection.near(transaction.doc.resolve($from.before()))
|
TextSelection.near(transaction.doc.resolve($from.before()))
|
||||||
@@ -75,7 +74,7 @@ export default function splitHeading(type: NodeType) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Move the selection into the new heading node and make sure it's on screen
|
// Move the selection into the new heading node and make sure it's on screen
|
||||||
dispatch(
|
dispatch?.(
|
||||||
transaction
|
transaction
|
||||||
.setSelection(
|
.setSelection(
|
||||||
TextSelection.near(
|
TextSelection.near(
|
||||||
|
|||||||
152
shared/editor/commands/table.ts
Normal file
152
shared/editor/commands/table.ts
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import { Fragment, Node, NodeType } from "prosemirror-model";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
EditorState,
|
||||||
|
TextSelection,
|
||||||
|
Transaction,
|
||||||
|
} from "prosemirror-state";
|
||||||
|
import {
|
||||||
|
CellSelection,
|
||||||
|
addRow,
|
||||||
|
isInTable,
|
||||||
|
selectedRect,
|
||||||
|
tableNodeTypes,
|
||||||
|
} from "prosemirror-tables";
|
||||||
|
import { getCellsInColumn } from "../queries/table";
|
||||||
|
|
||||||
|
export function createTable(
|
||||||
|
state: EditorState,
|
||||||
|
rowsCount: number,
|
||||||
|
colsCount: number,
|
||||||
|
withHeaderRow = true,
|
||||||
|
cellContent?: Node
|
||||||
|
) {
|
||||||
|
const types = tableNodeTypes(state.schema);
|
||||||
|
const headerCells: Node[] = [];
|
||||||
|
const cells: Node[] = [];
|
||||||
|
const rows: Node[] = [];
|
||||||
|
|
||||||
|
const createCell = (
|
||||||
|
cellType: NodeType,
|
||||||
|
cellContent: Fragment | Node | readonly Node[] | null | undefined
|
||||||
|
) =>
|
||||||
|
cellContent
|
||||||
|
? cellType.createChecked(null, cellContent)
|
||||||
|
: cellType.createAndFill();
|
||||||
|
|
||||||
|
for (let index = 0; index < colsCount; index += 1) {
|
||||||
|
const cell = createCell(types.cell, cellContent);
|
||||||
|
|
||||||
|
if (cell) {
|
||||||
|
cells.push(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (withHeaderRow) {
|
||||||
|
const headerCell = createCell(types.header_cell, cellContent);
|
||||||
|
|
||||||
|
if (headerCell) {
|
||||||
|
headerCells.push(headerCell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let index = 0; index < rowsCount; index += 1) {
|
||||||
|
rows.push(
|
||||||
|
types.row.createChecked(
|
||||||
|
null,
|
||||||
|
withHeaderRow && index === 0 ? headerCells : cells
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.table.createChecked(null, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addRowAfterAndMoveSelection({
|
||||||
|
index,
|
||||||
|
}: {
|
||||||
|
index?: number;
|
||||||
|
} = {}): Command {
|
||||||
|
return (state, dispatch) => {
|
||||||
|
if (!isInTable(state)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dispatch) {
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
const indexAfter = index !== undefined ? index + 1 : rect.bottom;
|
||||||
|
const tr = addRow(state.tr, rect, indexAfter);
|
||||||
|
const cells = getCellsInColumn(0)(state);
|
||||||
|
|
||||||
|
// Special case when adding row to the end of the table as the calculated
|
||||||
|
// rect does not include the row that we just added.
|
||||||
|
if (indexAfter !== rect.map.height) {
|
||||||
|
const pos = cells[Math.min(cells.length - 1, indexAfter)];
|
||||||
|
const $pos = tr.doc.resolve(pos);
|
||||||
|
dispatch(tr.setSelection(TextSelection.near($pos)));
|
||||||
|
} else {
|
||||||
|
const $pos = tr.doc.resolve(rect.tableStart + rect.table.nodeSize);
|
||||||
|
dispatch(tr.setSelection(TextSelection.near($pos)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setColumnAttr({
|
||||||
|
index,
|
||||||
|
alignment,
|
||||||
|
}: {
|
||||||
|
index: number;
|
||||||
|
alignment: string;
|
||||||
|
}): Command {
|
||||||
|
return (state, dispatch) => {
|
||||||
|
if (dispatch) {
|
||||||
|
const cells = getCellsInColumn(index)(state) || [];
|
||||||
|
let transaction = state.tr;
|
||||||
|
cells.forEach((pos) => {
|
||||||
|
transaction = transaction.setNodeMarkup(pos, undefined, {
|
||||||
|
alignment,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
dispatch(transaction);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectRow(index: number, expand = false) {
|
||||||
|
return (state: EditorState): Transaction => {
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
const pos = rect.map.positionAt(index, 0, rect.table);
|
||||||
|
const $pos = state.doc.resolve(rect.tableStart + pos);
|
||||||
|
const rowSelection =
|
||||||
|
expand && state.selection instanceof CellSelection
|
||||||
|
? CellSelection.rowSelection(state.selection.$anchorCell, $pos)
|
||||||
|
: CellSelection.rowSelection($pos);
|
||||||
|
return state.tr.setSelection(rowSelection);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectColumn(index: number, expand = false) {
|
||||||
|
return (state: EditorState): Transaction => {
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
const pos = rect.map.positionAt(0, index, rect.table);
|
||||||
|
const $pos = state.doc.resolve(rect.tableStart + pos);
|
||||||
|
const colSelection =
|
||||||
|
expand && state.selection instanceof CellSelection
|
||||||
|
? CellSelection.colSelection(state.selection.$anchorCell, $pos)
|
||||||
|
: CellSelection.colSelection($pos);
|
||||||
|
return state.tr.setSelection(colSelection);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectTable(state: EditorState): Transaction {
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
const map = rect.map.map;
|
||||||
|
const $anchor = state.doc.resolve(rect.tableStart + map[0]);
|
||||||
|
const $head = state.doc.resolve(rect.tableStart + map[map.length - 1]);
|
||||||
|
const tableSelection = new CellSelection($anchor, $head);
|
||||||
|
return state.tr.setSelection(tableSelection);
|
||||||
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
import { setBlockType } from "prosemirror-commands";
|
import { setBlockType } from "prosemirror-commands";
|
||||||
import { NodeType } from "prosemirror-model";
|
import { NodeType } from "prosemirror-model";
|
||||||
import { EditorState } from "prosemirror-state";
|
import { Command } 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 = {}
|
||||||
) {
|
): Command {
|
||||||
return (state: EditorState, dispatch?: Dispatch) => {
|
return (state, dispatch) => {
|
||||||
const isActive = isNodeActive(type, attrs)(state);
|
const isActive = isNodeActive(type, attrs)(state);
|
||||||
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
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 } from "prosemirror-state";
|
import { Command } from "prosemirror-state";
|
||||||
import { findParentNode } from "prosemirror-utils";
|
|
||||||
import chainTransactions from "../lib/chainTransactions";
|
import chainTransactions from "../lib/chainTransactions";
|
||||||
|
import { findParentNode } from "../queries/findParentNode";
|
||||||
import isList from "../queries/isList";
|
import isList from "../queries/isList";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
import clearNodes from "./clearNodes";
|
import clearNodes from "./clearNodes";
|
||||||
|
|
||||||
export default function toggleList(listType: NodeType, itemType: NodeType) {
|
export default function toggleList(
|
||||||
return (state: EditorState, dispatch?: Dispatch) => {
|
listType: NodeType,
|
||||||
|
itemType: NodeType
|
||||||
|
): Command {
|
||||||
|
return (state, 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);
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { wrapIn, lift } from "prosemirror-commands";
|
import { wrapIn, lift } from "prosemirror-commands";
|
||||||
import { NodeType } from "prosemirror-model";
|
import { NodeType } from "prosemirror-model";
|
||||||
import { EditorState } from "prosemirror-state";
|
import { Command } 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>
|
||||||
) {
|
): Command {
|
||||||
return (state: EditorState, dispatch?: Dispatch) => {
|
return (state, dispatch) => {
|
||||||
const isActive = isNodeActive(type)(state);
|
const isActive = isNodeActive(type)(state);
|
||||||
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
|
|||||||
@@ -1245,7 +1245,7 @@ table {
|
|||||||
* https://github.com/ProseMirror/prosemirror/issues/947 */
|
* https://github.com/ProseMirror/prosemirror/issues/947 */
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: "";
|
||||||
cursor: pointer;
|
cursor: var(--pointer);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -16px;
|
top: -16px;
|
||||||
${props.rtl ? "right" : "left"}: 0;
|
${props.rtl ? "right" : "left"}: 0;
|
||||||
@@ -1273,7 +1273,7 @@ table {
|
|||||||
.grip-row {
|
.grip-row {
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: "";
|
||||||
cursor: pointer;
|
cursor: var(--pointer);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
${props.rtl ? "right" : "left"}: -16px;
|
${props.rtl ? "right" : "left"}: -16px;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -1302,7 +1302,7 @@ table {
|
|||||||
.grip-table {
|
.grip-table {
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: "";
|
||||||
cursor: pointer;
|
cursor: var(--pointer);
|
||||||
background: ${props.theme.tableDivider};
|
background: ${props.theme.tableDivider};
|
||||||
width: 13px;
|
width: 13px;
|
||||||
height: 13px;
|
height: 13px;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { PlusIcon } from "outline-icons";
|
import { PlusIcon } from "outline-icons";
|
||||||
import { Plugin } from "prosemirror-state";
|
import { Plugin } from "prosemirror-state";
|
||||||
import { findParentNode } from "prosemirror-utils";
|
|
||||||
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 { SuggestionsMenuType } from "../plugins/Suggestions";
|
import { SuggestionsMenuType } from "../plugins/Suggestions";
|
||||||
|
import { findParentNode } from "../queries/findParentNode";
|
||||||
import { EventType } from "../types";
|
import { EventType } from "../types";
|
||||||
import Suggestion from "./Suggestion";
|
import Suggestion from "./Suggestion";
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { InputRule } from "prosemirror-inputrules";
|
import { InputRule } from "prosemirror-inputrules";
|
||||||
import { Schema } from "prosemirror-model";
|
import { Schema } from "prosemirror-model";
|
||||||
import { EditorState } from "prosemirror-state";
|
import { Command } from "prosemirror-state";
|
||||||
import {
|
import {
|
||||||
getCurrentDateAsString,
|
getCurrentDateAsString,
|
||||||
getCurrentDateTimeAsString,
|
getCurrentDateTimeAsString,
|
||||||
getCurrentTimeAsString,
|
getCurrentTimeAsString,
|
||||||
} from "../../utils/date";
|
} from "../../utils/date";
|
||||||
import Extension from "../lib/Extension";
|
import Extension from "../lib/Extension";
|
||||||
import { Dispatch, EventType } from "../types";
|
import { EventType } from "../types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An editor extension that adds commands to insert the current date and time.
|
* An editor extension that adds commands to insert the current date and time.
|
||||||
@@ -43,16 +43,16 @@ export default class DateTime extends Extension {
|
|||||||
|
|
||||||
commands(_options: { schema: Schema }) {
|
commands(_options: { schema: Schema }) {
|
||||||
return {
|
return {
|
||||||
date: () => (state: EditorState, dispatch: Dispatch) => {
|
date: (): Command => (state, dispatch) => {
|
||||||
dispatch(state.tr.insertText(getCurrentDateAsString() + " "));
|
dispatch?.(state.tr.insertText(getCurrentDateAsString() + " "));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
time: () => (state: EditorState, dispatch: Dispatch) => {
|
time: (): Command => (state, dispatch) => {
|
||||||
dispatch(state.tr.insertText(getCurrentTimeAsString() + " "));
|
dispatch?.(state.tr.insertText(getCurrentTimeAsString() + " "));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
datetime: () => (state: EditorState, dispatch: Dispatch) => {
|
datetime: (): Command => (state, dispatch) => {
|
||||||
dispatch(state.tr.insertText(getCurrentDateTimeAsString() + " "));
|
dispatch?.(state.tr.insertText(getCurrentDateTimeAsString() + " "));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import {
|
|||||||
AllSelection,
|
AllSelection,
|
||||||
TextSelection,
|
TextSelection,
|
||||||
EditorState,
|
EditorState,
|
||||||
|
Command,
|
||||||
} from "prosemirror-state";
|
} from "prosemirror-state";
|
||||||
import Extension, { Command } from "../lib/Extension";
|
import Extension from "../lib/Extension";
|
||||||
import isInCode from "../queries/isInCode";
|
import isInCode from "../queries/isInCode";
|
||||||
|
|
||||||
export default class Keys extends Extension {
|
export default class Keys extends Extension {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { MathView } from "@benrbray/prosemirror-math";
|
import { MathView } from "@benrbray/prosemirror-math";
|
||||||
import { Node as ProseNode } from "prosemirror-model";
|
|
||||||
import { Plugin, PluginKey, PluginSpec } from "prosemirror-state";
|
import { Plugin, PluginKey, PluginSpec } from "prosemirror-state";
|
||||||
import { EditorView } from "prosemirror-view";
|
import { NodeViewConstructor } from "prosemirror-view";
|
||||||
|
|
||||||
export interface IMathPluginState {
|
export interface IMathPluginState {
|
||||||
macros: { [cmd: string]: string };
|
macros: { [cmd: string]: string };
|
||||||
@@ -11,12 +10,8 @@ export interface IMathPluginState {
|
|||||||
|
|
||||||
const MATH_PLUGIN_KEY = new PluginKey<IMathPluginState>("prosemirror-math");
|
const MATH_PLUGIN_KEY = new PluginKey<IMathPluginState>("prosemirror-math");
|
||||||
|
|
||||||
export function createMathView(displayMode: boolean) {
|
export function createMathView(displayMode: boolean): NodeViewConstructor {
|
||||||
return (
|
return (node, view, getPos) => {
|
||||||
node: ProseNode,
|
|
||||||
view: EditorView,
|
|
||||||
getPos: boolean | (() => number)
|
|
||||||
): MathView => {
|
|
||||||
// dynamically load katex styles and fonts
|
// dynamically load katex styles and fonts
|
||||||
import("katex/dist/katex.min.css");
|
import("katex/dist/katex.min.css");
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Node } from "prosemirror-model";
|
import { Node } from "prosemirror-model";
|
||||||
import { Plugin, PluginKey, Transaction } from "prosemirror-state";
|
import { Plugin, PluginKey, Transaction } from "prosemirror-state";
|
||||||
import { findBlockNodes } from "prosemirror-utils";
|
|
||||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import { findBlockNodes } from "../queries/findChildren";
|
||||||
|
|
||||||
type MermaidState = {
|
type MermaidState = {
|
||||||
decorationSet: DecorationSet;
|
decorationSet: DecorationSet;
|
||||||
@@ -135,7 +135,7 @@ export default function Mermaid({
|
|||||||
return new Plugin({
|
return new Plugin({
|
||||||
key: new PluginKey("mermaid"),
|
key: new PluginKey("mermaid"),
|
||||||
state: {
|
state: {
|
||||||
init: (_: Plugin, { doc }) => {
|
init: (_, { doc }) => {
|
||||||
const pluginState: MermaidState = {
|
const pluginState: MermaidState = {
|
||||||
decorationSet: DecorationSet.create(doc, []),
|
decorationSet: DecorationSet.create(doc, []),
|
||||||
diagramVisibility: {},
|
diagramVisibility: {},
|
||||||
@@ -208,7 +208,7 @@ export default function Mermaid({
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
decorations(state) {
|
decorations(state) {
|
||||||
return this.getState(state).decorationSet;
|
return this.getState(state)?.decorationSet;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -165,6 +165,10 @@ export default class PasteHandler extends Extension {
|
|||||||
const paste = this.editor.pasteParser.parse(
|
const paste = this.editor.pasteParser.parse(
|
||||||
normalizePastedMarkdown(text)
|
normalizePastedMarkdown(text)
|
||||||
);
|
);
|
||||||
|
if (!paste) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const slice = paste.slice(0);
|
const slice = paste.slice(0);
|
||||||
const tr = view.state.tr;
|
const tr = view.state.tr;
|
||||||
let currentPos = view.state.selection.from;
|
let currentPos = view.state.selection.from;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import Extension, { Command } from "../lib/Extension";
|
import { Command } from "prosemirror-state";
|
||||||
|
import Extension from "../lib/Extension";
|
||||||
|
|
||||||
export default class PreventTab extends Extension {
|
export default class PreventTab extends Extension {
|
||||||
get name() {
|
get name() {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { flattenDeep, padStart } from "lodash";
|
import { flattenDeep, padStart } from "lodash";
|
||||||
import { Node } from "prosemirror-model";
|
import { Node } from "prosemirror-model";
|
||||||
import { Plugin, PluginKey, Transaction } from "prosemirror-state";
|
import { Plugin, PluginKey, Transaction } from "prosemirror-state";
|
||||||
import { findBlockNodes } from "prosemirror-utils";
|
|
||||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||||
import refractor from "refractor/core";
|
import refractor from "refractor/core";
|
||||||
|
import { findBlockNodes } from "../queries/findChildren";
|
||||||
|
|
||||||
export const LANGUAGES = {
|
export const LANGUAGES = {
|
||||||
none: "None", // additional entry to disable highlighting
|
none: "None", // additional entry to disable highlighting
|
||||||
@@ -75,18 +75,23 @@ function getDecorations({
|
|||||||
function parseNodes(
|
function parseNodes(
|
||||||
nodes: refractor.RefractorNode[],
|
nodes: refractor.RefractorNode[],
|
||||||
classNames: string[] = []
|
classNames: string[] = []
|
||||||
): any {
|
): {
|
||||||
return nodes.map((node) => {
|
text: string;
|
||||||
if (node.type === "element") {
|
classes: string[];
|
||||||
const classes = [...classNames, ...(node.properties.className || [])];
|
}[] {
|
||||||
return parseNodes(node.children, classes);
|
return flattenDeep(
|
||||||
}
|
nodes.map((node) => {
|
||||||
|
if (node.type === "element") {
|
||||||
|
const classes = [...classNames, ...(node.properties.className || [])];
|
||||||
|
return parseNodes(node.children, classes);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
text: node.value,
|
text: node.value,
|
||||||
classes: classNames,
|
classes: classNames,
|
||||||
};
|
};
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks.forEach((block) => {
|
blocks.forEach((block) => {
|
||||||
@@ -125,7 +130,7 @@ function getDecorations({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const nodes = refractor.highlight(block.node.textContent, language);
|
const nodes = refractor.highlight(block.node.textContent, language);
|
||||||
const newDecorations = flattenDeep(parseNodes(nodes))
|
const newDecorations = parseNodes(nodes)
|
||||||
.map((node: ParsedNode) => {
|
.map((node: ParsedNode) => {
|
||||||
const from = startPos;
|
const from = startPos;
|
||||||
const to = from + node.text.length;
|
const to = from + node.text.length;
|
||||||
@@ -180,7 +185,7 @@ export default function Prism({
|
|||||||
return new Plugin({
|
return new Plugin({
|
||||||
key: new PluginKey("prism"),
|
key: new PluginKey("prism"),
|
||||||
state: {
|
state: {
|
||||||
init: (_: Plugin, { doc }) => DecorationSet.create(doc, []),
|
init: (_, { doc }) => DecorationSet.create(doc, []),
|
||||||
apply: (transaction: Transaction, decorationSet, oldState, state) => {
|
apply: (transaction: Transaction, decorationSet, oldState, state) => {
|
||||||
const nodeName = state.selection.$head.parent.type.name;
|
const nodeName = state.selection.$head.parent.type.name;
|
||||||
const previousNodeName = oldState.selection.$head.parent.type.name;
|
const previousNodeName = oldState.selection.$head.parent.type.name;
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
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 } from "prosemirror-state";
|
import { Command, Plugin } from "prosemirror-state";
|
||||||
import { EditorView } from "prosemirror-view";
|
|
||||||
import { Editor } from "../../../app/editor";
|
import { Editor } from "../../../app/editor";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
|
|
||||||
export type Command = (state: EditorState, dispatch: Dispatch) => boolean;
|
export type CommandFactory = (attrs?: Record<string, any>) => Command;
|
||||||
|
|
||||||
export type CommandFactory = (
|
|
||||||
attrs?: Record<string, any>
|
|
||||||
) => (state: EditorState, dispatch: Dispatch, view: EditorView) => boolean;
|
|
||||||
|
|
||||||
export default class Extension {
|
export default class Extension {
|
||||||
options: any;
|
options: any;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PluginSimple } from "markdown-it";
|
import { PluginSimple } from "markdown-it";
|
||||||
import { keymap } from "prosemirror-keymap";
|
import { keymap } from "prosemirror-keymap";
|
||||||
import { MarkdownParser, TokenConfig } from "prosemirror-markdown";
|
import { MarkdownParser } from "prosemirror-markdown";
|
||||||
import { Schema } from "prosemirror-model";
|
import { Schema } from "prosemirror-model";
|
||||||
import { EditorView } from "prosemirror-view";
|
import { EditorView } from "prosemirror-view";
|
||||||
import { Editor } from "~/editor";
|
import { Editor } from "~/editor";
|
||||||
@@ -110,19 +110,19 @@ export default class ExtensionManager {
|
|||||||
rules?: Record<string, any>;
|
rules?: Record<string, any>;
|
||||||
plugins?: PluginSimple[];
|
plugins?: PluginSimple[];
|
||||||
}): MarkdownParser {
|
}): MarkdownParser {
|
||||||
const tokens: Record<string, TokenConfig> = this.extensions
|
const tokens = this.extensions
|
||||||
.filter(
|
.filter(
|
||||||
(extension) => extension.type === "mark" || extension.type === "node"
|
(extension) => extension.type === "mark" || extension.type === "node"
|
||||||
)
|
)
|
||||||
.reduce((nodes, extension: Node | Mark) => {
|
.reduce((nodes, extension: Node | Mark) => {
|
||||||
const md = extension.parseMarkdown();
|
const parseSpec = extension.parseMarkdown();
|
||||||
if (!md) {
|
if (!parseSpec) {
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...nodes,
|
...nodes,
|
||||||
[extension.markdownToken || extension.name]: md,
|
[extension.markdownToken || extension.name]: parseSpec,
|
||||||
};
|
};
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { EditorState, Transaction } from "prosemirror-state";
|
import { Command, Transaction } from "prosemirror-state";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
|
|
||||||
export default function chainTransactions(
|
export default function chainTransactions(...commands: Command[]): Command {
|
||||||
...commands: ((state: EditorState, dispatch?: Dispatch) => boolean)[]
|
return (state, dispatch): boolean => {
|
||||||
) {
|
|
||||||
return (state: EditorState, dispatch?: Dispatch): boolean => {
|
|
||||||
const dispatcher = (tr: Transaction): void => {
|
const dispatcher = (tr: Transaction): void => {
|
||||||
state = state.apply(tr);
|
state = state.apply(tr);
|
||||||
dispatch?.(tr);
|
dispatch?.(tr);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export function findPlaceholder(
|
|||||||
state: EditorState,
|
state: EditorState,
|
||||||
id: string
|
id: string
|
||||||
): [number, number] | null {
|
): [number, number] | null {
|
||||||
const decos: DecorationSet = uploadPlaceholder.getState(state);
|
const decos = uploadPlaceholder.getState(state);
|
||||||
const found = decos.find(undefined, undefined, (spec) => spec.id === id);
|
const found = decos?.find(undefined, undefined, (spec) => spec.id === id);
|
||||||
return found.length ? [found[0].from, found[0].to] : null;
|
return found?.length ? [found[0].from, found[0].to] : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { toggleMark } from "prosemirror-commands";
|
import { toggleMark } from "prosemirror-commands";
|
||||||
import { MarkSpec, MarkType, Schema } from "prosemirror-model";
|
import { MarkSpec, MarkType, Schema } from "prosemirror-model";
|
||||||
import { EditorState, Plugin } from "prosemirror-state";
|
import { Command, Plugin } from "prosemirror-state";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import collapseSelection from "../commands/collapseSelection";
|
import collapseSelection from "../commands/collapseSelection";
|
||||||
import { Command } from "../lib/Extension";
|
|
||||||
import chainTransactions from "../lib/chainTransactions";
|
import chainTransactions from "../lib/chainTransactions";
|
||||||
import isMarkActive from "../queries/isMarkActive";
|
import isMarkActive from "../queries/isMarkActive";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
import Mark from "./Mark";
|
import Mark from "./Mark";
|
||||||
|
|
||||||
export default class Comment extends Mark {
|
export default class Comment extends Mark {
|
||||||
@@ -32,7 +30,7 @@ export default class Comment extends Mark {
|
|||||||
keys({ type }: { type: MarkType }): Record<string, Command> {
|
keys({ type }: { type: MarkType }): Record<string, Command> {
|
||||||
return this.options.onCreateCommentMark
|
return this.options.onCreateCommentMark
|
||||||
? {
|
? {
|
||||||
"Mod-Alt-m": (state: EditorState, dispatch: Dispatch) => {
|
"Mod-Alt-m": (state, dispatch) => {
|
||||||
if (isMarkActive(state.schema.marks.comment)(state)) {
|
if (isMarkActive(state.schema.marks.comment)(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -53,7 +51,7 @@ export default class Comment extends Mark {
|
|||||||
|
|
||||||
commands({ type }: { type: MarkType; schema: Schema }) {
|
commands({ type }: { type: MarkType; schema: Schema }) {
|
||||||
return this.options.onCreateCommentMark
|
return this.options.onCreateCommentMark
|
||||||
? () => (state: EditorState, dispatch: Dispatch) => {
|
? (): Command => (state, dispatch) => {
|
||||||
if (isMarkActive(state.schema.marks.comment)(state)) {
|
if (isMarkActive(state.schema.marks.comment)(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { toggleMark } from "prosemirror-commands";
|
import { toggleMark } from "prosemirror-commands";
|
||||||
import { InputRule } from "prosemirror-inputrules";
|
import { InputRule } from "prosemirror-inputrules";
|
||||||
import { MarkSpec, MarkType } from "prosemirror-model";
|
import { MarkSpec, MarkType } from "prosemirror-model";
|
||||||
import { Command } from "../lib/Extension";
|
import { Command } from "prosemirror-state";
|
||||||
import markInputRule from "../lib/markInputRule";
|
import markInputRule from "../lib/markInputRule";
|
||||||
import Mark from "./Mark";
|
import Mark from "./Mark";
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
Node,
|
Node,
|
||||||
Mark as ProsemirrorMark,
|
Mark as ProsemirrorMark,
|
||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import { EditorState, Plugin } from "prosemirror-state";
|
import { Command, EditorState, Plugin } from "prosemirror-state";
|
||||||
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
@@ -17,7 +17,7 @@ import { isExternalUrl, sanitizeUrl } from "../../utils/urls";
|
|||||||
import findLinkNodes from "../queries/findLinkNodes";
|
import findLinkNodes from "../queries/findLinkNodes";
|
||||||
import getMarkRange from "../queries/getMarkRange";
|
import getMarkRange from "../queries/getMarkRange";
|
||||||
import isMarkActive from "../queries/isMarkActive";
|
import isMarkActive from "../queries/isMarkActive";
|
||||||
import { EventType, Dispatch } from "../types";
|
import { EventType } from "../types";
|
||||||
import Mark from "./Mark";
|
import Mark from "./Mark";
|
||||||
|
|
||||||
const LINK_INPUT_REGEX = /\[([^[]+)]\((\S+)\)$/;
|
const LINK_INPUT_REGEX = /\[([^[]+)]\((\S+)\)$/;
|
||||||
@@ -113,9 +113,9 @@ export default class Link extends Mark {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
keys({ type }: { type: MarkType }) {
|
keys({ type }: { type: MarkType }): Record<string, Command> {
|
||||||
return {
|
return {
|
||||||
"Mod-k": (state: EditorState, dispatch: Dispatch) => {
|
"Mod-k": (state, dispatch) => {
|
||||||
if (state.selection.empty) {
|
if (state.selection.empty) {
|
||||||
this.editor.events.emit(EventType.LinkToolbarOpen);
|
this.editor.events.emit(EventType.LinkToolbarOpen);
|
||||||
return true;
|
return true;
|
||||||
@@ -123,7 +123,7 @@ export default class Link extends Mark {
|
|||||||
|
|
||||||
return toggleMark(type, { href: "" })(state, dispatch);
|
return toggleMark(type, { href: "" })(state, dispatch);
|
||||||
},
|
},
|
||||||
"Mod-Enter": (state: EditorState) => {
|
"Mod-Enter": (state) => {
|
||||||
if (isMarkActive(type)(state)) {
|
if (isMarkActive(type)(state)) {
|
||||||
const range = getMarkRange(
|
const range = getMarkRange(
|
||||||
state.selection.$from,
|
state.selection.$from,
|
||||||
@@ -303,7 +303,7 @@ export default class Link extends Mark {
|
|||||||
? ">"
|
? ">"
|
||||||
: "](" +
|
: "](" +
|
||||||
state.esc(mark.attrs.href) +
|
state.esc(mark.attrs.href) +
|
||||||
(mark.attrs.title ? " " + state.quote(mark.attrs.title) : "") +
|
(mark.attrs.title ? " " + this.quote(mark.attrs.title) : "") +
|
||||||
")";
|
")";
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -318,4 +318,10 @@ export default class Link extends Mark {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private quote(str: string) {
|
||||||
|
const wrap =
|
||||||
|
str.indexOf('"') === -1 ? '""' : str.indexOf("'") === -1 ? "''" : "()";
|
||||||
|
return wrap[0] + str + wrap[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { toggleMark } from "prosemirror-commands";
|
import { toggleMark } from "prosemirror-commands";
|
||||||
import { InputRule } from "prosemirror-inputrules";
|
import { InputRule } from "prosemirror-inputrules";
|
||||||
import { TokenConfig } from "prosemirror-markdown";
|
import { ParseSpec } from "prosemirror-markdown";
|
||||||
import {
|
import {
|
||||||
MarkSpec,
|
MarkSpec,
|
||||||
MarkType,
|
MarkType,
|
||||||
Node as ProsemirrorNode,
|
Node as ProsemirrorNode,
|
||||||
Schema,
|
Schema,
|
||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import Extension, { Command, CommandFactory } from "../lib/Extension";
|
import { Command } from "prosemirror-state";
|
||||||
|
import Extension, { CommandFactory } from "../lib/Extension";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
|
|
||||||
export default abstract class Mark extends Extension {
|
export default abstract class Mark extends Extension {
|
||||||
@@ -35,7 +36,7 @@ export default abstract class Mark extends Extension {
|
|||||||
throw new Error("toMarkdown not implemented");
|
throw new Error("toMarkdown not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMarkdown(): TokenConfig | void {
|
parseMarkdown(): ParseSpec | void {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
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 } from "prosemirror-state";
|
import { Command } 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 {
|
||||||
@@ -34,17 +33,17 @@ export default class Blockquote extends Node {
|
|||||||
return () => toggleWrap(type);
|
return () => toggleWrap(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
keys({ type }: { type: NodeType }) {
|
keys({ type }: { type: NodeType }): Record<string, Command> {
|
||||||
return {
|
return {
|
||||||
"Ctrl->": toggleWrap(type),
|
"Ctrl->": toggleWrap(type),
|
||||||
"Mod-]": toggleWrap(type),
|
"Mod-]": toggleWrap(type),
|
||||||
"Shift-Enter": (state: EditorState, dispatch: Dispatch) => {
|
"Shift-Enter": (state, dispatch) => {
|
||||||
if (!isNodeActive(type)(state)) {
|
if (!isNodeActive(type)(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tr, selection } = state;
|
const { tr, selection } = state;
|
||||||
dispatch(tr.split(selection.to));
|
dispatch?.(tr.split(selection.to));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
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 } from "prosemirror-state";
|
import { Command } from "prosemirror-state";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { sanitizeUrl } from "../../utils/urls";
|
import { sanitizeUrl } from "../../utils/urls";
|
||||||
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, Dispatch } from "../types";
|
import { ComponentProps } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
const cache = {};
|
const cache = {};
|
||||||
@@ -114,9 +114,9 @@ export default class Embed extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return (attrs: Record<string, any>) =>
|
return (attrs: Record<string, any>): Command =>
|
||||||
(state: EditorState, dispatch: Dispatch) => {
|
(state, dispatch) => {
|
||||||
dispatch(
|
dispatch?.(
|
||||||
state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView()
|
state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView()
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import {
|
|||||||
NodeType,
|
NodeType,
|
||||||
Schema,
|
Schema,
|
||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import { EditorState, TextSelection } from "prosemirror-state";
|
import { Command, TextSelection } from "prosemirror-state";
|
||||||
import Suggestion from "../extensions/Suggestion";
|
import Suggestion from "../extensions/Suggestion";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import { SuggestionsMenuType } from "../plugins/Suggestions";
|
import { SuggestionsMenuType } from "../plugins/Suggestions";
|
||||||
import emojiRule from "../rules/emoji";
|
import emojiRule from "../rules/emoji";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
|
|
||||||
export default class Emoji extends Suggestion {
|
export default class Emoji extends Suggestion {
|
||||||
get type() {
|
get type() {
|
||||||
@@ -78,8 +77,8 @@ export default class Emoji extends Suggestion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commands({ type }: { type: NodeType; schema: Schema }) {
|
commands({ type }: { type: NodeType; schema: Schema }) {
|
||||||
return (attrs: Record<string, string>) =>
|
return (attrs: Record<string, string>): Command =>
|
||||||
(state: EditorState, dispatch: Dispatch) => {
|
(state, dispatch) => {
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
const position =
|
const position =
|
||||||
selection instanceof TextSelection
|
selection instanceof TextSelection
|
||||||
@@ -91,7 +90,7 @@ export default class Emoji extends Suggestion {
|
|||||||
|
|
||||||
const node = type.create(attrs);
|
const node = type.create(attrs);
|
||||||
const transaction = state.tr.insert(position, node);
|
const transaction = state.tr.insert(position, node);
|
||||||
dispatch(transaction);
|
dispatch?.(transaction);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { NodeSpec, NodeType } from "prosemirror-model";
|
import { NodeSpec, NodeType } from "prosemirror-model";
|
||||||
import { EditorState } from "prosemirror-state";
|
import { Command } 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 isNodeActive from "../queries/isNodeActive";
|
import isNodeActive from "../queries/isNodeActive";
|
||||||
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 {
|
||||||
@@ -28,22 +27,24 @@ export default class HardBreak extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return () => (state: EditorState, dispatch: Dispatch) => {
|
return (): Command => (state, dispatch) => {
|
||||||
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
dispatch?.(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
keys({ type }: { type: NodeType }) {
|
keys({ type }: { type: NodeType }): Record<string, Command> {
|
||||||
return {
|
return {
|
||||||
"Shift-Enter": (state: EditorState, dispatch: Dispatch) => {
|
"Shift-Enter": (state, dispatch) => {
|
||||||
if (
|
if (
|
||||||
!isInTable(state) &&
|
!isInTable(state) &&
|
||||||
!isNodeActive(state.schema.nodes.paragraph)(state)
|
!isNodeActive(state.schema.nodes.paragraph)(state)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
dispatch?.(
|
||||||
|
state.tr.replaceSelectionWith(type.create()).scrollIntoView()
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ import {
|
|||||||
NodeType,
|
NodeType,
|
||||||
Schema,
|
Schema,
|
||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import { Plugin, Selection } from "prosemirror-state";
|
import { Command, Plugin, Selection } from "prosemirror-state";
|
||||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||||
import Storage from "../../utils/Storage";
|
import Storage from "../../utils/Storage";
|
||||||
import backspaceToParagraph from "../commands/backspaceToParagraph";
|
import backspaceToParagraph from "../commands/backspaceToParagraph";
|
||||||
import splitHeading from "../commands/splitHeading";
|
import splitHeading from "../commands/splitHeading";
|
||||||
import toggleBlockType from "../commands/toggleBlockType";
|
import toggleBlockType from "../commands/toggleBlockType";
|
||||||
import { Command } from "../lib/Extension";
|
|
||||||
import headingToSlug, { headingToPersistenceKey } from "../lib/headingToSlug";
|
import headingToSlug, { headingToPersistenceKey } from "../lib/headingToSlug";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import { FoldingHeadersPlugin } from "../plugins/FoldingHeaders";
|
import { FoldingHeadersPlugin } from "../plugins/FoldingHeaders";
|
||||||
@@ -119,7 +118,10 @@ export default class Heading extends Node {
|
|||||||
|
|
||||||
handleFoldContent = (event: MouseEvent) => {
|
handleFoldContent = (event: MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!(event.currentTarget instanceof HTMLButtonElement)) {
|
if (
|
||||||
|
!(event.currentTarget instanceof HTMLButtonElement) ||
|
||||||
|
event.button !== 0
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
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 } from "prosemirror-state";
|
import { Command } 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 {
|
||||||
@@ -28,19 +27,21 @@ export default class HorizontalRule extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return (attrs: Record<string, any>) =>
|
return (attrs: Record<string, any>): Command =>
|
||||||
(state: EditorState, dispatch: Dispatch) => {
|
(state, dispatch) => {
|
||||||
dispatch(
|
dispatch?.(
|
||||||
state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView()
|
state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView()
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
keys({ type }: { type: NodeType }) {
|
keys({ type }: { type: NodeType }): Record<string, Command> {
|
||||||
return {
|
return {
|
||||||
"Mod-_": (state: EditorState, dispatch: Dispatch) => {
|
"Mod-_": (state, dispatch) => {
|
||||||
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
dispatch?.(
|
||||||
|
state.tr.replaceSelectionWith(type.create()).scrollIntoView()
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
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 { Node as ProsemirrorNode, NodeSpec, NodeType } from "prosemirror-model";
|
import { Node as ProsemirrorNode, NodeSpec, NodeType } from "prosemirror-model";
|
||||||
import { NodeSelection, EditorState, Plugin } from "prosemirror-state";
|
import { NodeSelection, Plugin, Command } from "prosemirror-state";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { sanitizeUrl } from "../../utils/urls";
|
import { sanitizeUrl } from "../../utils/urls";
|
||||||
import { default as ImageComponent, Caption } from "../components/Image";
|
import { default as ImageComponent, Caption } from "../components/Image";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import { ComponentProps, Dispatch } from "../types";
|
import { ComponentProps } from "../types";
|
||||||
import SimpleImage from "./SimpleImage";
|
import SimpleImage from "./SimpleImage";
|
||||||
|
|
||||||
const imageSizeRegex = /\s=(\d+)?x(\d+)?$/;
|
const imageSizeRegex = /\s=(\d+)?x(\d+)?$/;
|
||||||
@@ -282,7 +282,7 @@ export default class Image extends SimpleImage {
|
|||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return {
|
return {
|
||||||
...super.commands({ type }),
|
...super.commands({ type }),
|
||||||
downloadImage: () => (state: EditorState) => {
|
downloadImage: (): Command => (state) => {
|
||||||
if (!(state.selection instanceof NodeSelection)) {
|
if (!(state.selection instanceof NodeSelection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -296,7 +296,7 @@ export default class Image extends SimpleImage {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
alignRight: () => (state: EditorState, dispatch: Dispatch) => {
|
alignRight: (): Command => (state, dispatch) => {
|
||||||
if (!(state.selection instanceof NodeSelection)) {
|
if (!(state.selection instanceof NodeSelection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -306,10 +306,10 @@ export default class Image extends SimpleImage {
|
|||||||
layoutClass: "right-50",
|
layoutClass: "right-50",
|
||||||
};
|
};
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
dispatch(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
dispatch?.(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
alignLeft: () => (state: EditorState, dispatch: Dispatch) => {
|
alignLeft: (): Command => (state, dispatch) => {
|
||||||
if (!(state.selection instanceof NodeSelection)) {
|
if (!(state.selection instanceof NodeSelection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -319,10 +319,10 @@ export default class Image extends SimpleImage {
|
|||||||
layoutClass: "left-50",
|
layoutClass: "left-50",
|
||||||
};
|
};
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
dispatch(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
dispatch?.(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
alignFullWidth: () => (state: EditorState, dispatch: Dispatch) => {
|
alignFullWidth: (): Command => (state, dispatch) => {
|
||||||
if (!(state.selection instanceof NodeSelection)) {
|
if (!(state.selection instanceof NodeSelection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -332,16 +332,16 @@ export default class Image extends SimpleImage {
|
|||||||
layoutClass: "full-width",
|
layoutClass: "full-width",
|
||||||
};
|
};
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
dispatch(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
dispatch?.(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
alignCenter: () => (state: EditorState, dispatch: Dispatch) => {
|
alignCenter: (): Command => (state, dispatch) => {
|
||||||
if (!(state.selection instanceof NodeSelection)) {
|
if (!(state.selection instanceof NodeSelection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const attrs = { ...state.selection.node.attrs, layoutClass: null };
|
const attrs = { ...state.selection.node.attrs, layoutClass: null };
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
dispatch(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
dispatch?.(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import {
|
|||||||
EditorState,
|
EditorState,
|
||||||
Plugin,
|
Plugin,
|
||||||
TextSelection,
|
TextSelection,
|
||||||
|
Command,
|
||||||
} from "prosemirror-state";
|
} from "prosemirror-state";
|
||||||
import { findParentNodeClosestToPos } from "prosemirror-utils";
|
|
||||||
import { DecorationSet, Decoration } from "prosemirror-view";
|
import { DecorationSet, Decoration } from "prosemirror-view";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
|
import { findParentNodeClosestToPos } from "../queries/findParentNode";
|
||||||
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 {
|
||||||
@@ -200,14 +200,14 @@ export default class ListItem extends Node {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
keys({ type }: { type: NodeType }) {
|
keys({ type }: { type: NodeType }): Record<string, Command> {
|
||||||
return {
|
return {
|
||||||
Enter: splitListItem(type),
|
Enter: splitListItem(type),
|
||||||
Tab: sinkListItem(type),
|
Tab: sinkListItem(type),
|
||||||
"Shift-Tab": liftListItem(type),
|
"Shift-Tab": liftListItem(type),
|
||||||
"Mod-]": sinkListItem(type),
|
"Mod-]": sinkListItem(type),
|
||||||
"Mod-[": liftListItem(type),
|
"Mod-[": liftListItem(type),
|
||||||
"Shift-Enter": (state: EditorState, dispatch: Dispatch) => {
|
"Shift-Enter": (state, dispatch) => {
|
||||||
if (!isInList(state)) {
|
if (!isInList(state)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -216,10 +216,10 @@ export default class ListItem extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { tr, selection } = state;
|
const { tr, selection } = state;
|
||||||
dispatch(tr.split(selection.to));
|
dispatch?.(tr.split(selection.to));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
"Alt-ArrowUp": (state: EditorState, dispatch: Dispatch) => {
|
"Alt-ArrowUp": (state, dispatch) => {
|
||||||
if (!state.selection.empty) {
|
if (!state.selection.empty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -241,7 +241,7 @@ export default class ListItem extends Node {
|
|||||||
const { tr } = state;
|
const { tr } = state;
|
||||||
const newPos = pos - $pos.nodeBefore.nodeSize;
|
const newPos = pos - $pos.nodeBefore.nodeSize;
|
||||||
|
|
||||||
dispatch(
|
dispatch?.(
|
||||||
tr
|
tr
|
||||||
.delete(pos, pos + li.nodeSize)
|
.delete(pos, pos + li.nodeSize)
|
||||||
.insert(newPos, li)
|
.insert(newPos, li)
|
||||||
@@ -249,7 +249,7 @@ export default class ListItem extends Node {
|
|||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
"Alt-ArrowDown": (state: EditorState, dispatch: Dispatch) => {
|
"Alt-ArrowDown": (state, dispatch) => {
|
||||||
if (!state.selection.empty) {
|
if (!state.selection.empty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -271,7 +271,7 @@ export default class ListItem extends Node {
|
|||||||
const { tr } = state;
|
const { tr } = state;
|
||||||
const newPos = pos + li.nodeSize + $pos.nodeAfter.nodeSize;
|
const newPos = pos + li.nodeSize + $pos.nodeAfter.nodeSize;
|
||||||
|
|
||||||
dispatch(
|
dispatch?.(
|
||||||
tr
|
tr
|
||||||
.insert(newPos, li)
|
.insert(newPos, li)
|
||||||
.setSelection(TextSelection.near(tr.doc.resolve(newPos)))
|
.setSelection(TextSelection.near(tr.doc.resolve(newPos)))
|
||||||
|
|||||||
@@ -17,11 +17,10 @@ import {
|
|||||||
Schema,
|
Schema,
|
||||||
Node as ProsemirrorNode,
|
Node as ProsemirrorNode,
|
||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import { EditorState, Plugin } from "prosemirror-state";
|
import { Command, Plugin } from "prosemirror-state";
|
||||||
import MathPlugin from "../extensions/Math";
|
import MathPlugin from "../extensions/Math";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import mathRule, { REGEX_INLINE_MATH_DOLLARS } from "../rules/math";
|
import mathRule, { REGEX_INLINE_MATH_DOLLARS } from "../rules/math";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class Math extends Node {
|
export default class Math extends Node {
|
||||||
@@ -34,8 +33,8 @@ export default class Math extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return () => (state: EditorState, dispatch: Dispatch) => {
|
return (): Command => (state, dispatch) => {
|
||||||
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
dispatch?.(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import {
|
|||||||
} from "@benrbray/prosemirror-math";
|
} from "@benrbray/prosemirror-math";
|
||||||
import { PluginSimple } from "markdown-it";
|
import { PluginSimple } from "markdown-it";
|
||||||
import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
|
import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
|
||||||
import { EditorState } from "prosemirror-state";
|
import { Command } from "prosemirror-state";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import mathRule, { REGEX_BLOCK_MATH_DOLLARS } from "../rules/math";
|
import mathRule, { REGEX_BLOCK_MATH_DOLLARS } from "../rules/math";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class MathBlock extends Node {
|
export default class MathBlock extends Node {
|
||||||
@@ -24,8 +23,8 @@ export default class MathBlock extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return () => (state: EditorState, dispatch: Dispatch) => {
|
return (): Command => (state, dispatch) => {
|
||||||
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
dispatch?.(state.tr.replaceSelectionWith(type.create()).scrollIntoView());
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ import {
|
|||||||
NodeType,
|
NodeType,
|
||||||
Schema,
|
Schema,
|
||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import { EditorState, TextSelection } from "prosemirror-state";
|
import { Command, TextSelection } from "prosemirror-state";
|
||||||
import Suggestion from "../extensions/Suggestion";
|
import Suggestion from "../extensions/Suggestion";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import { SuggestionsMenuType } from "../plugins/Suggestions";
|
import { SuggestionsMenuType } from "../plugins/Suggestions";
|
||||||
import mentionRule from "../rules/mention";
|
import mentionRule from "../rules/mention";
|
||||||
import { Dispatch } from "../types";
|
|
||||||
|
|
||||||
export default class Mention extends Suggestion {
|
export default class Mention extends Suggestion {
|
||||||
get type() {
|
get type() {
|
||||||
@@ -80,8 +79,8 @@ export default class Mention extends Suggestion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commands({ type }: { type: NodeType; schema: Schema }) {
|
commands({ type }: { type: NodeType; schema: Schema }) {
|
||||||
return (attrs: Record<string, string>) =>
|
return (attrs: Record<string, string>): Command =>
|
||||||
(state: EditorState, dispatch: Dispatch) => {
|
(state, dispatch) => {
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
const position =
|
const position =
|
||||||
selection instanceof TextSelection
|
selection instanceof TextSelection
|
||||||
@@ -93,7 +92,7 @@ export default class Mention extends Suggestion {
|
|||||||
|
|
||||||
const node = type.create(attrs);
|
const node = type.create(attrs);
|
||||||
const transaction = state.tr.insert(position, node);
|
const transaction = state.tr.insert(position, node);
|
||||||
dispatch(transaction);
|
dispatch?.(transaction);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { InputRule } from "prosemirror-inputrules";
|
import { InputRule } from "prosemirror-inputrules";
|
||||||
import { TokenConfig } from "prosemirror-markdown";
|
import { ParseSpec } from "prosemirror-markdown";
|
||||||
import {
|
import {
|
||||||
NodeSpec,
|
NodeSpec,
|
||||||
Node as ProsemirrorNode,
|
Node as ProsemirrorNode,
|
||||||
NodeType,
|
NodeType,
|
||||||
Schema,
|
Schema,
|
||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import Extension, { Command, CommandFactory } from "../lib/Extension";
|
import { Command } from "prosemirror-state";
|
||||||
|
import Extension, { CommandFactory } from "../lib/Extension";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
|
|
||||||
export default abstract class Node extends Extension {
|
export default abstract class Node extends Extension {
|
||||||
@@ -41,7 +42,7 @@ export default abstract class Node extends Extension {
|
|||||||
throw new Error("toMarkdown not implemented");
|
throw new Error("toMarkdown not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMarkdown(): TokenConfig | void {
|
parseMarkdown(): ParseSpec | void {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { setBlockType } from "prosemirror-commands";
|
import { setBlockType } from "prosemirror-commands";
|
||||||
import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
|
import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
|
||||||
|
import deleteEmptyFirstParagraph from "../commands/deleteEmptyFirstParagraph";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ export default class Paragraph extends Node {
|
|||||||
keys({ type }: { type: NodeType }) {
|
keys({ type }: { type: NodeType }) {
|
||||||
return {
|
return {
|
||||||
"Shift-Ctrl-0": setBlockType(type),
|
"Shift-Ctrl-0": setBlockType(type),
|
||||||
|
Backspace: deleteEmptyFirstParagraph,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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 { Node as ProsemirrorNode, NodeSpec, NodeType } from "prosemirror-model";
|
import { Node as ProsemirrorNode, NodeSpec, NodeType } from "prosemirror-model";
|
||||||
import { TextSelection, NodeSelection, EditorState } from "prosemirror-state";
|
import { TextSelection, NodeSelection, Command } from "prosemirror-state";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { getEventFiles } from "../../utils/files";
|
import { getEventFiles } from "../../utils/files";
|
||||||
import { sanitizeUrl } from "../../utils/urls";
|
import { sanitizeUrl } from "../../utils/urls";
|
||||||
@@ -11,7 +11,7 @@ import { default as ImageComponent } from "../components/Image";
|
|||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import uploadPlaceholderPlugin from "../lib/uploadPlaceholder";
|
import uploadPlaceholderPlugin from "../lib/uploadPlaceholder";
|
||||||
import uploadPlugin from "../lib/uploadPlugin";
|
import uploadPlugin from "../lib/uploadPlugin";
|
||||||
import { ComponentProps, Dispatch } from "../types";
|
import { ComponentProps } from "../types";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class SimpleImage extends Node {
|
export default class SimpleImage extends Node {
|
||||||
@@ -171,11 +171,11 @@ export default class SimpleImage extends Node {
|
|||||||
|
|
||||||
commands({ type }: { type: NodeType }) {
|
commands({ type }: { type: NodeType }) {
|
||||||
return {
|
return {
|
||||||
deleteImage: () => (state: EditorState, dispatch: Dispatch) => {
|
deleteImage: (): Command => (state, dispatch) => {
|
||||||
dispatch(state.tr.deleteSelection());
|
dispatch?.(state.tr.deleteSelection());
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
replaceImage: () => (state: EditorState) => {
|
replaceImage: (): Command => (state) => {
|
||||||
if (!(state.selection instanceof NodeSelection)) {
|
if (!(state.selection instanceof NodeSelection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -214,8 +214,8 @@ export default class SimpleImage extends Node {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
createImage:
|
createImage:
|
||||||
(attrs: Record<string, any>) =>
|
(attrs: Record<string, any>): Command =>
|
||||||
(state: EditorState, dispatch: Dispatch) => {
|
(state, dispatch) => {
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
const position =
|
const position =
|
||||||
selection instanceof TextSelection
|
selection instanceof TextSelection
|
||||||
@@ -227,7 +227,7 @@ export default class SimpleImage extends Node {
|
|||||||
|
|
||||||
const node = type.create(attrs);
|
const node = type.create(attrs);
|
||||||
const transaction = state.tr.insert(position, node);
|
const transaction = state.tr.insert(position, node);
|
||||||
dispatch(transaction);
|
dispatch?.(transaction);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { NodeSpec, Node as ProsemirrorNode, Schema } from "prosemirror-model";
|
import { chainCommands } from "prosemirror-commands";
|
||||||
import { EditorState, Plugin, TextSelection } from "prosemirror-state";
|
import { NodeSpec, Node as ProsemirrorNode } from "prosemirror-model";
|
||||||
|
import { Command, Plugin, TextSelection } from "prosemirror-state";
|
||||||
import {
|
import {
|
||||||
addColumnAfter,
|
addColumnAfter,
|
||||||
addColumnBefore,
|
addColumnBefore,
|
||||||
@@ -7,25 +8,19 @@ import {
|
|||||||
deleteRow,
|
deleteRow,
|
||||||
deleteTable,
|
deleteTable,
|
||||||
goToNextCell,
|
goToNextCell,
|
||||||
isInTable,
|
|
||||||
tableEditing,
|
tableEditing,
|
||||||
toggleHeaderCell,
|
toggleHeaderCell,
|
||||||
toggleHeaderColumn,
|
toggleHeaderColumn,
|
||||||
toggleHeaderRow,
|
toggleHeaderRow,
|
||||||
CellSelection,
|
|
||||||
} from "prosemirror-tables";
|
} from "prosemirror-tables";
|
||||||
import {
|
|
||||||
addRowAt,
|
|
||||||
createTable,
|
|
||||||
getCellsInColumn,
|
|
||||||
moveRow,
|
|
||||||
setTextSelection,
|
|
||||||
} from "prosemirror-utils";
|
|
||||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||||
|
import {
|
||||||
|
addRowAfterAndMoveSelection,
|
||||||
|
setColumnAttr,
|
||||||
|
createTable,
|
||||||
|
} from "../commands/table";
|
||||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||||
import { getRowIndexFromText } from "../queries/getRowIndex";
|
|
||||||
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 {
|
||||||
@@ -58,49 +53,32 @@ export default class Table extends Node {
|
|||||||
return [tablesRule];
|
return [tablesRule];
|
||||||
}
|
}
|
||||||
|
|
||||||
commands({ schema }: { schema: Schema }) {
|
commands() {
|
||||||
return {
|
return {
|
||||||
createTable:
|
createTable:
|
||||||
({ rowsCount, colsCount }: { rowsCount: number; colsCount: number }) =>
|
({
|
||||||
(state: EditorState, dispatch: Dispatch) => {
|
rowsCount,
|
||||||
const offset = state.tr.selection.anchor + 1;
|
colsCount,
|
||||||
const nodes = createTable(schema, rowsCount, colsCount);
|
}: {
|
||||||
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView();
|
rowsCount: number;
|
||||||
const resolvedPos = tr.doc.resolve(offset);
|
colsCount: number;
|
||||||
|
}): Command =>
|
||||||
tr.setSelection(TextSelection.near(resolvedPos));
|
(state, dispatch) => {
|
||||||
dispatch(tr);
|
if (dispatch) {
|
||||||
return true;
|
const offset = state.tr.selection.anchor + 1;
|
||||||
},
|
const nodes = createTable(state, rowsCount, colsCount);
|
||||||
setColumnAttr:
|
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView();
|
||||||
({ index, alignment }: { index: number; alignment: string }) =>
|
const resolvedPos = tr.doc.resolve(offset);
|
||||||
(state: EditorState, dispatch: Dispatch) => {
|
tr.setSelection(TextSelection.near(resolvedPos));
|
||||||
const cells = getCellsInColumn(index)(state.selection) || [];
|
dispatch(tr);
|
||||||
let transaction = state.tr;
|
|
||||||
cells.forEach(({ pos }) => {
|
|
||||||
transaction = transaction.setNodeMarkup(pos, undefined, {
|
|
||||||
alignment,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
dispatch(transaction);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
addColumnBefore: () => addColumnBefore,
|
|
||||||
addColumnAfter: () => addColumnAfter,
|
|
||||||
deleteColumn: () => deleteColumn,
|
|
||||||
addRowAfter:
|
|
||||||
({ index }: { index: number }) =>
|
|
||||||
(state: EditorState, dispatch: Dispatch) => {
|
|
||||||
if (index === 0) {
|
|
||||||
// A little hack to avoid cloning the heading row by cloning the row
|
|
||||||
// beneath and then moving it to the right index.
|
|
||||||
const tr = addRowAt(index + 2, true)(state.tr);
|
|
||||||
dispatch(moveRow(index + 2, index + 1)(tr));
|
|
||||||
} else {
|
|
||||||
dispatch(addRowAt(index + 1, true)(state.tr));
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
setColumnAttr,
|
||||||
|
addColumnBefore: () => addColumnBefore,
|
||||||
|
addColumnAfter: () => addColumnAfter,
|
||||||
|
deleteColumn: () => deleteColumn,
|
||||||
|
addRowAfter: addRowAfterAndMoveSelection,
|
||||||
deleteRow: () => deleteRow,
|
deleteRow: () => deleteRow,
|
||||||
deleteTable: () => deleteTable,
|
deleteTable: () => deleteTable,
|
||||||
toggleHeaderColumn: () => toggleHeaderColumn,
|
toggleHeaderColumn: () => toggleHeaderColumn,
|
||||||
@@ -111,31 +89,9 @@ export default class Table extends Node {
|
|||||||
|
|
||||||
keys() {
|
keys() {
|
||||||
return {
|
return {
|
||||||
Tab: goToNextCell(1),
|
Tab: chainCommands(goToNextCell(1), addRowAfterAndMoveSelection()),
|
||||||
"Shift-Tab": goToNextCell(-1),
|
"Shift-Tab": goToNextCell(-1),
|
||||||
Enter: (state: EditorState, dispatch: Dispatch) => {
|
Enter: addRowAfterAndMoveSelection(),
|
||||||
if (!isInTable(state)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const index = getRowIndexFromText(
|
|
||||||
state.selection as unknown as CellSelection
|
|
||||||
);
|
|
||||||
|
|
||||||
if (index === 0) {
|
|
||||||
const cells = getCellsInColumn(0)(state.selection);
|
|
||||||
if (!cells) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tr = addRowAt(index + 2, true)(state.tr);
|
|
||||||
dispatch(
|
|
||||||
setTextSelection(cells[1].pos)(moveRow(index + 2, index + 1)(tr))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
dispatch(addRowAt(index + 1, true)(state.tr));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import Token from "markdown-it/lib/token";
|
import Token from "markdown-it/lib/token";
|
||||||
import { NodeSpec } from "prosemirror-model";
|
import { NodeSpec } from "prosemirror-model";
|
||||||
import { Plugin } from "prosemirror-state";
|
import { Plugin } from "prosemirror-state";
|
||||||
import {
|
|
||||||
isTableSelected,
|
|
||||||
isRowSelected,
|
|
||||||
getCellsInColumn,
|
|
||||||
selectRow,
|
|
||||||
selectTable,
|
|
||||||
} from "prosemirror-utils";
|
|
||||||
import { DecorationSet, Decoration } from "prosemirror-view";
|
import { DecorationSet, Decoration } from "prosemirror-view";
|
||||||
|
import { selectRow, selectTable } from "../commands/table";
|
||||||
|
import {
|
||||||
|
getCellsInColumn,
|
||||||
|
isRowSelected,
|
||||||
|
isTableSelected,
|
||||||
|
} from "../queries/table";
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class TableCell extends Node {
|
export default class TableCell extends Node {
|
||||||
@@ -55,17 +54,17 @@ export default class TableCell extends Node {
|
|||||||
new Plugin({
|
new Plugin({
|
||||||
props: {
|
props: {
|
||||||
decorations: (state) => {
|
decorations: (state) => {
|
||||||
const { doc, selection } = state;
|
const { doc } = state;
|
||||||
const decorations: Decoration[] = [];
|
const decorations: Decoration[] = [];
|
||||||
const cells = getCellsInColumn(0)(selection);
|
const cells = getCellsInColumn(0)(state);
|
||||||
|
|
||||||
if (cells) {
|
if (cells) {
|
||||||
cells.forEach(({ pos }, index) => {
|
cells.forEach((pos, index) => {
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
decorations.push(
|
decorations.push(
|
||||||
Decoration.widget(pos + 1, () => {
|
Decoration.widget(pos + 1, () => {
|
||||||
let className = "grip-table";
|
let className = "grip-table";
|
||||||
const selected = isTableSelected(selection);
|
const selected = isTableSelected(state);
|
||||||
if (selected) {
|
if (selected) {
|
||||||
className += " selected";
|
className += " selected";
|
||||||
}
|
}
|
||||||
@@ -74,7 +73,7 @@ export default class TableCell extends Node {
|
|||||||
grip.addEventListener("mousedown", (event) => {
|
grip.addEventListener("mousedown", (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
this.editor.view.dispatch(selectTable(state.tr));
|
this.editor.view.dispatch(selectTable(state));
|
||||||
});
|
});
|
||||||
return grip;
|
return grip;
|
||||||
})
|
})
|
||||||
@@ -82,7 +81,7 @@ export default class TableCell extends Node {
|
|||||||
}
|
}
|
||||||
decorations.push(
|
decorations.push(
|
||||||
Decoration.widget(pos + 1, () => {
|
Decoration.widget(pos + 1, () => {
|
||||||
const rowSelected = isRowSelected(index)(selection);
|
const rowSelected = isRowSelected(index)(state);
|
||||||
|
|
||||||
let className = "grip-row";
|
let className = "grip-row";
|
||||||
if (rowSelected) {
|
if (rowSelected) {
|
||||||
@@ -100,10 +99,7 @@ export default class TableCell extends Node {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
this.editor.view.dispatch(
|
this.editor.view.dispatch(
|
||||||
selectRow(
|
selectRow(index, event.metaKey || event.shiftKey)(state)
|
||||||
index,
|
|
||||||
event.metaKey || event.shiftKey
|
|
||||||
)(state.tr)
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return grip;
|
return grip;
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import Token from "markdown-it/lib/token";
|
import Token from "markdown-it/lib/token";
|
||||||
import { NodeSpec } from "prosemirror-model";
|
import { NodeSpec } from "prosemirror-model";
|
||||||
import { Plugin } from "prosemirror-state";
|
import { Plugin } from "prosemirror-state";
|
||||||
import {
|
|
||||||
isColumnSelected,
|
|
||||||
getCellsInRow,
|
|
||||||
selectColumn,
|
|
||||||
} from "prosemirror-utils";
|
|
||||||
import { DecorationSet, Decoration } from "prosemirror-view";
|
import { DecorationSet, Decoration } from "prosemirror-view";
|
||||||
|
import { selectColumn } from "../commands/table";
|
||||||
|
import { getCellsInRow, isColumnSelected } from "../queries/table";
|
||||||
|
|
||||||
import Node from "./Node";
|
import Node from "./Node";
|
||||||
|
|
||||||
export default class TableHeadCell extends Node {
|
export default class TableHeadCell extends Node {
|
||||||
@@ -53,15 +51,15 @@ export default class TableHeadCell extends Node {
|
|||||||
new Plugin({
|
new Plugin({
|
||||||
props: {
|
props: {
|
||||||
decorations: (state) => {
|
decorations: (state) => {
|
||||||
const { doc, selection } = state;
|
const { doc } = state;
|
||||||
const decorations: Decoration[] = [];
|
const decorations: Decoration[] = [];
|
||||||
const cells = getCellsInRow(0)(selection);
|
const cells = getCellsInRow(0)(state);
|
||||||
|
|
||||||
if (cells) {
|
if (cells) {
|
||||||
cells.forEach(({ pos }, index) => {
|
cells.forEach((pos, index) => {
|
||||||
decorations.push(
|
decorations.push(
|
||||||
Decoration.widget(pos + 1, () => {
|
Decoration.widget(pos + 1, () => {
|
||||||
const colSelected = isColumnSelected(index)(selection);
|
const colSelected = isColumnSelected(index)(state);
|
||||||
let className = "grip-column";
|
let className = "grip-column";
|
||||||
if (colSelected) {
|
if (colSelected) {
|
||||||
className += " selected";
|
className += " selected";
|
||||||
@@ -80,7 +78,7 @@ export default class TableHeadCell extends Node {
|
|||||||
selectColumn(
|
selectColumn(
|
||||||
index,
|
index,
|
||||||
event.metaKey || event.shiftKey
|
event.metaKey || event.shiftKey
|
||||||
)(state.tr)
|
)(state)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return grip;
|
return grip;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Plugin, PluginKey } from "prosemirror-state";
|
import { Plugin, PluginKey } from "prosemirror-state";
|
||||||
import { findBlockNodes } from "prosemirror-utils";
|
|
||||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||||
import Storage from "../../utils/Storage";
|
import Storage from "../../utils/Storage";
|
||||||
import { headingToPersistenceKey } from "../lib/headingToSlug";
|
import { headingToPersistenceKey } from "../lib/headingToSlug";
|
||||||
|
import { findBlockNodes } from "../queries/findChildren";
|
||||||
import findCollapsedNodes from "../queries/findCollapsedNodes";
|
import findCollapsedNodes from "../queries/findCollapsedNodes";
|
||||||
|
|
||||||
export class FoldingHeadersPlugin extends Plugin {
|
export class FoldingHeadersPlugin extends Plugin {
|
||||||
|
|||||||
57
shared/editor/queries/findChildren.ts
Normal file
57
shared/editor/queries/findChildren.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { Node } from "prosemirror-model";
|
||||||
|
|
||||||
|
type Predicate = (node: Node) => boolean;
|
||||||
|
|
||||||
|
export type NodeWithPos = {
|
||||||
|
pos: number;
|
||||||
|
node: Node;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function flatten(node: Node, descend = true): NodeWithPos[] {
|
||||||
|
if (!node) {
|
||||||
|
throw new Error('Invalid "node" parameter');
|
||||||
|
}
|
||||||
|
const result: NodeWithPos[] = [];
|
||||||
|
node.descendants((child, pos) => {
|
||||||
|
result.push({ node: child, pos });
|
||||||
|
if (!descend) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over descendants of a given `node`, returning child nodes predicate
|
||||||
|
* returns truthy for. It doesn't descend into a node when descend argument is
|
||||||
|
* `false` (defaults to `true`).
|
||||||
|
*
|
||||||
|
* @param node The node to iterate over
|
||||||
|
* @param predicate Filtering predicate function
|
||||||
|
* @param descend Whether to descend into a node
|
||||||
|
* @returns Child nodes
|
||||||
|
*/
|
||||||
|
export function findChildren(
|
||||||
|
node: Node,
|
||||||
|
predicate: Predicate,
|
||||||
|
descend = false
|
||||||
|
) {
|
||||||
|
if (!node) {
|
||||||
|
throw new Error('Invalid "node" parameter');
|
||||||
|
} else if (!predicate) {
|
||||||
|
throw new Error('Invalid "predicate" parameter');
|
||||||
|
}
|
||||||
|
return flatten(node, descend).filter((child) => predicate(child.node));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over descendants of a given `node`, returning child nodes that
|
||||||
|
* are blocks.
|
||||||
|
*
|
||||||
|
* @param node The node to iterate over
|
||||||
|
* @returns Child nodes that are blocks
|
||||||
|
*/
|
||||||
|
export function findBlockNodes(node: Node): NodeWithPos[] {
|
||||||
|
return findChildren(node, (child) => child.isBlock);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Node } from "prosemirror-model";
|
import { Node } from "prosemirror-model";
|
||||||
import { findBlockNodes, NodeWithPos } from "prosemirror-utils";
|
import { findBlockNodes, NodeWithPos } from "./findChildren";
|
||||||
|
|
||||||
export default function findCollapsedNodes(doc: Node): NodeWithPos[] {
|
export default function findCollapsedNodes(doc: Node): NodeWithPos[] {
|
||||||
const blocks = findBlockNodes(doc);
|
const blocks = findBlockNodes(doc);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Node } from "prosemirror-model";
|
import { Node } from "prosemirror-model";
|
||||||
import { findTextNodes, NodeWithPos } from "prosemirror-utils";
|
import { findChildren, NodeWithPos } from "./findChildren";
|
||||||
|
|
||||||
export default function findLinkNodes(doc: Node): NodeWithPos[] {
|
export default function findLinkNodes(doc: Node): NodeWithPos[] {
|
||||||
const textNodes = findTextNodes(doc);
|
const textNodes = findChildren(doc, (child) => child.isText);
|
||||||
const nodes: NodeWithPos[] = [];
|
const nodes: NodeWithPos[] = [];
|
||||||
|
|
||||||
for (const nodeWithPos of textNodes) {
|
for (const nodeWithPos of textNodes) {
|
||||||
|
|||||||
44
shared/editor/queries/findParentNode.ts
Normal file
44
shared/editor/queries/findParentNode.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { Node, ResolvedPos } from "prosemirror-model";
|
||||||
|
import { Selection } from "prosemirror-state";
|
||||||
|
|
||||||
|
type Predicate = (node: Node) => boolean;
|
||||||
|
|
||||||
|
type ContentNodeWithPos = {
|
||||||
|
pos: number;
|
||||||
|
start: number;
|
||||||
|
depth: number;
|
||||||
|
node: Node;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const findParentNode =
|
||||||
|
(predicate: Predicate) =>
|
||||||
|
({ $from }: Selection) =>
|
||||||
|
findParentNodeClosestToPos($from, predicate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over parent nodes starting from the given `$pos`, returning the
|
||||||
|
* closest node and its start position `predicate` returns truthy for. `start`
|
||||||
|
* points to the start position of the node, `pos` points directly before the node.
|
||||||
|
*
|
||||||
|
* @param $pos position to start from
|
||||||
|
* @param predicate filtering predicate function
|
||||||
|
* @returns node and its start position
|
||||||
|
*/
|
||||||
|
export const findParentNodeClosestToPos = (
|
||||||
|
$pos: ResolvedPos,
|
||||||
|
predicate: Predicate
|
||||||
|
): ContentNodeWithPos | undefined => {
|
||||||
|
for (let i = $pos.depth; i > 0; i--) {
|
||||||
|
const node = $pos.node(i);
|
||||||
|
if (predicate(node)) {
|
||||||
|
return {
|
||||||
|
pos: i > 0 ? $pos.before(i) : 0,
|
||||||
|
start: $pos.start(i),
|
||||||
|
depth: i,
|
||||||
|
node,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { CellSelection } from "prosemirror-tables";
|
|
||||||
|
|
||||||
export default function getColumnIndex(selection: CellSelection) {
|
|
||||||
const isColSelection = selection.isColSelection && selection.isColSelection();
|
|
||||||
if (!isColSelection) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = (selection.$from as any).path;
|
|
||||||
return path[path.length - 5];
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { CellSelection } from "prosemirror-tables";
|
|
||||||
|
|
||||||
export default function getRowIndex(selection: CellSelection) {
|
|
||||||
const isRowSelection = selection.isRowSelection && selection.isRowSelection();
|
|
||||||
if (!isRowSelection) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = (selection.$from as any).path;
|
|
||||||
return path[path.length - 8];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRowIndexFromText(selection: CellSelection) {
|
|
||||||
const isRowSelection = selection.isRowSelection && selection.isRowSelection();
|
|
||||||
const path = (selection.$from as any).path;
|
|
||||||
if (isRowSelection) {
|
|
||||||
return path[path.length - 8];
|
|
||||||
} else {
|
|
||||||
return path[path.length - 11];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { NodeType } from "prosemirror-model";
|
import { NodeType } from "prosemirror-model";
|
||||||
import { EditorState } from "prosemirror-state";
|
import { EditorState } from "prosemirror-state";
|
||||||
import { findParentNode, findSelectedNodeOfType } from "prosemirror-utils";
|
import { findParentNode } from "./findParentNode";
|
||||||
|
|
||||||
const isNodeActive =
|
const isNodeActive =
|
||||||
(type: NodeType, attrs: Record<string, any> = {}) =>
|
(type: NodeType, attrs: Record<string, any> = {}) =>
|
||||||
@@ -9,15 +9,21 @@ const isNodeActive =
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const node =
|
const nodeAfter = state.selection.$from.nodeAfter;
|
||||||
findSelectedNodeOfType(type)(state.selection) ||
|
let node = nodeAfter?.type === type ? nodeAfter : undefined;
|
||||||
findParentNode((node) => node.type === type)(state.selection);
|
|
||||||
|
if (!node) {
|
||||||
|
const parent = findParentNode((node) => node.type === type)(
|
||||||
|
state.selection
|
||||||
|
);
|
||||||
|
node = parent?.node;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Object.keys(attrs).length || !node) {
|
if (!Object.keys(attrs).length || !node) {
|
||||||
return !!node;
|
return !!node;
|
||||||
}
|
}
|
||||||
|
|
||||||
return node.node.hasMarkup(type, { ...node.node.attrs, ...attrs });
|
return node.hasMarkup(type, { ...node.attrs, ...attrs });
|
||||||
};
|
};
|
||||||
|
|
||||||
export default isNodeActive;
|
export default isNodeActive;
|
||||||
|
|||||||
95
shared/editor/queries/table.ts
Normal file
95
shared/editor/queries/table.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { EditorState } from "prosemirror-state";
|
||||||
|
import { CellSelection, isInTable, selectedRect } from "prosemirror-tables";
|
||||||
|
|
||||||
|
export function getColumnIndex(state: EditorState): number | undefined {
|
||||||
|
if (state.selection instanceof CellSelection) {
|
||||||
|
if (state.selection.isColSelection()) {
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
return rect.left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRowIndex(state: EditorState): number | undefined {
|
||||||
|
if (state.selection instanceof CellSelection) {
|
||||||
|
if (state.selection.isRowSelection()) {
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
return rect.top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCellsInColumn(index: number) {
|
||||||
|
return (state: EditorState): number[] => {
|
||||||
|
if (!isInTable(state)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
const cells = [];
|
||||||
|
|
||||||
|
for (let i = index; i < rect.map.map.length; i += rect.map.width) {
|
||||||
|
const cell = rect.tableStart + rect.map.map[i];
|
||||||
|
cells.push(cell);
|
||||||
|
}
|
||||||
|
return cells;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCellsInRow(index: number) {
|
||||||
|
return (state: EditorState): number[] => {
|
||||||
|
if (!isInTable(state)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
const cells = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < rect.map.width; i += 1) {
|
||||||
|
const cell = rect.tableStart + rect.map.map[index * rect.map.width + i];
|
||||||
|
cells.push(cell);
|
||||||
|
}
|
||||||
|
return cells;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isColumnSelected(index: number) {
|
||||||
|
return (state: EditorState): boolean => {
|
||||||
|
if (state.selection instanceof CellSelection) {
|
||||||
|
if (state.selection.isColSelection()) {
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
return rect.left <= index && rect.right > index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRowSelected(index: number) {
|
||||||
|
return (state: EditorState): boolean => {
|
||||||
|
if (state.selection instanceof CellSelection) {
|
||||||
|
if (state.selection.isRowSelection()) {
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
return rect.top <= index && rect.bottom > index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTableSelected(state: EditorState): boolean {
|
||||||
|
const rect = selectedRect(state);
|
||||||
|
|
||||||
|
return (
|
||||||
|
rect.top === 0 &&
|
||||||
|
rect.left === 0 &&
|
||||||
|
rect.bottom === rect.map.height &&
|
||||||
|
rect.right === rect.map.width
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Node as ProsemirrorNode } from "prosemirror-model";
|
import { Node as ProsemirrorNode } from "prosemirror-model";
|
||||||
import { EditorState, Transaction } from "prosemirror-state";
|
import { EditorState } from "prosemirror-state";
|
||||||
import { EditorView } from "prosemirror-view";
|
import { EditorView } from "prosemirror-view";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { DefaultTheme } from "styled-components";
|
import { DefaultTheme } from "styled-components";
|
||||||
@@ -34,5 +34,3 @@ export type ComponentProps = {
|
|||||||
isEditable: boolean;
|
isEditable: boolean;
|
||||||
getPos: () => number;
|
getPos: () => number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Dispatch = (tr: Transaction) => void;
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
"suppressImplicitAnyIndexErrors": true,
|
"suppressImplicitAnyIndexErrors": true,
|
||||||
"target": "es2020",
|
"target": "es2020",
|
||||||
"paths": {
|
"paths": {
|
||||||
"prosemirror-markdown": ["./node_modules/@types/prosemirror-markdown"],
|
|
||||||
"@server/*": ["./server/*"],
|
"@server/*": ["./server/*"],
|
||||||
"@shared/*": ["./shared/*"],
|
"@shared/*": ["./shared/*"],
|
||||||
"~/*": ["./app/*"]
|
"~/*": ["./app/*"]
|
||||||
|
|||||||
233
yarn.lock
233
yarn.lock
@@ -3152,11 +3152,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/orderedmap@*":
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/orderedmap/-/orderedmap-1.0.0.tgz#807455a192bba52cbbb4517044bc82bdbfa8c596"
|
|
||||||
integrity sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw==
|
|
||||||
|
|
||||||
"@types/pako@^1.0.3":
|
"@types/pako@^1.0.3":
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/pako/-/pako-1.0.4.tgz#b4262aef92680a9331fcdb8420c69cf3dd98d3f3"
|
resolved "https://registry.yarnpkg.com/@types/pako/-/pako-1.0.4.tgz#b4262aef92680a9331fcdb8420c69cf3dd98d3f3"
|
||||||
@@ -3193,104 +3188,6 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
|
||||||
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
||||||
|
|
||||||
"@types/prosemirror-commands@*", "@types/prosemirror-commands@^1.0.4":
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-commands/-/prosemirror-commands-1.0.4.tgz#d08551415127d93ae62e7239d30db0b5e7208e22"
|
|
||||||
integrity sha512-utDNYB3EXLjAfYIcRWJe6pn3kcQ5kG4RijbT/0Y/TFOm6yhvYS/D9eJVnijdg9LDjykapcezchxGRqFD5LcyaQ==
|
|
||||||
dependencies:
|
|
||||||
"@types/prosemirror-model" "*"
|
|
||||||
"@types/prosemirror-state" "*"
|
|
||||||
"@types/prosemirror-view" "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-dropcursor@^1.5.0":
|
|
||||||
version "1.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-dropcursor/-/prosemirror-dropcursor-1.5.0.tgz#5633fb20e7c1bf27edbdb64781b09bfb9f150f25"
|
|
||||||
integrity sha512-Xa13THoY0YkvYP/peH995ahT79w3ErdsmFUIaTY21nshxxnn5mdSgG+RTpkqXwZ85v+n28MvNfLF2gm+c8RZ1A==
|
|
||||||
dependencies:
|
|
||||||
prosemirror-dropcursor "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-gapcursor@^1.3.0":
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.0.tgz#c1fcbf2504adc2b060b7a0c05f2d9dbb0631ce2b"
|
|
||||||
integrity sha512-KbZbwrr2i6+AAOtTTQhbgXlAL1ZTY+FE8PsGz4vqRLeS4ow7sppdI3oHGMn0xmCgqXI+ajEDYENKHUQ2WZkXew==
|
|
||||||
dependencies:
|
|
||||||
prosemirror-gapcursor "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-history@^1.0.1":
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-history/-/prosemirror-history-1.0.3.tgz#f1110efbe758129b5475e466ff077f0a8d9b964f"
|
|
||||||
integrity sha512-5TloMDRavgLjOAKXp1Li8u0xcsspzbT1Cm9F2pwHOkgvQOz1jWQb2VIXO7RVNsFjLBZdIXlyfSLivro3DuMWXg==
|
|
||||||
dependencies:
|
|
||||||
"@types/prosemirror-model" "*"
|
|
||||||
"@types/prosemirror-state" "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-inputrules@^1.0.2":
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-inputrules/-/prosemirror-inputrules-1.0.4.tgz#4cb75054d954aa0f6f42099be05eb6c0e6958bae"
|
|
||||||
integrity sha512-lJIMpOjO47SYozQybUkpV6QmfuQt7GZKHtVrvS+mR5UekA8NMC5HRIVMyaIauJLWhKU6oaNjpVaXdw41kh165g==
|
|
||||||
dependencies:
|
|
||||||
"@types/prosemirror-model" "*"
|
|
||||||
"@types/prosemirror-state" "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-keymap@^1.0.1":
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz#f73c79810e8d0e0a20d153d84f998f02e5afbc0c"
|
|
||||||
integrity sha512-ycevwkqUh+jEQtPwqO7sWGcm+Sybmhu8MpBsM8DlO3+YTKnXbKA6SDz/+q14q1wK3UA8lHJyfR+v+GPxfUSemg==
|
|
||||||
dependencies:
|
|
||||||
"@types/prosemirror-commands" "*"
|
|
||||||
"@types/prosemirror-model" "*"
|
|
||||||
"@types/prosemirror-state" "*"
|
|
||||||
"@types/prosemirror-view" "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-markdown@^1.0.3":
|
|
||||||
version "1.5.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-markdown/-/prosemirror-markdown-1.5.6.tgz#f985cec020ff37adcc2b490536adb4f82985dfae"
|
|
||||||
integrity sha512-Zm2YvkDsOrvvTct6GxTHAOQ/eAMwmeUMWoyyS1meNqdM3QHmp+mHln03tTAZKd6iRu1WbIKwHzTz/Mhof3C54Q==
|
|
||||||
dependencies:
|
|
||||||
"@types/markdown-it" "*"
|
|
||||||
"@types/prosemirror-model" "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-model@*", "@types/prosemirror-model@^1.7.2":
|
|
||||||
version "1.16.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-model/-/prosemirror-model-1.16.0.tgz#8b22c7431a4c93f7f550fc89c4b0e2d44d42c8b6"
|
|
||||||
integrity sha512-nv93YLyTEcDDl17OB90EldxZjyJQJll2WSMLDvLzTewbpvE/vtMjHT3j4mik3uSzQ6YD486AcloCO3WODY/lDg==
|
|
||||||
dependencies:
|
|
||||||
"@types/orderedmap" "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-schema-list@^1.0.3":
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz#bdf1893a7915fbdc5c49b3cac9368e96213d70de"
|
|
||||||
integrity sha512-uWybOf+M2Ea7rlbs0yLsS4YJYNGXYtn4N+w8HCw3Vvfl6wBAROzlMt0gV/D/VW/7J/LlAjwMezuGe8xi24HzXA==
|
|
||||||
dependencies:
|
|
||||||
"@types/orderedmap" "*"
|
|
||||||
"@types/prosemirror-model" "*"
|
|
||||||
"@types/prosemirror-state" "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-state@*", "@types/prosemirror-state@^1.2.4":
|
|
||||||
version "1.2.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-state/-/prosemirror-state-1.2.8.tgz#65080eeec52f63c50bf7034377f07773b4f6b2ac"
|
|
||||||
integrity sha512-mq9uyQWcpu8jeamO6Callrdvf/e1H/aRLR2kZWSpZrPHctEsxWHBbluD/wqVjXBRIOoMHLf6ZvOkrkmGLoCHVA==
|
|
||||||
dependencies:
|
|
||||||
"@types/prosemirror-model" "*"
|
|
||||||
"@types/prosemirror-transform" "*"
|
|
||||||
"@types/prosemirror-view" "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-transform@*":
|
|
||||||
version "1.1.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-transform/-/prosemirror-transform-1.1.4.tgz#c3565e81b2ef3ce3254e6927d6f63eb8d7bb20d0"
|
|
||||||
integrity sha512-HP1PauvkqSgDquZut8HaLOTUDQ6jja/LAy4OA7tTS1XG7wqRnX3gLUyEj0mD6vFd4y8BPkNddNdOh/BeGHlUjg==
|
|
||||||
dependencies:
|
|
||||||
"@types/prosemirror-model" "*"
|
|
||||||
|
|
||||||
"@types/prosemirror-view@*", "@types/prosemirror-view@^1.11.4":
|
|
||||||
version "1.19.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prosemirror-view/-/prosemirror-view-1.19.2.tgz#1bab4daf0f1f14313fe0d3f6b57f0a3b4ef6c50d"
|
|
||||||
integrity sha512-pmh2DuMJzva4D7SxspRKIzkV6FK2o52uAqGjq2dPYcQFPwu4+5RcS1TMjFVCh1R+Ia1Rx8wsCNIId/5+6DB0Bg==
|
|
||||||
dependencies:
|
|
||||||
"@types/prosemirror-model" "*"
|
|
||||||
"@types/prosemirror-state" "*"
|
|
||||||
"@types/prosemirror-transform" "*"
|
|
||||||
|
|
||||||
"@types/qs@*":
|
"@types/qs@*":
|
||||||
version "6.9.7"
|
version "6.9.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
|
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
|
||||||
@@ -10058,10 +9955,10 @@ ordered-read-streams@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
readable-stream "^2.0.1"
|
readable-stream "^2.0.1"
|
||||||
|
|
||||||
orderedmap@^1.1.0:
|
orderedmap@^2.0.0:
|
||||||
version "1.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.1.tgz#c618e77611b3b21d0fe3edc92586265e0059c789"
|
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.1.1.tgz#61481269c44031c449915497bf5a4ad273c512d2"
|
||||||
integrity sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ==
|
integrity sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==
|
||||||
|
|
||||||
os-tmpdir@~1.0.2:
|
os-tmpdir@~1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@@ -10659,94 +10556,97 @@ property-information@^5.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
xtend "^4.0.0"
|
xtend "^4.0.0"
|
||||||
|
|
||||||
prosemirror-commands@1.2.2:
|
prosemirror-commands@^1.5.2:
|
||||||
version "1.2.2"
|
version "1.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.2.2.tgz#1bd167372ee20abf488aca9cece63c43fab182c9"
|
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.5.2.tgz#e94aeea52286f658cd984270de9b4c3fff580852"
|
||||||
integrity sha512-TX+KpWudMon06frryfpO/u7hsQv2hu8L4VSVbCpi3/7wXHBgl+35mV85qfa3RpT8xD2f3MdeoTqH0vy5JdbXPg==
|
integrity sha512-hgLcPaakxH8tu6YvVAaILV2tXYsW3rAdDR8WNkeKGcgeMVQg3/TMhPdVoh7iAmfgVjZGtcOSjKiQaoeKjzd2mQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-model "^1.0.0"
|
prosemirror-model "^1.0.0"
|
||||||
prosemirror-state "^1.0.0"
|
prosemirror-state "^1.0.0"
|
||||||
prosemirror-transform "^1.0.0"
|
prosemirror-transform "^1.0.0"
|
||||||
|
|
||||||
prosemirror-dropcursor@*, prosemirror-dropcursor@^1.6.1:
|
prosemirror-dropcursor@^1.8.1:
|
||||||
version "1.6.1"
|
version "1.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.6.1.tgz#31f696172105f232bd17543ccf305e0f33e59d1d"
|
resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz#49b9fb2f583e0d0f4021ff87db825faa2be2832d"
|
||||||
integrity sha512-LtyqQpkIknaT7NnZl3vDr3TpkNcG4ABvGRXx37XJ8tJNUGtcrZBh40A0344rDwlRTfUEmynQS/grUsoSWz+HgA==
|
integrity sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-state "^1.0.0"
|
prosemirror-state "^1.0.0"
|
||||||
prosemirror-transform "^1.1.0"
|
prosemirror-transform "^1.1.0"
|
||||||
prosemirror-view "^1.1.0"
|
prosemirror-view "^1.1.0"
|
||||||
|
|
||||||
prosemirror-gapcursor@*, prosemirror-gapcursor@^1.3.1:
|
prosemirror-gapcursor@^1.3.2:
|
||||||
version "1.3.1"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.1.tgz#8cfd874592e4504d63720e14ed680c7866e64554"
|
resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz#5fa336b83789c6199a7341c9493587e249215cb4"
|
||||||
integrity sha512-GKTeE7ZoMsx5uVfc51/ouwMFPq0o8YrZ7Hx4jTF4EeGbXxBveUV8CGv46mSHuBBeXGmvu50guoV2kSnOeZZnUA==
|
integrity sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-keymap "^1.0.0"
|
prosemirror-keymap "^1.0.0"
|
||||||
prosemirror-model "^1.0.0"
|
prosemirror-model "^1.0.0"
|
||||||
prosemirror-state "^1.0.0"
|
prosemirror-state "^1.0.0"
|
||||||
prosemirror-view "^1.0.0"
|
prosemirror-view "^1.0.0"
|
||||||
|
|
||||||
prosemirror-history@^1.2.0:
|
prosemirror-history@^1.3.2:
|
||||||
version "1.2.0"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.2.0.tgz#04cc4df8d2f7b2a46651a2780de191ada6d465ea"
|
resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.3.2.tgz#ce6ad7ab9db83e761aee716f3040d74738311b15"
|
||||||
integrity sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==
|
integrity sha512-/zm0XoU/N/+u7i5zepjmZAEnpvjDtzoPWW6VmKptcAnPadN/SStsBjMImdCEbb3seiNTpveziPTIrXQbHLtU1g==
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-state "^1.2.2"
|
prosemirror-state "^1.2.2"
|
||||||
prosemirror-transform "^1.0.0"
|
prosemirror-transform "^1.0.0"
|
||||||
|
prosemirror-view "^1.31.0"
|
||||||
rope-sequence "^1.3.0"
|
rope-sequence "^1.3.0"
|
||||||
|
|
||||||
prosemirror-inputrules@^1.1.3:
|
prosemirror-inputrules@^1.2.1:
|
||||||
version "1.1.3"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz#93f9199ca02473259c30d7e352e4c14022d54638"
|
resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.2.1.tgz#8faf3d78c16150aedac71d326a3e3947417ce557"
|
||||||
integrity sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==
|
integrity sha512-3LrWJX1+ULRh5SZvbIQlwZafOXqp1XuV21MGBu/i5xsztd+9VD15x6OtN6mdqSFI7/8Y77gYUbQ6vwwJ4mr6QQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-state "^1.0.0"
|
prosemirror-state "^1.0.0"
|
||||||
prosemirror-transform "^1.0.0"
|
prosemirror-transform "^1.0.0"
|
||||||
|
|
||||||
prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.1.5:
|
prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.2.2:
|
||||||
version "1.1.5"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz#b5984c7d30f5c75956c853126c54e9e624c0327b"
|
resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz#14a54763a29c7b2704f561088ccf3384d14eb77e"
|
||||||
integrity sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==
|
integrity sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-state "^1.0.0"
|
prosemirror-state "^1.0.0"
|
||||||
w3c-keyname "^2.2.0"
|
w3c-keyname "^2.2.0"
|
||||||
|
|
||||||
prosemirror-markdown@^1.9.3:
|
prosemirror-markdown@^1.11.0:
|
||||||
version "1.9.3"
|
version "1.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.9.3.tgz#13615fdeea71b744cc7bbbc64480968c8b7a2cee"
|
resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.11.0.tgz#75f2d6f14655762b4b8a247436b87ed81e22c7ee"
|
||||||
integrity sha512-tPJ3jEUTF3C5m60m/Eq8Y3SW6d5av0Ll61HuK1NuDP+jXsFFywG2nw500+nn7GQi5lSXP3n1HQP9zd0fpmAlzw==
|
integrity sha512-yP9mZqPRstjZhhf3yykCQNE3AijxARrHe4e7esV9A+gp4cnGOH4QvrKYPpXLHspNWyvJJ+0URH+iIvV5qP1I2Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
markdown-it "^13.0.1"
|
markdown-it "^13.0.1"
|
||||||
prosemirror-model "^1.0.0"
|
prosemirror-model "^1.0.0"
|
||||||
|
|
||||||
prosemirror-model@1.16.1, prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.8.1:
|
prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.19.1, prosemirror-model@^1.8.1:
|
||||||
version "1.16.1"
|
version "1.19.1"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.16.1.tgz#fb388270bc9609b66298d6a7e15d0cc1d6c61253"
|
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.1.tgz#7e10cd9584a0a55c87ffbecc68aa9d105b9a0f53"
|
||||||
integrity sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==
|
integrity sha512-RpV0fZfy74DEO9GPRbGcG6xN33KuqEvlLE2V0e5CXUGs3xkZsiJfx1dcYPU57+606NVYCaDN1riFXdXBQRaRcg==
|
||||||
dependencies:
|
dependencies:
|
||||||
orderedmap "^1.1.0"
|
orderedmap "^2.0.0"
|
||||||
|
|
||||||
prosemirror-schema-list@1.1.4:
|
prosemirror-schema-list@^1.2.3:
|
||||||
version "1.1.4"
|
version "1.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.4.tgz#471f9caf2d2bed93641d2e490434c0d2d4330df1"
|
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.2.3.tgz#12e3d70cb17780980a3c28588ed7c888121d5e8d"
|
||||||
integrity sha512-pNTuZflacFOBlxrTcWSdWhjoB8BaucwfJVp/gJNxztOwaN3wQiC65axclXyplf6TKgXD/EkWfS/QAov3/Znadw==
|
integrity sha512-HD8yjDOusz7JB3oBFCaMOpEN9Z9DZttLr6tcASjnvKMc0qTyX5xgAN8YiMFFEcwyhF7WZrZ2YQkAwzsn8ICVbQ==
|
||||||
|
dependencies:
|
||||||
|
prosemirror-model "^1.0.0"
|
||||||
|
prosemirror-state "^1.0.0"
|
||||||
|
prosemirror-transform "^1.0.0"
|
||||||
|
|
||||||
|
prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.4.3:
|
||||||
|
version "1.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.4.3.tgz#94aecf3ffd54ec37e87aa7179d13508da181a080"
|
||||||
|
integrity sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-model "^1.0.0"
|
prosemirror-model "^1.0.0"
|
||||||
prosemirror-transform "^1.0.0"
|
prosemirror-transform "^1.0.0"
|
||||||
|
prosemirror-view "^1.27.0"
|
||||||
|
|
||||||
prosemirror-state@1.3.4, prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1:
|
prosemirror-tables@^1.3.2:
|
||||||
version "1.3.4"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.3.4.tgz#4c6b52628216e753fc901c6d2bfd84ce109e8952"
|
resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.3.2.tgz#ca208c6a55d510af14b652d23e800e00ba6bebd4"
|
||||||
integrity sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==
|
integrity sha512-/9JTeN6s58Zq66HXaxP6uf8PAmc7XXKZFPlOGVtLvxEd6xBP6WtzaJB9wBjiGUzwbdhdMEy7V62yuHqk/3VrnQ==
|
||||||
dependencies:
|
|
||||||
prosemirror-model "^1.0.0"
|
|
||||||
prosemirror-transform "^1.0.0"
|
|
||||||
|
|
||||||
prosemirror-tables@^1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.1.1.tgz#ad66300cc49500455cf1243bb129c9e7d883321e"
|
|
||||||
integrity sha512-LmCz4jrlqQZRsYRDzCRYf/pQ5CUcSOyqZlAj5kv67ZWBH1SVLP2U9WJEvQfimWgeRlIz0y0PQVqO1arRm1+woA==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-keymap "^1.1.2"
|
prosemirror-keymap "^1.1.2"
|
||||||
prosemirror-model "^1.8.1"
|
prosemirror-model "^1.8.1"
|
||||||
@@ -10754,22 +10654,17 @@ prosemirror-tables@^1.1.1:
|
|||||||
prosemirror-transform "^1.2.1"
|
prosemirror-transform "^1.2.1"
|
||||||
prosemirror-view "^1.13.3"
|
prosemirror-view "^1.13.3"
|
||||||
|
|
||||||
prosemirror-transform@1.2.5, prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1:
|
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.7.2:
|
||||||
version "1.2.5"
|
version "1.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.2.5.tgz#7a3e2c61fcdbaf1d0844a2a3bc34fc3524e9809c"
|
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.7.2.tgz#f3e57d8424afa6ab7c2b2319cc0ac58e75f7160b"
|
||||||
integrity sha512-eqeIaxWtUfOnpA1ERrXCuSIMzqIJtL9Qrs5uJMCjY5RMSaH5o4pc390SAjn/IDPeIlw6auh0hCCXs3wRvGnQug==
|
integrity sha512-b94lVUdA9NyaYRb2WuGSgb5YANiITa05dtew9eSK+KkYu64BCnU27WhJPE95gAWAnhV57CM3FabWXM23gri8Kg==
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-model "^1.0.0"
|
prosemirror-model "^1.0.0"
|
||||||
|
|
||||||
prosemirror-utils@^0.9.6:
|
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.31.3:
|
||||||
version "0.9.6"
|
version "1.31.3"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-utils/-/prosemirror-utils-0.9.6.tgz#3d97bd85897e3b535555867dc95a51399116a973"
|
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.31.3.tgz#cfe171c4e50a577526d0235d9ec757cdddf6017d"
|
||||||
integrity sha512-UC+j9hQQ1POYfMc5p7UFxBTptRiGPR7Kkmbl3jVvU8VgQbkI89tR/GK+3QYC8n+VvBZrtAoCrJItNhWSxX3slA==
|
integrity sha512-UYDa8WxRFZm0xQLXiPJUVTl6H08Fn0IUVDootA7ZlQwzooqVWnBOXLovJyyTKgws1nprfsPhhlvWgt2jo4ZA6g==
|
||||||
|
|
||||||
prosemirror-view@1.26.5, prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3:
|
|
||||||
version "1.26.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.26.5.tgz#65cb890d0971e94e0cbc09fdb0c5320cd474bc6f"
|
|
||||||
integrity sha512-SO+AX6WwdbJZHVvuloXI0qfO+YJAnZAat8qrYwfiqTQwL/FewLUnr0m3EXZ6a60hQs8/Q/lzeJXiFR/dOPaaKQ==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-model "^1.16.0"
|
prosemirror-model "^1.16.0"
|
||||||
prosemirror-state "^1.0.0"
|
prosemirror-state "^1.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user