Skip to content

Commit

Permalink
feat(tabs): support tab-pane lazy load (#1423)
Browse files Browse the repository at this point in the history
* feat(tabs): support `tab-pane` lazy load

* docs(tabs): update docs

* chore(tab-pane): revised name

* perf(tabs): reduce the number of patch

* docs(tabs): update docs

* test(tabs): add test

* refactor(tabs): refactor `lazyload` of `tab-pane`

* fix(tabs): avoid dependency loop

* Apply suggestions from code review

Co-authored-by: 07akioni <[email protected]>
  • Loading branch information
nooooooom and 07akioni authored Oct 26, 2021
1 parent 2efa2f2 commit 079b8f4
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

- `n-tabs` add `on-before-leave` prop, closes [#1337](https://github.com/TuSimple/naive-ui/issues/1337).
- `n-color-picker` add `show-preview` prop, closes [#1281](https://github.com/TuSimple/naive-ui/issues/1281).
- `n-tab-pane`'s `display-directive` prop support `lazyload` option, closes [#1374](https://github.com/TuSimple/naive-ui/issues/1374).

## 2.19.9 (2021-10-18)

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

- `n-tabs` 新增 `on-before-leave` 属性,关闭 [#1337](https://github.com/TuSimple/naive-ui/issues/1337)
- `n-color-picker` 新增 `show-preview` 属性,关闭 [#1281](https://github.com/TuSimple/naive-ui/issues/1281)
- `n-tab-pane` 属性 `display-directive` 支持 `lazyload` 选项,关闭 [#1374](https://github.com/TuSimple/naive-ui/issues/1374)

## 2.19.9 (2021-10-18)

Expand Down
16 changes: 14 additions & 2 deletions src/tabs/demos/enUS/display-directive.demo.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Display directive

You can set tab-panel's display directive to `if` or `show`. When use show, the tab-panel's content won't be reset after tab changes.
You can set tab-panel's display directive to `if` or `show`. When use `show`, the tab-panel's content won't be reset after tab changes. When use `lazyload`, the display effect is the same as `show`, but the content will be lazily loaded.

```html
<n-tabs default-value="show">
Expand All @@ -10,6 +10,9 @@ You can set tab-panel's display directive to `if` or `show`. When use show, the
<n-tab-pane name="if" display-directive="if" tab="if">
<if-input />
</n-tab-pane>
<n-tab-pane name="lazyload" display-directive="lazyload" tab="lazyload">
<lazyload-input />
</n-tab-pane>
</n-tabs>
```

Expand All @@ -33,10 +36,19 @@ const ifInput = defineComponent({
}
})

const lazyloadInput = defineComponent({
render () {
return h(NInput, {
placeholder: 'I will delay loading, and my content will not be reset afterwards'
})
}
})

export default defineComponent({
components: {
showInput,
ifInput
ifInput,
lazyloadInput
}
})
```
2 changes: 1 addition & 1 deletion src/tabs/demos/enUS/index.demo-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ before-leave
| --- | --- | --- | --- |
| closable | `boolean` | `false` | Whether to allow the tag to be closed. Only works when the tag's `type` is `card`. |
| disabled | `boolean` | `false` | Whether to disable. |
| display-directive | `'if' \| 'show'` | `'if'` | The directive to use in conditionally rendering. `if` will use `v-if` and `show` will use `v-show`. When use show directive, the status of tab won't be reset after tab changes. |
| display-directive | `'if' \| 'show'` \| 'lazyload' | `'if'` | The directive to use in conditionally rendering. `if` will use `v-if` and `show` will use `v-show`. When use `show` directive, the status of tab won't be reset after tab changes. When use `lazyload`, the display effect is the same as `show`, but the content will be lazily loaded. |
| tab | `string \| VNode \| () => VNodeChild` | `undefined` | Tab label. |
| name | `string \| number` | `undefined` | Required, the name of the tab. |

Expand Down
16 changes: 14 additions & 2 deletions src/tabs/demos/zhCN/display-directive.demo.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 展示指令

可以制定标签页展示的指令为 `if` 或者 `show`。使用 `show` 的时候标签页内容不会随着切换重置。
可以制定标签页展示的指令为 `if` `show` 或者 `lazyload` 。使用 `show` 的时候标签页内容不会随着切换重置。使用 `lazyload` 的时候显示效果跟 `show` 一致,不过内容会进行延迟加载

```html
<n-tabs default-value="show">
Expand All @@ -10,6 +10,9 @@
<n-tab-pane name="if" display-directive="if" tab="if">
<if-input />
</n-tab-pane>
<n-tab-pane name="lazyload" display-directive="lazyload" tab="lazyload">
<lazyload-input />
</n-tab-pane>
</n-tabs>
```

Expand All @@ -33,10 +36,19 @@ const ifInput = defineComponent({
}
})

const lazyloadInput = defineComponent({
render () {
return h(NInput, {
placeholder: '我会延迟加载,并且之后我的内容不会被重置'
})
}
})

export default defineComponent({
components: {
showInput,
ifInput
ifInput,
lazyloadInput
}
})
```
2 changes: 1 addition & 1 deletion src/tabs/demos/zhCN/index.demo-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ before-leave
| --- | --- | --- | --- |
| closable | `boolean` | `false` | 是否允许关闭标签,只在标签的 `type``card` 时生效 |
| disabled | `boolean` | `false` | 是否禁用 |
| display-directive | `'if' \| 'show'` | `'if'` | 选择性渲染使用的指令,`if` 对应 `v-if``show` 对应 `v-show`,使用 `show` 的时候标签页状态切换后不会被重置 |
| display-directive | `'if' \| 'show' \| 'lazyload'` | `'if'` | 选择性渲染使用的指令,`if` 对应 `v-if``show` 对应 `v-show`,使用 `show` 的时候标签页状态切换后不会被重置,使用 `lazyload` 的时候显示效果跟 `show` 一致,不过内容会进行延迟加载 |
| tab | `string \| VNode \| () => VNodeChild` | `undefined` | 标签的 `tab` |
| name | `string \| number` | `undefined` | 必填,标签的名称 |

Expand Down
2 changes: 1 addition & 1 deletion src/tabs/src/TabPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const tabPaneProps = {
},
disabled: Boolean,
displayDirective: {
type: String as PropType<'if' | 'show'>,
type: String as PropType<'if' | 'show' | 'lazyload'>,
default: 'if'
},
closable: {
Expand Down
39 changes: 26 additions & 13 deletions src/tabs/src/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
nextTick,
withDirectives,
vShow,
watchEffect
watchEffect,
ExtractPropTypes
} from 'vue'
import { VResizeObserver, VXScroll, VXScrollInst } from 'vueuc'
import { throttle } from 'lodash-es'
Expand All @@ -35,6 +36,11 @@ import {
import type { OnUpdateValue, OnUpdateValueImpl } from './interface'
import style from './styles/index.cssr'
import Tab from './Tab'
import { tabPaneProps } from './TabPane'

type TabPaneProps = ExtractPropTypes<typeof tabPaneProps> & {
'display-directive': 'if' | 'show' | 'lazyload'
}

const tabsProps = {
...(useTheme.props as ThemeProps<TabsTheme>),
Expand Down Expand Up @@ -310,6 +316,7 @@ export default defineComponent({
return {
mergedClsPrefix: mergedClsPrefixRef,
mergedValue: mergedValueRef,
renderedNames: new Set<NonNullable<TabPaneProps['name']>>(),
tabsElRef,
barElRef,
addTabInstRef,
Expand Down Expand Up @@ -535,36 +542,42 @@ export default defineComponent({
<div class={`${mergedClsPrefix}-tabs-nav__suffix`}>{suffix}</div>
) : null}
</div>
{filterMapTabPanes(children, this.mergedValue)}
{filterMapTabPanes(children, this.mergedValue, this.renderedNames)}
</div>
)
}
})

function filterMapTabPanes (
tabPaneVNodes: VNode[],
value: string | number | null
value: string | number | null,
renderedNames: Set<string | number>
): VNode[] {
const children: VNode[] = []
tabPaneVNodes.forEach((vNode) => {
const {
name,
displayDirective,
'display-directive': _displayDirective
} = vNode.props as {
name: string | number
displayDirective: 'show' | 'if' | undefined
'display-directive': 'show' | 'if' | undefined
}
const useVShow = displayDirective === 'show' || _displayDirective === 'show'
} = vNode.props as TabPaneProps
const matchDisplayDirective = (
directive: TabPaneProps['displayDirective']
): boolean =>
displayDirective === directive || _displayDirective === directive
const show = value === name
if (vNode.key !== undefined) {
vNode.key = name
}
if (useVShow) {
children.push(withDirectives(vNode, [[vShow, show]]))
} else if (show) {
children.push(vNode)
if (
show ||
matchDisplayDirective('show') ||
(matchDisplayDirective('lazyload') && renderedNames.has(name))
) {
if (!renderedNames.has(name)) {
renderedNames.add(name)
}
const useVShow = !matchDisplayDirective('if')
children.push(useVShow ? withDirectives(vNode, [[vShow, show]]) : vNode)
}
})
return children
Expand Down
35 changes: 35 additions & 0 deletions src/tabs/tests/Tabs.spec.ts → src/tabs/tests/Tabs.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,39 @@ describe('n-tabs', () => {
wrapper.find('.n-tabs-scroll-padding').attributes('style')
).toContain('width: 100px;')
})

it('should work with `display-directive` prop', async () => {
const displayDirectives: Array<'show' | 'if' | 'lazyload'> = [
'show',
'if',
'lazyload'
]
const wrapper = mount(NTabs, {
props: { value: 'show' },
slots: {
default: () =>
displayDirectives.map((directive) => (
<NTabPane
displayDirective={directive}
tab={directive}
name={directive}
>
{{ default: () => <span class={`test-${directive}`} /> }}
</NTabPane>
))
}
})
await wrapper.setProps({ value: 'if' })
expect(wrapper.find('.test-show').exists()).toEqual(true)
expect(wrapper.find('.test-if').exists()).toEqual(true)
expect(wrapper.find('.test-lazyload').exists()).toEqual(false)
await wrapper.setProps({ value: 'lazyload' })
expect(wrapper.find('.test-show').exists()).toEqual(true)
expect(wrapper.find('.test-if').exists()).toEqual(false)
expect(wrapper.find('.test-lazyload').exists()).toEqual(true)
await wrapper.setProps({ value: 'show' })
expect(wrapper.find('.test-show').exists()).toEqual(true)
expect(wrapper.find('.test-if').exists()).toEqual(false)
expect(wrapper.find('.test-lazyload').exists()).toEqual(true)
})
})

0 comments on commit 079b8f4

Please sign in to comment.