From 3ff69d69459dcc9c50ed7f9c4e1ef9ffc52fdcc9 Mon Sep 17 00:00:00 2001 From: "diana.dolgolyova" Date: Tue, 10 Mar 2026 13:22:47 +0300 Subject: [PATCH] feat: FAQ two-column layout, team swipe & counter, showcase improvements - FAQ: split into 2 columns on desktop to reduce section height - Team: remove description line-clamp, show full bio - ShowcaseLayout: add swipe navigation on mobile, optional counter - Counter shows current/total with gold accent styling Co-Authored-By: Claude Opus 4.6 --- src/components/sections/FAQ.tsx | 84 ++++++++++++++++------------ src/components/sections/Team.tsx | 3 +- src/components/ui/ShowcaseLayout.tsx | 52 ++++++++++++++++- 3 files changed, 100 insertions(+), 39 deletions(-) diff --git a/src/components/sections/FAQ.tsx b/src/components/sections/FAQ.tsx index 6319bc5..673bbbd 100644 --- a/src/components/sections/FAQ.tsx +++ b/src/components/sections/FAQ.tsx @@ -22,43 +22,55 @@ export function FAQ() { {faq.title} -
- {faq.items.map((item, i) => ( - -
- -
-
-
- {item.answer} -
-
-
+
+ {[0, 1].map((col) => { + const half = Math.ceil(faq.items.length / 2); + const items = col === 0 ? faq.items.slice(0, half) : faq.items.slice(half); + const offset = col === 0 ? 0 : half; + return ( +
+ {items.map((item, i) => { + const idx = offset + i; + return ( + +
+ +
+
+
+ {item.answer} +
+
+
+
+
+ ); + })}
- - ))} + ); + })}
diff --git a/src/components/sections/Team.tsx b/src/components/sections/Team.tsx index 9ba2d4b..0a4a4bf 100644 --- a/src/components/sections/Team.tsx +++ b/src/components/sections/Team.tsx @@ -31,6 +31,7 @@ export function Team() { activeIndex={activeIndex} onSelect={select} onHoverChange={setHovering} + counter renderDetail={(member) => (
+

{member.description}

)} diff --git a/src/components/ui/ShowcaseLayout.tsx b/src/components/ui/ShowcaseLayout.tsx index b82be8f..5275e7d 100644 --- a/src/components/ui/ShowcaseLayout.tsx +++ b/src/components/ui/ShowcaseLayout.tsx @@ -1,6 +1,6 @@ "use client"; -import { useRef, useEffect, useState } from "react"; +import { useRef, useEffect, useState, useCallback } from "react"; interface ShowcaseLayoutProps { items: T[]; @@ -9,6 +9,7 @@ interface ShowcaseLayoutProps { onHoverChange?: (hovering: boolean) => void; renderDetail: (item: T, index: number) => React.ReactNode; renderSelectorItem: (item: T, index: number, isActive: boolean) => React.ReactNode; + counter?: boolean; } export function ShowcaseLayout({ @@ -18,9 +19,11 @@ export function ShowcaseLayout({ onHoverChange, renderDetail, renderSelectorItem, + counter = false, }: ShowcaseLayoutProps) { const selectorRef = useRef(null); const activeItemRef = useRef(null); + const detailRef = useRef(null); const [isUserInteracting, setIsUserInteracting] = useState(false); // Auto-scroll selector only when item is out of view @@ -56,6 +59,32 @@ export function ShowcaseLayout({ } }, [activeIndex, isUserInteracting]); + // Swipe support on detail area + const touchStart = useRef<{ x: number; y: number } | null>(null); + + const handleTouchStart = useCallback((e: React.TouchEvent) => { + touchStart.current = { x: e.touches[0].clientX, y: e.touches[0].clientY }; + }, []); + + const handleTouchEnd = useCallback( + (e: React.TouchEvent) => { + if (!touchStart.current) return; + const dx = e.changedTouches[0].clientX - touchStart.current.x; + const dy = e.changedTouches[0].clientY - touchStart.current.y; + touchStart.current = null; + + // Only trigger if horizontal swipe is dominant and > 50px + if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy) * 1.5) { + if (dx < 0 && activeIndex < items.length - 1) { + onSelect(activeIndex + 1); + } else if (dx > 0 && activeIndex > 0) { + onSelect(activeIndex - 1); + } + } + }, + [activeIndex, items.length, onSelect], + ); + function handleMouseEnter() { setIsUserInteracting(true); onHoverChange?.(true); @@ -74,9 +103,28 @@ export function ShowcaseLayout({ > {/* Detail area */}
-
+
{renderDetail(items[activeIndex], activeIndex)}
+ + {/* Counter */} + {counter && ( +
+ + {String(activeIndex + 1).padStart(2, "0")} + + + + {String(items.length).padStart(2, "0")} + +
+ )}
{/* Selector */}