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..c9b3f9f 100644 --- a/public/js/example.jsx +++ b/public/js/example.jsx @@ -1,41 +1,54 @@ - - var data = [ - { - text: 'Parent 1', - nodes: [ - { - text: 'Child 1', + { + text: 'Parent 1', + id: '1', nodes: [ - { - text: 'Grandchild 1' - }, - { - text: 'Grandchild 2' - } + { + text: 'Child 1', + id: '11', + nodes: [ + { + text: 'Grandchild 1', + id: '111' + }, + { + text: 'Grandchild 2', + id: '112' + } + ] + }, + { + text: 'Child 2', + id: '12' + } ] - }, - { - text: 'Child 2' - } - ] - }, - { - text: 'Parent 2' - }, - { - text: 'Parent 3' - }, - { - text: 'Parent 4' - }, - { - text: 'Parent 5' - } + }, + { + text: 'Parent 2', + id: '2' + }, + { + text: 'Parent 3', + id: '3' + }, + { + text: 'Parent 4', + id: '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..0f644b1 100644 --- a/public/js/react-bootstrap-treeview.js +++ b/public/js/react-bootstrap-treeview.js @@ -1,232 +1,274 @@ 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: [] - } - }, - - 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() { - - this.setNodeId({ nodes: data }); - - 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})); - }); - } + 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", {id: "treeview", className: "treeview"}, - React.createElement("ul", {className: "list-group"}, + return ( + React.createElement("div", {className: "treeview"}, + 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)} - ) - ); - } - else { - expandCollapseIcon = ( - React.createElement("span", {className: options.collapseIcon, - onClick: this.toggleExpanded.bind(this, node.nodeId)} - ) + 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}) + ) ); - } - } - 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, + onLineClicked: this.props.onLineClicked, + attributes: this.props.attributes})); + }, this); + } - var badges; - if (options.showTags && node.tags) { - badges = node.tags.map(function (tag) { return ( - React.createElement("span", {className: "badge"}, tag) + 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 + ) ); - }); } - - 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}, - 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..99b0b5f 100644 --- a/src/react-bootstrap-treeview.jsx +++ b/src/react-bootstrap-treeview.jsx @@ -1,232 +1,274 @@ 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: [] - } - }, - - 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() { - - this.setNodeId({ nodes: data }); - - var children = []; - if (data) { - var _this = this; - data.forEach(function (node) { - children.push(); - }); - } + 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(); + }.bind(this)); + } - 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 = ( - - - ); - } - else { - expandCollapseIcon = ( - - + 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(); + } + + 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 = ( + + + ); + } + else { + expandCollapseIcon = ( + + + ); + } + } + else { + expandCollapseIcon = ( + + ); + } + + var nodeIcon = ( + + + ); - } - } - 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} +
  • + {indents} + {expandCollapseIcon} + {nodeIcon} + {nodeText} + {badges} + {children} +
  • ); - }); } - - 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 +});