chore: Upgrade all of prosemirror (#5366)
Co-authored-by: Apoorv Mishra <apoorvmishra101092@gmail.com>
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
import { findParentNode } from "prosemirror-utils";
|
||||
import React from "react";
|
||||
import useDictionary from "~/hooks/useDictionary";
|
||||
import getMenuItems from "../menus/block";
|
||||
import { useEditor } from "./EditorContext";
|
||||
import SuggestionsMenu, {
|
||||
Props as SuggestionsMenuProps,
|
||||
} from "./SuggestionsMenu";
|
||||
@@ -10,28 +8,18 @@ import SuggestionsMenuItem from "./SuggestionsMenuItem";
|
||||
|
||||
type Props = Omit<
|
||||
SuggestionsMenuProps,
|
||||
"renderMenuItem" | "items" | "onClearSearch"
|
||||
"renderMenuItem" | "items" | "trigger"
|
||||
> &
|
||||
Required<Pick<SuggestionsMenuProps, "onLinkToolbarOpen" | "embeds">>;
|
||||
|
||||
function BlockMenu(props: Props) {
|
||||
const { view } = useEditor();
|
||||
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 (
|
||||
<SuggestionsMenu
|
||||
{...props}
|
||||
filterable
|
||||
onClearSearch={clearSearch}
|
||||
trigger="/"
|
||||
renderMenuItem={(item, _index, options) => (
|
||||
<SuggestionsMenuItem
|
||||
onClick={options.onClick}
|
||||
|
||||
@@ -16,9 +16,7 @@ export default class ComponentView {
|
||||
node: ProsemirrorNode;
|
||||
view: EditorView;
|
||||
getPos: () => number;
|
||||
decorations: Decoration<{
|
||||
[key: string]: any;
|
||||
}>[];
|
||||
decorations: Decoration[];
|
||||
|
||||
isSelected = false;
|
||||
dom: HTMLElement | null;
|
||||
@@ -39,9 +37,7 @@ export default class ComponentView {
|
||||
node: ProsemirrorNode;
|
||||
view: EditorView;
|
||||
getPos: () => number;
|
||||
decorations: Decoration<{
|
||||
[key: string]: any;
|
||||
}>[];
|
||||
decorations: Decoration[];
|
||||
}
|
||||
) {
|
||||
this.component = component;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import FuzzySearch from "fuzzy-search";
|
||||
import gemojies from "gemoji";
|
||||
import React from "react";
|
||||
import { useEditor } from "./EditorContext";
|
||||
import EmojiMenuItem from "./EmojiMenuItem";
|
||||
import SuggestionsMenu, {
|
||||
Props as SuggestionsMenuProps,
|
||||
@@ -26,12 +25,11 @@ const searcher = new FuzzySearch<{
|
||||
|
||||
type Props = Omit<
|
||||
SuggestionsMenuProps<Emoji>,
|
||||
"renderMenuItem" | "items" | "onLinkToolbarOpen" | "embeds" | "onClearSearch"
|
||||
"renderMenuItem" | "items" | "onLinkToolbarOpen" | "embeds" | "trigger"
|
||||
>;
|
||||
|
||||
const EmojiMenu = (props: Props) => {
|
||||
const { search = "" } = props;
|
||||
const { view } = useEditor();
|
||||
|
||||
const items = React.useMemo(() => {
|
||||
const n = search.toLowerCase();
|
||||
@@ -50,24 +48,11 @@ const EmojiMenu = (props: Props) => {
|
||||
return result.slice(0, 10);
|
||||
}, [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 (
|
||||
<SuggestionsMenu
|
||||
{...props}
|
||||
trigger=":"
|
||||
filterable={false}
|
||||
onClearSearch={clearSearch}
|
||||
renderMenuItem={(item, _index, options) => (
|
||||
<EmojiMenuItem
|
||||
onClick={options.onClick}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NodeSelection } from "prosemirror-state";
|
||||
import { CellSelection } from "prosemirror-tables";
|
||||
import { CellSelection, selectedRect } from "prosemirror-tables";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import { depths, s } from "@shared/styles";
|
||||
@@ -87,23 +87,37 @@ function usePosition({
|
||||
|
||||
// tables are an oddity, and need their own positioning logic
|
||||
const isColSelection =
|
||||
selection instanceof CellSelection &&
|
||||
selection.isColSelection &&
|
||||
selection.isColSelection();
|
||||
selection instanceof CellSelection && selection.isColSelection();
|
||||
const isRowSelection =
|
||||
selection instanceof CellSelection &&
|
||||
selection.isRowSelection &&
|
||||
selection.isRowSelection();
|
||||
selection instanceof CellSelection && selection.isRowSelection();
|
||||
|
||||
if (isColSelection) {
|
||||
const { node: element } = view.domAtPos(selection.from);
|
||||
const { width } = (element as HTMLElement).getBoundingClientRect();
|
||||
selectionBounds.top -= 20;
|
||||
selectionBounds.right = selectionBounds.left + width;
|
||||
}
|
||||
|
||||
if (isRowSelection) {
|
||||
selectionBounds.right = selectionBounds.left = selectionBounds.left - 18;
|
||||
if (isColSelection && isRowSelection) {
|
||||
const rect = selectedRect(view.state);
|
||||
const table = view.domAtPos(rect.tableStart);
|
||||
const bounds = (table.node as HTMLElement).getBoundingClientRect();
|
||||
selectionBounds.top = bounds.top - 16;
|
||||
selectionBounds.left = bounds.left - 10;
|
||||
selectionBounds.right = bounds.left - 10;
|
||||
} else if (isColSelection) {
|
||||
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 =
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
OpenIcon,
|
||||
} from "outline-icons";
|
||||
import { Mark } from "prosemirror-model";
|
||||
import { setTextSelection } from "prosemirror-utils";
|
||||
import { Selection } from "prosemirror-state";
|
||||
import { EditorView } from "prosemirror-view";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
@@ -285,7 +285,10 @@ class LinkEditor extends React.Component<Props, State> {
|
||||
moveSelectionToEnd = () => {
|
||||
const { to, view } = this.props;
|
||||
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();
|
||||
};
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import { MentionType } from "@shared/types";
|
||||
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
|
||||
import User from "~/models/User";
|
||||
import Avatar from "~/components/Avatar";
|
||||
import { AvatarSize } from "~/components/Avatar/Avatar";
|
||||
import Flex from "~/components/Flex";
|
||||
import useRequest from "~/hooks/useRequest";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { useEditor } from "./EditorContext";
|
||||
import MentionMenuItem from "./MentionMenuItem";
|
||||
import SuggestionsMenu, {
|
||||
Props as SuggestionsMenuProps,
|
||||
@@ -32,7 +32,7 @@ interface MentionItem extends MenuItem {
|
||||
|
||||
type Props = Omit<
|
||||
SuggestionsMenuProps<MentionItem>,
|
||||
"renderMenuItem" | "items" | "onLinkToolbarOpen" | "embeds" | "onClearSearch"
|
||||
"renderMenuItem" | "items" | "onLinkToolbarOpen" | "embeds" | "trigger"
|
||||
>;
|
||||
|
||||
function MentionMenu({ search, isActive, ...rest }: Props) {
|
||||
@@ -42,7 +42,6 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
||||
const { users, auth } = useStores();
|
||||
const location = useLocation();
|
||||
const documentId = parseDocumentSlug(location.pathname);
|
||||
const { view } = useEditor();
|
||||
const { data, loading, request } = useRequest(
|
||||
React.useCallback(
|
||||
() =>
|
||||
@@ -80,19 +79,6 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
||||
}
|
||||
}, [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
|
||||
// incorrectly due to the height being unknown.
|
||||
if (!loaded) {
|
||||
@@ -104,7 +90,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
||||
{...rest}
|
||||
isActive={isActive}
|
||||
filterable={false}
|
||||
onClearSearch={clearSearch}
|
||||
trigger="@"
|
||||
search={search}
|
||||
renderMenuItem={(item, _index, options) => (
|
||||
<MentionMenuItem
|
||||
@@ -122,7 +108,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
||||
model={item.user}
|
||||
showBorder={false}
|
||||
alt={t("Profile picture")}
|
||||
size={16}
|
||||
size={AvatarSize.Small}
|
||||
/>
|
||||
</Flex>
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { some } from "lodash";
|
||||
import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
|
||||
import { CellSelection } from "prosemirror-tables";
|
||||
import * as React from "react";
|
||||
import createAndInsertLink from "@shared/editor/commands/createAndInsertLink";
|
||||
import filterExcessSeparators from "@shared/editor/lib/filterExcessSeparators";
|
||||
import getColumnIndex from "@shared/editor/queries/getColumnIndex";
|
||||
import getMarkRange from "@shared/editor/queries/getMarkRange";
|
||||
import getRowIndex from "@shared/editor/queries/getRowIndex";
|
||||
import isMarkActive from "@shared/editor/queries/isMarkActive";
|
||||
import isNodeActive from "@shared/editor/queries/isNodeActive";
|
||||
import { getColumnIndex, getRowIndex } from "@shared/editor/queries/table";
|
||||
import { MenuItem } from "@shared/editor/types";
|
||||
import { creatingUrlPrefix } from "@shared/utils/urls";
|
||||
import useBoolean from "~/hooks/useBoolean";
|
||||
@@ -197,8 +195,8 @@ export default function SelectionToolbar(props: Props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const colIndex = getColumnIndex(state.selection as unknown as CellSelection);
|
||||
const rowIndex = getRowIndex(state.selection as unknown as CellSelection);
|
||||
const colIndex = getColumnIndex(state);
|
||||
const rowIndex = getRowIndex(state);
|
||||
const isTableSelection = colIndex !== undefined && rowIndex !== undefined;
|
||||
const link = isMarkActive(state.schema.marks.link)(state);
|
||||
const range = getMarkRange(selection.$from, state.schema.marks.link);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import commandScore from "command-score";
|
||||
import { capitalize } from "lodash";
|
||||
import { findParentNode } from "prosemirror-utils";
|
||||
import * as React from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
||||
@@ -8,6 +7,7 @@ import styled from "styled-components";
|
||||
import insertFiles from "@shared/editor/commands/insertFiles";
|
||||
import { EmbedDescriptor } from "@shared/editor/embeds";
|
||||
import filterExcessSeparators from "@shared/editor/lib/filterExcessSeparators";
|
||||
import { findParentNode } from "@shared/editor/queries/findParentNode";
|
||||
import { MenuItem } from "@shared/editor/types";
|
||||
import { depths, s } from "@shared/styles";
|
||||
import { getEventFiles } from "@shared/utils/files";
|
||||
@@ -56,12 +56,12 @@ export type Props<T extends MenuItem = MenuItem> = {
|
||||
rtl: boolean;
|
||||
isActive: boolean;
|
||||
search: string;
|
||||
trigger: string;
|
||||
uploadFile?: (file: File) => Promise<string>;
|
||||
onFileUploadStart?: () => void;
|
||||
onFileUploadStop?: () => void;
|
||||
onLinkToolbarOpen?: () => void;
|
||||
onClose: (insertNewLine?: boolean) => void;
|
||||
onClearSearch: () => void;
|
||||
embeds?: EmbedDescriptor[];
|
||||
renderMenuItem: (
|
||||
item: T,
|
||||
@@ -163,6 +163,30 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
||||
[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(() => {
|
||||
if (!props.isActive) {
|
||||
return;
|
||||
@@ -185,7 +209,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
||||
|
||||
const insertNode = React.useCallback(
|
||||
(item: MenuItem | EmbedDescriptor) => {
|
||||
props.onClearSearch();
|
||||
handleClearSearch();
|
||||
|
||||
const command = item.name ? commands[item.name] : undefined;
|
||||
|
||||
@@ -201,7 +225,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
||||
|
||||
props.onClose();
|
||||
},
|
||||
[commands, props, view]
|
||||
[commands, handleClearSearch, props, view]
|
||||
);
|
||||
|
||||
const handleClickItem = React.useCallback(
|
||||
@@ -216,7 +240,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
||||
case "embed":
|
||||
return triggerLinkInput(item);
|
||||
case "link": {
|
||||
props.onClearSearch();
|
||||
handleClearSearch();
|
||||
props.onClose();
|
||||
props.onLinkToolbarOpen?.();
|
||||
return;
|
||||
@@ -225,7 +249,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
||||
insertNode(item);
|
||||
}
|
||||
},
|
||||
[insertNode, props]
|
||||
[insertNode, handleClearSearch, props]
|
||||
);
|
||||
|
||||
const close = React.useCallback(() => {
|
||||
@@ -313,7 +337,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
||||
const files = getEventFiles(event);
|
||||
const parent = findParentNode((node) => !!node)(view.state.selection);
|
||||
|
||||
props.onClearSearch();
|
||||
handleClearSearch();
|
||||
|
||||
if (!uploadFile) {
|
||||
throw new Error("uploadFile prop is required to replace files");
|
||||
|
||||
Reference in New Issue
Block a user