@astral/ui 4.102.0 → 4.103.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.
Files changed (57) hide show
  1. package/components/Dialog/Dialog.d.ts +1 -1
  2. package/components/Dialog/Dialog.js +5 -4
  3. package/components/Dialog/styles.js +8 -0
  4. package/components/Dialog/types.d.ts +5 -0
  5. package/components/Dialog/useLogic/useLogic.d.ts +3 -3
  6. package/components/Dialog/useLogic/useLogic.js +6 -2
  7. package/components/HintPopover/styles.js +2 -1
  8. package/components/NewPopover/NewPopover.js +1 -0
  9. package/components/NewPopover/constants.d.ts +5 -0
  10. package/components/NewPopover/constants.js +6 -1
  11. package/components/NewPopover/styles.js +2 -1
  12. package/components/NewPopover/types.d.ts +1 -1
  13. package/components/NewPopover/useLogic/useLogic.d.ts +2 -2
  14. package/components/NewPopover/useLogic/useLogic.js +12 -12
  15. package/components/NewPopover/useLogic/utils/getFallbackPlacements/getFallbackPlacements.d.ts +7 -0
  16. package/components/NewPopover/useLogic/utils/getFallbackPlacements/getFallbackPlacements.js +19 -0
  17. package/components/NewPopover/useLogic/utils/getFallbackPlacements/index.d.ts +1 -0
  18. package/components/NewPopover/useLogic/utils/getFallbackPlacements/index.js +1 -0
  19. package/components/NewPopover/useLogic/utils/index.d.ts +4 -0
  20. package/components/NewPopover/useLogic/utils/index.js +4 -0
  21. package/components/Onboarding/types/step.d.ts +1 -1
  22. package/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.d.ts +1 -1
  23. package/components/Onboarding/ui/StepStrategy/steps/StandaloneStep/StandaloneStep.js +1 -1
  24. package/components/Onboarding/ui/StepStrategy/steps/StandaloneStep/styles.d.ts +1 -0
  25. package/components/Onboarding/ui/hooks/useTrackedRect/constants.d.ts +6 -0
  26. package/components/Onboarding/ui/hooks/useTrackedRect/constants.js +6 -0
  27. package/components/Onboarding/ui/hooks/useTrackedRect/useTrackedRect.js +34 -12
  28. package/hook-form/FormFilters/FormFiltersDialog/styles.d.ts +1 -0
  29. package/node/components/Dialog/Dialog.d.ts +1 -1
  30. package/node/components/Dialog/Dialog.js +5 -4
  31. package/node/components/Dialog/styles.js +8 -0
  32. package/node/components/Dialog/types.d.ts +5 -0
  33. package/node/components/Dialog/useLogic/useLogic.d.ts +3 -3
  34. package/node/components/Dialog/useLogic/useLogic.js +6 -2
  35. package/node/components/HintPopover/styles.js +2 -1
  36. package/node/components/NewPopover/NewPopover.js +1 -0
  37. package/node/components/NewPopover/constants.d.ts +5 -0
  38. package/node/components/NewPopover/constants.js +7 -2
  39. package/node/components/NewPopover/styles.js +1 -0
  40. package/node/components/NewPopover/types.d.ts +1 -1
  41. package/node/components/NewPopover/useLogic/useLogic.d.ts +2 -2
  42. package/node/components/NewPopover/useLogic/useLogic.js +15 -15
  43. package/node/components/NewPopover/useLogic/utils/getFallbackPlacements/getFallbackPlacements.d.ts +7 -0
  44. package/node/components/NewPopover/useLogic/utils/getFallbackPlacements/getFallbackPlacements.js +23 -0
  45. package/node/components/NewPopover/useLogic/utils/getFallbackPlacements/index.d.ts +1 -0
  46. package/node/components/NewPopover/useLogic/utils/getFallbackPlacements/index.js +17 -0
  47. package/node/components/NewPopover/useLogic/utils/index.d.ts +4 -0
  48. package/node/components/NewPopover/useLogic/utils/index.js +20 -0
  49. package/node/components/Onboarding/types/step.d.ts +1 -1
  50. package/node/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.d.ts +1 -1
  51. package/node/components/Onboarding/ui/StepStrategy/steps/StandaloneStep/StandaloneStep.js +1 -1
  52. package/node/components/Onboarding/ui/StepStrategy/steps/StandaloneStep/styles.d.ts +1 -0
  53. package/node/components/Onboarding/ui/hooks/useTrackedRect/constants.d.ts +6 -0
  54. package/node/components/Onboarding/ui/hooks/useTrackedRect/constants.js +9 -0
  55. package/node/components/Onboarding/ui/hooks/useTrackedRect/useTrackedRect.js +34 -12
  56. package/node/hook-form/FormFilters/FormFiltersDialog/styles.d.ts +1 -0
  57. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
1
  import type { DialogProps } from './types';
2
- export declare const Dialog: ({ children, title, disableBackdropClick, onClose, size, headerContent, className, classes, ...props }: DialogProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const Dialog: ({ children, title, disableBackdropClick, onClose, size, headerContent, className, classes, disableMobileDrawer, ...props }: DialogProps) => import("react/jsx-runtime").JSX.Element;
@@ -13,14 +13,15 @@ const dialogCva = cva(dialogClassnames.root, {
13
13
  },
14
14
  },
15
15
  });
