Refactor #3965 - For TabView

pull/3997/head
Tuğçe Küçükoğlu 2023-05-24 09:24:33 +03:00
parent 61d659d3de
commit 85f6669263
5 changed files with 233 additions and 180 deletions

View File

@ -0,0 +1,19 @@
<script>
import BaseComponent from 'primevue/basecomponent';
export default {
name: 'BaseTabPanel',
extends: BaseComponent,
props: {
header: null,
headerStyle: null,
headerClass: null,
headerProps: null,
headerActionProps: null,
contentStyle: null,
contentClass: null,
contentProps: null,
disabled: Boolean
}
};
</script>

View File

@ -3,20 +3,10 @@
</template> </template>
<script> <script>
import BaseComponent from 'primevue/basecomponent'; import BaseTabPanel from './BaseTabPanel.vue';
export default { export default {
name: 'TabPanel', name: 'TabPanel',
extends: BaseComponent, extends: BaseTabPanel
props: {
header: null,
headerStyle: null,
headerClass: null,
headerProps: null,
headerActionProps: null,
contentStyle: null,
contentClass: null,
contentProps: null,
disabled: Boolean
}
}; };
</script> </script>

View File

@ -0,0 +1,163 @@
<script>
import BaseComponent from 'primevue/basecomponent';
import { useStyle } from 'primevue/usestyle';
const styles = `
.p-tabview-nav-container {
position: relative;
}
.p-tabview-scrollable .p-tabview-nav-container {
overflow: hidden;
}
.p-tabview-nav-content {
overflow-x: auto;
overflow-y: hidden;
scroll-behavior: smooth;
scrollbar-width: none;
overscroll-behavior: contain auto;
}
.p-tabview-nav {
display: flex;
margin: 0;
padding: 0;
list-style-type: none;
flex: 1 1 auto;
}
.p-tabview-header-action {
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
position: relative;
text-decoration: none;
overflow: hidden;
}
.p-tabview-ink-bar {
display: none;
z-index: 1;
}
.p-tabview-header-action:focus {
z-index: 1;
}
.p-tabview-title {
line-height: 1;
white-space: nowrap;
}
.p-tabview-nav-btn {
position: absolute;
top: 0;
z-index: 2;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.p-tabview-nav-prev {
left: 0;
}
.p-tabview-nav-next {
right: 0;
}
.p-tabview-nav-content::-webkit-scrollbar {
display: none;
}
`;
const classes = {
root: ({ props }) => [
'p-tabview p-component',
{
'p-tabview-scrollable': props.scrollable
}
],
navContainer: 'p-tabview-nav-container',
previousButton: 'p-tabview-nav-prev p-tabview-nav-btn p-link',
previousIcon: ({ props }) => props.prevIcon,
navContent: 'p-tabview-nav-content',
nav: 'p-tabview-nav',
tab: {
header: ({ instance, tab, index }) => [
'p-tabview-header',
instance.getTabProp(tab, 'headerClass'),
{
'p-highlight': instance.d_activeIndex === index,
'p-disabled': instance.getTabProp(tab, 'disabled')
}
],
headerAction: 'p-tabview-nav-link p-tabview-header-action',
headerTitle: 'p-tabview-title',
content: ({ instance, tab }) => ['p-tabview-panel', instance.getTabProp(tab, 'contentClass')]
},
inkbar: 'p-tabview-ink-bar',
nextButton: 'p-tabview-nav-next p-tabview-nav-btn p-link',
nextIcon: ({ props }) => props.nextIcon,
panelContainer: 'p-tabview-panels'
};
const { load: loadStyle, unload: unloadStyle } = useStyle(styles, { id: 'primevue_tabview_style', manual: true });
export default {
name: 'BaseTabView',
extends: BaseComponent,
props: {
activeIndex: {
type: Number,
default: 0
},
lazy: {
type: Boolean,
default: false
},
scrollable: {
type: Boolean,
default: false
},
tabindex: {
type: Number,
default: 0
},
selectOnFocus: {
type: Boolean,
default: false
},
previousButtonProps: {
type: null,
default: null
},
nextButtonProps: {
type: null,
default: null
},
prevIcon: {
type: String,
default: undefined
},
nextIcon: {
type: String,
default: undefined
}
},
css: {
classes
},
watch: {
isUnstyled: {
immediate: true,
handler(newValue) {
!newValue && loadStyle();
}
}
}
};
</script>

View File

