@pradip1995/segment-powerhouse-carousel 0.1.2 → 0.1.3
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/package.json +2 -1
- package/src/carousel.css +49 -0
- package/src/carousel.tsx +8 -1
- package/src/product-card.tsx +21 -12
- package/src/segment.tsx +7 -1
- package/src/wishlist-button.tsx +93 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pradip1995/segment-powerhouse-carousel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@pradip1995/commerce-core": "^4.0.0",
|
|
26
26
|
"@pradip1995/medusa-connector": "^0.2.0",
|
|
27
|
+
"@pradip1995/segment-login-popup": "^0.1.2",
|
|
27
28
|
"@pradip1995/segment-primitives": "^0.4.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
package/src/carousel.css
CHANGED
|
@@ -100,6 +100,10 @@
|
|
|
100
100
|
background: #fff;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
.powerhouse-carousel__media-wrap {
|
|
104
|
+
position: relative;
|
|
105
|
+
}
|
|
106
|
+
|
|
103
107
|
.powerhouse-carousel__card-media {
|
|
104
108
|
position: relative;
|
|
105
109
|
display: block;
|
|
@@ -108,6 +112,51 @@
|
|
|
108
112
|
background: #f3f3f3;
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
.powerhouse-carousel__wishlist {
|
|
116
|
+
position: absolute;
|
|
117
|
+
top: 0.5rem;
|
|
118
|
+
right: 0.5rem;
|
|
119
|
+
z-index: 2;
|
|
120
|
+
display: inline-flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
width: 2.25rem;
|
|
124
|
+
height: 2.25rem;
|
|
125
|
+
padding: 0;
|
|
126
|
+
border: 1px solid rgba(29, 29, 29, 0.12);
|
|
127
|
+
background: rgba(255, 255, 255, 0.94);
|
|
128
|
+
color: #1d1d1d;
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
transition: color 0.2s ease, border-color 0.2s ease;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.powerhouse-carousel__wishlist:hover {
|
|
134
|
+
border-color: #1d1d1d;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.powerhouse-carousel__wishlist.is-active {
|
|
138
|
+
color: #c1272d;
|
|
139
|
+
border-color: #c1272d;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.powerhouse-carousel__wishlist.is-loading {
|
|
143
|
+
opacity: 0.55;
|
|
144
|
+
cursor: wait;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.powerhouse-carousel__wishlist-icon {
|
|
148
|
+
width: 1.05rem;
|
|
149
|
+
height: 1.05rem;
|
|
150
|
+
fill: none;
|
|
151
|
+
stroke: currentColor;
|
|
152
|
+
stroke-width: 1.75;
|
|
153
|
+
stroke-linejoin: round;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.powerhouse-carousel__wishlist.is-active .powerhouse-carousel__wishlist-icon {
|
|
157
|
+
fill: currentColor;
|
|
158
|
+
}
|
|
159
|
+
|
|
111
160
|
.powerhouse-carousel__card-image,
|
|
112
161
|
.powerhouse-carousel__card-placeholder {
|
|
113
162
|
width: 100%;
|
package/src/carousel.tsx
CHANGED
|
@@ -20,10 +20,12 @@ export default function ProductCarousel({
|
|
|
20
20
|
products,
|
|
21
21
|
region,
|
|
22
22
|
countryCode,
|
|
23
|
+
wishlistProductIds,
|
|
23
24
|
}: {
|
|
24
25
|
products: HttpTypes.StoreProduct[]
|
|
25
26
|
region?: HttpTypes.StoreRegion
|
|
26
27
|
countryCode: string
|
|
28
|
+
wishlistProductIds?: string[]
|
|
27
29
|
}) {
|
|
28
30
|
const trackRef = useRef<HTMLDivElement>(null)
|
|
29
31
|
const [canPrev, setCanPrev] = useState(false)
|
|
@@ -87,7 +89,12 @@ export default function ProductCarousel({
|
|
|
87
89
|
<div ref={trackRef} className="powerhouse-carousel__track">
|
|
88
90
|
{products.map((product) => (
|
|
89
91
|
<div key={product.id} className="powerhouse-carousel__slide">
|
|
90
|
-
<ProductCard
|
|
92
|
+
<ProductCard
|
|
93
|
+
product={product}
|
|
94
|
+
region={region}
|
|
95
|
+
countryCode={countryCode}
|
|
96
|
+
wishlistProductIds={wishlistProductIds}
|
|
97
|
+
/>
|
|
91
98
|
</div>
|
|
92
99
|
))}
|
|
93
100
|
</div>
|
package/src/product-card.tsx
CHANGED
|
@@ -7,14 +7,16 @@ import LocalizedLink from "@pradip1995/segment-primitives/localized-link"
|
|
|
7
7
|
import { formatPrice } from "@pradip1995/segment-primitives/format-price"
|
|
8
8
|
import { isNextRedirect } from "@pradip1995/segment-primitives/is-next-redirect"
|
|
9
9
|
import type { HttpTypes } from "@medusajs/types"
|
|
10
|
+
import WishlistButton from "./wishlist-button"
|
|
10
11
|
|
|
11
12
|
type Props = {
|
|
12
13
|
product: HttpTypes.StoreProduct
|
|
13
14
|
region?: HttpTypes.StoreRegion
|
|
14
15
|
countryCode: string
|
|
16
|
+
wishlistProductIds?: string[]
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
export default function ProductCard({ product, region, countryCode }: Props) {
|
|
19
|
+
export default function ProductCard({ product, region, countryCode, wishlistProductIds }: Props) {
|
|
18
20
|
const router = useRouter()
|
|
19
21
|
const [adding, setAdding] = useState(false)
|
|
20
22
|
const variant = product.variants?.[0]
|
|
@@ -55,17 +57,24 @@ export default function ProductCard({ product, region, countryCode }: Props) {
|
|
|
55
57
|
|
|
56
58
|
return (
|
|
57
59
|
<article className={`powerhouse-carousel__card${onSale ? " is-sale" : ""}`}>
|
|
58
|
-
<
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
<div className="powerhouse-carousel__media-wrap">
|
|
61
|
+
<LocalizedLink href={href} countryCode={countryCode} className="powerhouse-carousel__card-media">
|
|
62
|
+
{product.thumbnail?.trim() ? (
|
|
63
|
+
<img src={product.thumbnail.trim()} alt={product.title || ""} className="powerhouse-carousel__card-image" />
|
|
64
|
+
) : (
|
|
65
|
+
<div className="powerhouse-carousel__card-placeholder" />
|
|
66
|
+
)}
|
|
67
|
+
{onSale && saved != null ? (
|
|
68
|
+
<span className="powerhouse-carousel__badge">Save {formatPrice(saved, currency)}</span>
|
|
69
|
+
) : null}
|
|
70
|
+
{!inStock ? <span className="powerhouse-carousel__sold">Sold out</span> : null}
|
|
71
|
+
</LocalizedLink>
|
|
72
|
+
<WishlistButton
|
|
73
|
+
productId={product.id}
|
|
74
|
+
countryCode={countryCode}
|
|
75
|
+
wishlistProductIds={wishlistProductIds}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
69
78
|
|
|
70
79
|
<div className="powerhouse-carousel__card-info">
|
|
71
80
|
{price != null ? (
|
package/src/segment.tsx
CHANGED
|
@@ -10,6 +10,7 @@ export default async function PowerhouseCarousel({
|
|
|
10
10
|
countryCode: countryCodeProp,
|
|
11
11
|
title = DEFAULT_TITLE,
|
|
12
12
|
emptyMessage = DEFAULT_EMPTY,
|
|
13
|
+
wishlistProductIds,
|
|
13
14
|
}: PowerhouseCarouselProps) {
|
|
14
15
|
const countryCode =
|
|
15
16
|
countryCodeProp?.toLowerCase() ||
|
|
@@ -47,7 +48,12 @@ export default async function PowerhouseCarousel({
|
|
|
47
48
|
{list.length === 0 ? (
|
|
48
49
|
<p className="powerhouse-carousel__empty">{emptyMessage}</p>
|
|
49
50
|
) : (
|
|
50
|
-
<ProductCarousel
|
|
51
|
+
<ProductCarousel
|
|
52
|
+
products={list}
|
|
53
|
+
region={displayRegion ?? undefined}
|
|
54
|
+
countryCode={countryCode}
|
|
55
|
+
wishlistProductIds={wishlistProductIds}
|
|
56
|
+
/>
|
|
51
57
|
)}
|
|
52
58
|
</section>
|
|
53
59
|
)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState, type MouseEvent } from "react"
|
|
4
|
+
import { useRouter } from "next/navigation"
|
|
5
|
+
import {
|
|
6
|
+
addToWishlist,
|
|
7
|
+
getWishlistProductIds,
|
|
8
|
+
removeFromWishlist,
|
|
9
|
+
} from "@pradip1995/commerce-core/client/actions/wishlist"
|
|
10
|
+
import LoginRequiredModal from "@pradip1995/segment-login-popup/required"
|
|
11
|
+
|
|
12
|
+
type WishlistButtonProps = {
|
|
13
|
+
productId?: string | null
|
|
14
|
+
countryCode: string
|
|
15
|
+
wishlistProductIds?: string[]
|
|
16
|
+
className?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default function WishlistButton({
|
|
20
|
+
productId,
|
|
21
|
+
countryCode,
|
|
22
|
+
wishlistProductIds,
|
|
23
|
+
className = "powerhouse-carousel__wishlist",
|
|
24
|
+
}: WishlistButtonProps) {
|
|
25
|
+
const router = useRouter()
|
|
26
|
+
const [wishlisted, setWishlisted] = useState(false)
|
|
27
|
+
const [loading, setLoading] = useState(false)
|
|
28
|
+
const [loginRequired, setLoginRequired] = useState(false)
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!productId || wishlistProductIds === undefined) return
|
|
32
|
+
setWishlisted(wishlistProductIds.includes(productId))
|
|
33
|
+
}, [productId, wishlistProductIds])
|
|
34
|
+
|
|
35
|
+
async function handleToggle(event: MouseEvent) {
|
|
36
|
+
event.preventDefault()
|
|
37
|
+
event.stopPropagation()
|
|
38
|
+
if (!productId || loading) return
|
|
39
|
+
|
|
40
|
+
setLoading(true)
|
|
41
|
+
try {
|
|
42
|
+
let currentlyWishlisted = wishlisted
|
|
43
|
+
if (wishlistProductIds === undefined) {
|
|
44
|
+
try {
|
|
45
|
+
const ids = await getWishlistProductIds()
|
|
46
|
+
currentlyWishlisted = ids.includes(productId)
|
|
47
|
+
setWishlisted(currentlyWishlisted)
|
|
48
|
+
} catch {
|
|
49
|
+
currentlyWishlisted = false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const result = currentlyWishlisted
|
|
54
|
+
? await removeFromWishlist(productId)
|
|
55
|
+
: await addToWishlist(productId)
|
|
56
|
+
|
|
57
|
+
if (result.success) {
|
|
58
|
+
setWishlisted(!currentlyWishlisted)
|
|
59
|
+
router.refresh()
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (result.error?.toLowerCase().includes("login")) {
|
|
64
|
+
setLoginRequired(true)
|
|
65
|
+
}
|
|
66
|
+
} finally {
|
|
67
|
+
setLoading(false)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
className={`${className}${wishlisted ? " is-active" : ""}${loading ? " is-loading" : ""}`}
|
|
76
|
+
onClick={handleToggle}
|
|
77
|
+
disabled={loading || !productId}
|
|
78
|
+
aria-label={wishlisted ? "Remove from wishlist" : "Add to wishlist"}
|
|
79
|
+
aria-pressed={wishlisted}
|
|
80
|
+
>
|
|
81
|
+
<svg viewBox="0 0 24 24" aria-hidden className={`${className}-icon`}>
|
|
82
|
+
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
|
|
83
|
+
</svg>
|
|
84
|
+
</button>
|
|
85
|
+
<LoginRequiredModal
|
|
86
|
+
isOpen={loginRequired}
|
|
87
|
+
onClose={() => setLoginRequired(false)}
|
|
88
|
+
countryCode={countryCode}
|
|
89
|
+
message="Please log in to save items to your wishlist."
|
|
90
|
+
/>
|
|
91
|
+
</>
|
|
92
|
+
)
|
|
93
|
+
}
|