Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(textarea): allowinputovermax支持超出字数限制可以输入 #838

Merged
merged 8 commits into from
Jun 7, 2022
3 changes: 3 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export type ScrollContainer = (() => ScrollContainerElement) | CSSSelector;
export type FormResetEvent = FormEvent<HTMLFormElement>;
export type FormSubmitEvent = FormEvent<HTMLFormElement>;

// 组件 TS 类型,暂定 any,可能调整为 () => JSX.Element
export type ComponentType = any;

export type Styles = CSSProperties;

export interface StyledProps {
Expand Down
32 changes: 22 additions & 10 deletions src/textarea/Textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,23 @@ const Textarea = forwardRef((props: TextareaProps, ref: TextareaRefInterface) =>
autosize,
status,
tips,
allowInputOverMax = false,
carolin913 marked this conversation as resolved.
Show resolved Hide resolved
...otherProps
} = props;

const [value = '', setValue] = useControlled(props, 'value', props.onChange);
const [isFocused, setIsFocused] = useState(false);
const [isOvermax, setIsOvermax] = useState(false);
const [textareaStyle, setTextareaStyle] = useState({});
const hasMaxcharacter = typeof maxcharacter !== 'undefined';
const textareaRef: React.RefObject<HTMLTextAreaElement> = useRef();
const wrapperRef: React.RefObject<HTMLDivElement> = useRef();
const currentLength = useMemo(() => (value ? String(value).length : 0), [value]);
const characterLength = useMemo(() => {
const characterInfo = getCharacterLength(String(value), maxcharacter);
const characterInfo = getCharacterLength(String(value), allowInputOverMax ? Infinity : maxcharacter);
if (typeof characterInfo === 'object') return characterInfo.length;
return characterInfo;
}, [value, maxcharacter]);
}, [value, allowInputOverMax, maxcharacter]);

const { classPrefix } = useConfig();

