1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-09 10:50:24 +00:00

12.0 support drag side nav (#6340)

* 01 optimize resize bar

* 02 app side panel support resize

* 03 side panel is folded do not support resize

* 04 optimise code

* 05 use localStorage
This commit is contained in:
Michael An
2024-07-12 15:52:10 +08:00
committed by GitHub
parent 7818cdb2ab
commit d137f85420
7 changed files with 182 additions and 120 deletions

View File

@@ -9,7 +9,13 @@ import SystemNotification from './components/system-notification';
import EventBus from './components/common/event-bus'; import EventBus from './components/common/event-bus';
import Header from './components/header'; import Header from './components/header';
import SidePanel from './components/side-panel'; import SidePanel from './components/side-panel';
import MainPanel from './components/main-panel'; import ResizeBar from './components/resize-bar';
import {
DRAG_HANDLER_HEIGHT,
INIT_SIDE_PANEL_RATE,
MAX_SIDE_PANEL_RATE,
MIN_SIDE_PANEL_RATE
} from './components/resize-bar/constants';
import FilesActivities from './pages/dashboard/files-activities'; import FilesActivities from './pages/dashboard/files-activities';
import MyFileActivities from './pages/dashboard/my-file-activities'; import MyFileActivities from './pages/dashboard/my-file-activities';
import Starred from './pages/starred/starred'; import Starred from './pages/starred/starred';
@@ -48,11 +54,15 @@ class App extends Component {
isSidePanelFolded: localStorage.getItem('sf_user_side_nav_folded') == 'true' || false, isSidePanelFolded: localStorage.getItem('sf_user_side_nav_folded') == 'true' || false,
currentTab: '', currentTab: '',
pathPrefix: [], pathPrefix: [],
inResizing: false,
sidePanelRate: parseFloat(localStorage.getItem('sf_side_panel_rate') || INIT_SIDE_PANEL_RATE),
}; };
this.dirViewPanels = ['libraries', 'my-libs', 'shared-libs', 'org']; // and group this.dirViewPanels = ['libraries', 'my-libs', 'shared-libs', 'org']; // and group
window.onpopstate = this.onpopstate; window.onpopstate = this.onpopstate;
const eventBus = new EventBus(); const eventBus = new EventBus();
this.eventBus = eventBus; this.eventBus = eventBus;
this.resizeBarRef = React.createRef();
this.dragHandlerRef = React.createRef();
} }
onpopstate = (event) => { onpopstate = (event) => {
@@ -207,9 +217,53 @@ class App extends Component {
}); });
}; };
render() { onResizeMouseUp = () => {
const { currentTab, isSidePanelClosed, isSidePanelFolded } = this.state; if (this.state.inResizing) {
this.setState({
inResizing: false
});
}
localStorage.setItem('sf_side_panel_rate', this.state.sidePanelRate);
};
onResizeMouseDown = () => {
this.setState({
inResizing: true
});
};
onResizeMouseMove = (e) => {
let rate = e.nativeEvent.clientX / window.innerWidth;
this.setState({
sidePanelRate: Math.max(Math.min(rate, MAX_SIDE_PANEL_RATE), MIN_SIDE_PANEL_RATE),
});
};
onResizeMouseOver = (event) => {
if (!this.dragHandlerRef.current) return;
const { top } = this.resizeBarRef.current.getBoundingClientRect();
const dragHandlerRefTop = event.pageY - top - DRAG_HANDLER_HEIGHT / 2;
this.setDragHandlerTop(dragHandlerRefTop);
};
setDragHandlerTop = (top) => {
this.dragHandlerRef.current.style.top = top + 'px';
};
render() {
const { currentTab, isSidePanelClosed, isSidePanelFolded, sidePanelRate, inResizing } = this.state;
let mainPanelStyle = {};
let sidePanelStyle = {};
if (!isSidePanelFolded) {
mainPanelStyle = {
userSelect: inResizing ? 'none' : '',
flex: sidePanelRate ? `1 0 ${(1 - sidePanelRate) * 100}%` : `0 0 ${100 - INIT_SIDE_PANEL_RATE * 100}%`,
};
sidePanelStyle = {
userSelect: inResizing ? 'none' : '',
flex: sidePanelRate ? `0 0 ${sidePanelRate * 100}%` : `0 0 ${INIT_SIDE_PANEL_RATE * 100}%`,
};
}
return ( return (
<React.Fragment> <React.Fragment>
<SystemNotification /> <SystemNotification />
@@ -220,7 +274,12 @@ class App extends Component {
onSearchedClick={this.onSearchedClick} onSearchedClick={this.onSearchedClick}
eventBus={this.eventBus} eventBus={this.eventBus}
/> />
<div id="main" className="user-panel"> <div
id="main"
className="user-panel"
onMouseMove={inResizing ? this.onResizeMouseMove : null}
onMouseUp={this.onResizeMouseUp}
>
<SidePanel <SidePanel
isSidePanelClosed={isSidePanelClosed} isSidePanelClosed={isSidePanelClosed}
isSidePanelFolded={isSidePanelFolded} isSidePanelFolded={isSidePanelFolded}
@@ -228,8 +287,19 @@ class App extends Component {
currentTab={currentTab} currentTab={currentTab}
tabItemClick={this.tabItemClick} tabItemClick={this.tabItemClick}
toggleFoldSideNav={this.toggleFoldSideNav} toggleFoldSideNav={this.toggleFoldSideNav}
style={sidePanelStyle}
/> />
<MainPanel> {!isSidePanelFolded &&
<ResizeBar
resizeBarRef={this.resizeBarRef}
dragHandlerRef={this.dragHandlerRef}
resizeBarStyle={{ left: `calc(${sidePanelRate ? sidePanelRate * 100 + '%' : `${INIT_SIDE_PANEL_RATE * 100}%`} - 1px)` }}
dragHandlerStyle={{ height: DRAG_HANDLER_HEIGHT }}
onResizeMouseDown={this.onResizeMouseDown}
onResizeMouseOver={this.onResizeMouseOver}
/>
}
<div className="main-panel" style={mainPanelStyle}>
<Router className="reach-router"> <Router className="reach-router">
<Libraries path={siteRoot} /> <Libraries path={siteRoot} />
<Libraries path={siteRoot + 'libraries'} /> <Libraries path={siteRoot + 'libraries'} />
@@ -263,7 +333,7 @@ class App extends Component {
eventBus={this.eventBus} eventBus={this.eventBus}
/> />
</Router> </Router>
</MainPanel> </div>
<MediaQuery query="(max-width: 767.8px)"> <MediaQuery query="(max-width: 767.8px)">
<Modal zIndex="1030" isOpen={!isSidePanelClosed} toggle={this.toggleSidePanel} contentClassName="d-none"></Modal> <Modal zIndex="1030" isOpen={!isSidePanelClosed} toggle={this.toggleSidePanel} contentClassName="d-none"></Modal>
</MediaQuery> </MediaQuery>

View File

@@ -5,6 +5,8 @@ import DirColumnFile from './dir-column-file';
import DirListView from './dir-list-view'; import DirListView from './dir-list-view';
import DirGridView from './dir-grid-view'; import DirGridView from './dir-grid-view';
import { SIDE_PANEL_FOLDED_WIDTH } from '../../constants'; import { SIDE_PANEL_FOLDED_WIDTH } from '../../constants';
import ResizeBar from '../resize-bar';
import { DRAG_HANDLER_HEIGHT, MAX_SIDE_PANEL_RATE, MIN_SIDE_PANEL_RATE } from '../resize-bar/constants';
const propTypes = { const propTypes = {
isSidePanelFolded: PropTypes.bool, isSidePanelFolded: PropTypes.bool,
@@ -73,19 +75,17 @@ const propTypes = {
isDirentDetailShow: PropTypes.bool.isRequired isDirentDetailShow: PropTypes.bool.isRequired
}; };
const DRAG_HANDLER_HEIGHT = 26;
class DirColumnView extends React.Component { class DirColumnView extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
inResizing: false, inResizing: false,
navRate: 0.25, navRate: parseFloat(localStorage.getItem('sf_dir_content_nav_rate') || 0.25),
}; };
this.containerWidth = null; this.containerWidth = null;
this.resizeRef = null; this.resizeBarRef = React.createRef();
this.dragHandler = null; this.dragHandlerRef = React.createRef();
this.viewModeContainer = React.createRef(); this.viewModeContainer = React.createRef();
} }
@@ -95,7 +95,7 @@ class DirColumnView extends React.Component {
inResizing: false inResizing: false
}); });
} }
this.setCookie('navRate', this.state.navRate); localStorage.setItem('sf_dir_content_nav_rate', this.state.navRate);
}; };
onResizeMouseDown = () => { onResizeMouseDown = () => {
@@ -107,65 +107,24 @@ class DirColumnView extends React.Component {
onResizeMouseMove = (e) => { onResizeMouseMove = (e) => {
const { isSidePanelFolded } = this.props; const { isSidePanelFolded } = this.props;
let sizeNavWidth = isSidePanelFolded ? SIDE_PANEL_FOLDED_WIDTH + 3 : this.containerWidth / 0.78 * 0.22 + 3; let sizeNavWidth = isSidePanelFolded ? SIDE_PANEL_FOLDED_WIDTH + 3 : window.innerWidth - this.containerWidth;
let rate = (e.nativeEvent.clientX - sizeNavWidth) / this.containerWidth; let rate = (e.nativeEvent.clientX - sizeNavWidth) / this.containerWidth;
if (rate < 0.1) {
this.setState({ this.setState({
inResizing: false, navRate: Math.max(Math.min(rate, MAX_SIDE_PANEL_RATE), MIN_SIDE_PANEL_RATE),
navRate: 0.12,
}); });
}
else if (rate > 0.4) {
this.setState({
inResizing: false,
navRate: 0.38,
});
}
else {
this.setState({
navRate: rate
});
}
}; };
onMouseOver = (event) => { onResizeMouseOver = (event) => {
if (!this.dragHandler) return; if (!this.dragHandlerRef.current) return;
const { top } = this.resizeRef.getBoundingClientRect(); const { top } = this.resizeBarRef.current.getBoundingClientRect();
const dragHandlerTop = event.pageY - top - DRAG_HANDLER_HEIGHT / 2; const dragHandlerRefTop = event.pageY - top - DRAG_HANDLER_HEIGHT / 2;
this.setDragHandlerTop(dragHandlerTop); this.setDragHandlerTop(dragHandlerRefTop);
}; };
setDragHandlerTop = (top) => { setDragHandlerTop = (top) => {
this.dragHandler.style.top = top + 'px'; this.dragHandlerRef.current.style.top = top + 'px';
}; };
setCookie = (name, value) => {
let cookie = name + '=' + value + ';';
document.cookie = cookie;
};
getCookie = (cookiename) => {
let name = cookiename + '=';
let cookie = document.cookie.split(';');
for (let i = 0, len = cookie.length; i < len; i++) {
let c = cookie[i].trim();
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length) * 1;
}
}
return '';
};
UNSAFE_componentWillMount() {
let rate = this.getCookie('navRate');
if (rate) {
this.setState({
navRate: rate,
});
}
}
render() { render() {
const { currentMode, isTreePanelShown } = this.props; const { currentMode, isTreePanelShown } = this.props;
const { navRate, inResizing } = this.state; const { navRate, inResizing } = this.state;
@@ -198,16 +157,14 @@ class DirColumnView extends React.Component {
selectedDirentList={this.props.selectedDirentList} selectedDirentList={this.props.selectedDirentList}
onItemsMove={this.props.onItemsMove} onItemsMove={this.props.onItemsMove}
/> />
<div <ResizeBar
className="dir-content-resize" resizeBarRef={this.resizeBarRef}
ref={ref => this.resizeRef = ref} dragHandlerRef={this.dragHandlerRef}
style={{ left: `calc(${navRate ? navRate * 100 + '%' : '25%'} - 1px)` }} resizeBarStyle={{ left: `calc(${navRate ? navRate * 100 + '%' : '25%'} - 1px)` }}
onMouseDown={this.onResizeMouseDown} dragHandlerStyle={{ height: DRAG_HANDLER_HEIGHT }}
onMouseOver={this.onMouseOver} onResizeMouseDown={this.onResizeMouseDown}
> onResizeMouseOver={this.onResizeMouseOver}
<div className="line"></div> />
<div className="drag-handler" ref={ref => this.dragHandler = ref} style={{ height: DRAG_HANDLER_HEIGHT }}></div>
</div>
</> </>
)} )}
<div className="dir-content-main" style={{userSelect: select, flex: mainFlex}} onScroll={this.props.isViewFile ? () => {} : this.props.onItemsScroll}> <div className="dir-content-main" style={{userSelect: select, flex: mainFlex}} onScroll={this.props.isViewFile ? () => {} : this.props.onItemsScroll}>

