@cloud-ru/ds-uikit-product-welcome-tour 1.2.0 → 1.2.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 1.2.1 (2026-09-06)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **FF-8762:** swallow overlay presses so page layers stay open during the tour ([3812a8f](https://github.com/cloud-ru-tech/snack-v2/commit/3812a8f7a207a0f61e9d85d890e58a5ba5762192))
11
+
6
12
  # 1.2.0 (2026-09-03)
7
13
 
8
14
  ### Features
@@ -110,6 +110,21 @@ function WelcomeTour({ steps, open, defaultOpen = false, onOpenChange, stepIndex
110
110
  break;
111
111
  }
112
112
  }, [steps, onStepChange, setOpen]);
113
+ // `overlayClickAction: false` отменяет только реакцию самого тура: нажатие всё равно
114
+ // доходит до документа, и раскрытый список, вокруг которого построен шаг, закрывается
115
+ // как от клика мимо себя. Гасим на подхвате в портале, вместе с уводом фокуса.
116
+ (0, react_1.useEffect)(() => {
117
+ if (!isOpen || !portalElement)
118
+ return;
119
+ const swallowPress = (event) => {
120
+ if (!(0, utils_2.isOverlayTarget)(event.target))
121
+ return;
122
+ event.stopPropagation();
123
+ event.preventDefault();
124
+ };
125
+ constants_2.OVERLAY_PRESS_EVENTS.forEach(type => portalElement.addEventListener(type, swallowPress, true));
126
+ return () => constants_2.OVERLAY_PRESS_EVENTS.forEach(type => portalElement.removeEventListener(type, swallowPress, true));
127
+ }, [isOpen, portalElement]);
113
128
  // Esc завершает тур целиком, как у остальных оверлеев ДС; `dismissKeyAction` движка
114
129
  // закрывает только текущий шаг, поэтому он выключен. Уступаем чужому слою: шаг тура
115
130
  // может открыть диалог, и Esc тогда адресован ему.
@@ -27,3 +27,7 @@ export declare const TOUR_COLORS: {
27
27
  * расчётной уезжает за край экрана.
28
28
  */
29
29
  export declare const DEFAULT_HINT_WIDTH = 480;
30
+ /** Класс затемнения у react-joyride: отличить его от подсказки в общем портале больше нечем. */
31
+ export declare const OVERLAY_CLASS_NAME = "react-joyride__overlay";
32
+ /** События, по которым закрываются слои страницы: floating-ui слушает `pointerdown`, прочие — `mousedown` и `click`. */
33
+ export declare const OVERLAY_PRESS_EVENTS: readonly ["pointerdown", "mousedown", "click"];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_HINT_WIDTH = exports.TOUR_COLORS = exports.TOUR_GEOMETRY = void 0;
3
+ exports.OVERLAY_PRESS_EVENTS = exports.OVERLAY_CLASS_NAME = exports.DEFAULT_HINT_WIDTH = exports.TOUR_COLORS = exports.TOUR_GEOMETRY = void 0;
4
4
  /**
5
5
  * Геометрия для react-joyride. Движок считает позиции и рисует стрелку инлайн-стилями,
6
6
  * поэтому нужны числа, а не CSS-переменные. Рядом с каждым значением — переменная
@@ -36,3 +36,7 @@ exports.TOUR_COLORS = {
36
36
  * расчётной уезжает за край экрана.
37
37
  */
38
38
  exports.DEFAULT_HINT_WIDTH = 480;
39
+ /** Класс затемнения у react-joyride: отличить его от подсказки в общем портале больше нечем. */
40
+ exports.OVERLAY_CLASS_NAME = 'react-joyride__overlay';
41
+ /** События, по которым закрываются слои страницы: floating-ui слушает `pointerdown`, прочие — `mousedown` и `click`. */
42
+ exports.OVERLAY_PRESS_EVENTS = ['pointerdown', 'mousedown', 'click'];
@@ -7,6 +7,8 @@ type ToJoyrideStepsParams = {
7
7
  buttons: TourButton[];
8
8
  showStepIndicator: boolean;
9
9
  };
10
+ /** Пришло ли нажатие в затемнение, а не в подсказку: в портале лежат они оба. */
11
+ export declare function isOverlayTarget(target: EventTarget | null): boolean;
10
12
  /**
11
13
  * Приводит статус движка к публичному `TourStatus`: наружу сообщаются только терминальные
12
14
  * `finished` и `skipped`, а каст пропустил бы любую строку.
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isOverlayTarget = isOverlayTarget;
3
4
  exports.toTourStatus = toTourStatus;
4
5
  exports.resolveLabels = resolveLabels;
5
6
  exports.toJoyrideSteps = toJoyrideSteps;
@@ -7,6 +8,10 @@ const constants_1 = require("../../constants");
7
8
  const constants_2 = require("./constants");
8
9
  const spotlight_1 = require("./spotlight");
9
10
  const targets_1 = require("./targets");
11
+ /** Пришло ли нажатие в затемнение, а не в подсказку: в портале лежат они оба. */
12
+ function isOverlayTarget(target) {
13
+ return target instanceof Element && Boolean(target.closest(`.${constants_2.OVERLAY_CLASS_NAME}`));
14
+ }
10
15
  /**
11
16
  * Приводит статус движка к публичному `TourStatus`: наружу сообщаются только терминальные
12
17
  * `finished` и `skipped`, а каст пропустил бы любую строку.
@@ -7,9 +7,9 @@ import { ACTIONS, EVENTS, Joyride } from 'react-joyride';
7
7
  import { TOUR_BUTTON, TOUR_STATUS } from '../../constants.js';
8
8
  import { TourHint } from '../../helperComponents/index.js';
9
9
  import { welcomeTourLocale } from '../../locale/index.js';
10
- import { TOUR_COLORS, TOUR_GEOMETRY } from './constants.js';
10
+ import { OVERLAY_PRESS_EVENTS, TOUR_COLORS, TOUR_GEOMETRY } from './constants.js';
11
11
  import { removeUnionElement } from './spotlight.js';
12
- import { toJoyrideSteps, toTourStatus } from './utils.js';
12
+ import { isOverlayTarget, toJoyrideSteps, toTourStatus } from './utils.js';
13
13
  const DEFAULT_BUTTONS = [TOUR_BUTTON.Back, TOUR_BUTTON.Primary, TOUR_BUTTON.Skip];
14
14
  const FLOATING_OPTIONS = { shiftOptions: { crossAxis: true, padding: 16 } };
15
15
  /**
@@ -107,6 +107,21 @@ export function WelcomeTour({ steps, open, defaultOpen = false, onOpenChange, st
107
107
  break;
108
108
  }
109
109
  }, [steps, onStepChange, setOpen]);
110
+ // `overlayClickAction: false` отменяет только реакцию самого тура: нажатие всё равно
111
+ // доходит до документа, и раскрытый список, вокруг которого построен шаг, закрывается
112
+ // как от клика мимо себя. Гасим на подхвате в портале, вместе с уводом фокуса.
113
+ useEffect(() => {
114
+ if (!isOpen || !portalElement)
115
+ return;
116
+ const swallowPress = (event) => {
117
+ if (!isOverlayTarget(event.target))
118
+ return;
119
+ event.stopPropagation();
120
+ event.preventDefault();
121
+ };
122
+ OVERLAY_PRESS_EVENTS.forEach(type => portalElement.addEventListener(type, swallowPress, true));
123
+ return () => OVERLAY_PRESS_EVENTS.forEach(type => portalElement.removeEventListener(type, swallowPress, true));
124
+ }, [isOpen, portalElement]);
110
125
  // Esc завершает тур целиком, как у остальных оверлеев ДС; `dismissKeyAction` движка
111
126
  // закрывает только текущий шаг, поэтому он выключен. Уступаем чужому слою: шаг тура
112
127
  // может открыть диалог, и Esc тогда адресован ему.
@@ -27,3 +27,7 @@ export declare const TOUR_COLORS: {
27
27
  * расчётной уезжает за край экрана.
28
28
  */
29
29
  export declare const DEFAULT_HINT_WIDTH = 480;
30
+ /** Класс затемнения у react-joyride: отличить его от подсказки в общем портале больше нечем. */
31
+ export declare const OVERLAY_CLASS_NAME = "react-joyride__overlay";
32
+ /** События, по которым закрываются слои страницы: floating-ui слушает `pointerdown`, прочие — `mousedown` и `click`. */
33
+ export declare const OVERLAY_PRESS_EVENTS: readonly ["pointerdown", "mousedown", "click"];
@@ -33,3 +33,7 @@ export const TOUR_COLORS = {
33
33
  * расчётной уезжает за край экрана.
34
34
  */
35
35
  export const DEFAULT_HINT_WIDTH = 480;
36
+ /** Класс затемнения у react-joyride: отличить его от подсказки в общем портале больше нечем. */
37
+ export const OVERLAY_CLASS_NAME = 'react-joyride__overlay';
38
+ /** События, по которым закрываются слои страницы: floating-ui слушает `pointerdown`, прочие — `mousedown` и `click`. */
39
+ export const OVERLAY_PRESS_EVENTS = ['pointerdown', 'mousedown', 'click'];
@@ -7,6 +7,8 @@ type ToJoyrideStepsParams = {
7
7
  buttons: TourButton[];
8
8
  showStepIndicator: boolean;
9
9
  };
10
+ /** Пришло ли нажатие в затемнение, а не в подсказку: в портале лежат они оба. */
11
+ export declare function isOverlayTarget(target: EventTarget | null): boolean;
10
12
  /**
11
13
  * Приводит статус движка к публичному `TourStatus`: наружу сообщаются только терминальные
12
14
  * `finished` и `skipped`, а каст пропустил бы любую строку.
@@ -1,7 +1,11 @@
1
1
  import { TOUR_STATUS } from '../../constants.js';
2
- import { DEFAULT_HINT_WIDTH } from './constants.js';
2
+ import { DEFAULT_HINT_WIDTH, OVERLAY_CLASS_NAME } from './constants.js';
3
3
  import { getUnionElement } from './spotlight.js';
4
4
  import { prepareStep, resolveTargets } from './targets.js';
5
+ /** Пришло ли нажатие в затемнение, а не в подсказку: в портале лежат они оба. */
6
+ export function isOverlayTarget(target) {
7
+ return target instanceof Element && Boolean(target.closest(`.${OVERLAY_CLASS_NAME}`));
8
+ }
5
9
  /**
6
10
  * Приводит статус движка к публичному `TourStatus`: наружу сообщаются только терминальные
7
11
  * `finished` и `skipped`, а каст пропустил бы любую строку.