Adding new doc menu to sidebar items

This commit is contained in:
Tom Moor 2018-05-05 22:45:10 -07:00
parent d55a8ad02d
commit 79c9582020
6 changed files with 238 additions and 212 deletions

View File

@ -0,0 +1,95 @@
// @flow
import * as React from 'react';
import { observer } from 'mobx-react';
import { observable } from 'mobx';
import { CollectionIcon } from 'outline-icons';
import styled from 'styled-components';
import Collection from 'models/Collection';
import Document from 'models/Document';
import CollectionMenu from 'menus/CollectionMenu';
import UiStore from 'stores/UiStore';
import SidebarLink from './SidebarLink';
import DocumentLink from './DocumentLink';
import DropToImport from 'components/DropToImport';
import Flex from 'shared/components/Flex';
type Props = {
history: Object,
collection: Collection,
ui: UiStore,
activeDocument: ?Document,
prefetchDocument: (id: string) => Promise<void>,
};
@observer
class CollectionLink extends React.Component<Props> {
@observable menuOpen = false;
render() {
const {
history,
collection,
activeDocument,
prefetchDocument,
ui,
} = this.props;
const expanded = collection.id === ui.activeCollectionId;
return (
<DropToImport
key={collection.id}
history={history}
collectionId={collection.id}
activeClassName="activeDropZone"
menuOpen={this.menuOpen}
>
<SidebarLink
key={collection.id}
to={collection.url}
icon={<CollectionIcon expanded={expanded} color={collection.color} />}
iconColor={collection.color}
expand={expanded}
hideExpandToggle
expandedContent={
<CollectionChildren column>
{collection.documents.map(document => (
<DocumentLink
key={document.id}
history={history}
document={document}
activeDocument={activeDocument}
prefetchDocument={prefetchDocument}
depth={0}
/>
))}
</CollectionChildren>
}
menu={
<CollectionMenu
history={history}
collection={collection}
onOpen={() => (this.menuOpen = true)}
onClose={() => (this.menuOpen = false)}
/>
}
>
<CollectionName justify="space-between">
{collection.name}
</CollectionName>
</SidebarLink>
</DropToImport>
);
}
}
const CollectionName = styled(Flex)`
padding: 0 0 4px;
`;
const CollectionChildren = styled(Flex)`
margin-top: -4px;
margin-left: 36px;
padding-bottom: 4px;
`;
export default CollectionLink;

View File

