mirror of
https://github.com/xodio/xod.git
synced 2026-03-25 01:56:54 +01:00
feat([projectBrowser, state, treeView, Sidebar, styles]): add projectBrowser, that contains folders and patches, that just could be selected and dragged at this moment, but without any outcome for state
This commit is contained in:
81
app/components/ProjectBrowserTree.jsx
Normal file
81
app/components/ProjectBrowserTree.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import R from 'ramda';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Tree from 'react-ui-tree';
|
||||
|
||||
|
||||
class ProjectBrowserTree extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
active: null,
|
||||
tree: props.tree,
|
||||
};
|
||||
|
||||
this.onClickNode = this.onClickNode.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.renderNode = this.renderNode.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.tree !== this.state.tree) {
|
||||
this.updateTree(nextProps.tree);
|
||||
}
|
||||
}
|
||||
|
||||
onClickNode(node) {
|
||||
const nodeRef = node;
|
||||
if (!node.hasOwnProperty('leaf')) {
|
||||
nodeRef.collapsed = !nodeRef.collapsed;
|
||||
}
|
||||
|
||||
this.setState(R.assoc('active', nodeRef, this.state));
|
||||
}
|
||||
onChange(tree) {
|
||||
this.props.onChange(tree);
|
||||
}
|
||||
|
||||
updateTree(tree) {
|
||||
this.setState(R.assoc('tree', tree, this.state));
|
||||
}
|
||||
|
||||
bindOnClickNode(node) {
|
||||
return this.onClickNode.bind(this, node);
|
||||
}
|
||||
|
||||
renderNode(node) {
|
||||
const nodeClassName = classNames('node', {
|
||||
'is-active': node === this.state.active,
|
||||
'is-current': (node.hasOwnProperty('leaf') && node.id === this.props.currentPatchId),
|
||||
});
|
||||
|
||||
return (
|
||||
<span
|
||||
className={nodeClassName}
|
||||
onClick={this.bindOnClickNode(node)}
|
||||
>
|
||||
{node.module}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="ProjectBrowserTree">
|
||||
<Tree
|
||||
tree={this.state.tree}
|
||||
renderNode={this.renderNode}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ProjectBrowserTree.propTypes = {
|
||||
tree: React.PropTypes.object.isRequired,
|
||||
currentPatchId: React.PropTypes.number,
|
||||
onChange: React.PropTypes.func,
|
||||
};
|
||||
|
||||
export default ProjectBrowserTree;
|
||||
13
app/components/Sidebar.jsx
Normal file
13
app/components/Sidebar.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
const Sidebar = ({ children }) => (
|
||||
<div className="Sidebar">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
Sidebar.propTypes = {
|
||||
children: React.PropTypes.arrayOf(React.PropTypes.element),
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
@@ -8,6 +8,7 @@ import * as KEYCODE from '../constants/keycodes';
|
||||
import { isInput } from '../utils/browser';
|
||||
import Patch from './Patch';
|
||||
import EventListener from 'react-event-listener';
|
||||
import Sidebar from '../components/Sidebar';
|
||||
import Inspector from '../components/Inspector';
|
||||
import ProjectBrowser from './ProjectBrowser';
|
||||
|
||||
@@ -74,13 +75,15 @@ class Editor extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<EventListener target={document} onKeyDown={this.onKeyDown} />
|
||||
<ProjectBrowser />
|
||||
<Inspector
|
||||
selection={this.props.selection}
|
||||
nodes={this.props.nodes}
|
||||
nodeTypes={this.props.nodeTypes}
|
||||
onPropUpdate={this.onPropUpdate}
|
||||
/>
|
||||
<Sidebar>
|
||||
<ProjectBrowser />
|
||||
<Inspector
|
||||
selection={this.props.selection}
|
||||
nodes={this.props.nodes}
|
||||
nodeTypes={this.props.nodeTypes}
|
||||
onPropUpdate={this.onPropUpdate}
|
||||
/>
|
||||
</Sidebar>
|
||||
<Patch
|
||||
size={this.patchSize}
|
||||
/>
|
||||
|
||||
@@ -1,27 +1,45 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as Actions from '../actions';
|
||||
// import * as Actions from '../actions';
|
||||
import Selectors from '../selectors';
|
||||
import ProjectBrowserTree from '../components/ProjectBrowserTree';
|
||||
|
||||
|
||||
class ProjectBrowser extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.displayName = 'ProjectBrowser';
|
||||
|
||||
this.onTreeChange = this.onTreeChange.bind(this);
|
||||
}
|
||||
|
||||
onTreeChange(newTree) {
|
||||
console.log('tree changed!', newTree);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>ProjectBrowser</div>;
|
||||
return (
|
||||
<div className="ProjectBrowser">
|
||||
<small className="title">Project browser</small>
|
||||
<ProjectBrowserTree
|
||||
tree={this.props.tree}
|
||||
currentPatchId={this.props.currentPatchId}
|
||||
onChange={this.onTreeChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ProjectBrowser.propTypes = {
|
||||
tree: React.PropTypes.object.isRequired,
|
||||
actions: React.PropTypes.object,
|
||||
patches: React.PropTypes.object,
|
||||
currentPatchId: React.PropTypes.number,
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
tree: Selectors.Project.getTreeView(state),
|
||||
patches: Selectors.Project.getPatches(state),
|
||||
currentPatchId: Selectors.Editor.getCurrentPatchId(state),
|
||||
});
|
||||
|
||||
@@ -596,3 +596,59 @@ export const getLinkGhost = (state) => {
|
||||
to: { x: 0, y: 0 },
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Folders
|
||||
*/
|
||||
export const getFolders = R.pipe(
|
||||
getProject,
|
||||
R.prop('folders')
|
||||
);
|
||||
|
||||
/*
|
||||
Tree view (get / parse)
|
||||
*/
|
||||
export const getTreeView = (state) => {
|
||||
const makeTree = (folders, patches, parentId) => {
|
||||
const foldersAtLevel = R.pipe(
|
||||
R.values,
|
||||
R.filter(R.propEq('parentId', parentId))
|
||||
)(folders);
|
||||
const patchesAtLevel = R.pipe(
|
||||
R.values,
|
||||
R.map(R.prop('present')),
|
||||
R.filter(R.propEq('folderId', parentId))
|
||||
)(patches);
|
||||
|
||||
return R.concat(
|
||||
R.map(
|
||||
folder => ({
|
||||
id: folder.id,
|
||||
module: folder.name,
|
||||
collapsed: true,
|
||||
children: makeTree(folders, patches, folder.id),
|
||||
}),
|
||||
foldersAtLevel
|
||||
),
|
||||
R.map(
|
||||
patch => ({
|
||||
id: patch.id,
|
||||
module: patch.name,
|
||||
leaf: true,
|
||||
}),
|
||||
patchesAtLevel
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const folders = getFolders(state);
|
||||
const patches = getPatches(state);
|
||||
|
||||
return {
|
||||
module: 'Project',
|
||||
collapsed: false,
|
||||
children: makeTree(folders, patches, null),
|
||||
};
|
||||
};
|
||||
|
||||
// export const parseTreeView = (state) => {};
|
||||
|
||||
@@ -78,7 +78,8 @@ const initialState = {
|
||||
},
|
||||
},
|
||||
nodeTypes,
|
||||
counter: {
|
||||
folders: {},
|
||||
counter: {
|
||||
patches: 1,
|
||||
nodes: 1,
|
||||
pins: 1,
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
@mixin inspector-widget() {
|
||||
padding: 4px;
|
||||
margin-bottom: 2px;
|
||||
border-top: 1px solid #ccc;
|
||||
// border-top: 1px solid #ccc;
|
||||
overflow: hidden;
|
||||
|
||||
font-size: $font-size-m;
|
||||
|
||||
@@ -23,3 +23,13 @@ $color-ctrl-background: #2d4c29;
|
||||
$color-ctrl-background-text: #fff;
|
||||
|
||||
$snackbar-width: 200px;
|
||||
$sidebar-width: 200px;
|
||||
|
||||
$sidebar-background: #eee;
|
||||
$sidebar-color: #000;
|
||||
$sidebar-title-color: #999;
|
||||
$sidebar-title-divider: #ccc;
|
||||
$sidebar-selected-background: rgb(128, 160, 190);
|
||||
$sidebar-selected-color: #000;
|
||||
$sidebar-block-shadow: inset 0 5px 15px 1px rgba(0,0,0,.1);
|
||||
$sidebar-block-divider: 1px solid #fff;
|
||||
@@ -1,16 +1,10 @@
|
||||
|
||||
.Inspector {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #eee;
|
||||
color: #000;
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
padding: 26px 20px 4px 8px;
|
||||
color: #999;
|
||||
}
|
||||
background: $sidebar-background;
|
||||
color: $sidebar-color;
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
|
||||
87
app/styles/components/ProjectBrowserTree.scss
Normal file
87
app/styles/components/ProjectBrowserTree.scss
Normal file
@@ -0,0 +1,87 @@
|
||||
.ProjectBrowserTree {
|
||||
width: $sidebar-width;
|
||||
background: $sidebar-background;
|
||||
color: $sidebar-color;
|
||||
padding: 8px 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
.f-no-select {
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.m-tree {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-family: $font-family-normal;
|
||||
font-size: $font-size-m;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.m-draggable {
|
||||
position: absolute;
|
||||
opacity: 0.8;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.m-node {
|
||||
box-sizing: border-box;
|
||||
|
||||
&.placeholder {
|
||||
border: 1px dashed $sidebar-selected-background;
|
||||
|
||||
& > * {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.inner {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding-left: 12px;
|
||||
}
|
||||
.node {
|
||||
display: block;
|
||||
padding: 4px 6px;
|
||||
|
||||
&.is-active {
|
||||
border-left: 100px solid $sidebar-selected-background;
|
||||
border-right: 100px solid $sidebar-selected-background;
|
||||
margin-left: -100px;
|
||||
background: $sidebar-selected-background;
|
||||
color: #006;
|
||||
}
|
||||
}
|
||||
.collapse {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
padding: 2px 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.children {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.caret-down:before {
|
||||
font-size: $font-size-m;
|
||||
content: '\25BE';
|
||||
}
|
||||
.caret-right:before {
|
||||
font-size: $font-size-m;
|
||||
content: '\25B8';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
app/styles/components/Sidebar.scss
Normal file
20
app/styles/components/Sidebar.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
.Sidebar {
|
||||
position: absolute;
|
||||
width: $sidebar-width;
|
||||
height: 100%;
|
||||
|
||||
background: $sidebar-background;
|
||||
|
||||
& > * {
|
||||
box-shadow: $sidebar-block-shadow;
|
||||
border-bottom: $sidebar-block-divider;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
margin: 0 8px;
|
||||
padding: 10px 20px 4px 0;
|
||||
color: $sidebar-title-color;
|
||||
border-bottom: 1px solid $sidebar-title-divider;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
'base/base';
|
||||
|
||||
@import
|
||||
'components/Sidebar',
|
||||
'components/PatchWrapper',
|
||||
'components/PatchSVG',
|
||||
'components/BackgroundLayer',
|
||||
@@ -25,4 +26,5 @@
|
||||
'components/Inspector',
|
||||
'components/SnackBarList',
|
||||
'components/SnackBarError',
|
||||
'components/ProjectBrowserTree',
|
||||
'components/CreateNodeWidget';
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"react-event-listener": "^0.2.1",
|
||||
"react-redux": "^4.0.6",
|
||||
"react-skylight": "^0.4.0",
|
||||
"react-ui-tree": "^2.5.0",
|
||||
"redux": "^3.0.5",
|
||||
"redux-thunk": "^2.1.0",
|
||||
"redux-undo": "^1.0.0-beta8",
|
||||
|
||||
Reference in New Issue
Block a user