UNPKG

framework-entersol-web

Version:

Framework based on bootstrap 5

277 lines (256 loc) 9.06 kB
import React, { Fragment, createRef } from "react"; import PropTypes from "prop-types"; import { randomS4 } from "../../functions"; import eventHandler from "../../functions/event-handler"; import Component from "../../component"; export default class Field extends Component { static jsClass = 'Field'; static propTypes = { ...Component.propTypes, autoComplete: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), checkValidity: PropTypes.func, controlClasses: PropTypes.string, default: PropTypes.any, disabled: PropTypes.bool, readOnly: PropTypes.bool, errorMessage: PropTypes.oneOfType([PropTypes.string, PropTypes.bool, PropTypes.node]), first: PropTypes.oneOf(['label', 'control']), inline: PropTypes.bool, label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), labelClasses: PropTypes.string, max: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), min: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), noValidate: PropTypes.bool, options: PropTypes.arrayOf(PropTypes.shape({ label: PropTypes.oneOfType([PropTypes.string, PropTypes.node, PropTypes.bool]), value: PropTypes.any, disabled: PropTypes.bool, divider: PropTypes.bool })), pattern: PropTypes.string, placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), required: PropTypes.bool, step: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), type: PropTypes.string.isRequired, value: PropTypes.any, accept: PropTypes.string, message: PropTypes.oneOfType([PropTypes.string, PropTypes.bool, PropTypes.node]), floating: PropTypes.bool } static defaultProps = { ...Component.defaultProps, type: 'text', default: '', value: '', first: 'label', floating: true } unique = randomS4(); state = { value: this.props.value || this.props.default, options: this.props.options, error: false } ContentWrap = 'div'; constructor(props) { super(props); this.onChange = this.onChange.bind(this); this.onInvalid = this.onInvalid.bind(this); this.onUpdate = this.onUpdate.bind(this); this.input = createRef(); } componentDidMount() { eventHandler.subscribe('update.' + this.props.name, this.onUpdate, this.unique); eventHandler.dispatch('ready.' + this.props.name); } componentWillUnmount() { clearTimeout(this.timeout); eventHandler.unsubscribe('update.' + this.props.name, this.unique); } extractString(obj) { if (typeof obj === 'string') return obj; else if (Array.isArray(obj)) { return obj.map(e => this.extractString(e)).join(' '); } else if (React.isValidElement(obj)) { return this.extractString(obj.props.children); } else if (!obj) return ''; return obj.toString(); } returnData(value = this.state.value, extra) { let { name, id, data } = this.props; let { error } = this.state; const toDispatch = { [name]: value }; if (id) toDispatch.id = id; if (data) toDispatch.data = data; if (this._reset) this._reset = false; else if (!error) { clearTimeout(this.timeout); this.timeout = setTimeout(() => { eventHandler.dispatch(name, toDispatch, extra); }, 300); } } isInvalid(value) { let { checkValidity, pattern, required } = this.props; let inputValid = true; let customInput = this.input.current?.querySelector('input') || this.input.current customInput?.setCustomValidity(''); if (typeof customInput?.checkValidity === 'function') { inputValid = customInput.checkValidity(); } let valueInvalid = !value; if (typeof value === 'boolean' || typeof value === 'number') { valueInvalid = false; } let error = (!inputValid || (required && valueInvalid)); if (!error && typeof checkValidity === 'function') error = !checkValidity(value); else if (pattern) error = !(new RegExp(pattern, "i")).test(value); if (!required && !value) error = false; if (error) { const errorMessage = this.extractString(this.props.errorMessage); customInput?.setCustomValidity(errorMessage); } return error; } onInvalid() { const { name, required } = this.props; const { value } = this.state; if (!required && !value) return; this.setState( { error: true }, () => eventHandler.dispatch('invalid.' + name, { [name]: value }) ); } onChange(e) { let { value } = e.target; let error; if(this.props.name === 'orden_compra') error = false else error = this.isInvalid(value); this.setState({ value, error }, () => this.returnData()); } onUpdate({ value, options, error, reset }) { const newState = {}; if (typeof value !== 'undefined') newState.value = (value !== null ? value : this.props.default); if (options) newState.options = options; if (typeof error === 'boolean') { newState.error = error; let message = ''; if (error) message = this.extractString(this.props.errorMessage); this.input.current.setCustomValidity(message); } if (reset) { newState.value = newState.value || this.props.default; this._reset = true; return this.setState(newState, this.returnData); } this.setState(newState, () => { if (this.input.current && typeof value !== 'undefined') { setTimeout(() => { const error = this.isInvalid(value); if (this.state.error !== error) this.setState({ error }); }, 300); } }); } onFocus = () => { const { name } = this.props; eventHandler.dispatch('focus.' + name); } get type() { return this.props.type; } get inputProps() { const { disabled, readOnly, accept, minLength, required, name, controlClasses, maxLength, list, placeholder: prePlaceholder, step, noValidate, multiple, autoComplete, min, max, pattern, dir, _propsControl = {} } = this.props; const { value, error } = this.state; const cn = [ 'form-control', controlClasses, error ? 'is-invalid' : '' ]; if (autoComplete === false) { var autocomplete = 'off'; var list1 = 'autocompleteOff'; } const placeholder = !!prePlaceholder ? this.extractString(prePlaceholder) : null; return { id: name, name, autoComplete: autocomplete || autoComplete, list: list1 || list, pattern, placeholder, required, type: this.type, value, className: cn.join(' '), min, max, step, noValidate, disabled, readOnly, ref: this.input, dir, accept, multiple, maxLength, minLength, onChange: this.onChange, onInvalid: this.onInvalid, onFocus: this.onFocus, ..._propsControl } } get labelNode() { const { placeholder, required, name, labelClasses, inline, label, disabled } = this.props; const cn = ['form-label', labelClasses]; if (inline) { cn.shift(); cn.push('py-2') } const style = {}; if (disabled) style['opacity'] = .9; const labelNode = <label className={cn.join(' ')} htmlFor={name} style={style}> {label ? label : placeholder} {required && <b title="Este campo es indispensable" className="text-inherit"> *</b>} </label> return (labelNode); } get inputNode() { const inputNode = (<input {...this.inputProps} />); return inputNode; } get errorMessageNode() { const { errorMessage } = this.props; const { error } = this.state; const errorNode = (<p className="m-1 lh-1"><small className="text-danger"> {errorMessage} </small></p>); return (error && errorMessage && errorNode); } get messageNode() { const { message, messageClasses } = this.props; const cnm = ['m-1 lh-1']; if (messageClasses) cnm.push(messageClasses); const node = (<p className={cnm.flat().join(' ')}><small> {message} </small></p>); return (message && node); } content(children = this.props.children) { const { inline, first, placeholder, label, floating, inlineControlClasses } = this.props; const cn = ['position-relative']; if (inline) cn.push('d-flex'); if (placeholder && !label && floating) cn.push('form-floating'); const wrapProps = {}; const className = cn.join(' '); const isSetPassField = placeholder === 'Repetir contraseña' if (this.ContentWrap !== Fragment) wrapProps.className = className; return (<> <this.ContentWrap {...wrapProps}> {floating && (first === 'label' && label) && this.labelNode} {inline ? <div className={inlineControlClasses}> {this.inputNode} {this.errorMessageNode} {this.messageNode} </div> : this.inputNode} {(floating && (first !== 'label' || (placeholder && !label)) || isSetPassField) && this.labelNode} {!inline && <> {this.errorMessageNode} {this.messageNode} </>} </this.ContentWrap> {children} </>); } };