Skip to content

Commit

Permalink
feat(cascader): virtual-scroll prop
Browse files Browse the repository at this point in the history
  • Loading branch information
07akioni committed May 17, 2021
1 parent ac4d662 commit efce691
Show file tree
Hide file tree
Showing 14 changed files with 214 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

- `n-tree` add `virtual-scroll` prop.
- `n-data-table` add `virtual-scroll` prop.
- `n-cascader` add `virtual-scroll` prop.

### Fixes

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

- `n-tree` 新增 `virtual-scroll` 属性
- `n-data-table` 新增 `virtual-scroll` 属性
- `n-cascader` 新增 `virtual-scroll` 属性

### Fixes

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ What's more, you don't need to import any CSS to use the components.

### Not too Slow

I try to make it not rather slow. At least select, tree, transfer, table work with virtual list.
I try to make it not rather slow. At least select, tree, transfer, table and cascader work with virtual list.

What's more, ..., no more. Just enjoy it.

Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Naive UI 全量使用 Typescript 编写,和你的 Typescript 项目无缝衔

### 不算太慢

我尽力让它不要太慢。至少 select、tree、transfer、table 都可以用虚拟列表。
我尽力让它不要太慢。至少 select、tree、transfer、table、cascader 都可以用虚拟列表。

顺便一提,...,没有顺便了。祝你使用愉快。

Expand Down
2 changes: 1 addition & 1 deletion demo/pages/docs/introduction/enUS/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ What's more, you don't need to import any CSS to use the components.

## Not too Slow

I try to make it not rather slow. At least select, tree, transfer, table work with virtual list.
I try to make it not rather slow. At least select, tree, transfer, table and cascader work with virtual list.

What's more, ..., no more. Just enjoy it.

Expand Down
2 changes: 1 addition & 1 deletion demo/pages/docs/introduction/zhCN/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Naive UI 全量使用 Typescript 编写,和你的 Typescript 项目无缝衔

## 不算太慢

我尽力让它不要太慢。至少 select、tree、transfer、table 都可以用虚拟列表。
我尽力让它不要太慢。至少 select、tree、transfer、table、cascader 都可以用虚拟列表。

顺便一提,...,没有顺便了。祝你使用愉快。

Expand Down
2 changes: 2 additions & 0 deletions src/cascader/demos/enUS/index.demo-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ size
single-lazy
multiple-lazy
action
virtual
```

## Props
Expand All @@ -35,6 +36,7 @@ action
| show-path | `boolean` | `true` | Whether to show path in selector. |
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | |
| value | `string \| number \| Array<number \| string> \| null` | `undefined` | |
| virtual-scroll | `boolean` | `true` | |
| on-blur | `() => void` | `undefined` | |
| on-focus | `() => void` | `undefined` |
| on-load | `(option: CascaderOption) => Promise<any>` | `undefined` | Callback when click unloaded node. Set `option.children` in the returned promise. Loading is end after the promise is resolved or rejected. |
Expand Down
72 changes: 72 additions & 0 deletions src/cascader/demos/enUS/virtual.demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Large Data

Large data is ok. By default `virtual-scroll` is true, so the demo only wants to show you cascader work with large data.

In the example there are 5000 _ 2 _ 2 = 20000 entries.

```html
<n-space vertical>
<n-space>
<n-space><n-switch v-model:value="leafOnly" />Leaf Only</n-space>
<n-space><n-switch v-model:value="cascade" />Cascade</n-space>
<n-space><n-switch v-model:value="showPath" />Show Path</n-space>
<n-space><n-switch v-model:value="hoverTrigger" />Hover Trigger</n-space>
<n-space><n-switch v-model:value="filterable" />Filterable</n-space>
</n-space>
<n-cascader
v-model:value="value"
placeholder="Useless Value"
:expand-trigger="hoverTrigger ? 'hover' : 'click'"
:options="options"
:cascade="cascade"
:leaf-only="leafOnly"
:show-path="showPath"
:filterable="filterable"
/>
</n-space>
```

```js
function genOptions (depth = 3, iterator = 1, prefix = '') {
const length = iterator === 1 ? 5000 : 2
const options = []
for (let i = 1; i <= length; ++i) {
if (iterator === 1) {
options.push({
value: `v-${i}`,
label: `l-${i}`,
disabled: i % 5 === 0,
children: genOptions(depth, iterator + 1, '' + i)
})
} else if (iterator === depth) {
options.push({
value: `v-${prefix}-${i}`,
label: `l-${prefix}-${i}`,
disabled: i % 5 === 0
})
} else {
options.push({
value: `v-${prefix}-${i}`,
label: `l-${prefix}-${i}`,
disabled: i % 5 === 0,
children: genOptions(depth, iterator + 1, `${prefix}-${i}`)
})
}
}
return options
}

