import type { ReactNode, HTMLAttributes } from 'react'
import React, { forwardRef } from 'react'
import { twMerge } from '@toptal/picasso-tailwind-merge'
import { type BaseProps } from '@toptal/picasso-shared'
import type { BreakpointKeys } from '@toptal/picasso-provider'
import { useCurrentBreakpointRange } from '@toptal/picasso-provider'
import { GridContext } from '../GridContext'
import type {
GridDirection,
GridItemsAlignment,
GridJustification,
GridSpacing,
GridWrap,
} from '../types'
import { getGridSpacingClassName } from './utils/get-grid-spacing-class-name'
export interface Props extends BaseProps, HTMLAttributes {
/** Grid content containing Grid.Item */
children?: ReactNode
/** Defines amount of space between Grid.Item components (in px). If spacing is not set, then it will
* be automatically adjusted based on the screen size (16px for screens smaller than medium, 24px
* for medium screens, and 32px for screens bigger than medium)
*/
spacing?: GridSpacing
/** Defines the orientation of the grid */
direction?: GridDirection
/** Defines the align-items style property based on the direction */
alignItems?: GridItemsAlignment
/** Defines the justify-content style property based on the direction */
justifyContent?: GridJustification
/** Defines the flex-wrap style property based on the direction */
wrap?: GridWrap
}
const responsiveSpacingConfiguration: Record = {
xs: 16,
sm: 16,
md: 24,
lg: 32,
xl: 32,
}
const useResponsiveSpacing = () => {
const { currentBreakpointRange } = useCurrentBreakpointRange()
if (currentBreakpointRange) {
return responsiveSpacingConfiguration[currentBreakpointRange]
}
return responsiveSpacingConfiguration.md
}
const directionClassNamesMapping: { [K in GridDirection]: string } = {
row: 'flex-row',
'row-reverse': 'flex-row-reverse',
column: 'flex-col',
'column-reverse': 'flex-col-reverse',
}
const wrapClassNamesMapping: { [K in GridWrap]: string } = {
wrap: 'flex-wrap',
nowrap: 'flex-nowrap',
'wrap-reverse': 'flex-wrap-reverse',
}
const alignItemsClassNamesMapping: { [K in GridItemsAlignment]: string } = {
'flex-start': 'items-start',
center: 'items-center',
'flex-end': 'items-end',
stretch: 'items-stretch',
baseline: 'items-baseline',
}
const justifyContentClassNamesMapping: { [K in GridJustification]: string } = {
'flex-start': 'justify-start',
center: 'justify-center',
'flex-end': 'justify-end',
'space-between': 'justify-between',
'space-around': 'justify-around',
'space-evenly': 'justify-evenly',
}
export const Grid = forwardRef(function Grid(
{
direction = 'row',
alignItems = 'flex-start',
justifyContent = 'flex-start',
wrap = 'wrap',
...props
},
ref
) {
const { children, spacing: userSpacing, className, ...rest } = props
const responsiveSpacing = useResponsiveSpacing()
const gridSpacing = userSpacing ?? responsiveSpacing
const directionClassName = directionClassNamesMapping[direction]
const wrapClassName = wrapClassNamesMapping[wrap]
const alignItemsClassName = alignItemsClassNamesMapping[alignItems]
const justifyContentClassName =
justifyContentClassNamesMapping[justifyContent]
const gridSpacingClassName = getGridSpacingClassName(gridSpacing)
return (
{children}
)
})
Grid.displayName = 'Grid'
export default Grid