negodesign 0.0.56 → 0.0.57
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/card/types.d.ts +4 -0
- package/dist/components/core/carousel/badge/type.d.ts +27 -0
- package/dist/components/core/carousel/badge/ui/CarouselBadge.svelte +109 -109
- package/dist/components/core/carousel/grid/media/types.d.ts +3 -0
- package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte +2 -1
- package/dist/components/core/carousel/grid/product/types.d.ts +3 -0
- package/dist/components/core/carousel/grid/product/ui/CarouselGridProduct.svelte +5 -4
- package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte +1 -1
- package/dist/components/core/carousel/grid/promotion/ui/CarouselGridPromotion.svelte +1 -1
- package/dist/components/core/carousel/grid/shared/ui/CarouselGridSlot.svelte +8 -3
- package/dist/components/core/carousel/grid/shared/ui/CarouselGridSlot.svelte.d.ts +2 -1
- package/dist/components/core/carousel/hero/types.d.ts +30 -0
- package/dist/components/core/carousel/hero/{CarouselHero.svelte → ui/CarouselHero.svelte} +163 -161
- package/dist/components/core/carousel/shared/ui/CarouselHeader.svelte +60 -60
- package/dist/components/core/carousel/types.d.ts +2 -56
- 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/data/types.d.ts +16 -0
- package/dist/components/core/grid/ui/01/Grid01.svelte +27 -0
- package/dist/components/core/grid/ui/01/Grid01.svelte.d.ts +4 -0
- package/dist/components/core/grid/ui/02/Grid02.svelte +27 -0
- package/dist/components/core/grid/ui/02/Grid02.svelte.d.ts +4 -0
- package/dist/components/core/grid/ui/PanelGrid.svelte +27 -0
- package/dist/components/core/grid/ui/PanelGrid.svelte.d.ts +4 -0
- package/dist/components/core/grid/ui/SimpleGrid.svelte +35 -0
- package/dist/components/core/grid/ui/SimpleGrid.svelte.d.ts +4 -0
- package/dist/components/core/grid/ui/shared/DescriptionGrid.svelte +17 -0
- package/dist/components/core/grid/ui/shared/DescriptionGrid.svelte.d.ts +7 -0
- package/dist/components/core/grid/ui/shared/IconRenderGrid.svelte +16 -0
- package/dist/components/core/grid/ui/shared/IconRenderGrid.svelte.d.ts +7 -0
- package/dist/components/core/grid/ui/shared/TitleGrid.svelte +11 -0
- package/dist/components/core/grid/ui/shared/TitleGrid.svelte.d.ts +7 -0
- 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/data/complex-menu-utils.d.ts +7 -0
- package/dist/components/core/nav/data/complex-menu-utils.js +15 -0
- package/dist/components/core/nav/data/nav-menu.d.ts +21 -7
- package/dist/components/core/nav/{ui/shared → data}/types.d.ts +15 -2
- package/dist/components/core/nav/ui/ComplexMenu.svelte +36 -0
- package/dist/components/core/nav/ui/ComplexMenu.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/Menu.svelte +70 -66
- package/dist/components/core/nav/ui/Menu.svelte.d.ts +1 -1
- package/dist/components/core/nav/ui/SimpleMenu.svelte +14 -0
- package/dist/components/core/nav/ui/SimpleMenu.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/mobile/ComplexMenuMobile.svelte +98 -0
- package/dist/components/core/nav/ui/mobile/ComplexMenuMobile.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/mobile/MenuMobile.svelte +54 -0
- package/dist/components/core/nav/ui/mobile/MenuMobile.svelte.d.ts +11 -0
- package/dist/components/core/nav/ui/mobile/SimpleMenuMobile.svelte +42 -0
- package/dist/components/core/nav/ui/mobile/{nav-menu-drawer.svelte.d.ts → SimpleMenuMobile.svelte.d.ts} +3 -3
- package/dist/components/core/nav/ui/shared/CollapsibleNavMenu.svelte +24 -0
- package/dist/components/core/nav/ui/shared/CollapsibleNavMenu.svelte.d.ts +8 -0
- package/dist/components/core/nav/ui/shared/MenuButton.svelte +51 -0
- package/dist/components/core/nav/ui/shared/MenuButton.svelte.d.ts +4 -0
- 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/MenuListItem.svelte.d.ts +1 -1
- package/dist/components/core/nav/ui/shared/MenuLogo.svelte +13 -0
- package/dist/components/core/nav/ui/shared/MenuLogo.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/shared/NavMenuItem.svelte +28 -0
- package/dist/components/core/nav/ui/shared/NavMenuItem.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownMenuListItem.svelte +40 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownMenuListItem.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItem.svelte +12 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItem.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItemCard.svelte +49 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItemCard.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItemGrid.svelte +30 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItemGrid.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItemList.svelte +24 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItemList.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItems.svelte +24 -0
- package/dist/components/core/nav/ui/shared/dropdown/DropdownNavigationMenuItems.svelte.d.ts +4 -0
- package/dist/components/core/nav/ui/shared/{NavigationMenuItem.svelte → navigation/NavigationMenuItem.svelte} +15 -15
- package/dist/components/core/nav/ui/shared/{NavigationMenuItem.svelte.d.ts → navigation/NavigationMenuItem.svelte.d.ts} +1 -1
- package/dist/components/core/nav/ui/shared/{NavigationMenuItemCard.svelte → navigation/NavigationMenuItemCard.svelte} +62 -62
- package/dist/components/core/nav/ui/shared/{NavigationMenuItemCard.svelte.d.ts → navigation/NavigationMenuItemCard.svelte.d.ts} +1 -1
- package/dist/components/core/nav/ui/shared/{NavigationMenuItemGrid.svelte → navigation/NavigationMenuItemGrid.svelte} +32 -32
- package/dist/components/core/nav/ui/shared/{NavigationMenuItemGrid.svelte.d.ts → navigation/NavigationMenuItemGrid.svelte.d.ts} +1 -1
- package/dist/components/core/nav/ui/shared/{NavigationMenuItemList.svelte → navigation/NavigationMenuItemList.svelte} +24 -24
- package/dist/components/core/nav/ui/shared/{NavigationMenuItemList.svelte.d.ts → navigation/NavigationMenuItemList.svelte.d.ts} +1 -1
- package/dist/components/core/nav/ui/shared/{NavigationMenuItems.svelte → navigation/NavigationMenuItems.svelte} +21 -21
- package/dist/components/core/nav/ui/shared/{NavigationMenuItems.svelte.d.ts → navigation/NavigationMenuItems.svelte.d.ts} +1 -1
- 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/MenuBarSidebar.svelte.d.ts +2 -2
- package/dist/components/core/sidebar/data/nav-menu-sidebar.js +1 -0
- package/dist/components/core/sidebar/data/nav-top-bar-sidebar.js +1 -0
- package/dist/components/core/sidebar/data/nav-user-sidebar.js +1 -0
- package/dist/components/core/sidebar/types.d.ts +2 -2
- package/dist/components/core/sidebar/ui/navbar/NavMenuBarSidebar.svelte +97 -97
- package/dist/components/core/sidebar/ui/navbar/NavMenuBarSidebar.svelte.d.ts +1 -1
- package/dist/components/core/sidebar/ui/navbar/NavMenuBottomSidebar.svelte +33 -33
- package/dist/components/core/sidebar/ui/navbar/NavMenuBottomSidebar.svelte.d.ts +1 -1
- package/dist/components/core/sidebar/ui/navbar/NavMenuTopBarSidebar.svelte +103 -103
- package/dist/components/core/sidebar/ui/navbar/NavMenuTopBarSidebar.svelte.d.ts +1 -1
- 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/privacy-policy-or-terms-of-use/types.d.ts +2 -0
- 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/globals.css +56 -2
- package/dist/index.d.ts +1 -8
- package/dist/index.js +1 -8
- package/dist/styles/globals.css +149 -149
- package/dist/styles/hgi-stroke-rounded.css +1 -1
- package/dist/types/index.d.ts +9 -7
- package/package.json +113 -113
- package/dist/components/core/nav/ui/NavMenu.svelte +0 -122
- package/dist/components/core/nav/ui/NavMenu.svelte.d.ts +0 -4
- package/dist/components/core/nav/ui/mobile/nav-menu-drawer.svelte +0 -85
- package/dist/components/core/{nav/data/nav-menu-sidebar.js → carousel/badge/type.js} +0 -0
- package/dist/components/core/{nav/ui/shared → carousel/hero}/types.js +0 -0
- package/dist/components/core/carousel/hero/{CarouselHero.svelte.d.ts → ui/CarouselHero.svelte.d.ts} +1 -1
- /package/dist/components/core/{nav/data/nav-top-bar-sidebar.js → grid/data/types.js} +0 -0
- /package/dist/components/core/nav/data/{nav-user-sidebar.js → types.js} +0 -0
- /package/dist/components/core/{nav → sidebar}/data/nav-menu-sidebar.d.ts +0 -0
- /package/dist/components/core/{nav → sidebar}/data/nav-top-bar-sidebar.d.ts +0 -0
- /package/dist/components/core/{nav → sidebar}/data/nav-user-sidebar.d.ts +0 -0
|
@@ -1,232 +1,232 @@
|
|
|
1
|
-
<!-- CardMedia.svelte -->
|
|
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 {
|
|
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 = "h-32 w-full md: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;
|
|
65
|
-
}
|
|
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();
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
{ rootMargin: "100px" },
|
|
78
|
-
);
|
|
79
|
-
observer.observe(containerEl);
|
|
80
|
-
return () => observer.disconnect();
|
|
81
|
-
});
|
|
82
|
-
</script>
|
|
83
|
-
|
|
84
|
-
<svelte:window
|
|
85
|
-
onkeydown={isMaximized
|
|
86
|
-
? (e) => e.key === "Escape" && (isMaximized = false)
|
|
87
|
-
: undefined}
|
|
88
|
-
/>
|
|
89
|
-
|
|
90
|
-
{#snippet maximizeButton()}
|
|
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>
|
|
108
|
-
{/snippet}
|
|
109
|
-
|
|
110
|
-
{#if isLoading}
|
|
111
|
-
<Skeleton class="w-full h-12.5 md:h-50 bg-gray-500/90" />
|
|
112
|
-
{:else}
|
|
113
|
-
{#if imageUrl || videoUrl}
|
|
114
|
-
<div
|
|
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))}
|
|
126
|
-
>
|
|
127
|
-
{#if imageUrl}
|
|
128
|
-
<img
|
|
129
|
-
src={isVisible ? imageUrl : undefined}
|
|
130
|
-
loading="lazy"
|
|
131
|
-
decoding="async"
|
|
132
|
-
alt={imageUrl}
|
|
133
|
-
class="absolute inset-0 h-full w-full 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 h-full w-full 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}
|
|
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>
|
|
176
|
-
{/if}
|
|
177
|
-
{/if}
|
|
178
|
-
|
|
179
|
-
{#if showMaximize}
|
|
180
|
-
{@render maximizeButton()}
|
|
181
|
-
{/if}
|
|
182
|
-
</div>
|
|
183
|
-
{/if}
|
|
184
|
-
|
|
185
|
-
{#if isMaximized}
|
|
186
|
-
<div class="fixed inset-0 z-50">
|
|
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
|
|
199
|
-
type="button"
|
|
200
|
-
class="absolute top-3 right-3 rounded-full bg-red-900/50 p-2 text-white hover:bg-black/70"
|
|
201
|
-
onclick={() => (isMaximized = false)}
|
|
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}
|
|
228
|
-
</div>
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
{/if}
|
|
232
|
-
{/if}
|
|
1
|
+
<!-- CardMedia.svelte -->
|
|
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 {
|
|
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 = "h-32 w-full md: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;
|
|
65
|
+
}
|
|
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();
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{ rootMargin: "100px" },
|
|
78
|
+
);
|
|
79
|
+
observer.observe(containerEl);
|
|
80
|
+
return () => observer.disconnect();
|
|
81
|
+
});
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<svelte:window
|
|
85
|
+
onkeydown={isMaximized
|
|
86
|
+
? (e) => e.key === "Escape" && (isMaximized = false)
|
|
87
|
+
: undefined}
|
|
88
|
+
/>
|
|
89
|
+
|
|
90
|
+
{#snippet maximizeButton()}
|
|
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>
|
|
108
|
+
{/snippet}
|
|
109
|
+
|
|
110
|
+
{#if isLoading}
|
|
111
|
+
<Skeleton class="w-full h-12.5 md:h-50 bg-gray-500/90" />
|
|
112
|
+
{:else}
|
|
113
|
+
{#if imageUrl || videoUrl}
|
|
114
|
+
<div
|
|
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))}
|
|
126
|
+
>
|
|
127
|
+
{#if imageUrl}
|
|
128
|
+
<img
|
|
129
|
+
src={isVisible ? imageUrl : undefined}
|
|
130
|
+
loading="lazy"
|
|
131
|
+
decoding="async"
|
|
132
|
+
alt={imageUrl}
|
|
133
|
+
class="absolute inset-0 h-full w-full 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 h-full w-full 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}
|
|
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>
|
|
176
|
+
{/if}
|
|
177
|
+
{/if}
|
|
178
|
+
|
|
179
|
+
{#if showMaximize}
|
|
180
|
+
{@render maximizeButton()}
|
|
181
|
+
{/if}
|
|
182
|
+
</div>
|
|
183
|
+
{/if}
|
|
184
|
+
|
|
185
|
+
{#if isMaximized}
|
|
186
|
+
<div class="fixed inset-0 z-50">
|
|
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
|
|
199
|
+
type="button"
|
|
200
|
+
class="absolute top-3 right-3 rounded-full bg-red-900/50 p-2 text-white hover:bg-black/70"
|
|
201
|
+
onclick={() => (isMaximized = false)}
|
|
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}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
{/if}
|
|
232
|
+
{/if}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import Skeleton from "../../../ui/skeleton/skeleton.svelte";
|
|
3
|
-
|
|
4
|
-
let { title, isLoading, price } = $props();
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<div class="flex justify-between w-full items-center">
|
|
8
|
-
{#if title}
|
|
9
|
-
{#if isLoading}
|
|
10
|
-
<Skeleton class="h-4 w-30 rounded-lg" />
|
|
11
|
-
{:else}
|
|
12
|
-
<div class="font-semibold line-clamp-1 flex-nowrap">
|
|
13
|
-
{title}
|
|
14
|
-
</div>
|
|
15
|
-
{/if}
|
|
16
|
-
{/if}
|
|
17
|
-
<div class="flex justify-start gap-2">
|
|
18
|
-
{#if isLoading}
|
|
19
|
-
<Skeleton class="h-5 bg-black/10 w-25 my-2 mr-2" />
|
|
20
|
-
{:else if price}
|
|
21
|
-
<div class="text-lg font-semibold text-primary">{price}</div>
|
|
22
|
-
{/if}
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
1
|
+
<script>
|
|
2
|
+
import Skeleton from "../../../ui/skeleton/skeleton.svelte";
|
|
3
|
+
|
|
4
|
+
let { title, isLoading, price } = $props();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<div class="flex justify-between w-full items-center">
|
|
8
|
+
{#if title}
|
|
9
|
+
{#if isLoading}
|
|
10
|
+
<Skeleton class="h-4 w-30 rounded-lg" />
|
|
11
|
+
{:else}
|
|
12
|
+
<div class="font-semibold line-clamp-1 flex-nowrap">
|
|
13
|
+
{title}
|
|
14
|
+
</div>
|
|
15
|
+
{/if}
|
|
16
|
+
{/if}
|
|
17
|
+
<div class="flex justify-start gap-2">
|
|
18
|
+
{#if isLoading}
|
|
19
|
+
<Skeleton class="h-5 bg-black/10 w-25 my-2 mr-2" />
|
|
20
|
+
{:else if price}
|
|
21
|
+
<div class="text-lg font-semibold text-primary">{price}</div>
|
|
22
|
+
{/if}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
<!-- shared/TruncatableText.svelte -->
|
|
2
|
-
<script lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Clamps text to N lines; if it overflows, shows an indicator
|
|
5
|
-
* ("Ler texto completo") positioned absolutely at the end of the
|
|
6
|
-
* text, that opens the full text in a Popover.
|
|
7
|
-
* @component
|
|
8
|
-
*/
|
|
9
|
-
import * as Popover from "../../../ui/popover";
|
|
10
|
-
import { t } from "../../../../i18n";
|
|
11
|
-
|
|
12
|
-
type Props = { text: string; class?: string; lines?: number };
|
|
13
|
-
let { text, class: className = "", lines = 2 }: Props = $props();
|
|
14
|
-
|
|
15
|
-
let textEl: HTMLParagraphElement | undefined = $state();
|
|
16
|
-
let isTruncated = $state(false);
|
|
17
|
-
|
|
18
|
-
$effect(() => {
|
|
19
|
-
if (!textEl) return;
|
|
20
|
-
const check = () =>
|
|
21
|
-
(isTruncated =
|
|
22
|
-
(textEl?.scrollHeight ?? 0) > (textEl?.clientHeight ?? 0) + 1);
|
|
23
|
-
// requestAnimationFrame garante que o layout com o clamp já foi aplicado
|
|
24
|
-
// antes de medirmos — medir no mesmo tick pode dar valores desatualizados.
|
|
25
|
-
requestAnimationFrame(check);
|
|
26
|
-
const ro = new ResizeObserver(check);
|
|
27
|
-
ro.observe(textEl);
|
|
28
|
-
return () => ro.disconnect();
|
|
29
|
-
});
|
|
30
|
-
</script>
|
|
31
|
-
|
|
32
|
-
<div class="relative">
|
|
33
|
-
<p bind:this={textEl} class="line-clamp-3 {className}">
|
|
34
|
-
{text}
|
|
35
|
-
</p>
|
|
36
|
-
|
|
37
|
-
{#if isTruncated}
|
|
38
|
-
<Popover.Root>
|
|
39
|
-
<Popover.Trigger
|
|
40
|
-
class="absolute bottom-0 right-0 bg-background pl-1 text-[11px] font-medium text-primary underline underline-offset-2 cursor-pointer"
|
|
41
|
-
>
|
|
42
|
-
{$t("label.view.full")}
|
|
43
|
-
</Popover.Trigger>
|
|
44
|
-
<Popover.Content>
|
|
45
|
-
<p class="max-w-xs text-justify text-[12px]">{text}</p>
|
|
46
|
-
</Popover.Content>
|
|
47
|
-
</Popover.Root>
|
|
48
|
-
{/if}
|
|
49
|
-
</div>
|
|
1
|
+
<!-- shared/TruncatableText.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Clamps text to N lines; if it overflows, shows an indicator
|
|
5
|
+
* ("Ler texto completo") positioned absolutely at the end of the
|
|
6
|
+
* text, that opens the full text in a Popover.
|
|
7
|
+
* @component
|
|
8
|
+
*/
|
|
9
|
+
import * as Popover from "../../../ui/popover";
|
|
10
|
+
import { t } from "../../../../i18n";
|
|
11
|
+
|
|
12
|
+
type Props = { text: string; class?: string; lines?: number };
|
|
13
|
+
let { text, class: className = "", lines = 2 }: Props = $props();
|
|
14
|
+
|
|
15
|
+
let textEl: HTMLParagraphElement | undefined = $state();
|
|
16
|
+
let isTruncated = $state(false);
|
|
17
|
+
|
|
18
|
+
$effect(() => {
|
|
19
|
+
if (!textEl) return;
|
|
20
|
+
const check = () =>
|
|
21
|
+
(isTruncated =
|
|
22
|
+
(textEl?.scrollHeight ?? 0) > (textEl?.clientHeight ?? 0) + 1);
|
|
23
|
+
// requestAnimationFrame garante que o layout com o clamp já foi aplicado
|
|
24
|
+
// antes de medirmos — medir no mesmo tick pode dar valores desatualizados.
|
|
25
|
+
requestAnimationFrame(check);
|
|
26
|
+
const ro = new ResizeObserver(check);
|
|
27
|
+
ro.observe(textEl);
|
|
28
|
+
return () => ro.disconnect();
|
|
29
|
+
});
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div class="relative">
|
|
33
|
+
<p bind:this={textEl} class="line-clamp-3 {className}">
|
|
34
|
+
{text}
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
{#if isTruncated}
|
|
38
|
+
<Popover.Root>
|
|
39
|
+
<Popover.Trigger
|
|
40
|
+
class="absolute bottom-0 right-0 bg-background pl-1 text-[11px] font-medium text-primary underline underline-offset-2 cursor-pointer"
|
|
41
|
+
>
|
|
42
|
+
{$t("label.view.full")}
|
|
43
|
+
</Popover.Trigger>
|
|
44
|
+
<Popover.Content>
|
|
45
|
+
<p class="max-w-xs text-justify text-[12px]">{text}</p>
|
|
46
|
+
</Popover.Content>
|
|
47
|
+
</Popover.Root>
|
|
48
|
+
{/if}
|
|
49
|
+
</div>
|
|
@@ -90,10 +90,14 @@ export interface CardProfileProps {
|
|
|
90
90
|
isTagBorderBottom?: boolean;
|
|
91
91
|
/** Classe CSS personalizada para botão de perfil */
|
|
92
92
|
buttonProfileClass?: string;
|
|
93
|
+
/** Classe CSS personalizada para o botão de detalhes */
|
|
94
|
+
buttonDetailsClass?: string;
|
|
93
95
|
/** Callback ao clicar no botão favorito */
|
|
94
96
|
onFavoriteClick?: (id: string | number) => void;
|
|
95
97
|
/** Callback ao clicar no botão perfil */
|
|
96
98
|
onButtonProfile?: (id: string | number) => void;
|
|
99
|
+
/** Callback acionado ao clicar no botão de detalhes */
|
|
100
|
+
onButtonDetails?: (id: string | number) => void;
|
|
97
101
|
/** Callback ao clicar no botão email */
|
|
98
102
|
onEmailClick?: (id: string | number) => void;
|
|
99
103
|
/** Callback ao clicar no botão whatsapp */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ItemCarousel } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Props do CarouselBadge — carousel de badges/categorias
|
|
4
|
+
* com orientação horizontal ou vertical.
|
|
5
|
+
*/
|
|
6
|
+
export interface CarouselBadgeProps {
|
|
7
|
+
/** Lista de itens do carousel de badges */
|
|
8
|
+
items: ItemCarousel[];
|
|
9
|
+
/** Orientação do layout: horizontal (padrão) ou vertical */
|
|
10
|
+
orientation?: "horizontal" | "vertical";
|
|
11
|
+
/** Classe CSS personalizada para a imagem */
|
|
12
|
+
imageClass?: string;
|
|
13
|
+
/** Classe CSS personalizada para o ícone */
|
|
14
|
+
iconClass?: string;
|
|
15
|
+
/** Classe CSS personalizada para o label */
|
|
16
|
+
labelClass?: string;
|
|
17
|
+
/** Classe CSS personalizada para o estado ativo */
|
|
18
|
+
activeClass?: string;
|
|
19
|
+
/** Estado de carregamento (skeleton) */
|
|
20
|
+
isLoading?: boolean;
|
|
21
|
+
itemClass?: string;
|
|
22
|
+
btnNavClass?: string;
|
|
23
|
+
isBorderInline?: boolean;
|
|
24
|
+
showButton?: boolean;
|
|
25
|
+
itemStyle?: 'BORDER' | 'INLINE' | 'DEFAULT';
|
|
26
|
+
onClick?: (value: string | number) => void;
|
|
27
|
+
}
|