1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-01 23:20:51 +00:00

Dir view file view toolbar redesign (#6122)

* [dir view] redesigned the toolbar for 'view a markdown file'

* [dir view] markdown file view: don't use secondary dropdown menu for the toolbar
This commit is contained in:
llj
2024-05-28 18:17:53 +08:00
committed by GitHub
parent c604bb2a94
commit af3522d312
6 changed files with 229 additions and 116 deletions

View File

@@ -6,17 +6,31 @@ import { siteRoot, gettext } from '../../utils/constants';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import { InternalLinkOperation } from '../operations'; import { InternalLinkOperation } from '../operations';
import DirOperationToolBar from '../../components/toolbar/dir-operation-toolbar'; import DirOperationToolBar from '../../components/toolbar/dir-operation-toolbar';
import ViewFileToolbar from '../../components/toolbar/view-file-toolbar';
const propTypes = { const propTypes = {
repoID: PropTypes.string.isRequired,
repoName: PropTypes.string.isRequired, repoName: PropTypes.string.isRequired,
currentPath: PropTypes.string.isRequired, currentPath: PropTypes.string.isRequired,
onPathClick: PropTypes.func.isRequired, onPathClick: PropTypes.func.isRequired,
onTabNavClick: PropTypes.func, onTabNavClick: PropTypes.func,
pathPrefix: PropTypes.array, pathPrefix: PropTypes.array,
repoID: PropTypes.string.isRequired,
isViewFile: PropTypes.bool, isViewFile: PropTypes.bool,
fileTags: PropTypes.array.isRequired, fileTags: PropTypes.array.isRequired,
toggleTreePanel: PropTypes.func.isRequired toggleTreePanel: PropTypes.func.isRequired,
repoEncrypted: PropTypes.bool.isRequired,
enableDirPrivateShare: PropTypes.bool.isRequired,
userPerm: PropTypes.string.isRequired,
isGroupOwnedRepo: PropTypes.bool.isRequired,
showShareBtn: PropTypes.bool.isRequired,
onAddFile: PropTypes.func.isRequired,
onAddFolder: PropTypes.func.isRequired,
onUploadFile: PropTypes.func.isRequired,
onUploadFolder: PropTypes.func.isRequired,
direntList: PropTypes.array.isRequired,
repoTags: PropTypes.array.isRequired,
filePermission: PropTypes.string,
onFileTagChanged: PropTypes.func.isRequired,
}; };
class DirPath extends React.Component { class DirPath extends React.Component {
@@ -51,23 +65,41 @@ class DirPath extends React.Component {
return ( return (
<Fragment key={index}> <Fragment key={index}>
<span className="path-split">/</span> <span className="path-split">/</span>
<DirOperationToolBar {this.props.isViewFile ?
path={this.props.currentPath} <ViewFileToolbar
repoID={this.props.repoID} path={this.props.currentPath}
repoName={this.props.repoName} repoID={this.props.repoID}
repoEncrypted={this.props.repoEncrypted} userPerm={this.props.userPerm}
direntList={this.props.direntList} repoEncrypted={this.props.repoEncrypted}
showShareBtn={this.props.showShareBtn} enableDirPrivateShare={this.props.enableDirPrivateShare}
enableDirPrivateShare={this.props.enableDirPrivateShare} isGroupOwnedRepo={this.props.isGroupOwnedRepo}
userPerm={this.props.userPerm} filePermission={this.props.filePermission}
isGroupOwnedRepo={this.props.isGroupOwnedRepo} fileTags={this.props.fileTags}
onAddFile={this.props.onAddFile} onFileTagChanged={this.props.onFileTagChanged}
onAddFolder={this.props.onAddFolder} showShareBtn={this.props.showShareBtn}
onUploadFile={this.props.onUploadFile} repoTags={this.props.repoTags}
onUploadFolder={this.props.onUploadFolder} >
> <span className="path-file-name">{item}</span>
<span className="path-file-name">{item}</span> </ViewFileToolbar>
</DirOperationToolBar> :
<DirOperationToolBar
path={this.props.currentPath}
repoID={this.props.repoID}
repoName={this.props.repoName}
repoEncrypted={this.props.repoEncrypted}
direntList={this.props.direntList}
showShareBtn={this.props.showShareBtn}
enableDirPrivateShare={this.props.enableDirPrivateShare}
userPerm={this.props.userPerm}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
onAddFile={this.props.onAddFile}
onAddFolder={this.props.onAddFolder}
onUploadFile={this.props.onUploadFile}
onUploadFolder={this.props.onUploadFolder}
>
<span className="path-file-name">{item}</span>
</DirOperationToolBar>
}
</Fragment> </Fragment>
); );
} else { } else {

View File

@@ -25,6 +25,18 @@ const propTypes = {
currentMode: PropTypes.string.isRequired, currentMode: PropTypes.string.isRequired,
switchViewMode: PropTypes.func.isRequired, switchViewMode: PropTypes.func.isRequired,
isCustomPermission: PropTypes.bool, isCustomPermission: PropTypes.bool,
repoEncrypted: PropTypes.bool.isRequired,
isGroupOwnedRepo: PropTypes.bool.isRequired,
enableDirPrivateShare: PropTypes.bool.isRequired,
showShareBtn: PropTypes.bool.isRequired,
onAddFile: PropTypes.func.isRequired,
onAddFolder: PropTypes.func.isRequired,
onUploadFile: PropTypes.func.isRequired,
onUploadFolder: PropTypes.func.isRequired,
fullDirentList: PropTypes.array.isRequired,
filePermission: PropTypes.string,
repoTags: PropTypes.array.isRequired,
onFileTagChanged: PropTypes.func.isRequired,
}; };
class CurDirPath extends React.Component { class CurDirPath extends React.Component {
@@ -66,6 +78,9 @@ class CurDirPath extends React.Component {
onUploadFile={this.props.onUploadFile} onUploadFile={this.props.onUploadFile}
onUploadFolder={this.props.onUploadFolder} onUploadFolder={this.props.onUploadFolder}
direntList={this.props.fullDirentList} direntList={this.props.fullDirentList}
filePermission={this.props.filePermission}
onFileTagChanged={this.props.onFileTagChanged}
repoTags={this.props.repoTags}
/> />
{isDesktop && {isDesktop &&
<DirTool <DirTool

View File

@@ -138,7 +138,8 @@ class DirOperationToolbar extends React.Component {
toggleSubMenu = (e) => { toggleSubMenu = (e) => {
e.stopPropagation(); e.stopPropagation();
this.setState({ this.setState({
isSubMenuShown: !this.state.isSubMenuShown}, () => { isSubMenuShown: !this.state.isSubMenuShown
}, () => {
this.toggleDesktopOpMenu(); this.toggleDesktopOpMenu();
}); });
}; };
@@ -186,7 +187,11 @@ class DirOperationToolbar extends React.Component {
] ]
}); });
} else { } else {
opList.push({'text': gettext('Upload'), 'onClick': this.onUploadFile}); opList.push({
'icon': 'upload-files',
'text': gettext('Upload'),
'onClick': this.onUploadFile
});
} }
} }
@@ -206,7 +211,7 @@ class DirOperationToolbar extends React.Component {
opList.push({ opList.push({
'icon': 'new', 'icon': 'new',
'text': gettext('New'), 'text': gettext('New'),
subOpList: newSubOpList 'subOpList': newSubOpList
}); });
} }
@@ -266,10 +271,12 @@ class DirOperationToolbar extends React.Component {
</Dropdown> </Dropdown>
); );
} else { } else {
return (<DropdownItem key={index} onClick={item.onClick} onKeyDown={this.onMenuItemKeyDown.bind(this, item)}> return (
<i className={`sf3-font-${item.icon} sf3-font mr-2 dropdown-item-icon`}></i> <DropdownItem key={index} onClick={item.onClick} onKeyDown={this.onMenuItemKeyDown.bind(this, item)}>
{item.text} <i className={`sf3-font-${item.icon} sf3-font mr-2 dropdown-item-icon`}></i>
</DropdownItem>); {item.text}
</DropdownItem>
);
} }
})} })}
</DropdownMenu> </DropdownMenu>

