From b843bea492e9b0627a06ae99ce8834d954ca79cb Mon Sep 17 00:00:00 2001 From: Sg Date: Thu, 21 Dec 2023 03:17:37 +0800 Subject: [PATCH] feat(tabs): add animation for segment (#5063) * feat(tabs): add animation for segment * fix(tabs): fix the initialization problem * fix(tabs): fix change tabs's failure to move segment * refactor(tabs): refactor watcher to function * refactor(tabs): rename style class & remove unused class --- src/tabs/src/Tabs.tsx | 48 ++++++++++++++++++++++--------- src/tabs/src/styles/index.cssr.ts | 12 ++++++-- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/tabs/src/Tabs.tsx b/src/tabs/src/Tabs.tsx index 36a6370d7..e8e958819 100644 --- a/src/tabs/src/Tabs.tsx +++ b/src/tabs/src/Tabs.tsx @@ -412,6 +412,33 @@ export default defineComponent({ barEl.classList.remove(disableTransitionClassName) } + const tabsRailElRef = ref(null) + const segmentCapsuleElRef = ref(null) + + function updateSegmentPosition (): void { + if (props.type === 'segment') { + const { value: tabsRailEl } = tabsRailElRef + if (!tabsRailEl) return + void nextTick(() => { + tabsRailEl.classList.add('transition-disabled') + const activeTabEl = tabsRailEl.querySelector('.n-tabs-tab--active') + if (activeTabEl && segmentCapsuleElRef.value) { + const rect = activeTabEl.getBoundingClientRect() + // move segment capsule to match the position of the active tab + segmentCapsuleElRef.value.style.width = `${rect.width}px` + segmentCapsuleElRef.value.style.transform = `translateX(${ + rect.left - tabsRailEl.getBoundingClientRect().left - 3 + }px)` + } + tabsRailEl.classList.remove('transition-disabled') + }) + } + } + + watch([mergedValueRef, tabsRailElRef], () => { + updateSegmentPosition() + }) + let memorizedWidth = 0 function _handleNavResize (entry: ResizeObserverEntry): void { if (entry.contentRect.width === 0 && entry.contentRect.height === 0) { @@ -542,20 +569,6 @@ export default defineComponent({ } }) - const tabsRailElRef = ref(null) - watch(mergedValueRef, () => { - if (props.type === 'segment') { - const tabsRailEl = tabsRailElRef.value - if (tabsRailEl) { - void nextTick(() => { - tabsRailEl.classList.add('transition-disabled') - void tabsRailEl.offsetWidth - tabsRailEl.classList.remove('transition-disabled') - }) - } - } - }) - const exposedMethods: TabsInst = { syncBarPosition: () => { updateCurrentBarStyle() @@ -659,6 +672,7 @@ export default defineComponent({ mergedValue: mergedValueRef, renderedNames: new Set>(), tabsRailElRef, + segmentCapsuleElRef, tabsPaneWrapperRef, tabsElRef, barElRef, @@ -833,6 +847,12 @@ export default defineComponent({ )} {isSegment ? (
+
+   +
{showPane ? tabPaneChildren.map((tabPaneVNode: any, index: number) => { renderNameListRef.value.push( diff --git a/src/tabs/src/styles/index.cssr.ts b/src/tabs/src/styles/index.cssr.ts index ed29b613b..18916f24c 100644 --- a/src/tabs/src/styles/index.cssr.ts +++ b/src/tabs/src/styles/index.cssr.ts @@ -104,6 +104,15 @@ export default cB('tabs', ` display: flex; align-items: center; `, [ + cB('tabs-capsule', ` + border-radius: var(--n-tab-border-radius); + width: 50px; + position: absolute; + pointer-events: none; + background-color: var(--n-tab-color-segment); + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .08); + transition: 0.3s; + `), cB('tabs-tab-wrapper', ` flex-basis: 0; flex-grow: 1; @@ -122,8 +131,6 @@ export default cB('tabs', ` cM('active', ` font-weight: var(--n-font-weight-strong); color: var(--n-tab-text-color-active); - background-color: var(--n-tab-color-segment); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .08); `), c('&:hover', ` color: var(--n-tab-text-color-hover); @@ -283,6 +290,7 @@ export default cB('tabs', ` cE('label', ` display: flex; align-items: center; + z-index: 1; `) ]), cB('tabs-bar', `