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
This commit is contained in:
Sg 2023-12-21 03:17:37 +08:00 committed by GitHub
parent 0ea72eab04
commit b843bea492
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 16 deletions

View File

@ -412,6 +412,33 @@ export default defineComponent({
barEl.classList.remove(disableTransitionClassName)
}
const tabsRailElRef = ref<HTMLElement | null>(null)
const segmentCapsuleElRef = ref<HTMLElement | null>(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<HTMLElement | null>(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<NonNullable<TabPaneProps['name']>>(),
tabsRailElRef,
segmentCapsuleElRef,
tabsPaneWrapperRef,
tabsElRef,
barElRef,
@ -833,6 +847,12 @@ export default defineComponent({
)}
{isSegment ? (
<div class={`${mergedClsPrefix}-tabs-rail`} ref="tabsRailElRef">
<div
class={`${mergedClsPrefix}-tabs-capsule`}
ref="segmentCapsuleElRef"
>
<Tab name="">&nbsp;</Tab>
</div>
{showPane
? tabPaneChildren.map((tabPaneVNode: any, index: number) => {
renderNameListRef.value.push(

View File

@ -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', `