From a86ce70e5138c5a8524d93780e410b7af20657c3 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Fri, 7 Jun 2024 16:16:31 -0400 Subject: [PATCH 1/7] Grid init --- apps/pigment-css-vite-app/src/pages/grid.tsx | 50 +++++ packages/pigment-css-react/package.json | 9 + packages/pigment-css-react/src/Grid.d.ts | 20 ++ packages/pigment-css-react/src/Grid.jsx | 198 ++++++++++++++++++ packages/pigment-css-react/src/baseAtomics.js | 95 +++++++++ .../pigment-css-react/src/generateAtomics.js | 20 +- .../src/utils/convertAtomicsToCss.ts | 12 +- .../tests/generateAtomics.test.js | 5 +- packages/pigment-css-react/tsup.config.ts | 2 +- 9 files changed, 399 insertions(+), 12 deletions(-) create mode 100644 apps/pigment-css-vite-app/src/pages/grid.tsx create mode 100644 packages/pigment-css-react/src/Grid.d.ts create mode 100644 packages/pigment-css-react/src/Grid.jsx create mode 100644 packages/pigment-css-react/src/baseAtomics.js diff --git a/apps/pigment-css-vite-app/src/pages/grid.tsx b/apps/pigment-css-vite-app/src/pages/grid.tsx new file mode 100644 index 00000000..caa557fa --- /dev/null +++ b/apps/pigment-css-vite-app/src/pages/grid.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import Grid from '@pigment-css/react/Grid'; +import { styled } from '@pigment-css/react'; + +const Card = styled.div` + transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + border-radius: 4px; + box-shadow: + rgba(0, 0, 0, 0.2) 0px 2px 1px -1px, + rgba(0, 0, 0, 0.14) 0px 1px 1px 0px, + rgba(0, 0, 0, 0.12) 0px 1px 3px 0px; + background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.05)); + padding: 8px 16px; + font-family: Roboto, Helvetica, Arial, sans-serif; + font-weight: 400; + font-size: 0.875rem; + line-height: 1.43; + letter-spacing: 0.01071em; + background-color: rgb(26, 32, 39); + width: 100%; + text-wrap: nowrap; + color: white; +`; + +const items = [ + { id: '1', size: 1, offset: 0}, + { id: '2', size: 1, offset: 0}, + { id: '3', size: 1, offset: 0}, + { id: '4', size: 1, offset: 0}, + { id: '5', size: 1, offset: 0}, + { id: '6', size: 1, offset: 0}, +] + +export default function InteractiveGrid() { + + return ( + + {items.map(({ id, size, offset}) => ( + Item {id} + ))} + + ); +} diff --git a/packages/pigment-css-react/package.json b/packages/pigment-css-react/package.json index c0c2d053..7619f5d3 100644 --- a/packages/pigment-css-react/package.json +++ b/packages/pigment-css-react/package.json @@ -161,6 +161,15 @@ }, "require": "./build/Stack.js", "default": "./build/Stack.js" + }, + "./Grid": { + "types": "./build/Grid.d.ts", + "import": { + "types": "./build/Grid.d.mts", + "default": "./build/Grid.mjs" + }, + "require": "./build/Grid.js", + "default": "./build/Grid.js" } }, "nx": { diff --git a/packages/pigment-css-react/src/Grid.d.ts b/packages/pigment-css-react/src/Grid.d.ts new file mode 100644 index 00000000..ee6e31d5 --- /dev/null +++ b/packages/pigment-css-react/src/Grid.d.ts @@ -0,0 +1,20 @@ +import * as CSS from 'csstype'; + +import { Breakpoint } from './base'; +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; +}; + +declare const Grid: PolymorphicComponent; + +export default Grid; diff --git a/packages/pigment-css-react/src/Grid.jsx b/packages/pigment-css-react/src/Grid.jsx new file mode 100644 index 00000000..9e60dab9 --- /dev/null +++ b/packages/pigment-css-react/src/Grid.jsx @@ -0,0 +1,198 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ +/* eslint-disable react/jsx-filename-extension */ +import clsx from 'clsx'; +import PropTypes from 'prop-types'; +import * as React from 'react'; + +import { gridAtomics } from './baseAtomics'; + + +const itemStyle = { + width: 'calc(100% * var(--Column-span) / var(--Column-count) - (var(--Column-count) - var(--Column-span)) * var(--Column-gap) / var(--Column-count))', + marginLeft: 'calc(100% * var(--Item-offset) / var(--Column-count) + var(--Column-gap) * var(--Item-offset) / var(--Column-count))' +} + +const containerStyle = { + gap: 'var(--Row-gap) var(--Column-gap)', +} + +const Grid = React.forwardRef(function Grid( + { + children, + spacing = 0, + columnSpacing, + rowSpacing, + style, + className, + display = 'flex', + component = 'div', + direction = 'column', + flexWrap = 'wrap', + columns = 12, + container = false, + size, + offset, + alignItems, + justifyContent, + ...rest + }, + ref, +) { + const GridAtomicsObj = { + display, + direction, + flexWrap, + }; + if (alignItems) { + GridAtomicsObj.alignItems = alignItems; + } + if (justifyContent) { + GridAtomicsObj.justifyContent = justifyContent; + } + if (container) { + GridAtomicsObj['--Column-count'] = columns; + GridAtomicsObj['--Column-gap'] = spacing; + GridAtomicsObj['--Row-gap'] = spacing; + + if (columnSpacing) { + GridAtomicsObj['--Column-gap'] = columnSpacing; + } + + if (rowSpacing) { + GridAtomicsObj['--Row-gap'] = rowSpacing; + } + } + if (size) { + GridAtomicsObj['--Column-span'] = size; + } + if (offset) { + GridAtomicsObj['--Item-offset'] = offset; + } + const GridClasses = gridAtomics(GridAtomicsObj); + const Component = component; + return ( + + {children} + + ); +}); + +process.env.NODE_ENV !== 'production' + ? (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. + */ + children: PropTypes.node, + /** + * @ignore + */ + className: PropTypes.string, + /** + * The component used for the root node. + * Either a string to use a HTML element or a component. + */ + component: PropTypes.elementType, + /** + * @ignore + */ + direction: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.oneOf(['column', 'column-reverse', 'row', 'row-reverse']), + PropTypes.arrayOf(PropTypes.oneOf(['column', 'column-reverse', 'row', 'row-reverse'])), + PropTypes.object, + ]), + /** + * @ignore + */ + display: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.oneOf(['flex', 'inline-flex']), + PropTypes.arrayOf(PropTypes.oneOf(['flex', 'inline-flex']).isRequired), + PropTypes.object, + ]), + /** + * @ignore + */ + divider: PropTypes.node, + /** + * @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', + ]), + ), + PropTypes.object, + ]), + /** + * @ignore + */ + spacing: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired), + PropTypes.number, + PropTypes.object, + PropTypes.string, + ]), + }) + : void 0; + +Grid.displayName = 'Grid'; + +export default Grid; diff --git a/packages/pigment-css-react/src/baseAtomics.js b/packages/pigment-css-react/src/baseAtomics.js new file mode 100644 index 00000000..4d98e9ad --- /dev/null +++ b/packages/pigment-css-react/src/baseAtomics.js @@ -0,0 +1,95 @@ +import { generateAtomics } from './generateAtomics'; + +export const stackAtomics = generateAtomics(({ theme }) => ({ + conditions: Object.keys(theme.breakpoints.values).reduce((acc, breakpoint) => { + acc[breakpoint] = `@media (min-width: ${theme.breakpoints.values[breakpoint]}${ + theme.breakpoints.unit ?? 'px' + })`; + return acc; + }, {}), + defaultCondition: theme.breakpoints?.keys?.[0] ?? 'xs', + properties: { + display: ['flex', 'inline-flex'], + flexDirection: ['column', 'column-reverse', 'row', 'row-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', + ], + gap: ['--Stack-gap'], + }, + shorthands: { + direction: ['flexDirection'], + spacing: ['gap'], + }, + multipliers: { + gap: 8, + }, +})); + +export const gridAtomics = generateAtomics(({ theme }) => ({ + conditions: Object.keys(theme.breakpoints.values).reduce((acc, breakpoint) => { + acc[breakpoint] = `@media (min-width: ${theme.breakpoints.values[breakpoint]}${ + theme.breakpoints.unit ?? 'px' + })`; + return acc; + }, {}), + 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-count': ['--Column-count'], + '--Column-span': ['--Column-span'], + '--Column-gap': ['--Column-gap'], + '--Row-gap': ['--Row-gap'], + '--Item-offset': ['--Item-offset'] + }, + shorthands: { + direction: ['flexDirection'], + }, + unitless: ['--Column-count', '--Column-span', '--Item-offset'], + multipliers: { + '--Column-gap': 8, + '--Row-gap': 8, + }, +})); diff --git a/packages/pigment-css-react/src/generateAtomics.js b/packages/pigment-css-react/src/generateAtomics.js index 7b0c2fdc..0ba18255 100644 --- a/packages/pigment-css-react/src/generateAtomics.js +++ b/packages/pigment-css-react/src/generateAtomics.js @@ -12,7 +12,8 @@ export function generateAtomics() { * @property {Object.} shorthands * @property {string[]} conditions * @property {string} defaultCondition - * @property {string} multiplier + * @property {string[]} unitless + * @property {string} multipliers */ /** @@ -21,14 +22,21 @@ export function generateAtomics() { * * @param {RuntimeConfig} runtimeConfig */ -export function atomics({ styles, shorthands, conditions, defaultCondition, multiplier }) { +export function atomics({ + styles, + shorthands, + conditions, + defaultCondition, + unitless, + multipliers = {}, +}) { function addStyles(cssProperty, propertyValue, classes, inlineStyle) { const styleClasses = styles[cssProperty]; if (!styleClasses) { return; } - function handlePrimitive(value, breakpoint = defaultCondition) { + function handlePrimitive(value, multiplier = 1, breakpoint = defaultCondition) { if (!(value in styleClasses)) { const keys = Object.keys(styleClasses); if (keys.length !== 1) { @@ -48,7 +56,7 @@ export function atomics({ styles, shorthands, conditions, defaultCondition, mult } if (typeof propertyValue === 'string' || typeof propertyValue === 'number') { - handlePrimitive(propertyValue); + handlePrimitive(propertyValue, multipliers[cssProperty]); } else if (Array.isArray(propertyValue)) { propertyValue.forEach((value, index) => { if (value) { @@ -56,7 +64,7 @@ export function atomics({ styles, shorthands, conditions, defaultCondition, mult if (!breakpoint) { return; } - handlePrimitive(value, conditions[index]); + handlePrimitive(value, multipliers[cssProperty], conditions[index]); } }); } else if (propertyValue) { @@ -64,7 +72,7 @@ export function atomics({ styles, shorthands, conditions, defaultCondition, mult if (propertyValue[condition]) { const propertyClasses = styleClasses[propertyValue[condition]]; if (!propertyClasses) { - handlePrimitive(propertyValue[condition], condition); + handlePrimitive(propertyValue[condition], multipliers[cssProperty], condition); return; } classes.push(propertyClasses[condition]); diff --git a/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts b/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts index 4408e0bb..137051ba 100644 --- a/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts +++ b/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts @@ -7,7 +7,8 @@ export type Atomics = { [key: string]: string[]; }; shorthands: Record; - multiplier?: string; + unitless: string[]; + multipliers?: Record; }; export type RuntimeConfig = { @@ -15,7 +16,8 @@ export type RuntimeConfig = { styles: Record>>; shorthands: Atomics['shorthands']; defaultCondition: string; - multiplier?: string; + unitless: string[]; + multipliers?: Record; }; function getClassName(...items: string[]) { @@ -28,7 +30,8 @@ export function convertAtomicsToCss( defaultCondition, properties, shorthands = {}, - multiplier = undefined, + unitless = [], + multipliers = {}, }: Atomics, mainClassName: string, isGlobal = false, @@ -40,7 +43,8 @@ export function convertAtomicsToCss( shorthands, conditions: Object.keys(conditions), defaultCondition, - multiplier, + unitless, + multipliers, }; let count = 1; function getCount() { diff --git a/packages/pigment-css-react/tests/generateAtomics.test.js b/packages/pigment-css-react/tests/generateAtomics.test.js index 940f108f..e602801c 100644 --- a/packages/pigment-css-react/tests/generateAtomics.test.js +++ b/packages/pigment-css-react/tests/generateAtomics.test.js @@ -47,7 +47,10 @@ const atomic = atomics({ // @ts-ignore This is not expected while calling the pre-transpiled generateAtomics conditions: ['xs', 'sm', 'md', 'lg', 'xl'], defaultCondition: 'xs', - multiplier: '8px', + unitless: [], + multipliers: { + gap: '8px', + }, }); describe('generateAtomics', () => { diff --git a/packages/pigment-css-react/tsup.config.ts b/packages/pigment-css-react/tsup.config.ts index 57dfbe01..e2b94548 100644 --- a/packages/pigment-css-react/tsup.config.ts +++ b/packages/pigment-css-react/tsup.config.ts @@ -20,7 +20,7 @@ const baseConfig: Options = { external, }; -const BASE_FILES = ['index.ts', 'theme.ts', 'Box.jsx', 'RtlProvider.tsx', 'Stack.jsx']; +const BASE_FILES = ['index.ts', 'theme.ts', 'Box.jsx', 'RtlProvider.tsx', 'Stack.jsx', 'Grid.jsx']; export default defineConfig([ { From 000419eac6363c1e896a15eba85bad0a52b424ed Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Fri, 7 Jun 2024 17:43:49 -0400 Subject: [PATCH 2/7] Add row direction --- packages/pigment-css-react/src/Grid.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pigment-css-react/src/Grid.jsx b/packages/pigment-css-react/src/Grid.jsx index 9e60dab9..5cba90ca 100644 --- a/packages/pigment-css-react/src/Grid.jsx +++ b/packages/pigment-css-react/src/Grid.jsx @@ -26,7 +26,7 @@ const Grid = React.forwardRef(function Grid( className, display = 'flex', component = 'div', - direction = 'column', + direction = 'row', flexWrap = 'wrap', columns = 12, container = false, From d1fedaf72b48b57b07eaa5e5cc1ef6175fc20972 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Mon, 10 Jun 2024 13:39:24 -0400 Subject: [PATCH 3/7] Implement shared styles through styled --- packages/pigment-css-react/src/Grid.jsx | 39 ++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/pigment-css-react/src/Grid.jsx b/packages/pigment-css-react/src/Grid.jsx index 5cba90ca..0ad86d42 100644 --- a/packages/pigment-css-react/src/Grid.jsx +++ b/packages/pigment-css-react/src/Grid.jsx @@ -5,16 +5,25 @@ import PropTypes from 'prop-types'; import * as React from 'react'; import { gridAtomics } from './baseAtomics'; +import styled from './styled'; - -const itemStyle = { - width: 'calc(100% * var(--Column-span) / var(--Column-count) - (var(--Column-count) - var(--Column-span)) * var(--Column-gap) / var(--Column-count))', - marginLeft: 'calc(100% * var(--Item-offset) / var(--Column-count) + var(--Column-gap) * var(--Item-offset) / var(--Column-count))' -} - -const containerStyle = { - gap: 'var(--Row-gap) var(--Column-gap)', -} +const GridComponent = styled('div')({ + variants: [ + { + props: { container: true }, + style: { + gap: 'var(--Row-gap) var(--Column-gap)', + } + }, + { + props: { container: false }, + style: { + width: 'calc(100% * var(--Column-span) / var(--Column-count) - (var(--Column-count) - var(--Column-span)) * var(--Column-gap) / var(--Column-count))', + marginLeft: 'calc(100% * var(--Item-offset) / var(--Column-count) + var(--Column-gap) * var(--Item-offset) / var(--Column-count))' + } + } + ] +}) const Grid = React.forwardRef(function Grid( { @@ -68,17 +77,21 @@ const Grid = React.forwardRef(function Grid( if (offset) { GridAtomicsObj['--Item-offset'] = offset; } + + const ownerState = { container }; + const GridClasses = gridAtomics(GridAtomicsObj); - const Component = component; return ( - {children} - + ); }); From 1f7f02f6c5ea6aea47f1bd8b3e189825614cf874 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Tue, 11 Jun 2024 16:39:01 -0400 Subject: [PATCH 4/7] Support keywords in size and offset props --- apps/pigment-css-vite-app/src/pages/grid.tsx | 40 +++++----------- packages/pigment-css-react/src/Grid.jsx | 19 ++++---- packages/pigment-css-react/src/baseAtomics.js | 48 ++++++++++++++++++- .../pigment-css-react/src/generateAtomics.js | 30 ++++++++++-- .../src/utils/convertAtomicsToCss.ts | 4 ++ 5 files changed, 98 insertions(+), 43 deletions(-) diff --git a/apps/pigment-css-vite-app/src/pages/grid.tsx b/apps/pigment-css-vite-app/src/pages/grid.tsx index caa557fa..e0ec1d75 100644 --- a/apps/pigment-css-vite-app/src/pages/grid.tsx +++ b/apps/pigment-css-vite-app/src/pages/grid.tsx @@ -3,48 +3,32 @@ import Grid from '@pigment-css/react/Grid'; import { styled } from '@pigment-css/react'; const Card = styled.div` - transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + background-color: #fff; + border: 1px solid #ced7e0; + padding: 8px; border-radius: 4px; - box-shadow: - rgba(0, 0, 0, 0.2) 0px 2px 1px -1px, - rgba(0, 0, 0, 0.14) 0px 1px 1px 0px, - rgba(0, 0, 0, 0.12) 0px 1px 3px 0px; - background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.05)); - padding: 8px 16px; - font-family: Roboto, Helvetica, Arial, sans-serif; - font-weight: 400; - font-size: 0.875rem; - line-height: 1.43; - letter-spacing: 0.01071em; - background-color: rgb(26, 32, 39); - width: 100%; - text-wrap: nowrap; - color: white; + text-align: center; `; const items = [ - { id: '1', size: 1, offset: 0}, - { id: '2', size: 1, offset: 0}, - { id: '3', size: 1, offset: 0}, - { id: '4', size: 1, offset: 0}, - { id: '5', size: 1, offset: 0}, - { id: '6', size: 1, offset: 0}, + { id: '1', size: 2}, + { id: '2', size: 2}, ] export default function InteractiveGrid() { return ( +
- {items.map(({ id, size, offset}) => ( - Item {id} + {items.map(({ id, size, offset, extraText}) => ( + Item {id}{extraText} ))} +
); } diff --git a/packages/pigment-css-react/src/Grid.jsx b/packages/pigment-css-react/src/Grid.jsx index 0ad86d42..c8ed9cd5 100644 --- a/packages/pigment-css-react/src/Grid.jsx +++ b/packages/pigment-css-react/src/Grid.jsx @@ -8,20 +8,18 @@ import { gridAtomics } from './baseAtomics'; import styled from './styled'; 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)', } }, - { - props: { container: false }, - style: { - width: 'calc(100% * var(--Column-span) / var(--Column-count) - (var(--Column-count) - var(--Column-span)) * var(--Column-gap) / var(--Column-count))', - marginLeft: 'calc(100% * var(--Item-offset) / var(--Column-count) + var(--Column-gap) * var(--Item-offset) / var(--Column-count))' - } - } ] }) @@ -33,7 +31,6 @@ const Grid = React.forwardRef(function Grid( rowSpacing, style, className, - display = 'flex', component = 'div', direction = 'row', flexWrap = 'wrap', @@ -48,7 +45,6 @@ const Grid = React.forwardRef(function Grid( ref, ) { const GridAtomicsObj = { - display, direction, flexWrap, }; @@ -73,9 +69,14 @@ const Grid = React.forwardRef(function Grid( } if (size) { GridAtomicsObj['--Column-span'] = size; + GridAtomicsObj['--Column-width'] = size; + GridAtomicsObj['--Column-max-width'] = size; + GridAtomicsObj['--Column-flex'] = size; + } if (offset) { GridAtomicsObj['--Item-offset'] = offset; + GridAtomicsObj['--Item-margin-left'] = offset; } const ownerState = { container }; diff --git a/packages/pigment-css-react/src/baseAtomics.js b/packages/pigment-css-react/src/baseAtomics.js index 4d98e9ad..7a05e0ce 100644 --- a/packages/pigment-css-react/src/baseAtomics.js +++ b/packages/pigment-css-react/src/baseAtomics.js @@ -78,11 +78,15 @@ export const gridAtomics = generateAtomics(({ theme }) => ({ '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-offset': ['--Item-offset'], + '--Item-margin-left': ['--Item-margin-left'], }, shorthands: { direction: ['flexDirection'], @@ -92,4 +96,46 @@ export const gridAtomics = generateAtomics(({ theme }) => ({ '--Column-gap': 8, '--Row-gap': 8, }, + inlineGetters: { + '--Column-width': (value) => { + if (value === 'grow') { + return 'unset'; + } + + if (value === 'auto') { + return 'auto'; + } + + return 'calc(100% * var(--Column-span) / var(--Column-count) - (var(--Column-count) - var(--Column-span)) * var(--Column-gap) / var(--Column-count))'; + }, + '--Column-max-width': (value) => { + if (value === 'grow') { + return '100%'; + } + + if (value === 'auto') { + return 'none'; + } + + return 'unset'; + }, + '--Column-flex': (value) => { + if (value === 'grow') { + return '1 1 0'; + } + + if (value === 'auto') { + return '0 0 auto'; + } + + return '0 1 auto'; + }, + '--Item-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))'; + }, + }, })); diff --git a/packages/pigment-css-react/src/generateAtomics.js b/packages/pigment-css-react/src/generateAtomics.js index 0ba18255..41284c45 100644 --- a/packages/pigment-css-react/src/generateAtomics.js +++ b/packages/pigment-css-react/src/generateAtomics.js @@ -6,6 +6,10 @@ export function generateAtomics() { ); } +function defaultInlineGetter(styleValue, cssProperty, unitless) { + return unitless.includes(cssProperty) ? styleValue : `${styleValue}px`; +} + /** * @typedef {Object} RuntimeConfig * @property {Object.>>} styles @@ -29,6 +33,7 @@ export function atomics({ defaultCondition, unitless, multipliers = {}, + inlineGetters = {}, }) { function addStyles(cssProperty, propertyValue, classes, inlineStyle) { const styleClasses = styles[cssProperty]; @@ -36,7 +41,12 @@ export function atomics({ return; } - function handlePrimitive(value, multiplier = 1, breakpoint = defaultCondition) { + function handlePrimitive( + value, + multiplier = 1, + inlineGetter = defaultInlineGetter, + breakpoint = defaultCondition, + ) { if (!(value in styleClasses)) { const keys = Object.keys(styleClasses); if (keys.length !== 1) { @@ -49,14 +59,14 @@ export function atomics({ } classes.push(styleClasses[key][breakpoint]); inlineStyle[`${key}${breakpoint === defaultCondition ? '' : `-${breakpoint}`}`] = - styleValue; + inlineGetter(styleValue, cssProperty, unitless); } else { classes.push(styleClasses[value][breakpoint]); } } if (typeof propertyValue === 'string' || typeof propertyValue === 'number') { - handlePrimitive(propertyValue, multipliers[cssProperty]); + handlePrimitive(propertyValue, multipliers[cssProperty], inlineGetters[cssProperty]); } else if (Array.isArray(propertyValue)) { propertyValue.forEach((value, index) => { if (value) { @@ -64,7 +74,12 @@ export function atomics({ if (!breakpoint) { return; } - handlePrimitive(value, multipliers[cssProperty], conditions[index]); + handlePrimitive( + value, + multipliers[cssProperty], + inlineGetters[cssProperty], + conditions[index], + ); } }); } else if (propertyValue) { @@ -72,7 +87,12 @@ export function atomics({ if (propertyValue[condition]) { const propertyClasses = styleClasses[propertyValue[condition]]; if (!propertyClasses) { - handlePrimitive(propertyValue[condition], multipliers[cssProperty], condition); + handlePrimitive( + propertyValue[condition], + multipliers[cssProperty], + inlineGetters[cssProperty], + condition, + ); return; } classes.push(propertyClasses[condition]); diff --git a/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts b/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts index 137051ba..3fb1e8fc 100644 --- a/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts +++ b/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts @@ -9,6 +9,7 @@ export type Atomics = { shorthands: Record; unitless: string[]; multipliers?: Record; + inlineGetters: Record string>; }; export type RuntimeConfig = { @@ -18,6 +19,7 @@ export type RuntimeConfig = { defaultCondition: string; unitless: string[]; multipliers?: Record; + inlineGetters: Record string>; }; function getClassName(...items: string[]) { @@ -32,6 +34,7 @@ export function convertAtomicsToCss( shorthands = {}, unitless = [], multipliers = {}, + inlineGetters = {}, }: Atomics, mainClassName: string, isGlobal = false, @@ -45,6 +48,7 @@ export function convertAtomicsToCss( defaultCondition, unitless, multipliers, + inlineGetters, }; let count = 1; function getCount() { From a95bc8fe9e67f8f1c2fbc1d3445ab5c5084f25ab Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Thu, 13 Jun 2024 16:56:01 -0400 Subject: [PATCH 5/7] Support nested grids and refactor code --- apps/pigment-css-vite-app/src/pages/grid.tsx | 301 +++++++++++++++++- packages/pigment-css-react/src/Grid.d.ts | 14 +- packages/pigment-css-react/src/Grid.jsx | 204 ++++++------ packages/pigment-css-react/src/baseAtomics.js | 64 ++-- 4 files changed, 424 insertions(+), 159 deletions(-) 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))'; }, }, })); From 9577e9328101769ddc4ae47706691552082580c3 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Mon, 17 Jun 2024 11:25:36 -0400 Subject: [PATCH 6/7] Use RSC in grid demos --- .../src/app/grid/demo3.tsx | 43 +++ .../src/app/grid/page.tsx | 272 ++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 apps/pigment-css-next-app/src/app/grid/demo3.tsx create mode 100644 apps/pigment-css-next-app/src/app/grid/page.tsx diff --git a/apps/pigment-css-next-app/src/app/grid/demo3.tsx b/apps/pigment-css-next-app/src/app/grid/demo3.tsx new file mode 100644 index 00000000..b3419d9c --- /dev/null +++ b/apps/pigment-css-next-app/src/app/grid/demo3.tsx @@ -0,0 +1,43 @@ +'use client'; +import * as React from 'react'; +import Grid from '@pigment-css/react/Grid'; +import { styled } from '@pigment-css/react'; + +const Item = styled.div` + background-color: #fff; + border: 1px solid #ced7e0; + padding: 8px; + border-radius: 4px; + text-align: center; +`; + +export default function GridDemo3() { + const [spacing, setSpacing] = React.useState(2); + return ( +
    + + {[0, 1, 2].map((value) => ( + + + + ))} + + + Spacing: + {[0, 0.5, 1, 2, 3, 4, 8, 12].map((value) => ( + + + + ))} + +
    + ) +} diff --git a/apps/pigment-css-next-app/src/app/grid/page.tsx b/apps/pigment-css-next-app/src/app/grid/page.tsx new file mode 100644 index 00000000..4ac3c87a --- /dev/null +++ b/apps/pigment-css-next-app/src/app/grid/page.tsx @@ -0,0 +1,272 @@ +import Box from '@pigment-css/react/Box'; +import Grid from '@pigment-css/react/Grid'; +import { styled } from '@pigment-css/react'; +import GridDemo3 from './demo3'; + +const Item = styled.div` + background-color: #fff; + border: 1px solid #ced7e0; + padding: 8px; + border-radius: 4px; + text-align: center; +`; + +function GridDemo1() { + return ( + + + size=8 + + + size=4 + + + size=4 + + + size=8 + + + ) +} + +function GridDemo2() { + return ( + + + xs=6 md=8 + + + xs=6 md=4 + + + xs=6 md=4 + + + xs=6 md=8 + + + ) +} + +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}

    + +
    + ); + })} +
    + ); +} From 9e07c7db33c5e16432bfbf3609543e5ea8580f67 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Mon, 17 Jun 2024 14:21:23 -0400 Subject: [PATCH 7/7] Adjust to atomics changes --- packages/pigment-css-react/src/Stack.jsx | 4 +++- packages/pigment-css-react/src/baseAtomics.js | 8 +++---- .../pigment-css-react/src/generateAtomics.js | 23 +++++++++---------- .../src/utils/convertAtomicsToCss.ts | 4 +--- .../tests/generateAtomics.test.js | 8 +++---- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/pigment-css-react/src/Stack.jsx b/packages/pigment-css-react/src/Stack.jsx index ea1db349..58c57166 100644 --- a/packages/pigment-css-react/src/Stack.jsx +++ b/packages/pigment-css-react/src/Stack.jsx @@ -23,7 +23,9 @@ const stackAtomics = generateAtomics(({ theme }) => { direction: ['flexDirection'], spacing: ['gap'], }, - multiplier: Array.isArray(theme.vars?.spacing) ? theme.vars.spacing[0] : theme.vars?.spacing, + multipliers: { + gap: Array.isArray(theme.vars?.spacing) ? theme.vars.spacing[0] : theme.vars?.spacing + }, }; }); diff --git a/packages/pigment-css-react/src/baseAtomics.js b/packages/pigment-css-react/src/baseAtomics.js index 9a2bd815..03995b50 100644 --- a/packages/pigment-css-react/src/baseAtomics.js +++ b/packages/pigment-css-react/src/baseAtomics.js @@ -71,10 +71,10 @@ export const gridAtomics = generateAtomics(({ theme }) => ({ }, unitless: ['--Grid-parent-column-count', '--Grid-self-column-span', '--Grid-self-offset'], multipliers: { - '--Grid-parent-column-spacing': 8, - '--Grid-parent-row-spacing': 8, - '--Grid-self-column-spacing': 8, - '--Grid-self-row-spacing': 8, + '--Grid-parent-column-spacing': Array.isArray(theme.vars?.spacing) ? theme.vars.spacing[0] : theme.vars?.spacing, + '--Grid-parent-row-spacing': Array.isArray(theme.vars?.spacing) ? theme.vars.spacing[0] : theme.vars?.spacing, + '--Grid-self-column-spacing': Array.isArray(theme.vars?.spacing) ? theme.vars.spacing[0] : theme.vars?.spacing, + '--Grid-self-row-spacing': Array.isArray(theme.vars?.spacing) ? theme.vars.spacing[0] : theme.vars?.spacing, }, inlineGetters: { '--Grid-self-width': (value) => { diff --git a/packages/pigment-css-react/src/generateAtomics.js b/packages/pigment-css-react/src/generateAtomics.js index 41284c45..f9433768 100644 --- a/packages/pigment-css-react/src/generateAtomics.js +++ b/packages/pigment-css-react/src/generateAtomics.js @@ -6,10 +6,6 @@ export function generateAtomics() { ); } -function defaultInlineGetter(styleValue, cssProperty, unitless) { - return unitless.includes(cssProperty) ? styleValue : `${styleValue}px`; -} - /** * @typedef {Object} RuntimeConfig * @property {Object.>>} styles @@ -31,7 +27,7 @@ export function atomics({ shorthands, conditions, defaultCondition, - unitless, + unitless = [], multipliers = {}, inlineGetters = {}, }) { @@ -43,8 +39,8 @@ export function atomics({ function handlePrimitive( value, - multiplier = 1, - inlineGetter = defaultInlineGetter, + multiplier = undefined, + inlineGetter = undefined, breakpoint = defaultCondition, ) { if (!(value in styleClasses)) { @@ -55,11 +51,14 @@ export function atomics({ const key = keys[0]; let styleValue = value; if (typeof value === 'number') { - styleValue = multiplier ? `calc(${value} * ${multiplier})` : `${value}px`; + if (multiplier) { + styleValue = `calc(${value} * ${multiplier})`; + } else if (!unitless.includes(cssProperty)) { + styleValue = `${value}px`; + } } classes.push(styleClasses[key][breakpoint]); - inlineStyle[`${key}${breakpoint === defaultCondition ? '' : `-${breakpoint}`}`] = - inlineGetter(styleValue, cssProperty, unitless); + inlineStyle[`${key}-${breakpoint}`] = inlineGetter ? inlineGetter(styleValue) : styleValue; } else { classes.push(styleClasses[value][breakpoint]); } @@ -69,7 +68,7 @@ export function atomics({ handlePrimitive(propertyValue, multipliers[cssProperty], inlineGetters[cssProperty]); } else if (Array.isArray(propertyValue)) { propertyValue.forEach((value, index) => { - if (value) { + if (value !== undefined && value !== null) { const breakpoint = conditions[index]; if (!breakpoint) { return; @@ -84,7 +83,7 @@ export function atomics({ }); } else if (propertyValue) { Object.keys(propertyValue).forEach((condition) => { - if (propertyValue[condition]) { + if (propertyValue[condition] !== undefined && propertyValue[condition] !== null) { const propertyClasses = styleClasses[propertyValue[condition]]; if (!propertyClasses) { handlePrimitive( diff --git a/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts b/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts index 3fb1e8fc..b398a5cd 100644 --- a/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts +++ b/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts @@ -66,9 +66,7 @@ export function convertAtomicsToCss( Object.entries(properties).forEach(([cssPropertyName, propertyValues]) => { propertyValues.forEach((propertyValue) => { const propValue = propertyValue.startsWith('--') - ? cssesc( - `var(${propertyValue}${conditionName === defaultCondition ? '' : `-${conditionName}`})`, - ) + ? cssesc(`var(${propertyValue}-${conditionName})`) : propertyValue; const className = isGlobal || debug diff --git a/packages/pigment-css-react/tests/generateAtomics.test.js b/packages/pigment-css-react/tests/generateAtomics.test.js index e602801c..7d752b35 100644 --- a/packages/pigment-css-react/tests/generateAtomics.test.js +++ b/packages/pigment-css-react/tests/generateAtomics.test.js @@ -65,7 +65,7 @@ describe('generateAtomics', () => { ).to.deep.equal({ className: 'gap--Stack-gap-lg gap--Stack-gap-xs', style: { - '--Stack-gap': 'calc(2 * 8px)', + '--Stack-gap-xs': 'calc(2 * 8px)', '--Stack-gap-lg': 'calc(1 * 8px)', }, }); @@ -80,7 +80,7 @@ describe('generateAtomics', () => { ).to.deep.equal({ className: 'flex-direction-row-xs gap--Stack-gap-xs', style: { - '--Stack-gap': 'calc(1 * 8px)', + '--Stack-gap-xs': 'calc(1 * 8px)', }, }); }); @@ -107,7 +107,7 @@ describe('generateAtomics', () => { className: 'flex-direction-row-xs flex-direction-column-sm gap--Stack-gap-xs gap--Stack-gap-sm', style: { - '--Stack-gap': 'calc(1 * 8px)', + '--Stack-gap-xs': 'calc(1 * 8px)', '--Stack-gap-sm': 'calc(2 * 8px)', }, }); @@ -135,7 +135,7 @@ describe('generateAtomics', () => { className: 'flex-direction-row-xs flex-direction-column-sm gap--Stack-gap-xs gap--Stack-gap-sm', style: { - '--Stack-gap': 'calc(1 * 8px)', + '--Stack-gap-xs': 'calc(1 * 8px)', '--Stack-gap-sm': 'calc(2 * 8px)', }, });