@cloud-ru/ds-drawer 2.0.1-preview-20b4671f.0 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-ru/ds-drawer",
3
- "version": "2.0.1-preview-20b4671f.0",
3
+ "version": "2.1.0",
4
4
  "description": "Пакет выезжающих панелей — компоненты Drawer и DrawerCustom с едиными токенами позиции и ширины.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,13 +40,13 @@
40
40
  "classnames": "^2.5.1",
41
41
  "react-remove-scroll": "^2.7.2",
42
42
  "tabbable": "^6.5.0",
43
- "@cloud-ru/ds-scroll": "1.0.5",
44
- "@cloud-ru/ds-popup-private": "1.1.10-preview-20b4671f.0",
45
- "@cloud-ru/ds-portal-context": "1.0.2",
46
- "@cloud-ru/ds-tooltip": "1.0.12-preview-20b4671f.0",
47
- "@cloud-ru/ds-adaptive": "1.0.2",
48
- "@cloud-ru/ds-materials": "2.0.1",
49
- "@cloud-ru/ds-utils": "1.2.0",
50
- "@cloud-ru/ds-bottom-sheet": "2.0.1-preview-20b4671f.0"
43
+ "@cloud-ru/ds-adaptive": "^1.0.2",
44
+ "@cloud-ru/ds-materials": "^2.0.1",
45
+ "@cloud-ru/ds-popup-private": "^1.1.10",
46
+ "@cloud-ru/ds-portal-context": "^1.0.2",
47
+ "@cloud-ru/ds-bottom-sheet": "^2.0.1",
48
+ "@cloud-ru/ds-scroll": "^1.0.5",
49
+ "@cloud-ru/ds-tooltip": "^1.0.12",
50
+ "@cloud-ru/ds-utils": "^1.2.0"
51
51
  }
52
52
  }
