diff --git a/packages/vkui/src/components/Flex/Flex.tsx b/packages/vkui/src/components/Flex/Flex.tsx index 0f251e4bb6..48ccf46cca 100644 --- a/packages/vkui/src/components/Flex/Flex.tsx +++ b/packages/vkui/src/components/Flex/Flex.tsx @@ -46,6 +46,8 @@ export interface FlexProps extends HTMLAttributesWithRootRef { * Отступы между элементами. * Значение из списка предопределённых пресетов или число, которое будет приведено к пикселям. * Через массив можно задать отступ между столбцами и строками [column, row], если они отличаются. + * + * TODO [>=7]: порядок следования будет [row, column] */ gap?: GapsProp; /** diff --git a/packages/vkui/src/components/SimpleGrid/Readme.md b/packages/vkui/src/components/SimpleGrid/Readme.md new file mode 100644 index 0000000000..548b61e3d9 --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/Readme.md @@ -0,0 +1,189 @@ +Базовый компонент для позиционирования элементов, каждый из которых занимает равное количество пространства. +Построен на базе `grid layout`. Можно указать либо `columns` (тогда пространство будет поделено на заданное количество колонок), либо `minColWidth` (тогда пространство будет распределено с учетом минимальной ширины колонки) + +```jsx { "props": { "layout": false, "iframe": false } } +const halsey = { + subtitle: 'ALBUM', + header: 'Halsey – Badlands⁣', + caption: 'Blue Vinyl · EU · 2015⁣', + text: 'Badlands is the story about dreams and cruel reality...', +}; + +const lorde = { + subtitle: 'ALBUM', + header: 'Lorde – Melodrama', + caption: 'Blue Vinyl · EU · 2018⁣', + text: 'Lorde captures emotions like none other. Her second album is a masterful study of being a young woman, a sleek and humid pop record full of grief and hedonism, crafted with the utmost care and wisdom.', +}; + +const GapSelectValues = [ + { + label: '2xs', + value: '2xs', + }, + { + label: 'xs', + value: 'xs', + }, + { + label: 's', + value: 's', + }, + { + label: 'm', + value: 'm', + }, + { + label: 'l', + value: 'l', + }, + { + label: 'xl', + value: 'xl', + }, + { + label: '2xl', + value: '2xl', + }, + { + label: '3xl', + value: '3xl', + }, + { + label: '4xl', + value: '4xl', + }, +]; + +const Example = () => { + const [gap, setGap] = useState('m'); + const [rowGap, setRowGap] = useState('m'); + const [columnGap, setColumnGap] = useState('m'); + const [columns, setColumns] = useState(3); + const [itemsCount, setItemsCount] = useState(5); + const [margin, setMargin] = useState('none'); + const [complexGap, setComplexGap] = useState(false); + const [align, setAlign] = useState('stretch'); + const platform = usePlatform(); + + return ( +
+
+ + {Array.from({ length: itemsCount }, (item, index) => { + return ; + })} + +
+
+ + setItemsCount(value)} + /> + + + setColumns(value)} + /> + + + + setMargin(e.target.value)} + > + none + + setMargin(e.target.value)} + > + auto + + setMargin(e.target.value)} + > + auto-inline + + setMargin(e.target.value)} + > + auto-block + + + + + setGap(e.target.value)} + options={GapSelectValues} + /> + + )} + {complexGap && ( + + setColumnGap(e.target.value)} + options={GapSelectValues} + /> + + )} +
+
+ ); +}; + +; +``` diff --git a/packages/vkui/src/components/SimpleGrid/SimpleGrid.e2e-playground.tsx b/packages/vkui/src/components/SimpleGrid/SimpleGrid.e2e-playground.tsx new file mode 100644 index 0000000000..17abec691c --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/SimpleGrid.e2e-playground.tsx @@ -0,0 +1,32 @@ +import { ComponentPlayground, type ComponentPlaygroundProps } from '@vkui-e2e/playground-helpers'; +import { SimpleGrid, type SimpleGridProps } from './SimpleGrid'; + +const ChildNode = () =>
Grid Item
; + +export const SimpleGridPlayground = (props: ComponentPlaygroundProps) => { + return ( + , ]], + columns: [1, 2], + gap: ['m'], + margin: ['auto'], + }, + { + margin: ['auto-inline', 'auto-block'], + children: [[, ]], + gap: ['xl'], + }, + { + children: [[, , ]], + gap: [[8, 16]], + columns: [2], + }, + ]} + > + {(props: SimpleGridProps) => } + + ); +}; diff --git a/packages/vkui/src/components/SimpleGrid/SimpleGrid.e2e.tsx b/packages/vkui/src/components/SimpleGrid/SimpleGrid.e2e.tsx new file mode 100644 index 0000000000..fb50dc04a0 --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/SimpleGrid.e2e.tsx @@ -0,0 +1,16 @@ +import { test } from '@vkui-e2e/test'; +import { SimpleGridPlayground } from './SimpleGrid.e2e-playground'; + +test.describe('SimpleGrid', () => { + test.use({ + onlyForAppearances: ['light'], + }); + test('Rendering', async ({ + mount, + expectScreenshotClippedToContent, + componentPlaygroundProps, + }) => { + await mount(); + await expectScreenshotClippedToContent(); + }); +}); diff --git a/packages/vkui/src/components/SimpleGrid/SimpleGrid.module.css b/packages/vkui/src/components/SimpleGrid/SimpleGrid.module.css new file mode 100644 index 0000000000..71b5039627 --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/SimpleGrid.module.css @@ -0,0 +1,45 @@ +.SimpleGrid { + --vkui_internal--grid_columns: 1; + --vkui_internal--min_col_width: 0; + + display: grid; + gap: var(--vkui_internal--row_gap) var(--vkui_internal--column_gap); + grid-template-columns: repeat(var(--vkui_internal--grid_columns), minmax(0, 1fr)); +} + +.SimpleGrid--margin-auto { + margin-inline: var(--vkui--size_base_padding_horizontal--regular); + margin-block: var(--vkui--size_base_padding_vertical--regular); +} + +.SimpleGrid--margin-auto-inline { + margin-inline: var(--vkui--size_base_padding_horizontal--regular); +} + +.SimpleGrid--margin-auto-block { + margin-block: var(--vkui--size_base_padding_vertical--regular); +} + +.SimpleGrid--with-min-width { + grid-template-columns: repeat(auto-fit, minmax(var(--vkui_internal--min_col_width), 1fr)); +} + +.SimpleGrid--align-start { + align-items: flex-start; +} + +.SimpleGrid--align-end { + align-items: flex-end; +} + +.SimpleGrid--align-center { + align-items: center; +} + +.SimpleGrid--align-stretch { + align-items: stretch; +} + +.SimpleGrid--align-baseline { + align-items: baseline; +} diff --git a/packages/vkui/src/components/SimpleGrid/SimpleGrid.stories.tsx b/packages/vkui/src/components/SimpleGrid/SimpleGrid.stories.tsx new file mode 100644 index 0000000000..80324c01dd --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/SimpleGrid.stories.tsx @@ -0,0 +1,42 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants'; +import { ContentCard } from '../ContentCard/ContentCard'; +import { SimpleGrid, SimpleGridProps } from './SimpleGrid'; + +const story: Meta = { + title: 'Layout/SimpleGrid', + component: SimpleGrid, + parameters: { ...CanvasFullLayout, ...DisableCartesianParam }, +}; + +export default story; + +type Story = StoryObj; + +export const Playground: Story = { + args: { + gap: 'm', + }, + render: (args) => ( + + {Array.from({ length: 5 }, (_, index) => { + return ( + + ); + })} + + ), + decorators: [ + (Component) => ( +
+ +
+ ), + ], +}; diff --git a/packages/vkui/src/components/SimpleGrid/SimpleGrid.test.tsx b/packages/vkui/src/components/SimpleGrid/SimpleGrid.test.tsx new file mode 100644 index 0000000000..fe94467891 --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/SimpleGrid.test.tsx @@ -0,0 +1,6 @@ +import { baselineComponent } from '../../testing/utils'; +import { SimpleGrid } from './SimpleGrid'; + +describe('SimpleGrid', () => { + baselineComponent(SimpleGrid); +}); diff --git a/packages/vkui/src/components/SimpleGrid/SimpleGrid.tsx b/packages/vkui/src/components/SimpleGrid/SimpleGrid.tsx new file mode 100644 index 0000000000..d7268e8e59 --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/SimpleGrid.tsx @@ -0,0 +1,93 @@ +import { classNames } from '@vkontakte/vkjs'; +import { + calculateGap, + columnGapClassNames, + type GapsProp, + rowGapClassNames, +} from '../../lib/layouts'; +import { CSSCustomProperties, HTMLAttributesWithRootRef } from '../../types'; +import { RootComponent } from '../RootComponent/RootComponent'; +import styles from './SimpleGrid.module.css'; + +const marginClassNames = { + 'auto': styles['SimpleGrid--margin-auto'], + 'auto-inline': styles['SimpleGrid--margin-auto-inline'], + 'auto-block': styles['SimpleGrid--margin-auto-block'], +}; + +const alignClassNames = { + start: styles['SimpleGrid--align-start'], + end: styles['SimpleGrid--align-end'], + center: styles['SimpleGrid--align-center'], + stretch: styles['SimpleGrid--align-stretch'], + baseline: styles['SimpleGrid--align-baseline'], +}; + +export interface SimpleGridProps extends HTMLAttributesWithRootRef { + /** + * Количество колонок + */ + columns?: number; + /** + * Отступы между элементами. + * Значение из списка предопределённых пресетов или число, которое будет приведено к пикселям. + * Через массив можно задать отступ между столбцами и строками [column, row], если они отличаются. + * + * TODO [>=7]: порядок следования будет [row, column] + */ + gap?: GapsProp; + /** + * Управляет отступами вокруг контейнера + * Значение `none` позволяет отключить отступы + * Значение `auto` позволяет задать платформенные отступы + * Значение `auto-inline` позволяет задать платформенные inline-отступы + * Значение `auto-block` позволяет задать платформенные block-отступы + */ + margin?: 'none' | 'auto' | 'auto-inline' | 'auto-block'; + /** + * Вместо задания количества колонок, можно указать минимальную ширину элементов + */ + minColWidth?: number; + /** + * Выравнивание элементов по вспомогательной оси, эквивалентно `align-items` + */ + align?: 'start' | 'end' | 'center' | 'stretch' | 'baseline'; +} + +export const SimpleGrid = ({ + columns = 1, + gap, + style: styleProp, + margin = 'none', + minColWidth, + align = 'stretch', + ...props +}: SimpleGridProps) => { + const style: CSSCustomProperties = {}; + const [columnGap, rowGap] = calculateGap(gap); + if (typeof rowGap === 'number') { + style['--vkui_internal--row_gap'] = `${rowGap}px`; + } + if (typeof columnGap === 'number') { + style['--vkui_internal--column_gap'] = `${columnGap}px`; + } + style['--vkui_internal--grid_columns'] = `${columns}`; + if (minColWidth) { + style['--vkui_internal--min_col_width'] = `${minColWidth}px`; + } + + return ( + + ); +}; diff --git a/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-android-chromium-light-1-snap.png b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-android-chromium-light-1-snap.png new file mode 100644 index 0000000000..af44440e79 --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-android-chromium-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43ff18747569867ec3133b7ca523706e90e6389d1356d2d3de1b3c14b3604012 +size 26454 diff --git a/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-ios-webkit-light-1-snap.png b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-ios-webkit-light-1-snap.png new file mode 100644 index 0000000000..df2454845c --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-ios-webkit-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3abf41d470ebf72c93073e9bd048848590682a5de903fd1077ef18c7ada91613 +size 26760 diff --git a/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-vkcom-chromium-light-1-snap.png b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-vkcom-chromium-light-1-snap.png new file mode 100644 index 0000000000..8bb6ad4bbe --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-vkcom-chromium-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e2a69280c2f333b74470dc578f4dd8b65f14c5d5062511f89635da82acde5ff +size 25643 diff --git a/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-vkcom-firefox-light-1-snap.png b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-vkcom-firefox-light-1-snap.png new file mode 100644 index 0000000000..cbec462021 --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-vkcom-firefox-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a15f87ff71fe19bf009a1187474280c1b94ce52a221cb327e8902f5a7bb4468 +size 37704 diff --git a/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-vkcom-webkit-light-1-snap.png b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-vkcom-webkit-light-1-snap.png new file mode 100644 index 0000000000..663627a397 --- /dev/null +++ b/packages/vkui/src/components/SimpleGrid/__image_snapshots__/simplegrid-rendering-vkcom-webkit-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6027fed85deddc9d55924efc737891abd4c6b34e1b648506b84ebe4790b994dd +size 25807 diff --git a/packages/vkui/src/index.ts b/packages/vkui/src/index.ts index 09e38c8c63..4f41b50af8 100644 --- a/packages/vkui/src/index.ts +++ b/packages/vkui/src/index.ts @@ -106,6 +106,8 @@ export { AspectRatio } from './components/AspectRatio/AspectRatio'; export type { AspectRatioProps } from './components/AspectRatio/AspectRatio'; export { Flex } from './components/Flex/Flex'; export type { FlexProps } from './components/Flex/Flex'; +export { SimpleGrid } from './components/SimpleGrid/SimpleGrid'; +export type { SimpleGridProps } from './components/SimpleGrid/SimpleGrid'; /** * Popouts diff --git a/styleguide/config.js b/styleguide/config.js index 9803fb58b2..2f42c0fff2 100644 --- a/styleguide/config.js +++ b/styleguide/config.js @@ -166,6 +166,7 @@ const baseConfig = { `../${VKUI_PACKAGE.PATHS.COMPONENTS_DIR}/FixedLayout/FixedLayout.tsx`, `../${VKUI_PACKAGE.PATHS.COMPONENTS_DIR}/AspectRatio/AspectRatio.tsx`, `../${VKUI_PACKAGE.PATHS.COMPONENTS_DIR}/Flex/Flex.tsx`, + `../${VKUI_PACKAGE.PATHS.COMPONENTS_DIR}/SimpleGrid/SimpleGrid.tsx`, ], sections: [ {