diff --git a/js/component/common.js b/js/component/common.js index 41a10c97b..40906d467 100644 --- a/js/component/common.js +++ b/js/component/common.js @@ -4,9 +4,11 @@ var Icon = React.createClass({ propTypes: { style: React.PropTypes.object, fixed: React.PropTypes.bool, + className: React.PropTypes.string, }, render: function() { - var className = 'icon ' + ('fixed' in this.props ? 'icon-fixed-width ' : '') + this.props.icon; + var className = ('icon ' + ('fixed' in this.props ? 'icon-fixed-width ' : '') + this.props.icon + ' ' + + (this.props.className || '')); return } }); diff --git a/js/component/form.js b/js/component/form.js index 7e4b26094..70cb3beb0 100644 --- a/js/component/form.js +++ b/js/component/form.js @@ -1,9 +1,5 @@ -var requiredFieldWarningStyle = { - color: '#cc0000', - transition: 'opacity 400ms ease-in', -}; - var FormField = React.createClass({ + _fieldRequiredText: 'This field is required', _type: null, _element: null, @@ -13,7 +9,8 @@ var FormField = React.createClass({ }, getInitialState: function() { return { - warningState: 'hidden', + adviceState: 'hidden', + adviceText: null, } }, componentWillMount: function() { @@ -25,22 +22,26 @@ var FormField = React.createClass({ this._element = this.props.type; } }, - warnRequired: function() { + showAdvice: function(text) { this.setState({ - warningState: 'shown', + adviceState: 'shown', + adviceText: text, }); setTimeout(() => { this.setState({ - warningState: 'fading', + adviceState: 'fading', }); setTimeout(() => { this.setState({ - warningState: 'hidden', + adviceState: 'hidden', }); }, 450); }, 5000); }, + warnRequired: function() { + this.showAdvice(this._fieldRequiredText); + }, focus: function() { this.refs.field.focus(); }, @@ -55,24 +56,43 @@ var FormField = React.createClass({ return this.refs.field.options[this.refs.field.selectedIndex]; }, render: function() { - var warningStyle = Object.assign({}, requiredFieldWarningStyle); - if (this.state.warningState == 'fading') { - warningStyle.opacity = '0'; - } - // Pass all unhandled props to the field element var otherProps = Object.assign({}, this.props); delete otherProps.type; delete otherProps.hidden; return ( - - - {this.props.children} - - This field is required - + !this.props.hidden + ?
+ + {this.props.children} + + {this.state.adviceText} +
+ : null + ); + } +}); + +var FormFieldAdvice = React.createClass({ + propTypes: { + state: React.PropTypes.string.isRequired, + }, + render: function() { + return ( + this.props.state != 'hidden' + ?
+
+ +
+ + {this.props.children} + +
+
+
+ : null ); } }); diff --git a/js/page/publish.js b/js/page/publish.js index 007b9fd48..12dc29a9c 100644 --- a/js/page/publish.js +++ b/js/page/publish.js @@ -38,7 +38,14 @@ var PublishPage = React.createClass({ } } - if (missingFieldFound) { + let fileProcessing = false; + if (this.state.fileInfo && !this.state.tempFileReady) { + this.refs.file.showAdvice('Your file is still processing.'); + this.refs.file.focus(); + fileProcessing = true; + } + + if (missingFieldFound || fileProcessing) { this.setState({ submitting: false, }); diff --git a/scss/_gui.scss b/scss/_gui.scss index 184bf68b5..291afd375 100644 --- a/scss/_gui.scss +++ b/scss/_gui.scss @@ -234,6 +234,55 @@ input[type="text"], input[type="search"] } } +.form-field-container { + display: inline-block; +} + +.form-field-advice-container { + position: relative; +} + +.form-field-advice { + position: absolute; + top: 0px; + left: 0px; + + display: flex; + flex-direction: column; + + white-space: nowrap; + + transition: opacity 400ms ease-in; +} + +.form-field-advice--fading { + opacity: 0; +} + +.form-field-advice__arrow { + text-align: left; + padding-left: 18px; + + font-size: 22px; + line-height: 0.3; + color: darken($color-primary, 5%); +} + + +.form-field-advice__content-container { + display: inline-block; +} + +.form-field-advice__content { + display: inline-block; + + padding: 5px; + border-radius: 2px; + + background-color: darken($color-primary, 5%); + color: #fff; +} + .modal-overlay { position: fixed;