Skip to content

Commit b14de98

Browse files
committed
chore: clear logic
1 parent d954431 commit b14de98

File tree

6 files changed

+90
-38
lines changed

6 files changed

+90
-38
lines changed

assets/patch.less

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
display: inline-flex;
66
align-items: center;
77
user-select: none;
8+
border: 1px solid blue;
9+
position: relative;
810

911
// Content 部分自动占据剩余宽度
1012
.@{select-prefix}-content {
@@ -37,6 +39,7 @@
3739
}
3840
}
3941

42+
.@{select-prefix}-content,
4043
.@{select-prefix}-input,
4144
.@{select-prefix}-placeholder {
4245
padding: 0;
@@ -52,4 +55,10 @@
5255
.@{select-prefix}-clear {
5356
flex: none;
5457
}
58+
59+
.@{select-prefix}-clear {
60+
position: absolute;
61+
top: 0;
62+
right: 0;
63+
}
5564
}

src/BaseSelect/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export interface BaseSelectProps extends BaseSelectPrivateProps, React.AriaAttri
190190
tokenSeparators?: string[];
191191

192192
// >>> Icons
193-
allowClear?: boolean | { clearIcon?: RenderNode };
193+
allowClear?: boolean | { clearIcon?: React.ReactNode };
194194
prefix?: React.ReactNode;
195195
suffix?: React.ReactNode;
196196
/**

src/SelectInput/Affix.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
import * as React from 'react';
22
import { useSelectInputContext } from './context';
33

4-
export interface AffixProps {
4+
export interface AffixProps extends React.HTMLAttributes<HTMLDivElement> {
55
type: 'prefix' | 'suffix' | 'clear';
66
children?: React.ReactNode;
77
}
88

99
export default function Affix(props: AffixProps) {
10-
const { type, children } = props;
10+
const { type, children, ...restProps } = props;
1111
const { prefixCls } = useSelectInputContext();
1212

1313
if (!children) {
1414
return null;
1515
}
1616

17-
return <div className={`${prefixCls}-${type}`}>{children}</div>;
17+
return (
18+
<div className={`${prefixCls}-${type}`} {...restProps}>
19+
{children}
20+
</div>
21+
);
1822
}

src/SelectInput/index.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import SelectContent from './Content';
55
import SelectInputContext, { type ContentContextProps } from './context';
66
import type { DisplayValueType, Mode } from '../interface';
77
import useBaseProps from '../hooks/useBaseProps';
8+
import { useEvent } from '@rc-component/util';
89

910
export interface SelectInputRef {
1011
focus: (options?: FocusOptions) => void;
@@ -87,13 +88,19 @@ export default React.forwardRef<SelectInputRef, SelectInputProps>(function Selec
8788
);
8889

8990
// ====================== Open ======================
90-
const onInternalMouseDown: SelectInputProps['onMouseDown'] = (event) => {
91+
const onInternalMouseDown: SelectInputProps['onMouseDown'] = useEvent((event) => {
9192
event.preventDefault();
92-
inputRef.current?.focus();
9393

94-
toggleOpen();
94+
if (!(event.nativeEvent as any)._select_lazy) {
95+
inputRef.current?.focus();
96+
toggleOpen();
97+
} else if (triggerOpen) {
98+
// Lazy should also close when click clear icon
99+
toggleOpen(false);
100+
}
101+
95102
onMouseDown?.(event);
96-
};
103+
});
97104

98105
const onInternalBlur: SelectInputProps['onBlur'] = (event) => {
99106
toggleOpen(false);
@@ -148,7 +155,17 @@ export default React.forwardRef<SelectInputRef, SelectInputProps>(function Selec
148155
{/* Suffix */}
149156
<Affix type="suffix">{suffix}</Affix>
150157
{/* Clear Icon */}
151-
{clearIcon && <Affix type="clear">{clearIcon}</Affix>}
158+
{clearIcon && (
159+
<Affix
160+
type="clear"
161+
onMouseDown={(e) => {
162+
// Mark to tell not trigger open or focus
163+
(e.nativeEvent as any)._select_lazy = true;
164+
}}
165+
>
166+
{clearIcon}
167+
</Affix>
168+
)}
152169
</div>
153170
</SelectInputContext.Provider>
154171
);

