n8n/packages/editor-ui/src/components/TemplatesInfoCarousel.vue
Milorad FIlipović 240d259260
refactor(editor): Rename CollectionCard and CollectionCardCarousel (#7994)
## Summary
As part of the plan to reuse current `CollectionCarousel` component,
this PR makes it, and the cards it uses, more generic by renaming them
and adds a new property to card component which can be used to hide the
item count.

#### How to test the change:
Until the component that will use this is implmeneted, this can be
tested manually:
1. Run n8n
2. Set `showItemCount` property for workflow collection cards to `false`
3. Check if workflow count is showing (it shouldn't) and if everything
rendering correctly

## Issues fixed
Fixes ADO-1567

## Review / Merge checklist
- [ ] PR title and summary are descriptive. **Remember, the title
automatically goes into the changelog. Use `(no-changelog)` otherwise.**
([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md))
- [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up
ticket created.
- [ ] Tests included.
> A bug is not considered fixed, unless a test is added to prevent it
from happening again. A feature is not complete without tests.
  >
> *(internal)* You can use Slack commands to trigger [e2e
tests](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#a39f9e5ba64a48b58a71d81c837e8227)
or [deploy test
instance](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#f6a177d32bde4b57ae2da0b8e454bfce)
or [deploy early access version on
Cloud](https://www.notion.so/n8n/Cloudbot-3dbe779836004972b7057bc989526998?pvs=4#fef2d36ab02247e1a0f65a74f6fb534e).
2023-12-11 17:37:26 +01:00

213 lines
4.5 KiB
Vue

<template>
<div :class="$style.container" v-show="loading || collections.length">
<agile
ref="slider"
:dots="false"
:navButtons="false"
:infinite="false"
:slides-to-show="4"
@after-change="updateCarouselScroll"
>
<Card v-for="n in loading ? 4 : 0" :key="`loading-${n}`" :loading="loading" />
<TemplatesInfoCard
v-for="collection in loading ? [] : collections"
:key="collection.id"
:collection="collection"
@click="(e) => onCardClick(e, collection.id)"
/>
</agile>
<button v-show="carouselScrollPosition > 0" :class="$style.leftButton" @click="scrollLeft">
<font-awesome-icon icon="chevron-left" />
</button>
<button v-show="!scrollEnd" :class="$style.rightButton" @click="scrollRight">
<font-awesome-icon icon="chevron-right" />
</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import type { PropType } from 'vue';
import type { ITemplatesCollection } from '@/Interface';
import Card from '@/components/CollectionWorkflowCard.vue';
import TemplatesInfoCard from '@/components/TemplatesInfoCard.vue';
import { VueAgile } from 'vue-agile';
import { genericHelpers } from '@/mixins/genericHelpers';
type SliderRef = InstanceType<typeof VueAgile>;
export default defineComponent({
name: 'TemplatesInfoCarousel',
mixins: [genericHelpers],
props: {
collections: {
type: Array as PropType<ITemplatesCollection[]>,
},
loading: {
type: Boolean,
},
},
watch: {
collections() {
setTimeout(() => {
this.updateCarouselScroll();
}, 0);
},
loading() {
setTimeout(() => {
this.updateCarouselScroll();
}, 0);
},
},
components: {
Card,
TemplatesInfoCard,
agile: VueAgile,
},
data() {
return {
carouselScrollPosition: 0,
cardWidth: 240,
scrollEnd: false,
listElement: null as null | Element,
};
},
methods: {
updateCarouselScroll() {
if (this.listElement) {
this.carouselScrollPosition = Number(this.listElement.scrollLeft.toFixed());
const width = this.listElement.clientWidth;
const scrollWidth = this.listElement.scrollWidth;
const scrollLeft = this.carouselScrollPosition;
this.scrollEnd = scrollWidth - width <= scrollLeft + 7;
}
},
onCardClick(event: MouseEvent, id: string) {
this.$emit('openCollection', { event, id });
},
scrollLeft() {
if (this.listElement) {
this.listElement.scrollBy({ left: -(this.cardWidth * 2), top: 0, behavior: 'smooth' });
}
},
scrollRight() {
if (this.listElement) {
this.listElement.scrollBy({ left: this.cardWidth * 2, top: 0, behavior: 'smooth' });
}
},
},
async mounted() {
await this.$nextTick();
const sliderRef = this.$refs.slider as SliderRef | undefined;
if (!sliderRef) {
return;
}
this.listElement = sliderRef.$el.querySelector('.agile__list');
if (this.listElement) {
this.listElement.addEventListener('scroll', this.updateCarouselScroll);
}
},
beforeUnmount() {
const sliderRef = this.$refs.slider as SliderRef | undefined;
if (sliderRef) {
sliderRef.destroy();
}
window.removeEventListener('scroll', this.updateCarouselScroll);
},
});
</script>
<style lang="scss" module>
.container {
position: relative;
}
.button {
width: 28px;
height: 37px;
position: absolute;
top: 35%;
border-radius: var(--border-radius-large);
border: var(--border-base);
background-color: #fbfcfe;
cursor: pointer;
&:after {
content: '';
width: 40px;
height: 140px;
top: -55px;
position: absolute;
}
svg {
color: var(--color-foreground-xdark);
}
}
.leftButton {
composes: button;
left: -30px;
&:after {
left: 27px;
background: linear-gradient(
270deg,
hsla(
var(--color-background-light-h),
var(--color-background-light-s),
var(--color-background-light-l),
50%
),
hsla(
var(--color-background-light-h),
var(--color-background-light-s),
var(--color-background-light-l),
100%
)
);
}
}
.rightButton {
composes: button;
right: -30px;
&:after {
right: 27px;
background: linear-gradient(
90deg,
hsla(
var(--color-background-light-h),
var(--color-background-light-s),
var(--color-background-light-l),
50%
),
hsla(
var(--color-background-light-h),
var(--color-background-light-s),
var(--color-background-light-l),
100%
)
);
}
}
</style>
<style lang="scss">
.agile {
&__list {
width: 100%;
padding-bottom: var(--spacing-2xs);
overflow-x: auto;
transition: all 1s ease-in-out;
}
&__track {
width: 50px;
}
}
</style>