2017-05-19 14:42:43 -06:00
|
|
|
import React, { PureComponent } from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
2017-06-01 11:42:30 +09:00
|
|
|
import { intlShape } from 'meteor/vulcan:i18n';
|
2017-06-07 17:59:02 -07:00
|
|
|
import classNames from 'classnames';
|
2017-08-19 10:41:08 +09:00
|
|
|
import { Components } from 'meteor/vulcan:core';
|
2016-03-30 19:08:06 +09:00
|
|
|
|
2017-05-19 14:42:43 -06:00
|
|
|
class FormComponent extends PureComponent {
|
2016-03-30 19:08:06 +09:00
|
|
|
|
2016-04-17 16:47:04 +09:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.handleBlur = this.handleBlur.bind(this);
|
2017-06-07 17:59:02 -07:00
|
|
|
this.updateCharacterCount = this.updateCharacterCount.bind(this);
|
2017-08-16 16:18:40 +09:00
|
|
|
this.renderErrors = this.renderErrors.bind(this);
|
2017-06-07 17:59:02 -07:00
|
|
|
|
|
|
|
if (props.limit) {
|
|
|
|
this.state = {
|
|
|
|
limit: props.value ? props.limit - props.value.length : props.limit
|
|
|
|
}
|
|
|
|
}
|
2016-04-17 16:47:04 +09:00
|
|
|
}
|
|
|
|
|
2017-07-19 16:55:51 +09:00
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
this.updateCharacterCount(nextProps.name, nextProps.value)
|
|
|
|
}
|
|
|
|
|
2016-04-17 16:47:04 +09:00
|
|
|
handleBlur() {
|
2016-10-05 11:19:20 +02:00
|
|
|
// see https://facebook.github.io/react/docs/more-about-refs.html
|
|
|
|
if (this.formControl !== null) {
|
2017-01-23 15:50:55 +01:00
|
|
|
this.props.updateCurrentValues({[this.props.name]: this.formControl.getValue()});
|
2016-10-05 11:19:20 +02:00
|
|
|
}
|
2016-04-17 16:47:04 +09:00
|
|
|
}
|
|
|
|
|
2017-06-07 17:59:02 -07:00
|
|
|
updateCharacterCount(name, value) {
|
|
|
|
if (this.props.limit) {
|
2017-08-16 16:24:50 +09:00
|
|
|
const characterCount = value ? value.length : 0;
|
2017-06-07 17:59:02 -07:00
|
|
|
this.setState({
|
2017-08-16 16:24:50 +09:00
|
|
|
limit: this.props.limit - characterCount
|
2017-06-07 17:59:02 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-01 20:57:37 +09:00
|
|
|
renderComponent() {
|
2016-03-30 19:08:06 +09:00
|
|
|
|
2016-09-07 14:35:49 +02:00
|
|
|
// see https://facebook.github.io/react/warnings/unknown-prop.html
|
2017-08-16 16:18:40 +09:00
|
|
|
const { control, group, updateCurrentValues, document, beforeComponent, afterComponent, limit, errors, ...rest } = this.props; // eslint-disable-line
|
2016-09-07 14:35:49 +02:00
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
// const base = typeof this.props.control === 'function' ? this.props : rest;
|
2016-09-07 14:35:49 +02:00
|
|
|
|
2016-04-17 16:47:04 +09:00
|
|
|
const properties = {
|
2017-08-16 16:24:50 +09:00
|
|
|
value: '', // default value, will be overridden by `rest` if real value has been passed down through props
|
2017-05-04 11:16:03 +09:00
|
|
|
...rest,
|
2016-04-17 16:47:04 +09:00
|
|
|
onBlur: this.handleBlur,
|
2017-08-16 16:18:40 +09:00
|
|
|
ref: (ref) => this.formControl = ref,
|
2016-04-17 16:47:04 +09:00
|
|
|
};
|
|
|
|
|
2016-04-07 15:24:38 +09:00
|
|
|
// if control is a React component, use it
|
2017-08-19 16:17:52 +09:00
|
|
|
if (typeof this.props.control === 'function') {
|
2016-04-07 14:20:44 +09:00
|
|
|
|
2016-11-15 10:44:01 +01:00
|
|
|
return <this.props.control {...properties} document={document} />
|
2016-04-04 16:50:07 +09:00
|
|
|
|
2016-04-07 15:24:38 +09:00
|
|
|
} else { // else pick a predefined component
|
2016-04-04 16:50:07 +09:00
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
// for text fields, update character count on change
|
|
|
|
if (!this.props.control || ['number', 'url', 'email', 'textarea', 'text'].includes(this.props.control)) {
|
|
|
|
properties.onChange = this.updateCharacterCount;
|
|
|
|
}
|
|
|
|
|
2016-04-07 15:24:38 +09:00
|
|
|
switch (this.props.control) {
|
2017-08-19 10:41:08 +09:00
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'number':
|
2017-08-19 10:41:08 +09:00
|
|
|
return <Components.FormComponentNumber {...properties}/>;
|
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'url':
|
2017-08-19 10:41:08 +09:00
|
|
|
return <Components.FormComponentUrl {...properties}/>;
|
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'email':
|
2017-08-19 10:41:08 +09:00
|
|
|
return <Components.FormComponentEmail {...properties}/>;
|
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'textarea':
|
2017-08-19 10:41:08 +09:00
|
|
|
return <Components.FormComponentTextarea {...properties}/>;
|
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'checkbox':
|
2017-08-19 10:41:08 +09:00
|
|
|
return <Components.FormComponentCheckbox {...properties} />;
|
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'checkboxgroup':
|
2017-08-19 10:41:08 +09:00
|
|
|
return <Components.FormComponentCheckboxGroup {...properties} />;
|
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'radiogroup':
|
2017-05-04 11:16:03 +09:00
|
|
|
// not sure why, but onChange needs to be specified here
|
2017-08-19 10:41:08 +09:00
|
|
|
properties.onChange = (name, value) => {this.props.updateCurrentValues({[name]: value})};
|
|
|
|
return <Components.FormComponentRadioGroup {...properties} />;
|
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'select':
|
|
|
|
properties.options = [{label: this.context.intl.formatMessage({id: 'forms.select_option'}), disabled: true}, ...properties.options];
|
2017-08-19 10:41:08 +09:00
|
|
|
return <Components.FormComponentSelect {...properties} />;
|
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'datetime':
|
2017-08-19 10:41:08 +09:00
|
|
|
return <Components.FormComponentDateTime {...properties} />;
|
|
|
|
|
2017-08-19 16:17:52 +09:00
|
|
|
case 'text':
|
2017-08-19 10:41:08 +09:00
|
|
|
default:
|
|
|
|
return <Components.FormComponentDefault {...properties}/>;
|
2017-08-16 16:24:50 +09:00
|
|
|
|
2016-04-04 16:50:07 +09:00
|
|
|
}
|
|
|
|
|
2016-04-01 20:57:37 +09:00
|
|
|
}
|
2016-03-30 19:08:06 +09:00
|
|
|
}
|
|
|
|
|
2017-08-16 16:18:40 +09:00
|
|
|
renderErrors() {
|
|
|
|
return (
|
2017-08-19 16:17:52 +09:00
|
|
|
<ul className='form-input-errors'>
|
2017-08-16 16:18:40 +09:00
|
|
|
{this.props.errors.map((error, index) => <li key={index}>{error.message}</li>)}
|
|
|
|
</ul>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2016-04-01 20:57:37 +09:00
|
|
|
render() {
|
2017-08-16 16:18:40 +09:00
|
|
|
|
|
|
|
const hasErrors = this.props.errors && this.props.errors.length;
|
|
|
|
const inputClass = classNames('form-input', `input-${this.props.name}`, {'input-error': hasErrors});
|
|
|
|
|
2016-06-28 17:33:30 +09:00
|
|
|
return (
|
2017-08-16 16:18:40 +09:00
|
|
|
<div className={inputClass}>
|
2016-06-28 17:33:30 +09:00
|
|
|
{this.props.beforeComponent ? this.props.beforeComponent : null}
|
|
|
|
{this.renderComponent()}
|
2017-08-16 16:18:40 +09:00
|
|
|
{hasErrors ? this.renderErrors() : null}
|
2017-06-07 17:59:02 -07:00
|
|
|
{this.props.limit ? <div className={classNames('form-control-limit', {danger: this.state.limit < 10})}>{this.state.limit}</div> : null}
|
2016-06-28 17:33:30 +09:00
|
|
|
{this.props.afterComponent ? this.props.afterComponent : null}
|
|
|
|
</div>
|
|
|
|
)
|
2016-03-30 19:08:06 +09:00
|
|
|
}
|
2016-04-01 20:57:37 +09:00
|
|
|
|
2016-03-30 19:08:06 +09:00
|
|
|
}
|
|
|
|
|
2016-04-01 20:57:37 +09:00
|
|
|
FormComponent.propTypes = {
|
2017-05-19 14:42:43 -06:00
|
|
|
document: PropTypes.object,
|
|
|
|
name: PropTypes.string,
|
|
|
|
label: PropTypes.string,
|
|
|
|
value: PropTypes.any,
|
|
|
|
placeholder: PropTypes.string,
|
|
|
|
prefilledValue: PropTypes.any,
|
|
|
|
options: PropTypes.any,
|
|
|
|
control: PropTypes.any,
|
|
|
|
datatype: PropTypes.any,
|
|
|
|
disabled: PropTypes.bool,
|
|
|
|
updateCurrentValues: PropTypes.func
|
2016-04-01 20:57:37 +09:00
|
|
|
}
|
|
|
|
|
2017-01-31 17:55:26 +09:00
|
|
|
FormComponent.contextTypes = {
|
|
|
|
intl: intlShape
|
|
|
|
};
|
|
|
|
|
2016-04-01 20:57:37 +09:00
|
|
|
export default FormComponent;
|