mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-19 18:29:23 +00:00
12.0 change app structure (#6335)
* 01 change app structure * 02 change setting page * 03 optimize header style * 04 change app mobile side panel logo
This commit is contained in:
41
frontend/src/components/common/event-bus-type.js
Normal file
41
frontend/src/components/common/event-bus-type.js
Normal file
@@ -0,0 +1,41 @@
|
||||
export const EVENT_BUS_TYPE = {
|
||||
// metadata
|
||||
QUERY_COLLABORATORS: 'query-collaborators',
|
||||
QUERY_COLLABORATOR: 'query-collaborator',
|
||||
UPDATE_TABLE_ROWS: 'update-table-rows',
|
||||
|
||||
// table
|
||||
LOCAL_TABLE_CHANGED: 'local-table-changed',
|
||||
SERVER_TABLE_CHANGED: 'server-table-changed',
|
||||
TABLE_ERROR: 'table-error',
|
||||
OPEN_EDITOR: 'open-editor',
|
||||
CLOSE_EDITOR: 'close-editor',
|
||||
SELECT_CELL: 'select_cell',
|
||||
SELECT_START: 'select_start',
|
||||
SELECT_UPDATE: 'select_update',
|
||||
SELECT_END: 'select_end',
|
||||
SELECT_END_WITH_SHIFT: 'select_end_with_shift',
|
||||
SELECT_NONE: 'select_none',
|
||||
COPY_CELLS: 'copy_cells',
|
||||
PASTE_CELLS: 'paste_cells',
|
||||
SEARCH_CELLS: 'search-cells',
|
||||
CLOSE_SEARCH_CELLS: 'close-search-cells',
|
||||
OPEN_SELECT: 'open-select',
|
||||
UPDATE_LINKED_RECORDS: 'update_linked_records',
|
||||
SELECT_COLUMN: 'select_column',
|
||||
DRAG_ENTER: 'drag_enter',
|
||||
COLLAPSE_ALL_GROUPS: 'collapse_all_groups',
|
||||
EXPAND_ALL_GROUPS: 'expand_all_groups',
|
||||
|
||||
// modify view
|
||||
MODIFY_FILTERS: 'modify_filters',
|
||||
MODIFY_SORTS:'modify_sorts',
|
||||
MODIFY_GROUPBYS:'modify_groupbys',
|
||||
MODIFY_HIDDEN_COLUMNS:'modify_hidden_columns',
|
||||
|
||||
// change
|
||||
VIEW_CHANGED: 'view_changed',
|
||||
|
||||
// library
|
||||
CURRENT_LIBRARY_CHANGED: 'current_library_changed',
|
||||
};
|
28
frontend/src/components/common/event-bus.js
Normal file
28
frontend/src/components/common/event-bus.js
Normal file
@@ -0,0 +1,28 @@
|
||||
class EventBus {
|
||||
subscribers = {};
|
||||
|
||||
subscribe(type, handler) {
|
||||
if (!this.subscribers[type]) {
|
||||
this.subscribers[type] = [];
|
||||
}
|
||||
|
||||
const handlers = this.subscribers[type];
|
||||
handlers.push(handler);
|
||||
|
||||
return () => {
|
||||
const index = handlers.indexOf(handler);
|
||||
if (index > -1) {
|
||||
handlers.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
dispatch(type, ...data) {
|
||||
const handlers = this.subscribers[type];
|
||||
if (Array.isArray(handlers)) {
|
||||
handlers.forEach(handler => handler(...data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default EventBus;
|
5
frontend/src/components/header.css
Normal file
5
frontend/src/components/header.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.top-header {
|
||||
background-color: #f8fafd;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: .5rem 1rem;
|
||||
}
|
@@ -3,7 +3,11 @@ import PropTypes from 'prop-types';
|
||||
import Logo from './logo';
|
||||
import CommonToolbar from './toolbar/common-toolbar';
|
||||
|
||||
import './header.css';
|
||||
|
||||
const propTypes = {
|
||||
children: PropTypes.object,
|
||||
eventBus: PropTypes.object.isRequired,
|
||||
isSidePanelClosed: PropTypes.bool,
|
||||
onCloseSidePanel: PropTypes.func,
|
||||
onShowSidePanel: PropTypes.func,
|
||||
@@ -15,21 +19,23 @@ const propTypes = {
|
||||
class Header extends React.Component {
|
||||
|
||||
render() {
|
||||
const { onShowSidePanel, onSearchedClick, showSearch } = this.props;
|
||||
const { onShowSidePanel, onSearchedClick, showSearch, children } = this.props;
|
||||
return (
|
||||
<div id="header" className="d-flex justify-content-between py-2 px-4">
|
||||
<div id="header" className="top-header d-flex justify-content-between flex-shrink-0">
|
||||
<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={`flex-shrink-0 d-flex flex-fill ${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>
|
||||
{children}
|
||||
</div>
|
||||
<CommonToolbar
|
||||
showSearch={showSearch}
|
||||
searchPlaceholder={this.props.searchPlaceholder}
|
||||
onSearchedClick={onSearchedClick}
|
||||
eventBus={this.props.eventBus}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,13 +1,10 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { siteRoot, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle } from '../utils/constants';
|
||||
import { Utils } from '../utils/utils';
|
||||
|
||||
const propTypes = {
|
||||
onCloseSidePanel: PropTypes.func,
|
||||
showCloseSidePanelIcon: PropTypes.bool,
|
||||
positioned: PropTypes.bool,
|
||||
showLogoOnlyInMobile: PropTypes.bool
|
||||
};
|
||||
|
||||
class Logo extends React.Component {
|
||||
@@ -17,14 +14,8 @@ class Logo extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { positioned, showLogoOnlyInMobile } = this.props;
|
||||
|
||||
if (showLogoOnlyInMobile && Utils.isDesktop()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`top-logo ${positioned ? 'd-none d-md-block positioned-top-logo' : ''}`}>
|
||||
<div className='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>
|
||||
|
@@ -1,22 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import GeneralToolBar from './toolbar/general-toolbar';
|
||||
|
||||
const MainContentWrapper = (WrapperedComponent) => {
|
||||
return class Wrapper extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fragment>
|
||||
<GeneralToolBar {...this.props} />
|
||||
<WrapperedComponent {...this.props} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default MainContentWrapper;
|
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import MediaQuery from 'react-responsive';
|
||||
import Logo from './logo';
|
||||
import MainSideNav from './main-side-nav';
|
||||
import { SIDE_PANEL_FOLDED_WIDTH } from '../constants';
|
||||
@@ -11,7 +12,6 @@ const propTypes = {
|
||||
onCloseSidePanel: PropTypes.func,
|
||||
tabItemClick: PropTypes.func,
|
||||
children: PropTypes.object,
|
||||
showLogoOnlyInMobile: PropTypes.bool,
|
||||
isSidePanelFolded: PropTypes.bool,
|
||||
toggleFoldSideNav: PropTypes.func
|
||||
};
|
||||
@@ -19,16 +19,15 @@ const propTypes = {
|
||||
class SidePanel extends React.Component {
|
||||
|
||||
render() {
|
||||
const { children, isSidePanelFolded, showLogoOnlyInMobile = false } = this.props;
|
||||
const { children, isSidePanelFolded } = this.props;
|
||||
const style = isSidePanelFolded ? { flexBasis: SIDE_PANEL_FOLDED_WIDTH } : {};
|
||||
return (
|
||||
<div className={classnames('side-panel', { 'side-panel-folded': isSidePanelFolded, 'left-zero': !this.props.isSidePanelClosed })} style={style}>
|
||||
<div className={'side-panel-north'}>
|
||||
<Logo
|
||||
onCloseSidePanel={this.props.onCloseSidePanel}
|
||||
showLogoOnlyInMobile={showLogoOnlyInMobile}
|
||||
/>
|
||||
</div>
|
||||
<MediaQuery query="(max-width: 767.8px)">
|
||||
<div className='side-panel-north'>
|
||||
<Logo onCloseSidePanel={this.props.onCloseSidePanel} />
|
||||
</div>
|
||||
</MediaQuery>
|
||||
<div className="side-panel-center">
|
||||
{children ? children : (
|
||||
<MainSideNav
|
||||
|
@@ -7,6 +7,7 @@ import SearchByName from '../search/search-by-name';
|
||||
import Notification from '../common/notification';
|
||||
import Account from '../common/account';
|
||||
import Logout from '../common/logout';
|
||||
import { EVENT_BUS_TYPE } from '../common/event-bus-type';
|
||||
|
||||
const propTypes = {
|
||||
repoID: PropTypes.string,
|
||||
@@ -16,14 +17,41 @@ const propTypes = {
|
||||
onSearchedClick: PropTypes.func,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
currentRepoInfo: PropTypes.object,
|
||||
eventBus: PropTypes.object,
|
||||
isViewFile: PropTypes.bool,
|
||||
showSearch: PropTypes.bool
|
||||
};
|
||||
|
||||
class CommonToolbar extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
repoID: props.repoID,
|
||||
repoName: props.repoName,
|
||||
isLibView: props.isLibView,
|
||||
path: props.path,
|
||||
isViewFile: props.isViewFile,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.eventBus) {
|
||||
this.unsubscribeLibChange = this.props.eventBus.subscribe(EVENT_BUS_TYPE.CURRENT_LIBRARY_CHANGED, this.onRepoChange);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unsubscribeLibChange && this.unsubscribeLibChange();
|
||||
}
|
||||
|
||||
onRepoChange = ({ repoID, repoName, isLibView, path, isViewFile }) => {
|
||||
this.setState({ repoID, repoName, isLibView, path, isViewFile });
|
||||
};
|
||||
|
||||
renderSearch = () => {
|
||||
const { repoID, repoName, isLibView, searchPlaceholder, path, isViewFile } = this.props;
|
||||
const { repoID, repoName, isLibView, path, isViewFile } = this.state;
|
||||
const { searchPlaceholder } = this.props;
|
||||
const placeholder = searchPlaceholder || gettext('Search files');
|
||||
|
||||
if (isPro) {
|
||||
|
@@ -1,36 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import CommonToolbar from './common-toolbar';
|
||||
|
||||
const propTypes = {
|
||||
searchPlaceholder: PropTypes.string,
|
||||
onShowSidePanel: PropTypes.func.isRequired,
|
||||
onSearchedClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class GeneralToolbar extends React.Component {
|
||||
|
||||
render() {
|
||||
// todo get repoID?
|
||||
let { onShowSidePanel, onSearchedClick } = this.props;
|
||||
return (
|
||||
<div className="main-panel-north">
|
||||
<div className="cur-view-toolbar">
|
||||
<span
|
||||
className="sf2-icon-menu side-nav-toggle hidden-md-up d-md-none"
|
||||
title="Side Nav Menu"
|
||||
onClick={onShowSidePanel}>
|
||||
</span>
|
||||
</div>
|
||||
<CommonToolbar
|
||||
searchPlaceholder={this.props.searchPlaceholder}
|
||||
onSearchedClick={onSearchedClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GeneralToolbar.propTypes = propTypes;
|
||||
|
||||
export default GeneralToolbar;
|
31
frontend/src/components/user-settings/setting-side-panel.js
Normal file
31
frontend/src/components/user-settings/setting-side-panel.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Logo from '../logo';
|
||||
import classnames from 'classnames';
|
||||
import SideNav from './side-nav';
|
||||
|
||||
const propTypes = {
|
||||
isSidePanelClosed: PropTypes.bool,
|
||||
curItemID: PropTypes.string.isRequired,
|
||||
data: PropTypes.array.isRequired,
|
||||
onCloseSidePanel: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class SettingSidePanel extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className={classnames('side-panel', { 'left-zero': !this.props.isSidePanelClosed })}>
|
||||
<div className="side-panel-north">
|
||||
<Logo onCloseSidePanel={this.props.onCloseSidePanel}/>
|
||||
</div>
|
||||
<div className="side-panel-center">
|
||||
<SideNav data={this.props.data} curItemID={this.props.curItemID} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SettingSidePanel.propTypes = propTypes;
|
||||
|
||||
export default SettingSidePanel;
|
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import CommonToolbar from './common-toolbar';
|
||||
import CommonToolbar from '../toolbar/common-toolbar';
|
||||
|
||||
const propTypes = {
|
||||
onShowSidePanel: PropTypes.func,
|
||||
@@ -10,7 +10,7 @@ const propTypes = {
|
||||
showSearch: PropTypes.bool
|
||||
};
|
||||
|
||||
class TopToolbar extends React.Component {
|
||||
class SettingTopToolbar extends React.Component {
|
||||
|
||||
render() {
|
||||
const { onShowSidePanel, onSearchedClick, children, showSearch } = this.props;
|
||||
@@ -31,6 +31,6 @@ class TopToolbar extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
TopToolbar.propTypes = propTypes;
|
||||
SettingTopToolbar.propTypes = propTypes;
|
||||
|
||||
export default TopToolbar;
|
||||
export default SettingTopToolbar;
|
Reference in New Issue
Block a user