import React, { forwardRef, ForwardedRef, ElementType, ComponentPropsWithoutRef } from 'react'; /** * WARNING: DO NOT VIOLATE THE PURPOSE OF THIS PRIMITIVE. * * Box is a basic container primitive for spacing, sizing and basic styling. * * - DO NOT add layout props (flex, grid, gap) - use Stack or Grid instead. * - DO NOT add positioning props (absolute, top, zIndex) - create a specific component. * - DO NOT add animation props - create a specific component. * * If you need more complex behavior, create a specific component in apps/website/components. */ type Spacing = 0 | 0.5 | 1 | 1.5 | 2 | 2.5 | 3 | 3.5 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 72 | 80 | 96; interface ResponsiveSpacing { base?: Spacing; md?: Spacing; lg?: Spacing; } export type ResponsiveValue = { base?: T; sm?: T; md?: T; lg?: T; xl?: T; '2xl'?: T; }; export interface BoxProps { as?: T; children?: React.ReactNode; className?: string; // Spacing m?: Spacing | ResponsiveSpacing; mt?: Spacing | ResponsiveSpacing; mb?: Spacing | ResponsiveSpacing; ml?: Spacing | 'auto' | ResponsiveSpacing; mr?: Spacing | 'auto' | ResponsiveSpacing; mx?: Spacing | 'auto' | ResponsiveSpacing; my?: Spacing | ResponsiveSpacing; p?: Spacing | ResponsiveSpacing; pt?: Spacing | ResponsiveSpacing; pb?: Spacing | ResponsiveSpacing; pl?: Spacing | ResponsiveSpacing; pr?: Spacing | ResponsiveSpacing; px?: Spacing | ResponsiveSpacing; py?: Spacing | ResponsiveSpacing; // Sizing w?: string | ResponsiveValue; h?: string | ResponsiveValue; width?: string; height?: string; maxWidth?: string | ResponsiveValue; minWidth?: string | ResponsiveValue; maxHeight?: string | ResponsiveValue; minHeight?: string | ResponsiveValue; // Display display?: 'block' | 'inline-block' | 'flex' | 'inline-flex' | 'grid' | 'none' | ResponsiveValue<'block' | 'inline-block' | 'flex' | 'inline-flex' | 'grid' | 'none'>; // Basic Styling rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'; border?: boolean; borderColor?: string; bg?: string; color?: string; shadow?: string; opacity?: number; // Flex/Grid Item props flex?: number | string; flexShrink?: number; flexGrow?: number; alignSelf?: 'auto' | 'start' | 'end' | 'center' | 'stretch' | 'baseline'; order?: number | string | ResponsiveValue; // Events onMouseEnter?: React.MouseEventHandler; onMouseLeave?: React.MouseEventHandler; onClick?: React.MouseEventHandler; style?: React.CSSProperties; id?: string; role?: string; tabIndex?: number; } export const Box = forwardRef(( { as, children, className = '', m, mt, mb, ml, mr, mx, my, p, pt, pb, pl, pr, px, py, w, h, width, height, maxWidth, minWidth, maxHeight, minHeight, display, rounded, border, borderColor, bg, color, shadow, opacity, flex, flexShrink, flexGrow, alignSelf, order, onMouseEnter, onMouseLeave, onClick, style: styleProp, id, role, tabIndex, ...props }: BoxProps & ComponentPropsWithoutRef, ref: ForwardedRef ) => { const Tag = (as as ElementType) || 'div'; const spacingMap: Record = { 0: '0', 0.5: '0.5', 1: '1', 1.5: '1.5', 2: '2', 2.5: '2.5', 3: '3', 3.5: '3.5', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10', 11: '11', 12: '12', 14: '14', 16: '16', 20: '20', 24: '24', 28: '28', 32: '32', 36: '36', 40: '40', 44: '44', 48: '48', 52: '52', 56: '56', 60: '60', 64: '64', 72: '72', 80: '80', 96: '96', 'auto': 'auto' }; const getSpacingClass = (prefix: string, value: Spacing | 'auto' | ResponsiveSpacing | undefined) => { if (value === undefined) return ''; if (typeof value === 'object') { const classes = []; if (value.base !== undefined) classes.push(`${prefix}-${spacingMap[value.base]}`); if (value.md !== undefined) classes.push(`md:${prefix}-${spacingMap[value.md]}`); if (value.lg !== undefined) classes.push(`lg:${prefix}-${spacingMap[value.lg]}`); return classes.join(' '); } return `${prefix}-${spacingMap[value]}`; }; const getResponsiveClasses = (prefix: string, value: string | number | ResponsiveValue | undefined) => { if (value === undefined) return ''; if (typeof value === 'object') { const classes = []; if (value.base !== undefined) classes.push(prefix ? `${prefix}-${value.base}` : String(value.base)); if (value.sm !== undefined) classes.push(prefix ? `sm:${prefix}-${value.sm}` : `sm:${value.sm}`); if (value.md !== undefined) classes.push(prefix ? `md:${prefix}-${value.md}` : `md:${value.md}`); if (value.lg !== undefined) classes.push(prefix ? `lg:${prefix}-${value.lg}` : `lg:${value.lg}`); if (value.xl !== undefined) classes.push(prefix ? `xl:${prefix}-${value.xl}` : `xl:${value.xl}`); if (value['2xl'] !== undefined) classes.push(prefix ? `2xl:${prefix}-${value['2xl']}` : `2xl:${value['2xl']}`); return classes.join(' '); } return prefix ? `${prefix}-${value}` : String(value); }; const classes = [ getSpacingClass('m', m), getSpacingClass('mt', mt), getSpacingClass('mb', mb), getSpacingClass('ml', ml), getSpacingClass('mr', mr), getSpacingClass('mx', mx), getSpacingClass('my', my), getSpacingClass('p', p), getSpacingClass('pt', pt), getSpacingClass('pb', pb), getSpacingClass('pl', pl), getSpacingClass('pr', pr), getSpacingClass('px', px), getSpacingClass('py', py), getResponsiveClasses('w', w), getResponsiveClasses('h', h), getResponsiveClasses('max-w', maxWidth), getResponsiveClasses('min-w', minWidth), getResponsiveClasses('max-h', maxHeight), getResponsiveClasses('min-h', minHeight), getResponsiveClasses('', display), rounded ? `rounded-${rounded}` : '', border ? 'border' : '', borderColor ? borderColor : '', bg ? bg : '', color ? color : '', shadow ? shadow : '', flex !== undefined ? `flex-${flex}` : '', flexShrink !== undefined ? `flex-shrink-${flexShrink}` : '', flexGrow !== undefined ? `flex-grow-${flexGrow}` : '', alignSelf !== undefined ? `self-${alignSelf}` : '', opacity !== undefined ? `opacity-${opacity * 100}` : '', getResponsiveClasses('order', order), className ].filter(Boolean).join(' '); const style: React.CSSProperties = { ...(width ? { width } : {}), ...(height ? { height } : {}), ...(typeof maxWidth === 'string' ? { maxWidth } : {}), ...(typeof minWidth === 'string' ? { minWidth } : {}), ...(typeof maxHeight === 'string' ? { maxHeight } : {}), ...(typeof minHeight === 'string' ? { minHeight } : {}), ...(styleProp || {}) }; return ( } className={classes} onClick={onClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} style={style} id={id} role={role} tabIndex={tabIndex} {...props} > {children} ); }); Box.displayName = 'Box';