negodesign 0.0.59 → 0.0.60

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 (136) hide show
  1. package/dist/components/core/banner/AdCardBanner.svelte +74 -74
  2. package/dist/components/core/banner/CtaCardBanner.svelte +118 -118
  3. package/dist/components/core/breadcrumb/breadcrumb-list.svelte +31 -31
  4. package/dist/components/core/button/ButtonBack.svelte +18 -18
  5. package/dist/components/core/button/ButtonLogin.svelte +15 -15
  6. package/dist/components/core/button/ButtonLoginWith.svelte +57 -57
  7. package/dist/components/core/button/ButtonSend.svelte +15 -15
  8. package/dist/components/core/card/highlight/01/CardHighlight01.svelte +103 -103
  9. package/dist/components/core/card/highlight/02/CardHighlight02.svelte +99 -99
  10. package/dist/components/core/card/highlight/CardHighlight.svelte +22 -22
  11. package/dist/components/core/card/media/01/CardMedia01.svelte +133 -133
  12. package/dist/components/core/card/media/02/CardMedia02.svelte +127 -127
  13. package/dist/components/core/card/media/CardMedia.svelte +18 -18
  14. package/dist/components/core/card/product/01/CardProduct01.svelte +136 -136
  15. package/dist/components/core/card/product/02/CardProduct02.svelte +124 -124
  16. package/dist/components/core/card/product/CardProduct.svelte +22 -22
  17. package/dist/components/core/card/profile/01/CardProfile01.svelte +134 -134
  18. package/dist/components/core/card/profile/02/CardProfile02.svelte +132 -132
  19. package/dist/components/core/card/profile/CardProfile.svelte +18 -18
  20. package/dist/components/core/card/promotion/CardPromotion.svelte +139 -139
  21. package/dist/components/core/card/promotion/CardPromotionPrice.svelte +32 -32
  22. package/dist/components/core/card/shared/CardButton.svelte +42 -42
  23. package/dist/components/core/card/shared/CardCart.svelte +36 -36
  24. package/dist/components/core/card/shared/CardDescription.svelte +41 -41
  25. package/dist/components/core/card/shared/CardFavorite.svelte +33 -33
  26. package/dist/components/core/card/shared/CardLogo.svelte +19 -19
  27. package/dist/components/core/card/shared/CardPhoneOrWhatsapp.svelte +45 -45
  28. package/dist/components/core/card/shared/CardStarRating.svelte +24 -24
  29. package/dist/components/core/card/shared/CardTags.svelte +95 -95
  30. package/dist/components/core/card/shared/CardThumbnailVideo.svelte +232 -232
  31. package/dist/components/core/card/shared/CardTitlePrice.svelte +24 -24
  32. package/dist/components/core/card/shared/TruncatableText.svelte +49 -49
  33. package/dist/components/core/carousel/badge/ui/CarouselBadge.svelte +109 -109
  34. package/dist/components/core/carousel/grid/shared/ui/CarouselGridSlot.svelte +38 -38
  35. package/dist/components/core/carousel/hero/ui/CarouselHero.svelte +163 -163
  36. package/dist/components/core/carousel/shared/ui/CarouselHeader.svelte +60 -60
  37. package/dist/components/core/datatable/ui/DataTableActions.svelte +28 -28
  38. package/dist/components/core/datatable/ui/DataTableCheckbox.svelte +22 -22
  39. package/dist/components/core/datatable/ui/DataTableContent.svelte +59 -59
  40. package/dist/components/core/datatable/ui/DataTableCore.svelte +151 -151
  41. package/dist/components/core/datatable/ui/DataTableDragHandle.svelte +8 -8
  42. package/dist/components/core/datatable/ui/DataTableListFilter.svelte +59 -59
  43. package/dist/components/core/datatable/ui/DataTableListPagination.svelte +35 -35
  44. package/dist/components/core/footer/ui/Footer.svelte +18 -18
  45. package/dist/components/core/form/ui/input-base.svelte +81 -81
  46. package/dist/components/core/form/ui/input-email-or-phone.svelte +23 -23
  47. package/dist/components/core/form/ui/input-email.svelte +23 -23
  48. package/dist/components/core/form/ui/input-password-confirm.svelte +23 -23
  49. package/dist/components/core/form/ui/input-password.svelte +23 -23
  50. package/dist/components/core/form/ui/input-phone.svelte +33 -33
  51. package/dist/components/core/form/ui/input-username.svelte +22 -22
  52. package/dist/components/core/grid/ui/01/Grid01.svelte +28 -28
  53. package/dist/components/core/grid/ui/02/Grid02.svelte +28 -28
  54. package/dist/components/core/grid/ui/PanelGrid.svelte +27 -27
  55. package/dist/components/core/grid/ui/SimpleGrid.svelte +35 -35
  56. package/dist/components/core/grid/ui/shared/DescriptionGrid.svelte +23 -23
  57. package/dist/components/core/grid/ui/shared/IconRenderGrid.svelte +21 -21
  58. package/dist/components/core/grid/ui/shared/TitleGrid.svelte +19 -19
  59. package/dist/components/core/hero/SimpleHero.svelte +65 -65
  60. package/dist/components/core/label/LabelOr.svelte +25 -25
  61. package/dist/components/core/link/link-forget-password.svelte +12 -12
  62. package/dist/components/core/link/link-privacy-polity.svelte +12 -12
  63. package/dist/components/core/link/link-terms-of-service.svelte +12 -12
  64. package/dist/components/core/nav/ui/ComplexMenu.svelte +36 -36
  65. package/dist/components/core/nav/ui/Menu.svelte +70 -70
  66. package/dist/components/core/nav/ui/SimpleMenu.svelte +14 -14
  67. package/dist/components/core/nav/ui/mobile/MenuMobile.svelte +54 -54
  68. package/dist/components/core/nav/ui/shared/MenuButton.svelte +51 -51
  69. package/dist/components/core/nav/ui/shared/MenuLinks.svelte +40 -40
  70. package/dist/components/core/nav/ui/shared/MenuListItem.svelte +48 -48
  71. package/dist/components/core/nav/ui/shared/MenuLogo.svelte +13 -13
  72. package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItem.svelte +15 -15
  73. package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItemCard.svelte +62 -62
  74. package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItemGrid.svelte +32 -32
  75. package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItemList.svelte +24 -24
  76. package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItems.svelte +21 -21
  77. package/dist/components/core/panel/CarouselSlot.svelte +57 -57
  78. package/dist/components/core/panel/LeftHero.svelte +117 -117
  79. package/dist/components/core/search/SearchInput.svelte +32 -32
  80. package/dist/components/core/search/shared/SearchInputCore.svelte +66 -66
  81. package/dist/components/core/sidebar/MenuBarSidebar.svelte +70 -70
  82. package/dist/components/core/sidebar/ui/navbar/NavMenuBarSidebar.svelte +97 -97
  83. package/dist/components/core/sidebar/ui/navbar/NavMenuBottomSidebar.svelte +33 -33
  84. package/dist/components/core/sidebar/ui/navbar/NavMenuTopBarSidebar.svelte +103 -103
  85. package/dist/components/core/tabs/ui/tab-underline.svelte +43 -43
  86. package/dist/components/pages/admin/01/ui/AdminContent.svelte +14 -14
  87. package/dist/components/pages/admin/01/ui/AdminPanel.svelte +46 -46
  88. package/dist/components/pages/security/login/01/ui/FormRegister.svelte +28 -28
  89. package/dist/components/pages/security/login/01/ui/LoginCard.svelte +107 -107
  90. package/dist/components/pages/security/login/PageLogin.svelte +22 -22
  91. package/dist/components/ui/badge/badge.svelte +49 -49
  92. package/dist/components/ui/button/button.svelte +82 -82
  93. package/dist/components/ui/chart/chart-container.svelte +80 -80
  94. package/dist/components/ui/chart/chart-style.svelte +37 -37
  95. package/dist/components/ui/chart/chart-tooltip.svelte +184 -184
  96. package/dist/components/ui/checkbox/checkbox.svelte +40 -40
  97. package/dist/components/ui/data-table/flex-render.svelte +40 -40
  98. package/dist/components/ui/drawer/drawer-close.svelte +7 -7
  99. package/dist/components/ui/drawer/drawer-content.svelte +33 -33
  100. package/dist/components/ui/drawer/drawer-description.svelte +17 -17
  101. package/dist/components/ui/drawer/drawer-footer.svelte +20 -20
  102. package/dist/components/ui/drawer/drawer-header.svelte +20 -20
  103. package/dist/components/ui/drawer/drawer-nested.svelte +12 -12
  104. package/dist/components/ui/drawer/drawer-overlay.svelte +17 -17
  105. package/dist/components/ui/drawer/drawer-portal.svelte +7 -7
  106. package/dist/components/ui/drawer/drawer-title.svelte +17 -17
  107. package/dist/components/ui/drawer/drawer-trigger.svelte +7 -7
  108. package/dist/components/ui/drawer/drawer.svelte +12 -12
  109. package/dist/components/ui/select/select-content.svelte +45 -45
  110. package/dist/components/ui/select/select-group-heading.svelte +21 -21
  111. package/dist/components/ui/select/select-group.svelte +17 -17
  112. package/dist/components/ui/select/select-item.svelte +41 -41
  113. package/dist/components/ui/select/select-label.svelte +20 -20
  114. package/dist/components/ui/select/select-portal.svelte +7 -7
  115. package/dist/components/ui/select/select-scroll-down-button.svelte +21 -21
  116. package/dist/components/ui/select/select-scroll-up-button.svelte +21 -21
  117. package/dist/components/ui/select/select-separator.svelte +18 -18
  118. package/dist/components/ui/select/select-trigger.svelte +30 -30
  119. package/dist/components/ui/select/select.svelte +11 -11
  120. package/dist/components/ui/sidebar/sidebar-menu-sub-button.svelte +39 -39
  121. package/dist/components/ui/star-rating/star-rating-star.svelte +41 -41
  122. package/dist/components/ui/star-rating/star-rating.svelte +20 -20
  123. package/dist/components/ui/table/table-body.svelte +15 -15
  124. package/dist/components/ui/table/table-caption.svelte +20 -20
  125. package/dist/components/ui/table/table-cell.svelte +15 -15
  126. package/dist/components/ui/table/table-footer.svelte +20 -20
  127. package/dist/components/ui/table/table-head.svelte +15 -15
  128. package/dist/components/ui/table/table-header.svelte +20 -20
  129. package/dist/components/ui/table/table-row.svelte +15 -15
  130. package/dist/components/ui/table/table.svelte +17 -17
  131. package/dist/components/ui/tabs/tabs-content.svelte +17 -17
  132. package/dist/components/ui/tabs/tabs-list.svelte +40 -40
  133. package/dist/components/ui/tabs/tabs-trigger.svelte +23 -23
  134. package/dist/components/ui/tabs/tabs.svelte +19 -19
  135. package/dist/styles/globals.css +149 -149
  136. package/package.json +113 -113
