import * as React from "react"; import {cloneElement, createContext, CSSProperties, useCallback, useContext, useState} from "react"; import {Dimensions} from "./dimensions"; export enum Align { Center = 'center', Stretch = 'stretch', FlexStart = 'flex-start', FlexEnd = 'flex-end', } export enum JustifyContent { SpaceAround = 'space-around', SpaceEvenly = 'space-evenly', SpaceBetween = 'space-between', NoSpace = 'space-none' } export enum FlexDirection { Row = 'row', Column = 'column', } export enum FlexWrap { Wrap = 'wrap', NoWrap = 'nowrap', WrapReverse = 'wrap-reverse', } interface UseFlexValues extends Dimensions { // width: number // height: number flexDirection: FlexDirection } const initialFlexValues: UseFlexValues = { width: 0, height: 0, flexDirection: FlexDirection.Row } const FlexContext = createContext(initialFlexValues) interface FlexProps { // supplies the dimensions of the (parent) container whose dimensions // this grid uses. dimensionsSupplier?: () => Dimensions // the layout direction, or the direction of the main axis flexDirection?: FlexDirection // distribution of space around the main axis (i.e. if flex direction is row, this // would distribute the horizontal space,or if the flex direction is column, this would // distribute the vertical space) justifyContent?: JustifyContent // distribution of space around the cross-axis (i.e. if flex direction is row, this // would distribute the vertical space,or if the flex direction is column, this would // distribute the horizontal space) alignContent?: JustifyContent // alignment for the cross-axis (i.e. if flex direction is row, this would align vertically, // or if the flex direction is column, this would align horizontally) which sets the `align-self` // property on all the children alignItems?: Align // sets whether items are forced on one line (no-wrap) or are allowed to wrap on multiple lines flexWrap?: FlexWrap // the vertical gap between columns columnGap?: number // the horizontal gap between rows rowGap?: number // additional styles passed from the parent component styles?: CSSProperties // the children of the flex container, which must be types children: JSX.Element | Array } export function FlexContainer(props: FlexProps): JSX.Element { const { dimensionsSupplier, flexDirection = FlexDirection.Row, justifyContent, alignContent, alignItems = Align.Center, flexWrap = FlexWrap.NoWrap, columnGap, rowGap, styles = {}, children } = props const {width, height} = dimensionsSupplier ? dimensionsSupplier() : {width: 0, height: 0} function enrich(children: JSX.Element | Array): JSX.Element | Array { const childElements = Array.isArray(children) ? children : [children]; return childElements.map(child => cloneElement(child, { width, height, flexDirection, })) } return (
{enrich(children)}
) } interface UseFlexItemValues extends Dimensions { // width: number // height: number flexDirection: FlexDirection } const initialFlexItemValues: UseFlexItemValues = { width: 0, height: 0, flexDirection: FlexDirection.Row } const FlexItemContext = createContext(initialFlexItemValues) interface FlexItemProps { // property sets the initial main size of a flex item. it sets the size of the // content box unless otherwise set with box-sizing. flexBasis?: number // sets the grow property for the main-axis size flexGrow?: number // sets the shrink property for the main-axis size flexShrink?: number justifyContent?: JustifyContent justifySelf?: JustifyContent alignSelf?: Align order?: number children: JSX.Element | Array } export function FlexItem(props: FlexItemProps): JSX.Element { const { flexGrow, flexBasis, flexShrink, justifyContent = JustifyContent.SpaceAround, justifySelf, alignSelf, order, children, } = props const {width, height, flexDirection} = useContext(FlexContext) const [cellDimensions, setCellDimensions] = useState({width: 0, height: 0}) const divRef = useCallback( (node: HTMLDivElement) => { if (node !== null) { // const {width: cellWidth, height: cellHeight} = node.getBoundingClientRect() const dims = node.getBoundingClientRect() if (flexDirection === FlexDirection.Row) { dims.height = height || dims.height } else { dims.width = width || dims.width } setCellDimensions({width: Math.floor(dims.width), height: Math.floor(dims.height)}) } }, [flexDirection, height, width] ) function enrich(children: JSX.Element | Array): JSX.Element | Array { const childElements = Array.isArray(children) ? children : [children]; return childElements.map(child => cloneElement(child, { width: cellDimensions.width, height: cellDimensions.height })) } return (
{enrich(children)}
) } export function useFlexItem(): UseFlexItemValues { const context = useContext(FlexItemContext) return context }