import type { ExtractPropTypes, HTMLAttributes, App } from 'vue'; import { watch, defineComponent, shallowRef, computed } from 'vue'; import classNames from '../_util/classNames'; import UpOutlined from '@ant-design/icons-vue/UpOutlined'; import DownOutlined from '@ant-design/icons-vue/DownOutlined'; import VcInputNumber, { inputNumberProps as baseInputNumberProps } from './src/InputNumber'; import type { SizeType } from '../config-provider'; import { FormItemInputContext, NoFormStatus, useInjectFormItemContext, } from '../form/FormItemContext'; import useConfigInject from '../config-provider/hooks/useConfigInject'; import { cloneElement } from '../_util/vnode'; import omit from '../_util/omit'; import PropTypes from '../_util/vue-types'; import isValidValue from '../_util/isValidValue'; import type { InputStatus } from '../_util/statusUtils'; import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils'; import { booleanType, stringType } from '../_util/type'; // CSSINJS import useStyle from './style'; import { NoCompactStyle, useCompactItemContext } from '../space/Compact'; import { useInjectDisabled } from '../config-provider/DisabledContext'; import type { CustomSlotsType } from '../_util/type'; const baseProps = baseInputNumberProps(); export const inputNumberProps = () => ({ ...baseProps, size: stringType(), bordered: booleanType(true), placeholder: String, name: String, id: String, type: String, addonBefore: PropTypes.any, addonAfter: PropTypes.any, prefix: PropTypes.any, 'onUpdate:value': baseProps.onChange, valueModifiers: Object, status: stringType(), }); export type InputNumberProps = Partial>>; const InputNumber = defineComponent({ compatConfig: { MODE: 3 }, name: 'AInputNumber', inheritAttrs: false, props: inputNumberProps(), // emits: ['focus', 'blur', 'change', 'input', 'update:value'], slots: Object as CustomSlotsType<{ addonBefore?: any; addonAfter?: any; prefix?: any; default?: any; upIcon?: any; downIcon?: any; }>, setup(props, { emit, expose, attrs, slots }) { const formItemContext = useInjectFormItemContext(); const formItemInputContext = FormItemInputContext.useInject(); const mergedStatus = computed(() => getMergedStatus(formItemInputContext.status, props.status)); const { prefixCls, size, direction, disabled } = useConfigInject('input-number', props); const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction); // Style const [wrapSSR, hashId] = useStyle(prefixCls); const mergedSize = computed(() => compactSize.value || size.value); const disabledContext = useInjectDisabled(); const mergedDisabled = computed(() => disabled.value ?? disabledContext.value); const mergedValue = shallowRef(props.value === undefined ? props.defaultValue : props.value); const focused = shallowRef(false); watch( () => props.value, () => { mergedValue.value = props.value; }, ); const inputNumberRef = shallowRef(null); const focus = () => { inputNumberRef.value?.focus(); }; const blur = () => { inputNumberRef.value?.blur(); }; expose({ focus, blur, }); const handleChange = (val: number) => { if (props.value === undefined) { mergedValue.value = val; } emit('update:value', val); emit('change', val); formItemContext.onFieldChange(); }; const handleBlur = (e: FocusEvent) => { focused.value = false; emit('blur', e); formItemContext.onFieldBlur(); }; const handleFocus = (e: FocusEvent) => { focused.value = true; emit('focus', e); }; return () => { const { hasFeedback, isFormItemInput, feedbackIcon } = formItemInputContext; const id = props.id ?? formItemContext.id.value; const { class: className, bordered, readonly, style, addonBefore = slots.addonBefore?.(), addonAfter = slots.addonAfter?.(), prefix = slots.prefix?.(), valueModifiers = {}, ...others } = { ...attrs, ...props, id } as InputNumberProps & HTMLAttributes; const preCls = prefixCls.value; const inputNumberClass = classNames( { [`${preCls}-lg`]: mergedSize.value === 'large', [`${preCls}-sm`]: mergedSize.value === 'small', [`${preCls}-rtl`]: direction.value === 'rtl', [`${preCls}-readonly`]: readonly, [`${preCls}-borderless`]: !bordered, [`${preCls}-in-form-item`]: isFormItemInput, }, getStatusClassNames(preCls, mergedStatus.value), className, compactItemClassnames.value, hashId.value, ); let element = ( {slots.upIcon()} : () => , downHandler: slots.downIcon ? () => {slots.downIcon()} : () => , }} /> ); const hasAddon = isValidValue(addonBefore) || isValidValue(addonAfter); const hasPrefix = isValidValue(prefix); if (hasPrefix || hasFeedback) { const affixWrapperCls = classNames( `${preCls}-affix-wrapper`, getStatusClassNames(`${preCls}-affix-wrapper`, mergedStatus.value, hasFeedback), { [`${preCls}-affix-wrapper-focused`]: focused.value, [`${preCls}-affix-wrapper-disabled`]: mergedDisabled.value, [`${preCls}-affix-wrapper-sm`]: mergedSize.value === 'small', [`${preCls}-affix-wrapper-lg`]: mergedSize.value === 'large', [`${preCls}-affix-wrapper-rtl`]: direction.value === 'rtl', [`${preCls}-affix-wrapper-readonly`]: readonly, [`${preCls}-affix-wrapper-borderless`]: !bordered, // className will go to addon wrapper [`${className}`]: !hasAddon && className, }, hashId.value, ); element = (
inputNumberRef.value!.focus()} > {hasPrefix && {prefix}} {element} {hasFeedback && {feedbackIcon}}
); } if (hasAddon) { const wrapperClassName = `${preCls}-group`; const addonClassName = `${wrapperClassName}-addon`; const addonBeforeNode = addonBefore ? (
{addonBefore}
) : null; const addonAfterNode = addonAfter ?
{addonAfter}
: null; const mergedWrapperClassName = classNames( `${preCls}-wrapper`, wrapperClassName, { [`${wrapperClassName}-rtl`]: direction.value === 'rtl', }, hashId.value, ); const mergedGroupClassName = classNames( `${preCls}-group-wrapper`, { [`${preCls}-group-wrapper-sm`]: mergedSize.value === 'small', [`${preCls}-group-wrapper-lg`]: mergedSize.value === 'large', [`${preCls}-group-wrapper-rtl`]: direction.value === 'rtl', }, getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus.value, hasFeedback), className, hashId.value, ); element = (
{addonBeforeNode && ( {addonBeforeNode} )} {element} {addonAfterNode && ( {addonAfterNode} )}
); } return wrapSSR(cloneElement(element, { style })); }; }, }); export default Object.assign(InputNumber, { install: (app: App) => { app.component(InputNumber.name, InputNumber); return app; }, });