@astral/ui 4.102.0 → 4.103.1

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 (89) 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/components/fileUploading/FileUploader/FIleList/FileList.d.ts +1 -1
  29. package/components/fileUploading/FileUploaderFile/Actions/Actions.js +2 -2
  30. package/components/fileUploading/FileUploaderFile/Actions/styles.js +7 -0
  31. package/components/fileUploading/FileUploaderFile/Actions/useLogic/useLogic.d.ts +6 -1
  32. package/components/fileUploading/FileUploaderFile/Actions/useLogic/useLogic.js +2 -1
  33. package/components/fileUploading/FileUploaderFile/FileUploaderFile.d.ts +1 -1
  34. package/components/fileUploading/PreviewFileUploader/FileList/FileList.d.ts +1 -1
  35. package/components/fileUploading/PreviewFileUploaderFile/FilePreview/FilePreview.d.ts +4 -0
  36. package/components/fileUploading/PreviewFileUploaderFile/FilePreview/FilePreview.js +1 -1
  37. package/components/fileUploading/PreviewFileUploaderFile/FilePreview/useLogic/useLogic.d.ts +13 -51
  38. package/components/fileUploading/PreviewFileUploaderFile/FilePreview/useLogic/useLogic.js +5 -1
  39. package/components/fileUploading/PreviewFileUploaderFile/PreviewFileUploaderFile.d.ts +4 -0
  40. package/components/fileUploading/PreviewFileUploaderFile/PreviewFileUploaderFile.js +2 -2
  41. package/components/fileUploading/hooks/useFileUploader/useFileUploader.d.ts +1 -0
  42. package/components/fileUploading/hooks/useFileUploader/useFileUploader.js +1 -0
  43. package/components/fileUploading/hooks/usePreviewFileUploader/usePreviewFileUploader.d.ts +1 -0
  44. package/hook-form/FormFilters/FormFiltersDialog/styles.d.ts +1 -0
  45. package/node/components/Dialog/Dialog.d.ts +1 -1
  46. package/node/components/Dialog/Dialog.js +5 -4
  47. package/node/components/Dialog/styles.js +8 -0
  48. package/node/components/Dialog/types.d.ts +5 -0
  49. package/node/components/Dialog/useLogic/useLogic.d.ts +3 -3
  50. package/node/components/Dialog/useLogic/useLogic.js +6 -2
  51. package/node/components/HintPopover/styles.js +2 -1
  52. package/node/components/NewPopover/NewPopover.js +1 -0
  53. package/node/components/NewPopover/constants.d.ts +5 -0
  54. package/node/components/NewPopover/constants.js +7 -2
  55. package/node/components/NewPopover/styles.js +1 -0
  56. package/node/components/NewPopover/types.d.ts +1 -1
  57. package/node/components/NewPopover/useLogic/useLogic.d.ts +2 -2
  58. package/node/components/NewPopover/useLogic/useLogic.js +15 -15
  59. package/node/components/NewPopover/useLogic/utils/getFallbackPlacements/getFallbackPlacements.d.ts +7 -0
  60. package/node/components/NewPopover/useLogic/utils/getFallbackPlacements/getFallbackPlacements.js +23 -0
  61. package/node/components/NewPopover/useLogic/utils/getFallbackPlacements/index.d.ts +1 -0
  62. package/node/components/NewPopover/useLogic/utils/getFallbackPlacements/index.js +17 -0
  63. package/node/components/NewPopover/useLogic/utils/index.d.ts +4 -0
  64. package/node/components/NewPopover/useLogic/utils/index.js +20 -0
  65. package/node/components/Onboarding/types/step.d.ts +1 -1
  66. package/node/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.d.ts +1 -1
  67. package/node/components/Onboarding/ui/StepStrategy/steps/StandaloneStep/StandaloneStep.js +1 -1
  68. package/node/components/Onboarding/ui/StepStrategy/steps/StandaloneStep/styles.d.ts +1 -0
  69. package/node/components/Onboarding/ui/hooks/useTrackedRect/constants.d.ts +6 -0
  70. package/node/components/Onboarding/ui/hooks/useTrackedRect/constants.js +9 -0
  71. package/node/components/Onboarding/ui/hooks/useTrackedRect/useTrackedRect.js +34 -12
  72. package/node/components/fileUploading/FileUploader/FIleList/FileList.d.ts +1 -1
  73. package/node/components/fileUploading/FileUploaderFile/Actions/Actions.js +2 -2
  74. package/node/components/fileUploading/FileUploaderFile/Actions/styles.js +7 -0
  75. package/node/components/fileUploading/FileUploaderFile/Actions/useLogic/useLogic.d.ts +6 -1
  76. package/node/components/fileUploading/FileUploaderFile/Actions/useLogic/useLogic.js +2 -1
  77. package/node/components/fileUploading/FileUploaderFile/FileUploaderFile.d.ts +1 -1
  78. package/node/components/fileUploading/PreviewFileUploader/FileList/FileList.d.ts +1 -1
  79. package/node/components/fileUploading/PreviewFileUploaderFile/FilePreview/FilePreview.d.ts +4 -0
  80. package/node/components/fileUploading/PreviewFileUploaderFile/FilePreview/FilePreview.js +1 -1
  81. package/node/components/fileUploading/PreviewFileUploaderFile/FilePreview/useLogic/useLogic.d.ts +13 -51
  82. package/node/components/fileUploading/PreviewFileUploaderFile/FilePreview/useLogic/useLogic.js +5 -1
  83. package/node/components/fileUploading/PreviewFileUploaderFile/PreviewFileUploaderFile.d.ts +4 -0
  84. package/node/components/fileUploading/PreviewFileUploaderFile/PreviewFileUploaderFile.js +2 -2
  85. package/node/components/fileUploading/hooks/useFileUploader/useFileUploader.d.ts +1 -0
  86. package/node/components/fileUploading/hooks/useFileUploader/useFileUploader.js +1 -0
  87. package/node/components/fileUploading/hooks/usePreviewFileUploader/usePreviewFileUploader.d.ts +1 -0
  88. package/node/hook-form/FormFilters/FormFiltersDialog/styles.d.ts +1 -0
  89. 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;
