Skip to content

Commit

Permalink
feat(cascader): add virtual list support (#2577)
Browse files Browse the repository at this point in the history
  • Loading branch information
flsion authored Jul 21, 2023
1 parent 3892fde commit af33cb8
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 49 deletions.
3 changes: 3 additions & 0 deletions packages/web-vue/components/cascader/README.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ description: Refers to the use of multi-level classification to separate the opt
@import ./__demo__/panel.md
@import ./__demo__/virtual.md
## API
Expand Down Expand Up @@ -73,6 +75,7 @@ description: Refers to the use of multi-level classification to separate the opt
|value-key|Used to determine the option key value attribute name|`string`|`'value'`|2.29.0|
|fallback|Options that do not exist in custom values|`boolean\| (( value: \| string \| number \| Record<string, unknown> \| (string \| number \| Record<string, unknown>)[] ) => string)`|`true`|2.29.0|
|expand-child|whether to expand the submenu|`boolean`|`false`|2.29.0|
|virtual-list-props|Pass the virtual list attribute, pass in this parameter to turn on virtual scrolling [VirtualListProps](#VirtualListProps)|`VirtualListProps`|`-`|2.49.0|
### `<cascader>` Events

|Event Name|Description|Parameters|
Expand Down
3 changes: 3 additions & 0 deletions packages/web-vue/components/cascader/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ description: 指在选择器选项数量较多时,采用多级分类的方式
@import ./__demo__/panel.md
@import ./__demo__/virtual.md
## API
Expand Down Expand Up @@ -71,6 +73,7 @@ description: 指在选择器选项数量较多时,采用多级分类的方式
|value-key|用于确定选项键值的属性名|`string`|`'value'`|2.29.0|
|fallback|自定义不存在选项的值的展示|`boolean\| (( value: \| string \| number \| Record<string, unknown> \| (string \| number \| Record<string, unknown>)[] ) => string)`|`true`|2.29.0|
|expand-child|是否展开子菜单|`boolean`|`false`|2.29.0|
|virtual-list-props|传递虚拟列表属性,传入此参数以开启虚拟滚动 [VirtualListProps](#VirtualListProps)|`VirtualListProps`|`-`|2.49.0|
### `<cascader>` Events

|事件名|描述|参数|
Expand Down
2 changes: 2 additions & 0 deletions packages/web-vue/components/cascader/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ description: Refers to the use of multi-level classification to separate the opt
@import ./__demo__/panel.md
@import ./__demo__/virtual.md
## API
%%API(cascader.vue)%%
Expand Down
86 changes: 86 additions & 0 deletions packages/web-vue/components/cascader/__demo__/virtual.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
```yaml
title:
zh-CN: 虚拟列表
en-US: Virtual List
```
## zh-CN
虚拟列表的使用方法。
---
## en-US
How to use the virtual list.
---
```vue

<template>
<a-cascader :options="options" :style="{width:'320px'}" placeholder="Please select ..."
:virtual-list-props="{height:200}" />
</template>

<script>
export default {
setup() {
const options = [
{
value: 'beijing',
label: 'Beijing',
children: [
{
value: 'chaoyang',
label: 'ChaoYang',
children: [
{
value: 'datunli',
label: 'Datunli',
},
],
},
{
value: 'haidian',
label: 'Haidian',
},
{
value: 'dongcheng',
label: 'Dongcheng',
},
{
value: 'xicheng',
label: 'Xicheng',
children: [
{
value: 'jinrongjie',
label: 'Jinrongjie',
},
{
value: 'tianqiao',
label: 'Tianqiao',
},
],
},
],
},
{
value: 'shanghai',
label: 'Shanghai',
children: Array(1000).fill(null).map((_, index) => {
return {
value: `Option ${index}`,
label: `Option ${index}`
}
})
},
];

return {
options
}
},
}
</script>
```
67 changes: 18 additions & 49 deletions packages/web-vue/components/cascader/base-cascader-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { defineComponent, inject, PropType, TransitionGroup } from 'vue';
import { configProviderInjectionKey } from '../config-provider/context';
import { CascaderOptionInfo } from './interface';
import CascaderOption from './cascader-option';
import { getPrefixCls } from '../_utils/global-config';
import Empty from '../empty';
import Spin from '../spin';
import Scrollbar from '../scrollbar';
import { VirtualListProps } from '../_components/virtual-list-v2/interface';
import CascaderColumn from './cascader-column';

export default defineComponent({
name: 'BaseCascaderPanel',
Expand All @@ -27,6 +27,9 @@ export default defineComponent({
checkStrictly: Boolean,
loading: Boolean,
dropdown: Boolean,
virtualListProps: {
type: Object as PropType<VirtualListProps>,
},
},
setup(props, { slots }) {
const prefixCls = getPrefixCls('cascader');
Expand All @@ -39,50 +42,6 @@ export default defineComponent({
);
};

const renderColumn = (column: CascaderOptionInfo[], level = 0) => {
return (
<div
class={`${prefixCls}-panel-column`}
key={`column-${level}`}
style={{ zIndex: props.totalLevel - level }}
>
<Scrollbar class={`${prefixCls}-column-content`}>
{column.length === 0 ? (
<div class={`${prefixCls}-list-empty`}>{renderEmpty()}</div>
) : (
<ul
role="menu"
class={[
`${prefixCls}-list`,
{
[`${prefixCls}-list-multiple`]: Boolean(props?.multiple),
[`${prefixCls}-list-strictly`]: Boolean(
props?.checkStrictly
),
},
]}
>
{column.map((item) => {
return (
<CascaderOption
key={item.key}
option={item}
active={
props.selectedPath.includes(item.key) ||
item.key === props.activeKey
}
multiple={props.multiple}
checkStrictly={props.checkStrictly}
/>
);
})}
</ul>
)}
</Scrollbar>
</div>
);
};

const renderContent = () => {
if (props.loading) {
return (
Expand All @@ -104,9 +63,19 @@ export default defineComponent({
</div>
);
}
return props.displayColumns.map((column, index) =>
renderColumn(column, index)
);
return props.displayColumns.map((column, index) => (
<CascaderColumn
key={`column-${index}`}
column={column}
level={index}
selectedPath={props.selectedPath}
activeKey={props.activeKey}
totalLevel={props.totalLevel}
multiple={props.multiple}
checkStrictly={props.checkStrictly}
virtualListProps={props.virtualListProps}
/>
));
};

return () => (
Expand Down
118 changes: 118 additions & 0 deletions packages/web-vue/components/cascader/cascader-column.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { defineComponent, inject, PropType, ref } from 'vue';
import { configProviderInjectionKey } from '../config-provider/context';
import { CascaderOptionInfo } from './interface';
import CascaderOption from './cascader-option';
import { getPrefixCls } from '../_utils/global-config';
import Empty from '../empty';
import Scrollbar from '../scrollbar';
import VirtualList from '../_components/virtual-list-v2';
import { VirtualListProps } from '../_components/virtual-list-v2/interface';

export default defineComponent({
name: 'CascaderColumn',
props: {
column: {
type: Array as PropType<CascaderOptionInfo[]>,
required: true,
},
level: {
type: Number,
default: 0,
},
selectedPath: {
type: Array as PropType<string[]>,
required: true,
},
activeKey: String,
totalLevel: {
type: Number,
required: true,
},
multiple: Boolean,
checkStrictly: Boolean,
virtualListProps: {
type: Object as PropType<VirtualListProps>,
},
},
setup(props, { slots }) {
const prefixCls = getPrefixCls('cascader');
const configCtx = inject(configProviderInjectionKey, undefined);
const virtualListRef = ref();
const isVirtual = ref(Boolean(props.virtualListProps));

const renderEmpty = () => {
return (
slots.empty?.() ??
configCtx?.slots.empty?.({ component: 'cascader' }) ?? <Empty />
);
};

return () => {
return (
<div
class={`${prefixCls}-panel-column`}
style={{ zIndex: props.totalLevel - props.level }}
>
{isVirtual.value ? (
<VirtualList
key={props.column?.length}
{...props.virtualListProps}
ref={virtualListRef}
data={props.column}
v-slots={{
item: ({ item }: { item: CascaderOptionInfo }) => {
return (
<CascaderOption
key={item.key}
option={item}
active={
props.selectedPath.includes(item.key) ||
item.key === props.activeKey
}
multiple={props.multiple}
checkStrictly={props.checkStrictly}
/>
);
},
}}
/>
) : (
<Scrollbar class={`${prefixCls}-column-content`}>
{props.column.length === 0 ? (
<div class={`${prefixCls}-list-empty`}>{renderEmpty()}</div>
) : (
<ul
role="menu"
class={[
`${prefixCls}-list`,
{
[`${prefixCls}-list-multiple`]: Boolean(props?.multiple),
[`${prefixCls}-list-strictly`]: Boolean(
props?.checkStrictly
),
},
]}
>
{props.column.map((item) => {
return (
<CascaderOption
key={item.key}
option={item}
active={
props.selectedPath.includes(item.key) ||
item.key === props.activeKey
}
multiple={props.multiple}
checkStrictly={props.checkStrictly}
/>
);
})}
</ul>
)}
</Scrollbar>
)}
</div>
);
};
},
});
11 changes: 11 additions & 0 deletions packages/web-vue/components/cascader/cascader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
:total-level="totalLevel"
:check-strictly="checkStrictly"
:loading="loading"
:virtual-list-props="virtualListProps"
dropdown
>
<template v-if="$slots.empty" #empty>
Expand Down Expand Up @@ -121,6 +122,7 @@ import { cascaderInjectionKey } from './context';
import { Size } from '../_utils/constant';
import { debounce } from '../_utils/debounce';
import { useFormItem } from '../_hooks/use-form-item';
import { VirtualListProps } from '../_components/virtual-list-v2/interface';
export default defineComponent({
name: 'Cascader',
Expand Down Expand Up @@ -417,6 +419,15 @@ export default defineComponent({
type: Boolean,
default: false,
},
/**
* @zh 传递虚拟列表属性,传入此参数以开启虚拟滚动 [VirtualListProps](#VirtualListProps)
* @en Pass the virtual list attribute, pass in this parameter to turn on virtual scrolling [VirtualListProps](#VirtualListProps)
* @type VirtualListProps
* @version 2.49.0
*/
virtualListProps: {
type: Object as PropType<VirtualListProps>,
},
},
emits: {
'update:modelValue': (
Expand Down

0 comments on commit af33cb8

Please sign in to comment.