16
- export const Dialog = ({ children, title, disableBackdropClick, onClose, size = 'md', headerContent, className, classes, ...props }) => {
17
- const { handleClose, isMobile, dialogClasses } = useLogic({
16
+ export const Dialog = ({ children, title, disableBackdropClick, onClose, size = 'md', headerContent, className, classes, disableMobileDrawer, ...props }) => {
17
+ const { handleClose, shouldRenderDrawer, dialogClasses } = useLogic({
18
18
  onClose,
19
19
  disableBackdropClick,
20
20
  classes,
21
+ disableMobileDrawer,
21
22
  });
22
- if (isMobile) {
23
- return (_jsx(BottomDrawer, { onClose: handleClose, title: title, headerContent: headerContent, ...props, children: children }));
23
+ if (shouldRenderDrawer) {
24
+ return (_jsx(BottomDrawer, { onClose: handleClose, title: title, headerContent: headerContent, className: className, ...props, children: children }));
24
25
  }
25
26
  return (_jsxs(StyledDialog, { "$size": size, onClose: handleClose, className: cn(dialogCva({ withoutTitle: !title }), className), classes: dialogClasses, ...props, children: [title && (_jsx(DialogTitle, { onClose: onClose, headerContent: headerContent, children: title })), children] }));
26
27
  };
@@ -28,6 +28,14 @@ export const StyledDialog = styled(MuiDialog, {
28
28
  box-shadow: ${({ theme }) => theme.elevation[300]};
29
29
  }
30
30
 
31
+ ${({ theme }) => theme.breakpoints.down('mobile')} {
32
+ .${dialogClassnames.paper} {
33
+ width: calc(100% - ${({ theme }) => theme.spacing(8)});
34
+ min-width: auto;
35
+ margin: ${({ theme }) => theme.spacing(4)};
36
+ }
37
+ }
38
+
31
39
  /* Причина игнора: Не критично для отображения */
32
40
  /* stylelint-disable-next-line plugin/no-unsupported-browser-features */
33
41
  &:not(:has(.${dialogActionsClassnames.root})) .${dialogContentClassnames.root} {
@@ -20,4 +20,9 @@ export type DialogProps = WithoutEmotionSpecific<Omit<MuiDialogProps, 'title' |
20
20
  * Дополнительный контент хедера
21
21
  */
22
22
  headerContent?: ReactNode;
23
+ /**
24
+ * Отключить BottomDrawer на mobile: диалог остаётся центрированным модальным окном.
25
+ * @default false
26
+ */
27
+ disableMobileDrawer?: boolean;
23
28
  };
@@ -1,9 +1,9 @@
1
1
  import { type MouseEvent } from 'react';
2
2
  import { type DialogProps } from '../types';
3
- type UseLogicParams = Pick<DialogProps, 'onClose' | 'disableBackdropClick' | 'classes'>;
4
- export declare const useLogic: ({ onClose, disableBackdropClick, classes, }: UseLogicParams) => {
3
+ type UseLogicParams = Pick<DialogProps, 'onClose' | 'disableBackdropClick' | 'classes' | 'disableMobileDrawer'>;
4
+ export declare const useLogic: ({ onClose, disableBackdropClick, classes, disableMobileDrawer, }: UseLogicParams) => {
5
5
  handleClose: ((event: MouseEvent<HTMLButtonElement>, reason: 'backdropClick' | 'escapeKeyDown') => void) | undefined;
6
- isMobile: boolean;
6
+ shouldRenderDrawer: boolean;
7
7
  dialogClasses: {
8
8
  root?: string | undefined;
9
9
  scrollPaper?: string | undefined;
@@ -1,7 +1,7 @@
1
1
  import { useViewportType } from '../../useViewportType';
2
2
  import { cn } from '../../utils/cn';
3
3
  import { dialogClassnames } from '../constants';
4
- export const useLogic = ({ onClose, disableBackdropClick, classes, }) => {
4
+ export const useLogic = ({ onClose, disableBackdropClick, classes, disableMobileDrawer, }) => {
5
5
  const { isMobile } = useViewportType();
6
6
  const handleClose = onClose &&
7
7
  ((event, reason) => {
@@ -15,5 +15,9 @@ export const useLogic = ({ onClose, disableBackdropClick, classes, }) => {
15
15
  paper: cn(dialogClassnames.paper, paper),
16
16
  ...restClasses,
17
17
  };
18
- return { handleClose, isMobile, dialogClasses };
18
+ return {
19
+ handleClose,
20
+ shouldRenderDrawer: isMobile && !disableMobileDrawer,
21
+ dialogClasses,
22
+ };
19
23
  };
@@ -22,7 +22,8 @@ export const Body = styled.div `
22
22
  display: flex;
23
23
  flex-direction: column;
24
24
 
25
- width: min(400px, calc(100vw - ${({ theme }) => theme.spacing(8)}));
25
+ /* Отступ popover от края вьюпорта — иначе поля по краям разъедутся. */
26
+ width: min(400px, calc(100vw - var(--popover-viewport-padding, 16px) * 2));
26
27
  padding: ${({ theme }) => theme.spacing(6)};
27
28
 
28
29
  ${({ theme }) => theme.breakpoints.down('mobile')} {
@@ -23,6 +23,7 @@ export const NewPopover = ({ children, onClose, isOpen, title, className, style,
23
23
  disableAutoFocus,
24
24
  hasArrow,
25
25
  disableMobileDrawer,
26
+ placement,
26
27
  });
27
28
  if (shouldRenderDrawer) {
28
29
  return (_jsx(BottomDrawer, { title: title, onClose: handleClose, open: isOpened, className: className, style: style, ModalProps: {
@@ -18,6 +18,11 @@ export declare const ARROW_PADDING = 24;
18
18
  export declare const ARROW_MIN_PADDING = 16;
19
19
  /** Мин. «ход» стрелки вдоль кромки (px). */
20
20
  export declare const ARROW_MIN_TRACK = 16;
21
+ /**
22
+ * Мин. отступ popover от края вьюпорта (px).
23
+ * Наружу отдаётся CSS-переменной `--popover-viewport-padding`.
24
+ */
25
+ export declare const VIEWPORT_MIN_PADDING = 16;
21
26
  /**
22
27
  * Базовые модификаторы Popper.js. У MUI Popper (popper.js)
23
28
  */
@@ -18,12 +18,17 @@ export const ARROW_PADDING = 24;
18
18
  export const ARROW_MIN_PADDING = 16;
19
19
  /** Мин. «ход» стрелки вдоль кромки (px). */
20
20
  export const ARROW_MIN_TRACK = 16;
21
+ /**
22
+ * Мин. отступ popover от края вьюпорта (px).
23
+ * Наружу отдаётся CSS-переменной `--popover-viewport-padding`.
24
+ */
25
+ export const VIEWPORT_MIN_PADDING = 16;
21
26
  /**
22
27
  * Базовые модификаторы Popper.js. У MUI Popper (popper.js)
23
28
  */
24
29
  export const DEFAULT_MODIFIERS = [
25
30
  {
26
31
  name: 'preventOverflow',
27
- options: { altAxis: true, tether: false, padding: 8 },
32
+ options: { altAxis: true, tether: false, padding: VIEWPORT_MIN_PADDING },
28
33
  },
29
34
  ];
@@ -1,10 +1,11 @@
1
1
  import MuiPopper from '@mui/material/Popper';
2
2
  import { styled } from '../styled';
3
- import { ARROW_BASE, ARROW_HEIGHT, popoverClassnames } from './constants';
3
+ import { ARROW_BASE, ARROW_HEIGHT, popoverClassnames, VIEWPORT_MIN_PADDING, } from './constants';
4
4
  export const StyledMuiPopper = styled(MuiPopper) `
5
5
  &.${popoverClassnames.root} {
6
6
  --arrow-base: ${ARROW_BASE}px;
7
7
  --arrow-height: ${ARROW_HEIGHT}px;
8
+ --popover-viewport-padding: ${VIEWPORT_MIN_PADDING}px;
8
9
 
9
10
  z-index: ${({ theme }) => theme.zIndex.modal};
10
11
  }
@@ -23,7 +23,7 @@ export type NewPopoverProps = {
23
23
  */
24
24
  isOpen?: boolean;
25
25
  /**
26
- * Позиция относительно anchor
26
+ * Позиция относительно anchor.
27
27
  * @default 'bottom'
28
28
  */
29
29
  placement?: NewPopoverPlacement;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import { type NewPopoverModifier, type NewPopoverProps } from '../types';
3
- type UseLogicParams = Pick<NewPopoverProps, 'isOpen' | 'anchorEl' | 'onClose' | 'disableAutoFocus' | 'hasArrow' | 'disableMobileDrawer'>;
4
- export declare const useLogic: ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disableMobileDrawer, }: UseLogicParams) => {
3
+ type UseLogicParams = Pick<NewPopoverProps, 'isOpen' | 'anchorEl' | 'onClose' | 'disableAutoFocus' | 'hasArrow' | 'disableMobileDrawer'> & Required<Pick<NewPopoverProps, 'placement'>>;
4
+ export declare const useLogic: ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disableMobileDrawer, placement, }: UseLogicParams) => {
5
5
  shouldRenderDrawer: boolean;
6
6
  isOpened: boolean;
7
7
  shouldRender: boolean;
@@ -5,10 +5,8 @@ import { useFocusTrapEffect } from '../../useFocusTrapEffect';
5
5
  import { usePopoverAnimation } from '../../usePopoverAnimation';
6
6
  import { useViewportType } from '../../useViewportType';
7
7
  import { DEFAULT_MODIFIERS } from '../constants';
8
- import { getArrowPadding } from './utils/getArrowPadding';
9
- import { getPopoverCssVars } from './utils/getPopoverCssVars';
10
- import { resolveAnchorNode } from './utils/resolveAnchorNode';
11
- export const useLogic = ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disableMobileDrawer, }) => {
8
+ import { getArrowPadding, getFallbackPlacements, getPopoverCssVars, resolveAnchorNode, } from './utils';
9
+ export const useLogic = ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disableMobileDrawer, placement, }) => {
12
10
  const containerRef = useRef(null);
13
11
  const [isContainerMounted, setIsContainerMounted] = useState(false);
14
12
  const [arrowElement, setArrowElement] = useState(null);
@@ -50,18 +48,20 @@ export const useLogic = ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow
50
48
  fallbackFocus: () => containerRef.current || document.body,
51
49
  });
52
50
  const mergedModifiers = useMemo(() => {
53
- if (!hasArrow) {
54
- return DEFAULT_MODIFIERS;
51
+ const modifiers = [...DEFAULT_MODIFIERS];
52
+ const fallbackPlacements = getFallbackPlacements(placement);
53
+ if (fallbackPlacements) {
54
+ modifiers.push({ name: 'flip', options: { fallbackPlacements } });
55
55
  }
56
- return [
57
- ...DEFAULT_MODIFIERS,
58
- {
56
+ if (hasArrow) {
57
+ modifiers.push({
59
58
  name: 'arrow',
60
59
  enabled: true,
61
60
  options: { element: arrowElement, padding: getArrowPadding },
62
- },
63
- ];
64
- }, [hasArrow, arrowElement]);
61
+ });
62
+ }
63
+ return modifiers;
64
+ }, [hasArrow, arrowElement, placement]);
65
65
  return {
66
66
  shouldRenderDrawer,
67
67
  isOpened,
@@ -0,0 +1,7 @@
1
+ import { type NewPopoverPlacement } from '../../../types';
2
+ /**
3
+ * Значение fallbackPlacements для модификатора flip: заданная сторона не подошла —
4
+ * берётся следующая по списку. Для вертикальной стороны и `auto` список не задаётся,
5
+ * flip справляется сам.
6
+ */
7
+ export declare const getFallbackPlacements: (placement: NewPopoverPlacement) => NewPopoverPlacement[] | undefined;
@@ -0,0 +1,19 @@
1
+ const OPPOSITE_SIDE = {
2
+ left: 'right',
3
+ right: 'left',
4
+ };
5
+ const VERTICAL_SIDES = ['bottom', 'top'];
6
+ const checkIsSidePlacement = (side) => side in OPPOSITE_SIDE;
7
+ /**
8
+ * Значение fallbackPlacements для модификатора flip: заданная сторона не подошла —
9
+ * берётся следующая по списку. Для вертикальной стороны и `auto` список не задаётся,
10
+ * flip справляется сам.
11
+ */
12
+ export const getFallbackPlacements = (placement) => {
13
+ const [side, alignment] = placement.split('-');
14
+ if (!checkIsSidePlacement(side)) {
15
+ return undefined;
16
+ }
17
+ const suffix = alignment ? `-${alignment}` : '';
18
+ return [OPPOSITE_SIDE[side], ...VERTICAL_SIDES].map((fallbackSide) => `${fallbackSide}${suffix}`);
19
+ };
@@ -0,0 +1 @@
1
+ export * from './getFallbackPlacements';
@@ -0,0 +1 @@
1
+ export * from './getFallbackPlacements';
@@ -0,0 +1,4 @@
1
+ export * from './getArrowPadding';
2
+ export * from './getFallbackPlacements';
3
+ export * from './getPopoverCssVars';
4
+ export * from './resolveAnchorNode';
@@ -0,0 +1,4 @@
1
+ export * from './getArrowPadding';
2
+ export * from './getFallbackPlacements';
3
+ export * from './getPopoverCssVars';
4
+ export * from './resolveAnchorNode';
@@ -45,7 +45,7 @@ export type Step = {
45
45
  */
46
46
  type: StepType;
47
47
  /**
48
- * Желаемая позиция popover (только для highlight).
48
+ * Желаемая позиция popover относительно подсвечиваемого элемента (только для highlight).
49
49
  * @default 'bottom'
50
50
  */
51
51
  position?: StepPosition;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- export declare const StepPopover: import("../../../../../styled").StyledComponent<Omit<import("../../../../../NewPopover").NewPopoverProps, "title" | "hasArrow" | "disableMobileDrawer"> & {
2
+ export declare const StepPopover: import("../../../../../styled").StyledComponent<Omit<import("../../../../../NewPopover").NewPopoverProps, "title" | "disableMobileDrawer" | "hasArrow"> & {
3
3
  variant?: import("../../../../../HintPopover").HintPopoverVariant | undefined;
4
4
  title?: import("react").ReactNode;
5
5
  action?: import("react").ReactNode;
@@ -7,5 +7,5 @@ import { Footer, StepDialog } from './styles';
7
7
  */
8
8
  export const StandaloneStep = ({ activeStep, isClosing, }) => {
9
9
  const { step, isConfirming, requestClose } = activeStep;
10
- return (_jsxs(StepDialog, { open: !isConfirming && !isClosing, title: step.title, onClose: requestClose, size: step.dialogSize, disableAutoFocus: true, children: [_jsx(DialogContent, { children: step.content }), _jsx(Footer, { children: _jsx(StepFooter, { activeStep: activeStep }) })] }));
10
+ return (_jsxs(StepDialog, { open: !isConfirming && !isClosing, title: step.title, onClose: requestClose, size: step.dialogSize, disableAutoFocus: true, disableMobileDrawer: true, children: [_jsx(DialogContent, { children: step.content }), _jsx(Footer, { children: _jsx(StepFooter, { activeStep: activeStep }) })] }));
11
11
  };
@@ -4,6 +4,7 @@ export declare const StepDialog: import("../../../../../styled").StyledComponent
4
4
  disableBackdropClick?: boolean | undefined;
5
5
  size?: import("../../../../../Dialog").DialogSize | undefined;
6
6
  headerContent?: import("react").ReactNode;
7
+ disableMobileDrawer?: boolean | undefined;
7
8
  } & {
8
9
  theme?: import("@emotion/react").Theme | undefined;
9
10
  }, {}, {}>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Сколько миллисекунд без изменений области считать покоем и останавливать
3
+ * отслеживание по кадрам. Перекрывает длительность анимаций MUI, чтобы
4
+ * отслеживание не выключилось в паузе перед стартом анимации.
5
+ */
6
+ export declare const TRACKING_IDLE_TIMEOUT_MS = 300;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Сколько миллисекунд без изменений области считать покоем и останавливать
3
+ * отслеживание по кадрам. Перекрывает длительность анимаций MUI, чтобы
4
+ * отслеживание не выключилось в паузе перед стартом анимации.
5
+ */
6
+ export const TRACKING_IDLE_TIMEOUT_MS = 300;
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { clampToScrollParents, getViewport, isSameRect, rectFromDomRect, } from '../../../utils/geometry';
3
3
  import { getScrollParents } from '../../../utils/getScrollParents';
4
+ import { TRACKING_IDLE_TIMEOUT_MS } from './constants';
4
5
  /**
5
6
  * Отслеживает область элемента для hole overlay и якоря popover.
6
7
  */
@@ -11,32 +12,53 @@ export const useTrackedRect = (element) => {
11
12
  setRect(null);
12
13
  return;
13
14
  }
14
- // ID текущей рAF-очереди обновления.
15
+ // ID кадра активного отслеживания, 0 — отслеживание остановлено.
15
16
  let frame = 0;
17
+ let lastChangeTime = 0;
18
+ let lastRect = null;
16
19
  const scrollParents = getScrollParents(element);
17
20
  const update = () => {
18
21
  const elementRect = rectFromDomRect(element.getBoundingClientRect());
19
22
  const viewport = getViewport();
20
23
  const next = clampToScrollParents(elementRect, scrollParents.map((parent) => rectFromDomRect(parent.getBoundingClientRect())), viewport);
21
- setRect((prev) => (isSameRect(prev, next) ? prev : next));
24
+ if (isSameRect(lastRect, next)) {
25
+ return false;
26
+ }
27
+ lastRect = next;
28
+ setRect(next);
29
+ return true;
22
30
  };
23
- const scheduleUpdate = () => {
24
- cancelAnimationFrame(frame);
25
- frame = requestAnimationFrame(update);
31
+ const track = () => {
32
+ if (update()) {
33
+ lastChangeTime = performance.now();
34
+ }
35
+ if (performance.now() - lastChangeTime >= TRACKING_IDLE_TIMEOUT_MS) {
36
+ frame = 0;
37
+ return;
38
+ }
39
+ frame = requestAnimationFrame(track);
40
+ };
41
+ // Продлевает отслеживание, а если оно уже остановилось — запускает заново.
42
+ const scheduleTracking = () => {
43
+ lastChangeTime = performance.now();
44
+ if (!frame) {
45
+ frame = requestAnimationFrame(track);
46
+ }
26
47
  };
27
48
  update();
49
+ scheduleTracking();
28
50
  const scrollOptions = { passive: true };
29
- scrollParents.forEach((parent) => parent.addEventListener('scroll', scheduleUpdate, scrollOptions));
30
- window.addEventListener('scroll', scheduleUpdate, scrollOptions);
31
- window.addEventListener('resize', scheduleUpdate);
32
- const resizeObserver = new ResizeObserver(scheduleUpdate);
51
+ scrollParents.forEach((parent) => parent.addEventListener('scroll', scheduleTracking, scrollOptions));
52
+ window.addEventListener('scroll', scheduleTracking, scrollOptions);
53
+ window.addEventListener('resize', scheduleTracking);
54
+ const resizeObserver = new ResizeObserver(scheduleTracking);
33
55
  resizeObserver.observe(element);
34
56
  return () => {
35
57
  cancelAnimationFrame(frame);
36
58
  resizeObserver.disconnect();
37
- scrollParents.forEach((parent) => parent.removeEventListener('scroll', scheduleUpdate, scrollOptions));
38
- window.removeEventListener('scroll', scheduleUpdate, scrollOptions);
39
- window.removeEventListener('resize', scheduleUpdate);
59
+ scrollParents.forEach((parent) => parent.removeEventListener('scroll', scheduleTracking, scrollOptions));
60
+ window.removeEventListener('scroll', scheduleTracking, scrollOptions);
61
+ window.removeEventListener('resize', scheduleTracking);
40
62
  };
41
63
  }, [element]);
42
64
  return rect;
@@ -12,6 +12,7 @@ export declare const StyledDialog: import("../../../components/styled").StyledCo
12
12
  disableBackdropClick?: boolean | undefined;
13
13
  size?: import("../../../components/Dialog").DialogSize | undefined;
14
14
  headerContent?: import("react").ReactNode;
15
+ disableMobileDrawer?: boolean | undefined;
15
16
  } & {
16
17
  theme?: import("@emotion/react").Theme | undefined;
17
18
  }, {}, {}>;
@@ -1,2 +1,2 @@
1
1
  import type { DialogProps } from './types';
2
- export declare const Dialog: ({ children, title, disableBackdropClick, onClose, size, headerContent, className, classes, ...props }: DialogProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const Dialog: ({ children, title, disableBackdropClick, onClose, size, headerContent, className, classes, disableMobileDrawer, ...props }: DialogProps) => import("react/jsx-runtime").JSX.Element;
@@ -16,14 +16,15 @@ const dialogCva = (0, cva_1.cva)(constants_1.dialogClassnames.root, {
16
16
  },
17
17
  },
18
18
  });
19
- const Dialog = ({ children, title, disableBackdropClick, onClose, size = 'md', headerContent, className, classes, ...props }) => {
20
- const { handleClose, isMobile, dialogClasses } = (0, useLogic_1.useLogic)({
19
+ const Dialog = ({ children, title, disableBackdropClick, onClose, size = 'md', headerContent, className, classes, disableMobileDrawer, ...props }) => {
20
+ const { handleClose, shouldRenderDrawer, dialogClasses } = (0, useLogic_1.useLogic)({
21
21
  onClose,
22
22
  disableBackdropClick,
23
23
  classes,
24
+ disableMobileDrawer,
24
25
  });
25
- if (isMobile) {
26
- return ((0, jsx_runtime_1.jsx)(BottomDrawer_1.BottomDrawer, { onClose: handleClose, title: title, headerContent: headerContent, ...props, children: children }));
26
+ if (shouldRenderDrawer) {
27
+ return ((0, jsx_runtime_1.jsx)(BottomDrawer_1.BottomDrawer, { onClose: handleClose, title: title, headerContent: headerContent, className: className, ...props, children: children }));
27
28
  }
28
29
  return ((0, jsx_runtime_1.jsxs)(styles_1.StyledDialog, { "$size": size, onClose: handleClose, className: (0, cn_1.cn)(dialogCva({ withoutTitle: !title }), className), classes: dialogClasses, ...props, children: [title && ((0, jsx_runtime_1.jsx)(DialogTitle_1.DialogTitle, { onClose: onClose, headerContent: headerContent, children: title })), children] }));
29
30
  };
@@ -34,6 +34,14 @@ exports.StyledDialog = (0, styled_1.styled)(Dialog_1.default, {
34
34
  box-shadow: ${({ theme }) => theme.elevation[300]};
35
35
  }
36
36
 
37
+ ${({ theme }) => theme.breakpoints.down('mobile')} {
38
+ .${constants_1.dialogClassnames.paper} {
39
+ width: calc(100% - ${({ theme }) => theme.spacing(8)});
40
+ min-width: auto;
41
+ margin: ${({ theme }) => theme.spacing(4)};
42
+ }
43
+ }
44
+
37
45
  /* Причина игнора: Не критично для отображения */
38
46
  /* stylelint-disable-next-line plugin/no-unsupported-browser-features */
39
47
  &:not(:has(.${DialogActions_1.dialogActionsClassnames.root})) .${DialogContent_1.dialogContentClassnames.root} {
@@ -20,4 +20,9 @@ export type DialogProps = WithoutEmotionSpecific<Omit<MuiDialogProps, 'title' |
20
20
  * Дополнительный контент хедера
21
21
  */
22
22
  headerContent?: ReactNode;
23
+ /**
24
+ * Отключить BottomDrawer на mobile: диалог остаётся центрированным модальным окном.
25
+ * @default false
26
+ */
27
+ disableMobileDrawer?: boolean;
23
28
  };
@@ -1,9 +1,9 @@
1
1
  import { type MouseEvent } from 'react';
2
2
  import { type DialogProps } from '../types';
3
- type UseLogicParams = Pick<DialogProps, 'onClose' | 'disableBackdropClick' | 'classes'>;
4
- export declare const useLogic: ({ onClose, disableBackdropClick, classes, }: UseLogicParams) => {
3
+ type UseLogicParams = Pick<DialogProps, 'onClose' | 'disableBackdropClick' | 'classes' | 'disableMobileDrawer'>;
4
+ export declare const useLogic: ({ onClose, disableBackdropClick, classes, disableMobileDrawer, }: UseLogicParams) => {
5
5
  handleClose: ((event: MouseEvent<HTMLButtonElement>, reason: 'backdropClick' | 'escapeKeyDown') => void) | undefined;
6
- isMobile: boolean;
6
+ shouldRenderDrawer: boolean;
7
7
  dialogClasses: {
8
8
  root?: string | undefined;
9
9
  scrollPaper?: string | undefined;
@@ -4,7 +4,7 @@ exports.useLogic = void 0;
4
4
  const useViewportType_1 = require("../../useViewportType");
5
5
  const cn_1 = require("../../utils/cn");
6
6
  const constants_1 = require("../constants");
7
- const useLogic = ({ onClose, disableBackdropClick, classes, }) => {
7
+ const useLogic = ({ onClose, disableBackdropClick, classes, disableMobileDrawer, }) => {
8
8
  const { isMobile } = (0, useViewportType_1.useViewportType)();
9
9
  const handleClose = onClose &&
10
10
  ((event, reason) => {
@@ -18,6 +18,10 @@ const useLogic = ({ onClose, disableBackdropClick, classes, }) => {
18
18
  paper: (0, cn_1.cn)(constants_1.dialogClassnames.paper, paper),
19
19
  ...restClasses,
20
20
  };
21
- return { handleClose, isMobile, dialogClasses };
21
+ return {
22
+ handleClose,
23
+ shouldRenderDrawer: isMobile && !disableMobileDrawer,
24
+ dialogClasses,
25
+ };
22
26
  };
23
27
  exports.useLogic = useLogic;
@@ -25,7 +25,8 @@ exports.Body = styled_1.styled.div `
25
25
  display: flex;
26
26
  flex-direction: column;
27
27
 
28
- width: min(400px, calc(100vw - ${({ theme }) => theme.spacing(8)}));
28
+ /* Отступ popover от края вьюпорта — иначе поля по краям разъедутся. */
29
+ width: min(400px, calc(100vw - var(--popover-viewport-padding, 16px) * 2));
29
30
  padding: ${({ theme }) => theme.spacing(6)};
30
31
 
31
32
  ${({ theme }) => theme.breakpoints.down('mobile')} {
@@ -26,6 +26,7 @@ const NewPopover = ({ children, onClose, isOpen, title, className, style, placem
26
26
  disableAutoFocus,
27
27
  hasArrow,
28
28
  disableMobileDrawer,
29
+ placement,
29
30
  });
30
31
  if (shouldRenderDrawer) {
31
32
  return ((0, jsx_runtime_1.jsx)(BottomDrawer_1.BottomDrawer, { title: title, onClose: handleClose, open: isOpened, className: className, style: style, ModalProps: {
@@ -18,6 +18,11 @@ export declare const ARROW_PADDING = 24;
18
18
  export declare const ARROW_MIN_PADDING = 16;
19
19
  /** Мин. «ход» стрелки вдоль кромки (px). */
20
20
  export declare const ARROW_MIN_TRACK = 16;
21
+ /**
22
+ * Мин. отступ popover от края вьюпорта (px).
23
+ * Наружу отдаётся CSS-переменной `--popover-viewport-padding`.
24
+ */
25
+ export declare const VIEWPORT_MIN_PADDING = 16;
21
26
  /**
22
27
  * Базовые модификаторы Popper.js. У MUI Popper (popper.js)
23
28
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_MODIFIERS = exports.ARROW_MIN_TRACK = exports.ARROW_MIN_PADDING = exports.ARROW_PADDING = exports.ARROW_HEIGHT = exports.ARROW_BASE = exports.popoverClassnames = void 0;
3
+ exports.DEFAULT_MODIFIERS = exports.VIEWPORT_MIN_PADDING = exports.ARROW_MIN_TRACK = exports.ARROW_MIN_PADDING = exports.ARROW_PADDING = exports.ARROW_HEIGHT = exports.ARROW_BASE = exports.popoverClassnames = void 0;
4
4
  const createUIKitClassname_1 = require("../utils/createUIKitClassname");
5
5
  exports.popoverClassnames = {
6
6
  root: (0, createUIKitClassname_1.createUIKitClassname)('popover'),
@@ -21,12 +21,17 @@ exports.ARROW_PADDING = 24;
21
21
  exports.ARROW_MIN_PADDING = 16;
22
22
  /** Мин. «ход» стрелки вдоль кромки (px). */
23
23
  exports.ARROW_MIN_TRACK = 16;
24
+ /**
25
+ * Мин. отступ popover от края вьюпорта (px).
26
+ * Наружу отдаётся CSS-переменной `--popover-viewport-padding`.
27
+ */
28
+ exports.VIEWPORT_MIN_PADDING = 16;
24
29
  /**
25
30
  * Базовые модификаторы Popper.js. У MUI Popper (popper.js)
26
31
  */
27
32
  exports.DEFAULT_MODIFIERS = [
28
33
  {
29
34
  name: 'preventOverflow',
30
- options: { altAxis: true, tether: false, padding: 8 },
35
+ options: { altAxis: true, tether: false, padding: exports.VIEWPORT_MIN_PADDING },
31
36
  },
32
37
  ];
@@ -11,6 +11,7 @@ exports.StyledMuiPopper = (0, styled_1.styled)(Popper_1.default) `
11
11
  &.${constants_1.popoverClassnames.root} {
12
12
  --arrow-base: ${constants_1.ARROW_BASE}px;
13
13
  --arrow-height: ${constants_1.ARROW_HEIGHT}px;
14
+ --popover-viewport-padding: ${constants_1.VIEWPORT_MIN_PADDING}px;
14
15
 
15
16
  z-index: ${({ theme }) => theme.zIndex.modal};
16
17
  }
@@ -23,7 +23,7 @@ export type NewPopoverProps = {
23
23
  */
24
24
  isOpen?: boolean;
25
25
  /**
26
- * Позиция относительно anchor
26
+ * Позиция относительно anchor.
27
27
  * @default 'bottom'
28
28
  */
29
29
  placement?: NewPopoverPlacement;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import { type NewPopoverModifier, type NewPopoverProps } from '../types';
3
- type UseLogicParams = Pick<NewPopoverProps, 'isOpen' | 'anchorEl' | 'onClose' | 'disableAutoFocus' | 'hasArrow' | 'disableMobileDrawer'>;
4
- export declare const useLogic: ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disableMobileDrawer, }: UseLogicParams) => {
3
+ type UseLogicParams = Pick<NewPopoverProps, 'isOpen' | 'anchorEl' | 'onClose' | 'disableAutoFocus' | 'hasArrow' | 'disableMobileDrawer'> & Required<Pick<NewPopoverProps, 'placement'>>;
4
+ export declare const useLogic: ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disableMobileDrawer, placement, }: UseLogicParams) => {
5
5
  shouldRenderDrawer: boolean;
6
6
  isOpened: boolean;
7
7
  shouldRender: boolean;
@@ -8,10 +8,8 @@ const useFocusTrapEffect_1 = require("../../useFocusTrapEffect");
8
8
  const usePopoverAnimation_1 = require("../../usePopoverAnimation");
9
9
  const useViewportType_1 = require("../../useViewportType");
10
10
  const constants_1 = require("../constants");
11
- const getArrowPadding_1 = require("./utils/getArrowPadding");
12
- const getPopoverCssVars_1 = require("./utils/getPopoverCssVars");
13
- const resolveAnchorNode_1 = require("./utils/resolveAnchorNode");
14
- const useLogic = ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disableMobileDrawer, }) => {
11
+ const utils_1 = require("./utils");
12
+ const useLogic = ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disableMobileDrawer, placement, }) => {
15
13
  const containerRef = (0, react_1.useRef)(null);
16
14
  const [isContainerMounted, setIsContainerMounted] = (0, react_1.useState)(false);
17
15
  const [arrowElement, setArrowElement] = (0, react_1.useState)(null);
@@ -23,10 +21,10 @@ const useLogic = ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disab
23
21
  const isControlled = isOpen !== undefined;
24
22
  const { isMobile } = (0, useViewportType_1.useViewportType)();
25
23
  const { shouldRender, handleAnimationEnd } = (0, usePopoverAnimation_1.usePopoverAnimation)(isOpened);
26
- const animatedWrapperStyle = (0, getPopoverCssVars_1.getPopoverCssVars)();
24
+ const animatedWrapperStyle = (0, utils_1.getPopoverCssVars)();
27
25
  const shouldRenderDrawer = isMobile && !disableMobileDrawer;
28
26
  const isPopoverActive = !shouldRenderDrawer && isOpened && isContainerMounted;
29
- const anchorNode = (0, react_1.useMemo)(() => (0, resolveAnchorNode_1.resolveAnchorNode)(anchorEl), [anchorEl]);
27
+ const anchorNode = (0, react_1.useMemo)(() => (0, utils_1.resolveAnchorNode)(anchorEl), [anchorEl]);
30
28
  const excludeNodes = (0, react_1.useMemo)(() => [anchorNode], [anchorNode]);
31
29
  const handleClose = (event) => {
32
30
  onClose?.(event);
@@ -53,18 +51,20 @@ const useLogic = ({ isOpen, anchorEl, onClose, disableAutoFocus, hasArrow, disab
53
51
  fallbackFocus: () => containerRef.current || document.body,
54
52
  });
55
53
  const mergedModifiers = (0, react_1.useMemo)(() => {
56
- if (!hasArrow) {
57
- return constants_1.DEFAULT_MODIFIERS;
54
+ const modifiers = [...constants_1.DEFAULT_MODIFIERS];
55
+ const fallbackPlacements = (0, utils_1.getFallbackPlacements)(placement);
56
+ if (fallbackPlacements) {
57
+ modifiers.push({ name: 'flip', options: { fallbackPlacements } });
58
58
  }
59
- return [
60
- ...constants_1.DEFAULT_MODIFIERS,
61
- {
59
+ if (hasArrow) {
60
+ modifiers.push({
62
61
  name: 'arrow',
63
62
  enabled: true,
64
- options: { element: arrowElement, padding: getArrowPadding_1.getArrowPadding },
65
- },
66
- ];
67
- }, [hasArrow, arrowElement]);
63
+ options: { element: arrowElement, padding: utils_1.getArrowPadding },
64
+ });
65
+ }
66
+ return modifiers;
67
+ }, [hasArrow, arrowElement, placement]);
68
68
  return {
69
69
  shouldRenderDrawer,
70
70
  isOpened,
@@ -0,0 +1,7 @@
1
+ import { type NewPopoverPlacement } from '../../../types';
2
+ /**
3
+ * Значение fallbackPlacements для модификатора flip: заданная сторона не подошла —
4
+ * берётся следующая по списку. Для вертикальной стороны и `auto` список не задаётся,
5
+ * flip справляется сам.
6
+ */
7
+ export declare const getFallbackPlacements: (placement: NewPopoverPlacement) => NewPopoverPlacement[] | undefined;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFallbackPlacements = void 0;
4
+ const OPPOSITE_SIDE = {
5
+ left: 'right',
6
+ right: 'left',
7
+ };
8
+ const VERTICAL_SIDES = ['bottom', 'top'];
9
+ const checkIsSidePlacement = (side) => side in OPPOSITE_SIDE;
10
+ /**
11
+ * Значение fallbackPlacements для модификатора flip: заданная сторона не подошла —
12
+ * берётся следующая по списку. Для вертикальной стороны и `auto` список не задаётся,
13
+ * flip справляется сам.
14
+ */
15
+ const getFallbackPlacements = (placement) => {
16
+ const [side, alignment] = placement.split('-');
17
+ if (!checkIsSidePlacement(side)) {
18
+ return undefined;
19
+ }
20
+ const suffix = alignment ? `-${alignment}` : '';
21
+ return [OPPOSITE_SIDE[side], ...VERTICAL_SIDES].map((fallbackSide) => `${fallbackSide}${suffix}`);
22
+ };
23
+ exports.getFallbackPlacements = getFallbackPlacements;
@@ -0,0 +1 @@
1
+ export * from './getFallbackPlacements';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./getFallbackPlacements"), exports);
@@ -0,0 +1,4 @@
1
+ export * from './getArrowPadding';
2
+ export * from './getFallbackPlacements';
3
+ export * from './getPopoverCssVars';
4
+ export * from './resolveAnchorNode';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./getArrowPadding"), exports);
18
+ __exportStar(require("./getFallbackPlacements"), exports);
19
+ __exportStar(require("./getPopoverCssVars"), exports);
20
+ __exportStar(require("./resolveAnchorNode"), exports);
@@ -45,7 +45,7 @@ export type Step = {
45
45
  */
46
46
  type: StepType;
47
47
  /**
48
- * Желаемая позиция popover (только для highlight).
48
+ * Желаемая позиция popover относительно подсвечиваемого элемента (только для highlight).
49
49
  * @default 'bottom'
50
50
  */
51
51
  position?: StepPosition;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- export declare const StepPopover: import("@emotion/styled/dist/declarations/src/types").StyledComponent<Omit<import("../../../../../NewPopover").NewPopoverProps, "title" | "hasArrow" | "disableMobileDrawer"> & {
2
+ export declare const StepPopover: import("@emotion/styled/dist/declarations/src/types").StyledComponent<Omit<import("../../../../../NewPopover").NewPopoverProps, "title" | "disableMobileDrawer" | "hasArrow"> & {
3
3
  variant?: import("../../../../../HintPopover").HintPopoverVariant | undefined;
4
4
  title?: import("react").ReactNode;
5
5
  action?: import("react").ReactNode;
@@ -10,6 +10,6 @@ const styles_1 = require("./styles");
10
10
  */
11
11
  const StandaloneStep = ({ activeStep, isClosing, }) => {
12
12
  const { step, isConfirming, requestClose } = activeStep;
13
- return ((0, jsx_runtime_1.jsxs)(styles_1.StepDialog, { open: !isConfirming && !isClosing, title: step.title, onClose: requestClose, size: step.dialogSize, disableAutoFocus: true, children: [(0, jsx_runtime_1.jsx)(DialogContent_1.DialogContent, { children: step.content }), (0, jsx_runtime_1.jsx)(styles_1.Footer, { children: (0, jsx_runtime_1.jsx)(StepFooter_1.StepFooter, { activeStep: activeStep }) })] }));
13
+ return ((0, jsx_runtime_1.jsxs)(styles_1.StepDialog, { open: !isConfirming && !isClosing, title: step.title, onClose: requestClose, size: step.dialogSize, disableAutoFocus: true, disableMobileDrawer: true, children: [(0, jsx_runtime_1.jsx)(DialogContent_1.DialogContent, { children: step.content }), (0, jsx_runtime_1.jsx)(styles_1.Footer, { children: (0, jsx_runtime_1.jsx)(StepFooter_1.StepFooter, { activeStep: activeStep }) })] }));
14
14
  };
15
15
  exports.StandaloneStep = StandaloneStep;
@@ -4,6 +4,7 @@ export declare const StepDialog: import("@emotion/styled/dist/declarations/src/t
4
4
  disableBackdropClick?: boolean | undefined;
5
5
  size?: import("../../../../../Dialog").DialogSize | undefined;
6
6
  headerContent?: import("react").ReactNode;
7
+ disableMobileDrawer?: boolean | undefined;
7
8
  } & {
8
9
  theme?: import("@emotion/react").Theme | undefined;
9
10
  }, {}, {}>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Сколько миллисекунд без изменений области считать покоем и останавливать
3
+ * отслеживание по кадрам. Перекрывает длительность анимаций MUI, чтобы
4
+ * отслеживание не выключилось в паузе перед стартом анимации.
5
+ */
6
+ export declare const TRACKING_IDLE_TIMEOUT_MS = 300;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TRACKING_IDLE_TIMEOUT_MS = void 0;
4
+ /**
5
+ * Сколько миллисекунд без изменений области считать покоем и останавливать
6
+ * отслеживание по кадрам. Перекрывает длительность анимаций MUI, чтобы
7
+ * отслеживание не выключилось в паузе перед стартом анимации.
8
+ */
9
+ exports.TRACKING_IDLE_TIMEOUT_MS = 300;
@@ -4,6 +4,7 @@ exports.useTrackedRect = void 0;
4
4
  const react_1 = require("react");
5
5
  const geometry_1 = require("../../../utils/geometry");
6
6
  const getScrollParents_1 = require("../../../utils/getScrollParents");
7
+ const constants_1 = require("./constants");
7
8
  /**
8
9
  * Отслеживает область элемента для hole overlay и якоря popover.
9
10
  */
@@ -14,32 +15,53 @@ const useTrackedRect = (element) => {
14
15
  setRect(null);
15
16
  return;
16
17
  }
17
- // ID текущей рAF-очереди обновления.
18
+ // ID кадра активного отслеживания, 0 — отслеживание остановлено.
18
19
  let frame = 0;
20
+ let lastChangeTime = 0;
21
+ let lastRect = null;
19
22
  const scrollParents = (0, getScrollParents_1.getScrollParents)(element);
20
23
  const update = () => {
21
24
  const elementRect = (0, geometry_1.rectFromDomRect)(element.getBoundingClientRect());
22
25
  const viewport = (0, geometry_1.getViewport)();
23
26
  const next = (0, geometry_1.clampToScrollParents)(elementRect, scrollParents.map((parent) => (0, geometry_1.rectFromDomRect)(parent.getBoundingClientRect())), viewport);
24
- setRect((prev) => ((0, geometry_1.isSameRect)(prev, next) ? prev : next));
27
+ if ((0, geometry_1.isSameRect)(lastRect, next)) {
28
+ return false;
29
+ }
30
+ lastRect = next;
31
+ setRect(next);
32
+ return true;
25
33
  };
26
- const scheduleUpdate = () => {
27
- cancelAnimationFrame(frame);
28
- frame = requestAnimationFrame(update);
34
+ const track = () => {
35
+ if (update()) {
36
+ lastChangeTime = performance.now();
37
+ }
38
+ if (performance.now() - lastChangeTime >= constants_1.TRACKING_IDLE_TIMEOUT_MS) {
39
+ frame = 0;
40
+ return;
41
+ }
42
+ frame = requestAnimationFrame(track);
43
+ };
44
+ // Продлевает отслеживание, а если оно уже остановилось — запускает заново.
45
+ const scheduleTracking = () => {
46
+ lastChangeTime = performance.now();
47
+ if (!frame) {
48
+ frame = requestAnimationFrame(track);
49
+ }
29
50
  };
30
51
  update();
52
+ scheduleTracking();
31
53
  const scrollOptions = { passive: true };
32
- scrollParents.forEach((parent) => parent.addEventListener('scroll', scheduleUpdate, scrollOptions));
33
- window.addEventListener('scroll', scheduleUpdate, scrollOptions);
34
- window.addEventListener('resize', scheduleUpdate);
35
- const resizeObserver = new ResizeObserver(scheduleUpdate);
54
+ scrollParents.forEach((parent) => parent.addEventListener('scroll', scheduleTracking, scrollOptions));
55
+ window.addEventListener('scroll', scheduleTracking, scrollOptions);
56
+ window.addEventListener('resize', scheduleTracking);
57
+ const resizeObserver = new ResizeObserver(scheduleTracking);
36
58
  resizeObserver.observe(element);
37
59
  return () => {
38
60
  cancelAnimationFrame(frame);
39
61
  resizeObserver.disconnect();
40
- scrollParents.forEach((parent) => parent.removeEventListener('scroll', scheduleUpdate, scrollOptions));
41
- window.removeEventListener('scroll', scheduleUpdate, scrollOptions);
42
- window.removeEventListener('resize', scheduleUpdate);
62
+ scrollParents.forEach((parent) => parent.removeEventListener('scroll', scheduleTracking, scrollOptions));
63
+ window.removeEventListener('scroll', scheduleTracking, scrollOptions);
64
+ window.removeEventListener('resize', scheduleTracking);
43
65
  };
44
66
  }, [element]);
45
67
  return rect;
@@ -12,6 +12,7 @@ export declare const StyledDialog: import("@emotion/styled/dist/declarations/src
12
12
  disableBackdropClick?: boolean | undefined;
13
13
  size?: import("../../../components/Dialog").DialogSize | undefined;
14
14
  headerContent?: import("react").ReactNode;
15
+ disableMobileDrawer?: boolean | undefined;
15
16
  } & {
16
17
  theme?: import("@emotion/react").Theme | undefined;
17
18
  }, {}, {}>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astral/ui",
3
- "version": "4.102.0",
3
+ "version": "4.103.0",
4
4
  "browser": "./index.js",
5
5
  "main": "./node/index.js",
6
6
  "dependencies": {