import DownOutlined from '@ant-design/icons/DownOutlined'; import UpOutlined from '@ant-design/icons/UpOutlined'; import classNames from 'classnames'; import type { InputNumberProps as RcInputNumberProps } from 'rc-input-number'; import RcInputNumber from 'rc-input-number'; import type { ValueType } from 'rc-input-number/lib/utils/MiniDecimal'; import * as React from 'react'; import { useContext } from 'react'; import ConfigProvider, { ConfigContext } from '../config-provider'; import DisabledContext from '../config-provider/DisabledContext'; import type { SizeType } from '../config-provider/SizeContext'; import SizeContext from '../config-provider/SizeContext'; import { FormItemInputContext, NoFormStyle } from '../form/context'; import { NoCompactStyle, useCompactItemContext } from '../space/Compact'; import { cloneElement } from '../_util/reactNode'; import type { InputStatus } from '../_util/statusUtils'; import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils'; import useStyle from './style'; export interface InputNumberProps extends Omit, 'prefix' | 'size' | 'controls'> { prefixCls?: string; addonBefore?: React.ReactNode; addonAfter?: React.ReactNode; prefix?: React.ReactNode; size?: SizeType; disabled?: boolean; bordered?: boolean; status?: InputStatus; controls?: boolean | { upIcon?: React.ReactNode; downIcon?: React.ReactNode }; } const InputNumber = React.forwardRef((props, ref) => { const { getPrefixCls, direction } = React.useContext(ConfigContext); const size = React.useContext(SizeContext); const [focused, setFocus] = React.useState(false); const inputRef = React.useRef(null); React.useImperativeHandle(ref, () => inputRef.current!); const { className, size: customizeSize, disabled: customDisabled, prefixCls: customizePrefixCls, addonBefore, addonAfter, prefix, bordered = true, readOnly, status: customStatus, controls, ...others } = props; const prefixCls = getPrefixCls('input-number', customizePrefixCls); // Style const [wrapSSR, hashId] = useStyle(prefixCls); 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, } = useContext(FormItemInputContext); const mergedStatus = getMergedStatus(contextStatus, customStatus); const mergeSize = compactSize || customizeSize || size; // ===================== Disabled ===================== const disabled = React.useContext(DisabledContext); const mergedDisabled = customDisabled ?? disabled; const inputNumberClass = classNames( { [`${prefixCls}-lg`]: mergeSize === 'large', [`${prefixCls}-sm`]: mergeSize === 'small', [`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-borderless`]: !bordered, [`${prefixCls}-in-form-item`]: isFormItemInput, }, getStatusClassNames(prefixCls, mergedStatus), compactItemClassnames, hashId, className, ); let element = ( ); if (prefix != null || hasFeedback) { const affixWrapperCls = classNames( `${prefixCls}-affix-wrapper`, getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback), { [`${prefixCls}-affix-wrapper-focused`]: focused, [`${prefixCls}-affix-wrapper-disabled`]: props.disabled, [`${prefixCls}-affix-wrapper-sm`]: mergeSize === 'small', [`${prefixCls}-affix-wrapper-lg`]: mergeSize === 'large', [`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl', [`${prefixCls}-affix-wrapper-readonly`]: readOnly, [`${prefixCls}-affix-wrapper-borderless`]: !bordered, // className will go to addon wrapper [`${className}`]: !(addonBefore || addonAfter) && className, }, hashId, ); element = (
inputRef.current!.focus()} > {prefix && {prefix}} {cloneElement(element, { style: null, value: props.value, onFocus: (event: React.FocusEvent) => { setFocus(true); props.onFocus?.(event); }, onBlur: (event: React.FocusEvent) => { setFocus(false); props.onBlur?.(event); }, })} {hasFeedback && {feedbackIcon}}
); } if (addonBefore != null || addonAfter != null) { const wrapperClassName = `${prefixCls}-group`; const addonClassName = `${wrapperClassName}-addon`; const addonBeforeNode = addonBefore ? (
{addonBefore}
) : null; const addonAfterNode = addonAfter ?
{addonAfter}
: null; const mergedWrapperClassName = classNames(`${prefixCls}-wrapper`, wrapperClassName, hashId, { [`${wrapperClassName}-rtl`]: direction === 'rtl', }); const mergedGroupClassName = classNames( `${prefixCls}-group-wrapper`, { [`${prefixCls}-group-wrapper-sm`]: mergeSize === 'small', [`${prefixCls}-group-wrapper-lg`]: mergeSize === 'large', [`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl', }, getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback), hashId, className, ); element = (
{addonBeforeNode && ( {addonBeforeNode} )} {cloneElement(element, { style: null, disabled: mergedDisabled })} {addonAfterNode && ( {addonAfterNode} )}
); } return wrapSSR(element); }); const TypedInputNumber = InputNumber as unknown as (( props: React.PropsWithChildren> & { ref?: React.Ref; }, ) => React.ReactElement) & { displayName?: string; _InternalPanelDoNotUseOrYouWillBeFired: typeof PureInputNumber; }; const PureInputNumber = (props: InputNumberProps) => ( ); TypedInputNumber._InternalPanelDoNotUseOrYouWillBeFired = PureInputNumber; export default TypedInputNumber;