@ -1,24 +1,17 @@
// @flow
import * as React from 'react';
import { observable } from 'mobx';
import { observer, inject } from 'mobx-react';
import type { Location } from 'react-router-dom';
import Flex from 'shared/components/Flex';
import styled from 'styled-components';
import { color } from 'shared/styles/constants';
import { PlusIcon, CollectionIcon } from 'outline-icons';
import { PlusIcon } from 'outline-icons';
import Header from './Header';
import SidebarLink from './SidebarLink';
import DropToImport from 'components/DropToImport';
import CollectionMenu from 'menus/CollectionMenu';
import CollectionLink from './CollectionLink';
import CollectionsStore from 'stores/CollectionsStore';
import UiStore from 'stores/UiStore';
import Document from 'models/Document';
import Collection from 'models/Collection';
import DocumentsStore from 'stores/DocumentsStore';
import { type NavigationNode } from 'types';
type Props = {
history: Object,
@ -62,201 +55,4 @@ class Collections extends React.Component<Props> {
}
}
type CollectionLinkProps = {
history: Object,
collection: Collection,
ui: UiStore,
activeDocument: ?Document,
prefetchDocument: (id: string) => Promise<void>,
};
@observer
class CollectionLink extends React.Component<*> {
props: CollectionLinkProps;
@observable menuOpen = false;
renderDocuments() {
const {
history,
collection,
activeDocument,
prefetchDocument,
} = this.props;
return (
<CollectionChildren column>
{collection.documents.map(document => (
<DocumentLink
key={document.id}
history={history}
document={document}
activeDocument={activeDocument}
prefetchDocument={prefetchDocument}
depth={0}
/>
))}
</CollectionChildren>
);
}
render() {
const { history, collection, ui } = this.props;
const expanded = collection.id === ui.activeCollectionId;
return (
<StyledDropToImport
key={collection.id}
history={history}
collectionId={collection.id}
activeClassName="activeDropZone"
menuOpen={this.menuOpen}
>
<SidebarLink
key={collection.id}
to={collection.url}
icon={<CollectionIcon expanded={expanded} color={collection.color} />}
iconColor={collection.color}
expandedContent={this.renderDocuments()}
hideExpandToggle
expand={expanded}
>
<CollectionName justify="space-between">
{collection.name}
</CollectionName>
</SidebarLink>
<CollectionAction>
<CollectionMenu
history={history}
collection={collection}
onOpen={() => (this.menuOpen = true)}
onClose={() => (this.menuOpen = false)}
/>
</CollectionAction>
</StyledDropToImport>
);
}
}
type DocumentLinkProps = {
document: NavigationNode,
history: Object,
activeDocument: ?Document,
activeDocumentRef: HTMLElement => void,
prefetchDocument: (documentId: string) => void,
depth: number,
};
const DocumentLink = observer(
({
document,
activeDocument,
activeDocumentRef,
prefetchDocument,
depth,
history,
}: DocumentLinkProps) => {
const isActiveDocument =
activeDocument && activeDocument.id === document.id;
const showChildren = !!(
activeDocument &&
(activeDocument.pathToDocument
.map(entry => entry.id)
.includes(document.id) ||
isActiveDocument)
);
const handleMouseEnter = (event: SyntheticEvent<*>) => {
event.stopPropagation();
event.preventDefault();
prefetchDocument(document.id);
};
return (
<Flex
column
key={document.id}
innerRef={isActiveDocument ? activeDocumentRef : undefined}
onMouseEnter={handleMouseEnter}
>
<DropToImport
history={history}
documentId={document.id}
activeClassName="activeDropZone"
>
<SidebarLink
to={document.url}
expand={showChildren}
expandedContent={
document.children.length ? (
<DocumentChildren column>
{document.children.map(childDocument => (
<DocumentLink
key={childDocument.id}
history={history}
document={childDocument}
activeDocument={activeDocument}
prefetchDocument={prefetchDocument}
depth={depth + 1}
/>
))}
</DocumentChildren>
) : (
undefined
)
}
>
{document.title}
</SidebarLink>
</DropToImport>
</Flex>
);
}
);
const CollectionName = styled(Flex)`
padding: 0 0 4px;
`;
const CollectionAction = styled.span`
position: absolute;
right: 0;
top: 0;
color: ${color.slate};
svg {
opacity: 0.75;
}
&:hover {
svg {
opacity: 1;
}
}
`;
const StyledDropToImport = styled(DropToImport)`
position: relative;
${CollectionAction} {
display: ${props => (props.menuOpen ? 'inline' : 'none')};
}
&:hover {
${CollectionAction} {
display: inline;
}
}
`;
const CollectionChildren = styled(Flex)`
margin-top: -4px;
margin-left: 36px;
padding-bottom: 4px;
`;
const DocumentChildren = styled(Flex)`
margin-top: -4px;
margin-left: 12px;
`;
export default inject('collections', 'ui', 'documents')(Collections);

View File

