Michael Lando | efa037d | 2017-02-19 12:57:33 +0200 | [diff] [blame] | 1 | import React from 'react'; |
| 2 | import FontAwesome from 'react-fontawesome'; |
| 3 | import classNames from 'classnames'; |
| 4 | import Collapse from 'react-bootstrap/lib/Collapse.js'; |
| 5 | |
| 6 | import i18n from 'nfvo-utils/i18n/i18n.js'; |
| 7 | import {nodeTypes, mouseActions} from './AttachmentsConstants'; |
| 8 | |
| 9 | const typeToIcon = Object.freeze({ |
| 10 | heat: 'building-o', |
| 11 | volume: 'database', |
| 12 | network: 'cloud', |
| 13 | artifact: 'gear', |
| 14 | env: 'server', |
| 15 | other: 'cube' |
| 16 | }); |
| 17 | |
| 18 | const leftPanelWidth = 250; |
| 19 | |
| 20 | class SoftwareProductAttachmentsView extends React.Component { |
| 21 | |
| 22 | static propTypes = { |
| 23 | attachmentsTree: React.PropTypes.object.isRequired |
| 24 | }; |
| 25 | state = { |
| 26 | treeWidth: '400', |
| 27 | }; |
| 28 | |
| 29 | render() { |
| 30 | let {attachmentsTree, errorList = []} = this.props; |
| 31 | |
| 32 | let {treeWidth} = this.state; |
| 33 | return ( |
| 34 | <div className='software-product-attachments'> |
| 35 | <div className='software-product-view'> |
| 36 | <div className='software-product-landing-view-right-side'> |
| 37 | <div className='software-product-attachments-main'> |
| 38 | <div className='software-product-attachments-tree' style={{'width' : treeWidth + 'px'}}> |
| 39 | <div className='tree-wrapper'> |
| 40 | { |
| 41 | attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind])) |
| 42 | } |
| 43 | </div> |
| 44 | </div> |
| 45 | <div className='software-product-attachments-separator' onMouseDown={e => this.onChangeTreeWidth(e)} /> |
| 46 | <div className='software-product-attachments-error-list'> |
| 47 | {errorList.length ? this.renderErrorList(errorList) : <div className='no-errors'>{attachmentsTree.children ? |
| 48 | i18n('VALIDATION SUCCESS') : i18n('THERE IS NO HEAT DATA TO PRESENT') }</div>} |
| 49 | </div> |
| 50 | </div> |
| 51 | </div> |
| 52 | </div> |
| 53 | </div> |
| 54 | ); |
| 55 | } |
| 56 | |
| 57 | |
| 58 | |
| 59 | renderNode(node, path) { |
| 60 | let isFolder = node.children && node.children.length > 0; |
| 61 | let {onSelectNode} = this.props; |
| 62 | return ( |
| 63 | <div key={node.name} className='tree-block-inside'> |
| 64 | { |
| 65 | <div onDoubleClick={() => this.props.toggleExpanded(path)} className={this.getTreeRowClassName(node.name)}> |
| 66 | { |
| 67 | isFolder && |
| 68 | <div onClick={() => this.props.toggleExpanded(path)} className={classNames('tree-node-expander', {'tree-node-expander-collapsed': !node.expanded})}> |
| 69 | <FontAwesome name='caret-down'/> |
| 70 | </div> |
| 71 | } |
| 72 | { |
| 73 | |
| 74 | <span className='tree-node-icon'> |
| 75 | <FontAwesome name={typeToIcon[node.type]}/> |
| 76 | </span> |
| 77 | } |
| 78 | { |
| 79 | |
| 80 | <span onClick={() => onSelectNode(node.name)} className={this.getTreeTextClassName(node)}> |
| 81 | {node.name} |
| 82 | </span> |
| 83 | } |
| 84 | </div> |
| 85 | } |
| 86 | { |
| 87 | isFolder && |
| 88 | <Collapse in={node.expanded}> |
| 89 | <div className='tree-node-children'> |
| 90 | { |
| 91 | node.children.map((child, ind) => this.renderNode(child, [...path, ind])) |
| 92 | } |
| 93 | </div> |
| 94 | </Collapse> |
| 95 | } |
| 96 | </div> |
| 97 | ); |
| 98 | } |
| 99 | |
| 100 | createErrorList(errorList, node, parent) { |
| 101 | if (node.errors) { |
| 102 | node.errors.forEach(error => errorList.push({ |
| 103 | error, |
| 104 | name: node.name, |
| 105 | parentName: parent.name, |
| 106 | type: node.type |
| 107 | })); |
| 108 | } |
| 109 | if (node.children && node.children.length) { |
| 110 | node.children.map((child) => this.createErrorList(errorList, child, node)); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | renderErrorList(errors) { |
| 115 | let prevError = {}; |
| 116 | let {selectedNode} = this.props; |
| 117 | return errors.map(error => { |
| 118 | let isSameNodeError = error.name === prevError.name && error.parentName === prevError.parentName; |
| 119 | prevError = error; |
| 120 | |
| 121 | return ( |
| 122 | <div |
| 123 | key={error.name + error.errorMessage + error.parentName} |
| 124 | |
| 125 | onClick={() => this.selectNode(error.name)} |
| 126 | className={classNames('error-item', {'clicked': selectedNode === error.name, 'shifted': !isSameNodeError})}> |
| 127 | <span className={classNames('error-item-file-type', {'strong': !isSameNodeError})}> |
| 128 | { |
| 129 | error.hasParent ? |
| 130 | i18n('{type} {name} in {parentName}: ', { |
| 131 | type: nodeTypes[error.type], |
| 132 | name: error.name, |
| 133 | parentName: error.parentName |
| 134 | }) : |
| 135 | i18n('{type} {name}: ', { |
| 136 | type: nodeTypes[error.type], |
| 137 | name: error.name |
| 138 | }) |
| 139 | } |
| 140 | </span> |
| 141 | <span className={`error-item-file-type ${error.errorLevel}`}> {error.errorMessage} </span> |
| 142 | </div> |
| 143 | ); |
| 144 | }); |
| 145 | } |
| 146 | |
| 147 | selectNode(currentSelectedNode) { |
| 148 | let {onUnselectNode, onSelectNode, selectedNode} = this.props; |
| 149 | if (currentSelectedNode !== selectedNode) { |
| 150 | onSelectNode(currentSelectedNode); |
| 151 | }else{ |
| 152 | onUnselectNode(); |
| 153 | } |
| 154 | |
| 155 | } |
| 156 | |
| 157 | getTreeRowClassName(name) { |
| 158 | let {hoveredNode, selectedNode} = this.props; |
| 159 | return classNames({ |
| 160 | 'tree-node-row': true, |
| 161 | 'tree-node-selected': name === hoveredNode, |
| 162 | 'tree-node-clicked': name === selectedNode |
| 163 | }); |
| 164 | } |
| 165 | |
| 166 | getTreeTextClassName(node) { |
| 167 | let {selectedNode} = this.props; |
| 168 | return classNames({ |
| 169 | 'tree-element-text': true, |
| 170 | 'error-status': node.errors, |
| 171 | 'error-status-selected': node.name === selectedNode |
| 172 | }); |
| 173 | } |
| 174 | |
| 175 | onChangeTreeWidth(e) { |
| 176 | if (e.button === mouseActions.MOUSE_BUTTON_CLICK) { |
| 177 | let onMouseMove = (e) => { |
| 178 | this.setState({treeWidth: e.clientX - leftPanelWidth}); |
| 179 | }; |
| 180 | let onMouseUp = () => { |
| 181 | document.removeEventListener('mousemove', onMouseMove); |
| 182 | document.removeEventListener('mouseup', onMouseUp); |
| 183 | }; |
| 184 | document.addEventListener('mousemove', onMouseMove); |
| 185 | document.addEventListener('mouseup', onMouseUp); |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | export default SoftwareProductAttachmentsView; |