View File

@@ -0,0 +1,4 @@
export const DRAG_HANDLER_HEIGHT = 26;
export const INIT_SIDE_PANEL_RATE = 0.22;
export const MAX_SIDE_PANEL_RATE = 0.40;
export const MIN_SIDE_PANEL_RATE = 0.15;

View File

@@ -0,0 +1,43 @@
.resize-bar {
height: 100%;
width: 6px;
position: absolute;
top: 0;
z-index: 8;
opacity: 0;
transition-duration: .2s;
transition-property: opacity;
}
.resize-bar:hover {
opacity: 1;
cursor: col-resize;
}
.resize-bar .resize-bar-line {
background-color: #aaa;
height: 100%;
width: 2px;
display: none;
user-select: none;
}
.resize-bar:hover .resize-bar-line {
display: block;
}
.resize-bar .resize-bar-drag-handler {
background: #2d7ff9;
border-radius: 10px;
margin-left: 1px;
position: absolute;
transform: translate(-50%);
width: 6px;
display: none;
z-index: 1;
user-select: none;
}
.resize-bar:hover .resize-bar-drag-handler {
display: block;
}

View File

@@ -0,0 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
import './index.css';
function ResizeBar(props) {
return (
<div
className="resize-bar"
ref={props.resizeBarRef}
style={props.resizeBarStyle}
onMouseDown={props.onResizeMouseDown}
onMouseOver={props.onResizeMouseOver}
>
<div className="resize-bar-line"></div>
<div className="resize-bar-drag-handler" ref={props.dragHandlerRef} style={props.dragHandlerStyle}></div>
</div>
);
}
ResizeBar.propTypes = {
resizeBarRef: PropTypes.object.isRequired,
resizeBarStyle: PropTypes.object.isRequired,
dragHandlerRef: PropTypes.object.isRequired,
dragHandlerStyle: PropTypes.object.isRequired,
onResizeMouseDown: PropTypes.func.isRequired,
onResizeMouseOver: PropTypes.func.isRequired,
};
export default ResizeBar;

