194 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Vue
		
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Vue
		
	
	
import type { PropType, ExtractPropTypes, HTMLAttributes, App } from 'vue';
 | 
						|
import { watch, defineComponent, nextTick, onMounted, ref } 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 { useInjectFormItemContext } from '../form/FormItemContext';
 | 
						|
import useConfigInject from '../_util/hooks/useConfigInject';
 | 
						|
import { cloneElement } from '../_util/vnode';
 | 
						|
import omit from '../_util/omit';
 | 
						|
import PropTypes from '../_util/vue-types';
 | 
						|
import isValidValue from '../_util/isValidValue';
 | 
						|
export const inputNumberProps = {
 | 
						|
  ...baseInputNumberProps,
 | 
						|
  size: { type: String as PropType<SizeType> },
 | 
						|
  bordered: { type: Boolean, default: true },
 | 
						|
  placeholder: String,
 | 
						|
  name: String,
 | 
						|
  id: String,
 | 
						|
  type: String,
 | 
						|
  addonBefore: PropTypes.any,
 | 
						|
  addonAfter: PropTypes.any,
 | 
						|
  prefix: PropTypes.any,
 | 
						|
  'update:value': baseInputNumberProps.onChange,
 | 
						|
};
 | 
						|
 | 
						|
export type InputNumberProps = Partial<ExtractPropTypes<typeof inputNumberProps>>;
 | 
						|
 | 
						|
const InputNumber = defineComponent({
 | 
						|
  name: 'AInputNumber',
 | 
						|
  inheritAttrs: false,
 | 
						|
  props: inputNumberProps,
 | 
						|
  emits: ['focus', 'blur', 'change', 'input', 'update:value'],
 | 
						|
  slots: ['addonBefore', 'addonAfter', 'prefix'],
 | 
						|
  setup(props, { emit, expose, attrs, slots }) {
 | 
						|
    const formItemContext = useInjectFormItemContext();
 | 
						|
    const { prefixCls, size, direction } = useConfigInject('input-number', props);
 | 
						|
    const mergedValue = ref(props.value === undefined ? props.defaultValue : props.value);
 | 
						|
    const focused = ref(false);
 | 
						|
    watch(
 | 
						|
      () => props.value,
 | 
						|
      () => {
 | 
						|
        mergedValue.value = props.value;
 | 
						|
      },
 | 
						|
    );
 | 
						|
    const inputNumberRef = ref(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 = () => {
 | 
						|
      focused.value = false;
 | 
						|
      emit('blur');
 | 
						|
      formItemContext.onFieldBlur();
 | 
						|
    };
 | 
						|
    const handleFocus = () => {
 | 
						|
      focused.value = true;
 | 
						|
      emit('focus');
 | 
						|
    };
 | 
						|
    onMounted(() => {
 | 
						|
      nextTick(() => {
 | 
						|
        if (process.env.NODE_ENV === 'test') {
 | 
						|
          if (props.autofocus && !props.disabled) {
 | 
						|
            focus();
 | 
						|
          }
 | 
						|
        }
 | 
						|
      });
 | 
						|
    });
 | 
						|
    return () => {
 | 
						|
      const {
 | 
						|
        class: className,
 | 
						|
        bordered,
 | 
						|
        readonly,
 | 
						|
        style,
 | 
						|
        addonBefore = slots.addonBefore?.(),
 | 
						|
        addonAfter = slots.addonAfter?.(),
 | 
						|
        prefix = slots.prefix?.(),
 | 
						|
        ...others
 | 
						|
      } = { ...(attrs as HTMLAttributes), ...props };
 | 
						|
 | 
						|
      const preCls = prefixCls.value;
 | 
						|
 | 
						|
      const mergeSize = size.value;
 | 
						|
      const inputNumberClass = classNames(
 | 
						|
        {
 | 
						|
          [`${preCls}-lg`]: mergeSize === 'large',
 | 
						|
          [`${preCls}-sm`]: mergeSize === 'small',
 | 
						|
          [`${preCls}-rtl`]: direction.value === 'rtl',
 | 
						|
          [`${preCls}-readonly`]: readonly,
 | 
						|
          [`${preCls}-borderless`]: !bordered,
 | 
						|
        },
 | 
						|
        className,
 | 
						|
      );
 | 
						|
 | 
						|
      let element = (
 | 
						|
        <VcInputNumber
 | 
						|
          {...omit(others, ['size', 'defaultValue'])}
 | 
						|
          ref={inputNumberRef}
 | 
						|
          value={mergedValue.value}
 | 
						|
          class={inputNumberClass}
 | 
						|
          prefixCls={preCls}
 | 
						|
          readonly={readonly}
 | 
						|
          onChange={handleChange}
 | 
						|
          onBlur={handleBlur}
 | 
						|
          onFocus={handleFocus}
 | 
						|
          v-slots={{
 | 
						|
            upHandler: () => <UpOutlined class={`${preCls}-handler-up-inner`} />,
 | 
						|
            downHandler: () => <DownOutlined class={`${preCls}-handler-down-inner`} />,
 | 
						|
          }}
 | 
						|
        />
 | 
						|
      );
 | 
						|
      const hasAddon = isValidValue(addonBefore) || isValidValue(addonAfter);
 | 
						|
      if (isValidValue(prefix)) {
 | 
						|
        const affixWrapperCls = classNames(`${preCls}-affix-wrapper`, {
 | 
						|
          [`${preCls}-affix-wrapper-focused`]: focused.value,
 | 
						|
          [`${preCls}-affix-wrapper-disabled`]: props.disabled,
 | 
						|
          [`${preCls}-affix-wrapper-sm`]: size.value === 'small',
 | 
						|
          [`${preCls}-affix-wrapper-lg`]: size.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,
 | 
						|
        });
 | 
						|
        element = (
 | 
						|
          <div
 | 
						|
            class={affixWrapperCls}
 | 
						|
            style={style}
 | 
						|
            onMouseup={() => inputNumberRef.value!.focus()}
 | 
						|
          >
 | 
						|
            <span class={`${preCls}-prefix`}>{prefix}</span>
 | 
						|
            {element}
 | 
						|
          </div>
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      if (hasAddon) {
 | 
						|
        const wrapperClassName = `${preCls}-group`;
 | 
						|
        const addonClassName = `${wrapperClassName}-addon`;
 | 
						|
        const addonBeforeNode = addonBefore ? (
 | 
						|
          <div class={addonClassName}>{addonBefore}</div>
 | 
						|
        ) : null;
 | 
						|
        const addonAfterNode = addonAfter ? <div class={addonClassName}>{addonAfter}</div> : null;
 | 
						|
 | 
						|
        const mergedWrapperClassName = classNames(`${preCls}-wrapper`, wrapperClassName, {
 | 
						|
          [`${wrapperClassName}-rtl`]: direction.value === 'rtl',
 | 
						|
        });
 | 
						|
 | 
						|
        const mergedGroupClassName = classNames(
 | 
						|
          `${preCls}-group-wrapper`,
 | 
						|
          {
 | 
						|
            [`${preCls}-group-wrapper-sm`]: mergeSize === 'small',
 | 
						|
            [`${preCls}-group-wrapper-lg`]: mergeSize === 'large',
 | 
						|
            [`${preCls}-group-wrapper-rtl`]: direction.value === 'rtl',
 | 
						|
          },
 | 
						|
          className,
 | 
						|
        );
 | 
						|
        element = (
 | 
						|
          <div class={mergedGroupClassName} style={style}>
 | 
						|
            <div class={mergedWrapperClassName}>
 | 
						|
              {addonBeforeNode}
 | 
						|
              {element}
 | 
						|
              {addonAfterNode}
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        );
 | 
						|
      }
 | 
						|
      return cloneElement(element, { style });
 | 
						|
    };
 | 
						|
  },
 | 
						|
});
 | 
						|
 | 
						|
export default Object.assign(InputNumber, {
 | 
						|
  install: (app: App) => {
 | 
						|
    app.component(InputNumber.name, InputNumber);
 | 
						|
    return app;
 | 
						|
  },
 | 
						|
});
 |