Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bulk DND Move files #1028

Merged
merged 20 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/files-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
"mime-matcher": "^1.0.5",
"react": "^16.14.0",
"react-beforeunload": "^2.4.0",
"react-dnd": "^11.1.3",
"react-dnd-html5-backend": "^11.1.3",
"react-dnd": "14.0.2",
"react-dnd-html5-backend": "14.0.0",
"react-dom": "^16.14.0",
"react-h5-audio-player": "^3.5.0",
"react-hotkeys-hook": "^2.4.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
import { FolderFilledSvg, FileImageSvg, FilePdfSvg, FileTextSvg, formatBytes,
MenuDropdown, MoreIcon, TableCell, TableRow, Typography, CheckboxInput } from "@chainsafe/common-components"
import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme"
import clsx from "clsx"
import dayjs from "dayjs"
import React from "react"
import { useDragLayer, XYCoord } from "react-dnd"
import { FileSystemItem } from "../../../../Contexts/DriveContext"
import { CSFTheme } from "../../../../Themes/types"
import { DragTypes } from "../DragConstants"
import { BrowserView } from "../types"

const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => {
const desktopGridSettings = "50px 69px 3fr 190px 100px 45px !important"
const mobileGridSettings = "69px 3fr 45px !important"

return createStyles({
tableRow: {
width: "calc(100vw - 504px)",
FSM1 marked this conversation as resolved.
Show resolved Hide resolved
border: "2px solid transparent",
[breakpoints.up("md")]: {
gridTemplateColumns: desktopGridSettings
},
[breakpoints.down("md")]: {
gridTemplateColumns: mobileGridSettings
}
},
fileIcon: {
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
"& svg": {
width: constants.generalUnit * 2.5,
fill: constants.fileSystemItemRow.icon
}
},
folderIcon: {
"& svg": {
fill: palette.additional.gray[9]
}
},
filename: {
whiteSpace: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden"
},
previewDragLayer: {
position: "fixed",
pointerEvents: "none",
zIndex: 100,
left: 0,
top: 0,
bottom: 0,
right: 0
},
dropdownIcon: {
"& svg": {
fill: constants.fileSystemItemRow.dropdownIcon
}
},
dropdownOptions: {
backgroundColor: constants.fileSystemItemRow.optionsBackground,
color: constants.fileSystemItemRow.optionsColor,
border: `1px solid ${constants.fileSystemItemRow.optionsBorder}`
},
gridViewContainer: {
display: "flex",
flex: 1,
maxWidth: constants.generalUnit * 24
},
gridFolderName: {
textAlign: "center",
wordBreak: "break-all",
overflowWrap: "break-word",
padding: constants.generalUnit
},
gridViewIconNameBox: {
display: "flex",
flexDirection: "column",
width: "100%",
cursor: "pointer"
},
gridIcon: {
display: "flex",
justifyContent: "center",
alignItems: "center",
height: constants.generalUnit * 16,
maxWidth: constants.generalUnit * 24,
border: `1px solid ${palette.additional["gray"][6]}`,
boxShadow: constants.filesTable.gridItemShadow,
"& svg": {
width: "30%"
},
[breakpoints.down("lg")]: {
height: constants.generalUnit * 16
},
[breakpoints.down("sm")]: {
height: constants.generalUnit * 16
}
},
menuTitleGrid: {
padding: `0 ${constants.generalUnit * 0.5}px`,
[breakpoints.down("md")]: {
padding: 0
}
}
})})

const DragPreviewTableItem: React.FC<{item: FileSystemItem; icon: React.ReactNode}> = ({
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
item: { name, isFolder, created_at, size },
icon
}) => {
const classes = useStyles()
const { desktop } = useThemeSwitcher()
return (
<TableRow
className={classes.tableRow}
type="grid"
>
{desktop && (
<TableCell>
FSM1 marked this conversation as resolved.
Show resolved Hide resolved
<CheckboxInput
value={false}
onChange={() => {return}}
/>
</TableCell>
)}
<TableCell
className={clsx(classes.fileIcon, isFolder && classes.folderIcon)}
>
{icon}
</TableCell>
<TableCell
align="left"
className={classes.filename}
>
<Typography>{name}</Typography>
</TableCell>
{desktop && (
<>
<TableCell align="left">
{
!isFolder && created_at && dayjs.unix(created_at).format("DD MMM YYYY h:mm a")
}
</TableCell>
<TableCell align="left">
{!isFolder && formatBytes(size)}
</TableCell>
</>
)}
<TableCell align="right">
<MenuDropdown
animation="none"
anchor={desktop ? "bottom-center" : "bottom-right"}
menuItems={[]}
classNames={{
icon: classes.dropdownIcon,
options: classes.dropdownOptions
}}
indicator={MoreIcon}
/>
</TableCell>
</TableRow>
)
}


