From 1d100274540f52c74a0d47cb78cb80fdd3cf0e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=94=89LEntropy?= Date: Wed, 1 Jul 2026 20:20:43 +0800 Subject: [PATCH 1/3] feat(textarea): support clearable --- packages/components/textarea/Textarea.tsx | 59 +++++++++++++++- .../textarea/__tests__/textarea.test.tsx | 70 +++++++++++++++++-- .../textarea/_example/clearable.tsx | 21 ++++++ .../components/textarea/_usage/props.json | 8 ++- packages/components/textarea/defaultProps.ts | 1 + .../components/textarea/textarea.en-US.md | 10 ++- packages/components/textarea/textarea.md | 10 ++- packages/components/textarea/type.ts | 57 ++++++++++++--- 8 files changed, 215 insertions(+), 21 deletions(-) create mode 100644 packages/components/textarea/_example/clearable.tsx diff --git a/packages/components/textarea/Textarea.tsx b/packages/components/textarea/Textarea.tsx index a78dc72db0..539cd275de 100644 --- a/packages/components/textarea/Textarea.tsx +++ b/packages/components/textarea/Textarea.tsx @@ -1,5 +1,6 @@ import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; +import { CloseCircleFilledIcon as TdCloseCircleFilledIcon } from 'tdesign-icons-react'; import calcTextareaHeight from '@tdesign/common-js/utils/calcTextareaHeight'; import { getCharacterLength, getUnicodeLength, limitUnicodeMaxLength } from '@tdesign/common-js/utils/helper'; @@ -9,6 +10,7 @@ import useConfig from '../hooks/useConfig'; import useControlled from '../hooks/useControlled'; import useDefaultProps from '../hooks/useDefaultProps'; import useEventCallback from '../hooks/useEventCallback'; +import useGlobalIcon from '../hooks/useGlobalIcon'; import useIsomorphicLayoutEffect from '../hooks/useLayoutEffect'; import { textareaDefaultProps } from './defaultProps'; @@ -49,6 +51,8 @@ const Textarea = forwardRef((originalProps, tips, allowInputOverMax, rows, + clearable, + onClear, ...otherProps } = props; const hasMaxcharacter = typeof maxcharacter !== 'undefined'; @@ -56,6 +60,7 @@ const Textarea = forwardRef((originalProps, const [value = '', setValue] = useControlled(props, 'value', props.onChange); const [isFocused, setIsFocused] = useState(false); + const [isHover, setIsHover] = useState(false); const [isOvermax, setIsOvermax] = useState(false); const [textareaStyle, setTextareaStyle] = useState>(DEFAULT_TEXTAREA_STYLE); const [composingValue, setComposingValue] = useState(''); @@ -72,6 +77,10 @@ const Textarea = forwardRef((originalProps, }, [value, allowInputOverMax, maxcharacter]); const { classPrefix } = useConfig(); + const { CloseCircleFilledIcon } = useGlobalIcon({ + CloseCircleFilledIcon: TdCloseCircleFilledIcon, + }); + const isReadonly = props.readOnly || props.readonly; const textareaPropsNames = Object.keys(otherProps).filter( (key) => !/^on[A-Z]/.test(key) && !OMIT_PROPS.includes(key), @@ -136,10 +145,32 @@ const Textarea = forwardRef((originalProps, } } setComposingValue(val); - setValue(val, { e }); + setValue(val, { e, trigger: 'input' }); } } + function handleClear(e: React.MouseEvent) { + setValue('', { e, trigger: 'clear' }); + onClear?.({ e }); + textareaRef.current?.focus(); + adjustTextareaHeight(); + } + + function handleClearMouseDown(e: React.MouseEvent) { + e.preventDefault(); + } + + function handleFocus(e: React.FocusEvent) { + if (disabled) return; + setIsFocused(true); + props.onFocus?.(e.currentTarget.value, { e }); + } + + function handleBlur(e: React.FocusEvent) { + setIsFocused(false); + props.onBlur?.(e.currentTarget.value, { e }); + } + function handleCompositionStart() { composingRef.current = true; } @@ -214,9 +245,20 @@ const Textarea = forwardRef((originalProps, hasMaxcharacter ? characterLength : currentLength, hasMaxcharacter ? maxcharacter : maxlength, ); + const showClear = Boolean(clearable && value && !disabled && !isReadonly && isHover); return ( -
+
setIsHover(true)} + onMouseLeave={() => setIsHover(false)} + > + + + +
+
+`; + exports[`csr snapshot test > csr test packages/components/textarea/_example/events.tsx 1`] = `
csr test packages/components/textarea/_example/type class="t-space-item" >
请选择单张图片文件上传
提交
重置
"`; +exports[`ssr snapshot test > ssr test packages/components/form/_example/disabled.tsx 1`] = `"
请选择单张图片文件上传
提交
重置
"`; exports[`ssr snapshot test > ssr test packages/components/form/_example/error-message.tsx 1`] = `"
一句话介绍自己
"`; @@ -154795,11 +154825,13 @@ exports[`ssr snapshot test > ssr test packages/components/tag-input/_example/the exports[`ssr snapshot test > ssr test packages/components/textarea/_example/base.tsx 1`] = `"
"`; +exports[`ssr snapshot test > ssr test packages/components/textarea/_example/clearable.tsx 1`] = `"
"`; + exports[`ssr snapshot test > ssr test packages/components/textarea/_example/events.tsx 1`] = `"
"`; exports[`ssr snapshot test > ssr test packages/components/textarea/_example/maxlength.tsx 1`] = `"
这里可以放一些提示文字
0/20
0/20
0/20
0/20
0/20
"`; -exports[`ssr snapshot test > ssr test packages/components/textarea/_example/type.tsx 1`] = `"
正常提示
成功提示
警告提示
错误提示
"`; +exports[`ssr snapshot test > ssr test packages/components/textarea/_example/type.tsx 1`] = `"
正常提示
成功提示
警告提示
错误提示
"`; exports[`ssr snapshot test > ssr test packages/components/time-picker/_example/disabled.tsx 1`] = `"

禁用整个选择器

禁用指定时间

"`; diff --git a/test/snap/__snapshots__/ssr.test.jsx.snap b/test/snap/__snapshots__/ssr.test.jsx.snap index f327059e29..02432df365 100644 --- a/test/snap/__snapshots__/ssr.test.jsx.snap +++ b/test/snap/__snapshots__/ssr.test.jsx.snap @@ -428,7 +428,7 @@ exports[`ssr snapshot test > ssr test packages/components/form/_example/custom-v exports[`ssr snapshot test > ssr test packages/components/form/_example/customized-form-controls.tsx 1`] = `"
"`; -exports[`ssr snapshot test > ssr test packages/components/form/_example/disabled.tsx 1`] = `"
请选择单张图片文件上传
提交
重置
"`; +exports[`ssr snapshot test > ssr test packages/components/form/_example/disabled.tsx 1`] = `"
请选择单张图片文件上传
提交
重置
"`; exports[`ssr snapshot test > ssr test packages/components/form/_example/error-message.tsx 1`] = `"
一句话介绍自己
"`; @@ -1116,11 +1116,13 @@ exports[`ssr snapshot test > ssr test packages/components/tag-input/_example/the exports[`ssr snapshot test > ssr test packages/components/textarea/_example/base.tsx 1`] = `"
"`; +exports[`ssr snapshot test > ssr test packages/components/textarea/_example/clearable.tsx 1`] = `"
"`; + exports[`ssr snapshot test > ssr test packages/components/textarea/_example/events.tsx 1`] = `"
"`; exports[`ssr snapshot test > ssr test packages/components/textarea/_example/maxlength.tsx 1`] = `"
这里可以放一些提示文字
0/20
0/20
0/20
0/20
0/20
"`; -exports[`ssr snapshot test > ssr test packages/components/textarea/_example/type.tsx 1`] = `"
正常提示
成功提示
警告提示
错误提示
"`; +exports[`ssr snapshot test > ssr test packages/components/textarea/_example/type.tsx 1`] = `"
正常提示
成功提示
警告提示
错误提示
"`; exports[`ssr snapshot test > ssr test packages/components/time-picker/_example/disabled.tsx 1`] = `"

禁用整个选择器

禁用指定时间

"`; From 43a0aca50fb003dae09bed920dcf76cde1a5f7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=94=89LEntropy?= Date: Thu, 2 Jul 2026 08:54:08 +0800 Subject: [PATCH 3/3] fix(textarea): keep wrapper status classes unchanged --- packages/components/textarea/Textarea.tsx | 2 -- test/snap/__snapshots__/csr.test.jsx.snap | 10 +++++----- test/snap/__snapshots__/ssr.test.jsx.snap | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/components/textarea/Textarea.tsx b/packages/components/textarea/Textarea.tsx index 539cd275de..ccde847f11 100644 --- a/packages/components/textarea/Textarea.tsx +++ b/packages/components/textarea/Textarea.tsx @@ -253,8 +253,6 @@ const Textarea = forwardRef((originalProps, ref={wrapperRef} className={classNames(`${classPrefix}-textarea`, className, { [`${classPrefix}-textarea--clearable`]: clearable, - [`${classPrefix}-is-disabled`]: disabled, - [`${classPrefix}-is-readonly`]: isReadonly, })} onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)} diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index 491f9462c2..a52d636898 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -50034,7 +50034,7 @@ exports[`csr snapshot test > csr test packages/components/form/_example/disabled class="t-form__controls-content" >
请选择单张图片文件上传
提交
重置
"`; +exports[`ssr snapshot test > ssr test packages/components/form/_example/disabled.tsx 1`] = `"
请选择单张图片文件上传
提交
重置
"`; exports[`ssr snapshot test > ssr test packages/components/form/_example/error-message.tsx 1`] = `"
一句话介绍自己
"`; @@ -154831,7 +154831,7 @@ exports[`ssr snapshot test > ssr test packages/components/textarea/_example/even exports[`ssr snapshot test > ssr test packages/components/textarea/_example/maxlength.tsx 1`] = `"
这里可以放一些提示文字
0/20
0/20
0/20
0/20
0/20
"`; -exports[`ssr snapshot test > ssr test packages/components/textarea/_example/type.tsx 1`] = `"
正常提示
成功提示
警告提示
错误提示
"`; +exports[`ssr snapshot test > ssr test packages/components/textarea/_example/type.tsx 1`] = `"
正常提示
成功提示
警告提示
错误提示
"`; exports[`ssr snapshot test > ssr test packages/components/time-picker/_example/disabled.tsx 1`] = `"

禁用整个选择器

禁用指定时间

"`; diff --git a/test/snap/__snapshots__/ssr.test.jsx.snap b/test/snap/__snapshots__/ssr.test.jsx.snap index 02432df365..c40a4e5aa2 100644 --- a/test/snap/__snapshots__/ssr.test.jsx.snap +++ b/test/snap/__snapshots__/ssr.test.jsx.snap @@ -428,7 +428,7 @@ exports[`ssr snapshot test > ssr test packages/components/form/_example/custom-v exports[`ssr snapshot test > ssr test packages/components/form/_example/customized-form-controls.tsx 1`] = `"
"`; -exports[`ssr snapshot test > ssr test packages/components/form/_example/disabled.tsx 1`] = `"
请选择单张图片文件上传
提交
重置
"`; +exports[`ssr snapshot test > ssr test packages/components/form/_example/disabled.tsx 1`] = `"
请选择单张图片文件上传
提交
重置
"`; exports[`ssr snapshot test > ssr test packages/components/form/_example/error-message.tsx 1`] = `"
一句话介绍自己
"`; @@ -1122,7 +1122,7 @@ exports[`ssr snapshot test > ssr test packages/components/textarea/_example/even exports[`ssr snapshot test > ssr test packages/components/textarea/_example/maxlength.tsx 1`] = `"
这里可以放一些提示文字
0/20
0/20
0/20
0/20
0/20
"`; -exports[`ssr snapshot test > ssr test packages/components/textarea/_example/type.tsx 1`] = `"
正常提示
成功提示
警告提示
错误提示
"`; +exports[`ssr snapshot test > ssr test packages/components/textarea/_example/type.tsx 1`] = `"
正常提示
成功提示
警告提示
错误提示
"`; exports[`ssr snapshot test > ssr test packages/components/time-picker/_example/disabled.tsx 1`] = `"

禁用整个选择器

禁用指定时间

"`;