diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-18 19:25:02 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:14:41 +0100 |
| commit | 94448fa278ab352a741ff13f22d6104869571144 (patch) | |
| tree | 2185e77f2866d11a0144d4ac5a01c71a76807341 /src/components/molecules/grid/grid.tsx | |
| parent | c153f93dc8691a71dc76aad3dd618298da9d238a (diff) | |
feat(components): add a generic Grid component
* merge Columns, Gallery and CardsList into Grid component
* add more options to control the grid
Diffstat (limited to 'src/components/molecules/grid/grid.tsx')
| -rw-r--r-- | src/components/molecules/grid/grid.tsx | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/src/components/molecules/grid/grid.tsx b/src/components/molecules/grid/grid.tsx new file mode 100644 index 0000000..ca920f8 --- /dev/null +++ b/src/components/molecules/grid/grid.tsx @@ -0,0 +1,116 @@ +import { + type ForwardedRef, + type ReactNode, + forwardRef, + type CSSProperties, +} from 'react'; +import type { Spacing } from '../../../types'; +import { List, ListItem, type ListProps } from '../../atoms'; +import styles from './grid.module.scss'; + +export type GridItem = { + id: string; + item: ReactNode; +}; + +export type GridProps<T extends boolean> = Omit< + ListProps<T, false>, + 'children' | 'hideMarker' | 'isHierarchical' | 'isInline' | 'spacing' +> & { + /** + * Control the number of column. + * + * @default 'auto-fit' + */ + col?: number | 'auto-fill' | 'auto-fit'; + /** + * The gap between the items. + * + * @default null + */ + gap?: Spacing | null; + /** + * Should the grid be centered? + * + * @default false + */ + isCentered?: boolean; + /** + * The grid items. + */ + items: GridItem[]; + /** + * Define a fixed size for each item. + * + * You should either use `size` or `sizeMax`/`sizeMin` not both. + * + * @default undefined + */ + size?: string; + /** + * Define the maximal size of each item. + * + * You should either use `size` or `sizeMax`/`sizeMin` not both. + * + * @default '1fr' + */ + sizeMax?: string; + /** + * Define the maximal size of each item. + * + * You should either use `size` or `sizeMax`/`sizeMin` not both. + * + * @default 0 + */ + sizeMin?: 0 | string; +}; + +const GridWithRef = <T extends boolean>( + { + className = '', + col = 'auto-fit', + gap, + isCentered = false, + items, + size, + sizeMax, + sizeMin, + style, + ...props + }: GridProps<T>, + ref?: ForwardedRef<T extends true ? HTMLOListElement : HTMLUListElement> +) => { + const gridClass = [ + styles.wrapper, + styles[isCentered ? 'wrapper--is-centered' : ''], + styles[size ? 'wrapper--has-fixed-size' : ''], + styles[sizeMin ? 'wrapper--has-min-size' : ''], + className, + ].join(' '); + const gridStyles = { + ...style, + '--col': col, + ...(size ? { '--size': size } : {}), + ...(sizeMax ? { '--size-max': sizeMax } : {}), + ...(sizeMin ? { '--size-min': sizeMin } : {}), + ...(gap ? { '--gap': `var(--spacing-${gap})` } : {}), + } as CSSProperties; + + return ( + <List + {...props} + className={gridClass} + hideMarker + ref={ref} + style={gridStyles} + > + {items.map(({ id, item }) => ( + <ListItem className={styles.item} key={id}> + {item} + </ListItem> + ))} + </List> + ); +}; + +export const Grid = forwardRef(GridWithRef); |
