@pradip1995/segment-product-card 0.3.16 → 0.3.17

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pradip1995/segment-product-card",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -111,6 +111,7 @@ export function ProductRailByVariant({
111
111
  wishlistProductIds,
112
112
  productCardVariant,
113
113
  showNavButtons,
114
+ autoScroll,
114
115
  }: {
115
116
  variant: ProductRailVariant
116
117
  products: HttpTypes.StoreProduct[]
@@ -123,6 +124,7 @@ export function ProductRailByVariant({
123
124
  wishlistProductIds?: string[]
124
125
  productCardVariant?: ProductCardVariant
125
126
  showNavButtons?: boolean
127
+ autoScroll?: boolean
126
128
  }) {
127
129
  return (
128
130
  <>
@@ -136,6 +138,7 @@ export function ProductRailByVariant({
136
138
  wishlistProductIds={wishlistProductIds}
137
139
  productCardVariant={productCardVariant}
138
140
  showNavButtons={showNavButtons}
141
+ autoScroll={autoScroll}
139
142
  />
140
143
  ) : variant === "featured" ? (
141
144
  <ProductRailFeatured
@@ -1,9 +1,13 @@
1
1
  "use client"
2
2
 
3
- import { useRef } from "react"
3
+ import { useEffect, useRef, useState } from "react"
4
4
  import ProductCard, { type ProductCardVariant } from "./segment"
5
5
  import type { HttpTypes } from "@medusajs/types"
6
6
 
7
+ /** Auto-scroll only when the rail has more cards than fit in one row. */
8
+ const VISIBLE_CARDS = 4
9
+ const AUTO_SCROLL_MS = 3200
10
+
7
11
  type ProductScrollProps = {
8
12
  products: HttpTypes.StoreProduct[]
9
13
  region?: HttpTypes.StoreRegion | null
@@ -12,6 +16,8 @@ type ProductScrollProps = {
12
16
  wishlistProductIds?: string[]
13
17
  productCardVariant?: ProductCardVariant
14
18
  showNavButtons?: boolean
19
+ /** When true and there are more than 4 products, advance the rail automatically. */
20
+ autoScroll?: boolean
15
21
  }
16
22
 
17
23
  export default function ProductScroll({
@@ -22,8 +28,13 @@ export default function ProductScroll({
22
28
  wishlistProductIds,
23
29
  productCardVariant,
24
30
  showNavButtons = true,
31
+ autoScroll = false,
25
32
  }: ProductScrollProps) {
26
33
  const viewportRef = useRef<HTMLDivElement>(null)
34
+ const pauseUntilRef = useRef(0)
35
+ const [paused, setPaused] = useState(false)
36
+
37
+ const shouldAutoScroll = autoScroll && products.length > VISIBLE_CARDS
27
38
 
28
39
  const scroll = (direction: "prev" | "next") => {
29
40
  const viewport = viewportRef.current
@@ -35,15 +46,75 @@ export default function ProductScroll({
35
46
  })
36
47
  }
37
48
 
49
+ useEffect(() => {
50
+ if (!shouldAutoScroll) return
51
+ if (
52
+ typeof window !== "undefined" &&
53
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches
54
+ ) {
55
+ return
56
+ }
57
+
58
+ const tick = () => {
59
+ const viewport = viewportRef.current
60
+ if (!viewport || paused) return
61
+ if (Date.now() < pauseUntilRef.current) return
62
+
63
+ const maxScroll = viewport.scrollWidth - viewport.clientWidth
64
+ if (maxScroll <= 4) return
65
+
66
+ const slide = viewport.querySelector(
67
+ ".product-scroll__slide, .bestsellers-carousel__slide"
68
+ ) as HTMLElement | null
69
+ const styles = getComputedStyle(viewport.querySelector(".product-scroll__track, .bestsellers-carousel__track") || viewport)
70
+ const gap = parseFloat(styles.columnGap || styles.gap || "16") || 16
71
+ const step = (slide?.offsetWidth || viewport.clientWidth / VISIBLE_CARDS) + gap
72
+ const next = viewport.scrollLeft + step
73
+
74
+ if (next >= maxScroll - 2) {
75
+ viewport.scrollTo({ left: 0, behavior: "smooth" })
76
+ } else {
77
+ viewport.scrollBy({ left: step, behavior: "smooth" })
78
+ }
79
+ }
80
+
81
+ const id = window.setInterval(tick, AUTO_SCROLL_MS)
82
+ return () => window.clearInterval(id)
83
+ }, [shouldAutoScroll, paused, products.length])
84
+
85
+ const pauseAutoScroll = (ms = AUTO_SCROLL_MS * 2) => {
86
+ pauseUntilRef.current = Date.now() + ms
87
+ }
88
+
38
89
  if (products.length === 0) return null
39
90
 
40
91
  return (
41
- <div className="product-scroll bestsellers-carousel">
92
+ <div
93
+ className="product-scroll bestsellers-carousel"
94
+ onMouseEnter={() => {
95
+ if (shouldAutoScroll) setPaused(true)
96
+ }}
97
+ onMouseLeave={() => {
98
+ if (shouldAutoScroll) {
99
+ setPaused(false)
100
+ pauseAutoScroll(800)
101
+ }
102
+ }}
103
+ onFocusCapture={() => {
104
+ if (shouldAutoScroll) pauseAutoScroll()
105
+ }}
106
+ onPointerDown={() => {
107
+ if (shouldAutoScroll) pauseAutoScroll()
108
+ }}
109
+ >
42
110
  {showNavButtons ? (
43
111
  <button
44
112
  type="button"
45
113
  className="product-scroll__nav bestsellers-carousel__nav product-scroll__nav--prev bestsellers-carousel__nav--prev"
46
- onClick={() => scroll("prev")}
114
+ onClick={() => {
115
+ pauseAutoScroll()
116
+ scroll("prev")
117
+ }}
47
118
  aria-label="Previous products"
48
119
  >
49
120
 
@@ -75,7 +146,10 @@ export default function ProductScroll({
75
146
  <button
76
147
  type="button"
77
148
  className="product-scroll__nav bestsellers-carousel__nav product-scroll__nav--next bestsellers-carousel__nav--next"
78
- onClick={() => scroll("next")}
149
+ onClick={() => {
150
+ pauseAutoScroll()
151
+ scroll("next")
152
+ }}
79
153
  aria-label="Next products"
80
154
  >
81
155