import { isDefined } from '@o/utils'
import React, { Fragment, isValidElement } from 'react'
import { hasMediaQueries, mediaQueryKeysSpace } from './mediaQueryKeys'
import { Size, Space } from './Space'
export type SpaceGroupProps = {
children?: React.ReactNode
space?: Size
spaceAround?: Size
separator?: React.ReactNode
beforeSpace?: React.ReactNode
afterSpace?: React.ReactNode
}
const childrenToArr = (x: React.ReactNode): JSX.Element[] =>
React.Children.toArray(x).filter(y => y !== null && y !== false) as any
export function SpaceGroup(props: SpaceGroupProps) {
return createSpacedChildren(props, props)
}
function getChildrenForSpacing(childs: React.ReactNode) {
let children = []
// Allows special cases for unwrapping/forwarding spacing
// Allow nested unwraps, for example see
for (const child of React.Children.toArray(childs)) {
if (!isValidElement(child)) {
if (child === null || child === false || child === undefined || child === '') {
continue
}
children.push(child)
continue
}
const type = child.type
if (type === React.Fragment || (type && type['canUnwrap'])) {
const subChildren = child.props['children']
if (!subChildren) {
continue
}
const next = childrenToArr(subChildren)
children = [...children, ...next]
} else {
children.push(child)
}
}
return children
}
export function createSpacedChildren(
props: SpaceGroupProps,
// allow media query sizes like sm-size md-size
allProps: {
[key: string]: any
},
) {
if (!props.children) {
return null
}
const childs = getChildrenForSpacing(props.children)
const total = childs.length
// media query props
let sizeMediaQueries = null
if (hasMediaQueries) {
for (const key in mediaQueryKeysSpace) {
if (isDefined(allProps[key])) {
sizeMediaQueries = sizeMediaQueries || {}
sizeMediaQueries[key.replace('-space', '-size')] = allProps[key]
}
}
}
if ((!props.space && !props.spaceAround) || (!props.spaceAround && total <= 1)) {
return (
<>
{props.beforeSpace}
{props.children}
{props.afterSpace}
>
)
}
const spaceElement = props.separator ||
const spaceAroundElement =
props.separator || props.spaceAround === true ? (
spaceElement
) : (
)
return (
<>
{props.beforeSpace}
{props.spaceAround && spaceAroundElement}
{childs.map((child, index) => {
const isSpace = child && child.type && child.type.isSpace
const isNextSpace =
childs[index + 1] && childs[index + 1].type && childs[index + 1].type.isSpace
return isSpace || isNextSpace ? (
{child}
) : (
{child}
{index !== total - 1 && spaceElement}
)
})}
{props.spaceAround && spaceAroundElement}
{props.afterSpace}
>
)
}