UNPKG

@kne/react-form-antd

Version:

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

854 lines (809 loc) 23.6 kB
import React, { forwardRef, useCallback, useState, useRef, useMemo } from 'react'; import classnames from 'classnames'; import ReactForm__default, { preset as preset$2, useReset, useSubmit } from '@kne/react-form'; export * from '@kne/react-form'; import { preset as preset$1, widget, hooks, hoc } from '@kne/react-form-helper'; export { hoc, hooks, utils, widget } from '@kne/react-form-helper'; import merge from 'lodash/merge'; import { TreeSelect, TimePicker, Input, Switch as Switch$1, Select, Radio, InputNumber, Button, DatePicker, Checkbox as Checkbox$1, Cascader as Cascader$1, Rate as Rate$1, Slider as Slider$1 } from 'antd'; import { IntlProvider, useIntl } from '@kne/react-intl'; import { CloseCircleFilled, CalendarOutlined } from '@ant-design/icons'; import dayjs from 'dayjs'; import useControlValue from '@kne/use-control-value'; function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; } const globalParams = { type: 'default', size: 'middle', rules: {}, formModal: {}, resetButton: {}, submitButton: {}, field: {} }; var preset = props => { const defaultProps = {}; merge(globalParams, props); Object.keys(globalParams.field).forEach(name => { defaultProps[name] = (globalParams.field[name] || {}).defaultProps; }); preset$1({ field: defaultProps, globalProps: globalParams.globalProps }); preset$2(globalParams.rules); }; const _excluded$5 = ["className", "cache", "enterSubmit", "scrollToError", "scrollProps", "type", "size", "children"]; const { ScrollToError, EnterSubmit, FormStore, MaxLabelProvider, SizeProvider } = widget; const Form = forwardRef((originProps, ref) => { const { className, cache, enterSubmit = false, scrollToError = true, scrollProps = { block: 'center' }, children } = originProps, props = _objectWithoutPropertiesLoose(originProps, _excluded$5); const baseClass = 'react-form'; let computedClass = baseClass; const { type, size } = Object.assign({ type: 'default', size: 'middle' }, globalParams, originProps); if (type !== 'default') { computedClass += `--${type}`; } if (size !== 'middle') { computedClass += `--${size}`; } const maxLabel = /*#__PURE__*/React.createElement(MaxLabelProvider, null, children); return /*#__PURE__*/React.createElement("form", { className: classnames(baseClass, computedClass, className), onSubmit: e => { e.preventDefault(); e.stopPropagation(); } }, /*#__PURE__*/React.createElement(SizeProvider, { value: { size } }, /*#__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, maxLabel) : maxLabel))); }); const locale$1 = { PleaseSelect: '请选择{label}', PleaseInput: '请输入{label}', StartTime: '开始时间', EndTime: '结束时间', StartDate: '开始日期', EndDate: '结束日期', SoFar: '至今' }; const locale = { PleaseSelect: 'Please select {label}', PleaseInput: 'Please enter {label}', StartTime: 'Start Time', EndTime: 'End Time', StartDate: 'Start Date', EndDate: 'End Date', SoFar: 'Present' }; const _excluded$4 = ["locale"]; const localeMap = { 'zh-CN': locale$1, 'en-US': locale }; const withLocale = WrappedComponent => { const WithLocaleComponent = _ref => { let { locale = 'zh-CN' } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$4); const messages = localeMap[locale] || locale$1; return /*#__PURE__*/React.createElement(IntlProvider, { locale: locale, messages: messages }, /*#__PURE__*/React.createElement(WrappedComponent, props)); }; WithLocaleComponent.displayName = `withLocale(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`; return WithLocaleComponent; }; const { useOnChange: useOnChange$b } = hooks; const TreeSelectInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'treeSelect' }, props); const render = useOnChange$b(Object.assign({ placeholder: formatMessage({ id: 'PleaseSelect' }, { label: mergedProps.label || '' }) }, mergedProps)); return render(TreeSelect); }; TreeSelectInner.Field = TreeSelect; TreeSelectInner.TreeNode = TreeSelect.TreeNode; const _TreeSelect = withLocale(TreeSelectInner); const { useOnChange: useOnChange$a } = hooks; const { RangePicker: RangePicker$1 } = TimePicker; const TimePickerInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'timePicker' }, props); const render = useOnChange$a(Object.assign({ placeholder: formatMessage({ id: 'PleaseSelect' }, { label: mergedProps.label || '' }) }, mergedProps)); return render(TimePicker); }; TimePickerInner.Field = TimePicker; const RangePickerInner$1 = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'rangeTimePicker' }, props); const render = useOnChange$a(Object.assign({ placeholder: formatMessage({ id: 'PleaseSelect' }, { label: mergedProps.label || '' }) }, mergedProps)); return render(RangePicker$1); }; RangePickerInner$1.Field = RangePicker$1; const _TimePicker = withLocale(TimePickerInner); _TimePicker.RangePicker = withLocale(RangePickerInner$1); const { useDecorator: useDecorator$2 } = hooks; const TextAreaInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'textArea' }, props); const render = useDecorator$2(Object.assign({ placeholder: formatMessage({ id: 'PleaseInput' }, { label: mergedProps.label }) }, mergedProps)); return render(Input.TextArea); }; TextAreaInner.Field = Input.TextArea; const TextArea = withLocale(TextAreaInner); const { useOnChange: useOnChange$9, useCheckedToValue: useCheckedToValue$1 } = hooks; const { withChecked: withChecked$1 } = hoc; const WithSwitch = withChecked$1(Switch$1); const Switch = props => { props = Object.assign({}, { fieldName: 'switch' }, props); const checkedProps = useCheckedToValue$1(props); const render = useOnChange$9(checkedProps); return render(WithSwitch); }; Switch.Field = Switch$1; const { useOnChange: useOnChange$8 } = hooks; const SelectInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'select' }, props); const render = useOnChange$8(Object.assign({ placeholder: formatMessage({ id: 'PleaseSelect' }, { label: mergedProps.label || '' }) }, mergedProps)); return render(Select); }; SelectInner.Field = Select; SelectInner.Option = Select.Option; SelectInner.OptGroup = Select.OptGroup; const _Select = withLocale(SelectInner); const _excluded$3 = ["onChange"]; const { useOnChange: useOnChange$7 } = hooks; const RadioGroup = _ref => { let { onChange } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$3); props = Object.assign({}, { fieldName: 'radioGroup' }, props); const handler = useCallback(e => { onChange && onChange(e.target.value); }, [onChange]); return /*#__PURE__*/React.createElement(Radio.Group, _extends({}, props, { onChange: handler })); }; const _RadioGroup = props => { const render = useOnChange$7(props); return render(RadioGroup); }; _RadioGroup.Field = RadioGroup; _RadioGroup.Radio = Radio; const { useDecorator: useDecorator$1 } = hooks; const InputNumberFieldInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'inputNumber', autoComplete: 'off' }, props); const render = useDecorator$1(Object.assign({ placeholder: formatMessage({ id: 'PleaseInput' }, { label: mergedProps.label }) }, mergedProps)); return render(InputNumber); }; InputNumberFieldInner.Field = InputNumber; const InputNumberField = withLocale(InputNumberFieldInner); const { useDecorator } = hooks; const InputFieldInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'input', autoComplete: 'off' }, props); const render = useDecorator(Object.assign({ placeholder: formatMessage({ id: 'PleaseInput' }, { label: mergedProps.label }) }, mergedProps)); return render(Input); }; InputFieldInner.Field = Input; const PasswordInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'password', autoComplete: 'off' }, props); const render = useDecorator(Object.assign({ placeholder: formatMessage({ id: 'PleaseInput' }, { label: mergedProps.label }) }, mergedProps)); return render(Input.Password); }; PasswordInner.Field = Input.Password; const InputField = withLocale(InputFieldInner); InputField.Password = withLocale(PasswordInner); const _excluded$2 = ["soFarText", "soFarValue", "picker"]; const { useOnChange: useOnChange$6 } = hooks; const PickerTodayInner = _ref => { let { soFarText, soFarValue = 'soFar', picker = 'date' } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$2); const { formatMessage } = useIntl(); const [data, onChange] = useControlValue(props); const [openStart, setOpenStart] = useState(false); const [openEnd, setOpenEnd] = useState(false); const [tempStart, setTempStart] = useState(null); // 临时存储开始时间 const [tempEnd, setTempEnd] = useState(null); // 临时存储结束时间 const [hovering, setHovering] = useState(false); // hover 状态 const containerRef = useRef(null); const isSwitchingRef = useRef(false); // 标记是否正在切换到结束时间选择 const soFarLabel = soFarText || formatMessage({ id: 'SoFar' }); // 根据 picker 类型获取格式化字符串 const formatPattern = useMemo(() => { const patterns = { date: 'YYYY-MM-DD', week: 'YYYY-wo', month: 'YYYY-MM', year: 'YYYY' }; return patterns[picker] || 'YYYY-MM-DD'; }, [picker]); // 判断是否有值 const hasValue = useMemo(() => { const [start, end] = data || []; return !!(start || end); }, [data]); // 判断是否为"至今" const isSoFar = useMemo(() => { const [, end] = data || []; return end === soFarValue; }, [data, soFarValue]); // 解析数据 const parsedValue = useMemo(() => { const [start, end] = data || []; return { start: start ? dayjs(start) : null, end: isSoFar ? null : end ? dayjs(end) : null }; }, [data, isSoFar]); // 显示文本 - 弹窗打开时显示临时值,关闭后显示实际值 const displayText = useMemo(() => { // 开始时间:有临时值时显示临时值 const startText = tempStart ? tempStart.format(formatPattern) : parsedValue.start ? parsedValue.start.format(formatPattern) : ''; // 结束时间:有临时值时显示临时值 const endText = tempEnd ? tempEnd.format(formatPattern) : isSoFar ? soFarLabel : parsedValue.end ? parsedValue.end.format(formatPattern) : ''; return { start: startText, end: endText }; }, [parsedValue, isSoFar, soFarLabel, tempStart, tempEnd, formatPattern]); // 点击整个Input框时,先弹出开始时间选择 const handleInputClick = useCallback(() => { const [start] = data || []; setTempStart(start ? dayjs(start) : null); setOpenStart(true); setOpenEnd(false); }, [data]); // 清除值 const handleClear = useCallback(e => { e.stopPropagation(); onChange([]); setTempStart(null); setTempEnd(null); setOpenStart(false); setOpenEnd(false); }, [onChange]); // 处理开始时间变化 const handleStartChange = useCallback(date => { setTempStart(date); isSwitchingRef.current = true; // 标记正在切换 setOpenStart(false); setTimeout(() => { setOpenEnd(true); isSwitchingRef.current = false; }, 100); }, []); // 处理结束时间变化(选择日期时实时更新显示,选择后确认值) const handleEndChange = useCallback(date => { if (date) { // 选择了有效日期,确认值 if (!tempStart) return; onChange([tempStart.toISOString(), date.toISOString()]); setOpenEnd(false); setTempStart(null); setTempEnd(null); } else { // 清空选择 setTempEnd(null); } }, [tempStart, onChange]); // 点击"至今"按钮 const handleSoFarClick = useCallback(() => { if (!tempStart) return; onChange([tempStart.toISOString(), soFarValue]); setOpenEnd(false); setTempStart(null); setTempEnd(null); }, [tempStart, onChange, soFarValue]); // 处理开始时间弹窗关闭(未完成选择) const handleStartOpenChange = useCallback(open => { if (!open) { setOpenStart(false); // 如果不是正在切换到结束时间,则清空临时状态 if (!isSwitchingRef.current) { setTempStart(null); } } }, []); // 处理结束时间弹窗关闭(未完成选择) const handleEndOpenChange = useCallback(open => { if (!open) { setOpenEnd(false); // 关闭结束时间弹窗时,不修改field值 setTempStart(null); setTempEnd(null); } }, []); // 结束时间面板变化时更新临时值(用于实时显示) const handleEndPanelChange = useCallback(date => { setTempEnd(date); }, []); // 空的 footer(用于开始时间,保持高度一致) const renderEmptyFooter = useCallback(() => /*#__PURE__*/React.createElement("div", { style: { height: 40 } }), []); // 带至今按钮的 footer const renderExtraFooter = useCallback(() => { const isCurrentSoFar = !tempStart && isSoFar; return /*#__PURE__*/React.createElement("div", { style: { textAlign: 'right' } }, /*#__PURE__*/React.createElement(Button, { type: isCurrentSoFar ? 'primary' : 'default', onClick: handleSoFarClick }, soFarLabel)); }, [isSoFar, tempStart, handleSoFarClick, soFarLabel]); // 结束时间的可选日期(不能早于开始时间) const endDisabledDate = useCallback(current => { if (!tempStart) return false; const startOfMap = { date: 'day', week: 'week', month: 'month', year: 'year' }; const unit = startOfMap[picker] || 'day'; return current && current < tempStart.startOf(unit); }, [tempStart, picker]); return /*#__PURE__*/React.createElement("div", { className: "date-picker-today-container", ref: containerRef }, /*#__PURE__*/React.createElement("div", { className: "date-picker-today-inputs", onClick: handleInputClick, onMouseEnter: () => setHovering(true), onMouseLeave: () => setHovering(false) }, /*#__PURE__*/React.createElement("div", { className: "date-picker-today-start" }, /*#__PURE__*/React.createElement("input", { readOnly: true, placeholder: formatMessage({ id: 'StartDate' }), value: displayText.start, className: "ant-input" })), /*#__PURE__*/React.createElement("span", { className: "date-picker-today-separator" }, "~"), /*#__PURE__*/React.createElement("div", { className: "date-picker-today-end" }, /*#__PURE__*/React.createElement("input", { readOnly: true, placeholder: formatMessage({ id: 'EndDate' }), value: displayText.end, className: `ant-input ${isSoFar ? 'so-far-active' : ''}` })), /*#__PURE__*/React.createElement("span", { className: "date-picker-today-suffix" }, hasValue && hovering ? /*#__PURE__*/React.createElement(CloseCircleFilled, { className: "date-picker-today-clear", onClick: handleClear }) : /*#__PURE__*/React.createElement(CalendarOutlined, null))), /*#__PURE__*/React.createElement(DatePicker, { picker: picker, open: openStart, onOpenChange: handleStartOpenChange, value: tempStart || parsedValue.start, onChange: handleStartChange, renderExtraFooter: renderEmptyFooter, showNow: false, getPopupContainer: () => containerRef.current || document.body, style: { width: 0, height: 0, visibility: 'hidden', position: 'absolute' }, placement: "bottomLeft" }), /*#__PURE__*/React.createElement(DatePicker, { picker: picker, open: openEnd, onOpenChange: handleEndOpenChange, value: tempEnd || parsedValue.end, onChange: handleEndChange, onPanelChange: handleEndPanelChange, disabledDate: endDisabledDate, renderExtraFooter: renderExtraFooter, showNow: false, getPopupContainer: () => containerRef.current || document.body, style: { width: 0, height: 0, visibility: 'hidden', position: 'absolute' }, placement: "bottomLeft" })); }; const PickerToday = withLocale(PickerTodayInner); const RangePickerToday = props => { props = Object.assign({}, { fieldName: 'rangePickerToday' }, props); const render = useOnChange$6(props); return render(PickerToday); }; RangePickerToday.Field = PickerToday; const { useOnChange: useOnChange$5 } = hooks; const { MonthPicker, RangePicker, WeekPicker } = DatePicker; const DatePickerInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'datePicker' }, props); const render = useOnChange$5(Object.assign({ placeholder: formatMessage({ id: 'PleaseSelect' }, { label: mergedProps.label || '' }) }, mergedProps)); return render(DatePicker); }; DatePickerInner.Field = DatePicker; const MonthPickerInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'monthDatePicker' }, props); const render = useOnChange$5(Object.assign({ placeholder: [formatMessage({ id: 'StartTime' }), formatMessage({ id: 'EndTime' })] }, mergedProps)); return render(MonthPicker); }; MonthPickerInner.Field = MonthPicker; const RangePickerInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'rangeDatePicker' }, props); const render = useOnChange$5(Object.assign({ placeholder: [formatMessage({ id: 'StartTime' }), formatMessage({ id: 'EndTime' })] }, mergedProps)); return render(RangePicker); }; RangePickerInner.Field = RangePicker; const WeekPickerInner = props => { const { formatMessage } = useIntl(); const mergedProps = Object.assign({}, { fieldName: 'weekDatePicker' }, props); const render = useOnChange$5(Object.assign({ placeholder: [formatMessage({ id: 'StartTime' }), formatMessage({ id: 'EndTime' })] }, mergedProps)); return render(WeekPicker); }; WeekPickerInner.Field = WeekPicker; const _DatePicker = withLocale(DatePickerInner); _DatePicker.MonthPicker = withLocale(MonthPickerInner); _DatePicker.RangePicker = withLocale(RangePickerInner); _DatePicker.WeekPicker = withLocale(WeekPickerInner); const { useOnChange: useOnChange$4 } = hooks; const _CheckboxGroup = Checkbox$1.Group; const CheckboxGroup = props => { props = Object.assign({}, { fieldName: 'checkboxGroup' }, props); const render = useOnChange$4(props); return render(_CheckboxGroup); }; CheckboxGroup.Checkbox = Checkbox$1; CheckboxGroup.Field = _CheckboxGroup; const { useOnChange: useOnChange$3, useCheckedToValue } = hooks; const { withChecked } = hoc; const WithCheckbox = withChecked(Checkbox$1); const Checkbox = props => { props = Object.assign({}, { fieldName: 'checkbox' }, props); const checkedProps = useCheckedToValue(Object.assign({}, props, { onChange: e => { e.target.type = 'checkbox'; props.onChange && props.onChange(e); } })); const render = useOnChange$3(checkedProps); return render(WithCheckbox); }; Checkbox.Field = Checkbox$1; const { useOnChange: useOnChange$2 } = hooks; const Cascader = props => { props = Object.assign({}, { fieldName: 'cascader' }, props); const render = useOnChange$2(props); return render(Cascader$1); }; Cascader.Field = Cascader$1; const { useOnChange: useOnChange$1 } = hooks; const Rate = props => { props = Object.assign({}, { fieldName: 'rate' }, props); const render = useOnChange$1(Object.assign({}, props)); return render(Rate$1); }; Rate.Field = Rate$1; const { useOnChange } = hooks; const Slider = props => { props = Object.assign({}, { fieldName: 'slider' }, props); const render = useOnChange(Object.assign({}, props)); return render(Slider$1); }; Slider.Field = Slider$1; const ResetButton = props => { const resetProps = useReset(); return /*#__PURE__*/React.createElement(Button, _extends({}, resetProps, props)); }; const _excluded$1 = ["type", "realtime", "disabled"], _excluded2 = ["isPass", "isLoading"]; const SubmitButton = _ref => { let { type = 'primary', realtime, disabled = false } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$1); const _useSubmit = useSubmit(props), { isPass, isLoading } = _useSubmit, submitProps = _objectWithoutPropertiesLoose(_useSubmit, _excluded2); return /*#__PURE__*/React.createElement(Button, _extends({ type: type, loading: isLoading, disabled: disabled || (realtime ? !isPass : false) }, props, submitProps)); }; 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 FormAntd = Form; const fields = { TreeSelect: _TreeSelect, TimePicker: _TimePicker, TextArea, Switch, Select: _Select, RadioGroup: _RadioGroup, InputNumber: InputNumberField, Input: InputField, DatePickerToday: RangePickerToday, DatePicker: _DatePicker, CheckboxGroup, Checkbox, Cascader, Rate, Slider }; export { CancelButton, Cascader, Checkbox, CheckboxGroup, _DatePicker as DatePicker, RangePickerToday as DatePickerToday, FormAntd, InputField as Input, InputNumberField as InputNumber, _RadioGroup as RadioGroup, Rate, ResetButton, _Select as Select, Slider, SubmitButton, Switch, TextArea, _TimePicker as TimePicker, _TreeSelect as TreeSelect, Form as default, fields, preset }; //# sourceMappingURL=index.modern.js.map