Convert file fields to use native Electron dialog + add directory selector field #124
7 changed files with 101 additions and 12 deletions
|
@ -23,8 +23,8 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
|||
* Enable windows code signing of binary
|
||||
|
||||
### Changed
|
||||
*
|
||||
*
|
||||
* Use directory selector instead of ugly text box on Settings page
|
||||
* Use Electron's native file selector everywhere instead of WebKit file selector
|
||||
|
||||
### Fixed
|
||||
* Error modals now display full screen properly
|
||||
|
|
58
ui/js/component/file-selector.js
Normal file
58
ui/js/component/file-selector.js
Normal file
|
@ -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 (
|
||||
<div className="file-selector">
|
||||
<button type="button" className="file-selector__choose-button" onClick={() => this.handleButtonClick()}>
|
||||
{this.props.type == 'file' ?
|
||||
'Choose File' :
|
||||
'Choose Directory'}
|
||||
</button>
|
||||
{' '}
|
||||
<span className="file-selector__path">
|
||||
{this.state.path ?
|
||||
this.state.path :
|
||||
'No File Chosen'}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default FileSelector;
|
|
@ -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 <input> field, e.g. <select>, <textarea>
|
||||
this._element = this.props.type;
|
||||
}
|
||||
},
|
||||
componentDidMount: function() {
|
||||
/**
|
||||
* We have to add the webkitdirectory attribute here because React doesn't allow it in JSX
|
||||
* https://github.com/facebook/react/issues/3468
|
||||
*/
|
||||
if (this.props.type == 'directory') {
|
||||
this.refs.field.webkitdirectory = true;
|
||||
}
|
||||
},
|
||||
showError: function(text) {
|
||||
this.setState({
|
||||
isError: true,
|
||||
|
@ -49,9 +71,6 @@ export let FormField = React.createClass({
|
|||
getValue: function() {
|
||||
if (this.props.type == 'checkbox') {
|
||||
return this.refs.field.checked;
|
||||
} else if (this.props.type == 'file') {
|
||||
return this.refs.field.files.length && this.refs.field.files[0].path ?
|
||||
this.refs.field.files[0].path : null;
|
||||
} else {
|
||||
return this.refs.field.value;
|
||||
}
|
||||
|
@ -87,6 +106,10 @@ export let FormField = React.createClass({
|
|||
{this.props.label}
|
||||
</label> :
|
||||
element }
|
||||
{ formFieldFileSelectorTypes.includes(this.props.type) ?
|
||||
<FileSelector type={this.props.type} onFileChosen={this.handleFileChosen}
|
||||
{... this.props.defaultValue ? {initPath: this.props.defaultValue} : {}} /> :
|
||||
null }
|
||||
{ this.props.postfix ? <span className="form-field__postfix">{this.props.postfix}</span> : '' }
|
||||
{ isError && this.state.errorMessage ? <div className="form-field__error">{this.state.errorMessage}</div> : '' }
|
||||
</div>
|
||||
|
|
|
@ -30,7 +30,7 @@ const DeveloperPage = React.createClass({
|
|||
},
|
||||
handleUpgradeFileChange: function(event) {
|
||||
this.setState({
|
||||
upgradePath: event.target.files[0].path,
|
||||
upgradePath: event.target.value,
|
||||
});
|
||||
},
|
||||
handleForceUpgradeClick: function() {
|
||||
|
|
|
@ -97,7 +97,7 @@ var SettingsPage = React.createClass({
|
|||
<h3>Download Directory</h3>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<FormRow type="text"
|
||||
<FormRow type="directory"
|
||||
name="download_directory"
|
||||
defaultValue={this.state.daemonSettings.download_directory}
|
||||
helper="LBRY downloads will be saved here."
|
||||
|
|
|
@ -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";
|
||||
|
|
7
ui/scss/component/_file-selector.scss
Normal file
7
ui/scss/component/_file-selector.scss
Normal file
|
@ -0,0 +1,7 @@
|
|||
.file-selector__choose-button {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.file-selector__path {
|
||||
font-size: 14px;
|
||||
}
|
Loading…
Reference in a new issue