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:
@@ -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();
|
||||
|
@@ -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
|
||||
|
@@ -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>
|
||||
|
165
frontend/src/components/tree-view/tree-view-contextmenu.js
Normal file
165
frontend/src/components/tree-view/tree-view-contextmenu.js
Normal 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;
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
@@ -39,6 +39,7 @@
|
||||
margin-left: -10px;
|
||||
padding: 12px 12px 12px 0;
|
||||
line-height: 1.5;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tree-node-inner {
|
||||
|
35
frontend/src/css/tree-view-contextmenu.css
Normal file
35
frontend/src/css/tree-view-contextmenu.css
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user