UNPKG

cosmo-ui

Version:
217 lines 9.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var React = require("react"); var _1 = require("."); var cx = require("classnames"); var PropTypes = require("prop-types"); var data_1 = require("../data"); var styles = require('../../src/styles/components/forms.scss'); var borderStyles = require('../../src/styles/common/borders.scss'); /** * A wrapper component for form fields that * reads the form name of parent <Form /> elements */ var FormFieldWrapper = (function (_super) { tslib_1.__extends(FormFieldWrapper, _super); function FormFieldWrapper() { return _super !== null && _super.apply(this, arguments) || this; } FormFieldWrapper.prototype.componentWillMount = function () { // console.log('FORM FIELD WRAPPER WILL MOUNT', this) var name = this.context ? this.context.formName : this.props.formName; if (name) { this.formName = name; } }; return FormFieldWrapper; }(React.Component)); FormFieldWrapper.contextTypes = { formName: PropTypes.string, }; exports.FormFieldWrapper = FormFieldWrapper; /** * The base form field component is responsible for: * * - initializing field data using a set of standard fields (AbstractFormField) * - dispatching events and updating the store * - running the validators prior to updating the store * - rendering error messages underneath the field * - styling success and errors in red/green * * All of these are intedend to be handled in a standard way and thus * have been delegated to this class. Every class that extends this one * must implement the renderField method to render a specific input type */ var BaseFormField = (function (_super) { tslib_1.__extends(BaseFormField, _super); function BaseFormField(props) { var _this = _super.call(this, props) || this; /** * Use React refs to set the field elem */ _this.setFieldRef = function (e) { return _this.field = e; }; _this.validate = _this.props.validate ? _this.props.validate.bind(_this) : _this.validate.bind(_this); _this.onChange = _this.onChange.bind(_this); _this.onBlur = _this.onBlur.bind(_this); _this.onFocus = _this.onFocus.bind(_this); return _this; } /** * Initialize the field * this will also trigger the validation * but will not mark the field as dirty (only onFocus does that) */ BaseFormField.prototype.componentDidMount = function () { // console.log('FORM FIELD DID MOUNT', this) this.dispatchUpdate(this.getNextFieldState(this.getValueFromState())); if (this.props.autofocus) { this.field.focus(); } }; BaseFormField.prototype.render = function () { // console.log('RENDER FORM FIELD', this) // the field may not be initialized yet if (!this.props.field) { return null; } return (React.createElement(_1.Column, { className: styles.fieldset }, this.renderField(), React.createElement(_1.Row, { align: "flex-start", className: styles.errorText }, this.renderErrors()))); }; BaseFormField.prototype.renderErrors = function () { if (this.shouldShowErrors()) { var errors = this.props.field.errors; return React.createElement("span", null, errors[0]); } }; /** * Default validation function * Checks to see if the field is required * And if a value is present * * @param value */ BaseFormField.prototype.validate = function (value) { if (this.props.field.required && !value) { return ['* required']; } return []; }; /** * This function instructs the class in how to create a blank field * It may be different for different types of form fields * Since a lot of fields are based on text input a default version * has been provided that uses the empty string as a default value */ BaseFormField.prototype.initializeField = function () { var _a = this.props, value = _a.value, required = _a.required; return data_1.createEmptyFormField(value || '', required === false ? false : true, false); }; /** * Create an object representing the next state state of the field * * @param value */ BaseFormField.prototype.getNextFieldState = function (value) { var field = tslib_1.__assign({}, this.props.field, { value: value }); // do not mess with the validation if an async validation function is present if (!this.props.asyncValidate) { // people might think that the validation function is supposed // to return a boolean rather than an array of string errors // rather than leave this as a potential bug better to support it here var res = this.validate(value); field.errors = res === true ? [] : (res === false ? ['* error'] : res); field.valid = field.errors && field.errors.length ? false : true; } return field; }; BaseFormField.prototype.onChange = function (e) { e.preventDefault(); var _a = this.props, name = _a.name, formName = _a.formName, onChange = _a.onChange, asyncOnChange = _a.asyncOnChange; // console.log('handling change', this.props) var field = this.getNextFieldState(this.getValueFromEvent(e)); if (onChange) { onChange(name, formName, field); } if (asyncOnChange) { asyncOnChange(name, formName, field); } return this.dispatchUpdate(field); }; BaseFormField.prototype.onBlur = function (e) { e.preventDefault(); var field = tslib_1.__assign({}, this.props.field, { focused: false }); if (this.props.onBlur) { this.props.onBlur(this.props.name, this.props.formName, field); } return this.dispatchUpdate(field); }; BaseFormField.prototype.onFocus = function (e) { e.preventDefault(); var field = tslib_1.__assign({}, this.props.field, { focused: true, dirty: true }); if (this.props.onFocus) { this.props.onFocus(this.props.name, this.props.formName, field); } return this.dispatchUpdate(field); }; BaseFormField.prototype.dispatchUpdate = function (field) { this.props.setFormField(this.props.name, this.props.formName, field); }; BaseFormField.prototype.getValueFromEvent = function (e) { return e.currentTarget.value; }; /** * Decide which value to use - props.value or props.field.value * This distinction is there because some inputs are 'semi-controlled' * This happens when setting and updating the value is handled by the client * but fields such as dirty, valid, focused etc are handled by cosmo ui * * NOTE: this function should not be typed because in the case of some inputs * such as the number input, it is saved as a number in the state but it is * inputted as a string */ BaseFormField.prototype.getValueFromState = function () { var _a = this.props, value = _a.value, onChange = _a.onChange, field = _a.field; // if both a value and an onChange handler are provided // then the client wishes to control the field manually // otherwise assume the version provided by cosmo-ui formReducer is in play return value && onChange ? value : field.value; }; BaseFormField.prototype.shouldShowErrors = function () { var _a = this.props.field, valid = _a.valid, dirty = _a.dirty, submitted = _a.submitted, focused = _a.focused; // to show errors then the field must be invalid and either submitted or // dirty and not focused (if it's focused the user is changing stuff) return !valid && (submitted || (dirty && !focused)); }; BaseFormField.prototype.shouldShowValid = function () { var _a = this.props.field, valid = _a.valid, dirty = _a.dirty, submitted = _a.submitted, focused = _a.focused, required = _a.required, errors = _a.errors; // obviously it becoes visible if its dirty or submitted // but if the field isn't required then we won't show any colour // at all whilst it's still pristine (hence the required part here) var visible = (required || dirty || submitted); return !this.props.disabled && visible && valid && !errors.length; }; BaseFormField.prototype.classNames = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var _a = this.props.field, valid = _a.valid, errors = _a.errors, focused = _a.focused, required = _a.required; var res = cx.apply(void 0, [styles.field].concat(args, [this.props.className, (_b = {}, _b[styles.disabled] = this.props.disabled === true, _b[borderStyles.error] = !this.props.disabled && this.shouldShowErrors(), _b[borderStyles.success] = this.shouldShowValid(), _b[borderStyles.info] = !this.props.disabled && focused && !valid, _b)])); // console.log('classnames', this.props, res) return res; var _b; }; return BaseFormField; }(React.Component)); exports.BaseFormField = BaseFormField; //# sourceMappingURL=base-form-field.js.map