|
1 | | -import React, { useState, useEffect } from 'react'; |
| 1 | +import React from 'react'; |
2 | 2 | import './Testimonials.css'; |
3 | 3 |
|
4 | | -// Custom hook to track window width |
5 | | -const useWindowWidth = () => { |
6 | | - const [windowWidth, setWindowWidth] = useState<number>(() => { |
7 | | - // Check if we're in a browser environment |
8 | | - if (typeof window !== 'undefined') { |
9 | | - return window.innerWidth; |
10 | | - } |
11 | | - return 1024; // Default fallback for SSR |
12 | | - }); |
13 | | - |
14 | | - useEffect(() => { |
15 | | - if (typeof window === 'undefined') return; |
16 | | - |
17 | | - const handleResize = () => { |
18 | | - setWindowWidth(window.innerWidth); |
19 | | - }; |
20 | | - |
21 | | - window.addEventListener('resize', handleResize); |
22 | | - return () => window.removeEventListener('resize', handleResize); |
23 | | - }, []); |
24 | | - |
25 | | - return windowWidth; |
26 | | -}; |
27 | | - |
28 | | -// Custom hook to track if component has mounted |
29 | | -const useHasMounted = () => { |
30 | | - const [hasMounted, setHasMounted] = useState(false); |
31 | | - |
32 | | - useEffect(() => { |
33 | | - // Add a small delay to ensure horizontal layout is fully rendered |
34 | | - const timer = setTimeout(() => { |
35 | | - setHasMounted(true); |
36 | | - }, 100); |
37 | | - |
38 | | - return () => clearTimeout(timer); |
39 | | - }, []); |
40 | | - |
41 | | - return hasMounted; |
42 | | -}; |
43 | | - |
44 | | -// Function to calculate card width based on screen size |
45 | | -const calculateCardWidth = (screenWidth: number): number => { |
46 | | - // Account for container padding (p-7 = 28px on each side = 56px total) |
47 | | - // and gaps between cards (gap-4 = 16px between each card) |
48 | | - const containerPadding = 56; |
49 | | - |
50 | | - if (screenWidth < 590) { |
51 | | - // Mobile: Single card takes most of the width |
52 | | - return Math.max(280, screenWidth - containerPadding - 20); // Extra margin for safety |
53 | | - } else if (screenWidth < 900) { |
54 | | - // At 590px and above: Show 2 cards |
55 | | - const availableWidth = screenWidth - containerPadding; |
56 | | - const gapWidth = 16; // gap-4 between 2 cards = 1 gap |
57 | | - return Math.max(280, (availableWidth - gapWidth) / 2); |
58 | | - } else { |
59 | | - // At 900px and above: Show 3 cards, but cap at 350px max |
60 | | - const availableWidth = screenWidth - containerPadding; |
61 | | - const gapWidth = 32; // gap-4 between 3 cards = 2 gaps |
62 | | - const calculatedWidth = (availableWidth - gapWidth) / 3; |
63 | | - return Math.min(350, calculatedWidth); |
64 | | - } |
65 | | -}; |
66 | | - |
67 | 4 | // Types for testimonials data |
68 | 5 | export interface Testimonial { |
69 | 6 | id: string; |
@@ -197,86 +134,19 @@ const TestimonialsContainer: React.FC<{ |
197 | 134 | testimonials: Testimonial[]; |
198 | 135 | layout?: TestimonialLayout; |
199 | 136 | }> = ({ testimonials, layout = 'horizontal' }) => { |
200 | | - const windowWidth = useWindowWidth(); |
201 | | - const hasMounted = useHasMounted(); |
202 | | - const cardWidth = calculateCardWidth(windowWidth); |
203 | | - |
204 | | - // Determine the effective layout - start with horizontal on mobile until mounted |
205 | | - const effectiveLayout = (() => { |
206 | | - if (layout === 'masonry' && hasMounted) { |
207 | | - // Only use masonry after component has mounted |
208 | | - return 'masonry'; |
209 | | - } |
210 | | - // Default to horizontal for better mobile experience |
211 | | - return 'horizontal'; |
212 | | - })(); |
213 | | - |
214 | | - // Layout-specific classes and styles |
215 | | - const getLayoutClasses = () => { |
216 | | - switch (effectiveLayout) { |
217 | | - case 'horizontal': |
218 | | - return "flex gap-4 overflow-x-auto pb-2 items-start swiper-horizontal"; |
219 | | - case 'masonry': |
220 | | - return "masonry-grid"; |
221 | | - default: |
222 | | - return "flex gap-4 overflow-x-auto pb-2 items-start"; |
223 | | - } |
224 | | - }; |
225 | | - |
226 | | - const getCardClasses = () => { |
227 | | - switch (effectiveLayout) { |
228 | | - case 'horizontal': |
229 | | - return "flex-shrink-0"; |
230 | | - case 'masonry': |
231 | | - return "masonry-item"; |
232 | | - default: |
233 | | - return "flex-shrink-0"; |
234 | | - } |
235 | | - }; |
236 | | - |
237 | | - const getContainerStyle = (): React.CSSProperties => { |
238 | | - if (effectiveLayout === 'masonry') { |
239 | | - return { |
240 | | - columns: windowWidth < 590 ? 1 : windowWidth < 900 ? 2 : 3, |
241 | | - columnGap: '1rem', |
242 | | - columnFill: 'balance' as const |
243 | | - }; |
244 | | - } |
245 | | - return {}; |
246 | | - }; |
247 | | - |
248 | | - const getCardStyle = (testimonial: Testimonial): React.CSSProperties => { |
249 | | - if (effectiveLayout === 'masonry') { |
250 | | - return { |
251 | | - breakInside: 'avoid' as const, |
252 | | - marginBottom: '1rem', |
253 | | - width: '100%' |
254 | | - }; |
255 | | - } |
256 | | - return { width: `${cardWidth}px` }; |
257 | | - }; |
| 137 | + // Use CSS classes for responsive layout - no JavaScript calculations needed |
| 138 | + const containerClass = layout === 'masonry' |
| 139 | + ? 'testimonials-masonry' |
| 140 | + : 'testimonials-horizontal'; |
258 | 141 |
|
259 | 142 | return ( |
260 | 143 | <div className="p-7" data-id="d4c7d44a-95be-4b3c-978f-4ba863490a54"> |
261 | | - <div className="sj-container "> |
| 144 | + <div className="sj-container"> |
262 | 145 | <div className="relative w-full px-0"> |
263 | 146 | <div className="w-full"> |
264 | | - {/* Loading indicator for layout transition */} |
265 | | - {layout === 'masonry' && !hasMounted && ( |
266 | | - <div className="text-center text-sm text-gray-500 mb-4"> |
267 | | - Loading masonry layout... |
268 | | - </div> |
269 | | - )} |
270 | | - <div |
271 | | - className={getLayoutClasses()} |
272 | | - style={getContainerStyle()} |
273 | | - > |
| 147 | + <div className={containerClass}> |
274 | 148 | {testimonials.map((testimonial) => ( |
275 | | - <div |
276 | | - key={testimonial.id} |
277 | | - className={getCardClasses()} |
278 | | - style={getCardStyle(testimonial)} |
279 | | - > |
| 149 | + <div key={testimonial.id}> |
280 | 150 | <TestimonialCard testimonial={testimonial} /> |
281 | 151 | </div> |
282 | 152 | ))} |
|
0 commit comments