diff --git a/packages/vkui/src/components/AppRoot/AppRoot.tsx b/packages/vkui/src/components/AppRoot/AppRoot.tsx index 7ba821f43ae..807f0525a32 100644 --- a/packages/vkui/src/components/AppRoot/AppRoot.tsx +++ b/packages/vkui/src/components/AppRoot/AppRoot.tsx @@ -28,6 +28,25 @@ const vkuiSizeXClassNames = { [SizeType.REGULAR]: 'vkui--sizeX-regular', }; +const vkuiLayoutClassNames = { + card: 'vkui--layout-card', + plain: 'vkui--layout-plain', +}; + +function containerClassNames(layout: AppRootProps['layout'], sizeX: SizeType | 'none'): string[] { + const classNames: string[] = []; + + if (layout) { + classNames.push(vkuiLayoutClassNames[layout]); + } + + if (sizeX !== SizeType.COMPACT) { + classNames.push(vkuiSizeXClassNames[sizeX]); + } + + return classNames; +} + const INSET_CUSTOM_PROPERTY_PREFIX = `--vkui_internal--safe_area_inset_`; // Используйте classList, но будьте осторожны @@ -45,6 +64,7 @@ export interface AppRootProps extends React.HTMLAttributes { portalRoot?: HTMLElement | React.RefObject | null; /** Disable portal for components */ disablePortal?: boolean; + layout?: 'card' | 'plain'; } /** @@ -58,6 +78,7 @@ export const AppRoot = ({ disablePortal, className, safeAreaInsets, + layout, ...props }: AppRootProps) => { const isKeyboardInputActive = useKeyboardInputTracker(); @@ -160,18 +181,20 @@ export const AppRoot = ({ if (mode === 'partial') { return noop; } - const className = sizeX !== SizeType.COMPACT ? vkuiSizeXClassNames[sizeX] : null; + + const classNames = containerClassNames(layout, sizeX); + const container = mode === 'embedded' ? rootRef.current?.parentElement : document!.body; - if (className === null || !container) { + if (!classNames.length || !container) { return noop; } - container.classList.add(className); + container.classList.add(...classNames); return () => { - container.classList.remove(className); + container.classList.remove(...classNames); }; - }, [sizeX]); + }, [sizeX, layout]); useIsomorphicLayoutEffect(() => { if (mode !== 'full' || appearance === undefined) { @@ -196,6 +219,7 @@ export const AppRoot = ({ keyboardInput: isKeyboardInputActive, mode, disablePortal, + layout, }} > diff --git a/packages/vkui/src/components/AppRoot/AppRootContext.ts b/packages/vkui/src/components/AppRoot/AppRootContext.ts index 5ff48a36f72..32d4d9ecf38 100644 --- a/packages/vkui/src/components/AppRoot/AppRootContext.ts +++ b/packages/vkui/src/components/AppRoot/AppRootContext.ts @@ -7,6 +7,7 @@ export interface AppRootContextInterface { mode?: 'partial' | 'embedded' | 'full'; keyboardInput?: boolean; disablePortal?: boolean; + layout?: 'card' | 'plain'; } export const AppRootContext = React.createContext({ diff --git a/packages/vkui/src/components/Group/Group.module.css b/packages/vkui/src/components/Group/Group.module.css index fcd926ac1d2..0829a7d2b68 100644 --- a/packages/vkui/src/components/Group/Group.module.css +++ b/packages/vkui/src/components/Group/Group.module.css @@ -64,6 +64,18 @@ } } +.Group--sizeX-compact.Group--mode-card { + padding-left: 0; + padding-right: 0; +} + +@media (--sizeX-compact) { + .Group--sizeX-none.Group--mode-card { + padding-left: 0; + padding-right: 0; + } +} + .Group--sizeX-compact { padding-left: 0; padding-right: 0; @@ -90,6 +102,18 @@ } } +.Group--sizeX-compact.Group--mode-card:first-of-type { + border-radius: 0 0 var(--vkui--size_border_radius_paper--regular) + var(--vkui--size_border_radius_paper--regular); +} + +@media (--sizeX-compact) { + .Group--sizeX-none.Group--mode-card:first-of-type { + border-radius: 0 0 var(--vkui--size_border_radius_paper--regular) + var(--vkui--size_border_radius_paper--regular); + } +} + /** * Изменено с ::after на ::before * потому что при ::after абсолютно позиционированный элемент накладывается @@ -123,6 +147,16 @@ } } +.Group--sizeX-compact.Group--mode-card::before { + box-shadow: none; +} + +@media (--sizeX-compact) { + .Group--sizeX-none.Group--mode-card::before { + box-shadow: none; + } +} + .Group--mode-plain + .Group__separator, .Group--mode-plain + .Group__separator + .Group__separator { padding-top: 8px; diff --git a/packages/vkui/src/components/Group/Group.tsx b/packages/vkui/src/components/Group/Group.tsx index 9f9bff0e1d3..f23a31c5bdc 100644 --- a/packages/vkui/src/components/Group/Group.tsx +++ b/packages/vkui/src/components/Group/Group.tsx @@ -6,6 +6,7 @@ import { SizeType } from '../../lib/adaptivity'; import { Platform } from '../../lib/platform'; import { warnOnce } from '../../lib/warnOnce'; import { HasChildren, HasRootRef } from '../../types'; +import { AppRootContext } from '../AppRoot/AppRootContext'; import { ModalRootContext } from '../ModalRoot/ModalRootContext'; import { Separator } from '../Separator/Separator'; import { Spacing } from '../Spacing/Spacing'; @@ -17,6 +18,33 @@ const sizeXClassNames = { [SizeType.COMPACT]: styles['Group--sizeX-compact'], }; +/** + * Вычисляем mode для Group. + */ +function useGroupMode(modeProps: GroupProps['mode']): 'plain' | 'card' | 'none' { + const { isInsideModal } = React.useContext(ModalRootContext); + const { layout } = React.useContext(AppRootContext); + const { sizeX = 'none' } = useAdaptivity(); + + if (modeProps) { + return modeProps; + } + + if (isInsideModal) { + return 'plain'; + } + + if (layout) { + return layout; + } + + if (sizeX !== 'none') { + return sizeX === SizeType.REGULAR ? 'card' : 'plain'; + } + + return 'none'; +} + export interface GroupProps extends HasRootRef, React.HTMLAttributes, @@ -62,16 +90,7 @@ export const Group = ({ const platform = usePlatform(); const { sizeX = 'none' } = useAdaptivity(); - let mode: GroupProps['mode'] | 'none' = modeProps; - - if (!modeProps) { - // Подробнее в "none" можно прочитать в ADAPTIVITY_GUIDE.md - mode = isInsideModal ? 'plain' : 'none'; - } - - if (mode === 'none' && sizeX !== 'none') { - mode = sizeX === SizeType.REGULAR ? 'card' : 'plain'; - } + const mode = useGroupMode(modeProps); const isTabPanel = restProps.role === 'tabpanel'; diff --git a/packages/vkui/src/components/Group/Readme.md b/packages/vkui/src/components/Group/Readme.md index 43bd0d4fbcd..531edb05af5 100644 --- a/packages/vkui/src/components/Group/Readme.md +++ b/packages/vkui/src/components/Group/Readme.md @@ -1,6 +1,6 @@ Группа – базовый компонент для группировки контента по смыслу. -```jsx { "props": { "layout": false, "adaptivity": true } } +```jsx { "props": { "layout": false, "showLayoutSelect": true, "adaptivity": true } } const MODAL_NAME = 'modal'; const Example = () => { diff --git a/packages/vkui/src/components/Panel/Panel.module.css b/packages/vkui/src/components/Panel/Panel.module.css index b77ae8eb760..b78da9ded8f 100644 --- a/packages/vkui/src/components/Panel/Panel.module.css +++ b/packages/vkui/src/components/Panel/Panel.module.css @@ -162,8 +162,8 @@ background-color: var(--vkui--color_background_content); } -.Panel.Panel--sizeX-regular .Panel__in, -.Panel.Panel--sizeX-regular::before { +.Panel--sizeX-regular .Panel__in, +.Panel--sizeX-regular::before { background-color: transparent; } @@ -173,3 +173,9 @@ background-color: transparent; } } + +/* В AppRoot была установлена layout настройка */ +.Panel--layoutSetting .Panel__in, +.Panel--layoutSetting::before { + background-color: transparent; +} diff --git a/packages/vkui/src/components/Panel/Panel.tsx b/packages/vkui/src/components/Panel/Panel.tsx index de320b343b8..7b78ef3c64a 100644 --- a/packages/vkui/src/components/Panel/Panel.tsx +++ b/packages/vkui/src/components/Panel/Panel.tsx @@ -6,6 +6,7 @@ import { SizeType } from '../../lib/adaptivity'; import { NavIdProps } from '../../lib/getNavId'; import { Platform } from '../../lib/platform'; import { HasRootRef } from '../../types'; +import { AppRootContext } from '../AppRoot/AppRootContext'; import { TooltipContainer } from '../Tooltip/TooltipContainer'; import { Touch } from '../Touch/Touch'; import styles from './Panel.module.css'; @@ -36,6 +37,7 @@ export const Panel = ({ }: PanelProps) => { const platform = usePlatform(); const { sizeX = 'none' } = useAdaptivity(); + const { layout } = React.useContext(AppRootContext); return (
diff --git a/packages/vkui/src/components/SplitLayout/Readme.md b/packages/vkui/src/components/SplitLayout/Readme.md index 7cf341adb24..8a7738c6e69 100644 --- a/packages/vkui/src/components/SplitLayout/Readme.md +++ b/packages/vkui/src/components/SplitLayout/Readme.md @@ -1,6 +1,6 @@ Компонент-контейнер для реализации интерфейса с [многоколоночной структурой](#!/Adaptivity). Тесно связан со [SplitCol](#!/SplitCol). -```jsx { "props": { "layout": false, "showCustomPanelHeaderAfterProps": true, "adaptivity": true } } +```jsx { "props": { "layout": false, "showCustomPanelHeaderAfterProps": true, "showLayoutSelect": true, "adaptivity": true } } const panels = ['panel 1', 'panel 2', 'panel 3']; const modals = ['modal 1', 'modal 2']; diff --git a/packages/vkui/src/styles/common.css b/packages/vkui/src/styles/common.css index e1bc81513b5..5f726a9d3ab 100644 --- a/packages/vkui/src/styles/common.css +++ b/packages/vkui/src/styles/common.css @@ -34,3 +34,11 @@ background: var(--vkui--color_background); } } + +.vkui--layout-card { + background: var(--vkui--color_background); +} + +.vkui--layout-plain { + background: var(--vkui--color_background_content); +} diff --git a/styleguide/Components/Playground/PlaygroundRenderer.js b/styleguide/Components/Playground/PlaygroundRenderer.js index 3be28bbed98..a13d74fb193 100644 --- a/styleguide/Components/Playground/PlaygroundRenderer.js +++ b/styleguide/Components/Playground/PlaygroundRenderer.js @@ -9,6 +9,7 @@ const PlaygroundRenderer = ({ name, preview, previewProps, tabBody, exampleIndex iframe = true, // Рендерить пример в айфреме adaptivity: _adaptivity, // Продвинутые контролеры настроек адаптивности showCustomPanelHeaderAfterProps = false, // Контроллер настроек, связанных с пользовательским слотом `after` у `PanelHeader` + showLayoutSelect = false, integration, containerStyle, config, @@ -28,6 +29,7 @@ const PlaygroundRenderer = ({ name, preview, previewProps, tabBody, exampleIndex
{cloneElement(preview, { diff --git a/styleguide/Components/Preview.js b/styleguide/Components/Preview.js index 78999e87f5c..ef77f23355a 100644 --- a/styleguide/Components/Preview.js +++ b/styleguide/Components/Preview.js @@ -39,11 +39,11 @@ const Layout = ({ children }) => { ); }; -const Config = ({ hasPointer, children, ...config }) => { +const Config = ({ hasPointer, layout, children, ...config }) => { return ( - {children} + {children} ); diff --git a/styleguide/Components/Setting/Setting.js b/styleguide/Components/Setting/Setting.js index fb7aa989ae0..28feb90e630 100644 --- a/styleguide/Components/Setting/Setting.js +++ b/styleguide/Components/Setting/Setting.js @@ -25,6 +25,14 @@ export const Setting = ({ [], ); + const title = + options?.find((option) => { + if (typeof option === 'string' || typeof option === 'number') { + return false; + } + return option.value === value; + })?.title || value; + const labelJsx = {label}: ; return ( @@ -69,7 +77,7 @@ export const Setting = ({ ); }} > - {value} + {title} )} diff --git a/styleguide/Components/Settings/LayoutSelect.js b/styleguide/Components/Settings/LayoutSelect.js new file mode 100644 index 00000000000..11835a42536 --- /dev/null +++ b/styleguide/Components/Settings/LayoutSelect.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { Link } from '@vkui'; +import { Setting } from '../Setting/Setting'; + +const layouts = [{ title: 'Не задано', value: undefined }, 'card', 'plain']; + +export const LayoutSelect = ({ onChange, value }) => { + const onChangeValue = (changeValue) => { + onChange(changeValue); + }; + + return ( + + Свойство AppRoot + + } + label="layout" + onChange={onChangeValue} + value={value} + options={layouts} + /> + ); +}; diff --git a/styleguide/Components/Settings/Settings.js b/styleguide/Components/Settings/Settings.js index a5991892b58..503ba1e715b 100644 --- a/styleguide/Components/Settings/Settings.js +++ b/styleguide/Components/Settings/Settings.js @@ -4,12 +4,13 @@ import { StyleGuideContext } from '../StyleGuide/StyleGuideRenderer'; import { AppearanceSelect } from './AppearanceSelect'; import { HasCustomPanelHeaderAfter } from './HasCustomPanelHeaderAfter'; import { HasPointerCheckbox } from './HasPointerCheckbox'; +import { LayoutSelect } from './LayoutSelect'; import { PlatformSelect } from './PlatformSelect'; import { ViewHeightSelect } from './ViewHeightSelect'; import { ViewWidthSelect } from './ViewWidthSelect'; import './Settings.css'; -export const Settings = ({ adaptivity, showCustomPanelHeaderAfterProps }) => { +export const Settings = ({ adaptivity, showCustomPanelHeaderAfterProps, showLayoutSelect }) => { const { sizeX } = useAdaptivityConditionalRender(); return ( @@ -58,6 +59,12 @@ export const Settings = ({ adaptivity, showCustomPanelHeaderAfterProps }) => { )} )} + {showLayoutSelect && ( + context.setContext({ layout })} + value={context.layout} + /> + )}
); diff --git a/styleguide/Components/StyleGuide/StyleGuideRenderer.js b/styleguide/Components/StyleGuide/StyleGuideRenderer.js index b1bb5b2f652..488808bc156 100644 --- a/styleguide/Components/StyleGuide/StyleGuideRenderer.js +++ b/styleguide/Components/StyleGuide/StyleGuideRenderer.js @@ -16,6 +16,7 @@ let initialState = { styleguideAppearance: Appearance.LIGHT, hasCustomPanelHeaderAfter: true, transitionMotionEnabled: true, + layout: undefined, }; try {