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:
parent
ed2a42ac27
commit
bac7a364d0
|
@ -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;
|
||||||
|
|
|
@ -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,126 +65,142 @@ 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}>
|
||||||
<AccountMenu>
|
{dndArea && (
|
||||||
{(props) => (
|
<DndProvider backend={HTML5Backend} options={html5Options}>
|
||||||
<TeamButton
|
<AccountMenu>
|
||||||
{...props}
|
{(props) => (
|
||||||
subheading={user.name}
|
<TeamButton
|
||||||
teamName={team.name}
|
{...props}
|
||||||
logoUrl={team.avatarUrl}
|
subheading={user.name}
|
||||||
showDisclosure
|
teamName={team.name}
|
||||||
/>
|
logoUrl={team.avatarUrl}
|
||||||
)}
|
showDisclosure
|
||||||
</AccountMenu>
|
/>
|
||||||
<Scrollable flex topShadow>
|
)}
|
||||||
<Section>
|
</AccountMenu>
|
||||||
<SidebarLink
|
<Scrollable flex topShadow>
|
||||||
to="/home"
|
<Section>
|
||||||
icon={<HomeIcon color="currentColor" />}
|
<SidebarLink
|
||||||
exact={false}
|
to="/home"
|
||||||
label={t("Home")}
|
icon={<HomeIcon color="currentColor" />}
|
||||||
/>
|
exact={false}
|
||||||
<SidebarLink
|
label={t("Home")}
|
||||||
to={{
|
/>
|
||||||
pathname: "/search",
|
<SidebarLink
|
||||||
state: { fromMenu: true },
|
to={{
|
||||||
}}
|
pathname: "/search",
|
||||||
icon={<SearchIcon color="currentColor" />}
|
state: { fromMenu: true },
|
||||||
label={t("Search")}
|
}}
|
||||||
exact={false}
|
icon={<SearchIcon color="currentColor" />}
|
||||||
/>
|
label={t("Search")}
|
||||||
<SidebarLink
|
exact={false}
|
||||||
to="/starred"
|
/>
|
||||||
icon={<StarredIcon color="currentColor" />}
|
<SidebarLink
|
||||||
exact={false}
|
to="/starred"
|
||||||
label={t("Starred")}
|
icon={<StarredIcon color="currentColor" />}
|
||||||
/>
|
exact={false}
|
||||||
<SidebarLink
|
label={t("Starred")}
|
||||||
to="/templates"
|
/>
|
||||||
icon={<ShapesIcon color="currentColor" />}
|
<SidebarLink
|
||||||
exact={false}
|
to="/templates"
|
||||||
label={t("Templates")}
|
icon={<ShapesIcon color="currentColor" />}
|
||||||
active={documents.active ? documents.active.template : undefined}
|
exact={false}
|
||||||
/>
|
label={t("Templates")}
|
||||||
<SidebarLink
|
active={
|
||||||
to="/drafts"
|
documents.active ? documents.active.template : undefined
|
||||||
icon={<EditIcon color="currentColor" />}
|
}
|
||||||
label={
|
/>
|
||||||
<Drafts align="center">
|
<SidebarLink
|
||||||
{t("Drafts")}
|
to="/drafts"
|
||||||
<Bubble count={documents.totalDrafts} />
|
icon={<EditIcon color="currentColor" />}
|
||||||
</Drafts>
|
label={
|
||||||
}
|
<Drafts align="center">
|
||||||
active={
|
{t("Drafts")}
|
||||||
documents.active
|
<Bubble count={documents.totalDrafts} />
|
||||||
? !documents.active.publishedAt &&
|
</Drafts>
|
||||||
!documents.active.isDeleted &&
|
}
|
||||||
!documents.active.isTemplate
|
active={
|
||||||
: undefined
|
documents.active
|
||||||
}
|
? !documents.active.publishedAt &&
|
||||||
/>
|
!documents.active.isDeleted &&
|
||||||
</Section>
|
!documents.active.isTemplate
|
||||||
<Section auto>
|
: undefined
|
||||||
<Collections onCreateCollection={handleCreateCollectionModalOpen} />
|
}
|
||||||
</Section>
|
/>
|
||||||
<Section>
|
</Section>
|
||||||
<SidebarLink
|
<Section auto>
|
||||||
to="/archive"
|
<Collections
|
||||||
icon={<ArchiveIcon color="currentColor" />}
|
onCreateCollection={handleCreateCollectionModalOpen}
|
||||||
exact={false}
|
/>
|
||||||
label={t("Archive")}
|
</Section>
|
||||||
active={
|
<Section>
|
||||||
documents.active
|
<SidebarLink
|
||||||
? documents.active.isArchived && !documents.active.isDeleted
|
to="/archive"
|
||||||
: undefined
|
icon={<ArchiveIcon color="currentColor" />}
|
||||||
}
|
exact={false}
|
||||||
/>
|
label={t("Archive")}
|
||||||
<SidebarLink
|
active={
|
||||||
to="/trash"
|
documents.active
|
||||||
icon={<TrashIcon color="currentColor" />}
|
? documents.active.isArchived && !documents.active.isDeleted
|
||||||
exact={false}
|
: undefined
|
||||||
label={t("Trash")}
|
}
|
||||||
active={documents.active ? documents.active.isDeleted : undefined}
|
/>
|
||||||
/>
|
<SidebarLink
|
||||||
<SidebarLink
|
to="/trash"
|
||||||
to="/settings"
|
icon={<TrashIcon color="currentColor" />}
|
||||||
icon={<SettingsIcon color="currentColor" />}
|
exact={false}
|
||||||
exact={false}
|
label={t("Trash")}
|
||||||
label={t("Settings")}
|
active={
|
||||||
/>
|
documents.active ? documents.active.isDeleted : undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SidebarLink
|
||||||
|
to="/settings"
|
||||||
|
icon={<SettingsIcon color="currentColor" />}
|
||||||
|
exact={false}
|
||||||
|
label={t("Settings")}
|
||||||
|
/>
|
||||||
|
{can.invite && (
|
||||||
|
<SidebarLink
|
||||||
|
to="/settings/people"
|
||||||
|
onClick={handleInviteModalOpen}
|
||||||
|
icon={<PlusIcon color="currentColor" />}
|
||||||
|
label={`${t("Invite people")}…`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Section>
|
||||||
|
</Scrollable>
|
||||||
{can.invite && (
|
{can.invite && (
|
||||||
<SidebarLink
|
<Modal
|
||||||
to="/settings/people"
|
title={t("Invite people")}
|
||||||
onClick={handleInviteModalOpen}
|
onRequestClose={handleInviteModalClose}
|
||||||
icon={<PlusIcon color="currentColor" />}
|
isOpen={inviteModalOpen}
|
||||||
label={`${t("Invite people")}…`}
|
>
|
||||||
/>
|
<Invite onSubmit={handleInviteModalClose} />
|
||||||
|
</Modal>
|
||||||
)}
|
)}
|
||||||
</Section>
|
<Modal
|
||||||
</Scrollable>
|
title={t("Create a collection")}
|
||||||
{can.invite && (
|
onRequestClose={handleCreateCollectionModalClose}
|
||||||
<Modal
|
isOpen={createCollectionModalOpen}
|
||||||
title={t("Invite people")}
|
>
|
||||||
onRequestClose={handleInviteModalClose}
|
<CollectionNew onSubmit={handleCreateCollectionModalClose} />
|
||||||
isOpen={inviteModalOpen}
|
</Modal>
|
||||||
>
|
</DndProvider>
|
||||||
<Invite onSubmit={handleInviteModalClose} />
|
|
||||||
</Modal>
|
|
||||||
)}
|
)}
|
||||||
<Modal
|
|
||||||
title={t("Create a collection")}
|
|
||||||
onRequestClose={handleCreateCollectionModalClose}
|
|
||||||
isOpen={createCollectionModalOpen}
|
|
||||||
>
|
|
||||||
<CollectionNew onSubmit={handleCreateCollectionModalClose} />
|
|
||||||
</Modal>
|
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,153 +13,145 @@ 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>(
|
||||||
const [isCollapsing, setCollapsing] = React.useState(false);
|
({ children }: Props, ref) => {
|
||||||
const theme = useTheme();
|
const [isCollapsing, setCollapsing] = React.useState(false);
|
||||||
const { t } = useTranslation();
|
const theme = useTheme();
|
||||||
const { ui } = useStores();
|
const { t } = useTranslation();
|
||||||
const location = useLocation();
|
const { ui } = useStores();
|
||||||
const previousLocation = usePrevious(location);
|
const location = useLocation();
|
||||||
|
const previousLocation = usePrevious(location);
|
||||||
|
|
||||||
const width = ui.sidebarWidth;
|
const width = ui.sidebarWidth;
|
||||||
const collapsed = ui.isEditing || ui.sidebarCollapsed;
|
const collapsed = ui.isEditing || ui.sidebarCollapsed;
|
||||||
const maxWidth = theme.sidebarMaxWidth;
|
const maxWidth = theme.sidebarMaxWidth;
|
||||||
const minWidth = theme.sidebarMinWidth + 16; // padding
|
const minWidth = theme.sidebarMinWidth + 16; // padding
|
||||||
const setWidth = ui.setSidebarWidth;
|
const setWidth = ui.setSidebarWidth;
|
||||||
|
|
||||||
const [offset, setOffset] = React.useState(0);
|
const [offset, setOffset] = React.useState(0);
|
||||||
const [isAnimating, setAnimating] = React.useState(false);
|
const [isAnimating, setAnimating] = React.useState(false);
|
||||||
const [isResizing, setResizing] = React.useState(false);
|
const [isResizing, setResizing] = React.useState(false);
|
||||||
const isSmallerThanMinimum = width < minWidth;
|
const isSmallerThanMinimum = width < minWidth;
|
||||||
|
|
||||||
const handleDrag = React.useCallback(
|
const handleDrag = React.useCallback(
|
||||||
(event: MouseEvent) => {
|
(event: MouseEvent) => {
|
||||||
// suppresses text selection
|
// suppresses text selection
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// this is simple because the sidebar is always against the left edge
|
// this is simple because the sidebar is always against the left edge
|
||||||
const width = Math.min(event.pageX - offset, maxWidth);
|
const width = Math.min(event.pageX - offset, maxWidth);
|
||||||
const isSmallerThanCollapsePoint = width < minWidth / 2;
|
|
||||||
|
|
||||||
if (isSmallerThanCollapsePoint) {
|
|
||||||
setWidth(theme.sidebarCollapsedWidth);
|
|
||||||
} else {
|
|
||||||
setWidth(width);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[theme, offset, minWidth, maxWidth, setWidth]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleStopDrag = React.useCallback(
|
|
||||||
(event: MouseEvent) => {
|
|
||||||
setResizing(false);
|
|
||||||
|
|
||||||
if (document.activeElement) {
|
|
||||||
document.activeElement.blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSmallerThanMinimum) {
|
|
||||||
const isSmallerThanCollapsePoint = width < minWidth / 2;
|
const isSmallerThanCollapsePoint = width < minWidth / 2;
|
||||||
|
|
||||||
if (isSmallerThanCollapsePoint) {
|
if (isSmallerThanCollapsePoint) {
|
||||||
setAnimating(false);
|
setWidth(theme.sidebarCollapsedWidth);
|
||||||
setCollapsing(true);
|
|
||||||
ui.collapseSidebar();
|
|
||||||
} else {
|
} else {
|
||||||
setWidth(minWidth);
|
setWidth(width);
|
||||||
setAnimating(true);
|
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
setWidth(width);
|
[theme, offset, minWidth, maxWidth, setWidth]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleStopDrag = React.useCallback(
|
||||||
|
(event: MouseEvent) => {
|
||||||
|
setResizing(false);
|
||||||
|
|
||||||
|
if (document.activeElement) {
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSmallerThanMinimum) {
|
||||||
|
const isSmallerThanCollapsePoint = width < minWidth / 2;
|
||||||
|
|
||||||
|
if (isSmallerThanCollapsePoint) {
|
||||||
|
setAnimating(false);
|
||||||
|
setCollapsing(true);
|
||||||
|
ui.collapseSidebar();
|
||||||
|
} else {
|
||||||
|
setWidth(minWidth);
|
||||||
|
setAnimating(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setWidth(width);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ui, isSmallerThanMinimum, minWidth, width, setWidth]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleMouseDown = React.useCallback(
|
||||||
|
(event: MouseEvent) => {
|
||||||
|
setOffset(event.pageX - width);
|
||||||
|
setResizing(true);
|
||||||
|
setAnimating(false);
|
||||||
|
},
|
||||||
|
[width]
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (isAnimating) {
|
||||||
|
setTimeout(() => setAnimating(false), ANIMATION_MS);
|
||||||
}
|
}
|
||||||
},
|
}, [isAnimating]);
|
||||||
[ui, isSmallerThanMinimum, minWidth, width, setWidth]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMouseDown = React.useCallback(
|
React.useEffect(() => {
|
||||||
(event: MouseEvent) => {
|
if (isCollapsing) {
|
||||||
setOffset(event.pageX - width);
|
setTimeout(() => {
|
||||||
setResizing(true);
|
setWidth(minWidth);
|
||||||
setAnimating(false);
|
setCollapsing(false);
|
||||||
},
|
}, ANIMATION_MS);
|
||||||
[width]
|
}
|
||||||
);
|
}, [setWidth, minWidth, isCollapsing]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isAnimating) {
|
if (isResizing) {
|
||||||
setTimeout(() => setAnimating(false), ANIMATION_MS);
|
document.addEventListener("mousemove", handleDrag);
|
||||||
}
|
document.addEventListener("mouseup", handleStopDrag);
|
||||||
}, [isAnimating]);
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
return () => {
|
||||||
if (isCollapsing) {
|
document.removeEventListener("mousemove", handleDrag);
|
||||||
setTimeout(() => {
|
document.removeEventListener("mouseup", handleStopDrag);
|
||||||
setWidth(minWidth);
|
};
|
||||||
setCollapsing(false);
|
}, [isResizing, handleDrag, handleStopDrag]);
|
||||||
}, ANIMATION_MS);
|
|
||||||
}
|
|
||||||
}, [setWidth, minWidth, isCollapsing]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
const handleReset = React.useCallback(() => {
|
||||||
if (isResizing) {
|
ui.setSidebarWidth(theme.sidebarWidth);
|
||||||
document.addEventListener("mousemove", handleDrag);
|
}, [ui, theme.sidebarWidth]);
|
||||||
document.addEventListener("mouseup", handleStopDrag);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
React.useEffect(() => {
|
||||||
document.removeEventListener("mousemove", handleDrag);
|
ui.setSidebarResizing(isResizing);
|
||||||
document.removeEventListener("mouseup", handleStopDrag);
|
}, [ui, isResizing]);
|
||||||
};
|
|
||||||
}, [isResizing, handleDrag, handleStopDrag]);
|
|
||||||
|
|
||||||
const handleReset = React.useCallback(() => {
|
React.useEffect(() => {
|
||||||
ui.setSidebarWidth(theme.sidebarWidth);
|
if (location !== previousLocation) {
|
||||||
}, [ui, theme.sidebarWidth]);
|
isFirstRender = false;
|
||||||
|
ui.hideMobileSidebar();
|
||||||
|
}
|
||||||
|
}, [ui, location, previousLocation]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
const style = React.useMemo(
|
||||||
ui.setSidebarResizing(isResizing);
|
() => ({
|
||||||
}, [ui, isResizing]);
|
width: `${width}px`,
|
||||||
|
}),
|
||||||
|
[width]
|
||||||
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
const toggleStyle = React.useMemo(
|
||||||
if (location !== previousLocation) {
|
() => ({
|
||||||
ui.hideMobileSidebar();
|
right: "auto",
|
||||||
}
|
marginLeft: `${collapsed ? theme.sidebarCollapsedWidth : width}px`,
|
||||||
}, [ui, location, previousLocation]);
|
}),
|
||||||
|
[width, theme.sidebarCollapsedWidth, collapsed]
|
||||||
|
);
|
||||||
|
|
||||||
const style = React.useMemo(
|
const content = (
|
||||||
() => ({
|
<>
|
||||||
width: `${width}px`,
|
|
||||||
}),
|
|
||||||
[width]
|
|
||||||
);
|
|
||||||
|
|
||||||
const toggleStyle = React.useMemo(
|
|
||||||
() => ({
|
|
||||||
right: "auto",
|
|
||||||
marginLeft: `${collapsed ? theme.sidebarCollapsedWidth : width}px`,
|
|
||||||
}),
|
|
||||||
[width, theme.sidebarCollapsedWidth, collapsed]
|
|
||||||
);
|
|
||||||
|
|
||||||
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,26 +172,36 @@ function Sidebar({ children }: Props) {
|
||||||
aria-label={t("Expand")}
|
aria-label={t("Expand")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</>
|
||||||
{!ui.isEditing && (
|
);
|
||||||
<Toggle
|
|
||||||
style={toggleStyle}
|
|
||||||
onClick={ui.toggleCollapsedSidebar}
|
|
||||||
direction={ui.sidebarCollapsed ? "right" : "left"}
|
|
||||||
aria-label={ui.sidebarCollapsed ? t("Expand") : t("Collapse")}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fade in the sidebar on first render after page load
|
return (
|
||||||
if (firstRender) {
|
<>
|
||||||
firstRender = false;
|
<Container
|
||||||
return <Fade>{content}</Fade>;
|
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>
|
||||||
|
{!ui.isEditing && (
|
||||||
|
<Toggle
|
||||||
|
style={toggleStyle}
|
||||||
|
onClick={ui.toggleCollapsedSidebar}
|
||||||
|
direction={ui.sidebarCollapsed ? "right" : "left"}
|
||||||
|
aria-label={ui.sidebarCollapsed ? t("Expand") : t("Collapse")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Background = styled.a`
|
const Background = styled.a`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
22
app/index.js
22
app/index.js
|
@ -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,17 +45,15 @@ if (element) {
|
||||||
<Provider {...stores}>
|
<Provider {...stores}>
|
||||||
<Theme>
|
<Theme>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<DndProvider backend={HTML5Backend}>
|
<Router history={history}>
|
||||||
<Router history={history}>
|
<>
|
||||||
<>
|
<PageTheme />
|
||||||
<PageTheme />
|
<ScrollToTop>
|
||||||
<ScrollToTop>
|
<Routes />
|
||||||
<Routes />
|
</ScrollToTop>
|
||||||
</ScrollToTop>
|
<Toasts />
|
||||||
<Toasts />
|
</>
|
||||||
</>
|
</Router>
|
||||||
</Router>
|
|
||||||
</DndProvider>
|
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</Theme>
|
</Theme>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
|
|
|
@ -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",
|
||||||
|
|
67
yarn.lock
67
yarn.lock
|
@ -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==
|
||||||
|
|
Reference in New Issue