@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
329 lines (328 loc) • 11.8 kB
JavaScript
import React__default, { createContext, useRef } from "react";
import Cell__default from "./Cell.js";
import { C as ComponentDefaults } from "./typings.js";
import { a as __awaiter } from "./tslib.es6.js";
import Schema from "async-validator";
const Context = createContext({});
const SECRET = "NUT_FORM_INTERNAL";
class FormStore {
constructor() {
this.initialValues = {};
this.updateList = [];
this.store = {};
this.fieldEntities = [];
this.callbacks = {};
this.errors = {};
this.registerField = (field) => {
this.fieldEntities.push(field);
return () => {
this.fieldEntities = this.fieldEntities.filter((item) => item !== field);
if (this.store) {
delete this.store[field.props.name];
}
};
};
this.getFieldValue = (name) => {
var _a;
return (_a = this.store) === null || _a === void 0 ? void 0 : _a[name];
};
this.getFieldsValue = (nameList) => {
if (typeof nameList === "boolean") {
return JSON.parse(JSON.stringify(this.store));
}
const fieldsValue = {};
nameList.forEach((field) => {
fieldsValue[field] = this.getFieldValue(field);
});
return fieldsValue;
};
this.setInitialValues = (values, init) => {
if (init) {
this.initialValues = values;
this.store = values;
}
};
this.setFieldsValue = (newStore, needValidate = true) => {
this.store = Object.assign(Object.assign({}, this.store), newStore);
this.fieldEntities.forEach((entity) => {
const { name } = entity.props;
Object.keys(newStore).forEach((key) => {
if (key === name) {
entity.onStoreChange("update");
}
});
});
this.updateList.forEach((item) => {
let shouldUpdate = item.condition;
if (typeof item.condition === "function") {
shouldUpdate = item.condition();
}
if (shouldUpdate) {
item.entity.onStoreChange("update");
}
});
needValidate && this.validateFields();
};
this.setCallback = (callback) => {
this.callbacks = Object.assign(Object.assign({}, this.callbacks), callback);
};
this.validateFields = (nameList) => __awaiter(this, void 0, void 0, function* () {
var _a;
let filterEntitys = [];
const errs = [];
this.errors.length = 0;
if (!nameList || nameList.length === 0) {
filterEntitys = this.fieldEntities;
} else {
filterEntitys = this.fieldEntities.filter(({ props: { name } }) => nameList.includes(name));
}
for (const entity of filterEntitys) {
const { name, rules = [] } = entity.props;
const descriptor = {};
if (rules.length) {
if (rules.length > 1) {
descriptor[name] = [];
rules.forEach((v) => {
descriptor[name].push(v);
});
} else {
descriptor[name] = rules[0];
}
}
const validator = new Schema(descriptor);
try {
yield validator.validate({ [name]: (_a = this.store) === null || _a === void 0 ? void 0 : _a[name] });
} catch ({ errors }) {
if (errors) {
errs.push(...errors);
this.errors[name] = errors;
}
} finally {
if (!errs || errs.length === 0) {
this.errors[name] = [];
}
}
entity.onStoreChange("validate");
}
return errs;
});
this.submit = () => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d;
const errors = yield this.validateFields();
if (errors.length === 0) {
(_b = (_a = this.callbacks).onFinish) === null || _b === void 0 ? void 0 : _b.call(_a, this.store);
} else if (errors.length > 0) {
(_d = (_c = this.callbacks).onFinishFailed) === null || _d === void 0 ? void 0 : _d.call(_c, this.store, errors);
}
});
this.resetFields = () => {
this.errors.length = 0;
this.store = this.initialValues;
this.fieldEntities.forEach((entity) => {
entity.onStoreChange("reset");
});
};
this.registerUpdate = (field, shouldUpdate) => {
this.updateList.push({
entity: field,
condition: shouldUpdate
});
return () => {
this.updateList = this.updateList.filter((i) => i.entity !== field);
};
};
this.dispatch = ({ name }) => {
this.validateFields([name]);
};
this.getInternal = (key) => {
if (key === SECRET) {
return {
registerField: this.registerField,
setCallback: this.setCallback,
setInitialValues: this.setInitialValues,
dispatch: this.dispatch,
store: this.store,
fieldEntities: this.fieldEntities,
registerUpdate: this.registerUpdate
};
}
};
this.getForm = () => {
return {
getFieldValue: this.getFieldValue,
getFieldsValue: this.getFieldsValue,
setFieldsValue: this.setFieldsValue,
resetFields: this.resetFields,
validateFields: this.validateFields,
submit: this.submit,
errors: this.errors,
getInternal: this.getInternal
};
};
this.callbacks = {
onFinish: () => {
},
onFinishFailed: () => {
}
};
}
}
const useForm = (form) => {
const formRef = useRef();
if (!formRef.current) {
if (form) {
formRef.current = form;
} else {
const formStore = new FormStore();
formRef.current = formStore.getForm();
}
}
return [formRef.current];
};
function isForwardRefComponent(component) {
return component.type && component.type.$$typeof && // eslint-disable-next-line react/display-name
React__default.forwardRef(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
() => {
}
).$$typeof === component.type.$$typeof;
}
const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { required: false, name: "", label: "", rules: [{ required: false, message: "" }], errorMessageAlign: "left", validateTrigger: "onChange", shouldUpdate: false, noStyle: false });
class FormItem extends React__default.Component {
constructor(props) {
super(props);
this.getControlled = (children) => {
var _a;
const { setFieldsValue, getFieldValue } = this.context;
const { dispatch } = this.context.getInternal(SECRET);
const { name = "" } = this.props;
if ((_a = children === null || children === void 0 ? void 0 : children.props) === null || _a === void 0 ? void 0 : _a.defaultValue) {
console.warn("通过 initialValue 设置初始值");
}
const fieldValue = getFieldValue(name);
const controlled = Object.assign(Object.assign({}, children.props), { [this.props.valuePropName || "value"]: fieldValue !== void 0 ? fieldValue : this.props.initialValue, [this.props.trigger || "onChange"]: (...args) => {
const originOnChange = children.props[this.props.trigger || "onChange"];
if (originOnChange) {
originOnChange(...args);
}
let [next] = args;
if (this.props.getValueFromEvent) {
next = this.props.getValueFromEvent(...args);
}
setFieldsValue({ [name]: next }, false);
} });
const { validateTrigger } = this.props;
let validateTriggers = [this.props.trigger || "onChange"];
if (validateTrigger) {
validateTriggers = typeof validateTrigger === "string" ? [validateTrigger] : [...validateTrigger];
validateTriggers.forEach((trigger) => {
const originTrigger = controlled[trigger];
controlled[trigger] = (...args) => {
if (originTrigger) {
originTrigger(...args);
}
if (this.props.rules && this.props.rules.length) {
dispatch({
name: this.props.name
});
}
};
});
}
if (isForwardRefComponent(children)) {
controlled.ref = (componentInstance) => {
const originRef = children.ref;
if (originRef) {
if (typeof originRef === "function") {
originRef(componentInstance);
}
if ("current" in originRef) {
originRef.current = componentInstance;
}
}
this.componentRef = componentInstance;
};
}
return controlled;
};
this.refresh = () => {
this.setState(({ resetCount }) => ({
resetCount: resetCount + 1
}));
};
this.onStoreChange = (type) => {
if (type === "reset") {
this.context.errors[this.props.name] = [];
this.refresh();
} else {
this.forceUpdate();
}
};
this.renderLayout = (childNode) => {
const { label, name, required, rules, className, style, errorMessageAlign, align } = Object.assign(Object.assign({}, defaultProps), this.props);
const requiredInRules = rules === null || rules === void 0 ? void 0 : rules.some((rule) => rule.required);
const item = name ? this.context.errors[name] : [];
const { starPosition } = this.context;
const renderStar = (required || requiredInRules) && React__default.createElement("i", { className: "required" });
const renderLabel = React__default.createElement(
React__default.Fragment,
null,
starPosition === "left" ? renderStar : null,
label,
starPosition === "right" ? renderStar : null
);
return React__default.createElement(
Cell__default,
{ className: `nut-form-item ${className}`, style, align, onClick: (e) => this.props.onClick && this.props.onClick(e, this.componentRef) },
label ? React__default.createElement("div", { className: "nut-cell-title nut-form-item-label" }, renderLabel) : null,
React__default.createElement(
"div",
{ className: "nut-cell-value nut-form-item-body" },
React__default.createElement("div", { className: "nut-form-item-body-slots" }, childNode),
item && item.length > 0 && React__default.createElement("div", { className: "nut-form-item-body-tips", style: { textAlign: errorMessageAlign } }, item[0].message)
)
);
};
this.componentRef = React__default.createRef();
this.state = {
resetCount: 1
};
}
componentDidMount() {
const { store = {}, setInitialValues } = this.context.getInternal(SECRET);
if (this.props.initialValue && this.props.name && !Object.keys(store).includes(this.props.name)) {
setInitialValues(Object.assign(Object.assign({}, store), { [this.props.name]: this.props.initialValue }), true);
}
const { registerField, registerUpdate } = this.context.getInternal(SECRET);
this.cancelRegister = registerField(this);
this.eventOff = registerUpdate(this, this.props.shouldUpdate);
}
componentWillUnmount() {
if (this.cancelRegister) {
this.cancelRegister();
}
if (this.eventOff) {
this.eventOff();
}
}
render() {
const { children } = this.props;
const child = Array.isArray(children) ? children[0] : children;
let returnChildNode;
if (!this.props.shouldUpdate) {
returnChildNode = React__default.cloneElement(child, this.getControlled(child));
} else {
returnChildNode = child(this.context);
}
return React__default.createElement(React__default.Fragment, { key: this.state.resetCount }, this.props.noStyle ? returnChildNode : this.renderLayout(returnChildNode));
}
}
FormItem.defaultProps = defaultProps;
FormItem.contextType = Context;
export {
Context as C,
FormItem as F,
SECRET as S,
useForm as u
};