From 474fbf07e6d65346002a21cf8c0eec08ea1b9889 Mon Sep 17 00:00:00 2001 From: Nan Yu Date: Tue, 1 Dec 2020 21:59:18 -0800 Subject: [PATCH] chore: Flatten left nav in preparation to refactor drag to reorder (#1689) * flatten hierarchy * fix drop to import positioning on collections --- app/components/Sidebar/Sidebar.js | 2 +- .../Sidebar/components/CollectionLink.js | 90 +++--- .../Sidebar/components/Collections.js | 1 - .../Sidebar/components/DocumentLink.js | 261 +++++++++--------- .../Sidebar/components/SidebarLink.js | 81 ++---- 5 files changed, 199 insertions(+), 236 deletions(-) diff --git a/app/components/Sidebar/Sidebar.js b/app/components/Sidebar/Sidebar.js index 6941c5ca3..7f785a1f2 100644 --- a/app/components/Sidebar/Sidebar.js +++ b/app/components/Sidebar/Sidebar.js @@ -26,7 +26,7 @@ function Sidebar({ location, children }: Props) { if (location !== previousLocation) { ui.hideMobileSidebar(); } - }, [ui, location]); + }, [ui, location, previousLocation]); const content = ( Promise, |}; @@ -34,61 +31,62 @@ class CollectionLink extends React.Component { render() { const { collection, - documents, activeDocument, prefetchDocument, canUpdate, ui, } = this.props; + const expanded = collection.id === ui.activeCollectionId; return ( - - + } - iconColor={collection.color} - expanded={expanded} - hideDisclosure - menuOpen={this.menuOpen} - label={ - - } - exact={false} - menu={ - (this.menuOpen = true)} - onClose={() => (this.menuOpen = false)} - /> - } + collectionId={collection.id} + activeClassName="activeDropZone" > - - {collection.documents.map((node) => ( - + } + iconColor={collection.color} + expanded={expanded} + menuOpen={this.menuOpen} + label={ + - ))} - - - + } + exact={false} + menu={ + (this.menuOpen = true)} + onClose={() => (this.menuOpen = false)} + /> + } + > + + + {expanded && + collection.documents.map((node) => ( + + ))} + ); } } diff --git a/app/components/Sidebar/components/Collections.js b/app/components/Sidebar/components/Collections.js index 1a6ee1f5d..d74e54245 100644 --- a/app/components/Sidebar/components/Collections.js +++ b/app/components/Sidebar/components/Collections.js @@ -61,7 +61,6 @@ class Collections extends React.Component { {collections.orderedData.map((collection) => ( void, prefetchDocument: (documentId: string) => Promise, depth: number, - t: TFunction, |}; -@observer -class DocumentLink extends React.Component { - @observable menuOpen = false; +function DocumentLink({ + node, + collection, + activeDocument, + activeDocumentRef, + prefetchDocument, + depth, + canUpdate, +}: Props) { + const { documents } = useStores(); + const { t } = useTranslation(); - componentDidMount() { - if (this.isActiveDocument() && this.hasChildDocuments()) { - this.props.documents.fetchChildDocuments(this.props.node.id); + const isActiveDocument = activeDocument && activeDocument.id === node.id; + const hasChildDocuments = !!node.children.length; + + const document = documents.get(node.id); + const { fetchChildDocuments } = documents; + + React.useEffect(() => { + if (isActiveDocument && hasChildDocuments) { + fetchChildDocuments(node.id); } - } + }, [fetchChildDocuments, node, hasChildDocuments, isActiveDocument]); - componentDidUpdate(prevProps: Props) { - if (prevProps.activeDocument !== this.props.activeDocument) { - if (this.isActiveDocument() && this.hasChildDocuments()) { - this.props.documents.fetchChildDocuments(this.props.node.id); - } - } - } - - handleMouseEnter = (ev: SyntheticEvent<>) => { - const { node, prefetchDocument } = this.props; - - ev.stopPropagation(); - ev.preventDefault(); - prefetchDocument(node.id); - }; - - handleTitleChange = async (title: string) => { - const document = this.props.documents.get(this.props.node.id); - if (!document) return; - - await this.props.documents.update({ - id: document.id, - lastRevision: document.revision, - text: document.text, - title, - }); - }; - - isActiveDocument = () => { - return ( - this.props.activeDocument && - this.props.activeDocument.id === this.props.node.id - ); - }; - - hasChildDocuments = () => { - return !!this.props.node.children.length; - }; - - render() { - const { - node, - documents, - collection, - activeDocument, - activeDocumentRef, - prefetchDocument, - depth, - canUpdate, - t, - } = this.props; - - const showChildren = !!( + const showChildren = React.useMemo(() => { + return !!( + hasChildDocuments && activeDocument && collection && (collection .pathToDocument(activeDocument) .map((entry) => entry.id) .includes(node.id) || - this.isActiveDocument()) + isActiveDocument) ); - const document = documents.get(node.id); - const title = node.title || t("Untitled"); + }, [hasChildDocuments, activeDocument, isActiveDocument, node, collection]); - return ( - - - { + if (showChildren) { + setExpanded(showChildren); + } + }, [showChildren]); + + const handleDisclosureClick = React.useCallback( + (ev: SyntheticEvent<>) => { + ev.preventDefault(); + ev.stopPropagation(); + setExpanded(!expanded); + }, + [expanded] + ); + + const handleMouseEnter = React.useCallback( + (ev: SyntheticEvent<>) => { + prefetchDocument(node.id); + }, + [prefetchDocument, node] + ); + + const handleTitleChange = React.useCallback( + async (title: string) => { + if (!document) return; + + await documents.update({ + id: document.id, + lastRevision: document.revision, + text: document.text, + title, + }); + }, + [documents, document] + ); + + const [menuOpen, setMenuOpen] = React.useState(false); + + return ( + + + + {hasChildDocuments && ( + + )} - } - depth={depth} - exact={false} - menuOpen={this.menuOpen} - menu={ - document ? ( - - (this.menuOpen = true)} - onClose={() => (this.menuOpen = false)} - /> - - ) : undefined - } - > - {this.hasChildDocuments() && ( - - {node.children.map((childNode) => ( - - ))} - - )} - - - - ); - } + + } + depth={depth} + exact={false} + menuOpen={menuOpen} + menu={ + document ? ( + + setMenuOpen(true)} + onClose={() => setMenuOpen(false)} + /> + + ) : undefined + } + > + + + {expanded && ( + <> + {node.children.map((childNode) => ( + + ))} + + )} + + ); } -const DocumentChildren = styled(Flex)``; +const Disclosure = styled(CollapsedIcon)` + position: absolute; + left: -24px; -export default withTranslation()(DocumentLink); + ${({ expanded }) => !expanded && "transform: rotate(-90deg);"}; +`; + +const ObservedDocumentLink = observer(DocumentLink); +export default ObservedDocumentLink; diff --git a/app/components/Sidebar/components/SidebarLink.js b/app/components/Sidebar/components/SidebarLink.js index b56ac8c51..a205dff8a 100644 --- a/app/components/Sidebar/components/SidebarLink.js +++ b/app/components/Sidebar/components/SidebarLink.js @@ -1,23 +1,20 @@ // @flow -import { observer } from "mobx-react"; -import { CollapsedIcon } from "outline-icons"; import * as React from "react"; import { withRouter, NavLink } from "react-router-dom"; import styled, { withTheme } from "styled-components"; -import Flex from "components/Flex"; import { type Theme } from "types"; type Props = { to?: string | Object, href?: string | Object, + innerRef?: (?HTMLElement) => void, onClick?: (SyntheticEvent<>) => void, + onMouseEnter?: (SyntheticEvent<>) => void, children?: React.Node, icon?: React.Node, - expanded?: boolean, label?: React.Node, menu?: React.Node, menuOpen?: boolean, - hideDisclosure?: boolean, iconColor?: string, active?: boolean, theme: Theme, @@ -29,46 +26,25 @@ function SidebarLink({ icon, children, onClick, + onMouseEnter, to, label, active, menu, menuOpen, - hideDisclosure, theme, exact, href, + innerRef, depth, ...rest }: Props) { - const [expanded, setExpanded] = React.useState(rest.expanded); - const style = React.useMemo(() => { return { paddingLeft: `${(depth || 0) * 16 + 16}px`, }; }, [depth]); - React.useEffect(() => { - if (rest.expanded !== undefined) { - setExpanded(rest.expanded); - } - }, [rest.expanded]); - - const handleClick = React.useCallback( - (ev: SyntheticEvent<>) => { - ev.preventDefault(); - ev.stopPropagation(); - setExpanded(!expanded); - }, - [expanded] - ); - - const handleExpand = React.useCallback(() => { - setExpanded(true); - }, []); - - const showDisclosure = !!children && !hideDisclosure; const activeStyle = { color: theme.text, background: theme.sidebarItemBackground, @@ -77,27 +53,21 @@ function SidebarLink({ }; return ( - - - {icon && {icon}} - - {menu && {menu}} - - {expanded && children} - + + {icon && {icon}} + + {menu && {menu}} + ); } @@ -153,10 +123,6 @@ const StyledNavLink = styled(NavLink)` } `; -const Wrapper = styled(Flex)` - position: relative; -`; - const Label = styled.div` position: relative; width: 100%; @@ -164,11 +130,4 @@ const Label = styled.div` line-height: 1.6; `; -const Disclosure = styled(CollapsedIcon)` - position: absolute; - left: -24px; - - ${({ expanded }) => !expanded && "transform: rotate(-90deg);"}; -`; - -export default withRouter(withTheme(observer(SidebarLink))); +export default withRouter(withTheme(SidebarLink));