UNPKG

responsive-reusable-package

Version:

A comprehensive collection of highly customizable, responsive React components for modern web applications. Features flexible layouts, dark mode support, and accessibility.

330 lines (306 loc) 11.1 kB
import React, { useEffect } from 'react'; import '../style.css' /** * ResponsiveContainer - A flexible container component that adapts to different screen sizes * Uses custom CSS classes instead of Tailwind CSS * * @param {Object} props - Component props * @param {React.ReactNode} props.children - Child elements to render * @param {string} props.className - Additional CSS classes * @param {'mobile'|'tablet'|'desktop'|'full'} props.maxWidth - Maximum width breakpoint * @param {'25'|'50'|'75'|'85'|'95'|'auto'|'screen'} props.width - Custom width of the container * @param {'start'|'center'|'end'} props.align - Horizontal alignment * @param {boolean} props.padding - Whether to add responsive padding * @param {boolean} props.margin - Whether to add responsive margin * @param {'1'|'2'|'4'|'6'|'8'|'12'|'16'} props.paddingX - Custom horizontal padding * @param {'1'|'2'|'4'|'6'|'8'|'12'|'16'} props.paddingY - Custom vertical padding * @param {'1'|'2'|'4'|'6'|'8'|'12'|'16'} props.marginX - Custom horizontal margin * @param {'1'|'2'|'4'|'6'|'8'|'12'|'16'} props.marginY - Custom vertical margin * @param {'stack'|'grid'|'flex'} props.layout - Layout type for children * @param {'1'|'2'|'4'|'6'|'8'} props.gap - Gap between child elements (only applies to flex and grid layouts) * @param {'left'|'center'|'right'|'justify'} props.textAlign - Text alignment for child elements * @param {'start'|'center'|'end'|'stretch'|'baseline'} props.alignItems - Flex align-items property (for flex layout) * @param {'start'|'center'|'end'|'between'|'around'|'evenly'} props.justifyContent - Flex justify-content property (for flex layout) * @param {'row'|'column'|'row-reverse'|'column-reverse'} props.flexDirection - Flex direction (for flex layout) * @param {'nowrap'|'wrap'|'wrap-reverse'} props.flexWrap - Flex wrap property (for flex layout) * @param {boolean} props.darkBackground - Dark mode background variants * @param {boolean} props.modal - Whether to render as a modal * @param {boolean} props.modalOpen - Whether the modal is open (required if modal=true) * @param {Function} props.onModalClose - Callback when modal should close (required if modal=true) * @param {boolean} props.modalCloseOnBackdrop - Whether clicking backdrop closes modal (default: true) * @param {boolean} props.modalCloseOnEscape - Whether pressing Escape closes modal (default: true) * @param {'sm'|'md'|'lg'|'xl'|'full'} props.modalSize - Modal size variant * @param {'center'|'top'|'bottom'} props.modalPosition - Modal position on screen * @param {boolean} props.modalBlurBackdrop - Whether to blur the backdrop */ const ResponsiveContainer = ({ children, className = '', maxWidth = 'full', width, align = 'center', padding = true, margin = true, paddingX, paddingY, marginX, marginY, layout = 'stack', gap = '4', // New alignment props textAlign, alignItems, justifyContent, flexDirection = 'column', flexWrap = 'nowrap', darkBackground = false, modal = false, modalOpen = false, onModalClose, modalCloseOnBackdrop = true, modalCloseOnEscape = true, modalSize = 'md', modalPosition = 'center', modalBlurBackdrop = false, ...props }) => { // Handle escape key press for modal useEffect(() => { if (modal && modalOpen && modalCloseOnEscape && onModalClose) { const handleEscape = (e) => { if (e.key === 'Escape') { onModalClose(); } }; document.addEventListener('keydown', handleEscape); return () => document.removeEventListener('keydown', handleEscape); } }, [modal, modalOpen, modalCloseOnEscape, onModalClose]); // Handle body scroll lock for modal useEffect(() => { if (modal && modalOpen) { document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = 'unset'; }; } }, [modal, modalOpen]); // Handle backdrop click const handleBackdropClick = (e) => { if (modalCloseOnBackdrop && onModalClose && e.target === e.currentTarget) { onModalClose(); } }; // Combine all classes for the container const containerClasses = [ 'responsive-container', // Base class !modal && `responsive-container--${maxWidth}`, // Max width (not for modal) !modal && width && `responsive-container--width-${width}`, // Custom width (not for modal) !modal && `responsive-container--align-${align}`, // Alignment (not for modal) // Use custom padding/margin if provided, otherwise use default responsive padding/margin paddingX && `responsive-container--padding-x-${paddingX}`, paddingY && `responsive-container--padding-y-${paddingY}`, marginX && `responsive-container--margin-x-${marginX}`, marginY && `responsive-container--margin-y-${marginY}`, // Only apply default padding/margin if custom ones aren't specified (!paddingX && !paddingY && padding) && 'responsive-container--padding', (!marginX && !marginY && margin && !modal) && 'responsive-container--margin', // No default margin for modal `responsive-container--${layout}`, // Layout type // Gap class (only for flex and grid layouts) (layout === 'flex' || layout === 'grid') && `responsive-container--gap-${gap}`, // Text alignment textAlign && `responsive-container--text-${textAlign}`, // Flex alignment properties (only for flex layout) layout === 'flex' && alignItems && `responsive-container--align-items-${alignItems}`, layout === 'flex' && justifyContent && `responsive-container--justify-${justifyContent}`, layout === 'flex' && flexDirection && `responsive-container--flex-${flexDirection}`, layout === 'flex' && flexWrap && `responsive-container--flex-wrap-${flexWrap}`, darkBackground && 'responsive-container--dark-bg', // Dark background variant // Modal specific classes modal && 'responsive-container--modal', modal && `responsive-container--modal-${modalSize}`, modal && `responsive-container--modal-${modalPosition}`, className // Additional custom classes ].filter(Boolean).join(' '); // Combine backdrop classes const backdropClasses = [ 'responsive-container__modal-backdrop', modalBlurBackdrop && 'responsive-container__modal-backdrop--blur' ].filter(Boolean).join(' '); // If it's a modal, render with backdrop if (modal) { if (!modalOpen) return null; return ( <div className={backdropClasses} onClick={handleBackdropClick}> <div className={containerClasses} {...props}> {children} </div> </div> ); } // Regular container return ( <div className={containerClasses} {...props}> {children} </div> ); }; export default ResponsiveContainer; // // Basic centered container with padding and margin // <ResponsiveContainer maxWidth="desktop"> // <h1>My Content</h1> // <p>This container adapts to screen sizes</p> // </ResponsiveContainer> // // Flex layout container without padding // <ResponsiveContainer // layout="flex" // gap="6" // padding={false} // align="start" // > // <div>Item 1</div> // <div>Item 2</div> // <div>Item 3</div> // </ResponsiveContainer> // // Grid layout with custom gap // <ResponsiveContainer // layout="grid" // gap="8" // maxWidth="tablet" // > // <div>Grid Item 1</div> // <div>Grid Item 2</div> // <div>Grid Item 3</div> // </ResponsiveContainer> // // Stack layout (default) - no gap prop needed // <ResponsiveContainer layout="stack"> // <section>Section 1</section> // <section>Section 2</section> // <section>Section 3</section> // </ResponsiveContainer> // Half width container {/* <ResponsiveContainer width="50" maxWidth="desktop"> Content takes 50% width with desktop max-width constraint </ResponsiveContainer> // Quarter width container <ResponsiveContainer width="25" align="start"> Content takes 25% width aligned to start </ResponsiveContainer> // Auto width (fits content) <ResponsiveContainer width="auto" paddingX="8"> Content auto-sizes with custom padding </ResponsiveContainer> // Full viewport width <ResponsiveContainer width="screen" maxWidth="full"> Content spans full viewport width </ResponsiveContainer> */} // Example 1: Center everything (text and elements) {/* <ResponsiveContainer modal={true} modalOpen={isModalOpen} onModalClose={() => setIsModalOpen(false)} modalSize="sm" modalPosition="center" modalBlurBackdrop={true} padding={true} layout="flex" flexDirection="column" alignItems="center" textAlign="center" gap="4" > <h2>Modal Title</h2> <p>Modal content goes here...</p> <ResponsiveButton variant="primary" size="md" shape="rounded" onClick={() => setIsModalOpen(false)} > Close Modal </ResponsiveButton> </ResponsiveContainer> // Example 2: Right align everything <ResponsiveContainer modal={true} modalOpen={isModalOpen} onModalClose={() => setIsModalOpen(false)} modalSize="sm" modalPosition="center" modalBlurBackdrop={true} padding={true} layout="flex" flexDirection="column" alignItems="end" textAlign="right" gap="4" > <h2>Modal Title</h2> <p>Modal content goes here...</p> <ResponsiveButton variant="primary" size="md" shape="rounded" onClick={() => setIsModalOpen(false)} > Close Modal </ResponsiveButton> </ResponsiveContainer> // Example 3: Center text but right-align button <ResponsiveContainer modal={true} modalOpen={isModalOpen} onModalClose={() => setIsModalOpen(false)} modalSize="sm" modalPosition="center" modalBlurBackdrop={true} padding={true} layout="flex" flexDirection="column" textAlign="center" gap="4" > <h2>Modal Title</h2> <p>Modal content goes here...</p> <ResponsiveContainer layout="flex" justifyContent="end" > <ResponsiveButton variant="primary" size="md" shape="rounded" onClick={() => setIsModalOpen(false)} > Close Modal </ResponsiveButton> </ResponsiveContainer> </ResponsiveContainer> // Example 4: Space between elements (title at top, button at bottom) <ResponsiveContainer modal={true} modalOpen={isModalOpen} onModalClose={() => setIsModalOpen(false)} modalSize="sm" modalPosition="center" modalBlurBackdrop={true} padding={true} layout="flex" flexDirection="column" justifyContent="between" textAlign="center" className="min-h-96" // Add minimum height for space-between to work > <div> <h2>Modal Title</h2> <p>Modal content goes here...</p> </div> <ResponsiveButton variant="primary" size="md" shape="rounded" onClick={() => setIsModalOpen(false)} > Close Modal </ResponsiveButton> </ResponsiveContainer> */}