negodesign 0.0.28 → 0.0.30

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.
Files changed (79) hide show
  1. package/dist/assets/lonely-404.mp4 +0 -0
  2. package/dist/components/core/banner/AdCardBanner.svelte +74 -0
  3. package/dist/components/core/banner/AdCardBanner.svelte.d.ts +4 -0
  4. package/dist/components/core/banner/CtaCardBanner.svelte +118 -0
  5. package/dist/components/core/banner/CtaCardBanner.svelte.d.ts +4 -0
  6. package/dist/components/core/banner/NotificationBanner.svelte +95 -0
  7. package/dist/components/core/banner/NotificationBanner.svelte.d.ts +4 -0
  8. package/dist/components/core/banner/types.d.ts +63 -0
  9. package/dist/components/core/banner/types.js +1 -0
  10. package/dist/components/core/card/highlight/01/CardHighlight01.svelte +20 -27
  11. package/dist/components/core/card/highlight/02/CardHighlight02.svelte +18 -27
  12. package/dist/components/core/card/highlight/data.js +1 -4
  13. package/dist/components/core/card/media/01/CardMedia01.svelte +114 -98
  14. package/dist/components/core/card/media/02/CardMedia02.svelte +115 -99
  15. package/dist/components/core/card/media/CardMedia.svelte +10 -9
  16. package/dist/components/core/card/media/CardMedia.svelte.d.ts +1 -0
  17. package/dist/components/core/card/profile/01/CardProfile01.svelte +104 -86
  18. package/dist/components/core/card/profile/02/CardProfile02.svelte +103 -87
  19. package/dist/components/core/card/profile/CardProfile.svelte +8 -7
  20. package/dist/components/core/card/profile/CardProfile.svelte.d.ts +1 -0
  21. package/dist/components/core/card/promotion/CardPromotion.svelte +10 -4
  22. package/dist/components/core/card/shared/CardButton.svelte +38 -23
  23. package/dist/components/core/card/shared/CardButton.svelte.d.ts +2 -1
  24. package/dist/components/core/card/shared/CardDescription.svelte +30 -30
  25. package/dist/components/core/card/shared/CardLogo.svelte +19 -0
  26. package/dist/components/core/card/shared/CardLogo.svelte.d.ts +7 -0
  27. package/dist/components/core/card/shared/CardStarRating.svelte +18 -14
  28. package/dist/components/core/card/shared/CardStarRating.svelte.d.ts +1 -0
  29. package/dist/components/core/card/shared/CardStartPrice.svelte +1 -1
  30. package/dist/components/core/card/shared/CardThumbnailVideo.svelte +210 -148
  31. package/dist/components/core/card/shared/CardThumbnailVideo.svelte.d.ts +1 -0
  32. package/dist/components/core/card/types.d.ts +5 -0
  33. package/dist/components/core/carousel/grid/media/types.d.ts +11 -0
  34. package/dist/components/core/carousel/grid/media/types.js +1 -0
  35. package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte +49 -0
  36. package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte.d.ts +4 -0
  37. package/dist/components/core/carousel/grid/profile/types.d.ts +11 -0
  38. package/dist/components/core/carousel/grid/profile/types.js +1 -0
  39. package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte +49 -0
  40. package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte.d.ts +4 -0
  41. package/dist/components/core/carousel/grid/shared/ui/CarouselGridSlot.svelte +33 -0
  42. package/dist/components/core/carousel/grid/shared/ui/CarouselGridSlot.svelte.d.ts +11 -0
  43. package/dist/components/core/carousel/hero/CarouselHero.svelte +21 -19
  44. package/dist/components/core/carousel/hero/CarouselHero.svelte.d.ts +1 -0
  45. package/dist/components/core/carousel/highlights/types.d.ts +4 -4
  46. package/dist/components/core/carousel/highlights/ui/CarouselHighlights.svelte +4 -22
  47. package/dist/components/core/carousel/promotion/types.d.ts +5 -5
  48. package/dist/components/core/carousel/promotion/ui/CarouselPromotion.svelte +4 -21
  49. package/dist/components/core/carousel/shared/ui/CarouselHeader.svelte +6 -1
  50. package/dist/components/core/carousel/types.d.ts +1 -0
  51. package/dist/components/core/footer/types.d.ts +23 -0
  52. package/dist/components/core/footer/types.js +1 -0
  53. package/dist/components/core/footer/ui/01/Footer01.svelte +76 -0
  54. package/dist/components/core/footer/ui/01/Footer01.svelte.d.ts +4 -0
  55. package/dist/components/core/footer/ui/02/Footer02.svelte +53 -0
  56. package/dist/components/core/footer/ui/02/Footer02.svelte.d.ts +4 -0
  57. package/dist/components/core/footer/ui/Footer.svelte +18 -0
  58. package/dist/components/core/footer/ui/Footer.svelte.d.ts +7 -0
  59. package/dist/components/core/footer/ui/shared/FooterColumn.svelte +18 -0
  60. package/dist/components/core/footer/ui/shared/FooterColumn.svelte.d.ts +4 -0
  61. package/dist/components/core/footer/ui/shared/FooterNewsLetter.svelte +57 -0
  62. package/dist/components/core/footer/ui/shared/FooterNewsLetter.svelte.d.ts +9 -0
  63. package/dist/components/core/footer/ui/shared/FooterSocialIcon.svelte +62 -0
  64. package/dist/components/core/footer/ui/shared/FooterSocialIcon.svelte.d.ts +4 -0
  65. package/dist/components/core/panel/CarouselSlot.svelte +4 -15
  66. package/dist/components/core/panel/CarouselSlot.svelte.d.ts +2 -12
  67. package/dist/components/core/panel/LeftHero.svelte +2 -2
  68. package/dist/components/core/panel/type.d.ts +11 -0
  69. package/dist/components/core/panel/type.js +1 -0
  70. package/dist/globals.css +463 -47
  71. package/dist/i18n/langs/en.d.ts +4 -0
  72. package/dist/i18n/langs/en.js +4 -0
  73. package/dist/i18n/langs/pt.d.ts +4 -0
  74. package/dist/i18n/langs/pt.js +10 -6
  75. package/dist/i18n/translations.d.ts +8 -0
  76. package/dist/index.d.ts +17 -5
  77. package/dist/index.js +17 -5
  78. package/dist/styles/globals.css +1 -1
  79. package/package.json +1 -1
