Refactor FileNode.jsx to remove inline functions and match React functional best practices
This commit is contained in:
		
							parent
							
								
									24f14f9b5d
								
							
						
					
					
						commit
						6bf303dfe7
					
				
					 1 changed files with 189 additions and 189 deletions
				
			
		|  | @ -6,40 +6,29 @@ import InlineSVG from 'react-inlinesvg'; | ||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| import * as IDEActions from '../actions/ide'; | import * as IDEActions from '../actions/ide'; | ||||||
| import * as FileActions from '../actions/files'; | import * as FileActions from '../actions/files'; | ||||||
| 
 | import downArrowUrl from '../../../images/down-filled-triangle.svg'; | ||||||
| const downArrowUrl = require('../../../images/down-filled-triangle.svg'); | import folderRightUrl from '../../../images/triangle-arrow-right.svg'; | ||||||
| const folderRightUrl = require('../../../images/triangle-arrow-right.svg'); | import folderDownUrl from '../../../images/triangle-arrow-down.svg'; | ||||||
| const folderDownUrl = require('../../../images/triangle-arrow-down.svg'); | import fileUrl from '../../../images/file.svg'; | ||||||
| const fileUrl = require('../../../images/file.svg'); |  | ||||||
| 
 | 
 | ||||||
| export class FileNode extends React.Component { | export class FileNode extends React.Component { | ||||||
|   constructor(props) { |   constructor(props) { | ||||||
|     super(props); |     super(props); | ||||||
|     this.renderChild = this.renderChild.bind(this); |  | ||||||
|     this.handleKeyPress = this.handleKeyPress.bind(this); |  | ||||||
|     this.handleFileNameChange = this.handleFileNameChange.bind(this); |  | ||||||
|     this.validateFileName = this.validateFileName.bind(this); |  | ||||||
|     this.handleFileClick = this.handleFileClick.bind(this); |  | ||||||
|     this.toggleFileOptions = this.toggleFileOptions.bind(this); |  | ||||||
|     this.hideFileOptions = this.hideFileOptions.bind(this); |  | ||||||
|     this.showEditFileName = this.showEditFileName.bind(this); |  | ||||||
|     this.hideEditFileName = this.hideEditFileName.bind(this); |  | ||||||
|     this.onBlurComponent = this.onBlurComponent.bind(this); |  | ||||||
|     this.onFocusComponent = this.onFocusComponent.bind(this); |  | ||||||
| 
 | 
 | ||||||
|     this.state = { |     this.state = { | ||||||
|       isOptionsOpen: false, |       isOptionsOpen: false, | ||||||
|       isEditingName: false, |       isEditingName: false, | ||||||
|       isFocused: false, |       isFocused: false, | ||||||
|  |       isDeleting: false, | ||||||
|       updatedName: this.props.name |       updatedName: this.props.name | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onFocusComponent() { |   onFocusComponent = () => { | ||||||
|     this.setState({ isFocused: true }); |     this.setState({ isFocused: true }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onBlurComponent() { |   onBlurComponent = () => { | ||||||
|     this.setState({ isFocused: false }); |     this.setState({ isFocused: false }); | ||||||
|     setTimeout(() => { |     setTimeout(() => { | ||||||
|       if (!this.state.isFocused) { |       if (!this.state.isFocused) { | ||||||
|  | @ -48,11 +37,12 @@ export class FileNode extends React.Component { | ||||||
|     }, 200); |     }, 200); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get updatedName() { | 
 | ||||||
|     return this.state.updatedName; |   setUpdatedName = (updatedName) => { | ||||||
|  |     this.setState({ updatedName }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   commitFileNameChange() { |   saveUpdatedFileName = () => { | ||||||
|     const { updatedName } = this.state; |     const { updatedName } = this.state; | ||||||
|     const { name, updateFileName, id } = this.props; |     const { name, updateFileName, id } = this.props; | ||||||
| 
 | 
 | ||||||
|  | @ -61,42 +51,77 @@ export class FileNode extends React.Component { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleFileClick(e) { |   handleFileClick = (event) => { | ||||||
|     e.stopPropagation(); |     event.stopPropagation(); | ||||||
|     if (this.updatedName !== 'root' && !this.isDeleting) { |     const { isDeleting } = this.state; | ||||||
|       this.props.setSelectedFile(this.props.id); |     const { id, setSelectedFile, name } = this.props; | ||||||
|  |     if (name !== 'root' && !isDeleting) { | ||||||
|  |       setSelectedFile(id); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleFileNameChange(event) { |   handleFileNameChange = (event) => { | ||||||
|     const newname = event.target.value; |     const newName = event.target.value; | ||||||
|     this.setState({ updatedName: newname }); |     this.setUpdatedName(newName); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleKeyPress(event) { |   handleFileNameBlur = () => { | ||||||
|  |     this.validateFileName(); | ||||||
|  |     this.hideEditFileName(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClickRename = () => { | ||||||
|  |     this.setUpdatedName(this.props.name); | ||||||
|  |     this.showEditFileName(); | ||||||
|  |     setTimeout(() => this.fileNameInput.focus(), 0); | ||||||
|  |     setTimeout(() => this.hideFileOptions(), 0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClickAddFile = () => { | ||||||
|  |     this.props.newFile(this.props.id); | ||||||
|  |     setTimeout(() => this.hideFileOptions(), 0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClickAddFolder = () => { | ||||||
|  |     this.props.newFolder(this.props.id); | ||||||
|  |     setTimeout(() => this.hideFileOptions(), 0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClickDelete = () => { | ||||||
|  |     if (window.confirm(`Are you sure you want to delete ${this.props.name}?`)) { | ||||||
|  |       this.setState({ isDeleting: true }); | ||||||
|  |       this.props.resetSelectedFile(this.props.id); | ||||||
|  |       setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleKeyPress = (event) => { | ||||||
|     if (event.key === 'Enter') { |     if (event.key === 'Enter') { | ||||||
|       this.hideEditFileName(); |       this.hideEditFileName(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   validateFileName() { |   validateFileName = () => { | ||||||
|     const oldFileExtension = this.originalFileName.match(/\.[0-9a-z]+$/i); |     const currentName = this.props.name; | ||||||
|     const newFileExtension = this.updatedName.match(/\.[0-9a-z]+$/i); |     const { updatedName } = this.state; | ||||||
|     const hasPeriod = this.updatedName.match(/\.+/); |     const oldFileExtension = currentName.match(/\.[0-9a-z]+$/i); | ||||||
|     const newFileName = this.updatedName; |     const newFileExtension = updatedName.match(/\.[0-9a-z]+$/i); | ||||||
|  |     const hasPeriod = updatedName.match(/\.+/); | ||||||
|     const hasNoExtension = oldFileExtension && !newFileExtension; |     const hasNoExtension = oldFileExtension && !newFileExtension; | ||||||
|     const hasExtensionIfFolder = this.props.fileType === 'folder' && hasPeriod; |     const hasExtensionIfFolder = this.props.fileType === 'folder' && hasPeriod; | ||||||
|     const notSameExtension = oldFileExtension && newFileExtension |     const notSameExtension = oldFileExtension && newFileExtension | ||||||
|       && oldFileExtension[0].toLowerCase() !== newFileExtension[0].toLowerCase(); |       && oldFileExtension[0].toLowerCase() !== newFileExtension[0].toLowerCase(); | ||||||
|     const hasEmptyFilename = newFileName === ''; |     const hasEmptyFilename = updatedName === ''; | ||||||
|     const hasOnlyExtension = newFileExtension && newFileName === newFileExtension[0]; |     const hasOnlyExtension = newFileExtension && updatedName === newFileExtension[0]; | ||||||
|     if (hasEmptyFilename || hasNoExtension || notSameExtension || hasOnlyExtension || hasExtensionIfFolder) { |     if (hasEmptyFilename || hasNoExtension || notSameExtension || hasOnlyExtension || hasExtensionIfFolder) { | ||||||
|       this.setState({ updatedName: this.originalFileName }); |       this.setUpdatedName(currentName); | ||||||
|     } else this.commitFileNameChange(); |     } else { | ||||||
|  |       this.saveUpdatedFileName(); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   toggleFileOptions(e) { |   toggleFileOptions = (event) => { | ||||||
|     e.preventDefault(); |     event.preventDefault(); | ||||||
|     if (!this.props.canEdit) { |     if (!this.props.canEdit) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  | @ -108,181 +133,156 @@ export class FileNode extends React.Component { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   hideFileOptions() { |   hideFileOptions = () => { | ||||||
|     this.setState({ isOptionsOpen: false }); |     this.setState({ isOptionsOpen: false }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   showEditFileName() { |   showEditFileName = () => { | ||||||
|     this.setState({ isEditingName: true }); |     this.setState({ isEditingName: true }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   hideEditFileName() { |   hideEditFileName = () => { | ||||||
|     this.setState({ isEditingName: false }); |     this.setState({ isEditingName: false }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   renderChild(childId) { |   showFolderChildren = () => { | ||||||
|     return ( |     this.props.showFolderChildren(this.props.id); | ||||||
|       <li key={childId}> |  | ||||||
|         <ConnectedFileNode id={childId} parentId={this.props.id} canEdit={this.props.canEdit} /> |  | ||||||
|       </li> |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   hideFolderChildren = () => { | ||||||
|  |     this.props.hideFolderChildren(this.props.id); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   renderChild = childId => ( | ||||||
|  |     <li key={childId}> | ||||||
|  |       <ConnectedFileNode id={childId} parentId={this.props.id} canEdit={this.props.canEdit} /> | ||||||
|  |     </li> | ||||||
|  |   ) | ||||||
|  | 
 | ||||||
|   render() { |   render() { | ||||||
|     const itemClass = classNames({ |     const itemClass = classNames({ | ||||||
|       'sidebar__root-item': this.updatedName === 'root', |       'sidebar__root-item': this.props.name === 'root', | ||||||
|       'sidebar__file-item': this.updatedName !== 'root', |       'sidebar__file-item': this.props.name !== 'root', | ||||||
|       'sidebar__file-item--selected': this.props.isSelectedFile, |       'sidebar__file-item--selected': this.props.isSelectedFile, | ||||||
|       'sidebar__file-item--open': this.state.isOptionsOpen, |       'sidebar__file-item--open': this.state.isOptionsOpen, | ||||||
|       'sidebar__file-item--editing': this.state.isEditingName, |       'sidebar__file-item--editing': this.state.isEditingName, | ||||||
|       'sidebar__file-item--closed': this.props.isFolderClosed |       'sidebar__file-item--closed': this.props.isFolderClosed | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     const isFile = this.props.fileType === 'file'; | ||||||
|  |     const isFolder = this.props.fileType === 'folder'; | ||||||
|  |     const isRoot = this.props.name === 'root'; | ||||||
|  | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className={itemClass}> |       <div className={itemClass}> | ||||||
|         {(() => { // eslint-disable-line |         { !isRoot && | ||||||
|           if (this.updatedName !== 'root') { |           <div className="file-item__content" onContextMenu={this.toggleFileOptions}> | ||||||
|             return ( |             <span className="file-item__spacer"></span> | ||||||
|               <div className="file-item__content" onContextMenu={this.toggleFileOptions}> |             { isFile && | ||||||
|                 <span className="file-item__spacer"></span> |               <span className="sidebar__file-item-icon"> | ||||||
|                 {(() => { // eslint-disable-line |                 <InlineSVG src={fileUrl} /> | ||||||
|                   if (this.props.fileType === 'file') { |               </span> | ||||||
|                     return ( |             } | ||||||
|                       <span className="sidebar__file-item-icon"> |             { isFolder && | ||||||
|                         <InlineSVG src={fileUrl} /> |               <div className="sidebar__file-item--folder"> | ||||||
|                       </span> |  | ||||||
|                     ); |  | ||||||
|                   } |  | ||||||
|                   return ( |  | ||||||
|                     <div className="sidebar__file-item--folder"> |  | ||||||
|                       <button |  | ||||||
|                         className="sidebar__file-item-closed" |  | ||||||
|                         onClick={() => this.props.showFolderChildren(this.props.id)} |  | ||||||
|                       > |  | ||||||
|                         <InlineSVG className="folder-right" src={folderRightUrl} /> |  | ||||||
|                       </button> |  | ||||||
|                       <button |  | ||||||
|                         className="sidebar__file-item-open" |  | ||||||
|                         onClick={() => this.props.hideFolderChildren(this.props.id)} |  | ||||||
|                       > |  | ||||||
|                         <InlineSVG className="folder-down" src={folderDownUrl} /> |  | ||||||
|                       </button> |  | ||||||
|                     </div> |  | ||||||
|                   ); |  | ||||||
|                 })()} |  | ||||||
|                 <button className="sidebar__file-item-name" onClick={this.handleFileClick}>{this.updatedName}</button> |  | ||||||
|                 <input |  | ||||||
|                   type="text" |  | ||||||
|                   className="sidebar__file-item-input" |  | ||||||
|                   value={this.updatedName} |  | ||||||
|                   maxLength="128" |  | ||||||
|                   onChange={this.handleFileNameChange} |  | ||||||
|                   ref={(element) => { this.fileNameInput = element; }} |  | ||||||
|                   onBlur={() => { |  | ||||||
|                     this.validateFileName(); |  | ||||||
|                     this.hideEditFileName(); |  | ||||||
|                   }} |  | ||||||
|                   onKeyPress={this.handleKeyPress} |  | ||||||
|                 /> |  | ||||||
|                 <button |                 <button | ||||||
|                   className="sidebar__file-item-show-options" |                   className="sidebar__file-item-closed" | ||||||
|                   aria-label="view file options" |                   onClick={this.showFolderChildren} | ||||||
|                   ref={(element) => { this[`fileOptions-${this.props.id}`] = element; }} |  | ||||||
|                   tabIndex="0" |  | ||||||
|                   onClick={this.toggleFileOptions} |  | ||||||
|                   onBlur={this.onBlurComponent} |  | ||||||
|                   onFocus={this.onFocusComponent} |  | ||||||
|                 > |                 > | ||||||
|                   <InlineSVG src={downArrowUrl} /> |                   <InlineSVG className="folder-right" src={folderRightUrl} /> | ||||||
|  |                 </button> | ||||||
|  |                 <button | ||||||
|  |                   className="sidebar__file-item-open" | ||||||
|  |                   onClick={this.hideFolderChildren} | ||||||
|  |                 > | ||||||
|  |                   <InlineSVG className="folder-down" src={folderDownUrl} /> | ||||||
|                 </button> |                 </button> | ||||||
|                 <div className="sidebar__file-item-options"> |  | ||||||
|                   <ul title="file options"> |  | ||||||
|                     {(() => { // eslint-disable-line |  | ||||||
|                       if (this.props.fileType === 'folder') { |  | ||||||
|                         return ( |  | ||||||
|                           <li> |  | ||||||
|                             <button |  | ||||||
|                               aria-label="add file" |  | ||||||
|                               onClick={() => { |  | ||||||
|                                 this.props.newFile(this.props.id); |  | ||||||
|                                 setTimeout(() => this.hideFileOptions(), 0); |  | ||||||
|                               }} |  | ||||||
|                               onBlur={this.onBlurComponent} |  | ||||||
|                               onFocus={this.onFocusComponent} |  | ||||||
|                               className="sidebar__file-item-option" |  | ||||||
|                             > |  | ||||||
|                               Add File |  | ||||||
|                             </button> |  | ||||||
|                           </li> |  | ||||||
|                         ); |  | ||||||
|                       } |  | ||||||
|                     })()} |  | ||||||
|                     {(() => { // eslint-disable-line |  | ||||||
|                       if (this.props.fileType === 'folder') { |  | ||||||
|                         return ( |  | ||||||
|                           <li> |  | ||||||
|                             <button |  | ||||||
|                               aria-label="add folder" |  | ||||||
|                               onClick={() => { |  | ||||||
|                                 this.props.newFolder(this.props.id); |  | ||||||
|                                 setTimeout(() => this.hideFileOptions(), 0); |  | ||||||
|                               }} |  | ||||||
|                               onBlur={this.onBlurComponent} |  | ||||||
|                               onFocus={this.onFocusComponent} |  | ||||||
|                               className="sidebar__file-item-option" |  | ||||||
|                             > |  | ||||||
|                               Add Folder |  | ||||||
|                             </button> |  | ||||||
|                           </li> |  | ||||||
|                         ); |  | ||||||
|                       } |  | ||||||
|                     })()} |  | ||||||
|                     <li> |  | ||||||
|                       <button |  | ||||||
|                         onClick={() => { |  | ||||||
|                           this.originalFileName = this.props.name; |  | ||||||
|                           this.showEditFileName(); |  | ||||||
|                           setTimeout(() => this.fileNameInput.focus(), 0); |  | ||||||
|                           setTimeout(() => this.hideFileOptions(), 0); |  | ||||||
|                         }} |  | ||||||
|                         onBlur={this.onBlurComponent} |  | ||||||
|                         onFocus={this.onFocusComponent} |  | ||||||
|                         className="sidebar__file-item-option" |  | ||||||
|                       > |  | ||||||
|                         Rename |  | ||||||
|                       </button> |  | ||||||
|                     </li> |  | ||||||
|                     <li> |  | ||||||
|                       <button |  | ||||||
|                         onClick={() => { |  | ||||||
|                           if (window.confirm(`Are you sure you want to delete ${this.updatedName}?`)) { |  | ||||||
|                             this.isDeleting = true; |  | ||||||
|                             this.props.resetSelectedFile(this.props.id); |  | ||||||
|                             setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100); |  | ||||||
|                           } |  | ||||||
|                         }} |  | ||||||
|                         onBlur={this.onBlurComponent} |  | ||||||
|                         onFocus={this.onFocusComponent} |  | ||||||
|                         className="sidebar__file-item-option" |  | ||||||
|                       > |  | ||||||
|                         Delete |  | ||||||
|                       </button> |  | ||||||
|                     </li> |  | ||||||
|                   </ul> |  | ||||||
|                 </div> |  | ||||||
|               </div> |               </div> | ||||||
|             ); |             } | ||||||
|           } |             <button | ||||||
|         })()} |               className="sidebar__file-item-name" | ||||||
|         {(() => { // eslint-disable-line |               onClick={this.handleFileClick} | ||||||
|           if (this.props.children) { |             > | ||||||
|             return ( |               {this.state.updatedName} | ||||||
|               <ul className="file-item__children"> |             </button> | ||||||
|                 {this.props.children.map(this.renderChild)} |             <input | ||||||
|  |               type="text" | ||||||
|  |               className="sidebar__file-item-input" | ||||||
|  |               value={this.state.updatedName} | ||||||
|  |               maxLength="128" | ||||||
|  |               onChange={this.handleFileNameChange} | ||||||
|  |               ref={(element) => { this.fileNameInput = element; }} | ||||||
|  |               onBlur={this.handleFileNameBlur} | ||||||
|  |               onKeyPress={this.handleKeyPress} | ||||||
|  |             /> | ||||||
|  |             <button | ||||||
|  |               className="sidebar__file-item-show-options" | ||||||
|  |               aria-label="view file options" | ||||||
|  |               ref={(element) => { this[`fileOptions-${this.props.id}`] = element; }} | ||||||
|  |               tabIndex="0" | ||||||
|  |               onClick={this.toggleFileOptions} | ||||||
|  |               onBlur={this.onBlurComponent} | ||||||
|  |               onFocus={this.onFocusComponent} | ||||||
|  |             > | ||||||
|  |               <InlineSVG src={downArrowUrl} /> | ||||||
|  |             </button> | ||||||
|  |             <div className="sidebar__file-item-options"> | ||||||
|  |               <ul title="file options"> | ||||||
|  |                 { isFolder && | ||||||
|  |                   <React.Fragment> | ||||||
|  |                     <li> | ||||||
|  |                       <button | ||||||
|  |                         aria-label="add file" | ||||||
|  |                         onClick={this.handleClickAddFile} | ||||||
|  |                         onBlur={this.onBlurComponent} | ||||||
|  |                         onFocus={this.onFocusComponent} | ||||||
|  |                         className="sidebar__file-item-option" | ||||||
|  |                       > | ||||||
|  |                         Add File | ||||||
|  |                       </button> | ||||||
|  |                     </li> | ||||||
|  |                     <li> | ||||||
|  |                       <button | ||||||
|  |                         aria-label="add folder" | ||||||
|  |                         onClick={this.handleClickAddFolder} | ||||||
|  |                         onBlur={this.onBlurComponent} | ||||||
|  |                         onFocus={this.onFocusComponent} | ||||||
|  |                         className="sidebar__file-item-option" | ||||||
|  |                       > | ||||||
|  |                         Add Folder | ||||||
|  |                       </button> | ||||||
|  |                     </li> | ||||||
|  |                   </React.Fragment> | ||||||
|  |                 } | ||||||
|  |                 <li> | ||||||
|  |                   <button | ||||||
|  |                     onClick={this.handleClickRename} | ||||||
|  |                     onBlur={this.onBlurComponent} | ||||||
|  |                     onFocus={this.onFocusComponent} | ||||||
|  |                     className="sidebar__file-item-option" | ||||||
|  |                   > | ||||||
|  |                     Rename | ||||||
|  |                   </button> | ||||||
|  |                 </li> | ||||||
|  |                 <li> | ||||||
|  |                   <button | ||||||
|  |                     onClick={this.handleClickDelete} | ||||||
|  |                     onBlur={this.onBlurComponent} | ||||||
|  |                     onFocus={this.onFocusComponent} | ||||||
|  |                     className="sidebar__file-item-option" | ||||||
|  |                   > | ||||||
|  |                     Delete | ||||||
|  |                   </button> | ||||||
|  |                 </li> | ||||||
|               </ul> |               </ul> | ||||||
|             ); |             </div> | ||||||
|           } |           </div> | ||||||
|         })()} |         } | ||||||
|  |         { this.props.children && | ||||||
|  |           <ul className="file-item__children"> | ||||||
|  |             {this.props.children.map(this.renderChild)} | ||||||
|  |           </ul> | ||||||
|  |         } | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Cassie Tarakajian
						Cassie Tarakajian