Skip to content

Commit

Permalink
refactor: BaseInput (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
MadCcc authored Dec 14, 2023
1 parent 3c501e3 commit 063185a
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 125 deletions.
120 changes: 54 additions & 66 deletions src/BaseInput.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import clsx from 'classnames';
import type { FC, ReactElement } from 'react';
import type { FC, ReactElement, ReactNode } from 'react';
import React, { cloneElement, useRef } from 'react';
import type { BaseInputProps } from './interface';
import { hasAddon, hasPrefixSuffix } from './utils/commonUtils';

const BaseInput: FC<BaseInputProps> = (props) => {
const {
inputElement,
inputElement: inputEl,
children,
prefixCls,
prefix,
suffix,
Expand All @@ -29,6 +30,8 @@ const BaseInput: FC<BaseInputProps> = (props) => {
components,
} = props;

const inputElement = children ?? inputEl;

const AffixWrapperComponent = components?.affixWrapper || 'span';
const GroupWrapperComponent = components?.groupWrapper || 'span';
const WrapperComponent = components?.wrapper || 'span';
Expand All @@ -42,63 +45,51 @@ const BaseInput: FC<BaseInputProps> = (props) => {
}
};

// ================== Clear Icon ================== //
const getClearIcon = () => {
if (!allowClear) {
return null;
}
const needClear = !disabled && !readOnly && value;
const clearIconCls = `${prefixCls}-clear-icon`;
const iconNode =
typeof allowClear === 'object' && allowClear?.clearIcon
? allowClear.clearIcon
: '✖';

return (
<span
onClick={handleReset}
// Do not trigger onBlur when clear input
// https://github.com/ant-design/ant-design/issues/31200
onMouseDown={(e) => e.preventDefault()}
className={clsx(clearIconCls, {
[`${clearIconCls}-hidden`]: !needClear,
[`${clearIconCls}-has-suffix`]: !!suffix,
})}
role="button"
tabIndex={-1}
>
{iconNode}
</span>
);
};

let element: ReactElement = cloneElement(inputElement, {
value,
hidden,
className:
clsx(
inputElement.props?.className,
!hasPrefixSuffix(props) && !hasAddon(props) && className,
) || null,
style: {
...inputElement.props?.style,
...(!hasPrefixSuffix(props) && !hasAddon(props) ? style : {}),
},
});

// ================== Prefix & Suffix ================== //
if (hasPrefixSuffix(props)) {
// ================== Clear Icon ================== //
let clearIcon: ReactNode = null;
if (allowClear) {
const needClear = !disabled && !readOnly && value;
const clearIconCls = `${prefixCls}-clear-icon`;
const iconNode =
typeof allowClear === 'object' && allowClear?.clearIcon
? allowClear.clearIcon
: '✖';

clearIcon = (
<span
onClick={handleReset}
// Do not trigger onBlur when clear input
// https://github.com/ant-design/ant-design/issues/31200
onMouseDown={(e) => e.preventDefault()}
className={clsx(clearIconCls, {
[`${clearIconCls}-hidden`]: !needClear,
[`${clearIconCls}-has-suffix`]: !!suffix,
})}
role="button"
tabIndex={-1}
>
{iconNode}
</span>
);
}

const affixWrapperPrefixCls = `${prefixCls}-affix-wrapper`;
const affixWrapperCls = clsx(
affixWrapperPrefixCls,
{
[`${affixWrapperPrefixCls}-disabled`]: disabled,
[`${affixWrapperPrefixCls}-focused`]: focused,
[`${prefixCls}-disabled`]: disabled,
[`${affixWrapperPrefixCls}-disabled`]: disabled, // Not used, but keep it
[`${affixWrapperPrefixCls}-focused`]: focused, // Not used, but keep it
[`${affixWrapperPrefixCls}-readonly`]: readOnly,
[`${affixWrapperPrefixCls}-input-with-clear-btn`]:
suffix && allowClear && value,
},
!hasAddon(props) && className,
classes?.affixWrapper,
classNames?.affixWrapper,
);
Expand All @@ -108,19 +99,15 @@ const BaseInput: FC<BaseInputProps> = (props) => {
className={clsx(`${prefixCls}-suffix`, classNames?.suffix)}
style={styles?.suffix}
>
{getClearIcon()}
{clearIcon}
{suffix}
</span>
);

element = (
<AffixWrapperComponent
className={affixWrapperCls}
style={{
...(!hasAddon(props) ? style : undefined),
...styles?.affixWrapper,
}}
hidden={!hasAddon(props) && hidden}
style={styles?.affixWrapper}
onClick={onInputClick}
{...dataAttrs?.affixWrapper}
ref={containerRef}
Expand All @@ -133,10 +120,7 @@ const BaseInput: FC<BaseInputProps> = (props) => {
{prefix}
</span>
)}
{cloneElement(inputElement, {
value,
hidden: null,
})}
{element}
{suffixNode}
</AffixWrapperComponent>
);
Expand All @@ -151,31 +135,26 @@ const BaseInput: FC<BaseInputProps> = (props) => {
`${prefixCls}-wrapper`,
wrapperCls,
classes?.wrapper,
classNames?.wrapper,
);

const mergedGroupClassName = clsx(
`${prefixCls}-group-wrapper`,
className,
classes?.group,
classNames?.groupWrapper,
);

// Need another wrapper for changing display:table to display:inline-block
// and put style prop in wrapper
return (
<GroupWrapperComponent
className={mergedGroupClassName}
style={style}
hidden={hidden}
>
element = (
<GroupWrapperComponent className={mergedGroupClassName}>
<WrapperComponent className={mergedWrapperClassName}>
{addonBefore && (
<GroupAddonComponent className={addonCls}>
{addonBefore}
</GroupAddonComponent>
)}
{cloneElement(element, {
hidden: null,
})}
{element}
{addonAfter && (
<GroupAddonComponent className={addonCls}>
{addonAfter}
Expand All @@ -185,7 +164,16 @@ const BaseInput: FC<BaseInputProps> = (props) => {
</GroupWrapperComponent>
);
}
return element;

// `className` and `style` are always on the root element
return React.cloneElement(element, {
className: clsx(element.props?.className, className) || null,
style: {
...element.props?.style,
...style,
},
hidden,
});
};

export default BaseInput;
5 changes: 3 additions & 2 deletions src/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,6 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
{...rest}
prefixCls={prefixCls}
className={clsx(className, outOfRangeCls)}
inputElement={getInputElement()}
handleReset={handleReset}
value={formatValue}
focused={focused}
Expand All @@ -277,7 +276,9 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
classes={classes}
classNames={classNames}
styles={styles}
/>
>
{getInputElement()}
</BaseInput>
);
});

Expand Down
6 changes: 5 additions & 1 deletion src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export interface CommonInputProps {
affixWrapper?: string;
prefix?: string;
suffix?: string;
groupWrapper?: string;
wrapper?: string;
};
styles?: {
affixWrapper?: CSSProperties;
Expand All @@ -39,7 +41,8 @@ export type ValueType = InputHTMLAttributes<HTMLInputElement>['value'] | bigint;

export interface BaseInputProps extends CommonInputProps {
value?: ValueType;
inputElement: ReactElement;
/** @deprecated Use `children` instead */
inputElement?: ReactElement;
prefixCls?: string;
className?: string;
style?: CSSProperties;
Expand All @@ -58,6 +61,7 @@ export interface BaseInputProps extends CommonInputProps {
wrapper?: 'span' | 'div';
groupAddon?: 'span' | 'div';
};
children: ReactElement;
}

export type ShowCountFormatter = (args: {
Expand Down
Loading

0 comments on commit 063185a

Please sign in to comment.