154 lines
5 KiB
JavaScript
154 lines
5 KiB
JavaScript
import React from 'react';
|
|
import {Icon} from './common.js';
|
|
|
|
var formFieldCounter = 0,
|
|
formFieldNestedLabelTypes = ['radio', 'checkbox'];
|
|
|
|
function formFieldId() {
|
|
return "form-field-" + (++formFieldCounter);
|
|
}
|
|
|
|
export let FormField = React.createClass({
|
|
_fieldRequiredText: 'This field is required',
|
|
_type: null,
|
|
_element: null,
|
|
|
|
propTypes: {
|
|
type: React.PropTypes.string.isRequired,
|
|
prefix: React.PropTypes.string,
|
|
postfix: React.PropTypes.string,
|
|
hasError: React.PropTypes.bool
|
|
},
|
|
getInitialState: function() {
|
|
return {
|
|
isError: null,
|
|
errorMessage: null,
|
|
}
|
|
},
|
|
componentWillMount: function() {
|
|
if (['text', 'number', 'radio', 'checkbox', 'file'].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 {
|
|
// Non <input> field, e.g. <select>, <textarea>
|
|
this._element = this.props.type;
|
|
}
|
|
},
|
|
showError: function(text) {
|
|
this.setState({
|
|
isError: true,
|
|
errorMessage: text,
|
|
});
|
|
},
|
|
focus: function() {
|
|
this.refs.field.focus();
|
|
},
|
|
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;
|
|
}
|
|
},
|
|
getSelectedElement: function() {
|
|
return this.refs.field.options[this.refs.field.selectedIndex];
|
|
},
|
|
render: function() {
|
|
// Pass all unhandled props to the field element
|
|
const otherProps = Object.assign({}, this.props),
|
|
isError = this.state.isError !== null ? this.state.isError : this.props.hasError,
|
|
elementId = this.props.id ? this.props.id : formFieldId(),
|
|
renderElementInsideLabel = this.props.label && formFieldNestedLabelTypes.includes(this.props.type);
|
|
|
|
delete otherProps.type;
|
|
delete otherProps.label;
|
|
delete otherProps.hasError;
|
|
delete otherProps.className;
|
|
delete otherProps.postfix;
|
|
delete otherProps.prefix;
|
|
|
|
const element = <this._element id={elementId} type={this._type} name={this.props.name} ref="field" placeholder={this.props.placeholder}
|
|
className={'form-field__input form-field__input-' + this.props.type + ' ' + (this.props.className || '') + (isError ? 'form-field__input--error' : '')}
|
|
{...otherProps}>
|
|
{this.props.children}
|
|
</this._element>;
|
|
|
|
return <div className="form-field">
|
|
{ this.props.prefix ? <span className="form-field__prefix">{this.props.prefix}</span> : '' }
|
|
{ renderElementInsideLabel ?
|
|
<label htmlFor={elementId} className={"form-field__label " + (isError ? 'form-field__label--error' : '')}>
|
|
{element}
|
|
{this.props.label}
|
|
</label> :
|
|
element }
|
|
{ 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>
|
|
}
|
|
})
|
|
|
|
export let FormRow = React.createClass({
|
|
_fieldRequiredText: 'This field is required',
|
|
propTypes: {
|
|
label: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element])
|
|
// helper: React.PropTypes.html,
|
|
},
|
|
getInitialState: function() {
|
|
return {
|
|
isError: false,
|
|
errorMessage: null,
|
|
}
|
|
},
|
|
showError: function(text) {
|
|
this.setState({
|
|
isError: true,
|
|
errorMessage: text,
|
|
});
|
|
},
|
|
showRequiredError: function() {
|
|
this.showError(this._fieldRequiredText);
|
|
},
|
|
clearError: function(text) {
|
|
this.setState({
|
|
isError: false,
|
|
errorMessage: ''
|
|
});
|
|
},
|
|
getValue: function() {
|
|
return this.refs.field.getValue();
|
|
},
|
|
getSelectedElement: function() {
|
|
return this.refs.field.getSelectedElement();
|
|
},
|
|
focus: function() {
|
|
this.refs.field.focus();
|
|
},
|
|
render: function() {
|
|
const fieldProps = Object.assign({}, this.props),
|
|
elementId = formFieldId(),
|
|
renderLabelInFormField = formFieldNestedLabelTypes.includes(this.props.type);
|
|
|
|
if (!renderLabelInFormField) {
|
|
delete fieldProps.label;
|
|
}
|
|
delete fieldProps.helper;
|
|
|
|
return <div className="form-row">
|
|
{ this.props.label && !renderLabelInFormField ?
|
|
<div className={"form-row__label-row " + (this.props.labelPrefix ? "form-row__label-row--prefix" : "") }>
|
|
<label htmlFor={elementId} className={"form-field__label " + (this.state.isError ? 'form-field__label--error' : '')}>
|
|
{this.props.label}
|
|
</label>
|
|
</div> : '' }
|
|
<FormField ref="field" hasError={this.state.isError} {...fieldProps} />
|
|
{ !this.state.isError && this.props.helper ? <div className="form-field__helper">{this.props.helper}</div> : '' }
|
|
{ this.state.isError ? <div className="form-field__error">{this.state.errorMessage}</div> : '' }
|
|
</div>
|
|
}
|
|
})
|