From d7782b461ad56a9843a350555fddda59c40d0557 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Thu, 16 Apr 2015 14:59:39 +0200 Subject: [PATCH 01/26] Use context in order to pass custom click listener. Add "key" for efficient react comparison. --- .gitignore | 1 + public/index.html | 15 +- public/js/example.jsx | 70 ++-- public/js/react-bootstrap-treeview.js | 471 ++++++++++++++------------ src/react-bootstrap-treeview.jsx | 471 ++++++++++++++------------ 5 files changed, 572 insertions(+), 456 deletions(-) diff --git a/.gitignore b/.gitignore index c7b9408..c780fac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ public/bower_components/ +public/index.html \ No newline at end of file diff --git a/public/index.html b/public/index.html index 7fe34d9..f050e5c 100644 --- a/public/index.html +++ b/public/index.html @@ -1,7 +1,15 @@ React.js Tree View <i>for Twitter Bootstrap - + + + + + + + + + @@ -12,9 +20,10 @@

React.js Tree View for Twitter Bootstrap

- - + + + \ No newline at end of file diff --git a/public/js/example.jsx b/public/js/example.jsx index 81ead49..4ab134f 100644 --- a/public/js/example.jsx +++ b/public/js/example.jsx @@ -1,41 +1,45 @@ - - var data = [ - { - text: 'Parent 1', - nodes: [ - { - text: 'Child 1', + { + text: 'Parent 1', nodes: [ - { - text: 'Grandchild 1' - }, - { - text: 'Grandchild 2' - } + { + text: 'Child 1', + nodes: [ + { + text: 'Grandchild 1' + }, + { + text: 'Grandchild 2' + } + ] + }, + { + text: 'Child 2' + } ] - }, - { - text: 'Child 2' - } - ] - }, - { - text: 'Parent 2' - }, - { - text: 'Parent 3' - }, - { - text: 'Parent 4' - }, - { - text: 'Parent 5' - } + }, + { + text: 'Parent 2' + }, + { + text: 'Parent 3' + }, + { + text: 'Parent 4' + }, + { + text: 'Parent 5' + } ]; +var test = function () { + console.log('click'); +} React.render( - , - document.getElementById('treeview') + , + document.getElementById('treeview') ); \ No newline at end of file diff --git a/public/js/react-bootstrap-treeview.js b/public/js/react-bootstrap-treeview.js index a3d512d..a74d793 100644 --- a/public/js/react-bootstrap-treeview.js +++ b/public/js/react-bootstrap-treeview.js @@ -1,232 +1,283 @@ -var TreeView = React.createClass({displayName: "TreeView", - - propTypes: { - levels: React.PropTypes.number, - - expandIcon: React.PropTypes.string, - collapseIcon: React.PropTypes.string, - emptyIcon: React.PropTypes.string, - nodeIcon: React.PropTypes.string, - - color: React.PropTypes.string, - backColor: React.PropTypes.string, - borderColor: React.PropTypes.string, - onhoverColor: React.PropTypes.string, - selectedColor: React.PropTypes.string, - selectedBackColor: React.PropTypes.string, - - enableLinks: React.PropTypes.bool, - highlightSelected: React.PropTypes.bool, - showBorder: React.PropTypes.bool, - showTags: React.PropTypes.bool, - - nodes: React.PropTypes.arrayOf(React.PropTypes.number) - }, - - getDefaultProps: function () { - return { - levels: 2, - - expandIcon: 'glyphicon glyphicon-plus', - collapseIcon: 'glyphicon glyphicon-minus', - emptyIcon: 'glyphicon', - nodeIcon: 'glyphicon glyphicon-stop', - - color: undefined, - backColor: undefined, - borderColor: undefined, - onhoverColor: '#F5F5F5', // TODO Not implemented yet, investigate radium.js 'A toolchain for React component styling' - selectedColor: '#FFFFFF', - selectedBackColor: '#428bca', - - enableLinks: false, - highlightSelected: true, - showBorder: true, - showTags: false, - - nodes: [] +var TreeViewWrapper = React.createClass({displayName: "TreeViewWrapper", + + propTypes: { + handleClick: React.PropTypes.func + }, + + childContextTypes: { + handleClick: React.PropTypes.func + }, + + getChildContext: function () { + return { + handleClick: function (evt) { + // Dev click listener defined + if (this.props.handleClick != undefined) { + this.props.handleClick(jQuery.extend(true, {}, evt)); + } + }.bind(this) + } + }, + + render: function () { + return ( + React.createElement("div", {id: "treeview", className: "treeview"}, + React.createElement(TreeView, React.__spread({}, this.props)) + ) + ); } - }, - - setNodeId: function(node) { - - if (!node.nodes) return; - - var _this = this; - node.nodes.forEach(function checkStates(node) { - node.nodeId = _this.props.nodes.length; - _this.props.nodes.push(node); - _this.setNodeId(node); - }); - }, +}); - render: function() { +var TreeView = React.createClass({displayName: "TreeView", - this.setNodeId({ nodes: data }); + contextTypes: { + handleClick: React.PropTypes.func + }, + + propTypes: { + levels: React.PropTypes.number, + expandIcon: React.PropTypes.string, + collapseIcon: React.PropTypes.string, + emptyIcon: React.PropTypes.string, + nodeIcon: React.PropTypes.string, + + color: React.PropTypes.string, + backColor: React.PropTypes.string, + borderColor: React.PropTypes.string, + onhoverColor: React.PropTypes.string, + selectedColor: React.PropTypes.string, + selectedBackColor: React.PropTypes.string, + + enableLinks: React.PropTypes.bool, + highlightSelected: React.PropTypes.bool, + showBorder: React.PropTypes.bool, + showTags: React.PropTypes.bool, + + nodes: React.PropTypes.arrayOf(React.PropTypes.number) + }, + + + getDefaultProps: function () { + return { + levels: 2, + + expandIcon: 'glyphicon glyphicon-plus', + collapseIcon: 'glyphicon glyphicon-minus', + emptyIcon: 'glyphicon', + nodeIcon: 'glyphicon glyphicon-stop', + + color: undefined, + backColor: undefined, + borderColor: undefined, + onhoverColor: '#F5F5F5', // TODO Not implemented yet, investigate radium.js 'A toolchain for React component styling' + selectedColor: '#FFFFFF', + selectedBackColor: '#428bca', + + enableLinks: false, + highlightSelected: true, + showBorder: true, + showTags: false, + + nodes: [] + } + }, + + setNodeId: function (node) { + + if (!node.nodes) return; + + node.nodes.forEach(function checkStates(node) { + node.nodeId = this.props.nodes.length; + this.props.nodes.push(node); + this.setNodeId(node); + }, this); + }, + + render: function () { + + this.setNodeId({nodes: data}); + + var children = []; + if (data) { + var _this = this; + data.forEach(function (node, index) { + children.push(React.createElement(TreeNode, {node: node, + level: 1, + visible: true, + options: _this.props, + key: index})); + }); + } - var children = []; - if (data) { - var _this = this; - data.forEach(function (node) { - children.push(React.createElement(TreeNode, {node: node, - level: 1, - visible: true, - options: _this.props})); - }); - } - - return ( - React.createElement("div", {id: "treeview", className: "treeview"}, - React.createElement("ul", {className: "list-group"}, + return ( + React.createElement("ul", {className: "list-group"}, children - ) - ) - ); - } + ) + ); + } }); var TreeNode = React.createClass({displayName: "TreeNode", - getInitialState: function() { - var node = this.props.node; - return { - expanded: (node.state && node.state.hasOwnProperty('expanded')) ? - node.state.expanded : - (this.props.level < this.props.options.levels) ? - true : - false, - selected: (node.state && node.state.hasOwnProperty('selected')) ? - node.state.selected : - false - } - }, - - toggleExpanded: function(id, event) { - this.setState({ expanded: !this.state.expanded }); - event.stopPropagation(); - }, - - toggleSelected: function(id, event) { - this.setState({ selected: !this.state.selected }); - event.stopPropagation(); - }, - - render: function() { - - var node = this.props.node; - var options = this.props.options; - - var style; - if (!this.props.visible) { - - style = { - display: 'none' - }; - } - else { - - if (options.highlightSelected && this.state.selected) { - style = { - color: options.selectedColor, - backgroundColor: options.selectedBackColor - }; - } - else { - style = { - color: node.color || options.color, - backgroundColor: node.backColor || options.backColor - }; - } - - if (!options.showBorder) { - style.border = 'none'; - } - else if (options.borderColor) { - style.border = '1px solid ' + options.borderColor; - } - } - - var indents = []; - for (var i = 0; i < this.props.level-1; i++) { - indents.push(React.createElement("span", {className: "indent"})); - } - - var expandCollapseIcon; - if (node.nodes) { - if (!this.state.expanded) { - expandCollapseIcon = ( - React.createElement("span", {className: options.expandIcon, - onClick: this.toggleExpanded.bind(this, node.nodeId)} - ) + contextTypes: { + handleClick: React.PropTypes.func + }, + + getInitialState: function () { + var node = this.props.node; + return { + expanded: (node.state && node.state.hasOwnProperty('expanded')) ? + node.state.expanded : + (this.props.level < this.props.options.levels) ? + true : + false, + selected: (node.state && node.state.hasOwnProperty('selected')) ? + node.state.selected : + false + } + }, + + toggleExpanded: function (id, event) { + this.setState({expanded: !this.state.expanded}); + event.stopPropagation(); + }, + + toggleSelected: function (id, event) { + this.setState({selected: !this.state.selected}); + event.stopPropagation(); + }, + + handleClickTreeNode: function (nodeId, evt) { + + // SELECT LINE + this.toggleSelected(nodeId, jQuery.extend(true, {}, evt)); + // DEV CLICK + this.context.handleClick(jQuery.extend(true, {}, evt)); + evt.stopPropagation(); + }, + + render: function () { + + var node = this.props.node; + var options = this.props.options; + + var style; + if (!this.props.visible) { + + style = { + display: 'none' + }; + } + else { + + if (options.highlightSelected && this.state.selected) { + style = { + color: options.selectedColor, + backgroundColor: options.selectedBackColor + }; + } + else { + style = { + color: node.color || options.color, + backgroundColor: node.backColor || options.backColor + }; + } + + if (!options.showBorder) { + style.border = 'none'; + } + else if (options.borderColor) { + style.border = '1px solid ' + options.borderColor; + } + } + + var indents = []; + for (var i = 0; i < this.props.level - 1; i++) { + indents.push(React.createElement("span", { + className: "indent", + key: i})); + } + + var expandCollapseIcon; + if (node.nodes) { + if (!this.state.expanded) { + expandCollapseIcon = ( + React.createElement("span", {className: options.expandIcon, + onClick: this.toggleExpanded.bind(this, node.nodeId)} + ) + ); + } + else { + expandCollapseIcon = ( + React.createElement("span", {className: options.collapseIcon, + onClick: this.toggleExpanded.bind(this, node.nodeId)} + ) + ); + } + } + else { + expandCollapseIcon = ( + React.createElement("span", {className: options.emptyIcon}) + ); + } + + var nodeIcon = ( + React.createElement("span", {className: "icon"}, + React.createElement("i", {className: node.icon || options.nodeIcon}) + ) ); - } - else { - expandCollapseIcon = ( - React.createElement("span", {className: options.collapseIcon, - onClick: this.toggleExpanded.bind(this, node.nodeId)} - ) - ); - } - } - else { - expandCollapseIcon = ( - React.createElement("span", {className: options.emptyIcon}) - ); - } - - var nodeIcon = ( - React.createElement("span", {className: "icon"}, - React.createElement("i", {className: node.icon || options.nodeIcon}) - ) - ); - var nodeText; - if (options.enableLinks) { - nodeText = ( - React.createElement("a", {href: node.href/*style="color:inherit;"*/}, + var nodeText; + if (options.enableLinks) { + nodeText = ( + React.createElement("a", {href: node.href/*style="color:inherit;"*/}, node.text - ) - ); - } - else { - nodeText = ( - React.createElement("span", null, node.text) - ); - } + ) + ); + } + else { + nodeText = ( + React.createElement("span", null, node.text) + ); + } + + var badges; + if (options.showTags && node.tags) { + badges = node.tags.map(function (tag, index) { + return ( + React.createElement("span", { + className: "badge", + key: index}, + tag + ) + ); + }); + } + + var children = []; + if (node.nodes) { + node.nodes.forEach(function (node, index) { + children.push(React.createElement(TreeNode, {node: node, + level: this.props.level + 1, + visible: this.state.expanded && this.props.visible, + options: options, + key: index})); + }, this); + } - var badges; - if (options.showTags && node.tags) { - badges = node.tags.map(function (tag) { return ( - React.createElement("span", {className: "badge"}, tag) - ); - }); - } - - var children = []; - if (node.nodes) { - var _this = this; - node.nodes.forEach(function (node) { - children.push(React.createElement(TreeNode, {node: node, - level: _this.props.level+1, - visible: _this.state.expanded && _this.props.visible, - options: options})); - }); - } - - return ( - React.createElement("li", {className: "list-group-item", - style: style, - onClick: this.toggleSelected.bind(this, node.nodeId), - key: node.nodeId}, + React.createElement("li", {className: "list-group-item", + style: style, + onClick: this.handleClickTreeNode.bind(this, node.nodeId), + key: node.nodeId}, indents, expandCollapseIcon, nodeIcon, nodeText, badges, children - ) - ); - } + ) + ); + } }); \ No newline at end of file diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index e65fedf..88c8559 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -1,232 +1,283 @@ -var TreeView = React.createClass({ - - propTypes: { - levels: React.PropTypes.number, - - expandIcon: React.PropTypes.string, - collapseIcon: React.PropTypes.string, - emptyIcon: React.PropTypes.string, - nodeIcon: React.PropTypes.string, - - color: React.PropTypes.string, - backColor: React.PropTypes.string, - borderColor: React.PropTypes.string, - onhoverColor: React.PropTypes.string, - selectedColor: React.PropTypes.string, - selectedBackColor: React.PropTypes.string, - - enableLinks: React.PropTypes.bool, - highlightSelected: React.PropTypes.bool, - showBorder: React.PropTypes.bool, - showTags: React.PropTypes.bool, - - nodes: React.PropTypes.arrayOf(React.PropTypes.number) - }, - - getDefaultProps: function () { - return { - levels: 2, - - expandIcon: 'glyphicon glyphicon-plus', - collapseIcon: 'glyphicon glyphicon-minus', - emptyIcon: 'glyphicon', - nodeIcon: 'glyphicon glyphicon-stop', - - color: undefined, - backColor: undefined, - borderColor: undefined, - onhoverColor: '#F5F5F5', // TODO Not implemented yet, investigate radium.js 'A toolchain for React component styling' - selectedColor: '#FFFFFF', - selectedBackColor: '#428bca', - - enableLinks: false, - highlightSelected: true, - showBorder: true, - showTags: false, - - nodes: [] +var TreeViewWrapper = React.createClass({ + + propTypes: { + handleClick: React.PropTypes.func + }, + + childContextTypes: { + handleClick: React.PropTypes.func + }, + + getChildContext: function () { + return { + handleClick: function (evt) { + // Dev click listener defined + if (this.props.handleClick != undefined) { + this.props.handleClick(jQuery.extend(true, {}, evt)); + } + }.bind(this) + } + }, + + render: function () { + return ( +
+ +
+ ); } - }, - - setNodeId: function(node) { - - if (!node.nodes) return; - - var _this = this; - node.nodes.forEach(function checkStates(node) { - node.nodeId = _this.props.nodes.length; - _this.props.nodes.push(node); - _this.setNodeId(node); - }); - }, +}); - render: function() { +var TreeView = React.createClass({ - this.setNodeId({ nodes: data }); + contextTypes: { + handleClick: React.PropTypes.func + }, + + propTypes: { + levels: React.PropTypes.number, + expandIcon: React.PropTypes.string, + collapseIcon: React.PropTypes.string, + emptyIcon: React.PropTypes.string, + nodeIcon: React.PropTypes.string, + + color: React.PropTypes.string, + backColor: React.PropTypes.string, + borderColor: React.PropTypes.string, + onhoverColor: React.PropTypes.string, + selectedColor: React.PropTypes.string, + selectedBackColor: React.PropTypes.string, + + enableLinks: React.PropTypes.bool, + highlightSelected: React.PropTypes.bool, + showBorder: React.PropTypes.bool, + showTags: React.PropTypes.bool, + + nodes: React.PropTypes.arrayOf(React.PropTypes.number) + }, + + + getDefaultProps: function () { + return { + levels: 2, + + expandIcon: 'glyphicon glyphicon-plus', + collapseIcon: 'glyphicon glyphicon-minus', + emptyIcon: 'glyphicon', + nodeIcon: 'glyphicon glyphicon-stop', + + color: undefined, + backColor: undefined, + borderColor: undefined, + onhoverColor: '#F5F5F5', // TODO Not implemented yet, investigate radium.js 'A toolchain for React component styling' + selectedColor: '#FFFFFF', + selectedBackColor: '#428bca', + + enableLinks: false, + highlightSelected: true, + showBorder: true, + showTags: false, + + nodes: [] + } + }, + + setNodeId: function (node) { + + if (!node.nodes) return; + + node.nodes.forEach(function checkStates(node) { + node.nodeId = this.props.nodes.length; + this.props.nodes.push(node); + this.setNodeId(node); + }, this); + }, + + render: function () { + + this.setNodeId({nodes: data}); + + var children = []; + if (data) { + var _this = this; + data.forEach(function (node, index) { + children.push(); + }); + } - var children = []; - if (data) { - var _this = this; - data.forEach(function (node) { - children.push(); - }); - } - - return ( -
-
    + return ( +
      {children} -
    -
- ); - } + + ); + } }); var TreeNode = React.createClass({ - getInitialState: function() { - var node = this.props.node; - return { - expanded: (node.state && node.state.hasOwnProperty('expanded')) ? - node.state.expanded : - (this.props.level < this.props.options.levels) ? - true : - false, - selected: (node.state && node.state.hasOwnProperty('selected')) ? - node.state.selected : - false - } - }, - - toggleExpanded: function(id, event) { - this.setState({ expanded: !this.state.expanded }); - event.stopPropagation(); - }, - - toggleSelected: function(id, event) { - this.setState({ selected: !this.state.selected }); - event.stopPropagation(); - }, - - render: function() { - - var node = this.props.node; - var options = this.props.options; - - var style; - if (!this.props.visible) { - - style = { - display: 'none' - }; - } - else { - - if (options.highlightSelected && this.state.selected) { - style = { - color: options.selectedColor, - backgroundColor: options.selectedBackColor - }; - } - else { - style = { - color: node.color || options.color, - backgroundColor: node.backColor || options.backColor - }; - } - - if (!options.showBorder) { - style.border = 'none'; - } - else if (options.borderColor) { - style.border = '1px solid ' + options.borderColor; - } - } - - var indents = []; - for (var i = 0; i < this.props.level-1; i++) { - indents.push(); - } - - var expandCollapseIcon; - if (node.nodes) { - if (!this.state.expanded) { - expandCollapseIcon = ( - - + contextTypes: { + handleClick: React.PropTypes.func + }, + + getInitialState: function () { + var node = this.props.node; + return { + expanded: (node.state && node.state.hasOwnProperty('expanded')) ? + node.state.expanded : + (this.props.level < this.props.options.levels) ? + true : + false, + selected: (node.state && node.state.hasOwnProperty('selected')) ? + node.state.selected : + false + } + }, + + toggleExpanded: function (id, event) { + this.setState({expanded: !this.state.expanded}); + event.stopPropagation(); + }, + + toggleSelected: function (id, event) { + this.setState({selected: !this.state.selected}); + event.stopPropagation(); + }, + + handleClickTreeNode: function (nodeId, evt) { + + // SELECT LINE + this.toggleSelected(nodeId, jQuery.extend(true, {}, evt)); + // DEV CLICK + this.context.handleClick(jQuery.extend(true, {}, evt)); + evt.stopPropagation(); + }, + + render: function () { + + var node = this.props.node; + var options = this.props.options; + + var style; + if (!this.props.visible) { + + style = { + display: 'none' + }; + } + else { + + if (options.highlightSelected && this.state.selected) { + style = { + color: options.selectedColor, + backgroundColor: options.selectedBackColor + }; + } + else { + style = { + color: node.color || options.color, + backgroundColor: node.backColor || options.backColor + }; + } + + if (!options.showBorder) { + style.border = 'none'; + } + else if (options.borderColor) { + style.border = '1px solid ' + options.borderColor; + } + } + + var indents = []; + for (var i = 0; i < this.props.level - 1; i++) { + indents.push(); + } + + var expandCollapseIcon; + if (node.nodes) { + if (!this.state.expanded) { + expandCollapseIcon = ( + + + ); + } + else { + expandCollapseIcon = ( + + + ); + } + } + else { + expandCollapseIcon = ( + + ); + } + + var nodeIcon = ( + + + ); - } - else { - expandCollapseIcon = ( - - - ); - } - } - else { - expandCollapseIcon = ( - - ); - } - - var nodeIcon = ( - - - - ); - var nodeText; - if (options.enableLinks) { - nodeText = ( - + var nodeText; + if (options.enableLinks) { + nodeText = ( + {node.text} - - ); - } - else { - nodeText = ( - {node.text} - ); - } + + ); + } + else { + nodeText = ( + {node.text} + ); + } + + var badges; + if (options.showTags && node.tags) { + badges = node.tags.map(function (tag, index) { + return ( + + {tag} + + ); + }); + } + + var children = []; + if (node.nodes) { + node.nodes.forEach(function (node, index) { + children.push(); + }, this); + } - var badges; - if (options.showTags && node.tags) { - badges = node.tags.map(function (tag) { return ( - {tag} - ); - }); - } - - var children = []; - if (node.nodes) { - var _this = this; - node.nodes.forEach(function (node) { - children.push(); - }); - } - - return ( -
  • +
  • {indents} {expandCollapseIcon} {nodeIcon} {nodeText} {badges} {children} -
  • - ); - } + + ); + } }); \ No newline at end of file From 95c53c81a9e38a8f2e9f332d27b8044bc2322968 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Thu, 16 Apr 2015 17:39:04 +0200 Subject: [PATCH 02/26] TEST TreeView / TreeNode /TreeViewwith contexts --- public/js/example.jsx | 4 +- public/js/react-bootstrap-treeview.js | 89 ++++++++++---------------- src/react-bootstrap-treeview.jsx | 91 ++++++++++----------------- 3 files changed, 69 insertions(+), 115 deletions(-) diff --git a/public/js/example.jsx b/public/js/example.jsx index 4ab134f..1f877cd 100644 --- a/public/js/example.jsx +++ b/public/js/example.jsx @@ -37,9 +37,9 @@ var test = function () { } React.render( - , + onLineClicked={test}/>, document.getElementById('treeview') ); \ No newline at end of file diff --git a/public/js/react-bootstrap-treeview.js b/public/js/react-bootstrap-treeview.js index a74d793..24e287a 100644 --- a/public/js/react-bootstrap-treeview.js +++ b/public/js/react-bootstrap-treeview.js @@ -1,39 +1,5 @@ -var TreeViewWrapper = React.createClass({displayName: "TreeViewWrapper", - - propTypes: { - handleClick: React.PropTypes.func - }, - - childContextTypes: { - handleClick: React.PropTypes.func - }, - - getChildContext: function () { - return { - handleClick: function (evt) { - // Dev click listener defined - if (this.props.handleClick != undefined) { - this.props.handleClick(jQuery.extend(true, {}, evt)); - } - }.bind(this) - } - }, - - render: function () { - return ( - React.createElement("div", {id: "treeview", className: "treeview"}, - React.createElement(TreeView, React.__spread({}, this.props)) - ) - ); - } -}); - var TreeView = React.createClass({displayName: "TreeView", - contextTypes: { - handleClick: React.PropTypes.func - }, - propTypes: { levels: React.PropTypes.number, expandIcon: React.PropTypes.string, @@ -53,7 +19,8 @@ var TreeView = React.createClass({displayName: "TreeView", showBorder: React.PropTypes.bool, showTags: React.PropTypes.bool, - nodes: React.PropTypes.arrayOf(React.PropTypes.number) + data: React.PropTypes.arrayOf(React.PropTypes.object), + onLineClicked: React.PropTypes.func }, @@ -78,40 +45,51 @@ var TreeView = React.createClass({displayName: "TreeView", showBorder: true, showTags: false, - nodes: [] + data: [] } }, + nodes: [], + setNodeId: function (node) { if (!node.nodes) return; node.nodes.forEach(function checkStates(node) { - node.nodeId = this.props.nodes.length; - this.props.nodes.push(node); + node.nodeId = this.nodes.length; + this.nodes.push(node); this.setNodeId(node); }, this); }, + handleLineClicked: function (evt) { + if (this.props.onLineClicked !== undefined) { + // CLONE EVT + CALLBACK DEV + this.props.onLineClicked($.extend(true, {}, evt)); + } + }, + render: function () { - this.setNodeId({nodes: data}); + this.setNodeId({nodes: this.props.data}); var children = []; - if (data) { - var _this = this; - data.forEach(function (node, index) { + if (this.props.data) { + this.props.data.forEach(function (node, index) { children.push(React.createElement(TreeNode, {node: node, level: 1, visible: true, - options: _this.props, - key: index})); - }); + options: this.props, + key: index, + onLineClicked: this.handleLineClicked})); + }.bind(this)); } return ( - React.createElement("ul", {className: "list-group"}, + React.createElement("div", {className: "treeview"}, + React.createElement("ul", {className: "list-group"}, children + ) ) ); } @@ -120,8 +98,8 @@ var TreeView = React.createClass({displayName: "TreeView", var TreeNode = React.createClass({displayName: "TreeNode", - contextTypes: { - handleClick: React.PropTypes.func + propTypes: { + onLineClicked: React.PropTypes.func }, getInitialState: function () { @@ -129,9 +107,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", return { expanded: (node.state && node.state.hasOwnProperty('expanded')) ? node.state.expanded : - (this.props.level < this.props.options.levels) ? - true : - false, + (this.props.level < this.props.options.levels), selected: (node.state && node.state.hasOwnProperty('selected')) ? node.state.selected : false @@ -148,12 +124,12 @@ var TreeNode = React.createClass({displayName: "TreeNode", event.stopPropagation(); }, - handleClickTreeNode: function (nodeId, evt) { + handleLineClicked: function (nodeId, evt) { // SELECT LINE - this.toggleSelected(nodeId, jQuery.extend(true, {}, evt)); + this.toggleSelected(nodeId, $.extend(true, {}, evt)); // DEV CLICK - this.context.handleClick(jQuery.extend(true, {}, evt)); + this.props.onLineClicked($.extend(true, {}, evt)); evt.stopPropagation(); }, @@ -262,14 +238,15 @@ var TreeNode = React.createClass({displayName: "TreeNode", level: this.props.level + 1, visible: this.state.expanded && this.props.visible, options: options, - key: index})); + key: index, + onLineClicked: this.props.onLineClicked})); }, this); } return ( React.createElement("li", {className: "list-group-item", style: style, - onClick: this.handleClickTreeNode.bind(this, node.nodeId), + onClick: this.handleLineClicked.bind(this, node.nodeId), key: node.nodeId}, indents, expandCollapseIcon, diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index 88c8559..ece2c51 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -1,39 +1,5 @@ -var TreeViewWrapper = React.createClass({ - - propTypes: { - handleClick: React.PropTypes.func - }, - - childContextTypes: { - handleClick: React.PropTypes.func - }, - - getChildContext: function () { - return { - handleClick: function (evt) { - // Dev click listener defined - if (this.props.handleClick != undefined) { - this.props.handleClick(jQuery.extend(true, {}, evt)); - } - }.bind(this) - } - }, - - render: function () { - return ( -
    - -
    - ); - } -}); - var TreeView = React.createClass({ - contextTypes: { - handleClick: React.PropTypes.func - }, - propTypes: { levels: React.PropTypes.number, expandIcon: React.PropTypes.string, @@ -53,7 +19,8 @@ var TreeView = React.createClass({ showBorder: React.PropTypes.bool, showTags: React.PropTypes.bool, - nodes: React.PropTypes.arrayOf(React.PropTypes.number) + data: React.PropTypes.arrayOf(React.PropTypes.object), + onLineClicked: React.PropTypes.func }, @@ -78,41 +45,52 @@ var TreeView = React.createClass({ showBorder: true, showTags: false, - nodes: [] + data: [] } }, + nodes: [], + setNodeId: function (node) { if (!node.nodes) return; node.nodes.forEach(function checkStates(node) { - node.nodeId = this.props.nodes.length; - this.props.nodes.push(node); + node.nodeId = this.nodes.length; + this.nodes.push(node); this.setNodeId(node); }, this); }, + handleLineClicked: function (evt) { + if (this.props.onLineClicked !== undefined) { + // CLONE EVT + CALLBACK DEV + this.props.onLineClicked($.extend(true, {}, evt)); + } + }, + render: function () { - this.setNodeId({nodes: data}); + this.setNodeId({nodes: this.props.data}); var children = []; - if (data) { - var _this = this; - data.forEach(function (node, index) { + if (this.props.data) { + this.props.data.forEach(function (node, index) { children.push(); - }); + options={this.props} + key={index} + onLineClicked={this.handleLineClicked}/>); + }.bind(this)); } return ( -
      +
      +
        {children} -
      +
    + ); } }); @@ -120,8 +98,8 @@ var TreeView = React.createClass({ var TreeNode = React.createClass({ - contextTypes: { - handleClick: React.PropTypes.func + propTypes: { + onLineClicked: React.PropTypes.func }, getInitialState: function () { @@ -129,9 +107,7 @@ var TreeNode = React.createClass({ return { expanded: (node.state && node.state.hasOwnProperty('expanded')) ? node.state.expanded : - (this.props.level < this.props.options.levels) ? - true : - false, + (this.props.level < this.props.options.levels), selected: (node.state && node.state.hasOwnProperty('selected')) ? node.state.selected : false @@ -148,12 +124,12 @@ var TreeNode = React.createClass({ event.stopPropagation(); }, - handleClickTreeNode: function (nodeId, evt) { + handleLineClicked: function (nodeId, evt) { // SELECT LINE - this.toggleSelected(nodeId, jQuery.extend(true, {}, evt)); + this.toggleSelected(nodeId, $.extend(true, {}, evt)); // DEV CLICK - this.context.handleClick(jQuery.extend(true, {}, evt)); + this.props.onLineClicked($.extend(true, {}, evt)); evt.stopPropagation(); }, @@ -262,14 +238,15 @@ var TreeNode = React.createClass({ level={this.props.level + 1} visible={this.state.expanded && this.props.visible} options={options} - key={index}/>); + key={index} + onLineClicked={this.props.onLineClicked}/>); }, this); } return (
  • {indents} {expandCollapseIcon} From 65fcfdd34c0f2020ab3ec3513675ac9531045b8d Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Thu, 16 Apr 2015 17:43:01 +0200 Subject: [PATCH 03/26] Message of precedent commit: Add an onLineClicked custum event. --- src/react-bootstrap-treeview.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index ece2c51..870d5ca 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -140,7 +140,6 @@ var TreeNode = React.createClass({ var style; if (!this.props.visible) { - style = { display: 'none' }; @@ -257,4 +256,4 @@ var TreeNode = React.createClass({
  • ); } -}); \ No newline at end of file +}); From 360c76f1d642102e998dd701a513118da11bd72f Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Thu, 16 Apr 2015 18:12:28 +0200 Subject: [PATCH 04/26] Add a treeNodeAttributes propType in TreeView. Allow developper to pass dynamic information in TreeNode. Dynamic attributes are passed in an object {attribute key : attribute value } attribute key: what we want => data-test attribute value: a key of this.props.data => id For example, var data = [ { text: 'Parent 1', id: '1', } ]; React.render( , document.getElementById('treeview') ); --- public/js/example.jsx | 23 ++++++++++----- public/js/react-bootstrap-treeview.js | 42 ++++++++++++++++++--------- src/react-bootstrap-treeview.jsx | 39 +++++++++++++++++-------- 3 files changed, 71 insertions(+), 33 deletions(-) diff --git a/public/js/example.jsx b/public/js/example.jsx index 1f877cd..c9b3f9f 100644 --- a/public/js/example.jsx +++ b/public/js/example.jsx @@ -1,31 +1,39 @@ var data = [ { text: 'Parent 1', + id: '1', nodes: [ { text: 'Child 1', + id: '11', nodes: [ { - text: 'Grandchild 1' + text: 'Grandchild 1', + id: '111' }, { - text: 'Grandchild 2' + text: 'Grandchild 2', + id: '112' } ] }, { - text: 'Child 2' + text: 'Child 2', + id: '12' } ] }, { - text: 'Parent 2' + text: 'Parent 2', + id: '2' }, { - text: 'Parent 3' + text: 'Parent 3', + id: '3' }, { - text: 'Parent 4' + text: 'Parent 4', + id: '4' }, { text: 'Parent 5' @@ -40,6 +48,7 @@ React.render( , + onLineClicked={test} + treeNodeAttributes={{'data-id':'id'}}/>, document.getElementById('treeview') ); \ No newline at end of file diff --git a/public/js/react-bootstrap-treeview.js b/public/js/react-bootstrap-treeview.js index 24e287a..0f644b1 100644 --- a/public/js/react-bootstrap-treeview.js +++ b/public/js/react-bootstrap-treeview.js @@ -20,7 +20,8 @@ var TreeView = React.createClass({displayName: "TreeView", showTags: React.PropTypes.bool, data: React.PropTypes.arrayOf(React.PropTypes.object), - onLineClicked: React.PropTypes.func + onLineClicked: React.PropTypes.func, + treeNodeAttributes: React.PropTypes.object //ex:{'data-id': a key in this.props.data} }, @@ -45,7 +46,8 @@ var TreeView = React.createClass({displayName: "TreeView", showBorder: true, showTags: false, - data: [] + data: [], + treeNodeAttributes: {} } }, @@ -81,7 +83,8 @@ var TreeView = React.createClass({displayName: "TreeView", visible: true, options: this.props, key: index, - onLineClicked: this.handleLineClicked})); + onLineClicked: this.handleLineClicked, + attributes: this.props.treeNodeAttributes})); }.bind(this)); } @@ -99,7 +102,8 @@ var TreeView = React.createClass({displayName: "TreeView", var TreeNode = React.createClass({displayName: "TreeNode", propTypes: { - onLineClicked: React.PropTypes.func + onLineClicked: React.PropTypes.func, + attributes: React.PropTypes.object }, getInitialState: function () { @@ -140,7 +144,6 @@ var TreeNode = React.createClass({displayName: "TreeNode", var style; if (!this.props.visible) { - style = { display: 'none' }; @@ -175,6 +178,15 @@ var TreeNode = React.createClass({displayName: "TreeNode", key: i})); } + var attrs = {}; + if (this.props.attributes !== undefined) { + for( var i in this.props.attributes) { + if (node[this.props.attributes[i]] !== undefined) { + attrs[i] = node[this.props.attributes[i]]; + } + }; + } + var expandCollapseIcon; if (node.nodes) { if (!this.state.expanded) { @@ -239,22 +251,24 @@ var TreeNode = React.createClass({displayName: "TreeNode", visible: this.state.expanded && this.props.visible, options: options, key: index, - onLineClicked: this.props.onLineClicked})); + onLineClicked: this.props.onLineClicked, + attributes: this.props.attributes})); }, this); } return ( - React.createElement("li", {className: "list-group-item", + React.createElement("li", React.__spread({className: "list-group-item", style: style, onClick: this.handleLineClicked.bind(this, node.nodeId), key: node.nodeId}, - indents, - expandCollapseIcon, - nodeIcon, - nodeText, - badges, - children + attrs), + indents, + expandCollapseIcon, + nodeIcon, + nodeText, + badges, + children ) ); } -}); \ No newline at end of file +}); diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index 870d5ca..99b0b5f 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -20,7 +20,8 @@ var TreeView = React.createClass({ showTags: React.PropTypes.bool, data: React.PropTypes.arrayOf(React.PropTypes.object), - onLineClicked: React.PropTypes.func + onLineClicked: React.PropTypes.func, + treeNodeAttributes: React.PropTypes.object //ex:{'data-id': a key in this.props.data} }, @@ -45,7 +46,8 @@ var TreeView = React.createClass({ showBorder: true, showTags: false, - data: [] + data: [], + treeNodeAttributes: {} } }, @@ -81,7 +83,8 @@ var TreeView = React.createClass({ visible={true} options={this.props} key={index} - onLineClicked={this.handleLineClicked}/>); + onLineClicked={this.handleLineClicked} + attributes={this.props.treeNodeAttributes}/>); }.bind(this)); } @@ -99,7 +102,8 @@ var TreeView = React.createClass({ var TreeNode = React.createClass({ propTypes: { - onLineClicked: React.PropTypes.func + onLineClicked: React.PropTypes.func, + attributes: React.PropTypes.object }, getInitialState: function () { @@ -174,6 +178,15 @@ var TreeNode = React.createClass({ key={i}>); } + var attrs = {}; + if (this.props.attributes !== undefined) { + for( var i in this.props.attributes) { + if (node[this.props.attributes[i]] !== undefined) { + attrs[i] = node[this.props.attributes[i]]; + } + }; + } + var expandCollapseIcon; if (node.nodes) { if (!this.state.expanded) { @@ -238,7 +251,8 @@ var TreeNode = React.createClass({ visible={this.state.expanded && this.props.visible} options={options} key={index} - onLineClicked={this.props.onLineClicked}/>); + onLineClicked={this.props.onLineClicked} + attributes={this.props.attributes}/>); }, this); } @@ -246,13 +260,14 @@ var TreeNode = React.createClass({
  • - {indents} - {expandCollapseIcon} - {nodeIcon} - {nodeText} - {badges} - {children} + key={node.nodeId} + {...attrs}> + {indents} + {expandCollapseIcon} + {nodeIcon} + {nodeText} + {badges} + {children}
  • ); } From ee3ce150ce77902d2e23d6c59e501e46dcf76a5e Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 17 Apr 2015 13:31:13 +0200 Subject: [PATCH 05/26] react-bootstrap-treeview is now a commonJS component. It can be required via TreeView = require('react-bootstrap-treeview'); --- Gruntfile.js | 84 +++--- README.md | 55 ++-- {public => dist}/index.html | 0 example/index.html | 24 ++ .../js/example.jsx => example/js/example.js | 22 +- package.json | 15 +- public/css/react-bootstrap-treeview.css | 19 -- public/js/react-bootstrap-treeview.js | 274 ------------------ src/react-bootstrap-treeview.jsx | 2 + 9 files changed, 121 insertions(+), 374 deletions(-) rename {public => dist}/index.html (100%) create mode 100644 example/index.html rename public/js/example.jsx => example/js/example.js (69%) delete mode 100644 public/css/react-bootstrap-treeview.css delete mode 100644 public/js/react-bootstrap-treeview.js diff --git a/Gruntfile.js b/Gruntfile.js index a42a5fb..5a91411 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,36 +1,52 @@ -module.exports = function(grunt) { - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), // the package file to use - - react: { - single_file_output: { - files: { - 'public/js/react-bootstrap-treeview.js': 'src/react-bootstrap-treeview.jsx' +module.exports = function (grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), // the package file to use + + react: { + single_file_output: { + files: { + 'dist/js/react-bootstrap-treeview.js': 'src/react-bootstrap-treeview.jsx' + } + } + }, + + watch: { + files: ['<%= react.single_file_output.files[\'dist/js/react-bootstrap-treeview.js\'] %>', '<%= browserify.main.src %>'], + tasks: ['default', 'browserify'] + }, + + copy: { + main: { + files: [ + // copy src to example + {expand: true, cwd: 'src/', src: '*.css', dest: 'dist/css/'}, + {expand: true, cwd: 'src/', src: '*.css', dest: 'example/public/css/'} + // { expand: true, cwd: 'src/js', src: '*', dest: 'public/js/' } + ] + } + }, + + 'browserify': { + main: { + options: { + debug: true, + transform: ['reactify'], + extensions: ['.jsx'] + }, + src: 'example/js/example.js', + dest: 'example/public/js/example.js' + + } + } - } - }, - - watch: { - files: [/*'tests/*.js', 'tests/*.html', */'src/**'], - tasks: ['default'] - }, - - copy: { - main: { - files: [ - // copy src to example - { expand: true, cwd: 'src/', src: '*.css', dest: 'public/css/' }, - // { expand: true, cwd: 'src/js', src: '*', dest: 'public/js/' } - ] - } - } - }); - - // load up your plugins - grunt.loadNpmTasks('grunt-react'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-contrib-copy'); - - // register one or more task lists (you should ALWAYS have a "default" task list) - grunt.registerTask('default', ['react', 'copy', 'watch']); + }); + + // load up your plugins + grunt.loadNpmTasks('grunt-react'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-browserify'); + + // register one or more task lists (you should ALWAYS have a "default" task list) + grunt.registerTask('default', ['react', 'copy', 'browserify', 'watch']); }; diff --git a/README.md b/README.md index 3e29706..f7b6d1c 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,10 @@ Include the correct styles, it's mainly just bootstrap but we add a few tweaks a ``` -Add required libraries. +Require the commonJS TreeView -```html - - - +```js +var TreeView = require('react-bootstrap-treeview'); ``` Then, a basic initialization would look like. @@ -55,36 +53,16 @@ React.render( ); ``` +Ifyou don't use browserify, include js files in dist folder + + ### Example -Putting it all together a minimal implementation might look like this. +On example can be run via the commend: +grunt + +Files are created in example/public folder. -```html - - - React + Bootstrap Tree View - - - - -
    -

    React + Bootstrap Tree View

    -
    -
    -
    -
    -
    - - - - - - ``` @@ -286,6 +264,9 @@ String, class name(s). Default: "glyphicon glyphicon-stop" as defined by [Boots Sets the default icon to be used on all nodes, except when overridden on a per node basis in data. +### onLineClicked +Function, callback call when a line (node) is clicked + #### selectedBackColor String, [any legal color value](http://www.w3schools.com/cssref/css_colors_legal.asp). Default: '#428bca'. @@ -306,7 +287,15 @@ Boolean. Default: false Whether or not to display tags to the right of each node. The values of which must be provided in the data structure on a per node basis. - +### treeNodeAttributes +Object, couples of keys, values {key1 : value1, key2 : value2} +key: HTML attribute od the node (LI) +value: Dynamic data computed from this.props. +example: treeNodeAttributes = {'data-id' : 'id'} data = { + text: 'Parent 1', + id: '1' + } + The node "Parent 1" will have a data-id attribute equals to 1 =>
  • Parent 1
  • ## Copyright and Licensing Copyright 2013 Jonathan Miles diff --git a/public/index.html b/dist/index.html similarity index 100% rename from public/index.html rename to dist/index.html diff --git a/example/index.html b/example/index.html new file mode 100644 index 0000000..d8395ff --- /dev/null +++ b/example/index.html @@ -0,0 +1,24 @@ + + + Treeview + + + + + + + + + + +
    +

    React.js Tree View for Twitter Bootstrap

    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/public/js/example.jsx b/example/js/example.js similarity index 69% rename from public/js/example.jsx rename to example/js/example.js index c9b3f9f..a87ef34 100644 --- a/public/js/example.jsx +++ b/example/js/example.js @@ -1,3 +1,6 @@ +var React = require('react/addons'); +var TreeView = require('../../src/react-bootstrap-treeview.jsx'); + var data = [ { text: 'Parent 1', @@ -44,11 +47,14 @@ var test = function () { console.log('click'); } -React.render( - , - document.getElementById('treeview') -); \ No newline at end of file +// DOM loaded +$(function () { + React.render( + , + document.getElementById('treeview') + ); +}) diff --git a/package.json b/package.json index cb1a9eb..fe2f69e 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,7 @@ "url": "https://github.com/jonmiles/bootstrap-treeview/blob/master/LICENSE" } ], - "main": [ - - ], + "main": "./src/react-bootstrap-treeview.jsx", "scripts": { "install": "bower install", "start": "node app" @@ -30,14 +28,19 @@ "node": ">= 0.10.0" }, "dependencies": { - "express": "3.4.4" + "express": "3.4.4", + "react": "^0.13.1" }, "devDependencies": { "bower": "1.3.x", + "browserify": "^9.0.8", "grunt": "0.4.x", - "grunt-react": "~0.12.0", + "grunt-browserify": "^3.7.0", + "grunt-contrib-copy": "~0.8.0", "grunt-contrib-watch": "~0.6.1", - "grunt-contrib-copy": "~0.8.0" + "grunt-react": "~0.12.0", + "reactify": "^1.1.0", + "watchify": "^3.1.1" }, "keywords": [ "twitter", diff --git a/public/css/react-bootstrap-treeview.css b/public/css/react-bootstrap-treeview.css deleted file mode 100644 index 3f249e8..0000000 --- a/public/css/react-bootstrap-treeview.css +++ /dev/null @@ -1,19 +0,0 @@ - -.treeview .list-group-item { - cursor: pointer; -} - -.treeview span { - width: 1rem; - height: 1rem; -} - -.treeview span.indent { - margin-left: 10px; - margin-right: 10px; -} - -.treeview span.icon { - margin-left: 10px; - margin-right: 5px; -} diff --git a/public/js/react-bootstrap-treeview.js b/public/js/react-bootstrap-treeview.js deleted file mode 100644 index 0f644b1..0000000 --- a/public/js/react-bootstrap-treeview.js +++ /dev/null @@ -1,274 +0,0 @@ -var TreeView = React.createClass({displayName: "TreeView", - - propTypes: { - levels: React.PropTypes.number, - expandIcon: React.PropTypes.string, - collapseIcon: React.PropTypes.string, - emptyIcon: React.PropTypes.string, - nodeIcon: React.PropTypes.string, - - color: React.PropTypes.string, - backColor: React.PropTypes.string, - borderColor: React.PropTypes.string, - onhoverColor: React.PropTypes.string, - selectedColor: React.PropTypes.string, - selectedBackColor: React.PropTypes.string, - - enableLinks: React.PropTypes.bool, - highlightSelected: React.PropTypes.bool, - showBorder: React.PropTypes.bool, - showTags: React.PropTypes.bool, - - data: React.PropTypes.arrayOf(React.PropTypes.object), - onLineClicked: React.PropTypes.func, - treeNodeAttributes: React.PropTypes.object //ex:{'data-id': a key in this.props.data} - }, - - - getDefaultProps: function () { - return { - levels: 2, - - expandIcon: 'glyphicon glyphicon-plus', - collapseIcon: 'glyphicon glyphicon-minus', - emptyIcon: 'glyphicon', - nodeIcon: 'glyphicon glyphicon-stop', - - color: undefined, - backColor: undefined, - borderColor: undefined, - onhoverColor: '#F5F5F5', // TODO Not implemented yet, investigate radium.js 'A toolchain for React component styling' - selectedColor: '#FFFFFF', - selectedBackColor: '#428bca', - - enableLinks: false, - highlightSelected: true, - showBorder: true, - showTags: false, - - data: [], - treeNodeAttributes: {} - } - }, - - nodes: [], - - setNodeId: function (node) { - - if (!node.nodes) return; - - node.nodes.forEach(function checkStates(node) { - node.nodeId = this.nodes.length; - this.nodes.push(node); - this.setNodeId(node); - }, this); - }, - - handleLineClicked: function (evt) { - if (this.props.onLineClicked !== undefined) { - // CLONE EVT + CALLBACK DEV - this.props.onLineClicked($.extend(true, {}, evt)); - } - }, - - render: function () { - - this.setNodeId({nodes: this.props.data}); - - var children = []; - if (this.props.data) { - this.props.data.forEach(function (node, index) { - children.push(React.createElement(TreeNode, {node: node, - level: 1, - visible: true, - options: this.props, - key: index, - onLineClicked: this.handleLineClicked, - attributes: this.props.treeNodeAttributes})); - }.bind(this)); - } - - return ( - React.createElement("div", {className: "treeview"}, - React.createElement("ul", {className: "list-group"}, - children - ) - ) - ); - } -}); - - -var TreeNode = React.createClass({displayName: "TreeNode", - - propTypes: { - onLineClicked: React.PropTypes.func, - attributes: React.PropTypes.object - }, - - getInitialState: function () { - var node = this.props.node; - return { - expanded: (node.state && node.state.hasOwnProperty('expanded')) ? - node.state.expanded : - (this.props.level < this.props.options.levels), - selected: (node.state && node.state.hasOwnProperty('selected')) ? - node.state.selected : - false - } - }, - - toggleExpanded: function (id, event) { - this.setState({expanded: !this.state.expanded}); - event.stopPropagation(); - }, - - toggleSelected: function (id, event) { - this.setState({selected: !this.state.selected}); - event.stopPropagation(); - }, - - handleLineClicked: function (nodeId, evt) { - - // SELECT LINE - this.toggleSelected(nodeId, $.extend(true, {}, evt)); - // DEV CLICK - this.props.onLineClicked($.extend(true, {}, evt)); - evt.stopPropagation(); - }, - - render: function () { - - var node = this.props.node; - var options = this.props.options; - - var style; - if (!this.props.visible) { - style = { - display: 'none' - }; - } - else { - - if (options.highlightSelected && this.state.selected) { - style = { - color: options.selectedColor, - backgroundColor: options.selectedBackColor - }; - } - else { - style = { - color: node.color || options.color, - backgroundColor: node.backColor || options.backColor - }; - } - - if (!options.showBorder) { - style.border = 'none'; - } - else if (options.borderColor) { - style.border = '1px solid ' + options.borderColor; - } - } - - var indents = []; - for (var i = 0; i < this.props.level - 1; i++) { - indents.push(React.createElement("span", { - className: "indent", - key: i})); - } - - var attrs = {}; - if (this.props.attributes !== undefined) { - for( var i in this.props.attributes) { - if (node[this.props.attributes[i]] !== undefined) { - attrs[i] = node[this.props.attributes[i]]; - } - }; - } - - var expandCollapseIcon; - if (node.nodes) { - if (!this.state.expanded) { - expandCollapseIcon = ( - React.createElement("span", {className: options.expandIcon, - onClick: this.toggleExpanded.bind(this, node.nodeId)} - ) - ); - } - else { - expandCollapseIcon = ( - React.createElement("span", {className: options.collapseIcon, - onClick: this.toggleExpanded.bind(this, node.nodeId)} - ) - ); - } - } - else { - expandCollapseIcon = ( - React.createElement("span", {className: options.emptyIcon}) - ); - } - - var nodeIcon = ( - React.createElement("span", {className: "icon"}, - React.createElement("i", {className: node.icon || options.nodeIcon}) - ) - ); - - var nodeText; - if (options.enableLinks) { - nodeText = ( - React.createElement("a", {href: node.href/*style="color:inherit;"*/}, - node.text - ) - ); - } - else { - nodeText = ( - React.createElement("span", null, node.text) - ); - } - - var badges; - if (options.showTags && node.tags) { - badges = node.tags.map(function (tag, index) { - return ( - React.createElement("span", { - className: "badge", - key: index}, - tag - ) - ); - }); - } - - var children = []; - if (node.nodes) { - node.nodes.forEach(function (node, index) { - children.push(React.createElement(TreeNode, {node: node, - level: this.props.level + 1, - visible: this.state.expanded && this.props.visible, - options: options, - key: index, - onLineClicked: this.props.onLineClicked, - attributes: this.props.attributes})); - }, this); - } - - return ( - React.createElement("li", React.__spread({className: "list-group-item", - style: style, - onClick: this.handleLineClicked.bind(this, node.nodeId), - key: node.nodeId}, - attrs), - indents, - expandCollapseIcon, - nodeIcon, - nodeText, - badges, - children - ) - ); - } -}); diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index 99b0b5f..cbbf9e2 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -1,3 +1,4 @@ +var React = require('react/addons'); var TreeView = React.createClass({ propTypes: { @@ -98,6 +99,7 @@ var TreeView = React.createClass({ } }); +module.exports = TreeView; var TreeNode = React.createClass({ From 589f6f51e8c506fbff6c6e74d965af779993ece1 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 17 Apr 2015 13:34:17 +0200 Subject: [PATCH 06/26] modify readme --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f7b6d1c..e3d5b5f 100644 --- a/README.md +++ b/README.md @@ -58,13 +58,11 @@ Ifyou don't use browserify, include js files in dist folder ### Example -On example can be run via the commend: +An example can be run via the command: grunt Files are created in example/public folder. -``` - ## Data Structure @@ -264,7 +262,7 @@ String, class name(s). Default: "glyphicon glyphicon-stop" as defined by [Boots Sets the default icon to be used on all nodes, except when overridden on a per node basis in data. -### onLineClicked +#### onLineClicked Function, callback call when a line (node) is clicked #### selectedBackColor @@ -287,7 +285,7 @@ Boolean. Default: false Whether or not to display tags to the right of each node. The values of which must be provided in the data structure on a per node basis. -### treeNodeAttributes +#### treeNodeAttributes Object, couples of keys, values {key1 : value1, key2 : value2} key: HTML attribute od the node (LI) value: Dynamic data computed from this.props. From d51cb432e93ba615bb64ed942c62659b2722c9b0 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 17 Apr 2015 14:31:19 +0200 Subject: [PATCH 07/26] update readme --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e3d5b5f..685c091 100644 --- a/README.md +++ b/README.md @@ -286,14 +286,18 @@ Boolean. Default: false Whether or not to display tags to the right of each node. The values of which must be provided in the data structure on a per node basis. #### treeNodeAttributes -Object, couples of keys, values {key1 : value1, key2 : value2} -key: HTML attribute od the node (LI) +Object, couples of keys/values ```{key1 : value1, key2 : value2}``` +key: HTML attribute of the node (LI) value: Dynamic data computed from this.props. -example: treeNodeAttributes = {'data-id' : 'id'} data = { - text: 'Parent 1', - id: '1' - } - The node "Parent 1" will have a data-id attribute equals to 1 =>
  • Parent 1
  • +example: +``` treeNodeAttributes = {'data-id' : 'id'} + data = { + text: 'Parent 1', + id: '1' + } + ``` + The node "Parent 1" will have a data-id attribute equals to 1 + ```
  • Parent 1
  • ``` ## Copyright and Licensing Copyright 2013 Jonathan Miles From c9e084b3b3605e92e910106320107c6b89693522 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 17 Apr 2015 14:33:38 +0200 Subject: [PATCH 08/26] update readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 685c091..9ba0b8f 100644 --- a/README.md +++ b/README.md @@ -286,18 +286,19 @@ Boolean. Default: false Whether or not to display tags to the right of each node. The values of which must be provided in the data structure on a per node basis. #### treeNodeAttributes -Object, couples of keys/values ```{key1 : value1, key2 : value2}``` +Object, couples of keys/values ```javascript {key1 : value1, key2 : value2}``` key: HTML attribute of the node (LI) value: Dynamic data computed from this.props. example: -``` treeNodeAttributes = {'data-id' : 'id'} +```javascript + treeNodeAttributes = {'data-id' : 'id'} data = { text: 'Parent 1', id: '1' } ``` The node "Parent 1" will have a data-id attribute equals to 1 - ```
  • Parent 1
  • ``` + ```html
  • Parent 1
  • ``` ## Copyright and Licensing Copyright 2013 Jonathan Miles From bc15010028aa416ff3280ebd4887414f30bdec00 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 17 Apr 2015 14:40:24 +0200 Subject: [PATCH 09/26] update readme --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9ba0b8f..b79d592 100644 --- a/README.md +++ b/README.md @@ -286,19 +286,22 @@ Boolean. Default: false Whether or not to display tags to the right of each node. The values of which must be provided in the data structure on a per node basis. #### treeNodeAttributes -Object, couples of keys/values ```javascript {key1 : value1, key2 : value2}``` -key: HTML attribute of the node (LI) -value: Dynamic data computed from this.props. +Object, couples of keys/values ```{key1 : value1, key2 : value2}``` + +*key:* HTML attribute of the node ```
  • ``` + +*value:* Dynamic data computed from this.props.data + example: ```javascript - treeNodeAttributes = {'data-id' : 'id'} + treeNodeAttributes = {'data-foo' : 'bar'} data = { text: 'Parent 1', - id: '1' - } + bar: '1' + } ``` The node "Parent 1" will have a data-id attribute equals to 1 - ```html
  • Parent 1
  • ``` + ```html
  • Parent 1
  • ``` ## Copyright and Licensing Copyright 2013 Jonathan Miles From 60a664a7fbdff3f2dd0c2fb2b3d9dc90ab9ebf5b Mon Sep 17 00:00:00 2001 From: Yann Plantevin Date: Fri, 17 Apr 2015 14:45:05 +0200 Subject: [PATCH 10/26] Update package.json Delete useless bower call on install --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index fe2f69e..3285e50 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ ], "main": "./src/react-bootstrap-treeview.jsx", "scripts": { - "install": "bower install", "start": "node app" }, "engines": { From e1c4d1a2edefd3b9ff1a9e4bd2db82df819cf644 Mon Sep 17 00:00:00 2001 From: Yann Plantevin Date: Fri, 17 Apr 2015 14:45:42 +0200 Subject: [PATCH 11/26] Delete bower.json Useless since last commit --- bower.json | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 bower.json diff --git a/bower.json b/bower.json deleted file mode 100644 index 9c5c8c6..0000000 --- a/bower.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "react-bootstrap-treeview", - "description": "React Tree View for Twitter Bootstrap", - "version": "0.1.0", - "homepage": "https://github.com/jonmiles/react-bootstrap-treeview", - "main": [ - - ], - "keywords": [ - "twitter", - "bootstrap", - "tree", - "treeview", - "tree-view", - "navigation", - "javascript", - "react", - "react-component" - ], - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "dependencies": { - "bootstrap": ">= 3.0.0", - "react": "~0.13.1" - }, - "devDependencies": {} -} From 10a9649f673f2431349ed6f348ebab5f218ded9f Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 17 Apr 2015 14:50:19 +0200 Subject: [PATCH 12/26] publish DIST --- dist/css/react-bootstrap-treeview.css | 19 ++ dist/js/react-bootstrap-treeview.js | 276 ++++++++++++++++++++++++++ 2 files changed, 295 insertions(+) create mode 100644 dist/css/react-bootstrap-treeview.css create mode 100644 dist/js/react-bootstrap-treeview.js diff --git a/dist/css/react-bootstrap-treeview.css b/dist/css/react-bootstrap-treeview.css new file mode 100644 index 0000000..3f249e8 --- /dev/null +++ b/dist/css/react-bootstrap-treeview.css @@ -0,0 +1,19 @@ + +.treeview .list-group-item { + cursor: pointer; +} + +.treeview span { + width: 1rem; + height: 1rem; +} + +.treeview span.indent { + margin-left: 10px; + margin-right: 10px; +} + +.treeview span.icon { + margin-left: 10px; + margin-right: 5px; +} diff --git a/dist/js/react-bootstrap-treeview.js b/dist/js/react-bootstrap-treeview.js new file mode 100644 index 0000000..8997a6c --- /dev/null +++ b/dist/js/react-bootstrap-treeview.js @@ -0,0 +1,276 @@ +var React = require('react/addons'); +var TreeView = React.createClass({displayName: "TreeView", + + propTypes: { + levels: React.PropTypes.number, + expandIcon: React.PropTypes.string, + collapseIcon: React.PropTypes.string, + emptyIcon: React.PropTypes.string, + nodeIcon: React.PropTypes.string, + + color: React.PropTypes.string, + backColor: React.PropTypes.string, + borderColor: React.PropTypes.string, + onhoverColor: React.PropTypes.string, + selectedColor: React.PropTypes.string, + selectedBackColor: React.PropTypes.string, + + enableLinks: React.PropTypes.bool, + highlightSelected: React.PropTypes.bool, + showBorder: React.PropTypes.bool, + showTags: React.PropTypes.bool, + + data: React.PropTypes.arrayOf(React.PropTypes.object), + onLineClicked: React.PropTypes.func, + treeNodeAttributes: React.PropTypes.object //ex:{'data-id': a key in this.props.data} + }, + + + getDefaultProps: function () { + return { + levels: 2, + + expandIcon: 'glyphicon glyphicon-plus', + collapseIcon: 'glyphicon glyphicon-minus', + emptyIcon: 'glyphicon', + nodeIcon: 'glyphicon glyphicon-stop', + + color: undefined, + backColor: undefined, + borderColor: undefined, + onhoverColor: '#F5F5F5', // TODO Not implemented yet, investigate radium.js 'A toolchain for React component styling' + selectedColor: '#FFFFFF', + selectedBackColor: '#428bca', + + enableLinks: false, + highlightSelected: true, + showBorder: true, + showTags: false, + + data: [], + treeNodeAttributes: {} + } + }, + + nodes: [], + + setNodeId: function (node) { + + if (!node.nodes) return; + + node.nodes.forEach(function checkStates(node) { + node.nodeId = this.nodes.length; + this.nodes.push(node); + this.setNodeId(node); + }, this); + }, + + handleLineClicked: function (evt) { + if (this.props.onLineClicked !== undefined) { + // CLONE EVT + CALLBACK DEV + this.props.onLineClicked($.extend(true, {}, evt)); + } + }, + + render: function () { + + this.setNodeId({nodes: this.props.data}); + + var children = []; + if (this.props.data) { + this.props.data.forEach(function (node, index) { + children.push(React.createElement(TreeNode, {node: node, + level: 1, + visible: true, + options: this.props, + key: index, + onLineClicked: this.handleLineClicked, + attributes: this.props.treeNodeAttributes})); + }.bind(this)); + } + + return ( + React.createElement("div", {className: "treeview"}, + React.createElement("ul", {className: "list-group"}, + children + ) + ) + ); + } +}); + +module.exports = TreeView; + +var TreeNode = React.createClass({displayName: "TreeNode", + + propTypes: { + onLineClicked: React.PropTypes.func, + attributes: React.PropTypes.object + }, + + getInitialState: function () { + var node = this.props.node; + return { + expanded: (node.state && node.state.hasOwnProperty('expanded')) ? + node.state.expanded : + (this.props.level < this.props.options.levels), + selected: (node.state && node.state.hasOwnProperty('selected')) ? + node.state.selected : + false + } + }, + + toggleExpanded: function (id, event) { + this.setState({expanded: !this.state.expanded}); + event.stopPropagation(); + }, + + toggleSelected: function (id, event) { + this.setState({selected: !this.state.selected}); + event.stopPropagation(); + }, + + handleLineClicked: function (nodeId, evt) { + + // SELECT LINE + this.toggleSelected(nodeId, $.extend(true, {}, evt)); + // DEV CLICK + this.props.onLineClicked($.extend(true, {}, evt)); + evt.stopPropagation(); + }, + + render: function () { + + var node = this.props.node; + var options = this.props.options; + + var style; + if (!this.props.visible) { + style = { + display: 'none' + }; + } + else { + + if (options.highlightSelected && this.state.selected) { + style = { + color: options.selectedColor, + backgroundColor: options.selectedBackColor + }; + } + else { + style = { + color: node.color || options.color, + backgroundColor: node.backColor || options.backColor + }; + } + + if (!options.showBorder) { + style.border = 'none'; + } + else if (options.borderColor) { + style.border = '1px solid ' + options.borderColor; + } + } + + var indents = []; + for (var i = 0; i < this.props.level - 1; i++) { + indents.push(React.createElement("span", { + className: "indent", + key: i})); + } + + var attrs = {}; + if (this.props.attributes !== undefined) { + for( var i in this.props.attributes) { + if (node[this.props.attributes[i]] !== undefined) { + attrs[i] = node[this.props.attributes[i]]; + } + }; + } + + var expandCollapseIcon; + if (node.nodes) { + if (!this.state.expanded) { + expandCollapseIcon = ( + React.createElement("span", {className: options.expandIcon, + onClick: this.toggleExpanded.bind(this, node.nodeId)} + ) + ); + } + else { + expandCollapseIcon = ( + React.createElement("span", {className: options.collapseIcon, + onClick: this.toggleExpanded.bind(this, node.nodeId)} + ) + ); + } + } + else { + expandCollapseIcon = ( + React.createElement("span", {className: options.emptyIcon}) + ); + } + + var nodeIcon = ( + React.createElement("span", {className: "icon"}, + React.createElement("i", {className: node.icon || options.nodeIcon}) + ) + ); + + var nodeText; + if (options.enableLinks) { + nodeText = ( + React.createElement("a", {href: node.href/*style="color:inherit;"*/}, + node.text + ) + ); + } + else { + nodeText = ( + React.createElement("span", null, node.text) + ); + } + + var badges; + if (options.showTags && node.tags) { + badges = node.tags.map(function (tag, index) { + return ( + React.createElement("span", { + className: "badge", + key: index}, + tag + ) + ); + }); + } + + var children = []; + if (node.nodes) { + node.nodes.forEach(function (node, index) { + children.push(React.createElement(TreeNode, {node: node, + level: this.props.level + 1, + visible: this.state.expanded && this.props.visible, + options: options, + key: index, + onLineClicked: this.props.onLineClicked, + attributes: this.props.attributes})); + }, this); + } + + return ( + React.createElement("li", React.__spread({className: "list-group-item", + style: style, + onClick: this.handleLineClicked.bind(this, node.nodeId), + key: node.nodeId}, + attrs), + indents, + expandCollapseIcon, + nodeIcon, + nodeText, + badges, + children + ) + ); + } +}); From bf1e9f9271062a0c60e34c22deae2a140dbfe40a Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Mon, 20 Apr 2015 16:27:49 +0200 Subject: [PATCH 13/26] TreeView ADD prop boolean iSelectionExclusive (default false): When a line is selected, others are unselected --- dist/js/react-bootstrap-treeview.js | 93 ++++++++++++++++++++++------- example/js/example.js | 9 ++- package.json | 2 +- src/react-bootstrap-treeview.jsx | 93 ++++++++++++++++++++++------- 4 files changed, 149 insertions(+), 48 deletions(-) diff --git a/dist/js/react-bootstrap-treeview.js b/dist/js/react-bootstrap-treeview.js index 8997a6c..e92fe0d 100644 --- a/dist/js/react-bootstrap-treeview.js +++ b/dist/js/react-bootstrap-treeview.js @@ -17,6 +17,7 @@ var TreeView = React.createClass({displayName: "TreeView", enableLinks: React.PropTypes.bool, highlightSelected: React.PropTypes.bool, + isSelectionExclusive: React.PropTypes.bool, showBorder: React.PropTypes.bool, showTags: React.PropTypes.bool, @@ -44,6 +45,7 @@ var TreeView = React.createClass({displayName: "TreeView", enableLinks: false, highlightSelected: true, + isSelectionExclusive: false, showBorder: true, showTags: false, @@ -53,6 +55,13 @@ var TreeView = React.createClass({displayName: "TreeView", }, nodes: [], + nodesSelected: {}, + + getInitialState: function () { + this.setNodeId({nodes: this.props.data}); + + return {nodesSelected: this.nodesSelected}; + }, setNodeId: function (node) { @@ -60,32 +69,56 @@ var TreeView = React.createClass({displayName: "TreeView", node.nodes.forEach(function checkStates(node) { node.nodeId = this.nodes.length; + this.nodesSelected[node.nodeId] = false; this.nodes.push(node); this.setNodeId(node); }, this); }, - handleLineClicked: function (evt) { + /** + * Line clicked from TreeNode + * @param nodeId: node ID + * @param evt: event + */ + handleLineClicked: function (nodeId, evt) { if (this.props.onLineClicked !== undefined) { // CLONE EVT + CALLBACK DEV this.props.onLineClicked($.extend(true, {}, evt)); } + + var matrice = this.state.nodesSelected; + // Exclusive selection + if (this.props.isSelectionExclusive) { + // Unselection + for (var i in matrice) { + matrice[i] = false; + } + } + // TOGGLE SELECTION OF CURRENT NODE + matrice[nodeId] = !this.state.nodesSelected[nodeId]; + + this.setState({nodesSelected: matrice}); }, render: function () { - this.setNodeId({nodes: this.props.data}); - var children = []; if (this.props.data) { this.props.data.forEach(function (node, index) { - children.push(React.createElement(TreeNode, {node: node, - level: 1, - visible: true, - options: this.props, - key: index, - onLineClicked: this.handleLineClicked, - attributes: this.props.treeNodeAttributes})); + + // SELECTION + node.selected = (this.state.nodesSelected[node.nodeId]); + + children.push( + React.createElement(TreeNode, { + node: node, + level: 1, + visible: true, + options: this.props, + key: index, + onLineClicked: this.handleLineClicked, + attributes: this.props.treeNodeAttributes, + nodesSelected: this.state.nodesSelected})); }.bind(this)); } @@ -104,8 +137,10 @@ module.exports = TreeView; var TreeNode = React.createClass({displayName: "TreeNode", propTypes: { + node: React.PropTypes.object.isRequired, onLineClicked: React.PropTypes.func, - attributes: React.PropTypes.object + attributes: React.PropTypes.object, + nodesSelected: React.PropTypes.object.isRequired }, getInitialState: function () { @@ -120,13 +155,21 @@ var TreeNode = React.createClass({displayName: "TreeNode", } }, + componentWillUpdate: function (np, ns) { + ns.selected = np.node.selected; + + }, + toggleExpanded: function (id, event) { this.setState({expanded: !this.state.expanded}); event.stopPropagation(); }, toggleSelected: function (id, event) { - this.setState({selected: !this.state.selected}); + // Exclusive selection + if (!this.props.isSelectionExclusive) { + this.setState({selected: !this.state.selected}); + } event.stopPropagation(); }, @@ -135,7 +178,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", // SELECT LINE this.toggleSelected(nodeId, $.extend(true, {}, evt)); // DEV CLICK - this.props.onLineClicked($.extend(true, {}, evt)); + this.props.onLineClicked(nodeId, $.extend(true, {}, evt)); evt.stopPropagation(); }, @@ -182,11 +225,12 @@ var TreeNode = React.createClass({displayName: "TreeNode", var attrs = {}; if (this.props.attributes !== undefined) { - for( var i in this.props.attributes) { + for (var i in this.props.attributes) { if (node[this.props.attributes[i]] !== undefined) { attrs[i] = node[this.props.attributes[i]]; } - }; + } + ; } var expandCollapseIcon; @@ -248,13 +292,18 @@ var TreeNode = React.createClass({displayName: "TreeNode", var children = []; if (node.nodes) { node.nodes.forEach(function (node, index) { - children.push(React.createElement(TreeNode, {node: node, - level: this.props.level + 1, - visible: this.state.expanded && this.props.visible, - options: options, - key: index, - onLineClicked: this.props.onLineClicked, - attributes: this.props.attributes})); + // SELECTION + node.selected = (this.props.nodesSelected[node.nodeId]); + children.push( + React.createElement(TreeNode, { + node: node, + level: this.props.level + 1, + visible: this.state.expanded && this.props.visible, + options: options, + key: index, + onLineClicked: this.props.onLineClicked, + attributes: this.props.attributes, + nodesSelected: this.props.nodesSelected})); }, this); } diff --git a/example/js/example.js b/example/js/example.js index a87ef34..9ecbf23 100644 --- a/example/js/example.js +++ b/example/js/example.js @@ -43,8 +43,9 @@ var data = [ } ]; -var test = function () { - console.log('click'); +var test = function (evt) { + + console.log('click nodeID ' + $(evt.currentTarget).data('id')); } // DOM loaded @@ -54,7 +55,9 @@ $(function () { data={data} color={"#428bca"} onLineClicked={test} - treeNodeAttributes={{'data-id': 'id'}}/>, + treeNodeAttributes={{'data-id': 'id'}} + isSelectionExclusive={true} + levels={0} />, document.getElementById('treeview') ); }) diff --git a/package.json b/package.json index 3285e50..052e60c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-bootstrap-treeview", "description": "React Tree View for Twitter Bootstrap", - "version": "0.1.0", + "version": "0.2.0", "homepage": "https://github.com/jonmiles/react-bootstrap-treeview", "author": { "name": "Jonathan Miles" diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index cbbf9e2..48909dd 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -17,6 +17,7 @@ var TreeView = React.createClass({ enableLinks: React.PropTypes.bool, highlightSelected: React.PropTypes.bool, + isSelectionExclusive: React.PropTypes.bool, showBorder: React.PropTypes.bool, showTags: React.PropTypes.bool, @@ -44,6 +45,7 @@ var TreeView = React.createClass({ enableLinks: false, highlightSelected: true, + isSelectionExclusive: false, showBorder: true, showTags: false, @@ -53,6 +55,13 @@ var TreeView = React.createClass({ }, nodes: [], + nodesSelected: {}, + + getInitialState: function () { + this.setNodeId({nodes: this.props.data}); + + return {nodesSelected: this.nodesSelected}; + }, setNodeId: function (node) { @@ -60,32 +69,56 @@ var TreeView = React.createClass({ node.nodes.forEach(function checkStates(node) { node.nodeId = this.nodes.length; + this.nodesSelected[node.nodeId] = false; this.nodes.push(node); this.setNodeId(node); }, this); }, - handleLineClicked: function (evt) { + /** + * Line clicked from TreeNode + * @param nodeId: node ID + * @param evt: event + */ + handleLineClicked: function (nodeId, evt) { if (this.props.onLineClicked !== undefined) { // CLONE EVT + CALLBACK DEV this.props.onLineClicked($.extend(true, {}, evt)); } + + var matrice = this.state.nodesSelected; + // Exclusive selection + if (this.props.isSelectionExclusive) { + // Unselection + for (var i in matrice) { + matrice[i] = false; + } + } + // TOGGLE SELECTION OF CURRENT NODE + matrice[nodeId] = !this.state.nodesSelected[nodeId]; + + this.setState({nodesSelected: matrice}); }, render: function () { - this.setNodeId({nodes: this.props.data}); - var children = []; if (this.props.data) { this.props.data.forEach(function (node, index) { - children.push(); + + // SELECTION + node.selected = (this.state.nodesSelected[node.nodeId]); + + children.push( + ); }.bind(this)); } @@ -104,8 +137,10 @@ module.exports = TreeView; var TreeNode = React.createClass({ propTypes: { + node: React.PropTypes.object.isRequired, onLineClicked: React.PropTypes.func, - attributes: React.PropTypes.object + attributes: React.PropTypes.object, + nodesSelected: React.PropTypes.object.isRequired }, getInitialState: function () { @@ -120,13 +155,21 @@ var TreeNode = React.createClass({ } }, + componentWillUpdate: function (np, ns) { + ns.selected = np.node.selected; + + }, + toggleExpanded: function (id, event) { this.setState({expanded: !this.state.expanded}); event.stopPropagation(); }, toggleSelected: function (id, event) { - this.setState({selected: !this.state.selected}); + // Exclusive selection + if (!this.props.isSelectionExclusive) { + this.setState({selected: !this.state.selected}); + } event.stopPropagation(); }, @@ -135,7 +178,7 @@ var TreeNode = React.createClass({ // SELECT LINE this.toggleSelected(nodeId, $.extend(true, {}, evt)); // DEV CLICK - this.props.onLineClicked($.extend(true, {}, evt)); + this.props.onLineClicked(nodeId, $.extend(true, {}, evt)); evt.stopPropagation(); }, @@ -182,11 +225,12 @@ var TreeNode = React.createClass({ var attrs = {}; if (this.props.attributes !== undefined) { - for( var i in this.props.attributes) { + for (var i in this.props.attributes) { if (node[this.props.attributes[i]] !== undefined) { attrs[i] = node[this.props.attributes[i]]; } - }; + } + ; } var expandCollapseIcon; @@ -248,13 +292,18 @@ var TreeNode = React.createClass({ var children = []; if (node.nodes) { node.nodes.forEach(function (node, index) { - children.push(); + // SELECTION + node.selected = (this.props.nodesSelected[node.nodeId]); + children.push( + ); }, this); } From bf4e20e3436634e62edd788e27037fba753ce228 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Mon, 20 Apr 2015 16:32:08 +0200 Subject: [PATCH 14/26] UPDATE readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index b79d592..0fb1654 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,12 @@ Boolean. Default: true Whether or not to highlight the selected node. +#### isSelectionExclusive +Boolean, Default false + +If true, when a line is selected, others are unselected + + #### levels Integer. Default: 2 From b56c378f194a5f3e19f46a1eb3e6e6918353f412 Mon Sep 17 00:00:00 2001 From: PEREZ Vivian Date: Mon, 20 Apr 2015 16:36:57 +0200 Subject: [PATCH 15/26] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 052e60c..12a668d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-bootstrap-treeview", "description": "React Tree View for Twitter Bootstrap", - "version": "0.2.0", + "version": "0.2.1", "homepage": "https://github.com/jonmiles/react-bootstrap-treeview", "author": { "name": "Jonathan Miles" From 6814ee1908d6a3dfe7349fdb710c40822bfc9433 Mon Sep 17 00:00:00 2001 From: Yann Plantevin Date: Tue, 28 Apr 2015 10:51:36 +0200 Subject: [PATCH 16/26] Update react-bootstrap-treeview.jsx key unique dans TreeView et TreeNiode. On se base sur le nodeId unique sur tous les niveaux. --- src/react-bootstrap-treeview.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index 48909dd..3960aa6 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -115,7 +115,7 @@ var TreeView = React.createClass({ level={1} visible={true} options={this.props} - key={index} + key={node.nodeId} onLineClicked={this.handleLineClicked} attributes={this.props.treeNodeAttributes} nodesSelected={this.state.nodesSelected} />); @@ -300,7 +300,7 @@ var TreeNode = React.createClass({ level={this.props.level + 1} visible={this.state.expanded && this.props.visible} options={options} - key={index} + key={node.nodeId} onLineClicked={this.props.onLineClicked} attributes={this.props.attributes} nodesSelected={this.props.nodesSelected} />); From 6dbe0b77110cc6512c9d336a95f19d6673489860 Mon Sep 17 00:00:00 2001 From: Yann Plantevin Date: Tue, 28 Apr 2015 10:51:58 +0200 Subject: [PATCH 17/26] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 12a668d..a41adc3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-bootstrap-treeview", "description": "React Tree View for Twitter Bootstrap", - "version": "0.2.1", + "version": "0.2.2", "homepage": "https://github.com/jonmiles/react-bootstrap-treeview", "author": { "name": "Jonathan Miles" From ca1c18dab6d5cba5eee398c17032b0ba1d0600e2 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Tue, 28 Apr 2015 11:07:16 +0200 Subject: [PATCH 18/26] =?UTF-8?q?GRUNT=20des=20modifs=20effectu=C3=A9es=20?= =?UTF-8?q?dans=20le=20pr=C3=A9c=C3=A9dent=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/js/react-bootstrap-treeview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/js/react-bootstrap-treeview.js b/dist/js/react-bootstrap-treeview.js index e92fe0d..e039883 100644 --- a/dist/js/react-bootstrap-treeview.js +++ b/dist/js/react-bootstrap-treeview.js @@ -115,7 +115,7 @@ var TreeView = React.createClass({displayName: "TreeView", level: 1, visible: true, options: this.props, - key: index, + key: node.nodeId, onLineClicked: this.handleLineClicked, attributes: this.props.treeNodeAttributes, nodesSelected: this.state.nodesSelected})); @@ -300,7 +300,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", level: this.props.level + 1, visible: this.state.expanded && this.props.visible, options: options, - key: index, + key: node.nodeId, onLineClicked: this.props.onLineClicked, attributes: this.props.attributes, nodesSelected: this.props.nodesSelected})); From 072a7d0c6f401d952195f2b0630ade3735d8a167 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Tue, 28 Apr 2015 11:13:26 +0200 Subject: [PATCH 19/26] TreeNaode : suppression du keu au niveau du LI --- dist/js/react-bootstrap-treeview.js | 3 +-- src/react-bootstrap-treeview.jsx | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dist/js/react-bootstrap-treeview.js b/dist/js/react-bootstrap-treeview.js index e039883..1e57f25 100644 --- a/dist/js/react-bootstrap-treeview.js +++ b/dist/js/react-bootstrap-treeview.js @@ -310,8 +310,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", return ( React.createElement("li", React.__spread({className: "list-group-item", style: style, - onClick: this.handleLineClicked.bind(this, node.nodeId), - key: node.nodeId}, + onClick: this.handleLineClicked.bind(this, node.nodeId)}, attrs), indents, expandCollapseIcon, diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index 3960aa6..c0966c8 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -311,7 +311,6 @@ var TreeNode = React.createClass({
  • {indents} {expandCollapseIcon} From 4494bdefd26825ab05480af311b97020621f4a69 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Wed, 3 Jun 2015 17:34:41 +0200 Subject: [PATCH 20/26] =?UTF-8?q?Treenode:=20-=20Possibilit=C3=A9=20de=20r?= =?UTF-8?q?=C3=A9duire=20le=20texte=20-=20Possibilit=C3=A9=20de=20ne=20pas?= =?UTF-8?q?=20mettre=20de=20glyphicon=20-=20Possibilit=C3=A9=20d'avoir=20d?= =?UTF-8?q?es=20badges=20qui=20calculent=20automatiquement=20le=20nombre?= =?UTF-8?q?=20de=20fils=20-=20Les=20badges=20sont=20affich=C3=A9s=20correc?= =?UTF-8?q?tement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/css/react-bootstrap-treeview.css | 17 ++--- dist/js/react-bootstrap-treeview.js | 90 ++++++++++++++++++-------- example/js/example.js | 10 ++- src/react-bootstrap-treeview.css | 17 ++--- src/react-bootstrap-treeview.jsx | 92 ++++++++++++++++++--------- 5 files changed, 152 insertions(+), 74 deletions(-) diff --git a/dist/css/react-bootstrap-treeview.css b/dist/css/react-bootstrap-treeview.css index 3f249e8..d8f03ec 100644 --- a/dist/css/react-bootstrap-treeview.css +++ b/dist/css/react-bootstrap-treeview.css @@ -1,19 +1,20 @@ .treeview .list-group-item { - cursor: pointer; + cursor: pointer; } -.treeview span { - width: 1rem; - height: 1rem; +.treeview span:not(.badge) { + width: 1rem; + height: 1rem; } .treeview span.indent { - margin-left: 10px; - margin-right: 10px; + margin-left: 6px; + margin-right: 6px; } .treeview span.icon { - margin-left: 10px; - margin-right: 5px; + margin-left: 0px; + margin-right: 6px; } + diff --git a/dist/js/react-bootstrap-treeview.js b/dist/js/react-bootstrap-treeview.js index 1e57f25..2ca3f0c 100644 --- a/dist/js/react-bootstrap-treeview.js +++ b/dist/js/react-bootstrap-treeview.js @@ -33,7 +33,7 @@ var TreeView = React.createClass({displayName: "TreeView", expandIcon: 'glyphicon glyphicon-plus', collapseIcon: 'glyphicon glyphicon-minus', - emptyIcon: 'glyphicon', + emptyIcon: '', nodeIcon: 'glyphicon glyphicon-stop', color: undefined, @@ -42,6 +42,7 @@ var TreeView = React.createClass({displayName: "TreeView", onhoverColor: '#F5F5F5', // TODO Not implemented yet, investigate radium.js 'A toolchain for React component styling' selectedColor: '#FFFFFF', selectedBackColor: '#428bca', + classText: '', enableLinks: false, highlightSelected: true, @@ -140,7 +141,8 @@ var TreeNode = React.createClass({displayName: "TreeNode", node: React.PropTypes.object.isRequired, onLineClicked: React.PropTypes.func, attributes: React.PropTypes.object, - nodesSelected: React.PropTypes.object.isRequired + nodesSelected: React.PropTypes.object.isRequired, + options: React.PropTypes.object }, getInitialState: function () { @@ -187,12 +189,14 @@ var TreeNode = React.createClass({displayName: "TreeNode", var node = this.props.node; var options = this.props.options; + // Noeud invisible var style; if (!this.props.visible) { style = { display: 'none' }; } + // Noeud visible else { if (options.highlightSelected && this.state.selected) { @@ -216,6 +220,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", } } + // Indentation var indents = []; for (var i = 0; i < this.props.level - 1; i++) { indents.push(React.createElement("span", { @@ -223,6 +228,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", key: i})); } + // Custom attributes var attrs = {}; if (this.props.attributes !== undefined) { for (var i in this.props.attributes) { @@ -234,18 +240,24 @@ var TreeNode = React.createClass({displayName: "TreeNode", } var expandCollapseIcon; + // There are children if (node.nodes) { + // Collapse if (!this.state.expanded) { expandCollapseIcon = ( - React.createElement("span", {className: options.expandIcon, - onClick: this.toggleExpanded.bind(this, node.nodeId)} + React.createElement("span", {className: "icon plusmoins"}, + React.createElement("i", {className: options.expandIcon, + onClick: this.toggleExpanded.bind(this, node.nodeId)} + ) ) ); } + // Expanded else { expandCollapseIcon = ( - React.createElement("span", {className: options.collapseIcon, - onClick: this.toggleExpanded.bind(this, node.nodeId)} + React.createElement("span", {className: "icon"}, + React.createElement("i", {className: options.collapseIcon, + onClick: this.toggleExpanded.bind(this, node.nodeId)}) ) ); } @@ -256,39 +268,61 @@ var TreeNode = React.createClass({displayName: "TreeNode", ); } - var nodeIcon = ( - React.createElement("span", {className: "icon"}, - React.createElement("i", {className: node.icon || options.nodeIcon}) - ) - ); + // Icon (if no nodes children) + var nodeIcon = ''; + if (options.nodeIcon !== '' && !node.nodes) { + nodeIcon = ( + React.createElement("span", {className: "icon"}, + React.createElement("i", {className: node.icon || options.nodeIcon}) + ) + ); + } + + var badges; + if (options.showTags) { + // If tags are defined in the data + if (node.tags) { + badges = node.tags.map(function (tag, index) { + return ( + React.createElement("span", { + className: "badge", + key: index}, + tag + ) + ); + }); + } + // No tags in data => number of children + else { + badges = ( + React.createElement("span", { + className: "badge"}, + node.nodes ? node.nodes.length : 0 + ) + ); + } + } var nodeText; if (options.enableLinks) { nodeText = ( - React.createElement("a", {href: node.href/*style="color:inherit;"*/}, - node.text + React.createElement("span", { + className: options.classText}, + React.createElement("a", {href: node.href/*style="color:inherit;"*/}, + node.text + ) ) ); } else { nodeText = ( - React.createElement("span", null, node.text) + React.createElement("span", { + className: options.classText}, + node.text + ) ); } - var badges; - if (options.showTags && node.tags) { - badges = node.tags.map(function (tag, index) { - return ( - React.createElement("span", { - className: "badge", - key: index}, - tag - ) - ); - }); - } - var children = []; if (node.nodes) { node.nodes.forEach(function (node, index) { @@ -316,7 +350,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", expandCollapseIcon, nodeIcon, nodeText, - badges, + badges, children ) ); diff --git a/example/js/example.js b/example/js/example.js index 9ecbf23..0f3b266 100644 --- a/example/js/example.js +++ b/example/js/example.js @@ -5,6 +5,7 @@ var data = [ { text: 'Parent 1', id: '1', + href: '#', nodes: [ { text: 'Child 1', @@ -57,7 +58,14 @@ $(function () { onLineClicked={test} treeNodeAttributes={{'data-id': 'id'}} isSelectionExclusive={true} - levels={0} />, + levels={0} + nodeIcon= "glyphicon glyphicon-stop small" + enableLinks={false} + expandIcon= "glyphicon glyphicon-plus small" + collapseIcon= "glyphicon glyphicon-minus small" + classText="small" + showTags={true} + />, document.getElementById('treeview') ); }) diff --git a/src/react-bootstrap-treeview.css b/src/react-bootstrap-treeview.css index 3f249e8..d8f03ec 100644 --- a/src/react-bootstrap-treeview.css +++ b/src/react-bootstrap-treeview.css @@ -1,19 +1,20 @@ .treeview .list-group-item { - cursor: pointer; + cursor: pointer; } -.treeview span { - width: 1rem; - height: 1rem; +.treeview span:not(.badge) { + width: 1rem; + height: 1rem; } .treeview span.indent { - margin-left: 10px; - margin-right: 10px; + margin-left: 6px; + margin-right: 6px; } .treeview span.icon { - margin-left: 10px; - margin-right: 5px; + margin-left: 0px; + margin-right: 6px; } + diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index c0966c8..d9cad57 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -33,7 +33,7 @@ var TreeView = React.createClass({ expandIcon: 'glyphicon glyphicon-plus', collapseIcon: 'glyphicon glyphicon-minus', - emptyIcon: 'glyphicon', + emptyIcon: '', nodeIcon: 'glyphicon glyphicon-stop', color: undefined, @@ -42,6 +42,7 @@ var TreeView = React.createClass({ onhoverColor: '#F5F5F5', // TODO Not implemented yet, investigate radium.js 'A toolchain for React component styling' selectedColor: '#FFFFFF', selectedBackColor: '#428bca', + classText: '', enableLinks: false, highlightSelected: true, @@ -140,7 +141,8 @@ var TreeNode = React.createClass({ node: React.PropTypes.object.isRequired, onLineClicked: React.PropTypes.func, attributes: React.PropTypes.object, - nodesSelected: React.PropTypes.object.isRequired + nodesSelected: React.PropTypes.object.isRequired, + options: React.PropTypes.object }, getInitialState: function () { @@ -187,12 +189,14 @@ var TreeNode = React.createClass({ var node = this.props.node; var options = this.props.options; + // Noeud invisible var style; if (!this.props.visible) { style = { display: 'none' }; } + // Noeud visible else { if (options.highlightSelected && this.state.selected) { @@ -216,6 +220,7 @@ var TreeNode = React.createClass({ } } + // Indentation var indents = []; for (var i = 0; i < this.props.level - 1; i++) { indents.push(); } + // Custom attributes var attrs = {}; if (this.props.attributes !== undefined) { for (var i in this.props.attributes) { @@ -234,18 +240,24 @@ var TreeNode = React.createClass({ } var expandCollapseIcon; + // There are children if (node.nodes) { + // Collapse if (!this.state.expanded) { expandCollapseIcon = ( - + + + ); } + // Expanded else { expandCollapseIcon = ( - + + ); } @@ -256,39 +268,61 @@ var TreeNode = React.createClass({ ); } - var nodeIcon = ( - - - - ); + // Icon (if no nodes children) + var nodeIcon = ''; + if (options.nodeIcon !== '' && !node.nodes) { + nodeIcon = ( + + + + ); + } + + var badges; + if (options.showTags) { + // If tags are defined in the data + if (node.tags) { + badges = node.tags.map(function (tag, index) { + return ( + + {tag} + + ); + }); + } + // No tags in data => number of children + else { + badges = ( + + {node.nodes ? node.nodes.length : 0} + + ); + } + } var nodeText; if (options.enableLinks) { nodeText = ( - - {node.text} - + + + {node.text} + + ); } else { nodeText = ( - {node.text} + + {node.text} + ); } - var badges; - if (options.showTags && node.tags) { - badges = node.tags.map(function (tag, index) { - return ( - - {tag} - - ); - }); - } - var children = []; if (node.nodes) { node.nodes.forEach(function (node, index) { @@ -316,7 +350,7 @@ var TreeNode = React.createClass({ {expandCollapseIcon} {nodeIcon} {nodeText} - {badges} + {badges} {children}
  • ); From 70a7678f48ee97844a439f07a281d2c0fce338c4 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Wed, 3 Jun 2015 17:35:39 +0200 Subject: [PATCH 21/26] Version 0.2.3 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a41adc3..49bc450 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "react-bootstrap-treeview", "description": "React Tree View for Twitter Bootstrap", - "version": "0.2.2", + "version": "0.2.3", "homepage": "https://github.com/jonmiles/react-bootstrap-treeview", "author": { - "name": "Jonathan Miles" + "name": "Elipce" }, "repository": { "type": "git", From 0bb6fe0b98cd18958d9dcb2d14485187f22c1b0e Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Thu, 11 Jun 2015 17:26:51 +0200 Subject: [PATCH 22/26] Badge Pas de badge 0 sur les feuilles --- dist/js/react-bootstrap-treeview.js | 19 ++++++++++++------- src/react-bootstrap-treeview.jsx | 19 ++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/dist/js/react-bootstrap-treeview.js b/dist/js/react-bootstrap-treeview.js index 2ca3f0c..6caa636 100644 --- a/dist/js/react-bootstrap-treeview.js +++ b/dist/js/react-bootstrap-treeview.js @@ -18,6 +18,7 @@ var TreeView = React.createClass({displayName: "TreeView", enableLinks: React.PropTypes.bool, highlightSelected: React.PropTypes.bool, isSelectionExclusive: React.PropTypes.bool, + underlineLeafOnly: React.PropTypes.bool, showBorder: React.PropTypes.bool, showTags: React.PropTypes.bool, @@ -47,6 +48,7 @@ var TreeView = React.createClass({displayName: "TreeView", enableLinks: false, highlightSelected: true, isSelectionExclusive: false, + underlineLeafOnly: false, showBorder: true, showTags: false, @@ -278,7 +280,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", ); } - var badges; + var badges = ''; if (options.showTags) { // If tags are defined in the data if (node.tags) { @@ -294,12 +296,15 @@ var TreeNode = React.createClass({displayName: "TreeNode", } // No tags in data => number of children else { - badges = ( - React.createElement("span", { - className: "badge"}, - node.nodes ? node.nodes.length : 0 - ) - ); + // Children exist + if(node.nodes) { + badges = ( + React.createElement("span", { + className: "badge"}, + node.nodes.length + ) + ); + } } } diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index d9cad57..d4a320f 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -18,6 +18,7 @@ var TreeView = React.createClass({ enableLinks: React.PropTypes.bool, highlightSelected: React.PropTypes.bool, isSelectionExclusive: React.PropTypes.bool, + underlineLeafOnly: React.PropTypes.bool, showBorder: React.PropTypes.bool, showTags: React.PropTypes.bool, @@ -47,6 +48,7 @@ var TreeView = React.createClass({ enableLinks: false, highlightSelected: true, isSelectionExclusive: false, + underlineLeafOnly: false, showBorder: true, showTags: false, @@ -278,7 +280,7 @@ var TreeNode = React.createClass({ ); } - var badges; + var badges = ''; if (options.showTags) { // If tags are defined in the data if (node.tags) { @@ -294,12 +296,15 @@ var TreeNode = React.createClass({ } // No tags in data => number of children else { - badges = ( - - {node.nodes ? node.nodes.length : 0} - - ); + // Children exist + if(node.nodes) { + badges = ( + + {node.nodes.length} + + ); + } } } From 2520329e448d437283ce2fc66c9941e7d49425b7 Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 12 Jun 2015 10:56:46 +0200 Subject: [PATCH 23/26] =?UTF-8?q?Improvments=20-=20Si=20on=20est=20sur=20u?= =?UTF-8?q?ne=20feuille,=20pas=20de=20badge=20-=20mode=20selectLeafOnly:?= =?UTF-8?q?=20ne=20s=C3=A9lectionne=20visuellement=20qui=20si=20l'=C3=A9l?= =?UTF-8?q?=C3=A9ment=20cliqu=C3=A9=20est=20une=20feuille=20-=20collapse?= =?UTF-8?q?=20/=20expand=20sur=20toute=20la=20ligne,=20pas=20seulement=20s?= =?UTF-8?q?ur=20le=20+=20ou=20-=20-=20gestion=20d'un=20glyph=20dif=C3=A9re?= =?UTF-8?q?nt=20si=20ligne=20selected:=20nodeIconSelected?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/js/react-bootstrap-treeview.js | 88 +++++++++++++++++++++++------ example/js/example.js | 6 +- package.json | 2 +- src/react-bootstrap-treeview.jsx | 88 +++++++++++++++++++++++------ 4 files changed, 147 insertions(+), 37 deletions(-) diff --git a/dist/js/react-bootstrap-treeview.js b/dist/js/react-bootstrap-treeview.js index 6caa636..3fd1444 100644 --- a/dist/js/react-bootstrap-treeview.js +++ b/dist/js/react-bootstrap-treeview.js @@ -7,6 +7,7 @@ var TreeView = React.createClass({displayName: "TreeView", collapseIcon: React.PropTypes.string, emptyIcon: React.PropTypes.string, nodeIcon: React.PropTypes.string, + nodeIconSelected: React.PropTypes.string, color: React.PropTypes.string, backColor: React.PropTypes.string, @@ -36,7 +37,7 @@ var TreeView = React.createClass({displayName: "TreeView", collapseIcon: 'glyphicon glyphicon-minus', emptyIcon: '', nodeIcon: 'glyphicon glyphicon-stop', - + nodeIconSelected: 'glyphicon glyphicon-eye-open', color: undefined, backColor: undefined, borderColor: undefined, @@ -78,6 +79,22 @@ var TreeView = React.createClass({displayName: "TreeView", }, this); }, + /** + * Find a node by nodeId + * @param nodeId: node ID + * @returns {{}} node object or {} + */ + findNode: function (nodeId) { + var find = {}; + this.nodes.forEach(function (node) { + // Node find + if (node.nodeId == nodeId) { + find = node; + } + }); + return find; + }, + /** * Line clicked from TreeNode * @param nodeId: node ID @@ -92,13 +109,40 @@ var TreeView = React.createClass({displayName: "TreeView", var matrice = this.state.nodesSelected; // Exclusive selection if (this.props.isSelectionExclusive) { - // Unselection - for (var i in matrice) { - matrice[i] = false; + + // Underline only if the element is a leaf + if (this.props.underlineLeafOnly) { + var currentNode = this.findNode(nodeId); + + // Node clicked is a leaf + if (!currentNode.nodes) { + // Unselection + for (var i in matrice) { + matrice[i] = false; + } + matrice[nodeId] = !this.state.nodesSelected[nodeId]; + } + // Node clicked is a parentNode + else { + // Simulation click expand/collapse icon + $(evt.currentTarget).find('[data-target=plusmoins]').click(); + } + } + // Underline on all nodes + else { + // Unselection + for (var i in matrice) { + matrice[i] = false; + } + // TOGGLE SELECTION OF CURRENT NODE + matrice[nodeId] = !this.state.nodesSelected[nodeId]; } } - // TOGGLE SELECTION OF CURRENT NODE - matrice[nodeId] = !this.state.nodesSelected[nodeId]; + // MULTIPLE SELECTION + else { + // TOGGLE SELECTION OF CURRENT NODE + matrice[nodeId] = !this.state.nodesSelected[nodeId]; + } this.setState({nodesSelected: matrice}); }, @@ -248,9 +292,10 @@ var TreeNode = React.createClass({displayName: "TreeNode", if (!this.state.expanded) { expandCollapseIcon = ( React.createElement("span", {className: "icon plusmoins"}, - React.createElement("i", {className: options.expandIcon, - onClick: this.toggleExpanded.bind(this, node.nodeId)} - ) + React.createElement("i", { + className: options.expandIcon, + onClick: this.toggleExpanded.bind(this, node.nodeId), + "data-target": "plusmoins"}) ) ); } @@ -258,24 +303,33 @@ var TreeNode = React.createClass({displayName: "TreeNode", else { expandCollapseIcon = ( React.createElement("span", {className: "icon"}, - React.createElement("i", {className: options.collapseIcon, - onClick: this.toggleExpanded.bind(this, node.nodeId)}) + React.createElement("i", { + className: options.collapseIcon, + onClick: this.toggleExpanded.bind(this, node.nodeId), + "data-target": "plusmoins"}) ) ); } } + // Node is a leaf else { expandCollapseIcon = ( React.createElement("span", {className: options.emptyIcon}) ); } - // Icon (if no nodes children) + // Icon (if current node is a leaf) var nodeIcon = ''; if (options.nodeIcon !== '' && !node.nodes) { + console.log('node %o %o %o',node, this.state.selected, options); + var iTarget = (React.createElement("i", {className: node.icon || options.nodeIcon})); + // Current node selected + if (this.state.selected) { + iTarget = (React.createElement("i", {className: options.nodeIconSelected})) + } nodeIcon = ( React.createElement("span", {className: "icon"}, - React.createElement("i", {className: node.icon || options.nodeIcon}) + iTarget ) ); } @@ -297,7 +351,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", // No tags in data => number of children else { // Children exist - if(node.nodes) { + if (node.nodes) { badges = ( React.createElement("span", { className: "badge"}, @@ -321,10 +375,10 @@ var TreeNode = React.createClass({displayName: "TreeNode", } else { nodeText = ( - React.createElement("span", { - className: options.classText}, + React.createElement("span", { + className: options.classText}, node.text - ) + ) ); } diff --git a/example/js/example.js b/example/js/example.js index 0f3b266..0e4f0a3 100644 --- a/example/js/example.js +++ b/example/js/example.js @@ -46,7 +46,7 @@ var data = [ var test = function (evt) { - console.log('click nodeID ' + $(evt.currentTarget).data('id')); + //console.log('click nodeID ' + $(evt.currentTarget).data('id')); } // DOM loaded @@ -58,8 +58,10 @@ $(function () { onLineClicked={test} treeNodeAttributes={{'data-id': 'id'}} isSelectionExclusive={true} + underlineLeafOnly={true} levels={0} - nodeIcon= "glyphicon glyphicon-stop small" + nodeIcon= "glyphicon glyphicon-eye-close" + nodeIconSelected= "glyphicon glyphicon-eye-open" enableLinks={false} expandIcon= "glyphicon glyphicon-plus small" collapseIcon= "glyphicon glyphicon-minus small" diff --git a/package.json b/package.json index 49bc450..fea34ed 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-bootstrap-treeview", "description": "React Tree View for Twitter Bootstrap", - "version": "0.2.3", + "version": "0.2.4", "homepage": "https://github.com/jonmiles/react-bootstrap-treeview", "author": { "name": "Elipce" diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index d4a320f..b1001a4 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -7,6 +7,7 @@ var TreeView = React.createClass({ collapseIcon: React.PropTypes.string, emptyIcon: React.PropTypes.string, nodeIcon: React.PropTypes.string, + nodeIconSelected: React.PropTypes.string, color: React.PropTypes.string, backColor: React.PropTypes.string, @@ -36,7 +37,7 @@ var TreeView = React.createClass({ collapseIcon: 'glyphicon glyphicon-minus', emptyIcon: '', nodeIcon: 'glyphicon glyphicon-stop', - + nodeIconSelected: 'glyphicon glyphicon-eye-open', color: undefined, backColor: undefined, borderColor: undefined, @@ -78,6 +79,22 @@ var TreeView = React.createClass({ }, this); }, + /** + * Find a node by nodeId + * @param nodeId: node ID + * @returns {{}} node object or {} + */ + findNode: function (nodeId) { + var find = {}; + this.nodes.forEach(function (node) { + // Node find + if (node.nodeId == nodeId) { + find = node; + } + }); + return find; + }, + /** * Line clicked from TreeNode * @param nodeId: node ID @@ -92,13 +109,40 @@ var TreeView = React.createClass({ var matrice = this.state.nodesSelected; // Exclusive selection if (this.props.isSelectionExclusive) { - // Unselection - for (var i in matrice) { - matrice[i] = false; + + // Underline only if the element is a leaf + if (this.props.underlineLeafOnly) { + var currentNode = this.findNode(nodeId); + + // Node clicked is a leaf + if (!currentNode.nodes) { + // Unselection + for (var i in matrice) { + matrice[i] = false; + } + matrice[nodeId] = !this.state.nodesSelected[nodeId]; + } + // Node clicked is a parentNode + else { + // Simulation click expand/collapse icon + $(evt.currentTarget).find('[data-target=plusmoins]').click(); + } } + // Underline on all nodes + else { + // Unselection + for (var i in matrice) { + matrice[i] = false; + } + // TOGGLE SELECTION OF CURRENT NODE + matrice[nodeId] = !this.state.nodesSelected[nodeId]; + } + } + // MULTIPLE SELECTION + else { + // TOGGLE SELECTION OF CURRENT NODE + matrice[nodeId] = !this.state.nodesSelected[nodeId]; } - // TOGGLE SELECTION OF CURRENT NODE - matrice[nodeId] = !this.state.nodesSelected[nodeId]; this.setState({nodesSelected: matrice}); }, @@ -248,9 +292,10 @@ var TreeNode = React.createClass({ if (!this.state.expanded) { expandCollapseIcon = ( - - + ); } @@ -258,24 +303,33 @@ var TreeNode = React.createClass({ else { expandCollapseIcon = ( - + ); } } + // Node is a leaf else { expandCollapseIcon = ( ); } - // Icon (if no nodes children) + // Icon (if current node is a leaf) var nodeIcon = ''; if (options.nodeIcon !== '' && !node.nodes) { + console.log('node %o %o %o',node, this.state.selected, options); + var iTarget = (); + // Current node selected + if (this.state.selected) { + iTarget = () + } nodeIcon = ( - + {iTarget} ); } @@ -297,7 +351,7 @@ var TreeNode = React.createClass({ // No tags in data => number of children else { // Children exist - if(node.nodes) { + if (node.nodes) { badges = ( @@ -321,10 +375,10 @@ var TreeNode = React.createClass({ } else { nodeText = ( - + {node.text} - + ); } From c9e772d68bfd0d9c54bf38c2a7137b30c9bfa69b Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 12 Jun 2015 11:16:45 +0200 Subject: [PATCH 24/26] Suppression log console --- package.json | 2 +- src/react-bootstrap-treeview.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fea34ed..73ebe23 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-bootstrap-treeview", "description": "React Tree View for Twitter Bootstrap", - "version": "0.2.4", + "version": "0.2.5", "homepage": "https://github.com/jonmiles/react-bootstrap-treeview", "author": { "name": "Elipce" diff --git a/src/react-bootstrap-treeview.jsx b/src/react-bootstrap-treeview.jsx index b1001a4..da0e3a9 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -321,7 +321,7 @@ var TreeNode = React.createClass({ // Icon (if current node is a leaf) var nodeIcon = ''; if (options.nodeIcon !== '' && !node.nodes) { - console.log('node %o %o %o',node, this.state.selected, options); + //console.log('node %o %o %o',node, this.state.selected, options); var iTarget = (); // Current node selected if (this.state.selected) { From 6433540d2d0fd843e8e3e428b5f8fc0d5101c17b Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 12 Jun 2015 11:23:12 +0200 Subject: [PATCH 25/26] Suppression log console --- dist/js/react-bootstrap-treeview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/js/react-bootstrap-treeview.js b/dist/js/react-bootstrap-treeview.js index 3fd1444..e81e1d5 100644 --- a/dist/js/react-bootstrap-treeview.js +++ b/dist/js/react-bootstrap-treeview.js @@ -321,7 +321,7 @@ var TreeNode = React.createClass({displayName: "TreeNode", // Icon (if current node is a leaf) var nodeIcon = ''; if (options.nodeIcon !== '' && !node.nodes) { - console.log('node %o %o %o',node, this.state.selected, options); + //console.log('node %o %o %o',node, this.state.selected, options); var iTarget = (React.createElement("i", {className: node.icon || options.nodeIcon})); // Current node selected if (this.state.selected) { From c61bf6805b9c619e9389e287ae28bcfe7bf4b8af Mon Sep 17 00:00:00 2001 From: vivian-perez Date: Fri, 12 Jun 2015 11:24:20 +0200 Subject: [PATCH 26/26] 0.2.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 73ebe23..1193500 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-bootstrap-treeview", "description": "React Tree View for Twitter Bootstrap", - "version": "0.2.5", + "version": "0.2.6", "homepage": "https://github.com/jonmiles/react-bootstrap-treeview", "author": { "name": "Elipce"