src/TransBtn.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ export interface TransBtnProps {
1212
children?: React.ReactNode;
1313
}
1414

15+
/**
16+
* Small wrapper for Select icons (clear/arrow/etc.).
17+
* Prevents default mousedown to avoid blurring or caret moves, and
18+
* renders a custom icon or a fallback icon span.
19+
*
20+
* DOM structure:
21+
* <span className={className} ...>
22+
* { icon || <span className={`${className}-icon`}>{children}</span> }
23+
* </span>
24+
*/
1525
const TransBtn: React.FC<TransBtnProps> = (props) => {
1626
const { className, style, customizeIcon, customizeIconProps, children, onMouseDown, onClick } =
1727
props;

src/hooks/useAllowClear.tsx

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import TransBtn from '../TransBtn';
22
import type { DisplayValueType, Mode, RenderNode } from '../interface';
3-
import React from 'react';
3+
import React, { useMemo } from 'react';
4+
5+
export interface AllowClearConfig {
6+
allowClear: boolean;
7+
clearIcon: React.ReactNode;
8+
}
49

510
export const useAllowClear = (
611
prefixCls: string,
@@ -11,38 +16,45 @@ export const useAllowClear = (
1116
disabled: boolean = false,
1217
mergedSearchValue?: string,
1318
mode?: Mode,
14-
) => {
15-
const mergedClearIcon = React.useMemo(() => {
16-
if (typeof allowClear === 'object') {
17-
return allowClear.clearIcon;
19+
): AllowClearConfig => {
20+
// Convert boolean to object first
21+
const allowClearConfig = useMemo<Partial<AllowClearConfig>>(() => {
22+
if (typeof allowClear === 'boolean') {
23+
return { allowClear };
1824
}
19-
if (clearIcon) {
20-
return clearIcon;
25+
if (allowClear && typeof allowClear === 'object') {
26+
return allowClear;
2127
}
22-
}, [allowClear, clearIcon]);
28+
return { allowClear: false };
29+
}, [allowClear]);
2330

24-
const mergedAllowClear = React.useMemo<boolean>(() => {
25-
if (
31+
return useMemo(() => {
32+
const mergedAllowClear =
2633
!disabled &&
27-
!!allowClear &&
34+
allowClearConfig.allowClear !== false &&
2835
(displayValues.length || mergedSearchValue) &&
29-
!(mode === 'combobox' && mergedSearchValue === '')
30-
) {
31-
return true;
32-
}
33-
return false;
34-
}, [allowClear, disabled, displayValues.length, mergedSearchValue, mode]);
36+
!(mode === 'combobox' && mergedSearchValue === '');
3537

36-
return {
37-
allowClear: mergedAllowClear,
38-
clearIcon: (
39-
<TransBtn
40-
className={`${prefixCls}-clear`}
41-
onMouseDown={onClearMouseDown}
42-
customizeIcon={mergedClearIcon}
43-
>
44-
×
45-
</TransBtn>
46-
),
47-
};
38+
return {
39+
allowClear: mergedAllowClear,
40+
clearIcon: mergedAllowClear ? (
41+
<TransBtn
42+
className={`${prefixCls}-clear`}
43+
onMouseDown={onClearMouseDown}
44+
customizeIcon={allowClearConfig.clearIcon || clearIcon}
45+
>
46+
×
47+
</TransBtn>
48+
) : null,
49+
};
50+
}, [
51+
allowClearConfig,
52+
clearIcon,
53+
disabled,
54+
displayValues.length,
55+
mergedSearchValue,
56+
mode,
57+
onClearMouseDown,
58+
prefixCls,
59+
]);
4860
};

0 commit comments

Comments
 (0)