feat: Input & Input.TextArea support bordered prop (#25617)

* feat: Input bordered prop

* fix: snapshot

* fix: borderless Input background-color -> transparent
This commit is contained in:
07akioni 2020-07-16 00:25:47 +08:00 committed by GitHub
parent b814270983
commit f937a7fffe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 284 additions and 58 deletions

View File

@ -28,6 +28,7 @@ interface BasicProps {
direction?: any;
focused?: boolean;
readOnly?: boolean;
bordered: boolean;
}
/**
@ -102,6 +103,7 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
direction,
style,
readOnly,
bordered,
} = this.props;
const suffixNode = this.renderSuffix(prefixCls);
if (!hasPrefixSuffix(this.props)) {
@ -120,6 +122,7 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
[`${prefixCls}-affix-wrapper-input-with-clear-btn`]: suffix && allowClear && value,
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
});
return (
<span
@ -132,7 +135,7 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
{cloneElement(element, {
style: null,
value,
className: getInputClassName(prefixCls, size, disabled),
className: getInputClassName(prefixCls, bordered, size, disabled),
})}
{suffixNode}
</span>
@ -178,7 +181,7 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
}
renderTextAreaWithClearIcon(prefixCls: string, element: React.ReactElement) {
const { value, allowClear, className, style, direction } = this.props;
const { value, allowClear, className, style, direction, bordered } = this.props;
if (!allowClear) {
return cloneElement(element, {
value,
@ -187,8 +190,11 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
const affixWrapperCls = classNames(
className,
`${prefixCls}-affix-wrapper`,
{ [`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl' },
`${prefixCls}-affix-wrapper-textarea-with-clear-btn`,
{
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
},
);
return (
<span className={affixWrapperCls} style={style}>

View File

@ -47,6 +47,7 @@ export interface InputProps
prefix?: React.ReactNode;
suffix?: React.ReactNode;
allowClear?: boolean;
bordered?: boolean;
}
export function fixControlledValue<T>(value: T) {
@ -84,6 +85,7 @@ export function resolveOnChange(
export function getInputClassName(
prefixCls: string,
bordered: boolean,
size?: SizeType,
disabled?: boolean,
direction?: any,
@ -93,6 +95,7 @@ export function getInputClassName(
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-borderless`]: !bordered,
});
}
@ -220,6 +223,7 @@ class Input extends React.Component<InputProps, InputState> {
renderInput = (
prefixCls: string,
size: SizeType | undefined,
bordered: boolean,
input: ConfigConsumerProps['input'] = {},
) => {
const { className, addonBefore, addonAfter, size: customizeSize, disabled } = this.props;
@ -237,6 +241,7 @@ class Input extends React.Component<InputProps, InputState> {
'defaultValue',
'size',
'inputType',
'bordered',
]);
return (
<input
@ -247,7 +252,7 @@ class Input extends React.Component<InputProps, InputState> {
onBlur={this.onBlur}
onKeyDown={this.handleKeyDown}
className={classNames(
getInputClassName(prefixCls, customizeSize || size, disabled, this.direction),
getInputClassName(prefixCls, bordered, customizeSize || size, disabled, this.direction),
{
[className!]: className && !addonBefore && !addonAfter,
},
@ -287,7 +292,7 @@ class Input extends React.Component<InputProps, InputState> {
renderComponent = ({ getPrefixCls, direction, input }: ConfigConsumerProps) => {
const { value, focused } = this.state;
const { prefixCls: customizePrefixCls } = this.props;
const { prefixCls: customizePrefixCls, bordered = true } = this.props;
const prefixCls = getPrefixCls('input', customizePrefixCls);
this.direction = direction;
@ -300,12 +305,13 @@ class Input extends React.Component<InputProps, InputState> {
prefixCls={prefixCls}
inputType="input"
value={fixControlledValue(value)}
element={this.renderInput(prefixCls, size, input)}
element={this.renderInput(prefixCls, size, bordered, input)}
handleReset={this.handleReset}
ref={this.saveClearableInput}
direction={direction}
focused={focused}
triggerFocus={this.focus}
bordered={bordered}
/>
)}
</SizeContext.Consumer>

View File

@ -1,12 +1,14 @@
import * as React from 'react';
import RcTextArea, { TextAreaProps as RcTextAreaProps, ResizableTextArea } from 'rc-textarea';
import omit from 'omit.js';
import classNames from 'classnames';
import ClearableLabeledInput from './ClearableLabeledInput';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { fixControlledValue, resolveOnChange } from './Input';
export interface TextAreaProps extends RcTextAreaProps {
allowClear?: boolean;
bordered?: boolean;
}
export interface TextAreaState {
@ -69,10 +71,13 @@ class TextArea extends React.Component<TextAreaProps, TextAreaState> {
resolveOnChange(this.resizableTextArea.textArea, e, this.props.onChange);
};
renderTextArea = (prefixCls: string) => {
renderTextArea = (prefixCls: string, bordered: boolean) => {
return (
<RcTextArea
{...omit(this.props, ['allowClear'])}
{...omit(this.props, ['allowClear', 'bordered'])}
className={classNames(this.props.className, {
[`${prefixCls}-borderless`]: !bordered,
})}
prefixCls={prefixCls}
onChange={this.handleChange}
ref={this.saveTextArea}
@ -82,7 +87,7 @@ class TextArea extends React.Component<TextAreaProps, TextAreaState> {
renderComponent = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
const { value } = this.state;
const { prefixCls: customizePrefixCls } = this.props;
const { prefixCls: customizePrefixCls, bordered = true } = this.props;
const prefixCls = getPrefixCls('input', customizePrefixCls);
return (
<ClearableLabeledInput
@ -91,10 +96,11 @@ class TextArea extends React.Component<TextAreaProps, TextAreaState> {
direction={direction}
inputType="text"
value={fixControlledValue(value)}
element={this.renderTextArea(prefixCls)}
element={this.renderTextArea(prefixCls, bordered)}
handleReset={this.handleReset}
ref={this.saveClearableInput}
triggerFocus={this.focus}
bordered={bordered}
/>
);
};

View File

@ -1063,6 +1063,141 @@ exports[`renders ./components/input/demo/basic.md correctly 1`] = `
/>
`;
exports[`renders ./components/input/demo/borderless.md correctly 1`] = `
<input
class="ant-input ant-input-borderless"
placeholder="Borderless"
type="text"
value=""
/>
`;
exports[`renders ./components/input/demo/borderless-debug.md correctly 1`] = `
<div
style="background-color:rgba(0, 0, 128, .2)"
>
<input
class="ant-input ant-input-borderless"
placeholder="Unbordered"
type="text"
value=""
/>
<input
class="ant-input ant-input-lg ant-input-borderless"
placeholder="Unbordered"
type="text"
value=""
/>
<textarea
class="ant-input ant-input-borderless"
placeholder="Unbordered"
/>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn ant-input-affix-wrapper-borderless"
>
<textarea
class="ant-input ant-input-borderless"
placeholder="Unbordered"
/>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-textarea-clear-icon ant-input-textarea-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
class=""
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-borderless"
>
<input
class="ant-input ant-input-borderless"
placeholder="Unbordered"
type="text"
value=""
/>
<span
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
class=""
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-borderless"
>
<span
class="ant-input-prefix"
>
</span>
<input
class="ant-input ant-input-borderless"
type="text"
value=""
/>
<span
class="ant-input-suffix"
>
RMB
</span>
</span>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-disabled ant-input-affix-wrapper-borderless"
>
<span
class="ant-input-prefix"
>
</span>
<input
class="ant-input ant-input-disabled ant-input-borderless"
disabled=""
type="text"
value=""
/>
<span
class="ant-input-suffix"
>
RMB
</span>
</span>
</div>
`;
exports[`renders ./components/input/demo/group.md correctly 1`] = `
<div
class="site-input-group-wrapper"

View File

@ -0,0 +1,34 @@
---
order: 98
title:
zh-CN: Borderless Debug
en-US: Borderless Debug
debug: true
---
## zh-CN
Buggy!
## en-US
Buggy!
```jsx
import { Input } from 'antd';
const { TextArea } = Input;
ReactDOM.render(
<div style={{ backgroundColor: 'rgba(0, 0, 128, .2)' }}>
<Input placeholder="Unbordered" bordered={false} />
<Input placeholder="Unbordered" bordered={false} size="large" />
<TextArea placeholder="Unbordered" bordered={false} />
<TextArea placeholder="Unbordered" bordered={false} allowClear />
<Input placeholder="Unbordered" bordered={false} allowClear />
<Input prefix="¥" suffix="RMB" bordered={false} />
<Input prefix="¥" suffix="RMB" disabled bordered={false} />
</div>,
mountNode,
);
```

View File

@ -0,0 +1,20 @@
---
order: 20
title:
zh-CN: 无边框
en-US: Borderless
---
## zh-CN
没有边框。
## en-US
No border.
```jsx
import { Input } from 'antd';
ReactDOM.render(<Input placeholder="Borderless" bordered={false} />, mountNode);
```

View File

@ -16,22 +16,23 @@ A basic widget for getting the user input is a text field. Keyboard and mouse ca
### Input
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| addonAfter | The label text displayed after (on the right side of) the input field | string \| ReactNode | - |
| addonBefore | The label text displayed before (on the left side of) the input field | string \| ReactNode | - |
| defaultValue | The initial input content | string | - |
| disabled | Whether the input is disabled | boolean | false |
| id | The ID for input | string | - |
| maxLength | The max length | number | - |
| prefix | The prefix icon for the Input | string \| ReactNode | - |
| size | The size of the input box. Note: in the context of a form, the `large` size is used | `large` \| `middle` \| `small` | - |
| suffix | The suffix icon for the Input | string \| ReactNode | - |
| 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 | The input content value | string | - |
| onChange | Callback when user input | function(e) | - |
| onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - |
| allowClear | If allow to remove input content with clear icon | boolean | false |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| addonAfter | The label text displayed after (on the right side of) the input field | string \| ReactNode | - | |
| addonBefore | The label text displayed before (on the left side of) the input field | string \| ReactNode | - | |
| defaultValue | The initial input content | string | - | |
| disabled | Whether the input is disabled | boolean | false | |
| id | The ID for input | string | - | |
| maxLength | The max length | number | - | |
| prefix | The prefix icon for the Input | string \| ReactNode | - | |
| size | The size of the input box. Note: in the context of a form, the `large` size is used | `large` \| `middle` \| `small` | - | |
| suffix | The suffix icon for the Input | string \| ReactNode | - | |
| 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 | The input content value | string | - | |
| onChange | Callback when user input | function(e) | - | |
| onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - | |
| allowClear | If allow to remove input content with clear icon | boolean | false | |
| bordered | Whether has border style | boolean | true | 4.5.0 |
> When `Input` is used in a `Form.Item` context, if the `Form.Item` has the `id` and `options` props defined then `value`, `defaultValue`, and `id` props of `Input` are automatically set.
@ -39,14 +40,15 @@ The rest of the props of Input are exactly the same as the original [input](http
### 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 | The input content value | string | - |
| onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - |
| allowClear | If allow to remove input content with clear icon | boolean | false |
| onResize | The callback function that is triggered when resize | function({ width, height }) | - |
| 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 | The input content value | string | - | |
| onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - | |
| allowClear | If allow to remove input content with clear icon | boolean | false | |
| onResize | The callback function that is triggered when resize | function({ width, height }) | - | |
| bordered | Whether has border style | boolean | true | 4.5.0 |
The rest of the props of `Input.TextArea` are the same as the original [textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea).

View File

@ -17,22 +17,23 @@ cover: https://gw.alipayobjects.com/zos/alicdn/xS9YEJhfe/Input.svg
### Input
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| addonAfter | 带标签的 input设置后置标签 | string \| ReactNode | - |
| addonBefore | 带标签的 input设置前置标签 | string \| ReactNode | - |
| defaultValue | 输入框默认内容 | string | - |
| disabled | 是否禁用状态,默认为 false | boolean | false |
| id | 输入框的 id | string | - |
| maxLength | 最大长度 | number | - |
| prefix | 带有前缀图标的 input | string \| ReactNode | - |
| size | 控件大小。注:标准表单内的输入框大小限制为 `large` | `large` \| `middle` \| `small` | - |
| suffix | 带有后缀图标的 input | string \| ReactNode | - |
| type | 声明 input 类型,同原生 input 标签的 type 属性,见:[MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input#属性)(请直接使用 `Input.TextArea` 代替 `type="textarea"`) | string | `text` |
| value | 输入框内容 | string | - |
| onChange | 输入框内容变化时的回调 | function(e) | - |
| onPressEnter | 按下回车的回调 | function(e) | - |
| allowClear | 可以点击清除图标删除内容 | boolean | - |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| addonAfter | 带标签的 input设置后置标签 | string \| ReactNode | - | |
| addonBefore | 带标签的 input设置前置标签 | string \| ReactNode | - | |
| defaultValue | 输入框默认内容 | string | - | |
| disabled | 是否禁用状态,默认为 false | boolean | false | |
| id | 输入框的 id | string | - | |
| maxLength | 最大长度 | number | - | |
| prefix | 带有前缀图标的 input | string \| ReactNode | - | |
| size | 控件大小。注:标准表单内的输入框大小限制为 `large` | `large` \| `middle` \| `small` | - | |
| suffix | 带有后缀图标的 input | string \| ReactNode | - | |
| type | 声明 input 类型,同原生 input 标签的 type 属性,见:[MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input#属性)(请直接使用 `Input.TextArea` 代替 `type="textarea"`) | string | `text` | |
| value | 输入框内容 | string | - | |
| onChange | 输入框内容变化时的回调 | function(e) | - | |
| onPressEnter | 按下回车的回调 | function(e) | - | |
| allowClear | 可以点击清除图标删除内容 | boolean | - | |
| bordered | 是否有边框 | boolean | true | 4.5.0 |
> 如果 `Input``Form.Item` 内,并且 `Form.Item` 设置了 `id``options` 属性,则 `value` `defaultValue``id` 属性会被自动设置。
@ -40,14 +41,15 @@ Input 的其他属性和 React 自带的 [input](https://facebook.github.io/reac
### Input.TextArea
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| autoSize | 自适应内容高度,可设置为 true \| false 或对象:{ minRows: 2, maxRows: 6 } | boolean \| object | false |
| defaultValue | 输入框默认内容 | string | - |
| value | 输入框内容 | string | - |
| onPressEnter | 按下回车的回调 | function(e) | - |
| allowClear | 可以点击清除图标删除内容 | boolean | false |
| onResize | resize 回调 | function({ width, height }) | - |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| autoSize | 自适应内容高度,可设置为 true \| false 或对象:{ minRows: 2, maxRows: 6 } | boolean \| object | false | |
| defaultValue | 输入框默认内容 | string | - | |
| value | 输入框内容 | string | - | |
| onPressEnter | 按下回车的回调 | function(e) | - | |
| allowClear | 可以点击清除图标删除内容 | boolean | false | |
| onResize | resize 回调 | function({ width, height }) | - | |
| bordered | 是否有边框 | boolean | true | 4.5.0 |
`Input.TextArea` 的其他属性和浏览器自带的 [textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) 一致。

View File

@ -78,6 +78,19 @@
.disabled();
}
&-borderless {
&,
&:hover,
&:focus,
&-focused,
&-disabled,
&[disabled] {
background-color: transparent;
border: none;
box-shadow: none;
}
}
// Reset height for `textarea`s
textarea& {
max-width: 100%; // prevent textearea resize from coming out of its container

View File

@ -13,6 +13,7 @@ exports[`Transfer.Search should show cross icon when input value exists 1`] = `
value=""
>
<ClearableLabeledInput
bordered={true}
element={
<input
className="ant-input"
@ -141,6 +142,7 @@ exports[`Transfer.Search should show cross icon when input value exists 2`] = `
value="a"
>
<ClearableLabeledInput
bordered={true}
element={
<input
className="ant-input"