Sidebar editing toggle and only scroll atlas content

This commit is contained in:
Jori Lallo
2016-08-11 13:32:56 +02:00
parent 41505039b2
commit eb156e3069
15 changed files with 167 additions and 85 deletions

View File

@@ -22,7 +22,6 @@ class Layout extends React.Component {
actions: React.PropTypes.node, actions: React.PropTypes.node,
title: React.PropTypes.node, title: React.PropTypes.node,
titleText: React.PropTypes.node, titleText: React.PropTypes.node,
fixed: React.PropTypes.bool,
loading: React.PropTypes.bool, loading: React.PropTypes.bool,
user: React.PropTypes.object.isRequired, user: React.PropTypes.object.isRequired,
search: React.PropTypes.bool, search: React.PropTypes.bool,
@@ -59,7 +58,7 @@ class Layout extends React.Component {
<LoadingIndicator /> <LoadingIndicator />
) : null } ) : null }
<div className={ cx(styles.header, { fixed: this.props.fixed }) }> <div className={ cx(styles.header) }>
<div className={ styles.headerLeft }> <div className={ styles.headerLeft }>
<Link to="/" className={ styles.team }>{ user.team.name }</Link> <Link to="/" className={ styles.team }>{ user.team.name }</Link>
<span className={ styles.title }> <span className={ styles.title }>
@@ -93,7 +92,9 @@ class Layout extends React.Component {
</Flex> </Flex>
</div> </div>
<div className={ cx(styles.content, { fixed: this.props.fixed }) }> <div
className={ cx(styles.content) }
>
{ this.props.children } { this.props.children }
</div> </div>
</div> </div>

View File

@@ -6,6 +6,7 @@
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
height: 100%;
} }
.header { .header {
@@ -20,15 +21,6 @@
font-size: 14px; font-size: 14px;
line-height: 1; line-height: 1;
&.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 900;
background: #fff;
}
} }
.headerLeft { .headerLeft {
@@ -73,8 +65,6 @@
display: flex; display: flex;
flex: 1; flex: 1;
justify-content: center; justify-content: center;
height: 100%;
&.fixed { overflow: scroll;
padding-top: $headerHeight;
}
} }

View File

