UNPKG

extpoint-yii2

Version:

JavaScript part for projects on ExtPoint Yii2 Boilerplate and yii2-core

263 lines (228 loc) 8.16 kB
import React from 'react'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; import {Field as ReduxField, Fields as ReduxFields} from 'redux-form'; import _isBoolean from 'lodash-es/isBoolean'; import _isFunction from 'lodash-es/isFunction'; import _isArray from 'lodash-es/isArray'; import _isObject from 'lodash-es/isObject'; import _get from 'lodash-es/get'; import {types} from 'components'; class Input extends React.Component { static propTypes = { component: PropTypes.func, attributesMap: PropTypes.object, input: PropTypes.object, }; render() { const {component, ...props} = this.props; const FieldComponent = component; const values = {}; let errors = []; if (this.props.attributesMap) { Object.keys(this.props.attributesMap).forEach(attribute => { const name = this.props.attributesMap[attribute]; const fieldProps = _get(this.props, name); if (fieldProps.input) { values[name] = fieldProps.input.value; } else { fieldProps.input = {}; } // Set error if (fieldProps.meta.touched) { errors = errors.concat(fieldProps.meta.error || []); } }); } else { if (props.input) { values[props.input.name] = props.input.value; } else { props.input = {}; } // Set error if (this.props.meta.touched) { errors = errors.concat(this.props.meta.error || []); } } if (errors.length > 0) { props.errorProps = { error: errors.join(', ') }; } return ( <span> {Object.keys(values).map(name => this.renderHiddenInput(name, values[name]))} <FieldComponent {...props} /> </span> ); } renderHiddenInput(reduxName, value) { const inputName = reduxName.replace(/\.([^\.]+)/g, '[$1]'); if (_isArray(value)) { return [].concat(value || []).map((valueItem, index) => ( <span key={index}> <input key={`${reduxName}[]`} type='hidden' name={inputName} value={valueItem} /> </span> )); } return ( <input key={reduxName} type='hidden' name={inputName} value={value} /> ); } } class Field extends React.Component { static propTypes = { attribute: PropTypes.string.isRequired, formId: PropTypes.string, model: PropTypes.oneOfType([ PropTypes.string, PropTypes.func, ]), prefix: PropTypes.string, layout: PropTypes.string, layoutCols: PropTypes.arrayOf(PropTypes.number), component: PropTypes.oneOfType([ PropTypes.string, PropTypes.func, ]), label: PropTypes.oneOfType([ PropTypes.string, PropTypes.bool, PropTypes.element, ]), hint: PropTypes.oneOfType([ PropTypes.string, PropTypes.bool, PropTypes.element, ]), required: PropTypes.bool, }; static contextTypes = { formId: PropTypes.string, model: PropTypes.oneOfType([ PropTypes.string, PropTypes.func, ]), prefix: PropTypes.string, layout: PropTypes.string, layoutCols: PropTypes.arrayOf(PropTypes.number), }; static defaultProps = { prefix: '', }; render() { // Get params const {attribute, formId, model, prefix, component, label, hint, required, layout, layoutCols, ...props} = this.props; props.attribute = attribute; props.layout = layout || this.context.layout; props.layoutCols = layoutCols || this.context.layoutCols; // Form id const finedFormId = formId || this.context.formId; if (!finedFormId) { throw new Error(`Not found required param 'formId' for attribute '${attribute}'`); } props.formId = finedFormId; // Model meta const finedModelMeta = model || this.context.model; props.model = finedModelMeta; props.modelClass = _isObject(finedModelMeta) ? finedModelMeta.className : finedModelMeta; // Get meta item const metaItem = finedModelMeta ? types.getMetaItem(finedModelMeta, attribute) : {}; props.metaItem = metaItem; // Get input field config const fieldConfig = types.getFieldConfig(metaItem.appType || this.props.appType); // Get input component props.component = _isFunction(component) ? component : types.getFieldComponent(component || fieldConfig.component || 'StringField'); delete fieldConfig.component; // Get prefix let fullPrefix = [this.context.prefix, prefix].filter(Boolean).join('.'); if (fullPrefix) { fullPrefix += '.'; props.prefix = fullPrefix; } // Get label, hint, required let resultLabel = label || label === false || label === '' ? label : metaItem.label || ''; let resultHint = hint || hint === false || hint === '' ? hint : metaItem.hint || ''; let resultRequired = _isBoolean(required) ? required : metaItem.required || false; if (resultLabel !== false) { props.labelProps = { label: resultLabel, hint: resultHint, required: resultRequired, layout: props.layout, layoutCols: props.layoutCols, }; } if (resultHint) { props.hintProps = { hint: resultHint, layout: props.layout, layoutCols: props.layoutCols, }; } // Generate unique field id props.fieldId = `${props.formId}_${props.attribute}`; // Render redux field component if (fieldConfig.refAttributeOptions.length > 0) { const names = [fullPrefix + attribute]; props.attributesMap = { [attribute]: fullPrefix + attribute, }; fieldConfig.refAttributeOptions.forEach(key => { const refAttribute = metaItem[key]; const name = fullPrefix + refAttribute; props.attributesMap[refAttribute] = name; names.push(name); }); return ( <ReduxFields names={names} component={Input} props={{ ...fieldConfig, ...props, }} /> ); } else { return ( <ReduxField name={fullPrefix + attribute} component={Input} props={{ ...fieldConfig, ...props, }} /> ); } } } const FieldHoc = connect()(Field); export default class FieldHocWrapper extends React.Component { static contextTypes = { formId: PropTypes.string, }; render() { return ( <FieldHoc {...this.props} formId={this.props.formId || this.context.formId} /> ); } }