1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-02 23:48:47 +00:00

[dir view] redesigned the toolbar for the current folder (#6117)

* [dir view] redesigned the toolbar for the current folder

* [dir view] toolbar for the current folder: code cleanup

* [dir view] toolbar for the current folder: updated the 'upload' icon; modified UI details
This commit is contained in:
llj
2024-05-27 16:47:19 +08:00
committed by GitHub
parent d24d71420e
commit 69011567a3
15 changed files with 257 additions and 116 deletions

View File

@@ -5,6 +5,7 @@ import { UncontrolledTooltip } from 'reactstrap';
import { siteRoot, gettext } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import { InternalLinkOperation } from '../operations';
import DirOperationToolBar from '../../components/toolbar/dir-operation-toolbar';
const propTypes = {
repoName: PropTypes.string.isRequired,
@@ -50,7 +51,23 @@ class DirPath extends React.Component {
return (
<Fragment key={index}>
<span className="path-split">/</span>
<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>
);
} else {
@@ -58,7 +75,7 @@ class DirPath extends React.Component {
return (
<Fragment key={index} >
<span className="path-split">/</span>
<a className="path-link" data-path={nodePath} onClick={this.onPathClick}>{item}</a>
<a className="path-link path-item" data-path={nodePath} onClick={this.onPathClick}>{item}</a>
</Fragment>
);
}
@@ -78,31 +95,47 @@ class DirPath extends React.Component {
}
return (
<div className="path-container">
<div className="path-container dir-view-path">
<button className="op-btn mr-2" onClick={this.props.toggleTreePanel}><span className="sf3-font-side-bar sf3-font"></span></button>
{this.props.pathPrefix && this.props.pathPrefix.map((item, index) => {
return (
<Fragment key={index}>
<Link to={item.url} className="normal" onClick={(e) => this.onTabNavClick(e, item.name, item.id)}>{gettext(item.showName)}</Link>
<Link to={item.url} className="path-item normal" onClick={(e) => this.onTabNavClick(e, item.name, item.id)}>{gettext(item.showName)}</Link>
<span className="path-split">/</span>
</Fragment>
);
})}
{this.props.pathPrefix && this.props.pathPrefix.length === 0 && (
<Fragment>
<Link to={siteRoot + 'libraries/'} className="normal" onClick={(e) => this.onTabNavClick(e, 'libraries')}>{gettext('Files')}</Link>
<Link to={siteRoot + 'libraries/'} className="path-item normal" onClick={(e) => this.onTabNavClick(e, 'libraries')}>{gettext('Files')}</Link>
<span className="path-split">/</span>
</Fragment>
)}
{!this.props.pathPrefix && (
<Fragment>
<Link to={siteRoot + 'libraries/'} className="normal" onClick={(e) => this.onTabNavClick(e, 'libraries')}>{gettext('Files')}</Link>
<Link to={siteRoot + 'libraries/'} className="path-item normal" onClick={(e) => this.onTabNavClick(e, 'libraries')}>{gettext('Files')}</Link>
<span className="path-split">/</span>
</Fragment>
)}
{(currentPath === '/' || currentPath === '') ?
<span className="path-repo-name">{repoName}</span>:
<a className="path-link" data-path="/" onClick={this.onPathClick}>{repoName}</a>
<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-repo-name">{repoName}</span>
</DirOperationToolBar> :
<a className="path-item" data-path="/" onClick={this.onPathClick}>{repoName}</a>
}
{pathElem}
{this.props.isViewFile && (

View File

@@ -47,15 +47,25 @@ class CurDirPath extends React.Component {
return (
<Fragment>
<DirPath
repoID={this.props.repoID}
repoName={this.props.repoName}
repoEncrypted={this.props.repoEncrypted}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
pathPrefix={this.props.pathPrefix}
currentPath={this.props.currentPath}
userPerm={this.props.userPerm}
onPathClick={this.props.onPathClick}
onTabNavClick={this.props.onTabNavClick}
repoID={this.props.repoID}
isViewFile={this.props.isViewFile}
fileTags={this.props.fileTags}
toggleTreePanel={this.props.toggleTreePanel}
enableDirPrivateShare={this.props.enableDirPrivateShare}
showShareBtn={this.props.showShareBtn}
onAddFolder={this.props.onAddFolder}
onAddFile={this.props.onAddFile}
onUploadFile={this.props.onUploadFile}
onUploadFolder={this.props.onUploadFolder}
direntList={this.props.fullDirentList}
/>
{isDesktop &&
<DirTool

View File

@@ -22,8 +22,7 @@ const propTypes = {
onUploadFile: PropTypes.func.isRequired,
onUploadFolder: PropTypes.func.isRequired,
direntList: PropTypes.array.isRequired,
currentMode: PropTypes.string.isRequired,
switchViewMode: PropTypes.func.isRequired,
children: PropTypes.object
};
class DirOperationToolbar extends React.Component {
@@ -34,68 +33,29 @@ class DirOperationToolbar extends React.Component {
fileType: '.md',
isCreateFileDialogShow: false,
isCreateFolderDialogShow: false,
isUploadMenuShow: false,
isCreateMenuShow: false,
isShareDialogShow: false,
operationMenuStyle: '',
isDesktopMenuOpen: false,
isMobileOpMenuOpen: false
};
}
componentDidMount() {
document.addEventListener('click', this.hideOperationMenu);
}
componentWillUnmount() {
document.removeEventListener('click', this.hideOperationMenu);
}
toggleDesktopOpMenu = () => {
this.setState({isDesktopMenuOpen: !this.state.isDesktopMenuOpen});
};
toggleMobileOpMenu = () => {
this.setState({isMobileOpMenuOpen: !this.state.isMobileOpMenuOpen});
};
hideOperationMenu = () => {
this.setState({
isUploadMenuShow: false,
isCreateMenuShow: false,
});
};
toggleOperationMenu = (e) => {
e.nativeEvent.stopImmediatePropagation();
let targetRect = e.target.getBoundingClientRect();
let left = targetRect.left;
let top = targetRect.bottom;
let style = {position: 'fixed', display: 'block', left: left, top: top};
this.setState({operationMenuStyle: style});
};
onUploadClick = (e) => {
this.toggleOperationMenu(e);
this.setState({
isUploadMenuShow: !this.state.isUploadMenuShow,
isCreateMenuShow: false,
});
};
onUploadFile = (e) => {
this.setState({isUploadMenuShow: false});
this.props.onUploadFile(e);
};
onUploadFolder = (e) => {
this.setState({isUploadMenuShow: false});
this.props.onUploadFolder(e);
};
onCreateClick = (e) => {
this.toggleOperationMenu(e);
this.setState({
isCreateMenuShow: !this.state.isCreateMenuShow,
isUploadMenuShow: false,
});
};
onShareClick = () => {
this.setState({
isShareDialogShow: !this.state.isShareDialogShow
@@ -161,6 +121,41 @@ class DirOperationToolbar extends React.Component {
return isDuplicated;
};
onDropdownToggleKeyDown = (e) => {
if (e.key == 'Enter' || e.key == 'Space') {
this.toggleDesktopOpMenu();
}
};
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.toggleDesktopOpMenu();
});
};
toggleSubMenuShown = (item) => {
this.setState({
isSubMenuShown: true,
currentItem: item.text
});
};
onMenuItemKeyDown = (item, e) => {
if (e.key == 'Enter' || e.key == 'Space') {
item.onClick();
}
};
render() {
let { path, repoName, userPerm } = this.props;
@@ -179,42 +174,108 @@ class DirOperationToolbar extends React.Component {
let content = null;
if (Utils.isDesktop()) {
const { showShareBtn, repoEncrypted } = this.props;
let opList = [];
if (canUpload) {
if (Utils.isSupportUploadFolder()) {
opList.push({
'icon': 'upload-files',
'text': gettext('Upload'),
subOpList: [
{'text': gettext('Upload Files'), 'onClick': this.onUploadFile},
{'text': gettext('Upload Folder'), 'onClick': this.onUploadFolder}
]
});
} else {
opList.push({'text': gettext('Upload'), 'onClick': this.onUploadFile});
}
}
if (canCreate) {
let newSubOpList = [
{'text': gettext('New Folder'), 'onClick': this.onCreateFolderToggle},
{'text': gettext('New File'), 'onClick': this.onCreateFileToggle},
'Divider',
{'text': gettext('New Markdown File'), 'onClick': this.onCreateMarkdownToggle},
{'text': gettext('New Excel File'), 'onClick': this.onCreateExcelToggle},
{'text': gettext('New PowerPoint File'), 'onClick': this.onCreatePPTToggle},
{'text': gettext('New Word File'), 'onClick': this.onCreateWordToggle}
];
if (enableSeadoc && !repoEncrypted) {
newSubOpList.push({'text': gettext('New SeaDoc File'), 'onClick': this.onCreateSeaDocToggle});
}
opList.push({
'icon': 'new',
'text': gettext('New'),
subOpList: newSubOpList
});
}
if (showShareBtn) {
opList.push({
'icon': 'share',
'text': gettext('Share'),
'onClick': this.onShareClick
});
}
content = (
<Fragment>
{canUpload && (
<Fragment>
{Utils.isSupportUploadFolder() ?
<Fragment>
<button className="btn btn-secondary operation-item" onClick={this.onUploadClick} aria-haspopup="true" aria-expanded={this.state.isUploadMenuShow} aria-controls="upload-menu">{gettext('Upload')}</button>
{this.state.isUploadMenuShow && (
<div className="menu dropdown-menu" style={this.state.operationMenuStyle} role="menu" id="upload-menu">
<button type="button" className="dropdown-item" onClick={this.onUploadFile} role="menuitem">{gettext('Upload Files')}</button>
<button type="button" className="dropdown-item" onClick={this.onUploadFolder} role="menuitem">{gettext('Upload Folder')}</button>
</div>
)}
</Fragment>
:
<button className="btn btn-secondary operation-item" title={gettext('Upload')} onClick={this.onUploadFile}>{gettext('Upload')}</button>}
</Fragment>
)}
{canCreate &&
<Fragment>
<button className="btn btn-secondary operation-item" onClick={this.onCreateClick} aria-haspopup="true" aria-expanded={this.state.isUploadMenuShow} aria-controls="new-menu">{gettext('New')}</button>
{this.state.isCreateMenuShow && (
<div className="menu dropdown-menu" style={this.state.operationMenuStyle} role="menu" id="new-menu">
<button className="dropdown-item" onClick={this.onCreateFolderToggle} role="menuitem">{gettext('New Folder')}</button>
<button className="dropdown-item" onClick={this.onCreateFileToggle}>{gettext('New File')}</button>
<div className="dropdown-divider"></div>
<button className="dropdown-item" onClick={this.onCreateMarkdownToggle} role="menuitem">{gettext('New Markdown File')}</button>
<button className="dropdown-item" onClick={this.onCreateExcelToggle} role="menuitem">{gettext('New Excel File')}</button>
<button className="dropdown-item" onClick={this.onCreatePPTToggle} role="menuitem">{gettext('New PowerPoint File')}</button>
<button className="dropdown-item" onClick={this.onCreateWordToggle} role="menuitem">{gettext('New Word File')}</button>
{enableSeadoc && !repoEncrypted && <button className="dropdown-item" onClick={this.onCreateSeaDocToggle} role="menuitem">{gettext('New SeaDoc File')} (beta)</button>}
</div>
)}
</Fragment>
<Dropdown isOpen={this.state.isDesktopMenuOpen} toggle={this.toggleDesktopOpMenu}>
<DropdownToggle
tag="div"
role="button"
className="path-item"
title={gettext('More operations')}
aria-label={gettext('More operations')}
onClick={this.toggleDesktopOpMenu}
onKeyDown={this.onDropdownToggleKeyDown}
data-toggle="dropdown"
>
{this.props.children}
<i className="sf3-font-drop-down sf3-font ml-1 path-item-dropdown-toggle"></i>
</DropdownToggle>
<DropdownMenu onMouseMove={this.onDropDownMouseMove} style={{'width': '200px'}}>
{opList.map((item, index)=> {
if (item == 'Divider') {
return <DropdownItem key={index} divider />;
} else if (item.subOpList) {
return (
<Dropdown
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>);
}
{showShareBtn && <button className="btn btn-secondary operation-item" title={gettext('Share')} onClick={this.onShareClick}>{gettext('Share')}</button>}
})}
</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>
</Fragment>
);
} else {

View File

@@ -237,3 +237,25 @@
.op-btn:hover {
background: #f5f5f5;
}
.dir-view-path .path-item {
min-width: 0; /* overwrite some styles */
padding: 0 6px;
font-size: 1rem;
color: inherit;
border-radius: 3px;
text-decoration: none;
}
.dir-view-path .path-item:hover {
background: #efefef;
}
.dir-view-path .path-item-dropdown-toggle {
color: #999;
font-size: .6rem;
}
.dir-view-path .path-split {
padding: 0 2px;
}

View File

@@ -209,6 +209,8 @@ class LibContentContainer extends React.Component {
<CurDirPath
repoID={repoID}
repoName={this.props.currentRepoInfo.repo_name}
repoEncrypted={this.props.repoEncrypted}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
pathPrefix={this.props.pathPrefix}
currentPath={this.props.path}
userPerm={this.props.userPerm}
@@ -226,6 +228,13 @@ class LibContentContainer extends React.Component {
currentMode={this.props.currentMode}
switchViewMode={this.props.switchViewMode}
isCustomPermission={this.props.isCustomPermission}
enableDirPrivateShare={this.props.enableDirPrivateShare}
showShareBtn={this.props.showShareBtn}
onAddFolder={this.props.onAddFolder}
onAddFile={this.props.onAddFile}
onUploadFile={this.props.onUploadFile}
onUploadFolder={this.props.onUploadFolder}
fullDirentList={this.props.fullDirentList}
/>
}
</div>

View File

@@ -2,7 +2,6 @@ import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import CommonToolbar from '../../components/toolbar/common-toolbar';
import DirOperationToolBar from '../../components/toolbar/dir-operation-toolbar';
import ViewFileToolbar from '../../components/toolbar/view-file-toolbar';
const propTypes = {
@@ -86,25 +85,6 @@ class LibContentToolbar extends React.Component {
<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>
{!this.props.isDirentSelected &&
<DirOperationToolBar
path={this.props.path}
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}
currentMode={this.props.currentMode}
switchViewMode={this.props.switchViewMode}
/>
}
</div>
<CommonToolbar
isLibView={true}

View File

@@ -2138,6 +2138,9 @@ class LibContentView extends React.Component {
updateDetail={this.state.updateDetail}
onListContainerScroll={this.onListContainerScroll}
loadDirentList={this.loadDirentList}
showShareBtn={showShareBtn}
onUploadFile={this.onUploadFile}
onUploadFolder={this.onUploadFolder}
/>
{canUpload && this.state.pathExist && !this.state.isViewFile && (
<FileUploader

View File

@@ -1028,6 +1028,23 @@ a.table-sort-op:hover {
background-color: #20a0ff;
}
.dropdown-item-icon,
.btn .dropdown-item-icon {
color: #444;
font-size: 1rem;
}
.show>.btn-secondary.dropdown-toggle.dropdown-item .dropdown-item-icon {
color: #fff;
}
.dropdown-item:hover .dropdown-item-icon,
.dropdown-item:hover .btn .dropdown-item-icon,
.dropdown-item:focus .dropdown-item-icon,
.dropdown-item:focus .btn .dropdown-item-icon {
color: #fff;
}
/* empty-tip */
.empty-tip {
margin: 5.5em 1em;

View File

@@ -1,11 +1,11 @@
@font-face {
font-family: "sf3-font"; /* Project id 1230969 */
src: url('iconfont.eot?t=1716614768424'); /* IE9 */
src: url('iconfont.eot?t=1716614768424#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('iconfont.woff2?t=1716614768424') format('woff2'),
url('iconfont.woff?t=1716614768424') format('woff'),
url('iconfont.ttf?t=1716614768424') format('truetype'),
url('iconfont.svg?t=1716614768424#sf3-font') format('svg');
src: url('iconfont.eot?t=1716779999367'); /* IE9 */
src: url('iconfont.eot?t=1716779999367#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('iconfont.woff2?t=1716779999367') format('woff2'),
url('iconfont.woff?t=1716779999367') format('woff'),
url('iconfont.ttf?t=1716779999367') format('truetype'),
url('iconfont.svg?t=1716779999367#sf3-font') format('svg');
}
.sf3-font {
@@ -16,6 +16,10 @@
-moz-osx-font-smoothing: grayscale;
}
.sf3-font-upload-files:before {
content: "\e821";
}
.sf3-font-share:before {
content: "\e820";
}

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -14,6 +14,8 @@
/>
<missing-glyph />
<glyph glyph-name="upload-files" unicode="&#59425;" d="M160 160v-160h704v160h96v-192c0-35.2-25.6-60.8-57.6-64H128c-35.2 0-64 28.8-64 64v192h96zM518.4 864l278.4-268.8-80-76.8-156.8 150.4v-585.6h-96V656L320 518.4 240 595.2 518.4 864z" horiz-adv-x="1024" />
<glyph glyph-name="share" unicode="&#59424;" d="M800 832c-70.4 0-128-54.4-128-128s57.6-128 128-128 128 54.4 128 128-57.6 128-128 128zM224 512c-70.4 0-128-54.4-128-128s57.6-128 128-128 128 54.4 128 128-57.6 128-128 128z m576-320c-70.4 0-128-54.4-128-128s57.6-128 128-128 128 54.4 128 128-60.8 128-128 128z m-425.6 307.2l217.6 134.4 48-83.2-217.6-134.4-48 83.2z m60.8-169.6l220.8-131.2-48-80-220.8 131.2 48 80z" horiz-adv-x="1024" />
<glyph glyph-name="rename" unicode="&#58972;" d="M796.8 873.6L928 742.4c32-32 32-83.2 0-112L336 32l-265.6-19.2 16 262.4L681.6 873.6c28.8 28.8 86.4 28.8 115.2 0z m-204.8-227.2L182.4 233.6l-6.4-115.2 118.4 9.6L704 537.6l-112 108.8z m147.2 147.2l-80-80L768 604.8l80 80-108.8 108.8zM976-128h-928C22.4-128 0-105.6 0-80S22.4-32 48-32h928c25.6 0 48-22.4 48-48s-22.4-48-48-48z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.