Skip to content

Commit

Permalink
fix(tabs): scroll to better position when active tab is in middle (#3699
Browse files Browse the repository at this point in the history
)

* feat(tabs): calculate mounted correct position

* feat(tabs): scroll to better position when active tabs is in middle

* chore: update snapshot and demo
  • Loading branch information
uyarn authored Dec 7, 2023
1 parent 821dc1c commit c82c3d3
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 20 deletions.
4 changes: 2 additions & 2 deletions src/tabs/_example/combination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</t-radio-group>
</t-space>
<t-tabs v-model="value" :theme="theme">
<t-tab-panel v-for="index in 20" :key="index" :value="index + ''" :label="`选项卡${index}`">
<t-tab-panel v-for="index in 30" :key="index" :value="index + ''" :label="`选项卡${index}`">
<p style="padding: 25px">选项卡{{ index + 1 }}</p>
</t-tab-panel>
</t-tabs>
Expand All @@ -17,6 +17,6 @@
<script setup>
import { ref } from 'vue';
const value = ref('20');
const value = ref('22');
const theme = ref('normal');
</script>
40 changes: 25 additions & 15 deletions src/tabs/tab-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default defineComponent({
onDragSort: tabProps.onDragSort,
},
setup(props) {
const COMPONENT_NAME = usePrefixClass('tabs');
const componentName = usePrefixClass('tabs');
const { ChevronLeftIcon, ChevronRightIcon, AddIcon } = useGlobalIcon({
ChevronLeftIcon: TdChevronLeftIcon,
ChevronRightIcon: TdChevronRightIcon,
Expand All @@ -69,6 +69,7 @@ export default defineComponent({
const rightOperationsRef = ref();
const toRightBtnRef = ref();
const activeTabRef = ref();

const getRefs = () => ({
navsContainer: navsContainerRef.value,
navsWrap: navsWrapRef.value,
Expand All @@ -95,47 +96,47 @@ export default defineComponent({
// class
const iconBaseClass = computed(() => {
return {
[`${COMPONENT_NAME.value}__btn`]: true,
[`${componentName.value}__btn`]: true,
[SIZE.value.medium]: props.size === 'medium',
[SIZE.value.large]: props.size === 'large',
};
});
const leftIconClass = computed(() => {
return {
[`${COMPONENT_NAME.value}__btn--left`]: true,
[`${componentName.value}__btn--left`]: true,
...iconBaseClass.value,
};
});
const rightIconClass = computed(() => {
return {
[`${COMPONENT_NAME.value}__btn--right`]: true,
[`${componentName.value}__btn--right`]: true,
...iconBaseClass.value,
};
});
const addIconClass = computed(() => {
return {
[`${COMPONENT_NAME.value}__add-btn`]: true,
[`${componentName.value}__add-btn`]: true,
...iconBaseClass.value,
};
});
const navContainerClass = computed(() => {
return {
[`${COMPONENT_NAME.value}__nav-container`]: true,
[`${COMPONENT_NAME.value}__nav--card`]: props.theme === 'card',
[`${componentName.value}__nav-container`]: true,
[`${componentName.value}__nav--card`]: props.theme === 'card',
[`${classPrefix.value}-is-${props.placement}`]: true,
[`${classPrefix.value}-is-addable`]: props.addable,
};
});
const navScrollContainerClass = computed(() => {
return {
[`${COMPONENT_NAME.value}__nav-scroll`]: true,
[`${componentName.value}__nav-scroll`]: true,
[`${classPrefix.value}-is-scrollable`]: canToLeft.value || canToRight.value,
};
});

const navsWrapClass = computed(() => {
return [
`${COMPONENT_NAME.value}__nav-wrap`,
`${componentName.value}__nav-wrap`,
`${classPrefix.value}-is-smooth`,
{ [`${classPrefix.value}-is-vertical`]: isVerticalPlacement.value },
];
Expand All @@ -152,9 +153,10 @@ export default defineComponent({

// life times
useResize(debounce(totalAdjust), navsContainerRef.value);

onMounted(() => {
totalAdjust();
calculateMountedScrollLeft();
totalAdjust();
});

// calculate scroll left after mounted
Expand All @@ -163,10 +165,18 @@ export default defineComponent({

const container = navsContainerRef.value;
const activeTabEl = activeTabRef.value;
const activeTabWidth = activeTabEl?.offsetWidth || 0;
const containerWidth = container?.offsetWidth || 0;

const activeElIndex = Array.prototype.indexOf.call(navsWrapRef.value.children, activeTabEl); // index of the active tab

const isRightBtnShow =
navs.value.length - activeElIndex >= Math.round((containerWidth - activeTabWidth) / activeTabWidth) ? 1 : 0; // calculate whether the right btn is display or not
const totalWidthBeforeActiveTab = activeTabEl?.offsetLeft;
const containerWidth = container.offsetWidth || 0;
if (totalWidthBeforeActiveTab > containerWidth) scrollLeft.value = totalWidthBeforeActiveTab;
if (totalWidthBeforeActiveTab > containerWidth - activeTabWidth)
scrollLeft.value = totalWidthBeforeActiveTab - isRightBtnShow * activeTabWidth;
};

// methods
const adjustScrollLeft = () => {
scrollLeft.value = calcScrollLeft(getRefs(), scrollLeft.value);
Expand Down Expand Up @@ -256,7 +266,7 @@ export default defineComponent({
return [
<div
ref={leftOperationsRef}
class={[`${COMPONENT_NAME.value}__operations`, `${COMPONENT_NAME.value}__operations--left`]}
class={[`${componentName.value}__operations`, `${componentName.value}__operations--left`]}
>
<Transition name="fade" mode="out-in" appear>
{canToLeft.value ? (
Expand All @@ -268,7 +278,7 @@ export default defineComponent({
</div>,
<div
ref={rightOperationsRef}
class={[`${COMPONENT_NAME.value}__operations`, `${COMPONENT_NAME.value}__operations--right`]}
class={[`${componentName.value}__operations`, `${componentName.value}__operations--right`]}
>
<Transition name="fade" mode="out-in" appear>
{canToRight.value ? (
Expand Down Expand Up @@ -303,7 +313,7 @@ export default defineComponent({

return () => {
return (
<div ref={navsContainerRef} class={[`${COMPONENT_NAME.value}__nav`]} style={navsContainerStyle.value}>
<div ref={navsContainerRef} class={[`${componentName.value}__nav`]} style={navsContainerStyle.value}>
{renderArrows()}
{renderNavs()}
</div>
Expand Down
164 changes: 162 additions & 2 deletions test/unit/snap/__snapshots__/csr.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -164349,6 +164349,36 @@ exports[`csr snapshot test > csr test ./src/tabs/_example/combination.vue 1`] =
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡20
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡21
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-is-active t-size-m"
draggable="false"
Expand All @@ -164359,7 +164389,127 @@ exports[`csr snapshot test > csr test ./src/tabs/_example/combination.vue 1`] =
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡20
选项卡22
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡23
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡24
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡25
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡26
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡27
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡28
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡29
</span>
</div>
<!---->
</div>
<div
class="t-tabs__nav-item t-size-m"
draggable="false"
>
<div
class="t-tabs__nav-item-wrapper"
>
<span
class="t-tabs__nav-item-text-wrapper"
>
选项卡30
</span>
</div>
<!---->
Expand Down Expand Up @@ -164393,17 +164543,27 @@ exports[`csr snapshot test > csr test ./src/tabs/_example/combination.vue 1`] =
<!---->
<!---->
<!---->
<!---->
<!---->
<div
class="t-tab-panel"
>

<p
style="padding: 25px;"
>
选项卡21
选项卡23
</p>

</div>
<!---->
<!---->
<!---->
<!---->
<!---->
<!---->
<!---->
<!---->

</div>

Expand Down
Loading

0 comments on commit c82c3d3

Please sign in to comment.