View File

@@ -12,6 +12,7 @@ const propTypes = {
onCloseSidePanel: PropTypes.func, onCloseSidePanel: PropTypes.func,
tabItemClick: PropTypes.func, tabItemClick: PropTypes.func,
children: PropTypes.object, children: PropTypes.object,
style: PropTypes.object,
isSidePanelFolded: PropTypes.bool, isSidePanelFolded: PropTypes.bool,
toggleFoldSideNav: PropTypes.func toggleFoldSideNav: PropTypes.func
}; };
@@ -19,10 +20,12 @@ const propTypes = {
class SidePanel extends React.Component { class SidePanel extends React.Component {
render() { render() {
const { children, isSidePanelFolded } = this.props; const { children, isSidePanelFolded, style } = this.props;
const style = isSidePanelFolded ? { flexBasis: SIDE_PANEL_FOLDED_WIDTH } : {};
return ( return (
<div className={classnames('side-panel', { 'side-panel-folded': isSidePanelFolded, 'left-zero': !this.props.isSidePanelClosed })} style={style}> <div
className={classnames('side-panel', {'side-panel-folded': isSidePanelFolded, 'left-zero': !this.props.isSidePanelClosed})}
style={isSidePanelFolded ? { flexBasis: SIDE_PANEL_FOLDED_WIDTH } : style}
>
<MediaQuery query="(max-width: 767.8px)"> <MediaQuery query="(max-width: 767.8px)">
<div className='side-panel-north'> <div className='side-panel-north'>
<Logo onCloseSidePanel={this.props.onCloseSidePanel} /> <Logo onCloseSidePanel={this.props.onCloseSidePanel} />

View File

@@ -224,50 +224,6 @@
width: 100%; width: 100%;
} }
.dir-content-resize {
height: 100%;
width: 6px;
position: absolute;
top: 0;
z-index: 8;
opacity: 0;
transition-duration: .2s;
transition-property: opacity;
}
.dir-content-resize:hover {
opacity: 1;
cursor: col-resize;
}
.dir-content-resize .line {
background-color: #aaa;
height: 100%;
width: 2px;
display: none;
user-select: none;
}
.dir-content-resize:hover .line {
display: block;
}
.dir-content-resize .drag-handler {
background: #2d7ff9;
border-radius: 10px;
margin-left: 1px;
position: absolute;
transform: translate(-50%);
width: 6px;
display: none;
z-index: 1;
user-select: none;
}
.dir-content-resize:hover .drag-handler {
display: block;
}
.readonly-tip-message { .readonly-tip-message {
display: flex; display: flex;
padding: 0.25rem 0; padding: 0.25rem 0;