This commit is contained in:
Parsa Nazer
2024-12-25 22:50:12 +03:30
51 changed files with 1245 additions and 145 deletions
+73
View File
@@ -0,0 +1,73 @@
@layer base {
@font-face {
font-family: "Morabba";
src: url("/fonts/Morabba/IRANYekanX-UltraLight.woff2");
font-weight: 200;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Morabba";
src: url("/fonts/Morabba/Morabba-Light.woff2");
font-weight: 300;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Morabba";
src: url("/fonts/Morabba/Morabba-Regular.woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Morabba";
src: url("/fonts/Morabba/Morabba-Medium.woff2");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Morabba";
src: url("/fonts/Morabba/Morabba-SemiBold.woff2");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Morabba";
src: url("/fonts/Morabba/Morabba-Bold.woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Morabba";
src: url("/fonts/Morabba/Morabba-ExtraBold.woff2");
font-weight: 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Morabba";
src: url("/fonts/Morabba/Morabba-Black.woff2");
font-weight: 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Morabba";
src: url("/fonts/Morabba/Morabba-Heavy.woff2");
font-weight: 1000;
font-style: normal;
font-display: swap;
}
}
+41
View File
@@ -0,0 +1,41 @@
@layer base {
@font-face {
font-family: "YekanBakh";
src: url("/fonts/YekanBakh/YekanBakh-Thin.woff");
font-weight: 100;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "YekanBakh";
src: url("/fonts/YekanBakh/YekanBakh-Light.woff");
font-weight: 300;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "YekanBakh";
src: url("/fonts/YekanBakh/YekanBakh-Regular.woff");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "YekanBakh";
src: url("/fonts/YekanBakh/YekanBakh-SemiBold.woff");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "YekanBakh";
src: url("/fonts/YekanBakh/YekanBakh-Bold.woff");
font-weight: 700;
font-style: normal;
font-display: swap;
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
@utility persian-number {
-moz-font-feature-settings: "ss02";
-webkit-font-feature-settings: "ss02";
font-feature-settings: "ss02";
-moz-font-feature-settings: "ss07";
-webkit-font-feature-settings: "ss07";
font-feature-settings: "ss07";
}
@utility text-gradient {
+9 -4
View File
@@ -4,7 +4,9 @@
@import "./typo.utils.css";
@import "./button.comp.css";
@import "./input.comp.css";
@import "./fonts/iran-yekan-x.css"
@import "./fonts/iran-yekan-x.css";
@import "./fonts/morabba.css";
@import "./fonts/yekan-bakh.css";
@theme {
/* COLORS */
@@ -109,19 +111,21 @@
--font-inter: "Inter", sans-serif;
--font-dmsans: "DM Sans", sans-serif;
--font-iran-yekan-x: "IRANYekanXVF", "sans-serif";
--font-yekan-bakh: "YekanBakh", "sans-serif";
--font-morabba: "Morabba", "sans-serif";
/* BREAKPOINTS */
--breakpoint-3xl: 1700px;
--breakpoint-2xs: 400px;
--breakpoint-xs: 480px;
/* ANIMATIONS */
--animate-marquee: marquee 3s linear infinite;
--animate-marquee: marquee 25s linear infinite;
--animate-marquee-reverse: marquee 25s linear infinite reverse;
@keyframes marquee {
to {
transform: translateY(-50%);
transform: translateX(50%);
}
}
}
@@ -129,6 +133,7 @@
/* CONTAINER */
@utility container {
@apply mx-auto;
padding-inline: 3rem;
@screen xl {
padding-inline: 5rem;
+8 -8
View File
@@ -1,35 +1,35 @@
/* TYPO HERO */
@utility typo-hero-1 {
@apply text-[96px] leading-[96px] font-bold ;
@apply text-[96px] leading-[96px] font-semibold ;
}
@utility typo-hero-2 {
@apply text-[72px] leading-[72px] font-bold ;
@apply text-[72px] leading-[72px] font-semibold ;
}
/* TYPE HEADING */
@utility typo-h-1 {
@apply text-[64px] leading-[72px] font-bold ;
@apply text-[64px] leading-[72px] font-semibold ;
}
@utility typo-h-2 {
@apply text-[56px] leading-[64px] font-bold ;
@apply text-[56px] leading-[64px] font-semibold ;
}
@utility typo-h-3 {
@apply text-[48px] leading-[56px] font-bold ;
@apply text-[48px] leading-[56px] font-semibold ;
}
@utility typo-h-4 {
@apply text-[40px] leading-[52px] font-bold ;
@apply text-[40px] leading-[52px] font-semibold ;
}
@utility typo-h-5 {
@apply text-[32px] leading-[40px] font-bold ;
@apply text-[32px] leading-[40px] font-semibold ;
}
@utility typo-h-6 {
@apply text-[24px] leading-[32px] font-bold ;
@apply text-[24px] leading-[32px] font-semibold ;
}
/* TYPE SUBHEADING */
+117
View File
@@ -0,0 +1,117 @@
<script setup lang="ts">
// types
type Props = {
tag: string;
date: string;
comments: number;
title: string;
description: string;
link: string;
variant?: "sm" | "lg";
}
// props
const props = withDefaults(defineProps<Props>(), {
variant: "lg"
});
const {} = toRefs(props);
</script>
<template>
<div
:class="variant === 'lg' ? 'rounded-150 overflow-hidden' : ''"
class="max-h-[700px] h-[700px] relative"
>
<Tag
v-if="variant === 'lg'"
class="bg-success-500 absolute left-10 top-10 z-20"
>
اسپیکر
</Tag>
<div
v-if="variant === 'sm'"
class="h-[350px] rounded-150 overflow-hidden relative"
>
<Tag
class="bg-success-500 absolute z-20 left-6 top-6"
>
اسپیکر
</Tag>
<img
src="/img/hero-bg.jpg"
class="absolute size-full object-cover z-10"
alt=""
/>
</div>
<div
:class="variant === 'lg' ? 'absolute px-10' : 'invert mt-8'"
class="bottom-10 flex flex-col gap-6 z-20"
>
<div class="flex items-center gap-6">
<div class="flex items-center gap-2">
<Icon
name="ci:comment"
size="18"
class="**:stroke-white"
/>
<span class="typo-p-sm text-white">
۰ نظر
</span>
</div>
<div class="flex items-center gap-2">
<Icon
name="ci:calendar"
size="18"
class="**:stroke-white"
/>
<span class="typo-p-sm text-white">
۳۱ مهر ۱۴۰۳
</span>
</div>
</div>
<div class="flex gap-4 flex-col">
<span
:class="variant === 'lg' ? 'typo-h-4' : 'typo-h-6'"
class="text-white"
>
برسی آیفون ۱۶ پرومکس
</span>
<p
:class="variant === 'lg' ? 'typo-h-4' : 'typo-h-6 text-slate-500'"
class="typo-p-md text-white text-justify"
>
نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد.
نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد.
کتابهای زیادی در شصت و سه درصد گذشته.
</p>
</div>
<span class="underline text-white typo-p-md">
بیشتر بخوانید...
</span>
</div>
<img
v-if="variant === 'lg'"
src="/img/hero-bg.jpg"
class="absolute size-full object-cover z-10"
alt=""
/>
<div
v-if="variant === 'lg'"
class="w-full h-full bg-linear-to-t from-black to-transparent absolute inset-0 z-15"
/>
</div>
</template>
+73
View File
@@ -0,0 +1,73 @@
<script setup lang="ts">
// types
type Props = {
// brands: string[];
}
// props
const props = defineProps<Props>();
const {} = toRefs(props);
// state
const { $gsap: gsap } = useNuxtApp();
// lifecycle
onMounted(() => {
gsap.to("#marquee-text-container", {
xPercent: -50, // Adjust based on content width
duration: 10, // Adjust for desired speed
ease: "none",
repeat: -1
});
});
</script>
<template>
<div class="relative w-full flex flex-col justify-center h-[350px]">
<div class="-rotate-z-2 z-20">
<div
class="bg-warning-500 flex pr-20 gap-20 py-2 w-max animate-marquee-reverse"
>
<span
v-for="i in 10"
class="text-[50px] text-white whitespace-nowrap font-semibold"
>
TEST {{ i }}
</span>
<span
v-for="i in 10"
class="text-[50px] text-white whitespace-nowrap font-semibold"
>
TEST {{ i }}
</span>
</div>
</div>
<div class="rotate-z-2 z-10">
<div
class="bg-slate-50 flex pr-20 gap-20 py-2 w-max animate-marquee"
>
<span
v-for="i in 10"
class="text-[50px] text-slate-300 whitespace-nowrap font-semibold"
>
TEST {{ i }}
</span>
<span
v-for="i in 10"
class="text-[50px] text-slate-300 whitespace-nowrap font-semibold"
>
TEST {{ i }}
</span>
</div>
</div>
</div>
</template>
@@ -0,0 +1,47 @@
<script setup lang="ts">
// types
type Props = {
category: string;
count: number;
description: string;
picture: string;
}
// props
defineProps<Props>();
</script>
<template>
<div class="relative rounded-150 overflow-hidden w-full h-[500px]">
<img
class="absolute object-cover size-full"
:src="picture"
alt=""
/>
<div class="bg-linear-to-t from-black/80 to-transparent absolute z-10 size-full" />
<div class="absolute z-20 bottom-0 p-6 flex items-end justify-between w-full">
<div class="flex flex-col gap-2 text-white">
<div class="typo-s-h-md">
تمام دسته ها
<span class="typo-p-xs -translate-y-1 inline-block mr-1">
24
</span>
</div>
<span class="typo-p-md">محصولات ما را مشاهده کنید</span>
</div>
<Icon
size="24"
name="ci:arrow-left"
class="**:stroke-white mb-1"
/>
</div>
</div>
</template>
+1 -1
View File
@@ -35,7 +35,7 @@ const nav_links = ref<NavLink[]>([
<template>
<header class="w-full bg-white flex-center">
<div
class="w-full flex items-center justify-between container py-[2.25rem]"
class="size-full flex items-center justify-between container py-[2.25rem]"
>
<div class="w-2/12 flex items-center justify-start">
<span class="size-[2rem] bg-black rounded-full"></span>
@@ -9,12 +9,12 @@
<Icon name="ci:check" class="size-4 **:stroke-white"/>
</div>
<div class="flex flex-col gap-1">
<span class="typo-label-sm whitespace-nowrap">Pick up from Store</span>
<span class="typo-p-sm whitespace-nowrap">Usually ready in 2 hours</span>
<span class="typo-label-sm whitespace-nowrap">دریافت حضوری فروشگاه</span>
<span class="typo-p-sm whitespace-nowrap">معمولا طی ۲ ساعت اماده میشود</span>
</div>
</div>
<span class="typo-p-xs">
Check availability at other stores
برسی موجودی در فروشگاه های دیگر
</span>
</div>
</template>
@@ -0,0 +1,96 @@
<script setup lang="ts">
// imports
import { Swiper, SwiperSlide } from "swiper/vue";
import type { SwiperClass } from "swiper/react";
// types
type Props = {
title: string;
};
// props
defineProps<Props>();
// state
const swiper_instance = ref<SwiperClass | null>(null);
// methods
const onSwiper = (swiper: SwiperClass) => {
swiper_instance.value = swiper;
};
</script>
<template>
<div class="w-full flex flex-col gap-[4rem] py-[5rem] container">
<div class="w-full flex justify-between items-center">
<span class="text-black typo-h-3">
{{ title }}
</span>
<div class="flex-center gap-[.5rem]">
<button
@click="swiper_instance?.slidePrev()"
:disabled="swiper_instance?.isBeginning"
:class="
swiper_instance?.isBeginning
? 'border-slate-200 cursor-not-allowed'
: 'border-black cursor-pointer'
"
class="size-[2.75rem] rounded-full border-[1.5px] click-effect flex-center"
>
<Icon
name="ci:chevron-right"
:class="
swiper_instance?.isBeginning
? '**:stroke-slate-200'
: '**:stroke-black'
"
size="24"
/>
</button>
<button
@click="swiper_instance?.slideNext()"
:disabled="swiper_instance?.isEnd"
:class="
swiper_instance?.isEnd
? 'border-slate-200 cursor-not-allowed'
: 'border-black cursor-pointer'
"
class="size-[2.75rem] rounded-full border-[1.5px] click-effect flex-center"
>
<Icon
name="ci:chevron-left"
:class="
swiper_instance?.isEnd
? '**:stroke-slate-200'
: '**:stroke-black'
"
size="24"
/>
</button>
</div>
</div>
<div class="w-full">
<Swiper :slides-per-view="3" :space-between="24" @swiper="onSwiper">
<SwiperSlide v-for="i in 4" :key="i">
<ProductCard
brand="Samsung"
title="Galaxy S20 Ultra"
picture="/assets/img/product-1.jpg"
:colors="['#0000ff', '#00ff00', 'red']"
:price="599"
:rate="2.4"
tag="New"
/>
</SwiperSlide>
</Swiper>
</div>
</div>
</template>
<style scoped></style>
@@ -4,9 +4,6 @@
<template>
<div class="flex items-center gap-2">
<span class="typo-p-sm">
5.0
</span>
<div class="flex items-center gap-1">
<Icon
name="ci:star-solid"
@@ -29,5 +26,8 @@
class="size-4.5 **:fill-yellow-500"
/>
</div>
<span class="typo-p-sm">
5.0
</span>
</div>
</template>
@@ -3,6 +3,7 @@
// types
type Props = {
maxQuantity: number;
quantity: number;
}
@@ -13,16 +14,19 @@ defineProps<Props>();
</script>
<template>
<div class="flex flex-col gap-2 w-full items-end">
<div class="flex flex-col gap-2 w-full">
<p class="typo-p-sm text-slate-500">
Hurry, only
سریع باش فقط
<span class="text-black">
{{ quantity }}
{{ maxQuantity }}
</span>
items left in stock
عدد از این محصول باقی مانده
</p>
<div class="h-2 rounded-full relative bg-slate-200 w-full">
<div class="w-[85%] h-full absolute left-0 rounded-full bg-black" />
<div
:style="{ width: `${quantity * (100 / maxQuantity)}%` }"
class="h-full absolute right-0 rounded-full bg-black transition-all ease-out"
/>
</div>
</div>
</template>
@@ -1,7 +1,7 @@
<template>
<div class="flex items-center gap-6">
<span class="typo-p-md text-black">
Share:
اشتراک گذاری:
</span>
<div class="flex items-center gap-3">
<NuxtLink>
@@ -0,0 +1,58 @@
<script lang="ts" setup>
// types
type Props = {
selectedSlide: number;
slides: {
id: number;
picture: string;
}[]
}
// props
const props = defineProps<Props>();
const { slides, selectedSlide } = toRefs(props);
// emit
const emit = defineEmits(["update:selectedSlide"]);
// computed
const selectedSlideDetail = computed(() => {
return slides.value.find((item) => {
return item.id === selectedSlide.value;
})!;
});
// method
const changeSlide = (id: number) => {
emit("update:selectedSlide", id);
};
</script>
<template>
<div class="flex flex-col relative gap-4">
<div class="bg-red-300 w-full relative aspect-square overflow-hidden rounded-200">
<img
class="size-full absolute object-cover"
:src="selectedSlideDetail.picture"
:alt="String(selectedSlideDetail.id)"
/>
</div>
<div class="flex items-center justify-between gap-4">
<div
@click="changeSlide(slide.id)"
v-for="slide in slides"
:class="selectedSlide === slide.id ? 'ring-black' : 'ring-transparent'"
class="cursor-pointer aspect-square w-[108px] ring-2 ring-offset-4 rounded-200 w-full overflow-hidden relative"
:key="slide.id"
>
<img class="absolute object-cover size-full" :src="slide.picture" :alt="String(slide.id)" />
</div>
</div>
</div>
</template>
@@ -0,0 +1,23 @@
<script setup lang="ts">
</script>
<template>
<div class="w-full p-12 rounded-150 bg-white border-[0.5px] border-slate-200 flex flex-col gap-6">
<div class="flex justify-between items-start w-full">
<div class="flex flex-col gap-3">
<span class="typo-h-6 text-black">
خیلی محصول خوبی بودددد
</span>
<span class="typo-p-sm text-slate-500">
منصور مرزبان در ۱۴۰۳/۱۲/۲ ساعت ۱۲:۳۴
</span>
</div>
<Rating />
</div>
<div class="typo-p-md">
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون
بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرد گذشته.
</div>
</div>
</template>
@@ -1,5 +1,4 @@
<script lang="ts" setup>
// import
import Tag from "~/components/ui/Tag.vue";
@@ -16,22 +15,25 @@ type Props = {
picture: string;
tag?: string;
rate?: number;
}
};
// props
defineProps<Props>();
</script>
<template>
<div class="relative h-[500px] w-[400px] rounded-2xl bg-black/10 overflow-hidden p-6">
<div
class="relative size-full min-h-[31.25rem] rounded-2xl bg-black/10 overflow-hidden p-6"
>
<img
src="~/assets/img/product-2.jpg"
class="size-full object-cover absolute inset-0"
alt="product-background"
/>
<div class="flex justify-between items-center absolute px-6 pt-6 top-0 w-full inset-x-0">
<div
class="flex justify-between items-center absolute px-6 pt-6 top-0 w-full inset-x-0"
>
<Rate v-if="rate">
{{ rate }}
</Rate>
@@ -39,12 +41,12 @@ defineProps<Props>();
{{ tag }}
</Tag>
</div>
<div class="absolute inset-x-0 bottom-0 pb-6 px-6 flex justify-between items-center">
<span class="typo-p-md">
${{ price }}
</span>
<div
class="absolute inset-x-0 bottom-0 pb-6 px-6 flex justify-between items-center"
>
<span class="typo-p-md"> ${{ price }} </span>
<div class="flex flex-col gap-2 items-end">
<span class="typo-p-md ">
<span class="typo-p-md">
{{ brand }}
</span>
<span class="typo-sub-h-md">
@@ -54,7 +56,7 @@ defineProps<Props>();
<ColorCircle
v-for="color in colors"
:key="color"
:style="{backgroundColor: color}"
:style="{ backgroundColor: color }"
/>
</div>
</div>
@@ -0,0 +1,35 @@
<script lang="ts" setup>
// types
type Props = {
picture: string;
title: string;
color: string;
price: number;
}
// props
const props = defineProps<Props>();
const { picture, price, title, color } = toRefs(props);
</script>
<template>
<div class="max-w-[500px] w-full h-[116px] flex items-center justify-between py-2 pe-6 ps-2 bg-white rounded-150">
<div class="flex items-center gap-4">
<div class="relative size-[100px] rounded-100 overflow-hidden border-[0.5px] border-slate-200">
<img :src="picture" :alt="title" class="object-cover absolute" />
</div>
<div class="flex flex-col gap-1">
<span class="typo-sub-h-md text-black">{{ title }}</span>
<span class="typo-p-sm text-slate-500">{{ color }}</span>
<span class="typo-p-md text-black">{{ price }}</span>
</div>
</div>
<Button class="rounded-full">
افزودن به سبد
</Button>
</div>
</template>
+96
View File
@@ -0,0 +1,96 @@
<script setup lang="ts">
// types
import { Swiper, SwiperSlide } from "swiper/vue";
import type { SwiperClass } from "swiper/react";
type Props = {}
// props
const props = defineProps<Props>();
const {} = toRefs(props);
// state
const swiper_instance = ref<SwiperClass | null>(null);
const slides = [
{
id: 0,
title: "TEST"
},
{
id: 1,
title: "TEST"
},
{
id: 2,
title: "TEST"
},
{
id: 3,
title: "TEST"
},
{
id: 4,
title: "TEST"
}
];
// methods
const onSwiper = (swiper: SwiperClass) => {
swiper_instance.value = swiper;
};
</script>
<template>
<div class="w-full my-20 relative">
<Swiper
:slides-per-view="3.65"
:space-between="20"
:slides-offset-after="125"
:slides-offset-before="125"
@swiper="onSwiper"
>
<SwiperSlide
v-for="slide in slides"
:key="slide.id"
>
<CategoryCard
category="یک دسته بندی تست"
picture="/img/product-1.jpg"
:count="20"
description="یک دسته بندی تستasdasd"
/>
</SwiperSlide>
</Swiper>
<div
v-if="!swiper_instance?.isBeginning"
@click="swiper_instance?.slidePrev()"
class="absolute z-20 right-20 shadow-lg cursor-pointer shadow-black/25 bottom-[50%] bg-white rounded-full size-11.5 flex justify-center items-center"
>
<Icon
name="ci:arrow-right"
class="**:stroke-black"
size="24"
/>
</div>
<div
v-if="!swiper_instance?.isEnd"
@click="swiper_instance?.slideNext()"
class="absolute z-20 left-20 shadow-lg cursor-pointer shadow-black/25 bottom-[50%] bg-white rounded-full size-11.5 flex justify-center items-center"
>
<Icon
name="ci:arrow-left"
class="**:stroke-black"
size="24"
/>
</div>
</div>
</template>
+121
View File
@@ -0,0 +1,121 @@
<script setup lang="ts">
// import
import { Swiper, SwiperSlide } from "swiper/vue";
import type { SwiperClass } from "swiper/react";
// state
const swiper_instance = ref<SwiperClass | null>(null);
const slides = [
{
id: 0,
title: "TEST"
},
{
id: 1,
title: "TEST"
},
{
id: 2,
title: "TEST"
},
{
id: 3,
title: "TEST"
},
{
id: 4,
title: "TEST"
}
];
// methods
const onSwiper = (swiper: SwiperClass) => {
swiper_instance.value = swiper;
};
const onChange = (swiper: SwiperClass) => {
console.log(swiper.activeIndex, swiper.realIndex, swiper.snapIndex);
};
</script>
<template>
<div class="w-full mb-20">
<div
class="relative"
>
<Swiper
:slides-per-view="1.2"
:loop="true"
:space-between="40"
:centered-slides="true"
@swiper="onSwiper"
@slide-change="onChange"
>
<SwiperSlide
v-for="slide in slides"
:key="slide.id"
>
<div class="relative w-full rounded-200 h-[80svh] overflow-hidden">
<img
class="absolute inset-0 size-full object-cover"
src="/img/hero-bg.jpg"
alt=""
/>
<div class="size-full absolute z-10 bg-linear-to-t from-black to-transparent" />
<div class="px-20 absolute z-10 w-full bottom-36">
<div class="border-b border-white/10 pb-6">
<h3 class="typo-hero-1 text-white">
Samsung {{ slide.id }}
</h3>
<div class="flex justify-between items-end">
<span class="typo-p-lg text-white">
توضیحات درمورد این محصول خاص
</span>
<Button class="invert rounded-full hover:bg-transparent">
خرید Samsung
</Button>
</div>
</div>
</div>
</div>
</SwiperSlide>
</Swiper>
<div class="absolute w-full bottom-20 left-[50%] translate-x-[-50%] z-100">
<div class="container h-full">
<div class="h-full flex items-center justify-between px-20">
<button @click="swiper_instance?.slidePrev()">
<Icon
class="**:stroke-white cursor-pointer"
name="ci:arrow-right"
size="24"
/>
</button>
<div class="flex items-center justify-center gap-3 text-white">
<div
v-for="(slide, index) in slides"
:class="swiper_instance?.realIndex === index ? 'bg-white' : 'bg-transparent'"
class="border border-white size-3 rounded-full transition-all duration-200"
@click="swiper_instance?.slideTo(index)"
>
</div>
</div>
<button>
<Icon
@click="swiper_instance?.slideNext()"
class="**:stroke-white cursor-pointer"
name="ci:arrow-left"
size="24"
/>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
@@ -0,0 +1,65 @@
<script setup lang="ts">
// types
type Props = {}
// props
const props = defineProps<Props>();
const {} = toRefs(props);
</script>
<template>
<section class="h-[100svh] mt-20 px-20">
<div class="flex items-center justify-between mb-20">
<span class="typo-h-4 text-black">
مقالات اخیر سایت
</span>
<Button variant="outlined" class="rounded-full" start-icon="ci:paper">
نمایش همه
</Button>
</div>
<div class="flex gap-12">
<div class="flex-1 flex flex-col gap-12">
<BlogPost
description="aaasd"
title="asd"
:comments="2"
link="#"
date="2020-06-10"
tag="asdsa"
/>
<BlogPost
description="aaasd"
title="asd"
:comments="2"
link="#"
date="2020-06-10"
tag="asdsa"
/>
</div>
<div class="flex-[0.8] flex flex-col">
<BlogPost
description="aaasd"
title="asd"
:comments="2"
link="#"
date="2020-06-10"
tag="asdsa"
variant="sm"
/>
<BlogPost
description="aaasd"
title="asd"
:comments="2"
link="#"
date="2020-06-10"
tag="asdsa"
variant="sm"
/>
</div>
</div>
</section>
</template>
@@ -0,0 +1,78 @@
<script setup lang="ts">
// import
import { Swiper, SwiperSlide } from "swiper/vue";
import type { SwiperClass } from "swiper/react";
// types
type Props = {}
// props
const props = defineProps<Props>();
const {} = toRefs(props);
// state
const swiper_instance = ref<SwiperClass | null>(null);
// methods
const onSwiper = (swiper: SwiperClass) => {
swiper_instance.value = swiper;
};
</script>
<template>
<div class="relative max-h-[700px] flex justify-center items-center h-svh w-full">
<img
class="absolute size-full object-cover"
src="/img/hero-bg.jpg"
alt=""
/>
<div class="absolute bg-black/60 size-full z-10" />
<div class="w-full relative z-10">
<Swiper
:slides-per-view="1"
:loop="true"
:space-between="40"
:centered-slides="true"
:grab-cursor="true"
@swiper="onSwiper"
>
<SwiperSlide
v-for="i in 6"
:key="i"
>
<div class="flex justify-center items-center">
<div class="max-w-[900px] px-4 text-white flex flex-col items-center gap-4">
<Icon name="ci:instagram" size="28" class="**:stroke-white" />
<p class="typo-h-5 leading-[150%] text-center">
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با
استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله
در ستون و سطرآنچنان که لازم.
</p>
<span class="typo-p-xl text-center">
- منصور مرزبان
</span>
</div>
</div>
</SwiperSlide>
</Swiper>
<div class="flex items-center justify-center gap-3 text-white mt-6 relative z-10">
<div
v-for="(i, index) in 6"
:class="swiper_instance?.realIndex === index ? 'bg-white' : 'bg-transparent'"
class="border border-white size-2 rounded-full transition-all duration-200"
>
</div>
</div>
</div>
</div>
</template>
+93
View File
@@ -0,0 +1,93 @@
<script setup lang="ts">
// import
import { useDraggable } from "@vueuse/core";
// state
const clipPathPercent = ref(49);
const draggableEl = ref<HTMLElement | null>(null);
const previewContainerEl = ref<HTMLElement | null>(null);
const { x: dragAxisX } = useDraggable(draggableEl, {
initialValue: { x: 0, y: 0 },
axis: "x"
});
// watch
watch(() => dragAxisX.value, (newValue) => {
const clientRect = previewContainerEl.value?.getBoundingClientRect()!;
const percent = clientRect.width / 100;
const clipPercent = (newValue - clientRect.x - 8) / percent;
if (clipPercent >= 5 && clipPercent <= 95) {
clipPathPercent.value = clipPercent;
}
});
</script>
<template>
<div class="container">
<div class="flex flex-col items-center gap-3 mb-16">
<span class="typo-p-md text-slate-500">یک متن تست لورم</span>
<span class="typo-h-3 text-black">تفاوت محصول را ببینید در اینجا</span>
</div>
<div ref="previewContainerEl" class="rounded-200 overflow-hidden h-[90svh] relative">
<img
src="/img/hero-bg.jpg"
class="select-none absolute size-full object-cover"
alt=""
/>
<div
class="absolute size-full right-0 w-full"
>
<img
src="/img/hero-bg.jpg"
class="overlay-image select-none absolute object-cover size-full hue-rotate-200 brightness-35"
alt=""
/>
<div
:style="{
left: `${clipPathPercent}%`
}"
ref="draggableEl"
class="select-none w-2 h-full bg-white absolute left-0 flex items-center justify-center"
>
<div
class="cursor-grab hover:scale-115 transition-transform rounded-full absolute bg-white size-11 flex items-center justify-center"
>
<Icon name="ci:arrows" size="24" class="**:stroke-black" />
</div>
</div>
</div>
<div class="absolute bottom-0 p-10 w-full flex justify-between items-end bg-linear-to-t from-black/55 to-transparent">
<div class="flex flex-col gap-2 text-white">
<span class="typo-p-md">رنگ محصول</span>
<span class="typo-h-3">نارنجی</span>
</div>
<div class="flex flex-col justify-start gap-2 text-white">
<span class="typo-p-md">رنگ محصول</span>
<span class="typo-h-3">سفید</span>
</div>
</div>
</div>
</div>
</template>
<style>
.overlay-image {
clip-path: polygon(
v-bind('clipPathPercent + "%"') 0,
100% 0,
100% 100%,
v-bind('clipPathPercent + "%"') 100%
);
}
</style>
+29
View File
@@ -0,0 +1,29 @@
<script setup lang="ts">
</script>
<template>
<section class="bg-slate-50 p-20">
<div class="flex gap-12 my-42 container">
<div class="flex flex-col gap-6 min-w-fit">
<h3 class="typo-h-3">
نظرات کاربران
</h3>
<div class="flex flex-col gap-2">
<Rating />
<span class="typo-p-sm">
بر اساس ۴ نظر
</span>
</div>
<Button class="rounded-full">
نظر بنویسید
</Button>
</div>
<div class="flex flex-col gap-12">
<Comment />
<Comment />
<Comment />
</div>
</div>
</section>
</template>
@@ -0,0 +1,68 @@
<script lang="ts" setup>
// state
const quantity = ref(1);
const maxQuantity = ref(10);
const selectedSlide = ref(0);
const slides = [
{
id: 0,
picture: "/img/product-1.jpg",
},
{
id: 1,
picture: "/img/product-2.jpg",
},
{
id: 2,
picture: "/img/product-3.jpg",
},
];
</script>
<template>
<div class="flex gap-12 container pt-[5rem]">
<Slider
class="flex-1"
v-model:selectedSlide="selectedSlide"
:slides="slides"
/>
<div class="flex-1 flex flex-col gap-3 mt-12">
<span class="typo-label-sm"> سامسونگ </span>
<h1 class="typo-h-2">موبایل Nova</h1>
<div class="flex w-full items-center justify-between">
<span class="typo-p-2xl"> $689.00 </span>
<Rating />
</div>
<p class="typo-p-md text-slate-500 text-justify">
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با
استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله
در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد
نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد.
کتابهای زیادی در شصت و سه درصد گذشته.
</p>
<div class="w-full flex flex-col gap-6 mt-4">
<RemainQuantity
:maxQuantity="maxQuantity"
:quantity="quantity"
/>
<div class="w-full flex gap-3 flex-col">
<div class="w-full flex gap-3">
<Button class="w-full rounded-full" end-icon="ci:plus">
افزودن به سبد خرید
</Button>
<QuantityCounter
v-model="quantity"
:max="maxQuantity"
/>
</div>
<Button class="w-full rounded-full" variant="outlined">
همین الان بخر
</Button>
</div>
<InfoCard />
<Share />
</div>
</div>
</div>
</template>
@@ -1,55 +0,0 @@
<script lang="ts" setup>
// state
const quantity = ref(1);
const maxQuantity = ref(10);
</script>
<template>
<div class="flex gap-12">
<div class="w-[800px] flex flex-col items-end gap-3 mt-12">
<span class="typo-label-sm">
Nova
</span>
<h1 class="typo-h-2">
Nova
</h1>
<div class="flex w-full items-center justify-between">
<Rating />
<span class="typo-p-2xl">
$689.00
</span>
</div>
<p class="typo-p-md text-slate-500 text-left">
Compact, stylish, and engineered for the future, Eco Tunes are more than just headphones; they're a
statement. Hear the future, save the planet.
</p>
<div class="w-full flex flex-col gap-6 mt-4">
<RemainQuantity :quantity="maxQuantity" />
<div class="w-full flex gap-3 flex-col">
<div class="w-full flex gap-3">
<Button
class="w-full"
end-icon="ci:plus"
>
Add to cart
</Button>
<QuantityCounter v-model="quantity" :max="maxQuantity" />
</div>
<Button
class="w-full"
variant="outlined"
>
Buy it now
</Button>
</div>
<InfoCard />
<Share />
</div>
</div>
<div class="bg-red-300 w-full h-[500px] rounded-200">
</div>
</div>
</template>
+15
View File
@@ -0,0 +1,15 @@
<script lang="ts" setup></script>
<template>
<div class="h-[95svh] w-full relative bg-black mt-[5rem]">
<img src="/img/product-3.jpg" class="object-cover absolute size-full" />
<div class="size-full absolute inset-0 bg-black/60" />
<StickyCard
color="سبز"
:price="240000"
picture="/img/product-1.jpg"
title="نام محصول"
class="absolute right-6 bottom-6"
/>
</div>
</template>
@@ -1,6 +1,6 @@
<script setup lang="ts">
// types
import Tooltip from "~/components/Tooltip.vue";
import Tooltip from "~/components/ui/Tooltip.vue";
type Props = {
variant?: "solid" | "outlined";
+2 -2
View File
@@ -11,9 +11,9 @@
</script>
<template>
<div class="w-full flex flex-col-center font-iran-yekan-x" dir="rtl">
<div class="w-full flex flex-col-center persian-number font-yekan-bakh" dir="rtl">
<Header />
<main class="w-full overflow-x-hidden container">
<main class="w-full overflow-x-hidden">
<slot />
</main>
<Footer />
+17 -15
View File
@@ -3,22 +3,20 @@ export default defineNuxtConfig({
compatibilityDate: "2024-11-01",
ssr: false,
devtools: { enabled: false },
css: [
"~/assets/css/tailwind.css",
],
css: ["~/assets/css/tailwind.css", "swiper/css"],
postcss: {
plugins: {
"@tailwindcss/postcss": {},
autoprefixer: {}
}
autoprefixer: {},
},
},
components: [
{
path: "~/components",
pathPrefix: false
}
pathPrefix: false,
},
],
icon: {
@@ -26,20 +24,24 @@ export default defineNuxtConfig({
customCollections: [
{
prefix: "ci",
dir: "./public/icons"
}
]
dir: "./public/icons",
},
],
},
modules: [[
modules: [
[
"@nuxtjs/google-fonts",
{
families: {
"DM Sans": "100..900",
Inter: "100..900",
download: true,
inject: false
}
}
], "@nuxt/icon", "reka-ui/nuxt"]
inject: false,
},
},
],
"@nuxt/icon",
"reka-ui/nuxt",
],
});
+20
View File
@@ -15,6 +15,7 @@
"gsap": "^3.12.5",
"nuxt": "^3.14.1592",
"reka-ui": "^1.0.0-alpha.6",
"swiper": "^11.1.15",
"vue": "latest",
"vue-router": "latest",
"vue-scrollto": "^2.20.0",
@@ -8957,6 +8958,25 @@
"url": "https://opencollective.com/svgo"
}
},
"node_modules/swiper": {
"version": "11.1.15",
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.1.15.tgz",
"integrity": "sha512-IzWeU34WwC7gbhjKsjkImTuCRf+lRbO6cnxMGs88iVNKDwV+xQpBCJxZ4bNH6gSrIbbyVJ1kuGzo3JTtz//CBw==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/swiperjs"
},
{
"type": "open_collective",
"url": "http://opencollective.com/swiper"
}
],
"license": "MIT",
"engines": {
"node": ">= 4.7.0"
}
},
"node_modules/system-architecture": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz",
+1
View File
@@ -19,6 +19,7 @@
"gsap": "^3.12.5",
"nuxt": "^3.14.1592",
"reka-ui": "^1.0.0-alpha.6",
"swiper": "^11.1.15",
"vue": "latest",
"vue-router": "latest",
"vue-scrollto": "^2.20.0",
+11 -19
View File
@@ -1,26 +1,18 @@
<script setup lang="ts">
import Categories from "~/components/home/Categories.vue";
</script>
<template>
<div class="w-full h-screen p-8 flex gap-8 justify-start items-start">
<Product
brand="Samsung"
title="Galaxy S20 Ultra"
picture="/assets/img/product-1.jpg"
:colors="['#0000ff', '#00ff00','red']"
:price="599"
:rate="2.4"
tag="New"
/>
<Product
brand="Samsung"
title="Galaxy S20 Ultra"
picture="/assets/img/product-1.jpg"
:colors="['#0000ff', '#00ff00','red']"
:price="599"
:rate="2.4"
tag="New"
/>
<div class="w-full">
<Hero />
<Preview />
<Categories />
<ProductsSlider title="یک عنوان تستی" />
<Brands />
<ProductHero />
<MostRecentComments />
<LatestStories />
</div>
</template>
+5 -4
View File
@@ -1,7 +1,8 @@
<script lang="ts" setup>
</script>
<script lang="ts" setup></script>
<template>
<ProductView />
<ProductHero />
<Video />
<Comments />
<RelatedProducts title="محصولات مشابه" />
</template>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB