mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-20 02:48:51 +00:00
Side nav (#6309)
* [user panel] combined 7 commits into 1: [user side nav] added 'fold/unfold' for the sidebar [redesign] redesigned toolbar for 'my libs' & 'shared with all'; redesigned 'top bar' for 'help', 'clients' and other pages [Shared with all] 'share existing libraries' dialog: added the 'close' icon back, enabled clicking outside to close the dialog ['Invite Guest' page] redesigned the toolbar ['Share Admin' - 'Links'] 'Share Links' page: redesigned the toolbar ['Share Admin' - 'Links'] 'Upload Links' page: redesigned the toolbar cleaned up code * [user side panel] update * [user panel] update * [code style] update: remove an eslint warning
This commit is contained in:
@@ -108,10 +108,10 @@ class ShareRepoDialog extends React.Component {
|
||||
}
|
||||
|
||||
this.props.onRepoSelectedHandler(this.state.selectedRepoList);
|
||||
this.onCloseDialog();
|
||||
this.toggle();
|
||||
};
|
||||
|
||||
onCloseDialog = () => {
|
||||
toggle = () => {
|
||||
this.props.onShareRepoDialogClose();
|
||||
};
|
||||
|
||||
@@ -146,7 +146,7 @@ class ShareRepoDialog extends React.Component {
|
||||
</ModalBody>
|
||||
{this.state.errMessage && <Alert color="danger" className="mt-2">{this.state.errMessage}</Alert>}
|
||||
<ModalFooter>
|
||||
<Button color="secondary" onClick={this.onCloseDialog}>{gettext('Close')}</Button>
|
||||
<Button color="secondary" onClick={this.toggle}>{gettext('Close')}</Button>
|
||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
|
42
frontend/src/components/header.js
Normal file
42
frontend/src/components/header.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Logo from './logo';
|
||||
import CommonToolbar from './toolbar/common-toolbar';
|
||||
|
||||
const propTypes = {
|
||||
isSidePanelClosed: PropTypes.bool,
|
||||
onCloseSidePanel: PropTypes.func,
|
||||
onShowSidePanel: PropTypes.func,
|
||||
onSearchedClick: PropTypes.func,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
showSearch: PropTypes.bool
|
||||
};
|
||||
|
||||
class Header extends React.Component {
|
||||
|
||||
render() {
|
||||
const { onShowSidePanel, onSearchedClick, showSearch } = this.props;
|
||||
return (
|
||||
<div id="header" className="d-flex justify-content-between py-2 px-4">
|
||||
<div className={'flex-shrink-0 d-none d-md-flex'}>
|
||||
<Logo onCloseSidePanel={this.props.onCloseSidePanel} />
|
||||
</div>
|
||||
<div className="flex-shrink-0 d-flex flex-fill">
|
||||
<div className="cur-view-toolbar">
|
||||
<span title="Side Nav Menu" onClick={onShowSidePanel} className="sf2-icon-menu side-nav-toggle hidden-md-up d-md-none">
|
||||
</span>
|
||||
</div>
|
||||
<CommonToolbar
|
||||
showSearch={showSearch}
|
||||
searchPlaceholder={this.props.searchPlaceholder}
|
||||
onSearchedClick={onSearchedClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Header.propTypes = propTypes;
|
||||
|
||||
export default Header;
|
@@ -5,6 +5,7 @@ import { siteRoot, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle } from '
|
||||
const propTypes = {
|
||||
onCloseSidePanel: PropTypes.func,
|
||||
showCloseSidePanelIcon: PropTypes.bool,
|
||||
positioned: PropTypes.bool
|
||||
};
|
||||
|
||||
class Logo extends React.Component {
|
||||
@@ -14,8 +15,9 @@ class Logo extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { positioned } = this.props;
|
||||
return (
|
||||
<div className="top-logo">
|
||||
<div className={`top-logo ${positioned ? 'd-none d-md-block positioned-top-logo' : ''}`}>
|
||||
<a href={siteRoot} id="logo">
|
||||
<img src={logoPath.indexOf('image-view') != -1 ? logoPath : mediaUrl + logoPath} height={logoHeight} width={logoWidth} title={siteTitle} alt="logo" />
|
||||
</a>
|
||||
|
@@ -5,7 +5,7 @@ import {
|
||||
gettext, siteRoot, canAddGroup, canAddRepo, canShareRepo,
|
||||
canGenerateShareLink, canGenerateUploadLink, canInvitePeople,
|
||||
enableTC, sideNavFooterCustomHtml, additionalAppBottomLinks,
|
||||
canViewOrg, isDocs, isPro, isDBSqlite3, customNavItems
|
||||
canViewOrg, isDocs, isPro, isDBSqlite3, customNavItems, mediaUrl
|
||||
} from '../utils/constants';
|
||||
import { seafileAPI } from '../utils/seafile-api';
|
||||
import { Utils } from '../utils/utils';
|
||||
@@ -16,6 +16,8 @@ import CreateGroupDialog from '../components/dialog/create-group-dialog';
|
||||
const propTypes = {
|
||||
currentTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||
tabItemClick: PropTypes.func.isRequired,
|
||||
isSidePanelFolded: PropTypes.bool,
|
||||
toggleFoldSideNav: PropTypes.func
|
||||
};
|
||||
|
||||
const SUB_NAV_ITEM_HEIGHT = 28;
|
||||
@@ -23,12 +25,13 @@ const SUB_NAV_ITEM_HEIGHT = 28;
|
||||
class MainSideNav extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
filesNavUnfolded: false,
|
||||
sharedExtended: false,
|
||||
closeSideBar:false,
|
||||
groupItems: [],
|
||||
isCreateGroupDialogOpen: false
|
||||
isCreateGroupDialogOpen: false,
|
||||
};
|
||||
this.adminHeight = 0;
|
||||
this.filesNavHeight = 0;
|
||||
@@ -214,12 +217,22 @@ class MainSideNav extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
toggleMinimize = () => {
|
||||
this.setState({
|
||||
isMinimized: !this.state.isMinimized
|
||||
}, () => {
|
||||
localStorage.setItem('sf_user_side_nav_minimized', this.state.isMinimized);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
render() {
|
||||
let showActivity = isDocs || isPro || !isDBSqlite3;
|
||||
const { filesNavUnfolded } = this.state;
|
||||
const { isSidePanelFolded } = this.props;
|
||||
return (
|
||||
<div className="side-nav">
|
||||
<div className="side-nav-con">
|
||||
<div className={'side-nav-con d-flex flex-column'}>
|
||||
<h2 className="mb-2 px-2 font-weight-normal heading">{gettext('Workspace')}</h2>
|
||||
<ul className="nav nav-pills flex-column nav-container">
|
||||
<li id="files" className={`nav-item flex-column ${this.getActiveClass('libraries')}`}>
|
||||
@@ -334,6 +347,16 @@ class MainSideNav extends React.Component {
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
<div className="side-nav-bottom-toolbar d-none d-md-flex mt-auto px-2 rounded flex-shrink-0 align-items-center" onClick={this.props.toggleFoldSideNav}>
|
||||
{isSidePanelFolded ? <img src={`${mediaUrl}img/open-sidebar.svg`} width="20" alt="" title={gettext('Unfold the sidebar')} /> : (
|
||||
<>
|
||||
<img className="mr-2" src={`${mediaUrl}img/close-sidebar.svg`} width="20" alt="" />
|
||||
<span>{gettext('Fold the sidebar')}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Utils } from '../utils/utils';
|
||||
import Logo from './logo';
|
||||
import MainSideNav from './main-side-nav';
|
||||
|
||||
@@ -8,23 +9,30 @@ const propTypes = {
|
||||
currentTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
onCloseSidePanel: PropTypes.func,
|
||||
tabItemClick: PropTypes.func,
|
||||
children: PropTypes.object
|
||||
children: PropTypes.object,
|
||||
showLogoOnlyInMobile: PropTypes.bool,
|
||||
isSidePanelFolded: PropTypes.bool,
|
||||
toggleFoldSideNav: PropTypes.func
|
||||
};
|
||||
|
||||
class SidePanel extends React.Component {
|
||||
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
const { children, isSidePanelFolded, showLogoOnlyInMobile = false } = this.props;
|
||||
return (
|
||||
<div className={`side-panel ${this.props.isSidePanelClosed ? '' : 'left-zero'}`}>
|
||||
<div className="side-panel-north">
|
||||
<Logo onCloseSidePanel={this.props.onCloseSidePanel}/>
|
||||
<div className={`side-panel ${isSidePanelFolded ? 'side-panel-folded' : ''} ${this.props.isSidePanelClosed ? '' : 'left-zero'}`}>
|
||||
<div className={'side-panel-north'}>
|
||||
{showLogoOnlyInMobile && !Utils.isDesktop() && <Logo onCloseSidePanel={this.props.onCloseSidePanel} />}
|
||||
</div>
|
||||
<div className="side-panel-center">
|
||||
{children ?
|
||||
children :
|
||||
<MainSideNav tabItemClick={this.props.tabItemClick} currentTab={this.props.currentTab} />
|
||||
}
|
||||
{children ? children : (
|
||||
<MainSideNav
|
||||
tabItemClick={this.props.tabItemClick}
|
||||
currentTab={this.props.currentTab}
|
||||
isSidePanelFolded={isSidePanelFolded}
|
||||
toggleFoldSideNav={this.props.toggleFoldSideNav}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -1,46 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import CommonToolbar from './common-toolbar';
|
||||
import { Button } from 'reactstrap';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
|
||||
const propTypes = {
|
||||
onShowSidePanel: PropTypes.func.isRequired,
|
||||
onSearchedClick: PropTypes.func.isRequired,
|
||||
toggleInvitePeopleDialog: PropTypes.func.isRequired,
|
||||
searchPlaceholder: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
class InvitationsToolbar extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let { onShowSidePanel, onSearchedClick, toggleInvitePeopleDialog } = this.props;
|
||||
return (
|
||||
<div className="main-panel-north border-left-show">
|
||||
<div className="cur-view-toolbar">
|
||||
<span title="Side Nav Menu" onClick={onShowSidePanel} className="sf2-icon-menu side-nav-toggle hidden-md-up d-md-none">
|
||||
</span>
|
||||
{Utils.isDesktop() ? (
|
||||
<div className="operation">
|
||||
<Button color="btn btn-secondary operation-item" onClick={toggleInvitePeopleDialog}>
|
||||
<i className="sf3-font sf3-font-enlarge text-secondary mr-1"></i>{gettext('Invite Guest')}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<span className="sf2-icon-plus mobile-toolbar-icon" title={gettext('Invite Guest')} onClick={toggleInvitePeopleDialog}></span>
|
||||
)}
|
||||
</div>
|
||||
<CommonToolbar searchPlaceholder={this.props.searchPlaceholder} onSearchedClick={onSearchedClick}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
InvitationsToolbar.propTypes = propTypes;
|
||||
|
||||
export default InvitationsToolbar;
|
@@ -1,91 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { DropdownToggle, Dropdown, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||
import { Link, navigate } from '@gatsbyjs/reach-router';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { siteRoot, gettext } from '../../utils/constants';
|
||||
import ModalPortal from '../modal-portal';
|
||||
import CreateRepoDialog from '../dialog/create-repo-dialog';
|
||||
|
||||
const propTypes = {
|
||||
onCreateRepo: PropTypes.func.isRequired,
|
||||
moreShown: PropTypes.bool
|
||||
};
|
||||
|
||||
class MyLibsToolbar extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isCreateRepoDialogOpen: false,
|
||||
isOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
onCreateRepo = (repo) => {
|
||||
this.props.onCreateRepo(repo);
|
||||
this.onCreateToggle();
|
||||
};
|
||||
|
||||
onCreateToggle = () => {
|
||||
this.setState({isCreateRepoDialogOpen: !this.state.isCreateRepoDialogOpen});
|
||||
};
|
||||
|
||||
toggleMore = () => {
|
||||
this.setState({ isOpen: !this.state.isOpen });
|
||||
};
|
||||
|
||||
onDropdownToggleKeyDown = (e) => {
|
||||
if (e.key == 'Enter' || e.key == 'Space') {
|
||||
this.toggleMore();
|
||||
}
|
||||
};
|
||||
|
||||
visitDeletedviaKey = (e) => {
|
||||
if (e.key == 'Enter' || e.key == 'Space') {
|
||||
navigate(`${siteRoot}my-libs/deleted/`);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { moreShown = false } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
{Utils.isDesktop() ? (
|
||||
<div className="operation">
|
||||
<button className="btn btn-secondary operation-item" title={gettext('New Library')} onClick={this.onCreateToggle}>
|
||||
<i className="sf3-font sf3-font-enlarge text-secondary mr-1"></i>{gettext('New Library')}
|
||||
</button>
|
||||
{moreShown &&
|
||||
<Dropdown isOpen={this.state.isOpen} toggle={this.toggleMore}>
|
||||
<DropdownToggle className='btn btn-secondary operation-item' onKeyDown={this.onDropdownToggleKeyDown}>
|
||||
{gettext('More')}
|
||||
</DropdownToggle>
|
||||
<DropdownMenu>
|
||||
<DropdownItem className="link-dropdown-container" onKeyDown={this.visitDeletedviaKey}>
|
||||
<Link className="link-dropdown-item" to={siteRoot + 'my-libs/deleted/'}>{gettext('Deleted Libraries')}</Link>
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
}
|
||||
</div>
|
||||
) : (
|
||||
<span className="sf2-icon-plus mobile-toolbar-icon" title={gettext('New Library')} onClick={this.onCreateToggle}></span>
|
||||
)}
|
||||
{this.state.isCreateRepoDialogOpen && (
|
||||
<ModalPortal>
|
||||
<CreateRepoDialog
|
||||
libraryType='mine'
|
||||
onCreateRepo={this.onCreateRepo}
|
||||
onCreateToggle={this.onCreateToggle}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MyLibsToolbar.propTypes = propTypes;
|
||||
|
||||
export default MyLibsToolbar;
|
Reference in New Issue
Block a user