diff --git a/apps/pigment-css-vite-app/src/pages/grid.tsx b/apps/pigment-css-vite-app/src/pages/grid.tsx index e0ec1d75..ee70440f 100644 --- a/apps/pigment-css-vite-app/src/pages/grid.tsx +++ b/apps/pigment-css-vite-app/src/pages/grid.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; +import Box from '@pigment-css/react/Box'; import Grid from '@pigment-css/react/Grid'; import { styled } from '@pigment-css/react'; -const Card = styled.div` +const Item = styled.div` background-color: #fff; border: 1px solid #ced7e0; padding: 8px; @@ -10,25 +11,293 @@ const Card = styled.div` text-align: center; `; -const items = [ - { id: '1', size: 2}, - { id: '2', size: 2}, -] +function GridDemo1() { + return ( + + + size=8 + + + size=4 + + + size=4 + + + size=8 + + + ) +} -export default function InteractiveGrid() { +function GridDemo2() { + return ( + + + xs=6 md=8 + + + xs=6 md=4 + + + xs=6 md=4 + + + xs=6 md=8 + + + ) +} +function GridDemo3() { + const [spacing, setSpacing] = React.useState(2); return ( -
- - {items.map(({ id, size, offset, extraText}) => ( - Item {id}{extraText} +
+ + {[0, 1, 2].map((value) => ( + + + + ))} + + + Spacing: + {[0, 0.5, 1, 2, 3, 4, 8, 12].map((value) => ( + + + ))} + +
+ ) +} + +function GridDemo4() { + return ( + + + 1 + + + 2 + + + 3 + + + 4 + + + ) +} + +function GridDemo5() { + return ( + + {Array.from(Array(6)).map((_, index) => ( + + {index + 1} + + ))} + + ) +} + +function GridDemo6() { + return ( + + + grow + + + size=6 + + + grow -
+ + ) +} + +function GridDemo7() { + return ( + + + Variable width item + + + size=6 + + + grow + + + ) +} + +function GridDemo8() { + return ( + + + + Email subscribe section + + + + + + Category A + + +
  • Link 1.1
  • +
  • Link 1.2
  • +
  • Link 1.3
  • +
    +
    +
    + + + + Category B + + +
  • Link 2.1
  • +
  • Link 2.2
  • +
  • Link 2.3
  • +
    +
    +
    + + + + Category C + + +
  • Link 3.1
  • +
  • Link 3.2
  • +
  • Link 3.3
  • +
    +
    +
    + + + + Category D + + +
  • Link 4.1
  • +
  • Link 4.2
  • +
  • Link 4.3
  • +
    +
    +
    +
    + + + © Copyright + + + + Link A + + + Link B + + + Link C + + + +
    +
    + ) +} + +function GridDemo9() { + return ( + + + size=8 + + + size=8 + + + ) +} + +function GridDemo10() { + return ( + + + 1 + + + 2 + + + 3 + + + 4 + + + ) +} + +const demos = [ + { id: '1', component: GridDemo1 }, + { id: '2', component: GridDemo2 }, + { id: '3', component: GridDemo3 }, + { id: '4', component: GridDemo4 }, + { id: '5', component: GridDemo5 }, + { id: '6', component: GridDemo6 }, + { id: '7', component: GridDemo7 }, + { id: '8', component: GridDemo8 }, + { id: '9', component: GridDemo9 }, + { id: '10', component: GridDemo10 }, +] + +export default function InteractiveGrid() { + + return ( +
    + Benchmark v5 + Benchmark next + {demos.map(demo => { + const Demo = demo.component; + return ( +
    +

    Grid Demo {demo.id}

    + +
    + ); + })} +
    ); } diff --git a/packages/pigment-css-react/src/Grid.d.ts b/packages/pigment-css-react/src/Grid.d.ts index ee6e31d5..262d8c85 100644 --- a/packages/pigment-css-react/src/Grid.d.ts +++ b/packages/pigment-css-react/src/Grid.d.ts @@ -6,13 +6,15 @@ import { PolymorphicComponent } from './Box'; type CssProperty = T | Array | Partial>; type GridBaseProps = { - display?: CssProperty<'flex' | 'inline-flex'>; - spacing?: CssProperty; - direction?: CssProperty; - justifyContent?: CssProperty; - alignItems?: CssProperty; - divider?: React.ReactNode; className?: string; + columns?: CssProperty; + columnSpacing?: CssProperty; + container?: boolean; + direction?: CssProperty; + offset?: CssProperty; + rowSpacing?: CssProperty; + size?: CssProperty; + spacing?: CssProperty; }; declare const Grid: PolymorphicComponent; diff --git a/packages/pigment-css-react/src/Grid.jsx b/packages/pigment-css-react/src/Grid.jsx index c8ed9cd5..200b192d 100644 --- a/packages/pigment-css-react/src/Grid.jsx +++ b/packages/pigment-css-react/src/Grid.jsx @@ -7,79 +7,104 @@ import * as React from 'react'; import { gridAtomics } from './baseAtomics'; import styled from './styled'; +function isGridComponent(element) { + // For server components `muiName` is avaialble in element.type._payload.value.muiName + // relevant info - https://github.com/facebook/react/blob/2807d781a08db8e9873687fccc25c0f12b4fb3d4/packages/react/src/ReactLazy.js#L45 + // eslint-disable-next-line no-underscore-dangle + return element.type.muiName === 'Grid' || element.type?._payload?.value?.muiName === 'Grid'; +} + const GridComponent = styled('div')({ - width: 'var(--Column-width)', - maxWidth: 'var(--Column-max-width)', - flex: 'var(--Column-flex)', - marginLeft: 'var(--Item-margin-left)', variants: [ { props: { container: true }, style: { display: 'flex', - gap: 'var(--Row-gap) var(--Column-gap)', + flexWrap: 'wrap', + gap: 'var(--Grid-self-row-spacing) var(--Grid-self-column-spacing)', } }, + { + props: ({ size }) => size !== undefined, + style: { + width: 'var(--Grid-self-width)', + maxWidth: 'var(--Grid-self-max-width)', + flex: 'var(--Grid-self-flex)', + } + }, + { + props: ({ offset }) => offset !== undefined, + style: { + marginLeft: 'var(--Grid-self-margin-left)', + } + } ] }) const Grid = React.forwardRef(function Grid( { children, - spacing = 0, + columns, + spacing, columnSpacing, rowSpacing, + direction = 'row', style, className, component = 'div', - direction = 'row', - flexWrap = 'wrap', - columns = 12, container = false, size, offset, - alignItems, - justifyContent, + // internal props + // eslint-disable-next-line react/prop-types + unstable_parent_columns, + // eslint-disable-next-line react/prop-types + unstable_parent_column_spacing, + // eslint-disable-next-line react/prop-types + unstable_parent_row_spacing, ...rest }, ref, ) { + + const selfColumns = columns ?? unstable_parent_columns ?? 12; + const selfColumnSpacing = columnSpacing ?? spacing ?? unstable_parent_column_spacing ?? 0; + const selfRowSpacing = rowSpacing ?? spacing ?? unstable_parent_row_spacing ?? 0; + const GridAtomicsObj = { direction, - flexWrap, }; - if (alignItems) { - GridAtomicsObj.alignItems = alignItems; + + if (unstable_parent_columns !== undefined) { + GridAtomicsObj['--Grid-parent-column-count'] = unstable_parent_columns; } - if (justifyContent) { - GridAtomicsObj.justifyContent = justifyContent; + + if (unstable_parent_column_spacing !== undefined) { + GridAtomicsObj['--Grid-parent-column-spacing'] = unstable_parent_column_spacing; } - if (container) { - GridAtomicsObj['--Column-count'] = columns; - GridAtomicsObj['--Column-gap'] = spacing; - GridAtomicsObj['--Row-gap'] = spacing; - if (columnSpacing) { - GridAtomicsObj['--Column-gap'] = columnSpacing; - } + if (unstable_parent_row_spacing !== undefined) { + GridAtomicsObj['--Grid-parent-row-spacing'] = unstable_parent_row_spacing; + } - if (rowSpacing) { - GridAtomicsObj['--Row-gap'] = rowSpacing; - } + if (container) { + GridAtomicsObj['--Grid-self-column-spacing'] = selfColumnSpacing; + GridAtomicsObj['--Grid-self-row-spacing'] = selfRowSpacing; } + if (size) { - GridAtomicsObj['--Column-span'] = size; - GridAtomicsObj['--Column-width'] = size; - GridAtomicsObj['--Column-max-width'] = size; - GridAtomicsObj['--Column-flex'] = size; + GridAtomicsObj['--Grid-self-column-span'] = size; + GridAtomicsObj['--Grid-self-width'] = size; + GridAtomicsObj['--Grid-self-max-width'] = size; + GridAtomicsObj['--Grid-self-flex'] = size; } if (offset) { - GridAtomicsObj['--Item-offset'] = offset; - GridAtomicsObj['--Item-margin-left'] = offset; + GridAtomicsObj['--Grid-self-offset'] = offset; + GridAtomicsObj['--Grid-self-margin-left'] = offset; } - const ownerState = { container }; + const ownerState = { container, size, offset }; const GridClasses = gridAtomics(GridAtomicsObj); return ( @@ -91,49 +116,28 @@ const Grid = React.forwardRef(function Grid( {...rest} ownerState={ownerState} > - {children} + {React.Children.map(children, (child) => { + if (React.isValidElement(child) && isGridComponent(child)) { + return React.cloneElement(child, { + unstable_parent_columns: selfColumns, + unstable_parent_column_spacing: selfColumnSpacing, + unstable_parent_row_spacing: selfRowSpacing, + }); + } + return child; + })} ); }); +Grid.muiName = 'Grid'; + process.env.NODE_ENV !== 'production' - ? (Grid.propTypes /* remove-proptypes */ = { + && (Grid.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ - /** - * @ignore - */ - alignItems: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf([ - 'center', - 'end', - 'flex-end', - 'flex-start', - 'self-end', - 'self-start', - 'start', - 'baseline', - 'normal', - 'stretch', - ]), - PropTypes.arrayOf( - PropTypes.oneOf([ - 'center', - 'end', - 'flex-end', - 'flex-start', - 'self-end', - 'self-start', - 'start', - 'baseline', - 'normal', - 'stretch', - ]), - ), - PropTypes.object, - ]), /** * The content of the component. */ @@ -142,11 +146,32 @@ process.env.NODE_ENV !== 'production' * @ignore */ className: PropTypes.string, + /** + * @ignore + */ + columns: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.arrayOf(PropTypes.number), + PropTypes.number, + PropTypes.object, + ]), + /** + * @ignore + */ + columnSpacing: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired), + PropTypes.number, + PropTypes.object, + PropTypes.string, + ]), /** * The component used for the root node. * Either a string to use a HTML element or a component. */ component: PropTypes.elementType, + /** + * @ignore + */ + container: PropTypes.bool, /** * @ignore */ @@ -158,41 +183,26 @@ process.env.NODE_ENV !== 'production' /** * @ignore */ - display: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['flex', 'inline-flex']), - PropTypes.arrayOf(PropTypes.oneOf(['flex', 'inline-flex']).isRequired), + offset: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.arrayOf(PropTypes.number), + PropTypes.number, PropTypes.object, ]), /** * @ignore */ - divider: PropTypes.node, + rowSpacing: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired), + PropTypes.number, + PropTypes.object, + PropTypes.string, + ]), /** * @ignore */ - justifyContent: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf([ - 'end', - 'start', - 'flex-end', - 'flex-start', - 'center', - 'space-between', - 'space-around', - 'space-evenly', - ]), - PropTypes.arrayOf( - PropTypes.oneOf([ - 'end', - 'start', - 'flex-end', - 'flex-start', - 'center', - 'space-between', - 'space-around', - 'space-evenly', - ]), - ), + size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.arrayOf(PropTypes.number), + PropTypes.number, PropTypes.object, ]), /** @@ -204,8 +214,12 @@ process.env.NODE_ENV !== 'production' PropTypes.object, PropTypes.string, ]), + /** + * @ignore + */ + style: PropTypes.object, + }) - : void 0; Grid.displayName = 'Grid'; diff --git a/packages/pigment-css-react/src/baseAtomics.js b/packages/pigment-css-react/src/baseAtomics.js index 7a05e0ce..9a2bd815 100644 --- a/packages/pigment-css-react/src/baseAtomics.js +++ b/packages/pigment-css-react/src/baseAtomics.js @@ -53,51 +53,31 @@ export const gridAtomics = generateAtomics(({ theme }) => ({ }, {}), defaultCondition: theme.breakpoints?.keys?.[0] ?? 'xs', properties: { - display: ['flex', 'inline-flex'], flexDirection: ['column', 'column-reverse', 'row', 'row-reverse'], - flexWrap: ['wrap', 'nowrap', 'wrap-reverse'], - justifyContent: [ - 'end', - 'start', - 'flex-end', - 'flex-start', - 'center', - 'space-between', - 'space-around', - 'space-evenly', - ], - alignItems: [ - 'center', - 'end', - 'flex-end', - 'flex-start', - 'self-end', - 'self-start', - 'start', - 'baseline', - 'normal', - 'stretch', - ], - '--Column-width': ['--Column-width'], - '--Column-max-width': ['--Column-max-width'], - '--Column-flex': ['--Column-flex'], - '--Column-count': ['--Column-count'], - '--Column-span': ['--Column-span'], - '--Column-gap': ['--Column-gap'], - '--Row-gap': ['--Row-gap'], - '--Item-offset': ['--Item-offset'], - '--Item-margin-left': ['--Item-margin-left'], + '--Grid-parent-column-count': ['--Grid-parent-column-count'], + '--Grid-parent-column-spacing': ['--Grid-parent-column-spacing'], + '--Grid-parent-row-spacing': ['--Grid-parent-row-spacing'], + '--Grid-self-column-span': ['--Grid-self-column-span'], + '--Grid-self-width': ['--Grid-self-width'], + '--Grid-self-max-width': ['--Grid-self-max-width'], + '--Grid-self-flex': ['--Grid-self-flex'], + '--Grid-self-column-spacing': ['--Grid-self-column-spacing'], + '--Grid-self-row-spacing': ['--Grid-self-row-spacing'], + '--Grid-self-offset': ['--Grid-self-offset'], + '--Grid-self-margin-left': ['--Grid-self-margin-left'], }, shorthands: { direction: ['flexDirection'], }, - unitless: ['--Column-count', '--Column-span', '--Item-offset'], + unitless: ['--Grid-parent-column-count', '--Grid-self-column-span', '--Grid-self-offset'], multipliers: { - '--Column-gap': 8, - '--Row-gap': 8, + '--Grid-parent-column-spacing': 8, + '--Grid-parent-row-spacing': 8, + '--Grid-self-column-spacing': 8, + '--Grid-self-row-spacing': 8, }, inlineGetters: { - '--Column-width': (value) => { + '--Grid-self-width': (value) => { if (value === 'grow') { return 'unset'; } @@ -106,9 +86,9 @@ export const gridAtomics = generateAtomics(({ theme }) => ({ return 'auto'; } - return 'calc(100% * var(--Column-span) / var(--Column-count) - (var(--Column-count) - var(--Column-span)) * var(--Column-gap) / var(--Column-count))'; + return 'calc(100% * var(--Grid-self-column-span) / var(--Grid-parent-column-count) - (var(--Grid-parent-column-count) - var(--Grid-self-column-span)) * var(--Grid-parent-column-spacing) / var(--Grid-parent-column-count))'; }, - '--Column-max-width': (value) => { + '--Grid-self-max-width': (value) => { if (value === 'grow') { return '100%'; } @@ -119,7 +99,7 @@ export const gridAtomics = generateAtomics(({ theme }) => ({ return 'unset'; }, - '--Column-flex': (value) => { + '--Grid-self-flex': (value) => { if (value === 'grow') { return '1 1 0'; } @@ -130,12 +110,12 @@ export const gridAtomics = generateAtomics(({ theme }) => ({ return '0 1 auto'; }, - '--Item-margin-left': (value) => { + '--Grid-self-margin-left': (value) => { if (value === 'auto') { return 'auto'; } - return 'calc(100% * var(--Item-offset) / var(--Column-count) + var(--Column-gap) * var(--Item-offset) / var(--Column-count))'; + return 'calc(100% * var(--Grid-self-offset) / var(--Grid-parent-column-count) + var(--Grid-parent-column-spacing) * var(--Grid-self-offset) / var(--Grid-parent-column-count))'; }, }, })); diff --git a/packages/pigment-css-react/src/generateAtomics.js b/packages/pigment-css-react/src/generateAtomics.js index 765fd71b..6c59fd29 100644 --- a/packages/pigment-css-react/src/generateAtomics.js +++ b/packages/pigment-css-react/src/generateAtomics.js @@ -89,12 +89,7 @@ export function atomics({ ); return; } - handlePrimitive( - propertyClasses[condition], - multipliers[cssProperty], - inlineGetters[cssProperty], - condition, - ); + classes.push(propertyClasses[condition]); }); } }