@@ -1,109 +1,109 @@
1
- <script lang="ts">
2
- import * as Carousel from "../../../../ui/carousel/index.js";
3
- import type { CarouselBadgeProps } from "../../types.js";
4
- import { HugeiconsIcon } from "@hugeicons/svelte";
5
- import Skeleton from "../../../../ui/skeleton/skeleton.svelte";
6
- import { useDevice } from "../../../../../hooks/responsive.svelte";
7
-
8
- const {
9
- items = [],
10
- imageClass,
11
- iconClass,
12
- labelClass,
13
- activeClass = "bg-gradient text-white rounded-full px-3",
14
- orientation = "horizontal",
15
- itemStyle = "DEFAULT",
16
- isBorderInline = false,
17
- showButton = true,
18
- isLoading = false,
19
- btnNavClass,
20
- itemClass,
21
- onClick,
22
- }: CarouselBadgeProps = $props();
23
-
24
- const TOP_NAV = $derived(isBorderInline ? "top-5.5" : "top-7.5");
25
- const borderB = $derived(
26
- isBorderInline ? `border-b-2 ${itemStyle == "BORDER" ? "pb-2" : ""}` : "",
27
- );
28
- const isInlineBorder = $derived(isBorderInline && itemStyle == "INLINE");
29
- const DEFAULT_IMG_OR_ICON_CLASS = "size-3 md:size-6";
30
-
31
- const responsive = useDevice();
32
- const skeletonCount = $derived(responsive.isMobile ? 2 : 8);
33
-
34
- let selectedValue = $state<unknown>(undefined);
35
-
36
- function isSelected(item: (typeof items)[number]) {
37
- return item.value !== undefined && item.value === selectedValue;
38
- }
39
-
40
- function selectItem(item: (typeof items)[number]) {
41
- selectedValue = item.value;
42
- item.onClick?.(item.value);
43
- onClick?.(item.value);
44
- }
45
- </script>
46
-
47
- {#snippet itemVisual(item: (typeof items)[number])}
48
- {#if item.image}
49
- <img
50
- src={item.image}
51
- alt={item.label}
52
- class={imageClass || DEFAULT_IMG_OR_ICON_CLASS}
53
- />
54
- {:else if typeof item.icon === "string"}
55
- <i class={`${item.icon} ${iconClass || DEFAULT_IMG_OR_ICON_CLASS}`}></i>
56
- {:else if item.icon}
57
- <HugeiconsIcon
58
- icon={item.icon}
59
- class={iconClass || DEFAULT_IMG_OR_ICON_CLASS}
60
- />
61
- {/if}
62
- {/snippet}
63
-
64
- {#if items.length > 0}
65
- <Carousel.Root>
66
- <Carousel.Content class={borderB}>
67
- {#if isLoading}
68
- <div class="flex items-center justify-between w-full gap-1 md:gap-2">
69
- {#each Array.from( { length: skeletonCount }, ) as _, i (`skeleton-${i}`)}
70
- <Skeleton class="h-4 w-25 bg-gray-50/90" />
71
- {/each}
72
- </div>
73
- {:else}
74
- {#each items as item, i (`badge-${i}-${item.value ?? item.label ?? i}`)}
75
- <Carousel.Item
76
- class="basis-auto relative"
77
- onclick={() => selectItem(item)}
78
- >
79
- <div
80
- class="flex gap-1 mx-2 p-1 justify-center items-center cursor-pointer relative hover:text-lg hover:font-bold
81
- {isSelected(item)
82
- ? isInlineBorder
83
- ? 'text-gradient font-bold'
84
- : activeClass
85
- : ''}
86
- {itemStyle == 'BORDER' ? 'border-2 rounded-full min-w-25 px-1' : ''}
87
- {orientation === 'horizontal' ? 'flex-row' : 'flex-col'}
88
- {isInlineBorder ? 'pb-3' : ''}
89
- {itemClass}
90
- "
91
- >
92
- {@render itemVisual(item)}
93
- <div class={labelClass}>{item.label}</div>
94
- </div>
95
- {#if isSelected(item) && isInlineBorder}
96
- <div class="absolute pt-5 w-full h-2 bg-gradient"></div>
97
- {/if}
98
- </Carousel.Item>
99
- {/each}
100
- {/if}
101
- </Carousel.Content>
102
- {#if showButton}
103
- {@const navClass =
104
- "absolute -translate-y-1/2 z-50 bg-blue-700! text-white! cursor-pointer"}
105
- <Carousel.Previous class="-left-8 {navClass} {TOP_NAV} {btnNavClass}" />
106
- <Carousel.Next class="-right-8 {navClass} {TOP_NAV} {btnNavClass}" />
107
- {/if}
108
- </Carousel.Root>
109
- {/if}
1
+ <script lang="ts">
2
+ import * as Carousel from "../../../../ui/carousel/index.js";
3
+ import type { CarouselBadgeProps } from "../../types.js";
4
+ import { HugeiconsIcon } from "@hugeicons/svelte";
5
+ import Skeleton from "../../../../ui/skeleton/skeleton.svelte";
6
+ import { useDevice } from "../../../../../hooks/responsive.svelte";
7
+
8
+ const {
9
+ items = [],
10
+ imageClass,
11
+ iconClass,
12
+ labelClass,
13
+ activeClass = "bg-gradient text-white rounded-full px-3",
14
+ orientation = "horizontal",
15
+ itemStyle = "DEFAULT",
16
+ isBorderInline = false,
17
+ showButton = true,
18
+ isLoading = false,
19
+ btnNavClass,
20
+ itemClass,
21
+ onClick,
22
+ }: CarouselBadgeProps = $props();
23
+
24
+ const TOP_NAV = $derived(isBorderInline ? "top-5.5" : "top-7.5");
25
+ const borderB = $derived(
26
+ isBorderInline ? `border-b-2 ${itemStyle == "BORDER" ? "pb-2" : ""}` : "",
27
+ );
28
+ const isInlineBorder = $derived(isBorderInline && itemStyle == "INLINE");
29
+ const DEFAULT_IMG_OR_ICON_CLASS = "size-3 md:size-6";
30
+
31
+ const responsive = useDevice();
32
+ const skeletonCount = $derived(responsive.isMobile ? 2 : 8);
33
+
34
+ let selectedValue = $state<unknown>(undefined);
35
+
36
+ function isSelected(item: (typeof items)[number]) {
37
+ return item.value !== undefined && item.value === selectedValue;
38
+ }
39
+
40
+ function selectItem(item: (typeof items)[number]) {
41
+ selectedValue = item.value;
42
+ item.onClick?.(item.value);
43
+ onClick?.(item.value);
44
+ }
45
+ </script>
46
+
47
+ {#snippet itemVisual(item: (typeof items)[number])}
48
+ {#if item.image}
49
+ <img
50
+ src={item.image}
51
+ alt={item.label}
52
+ class={imageClass || DEFAULT_IMG_OR_ICON_CLASS}
53
+ />
54
+ {:else if typeof item.icon === "string"}
55
+ <i class={`${item.icon} ${iconClass || DEFAULT_IMG_OR_ICON_CLASS}`}></i>
56
+ {:else if item.icon}
57
+ <HugeiconsIcon
58
+ icon={item.icon}
59
+ class={iconClass || DEFAULT_IMG_OR_ICON_CLASS}
60
+ />
61
+ {/if}
62
+ {/snippet}
63
+
64
+ {#if items.length > 0}
65
+ <Carousel.Root>
66
+ <Carousel.Content class={borderB}>
67
+ {#if isLoading}
68
+ <div class="flex items-center justify-between w-full gap-1 md:gap-2">
69
+ {#each Array.from( { length: skeletonCount }, ) as _, i (`skeleton-${i}`)}
70
+ <Skeleton class="h-4 w-25 bg-gray-50/90" />
71
+ {/each}
72
+ </div>
73
+ {:else}
74
+ {#each items as item, i (`badge-${i}-${item.value ?? item.label ?? i}`)}
75
+ <Carousel.Item
76
+ class="basis-auto relative"
77
+ onclick={() => selectItem(item)}
78
+ >
79
+ <div
80
+ class="flex gap-1 mx-2 p-1 justify-center items-center cursor-pointer relative hover:text-lg hover:font-bold
81
+ {isSelected(item)
82
+ ? isInlineBorder
83
+ ? 'text-gradient font-bold'
84
+ : activeClass
85
+ : ''}
86
+ {itemStyle == 'BORDER' ? 'border-2 rounded-full min-w-25 px-1' : ''}
87
+ {orientation === 'horizontal' ? 'flex-row' : 'flex-col'}
88
+ {isInlineBorder ? 'pb-3' : ''}
89
+ {itemClass}
90
+ "
91
+ >
92
+ {@render itemVisual(item)}
93
+ <div class={labelClass}>{item.label}</div>
94
+ </div>
95
+ {#if isSelected(item) && isInlineBorder}
96
+ <div class="absolute pt-5 w-full h-2 bg-gradient"></div>
97
+ {/if}
98
+ </Carousel.Item>
99
+ {/each}
100
+ {/if}
101
+ </Carousel.Content>
102
+ {#if showButton}
103
+ {@const navClass =
104
+ "absolute -translate-y-1/2 z-50 bg-blue-700! text-white! cursor-pointer"}
105
+ <Carousel.Previous class="-left-8 {navClass} {TOP_NAV} {btnNavClass}" />
106
+ <Carousel.Next class="-right-8 {navClass} {TOP_NAV} {btnNavClass}" />
107
+ {/if}
108
+ </Carousel.Root>
109
+ {/if}
@@ -1,38 +1,38 @@
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
- import type { GridProps } from "../../../../grid/data/types";
8
- import SimpleGrid from "../../../../grid/ui/SimpleGrid.svelte";
9
-
10
- type Props = {
11
- slotProps?: CarouselSlotProps;
12
- gridProps?: GridProps;
13
- gridClass?: string;
14
- children: Snippet;
15
- };
16
-
17
- const { slotProps, gridProps, gridClass, children }: Props = $props();
18
-
19
- const responsive = useDevice();
20
- </script>
21
-
22
- {#if gridProps}
23
- <SimpleGrid {...gridProps} />
24
- {/if}
25
- {#if responsive.isMobile}
26
- <CarouselSlot
27
- {...slotProps}
28
- plugins={[Autoplay({ delay: 4000, stopOnInteraction: true })]}
29
- >
30
- {@render children()}
31
- </CarouselSlot>
32
- {:else}
33
- <div
34
- class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 p-2 {gridClass}"
35
- >
36
- {@render children()}
37
- </div>
38
- {/if}
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
+ import type { GridProps } from "../../../../grid/data/types";
8
+ import SimpleGrid from "../../../../grid/ui/SimpleGrid.svelte";
9
+
10
+ type Props = {
11
+ slotProps?: CarouselSlotProps;
12
+ gridProps?: GridProps;
13
+ gridClass?: string;
14
+ children: Snippet;
15
+ };
16
+
17
+ const { slotProps, gridProps, gridClass, children }: Props = $props();
18
+
19
+ const responsive = useDevice();
20
+ </script>
21
+
22
+ {#if gridProps}
23
+ <SimpleGrid {...gridProps} />
24
+ {/if}
25
+ {#if responsive.isMobile}
26
+ <CarouselSlot
27
+ {...slotProps}
28
+ plugins={[Autoplay({ delay: 4000, stopOnInteraction: true })]}
29
+ >
30
+ {@render children()}
31
+ </CarouselSlot>
32
+ {:else}
33
+ <div
34
+ class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 p-2 {gridClass}"
35
+ >
36
+ {@render children()}
37
+ </div>
38
+ {/if}
@@ -1,163 +1,163 @@
1
- <script lang="ts">
2
- import type { Snippet } from "svelte";
3
- import Menu from "../../../nav/ui/Menu.svelte";
4
- import type { CarouselHeroItem, CarouselHeroProps } from "../types";
5
-
6
- let {
7
- items,
8
- navMenu,
9
- children,
10
- className,
11
- titleClass,
12
- descriptionClass,
13
- intervalMs = 5000,
14
- transitionMs = 1200,
15
- indicatorShow = false,
16
- sectionClass,
17
- }: CarouselHeroProps & {
18
- children?: Snippet;
19
- intervalMs?: number;
20
- transitionMs?: number;
21
- indicatorShow?: boolean;
22
- } = $props();
23
-
24
- const initialImage = $derived(items?.[0]?.image ?? "");
25
- let layers = $state<[string, string]>(["", ""]);
26
- let active = $state<0 | 1>(0);
27
- let index = $state(0);
28
- let switching = false;
29
-
30
- // eslint-disable-next-line svelte/prefer-svelte-reactivity
31
- const preloadCache = new Map<string, Promise<void>>();
32
-
33
- function preload(src: string): Promise<void> {
34
- if (!src) return Promise.resolve();
35
- const cached = preloadCache.get(src);
36
- if (cached) return cached;
37
-
38
- const promise = new Promise<void>((resolve) => {
39
- const img = new Image();
40
- img.onload = () => resolve();
41
- img.onerror = () => resolve();
42
- img.src = src;
43
- });
44
-
45
- preloadCache.set(src, promise);
46
- return promise;
47
- }
48
-
49
- $effect(() => {
50
- items?.forEach((item) => void preload(item.image));
51
- });
52
-
53
- async function goTo(nextIndex: number) {
54
- if (!items || nextIndex === index || switching) return;
55
- switching = true;
56
-
57
- const nextSrc = items[nextIndex].image;
58
- await preload(nextSrc);
59
-
60
- const nextLayer = active === 0 ? 1 : 0;
61
- layers[nextLayer] = nextSrc;
62
- active = nextLayer;
63
- index = nextIndex;
64
- switching = false;
65
- }
66
-
67
- $effect(() => {
68
- if (!items || items.length <= 1) return;
69
-
70
- const timer = setInterval(() => {
71
- goTo((index + 1) % items.length);
72
- }, intervalMs);
73
-
74
- return () => clearInterval(timer);
75
- });
76
-
77
- function fadeIn(node: HTMLElement) {
78
- requestAnimationFrame(() => {
79
- node.style.opacity = "1";
80
- });
81
- return {};
82
- }
83
-
84
- let selected = $derived<CarouselHeroItem | undefined>(items?.[index]);
85
- </script>
86
-
87
- {#if selected}
88
- <header
89
- class="w-full relative flex flex-col justify-center items-center h-75 md:h-100 overflow-hidden {className}"
90
- >
91
- {#each layers as bg, i (`layer-${i}`)}
92
- {@const src = bg || (i === 0 ? initialImage : "")}
93
- <div
94
- style="
95
- position: absolute;
96
- inset: 0;
97
- will-change: opacity;
98
- background-image: {src ? `url(${src})` : 'none'};
99
- background-size: cover;
100
- background-position: center;
101
- background-repeat: no-repeat;
102
- opacity: {i === active ? 1 : 0};
103
- transition: opacity {transitionMs}ms ease-in-out;
104
- "
105
- ></div>
106
- {/each}
107
-
108
- <div
109
- style="position: absolute; inset: 0; height: 100%; background-color: rgba(0,0,0,0.4);"
110
- ></div>
111
-
112
- <div class="relative z-10 w-full flex flex-col items-center flex-1">
113
- <Menu {navMenu} />
114
-
115
- <section
116
- class="flex flex-col justify-center items-center flex-1 w-full h-auto text-center px-4 {sectionClass}"
117
- >
118
- {#key selected.title}
119
- <div
120
- style="transition: opacity {transitionMs * 0.6}ms ease-in-out;"
121
- class="opacity-0 flex flex-col justify-center items-center text-center"
122
- use:fadeIn
123
- >
124
- <div>
125
- <h1
126
- class="text-white text-2xl md:text-4xl font-bold {titleClass}"
127
- >
128
- {selected.title}
129
- </h1>
130
- </div>
131
- <div class="flex flex-col justify-center items-center">
132
- <p class="text-white/90 mt-2 max-w-2xl {descriptionClass}">
133
- {selected.description}
134
- </p>
135
- </div>
136
- </div>
137
- {/key}
138
-
139
- {#if children}
140
- {@render children()}
141
- {/if}
142
- </section>
143
- </div>
144
-
145
- {#if indicatorShow}
146
- {#if items && items.length > 1}
147
- <div
148
- style="position: absolute; bottom: 0.75rem; z-index: 10; display: flex; gap: 0.5rem;"
149
- >
150
- {#each items as item, i (`hero-dot-${i}`)}
151
- <button
152
- aria-label={`Ir para o slide ${i + 1}: ${item.title}`}
153
- class="w-2 h-2 rounded-full transition-colors {i === index
154
- ? 'bg-white'
155
- : 'bg-white/40'}"
156
- onclick={() => goTo(i)}
157
- ></button>
158
- {/each}
159
- </div>
160
- {/if}
161
- {/if}
162
- </header>
163
- {/if}
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import Menu from "../../../nav/ui/Menu.svelte";
4
+ import type { CarouselHeroItem, CarouselHeroProps } from "../types";
5
+
6
+ let {
7
+ items,
8
+ navMenu,
9
+ children,
10
+ className,
11
+ titleClass,
12
+ descriptionClass,
13
+ intervalMs = 5000,
14
+ transitionMs = 1200,
15
+ indicatorShow = false,
16
+ sectionClass,
17
+ }: CarouselHeroProps & {
18
+ children?: Snippet;
19
+ intervalMs?: number;
20
+ transitionMs?: number;
21
+ indicatorShow?: boolean;
22
+ } = $props();
23
+
24
+ const initialImage = $derived(items?.[0]?.image ?? "");
25
+ let layers = $state<[string, string]>(["", ""]);
26
+ let active = $state<0 | 1>(0);
27
+ let index = $state(0);
28
+ let switching = false;
29
+
30
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
31
+ const preloadCache = new Map<string, Promise<void>>();
32
+
33
+ function preload(src: string): Promise<void> {
34
+ if (!src) return Promise.resolve();
35
+ const cached = preloadCache.get(src);
36
+ if (cached) return cached;
37
+
38
+ const promise = new Promise<void>((resolve) => {
39
+ const img = new Image();
40
+ img.onload = () => resolve();
41
+ img.onerror = () => resolve();
42
+ img.src = src;
43
+ });
44
+
45
+ preloadCache.set(src, promise);
46
+ return promise;
47
+ }
48
+
49
+ $effect(() => {
50
+ items?.forEach((item) => void preload(item.image));
51
+ });
52
+
53
+ async function goTo(nextIndex: number) {
54
+ if (!items || nextIndex === index || switching) return;
55
+ switching = true;
56
+
57
+ const nextSrc = items[nextIndex].image;
58
+ await preload(nextSrc);
59
+
60
+ const nextLayer = active === 0 ? 1 : 0;
61
+ layers[nextLayer] = nextSrc;
62
+ active = nextLayer;
63
+ index = nextIndex;
64
+ switching = false;
65
+ }
66
+
67
+ $effect(() => {
68
+ if (!items || items.length <= 1) return;
69
+
70
+ const timer = setInterval(() => {
71
+ goTo((index + 1) % items.length);
72
+ }, intervalMs);
73
+
74
+ return () => clearInterval(timer);
75
+ });
76
+
77
+ function fadeIn(node: HTMLElement) {
78
+ requestAnimationFrame(() => {
79
+ node.style.opacity = "1";
80
+ });
81
+ return {};
82
+ }
83
+
84
+ let selected = $derived<CarouselHeroItem | undefined>(items?.[index]);
85
+ </script>
86
+
87
+ {#if selected}
88
+ <header
89
+ class="w-full relative flex flex-col justify-center items-center h-75 md:h-100 overflow-hidden {className}"
90
+ >
91
+ {#each layers as bg, i (`layer-${i}`)}
92
+ {@const src = bg || (i === 0 ? initialImage : "")}
93
+ <div
94
+ style="
95
+ position: absolute;
96
+ inset: 0;
97
+ will-change: opacity;
98
+ background-image: {src ? `url(${src})` : 'none'};
99
+ background-size: cover;
100
+ background-position: center;
101
+ background-repeat: no-repeat;
102
+ opacity: {i === active ? 1 : 0};
103
+ transition: opacity {transitionMs}ms ease-in-out;
104
+ "
105
+ ></div>
106
+ {/each}
107
+
108
+ <div
109
+ style="position: absolute; inset: 0; height: 100%; background-color: rgba(0,0,0,0.4);"
110
+ ></div>
111
+
112
+ <div class="relative z-10 w-full flex flex-col items-center flex-1">
113
+ <Menu {navMenu} />
114
+
115
+ <section
116
+ class="flex flex-col justify-center items-center flex-1 w-full h-auto text-center px-4 {sectionClass}"
117
+ >
118
+ {#key selected.title}
119
+ <div
120
+ style="transition: opacity {transitionMs * 0.6}ms ease-in-out;"
121
+ class="opacity-0 flex flex-col justify-center items-center text-center"
122
+ use:fadeIn
123
+ >
124
+ <div>
125
+ <h1
126
+ class="text-white text-2xl md:text-4xl font-bold {titleClass}"
127
+ >
128
+ {selected.title}
129
+ </h1>
130
+ </div>
131
+ <div class="flex flex-col justify-center items-center">
132
+ <p class="text-white/90 mt-2 max-w-2xl {descriptionClass}">
133
+ {selected.description}
134
+ </p>
135
+ </div>
136
+ </div>
137
+ {/key}
138
+
139
+ {#if children}
140
+ {@render children()}
141
+ {/if}
142
+ </section>
143
+ </div>
144
+
145
+ {#if indicatorShow}
146
+ {#if items && items.length > 1}
147
+ <div
148
+ style="position: absolute; bottom: 0.75rem; z-index: 10; display: flex; gap: 0.5rem;"
149
+ >
150
+ {#each items as item, i (`hero-dot-${i}`)}
151
+ <button
152
+ aria-label={`Ir para o slide ${i + 1}: ${item.title}`}
153
+ class="w-2 h-2 rounded-full transition-colors {i === index
154
+ ? 'bg-white'
155
+ : 'bg-white/40'}"
156
+ onclick={() => goTo(i)}
157
+ ></button>
158
+ {/each}
159
+ </div>
160
+ {/if}
161
+ {/if}
162
+ </header>
163
+ {/if}