import { Iterable } from 'ix'; import * as React from 'react'; import * as Layout from './GridLayout'; import { Panel, PanelItemContext, PanelProps } from './Panel'; export { RowDefinition, ColumnDefinition } from './GridLayout'; export interface GridRenderProps { /** * true to display a border around grid cells */ border?: boolean; } export interface GridProps< T = {}, TContext extends PanelItemContext = PanelItemContext > extends PanelProps, GridRenderProps {} export interface GridComponentProps extends React.HTMLProps, GridProps {} export class Grid extends Panel { public static displayName = 'Grid'; /** * Use this component to define your Row collection * i.e., */ public static Rows = Layout.GridRowDefinitions; /** * Use this component to define your Column collection * i.e., */ public static Columns = Layout.GridColumnDefinitions; render() { const { border, ...rest } = this.props; const bordered = { 'Grid-Border': border === true }; const compact = { compact: this.props.compact }; return this.renderPanel( this.wxr.classNames('Grid', bordered, compact), rest, ); } renderItems(allChildren?: React.ReactNode, componentClass?: React.ReactType) { const { children, rows, cols } = this.getLayout(); return Iterable.range(0, rows.length) .map(row => { const colItems = Iterable.range(0, cols.length) .map(col => { return this.renderColumn(row, col, rows, cols, children); }) .toArray(); return this.renderRow(row, rows, colItems); }) .defaultIfEmpty(this.renderEmpty()) .toArray(); } protected renderRow( row: number, rows: Layout.GridLayoutDefinition[], colItems: React.ReactNode, ) { const def = rows[row]; const t = def.itemTemplate; const context = { row, index: row }; const itemClassName = Panel.getPanelItemPropValue( def.itemClassName, context, ); const itemStyle = Panel.getPanelItemPropValue(def.itemStyle, context); const itemProps = Panel.getPanelItemPropValue(def.itemProps, context) || {}; const compact = { compact: this.props.compact }; const itemTemplate = def.itemTemplate; const layoutStyle = Object.assign({}, itemStyle, { height: this.getCellLayoutValue(def), }); const fragment = (
{colItems}
); if (itemTemplate == null) { return fragment; } return itemTemplate(fragment, context); } protected renderColumn( row: number, column: number, rows: Layout.GridLayoutDefinition[], cols: Layout.GridLayoutDefinition[], children: React.ReactChild[], ) { const def = cols[column]; const cellItems: React.ReactChild[] = []; let index = 0; while (index < children.length) { const child: any = children[index]; const childProps = child != null && child.props != null ? child.props : undefined; const desiredRow = (childProps == null ? undefined : childProps['data-grid-row']) || 0; const desiredCol = (childProps == null ? undefined : childProps['data-grid-column']) || 0; if (desiredRow === row && desiredCol === column) { const cellItem = React.isValidElement(child) ? React.cloneElement(child, { 'data-grid-row': undefined, 'data-grid-column': undefined, }) : child; cellItems.push(cellItem); children.splice(index, 1); } else { ++index; } } const context = { row, column, index }; const itemClassName = Panel.getPanelItemPropValue( def.itemClassName, context, ); const itemStyle = Panel.getPanelItemPropValue(def.itemStyle, context); const itemProps = Panel.getPanelItemPropValue(def.itemProps, context) || {}; const compact = { compact: this.props.compact }; const itemTemplate = def.itemTemplate; const layoutStyle = Object.assign({}, itemStyle, { width: this.getCellLayoutValue(def), }); const fragment = (
{super.renderItems(cellItems)}
); if (itemTemplate == null) { return fragment; } return itemTemplate(fragment, context); } protected getCellLayoutValue(def: Layout.GridLayoutDefinition) { return def.stretch ? `${(def.amount || 1) * 100}%` : def.amount; } protected getLayout() { const children = React.Children.toArray(this.props.children); let rows: React.ReactElement | undefined; let cols: React.ReactElement | undefined; let index = 0; while (index < children.length) { const elem: any = children[index]; if (React.isType(elem, Layout.GridRowDefinitions)) { rows = elem; children.splice(index, 1); } else if (React.isType(elem, Layout.GridColumnDefinitions)) { cols = elem; children.splice(index, 1); } else { ++index; } if (rows != null && cols != null) { break; } } return { children, rows: this.getLayoutDefinitions(rows), cols: this.getLayoutDefinitions(cols), }; } protected getLayoutDefinitions( elem: Layout.GridLayoutDefinitionGroupElement | undefined, ) { if (elem == null) { return [new Layout.GridLayoutDefinition()]; } return React.Children.map( elem.props.children, (x: Layout.GridLayoutDefinitionElement) => { return new Layout.GridLayoutDefinition(x, elem); }, ); } }