diff --git a/packages/vkui/src/components/Tabs/Readme.md b/packages/vkui/src/components/Tabs/Readme.md index e01e600d63..f7ca4d2187 100644 --- a/packages/vkui/src/components/Tabs/Readme.md +++ b/packages/vkui/src/components/Tabs/Readme.md @@ -131,12 +131,18 @@ const DefaultInPanel = ({ menuOpened, onMenuClick, selected, setSelected }) => { const Scrollable = () => { const [mode, setMode] = React.useState('default'); + const [layoutFillMode, setLayoutFillMode] = React.useState('auto'); const [selected, setSelected] = React.useState('news'); const [disabled, setDisabled] = React.useState(false); return ( - + { onChange={(event) => setMode(event.target.value)} /> + + setLayoutFillMode(event.target.value)} + /> + setDisabled((prev) => !prev)}>disabled ); diff --git a/packages/vkui/src/components/Tabs/Tabs.e2e-playground.tsx b/packages/vkui/src/components/Tabs/Tabs.e2e-playground.tsx index 5c45c7f19a..fccee103f0 100644 --- a/packages/vkui/src/components/Tabs/Tabs.e2e-playground.tsx +++ b/packages/vkui/src/components/Tabs/Tabs.e2e-playground.tsx @@ -125,3 +125,31 @@ export const TabsPlayground = (props: ComponentPlaygroundProps) => { ); }; + +export const TabsItemsFlexModePlayground = (props: ComponentPlaygroundProps) => { + return ( + + Unscrollable + Unscrollable + , + + Scrollable + + Scrollable + + , + ], + layoutFillMode: ['auto', 'shrinked', 'stretched'], + }, + ]} + > + {(props: TabsProps) => } + + ); +}; diff --git a/packages/vkui/src/components/Tabs/Tabs.e2e.tsx b/packages/vkui/src/components/Tabs/Tabs.e2e.tsx index 37e27d10db..8eabbbe168 100644 --- a/packages/vkui/src/components/Tabs/Tabs.e2e.tsx +++ b/packages/vkui/src/components/Tabs/Tabs.e2e.tsx @@ -1,8 +1,23 @@ import * as React from 'react'; import { test } from '@vkui-e2e/test'; -import { TabsPlayground } from './Tabs.e2e-playground'; +import { Appearance } from '../../helpers/appearance'; +import { TabsItemsFlexModePlayground, TabsPlayground } from './Tabs.e2e-playground'; test('Tabs', async ({ mount, expectScreenshotClippedToContent, componentPlaygroundProps }) => { await mount(); await expectScreenshotClippedToContent(); }); + +test.describe('Tabs', () => { + test.use({ + onlyForAppearances: [Appearance.LIGHT], + }); + test('layout fill mode', async ({ + mount, + expectScreenshotClippedToContent, + componentPlaygroundProps, + }) => { + await mount(); + await expectScreenshotClippedToContent(); + }); +}); diff --git a/packages/vkui/src/components/Tabs/Tabs.tsx b/packages/vkui/src/components/Tabs/Tabs.tsx index babf30a3e2..3c2d656e9a 100644 --- a/packages/vkui/src/components/Tabs/Tabs.tsx +++ b/packages/vkui/src/components/Tabs/Tabs.tsx @@ -22,11 +22,20 @@ export interface TabsProps extends HTMLAttributesWithRootRef { * @since 5.10.0 */ scrollBehaviorToSelectedTab?: ScrollIntoViewOptions['inline']; + /** + * При `auto` ширина вкладок определяется контекстом: + * - равномерно занимают всю доступную ширину при вложении в `HorizontalScroll` + * - равномерно занимают всю доступную ширину при `mode=default` и platform !== 'VKCOM' + * При `stretched` и `shrinked` вкладки либо равномерно занимают всю ширину, + * либо выравниваются по контенту соответственно + */ + layoutFillMode?: 'auto' | 'stretched' | 'shrinked'; } export interface TabsContextProps { mode: TabsProps['mode']; withGaps: boolean; + layoutFillMode: NonNullable; withScrollToSelectedTab: TabsProps['withScrollToSelectedTab']; scrollBehaviorToSelectedTab: Required; } @@ -34,6 +43,7 @@ export interface TabsContextProps { export const TabsModeContext = React.createContext({ mode: 'default', withGaps: false, + layoutFillMode: 'auto', withScrollToSelectedTab: false, scrollBehaviorToSelectedTab: 'nearest', }); @@ -47,6 +57,7 @@ export const Tabs = ({ role = 'tablist', withScrollToSelectedTab, scrollBehaviorToSelectedTab = 'nearest', + layoutFillMode = 'auto', ...restProps }: TabsProps) => { const platform = usePlatform(); @@ -167,6 +178,7 @@ export const Tabs = ({ value={{ mode, withGaps, + layoutFillMode, withScrollToSelectedTab, scrollBehaviorToSelectedTab, }} diff --git a/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-android-chromium-light-1-snap.png b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-android-chromium-light-1-snap.png new file mode 100644 index 0000000000..2c93c09cf7 --- /dev/null +++ b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-android-chromium-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21341ab6228655178b06c597f564a53743840a627c83a657a1788ebd6b6fdbcc +size 139892 diff --git a/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-ios-webkit-light-1-snap.png b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-ios-webkit-light-1-snap.png new file mode 100644 index 0000000000..6c93384723 --- /dev/null +++ b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-ios-webkit-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d907ae7a04f25a0fbeb5c7aa3f13ba5c60535c296e5356159862856f97425da8 +size 129027 diff --git a/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-vkcom-chromium-light-1-snap.png b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-vkcom-chromium-light-1-snap.png new file mode 100644 index 0000000000..f160d3217c --- /dev/null +++ b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-vkcom-chromium-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aad87d00a33b748cb6611ba2469d9c12ef1c6eff45fb6d5a0f01704cf8ba4253 +size 130264 diff --git a/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-vkcom-firefox-light-1-snap.png b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-vkcom-firefox-light-1-snap.png new file mode 100644 index 0000000000..40d5c85b29 --- /dev/null +++ b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-vkcom-firefox-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5e59121b9e2301c2c5d1c07c434c644923627fabc9212afae600cddf8ffeb36 +size 158093 diff --git a/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-vkcom-webkit-light-1-snap.png b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-vkcom-webkit-light-1-snap.png new file mode 100644 index 0000000000..1651a8e0d9 --- /dev/null +++ b/packages/vkui/src/components/Tabs/__image_snapshots__/tabs-layout-fill-mode-vkcom-webkit-light-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:175b28c7ed96f957b897e9078a4f139d9c04a94abb7a88d51752a8d5b8d4fc1e +size 119808 diff --git a/packages/vkui/src/components/TabsItem/TabsItem.module.css b/packages/vkui/src/components/TabsItem/TabsItem.module.css index 02a10deb45..9065717663 100644 --- a/packages/vkui/src/components/TabsItem/TabsItem.module.css +++ b/packages/vkui/src/components/TabsItem/TabsItem.module.css @@ -217,3 +217,12 @@ flex-grow: 1; flex-shrink: 0; } + +.TabsItem.TabsItem--stretched { + flex-grow: 1; +} + +.TabsItem.TabsItem--shrinked { + flex-grow: 0; + min-width: auto; +} diff --git a/packages/vkui/src/components/TabsItem/TabsItem.tsx b/packages/vkui/src/components/TabsItem/TabsItem.tsx index 3244bb0986..e271f7fd80 100644 --- a/packages/vkui/src/components/TabsItem/TabsItem.tsx +++ b/packages/vkui/src/components/TabsItem/TabsItem.tsx @@ -25,6 +25,11 @@ const stylesMode = { secondary: styles['TabsItem--mode-secondary'], }; +const fillModeClassNames = { + stretched: styles['TabsItem--stretched'], + shrinked: styles['TabsItem--shrinked'], +}; + export interface TabsItemProps extends HTMLAttributesWithRootRef { /** * Добавляет иконку слева. @@ -69,8 +74,13 @@ export const TabsItem = ({ ...restProps }: TabsItemProps) => { const { sizeY = 'none' } = useAdaptivity(); - const { mode, withGaps, scrollBehaviorToSelectedTab, withScrollToSelectedTab }: TabsContextProps = - React.useContext(TabsModeContext); + const { + mode, + withGaps, + layoutFillMode, + scrollBehaviorToSelectedTab, + withScrollToSelectedTab, + }: TabsContextProps = React.useContext(TabsModeContext); let statusComponent = null; const isTabFlow = role === 'tab'; @@ -160,6 +170,7 @@ export const TabsItem = ({ selected && styles['TabsItem--selected'], sizeY !== SizeType.REGULAR && sizeYClassNames[sizeY], withGaps && styles['TabsItem--withGaps'], + layoutFillMode !== 'auto' && fillModeClassNames[layoutFillMode], className, )} hoverMode={styles['TabsItem--hover']}