2019-02-20 03:54:25 +00:00
|
|
|
import React, { Fragment } from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import TreeView from '../../components/tree-view/tree-view';
|
|
|
|
import Loading from '../../components/loading';
|
|
|
|
import ModalPortal from '../../components/modal-portal';
|
|
|
|
import Rename from '../../components/dialog/rename-dialog';
|
2019-03-20 03:04:36 +00:00
|
|
|
import Copy from '../../components/dialog/copy-dirent-dialog';
|
|
|
|
import Move from '../../components/dialog/move-dirent-dialog';
|
2019-02-20 03:54:25 +00:00
|
|
|
import CreateFolder from '../../components/dialog/create-folder-dialog';
|
|
|
|
import CreateFile from '../../components/dialog/create-file-dialog';
|
2019-04-09 09:24:44 +00:00
|
|
|
import ImageDialog from '../../components/dialog/image-dialog';
|
2019-04-11 13:04:47 +00:00
|
|
|
import { siteRoot, thumbnailSizeForOriginal } from '../../utils/constants';
|
2024-06-12 07:31:14 +00:00
|
|
|
import { gettext } from '../../utils/constants';
|
2019-02-27 09:34:39 +00:00
|
|
|
import { Utils } from '../../utils/utils';
|
2024-06-19 03:31:38 +00:00
|
|
|
import TreeSection from '../../components/tree-section';
|
|
|
|
import DirViews from './dir-views';
|
2024-07-09 01:45:50 +00:00
|
|
|
import DirOthers from './dir-others';
|
|
|
|
|
|
|
|
import './dir-column-nav.css';
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
const propTypes = {
|
2019-02-21 09:37:04 +00:00
|
|
|
currentPath: PropTypes.string.isRequired,
|
2021-09-13 02:37:07 +00:00
|
|
|
userPerm: PropTypes.string.isRequired,
|
2019-02-20 03:54:25 +00:00
|
|
|
isTreeDataLoading: PropTypes.bool.isRequired,
|
|
|
|
treeData: PropTypes.object.isRequired,
|
2019-02-21 09:37:04 +00:00
|
|
|
currentNode: PropTypes.object,
|
2019-02-20 03:54:25 +00:00
|
|
|
onNodeClick: PropTypes.func.isRequired,
|
|
|
|
onNodeCollapse: PropTypes.func.isRequired,
|
|
|
|
onNodeExpanded: PropTypes.func.isRequired,
|
|
|
|
onRenameNode: PropTypes.func.isRequired,
|
|
|
|
onDeleteNode: PropTypes.func.isRequired,
|
|
|
|
onAddFileNode: PropTypes.func.isRequired,
|
|
|
|
onAddFolderNode: PropTypes.func.isRequired,
|
2019-02-27 09:34:39 +00:00
|
|
|
repoID: PropTypes.string.isRequired,
|
2019-02-28 06:33:21 +00:00
|
|
|
navRate: PropTypes.number,
|
|
|
|
inResizing: PropTypes.bool.isRequired,
|
2019-03-15 07:48:16 +00:00
|
|
|
currentRepoInfo: PropTypes.object.isRequired,
|
2019-05-14 02:15:09 +00:00
|
|
|
onItemMove: PropTypes.func.isRequired,
|
|
|
|
onItemCopy: PropTypes.func.isRequired,
|
2019-04-11 13:04:47 +00:00
|
|
|
selectedDirentList: PropTypes.array.isRequired,
|
2019-06-17 08:11:54 +00:00
|
|
|
onItemsMove: PropTypes.func.isRequired,
|
2024-07-17 02:45:53 +00:00
|
|
|
getMenuContainerSize: PropTypes.func,
|
2019-02-20 03:54:25 +00:00
|
|
|
};
|
|
|
|
|
2019-02-21 09:37:04 +00:00
|
|
|
class DirColumnNav extends React.Component {
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
opNode: null,
|
|
|
|
isAddFileDialogShow: false,
|
|
|
|
isAddFolderDialogShow: false,
|
|
|
|
isRenameDialogShow: false,
|
2019-03-15 07:48:16 +00:00
|
|
|
isNodeImagePopupOpen: false,
|
|
|
|
imageNodeItems: [],
|
|
|
|
imageIndex: 0,
|
2019-03-20 03:04:36 +00:00
|
|
|
isCopyDialogShow: false,
|
|
|
|
isMoveDialogShow: false,
|
2019-03-23 06:16:48 +00:00
|
|
|
isMutipleOperation: false,
|
2019-02-20 03:54:25 +00:00
|
|
|
};
|
|
|
|
this.isNodeMenuShow = true;
|
|
|
|
}
|
|
|
|
|
2023-12-06 09:24:05 +00:00
|
|
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
2019-02-21 09:37:04 +00:00
|
|
|
this.setState({opNode: nextProps.currentNode});
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
onNodeClick = (node) => {
|
|
|
|
this.setState({opNode: node});
|
2024-06-29 09:58:27 +00:00
|
|
|
if (Utils.imageCheck(node?.object?.name || '')) {
|
2019-03-15 07:48:16 +00:00
|
|
|
this.showNodeImagePopup(node);
|
|
|
|
return;
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
this.props.onNodeClick(node);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onMenuItemClick = (operation, node) => {
|
|
|
|
this.setState({opNode: node});
|
|
|
|
switch (operation) {
|
|
|
|
case 'New Folder':
|
2019-03-23 06:16:48 +00:00
|
|
|
if (!node) {
|
|
|
|
this.onAddFolderToggle('root');
|
|
|
|
} else {
|
|
|
|
this.onAddFolderToggle();
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
break;
|
|
|
|
case 'New File':
|
2019-03-23 06:16:48 +00:00
|
|
|
if (!node) {
|
|
|
|
this.onAddFileToggle('root');
|
|
|
|
} else {
|
|
|
|
this.onAddFileToggle();
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
break;
|
|
|
|
case 'Rename':
|
|
|
|
this.onRenameToggle();
|
|
|
|
break;
|
|
|
|
case 'Delete':
|
2019-12-16 04:01:41 +00:00
|
|
|
this.onDeleteNode(node);
|
2019-02-20 03:54:25 +00:00
|
|
|
break;
|
2019-03-20 03:04:36 +00:00
|
|
|
case 'Copy':
|
|
|
|
this.onCopyToggle();
|
|
|
|
break;
|
|
|
|
case 'Move':
|
|
|
|
this.onMoveToggle();
|
|
|
|
break;
|
2019-02-27 09:34:39 +00:00
|
|
|
case 'Open in New Tab':
|
|
|
|
this.onOpenFile(node);
|
|
|
|
break;
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onAddFileToggle = (type) => {
|
|
|
|
if (type === 'root') {
|
|
|
|
let root = this.props.treeData.root;
|
|
|
|
this.setState({
|
|
|
|
isAddFileDialogShow: !this.state.isAddFileDialogShow,
|
|
|
|
opNode: root,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.setState({isAddFileDialogShow: !this.state.isAddFileDialogShow});
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2020-11-02 05:56:35 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
onAddFolderToggle = (type) => {
|
|
|
|
if (type === 'root') {
|
|
|
|
let root = this.props.treeData.root;
|
|
|
|
this.setState({
|
|
|
|
isAddFolderDialogShow: !this.state.isAddFolderDialogShow,
|
|
|
|
opNode: root,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.setState({isAddFolderDialogShow: !this.state.isAddFolderDialogShow});
|
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onRenameToggle = () => {
|
|
|
|
this.setState({isRenameDialogShow: !this.state.isRenameDialogShow});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-03-23 06:16:48 +00:00
|
|
|
onCopyToggle = () => {
|
2019-04-09 09:24:44 +00:00
|
|
|
this.setState({isCopyDialogShow: !this.state.isCopyDialogShow});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-20 03:04:36 +00:00
|
|
|
|
|
|
|
onMoveToggle = () => {
|
2019-04-09 09:24:44 +00:00
|
|
|
this.setState({isMoveDialogShow: !this.state.isMoveDialogShow});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-20 03:04:36 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
onAddFolderNode = (dirPath) => {
|
|
|
|
this.setState({isAddFolderDialogShow: !this.state.isAddFolderDialogShow});
|
|
|
|
this.props.onAddFolderNode(dirPath);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
onRenameNode = (newName) => {
|
|
|
|
this.setState({isRenameDialogShow: !this.state.isRenameDialogShow});
|
|
|
|
let node = this.state.opNode;
|
|
|
|
this.props.onRenameNode(node, newName);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-12-16 04:01:41 +00:00
|
|
|
onDeleteNode = (node) => {
|
2019-02-20 03:54:25 +00:00
|
|
|
this.props.onDeleteNode(node);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-02-27 09:34:39 +00:00
|
|
|
onOpenFile = (node) => {
|
|
|
|
let newUrl = siteRoot + 'lib/' + this.props.repoID + '/file' + Utils.encodePath(node.path);
|
|
|
|
window.open(newUrl, '_blank');
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-27 09:34:39 +00:00
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
checkDuplicatedName = (newName) => {
|
|
|
|
let node = this.state.opNode;
|
2020-11-02 05:56:35 +00:00
|
|
|
// root node to new node conditions: parentNode is null,
|
2019-02-20 03:54:25 +00:00
|
|
|
let parentNode = node.parentNode ? node.parentNode : node;
|
|
|
|
let childrenObject = parentNode.children.map(item => {
|
|
|
|
return item.object;
|
|
|
|
});
|
|
|
|
let isDuplicated = childrenObject.some(object => {
|
|
|
|
return object.name === newName;
|
|
|
|
});
|
|
|
|
return isDuplicated;
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-03-15 07:48:16 +00:00
|
|
|
showNodeImagePopup = (node) => {
|
|
|
|
let childrenNode = node.parentNode.children;
|
|
|
|
let items = childrenNode.filter((item) => {
|
|
|
|
return Utils.imageCheck(item.object.name);
|
|
|
|
});
|
|
|
|
let imageNames = items.map((item) => {
|
|
|
|
return item.object.name;
|
2019-04-09 09:24:44 +00:00
|
|
|
});
|
2019-03-15 07:48:16 +00:00
|
|
|
this.setState({
|
|
|
|
isNodeImagePopupOpen: true,
|
|
|
|
imageNodeItems: this.prepareImageItems(node),
|
|
|
|
imageIndex: imageNames.indexOf(node.object.name)
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-15 07:48:16 +00:00
|
|
|
|
|
|
|
prepareImageItems = (node) => {
|
|
|
|
let childrenNode = node.parentNode.children;
|
|
|
|
let items = childrenNode.filter((item) => {
|
|
|
|
return Utils.imageCheck(item.object.name);
|
|
|
|
});
|
|
|
|
|
|
|
|
const useThumbnail = !this.props.currentRepoInfo.encrypted;
|
|
|
|
let prepareItem = (item) => {
|
|
|
|
const name = item.object.name;
|
|
|
|
|
|
|
|
const path = Utils.encodePath(Utils.joinPath(node.parentNode.path, name));
|
|
|
|
const fileExt = name.substr(name.lastIndexOf('.') + 1).toLowerCase();
|
|
|
|
const isGIF = fileExt === 'gif';
|
|
|
|
|
|
|
|
const repoID = this.props.repoID;
|
|
|
|
let src = '';
|
|
|
|
if (useThumbnail && !isGIF) {
|
|
|
|
src = `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForOriginal}${path}`;
|
|
|
|
} else {
|
|
|
|
src = `${siteRoot}repo/${repoID}/raw${path}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
'name': name,
|
|
|
|
'url': `${siteRoot}lib/${repoID}/file${path}`,
|
|
|
|
'src': src
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
return items.map((item) => { return prepareItem(item); });
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-15 07:48:16 +00:00
|
|
|
|
|
|
|
closeNodeImagePopup = () => {
|
|
|
|
this.setState({
|
|
|
|
isNodeImagePopupOpen: false
|
|
|
|
});
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-15 07:48:16 +00:00
|
|
|
|
|
|
|
moveToPrevImage = () => {
|
|
|
|
const imageItemsLength = this.state.imageNodeItems.length;
|
|
|
|
this.setState((prevState) => ({
|
|
|
|
imageIndex: (prevState.imageIndex + imageItemsLength - 1) % imageItemsLength
|
|
|
|
}));
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-15 07:48:16 +00:00
|
|
|
|
|
|
|
moveToNextImage = () => {
|
|
|
|
const imageItemsLength = this.state.imageNodeItems.length;
|
|
|
|
this.setState((prevState) => ({
|
|
|
|
imageIndex: (prevState.imageIndex + 1) % imageItemsLength
|
|
|
|
}));
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-03-15 07:48:16 +00:00
|
|
|
|
2019-04-24 02:24:09 +00:00
|
|
|
stopTreeScrollPropagation = (e) => {
|
2019-04-24 02:10:28 +00:00
|
|
|
e.stopPropagation();
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-04-24 02:10:28 +00:00
|
|
|
|
2024-06-19 03:31:38 +00:00
|
|
|
renderContent = () => {
|
|
|
|
if (this.props.isTreeDataLoading) return (<Loading/>);
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<TreeSection title={gettext('Files')}>
|
|
|
|
<TreeView
|
|
|
|
userPerm={this.props.userPerm}
|
|
|
|
isNodeMenuShow={this.isNodeMenuShow}
|
|
|
|
treeData={this.props.treeData}
|
|
|
|
currentPath={this.props.currentPath}
|
|
|
|
onNodeClick={this.onNodeClick}
|
|
|
|
onNodeExpanded={this.props.onNodeExpanded}
|
|
|
|
onNodeCollapse={this.props.onNodeCollapse}
|
|
|
|
onMenuItemClick={this.onMenuItemClick}
|
|
|
|
onFreezedItem={this.onFreezedItem}
|
|
|
|
onUnFreezedItem={this.onUnFreezedItem}
|
|
|
|
onItemMove={this.props.onItemMove}
|
|
|
|
currentRepoInfo={this.props.currentRepoInfo}
|
|
|
|
selectedDirentList={this.props.selectedDirentList}
|
|
|
|
onItemsMove={this.props.onItemsMove}
|
|
|
|
repoID={this.props.repoID}
|
2024-07-17 02:45:53 +00:00
|
|
|
getMenuContainerSize={this.props.getMenuContainerSize}
|
2024-06-19 03:31:38 +00:00
|
|
|
/>
|
|
|
|
</TreeSection>
|
2024-07-09 01:45:50 +00:00
|
|
|
<DirViews
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
currentPath={this.props.currentPath}
|
|
|
|
userPerm={this.props.userPerm}
|
|
|
|
onNodeClick={this.onNodeClick}
|
|
|
|
/>
|
|
|
|
<DirOthers
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
userPerm={this.props.userPerm}
|
|
|
|
/>
|
2024-06-19 03:31:38 +00:00
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
render() {
|
2019-02-28 06:33:21 +00:00
|
|
|
let flex = this.props.navRate ? '0 0 ' + this.props.navRate * 100 + '%' : '0 0 25%';
|
|
|
|
const select = this.props.inResizing ? 'none' : '';
|
2019-02-20 03:54:25 +00:00
|
|
|
return (
|
|
|
|
<Fragment>
|
2019-04-24 02:24:09 +00:00
|
|
|
<div className="dir-content-nav" role="navigation" style={{flex: (flex), userSelect: select}} onScroll={this.stopTreeScrollPropagation}>
|
2024-06-19 03:31:38 +00:00
|
|
|
{this.renderContent()}
|
2019-02-20 03:54:25 +00:00
|
|
|
</div>
|
|
|
|
{this.state.isAddFolderDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<CreateFolder
|
|
|
|
parentPath={this.state.opNode.path}
|
|
|
|
onAddFolder={this.onAddFolderNode}
|
|
|
|
checkDuplicatedName={this.checkDuplicatedName}
|
|
|
|
addFolderCancel={this.onAddFolderToggle}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
|
|
|
{this.state.isAddFileDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<CreateFile
|
|
|
|
parentPath={this.state.opNode.path}
|
2023-07-22 07:54:25 +00:00
|
|
|
onAddFile={this.props.onAddFileNode}
|
2019-02-20 03:54:25 +00:00
|
|
|
checkDuplicatedName={this.checkDuplicatedName}
|
2023-07-22 07:54:25 +00:00
|
|
|
toggleDialog={this.onAddFileToggle}
|
2019-02-20 03:54:25 +00:00
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
|
|
|
{this.state.isRenameDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<Rename
|
|
|
|
currentNode={this.state.opNode}
|
|
|
|
onRename={this.onRenameNode}
|
|
|
|
checkDuplicatedName={this.checkDuplicatedName}
|
|
|
|
toggleCancel={this.onRenameToggle}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
2019-03-20 03:04:36 +00:00
|
|
|
{this.state.isCopyDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<Copy
|
|
|
|
path={this.state.opNode.parentNode.path}
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
dirent={this.state.opNode.object}
|
|
|
|
onItemCopy={this.props.onItemCopy}
|
|
|
|
repoEncrypted={this.props.currentRepoInfo.encrypted}
|
|
|
|
onCancelCopy={this.onCopyToggle}
|
|
|
|
isMutipleOperation={this.state.isMutipleOperation}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
|
|
|
{this.state.isMoveDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<Move
|
|
|
|
path={this.state.opNode.parentNode.path}
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
dirent={this.state.opNode.object}
|
|
|
|
onItemMove={this.props.onItemMove}
|
|
|
|
repoEncrypted={this.props.currentRepoInfo.encrypted}
|
|
|
|
onCancelMove={this.onMoveToggle}
|
|
|
|
isMutipleOperation={this.state.isMutipleOperation}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
2019-03-15 07:48:16 +00:00
|
|
|
{this.state.isNodeImagePopupOpen && (
|
2019-04-09 09:24:44 +00:00
|
|
|
<ModalPortal>
|
|
|
|
<ImageDialog
|
|
|
|
imageItems={this.state.imageNodeItems}
|
|
|
|
imageIndex={this.state.imageIndex}
|
|
|
|
closeImagePopup={this.closeNodeImagePopup}
|
|
|
|
moveToPrevImage={this.moveToPrevImage}
|
|
|
|
moveToNextImage={this.moveToNextImage}
|
2019-03-15 07:48:16 +00:00
|
|
|
/>
|
2019-04-09 09:24:44 +00:00
|
|
|
</ModalPortal>
|
2019-03-15 07:48:16 +00:00
|
|
|
)}
|
2019-02-20 03:54:25 +00:00
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-28 06:33:21 +00:00
|
|
|
DirColumnNav.defaultProps={
|
|
|
|
navRate: 0.25
|
|
|
|
};
|
|
|
|
|
2019-02-21 09:37:04 +00:00
|
|
|
DirColumnNav.propTypes = propTypes;
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-02-21 09:37:04 +00:00
|
|
|
export default DirColumnNav;
|