1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-06 17:33:18 +00:00

Add right-click menu, move and copy effects (#3148)

This commit is contained in:
zxj96
2019-03-23 14:16:48 +08:00
committed by Daniel Pan
parent 2327106844
commit 5a6ab8ab07
7 changed files with 293 additions and 4 deletions

View File

@@ -71,10 +71,18 @@ class DirColumnNav extends React.Component {
this.setState({opNode: node});
switch (operation) {
case 'New Folder':
if (!node) {
this.onAddFolderToggle('root');
} else {
this.onAddFolderToggle();
}
break;
case 'New File':
if (!node) {
this.onAddFileToggle('root');
} else {
this.onAddFileToggle();
}
break;
case 'Rename':
this.onRenameToggle();

View File

@@ -8,6 +8,8 @@ const propTypes = {
onMenuItemClick: PropTypes.func.isRequired,
onFreezedItem: PropTypes.func.isRequired,
onUnFreezedItem: PropTypes.func.isRequired,
registerHandlers: PropTypes.func,
unregisterHandlers: PropTypes.func,
};
class TreeNodeMenu extends React.Component {
@@ -91,6 +93,8 @@ class TreeNodeMenu extends React.Component {
}
render() {
this.state.isItemMenuShow ? this.props.unregisterHandlers() : this.props.registerHandlers()
return (
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleOperationMenu}>
<DropdownToggle

View File

@@ -17,6 +17,8 @@ const propTypes = {
onFreezedItem: PropTypes.func.isRequired,
onUnFreezedItem: PropTypes.func.isRequired,
onMenuItemClick: PropTypes.func,
registerHandlers: PropTypes.func,
unregisterHandlers: PropTypes.func,
};
class TreeNodeView extends React.Component {
@@ -36,6 +38,7 @@ class TreeNodeView extends React.Component {
isHighlight: true,
});
}
this.props.onNodeChanged(this.props.node)
}
onMouseLeave = () => {
@@ -45,6 +48,7 @@ class TreeNodeView extends React.Component {
isHighlight: false,
});
}
this.props.onNodeChanged(null)
}
onNodeClick = () => {
@@ -127,6 +131,9 @@ class TreeNodeView extends React.Component {
onFreezedItem={this.props.onFreezedItem}
onMenuItemClick={this.onMenuItemClick}
onUnFreezedItem={this.onUnFreezedItem}
onNodeChanged={this.props.onNodeChanged}
registerHandlers={this.props.registerHandlers}
unregisterHandlers={this.props.unregisterHandlers}
/>
);
})}
@@ -165,6 +172,8 @@ class TreeNodeView extends React.Component {
onMenuItemClick={this.onMenuItemClick}
onUnFreezedItem={this.onUnFreezedItem}
onFreezedItem={this.props.onFreezedItem}
registerHandlers={this.props.registerHandlers}
unregisterHandlers={this.props.unregisterHandlers}
/>
)}
</div>

View File

@@ -0,0 +1,165 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import '../../css/tree-view-contextmenu.css'
const propTypes = {
onMenuItemClick: PropTypes.func.isRequired,
node: PropTypes.object,
mousePosition: PropTypes.object,
closeRightMenu: PropTypes.func,
registerHandlers:PropTypes.func,
unregisterHandlers:PropTypes.func
};
class TreeViewContextMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
isItemMenuShow: false,
menuList: [],
};
}
componentDidMount() {
let menuList = this.caculateMenuList();
this.setState({menuList: menuList});
}
componentDidUpdate() {
this.calculateMenuDistance();
}
componentWillUnmount() {
document.removeEventListener('click', this.listenClick);
document.removeEventListener('mousemove', this.handleContextMenu);
}
caculateMenuList() {
let { node } = this.props;
let menuList = [];
if (!node) {
menuList = ['New Folder', 'New File'];
} else {
if (node.object.type === 'dir') {
menuList = ['New Folder', 'New File', 'Copy', 'Move', 'Rename', 'Delete'];
} else {
menuList = ['Rename', 'Delete', 'Copy', 'Move', 'Open in New Tab'];
}
}
return menuList;
}
calculateMenuDistance = () => {
let mousePosition = this.props.mousePosition;
let rightTreeMenu = document.querySelector('.right-tree-menu');
let rightTreeMenuHeight = rightTreeMenu.offsetHeight;
if (mousePosition.clientY + rightTreeMenuHeight > document.body.clientHeight) {
rightTreeMenu.style.top = mousePosition.clientY - rightTreeMenuHeight + 'px';
} else {
rightTreeMenu.style.top = mousePosition.clientY + 'px';
}
rightTreeMenu.style.left = mousePosition.clientX + 'px';
document.addEventListener('click', this.listenClick);
document.addEventListener('mousemove', this.handleContextMenu);
}
translateMenuItem = (menuItem) => {
let translateResult = '';
switch(menuItem) {
case 'New Folder':
translateResult = gettext('New Folder');
break;
case 'New File':
translateResult = gettext('New File');
break;
case 'Rename':
translateResult = gettext('Rename');
break;
case 'Copy':
translateResult = gettext('Copy');
break;
case 'Move':
translateResult = gettext('Move');
break;
case 'Delete':
translateResult = gettext('Delete');
break;
case 'Open in New Tab':
translateResult = gettext('Open in New Tab');
break;
default:
break;
}
return translateResult;
}
handleContextMenu = (e) => {
let mousePosition = this.props.mousePosition;
let rightTreeMenu = document.querySelector('.right-tree-menu');
let rightTreeMenuHeight = rightTreeMenu.offsetHeight;
let rightTreeMenuWidth = rightTreeMenu.offsetWidth;
if (mousePosition.clientY + rightTreeMenuHeight > document.body.clientHeight) {
if ((e.clientX >= mousePosition.clientX) && (e.clientX <= (mousePosition.clientX + rightTreeMenuWidth)) && (e.clientY <= mousePosition.clientY) && (e.clientY >= (mousePosition.clientY - rightTreeMenuHeight))) {
this.props.unregisterHandlers();
} else {
this.props.registerHandlers();
}
} else {
if ((e.clientX >= mousePosition.clientX) && (e.clientX <= (mousePosition.clientX + rightTreeMenuWidth)) && (e.clientY >= mousePosition.clientY) && (e.clientY <= (mousePosition.clientY + rightTreeMenuHeight))) {
this.props.unregisterHandlers();
} else {
this.props.registerHandlers();
}
}
}
listenClick = (e) => {
let mousePosition = this.props.mousePosition;
let rightTreeMenu = document.querySelector('.right-tree-menu');
let rightTreeMenuHeight = rightTreeMenu.offsetHeight;
let rightTreeMenuWidth = rightTreeMenu.offsetWidth;
if ((e.clientX <= mousePosition.clientX) || (e.clientX >= (mousePosition.clientX + rightTreeMenuWidth))) {
this.props.closeRightMenu();
}
if (mousePosition.clientY + rightTreeMenuHeight > document.body.clientHeight) {
if ((e.clientY <= (mousePosition.clientY - rightTreeMenuHeight)) || e.clientY >= mousePosition.clientY) {
this.props.closeRightMenu();
}
} else {
if ((e.clientY <= mousePosition.clientY) || (e.clientY >= (mousePosition.clientY + rightTreeMenuHeight))) {
this.props.closeRightMenu();
}
}
}
onMenuItemClick = (event) => {
let operation = event.target.dataset.toggle;
let node = this.props.node;
this.props.onMenuItemClick(operation, node);
this.props.closeRightMenu();
}
render() {
return (
<div className='right-tree-menu'>
{this.state.menuList.map((menuItem, index) => {
return (
<button className='right-tree-item' key={index} data-toggle={menuItem} onClick={this.onMenuItemClick}>{this.translateMenuItem(menuItem)}</button>
);
})}
</div>
);
}
}
TreeViewContextMenu.propTypes = propTypes;
export default TreeViewContextMenu;

