feat: update textarea

pull/1845/head
tangjinzhou 2020-02-20 15:44:08 +08:00
parent f39da916af
commit 356e60a12c
25 changed files with 748 additions and 406 deletions

View File

@ -1,5 +1,5 @@
module.exports = {
dev: {
componentName: 'slider', // dev components
componentName: 'input', // dev components
},
};

View File

@ -1,9 +1,8 @@
import PropTypes from '../_util/vue-types';
import addEventListener from '../vc-util/Dom/addEventListener';
import classNames from 'classnames';
import shallowequal from 'shallowequal';
import omit from 'omit.js';
import getScroll from '../_util/getScroll';
import ResizeObserver from '../vc-resize-observer';
import BaseMixin from '../_util/BaseMixin';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import { ConfigConsumerProps } from '../config-provider';
@ -242,11 +241,17 @@ const Affix = {
attrs: omit($props, ['prefixCls', 'offsetTop', 'offsetBottom', 'target']),
};
return (
<div {...props} style={placeholderStyle} ref="placeholderNode">
<div class={className} ref="fixedNode" style={affixStyle}>
{$slots.default}
<ResizeObserver
onResize={() => {
this.updatePosition();
}}
>
<div {...props} style={placeholderStyle} ref="placeholderNode">
<div class={className} ref="fixedNode" style={affixStyle}>
{$slots.default}
</div>
</div>
</div>
</ResizeObserver>
);
},
};

View File

@ -0,0 +1,173 @@
import classNames from 'classnames';
import Icon from '../icon';
import { getInputClassName } from './Input';
import PropTypes from '../_util/vue-types';
import { cloneElement } from '../_util/vnode';
import { getComponentFromProp } from '../_util/props-util';
export function hasPrefixSuffix(instance) {
return !!(
getComponentFromProp(instance, 'prefix') ||
getComponentFromProp(instance, 'suffix') ||
instance.$props.allowClear
);
}
const ClearableInputType = ['text', 'input'];
const ClearableLabeledInput = {
props: {
prefixCls: PropTypes.string,
inputType: PropTypes.oneOf(ClearableInputType),
value: PropTypes.any,
defaultValue: PropTypes.any,
allowClear: PropTypes.bool,
element: PropTypes.any,
handleReset: PropTypes.func,
disabled: PropTypes.bool,
size: PropTypes.oneOf(['small', 'large', 'default']),
suffix: PropTypes.any,
prefix: PropTypes.any,
addonBefore: PropTypes.any,
addonAfter: PropTypes.any,
className: PropTypes.string,
},
methods: {
renderClearIcon(prefixCls) {
const { allowClear, value, disabled, inputType, handleReset } = this.$props;
if (!allowClear || disabled || value === undefined || value === null || value === '') {
return null;
}
const className =
inputType === ClearableInputType[0]
? `${prefixCls}-textarea-clear-icon`
: `${prefixCls}-clear-icon`;
return (
<Icon
type="close-circle"
theme="filled"
onClick={handleReset}
class={className}
role="button"
/>
);
},
renderSuffix(prefixCls) {
const { suffix, allowClear } = this.$props;
if (suffix || allowClear) {
return (
<span class={`${prefixCls}-suffix`}>
{this.renderClearIcon(prefixCls)}
{suffix}
</span>
);
}
return null;
},
renderLabeledIcon(prefixCls, element) {
const props = this.$props;
const suffix = this.renderSuffix(prefixCls);
if (!hasPrefixSuffix(this)) {
return cloneElement(element, {
props: { value: props.value },
});
}
const prefix = props.prefix ? (
<span class={`${prefixCls}-prefix`}>{props.prefix}</span>
) : null;
const affixWrapperCls = classNames(props.className, `${prefixCls}-affix-wrapper`, {
[`${prefixCls}-affix-wrapper-sm`]: props.size === 'small',
[`${prefixCls}-affix-wrapper-lg`]: props.size === 'large',
[`${prefixCls}-affix-wrapper-input-with-clear-btn`]:
props.suffix && props.allowClear && this.$props.value,
});
return (
<span class={affixWrapperCls} style={props.style}>
{prefix}
{cloneElement(element, {
style: null,
props: { value: props.value },
class: getInputClassName(prefixCls, props.size, props.disabled),
})}
{suffix}
</span>
);
},
renderInputWithLabel(prefixCls, labeledElement) {
const { addonBefore, addonAfter, style, size, className } = this.$props;
// Not wrap when there is not addons
if (!addonBefore && !addonAfter) {
return labeledElement;
}
const wrapperClassName = `${prefixCls}-group`;
const addonClassName = `${wrapperClassName}-addon`;
const addonBeforeNode = addonBefore ? (
<span class={addonClassName}>{addonBefore}</span>
) : null;
const addonAfterNode = addonAfter ? <span class={addonClassName}>{addonAfter}</span> : null;
const mergedWrapperClassName = classNames(`${prefixCls}-wrapper`, {
[wrapperClassName]: addonBefore || addonAfter,
});
const mergedGroupClassName = classNames(className, `${prefixCls}-group-wrapper`, {
[`${prefixCls}-group-wrapper-sm`]: size === 'small',
[`${prefixCls}-group-wrapper-lg`]: size === 'large',
});
// Need another wrapper for changing display:table to display:inline-block
// and put style prop in wrapper
return (
<span class={mergedGroupClassName} style={style}>
<span class={mergedWrapperClassName}>
{addonBeforeNode}
{cloneElement(labeledElement, { style: null })}
{addonAfterNode}
</span>
</span>
);
},
renderTextAreaWithClearIcon(prefixCls, element) {
const { value, allowClear, className, style } = this.$props;
if (!allowClear) {
return cloneElement(element, {
props: { value },
});
}
const affixWrapperCls = classNames(
className,
`${prefixCls}-affix-wrapper`,
`${prefixCls}-affix-wrapper-textarea-with-clear-btn`,
);
return (
<span class={affixWrapperCls} style={style}>
{cloneElement(element, {
style: null,
props: { value },
})}
{this.renderClearIcon(prefixCls)}
</span>
);
},
renderClearableLabeledInput() {
const { prefixCls, inputType, element } = this.$props;
if (inputType === ClearableInputType[0]) {
return this.renderTextAreaWithClearIcon(prefixCls, element);
}
return this.renderInputWithLabel(prefixCls, this.renderLabeledIcon(prefixCls, element));
},
},
render() {
return this.renderClearableLabeledInput();
},
};
export default ClearableLabeledInput;

View File

