negodesign 0.0.32 → 0.0.34
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/card/profile/01/CardProfile01.svelte +92 -72
- package/dist/components/core/card/profile/02/CardProfile02.svelte +93 -66
- package/dist/components/core/card/profile/CardProfile.svelte +1 -1
- package/dist/components/core/card/profile/data.js +10 -0
- package/dist/components/core/card/shared/CardDescription.svelte +6 -3
- package/dist/components/core/card/shared/CardDescription.svelte.d.ts +1 -0
- package/dist/components/core/card/shared/CardPhoneOrWhatsapp.svelte +38 -0
- package/dist/components/core/card/shared/CardPhoneOrWhatsapp.svelte.d.ts +4 -0
- package/dist/components/core/card/shared/CardStartPrice.svelte +1 -1
- package/dist/components/core/card/shared/CardTags.svelte +2 -2
- package/dist/components/core/card/shared/TruncatableText.svelte +39 -47
- package/dist/components/core/card/shared/TruncatableText.svelte.d.ts +1 -1
- package/dist/components/core/card/types.d.ts +19 -4
- package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte +1 -1
- package/dist/components/core/carousel/grid/profile/types.d.ts +9 -0
- package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte +28 -4
- package/dist/components/core/carousel/promotion/ui/CarouselPromotion.svelte +1 -1
- package/dist/globals.css +40 -19
- package/dist/styles/animate.min.css +0 -2
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Card component with a title and content.
|
|
4
4
|
* @component
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { UserIcon } from "@hugeicons/core-free-icons";
|
|
7
7
|
import { t } from "../../../../../i18n";
|
|
8
8
|
import type { CardProfileProps } from "../../types";
|
|
9
9
|
import CardStarRating from "../../shared/CardStarRating.svelte";
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
import CardButton from "../../shared/CardButton.svelte";
|
|
13
13
|
import CardFavorite from "../../shared/CardFavorite.svelte";
|
|
14
14
|
import Skeleton from "../../../../ui/skeleton/skeleton.svelte";
|
|
15
|
+
import CardPhoneOrWhatsapp from "../../shared/CardPhoneOrWhatsapp.svelte";
|
|
16
|
+
import CardThumbnailVideo from "../../shared/CardThumbnailVideo.svelte";
|
|
17
|
+
import ImgPlaceholder from "../../../../../assets/placeholder-image.png";
|
|
15
18
|
|
|
16
19
|
let {
|
|
17
20
|
id,
|
|
@@ -24,93 +27,110 @@
|
|
|
24
27
|
isTagBorderBottom = true,
|
|
25
28
|
isFavorite = false,
|
|
26
29
|
buttonProfileClass,
|
|
27
|
-
buttonDetailsClass,
|
|
28
30
|
isLoading = false,
|
|
31
|
+
imageUrl,
|
|
32
|
+
videoUrl,
|
|
33
|
+
isImageButtonMaximized = false,
|
|
34
|
+
isVideoButtonMaximized = false,
|
|
35
|
+
isDescriptionIcon = false,
|
|
36
|
+
isDescriptionLabel = false,
|
|
37
|
+
onEmailClick,
|
|
38
|
+
onWhatsappClick,
|
|
29
39
|
onFavoriteClick,
|
|
30
40
|
onButtonProfile,
|
|
31
|
-
onButtonDetails,
|
|
32
41
|
}: CardProfileProps = $props();
|
|
33
|
-
|
|
34
|
-
let isShowButtonProfileAndDetails = $derived(
|
|
35
|
-
onButtonProfile != null && onButtonDetails != null,
|
|
36
|
-
);
|
|
37
42
|
</script>
|
|
38
43
|
|
|
39
44
|
<article
|
|
40
|
-
class="border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-transparent rounded-lg
|
|
45
|
+
class="border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-transparent rounded-lg relative"
|
|
41
46
|
>
|
|
42
|
-
<div class="
|
|
43
|
-
<div class="flex justify-
|
|
44
|
-
<div class="flex justify-center items-center">
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
<div class="p-5">
|
|
48
|
+
<div class="flex justify-between items-center w-full">
|
|
49
|
+
<div class="w-full flex flex-col justify-center items-center relative">
|
|
50
|
+
<div>
|
|
51
|
+
{#if isLoading}
|
|
52
|
+
<Skeleton
|
|
53
|
+
class="w-12 h-12 md:w-14 md:h-14 lg:w-20 lg:h-20 rounded-full bg-gray-300"
|
|
54
|
+
/>
|
|
55
|
+
{:else if imageUrl}
|
|
56
|
+
<img
|
|
57
|
+
src={imageUrl}
|
|
58
|
+
alt={imageUrl}
|
|
59
|
+
class="w-12 h-12 md:w-14 md:h-14 lg:w-20 lg:h-20 rounded-full"
|
|
60
|
+
onerror={(e) =>
|
|
61
|
+
((e.target as HTMLImageElement).src = ImgPlaceholder)}
|
|
62
|
+
/>
|
|
63
|
+
{/if}
|
|
64
|
+
</div>
|
|
65
|
+
<div>
|
|
66
|
+
{#if title}
|
|
67
|
+
<div class="text-center font-bold">{title}</div>
|
|
68
|
+
{:else if isLoading}
|
|
69
|
+
<Skeleton class="w-40 h-5 rounded-lg bg-gray-400 mb-0.5" />
|
|
70
|
+
{/if}
|
|
71
|
+
</div>
|
|
72
|
+
<div>
|
|
73
|
+
{#if startNumber}
|
|
74
|
+
<CardStarRating {startNumber} {startMax} {isLoading} />
|
|
75
|
+
{:else if isLoading}
|
|
76
|
+
<Skeleton class="w-20 h-4 rounded-lg bg-gray-300 mt-0.5" />
|
|
77
|
+
{/if}
|
|
78
|
+
</div>
|
|
50
79
|
</div>
|
|
51
|
-
<div class="flex-col">
|
|
52
|
-
{#if title}
|
|
53
|
-
<div class="text-center font-bold text-lg">{title}</div>
|
|
54
|
-
{:else if isLoading}
|
|
55
|
-
<Skeleton class="w-40 h-5 rounded-lg bg-gray-400 mb-0.5" />
|
|
56
|
-
{/if}
|
|
57
80
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
81
|
+
<div class="flex justify-end items-center absolute top-1 right-1">
|
|
82
|
+
<CardFavorite
|
|
83
|
+
{id}
|
|
84
|
+
{isFavorite}
|
|
85
|
+
onFavoriteClick={() => onFavoriteClick!(id)}
|
|
86
|
+
{isLoading}
|
|
87
|
+
/>
|
|
63
88
|
</div>
|
|
64
89
|
</div>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
onFavoriteClick={() => onFavoriteClick!(id)}
|
|
90
|
+
|
|
91
|
+
<div class="my-2">
|
|
92
|
+
<CardDescription
|
|
93
|
+
{content}
|
|
70
94
|
{isLoading}
|
|
95
|
+
{isDescriptionIcon}
|
|
96
|
+
{isDescriptionLabel}
|
|
97
|
+
lines={tags?.length == 0 ? 3 : 2}
|
|
71
98
|
/>
|
|
72
99
|
</div>
|
|
73
|
-
</div>
|
|
74
100
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
101
|
+
<CardTags
|
|
102
|
+
className="my-4"
|
|
103
|
+
tags={tags ?? []}
|
|
104
|
+
{isTagBorderBottom}
|
|
105
|
+
{isLoading}
|
|
106
|
+
/>
|
|
78
107
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
108
|
+
<CardThumbnailVideo
|
|
109
|
+
{imageUrl}
|
|
110
|
+
{videoUrl}
|
|
111
|
+
{isImageButtonMaximized}
|
|
112
|
+
{isVideoButtonMaximized}
|
|
113
|
+
{isLoading}
|
|
114
|
+
class="md:h-32.5 lg:h-44"
|
|
115
|
+
/>
|
|
85
116
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
className={buttonDetailsClass}
|
|
106
|
-
text={$t("label.more.details")}
|
|
107
|
-
onClick={onButtonDetails}
|
|
108
|
-
isFlex={isShowButtonProfileAndDetails}
|
|
109
|
-
{isLoading}
|
|
110
|
-
/>
|
|
111
|
-
{:else if isLoading}
|
|
112
|
-
<Skeleton class="w-20 h-4 rounded-lg bg-gray-400 mt-0.5" />
|
|
113
|
-
{/if}
|
|
114
|
-
</div>
|
|
115
|
-
{/if}
|
|
117
|
+
{#if onButtonProfile}
|
|
118
|
+
<div class="flex justify-center items-center gap-2 w-full mt-3 -mb-1">
|
|
119
|
+
{#if onButtonProfile}
|
|
120
|
+
<CardButton
|
|
121
|
+
{id}
|
|
122
|
+
icon={UserIcon}
|
|
123
|
+
className={buttonProfileClass}
|
|
124
|
+
text={$t("label.view.profile")}
|
|
125
|
+
onClick={onButtonProfile}
|
|
126
|
+
{isLoading}
|
|
127
|
+
isFlex
|
|
128
|
+
/>
|
|
129
|
+
{:else if isLoading}
|
|
130
|
+
<Skeleton class="w-20 h-4 rounded-lg bg-gray-400 mt-0.5" />
|
|
131
|
+
{/if}
|
|
132
|
+
</div>
|
|
133
|
+
{/if}
|
|
134
|
+
</div>
|
|
135
|
+
<CardPhoneOrWhatsapp {id} {onEmailClick} {onWhatsappClick} />
|
|
116
136
|
</article>
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
import CardButton from "../../shared/CardButton.svelte";
|
|
13
13
|
import CardFavorite from "../../shared/CardFavorite.svelte";
|
|
14
14
|
import Skeleton from "../../../../ui/skeleton/skeleton.svelte";
|
|
15
|
+
import CardThumbnailVideo from "../../shared/CardThumbnailVideo.svelte";
|
|
16
|
+
import CardPhoneOrWhatsapp from "../../shared/CardPhoneOrWhatsapp.svelte";
|
|
17
|
+
import ImgPlaceholder from "../../../../../assets/placeholder-image.png";
|
|
18
|
+
|
|
15
19
|
let {
|
|
16
20
|
id,
|
|
17
21
|
tags,
|
|
@@ -24,45 +28,56 @@
|
|
|
24
28
|
isTagBorderBottom = true,
|
|
25
29
|
isFavorite = false,
|
|
26
30
|
buttonProfileClass,
|
|
27
|
-
|
|
31
|
+
onEmailClick,
|
|
32
|
+
onWhatsappClick,
|
|
28
33
|
isLoading = false,
|
|
29
34
|
onFavoriteClick,
|
|
30
35
|
onButtonProfile,
|
|
31
|
-
|
|
36
|
+
imageUrl,
|
|
37
|
+
videoUrl,
|
|
38
|
+
isImageButtonMaximized = false,
|
|
39
|
+
isVideoButtonMaximized = false,
|
|
40
|
+
isDescriptionLabel = false,
|
|
41
|
+
isDescriptionIcon = false,
|
|
32
42
|
}: CardProfileProps = $props();
|
|
33
|
-
|
|
34
|
-
let isShowButtonProfileAndDetails = $derived(
|
|
35
|
-
onButtonProfile != null && onButtonDetails != null,
|
|
36
|
-
);
|
|
37
43
|
</script>
|
|
38
44
|
|
|
39
45
|
<article
|
|
40
|
-
class="border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-transparent rounded-lg
|
|
46
|
+
class="border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-transparent rounded-lg space-y-1 md:space-y-3 relative {className}"
|
|
41
47
|
>
|
|
42
|
-
<div class="
|
|
43
|
-
<div class="flex
|
|
44
|
-
<div class="flex justify-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
<div class="p-5">
|
|
49
|
+
<div class="flex justify-start items-center w-full relative">
|
|
50
|
+
<div class="flex justify-start gap-2">
|
|
51
|
+
<div class="flex justify-center items-center">
|
|
52
|
+
{#if isLoading}
|
|
53
|
+
<Skeleton class="w-12 h-12 rounded-lg bg-gray-400 mt-0.5" />
|
|
54
|
+
{:else if imageUrl}
|
|
55
|
+
<img
|
|
56
|
+
src={imageUrl}
|
|
57
|
+
alt={imageUrl}
|
|
58
|
+
class="w-12 h-12 rounded-lg"
|
|
59
|
+
onerror={(e) =>
|
|
60
|
+
((e.target as HTMLImageElement).src = ImgPlaceholder)}
|
|
61
|
+
/>
|
|
62
|
+
{/if}
|
|
63
|
+
</div>
|
|
64
|
+
<div class="flex flex-col justify-start items-start">
|
|
65
|
+
{#if title}
|
|
66
|
+
<div class="text-left font-bold">{title}</div>
|
|
67
|
+
{:else if isLoading}
|
|
68
|
+
<Skeleton class="w-20 h-4 rounded-lg bg-gray-400 mt-0.5" />
|
|
69
|
+
{/if}
|
|
57
70
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
{#if startNumber}
|
|
72
|
+
<CardStarRating {startNumber} {startMax} {isLoading} />
|
|
73
|
+
{:else if isLoading}
|
|
74
|
+
<Skeleton class="w-20 h-4 rounded-lg bg-gray-300 mt-0.5" />
|
|
75
|
+
{/if}
|
|
76
|
+
</div>
|
|
63
77
|
</div>
|
|
64
78
|
</div>
|
|
65
|
-
|
|
79
|
+
|
|
80
|
+
<div class="flex justify-end items-center absolute top-1 right-1">
|
|
66
81
|
<CardFavorite
|
|
67
82
|
{id}
|
|
68
83
|
{isFavorite}
|
|
@@ -70,45 +85,57 @@
|
|
|
70
85
|
{isLoading}
|
|
71
86
|
/>
|
|
72
87
|
</div>
|
|
73
|
-
</div>
|
|
74
88
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
<div class="mb-4">
|
|
90
|
+
<CardDescription
|
|
91
|
+
{content}
|
|
92
|
+
{isLoading}
|
|
93
|
+
{isDescriptionLabel}
|
|
94
|
+
{isDescriptionIcon}
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
78
97
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
{#if tags && tags.length > 0}
|
|
99
|
+
<CardTags
|
|
100
|
+
className="my-4"
|
|
101
|
+
tags={tags ?? []}
|
|
102
|
+
{isTagBorderBottom}
|
|
103
|
+
{isLoading}
|
|
104
|
+
/>
|
|
105
|
+
{:else}
|
|
106
|
+
<CardTags
|
|
107
|
+
className="my-4"
|
|
108
|
+
tags={[{ text: "- - -" }]}
|
|
109
|
+
{isTagBorderBottom}
|
|
110
|
+
{isLoading}
|
|
111
|
+
/>
|
|
112
|
+
{/if}
|
|
85
113
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
{/if}
|
|
114
|
+
<CardThumbnailVideo
|
|
115
|
+
{imageUrl}
|
|
116
|
+
{videoUrl}
|
|
117
|
+
{isImageButtonMaximized}
|
|
118
|
+
{isVideoButtonMaximized}
|
|
119
|
+
{isLoading}
|
|
120
|
+
class="md:h-32.5 lg:h-44"
|
|
121
|
+
/>
|
|
122
|
+
|
|
123
|
+
{#if onButtonProfile}
|
|
124
|
+
<div class="flex justify-center items-center gap-2 mt-2 -mb-2 w-full">
|
|
125
|
+
{#if onButtonProfile}
|
|
126
|
+
<CardButton
|
|
127
|
+
{id}
|
|
128
|
+
icon={UserIcon}
|
|
129
|
+
className={buttonProfileClass}
|
|
130
|
+
text={$t("label.view.profile")}
|
|
131
|
+
onClick={onButtonProfile}
|
|
132
|
+
isFlex
|
|
133
|
+
/>
|
|
134
|
+
{:else if isLoading}
|
|
135
|
+
<Skeleton class="w-20 h-4 rounded-lg bg-gray-400 mt-0.5" />
|
|
136
|
+
{/if}
|
|
137
|
+
</div>
|
|
138
|
+
{/if}
|
|
139
|
+
</div>
|
|
140
|
+
<CardPhoneOrWhatsapp {id} {onEmailClick} {onWhatsappClick} />
|
|
114
141
|
</article>
|
|
@@ -8,6 +8,8 @@ const items = [
|
|
|
8
8
|
startNumber: 5,
|
|
9
9
|
startMax: 5,
|
|
10
10
|
isFavorite: true,
|
|
11
|
+
imageUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
12
|
+
videoUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
11
13
|
tags: [
|
|
12
14
|
{
|
|
13
15
|
icon: GlobalIcon,
|
|
@@ -30,6 +32,8 @@ const items = [
|
|
|
30
32
|
content: 'Suítes modernas no coração da cidade, com spa completo e serviço de quarto 24 horas.',
|
|
31
33
|
startNumber: 4,
|
|
32
34
|
startMax: 5,
|
|
35
|
+
imageUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
36
|
+
videoUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
33
37
|
tags: [
|
|
34
38
|
{
|
|
35
39
|
icon: GlobalIcon,
|
|
@@ -57,6 +61,8 @@ const items = [
|
|
|
57
61
|
startNumber: 5,
|
|
58
62
|
startMax: 5,
|
|
59
63
|
isFavorite: true,
|
|
64
|
+
imageUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
65
|
+
videoUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
60
66
|
tags: [
|
|
61
67
|
{
|
|
62
68
|
icon: GlobalIcon,
|
|
@@ -75,6 +81,8 @@ const items = [
|
|
|
75
81
|
content: 'Localização privilegiada no centro urbano, ideal para viagens de negócios, com centro de conferências.',
|
|
76
82
|
startNumber: 3,
|
|
77
83
|
startMax: 5,
|
|
84
|
+
imageUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
85
|
+
videoUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
78
86
|
tags: [
|
|
79
87
|
{
|
|
80
88
|
icon: NecklaceIcon,
|
|
@@ -102,6 +110,8 @@ const items = [
|
|
|
102
110
|
startNumber: 4,
|
|
103
111
|
startMax: 5,
|
|
104
112
|
isFavorite: true,
|
|
113
|
+
imageUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
114
|
+
videoUrl: 'https://api.dicebear.com/9.x/initials/svg?seed=Hotel%20Baía%20Azul&backgroundColor=1a2420&textColor=e3b563',
|
|
105
115
|
},
|
|
106
116
|
];
|
|
107
117
|
export default items;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import Skeleton from "../../../ui/skeleton/skeleton.svelte";
|
|
7
7
|
|
|
8
8
|
type CardDescriptionProps = {
|
|
9
|
+
lines?: number;
|
|
9
10
|
content?: string;
|
|
10
11
|
isLoading?: boolean;
|
|
11
12
|
isDescriptionIcon?: boolean;
|
|
@@ -13,9 +14,10 @@
|
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
let {
|
|
17
|
+
lines = 2,
|
|
16
18
|
content,
|
|
17
19
|
isLoading = false,
|
|
18
|
-
isDescriptionIcon =
|
|
20
|
+
isDescriptionIcon = false,
|
|
19
21
|
isDescriptionLabel = true,
|
|
20
22
|
}: CardDescriptionProps = $props();
|
|
21
23
|
</script>
|
|
@@ -27,12 +29,13 @@
|
|
|
27
29
|
<div class="mb-1 text-[14px] md:text-[15px] flex items-center gap-1">
|
|
28
30
|
{#if isDescriptionIcon}
|
|
29
31
|
<HugeiconsIcon icon={Message01Icon} class="h-4 w-4" />
|
|
30
|
-
{/if}
|
|
32
|
+
{/if}
|
|
31
33
|
<div>{$t("label.description")}</div>
|
|
32
34
|
</div>
|
|
33
35
|
{/if}
|
|
34
36
|
<TruncatableText
|
|
35
37
|
text={content}
|
|
36
|
-
|
|
38
|
+
{lines}
|
|
39
|
+
class="text-justify text-[13px] md:text-[14px] text-gray-800 dark:text-gray-50 mt-3 my-2"
|
|
37
40
|
/>
|
|
38
41
|
{/if}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Mail01Icon, WhatsappIcon } from "@hugeicons/core-free-icons";
|
|
3
|
+
import { HugeiconsIcon } from "@hugeicons/svelte";
|
|
4
|
+
import type { CardPhoneOrWhatsappProps } from "../types";
|
|
5
|
+
|
|
6
|
+
let { id, onEmailClick, onWhatsappClick }: CardPhoneOrWhatsappProps =
|
|
7
|
+
$props();
|
|
8
|
+
|
|
9
|
+
let isFlex = $derived(onEmailClick && onWhatsappClick);
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div class="flex border-t border-gray-300 dark:border-gray-700">
|
|
13
|
+
{#if onEmailClick}
|
|
14
|
+
<button
|
|
15
|
+
class="flex-1 flex justify-center items-center gap-2 p-3 hover:bg-red-800 hover:text-white cursor-pointer rounded-bl-lg {isFlex
|
|
16
|
+
? 'rounded-br-lg'
|
|
17
|
+
: ''}"
|
|
18
|
+
onclick={() => onEmailClick?.(id)}
|
|
19
|
+
>
|
|
20
|
+
<HugeiconsIcon icon={Mail01Icon} size={20} />
|
|
21
|
+
<span>Email</span>
|
|
22
|
+
</button>
|
|
23
|
+
{/if}
|
|
24
|
+
{#if isFlex}
|
|
25
|
+
<div class="border-l border-gray-300 dark:border-gray-700"></div>
|
|
26
|
+
{/if}
|
|
27
|
+
{#if onWhatsappClick}
|
|
28
|
+
<button
|
|
29
|
+
class="flex-1 flex justify-center items-center gap-2 p-3 hover:bg-green-600 hover:text-white cursor-pointer rounded-br-lg {isFlex
|
|
30
|
+
? 'rounded-bl-lg'
|
|
31
|
+
: ''}"
|
|
32
|
+
onclick={() => onWhatsappClick?.(id)}
|
|
33
|
+
>
|
|
34
|
+
<HugeiconsIcon icon={WhatsappIcon} size={20} />
|
|
35
|
+
<span>Whatsapp</span>
|
|
36
|
+
</button>
|
|
37
|
+
{/if}
|
|
38
|
+
</div>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
/>
|
|
20
20
|
{/if}
|
|
21
21
|
{#if newPrice && oldPrice}
|
|
22
|
-
<div class="z-20 flex justify-
|
|
22
|
+
<div class="z-20 flex justify-start items-center">
|
|
23
23
|
<div class="font-extrabold text-lg">{newPrice}</div>
|
|
24
24
|
<div class="mx-2">/</div>
|
|
25
25
|
<div class="font-semibold line-through text-red-600 text-[10px]">
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
const {
|
|
15
|
-
tags,
|
|
15
|
+
tags = [],
|
|
16
16
|
className,
|
|
17
17
|
isTagBorderBottom,
|
|
18
18
|
isWrap = false,
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
</div>
|
|
56
56
|
{:else}
|
|
57
57
|
<div
|
|
58
|
-
class="flex overflow-x-auto no-scrollbar w-full
|
|
58
|
+
class="flex overflow-x-auto no-scrollbar w-full min-w-0
|
|
59
59
|
{isJustify ? 'justify-between gap-3' : 'gap-4 md:gap-5 lg:gap-10'}
|
|
60
60
|
{className}"
|
|
61
61
|
>
|
|
@@ -1,56 +1,48 @@
|
|
|
1
1
|
<!-- shared/TruncatableText.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
type Props = { text: string; class?: string; lines?: number };
|
|
13
|
+
let { text, class: className = "", lines = 2 }: Props = $props();
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const clampClasses = {
|
|
18
|
-
2: "line-clamp-2",
|
|
19
|
-
3: "line-clamp-3",
|
|
20
|
-
4: "line-clamp-4",
|
|
21
|
-
} as const;
|
|
15
|
+
let textEl: HTMLParagraphElement | undefined = $state();
|
|
16
|
+
let isTruncated = $state(false);
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
ro.observe(textEl);
|
|
35
|
-
return () => ro.disconnect();
|
|
36
|
-
});
|
|
18
|
+
$effect(() => {
|
|
19
|
+
if (!textEl) return;
|
|
20
|
+
const check = () =>
|
|
21
|
+
(isTruncated = textEl!.scrollHeight > textEl!.clientHeight + 1);
|
|
22
|
+
// requestAnimationFrame garante que o layout com o clamp já foi aplicado
|
|
23
|
+
// antes de medirmos — medir no mesmo tick pode dar valores desatualizados.
|
|
24
|
+
requestAnimationFrame(check);
|
|
25
|
+
const ro = new ResizeObserver(check);
|
|
26
|
+
ro.observe(textEl);
|
|
27
|
+
return () => ro.disconnect();
|
|
28
|
+
});
|
|
37
29
|
</script>
|
|
38
30
|
|
|
39
31
|
<div class="relative">
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
<p bind:this={textEl} class="line-clamp-{lines} {className}">
|
|
33
|
+
{text}
|
|
34
|
+
</p>
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
</div>
|
|
36
|
+
{#if isTruncated}
|
|
37
|
+
<Popover.Root>
|
|
38
|
+
<Popover.Trigger
|
|
39
|
+
class="absolute bottom-0 right-0 bg-background pl-1 text-[11px] font-medium text-primary underline underline-offset-2 cursor-pointer"
|
|
40
|
+
>
|
|
41
|
+
{$t("label.view.full")}
|
|
42
|
+
</Popover.Trigger>
|
|
43
|
+
<Popover.Content>
|
|
44
|
+
<p class="max-w-xs text-justify text-[12px]">{text}</p>
|
|
45
|
+
</Popover.Content>
|
|
46
|
+
</Popover.Root>
|
|
47
|
+
{/if}
|
|
48
|
+
</div>
|
|
@@ -86,14 +86,24 @@ export interface CardProfileProps {
|
|
|
86
86
|
isTagBorderBottom?: boolean;
|
|
87
87
|
/** Classe CSS personalizada para botão de perfil */
|
|
88
88
|
buttonProfileClass?: string;
|
|
89
|
-
/** Classe CSS personalizada para botão de detalhes */
|
|
90
|
-
buttonDetailsClass?: string;
|
|
91
89
|
/** Callback ao clicar no botão favorito */
|
|
92
90
|
onFavoriteClick?: (id: string | number) => void;
|
|
93
91
|
/** Callback ao clicar no botão perfil */
|
|
94
92
|
onButtonProfile?: (id: string | number) => void;
|
|
95
|
-
/** Callback ao clicar no botão
|
|
96
|
-
|
|
93
|
+
/** Callback ao clicar no botão email */
|
|
94
|
+
onEmailClick?: (id: string | number) => void;
|
|
95
|
+
/** Callback ao clicar no botão whatsapp */
|
|
96
|
+
onWhatsappClick?: (id: string | number) => void;
|
|
97
|
+
/** URL da foto/imagem do perfil */
|
|
98
|
+
imageUrl?: string;
|
|
99
|
+
/** URL do vídeo do perfil */
|
|
100
|
+
videoUrl?: string;
|
|
101
|
+
/** Indica se o botão de maximizar imagem está ativo */
|
|
102
|
+
isImageButtonMaximized?: boolean;
|
|
103
|
+
/** Indica se o botão de maximizar vídeo está ativo */
|
|
104
|
+
isVideoButtonMaximized?: boolean;
|
|
105
|
+
isDescriptionIcon?: boolean;
|
|
106
|
+
isDescriptionLabel?: boolean;
|
|
97
107
|
}
|
|
98
108
|
/**
|
|
99
109
|
* Props do CardPromotion — card de promoção com preço antigo/novo,
|
|
@@ -177,3 +187,8 @@ export interface CardHighlightProps {
|
|
|
177
187
|
/** Callback ao clicar no botão principal */
|
|
178
188
|
onClickBtn?: (id: string | number) => void;
|
|
179
189
|
}
|
|
190
|
+
export interface CardPhoneOrWhatsappProps {
|
|
191
|
+
id: string | number;
|
|
192
|
+
onEmailClick?: (id: string | number) => void;
|
|
193
|
+
onWhatsappClick?: (id: string | number) => void;
|
|
194
|
+
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<CarouselHeader {...headerProps}>
|
|
22
22
|
<CarouselGridSlot {slotProps} {gridClass} totalItems={items.length}>
|
|
23
23
|
{#if isLoading}
|
|
24
|
-
{#each Array.from( { length: responsive.isMobile ?
|
|
24
|
+
{#each Array.from( { length: responsive.isMobile ? 3 : 6 }, ) as _, i (`loading-${i}`)}
|
|
25
25
|
{#if responsive.isMobile}
|
|
26
26
|
<Carousel.Item class="pl-2 basis-auto">
|
|
27
27
|
<CardMedia id={i} {variant} isLoading />
|
|
@@ -18,4 +18,13 @@ export interface CarouselGridProfileProps {
|
|
|
18
18
|
items: CardProfileProps[];
|
|
19
19
|
/** Estado de carregamento (skeleton) */
|
|
20
20
|
isLoading?: boolean;
|
|
21
|
+
isDescriptionIcon?: boolean;
|
|
22
|
+
isDescriptionLabel?: boolean;
|
|
23
|
+
onFavoriteClick?: (id: string | number) => void;
|
|
24
|
+
/** Callback ao clicar no botão perfil */
|
|
25
|
+
onButtonProfile?: (id: string | number) => void;
|
|
26
|
+
/** Callback ao clicar no botão email */
|
|
27
|
+
onEmailClick?: (id: string | number) => void;
|
|
28
|
+
/** Callback ao clicar no botão whatsapp */
|
|
29
|
+
onWhatsappClick?: (id: string | number) => void;
|
|
21
30
|
}
|
|
@@ -9,10 +9,16 @@
|
|
|
9
9
|
const {
|
|
10
10
|
headerProps,
|
|
11
11
|
slotProps,
|
|
12
|
-
gridClass,
|
|
12
|
+
gridClass = "lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5",
|
|
13
13
|
items,
|
|
14
14
|
variant,
|
|
15
15
|
isLoading = false,
|
|
16
|
+
isDescriptionIcon = false,
|
|
17
|
+
isDescriptionLabel = false,
|
|
18
|
+
onEmailClick,
|
|
19
|
+
onWhatsappClick,
|
|
20
|
+
onButtonProfile,
|
|
21
|
+
onFavoriteClick,
|
|
16
22
|
}: CarouselGridProfileProps = $props();
|
|
17
23
|
|
|
18
24
|
const responsive = useDevice();
|
|
@@ -21,7 +27,7 @@
|
|
|
21
27
|
<CarouselHeader {...headerProps}>
|
|
22
28
|
<CarouselGridSlot {slotProps} {gridClass} totalItems={items.length}>
|
|
23
29
|
{#if isLoading}
|
|
24
|
-
{#each Array.from( { length: responsive.isMobile ?
|
|
30
|
+
{#each Array.from( { length: responsive.isMobile ? 3 : 6 }, ) as _, i (`loading-${i}`)}
|
|
25
31
|
{#if responsive.isMobile}
|
|
26
32
|
<Carousel.Item class="pl-2 basis-auto">
|
|
27
33
|
<CardProfile id={i} {variant} isLoading />
|
|
@@ -36,11 +42,29 @@
|
|
|
36
42
|
{#each items as item, i (`profile-${item.id ?? i}`)}
|
|
37
43
|
{#if responsive.isMobile}
|
|
38
44
|
<Carousel.Item class="pl-2 basis-auto">
|
|
39
|
-
<CardProfile
|
|
45
|
+
<CardProfile
|
|
46
|
+
{...item}
|
|
47
|
+
{variant}
|
|
48
|
+
{isDescriptionIcon}
|
|
49
|
+
{isDescriptionLabel}
|
|
50
|
+
{onEmailClick}
|
|
51
|
+
{onWhatsappClick}
|
|
52
|
+
{onButtonProfile}
|
|
53
|
+
{onFavoriteClick}
|
|
54
|
+
/>
|
|
40
55
|
</Carousel.Item>
|
|
41
56
|
{:else}
|
|
42
57
|
<div>
|
|
43
|
-
<CardProfile
|
|
58
|
+
<CardProfile
|
|
59
|
+
{...item}
|
|
60
|
+
{variant}
|
|
61
|
+
{isDescriptionIcon}
|
|
62
|
+
{isDescriptionLabel}
|
|
63
|
+
{onEmailClick}
|
|
64
|
+
{onWhatsappClick}
|
|
65
|
+
{onButtonProfile}
|
|
66
|
+
{onFavoriteClick}
|
|
67
|
+
/>
|
|
44
68
|
</div>
|
|
45
69
|
{/if}
|
|
46
70
|
{/each}
|
package/dist/globals.css
CHANGED
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
"Courier New", monospace;
|
|
8
8
|
--color-red-500: oklch(63.7% 0.237 25.331);
|
|
9
9
|
--color-red-600: oklch(57.7% 0.245 27.325);
|
|
10
|
+
--color-red-800: oklch(44.4% 0.177 26.899);
|
|
10
11
|
--color-red-900: oklch(39.6% 0.141 25.723);
|
|
11
12
|
--color-yellow-500: oklch(79.5% 0.184 86.047);
|
|
12
13
|
--color-green-500: oklch(72.3% 0.219 149.579);
|
|
14
|
+
--color-green-600: oklch(62.7% 0.194 149.214);
|
|
13
15
|
--color-blue-50: oklch(97% 0.014 254.604);
|
|
14
16
|
--color-blue-400: oklch(70.7% 0.165 254.624);
|
|
15
17
|
--color-blue-500: oklch(62.3% 0.214 259.815);
|
|
@@ -539,6 +541,12 @@
|
|
|
539
541
|
.mr-2 {
|
|
540
542
|
margin-right: calc(var(--spacing) * 2);
|
|
541
543
|
}
|
|
544
|
+
.-mb-1 {
|
|
545
|
+
margin-bottom: calc(var(--spacing) * -1);
|
|
546
|
+
}
|
|
547
|
+
.-mb-2 {
|
|
548
|
+
margin-bottom: calc(var(--spacing) * -2);
|
|
549
|
+
}
|
|
542
550
|
.-mb-8 {
|
|
543
551
|
margin-bottom: calc(var(--spacing) * -8);
|
|
544
552
|
}
|
|
@@ -585,24 +593,6 @@
|
|
|
585
593
|
-webkit-box-orient: vertical;
|
|
586
594
|
-webkit-line-clamp: 1;
|
|
587
595
|
}
|
|
588
|
-
.line-clamp-2 {
|
|
589
|
-
overflow: hidden;
|
|
590
|
-
display: -webkit-box;
|
|
591
|
-
-webkit-box-orient: vertical;
|
|
592
|
-
-webkit-line-clamp: 2;
|
|
593
|
-
}
|
|
594
|
-
.line-clamp-3 {
|
|
595
|
-
overflow: hidden;
|
|
596
|
-
display: -webkit-box;
|
|
597
|
-
-webkit-box-orient: vertical;
|
|
598
|
-
-webkit-line-clamp: 3;
|
|
599
|
-
}
|
|
600
|
-
.line-clamp-4 {
|
|
601
|
-
overflow: hidden;
|
|
602
|
-
display: -webkit-box;
|
|
603
|
-
-webkit-box-orient: vertical;
|
|
604
|
-
-webkit-line-clamp: 4;
|
|
605
|
-
}
|
|
606
596
|
.block {
|
|
607
597
|
display: block;
|
|
608
598
|
}
|
|
@@ -1437,6 +1427,9 @@
|
|
|
1437
1427
|
.rounded-tr-lg {
|
|
1438
1428
|
border-top-right-radius: var(--radius);
|
|
1439
1429
|
}
|
|
1430
|
+
.rounded-br-lg {
|
|
1431
|
+
border-bottom-right-radius: var(--radius);
|
|
1432
|
+
}
|
|
1440
1433
|
.rounded-bl-lg {
|
|
1441
1434
|
border-bottom-left-radius: var(--radius);
|
|
1442
1435
|
}
|
|
@@ -2999,6 +2992,9 @@
|
|
|
2999
2992
|
background-color: color-mix(in oklab, var(--destructive) 20%, transparent);
|
|
3000
2993
|
}
|
|
3001
2994
|
}
|
|
2995
|
+
.hover\:bg-green-600:hover {
|
|
2996
|
+
background-color: var(--color-green-600);
|
|
2997
|
+
}
|
|
3002
2998
|
.hover\:bg-input\/50:hover {
|
|
3003
2999
|
background-color: var(--input);
|
|
3004
3000
|
}
|
|
@@ -3037,6 +3033,9 @@
|
|
|
3037
3033
|
background-color: color-mix(in oklab, var(--primary) 80%, transparent);
|
|
3038
3034
|
}
|
|
3039
3035
|
}
|
|
3036
|
+
.hover\:bg-red-800:hover {
|
|
3037
|
+
background-color: var(--color-red-800);
|
|
3038
|
+
}
|
|
3040
3039
|
.hover\:bg-secondary\/80:hover {
|
|
3041
3040
|
background-color: var(--secondary);
|
|
3042
3041
|
}
|
|
@@ -3835,9 +3834,15 @@
|
|
|
3835
3834
|
.md\:flex {
|
|
3836
3835
|
display: flex;
|
|
3837
3836
|
}
|
|
3837
|
+
.md\:h-14 {
|
|
3838
|
+
height: calc(var(--spacing) * 14);
|
|
3839
|
+
}
|
|
3838
3840
|
.md\:h-20 {
|
|
3839
3841
|
height: calc(var(--spacing) * 20);
|
|
3840
3842
|
}
|
|
3843
|
+
.md\:h-32\.5 {
|
|
3844
|
+
height: calc(var(--spacing) * 32.5);
|
|
3845
|
+
}
|
|
3841
3846
|
.md\:h-50 {
|
|
3842
3847
|
height: calc(var(--spacing) * 50);
|
|
3843
3848
|
}
|
|
@@ -3865,6 +3870,9 @@
|
|
|
3865
3870
|
.md\:w-11\/12 {
|
|
3866
3871
|
width: calc(11 / 12 * 100%);
|
|
3867
3872
|
}
|
|
3873
|
+
.md\:w-14 {
|
|
3874
|
+
width: calc(var(--spacing) * 14);
|
|
3875
|
+
}
|
|
3868
3876
|
.md\:w-20 {
|
|
3869
3877
|
width: calc(var(--spacing) * 20);
|
|
3870
3878
|
}
|
|
@@ -4009,15 +4017,24 @@
|
|
|
4009
4017
|
.lg\:block {
|
|
4010
4018
|
display: block;
|
|
4011
4019
|
}
|
|
4020
|
+
.lg\:h-20 {
|
|
4021
|
+
height: calc(var(--spacing) * 20);
|
|
4022
|
+
}
|
|
4012
4023
|
.lg\:h-24 {
|
|
4013
4024
|
height: calc(var(--spacing) * 24);
|
|
4014
4025
|
}
|
|
4026
|
+
.lg\:h-44 {
|
|
4027
|
+
height: calc(var(--spacing) * 44);
|
|
4028
|
+
}
|
|
4015
4029
|
.lg\:h-105 {
|
|
4016
4030
|
height: calc(var(--spacing) * 105);
|
|
4017
4031
|
}
|
|
4018
4032
|
.lg\:h-225 {
|
|
4019
4033
|
height: calc(var(--spacing) * 225);
|
|
4020
4034
|
}
|
|
4035
|
+
.lg\:w-20 {
|
|
4036
|
+
width: calc(var(--spacing) * 20);
|
|
4037
|
+
}
|
|
4021
4038
|
.lg\:w-32 {
|
|
4022
4039
|
width: calc(var(--spacing) * 32);
|
|
4023
4040
|
}
|
|
@@ -4057,6 +4074,11 @@
|
|
|
4057
4074
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
4058
4075
|
}
|
|
4059
4076
|
}
|
|
4077
|
+
@media (width >= 96rem) {
|
|
4078
|
+
.\32 xl\:grid-cols-5 {
|
|
4079
|
+
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4060
4082
|
.ltr\:fill-current:where(:dir(ltr), [dir="ltr"], [dir="ltr"] *) {
|
|
4061
4083
|
fill: currentcolor;
|
|
4062
4084
|
}
|
|
@@ -17191,7 +17213,6 @@
|
|
|
17191
17213
|
*
|
|
17192
17214
|
* Copyright (c) 2020 Animate.css
|
|
17193
17215
|
*/
|
|
17194
|
-
@charset "UTF-8";
|
|
17195
17216
|
:root {
|
|
17196
17217
|
--animate-duration: 1s;
|
|
17197
17218
|
--animate-delay: 1s;
|