From 044d3612c2ffcda756b30cd4ce0436ee6ffa8a6e Mon Sep 17 00:00:00 2001 From: Alex Liebowitz Date: Fri, 12 May 2017 06:15:59 -0400 Subject: [PATCH 1/8] Add FileSelector component --- ui/js/component/file-selector.js | 58 +++++++++++++++++++++++++++ ui/scss/all.scss | 1 + ui/scss/component/_file-selector.scss | 7 ++++ 3 files changed, 66 insertions(+) create mode 100644 ui/js/component/file-selector.js create mode 100644 ui/scss/component/_file-selector.scss diff --git a/ui/js/component/file-selector.js b/ui/js/component/file-selector.js new file mode 100644 index 000000000..4670f4830 --- /dev/null +++ b/ui/js/component/file-selector.js @@ -0,0 +1,58 @@ +import React from 'react'; + +const {remote} = require('electron'); +class FileSelector extends React.Component { + static propTypes = { + type: React.PropTypes.oneOf(['file', 'directory']), + initPath: React.PropTypes.string, + onFileChosen: React.PropTypes.func, + } + + static defaultProps = { + type: 'file', + } + + componentWillMount() { + this.setState({ + path: this.props.initPath || null, + }); + } + + handleButtonClick() { + remote.dialog.showOpenDialog({ + properties: [this.props.type == 'file' ? 'openFile' : 'openDirectory'], + }, (paths) => { + if (!paths) { // User hit cancel, so do nothing + return; + } + + const path = paths[0]; + this.setState({ + path: path, + }); + if (this.props.onFileChosen) { + this.props.onFileChosen(path); + } + }); + } + + render() { + return ( +
+ + {' '} + + {this.state.path ? + this.state.path : + 'No File Chosen'} + +
+ ); + } +}; + +export default FileSelector; diff --git a/ui/scss/all.scss b/ui/scss/all.scss index 7c87a5fbb..14833dfd3 100644 --- a/ui/scss/all.scss +++ b/ui/scss/all.scss @@ -6,6 +6,7 @@ @import "component/_button.scss"; @import "component/_card.scss"; @import "component/_file-actions.scss"; +@import "component/_file-selector.scss"; @import "component/_file-tile.scss"; @import "component/_form-field.scss"; @import "component/_header.scss"; diff --git a/ui/scss/component/_file-selector.scss b/ui/scss/component/_file-selector.scss new file mode 100644 index 000000000..2cb9fd322 --- /dev/null +++ b/ui/scss/component/_file-selector.scss @@ -0,0 +1,7 @@ +.file-selector__choose-button { + font-size: 13px; +} + +.file-selector__path { + font-size: 14px; +} From c8f7cb21101dbd541896f671f7fc8df1614d660d Mon Sep 17 00:00:00 2001 From: Alex Liebowitz Date: Fri, 12 May 2017 06:36:08 -0400 Subject: [PATCH 2/8] Update FormField to use new Electron file/directory selector --- ui/js/component/form.js | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/ui/js/component/form.js b/ui/js/component/form.js index f75310c92..d5deaf23c 100644 --- a/ui/js/component/form.js +++ b/ui/js/component/form.js @@ -1,7 +1,9 @@ import React from 'react'; +import FileSelector from './file-selector.js'; import {Icon} from './common.js'; var formFieldCounter = 0, + formFieldFileSelectorTypes = ['file', 'directory'], formFieldNestedLabelTypes = ['radio', 'checkbox']; function formFieldId() { @@ -19,6 +21,14 @@ export let FormField = React.createClass({ postfix: React.PropTypes.string, hasError: React.PropTypes.bool }, + handleFileChosen: function(path) { + this.refs.field.value = path; + if (this.props.onChange) { // Updating inputs programmatically doesn't generate an event, so we have to make our own + const event = new Event('change', {bubbles: true}) + this.refs.field.dispatchEvent(event); // This alone won't generate a React event, but we use it to attach the field as a target + this.props.onChange(event); + } + }, getInitialState: function() { return { isError: null, @@ -26,17 +36,29 @@ export let FormField = React.createClass({ } }, componentWillMount: function() { - if (['text', 'number', 'radio', 'checkbox', 'file'].includes(this.props.type)) { + if (['text', 'number', 'radio', 'checkbox'].includes(this.props.type)) { this._element = 'input'; this._type = this.props.type; } else if (this.props.type == 'text-number') { this._element = 'input'; this._type = 'text'; + } else if (formFieldFileSelectorTypes.includes(this.props.type)) { + this._element = 'input'; + this._type = 'hidden'; } else { // Non field, e.g.