@ -166,6 +166,11 @@ export interface TabViewProps {
* @type {TabViewPassThroughOptions} * @type {TabViewPassThroughOptions}
*/ */
pt?: TabViewPassThroughOptions; pt?: TabViewPassThroughOptions;
/**
* When enabled, it removes component related styles in the core.
* @defaultValue false
*/
unstyled?: boolean;
} }
/** /**

View File

@ -1,50 +1,55 @@
<template> <template>
<div :class="contentClasses" v-bind="ptm('root')"> <div :class="cx('root')" data-pc-name="tabview" data-pc-section="root" v-bind="ptm('root')">
<div class="p-tabview-nav-container" v-bind="ptm('navcontainer')"> <div :class="cx('navContainer')" data-pc-section="navcontainer" v-bind="ptm('navContainer')">
<button <button
v-if="scrollable && !isPrevButtonDisabled" v-if="scrollable && !isPrevButtonDisabled"
ref="prevBtn" ref="prevBtn"
v-ripple v-ripple
type="button" type="button"
class="p-tabview-nav-prev p-tabview-nav-btn p-link" :class="cx('previousButton')"
:tabindex="tabindex" :tabindex="tabindex"
:aria-label="prevButtonAriaLabel" :aria-label="prevButtonAriaLabel"
@click="onPrevButtonClick" @click="onPrevButtonClick"
v-bind="{ ...previousButtonProps, ...ptm('prevbutton') }" data-pc-section="previousbutton"
v-bind="{ ...previousButtonProps, ...ptm('previousButton') }"
> >
<slot name="previcon"> <slot name="previcon">
<component :is="prevIcon ? 'span' : 'ChevronLeftIcon'" aria-hidden="true" :class="prevIcon" v-bind="ptm('previcon')" /> <component :is="prevIcon ? 'span' : 'ChevronLeftIcon'" aria-hidden="true" :class="prevIcon" data-pc-section="previousicon" v-bind="ptm('previousIcon')" />
</slot> </slot>
</button> </button>
<div ref="content" class="p-tabview-nav-content" @scroll="onScroll" v-bind="ptm('navcontent')"> <div ref="content" :class="cx('navContent')" @scroll="onScroll" data-pc-section="navcontent" v-bind="ptm('navContent')">
<ul ref="nav" class="p-tabview-nav" role="tablist" v-bind="ptm('nav')"> <ul ref="nav" :class="cx('nav')" role="tablist" data-pc-section="nav" v-bind="ptm('nav')">
<li <li
v-for="(tab, i) of tabs" v-for="(tab, index) of tabs"
:key="getKey(tab, i)" :key="getKey(tab, index)"
:style="getTabProp(tab, 'headerStyle')" :style="getTabProp(tab, 'headerStyle')"
:class="getTabHeaderClass(tab, i)" :class="cx('tab.header', { tab, index })"
role="presentation" role="presentation"
:data-index="i" :data-index="index"
:data-p-highlight="d_activeIndex === index"
:data-p-disabled="getTabProp(tab, 'disabled')"
data-pc-section="header"
v-bind="{ ...getTabProp(tab, 'headerProps'), ...getTabPT(tab, 'root'), ...getTabPT(tab, 'header') }" v-bind="{ ...getTabProp(tab, 'headerProps'), ...getTabPT(tab, 'root'), ...getTabPT(tab, 'header') }"
> >
<a <a
:id="getTabHeaderActionId(i)" :id="getTabHeaderActionId(index)"
v-ripple v-ripple
class="p-tabview-nav-link p-tabview-header-action" :class="cx('tab.headerAction')"
:tabindex="getTabProp(tab, 'disabled') || !isTabActive(i) ? -1 : tabindex" :tabindex="getTabProp(tab, 'disabled') || !isTabActive(index) ? -1 : tabindex"
role="tab" role="tab"
:aria-disabled="getTabProp(tab, 'disabled')" :aria-disabled="getTabProp(tab, 'disabled')"
:aria-selected="isTabActive(i)" :aria-selected="isTabActive(index)"
:aria-controls="getTabContentId(i)" :aria-controls="getTabContentId(index)"
@click="onTabClick($event, tab, i)" @click="onTabClick($event, tab, index)"
@keydown="onTabKeyDown($event, tab, i)" @keydown="onTabKeyDown($event, tab, index)"
v-bind="{ ...getTabProp(tab, 'headerActionProps'), ...getTabPT(tab, 'headeraction') }" data-pc-section="headeraction"
v-bind="{ ...getTabProp(tab, 'headerActionProps'), ...getTabPT(tab, 'headerAction') }"
> >
<span v-if="tab.props && tab.props.header" class="p-tabview-title" v-bind="getTabPT(tab, 'headertitle')">{{ tab.props.header }}</span> <span v-if="tab.props && tab.props.header" :class="cx('tab.headerTitle')" data-pc-section="headertitle" v-bind="getTabPT(tab, 'headerTitle')">{{ tab.props.header }}</span>
<component v-if="tab.children && tab.children.header" :is="tab.children.header"></component> <component v-if="tab.children && tab.children.header" :is="tab.children.header"></component>
</a> </a>
</li> </li>
<li ref="inkbar" class="p-tabview-ink-bar" role="presentation" aria-hidden="true" v-bind="ptm('inkbar')"></li> <li ref="inkbar" :class="cx('inkbar')" role="presentation" aria-hidden="true" data-pc-section="inkbar" v-bind="ptm('inkbar')"></li>
</ul> </ul>
</div> </div>
<button <button
@ -52,26 +57,28 @@
ref="nextBtn" ref="nextBtn"
v-ripple v-ripple
type="button" type="button"
class="p-tabview-nav-next p-tabview-nav-btn p-link" :class="cx('nextButton')"
:tabindex="tabindex" :tabindex="tabindex"
:aria-label="nextButtonAriaLabel" :aria-label="nextButtonAriaLabel"
@click="onNextButtonClick" @click="onNextButtonClick"
v-bind="{ ...nextButtonProps, ...ptm('nextbutton') }" data-pc-section="nextbutton"
v-bind="{ ...nextButtonProps, ...ptm('nextButton') }"
> >
<slot name="nexticon"> <slot name="nexticon">
<component :is="nextIcon ? 'span' : 'ChevronRightIcon'" aria-hidden="true" :class="nextIcon" v-bind="ptm('nexticon')" /> <component :is="nextIcon ? 'span' : 'ChevronRightIcon'" aria-hidden="true" :class="nextIcon" data-pc-section="nexticon" v-bind="ptm('nextIcon')" />
</slot> </slot>
</button> </button>
</div> </div>
<div class="p-tabview-panels" v-bind="ptm('panelcontainer')"> <div :class="cx('panelContainer')" data-pc-section="panelcontainer" v-bind="ptm('panelContainer')">
<template v-for="(tab, i) of tabs" :key="getKey(tab, i)"> <template v-for="(tab, index) of tabs" :key="getKey(tab, index)">
<div <div
v-if="lazy ? isTabActive(i) : true" v-if="lazy ? isTabActive(index) : true"
v-show="lazy ? true : isTabActive(i)" v-show="lazy ? true : isTabActive(index)"
:style="getTabProp(tab, 'contentStyle')" :style="getTabProp(tab, 'contentStyle')"
:class="getTabContentClass(tab)" :class="cx('tab.content', { tab })"
role="tabpanel" role="tabpanel"
:aria-labelledby="getTabHeaderActionId(i)" :aria-labelledby="getTabHeaderActionId(index)"
data-pc-section="content"
v-bind="{ ...getTabProp(tab, 'contentProps'), ...getTabPT(tab, 'root'), ...getTabPT(tab, 'content') }" v-bind="{ ...getTabProp(tab, 'contentProps'), ...getTabPT(tab, 'root'), ...getTabPT(tab, 'content') }"
> >
<component :is="tab"></component> <component :is="tab"></component>
@ -82,54 +89,16 @@
</template> </template>
<script> <script>
import BaseComponent from 'primevue/basecomponent';
import ChevronLeftIcon from 'primevue/icons/chevronleft'; import ChevronLeftIcon from 'primevue/icons/chevronleft';
import ChevronRightIcon from 'primevue/icons/chevronright'; import ChevronRightIcon from 'primevue/icons/chevronright';
import Ripple from 'primevue/ripple'; import Ripple from 'primevue/ripple';
import { DomHandler, UniqueComponentId } from 'primevue/utils'; import { DomHandler, UniqueComponentId } from 'primevue/utils';
import BaseTabView from './BaseTabView.vue';
export default { export default {
name: 'TabView', name: 'TabView',
extends: BaseComponent, extends: BaseTabView,
emits: ['update:activeIndex', 'tab-change', 'tab-click'], emits: ['update:activeIndex', 'tab-change', 'tab-click'],
props: {
activeIndex: {
type: Number,
default: 0
},
lazy: {
type: Boolean,
default: false
},
scrollable: {
type: Boolean,
default: false
},
tabindex: {
type: Number,
default: 0
},
selectOnFocus: {
type: Boolean,
default: false
},
previousButtonProps: {
type: null,
default: null
},
nextButtonProps: {
type: null,
default: null
},
prevIcon: {
type: String,
default: undefined
},
nextIcon: {
type: String,
default: undefined
}
},
data() { data() {
return { return {
id: this.$attrs.id, id: this.$attrs.id,
@ -285,18 +254,18 @@ export default {
const headerElement = selfCheck ? tabElement : tabElement.nextElementSibling; const headerElement = selfCheck ? tabElement : tabElement.nextElementSibling;
return headerElement return headerElement
? DomHandler.hasClass(headerElement, 'p-disabled') || DomHandler.hasClass(headerElement, 'p-tabview-ink-bar') ? DomHandler.getAttribute(headerElement, 'data-p-disabled') || DomHandler.getAttribute(headerElement, 'data-pc-section') === 'inkbar'
? this.findNextHeaderAction(headerElement) ? this.findNextHeaderAction(headerElement)
: DomHandler.findSingle(headerElement, '.p-tabview-header-action') : DomHandler.findSingle(headerElement, '[data-pc-section="headeraction"]')
: null; : null;
}, },
findPrevHeaderAction(tabElement, selfCheck = false) { findPrevHeaderAction(tabElement, selfCheck = false) {
const headerElement = selfCheck ? tabElement : tabElement.previousElementSibling; const headerElement = selfCheck ? tabElement : tabElement.previousElementSibling;
return headerElement return headerElement
? DomHandler.hasClass(headerElement, 'p-disabled') || DomHandler.hasClass(headerElement, 'p-tabview-ink-bar') ? DomHandler.getAttribute(headerElement, 'data-p-disabled') || DomHandler.getAttribute(headerElement, 'data-pc-section') === 'inkbar'
? this.findPrevHeaderAction(headerElement) ? this.findPrevHeaderAction(headerElement)
: DomHandler.findSingle(headerElement, '.p-tabview-header-action') : DomHandler.findSingle(headerElement, '[data-pc-section="headeraction"]')
: null; : null;
}, },
findFirstHeaderAction() { findFirstHeaderAction() {
@ -353,30 +322,9 @@ export default {
const { prevBtn, nextBtn } = this.$refs; const { prevBtn, nextBtn } = this.$refs;
return [prevBtn, nextBtn].reduce((acc, el) => (el ? acc + DomHandler.getWidth(el) : acc), 0); return [prevBtn, nextBtn].reduce((acc, el) => (el ? acc + DomHandler.getWidth(el) : acc), 0);
},
getTabHeaderClass(tab, i) {
return [
'p-tabview-header',
this.getTabProp(tab, 'headerClass'),
{
'p-highlight': this.d_activeIndex === i,
'p-disabled': this.getTabProp(tab, 'disabled')
}
];
},
getTabContentClass(tab) {
return ['p-tabview-panel', this.getTabProp(tab, 'contentClass')];
} }
}, },
computed: { computed: {
contentClasses() {
return [
'p-tabview p-component',
{
'p-tabview-scrollable': this.scrollable
}
];
},
tabs() { tabs() {
return this.$slots.default().reduce((tabs, child) => { return this.$slots.default().reduce((tabs, child) => {
if (this.isTabPanel(child)) { if (this.isTabPanel(child)) {
@ -408,75 +356,3 @@ export default {
} }
}; };
</script> </script>
<style>
.p-tabview-nav-container {
position: relative;
}
.p-tabview-scrollable .p-tabview-nav-container {
overflow: hidden;
}
.p-tabview-nav-content {
overflow-x: auto;
overflow-y: hidden;
scroll-behavior: smooth;
scrollbar-width: none;
overscroll-behavior: contain auto;
}
.p-tabview-nav {
display: flex;
margin: 0;
padding: 0;
list-style-type: none;
flex: 1 1 auto;
}
.p-tabview-header-action {
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
position: relative;
text-decoration: none;
overflow: hidden;
}
.p-tabview-ink-bar {
display: none;
z-index: 1;
}
.p-tabview-header-action:focus {
z-index: 1;
}
.p-tabview-title {
line-height: 1;
white-space: nowrap;
}
.p-tabview-nav-btn {
position: absolute;
top: 0;
z-index: 2;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.p-tabview-nav-prev {
left: 0;
}
.p-tabview-nav-next {
right: 0;
}
.p-tabview-nav-content::-webkit-scrollbar {
display: none;
}
</style>