Date: Mon, 2 Dec 2024 14:29:31 +0800
Subject: [PATCH 31/36] chore: adjust context api
---
src/OptionList.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/OptionList.tsx b/src/OptionList.tsx
index 4b753432..c8defcc5 100644
--- a/src/OptionList.tsx
+++ b/src/OptionList.tsx
@@ -2,7 +2,7 @@ import { useBaseProps } from 'rc-select';
import type { RefOptionListProps } from 'rc-select/lib/OptionList';
import type { TreeProps } from 'rc-tree';
import Tree from 'rc-tree';
-import { InternalContext } from 'rc-tree';
+import { UnstableContext } from 'rc-tree';
import type { EventDataNode, ScrollTo } from 'rc-tree/lib/interface';
import KeyCode from 'rc-util/lib/KeyCode';
import useMemo from 'rc-util/lib/hooks/useMemo';
@@ -342,7 +342,7 @@ const OptionList: React.ForwardRefRenderFunction = (_,
{activeEntity.node.value}
)}
-
+
= (_,
expandAction={treeExpandAction}
onScroll={onPopupScroll}
/>
-
+
);
};
From 2bd8a5c212786d3ce3bbcc8f44d168a7b7d70f29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E8=B1=AA?= <1844749591@qq.com>
Date: Mon, 2 Dec 2024 15:46:50 +0800
Subject: [PATCH 32/36] chore: bump rc-tree version to 5.11.0 for support
maxCount
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 89bf0997..ecc3e84a 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
"@babel/runtime": "^7.25.7",
"classnames": "2.x",
"rc-select": "~14.16.2",
- "rc-tree": "~5.10.1",
+ "rc-tree": "~5.11.0",
"rc-util": "^5.43.0"
},
"devDependencies": {
From 142385a8b53fb1723c46a72fc20134236049261c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E8=B1=AA?= <1844749591@qq.com>
Date: Mon, 2 Dec 2024 16:27:40 +0800
Subject: [PATCH 33/36] fix: test coverage
---
src/OptionList.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/OptionList.tsx b/src/OptionList.tsx
index c8defcc5..4e9e26d2 100644
--- a/src/OptionList.tsx
+++ b/src/OptionList.tsx
@@ -200,7 +200,7 @@ const OptionList: React.ForwardRefRenderFunction
= (_,
const getNextMatchingNode = (
currentKey: Key | null,
- direction: 'next' | 'prev' = 'next',
+ direction: 'next' | 'prev',
): EventDataNode | null => {
const availableNodes = availableNodesRef.current;
From 73e9ae7f70586d0503397b60d3544b86d7c0bc1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E8=B1=AA?= <1844749591@qq.com>
Date: Tue, 3 Dec 2024 09:38:26 +0800
Subject: [PATCH 34/36] fix: fix some case
---
src/OptionList.tsx | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/OptionList.tsx b/src/OptionList.tsx
index 4e9e26d2..af281cd0 100644
--- a/src/OptionList.tsx
+++ b/src/OptionList.tsx
@@ -11,6 +11,7 @@ import LegacyContext from './LegacyContext';
import TreeSelectContext from './TreeSelectContext';
import type { DataNode, Key, SafeKey } from './interface';
import { getAllKeys, isCheckDisabled } from './utils/valueUtil';
+import { useEvent } from 'rc-util';
const HIDDEN_STYLE = {
width: 0,
@@ -79,7 +80,10 @@ const OptionList: React.ForwardRefRenderFunction = (_,
(prev, next) => next[0] && prev[1] !== next[1],
);
- const memoDisplayValues = React.useMemo(() => displayValues?.map(v => v.value), [displayValues]);
+ const memoDisplayValues = React.useMemo(
+ () => (displayValues || []).map(v => v.value),
+ [displayValues],
+ );
// ========================== Values ==========================
const mergedCheckedKeys = React.useMemo(() => {
@@ -154,21 +158,17 @@ const OptionList: React.ForwardRefRenderFunction = (_,
React.useEffect(() => {
if (searchValue) {
- setSearchExpandedKeys(getAllKeys(memoTreeData, fieldNames));
+ setSearchExpandedKeys(getAllKeys(treeData, fieldNames));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchValue]);
- const nodeDisabled = (node: DataNode) => {
- if (isOverMaxCount) {
- const selectedValues = memoDisplayValues;
- if (!selectedValues.includes(node[fieldNames.value])) {
- return true;
- }
+ const nodeDisabled = useEvent((node: DataNode) => {
+ if (isOverMaxCount && !memoDisplayValues.includes(node[fieldNames.value])) {
+ return true;
}
-
- return undefined;
- };
+ return false;
+ });
// ========================== Get First Selectable Node ==========================
const getFirstMatchingNode = (nodes: EventDataNode[]): EventDataNode | null => {
From f7378326deb75a2c9d603dfeed72eda988357b39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E8=B1=AA?= <1844749591@qq.com>
Date: Tue, 3 Dec 2024 11:48:51 +0800
Subject: [PATCH 35/36] chore: remove keyboard operation logic
---
src/OptionList.tsx | 55 ++------------------------
tests/Select.maxCount.spec.tsx | 72 ----------------------------------
2 files changed, 3 insertions(+), 124 deletions(-)
diff --git a/src/OptionList.tsx b/src/OptionList.tsx
index af281cd0..2268454c 100644
--- a/src/OptionList.tsx
+++ b/src/OptionList.tsx
@@ -195,46 +195,6 @@ const OptionList: React.ForwardRefRenderFunction = (_,
return null;
};
- // ========================== Get Next Matching Node ==========================
- const availableNodesRef = React.useRef[]>([]);
-
- const getNextMatchingNode = (
- currentKey: Key | null,
- direction: 'next' | 'prev',
- ): EventDataNode | null => {
- const availableNodes = availableNodesRef.current;
-
- const currentIndex = availableNodes.findIndex(node => node[fieldNames.value] === currentKey);
-
- const nextIndex =
- direction === 'next'
- ? (currentIndex + 1) % availableNodes.length
- : (currentIndex - 1 + availableNodes.length) % availableNodes.length;
-
- return availableNodes[nextIndex];
- };
-
- React.useEffect(() => {
- const nodes: EventDataNode[] = [];
- const selectedValueSet = new Set(memoDisplayValues);
-
- const collectNodes = (nodeList: EventDataNode[]) => {
- nodeList.forEach(node => {
- if (!node.disabled && node.selectable !== false) {
- if (selectedValueSet.has(node[fieldNames.value])) {
- nodes.push(node);
- }
- }
- if (node[fieldNames.children]) {
- collectNodes(node[fieldNames.children]);
- }
- });
- };
-
- collectNodes(memoTreeData);
- availableNodesRef.current = nodes;
- }, [memoDisplayValues, memoTreeData]);
-
// ========================== Active ==========================
const [activeKey, setActiveKey] = React.useState(null);
const activeEntity = keyEntities[activeKey as SafeKey];
@@ -271,24 +231,15 @@ const OptionList: React.ForwardRefRenderFunction = (_,
case KeyCode.DOWN:
case KeyCode.LEFT:
case KeyCode.RIGHT:
- if (isOverMaxCount) {
- event.preventDefault();
- const direction = which === KeyCode.UP || which === KeyCode.LEFT ? 'prev' : 'next';
- const nextNode = getNextMatchingNode(activeKey, direction);
- if (nextNode) {
- setActiveKey(nextNode[fieldNames.value]);
- treeRef.current?.scrollTo({ key: nextNode[fieldNames.value] });
- }
- } else {
- treeRef.current?.onKeyDown(event as React.KeyboardEvent);
- }
+ treeRef.current?.onKeyDown(event as React.KeyboardEvent);
break;
// >>> Select item
case KeyCode.ENTER: {
if (activeEntity) {
+ const isNodeDisabled = nodeDisabled(activeEntity.node);
const { selectable, value, disabled } = activeEntity?.node || {};
- if (selectable !== false && !disabled) {
+ if (selectable !== false && !disabled && !isNodeDisabled) {
onInternalSelect(null, {
node: { key: activeKey },
selected: !checkedKeys.includes(value),
diff --git a/tests/Select.maxCount.spec.tsx b/tests/Select.maxCount.spec.tsx
index 3c8202e5..79fe48b8 100644
--- a/tests/Select.maxCount.spec.tsx
+++ b/tests/Select.maxCount.spec.tsx
@@ -170,8 +170,6 @@ describe('TreeSelect.maxCount keyboard operations', () => {
keyDown(input, KeyCode.DOWN);
keyDown(input, KeyCode.ENTER);
keyUp(input, KeyCode.ENTER);
-
- expect(onSelect).toHaveBeenCalledTimes(2);
});
it('when maxCount is reached, the option should be disabled', () => {
@@ -206,76 +204,6 @@ describe('TreeSelect.maxCount keyboard operations', () => {
// verify only two options are selected
expect(container.querySelectorAll('.rc-tree-select-tree-treenode-selected')).toHaveLength(2);
});
-
- it('should cycle through selected options when maxCount is reached', () => {
- const { container } = render(
- ,
- );
-
- const input = container.querySelector('input');
-
- keyDown(input, KeyCode.DOWN);
- expect(
- container.querySelector('.rc-tree-select-tree-treenode.rc-tree-select-tree-treenode-active')
- ?.textContent,
- ).toBe('2 label');
-
- // Move down again to cycle back to the first selected item
- keyDown(input, KeyCode.DOWN);
- expect(
- container.querySelector('.rc-tree-select-tree-treenode.rc-tree-select-tree-treenode-active')
- ?.textContent,
- ).toBe('0 label');
- });
-
- it('should cycle through selected options in reverse when using UP key', () => {
- const { container } = render(
- ,
- );
-
- const input = container.querySelector('input');
-
- // Initially activate the last selected item
- keyDown(input, KeyCode.UP);
- expect(
- container.querySelector('.rc-tree-select-tree-treenode.rc-tree-select-tree-treenode-active')
- ?.textContent,
- ).toBe('2 label');
-
- // Move up again to cycle back to the first selected item
- keyDown(input, KeyCode.UP);
- expect(
- container.querySelector('.rc-tree-select-tree-treenode.rc-tree-select-tree-treenode-active')
- ?.textContent,
- ).toBe('0 label');
-
- // Move up again to cycle back to the last selected item
- keyDown(input, KeyCode.UP);
- expect(
- container.querySelector('.rc-tree-select-tree-treenode.rc-tree-select-tree-treenode-active')
- ?.textContent,
- ).toBe('2 label');
- });
-
- it('should handle LEFT/RIGHT keys correctly when maxCount is reached', () => {
- const { container } = render(
- ,
- );
-
- const input = container.querySelector('input');
-
- keyDown(input, KeyCode.RIGHT);
- expect(
- container.querySelector('.rc-tree-select-tree-treenode.rc-tree-select-tree-treenode-active')
- ?.textContent,
- ).toBe('2 label');
-
- keyDown(input, KeyCode.LEFT);
- expect(
- container.querySelector('.rc-tree-select-tree-treenode.rc-tree-select-tree-treenode-active')
- ?.textContent,
- ).toBe('0 label');
- });
});
describe('TreeSelect.maxCount with different strategies', () => {
From 4c23314c9225749ab4c413230539b0d040cb2bac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E8=B1=AA?= <1844749591@qq.com>
Date: Tue, 3 Dec 2024 14:23:37 +0800
Subject: [PATCH 36/36] chore: optimized code logic
---
src/OptionList.tsx | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/src/OptionList.tsx b/src/OptionList.tsx
index 2268454c..b49e552e 100644
--- a/src/OptionList.tsx
+++ b/src/OptionList.tsx
@@ -80,7 +80,7 @@ const OptionList: React.ForwardRefRenderFunction = (_,
(prev, next) => next[0] && prev[1] !== next[1],
);
- const memoDisplayValues = React.useMemo(
+ const memoRawValues = React.useMemo(
() => (displayValues || []).map(v => v.value),
[displayValues],
);
@@ -164,10 +164,7 @@ const OptionList: React.ForwardRefRenderFunction = (_,
}, [searchValue]);
const nodeDisabled = useEvent((node: DataNode) => {
- if (isOverMaxCount && !memoDisplayValues.includes(node[fieldNames.value])) {
- return true;
- }
- return false;
+ return isOverMaxCount && !memoRawValues.includes(node[fieldNames.value]);
});
// ========================== Get First Selectable Node ==========================