Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(comp:tabs): offset error when setting selectedKey #1236

Merged
merged 1 commit into from
Oct 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions packages/components/tabs/demo/Scroll.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<IxTab v-for="panel in panels" :key="panel" :title="'Tab ' + panel"> Content of Tab {{ panel }} </IxTab>
</IxTabs>
<IxSpace align="center">
<IxButton @click="onOpen">addTab</IxButton>
<IxButton @click="addTab">addTab</IxButton>
<IxButton @click="closeTab">closeTab</IxButton>
移动到第几个:<IxInputNumber v-model:value="selectedKey" :max="20" :min="1"></IxInputNumber>
</IxSpace>
Expand All @@ -23,13 +23,17 @@
<script setup lang="ts">
import { ref } from 'vue'
const selectedKey = ref(1)
const selectedKey = ref(7)
const panels = ref([1, 2, 3])
const onOpen = () => {
panels.value.push((panels.value[panels.value.length - 1] || 0) + 1)
const panels = ref([1, 2, 3, 4, 5, 6, 7])
const addTab = () => {
const key = (panels.value[panels.value.length - 1] || 0) + 1
panels.value.push(key)
selectedKey.value = key
}
const closeTab = () => {
panels.value.pop()
const key = panels.value[panels.value.length - 1]
selectedKey.value = key
}
</script>
68 changes: 42 additions & 26 deletions packages/components/tabs/src/InternalTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,33 @@ export default defineComponent({

const [navOffset, setNavOffset] = useState(0)
const [barStyle, setBarStyle] = useState<CSSProperties>({})
const { navSize, navWrapperSize, navPreNextSize, selectedElSize, syncNavElSize, syncSelectedElSize } =
useNavRelatedElSize(isHorizontal, navWrapperElRef, navElRef, navPreElRef, selectedElRef)
const { selectedElOffset, syncSelectedElOffset } = useSelectedElOffset(isHorizontal, navPreNextSize, selectedElRef)
const {
navSize,
navWrapperSize,
navPreNextSize,
selectedElSize,
setNavElSize,
setSelectedElSize,
setNavPreNextElSize,
} = useNavRelatedElSize(isHorizontal, navWrapperElRef, navElRef, navPreElRef, selectedElRef)
const { selectedElOffset, setSelectedElOffset } = useSelectedElOffset(isHorizontal, navPreNextSize, selectedElRef)

const hasScroll = computed(() => {
return navSize.value! > navWrapperSize.value
return navSize.value > navWrapperSize.value
})

watch(
hasScroll,
() => {
setNavPreNextElSize()
updateNavBarStyle()
updateSelectedOffset()
},
{
flush: 'post',
},
)

const selectedElVisibleSize = useSelectedElVisibleSize(navWrapperSize, selectedElOffset, navOffset)

// 处理存在滚动状态下,滚动到被选中的tab,并修正其位置
Expand Down Expand Up @@ -135,6 +154,14 @@ export default defineComponent({
})
})

const navWrapperClass = computed(() => {
const prefixCls = mergedPrefixCls.value
return normalizeClass({
[`${prefixCls}-nav-wrapper`]: true,
[`${prefixCls}-nav-wrapper-has-scroll`]: hasScroll.value,
})
})

const curryNavPreNextClasses = curry(useNavPreNextClasses)(props, mergedPrefixCls)
const navPreClasses = curryNavPreNextClasses('pre', preReached)
const navNextClasses = curryNavPreNextClasses('next', nextReached)
Expand Down Expand Up @@ -203,9 +230,10 @@ export default defineComponent({
}

const update = () => {
syncNavElSize()
syncSelectedElSize()
syncSelectedElOffset()
setNavElSize()
setNavPreNextElSize()
setSelectedElSize()
setSelectedElOffset()
updateNavBarStyle()
judgePreNextStatus()
}
Expand Down Expand Up @@ -239,19 +267,11 @@ export default defineComponent({

watch(
navSize,
(val, oldSize) => {
(currentSize, oldSize) => {
let offset = navOffset.value
const currentSize = val!
if (currentSize > oldSize && isAddTabs) {
offset += currentSize - oldSize
if (hasScroll.value) {
setNavOffset(offset)
}
} else if (currentSize < oldSize && !isAddTabs) {
if (currentSize < oldSize && !isAddTabs) {
offset += currentSize - oldSize
if (offset >= 0) {
setNavOffset(offset)
}
setNavOffset(offset > 0 ? offset : 0)
}
},
{
Expand All @@ -271,8 +291,9 @@ export default defineComponent({
watch(
selectedElRef,
() => {
syncSelectedElSize()
syncSelectedElOffset()
setSelectedElSize()
setSelectedElOffset()
setSelectedElOffset()
updateSelectedOffset()
updateNavBarStyle()
},
Expand Down Expand Up @@ -305,12 +326,7 @@ export default defineComponent({

return (
<div class={classes.value}>
<div
class={`${mergedPrefixCls.value}-nav-wrapper ${
hasScroll.value ? `${mergedPrefixCls.value}-nav-wrapper-has-scroll` : ''
}`}
ref={navWrapperElRef}
>
<div class={navWrapperClass.value} ref={navWrapperElRef}>
{hasScroll.value && (
<IxIcon
class={navPreClasses.value}
Expand Down
6 changes: 3 additions & 3 deletions packages/components/tabs/src/composables/useOffset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useState } from '@idux/cdk/utils'

export interface Offset {
selectedElOffset: ComputedRef<number>
syncSelectedElOffset: () => void
setSelectedElOffset: () => void
}

export function useSelectedElOffset(
Expand All @@ -26,13 +26,13 @@ export function useSelectedElOffset(
() => (isHorizontal.value ? selectedLeft.value : selectedTop.value) + navPreNextSize.value,
)

const syncSelectedElOffset = () => {
const setSelectedElOffset = () => {
setSelectedLeft(selectedElRef.value?.offsetLeft ?? 0)
setSelectedTop(selectedElRef.value?.offsetTop ?? 0)
}

return {
selectedElOffset,
syncSelectedElOffset,
setSelectedElOffset,
}
}
21 changes: 11 additions & 10 deletions packages/components/tabs/src/composables/useSize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import type { IconInstance } from '@idux/components/icon'

import { type ComputedRef, type Ref, computed, watchEffect } from 'vue'
import { type ComputedRef, type Ref, computed } from 'vue'

import { useState } from '@idux/cdk/utils'

Expand All @@ -16,8 +16,9 @@ export interface NavRelatedElSize {
navWrapperSize: ComputedRef<number>
navPreNextSize: ComputedRef<number>
selectedElSize: ComputedRef<number>
syncNavElSize: () => void
syncSelectedElSize: () => void
setNavElSize: () => void
setSelectedElSize: () => void
setNavPreNextElSize: () => void
}

export function useNavRelatedElSize(
Expand All @@ -43,32 +44,32 @@ export function useNavRelatedElSize(
const selectedElSize = computed(() => (isHorizontal.value ? selectedWidth.value : selectedHeight.value))

// dom 的size无法响应式获取,只能手动获取
const syncNavElSize = () => {
const setNavElSize = () => {
setNavWrapperWidth(navWrapperElRef.value?.offsetWidth ?? 0)
setNavWrapperHeight(navWrapperElRef.value?.offsetHeight ?? 0)

setNavWidth(navElRef.value?.offsetWidth ?? 0)
setNavHeight(navElRef.value?.offsetHeight ?? 0)
}

const syncSelectedElSize = () => {
const setSelectedElSize = () => {
setSelectedWidth(selectedElRef.value?.offsetWidth ?? 0)
setSelectedHeight(selectedElRef.value?.offsetHeight ?? 0)
}

// 向前、向后按钮是动态渲染的,所以可以使用 watchEffect 获取其size
watchEffect(() => {
const setNavPreNextElSize = () => {
setNavPreNextWidth(navPreElRef.value?.$el.offsetWidth ?? 0)
setNavPreNextHeight(navPreElRef.value?.$el.offsetHeight ?? 0)
})
}

return {
navSize,
navWrapperSize,
navPreNextSize,
selectedElSize,
syncNavElSize,
syncSelectedElSize,
setNavElSize,
setSelectedElSize,
setNavPreNextElSize,
}
}

Expand Down