perf: virtual list
parent
7aae6f675c
commit
4e70c6dd77
|
@ -7,7 +7,7 @@ import { isValidElement } from '../_util/props-util';
|
||||||
import createRef from '../_util/createRef';
|
import createRef from '../_util/createRef';
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { computed, defineComponent, nextTick, reactive, watch } from 'vue';
|
import { computed, defineComponent, nextTick, reactive, watch } from 'vue';
|
||||||
import List from '../vc-virtual-list/List';
|
import List from '../vc-virtual-list';
|
||||||
import type {
|
import type {
|
||||||
OptionsType as SelectOptionsType,
|
OptionsType as SelectOptionsType,
|
||||||
OptionData,
|
OptionData,
|
||||||
|
@ -346,85 +346,95 @@ const OptionList = defineComponent<OptionListProps<SelectOptionsType[number]>, {
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
virtual={virtual}
|
virtual={virtual}
|
||||||
onMouseenter={onMouseenter}
|
onMouseenter={onMouseenter}
|
||||||
children={({ group, groupOption, data, label, value }, itemIndex) => {
|
v-slots={{
|
||||||
const { key } = data;
|
default: ({ group, groupOption, data, label, value }, itemIndex) => {
|
||||||
// Group
|
const { key } = data;
|
||||||
if (group) {
|
// Group
|
||||||
|
if (group) {
|
||||||
|
return (
|
||||||
|
<div class={classNames(itemPrefixCls, `${itemPrefixCls}-group`)}>
|
||||||
|
{renderOption ? renderOption(data) : label !== undefined ? label : key}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
disabled,
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
class: cls,
|
||||||
|
className,
|
||||||
|
...otherProps
|
||||||
|
} = data;
|
||||||
|
const passedProps = omit(otherProps, omitFieldNameList);
|
||||||
|
// Option
|
||||||
|
const selected = values.has(value);
|
||||||
|
|
||||||
|
const optionPrefixCls = `${itemPrefixCls}-option`;
|
||||||
|
const optionClassName = classNames(itemPrefixCls, optionPrefixCls, cls, className, {
|
||||||
|
[`${optionPrefixCls}-grouped`]: groupOption,
|
||||||
|
[`${optionPrefixCls}-active`]: activeIndex === itemIndex && !disabled,
|
||||||
|
[`${optionPrefixCls}-disabled`]: disabled,
|
||||||
|
[`${optionPrefixCls}-selected`]: selected,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mergedLabel = childrenAsData ? children : label;
|
||||||
|
|
||||||
|
const iconVisible =
|
||||||
|
!menuItemSelectedIcon || typeof menuItemSelectedIcon === 'function' || selected;
|
||||||
|
|
||||||
|
const content = mergedLabel || value;
|
||||||
|
// https://github.com/ant-design/ant-design/issues/26717
|
||||||
|
let optionTitle =
|
||||||
|
typeof content === 'string' || typeof content === 'number'
|
||||||
|
? content.toString()
|
||||||
|
: undefined;
|
||||||
|
if (title !== undefined) {
|
||||||
|
optionTitle = title;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={classNames(itemPrefixCls, `${itemPrefixCls}-group`)}>
|
<div
|
||||||
{renderOption ? renderOption(data) : label !== undefined ? label : key}
|
{...passedProps}
|
||||||
|
aria-selected={selected}
|
||||||
|
class={optionClassName}
|
||||||
|
title={optionTitle}
|
||||||
|
onMousemove={e => {
|
||||||
|
if (otherProps.onMousemove) {
|
||||||
|
otherProps.onMousemove(e);
|
||||||
|
}
|
||||||
|
if (activeIndex === itemIndex || disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setActive(itemIndex);
|
||||||
|
}}
|
||||||
|
onClick={e => {
|
||||||
|
if (!disabled) {
|
||||||
|
onSelectValue(value);
|
||||||
|
}
|
||||||
|
if (otherProps.onClick) {
|
||||||
|
otherProps.onClick(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
<div class={`${optionPrefixCls}-content`}>
|
||||||
|
{renderOption ? renderOption(data) : content}
|
||||||
|
</div>
|
||||||
|
{isValidElement(menuItemSelectedIcon) || selected}
|
||||||
|
{iconVisible && (
|
||||||
|
<TransBtn
|
||||||
|
class={`${itemPrefixCls}-option-state`}
|
||||||
|
customizeIcon={menuItemSelectedIcon}
|
||||||
|
customizeIconProps={{ isSelected: selected }}
|
||||||
|
>
|
||||||
|
{selected ? '✓' : null}
|
||||||
|
</TransBtn>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
const { disabled, title, children, style, class: cls, className, ...otherProps } = data;
|
|
||||||
const passedProps = omit(otherProps, omitFieldNameList);
|
|
||||||
// Option
|
|
||||||
const selected = values.has(value);
|
|
||||||
|
|
||||||
const optionPrefixCls = `${itemPrefixCls}-option`;
|
|
||||||
const optionClassName = classNames(itemPrefixCls, optionPrefixCls, cls, className, {
|
|
||||||
[`${optionPrefixCls}-grouped`]: groupOption,
|
|
||||||
[`${optionPrefixCls}-active`]: activeIndex === itemIndex && !disabled,
|
|
||||||
[`${optionPrefixCls}-disabled`]: disabled,
|
|
||||||
[`${optionPrefixCls}-selected`]: selected,
|
|
||||||
});
|
|
||||||
|
|
||||||
const mergedLabel = childrenAsData ? children : label;
|
|
||||||
|
|
||||||
const iconVisible =
|
|
||||||
!menuItemSelectedIcon || typeof menuItemSelectedIcon === 'function' || selected;
|
|
||||||
|
|
||||||
const content = mergedLabel || value;
|
|
||||||
// https://github.com/ant-design/ant-design/issues/26717
|
|
||||||
let optionTitle =
|
|
||||||
typeof content === 'string' || typeof content === 'number'
|
|
||||||
? content.toString()
|
|
||||||
: undefined;
|
|
||||||
if (title !== undefined) {
|
|
||||||
optionTitle = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
{...passedProps}
|
|
||||||
aria-selected={selected}
|
|
||||||
class={optionClassName}
|
|
||||||
title={optionTitle}
|
|
||||||
onMousemove={e => {
|
|
||||||
if (otherProps.onMousemove) {
|
|
||||||
otherProps.onMousemove(e);
|
|
||||||
}
|
|
||||||
if (activeIndex === itemIndex || disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setActive(itemIndex);
|
|
||||||
}}
|
|
||||||
onClick={e => {
|
|
||||||
if (!disabled) {
|
|
||||||
onSelectValue(value);
|
|
||||||
}
|
|
||||||
if (otherProps.onClick) {
|
|
||||||
otherProps.onClick(e);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={style}
|
|
||||||
>
|
|
||||||
<div class={`${optionPrefixCls}-content`}>
|
|
||||||
{renderOption ? renderOption(data) : content}
|
|
||||||
</div>
|
|
||||||
{isValidElement(menuItemSelectedIcon) || selected}
|
|
||||||
{iconVisible && (
|
|
||||||
<TransBtn
|
|
||||||
class={`${itemPrefixCls}-option-state`}
|
|
||||||
customizeIcon={menuItemSelectedIcon}
|
|
||||||
customizeIconProps={{ isSelected: selected }}
|
|
||||||
>
|
|
||||||
{selected ? '✓' : null}
|
|
||||||
</TransBtn>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
></List>
|
></List>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -269,42 +269,43 @@ export default defineComponent({
|
||||||
itemHeight={itemHeight}
|
itemHeight={itemHeight}
|
||||||
prefixCls={`${prefixCls}-list`}
|
prefixCls={`${prefixCls}-list`}
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
children={(treeNode: FlattenNode) => {
|
v-slots={{
|
||||||
const {
|
default: (treeNode: FlattenNode) => {
|
||||||
pos,
|
const {
|
||||||
data: { ...restProps },
|
pos,
|
||||||
title,
|
data: { ...restProps },
|
||||||
key,
|
title,
|
||||||
isStart,
|
key,
|
||||||
isEnd,
|
isStart,
|
||||||
} = treeNode;
|
isEnd,
|
||||||
const mergedKey = getKey(key, pos);
|
} = treeNode;
|
||||||
delete restProps.key;
|
const mergedKey = getKey(key, pos);
|
||||||
delete restProps.children;
|
delete restProps.key;
|
||||||
|
delete restProps.children;
|
||||||
|
|
||||||
const treeNodeProps = getTreeNodeProps(mergedKey, treeNodeRequiredProps);
|
const treeNodeProps = getTreeNodeProps(mergedKey, treeNodeRequiredProps);
|
||||||
|
return (
|
||||||
return (
|
<MotionTreeNode
|
||||||
<MotionTreeNode
|
{...restProps}
|
||||||
{...restProps}
|
{...treeNodeProps}
|
||||||
{...treeNodeProps}
|
title={title}
|
||||||
title={title}
|
active={!!activeItem && key === activeItem.key}
|
||||||
active={!!activeItem && key === activeItem.key}
|
pos={pos}
|
||||||
pos={pos}
|
data={treeNode.data}
|
||||||
data={treeNode.data}
|
isStart={isStart}
|
||||||
isStart={isStart}
|
isEnd={isEnd}
|
||||||
isEnd={isEnd}
|
motion={motion}
|
||||||
motion={motion}
|
motionNodes={key === MOTION_KEY ? transitionRange.value : null}
|
||||||
motionNodes={key === MOTION_KEY ? transitionRange.value : null}
|
motionType={motionType.value}
|
||||||
motionType={motionType.value}
|
onMotionStart={onListChangeStart}
|
||||||
onMotionStart={onListChangeStart}
|
onMotionEnd={onMotionEnd}
|
||||||
onMotionEnd={onMotionEnd}
|
treeNodeRequiredProps={treeNodeRequiredProps}
|
||||||
treeNodeRequiredProps={treeNodeRequiredProps}
|
onMousemove={() => {
|
||||||
onMousemove={() => {
|
onActiveChange(null);
|
||||||
onActiveChange(null);
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
);
|
||||||
);
|
},
|
||||||
}}
|
}}
|
||||||
></VirtualList>
|
></VirtualList>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -398,7 +398,7 @@ const List = defineComponent({
|
||||||
virtual,
|
virtual,
|
||||||
component: Component = 'div',
|
component: Component = 'div',
|
||||||
onScroll,
|
onScroll,
|
||||||
children,
|
children = this.$slots.default,
|
||||||
style,
|
style,
|
||||||
class: className,
|
class: className,
|
||||||
...restProps
|
...restProps
|
||||||
|
|
Loading…
Reference in New Issue