import * as React from 'react'; import classNames from 'classnames'; import type { Breakpoint, ScreenMap } from '../_util/responsiveObserver'; import { responsiveArray } from '../_util/responsiveObserver'; import { ConfigContext } from '../config-provider'; import useBreakpoint from './hooks/useBreakpoint'; import useGutter from './hooks/useGutter'; import RowContext from './RowContext'; import type { RowContextState } from './RowContext'; import { useRowStyle } from './style'; const _RowAligns = ['top', 'middle', 'bottom', 'stretch'] as const; const _RowJustify = [ 'start', 'end', 'center', 'space-around', 'space-between', 'space-evenly', ] as const; type Responsive = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs'; type ResponsiveLike = { [key in Responsive]?: T; }; export type Gutter = number | undefined | Partial>; type ResponsiveAligns = ResponsiveLike<(typeof _RowAligns)[number]>; type ResponsiveJustify = ResponsiveLike<(typeof _RowJustify)[number]>; export interface RowProps extends React.HTMLAttributes { gutter?: Gutter | [Gutter, Gutter]; align?: (typeof _RowAligns)[number] | ResponsiveAligns; justify?: (typeof _RowJustify)[number] | ResponsiveJustify; prefixCls?: string; wrap?: boolean; } function useMergedPropByScreen( oriProp: RowProps['align'] | RowProps['justify'], screen: ScreenMap | null, ) { const [prop, setProp] = React.useState(typeof oriProp === 'string' ? oriProp : ''); const calcMergedAlignOrJustify = () => { if (typeof oriProp === 'string') { setProp(oriProp); } if (typeof oriProp !== 'object') { return; } for (let i = 0; i < responsiveArray.length; i++) { const breakpoint: Breakpoint = responsiveArray[i]; // if do not match, do nothing if (!screen || !screen[breakpoint]) { continue; } const curVal = oriProp[breakpoint]; if (curVal !== undefined) { setProp(curVal); return; } } }; React.useEffect(() => { calcMergedAlignOrJustify(); }, [JSON.stringify(oriProp), screen]); return prop; } const Row = React.forwardRef((props, ref) => { const { prefixCls: customizePrefixCls, justify, align, className, style, children, gutter = 0, wrap, ...others } = props; const { getPrefixCls, direction } = React.useContext(ConfigContext); const screens = useBreakpoint(true, null); const mergedAlign = useMergedPropByScreen(align, screens); const mergedJustify = useMergedPropByScreen(justify, screens); const prefixCls = getPrefixCls('row', customizePrefixCls); const [wrapCSSVar, hashId, cssVarCls] = useRowStyle(prefixCls); const gutters = useGutter(gutter, screens); const classes = classNames( prefixCls, { [`${prefixCls}-no-wrap`]: wrap === false, [`${prefixCls}-${mergedJustify}`]: mergedJustify, [`${prefixCls}-${mergedAlign}`]: mergedAlign, [`${prefixCls}-rtl`]: direction === 'rtl', }, className, hashId, cssVarCls, ); // Add gutter related style const rowStyle: React.CSSProperties = {}; const horizontalGutter = gutters[0] != null && gutters[0] > 0 ? gutters[0] / -2 : undefined; if (horizontalGutter) { rowStyle.marginLeft = horizontalGutter; rowStyle.marginRight = horizontalGutter; } // "gutters" is a new array in each rendering phase, it'll make 'React.useMemo' effectless. // So we deconstruct "gutters" variable here. const [gutterH, gutterV] = gutters; rowStyle.rowGap = gutterV; const rowContext = React.useMemo( () => ({ gutter: [gutterH, gutterV] as [number, number], wrap }), [gutterH, gutterV, wrap], ); return wrapCSSVar(
{children}
, ); }); if (process.env.NODE_ENV !== 'production') { Row.displayName = 'Row'; } export default Row;