Skip to content

Commit 063185a

Browse files
authored
refactor: BaseInput (#55)
1 parent 3c501e3 commit 063185a

4 files changed

Lines changed: 107 additions & 125 deletions

File tree

src/BaseInput.tsx

Lines changed: 54 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import clsx from 'classnames';
2-
import type { FC, ReactElement } from 'react';
2+
import type { FC, ReactElement, ReactNode } from 'react';
33
import React, { cloneElement, useRef } from 'react';
44
import type { BaseInputProps } from './interface';
55
import { hasAddon, hasPrefixSuffix } from './utils/commonUtils';
66

77
const BaseInput: FC<BaseInputProps> = (props) => {
88
const {
9-
inputElement,
9+
inputElement: inputEl,
10+
children,
1011
prefixCls,
1112
prefix,
1213
suffix,
@@ -29,6 +30,8 @@ const BaseInput: FC<BaseInputProps> = (props) => {
2930
components,
3031
} = props;
3132

33+
const inputElement = children ?? inputEl;
34+
3235
const AffixWrapperComponent = components?.affixWrapper || 'span';
3336
const GroupWrapperComponent = components?.groupWrapper || 'span';
3437
const WrapperComponent = components?.wrapper || 'span';
@@ -42,63 +45,51 @@ const BaseInput: FC<BaseInputProps> = (props) => {
4245
}
4346
};
4447

45-
// ================== Clear Icon ================== //
46-
const getClearIcon = () => {
47-
if (!allowClear) {
48-
return null;
49-
}
50-
const needClear = !disabled && !readOnly && value;
51-
const clearIconCls = `${prefixCls}-clear-icon`;
52-
const iconNode =
53-
typeof allowClear === 'object' && allowClear?.clearIcon
54-
? allowClear.clearIcon
55-
: '✖';
56-
57-
return (
58-
<span
59-
onClick={handleReset}
60-
// Do not trigger onBlur when clear input
61-
// https://github.com/ant-design/ant-design/issues/31200
62-
onMouseDown={(e) => e.preventDefault()}
63-
className={clsx(clearIconCls, {
64-
[`${clearIconCls}-hidden`]: !needClear,
65-
[`${clearIconCls}-has-suffix`]: !!suffix,
66-
})}
67-
role="button"
68-
tabIndex={-1}
69-
>
70-
{iconNode}
71-
</span>
72-
);
73-
};
74-
7548
let element: ReactElement = cloneElement(inputElement, {
7649
value,
77-
hidden,
78-
className:
79-
clsx(
80-
inputElement.props?.className,
81-
!hasPrefixSuffix(props) && !hasAddon(props) && className,
82-
) || null,
83-
style: {
84-
...inputElement.props?.style,
85-
...(!hasPrefixSuffix(props) && !hasAddon(props) ? style : {}),
86-
},
8750
});
8851

8952
// ================== Prefix & Suffix ================== //
9053
if (hasPrefixSuffix(props)) {
54+
// ================== Clear Icon ================== //
55+
let clearIcon: ReactNode = null;
56+
if (allowClear) {
57+
const needClear = !disabled && !readOnly && value;
58+
const clearIconCls = `${prefixCls}-clear-icon`;
59+
const iconNode =
60+
typeof allowClear === 'object' && allowClear?.clearIcon
61+
? allowClear.clearIcon
62+
: '✖';
63+
64+
clearIcon = (
65+
<span
66+
onClick={handleReset}
67+
// Do not trigger onBlur when clear input
68+
// https://github.com/ant-design/ant-design/issues/31200
69+
onMouseDown={(e) => e.preventDefault()}
70+
className={clsx(clearIconCls, {
71+
[`${clearIconCls}-hidden`]: !needClear,
72+
[`${clearIconCls}-has-suffix`]: !!suffix,
73+
})}
74+
role="button"
75+
tabIndex={-1}
76+
>
77+
{iconNode}
78+
</span>
79+
);
80+
}
81+
9182
const affixWrapperPrefixCls = `${prefixCls}-affix-wrapper`;
9283
const affixWrapperCls = clsx(
9384
affixWrapperPrefixCls,
9485
{
95-
[`${affixWrapperPrefixCls}-disabled`]: disabled,
96-
[`${affixWrapperPrefixCls}-focused`]: focused,
86+
[`${prefixCls}-disabled`]: disabled,
87+
[`${affixWrapperPrefixCls}-disabled`]: disabled, // Not used, but keep it
88+
[`${affixWrapperPrefixCls}-focused`]: focused, // Not used, but keep it
9789
[`${affixWrapperPrefixCls}-readonly`]: readOnly,
9890
[`${affixWrapperPrefixCls}-input-with-clear-btn`]:
9991
suffix && allowClear && value,
10092
},
101-
!hasAddon(props) && className,
10293
classes?.affixWrapper,
10394
classNames?.affixWrapper,
10495
);
@@ -108,19 +99,15 @@ const BaseInput: FC<BaseInputProps> = (props) => {
10899
className={clsx(`${prefixCls}-suffix`, classNames?.suffix)}
109100
style={styles?.suffix}
110101
>
111-
{getClearIcon()}
102+
{clearIcon}
112103
{suffix}
113104
</span>
114105
);
115106

116107
element = (
117108
<AffixWrapperComponent
118109
className={affixWrapperCls}
119-
style={{
120-
...(!hasAddon(props) ? style : undefined),
121-
...styles?.affixWrapper,
122-
}}
123-
hidden={!hasAddon(props) && hidden}
110+
style={styles?.affixWrapper}
124111
onClick={onInputClick}
125112
{...dataAttrs?.affixWrapper}
126113
ref={containerRef}
@@ -133,10 +120,7 @@ const BaseInput: FC<BaseInputProps> = (props) => {
133120
{prefix}
134121
</span>
135122
)}
136-
{cloneElement(inputElement, {
137-
value,
138-
hidden: null,
139-
})}
123+
{element}
140124
{suffixNode}
141125
</AffixWrapperComponent>
142126
);
@@ -151,31 +135,26 @@ const BaseInput: FC<BaseInputProps> = (props) => {
151135
`${prefixCls}-wrapper`,
152136
wrapperCls,
153137
classes?.wrapper,
138+
classNames?.wrapper,
154139
);
155140

156141
const mergedGroupClassName = clsx(
157142
`${prefixCls}-group-wrapper`,
158-
className,
159143
classes?.group,
144+
classNames?.groupWrapper,
160145
);
161146

162147
// Need another wrapper for changing display:table to display:inline-block
163148
// and put style prop in wrapper
164-
return (
165-
<GroupWrapperComponent
166-
className={mergedGroupClassName}
167-
style={style}
168-
hidden={hidden}
169-
>
149+
element = (
150+
<GroupWrapperComponent className={mergedGroupClassName}>
170151
<WrapperComponent className={mergedWrapperClassName}>
171152
{addonBefore && (
172153
<GroupAddonComponent className={addonCls}>
173154
{addonBefore}
174155
</GroupAddonComponent>
175156
)}
176-
{cloneElement(element, {
177-
hidden: null,
178-
})}
157+
{element}
179158
{addonAfter && (
180159
<GroupAddonComponent className={addonCls}>
181160
{addonAfter}
@@ -185,7 +164,16 @@ const BaseInput: FC<BaseInputProps> = (props) => {
185164
</GroupWrapperComponent>
186165
);
187166
}
188-
return element;
167+
168+
// `className` and `style` are always on the root element
169+
return React.cloneElement(element, {
170+
className: clsx(element.props?.className, className) || null,
171+
style: {
172+
...element.props?.style,
173+
...style,
174+
},
175+
hidden,
176+
});
189177
};
190178

191179
export default BaseInput;

src/Input.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,6 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
267267
{...rest}
268268
prefixCls={prefixCls}
269269
className={clsx(className, outOfRangeCls)}
270-
inputElement={getInputElement()}
271270
handleReset={handleReset}
272271
value={formatValue}
273272
focused={focused}
@@ -277,7 +276,9 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
277276
classes={classes}
278277
classNames={classNames}
279278
styles={styles}
280-
/>
279+
>
280+
{getInputElement()}
281+
</BaseInput>
281282
);
282283
});
283284

src/interface.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export interface CommonInputProps {
2424
affixWrapper?: string;
2525
prefix?: string;
2626
suffix?: string;
27+
groupWrapper?: string;
28+
wrapper?: string;
2729
};
2830
styles?: {
2931
affixWrapper?: CSSProperties;
@@ -39,7 +41,8 @@ export type ValueType = InputHTMLAttributes<HTMLInputElement>['value'] | bigint;
3941

4042
export interface BaseInputProps extends CommonInputProps {
4143
value?: ValueType;
42-
inputElement: ReactElement;
44+
/** @deprecated Use `children` instead */
45+
inputElement?: ReactElement;
4346
prefixCls?: string;
4447
className?: string;
4548
style?: CSSProperties;
@@ -58,6 +61,7 @@ export interface BaseInputProps extends CommonInputProps {
5861
wrapper?: 'span' | 'div';
5962
groupAddon?: 'span' | 'div';
6063
};
64+
children: ReactElement;
6165
}
6266

6367
export type ShowCountFormatter = (args: {

0 commit comments

Comments
 (0)