@mijadesign/mjui-react-taro
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
440 lines (439 loc) • 16.4 kB
JavaScript
import { a as __awaiter, _ as __rest } from "./tslib.es6-iWu3F_1J.js";
import React, { useRef, createContext, useContext as useContext$1 } from "react";
import { Text, View } from "@tarojs/components";
import classNames from "classnames";
import { merge, recursive, toArray } from "@nutui/nutui-react-taro";
import { Context as Context$1 } from "@nutui/nutui-react-taro/dist/es/packages/form/context";
import { C as Cell } from "./cell.taro-DwViuoF7.js";
import { C as ComponentDefaults } from "./typings-DV9RBfhj.js";
import Schema from "async-validator";
function isForwardRefComponent(component) {
return component.type && component.type.$$typeof && // eslint-disable-next-line react/display-name
React.forwardRef(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
() => {
}
).$$typeof === component.type.$$typeof;
}
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);
};
};
this.getFieldValue = (name) => {
var _a;
return (_a = this.store) === null || _a === void 0 ? void 0 : _a[name];
};
this.getFieldsValue = (nameList, filterFunc) => {
let names;
if (nameList === true) {
names = this.fieldEntities.map((e) => {
var _a;
return (_a = e === null || e === void 0 ? void 0 : e.props) === null || _a === void 0 ? void 0 : _a.name;
}).filter((n) => n !== void 0);
} else if (Array.isArray(nameList)) {
names = nameList;
} else {
names = Object.keys(this.store);
}
const result = {};
const mountedNameSet = new Set(this.fieldEntities.map((e) => {
var _a;
return (_a = e === null || e === void 0 ? void 0 : e.props) === null || _a === void 0 ? void 0 : _a.name;
}).filter((n) => n !== void 0));
names.forEach((name) => {
const value = this.store[name];
if (filterFunc) {
const meta = { name, value, mounted: mountedNameSet.has(name) };
if (!filterFunc(meta))
return;
}
result[name] = value;
});
return result;
};
this.setInitialValues = (initialValues, init) => {
this.initialValues = initialValues || {};
if (init) {
const nextStore = merge(initialValues, this.store);
this.updateStore(nextStore);
this.notifyWatch();
}
};
this.setFieldsValue = (newStore) => {
const nextStore = recursive(true, this.store, newStore);
this.updateStore(nextStore);
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");
}
});
this.notifyWatch();
};
this.setFieldValue = (name, value) => {
const store = {
[name]: value
};
this.setFieldsValue(store);
this.notifyWatch([name]);
};
this.setCallback = (callback) => {
this.callbacks = Object.assign(Object.assign({}, this.callbacks), callback);
};
this.validateEntities = (entity, errs) => __awaiter(this, void 0, void 0, function* () {
var _a;
const { name, rules = [] } = entity.props;
if (!name) {
console.warn("Form field missing name property");
return;
}
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");
});
this.validateFields = (nameList) => __awaiter(this, void 0, void 0, function* () {
let filterEntities = [];
if (!nameList || nameList.length === 0) {
filterEntities = this.fieldEntities;
} else {
filterEntities = this.fieldEntities.filter(({ props: { name } }) => nameList.includes(name));
}
const errs = [];
yield Promise.all(filterEntities.map((entity) => __awaiter(this, void 0, void 0, function* () {
yield this.validateEntities(entity, errs);
})));
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 = (namePaths) => {
if (namePaths && namePaths.length) {
namePaths.forEach((path) => {
this.errors[path] = null;
this.fieldEntities.forEach((entity) => {
const name = entity.props.name;
if (name === path) {
if (path in this.initialValues) {
this.updateStore({ [path]: this.initialValues[path] });
} else {
delete this.store[path];
}
entity.onStoreChange("reset");
}
});
});
} else {
const nextStore = merge({}, this.initialValues);
this.updateStore(nextStore);
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,
registerWatch: this.registerWatch
};
}
};
this.getForm = () => {
return {
getFieldValue: this.getFieldValue,
getFieldsValue: this.getFieldsValue,
setFieldsValue: this.setFieldsValue,
setFieldValue: this.setFieldValue,
resetFields: this.resetFields,
validateFields: this.validateFields,
submit: this.submit,
errors: this.errors,
getInternal: this.getInternal
};
};
this.watchList = [];
this.registerWatch = (callback) => {
this.watchList.push(callback);
return () => {
this.watchList = this.watchList.filter((fn) => fn !== callback);
};
};
this.notifyWatch = (namePath = []) => {
if (this.watchList.length) {
let allValues;
if (!namePath || namePath.length === 0) {
allValues = this.getFieldsValue(true);
} else {
allValues = this.getFieldsValue(namePath);
}
this.watchList.forEach((callback) => {
callback(allValues, namePath);
});
}
};
this.callbacks = {
onFinish: () => {
},
onFinishFailed: () => {
}
};
}
updateStore(nextStore) {
this.store = nextStore;
}
}
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];
};
const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { required: false, name: "", label: "", rules: [{ required: false, message: "" }], errorMessageAlign: "left", shouldUpdate: false, noStyle: false });
let FormItem$1 = class FormItem extends React.Component {
constructor(props) {
super(props);
this.getControlled = (children) => {
var _a;
const { setFieldsValue, getFieldValue } = this.context.formInstance;
const { dispatch } = this.context.formInstance.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) {
if (process.env.NODE_ENV !== "production") {
console.warn("[NutUI] FormItem:", "请通过 initialValue 设置初始值,而不是 defaultValue");
}
}
const fieldValue = getFieldValue(name);
const controlled = Object.assign(Object.assign({}, children.props), { className: children.props.className, [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 });
} });
const { validateTrigger } = this.props;
const mergedValidateTrigger = validateTrigger || this.context.validateTrigger;
const validateTriggers = toArray(mergedValidateTrigger);
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.formInstance.errors[this.props.name] = [];
this.refresh();
} else {
this.forceUpdate();
}
};
this.renderLayout = (childNode) => {
var _a;
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.formInstance.errors[name] : [];
const { starPosition } = this.context.formInstance;
const renderStar = (required || requiredInRules) && React.createElement(Text, { className: "nut-form-item-label-required required" }, "*");
const renderLabel = React.createElement(
React.Fragment,
null,
React.createElement(
"div",
{ className: "nut-form-item-labeltxt" },
starPosition === "left" ? renderStar : null,
label
),
starPosition === "right" ? renderStar : null
);
return React.createElement(
Cell,
{ className: `${this.getClassNameWithDirection("nut-form-item")} ${className} ${this.context.disabled ? "nut-form-item-disabled" : ""}`, style, align, onClick: (e) => this.props.onClick && this.props.onClick(e, this.componentRef), catchMove: this.context.disabled },
label ? React.createElement(View, { className: `nut-cell-title ${this.getClassNameWithDirection("nut-form-item-label")}` }, renderLabel) : null,
React.createElement(
View,
{ className: `nut-cell-value ${this.getClassNameWithDirection("nut-form-item-body")}`, catchMove: this.context.disabled },
React.createElement(View, { className: "nut-form-item-body-slots" }, childNode),
React.createElement(View, { className: "nut-form-item-body-tips", style: {
textAlign: errorMessageAlign,
display: (item === null || item === void 0 ? void 0 : item.length) ? "initial" : "none"
} }, (_a = item === null || item === void 0 ? void 0 : item[0]) === null || _a === void 0 ? void 0 : _a.message)
)
);
};
this.componentRef = React.createRef();
this.state = {
resetCount: 1
};
}
componentDidMount() {
const { store = {}, setInitialValues } = this.context.formInstance.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.formInstance.getInternal(SECRET);
this.cancelRegister = registerField(this);
this.eventOff = registerUpdate(this, this.props.shouldUpdate);
}
componentWillUnmount() {
if (this.cancelRegister) {
this.cancelRegister();
}
if (this.eventOff) {
this.eventOff();
}
}
getClassNameWithDirection(className) {
if (className && this.context.labelPosition) {
return `${className} ${className}-${this.context.labelPosition}`;
}
return className;
}
render() {
const { children } = this.props;
const child = Array.isArray(children) ? children[0] : children;
let returnChildNode;
if (!this.props.shouldUpdate) {
returnChildNode = React.cloneElement(child, this.getControlled(child));
} else {
returnChildNode = child(this.context.formInstance);
}
return React.createElement(React.Fragment, { key: this.state.resetCount }, this.props.noStyle ? returnChildNode : this.renderLayout(returnChildNode));
}
};
FormItem$1.defaultProps = defaultProps;
FormItem$1.contextType = Context$1;
const Context = createContext({});
const Provider = Context.Provider;
const useContext = () => useContext$1(Context);
const FormItem2 = (props) => {
const { label, labelExtra, divider: middleDivider, noStyle, className, layout = "horizontal" } = props, rest = __rest(props, ["label", "labelExtra", "divider", "noStyle", "className", "layout"]);
const { divider: propsDivider } = useContext();
const divider = middleDivider !== null && middleDivider !== void 0 ? middleDivider : propsDivider;
const renderLabel = labelExtra ? React.createElement(
Text,
{ className: "mj-form-item-label-wrap" },
React.createElement(Text, { className: "mj-form-item-label" }, label),
labelExtra
) : label;
return React.createElement(
React.Fragment,
null,
React.createElement(FormItem$1, Object.assign({ className: classNames("mj-form-item", className, {
[`mj-form-item-${layout}`]: true,
[`mj-form-item-divider`]: !!divider && !noStyle
}), noStyle, label: renderLabel }, rest))
);
};
export {
FormItem2 as F,
Provider as P,
useForm as u
};