fix: Flash of content when selecting text on RHS of document (#2981)
fix: Double portal
This commit is contained in:
@@ -37,7 +37,13 @@ function usePosition({
|
||||
const viewportHeight = useViewportHeight();
|
||||
const isTouchDevice = useMediaQuery("(hover: none) and (pointer: coarse)");
|
||||
|
||||
if (!active || !menuWidth || !menuHeight || isSelectingText) {
|
||||
if (
|
||||
!active ||
|
||||
!menuWidth ||
|
||||
!menuHeight ||
|
||||
!menuRef.current ||
|
||||
isSelectingText
|
||||
) {
|
||||
return defaultPosition;
|
||||
}
|
||||
|
||||
@@ -146,54 +152,54 @@ function usePosition({
|
||||
}
|
||||
}
|
||||
|
||||
function FloatingToolbar(props: Props) {
|
||||
const menuRef = props.forwardedRef || React.createRef<HTMLDivElement>();
|
||||
const [isSelectingText, setSelectingText] = React.useState(false);
|
||||
const FloatingToolbar = React.forwardRef(
|
||||
(props: Props, forwardedRef: React.RefObject<HTMLDivElement>) => {
|
||||
const menuRef = forwardedRef || React.createRef<HTMLDivElement>();
|
||||
const [isSelectingText, setSelectingText] = React.useState(false);
|
||||
|
||||
const position = usePosition({
|
||||
menuRef,
|
||||
isSelectingText,
|
||||
props,
|
||||
});
|
||||
const position = usePosition({
|
||||
menuRef,
|
||||
isSelectingText,
|
||||
props,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleMouseDown = () => {
|
||||
if (!props.active) {
|
||||
setSelectingText(true);
|
||||
}
|
||||
};
|
||||
React.useEffect(() => {
|
||||
const handleMouseDown = () => {
|
||||
if (!props.active) {
|
||||
setSelectingText(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
setSelectingText(false);
|
||||
};
|
||||
const handleMouseUp = () => {
|
||||
setSelectingText(false);
|
||||
};
|
||||
|
||||
window.addEventListener("mousedown", handleMouseDown);
|
||||
window.addEventListener("mouseup", handleMouseUp);
|
||||
window.addEventListener("mousedown", handleMouseDown);
|
||||
window.addEventListener("mouseup", handleMouseUp);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("mousedown", handleMouseDown);
|
||||
window.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
}, [props.active]);
|
||||
return () => {
|
||||
window.removeEventListener("mousedown", handleMouseDown);
|
||||
window.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
}, [props.active]);
|
||||
|
||||
// only render children when state is updated to visible
|
||||
// to prevent gaining input focus before calculatePosition runs
|
||||
return (
|
||||
<Portal>
|
||||
<Wrapper
|
||||
active={props.active && position.visible}
|
||||
ref={menuRef}
|
||||
offset={position.offset}
|
||||
style={{
|
||||
top: `${position.top}px`,
|
||||
left: `${position.left}px`,
|
||||
}}
|
||||
>
|
||||
{position.visible && props.children}
|
||||
</Wrapper>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Portal>
|
||||
<Wrapper
|
||||
active={props.active && position.visible}
|
||||
ref={menuRef}
|
||||
offset={position.offset}
|
||||
style={{
|
||||
top: `${position.top}px`,
|
||||
left: `${position.left}px`,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</Wrapper>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const Wrapper = styled.div<{
|
||||
active?: boolean;
|
||||
@@ -259,9 +265,4 @@ const Wrapper = styled.div<{
|
||||
}
|
||||
`;
|
||||
|
||||
export default React.forwardRef(function FloatingToolbarWithForwardedRef(
|
||||
props: Props,
|
||||
ref: React.RefObject<HTMLDivElement>
|
||||
) {
|
||||
return <FloatingToolbar {...props} forwardedRef={ref} />;
|
||||
});
|
||||
export default FloatingToolbar;
|
||||
|
||||
@@ -3,8 +3,8 @@ import { NodeSelection, TextSelection } from "prosemirror-state";
|
||||
import { CellSelection } from "prosemirror-tables";
|
||||
import { EditorView } from "prosemirror-view";
|
||||
import * as React from "react";
|
||||
import { Portal } from "react-portal";
|
||||
import createAndInsertLink from "@shared/editor/commands/createAndInsertLink";
|
||||
import { CommandFactory } from "@shared/editor/lib/Extension";
|
||||
import filterExcessSeparators from "@shared/editor/lib/filterExcessSeparators";
|
||||
import getColumnIndex from "@shared/editor/queries/getColumnIndex";
|
||||
import getMarkRange from "@shared/editor/queries/getMarkRange";
|
||||
@@ -27,7 +27,7 @@ type Props = {
|
||||
dictionary: Dictionary;
|
||||
rtl: boolean;
|
||||
isTemplate: boolean;
|
||||
commands: Record<string, any>;
|
||||
commands: Record<string, CommandFactory>;
|
||||
onOpen: () => void;
|
||||
onClose: () => void;
|
||||
onSearchLink?: (term: string) => Promise<SearchResult[]>;
|
||||
@@ -229,27 +229,25 @@ export default class SelectionToolbar extends React.Component<Props> {
|
||||
}
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<FloatingToolbar
|
||||
view={view}
|
||||
active={isVisible(this.props)}
|
||||
ref={this.menuRef}
|
||||
>
|
||||
{link && range ? (
|
||||
<LinkEditor
|
||||
dictionary={dictionary}
|
||||
mark={range.mark}
|
||||
from={range.from}
|
||||
to={range.to}
|
||||
onCreateLink={onCreateLink ? this.handleOnCreateLink : undefined}
|
||||
onSelectLink={this.handleOnSelectLink}
|
||||
{...rest}
|
||||
/>
|
||||
) : (
|
||||
<ToolbarMenu items={items} {...rest} />
|
||||
)}
|
||||
</FloatingToolbar>
|
||||
</Portal>
|
||||
<FloatingToolbar
|
||||
view={view}
|
||||
active={isVisible(this.props)}
|
||||
ref={this.menuRef}
|
||||
>
|
||||
{link && range ? (
|
||||
<LinkEditor
|
||||
dictionary={dictionary}
|
||||
mark={range.mark}
|
||||
from={range.from}
|
||||
to={range.to}
|
||||
onCreateLink={onCreateLink ? this.handleOnCreateLink : undefined}
|
||||
onSelectLink={this.handleOnSelectLink}
|
||||
{...rest}
|
||||
/>
|
||||
) : (
|
||||
<ToolbarMenu items={items} {...rest} />
|
||||
)}
|
||||
</FloatingToolbar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EditorView } from "prosemirror-view";
|
||||
import * as React from "react";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
import { CommandFactory } from "@shared/editor/lib/Extension";
|
||||
import { MenuItem } from "@shared/editor/types";
|
||||
import ToolbarButton from "./ToolbarButton";
|
||||
@@ -14,12 +14,12 @@ type Props = {
|
||||
};
|
||||
|
||||
const FlexibleWrapper = styled.div`
|
||||
color: ${(props) => props.theme.toolbarItem};
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
`;
|
||||
|
||||
function ToolbarMenu(props: Props) {
|
||||
const theme = useTheme();
|
||||
const { view, items } = props;
|
||||
const { state } = view;
|
||||
|
||||
@@ -36,13 +36,12 @@ function ToolbarMenu(props: Props) {
|
||||
const isActive = item.active ? item.active(state) : false;
|
||||
|
||||
return (
|
||||
<Tooltip tooltip={item.tooltip}>
|
||||
<Tooltip tooltip={item.tooltip} key={index}>
|
||||
<ToolbarButton
|
||||
key={index}
|
||||
onClick={() => item.name && props.commands[item.name](item.attrs)}
|
||||
active={isActive}
|
||||
>
|
||||
<Icon color={theme.toolbarItem} />
|
||||
<Icon color="currentColor" />
|
||||
</ToolbarButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { selectColumn, selectRow, selectTable } from "prosemirror-utils";
|
||||
import { Decoration, EditorView } from "prosemirror-view";
|
||||
import * as React from "react";
|
||||
import { DefaultTheme, ThemeProps } from "styled-components";
|
||||
import Extension from "@shared/editor/lib/Extension";
|
||||
import Extension, { CommandFactory } from "@shared/editor/lib/Extension";
|
||||
import ExtensionManager from "@shared/editor/lib/ExtensionManager";
|
||||
import headingToSlug from "@shared/editor/lib/headingToSlug";
|
||||
import { MarkdownSerializer } from "@shared/editor/lib/markdown/serializer";
|
||||
@@ -219,7 +219,7 @@ export class Editor extends React.PureComponent<
|
||||
|
||||
nodes: { [name: string]: NodeSpec };
|
||||
marks: { [name: string]: MarkSpec };
|
||||
commands: Record<string, any>;
|
||||
commands: Record<string, CommandFactory>;
|
||||
rulePlugins: PluginSimple[];
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
@@ -4,8 +4,8 @@ export default function useComponentSize(
|
||||
ref: React.RefObject<HTMLElement>
|
||||
): { width: number; height: number } {
|
||||
const [size, setSize] = useState({
|
||||
width: 0,
|
||||
height: 0,
|
||||
width: ref.current?.clientWidth || 0,
|
||||
height: ref.current?.clientHeight || 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user