feat: update input
parent
e00637f76c
commit
69f7573fe5
|
@ -1 +1 @@
|
|||
Subproject commit c2e13cef53abb73e36ad7e506a9a03f1268fc3e8
|
||||
Subproject commit 6cde2f94ca4a1dbbb260a4436369e2084ff467ed
|
|
@ -73,3 +73,7 @@ renderTabBar({props, on, style, class}, DefaultTabBar) -> {DefaultTabBar, ...pro
|
|||
## card
|
||||
|
||||
tabList[{scopedSlots}] -> tabList[{slots}]
|
||||
|
||||
## rate
|
||||
|
||||
v-model -> v-model:value
|
||||
|
|
|
@ -3,12 +3,12 @@ import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
|
|||
import { getInputClassName } from './Input';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import { getComponentFromProp } from '../_util/props-util';
|
||||
import { getComponent } from '../_util/props-util';
|
||||
|
||||
export function hasPrefixSuffix(instance) {
|
||||
return !!(
|
||||
getComponentFromProp(instance, 'prefix') ||
|
||||
getComponentFromProp(instance, 'suffix') ||
|
||||
getComponent(instance, 'prefix') ||
|
||||
getComponent(instance, 'suffix') ||
|
||||
instance.$props.allowClear
|
||||
);
|
||||
}
|
||||
|
@ -16,6 +16,8 @@ export function hasPrefixSuffix(instance) {
|
|||
const ClearableInputType = ['text', 'input'];
|
||||
|
||||
const ClearableLabeledInput = {
|
||||
name: 'ClearableLabeledInput',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
prefixCls: PropTypes.string,
|
||||
inputType: PropTypes.oneOf(ClearableInputType),
|
||||
|
@ -30,7 +32,6 @@ const ClearableLabeledInput = {
|
|||
prefix: PropTypes.any,
|
||||
addonBefore: PropTypes.any,
|
||||
addonAfter: PropTypes.any,
|
||||
className: PropTypes.string,
|
||||
readOnly: PropTypes.bool,
|
||||
},
|
||||
methods: {
|
||||
|
@ -71,7 +72,7 @@ const ClearableLabeledInput = {
|
|||
const suffix = this.renderSuffix(prefixCls);
|
||||
if (!hasPrefixSuffix(this)) {
|
||||
return cloneElement(element, {
|
||||
props: { value: props.value },
|
||||
value: props.value,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -91,7 +92,7 @@ const ClearableLabeledInput = {
|
|||
{prefix}
|
||||
{cloneElement(element, {
|
||||
style: null,
|
||||
props: { value: props.value },
|
||||
value: props.value,
|
||||
class: getInputClassName(prefixCls, props.size, props.disabled),
|
||||
})}
|
||||
{suffix}
|
||||
|
@ -100,7 +101,8 @@ const ClearableLabeledInput = {
|
|||
},
|
||||
|
||||
renderInputWithLabel(prefixCls, labeledElement) {
|
||||
const { addonBefore, addonAfter, style, size, className } = this.$props;
|
||||
const { addonBefore, addonAfter, size } = this.$props;
|
||||
const { style, class: className } = this.$attrs;
|
||||
// Not wrap when there is not addons
|
||||
if (!addonBefore && !addonAfter) {
|
||||
return labeledElement;
|
||||
|
@ -136,11 +138,10 @@ const ClearableLabeledInput = {
|
|||
},
|
||||
|
||||
renderTextAreaWithClearIcon(prefixCls, element) {
|
||||
const { value, allowClear, className, style } = this.$props;
|
||||
const { value, allowClear } = this.$props;
|
||||
const { style, class: className } = this.$attrs;
|
||||
if (!allowClear) {
|
||||
return cloneElement(element, {
|
||||
props: { value },
|
||||
});
|
||||
return cloneElement(element, { value });
|
||||
}
|
||||
const affixWrapperCls = classNames(
|
||||
className,
|
||||
|
@ -151,7 +152,7 @@ const ClearableLabeledInput = {
|
|||
<span class={affixWrapperCls} style={style}>
|
||||
{cloneElement(element, {
|
||||
style: null,
|
||||
props: { value },
|
||||
value,
|
||||
})}
|
||||
{this.renderClearIcon(prefixCls)}
|
||||
</span>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { inject } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { filterEmpty, getListeners } from '../_util/props-util';
|
||||
import { filterEmpty, getSlot } from '../_util/props-util';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
|
||||
export default {
|
||||
|
@ -13,8 +14,10 @@ export default {
|
|||
},
|
||||
compact: Boolean,
|
||||
},
|
||||
inject: {
|
||||
configProvider: { default: () => ConfigConsumerProps },
|
||||
setup() {
|
||||
return {
|
||||
configProvider: inject('configProvider', ConfigConsumerProps),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
classes() {
|
||||
|
@ -30,12 +33,7 @@ export default {
|
|||
};
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
render() {
|
||||
return (
|
||||
<span class={this.classes} {...{ on: getListeners(this) }}>
|
||||
{filterEmpty(this.$slots.default)}
|
||||
</span>
|
||||
);
|
||||
return <span class={this.classes}>{filterEmpty(getSlot(this))}</span>;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { inject } from 'vue';
|
||||
import classNames from 'classnames';
|
||||
import TextArea from './TextArea';
|
||||
import omit from 'omit.js';
|
||||
import inputProps from './inputProps';
|
||||
import { hasProp, getComponentFromProp, getListeners, getOptionProps } from '../_util/props-util';
|
||||
import { hasProp, getComponent, getOptionProps } from '../_util/props-util';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
import ClearableLabeledInput from './ClearableLabeledInput';
|
||||
|
||||
|
@ -52,15 +52,13 @@ export function getInputClassName(prefixCls, size, disabled) {
|
|||
export default {
|
||||
name: 'AInput',
|
||||
inheritAttrs: false,
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change.value',
|
||||
},
|
||||
props: {
|
||||
...inputProps,
|
||||
},
|
||||
inject: {
|
||||
configProvider: { default: () => ConfigConsumerProps },
|
||||
setup() {
|
||||
return {
|
||||
configProvider: inject('configProvider', ConfigConsumerProps),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
const props = this.$props;
|
||||
|
@ -89,14 +87,22 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
focus() {
|
||||
this.$refs.input.focus();
|
||||
this.input.focus();
|
||||
},
|
||||
|
||||
blur() {
|
||||
this.$refs.input.blur();
|
||||
this.input.blur();
|
||||
},
|
||||
select() {
|
||||
this.$refs.input.select();
|
||||
this.input.select();
|
||||
},
|
||||
|
||||
saveClearableInput(input) {
|
||||
this.clearableInput = input;
|
||||
},
|
||||
|
||||
saveInput(input) {
|
||||
this.input = input;
|
||||
},
|
||||
|
||||
setValue(value, callback) {
|
||||
|
@ -109,13 +115,11 @@ export default {
|
|||
callback && callback();
|
||||
});
|
||||
} else {
|
||||
// 不在严格受控
|
||||
// https://github.com/vueComponent/ant-design-vue/issues/2207,modal 是 新 new 实例,更新队列和当前不在同一个更新队列中
|
||||
// this.$forceUpdate();
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
onChange(e) {
|
||||
this.$emit('change.value', e.target.value);
|
||||
this.$emit('update:value', e.target.value);
|
||||
this.$emit('change', e);
|
||||
this.$emit('input', e);
|
||||
},
|
||||
|
@ -123,9 +127,9 @@ export default {
|
|||
this.setValue('', () => {
|
||||
this.focus();
|
||||
});
|
||||
resolveOnChange(this.$refs.input, e, this.onChange);
|
||||
resolveOnChange(this.input, e, this.onChange);
|
||||
},
|
||||
renderInput(prefixCls) {
|
||||
renderInput(prefixCls, { addonBefore, addonAfter }) {
|
||||
const otherProps = omit(this.$props, [
|
||||
'prefixCls',
|
||||
'addonBefore',
|
||||
|
@ -133,42 +137,43 @@ export default {
|
|||
'prefix',
|
||||
'suffix',
|
||||
'allowClear',
|
||||
'value',
|
||||
'defaultValue',
|
||||
'lazy',
|
||||
'size',
|
||||
'inputType',
|
||||
'className',
|
||||
'autoFocus',
|
||||
'inputPrefixCls',
|
||||
'loading',
|
||||
]);
|
||||
const { stateValue, handleKeyDown, handleChange, size, disabled } = this;
|
||||
const { handleKeyDown, handleChange, size, disabled, $attrs } = this;
|
||||
|
||||
const inputProps = {
|
||||
directives: [{ name: 'ant-input' }],
|
||||
domProps: {
|
||||
value: fixControlledValue(stateValue),
|
||||
},
|
||||
attrs: { ...otherProps, ...this.$attrs },
|
||||
on: {
|
||||
...getListeners(this),
|
||||
keydown: handleKeyDown,
|
||||
input: handleChange,
|
||||
change: noop,
|
||||
},
|
||||
class: getInputClassName(prefixCls, size, disabled),
|
||||
ref: 'input',
|
||||
...otherProps,
|
||||
...$attrs,
|
||||
onKeydown: handleKeyDown,
|
||||
class: classNames(getInputClassName(prefixCls, size, disabled), {
|
||||
[$attrs.class]: $attrs.class && !addonBefore && !addonAfter,
|
||||
}),
|
||||
ref: this.saveInput,
|
||||
key: 'ant-input',
|
||||
};
|
||||
return <input {...inputProps} />;
|
||||
// vue bug ?
|
||||
if (inputProps.maxLength === undefined) {
|
||||
delete inputProps.maxLength;
|
||||
}
|
||||
return <input {...inputProps} onInput={handleChange} onChange={noop} />;
|
||||
},
|
||||
clearPasswordValueAttribute() {
|
||||
// https://github.com/ant-design/ant-design/issues/20541
|
||||
this.removePasswordTimeout = setTimeout(() => {
|
||||
if (
|
||||
this.$refs.input &&
|
||||
this.$refs.input.getAttribute &&
|
||||
this.$refs.input.getAttribute('type') === 'password' &&
|
||||
this.$refs.input.hasAttribute('value')
|
||||
this.input &&
|
||||
this.input.getAttribute &&
|
||||
this.input.getAttribute('type') === 'password' &&
|
||||
this.input.hasAttribute('value')
|
||||
) {
|
||||
this.$refs.input.removeAttribute('value');
|
||||
this.input.removeAttribute('value');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -177,7 +182,7 @@ export default {
|
|||
// https://github.com/vueComponent/ant-design-vue/issues/2203
|
||||
if (((e.isComposing || composing) && this.lazy) || this.stateValue === value) return;
|
||||
this.setValue(value, this.clearPasswordValueAttribute);
|
||||
resolveOnChange(this.$refs.input, e, this.onChange);
|
||||
resolveOnChange(this.input, e, this.onChange);
|
||||
},
|
||||
handleKeyDown(e) {
|
||||
if (e.keyCode === 13) {
|
||||
|
@ -187,42 +192,37 @@ export default {
|
|||
},
|
||||
},
|
||||
render() {
|
||||
if (this.$props.type === 'textarea') {
|
||||
const textareaProps = {
|
||||
props: this.$props,
|
||||
attrs: this.$attrs,
|
||||
on: {
|
||||
...getListeners(this),
|
||||
input: this.handleChange,
|
||||
keydown: this.handleKeyDown,
|
||||
change: noop,
|
||||
},
|
||||
};
|
||||
return <TextArea {...textareaProps} ref="input" />;
|
||||
}
|
||||
// if (this.$props.type === 'textarea') {
|
||||
// const textareaProps = {
|
||||
// ...this.$props,
|
||||
// ...this.$attrs,
|
||||
// onInput: this.handleChange,
|
||||
// onKeydown: this.handleKeyDown,
|
||||
// onChange: noop,
|
||||
// };
|
||||
// return <TextArea {...textareaProps} ref="input" />;
|
||||
// }
|
||||
const { prefixCls: customizePrefixCls } = this.$props;
|
||||
const { stateValue } = this.$data;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('input', customizePrefixCls);
|
||||
const addonAfter = getComponentFromProp(this, 'addonAfter');
|
||||
const addonBefore = getComponentFromProp(this, 'addonBefore');
|
||||
const suffix = getComponentFromProp(this, 'suffix');
|
||||
const prefix = getComponentFromProp(this, 'prefix');
|
||||
const addonAfter = getComponent(this, 'addonAfter');
|
||||
const addonBefore = getComponent(this, 'addonBefore');
|
||||
const suffix = getComponent(this, 'suffix');
|
||||
const prefix = getComponent(this, 'prefix');
|
||||
const props = {
|
||||
props: {
|
||||
...getOptionProps(this),
|
||||
prefixCls,
|
||||
inputType: 'input',
|
||||
value: fixControlledValue(stateValue),
|
||||
element: this.renderInput(prefixCls),
|
||||
handleReset: this.handleReset,
|
||||
addonAfter,
|
||||
addonBefore,
|
||||
suffix,
|
||||
prefix,
|
||||
},
|
||||
on: getListeners(this),
|
||||
...this.$attrs,
|
||||
...getOptionProps(this),
|
||||
prefixCls,
|
||||
inputType: 'input',
|
||||
value: fixControlledValue(stateValue),
|
||||
element: this.renderInput(prefixCls, { addonAfter, addonBefore }),
|
||||
handleReset: this.handleReset,
|
||||
addonAfter,
|
||||
addonBefore,
|
||||
suffix,
|
||||
prefix,
|
||||
};
|
||||
return <ClearableLabeledInput {...props} />;
|
||||
return <ClearableLabeledInput {...props} ref={this.saveClearableInput} />;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import { getComponentFromProp, getOptionProps, getListeners } from '../_util/props-util';
|
||||
import { getComponent, getOptionProps } from '../_util/props-util';
|
||||
import Input from './Input';
|
||||
import EyeOutlined from '@ant-design/icons-vue/EyeOutlined';
|
||||
import EyeInvisibleOutlined from '@ant-design/icons-vue/EyeInvisibleOutlined';
|
||||
|
@ -8,18 +8,14 @@ import PropTypes from '../_util/vue-types';
|
|||
import BaseMixin from '../_util/BaseMixin';
|
||||
|
||||
const ActionMap = {
|
||||
click: 'click',
|
||||
hover: 'mouseover',
|
||||
click: 'onClick',
|
||||
hover: 'onMouseover',
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'AInputPassword',
|
||||
mixins: [BaseMixin],
|
||||
inheritAttrs: false,
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change.value',
|
||||
},
|
||||
props: {
|
||||
...inputProps,
|
||||
prefixCls: PropTypes.string.def('ant-input-password'),
|
||||
|
@ -33,11 +29,14 @@ export default {
|
|||
};
|
||||
},
|
||||
methods: {
|
||||
saveInput(node) {
|
||||
this.input = node;
|
||||
},
|
||||
focus() {
|
||||
this.$refs.input.focus();
|
||||
this.input.focus();
|
||||
},
|
||||
blur() {
|
||||
this.$refs.input.blur();
|
||||
this.input.blur();
|
||||
},
|
||||
onVisibleChange() {
|
||||
if (this.disabled) {
|
||||
|
@ -51,18 +50,16 @@ export default {
|
|||
const { prefixCls, action } = this.$props;
|
||||
const iconTrigger = ActionMap[action] || '';
|
||||
const iconProps = {
|
||||
on: {
|
||||
[iconTrigger]: this.onVisibleChange,
|
||||
mousedown: e => {
|
||||
// Prevent focused state lost
|
||||
// https://github.com/ant-design/ant-design/issues/15173
|
||||
e.preventDefault();
|
||||
},
|
||||
mouseup: e => {
|
||||
// Prevent focused state lost
|
||||
// https://github.com/ant-design/ant-design/pull/23633/files
|
||||
e.preventDefault();
|
||||
},
|
||||
[iconTrigger]: this.onVisibleChange,
|
||||
onMousedown: e => {
|
||||
// Prevent focused state lost
|
||||
// https://github.com/ant-design/ant-design/issues/15173
|
||||
e.preventDefault();
|
||||
},
|
||||
onMouseup: e => {
|
||||
// Prevent focused state lost
|
||||
// https://github.com/ant-design/ant-design/pull/23633/files
|
||||
e.preventDefault();
|
||||
},
|
||||
class: `${prefixCls}-icon`,
|
||||
key: 'passwordIcon',
|
||||
|
@ -83,28 +80,24 @@ export default {
|
|||
visibilityToggle,
|
||||
...restProps
|
||||
} = getOptionProps(this);
|
||||
const { class: className } = this.$attrs;
|
||||
const suffixIcon = visibilityToggle && this.getIcon();
|
||||
const inputClassName = classNames(prefixCls, {
|
||||
const inputClassName = classNames(prefixCls, className, {
|
||||
[`${prefixCls}-${size}`]: !!size,
|
||||
});
|
||||
const inputProps = {
|
||||
props: {
|
||||
...restProps,
|
||||
prefixCls: inputPrefixCls,
|
||||
size,
|
||||
suffix: suffixIcon,
|
||||
prefix: getComponentFromProp(this, 'prefix'),
|
||||
addonAfter: getComponentFromProp(this, 'addonAfter'),
|
||||
addonBefore: getComponentFromProp(this, 'addonBefore'),
|
||||
},
|
||||
attrs: {
|
||||
...this.$attrs,
|
||||
type: this.visible ? 'text' : 'password',
|
||||
},
|
||||
...restProps,
|
||||
prefixCls: inputPrefixCls,
|
||||
size,
|
||||
suffix: suffixIcon,
|
||||
prefix: getComponent(this, 'prefix'),
|
||||
addonAfter: getComponent(this, 'addonAfter'),
|
||||
addonBefore: getComponent(this, 'addonBefore'),
|
||||
...this.$attrs,
|
||||
type: this.visible ? 'text' : 'password',
|
||||
class: inputClassName,
|
||||
ref: 'input',
|
||||
on: getListeners(this),
|
||||
};
|
||||
return <Input {...inputProps} />;
|
||||
return <Input {...inputProps} ref={this.saveInput} />;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@ import warning from '../_util/warning';
|
|||
import BaseMixin from '../_util/BaseMixin';
|
||||
import inputProps from './inputProps';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { getOptionProps, getListeners } from '../_util/props-util';
|
||||
import { getOptionProps } from '../_util/props-util';
|
||||
|
||||
const RESIZE_STATUS_NONE = 0;
|
||||
const RESIZE_STATUS_RESIZING = 1;
|
||||
|
@ -20,6 +20,7 @@ const TextAreaProps = {
|
|||
};
|
||||
const ResizableTextArea = {
|
||||
name: 'ResizableTextArea',
|
||||
inheritAttrs: false,
|
||||
props: TextAreaProps,
|
||||
data() {
|
||||
return {
|
||||
|
@ -43,6 +44,9 @@ const ResizableTextArea = {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
saveTextArea(textArea) {
|
||||
this.textArea = textArea;
|
||||
},
|
||||
handleResize(size) {
|
||||
const { resizeStatus } = this.$data;
|
||||
const { autoSize } = this.$props;
|
||||
|
@ -62,11 +66,11 @@ const ResizableTextArea = {
|
|||
|
||||
resizeTextarea() {
|
||||
const autoSize = this.$props.autoSize || this.$props.autosize;
|
||||
if (!autoSize || !this.$refs.textArea) {
|
||||
if (!autoSize || !this.textArea) {
|
||||
return;
|
||||
}
|
||||
const { minRows, maxRows } = autoSize;
|
||||
const textareaStyles = calculateNodeHeight(this.$refs.textArea, false, minRows, maxRows);
|
||||
const textareaStyles = calculateNodeHeight(this.textArea, false, minRows, maxRows);
|
||||
this.setState({ textareaStyles, resizeStatus: RESIZE_STATUS_RESIZING }, () => {
|
||||
raf.cancel(this.resizeFrameId);
|
||||
this.resizeFrameId = raf(() => {
|
||||
|
@ -82,10 +86,10 @@ const ResizableTextArea = {
|
|||
// https://github.com/ant-design/ant-design/issues/21870
|
||||
fixFirefoxAutoScroll() {
|
||||
try {
|
||||
if (document.activeElement === this.$refs.textArea) {
|
||||
const currentStart = this.$refs.textArea.selectionStart;
|
||||
const currentEnd = this.$refs.textArea.selectionEnd;
|
||||
this.$refs.textArea.setSelectionRange(currentStart, currentEnd);
|
||||
if (document.activeElement === this.textArea) {
|
||||
const currentStart = this.textArea.selectionStart;
|
||||
const currentEnd = this.textArea.selectionEnd;
|
||||
this.textArea.setSelectionRange(currentStart, currentEnd);
|
||||
}
|
||||
} catch (e) {
|
||||
// Fix error in Chrome:
|
||||
|
@ -95,8 +99,8 @@ const ResizableTextArea = {
|
|||
},
|
||||
|
||||
renderTextArea() {
|
||||
const props = getOptionProps(this);
|
||||
const { prefixCls, autoSize, autosize, disabled } = props;
|
||||
const props = { ...getOptionProps(this), ...this.$attrs };
|
||||
const { prefixCls, autoSize, autosize, disabled, class: className } = props;
|
||||
const { textareaStyles, resizeStatus } = this.$data;
|
||||
warning(
|
||||
autosize === undefined,
|
||||
|
@ -105,35 +109,33 @@ const ResizableTextArea = {
|
|||
);
|
||||
const otherProps = omit(props, [
|
||||
'prefixCls',
|
||||
'onPressEnter',
|
||||
'autoSize',
|
||||
'autosize',
|
||||
'defaultValue',
|
||||
'allowClear',
|
||||
'type',
|
||||
'lazy',
|
||||
'value',
|
||||
]);
|
||||
const cls = classNames(prefixCls, {
|
||||
const cls = classNames(prefixCls, className, {
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
});
|
||||
const domProps = {};
|
||||
// Fix https://github.com/ant-design/ant-design/issues/6776
|
||||
// Make sure it could be reset when using form.getFieldDecorator
|
||||
if ('value' in props) {
|
||||
domProps.value = props.value || '';
|
||||
if ('value' in otherProps) {
|
||||
otherProps.value = otherProps.value || '';
|
||||
}
|
||||
const style = {
|
||||
...props.style,
|
||||
...textareaStyles,
|
||||
...(resizeStatus === RESIZE_STATUS_RESIZING
|
||||
? { overflowX: 'hidden', overflowY: 'hidden' }
|
||||
: null),
|
||||
};
|
||||
const textareaProps = {
|
||||
attrs: otherProps,
|
||||
domProps,
|
||||
...otherProps,
|
||||
style,
|
||||
class: cls,
|
||||
on: omit(getListeners(this), 'pressEnter'),
|
||||
directives: [
|
||||
{
|
||||
name: 'ant-input',
|
||||
|
@ -142,7 +144,7 @@ const ResizableTextArea = {
|
|||
};
|
||||
return (
|
||||
<ResizeObserver onResize={this.handleResize} disabled={!(autoSize || autosize)}>
|
||||
<textarea {...textareaProps} ref="textArea" />
|
||||
<textarea {...textareaProps} ref={this.saveTextArea} />
|
||||
</ResizeObserver>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { inject } from 'vue';
|
||||
import classNames from 'classnames';
|
||||
import { isMobile } from 'is-mobile';
|
||||
import Input from './Input';
|
||||
|
@ -7,25 +8,27 @@ import inputProps from './inputProps';
|
|||
import Button from '../button';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { getOptionProps, getComponentFromProp, getListeners } from '../_util/props-util';
|
||||
import { getOptionProps, getComponent } from '../_util/props-util';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
|
||||
export default {
|
||||
name: 'AInputSearch',
|
||||
inheritAttrs: false,
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change.value',
|
||||
},
|
||||
props: {
|
||||
...inputProps,
|
||||
// 不能设置默认值 https://github.com/vueComponent/ant-design-vue/issues/1916
|
||||
enterButton: PropTypes.any,
|
||||
},
|
||||
inject: {
|
||||
configProvider: { default: () => ConfigConsumerProps },
|
||||
setup() {
|
||||
return {
|
||||
configProvider: inject('configProvider', ConfigConsumerProps),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
saveInput(node) {
|
||||
this.input = node;
|
||||
},
|
||||
onChange(e) {
|
||||
if (e && e.target && e.type === 'click') {
|
||||
this.$emit('search', e.target.value, e);
|
||||
|
@ -36,21 +39,21 @@ export default {
|
|||
if (this.loading || this.disabled) {
|
||||
return;
|
||||
}
|
||||
this.$emit('search', this.$refs.input.stateValue, e);
|
||||
this.$emit('search', this.input.stateValue, e);
|
||||
if (!isMobile({ tablet: true })) {
|
||||
this.$refs.input.focus();
|
||||
this.input.focus();
|
||||
}
|
||||
},
|
||||
focus() {
|
||||
this.$refs.input.focus();
|
||||
this.input.focus();
|
||||
},
|
||||
|
||||
blur() {
|
||||
this.$refs.input.blur();
|
||||
this.input.blur();
|
||||
},
|
||||
renderLoading(prefixCls) {
|
||||
const { size } = this.$props;
|
||||
let enterButton = getComponentFromProp(this, 'enterButton');
|
||||
let enterButton = getComponent(this, 'enterButton');
|
||||
// 兼容 <a-input-search enterButton />, 因enterButton类型为 any,此类写法 enterButton 为空字符串
|
||||
enterButton = enterButton || enterButton === '';
|
||||
if (enterButton) {
|
||||
|
@ -64,8 +67,8 @@ export default {
|
|||
},
|
||||
renderSuffix(prefixCls) {
|
||||
const { loading } = this;
|
||||
const suffix = getComponentFromProp(this, 'suffix');
|
||||
let enterButton = getComponentFromProp(this, 'enterButton');
|
||||
const suffix = getComponent(this, 'suffix');
|
||||
let enterButton = getComponent(this, 'enterButton');
|
||||
// 兼容 <a-input-search enterButton />, 因enterButton类型为 any,此类写法 enterButton 为空字符串
|
||||
enterButton = enterButton || enterButton === '';
|
||||
if (loading && !enterButton) {
|
||||
|
@ -93,9 +96,9 @@ export default {
|
|||
renderAddonAfter(prefixCls) {
|
||||
const { size, disabled, loading } = this;
|
||||
const btnClassName = `${prefixCls}-button`;
|
||||
let enterButton = getComponentFromProp(this, 'enterButton');
|
||||
let enterButton = getComponent(this, 'enterButton');
|
||||
enterButton = enterButton || enterButton === '';
|
||||
const addonAfter = getComponentFromProp(this, 'addonAfter');
|
||||
const addonAfter = getComponent(this, 'addonAfter');
|
||||
if (loading && enterButton) {
|
||||
return [this.renderLoading(prefixCls), addonAfter];
|
||||
}
|
||||
|
@ -103,16 +106,15 @@ export default {
|
|||
const enterButtonAsElement = Array.isArray(enterButton) ? enterButton[0] : enterButton;
|
||||
let button;
|
||||
const isAntdButton =
|
||||
enterButtonAsElement.componentOptions &&
|
||||
enterButtonAsElement.componentOptions.Ctor.extendOptions.__ANT_BUTTON;
|
||||
if (enterButtonAsElement.tag === 'button' || isAntdButton) {
|
||||
enterButtonAsElement.type &&
|
||||
isPlainObject(enterButtonAsElement.type) &&
|
||||
enterButtonAsElement.type.__ANT_BUTTON;
|
||||
if (enterButtonAsElement.tagName === 'button' || isAntdButton) {
|
||||
button = cloneElement(enterButtonAsElement, {
|
||||
key: 'enterButton',
|
||||
class: isAntdButton ? btnClassName : '',
|
||||
props: isAntdButton ? { size } : {},
|
||||
on: {
|
||||
click: this.onSearch,
|
||||
},
|
||||
...(isAntdButton ? { size } : {}),
|
||||
onClick: this.onSearch,
|
||||
});
|
||||
} else {
|
||||
button = (
|
||||
|
@ -140,19 +142,23 @@ export default {
|
|||
prefixCls: customizePrefixCls,
|
||||
inputPrefixCls: customizeInputPrefixCls,
|
||||
size,
|
||||
loading,
|
||||
...others
|
||||
} = getOptionProps(this);
|
||||
class: className,
|
||||
...restProps
|
||||
} = { ...getOptionProps(this), ...this.$attrs };
|
||||
delete restProps.onSearch;
|
||||
delete restProps.loading;
|
||||
delete restProps.enterButton;
|
||||
delete restProps.addonBefore;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('input-search', customizePrefixCls);
|
||||
const inputPrefixCls = getPrefixCls('input', customizeInputPrefixCls);
|
||||
|
||||
let enterButton = getComponentFromProp(this, 'enterButton');
|
||||
const addonBefore = getComponentFromProp(this, 'addonBefore');
|
||||
let enterButton = getComponent(this, 'enterButton');
|
||||
const addonBefore = getComponent(this, 'addonBefore');
|
||||
enterButton = enterButton || enterButton === '';
|
||||
let inputClassName;
|
||||
if (enterButton) {
|
||||
inputClassName = classNames(prefixCls, {
|
||||
inputClassName = classNames(prefixCls, className, {
|
||||
[`${prefixCls}-enter-button`]: !!enterButton,
|
||||
[`${prefixCls}-${size}`]: !!size,
|
||||
});
|
||||
|
@ -160,27 +166,18 @@ export default {
|
|||
inputClassName = prefixCls;
|
||||
}
|
||||
|
||||
const on = { ...getListeners(this) };
|
||||
delete on.search;
|
||||
const inputProps = {
|
||||
props: {
|
||||
...others,
|
||||
prefixCls: inputPrefixCls,
|
||||
size,
|
||||
suffix: this.renderSuffix(prefixCls),
|
||||
prefix: getComponentFromProp(this, 'prefix'),
|
||||
addonAfter: this.renderAddonAfter(prefixCls),
|
||||
addonBefore,
|
||||
className: inputClassName,
|
||||
},
|
||||
attrs: this.$attrs,
|
||||
ref: 'input',
|
||||
on: {
|
||||
pressEnter: this.onSearch,
|
||||
...on,
|
||||
change: this.onChange,
|
||||
},
|
||||
...restProps,
|
||||
prefixCls: inputPrefixCls,
|
||||
size,
|
||||
suffix: this.renderSuffix(prefixCls),
|
||||
prefix: getComponent(this, 'prefix'),
|
||||
addonAfter: this.renderAddonAfter(prefixCls),
|
||||
addonBefore,
|
||||
class: inputClassName,
|
||||
onPressEnter: this.onSearch,
|
||||
onChange: this.onChange,
|
||||
};
|
||||
return <Input {...inputProps} />;
|
||||
return <Input {...inputProps} ref={this.saveInput} />;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { inject } from 'vue';
|
||||
import ClearableLabeledInput from './ClearableLabeledInput';
|
||||
import ResizableTextArea from './ResizableTextArea';
|
||||
import inputProps from './inputProps';
|
||||
import hasProp, { getListeners, getOptionProps } from '../_util/props-util';
|
||||
import { hasProp, getOptionProps } from '../_util/props-util';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
import { fixControlledValue, resolveOnChange } from './Input';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
|
@ -15,15 +16,13 @@ const TextAreaProps = {
|
|||
export default {
|
||||
name: 'ATextarea',
|
||||
inheritAttrs: false,
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change.value',
|
||||
},
|
||||
props: {
|
||||
...TextAreaProps,
|
||||
},
|
||||
inject: {
|
||||
configProvider: { default: () => ConfigConsumerProps },
|
||||
setup() {
|
||||
return {
|
||||
configProvider: inject('configProvider', ConfigConsumerProps),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
const value = typeof this.value === 'undefined' ? this.defaultValue : this.value;
|
||||
|
@ -52,9 +51,7 @@ export default {
|
|||
callback && callback();
|
||||
});
|
||||
} else {
|
||||
// 不在严格受控
|
||||
// https://github.com/vueComponent/ant-design-vue/issues/2207,modal 是 新 new 实例,更新队列和当前不在同一个更新队列中
|
||||
// this.$forceUpdate();
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
handleKeyDown(e) {
|
||||
|
@ -64,7 +61,7 @@ export default {
|
|||
this.$emit('keydown', e);
|
||||
},
|
||||
onChange(e) {
|
||||
this.$emit('change.value', e.target.value);
|
||||
this.$emit('update:value', e.target.value);
|
||||
this.$emit('change', e);
|
||||
this.$emit('input', e);
|
||||
},
|
||||
|
@ -73,41 +70,43 @@ export default {
|
|||
if (((e.isComposing || composing) && this.lazy) || this.stateValue === value) return;
|
||||
|
||||
this.setValue(e.target.value, () => {
|
||||
this.$refs.resizableTextArea.resizeTextarea();
|
||||
this.resizableTextArea.resizeTextarea();
|
||||
});
|
||||
resolveOnChange(this.$refs.resizableTextArea.$refs.textArea, e, this.onChange);
|
||||
resolveOnChange(this.resizableTextArea.textArea, e, this.onChange);
|
||||
},
|
||||
|
||||
focus() {
|
||||
this.$refs.resizableTextArea.$refs.textArea.focus();
|
||||
this.resizableTextArea.textArea.focus();
|
||||
},
|
||||
|
||||
blur() {
|
||||
this.$refs.resizableTextArea.$refs.textArea.blur();
|
||||
this.resizableTextArea.textArea.blur();
|
||||
},
|
||||
saveTextArea(resizableTextArea) {
|
||||
this.resizableTextArea = resizableTextArea;
|
||||
},
|
||||
|
||||
saveClearableInput(clearableInput) {
|
||||
this.clearableInput = clearableInput;
|
||||
},
|
||||
handleReset(e) {
|
||||
this.setValue('', () => {
|
||||
this.$refs.resizableTextArea.renderTextArea();
|
||||
this.resizableTextArea.renderTextArea();
|
||||
this.focus();
|
||||
});
|
||||
resolveOnChange(this.$refs.resizableTextArea.$refs.textArea, e, this.onChange);
|
||||
resolveOnChange(this.resizableTextArea.textArea, e, this.onChange);
|
||||
},
|
||||
|
||||
renderTextArea(prefixCls) {
|
||||
const props = getOptionProps(this);
|
||||
const resizeProps = {
|
||||
props: {
|
||||
...props,
|
||||
prefixCls,
|
||||
},
|
||||
on: {
|
||||
...getListeners(this),
|
||||
input: this.handleChange,
|
||||
keydown: this.handleKeyDown,
|
||||
},
|
||||
attrs: this.$attrs,
|
||||
...props,
|
||||
...this.$attrs,
|
||||
prefixCls,
|
||||
onInput: this.handleChange,
|
||||
onKeydown: this.handleKeyDown,
|
||||
};
|
||||
return <ResizableTextArea {...resizeProps} ref="resizableTextArea" />;
|
||||
return <ResizableTextArea {...resizeProps} ref={this.saveTextArea} />;
|
||||
},
|
||||
},
|
||||
render() {
|
||||
|
@ -116,16 +115,14 @@ export default {
|
|||
const prefixCls = getPrefixCls('input', customizePrefixCls);
|
||||
|
||||
const props = {
|
||||
props: {
|
||||
...getOptionProps(this),
|
||||
prefixCls,
|
||||
inputType: 'text',
|
||||
value: fixControlledValue(stateValue),
|
||||
element: this.renderTextArea(prefixCls),
|
||||
handleReset: this.handleReset,
|
||||
},
|
||||
on: getListeners(this),
|
||||
...getOptionProps(this),
|
||||
...this.$attrs,
|
||||
prefixCls,
|
||||
inputType: 'text',
|
||||
value: fixControlledValue(stateValue),
|
||||
element: this.renderTextArea(prefixCls),
|
||||
handleReset: this.handleReset,
|
||||
};
|
||||
return <ClearableLabeledInput {...props} />;
|
||||
return <ClearableLabeledInput {...props} ref={this.saveClearableInput} />;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -40,7 +40,7 @@ describe('TextArea', () => {
|
|||
sync: false,
|
||||
});
|
||||
|
||||
const mockFunc = jest.spyOn(wrapper.vm.$refs.resizableTextArea, 'resizeTextarea');
|
||||
const mockFunc = jest.spyOn(wrapper.vm.resizableTextArea, 'resizeTextarea');
|
||||
await asyncExpect(() => {
|
||||
wrapper.setProps({ value: '1111\n2222\n3333' });
|
||||
});
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import Vue from 'vue';
|
||||
import Input from './Input';
|
||||
import Group from './Group';
|
||||
import Search from './Search';
|
||||
import TextArea from './TextArea';
|
||||
import Password from './Password';
|
||||
import antInputDirective from '../_util/antInputDirective';
|
||||
import Base from '../base';
|
||||
|
||||
Vue.use(antInputDirective);
|
||||
|
||||
Input.Group = Group;
|
||||
Input.Search = Search;
|
||||
|
@ -15,13 +10,12 @@ Input.TextArea = TextArea;
|
|||
Input.Password = Password;
|
||||
|
||||
/* istanbul ignore next */
|
||||
Input.install = function(Vue) {
|
||||
Vue.use(Base);
|
||||
Vue.component(Input.name, Input);
|
||||
Vue.component(Input.Group.name, Input.Group);
|
||||
Vue.component(Input.Search.name, Input.Search);
|
||||
Vue.component(Input.TextArea.name, Input.TextArea);
|
||||
Vue.component(Input.Password.name, Input.Password);
|
||||
Input.install = function(app) {
|
||||
app.component(Input.name, Input);
|
||||
app.component(Input.Group.name, Input.Group);
|
||||
app.component(Input.Search.name, Input.Search);
|
||||
app.component(Input.TextArea.name, Input.TextArea);
|
||||
app.component(Input.Password.name, Input.Password);
|
||||
};
|
||||
|
||||
export default Input;
|
||||
|
|
|
@ -32,5 +32,4 @@ export default {
|
|||
},
|
||||
maxLength: PropTypes.number,
|
||||
loading: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import demo from '../antdv-demo/docs/rate/demo/index';
|
||||
import demo from '../antdv-demo/docs/input/demo/tooltip';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
import '@babel/polyfill';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import { Rate, Button, Upload, Icon, Modal, Progress, notification, message } from 'ant-design-vue';
|
||||
import {
|
||||
Input,
|
||||
Rate,
|
||||
Button,
|
||||
Upload,
|
||||
Icon,
|
||||
Modal,
|
||||
Progress,
|
||||
Tooltip,
|
||||
Col,
|
||||
Row,
|
||||
notification,
|
||||
message,
|
||||
} from 'ant-design-vue';
|
||||
import 'ant-design-vue/style.js';
|
||||
|
||||
const basic = {
|
||||
|
@ -24,4 +37,8 @@ app
|
|||
.use(Modal)
|
||||
.use(Progress)
|
||||
.use(Rate)
|
||||
.use(Input)
|
||||
.use(Tooltip)
|
||||
.use(Col)
|
||||
.use(Row)
|
||||
.mount('#app');
|
||||
|
|
Loading…
Reference in New Issue