This commit is contained in:
Parsa Nazer
2025-02-26 21:25:34 +03:30
6 changed files with 76 additions and 23 deletions
+6 -3
View File
@@ -3,18 +3,21 @@
// types // types
type Props = { type Props = {
selectable?: boolean,
selected?: boolean; selected?: boolean;
} }
// props // props
defineProps<Props>(); withDefaults(defineProps<Props>(), {
selectable: false
});
</script> </script>
<template> <template>
<div <div
class="size-[25px] rounded-full transition-all ring-2 ring-offset-4 shadow-black/30 shadow-inner" class="size-[25px] rounded-full transition-all shadow-black/30 shadow-inner"
:class="selected ? 'ring-blue-500' : 'ring-transparent'" :class="[selectable ? 'ring-2 ring-offset-4 ' : '', selected ? 'ring-blue-500' : 'ring-transparent']"
/> />
</template> </template>
+26 -5
View File
@@ -1,12 +1,23 @@
<script lang="ts" setup> <script lang="ts" setup>
// import
import type { LoadingOverlayProvideType } from "~/pages/index.vue";
// provide / inject
const { showLoadingOverlay } = inject("loadingOverlay") as LoadingOverlayProvideType;
// state // state
const { $gsap: gsap } = useNuxtApp(); const { $gsap: gsap } = useNuxtApp();
const shouldRenderLoadingOverlay = ref(true);
// lifecycle // lifecycle
onMounted(() => { watch(() => showLoadingOverlay.value, (value) => {
if (!value) {
const timeline = gsap.timeline(); const timeline = gsap.timeline();
timeline timeline
@@ -16,18 +27,28 @@ onMounted(() => {
.to("#loading-overlay", { .to("#loading-overlay", {
scale: 0.8, scale: 0.8,
opacity: 0, opacity: 0,
delay: 5 delay: 3
}) })
.to("#loading-overlay", { .to("#loading-overlay", {
opacity: 0, opacity: 0,
y: "20%" y: "20%",
onComplete: () => {
shouldRenderLoadingOverlay.value = false;
}
}); });
}
}, {
once: true
}); });
</script> </script>
<template> <template>
<div id="loading-overlay" class="fixed inset-0 size-full z-9999 flex-center bg-black"> <div
v-if="shouldRenderLoadingOverlay"
id="loading-overlay"
class="fixed inset-0 size-full z-9999 flex-center bg-black"
>
<img id="loading-overlay-image" src="/video/loading-2.gif" class="opacity-0 scale-70 absolute z-20" alt="" /> <img id="loading-overlay-image" src="/video/loading-2.gif" class="opacity-0 scale-70 absolute z-20" alt="" />
<div <div
id="loading-overlay-gradient" id="loading-overlay-gradient"
@@ -41,7 +62,7 @@ onMounted(() => {
#loading-overlay-image { #loading-overlay-image {
animation-name: loading-overlay-image-animation; animation-name: loading-overlay-image-animation;
animation-duration: 1s; animation-duration: 1s;
animation-delay: 0.75s; animation-delay: 0.35s;
animation-fill-mode: forwards; animation-fill-mode: forwards;
} }
@@ -92,7 +92,7 @@ const onSwiper = (swiper: SwiperClass) => {
brand="برند محصول" brand="برند محصول"
:title="product.name" :title="product.name"
:picture="product.variants[0].images[0].image" :picture="product.variants[0].images[0].image"
:colors="product.variants.map(v => v.color)" :colors="product.colors"
:price="product.variants[0].price" :price="product.variants[0].price"
:rate="product.rating" :rate="product.rating"
:dark-layer="true" :dark-layer="true"
+7 -1
View File
@@ -5,6 +5,11 @@
import { Swiper, SwiperSlide } from "swiper/vue"; import { Swiper, SwiperSlide } from "swiper/vue";
import type { SwiperClass } from "swiper/react"; import type { SwiperClass } from "swiper/react";
import useHomeData from "~/composables/api/home/useHomeData"; import useHomeData from "~/composables/api/home/useHomeData";
import type { LoadingOverlayProvideType } from "~/pages/index.vue";
// provide / inject
const { changeLoadingOverlay } = inject("loadingOverlay") as LoadingOverlayProvideType;
// state // state
@@ -31,6 +36,7 @@ let gsapTimeline: gsap.core.Timeline;
// methods // methods
const onSwiper = (swiper: SwiperClass) => { const onSwiper = (swiper: SwiperClass) => {
changeLoadingOverlay(false);
swiper_instance.value = swiper; swiper_instance.value = swiper;
}; };
@@ -105,7 +111,7 @@ onMounted(() => {
padding: "0px 80px" padding: "0px 80px"
}, { }, {
padding: "0px 40px" padding: "0px 40px"
}, "=") }, "=");
ScrollTrigger.create({ ScrollTrigger.create({
trigger: "#header-slider-container", trigger: "#header-slider-container",
@@ -101,6 +101,7 @@ watch(() => selectedVariant.value, (newValue) => {
v-for="color in product!.colors" v-for="color in product!.colors"
:key="color" :key="color"
@click="selectedColor = color" @click="selectedColor = color"
selectable
:selected="selectedColor === color " :selected="selectedColor === color "
:style="{backgroundColor: color}" :style="{backgroundColor: color}"
class="cursor-pointer" class="cursor-pointer"
+23 -1
View File
@@ -4,10 +4,32 @@
import useHomeData from "~/composables/api/home/useHomeData"; import useHomeData from "~/composables/api/home/useHomeData";
// type
export type LoadingOverlayProvideType = {
showLoadingOverlay: Ref<boolean>,
changeLoadingOverlay: (value: boolean) => void,
}
// state // state
const { suspense } = useHomeData(); const { suspense } = useHomeData();
const showLoadingOverlay = ref(true);
// method
const changeLoadingOverlay = (value: boolean) => {
showLoadingOverlay.value = value;
};
// provide / inject
provide("loadingOverlay", {
showLoadingOverlay,
changeLoadingOverlay
});
// ssr // ssr
const response = await suspense(); const response = await suspense();
@@ -23,7 +45,7 @@ if (response.isError) {
<template> <template>
<div class="w-full"> <div class="w-full">
<!-- <LoadingOverlay />--> <LoadingOverlay />
<Hero /> <Hero />
<Preview /> <Preview />
<ProductsShowcase /> <ProductsShowcase />