negodesign 0.0.25 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/start.png +0 -0
- package/dist/components/core/card/highlight/01/CardHighlight01.svelte +91 -0
- package/dist/components/core/card/highlight/01/CardHighlight01.svelte.d.ts +8 -0
- package/dist/components/core/card/highlight/02/CardHighlight02.svelte +93 -0
- package/dist/components/core/card/highlight/02/CardHighlight02.svelte.d.ts +8 -0
- package/dist/components/core/card/highlight/CardHighlight.svelte +13 -58
- package/dist/components/core/card/highlight/CardHighlight.svelte.d.ts +5 -2
- package/dist/components/core/card/highlight/data.js +32 -1
- package/dist/components/core/card/media/01/CardMedia01.svelte +8 -11
- package/dist/components/core/card/media/02/CardMedia02.svelte +4 -7
- package/dist/components/core/card/profile/01/CardProfile01.svelte +7 -10
- package/dist/components/core/card/profile/02/CardProfile02.svelte +2 -1
- package/dist/components/core/card/promotion/CardPromotion.svelte +49 -39
- package/dist/components/core/card/promotion/data.js +31 -0
- package/dist/components/core/card/shared/CardDescription.svelte +23 -8
- package/dist/components/core/card/shared/CardDescription.svelte.d.ts +3 -0
- package/dist/components/core/card/shared/CardStartPrice.svelte +34 -0
- package/dist/components/core/card/shared/CardStartPrice.svelte.d.ts +8 -0
- package/dist/components/core/card/shared/CardTags.svelte +32 -15
- package/dist/components/core/card/shared/CardTags.svelte.d.ts +2 -1
- package/dist/components/core/card/types.d.ts +13 -1
- package/dist/components/core/carousel/badge/ui/CarouselBadge.svelte +8 -5
- package/dist/components/core/carousel/highlights/types.d.ts +3 -0
- package/dist/components/core/carousel/highlights/ui/CarouselHighlights.svelte +27 -5
- package/dist/components/core/carousel/promotion/types.d.ts +2 -0
- package/dist/components/core/carousel/promotion/ui/CarouselPromotion.svelte +28 -5
- package/dist/components/core/carousel/shared/ui/CarouselHeader.svelte +10 -5
- package/dist/components/core/carousel/shared/ui/CarouselHeader.svelte.d.ts +6 -1
- package/dist/components/core/carousel/types.d.ts +1 -2
- package/dist/components/core/nav/ui/nav-menu.svelte +3 -3
- package/dist/components/core/panel/CarouselSlot.svelte +37 -12
- package/dist/components/core/panel/CarouselSlot.svelte.d.ts +5 -2
- package/dist/globals.css +139 -17
- package/dist/hooks/responsive.svelte.d.ts +1 -1
- package/dist/hooks/responsive.svelte.js +1 -1
- package/package.json +3 -3
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { GlobalIcon, MapPin, NecklaceIcon } from "@hugeicons/core-free-icons";
|
|
1
2
|
const items = [
|
|
2
3
|
{
|
|
3
4
|
id: 1,
|
|
4
5
|
imageUrl: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
|
|
5
6
|
title: 'Hotel Baía Azul',
|
|
7
|
+
content: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).",
|
|
6
8
|
isFavorite: true,
|
|
7
9
|
newPrice: 1000,
|
|
8
10
|
oldPrice: 2000,
|
|
@@ -10,22 +12,34 @@ const items = [
|
|
|
10
12
|
onClickBuy: () => alert('Comprar'),
|
|
11
13
|
onClickShop: (id) => alert(`Shop ${id}`),
|
|
12
14
|
isCart: true,
|
|
15
|
+
tags: [
|
|
16
|
+
{ icon: GlobalIcon, text: 'Luanda', },
|
|
17
|
+
{ icon: MapPin, text: 'Kilamba', },
|
|
18
|
+
{ icon: NecklaceIcon, text: 'Rua 19', },
|
|
19
|
+
]
|
|
13
20
|
},
|
|
14
21
|
{
|
|
15
22
|
id: 2,
|
|
16
23
|
imageUrl: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
|
|
17
24
|
title: 'Miramar Suites',
|
|
25
|
+
content: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).",
|
|
18
26
|
newPrice: 1000,
|
|
19
27
|
oldPrice: 2000,
|
|
20
28
|
buttonBuyText: 'Comprar',
|
|
21
29
|
onClickBuy: (id) => alert(`Buy ${id}`),
|
|
22
30
|
onClickShop: (id) => alert(`Shop ${id}`),
|
|
23
31
|
isCart: true,
|
|
32
|
+
tags: [
|
|
33
|
+
{ icon: GlobalIcon, text: 'Luanda', },
|
|
34
|
+
{ icon: MapPin, text: 'Kilamba', },
|
|
35
|
+
{ icon: NecklaceIcon, text: 'Rua 19', },
|
|
36
|
+
]
|
|
24
37
|
},
|
|
25
38
|
{
|
|
26
39
|
id: 3,
|
|
27
40
|
imageUrl: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
|
|
28
41
|
title: 'Palm Resort & Spa',
|
|
42
|
+
content: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).",
|
|
29
43
|
isFavorite: true,
|
|
30
44
|
newPrice: 1000,
|
|
31
45
|
oldPrice: 2000,
|
|
@@ -33,22 +47,34 @@ const items = [
|
|
|
33
47
|
onClickBuy: (id) => alert(`Buy ${id}`),
|
|
34
48
|
onClickShop: (id) => alert(`Shop ${id}`),
|
|
35
49
|
isCart: true,
|
|
50
|
+
tags: [
|
|
51
|
+
{ icon: GlobalIcon, text: 'Luanda', },
|
|
52
|
+
{ icon: MapPin, text: 'Kilamba', },
|
|
53
|
+
{ icon: NecklaceIcon, text: 'Rua 19', },
|
|
54
|
+
]
|
|
36
55
|
},
|
|
37
56
|
{
|
|
38
57
|
id: 4,
|
|
39
58
|
imageUrl: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
|
|
40
59
|
title: 'Hotel Central Park',
|
|
60
|
+
content: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).",
|
|
41
61
|
newPrice: 1000,
|
|
42
62
|
oldPrice: 2000,
|
|
43
63
|
buttonBuyText: 'Comprar',
|
|
44
64
|
onClickBuy: (id) => alert(`Buy ${id}`),
|
|
45
65
|
onClickShop: (id) => alert(`Shop ${id}`),
|
|
46
66
|
isCart: true,
|
|
67
|
+
tags: [
|
|
68
|
+
{ icon: GlobalIcon, text: 'Luanda', },
|
|
69
|
+
{ icon: MapPin, text: 'Kilamba', },
|
|
70
|
+
{ icon: NecklaceIcon, text: 'Rua 19', },
|
|
71
|
+
]
|
|
47
72
|
},
|
|
48
73
|
{
|
|
49
74
|
id: 5,
|
|
50
75
|
imageUrl: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
|
|
51
76
|
title: 'Villa Serena Boutique',
|
|
77
|
+
content: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).",
|
|
52
78
|
isFavorite: true,
|
|
53
79
|
newPrice: 1000,
|
|
54
80
|
oldPrice: 2000,
|
|
@@ -56,6 +82,11 @@ const items = [
|
|
|
56
82
|
onClickBuy: (id) => alert(`Buy ${id}`),
|
|
57
83
|
onClickShop: (id) => alert(`Shop ${id}`),
|
|
58
84
|
isCart: true,
|
|
85
|
+
tags: [
|
|
86
|
+
{ icon: GlobalIcon, text: 'Luanda', },
|
|
87
|
+
{ icon: MapPin, text: 'Kilamba', },
|
|
88
|
+
{ icon: NecklaceIcon, text: 'Rua 19', },
|
|
89
|
+
]
|
|
59
90
|
},
|
|
60
91
|
];
|
|
61
92
|
export default items;
|
|
@@ -3,21 +3,36 @@
|
|
|
3
3
|
import { HugeiconsIcon } from "@hugeicons/svelte";
|
|
4
4
|
import TruncatableText from "./TruncatableText.svelte";
|
|
5
5
|
import { t } from "../../../../i18n";
|
|
6
|
-
|
|
6
|
+
import Skeleton from "../../../ui/skeleton/skeleton.svelte";
|
|
7
|
+
|
|
7
8
|
type CardDescriptionProps = {
|
|
8
9
|
content?: string;
|
|
10
|
+
isLoading?: boolean;
|
|
11
|
+
isDescriptionIcon?: boolean;
|
|
12
|
+
isDescriptionLabel?: boolean;
|
|
9
13
|
};
|
|
10
14
|
|
|
11
|
-
let {
|
|
15
|
+
let {
|
|
16
|
+
content,
|
|
17
|
+
isLoading = false,
|
|
18
|
+
isDescriptionIcon = true,
|
|
19
|
+
isDescriptionLabel = true,
|
|
20
|
+
}: CardDescriptionProps = $props();
|
|
12
21
|
</script>
|
|
13
22
|
|
|
14
|
-
{#if
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
{#if isLoading}
|
|
24
|
+
<Skeleton class="w-full h-5" />
|
|
25
|
+
{:else if content}
|
|
26
|
+
{#if isDescriptionLabel}
|
|
27
|
+
<div class="mb-1 text-[14px] md:text-[15px] flex items-center gap-1">
|
|
28
|
+
{#if isDescriptionIcon}
|
|
29
|
+
<HugeiconsIcon icon={Message01Icon} class="h-4 w-4" />
|
|
30
|
+
{/if}
|
|
31
|
+
<div>{$t("label.description")}</div>
|
|
32
|
+
</div>
|
|
33
|
+
{/if}
|
|
19
34
|
<TruncatableText
|
|
20
35
|
text={content}
|
|
21
|
-
class="text-justify text-[13px] text-gray-800 dark:text-gray-50 mt-3 my-4"
|
|
36
|
+
class="text-justify text-[13px] md:text-[14px] text-gray-800 dark:text-gray-50 mt-3 my-4"
|
|
22
37
|
/>
|
|
23
38
|
{/if}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
type CardDescriptionProps = {
|
|
2
2
|
content?: string;
|
|
3
|
+
isLoading?: boolean;
|
|
4
|
+
isDescriptionIcon?: boolean;
|
|
5
|
+
isDescriptionLabel?: boolean;
|
|
3
6
|
};
|
|
4
7
|
declare const CardDescription: import("svelte").Component<CardDescriptionProps, {}, "">;
|
|
5
8
|
type CardDescription = ReturnType<typeof CardDescription>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import StartBg from "../../../../assets/start.png";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
newPrice?: string | number;
|
|
6
|
+
oldPrice?: string | number;
|
|
7
|
+
type?: "start" | "normal";
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let { newPrice, oldPrice, type = "normal" }: Props = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div class="relative min-w-25 md:min-w-32">
|
|
14
|
+
{#if type == "start"}
|
|
15
|
+
<img
|
|
16
|
+
src={StartBg}
|
|
17
|
+
alt=""
|
|
18
|
+
class="shadow rounded-tl-lg rounded-bl-lg md:w-20 md:h-20 lg:w-32 lg:h-24 absolute"
|
|
19
|
+
/>
|
|
20
|
+
{/if}
|
|
21
|
+
{#if newPrice && oldPrice}
|
|
22
|
+
<div class="z-20 flex justify-center items-center">
|
|
23
|
+
<div class="font-extrabold md:text-3xl lg:text-4xl">{newPrice}</div>
|
|
24
|
+
<div class="mx-2">/</div>
|
|
25
|
+
<div
|
|
26
|
+
class="font-semibold line-through text-red-600 text-[10px] md:text-xl"
|
|
27
|
+
>
|
|
28
|
+
{oldPrice}
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
{:else}
|
|
32
|
+
<div class="z-20 font-bold">{newPrice}</div>
|
|
33
|
+
{/if}
|
|
34
|
+
</div>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
newPrice?: string | number;
|
|
3
|
+
oldPrice?: string | number;
|
|
4
|
+
type?: "start" | "normal";
|
|
5
|
+
};
|
|
6
|
+
declare const CardStartPrice: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type CardStartPrice = ReturnType<typeof CardStartPrice>;
|
|
8
|
+
export default CardStartPrice;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { HugeiconsIcon } from "@hugeicons/svelte";
|
|
3
3
|
import type { CardTagsProps } from "../types";
|
|
4
|
+
import Skeleton from "../../../ui/skeleton/skeleton.svelte";
|
|
4
5
|
|
|
5
6
|
type Props = {
|
|
6
7
|
className?: string;
|
|
7
|
-
tags
|
|
8
|
+
tags?: CardTagsProps[];
|
|
8
9
|
isTagBorderBottom?: boolean;
|
|
9
10
|
isWrap?: boolean;
|
|
11
|
+
isLoading?: boolean;
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
const {
|
|
@@ -14,9 +16,10 @@
|
|
|
14
16
|
className,
|
|
15
17
|
isTagBorderBottom,
|
|
16
18
|
isWrap = false,
|
|
19
|
+
isLoading = false,
|
|
17
20
|
}: Props = $props();
|
|
18
21
|
|
|
19
|
-
let isJustify = $derived(tags
|
|
22
|
+
let isJustify = $derived(tags?.length == 2 || tags?.length == 3);
|
|
20
23
|
</script>
|
|
21
24
|
|
|
22
25
|
{#snippet createBadge(tag: CardTagsProps)}
|
|
@@ -35,22 +38,36 @@
|
|
|
35
38
|
</div>
|
|
36
39
|
{/snippet}
|
|
37
40
|
|
|
38
|
-
{#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
{#snippet skeletonTag()}
|
|
42
|
+
<div class="my-2 flex gap-5">
|
|
43
|
+
{#each Array.from({ length: 3 }) as it, i (i)}
|
|
44
|
+
<Skeleton class="h-5 w-25 bg-black/50" />
|
|
45
|
+
{/each}
|
|
46
|
+
</div>
|
|
47
|
+
{/snippet}
|
|
48
|
+
|
|
49
|
+
{#if isWrap}
|
|
50
|
+
<div class="flex flex-wrap gap-2 md:gap-5 {className}">
|
|
51
|
+
{#if isLoading}
|
|
52
|
+
{@render skeletonTag()}
|
|
53
|
+
{:else if tags && tags.length > 0}
|
|
41
54
|
{#each tags as tag, i (i)}
|
|
42
|
-
|
|
55
|
+
{@render createBadge(tag)}
|
|
43
56
|
{/each}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
{/if}
|
|
58
|
+
</div>
|
|
59
|
+
{:else}
|
|
60
|
+
<div
|
|
61
|
+
class="flex overflow-x-auto no-scrollbar w-full
|
|
47
62
|
{isJustify ? 'justify-between' : 'gap-4 md:gap-5 lg:gap-10'}
|
|
48
|
-
{className}"
|
|
63
|
+
{className}"
|
|
64
|
+
>
|
|
65
|
+
{#if isLoading}
|
|
66
|
+
{@render skeletonTag()}
|
|
67
|
+
{:else if tags && tags.length > 0}
|
|
49
68
|
{#each tags as tag, i (i)}
|
|
50
|
-
|
|
69
|
+
{@render createBadge(tag)}
|
|
51
70
|
{/each}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
{:else}
|
|
55
|
-
<div class="w-full h-5 {className}"></div>
|
|
71
|
+
{/if}
|
|
72
|
+
</div>
|
|
56
73
|
{/if}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { CardTagsProps } from "../types";
|
|
2
2
|
type Props = {
|
|
3
3
|
className?: string;
|
|
4
|
-
tags
|
|
4
|
+
tags?: CardTagsProps[];
|
|
5
5
|
isTagBorderBottom?: boolean;
|
|
6
6
|
isWrap?: boolean;
|
|
7
|
+
isLoading?: boolean;
|
|
7
8
|
};
|
|
8
9
|
declare const CardTags: import("svelte").Component<Props, {}, "">;
|
|
9
10
|
type CardTags = ReturnType<typeof CardTags>;
|
|
@@ -47,13 +47,17 @@ export interface CardPromotionProps {
|
|
|
47
47
|
logo?: string;
|
|
48
48
|
imageUrl?: string;
|
|
49
49
|
title?: string;
|
|
50
|
+
content?: string;
|
|
50
51
|
className?: string;
|
|
51
52
|
isCart?: boolean;
|
|
52
|
-
isFavorite?: boolean;
|
|
53
53
|
isLoading?: boolean;
|
|
54
|
+
isFavorite?: boolean;
|
|
55
|
+
tags?: CardTagsProps[];
|
|
54
56
|
oldPrice?: string | number;
|
|
55
57
|
newPrice?: string | number;
|
|
56
58
|
buttonBuyText?: string;
|
|
59
|
+
isDescriptionIcon?: boolean;
|
|
60
|
+
isDescriptionLabel?: boolean;
|
|
57
61
|
onClickBuy?: (id: string | number) => void;
|
|
58
62
|
onClickShop?: (id: string | number) => void;
|
|
59
63
|
onClickFavorite?: (id: string | number) => void;
|
|
@@ -65,4 +69,12 @@ export interface CardHighlightProps {
|
|
|
65
69
|
title?: string;
|
|
66
70
|
className?: string;
|
|
67
71
|
isFavorite?: boolean;
|
|
72
|
+
isLoading?: boolean;
|
|
73
|
+
content?: string;
|
|
74
|
+
buttonText?: string;
|
|
75
|
+
isDescriptionIcon?: boolean;
|
|
76
|
+
isDescriptionLabel?: boolean;
|
|
77
|
+
tags?: CardTagsProps[];
|
|
78
|
+
onClickFavorite?: (id: string | number) => void;
|
|
79
|
+
onClickBtn?: (id: string | number) => void;
|
|
68
80
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { HugeiconsIcon } from "@hugeicons/svelte";
|
|
5
5
|
import CarouselSlot from "../../../panel/CarouselSlot.svelte";
|
|
6
6
|
import Skeleton from "../../../../ui/skeleton/skeleton.svelte";
|
|
7
|
+
import { useDevice } from "../../../../../hooks/responsive.svelte";
|
|
7
8
|
|
|
8
9
|
const {
|
|
9
10
|
items,
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
}: CarouselBadgeProps = $props();
|
|
23
24
|
|
|
24
25
|
const DEFAULT_IMG_OR_ICON_CLASS = "size-3";
|
|
26
|
+
const responsive = useDevice();
|
|
25
27
|
</script>
|
|
26
28
|
|
|
27
29
|
<CarouselSlot
|
|
@@ -30,11 +32,12 @@
|
|
|
30
32
|
{buttonPreviousAndNextClass}
|
|
31
33
|
>
|
|
32
34
|
{#if isLoading}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<Skeleton class="h-4 w-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
<div class="flex items-center justify-between w-full gap-1 md:gap-2">
|
|
36
|
+
{#each Array.from({ length: responsive.isMobile ? 2 : 10 }) as it, i (i)}
|
|
37
|
+
<Skeleton class="h-4 w-25 bg-black/20" />
|
|
38
|
+
<div class="src-only">{it}</div>
|
|
39
|
+
{/each}
|
|
40
|
+
</div>
|
|
38
41
|
{:else}
|
|
39
42
|
{#each items as item, i (i)}
|
|
40
43
|
<Carousel.Item
|
|
@@ -2,8 +2,11 @@ import type { CardHighlightProps } from "../../card/types";
|
|
|
2
2
|
import type { CarouselHeaderProps } from "../types";
|
|
3
3
|
export interface CarouselHighlightsProps extends CarouselHeaderProps {
|
|
4
4
|
items: CardHighlightProps[];
|
|
5
|
+
varient?: 1 | 2;
|
|
5
6
|
isBorderBottom?: boolean;
|
|
6
7
|
isLoading?: boolean;
|
|
8
|
+
isDescriptionIcon?: boolean;
|
|
9
|
+
isDescriptionLabel?: boolean;
|
|
7
10
|
isButtonPreviousAndNext?: boolean;
|
|
8
11
|
buttonPreviousAndNextClass?: string;
|
|
9
12
|
}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import CardHighlight from "../../../card/highlight/CardHighlight.svelte";
|
|
3
3
|
import CarouselHeader from "../../shared/ui/CarouselHeader.svelte";
|
|
4
4
|
import CarouselSlot from "../../../panel/CarouselSlot.svelte";
|
|
5
|
+
import * as Carousel from "../../../../ui/carousel/index.js";
|
|
5
6
|
import type { CarouselHighlightsProps } from "../types";
|
|
7
|
+
import { useDevice } from "../../../../../hooks/responsive.svelte";
|
|
8
|
+
import Autoplay from "embla-carousel-autoplay";
|
|
6
9
|
|
|
7
10
|
const {
|
|
8
11
|
title,
|
|
@@ -14,8 +17,13 @@
|
|
|
14
17
|
buttonPreviousAndNextClass = "",
|
|
15
18
|
containerClass = "",
|
|
16
19
|
isLoading = false,
|
|
20
|
+
varient = 1,
|
|
21
|
+
isDescriptionIcon,
|
|
22
|
+
isDescriptionLabel,
|
|
17
23
|
onButtonViewAll,
|
|
18
24
|
}: CarouselHighlightsProps = $props();
|
|
25
|
+
|
|
26
|
+
const responsive = useDevice();
|
|
19
27
|
</script>
|
|
20
28
|
|
|
21
29
|
<CarouselHeader
|
|
@@ -31,11 +39,25 @@
|
|
|
31
39
|
{isButtonPreviousAndNext}
|
|
32
40
|
{buttonPreviousAndNextClass}
|
|
33
41
|
containerClass="w-full"
|
|
42
|
+
plugins={[Autoplay({ delay: 4000, stopOnInteraction: true })]}
|
|
34
43
|
>
|
|
35
|
-
{#
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
{#if isLoading}
|
|
45
|
+
{#each Array.from( { length: responsive.isMobile ? 5 : 10 }, ) as _, i (`loading-${i}`)}
|
|
46
|
+
<Carousel.Item class="pl-2 md:basis-1/2 lg:basis-auto">
|
|
47
|
+
<CardHighlight id={i} {isLoading} {varient} />
|
|
48
|
+
</Carousel.Item>
|
|
49
|
+
{/each}
|
|
50
|
+
{:else}
|
|
51
|
+
{#each items as item (item.id)}
|
|
52
|
+
<Carousel.Item class="pl-2 md:basis-1/2 lg:basis-auto">
|
|
53
|
+
<CardHighlight
|
|
54
|
+
{...item}
|
|
55
|
+
{varient}
|
|
56
|
+
{isDescriptionIcon}
|
|
57
|
+
{isDescriptionLabel}
|
|
58
|
+
/>
|
|
59
|
+
</Carousel.Item>
|
|
60
|
+
{/each}
|
|
61
|
+
{/if}
|
|
40
62
|
</CarouselSlot>
|
|
41
63
|
</CarouselHeader>
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import CardPromotion from "../../../card/promotion/CardPromotion.svelte";
|
|
3
3
|
import CarouselSlot from "../../../panel/CarouselSlot.svelte";
|
|
4
|
+
import * as Carousel from "../../../../ui/carousel/index.js";
|
|
4
5
|
import type { CarouselPromotionProps } from "../types";
|
|
5
6
|
import CarouselHeader from "../../shared/ui/CarouselHeader.svelte";
|
|
7
|
+
import { useDevice } from "../../../../../hooks/responsive.svelte";
|
|
8
|
+
import Autoplay from "embla-carousel-autoplay";
|
|
6
9
|
|
|
7
10
|
const {
|
|
8
11
|
title,
|
|
@@ -14,8 +17,13 @@
|
|
|
14
17
|
isLoading = false,
|
|
15
18
|
buttonPreviousAndNextClass = "",
|
|
16
19
|
containerClass = "",
|
|
20
|
+
positionButtonPreviousAndNext,
|
|
21
|
+
isDescriptionIcon,
|
|
22
|
+
isDescriptionLabel,
|
|
17
23
|
onButtonViewAll,
|
|
18
24
|
}: CarouselPromotionProps = $props();
|
|
25
|
+
|
|
26
|
+
const responsive = useDevice();
|
|
19
27
|
</script>
|
|
20
28
|
|
|
21
29
|
<CarouselHeader
|
|
@@ -30,12 +38,27 @@
|
|
|
30
38
|
isBorderBottom={false}
|
|
31
39
|
{isButtonPreviousAndNext}
|
|
32
40
|
{buttonPreviousAndNextClass}
|
|
41
|
+
{positionButtonPreviousAndNext}
|
|
33
42
|
containerClass="w-full"
|
|
43
|
+
plugins={[Autoplay({ delay: 4000, stopOnInteraction: true })]}
|
|
34
44
|
>
|
|
35
|
-
{#
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
{#if isLoading}
|
|
46
|
+
{#each Array.from( { length: responsive.isMobile ? 5 : 10 }, ) as _, i (`loading-${i}`)}
|
|
47
|
+
<Carousel.Item class="pl-2 md:basis-1/2 lg:basis-1/4">
|
|
48
|
+
<CardPromotion
|
|
49
|
+
id={i}
|
|
50
|
+
{isLoading}
|
|
51
|
+
{isDescriptionIcon}
|
|
52
|
+
{isDescriptionLabel}
|
|
53
|
+
/>
|
|
54
|
+
</Carousel.Item>
|
|
55
|
+
{/each}
|
|
56
|
+
{:else}
|
|
57
|
+
{#each items as item (item.id)}
|
|
58
|
+
<Carousel.Item class="pl-2 md:basis-1/2 lg:basis-1/4">
|
|
59
|
+
<CardPromotion {...item} {isDescriptionIcon} {isDescriptionLabel} />
|
|
60
|
+
</Carousel.Item>
|
|
61
|
+
{/each}
|
|
62
|
+
{/if}
|
|
40
63
|
</CarouselSlot>
|
|
41
64
|
</CarouselHeader>
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
import { HugeiconsIcon } from "@hugeicons/svelte";
|
|
5
5
|
import { ArrowRight02Icon } from "@hugeicons/core-free-icons";
|
|
6
6
|
import type { CarouselHeaderProps } from "../../types";
|
|
7
|
+
import type { Snippet } from "svelte";
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
children: Snippet;
|
|
11
|
+
};
|
|
7
12
|
|
|
8
13
|
const {
|
|
9
14
|
title,
|
|
@@ -13,19 +18,19 @@
|
|
|
13
18
|
containerClass = "",
|
|
14
19
|
onButtonViewAll,
|
|
15
20
|
children,
|
|
16
|
-
}: CarouselHeaderProps = $props();
|
|
21
|
+
}: CarouselHeaderProps & Props = $props();
|
|
17
22
|
</script>
|
|
18
23
|
|
|
19
24
|
<div class="w-full flex justify-center">
|
|
20
|
-
<div class="w-[95%] p-2 rounded-xl {containerClass}">
|
|
21
|
-
<div class="
|
|
25
|
+
<div class="w-[95%] p-2 rounded-xl border {containerClass}">
|
|
26
|
+
<div class="flex flex-col gap-1">
|
|
22
27
|
<div class="flex justify-between">
|
|
23
28
|
<div>
|
|
24
29
|
{#if title}
|
|
25
30
|
<h1 class={`text-xl ${titleClass}`}>{title}</h1>
|
|
26
31
|
{/if}
|
|
27
32
|
{#if description}
|
|
28
|
-
<div class={`mt-3
|
|
33
|
+
<div class={`mt-3 ${descriptionClass}`}>
|
|
29
34
|
{description}
|
|
30
35
|
</div>
|
|
31
36
|
{/if}
|
|
@@ -45,6 +50,6 @@
|
|
|
45
50
|
{/if}
|
|
46
51
|
</div>
|
|
47
52
|
</div>
|
|
53
|
+
<div>{@render children()}</div>
|
|
48
54
|
</div>
|
|
49
|
-
<div>{@render children()}</div>
|
|
50
55
|
</div>
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { CarouselHeaderProps } from "../../types";
|
|
2
|
-
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
type Props = {
|
|
4
|
+
children: Snippet;
|
|
5
|
+
};
|
|
6
|
+
type $$ComponentProps = CarouselHeaderProps & Props;
|
|
7
|
+
declare const CarouselHeader: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
3
8
|
type CarouselHeader = ReturnType<typeof CarouselHeader>;
|
|
4
9
|
export default CarouselHeader;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { IconSvgElement } from "@hugeicons/svelte";
|
|
2
2
|
import type { NavMenuProps } from "../nav/data/nav-menu";
|
|
3
|
-
import type { Snippet } from "svelte";
|
|
4
3
|
export interface ItemCarousel {
|
|
5
4
|
value: string;
|
|
6
5
|
label: string;
|
|
@@ -17,7 +16,7 @@ export interface CarouselHeaderProps {
|
|
|
17
16
|
descriptionClass?: string;
|
|
18
17
|
containerClass?: string;
|
|
19
18
|
onButtonViewAll?: () => void;
|
|
20
|
-
|
|
19
|
+
positionButtonPreviousAndNext?: "center" | "top_right";
|
|
21
20
|
}
|
|
22
21
|
export interface CarouselBadgeProps {
|
|
23
22
|
items: ItemCarousel[];
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import LanguageSwitcher from "../../../ui/language-switcher/language-switcher.svelte";
|
|
7
7
|
import { t } from "../../../../i18n";
|
|
8
8
|
import { Login03Icon, User03Icon } from "@hugeicons/core-free-icons";
|
|
9
|
-
import {
|
|
9
|
+
import { useDevice } from "../../../../hooks/responsive.svelte";
|
|
10
10
|
import NavMenuDrawer from "./mobile/nav-menu-drawer.svelte";
|
|
11
11
|
import MenuLinks from "./menu-links.svelte";
|
|
12
12
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
onclickButtonRegister,
|
|
26
26
|
}: NavMenuProps = $props();
|
|
27
27
|
|
|
28
|
-
const responsive =
|
|
28
|
+
const responsive = useDevice();
|
|
29
29
|
</script>
|
|
30
30
|
|
|
31
31
|
{#snippet actionButtons()}
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
{#if isLanguageSwitcher}
|
|
37
37
|
<LanguageSwitcher />
|
|
38
38
|
{/if}
|
|
39
|
-
{#each
|
|
39
|
+
{#each actions ?? [] as item (item.label)}
|
|
40
40
|
<Button
|
|
41
41
|
variant={item.type === "LINK" ? "ghost" : "outline"}
|
|
42
42
|
class={item.className}
|
|
@@ -1,38 +1,63 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as Carousel from "../../ui/carousel/index.js";
|
|
3
3
|
import type { Snippet } from "svelte";
|
|
4
|
+
import type { CarouselPlugins } from "../../ui/carousel/context.js";
|
|
4
5
|
|
|
5
6
|
type Props = {
|
|
6
|
-
|
|
7
|
-
isButtonPreviousAndNext?: boolean;
|
|
7
|
+
positionButtonPreviousAndNext?: "center" | "top_right";
|
|
8
8
|
buttonPreviousAndNextClass?: string;
|
|
9
|
+
isButtonPreviousAndNext?: boolean;
|
|
10
|
+
isBorderBottom?: boolean;
|
|
9
11
|
containerClass?: string;
|
|
12
|
+
plugins?: CarouselPlugins;
|
|
10
13
|
children: Snippet;
|
|
11
14
|
};
|
|
12
15
|
|
|
13
16
|
const {
|
|
14
|
-
|
|
17
|
+
positionButtonPreviousAndNext = "center",
|
|
15
18
|
isButtonPreviousAndNext = true,
|
|
16
19
|
buttonPreviousAndNextClass = "",
|
|
20
|
+
isBorderBottom = true,
|
|
17
21
|
containerClass = "",
|
|
22
|
+
plugins = [],
|
|
18
23
|
children,
|
|
19
24
|
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
const styleTopCenter = "bg-blue-700! text-white! cursor-pointer!";
|
|
27
|
+
const styleCenter =
|
|
28
|
+
"absolute top-1/2 -translate-y-1/2 mt-0.5 mr-0 z-50 bg-blue-700! text-white! cursor-pointer!";
|
|
20
29
|
</script>
|
|
21
30
|
|
|
22
31
|
<div
|
|
23
32
|
class={`relative py-3 px-2 ${isBorderBottom ? "border-b" : ""} ${containerClass}`}
|
|
24
33
|
>
|
|
25
|
-
<Carousel.Root>
|
|
26
|
-
<Carousel.Content
|
|
34
|
+
<Carousel.Root {plugins}>
|
|
35
|
+
<Carousel.Content
|
|
36
|
+
class={positionButtonPreviousAndNext == "top_right" ? "" : "ml-0"}
|
|
37
|
+
>
|
|
27
38
|
{@render children()}
|
|
28
39
|
</Carousel.Content>
|
|
29
|
-
{#if
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
{#if positionButtonPreviousAndNext == "center"}
|
|
41
|
+
{#if isButtonPreviousAndNext}
|
|
42
|
+
<Carousel.Previous
|
|
43
|
+
class={`-left-8 ${styleCenter} ${buttonPreviousAndNextClass}`}
|
|
44
|
+
/>
|
|
45
|
+
<Carousel.Next
|
|
46
|
+
class={`-right-8 ${styleCenter} ${buttonPreviousAndNextClass}`}
|
|
47
|
+
/>
|
|
48
|
+
{/if}
|
|
49
|
+
{/if}
|
|
50
|
+
{#if positionButtonPreviousAndNext == "top_right"}
|
|
51
|
+
{#if isButtonPreviousAndNext}
|
|
52
|
+
<div class="absolute -top-6 right-10 md:right-10 lg:right-12 z-20">
|
|
53
|
+
<Carousel.Previous
|
|
54
|
+
class={`${styleTopCenter} ${buttonPreviousAndNextClass}`}
|
|
55
|
+
/>
|
|
56
|
+
<Carousel.Next
|
|
57
|
+
class={`${styleTopCenter} ${buttonPreviousAndNextClass}`}
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
{/if}
|
|
36
61
|
{/if}
|
|
37
62
|
</Carousel.Root>
|
|
38
63
|
</div>
|