Fixed #5474 - Carousel Enhancement - Empty Slot
parent
038d9959d0
commit
2ddfb689d1
|
@ -346,6 +346,10 @@ export interface CarouselSlots {
|
||||||
* Custom next icon template.
|
* Custom next icon template.
|
||||||
*/
|
*/
|
||||||
nexticon(): VNode[];
|
nexticon(): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom empty template.
|
||||||
|
*/
|
||||||
|
empty(): VNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,96 +3,95 @@
|
||||||
<div v-if="$slots.header" :class="cx('header')" v-bind="ptm('header')">
|
<div v-if="$slots.header" :class="cx('header')" v-bind="ptm('header')">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div :class="[cx('content'), contentClass]" v-bind="ptm('content')">
|
<div v-if="!empty" :class="[cx('content'), contentClass]" v-bind="ptm('content')">
|
||||||
<template v-if="!empty">
|
<div :class="[cx('container'), containerClass]" :aria-live="allowAutoplay ? 'polite' : 'off'" v-bind="ptm('container')">
|
||||||
<div :class="[cx('container'), containerClass]" :aria-live="allowAutoplay ? 'polite' : 'off'" v-bind="ptm('container')">
|
<button
|
||||||
<button
|
v-if="showNavigators"
|
||||||
v-if="showNavigators"
|
v-ripple
|
||||||
v-ripple
|
type="button"
|
||||||
type="button"
|
:class="cx('previousButton')"
|
||||||
:class="cx('previousButton')"
|
:disabled="backwardIsDisabled"
|
||||||
:disabled="backwardIsDisabled"
|
:aria-label="ariaPrevButtonLabel"
|
||||||
:aria-label="ariaPrevButtonLabel"
|
@click="navBackward"
|
||||||
@click="navBackward"
|
v-bind="{ ...prevButtonProps, ...ptm('previousButton') }"
|
||||||
v-bind="{ ...prevButtonProps, ...ptm('previousButton') }"
|
data-pc-group-section="navigator"
|
||||||
data-pc-group-section="navigator"
|
>
|
||||||
>
|
<slot name="previousicon">
|
||||||
<slot name="previousicon">
|
<component :is="isVertical() ? 'ChevronUpIcon' : 'ChevronLeftIcon'" :class="cx('previousButtonIcon')" v-bind="ptm('previousButtonIcon')" />
|
||||||
<component :is="isVertical() ? 'ChevronUpIcon' : 'ChevronLeftIcon'" :class="cx('previousButtonIcon')" v-bind="ptm('previousButtonIcon')" />
|
</slot>
|
||||||
</slot>
|
</button>
|
||||||
</button>
|
|
||||||
|
|
||||||
<div :class="cx('itemsContent')" :style="[{ height: isVertical() ? verticalViewPortHeight : 'auto' }]" @touchend="onTouchEnd" @touchstart="onTouchStart" @touchmove="onTouchMove" v-bind="ptm('itemsContent')">
|
<div :class="cx('itemsContent')" :style="[{ height: isVertical() ? verticalViewPortHeight : 'auto' }]" @touchend="onTouchEnd" @touchstart="onTouchStart" @touchmove="onTouchMove" v-bind="ptm('itemsContent')">
|
||||||
<div ref="itemsContainer" :class="cx('itemsContainer')" @transitionend="onTransitionEnd" v-bind="ptm('itemsContainer')">
|
<div ref="itemsContainer" :class="cx('itemsContainer')" @transitionend="onTransitionEnd" v-bind="ptm('itemsContainer')">
|
||||||
<template v-if="isCircular()">
|
<template v-if="isCircular()">
|
||||||
<div
|
|
||||||
v-for="(item, index) of value.slice(-1 * d_numVisible)"
|
|
||||||
:key="index + '_scloned'"
|
|
||||||
:class="cx('itemCloned', { index, value, totalShiftedItems, d_numVisible })"
|
|
||||||
v-bind="ptm('itemCloned')"
|
|
||||||
:data-p-carousel-item-active="totalShiftedItems * -1 === value.length + d_numVisible"
|
|
||||||
:data-p-carousel-item-start="index === 0"
|
|
||||||
:data-p-carousel-item-end="value.slice(-1 * d_numVisible).length - 1 === index"
|
|
||||||
>
|
|
||||||
<slot name="item" :data="item" :index="index"></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) of value"
|
v-for="(item, index) of value.slice(-1 * d_numVisible)"
|
||||||
:key="index"
|
:key="index + '_scloned'"
|
||||||
:class="cx('item', { index })"
|
:class="cx('itemCloned', { index, value, totalShiftedItems, d_numVisible })"
|
||||||
role="group"
|
v-bind="ptm('itemCloned')"
|
||||||
:aria-hidden="firstIndex() > index || lastIndex() < index ? true : undefined"
|
:data-p-carousel-item-active="totalShiftedItems * -1 === value.length + d_numVisible"
|
||||||
:aria-label="ariaSlideNumber(index)"
|
:data-p-carousel-item-start="index === 0"
|
||||||
:aria-roledescription="ariaSlideLabel"
|
:data-p-carousel-item-end="value.slice(-1 * d_numVisible).length - 1 === index"
|
||||||
v-bind="ptm('item')"
|
|
||||||
:data-p-carousel-item-active="firstIndex() <= index && lastIndex() >= index"
|
|
||||||
:data-p-carousel-item-start="firstIndex() === index"
|
|
||||||
:data-p-carousel-item-end="lastIndex() === index"
|
|
||||||
>
|
>
|
||||||
<slot name="item" :data="item" :index="index"></slot>
|
<slot name="item" :data="item" :index="index"></slot>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="isCircular()">
|
</template>
|
||||||
<div v-for="(item, index) of value.slice(0, d_numVisible)" :key="index + '_fcloned'" :class="cx('itemCloned', { index, value, totalShiftedItems, d_numVisible })" v-bind="ptm('itemCloned')">
|
<div
|
||||||
<slot name="item" :data="item" :index="index"></slot>
|
v-for="(item, index) of value"
|
||||||
</div>
|
:key="index"
|
||||||
</template>
|
:class="cx('item', { index })"
|
||||||
|
role="group"
|
||||||
|
:aria-hidden="firstIndex() > index || lastIndex() < index ? true : undefined"
|
||||||
|
:aria-label="ariaSlideNumber(index)"
|
||||||
|
:aria-roledescription="ariaSlideLabel"
|
||||||
|
v-bind="ptm('item')"
|
||||||
|
:data-p-carousel-item-active="firstIndex() <= index && lastIndex() >= index"
|
||||||
|
:data-p-carousel-item-start="firstIndex() === index"
|
||||||
|
:data-p-carousel-item-end="lastIndex() === index"
|
||||||
|
>
|
||||||
|
<slot name="item" :data="item" :index="index"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="isCircular()">
|
||||||
|
<div v-for="(item, index) of value.slice(0, d_numVisible)" :key="index + '_fcloned'" :class="cx('itemCloned', { index, value, totalShiftedItems, d_numVisible })" v-bind="ptm('itemCloned')">
|
||||||
|
<slot name="item" :data="item" :index="index"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
v-if="showNavigators"
|
|
||||||
v-ripple
|
|
||||||
type="button"
|
|
||||||
:class="cx('nextButton')"
|
|
||||||
:disabled="forwardIsDisabled"
|
|
||||||
:aria-label="ariaNextButtonLabel"
|
|
||||||
@click="navForward"
|
|
||||||
v-bind="{ ...nextButtonProps, ...ptm('nextButton') }"
|
|
||||||
data-pc-group-section="navigator"
|
|
||||||
>
|
|
||||||
<slot name="nexticon">
|
|
||||||
<component :is="isVertical() ? 'ChevronDownIcon' : 'ChevronRightIcon'" :class="cx('nextButtonIcon')" v-bind="ptm('nextButtonIcon')" />
|
|
||||||
</slot>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<ul v-if="totalIndicators >= 0 && showIndicators" ref="indicatorContent" :class="[cx('indicators'), indicatorsContentClass]" @keydown="onIndicatorKeydown" v-bind="ptm('indicators')">
|
|
||||||
<li v-for="(indicator, i) of totalIndicators" :key="'p-carousel-indicator-' + i.toString()" :class="cx('indicator', { index: i })" v-bind="ptm('indicator', getIndicatorPTOptions(i))" :data-p-highlight="d_page === i">
|
<button
|
||||||
<button
|
v-if="showNavigators"
|
||||||
:class="cx('indicatorButton')"
|
v-ripple
|
||||||
type="button"
|
type="button"
|
||||||
:tabindex="d_page === i ? '0' : '-1'"
|
:class="cx('nextButton')"
|
||||||
:aria-label="ariaPageLabel(i + 1)"
|
:disabled="forwardIsDisabled"
|
||||||
:aria-current="d_page === i ? 'page' : undefined"
|
:aria-label="ariaNextButtonLabel"
|
||||||
@click="onIndicatorClick($event, i)"
|
@click="navForward"
|
||||||
v-bind="ptm('indicatorButton', getIndicatorPTOptions(i))"
|
v-bind="{ ...nextButtonProps, ...ptm('nextButton') }"
|
||||||
/>
|
data-pc-group-section="navigator"
|
||||||
</li>
|
>
|
||||||
</ul>
|
<slot name="nexticon">
|
||||||
</template>
|
<component :is="isVertical() ? 'ChevronDownIcon' : 'ChevronRightIcon'" :class="cx('nextButtonIcon')" v-bind="ptm('nextButtonIcon')" />
|
||||||
<slot name="empty" v-else>
|
</slot>
|
||||||
</slot>
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul v-if="totalIndicators >= 0 && showIndicators" ref="indicatorContent" :class="[cx('indicators'), indicatorsContentClass]" @keydown="onIndicatorKeydown" v-bind="ptm('indicators')">
|
||||||
|
<li v-for="(indicator, i) of totalIndicators" :key="'p-carousel-indicator-' + i.toString()" :class="cx('indicator', { index: i })" v-bind="ptm('indicator', getIndicatorPTOptions(i))" :data-p-highlight="d_page === i">
|
||||||
|
<button
|
||||||
|
:class="cx('indicatorButton')"
|
||||||
|
type="button"
|
||||||
|
:tabindex="d_page === i ? '0' : '-1'"
|
||||||
|
:aria-label="ariaPageLabel(i + 1)"
|
||||||
|
:aria-current="d_page === i ? 'page' : undefined"
|
||||||
|
@click="onIndicatorClick($event, i)"
|
||||||
|
v-bind="ptm('indicatorButton', getIndicatorPTOptions(i))"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<slot v-else name="empty">
|
||||||
|
{{ emptyMessageText }}
|
||||||
|
</slot>
|
||||||
<div v-if="$slots.footer" :class="cx('footer')" v-bind="ptm('footer')">
|
<div v-if="$slots.footer" :class="cx('footer')" v-bind="ptm('footer')">
|
||||||
<slot name="footer"></slot>
|
<slot name="footer"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
@ -190,77 +189,73 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
const isCircular = this.isCircular();
|
if (!this.empty) {
|
||||||
let stateChanged = false;
|
const isCircular = this.isCircular();
|
||||||
let totalShiftedItems = this.totalShiftedItems;
|
let stateChanged = false;
|
||||||
|
let totalShiftedItems = this.totalShiftedItems;
|
||||||
|
|
||||||
if (this.autoplayInterval) {
|
if (this.autoplayInterval) {
|
||||||
this.stopAutoplay();
|
this.stopAutoplay();
|
||||||
}
|
|
||||||
|
|
||||||
if (this.d_oldNumScroll !== this.d_numScroll || this.d_oldNumVisible !== this.d_numVisible || this.d_oldValue.length !== this.value.length) {
|
|
||||||
this.remainingItems = (this.value.length - this.d_numVisible) % this.d_numScroll;
|
|
||||||
|
|
||||||
let page = this.d_page;
|
|
||||||
|
|
||||||
if (this.totalIndicators !== 0 && page >= this.totalIndicators) {
|
|
||||||
page = this.totalIndicators - 1;
|
|
||||||
|
|
||||||
this.$emit('update:page', page);
|
|
||||||
this.d_page = page;
|
|
||||||
|
|
||||||
stateChanged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
totalShiftedItems = page * this.d_numScroll * -1;
|
if (this.d_oldNumScroll !== this.d_numScroll || this.d_oldNumVisible !== this.d_numVisible || this.d_oldValue.length !== this.value.length) {
|
||||||
|
this.remainingItems = (this.value.length - this.d_numVisible) % this.d_numScroll;
|
||||||
|
|
||||||
if (isCircular) {
|
let page = this.d_page;
|
||||||
totalShiftedItems -= this.d_numVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page === this.totalIndicators - 1 && this.remainingItems > 0) {
|
if (this.totalIndicators !== 0 && page >= this.totalIndicators) {
|
||||||
totalShiftedItems += -1 * this.remainingItems + this.d_numScroll;
|
page = this.totalIndicators - 1;
|
||||||
this.isRemainingItemsAdded = true;
|
this.$emit('update:page', page);
|
||||||
} else {
|
this.d_page = page;
|
||||||
this.isRemainingItemsAdded = false;
|
stateChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalShiftedItems !== this.totalShiftedItems) {
|
totalShiftedItems = page * this.d_numScroll * -1;
|
||||||
this.totalShiftedItems = totalShiftedItems;
|
|
||||||
|
|
||||||
stateChanged = true;
|
if (isCircular) {
|
||||||
}
|
totalShiftedItems -= this.d_numVisible;
|
||||||
|
}
|
||||||
|
|
||||||
this.d_oldNumScroll = this.d_numScroll;
|
if (page === this.totalIndicators - 1 && this.remainingItems > 0) {
|
||||||
this.d_oldNumVisible = this.d_numVisible;
|
totalShiftedItems += -1 * this.remainingItems + this.d_numScroll;
|
||||||
this.d_oldValue = this.value;
|
this.isRemainingItemsAdded = true;
|
||||||
|
} else {
|
||||||
|
this.isRemainingItemsAdded = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!this.empty) {
|
if (totalShiftedItems !== this.totalShiftedItems) {
|
||||||
|
this.totalShiftedItems = totalShiftedItems;
|
||||||
|
stateChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.d_oldNumScroll = this.d_numScroll;
|
||||||
|
this.d_oldNumVisible = this.d_numVisible;
|
||||||
|
this.d_oldValue = this.value;
|
||||||
this.$refs.itemsContainer.style.transform = this.isVertical() ? `translate3d(0, ${totalShiftedItems * (100 / this.d_numVisible)}%, 0)` : `translate3d(${totalShiftedItems * (100 / this.d_numVisible)}%, 0, 0)`;
|
this.$refs.itemsContainer.style.transform = this.isVertical() ? `translate3d(0, ${totalShiftedItems * (100 / this.d_numVisible)}%, 0)` : `translate3d(${totalShiftedItems * (100 / this.d_numVisible)}%, 0, 0)`;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isCircular) {
|
if (isCircular) {
|
||||||
if (this.d_page === 0) {
|
if (this.d_page === 0) {
|
||||||
totalShiftedItems = -1 * this.d_numVisible;
|
totalShiftedItems = -1 * this.d_numVisible;
|
||||||
} else if (totalShiftedItems === 0) {
|
} else if (totalShiftedItems === 0) {
|
||||||
totalShiftedItems = -1 * this.value.length;
|
totalShiftedItems = -1 * this.value.length;
|
||||||
|
|
||||||
if (this.remainingItems > 0) {
|
if (this.remainingItems > 0) {
|
||||||
this.isRemainingItemsAdded = true;
|
this.isRemainingItemsAdded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalShiftedItems !== this.totalShiftedItems) {
|
||||||
|
this.totalShiftedItems = totalShiftedItems;
|
||||||
|
|
||||||
|
stateChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalShiftedItems !== this.totalShiftedItems) {
|
if (!stateChanged && this.isAutoplay()) {
|
||||||
this.totalShiftedItems = totalShiftedItems;
|
this.startAutoplay();
|
||||||
|
|
||||||
stateChanged = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stateChanged && this.isAutoplay()) {
|
|
||||||
this.startAutoplay();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
if (this.responsiveOptions) {
|
if (this.responsiveOptions) {
|
||||||
|
@ -610,9 +605,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
empty() {
|
|
||||||
return !this.value || this.value.length === 0;
|
|
||||||
},
|
|
||||||
totalIndicators() {
|
totalIndicators() {
|
||||||
return this.value ? Math.max(Math.ceil((this.value.length - this.d_numVisible) / this.d_numScroll) + 1, 0) : 0;
|
return this.value ? Math.max(Math.ceil((this.value.length - this.d_numVisible) / this.d_numScroll) + 1, 0) : 0;
|
||||||
},
|
},
|
||||||
|
@ -633,6 +625,12 @@ export default {
|
||||||
},
|
},
|
||||||
attributeSelector() {
|
attributeSelector() {
|
||||||
return UniqueComponentId();
|
return UniqueComponentId();
|
||||||
|
},
|
||||||
|
empty() {
|
||||||
|
return !this.value || this.value.length === 0;
|
||||||
|
},
|
||||||
|
emptyMessageText() {
|
||||||
|
return this.$primevue.config?.locale?.emptyMessage || '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
Loading…
Reference in New Issue