Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 41 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,50 +58,50 @@ Then open `http://localhost:8000`.

### Input

| Property | Type | Default | Description |
| ------------ | ---------------------------------------------------------- | ---------- | --------------------------------------------------------------- |
| addonAfter | `ReactNode` | - | Element displayed after the input. |
| addonBefore | `ReactNode` | - | Element displayed before the input. |
| allowClear | `boolean \| { disabled?: boolean; clearIcon?: ReactNode }` | `false` | Show a clear button for the current value. |
| className | `string` | - | Class name for the input element. |
| classNames | `InputProps['classNames']` | - | Semantic class names for input slots. |
| count | `CountConfig` | - | Custom count strategy, limit, visibility, and exceed formatter. |
| defaultValue | `string \| number \| readonly string[] \| bigint` | - | Initial input value. |
| disabled | `boolean` | `false` | Disable the input. |
| htmlSize | `number` | - | Native input `size` attribute. |
| maxLength | `number` | - | Native input `maxLength` attribute. |
| prefix | `ReactNode` | - | Prefix content inside the input wrapper. |
| prefixCls | `string` | `rc-input` | Class name prefix. |
| showCount | `boolean \| { formatter: ShowCountFormatter }` | `false` | Show character count. Prefer `count.show` for new code. |
| styles | `InputProps['styles']` | - | Semantic styles for input slots. |
| suffix | `ReactNode` | - | Suffix content inside the input wrapper. |
| type | `InputProps['type']` | `text` | Native input type. Use `TextArea` for textarea behavior. |
| value | `string \| number \| readonly string[] \| bigint` | - | Controlled input value. |
| onChange | `React.ChangeEventHandler<HTMLInputElement>` | - | Triggered when the value changes. |
| onClear | `() => void` | - | Triggered when the clear button is clicked. |
| onPressEnter | `React.KeyboardEventHandler<HTMLInputElement>` | - | Triggered when Enter is pressed. |
| Property | Type | Default | Description |
| ------------ | ----------------------------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------- |
| addonAfter | `ReactNode` | - | Element displayed after the input. |
| addonBefore | `ReactNode` | - | Element displayed before the input. |
| allowClear | `boolean \| { disabled?: boolean; clearIcon?: ReactNode; tabIndex?: number }` | `false` | Show a clear button for the current value. Set `tabIndex: -1` to skip it during Tab navigation. |
| className | `string` | - | Class name for the input element. |
| classNames | `InputProps['classNames']` | - | Semantic class names for input slots. |
| count | `CountConfig` | - | Custom count strategy, limit, visibility, and exceed formatter. |
| defaultValue | `string \| number \| readonly string[] \| bigint` | - | Initial input value. |
| disabled | `boolean` | `false` | Disable the input. |
| htmlSize | `number` | - | Native input `size` attribute. |
| maxLength | `number` | - | Native input `maxLength` attribute. |
| prefix | `ReactNode` | - | Prefix content inside the input wrapper. |
| prefixCls | `string` | `rc-input` | Class name prefix. |
| showCount | `boolean \| { formatter: ShowCountFormatter }` | `false` | Show character count. Prefer `count.show` for new code. |
| styles | `InputProps['styles']` | - | Semantic styles for input slots. |
| suffix | `ReactNode` | - | Suffix content inside the input wrapper. |
| type | `InputProps['type']` | `text` | Native input type. Use `TextArea` for textarea behavior. |
| value | `string \| number \| readonly string[] \| bigint` | - | Controlled input value. |
| onChange | `React.ChangeEventHandler<HTMLInputElement>` | - | Triggered when the value changes. |
| onClear | `() => void` | - | Triggered when the clear button is clicked. |
| onPressEnter | `React.KeyboardEventHandler<HTMLInputElement>` | - | Triggered when Enter is pressed. |

### TextArea

| Property | Type | Default | Description |
| ------------ | ---------------------------------------------------------- | ------------- | --------------------------------------------------------------- |
| allowClear | `boolean \| { disabled?: boolean; clearIcon?: ReactNode }` | `false` | Show a clear button for the current value. |
| autoSize | `boolean \| { minRows?: number; maxRows?: number }` | `false` | Auto resize height by content. |
| className | `string` | - | Class name for the textarea. |
| classNames | `TextAreaProps['classNames']` | - | Semantic class names for textarea slots. |
| count | `CountConfig` | - | Custom count strategy, limit, visibility, and exceed formatter. |
| defaultValue | `string \| number \| readonly string[] \| bigint` | - | Initial textarea value. |
| maxLength | `number` | - | Native textarea `maxLength` attribute. |
| prefixCls | `string` | `rc-textarea` | Class name prefix. |
| showCount | `boolean \| { formatter: ShowCountFormatter }` | `false` | Show character count. Prefer `count.show` for new code. |
| style | `React.CSSProperties` | - | Inline styles for the textarea. |
| styles | `TextAreaProps['styles']` | - | Semantic styles for textarea slots. |
| suffix | `ReactNode` | - | Suffix content inside the textarea wrapper. |
| value | `string \| number \| readonly string[] \| bigint` | - | Controlled textarea value. |
| onChange | `React.ChangeEventHandler<HTMLTextAreaElement>` | - | Triggered when the value changes. |
| onClear | `() => void` | - | Triggered when the clear button is clicked. |
| onPressEnter | `React.KeyboardEventHandler<HTMLTextAreaElement>` | - | Triggered when Enter is pressed. |
| onResize | `(size: { width: number; height: number }) => void` | - | Triggered when textarea size changes. |
| Property | Type | Default | Description |
| ------------ | ----------------------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------- |
| allowClear | `boolean \| { disabled?: boolean; clearIcon?: ReactNode; tabIndex?: number }` | `false` | Show a clear button for the current value. Set `tabIndex: -1` to skip it during Tab navigation. |
| autoSize | `boolean \| { minRows?: number; maxRows?: number }` | `false` | Auto resize height by content. |
| className | `string` | - | Class name for the textarea. |
| classNames | `TextAreaProps['classNames']` | - | Semantic class names for textarea slots. |
| count | `CountConfig` | - | Custom count strategy, limit, visibility, and exceed formatter. |
| defaultValue | `string \| number \| readonly string[] \| bigint` | - | Initial textarea value. |
| maxLength | `number` | - | Native textarea `maxLength` attribute. |
| prefixCls | `string` | `rc-textarea` | Class name prefix. |
| showCount | `boolean \| { formatter: ShowCountFormatter }` | `false` | Show character count. Prefer `count.show` for new code. |
| style | `React.CSSProperties` | - | Inline styles for the textarea. |
| styles | `TextAreaProps['styles']` | - | Semantic styles for textarea slots. |
| suffix | `ReactNode` | - | Suffix content inside the textarea wrapper. |
| value | `string \| number \| readonly string[] \| bigint` | - | Controlled textarea value. |
| onChange | `React.ChangeEventHandler<HTMLTextAreaElement>` | - | Triggered when the value changes. |
| onClear | `() => void` | - | Triggered when the clear button is clicked. |
| onPressEnter | `React.KeyboardEventHandler<HTMLTextAreaElement>` | - | Triggered when Enter is pressed. |
| onResize | `(size: { width: number; height: number }) => void` | - | Triggered when textarea size changes. |

