refactor: input by ts

feat-dayjs
tanjinzhou 2020-10-16 14:24:14 +08:00
parent 20f54950a9
commit 6cb52efbc8
12 changed files with 151 additions and 132 deletions

@ -1 +1 @@
Subproject commit 88970d13f8e2e6f5c96a28697fe0b399eccdcb07
Subproject commit c9c4f31698398d5bafa5ccd80f0cb7c23b1ab608

View File

@ -4,8 +4,10 @@ import { getInputClassName } from './Input';
import PropTypes from '../_util/vue-types';
import { cloneElement } from '../_util/vnode';
import { getComponent } from '../_util/props-util';
import { defineComponent, VNode } from 'vue';
import { tuple } from '../_util/type';
export function hasPrefixSuffix(instance) {
export function hasPrefixSuffix(instance: any) {
return !!(
getComponent(instance, 'prefix') ||
getComponent(instance, 'suffix') ||
@ -15,27 +17,27 @@ export function hasPrefixSuffix(instance) {
const ClearableInputType = ['text', 'input'];
const ClearableLabeledInput = {
const ClearableLabeledInput = defineComponent({
name: 'ClearableLabeledInput',
inheritAttrs: false,
props: {
prefixCls: PropTypes.string,
inputType: PropTypes.oneOf(ClearableInputType),
inputType: PropTypes.oneOf(tuple('text', 'input')),
value: PropTypes.any,
defaultValue: PropTypes.any,
allowClear: PropTypes.looseBool,
element: PropTypes.any,
element: PropTypes.VNodeChild,
handleReset: PropTypes.func,
disabled: PropTypes.looseBool,
size: PropTypes.oneOf(['small', 'large', 'default']),
suffix: PropTypes.any,
prefix: PropTypes.any,
addonBefore: PropTypes.any,
addonAfter: PropTypes.any,
size: PropTypes.oneOf(tuple('small', 'large', 'default')),
suffix: PropTypes.VNodeChild,
prefix: PropTypes.VNodeChild,
addonBefore: PropTypes.VNodeChild,
addonAfter: PropTypes.VNodeChild,
readonly: PropTypes.looseBool,
},
methods: {
renderClearIcon(prefixCls) {
renderClearIcon(prefixCls: string) {
const { allowClear, value, disabled, readonly, inputType, handleReset } = this.$props;
if (
!allowClear ||
@ -54,7 +56,7 @@ const ClearableLabeledInput = {
return <CloseCircleFilled onClick={handleReset} class={className} role="button" />;
},
renderSuffix(prefixCls) {
renderSuffix(prefixCls: string) {
const { suffix, allowClear } = this.$props;
if (suffix || allowClear) {
return (
@ -67,7 +69,7 @@ const ClearableLabeledInput = {
return null;
},
renderLabeledIcon(prefixCls, element) {
renderLabeledIcon(prefixCls: string, element: VNode): VNode {
const props = this.$props;
const suffix = this.renderSuffix(prefixCls);
if (!hasPrefixSuffix(this)) {
@ -88,7 +90,7 @@ const ClearableLabeledInput = {
});
return (
<span class={affixWrapperCls} style={props.style}>
<span class={affixWrapperCls} style={this.$attrs?.style}>
{prefix}
{cloneElement(element, {
style: null,
@ -97,10 +99,10 @@ const ClearableLabeledInput = {
})}
{suffix}
</span>
);
) as VNode;
},
renderInputWithLabel(prefixCls, labeledElement) {
renderInputWithLabel(prefixCls: string, labeledElement: VNode) {
const { addonBefore, addonAfter, size } = this.$props;
const { style, class: className } = this.$attrs;
// Not wrap when there is not addons
@ -137,7 +139,7 @@ const ClearableLabeledInput = {
);
},
renderTextAreaWithClearIcon(prefixCls, element) {
renderTextAreaWithClearIcon(prefixCls: string, element: VNode) {
const { value, allowClear } = this.$props;
const { style, class: className } = this.$attrs;
if (!allowClear) {
@ -160,7 +162,7 @@ const ClearableLabeledInput = {
},
renderClearableLabeledInput() {
const { prefixCls, inputType, element } = this.$props;
const { prefixCls, inputType, element } = this.$props as any;
if (inputType === ClearableInputType[0]) {
return this.renderTextAreaWithClearIcon(prefixCls, element);
}
@ -170,6 +172,6 @@ const ClearableLabeledInput = {
render() {
return this.renderClearableLabeledInput();
},
};
});
export default ClearableLabeledInput;

View File

@ -1,17 +1,14 @@
import { inject } from 'vue';
import { defineComponent, inject } from 'vue';
import PropTypes from '../_util/vue-types';
import { getSlot } from '../_util/props-util';
import { defaultConfigProvider } from '../config-provider';
import { tuple } from '../_util/type';
export default {
export default defineComponent({
name: 'AInputGroup',
props: {
prefixCls: PropTypes.string,
size: {
validator(value) {
return ['small', 'large', 'default'].includes(value);
},
},
size: PropTypes.oneOf(tuple('small', 'large', 'default')),
compact: PropTypes.looseBool,
},
setup() {
@ -21,8 +18,8 @@ export default {
},
computed: {
classes() {
const { prefixCls: customizePrefixCls, size, compact = false } = this;
const getPrefixCls = this.configProvider.getPrefixCls;
const { prefixCls: customizePrefixCls, size, compact = false, configProvider } = this as any;
const getPrefixCls = configProvider.getPrefixCls;
const prefixCls = getPrefixCls('input-group', customizePrefixCls);
return {
@ -36,4 +33,4 @@ export default {
render() {
return <span class={this.classes}>{getSlot(this)}</span>;
},
};
});

View File

@ -1,4 +1,4 @@
import { defineComponent, inject, withDirectives } from 'vue';
import { defineComponent, inject, VNode, withDirectives } from 'vue';
import antInputDirective from '../_util/antInputDirective';
import classNames from '../_util/classNames';
import omit from 'omit.js';
@ -57,6 +57,9 @@ export default defineComponent({
setup() {
return {
configProvider: inject('configProvider', defaultConfigProvider),
removePasswordTimeout: undefined,
input: null,
clearableInput: null
};
},
data() {
@ -98,15 +101,15 @@ export default defineComponent({
this.input.select();
},
saveClearableInput(input) {
saveClearableInput(input: any) {
this.clearableInput = input;
},
saveInput(input) {
saveInput(input: any) {
this.input = input;
},
setValue(value, callback) {
setValue(value: any, callback?: Function) {
if (this.stateValue === value) {
return;
}
@ -119,18 +122,18 @@ export default defineComponent({
callback && callback();
});
},
triggerChange(e) {
this.$emit('update:value', e.target.value);
triggerChange(e: Event) {
this.$emit('update:value', (e.target as any).value);
this.$emit('change', e);
this.$emit('input', e);
},
handleReset(e) {
handleReset(e: Event) {
this.setValue('', () => {
this.focus();
});
resolveOnChange(this.input, e, this.triggerChange);
},
renderInput(prefixCls, { addonBefore, addonAfter }) {
renderInput(prefixCls: string, { addonBefore, addonAfter }) {
const otherProps = omit(this.$props, [
'prefixCls',
'onPressEnter',
@ -142,19 +145,17 @@ export default defineComponent({
'defaultValue',
'lazy',
'size',
'inputType',
'className',
'inputPrefixCls',
'loading',
]);
const { handleKeyDown, handleChange, size, disabled, $attrs } = this;
const inputProps = {
const inputProps: any = {
...otherProps,
...$attrs,
onKeydown: handleKeyDown,
class: classNames(getInputClassName(prefixCls, size, disabled), {
[$attrs.class]: $attrs.class && !addonBefore && !addonAfter,
[$attrs.class as string]: $attrs.class && !addonBefore && !addonAfter,
}),
ref: this.saveInput,
key: 'ant-input',
@ -164,7 +165,8 @@ export default defineComponent({
if (!inputProps.autofocus) {
delete inputProps.autofocus;
}
return withDirectives(<input {...inputProps} />, [[antInputDirective]]);
const inputNode = <input {...inputProps} />
return withDirectives(inputNode as VNode, [[antInputDirective]]);
},
clearPasswordValueAttribute() {
// https://github.com/ant-design/ant-design/issues/20541
@ -179,14 +181,14 @@ export default defineComponent({
}
});
},
handleChange(e) {
const { value, composing } = e.target;
handleChange(e: Event) {
const { value, composing, isComposing } = e.target as any;
// https://github.com/vueComponent/ant-design-vue/issues/2203
if (((e.isComposing || composing) && this.lazy) || this.stateValue === value) return;
if (((isComposing || composing) && this.lazy) || this.stateValue === value) return;
this.setValue(value, this.clearPasswordValueAttribute);
resolveOnChange(this.input, e, this.triggerChange);
},
handleKeyDown(e) {
handleKeyDown(e: KeyboardEvent) {
if (e.keyCode === 13) {
this.$emit('pressEnter', e);
}
@ -194,16 +196,6 @@ export default defineComponent({
},
},
render() {
// 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;
@ -212,7 +204,7 @@ export default defineComponent({
const addonBefore = getComponent(this, 'addonBefore');
const suffix = getComponent(this, 'suffix');
const prefix = getComponent(this, 'prefix');
const props = {
const props: any = {
...this.$attrs,
...getOptionProps(this),
prefixCls,

View File

@ -6,13 +6,14 @@ import EyeInvisibleOutlined from '@ant-design/icons-vue/EyeInvisibleOutlined';
import inputProps from './inputProps';
import PropTypes from '../_util/vue-types';
import BaseMixin from '../_util/BaseMixin';
import { defineComponent } from 'vue';
const ActionMap = {
click: 'onClick',
hover: 'onMouseover',
};
export default {
export default defineComponent({
name: 'AInputPassword',
mixins: [BaseMixin],
inheritAttrs: false,
@ -23,13 +24,18 @@ export default {
action: PropTypes.string.def('click'),
visibilityToggle: PropTypes.looseBool.def(true),
},
setup() {
return {
input: null,
}
},
data() {
return {
visible: false,
};
},
methods: {
saveInput(node) {
saveInput(node: any) {
this.input = node;
},
focus() {
@ -101,4 +107,4 @@ export default {
};
return <Input {...inputProps} ref={this.saveInput} />;
},
};
});

View File

@ -1,3 +1,4 @@
import {PropType, VNode} from 'vue';
import ResizeObserver from '../vc-resize-observer';
import omit from 'omit.js';
import classNames from '../_util/classNames';
@ -6,22 +7,28 @@ import raf from '../_util/raf';
import warning from '../_util/warning';
import BaseMixin from '../_util/BaseMixin';
import inputProps from './inputProps';
import PropTypes, { withUndefined } from '../_util/vue-types';
import PropTypes from '../_util/vue-types';
import { getOptionProps } from '../_util/props-util';
import { withDirectives } from 'vue';
import { defineComponent, withDirectives } from 'vue';
import antInput from '../_util/antInputDirective';
const RESIZE_STATUS_NONE = 0;
const RESIZE_STATUS_RESIZING = 1;
const RESIZE_STATUS_RESIZED = 2;
export interface AutoSizeType {
minRows?: number;
maxRows?: number;
}
const TextAreaProps = {
...inputProps,
autosize: withUndefined(PropTypes.oneOfType([Object, Boolean])),
autoSize: withUndefined(PropTypes.oneOfType([Object, Boolean])),
autosize: { type: [Boolean, Object] as PropType<AutoSizeType>, default: undefined},
autoSize: {type: [Boolean, Object] as PropType<AutoSizeType>, default: undefined},
onResize: PropTypes.func,
};
const ResizableTextArea = {
const ResizableTextArea = defineComponent({
name: 'ResizableTextArea',
inheritAttrs: false,
props: TextAreaProps,
@ -39,6 +46,13 @@ const ResizableTextArea = {
raf.cancel(this.nextFrameActionId);
raf.cancel(this.resizeFrameId);
},
setup() {
return {
nextFrameActionId: undefined,
textArea: null,
resizeFrameId: undefined,
}
},
watch: {
value() {
this.$nextTick(() => {
@ -47,10 +61,10 @@ const ResizableTextArea = {
},
},
methods: {
saveTextArea(textArea) {
saveTextArea(textArea: any) {
this.textArea = textArea;
},
handleResize(size) {
handleResize(size: any) {
const { resizeStatus } = this.$data;
if (resizeStatus !== RESIZE_STATUS_NONE) {
@ -98,7 +112,7 @@ const ResizableTextArea = {
},
renderTextArea() {
const props = { ...getOptionProps(this), ...this.$attrs };
const props: any = { ...getOptionProps(this), ...this.$attrs };
const { prefixCls, autoSize, autosize, disabled, class: className } = props;
const { textareaStyles, resizeStatus } = this.$data;
warning(
@ -131,7 +145,7 @@ const ResizableTextArea = {
? { overflowX: 'hidden', overflowY: 'hidden' }
: null),
};
const textareaProps = {
const textareaProps: any = {
...otherProps,
style,
class: cls,
@ -141,7 +155,7 @@ const ResizableTextArea = {
}
return (
<ResizeObserver onResize={this.handleResize} disabled={!(autoSize || autosize)}>
{withDirectives(<textarea {...textareaProps} ref={this.saveTextArea} />, [[antInput]])}
{withDirectives(<textarea {...textareaProps} ref={this.saveTextArea} /> as VNode, [[antInput]])}
</ResizeObserver>
);
},
@ -150,6 +164,6 @@ const ResizableTextArea = {
render() {
return this.renderTextArea();
},
};
});
export default ResizableTextArea;

View File

@ -1,4 +1,4 @@
import { inject } from 'vue';
import { defineComponent, inject } from 'vue';
import classNames from '../_util/classNames';
import { isMobile } from 'is-mobile';
import Input from './Input';
@ -12,22 +12,23 @@ import { getOptionProps, getComponent } from '../_util/props-util';
import { defaultConfigProvider } from '../config-provider';
import isPlainObject from 'lodash-es/isPlainObject';
export default {
export default defineComponent({
name: 'AInputSearch',
inheritAttrs: false,
props: {
...inputProps,
// 不能设置默认值 https://github.com/vueComponent/ant-design-vue/issues/1916
enterButton: PropTypes.any,
enterButton: PropTypes.VNodeChild,
onSearch: PropTypes.func,
},
setup() {
return {
configProvider: inject('configProvider', defaultConfigProvider),
input: null,
};
},
methods: {
saveInput(node) {
saveInput(node: any) {
this.input = node;
},
handleChange(e) {
@ -53,7 +54,7 @@ export default {
blur() {
this.input.blur();
},
renderLoading(prefixCls) {
renderLoading(prefixCls: string) {
const { size } = this.$props;
let enterButton = getComponent(this, 'enterButton');
// 兼容 <a-input-search enterButton /> 因enterButton类型为 any此类写法 enterButton 为空字符串
@ -67,7 +68,7 @@ export default {
}
return <LoadingOutlined class={`${prefixCls}-icon`} key="loadingIcon" />;
},
renderSuffix(prefixCls) {
renderSuffix(prefixCls: string) {
const { loading } = this;
const suffix = getComponent(this, 'suffix');
let enterButton = getComponent(this, 'enterButton');
@ -95,7 +96,7 @@ export default {
return icon;
},
renderAddonAfter(prefixCls) {
renderAddonAfter(prefixCls: string) {
const { size, disabled, loading } = this;
const btnClassName = `${prefixCls}-button`;
let enterButton = getComponent(this, 'enterButton');
@ -106,7 +107,7 @@ export default {
}
if (!enterButton) return addonAfter;
const enterButtonAsElement = Array.isArray(enterButton) ? enterButton[0] : enterButton;
let button;
let button: any;
const isAntdButton =
enterButtonAsElement.type &&
isPlainObject(enterButtonAsElement.type) &&
@ -146,7 +147,7 @@ export default {
size,
class: className,
...restProps
} = { ...getOptionProps(this), ...this.$attrs };
} = { ...getOptionProps(this), ...this.$attrs } as any;
delete restProps.onSearch;
delete restProps.loading;
delete restProps.enterButton;
@ -159,7 +160,7 @@ export default {
let enterButton = getComponent(this, 'enterButton');
const addonBefore = getComponent(this, 'addonBefore');
enterButton = enterButton || enterButton === '';
let inputClassName;
let inputClassName: string;
if (enterButton) {
inputClassName = classNames(prefixCls, className, {
[`${prefixCls}-enter-button`]: !!enterButton,
@ -183,4 +184,4 @@ export default {
};
return <Input {...inputProps} ref={this.saveInput} />;
},
};
});

View File

@ -1,4 +1,4 @@
import { inject } from 'vue';
import { defineComponent, inject } from 'vue';
import ClearableLabeledInput from './ClearableLabeledInput';
import ResizableTextArea from './ResizableTextArea';
import inputProps from './inputProps';
@ -13,7 +13,7 @@ const TextAreaProps = {
autoSize: withUndefined(PropTypes.oneOfType([Object, Boolean])),
};
export default {
export default defineComponent({
name: 'ATextarea',
inheritAttrs: false,
props: {
@ -22,6 +22,8 @@ export default {
setup() {
return {
configProvider: inject('configProvider', defaultConfigProvider),
resizableTextArea: null,
clearableInput: null,
};
},
data() {
@ -45,7 +47,7 @@ export default {
});
},
methods: {
setValue(value, callback) {
setValue(value: any, callback?: Function) {
if (!hasProp(this, 'value')) {
this.stateValue = value;
} else {
@ -55,22 +57,22 @@ export default {
callback && callback();
});
},
handleKeyDown(e) {
handleKeyDown(e: KeyboardEvent) {
if (e.keyCode === 13) {
this.$emit('pressEnter', e);
}
this.$emit('keydown', e);
},
triggerChange(e) {
this.$emit('update:value', e.target.value);
triggerChange(e: Event) {
this.$emit('update:value', (e.target as any).value);
this.$emit('change', e);
this.$emit('input', e);
},
handleChange(e) {
const { value, composing } = e.target;
if (((e.isComposing || composing) && this.lazy) || this.stateValue === value) return;
handleChange(e: Event) {
const { value, composing, isComposing } = e.target as any;
if (((isComposing || composing) && this.lazy) || this.stateValue === value) return;
this.setValue(e.target.value, () => {
this.setValue((e.target as any).value, () => {
this.resizableTextArea.resizeTextarea();
});
resolveOnChange(this.resizableTextArea.textArea, e, this.triggerChange);
@ -83,14 +85,14 @@ export default {
blur() {
this.resizableTextArea.textArea.blur();
},
saveTextArea(resizableTextArea) {
saveTextArea(resizableTextArea: any) {
this.resizableTextArea = resizableTextArea;
},
saveClearableInput(clearableInput) {
saveClearableInput(clearableInput: any) {
this.clearableInput = clearableInput;
},
handleReset(e) {
handleReset(e: Event) {
this.setValue('', () => {
this.resizableTextArea.renderTextArea();
this.focus();
@ -98,7 +100,7 @@ export default {
resolveOnChange(this.resizableTextArea.textArea, e, this.triggerChange);
},
renderTextArea(prefixCls) {
renderTextArea(prefixCls: string) {
const props = getOptionProps(this);
const resizeProps = {
...props,
@ -116,7 +118,7 @@ export default {
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('input', customizePrefixCls);
const props = {
const props: any = {
...getOptionProps(this),
...this.$attrs,
prefixCls,
@ -127,4 +129,4 @@ export default {
};
return <ClearableLabeledInput {...props} ref={this.saveClearableInput} />;
},
};
});

View File

@ -35,12 +35,20 @@ const SIZING_STYLE = [
'box-sizing',
];
const computedStyleCache = {};
let hiddenTextarea;
export interface NodeType {
sizingStyle: string;
paddingSize: number;
borderSize: number;
boxSizing: string;
}
export function calculateNodeStyling(node, useCache = false) {
const nodeRef =
node.getAttribute('id') || node.getAttribute('data-reactid') || node.getAttribute('name');
const computedStyleCache: { [key: string]: NodeType } = {};
let hiddenTextarea: HTMLTextAreaElement;
export function calculateNodeStyling(node: HTMLElement, useCache = false) {
const nodeRef = (node.getAttribute('id') ||
node.getAttribute('data-reactid') ||
node.getAttribute('name')) as string;
if (useCache && computedStyleCache[nodeRef]) {
return computedStyleCache[nodeRef];
@ -63,7 +71,7 @@ export function calculateNodeStyling(node, useCache = false) {
const sizingStyle = SIZING_STYLE.map(name => `${name}:${style.getPropertyValue(name)}`).join(';');
const nodeInfo = {
const nodeInfo: NodeType = {
sizingStyle,
paddingSize,
borderSize,
@ -78,10 +86,10 @@ export function calculateNodeStyling(node, useCache = false) {
}
export default function calculateNodeHeight(
uiTextNode,
uiTextNode: HTMLTextAreaElement,
useCache = false,
minRows = null,
maxRows = null,
minRows: number | null = null,
maxRows: number | null = null,
) {
if (!hiddenTextarea) {
hiddenTextarea = document.createElement('textarea');
@ -91,7 +99,7 @@ export default function calculateNodeHeight(
// Fix wrap="off" issue
// https://github.com/ant-design/ant-design/issues/6577
if (uiTextNode.getAttribute('wrap')) {
hiddenTextarea.setAttribute('wrap', uiTextNode.getAttribute('wrap'));
hiddenTextarea.setAttribute('wrap', uiTextNode.getAttribute('wrap') as string);
} else {
hiddenTextarea.removeAttribute('wrap');
}
@ -112,7 +120,7 @@ export default function calculateNodeHeight(
let minHeight = Number.MIN_SAFE_INTEGER;
let maxHeight = Number.MAX_SAFE_INTEGER;
let height = hiddenTextarea.scrollHeight;
let overflowY;
let overflowY: any;
if (boxSizing === 'border-box') {
// border-box: add border, since height = content + padding + border

View File

@ -3,6 +3,7 @@ import Group from './Group';
import Search from './Search';
import TextArea from './TextArea';
import Password from './Password';
import { App } from 'vue';
Input.Group = Group;
Input.Search = Search;
@ -10,7 +11,7 @@ Input.TextArea = TextArea;
Input.Password = Password;
/* istanbul ignore next */
Input.install = function(app) {
Input.install = function(app: App) {
app.component(Input.name, Input);
app.component(Input.Group.name, Input.Group);
app.component(Input.Search.name, Input.Search);
@ -19,4 +20,9 @@ Input.install = function(app) {
return app;
};
export default Input;
export default Input as typeof Input & {
readonly Group: typeof Group;
readonly Search: typeof Search;
readonly TextArea: typeof TextArea;
readonly Password: typeof Password;
};

View File

@ -1,35 +1,26 @@
import { tuple } from '../_util/type';
import { PropType } from 'vue';
import PropTypes from '../_util/vue-types';
export default {
prefixCls: PropTypes.string,
inputPrefixCls: PropTypes.string,
defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
placeholder: [String, Number],
type: {
default: 'text',
type: String,
placeholder: {
type: [String, Number] as PropType<string | number>,
},
name: String,
size: PropTypes.oneOf(['small', 'large', 'default']),
type: PropTypes.string.def('text'),
name: PropTypes.string,
size: PropTypes.oneOf(tuple('small', 'large', 'default')),
disabled: PropTypes.looseBool,
readonly: PropTypes.looseBool,
addonBefore: PropTypes.any,
addonAfter: PropTypes.any,
// onPressEnter?: React.FormEventHandler<any>;
// onKeyDown?: React.FormEventHandler<any>;
// onChange?: React.ChangeEventHandler<HTMLInputElement>;
// onClick?: React.FormEventHandler<any>;
// onFocus?: React.FormEventHandler<any>;
// onBlur?: React.FormEventHandler<any>;
prefix: PropTypes.any,
suffix: PropTypes.any,
// spellCheck: Boolean,
addonBefore: PropTypes.VNodeChild,
addonAfter: PropTypes.VNodeChild,
prefix: PropTypes.VNodeChild,
suffix: PropTypes.VNodeChild,
autofocus: PropTypes.looseBool,
allowClear: PropTypes.looseBool,
lazy: {
default: true,
type: Boolean,
},
lazy: PropTypes.looseBool.def(true),
maxlength: PropTypes.number,
loading: PropTypes.looseBool,
onPressEnter: PropTypes.func,