fix: Flash of content when selecting text on RHS of document (#2981)

fix: Double portal
This commit is contained in:
Tom Moor
2022-01-22 18:10:23 -08:00
committed by GitHub
parent 80c6e57aa3
commit e8c88b3c33
5 changed files with 79 additions and 81 deletions

View File

@@ -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;

View File

@@ -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>
);
}
}

View File

@@ -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>
);

View File

@@ -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() {

View File

@@ -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(() => {