140 lines
3.7 KiB
TypeScript
140 lines
3.7 KiB
TypeScript
import React, { forwardRef, ReactNode, HTMLAttributes } from 'react';
|
|
import { cn } from '../../lib/utils';
|
|
import { getViewport } from '../../lib/responsive';
|
|
|
|
// Container props interface
|
|
interface ContainerProps extends HTMLAttributes<HTMLDivElement> {
|
|
children?: ReactNode;
|
|
maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl' | 'full';
|
|
padding?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'responsive';
|
|
centered?: boolean;
|
|
fluid?: boolean;
|
|
safeArea?: boolean;
|
|
responsivePadding?: boolean;
|
|
}
|
|
|
|
// Helper function to get max-width styles
|
|
const getMaxWidthStyles = (maxWidth: ContainerProps['maxWidth']) => {
|
|
switch (maxWidth) {
|
|
case 'xs':
|
|
return 'max-w-xs';
|
|
case 'sm':
|
|
return 'max-w-sm';
|
|
case 'md':
|
|
return 'max-w-md';
|
|
case 'lg':
|
|
return 'max-w-lg';
|
|
case 'xl':
|
|
return 'max-w-xl';
|
|
case '2xl':
|
|
return 'max-w-2xl';
|
|
case '3xl':
|
|
return 'max-w-3xl';
|
|
case '4xl':
|
|
return 'max-w-4xl';
|
|
case '5xl':
|
|
return 'max-w-5xl';
|
|
case '6xl':
|
|
return 'max-w-6xl';
|
|
case 'full':
|
|
return 'max-w-full';
|
|
default:
|
|
return 'max-w-6xl';
|
|
}
|
|
};
|
|
|
|
// Helper function to get padding styles
|
|
const getPaddingStyles = (padding: ContainerProps['padding'], responsivePadding?: boolean) => {
|
|
if (padding === 'responsive' || responsivePadding) {
|
|
return 'px-4 xs:px-5 sm:px-6 md:px-8 lg:px-10 xl:px-12 2xl:px-16';
|
|
}
|
|
|
|
switch (padding) {
|
|
case 'none':
|
|
return 'px-0';
|
|
case 'sm':
|
|
return 'px-3 xs:px-4 sm:px-5';
|
|
case 'md':
|
|
return 'px-4 xs:px-5 sm:px-6 md:px-8';
|
|
case 'lg':
|
|
return 'px-4 xs:px-5 sm:px-6 md:px-8 lg:px-10';
|
|
case 'xl':
|
|
return 'px-4 xs:px-5 sm:px-6 md:px-8 lg:px-10 xl:px-12';
|
|
case '2xl':
|
|
return 'px-4 xs:px-5 sm:px-6 md:px-8 lg:px-10 xl:px-12 2xl:px-16';
|
|
default:
|
|
return 'px-4 xs:px-5 sm:px-6 md:px-8 lg:px-10';
|
|
}
|
|
};
|
|
|
|
// Main Container Component
|
|
export const Container = forwardRef<HTMLDivElement, ContainerProps>(
|
|
(
|
|
{
|
|
maxWidth = '6xl',
|
|
padding = 'md',
|
|
centered = true,
|
|
fluid = false,
|
|
safeArea = false,
|
|
responsivePadding = false,
|
|
className = '',
|
|
children,
|
|
...props
|
|
},
|
|
ref
|
|
) => {
|
|
// Get responsive padding if needed
|
|
const getResponsivePadding = () => {
|
|
if (!responsivePadding && padding !== 'responsive') return getPaddingStyles(padding, false);
|
|
|
|
if (typeof window === 'undefined') return getPaddingStyles('md', true);
|
|
|
|
const viewport = getViewport();
|
|
|
|
// Mobile-first responsive padding
|
|
if (viewport.isMobile) {
|
|
return 'px-4 xs:px-5 sm:px-6';
|
|
}
|
|
if (viewport.isTablet) {
|
|
return 'px-5 sm:px-6 md:px-8 lg:px-10';
|
|
}
|
|
if (viewport.isDesktop) {
|
|
return 'px-6 md:px-8 lg:px-10 xl:px-12';
|
|
}
|
|
|
|
return 'px-6 md:px-8 lg:px-10 xl:px-12 2xl:px-16';
|
|
};
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className={cn(
|
|
// Base container styles
|
|
'w-full',
|
|
// Centering
|
|
centered && 'mx-auto',
|
|
// Max width
|
|
!fluid && getMaxWidthStyles(maxWidth),
|
|
// Padding (responsive or static)
|
|
responsivePadding || padding === 'responsive' ? getResponsivePadding() : getPaddingStyles(padding, false),
|
|
// Safe area for mobile notch
|
|
safeArea && 'safe-area-p',
|
|
// Mobile-optimized max width
|
|
'mobile:max-w-full',
|
|
// Custom classes
|
|
className
|
|
)}
|
|
// Add role for accessibility
|
|
role="region"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
|
|
Container.displayName = 'Container';
|
|
|
|
// Export types for external use
|
|
export type { ContainerProps }; |