From eca2643273d0563578c590059d1bef017b8f8b9b Mon Sep 17 00:00:00 2001 From: Uyarn Date: Wed, 19 Jul 2023 17:04:52 +0800 Subject: [PATCH] fix(tabs): change tab navs dom to method render --- src/tabs/tab-nav.tsx | 94 ++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/src/tabs/tab-nav.tsx b/src/tabs/tab-nav.tsx index 30030577b..64efd1c86 100644 --- a/src/tabs/tab-nav.tsx +++ b/src/tabs/tab-nav.tsx @@ -1,34 +1,25 @@ -import { VNode } from 'vue'; import debounce from 'lodash/debounce'; import { ChevronLeftIcon as TdChevronLeftIcon, ChevronRightIcon as TdChevronRightIcon, AddIcon as TdAddIcon, } from 'tdesign-icons-vue'; +import type { ComponentPublicInstance } from '@vue/composition-api'; import TTabPanel from './tab-panel'; import TTabNavItem from './tab-nav-item'; import { emitEvent } from '../utils/event'; import { firstUpperCase } from '../utils/helper'; -import { TdTabsProps, TdTabPanelProps } from './type'; import tabProps from './props'; import { renderTNodeJSX } from '../utils/render-tnode'; -import { TabPanelProps } from '.'; import { getClassPrefixMixins, getGlobalIconMixins } from '../config-provider/config-receiver'; import mixins from '../utils/mixins'; +import type { TdTabsProps } from './type'; + const classPrefixMixins = getClassPrefixMixins('tab__nav'); const getDomWidth = (dom: HTMLElement): number => dom?.offsetWidth || 0; -const getActiveTabEl = (navs: Array, value: TabPanelProps['value']): HTMLElement => { - for (let i = 0; i < navs.length; i++) { - if ((navs[i].componentOptions.propsData as TdTabPanelProps).value === value) { - return navs[i].componentInstance?.$el as HTMLElement; - } - } - return null; -}; - interface GetLeftCoverWidth { leftZone: HTMLElement; leftIcon: HTMLElement; @@ -94,24 +85,19 @@ export default mixins(classPrefixMixins, getGlobalIconMixins()).extend({ }; }, computed: { - navs(): Array { - return this.panels.map((panel, index) => ( - this.tabClick(e, panel)} - onRemove={this.removeBtnClick} - > - )); + navs(): Array> { + return this.panels.map((panel, index) => ({ + ref: `tabItem${index}`, + key: panel.value, + theme: this.theme, + size: this.size, + placement: this.placement, + active: panel.value === this.value, + disabled: this.disabled || panel.disabled, + removable: panel.removable, + value: panel.value, + panel, + })); }, wrapTransformStyle(): { [key: string]: string } { if (['left', 'right'].includes(this.placement.toLowerCase())) return {}; @@ -177,6 +163,13 @@ export default mixins(classPrefixMixins, getGlobalIconMixins()).extend({ navsContainerStyle(): object { return this.addable ? { 'min-height': '48px' } : null; }, + activeElement(): HTMLElement { + const activeIndx = this.navs.findIndex((nav) => nav.active); + if (activeIndx > -1) { + return (this.$refs[`tabItem${activeIndx}`] as unknown as ComponentPublicInstance)?.$el; + } + return null; + }, }, watch: { dataCanUpdateArrow() { @@ -244,18 +237,24 @@ export default mixins(classPrefixMixins, getGlobalIconMixins()).extend({ return ['width', 'left']; }; let offset = 0; + const { activeElement } = this; + if (!activeElement) return {}; + const [sizePropName, offsetPropName] = getPropName(); let i = 0; for (; i < this.navs.length; i++) { - if ((this.navs[i].componentInstance as InstanceType)?.value === this.value) { + if (this.navs[i].active) { break; } - offset += this.navs[i].componentInstance?.$el?.[`client${firstUpperCase(sizePropName)}`] || 0; + offset + += (this.$refs[`tabItem${i}`] as unknown as ComponentPublicInstance)?.$el?.[ + `client${firstUpperCase(sizePropName)}` + ] || 0; } - if (!this.navs[i]) return {}; + return { [offsetPropName]: `${offset}px`, - [sizePropName]: `${this.navs[i].componentInstance?.$el?.[`client${firstUpperCase(sizePropName)}`] || 0}px`, + [sizePropName]: `${activeElement?.[`client${firstUpperCase(sizePropName)}`] || 0}px`, }; }; this.navBarStyle = getNavBarStyle(); @@ -265,7 +264,7 @@ export default mixins(classPrefixMixins, getGlobalIconMixins()).extend({ if (['left', 'right'].includes(this.placement.toLowerCase())) return; const container = this.$refs.navsContainer as HTMLElement; - const activeTabEl: HTMLElement = getActiveTabEl(this.navs, this.value); + const activeTabEl: HTMLElement = this.activeElement; const totalWidthBeforeActiveTab = activeTabEl?.offsetLeft; const containerWidth = getDomWidth(container); if (totalWidthBeforeActiveTab > containerWidth) this.scrollLeft = totalWidthBeforeActiveTab; @@ -360,7 +359,7 @@ export default mixins(classPrefixMixins, getGlobalIconMixins()).extend({ if (['left', 'right'].includes(this.placement)) { return false; } - const activeTabEl: HTMLElement = getActiveTabEl(this.navs, this.value); + const activeTabEl: HTMLElement = this.activeElement; if (!activeTabEl) { // 如果没有当前 value 对应的tab,一种情况是真没有;一种情况是在修改value的同时,新增了一个值为该value的tab。后者因为navs的更新在$nextTick之后,所以得等下一个updated才能拿到新的tab if (needCheckUpdate) { @@ -418,14 +417,31 @@ export default mixins(classPrefixMixins, getGlobalIconMixins()).extend({ removeBtnClick({ e, value, index }: Parameters[0]) { emitEvent>(this, 'remove', { e, value, index }); }, - + renderPanelContent() { + return this.navs.map((panel, index) => ( + this.tabClick(e, panel.panel)} + onRemove={this.removeBtnClick} + > + )); + }, renderArrows() { const { ChevronLeftIcon, ChevronRightIcon, AddIcon } = this.useGlobalIcon({ ChevronLeftIcon: TdChevronLeftIcon, ChevronRightIcon: TdChevronRightIcon, AddIcon: TdAddIcon, }); - return [
{this.renderNavBar()} - {this.navs} + {this.renderPanelContent()}