feat: Moving documents via drag and drop in sidebar (#1717)
* wip: added some basic drag and drop UI for combining items * refactor: pathToDocument to accept only id * fix: Multiple drop backends error fix: Incorrect styling dragging over active collection fix: Stay in disabled state until save is complete * Improving display while moving doc * fix: update by user should be changed when moving a doc * add move guard to drag Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
parent
3469b82beb
commit
051ecab0fc
|
@ -99,7 +99,7 @@ const Breadcrumb = ({ document, onlyText }: Props) => {
|
|||
}
|
||||
|
||||
const path = collection.pathToDocument
|
||||
? collection.pathToDocument(document).slice(0, -1)
|
||||
? collection.pathToDocument(document.id).slice(0, -1)
|
||||
: [];
|
||||
|
||||
if (onlyText === true) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useDrop } from "react-dnd";
|
||||
import UiStore from "stores/UiStore";
|
||||
import Collection from "models/Collection";
|
||||
import Document from "models/Document";
|
||||
|
@ -10,6 +10,7 @@ import DropToImport from "components/DropToImport";
|
|||
import DocumentLink from "./DocumentLink";
|
||||
import EditableTitle from "./EditableTitle";
|
||||
import SidebarLink from "./SidebarLink";
|
||||
import useStores from "hooks/useStores";
|
||||
import CollectionMenu from "menus/CollectionMenu";
|
||||
|
||||
type Props = {|
|
||||
|
@ -20,27 +21,44 @@ type Props = {|
|
|||
prefetchDocument: (id: string) => Promise<void>,
|
||||
|};
|
||||
|
||||
@observer
|
||||
class CollectionLink extends React.Component<Props> {
|
||||
@observable menuOpen = false;
|
||||
function CollectionLink({
|
||||
collection,
|
||||
activeDocument,
|
||||
prefetchDocument,
|
||||
canUpdate,
|
||||
ui,
|
||||
}: Props) {
|
||||
const [menuOpen, setMenuOpen] = React.useState(false);
|
||||
|
||||
handleTitleChange = async (name: string) => {
|
||||
await this.props.collection.save({ name });
|
||||
};
|
||||
const handleTitleChange = React.useCallback(
|
||||
async (name: string) => {
|
||||
await collection.save({ name });
|
||||
},
|
||||
[collection]
|
||||
);
|
||||
|
||||
render() {
|
||||
const {
|
||||
collection,
|
||||
activeDocument,
|
||||
prefetchDocument,
|
||||
canUpdate,
|
||||
ui,
|
||||
} = this.props;
|
||||
const { documents, policies } = useStores();
|
||||
const expanded = collection.id === ui.activeCollectionId;
|
||||
|
||||
const expanded = collection.id === ui.activeCollectionId;
|
||||
// Droppable
|
||||
const [{ isOver, canDrop }, drop] = useDrop({
|
||||
accept: "document",
|
||||
drop: (item, monitor) => {
|
||||
if (!collection) return;
|
||||
documents.move(item.id, collection.id);
|
||||
},
|
||||
canDrop: (item, monitor) => {
|
||||
return policies.abilities(collection.id).update;
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isOver: !!monitor.isOver(),
|
||||
canDrop: monitor.canDrop(),
|
||||
}),
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
return (
|
||||
<>
|
||||
<div ref={drop}>
|
||||
<DropToImport key={collection.id} collectionId={collection.id}>
|
||||
<SidebarLink
|
||||
key={collection.id}
|
||||
|
@ -50,11 +68,12 @@ class CollectionLink extends React.Component<Props> {
|
|||
}
|
||||
iconColor={collection.color}
|
||||
expanded={expanded}
|
||||
menuOpen={this.menuOpen}
|
||||
menuOpen={menuOpen}
|
||||
isActiveDrop={isOver && canDrop}
|
||||
label={
|
||||
<EditableTitle
|
||||
title={collection.name}
|
||||
onSubmit={this.handleTitleChange}
|
||||
onSubmit={handleTitleChange}
|
||||
canUpdate={canUpdate}
|
||||
/>
|
||||
}
|
||||
|
@ -63,28 +82,28 @@ class CollectionLink extends React.Component<Props> {
|
|||
<CollectionMenu
|
||||
position="right"
|
||||
collection={collection}
|
||||
onOpen={() => (this.menuOpen = true)}
|
||||
onClose={() => (this.menuOpen = false)}
|
||||
onOpen={() => setMenuOpen(true)}
|
||||
onClose={() => setMenuOpen(false)}
|
||||
/>
|
||||
}
|
||||
></SidebarLink>
|
||||
</DropToImport>
|
||||
</div>
|
||||
|
||||
{expanded &&
|
||||
collection.documents.map((node) => (
|
||||
<DocumentLink
|
||||
key={node.id}
|
||||
node={node}
|
||||
collection={collection}
|
||||
activeDocument={activeDocument}
|
||||
prefetchDocument={prefetchDocument}
|
||||
canUpdate={canUpdate}
|
||||
depth={1.5}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
{expanded &&
|
||||
collection.documents.map((node) => (
|
||||
<DocumentLink
|
||||
key={node.id}
|
||||
node={node}
|
||||
collection={collection}
|
||||
activeDocument={activeDocument}
|
||||
prefetchDocument={prefetchDocument}
|
||||
canUpdate={canUpdate}
|
||||
depth={1.5}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CollectionLink;
|
||||
export default observer(CollectionLink);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { observer } from "mobx-react";
|
||||
import { CollapsedIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useDrag, useDrop } from "react-dnd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import Collection from "models/Collection";
|
||||
|
@ -33,7 +34,7 @@ function DocumentLink({
|
|||
depth,
|
||||
canUpdate,
|
||||
}: Props) {
|
||||
const { documents } = useStores();
|
||||
const { documents, policies } = useStores();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isActiveDocument = activeDocument && activeDocument.id === node.id;
|
||||
|
@ -48,13 +49,19 @@ function DocumentLink({
|
|||
}
|
||||
}, [fetchChildDocuments, node, hasChildDocuments, isActiveDocument]);
|
||||
|
||||
const pathToNode = React.useMemo(
|
||||
() =>
|
||||
collection && collection.pathToDocument(node.id).map((entry) => entry.id),
|
||||
[collection, node]
|
||||
);
|
||||
|
||||
const showChildren = React.useMemo(() => {
|
||||
return !!(
|
||||
hasChildDocuments &&
|
||||
activeDocument &&
|
||||
collection &&
|
||||
(collection
|
||||
.pathToDocument(activeDocument)
|
||||
.pathToDocument(activeDocument.id)
|
||||
.map((entry) => entry.id)
|
||||
.includes(node.id) ||
|
||||
isActiveDocument)
|
||||
|
@ -100,51 +107,88 @@ function DocumentLink({
|
|||
);
|
||||
|
||||
const [menuOpen, setMenuOpen] = React.useState(false);
|
||||
const isMoving = documents.movingDocumentId === node.id;
|
||||
|
||||
// Draggable
|
||||
const [{ isDragging }, drag] = useDrag({
|
||||
item: { type: "document", ...node, depth, active: isActiveDocument },
|
||||
collect: (monitor) => ({
|
||||
isDragging: !!monitor.isDragging(),
|
||||
}),
|
||||
canDrag: (monitor) => {
|
||||
return policies.abilities(node.id).move;
|
||||
},
|
||||
});
|
||||
|
||||
// Droppable
|
||||
const [{ isOver, canDrop }, drop] = useDrop({
|
||||
accept: "document",
|
||||
drop: async (item, monitor) => {
|
||||
if (!collection) return;
|
||||
documents.move(item.id, collection.id, node.id);
|
||||
},
|
||||
canDrop: (item, monitor) =>
|
||||
pathToNode && !pathToNode.includes(monitor.getItem().id),
|
||||
collect: (monitor) => ({
|
||||
isOver: !!monitor.isOver(),
|
||||
canDrop: monitor.canDrop(),
|
||||
}),
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment key={node.id}>
|
||||
<DropToImport documentId={node.id} activeClassName="activeDropZone">
|
||||
<SidebarLink
|
||||
innerRef={isActiveDocument ? activeDocumentRef : undefined}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
to={{
|
||||
pathname: node.url,
|
||||
state: { title: node.title },
|
||||
}}
|
||||
label={
|
||||
<>
|
||||
{hasChildDocuments && (
|
||||
<Disclosure
|
||||
expanded={expanded}
|
||||
onClick={handleDisclosureClick}
|
||||
/>
|
||||
)}
|
||||
<EditableTitle
|
||||
title={node.title || t("Untitled")}
|
||||
onSubmit={handleTitleChange}
|
||||
canUpdate={canUpdate}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
depth={depth}
|
||||
exact={false}
|
||||
menuOpen={menuOpen}
|
||||
menu={
|
||||
document ? (
|
||||
<Fade>
|
||||
<DocumentMenu
|
||||
position="right"
|
||||
document={document}
|
||||
onOpen={() => setMenuOpen(true)}
|
||||
onClose={() => setMenuOpen(false)}
|
||||
/>
|
||||
</Fade>
|
||||
) : undefined
|
||||
}
|
||||
></SidebarLink>
|
||||
</DropToImport>
|
||||
<>
|
||||
<Draggable
|
||||
key={node.id}
|
||||
ref={drag}
|
||||
$isDragging={isDragging}
|
||||
$isMoving={isMoving}
|
||||
>
|
||||
<div ref={drop}>
|
||||
<DropToImport documentId={node.id} activeClassName="activeDropZone">
|
||||
<SidebarLink
|
||||
innerRef={isActiveDocument ? activeDocumentRef : undefined}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
to={{
|
||||
pathname: node.url,
|
||||
state: { title: node.title },
|
||||
}}
|
||||
label={
|
||||
<>
|
||||
{hasChildDocuments && (
|
||||
<Disclosure
|
||||
expanded={expanded && !isDragging}
|
||||
onClick={handleDisclosureClick}
|
||||
/>
|
||||
)}
|
||||
<EditableTitle
|
||||
title={node.title || t("Untitled")}
|
||||
onSubmit={handleTitleChange}
|
||||
canUpdate={canUpdate}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
isActiveDrop={isOver && canDrop}
|
||||
depth={depth}
|
||||
exact={false}
|
||||
menuOpen={menuOpen}
|
||||
menu={
|
||||
document && !isMoving ? (
|
||||
<Fade>
|
||||
<DocumentMenu
|
||||
position="right"
|
||||
document={document}
|
||||
onOpen={() => setMenuOpen(true)}
|
||||
onClose={() => setMenuOpen(false)}
|
||||
/>
|
||||
</Fade>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</DropToImport>
|
||||
</div>
|
||||
</Draggable>
|
||||
|
||||
{expanded && (
|
||||
{expanded && !isDragging && (
|
||||
<>
|
||||
{node.children.map((childNode) => (
|
||||
<ObservedDocumentLink
|
||||
|
@ -159,10 +203,15 @@ function DocumentLink({
|
|||
))}
|
||||
</>
|
||||
)}
|
||||
</React.Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const Draggable = styled("div")`
|
||||
opacity: ${(props) => (props.$isDragging || props.$isMoving ? 0.5 : 1)};
|
||||
pointer-events: ${(props) => (props.$isMoving ? "none" : "all")};
|
||||
`;
|
||||
|
||||
const Disclosure = styled(CollapsedIcon)`
|
||||
position: absolute;
|
||||
left: -24px;
|
||||
|
|
|
@ -17,6 +17,7 @@ type Props = {
|
|||
menuOpen?: boolean,
|
||||
iconColor?: string,
|
||||
active?: boolean,
|
||||
isActiveDrop?: boolean,
|
||||
theme: Theme,
|
||||
exact?: boolean,
|
||||
depth?: number,
|
||||
|
@ -30,6 +31,7 @@ function SidebarLink({
|
|||
to,
|
||||
label,
|
||||
active,
|
||||
isActiveDrop,
|
||||
menu,
|
||||
menuOpen,
|
||||
theme,
|
||||
|
@ -54,7 +56,8 @@ function SidebarLink({
|
|||
|
||||
return (
|
||||
<StyledNavLink
|
||||
activeStyle={activeStyle}
|
||||
$isActiveDrop={isActiveDrop}
|
||||
activeStyle={isActiveDrop ? undefined : activeStyle}
|
||||
style={active ? activeStyle : style}
|
||||
onClick={onClick}
|
||||
onMouseEnter={onMouseEnter}
|
||||
|
@ -103,12 +106,20 @@ const StyledNavLink = styled(NavLink)`
|
|||
text-overflow: ellipsis;
|
||||
padding: 4px 16px;
|
||||
border-radius: 4px;
|
||||
color: ${(props) => props.theme.sidebarText};
|
||||
background: ${(props) =>
|
||||
props.$isActiveDrop ? props.theme.slateDark : "inherit"};
|
||||
color: ${(props) =>
|
||||
props.$isActiveDrop ? props.theme.white : props.theme.sidebarText};
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
${(props) => (props.$isActiveDrop ? `fill: ${props.theme.white};` : "")}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: ${(props) => props.theme.text};
|
||||
color: ${(props) =>
|
||||
props.$isActiveDrop ? props.theme.white : props.theme.text};
|
||||
}
|
||||
|
||||
&:focus {
|
||||
|
|
21
app/index.js
21
app/index.js
|
@ -3,9 +3,10 @@ import "mobx-react-lite/batchingForReactDom";
|
|||
import "focus-visible";
|
||||
import { Provider } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { render } from "react-dom";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
import { initI18n } from "shared/i18n";
|
||||
import stores from "stores";
|
||||
import ErrorBoundary from "components/ErrorBoundary";
|
||||
|
@ -24,14 +25,16 @@ if (element) {
|
|||
<ErrorBoundary>
|
||||
<Provider {...stores}>
|
||||
<Theme>
|
||||
<Router>
|
||||
<>
|
||||
<ScrollToTop>
|
||||
<Routes />
|
||||
</ScrollToTop>
|
||||
<Toasts />
|
||||
</>
|
||||
</Router>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Router>
|
||||
<>
|
||||
<ScrollToTop>
|
||||
<Routes />
|
||||
</ScrollToTop>
|
||||
<Toasts />
|
||||
</>
|
||||
</Router>
|
||||
</DndProvider>
|
||||
</Theme>
|
||||
</Provider>
|
||||
</ErrorBoundary>,
|
||||
|
|
|
@ -79,12 +79,12 @@ export default class Collection extends BaseModel {
|
|||
return result;
|
||||
}
|
||||
|
||||
pathToDocument(document: Document) {
|
||||
pathToDocument(documentId: string) {
|
||||
let path;
|
||||
const traveler = (nodes, previousPath) => {
|
||||
nodes.forEach((childNode) => {
|
||||
const newPath = [...previousPath, childNode];
|
||||
if (childNode.id === document.id) {
|
||||
if (childNode.id === documentId) {
|
||||
path = newPath;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -268,7 +268,7 @@ export default class Document extends BaseModel {
|
|||
};
|
||||
|
||||
move = (collectionId: string, parentDocumentId: ?string) => {
|
||||
return this.store.move(this, collectionId, parentDocumentId);
|
||||
return this.store.move(this.id, collectionId, parentDocumentId);
|
||||
};
|
||||
|
||||
duplicate = () => {
|
||||
|
|
|
@ -19,6 +19,7 @@ export default class DocumentsStore extends BaseStore<Document> {
|
|||
@observable searchCache: Map<string, SearchResult[]> = new Map();
|
||||
@observable starredIds: Map<string, boolean> = new Map();
|
||||
@observable backlinks: Map<string, string[]> = new Map();
|
||||
@observable movingDocumentId: ?string;
|
||||
|
||||
importFileTypes: string[] = [
|
||||
"text/markdown",
|
||||
|
@ -450,20 +451,26 @@ export default class DocumentsStore extends BaseStore<Document> {
|
|||
|
||||
@action
|
||||
move = async (
|
||||
document: Document,
|
||||
documentId: string,
|
||||
collectionId: string,
|
||||
parentDocumentId: ?string
|
||||
) => {
|
||||
const res = await client.post("/documents.move", {
|
||||
id: document.id,
|
||||
collectionId,
|
||||
parentDocumentId,
|
||||
});
|
||||
invariant(res && res.data, "Data not available");
|
||||
this.movingDocumentId = documentId;
|
||||
|
||||
res.data.documents.forEach(this.add);
|
||||
res.data.collections.forEach(this.rootStore.collections.add);
|
||||
this.addPolicies(res.policies);
|
||||
try {
|
||||
const res = await client.post("/documents.move", {
|
||||
id: documentId,
|
||||
collectionId,
|
||||
parentDocumentId,
|
||||
});
|
||||
invariant(res && res.data, "Data not available");
|
||||
|
||||
res.data.documents.forEach(this.add);
|
||||
res.data.collections.forEach(this.rootStore.collections.add);
|
||||
this.addPolicies(res.policies);
|
||||
} finally {
|
||||
this.movingDocumentId = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
@action
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
// flow-typed signature: c69369aa4bc769d5f1d4f6ec9c76d8f2
|
||||
// flow-typed version: c6154227d1/react-dropzone_v4.x.x/flow_>=v0.104.x
|
||||
|
||||
declare module "react-dropzone" {
|
||||
declare type ChildrenProps = {
|
||||
draggedFiles: Array<File>,
|
||||
acceptedFiles: Array<File>,
|
||||
rejectedFiles: Array<File>,
|
||||
isDragActive: boolean,
|
||||
isDragAccept: boolean,
|
||||
isDragReject: boolean,
|
||||
...
|
||||
}
|
||||
|
||||
declare type DropzoneFile = File & { preview?: string, ... }
|
||||
|
||||
declare type DropzoneProps = {
|
||||
accept?: string,
|
||||
children?: React$Node | (ChildrenProps) => React$Node,
|
||||
disableClick?: boolean,
|
||||
disabled?: boolean,
|
||||
disablePreview?: boolean,
|
||||
preventDropOnDocument?: boolean,
|
||||
inputProps?: Object,
|
||||
multiple?: boolean,
|
||||
name?: string,
|
||||
maxSize?: number,
|
||||
minSize?: number,
|
||||
className?: string,
|
||||
activeClassName?: string,
|
||||
acceptClassName?: string,
|
||||
rejectClassName?: string,
|
||||
disabledClassName?: string,
|
||||
style?: Object,
|
||||
activeStyle?: Object,
|
||||
acceptStyle?: Object,
|
||||
rejectStyle?: Object,
|
||||
disabledStyle?: Object,
|
||||
onClick?: (event: SyntheticMouseEvent<>) => mixed,
|
||||
onDrop?: (acceptedFiles: Array<DropzoneFile>, rejectedFiles: Array<DropzoneFile>, event: SyntheticDragEvent<>) => mixed,
|
||||
onDropAccepted?: (acceptedFiles: Array<DropzoneFile>, event: SyntheticDragEvent<>) => mixed,
|
||||
onDropRejected?: (rejectedFiles: Array<DropzoneFile>, event: SyntheticDragEvent<>) => mixed,
|
||||
onDragStart?: (event: SyntheticDragEvent<>) => mixed,
|
||||
onDragEnter?: (event: SyntheticDragEvent<>) => mixed,
|
||||
onDragOver?: (event: SyntheticDragEvent<>) => mixed,
|
||||
onDragLeave?: (event: SyntheticDragEvent<>) => mixed,
|
||||
onFileDialogCancel?: () => mixed,
|
||||
...
|
||||
};
|
||||
|
||||
declare class Dropzone extends React$Component<DropzoneProps> {
|
||||
open(): void;
|
||||
}
|
||||
|
||||
declare module.exports: typeof Dropzone;
|
||||
}
|
|
@ -137,6 +137,8 @@
|
|||
"react-autosize-textarea": "^6.0.0",
|
||||
"react-avatar-editor": "^10.3.0",
|
||||
"react-color": "^2.17.3",
|
||||
"react-dnd": "^11.1.3",
|
||||
"react-dnd-html5-backend": "^11.1.3",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-dropzone": "^11.2.4",
|
||||
"react-helmet": "^5.2.0",
|
||||
|
|
|
@ -28,6 +28,8 @@ export default async function documentMover({
|
|||
|
||||
document.collectionId = collectionId;
|
||||
document.parentDocumentId = null;
|
||||
document.lastModifiedById = user.id;
|
||||
document.updatedBy = user;
|
||||
|
||||
await document.save();
|
||||
result.documents.push(document);
|
||||
|
@ -54,6 +56,8 @@ export default async function documentMover({
|
|||
// add to new collection (may be the same)
|
||||
document.collectionId = collectionId;
|
||||
document.parentDocumentId = parentDocumentId;
|
||||
document.lastModifiedById = user.id;
|
||||
document.updatedBy = user;
|
||||
|
||||
const newCollection: Collection = collectionChanged
|
||||
? await Collection.findByPk(collectionId, { transaction })
|
||||
|
|
82
yarn.lock
82
yarn.lock
|
@ -1365,6 +1365,21 @@
|
|||
dependencies:
|
||||
"@types/node" ">= 8"
|
||||
|
||||
"@react-dnd/asap@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-4.0.0.tgz#b300eeed83e9801f51bd66b0337c9a6f04548651"
|
||||
integrity sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ==
|
||||
|
||||
"@react-dnd/invariant@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-2.0.0.tgz#09d2e81cd39e0e767d7da62df9325860f24e517e"
|
||||
integrity sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==
|
||||
|
||||
"@react-dnd/shallowequal@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a"
|
||||
integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==
|
||||
|
||||
"@rehooks/window-scroll-position@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@rehooks/window-scroll-position/-/window-scroll-position-1.0.1.tgz#3cb80f22cbf9cdbd2041b5236ae1fce8245b2f1c"
|
||||
|
@ -1555,6 +1570,14 @@
|
|||
dependencies:
|
||||
"@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":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
|
||||
|
@ -1604,6 +1627,19 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00"
|
||||
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/stack-utils@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
|
||||
|
@ -3765,6 +3801,11 @@ cssstyle@^2.2.0:
|
|||
dependencies:
|
||||
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:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
||||
|
@ -4002,6 +4043,15 @@ direction@^0.1.5:
|
|||
resolved "https://registry.yarnpkg.com/direction/-/direction-0.1.5.tgz#ce5d797f97e26f8be7beff53f7dc40e1c1a9ec4c"
|
||||
integrity sha1-zl15f5fib4vnvv9T99xA4cGp7Ew=
|
||||
|
||||
dnd-core@^11.1.3:
|
||||
version "11.1.3"
|
||||
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-11.1.3.tgz#f92099ba7245e49729d2433157031a6267afcc98"
|
||||
integrity sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA==
|
||||
dependencies:
|
||||
"@react-dnd/asap" "^4.0.0"
|
||||
"@react-dnd/invariant" "^2.0.0"
|
||||
redux "^4.0.4"
|
||||
|
||||
doctrine@1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
|
||||
|
@ -5753,7 +5803,7 @@ hmac-drbg@^1.0.0:
|
|||
minimalistic-assert "^1.0.0"
|
||||
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.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
|
@ -9759,6 +9809,23 @@ react-color@^2.17.3:
|
|||
reactcss "^1.2.0"
|
||||
tinycolor2 "^1.4.1"
|
||||
|
||||
react-dnd-html5-backend@^11.1.3:
|
||||
version "11.1.3"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz#2749f04f416ec230ea193f5c1fbea2de7dffb8f7"
|
||||
integrity sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw==
|
||||
dependencies:
|
||||
dnd-core "^11.1.3"
|
||||
|
||||
react-dnd@^11.1.3:
|
||||
version "11.1.3"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-11.1.3.tgz#f9844f5699ccc55dfc81462c2c19f726e670c1af"
|
||||
integrity sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==
|
||||
dependencies:
|
||||
"@react-dnd/shallowequal" "^2.0.0"
|
||||
"@types/hoist-non-react-statics" "^3.3.1"
|
||||
dnd-core "^11.1.3"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
react-dom@^16.8.6:
|
||||
version "16.14.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
|
||||
|
@ -10027,6 +10094,14 @@ redis@^3.0.0:
|
|||
redis-errors "^1.2.0"
|
||||
redis-parser "^3.0.0"
|
||||
|
||||
redux@^4.0.4:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
|
||||
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
symbol-observable "^1.2.0"
|
||||
|
||||
referrer-policy@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/referrer-policy/-/referrer-policy-1.2.0.tgz#b99cfb8b57090dc454895ef897a4cc35ef67a98e"
|
||||
|
@ -11387,6 +11462,11 @@ supports-hyperlinks@^2.0.0:
|
|||
has-flag "^4.0.0"
|
||||
supports-color "^7.0.0"
|
||||
|
||||
symbol-observable@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
||||
|
||||
symbol-tree@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||
|
|
Reference in New Issue