Compare commits

...

27 Commits

Author SHA1 Message Date
Tom Moor d6ba98b262 stash 2020-11-08 18:10:49 -08:00
Tom Moor 4fc52b9f3d Merge branch 'develop' of github.com:outline/outline into Agilo-feature/reordering-documents-in-collection 2020-11-08 15:58:20 -08:00
Tom Moor 619b369295 Merge fix 2020-11-02 19:35:00 -08:00
Tom Moor 6f4cdc9359 Merge develop 2020-11-01 20:57:27 -08:00
Tom Moor 4fb5022324 Merge develop 2020-09-08 20:05:13 -07:00
Ante Primorac 70e79a5264 fix: collection documents property 2020-08-31 16:08:11 +02:00
Ante Primorac da3d08566e chore: fix prettier issues after merge 2020-08-31 15:41:25 +02:00
Ante Primorac 44a484e456 Merge remote-tracking branch 'upstream/develop' into feature/reordering-documents-in-collection 2020-08-31 15:35:25 +02:00
Ante Primorac 75e6734388 improve D'n'D documents UX in sidebar 2020-07-06 15:34:57 +02:00
Ante Primorac a6ee913211 expand collections and documents when dragging over them 2020-07-06 11:54:55 +02:00
Ante Primorac 525d34a1f7 Merge branch 'develop' into feature/reordering-documents-in-collection 2020-07-05 18:28:14 +02:00
Ante Primorac 6dde45cee9 move sidebarlink into droppable 2020-07-05 18:19:43 +02:00
Ante Primorac 3b07a53093 chore: fix prettier issues 2020-07-05 12:12:40 +02:00
Ante Primorac b25a98c114 Merge remote-tracking branch 'upstream/develop' into feature/reordering-documents-in-collection 2020-07-05 12:08:02 +02:00
Ante Primorac 2ae30a36cd Merge remote-tracking branch 'upstream/master' into feature/reordering-documents-in-collection 2020-06-08 18:57:43 +02:00
Ante Primorac b2e6a1a4f6 feat: disable moving document to itself 2020-05-25 17:01:09 +02:00
Ante Primorac d48a1eed35 Merge remote-tracking branch 'upstream/master' into feature/reordering-documents-in-collection 2020-05-25 16:09:04 +02:00
Ante Primorac a55d95c7b2 feat: move document to another parent document or collection 2020-05-25 15:37:33 +02:00
Ante Primorac 5d2be0a199 fix flow issues introduced in documents reordering feature 2020-05-24 23:12:33 +02:00
Ante Primorac 8911020f34 Merge remote-tracking branch 'upstream/master' into feature/reordering-documents-in-collection 2020-05-24 22:52:57 +02:00
Ante Primorac 3864443eae drag'n'drop reorder documents in the client store 2020-05-24 22:48:00 +02:00
Ante Primorac 660e9682c5 use empty array if collection's documentStructure is falsy 2020-05-24 22:41:23 +02:00
Ante Primorac ca36db1ac3 Merge remote-tracking branch 'upstream/master' into feature/reordering-documents-in-collection 2020-05-23 00:12:45 +02:00
Ante Primorac ff87da523f add drag'n'drop to CollectionLink and DocumentLink sidebar components 2020-05-23 00:11:20 +02:00
Ante Primorac 633ccc0800 add index argument to DocumentsStore.move 2020-05-23 00:10:05 +02:00
Ante Primorac 755ceab7dd remove natural sorting for documents 2020-05-23 00:09:36 +02:00
Ante Primorac e55290f118 add react-beautiful-dnd package 2020-05-23 00:09:03 +02:00
15 changed files with 1515 additions and 1035 deletions
+1
View File
@@ -91,6 +91,7 @@ class DropToImport extends React.Component<Props> {
match,
history,
staticContext,
ui,
...rest
} = this.props;
@@ -1,6 +1,6 @@
// @flow
import { observable } from "mobx";
import { observer } from "mobx-react";
import { observer, Observer } from "mobx-react";
import * as React from "react";
import DocumentsStore from "stores/DocumentsStore";
import UiStore from "stores/UiStore";
@@ -9,7 +9,10 @@ import Document from "models/Document";
import CollectionIcon from "components/CollectionIcon";
import DropToImport from "components/DropToImport";
import Flex from "components/Flex";
import { SidebarDnDContext } from "./Collections";
import DocumentLink from "./DocumentLink";
import Draggable from "./Draggable";
import Droppable from "./Droppable";
import EditableTitle from "./EditableTitle";
import SidebarLink from "./SidebarLink";
import CollectionMenu from "menus/CollectionMenu";
@@ -48,46 +51,66 @@ class CollectionLink extends React.Component<Props> {
collectionId={collection.id}
activeClassName="activeDropZone"
>
<SidebarLink
key={collection.id}
to={collection.url}
icon={<CollectionIcon collection={collection} expanded={expanded} />}
iconColor={collection.color}
expanded={expanded}
hideDisclosure
menuOpen={this.menuOpen}
label={
<EditableTitle
title={collection.name}
onSubmit={this.handleTitleChange}
canUpdate={canUpdate}
/>
}
exact={false}
menu={
<CollectionMenu
position="right"
collection={collection}
onOpen={() => (this.menuOpen = true)}
onClose={() => (this.menuOpen = false)}
/>
}
>
<Flex column>
{collection.documents.map((node) => (
<DocumentLink
key={node.id}
node={node}
documents={documents}
collection={collection}
activeDocument={activeDocument}
prefetchDocument={prefetchDocument}
canUpdate={canUpdate}
depth={1.5}
/>
))}
</Flex>
</SidebarLink>
<SidebarDnDContext.Consumer>
{({ draggingDocumentId, isDragging }) => (
<Droppable collectionId={collection.id}>
{(provided, snapshot) => (
<SidebarLink
key={collection.id}
to={collection.url}
icon={
<CollectionIcon
collection={collection}
expanded={expanded}
/>
}
iconColor={collection.color}
expanded={
isDragging ? expanded || snapshot.isDraggingOver : expanded
}
hideDisclosure
menuOpen={this.menuOpen}
label={
<EditableTitle
title={collection.name}
onSubmit={this.handleTitleChange}
canUpdate={canUpdate}
/>
}
exact={false}
menu={
<CollectionMenu
position="right"
collection={collection}
onOpen={() => (this.menuOpen = true)}
onClose={() => (this.menuOpen = false)}
/>
}
>
<Flex column>
<Observer>
{() =>
collection.documents.map((node, index) => (
<DocumentLink
key={node.id}
node={node}
index={index}
documents={documents}
collection={collection}
activeDocument={activeDocument}
prefetchDocument={prefetchDocument}
canUpdate={canUpdate}
depth={1.5}
/>
))
}
</Observer>
</Flex>
</SidebarLink>
)}
</Droppable>
)}
</SidebarDnDContext.Consumer>
</DropToImport>
);
}
+161 -14
View File
@@ -2,6 +2,8 @@
import { observer, inject } from "mobx-react";
import { PlusIcon } from "outline-icons";
import * as React from "react";
import type { DropResult, BeforeCapture } from "react-beautiful-dnd";
import { DragDropContext } from "react-beautiful-dnd";
import keydown from "react-keydown";
import { withRouter, type RouterHistory } from "react-router-dom";
@@ -15,8 +17,28 @@ import CollectionLink from "./CollectionLink";
import CollectionsLoading from "./CollectionsLoading";
import Header from "./Header";
import SidebarLink from "./SidebarLink";
import {
DROPPABLE_COLLECTION_SUFFIX,
DROPPABLE_DOCUMENT_SUFFIX,
DROPPABLE_DOCUMENT_SEPARATOR,
} from "utils/dnd";
import { newDocumentUrl } from "utils/routeHelpers";
type SidebarDnDContextObject = {
isDragging: boolean,
draggingDocumentId?: string,
};
const initialSidebarDnDContextValue: SidebarDnDContextObject = {
isDragging: false,
draggingDocumentId: undefined,
};
//$FlowFixMe
export const SidebarDnDContext = React.createContext(
initialSidebarDnDContextValue
);
type Props = {
history: RouterHistory,
policies: PoliciesStore,
@@ -26,8 +48,16 @@ type Props = {
ui: UiStore,
};
type State = {
draggingDocumentId?: string,
isDragging: boolean,
};
@observer
class Collections extends React.Component<Props> {
class Collections extends React.Component<Props, State> {
state: State = {
isDragging: false,
};
isPreloaded: boolean = !!this.props.collections.orderedData.length;
componentDidMount() {
@@ -51,22 +81,139 @@ class Collections extends React.Component<Props> {
this.props.history.push(newDocumentUrl(activeCollectionId));
}
getDroppableIdParts(droppableId: string) {
let collection, parentDocumentId;
const { collections } = this.props;
if (droppableId.indexOf(DROPPABLE_COLLECTION_SUFFIX) === 0) {
collection = collections.get(
droppableId.substring(DROPPABLE_COLLECTION_SUFFIX.length)
);
} else if (
droppableId.indexOf(DROPPABLE_DOCUMENT_SUFFIX) === 0 &&
droppableId.indexOf(DROPPABLE_DOCUMENT_SEPARATOR)
) {
const [documentId, collectionId] = droppableId
.substring(DROPPABLE_DOCUMENT_SUFFIX.length)
.split(DROPPABLE_DOCUMENT_SEPARATOR);
parentDocumentId = documentId;
collection = collections.get(collectionId);
}
return {
collection,
parentDocumentId,
};
}
handleBeforeCapture = (before: BeforeCapture) => {
this.setState({
isDragging: true,
draggingDocumentId: before.draggableId,
});
};
reorder = (result: DropResult) => {
this.setState({
isDragging: false,
draggingDocumentId: undefined,
});
// Bail out early if result doesn't have a destination or combine data
if (!result.destination && !result.combine) {
return;
}
// Bail out early if no changes
if (
(result.destination &&
result.destination.droppableId === result.source.droppableId &&
result.destination.index === result.source.index) ||
(result.combine && result.combine.draggableId === result.draggableId)
) {
return;
}
const { documents } = this.props;
const document = documents.get(result.draggableId);
let collection,
parentDocumentId,
index = 0;
// Bail out if document doesn't exist
if (!document) {
return;
}
if (result.destination) {
index = result.destination.index;
const droppableId = result.destination.droppableId;
const parts = this.getDroppableIdParts(droppableId);
collection = parts.collection;
parentDocumentId = parts.parentDocumentId;
} else if (result.combine) {
const { draggableId, droppableId } = result.combine;
const parts = this.getDroppableIdParts(droppableId);
collection = parts.collection;
parentDocumentId = draggableId;
}
// Bail out if collection doesn't exist
if (!collection) {
return;
}
if (parentDocumentId) {
// Bail out if moving document to itself
if (parentDocumentId === document.id) {
return;
}
const parentDocument = documents.get(parentDocumentId);
// Bail out if parent document doesn't exist
if (!parentDocument) {
return;
}
}
documents.move(document, collection.id, parentDocumentId, index);
};
render() {
const { collections, ui, policies, documents } = this.props;
const { draggingDocumentId, isDragging } = this.state;
const content = (
<>
{collections.orderedData.map((collection) => (
<CollectionLink
key={collection.id}
documents={documents}
collection={collection}
activeDocument={documents.active}
prefetchDocument={documents.prefetchDocument}
canUpdate={policies.abilities(collection.id).update}
ui={ui}
/>
))}
<React.Fragment>
<DragDropContext
onBeforeCapture={this.handleBeforeCapture}
onDragEnd={this.reorder}
>
<SidebarDnDContext.Provider
value={{
draggingDocumentId,
isDragging,
}}
>
{collections.orderedData.map((collection) => (
<CollectionLink
key={collection.id}
documents={documents}
collection={collection}
activeDocument={documents.active}
prefetchDocument={documents.prefetchDocument}
canUpdate={policies.abilities(collection.id).update}
ui={ui}
/>
))}
<div id="sidebar-collections-portal" />
</SidebarDnDContext.Provider>
</DragDropContext>
<SidebarLink
to="/collections"
onClick={this.props.onCreateCollection}
@@ -74,7 +221,7 @@ class Collections extends React.Component<Props> {
label="New collection…"
exact
/>
</>
</React.Fragment>
);
return (
@@ -1,6 +1,6 @@
// @flow
import { observable } from "mobx";
import { observer } from "mobx-react";
import { observer, Observer } from "mobx-react";
import * as React from "react";
import styled from "styled-components";
import DocumentsStore from "stores/DocumentsStore";
@@ -9,6 +9,8 @@ import Document from "models/Document";
import DropToImport from "components/DropToImport";
import Fade from "components/Fade";
import Flex from "components/Flex";
import { SidebarDnDContext } from "./Collections";
import Draggable from "./Draggable";
import EditableTitle from "./EditableTitle";
import SidebarLink from "./SidebarLink";
import DocumentMenu from "menus/DocumentMenu";
@@ -17,12 +19,15 @@ import { type NavigationNode } from "types";
type Props = {|
node: NavigationNode,
documents: DocumentsStore,
collection: Collection,
canUpdate: boolean,
collection?: Collection,
collection: Collection,
index?: number,
activeDocument: ?Document,
activeDocumentRef?: (?HTMLElement) => void,
prefetchDocument: (documentId: string) => Promise<void>,
depth: number,
isDropDisabled?: boolean,
|};
@observer
@@ -83,6 +88,8 @@ class DocumentLink extends React.Component<Props> {
activeDocumentRef,
prefetchDocument,
depth,
index,
isDropDisabled,
canUpdate,
} = this.props;
@@ -98,62 +105,78 @@ class DocumentLink extends React.Component<Props> {
const document = documents.get(node.id);
const title = node.title || "Untitled";
let hideDisclosure;
if (!this.hasChildDocuments()) {
hideDisclosure = true;
}
return (
<Flex
column
key={node.id}
ref={this.isActiveDocument() ? activeDocumentRef : undefined}
onMouseEnter={this.handleMouseEnter}
>
<DropToImport documentId={node.id} activeClassName="activeDropZone">
<SidebarLink
to={{
pathname: node.url,
state: { title: node.title },
}}
expanded={showChildren ? true : undefined}
label={
<EditableTitle
title={title}
onSubmit={this.handleTitleChange}
canUpdate={canUpdate}
/>
}
depth={depth}
exact={false}
menuOpen={this.menuOpen}
menu={
document ? (
<Fade>
<DocumentMenu
position="right"
document={document}
onOpen={() => (this.menuOpen = true)}
onClose={() => (this.menuOpen = false)}
/>
</Fade>
) : undefined
}
>
{this.hasChildDocuments() && (
<DocumentChildren column>
{node.children.map((childNode) => (
<DocumentLink
key={childNode.id}
collection={collection}
node={childNode}
documents={documents}
activeDocument={activeDocument}
prefetchDocument={prefetchDocument}
depth={depth + 1}
<>
{/* <DropToImport documentId={node.id} activeClassName="activeDropZone"> */}
<SidebarDnDContext.Consumer>
{({ draggingDocumentId, isDragging }) => {
const disableChildDrops =
isDropDisabled || draggingDocumentId === node.id;
return (
<SidebarLink
to={{
pathname: node.url,
state: { title: node.title },
}}
expanded={showChildren ? true : undefined}
hideDisclosure={hideDisclosure}
label={
<EditableTitle
title={title}
onSubmit={this.handleTitleChange}
canUpdate={canUpdate}
/>
))}
</DocumentChildren>
)}
</SidebarLink>
</DropToImport>
</Flex>
}
depth={depth}
exact={false}
menuOpen={this.menuOpen}
menu={
document ? (
<Fade>
<DocumentMenu
position="right"
document={document}
onOpen={() => (this.menuOpen = true)}
onClose={() => (this.menuOpen = false)}
/>
</Fade>
) : undefined
}
index={index}
draggableId={node.id}
>
{this.hasChildDocuments() && !disableChildDrops && (
<Observer>
{() =>
node.children.map((childNode, index) => (
<DocumentLink
key={childNode.id}
index={index}
collection={collection}
node={childNode}
documents={documents}
activeDocument={activeDocument}
prefetchDocument={prefetchDocument}
canUpdate={canUpdate}
depth={depth + 1}
isDropDisabled={disableChildDrops}
/>
))
}
</Observer>
)}
</SidebarLink>
);
}}
</SidebarDnDContext.Consumer>
{/* </DropToImport> */}
</>
);
}
}
@@ -0,0 +1,68 @@
// @flow
import * as React from "react";
import { Draggable as DnDDraggable } from "react-beautiful-dnd";
import type {
DraggableProvided,
DraggableStateSnapshot,
} from "react-beautiful-dnd";
import ReactDOM from "react-dom";
import styled, { withTheme } from "styled-components";
type Props = {
draggableId: string,
index: number,
children: React.Node,
};
type InnerProps = {
provided: DraggableProvided,
snapshot: DraggableStateSnapshot,
children: React.Node,
};
class Inner extends React.Component<InnerProps> {
render() {
const { provided, snapshot, children } = this.props;
const child = (
<DropContainer
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
isDraggingOver={Boolean(snapshot.combineTargetFor)}
>
{children}
</DropContainer>
);
const portalContainer = document.getElementById(
"sidebar-collections-portal"
);
if (snapshot.isDragging && portalContainer) {
return ReactDOM.createPortal(child, portalContainer);
}
return child;
}
}
class Draggable extends React.Component<Props> {
render() {
const { draggableId, index, children } = this.props;
return (
<DnDDraggable draggableId={draggableId} index={index}>
{(provided, snapshot) => children}
</DnDDraggable>
);
}
}
const DropContainer = styled.div((props) => ({
backgroundColor: props.isDraggingOver
? props.theme.sidebarDroppableBackground
: undefined,
}));
export default withTheme(Draggable);
@@ -0,0 +1,63 @@
// @flow
import * as React from "react";
import { Droppable as DnDDroppable } from "react-beautiful-dnd";
import type {
DroppableProvided,
DroppableStateSnapshot,
} from "react-beautiful-dnd";
import styled, { withTheme } from "styled-components";
import {
DROPPABLE_COLLECTION_SUFFIX,
DROPPABLE_DOCUMENT_SUFFIX,
DROPPABLE_DOCUMENT_SEPARATOR,
} from "utils/dnd";
type Props = {
collectionId: string,
documentId?: string,
isDropDisabled?: boolean,
children(
provided: DroppableProvided,
snapshot: DroppableStateSnapshot
): React.Node,
};
class Droppable extends React.Component<Props> {
static defaultProps = {
isDropDisabled: false,
};
render() {
const { collectionId, documentId, isDropDisabled, children } = this.props;
let droppableId;
if (documentId) {
droppableId = `${DROPPABLE_DOCUMENT_SUFFIX}${documentId}${DROPPABLE_DOCUMENT_SEPARATOR}${collectionId}`;
} else {
droppableId = `${DROPPABLE_COLLECTION_SUFFIX}${collectionId}`;
}
return (
<DnDDroppable droppableId={droppableId} isDropDisabled={isDropDisabled}>
{(provided, snapshot) => (
<DropContainer
isDraggingOver={snapshot.isDraggingOver}
{...provided.droppableProps}
ref={provided.innerRef}
>
{children(provided, snapshot)}
{provided.placeholder}
</DropContainer>
)}
</DnDDroppable>
);
}
}
const DropContainer = styled.div((props) => ({
backgroundColor: props.isDraggingOver
? props.theme.sidebarDroppableBackground
: undefined,
}));
export default withTheme(Droppable);
@@ -9,7 +9,7 @@ type Props = {|
canUpdate: boolean,
|};
function EditableTitle({ title, onSubmit, canUpdate }: Props) {
function EditableTitle({ title, onSubmit, canUpdate, ...rest }: Props) {
const [isEditing, setIsEditing] = React.useState(false);
const [originalValue, setOriginalValue] = React.useState(title);
const [value, setValue] = React.useState(title);
@@ -2,6 +2,7 @@
import { observer } from "mobx-react";
import { CollapsedIcon } from "outline-icons";
import * as React from "react";
import { Draggable as DnDDraggable } from "react-beautiful-dnd";
import { withRouter, NavLink } from "react-router-dom";
import styled, { withTheme } from "styled-components";
import Flex from "components/Flex";
@@ -17,6 +18,8 @@ type Props = {
menu?: React.Node,
menuOpen?: boolean,
hideDisclosure?: boolean,
draggableId?: string,
index?: number,
iconColor?: string,
active?: boolean,
theme: Object,
@@ -34,6 +37,8 @@ function SidebarLink({
menu,
menuOpen,
hideDisclosure,
draggableId,
index,
theme,
exact,
href,
@@ -75,28 +80,45 @@ function SidebarLink({
...style,
};
const linkElement = (props) => (
<StyledNavLink
activeStyle={activeStyle}
style={active ? activeStyle : style}
onClick={onClick}
exact={exact !== false}
to={to}
as={to ? undefined : href ? "a" : "div"}
href={href}
{...props}
>
{icon && <IconWrapper>{icon}</IconWrapper>}
<Label onClick={handleExpand}>
{showDisclosure && (
<Disclosure expanded={expanded} onClick={handleClick} />
)}
{label}
</Label>
{menu && <Action menuOpen={menuOpen}>{menu}</Action>}
</StyledNavLink>
);
return (
<Wrapper column>
<StyledNavLink
activeStyle={activeStyle}
style={active ? activeStyle : style}
onClick={onClick}
exact={exact !== false}
to={to}
as={to ? undefined : href ? "a" : "div"}
href={href}
>
{icon && <IconWrapper>{icon}</IconWrapper>}
<Label onClick={handleExpand}>
{showDisclosure && (
<Disclosure expanded={expanded} onClick={handleClick} />
)}
{label}
</Label>
{menu && <Action menuOpen={menuOpen}>{menu}</Action>}
</StyledNavLink>
<>
{draggableId ? (
<DnDDraggable draggableId={draggableId} index={index}>
{(provided, snapshot) =>
linkElement({
innerRef: provided.innerRef,
...provided.draggableProps,
...provided.dragHandleProps,
})
}
</DnDDraggable>
) : (
linkElement()
)}
{expanded && children}
</Wrapper>
</>
);
}
@@ -170,4 +192,8 @@ const Disclosure = styled(CollapsedIcon)`
${({ expanded }) => !expanded && "transform: rotate(-90deg);"};
`;
const ChildrenWrapper = styled.div(({ expanded }) => ({
display: expanded ? "block" : "none",
}));
export default withRouter(withTheme(observer(SidebarLink)));
+54
View File
@@ -100,6 +100,60 @@ export default class Collection extends BaseModel {
return [];
}
@action
addDocumentToStructure(
document: NavigationNode,
parentDocumentId: ?string,
index: ?number
) {
if (!parentDocumentId) {
this.documents.splice(
index !== undefined && index !== null ? index : this.documents.length,
0,
document
);
} else {
const recursivelyAddDocument = (nodes: NavigationNode[]) => {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].id === parentDocumentId) {
nodes[i].children.splice(
index !== undefined && index !== null
? index
: nodes[i].children.length,
0,
document
);
return true;
}
const isAdded = recursivelyAddDocument(nodes[i].children);
if (isAdded) return true;
}
};
recursivelyAddDocument(this.documents);
}
}
@action
removeDocumentInStructure(documentId: string) {
const recursivelyRemoveDocument = (nodes) => {
const index = nodes.findIndex((item) => item.id === documentId);
if (index !== -1) {
nodes.splice(index, 1);
return true;
}
for (let i = 0; i < nodes.length; i++) {
const isFound = recursivelyRemoveDocument(nodes[i].children);
if (isFound) return true;
}
return false;
};
return recursivelyRemoveDocument(this.documents);
}
toJS = () => {
return pick(this, [
"id",
+41 -2
View File
@@ -7,7 +7,12 @@ import naturalSort from "shared/utils/naturalSort";
import BaseStore from "stores/BaseStore";
import RootStore from "stores/RootStore";
import Document from "models/Document";
import type { FetchOptions, PaginationParams, SearchResult } from "types";
import type {
FetchOptions,
PaginationParams,
SearchResult,
NavigationNode,
} from "types";
import { client } from "utils/ApiClient";
type ImportOptions = {
@@ -430,15 +435,49 @@ export default class DocumentsStore extends BaseStore<Document> {
move = async (
document: Document,
collectionId: string,
parentDocumentId: ?string
parentDocumentId: ?string,
index: ?number
) => {
const oldCollection = this.rootStore.collections.get(document.collectionId);
let newCollection = oldCollection;
if (document.collectionId !== collectionId) {
newCollection = this.rootStore.collections.get(collectionId);
}
// Update UI
if (oldCollection && newCollection) {
// Retrive all children documents
const childDocuments = oldCollection.getDocumentChildren(document.id);
// Remove document from old collection
oldCollection.removeDocumentInStructure(document.id);
// Recreate navigation node object
const navigationNode: NavigationNode = {
id: document.id,
title: document.title,
url: document.url,
children: childDocuments,
};
// Move document to new location
newCollection.addDocumentToStructure(
navigationNode,
parentDocumentId,
index
);
}
// Send data to server
const res = await client.post("/documents.move", {
id: document.id,
collectionId,
parentDocumentId,
index: String(index),
});
invariant(res && res.data, "Data not available");
// Apply data from the server
res.data.documents.forEach(this.add);
res.data.collections.forEach(this.rootStore.collections.add);
this.addPolicies(res.policies);
+4
View File
@@ -0,0 +1,4 @@
// @flow
export const DROPPABLE_COLLECTION_SUFFIX = "droppable-collection-";
export const DROPPABLE_DOCUMENT_SUFFIX = "droppable-document-";
export const DROPPABLE_DOCUMENT_SEPARATOR = "_collection_";
+1
View File
@@ -133,6 +133,7 @@
"react": "^16.8.6",
"react-autosize-textarea": "^6.0.0",
"react-avatar-editor": "^10.3.0",
"react-beautiful-dnd": "^13.0.0",
"react-color": "^2.17.3",
"react-dom": "^16.8.6",
"react-dropzone": "4.2.1",
+1 -14
View File
@@ -1,5 +1,4 @@
// @flow
import naturalSort from "../../shared/utils/naturalSort";
import { Collection } from "../models";
type Document = {
@@ -9,15 +8,6 @@ type Document = {
url: string,
};
const sortDocuments = (documents: Document[]): Document[] => {
const orderedDocs = naturalSort(documents, "title");
return orderedDocs.map((document) => ({
...document,
children: sortDocuments(document.children),
}));
};
export default function present(collection: Collection) {
const data = {
id: collection.id,
@@ -30,11 +20,8 @@ export default function present(collection: Collection) {
createdAt: collection.createdAt,
updatedAt: collection.updatedAt,
deletedAt: collection.deletedAt,
documents: undefined,
documents: collection.documentStructure ? collection.documentStructure : [],
};
// Force alphabetical sorting
data.documents = sortDocuments(collection.documentStructure);
return data;
}
+2
View File
@@ -128,6 +128,7 @@ export const light = {
sidebarBackground: colors.warmGrey,
sidebarItemBackground: colors.black10,
sidebarText: "rgb(78, 92, 110)",
sidebarDroppableBackground: "rgba(0, 0, 0, .05)",
shadow: "rgba(0, 0, 0, 0.2)",
menuBackground: colors.white,
@@ -182,6 +183,7 @@ export const dark = {
sidebarBackground: colors.veryDarkBlue,
sidebarItemBackground: colors.veryDarkBlue,
sidebarText: colors.slate,
sidebarDroppableBackground: "rgba(255, 255, 255, .05)",
shadow: "rgba(0, 0, 0, 0.6)",
menuBorder: lighten(0.1, colors.almostBlack),
+930 -888
View File
File diff suppressed because it is too large Load Diff