diff --git a/src/components/AsideHeader/AsideHeader.scss b/src/components/AsideHeader/AsideHeader.scss index da11730..0e7bada 100644 --- a/src/components/AsideHeader/AsideHeader.scss +++ b/src/components/AsideHeader/AsideHeader.scss @@ -228,32 +228,22 @@ $block: '.#{variables.$ns}aside-header'; flex-direction: row; } - &__pane-top-divider { - height: 1px; - background-color: var( - --gn-aside-header-divider-horizontal-color, - var(--_--horizontal-divider-line-color) - ); - margin-top: -1px; - } - - &__pane-top { + &__top-alert { position: fixed; z-index: var(--gn-aside-header-pane-top-z-index, 98); top: 0; background: var(--g-color-base-background); width: 100%; - } - - &__pane-top-alert { - &_centered { - display: flex; - justify-content: space-around; - } - &_dense { - padding-top: var(--g-spacing-2); - padding-bottom: var(--g-spacing-2); + &::after { + display: block; + content: ''; + height: 1px; + background-color: var( + --gn-aside-header-divider-horizontal-color, + var(--_--horizontal-divider-line-color) + ); + margin-top: -1px; } } diff --git a/src/components/AsideHeader/components/PageLayout/PageLayout.tsx b/src/components/AsideHeader/components/PageLayout/PageLayout.tsx index 576c228..9932223 100644 --- a/src/components/AsideHeader/components/PageLayout/PageLayout.tsx +++ b/src/components/AsideHeader/components/PageLayout/PageLayout.tsx @@ -8,8 +8,8 @@ import {b} from '../../utils'; import '../../AsideHeader.scss'; -const TopPanel = React.lazy(() => - import('../TopPanel').then((module) => ({default: module.TopPanel})), +const TopAlert = React.lazy(() => + import('../../../TopAlert').then((module) => ({default: module.TopAlert})), ); export interface PageLayoutProps extends PropsWithChildren {} @@ -28,7 +28,7 @@ const Layout = ({compact, className, children, topAlert}: PageLayoutProps) => { > {topAlert && ( - + )}
{children}
diff --git a/src/components/AsideHeader/components/TopPanel.tsx b/src/components/AsideHeader/components/TopPanel.tsx deleted file mode 100644 index 099e8cf..0000000 --- a/src/components/AsideHeader/components/TopPanel.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; - -import {Alert} from '@gravity-ui/uikit'; - -import {AsideHeaderTopAlertProps} from '../../types'; -import {useAsideHeaderTopPanel} from '../useAsideHeaderTopPanel'; -import {b} from '../utils'; - -type Props = { - topAlert?: AsideHeaderTopAlertProps; -}; - -export const TopPanel = ({topAlert}: Props) => { - const {topRef, updateTopSize} = useAsideHeaderTopPanel({topAlert}); - - const [opened, setOpened] = React.useState(true); - - const handleClose = React.useCallback(() => { - setOpened(false); - topAlert?.onCloseTopAlert?.(); - }, [topAlert]); - - React.useEffect(() => { - if (!opened) { - updateTopSize(); - } - }, [opened, updateTopSize]); - - if (!topAlert || !topAlert.message) { - return null; - } - - return ( -
- {opened && ( - - -
-
- )} -
- ); -}; diff --git a/src/components/AsideHeader/components/index.ts b/src/components/AsideHeader/components/index.ts index 90c9941..6b3499c 100644 --- a/src/components/AsideHeader/components/index.ts +++ b/src/components/AsideHeader/components/index.ts @@ -1,2 +1 @@ export {FirstPanel} from './FirstPanel'; -export {TopPanel} from './TopPanel'; diff --git a/src/components/MobileHeader/MobileHeader.scss b/src/components/MobileHeader/MobileHeader.scss index 3f375a0..b9fd687 100644 --- a/src/components/MobileHeader/MobileHeader.scss +++ b/src/components/MobileHeader/MobileHeader.scss @@ -9,11 +9,14 @@ $block: '.#{variables.$ns}mobile-header'; background-color: var(--g-color-base-background); - &__header { - background-color: var(--g-color-base-background); - border-bottom: 1px solid var(--g-color-line-generic); + &__top { position: sticky; top: 0; + background-color: var(--g-color-base-background); + } + + &__header { + border-bottom: 1px solid var(--g-color-line-generic); padding: 0 10px; box-sizing: border-box; display: flex; diff --git a/src/components/MobileHeader/MobileHeader.tsx b/src/components/MobileHeader/MobileHeader.tsx index a0b4c86..fc01160 100644 --- a/src/components/MobileHeader/MobileHeader.tsx +++ b/src/components/MobileHeader/MobileHeader.tsx @@ -4,7 +4,7 @@ import {useForwardRef} from '../../hooks/useForwardRef'; import {Content, RenderContentType} from '../Content'; import {Drawer, DrawerItem, DrawerItemProps} from '../Drawer/Drawer'; import {MobileLogo} from '../MobileLogo'; -import {LogoProps} from '../types'; +import {LogoProps, TopAlertProps} from '../types'; import {block} from '../utils/cn'; import {Burger} from './Burger/Burger'; @@ -25,6 +25,10 @@ import {MobileHeaderEvent, MobileHeaderEventOptions, MobileMenuItem} from './typ import './MobileHeader.scss'; +const TopAlert = React.lazy(() => + import('../TopAlert').then((module) => ({default: module.TopAlert})), +); + const b = block('mobile-header'); type PanelName = DrawerItemProps['id'] | null; @@ -44,6 +48,7 @@ export interface MobileHeaderProps { burgerCloseTitle?: string; burgerOpenTitle?: string; panelItems?: PanelItem[]; + topAlert?: TopAlertProps; renderContent?: RenderContentType; sideItemRenderContent?: RenderContentType; onEvent?: (itemName: string, eventName: MobileHeaderEvent) => void; @@ -67,6 +72,7 @@ export const MobileHeader = React.forwardRef( className, contentClassName, overlapPanel, + topAlert, }, ref, ): React.ReactElement => { @@ -261,24 +267,27 @@ export const MobileHeader = React.forwardRef( return (
-
- onPanelToggle(BURGER_PANEL_ITEM_ID)} - className={b('burger')} - closeTitle={burgerCloseTitle} - openTitle={burgerOpenTitle} - /> - +
+ {topAlert && } +
+ onPanelToggle(BURGER_PANEL_ITEM_ID)} + className={b('burger')} + closeTitle={burgerCloseTitle} + openTitle={burgerOpenTitle} + /> + -
{sideItemRenderContent?.({size})}
-
+
{sideItemRenderContent?.({size})}
+
+
{[burgerPanelItem, ...panelItems].map((item) => ( { + const {alertRef, updateTopSize} = useTopAlertHeight({alert}); + + const [opened, setOpened] = React.useState(true); + + const handleClose = React.useCallback(() => { + setOpened(false); + alert?.onCloseTopAlert?.(); + }, [alert]); + + React.useEffect(() => { + if (!opened) { + updateTopSize(); + } + }, [opened, updateTopSize]); + + if (!alert || !alert.message) { + return null; + } + + return ( +
+ {opened && ( + + )} +
+ ); +}; diff --git a/src/components/TopAlert/index.ts b/src/components/TopAlert/index.ts new file mode 100644 index 0000000..f2f13ad --- /dev/null +++ b/src/components/TopAlert/index.ts @@ -0,0 +1 @@ +export {TopAlert} from './TopAlert'; diff --git a/src/components/AsideHeader/useAsideHeaderTopPanel.tsx b/src/components/TopAlert/useTopAlertHeight.ts similarity index 52% rename from src/components/AsideHeader/useAsideHeaderTopPanel.tsx rename to src/components/TopAlert/useTopAlertHeight.ts index 85fb21a..cdf85ef 100644 --- a/src/components/AsideHeader/useAsideHeaderTopPanel.tsx +++ b/src/components/TopAlert/useTopAlertHeight.ts @@ -2,33 +2,17 @@ import React from 'react'; import debounceFn from 'lodash/debounce'; -import {AsideHeaderTopAlertProps} from '../types'; +import {TopAlertProps} from '../types'; type AsideHeaderTopPanel = { - topRef: React.RefObject; + alertRef: React.RefObject; updateTopSize: () => void; }; const G_ROOT_CLASS_NAME = 'g-root'; -const useRefHeight = (ref: React.RefObject) => { - const [topHeight, setTopHeight] = React.useState(0); - React.useEffect(() => { - if (ref.current) { - const {current} = ref; - setTopHeight(current.clientHeight); - } - }, [ref]); - return topHeight; -}; - -export const useAsideHeaderTopPanel = ({ - topAlert, -}: { - topAlert?: AsideHeaderTopAlertProps; -}): AsideHeaderTopPanel => { - const topRef = React.useRef(null); - const topHeight = useRefHeight(topRef); +export const useTopAlertHeight = ({alert}: {alert?: TopAlertProps}): AsideHeaderTopPanel => { + const alertRef = React.useRef(null); const setAsideTopPanelHeight = React.useCallback((clientHeight: number) => { const gRootElement = document @@ -38,15 +22,15 @@ export const useAsideHeaderTopPanel = ({ }, []); const updateTopSize = React.useCallback(() => { - if (topRef.current) { - setAsideTopPanelHeight(topRef.current?.clientHeight || 0); + if (alertRef.current) { + setAsideTopPanelHeight(alertRef.current?.clientHeight || 0); } - }, [topRef, setAsideTopPanelHeight]); + }, [alertRef, setAsideTopPanelHeight]); React.useLayoutEffect(() => { const updateTopSizeDebounce = debounceFn(updateTopSize, 200, {leading: true}); - if (topAlert) { + if (alert) { window.addEventListener('resize', updateTopSizeDebounce); updateTopSizeDebounce(); } @@ -54,10 +38,10 @@ export const useAsideHeaderTopPanel = ({ window.removeEventListener('resize', updateTopSizeDebounce); setAsideTopPanelHeight(0); }; - }, [topAlert, topHeight, topRef, updateTopSize, setAsideTopPanelHeight]); + }, [alert, alertRef, updateTopSize, setAsideTopPanelHeight]); return { - topRef, + alertRef, updateTopSize, }; }; diff --git a/src/components/types.ts b/src/components/types.ts index 155441a..04c6a47 100644 --- a/src/components/types.ts +++ b/src/components/types.ts @@ -76,7 +76,7 @@ export interface LogoProps { 'aria-labelledby'?: string; } -export type AsideHeaderTopAlertProps = { +export interface TopAlertProps { message: AlertProps['message']; title?: AlertProps['title']; icon?: AlertProps['icon']; @@ -88,4 +88,9 @@ export type AsideHeaderTopAlertProps = { centered?: boolean; dense?: boolean; onCloseTopAlert?: () => void; -}; +} + +/** + * @deprecated use TopAlertProps instead + */ +export type AsideHeaderTopAlertProps = TopAlertProps;