UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

298 lines 10.6 kB
import _noop from "lodash/noop"; var __rest = this && this.__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import FormFoundation from '@douyinfe/semi-foundation/lib/es/form/foundation'; import { strings, cssClasses } from '@douyinfe/semi-foundation/lib/es/form/constants'; import { getUuidv4 } from '@douyinfe/semi-foundation/lib/es/utils/uuid'; import warning from '@douyinfe/semi-foundation/lib/es/utils/warning'; import BaseComponent from '../_base/baseComponent'; import { FormStateContext, FormApiContext, FormUpdaterContext } from './context'; import { isEmptyChildren } from '../_base/reactUtils'; import Row from '../grid/row'; import { cloneDeep } from '../_utils/index'; import Slot from './slot'; import Section from './section'; import Label from './label'; import ErrorMessage from './errorMessage'; import FormInputGroup from './group'; import '@douyinfe/semi-foundation/lib/es/form/form.css'; import { FormInput, FormInputNumber, FormTextArea, FormSelect, FormCheckboxGroup, FormCheckbox, FormRadioGroup, FormRadio, FormDatePicker, FormSwitch, FormSlider, FormTimePicker, FormTreeSelect, FormCascader, FormRating, FormAutoComplete, FormUpload, FormTagInput } from './field'; const prefix = cssClasses.PREFIX; class Form extends BaseComponent { constructor(props) { super(props); this.state = { formId: '' }; warning(Boolean(props.component && props.render), '[Semi Form] You should not use <Form component> and <Form render> in ths same time; <Form render> will be ignored'); warning(props.component && props.children && !isEmptyChildren(props.children), '[Semi Form] You should not use <Form component> and <Form>{children}</Form> in ths same time; <Form>{children}</Form> will be ignored'); warning(props.render && props.children && !isEmptyChildren(props.children), '[Semi Form] You should not use <Form render> and <Form>{children}</Form> in ths same time; <Form>{children}</Form> will be ignored'); this.submit = this.submit.bind(this); this.reset = this.reset.bind(this); this.foundation = new FormFoundation(this.adapter); this.formApi = this.foundation.getFormApi(); if (this.props.getFormApi) { this.props.getFormApi(this.formApi); } } componentDidMount() { this.foundation.init(); } componentWillUnmount() { this.foundation.destroy(); } get adapter() { return Object.assign(Object.assign({}, super.adapter), { cloneDeep, notifySubmit: (values, e) => { this.props.onSubmit(values, e); }, notifySubmitFail: (errors, values, e) => { this.props.onSubmitFail(errors, values, e); }, forceUpdate: callback => { this.forceUpdate(callback); }, notifyChange: formState => { this.props.onChange(formState); }, notifyValueChange: (values, changedValues) => { this.props.onValueChange(values, changedValues); }, notifyErrorChange: (errors, changedError) => { this.props.onErrorChange(errors, changedError); }, notifyReset: () => { this.props.onReset(); }, initFormId: () => { this.setState({ formId: getUuidv4() }); }, getInitValues: () => this.props.initValues, getFormProps: keys => { if (typeof keys === 'undefined') { return this.props; } else if (typeof keys === 'string') { return this.props[keys]; } else { const props = {}; keys.forEach(key => { props[key] = this.props[key]; }); return props; } }, getAllErrorDOM: () => { const { formId } = this.state; const { id } = this.props; const xId = id ? id : formId; return document.querySelectorAll(`form[x-form-id="${xId}"] .${cssClasses.PREFIX}-field-error-message`); }, getFieldDOM: field => { const { formId } = this.state; const { id } = this.props; const xId = id ? id : formId; return document.querySelector(`form[x-form-id="${xId}"] .${cssClasses.PREFIX}-field[x-field-id="${field}"]`); }, getFieldErrorDOM: field => { const { formId } = this.state; const { id } = this.props; const xId = id ? id : formId; let selector = `form[x-form-id="${xId}"] .${cssClasses.PREFIX}-field[x-field-id="${field}"] .${cssClasses.PREFIX}-field-error-message`; return document.querySelector(selector); } }); } get content() { const { children, component, render } = this.props; const formState = this.foundation.getFormState(); const props = { formState, formApi: this.foundation.getFormApi(), values: formState.values }; if (component) { return /*#__PURE__*/React.createElement(component, props); } if (render) { return render(props); } if (typeof children === 'function') { return children(props); } return children; } submit(e) { e.preventDefault(); if (this.props.stopPropagation && this.props.stopPropagation.submit) { e.stopPropagation(); } this.foundation.submit(e); } reset(e) { e.preventDefault(); if (this.props.stopPropagation && this.props.stopPropagation.reset) { e.stopPropagation(); } this.foundation.reset(); } render() { const needClone = false; const formState = this.foundation.getFormState(needClone); const updaterApi = this.foundation.getModifyFormStateApi(); const { formId } = this.state; const _a = this.props, { children, getFormApi, onChange, onSubmit, onSubmitFail, onErrorChange, onValueChange, component, render, validateFields, initValues, layout, style, className, labelPosition, labelWidth, labelAlign, labelCol, wrapperCol, allowEmpty, autoScrollToError, showValidateIcon, stopValidateWithError, extraTextPosition, id, trigger } = _a, rest = __rest(_a, ["children", "getFormApi", "onChange", "onSubmit", "onSubmitFail", "onErrorChange", "onValueChange", "component", "render", "validateFields", "initValues", "layout", "style", "className", "labelPosition", "labelWidth", "labelAlign", "labelCol", "wrapperCol", "allowEmpty", "autoScrollToError", "showValidateIcon", "stopValidateWithError", "extraTextPosition", "id", "trigger"]); const formCls = classNames(prefix, className, { [prefix + '-vertical']: layout === 'vertical', [prefix + '-horizontal']: layout === 'horizontal' }); const shouldAppendRow = wrapperCol && labelCol; const formContent = /*#__PURE__*/React.createElement("form", Object.assign({ style: style }, rest, { onReset: this.reset, onSubmit: this.submit, className: formCls, id: id ? id : formId, "x-form-id": id ? id : formId }), this.content); const withRowForm = /*#__PURE__*/React.createElement(Row, null, formContent); return /*#__PURE__*/React.createElement(FormUpdaterContext.Provider, { value: updaterApi }, /*#__PURE__*/React.createElement(FormApiContext.Provider, { value: this.formApi }, /*#__PURE__*/React.createElement(FormStateContext.Provider, { value: formState }, shouldAppendRow ? withRowForm : formContent))); } } Form.propTypes = { 'aria-label': PropTypes.string, onSubmit: PropTypes.func, onSubmitFail: PropTypes.func, /* Triggered from update, including field mount/unmount/value change/blur/verification status change/error prompt change, input parameter is formState, currentField */ onChange: PropTypes.func, onReset: PropTypes.func, // Triggered when the value of the form is updated, only when the value of the subfield changes. The entry parameter is formState.values onValueChange: PropTypes.func, autoScrollToError: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), allowEmpty: PropTypes.bool, className: PropTypes.string, component: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), disabled: PropTypes.bool, extraTextPosition: PropTypes.oneOf(strings.EXTRA_POS), getFormApi: PropTypes.func, initValues: PropTypes.object, validateFields: PropTypes.func, layout: PropTypes.oneOf(strings.LAYOUT), labelPosition: PropTypes.oneOf(strings.LABEL_POS), labelWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), labelAlign: PropTypes.oneOf(strings.LABEL_ALIGN), labelCol: PropTypes.object, render: PropTypes.func, style: PropTypes.object, showValidateIcon: PropTypes.bool, stopValidateWithError: PropTypes.bool, stopPropagation: PropTypes.shape({ submit: PropTypes.bool, reset: PropTypes.bool }), id: PropTypes.string, wrapperCol: PropTypes.object, trigger: PropTypes.oneOfType([PropTypes.oneOf(['blur', 'change', 'custom', 'mount']), PropTypes.arrayOf(PropTypes.oneOf(['blur', 'change', 'custom', 'mount']))]) }; Form.defaultProps = { onChange: _noop, onSubmitFail: _noop, onSubmit: _noop, onReset: _noop, onValueChange: _noop, onErrorChange: _noop, layout: 'vertical', labelPosition: 'top', allowEmpty: false, autoScrollToError: false, showValidateIcon: true }; Form.Input = FormInput; Form.TextArea = FormTextArea; Form.InputNumber = FormInputNumber; Form.Select = FormSelect; Form.Checkbox = FormCheckbox; Form.CheckboxGroup = FormCheckboxGroup; Form.Radio = FormRadio; Form.RadioGroup = FormRadioGroup; Form.DatePicker = FormDatePicker; Form.TimePicker = FormTimePicker; Form.Switch = FormSwitch; Form.Slider = FormSlider; Form.TreeSelect = FormTreeSelect; Form.Cascader = FormCascader; Form.Rating = FormRating; Form.AutoComplete = FormAutoComplete; Form.Upload = FormUpload; Form.TagInput = FormTagInput; Form.Slot = Slot; Form.ErrorMessage = ErrorMessage; Form.InputGroup = FormInputGroup; Form.Label = Label; Form.Section = Section; export default Form;