react-checkbox-tree
Advanced tools
Comparing version 0.2.0 to 0.3.0-rc.0
@@ -1,44 +0,45 @@ | ||
var gulp = require('gulp'); | ||
var eslint = require('gulp-eslint'); | ||
var mocha = require('gulp-mocha-phantomjs'); | ||
var header = require('gulp-header'); | ||
var webpack = require('webpack-stream'); | ||
var scsslint = require('gulp-scss-lint'); | ||
var sass = require('gulp-sass'); | ||
var pkg = require('./package.json'); | ||
const gulp = require('gulp'); | ||
const eslint = require('gulp-eslint'); | ||
const mocha = require('gulp-mocha-phantomjs'); | ||
const header = require('gulp-header'); | ||
const webpack = require('webpack-stream'); | ||
const scsslint = require('gulp-scss-lint'); | ||
const sass = require('gulp-sass'); | ||
const pkg = require('./package.json'); | ||
var banner = '/*! <%= pkg.name %> - v<%= pkg.version %> | <%= new Date().getFullYear() %> */\n'; | ||
const webpackConfig = require('./webpack.config'); | ||
const testWebpackConfig = require('./webpack.test.config'); | ||
gulp.task('test-script-format', function () { | ||
return gulp.src(['./src/js/**/*.js']) | ||
const banner = '/*! <%= pkg.name %> - v<%= pkg.version %> | <%= new Date().getFullYear() %> */\n'; | ||
gulp.task('test-script-format', () => | ||
gulp.src(['./src/js/**/*.js']) | ||
.pipe(eslint()) | ||
.pipe(eslint.format()) | ||
.pipe(eslint.failOnError()); | ||
}); | ||
.pipe(eslint.failOnError()) | ||
); | ||
gulp.task('compile-test-script', function () { | ||
return gulp.src(['./test/index.js']) | ||
gulp.task('compile-test-script', () => | ||
gulp.src(['./test/index.js']) | ||
.pipe(webpack(require('./webpack.config.js'))) | ||
.pipe(gulp.dest('./test/compiled/')); | ||
}); | ||
.pipe(gulp.dest('./test/compiled/')) | ||
); | ||
// Disabled for now | ||
gulp.task('test-mocha', ['script-compile-test'], function () { | ||
return gulp.src(['test/test.html']) | ||
.pipe(mocha({ reporter: 'spec' })); | ||
}); | ||
gulp.task('test-mocha', ['script-compile-test'], () => | ||
gulp.src(['test/test.html']) | ||
.pipe(mocha({ reporter: 'spec' })) | ||
); | ||
gulp.task('test-script', ['test-script-format']); | ||
gulp.task('build-script', ['test-script'], function () { | ||
return gulp.src(['./src/index.js']) | ||
.pipe(webpack(require('./webpack.config.js'))) | ||
.pipe(header(banner, { | ||
pkg: pkg, | ||
})) | ||
.pipe(gulp.dest('./lib/')); | ||
}); | ||
gulp.task('build-script', ['test-script'], () => | ||
gulp.src(['./src/index.js']) | ||
.pipe(webpack(webpackConfig)) | ||
.pipe(header(banner, { pkg })) | ||
.pipe(gulp.dest('./lib/')) | ||
); | ||
gulp.task('build-style', function () { | ||
return gulp.src('./src/sass/**/*.scss') | ||
gulp.task('build-style', () => | ||
gulp.src('./src/sass/**/*.scss') | ||
.pipe(scsslint()) | ||
@@ -49,12 +50,12 @@ .pipe(scsslint.failReporter()) | ||
}).on('error', sass.logError)) | ||
.pipe(gulp.dest('./lib')); | ||
}); | ||
.pipe(gulp.dest('./lib')) | ||
); | ||
gulp.task('build-examples', ['build-script', 'build-style'], function () { | ||
return gulp.src(['./examples/index.js']) | ||
.pipe(webpack(require('./webpack.test.config.js'))) | ||
.pipe(gulp.dest('./examples/compiled/')); | ||
}); | ||
gulp.task('build-examples', ['build-script', 'build-style'], () => | ||
gulp.src(['./examples/index.js']) | ||
.pipe(webpack(testWebpackConfig)) | ||
.pipe(gulp.dest('./examples/compiled/')) | ||
); | ||
gulp.task('watch', function () { | ||
gulp.task('watch', () => { | ||
gulp.watch(['./src/js/**/*.js'], ['build-script']); | ||
@@ -61,0 +62,0 @@ gulp.watch(['./src/sass/**/*.scss'], ['build-style']); |
331
lib/index.js
@@ -1,2 +0,2 @@ | ||
/*! react-checkbox-tree - v0.2.0 | 2016 */ | ||
/*! react-checkbox-tree - v0.3.0-rc.0 | 2016 */ | ||
(function webpackUniversalModuleDefinition(root, factory) { | ||
@@ -77,3 +77,3 @@ if(typeof exports === 'object' && typeof module === 'object') | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
@@ -104,10 +104,6 @@ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Tree).call(this, props)); | ||
var _this = _possibleConstructorReturn(this, (Tree.__proto__ || Object.getPrototypeOf(Tree)).call(this, props)); | ||
_this.state = { | ||
checked: props.checked, | ||
expanded: props.expanded | ||
}; | ||
_this.handleCheck = _this.handleCheck.bind(_this); | ||
_this.onCheck = _this.onCheck.bind(_this); | ||
_this.onExpand = _this.onExpand.bind(_this); | ||
return _this; | ||
@@ -117,2 +113,30 @@ } | ||
_createClass(Tree, [{ | ||
key: 'onCheck', | ||
value: function onCheck(node) { | ||
var checked = this.props.checked; | ||
var isChecked = node.checked; | ||
this.setCheckState(checked, node, isChecked); | ||
this.props.onCheck(checked); | ||
} | ||
}, { | ||
key: 'onExpand', | ||
value: function onExpand(node) { | ||
var isExpanded = node.expanded; | ||
var expanded = this.props.expanded; | ||
var nodeIndex = expanded.indexOf(node.value); | ||
if (!isExpanded && nodeIndex > -1) { | ||
// Node is now collapsed, remove from expanded list | ||
expanded.splice(nodeIndex, 1); | ||
} else if (isExpanded && nodeIndex === -1) { | ||
// Add node to expanded list | ||
expanded.push(node.value); | ||
} | ||
this.props.onExpand(expanded); | ||
} | ||
}, { | ||
key: 'getFormattedNodes', | ||
@@ -122,7 +146,12 @@ value: function getFormattedNodes(nodes) { | ||
var _props = this.props; | ||
var checked = _props.checked; | ||
var expanded = _props.expanded; | ||
return nodes.map(function (node) { | ||
var formatted = Object.create(node); | ||
formatted.checked = _this2.state.checked.indexOf(node.value) > -1; | ||
formatted.collapsed = _this2.state.expanded.indexOf(node.value) === -1; | ||
formatted.checked = checked.indexOf(node.value) > -1; | ||
formatted.expanded = expanded.indexOf(node.value) > -1; | ||
@@ -137,34 +166,2 @@ if (_this2.hasChildren(node)) { | ||
}, { | ||
key: 'getTreeNodes', | ||
value: function getTreeNodes(nodes) { | ||
var _this3 = this; | ||
var treeNodes = nodes.map(function (node, index) { | ||
var checked = _this3.getCheckState(node); | ||
var children = _this3.getChildNodes(node); | ||
return _react2.default.createElement( | ||
_TreeNode2.default, | ||
{ | ||
key: index, | ||
name: _this3.props.name, | ||
nameAsArray: _this3.props.nameAsArray, | ||
value: node.value, | ||
title: node.title, | ||
checked: checked, | ||
collapsed: node.collapsed, | ||
rawChildren: node.children, | ||
onCheck: _this3.handleCheck | ||
}, | ||
children | ||
); | ||
}); | ||
return _react2.default.createElement( | ||
'ol', | ||
null, | ||
treeNodes | ||
); | ||
} | ||
}, { | ||
key: 'getCheckState', | ||
@@ -187,25 +184,5 @@ value: function getCheckState(node) { | ||
}, { | ||
key: 'getChildNodes', | ||
value: function getChildNodes(node) { | ||
if (this.hasChildren(node)) { | ||
return this.getTreeNodes(node.children); | ||
} | ||
return null; | ||
} | ||
}, { | ||
key: 'getHiddenInput', | ||
value: function getHiddenInput() { | ||
if (this.props.name === undefined || this.props.nameAsArray) { | ||
return null; | ||
} | ||
var checked = this.state.checked.join(','); | ||
return _react2.default.createElement('input', { name: this.props.name, value: checked, type: 'hidden' }); | ||
} | ||
}, { | ||
key: 'setCheckState', | ||
value: function setCheckState(node, isChecked) { | ||
var _this4 = this; | ||
value: function setCheckState(checked, node, isChecked) { | ||
var _this3 = this; | ||
@@ -215,14 +192,14 @@ if (this.hasChildren(node)) { | ||
node.children.forEach(function (child) { | ||
_this4.setCheckState(child, isChecked); | ||
_this3.setCheckState(checked, child, isChecked); | ||
}); | ||
} else { | ||
// Set leaf to check/unchecked state | ||
var index = this.state.checked.indexOf(node.value); | ||
var index = checked.indexOf(node.value); | ||
if (index > -1) { | ||
this.state.checked.splice(index, 1); | ||
checked.splice(index, 1); | ||
} | ||
if (isChecked) { | ||
this.state.checked.push(node.value); | ||
checked.push(node.value); | ||
} | ||
@@ -234,7 +211,7 @@ } | ||
value: function isEveryChildChecked(node) { | ||
var _this5 = this; | ||
var _this4 = this; | ||
return node.children.every(function (child) { | ||
if (_this5.hasChildren(child)) { | ||
return _this5.isEveryChildChecked(child); | ||
if (_this4.hasChildren(child)) { | ||
return _this4.isEveryChildChecked(child); | ||
} | ||
@@ -248,7 +225,7 @@ | ||
value: function isSomeChildChecked(node) { | ||
var _this6 = this; | ||
var _this5 = this; | ||
return node.children.some(function (child) { | ||
if (_this6.hasChildren(child)) { | ||
return _this6.isSomeChildChecked(child); | ||
if (_this5.hasChildren(child)) { | ||
return _this5.isSomeChildChecked(child); | ||
} | ||
@@ -269,17 +246,77 @@ | ||
}, { | ||
key: 'handleCheck', | ||
value: function handleCheck(node) { | ||
var isChecked = node.checked; | ||
key: 'renderTreeNodes', | ||
value: function renderTreeNodes(nodes) { | ||
var _this6 = this; | ||
this.setCheckState(node, isChecked); | ||
var treeNodes = nodes.map(function (node, index) { | ||
var checked = _this6.getCheckState(node); | ||
var children = _this6.renderChildNodes(node); | ||
this.setState({ | ||
checked: this.state.checked | ||
return _react2.default.createElement( | ||
_TreeNode2.default, | ||
{ | ||
key: index, | ||
value: node.value, | ||
title: node.title, | ||
checked: checked, | ||
expanded: node.expanded, | ||
rawChildren: node.children, | ||
onCheck: _this6.onCheck, | ||
onExpand: _this6.onExpand | ||
}, | ||
children | ||
); | ||
}); | ||
return _react2.default.createElement( | ||
'ol', | ||
null, | ||
treeNodes | ||
); | ||
} | ||
}, { | ||
key: 'renderChildNodes', | ||
value: function renderChildNodes(node) { | ||
if (this.hasChildren(node)) { | ||
return this.renderTreeNodes(node.children); | ||
} | ||
return null; | ||
} | ||
}, { | ||
key: 'renderHiddenInput', | ||
value: function renderHiddenInput() { | ||
if (this.props.name === undefined) { | ||
return null; | ||
} | ||
if (this.props.nameAsArray) { | ||
return this.renderArrayHiddenInput(); | ||
} | ||
return this.renderJoinedHiddenInput(); | ||
} | ||
}, { | ||
key: 'renderArrayHiddenInput', | ||
value: function renderArrayHiddenInput() { | ||
var _this7 = this; | ||
return this.props.checked.map(function (value, index) { | ||
var name = _this7.props.name + '[]'; | ||
return _react2.default.createElement('input', { key: index, name: name, type: 'hidden', value: value }); | ||
}); | ||
} | ||
}, { | ||
key: 'renderJoinedHiddenInput', | ||
value: function renderJoinedHiddenInput() { | ||
var checked = this.props.checked.join(','); | ||
return _react2.default.createElement('input', { name: this.props.name, value: checked, type: 'hidden' }); | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var nodes = this.getFormattedNodes(this.props.nodes); | ||
var treeNodes = this.getTreeNodes(nodes); | ||
var treeNodes = this.renderTreeNodes(nodes); | ||
@@ -289,3 +326,3 @@ return _react2.default.createElement( | ||
{ className: 'react-checkbox-tree' }, | ||
this.getHiddenInput(), | ||
this.renderHiddenInput(), | ||
treeNodes | ||
@@ -304,8 +341,6 @@ ); | ||
checked: _react2.default.PropTypes.array, | ||
expanded: _react2.default.PropTypes.array | ||
expanded: _react2.default.PropTypes.array, | ||
onCheck: _react2.default.PropTypes.func, | ||
onExpand: _react2.default.PropTypes.func | ||
}; | ||
Tree.defaultProps = { | ||
checked: [], | ||
expanded: [] | ||
}; | ||
exports.default = Tree; | ||
@@ -349,10 +384,6 @@ | ||
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(TreeNode).call(this, props)); | ||
var _this = _possibleConstructorReturn(this, (TreeNode.__proto__ || Object.getPrototypeOf(TreeNode)).call(this, props)); | ||
_this.state = { | ||
collapsed: props.collapsed | ||
}; | ||
_this.handleCollapseClick = _this.handleCollapseClick.bind(_this); | ||
_this.handleCheckboxClick = _this.handleCheckboxClick.bind(_this); | ||
_this.onCheck = _this.onCheck.bind(_this); | ||
_this.onExpand = _this.onExpand.bind(_this); | ||
return _this; | ||
@@ -362,4 +393,28 @@ } | ||
_createClass(TreeNode, [{ | ||
key: "getCollapseIcon", | ||
value: function getCollapseIcon() { | ||
key: "onCheck", | ||
value: function onCheck() { | ||
var isChecked = 0; | ||
// Toggle off/partial check state to checked | ||
if (this.props.checked === 0 || this.props.checked === 2) { | ||
isChecked = 1; | ||
} | ||
this.props.onCheck({ | ||
value: this.props.value, | ||
checked: isChecked, | ||
children: this.props.rawChildren | ||
}); | ||
} | ||
}, { | ||
key: "onExpand", | ||
value: function onExpand() { | ||
this.props.onExpand({ | ||
value: this.props.value, | ||
expanded: !this.props.expanded | ||
}); | ||
} | ||
}, { | ||
key: "renderCollapseIcon", | ||
value: function renderCollapseIcon() { | ||
if (this.props.children === null) { | ||
@@ -369,3 +424,3 @@ return _react2.default.createElement("i", { className: "fa" }); | ||
if (this.state.collapsed) { | ||
if (!this.props.expanded) { | ||
return _react2.default.createElement("i", { className: "fa fa-chevron-right" }); | ||
@@ -377,4 +432,4 @@ } | ||
}, { | ||
key: "getCheckboxIcon", | ||
value: function getCheckboxIcon() { | ||
key: "renderCheckboxIcon", | ||
value: function renderCheckboxIcon() { | ||
if (this.props.checked === 0) { | ||
@@ -391,6 +446,6 @@ return _react2.default.createElement("i", { className: "fa fa-square-o" }); | ||
}, { | ||
key: "getNodeIcon", | ||
value: function getNodeIcon() { | ||
key: "renderNodeIcon", | ||
value: function renderNodeIcon() { | ||
if (this.props.children !== null) { | ||
if (this.state.collapsed) { | ||
if (!this.props.expanded) { | ||
return _react2.default.createElement("i", { className: "fa fa-folder-o" }); | ||
@@ -405,49 +460,11 @@ } | ||
}, { | ||
key: "getHiddenInput", | ||
value: function getHiddenInput() { | ||
if (this.props.name === undefined || !this.props.nameAsArray) { | ||
key: "renderChildren", | ||
value: function renderChildren() { | ||
if (!this.props.expanded) { | ||
return null; | ||
} | ||
var name = this.props.name + "[]"; | ||
if (this.props.checked === 1) { | ||
return _react2.default.createElement("input", { name: name, value: this.props.value, type: "hidden" }); | ||
} | ||
return null; | ||
} | ||
}, { | ||
key: "getChildren", | ||
value: function getChildren() { | ||
if (this.state.collapsed) { | ||
return null; | ||
} | ||
return this.props.children; | ||
} | ||
}, { | ||
key: "handleCollapseClick", | ||
value: function handleCollapseClick() { | ||
this.setState({ | ||
collapsed: !this.state.collapsed | ||
}); | ||
} | ||
}, { | ||
key: "handleCheckboxClick", | ||
value: function handleCheckboxClick() { | ||
var isChecked = 0; | ||
// Toggle off/partial check state to checked | ||
if (this.props.checked === 0 || this.props.checked === 2) { | ||
isChecked = 1; | ||
} | ||
this.props.onCheck({ | ||
value: this.props.value, | ||
checked: isChecked, | ||
children: this.props.rawChildren | ||
}); | ||
} | ||
}, { | ||
key: "render", | ||
@@ -463,12 +480,12 @@ value: function render() { | ||
"span", | ||
{ className: "rct-collapse", onClick: this.handleCollapseClick, title: "toggle" }, | ||
this.getCollapseIcon() | ||
{ className: "rct-collapse", onClick: this.onExpand, title: "Toggle" }, | ||
this.renderCollapseIcon() | ||
), | ||
_react2.default.createElement( | ||
"label", | ||
{ onClick: this.handleCheckboxClick }, | ||
{ onClick: this.onCheck }, | ||
_react2.default.createElement( | ||
"span", | ||
{ className: "rct-checkbox" }, | ||
this.getCheckboxIcon() | ||
this.renderCheckboxIcon() | ||
), | ||
@@ -478,3 +495,3 @@ _react2.default.createElement( | ||
{ className: "rct-icon" }, | ||
this.getNodeIcon() | ||
this.renderNodeIcon() | ||
), | ||
@@ -485,7 +502,6 @@ _react2.default.createElement( | ||
this.props.title | ||
), | ||
this.getHiddenInput() | ||
) | ||
) | ||
), | ||
this.getChildren() | ||
this.renderChildren() | ||
); | ||
@@ -499,11 +515,10 @@ } | ||
TreeNode.propTypes = { | ||
name: _react2.default.PropTypes.string, | ||
nameAsArray: _react2.default.PropTypes.bool, | ||
value: _react2.default.PropTypes.string, | ||
title: _react2.default.PropTypes.string, | ||
children: _react2.default.PropTypes.node, | ||
checked: _react2.default.PropTypes.number, | ||
collapsed: _react2.default.PropTypes.bool, | ||
expanded: _react2.default.PropTypes.bool, | ||
rawChildren: _react2.default.PropTypes.any, | ||
onCheck: _react2.default.PropTypes.func | ||
onCheck: _react2.default.PropTypes.func, | ||
onExpand: _react2.default.PropTypes.func, | ||
title: _react2.default.PropTypes.string, | ||
value: _react2.default.PropTypes.string | ||
}; | ||
@@ -510,0 +525,0 @@ exports.default = TreeNode; |
{ | ||
"name": "react-checkbox-tree", | ||
"version": "0.2.0", | ||
"version": "0.3.0-rc.0", | ||
"description": "React component for checkbox trees.", | ||
"author": "Jake Zatecky", | ||
"license": "MIT", | ||
"keywords": [ | ||
"react", | ||
"checkbox", | ||
"tree" | ||
], | ||
"repository": { | ||
@@ -17,29 +22,30 @@ "type": "git", | ||
"peerDependencies": { | ||
"react": "^15.0.0" | ||
"react": "^15.3.0" | ||
}, | ||
"devDependencies": { | ||
"babel-core": "^6.4.5", | ||
"babel-eslint": "^6.0.3", | ||
"babel-loader": "^6.2.2", | ||
"babel-preset-es2015": "^6.3.13", | ||
"babel-preset-react": "^6.3.13", | ||
"babel-preset-stage-0": "^6.3.13", | ||
"babel-core": "^6.13.2", | ||
"babel-eslint": "^7.0.0", | ||
"babel-loader": "^6.2.4", | ||
"babel-preset-es2015": "^6.13.2", | ||
"babel-preset-react": "^6.11.1", | ||
"babel-preset-stage-0": "^6.5.0", | ||
"chai": "^3.5.0", | ||
"eslint": "^2.2.0", | ||
"eslint-config-airbnb": "^7.0.0", | ||
"eslint-plugin-react": "^5.0.1", | ||
"eslint-plugin-jsx-a11y": "^0.6.2", | ||
"gulp": "^3.9.0", | ||
"gulp-eslint": "^2.0.0", | ||
"gulp-header": "^1.7.1", | ||
"eslint": "^3.2.2", | ||
"eslint-config-airbnb": "^12.0.0", | ||
"eslint-plugin-import": "^1.13.0", | ||
"eslint-plugin-jsx-a11y": "^2.1.0", | ||
"eslint-plugin-react": "^6.0.0", | ||
"gulp": "^3.9.1", | ||
"gulp-eslint": "^3.0.1", | ||
"gulp-header": "^1.8.8", | ||
"gulp-mocha-phantomjs": "^0.11.0", | ||
"gulp-sass": "^2.1.1", | ||
"gulp-scss-lint": "^0.3.9", | ||
"gulp-sass": "^2.3.2", | ||
"gulp-scss-lint": "^0.4.0", | ||
"http-server": "^0.9.0", | ||
"mocha": "^2.4.5", | ||
"react": "^15.0.0", | ||
"react-dom": "^15.0.1", | ||
"webpack": "^1.12.12", | ||
"webpack-stream": "^3.1.0" | ||
"mocha": "^3.0.2", | ||
"react": "^15.3.0", | ||
"react-dom": "^15.3.0", | ||
"webpack": "^1.13.1", | ||
"webpack-stream": "^3.2.0" | ||
} | ||
} |
@@ -8,34 +8,82 @@ # react-checkbox-tree | ||
Checkbox treeview for React. | ||
> Checkbox treeview for React. | ||
# Usage | ||
![Demo](demo.gif) | ||
Install the library: | ||
``` shell | ||
## Installation | ||
The easiest way to use react-checkbox-tree is to install from NPM and include it in your own React build process (e.g. using [Webpack](http://webpack.github.io/docs/what-is-webpack.html)): | ||
``` | ||
npm install react-checkbox-tree --save | ||
``` | ||
Then render the component: | ||
## Usage | ||
A quick usage example is included below. Note that the react-checkbox-tree component is [controlled](https://facebook.github.io/react/docs/forms.html#controlled-components). In other words, it is stateless. You must update its `checked` and `expanded` properties whenever a change occurs. | ||
``` javascript | ||
import CheckboxTree from 'react-checkbox-tree'; | ||
... | ||
const nodes = [{ | ||
value: 'node-1', | ||
title: 'Parent Node 1', | ||
children: [{ | ||
value: 'node-1-1', | ||
title: 'Leaf Node 1-1', | ||
}, { | ||
value: 'node-1-2', | ||
title: 'Leaf Node 1-2' | ||
}], | ||
}]; | ||
render() { | ||
const nodes = [{ | ||
value: 'node-1', | ||
title: 'Parent Node 1', | ||
children: [{ | ||
value: 'node-1-1', | ||
title: 'Leaf Node 1-1', | ||
}, { | ||
value: 'node-1-2', | ||
title: 'Leaf Node 1-2' | ||
}], | ||
}]; | ||
class Widget extends React.Component { | ||
constructor() { | ||
super(); | ||
return <CheckboxTree node={nodes} />; | ||
this.state = { | ||
checked: [], | ||
expanded: [], | ||
}; | ||
this.onCheck = this.onCheck.bind(this); | ||
this.onExpand = this.onExpand.bind(this); | ||
} | ||
onCheck(checked) { | ||
this.setState({ checked }); | ||
} | ||
onExpand(expanded) { | ||
this.setState({ expanded }); | ||
} | ||
render() { | ||
const { checked, expanded } = this.state; | ||
return ( | ||
<Tree | ||
name="tree_nodes" | ||
nodes={nodes} | ||
checked={checked} | ||
expanded={expanded} | ||
onCheck={this.onCheck} | ||
onExpand={this.onExpand} | ||
/> | ||
); | ||
} | ||
} | ||
``` | ||
### Properties | ||
| Property | Type | Description | | ||
| ------------- | -------- | --------------------------------------------------------------------------------------------- | | ||
| `nodes` | array | **Required**. Specifies the tree nodes and their children. | | ||
| `checked` | array | **Required**. An array of checked node values. | | ||
| `expanded` | array | **Required**. An array of expanded node values. | | ||
| `onCheck` | function | onCheck handler: `function(checked) {}` | | ||
| `onExpand` | function | onExpand handler: `function(expanded) {}` | | ||
| `name` | string | Optional name for the hidden `<input>` element. | | ||
| `nameAsArray` | bool | If true, the hidden `<input>` will encode its values as an array rather than a joined string. | |
@@ -6,2 +6,2 @@ // Use CommonJS export to trick Webpack into working around the issues that | ||
module.exports = require('./js/Tree.js').default; | ||
module.exports = require('./js/Tree').default; |
import React from 'react'; | ||
import TreeNode from './TreeNode.js'; | ||
import TreeNode from './TreeNode'; | ||
@@ -12,26 +12,46 @@ class Tree extends React.Component { | ||
expanded: React.PropTypes.array, | ||
onCheck: React.PropTypes.func, | ||
onExpand: React.PropTypes.func, | ||
}; | ||
static defaultProps = { | ||
checked: [], | ||
expanded: [], | ||
}; | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
checked: props.checked, | ||
expanded: props.expanded, | ||
}; | ||
this.onCheck = this.onCheck.bind(this); | ||
this.onExpand = this.onExpand.bind(this); | ||
} | ||
this.handleCheck = this.handleCheck.bind(this); | ||
onCheck(node) { | ||
const { checked } = this.props; | ||
const isChecked = node.checked; | ||
this.setCheckState(checked, node, isChecked); | ||
this.props.onCheck(checked); | ||
} | ||
onExpand(node) { | ||
const isExpanded = node.expanded; | ||
const expanded = this.props.expanded; | ||
const nodeIndex = expanded.indexOf(node.value); | ||
if (!isExpanded && nodeIndex > -1) { | ||
// Node is now collapsed, remove from expanded list | ||
expanded.splice(nodeIndex, 1); | ||
} else if (isExpanded && nodeIndex === -1) { | ||
// Add node to expanded list | ||
expanded.push(node.value); | ||
} | ||
this.props.onExpand(expanded); | ||
} | ||
getFormattedNodes(nodes) { | ||
const { checked, expanded } = this.props; | ||
return nodes.map((node) => { | ||
const formatted = Object.create(node); | ||
formatted.checked = this.state.checked.indexOf(node.value) > -1; | ||
formatted.collapsed = this.state.expanded.indexOf(node.value) === -1; | ||
formatted.checked = checked.indexOf(node.value) > -1; | ||
formatted.expanded = expanded.indexOf(node.value) > -1; | ||
@@ -46,31 +66,2 @@ if (this.hasChildren(node)) { | ||
getTreeNodes(nodes) { | ||
const treeNodes = nodes.map((node, index) => { | ||
const checked = this.getCheckState(node); | ||
const children = this.getChildNodes(node); | ||
return ( | ||
<TreeNode | ||
key={index} | ||
name={this.props.name} | ||
nameAsArray={this.props.nameAsArray} | ||
value={node.value} | ||
title={node.title} | ||
checked={checked} | ||
collapsed={node.collapsed} | ||
rawChildren={node.children} | ||
onCheck={this.handleCheck} | ||
> | ||
{children} | ||
</TreeNode> | ||
); | ||
}); | ||
return ( | ||
<ol> | ||
{treeNodes} | ||
</ol> | ||
); | ||
} | ||
getCheckState(node) { | ||
@@ -92,36 +83,18 @@ if (this.hasChildren(node) === false) { | ||
getChildNodes(node) { | ||
setCheckState(checked, node, isChecked) { | ||
if (this.hasChildren(node)) { | ||
return this.getTreeNodes(node.children); | ||
} | ||
return null; | ||
} | ||
getHiddenInput() { | ||
if (this.props.name === undefined || this.props.nameAsArray) { | ||
return null; | ||
} | ||
const checked = this.state.checked.join(','); | ||
return <input name={this.props.name} value={checked} type="hidden" />; | ||
} | ||
setCheckState(node, isChecked) { | ||
if (this.hasChildren(node)) { | ||
// Percolate check status down to all children | ||
node.children.forEach((child) => { | ||
this.setCheckState(child, isChecked); | ||
this.setCheckState(checked, child, isChecked); | ||
}); | ||
} else { | ||
// Set leaf to check/unchecked state | ||
const index = this.state.checked.indexOf(node.value); | ||
const index = checked.indexOf(node.value); | ||
if (index > -1) { | ||
this.state.checked.splice(index, 1); | ||
checked.splice(index, 1); | ||
} | ||
if (isChecked) { | ||
this.state.checked.push(node.value); | ||
checked.push(node.value); | ||
} | ||
@@ -159,19 +132,71 @@ } | ||
handleCheck(node) { | ||
const isChecked = node.checked; | ||
renderTreeNodes(nodes) { | ||
const treeNodes = nodes.map((node, index) => { | ||
const checked = this.getCheckState(node); | ||
const children = this.renderChildNodes(node); | ||
this.setCheckState(node, isChecked); | ||
return ( | ||
<TreeNode | ||
key={index} | ||
value={node.value} | ||
title={node.title} | ||
checked={checked} | ||
expanded={node.expanded} | ||
rawChildren={node.children} | ||
onCheck={this.onCheck} | ||
onExpand={this.onExpand} | ||
> | ||
{children} | ||
</TreeNode> | ||
); | ||
}); | ||
this.setState({ | ||
checked: this.state.checked, | ||
return ( | ||
<ol> | ||
{treeNodes} | ||
</ol> | ||
); | ||
} | ||
renderChildNodes(node) { | ||
if (this.hasChildren(node)) { | ||
return this.renderTreeNodes(node.children); | ||
} | ||
return null; | ||
} | ||
renderHiddenInput() { | ||
if (this.props.name === undefined) { | ||
return null; | ||
} | ||
if (this.props.nameAsArray) { | ||
return this.renderArrayHiddenInput(); | ||
} | ||
return this.renderJoinedHiddenInput(); | ||
} | ||
renderArrayHiddenInput() { | ||
return this.props.checked.map((value, index) => { | ||
const name = `${this.props.name}[]`; | ||
return <input key={index} name={name} type="hidden" value={value} />; | ||
}); | ||
} | ||
renderJoinedHiddenInput() { | ||
const checked = this.props.checked.join(','); | ||
return <input name={this.props.name} value={checked} type="hidden" />; | ||
} | ||
render() { | ||
const nodes = this.getFormattedNodes(this.props.nodes); | ||
const treeNodes = this.getTreeNodes(nodes); | ||
const treeNodes = this.renderTreeNodes(nodes); | ||
return ( | ||
<div className="react-checkbox-tree"> | ||
{this.getHiddenInput()} | ||
{this.renderHiddenInput()} | ||
{treeNodes} | ||
@@ -178,0 +203,0 @@ </div> |
@@ -5,11 +5,10 @@ import React from 'react'; | ||
static propTypes = { | ||
name: React.PropTypes.string, | ||
nameAsArray: React.PropTypes.bool, | ||
value: React.PropTypes.string, | ||
title: React.PropTypes.string, | ||
children: React.PropTypes.node, | ||
checked: React.PropTypes.number, | ||
collapsed: React.PropTypes.bool, | ||
expanded: React.PropTypes.bool, | ||
rawChildren: React.PropTypes.any, | ||
onCheck: React.PropTypes.func, | ||
onExpand: React.PropTypes.func, | ||
title: React.PropTypes.string, | ||
value: React.PropTypes.string, | ||
}; | ||
@@ -20,11 +19,29 @@ | ||
this.state = { | ||
collapsed: props.collapsed, | ||
}; | ||
this.onCheck = this.onCheck.bind(this); | ||
this.onExpand = this.onExpand.bind(this); | ||
} | ||
this.handleCollapseClick = this.handleCollapseClick.bind(this); | ||
this.handleCheckboxClick = this.handleCheckboxClick.bind(this); | ||
onCheck() { | ||
let isChecked = 0; | ||
// Toggle off/partial check state to checked | ||
if (this.props.checked === 0 || this.props.checked === 2) { | ||
isChecked = 1; | ||
} | ||
this.props.onCheck({ | ||
value: this.props.value, | ||
checked: isChecked, | ||
children: this.props.rawChildren, | ||
}); | ||
} | ||
getCollapseIcon() { | ||
onExpand() { | ||
this.props.onExpand({ | ||
value: this.props.value, | ||
expanded: !this.props.expanded, | ||
}); | ||
} | ||
renderCollapseIcon() { | ||
if (this.props.children === null) { | ||
@@ -34,3 +51,3 @@ return <i className="fa" />; | ||
if (this.state.collapsed) { | ||
if (!this.props.expanded) { | ||
return <i className="fa fa-chevron-right" />; | ||
@@ -42,3 +59,3 @@ } | ||
getCheckboxIcon() { | ||
renderCheckboxIcon() { | ||
if (this.props.checked === 0) { | ||
@@ -55,5 +72,5 @@ return <i className="fa fa-square-o" />; | ||
getNodeIcon() { | ||
renderNodeIcon() { | ||
if (this.props.children !== null) { | ||
if (this.state.collapsed) { | ||
if (!this.props.expanded) { | ||
return <i className="fa fa-folder-o" />; | ||
@@ -68,45 +85,10 @@ } | ||
getHiddenInput() { | ||
if (this.props.name === undefined || !this.props.nameAsArray) { | ||
renderChildren() { | ||
if (!this.props.expanded) { | ||
return null; | ||
} | ||
const name = `${this.props.name}[]`; | ||
if (this.props.checked === 1) { | ||
return <input name={name} value={this.props.value} type="hidden" />; | ||
} | ||
return null; | ||
} | ||
getChildren() { | ||
if (this.state.collapsed) { | ||
return null; | ||
} | ||
return this.props.children; | ||
} | ||
handleCollapseClick() { | ||
this.setState({ | ||
collapsed: !this.state.collapsed, | ||
}); | ||
} | ||
handleCheckboxClick() { | ||
let isChecked = 0; | ||
// Toggle off/partial check state to checked | ||
if (this.props.checked === 0 || this.props.checked === 2) { | ||
isChecked = 1; | ||
} | ||
this.props.onCheck({ | ||
value: this.props.value, | ||
checked: isChecked, | ||
children: this.props.rawChildren, | ||
}); | ||
} | ||
render() { | ||
@@ -116,11 +98,11 @@ return ( | ||
<span className="rct-text"> | ||
<span className="rct-collapse" onClick={this.handleCollapseClick} title="toggle"> | ||
{this.getCollapseIcon()} | ||
<span className="rct-collapse" onClick={this.onExpand} title="Toggle"> | ||
{this.renderCollapseIcon()} | ||
</span> | ||
<label onClick={this.handleCheckboxClick}> | ||
<label onClick={this.onCheck}> | ||
<span className="rct-checkbox"> | ||
{this.getCheckboxIcon()} | ||
{this.renderCheckboxIcon()} | ||
</span> | ||
<span className="rct-icon"> | ||
{this.getNodeIcon()} | ||
{this.renderNodeIcon()} | ||
</span> | ||
@@ -130,6 +112,5 @@ <span className="rct-title"> | ||
</span> | ||
{this.getHiddenInput()} | ||
</label> | ||
</span> | ||
{this.getChildren()} | ||
{this.renderChildren()} | ||
</li> | ||
@@ -136,0 +117,0 @@ ); |
@@ -1,3 +0,1 @@ | ||
/* eslint-disable */ | ||
module.exports = { | ||
@@ -11,7 +9,7 @@ output: { | ||
{ | ||
'react': { | ||
react: { | ||
root: 'React', | ||
commonjs2: 'react', | ||
commonjs: 'react', | ||
amd: 'react' | ||
amd: 'react', | ||
}, | ||
@@ -24,3 +22,3 @@ }, | ||
commonjs: 'react-dom', | ||
amd: 'react-dom' | ||
amd: 'react-dom', | ||
}, | ||
@@ -36,3 +34,3 @@ }, | ||
query: { | ||
presets: ['react', 'es2015', 'stage-0'] | ||
presets: ['react', 'es2015', 'stage-0'], | ||
}, | ||
@@ -39,0 +37,0 @@ }, |
@@ -1,3 +0,1 @@ | ||
/* eslint-disable */ | ||
module.exports = { | ||
@@ -16,3 +14,3 @@ output: { | ||
query: { | ||
presets: ['react', 'es2015', 'stage-0'] | ||
presets: ['react', 'es2015', 'stage-0'], | ||
}, | ||
@@ -19,0 +17,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
197967
18
834
89
24