UNPKG

@kne/react-form-antd-mobile

Version:

把@kne/react-form 表单校验逻辑应用到antd-mobile

996 lines (969 loc) 27 kB
import React, { forwardRef, useRef, useEffect, useImperativeHandle, useCallback, useState, useMemo } from 'react'; import PropTypes from 'prop-types'; import { List, Checkbox as Checkbox$2, Space, Popup, NavBar, Button, CheckList as CheckList$1, ImageUploader as ImageUploader$2, Input as Input$1, Picker as Picker$1, CascadePicker, DatePicker, Radio, Rate as Rate$1, Selector as Selector$1, Slider as Slider$1, Stepper as Stepper$1, Switch as Switch$2, TextArea as TextArea$1 } from 'antd-mobile'; import classnames from 'classnames'; import ReactForm__default, { preset as preset$1, RULES, useReset, useSubmit } from '@kne/react-form'; export * from '@kne/react-form'; import { widget, hoc, hooks } from '@kne/react-form-helper'; export { hooks, utils, widget } from '@kne/react-form-helper'; import { withFetch } from '@kne/react-fetch'; import useEvent from '@kne/use-event'; import ReactDOM from 'react-dom'; import merge from 'lodash/merge'; import range from 'lodash/range'; import get from 'lodash/get'; import dayjs from 'dayjs'; function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } const _excluded$a = ["className", "cache", "enterSubmit", "scrollToError", "scrollProps", "children"]; const { ScrollToError, EnterSubmit, FormStore } = widget; const Form = forwardRef((_ref, ref) => { let { className, cache, enterSubmit, scrollToError, scrollProps, children } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$a); const inner = children; return /*#__PURE__*/React.createElement("form", { className: classnames('react-form', className), onSubmit: e => { e.preventDefault(); e.stopPropagation(); } }, /*#__PURE__*/React.createElement(ReactForm__default, _extends({}, props, { ref: ref }), cache ? /*#__PURE__*/React.createElement(FormStore, { cache: cache }) : null, scrollToError ? /*#__PURE__*/React.createElement(ScrollToError, { scrollProps: scrollProps }) : null, enterSubmit ? /*#__PURE__*/React.createElement(EnterSubmit, null, inner) : inner)); }); Form.defaultProps = { scrollToError: true, enterSubmit: false, scrollProps: { block: 'center' } }; Form.propTypes = { className: PropTypes.string, enterSubmit: PropTypes.bool, scrollToError: PropTypes.bool, scrollProps: PropTypes.shape({ block: PropTypes.oneOf(['start', 'center', 'end', 'nearest']), behavior: PropTypes.oneOf(['auto', 'smooth']), inline: PropTypes.oneOf(['start', 'center', 'end', 'nearest']) }) }; const withItem = WrappedComponent => props => { console.log(props); const listProps = props.labelHidden === true ? {} : { title: props.label }; return /*#__PURE__*/React.createElement(List.Item, _extends({}, listProps, { className: classnames(listProps.className, { 'is-req': typeof props.rule === 'string' && (props.rule || '').split(' ').indexOf('REQ') > -1 }) }), /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, props, { labelHidden: true }))); }; const { useOnChange: useOnChange$9, useCheckedToValue: useCheckedToValue$1 } = hooks; const { withChecked: withChecked$1 } = hoc; const WithCheckbox = withChecked$1(Checkbox$2); const Checkbox$1 = props => { const checkedProps = useCheckedToValue$1(props); const render = useOnChange$9(checkedProps); return render(WithCheckbox); }; Checkbox$1.Item = withItem(Checkbox$1); const _excluded$9 = ["data", "setData", "refresh", "reload", "fetchEmitter", "isLoading", "children", "onLoaded"]; const { useOnChange: useOnChange$8 } = hooks; const withFetchList = WrappedComponent => { const FieldInner = withFetch(_ref => { let { data, setData, refresh, reload, fetchEmitter, isLoading, children, onLoaded } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$9); const refreshRef = useRef(refresh); refreshRef.current = refresh; const reloadRef = useRef(reload); reloadRef.current = reload; const setDataRef = useRef(setData); setDataRef.current = setData; const dataRef = useRef(data); useEffect(() => { const token1 = fetchEmitter.addListener('select-fetch-refresh', () => refreshRef.current()); const token2 = fetchEmitter.addListener('select-fetch-reload', () => reloadRef.current()); const token3 = fetchEmitter.addListener('select-fetch-set-data', () => setDataRef.current()); return () => { token1 && token1.remove(); token2 && token2.remove(); token3 && token3.remove(); }; }, [fetchEmitter]); useEffect(() => { onLoaded && onLoaded(dataRef.current); }, []); return /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, props, children({ data, refresh, isLoading, setData }))); }); const FetchComponent = forwardRef((props, ref) => { const fetchEmitter = useEvent(); useImperativeHandle(ref, () => { return { refresh: () => fetchEmitter.emit('select-fetch-refresh'), reload: () => fetchEmitter.emit('select-fetch-reload'), setData: data => fetchEmitter.emit('select-fetch-set-data', data) }; }, [fetchEmitter]); const render = useOnChange$8(Object.assign({ placeholder: `请选择${props.label}` }, props, { fetchEmitter })); return render(FieldInner); }); return FetchComponent; }; const _excluded$8 = ["onChange", "children", "options"]; const { useOnChange: useOnChange$7 } = hooks; const CheckboxGroup$1 = _ref => { let { onChange, children, options } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$8); const handler = useCallback(value => { onChange && onChange(value); }, [onChange]); return /*#__PURE__*/React.createElement(Checkbox$2.Group, _extends({}, props, { onChange: handler }), /*#__PURE__*/React.createElement(Space, { direction: "vertical" }, children || options.map(({ label, value, others }) => /*#__PURE__*/React.createElement(Checkbox$2, _extends({}, others, { value: value, key: value }), label)))); }; const _CheckboxGroup = props => { const render = useOnChange$7(props); return render(CheckboxGroup$1); }; _CheckboxGroup.Checkbox = Checkbox$2; _CheckboxGroup.Item = withItem(_CheckboxGroup); _CheckboxGroup.Fetch = withFetchList(CheckboxGroup$1); _CheckboxGroup.FetchItem = withItem(_CheckboxGroup.Fetch); const _excluded$7 = ["onConfirm", "onClose", "afterClose"]; const renderToBody = element => { const container = document.createElement('div'); document.body.appendChild(container); function unmount() { const unmountResult = ReactDOM.unmountComponentAtNode(container); if (unmountResult && container.parentNode) { container.parentNode.removeChild(container); } } ReactDOM.render(element, container); return unmount; }; const withLayer = WrappedComponent => props => { const Layer = _ref => { let { onConfirm, onClose, afterClose } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$7); const [visible, setVisible] = useState(false); useEffect(() => { setVisible(true); }, []); return /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, props, { visible: visible, onConfirm: (val, extend) => { onConfirm && onConfirm(val, extend); }, onClose: () => { onClose && onClose(); setVisible(false); }, afterClose: () => { afterClose && afterClose(); unmount(); } })); }; const unmount = renderToBody( /*#__PURE__*/React.createElement(Layer, props)); return unmount; }; const _excluded$6 = ["emitter"]; const { useOnChange: useOnChange$6 } = hooks; const withDecoratorList = (LabelComponent, hasFetch) => WrappedComponent => { const createPopup = withLayer(WrappedComponent); const FieldComponent = _ref => { let { emitter } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$6); const showPopup = (e, otherProps) => { e && e.stopPropagation(); createPopup(Object.assign({}, props, otherProps)); }; const showPopupRef = useRef(showPopup); showPopupRef.current = showPopup; useEffect(() => { const token = emitter && emitter.addListener('show', () => showPopupRef.current()); return () => { token && token.remove(); }; }, [emitter]); return /*#__PURE__*/React.createElement(LabelComponent, _extends({}, props, { showPopup: showPopup })); }; FieldComponent.defaultProps = { render: ({ label, placeholder, onClick }) => { return /*#__PURE__*/React.createElement("span", { className: classnames({ "react-form__placeholder": !label }), onClick: onClick }, label || placeholder); } }; const Field = props => { const render = useOnChange$6(Object.assign({ placeholder: `请选择${props.label}` }, props)); return render(FieldComponent); }; Field.defaultProps = {}; Field.Item = props => { const emitter = useEvent(); const listProps = props.labelHidden === true ? {} : { title: props.label }; return /*#__PURE__*/React.createElement(List.Item, _extends({}, listProps, { className: classnames(listProps.className, { 'is-req': typeof props.rule === 'string' && (props.rule || '').split(' ').indexOf('REQ') > -1 }), onClick: () => { emitter.emit('show'); } }), /*#__PURE__*/React.createElement(Field, _extends({}, props, { labelHidden: true, emitter: emitter }))); }; if (hasFetch) { const FetchField = withFetchList(FieldComponent); Field.Fetch = FetchField; Field.FetchItem = props => { const emitter = useEvent(); return /*#__PURE__*/React.createElement(List.Item, { title: props.label, className: classnames(props.className, { 'is-req': typeof props.rule === 'string' && (props.rule || '').split(' ').indexOf('REQ') > -1 }), onClick: () => { emitter.emit('show'); } }, /*#__PURE__*/React.createElement(FetchField, _extends({}, props, { labelHidden: true, emitter: emitter }))); }; } return Field; }; const _excluded$5 = ["value", "visible", "onConfirm", "onClose", "afterClose", "placeholder", "onChange", "options", "children"], _excluded2$2 = ["label", "value"]; const CheckListPopup = _ref => { let { value, visible, onConfirm, onClose, afterClose, placeholder, onChange, options, children } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$5); const [pickerValue, setPickerValue] = useState(value); return /*#__PURE__*/React.createElement(Popup, { bodyClassName: "react-form__popup", visible: visible, onConfirm: onConfirm, onClose: onClose, afterClose: afterClose, position: "right" }, /*#__PURE__*/React.createElement(NavBar, { backArrow: /*#__PURE__*/React.createElement(Button, { color: "primary", fill: "none" }, "\u8FD4\u56DE"), right: /*#__PURE__*/React.createElement(Button, { color: "primary", fill: "none", onClick: () => { onChange(pickerValue); onClose(); } }, "\u786E\u5B9A"), onBack: () => { onClose(); } }, placeholder), /*#__PURE__*/React.createElement(CheckList$1, _extends({}, props, { options: options, onChange: value => { setPickerValue(value); }, value: pickerValue }), children || options.map(_ref2 => { let { label, value } = _ref2, others = _objectWithoutPropertiesLoose(_ref2, _excluded2$2); return /*#__PURE__*/React.createElement(CheckList$1.Item, _extends({}, others, { value: value, key: value }), label); }))); }; const CheckListInput = ({ render, options, placeholder, value, showPopup }) => { const label = useMemo(() => { if (!value) { return ''; } return value.map(value => { const item = (options || []).find(item => item.value === value); if (item) { return item.label || item.value; } return ''; }).filter(item => !!item).join(','); }, [value, options]); return render({ label, value, placeholder, onClick: showPopup }); }; const CheckListField = withDecoratorList(CheckListInput, true)(CheckListPopup); const globalParams = { rules: {}, field: { imageUploader: { upload: file => { return { url: URL.createObjectURL(file) }; } } } }; const oldREQ = RULES.REQ; preset$1({ REQ: (...props) => { return Object.assign({}, oldREQ(...props), { errMsg: '%s不能为空' }); } }); var preset = (props => { merge(globalParams, props); preset$1(globalParams.rules); }); const { useOnChange: useOnChange$5 } = hooks; const ImageUploader$1 = props => { const render = useOnChange$5(Object.assign({}, props, { upload: globalParams.field.imageUploader.upload })); return render(ImageUploader$2); }; ImageUploader$1.defaultProps = { value: [] }; const { useDecorator: useDecorator$2 } = hooks; const InputField = props => { const render = useDecorator$2(Object.assign({ placeholder: `请输入${props.label}` }, props)); return render(Input$1); }; InputField.Item = withItem(InputField); const _excluded$4 = ["max", "min", "precision", "soFar", "renderLabel", "value", "onConfirm"]; const precisionToLength = precision => { return { 'year': 1, 'month': 2, 'day': 3 }[precision] || 3; }; const useRange = ({ max, min, value, renderLabel, soFar, precision }) => { const currentDate = dayjs(value), currentYear = currentDate.year(), currentMonth = currentDate.month(); const minDate = dayjs(min), minYear = minDate.year(), minMonth = minDate.month(), minDay = minDate.date(); const maxDate = dayjs(max), maxYear = maxDate.year(), maxMonth = maxDate.month(), maxDay = maxDate.date(); const yearList = useMemo(() => { const list = range(minYear, maxYear + 1).map(value => { return { value, label: renderLabel('year', value) }; }); soFar && list.push({ value: -1, label: '至今' }); return list; }, [minYear, maxYear, renderLabel, soFar]); const minRangeMonth = minYear === currentYear ? minMonth : 0, maxRangeMonth = maxYear === currentYear ? maxMonth + 1 : 12; const monthList = useMemo(() => { return range(minRangeMonth, maxRangeMonth).map(value => { return { value: value, label: renderLabel('month', value + 1) }; }); }, [minRangeMonth, maxRangeMonth, renderLabel]); const minRangeDay = minYear === currentYear && minMonth === currentMonth ? minDay : 1; const maxRangeDay = maxYear === currentYear && maxMonth === currentMonth ? maxDay + 1 : (() => { if (currentMonth === 1 && currentYear % 4 === 0) { return 30; } if (currentMonth === 1 && currentYear % 4 !== 0) { return 29; } if ([1, 3, 5, 7, 8, 10, 12].indexOf(currentMonth) >= 1) { return 32; } return 31; })(); const dayList = useMemo(() => { return range(minRangeDay, maxRangeDay).map(value => { return { value, label: renderLabel('day', value) }; }); }, [minRangeDay, maxRangeDay, renderLabel]); const length = precisionToLength(precision); return value === 'sofar' ? [yearList, [], []].slice(0, length) : [yearList, monthList, dayList].slice(0, length); }; const dateToColumns = (value, length = 3) => { const startValue = get(value, '0'), endValue = get(value, '1'), output = []; if (!startValue || !endValue) { const now = dayjs(), year = now.year(), month = now.month(), day = now.date(); return [...[year, month, day].slice(0, length), ...[year, month, day].slice(0, length)]; } const startDate = dayjs(startValue); output.push(...[startDate.year(), startDate.month(), startDate.date()].slice(0, length)); if (endValue === 'sofar') { output.push(-1); } else { const endDate = dayjs(endValue); output.push(...[endDate.year(), endDate.month(), endDate.date()].slice(0, length)); } return output; }; const columnsToDate = value => { if (value[0] === -1) { return 'sofar'; } let output = dayjs(new Date(0)); ['year', 'month', 'date'].forEach((key, index) => { if (value[index] !== void 0) { output = output[key](value[index]); } else { return; } }); return output.toDate(); }; const DateRangePicker = _ref => { let { max, min, precision, soFar, renderLabel, value: currentValue, onConfirm } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$4); const length = precisionToLength(precision); const [value, setValue] = useState(dateToColumns(currentValue, length)); const { startValue, endValue } = useMemo(() => { return { startValue: columnsToDate(value.slice(0, length)), endValue: columnsToDate(value.slice(length)) }; }, [value, length]); const startList = useRange({ max, min, value: startValue, renderLabel, precision }); const endList = useRange({ max, min: startValue || min, value: endValue, renderLabel, soFar, precision }); const columns = useMemo(() => startList.concat(endList), [startList, endList]); return /*#__PURE__*/React.createElement(Picker$1, _extends({}, props, { columns: columns, value: value, afterShow: () => { setValue(dateToColumns(currentValue, length)); }, onSelect: value => { setValue(value); }, onConfirm: () => { onConfirm && onConfirm([columnsToDate(value.slice(0, length)), columnsToDate(value.slice(length))]); } })); }; DateRangePicker.defaultProps = { renderLabel: (type, data) => { return data; }, value: [new Date(), new Date()], soFar: true, precision: 'day', min: new Date('1949-10-01'), max: new Date() }; const _excluded$3 = ["onChange"]; const withOnConfirm = WrappedComponent => _ref => { let { onChange } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$3); return /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, props, { title: props.placeholder, onConfirm: value => { onChange(value); } })); }; const PickerField = withDecoratorList(({ render, placeholder, showPopup, value, columns }) => { const label = useMemo(() => { if (!value) { return ''; } return value.map((value, index) => { const item = (columns[index] || []).find(item => item.value === value); if (item) { return item.label || item.value; } return ''; }).filter(item => !!item).join(','); }, [value, columns]); return render({ label, value, placeholder, onClick: showPopup }); }, true)(withOnConfirm(Picker$1)); PickerField.CascadePicker = withDecoratorList(({ render, placeholder, showPopup, value, options }) => { const label = useMemo(() => { if (!value) { return ''; } const output = []; let columns = options; for (let item of value) { const obj = columns.find(({ value }) => value === item); if (!obj) { break; } output.push(obj.label || obj.value); columns = obj.children || []; } return output.filter(item => !!item).join(','); }, [value, options]); return render({ label, value, placeholder, onClick: showPopup }); }, true)(withOnConfirm(CascadePicker)); PickerField.DatePicker = withDecoratorList(({ render, placeholder, showPopup, value, format }) => { const label = useMemo(() => { if (!value) { return ''; } return dayjs(value).format(format); }, [value]); return render({ label, value, placeholder, onClick: showPopup }); })(withOnConfirm(DatePicker)); PickerField.DatePicker.defaultProps = { format: 'YYYY-MM-DD', renderLabel: (type, data) => { switch (type) { case 'year': return data + '年'; case 'month': return data + '月'; case 'day': return data + '日'; case 'hour': return data + '时'; case 'minute': return data + '分'; case 'second': return data + '秒'; default: return data; } } }; PickerField.DateRangePicker = withDecoratorList(({ render, placeholder, showPopup, value, format }) => { const label = useMemo(() => { if (!value) { return ''; } return value.map(value => value === 'sofar' ? '至今' : dayjs(value).format(format)).join('~'); }, [value]); return render({ label, value, placeholder, onClick: showPopup }); })(withOnConfirm(DateRangePicker)); PickerField.DateRangePicker.defaultProps = { format: 'YYYY-MM-DD', renderLabel: (type, data) => { switch (type) { case 'year': return data + '年'; case 'month': return data + '月'; case 'day': return data + '日'; case 'hour': return data + '时'; case 'minute': return data + '分'; case 'second': return data + '秒'; default: return data; } } }; const _excluded$2 = ["onChange", "children", "options"], _excluded2$1 = ["label", "value"]; const { useOnChange: useOnChange$4 } = hooks; const RadioGroup$1 = _ref => { let { onChange, children, options } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$2); const handler = useCallback(value => { onChange && onChange(value); }, [onChange]); return /*#__PURE__*/React.createElement(Radio.Group, _extends({}, props, { onChange: handler }), /*#__PURE__*/React.createElement(Space, { direction: "vertical" }, children || options.map(_ref2 => { let { label, value } = _ref2, others = _objectWithoutPropertiesLoose(_ref2, _excluded2$1); return /*#__PURE__*/React.createElement(Radio, _extends({}, others, { value: value, key: value }), label); }))); }; const _RadioGroup = props => { const render = useOnChange$4(props); return render(RadioGroup$1); }; _RadioGroup.Radio = Radio; _RadioGroup.Item = withItem(_RadioGroup); _RadioGroup.Fetch = withFetchList(RadioGroup$1); _RadioGroup.FetchItem = withItem(_RadioGroup.Fetch); const { useOnChange: useOnChange$3 } = hooks; const RateField = props => { const render = useOnChange$3(props); return render(Rate$1); }; RateField.Item = withItem(RateField); const { useOnChange: useOnChange$2 } = hooks; const SelectorField = props => { const render = useOnChange$2(props); return render(Selector$1); }; SelectorField.Item = withItem(SelectorField); SelectorField.Fetch = withFetchList(Selector$1); SelectorField.FetchItem = withItem(SelectorField.Fetch); const { useOnChange: useOnChange$1 } = hooks; const SliderField = props => { const render = useOnChange$1(props); return render(Slider$1); }; SliderField.Item = withItem(SliderField); const { useDecorator: useDecorator$1 } = hooks; const StepperField = props => { const render = useDecorator$1(Object.assign({ placeholder: `请输入${props.label}` }, props)); return render(Stepper$1); }; StepperField.Item = withItem(StepperField); const { useOnChange, useCheckedToValue } = hooks; const { withChecked } = hoc; const WithSwitch = withChecked(Switch$2); const Switch$1 = props => { const checkedProps = useCheckedToValue(props); const render = useOnChange(checkedProps); return render(WithSwitch); }; Switch$1.Item = withItem(Switch$1); const { useDecorator } = hooks; const TextAreaField = props => { const render = useDecorator(Object.assign({ placeholder: `请输入${props.label}` }, props)); return render(TextArea$1); }; TextAreaField.Item = withItem(TextAreaField); const ResetButton = props => { const resetProps = useReset(); return /*#__PURE__*/React.createElement(Button, _extends({}, resetProps, props)); }; const _excluded$1 = ["realTime", "disabled"], _excluded2 = ["isPass", "isLoading"]; const SubmitButton = _ref => { let { realTime, disabled } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$1); const _useSubmit = useSubmit(props), { isPass, isLoading } = _useSubmit, submitProps = _objectWithoutPropertiesLoose(_useSubmit, _excluded2); return /*#__PURE__*/React.createElement(Button, _extends({ loading: isLoading, disabled: disabled || (realTime ? !isPass : false) }, submitProps, props)); }; SubmitButton.defaultProps = { type: 'primary', disabled: false }; const _excluded = ["onClick"]; const { useCacheRemove } = hooks; const CancelButton = _ref => { let { onClick } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded); const remove = useCacheRemove(); return /*#__PURE__*/React.createElement(Button, _extends({}, props, { onClick: (...args) => { remove(); onClick && onClick(...args); } })); }; const Checkbox = Checkbox$1; const CheckboxGroup = _CheckboxGroup; const CheckList = CheckListField; const ImageUploader = ImageUploader$1; const Input = InputField; const Picker = PickerField; const RadioGroup = _RadioGroup; const Rate = RateField; const Selector = SelectorField; const Slider = SliderField; const Stepper = StepperField; const Switch = Switch$1; const TextArea = TextAreaField; const fields = { Checkbox, CheckboxGroup, CheckList, ImageUploader, Input, Picker, RadioGroup, Rate, Selector, Slider, Stepper, Switch, TextArea }; export { CancelButton, CheckList, Checkbox, CheckboxGroup, ImageUploader, Input, Picker, RadioGroup, Rate, ResetButton, Selector, Slider, Stepper, SubmitButton, Switch, TextArea, Form as default, fields, preset, withDecoratorList }; //# sourceMappingURL=index.modern.js.map