@astral/ui 4.65.1 → 4.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/DashboardSidebar/SidebarNav/SidebarNav.js +2 -1
- package/components/DashboardSidebar/SidebarNav/constants.d.ts +3 -0
- package/components/DashboardSidebar/SidebarNav/constants.js +4 -0
- package/components/DashboardSidebar/SidebarNav/index.d.ts +1 -0
- package/components/DashboardSidebar/SidebarNav/index.js +1 -0
- package/components/DashboardSidebar/useLogic/hooks/index.d.ts +1 -0
- package/components/DashboardSidebar/useLogic/hooks/index.js +1 -0
- package/components/DashboardSidebar/useLogic/hooks/useCloseSidebarOnMobile/index.d.ts +1 -0
- package/components/DashboardSidebar/useLogic/hooks/useCloseSidebarOnMobile/index.js +1 -0
- package/components/DashboardSidebar/useLogic/hooks/useCloseSidebarOnMobile/useCloseSidebarOnMobile.d.ts +1 -0
- package/components/DashboardSidebar/useLogic/hooks/useCloseSidebarOnMobile/useCloseSidebarOnMobile.js +30 -0
- package/components/DashboardSidebar/useLogic/useLogic.js +2 -0
- package/components/NavMenu/index.d.ts +1 -0
- package/components/NavMenu/index.js +1 -0
- package/components/NewPopover/NewPopover.d.ts +2 -0
- package/components/NewPopover/NewPopover.js +23 -0
- package/components/NewPopover/constants.d.ts +6 -0
- package/components/NewPopover/constants.js +7 -0
- package/components/NewPopover/index.d.ts +2 -0
- package/components/NewPopover/index.js +1 -0
- package/components/NewPopover/public.d.ts +2 -0
- package/components/NewPopover/public.js +1 -0
- package/components/NewPopover/styles.d.ts +12 -0
- package/components/NewPopover/styles.js +80 -0
- package/components/NewPopover/types.d.ts +53 -0
- package/components/NewPopover/types.js +1 -0
- package/components/NewPopover/useLogic/index.d.ts +1 -0
- package/components/NewPopover/useLogic/index.js +1 -0
- package/components/NewPopover/useLogic/useLogic.d.ts +11 -0
- package/components/NewPopover/useLogic/useLogic.js +54 -0
- package/components/NewPopover/utils/index.d.ts +1 -0
- package/components/NewPopover/utils/index.js +1 -0
- package/components/NewPopover/utils/resolveAnchorNode.d.ts +6 -0
- package/components/NewPopover/utils/resolveAnchorNode.js +8 -0
- package/components/Popover/Popover.d.ts +4 -8
- package/components/Popover/Popover.js +3 -0
- package/components/Popover/index.d.ts +1 -0
- package/components/Popover/index.js +1 -0
- package/components/Popover/public.d.ts +2 -1
- package/components/Popover/public.js +1 -1
- package/components/Popover/types.d.ts +8 -0
- package/components/Popover/types.js +1 -0
- package/components/index.d.ts +0 -1
- package/components/index.js +0 -1
- package/components/useClickAwayEffect/useClickAwayEffect.d.ts +6 -1
- package/components/useClickAwayEffect/useClickAwayEffect.js +19 -9
- package/components/useFocusTrapEffect/index.d.ts +1 -0
- package/components/useFocusTrapEffect/index.js +1 -0
- package/components/useFocusTrapEffect/useFocusTrapEffect.d.ts +17 -0
- package/components/useFocusTrapEffect/useFocusTrapEffect.js +24 -0
- package/node/components/DashboardSidebar/SidebarNav/SidebarNav.js +2 -1
- package/node/components/DashboardSidebar/SidebarNav/constants.d.ts +3 -0
- package/node/components/DashboardSidebar/SidebarNav/constants.js +7 -0
- package/node/components/DashboardSidebar/SidebarNav/index.d.ts +1 -0
- package/node/components/DashboardSidebar/SidebarNav/index.js +3 -0
- package/node/components/DashboardSidebar/useLogic/hooks/index.d.ts +1 -0
- package/node/components/DashboardSidebar/useLogic/hooks/index.js +17 -0
- package/node/components/DashboardSidebar/useLogic/hooks/useCloseSidebarOnMobile/index.d.ts +1 -0
- package/node/components/DashboardSidebar/useLogic/hooks/useCloseSidebarOnMobile/index.js +17 -0
- package/node/components/DashboardSidebar/useLogic/hooks/useCloseSidebarOnMobile/useCloseSidebarOnMobile.d.ts +1 -0
- package/node/components/DashboardSidebar/useLogic/hooks/useCloseSidebarOnMobile/useCloseSidebarOnMobile.js +34 -0
- package/node/components/DashboardSidebar/useLogic/useLogic.js +2 -0
- package/node/components/NavMenu/index.d.ts +1 -0
- package/node/components/NavMenu/index.js +3 -0
- package/node/components/NewPopover/NewPopover.d.ts +2 -0
- package/node/components/NewPopover/NewPopover.js +27 -0
- package/node/components/NewPopover/constants.d.ts +6 -0
- package/node/components/NewPopover/constants.js +10 -0
- package/node/components/NewPopover/index.d.ts +2 -0
- package/node/components/NewPopover/index.js +5 -0
- package/node/components/NewPopover/public.d.ts +2 -0
- package/node/components/NewPopover/public.js +5 -0
- package/node/components/NewPopover/styles.d.ts +12 -0
- package/node/components/NewPopover/styles.js +86 -0
- package/node/components/NewPopover/types.d.ts +53 -0
- package/node/components/NewPopover/types.js +2 -0
- package/node/components/NewPopover/useLogic/index.d.ts +1 -0
- package/node/components/NewPopover/useLogic/index.js +17 -0
- package/node/components/NewPopover/useLogic/useLogic.d.ts +11 -0
- package/node/components/NewPopover/useLogic/useLogic.js +58 -0
- package/node/components/NewPopover/utils/index.d.ts +1 -0
- package/node/components/NewPopover/utils/index.js +17 -0
- package/node/components/NewPopover/utils/resolveAnchorNode.d.ts +6 -0
- package/node/components/NewPopover/utils/resolveAnchorNode.js +12 -0
- package/node/components/Popover/Popover.d.ts +4 -8
- package/node/components/Popover/Popover.js +3 -0
- package/node/components/Popover/index.d.ts +1 -0
- package/node/components/Popover/index.js +1 -0
- package/node/components/Popover/public.d.ts +2 -1
- package/node/components/Popover/public.js +3 -15
- package/node/components/Popover/types.d.ts +8 -0
- package/node/components/Popover/types.js +2 -0
- package/node/components/index.d.ts +0 -1
- package/node/components/index.js +0 -1
- package/node/components/useClickAwayEffect/useClickAwayEffect.d.ts +6 -1
- package/node/components/useClickAwayEffect/useClickAwayEffect.js +19 -9
- package/node/components/useFocusTrapEffect/index.d.ts +1 -0
- package/node/components/useFocusTrapEffect/index.js +17 -0
- package/node/components/useFocusTrapEffect/useFocusTrapEffect.d.ts +17 -0
- package/node/components/useFocusTrapEffect/useFocusTrapEffect.js +28 -0
- package/package.json +3 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from 'react';
|
|
3
|
+
import { sidebarNavClassnames } from './constants';
|
|
3
4
|
export const SidebarNav = forwardRef((props, ref) => {
|
|
4
5
|
const { menu } = props;
|
|
5
|
-
return _jsx("nav", { ref: ref, children: menu });
|
|
6
|
+
return (_jsx("nav", { ref: ref, className: sidebarNavClassnames.root, children: menu }));
|
|
6
7
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useCloseSidebarOnMobile';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useCloseSidebarOnMobile';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useCloseSidebarOnMobile';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useCloseSidebarOnMobile';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useCloseSidebarOnMobile: () => void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useContext, useEffect } from 'react';
|
|
2
|
+
import { DashboardSidebarContext } from '../../../../DashboardSidebarProvider';
|
|
3
|
+
import { navMenuItemButtonClassnames } from '../../../../NavMenu';
|
|
4
|
+
import { useViewportType } from '../../../../useViewportType';
|
|
5
|
+
import { sidebarNavClassnames } from '../../../SidebarNav';
|
|
6
|
+
export const useCloseSidebarOnMobile = () => {
|
|
7
|
+
const { isMobile } = useViewportType();
|
|
8
|
+
const { collapsedIn, onToggleSidebar } = useContext(DashboardSidebarContext);
|
|
9
|
+
const handleMenuListItemClick = () => {
|
|
10
|
+
onToggleSidebar(false);
|
|
11
|
+
};
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!isMobile || !collapsedIn) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const sidebarNavElement = document.querySelector(`.${sidebarNavClassnames.root}`);
|
|
17
|
+
if (!sidebarNavElement) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const listItems = sidebarNavElement.querySelectorAll(`.${navMenuItemButtonClassnames.root}:not(.${navMenuItemButtonClassnames.hasGroup})`);
|
|
21
|
+
listItems.forEach((item) => {
|
|
22
|
+
item.addEventListener('click', handleMenuListItemClick);
|
|
23
|
+
});
|
|
24
|
+
return () => {
|
|
25
|
+
listItems.forEach((item) => {
|
|
26
|
+
item.removeEventListener('click', handleMenuListItemClick);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}, [isMobile, collapsedIn]);
|
|
30
|
+
};
|
|
@@ -7,11 +7,13 @@ import { useTheme } from '../../theme/hooks/useTheme';
|
|
|
7
7
|
import { useViewportType } from '../../useViewportType';
|
|
8
8
|
import { classNames } from '../../utils/classNames';
|
|
9
9
|
import { dashboardSidebarClassnames } from '../constants';
|
|
10
|
+
import { useCloseSidebarOnMobile } from './hooks';
|
|
10
11
|
export const useLogic = (props) => {
|
|
11
12
|
const { className, menu, header, isLoading } = props;
|
|
12
13
|
const { onToggleSidebar, isPinned, onTogglePin, collapsedIn, setIsPopupOpen, } = useContext(DashboardSidebarContext);
|
|
13
14
|
const { alertHeight, isLoading: isDashboardLoading, hasMenuOrganizationRef, hasProfileRef, mobileHeaderPriorityFeature, } = useContext(DashboardContext);
|
|
14
15
|
const { isMobile } = useViewportType();
|
|
16
|
+
useCloseSidebarOnMobile();
|
|
15
17
|
const hoverTimerRef = useRef(null);
|
|
16
18
|
const theme = useTheme();
|
|
17
19
|
const onMouseEnter = () => {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { BottomDrawer } from '../BottomDrawer';
|
|
3
|
+
import { classNames } from '../utils/classNames';
|
|
4
|
+
import { cva } from '../utils/cva';
|
|
5
|
+
import { popoverClassnames } from './constants';
|
|
6
|
+
import { AnimatedWrapper, InnerContainer, StyledMuiPopper } from './styles';
|
|
7
|
+
import { useLogic } from './useLogic';
|
|
8
|
+
const animatedWrapperCva = cva(popoverClassnames.animatedWrapper, {
|
|
9
|
+
variants: {
|
|
10
|
+
isOpen: {
|
|
11
|
+
true: popoverClassnames.animatedWrapperOpen,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
export const NewPopover = ({ children, onClose, isOpen, title, className, style, placement = 'bottom', anchorEl, disablePortal, disableAutoFocus, }) => {
|
|
16
|
+
const { isMobile, isOpened, shouldRender, handleAnimationEnd, handleClose, setContainerRef, } = useLogic({ isOpen, anchorEl, onClose, disableAutoFocus });
|
|
17
|
+
if (isMobile) {
|
|
18
|
+
return (_jsx(BottomDrawer, { title: title, onClose: handleClose, open: isOpened, className: className, style: style, ModalProps: {
|
|
19
|
+
disableAutoFocus,
|
|
20
|
+
}, children: children }));
|
|
21
|
+
}
|
|
22
|
+
return (_jsx(StyledMuiPopper, { className: popoverClassnames.root, open: shouldRender, placement: placement, anchorEl: anchorEl, disablePortal: disablePortal, children: _jsx(AnimatedWrapper, { className: classNames(className, animatedWrapperCva({ isOpen: isOpened })), style: style, onAnimationEnd: handleAnimationEnd, children: _jsx(InnerContainer, { ref: setContainerRef, tabIndex: -1, className: popoverClassnames.innerContainer, children: children }) }) }));
|
|
23
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createUIKitClassname } from '../utils/createUIKitClassname';
|
|
2
|
+
export const popoverClassnames = {
|
|
3
|
+
root: createUIKitClassname('popover'),
|
|
4
|
+
innerContainer: createUIKitClassname('popover__inner-container'),
|
|
5
|
+
animatedWrapper: createUIKitClassname('popover__animated-wrapper'),
|
|
6
|
+
animatedWrapperOpen: createUIKitClassname('popover__animated-wrapper_is-open'),
|
|
7
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { NewPopover } from './NewPopover';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { NewPopover } from './NewPopover';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
export declare const StyledMuiPopper: import("../styled").StyledComponent<import("@mui/material/Popper").PopperProps & import("react").RefAttributes<HTMLDivElement> & {
|
|
3
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
4
|
+
}, {}, {}>;
|
|
5
|
+
export declare const AnimatedWrapper: import("../styled").StyledComponent<{
|
|
6
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
7
|
+
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
8
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
9
|
+
export declare const InnerContainer: import("../styled").StyledComponent<{
|
|
10
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
11
|
+
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
12
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import MuiPopper from '@mui/material/Popper';
|
|
2
|
+
import { styled } from '../styled';
|
|
3
|
+
import { popoverClassnames } from './constants';
|
|
4
|
+
export const StyledMuiPopper = styled(MuiPopper) `
|
|
5
|
+
&.${popoverClassnames.root} {
|
|
6
|
+
z-index: ${({ theme }) => theme.zIndex.tooltip};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
&&[data-popper-placement*='top'] .${popoverClassnames.animatedWrapper} {
|
|
10
|
+
transform-origin: bottom center;
|
|
11
|
+
|
|
12
|
+
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&&[data-popper-placement*='bottom'] .${popoverClassnames.animatedWrapper} {
|
|
16
|
+
transform-origin: top center;
|
|
17
|
+
|
|
18
|
+
margin-top: ${({ theme }) => theme.spacing(2)};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&&[data-popper-placement*='left'] .${popoverClassnames.animatedWrapper} {
|
|
22
|
+
transform-origin: right center;
|
|
23
|
+
|
|
24
|
+
margin-right: ${({ theme }) => theme.spacing(2)};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&&[data-popper-placement*='right'] .${popoverClassnames.animatedWrapper} {
|
|
28
|
+
transform-origin: left center;
|
|
29
|
+
|
|
30
|
+
margin-left: ${({ theme }) => theme.spacing(2)};
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
export const AnimatedWrapper = styled.div `
|
|
34
|
+
animation: popover-grow-out
|
|
35
|
+
${({ theme }) => theme.transitions.duration.enteringScreen}ms
|
|
36
|
+
${({ theme }) => theme.transitions.easing.easeInOut} forwards;
|
|
37
|
+
|
|
38
|
+
&.${popoverClassnames.animatedWrapperOpen} {
|
|
39
|
+
animation: popover-grow-in
|
|
40
|
+
${({ theme }) => theme.transitions.duration.enteringScreen}ms
|
|
41
|
+
${({ theme }) => theme.transitions.easing.easeInOut} forwards;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@keyframes popover-grow-in {
|
|
45
|
+
from {
|
|
46
|
+
transform: scale(0.75, 0.5625);
|
|
47
|
+
|
|
48
|
+
opacity: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
to {
|
|
52
|
+
transform: none;
|
|
53
|
+
|
|
54
|
+
opacity: 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@keyframes popover-grow-out {
|
|
59
|
+
from {
|
|
60
|
+
transform: none;
|
|
61
|
+
|
|
62
|
+
opacity: 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
to {
|
|
66
|
+
transform: scale(0.75, 0.5625);
|
|
67
|
+
|
|
68
|
+
opacity: 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
export const InnerContainer = styled.div `
|
|
73
|
+
&.${popoverClassnames.innerContainer} {
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
|
|
76
|
+
background: ${({ theme }) => theme.palette.background.paper};
|
|
77
|
+
border-radius: ${({ theme }) => theme.shape.medium};
|
|
78
|
+
box-shadow: ${({ theme }) => theme.elevation[200]};
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode, SyntheticEvent } from 'react';
|
|
2
|
+
export type NewPopoverOnClose = (event: Event | SyntheticEvent) => void;
|
|
3
|
+
export type NewPopoverPlacement = 'auto' | 'auto-start' | 'auto-end' | 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'right' | 'right-start' | 'right-end' | 'left' | 'left-start' | 'left-end';
|
|
4
|
+
type NewPopoverVirtualElement = {
|
|
5
|
+
getBoundingClientRect: () => DOMRect;
|
|
6
|
+
};
|
|
7
|
+
export type NewPopoverAnchorEl = null | NewPopoverVirtualElement | HTMLElement | (() => HTMLElement) | (() => NewPopoverVirtualElement);
|
|
8
|
+
export type NewPopoverProps = {
|
|
9
|
+
/**
|
|
10
|
+
* Якорь позиционирования
|
|
11
|
+
*/
|
|
12
|
+
anchorEl?: NewPopoverAnchorEl;
|
|
13
|
+
/**
|
|
14
|
+
* Показывать popover. Если не передан — видимость определяется наличием anchorEl.
|
|
15
|
+
*/
|
|
16
|
+
isOpen?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Позиция относительно anchor
|
|
19
|
+
* @default 'bottom'
|
|
20
|
+
*/
|
|
21
|
+
placement?: NewPopoverPlacement;
|
|
22
|
+
/**
|
|
23
|
+
* Контент popover
|
|
24
|
+
*/
|
|
25
|
+
children?: ReactNode;
|
|
26
|
+
/**
|
|
27
|
+
* className корневого popper
|
|
28
|
+
*/
|
|
29
|
+
className?: string;
|
|
30
|
+
/**
|
|
31
|
+
* style корневого popper
|
|
32
|
+
*/
|
|
33
|
+
style?: CSSProperties;
|
|
34
|
+
/**
|
|
35
|
+
* Заголовок для отображения в мобильной версии
|
|
36
|
+
*/
|
|
37
|
+
title?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Обработчик закрытия компонента
|
|
40
|
+
*/
|
|
41
|
+
onClose?: NewPopoverOnClose;
|
|
42
|
+
/**
|
|
43
|
+
* Рендерить popper внутри родителя, без portal
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
disablePortal?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Отключить автофокус при открытии popover (десктоп)
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
disableAutoFocus?: boolean;
|
|
52
|
+
};
|
|
53
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useLogic';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useLogic';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type NewPopoverProps } from '../types';
|
|
2
|
+
type UseLogicParams = Pick<NewPopoverProps, 'isOpen' | 'anchorEl' | 'onClose' | 'disableAutoFocus'>;
|
|
3
|
+
export declare const useLogic: ({ isOpen, anchorEl, onClose, disableAutoFocus, }: UseLogicParams) => {
|
|
4
|
+
isMobile: boolean;
|
|
5
|
+
isOpened: boolean;
|
|
6
|
+
shouldRender: boolean;
|
|
7
|
+
handleAnimationEnd: () => void;
|
|
8
|
+
handleClose: (event: Event) => void;
|
|
9
|
+
setContainerRef: (node: HTMLDivElement | null) => void;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useClickAwayEffect } from '../../useClickAwayEffect';
|
|
3
|
+
import { useEscapeClickEffect } from '../../useEscapeClickEffect';
|
|
4
|
+
import { useFocusTrapEffect } from '../../useFocusTrapEffect';
|
|
5
|
+
import { usePopoverAnimation } from '../../usePopoverAnimation';
|
|
6
|
+
import { useViewportType } from '../../useViewportType';
|
|
7
|
+
import { resolveAnchorNode } from '../utils';
|
|
8
|
+
export const useLogic = ({ isOpen, anchorEl, onClose, disableAutoFocus, }) => {
|
|
9
|
+
const containerRef = useRef(null);
|
|
10
|
+
const [isContainerMounted, setIsContainerMounted] = useState(false);
|
|
11
|
+
const setContainerRef = useCallback((node) => {
|
|
12
|
+
containerRef.current = node;
|
|
13
|
+
setIsContainerMounted(Boolean(node));
|
|
14
|
+
}, []);
|
|
15
|
+
const isOpened = isOpen ?? Boolean(anchorEl);
|
|
16
|
+
const isControlled = isOpen !== undefined;
|
|
17
|
+
const { isMobile } = useViewportType();
|
|
18
|
+
const { shouldRender, handleAnimationEnd } = usePopoverAnimation(isOpened);
|
|
19
|
+
const isEffectActive = !isMobile && isOpened && isContainerMounted;
|
|
20
|
+
const anchorNode = useMemo(() => resolveAnchorNode(anchorEl), [anchorEl]);
|
|
21
|
+
const excludeNodes = useMemo(() => [anchorNode], [anchorNode]);
|
|
22
|
+
const handleClose = (event) => {
|
|
23
|
+
onClose?.(event);
|
|
24
|
+
};
|
|
25
|
+
useClickAwayEffect({
|
|
26
|
+
ref: containerRef,
|
|
27
|
+
onClickAway: handleClose,
|
|
28
|
+
isActive: isEffectActive && !isControlled,
|
|
29
|
+
preventBubbling: true,
|
|
30
|
+
excludeNodes,
|
|
31
|
+
});
|
|
32
|
+
useEscapeClickEffect({
|
|
33
|
+
onEscape: handleClose,
|
|
34
|
+
isActive: isEffectActive,
|
|
35
|
+
preventBubbling: true,
|
|
36
|
+
});
|
|
37
|
+
useFocusTrapEffect({
|
|
38
|
+
ref: containerRef,
|
|
39
|
+
isActive: isEffectActive && !disableAutoFocus,
|
|
40
|
+
returnFocusOnDeactivate: true,
|
|
41
|
+
escapeDeactivates: false,
|
|
42
|
+
clickOutsideDeactivates: false,
|
|
43
|
+
allowOutsideClick: true,
|
|
44
|
+
fallbackFocus: () => containerRef.current || document.body,
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
isMobile,
|
|
48
|
+
isOpened,
|
|
49
|
+
shouldRender,
|
|
50
|
+
handleAnimationEnd,
|
|
51
|
+
handleClose,
|
|
52
|
+
setContainerRef,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './resolveAnchorNode';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './resolveAnchorNode';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Резолвит anchorEl в DOM-ноду, если это возможно.
|
|
3
|
+
* Для virtual-элементов вернёт null.
|
|
4
|
+
*/
|
|
5
|
+
export const resolveAnchorNode = (anchorEl) => {
|
|
6
|
+
const value = typeof anchorEl === 'function' ? anchorEl() : anchorEl;
|
|
7
|
+
return value instanceof Node ? value : null;
|
|
8
|
+
};
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import type { PopoverProps
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Заголовок для отображения в мобильной версии
|
|
6
|
-
*/
|
|
7
|
-
title?: string;
|
|
8
|
-
};
|
|
1
|
+
import type { PopoverProps } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated Причина отказа от поддержки: MUI Popover использует backdrop, который перехватывает первый внешний клик и создает UX-эффект "двойного клика". Используйте компонент `NewPopover`.
|
|
4
|
+
*/
|
|
9
5
|
export declare const Popover: ({ children, onClose, open, title, className, ...restProps }: PopoverProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,6 +2,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { BottomDrawer } from '../BottomDrawer';
|
|
3
3
|
import { useViewportType } from '../useViewportType';
|
|
4
4
|
import { StyledMuiPopover } from './styles';
|
|
5
|
+
/**
|
|
6
|
+
* @deprecated Причина отказа от поддержки: MUI Popover использует backdrop, который перехватывает первый внешний клик и создает UX-эффект "двойного клика". Используйте компонент `NewPopover`.
|
|
7
|
+
*/
|
|
5
8
|
export const Popover = ({ children, onClose, open, title, className, ...restProps }) => {
|
|
6
9
|
const { isMobile } = useViewportType();
|
|
7
10
|
if (isMobile) {
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { Popover } from './Popover';
|
|
2
|
+
export type { PopoverProps } from './types';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { Popover } from './Popover';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PopoverProps as MuiPopoverProps } from '@mui/material/Popover';
|
|
2
|
+
import { type WithoutEmotionSpecific } from '../types/WithoutEmotionSpecific';
|
|
3
|
+
export type PopoverProps = WithoutEmotionSpecific<MuiPopoverProps> & {
|
|
4
|
+
/**
|
|
5
|
+
* Заголовок для отображения в мобильной версии
|
|
6
|
+
*/
|
|
7
|
+
title?: string;
|
|
8
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/components/index.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ export * from './Checkbox';
|
|
|
20
20
|
export * from './CheckboxField';
|
|
21
21
|
export * from './Chevron';
|
|
22
22
|
export * from './ClickAwayListener';
|
|
23
|
-
export * from './ClickAwayListener';
|
|
24
23
|
export { CodeField, type CodeFieldProps } from './CodeField';
|
|
25
24
|
export * from './CollapsableAlert';
|
|
26
25
|
export * from './Collapse';
|
package/components/index.js
CHANGED
|
@@ -20,7 +20,6 @@ export * from './Checkbox';
|
|
|
20
20
|
export * from './CheckboxField';
|
|
21
21
|
export * from './Chevron';
|
|
22
22
|
export * from './ClickAwayListener';
|
|
23
|
-
export * from './ClickAwayListener';
|
|
24
23
|
export { CodeField } from './CodeField';
|
|
25
24
|
export * from './CollapsableAlert';
|
|
26
25
|
export * from './Collapse';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type RefObject } from 'react';
|
|
2
2
|
import { type CloseEventReason } from '../types/CloseEventReason';
|
|
3
|
+
export type ExcludedNode = Node | RefObject<Node | null> | null | undefined;
|
|
3
4
|
export type UseClickAwayListenerOptions = {
|
|
4
5
|
/**
|
|
5
6
|
* реф на дом ноду, клик вне которой надо отслеживать
|
|
@@ -17,9 +18,13 @@ export type UseClickAwayListenerOptions = {
|
|
|
17
18
|
* флаг необходимости предотвращать всплытие, подойдет когда используется внутри модалки
|
|
18
19
|
*/
|
|
19
20
|
preventBubbling?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Ноды (или рефы), клик по которым не приводит к срабатыванию onClickAway
|
|
23
|
+
*/
|
|
24
|
+
excludeNodes?: ExcludedNode[];
|
|
20
25
|
};
|
|
21
26
|
/**
|
|
22
27
|
* хук позволяющий подписаться на клик вне указанного рефа,
|
|
23
28
|
* подойдет для использования в кастомных попперах
|
|
24
29
|
*/
|
|
25
|
-
export declare const useClickAwayEffect: ({ ref, onClickAway, preventBubbling, isActive, }: UseClickAwayListenerOptions) => void;
|
|
30
|
+
export declare const useClickAwayEffect: ({ ref, onClickAway, preventBubbling, isActive, excludeNodes, }: UseClickAwayListenerOptions) => void;
|
|
@@ -1,26 +1,36 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
+
const resolveNode = (item) => {
|
|
3
|
+
if (!item) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
return item instanceof Node ? item : item.current;
|
|
7
|
+
};
|
|
2
8
|
/**
|
|
3
9
|
* хук позволяющий подписаться на клик вне указанного рефа,
|
|
4
10
|
* подойдет для использования в кастомных попперах
|
|
5
11
|
*/
|
|
6
|
-
export const useClickAwayEffect = ({ ref, onClickAway, preventBubbling, isActive, }) => {
|
|
12
|
+
export const useClickAwayEffect = ({ ref, onClickAway, preventBubbling, isActive, excludeNodes, }) => {
|
|
7
13
|
useEffect(() => {
|
|
8
14
|
const node = ref?.current;
|
|
9
15
|
if (!isActive || !node) {
|
|
10
16
|
return;
|
|
11
17
|
}
|
|
12
|
-
const onClick = (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
const onClick = (event) => {
|
|
19
|
+
const target = event.target;
|
|
20
|
+
const isClickInside = node.contains(target);
|
|
21
|
+
const isClickOnExcluded = excludeNodes?.some((item) => resolveNode(item)?.contains(target));
|
|
22
|
+
const isClickAway = !isClickInside && !isClickOnExcluded;
|
|
23
|
+
if (preventBubbling) {
|
|
24
|
+
event.stopPropagation();
|
|
25
|
+
event.stopImmediatePropagation();
|
|
26
|
+
}
|
|
27
|
+
if (isClickAway) {
|
|
28
|
+
onClickAway(event, 'clickAway');
|
|
19
29
|
}
|
|
20
30
|
};
|
|
21
31
|
window.addEventListener('pointerdown', onClick);
|
|
22
32
|
return () => {
|
|
23
33
|
window.removeEventListener('pointerdown', onClick);
|
|
24
34
|
};
|
|
25
|
-
}, [isActive, ref]);
|
|
35
|
+
}, [isActive, ref, excludeNodes, onClickAway, preventBubbling]);
|
|
26
36
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useFocusTrapEffect';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useFocusTrapEffect';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Options } from 'focus-trap';
|
|
2
|
+
import { type RefObject } from 'react';
|
|
3
|
+
export type UseFocusTrapEffectOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Реф на DOM-ноду контейнера ловушки фокуса
|
|
6
|
+
*/
|
|
7
|
+
ref: RefObject<HTMLElement | null>;
|
|
8
|
+
/**
|
|
9
|
+
* Флаг активности ловушки
|
|
10
|
+
*/
|
|
11
|
+
isActive: boolean;
|
|
12
|
+
} & Pick<Options, 'initialFocus' | 'fallbackFocus' | 'escapeDeactivates' | 'clickOutsideDeactivates' | 'returnFocusOnDeactivate' | 'allowOutsideClick' | 'preventScroll' | 'delayInitialFocus'>;
|
|
13
|
+
/**
|
|
14
|
+
* Хук для удержания фокуса внутри указанного контейнера.
|
|
15
|
+
* Опции соответствуют конфигурации focus-trap.
|
|
16
|
+
*/
|
|
17
|
+
export declare const useFocusTrapEffect: ({ ref, isActive, ...options }: UseFocusTrapEffectOptions) => void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createFocusTrap } from 'focus-trap';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Хук для удержания фокуса внутри указанного контейнера.
|
|
5
|
+
* Опции соответствуют конфигурации focus-trap.
|
|
6
|
+
*/
|
|
7
|
+
export const useFocusTrapEffect = ({ ref, isActive, ...options }) => {
|
|
8
|
+
const trapRef = useRef(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const node = ref.current;
|
|
11
|
+
if (!isActive || !node) {
|
|
12
|
+
trapRef.current?.deactivate();
|
|
13
|
+
trapRef.current = null;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const trap = createFocusTrap(node, options);
|
|
17
|
+
trapRef.current = trap;
|
|
18
|
+
trap.activate();
|
|
19
|
+
return () => {
|
|
20
|
+
trap.deactivate();
|
|
21
|
+
trapRef.current = null;
|
|
22
|
+
};
|
|
23
|
+
}, [isActive, ref]);
|
|
24
|
+
};
|
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SidebarNav = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
|
+
const constants_1 = require("./constants");
|
|
6
7
|
exports.SidebarNav = (0, react_1.forwardRef)((props, ref) => {
|
|
7
8
|
const { menu } = props;
|
|
8
|
-
return (0, jsx_runtime_1.jsx)("nav", { ref: ref, children: menu });
|
|
9
|
+
return ((0, jsx_runtime_1.jsx)("nav", { ref: ref, className: constants_1.sidebarNavClassnames.root, children: menu }));
|
|
9
10
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sidebarNavClassnames = void 0;
|
|
4
|
+
const createUIKitClassname_1 = require("../../utils/createUIKitClassname");
|
|
5
|
+
exports.sidebarNavClassnames = {
|
|
6
|
+
root: (0, createUIKitClassname_1.createUIKitClassname)('dashboard-layout-sidebar__navigation'),
|
|
7
|
+
};
|