2023-09-04 10:03:12 +08:00
|
|
|
import * as React from 'react';
|
|
|
|
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
|
|
|
|
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
|
|
|
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
|
|
|
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import type { Meta } from 'rc-field-form/lib/interface';
|
|
|
|
|
2023-09-05 13:41:06 +08:00
|
|
|
import type { FeedbackIcons, ValidateStatus } from '.';
|
|
|
|
import { FormContext, FormItemInputContext, type FormItemStatusContextProps } from '../context';
|
2023-09-04 10:03:12 +08:00
|
|
|
import { getStatus } from '../util';
|
|
|
|
|
|
|
|
const iconMap = {
|
|
|
|
success: CheckCircleFilled,
|
|
|
|
warning: ExclamationCircleFilled,
|
|
|
|
error: CloseCircleFilled,
|
|
|
|
validating: LoadingOutlined,
|
|
|
|
};
|
|
|
|
|
|
|
|
export interface StatusProviderProps {
|
|
|
|
children?: React.ReactNode;
|
|
|
|
validateStatus?: ValidateStatus;
|
|
|
|
prefixCls: string;
|
|
|
|
meta: Meta;
|
|
|
|
errors: React.ReactNode[];
|
|
|
|
warnings: React.ReactNode[];
|
2023-09-05 13:41:06 +08:00
|
|
|
hasFeedback?: boolean | { icons?: FeedbackIcons };
|
2023-09-04 10:03:12 +08:00
|
|
|
noStyle?: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function StatusProvider({
|
|
|
|
children,
|
|
|
|
errors,
|
|
|
|
warnings,
|
|
|
|
hasFeedback,
|
|
|
|
validateStatus,
|
|
|
|
prefixCls,
|
|
|
|
meta,
|
|
|
|
noStyle,
|
|
|
|
}: StatusProviderProps) {
|
|
|
|
const itemPrefixCls = `${prefixCls}-item`;
|
2023-09-05 13:41:06 +08:00
|
|
|
const { feedbackIcons } = React.useContext(FormContext);
|
2023-09-04 10:03:12 +08:00
|
|
|
|
2023-09-05 13:41:06 +08:00
|
|
|
const mergedValidateStatus = getStatus(
|
|
|
|
errors,
|
|
|
|
warnings,
|
|
|
|
meta,
|
|
|
|
null,
|
|
|
|
!!hasFeedback,
|
|
|
|
validateStatus,
|
|
|
|
);
|
2023-09-04 10:03:12 +08:00
|
|
|
|
|
|
|
const { isFormItemInput: parentIsFormItemInput, status: parentStatus } =
|
|
|
|
React.useContext(FormItemInputContext);
|
|
|
|
|
|
|
|
// ====================== Context =======================
|
|
|
|
const formItemStatusContext = React.useMemo<FormItemStatusContextProps>(() => {
|
|
|
|
let feedbackIcon: React.ReactNode;
|
|
|
|
if (hasFeedback) {
|
2023-09-05 13:41:06 +08:00
|
|
|
const customIcons = (hasFeedback !== true && hasFeedback.icons) || feedbackIcons;
|
|
|
|
const customIconNode =
|
|
|
|
mergedValidateStatus &&
|
|
|
|
customIcons?.({ status: mergedValidateStatus, errors, warnings })?.[mergedValidateStatus];
|
2023-09-04 10:03:12 +08:00
|
|
|
const IconNode = mergedValidateStatus && iconMap[mergedValidateStatus];
|
2023-09-05 13:41:06 +08:00
|
|
|
feedbackIcon =
|
|
|
|
customIconNode !== false && IconNode ? (
|
|
|
|
<span
|
|
|
|
className={classNames(
|
|
|
|
`${itemPrefixCls}-feedback-icon`,
|
|
|
|
`${itemPrefixCls}-feedback-icon-${mergedValidateStatus}`,
|
|
|
|
)}
|
|
|
|
>
|
|
|
|
{customIconNode || <IconNode />}
|
|
|
|
</span>
|
|
|
|
) : null;
|
2023-09-04 10:03:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
let isFormItemInput: boolean | undefined = true;
|
|
|
|
let status: ValidateStatus = mergedValidateStatus || '';
|
|
|
|
|
|
|
|
// No style will follow parent context
|
|
|
|
if (noStyle) {
|
|
|
|
isFormItemInput = parentIsFormItemInput;
|
|
|
|
status = (mergedValidateStatus ?? parentStatus) || '';
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
status,
|
|
|
|
errors,
|
|
|
|
warnings,
|
2023-09-05 13:41:06 +08:00
|
|
|
hasFeedback: !!hasFeedback,
|
2023-09-04 10:03:12 +08:00
|
|
|
feedbackIcon,
|
|
|
|
isFormItemInput,
|
|
|
|
};
|
|
|
|
}, [mergedValidateStatus, hasFeedback, noStyle, parentIsFormItemInput, parentStatus]);
|
|
|
|
|
|
|
|
// ======================= Render =======================
|
|
|
|
return (
|
|
|
|
<FormItemInputContext.Provider value={formItemStatusContext}>
|
|
|
|
{children}
|
|
|
|
</FormItemInputContext.Provider>
|
|
|
|
);
|
|
|
|
}
|