Expand Down Expand Up @@ -84,13 +86,20 @@ const Textarea = forwardRef((props: TextareaProps, ref: TextareaRefInterface) =>
function inputValueChangeHandle(e: React.FormEvent<HTMLTextAreaElement>) {
const { target } = e;
let val = (target as HTMLInputElement).value;
if (maxcharacter && maxcharacter >= 0) {
if (!allowInputOverMax && maxcharacter && maxcharacter >= 0) {
const stringInfo = getCharacterLength(val, maxcharacter);
val = typeof stringInfo === 'object' && stringInfo.characters;
}
setValue(val, { e });
}

const renderLimitText = (current: number, max: number) => (
<span className={`${classPrefix}-textarea__limit`}>
{isOvermax ? <span className={`${classPrefix}-textarea__tips--warning`}> {current}</span> : `${current}`}
{`/${max}`}
</span>
);

useEffect(() => {
// 当未设置 autosize 时,需要将 textarea 的 height 设置为 auto,以支持原生的 textarea rows 属性
if (autosize === false) {
Expand All @@ -102,6 +111,13 @@ const Textarea = forwardRef((props: TextareaProps, ref: TextareaRefInterface) =>
adjustTextareaHeight();
}, [adjustTextareaHeight, value]);

useEffect(() => {
if (allowInputOverMax) {
setIsOvermax(!!(maxlength && currentLength > maxlength) || !!(maxcharacter && characterLength > maxcharacter));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [characterLength]);

useImperativeHandle(ref as TextareaRefInterface, () => ({
currentElement: wrapperRef.current,
textareaElement: textareaRef.current,
Expand All @@ -118,19 +134,15 @@ const Textarea = forwardRef((props: TextareaProps, ref: TextareaRefInterface) =>
readOnly={readonly}
autoFocus={autofocus}
disabled={disabled}
maxLength={maxlength}
maxLength={allowInputOverMax ? Infinity : maxlength}
onChange={inputValueChangeHandle}
onKeyDown={(e) => onKeydown(e.currentTarget.value, { e })}
onKeyPress={(e) => onKeypress(e.currentTarget.value, { e })}
onKeyUp={(e) => onKeyup(e.currentTarget.value, { e })}
ref={textareaRef}
/>
{hasMaxcharacter ? (
<span className={`${classPrefix}-textarea__limit`}>{`${characterLength}/${maxcharacter}`}</span>
) : null}
{!hasMaxcharacter && maxlength ? (
<span className={`${classPrefix}-textarea__limit`}>{`${currentLength}/${maxlength}`}</span>
) : null}
{hasMaxcharacter && renderLimitText(characterLength, maxcharacter)}
{!hasMaxcharacter && maxlength && renderLimitText(currentLength, maxlength)}
{tips ? (
<div
className={classNames(`${classPrefix}-textarea__tips`, {
Expand Down
76 changes: 63 additions & 13 deletions src/textarea/__tests__/__snapshots__/textarea.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,70 @@ exports[`events.jsx 1`] = `

exports[`maxlength.jsx 1`] = `
<DocumentFragment>
<div
class="t-textarea"
>
<textarea
class="t-textarea__inner"
maxlength="20"
placeholder="请输入内容"
style="height: auto; min-height: auto;"
/>
<span
class="t-textarea__limit"
<div>
<div
class="t-textarea"
>
0/20
</span>
<textarea
class="t-textarea__inner"
maxlength="20"
placeholder="请输入内容,超出限制无法输入"
style="height: auto; min-height: auto;"
/>
<span
class="t-textarea__limit"
>
0/20
</span>
</div>
<br />
<div
class="t-textarea"
>
<textarea
class="t-textarea__inner"
maxlength="Infinity"
placeholder="请输入内容,超出限制可以输入"
style="height: auto; min-height: auto;"
/>
<span
class="t-textarea__limit"
>
0/20
</span>
</div>
<br />
<div
class="t-textarea"
>
<textarea
class="t-textarea__inner"
placeholder="请输入内容,一个中文汉字表示两个字符长度,超出限制无法输入"
style="height: auto; min-height: auto;"
/>
<span
class="t-textarea__limit"
>
0/20
</span>
</div>
<br />
<div
class="t-textarea"
>
<textarea
class="t-textarea__inner"
maxlength="Infinity"
placeholder="请输入内容,一个中文汉字表示两个字符长度,超出限制可以输入"
style="height: auto; min-height: auto;"
/>
<span
class="t-textarea__limit"
>
0/20
</span>
</div>
<br />
</div>
</DocumentFragment>
`;
Expand Down
11 changes: 10 additions & 1 deletion src/textarea/_example/maxlength.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,14 @@ import React from 'react';
import { Textarea } from 'tdesign-react';

export default function InputExample() {
return <Textarea placeholder="请输入内容" maxlength={20} />;
return <div>
carolin913 marked this conversation as resolved.
Show resolved Hide resolved
<Textarea placeholder="请输入内容,超出限制无法输入" maxlength={20} />
<br />
<Textarea placeholder="请输入内容,超出限制可以输入" maxlength={20} allowInputOverMax />
<br />
<Textarea placeholder="请输入内容,一个中文汉字表示两个字符长度,超出限制无法输入" maxcharacter={20} />
<br />
<Textarea placeholder="请输入内容,一个中文汉字表示两个字符长度,超出限制可以输入" maxcharacter={20} allowInputOverMax />
<br />
</div>;
}
1 change: 1 addition & 0 deletions src/textarea/defaultProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { TdTextareaProps } from './type';

export const textareaDefaultProps: TdTextareaProps = {
allowInputOverMax: false,
autofocus: false,
autosize: false,
disabled: false,
Expand Down
1 change: 1 addition & 0 deletions src/textarea/textarea.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
-- | -- | -- | -- | --
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
allowInputOverMax | Boolean | false | 超出maxlength或maxcharacter之后是否还允许输入 | N
autofocus | Boolean | false | 自动聚焦,拉起键盘 | N
autosize | Boolean / Object | false | 高度自动撑开。 autosize = true 表示组件高度自动撑开,同时,依旧允许手动拖高度。如果设置了 autosize.maxRows 或者 autosize.minRows 则不允许手动调整高度。TS 类型:`boolean | { minRows?: number; maxRows?: number }` | N
disabled | Boolean | false | 是否禁用文本框 | N
Expand Down
5 changes: 5 additions & 0 deletions src/textarea/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import { TNode } from '../common';
import { KeyboardEvent, FocusEvent, FormEvent } from 'react';

export interface TdTextareaProps {
/**
* 超出maxlength或maxcharacter之后是否还允许输入
* @default false
*/
allowInputOverMax?: boolean;
/**
* 自动聚焦,拉起键盘
* @default false
Expand Down
2 changes: 1 addition & 1 deletion test/ssr/__snapshots__/ssr.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ exports[`ssr snapshot test renders ./src/textarea/_example/base.jsx correctly 1`

exports[`ssr snapshot test renders ./src/textarea/_example/events.jsx correctly 1`] = `"<div class=\\"t-textarea\\"><textarea placeholder=\\"请输入内容\\" class=\\"t-textarea__inner\\"></textarea></div>"`;

exports[`ssr snapshot test renders ./src/textarea/_example/maxlength.jsx correctly 1`] = `"<div class=\\"t-textarea\\"><textarea placeholder=\\"请输入内容\\" class=\\"t-textarea__inner\\" maxLength=\\"20\\"></textarea><span class=\\"t-textarea__limit\\">0/20</span></div>"`;
exports[`ssr snapshot test renders ./src/textarea/_example/maxlength.jsx correctly 1`] = `"<div data-reactroot=\\"\\"><div class=\\"t-textarea\\"><textarea placeholder=\\"请输入内容,超出限制无法输入\\" class=\\"t-textarea__inner\\" maxLength=\\"20\\"></textarea><span class=\\"t-textarea__limit\\">0<!-- -->/20</span></div><br/><div class=\\"t-textarea\\"><textarea placeholder=\\"请输入内容,超出限制可以输入\\" class=\\"t-textarea__inner\\" maxLength=\\"Infinity\\"></textarea><span class=\\"t-textarea__limit\\">0<!-- -->/20</span></div><br/><div class=\\"t-textarea\\"><textarea placeholder=\\"请输入内容,一个中文汉字表示两个字符长度,超出限制无法输入\\" class=\\"t-textarea__inner\\"></textarea><span class=\\"t-textarea__limit\\">0<!-- -->/20</span></div><br/><div class=\\"t-textarea\\"><textarea placeholder=\\"请输入内容,一个中文汉字表示两个字符长度,超出限制可以输入\\" class=\\"t-textarea__inner\\" maxLength=\\"Infinity\\"></textarea><span class=\\"t-textarea__limit\\">0<!-- -->/20</span></div><br/></div>"`;

exports[`ssr snapshot test renders ./src/textarea/_example/status.jsx correctly 1`] = `"<div class=\\"t-textarea\\"><textarea placeholder=\\"normal\\" class=\\"t-textarea__inner\\"></textarea><div class=\\"t-textarea__tips t-textarea__tips--normal\\">正常提示</div></div><div style=\\"height:20px\\"></div><div class=\\"t-textarea\\"><textarea placeholder=\\"success\\" class=\\"t-textarea__inner t-is-success\\"></textarea><div class=\\"t-textarea__tips t-textarea__tips--success\\">成功提示</div></div><div style=\\"height:20px\\"></div><div class=\\"t-textarea\\"><textarea placeholder=\\"warning\\" class=\\"t-textarea__inner t-is-warning\\"></textarea><div class=\\"t-textarea__tips t-textarea__tips--warning\\">警告提示</div></div><div style=\\"height:20px\\"></div><div class=\\"t-textarea\\"><textarea placeholder=\\"error\\" class=\\"t-textarea__inner t-is-error\\"></textarea><div class=\\"t-textarea__tips t-textarea__tips--error\\">错误提示</div></div>"`;

Expand Down