cr exxx to nxxx

pull/1225/head
tanjinzhou 2019-09-11 18:11:48 +08:00
parent 4c58c52b21
commit f94f50f326
22 changed files with 153 additions and 122 deletions

View File

@ -92,12 +92,7 @@ exports[`renders ./components/empty/demo/config-provider.md correctly 1`] = `
<div class="ant-list ant-list-split">
<div class="ant-spin-nested-loading">
<div class="ant-spin-container">
<div class="ant-list-empty-text">
<div class="ant-empty ant-empty-normal">
<div class="ant-empty-image"><img alt="No Data" src=""></div>
<p class="ant-empty-description">No Data</p>
</div>
</div>
<div class="ant-list-empty-text"></div>
</div>
</div>
</div>

View File

@ -1,4 +1,3 @@
import intersperse from 'intersperse';
import PropTypes from '../_util/vue-types';
import classNames from 'classnames';
import find from 'lodash/find';
@ -21,6 +20,10 @@ import Icon from '../icon';
import { ConfigConsumerProps } from '../config-provider';
function noop() {}
function intersperseSpace(list) {
return list.reduce((current, item) => [...current, ' ', item], []).slice(1);
}
export const FormItemProps = {
id: PropTypes.string,
prefixCls: PropTypes.string,
@ -59,7 +62,6 @@ export default {
mixins: [BaseMixin],
props: initDefaultProps(FormItemProps, {
hasFeedback: false,
prefixCls: 'ant-form',
colon: true,
}),
inject: {
@ -83,8 +85,10 @@ export default {
this.collectFormItemContext(this.$vnode.context, 'delete');
},
mounted() {
const { help, validateStatus } = this.$props;
warning(
this.getControls(this.slotDefault, true).length <= 1,
this.getControls(this.slotDefault, true).length <= 1 ||
(help !== undefined || validateStatus !== undefined),
'`Form.Item` cannot generate `validateStatus` and `help` automatically, ' +
'while there are more than one `getFieldDecorator` in it.',
);
@ -118,13 +122,16 @@ export default {
if (help === undefined && onlyControl) {
const errors = this.getField().errors;
if (errors) {
return intersperse(
return intersperseSpace(
errors.map((e, index) => {
return isValidElement(e.message)
? cloneElement(e.message, { key: index })
: e.message;
let node = null;
if (isValidElement(e)) {
node = e;
} else if (isValidElement(e.message)) {
node = e.message;
}
return node ? cloneElement(node, { key: index }) : e.message;
}),
' ',
);
} else {
return '';
@ -339,15 +346,15 @@ export default {
if (!id) {
return;
}
const controls = document.querySelectorAll(`[id="${id}"]`);
if (controls.length !== 1) {
const formItemNode = this.$el;
const control = formItemNode.querySelector(`[id="${id}"]`);
if (control) {
// Only prevent in default situation
// Avoid preventing event in `label={<a href="xx">link</a>}``
if (typeof label === 'string') {
e.preventDefault();
}
const control = this.$el.querySelector(`[id="${id}"]`);
if (control && control.focus) {
if (control.focus) {
control.focus();
}
}

View File