const DragPreviewGridItem: React.FC<{item: FileSystemItem; icon: React.ReactNode}> = ({
item: { name, isFolder },
icon
}) => {
const classes = useStyles()
return (
<div className={classes.gridViewContainer}>
<div
className={clsx(classes.gridViewIconNameBox)}
>
<div
className={clsx(
classes.fileIcon,
isFolder && classes.folderIcon,
classes.gridIcon
)}
>
{icon}
</div>

<div className={classes.gridFolderName}>{name}</div>
</div>
<div>
<MenuDropdown
animation="none"
anchor="bottom-right"
menuItems={[]}
classNames={{
icon: classes.dropdownIcon,
options: classes.dropdownOptions,
title: classes.menuTitleGrid
}}
indicator={MoreIcon}
/>
</div>
</div>
)
}

export const DragPreviewLayer: React.FC<{items: FileSystemItem[]; previewType: BrowserView} > = ({ items, previewType }) => {
const classes = useStyles()
const { isDragging, dragItems, itemType, currentOffset } = useDragLayer(monitor => ({
itemType: monitor.getItemType(),
dragItems: monitor.getItem() as {ids: string[]},
isDragging: monitor.isDragging(),
initialOffset: monitor.getInitialSourceClientOffset(),
currentOffset: monitor.getSourceClientOffset()
}))

const getItemStyles = (currentOffset: XYCoord | null) => {
if (!currentOffset) {
return {
display: "none"
}
}
const { x, y } = currentOffset

const transform = `translate(${x}px, ${y}px)`
return {
transform,
WebkitTransform: transform
}
}

return (!isDragging || itemType !== DragTypes.MOVABLE_FILE)
? null
: <div className={classes.previewDragLayer}>
<ul style={getItemStyles(currentOffset)}>
{dragItems.ids.map(di => {
const previewItem = items.find(i => i.cid === di)

if (previewItem) {
let Icon
if (previewItem.isFolder) {
Icon = FolderFilledSvg
} else if (previewItem.content_type.includes("image")) {
Icon = FileImageSvg
} else if (previewItem.content_type.includes("pdf")) {
Icon = FilePdfSvg
} else {
Icon = FileTextSvg
}

return (previewType === "table")
? <DragPreviewTableItem
item={previewItem}
icon={<Icon />}
key={previewItem.cid}
/>
: <DragPreviewGridItem
item={previewItem}
icon={<Icon />}
key={previewItem.cid}
/>
} else {
return null
}})}
</ul>
</div>
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback } from "react"
import React, { useCallback, useEffect } from "react"
import {
FormikTextInput,
Typography,
Expand All @@ -25,7 +25,7 @@ import CustomModal from "../../../../Elements/CustomModal"
import { Trans } from "@lingui/macro"
import { useDrag, useDrop } from "react-dnd"
import { DragTypes } from "../../DragConstants"
import { NativeTypes } from "react-dnd-html5-backend"
import { getEmptyImage, NativeTypes } from "react-dnd-html5-backend"
import { BrowserView, FileOperation } from "../../types"
import { CSFTheme } from "../../../../../Themes/types"
import FileItemTableItem from "./FileSystemTableItem"
Expand Down Expand Up @@ -271,22 +271,41 @@ const FileSystemItemRow: React.FC<IFileSystemItemRowProps> = ({
(itemOperation) => allMenuItems[itemOperation]
)

const [, dragMoveRef, preview] = useDrag({
item: { type: DragTypes.MOVABLE_FILE, payload: file }
const [, dragMoveRef, preview] = useDrag(() =>
({ type: DragTypes.MOVABLE_FILE,
item: () => {
if (selected.includes(file.cid)) {
return { ids: selected }
} else {
return { ids: [...selected, file.cid] }
}
}
}), [selected])

useEffect(() => {
// This gets called after every render, by default

// Use empty image as a drag preview so browsers don't draw it
// and we can draw whatever we want on the custom drag layer instead.
preview(getEmptyImage(), {
// IE fallback: specify that we'd rather screenshot the node
// when it already knows it's being dragged so we can hide it with CSS.
captureDraggingState: true
})
})

const [{ isOverMove }, dropMoveRef] = useDrop({
accept: DragTypes.MOVABLE_FILE,
canDrop: () => isFolder,
drop: async (item: {
type: typeof DragTypes.MOVABLE_FILE
payload: FileSystemItem
}) => {
handleMove &&
(await handleMove(
`${currentPath}${item.payload.name}`,
`${currentPath}${name}/${item.payload.name}`
))
drop: (item: {ids: string[]}) => {
item.ids.map((cid) => {
const fileToMove = files.find(f => f.cid === cid)
handleMove && fileToMove &&
handleMove(
`${currentPath}${fileToMove.name}`,
`${currentPath}${name}/${fileToMove.name}`
)
})
},
collect: (monitor) => ({
isOverMove: monitor.isOver()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ function FileSystemTableItem({
<>
<TableCell align="left">
{
created_at && dayjs.unix(created_at).format("DD MMM YYYY h:mm a")
!isFolder && created_at && dayjs.unix(created_at).format("DD MMM YYYY h:mm a")
}
</TableCell>
<TableCell align="left">
Expand Down
Loading