Skip to content

Commit 78997ac

Browse files
committed
chore: optimize
1 parent 254256b commit 78997ac

File tree

3 files changed

+94
-88
lines changed

3 files changed

+94
-88
lines changed

packages/components/select/base/Select.tsx

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,21 @@ const Select = forwardRefWithStatics(
106106
const [isScrolling, toggleIsScrolling] = useState(false);
107107

108108
const name = `${classPrefix}-select`; // t-select
109-
110-
const [showPopup, setShowPopup] = useControlled(props, 'popupVisible', onPopupVisibleChange);
111109
const [inputValue, onInputChange] = useControlled(props, 'inputValue', props.onInputChange);
112110

111+
const [innerPopupVisible, setInnerPopupVisible] = useControlled(props, 'popupVisible', onPopupVisibleChange);
112+
113+
const handlePopupVisibleChange = (visible: boolean, ctx: PopupVisibleChangeContext) => {
114+
if (disabled) return;
115+
visible ? toggleIsScrolling(false) : onInputChange('', { trigger: 'blur' });
116+
setInnerPopupVisible(visible, ctx);
117+
};
118+
113119
const { currentOptions, setCurrentOptions, tmpPropOptions, valueToOption, selectedOptions, flattenedOptions } =
114120
useOptions(keys, options, children, valueType, value, reserveKeyword);
115121

116122
const onCheckAllChange = useCallback(
117-
(checkAll: boolean, e: React.MouseEvent<HTMLLIElement>) => {
123+
(checkAll: boolean, e: React.MouseEvent<HTMLLIElement> | React.KeyboardEvent<HTMLInputElement>) => {
118124
const isDisabledCheckAll = (opt: TdOptionProps) => opt.checkAll && opt.disabled;
119125
if (!multiple || currentOptions.some((opt) => !isSelectOptionGroup(opt) && isDisabledCheckAll(opt))) return;
120126

@@ -181,18 +187,6 @@ const Select = forwardRefWithStatics(
181187
[currentOptions, keys, multiple, onChange, value, valueToOption, valueType],
182188
);
183189

184-
const { handleKeyDown, hoverOption } = useKeyboardControl({
185-
displayOptions: flattenedOptions as TdOptionProps[],
186-
max,
187-
multiple,
188-
setInnerPopupVisible: setShowPopup,
189-
innerPopupVisible: showPopup,
190-
setInnerValue: onChange,
191-
innerValue: value,
192-
onCheckAllChange,
193-
selectInputRef,
194-
});
195-
196190
const selectedLabel = useMemo(() => {
197191
const { labelKey } = getKeyMapping(keys);
198192
if (multiple) {
@@ -201,12 +195,6 @@ const Select = forwardRefWithStatics(
201195
return get(selectedOptions[0] || {}, labelKey) || undefined;
202196
}, [selectedOptions, keys, multiple]);
203197

204-
const handleShowPopup = (visible: boolean, ctx: PopupVisibleChangeContext) => {
205-
if (disabled) return;
206-
visible ? toggleIsScrolling(false) : onInputChange('', { trigger: 'blur' });
207-
setShowPopup(visible, ctx);
208-
};
209-
210198
// 可以根据触发来源,自由定制标签变化时的筛选器行为
211199
const onTagChange = (_currentTags: SelectInputValue, context) => {
212200
const { trigger, index, item, e } = context;
@@ -259,10 +247,10 @@ const Select = forwardRefWithStatics(
259247
const handleChange = (
260248
value: string | number | Array<string | number | Record<string, string | number>>,
261249
context: {
262-
e: React.MouseEvent<HTMLLIElement>;
250+
e: React.MouseEvent<HTMLLIElement> | React.KeyboardEvent<HTMLInputElement>;
263251
trigger: SelectValueChangeTrigger;
264252
value?: SelectValue;
265-
label?: string;
253+
label?: string | number;
266254
},
267255
) => {
268256
const selectedValue = multiple ? context.value : value;
@@ -302,6 +290,18 @@ const Select = forwardRefWithStatics(
302290
}
303291
};
304292

293+
const { handleKeyDown, hoverOption } = useKeyboardControl({
294+
displayOptions: flattenedOptions as TdOptionProps[],
295+
max,
296+
multiple,
297+
handlePopupVisibleChange,
298+
innerPopupVisible,
299+
handleChange,
300+
value,
301+
onCheckAllChange,
302+
selectInputRef,
303+
});
304+
305305
// 处理filter逻辑
306306
const handleFilter = (value: string) => {
307307
let filteredOptions: SelectOption[] = [];
@@ -388,7 +388,9 @@ const Select = forwardRefWithStatics(
388388
);
389389
}
390390

391-
return showArrow && <FakeArrow className={`${name}__right-icon`} isActive={showPopup} disabled={disabled} />;
391+
return (
392+
showArrow && <FakeArrow className={`${name}__right-icon`} isActive={innerPopupVisible} disabled={disabled} />
393+
);
392394
};
393395
const getPopupInstance = useCallback(() => (selectInputRef as any).current?.getPopupContentElement(), []);
394396

@@ -407,9 +409,9 @@ const Select = forwardRefWithStatics(
407409
className,
408410
size,
409411
multiple,
410-
showPopup,
412+
showPopup: innerPopupVisible,
411413
// popup弹出层内容只会在点击事件之后触发 并且无任何透传参数
412-
setShowPopup: (show: boolean) => handleShowPopup(show, {}),
414+
setShowPopup: (show: boolean) => handlePopupVisibleChange(show, {}),
413415
options: currentOptions,
414416
empty,
415417
max,
@@ -578,7 +580,9 @@ const Select = forwardRefWithStatics(
578580
prefixIcon={prefixIcon}
579581
suffixIcon={renderSuffixIcon()}
580582
panel={renderContent()}
581-
placeholder={!multiple && showPopup && selectedLabel ? selectedLabel : placeholder || t(local.placeholder)}
583+
placeholder={
584+
!multiple && innerPopupVisible && selectedLabel ? selectedLabel : placeholder || t(local.placeholder)
585+
}
582586
inputValue={inputValue}
583587
tagInputProps={{
584588
size,
@@ -598,8 +602,8 @@ const Select = forwardRefWithStatics(
598602
onScroll: handleScroll,
599603
...restPopupProps,
600604
}}
601-
popupVisible={showPopup}
602-
onPopupVisibleChange={handleShowPopup}
605+
popupVisible={innerPopupVisible}
606+
onPopupVisibleChange={handlePopupVisibleChange}
603607
onTagChange={onTagChange}
604608
onInputChange={handleInputChange}
605609
onFocus={onFocus}

packages/components/select/hooks/useKeyboardControl.ts

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
1-
import { useState, KeyboardEvent, useRef, useEffect } from 'react';
1+
import { useState, useRef, useEffect } from 'react';
22

33
import useConfig from '../../hooks/useConfig';
44
import { getSelectValueArr } from '../util/helper';
55

6-
import type { SelectOption, TdOptionProps, SelectValue } from '../type';
6+
import type { SelectOption, TdOptionProps, SelectValueChangeTrigger, SelectValue } from '../type';
77

88
export type useKeyboardControlType = {
9-
displayOptions: TdOptionProps[];
10-
innerPopupVisible: boolean;
11-
setInnerPopupVisible: any;
12-
onCheckAllChange: (checkAll: boolean, e?: React.MouseEvent<HTMLLIElement>) => void;
13-
setInnerValue: Function;
14-
innerValue: SelectValue<SelectOption>;
15-
multiple: boolean;
169
max: number;
10+
multiple: boolean;
11+
value: SelectValue<SelectOption>;
12+
handleChange: (
13+
value: SelectValue,
14+
context: {
15+
e: React.KeyboardEvent<HTMLInputElement>;
16+
trigger: SelectValueChangeTrigger;
17+
},
18+
) => void;
19+
innerPopupVisible: boolean;
20+
handlePopupVisibleChange: (visible: boolean, ctx: { e: React.KeyboardEvent<HTMLInputElement> }) => void;
21+
displayOptions: TdOptionProps[];
22+
onCheckAllChange: (checkAll: boolean, e?: React.KeyboardEvent<HTMLInputElement>) => void;
1723
selectInputRef: any;
1824
};
1925

2026
export default function useKeyboardControl({
21-
displayOptions,
22-
innerPopupVisible,
23-
setInnerPopupVisible,
24-
setInnerValue,
25-
innerValue,
26-
multiple,
2727
max,
28+
multiple,
29+
value,
30+
handleChange,
31+
innerPopupVisible,
32+
handlePopupVisibleChange,
33+
displayOptions,
2834
onCheckAllChange,
2935
selectInputRef,
3036
}: useKeyboardControlType) {
@@ -35,9 +41,10 @@ export default function useKeyboardControl({
3541
// 全选判断
3642
const isCheckAll = useRef(false);
3743
useEffect(() => {
38-
if (!Array.isArray(innerValue)) return;
39-
isCheckAll.current = innerValue.length === displayOptions.filter((v) => !(v.disabled || v.checkAll)).length;
40-
}, [innerValue, displayOptions]);
44+
if (!Array.isArray(value)) return;
45+
isCheckAll.current =
46+
value.length === displayOptions.filter((v) => !((v.disabled || v.checkAll) && !value.includes(v.value))).length;
47+
}, [value, displayOptions]);
4148

4249
const handleKeyboardScroll = (hoverIndex: number) => {
4350
const popupContent = selectInputRef.current.getPopupContentElement();
@@ -56,65 +63,57 @@ export default function useKeyboardControl({
5663
}
5764
};
5865

59-
const handleKeyDown = (_value, { e }: { e: KeyboardEvent }) => {
66+
useEffect(() => {
67+
changeHoverOption(hoverIndex === -1 ? undefined : displayOptions[hoverIndex]);
68+
}, [hoverIndex, displayOptions]);
69+
70+
const handleKeyDown = (_value, { e }: { e: React.KeyboardEvent<HTMLInputElement> }) => {
6071
const optionsListLength = displayOptions.length;
6172

6273
let newIndex = hoverIndex;
6374

6475
switch (e.code) {
6576
case 'ArrowUp':
6677
e.preventDefault();
67-
if (hoverIndex === -1) {
68-
newIndex = 0;
69-
} else if (hoverIndex === 0 || hoverIndex > optionsListLength - 1) {
70-
newIndex = optionsListLength - 1;
71-
} else {
72-
newIndex -= 1;
73-
}
74-
if (displayOptions[newIndex]?.disabled) {
75-
newIndex -= 1;
76-
}
77-
changeHoverIndex(newIndex);
78+
if (hoverIndex === -1) newIndex = 0;
79+
else if (hoverIndex === 0 || hoverIndex > optionsListLength - 1) newIndex = optionsListLength - 1;
80+
else newIndex -= 1;
7881

79-
changeHoverOption(displayOptions[newIndex]);
82+
if (displayOptions[newIndex]?.disabled) newIndex -= 1;
83+
84+
changeHoverIndex(newIndex);
8085
handleKeyboardScroll(newIndex);
8186
break;
8287
case 'ArrowDown':
8388
e.preventDefault();
89+
if (hoverIndex === -1 || hoverIndex >= optionsListLength - 1) newIndex = 0;
90+
else newIndex += 1;
8491

85-
if (hoverIndex === -1 || hoverIndex >= optionsListLength - 1) {
86-
newIndex = 0;
87-
} else {
88-
newIndex += 1;
89-
}
90-
if (displayOptions[newIndex]?.disabled) {
91-
newIndex += 1;
92-
}
93-
changeHoverIndex(newIndex);
94-
changeHoverOption(displayOptions[newIndex]);
92+
if (displayOptions[newIndex]?.disabled) newIndex += 1;
9593

94+
changeHoverIndex(newIndex);
9695
handleKeyboardScroll(newIndex);
9796
break;
9897
case 'Enter':
99-
if (hoverIndex === -1) break;
100-
10198
if (!innerPopupVisible) {
102-
setInnerPopupVisible(true, { e });
99+
handlePopupVisibleChange(true, { e });
103100
break;
104101
}
105102

103+
if (hoverIndex === -1) return;
104+
106105
if (!multiple) {
107106
const selectedOptions = displayOptions[hoverIndex];
108-
setInnerValue(selectedOptions.value, {
109-
option: selectedOptions?.[0],
110-
selectedOptions: displayOptions[hoverIndex],
111-
trigger: 'check',
112-
e,
113-
});
114-
setInnerPopupVisible(false, { e });
115-
} else {
116-
if (hoverIndex === -1) return;
117107

108+
if (selectedOptions)
109+
handleChange(selectedOptions.value, {
110+
trigger: 'check',
111+
e,
112+
});
113+
handlePopupVisibleChange(false, { e });
114+
changeHoverIndex(-1);
115+
handleKeyboardScroll(0);
116+
} else {
118117
if (displayOptions[hoverIndex].checkAll) {
119118
onCheckAllChange(!isCheckAll.current);
120119
return;
@@ -123,22 +122,23 @@ export default function useKeyboardControl({
123122
const optionValue = displayOptions[hoverIndex]?.value;
124123

125124
if (!optionValue) return;
126-
const newValue = innerValue as Array<SelectValue>;
125+
const newValue = value as Array<SelectValue>;
127126
const valueIndex = newValue.indexOf(optionValue);
128127
const isSelected = valueIndex > -1;
129128

130-
const values = getSelectValueArr(innerValue, optionValue, isSelected);
129+
const values = getSelectValueArr(value, optionValue, isSelected);
131130

132131
if (max > 0 && values.length > max) return; // 如果已选达到最大值 则不处理
133-
setInnerValue(values, {
134-
option: [],
132+
handleChange(values, {
135133
trigger: !isSelected ? 'check' : 'uncheck',
136134
e,
137135
});
138136
}
139137
break;
140138
case 'Escape':
141-
setInnerPopupVisible(false, { e });
139+
handlePopupVisibleChange(false, { e });
140+
changeHoverIndex(-1);
141+
handleKeyboardScroll(0);
142142
}
143143
};
144144

packages/components/select/hooks/useOptions.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ function useOptions(
4242
const [tmpPropOptions, setTmpPropOptions] = useState<SelectOption[]>([]);
4343
const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([]);
4444

45+
useEffect(() => {
46+
setFlattenedOptions(flattenOptions(currentOptions));
47+
}, [currentOptions]);
4548
// 处理设置 option 的逻辑
4649
useEffect(() => {
4750
let transformedOptions = options;
@@ -81,7 +84,6 @@ function useOptions(
8184
}
8285
setCurrentOptions(transformedOptions);
8386
setTmpPropOptions(transformedOptions);
84-
setFlattenedOptions(flattenOptions(transformedOptions));
8587

8688
setValueToOption(getValueToOption(children as ReactElement, options as TdOptionProps[], keys) || {});
8789
// eslint-disable-next-line react-hooks/exhaustive-deps

0 commit comments

Comments
 (0)