From 826bf1dccb2a899268cd467a1cebc8e2e5358a5c Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Tue, 25 May 2021 22:41:06 +0800 Subject: [PATCH] feat(tabs): add label slot for pane & function + vnode typed label --- CHANGELOG.en-US.md | 2 ++ CHANGELOG.zh-CN.md | 2 ++ src/_utils/index.ts | 1 + src/tabs/demos/enUS/index.demo-entry.md | 11 ++++++-- src/tabs/demos/zhCN/index.demo-entry.md | 11 ++++++-- src/tabs/src/Tab.tsx | 12 ++++++-- src/tabs/src/TabPane.tsx | 29 ++++--------------- src/tabs/src/Tabs.tsx | 37 ++++++++++++++++++++++--- 8 files changed, 71 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 28d34fb2f..3db41d4ec 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -6,6 +6,8 @@ - `n-tabs` add `on-close` prop. - `n-tabs` add `on-add` prop. +- `n-tabs` add `label` slot. +- `n-tab-pane`'s `label` prop support render function & VNode. ## 2.9.0 diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index b56a7e474..c3256db72 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -6,6 +6,8 @@ - `n-tabs` 新增 `on-close` 属性 - `n-tabs` 新增 `on-add` 属性 +- `n-tab-pane` 新增 `label` slot +- `n-tab-pane` 的 `label` 属性支持渲染函数和 VNode ## 2.9.0 diff --git a/src/_utils/index.ts b/src/_utils/index.ts index 5b5986dd2..85258df8b 100644 --- a/src/_utils/index.ts +++ b/src/_utils/index.ts @@ -7,6 +7,7 @@ export { getVNodeChildren, keysOf, render, + render as Render, getFirstSlotVNode } from './vue' export type { MaybeArray } from './vue' diff --git a/src/tabs/demos/enUS/index.demo-entry.md b/src/tabs/demos/enUS/index.demo-entry.md index 09c94bafb..4b73ddc10 100644 --- a/src/tabs/demos/enUS/index.demo-entry.md +++ b/src/tabs/demos/enUS/index.demo-entry.md @@ -40,15 +40,22 @@ addable | closable | `boolean` | `false` | Whether to allow tab to close, Only works when tabs type is `'card'`. | | disabled | `boolean` | `false` | | | 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. | -| label | `string` | `undefined` | | +| label | `string \| VNode \| () => VNodeChild` | `undefined` | | | name | `string \| number` | required | | ## Slots -### Tabs, Tab Pane Slots +### Tabs Slots | Name | Parameters | Description | | ------- | ---------- | ----------- | | default | `()` | | | prefix | `()` | | | suffux | `()` | | + +### Tab Pane Slots + +| Name | Parameters | Description | +| ------- | ---------- | ----------- | +| default | `()` | | +| label | `()` | | diff --git a/src/tabs/demos/zhCN/index.demo-entry.md b/src/tabs/demos/zhCN/index.demo-entry.md index 9eed2b778..b3380d43a 100644 --- a/src/tabs/demos/zhCN/index.demo-entry.md +++ b/src/tabs/demos/zhCN/index.demo-entry.md @@ -41,15 +41,22 @@ line-debug | closable | `boolean` | `false` | 是否允许 tab 关闭,只在 tabs type 为 `'card'` 时生效 | | disabled | `boolean` | `false` | | | display-directive | `'if' \| 'show'` | `'if'` | 选择性渲染使用的指令。if 对应 v-if,show 对应 v-show,使用 show 的时候标签页状态切换后不会被重置 | -| label | `string` | `undefined` | | +| label | `string \| VNode \| () => VNodeChild` | `undefined` | | | name | `string \| number` | required | | ## Slots -### Tabs, Tab Pane Slots +### Tabs | 名称 | 参数 | 说明 | | ------- | ---- | ---- | | default | `()` | | | prefix | `()` | | | suffux | `()` | | + +### Tab Pane Slots + +| 名称 | 参数 | 说明 | +| ------- | ---- | ---- | +| default | `()` | | +| label | `()` | | diff --git a/src/tabs/src/Tab.tsx b/src/tabs/src/Tab.tsx index 95b496164..444f334fe 100644 --- a/src/tabs/src/Tab.tsx +++ b/src/tabs/src/Tab.tsx @@ -1,6 +1,7 @@ import { h, defineComponent, inject, computed } from 'vue' -import { NBaseClose, NBaseIcon } from '../../_internal' import { AddIcon } from '../../_internal/icons' +import { NBaseClose, NBaseIcon } from '../../_internal' +import { Render } from '../../_utils' import { tabsInjectionKey } from './interface' import { tabPaneProps } from './TabPane' @@ -60,7 +61,8 @@ export default defineComponent({ label, value, mergedClosable, - style + style, + $slots: { default: defaultSlot } } = this return (
@@ -90,8 +92,12 @@ export default defineComponent({ default: () => }} + ) : defaultSlot ? ( + defaultSlot() + ) : typeof label === 'object' ? ( + label // VNode ) : ( - label ?? name + )} {mergedClosable && this.type === 'card' ? ( diff --git a/src/tabs/src/TabPane.tsx b/src/tabs/src/TabPane.tsx index eeb635ba7..a23185f11 100644 --- a/src/tabs/src/TabPane.tsx +++ b/src/tabs/src/TabPane.tsx @@ -1,17 +1,12 @@ -import { - h, - withDirectives, - vShow, - defineComponent, - inject, - PropType -} from 'vue' +import { h, defineComponent, inject, PropType, VNodeChild, VNode } from 'vue' import { throwError } from '../../_utils' import { tabsInjectionKey } from './interface' import type { ExtractPublicPropTypes } from '../../_utils' export const tabPaneProps = { - label: [String, Number] as PropType, + label: [String, Number, Object, Function] as PropType< + string | number | VNode | (() => VNodeChild) + >, name: { type: [String, Number] as PropType, required: true @@ -39,22 +34,10 @@ export default defineComponent({ throwError('tab-pane', '`n-tab-pane` must be placed inside `n-tabs`.') } return { - mergedClsPrefix: NTab.mergedClsPrefixRef, - type: NTab.typeRef, - value: NTab.valueRef + mergedClsPrefix: NTab.mergedClsPrefixRef } }, render () { - const { name } = this - const useVShow = this.displayDirective === 'show' - const show = this.value === name - return useVShow || show - ? withDirectives( -
- {this.$slots} -
, - [[vShow, !useVShow || show]] - ) - : null + return
{this.$slots}
} }) diff --git a/src/tabs/src/Tabs.tsx b/src/tabs/src/Tabs.tsx index 91aa39641..bed36b9dd 100644 --- a/src/tabs/src/Tabs.tsx +++ b/src/tabs/src/Tabs.tsx @@ -10,7 +10,9 @@ import { toRef, ComponentPublicInstance, VNode, - nextTick + nextTick, + withDirectives, + vShow } from 'vue' import { VResizeObserver, VXScroll } from 'vueuc' import { throttle } from 'lodash-es' @@ -205,7 +207,6 @@ export default defineComponent({ } = entry // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const containerWidth = target.parentElement!.offsetWidth - console.log(width, containerWidth) if (!addTabFixedRef.value) { if (containerWidth < width) { addTabFixedRef.value = true @@ -365,7 +366,11 @@ export default defineComponent({ leftPadded={ index !== 0 && !mergedJustifyContent } - /> + > + {{ + default: tabPaneVNode.children.label + }} + ) })} {!addTabFixed && addable && !isLine @@ -419,12 +424,36 @@ export default defineComponent({
{suffix}
) : null}
- {children} + {filterMapTabPanes(children, this.mergedValue)} ) } }) +function filterMapTabPanes ( + tabPaneVNodes: VNode[], + value: string | number | undefined +): VNode[] { + const children: VNode[] = [] + tabPaneVNodes.forEach((vNode) => { + const { name, displayDirective } = vNode.props as { + name: string | number + displayDirective: 'show' | 'if' | undefined + } + const useVShow = displayDirective === 'show' + 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) + } + }) + return children +} + function createAddTag (addable: Addable, leftPadded: boolean): VNode { return (