@@ -1,20 +1,24 @@
1
1
  <script lang="ts">
2
- import * as StarRating from "../../../ui/star-rating";
2
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
3
+ import * as StarRating from "../../../ui/star-rating";
3
4
 
4
- type Props = {
5
- startNumber: number;
6
- startMax: number;
7
- };
5
+ type Props = {
6
+ startNumber: number;
7
+ startMax: number;
8
+ isLoading?: boolean;
9
+ };
8
10
 
9
- let { startNumber, startMax }: Props = $props();
11
+ let { startNumber, startMax, isLoading }: Props = $props();
10
12
  </script>
11
13
 
12
- {#if startNumber}
13
- <StarRating.Root max={startMax} value={startNumber} class="size-15 h-auto">
14
- {#snippet children({ items })}
15
- {#each items as item (item.index)}
16
- <StarRating.Star {...item} class="py-0! my-0!" />
17
- {/each}
18
- {/snippet}
19
- </StarRating.Root>
14
+ {#if isLoading}
15
+ <Skeleton class="w-12 h-6 rounded-lg bg-gray-300/90" />
16
+ {:else if startNumber}
17
+ <StarRating.Root max={startMax} value={startNumber} class="size-15 h-auto">
18
+ {#snippet children({ items })}
19
+ {#each items as item (item.index)}
20
+ <StarRating.Star {...item} class="py-0! my-0!" />
21
+ {/each}
22
+ {/snippet}
23
+ </StarRating.Root>
20
24
  {/if}
@@ -1,6 +1,7 @@
1
1
  type Props = {
2
2
  startNumber: number;
3
3
  startMax: number;
4
+ isLoading?: boolean;
4
5
  };
5
6
  declare const CardStarRating: import("svelte").Component<Props, {}, "">;
6
7
  type CardStarRating = ReturnType<typeof CardStarRating>;
@@ -10,7 +10,7 @@
10
10
  let { newPrice, oldPrice, type = "normal" }: Props = $props();
11
11
  </script>
12
12
 
13
- <div class="relative min-w-25 md:min-w-32">
13
+ <div class="relative min-w-25 md:min-w-32 ml-0 pl-0">
14
14
  {#if type == "start"}
15
15
  <img
16
16
  src={StartBg}
@@ -1,170 +1,232 @@
1
1
  <!-- CardMedia.svelte -->
2
2
  <script lang="ts">
3
- /**
4
- * Image thumbnail with hover-to-play video, lazy loading,
5
- * duration badge, video indicator, and optional maximize overlay.
6
- * @component
7
- */
8
- import { HugeiconsIcon } from "@hugeicons/svelte";
9
- import { Maximize01Icon, Cancel01Icon, VideoReplayIcon } from "@hugeicons/core-free-icons";
10
-
11
- type Props = {
12
- imageUrl?: string;
13
- videoUrl?: string;
14
- class?: string;
15
- isImageButtonMaximized?: boolean;
16
- isVideoButtonMaximized?: boolean;
17
- };
18
-
19
- let {
20
- imageUrl,
21
- videoUrl,
22
- class: className = "w-64 h-64",
23
- isImageButtonMaximized = false,
24
- isVideoButtonMaximized = false,
25
- }: Props = $props();
26
-
27
- let containerEl: HTMLDivElement | undefined = $state();
28
- let videoEl: HTMLVideoElement | undefined = $state();
29
-
30
- let isHovering = $state(false);
31
- let isVisible = $state(false);
32
- let isMaximized = $state(false);
33
- let duration = $state("");
34
-
35
- let showVideo = $derived(!imageUrl || isHovering);
36
- let showMaximize = $derived((imageUrl && isImageButtonMaximized) || (videoUrl && isVideoButtonMaximized));
37
- let showVideoBadge = $derived(!!imageUrl && !!videoUrl && !isHovering);
38
-
39
- function onLoadedMetadata() {
40
- if (!videoEl) return;
41
- const m = Math.floor(videoEl.duration / 60);
42
- const s = Math.floor(videoEl.duration % 60);
43
- duration = `${m}:${s.toString().padStart(2, "0")}`;
3
+ /**
4
+ * Image thumbnail with hover-to-play video, lazy loading,
5
+ * duration badge, video indicator, and optional maximize overlay.
6
+ * @component
7
+ */
8
+ import { HugeiconsIcon } from "@hugeicons/svelte";
9
+ import {
10
+ Maximize01Icon,
11
+ Cancel01Icon,
12
+ VideoReplayIcon,
13
+ } from "@hugeicons/core-free-icons";
14
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
15
+ import ImgPlaceholder from "../../../../assets/placeholder-image.png";
16
+ import VideoPlaceholder from "../../../../assets/lonely-404.mp4";
17
+
18
+ type Props = {
19
+ imageUrl?: string;
20
+ videoUrl?: string;
21
+ class?: string;
22
+ isImageButtonMaximized?: boolean;
23
+ isVideoButtonMaximized?: boolean;
24
+ isLoading?: boolean;
25
+ };
26
+
27
+ let {
28
+ imageUrl,
29
+ videoUrl,
30
+ class: className = "w-64 h-64",
31
+ isImageButtonMaximized = false,
32
+ isVideoButtonMaximized = false,
33
+ isLoading = false,
34
+ }: Props = $props();
35
+
36
+ let containerEl: HTMLDivElement | undefined = $state();
37
+ let videoEl: HTMLVideoElement | undefined = $state();
38
+
39
+ let isHovering = $state(false);
40
+ let isVisible = $state(false);
41
+ let isMaximized = $state(false);
42
+ let duration = $state("");
43
+
44
+ let showVideo = $derived(!imageUrl || isHovering);
45
+ let showMaximize = $derived(
46
+ (imageUrl && isImageButtonMaximized) ||
47
+ (videoUrl && isVideoButtonMaximized),
48
+ );
49
+ let showVideoBadge = $derived(!!imageUrl && !!videoUrl && !isHovering);
50
+
51
+ function onLoadedMetadata() {
52
+ if (!videoEl) return;
53
+ const m = Math.floor(videoEl.duration / 60);
54
+ const s = Math.floor(videoEl.duration % 60);
55
+ duration = `${m}:${s.toString().padStart(2, "0")}`;
56
+ }
57
+
58
+ function toggleHover(state: boolean) {
59
+ if (!videoUrl) return;
60
+ isHovering = state;
61
+ if (state) videoEl?.play();
62
+ else {
63
+ videoEl?.pause();
64
+ if (imageUrl && videoEl) videoEl.currentTime = 0;
44
65
  }
45
-
46
- function toggleHover(state: boolean) {
47
- if (!videoUrl) return;
48
- isHovering = state;
49
- if (state) videoEl?.play();
50
- else {
51
- videoEl?.pause();
52
- if (imageUrl && videoEl) videoEl.currentTime = 0;
66
+ }
67
+
68
+ $effect(() => {
69
+ if (!containerEl) return;
70
+ const observer = new IntersectionObserver(
71
+ ([entry]) => {
72
+ if (entry.isIntersecting) {
73
+ isVisible = true;
74
+ observer.disconnect();
53
75
  }
54
- }
55
-
56
- $effect(() => {
57
- if (!containerEl) return;
58
- const observer = new IntersectionObserver(
59
- ([entry]) => {
60
- if (entry.isIntersecting) {
61
- isVisible = true;
62
- observer.disconnect();
63
- }
64
- },
65
- { rootMargin: "100px" }
66
- );
67
- observer.observe(containerEl);
68
- return () => observer.disconnect();
69
- });
76
+ },
77
+ { rootMargin: "100px" },
78
+ );
79
+ observer.observe(containerEl);
80
+ return () => observer.disconnect();
81
+ });
70
82
  </script>
71
83
 
72
- <svelte:window onkeydown={isMaximized ? (e) => e.key === "Escape" && (isMaximized = false) : undefined} />
84
+ <svelte:window
85
+ onkeydown={isMaximized
86
+ ? (e) => e.key === "Escape" && (isMaximized = false)
87
+ : undefined}
88
+ />
73
89
 
74
90
  {#snippet maximizeButton()}
75
- <button
76
- type="button"
77
- class="absolute top-2 right-2 z-10 rounded-full bg-black/50 p-1.5 text-white transition-colors hover:bg-black/70"
78
- onclick={(e) => { e.stopPropagation(); isMaximized = true; videoEl?.pause(); }}
79
- aria-label="Maximizar"
80
- >
81
- <HugeiconsIcon icon={Maximize01Icon} size={16} color="currentColor" strokeWidth={1.5} />
82
- </button>
91
+ <button
92
+ type="button"
93
+ class="absolute top-2 right-2 z-10 rounded-full bg-black/50 p-1.5 text-white transition-colors hover:bg-black/70"
94
+ onclick={(e) => {
95
+ e.stopPropagation();
96
+ isMaximized = true;
97
+ videoEl?.pause();
98
+ }}
99
+ aria-label="Maximizar"
100
+ >
101
+ <HugeiconsIcon
102
+ icon={Maximize01Icon}
103
+ size={16}
104
+ color="currentColor"
105
+ strokeWidth={1.5}
106
+ />
107
+ </button>
83
108
  {/snippet}
84
109
 
85
- {#if imageUrl || videoUrl}
110
+ {#if isLoading}
111
+ <Skeleton class="w-full h-12.5 md:h-50 bg-gray-500/90" />
112
+ {:else}
113
+ {#if imageUrl || videoUrl}
86
114
  <div
87
- bind:this={containerEl}
88
- class="relative w-full {className}"
89
- role="button"
90
- tabindex="0"
91
- onmouseenter={() => toggleHover(true)}
92
- onmouseleave={() => toggleHover(false)}
93
- onfocus={() => toggleHover(true)}
94
- onblur={() => toggleHover(false)}
95
- onkeydown={(e) => (e.key === "Enter" || e.key === " ") && (e.preventDefault(), toggleHover(!isHovering))}
115
+ bind:this={containerEl}
116
+ class="relative w-full {className}"
117
+ role="button"
118
+ tabindex="0"
119
+ onmouseenter={() => toggleHover(true)}
120
+ onmouseleave={() => toggleHover(false)}
121
+ onfocus={() => toggleHover(true)}
122
+ onblur={() => toggleHover(false)}
123
+ onkeydown={(e) =>
124
+ (e.key === "Enter" || e.key === " ") &&
125
+ (e.preventDefault(), toggleHover(!isHovering))}
96
126
  >
97
- {#if imageUrl}
98
- <img
99
- src={isVisible ? imageUrl : undefined}
100
- loading="lazy"
101
- decoding="async"
102
- alt={imageUrl}
103
- class="absolute inset-0 w-full {className} rounded-md object-cover transition-opacity duration-500"
104
- class:opacity-0={videoUrl && showVideo}
127
+ {#if imageUrl}
128
+ <img
129
+ src={isVisible ? imageUrl : undefined}
130
+ loading="lazy"
131
+ decoding="async"
132
+ alt={imageUrl}
133
+ class="absolute inset-0 w-full {className} rounded-md object-cover transition-opacity duration-500"
134
+ class:opacity-0={videoUrl && showVideo}
135
+ onerror={(e) => ((e.target as HTMLImageElement).src = ImgPlaceholder)}
136
+ />
137
+ {/if}
138
+
139
+ {#if videoUrl && isVisible}
140
+ <video
141
+ bind:this={videoEl}
142
+ src={videoUrl}
143
+ preload="metadata"
144
+ onloadedmetadata={onLoadedMetadata}
145
+ class="absolute inset-0 w-full {className} rounded-md object-cover transition-opacity duration-500"
146
+ class:opacity-0={imageUrl && !showVideo}
147
+ muted
148
+ loop
149
+ playsinline
150
+ onerror={(e) =>
151
+ ((e.target as HTMLImageElement).src = VideoPlaceholder)}
152
+ ></video>
153
+ {/if}
154
+
155
+ {#if showVideoBadge}
156
+ <div
157
+ class="pointer-events-none absolute inset-0 flex items-center justify-center transition-opacity duration-300"
158
+ >
159
+ <div
160
+ class="flex h-11 w-11 items-center justify-center rounded-full bg-black/50 backdrop-blur-sm"
161
+ >
162
+ <HugeiconsIcon
163
+ icon={VideoReplayIcon}
164
+ size={22}
165
+ color="white"
166
+ strokeWidth={1.5}
105
167
  />
168
+ </div>
169
+ </div>
170
+ {#if duration}
171
+ <span
172
+ class="absolute bottom-2 right-2 z-10 rounded bg-black/60 px-1.5 py-0.5 text-xs font-medium text-white"
173
+ >
174
+ {duration}
175
+ </span>
106
176
  {/if}
177
+ {/if}
107
178
 
108
- {#if videoUrl && isVisible}
109
- <video
110
- bind:this={videoEl}
111
- src={videoUrl}
112
- preload="metadata"
113
- onloadedmetadata={onLoadedMetadata}
114
- class="absolute inset-0 w-full {className} rounded-md object-cover transition-opacity duration-500"
115
- class:opacity-0={imageUrl && !showVideo}
116
- muted
117
- loop
118
- playsinline
119
- ></video>
120
- {/if}
121
-
122
- {#if showVideoBadge}
123
- <div class="pointer-events-none absolute inset-0 flex items-center justify-center transition-opacity duration-300">
124
- <div class="flex h-11 w-11 items-center justify-center rounded-full bg-black/50 backdrop-blur-sm">
125
- <HugeiconsIcon icon={VideoReplayIcon} size={22} color="white" strokeWidth={1.5} />
126
- </div>
127
- </div>
128
- {#if duration}
129
- <span class="absolute bottom-2 right-2 z-10 rounded bg-black/60 px-1.5 py-0.5 text-xs font-medium text-white">
130
- {duration}
131
- </span>
132
- {/if}
133
- {/if}
134
-
135
- {#if showMaximize}
136
- {@render maximizeButton()}
137
- {/if}
179
+ {#if showMaximize}
180
+ {@render maximizeButton()}
181
+ {/if}
138
182
  </div>
139
- {/if}
183
+ {/if}
140
184
 
141
- {#if isMaximized}
185
+ {#if isMaximized}
142
186
  <div class="fixed inset-0 z-50">
143
- <button
187
+ <button
188
+ type="button"
189
+ class="absolute inset-0 h-full w-full cursor-default bg-black/80"
190
+ onclick={() => (isMaximized = false)}
191
+ aria-label="Fechar modal"
192
+ ></button>
193
+
194
+ <div
195
+ class="pointer-events-none fixed inset-0 z-50 flex items-center justify-center p-4"
196
+ >
197
+ <div class="pointer-events-auto relative max-h-[90vh] max-w-4xl">
198
+ <button
144
199
  type="button"
145
- class="absolute inset-0 h-full w-full cursor-default bg-black/80"
200
+ class="absolute top-3 right-3 rounded-full bg-red-900/50 p-2 text-white hover:bg-black/70"
146
201
  onclick={() => (isMaximized = false)}
147
- aria-label="Fechar modal"
148
- ></button>
149
-
150
- <div class="pointer-events-none fixed inset-0 z-50 flex items-center justify-center p-4">
151
- <div class="pointer-events-auto relative max-h-[90vh] max-w-4xl">
152
- <button
153
- type="button"
154
- class="absolute top-3 right-3 rounded-full bg-red-900/50 p-2 text-white hover:bg-black/70"
155
- onclick={() => (isMaximized = false)}
156
- aria-label="Fechar"
157
- >
158
- <HugeiconsIcon icon={Cancel01Icon} size={20} color="currentColor" strokeWidth={1.5} />
159
- </button>
160
-
161
- {#if videoUrl && showVideo}
162
- <!-- svelte-ignore a11y_media_has_caption -->
163
- <video src={videoUrl} class="max-h-[90vh] max-w-full rounded-md" controls autoplay playsinline></video>
164
- {:else if imageUrl}
165
- <img src={imageUrl} alt={imageUrl} class="max-h-[90vh] max-w-full rounded-md object-contain" />
166
- {/if}
167
- </div>
202
+ aria-label="Fechar"
203
+ >
204
+ <HugeiconsIcon
205
+ icon={Cancel01Icon}
206
+ size={20}
207
+ color="currentColor"
208
+ strokeWidth={1.5}
209
+ />
210
+ </button>
211
+
212
+ {#if videoUrl && showVideo}
213
+ <!-- svelte-ignore a11y_media_has_caption -->
214
+ <video
215
+ src={videoUrl}
216
+ class="max-h-[90vh] max-w-full rounded-md"
217
+ controls
218
+ autoplay
219
+ playsinline
220
+ ></video>
221
+ {:else if imageUrl}
222
+ <img
223
+ src={imageUrl}
224
+ alt={imageUrl}
225
+ class="max-h-[90vh] max-w-full rounded-md object-contain"
226
+ />
227
+ {/if}
168
228
  </div>
229
+ </div>
169
230
  </div>
170
- {/if}
231
+ {/if}
232
+ {/if}
@@ -4,6 +4,7 @@ type Props = {
4
4
  class?: string;
5
5
  isImageButtonMaximized?: boolean;
6
6
  isVideoButtonMaximized?: boolean;
7
+ isLoading?: boolean;
7
8
  };
8
9
  declare const CardThumbnailVideo: import("svelte").Component<Props, {}, "">;
9
10
  type CardThumbnailVideo = ReturnType<typeof CardThumbnailVideo>;
@@ -14,6 +14,7 @@ export interface CardMediaProps {
14
14
  className?: string;
15
15
  tags?: CardTagsProps[];
16
16
  isFavorite?: boolean;
17
+ isLoading?: boolean;
17
18
  startNumber?: number;
18
19
  isTagBorderBottom?: boolean;
19
20
  buttonProfileClass?: string;
@@ -35,6 +36,7 @@ export interface CardProfileProps {
35
36
  tags?: CardTagsProps[];
36
37
  isFavorite?: boolean;
37
38
  startNumber?: number;
39
+ isLoading?: boolean;
38
40
  isTagBorderBottom?: boolean;
39
41
  buttonProfileClass?: string;
40
42
  buttonDetailsClass?: string;
@@ -56,6 +58,7 @@ export interface CardPromotionProps {
56
58
  oldPrice?: string | number;
57
59
  newPrice?: string | number;
58
60
  buttonBuyText?: string;
61
+ buttonBuyClass?: string;
59
62
  isDescriptionIcon?: boolean;
60
63
  isDescriptionLabel?: boolean;
61
64
  onClickBuy?: (id: string | number) => void;
@@ -70,8 +73,10 @@ export interface CardHighlightProps {
70
73
  className?: string;
71
74
  isFavorite?: boolean;
72
75
  isLoading?: boolean;
76
+ isExpandButton?: boolean;
73
77
  content?: string;
74
78
  buttonText?: string;
79
+ buttonClass?: string;
75
80
  isDescriptionIcon?: boolean;
76
81
  isDescriptionLabel?: boolean;
77
82
  tags?: CardTagsProps[];
@@ -0,0 +1,11 @@
1
+ import type { CardMediaProps } from "../../../card/types";
2
+ import type { CarouselSlotProps } from "../../../panel/type";
3
+ import type { CarouselHeaderProps } from "../../types";
4
+ export interface CarouselMediaProps {
5
+ headerProps?: CarouselHeaderProps;
6
+ slotProps?: CarouselSlotProps;
7
+ gridClass?: string;
8
+ variant?: 1 | 2;
9
+ items: CardMediaProps[];
10
+ isLoading?: boolean;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import CarouselGridSlot from "../../shared/ui/CarouselGridSlot.svelte";
3
+ import * as Carousel from "../../../../../ui/carousel/index.js";
4
+ import type { CarouselMediaProps } from "../types";
5
+ import CarouselHeader from "../../../shared/ui/CarouselHeader.svelte";
6
+ import { useDevice } from "../../../../../../hooks/responsive.svelte";
7
+ import CardMedia from "../../../../card/media/CardMedia.svelte";
8
+
9
+ const {
10
+ headerProps,
11
+ slotProps,
12
+ gridClass,
13
+ items,
14
+ variant,
15
+ isLoading = false,
16
+ }: CarouselMediaProps = $props();
17
+
18
+ const responsive = useDevice();
19
+ </script>
20
+
21
+ <CarouselHeader {...headerProps}>
22
+ <CarouselGridSlot {slotProps} {gridClass} totalItems={items.length}>
23
+ {#if isLoading}
24
+ {#each Array.from( { length: responsive.isMobile ? 5 : 9 }, ) as _, i (`loading-${i}`)}
25
+ {#if responsive.isMobile}
26
+ <Carousel.Item class="pl-2 basis-auto">
27
+ <CardMedia id={i} {variant} isLoading />
28
+ </Carousel.Item>
29
+ {:else}
30
+ <div>
31
+ <CardMedia id={i} {variant} isLoading />
32
+ </div>
33
+ {/if}
34
+ {/each}
35
+ {:else}
36
+ {#each items as item, i (`media-${item.id ?? i}`)}
37
+ {#if responsive.isMobile}
38
+ <Carousel.Item class="pl-2 basis-auto">
39
+ <CardMedia {...item} {variant} />
40
+ </Carousel.Item>
41
+ {:else}
42
+ <div>
43
+ <CardMedia {...item} {variant} />
44
+ </div>
45
+ {/if}
46
+ {/each}
47
+ {/if}
48
+ </CarouselGridSlot>
49
+ </CarouselHeader>
@@ -0,0 +1,4 @@
1
+ import type { CarouselMediaProps } from "../types";
2
+ declare const CarouselGridMedia: import("svelte").Component<CarouselMediaProps, {}, "">;
3
+ type CarouselGridMedia = ReturnType<typeof CarouselGridMedia>;
4
+ export default CarouselGridMedia;
@@ -0,0 +1,11 @@
1
+ import type { CardProfileProps } from "../../../card/types";
2
+ import type { CarouselSlotProps } from "../../../panel/type";
3
+ import type { CarouselHeaderProps } from "../../types";
4
+ export interface CarouselProfileProps {
5
+ headerProps?: CarouselHeaderProps;
6
+ slotProps?: CarouselSlotProps;
7
+ gridClass?: string;
8
+ variant?: 1 | 2;
9
+ items: CardProfileProps[];
10
+ isLoading?: boolean;
11
+ }
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import CarouselGridSlot from "../../shared/ui/CarouselGridSlot.svelte";
3
+ import * as Carousel from "../../../../../ui/carousel/index.js";
4
+ import CarouselHeader from "../../../shared/ui/CarouselHeader.svelte";
5
+ import { useDevice } from "../../../../../../hooks/responsive.svelte";
6
+ import type { CarouselProfileProps } from "../types";
7
+ import CardProfile from "../../../../card/profile/CardProfile.svelte";
8
+
9
+ const {
10
+ headerProps,
11
+ slotProps,
12
+ gridClass,
13
+ items,
14
+ variant,
15
+ isLoading = false,
16
+ }: CarouselProfileProps = $props();
17
+
18
+ const responsive = useDevice();
19
+ </script>
20
+
21
+ <CarouselHeader {...headerProps}>
22
+ <CarouselGridSlot {slotProps} {gridClass} totalItems={items.length}>
23
+ {#if isLoading}
24
+ {#each Array.from( { length: responsive.isMobile ? 5 : 9 }, ) as _, i (`loading-${i}`)}
25
+ {#if responsive.isMobile}
26
+ <Carousel.Item class="pl-2 basis-auto">
27
+ <CardProfile id={i} {variant} isLoading />
28
+ </Carousel.Item>
29
+ {:else}
30
+ <div>
31
+ <CardProfile id={i} {variant} isLoading />
32
+ </div>
33
+ {/if}
34
+ {/each}
35
+ {:else}
36
+ {#each items as item, i (`profile-${item.id ?? i}`)}
37
+ {#if responsive.isMobile}
38
+ <Carousel.Item class="pl-2 basis-auto">
39
+ <CardProfile {...item} {variant} />
40
+ </Carousel.Item>
41
+ {:else}
42
+ <div>
43
+ <CardProfile {...item} {variant} />
44
+ </div>
45
+ {/if}
46
+ {/each}
47
+ {/if}
48
+ </CarouselGridSlot>
49
+ </CarouselHeader>
@@ -0,0 +1,4 @@
1
+ import type { CarouselProfileProps } from "../types";
2
+ declare const CarouselGridProfile: import("svelte").Component<CarouselProfileProps, {}, "">;
3
+ type CarouselGridProfile = ReturnType<typeof CarouselGridProfile>;
4
+ export default CarouselGridProfile;
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import CarouselSlot from "../../../../panel/CarouselSlot.svelte";
4
+ import type { CarouselSlotProps } from "../../../../panel/type";
5
+ import { useDevice } from "../../../../../../hooks/responsive.svelte";
6
+ import Autoplay from "embla-carousel-autoplay";
7
+
8
+ type Props = {
9
+ slotProps?: CarouselSlotProps;
10
+ totalItems: number;
11
+ gridClass?: string;
12
+ children: Snippet;
13
+ };
14
+
15
+ const { slotProps, totalItems, gridClass, children }: Props = $props();
16
+
17
+ const responsive = useDevice();
18
+
19
+ const colsClass = $derived(totalItems <= 2 ? "grid-cols-2" : "grid-cols-3");
20
+ </script>
21
+
22
+ {#if responsive.isMobile}
23
+ <CarouselSlot
24
+ {...slotProps}
25
+ plugins={[Autoplay({ delay: 4000, stopOnInteraction: true })]}
26
+ >
27
+ {@render children()}
28
+ </CarouselSlot>
29
+ {:else}
30
+ <div class="grid {colsClass} gap-4 p-2 {gridClass}">
31
+ {@render children()}
32
+ </div>
33
+ {/if}