Skip to content

Commit

Permalink
chore: Get basic structure for development set up
Browse files Browse the repository at this point in the history
  • Loading branch information
fritz-c committed Oct 18, 2017
0 parents commit 5843280
Show file tree
Hide file tree
Showing 11 changed files with 13,682 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "ie >= 9"]
}
}],
"react"
],
"plugins": [
"transform-object-rest-spread",
"react-hot-loader/babel"
]
}
11 changes: 11 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": ["airbnb", "prettier", "prettier/react"],
"env": {
"browser": true,
"jest": true
},
"rules": {
"react/jsx-filename-extension": 0,
"react/prefer-stateless-function": 0
}
}
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
node_modules
npm-debug.log
yarn.lock
coverage
cc-test-reporter

# Editor and other tmp files
*.swp
*.un~
*.iml
*.ipr
*.iws
*.sublime-*
.idea/
*.DS_Store

# Build directories (Will be preserved by npm)
dist
build
297 changes: 297 additions & 0 deletions demo/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
import React, { Component } from 'react';
import SortableTree, { toggleExpandedForAll } from 'react-sortable-tree';
import CustomNodeRenderer from '../index';

const maxDepth = 5;

class App extends Component {
constructor(props) {
super(props);

const renderDepthTitle = ({ path }) => `Depth: ${path.length}`;

this.state = {
searchString: '',
searchFocusIndex: 0,
searchFoundCount: null,
treeData: [
{
title: '`title`',
subtitle: '`subtitle`',
expanded: true,
children: [
{
title: 'Child Node',
subtitle: 'Defined in `children` array belonging to parent',
},
{
title: 'Nested structure is rendered virtually',
subtitle: (
<span>
The tree uses&nbsp;
<a href="https://github.com/bvaughn/react-virtualized">
react-virtualized
</a>
&nbsp;and the relationship lines are more of a visual trick.
</span>
),
},
],
},
{
expanded: true,
title: 'Any node can be the parent or child of any other node',
children: [
{
expanded: true,
title: 'Chicken',
children: [{ title: 'Egg' }],
},
],
},
{
title: 'Button(s) can be added to the node',
subtitle:
'Node info is passed when generating so you can use it in your onClick handler',
},
{
title: 'Show node children by setting `expanded`',
subtitle: ({ node }) =>
`expanded: ${node.expanded ? 'true' : 'false'}`,
children: [
{
title: 'Bruce',
subtitle: ({ node }) =>
`expanded: ${node.expanded ? 'true' : 'false'}`,
children: [{ title: 'Bruce Jr.' }, { title: 'Brucette' }],
},
],
},
{
title: 'Advanced',
subtitle: 'Settings, behavior, etc.',
children: [
{
title: (
<div>
<div
style={{
backgroundColor: 'gray',
display: 'inline-block',
borderRadius: 10,
color: '#FFF',
padding: '0 5px',
}}
>
Any Component
</div>
&nbsp;can be used for `title`
</div>
),
},
{
expanded: true,
title: 'Limit nesting with `maxDepth`',
subtitle: `It's set to ${maxDepth} for this example`,
children: [
{
expanded: true,
title: renderDepthTitle,
children: [
{
expanded: true,
title: renderDepthTitle,
children: [
{ title: renderDepthTitle },
{
title: ({ path }) =>
path.length >= maxDepth
? 'This cannot be dragged deeper'
: 'This can be dragged deeper',
},
],
},
],
},
],
},
{
title:
'Disable dragging on a per-node basis with the `canDrag` prop',
subtitle: 'Or set it to false to disable all dragging.',
noDragging: true,
},
{
title: 'You cannot give this children',
subtitle:
'Dropping is prevented via the `canDrop` API using `nextParent`',
noChildren: true,
},
{
title:
'When node contents are really long, it will cause a horizontal scrollbar' +
' to appear. Deeply nested elements will also trigger the scrollbar.',
},
],
},
],
};

this.updateTreeData = this.updateTreeData.bind(this);
this.expandAll = this.expandAll.bind(this);
this.collapseAll = this.collapseAll.bind(this);
}

updateTreeData(treeData) {
this.setState({ treeData });
}

expand(expanded) {
this.setState({
treeData: toggleExpandedForAll({
treeData: this.state.treeData,
expanded,
}),
});
}

expandAll() {
this.expand(true);
}

collapseAll() {
this.expand(false);
}

render() {
const {
treeData,
searchString,
searchFocusIndex,
searchFoundCount,
} = this.state;

const alertNodeInfo = ({ node, path, treeIndex }) => {
const objectString = Object.keys(node)
.map(k => (k === 'children' ? 'children: Array' : `${k}: '${node[k]}'`))
.join(',\n ');

global.alert(
'Info passed to the button generator:\n\n' +
`node: {\n ${objectString}\n},\n` +
`path: [${path.join(', ')}],\n` +
`treeIndex: ${treeIndex}`
);
};

const selectPrevMatch = () =>
this.setState({
searchFocusIndex:
searchFocusIndex !== null
? (searchFoundCount + searchFocusIndex - 1) % searchFoundCount
: searchFoundCount - 1,
});

const selectNextMatch = () =>
this.setState({
searchFocusIndex:
searchFocusIndex !== null
? (searchFocusIndex + 1) % searchFoundCount
: 0,
});

const isVirtualized = true;
const treeContainerStyle = isVirtualized ? { height: 450 } : {};

return (
<div>
<h3>Demo</h3>
<button onClick={this.expandAll}>Expand All</button>
<button onClick={this.collapseAll}>Collapse All</button>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<form
style={{ display: 'inline-block' }}
onSubmit={event => {
event.preventDefault();
}}
>
<label htmlFor="find-box">
Search:&nbsp;
<input
id="find-box"
type="text"
value={searchString}
onChange={event =>
this.setState({ searchString: event.target.value })}
/>
</label>

<button
type="button"
disabled={!searchFoundCount}
onClick={selectPrevMatch}
>
&lt;
</button>

<button
type="submit"
disabled={!searchFoundCount}
onClick={selectNextMatch}
>
&gt;
</button>

<span>
&nbsp;
{searchFoundCount > 0 ? searchFocusIndex + 1 : 0}
&nbsp;/&nbsp;
{searchFoundCount || 0}
</span>
</form>
<div style={treeContainerStyle}>
<SortableTree
treeData={treeData}
onChange={this.updateTreeData}
onMoveNode={({ node, treeIndex, path }) =>
global.console.debug(
'node:',
node,
'treeIndex:',
treeIndex,
'path:',
path
)}
maxDepth={maxDepth}
searchQuery={searchString}
searchFocusOffset={searchFocusIndex}
canDrag={({ node }) => !node.noDragging}
canDrop={({ nextParent }) => !nextParent || !nextParent.noChildren}
searchFinishCallback={matches =>
this.setState({
searchFoundCount: matches.length,
searchFocusIndex:
matches.length > 0 ? searchFocusIndex % matches.length : 0,
})}
isVirtualized={isVirtualized}
generateNodeProps={rowInfo => ({
buttons: [
<button
style={{
verticalAlign: 'middle',
}}
onClick={() => alertNodeInfo(rowInfo)}
>
</button>,
],
})}
nodeContentRenderer={CustomNodeRenderer}
/>
</div>
</div>
);
}
}

export default App;
17 changes: 17 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="UTF-8">
<title>React Sortable Tree</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" sizes="180x180" href="static/apple-touch-icon.png">
<link rel="icon" type="image/png" href="static/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="static/favicon-16x16.png" sizes="16x16">
<link rel="mask-icon" href="static/safari-pinned-tab.svg" color="#de7a32">
<link rel="shortcut icon" href="static/favicon.ico">
<meta name="theme-color" content="#ffffff">
</head>
<body>
<div id="app"></div>
</body>
</html>
19 changes: 19 additions & 0 deletions demo/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader'; // eslint-disable-line import/no-extraneous-dependencies

const rootEl = document.getElementById('app');
const render = Component => {
ReactDOM.render(
<AppContainer>
<Component />
</AppContainer>,
rootEl
);
};

/* eslint-disable global-require, import/newline-after-import */
render(require('./app').default);
if (module.hot)
module.hot.accept('./app', () => render(require('./app').default));
/* eslint-enable global-require, import/newline-after-import */
Loading

0 comments on commit 5843280

Please sign in to comment.