mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-04 16:31:13 +00:00
Grid view support drag drop item to path (#6429)
* feature - drag and drop file to target path in grid view fix typo * update target path item background color when drag enter * fix bug - select-all checkbox is actived after the last item is moved to another path * fix bug - drag drop action conflict with multi-select by keyboard * remove console log * fix type error
This commit is contained in:
@@ -10,6 +10,7 @@ import ViewFileToolbar from '../../components/toolbar/view-file-toolbar';
|
|||||||
import { PRIVATE_FILE_TYPE } from '../../constants';
|
import { PRIVATE_FILE_TYPE } from '../../constants';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
|
currentRepoInfo: PropTypes.object.isRequired,
|
||||||
repoID: PropTypes.string.isRequired,
|
repoID: PropTypes.string.isRequired,
|
||||||
repoName: PropTypes.string.isRequired,
|
repoName: PropTypes.string.isRequired,
|
||||||
currentPath: PropTypes.string.isRequired,
|
currentPath: PropTypes.string.isRequired,
|
||||||
@@ -32,10 +33,18 @@ const propTypes = {
|
|||||||
repoTags: PropTypes.array.isRequired,
|
repoTags: PropTypes.array.isRequired,
|
||||||
filePermission: PropTypes.string,
|
filePermission: PropTypes.string,
|
||||||
onFileTagChanged: PropTypes.func.isRequired,
|
onFileTagChanged: PropTypes.func.isRequired,
|
||||||
|
onItemMove: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirPath extends React.Component {
|
class DirPath extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
dropTargetPath: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
onPathClick = (e) => {
|
onPathClick = (e) => {
|
||||||
let path = Utils.getEventData(e, 'path');
|
let path = Utils.getEventData(e, 'path');
|
||||||
this.props.onPathClick(path);
|
this.props.onPathClick(path);
|
||||||
@@ -54,6 +63,54 @@ class DirPath extends React.Component {
|
|||||||
this.props.onTabNavClick(tabName, id);
|
this.props.onTabNavClick(tabName, id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onDragEnter = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (Utils.isIEBrower()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
dropTargetPath: e.target.dataset.path,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onDragLeave = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (Utils.isIEBrower()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
dropTargetPath: '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onDragOver = (e) => {
|
||||||
|
if (Utils.isIEBrower()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
e.dataTransfer.dropEffect = 'move';
|
||||||
|
};
|
||||||
|
|
||||||
|
onDrop = (e) => {
|
||||||
|
if (Utils.isIEBrower()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.dataTransfer.files.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dragStartItemData = e.dataTransfer.getData('application/drag-item-info');
|
||||||
|
dragStartItemData = JSON.parse(dragStartItemData);
|
||||||
|
let { nodeDirent, nodeParentPath } = dragStartItemData;
|
||||||
|
|
||||||
|
let selectedPath = Utils.getEventData(e, 'path');
|
||||||
|
this.props.onItemMove(this.props.currentRepoInfo, nodeDirent, selectedPath, nodeParentPath);
|
||||||
|
this.setState({
|
||||||
|
dropTargetPath: '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
turnPathToLink = (path) => {
|
turnPathToLink = (path) => {
|
||||||
path = path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path;
|
path = path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path;
|
||||||
let pathList = path.split('/');
|
let pathList = path.split('/');
|
||||||
@@ -126,7 +183,16 @@ class DirPath extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<Fragment key={index} >
|
<Fragment key={index} >
|
||||||
<span className="path-split">/</span>
|
<span className="path-split">/</span>
|
||||||
<span className="path-item" data-path={nodePath} onClick={this.onPathClick} role="button">{item}</span>
|
<span
|
||||||
|
className={`path-item ${nodePath === this.state.dropTargetPath ? 'path-item-drop' : ''}`}
|
||||||
|
data-path={nodePath} onClick={this.onPathClick}
|
||||||
|
onDragEnter={this.onDragEnter}
|
||||||
|
onDragLeave={this.onDragLeave}
|
||||||
|
onDragOver={this.onDragOver}
|
||||||
|
onDrop={this.onDrop}
|
||||||
|
role="button">
|
||||||
|
{item}
|
||||||
|
</span>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import DirPath from './dir-path';
|
|||||||
import DirTool from './dir-tool';
|
import DirTool from './dir-tool';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
|
currentRepoInfo: PropTypes.object.isRequired,
|
||||||
repoID: PropTypes.string.isRequired,
|
repoID: PropTypes.string.isRequired,
|
||||||
repoName: PropTypes.string.isRequired,
|
repoName: PropTypes.string.isRequired,
|
||||||
userPerm: PropTypes.string,
|
userPerm: PropTypes.string,
|
||||||
@@ -38,6 +39,7 @@ const propTypes = {
|
|||||||
repoTags: PropTypes.array.isRequired,
|
repoTags: PropTypes.array.isRequired,
|
||||||
onFileTagChanged: PropTypes.func.isRequired,
|
onFileTagChanged: PropTypes.func.isRequired,
|
||||||
metadataViewId: PropTypes.string,
|
metadataViewId: PropTypes.string,
|
||||||
|
onItemMove: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class CurDirPath extends React.Component {
|
class CurDirPath extends React.Component {
|
||||||
@@ -60,6 +62,7 @@ class CurDirPath extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="cur-dir-path d-flex justify-content-between align-items-center">
|
<div className="cur-dir-path d-flex justify-content-between align-items-center">
|
||||||
<DirPath
|
<DirPath
|
||||||
|
currentRepoInfo={this.props.currentRepoInfo}
|
||||||
repoID={this.props.repoID}
|
repoID={this.props.repoID}
|
||||||
repoName={this.props.repoName}
|
repoName={this.props.repoName}
|
||||||
repoEncrypted={this.props.repoEncrypted}
|
repoEncrypted={this.props.repoEncrypted}
|
||||||
@@ -82,6 +85,7 @@ class CurDirPath extends React.Component {
|
|||||||
filePermission={this.props.filePermission}
|
filePermission={this.props.filePermission}
|
||||||
onFileTagChanged={this.props.onFileTagChanged}
|
onFileTagChanged={this.props.onFileTagChanged}
|
||||||
repoTags={this.props.repoTags}
|
repoTags={this.props.repoTags}
|
||||||
|
onItemMove={this.props.onItemMove}
|
||||||
/>
|
/>
|
||||||
{isDesktop &&
|
{isDesktop &&
|
||||||
<DirTool
|
<DirTool
|
||||||
|
@@ -56,27 +56,27 @@ class DirentGridItem extends React.Component {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
const { dirent, activeDirent } = this.props;
|
const { dirent } = this.props;
|
||||||
|
|
||||||
if (this.clickTimeout) {
|
if (this.clickTimeout) {
|
||||||
clearTimeout(this.clickTimeout);
|
clearTimeout(this.clickTimeout);
|
||||||
this.clickTimeout = null;
|
this.clickTimeout = null;
|
||||||
this.handleSingleClick(dirent, activeDirent, e);
|
this.handleSingleClick(dirent, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clickTimeout = setTimeout(() => {
|
this.clickTimeout = setTimeout(() => {
|
||||||
this.clickTimeout = null;
|
this.clickTimeout = null;
|
||||||
this.handleSingleClick(dirent, activeDirent, e);
|
this.handleSingleClick(dirent, e);
|
||||||
}, 100); // Clicks within 100 milliseconds is considered a single click.
|
}, 100); // Clicks within 100 milliseconds is considered a single click.
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSingleClick = (dirent, activeDirent, event) => {
|
handleSingleClick = (dirent, event) => {
|
||||||
if (!this.canPreview) {
|
if (!this.canPreview) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dirent === activeDirent && !event.metaKey && !event.ctrlKey) {
|
if (dirent.isSelected && !event.metaKey && !event.ctrlKey) {
|
||||||
this.handleDoubleClick(dirent, event);
|
this.handleDoubleClick(dirent, event);
|
||||||
} else {
|
} else {
|
||||||
this.props.onGridItemClick(dirent, event);
|
this.props.onGridItemClick(dirent, event);
|
||||||
@@ -113,6 +113,7 @@ class DirentGridItem extends React.Component {
|
|||||||
if (Utils.isIEBrower() || !this.canDrag) {
|
if (Utils.isIEBrower() || !this.canDrag) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dragStartItemData = { nodeDirent: this.props.dirent, nodeParentPath: this.props.path };
|
let dragStartItemData = { nodeDirent: this.props.dirent, nodeParentPath: this.props.path };
|
||||||
dragStartItemData = JSON.stringify(dragStartItemData);
|
dragStartItemData = JSON.stringify(dragStartItemData);
|
||||||
|
|
||||||
|
@@ -597,7 +597,6 @@ class DirentGridView extends React.Component {
|
|||||||
|
|
||||||
onGridItemMouseDown = (event) => {
|
onGridItemMouseDown = (event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
|
||||||
if (event.button === 2) {
|
if (event.button === 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -130,7 +130,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tree-node-drop {
|
.tree-node-drop {
|
||||||
background-color: rgb(255, 239, 178);
|
background-color: #FFEFB2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree-node-icon {
|
.tree-node-icon {
|
||||||
@@ -282,6 +282,10 @@
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dir-view-path .path-item-drop{
|
||||||
|
background-color: #FFEFB2;
|
||||||
|
}
|
||||||
|
|
||||||
.dir-view-path .path-split {
|
.dir-view-path .path-split {
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
|
@@ -191,6 +191,7 @@ class LibContentContainer extends React.Component {
|
|||||||
}
|
}
|
||||||
<div className="cur-view-path d-block" style={curViewPathStyle}>
|
<div className="cur-view-path d-block" style={curViewPathStyle}>
|
||||||
<CurDirPath
|
<CurDirPath
|
||||||
|
currentRepoInfo={this.props.currentRepoInfo}
|
||||||
repoID={repoID}
|
repoID={repoID}
|
||||||
repoName={this.props.currentRepoInfo.repo_name}
|
repoName={this.props.currentRepoInfo.repo_name}
|
||||||
repoEncrypted={this.props.repoEncrypted}
|
repoEncrypted={this.props.repoEncrypted}
|
||||||
@@ -223,6 +224,7 @@ class LibContentContainer extends React.Component {
|
|||||||
onFileTagChanged={this.props.onToolbarFileTagChanged}
|
onFileTagChanged={this.props.onToolbarFileTagChanged}
|
||||||
repoTags={this.props.repoTags}
|
repoTags={this.props.repoTags}
|
||||||
metadataViewId={this.props.metadataViewId}
|
metadataViewId={this.props.metadataViewId}
|
||||||
|
onItemMove={this.props.onItemMove}
|
||||||
/>
|
/>
|
||||||
<ToolbarForSelectedDirents
|
<ToolbarForSelectedDirents
|
||||||
repoID={this.props.repoID}
|
repoID={this.props.repoID}
|
||||||
|
@@ -1912,7 +1912,7 @@ class LibContentView extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
selectedDirentList: selectedDirentList,
|
selectedDirentList: selectedDirentList,
|
||||||
isDirentSelected: selectedDirentList.length > 0,
|
isDirentSelected: selectedDirentList.length > 0,
|
||||||
isAllDirentSelected: selectedDirentList.length === newDirentList.length,
|
isAllDirentSelected: newDirentList.length ? selectedDirentList.length === newDirentList.length : false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user