import DownOutlined from '@ant-design/icons/DownOutlined'; import UpOutlined from '@ant-design/icons/UpOutlined'; import classNames from 'classnames'; import type { InputNumberProps as RcInputNumberProps, ValueType } from 'rc-input-number'; import RcInputNumber from 'rc-input-number'; import * as React from 'react'; import type { InputStatus } from '../_util/statusUtils'; import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils'; import ConfigProvider, { ConfigContext } from '../config-provider'; import DisabledContext from '../config-provider/DisabledContext'; import type { SizeType } from '../config-provider/SizeContext'; import useSize from '../config-provider/hooks/useSize'; import { FormItemInputContext, NoFormStyle } from '../form/context'; import { NoCompactStyle, useCompactItemContext } from '../space/Compact'; import useStyle from './style'; import useCSSVarCls from '../config-provider/hooks/useCSSVarCls'; import type { InputVariant } from '../input/Input'; import { devUseWarning } from '../_util/warning'; import useVariant from '../_util/hooks/useVariants'; import { InputVariants } from '../input/Input'; export interface InputNumberProps extends Omit, 'prefix' | 'size' | 'controls'> { prefixCls?: string; rootClassName?: string; addonBefore?: React.ReactNode; addonAfter?: React.ReactNode; prefix?: React.ReactNode; size?: SizeType; disabled?: boolean; /** @deprecated Use `variant` instead. */ bordered?: boolean; status?: InputStatus; controls?: boolean | { upIcon?: React.ReactNode; downIcon?: React.ReactNode }; /** @default "outlined" */ variant?: InputVariant; } const InputNumber = React.forwardRef((props, ref) => { if (process.env.NODE_ENV !== 'production') { const { deprecated } = devUseWarning('InputNumber'); deprecated(!('bordered' in props), 'bordered', 'variant'); } const { getPrefixCls, direction } = React.useContext(ConfigContext); const inputRef = React.useRef(null); React.useImperativeHandle(ref, () => inputRef.current!); const { className, rootClassName, size: customizeSize, disabled: customDisabled, prefixCls: customizePrefixCls, addonBefore, addonAfter, prefix, bordered, readOnly, status: customStatus, controls, variant: customVariant, ...others } = props; const prefixCls = getPrefixCls('input-number', customizePrefixCls); // Style const rootCls = useCSSVarCls(prefixCls); const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction); let upIcon = ; let downIcon = ; const controlsTemp = typeof controls === 'boolean' ? controls : undefined; if (typeof controls === 'object') { upIcon = typeof controls.upIcon === 'undefined' ? ( upIcon ) : ( {controls.upIcon} ); downIcon = typeof controls.downIcon === 'undefined' ? ( downIcon ) : ( {controls.downIcon} ); } const { hasFeedback, status: contextStatus, isFormItemInput, feedbackIcon, } = React.useContext(FormItemInputContext); const mergedStatus = getMergedStatus(contextStatus, customStatus); const mergedSize = useSize((ctx) => customizeSize ?? compactSize ?? ctx); // ===================== Disabled ===================== const disabled = React.useContext(DisabledContext); const mergedDisabled = customDisabled ?? disabled; const [variant, enableVariantCls] = useVariant(customVariant, bordered, InputVariants); const inputNumberClass = classNames( { [`${prefixCls}-lg`]: mergedSize === 'large', [`${prefixCls}-sm`]: mergedSize === 'small', [`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-${variant}`]: enableVariantCls, [`${prefixCls}-in-form-item`]: isFormItemInput, }, getStatusClassNames(prefixCls, mergedStatus), hashId, ); const wrapperClassName = `${prefixCls}-group`; // eslint-disable-next-line react/jsx-no-useless-fragment const suffixNode = hasFeedback && <>{feedbackIcon}; const element = ( {addonAfter} ) } addonBefore={ addonBefore && ( {addonBefore} ) } classNames={{ input: inputNumberClass, }} classes={{ affixWrapper: classNames( getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback), { [`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small', [`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large', [`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl', [`${prefixCls}-affix-wrapper-${variant}`]: enableVariantCls, }, hashId, ), wrapper: classNames( { [`${wrapperClassName}-rtl`]: direction === 'rtl', [`${prefixCls}-wrapper-disabled`]: mergedDisabled, }, hashId, ), group: classNames( { [`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small', [`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large', [`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl', }, getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback), hashId, ), }} {...others} /> ); return wrapCSSVar(element); }); const TypedInputNumber = InputNumber as unknown as (( props: React.PropsWithChildren> & { ref?: React.Ref; }, ) => React.ReactElement) & { displayName?: string; _InternalPanelDoNotUseOrYouWillBeFired: typeof PureInputNumber; }; /** @private Internal Component. Do not use in your production. */ const PureInputNumber: React.FC = (props) => ( ); if (process.env.NODE_ENV !== 'production') { TypedInputNumber.displayName = 'InputNumber'; } TypedInputNumber._InternalPanelDoNotUseOrYouWillBeFired = PureInputNumber; export default TypedInputNumber;