primevue-mirror/components/lib/tablist/TabList.vue

166 lines
6.0 KiB
Vue

<template>
<div ref="list" :class="cx('root')" v-bind="ptmi('root')">
<button
v-if="showNavigators && isPrevButtonEnabled"
ref="prevButton"
v-ripple
:class="cx('prevButton')"
:aria-label="prevButtonAriaLabel"
:tabindex="$pcTabs.tabindex"
@click="onPrevButtonClick"
v-bind="ptm('prevButton')"
data-pc-group-section="navigator"
>
<component :is="templates.previcon || 'ChevronLeftIcon'" aria-hidden="true" v-bind="ptm('prevIcon')" />
</button>
<div ref="content" :class="cx('content')" @scroll="onScroll" v-bind="ptm('content')">
<div ref="tabs" :class="cx('tabs')" role="tablist" :aria-orientation="$pcTabs.orientation || 'horizontal'" v-bind="ptm('tabs')">
<slot></slot>
<span ref="inkbar" :class="cx('inkbar')" role="presentation" aria-hidden="true" v-bind="ptm('inkbar')"></span>
</div>
</div>
<button
v-if="showNavigators && isNextButtonEnabled"
ref="nextButton"
v-ripple
:class="cx('nextButton')"
:aria-label="nextButtonAriaLabel"
:tabindex="$pcTabs.tabindex"
@click="onNextButtonClick"
v-bind="ptm('nextButton')"
data-pc-group-section="navigator"
>
<component :is="templates.nexticon || 'ChevronRightIcon'" aria-hidden="true" v-bind="ptm('nextIcon')" />
</button>
</div>
</template>
<script>
import ChevronLeftIcon from 'primevue/icons/chevronleft';
import ChevronRightIcon from 'primevue/icons/chevronright';
import { DomHandler } from 'primevue/utils';
import BaseTabList from './BaseTabList.vue';
export default {
name: 'TabList',
extends: BaseTabList,
inheritAttrs: false,
inject: ['$pcTabs'],
data() {
return {
isPrevButtonEnabled: false,
isNextButtonEnabled: true
};
},
resizeObserver: undefined,
watch: {
showNavigators(newValue) {
newValue ? this.bindResizeObserver() : this.unbindResizeObserver();
},
activeValue: {
flush: 'post',
handler() {
this.updateInkBar();
}
}
},
mounted() {
this.$nextTick(() => {
this.updateInkBar();
});
if (this.showNavigators) {
this.updateButtonState();
this.bindResizeObserver();
}
},
updated() {
this.showNavigators && this.updateButtonState();
},
beforeUnmount() {
this.unbindResizeObserver();
},
methods: {
onScroll(event) {
this.showNavigators && this.updateButtonState();
event.preventDefault();
},
onPrevButtonClick() {
const content = this.$refs.content;
const width = DomHandler.getWidth(content);
const pos = content.scrollLeft - width;
content.scrollLeft = pos <= 0 ? 0 : pos;
},
onNextButtonClick() {
const content = this.$refs.content;
const width = DomHandler.getWidth(content) - this.getVisibleButtonWidths();
const pos = content.scrollLeft + width;
const lastPos = content.scrollWidth - width;
content.scrollLeft = pos >= lastPos ? lastPos : pos;
},
bindResizeObserver() {
this.resizeObserver = new ResizeObserver(() => this.updateButtonState());
this.resizeObserver.observe(this.$refs.list);
},
unbindResizeObserver() {
this.resizeObserver?.unobserve(this.$refs.list);
this.resizeObserver = undefined;
},
updateInkBar() {
const { content, inkbar, tabs } = this.$refs;
const activeTab = DomHandler.findSingle(content, '[data-pc-name="tab"][data-p-active="true"]');
if (this.$pcTabs.isVertical()) {
inkbar.style.height = DomHandler.getOuterHeight(activeTab) + 'px';
inkbar.style.top = DomHandler.getOffset(activeTab).top - DomHandler.getOffset(tabs).top + 'px';
} else {
inkbar.style.width = DomHandler.getOuterWidth(activeTab) + 'px';
inkbar.style.left = DomHandler.getOffset(activeTab).left - DomHandler.getOffset(tabs).left + 'px';
}
},
updateButtonState() {
const { list, content } = this.$refs;
const { scrollLeft, scrollTop, scrollWidth, scrollHeight, offsetWidth, offsetHeight } = content;
const [width, height] = [DomHandler.getWidth(content), DomHandler.getHeight(content)];
if (this.$pcTabs.isVertical()) {
this.isPrevButtonEnabled = scrollTop !== 0;
this.isNextButtonEnabled = list.offsetHeight >= offsetHeight && parseInt(scrollTop) !== scrollHeight - height;
} else {
this.isPrevButtonEnabled = scrollLeft !== 0;
this.isNextButtonEnabled = list.offsetWidth >= offsetWidth && parseInt(scrollLeft) !== scrollWidth - width;
}
},
getVisibleButtonWidths() {
const { prevBtn, nextBtn } = this.$refs;
return [prevBtn, nextBtn].reduce((acc, el) => (el ? acc + DomHandler.getWidth(el) : acc), 0);
}
},
computed: {
templates() {
return this.$pcTabs.$slots;
},
activeValue() {
return this.$pcTabs.d_value;
},
showNavigators() {
return this.$pcTabs.scrollable && this.$pcTabs.showNavigators;
},
prevButtonAriaLabel() {
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.previous : undefined;
},
nextButtonAriaLabel() {
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.next : undefined;
}
},
components: {
ChevronLeftIcon,
ChevronRightIcon
}
};
</script>