import * as React from 'react'; import classNames from 'classnames'; import warning from '../_util/warning'; import ResponsiveObserve, { Breakpoint, BreakpointMap, responsiveArray, } from '../_util/responsiveObserve'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; export interface DescriptionsItemProps { prefixCls?: string; className?: string; label?: React.ReactNode; children: React.ReactNode; span?: number; } const DescriptionsItem: React.SFC = ({ children }) => children as JSX.Element; export interface DescriptionsProps { prefixCls?: string; className?: string; style?: React.CSSProperties; bordered?: boolean; size?: 'middle' | 'small' | 'default'; children?: React.ReactNode; title?: string; column?: number | Partial>; layout?: 'horizontal' | 'vertical'; colon?: boolean; } /** * Convert children into `column` groups. * @param cloneChildren: DescriptionsItem * @param column: number */ const generateChildrenRows = ( cloneChildren: React.ReactNode, column: number, ): React.ReactElement[][] => { const childrenArray: React.ReactElement[][] = []; let columnArray: React.ReactElement[] = []; let totalRowSpan = 0; React.Children.forEach(cloneChildren, (node: React.ReactElement) => { columnArray.push(node); if (node.props.span) { totalRowSpan += node.props.span; } else { totalRowSpan += 1; } if (totalRowSpan >= column) { warning( totalRowSpan <= column, 'Descriptions', 'Sum of column `span` in a line exceeds `column` of Descriptions.', ); childrenArray.push(columnArray); columnArray = []; totalRowSpan = 0; } }); if (columnArray.length > 0) { childrenArray.push(columnArray); columnArray = []; } return childrenArray; }; /** * This code is for handling react15 does not support returning an array, * It can convert a children into th and td * @param child DescriptionsItem * @returns * <> * {DescriptionsItem.label} * {DescriptionsItem.children} * */ const renderCol = ( child: React.ReactElement, bordered: boolean, colon: boolean, ) => { const { prefixCls, label, className, children, span = 1 } = child.props; if (bordered) { return [ {label} , {children} , ]; } return ( {label} {children} ); }; const renderLabelCol = ( child: React.ReactElement, bordered: boolean, colon: boolean, ) => { const { prefixCls, label, span = 1 } = child.props; if (bordered) { return ( {label} ); } return ( {label} ); }; const renderContentCol = (child: React.ReactElement, bordered: boolean) => { const { prefixCls, children, span = 1 } = child.props; if (bordered) { return ( {children} ); } return ( {children} ); }; const renderRow = ( children: React.ReactElement[], index: number, { prefixCls, column, isLast }: { prefixCls: string; column: number; isLast: boolean }, bordered: boolean, layout: string, colon: boolean, ) => { // copy children,prevent changes to incoming parameters const childrenArray = [...children]; let lastChildren = childrenArray.pop() as React.ReactElement; const span = column - childrenArray.length; if (isLast) { lastChildren = React.cloneElement(lastChildren as React.ReactElement, { span, }); } if (layout === 'vertical') { const cloneLabelChildren = React.Children.map( childrenArray, (childrenItem: React.ReactElement) => { return renderLabelCol(childrenItem, bordered, colon); }, ); const cloneContentChildren = React.Children.map( childrenArray, (childrenItem: React.ReactElement) => { return renderContentCol(childrenItem, bordered); }, ); return [ {cloneLabelChildren} {renderLabelCol(lastChildren, bordered, colon)} , {cloneContentChildren} {renderContentCol(lastChildren, bordered)} , ]; } const cloneChildren = React.Children.map( childrenArray, (childrenItem: React.ReactElement) => { return renderCol(childrenItem, bordered, colon); }, ); return ( {cloneChildren} {renderCol(lastChildren, bordered, colon)} ); }; const defaultColumnMap = { xxl: 3, xl: 3, lg: 3, md: 3, sm: 2, xs: 1, }; class Descriptions extends React.Component< DescriptionsProps, { screens: BreakpointMap; } > { static defaultProps: DescriptionsProps = { size: 'default', column: defaultColumnMap, }; static Item: typeof DescriptionsItem = DescriptionsItem; state: { screens: BreakpointMap; } = { screens: {}, }; token: string; componentDidMount() { const { column } = this.props; this.token = ResponsiveObserve.subscribe(screens => { if (typeof column !== 'object') { return; } this.setState({ screens, }); }); } componentWillUnmount() { ResponsiveObserve.unsubscribe(this.token); } getColumn(): number { const { column } = this.props; if (typeof column === 'object') { for (let i = 0; i < responsiveArray.length; i++) { const breakpoint: Breakpoint = responsiveArray[i]; if (this.state.screens[breakpoint] && column[breakpoint] !== undefined) { return column[breakpoint] || defaultColumnMap[breakpoint]; } } } //If the configuration is not an object, it is a number, return number if (typeof column === 'number') { return column as number; } // If it is an object, but no response is found, this happens only in the test. // Maybe there are some strange environments return 3; } render() { return ( {({ getPrefixCls }: ConfigConsumerProps) => { const { className, prefixCls: customizePrefixCls, title, size, children, bordered = false, layout = 'horizontal', colon = true, } = this.props; const prefixCls = getPrefixCls('descriptions', customizePrefixCls); const column = this.getColumn(); const cloneChildren = React.Children.map( children, (child: React.ReactElement) => { if (React.isValidElement(child)) { return React.cloneElement(child, { prefixCls, }); } return child; }, ); const childrenArray: Array< React.ReactElement[] > = generateChildrenRows(cloneChildren, column); return (
{title &&
{title}
}
{childrenArray.map((child, index) => renderRow( child, index, { prefixCls, column, isLast: index + 1 === childrenArray.length, }, bordered, layout, colon, ), )}
); }}
); } } export default Descriptions;