diff --git a/docs/src/docs/core/Grid.mdx b/docs/src/docs/core/Grid.mdx index 37144371184..dabe1009097 100644 --- a/docs/src/docs/core/Grid.mdx +++ b/docs/src/docs/core/Grid.mdx @@ -31,7 +31,7 @@ Use xs, sm, md, lg, xl values to set spacing from `theme.spacing` or number to s ## Grow -Set `grow` prop on Grid component to force last row take 100% of container width: +Set `grow` prop on Grid component to force all rows to take 100% of container width: @@ -70,6 +70,20 @@ In this example up to `md` there will be 1 column, from `md` to `lg` there will +## Auto sized columns + +All columns in a row with `span` or a breakpoint of `auto` will have equal size, growing as much as they can to fill the row. + +In this example, the second column takes up 50% of the row while the other two columns automatically resize to fill the remaining space: + + + +## Fit column content + +If you set `span` or a breakpoint to `content`, the column's size will automatically adjust to match the width of its content: + + + ## Change columns count By default, grid uses 12 columns layout, you can change it by setting `columns` prop on Grid component. diff --git a/src/mantine-core/src/Grid/Col/Col.styles.ts b/src/mantine-core/src/Grid/Col/Col.styles.ts index 903b287bc49..c36a794c1f7 100644 --- a/src/mantine-core/src/Grid/Col/Col.styles.ts +++ b/src/mantine-core/src/Grid/Col/Col.styles.ts @@ -6,6 +6,8 @@ import { MantineTheme, } from '@mantine/styles'; +export type ColSpan = number | 'auto' | 'content'; + interface ColStyles { gutter: MantineNumberSize; columns: number; @@ -16,12 +18,12 @@ interface ColStyles { offsetMd: number; offsetLg: number; offsetXl: number; - span: number; - xs: number; - sm: number; - md: number; - lg: number; - xl: number; + span: ColSpan; + xs: ColSpan; + sm: ColSpan; + md: ColSpan; + lg: ColSpan; + xl: ColSpan; order: React.CSSProperties['order']; orderXs: React.CSSProperties['order']; orderSm: React.CSSProperties['order']; @@ -30,8 +32,29 @@ interface ColStyles { orderXl: React.CSSProperties['order']; } -const getColumnWidth = (colSpan: number, columns: number) => - colSpan ? `${100 / (columns / colSpan)}%` : undefined; +const getColumnFlexBasis = (colSpan: ColSpan, columns: number) => { + if (colSpan === 'content') { + return 'auto'; + } + if (colSpan === 'auto') { + return '0px'; + } + return colSpan ? `${100 / (columns / colSpan)}%` : undefined; +}; + +const getColumnMaxWidth = (colSpan: ColSpan, columns: number, grow: boolean) => { + if (grow || colSpan === 'auto' || colSpan === 'content') { + return 'unset'; + } + return getColumnFlexBasis(colSpan, columns); +}; + +const getColumnFlexGrow = (colSpan: ColSpan, grow: boolean) => { + if (!colSpan) { + return undefined; + } + return colSpan === 'auto' || grow ? 1 : 0; +}; const getColumnOffset = (offset: number, columns: number) => offset ? `${100 / (columns / offset)}%` : undefined; @@ -44,7 +67,7 @@ function getBreakpointsStyles({ columns, grow, }: { - sizes: Record; + sizes: Record; offsets: Record; orders: Record; grow: boolean; @@ -54,10 +77,12 @@ function getBreakpointsStyles({ return MANTINE_SIZES.reduce((acc, size) => { acc[`@media (min-width: ${theme.breakpoints[size] + 1}px)`] = { order: orders[size], - flexBasis: getColumnWidth(sizes[size], columns), + flexBasis: getColumnFlexBasis(sizes[size], columns), flexShrink: 0, - maxWidth: grow ? 'unset' : getColumnWidth(sizes[size], columns), + width: sizes[size] === 'content' ? 'auto' : undefined, + maxWidth: getColumnMaxWidth(sizes[size], columns, grow), marginLeft: getColumnOffset(offsets[size], columns), + flexGrow: getColumnFlexGrow(sizes[size], grow), }; return acc; }, {}); @@ -92,13 +117,14 @@ export default createStyles( ) => ({ root: { boxSizing: 'border-box', - flexGrow: grow ? 1 : 0, + flexGrow: getColumnFlexGrow(span, grow), order, padding: theme.fn.size({ size: gutter, sizes: theme.spacing }) / 2, marginLeft: getColumnOffset(offset, columns), - flexBasis: getColumnWidth(span, columns), + flexBasis: getColumnFlexBasis(span, columns), flexShrink: 0, - maxWidth: grow ? 'unset' : getColumnWidth(span, columns), + width: span === 'content' ? 'auto' : undefined, + maxWidth: getColumnMaxWidth(span, columns, grow), ...getBreakpointsStyles({ sizes: { xs, sm, md, lg, xl }, offsets: { xs: offsetXs, sm: offsetSm, md: offsetMd, lg: offsetLg, xl: offsetXl }, diff --git a/src/mantine-core/src/Grid/Col/Col.tsx b/src/mantine-core/src/Grid/Col/Col.tsx index 37e490df53d..6b9d82ada72 100644 --- a/src/mantine-core/src/Grid/Col/Col.tsx +++ b/src/mantine-core/src/Grid/Col/Col.tsx @@ -2,11 +2,11 @@ import React, { forwardRef } from 'react'; import { DefaultProps, useComponentDefaultProps } from '@mantine/styles'; import { Box } from '../../Box'; import { useGridContext } from '../Grid.context'; -import useStyles from './Col.styles'; +import useStyles, { ColSpan } from './Col.styles'; export interface ColProps extends DefaultProps, React.ComponentPropsWithoutRef<'div'> { /** Default col span */ - span?: number; + span?: ColSpan; /** Column left offset */ offset?: number; @@ -45,19 +45,19 @@ export interface ColProps extends DefaultProps, React.ComponentPropsWithoutRef<' offsetXl?: number; /** Col span at (min-width: theme.breakpoints.xs) */ - xs?: number; + xs?: ColSpan; /** Col span at (min-width: theme.breakpoints.sm) */ - sm?: number; + sm?: ColSpan; /** Col span at (min-width: theme.breakpoints.md) */ - md?: number; + md?: ColSpan; /** Col span at (min-width: theme.breakpoints.lg) */ - lg?: number; + lg?: ColSpan; /** Col span at (min-width: theme.breakpoints.xl) */ - xl?: number; + xl?: ColSpan; } const defaultProps: Partial = { @@ -69,7 +69,10 @@ const defaultProps: Partial = { offsetXl: 0, }; -function isValidSpan(span: number) { +function isValidSpan(span: ColSpan) { + if (span === 'auto' || span === 'content') { + return true; + } return typeof span === 'number' && span > 0 && span % 1 === 0; } diff --git a/src/mantine-demos/src/demos/core/Grid/Grid.demo.auto.tsx b/src/mantine-demos/src/demos/core/Grid/Grid.demo.auto.tsx new file mode 100644 index 00000000000..1c1241472d0 --- /dev/null +++ b/src/mantine-demos/src/demos/core/Grid/Grid.demo.auto.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Grid } from '@mantine/core'; +import { ColWrapper as Col } from './_col-wrapper'; + +const code = ` +import { Grid } from '@mantine/core'; + +function Demo() { + return ( + + span=auto + span=6 + span=auto + + ); +} +`; + +function Demo() { + return ( + + 1 + 2 + 3 + + ); +} + +export const auto: MantineDemo = { + type: 'demo', + code, + component: Demo, +}; diff --git a/src/mantine-demos/src/demos/core/Grid/Grid.demo.content.tsx b/src/mantine-demos/src/demos/core/Grid/Grid.demo.content.tsx new file mode 100644 index 00000000000..3c376083c74 --- /dev/null +++ b/src/mantine-demos/src/demos/core/Grid/Grid.demo.content.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Grid } from '@mantine/core'; +import { ColWrapper as Col } from './_col-wrapper'; + +const code = ` +import { Grid } from '@mantine/core'; + +function Demo() { + return ( + + fit content + 2 + + ); +} +`; + +function Demo() { + return ( + + fit content + 2 + + ); +} + +export const content: MantineDemo = { + type: 'demo', + code, + component: Demo, +}; diff --git a/src/mantine-demos/src/demos/core/Grid/index.ts b/src/mantine-demos/src/demos/core/Grid/index.ts index df68478af02..171338570e5 100644 --- a/src/mantine-demos/src/demos/core/Grid/index.ts +++ b/src/mantine-demos/src/demos/core/Grid/index.ts @@ -6,3 +6,5 @@ export { rows } from './Grid.demo.rows'; export { flexConfigurator } from './Grid.demo.flexConfigurator'; export { responsive } from './Grid.demo.responsive'; export { columns } from './Grid.demo.columns'; +export { auto } from './Grid.demo.auto'; +export { content } from './Grid.demo.content';