lightswind
Version:
A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.
136 lines • 12.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const framer_motion_1 = require("framer-motion");
const lucide_react_1 = require("lucide-react");
// NOTE: Placeholder for your custom Input component
const Input = (props) => ((0, jsx_runtime_1.jsx)("input", { ...props }));
// --- Helper Components ---
/** A single item card for the carousel. */
const CarouselItemCard = ({ chain, side }) => {
const { distanceFromCenter, id, name, details, logo, icon: FallbackIcon } = chain;
const distance = Math.abs(distanceFromCenter);
// Visual effects based on distance from the center (0)
const opacity = 1 - distance / 4;
const scale = 1 - distance * 0.1;
const yOffset = distanceFromCenter * 90;
const xOffset = side === 'left' ? -distance * 50 : distance * 50;
const IconOrLogo = ((0, jsx_runtime_1.jsx)("div", { className: "rounded-full border border-muted-foreground/60 \r\n dark:border-muted-foreground/40 p-2 bg-foreground", children: logo ? ((0, jsx_runtime_1.jsx)("img", { src: logo, alt: `${name} logo`, className: "size-8 rounded-full object-cover" })) : ((0, jsx_runtime_1.jsx)(FallbackIcon, { className: "size-8 text-background" })) }));
return ((0, jsx_runtime_1.jsxs)(framer_motion_1.motion.div, { className: `absolute flex items-center gap-4 text-background px-6 py-3
${side === 'left' ? 'flex-row-reverse' : 'flex-row'}`, animate: {
opacity,
scale,
y: yOffset,
x: xOffset,
}, transition: { duration: 0.4, ease: 'easeInOut' }, children: [IconOrLogo, (0, jsx_runtime_1.jsxs)("div", { className: `flex flex-col mx-4 ${side === 'left' ? 'text-right' : 'text-left'}`, children: [(0, jsx_runtime_1.jsx)("span", { className: "text-md lg:text-lg font-semibold text-foreground whitespace-nowrap", children: name }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs lg:text-sm text-gray-500", children: details })] })] }, id));
};
// --- Main Component ---
const ChainCarousel = ({ items, scrollSpeedMs = 1500, visibleItemCount = 9, className = '', onChainSelect, }) => {
const [currentIndex, setCurrentIndex] = (0, react_1.useState)(0);
const [isPaused, setIsPaused] = (0, react_1.useState)(false);
const [searchTerm, setSearchTerm] = (0, react_1.useState)('');
const [showDropdown, setShowDropdown] = (0, react_1.useState)(false);
// References for Framer Motion scroll-based animation
const rightSectionRef = (0, react_1.useRef)(null);
const isInView = (0, framer_motion_1.useInView)(rightSectionRef, { margin: '-100px 0px -100px 0px' });
const totalItems = items.length;
// 1. Auto-scroll effect
(0, react_1.useEffect)(() => {
if (isPaused || totalItems === 0)
return;
const interval = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % totalItems);
}, scrollSpeedMs);
return () => clearInterval(interval);
}, [isPaused, totalItems, scrollSpeedMs]);
// 2. Scroll listener to pause carousel on page scroll
(0, react_1.useEffect)(() => {
let timeoutId;
const handleScroll = () => {
setIsPaused(true);
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
setIsPaused(false);
}, 500); // Resume auto-scroll after 500ms of no scrolling
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
clearTimeout(timeoutId);
};
}, []);
// Memoized function for carousel items
const getVisibleItems = (0, react_1.useCallback)(() => {
const visibleItems = [];
if (totalItems === 0)
return [];
// Ensure visibleItemCount is an odd number for a clear center item
const itemsToShow = visibleItemCount % 2 === 0 ? visibleItemCount + 1 : visibleItemCount;
const half = Math.floor(itemsToShow / 2);
for (let i = -half; i <= half; i++) {
let index = currentIndex + i;
if (index < 0)
index += totalItems;
if (index >= totalItems)
index -= totalItems;
visibleItems.push({
...items[index],
originalIndex: index,
distanceFromCenter: i,
});
}
return visibleItems;
}, [currentIndex, items, totalItems, visibleItemCount]);
// Filtered list for search dropdown
const filteredItems = (0, react_1.useMemo)(() => {
return items.filter((item) => item.name.toLowerCase().includes(searchTerm.toLowerCase()));
}, [items, searchTerm]);
// Handler for selecting an item from the dropdown
const handleSelectChain = (id, name) => {
const index = items.findIndex((c) => c.id === id);
if (index !== -1) {
setCurrentIndex(index); // Jump to the selected item
setIsPaused(true); // Pause to highlight the selection
if (onChainSelect) {
}
}
setSearchTerm(name); // Set search term to the selected item's name
setShowDropdown(false);
};
// The current item displayed in the center
const currentItem = items[currentIndex];
// --- JSX Render ---
return ((0, jsx_runtime_1.jsx)("div", { id: 'explore-section', className: ` space-y-20
${className}`, children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col xl:flex-row \r\n max-w-7xl mx-auto px-4 md:px-8 gap-12 justify-center items-center', children: [(0, jsx_runtime_1.jsxs)(framer_motion_1.motion.div, { className: "relative w-full max-w-md xl:max-w-2xl h-[450px] \r\n flex items-center justify-center hidden xl:flex -left-14", onMouseEnter: () => !searchTerm && setIsPaused(true), onMouseLeave: () => !searchTerm && setIsPaused(false), initial: { x: '-100%', opacity: 0 }, animate: isInView ? { x: 0, opacity: 1 } : {}, transition: { type: 'spring', stiffness: 80, damping: 20, duration: 0.8 }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "absolute inset-0 z-10 pointer-events-none", children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute top-0 h-1/4 w-full bg-transparent" }), (0, jsx_runtime_1.jsx)("div", { className: "absolute bottom-0 h-1/4 w-full bg-transparent " })] }), getVisibleItems().map((chain) => ((0, jsx_runtime_1.jsx)(CarouselItemCard, { chain: chain, side: "left" }, chain.id)))] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col text-center 2xl:text-center xl:text-center\r\n gap-4 max-w-md", children: [currentItem && ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center justify-center gap-0 mt-4", children: [(0, jsx_runtime_1.jsx)("div", { className: 'p-2 bg-foreground rounded-full', children: currentItem.logo ? ((0, jsx_runtime_1.jsx)("img", { src: currentItem.logo, alt: `${currentItem.name} logo`, className: "size-12 rounded-full object-cover" })) : ((() => {
const Icon = currentItem.icon;
return (0, jsx_runtime_1.jsx)(Icon, { className: "size-8 text-background" });
})()) }), (0, jsx_runtime_1.jsx)("h3", { className: "text-xl xl:text-2xl font-bold text-foreground mt-2", children: currentItem.name }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm xl:text-lg text-gray-400", children: currentItem.details || 'View Details' })] })), (0, jsx_runtime_1.jsxs)("div", { className: "mt-6 relative max-w-lg mx-auto xl:mx-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "px-3 flex items-center relative", children: [(0, jsx_runtime_1.jsx)(Input, { type: "text", value: searchTerm, placeholder: "Search Items...", onChange: (e) => {
const val = e.target.value;
setSearchTerm(val);
setShowDropdown(val.length > 0);
if (val === '')
setIsPaused(false);
}, onFocus: () => {
if (searchTerm.length > 0)
setShowDropdown(true);
setIsPaused(true);
}, onBlur: () => {
// Wait briefly before hiding to allow click on dropdown item
setTimeout(() => setShowDropdown(false), 200);
}, className: "flex-grow outline-none text-foreground bg-background px-4 \r\n !placeholder-gray-800 text-lg rounded-full border-gray-500 pr-10 \r\n pl-10 py-2 cursor-pointer border" }), (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { className: "absolute text-foreground w-5 h-5 left-6 pointer-events-none" }), searchTerm && ((0, jsx_runtime_1.jsx)("button", { onClick: () => {
setSearchTerm('');
setShowDropdown(false);
setIsPaused(false);
}, className: "absolute right-6 text-foreground hover:text-gray-300", children: (0, jsx_runtime_1.jsx)(lucide_react_1.DeleteIcon, {}) }))] }), showDropdown && filteredItems.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "absolute left-0 right-0 mt-2 bg-background \r\n rounded-lg border z-20 max-h-60 overflow-y-auto shadow-xl", children: filteredItems.slice(0, 10).map((chain) => ((0, jsx_runtime_1.jsxs)("div", {
// FIX: Use e.preventDefault() to stop browser's form validation/alert
onMouseDown: (e) => {
e.preventDefault();
handleSelectChain(chain.id, chain.name);
}, className: "flex items-center gap-3 px-4 py-3 cursor-pointer \r\n hover:bg-gray-100/10 transition-colors duration-150 rounded-lg m-2", children: [chain.logo ? ((0, jsx_runtime_1.jsx)("img", { src: chain.logo, alt: `${chain.name} logo`, className: "size-6 rounded-full object-cover" })) : ((() => {
const Icon = chain.icon;
return (0, jsx_runtime_1.jsx)(Icon, { size: 24, className: "text-primary" });
})()), (0, jsx_runtime_1.jsx)("span", { className: "text-foreground font-medium", children: chain.name }), (0, jsx_runtime_1.jsx)("span", { className: "ml-auto text-sm text-gray-500", children: chain.details })] }, chain.id))) }))] })] }), (0, jsx_runtime_1.jsxs)(framer_motion_1.motion.div, { ref: rightSectionRef, className: "relative w-full max-w-md xl:max-w-2xl h-[450px] \r\n flex items-center justify-center -right-14", onMouseEnter: () => !searchTerm && setIsPaused(true), onMouseLeave: () => !searchTerm && setIsPaused(false), initial: { x: '100%', opacity: 0 }, animate: isInView ? { x: 0, opacity: 1 } : {}, transition: { type: 'spring', stiffness: 80, damping: 20, duration: 0.8 }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "absolute inset-0 z-10 pointer-events-none", children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute top-0 h-1/4 w-full bg-transparent " }), (0, jsx_runtime_1.jsx)("div", { className: "absolute bottom-0 h-1/4 w-full bg-transparent " })] }), getVisibleItems().map((chain) => ((0, jsx_runtime_1.jsx)(CarouselItemCard, { chain: chain, side: "right" }, chain.id)))] })] }) }));
};
exports.default = ChainCarousel;
//# sourceMappingURL=chain-carousel.js.map