@ -3,6 +3,26 @@ import Form from '..';
import { asyncExpect } from '@/tests/utils';
describe('Form', () => {
// Mock of `querySelector`
const originQuerySelector = HTMLElement.prototype.querySelector;
HTMLElement.prototype.querySelector = function querySelector(str) {
const match = str.match(/^\[id=('|")(.*)('|")]$/);
const id = match && match[2];
// Use origin logic
if (id) {
const [input] = this.getElementsByTagName('input');
if (input && input.id === id) {
return input;
}
}
return originQuerySelector.call(this, str);
};
afterAll(() => {
HTMLElement.prototype.querySelector = originQuerySelector;
});
it('should remove duplicated user input colon', () => {
const wrapper = mount({
render() {

View File

@ -1,12 +1,11 @@
import PropTypes from '../_util/vue-types';
import { filterEmpty } from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider';
export default {
name: 'AInputGroup',
props: {
prefixCls: {
type: String,
},
prefixCls: PropTypes.string,
size: {
validator(value) {
return ['small', 'large', 'default'].includes(value);

View File

@ -38,12 +38,12 @@ export default {
data() {
const { value, defaultValue } = this.$props;
return {
stateValue: fixControlledValue(!hasProp(this, 'value') ? defaultValue : value),
stateValue: !hasProp(this, 'value') ? defaultValue : value,
};
},
watch: {
value(val) {
this.stateValue = fixControlledValue(val);
this.stateValue = val;
},
},
mounted() {
@ -95,6 +95,21 @@ export default {
if (!e.target.composing) {
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;
}
this.$emit('change', e);
this.$emit('input', e);
},
@ -129,7 +144,7 @@ export default {
let suffix = getComponentFromProp(this, 'suffix');
if (suffix || allowClear) {
return (
<span class={`${prefixCls}-suffix`}>
<span class={`${prefixCls}-suffix`} key="suffix">
{this.renderClearIcon(prefixCls)}
{suffix}
</span>
@ -179,14 +194,18 @@ export default {
return children;
}
let prefix = getComponentFromProp(this, 'prefix');
prefix = prefix ? <span class={`${prefixCls}-prefix`}>{prefix}</span> : null;
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}>
<span class={affixWrapperCls} key="affix">
{prefix}
{children}
{suffix}
@ -201,13 +220,14 @@ export default {
'addonAfter',
'prefix',
'suffix',
'allowClear',
'value',
'defaultValue',
]);
const { stateValue, getInputClassName, handleKeyDown, handleChange, $listeners } = this;
const inputProps = {
domProps: {
value: stateValue,
value: fixControlledValue(stateValue),
},
attrs: { ...otherProps, ...this.$attrs },
on: {
@ -218,6 +238,7 @@ export default {
},
class: getInputClassName(prefixCls),
ref: 'input',
key: 'ant-input',
};
if ($listeners['change.value']) {
inputProps.directives = [{ name: 'ant-input' }];

View File

@ -2,9 +2,6 @@ import classNames from 'classnames';
import Input from './Input';
import Icon from '../icon';
import inputProps from './inputProps';
import Button from '../button';
import { cloneElement } from '../_util/vnode';
import { getOptionProps, getComponentFromProp, isValidElement } from '../_util/props-util';
import PropTypes from '../_util/vue-types';
import BaseMixin from '../_util/BaseMixin';
@ -47,7 +44,7 @@ export default {
},
on: {
[iconTrigger]: this.onChange,
onMouseDown: e => {
mousedown: e => {
// Prevent focused state lost
// https://github.com/ant-design/ant-design/issues/15173
e.preventDefault();

View File

@ -17,14 +17,6 @@ export default {
},
props: {
...inputProps,
prefixCls: {
default: 'ant-input-search',
type: String,
},
inputPrefixCls: {
default: 'ant-input',
type: String,
},
enterButton: PropTypes.oneOfType([PropTypes.bool, PropTypes.string, PropTypes.object]),
},
inject: {
@ -52,13 +44,13 @@ export default {
);
if (suffix) {
let cloneSuffix = suffix;
if (isValidElement(cloneSuffix) && !cloneSuffix.key) {
cloneSuffix = cloneElement(cloneSuffix, {
key: 'originSuffix',
});
}
return [cloneSuffix, node];
// let cloneSuffix = suffix;
// if (isValidElement(cloneSuffix) && !cloneSuffix.key) {
// cloneSuffix = cloneElement(cloneSuffix, {
// key: 'originSuffix',
// });
// }
return [suffix, node];
}
return node;

View File

@ -10,7 +10,7 @@ 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" allowclear="true" class="ant-input"><span class="ant-input-suffix"></span></span>`;
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/autosize-textarea.md correctly 1`] = `
<div><textarea placeholder="Autosize height based on content lines" class="ant-input"></textarea>

View File

@ -27,7 +27,7 @@ You can use the Input in conjunction with [Tooltip](/components/tooltip/) compon
@change="onChange"
@blur="onBlur"
placeholder="Input a number"
maxLength="25"
:maxLength="25"
style="width: 120px"
/>
</a-tooltip>

View File

@ -68,21 +68,8 @@ Supports all props of `Input`.
<a-input />
</a-input-group>
````
#### Input.Password
#### Input.Password (Added in 1.14.0)
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| visibilityToggle | Whether show toggle button | boolean | true |
## FAQ
### Why Input lose focus when change `prefix/suffix`
When Input dynamic add or remove `prefix/suffix` will make Vue recreate the dom structure and new input will be not focused.
You can set an empty `<span />` element to keep the dom structure:
```jsx
const suffix = condition ? <Icon type="smile" /> : <span />;
<Input suffix={suffix} />
```

View File

@ -69,20 +69,7 @@
````
#### Input.Password
#### Input.Password (1.14.0 中新增)
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| visibilityToggle | 是否显示切换按钮 | boolean | true |
## FAQ
### 为什么我动态改变 `prefix/suffix`Input 会失去焦点?
当 Input 动态添加或者删除 `prefix/suffix`Vue 会重新创建 DOM 结构而新的 input 是没有焦点的。
你可以预设一个空的 `<span />` 来保持 DOM 结构不变:
```jsx
const suffix = condition ? <Icon type="smile" /> : <span />;
<Input suffix={suffix} />
```

View File

@ -1,8 +1,7 @@
import PropTypes from '../_util/vue-types';
export default {
prefixCls: {
type: String,
},
prefixCls: PropTypes.string,
inputPrefixCls: PropTypes.string,
defaultValue: [String, Number],
value: [String, Number],
placeholder: [String, Number],

View File

@ -10,13 +10,13 @@
| grid | The grid type of list. You can set grid to something like {gutter: 16, column: 4} | object | - |
| header | List header renderer | string\|slot | - |
| itemLayout | The layout of list, default is `horizontal`, If a vertical list is desired, set the itemLayout property to `vertical` | string | - |
| rowKey | Item's unique key, could be a string or function that returns a string | string\|Function(record):string | `key` |
| loading | Shows a loading indicator while the contents of the list are being fetched | boolean\|[object](https://vue.ant.design/components/spin/#API) | false |
| loadMore | Shows a load more content | string\|slot | - |
| locale | i18n text including empty text | object | emptyText: 'No Data' <br> |
| pagination | Pagination [config](https://vue.ant.design/components/pagination/#API), hide it by setting it to false | boolean \| object | false |
| split | Toggles rendering of the split under the list item | boolean | true |
| renderItem | Custom item renderer, slot="renderItem" and slot-scope="item, index" | (item, index) => vNode | | - |
| rowKey | Specify the key that will be used for uniquely identify each element | item => string\|number | |
### pagination

View File

@ -224,7 +224,7 @@ const List = {
childrenContent = grid ? <Row gutter={grid.gutter}>{childrenList}</Row> : childrenList;
} else if (!children.length && !isLoading) {
const renderEmpty =
(this.configProvider.renderEmpty && this.configProvider.renderEmpty()) ||
(this.configProvider.renderEmpty && this.configProvider.renderEmpty) ||
ConfigConsumerProps.renderEmpty;
childrenContent = this.renderEmpty(prefixCls, renderEmpty);
}

View File

@ -3,7 +3,6 @@ import Icon from '../icon';
import Dialog from './Modal';
import ActionButton from './ActionButton';
import { getConfirmLocale } from './locale';
import { hasProp } from '../_util/props-util';
import warning from '../_util/warning';
export default {
@ -92,7 +91,7 @@ export default {
>
<div class={`${contentPrefixCls}-body-wrapper`}>
<div class={`${contentPrefixCls}-body`}>
<Icon type={iconType} />
{iconNode}
<span class={`${contentPrefixCls}-title`}>
{typeof props.title === 'function' ? props.title(h) : props.title}
</span>

View File

@ -20,7 +20,7 @@ import Button from '../../button'
export default {
methods: {
showConfirm() {
const _self = this
const self = this
for (let i = 0; i < 3; i += 1) {
setTimeout(() => {
this.$confirm({
@ -32,7 +32,7 @@ export default {
},
cancelText: 'Click to destroy all',
onCancel() {
_self.destroyAll()
self.destroyAll()
},
});
}, i * 500);

View File

@ -57,7 +57,8 @@ The properties of the object are follows:
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | `false` |
| class | class of container | string | - |
| content | Content | string\|vNode \|function(h) | - |
| iconType | Icon `type` of the Icon component | string | `question-circle` |
| icon | custom icon (`Added in 1.14.0`) | string\|()=>VNode | `<Icon type="question-circle">` |
| iconType | Icon `type` of the Icon component (deperated after `1.14.0`) | string | `question-circle` |
| keyboard | Whether support press esc to close | Boolean | true |
| mask | Whether show mask or not. | Boolean | true |
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | Boolean | `false` |

View File

@ -56,7 +56,9 @@
| closable | 是否显示右上角的关闭按钮 | boolean | `false` |
| class | 容器类名 | string | - |
| content | 内容 | string \|vNode \|function(h) | 无 |
| iconType | 图标 Icon 类型 | string | question-circle |
| icon | 自定义图标1.14.0 新增) | string\|()=>VNode | `<Icon type="question-circle">` |
| iconType | 图标类型1.14.0 后废弃,请使用 `icon` | string | `question-circle` |
| mask | 是否展示遮罩 | Boolean | true |
| maskClosable | 点击蒙层是否允许关闭 | Boolean | `false` |
| keyboard | 是否支持键盘esc关闭 | boolean | true |
| okText | 确认按钮文字 | string | 确定 |

78
types/form/form.d.ts vendored
View File

@ -16,34 +16,36 @@ export interface Field {
export interface FieldValue {
[fieldName: string]: any;
}
/** dom-scroll-into-view 组件配置参数 */
export type DomScrollIntoViewConfig = {
/** 是否和左边界对齐 */
alignWithLeft?: boolean;
/** 是否和上边界对齐 */
alignWithTop?: boolean;
/** 顶部偏移量 */
offsetTop?: number;
/** 左侧偏移量 */
offsetLeft?: number;
/** 底部偏移量 */
offsetBottom?: number;
/** 右侧偏移量 */
offsetRight?: number;
/** 是否允许容器水平滚动 */
allowHorizontalScroll?: boolean;
/** 当内容可见时是否允许滚动容器 */
onlyScrollIfNeeded?: boolean;
};
export interface ValidateFieldOptions {
/**
* If true, every field will stop validation at first failed rule
* @default false
* @type boolean
*/
export type ValidateFieldsOptions = {
/** 所有表单域是否在第一个校验规则失败后停止继续校验 */
first?: boolean;
/**
* Those fields will stop validation at first failed rule
* @type string[]
*/
/** 指定哪些表单域在第一个校验规则失败后停止继续校验 */
firstFields?: string[];
/**
* Should validate validated field again when validateTrigger is been triggered again
* @default false
* @type boolean
*/
/** 已经校验过的表单域,在 validateTrigger 再次被触发时是否再次校验 */
force?: boolean;
/**
* Config scroll behavior of validateFieldsAndScroll
* @type object
*/
scroll?: object;
}
/** 定义 validateFieldsAndScroll 的滚动行为 */
scroll?: DomScrollIntoViewConfig;
};
declare interface ValidationRule {
/**
@ -211,7 +213,7 @@ export interface WrappedFormUtils {
* Get the specified fields' values. If you don't specify a parameter, you will get all fields' values.
* @type Funtion (Function([fieldNames: string[]))
*/
getFieldsValue(fieldNames?: string[]): object;
getFieldsValue(fieldNames?: string[]): { [field: string]: any };
/**
* Get the value of a field.
@ -262,13 +264,16 @@ export interface WrappedFormUtils {
* @type Function
*/
validateFields(
fieldNames: string[],
options: ValidateFieldOptions,
fieldNames: Array<string>,
options: ValidateFieldsOptions,
callback: ValidateCallback,
): void;
validateFields(fieldNames: string[], callback: ValidateCallback): void;
validateFields(options: ValidateFieldOptions, callback: ValidateCallback): void;
validateFields(options: ValidateFieldsOptions, callback: ValidateCallback): void;
validateFields(fieldNames: Array<string>, callback: ValidateCallback): void;
validateFields(fieldNames: Array<string>, options: ValidateFieldsOptions): void;
validateFields(fieldNames: Array<string>): void;
validateFields(callback: ValidateCallback): void;
validateFields(options: ValidateFieldsOptions): void;
validateFields(): void;
/**
@ -277,13 +282,16 @@ export interface WrappedFormUtils {
* @type Function
*/
validateFieldsAndScroll(
fieldNames?: string[],
options?: Object,
callback?: ValidateCallback,
fieldNames: Array<string>,
options: ValidateFieldsOptions,
callback: ValidateCallback,
): void;
validateFieldsAndScroll(fieldNames?: string[], callback?: ValidateCallback): void;
validateFieldsAndScroll(options?: Object, callback?: ValidateCallback): void;
validateFieldsAndScroll(callback?: ValidateCallback): void;
validateFieldsAndScroll(options: ValidateFieldsOptions, callback: ValidateCallback): void;
validateFieldsAndScroll(fieldNames: Array<string>, callback: ValidateCallback): void;
validateFieldsAndScroll(fieldNames: Array<string>, options: ValidateFieldsOptions): void;
validateFieldsAndScroll(fieldNames: Array<string>): void;
validateFieldsAndScroll(callback: ValidateCallback): void;
validateFieldsAndScroll(options: ValidateFieldsOptions): void;
validateFieldsAndScroll(): void;
}

View File

@ -6,11 +6,13 @@ import { AntdComponent } from '../component';
import { InputGroup } from './input-group';
import { InputSearch } from './input-search';
import { TextArea } from './textarea';
import { Passward } from './passward';
export declare class Input extends AntdComponent {
static Group: typeof InputGroup;
static Search: typeof InputSearch;
static TextArea: typeof TextArea;
static Passward: typeof Passward;
/**
* The label text displayed after (on the right side of) the input field.
@ -74,4 +76,6 @@ export declare class Input extends AntdComponent {
* @type string | number
*/
value: string | number;
allowClear?: boolean;
}

9
types/input/passward.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
// Project: https://github.com/vueComponent/ant-design-vue
// Definitions by: akki-jat <https://github.com/akki-jat>
// Definitions: https://github.com/vueComponent/ant-design-vue/types
import { AntdComponent } from '../component';
export declare class Passward extends AntdComponent {
visibilityToggle?: boolean;
}

4
types/modal.d.ts vendored
View File

@ -48,6 +48,10 @@ export interface ModalOptions {
*/
iconType?: string;
icon?: string | Function;
mask?: boolean;
/**
* Whether support press esc to close
* @default true