diff --git a/packages/primevue/src/tablist/TabList.vue b/packages/primevue/src/tablist/TabList.vue index 57c1da6f0..360ff0309 100644 --- a/packages/primevue/src/tablist/TabList.vue +++ b/packages/primevue/src/tablist/TabList.vue @@ -39,18 +39,20 @@ import { findSingle, getHeight, getOffset, getOuterHeight, getOuterWidth, getWidth } from '@primeuix/utils/dom'; import ChevronLeftIcon from '@primevue/icons/chevronleft'; import ChevronRightIcon from '@primevue/icons/chevronright'; -import BaseTabList from './BaseTabList.vue'; import Ripple from 'primevue/ripple'; +import BaseTabList from './BaseTabList.vue'; export default { name: 'TabList', extends: BaseTabList, inheritAttrs: false, inject: ['$pcTabs'], + mutationObserver: null, data() { return { isPrevButtonEnabled: false, - isNextButtonEnabled: true + isNextButtonEnabled: true, + isRTL: false }; }, resizeObserver: undefined, @@ -74,14 +76,39 @@ export default { this.updateButtonState(); this.bindResizeObserver(); } + + this.updateDirection(); + this.observeDirectionChanges(); }, updated() { this.showNavigators && this.updateButtonState(); }, beforeUnmount() { this.unbindResizeObserver(); + + if (this.mutationObserver) { + this.mutationObserver.disconnect(); + } }, methods: { + updateDirection() { + if (document) { + const isHtmlRtl = document.documentElement.getAttribute('dir') === 'rtl'; + const isBodyRtl = document.body.getAttribute('dir') === 'rtl'; + + this.isRTL = isHtmlRtl || isBodyRtl || this.$el.closest('[dir="rtl"]'); + } + }, + observeDirectionChanges() { + const targetNode = document.documentElement; + const config = { attributes: true, attributeFilter: ['dir'] }; + + this.mutationObserver = new MutationObserver(() => { + this.updateDirection(); + }); + + this.mutationObserver.observe(targetNode, config); + }, onScroll(event) { this.showNavigators && this.updateButtonState(); @@ -90,15 +117,28 @@ export default { onPrevButtonClick() { const content = this.$refs.content; const width = getWidth(content); - const pos = content.scrollLeft - width; + let pos; + + if (this.isRTL) { + pos = content.scrollLeft + width; + } else { + pos = content.scrollLeft - width; + } content.scrollLeft = pos <= 0 ? 0 : pos; }, onNextButtonClick() { const content = this.$refs.content; const width = getWidth(content) - this.getVisibleButtonWidths(); - const pos = content.scrollLeft + width; - const lastPos = content.scrollWidth - width; + let pos, lastPos; + + if (this.isRTL) { + pos = content.scrollLeft - width; + lastPos = content.scrollWidth + width; + } else { + pos = content.scrollLeft + width; + lastPos = content.scrollWidth - width; + } content.scrollLeft = pos >= lastPos ? lastPos : pos; }, diff --git a/packages/primevue/src/tabs/style/TabsStyle.js b/packages/primevue/src/tabs/style/TabsStyle.js index 7676f8062..e244efa2a 100644 --- a/packages/primevue/src/tabs/style/TabsStyle.js +++ b/packages/primevue/src/tabs/style/TabsStyle.js @@ -44,7 +44,7 @@ const theme = ({ dt }) => ` all: unset; position: absolute !important; flex-shrink: 0; - top: 0; + inset-block-start: 0; z-index: 2; height: 100%; display: flex; @@ -71,13 +71,19 @@ const theme = ({ dt }) => ` } .p-tablist-prev-button { - left: 0; + inset-inline-start: 0; } .p-tablist-next-button { - right: 0; + inset-inline-end: 0; } +.p-tablist-prev-button:dir(rtl), +.p-tablist-next-button:dir(rtl) { + transform: rotate(180deg); +} + + .p-tab { flex-shrink: 0; cursor: pointer; @@ -132,7 +138,7 @@ const theme = ({ dt }) => ` z-index: 1; display: block; position: absolute; - bottom: ${dt('tabs.active.bar.bottom')}; + inset-block-end: ${dt('tabs.active.bar.bottom')}; height: ${dt('tabs.active.bar.height')}; background: ${dt('tabs.active.bar.background')}; transition: 250ms cubic-bezier(0.35, 0, 0.25, 1);