Created
July 24, 2020 16:17
-
-
Save paibamboo/253031bd8f46ae258cfa2e0ce90108a9 to your computer and use it in GitHub Desktop.
Tile layout using display grid
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from "react"; | |
import {classes, style as typestyle} from "typestyle"; | |
import {buildBreakpoints} from "../../../helpers/buildBreakpoints"; | |
import {ITileBreakpoint} from "./interfaces"; | |
export interface IProps { | |
spanColumn?: number; | |
spanRow?: number; | |
/** | |
* Internal usage | |
*/ | |
rowGap?: number; | |
/** | |
* Internal usage | |
*/ | |
colGap?: number; | |
/** | |
* No need to assign this explicitly. It will be assigned by GridContainer parent. | |
*/ | |
breakpoints?: ITileBreakpoint[]; | |
className?: string; | |
style?: React.CSSProperties; | |
} | |
export class Tile extends React.Component<IProps> { | |
public static defaultProps: Partial<IProps> = { | |
breakpoints: [], | |
spanColumn: 1, | |
spanRow: 1 | |
}; | |
public render(): JSX.Element { | |
const {breakpoints, spanColumn, spanRow, rowGap, colGap, children, className, style} = this.props; | |
// todo: In case of flex, when colGap is defined, distant of left-most and right-most item should be 0 from parent | |
const classNames = { | |
gridItem: typestyle( | |
{ | |
$debugName: "gridItem", | |
$nest: { | |
"@supports (display: grid)": { | |
gridColumn: "span " + spanColumn, | |
gridRow: "span " + spanRow, | |
margin: 0 | |
} | |
}, | |
flexBasis: "auto", | |
marginBottom: rowGap ? rowGap / 2 + "em" : 0, | |
marginLeft: colGap ? colGap / 2 + "em" : 0, | |
marginRight: colGap ? colGap / 2 + "em" : 0, | |
marginTop: rowGap ? rowGap / 2 + "em" : 0 | |
}, | |
...buildBreakpoints(breakpoints, (breakpoint: ITileBreakpoint) => [ | |
{ | |
$nest: { | |
"@supports (display: grid)": { | |
width: "auto" | |
} | |
}, | |
width: colGap ? | |
`calc(${100 / breakpoint.numOfColumns }% - ${colGap}em)` : | |
`${100 / breakpoint.numOfColumns }%` | |
} | |
]) | |
) | |
}; | |
return ( | |
<div className={classes(classNames.gridItem, className)} style={style}> | |
{children} | |
</div> | |
); | |
} | |
} | |
export default Tile; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from "react"; | |
import {classes, style as typestyle} from "typestyle"; | |
import {buildBreakpoints} from "../../../helpers/buildBreakpoints"; | |
import {ITileBreakpoint} from "./interfaces"; | |
import {IProps as ITileProps} from "./Tile"; | |
export interface IProps { | |
/** | |
* row height in em unit | |
*/ | |
rowHeight?: number; | |
/** | |
* row gap in em unit | |
*/ | |
rowGap?: number; | |
/** | |
* col gap in em unit | |
*/ | |
colGap?: number; | |
/** | |
* specifies number of columns for each breakpoint. Default value is `[{minWidth: 0, numOfColumns: 4}]` | |
*/ | |
breakpoints?: ITileBreakpoint[]; | |
className?: string; | |
style?: React.CSSProperties; | |
} | |
/** | |
* Layout using display grid. Will fallback to display flex if display grid is not supported. | |
*/ | |
export class TilesContainer extends React.Component<IProps> { | |
public static defaultProps: Partial<IProps> = { | |
breakpoints: [{minWidth: 0, numOfColumns: 4}], | |
colGap: 0, | |
rowGap: 0 | |
}; | |
public render(): JSX.Element { | |
const {breakpoints, rowHeight, rowGap, colGap, className, style} = this.props; | |
const children = React.Children.map(this.props.children, (child: React.ReactElement<ITileProps>) => | |
React.cloneElement(child, {breakpoints, colGap, rowGap}) | |
); | |
const classNames = { | |
tilesContainer: typestyle( | |
{ | |
$debugName: "tilesContainer", | |
$nest: { | |
"@supports (display: grid)": { | |
display: "grid", | |
gridAutoFlow: "row dense", | |
gridAutoRows: rowHeight ? rowHeight + "em" : "auto", | |
gridColumnGap: colGap + "em", | |
gridRowGap: rowGap + "em" | |
} | |
}, | |
display: "flex", | |
flexWrap: "wrap" | |
}, | |
...buildBreakpoints(breakpoints, (breakpoint: ITileBreakpoint) => { | |
const numOfColumns = breakpoint.numOfColumns; | |
const baseItemWidth = Math.round(100 / numOfColumns * 100) / 100; | |
const itemGap = Math.round(colGap * (numOfColumns - 1) / numOfColumns * 100) / 100; | |
const itemWidth = colGap ? `calc(${baseItemWidth}% - ${itemGap}em)` : `${baseItemWidth}%`; | |
return [ | |
{ | |
gridTemplateColumns: `repeat(${numOfColumns}, ${itemWidth})` | |
} | |
]; | |
}) | |
) | |
}; | |
return ( | |
<section className={classes(classNames.tilesContainer, className)} style={style}> | |
{children} | |
</section> | |
); | |
} | |
} | |
export default TilesContainer; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment