🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

261 lines
6.8 KiB

// base 0.0.1-alpha.7
import type { VNode } from 'vue';
import {
onMounted,
defineComponent,
getCurrentInstance,
nextTick,
shallowRef,
watch,
withDirectives,
} from 'vue';
import classNames from '../_util/classNames';
import type { ChangeEvent, FocusEventHandler } from '../_util/EventInterface';
import omit from '../_util/omit';
import type { InputProps } from './inputProps';
import { inputProps } from './inputProps';
import type { InputFocusOptions } from './utils/commonUtils';
import {
fixControlledValue,
hasAddon,
hasPrefixSuffix,
resolveOnChange,
triggerFocus,
} from './utils/commonUtils';
import antInputDirective from '../_util/antInputDirective';
import BaseInput from './BaseInput';
export default defineComponent({
name: 'VCInput',
inheritAttrs: false,
props: inputProps(),
setup(props, { slots, attrs, expose, emit }) {
const stateValue = shallowRef(props.value === undefined ? props.defaultValue : props.value);
const focused = shallowRef(false);
const inputRef = shallowRef<HTMLInputElement>();
watch(
() => props.value,
() => {
stateValue.value = props.value;
},
);
watch(
() => props.disabled,
() => {
if (props.disabled) {
focused.value = false;
}
},
);
const focus = (option?: InputFocusOptions) => {
if (inputRef.value) {
triggerFocus(inputRef.value, option);
}
};
const blur = () => {
inputRef.value?.blur();
};
const setSelectionRange = (
start: number,
end: number,
direction?: 'forward' | 'backward' | 'none',
) => {
inputRef.value?.setSelectionRange(start, end, direction);
};
const select = () => {
inputRef.value?.select();
};
expose({
focus,
blur,
input: inputRef,
stateValue,
setSelectionRange,
select,
});
const triggerChange = (e: Event) => {
emit('change', e);
};
const instance = getCurrentInstance();
const setValue = (value: string | number, callback?: Function) => {
if (stateValue.value === value) {
return;
}
if (props.value === undefined) {
stateValue.value = value;
} else {
nextTick(() => {
if (inputRef.value.value !== stateValue.value) {
instance.update();
}
});
}
nextTick(() => {
callback && callback();
});
};
const handleChange = (e: ChangeEvent) => {
const { value, composing } = e.target as any;
// https://github.com/vueComponent/ant-design-vue/issues/2203
if ((((e as any).isComposing || composing) && props.lazy) || stateValue.value === value)
return;
const newVal = e.target.value;
resolveOnChange(inputRef.value, e, triggerChange);
setValue(newVal);
};
const handleKeyDown = (e: KeyboardEvent) => {
if (e.keyCode === 13) {
emit('pressEnter', e);
}
emit('keydown', e);
};
const handleFocus: FocusEventHandler = e => {
focused.value = true;
emit('focus', e);
};
const handleBlur: FocusEventHandler = e => {
focused.value = false;
emit('blur', e);
};
const handleReset = (e: MouseEvent) => {
resolveOnChange(inputRef.value, e, triggerChange);
setValue('', () => {
focus();
});
};
const getInputElement = () => {
const {
addonBefore = slots.addonBefore,
addonAfter = slots.addonAfter,
disabled,
valueModifiers = {},
htmlSize,
autocomplete,
prefixCls,
inputClassName,
prefix = slots.prefix?.(),
suffix = slots.suffix?.(),
allowClear,
type = 'text',
} = props;
const otherProps = omit(props as InputProps & { placeholder: string }, [
'prefixCls',
'onPressEnter',
'addonBefore',
'addonAfter',
'prefix',
'suffix',
'allowClear',
// Input elements must be either controlled or uncontrolled,
// specify either the value prop, or the defaultValue prop, but not both.
'defaultValue',
'size',
'bordered',
'htmlSize',
'lazy',
'showCount',
'valueModifiers',
'showCount',
'affixWrapperClassName',
'groupClassName',
'inputClassName',
'wrapperClassName',
]);
const inputProps = {
...otherProps,
...attrs,
autocomplete,
onChange: handleChange,
onInput: handleChange,
onFocus: handleFocus,
onBlur: handleBlur,
onKeydown: handleKeyDown,
class: classNames(
prefixCls,
{
[`${prefixCls}-disabled`]: disabled,
},
inputClassName,
!hasAddon({ addonAfter, addonBefore }) &&
!hasPrefixSuffix({ prefix, suffix, allowClear }) &&
attrs.class,
),
ref: inputRef,
key: 'ant-input',
size: htmlSize,
type,
};
if (valueModifiers.lazy) {
delete inputProps.onInput;
}
if (!inputProps.autofocus) {
delete inputProps.autofocus;
}
const inputNode = <input {...omit(inputProps, ['size'])} />;
return withDirectives(inputNode as VNode, [[antInputDirective]]);
};
const getSuffix = () => {
const { maxlength, suffix = slots.suffix?.(), showCount, prefixCls } = props;
// Max length value
const hasMaxLength = Number(maxlength) > 0;
if (suffix || showCount) {
const valueLength = [...fixControlledValue(stateValue.value)].length;
const dataCount =
typeof showCount === 'object'
? showCount.formatter({ count: valueLength, maxlength })
: `${valueLength}${hasMaxLength ? ` / ${maxlength}` : ''}`;
return (
<>
{!!showCount && (
<span
class={classNames(`${prefixCls}-show-count-suffix`, {
[`${prefixCls}-show-count-has-suffix`]: !!suffix,
})}
>
{dataCount}
</span>
)}
{suffix}
</>
);
}
return null;
};
onMounted(() => {
if (process.env.NODE_ENV === 'test') {
if (props.autofocus) {
focus();
}
}
});
return () => {
const { prefixCls, disabled, ...rest } = props;
return (
<BaseInput
{...rest}
{...attrs}
prefixCls={prefixCls}
inputElement={getInputElement()}
handleReset={handleReset}
value={fixControlledValue(stateValue.value)}
focused={focused.value}
triggerFocus={focus}
suffix={getSuffix()}
disabled={disabled}
v-slots={slots}
/>
);
};
},
});