@ -0,0 +1,99 @@
// @flow
import * as React from 'react';
import { observer } from 'mobx-react';
import styled from 'styled-components';
import Document from 'models/Document';
import DocumentMenu from 'menus/DocumentMenu';
import SidebarLink from './SidebarLink';
import DropToImport from 'components/DropToImport';
import Flex from 'shared/components/Flex';
import { type NavigationNode } from 'types';
type Props = {
document: NavigationNode,
history: Object,
activeDocument: ?Document,
activeDocumentRef?: HTMLElement => void,
prefetchDocument: (documentId: string) => Promise<void>,
depth: number,
};
@observer
class DocumentLink extends React.Component<Props> {
handleMouseEnter = (ev: SyntheticEvent<*>) => {
const { document, prefetchDocument } = this.props;
ev.stopPropagation();
ev.preventDefault();
prefetchDocument(document.id);
};
render() {
const {
document,
activeDocument,
activeDocumentRef,
prefetchDocument,
depth,
history,
} = this.props;
const isActiveDocument =
activeDocument && activeDocument.id === document.id;
const showChildren = !!(
activeDocument &&
(activeDocument.pathToDocument
.map(entry => entry.id)
.includes(document.id) ||
isActiveDocument)
);
return (
<Flex
column
key={document.id}
innerRef={isActiveDocument ? activeDocumentRef : undefined}
onMouseEnter={this.handleMouseEnter}
>
<DropToImport
history={history}
documentId={document.id}
activeClassName="activeDropZone"
>
<SidebarLink
to={document.url}
expand={showChildren}
expandedContent={
document.children.length ? (
<DocumentChildren column>
{document.children.map(childDocument => (
<DocumentLink
key={childDocument.id}
history={history}
document={childDocument}
activeDocument={activeDocument}
prefetchDocument={prefetchDocument}
depth={depth + 1}
/>
))}
</DocumentChildren>
) : (
undefined
)
}
menu={<DocumentMenu document={document} />}
>
{document.title}
</SidebarLink>
</DropToImport>
</Flex>
);
}
}
const DocumentChildren = styled(Flex)`
margin-top: -4px;
margin-left: 12px;
`;
export default DocumentLink;

View File

@ -52,6 +52,7 @@ type Props = {
icon?: React.Node,
expand?: boolean,
expandedContent?: React.Node,
menu?: React.Node,
hideExpandToggle?: boolean,
iconColor?: string,
active?: boolean,
@ -90,13 +91,14 @@ class SidebarLink extends React.Component<Props> {
expandedContent,
expand,
active,
menu,
hideExpandToggle,
} = this.props;
const Component = to ? StyledNavLink : StyledDiv;
const showExpandIcon = expandedContent && !hideExpandToggle;
return (
<Flex column>
<Wrapper column>
<Component
iconVisible={showExpandIcon}
activeStyle={activeStyle}
@ -113,11 +115,42 @@ class SidebarLink extends React.Component<Props> {
</Component>
{/* Collection */ expand && hideExpandToggle && expandedContent}
{/* Document */ this.expanded && !hideExpandToggle && expandedContent}
</Flex>
{menu && <Action>{menu}</Action>}
</Wrapper>
);
}
}
const Action = styled.span`
position: absolute;
right: 0;
top: 0;
color: ${color.slate};
svg {
opacity: 0.75;
}
&:hover {
svg {
opacity: 1;
}
}
`;
const Wrapper = styled(Flex)`
position: relative;
> ${Action} {
display: ${props => (props.menuOpen ? 'inline' : 'none')};
}
&:hover {
> ${Action} {
display: inline;
}
}
`;
const Content = styled.div`
width: 100%;
`;

View File

@ -15,6 +15,7 @@ type Props = {
history: Object,
document: Document,
className: string,
showPrint?: boolean,
};
@observer
@ -56,7 +57,7 @@ class DocumentMenu extends React.Component<Props> {
};
render() {
const { document, label, className } = this.props;
const { document, label, className, showPrint } = this.props;
const isDraft = !document.publishedAt;
return (
@ -84,7 +85,7 @@ class DocumentMenu extends React.Component<Props> {
onClick={this.handleNewChild}
title="Create a new child document for the current document"
>
New child
New child document
</DropdownMenuItem>
<DropdownMenuItem onClick={this.handleMove}>Move</DropdownMenuItem>
</React.Fragment>
@ -94,7 +95,9 @@ class DocumentMenu extends React.Component<Props> {
<DropdownMenuItem onClick={this.handleExport}>
Download
</DropdownMenuItem>
<DropdownMenuItem onClick={window.print}>Print</DropdownMenuItem>
{showPrint && (
<DropdownMenuItem onClick={window.print}>Print</DropdownMenuItem>
)}
</DropdownMenu>
);
}

View File

@ -98,7 +98,7 @@ class DocumentActions extends React.Component<Props> {
)}
{!isEditing && (
<Action>
<DocumentMenu document={document} />
<DocumentMenu document={document} showPrint />
</Action>
)}
{!isEditing &&