Skip to content

Commit

Permalink
feat(Flex): deprecate spacing in favor of gap with more CSS like …
Browse files Browse the repository at this point in the history
…appearance
  • Loading branch information
tujoworker committed May 15, 2024
1 parent 5f93866 commit 67885dc
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 22 deletions.
106 changes: 88 additions & 18 deletions packages/dnb-eufemia/src/components/flex/Container.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import React, { useCallback } from 'react'
import React, { isValidElement, useCallback } from 'react'
import classnames from 'classnames'
import Space, { SpaceProps } from '../space/Space'
import { Hr } from '../../elements'
import useMedia from '../../shared/useMedia'
import { sumTypes } from '../space/SpacingUtils'
import {
getSpaceValue,
isHeadingElement,
pickSpacingProps,
renderWithSpacing,
} from './utils'

import type { MediaQueryBreakpoints } from '../../shared/MediaQueryUtils'
import type { SpaceType } from '../space/types'
import type { UseMediaQueries } from '../../shared/useMedia'
import type { End, Start } from './types'
import Item from './Item'

export type BasicProps = {
direction?: 'horizontal' | 'vertical'
Expand Down Expand Up @@ -108,33 +111,72 @@ function FlexContainer(props: Props) {
const hasSizeProp =
!hasHeading &&
direction === 'horizontal' &&
childrenArray.some((child) => child['props']?.size)
childrenArray.some(
(child) =>
isValidElement(child) &&
child['type'] === Item &&
child.props?.['size']
)

const { key: mediaKey } = useMedia({
disabled: !hasSizeProp,
breakpoints,
queries,
})

let rowCount = 0
const content = childrenArray.map((child, i) => {
let endSpacing = null
let startSpacing = null

// Set spacing on child components by props (instead of CSS) to be able to dynamically override by props on each child. The default
// is the spacing-props that controls space between children. Then override with props sent to the children, including both top
// and bottom when th
const isFirst = i === 0
const isLast = i >= childrenArray.length - 1

// Used for horizontal layout only
let size = child?.['props']?.['size']

if (
hasSizeProp &&
isValidElement(child) &&
child['type'] === Item &&
size
) {
// Same logic as in the flex-item.scss file
switch (mediaKey) {
case 'small':
size = parseFloat(size[mediaKey] ?? size['medium'] ?? size) || 0
break
case 'medium':
size = parseFloat(size[mediaKey] ?? size['large'] ?? size) || 0
break
case 'large':
size = parseFloat(size[mediaKey] ?? size['medium'] ?? size) || 0
break
}

rowCount = rowCount + size
}

const isRowStart = rowCount === size
const isRowEnd = rowCount === 0

if (rowCount >= sizeCount) {
rowCount = 0
}

const previousChild = childrenArray?.[i - 1]
const isHeading = hasHeading && isHeadingElement(previousChild)

// Always set spacing between elements in the vertical layout on the top props, and 0 on bottom, to avoid
// having to divide spacing between both with smaller values.
const start: Start = direction === 'horizontal' ? 'left' : 'top'
const end: End = direction === 'horizontal' ? 'right' : 'bottom'
// const start: Start | End = direction === 'horizontal' ? 'right' : 'top'
// const end: Start | End = direction === 'horizontal' ? 'left' : 'bottom'
const endSpacing = 0
let startSpacing = null

if (
direction !== 'horizontal' &&
// No line above heading
!isHeading &&
((divider === 'line' && !isFirst) || divider === 'line-framed')
Expand All @@ -151,7 +193,7 @@ function FlexContainer(props: Props) {
/>

{renderWithSpacing(child, {
space: { [start]: startSpacing, [end]: endSpacing },
space: { [start]: startSpacing, [end]: endSpacing || 0 },
})}

{divider === 'line-framed' && isLast && (
Expand All @@ -165,15 +207,33 @@ function FlexContainer(props: Props) {
)
}

// No space above first element.
if (isFirst && direction !== 'horizontal') {
startSpacing = 0
if (direction === 'horizontal') {
if (hasSizeProp) {
// When we have a size prop, we don't expect the layout to wrap,
// so we add space to the start of each item to mimic CSS gap.
startSpacing = isRowStart || isFirst ? 0 : sumTypes(spacing) / 2
endSpacing = isRowEnd || isLast ? 0 : sumTypes(spacing) / 2
} else {
// Since we expect the layout to wrap, we add space only to the end of each item,
// except for the last item. This will make the items align as long as not wrapped.
// When wrapped, the items will align to the start of the container, but be a little off to the right.
endSpacing = isLast
? 0
: getSpaceValue(start, child) ??
getSpaceValue(end, previousChild) ??
spacing
}
} else {
// Since top space of current and bottom space of previous component is the same
startSpacing =
getSpaceValue(start, child) ??
getSpaceValue(end, previousChild) ??
spacing
if (isFirst) {
// No space above first element.
startSpacing = 0
} else {
// Since top space of current and bottom space of previous component is the same
startSpacing =
getSpaceValue(start, child) ??
getSpaceValue(end, previousChild) ??
spacing
}
}

if (
Expand All @@ -183,10 +243,20 @@ function FlexContainer(props: Props) {
startSpacing = 0
}

const givenSpacing = {
...child?.['props']?.space,
...pickSpacingProps(child?.['props']),
}
delete givenSpacing.space

const space =
direction === 'horizontal'
? { [start]: endSpacing, [end]: startSpacing }
: { [start]: startSpacing, [end]: endSpacing }
childrenArray.length === 1
? givenSpacing
: {
[start]: startSpacing || 0,
[end]: endSpacing || 0,
...givenSpacing,
}

return renderWithSpacing(child, {
key: child?.['key'] || `element-${i}`,
Expand Down
15 changes: 12 additions & 3 deletions packages/dnb-eufemia/src/components/flex/__tests__/Item.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,12 @@ describe('Flex.Item', () => {
<Flex.Item right="large" size={2}>
FlexItem
</Flex.Item>
<Flex.Item left="x-large" right="large" size={2}>
<Flex.Item
left="x-large"
right="large"
space={{ top: '1.5rem' }}
size={2}
>
FlexItem
</Flex.Item>
<Flex.Item size={2}>FlexItem</Flex.Item>
Expand All @@ -319,8 +324,12 @@ describe('Flex.Item', () => {

expect(getSpacingClasses()).toEqual([
['dnb-space__left--zero', 'dnb-space__right--large'],
['dnb-space__left--x-large', 'dnb-space__right--large'],
['dnb-space__left--zero', 'dnb-space__right--large'],
[
'dnb-space__left--x-large',
'dnb-space__right--large',
'dnb-space__top--medium',
],
['dnb-space__left--zero', 'dnb-space__right--small'],
])

rerender(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
&--direction {
&-horizontal {
flex-direction: row;
margin-right: calc(var(--gap) * -1);
}

&-vertical {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
--sizeCount--default: 12;
--size--default: var(--small);

// Same logic as in the Flex.Container.tsx file
.dnb-flex-container[data-media-key='small'] & {
--size: var(--small, var(--medium));
}
Expand Down

0 comments on commit 67885dc

Please sign in to comment.