@sparklink-pro/apant
Version:
Apollo & Antd tools
274 lines • 15.6 kB
JavaScript
import { __awaiter, __rest } from "tslib";
import { createElement as _createElement } from "react";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import React, { forwardRef, useContext, useImperativeHandle, useMemo, useState } from 'react';
import { Form as FormAnt } from 'antd';
import { filter, find, isArray, isEqual, isFunction, some } from 'lodash-es';
import { isFormContentType, } from '../definitions';
import { normalizeFieldName, mergeNamePath, getFormHelpers } from '../helpers';
import { resolveWidget, useFields } from '../hooks/Form/useFields';
import { useFilterValues } from '../hooks/Form/useFilterValues';
import { useInitialValues } from '../hooks/Form/useInitialValues';
import { ContextWrapperQueries, ContextWrapperDefault } from './AsyncWrappers';
import { FormError } from './FormError';
export const FieldsContext = React.createContext({});
export const FormDisabledContext = React.createContext(false);
const useFieldFromContext = (name) => {
const { fields, isList } = useContext(FieldsContext);
if (!name) {
return undefined;
}
const fieldName = normalizeFieldName(isArray(name) && isList ? name.slice(1) : name);
return find(fields, (f) => isEqual(f.item.name, fieldName));
};
const useFieldListPathFromContext = () => {
const { isList, listPath } = useContext(FieldsContext);
return isList ? listPath : undefined;
};
const useFieldsFromContext = (names, group) => {
const { fields } = useContext(FieldsContext);
if (names) {
const fieldNames = names.map(normalizeFieldName);
return filter(fields, (field) => {
if (!field.item.name) {
return false;
}
return some(fieldNames, (name) => isEqual(name, field.item.name));
});
}
return filter(fields, (field) => field.group === group);
};
function FormContentItemDynamic({ content }) {
const { context } = useContext(FieldsContext);
return (_jsx(FormAnt.Item, { noStyle: true, shouldUpdate: () => true, children: () => content(context) }));
}
function FormContentItem({ content }) {
if (isFunction(content)) {
return _jsx(FormContentItemDynamic, { content: content });
}
return content;
}
export function FormItem(_a) {
var { name, widgetProps = {} } = _a, props = __rest(_a, ["name", "widgetProps"]);
const normalizedName = name ? normalizeFieldName(name) : undefined;
const { context } = useContext(FieldsContext);
const field = useFieldFromContext(normalizedName);
const parentListPath = useFieldListPathFromContext();
const isDisabledForm = useContext(FormDisabledContext);
const path = mergeNamePath(parentListPath, normalizedName);
const callbackArgs = useMemo(() => (field ? { context, field, path } : undefined), [field, context, path]);
const shouldUpdate = useMemo(() => {
if (!field || !field.shouldUpdate) {
return true;
}
if (typeof field.shouldUpdate !== 'function') {
return field.shouldUpdate || true;
}
return (prev, next) => {
if (typeof field.shouldUpdate !== 'function') {
return field.shouldUpdate === undefined ? true : field.shouldUpdate;
}
return field.shouldUpdate(normalizedName, prev, next);
};
}, [field === null || field === void 0 ? void 0 : field.shouldUpdate, normalizedName]);
if (field && field.list) {
return (_jsxs(FormError, { children: ["Form list ", _jsx("strong", { children: JSON.stringify(normalizedName) }), " is a list but has been called with ", '<FormItem />'] }));
}
if (!field) {
if (normalizedName) {
return (_jsxs(FormError, { children: ["Field ", _jsx("strong", { children: JSON.stringify(normalizedName) }), " not found in form."] }));
}
return _jsx(FormAnt.Item, Object.assign({}, props));
}
if (field.item.children || props.children || !field.widget) {
return _jsx(FormAnt.Item, Object.assign({}, field.item, { name: normalizedName }, props));
}
// Boolean version of show
if (field.show === false) {
return null;
}
const isDynamic = isFunction(field.show) || isFunction(field.item.rules) || isFunction(field.disabled) || isFunction(field.widget);
/**
* A field is dynamic when we need to dynamically render it again if it has a show callback or dynamics rules
*/
if (!isDynamic || !callbackArgs) {
const _b = field.widget, { widget: Widget } = _b, wProps = __rest(_b, ["widget"]);
if (field.disabled === true) {
wProps.disabled = true;
}
return (_jsx(FormAnt.Item, Object.assign({}, field.item, { name: normalizedName }, props, { children: _jsx(Widget, Object.assign({}, wProps, widgetProps)) })));
}
return (_jsx(FormAnt.Item, { noStyle: true, shouldUpdate: shouldUpdate, children: (form) => {
if (isFunction(field.show) && !field.show(callbackArgs)) {
return null;
}
if (!field.widget) {
return null;
}
const rules = isFunction(field.item.rules) ? field.item.rules(callbackArgs) : field.item.rules;
const isDisabled = field.disabled === true || isDisabledForm || (isFunction(field.disabled) && field.disabled(callbackArgs));
let _a = isFunction(field.widget) ? field.widget(callbackArgs) : field.widget, { widget: Widget } = _a, wProps = __rest(_a, ["widget"]);
if (typeof Widget === 'string') {
const _b = resolveWidget(Widget), { widget: W, defaultValue } = _b, wfProps = __rest(_b, ["widget", "defaultValue"]);
Widget = W;
wProps = Object.assign(Object.assign({}, wProps), wfProps);
if (defaultValue && props.initialValue === undefined) {
props.initialValue = defaultValue;
}
}
return (_jsx(FormAnt.Item, Object.assign({}, field.item, { name: normalizedName, rules: rules }, props, { children: _jsx(Widget, Object.assign({}, wProps, { disabled: isDisabled }, widgetProps)) })));
} }));
}
export function FormList(_a) {
var _b, _c;
var { name, children } = _a, props = __rest(_a, ["name", "children"]);
const normalizedName = normalizeFieldName(name);
const { context } = useContext(FieldsContext);
const field = useFieldFromContext(name);
const parentListPath = useFieldListPathFromContext();
const shouldUpdate = useMemo(() => {
if (!field || !field.shouldUpdate) {
return true;
}
if (typeof field.shouldUpdate !== 'function') {
return field.shouldUpdate || true;
}
return (prev, next) => {
if (typeof field.shouldUpdate !== 'function') {
return field.shouldUpdate === undefined ? true : field.shouldUpdate;
}
return field.shouldUpdate(normalizedName, prev, next);
};
}, [field === null || field === void 0 ? void 0 : field.shouldUpdate, normalizedName]);
if (field && !field.list) {
return (_jsxs(FormError, { children: ["Form item ", _jsx("strong", { children: JSON.stringify(name) }), " is not a list but has been called with ", '<FormList />'] }));
}
const childrenProp = children || ((_b = field === null || field === void 0 ? void 0 : field.item) === null || _b === void 0 ? void 0 : _b.children);
if (!childrenProp) {
return (_jsxs(FormError, { children: ["Form list ", _jsx("strong", { children: JSON.stringify(name) }), " doesn't have any children"] }));
}
const fields = (field === null || field === void 0 ? void 0 : field.fields) || [];
const hasFields = fields.length > 0;
const listPath = mergeNamePath(parentListPath, normalizedName);
const listProps = Object.assign(Object.assign(Object.assign({}, (field ? field.item : {})), props), { name: normalizedName });
const fieldsContext = useMemo(() => ({ context, fields, isList: true, listPath }), [fields, context]);
/** Initial values used by default when adding new element in the list, don't bind to global context object */
const initialValuesDefault = useInitialValues({ fields, context: Object.assign(Object.assign({}, context), { object: undefined }) });
const callbackArgs = field ? { context, field, path: listPath } : undefined;
const isDynamic = isFunction(field === null || field === void 0 ? void 0 : field.show) || isFunction((_c = field === null || field === void 0 ? void 0 : field.item) === null || _c === void 0 ? void 0 : _c.rules);
/** Rewrite the add operation so it uses computed initialValues if no value are provided */
const newChildrenProps = (listFields, _a, meta) => {
var { add } = _a, operations = __rest(_a, ["add"]);
return childrenProp(listFields, Object.assign(Object.assign({}, operations), { add: (values, idx) => (values !== undefined ? add(values, idx) : add(hasFields ? initialValuesDefault : null, idx)) }), meta);
};
if (!field || !isDynamic || !callbackArgs) {
if ((field === null || field === void 0 ? void 0 : field.show) === false) {
return null;
}
return (_jsx(FieldsContext.Provider, { value: fieldsContext, children: _jsx(FormAnt.List, Object.assign({}, listProps, { children: newChildrenProps })) }));
}
return (_jsx(FormAnt.Item, { noStyle: true, shouldUpdate: shouldUpdate, children: () => {
if (field.show === false || (isFunction(field.show) && !field.show(callbackArgs))) {
return null;
}
const rules = (isFunction(field === null || field === void 0 ? void 0 : field.item.rules) ? field.item.rules(callbackArgs) : field.item.rules || []).filter((rule) => {
if (!rule.validator) {
console.warn(`Rules on Form List at ${listPath} only accept customs rules. Skipping rule: `, rule);
return false;
}
return true;
});
return (_jsx(FieldsContext.Provider, { value: fieldsContext, children: _jsx(FormAnt.List, Object.assign({}, listProps, { rules: rules, children: newChildrenProps })) }));
} }));
}
export function FormItems({ names, group }) {
const fields = useFieldsFromContext(names, group);
return (_jsx(_Fragment, { children: fields.map((field, idx) => {
const key = `field_${idx}`;
if (field.list) {
return _jsx(FormList, { name: field.item.name }, key);
}
const props = field.item.name ? {} : field.item;
return _jsx(FormItem, Object.assign({ name: field.item.name }, props), key);
}) }));
}
const FormInner = forwardRef((_a, ref) => {
var { debug, config, context: formContext, children, header, footer, onFinish, onValues, contextExtraProps, formRef, onSubmitState } = _a, formProps = __rest(_a, ["debug", "config", "context", "children", "header", "footer", "onFinish", "onValues", "contextExtraProps", "formRef", "onSubmitState"]);
const [defaultForm] = FormAnt.useForm();
const form = formProps.form || defaultForm;
const [isSubmitting, setIsSubmitting] = useState(false);
const context = Object.assign(Object.assign(Object.assign({ form }, formContext), getFormHelpers(form)), { extra: contextExtraProps });
const fields = useFields({
fields: (isFunction(config.fields) ? config.fields(context) : config.fields) || [],
mappers: (isFunction(config.mappers) ? config.mappers(context) : config.mappers) || [],
context,
});
const realFields = fields.filter((f) => !isFormContentType(f));
const additionalProps = (isFunction(config.props) ? config.props(context) : config.props) || {};
const fieldsContext = useMemo(() => ({ fields: realFields, context }), [fields]);
const initialValues = useInitialValues({ fields: realFields, context });
if (debug) {
console.log('[APANT] Form debug');
console.log('[APANT] Fields', fields);
console.log('[APANT] Fields context', fieldsContext);
console.log('[APANT] Initial values', initialValues);
}
const filterValues = useFilterValues({ fields: realFields, context });
useImperativeHandle(ref, () => ({
filterValues,
}), [form]);
const onFinishFiltered = onFinish
? (values) => __awaiter(void 0, void 0, void 0, function* () {
setIsSubmitting(true);
onSubmitState === null || onSubmitState === void 0 ? void 0 : onSubmitState(true);
if (debug) {
console.log('[APANT] Submitted values', values);
}
let res;
try {
let filteredValues = yield filterValues(values);
if (debug) {
console.log('[APANT] Submitted filtered values', values);
}
if (onValues) {
filteredValues = yield onValues(values, context);
}
res = yield onFinish(filteredValues);
}
catch (e) {
console.error('[APANT] Error while submitting form', e);
throw e;
}
finally {
setIsSubmitting(false);
onSubmitState === null || onSubmitState === void 0 ? void 0 : onSubmitState(false);
return res;
}
})
: undefined;
return (_jsx(FormAnt, Object.assign({ initialValues: initialValues }, additionalProps, formProps, { onFinish: onFinishFiltered, form: form, ref: formRef, children: _jsx(FormDisabledContext.Provider, { value: !!formProps.disabled, children: _jsxs(FieldsContext.Provider, { value: fieldsContext, children: [header, isFunction(children) ? children({ fields: realFields, form, isSubmitting }) : children, !children &&
fields
.filter((f) => isFormContentType(f) || f.auto)
.map((field, idx) => {
if (isFormContentType(field)) {
const key = `content_${idx}`;
return _createElement(FormContentItem, Object.assign({}, field, { key: key }));
}
const key = `field_${idx}`;
if (field.list) {
return _jsx(FormList, { name: field.item.name }, key);
}
const props = field.item.name ? {} : field.item;
return _jsx(FormItem, Object.assign({ name: field.item.name }, props), key);
}), footer] }) }) })));
});
/**
* Render a antd form wrapper with the list of fields.
*/
export const Form = forwardRef((props, ref) => {
const { config: { context }, } = props;
const ContextWrapperCustom = (context === null || context === void 0 ? void 0 : context.wrapper) || ContextWrapperDefault;
return (_jsx(ContextWrapperCustom, { children: (customExtraProps) => (_jsx(ContextWrapperQueries, { queries: (context === null || context === void 0 ? void 0 : context.queries) || {}, children: (queryExtraProps) => (_jsx(FormInner, Object.assign({}, props, { contextExtraProps: Object.assign(Object.assign({}, customExtraProps), queryExtraProps), ref: ref }))) })) }));
});
export default Form;
//# sourceMappingURL=Form.js.map