Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: support grouped Options in Virtualized Select #2643 #2645

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 75 additions & 2 deletions packages/semi-ui/select/_story/select.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2796,7 +2796,13 @@ const FilterDefaultOpen = () => {
filter
defaultOpen
>
<Select.OptGroup label="Asia">
<Select.OptGroup label="Asia" virtualize={
{
height: 270,
width: '100%',
itemSize: 36,
}
}>
<Select.Option value="a-1">China</Select.Option>
<Select.Option value="a-2">Korea</Select.Option>
</Select.OptGroup>
Expand Down Expand Up @@ -3709,4 +3715,71 @@ export const fix2465 = () => {
<Button onClick={()=>{multipleSelectBox.current.close()}}>close</Button>
</div>
);
}
}

export const FilterVirtualizedOptGroupSelect = () => {
const [value, setValue] = useState('a-1');

const asiaOptions = Array.from({ length: 50 }, (_, index) => ({
value: `a-${index + 1}`,
label: `Asia Country ${index + 1}`
}));

const europeOptions = Array.from({ length: 50 }, (_, index) => ({
value: `b-${index + 1}`,
label: `Europe Country ${index + 1}`
}));

const africaOptions = Array.from({ length: 50 }, (_, index) => ({
value: `c-${index + 1}`,
label: `Africa Country ${index + 1}`
}));

return (
<Select
placeholder="Select a country"
style={{ width: 300 }}
filter
defaultOpen
value={value}
onChange={val => setValue(val)}
virtualize={{
height: 300,
width: '100%',
itemSize: 36,
}}
>
<Select.OptGroup label="Asia" virtualize>
{asiaOptions.map(option => (
<Select.Option key={option.value} value={option.value}>
{option.label}
</Select.Option>
))}
</Select.OptGroup>
<Select.OptGroup label="Europe" virtualize>
{europeOptions.map(option => (
<Select.Option key={option.value} value={option.value}>
{option.label}
</Select.Option>
))}
</Select.OptGroup>
<Select.OptGroup label="Africa" virtualize>
{africaOptions.map(option => (
<Select.Option key={option.value} value={option.value}>
{option.label}
</Select.Option>
))}
</Select.OptGroup>
</Select>
);
};

FilterVirtualizedOptGroupSelect.story = {
name: '分组选择器虚拟化(1000个选项)',
parameters: {
info: {
title: '分组选择器虚拟化',
description: '测试OptGroup是否支持虚拟化'
}
}
};
54 changes: 43 additions & 11 deletions packages/semi-ui/select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,14 @@ export type SelectProps = {
showRestTagsPopover?: boolean;
restTagsPopoverProps?: PopoverProps
} & Pick<
TooltipProps,
| 'spacing'
| 'getPopupContainer'
| 'motion'
| 'autoAdjustOverflow'
| 'mouseLeaveDelay'
| 'mouseEnterDelay'
| 'stopPropagation'
TooltipProps,
| 'spacing'
| 'getPopupContainer'
| 'motion'
| 'autoAdjustOverflow'
| 'mouseLeaveDelay'
| 'mouseEnterDelay'
| 'stopPropagation'
> & React.RefAttributes<any>;

export interface SelectState {
Expand Down Expand Up @@ -316,7 +316,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
};

static __SemiComponentName__ = "Select";

static defaultProps: Partial<SelectProps> = getDefaultPropsFromGlobalConfig(Select.__SemiComponentName__, {
stopPropagation: true,
motion: true,
Expand Down Expand Up @@ -751,7 +751,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
* When searchPosition is trigger, the keyboard events are bound to the outer trigger div, so there is no need to listen in input.
* When searchPosition is dropdown, the popup and the outer trigger div are not parent- child relationships,
* and bubbles cannot occur, so onKeydown needs to be listened in input.
* */
* */
onKeyDown: (e) => this.foundation._handleKeyDown(e)
};

Expand Down Expand Up @@ -924,15 +924,47 @@ class Select extends BaseComponent<SelectProps, SelectState> {
const { direction } = this.context;
const { height, width, itemSize } = virtualize;

const content: any[] = [];

visibleOptions.forEach((option) => {
if (option._parentGroup && !content.some(item =>
item.type === OptionGroup &&
item.props.label === option._parentGroup.label
)) {
content.push(
<OptionGroup
key={`group-${option._parentGroup.label}`}
{...option._parentGroup}
/>
);
}

content.push(option);
});

return (
<List
ref={this.virtualizeListRef}
height={height || numbers.LIST_HEIGHT}
itemCount={visibleOptions.length}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

itemCount={visibleOptions.length} 应该修改为content 的 size ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

itemCount={visibleOptions.length} 应该修改为content 的 size ?

感谢回复,在有分组标题头的情况下这两者确实有差别,渲染的高度有差异,用content.length会更准确一些,已更改

itemSize={itemSize}
itemData={{ visibleOptions, renderOption: this.renderOption }}
width={width || '100%'}
style={{ direction }}
itemData={{
visibleOptions: content,
renderOption: (option, index, style) => {
if (option.type === OptionGroup) {
return React.cloneElement(option, { style });
}
return this.renderOption(option, index, style);
}
}}
itemKey={(index, data) => {
const option = data.visibleOptions[index];
return option.type === OptionGroup
? `group-${option.props.label}`
: option.value;
}}
>
{VirtualRow}
</List>
Expand Down
Loading