export default {
data () {
return {
leafOnly: true,
cascade: true,
showPath: true,
hoverTrigger: false,
filterable: false,
value: null,
options: genOptions()
}
}
}
```
2 changes: 2 additions & 0 deletions src/cascader/demos/zhCN/index.demo-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ size
single-lazy
multiple-lazy
action
virtual
```

## Props
Expand All @@ -35,6 +36,7 @@ action
| show-path | `boolean` | `true` | 是否在选择器中显示选项路径 |
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | |
| value | `string \| number \| Array<number \| string>` | `undefined` | |
| virtual-scroll | `boolean` | `true` | |
| on-blur | `() => void` | `undefined` | |
| on-focus | `() => void` | `undefined` | |
| on-load | `(option: CascaderOption) => Promise<any>` | `undefined` | 在点击未加载完成节点时的回调,在返回的 promise 中设定 `option.children`,在返回的 promise resolve 或 reject 之后完成加载 |
Expand Down
72 changes: 72 additions & 0 deletions src/cascader/demos/zhCN/virtual.demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# 大量数据

大量数据也可以,`virtual-scroll` 默认是打开的,所以这个例子只是为了给你看看。

下面这个例子有 5000 _ 2 _ 2 = 20000 条数据。

```html
<n-space vertical>
<n-space>
<n-space><n-switch v-model:value="leafOnly" />Leaf Only</n-space>
<n-space><n-switch v-model:value="cascade" />Cascade</n-space>
<n-space><n-switch v-model:value="showPath" />Show Path</n-space>
<n-space><n-switch v-model:value="hoverTrigger" />Hover Trigger</n-space>
<n-space><n-switch v-model:value="filterable" />Filterable</n-space>
</n-space>
<n-cascader
v-model:value="value"
placeholder="没啥用的值"
:expand-trigger="hoverTrigger ? 'hover' : 'click'"
:options="options"
:cascade="cascade"
:leaf-only="leafOnly"
:show-path="showPath"
:filterable="filterable"
/>
</n-space>
```

