fix: Drag and drop images in editor, conflict with sidebar react-dnd (#1918)

* fix: Drag and drop images in editor, conflict with sidebar react-dnd
see: https://github.com/react-dnd/react-dnd/pull/3052

* Bump to non-canary

* Upgrade react-dnd

* react-dnd api changes

* lint

* fix: dnd doesn't work on first render

* remove unneccessary async

* chore: Update react-dnd (API changed again)

* restore fade
This commit is contained in:
Tom Moor 2021-03-09 18:41:30 -08:00 committed by GitHub
parent ed2a42ac27
commit bac7a364d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 321 additions and 323 deletions

View File

@ -16,7 +16,7 @@ type AlignValues =
| "flex-start" | "flex-start"
| "flex-end"; | "flex-end";
type Props = { type Props = {|
column?: ?boolean, column?: ?boolean,
shrink?: ?boolean, shrink?: ?boolean,
align?: AlignValues, align?: AlignValues,
@ -24,12 +24,18 @@ type Props = {
auto?: ?boolean, auto?: ?boolean,
className?: string, className?: string,
children?: React.Node, children?: React.Node,
}; role?: string,
|};
const Flex = (props: Props) => { const Flex = React.forwardRef<Props, HTMLDivElement>((props: Props, ref) => {
const { children, ...restProps } = props; const { children, ...restProps } = props;
return <Container {...restProps}>{children}</Container>;
}; return (
<Container ref={ref} {...restProps}>
{children}
</Container>
);
});
const Container = styled.div` const Container = styled.div`
display: flex; display: flex;

View File

@ -12,6 +12,8 @@ import {
SettingsIcon, SettingsIcon,
} from "outline-icons"; } from "outline-icons";
import * as React from "react"; import * as React from "react";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import styled from "styled-components"; import styled from "styled-components";
import CollectionNew from "scenes/CollectionNew"; import CollectionNew from "scenes/CollectionNew";
@ -63,13 +65,21 @@ function MainSidebar() {
setInviteModalOpen(false); setInviteModalOpen(false);
}, []); }, []);
const [dndArea, setDndArea] = React.useState();
const handleSidebarRef = React.useCallback((node) => setDndArea(node), []);
const html5Options = React.useMemo(() => ({ rootElement: dndArea }), [
dndArea,
]);
const { user, team } = auth; const { user, team } = auth;
if (!user || !team) return null; if (!user || !team) return null;
const can = policies.abilities(team.id); const can = policies.abilities(team.id);
return ( return (
<Sidebar> <Sidebar ref={handleSidebarRef}>
{dndArea && (
<DndProvider backend={HTML5Backend} options={html5Options}>
<AccountMenu> <AccountMenu>
{(props) => ( {(props) => (
<TeamButton <TeamButton
@ -109,7 +119,9 @@ function MainSidebar() {
icon={<ShapesIcon color="currentColor" />} icon={<ShapesIcon color="currentColor" />}
exact={false} exact={false}
label={t("Templates")} label={t("Templates")}
active={documents.active ? documents.active.template : undefined} active={
documents.active ? documents.active.template : undefined
}
/> />
<SidebarLink <SidebarLink
to="/drafts" to="/drafts"
@ -130,7 +142,9 @@ function MainSidebar() {
/> />
</Section> </Section>
<Section auto> <Section auto>
<Collections onCreateCollection={handleCreateCollectionModalOpen} /> <Collections
onCreateCollection={handleCreateCollectionModalOpen}
/>
</Section> </Section>
<Section> <Section>
<SidebarLink <SidebarLink
@ -149,7 +163,9 @@ function MainSidebar() {
icon={<TrashIcon color="currentColor" />} icon={<TrashIcon color="currentColor" />}
exact={false} exact={false}
label={t("Trash")} label={t("Trash")}
active={documents.active ? documents.active.isDeleted : undefined} active={
documents.active ? documents.active.isDeleted : undefined
}
/> />
<SidebarLink <SidebarLink
to="/settings" to="/settings"
@ -183,6 +199,8 @@ function MainSidebar() {
> >
<CollectionNew onSubmit={handleCreateCollectionModalClose} /> <CollectionNew onSubmit={handleCreateCollectionModalClose} />
</Modal> </Modal>
</DndProvider>
)}
</Sidebar> </Sidebar>
); );
} }

View File

@ -13,14 +13,15 @@ import Toggle, { ToggleButton, Positioner } from "./components/Toggle";
import usePrevious from "hooks/usePrevious"; import usePrevious from "hooks/usePrevious";
import useStores from "hooks/useStores"; import useStores from "hooks/useStores";
let firstRender = true;
let ANIMATION_MS = 250; let ANIMATION_MS = 250;
let isFirstRender = true;
type Props = { type Props = {|
children: React.Node, children: React.Node,
}; |};
function Sidebar({ children }: Props) { const Sidebar = React.forwardRef<Props, HTMLButtonElement>(
({ children }: Props, ref) => {
const [isCollapsing, setCollapsing] = React.useState(false); const [isCollapsing, setCollapsing] = React.useState(false);
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
@ -129,6 +130,7 @@ function Sidebar({ children }: Props) {
React.useEffect(() => { React.useEffect(() => {
if (location !== previousLocation) { if (location !== previousLocation) {
isFirstRender = false;
ui.hideMobileSidebar(); ui.hideMobileSidebar();
} }
}, [ui, location, previousLocation]); }, [ui, location, previousLocation]);
@ -150,16 +152,6 @@ function Sidebar({ children }: Props) {
const content = ( const content = (
<> <>
<Container
style={style}
$sidebarWidth={ui.sidebarWidth}
$isCollapsing={isCollapsing}
$isAnimating={isAnimating}
$isSmallerThanMinimum={isSmallerThanMinimum}
$mobileSidebarVisible={ui.mobileSidebarVisible}
$collapsed={collapsed}
column
>
{ui.mobileSidebarVisible && ( {ui.mobileSidebarVisible && (
<Portal> <Portal>
<Fade> <Fade>
@ -180,6 +172,23 @@ function Sidebar({ children }: Props) {
aria-label={t("Expand")} aria-label={t("Expand")}
/> />
)} )}
</>
);
return (
<>
<Container
ref={ref}
style={style}
$sidebarWidth={ui.sidebarWidth}
$isCollapsing={isCollapsing}
$isAnimating={isAnimating}
$isSmallerThanMinimum={isSmallerThanMinimum}
$mobileSidebarVisible={ui.mobileSidebarVisible}
$collapsed={collapsed}
column
>
{isFirstRender ? <Fade>{content}</Fade> : content}
</Container> </Container>
{!ui.isEditing && ( {!ui.isEditing && (
<Toggle <Toggle
@ -191,15 +200,8 @@ function Sidebar({ children }: Props) {
)} )}
</> </>
); );
// Fade in the sidebar on first render after page load
if (firstRender) {
firstRender = false;
return <Fade>{content}</Fade>;
}
return content;
} }
);
const Background = styled.a` const Background = styled.a`
position: fixed; position: fixed;

View File

@ -35,7 +35,7 @@ class Collections extends React.Component<Props> {
componentDidMount() { componentDidMount() {
const { collections } = this.props; const { collections } = this.props;
if (!collections.isFetching && !collections.isLoaded) { if (!collections.isLoaded) {
collections.fetchPage({ limit: 100 }); collections.fetchPage({ limit: 100 });
} }
} }

View File

@ -123,7 +123,8 @@ function DocumentLink({
// Draggable // Draggable
const [{ isDragging }, drag] = useDrag({ const [{ isDragging }, drag] = useDrag({
item: { type: "document", ...node, depth, active: isActiveDocument }, type: "document",
item: () => ({ ...node, depth, active: isActiveDocument }),
collect: (monitor) => ({ collect: (monitor) => ({
isDragging: !!monitor.isDragging(), isDragging: !!monitor.isDragging(),
}), }),
@ -146,7 +147,7 @@ function DocumentLink({
// Drop to re-parent // Drop to re-parent
const [{ isOverReparent, canDropToReparent }, dropToReparent] = useDrop({ const [{ isOverReparent, canDropToReparent }, dropToReparent] = useDrop({
accept: "document", accept: "document",
drop: async (item, monitor) => { drop: (item, monitor) => {
if (monitor.didDrop()) return; if (monitor.didDrop()) return;
if (!collection) return; if (!collection) return;
documents.move(item.id, collection.id, node.id); documents.move(item.id, collection.id, node.id);
@ -183,7 +184,7 @@ function DocumentLink({
// Drop to reorder // Drop to reorder
const [{ isOverReorder }, dropToReorder] = useDrop({ const [{ isOverReorder }, dropToReorder] = useDrop({
accept: "document", accept: "document",
drop: async (item, monitor) => { drop: (item, monitor) => {
if (!collection) return; if (!collection) return;
if (item.id === node.id) return; if (item.id === node.id) return;

View File

@ -3,8 +3,6 @@ import "focus-visible";
import { createBrowserHistory } from "history"; import { createBrowserHistory } from "history";
import { Provider } from "mobx-react"; import { Provider } from "mobx-react";
import * as React from "react"; import * as React from "react";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { render } from "react-dom"; import { render } from "react-dom";
import { Router } from "react-router-dom"; import { Router } from "react-router-dom";
import { initI18n } from "shared/i18n"; import { initI18n } from "shared/i18n";
@ -47,7 +45,6 @@ if (element) {
<Provider {...stores}> <Provider {...stores}>
<Theme> <Theme>
<ErrorBoundary> <ErrorBoundary>
<DndProvider backend={HTML5Backend}>
<Router history={history}> <Router history={history}>
<> <>
<PageTheme /> <PageTheme />
@ -57,7 +54,6 @@ if (element) {
<Toasts /> <Toasts />
</> </>
</Router> </Router>
</DndProvider>
</ErrorBoundary> </ErrorBoundary>
</Theme> </Theme>
</Provider>, </Provider>,

View File

@ -140,8 +140,8 @@
"react-autosize-textarea": "^6.0.0", "react-autosize-textarea": "^6.0.0",
"react-avatar-editor": "^10.3.0", "react-avatar-editor": "^10.3.0",
"react-color": "^2.17.3", "react-color": "^2.17.3",
"react-dnd": "^11.1.3", "react-dnd": "^14.0.1",
"react-dnd-html5-backend": "^11.1.3", "react-dnd-html5-backend": "^14.0.0",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-dropzone": "^11.2.4", "react-dropzone": "^11.2.4",
"react-helmet": "^5.2.0", "react-helmet": "^5.2.0",

View File

@ -2033,14 +2033,6 @@
dependencies: dependencies:
"@types/unist" "*" "@types/unist" "*"
"@types/hoist-non-react-statics@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
@ -2105,19 +2097,6 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00"
integrity sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ== integrity sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ==
"@types/prop-types@*":
version "15.7.3"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
"@types/react@*":
version "17.0.0"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.0.tgz#5af3eb7fad2807092f0046a1302b7823e27919b8"
integrity sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==
dependencies:
"@types/prop-types" "*"
csstype "^3.0.2"
"@types/resolve@1.17.1": "@types/resolve@1.17.1":
version "1.17.1" version "1.17.1"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
@ -4339,11 +4318,6 @@ cssstyle@^2.2.0:
dependencies: dependencies:
cssom "~0.3.6" cssom "~0.3.6"
csstype@^3.0.2:
version "3.0.5"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.5.tgz#7fdec6a28a67ae18647c51668a9ff95bb2fa7bb8"
integrity sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==
cyclist@^1.0.1: cyclist@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@ -4624,14 +4598,14 @@ direction@^0.1.5:
resolved "https://registry.yarnpkg.com/direction/-/direction-0.1.5.tgz#ce5d797f97e26f8be7beff53f7dc40e1c1a9ec4c" resolved "https://registry.yarnpkg.com/direction/-/direction-0.1.5.tgz#ce5d797f97e26f8be7beff53f7dc40e1c1a9ec4c"
integrity sha1-zl15f5fib4vnvv9T99xA4cGp7Ew= integrity sha1-zl15f5fib4vnvv9T99xA4cGp7Ew=
dnd-core@^11.1.3: dnd-core@14.0.0:
version "11.1.3" version "14.0.0"
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-11.1.3.tgz#f92099ba7245e49729d2433157031a6267afcc98" resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-14.0.0.tgz#973ab3470d0a9ac5a0fa9021c4feba93ad12347d"
integrity sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA== integrity sha512-wTDYKyjSqWuYw3ZG0GJ7k+UIfzxTNoZLjDrut37PbcPGNfwhlKYlPUqjAKUjOOv80izshUiqusaKgJPItXSevA==
dependencies: dependencies:
"@react-dnd/asap" "^4.0.0" "@react-dnd/asap" "^4.0.0"
"@react-dnd/invariant" "^2.0.0" "@react-dnd/invariant" "^2.0.0"
redux "^4.0.4" redux "^4.0.5"
doctrine@1.5.0: doctrine@1.5.0:
version "1.5.0" version "1.5.0"
@ -5526,7 +5500,7 @@ fast-deep-equal@^1.0.0:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
fast-deep-equal@^3.1.1: fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3" version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@ -6453,7 +6427,7 @@ hmac-drbg@^1.0.1:
minimalistic-assert "^1.0.0" minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1" minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.2:
version "3.3.2" version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -10686,22 +10660,23 @@ react-color@^2.17.3:
reactcss "^1.2.0" reactcss "^1.2.0"
tinycolor2 "^1.4.1" tinycolor2 "^1.4.1"
react-dnd-html5-backend@^11.1.3: react-dnd-html5-backend@^14.0.0:
version "11.1.3" version "14.0.0"
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz#2749f04f416ec230ea193f5c1fbea2de7dffb8f7" resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-14.0.0.tgz#28d660a2ad1e07447c34a65cd25f7de8f1657194"
integrity sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw== integrity sha512-2wAQqRFC1hbRGmk6+dKhOXsyQQOn3cN8PSZyOUeOun9J8t3tjZ7PS2+aFu7CVu2ujMDwTJR3VTwZh8pj2kCv7g==
dependencies: dependencies:
dnd-core "^11.1.3" dnd-core "14.0.0"
react-dnd@^11.1.3: react-dnd@^14.0.1:
version "11.1.3" version "14.0.1"
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-11.1.3.tgz#f9844f5699ccc55dfc81462c2c19f726e670c1af" resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-14.0.1.tgz#012a855b0fb35a27c7c84aba7ff6c495e0baddb2"
integrity sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ== integrity sha512-r57KKBfmAYTwmQ/cREQehNjEX9U9Xi4AUWykLX92fB9JkY9z90DMWZhSE1M7o6Y71Y2/a2SBvSPQ385QboNrIQ==
dependencies: dependencies:
"@react-dnd/invariant" "^2.0.0"
"@react-dnd/shallowequal" "^2.0.0" "@react-dnd/shallowequal" "^2.0.0"
"@types/hoist-non-react-statics" "^3.3.1" dnd-core "14.0.0"
dnd-core "^11.1.3" fast-deep-equal "^3.1.3"
hoist-non-react-statics "^3.3.0" hoist-non-react-statics "^3.3.2"
react-dom@^16.8.6: react-dom@^16.8.6:
version "16.14.0" version "16.14.0"
@ -11028,7 +11003,7 @@ redis@^3.0.0:
redis-errors "^1.2.0" redis-errors "^1.2.0"
redis-parser "^3.0.0" redis-parser "^3.0.0"
redux@^4.0.4: redux@^4.0.5:
version "4.0.5" version "4.0.5"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==