mediahub-fe/components/form/shared/form-grid.tsx

271 lines
6.2 KiB
TypeScript
Raw Permalink Normal View History

/**
* FormGrid Component
*
* A reusable grid layout component that:
* - Provides responsive grid layouts for form fields
* - Handles different column configurations
* - Supports gap spacing
* - Maintains consistent alignment
*
* This component improves form layout and responsiveness.
*/
import React from 'react';
import { cn } from '@/lib/utils';
// =============================================================================
// TYPES
// =============================================================================
export interface FormGridProps {
// Grid configuration
cols?: 1 | 2 | 3 | 4 | 6 | 12;
sm?: 1 | 2 | 3 | 4 | 6 | 12;
md?: 1 | 2 | 3 | 4 | 6 | 12;
lg?: 1 | 2 | 3 | 4 | 6 | 12;
xl?: 1 | 2 | 3 | 4 | 6 | 12;
// Spacing
gap?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
gapX?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
gapY?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
// Alignment
align?: 'start' | 'center' | 'end' | 'stretch';
justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';
// Styling
className?: string;
// Children
children: React.ReactNode;
}
// =============================================================================
// COMPONENT
// =============================================================================
export function FormGrid({
cols = 1,
sm,
md,
lg,
xl,
gap = 'md',
gapX,
gapY,
align = 'start',
justify = 'start',
className = '',
children,
}: FormGridProps) {
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
const getGridCols = (cols: number) => {
return `grid-cols-${cols}`;
};
const getResponsiveCols = () => {
const classes = [getGridCols(cols)];
if (sm) classes.push(`sm:grid-cols-${sm}`);
if (md) classes.push(`md:grid-cols-${md}`);
if (lg) classes.push(`lg:grid-cols-${lg}`);
if (xl) classes.push(`xl:grid-cols-${xl}`);
return classes.join(' ');
};
const getGap = (gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl') => {
switch (gap) {
case 'xs':
return 'gap-1';
case 'sm':
return 'gap-2';
case 'lg':
return 'gap-6';
case 'xl':
return 'gap-8';
default:
return 'gap-4';
}
};
const getGapX = (gapX: 'xs' | 'sm' | 'md' | 'lg' | 'xl') => {
switch (gapX) {
case 'xs':
return 'gap-x-1';
case 'sm':
return 'gap-x-2';
case 'lg':
return 'gap-x-6';
case 'xl':
return 'gap-x-8';
default:
return 'gap-x-4';
}
};
const getGapY = (gapY: 'xs' | 'sm' | 'md' | 'lg' | 'xl') => {
switch (gapY) {
case 'xs':
return 'gap-y-1';
case 'sm':
return 'gap-y-2';
case 'lg':
return 'gap-y-6';
case 'xl':
return 'gap-y-8';
default:
return 'gap-y-4';
}
};
const getAlign = (align: 'start' | 'center' | 'end' | 'stretch') => {
switch (align) {
case 'center':
return 'items-center';
case 'end':
return 'items-end';
case 'stretch':
return 'items-stretch';
default:
return 'items-start';
}
};
const getJustify = (justify: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly') => {
switch (justify) {
case 'center':
return 'justify-center';
case 'end':
return 'justify-end';
case 'between':
return 'justify-between';
case 'around':
return 'justify-around';
case 'evenly':
return 'justify-evenly';
default:
return 'justify-start';
}
};
// =============================================================================
// MAIN RENDER
// =============================================================================
const gridClasses = cn(
'grid',
getResponsiveCols(),
gapX ? getGapX(gapX) : gapY ? getGapY(gapY) : getGap(gap),
getAlign(align),
getJustify(justify),
className
);
return (
<div className={gridClasses}>
{children}
</div>
);
}
// =============================================================================
// GRID ITEM COMPONENT
// =============================================================================
export interface FormGridItemProps {
// Span configuration
span?: 1 | 2 | 3 | 4 | 6 | 12;
sm?: 1 | 2 | 3 | 4 | 6 | 12;
md?: 1 | 2 | 3 | 4 | 6 | 12;
lg?: 1 | 2 | 3 | 4 | 6 | 12;
xl?: 1 | 2 | 3 | 4 | 6 | 12;
// Alignment
align?: 'start' | 'center' | 'end' | 'stretch';
justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';
// Styling
className?: string;
// Children
children: React.ReactNode;
}
export function FormGridItem({
span = 1,
sm,
md,
lg,
xl,
align,
justify,
className = '',
children,
}: FormGridItemProps) {
const getSpan = (span: number) => {
return `col-span-${span}`;
};
const getResponsiveSpan = () => {
const classes = [getSpan(span)];
if (sm) classes.push(`sm:col-span-${sm}`);
if (md) classes.push(`md:col-span-${md}`);
if (lg) classes.push(`lg:col-span-${lg}`);
if (xl) classes.push(`xl:col-span-${xl}`);
return classes.join(' ');
};
const getAlign = (align: 'start' | 'center' | 'end' | 'stretch') => {
switch (align) {
case 'center':
return 'self-center';
case 'end':
return 'self-end';
case 'stretch':
return 'self-stretch';
default:
return 'self-start';
}
};
const getJustify = (justify: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly') => {
switch (justify) {
case 'center':
return 'justify-self-center';
case 'end':
return 'justify-self-end';
case 'between':
return 'justify-self-stretch';
case 'around':
return 'justify-self-stretch';
case 'evenly':
return 'justify-self-stretch';
default:
return 'justify-self-start';
}
};
const itemClasses = cn(
getResponsiveSpan(),
align && getAlign(align),
justify && getJustify(justify),
className
);
return (
<div className={itemClasses}>
{children}
</div>
);
}
export default FormGrid;