@ -2,25 +2,45 @@ import classNames from 'classnames';
import TextArea from './TextArea';
import omit from 'omit.js';
import inputProps from './inputProps';
import { hasProp, getComponentFromProp, getListeners } from '../_util/props-util';
import { hasProp, getComponentFromProp, getListeners, getOptionProps } from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider';
import Icon from '../icon';
import ClearableLabeledInput from './ClearableLabeledInput';
function noop() {}
function fixControlledValue(value) {
export function fixControlledValue(value) {
if (typeof value === 'undefined' || value === null) {
return '';
}
return value;
}
function hasPrefixSuffix(instance) {
return !!(
getComponentFromProp(instance, 'prefix') ||
getComponentFromProp(instance, 'suffix') ||
instance.$props.allowClear
);
export function resolveOnChange(target, e, onChange) {
if (onChange) {
let event = e;
if (e.type === 'click') {
// click clear icon
event = { ...e };
event.target = target;
event.currentTarget = target;
const originalInputValue = target.value;
// change target ref value cause e.target.value should be '' when clear input
target.value = '';
onChange(event);
// reset target ref value
target.value = originalInputValue;
return;
}
onChange(event);
}
}
export function getInputClassName(prefixCls, size, disabled) {
return classNames(prefixCls, {
[`${prefixCls}-sm`]: size === 'small',
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-disabled`]: disabled,
});
}
export default {
@ -37,9 +57,10 @@ export default {
configProvider: { default: () => ConfigConsumerProps },
},
data() {
const { value = '', defaultValue = '' } = this.$props;
const props = this.$props;
const value = typeof props.value === 'undefined' ? props.defaultValue : props.value;
return {
stateValue: !hasProp(this, 'value') ? defaultValue : value,
stateValue: value,
};
},
watch: {
@ -52,16 +73,15 @@ export default {
if (this.autoFocus) {
this.focus();
}
this.clearPasswordValueAttribute();
});
},
beforeDestroy() {
if (this.removePasswordTimeout) {
clearTimeout(this.removePasswordTimeout);
}
},
methods: {
handleKeyDown(e) {
if (e.keyCode === 13) {
this.$emit('pressEnter', e);
}
this.$emit('keydown', e);
},
focus() {
this.$refs.input.focus();
},
@ -73,155 +93,30 @@ export default {
this.$refs.input.select();
},
getInputClassName(prefixCls) {
const { size, disabled } = this.$props;
return {
[`${prefixCls}`]: true,
[`${prefixCls}-sm`]: size === 'small',
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-disabled`]: disabled,
};
},
setValue(value, e) {
setValue(value, callback) {
if (this.stateValue === value) {
return;
}
if (!hasProp(this, 'value')) {
this.stateValue = value;
this.$nextTick(() => {
callback && callback();
});
} else {
this.$forceUpdate();
}
this.$emit('change.value', value);
let event = e;
if (e.type === 'click' && this.$refs.input) {
// click clear icon
event = { ...e };
event.target = this.$refs.input;
event.currentTarget = this.$refs.input;
const originalInputValue = this.$refs.input.value;
// change input value cause e.target.value should be '' when clear input
this.$refs.input.value = '';
this.$emit('change', event);
this.$emit('input', event);
// reset input value
this.$refs.input.value = originalInputValue;
return;
}
},
onChange(e) {
this.$emit('change.value', e.target.value);
this.$emit('change', e);
this.$emit('input', e);
},
handleReset(e) {
this.setValue('', e);
this.$nextTick(() => {
this.setValue('', () => {
this.focus();
});
resolveOnChange(this.$refs.input, e, this.onChange);
},
handleChange(e) {
const { value, composing } = e.target;
if (composing && this.lazy) return;
this.setValue(value, e);
},
renderClearIcon(prefixCls) {
const { allowClear, disabled } = this.$props;
const { stateValue } = this;
if (
!allowClear ||
disabled ||
stateValue === undefined ||
stateValue === null ||
stateValue === ''
) {
return null;
}
return (
<Icon
type="close-circle"
theme="filled"
onClick={this.handleReset}
class={`${prefixCls}-clear-icon`}
role="button"
/>
);
},
renderSuffix(prefixCls) {
const { allowClear } = this.$props;
let suffix = getComponentFromProp(this, 'suffix');
if (suffix || allowClear) {
return (
<span class={`${prefixCls}-suffix`} key="suffix">
{this.renderClearIcon(prefixCls)}
{suffix}
</span>
);
}
return null;
},
renderLabeledInput(prefixCls, children) {
const props = this.$props;
let addonAfter = getComponentFromProp(this, 'addonAfter');
let addonBefore = getComponentFromProp(this, 'addonBefore');
// Not wrap when there is not addons
if (!addonBefore && !addonAfter) {
return children;
}
const wrapperClassName = `${prefixCls}-group`;
const addonClassName = `${wrapperClassName}-addon`;
addonBefore = addonBefore ? <span class={addonClassName}>{addonBefore}</span> : null;
addonAfter = addonAfter ? <span class={addonClassName}>{addonAfter}</span> : null;
const mergedWrapperClassName = {
[`${prefixCls}-wrapper`]: true,
[wrapperClassName]: addonBefore || addonAfter,
};
const mergedGroupClassName = classNames(`${prefixCls}-group-wrapper`, {
[`${prefixCls}-group-wrapper-sm`]: props.size === 'small',
[`${prefixCls}-group-wrapper-lg`]: props.size === 'large',
});
return (
<span class={mergedGroupClassName}>
<span class={mergedWrapperClassName}>
{addonBefore}
{children}
{addonAfter}
</span>
</span>
);
},
renderLabeledIcon(prefixCls, children) {
const { size } = this.$props;
let suffix = this.renderSuffix(prefixCls);
if (!hasPrefixSuffix(this)) {
return children;
}
let prefix = getComponentFromProp(this, 'prefix');
prefix = prefix ? (
<span class={`${prefixCls}-prefix`} key="prefix">
{prefix}
</span>
) : null;
const affixWrapperCls = classNames(`${prefixCls}-affix-wrapper`, {
[`${prefixCls}-affix-wrapper-sm`]: size === 'small',
[`${prefixCls}-affix-wrapper-lg`]: size === 'large',
});
return (
<span class={affixWrapperCls} key="affix">
{prefix}
{children}
{suffix}
</span>
);
},
renderInput(prefixCls) {
const otherProps = omit(this.$props, [
'prefixCls',
@ -233,8 +128,10 @@ export default {
'value',
'defaultValue',
'lazy',
'size',
'inputType',
]);
const { stateValue, getInputClassName, handleKeyDown, handleChange } = this;
const { stateValue, handleKeyDown, handleChange, size, disabled } = this;
const inputProps = {
directives: [{ name: 'ant-input' }],
domProps: {
@ -247,11 +144,35 @@ export default {
input: handleChange,
change: noop,
},
class: getInputClassName(prefixCls),
class: getInputClassName(prefixCls, size, disabled),
ref: 'input',
key: 'ant-input',
};
return this.renderLabeledIcon(prefixCls, <input {...inputProps} />);
return <input {...inputProps} />;
},
clearPasswordValueAttribute() {
// https://github.com/ant-design/ant-design/issues/20541
this.removePasswordTimeout = setTimeout(() => {
if (
this.$refs.input &&
this.$refs.input.getAttribute('type') === 'password' &&
this.$refs.input.hasAttribute('value')
) {
this.$refs.input.removeAttribute('value');
}
});
},
handleChange(e) {
const { value, composing } = e.target;
if (composing && this.lazy) return;
this.setValue(value, this.clearPasswordValueAttribute);
resolveOnChange(this.$refs.input, e, this.onChange);
},
handleKeyDown(e) {
if (e.keyCode === 13) {
this.$emit('pressEnter', e);
}
this.$emit('keydown', e);
},
},
render() {
@ -274,8 +195,28 @@ export default {
return <TextArea {...textareaProps} ref="input" />;
}
const { prefixCls: customizePrefixCls } = this.$props;
const { stateValue } = this.$data;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('input', customizePrefixCls);
return this.renderLabeledInput(prefixCls, this.renderInput(prefixCls));
const addonAfter = getComponentFromProp(this, 'addonAfter');
const addonBefore = getComponentFromProp(this, 'addonBefore');
const suffix = getComponentFromProp(this, 'suffix');
const prefix = getComponentFromProp(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),
};
return <ClearableLabeledInput {...props} />;
},
};

View File

@ -38,7 +38,10 @@ export default {
blur() {
this.$refs.input.blur();
},
onChange() {
onVisibleChange() {
if (this.disabled) {
return;
}
this.setState({
visible: !this.visible,
});
@ -51,7 +54,7 @@ export default {
type: this.visible ? 'eye' : 'eye-invisible',
},
on: {
[iconTrigger]: this.onChange,
[iconTrigger]: this.onVisibleChange,
mousedown: e => {
// Prevent focused state lost
// https://github.com/ant-design/ant-design/issues/15173

View File

@ -0,0 +1,114 @@
import ResizeObserver from '../vc-resize-observer';
import omit from 'omit.js';
import classNames from 'classnames';
import calculateNodeHeight from './calculateNodeHeight';
import raf from '../_util/raf';
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';
const TextAreaProps = {
...inputProps,
autosize: PropTypes.oneOfType([Object, Boolean]),
autoSize: PropTypes.oneOfType([Object, Boolean]),
};
const ResizableTextArea = {
name: 'ResizableTextArea',
props: TextAreaProps,
data() {
return {
textareaStyles: {},
resizing: false,
};
},
mixins: [BaseMixin],
mounted() {
this.resizeTextarea();
},
beforeDestroy() {
raf.cancel(this.nextFrameActionId);
raf.cancel(this.resizeFrameId);
},
watch: {
value() {
this.$nextTick(() => {
this.resizeTextarea();
});
},
},
methods: {
resizeOnNextFrame() {
raf.cancel(this.nextFrameActionId);
this.nextFrameActionId = raf(this.resizeTextarea);
},
resizeTextarea() {
const autoSize = this.$props.autoSize || this.$props.autosize;
if (!autoSize || !this.$refs.textArea) {
return;
}
const { minRows, maxRows } = autoSize;
const textareaStyles = calculateNodeHeight(this.$refs.textArea, false, minRows, maxRows);
this.setState({ textareaStyles, resizing: true }, () => {
raf.cancel(this.resizeFrameId);
this.resizeFrameId = raf(() => {
this.setState({ resizing: false });
});
});
},
renderTextArea() {
const props = getOptionProps(this);
const { prefixCls, autoSize, autosize, disabled } = props;
const { textareaStyles, resizing } = this.$data;
warning(
autosize === undefined,
'Input.TextArea',
'autosize is deprecated, please use autoSize instead.',
);
const otherProps = omit(props, [
'prefixCls',
'autoSize',
'autosize',
'defaultValue',
'allowClear',
'type',
'lazy',
'value',
]);
const cls = classNames(prefixCls, {
[`${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 || '';
}
const style = {
...textareaStyles,
...(resizing ? { overflow: 'hidden' } : null),
};
const textareaProps = {
attrs: otherProps,
domProps,
style,
class: cls,
on: omit(getListeners(this), 'pressEnter'),
};
return (
<ResizeObserver onResize={this.resizeOnNextFrame} disabled={!(autoSize || autosize)}>
<textarea {...textareaProps} ref="textArea" />
</ResizeObserver>
);
},
},
render() {
return this.renderTextArea();
},
};
export default ResizableTextArea;

View File

@ -1,4 +1,5 @@
import classNames from 'classnames';
import { isMobile } from 'is-mobile';
import Input from './Input';
import Icon from '../icon';
import inputProps from './inputProps';
@ -17,15 +18,26 @@ export default {
},
props: {
...inputProps,
enterButton: PropTypes.oneOfType([PropTypes.bool, PropTypes.string, PropTypes.object]),
enterButton: PropTypes.any.def(false),
},
inject: {
configProvider: { default: () => ConfigConsumerProps },
},
methods: {
onChange(e) {
if (e && e.target && e.type === 'click') {
this.$emit('search', e.target.value, e);
}
this.$emit('change', e);
},
onSearch(e) {
if (this.loading || this.disabled) {
return;
}
this.$emit('search', this.$refs.input.stateValue, e);
this.$refs.input.focus();
if (!isMobile({ tablet: true })) {
this.$refs.input.focus();
}
},
focus() {
this.$refs.input.focus();
@ -34,12 +46,33 @@ export default {
blur() {
this.$refs.input.blur();
},
renderLoading(prefixCls) {
const { size } = this.$props;
let enterButton = getComponentFromProp(this, 'enterButton');
// <a-input-search enterButton /> enterButton any enterButton
enterButton = enterButton || enterButton === '';
if (enterButton) {
return (
<Button class={`${prefixCls}-button`} type="primary" size={size} key="enterButton">
<Icon type="loading" />
</Button>
);
}
return <Icon class={`${prefixCls}-icon`} type="loading" key="loadingIcon" />;
},
renderSuffix(prefixCls) {
const { loading } = this;
const suffix = getComponentFromProp(this, 'suffix');
const enterButton = getComponentFromProp(this, 'enterButton');
let enterButton = getComponentFromProp(this, 'enterButton');
// <a-input-search enterButton /> enterButton any enterButton
enterButton = enterButton || enterButton === '';
if (loading && !enterButton) {
return [suffix, this.renderLoading(prefixCls)];
}
if (enterButton) return suffix;
const node = (
const icon = (
<Icon class={`${prefixCls}-icon`} type="search" key="searchIcon" onClick={this.onSearch} />
);
@ -50,27 +83,31 @@ export default {
// key: 'originSuffix',
// });
// }
return [suffix, node];
return [suffix, icon];
}
return node;
return icon;
},
renderAddonAfter(prefixCls) {
const { size, disabled } = this;
const enterButton = getComponentFromProp(this, 'enterButton');
const addonAfter = getComponentFromProp(this, 'addonAfter');
if (!enterButton) return addonAfter;
const { size, disabled, loading } = this;
const btnClassName = `${prefixCls}-button`;
let enterButton = getComponentFromProp(this, 'enterButton');
enterButton = enterButton || enterButton === '';
const addonAfter = getComponentFromProp(this, 'addonAfter');
if (loading && enterButton) {
return [this.renderLoading(prefixCls), addonAfter];
}
if (!enterButton) return addonAfter;
const enterButtonAsElement = Array.isArray(enterButton) ? enterButton[0] : enterButton;
let button;
if (
enterButtonAsElement.tag === 'button' ||
(enterButtonAsElement.componentOptions &&
enterButtonAsElement.componentOptions.Ctor.extendOptions.__ANT_BUTTON)
) {
const isAntdButton =
enterButtonAsElement.componentOptions &&
enterButtonAsElement.componentOptions.Ctor.extendOptions.__ANT_BUTTON;
if (enterButtonAsElement.tag === 'button' || isAntdButton) {
button = cloneElement(enterButtonAsElement, {
class: btnClassName,
props: { size },
key: 'enterButton',
class: isAntdButton ? btnClassName : '',
props: isAntdButton ? { size } : {},
on: {
click: this.onSearch,
},
@ -85,7 +122,7 @@ export default {
key="enterButton"
onClick={this.onSearch}
>
{enterButton === true ? <Icon type="search" /> : enterButton}
{enterButton === true || enterButton === '' ? <Icon type="search" /> : enterButton}
</Button>
);
}
@ -101,14 +138,16 @@ export default {
prefixCls: customizePrefixCls,
inputPrefixCls: customizeInputPrefixCls,
size,
loading,
...others
} = getOptionProps(this);
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('input-search', customizePrefixCls);
const inputPrefixCls = getPrefixCls('input', customizeInputPrefixCls);
const enterButton = getComponentFromProp(this, 'enterButton');
let enterButton = getComponentFromProp(this, 'enterButton');
const addonBefore = getComponentFromProp(this, 'addonBefore');
enterButton = enterButton || enterButton === '';
let inputClassName;
if (enterButton) {
inputClassName = classNames(prefixCls, {
@ -137,6 +176,7 @@ export default {
on: {
pressEnter: this.onSearch,
...on,
change: this.onChange,
},
};
return <Input {...inputProps} />;

View File

@ -1,32 +1,16 @@
import classNames from 'classnames';
import omit from 'omit.js';
import ResizeObserver from 'resize-observer-polyfill';
import ClearableLabeledInput from './ClearableLabeledInput';
import ResizableTextArea from './ResizableTextArea';
import inputProps from './inputProps';
import calculateNodeHeight from './calculateNodeHeight';
import hasProp, { getListeners } from '../_util/props-util';
import hasProp, { getListeners, getOptionProps } from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider';
import { fixControlledValue, resolveOnChange } from './Input';
import PropTypes from '../_util/vue-types';
function onNextFrame(cb) {
if (window.requestAnimationFrame) {
return window.requestAnimationFrame(cb);
}
return window.setTimeout(cb, 1);
}
function clearNextFrameAction(nextFrameId) {
if (window.cancelAnimationFrame) {
window.cancelAnimationFrame(nextFrameId);
} else {
window.clearTimeout(nextFrameId);
}
}
function fixControlledValue(value) {
if (typeof value === 'undefined' || value === null) {
return '';
}
return value;
}
function noop() {}
const TextAreaProps = {
...inputProps,
autosize: PropTypes.oneOfType([Object, Boolean]),
autoSize: PropTypes.oneOfType([Object, Boolean]),
};
export default {
name: 'ATextarea',
@ -36,68 +20,39 @@ export default {
event: 'change.value',
},
props: {
...inputProps,
autosize: [Object, Boolean],
...TextAreaProps,
},
inject: {
configProvider: { default: () => ConfigConsumerProps },
},
data() {
const { value = '', defaultValue = '' } = this.$props;
const value = typeof this.value === 'undefined' ? this.defaultValue : this.value;
return {
stateValue: fixControlledValue(!hasProp(this, 'value') ? defaultValue : value),
nextFrameActionId: undefined,
textareaStyles: {},
stateValue: value,
};
},
computed: {},
watch: {
value(val) {
this.$nextTick(() => {
this.resizeOnNextFrame();
});
this.stateValue = fixControlledValue(val);
},
autosize(val) {
if (!val && this.$refs.textArea) {
this.textareaStyles = omit(this.textareaStyles, ['overflowY']);
}
this.stateValue = val;
},
},
mounted() {
this.$nextTick(() => {
this.resizeTextarea();
this.updateResizeObserverHook();
if (this.autoFocus) {
this.focus();
}
});
},
updated() {
this.updateResizeObserverHook();
},
beforeDestroy() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
},
methods: {
resizeOnNextFrame() {
if (this.nextFrameActionId) {
clearNextFrameAction(this.nextFrameActionId);
}
this.nextFrameActionId = onNextFrame(this.resizeTextarea);
},
// We will update hooks if `autosize` prop change
updateResizeObserverHook() {
if (!this.resizeObserver && this.$props.autosize) {
// Add resize observer
this.resizeObserver = new ResizeObserver(this.resizeOnNextFrame);
this.resizeObserver.observe(this.$refs.textArea);
} else if (this.resizeObserver && !this.$props.autosize) {
// Remove resize observer
this.resizeObserver.disconnect();
this.resizeObserver = null;
setValue(value, callback) {
if (!hasProp(this, 'value')) {
this.stateValue = value;
this.$nextTick(() => {
callback && callback();
});
} else {
this.$forceUpdate();
}
},
handleKeyDown(e) {
@ -106,82 +61,69 @@ export default {
}
this.$emit('keydown', e);
},
resizeTextarea() {
const { autosize } = this.$props;
if (!autosize || !this.$refs.textArea) {
return;
}
const { minRows, maxRows } = autosize;
const textareaStyles = calculateNodeHeight(this.$refs.textArea, false, minRows, maxRows);
this.textareaStyles = textareaStyles;
},
handleTextareaChange(e) {
const { value, composing } = e.target;
if (composing || this.stateValue === value) return;
if (!hasProp(this, 'value')) {
this.stateValue = value;
this.resizeTextarea();
} else {
this.$forceUpdate();
}
this.$emit('change.value', value);
onChange(e) {
this.$emit('change.value', e.target.value);
this.$emit('change', e);
this.$emit('input', e);
},
handleChange(e) {
const { value, composing } = e.target;
if (composing || this.stateValue === value) return;
this.setValue(e.target.value, () => {
this.$refs.resizableTextArea.resizeTextarea();
});
resolveOnChange(this.$refs.resizableTextArea.$refs.textArea, e, this.onChange);
},
focus() {
this.$refs.textArea.focus();
this.$refs.resizableTextArea.$refs.textArea.focus();
},
blur() {
this.$refs.textArea.blur();
this.$refs.resizableTextArea.$refs.textArea.blur();
},
handleReset(e) {
this.setValue('', () => {
this.$refs.resizableTextArea.renderTextArea();
this.focus();
});
resolveOnChange(this.$refs.resizableTextArea.$refs.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,
};
return <ResizableTextArea {...resizeProps} ref="resizableTextArea" />;
},
},
render() {
const {
stateValue,
handleKeyDown,
handleTextareaChange,
textareaStyles,
$attrs,
prefixCls: customizePrefixCls,
disabled,
} = this;
const otherProps = omit(this.$props, [
'prefixCls',
'autosize',
'type',
'value',
'defaultValue',
'lazy',
]);
const { stateValue, prefixCls: customizePrefixCls } = this;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('input', customizePrefixCls);
const cls = classNames(prefixCls, {
[`${prefixCls}-disabled`]: disabled,
});
const textareaProps = {
directives: [{ name: 'ant-input' }],
attrs: { ...otherProps, ...$attrs },
on: {
...getListeners(this),
keydown: handleKeyDown,
input: handleTextareaChange,
change: noop,
const props = {
props: {
...getOptionProps(this),
prefixCls,
inputType: 'text',
value: fixControlledValue(stateValue),
element: this.renderTextArea(prefixCls),
handleReset: this.handleReset,
},
on: getListeners(this),
};
return (
<textarea
{...textareaProps}
value={stateValue}
class={cls}
style={textareaStyles}
ref="textArea"
/>
);
return <ClearableLabeledInput {...props} />;
},
};

View File

@ -2,4 +2,4 @@
exports[`Input.Search should support custom Button 1`] = `<span class="ant-input-group-wrapper ant-input-search ant-input-search-enter-button"><span class="ant-input-wrapper ant-input-group"><input type="text" class="ant-input"><span class="ant-input-group-addon"><button type="button" class="ant-btn ant-input-search-button"><span>ok</span></button></span></span></span>`;
exports[`Input.Search should support custom button 1`] = `<span class="ant-input-group-wrapper ant-input-search ant-input-search-enter-button"><span class="ant-input-wrapper ant-input-group"><input type="text" class="ant-input"><span class="ant-input-group-addon"><button type="button" class="ant-input-search-button">ok</button></span></span></span>`;
exports[`Input.Search should support custom button 1`] = `<span class="ant-input-group-wrapper ant-input-search ant-input-search-enter-button"><span class="ant-input-wrapper ant-input-group"><input type="text" class="ant-input"><span class="ant-input-group-addon"><button type="button" class="">ok</button></span></span></span>`;

View File

@ -10,17 +10,18 @@ exports[`renders ./components/input/demo/addon.md correctly 1`] = `
</div>
`;
exports[`renders ./components/input/demo/allowClear.md correctly 1`] = `<span class="ant-input-affix-wrapper"><input placeholder="input with clear icon" type="text" class="ant-input"><span class="ant-input-suffix"></span></span>`;
exports[`renders ./components/input/demo/allowClear.md correctly 1`] = `<div><span class="ant-input-affix-wrapper"><input placeholder="input with clear icon" type="text" class="ant-input"><span class="ant-input-suffix"></span></span> <br> <br> <span class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"><textarea placeholder="textarea with clear icon" class="ant-input"></textarea></span></div>`;
exports[`renders ./components/input/demo/autosize-textarea.md correctly 1`] = `
<div><textarea placeholder="Autosize height based on content lines" class="ant-input"></textarea>
<div style="margin: 24px 0px;"></div> <textarea placeholder="Autosize height with minimum and maximum number of lines" class="ant-input"></textarea></div>
<div><textarea placeholder="Autosize height based on content lines" class="ant-input" style="height: 0px; min-height: -9007199254740991px; max-height: 9007199254740991px; overflow: hidden;"></textarea>
<div style="margin: 24px 0px;"></div> <textarea placeholder="Autosize height with minimum and maximum number of lines" class="ant-input" style="height: -24px; min-height: -8px; max-height: -24px; overflow: hidden;"></textarea>
<div style="margin: 24px 0px;"></div> <textarea placeholder="Controlled autosize" class="ant-input" style="height: -20px; min-height: -12px; max-height: -20px; overflow: hidden;"></textarea></div>
`;
exports[`renders ./components/input/demo/basic.md correctly 1`] = `<input placeholder="Basic usage" type="text" class="ant-input">`;
exports[`renders ./components/input/demo/group.md correctly 1`] = `
<div><span class="ant-input-group ant-input-group-lg"><div class="ant-row" style="margin-left: -4px; margin-right: -4px;"><div class="ant-col-5" style="padding-left: 4px; padding-right: 4px;"><input type="text" class="ant-input"></div> <div class="ant-col-8" style="padding-left: 4px; padding-right: 4px;"><input type="text" class="ant-input"></div></div></span> <br> <span class="ant-input-group ant-input-group-compact"><input type="text" class="ant-input" style="width: 20%;"><input type="text" class="ant-input" style="width: 30%;"></span> <br> <span class="ant-input-group ant-input-group-compact"><div tabindex="0" class="ant-select ant-select-enabled"><div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single"><div class="ant-select-selection__rendered"><div title="Zhejiang" class="ant-select-selection-selected-value" style="display: block; opacity: 1;">Zhejiang</div></div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></div>
<div><span class="ant-input-group ant-input-group-lg"><div class="ant-row" style="margin-left: -4px; margin-right: -4px;"><div class="ant-col ant-col-5" style="padding-left: 4px; padding-right: 4px;"><input type="text" class="ant-input"></div> <div class="ant-col ant-col-8" style="padding-left: 4px; padding-right: 4px;"><input type="text" class="ant-input"></div></div></span> <br> <span class="ant-input-group ant-input-group-compact"><input type="text" class="ant-input" style="width: 20%;"><input type="text" class="ant-input" style="width: 30%;"></span> <br> <span class="ant-input-group ant-input-group-compact"><div tabindex="0" class="ant-select ant-select-enabled"><div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single"><div class="ant-select-selection__rendered"><div title="Zhejiang" class="ant-select-selection-selected-value" style="display: block; opacity: 1;">Zhejiang</div></div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></div>
</div><input type="text" class="ant-input" style="width: 50%;"></span> <br> <span class="ant-input-group ant-input-group-compact"><div tabindex="0" class="ant-select ant-select-enabled"><div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single"><div class="ant-select-selection__rendered"><div title="Option1" class="ant-select-selection-selected-value" style="display: block; opacity: 1;">Option1</div></div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></div>
</div><input type="text" class="ant-input" style="width: 50%;"></span> <br> <span class="ant-input-group ant-input-group-compact"><input type="text" class="ant-input" style="width: 50%;"><span class="ant-calendar-picker" style="width: 50%;"><div class=""><input readonly="true" placeholder="Select date" class="ant-calendar-picker-input ant-input"><i aria-label="icon: calendar" class="ant-calendar-picker-icon anticon anticon-calendar"><svg viewBox="64 64 896 896" data-icon="calendar" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"></path></svg></i></div></span></span> <br> <span class="ant-input-group ant-input-group-compact"><div tabindex="0" class="ant-select ant-select-enabled"><div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single"><div class="ant-select-selection__rendered"><div title="Option1-1" class="ant-select-selection-selected-value" style="display: block; opacity: 1;">Option1-1</div></div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></div>
</div>
@ -32,7 +33,7 @@ exports[`renders ./components/input/demo/group.md correctly 1`] = `
</div>
</div>
</span> <br> <span class="ant-input-group ant-input-group-compact"><div tabindex="0" class="ant-select ant-select-enabled"><div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single"><div class="ant-select-selection__rendered"><div title="Between" class="ant-select-selection-selected-value" style="display: block; opacity: 1;">Between</div></div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></div>
</div><input placeholder="Minimum" type="text" class="ant-input" style="width: 100px; text-align: center;"><input placeholder="~" type="text" disabled="disabled" class="ant-input ant-input-disabled" style="width: 30px; border-left: 0; pointer-events: none; background-color: rgb(255, 255, 255);"><input placeholder="Maximum" type="text" class="ant-input" style="width: 100px; text-align: center; border-left: 0;"></span> <br> <span class="ant-input-group ant-input-group-compact"><div tabindex="0" class="ant-select ant-select-enabled"><div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single"><div class="ant-select-selection__rendered"><div title="Sign Up" class="ant-select-selection-selected-value" style="display: block; opacity: 1;">Sign Up</div></div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></div>
</div><input placeholder="Minimum" type="text" class="ant-input" style="width: 100px; text-align: center;"><input placeholder="~" type="text" disabled="disabled" class="ant-input ant-input-disabled" style="width: 30px; pointer-events: none; background-color: rgb(255, 255, 255); border-left: 0;"><input placeholder="Maximum" type="text" class="ant-input" style="width: 100px; text-align: center; border-left: 0;"></span> <br> <span class="ant-input-group ant-input-group-compact"><div tabindex="0" class="ant-select ant-select-enabled"><div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single"><div class="ant-select-selection__rendered"><div title="Sign Up" class="ant-select-selection-selected-value" style="display: block; opacity: 1;">Sign Up</div></div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></div>
</div>
<div tabindex="0" class="ant-select ant-select-combobox ant-select-enabled ant-select-show-search ant-select-auto-complete" style="width: 200px;">
<div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single">
@ -53,14 +54,16 @@ exports[`renders ./components/input/demo/group.md correctly 1`] = `
exports[`renders ./components/input/demo/password-input.md correctly 1`] = `<span class="ant-input-affix-wrapper ant-input-password"><input placeholder="input password" type="password" class="ant-input"><span class="ant-input-suffix"><i aria-label="icon: eye-invisible" tabindex="-1" class="ant-input-password-icon anticon anticon-eye-invisible"><svg viewBox="64 64 896 896" data-icon="eye-invisible" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 0 0 0-51.5zm-63.57-320.64L836 122.88a8 8 0 0 0-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 0 0 0 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 0 0 0 11.31L155.17 889a8 8 0 0 0 11.31 0l712.15-712.12a8 8 0 0 0 0-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 0 0-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 0 1 146.2-106.69L401.31 546.2A112 112 0 0 1 396 512z"></path><path d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 0 0 227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 0 1-112 112z"></path></svg></i></span></span>`;
exports[`renders ./components/input/demo/presuffix.md correctly 1`] = `<div class="components-input-demo-presuffix"><span class="ant-input-affix-wrapper"><span class="ant-input-prefix"><i slot="prefix" aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span><input placeholder="Basic usage" type="text" class="ant-input"><span class="ant-input-suffix"><i aria-label="icon: info-circle" class="anticon anticon-info-circle" style="color: rgba(0, 0, 0, 0.45);"><svg viewBox="64 64 896 896" data-icon="info-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path><path d="M464 336a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm72 112h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V456c0-4.4-3.6-8-8-8z"></path></svg></i></span></span></div>`;
exports[`renders ./components/input/demo/presuffix.md correctly 1`] = `<div class="components-input-demo-presuffix"><span class="ant-input-affix-wrapper"><span class="ant-input-prefix"><i slot="prefix" aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span><input placeholder="Basic usage" type="text" class="ant-input"><span class="ant-input-suffix"><i aria-label="icon: info-circle" class="anticon anticon-info-circle" style="color: rgba(0, 0, 0, 0.45);"><svg viewBox="64 64 896 896" data-icon="info-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path><path d="M464 336a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm72 112h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V456c0-4.4-3.6-8-8-8z"></path></svg></i></span></span> <br> <br> <span class="ant-input-affix-wrapper"><span class="ant-input-prefix">¥</span><input type="text" class="ant-input"><span class="ant-input-suffix">RMB</span></span></div>`;
exports[`renders ./components/input/demo/search-input.md correctly 1`] = `<div><span class="ant-input-affix-wrapper ant-input-search" style="width: 200px;"><input placeholder="input search text" type="text" class="ant-input"><span class="ant-input-suffix"><i aria-label="icon: search" tabindex="-1" class="ant-input-search-icon anticon anticon-search"><svg viewBox="64 64 896 896" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></i></span></span> <br><br> <span class="ant-input-group-wrapper ant-input-search ant-input-search-enter-button"><span class="ant-input-wrapper ant-input-group"><input placeholder="input search text" type="text" class="ant-input"><span class="ant-input-group-addon"><button type="button" class="ant-btn ant-btn-primary ant-input-search-button"><i aria-label="icon: search" class="anticon anticon-search"><svg viewBox="64 64 896 896" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></i></button></span></span></span> <br><br> <span class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-enter-button ant-input-search-large"><span class="ant-input-wrapper ant-input-group"><input placeholder="input search text" type="text" size="large" class="ant-input ant-input-lg"><span class="ant-input-group-addon"><button type="button" class="ant-btn ant-btn-primary ant-btn-lg ant-input-search-button"><span>Search</span></button></span></span></span> <br><br> <span class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-enter-button ant-input-search-large"><span class="ant-input-wrapper ant-input-group"><input placeholder="input search text" type="text" size="large" class="ant-input ant-input-lg"><span class="ant-input-group-addon"><button type="button" class="ant-btn ant-btn-lg ant-input-search-button"><span>Custom</span></button></span></span></span></div>`;
exports[`renders ./components/input/demo/search-input.md correctly 1`] = `<div><span class="ant-input-affix-wrapper ant-input-search" style="width: 200px;"><input placeholder="input search text" type="text" class="ant-input"><span class="ant-input-suffix"><i aria-label="icon: search" tabindex="-1" class="ant-input-search-icon anticon anticon-search"><svg viewBox="64 64 896 896" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></i></span></span> <br><br> <span class="ant-input-group-wrapper ant-input-search ant-input-search-enter-button"><span class="ant-input-wrapper ant-input-group"><input placeholder="input search text" type="text" class="ant-input"><span class="ant-input-group-addon"><button type="button" class="ant-btn ant-btn-primary ant-input-search-button"><i aria-label="icon: search" class="anticon anticon-search"><svg viewBox="64 64 896 896" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></i></button></span></span></span> <br><br> <span class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-enter-button ant-input-search-large"><span class="ant-input-wrapper ant-input-group"><input placeholder="input search text" type="text" class="ant-input ant-input-lg"><span class="ant-input-group-addon"><button type="button" class="ant-btn ant-btn-primary ant-btn-lg ant-input-search-button"><span>Search</span></button></span></span></span> <br><br> <span class="ant-input-affix-wrapper ant-input-affix-wrapper-lg ant-input-search"><input placeholder="input search text" type="text" class="ant-input ant-input-lg"><span class="ant-input-suffix"><i aria-label="icon: search" tabindex="-1" class="ant-input-search-icon anticon anticon-search"><svg viewBox="64 64 896 896" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></i></span></span></div>`;
exports[`renders ./components/input/demo/size.md correctly 1`] = `<div class="components-input-demo-size"><input placeholder="large size" type="text" size="large" class="ant-input ant-input-lg"> <input placeholder="default size" type="text" class="ant-input"> <input placeholder="small size" type="text" size="small" class="ant-input ant-input-sm"></div>`;
exports[`renders ./components/input/demo/search-input-loading.md correctly 1`] = `<div><span class="ant-input-affix-wrapper ant-input-search"><input placeholder="input search loading deault" type="text" class="ant-input"><span class="ant-input-suffix"><i aria-label="icon: loading" class="ant-input-search-icon anticon anticon-loading"><svg viewBox="0 0 1024 1024" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="anticon-spin"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg></i></span></span> <br> <br> <span class="ant-input-group-wrapper ant-input-search ant-input-search-enter-button"><span class="ant-input-wrapper ant-input-group"><input placeholder="input search loading with enterButton" type="text" class="ant-input"><span class="ant-input-group-addon"><button type="button" class="ant-btn ant-btn-primary ant-input-search-button"><i aria-label="icon: loading" class="anticon anticon-loading"><svg viewBox="0 0 1024 1024" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="anticon-spin"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg></i></button></span></span></span></div>`;
exports[`renders ./components/input/demo/textarea.md correctly 1`] = `<textarea placeholder="Basic usage" rows="4" class="ant-input"></textarea>`;
exports[`renders ./components/input/demo/size.md correctly 1`] = `<div class="components-input-demo-size"><input placeholder="large size" type="text" class="ant-input ant-input-lg"> <input placeholder="default size" type="text" class="ant-input"> <input placeholder="small size" type="text" class="ant-input ant-input-sm"></div>`;
exports[`renders ./components/input/demo/textarea-resize.md correctly 1`] = `<div><button type="button" class="ant-btn" style="margin-bottom: 16px;"><span>Auto Resize: false</span></button> <textarea rows="4" class="ant-input"></textarea></div>`;
exports[`renders ./components/input/demo/textarea.md correctly 1`] = `<textarea placeholder="Basic usage" class="ant-input" rows="4"></textarea>`;
exports[`renders ./components/input/demo/textarea-resize.md correctly 1`] = `<div><button type="button" class="ant-btn" style="margin-bottom: 16px;"><span>Auto Resize: false</span></button> <textarea class="ant-input" rows="4"></textarea></div>`;
exports[`renders ./components/input/demo/tooltip.md correctly 1`] = `<input placeholder="Input a number" type="text" maxlength="25" class="ant-input" style="width: 120px;">`;

View File

@ -36,11 +36,11 @@ focusTest(TextArea);
describe('TextArea', () => {
it('should auto calculate height according to content length', async () => {
const wrapper = mount(TextArea, {
propsData: { value: '', readOnly: true, autosize: true },
propsData: { value: '', readOnly: true, autoSize: true },
sync: false,
});
const mockFunc = jest.spyOn(wrapper.vm, 'resizeTextarea');
const mockFunc = jest.spyOn(wrapper.vm.$refs.resizableTextArea, 'resizeTextarea');
await asyncExpect(() => {
wrapper.setProps({ value: '1111\n2222\n3333' });
});

View File

@ -116,10 +116,10 @@ export default function calculateNodeHeight(
if (boxSizing === 'border-box') {
// border-box: add border, since height = content + padding + border
height = height + borderSize;
height += borderSize;
} else if (boxSizing === 'content-box') {
// remove padding, since height = content
height = height - paddingSize;
height -= paddingSize;
}
if (minRows !== null || maxRows !== null) {
@ -142,11 +142,11 @@ export default function calculateNodeHeight(
height = Math.min(maxHeight, height);
}
}
// Remove scroll bar flash when autosize without maxRows
// donot remove in vue
if (!maxRows) {
overflowY = 'hidden';
}
// // Remove scroll bar flash when autosize without maxRows
// // donot remove in vue
// if (!maxRows) {
// overflowY = 'hidden';
// }
return {
height: `${height}px`,
minHeight: `${minHeight}px`,

View File

@ -10,7 +10,12 @@ Input type of password.
```tpl
<template>
<div>
<a-input placeholder="input with clear icon" allowClear @change="onChange" />
<br />
<br />
<a-textarea placeholder="textarea with clear icon" allowClear @change="onChange" />
</div>
</template>
<script>
export default {

View File

@ -1,23 +1,39 @@
<cn>
#### 适应文本高度的文本域
`autosize` 属性适用于 `textarea` 节点,并且只有高度会自动变化。另外 `autosize` 可以设定为一个对象,指定最小行数和最大行数。
属性适用于 `textarea` 节点,并且只有高度会自动变化。另外 `autoSize` 可以设定为一个对象,指定最小行数和最大行数。
> `1.5.0``autosize` 被废弃,请使用 `autoSize`
</cn>
<us>
#### Autosizing the height to fit the content
`autosize` prop for a `textarea` type of `Input` makes the height to automatically adjust based on the content.
An options object can be provided to `autosize` to specify the minimum and maximum number of lines the textarea will automatically adjust.
`autoSize` prop for a `textarea` type of `Input` makes the height to automatically adjust based on the content. An options object can be provided to `autoSize` to specify the minimum and maximum number of lines the textarea will automatically adjust.
> `autosize` is deprecated after `1.5.0`, please use `autoSize`.
</us>
```tpl
<template>
<div>
<a-textarea placeholder="Autosize height based on content lines" autosize />
<a-textarea placeholder="Autosize height based on content lines" autoSize />
<div style="margin: 24px 0" />
<a-textarea
placeholder="Autosize height with minimum and maximum number of lines"
:autosize="{ minRows: 2, maxRows: 6 }"
:autoSize="{ minRows: 2, maxRows: 6 }"
/>
<div style="margin: 24px 0" />
<a-textarea
v-model="value"
placeholder="Controlled autosize"
:autoSize="{ minRows: 3, maxRows: 5 }"
/>
</div>
</template>
<script>
export default {
data() {
return {
value: ''
}
}
}
</script>
```

View File

@ -3,6 +3,7 @@ import Basic from './basic';
import AutosizeTextarea from './autosize-textarea';
import Presuffix from './presuffix';
import SearchInput from './search-input';
import SearchInputLoading from './search-input-loading';
import Size from './size';
import Group from './group';
import TextArea from './textarea';
@ -40,6 +41,7 @@ export default {
<AutosizeTextarea />
<Presuffix />
<SearchInput />
<SearchInputLoading />
<Size />
<Group />
<TextArea />

View File

@ -17,6 +17,9 @@ Add prefix or suffix icons inside input.
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
</a-tooltip>
</a-input>
<br />
<br />
<a-input prefix="¥" suffix="RMB" />
</div>
</template>
@ -35,18 +38,4 @@ Add prefix or suffix icons inside input.
},
};
</script>
<style scoped>
.components-input-demo-presuffix .anticon-close-circle {
cursor: pointer;
color: #ccc;
transition: color 0.3s;
font-size: 12px;
}
.components-input-demo-presuffix .anticon-close-circle:hover {
color: #999;
}
.components-input-demo-presuffix .anticon-close-circle:active {
color: #666;
}
</style>
```

View File

@ -0,0 +1,20 @@
<cn>
#### 搜索框 loading
用于 `onSearch` 的时候展示 `loading`
</cn>
<us>
#### Search box with loading
Search loading when onSearch.
</us>
```tpl
<template>
<div>
<a-input-search placeholder="input search loading deault" loading />
<br />
<br />
<a-input-search placeholder="input search loading with enterButton" loading enterButton />
</div>
</template>
```

View File

@ -14,7 +14,7 @@ For multi-line input.
<a-button style="margin-bottom: 16px" @click="() => this.autoResize = !autoResize">
Auto Resize: {{String(autoResize)}}
</a-button>
<a-textarea :rows="4" :autosize="autoResize" :defaultValue="defaultValue" />
<a-textarea :rows="4" :autoSize="autoResize" :defaultValue="defaultValue" />
</div>
</template>
<script>

View File

@ -54,7 +54,7 @@ You can use the Input in conjunction with [Tooltip](/components/tooltip/) compon
formatNumber,
onChange(e) {
const { value } = e.target;
const reg = /^-?(0|[1-9][0-9]*)(\.[0-9]*)?$/;
const reg = /^-?[0-9]*(\.[0-9]*)?$/;
if ((!isNaN(value) && reg.test(value)) || value === '' || value === '-') {
this.value = value;
}
@ -62,6 +62,7 @@ You can use the Input in conjunction with [Tooltip](/components/tooltip/) compon
// '.' at the end or only '-' in the input box.
onBlur() {
const { value, onChange } = this;
let valueTemp = value;
if (value.charAt(value.length - 1) === '.' || value === '-') {
onChange({ value: value.slice(0, -1) });
}

View File

@ -2,19 +2,20 @@
### Input
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| addonAfter | The label text displayed after (on the right side of) the input field. | string\|slot | |
| addonBefore | The label text displayed before (on the left side of) the input field. | string\|slot | |
| defaultValue | The initial input content | string | |
| disabled | Whether the input is disabled. | boolean | false |
| id | The ID for input | string | |
| prefix | The prefix icon for the Input. | string\|slot | |
| size | The size of the input box. Note: in the context of a form, the `large` size is used. Available: `large` `default` `small` | string | `default` |
| suffix | The suffix icon for the Input. | string\|slot | |
| type | The type of input, see: [MDN](https://developer.mozilla.org/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types)(use `Input.TextArea` instead of `type="textarea"`) | string | `text` |
| value(v-model) | The input content value | string | |
| allowClear | allow to remove input content with clear icon | boolean | |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| addonAfter | The label text displayed after (on the right side of) the input field. | string\|slot | | |
| addonBefore | The label text displayed before (on the left side of) the input field. | string\|slot | | |
| defaultValue | The initial input content | string | | |
| disabled | Whether the input is disabled. | boolean | false | |
| id | The ID for input | string | | |
| maxLength | max length | number | | 1.5.0 |
| prefix | The prefix icon for the Input. | string\|slot | | |
| size | The size of the input box. Note: in the context of a form, the `large` size is used. Available: `large` `default` `small` | string | `default` | |
| suffix | The suffix icon for the Input. | string\|slot | | |
| type | The type of input, see: [MDN](https://developer.mozilla.org/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types)(use `Input.TextArea` instead of `type="textarea"`) | string | `text` | |
| value(v-model) | The input content value | string | | |
| allowClear | allow to remove input content with clear icon | boolean | | |
### Input Events
@ -27,11 +28,12 @@
### Input.TextArea
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| autosize | Height autosize feature, can be set to `true|false` or an object `{ minRows: 2, maxRows: 6 }` | boolean\|object | false |
| defaultValue | The initial input content | string | |
| value(v-model) | The input content value | string | |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| autosize | Height autosize feature, can be set to `true|false` or an object `{ minRows: 2, maxRows: 6 }` | boolean\|object | false | |
| defaultValue | The initial input content | string | | |
| value(v-model) | The input content value | string | | |
| allowClear | allow to remove input content with clear icon | boolean | | 1.5.0 |
### Input.TextArea Events
@ -49,9 +51,10 @@ The rest of the props of `Input.TextArea` are the same as the original [textarea
### Input.Search Events
| Events Name | Description | Arguments |
| --- | --- | --- |
| search | The callback function that is triggered when you click on the search-icon or press Enter key. | function(value, event) |
| Events Name | Description | Arguments | Version |
| --- | --- | --- | --- |
| search | The callback function that is triggered when you click on the search-icon or press Enter key. | function(value, event) | |
| loading | Search box with loading. | boolean | | |
Supports all props of `Input`.

View File

@ -2,19 +2,20 @@
### Input
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| addonAfter | 带标签的 input设置后置标签 | string\|slot | |
| addonBefore | 带标签的 input设置前置标签 | string\|slot | |
| defaultValue | 输入框默认内容 | string | |
| disabled | 是否禁用状态,默认为 false | boolean | false |
| id | 输入框的 id | string | |
| prefix | 带有前缀图标的 input | string\|slot | |
| size | 控件大小。注:标准表单内的输入框大小限制为 `large`。可选 `large` `default` `small` | string | `default` |
| suffix | 带有后缀图标的 input | string\|slot | |
| type | 声明 input 类型,同原生 input 标签的 type 属性,见:[MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input#属性)(请直接使用 `Input.TextArea` 代替 `type="textarea"`)。 | string | `text` |
| value(v-model) | 输入框内容 | string | |
| allowClear | 可以点击清除图标删除内容 | boolean | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| addonAfter | 带标签的 input设置后置标签 | string\|slot | | |
| addonBefore | 带标签的 input设置前置标签 | string\|slot | | |
| defaultValue | 输入框默认内容 | string | | |
| disabled | 是否禁用状态,默认为 false | boolean | false | |
| id | 输入框的 id | string | | |
| maxLength | 最大长度 | number | | 1.5.0 |
| prefix | 带有前缀图标的 input | string\|slot | | |
| size | 控件大小。注:标准表单内的输入框大小限制为 `large`。可选 `large` `default` `small` | string | `default` | |
| suffix | 带有后缀图标的 input | string\|slot | | |
| type | 声明 input 类型,同原生 input 标签的 type 属性,见:[MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input#属性)(请直接使用 `Input.TextArea` 代替 `type="textarea"`)。 | string | `text` | |
| value(v-model) | 输入框内容 | string | | |
| allowClear | 可以点击清除图标删除内容 | boolean | | |
### Input 事件
@ -27,11 +28,12 @@
### Input.TextArea
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| autosize | 自适应内容高度,可设置为 `true|false` 或对象:`{ minRows: 2, maxRows: 6 }` | boolean\|object | false |
| defaultValue | 输入框默认内容 | string | |
| value(v-model) | 输入框内容 | string | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| autosize | 自适应内容高度,可设置为 `true|false` 或对象:`{ minRows: 2, maxRows: 6 }` | boolean\|object | false | |
| defaultValue | 输入框默认内容 | string | | |
| value(v-model) | 输入框内容 | string | | |
| allowClear | 可以点击清除图标删除内容 | boolean | | 1.5.0 |
### Input.TextArea 事件
@ -43,9 +45,10 @@
#### Input.Search
| 参数 | 说明 | 类型 | 默认值 |
| ----------- | ------------------------------------------------------- | ------------- | ------ |
| enterButton | 是否有确认按钮,可设为按钮文字。该属性会与 addon 冲突。 | boolean\|slot | false |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| enterButton | 是否有确认按钮,可设为按钮文字。该属性会与 addon 冲突。 | boolean\|slot | false | |
| loading | 搜索 loading | boolean | | 1.5.0 |
### Input.Search 事件

View File

@ -2,23 +2,16 @@ import PropTypes from '../_util/vue-types';
export default {
prefixCls: PropTypes.string,
inputPrefixCls: PropTypes.string,
defaultValue: [String, Number],
value: [String, Number],
defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
placeholder: [String, Number],
type: {
default: 'text',
type: String,
},
name: String,
size: {
validator(value) {
return ['small', 'large', 'default'].includes(value);
},
},
disabled: {
default: false,
type: Boolean,
},
size: PropTypes.oneOf(['small', 'large', 'default']),
disabled: PropTypes.bool,
readOnly: Boolean,
addonBefore: PropTypes.any,
addonAfter: PropTypes.any,
@ -37,4 +30,6 @@ export default {
default: true,
type: Boolean,
},
maxLength: PropTypes.number,
loading: PropTypes.bool,
};

View File

@ -0,0 +1,85 @@
// based on rc-resize-observer 0.1.3
import ResizeObserver from 'resize-observer-polyfill';
// Still need to be compatible with React 15, we use class component here
const VueResizeObserver = {
name: 'ResizeObserver',
props: {
disabled: Boolean,
},
data() {
this.currentElement = null;
this.resizeObserver = null;
return {
width: 0,
height: 0,
};
},
mounted() {
this.onComponentUpdated();
},
updated() {
this.onComponentUpdated();
},
beforeDestroy() {
this.destroyObserver();
},
methods: {
onComponentUpdated() {
const { disabled } = this.$props;
// Unregister if disabled
if (disabled) {
this.destroyObserver();
return;
}
// Unregister if element changed
const element = this.$el;
const elementChanged = element !== this.currentElement;
if (elementChanged) {
this.destroyObserver();
this.currentElement = element;
}
if (!this.resizeObserver && element) {
this.resizeObserver = new ResizeObserver(this.onResize);
this.resizeObserver.observe(element);
}
},
onResize(entries) {
const { target } = entries[0];
const { width, height } = target.getBoundingClientRect();
/**
* Resize observer trigger when content size changed.
* In most case we just care about element size,
* let's use `boundary` instead of `contentRect` here to avoid shaking.
*/
const fixedWidth = Math.floor(width);
const fixedHeight = Math.floor(height);
if (this.width !== fixedWidth || this.height !== fixedHeight) {
const size = { width: fixedWidth, height: fixedHeight };
this.width = fixedWidth;
this.fixedHeight = fixedHeight;
this.$emit('resize', size);
}
},
destroyObserver() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
}
},
},
render() {
return this.$slots.default[0];
},
};
export default VueResizeObserver;

View File

@ -189,6 +189,7 @@
"dom-scroll-into-view": "^2.0.0",
"enquire.js": "^2.1.6",
"intersperse": "^1.0.0",
"is-mobile": "^2.2.1",
"is-negative-zero": "^2.0.0",
"ismobilejs": "^1.0.0",
"json2mq": "^0.2.0",

View File

@ -11,6 +11,7 @@ export declare class TextArea extends AntdComponent {
* @type boolean | object
*/
autosize: boolean | { minRows: number; maxRows: number };
autoSize: boolean | { minRows: number; maxRows: number };
/**
* The initial input content