View File

@@ -20,6 +20,7 @@ const propTypes = {
onFileTagChanged: PropTypes.func.isRequired, onFileTagChanged: PropTypes.func.isRequired,
showShareBtn: PropTypes.bool.isRequired, showShareBtn: PropTypes.bool.isRequired,
dirent: PropTypes.object, dirent: PropTypes.object,
children: PropTypes.object
}; };
class ViewFileToolbar extends React.Component { class ViewFileToolbar extends React.Component {
@@ -27,12 +28,52 @@ class ViewFileToolbar extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
isDropdownMenuOpen: false,
isMoreMenuShow: false, isMoreMenuShow: false,
isShareDialogShow: false, isShareDialogShow: false,
isEditTagDialogShow: false, isEditTagDialogShow: false,
}; };
} }
toggleDropdownMenu = () => {
this.setState({isDropdownMenuOpen: !this.state.isDropdownMenuOpen});
};
onDropdownToggleKeyDown = (e) => {
if (e.key == 'Enter' || e.key == 'Space') {
this.toggleDropdownMenu();
}
};
onDropDownMouseMove = (e) => {
if (this.state.isSubMenuShown && e.target && e.target.className === 'dropdown-item') {
this.setState({
isSubMenuShown: false
});
}
};
toggleSubMenu = (e) => {
e.stopPropagation();
this.setState({
isSubMenuShown: !this.state.isSubMenuShown}, () => {
this.toggleDropdownMenu();
});
};
toggleSubMenuShown = (item) => {
this.setState({
isSubMenuShown: true,
currentItem: item.text
});
};
onMenuItemKeyDown = (item, e) => {
if (e.key == 'Enter' || e.key == 'Space') {
item.onClick();
}
};
onEditClick = (e) => { onEditClick = (e) => {
e.preventDefault(); e.preventDefault();
let { path, repoID } = this.props; let { path, repoID } = this.props;
@@ -58,30 +99,110 @@ class ViewFileToolbar extends React.Component {
}; };
render() { render() {
let { filePermission } = this.props; const { filePermission, showShareBtn } = this.props;
let opList = [];
if (filePermission === 'rw' || filePermission === 'cloud-edit') {
opList.push({
'icon': 'rename',
'text': gettext('Edit'),
'onClick': this.onEditClick
});
}
if (filePermission === 'rw') {
/*
let newSubOpList = [];
if (showShareBtn) {
newSubOpList.push({
'text': gettext('Share'),
'onClick': this.onShareToggle
});
}
newSubOpList.push(
{'text': gettext('Tags'), 'onClick': this.onEditFileTagToggle},
{'text': gettext('History'), 'onClick': this.onHistoryClick}
);
opList.push({
'icon': 'more-vertical',
'text': gettext('More'),
'subOpList': newSubOpList
});
*/
if (showShareBtn) {
opList.push({
'icon': 'share',
'text': gettext('Share'),
'onClick': this.onShareToggle
});
}
opList.push(
{'icon': 'tag', 'text': gettext('Tags'), 'onClick': this.onEditFileTagToggle},
{'icon': 'history', 'text': gettext('History'), 'onClick': this.onHistoryClick}
);
}
return ( return (
<Fragment> <Fragment>
<div className="dir-operation"> {opList.length > 0 &&
{(filePermission === 'rw' || filePermission === 'cloud-edit') && ( <Dropdown isOpen={this.state.isDropdownMenuOpen} toggle={this.toggleDropdownMenu}>
<Fragment> <DropdownToggle
<button className="btn btn-secondary operation-item" title={gettext('Edit File')} onClick={this.onEditClick}>{gettext('Edit')}</button> tag="div"
</Fragment> role="button"
)} className="path-item"
{filePermission === 'rw' && ( onClick={this.toggleDropdownMenu}
<Dropdown isOpen={this.state.isMoreMenuShow} toggle={this.toggleMore}> onKeyDown={this.onDropdownToggleKeyDown}
<DropdownToggle className='btn btn-secondary operation-item'> data-toggle="dropdown"
{gettext('More')} >
</DropdownToggle> {this.props.children}
<DropdownMenu> <i className="sf3-font-drop-down sf3-font ml-1 path-item-dropdown-toggle"></i>
{this.props.showShareBtn && </DropdownToggle>
<DropdownItem onClick={this.onShareToggle}>{gettext('Share')}</DropdownItem> <DropdownMenu onMouseMove={this.onDropDownMouseMove} style={{'width': '200px'}}>
} {opList.map((item, index)=> {
<DropdownItem onClick={this.onEditFileTagToggle}>{gettext('Tags')}</DropdownItem> if (item == 'Divider') {
<DropdownItem onClick={this.onHistoryClick}>{gettext('History')}</DropdownItem> return <DropdownItem key={index} divider />;
</DropdownMenu> } else if (item.subOpList) {
</Dropdown> return (
)} <Dropdown
</div> key={index}
direction="right"
className="w-100"
isOpen={this.state.isSubMenuShown && this.state.currentItem == item.text}
toggle={this.toggleSubMenu}
onMouseMove={(e) => {e.stopPropagation();}}
>
<DropdownToggle
caret
className="dropdown-item font-weight-normal rounded-0 d-flex align-items-center pr-2"
onMouseEnter={this.toggleSubMenuShown.bind(this, item)}
>
<i className={`sf3-font-${item.icon} sf3-font mr-2 dropdown-item-icon`}></i>
<span className="mr-auto">{item.text}</span>
</DropdownToggle>
<DropdownMenu>
{item.subOpList.map((item, index)=> {
if (item == 'Divider') {
return <DropdownItem key={index} divider />;
} else {
return (<DropdownItem key={index} onClick={item.onClick} onKeyDown={this.onMenuItemKeyDown.bind(this, item)}>{item.text}</DropdownItem>);
}
})}
</DropdownMenu>
</Dropdown>
);
} else {
return (
<DropdownItem key={index} onClick={item.onClick} onKeyDown={this.onMenuItemKeyDown.bind(this, item)}>
<i className={`sf3-font-${item.icon} sf3-font mr-2 dropdown-item-icon`}></i>
{item.text}
</DropdownItem>
);
}
})}
</DropdownMenu>
</Dropdown>
}
{this.state.isShareDialogShow && ( {this.state.isShareDialogShow && (
<ModalPotal> <ModalPotal>
<ShareDialog <ShareDialog

View File

@@ -91,10 +91,12 @@ const propTypes = {
direntDetailPanelTab: PropTypes.string, direntDetailPanelTab: PropTypes.string,
loadDirentList: PropTypes.func, loadDirentList: PropTypes.func,
fullDirentList: PropTypes.array, fullDirentList: PropTypes.array,
unSelectDirent: PropTypes.func, unSelectDirent: PropTypes.func,
onFilesTagChanged: PropTypes.func.isRequired, onFilesTagChanged: PropTypes.func.isRequired,
showShareBtn: PropTypes.bool.isRequired, showShareBtn: PropTypes.bool.isRequired,
onUploadFile: PropTypes.func.isRequired,
onUploadFolder: PropTypes.func.isRequired,
onToolbarFileTagChanged: PropTypes.func.isRequired
}; };
class LibContentContainer extends React.Component { class LibContentContainer extends React.Component {
@@ -235,6 +237,9 @@ class LibContentContainer extends React.Component {
onUploadFile={this.props.onUploadFile} onUploadFile={this.props.onUploadFile}
onUploadFolder={this.props.onUploadFolder} onUploadFolder={this.props.onUploadFolder}
fullDirentList={this.props.fullDirentList} fullDirentList={this.props.fullDirentList}
filePermission={this.props.filePermission}
onFileTagChanged={this.props.onToolbarFileTagChanged}
repoTags={this.props.repoTags}
/> />
} }
</div> </div>

View File

@@ -2,85 +2,18 @@ import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants'; import { gettext } from '../../utils/constants';
import CommonToolbar from '../../components/toolbar/common-toolbar'; import CommonToolbar from '../../components/toolbar/common-toolbar';
import ViewFileToolbar from '../../components/toolbar/view-file-toolbar';
const propTypes = { const propTypes = {
isViewFile: PropTypes.bool.isRequired,
filePermission: PropTypes.string,
fileTags: PropTypes.array.isRequired,
onFileTagChanged: PropTypes.func.isRequired, // for file-view-toolbar
// side-panel
onSideNavMenuClick: PropTypes.func.isRequired, onSideNavMenuClick: PropTypes.func.isRequired,
// mutiple-dir
isDirentSelected: PropTypes.bool.isRequired,
repoID: PropTypes.string.isRequired, repoID: PropTypes.string.isRequired,
repoTags: PropTypes.array.isRequired,
path: PropTypes.string.isRequired,
selectedDirentList: PropTypes.array.isRequired,
onItemsMove: PropTypes.func.isRequired,
onItemsCopy: PropTypes.func.isRequired,
onItemsDelete: PropTypes.func.isRequired,
// dir
direntList: PropTypes.array.isRequired,
repoName: PropTypes.string.isRequired, repoName: PropTypes.string.isRequired,
repoEncrypted: PropTypes.bool.isRequired,
isGroupOwnedRepo: PropTypes.bool.isRequired,
userPerm: PropTypes.string.isRequired,
showShareBtn: PropTypes.bool.isRequired,
enableDirPrivateShare: PropTypes.bool.isRequired,
onAddFile: PropTypes.func.isRequired,
onAddFolder: PropTypes.func.isRequired,
onUploadFile: PropTypes.func.isRequired,
onUploadFolder: PropTypes.func.isRequired,
// view-mode
currentMode: PropTypes.string.isRequired,
switchViewMode: PropTypes.func.isRequired,
// search
onSearchedClick: PropTypes.func.isRequired, onSearchedClick: PropTypes.func.isRequired,
isRepoOwner: PropTypes.bool.isRequired,
// selected menu
onFilesTagChanged: PropTypes.func.isRequired, // for mutiple select toolbar
updateDirent: PropTypes.func.isRequired,
unSelectDirent: PropTypes.func,
currentRepoInfo: PropTypes.object, currentRepoInfo: PropTypes.object,
onItemRename: PropTypes.func,
showDirentDetail: PropTypes.func,
}; };
class LibContentToolbar extends React.Component { class LibContentToolbar extends React.Component {
render() { render() {
if (this.props.isViewFile) {
return (
<Fragment>
<div className="cur-view-toolbar">
<span className="sf2-icon-menu hidden-md-up d-md-none side-nav-toggle" title={gettext('Side Nav Menu')} onClick={this.props.onSideNavMenuClick}></span>
<ViewFileToolbar
path={this.props.path}
repoID={this.props.repoID}
userPerm={this.props.userPerm}
repoEncrypted={this.props.repoEncrypted}
enableDirPrivateShare={this.props.enableDirPrivateShare}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
filePermission={this.props.filePermission}
fileTags={this.props.fileTags}
onFileTagChanged={this.props.onFileTagChanged}
showShareBtn={this.props.showShareBtn}
repoTags={this.props.repoTags}
/>
</div>
<CommonToolbar
isLibView={true}
repoID={this.props.repoID}
repoName={this.props.repoName}
onSearchedClick={this.props.onSearchedClick}
searchPlaceholder={gettext('Search files')}
/>
</Fragment>
);
}
return ( return (
<Fragment> <Fragment>
<div className="cur-view-toolbar"> <div className="cur-view-toolbar">