### Refs

Expand Down
22 changes: 10 additions & 12 deletions src/BaseInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { clsx } from 'clsx';
import { isReactRenderable } from '@rc-component/util';
import type { ReactElement, ReactNode } from 'react';
import React, { cloneElement, useRef } from 'react';
import useAllowClear from './hooks/useAllowClear';
import type { BaseInputProps } from './interface';
import { hasAddon, hasPrefixSuffix } from './utils/commonUtils';

Expand Down Expand Up @@ -37,6 +38,7 @@ const BaseInput = React.forwardRef<HolderRef, BaseInputProps>((props, ref) => {
onClear,
} = props;

const [showAllowClear, allowClearConfig] = useAllowClear(allowClear);
const inputElement = children ?? inputEl;

const AffixWrapperComponent = components?.affixWrapper || 'span';
Expand Down Expand Up @@ -74,22 +76,18 @@ const BaseInput = React.forwardRef<HolderRef, BaseInputProps>((props, ref) => {
if (hasAffix) {
// ================== Clear Icon ================== //
let clearIcon: ReactNode = null;
if (allowClear) {
if (showAllowClear) {
const needClear =
!disabled &&
!readOnly &&
value &&
!(typeof allowClear === 'object' && allowClear.disabled);
!disabled && !readOnly && value && !allowClearConfig.disabled;
const clearIconCls = `${prefixCls}-clear-icon`;
const iconNode =
typeof allowClear === 'object' &&
isReactRenderable(allowClear?.clearIcon)
? allowClear.clearIcon
: '✖';
const iconNode = isReactRenderable(allowClearConfig.clearIcon)
? allowClearConfig.clearIcon
: '✖';

clearIcon = (
<button
type="button"
tabIndex={allowClearConfig.tabIndex}
onClick={(event) => {
handleReset?.(event);
onClear?.();
Expand Down Expand Up @@ -121,14 +119,14 @@ const BaseInput = React.forwardRef<HolderRef, BaseInputProps>((props, ref) => {
[`${affixWrapperPrefixCls}-focused`]: focused, // Not used, but keep it
[`${affixWrapperPrefixCls}-readonly`]: readOnly,
[`${affixWrapperPrefixCls}-input-with-clear-btn`]:
isReactRenderable(suffix) && allowClear && value,
isReactRenderable(suffix) && showAllowClear && value,
},
classes?.affixWrapper,
classNames?.affixWrapper,
classNames?.variant,
);

const suffixNode = (isReactRenderable(suffix) || allowClear) && (
const suffixNode = (isReactRenderable(suffix) || showAllowClear) && (
<span
className={clsx(`${prefixCls}-suffix`, classNames?.suffix)}
style={styles?.suffix}
Expand Down
12 changes: 12 additions & 0 deletions src/hooks/useAllowClear.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as React from 'react';
import type { CommonInputProps } from '../interface';

export default function useAllowClear(
allowClear: CommonInputProps['allowClear'],
) {
return React.useMemo(
() =>
[!!allowClear, typeof allowClear === 'object' ? allowClear : {}] as const,
[allowClear],
);
}
6 changes: 3 additions & 3 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export interface CommonInputProps {
suffix?: CSSProperties;
clear?: CSSProperties;
};
allowClear?: boolean | { disabled?: boolean; clearIcon?: ReactNode };
allowClear?:
boolean | { disabled?: boolean; clearIcon?: ReactNode; tabIndex?: number };
}

type DataAttr = Record<`data-${string}`, string>;
Expand Down Expand Up @@ -172,8 +173,7 @@ export interface ResizableTextAreaRef {
}

export type ValueOrTextAreaValue =
| TextareaHTMLAttributes<HTMLTextAreaElement>['value']
| bigint;
TextareaHTMLAttributes<HTMLTextAreaElement>['value'] | bigint;

export type TextAreaProps = Omit<
TextareaHTMLAttributes<HTMLTextAreaElement>,
Expand Down
Loading
Loading