@@ -1,14 +1,14 @@
var React = require('react'); import React from 'react';
import history from 'utils/History'; import history from 'utils/History';
import styles from './Tree.scss'; import styles from './Tree.scss';
import classNames from 'classnames/bind'; import classNames from 'classnames/bind';
const cx = classNames.bind(styles); const cx = classNames.bind(styles);
var Node = React.createClass({ class Node extends React.Component {
displayName: 'UITreeNode', displayName: 'UITreeNode'
renderCollapse() { renderCollapse = () => {
var index = this.props.index; var index = this.props.index;
if(index.children && index.children.length) { if(index.children && index.children.length) {
@@ -26,9 +26,9 @@ var Node = React.createClass({
} }
return null; return null;
}, }
renderChildren() { renderChildren = () => {
var index = this.props.index; var index = this.props.index;
var tree = this.props.tree; var tree = this.props.tree;
var dragging = this.props.dragging; var dragging = this.props.dragging;
@@ -51,6 +51,7 @@ var Node = React.createClass({
index={childIndex} index={childIndex}
key={childIndex.id} key={childIndex.id}
dragging={dragging} dragging={dragging}
allowUpdates={ this.props.allowUpdates }
paddingLeft={this.props.paddingLeft} paddingLeft={this.props.paddingLeft}
onCollapse={this.props.onCollapse} onCollapse={this.props.onCollapse}
onDragStart={this.props.onDragStart} onDragStart={this.props.onDragStart}
@@ -62,14 +63,25 @@ var Node = React.createClass({
} }
return null; return null;
}, }
isModifying = () => {
return this.props.allowUpdates && !this.props.rootNode;
}
onClick = () => {
const index = this.props.index;
const node = index.node;
if (!this.isModifying()) history.push(node.url);
}
render() { render() {
var tree = this.props.tree; const tree = this.props.tree;
var index = this.props.index; const index = this.props.index;
var dragging = this.props.dragging; const dragging = this.props.dragging;
var node = index.node; const node = index.node;
var style = {}; const style = {};
return ( return (
<div className={cx(styles.node, { <div className={cx(styles.node, {
@@ -79,12 +91,15 @@ var Node = React.createClass({
<div <div
className={ styles.inner } className={ styles.inner }
ref="inner" ref="inner"
onMouseDown={this.props.rootNode ? (e) => e.stopPropagation() : this.handleMouseDown} onMouseDown={this.props.rootNode || !this.props.allowUpdates ? (e) => e.stopPropagation() : this.handleMouseDown}
> >
{!this.props.rootNode && this.renderCollapse()} { !this.props.rootNode && this.renderCollapse() }
<span <span
className={ cx(styles.nodeLabel, { rootLabel: this.props.rootNode }) } className={ cx(styles.nodeLabel, {
onClick={() => { history.push(node.url) }} rootLabel: this.props.rootNode,
isModifying: this.isModifying(),
}) }
onClick={ this.onClick }
> >
{ node.title } { node.title }
</span> </span>
@@ -92,15 +107,15 @@ var Node = React.createClass({
{this.renderChildren()} {this.renderChildren()}
</div> </div>
); );
}, }
handleCollapse(e) { handleCollapse = (e) => {
e.stopPropagation(); e.stopPropagation();
var nodeId = this.props.index.id; var nodeId = this.props.index.id;
if(this.props.onCollapse) this.props.onCollapse(nodeId); if(this.props.onCollapse) this.props.onCollapse(nodeId);
}, }
handleMouseDown(e) { handleMouseDown = (e) => {
var nodeId = this.props.index.id; var nodeId = this.props.index.id;
var dom = this.refs.inner; var dom = this.refs.inner;
@@ -108,6 +123,6 @@ var Node = React.createClass({
this.props.onDragStart(nodeId, dom, e); this.props.onDragStart(nodeId, dom, e);
} }
} }
}); };
module.exports = Node; module.exports = Node;

View File

@@ -7,9 +7,14 @@
} }
.tree { .tree {
position: relative;
overflow: hidden;
@include no-select; @include no-select;
padding: 20px 20px 20px 5px;
overflow: scroll;
position: absolute;
top: 0;
bottom: 40px;
right: 0;
left: 0;
} }
.draggable { .draggable {
@@ -71,6 +76,10 @@
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
&.isModifying {
cursor: move;
}
&.isActive { &.isActive {
background-color: #31363F; background-color: #31363F;
} }

View File

@@ -10,8 +10,8 @@ module.exports = React.createClass({
propTypes: { propTypes: {
tree: React.PropTypes.object.isRequired, tree: React.PropTypes.object.isRequired,
paddingLeft: React.PropTypes.number, paddingLeft: React.PropTypes.number,
renderNode: React.PropTypes.func.isRequired,
onCollapse: React.PropTypes.func, onCollapse: React.PropTypes.func,
allowUpdates: React.PropTypes.bool,
}, },
getDefaultProps() { getDefaultProps() {
@@ -32,7 +32,6 @@ module.exports = React.createClass({
init(props) { init(props) {
var tree = new Tree(props.tree); var tree = new Tree(props.tree);
tree.isNodeCollapsed = props.isNodeCollapsed; tree.isNodeCollapsed = props.isNodeCollapsed;
tree.renderNode = props.renderNode;
tree.changeNodeCollapsed = props.changeNodeCollapsed; tree.changeNodeCollapsed = props.changeNodeCollapsed;
tree.updateNodesPosition(); tree.updateNodesPosition();
@@ -61,11 +60,12 @@ module.exports = React.createClass({
}; };
return ( return (
<div className={ styles.draggable } style={draggingStyles}> <div className={ styles.draggable } style={ draggingStyles }>
<Node <Node
tree={tree} tree={ tree }
index={draggingIndex} index={ draggingIndex }
paddingLeft={this.props.paddingLeft} paddingLeft={ this.props.paddingLeft }
allowUpdates={ this.props.allowUpdates }
/> />
</div> </div>
); );
@@ -81,16 +81,17 @@ module.exports = React.createClass({
return ( return (
<div className={ styles.tree }> <div className={ styles.tree }>
{draggingDom} { draggingDom }
<Node <Node
rootNode={ true } rootNode
tree={tree} tree={ tree }
index={tree.getIndex(1)} index={ tree.getIndex(1) }
key={1} key={ 1 }
paddingLeft={this.props.paddingLeft} paddingLeft={ this.props.paddingLeft }
onDragStart={this.dragStart} allowUpdates={ this.props.allowUpdates }
onCollapse={this.toggleCollapse} onDragStart={ this.dragStart }
dragging={dragging && dragging.id} onCollapse={ this.toggleCollapse }
dragging={ dragging && dragging.id }
/> />
</div> </div>
); );

View File

@@ -43,7 +43,7 @@ function requireAuth(nextState, replace) {
} }
render(( render((
<div style={{ display: 'flex', flex: 1, minHeight: '100%' }}> <div style={{ display: 'flex', flex: 1, height: '100%' }}>
<Provider { ...stores }> <Provider { ...stores }>
<Router history={ History }> <Router history={ History }>
<Route path="/" component={ Application }> <Route path="/" component={ Application }>

View File

@@ -1,15 +1,18 @@
import React from "react"; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import Helmet from "react-helmet"; import Helmet from 'react-helmet';
const Application = observer((props) => { const Application = observer((props) => {
return ( return (
<div style={{ width: '100%', display: 'flex', flex: 1, }}> <div style={{ width: '100%', height: '100%', display: 'flex', flex: 1 }}>
<Helmet <Helmet
title="Atlas" title="Atlas"
meta={[ meta={ [
{"name": "viewport", "content": "width=device-width, initial-scale=1.0"}, {
]} name: 'viewport',
content: 'width=device-width, initial-scale=1.0',
},
] }
/> />
{ props.children } { props.children }
</div> </div>
@@ -18,6 +21,6 @@ const Application = observer((props) => {
Application.propTypes = { Application.propTypes = {
children: React.PropTypes.node.isRequired, children: React.PropTypes.node.isRequired,
} };
export default Application; export default Application;

View File

@@ -175,7 +175,7 @@ class DocumentScene extends React.Component {
onNodeCollapse={ this.store.onNodeCollapse } onNodeCollapse={ this.store.onNodeCollapse }
/> />
) } ) }
<Flex flex justify="center" className={ styles.content }> <Flex auto justify="center" className={ styles.content }>
<CenteredContent> <CenteredContent>
{ this.store.updatingContent ? ( { this.store.updatingContent ? (
<AtlasPreviewLoading /> <AtlasPreviewLoading />

View File

@@ -5,4 +5,5 @@
.content { .content {
position: relative; position: relative;
overflow: scroll;
} }

View File

@@ -8,10 +8,14 @@ import styles from './Sidebar.scss';
import classNames from 'classnames/bind'; import classNames from 'classnames/bind';
const cx = classNames.bind(styles); const cx = classNames.bind(styles);
import treeStyles from 'components/Tree/Tree.scss'; import SidebarStore from './SidebarStore';
// import treeStyles from 'components/Tree/Tree.scss';
@observer @observer
class Sidebar extends React.Component { class Sidebar extends React.Component {
static store;
static propTypes = { static propTypes = {
open: PropTypes.bool, open: PropTypes.bool,
onToggle: PropTypes.func.isRequired, onToggle: PropTypes.func.isRequired,
@@ -20,27 +24,39 @@ class Sidebar extends React.Component {
onNodeCollapse: PropTypes.func.isRequired, onNodeCollapse: PropTypes.func.isRequired,
} }
renderNode = (node) => { constructor(props) {
return ( super(props);
<span className={ treeStyles.nodeLabel } onClick={ this.onClickNode.bind(null, node) }>
{ node.module.name } this.store = new SidebarStore();
</span> }
);
toggleEdit = (e) => {
e.preventDefault();
this.store.toggleEdit();
} }
render() { render() {
return ( return (
<Flex> <Flex>
{ this.props.open && ( { this.props.open && (
<div className={ cx(styles.sidebar) }> <Flex column className={ cx(styles.sidebar) }>
<Tree <Flex auto className={ cx(styles.content) }>
paddingLeft={ 10 } <Tree
tree={ this.props.navigationTree } paddingLeft={ 10 }
onChange={ this.props.onNavigationUpdate } tree={ this.props.navigationTree }
onCollapse={ this.props.onNodeCollapse } allowUpdates={ this.store.isEditing }
renderNode={ this.renderNode } onChange={ this.props.onNavigationUpdate }
/> onCollapse={ this.props.onNodeCollapse }
</div> />
</Flex>
<Flex auto className={ styles.actions }>
<a
href
onClick={ this.toggleEdit }
className={ cx(styles.action, { active: this.store.isEditing }) }
>Edit</a>
</Flex>
</Flex>
) } ) }
<div <div
onClick={ this.props.onToggle } onClick={ this.props.onToggle }

View File

@@ -1,7 +1,8 @@
.sidebar { @import '~styles/constants.scss';
width: 250px;
padding: 20px 20px 20px 5px;
.sidebar {
position: relative;
width: 250px;
border-right: 1px solid #eee; border-right: 1px solid #eee;
} }
@@ -26,3 +27,30 @@
height: 28px; height: 28px;
opacity: 0.15; opacity: 0.15;
} }
.content {
padding: 20px 20px 20px 5px;
}
.tree {
}
.actions {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 10px 20px;
height: 40px;
font-size: 14px;
}
.action {
color: $darkGray;
&.active {
color: $textColor;
}
}

View File

@@ -0,0 +1,13 @@
import { observable, action } from 'mobx';
class SidebarStore {
@observable isEditing = false;
/* Actions */
@action toggleEdit = () => {
this.isEditing = !this.isEditing;
}
}
export default SidebarStore;

View File

@@ -3,6 +3,9 @@ $lightGray: #eee;
$textColor: #171B35; $textColor: #171B35;
$actionColor: #2da9e1; $actionColor: #2da9e1;
$darkGray: #ccc;
$lightGray: #eee;
$headerHeight: 42px; $headerHeight: 42px;
:export { :export {

View File

@@ -6,6 +6,7 @@
<style> <style>
#root { #root {
flex: 1; flex: 1;
height: 100%;
} }
.container { .container {
@@ -21,7 +22,7 @@
} }
</style> </style>
</head> </head>
<body style='display: flex; width: 100%'> <body style='display: flex; width: 100%; height: 100%;'>
<div id="root"> <div id="root">
<div class="container"> <div class="container">
<div class="header"></div> <div class="header"></div>

View File

@@ -5,6 +5,7 @@
<style> <style>
#root { #root {
flex: 1; flex: 1;
height: 100%;
} }
.container { .container {
@@ -20,7 +21,7 @@
} }
</style> </style>
</head> </head>
<body style='display: flex; width: 100%'> <body style='display: flex; width: 100%; height: 100%;'>
<div id="root"> <div id="root">
<div class="container"> <div class="container">
<div class="header"></div> <div class="header"></div>