1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-01 15:23:05 +00:00

[settings] redesigned the 'heading' bar; fixup for the side nav; impr… (#6250)

* [settings] redesigned the 'heading' bar; fixup for the side nav; improved UI for content headings

* [settings] resigned it (the top bar, the side panel; the code frame)
This commit is contained in:
llj 2024-06-25 21:48:57 +08:00 committed by GitHub
parent a5edeafb74
commit 26bdd0decd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 116 additions and 120 deletions

View File

@ -4,22 +4,26 @@ import Logo from './logo';
import MainSideNav from './main-side-nav';
const propTypes = {
isSidePanelClosed: PropTypes.bool.isRequired,
currentTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
onCloseSidePanel: PropTypes.func.isRequired,
tabItemClick: PropTypes.func.isRequired,
isSidePanelClosed: PropTypes.bool,
currentTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
onCloseSidePanel: PropTypes.func,
tabItemClick: PropTypes.func,
children: PropTypes.object
};
class SidePanel extends React.Component {
render() {
const { children } = this.props;
return (
<div className={`side-panel ${this.props.isSidePanelClosed ? '' : 'left-zero'}`}>
<div className="side-panel-north">
<Logo onCloseSidePanel={this.props.onCloseSidePanel}/>
</div>
<div className="side-panel-center">
<MainSideNav tabItemClick={this.props.tabItemClick} currentTab={this.props.currentTab} />
{children ? children :
<MainSideNav tabItemClick={this.props.tabItemClick} currentTab={this.props.currentTab} />
}
</div>
</div>
);

View File

@ -13,10 +13,11 @@ const propTypes = {
path: PropTypes.string,
repoName: PropTypes.string,
isLibView: PropTypes.bool,
onSearchedClick: PropTypes.func.isRequired,
onSearchedClick: PropTypes.func,
searchPlaceholder: PropTypes.string,
currentRepoInfo: PropTypes.object,
isViewFile: PropTypes.bool,
showSearch: PropTypes.bool
};
class CommonToolbar extends React.Component {
@ -62,9 +63,10 @@ class CommonToolbar extends React.Component {
};
render() {
const { showSearch = true } = this.props;
return (
<div className="common-toolbar">
{this.renderSearch()}
{showSearch && this.renderSearch()}
<Notification />
<Account />
{showLogoutIcon && (<Logout />)}

View File

@ -3,24 +3,29 @@ import PropTypes from 'prop-types';
import CommonToolbar from './common-toolbar';
const propTypes = {
onShowSidePanel: PropTypes.func.isRequired,
onSearchedClick: PropTypes.func.isRequired,
onShowSidePanel: PropTypes.func,
onSearchedClick: PropTypes.func,
searchPlaceholder: PropTypes.string,
children: PropTypes.object
children: PropTypes.object,
showSearch: PropTypes.bool
};
class TopToolbar extends React.Component {
render() {
const { onShowSidePanel, onSearchedClick } = this.props;
const { onShowSidePanel, onSearchedClick, children, showSearch } = this.props;
return (
<div className="main-panel-north border-left-show">
<div className={`main-panel-north ${children ? '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>
{this.props.children}
</div>
<CommonToolbar searchPlaceholder={this.props.searchPlaceholder} onSearchedClick={onSearchedClick} />
<CommonToolbar
showSearch={showSearch}
searchPlaceholder={this.props.searchPlaceholder}
onSearchedClick={onSearchedClick}
/>
</div>
);
}

View File

@ -225,7 +225,7 @@ class LinkedDevices extends Component {
return (
<div className="setting-item" id="linked-devices">
<h3 className="setting-item-heading">{gettext('Linked Devices')}</h3>
<div className="cur-view-content">
<div>
<Content
loading={loading}
errorMsg={errorMsg}

View File

@ -4,12 +4,16 @@ import PropTypes from 'prop-types';
class SideNav extends React.Component {
render() {
return (
<ul className="nav flex-column user-setting-nav">
{this.props.data.map((item, index) => {
return item.show ?
<li key={index} className={`nav-item${this.props.curItemID == item.href.substr(1) ? ' active' : ''}`}><a className="nav-link" href={item.href}>{item.text}</a></li> : null;
})}
</ul>
<div className="side-nav">
<div className="side-nav-con">
<ul className="nav nav-pills flex-column">
{this.props.data.map((item, index) => {
return item.show ?
<li key={index} className={`nav-item${this.props.curItemID == item.href.substr(1) ? ' active' : ''}`}><a className={`nav-link${this.props.curItemID == item.href.substr(1) ? ' active' : ''}`} href={item.href}>{item.text}</a></li> : null;
})}
</ul>
</div>
</div>
);
}
}

View File

@ -4,52 +4,24 @@ body {
#wrapper {
height: 100%;
}
.top-header {
background: #f4f4f7;
border-bottom: 1px solid #e8e8e8;
padding: .5rem 1rem;
flex-shrink: 0;
.main-panel {
overflow: hidden;
}
.side-panel {
flex: 0 0 22%;
padding: 1rem;
border-right: 1px solid #eee;
}
.main-panel {
flex: 1 0 78%;
}
.heading {
padding: 8px 16px;
background: #f9f9f9;
font-size: 1rem;
color: #212529;
font-weight: normal;
line-height: 1.5;
margin:0;
position: relative;
}
.heading:after {
position: absolute;
left: 16px;
right: 16px;
bottom: 0;
content: '';
border-bottom: 1px solid #e8e8e8;
}
.content {
padding: 0rem 1rem 8rem;
overflow: auto;
}
.setting-item {
font-size: 0.875rem;
margin: 1em 0 3em;
padding: 1em 0 3em;
}
.setting-item-heading {
font-size: 0.9375rem;
font-weight: normal;
padding-bottom: 0.3rem;
border-bottom: 1px solid #ddd;
border-bottom: 1px solid #eee;
margin-bottom: 0.7rem;
}
.user-avatar {
@ -69,20 +41,6 @@ body {
text-align: center;
cursor: pointer;
}
.user-setting-nav .nav-item .nav-link {
flex: auto;
margin: 0;
padding-left: 1em;
border-left: 2px solid transparent;
color: #212529;
}
.user-setting-nav .nav-item.active .nav-link {
color: #ff9800;
border-color: #ff9800;
}
.user-setting-nav .nav-item .nav-link:hover {
color: #eb8205;
}
.eye-icon {
color: #666;
}

View File

@ -1,11 +1,14 @@
import React from 'react';
import ReactDom from 'react-dom';
import { navigate } from '@gatsbyjs/reach-router';
import MediaQuery from 'react-responsive';
import { Modal } from 'reactstrap';
import { Utils } from './utils/utils';
import { isPro, isDBSqlite3, gettext, siteRoot, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle } from './utils/constants';
import { isPro, isDBSqlite3, gettext, siteRoot } from './utils/constants';
import { seafileAPI } from './utils/seafile-api';
import toaster from './components/toast';
import CommonToolbar from './components/toolbar/common-toolbar';
import SidePanel from './components/side-panel';
import MainPanel from './components/main-panel';
import TopToolbar from './components/toolbar/top-toolbar';
import SideNav from './components/user-settings/side-nav';
import UserAvatarForm from './components/user-settings/user-avatar-form';
import UserBasicInfoForm from './components/user-settings/user-basic-info-form';
@ -21,6 +24,7 @@ import SocialLoginSAML from './components/user-settings/social-login-saml';
import LinkedDevices from './components/user-settings/linked-devices';
import DeleteAccount from './components/user-settings/delete-account';
import './css/layout.css';
import './css/toolbar.css';
import './css/search.css';
@ -59,6 +63,7 @@ class Settings extends React.Component {
];
this.state = {
isSidePanelClosed: false,
curItemID: this.sideNavItems[0].href.substr(1)
};
}
@ -86,17 +91,6 @@ class Settings extends React.Component {
});
};
onSearchedClick = (selectedItem) => {
if (selectedItem.is_dir === true) {
let url = siteRoot + 'library/' + selectedItem.repo_id + '/' + selectedItem.repo_name + selectedItem.path;
navigate(url, {repalce: true});
} else {
let url = siteRoot + 'lib/' + selectedItem.repo_id + '/file' + Utils.encodePath(selectedItem.path);
let newWindow = window.open('about:blank');
newWindow.location.href = url;
}
};
handleContentScroll = (e) => {
const scrollTop = e.target.scrollTop;
const scrolled = this.sideNavItems.filter((item, index) => {
@ -109,50 +103,79 @@ class Settings extends React.Component {
}
};
onCloseSidePanel = () => {
this.setState({
isSidePanelClosed: !this.state.isSidePanelClosed
});
};
onShowSidePanel = () => {
this.setState({
isSidePanelClosed: !this.state.isSidePanelClosed
});
};
toggleSidePanel = () => {
this.setState({
isSidePanelClosed: !this.state.isSidePanelClosed
});
};
render() {
const { isSidePanelClosed } = this.state;
return (
<React.Fragment>
<div className="h-100 d-flex flex-column">
<div className="top-header d-flex justify-content-between">
<a href={siteRoot}>
<img src={mediaUrl + logoPath} height={logoHeight} width={logoWidth} title={siteTitle} alt="logo" />
</a>
<CommonToolbar onSearchedClick={this.onSearchedClick} />
</div>
<div className="flex-auto d-flex o-hidden">
<div className="side-panel o-auto">
<SideNav data={this.sideNavItems} curItemID={this.state.curItemID} />
</div>
<div className="main-panel d-flex flex-column">
<h2 className="heading">{gettext('Settings')}</h2>
<div className="content position-relative" onScroll={this.handleContentScroll}>
<div id="user-basic-info" className="setting-item">
<h3 className="setting-item-heading">{gettext('Profile Setting')}</h3>
<UserAvatarForm />
{this.state.userInfo && <UserBasicInfoForm userInfo={this.state.userInfo} updateUserInfo={this.updateUserInfo} />}
<div id="main" className="h-100">
<SidePanel
isSidePanelClosed={isSidePanelClosed}
onCloseSidePanel={this.onCloseSidePanel}
>
<SideNav data={this.sideNavItems} curItemID={this.state.curItemID} />
</SidePanel>
<MainPanel>
<>
<TopToolbar
onShowSidePanel={this.onShowSidePanel}
showSearch={false}
>
</TopToolbar>
<div className="main-panel-center flex-row">
<div className="cur-view-container">
<div className="cur-view-path">
<h3 className="sf-heading m-0">{gettext('Settings')}</h3>
</div>
<div className="cur-view-content content position-relative" onScroll={this.handleContentScroll}>
<div id="user-basic-info" className="setting-item">
<h3 className="setting-item-heading">{gettext('Profile Setting')}</h3>
<UserAvatarForm />
{this.state.userInfo && <UserBasicInfoForm userInfo={this.state.userInfo} updateUserInfo={this.updateUserInfo} />}
</div>
{canUpdatePassword &&
<div id="update-user-passwd" className="setting-item">
<h3 className="setting-item-heading">{gettext('Password')}</h3>
<a href={`${siteRoot}accounts/password/change/`} className="btn btn-outline-primary">{passwordOperationText}</a>
</div>
}
{enableGetAuthToken && <WebAPIAuthToken />}
{enableWebdavSecret && <WebdavPassword />}
{enableAddressBook && this.state.userInfo &&
<ListInAddressBook userInfo={this.state.userInfo} updateUserInfo={this.updateUserInfo} />}
<LanguageSetting />
{(isPro || !isDBSqlite3) && <EmailNotice />}
{twoFactorAuthEnabled && <TwoFactorAuthentication />}
{enableWechatWork && <SocialLogin />}
{enableDingtalk && <SocialLoginDingtalk />}
{(enableADFS || (enableMultiADFS && isOrgContext)) && <SocialLoginSAML />}
<LinkedDevices />
{enableDeleteAccount && <DeleteAccount />}
</div>
</div>
{canUpdatePassword &&
<div id="update-user-passwd" className="setting-item">
<h3 className="setting-item-heading">{gettext('Password')}</h3>
<a href={`${siteRoot}accounts/password/change/`} className="btn btn-outline-primary">{passwordOperationText}</a>
</div>
}
{enableGetAuthToken && <WebAPIAuthToken />}
{enableWebdavSecret && <WebdavPassword />}
{enableAddressBook && this.state.userInfo &&
<ListInAddressBook userInfo={this.state.userInfo} updateUserInfo={this.updateUserInfo} />}
<LanguageSetting />
{(isPro || !isDBSqlite3) && <EmailNotice />}
{twoFactorAuthEnabled && <TwoFactorAuthentication />}
{enableWechatWork && <SocialLogin />}
{enableDingtalk && <SocialLoginDingtalk />}
{(enableADFS || (enableMultiADFS && isOrgContext)) && <SocialLoginSAML />}
<LinkedDevices />
{enableDeleteAccount && <DeleteAccount />}
</div>
</div>
</div>
</>
</MainPanel>
<MediaQuery query="(max-width: 767.8px)">
<Modal zIndex="1030" isOpen={!isSidePanelClosed} toggle={this.toggleSidePanel} contentClassName="d-none"></Modal>
</MediaQuery>
</div>
</React.Fragment>
);