/* eslint-disable react/no-array-index-key */ import * as React from 'react'; import classNames from 'classnames'; import toArray from 'rc-util/lib/Children/toArray'; import type { Breakpoint, ScreenMap } from '../_util/responsiveObserve'; import ResponsiveObserve, { responsiveArray } from '../_util/responsiveObserve'; import devWarning from '../_util/devWarning'; import { ConfigContext } from '../config-provider'; import Row from './Row'; import DescriptionsItem from './Item'; import { cloneElement } from '../_util/reactNode'; export interface DescriptionsContextProps { labelStyle?: React.CSSProperties; contentStyle?: React.CSSProperties; } export const DescriptionsContext = React.createContext({}); const DEFAULT_COLUMN_MAP: Record = { xxl: 3, xl: 3, lg: 3, md: 3, sm: 2, xs: 1, }; function getColumn(column: DescriptionsProps['column'], screens: ScreenMap): number { if (typeof column === 'number') { return column; } if (typeof column === 'object') { for (let i = 0; i < responsiveArray.length; i++) { const breakpoint: Breakpoint = responsiveArray[i]; if (screens[breakpoint] && column[breakpoint] !== undefined) { return column[breakpoint] || DEFAULT_COLUMN_MAP[breakpoint]; } } } return 3; } function getFilledItem( node: React.ReactElement, span: number | undefined, rowRestCol: number, ): React.ReactElement { let clone = node; if (span === undefined || span > rowRestCol) { clone = cloneElement(node, { span: rowRestCol, }); devWarning( span === undefined, 'Descriptions', 'Sum of column `span` in a line not match `column` of Descriptions.', ); } return clone; } function getRows(children: React.ReactNode, column: number) { const childNodes = toArray(children).filter(n => n); const rows: React.ReactElement[][] = []; let tmpRow: React.ReactElement[] = []; let rowRestCol = column; childNodes.forEach((node, index) => { const span: number | undefined = node.props?.span; const mergedSpan = span || 1; // Additional handle last one if (index === childNodes.length - 1) { tmpRow.push(getFilledItem(node, span, rowRestCol)); rows.push(tmpRow); return; } if (mergedSpan < rowRestCol) { rowRestCol -= mergedSpan; tmpRow.push(node); } else { tmpRow.push(getFilledItem(node, mergedSpan, rowRestCol)); rows.push(tmpRow); rowRestCol = column; tmpRow = []; } }); return rows; } export interface DescriptionsProps { prefixCls?: string; className?: string; style?: React.CSSProperties; bordered?: boolean; size?: 'middle' | 'small' | 'default'; children?: React.ReactNode; title?: React.ReactNode; extra?: React.ReactNode; column?: number | Partial>; layout?: 'horizontal' | 'vertical'; colon?: boolean; labelStyle?: React.CSSProperties; contentStyle?: React.CSSProperties; } function Descriptions({ prefixCls: customizePrefixCls, title, extra, column = DEFAULT_COLUMN_MAP, colon = true, bordered, layout, children, className, style, size, labelStyle, contentStyle, }: DescriptionsProps) { const { getPrefixCls, direction } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('descriptions', customizePrefixCls); const [screens, setScreens] = React.useState({}); const mergedColumn = getColumn(column, screens); // Responsive React.useEffect(() => { const token = ResponsiveObserve.subscribe(newScreens => { if (typeof column !== 'object') { return; } setScreens(newScreens); }); return () => { ResponsiveObserve.unsubscribe(token); }; }, []); // Children const rows = getRows(children, mergedColumn); const contextValue = React.useMemo( () => ({ labelStyle, contentStyle }), [labelStyle, contentStyle], ); return (
{(title || extra) && (
{title &&
{title}
} {extra &&
{extra}
}
)}
{rows.map((row, index) => ( ))}
); } Descriptions.Item = DescriptionsItem; export default Descriptions;