From f45903fc0c5e61a42ad3a251cdd28979552c3d13 Mon Sep 17 00:00:00 2001 From: Rafael Ramalho Date: Tue, 25 Aug 2020 15:45:53 +0100 Subject: [PATCH 1/8] feat: improve breadcrumbs functionality --- src/files/breadcrumbs/Breadcrumbs.css | 15 ++++++ src/files/breadcrumbs/Breadcrumbs.js | 74 +++++++++++++++------------ src/files/header/Header.js | 9 +++- 3 files changed, 64 insertions(+), 34 deletions(-) create mode 100644 src/files/breadcrumbs/Breadcrumbs.css diff --git a/src/files/breadcrumbs/Breadcrumbs.css b/src/files/breadcrumbs/Breadcrumbs.css new file mode 100644 index 000000000..14b329186 --- /dev/null +++ b/src/files/breadcrumbs/Breadcrumbs.css @@ -0,0 +1,15 @@ +.BreadcrumbsButton::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: -4px; + height: 2px; + background: currentColor; + transition: opacity 0.2s ease-in-out; + opacity: 0; +} + +.BreadcrumbsButton.dragging::after { + opacity: 1; +} \ No newline at end of file diff --git a/src/files/breadcrumbs/Breadcrumbs.js b/src/files/breadcrumbs/Breadcrumbs.js index 559ba2639..5a4b6559c 100644 --- a/src/files/breadcrumbs/Breadcrumbs.js +++ b/src/files/breadcrumbs/Breadcrumbs.js @@ -1,7 +1,10 @@ -import React from 'react' +import React, { useEffect, useState, useRef } from 'react' +import classNames from 'classnames' import PropTypes from 'prop-types' import { withTranslation } from 'react-i18next' +import './Breadcrumbs.css' + function makeBread (root) { if (root.endsWith('/')) { root = root.substring(0, root.length - 1) @@ -35,36 +38,38 @@ function makeBread (root) { return parts } -class Breadcrumbs extends React.Component { - state = { - overflows: false - } +const Breadcrumbs = ({ t, tReady, path, onClick, className, onContextMenuHandle, ...props }) => { + const [overflows, setOverflows] = useState(false) + const anchors = useRef() + const buttonRef = useRef() + const [draggingOn, setDragging] = useState(null) - componentDidUpdate (_, prevState) { - const a = this.anchors - const overflows = a ? (a.offsetHeight < a.scrollHeight || a.offsetWidth < a.scrollWidth) : false + useEffect(() => { + const a = anchors.current - if (prevState.overflows !== overflows) { - this.setState({ overflows }) + const newOverflows = a ? (a.offsetHeight < a.scrollHeight || a.offsetWidth < a.scrollWidth) : false + if (newOverflows !== overflows) { + setOverflows(newOverflows) } - } + }, [overflows]) - render () { - const { t, tReady, path, onClick, className = '', ...props } = this.props + const cls = `Breadcrumbs flex items-center sans-serif overflow-hidden ${className}` + const bread = makeBread(path) + const root = bread[0] - const cls = `Breadcrumbs flex items-center sans-serif overflow-hidden ${className}` - const bread = makeBread(path) - const root = bread[0] + if (root.name === 'files' || root.name === 'pins') { + bread.shift() + } - if (root.name === 'files' || root.name === 'pins') { - bread.shift() - } + const handleOnContextMenuHandle = (ev) => onContextMenuHandle(ev, buttonRef.current) + const createItems = () => { const res = bread.map((link, index) => ([ { link.disabled ? {link.name} - : } @@ -80,9 +85,12 @@ class Breadcrumbs extends React.Component { res.reverse() - return ( - + ) } Breadcrumbs.propTypes = { path: PropTypes.string.isRequired, onClick: PropTypes.func.isRequired, t: PropTypes.func.isRequired, - tReady: PropTypes.bool.isRequired + tReady: PropTypes.bool.isRequired, + onContextMenuHandle: PropTypes.func } export default withTranslation('files')(Breadcrumbs) diff --git a/src/files/header/Header.js b/src/files/header/Header.js index 87ae220d0..e446803af 100644 --- a/src/files/header/Header.js +++ b/src/files/header/Header.js @@ -40,6 +40,13 @@ class Header extends React.Component { }, pos) } + handleBreadCrumbsContextMenu = (ev, breadcrumbsRef) => { + const pos = breadcrumbsRef.getBoundingClientRect() + this.props.handleContextMenu(ev, 'RIGHT', { + ...this.props.files + }, pos) + } + render () { const { files, @@ -55,7 +62,7 @@ class Header extends React.Component { return (
- + this.handleBreadCrumbsContextMenu(ev, ref)}/>
From ff05fc78578b16f0330d4e10aa1c8a3df7c3302c Mon Sep 17 00:00:00 2001 From: Rafael Ramalho Date: Mon, 31 Aug 2020 17:11:20 +0100 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20add=20super=20breadcrumbs=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 22 +-- package.json | 4 +- src/files/FilesPage.js | 1 + src/files/breadcrumbs/Breadcrumbs.css | 6 +- src/files/breadcrumbs/Breadcrumbs.js | 161 +++++++-------- src/files/file/File.js | 271 +++++++++++--------------- src/files/files-list/FilesList.js | 35 ++-- src/files/header/Header.js | 4 +- src/lib/dnd-backend.js | 2 +- 9 files changed, 241 insertions(+), 265 deletions(-) diff --git a/package-lock.json b/package-lock.json index e03d6a413..7dd69e2ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9455,9 +9455,9 @@ "dev": true }, "dnd-core": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-10.0.2.tgz", - "integrity": "sha512-PrxEjxF0+6Y1n1n1Z9hSWZ1tvnDXv9syL+BccV1r1RC08uWNsyetf8AnWmUF3NgYPwy0HKQJwTqGkZK+1NlaFA==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-11.1.3.tgz", + "integrity": "sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA==", "requires": { "@react-dnd/asap": "^4.0.0", "@react-dnd/invariant": "^2.0.0", @@ -22774,22 +22774,22 @@ } }, "react-dnd": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-10.0.2.tgz", - "integrity": "sha512-SC2Ymvntynhoqtf5zaFhZscm9xenCoMofilxPdlwUlaelAzmbl9fw82C4ZJ//+lNm3kWAKXjGDZg2/aWjKEAtg==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz", + "integrity": "sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==", "requires": { "@react-dnd/shallowequal": "^2.0.0", "@types/hoist-non-react-statics": "^3.3.1", - "dnd-core": "^10.0.2", + "dnd-core": "^11.1.3", "hoist-non-react-statics": "^3.3.0" } }, "react-dnd-html5-backend": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-10.0.2.tgz", - "integrity": "sha512-ny17gUdInZ6PIGXdzfwPhoztRdNVVvjoJMdG80hkDBamJBeUPuNF2Wv4D3uoQJLjXssX1+i9PhBqc7EpogClwQ==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz", + "integrity": "sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw==", "requires": { - "dnd-core": "^10.0.2" + "dnd-core": "^11.1.3" } }, "react-docgen": { diff --git a/package.json b/package.json index f5a7d2439..87e52d13b 100644 --- a/package.json +++ b/package.json @@ -68,8 +68,8 @@ "react-copy-to-clipboard": "^5.0.2", "react-country-flag": "^1.1.0", "react-debounce-render": "^5.0.0", - "react-dnd": "^10.0.2", - "react-dnd-html5-backend": "^10.0.2", + "react-dnd": "^11.1.3", + "react-dnd-html5-backend": "^11.1.3", "react-dom": "^16.13.1", "react-faux-dom": "^4.5.0", "react-helmet": "^5.2.1", diff --git a/src/files/FilesPage.js b/src/files/FilesPage.js index ad25a0295..3f9c40c6d 100644 --- a/src/files/FilesPage.js +++ b/src/files/FilesPage.js @@ -246,6 +246,7 @@ class FilesPage extends React.Component { files={files} onNavigate={this.props.doFilesNavigateTo} onAddFiles={this.onAddFiles} + onMove={this.props.doFilesMove} onAddByPath={(files) => this.showModal(ADD_BY_PATH, files)} onNewFolder={(files) => this.showModal(NEW_FOLDER, files)} onCliTutorMode={() => this.showModal(CLI_TUTOR_MODE)} diff --git a/src/files/breadcrumbs/Breadcrumbs.css b/src/files/breadcrumbs/Breadcrumbs.css index 14b329186..d8e75060a 100644 --- a/src/files/breadcrumbs/Breadcrumbs.css +++ b/src/files/breadcrumbs/Breadcrumbs.css @@ -10,6 +10,10 @@ opacity: 0; } +.BreadcrumbsButton.white::after { + background: inherit; +} + .BreadcrumbsButton.dragging::after { opacity: 1; -} \ No newline at end of file +} diff --git a/src/files/breadcrumbs/Breadcrumbs.js b/src/files/breadcrumbs/Breadcrumbs.js index 5a4b6559c..84db22985 100644 --- a/src/files/breadcrumbs/Breadcrumbs.js +++ b/src/files/breadcrumbs/Breadcrumbs.js @@ -2,47 +2,52 @@ import React, { useEffect, useState, useRef } from 'react' import classNames from 'classnames' import PropTypes from 'prop-types' import { withTranslation } from 'react-i18next' +import { NativeTypes } from 'react-dnd-html5-backend' import './Breadcrumbs.css' - -function makeBread (root) { - if (root.endsWith('/')) { - root = root.substring(0, root.length - 1) - } - - const parts = root.split('/').map(part => { - return { - name: part, - path: part - } +import { useDrop } from 'react-dnd' +import { basename, join } from 'path' +import { normalizeFiles } from '../../lib/files' + +const DropableBreadcrumb = ({ index, link, path, onAddFiles, onMove, onClick, onContextMenuHandle }) => { + const [{ isOver }, drop] = useDrop({ + accept: [NativeTypes.FILE, 'FILE'], + drop: ({ files, filesPromise, path: filePath }) => { + if (files) { + (async () => { + const files = await filesPromise + onAddFiles(await normalizeFiles(files), path) + })() + } else { + const src = filePath + const dst = join(path, basename(filePath)) + + onMove(src, dst) + } + }, + collect: (monitor) => ({ + isOver: monitor.isOver() + }) }) - for (let i = 1; i < parts.length; i++) { - const name = parts[i].name - - parts[i] = { - name: name, - path: parts[i - 1].path + '/' + parts[i].path - } - - if (name.length >= 30) { - parts[i].realName = name - parts[i].name = `${name.substring(0, 4)}...${name.substring(name.length - 4, name.length)}` - } - } + const buttonRef = useRef() - parts.shift() - parts[0].disabled = true + const handleOnContextMenuHandle = (ev) => onContextMenuHandle(ev, buttonRef.current) - parts[parts.length - 1].last = true - return parts + return ( + + + + ) } -const Breadcrumbs = ({ t, tReady, path, onClick, className, onContextMenuHandle, ...props }) => { +const Breadcrumbs = ({ t, tReady, path, onClick, className, onContextMenuHandle, onAddFiles, onMove, ...props }) => { const [overflows, setOverflows] = useState(false) const anchors = useRef() - const buttonRef = useRef() - const [draggingOn, setDragging] = useState(null) useEffect(() => { const a = anchors.current @@ -53,56 +58,22 @@ const Breadcrumbs = ({ t, tReady, path, onClick, className, onContextMenuHandle, } }, [overflows]) - const cls = `Breadcrumbs flex items-center sans-serif overflow-hidden ${className}` - const bread = makeBread(path) - const root = bread[0] - - if (root.name === 'files' || root.name === 'pins') { - bread.shift() - } - - const handleOnContextMenuHandle = (ev) => onContextMenuHandle(ev, buttonRef.current) - - const createItems = () => { - const res = bread.map((link, index) => ([ - - { link.disabled - ? {link.name} - : - } - , - /* eslint-disable-next-line jsx-a11y/anchor-is-valid */ - / - ])) - - if (res.length === 0) { - /* eslint-disable-next-line jsx-a11y/anchor-is-valid */ - res.push(/) - } - - res.reverse() - - return res - } + const bread = makeBread(path, t) return ( -