@@ -54,6 +54,7 @@ function DrawerFrame(props: DrawerCustomProps) {
54
54
  checkElement,
55
55
  width: userWidth,
56
56
  resizable: drawerResizable,
57
+ isResizing,
57
58
  } = useDrawerResize({
58
59
  resizable,
59
60
  position,
@@ -112,6 +113,7 @@ function DrawerFrame(props: DrawerCustomProps) {
112
113
  }}
113
114
  size={isPredefinedWidth ? 'null' : width}
114
115
  {...extractSupportProps(rest)}
116
+ data-resizing={isResizing || undefined}
115
117
  data-content-wrapper
116
118
  data-position={position}
117
119
  data-width={isPredefinedWidth ? width : undefined}
@@ -3,6 +3,7 @@ import { isBrowser } from '@cloud-ru/ds-utils';
3
3
  import { DrawerProps } from '@rc-component/drawer';
4
4
  import { useCallback, useMemo, useRef, useState } from 'react';
5
5
 
6
+ import { RESIZABLE_MAX_FULL } from '../../../constants';
6
7
  import styles from '../styles.module.scss';
7
8
  import { DrawerCustomProps } from '../types';
8
9
 
@@ -19,8 +20,12 @@ type UseDrawerResizeResult = {
19
20
  resizable: DrawerProps['resizable'];
20
21
  /** Ширина ползунка в пикселях */
21
22
  width?: number;
23
+ /** Флаг, указывающий, что дровер в процессе ресайза */
24
+ isResizing: boolean;
22
25
  };
23
26
 
27
+ const getFullMaxWidth = () => (isBrowser() ? Math.max(0, document.documentElement.clientWidth) : Infinity);
28
+
24
29
  export function useDrawerResize({ position, resizable: resizableProp }: UseDrawerResizeParams): UseDrawerResizeResult {
25
30
  const targetRef = useRef<HTMLElement | null>(null);
26
31
  const targetRefCallbackRef = useRef<(node: HTMLElement) => void | undefined>();
@@ -56,31 +61,43 @@ export function useDrawerResize({ position, resizable: resizableProp }: UseDrawe
56
61
  </Tooltip>
57
62
  ) : null;
58
63
 
64
+ const { onResize, onResizeEnd, max, min } = resizableProp ?? {};
65
+ const resizeEnabled = Boolean(resizableProp);
66
+ // Сколько стартовой ширины, которую держит rc-drawer, не помещается в окно: оно могло сузиться, пока дровер
67
+ // был закрыт или открыт. CSS обрезает панель визуально, а rc-drawer считает ресайз от «виртуальной» ширины.
68
+ const excessRef = useRef(0);
69
+
59
70
  const resizable = useMemo(
60
71
  () =>
61
- resizableProp
72
+ resizeEnabled
62
73
  ? ({
63
- onResize: value => {
64
- if (value < (resizableProp?.min || 0)) return;
65
- if (value > (resizableProp?.max || Infinity)) return;
74
+ onResize: rawValue => {
75
+ // Смещение мыши rc-drawer прибавляет к устаревшей стартовой ширине: вычитаем лишнее.
76
+ const limit = max === RESIZABLE_MAX_FULL ? getFullMaxWidth() : max;
77
+ const value = Math.min(rawValue - excessRef.current, limit ?? Infinity);
78
+
79
+ if (value < (min || 0)) return;
66
80
 
67
81
  setWidth(value);
68
82
 
69
- resizableProp?.onResize?.(value);
83
+ onResize?.(value);
70
84
  },
71
85
  onResizeEnd: () => {
72
86
  setOpen(false);
73
87
  setMuted(false);
74
88
 
75
- resizableProp?.onResizeEnd?.(width ?? 0);
89
+ onResizeEnd?.(width ?? 0);
76
90
  },
77
- onResizeStart: () => {
91
+ // rc-drawer передаёт во время выполнения реальную стартовую ширину (в типах аргумента нет).
92
+ onResizeStart: (startSize?: number) => {
93
+ excessRef.current =
94
+ max === RESIZABLE_MAX_FULL && startSize !== undefined ? Math.max(0, startSize - getFullMaxWidth()) : 0;
78
95
  setMuted(true);
79
96
  },
80
97
  } satisfies DrawerProps['resizable'])
81
98
  : undefined,
82
- [resizableProp, width],
99
+ [width, max, min, onResize, onResizeEnd, resizeEnabled],
83
100
  );
84
101
 
85
- return { tooltip, checkElement, resizable, width };
102
+ return { tooltip, checkElement, resizable, width, isResizing: muted };
86
103
  }
@@ -175,6 +175,12 @@ $panelMotions: (
175
175
  // Ширина в макете — это ширина самой панели; горизонтальный паддинг обёртки
176
176
  // (зона перетаскивания) лежит снаружи и не должен её сужать.
177
177
  box-sizing: content-box;
178
+
179
+ // `max-width: 100%` у обёртки считается по контенту, а паддинг (зона перетаскивания и отступ
180
+ // до края) лежит снаружи — вычитаем его, иначе панель упирается в край экрана.
181
+ max-width: calc(
182
+ 100% - #{drawer.$sn-drawer-anatomy-container-vertical-positionLeft-paddingLeft} - #{drawer.$sn-drawer-anatomy-container-vertical-positionLeft-paddingRight}
183
+ );
178
184
  height: 100%;
179
185
 
180
186
  @each $width in $widthSizes {
@@ -206,6 +212,12 @@ $panelMotions: (
206
212
  inset: auto 0 0;
207
213
  }
208
214
 
215
+ &[data-resizing] {
216
+ * {
217
+ pointer-events: none;
218
+ }
219
+ }
220
+
209
221
  @each $placement, $offscreen in $panelMotions {
210
222
  &.#{$panelMotionName}-#{$placement}-enter,
211
223
  &.#{$panelMotionName}-#{$placement}-appear {
@@ -3,7 +3,7 @@ import { WithSupportProps } from '@cloud-ru/ds-utils';
3
3
  import { DrawerProps as RcDrawerProps } from '@rc-component/drawer';
4
4
  import { PropsWithChildren, ReactElement } from 'react';
5
5
 
6
- import { Position, Width } from '../../types';
6
+ import { Position, ResizableMax, Width } from '../../types';
7
7
 
8
8
  export type DrawerCustomProps = WithSupportProps<
9
9
  PropsWithChildren<{
@@ -60,8 +60,11 @@ export type DrawerCustomProps = WithSupportProps<
60
60
  resizable?: {
61
61
  /** Минимальная доступная ширина */
62
62
  min: number;
63
- /** Максимальная доступная ширина */
64
- max?: number;
63
+ /**
64
+ * Максимальная доступная ширина: число в px либо `'full'` — вся ширина окна за вычетом 48px
65
+ * (двойной отступ обёртки).
66
+ */
67
+ max?: ResizableMax;
65
68
  /** Ширина по умолчанию */
66
69
  default?: number;
67
70
  /** Колбэк на изменение размера */
package/src/constants.ts CHANGED
@@ -29,3 +29,9 @@ export const POSITION = {
29
29
  Top: 'top',
30
30
  Bottom: 'bottom',
31
31
  } as const;
32
+
33
+ /** Значение `resizable.max`: растягивать до ширины окна за вычетом двойного отступа обёртки. */
34
+ export const RESIZABLE_MAX_FULL = 'full';
35
+
36
+ /** Горизонтальный отступ `content-wrapper`. */
37
+ export const FULL_WIDTH_OFFSET = 24;
package/src/types.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ValueOf } from '@cloud-ru/ds-utils';
2
2
 
3
- import { POSITION, WIDTH } from './constants';
3
+ import { POSITION, RESIZABLE_MAX_FULL, WIDTH } from './constants';
4
4
 
5
5
  export type Width = ValueOf<typeof WIDTH>;
6
6
  export type Position = ValueOf<typeof POSITION>;
7
+ export type ResizableMax = number | typeof RESIZABLE_MAX_FULL;