diff --git a/__tests__/renderers/Form/__snapshots__/date.test.tsx.snap b/__tests__/renderers/Form/__snapshots__/date.test.tsx.snap index f10dead8b..a202de9bd 100644 --- a/__tests__/renderers/Form/__snapshots__/date.test.tsx.snap +++ b/__tests__/renderers/Form/__snapshots__/date.test.tsx.snap @@ -65,8 +65,8 @@ exports[`Renderer:date 1`] = ` class="cxd-DatePicker-clear" > diff --git a/docs/zh-CN/components/form/input-text.md b/docs/zh-CN/components/form/input-text.md index 261193a59..9b62e1f5f 100755 --- a/docs/zh-CN/components/form/input-text.md +++ b/docs/zh-CN/components/form/input-text.md @@ -317,28 +317,29 @@ order: 56 当做选择器表单项使用时,除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置 -| 属性名 | 类型 | 默认值 | 说明 | -| ------------ | ----------------------------------------- | --------- | ------------------------------------------------------------------------------------------- | -| options | `Array`或`Array` | | [选项组](./options#%E9%9D%99%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-options) | -| source | `string`或 [API](../../../docs/types/api) | | [动态选项组](./options#%E5%8A%A8%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-source) | -| autoComplete | `string`或 [API](../../../docs/types/api) | | [自动补全](./options#%E8%87%AA%E5%8A%A8%E8%A1%A5%E5%85%A8-autocomplete) | -| multiple | `boolean` | | [是否多选](./options#%E5%A4%9A%E9%80%89-multiple) | -| delimeter | `string` | `,` | [拼接符](./options#%E6%8B%BC%E6%8E%A5%E7%AC%A6-delimiter) | -| labelField | `string` | `"label"` | [选项标签字段](./options#%E9%80%89%E9%A1%B9%E6%A0%87%E7%AD%BE%E5%AD%97%E6%AE%B5-labelfield) | -| valueField | `string` | `"value"` | [选项值字段](./options#%E9%80%89%E9%A1%B9%E5%80%BC%E5%AD%97%E6%AE%B5-valuefield) | -| joinValues | `boolean` | `true` | [拼接值](./options#%E6%8B%BC%E6%8E%A5%E5%80%BC-joinvalues) | -| extractValue | `boolean` | `false` | [提取值](./options#%E6%8F%90%E5%8F%96%E5%A4%9A%E9%80%89%E5%80%BC-extractvalue) | -| addOn | `addOn` | | 输入框附加组件,比如附带一个提示文字,或者附带一个提交按钮。 | -| addOn.type | `string` | | 请选择 `text` 、`button` 或者 `submit`。 | -| addOn.label | `string` | | 文字说明 | -| addOn.xxx | `string` | | 其他参数请参考按钮文档 | -| trimContents | `boolean` | | 是否去除首尾空白文本。 | -| creatable | `boolean` | | 是否可以创建,默认为可以,除非设置为 false 即只能选择选项中的值 | -| clearable | `boolean` | | 是否可清除 | -| resetValue | `string` | `""` | 清除后设置此配置项给定的值。 | -| prefix | `string` | `""` | 前缀 | -| suffix | `string` | `""` | 后缀 | -| showCounter | `boolean` | | 是否显示计数器 | -| minLength | `number` | | 限制最小字数 | -| maxLength | `number` | | 限制最大字数 | -| transform | `object` | | 自动转换值,可选 `transform: { lowerCase: true, upperCase: true }` | +| 属性名 | 类型 | 默认值 | 说明 | +| -------------- | ----------------------------------------- | --------- | ------------------------------------------------------------------------------------------- | +| options | `Array`或`Array` | | [选项组](./options#%E9%9D%99%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-options) | +| source | `string`或 [API](../../../docs/types/api) | | [动态选项组](./options#%E5%8A%A8%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-source) | +| autoComplete | `string`或 [API](../../../docs/types/api) | | [自动补全](./options#%E8%87%AA%E5%8A%A8%E8%A1%A5%E5%85%A8-autocomplete) | +| multiple | `boolean` | | [是否多选](./options#%E5%A4%9A%E9%80%89-multiple) | +| delimeter | `string` | `,` | [拼接符](./options#%E6%8B%BC%E6%8E%A5%E7%AC%A6-delimiter) | +| labelField | `string` | `"label"` | [选项标签字段](./options#%E9%80%89%E9%A1%B9%E6%A0%87%E7%AD%BE%E5%AD%97%E6%AE%B5-labelfield) | +| valueField | `string` | `"value"` | [选项值字段](./options#%E9%80%89%E9%A1%B9%E5%80%BC%E5%AD%97%E6%AE%B5-valuefield) | +| joinValues | `boolean` | `true` | [拼接值](./options#%E6%8B%BC%E6%8E%A5%E5%80%BC-joinvalues) | +| extractValue | `boolean` | `false` | [提取值](./options#%E6%8F%90%E5%8F%96%E5%A4%9A%E9%80%89%E5%80%BC-extractvalue) | +| addOn | `addOn` | | 输入框附加组件,比如附带一个提示文字,或者附带一个提交按钮。 | +| addOn.type | `string` | | 请选择 `text` 、`button` 或者 `submit`。 | +| addOn.label | `string` | | 文字说明 | +| addOn.position | `'left' \| 'right'` | `'right'` | addOn 位置 | +| addOn.xxx | `string` | | 其他参数请参考按钮文档 | +| trimContents | `boolean` | | 是否去除首尾空白文本。 | +| creatable | `boolean` | | 是否可以创建,默认为可以,除非设置为 false 即只能选择选项中的值 | +| clearable | `boolean` | | 是否可清除 | +| resetValue | `string` | `""` | 清除后设置此配置项给定的值。 | +| prefix | `string` | `""` | 前缀 | +| suffix | `string` | `""` | 后缀 | +| showCounter | `boolean` | | 是否显示计数器 | +| minLength | `number` | | 限制最小字数 | +| maxLength | `number` | | 限制最大字数 | +| transform | `object` | | 自动转换值,可选 `transform: { lowerCase: true, upperCase: true }` | diff --git a/docs/zh-CN/components/form/textarea.md b/docs/zh-CN/components/form/textarea.md index feb489cc5..b58c03e70 100755 --- a/docs/zh-CN/components/form/textarea.md +++ b/docs/zh-CN/components/form/textarea.md @@ -24,6 +24,43 @@ order: 57 } ``` +## 清空输入框 + +带移除图标的输入框,点击图标删除所有内容。 + +```schema: scope="body" +{ + "type": "form", + "api": "/api/mock2/form/saveForm", + "body": [ + { + "name": "textarea", + "type": "textarea", + "label": "多行文本", + "clearable": true + } + ] +} +``` + +设置`resetValue`,清空输入框后重置为指定值 + +```schema: scope="body" +{ + "type": "form", + "api": "/api/mock2/form/saveForm", + "body": [ + { + "name": "textarea", + "type": "textarea", + "label": "多行文本", + "clearable": true, + "resetValue": "reset" + } + ] +} +``` + ## 显示计数器 ```schema: scope="body" @@ -35,7 +72,8 @@ order: 57 "type": "textarea", "label": "A", "showCounter": true, - "placeholder": "请输入" + "placeholder": "请输入", + "clearable": true }, { "name": "b", @@ -53,12 +91,13 @@ order: 57 当做选择器表单项使用时,除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置 -| 属性名 | 类型 | 默认值 | 说明 | -| ------------ | --------- | ------ | -------------------- | -| minRows | `number` | | 最小行数 | -| maxRows | `number` | | 最大行数 | -| trimContents | `boolean` | | 是否去除首尾空白文本 | -| readOnly | `boolean` | | 是否只读 | -| showCounter | `boolean` | `` | 是否显示计数器 | -| minLength | `number` | `` | 限制最小字数 | -| maxLength | `number` | `` | 限制最大字数 | +| 属性名 | 类型 | 默认值 | 说明 | +| ------------ | --------- | ------- | ---------------------------- | +| minRows | `number` | `3` | 最小行数 | +| maxRows | `number` | `20` | 最大行数 | +| trimContents | `boolean` | `true` | 是否去除首尾空白文本 | +| readOnly | `boolean` | `false` | 是否只读 | +| showCounter | `boolean` | `false` | 是否显示计数器 | +| maxLength | `number` | - | 限制最大字数 | +| clearable | `boolean` | `false` | 是否可清除 | +| resetValue | `string` | `""` | 清除后设置此配置项给定的值。 | diff --git a/scss/_mixins.scss b/scss/_mixins.scss index 8de25fc0f..8ea87fcb7 100644 --- a/scss/_mixins.scss +++ b/scss/_mixins.scss @@ -303,22 +303,27 @@ } @mixin input-clear { - padding: px2rem(3px); + padding: var(--Form-input-clearBtn-padding); cursor: pointer; display: flex; align-items: center; justify-content: center; margin-left: auto; + text-decoration: none; svg { - fill: var(--Form-input-iconColor); + fill: var(--Form-input-clearBtn-color); top: 0; - width: px2rem(10px); - height: px2rem(10px); + width: var(--Form-input-clearBtn-size); + height: var(--Form-input-clearBtn-size); } &:hover svg { - fill: var(--Form-input-onHover-iconColor); + fill: var(--Form-input-clearBtn-color-onHover); + } + + &:active svg { + fill: var(--Form-input-clearBtn-color-onActive); } } diff --git a/scss/_properties.scss b/scss/_properties.scss index b2a9755bc..e06fe77b8 100644 --- a/scss/_properties.scss +++ b/scss/_properties.scss @@ -703,6 +703,12 @@ --Form-input-placeholderColor: var(--text--muted-color); --Form-input-onDisabled-color: var(--text--muted-color); + --Form-input-clearBtn-size: var(--fontSizeMd); + --Form-input-clearBtn-padding: #{px2rem(3px)}; + --Form-input-clearBtn-color: #cecfd1; + --Form-input-clearBtn-color-onHover: #5e626a; + --Form-input-clearBtn-color-onActive: #2d323c; + --Form-item-gap: var(--gap-base); --Form-label-paddingTop: calc( diff --git a/scss/components/_result-box.scss b/scss/components/_result-box.scss index ccd33da7d..b2dc72394 100644 --- a/scss/components/_result-box.scss +++ b/scss/components/_result-box.scss @@ -51,19 +51,6 @@ &-clear { @include input-clear(); - width: px2rem(26px); - height: px2rem(26px); - margin: 0 px2rem(-2px); - margin-left: auto; - - &:hover { - background: var(--ResultBox-value-bg); - } - - > svg { - width: px2rem(12px); - height: px2rem(12px); - } } > svg { diff --git a/scss/components/form/_color.scss b/scss/components/form/_color.scss index 65ecdd9c8..4bf086630 100644 --- a/scss/components/form/_color.scss +++ b/scss/components/form/_color.scss @@ -74,8 +74,6 @@ &-clear { @include input-clear(); - display: inline-block; - line-height: 1; } &-arrow { diff --git a/scss/components/form/_date-range.scss b/scss/components/form/_date-range.scss index 606310ae7..ceec3866c 100644 --- a/scss/components/form/_date-range.scss +++ b/scss/components/form/_date-range.scss @@ -55,19 +55,26 @@ } &-toggler { - cursor: pointer; - color: var(--DatePicker-iconColor); - display: inline-flex; - align-items: center; + @include input-clear(); + line-height: 1; - &:hover { - color: var(--DatePicker-onHover-iconColor); + svg { + polyline, + circle { + stroke: var(--DatePicker-iconColor); + } + } + + &:hover svg { + polyline, + circle { + stroke: var(--DatePicker-onHover-iconColor); + } } } &-clear { @include input-clear(); - display: inline-block; line-height: 1; margin-right: var(--gap-xs); } diff --git a/scss/components/form/_date.scss b/scss/components/form/_date.scss index 18153a1f6..4e5e2c809 100644 --- a/scss/components/form/_date.scss +++ b/scss/components/form/_date.scss @@ -56,18 +56,25 @@ } &-toggler { - cursor: pointer; - color: var(--DatePicker-iconColor); - display: inline-flex; - align-items: center; + @include input-clear(); + line-height: 1; - &:hover { - color: var(--DatePicker-onHover-iconColor); + svg { + polyline, + circle { + stroke: var(--DatePicker-iconColor); + } + } + + &:hover svg { + polyline, + circle { + stroke: var(--DatePicker-onHover-iconColor); + } } } &-clear { - display: inline-block; @include input-clear(); line-height: 1; margin-right: var(--gap-xs); diff --git a/scss/components/form/_icon-picker.scss b/scss/components/form/_icon-picker.scss index f3da8932a..d16a062bd 100644 --- a/scss/components/form/_icon-picker.scss +++ b/scss/components/form/_icon-picker.scss @@ -12,6 +12,8 @@ } &-valueWrap { + display: flex; + justify-content: space-between; flex-grow: 1; line-height: 1; white-space: nowrap; diff --git a/scss/components/form/_location.scss b/scss/components/form/_location.scss index 34344893f..0febb6c48 100644 --- a/scss/components/form/_location.scss +++ b/scss/components/form/_location.scss @@ -63,7 +63,6 @@ } &-clear { - display: inline-block; @include input-clear(); line-height: 1; margin-right: var(--gap-xs); diff --git a/scss/components/form/_picker.scss b/scss/components/form/_picker.scss index 971363438..89ab260db 100644 --- a/scss/components/form/_picker.scss +++ b/scss/components/form/_picker.scss @@ -87,7 +87,6 @@ } &-clear { - display: inline-block; @include input-clear(); line-height: 1; margin-right: var(--gap-xs); diff --git a/scss/components/form/_text.scss b/scss/components/form/_text.scss index 8372c4679..f3d006561 100644 --- a/scss/components/form/_text.scss +++ b/scss/components/form/_text.scss @@ -46,7 +46,6 @@ flex-basis: 1; flex-grow: 1; min-width: 0; - border-radius: 0; &:first-child { @@ -103,16 +102,16 @@ } &:first-child .#{$ns}Button { - @if var(--InputGroup-button-borderRadius) { - border-top-left-radius: var(--InputGroup-button-borderRadius); - border-bottom-left-radius: var(--InputGroup-button-borderRadius); + @if var(--InputGroup-addOn-borderRadius) { + border-top-left-radius: var(--InputGroup-addOn-borderRadius); + border-bottom-left-radius: var(--InputGroup-addOn-borderRadius); } } &:last-child .#{$ns}Button { - @if var(--InputGroup-button-borderRadius) { - border-top-right-radius: var(--InputGroup-button-borderRadius); - border-bottom-right-radius: var(--InputGroup-button-borderRadius); + @if var(--InputGroup-addOn-borderRadius) { + border-top-right-radius: var(--InputGroup-addOn-borderRadius); + border-bottom-right-radius: var(--InputGroup-addOn-borderRadius); } } } diff --git a/scss/components/form/_textarea.scss b/scss/components/form/_textarea.scss index 3faa4f4e0..d2fd53d18 100644 --- a/scss/components/form/_textarea.scss +++ b/scss/components/form/_textarea.scss @@ -1,4 +1,6 @@ .#{$ns}TextareaControl { + --Form-input-clearBtn-padding: #{px2rem(2px)}; + position: relative; > textarea { @@ -62,9 +64,23 @@ color: #666; background: rgba(0, 0, 0, 0.4); } + + &.is-clearable { + right: calc( + var(--Form-input-paddingX) + var(--Form-input-clearBtn-size) + + var(--Form-input-clearBtn-padding) * 2 + #{px2rem(5px)} + ); + } } .has-error--maxLength &-counter { background: var(--danger); } + + &-clear { + @include input-clear(); + position: absolute; + right: var(--Form-input-paddingX); + bottom: var(--Form-input-paddingY); + } } diff --git a/scss/themes/_cxd-variables.scss b/scss/themes/_cxd-variables.scss index 1e63e487d..5ad7806b2 100644 --- a/scss/themes/_cxd-variables.scss +++ b/scss/themes/_cxd-variables.scss @@ -148,6 +148,9 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06), --Form-input-addOnColor: #666; --Form-input-onFocus-addOnColor: var(--primary); --Form-input-addOnDividerBorderWidth: 0; + --Form-input-clearBtn-color: #{$G7}; + --Form-input-clearBtn-color-onHover: #{$G4}; + --Form-input-clearBtn-color-onActive: #{$G3}; --Form-select-borderWidth: #{px2rem(1px)}; --Form-select-borderRadius: #{$R3}; @@ -179,7 +182,7 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06), --InputGroup-select-arrowColor: #999; --InputGroup-select-onFocused-arrowColor: var(--primary); --InputGroup-button-borderWidth: #{px2rem(1px)}; - --InputGroup-button-borderRadius: 0; + --InputGroup-button-borderRadius: #{$R3}; // $Number-bg: #eaf6fe; --Number-borderWidth: #{px2rem(1px)}; diff --git a/scss/themes/cxd.scss b/scss/themes/cxd.scss index f5ddaaaad..f8904e307 100644 --- a/scss/themes/cxd.scss +++ b/scss/themes/cxd.scss @@ -106,19 +106,6 @@ } } -.#{$ns}InputGroup { - // .#{$ns}TextControl-input { - // } - .#{$ns}InputGroup-btn .#{$ns}Button { - border-radius: px2rem(3px); - } - - .#{$ns}InputGroup-btn:last-child .#{$ns}Button { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } -} - .#{$ns}Button--iconOnly { min-width: var(--Button-height); } diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index 0a48a7f55..d379bc5c3 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -267,7 +267,7 @@ export class ColorControl extends React.PureComponent< {clearable && !disabled && value ? ( - + ) : null} diff --git a/src/components/DatePicker.tsx b/src/components/DatePicker.tsx index c81c4cff9..f657be1ab 100644 --- a/src/components/DatePicker.tsx +++ b/src/components/DatePicker.tsx @@ -700,7 +700,7 @@ export class DatePicker extends React.Component { {clearable && !disabled && normalizeValue(value, format) ? ( - + ) : null} diff --git a/src/components/DateRangePicker.tsx b/src/components/DateRangePicker.tsx index 8d3201a26..869808da7 100644 --- a/src/components/DateRangePicker.tsx +++ b/src/components/DateRangePicker.tsx @@ -885,7 +885,7 @@ export class DateRangePicker extends React.Component< {clearable && !disabled && value ? ( - + ) : null} diff --git a/src/components/LocationPicker.tsx b/src/components/LocationPicker.tsx index 878bdd71e..9e426f00c 100644 --- a/src/components/LocationPicker.tsx +++ b/src/components/LocationPicker.tsx @@ -171,7 +171,7 @@ export class LocationPicker extends React.Component< {clearable && !disabled && value ? ( - + ) : null} diff --git a/src/components/ResultBox.tsx b/src/components/ResultBox.tsx index 8dbf3d407..9f5fb6210 100644 --- a/src/components/ResultBox.tsx +++ b/src/components/ResultBox.tsx @@ -184,7 +184,7 @@ export class ResultBox extends React.Component { !disabled && (Array.isArray(result) ? result.length : result) ? ( - + ) : null} {!allowInput && mobileUI ? ( diff --git a/src/components/icons.tsx b/src/components/icons.tsx index af0b01a46..62250d596 100644 --- a/src/components/icons.tsx +++ b/src/components/icons.tsx @@ -77,6 +77,7 @@ import AlertInfo from '../icons/alert-info.svg'; import AlertWarning from '../icons/alert-warning.svg'; import AlertDanger from '../icons/alert-danger.svg'; import FunctionIcon from '../icons/function.svg'; +import InputClearIcon from '../icons/input-clear.svg'; // 兼容原来的用法,后续不直接试用。 @@ -180,6 +181,7 @@ registerIcon('alert-warning', AlertWarning); registerIcon('alert-danger', AlertDanger); registerIcon('tree-down', TreeDownIcon); registerIcon('function', FunctionIcon); +registerIcon('input-clear', InputClearIcon); export function Icon({ icon, @@ -202,6 +204,7 @@ export function Icon({ } export { + InputClearIcon, CloseIcon, UnDoIcon, ReDoIcon, diff --git a/src/icons/input-clear.svg b/src/icons/input-clear.svg new file mode 100644 index 000000000..864aebeae --- /dev/null +++ b/src/icons/input-clear.svg @@ -0,0 +1 @@ + diff --git a/src/renderers/Form/IconPicker.tsx b/src/renderers/Form/IconPicker.tsx index 540e40d71..3c1378bdf 100644 --- a/src/renderers/Form/IconPicker.tsx +++ b/src/renderers/Form/IconPicker.tsx @@ -7,6 +7,7 @@ import {autobind} from '../../utils/helper'; import {ICONS} from './IconPickerIcons'; import {FormItem, FormControlProps, FormBaseControl} from './Item'; import {Option} from '../../components/Select'; +import {Icon} from '../../components/icons'; /** * 图标选择器 @@ -22,6 +23,7 @@ export interface IconPickerProps extends FormControlProps { placeholder?: string; resetValue?: any; noDataTip?: string; + clearable?: boolean; } export interface IconPickerState { @@ -210,6 +212,23 @@ export default class IconPickerControl extends React.PureComponent< } } + @autobind + handleClear() { + const {onChange, resetValue} = this.props; + + onChange?.(resetValue); + + this.setState( + { + inputValue: resetValue, + isFocused: true + }, + () => { + this.focus(); + } + ); + } + renderFontIcons() { const { className, @@ -220,6 +239,7 @@ export default class IconPickerControl extends React.PureComponent< value, noDataTip, disabled, + clearable, translate: __ } = this.props; const options = this.formatOptions(); @@ -278,6 +298,15 @@ export default class IconPickerControl extends React.PureComponent< disabled={disabled} size={10} /> + + {clearable && !disabled && value ? ( + + + + ) : null} {isOpen ? (
diff --git a/src/renderers/Form/InputText.tsx b/src/renderers/Form/InputText.tsx index 55120ddba..273ecd5da 100644 --- a/src/renderers/Form/InputText.tsx +++ b/src/renderers/Form/InputText.tsx @@ -634,7 +634,7 @@ export default class TextControl extends React.PureComponent< onClick={this.clearValue} className={cx('TextControl-clear')} > - + ) : null} @@ -740,7 +740,7 @@ export default class TextControl extends React.PureComponent< /> {clearable && !disabled && value ? ( - + ) : null} {showCounter ? ( diff --git a/src/renderers/Form/Picker.tsx b/src/renderers/Form/Picker.tsx index d62b617b1..f72637711 100644 --- a/src/renderers/Form/Picker.tsx +++ b/src/renderers/Form/Picker.tsx @@ -469,7 +469,7 @@ export default class PickerControl extends React.PureComponent< {clearable && !disabled && selectedOptions.length ? ( - + ) : null} diff --git a/src/renderers/Form/Textarea.tsx b/src/renderers/Form/Textarea.tsx index 710dae1fd..9c837b4f4 100644 --- a/src/renderers/Form/Textarea.tsx +++ b/src/renderers/Form/Textarea.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {FormItem, FormControlProps, FormBaseControl} from './Item'; import cx from 'classnames'; import Textarea from '../../components/Textarea'; +import {Icon} from '../../components/icons'; import {findDOMNode} from 'react-dom'; import {autobind, ucFirst} from '../../utils/helper'; /** @@ -43,12 +44,24 @@ export interface TextareaControlSchema extends FormBaseControl { * 是否显示计数 */ showCounter?: boolean; + + /** + * 输入内容是否可清除 + */ + clearable?: boolean; + + /** + * 重置值 + */ + resetValue?: string; } export interface TextAreaProps extends FormControlProps { placeholder?: string; minRows?: number; maxRows?: number; + clearable?: boolean; + resetValue?: string; } export default class TextAreaControl extends React.Component< @@ -58,7 +71,9 @@ export default class TextAreaControl extends React.Component< static defaultProps: Partial = { minRows: 3, maxRows: 20, - trimContents: true + trimContents: true, + resetValue: '', + clearable: false }; state = { @@ -68,6 +83,14 @@ export default class TextAreaControl extends React.Component< input?: HTMLInputElement; inputRef = (ref: any) => (this.input = findDOMNode(ref) as HTMLInputElement); + valueToString(value: any) { + return typeof value === 'undefined' || value === null + ? '' + : typeof value === 'string' + ? value + : JSON.stringify(value); + } + focus() { if (!this.input) { return; @@ -132,6 +155,14 @@ export default class TextAreaControl extends React.Component< ); } + @autobind + handleClear() { + const {onChange, resetValue} = this.props; + + onChange?.(resetValue); + this.focus(); + } + render() { const { className, @@ -147,17 +178,10 @@ export default class TextAreaControl extends React.Component< borderMode, classnames: cx, maxLength, - showCounter + showCounter, + clearable } = this.props; - - let counter = showCounter - ? (typeof value === 'undefined' || value === null - ? '' - : typeof value === 'string' - ? value - : JSON.stringify(value) - ).length - : 0; + const counter = showCounter ? this.valueToString(value).length : 0; return (
+ {clearable && !disabled && value ? ( + + + + ) : null} + {showCounter ? ( {`${counter}${ typeof maxLength === 'number' && maxLength ? `/${maxLength}` : ''