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.
- package/dist/components/core/banner/AdCardBanner.svelte +74 -74
- package/dist/components/core/banner/CtaCardBanner.svelte +118 -118
- package/dist/components/core/breadcrumb/breadcrumb-list.svelte +31 -31
- package/dist/components/core/button/ButtonBack.svelte +18 -18
- package/dist/components/core/button/ButtonLogin.svelte +15 -15
- package/dist/components/core/button/ButtonLoginWith.svelte +57 -57
- package/dist/components/core/button/ButtonSend.svelte +15 -15
- package/dist/components/core/card/highlight/01/CardHighlight01.svelte +103 -103
- package/dist/components/core/card/highlight/02/CardHighlight02.svelte +99 -99
- package/dist/components/core/card/highlight/CardHighlight.svelte +22 -22
- package/dist/components/core/card/media/01/CardMedia01.svelte +133 -133
- package/dist/components/core/card/media/02/CardMedia02.svelte +127 -127
- package/dist/components/core/card/media/CardMedia.svelte +18 -18
- package/dist/components/core/card/product/01/CardProduct01.svelte +136 -136
- package/dist/components/core/card/product/02/CardProduct02.svelte +124 -124
- package/dist/components/core/card/product/CardProduct.svelte +22 -22
- package/dist/components/core/card/profile/01/CardProfile01.svelte +134 -134
- package/dist/components/core/card/profile/02/CardProfile02.svelte +132 -132
- package/dist/components/core/card/profile/CardProfile.svelte +18 -18
- package/dist/components/core/card/promotion/CardPromotion.svelte +139 -139
- package/dist/components/core/card/promotion/CardPromotionPrice.svelte +32 -32
- package/dist/components/core/card/shared/CardButton.svelte +42 -42
- package/dist/components/core/card/shared/CardCart.svelte +36 -36
- package/dist/components/core/card/shared/CardDescription.svelte +41 -41
- package/dist/components/core/card/shared/CardFavorite.svelte +33 -33
- package/dist/components/core/card/shared/CardLogo.svelte +19 -19
- package/dist/components/core/card/shared/CardPhoneOrWhatsapp.svelte +45 -45
- package/dist/components/core/card/shared/CardStarRating.svelte +24 -24
- package/dist/components/core/card/shared/CardTags.svelte +95 -95
- package/dist/components/core/card/shared/CardThumbnailVideo.svelte +232 -232
- package/dist/components/core/card/shared/CardTitlePrice.svelte +24 -24
- package/dist/components/core/card/shared/TruncatableText.svelte +49 -49
- package/dist/components/core/carousel/badge/ui/CarouselBadge.svelte +109 -109
- package/dist/components/core/carousel/grid/shared/ui/CarouselGridSlot.svelte +38 -38
- package/dist/components/core/carousel/hero/ui/CarouselHero.svelte +163 -163
- package/dist/components/core/carousel/shared/ui/CarouselHeader.svelte +60 -60
- package/dist/components/core/datatable/ui/DataTableActions.svelte +28 -28
- package/dist/components/core/datatable/ui/DataTableCheckbox.svelte +22 -22
- package/dist/components/core/datatable/ui/DataTableContent.svelte +59 -59
- package/dist/components/core/datatable/ui/DataTableCore.svelte +151 -151
- package/dist/components/core/datatable/ui/DataTableDragHandle.svelte +8 -8
- package/dist/components/core/datatable/ui/DataTableListFilter.svelte +59 -59
- package/dist/components/core/datatable/ui/DataTableListPagination.svelte +35 -35
- package/dist/components/core/footer/ui/Footer.svelte +18 -18
- package/dist/components/core/form/ui/input-base.svelte +81 -81
- package/dist/components/core/form/ui/input-email-or-phone.svelte +23 -23
- package/dist/components/core/form/ui/input-email.svelte +23 -23
- package/dist/components/core/form/ui/input-password-confirm.svelte +23 -23
- package/dist/components/core/form/ui/input-password.svelte +23 -23
- package/dist/components/core/form/ui/input-phone.svelte +33 -33
- package/dist/components/core/form/ui/input-username.svelte +22 -22
- package/dist/components/core/grid/ui/01/Grid01.svelte +28 -28
- package/dist/components/core/grid/ui/02/Grid02.svelte +28 -28
- package/dist/components/core/grid/ui/PanelGrid.svelte +27 -27
- package/dist/components/core/grid/ui/SimpleGrid.svelte +35 -35
- package/dist/components/core/grid/ui/shared/DescriptionGrid.svelte +23 -23
- package/dist/components/core/grid/ui/shared/IconRenderGrid.svelte +21 -21
- package/dist/components/core/grid/ui/shared/TitleGrid.svelte +19 -19
- package/dist/components/core/hero/SimpleHero.svelte +65 -65
- package/dist/components/core/label/LabelOr.svelte +25 -25
- package/dist/components/core/link/link-forget-password.svelte +12 -12
- package/dist/components/core/link/link-privacy-polity.svelte +12 -12
- package/dist/components/core/link/link-terms-of-service.svelte +12 -12
- package/dist/components/core/nav/ui/ComplexMenu.svelte +36 -36
- package/dist/components/core/nav/ui/Menu.svelte +70 -70
- package/dist/components/core/nav/ui/SimpleMenu.svelte +14 -14
- package/dist/components/core/nav/ui/mobile/MenuMobile.svelte +54 -54
- package/dist/components/core/nav/ui/shared/MenuButton.svelte +51 -51
- package/dist/components/core/nav/ui/shared/MenuLinks.svelte +40 -40
- package/dist/components/core/nav/ui/shared/MenuListItem.svelte +48 -48
- package/dist/components/core/nav/ui/shared/MenuLogo.svelte +13 -13
- package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItem.svelte +15 -15
- package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItemCard.svelte +62 -62
- package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItemGrid.svelte +32 -32
- package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItemList.svelte +24 -24
- package/dist/components/core/nav/ui/shared/navigation/NavigationMenuItems.svelte +21 -21
- package/dist/components/core/panel/CarouselSlot.svelte +57 -57
- package/dist/components/core/panel/LeftHero.svelte +117 -117
- package/dist/components/core/search/SearchInput.svelte +32 -32
- package/dist/components/core/search/shared/SearchInputCore.svelte +66 -66
- package/dist/components/core/sidebar/MenuBarSidebar.svelte +70 -70
- package/dist/components/core/sidebar/ui/navbar/NavMenuBarSidebar.svelte +97 -97
- package/dist/components/core/sidebar/ui/navbar/NavMenuBottomSidebar.svelte +33 -33
- package/dist/components/core/sidebar/ui/navbar/NavMenuTopBarSidebar.svelte +103 -103
- package/dist/components/core/tabs/ui/tab-underline.svelte +43 -43
- package/dist/components/pages/admin/01/ui/AdminContent.svelte +14 -14
- package/dist/components/pages/admin/01/ui/AdminPanel.svelte +46 -46
- package/dist/components/pages/security/login/01/ui/FormRegister.svelte +28 -28
- package/dist/components/pages/security/login/01/ui/LoginCard.svelte +107 -107
- package/dist/components/pages/security/login/PageLogin.svelte +22 -22
- package/dist/components/ui/badge/badge.svelte +49 -49
- package/dist/components/ui/button/button.svelte +82 -82
- package/dist/components/ui/chart/chart-container.svelte +80 -80
- package/dist/components/ui/chart/chart-style.svelte +37 -37
- package/dist/components/ui/chart/chart-tooltip.svelte +184 -184
- package/dist/components/ui/checkbox/checkbox.svelte +40 -40
- package/dist/components/ui/data-table/flex-render.svelte +40 -40
- package/dist/components/ui/drawer/drawer-close.svelte +7 -7
- package/dist/components/ui/drawer/drawer-content.svelte +33 -33
- package/dist/components/ui/drawer/drawer-description.svelte +17 -17
- package/dist/components/ui/drawer/drawer-footer.svelte +20 -20
- package/dist/components/ui/drawer/drawer-header.svelte +20 -20
- package/dist/components/ui/drawer/drawer-nested.svelte +12 -12
- package/dist/components/ui/drawer/drawer-overlay.svelte +17 -17
- package/dist/components/ui/drawer/drawer-portal.svelte +7 -7
- package/dist/components/ui/drawer/drawer-title.svelte +17 -17
- package/dist/components/ui/drawer/drawer-trigger.svelte +7 -7
- package/dist/components/ui/drawer/drawer.svelte +12 -12
- package/dist/components/ui/select/select-content.svelte +45 -45
- package/dist/components/ui/select/select-group-heading.svelte +21 -21
- package/dist/components/ui/select/select-group.svelte +17 -17
- package/dist/components/ui/select/select-item.svelte +41 -41
- package/dist/components/ui/select/select-label.svelte +20 -20
- package/dist/components/ui/select/select-portal.svelte +7 -7
- package/dist/components/ui/select/select-scroll-down-button.svelte +21 -21
- package/dist/components/ui/select/select-scroll-up-button.svelte +21 -21
- package/dist/components/ui/select/select-separator.svelte +18 -18
- package/dist/components/ui/select/select-trigger.svelte +30 -30
- package/dist/components/ui/select/select.svelte +11 -11
- package/dist/components/ui/sidebar/sidebar-menu-sub-button.svelte +39 -39
- package/dist/components/ui/star-rating/star-rating-star.svelte +41 -41
- package/dist/components/ui/star-rating/star-rating.svelte +20 -20
- package/dist/components/ui/table/table-body.svelte +15 -15
- package/dist/components/ui/table/table-caption.svelte +20 -20
- package/dist/components/ui/table/table-cell.svelte +15 -15
- package/dist/components/ui/table/table-footer.svelte +20 -20
- package/dist/components/ui/table/table-head.svelte +15 -15
- package/dist/components/ui/table/table-header.svelte +20 -20
- package/dist/components/ui/table/table-row.svelte +15 -15
- package/dist/components/ui/table/table.svelte +17 -17
- package/dist/components/ui/tabs/tabs-content.svelte +17 -17
- package/dist/components/ui/tabs/tabs-list.svelte +40 -40
- package/dist/components/ui/tabs/tabs-trigger.svelte +23 -23
- package/dist/components/ui/tabs/tabs.svelte +19 -19
- package/dist/styles/globals.css +149 -149
- 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}
|