mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-24 01:03:50 +02:00
replace el-carousel with Carousel component
This commit is contained in:
870
package-lock.json
generated
870
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -62,6 +62,7 @@
|
|||||||
"electron": "^39.2.7",
|
"electron": "^39.2.7",
|
||||||
"electron-builder": "^26.4.0",
|
"electron-builder": "^26.4.0",
|
||||||
"element-plus": "^2.13.1",
|
"element-plus": "^2.13.1",
|
||||||
|
"embla-carousel-vue": "^8.6.0",
|
||||||
"esbuild-jest": "^0.5.0",
|
"esbuild-jest": "^0.5.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
@@ -76,6 +77,7 @@
|
|||||||
"prettier": "^3.7.4",
|
"prettier": "^3.7.4",
|
||||||
"reka-ui": "^2.7.0",
|
"reka-ui": "^2.7.0",
|
||||||
"remixicon": "^4.8.0",
|
"remixicon": "^4.8.0",
|
||||||
|
"sass-embedded": "^1.97.2",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"tailwindcss": "^4.1.18",
|
"tailwindcss": "^4.1.18",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
|
|||||||
@@ -354,45 +354,24 @@
|
|||||||
:indeterminate="true"
|
:indeterminate="true"
|
||||||
:percentage="100"
|
:percentage="100"
|
||||||
:stroke-width="3"
|
:stroke-width="3"
|
||||||
style="margin: 10px 0; max-width: 240px" />
|
style="margin: 10px 0" />
|
||||||
<el-carousel
|
<div class="mt-2 w-[80%] ml-20">
|
||||||
v-if="avatarDialog.galleryImages.length"
|
<Carousel v-if="avatarDialog.galleryImages.length" class="w-full">
|
||||||
type="card"
|
<CarouselContent class="h-50">
|
||||||
:autoplay="false"
|
<CarouselItem v-for="imageUrl in avatarDialog.galleryImages" :key="imageUrl">
|
||||||
height="200px">
|
<div class="relative h-50 w-full">
|
||||||
<el-carousel-item v-for="imageUrl in avatarDialog.galleryImages" :key="imageUrl">
|
<img
|
||||||
<img
|
:src="imageUrl"
|
||||||
:src="imageUrl"
|
style="width: 100%; height: 100%; object-fit: contain"
|
||||||
style="width: 100%; height: 100%; object-fit: contain"
|
@click="showFullscreenImageDialog(imageUrl)"
|
||||||
@click="showFullscreenImageDialog(imageUrl)"
|
loading="lazy" />
|
||||||
loading="lazy" />
|
</div>
|
||||||
<div
|
</CarouselItem>
|
||||||
v-if="avatarDialog.ref.authorId === currentUser.id"
|
</CarouselContent>
|
||||||
style="position: absolute; bottom: 35px; left: 38%">
|
<CarouselPrevious />
|
||||||
<el-button
|
<CarouselNext />
|
||||||
size="small"
|
</Carousel>
|
||||||
:icon="Back"
|
</div>
|
||||||
circle
|
|
||||||
class="x-link"
|
|
||||||
style="margin-left: 0"
|
|
||||||
@click.stop="reorderAvatarGalleryImage(imageUrl, -1)"></el-button>
|
|
||||||
<el-button
|
|
||||||
size="small"
|
|
||||||
:icon="Right"
|
|
||||||
circle
|
|
||||||
class="x-link"
|
|
||||||
style="margin-left: 0"
|
|
||||||
@click.stop="reorderAvatarGalleryImage(imageUrl, 1)"></el-button>
|
|
||||||
<el-button
|
|
||||||
size="small"
|
|
||||||
:icon="Delete"
|
|
||||||
circle
|
|
||||||
class="x-link"
|
|
||||||
style="margin-left: 0"
|
|
||||||
@click.stop="deleteAvatarGalleryImage(imageUrl)"></el-button>
|
|
||||||
</div>
|
|
||||||
</el-carousel-item>
|
|
||||||
</el-carousel>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="avatarDialog.ref.publishedListings?.length">
|
<div v-if="avatarDialog.ref.publishedListings?.length">
|
||||||
<span class="name">{{ t('dialog.avatar.info.listings') }}</span>
|
<span class="name">{{ t('dialog.avatar.info.listings') }}</span>
|
||||||
@@ -560,6 +539,7 @@
|
|||||||
User,
|
User,
|
||||||
Warning
|
Warning
|
||||||
} from '@element-plus/icons-vue';
|
} from '@element-plus/icons-vue';
|
||||||
|
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/components/ui/carousel';
|
||||||
import { CircleCheck, Ellipsis, RefreshCcw, Star, Trash2 } from 'lucide-vue-next';
|
import { CircleCheck, Ellipsis, RefreshCcw, Star, Trash2 } from 'lucide-vue-next';
|
||||||
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
|
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
56
src/components/ui/carousel/Carousel.vue
Normal file
56
src/components/ui/carousel/Carousel.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
import { useProvideCarousel } from './useCarousel';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
opts: { type: Object, required: false },
|
||||||
|
plugins: { type: null, required: false },
|
||||||
|
orientation: { type: String, required: false, default: 'horizontal' },
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits(['init-api']);
|
||||||
|
|
||||||
|
const { canScrollNext, canScrollPrev, carouselApi, carouselRef, orientation, scrollNext, scrollPrev } =
|
||||||
|
useProvideCarousel(props, emits);
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
canScrollNext,
|
||||||
|
canScrollPrev,
|
||||||
|
carouselApi,
|
||||||
|
carouselRef,
|
||||||
|
orientation,
|
||||||
|
scrollNext,
|
||||||
|
scrollPrev
|
||||||
|
});
|
||||||
|
|
||||||
|
function onKeyDown(event) {
|
||||||
|
const prevKey = props.orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft';
|
||||||
|
const nextKey = props.orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight';
|
||||||
|
|
||||||
|
if (event.key === prevKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
scrollPrev();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === nextKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
scrollNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
data-slot="carousel"
|
||||||
|
:class="cn('relative', props.class)"
|
||||||
|
role="region"
|
||||||
|
aria-roledescription="carousel"
|
||||||
|
tabindex="0"
|
||||||
|
@keydown="onKeyDown">
|
||||||
|
<slot :can-scroll-next :can-scroll-prev :carousel-api :carousel-ref :orientation :scroll-next :scroll-prev />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
25
src/components/ui/carousel/CarouselContent.vue
Normal file
25
src/components/ui/carousel/CarouselContent.vue
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
import { useCarousel } from './useCarousel';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const { carouselRef, orientation } = useCarousel();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="carouselRef" data-slot="carousel-content" class="overflow-hidden">
|
||||||
|
<div
|
||||||
|
:class="cn('flex', orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col', props.class)"
|
||||||
|
v-bind="$attrs">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
21
src/components/ui/carousel/CarouselItem.vue
Normal file
21
src/components/ui/carousel/CarouselItem.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
import { useCarousel } from './useCarousel';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const { orientation } = useCarousel();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
data-slot="carousel-item"
|
||||||
|
role="group"
|
||||||
|
aria-roledescription="slide"
|
||||||
|
:class="cn('min-w-0 shrink-0 grow-0 basis-full', orientation === 'horizontal' ? 'pl-4' : 'pt-4', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
38
src/components/ui/carousel/CarouselNext.vue
Normal file
38
src/components/ui/carousel/CarouselNext.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ArrowRight } from 'lucide-vue-next';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
import { useCarousel } from './useCarousel';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
variant: { type: String, required: false, default: 'outline' },
|
||||||
|
size: { type: String, required: false, default: 'icon' },
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const { orientation, canScrollNext, scrollNext } = useCarousel();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button
|
||||||
|
data-slot="carousel-next"
|
||||||
|
:disabled="!canScrollNext"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'absolute size-8 rounded-full',
|
||||||
|
orientation === 'horizontal'
|
||||||
|
? 'top-1/2 -right-12 -translate-y-1/2'
|
||||||
|
: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:variant="variant"
|
||||||
|
:size="size"
|
||||||
|
@click="scrollNext">
|
||||||
|
<slot>
|
||||||
|
<ArrowRight />
|
||||||
|
<span class="sr-only">Next Slide</span>
|
||||||
|
</slot>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
38
src/components/ui/carousel/CarouselPrevious.vue
Normal file
38
src/components/ui/carousel/CarouselPrevious.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ArrowLeft } from 'lucide-vue-next';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
import { useCarousel } from './useCarousel';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
variant: { type: String, required: false, default: 'outline' },
|
||||||
|
size: { type: String, required: false, default: 'icon' },
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const { orientation, canScrollPrev, scrollPrev } = useCarousel();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button
|
||||||
|
data-slot="carousel-previous"
|
||||||
|
:disabled="!canScrollPrev"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'absolute size-8 rounded-full',
|
||||||
|
orientation === 'horizontal'
|
||||||
|
? 'top-1/2 -left-12 -translate-y-1/2'
|
||||||
|
: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:variant="variant"
|
||||||
|
:size="size"
|
||||||
|
@click="scrollPrev">
|
||||||
|
<slot>
|
||||||
|
<ArrowLeft />
|
||||||
|
<span class="sr-only">Previous Slide</span>
|
||||||
|
</slot>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
7
src/components/ui/carousel/index.js
Normal file
7
src/components/ui/carousel/index.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export { default as Carousel } from './Carousel.vue';
|
||||||
|
export { default as CarouselContent } from './CarouselContent.vue';
|
||||||
|
export { default as CarouselItem } from './CarouselItem.vue';
|
||||||
|
export { default as CarouselNext } from './CarouselNext.vue';
|
||||||
|
export { default as CarouselPrevious } from './CarouselPrevious.vue';
|
||||||
|
|
||||||
|
export { useCarousel } from './useCarousel';
|
||||||
63
src/components/ui/carousel/useCarousel.js
Normal file
63
src/components/ui/carousel/useCarousel.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { createInjectionState } from '@vueuse/core';
|
||||||
|
|
||||||
|
import emblaCarouselVue from 'embla-carousel-vue';
|
||||||
|
|
||||||
|
const [useProvideCarousel, useInjectCarousel] = createInjectionState(
|
||||||
|
(props, emits) => {
|
||||||
|
const { opts, orientation, plugins } = props;
|
||||||
|
const [emblaNode, emblaApi] = emblaCarouselVue(
|
||||||
|
{
|
||||||
|
...opts,
|
||||||
|
axis: orientation === 'horizontal' ? 'x' : 'y'
|
||||||
|
},
|
||||||
|
plugins
|
||||||
|
);
|
||||||
|
|
||||||
|
function scrollPrev() {
|
||||||
|
emblaApi.value?.scrollPrev();
|
||||||
|
}
|
||||||
|
function scrollNext() {
|
||||||
|
emblaApi.value?.scrollNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
const canScrollNext = ref(false);
|
||||||
|
const canScrollPrev = ref(false);
|
||||||
|
|
||||||
|
function onSelect(api) {
|
||||||
|
canScrollNext.value = api?.canScrollNext() || false;
|
||||||
|
canScrollPrev.value = api?.canScrollPrev() || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!emblaApi.value) return;
|
||||||
|
|
||||||
|
emblaApi.value?.on('init', onSelect);
|
||||||
|
emblaApi.value?.on('reInit', onSelect);
|
||||||
|
emblaApi.value?.on('select', onSelect);
|
||||||
|
|
||||||
|
emits('init-api', emblaApi.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
carouselRef: emblaNode,
|
||||||
|
carouselApi: emblaApi,
|
||||||
|
canScrollPrev,
|
||||||
|
canScrollNext,
|
||||||
|
scrollPrev,
|
||||||
|
scrollNext,
|
||||||
|
orientation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function useCarousel() {
|
||||||
|
const carouselState = useInjectCarousel();
|
||||||
|
|
||||||
|
if (!carouselState)
|
||||||
|
throw new Error('useCarousel must be used within a <Carousel />');
|
||||||
|
|
||||||
|
return carouselState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useCarousel, useProvideCarousel };
|
||||||
@@ -111,36 +111,39 @@
|
|||||||
:hint="screenshotMetadataDialog.metadata.author.displayName"
|
:hint="screenshotMetadataDialog.metadata.author.displayName"
|
||||||
style="color: var(--el-text-color-secondary); font-family: monospace" />
|
style="color: var(--el-text-color-secondary); font-family: monospace" />
|
||||||
<br />
|
<br />
|
||||||
<el-carousel
|
<div class="my-2 w-[95%] ml-6.5">
|
||||||
ref="screenshotMetadataCarouselRef"
|
<Carousel :opts="{ loop: false }" @init-api="handleScreenshotMetadataCarouselInit">
|
||||||
:interval="0"
|
<CarouselContent class="h-150">
|
||||||
:initial-index="1"
|
<CarouselItem>
|
||||||
indicator-position="none"
|
<div class="h-150 w-full">
|
||||||
arrow="always"
|
<img
|
||||||
height="600px"
|
class="x-link"
|
||||||
style="margin-top: 10px"
|
:src="screenshotMetadataDialog.metadata.previousFilePath"
|
||||||
@change="screenshotMetadataCarouselChange">
|
style="width: 100%; height: 100%; object-fit: contain" />
|
||||||
<el-carousel-item>
|
</div>
|
||||||
<img
|
</CarouselItem>
|
||||||
class="x-link"
|
<CarouselItem>
|
||||||
:src="screenshotMetadataDialog.metadata.previousFilePath"
|
<div class="h-150 w-full">
|
||||||
style="width: 100%; height: 100%; object-fit: contain" />
|
<img
|
||||||
</el-carousel-item>
|
class="x-link"
|
||||||
<el-carousel-item>
|
:src="screenshotMetadataDialog.metadata.filePath"
|
||||||
<img
|
style="width: 100%; height: 100%; object-fit: contain"
|
||||||
class="x-link"
|
@click="showFullscreenImageDialog(screenshotMetadataDialog.metadata.filePath)" />
|
||||||
:src="screenshotMetadataDialog.metadata.filePath"
|
</div>
|
||||||
style="width: 100%; height: 100%; object-fit: contain"
|
</CarouselItem>
|
||||||
@click="showFullscreenImageDialog(screenshotMetadataDialog.metadata.filePath)" />
|
<CarouselItem>
|
||||||
</el-carousel-item>
|
<div class="h-150 w-full">
|
||||||
<el-carousel-item>
|
<img
|
||||||
<img
|
class="x-link"
|
||||||
class="x-link"
|
:src="screenshotMetadataDialog.metadata.nextFilePath"
|
||||||
:src="screenshotMetadataDialog.metadata.nextFilePath"
|
style="width: 100%; height: 100%; object-fit: contain" />
|
||||||
style="width: 100%; height: 100%; object-fit: contain" />
|
</div>
|
||||||
</el-carousel-item>
|
</CarouselItem>
|
||||||
</el-carousel>
|
</CarouselContent>
|
||||||
<br />
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
<template v-if="screenshotMetadataDialog.metadata.error">
|
<template v-if="screenshotMetadataDialog.metadata.error">
|
||||||
<pre
|
<pre
|
||||||
style="white-space: pre-wrap; font-size: 12px"
|
style="white-space: pre-wrap; font-size: 12px"
|
||||||
@@ -161,6 +164,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
|
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/components/ui/carousel';
|
||||||
import { reactive, ref, watch } from 'vue';
|
import { reactive, ref, watch } from 'vue';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
@@ -219,11 +223,20 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const screenshotMetadataSearchInputs = ref(0);
|
const screenshotMetadataSearchInputs = ref(0);
|
||||||
const screenshotMetadataCarouselRef = ref(null);
|
const screenshotMetadataCarouselApi = ref(null);
|
||||||
|
const ignoreCarouselSelect = ref(false);
|
||||||
|
|
||||||
const handleComponentKeyup = (event) => {
|
const handleComponentKeyup = (event) => {
|
||||||
const carouselNavigation = { ArrowLeft: 0, ArrowRight: 2 }[event.key];
|
const carouselNavigation = { ArrowLeft: 0, ArrowRight: 2 }[event.key];
|
||||||
if (typeof carouselNavigation !== 'undefined' && props.isScreenshotMetadataDialogVisible) {
|
if (typeof carouselNavigation !== 'undefined' && props.isScreenshotMetadataDialogVisible) {
|
||||||
|
if (screenshotMetadataCarouselApi.value) {
|
||||||
|
if (event.key === 'ArrowLeft') {
|
||||||
|
screenshotMetadataCarouselApi.value.scrollPrev();
|
||||||
|
} else {
|
||||||
|
screenshotMetadataCarouselApi.value.scrollNext();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
screenshotMetadataCarouselChange(carouselNavigation);
|
screenshotMetadataCarouselChange(carouselNavigation);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -407,9 +420,7 @@
|
|||||||
getAndDisplayScreenshot(D.metadata.filePath);
|
getAndDisplayScreenshot(D.metadata.filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof screenshotMetadataCarouselRef.value !== 'undefined') {
|
resetCarouselIndex();
|
||||||
screenshotMetadataCarouselRef.value.setActiveItem(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fullscreenImageDialog.value.visible) {
|
if (fullscreenImageDialog.value.visible) {
|
||||||
// TODO
|
// TODO
|
||||||
@@ -451,13 +462,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof screenshotMetadataCarouselRef.value !== 'undefined') {
|
resetCarouselIndex();
|
||||||
screenshotMetadataCarouselRef.value.setActiveItem(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
D.searchIndex = searchIndex;
|
D.searchIndex = searchIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleScreenshotMetadataCarouselInit(api) {
|
||||||
|
screenshotMetadataCarouselApi.value = api;
|
||||||
|
api.on('select', handleCarouselSelect);
|
||||||
|
api.on('reInit', handleCarouselSelect);
|
||||||
|
resetCarouselIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCarouselSelect() {
|
||||||
|
if (ignoreCarouselSelect.value || !screenshotMetadataCarouselApi.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const index = screenshotMetadataCarouselApi.value.selectedScrollSnap();
|
||||||
|
screenshotMetadataCarouselChange(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetCarouselIndex() {
|
||||||
|
const api = screenshotMetadataCarouselApi.value;
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ignoreCarouselSelect.value = true;
|
||||||
|
api.scrollTo(1, true);
|
||||||
|
setTimeout(() => {
|
||||||
|
ignoreCarouselSelect.value = false;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
async function getAndDisplayScreenshot(path, needsCarouselFiles = true) {
|
async function getAndDisplayScreenshot(path, needsCarouselFiles = true) {
|
||||||
const metadata = await AppApi.GetScreenshotMetadata(path);
|
const metadata = await AppApi.GetScreenshotMetadata(path);
|
||||||
displayScreenshotMetadata(metadata, needsCarouselFiles);
|
displayScreenshotMetadata(metadata, needsCarouselFiles);
|
||||||
|
|||||||
Reference in New Issue
Block a user