mirror of https://github.com/rancher/dashboard.git
374 lines
7.6 KiB
Vue
374 lines
7.6 KiB
Vue
<script>
|
|
import { get } from '@shell/utils/object';
|
|
import { BadgeState } from '@components/BadgeState';
|
|
import { mapGetters } from 'vuex';
|
|
|
|
const carouselSeenStorageKey = `carousel-seen`;
|
|
|
|
export default {
|
|
components: { BadgeState },
|
|
name: 'Carousel',
|
|
props: {
|
|
sliders: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
keyField: {
|
|
type: String,
|
|
default: 'key',
|
|
},
|
|
asLink: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
linkField: {
|
|
type: String,
|
|
default: 'link'
|
|
},
|
|
targetField: {
|
|
type: String,
|
|
default: 'target',
|
|
},
|
|
rel: {
|
|
type: String,
|
|
default: 'noopener noreferrer nofollow'
|
|
},
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
slider: this.sliders,
|
|
activeItemId: 0,
|
|
autoScroll: true,
|
|
autoScrollSlideInterval: null,
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters(['clusterId']),
|
|
trackStyle() {
|
|
let sliderItem = ( this.activeItemId + 1) * 100 / (this.slider.length + 2);
|
|
const width = 60 * (this.slider.length + 2);
|
|
|
|
if (this.slider.length === 1) {
|
|
sliderItem = 0;
|
|
}
|
|
|
|
return `transform: translateX(-${ sliderItem }%); width: ${ width }%`;
|
|
},
|
|
|
|
test() {
|
|
return 'test';
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
get,
|
|
|
|
select(slide, i) {
|
|
this.$emit('clicked', slide, i);
|
|
},
|
|
|
|
scrollSlide(i) {
|
|
this.autoScroll = false;
|
|
this.activeItemId = i;
|
|
},
|
|
|
|
nextPrev(direction) {
|
|
this.autoScroll = false;
|
|
const slideTrack = document.getElementById('slide-track');
|
|
|
|
slideTrack.style.transition = `transform 450ms ease-in-out`;
|
|
|
|
direction !== 'prev' ? (this.activeItemId++) : (this.activeItemId--);
|
|
|
|
slideTrack.addEventListener('transitionend', this.slideTransition);
|
|
},
|
|
|
|
slideTransition() {
|
|
const slideTrack = document.getElementById('slide-track');
|
|
const slidesArray = this.slider.length + 2;
|
|
|
|
if (this.activeItemId === -1) {
|
|
slideTrack.style.transition = 'none';
|
|
this.activeItemId = this.slider.length - 1;
|
|
}
|
|
if (this.activeItemId === slidesArray - 2) {
|
|
slideTrack.style.transition = 'none';
|
|
this.activeItemId = 0;
|
|
}
|
|
},
|
|
|
|
autoScrollSlide() {
|
|
if (this.activeItemId < (this.slider.length + 1) && this.autoScroll ) {
|
|
this.activeItemId++;
|
|
}
|
|
|
|
if (this.activeItemId > this.slider.length - 1) {
|
|
this.autoScroll = false;
|
|
this.activeItemId = 0;
|
|
}
|
|
},
|
|
},
|
|
|
|
beforeDestroy() {
|
|
if (this.autoScrollSlideInterval) {
|
|
clearInterval(this.autoScrollSlideInterval);
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
const slideTrack = document.getElementById('slide-track');
|
|
|
|
if (this.slider.length === 1) {
|
|
// singleSlide.style = 'width: 100%; max-width: 100%';
|
|
slideTrack.style = 'transform:translateX(0%); width:100%; left:0';
|
|
} else {
|
|
const node = document.getElementById('slide0');
|
|
const clone = node.cloneNode(true);
|
|
|
|
const nodeLast = document.getElementById(`slide${ this.slider.length - 1 }`);
|
|
const cloneLast = nodeLast.cloneNode(true);
|
|
|
|
slideTrack.appendChild(clone);
|
|
slideTrack.insertBefore(cloneLast, slideTrack.children[0]);
|
|
}
|
|
|
|
const lastSeenCluster = sessionStorage.getItem(carouselSeenStorageKey);
|
|
|
|
if (lastSeenCluster !== this.clusterId) {
|
|
// Session storage lasts until tab/window closed (retained on refresh)
|
|
sessionStorage.setItem(carouselSeenStorageKey, this.clusterId);
|
|
}
|
|
|
|
this.autoScrollSlideInterval = setInterval(this.autoScrollSlide, 5000);
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="slider"
|
|
:class="{'disable': sliders.length === 1}"
|
|
>
|
|
<div
|
|
id="slide-track"
|
|
ref="slider"
|
|
:style="trackStyle"
|
|
class="slide-track"
|
|
>
|
|
<div
|
|
:is="asLink ? 'a' : 'div'"
|
|
v-for="(slide, i) in sliders"
|
|
:id="`slide` + i"
|
|
ref="slide"
|
|
:key="get(slide, keyField)"
|
|
class="slide"
|
|
:class="{'singleSlide': sliders.length === 1}"
|
|
:href="asLink ? get(slide, linkField) : null"
|
|
:target="get(slide, targetField)"
|
|
:rel="rel"
|
|
@click="select(slide, i)"
|
|
>
|
|
<div class="slide-content">
|
|
<div class="slide-img">
|
|
<img :src="slide.icon ? slide.icon : `/_nuxt/shell/assets/images/generic-catalog.svg`">
|
|
</div>
|
|
<div class="slide-content-right">
|
|
<BadgeState
|
|
:label="slide.repoName"
|
|
color="slider-badge mb-20"
|
|
/>
|
|
<h1>{{ slide.chartNameDisplay }} {{ i + 1 }}</h1>
|
|
<p>{{ slide.chartDescription }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="controls"
|
|
:class="{'disable': sliders.length === 1}"
|
|
>
|
|
<div
|
|
v-for="(slide, i) in slider"
|
|
:key="i"
|
|
class="control-item"
|
|
:class="{'active': activeItemId === i}"
|
|
@click="scrollSlide(i, slider.length)"
|
|
/>
|
|
</div>
|
|
<div
|
|
ref="prev"
|
|
class="prev"
|
|
:class="{'disable': sliders.length === 1}"
|
|
@click="nextPrev('prev')"
|
|
>
|
|
<i class="icon icon-chevron-left icon-4x" />
|
|
</div>
|
|
<div
|
|
ref="next"
|
|
class="next"
|
|
:class="{'disable': sliders.length === 1}"
|
|
@click="nextPrev('next')"
|
|
>
|
|
<i class="icon icon-chevron-right icon-4x" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang='scss' scoped>
|
|
.slider {
|
|
margin: auto;
|
|
position: relative;
|
|
width: 100%;
|
|
place-items: center;
|
|
overflow: hidden;
|
|
margin-bottom: 30px;
|
|
// min-width: 700px;
|
|
|
|
&.disable::before,
|
|
&.disable::after {
|
|
display: none;
|
|
}
|
|
|
|
&.disable:hover {
|
|
.prev,
|
|
.next {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
&:hover {
|
|
.prev,
|
|
.next {
|
|
display: block;
|
|
}
|
|
}
|
|
}
|
|
|
|
.slide-track {
|
|
display: flex;
|
|
animation: scrolls 10s ;
|
|
position: relative;
|
|
transition: 1s ease-in-out;
|
|
left: 21%;
|
|
}
|
|
|
|
.slider-badge {
|
|
background: var(--app-partner-accent);
|
|
color: var(--body-bg);
|
|
}
|
|
.slide {
|
|
min-height: 210px;
|
|
width: 60%;
|
|
max-width: 60%;
|
|
margin: 0 10px;
|
|
position: relative;
|
|
border: 1px solid var(--tabbed-border);
|
|
border-radius: var(--border-radius);
|
|
cursor: pointer;
|
|
|
|
&.singleSlide {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
}
|
|
.slide-header {
|
|
background: var(--default);
|
|
width: 100%;
|
|
padding: 10px 15px;
|
|
}
|
|
.slide-content {
|
|
display: flex;
|
|
padding: 30px;
|
|
|
|
.slide-img {
|
|
width: 150px;
|
|
background: var(--card-badge-text);
|
|
border-radius: calc(2 * var(--border-radius));
|
|
|
|
img {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
.slide-content-right {
|
|
border-left: 1px solid var(--tabbed-border);
|
|
margin-left: 30px;
|
|
padding-left: 30px;
|
|
|
|
span {
|
|
margin: 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
.slider::before,
|
|
.slider::after {
|
|
background: linear-gradient(to right, var(--slider-light-bg) 0%, var(--slider-light-bg-right) 100%);
|
|
content: "";
|
|
height: 100%;
|
|
position: absolute;
|
|
width: 15%;
|
|
z-index: z-index('overContent');
|
|
}
|
|
|
|
.slider::before {
|
|
left: 0;
|
|
top: 0;
|
|
&.disable {
|
|
display: none;
|
|
}
|
|
}
|
|
.slider::after{
|
|
right: -1px;
|
|
top: 0;
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.controls {
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-top: 10px;
|
|
|
|
&.disable {
|
|
display: none;
|
|
}
|
|
|
|
.control-item {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
background: var(--scrollbar-thumb);
|
|
margin: 5px;
|
|
cursor: pointer;
|
|
|
|
&.active {
|
|
background:var(--body-text);
|
|
}
|
|
}
|
|
}
|
|
.prev,
|
|
.next {
|
|
position: absolute;
|
|
z-index: 20;
|
|
top: 90px;
|
|
display: none;
|
|
cursor: pointer;
|
|
|
|
&.disabled .icon {
|
|
color: var(--disabled-bg);
|
|
cursor: not-allowed;
|
|
}
|
|
}
|
|
|
|
.next {
|
|
right: 0;
|
|
}
|
|
|
|
</style>
|