2018-08-06 10:29:12 +00:00
|
|
|
import React from 'react';
|
2018-10-16 10:19:51 +00:00
|
|
|
import PropTypes from 'prop-types';
|
2019-04-11 13:04:47 +00:00
|
|
|
import TextTranslation from '../../utils/text-translation';
|
2018-08-06 10:29:12 +00:00
|
|
|
import TreeNodeView from './tree-node-view';
|
2019-04-11 13:04:47 +00:00
|
|
|
import ContextMenu from '../context-menu/context-menu';
|
|
|
|
import { hideMenu, showMenu } from '../context-menu/actions';
|
2019-03-23 06:16:48 +00:00
|
|
|
|
2018-10-16 10:19:51 +00:00
|
|
|
const propTypes = {
|
2019-02-20 03:54:25 +00:00
|
|
|
repoPermission: PropTypes.bool,
|
2019-01-28 08:48:03 +00:00
|
|
|
isNodeMenuShow: PropTypes.bool.isRequired,
|
2018-10-16 10:19:51 +00:00
|
|
|
treeData: PropTypes.object.isRequired,
|
2019-01-28 08:48:03 +00:00
|
|
|
currentPath: PropTypes.string.isRequired,
|
|
|
|
onMenuItemClick: PropTypes.func,
|
2018-10-16 10:19:51 +00:00
|
|
|
onNodeClick: PropTypes.func.isRequired,
|
2019-01-28 08:48:03 +00:00
|
|
|
onNodeExpanded: PropTypes.func.isRequired,
|
|
|
|
onNodeCollapse: PropTypes.func.isRequired,
|
2019-03-27 03:25:27 +00:00
|
|
|
onItemMove: PropTypes.func,
|
|
|
|
currentRepoInfo: PropTypes.object,
|
2019-04-11 13:04:47 +00:00
|
|
|
selectedDirentList: PropTypes.array.isRequired,
|
2018-10-16 10:19:51 +00:00
|
|
|
};
|
|
|
|
|
2019-03-04 09:45:28 +00:00
|
|
|
const PADDING_LEFT = 20;
|
2019-01-28 08:48:03 +00:00
|
|
|
|
|
|
|
class TreeView extends React.Component {
|
2018-08-06 10:29:12 +00:00
|
|
|
|
2019-01-28 08:48:03 +00:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
isItemFreezed: false,
|
2019-04-09 06:58:40 +00:00
|
|
|
isTreeViewDropTipShow: false,
|
2019-01-28 08:48:03 +00:00
|
|
|
};
|
2018-08-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
|
2019-03-27 03:25:27 +00:00
|
|
|
onItemMove = (repo, dirent, selectedPath, currentPath) => {
|
|
|
|
this.props.onItemMove(repo, dirent, selectedPath, currentPath);
|
|
|
|
}
|
|
|
|
|
2019-01-28 08:48:03 +00:00
|
|
|
onNodeDragStart = (e, node) => {
|
2019-03-27 03:25:27 +00:00
|
|
|
let dragStartNodeData = {nodeDirent: node.object, nodeParentPath: node.parentNode.path, nodeRootPath: node.path};
|
|
|
|
dragStartNodeData = JSON.stringify(dragStartNodeData);
|
|
|
|
|
|
|
|
e.dataTransfer.effectAllowed = "move";
|
|
|
|
e.dataTransfer.setData('applicaiton/drag-item-info', dragStartNodeData);
|
|
|
|
}
|
|
|
|
|
|
|
|
onNodeDragEnter = (e, node) => {
|
2019-04-09 06:58:40 +00:00
|
|
|
e.persist()
|
|
|
|
if (e.target.className === 'tree-view tree ') {
|
|
|
|
this.setState({
|
|
|
|
isTreeViewDropTipShow: true,
|
|
|
|
})
|
|
|
|
}
|
2019-03-27 03:25:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
onNodeDragMove = (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
e.dataTransfer.dropEffect = 'move';
|
|
|
|
}
|
|
|
|
|
|
|
|
onNodeDragLeave = (e, node) => {
|
2019-04-09 06:58:40 +00:00
|
|
|
if (e.target.className === 'tree-view tree tree-view-drop') {
|
|
|
|
this.setState({
|
|
|
|
isTreeViewDropTipShow: false,
|
|
|
|
})
|
|
|
|
}
|
2019-03-27 03:25:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
onNodeDrop = (e, node) => {
|
|
|
|
if (e.dataTransfer.files.length) { // uploaded files
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let dragStartNodeData = e.dataTransfer.getData('applicaiton/drag-item-info');
|
|
|
|
dragStartNodeData = JSON.parse(dragStartNodeData);
|
|
|
|
|
|
|
|
let {nodeDirent, nodeParentPath, nodeRootPath} = dragStartNodeData;
|
|
|
|
let dropNodeData = node;
|
|
|
|
|
2019-04-09 06:58:40 +00:00
|
|
|
if (!dropNodeData) {
|
2019-04-09 08:07:57 +00:00
|
|
|
if (nodeParentPath === '/') {
|
|
|
|
this.setState({isTreeViewDropTipShow: false});
|
|
|
|
return;
|
|
|
|
}
|
2019-04-09 06:58:40 +00:00
|
|
|
this.onItemMove(this.props.currentRepoInfo, nodeDirent, '/', nodeParentPath);
|
2019-04-09 08:07:57 +00:00
|
|
|
this.setState({isTreeViewDropTipShow: false});
|
2019-04-09 06:58:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-27 03:25:27 +00:00
|
|
|
if (dropNodeData.object.type !== 'dir') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-09 08:07:57 +00:00
|
|
|
if (nodeParentPath === dropNodeData.path) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-27 03:25:27 +00:00
|
|
|
// copy the dirent to itself. eg: A/B -> A/B
|
|
|
|
if (nodeParentPath === dropNodeData.parentNode.path) {
|
|
|
|
if (dropNodeData.object.name === nodeDirent.name) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy the dirent to it's child. eg: A/B -> A/B/C
|
|
|
|
if (dropNodeData.object.type === 'dir' && nodeDirent.type === 'dir') {
|
|
|
|
if (dropNodeData.parentNode.path !== nodeParentPath) {
|
|
|
|
if (dropNodeData.path.indexOf(nodeRootPath) !== -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.onItemMove(this.props.currentRepoInfo, nodeDirent, dropNodeData.path, nodeParentPath);
|
2018-08-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
|
2019-01-28 08:48:03 +00:00
|
|
|
onFreezedItem = () => {
|
|
|
|
this.setState({isItemFreezed: true});
|
2018-08-22 08:39:42 +00:00
|
|
|
}
|
|
|
|
|
2019-01-28 08:48:03 +00:00
|
|
|
onUnFreezedItem = () => {
|
|
|
|
this.setState({isItemFreezed: false});
|
2018-08-22 08:39:42 +00:00
|
|
|
}
|
|
|
|
|
2019-04-11 13:04:47 +00:00
|
|
|
onMenuItemClick = (operation, node) => {
|
|
|
|
this.props.onMenuItemClick(operation, node);
|
|
|
|
hideMenu();
|
|
|
|
}
|
2019-04-08 03:35:46 +00:00
|
|
|
|
2019-04-11 13:04:47 +00:00
|
|
|
onMouseDown = (event) => {
|
|
|
|
event.stopPropagation();
|
|
|
|
if (event.button === 2) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2019-03-23 06:16:48 +00:00
|
|
|
|
2019-04-11 13:04:47 +00:00
|
|
|
onContextMenu = (event) => {
|
|
|
|
this.handleContextClick(event);
|
2019-03-23 06:16:48 +00:00
|
|
|
}
|
2019-04-11 13:04:47 +00:00
|
|
|
|
|
|
|
handleContextClick = (event, node) => {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
2019-03-23 06:16:48 +00:00
|
|
|
|
2019-04-11 13:04:47 +00:00
|
|
|
let x = event.clientX || (event.touches && event.touches[0].pageX);
|
|
|
|
let y = event.clientY || (event.touches && event.touches[0].pageY);
|
|
|
|
|
|
|
|
if (this.props.posX) {
|
|
|
|
x -= this.props.posX;
|
|
|
|
}
|
|
|
|
if (this.props.posY) {
|
|
|
|
y -= this.props.posY;
|
|
|
|
}
|
|
|
|
|
|
|
|
hideMenu();
|
|
|
|
|
|
|
|
let menuList = this.getMenuList(node);
|
|
|
|
|
|
|
|
let showMenuConfig = {
|
|
|
|
id: 'tree-node-contextmenu',
|
|
|
|
position: { x, y },
|
|
|
|
target: event.target,
|
|
|
|
currentObject: node,
|
|
|
|
menuList: menuList,
|
|
|
|
};
|
|
|
|
|
|
|
|
showMenu(showMenuConfig);
|
2019-03-23 06:16:48 +00:00
|
|
|
}
|
|
|
|
|
2019-04-11 13:04:47 +00:00
|
|
|
getMenuList = (node) => {
|
|
|
|
let menuList = [];
|
|
|
|
|
2019-04-17 02:48:44 +00:00
|
|
|
let { NEW_FOLDER, NEW_FILE, COPY, MOVE, RENAME, DELETE, OPEN_VIA_CLIENT, TAGS} = TextTranslation;
|
2019-04-11 13:04:47 +00:00
|
|
|
|
|
|
|
if (!node) {
|
|
|
|
return [NEW_FOLDER, NEW_FILE];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node.object.type === 'dir') {
|
|
|
|
menuList = [NEW_FOLDER, NEW_FILE, COPY, MOVE, RENAME, DELETE];
|
|
|
|
} else {
|
2019-04-17 02:48:44 +00:00
|
|
|
menuList = [RENAME, DELETE, COPY, MOVE, TAGS, OPEN_VIA_CLIENT];
|
2019-04-11 13:04:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return menuList;
|
2019-03-23 06:16:48 +00:00
|
|
|
}
|
|
|
|
|
2019-04-11 13:04:47 +00:00
|
|
|
onShowMenu = () => {
|
|
|
|
this.onFreezedItem();
|
2019-03-23 06:16:48 +00:00
|
|
|
}
|
|
|
|
|
2019-04-11 13:04:47 +00:00
|
|
|
onHideMenu = () => {
|
|
|
|
this.onUnFreezedItem();
|
2019-03-23 06:16:48 +00:00
|
|
|
}
|
|
|
|
|
2018-08-22 08:39:42 +00:00
|
|
|
render() {
|
|
|
|
return (
|
2019-04-11 13:04:47 +00:00
|
|
|
<div
|
|
|
|
className={`tree-view tree ${this.state.isTreeViewDropTipShow ? 'tree-view-drop' : ''}`}
|
|
|
|
onDrop={this.onNodeDrop}
|
|
|
|
onDragEnter={this.onNodeDragEnter}
|
|
|
|
onDragLeave={this.onNodeDragLeave}
|
|
|
|
onMouseDown={this.onMouseDown}
|
|
|
|
onContextMenu={this.onContextMenu}
|
|
|
|
>
|
2019-01-28 08:48:03 +00:00
|
|
|
<TreeNodeView
|
2019-02-20 03:54:25 +00:00
|
|
|
repoPermission={this.props.repoPermission}
|
2018-08-22 08:39:42 +00:00
|
|
|
node={this.props.treeData.root}
|
2018-11-22 03:26:00 +00:00
|
|
|
currentPath={this.props.currentPath}
|
2019-01-28 08:48:03 +00:00
|
|
|
paddingLeft={PADDING_LEFT}
|
|
|
|
isNodeMenuShow={this.props.isNodeMenuShow}
|
|
|
|
isItemFreezed={this.state.isItemFreezed}
|
|
|
|
onNodeClick={this.props.onNodeClick}
|
|
|
|
onMenuItemClick={this.props.onMenuItemClick}
|
|
|
|
onNodeExpanded={this.props.onNodeExpanded}
|
|
|
|
onNodeCollapse={this.props.onNodeCollapse}
|
|
|
|
onNodeDragStart={this.onNodeDragStart}
|
|
|
|
onFreezedItem={this.onFreezedItem}
|
|
|
|
onUnFreezedItem={this.onUnFreezedItem}
|
2019-03-27 03:25:27 +00:00
|
|
|
onNodeDragMove={this.onNodeDragMove}
|
|
|
|
onNodeDrop={this.onNodeDrop}
|
|
|
|
onNodeDragEnter={this.onNodeDragEnter}
|
|
|
|
onNodeDragLeave={this.onNodeDragLeave}
|
2019-04-11 13:04:47 +00:00
|
|
|
handleContextClick={this.handleContextClick}
|
|
|
|
/>
|
|
|
|
<ContextMenu
|
|
|
|
id={'tree-node-contextmenu'}
|
|
|
|
onMenuItemClick={this.onMenuItemClick}
|
|
|
|
onHideMenu={this.onHideMenu}
|
|
|
|
onShowMenu={this.onShowMenu}
|
2018-08-22 08:39:42 +00:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
2018-08-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-16 10:19:51 +00:00
|
|
|
TreeView.propTypes = propTypes;
|
|
|
|
|
2018-08-06 10:29:12 +00:00
|
|
|
export default TreeView;
|