From 1a04308148217a9725f82c562dad06953f716136 Mon Sep 17 00:00:00 2001 From: lijianan <574980606@qq.com> Date: Thu, 28 Dec 2023 02:24:45 +0800 Subject: [PATCH 01/10] feat: Select support maxCount --- src/OptionList.tsx | 24 ++++++++++++++++-------- src/Select.tsx | 12 +++++++++--- src/SelectContext.ts | 1 + 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/OptionList.tsx b/src/OptionList.tsx index 6e880252..2c6ea96f 100644 --- a/src/OptionList.tsx +++ b/src/OptionList.tsx @@ -45,6 +45,7 @@ const OptionList: React.ForwardRefRenderFunction = (_, r onPopupScroll, } = useBaseProps(); const { + maxCount, flattenOptions, onActiveValue, defaultActiveFirstOption, @@ -70,6 +71,11 @@ const OptionList: React.ForwardRefRenderFunction = (_, r // =========================== List =========================== const listRef = React.useRef(null); + const shouldDisabled = React.useMemo( + () => multiple && typeof maxCount !== 'undefined' && rawValues.size >= maxCount, + [multiple, maxCount, rawValues.size], + ); + const onListMouseDown: React.MouseEventHandler = (event) => { event.preventDefault(); }; @@ -87,8 +93,8 @@ const OptionList: React.ForwardRefRenderFunction = (_, r for (let i = 0; i < len; i += 1) { const current = (index + i * offset + len) % len; - const { group, data } = memoFlattenOptions[current]; - if (!group && !data.disabled) { + const { group, data } = memoFlattenOptions[current] || {}; + if (!group && !data?.disabled && !shouldDisabled) { return current; } } @@ -197,7 +203,7 @@ const OptionList: React.ForwardRefRenderFunction = (_, r case KeyCode.ENTER: { // value const item = memoFlattenOptions[activeIndex]; - if (item && !item.data.disabled) { + if (item && !item?.data?.disabled && !shouldDisabled) { onSelectValue(item.value); } else { onSelectValue(undefined); @@ -323,14 +329,16 @@ const OptionList: React.ForwardRefRenderFunction = (_, r const { disabled, title, children, style, className, ...otherProps } = data; const passedProps = omit(otherProps, omitFieldNameList); + const mergedDisabled = disabled || shouldDisabled; + // Option const selected = isSelected(value); const optionPrefixCls = `${itemPrefixCls}-option`; const optionClassName = classNames(itemPrefixCls, optionPrefixCls, className, { [`${optionPrefixCls}-grouped`]: groupOption, - [`${optionPrefixCls}-active`]: activeIndex === itemIndex && !disabled, - [`${optionPrefixCls}-disabled`]: disabled, + [`${optionPrefixCls}-active`]: activeIndex === itemIndex && !mergedDisabled, + [`${optionPrefixCls}-disabled`]: mergedDisabled, [`${optionPrefixCls}-selected`]: selected, }); @@ -355,13 +363,13 @@ const OptionList: React.ForwardRefRenderFunction = (_, r className={optionClassName} title={optionTitle} onMouseMove={() => { - if (activeIndex === itemIndex || disabled) { + if (activeIndex === itemIndex || mergedDisabled) { return; } setActive(itemIndex); }} onClick={() => { - if (!disabled) { + if (!mergedDisabled) { onSelectValue(value); } }} @@ -379,7 +387,7 @@ const OptionList: React.ForwardRefRenderFunction = (_, r customizeIcon={menuItemSelectedIcon} customizeIconProps={{ value, - disabled, + disabled: mergedDisabled, isSelected: selected, }} > diff --git a/src/Select.tsx b/src/Select.tsx index 430395a0..45a2f868 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -45,6 +45,7 @@ import OptGroup from './OptGroup'; import Option from './Option'; import OptionList from './OptionList'; import SelectContext from './SelectContext'; +import type { SelectContextProps } from './SelectContext'; import useCache from './hooks/useCache'; import useFilterOptions from './hooks/useFilterOptions'; import useId from './hooks/useId'; @@ -156,6 +157,7 @@ export interface SelectProps void; } @@ -203,6 +205,7 @@ const Select = React.forwardRef( defaultValue, labelInValue, onChange, + maxCount, ...restProps } = props; @@ -596,7 +599,7 @@ const Select = React.forwardRef( }; // ========================== Context =========================== - const selectContext = React.useMemo(() => { + const selectContext = React.useMemo(() => { const realVirtual = virtual !== false && dropdownMatchSelectWidth !== false; return { ...parsedOptions, @@ -612,9 +615,13 @@ const Select = React.forwardRef( listHeight, listItemHeight, childrenAsData, + maxCount, optionRender, }; }, [ + maxCount, + virtual, + dropdownMatchSelectWidth, parsedOptions, displayOptions, onActiveValue, @@ -623,8 +630,7 @@ const Select = React.forwardRef( menuItemSelectedIcon, rawValues, mergedFieldNames, - virtual, - dropdownMatchSelectWidth, + direction, listHeight, listItemHeight, childrenAsData, diff --git a/src/SelectContext.ts b/src/SelectContext.ts index cd7d5f72..e124425f 100644 --- a/src/SelectContext.ts +++ b/src/SelectContext.ts @@ -25,6 +25,7 @@ export interface SelectContextProps { listHeight?: number; listItemHeight?: number; childrenAsData?: boolean; + maxCount?: number; } const SelectContext = React.createContext(null); From 70746227c55939682dcc78a13e7115da86db8a8b Mon Sep 17 00:00:00 2001 From: lijianan <574980606@qq.com> Date: Thu, 28 Dec 2023 02:48:37 +0800 Subject: [PATCH 02/10] fix: fix --- src/Select.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Select.tsx b/src/Select.tsx index 90c7a2fe..ee91bc50 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -620,8 +620,6 @@ const Select = React.forwardRef Date: Thu, 28 Dec 2023 03:06:58 +0800 Subject: [PATCH 03/10] demo: update demo --- docs/demo/multiple-with-maxCount.md | 8 ++++++ docs/examples/multiple-with-maxCount.tsx | 36 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 docs/demo/multiple-with-maxCount.md create mode 100644 docs/examples/multiple-with-maxCount.tsx diff --git a/docs/demo/multiple-with-maxCount.md b/docs/demo/multiple-with-maxCount.md new file mode 100644 index 00000000..668c3f94 --- /dev/null +++ b/docs/demo/multiple-with-maxCount.md @@ -0,0 +1,8 @@ +--- +title: multiple-with-maxCount +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/multiple-with-maxCount.tsx b/docs/examples/multiple-with-maxCount.tsx new file mode 100644 index 00000000..af41bd23 --- /dev/null +++ b/docs/examples/multiple-with-maxCount.tsx @@ -0,0 +1,36 @@ +/* eslint-disable no-console */ +import React from 'react'; +import Select from 'rc-select'; +import '../../assets/index.less'; + +const Test: React.FC = () => { + const [value, setValue] = React.useState(['1']); + + const onChange = (v: any) => { + setValue(v); + }; + + return ( + <> +

Multiple with maxCount

+ , ); - const inputSpy = jest.spyOn(wrapper.find('input').instance(), 'focus' as any); - wrapper.find('.rc-select-selection-placeholder').simulate('mousedown'); - wrapper.find('.rc-select-selection-placeholder').simulate('click'); + const inputSpy = jest.spyOn(selectWrapper.find('input').instance(), 'focus' as any); + selectWrapper.find('.rc-select-selection-placeholder').simulate('mousedown'); + selectWrapper.find('.rc-select-selection-placeholder').simulate('click'); expect(inputSpy).toHaveBeenCalled(); }); }); @@ -1499,7 +1499,7 @@ describe('Select.Basic', () => { ); expect(menuItemSelectedIcon).toHaveBeenCalledWith({ value: '1', - disabled: undefined, + disabled: false, isSelected: true, }); @@ -2105,7 +2105,7 @@ describe('Select.Basic', () => { + +); + +export default App; diff --git a/src/BaseSelect.tsx b/src/BaseSelect.tsx index 3fef85c8..5b55aed8 100644 --- a/src/BaseSelect.tsx +++ b/src/BaseSelect.tsx @@ -27,6 +27,8 @@ import type { RefTriggerProps } from './SelectTrigger'; import SelectTrigger from './SelectTrigger'; import TransBtn from './TransBtn'; import { getSeparatedContent } from './utils/valueUtil'; +import SelectContext from './SelectContext'; +import type { SelectContextProps } from './SelectContext'; export type { DisplayInfoType, @@ -286,6 +288,8 @@ const BaseSelect = React.forwardRef((props, ref) ...restProps } = props; + const { maxCount, rawValues } = React.useContext(SelectContext); + // ============================== MISC ============================== const multiple = isMultiple(mode); const mergedShowSearch = @@ -409,7 +413,13 @@ const BaseSelect = React.forwardRef((props, ref) if (mode !== 'combobox' && patchLabels) { newSearchText = ''; - onSearchSplit?.(patchLabels); + if (typeof maxCount !== 'undefined') { + if (rawValues.size < maxCount) { + onSearchSplit?.(patchLabels.slice(0, maxCount - rawValues.size)); + } + } else { + onSearchSplit?.(patchLabels); + } // Should close when paste finish onToggleOpen(false); From e26517595cdddf10b7933fe6a73bb2384dd3414e Mon Sep 17 00:00:00 2001 From: lijianan <574980606@qq.com> Date: Thu, 28 Dec 2023 13:48:29 +0800 Subject: [PATCH 07/10] fix: fix --- docs/demo/automatic-tokenization.md | 8 ------ docs/examples/automatic-tokenization.tsx | 32 ------------------------ src/BaseSelect.tsx | 12 +-------- 3 files changed, 1 insertion(+), 51 deletions(-) delete mode 100644 docs/demo/automatic-tokenization.md delete mode 100644 docs/examples/automatic-tokenization.tsx diff --git a/docs/demo/automatic-tokenization.md b/docs/demo/automatic-tokenization.md deleted file mode 100644 index 1ab84635..00000000 --- a/docs/demo/automatic-tokenization.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: automatic-tokenization -nav: - title: Demo - path: /demo ---- - - diff --git a/docs/examples/automatic-tokenization.tsx b/docs/examples/automatic-tokenization.tsx deleted file mode 100644 index 2fc45c09..00000000 --- a/docs/examples/automatic-tokenization.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import Select from 'rc-select'; -import '../../assets/index.less'; - -const options = []; - -for (let i = 10; i < 36; i++) { - options.push({ - value: i.toString(36) + i, - label: i.toString(36) + i, - }); -} - -const handleChange = (value: string) => { - console.log(`selected ${value}`); -}; - -const App: React.FC = () => ( - <> -

自动分词

- , + , + , + , + ); + const items = wrapper.find('div.rc-virtual-list-holder-inner .rc-select-item'); + expect(items.at(0).props().className.includes('rc-select-item-option-disabled')).toBeFalsy(); + expect(items.at(1).props().className.includes('rc-select-item-option-disabled')).toBeTruthy(); + }); }); }); From e87d9f6ea0c7ae01dfebdc601b5cc7ca0f6881e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=97=E5=98=89=E7=94=B7?= <574980606@qq.com> Date: Thu, 28 Dec 2023 16:38:02 +0800 Subject: [PATCH 10/10] test: fix case --- tests/Multiple.test.tsx | 15 --------------- tests/Select.test.tsx | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/tests/Multiple.test.tsx b/tests/Multiple.test.tsx index cfc4e2a4..6e6a051a 100644 --- a/tests/Multiple.test.tsx +++ b/tests/Multiple.test.tsx @@ -681,20 +681,5 @@ describe('Select.Multiple', () => { toggleOpen(wrapper); expect(wrapper.find('input').props().value).toBe(''); }); - - it('items should not disabled', () => { - const wrapper = mount( - , + ); + const element = container.querySelectorAll( + 'div.rc-virtual-list-holder-inner .rc-select-item', + ); + expect(element[0]).not.toHaveClass('rc-select-item-option-disabled'); + expect(element[1]).toHaveClass('rc-select-item-option-disabled'); + }); });