@@ -11,5 +11,5 @@ export type FileListProps = {
11
11
  * Идентификатор файла, для которого выполняется удаление
12
12
  */
13
13
  deletingFileId?: FileId | null;
14
- } & Pick<FileUploaderFileProps, 'onDelete' | 'onRetry' | 'onView' | 'generateDownloadLink' | 'selfFiles' | 'setErrorStatusFile'>;
14
+ } & Pick<FileUploaderFileProps, 'onDelete' | 'onRetry' | 'onView' | 'generateDownloadLink' | 'selfFiles' | 'setErrorStatusFile' | 'isDisabled'>;
15
15
  export declare const FileList: ({ isMultiple, files, className, deletingFileId, ...restProps }: FileListProps) => import("react/jsx-runtime").JSX.Element;
@@ -10,7 +10,7 @@ import { useViewportType } from '../../../useViewportType';
10
10
  import { Wrapper } from './styles';
11
11
  import { useLogic } from './useLogic';
12
12
  export const Actions = (props) => {
13
- const { downloadButtonProps, viewButtonProps, isVisibleAbortButton, isVisibleRetryButton, handleAbort, handleDelete, handleRetry, isLoading, isError, isDeletingFile, fileName, } = useLogic(props);
13
+ const { downloadButtonProps, viewButtonProps, isVisibleAbortButton, isVisibleRetryButton, handleAbort, handleDelete, handleRetry, isLoading, isError, isDeletingFile, isDisabled, fileName, } = useLogic(props);
14
14
  const { isMobile } = useViewportType();
15
15
  if (isLoading) {
16
16
  return (_jsx(Wrapper, { children: isVisibleAbortButton && (_jsx(Tooltip, { title: "\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0443", children: _jsx(IconButton, { variant: "text", onClick: handleAbort, children: _jsx(CrossOutlineMd, {}) }) })) }));
@@ -19,5 +19,5 @@ export const Actions = (props) => {
19
19
  return (_jsxs(Wrapper, { children: [isVisibleRetryButton && (_jsx(Tooltip, { title: "\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0443", children: _jsx(IconButton, { variant: "text", onClick: handleRetry, children: _jsx(RetryOutlineMd, {}) }) })), _jsx(Tooltip, { title: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C", children: _jsx(IconButton, { variant: "text", onClick: handleDelete, loading: isDeletingFile, children: _jsx(CrossOutlineMd, {}) }) })] }));
20
20
  }
21
21
  const { isShowViewButton, ...restViewButtonProps } = viewButtonProps;
22
- return (_jsxs(Wrapper, { children: [!isMobile && (_jsxs(_Fragment, { children: [isShowViewButton && (_jsx(Tooltip, { title: "\u041F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C", children: _jsx(IconButton, { variant: "text", ...restViewButtonProps, children: _jsx(EyeOutlineMd, {}) }) })), _jsx(Tooltip, { title: "\u0421\u043A\u0430\u0447\u0430\u0442\u044C", children: _jsx(IconButton, { component: "a", variant: "text", download: fileName, ...downloadButtonProps, children: _jsx(DownloadOutlineMd, {}) }) })] })), _jsx(Tooltip, { title: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C", children: _jsx(IconButton, { variant: "text", onClick: handleDelete, loading: isDeletingFile, children: _jsx(BinOutlineMd, {}) }) })] }));
22
+ return (_jsxs(Wrapper, { children: [!isMobile && (_jsxs(_Fragment, { children: [isShowViewButton && (_jsx(Tooltip, { title: "\u041F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C", children: _jsx(IconButton, { variant: "text", ...restViewButtonProps, children: _jsx(EyeOutlineMd, {}) }) })), _jsx(Tooltip, { title: "\u0421\u043A\u0430\u0447\u0430\u0442\u044C", children: _jsx(IconButton, { component: "a", variant: "text", download: fileName, ...downloadButtonProps, children: _jsx(DownloadOutlineMd, {}) }) })] })), _jsx(Tooltip, { title: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C", withoutContainer: !isDisabled, children: _jsx(IconButton, { variant: "text", onClick: handleDelete, loading: isDeletingFile, disabled: isDisabled, "aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C", children: _jsx(BinOutlineMd, {}) }) })] }));
23
23
  };
@@ -1,4 +1,11 @@
1
1
  import { styled } from '../../../styled';
2
+ import { tooltipClassnames } from '../../../Tooltip';
2
3
  export const Wrapper = styled.div `
3
4
  text-align: right;
5
+
6
+ & .${tooltipClassnames.contentWrapper} {
7
+ display: inline-flex;
8
+
9
+ vertical-align: middle;
10
+ }
4
11
  `;
@@ -49,9 +49,13 @@ export type UseLogicParams = {
49
49
  * Флаг загрузки при удалении файла
50
50
  */
51
51
  isDeletingFile?: boolean;
52
+ /**
53
+ * Флаг блокировки действий
54
+ */
55
+ isDisabled?: boolean;
52
56
  abortController?: AbortController;
53
57
  } & Pick<SelfFileApi, 'selfFiles' | 'setErrorStatusFile'>;
54
- export declare const useLogic: ({ fileId, file, fileUrl, isUploadError, generateDownloadLink, onDelete, onRetry, onView, setErrorStatusFile, selfFiles, isLoading, isError, isDeletingFile, fileName, }: UseLogicParams) => {
58
+ export declare const useLogic: ({ fileId, file, fileUrl, isUploadError, generateDownloadLink, onDelete, onRetry, onView, setErrorStatusFile, selfFiles, isLoading, isError, isDeletingFile, isDisabled, fileName, }: UseLogicParams) => {
55
59
  downloadButtonProps: {
56
60
  href: string;
57
61
  };
@@ -74,5 +78,6 @@ export declare const useLogic: ({ fileId, file, fileUrl, isUploadError, generate
74
78
  isLoading: boolean | undefined;
75
79
  isError: boolean | undefined;
76
80
  isDeletingFile: boolean | undefined;
81
+ isDisabled: boolean | undefined;
77
82
  fileName: string;
78
83
  };
@@ -3,7 +3,7 @@ import { ABORT_ERROR_MESSAGE, EXTENSIONS_IN_NEW_TAB, VIEW_NEW_TAB_ERROR_MESSAGE,
3
3
  import { useGenerateLocalFileURL } from '../../../hooks/useGenerateLocalFileURL';
4
4
  import { MimeTypeRegistry } from '../../../services/MimeTypeRegistry';
5
5
  import { generateAttrLinkNewTab } from '../../../utils/generateAttrLinkNewTab';
6
- export const useLogic = ({ fileId, file, fileUrl, isUploadError, generateDownloadLink, onDelete, onRetry, onView, setErrorStatusFile, selfFiles, isLoading, isError, isDeletingFile, fileName, }) => {
6
+ export const useLogic = ({ fileId, file, fileUrl, isUploadError, generateDownloadLink, onDelete, onRetry, onView, setErrorStatusFile, selfFiles, isLoading, isError, isDeletingFile, isDisabled, fileName, }) => {
7
7
  const { generateUrl } = useGenerateLocalFileURL();
8
8
  const { controller } = selfFiles[fileId] || {};
9
9
  const isVisibleAbortButton = Boolean(controller);
@@ -71,6 +71,7 @@ export const useLogic = ({ fileId, file, fileUrl, isUploadError, generateDownloa
71
71
  isLoading,
72
72
  isError,
73
73
  isDeletingFile,
74
+ isDisabled,
74
75
  fileName,
75
76
  };
76
77
  };
@@ -21,5 +21,5 @@ export type FileUploaderFileProps = UploadFile & {
21
21
  * Флаг загрузки при удалении файла
22
22
  */
23
23
  isDeletingFile?: boolean;
24
- } & Pick<ActionsProps, 'selfFiles' | 'setErrorStatusFile'>;
24
+ } & Pick<ActionsProps, 'selfFiles' | 'setErrorStatusFile' | 'isDisabled'>;
25
25
  export declare const FileUploaderFile: (props: FileUploaderFileProps) => import("react/jsx-runtime").JSX.Element;
@@ -6,5 +6,5 @@ export type FileListProps = {
6
6
  * Идентификатор файла, для которого выполняется удаление
7
7
  */
8
8
  deletingFileId?: FileId | null;
9
- } & Pick<PreviewFileUploaderFileProps, 'onDelete' | 'onRetry' | 'onView' | 'generateDownloadLink' | 'selfFiles' | 'setErrorStatusFile'>;
9
+ } & Pick<PreviewFileUploaderFileProps, 'onDelete' | 'onRetry' | 'onView' | 'generateDownloadLink' | 'selfFiles' | 'setErrorStatusFile' | 'isDisabled'>;
10
10
  export declare const FileList: ({ files, deletingFileId, ...restProps }: FileListProps) => import("react/jsx-runtime").JSX.Element;
@@ -46,6 +46,10 @@ export type FilePreviewProps = {
46
46
  * Флаг загрузки при удалении файла
47
47
  */
48
48
  isDeletingFile?: boolean;
49
+ /**
50
+ * Флаг блокировки действий
51
+ */
52
+ isDisabled?: boolean;
49
53
  /**
50
54
  * Компонент для отображения превью pdf-файлов
51
55
  */
@@ -10,5 +10,5 @@ import { useLogic } from './useLogic';
10
10
  export const FilePreview = (props) => {
11
11
  const { getActionsList, isImageFile, isPdfFile, isOtherFile } = useLogic(props);
12
12
  const { file, fileName, fileSize, fileUrl, generateDownloadLink, isHidePersonalData, PdfViewer, } = props;
13
- return (_jsxs(Wrapper, { children: [!isOtherFile && (_jsx(Preview, { isImageFile: isImageFile, isPdfFile: isPdfFile, file: file, fileUrl: fileUrl, generateDownloadLink: generateDownloadLink, isHidePersonalData: isHidePersonalData, PdfViewer: PdfViewer })), _jsxs(Details, { className: previewFileUploaderClassnames.details, "$isOtherFile": isOtherFile, children: [isOtherFile && _jsx(Typography, { variant: "h6", children: "\u0424\u0430\u0439\u043B \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D" }), _jsx(StyledFileInfo, { "$isOtherFile": isOtherFile, fileName: fileName, fileSize: fileSize }), _jsx(Actions, { children: getActionsList().map(({ type, ...action }) => (_jsx(Tooltip, { title: action.tooltip, disableInteractive: true, children: type === 'iconButton' ? (_jsx(IconButton, { ...action, children: action.icon })) : (_jsx(Button, { ...action, children: 'title' in action ? action.title : '' })) }, action.tooltip))) })] })] }));
13
+ return (_jsxs(Wrapper, { children: [!isOtherFile && (_jsx(Preview, { isImageFile: isImageFile, isPdfFile: isPdfFile, file: file, fileUrl: fileUrl, generateDownloadLink: generateDownloadLink, isHidePersonalData: isHidePersonalData, PdfViewer: PdfViewer })), _jsxs(Details, { className: previewFileUploaderClassnames.details, "$isOtherFile": isOtherFile, children: [isOtherFile && _jsx(Typography, { variant: "h6", children: "\u0424\u0430\u0439\u043B \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D" }), _jsx(StyledFileInfo, { "$isOtherFile": isOtherFile, fileName: fileName, fileSize: fileSize }), _jsx(Actions, { children: getActionsList().map(({ type, ...action }) => (_jsx(Tooltip, { title: action.tooltip, disableInteractive: true, withoutContainer: !action.disabled, children: type === 'iconButton' ? (_jsx(IconButton, { ...action, children: action.icon })) : (_jsx(Button, { ...action, children: action.title ?? '' })) }, action.tooltip))) })] })] }));
14
14
  };
@@ -1,61 +1,23 @@
1
- /// <reference types="react" />
1
+ import { type ReactElement } from 'react';
2
+ import { type ButtonProps } from '../../../../Button';
2
3
  import { type FilePreviewProps } from '../FilePreview';
3
4
  type UseLogicParams = FilePreviewProps;
4
- export declare const useLogic: ({ fileId, file, fileUrl, fileName, generateDownloadLink, onDelete, onView, fileType, isDeletingFile, }: UseLogicParams) => {
5
+ /**
6
+ * Действие в списке actions превью файла.
7
+ */
8
+ type FileAction = Omit<ButtonProps, 'type'> & {
9
+ type: 'button' | 'iconButton';
10
+ tooltip: string;
11
+ icon?: ReactElement;
12
+ title?: string;
13
+ };
14
+ export declare const useLogic: ({ fileId, file, fileUrl, fileName, generateDownloadLink, onDelete, onView, fileType, isDeletingFile, isDisabled, }: UseLogicParams) => {
5
15
  isImageFile: boolean;
6
16
  isPdfFile: boolean;
7
17
  /**
8
18
  * Формат файла отличающийся от pdf и изображения
9
19
  */
10
20
  isOtherFile: boolean;
11
- getActionsList: () => {
12
- type: string;
13
- tooltip: string;
14
- title: string;
15
- variant: "light";
16
- color: "grey";
17
- icon: import("react").FunctionComponentElement<import("../../../..").SvgIconProps>;
18
- loading: boolean | undefined;
19
- onClick: () => void;
20
- }[] | ({
21
- component: import("react").ElementType;
22
- href: string;
23
- target: string;
24
- rel: string;
25
- type: string;
26
- tooltip: string;
27
- variant: "light";
28
- color: "grey";
29
- icon: import("react").FunctionComponentElement<import("../../../..").SvgIconProps>;
30
- } | {
31
- onClick: () => void | undefined;
32
- type: string;
33
- tooltip: string;
34
- variant: "light";
35
- color: "grey";
36
- icon: import("react").FunctionComponentElement<import("../../../..").SvgIconProps>;
37
- } | {
38
- type: string;
39
- tooltip: string;
40
- component: "a";
41
- variant: "light";
42
- color: "grey";
43
- icon: import("react").FunctionComponentElement<import("../../../..").SvgIconProps>;
44
- download: string;
45
- href: string;
46
- loading?: undefined;
47
- onClick?: undefined;
48
- } | {
49
- type: string;
50
- tooltip: string;
51
- variant: "light";
52
- color: "grey";
53
- icon: import("react").FunctionComponentElement<import("../../../..").SvgIconProps>;
54
- loading: boolean | undefined;
55
- onClick: () => void;
56
- component?: undefined;
57
- download?: undefined;
58
- href?: undefined;
59
- })[];
21
+ getActionsList: () => FileAction[];
60
22
  };
61
23
  export {};
@@ -8,7 +8,7 @@ import { EXTENSIONS_IN_NEW_TAB, VIEW_NEW_TAB_ERROR_MESSAGE, } from '../../../con
8
8
  import { useGenerateLocalFileURL } from '../../../hooks/useGenerateLocalFileURL';
9
9
  import { MimeTypeRegistry } from '../../../services/MimeTypeRegistry';
10
10
  import { generateAttrLinkNewTab } from '../../../utils/generateAttrLinkNewTab';
11
- export const useLogic = ({ fileId, file, fileUrl, fileName, generateDownloadLink, onDelete, onView, fileType, isDeletingFile, }) => {
11
+ export const useLogic = ({ fileId, file, fileUrl, fileName, generateDownloadLink, onDelete, onView, fileType, isDeletingFile, isDisabled, }) => {
12
12
  const { generateUrl } = useGenerateLocalFileURL();
13
13
  const { isMobile } = useViewportType();
14
14
  const isPdfFile = MimeTypeRegistry.getInstance()
@@ -81,6 +81,8 @@ export const useLogic = ({ fileId, file, fileUrl, fileName, generateDownloadLink
81
81
  color: 'grey',
82
82
  icon: createElement(BinOutlineMd),
83
83
  loading: isDeletingFile,
84
+ disabled: isDisabled,
85
+ 'aria-label': 'Удалить',
84
86
  onClick: handleDelete,
85
87
  },
86
88
  ];
@@ -104,6 +106,8 @@ export const useLogic = ({ fileId, file, fileUrl, fileName, generateDownloadLink
104
106
  color: 'grey',
105
107
  icon: createElement(BinOutlineMd),
106
108
  loading: isDeletingFile,
109
+ disabled: isDisabled,
110
+ 'aria-label': 'Удалить',
107
111
  onClick: handleDelete,
108
112
  },
109
113
  ];