```js
function genOptions (depth = 3, iterator = 1, prefix = '') {
const length = iterator === 1 ? 5000 : 2
const options = []
for (let i = 1; i <= length; ++i) {
if (iterator === 1) {
options.push({
value: `v-${i}`,
label: `l-${i}`,
disabled: i % 5 === 0,
children: genOptions(depth, iterator + 1, '' + i)
})
} else if (iterator === depth) {
options.push({
value: `v-${prefix}-${i}`,
label: `l-${prefix}-${i}`,
disabled: i % 5 === 0
})
} else {
options.push({
value: `v-${prefix}-${i}`,
label: `l-${prefix}-${i}`,
disabled: i % 5 === 0,
children: genOptions(depth, iterator + 1, `${prefix}-${i}`)
})
}
}
return options
}

export default {
data () {
return {
leafOnly: true,
cascade: true,
showPath: true,
hoverTrigger: false,
filterable: false,
value: null,
options: genOptions()
}
}
}
```
6 changes: 6 additions & 0 deletions src/cascader/src/Cascader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ const cascaderProps = {
default: undefined
},
maxTagCount: [String, Number] as PropType<number | 'responsive'>,
virtualScroll: {
type: Boolean,
default: true
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:value': [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
// deprecated
Expand Down Expand Up @@ -687,6 +691,8 @@ export default defineComponent({
expandTriggerRef: toRef(props, 'expandTrigger'),
isMountedRef: useIsMounted(),
onLoadRef: toRef(props, 'onLoad'),
virtualScrollRef: toRef(props, 'virtualScroll'),
optionHeightRef,
localeRef,
syncCascaderMenuPosition,
syncSelectMenuPosition,
Expand Down
61 changes: 51 additions & 10 deletions src/cascader/src/CascaderSubmenu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { h, ref, defineComponent, inject, PropType } from 'vue'
import { h, ref, defineComponent, inject, PropType, computed } from 'vue'
import { VirtualList, VirtualListRef } from 'vueuc'
import NCascaderOption from './CascaderOption'
import { NScrollbar } from '../../scrollbar'
import type { ScrollbarInst } from '../../scrollbar'
Expand All @@ -7,6 +8,7 @@ import {
CascaderSubmenuInstance,
cascaderInjectionKey
} from './interface'
import { depx } from 'seemly'

export default defineComponent({
name: 'CascaderSubmenu',
Expand All @@ -22,40 +24,79 @@ export default defineComponent({
},
setup () {
const {
virtualScrollRef,
mergedClsPrefixRef,
mergedThemeRef
mergedThemeRef,
optionHeightRef
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
} = inject(cascaderInjectionKey)!
const scrollbarInstRef = ref<ScrollbarInst | null>(null)
const vlInstRef = ref<VirtualListRef | null>(null)
const inst: CascaderSubmenuInstance = {
scroll (index: number, elSize: number) {
scrollbarInstRef.value?.scrollTo({
index,
elSize
})
if (virtualScrollRef.value) {
vlInstRef.value?.scrollTo({
index
})
} else {
scrollbarInstRef.value?.scrollTo({
index,
elSize
})
}
}
}
return {
mergedClsPrefix: mergedClsPrefixRef,
mergedTheme: mergedThemeRef,
scrollbarInstRef,
vlInstRef,
virtualScroll: virtualScrollRef,
itemSize: computed(() => depx(optionHeightRef.value)),
handleVlScroll: () => {
scrollbarInstRef.value?.sync()
},
getVlContainer: () => {
return vlInstRef.value?.listRef
},
getVlContent: () => {
return vlInstRef.value?.itemsRef
},
...inst
}
},
render () {
const { mergedClsPrefix, mergedTheme } = this
const { mergedClsPrefix, mergedTheme, virtualScroll } = this
return (
<div class={`${mergedClsPrefix}-cascader-submenu`}>
<NScrollbar
ref="scrollbarInstRef"
theme={mergedTheme.peers.Scrollbar}
themeOverrides={mergedTheme.peerOverrides.Scrollbar}
container={virtualScroll ? this.getVlContainer : undefined}
content={virtualScroll ? this.getVlContent : undefined}
>
{{
default: () =>
this.tmNodes.map((tmNode) => (
<NCascaderOption key={tmNode.key} tmNode={tmNode} />
))
virtualScroll ? (
<VirtualList
items={this.tmNodes}
itemSize={this.itemSize}
onScroll={this.handleVlScroll}
showScrollbar={false}
ref="vlInstRef"
>
{{
default: ({ item: tmNode }: { item: TmNode }) => (
<NCascaderOption key={tmNode.key} tmNode={tmNode} />
)
}}
</VirtualList>
) : (
this.tmNodes.map((tmNode) => (
<NCascaderOption key={tmNode.key} tmNode={tmNode} />
))
)
}}
</NScrollbar>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/cascader/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export interface CascaderInjection {
cascadeRef: Ref<boolean>
onLoadRef: Ref<((value: BaseOption) => Promise<void>) | undefined>
localeRef: Ref<NLocale['Cascader']>
virtualScrollRef: Ref<boolean>
optionHeightRef: Ref<string>
syncCascaderMenuPosition: () => void
syncSelectMenuPosition: () => void
updateKeyboardKey: (value: Key | null) => void
Expand Down
2 changes: 1 addition & 1 deletion src/tree/demos/enUS/virtual.demo.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Huge Date
# Large Date

Set `virtual-scroll` to use virtual scroll. Note that you should set the height of the tree.

Expand Down

0 comments on commit efce691

Please sign in to comment.