View File

@@ -2,6 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import TreeNodeView from './tree-node-view';
import TreeViewContextMenu from './tree-view-contextmenu'
const propTypes = {
repoPermission: PropTypes.bool,
isNodeMenuShow: PropTypes.bool.isRequired,
@@ -21,9 +23,21 @@ class TreeView extends React.Component {
super(props);
this.state = {
isItemFreezed: false,
isRightMenuShow: false,
nodeData: null,
fileData: null,
mousePosition: {clientX: '', clientY: ''},
};
}
componentDidMount() {
this.registerHandlers();
}
componentWillUnmount() {
this.unregisterHandlers();
}
onNodeDragStart = (e, node) => {
// todo
}
@@ -36,6 +50,46 @@ class TreeView extends React.Component {
this.setState({isItemFreezed: false});
}
contextMenu = (e) => {
e.preventDefault();
this.setState({
isRightMenuShow:false,
});
setTimeout(() => {
this.setState({
isRightMenuShow:true,
fileData:this.state.nodeData,
mousePosition: {clientX: e.clientX, clientY: e.clientY}
})
},40)
}
unregisterHandlers = () => {
let treeView = document.querySelector('.tree-view');
treeView.removeEventListener('contextmenu', this.contextMenu);
}
registerHandlers = () => {
let treeView = document.querySelector('.tree-view');
treeView.addEventListener('contextmenu', this.contextMenu);
}
onNodeChanged = (node) => {
this.setState({
nodeData:node
})
}
closeRightMenu = () => {
this.setState({
isRightMenuShow:false,
})
}
onMenuItemClick = (operation, node) => {
this.props.onMenuItemClick(operation, node)
}
render() {
return (
<div className="tree-view tree">
@@ -53,7 +107,20 @@ class TreeView extends React.Component {
onNodeDragStart={this.onNodeDragStart}
onFreezedItem={this.onFreezedItem}
onUnFreezedItem={this.onUnFreezedItem}
onNodeChanged={this.onNodeChanged}
registerHandlers={this.registerHandlers}
unregisterHandlers={this.unregisterHandlers}
/>
{this.state.isRightMenuShow && (
<TreeViewContextMenu
node={this.state.fileData}
onMenuItemClick={this.onMenuItemClick}
mousePosition={this.state.mousePosition}
closeRightMenu={this.closeRightMenu}
registerHandlers={this.registerHandlers}
unregisterHandlers={this.unregisterHandlers}
/>
)}
</div>
);
}

View File

@@ -39,6 +39,7 @@
margin-left: -10px;
padding: 12px 12px 12px 0;
line-height: 1.5;
flex: 1;
}
.tree-node-inner {

View File

@@ -0,0 +1,35 @@
.right-tree-menu {
position: absolute;
left: 15rem;
top: 20rem;
z-index: 1000;
float: left;
min-width: 10rem;
padding: 0.5rem 0;
margin: 0.125rem 0 0;
font-size: 0.9375rem;
color: #212529;
text-align: left;
list-style: none;
background-color: #fff;
background-clip: padding-box;
border: 1px solid rgba(0, 40, 100, 0.12);
border-radius: 3px;
}
.right-tree-item {
display: block;
width: 100%;
padding: 0.25rem 1.5rem;
clear: both;
font-weight: 400;
color: #878E9C;
text-align: inherit;
white-space: nowrap;
background-color: transparent;
border: 0;
cursor: pointer;
}
.right-tree-item:hover {
color: #15181A;
background-color: #F8F9FA;
}