From 12c796488c5bc42e58b905a72e7b357e5d18d369 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Tue, 3 Dec 2024 21:05:03 +0000 Subject: [PATCH] Add first pass at Grid component Signed-off-by: Charles de Dreuille --- packages/canon/docs/Home.mdx | 37 +++--- .../src/components/Grid/Grid.stories.tsx | 91 ++++++++++++++ packages/canon/src/components/Grid/Grid.tsx | 70 +++++++++++ packages/canon/src/components/Grid/index.ts | 17 +++ .../src/components/Grid/sprinkles.css.ts | 113 ++++++++++++++++++ packages/canon/src/components/Grid/styles.css | 3 + packages/canon/src/components/Grid/types.ts | 48 ++++++++ packages/canon/src/index.ts | 3 + packages/canon/src/theme/styles.css | 1 + 9 files changed, 367 insertions(+), 16 deletions(-) create mode 100644 packages/canon/src/components/Grid/Grid.stories.tsx create mode 100644 packages/canon/src/components/Grid/Grid.tsx create mode 100644 packages/canon/src/components/Grid/index.ts create mode 100644 packages/canon/src/components/Grid/sprinkles.css.ts create mode 100644 packages/canon/src/components/Grid/styles.css create mode 100644 packages/canon/src/components/Grid/types.ts diff --git a/packages/canon/docs/Home.mdx b/packages/canon/docs/Home.mdx index 3a00de5d07..51574f57a1 100644 --- a/packages/canon/docs/Home.mdx +++ b/packages/canon/docs/Home.mdx @@ -43,33 +43,38 @@ import { list } from './components/Roadmap/list'; link="/?path=/docs/components-box--docs" /> + + + + - - - - + + - - + diff --git a/packages/canon/src/components/Grid/Grid.stories.tsx b/packages/canon/src/components/Grid/Grid.stories.tsx new file mode 100644 index 0000000000..e9833a0325 --- /dev/null +++ b/packages/canon/src/components/Grid/Grid.stories.tsx @@ -0,0 +1,91 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { Grid } from './Grid'; +import type { GridItemProps } from './types'; +import { Box } from '../Box/Box'; +import { argTypesSpacing, argTypesColor } from '../../../docs/utils/argTypes'; +import { Stack } from '../Stack'; + +const meta = { + title: 'Components/Grid', + component: Grid, + argTypes: { + ...argTypesSpacing, + ...argTypesColor, + children: { + control: false, + }, + className: { + control: 'text', + }, + }, + args: { + gap: 'xs', + }, +} satisfies Meta<typeof Grid>; + +export default meta; +type Story = StoryObj<typeof meta>; + +const FakeBox = () => ( + <Box + borderRadius="small" + style={{ background: '#1f47ff', color: 'white', height: '64px' }} + /> +); + +export const Default: Story = { + args: { + columns: 3, + }, + render: args => ( + <Grid {...args}> + <Grid.Item> + <FakeBox /> + </Grid.Item> + <Grid.Item> + <FakeBox /> + </Grid.Item> + <Grid.Item> + <FakeBox /> + </Grid.Item> + </Grid> + ), +}; + +export const ColumnSizes: Story = { + args: { + columns: 12, + gap: 'md', + }, + render: args => ( + <Stack gap="md"> + {Array.from({ length: 11 }, (_, i) => ( + <Grid {...args} key={i}> + <Grid.Item span={(i + 1) as GridItemProps['span']}> + <FakeBox /> + </Grid.Item> + <Grid.Item span={(11 - i) as GridItemProps['span']}> + <FakeBox /> + </Grid.Item> + </Grid> + ))} + </Stack> + ), +}; diff --git a/packages/canon/src/components/Grid/Grid.tsx b/packages/canon/src/components/Grid/Grid.tsx new file mode 100644 index 0000000000..28c49d87b1 --- /dev/null +++ b/packages/canon/src/components/Grid/Grid.tsx @@ -0,0 +1,70 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createElement } from 'react'; +import { GridItemProps, GridProps } from './types'; +import { gridItemSprinkles, gridSprinkles } from './sprinkles.css'; + +export const Grid = ({ + children, + columns = 1, + gap = 'xs', + className, + style, + ...restProps +}: GridProps) => { + const sprinklesClassName = gridSprinkles({ + ...restProps, + gap, + gridTemplateColumns: columns, + }); + + const classNames = ['grid', sprinklesClassName, className] + .filter(Boolean) + .join(' '); + + return createElement( + 'div', + { + className: classNames, + style, + }, + children, + ); +}; + +const GridItem = ({ + children, + span = 1, + start, + end, + className, + style, +}: GridItemProps) => { + const sprinklesClassName = gridItemSprinkles({ + span, + start, + end, + }); + + const classNames = ['grid-item', sprinklesClassName, className] + .filter(Boolean) + .join(' '); + + return createElement('div', { className: classNames, style }, children); +}; + +Grid.Item = GridItem; diff --git a/packages/canon/src/components/Grid/index.ts b/packages/canon/src/components/Grid/index.ts new file mode 100644 index 0000000000..b2534c6b77 --- /dev/null +++ b/packages/canon/src/components/Grid/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { Grid } from './Grid'; +export type { GridProps, GridItemProps } from './types'; diff --git a/packages/canon/src/components/Grid/sprinkles.css.ts b/packages/canon/src/components/Grid/sprinkles.css.ts new file mode 100644 index 0000000000..a2ac056d61 --- /dev/null +++ b/packages/canon/src/components/Grid/sprinkles.css.ts @@ -0,0 +1,113 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles'; +import { breakpoints } from '../../layout/properties'; +import { colorProperties, spacingProperties } from '../../layout/sprinkles.css'; + +const gridProperties = defineProperties({ + conditions: breakpoints, + defaultCondition: 'xs', + properties: { + gridTemplateColumns: { + 1: 'repeat(1, minmax(0, 1fr))', + 2: 'repeat(2, minmax(0, 1fr))', + 3: 'repeat(3, minmax(0, 1fr))', + 4: 'repeat(4, minmax(0, 1fr))', + 5: 'repeat(5, minmax(0, 1fr))', + 6: 'repeat(6, minmax(0, 1fr))', + 7: 'repeat(7, minmax(0, 1fr))', + 8: 'repeat(8, minmax(0, 1fr))', + 9: 'repeat(9, minmax(0, 1fr))', + 10: 'repeat(10, minmax(0, 1fr))', + 11: 'repeat(11, minmax(0, 1fr))', + 12: 'repeat(12, minmax(0, 1fr))', + }, + }, + shorthands: { + columns: ['gridTemplateColumns'], + }, +}); + +export const gridSprinkles = createSprinkles( + spacingProperties, + colorProperties, + gridProperties, +); + +const gridItemProperties = defineProperties({ + conditions: breakpoints, + defaultCondition: 'xs', + properties: { + gridColumn: { + 1: 'span 1 / span 1', + 2: 'span 2 / span 2', + 3: 'span 3 / span 3', + 4: 'span 4 / span 4', + 5: 'span 5 / span 5', + 6: 'span 6 / span 6', + 7: 'span 7 / span 7', + 8: 'span 8 / span 8', + 9: 'span 9 / span 9', + 10: 'span 10 / span 10', + 11: 'span 11 / span 11', + 12: 'span 12 / span 12', + full: '1 / -1', + }, + gridColumnStart: { + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13', + auto: 'auto', + }, + gridColumnEnd: { + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13', + auto: 'auto', + }, + }, + shorthands: { + span: ['gridColumn'], + start: ['gridColumnStart'], + end: ['gridColumnEnd'], + }, +}); + +export const gridItemSprinkles = createSprinkles( + colorProperties, + gridItemProperties, +); diff --git a/packages/canon/src/components/Grid/styles.css b/packages/canon/src/components/Grid/styles.css new file mode 100644 index 0000000000..81f8d5471d --- /dev/null +++ b/packages/canon/src/components/Grid/styles.css @@ -0,0 +1,3 @@ +.grid { + display: grid; +} diff --git a/packages/canon/src/components/Grid/types.ts b/packages/canon/src/components/Grid/types.ts new file mode 100644 index 0000000000..6432a8312c --- /dev/null +++ b/packages/canon/src/components/Grid/types.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Breakpoint, ColorProps } from '../../layout/types'; +import { SpaceProps } from '../../layout/types'; + +export interface GridProps extends SpaceProps, ColorProps { + children?: React.ReactNode; + columns?: + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | Partial< + Record<Breakpoint, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12> + >; + className?: string; + style?: React.CSSProperties; +} + +export interface GridItemProps { + children: React.ReactNode; + span?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 'full'; + start?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 'auto'; + end?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 'auto'; + className?: string; + style?: React.CSSProperties; +} diff --git a/packages/canon/src/index.ts b/packages/canon/src/index.ts index 198a626f06..d020041e4e 100644 --- a/packages/canon/src/index.ts +++ b/packages/canon/src/index.ts @@ -24,3 +24,6 @@ export * from './components/Button'; export * from './components/Box'; export * from './components/Icon'; export * from './layout/types'; +export * from './components/Grid'; +export * from './components/Stack'; +export * from './components/Inline'; diff --git a/packages/canon/src/theme/styles.css b/packages/canon/src/theme/styles.css index 4a31340575..65aaabe909 100644 --- a/packages/canon/src/theme/styles.css +++ b/packages/canon/src/theme/styles.css @@ -25,3 +25,4 @@ @import '../components/Button/styles.css'; @import '../components/Stack/styles.css'; @import '../components/Inline/styles.css'; +@import '../components/Grid/styles.css';