Skip to content
Merged
17 changes: 15 additions & 2 deletions examples/mutiple-with-maxCount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ export default () => {
key: '1-2',
value: '1-2',
title: '1-2',
disabled: true,
children: [
{
key: '1-2-1',
value: '1-2-1',
title: '1-2-1',
disabled: true,
},
{
key: '1-2-2',
value: '1-2-2',
title: '1-2-2',
},
],
},
{
key: '1-3',
Expand Down Expand Up @@ -70,8 +84,7 @@ export default () => {
multiple
treeCheckable
// showCheckedStrategy="SHOW_ALL"
showCheckedStrategy="SHOW_PARENT"
// showCheckedStrategy="SHOW_CHILD"
// showCheckedStrategy="SHOW_PARENT"
maxCount={4}
treeData={treeData}
onChange={onChange}
Expand Down
56 changes: 55 additions & 1 deletion src/OptionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import TreeSelectContext from './TreeSelectContext';
import type { DataNode, Key, SafeKey } from './interface';
import { getAllKeys, isCheckDisabled } from './utils/valueUtil';
import { useEvent } from 'rc-util';
import { formatStrategyValues } from './utils/strategyUtil';
import { conductCheck } from 'rc-tree/lib/utils/conductUtil';

const HIDDEN_STYLE = {
width: 0,
Expand Down Expand Up @@ -49,6 +51,8 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
onPopupScroll,
displayValues,
isOverMaxCount,
maxCount,
showCheckedStrategy,
} = React.useContext(TreeSelectContext);

const {
Expand Down Expand Up @@ -164,7 +168,57 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
}, [searchValue]);

const nodeDisabled = useEvent((node: DataNode) => {
return isOverMaxCount && !memoRawValues.includes(node[fieldNames.value]);
// Always enable selected nodes
if (checkedKeys.includes(node[fieldNames.value])) {
return false;
}

// Get all selectable keys under current node considering conduction rules
const getSelectableKeys = (nodes: DataNode[]) => {
const keys: Key[] = [];
const traverse = (n: DataNode) => {
if (!n.disabled) {
keys.push(n[fieldNames.value]);
// Only traverse children if node is not disabled
if (Array.isArray(n.children)) {
n.children.forEach(traverse);
}
}
};
nodes.forEach(traverse);
return keys;
};

const selectableNodeValues = getSelectableKeys([node]);

// Simulate checked state after selecting current node
const simulatedCheckedKeys = [...checkedKeys, ...selectableNodeValues];

const { checkedKeys: conductedKeys } = conductCheck(simulatedCheckedKeys, true, keyEntities);

// Calculate display keys based on strategy
const simulatedDisplayKeys = formatStrategyValues(
conductedKeys as SafeKey[],
showCheckedStrategy,
keyEntities,
fieldNames,
);

const currentDisplayKeys = formatStrategyValues(
checkedKeys as SafeKey[],
showCheckedStrategy,
keyEntities,
fieldNames,
);

const newDisplayValuesCount = simulatedDisplayKeys.length - currentDisplayKeys.length;

// Check if selecting this node would exceed maxCount
if (isOverMaxCount || memoRawValues.length + newDisplayValuesCount > maxCount) {
return true;
}

return false;
});

// ========================== Get First Selectable Node ==========================
Expand Down
10 changes: 3 additions & 7 deletions src/TreeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -422,13 +422,6 @@ const TreeSelect = React.forwardRef<BaseSelectRef, TreeSelectProps>((props, ref)
mergedFieldNames,
);

// if multiple and maxCount is set, check if exceed maxCount
if (mergedMultiple && maxCount !== undefined) {
if (formattedKeyList.length > maxCount) {
return;
}
}

const labeledValues = convert2LabelValues(newRawValues);
setInternalValue(labeledValues);

Expand Down Expand Up @@ -625,6 +618,8 @@ const TreeSelect = React.forwardRef<BaseSelectRef, TreeSelectProps>((props, ref)
onPopupScroll,
displayValues: cachedDisplayValues,
isOverMaxCount,
maxCount,
showCheckedStrategy: mergedShowCheckedStrategy,
};
}, [
virtual,
Expand All @@ -641,6 +636,7 @@ const TreeSelect = React.forwardRef<BaseSelectRef, TreeSelectProps>((props, ref)
maxCount,
cachedDisplayValues,
mergedMultiple,
mergedShowCheckedStrategy,
]);

// ======================= Legacy Context =======================
Expand Down
3 changes: 3 additions & 0 deletions src/TreeSelectContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import type { ExpandAction } from 'rc-tree/lib/Tree';
import type { DataNode, FieldNames, Key, LabeledValueType } from './interface';
import { CheckedStrategy } from './utils/strategyUtil';

export interface TreeSelectContextProps {
virtual?: boolean;
Expand All @@ -16,6 +17,8 @@ export interface TreeSelectContextProps {
onPopupScroll?: React.UIEventHandler<HTMLDivElement>;
displayValues?: LabeledValueType[];
isOverMaxCount?: boolean;
maxCount?: number;
showCheckedStrategy?: CheckedStrategy;
}

const TreeSelectContext = React.createContext<TreeSelectContextProps>(null as any);
Expand Down
Loading