@astral/ui 4.83.0 → 4.85.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 (110) hide show
  1. package/_mocks/LoggerMock/LoggerMock.d.ts +16 -0
  2. package/_mocks/LoggerMock/LoggerMock.js +18 -0
  3. package/_mocks/LoggerMock/index.d.ts +1 -0
  4. package/_mocks/LoggerMock/index.js +1 -0
  5. package/components/ConfigProvider/ConfigProvider.d.ts +7 -1
  6. package/components/ConfigProvider/ConfigProvider.js +14 -8
  7. package/components/DashboardLayout/Header/useLogic/useLogic.d.ts +1 -1
  8. package/components/HintPopover/HintPopover.d.ts +6 -0
  9. package/components/HintPopover/HintPopover.js +23 -0
  10. package/components/HintPopover/constants.d.ts +4 -0
  11. package/components/HintPopover/constants.js +5 -0
  12. package/components/HintPopover/index.d.ts +2 -0
  13. package/components/HintPopover/index.js +1 -0
  14. package/components/HintPopover/public.d.ts +2 -0
  15. package/components/HintPopover/public.js +1 -0
  16. package/components/HintPopover/styles.d.ts +38 -0
  17. package/components/HintPopover/styles.js +69 -0
  18. package/components/HintPopover/types.d.ts +24 -0
  19. package/components/HintPopover/types.js +1 -0
  20. package/components/NewPopover/NewPopover.d.ts +1 -1
  21. package/components/NewPopover/NewPopover.js +5 -4
  22. package/components/NewPopover/constants.d.ts +11 -0
  23. package/components/NewPopover/constants.js +11 -0
  24. package/components/NewPopover/styles.js +35 -30
  25. package/components/NewPopover/types.d.ts +5 -0
  26. package/components/NewPopover/useLogic/useLogic.d.ts +4 -3
  27. package/components/NewPopover/useLogic/useLogic.js +17 -8
  28. package/components/NewPopover/useLogic/utils/getArrowPadding/getArrowPadding.d.ts +14 -0
  29. package/components/NewPopover/useLogic/utils/getArrowPadding/getArrowPadding.js +12 -0
  30. package/components/NewPopover/useLogic/utils/getArrowPadding/index.d.ts +1 -0
  31. package/components/NewPopover/useLogic/utils/getArrowPadding/index.js +1 -0
  32. package/components/NewPopover/useLogic/utils/getPopoverCssVars/getPopoverCssVars.d.ts +2 -0
  33. package/components/NewPopover/useLogic/utils/getPopoverCssVars/getPopoverCssVars.js +6 -0
  34. package/components/NewPopover/useLogic/utils/getPopoverCssVars/index.d.ts +1 -0
  35. package/components/NewPopover/useLogic/utils/getPopoverCssVars/index.js +1 -0
  36. package/components/NewPopover/useLogic/utils/resolveAnchorNode/resolveAnchorNode.d.ts +6 -0
  37. package/components/NewPopover/useLogic/utils/resolveAnchorNode/resolveAnchorNode.js +8 -0
  38. package/components/Onboarding/Onboarding.js +13 -3
  39. package/components/Onboarding/core/ExitController/ExitController.d.ts +3 -1
  40. package/components/Onboarding/core/ExitController/ExitController.js +4 -1
  41. package/components/Onboarding/core/OnboardingTour/OnboardingTour.d.ts +3 -1
  42. package/components/Onboarding/core/OnboardingTour/OnboardingTour.js +25 -4
  43. package/components/Onboarding/core/ProgressStorage/ProgressStorage.d.ts +3 -1
  44. package/components/Onboarding/core/ProgressStorage/ProgressStorage.js +8 -4
  45. package/components/Onboarding/ui/StepStrategy/steps/HighlightStep/HighlightStep.js +3 -4
  46. package/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.d.ts +6 -22
  47. package/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.js +2 -45
  48. package/node/_mocks/LoggerMock/LoggerMock.d.ts +16 -0
  49. package/node/_mocks/LoggerMock/LoggerMock.js +23 -0
  50. package/node/_mocks/LoggerMock/index.d.ts +1 -0
  51. package/node/_mocks/LoggerMock/index.js +17 -0
  52. package/node/components/ConfigProvider/ConfigProvider.d.ts +7 -1
  53. package/node/components/ConfigProvider/ConfigProvider.js +13 -7
  54. package/node/components/DashboardLayout/Header/useLogic/useLogic.d.ts +1 -1
  55. package/node/components/HintPopover/HintPopover.d.ts +6 -0
  56. package/node/components/HintPopover/HintPopover.js +27 -0
  57. package/node/components/HintPopover/constants.d.ts +4 -0
  58. package/node/components/HintPopover/constants.js +8 -0
  59. package/node/components/HintPopover/index.d.ts +2 -0
  60. package/node/components/HintPopover/index.js +5 -0
  61. package/node/components/HintPopover/public.d.ts +2 -0
  62. package/node/components/HintPopover/public.js +5 -0
  63. package/node/components/HintPopover/styles.d.ts +38 -0
  64. package/node/components/HintPopover/styles.js +72 -0
  65. package/node/components/HintPopover/types.d.ts +24 -0
  66. package/node/components/HintPopover/types.js +2 -0
  67. package/node/components/NewPopover/NewPopover.d.ts +1 -1
  68. package/node/components/NewPopover/NewPopover.js +5 -4
  69. package/node/components/NewPopover/constants.d.ts +11 -0
  70. package/node/components/NewPopover/constants.js +12 -1
  71. package/node/components/NewPopover/styles.js +34 -29
  72. package/node/components/NewPopover/types.d.ts +5 -0
  73. package/node/components/NewPopover/useLogic/useLogic.d.ts +4 -3
  74. package/node/components/NewPopover/useLogic/useLogic.js +18 -9
  75. package/node/components/NewPopover/useLogic/utils/getArrowPadding/getArrowPadding.d.ts +14 -0
  76. package/node/components/NewPopover/useLogic/utils/getArrowPadding/getArrowPadding.js +16 -0
  77. package/node/components/NewPopover/useLogic/utils/getArrowPadding/index.d.ts +1 -0
  78. package/node/components/NewPopover/useLogic/utils/getArrowPadding/index.js +17 -0
  79. package/node/components/NewPopover/useLogic/utils/getPopoverCssVars/getPopoverCssVars.d.ts +2 -0
  80. package/node/components/NewPopover/useLogic/utils/getPopoverCssVars/getPopoverCssVars.js +10 -0
  81. package/node/components/NewPopover/useLogic/utils/getPopoverCssVars/index.d.ts +1 -0
  82. package/node/components/NewPopover/useLogic/utils/getPopoverCssVars/index.js +5 -0
  83. package/node/components/NewPopover/useLogic/utils/resolveAnchorNode/resolveAnchorNode.d.ts +6 -0
  84. package/node/components/NewPopover/{utils → useLogic/utils/resolveAnchorNode}/resolveAnchorNode.js +2 -2
  85. package/node/components/Onboarding/Onboarding.js +13 -3
  86. package/node/components/Onboarding/core/ExitController/ExitController.d.ts +3 -1
  87. package/node/components/Onboarding/core/ExitController/ExitController.js +4 -1
  88. package/node/components/Onboarding/core/OnboardingTour/OnboardingTour.d.ts +3 -1
  89. package/node/components/Onboarding/core/OnboardingTour/OnboardingTour.js +25 -4
  90. package/node/components/Onboarding/core/ProgressStorage/ProgressStorage.d.ts +3 -1
  91. package/node/components/Onboarding/core/ProgressStorage/ProgressStorage.js +8 -4
  92. package/node/components/Onboarding/ui/StepStrategy/steps/HighlightStep/HighlightStep.js +1 -2
  93. package/node/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.d.ts +6 -22
  94. package/node/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.js +3 -46
  95. package/node/services/Logger/Logger.d.ts +45 -0
  96. package/node/services/Logger/Logger.js +72 -0
  97. package/node/services/Logger/index.d.ts +10 -0
  98. package/node/services/Logger/index.js +17 -0
  99. package/package.json +1 -1
  100. package/services/Logger/Logger.d.ts +45 -0
  101. package/services/Logger/Logger.js +68 -0
  102. package/services/Logger/index.d.ts +10 -0
  103. package/services/Logger/index.js +12 -0
  104. package/components/NewPopover/utils/resolveAnchorNode.d.ts +0 -6
  105. package/components/NewPopover/utils/resolveAnchorNode.js +0 -8
  106. package/node/components/NewPopover/utils/resolveAnchorNode.d.ts +0 -6
  107. /package/components/NewPopover/{utils → useLogic/utils/resolveAnchorNode}/index.d.ts +0 -0
  108. /package/components/NewPopover/{utils → useLogic/utils/resolveAnchorNode}/index.js +0 -0
  109. /package/node/components/NewPopover/{utils → useLogic/utils/resolveAnchorNode}/index.d.ts +0 -0
  110. /package/node/components/NewPopover/{utils → useLogic/utils/resolveAnchorNode}/index.js +0 -0
@@ -0,0 +1,12 @@
1
+ import { ARROW_BASE, ARROW_MIN_PADDING, ARROW_MIN_TRACK, ARROW_PADDING, } from '../../../constants';
2
+ /**
3
+ * Фиксированный padding отобразил бы стрелку на скруглённый угол
4
+ */
5
+ export const getArrowPadding = ({ popper, placement }) => {
6
+ const isVertical = placement.startsWith('left') || placement.startsWith('right');
7
+ const side = isVertical ? popper.height : popper.width;
8
+ const free = Math.max(0, side - ARROW_BASE);
9
+ const centerPadding = free / 2;
10
+ const desiredPadding = Math.min(ARROW_PADDING, Math.max(ARROW_MIN_PADDING, (free - ARROW_MIN_TRACK) / 2));
11
+ return Math.min(centerPadding, desiredPadding);
12
+ };
@@ -0,0 +1 @@
1
+ export * from './getArrowPadding';
@@ -0,0 +1 @@
1
+ export * from './getArrowPadding';
@@ -0,0 +1,2 @@
1
+ import type { CSSProperties } from 'react';
2
+ export declare const getPopoverCssVars: () => CSSProperties;
@@ -0,0 +1,6 @@
1
+ export const getPopoverCssVars = () => ({
2
+ // Единая тень на весь силуэт (поповер + уголок стрелки).
3
+ // Эквивалент elevation[200], переведённого в drop-shadow(): box-shadow
4
+ // не умеет обводить единый силуэт поповера со стрелкой.
5
+ '--drop-shadow': 'drop-shadow(0px 0px 1px #072D574F) drop-shadow(0px 3px 5px #072D5733)',
6
+ });
@@ -0,0 +1 @@
1
+ export { getPopoverCssVars } from './getPopoverCssVars';
@@ -0,0 +1 @@
1
+ export { getPopoverCssVars } from './getPopoverCssVars';
@@ -0,0 +1,6 @@
1
+ import { type NewPopoverAnchorEl } from '../../../types';
2
+ /**
3
+ * Приводит anchorEl к DOM-ноде (virtual-anchor → null), чтобы исключить якорь из
4
+ * клика-вне — иначе клик по нему закрывал бы поповер.
5
+ */
6
+ export declare const resolveAnchorNode: (anchorEl: NewPopoverAnchorEl | undefined) => Node | null;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Приводит anchorEl к DOM-ноде (virtual-anchor → null), чтобы исключить якорь из
3
+ * клика-вне — иначе клик по нему закрывал бы поповер.
4
+ */
5
+ export const resolveAnchorNode = (anchorEl) => {
6
+ const value = typeof anchorEl === 'function' ? anchorEl() : anchorEl;
7
+ return value instanceof Node ? value : null;
8
+ };
@@ -1,3 +1,4 @@
1
+ import { createLogger } from '../../services/Logger';
1
2
  import { ErrorService } from '../services/ErrorService';
2
3
  import { ActiveStepStore } from './core/ActiveStepStore';
3
4
  import { OnboardingTour } from './core/OnboardingTour';
@@ -18,11 +19,20 @@ export class Onboarding {
18
19
  * Создаёт тур.
19
20
  */
20
21
  this.createTour = (tourConfig) => {
22
+ const logger = createLogger('Onboarding', {
23
+ context: { tourName: tourConfig.name },
24
+ });
21
25
  // Fail-fast на некорректной конфигурации до сборки тура.
22
- validateTourConfig(tourConfig);
26
+ try {
27
+ validateTourConfig(tourConfig);
28
+ }
29
+ catch (error) {
30
+ logger.error('Некорректная конфигурация тура', error);
31
+ throw error;
32
+ }
23
33
  // Ключ хранилища уникален на тур, поэтому стратегия строится на createTour.
24
- const progressStorage = new ProgressStorage(this.saveStrategyFactory(tourConfig.name));
25
- return new OnboardingTour(tourConfig, progressStorage, this.activeStepStore, this.errorService);
34
+ const progressStorage = new ProgressStorage(this.saveStrategyFactory(tourConfig.name), logger);
35
+ return new OnboardingTour(tourConfig, progressStorage, this.activeStepStore, this.errorService, logger);
26
36
  };
27
37
  /**
28
38
  * Закрывает показанный сейчас тур.
@@ -1,3 +1,4 @@
1
+ import { type Logger } from '../../../../services/Logger';
1
2
  export type ExitControllerParams = {
2
3
  /**
3
4
  * Фактическое закрытие тура. Вызывается только после подтверждения в
@@ -20,8 +21,9 @@ export type ExitControllerParams = {
20
21
  */
21
22
  export declare class ExitController {
22
23
  private readonly params;
24
+ private readonly logger;
23
25
  private confirming;
24
- constructor(params: ExitControllerParams);
26
+ constructor(params: ExitControllerParams, logger: Logger);
25
27
  /**
26
28
  * Идёт ли запрос подтверждения выхода.
27
29
  */
@@ -6,8 +6,9 @@
6
6
  * (ConfirmExit); закрытие происходит только по confirm.
7
7
  */
8
8
  export class ExitController {
9
- constructor(params) {
9
+ constructor(params, logger) {
10
10
  this.params = params;
11
+ this.logger = logger;
11
12
  this.confirming = false;
12
13
  /**
13
14
  * Запрос на закрытие — показывает ConfirmExit. Повторные вызовы во время
@@ -17,6 +18,7 @@ export class ExitController {
17
18
  if (this.confirming) {
18
19
  return;
19
20
  }
21
+ this.logger.debug('Запрошен выход из тура, показано подтверждение');
20
22
  this.confirming = true;
21
23
  this.params.onChange();
22
24
  };
@@ -37,6 +39,7 @@ export class ExitController {
37
39
  if (!this.confirming) {
38
40
  return;
39
41
  }
42
+ this.logger.debug('Выход из тура отменён');
40
43
  this.confirming = false;
41
44
  this.params.onChange();
42
45
  };
@@ -1,3 +1,4 @@
1
+ import { type Logger } from '../../../../services/Logger';
1
2
  import { type ErrorService } from '../../../services/ErrorService';
2
3
  import { type OnboardingTourConfig, type Step, type StepHandles, type TourProgress } from '../../types';
3
4
  import { type ActiveStepStore } from '../ActiveStepStore';
@@ -13,6 +14,7 @@ export declare class OnboardingTour<TSteps extends readonly Step[] = readonly St
13
14
  private readonly progressStorage;
14
15
  private readonly activeStepStore;
15
16
  private readonly errorService;
17
+ private readonly logger;
16
18
  private readonly navigator;
17
19
  private readonly resolver;
18
20
  private readonly stepsController;
@@ -22,7 +24,7 @@ export declare class OnboardingTour<TSteps extends readonly Step[] = readonly St
22
24
  * Типобезопасный доступ к шагам по их именам.
23
25
  */
24
26
  readonly steps: StepHandles<TSteps>;
25
- constructor(config: OnboardingTourConfig<TSteps>, progressStorage: ProgressStorage, activeStepStore: ActiveStepStore, errorService: ErrorService);
27
+ constructor(config: OnboardingTourConfig<TSteps>, progressStorage: ProgressStorage, activeStepStore: ActiveStepStore, errorService: ErrorService, logger: Logger);
26
28
  /**
27
29
  * Запуск тура с первого шага.
28
30
  */
@@ -9,11 +9,12 @@ import { StepsController } from '../StepsController';
9
9
  * состояние активного шага в store своего экземпляра онбординга.
10
10
  */
11
11
  export class OnboardingTour {
12
- constructor(config, progressStorage, activeStepStore, errorService) {
12
+ constructor(config, progressStorage, activeStepStore, errorService, logger) {
13
13
  this.config = config;
14
14
  this.progressStorage = progressStorage;
15
15
  this.activeStepStore = activeStepStore;
16
16
  this.errorService = errorService;
17
+ this.logger = logger;
17
18
  this.status = 'idle';
18
19
  /**
19
20
  * Запуск тура с первого шага.
@@ -21,8 +22,10 @@ export class OnboardingTour {
21
22
  this.start = async () => {
22
23
  const persisted = await this.progressStorage.load();
23
24
  if (persisted?.isDismissed) {
25
+ this.logger.info('Тур пропущен: ранее закрыт пользователем');
24
26
  return;
25
27
  }
28
+ this.logger.info('Тур запущен');
26
29
  this.exitController.reset();
27
30
  this.navigator.reset();
28
31
  this.enterCurrentStep();
@@ -32,8 +35,10 @@ export class OnboardingTour {
32
35
  */
33
36
  this.resume = () => {
34
37
  if (this.status !== 'waiting-resume') {
38
+ this.logger.debug('resume проигнорирован: тур не на паузе');
35
39
  return;
36
40
  }
41
+ this.logger.debug('Тур возобновлён после паузы');
37
42
  this.status = 'running';
38
43
  this.publishStep();
39
44
  };
@@ -42,6 +47,7 @@ export class OnboardingTour {
42
47
  */
43
48
  this.restore = async () => {
44
49
  const persisted = await this.progressStorage.load();
50
+ this.logger.info('Тур восстановлен');
45
51
  this.exitController.reset();
46
52
  this.navigator.reset();
47
53
  // Пройденный тур переигрываем с начала; иначе продолжаем с сохранённого шага.
@@ -148,6 +154,7 @@ export class OnboardingTour {
148
154
  */
149
155
  this.resetProgress = async () => {
150
156
  await this.progressStorage.reset();
157
+ this.logger.debug('Прогресс тура сброшен');
151
158
  };
152
159
  /**
153
160
  * Возвращает сохранённый прогресс тура
@@ -167,12 +174,14 @@ export class OnboardingTour {
167
174
  isDismissed: true,
168
175
  });
169
176
  }
177
+ this.logger.info('Тур закрыт', { reason });
170
178
  this.status = 'idle';
171
179
  this.exitController.reset();
172
180
  this.releaseStep();
173
181
  this.config.onClose?.(reason);
174
182
  };
175
183
  this.handleError = (error) => {
184
+ this.logger.error('Ошибка тура', error);
176
185
  this.errorService.captureException(error, {
177
186
  place: 'Onboarding',
178
187
  tourName: this.config.name,
@@ -185,17 +194,22 @@ export class OnboardingTour {
185
194
  this.resolver = new StepResolver(config);
186
195
  this.stepsController = new StepsController(config.steps, (stepName) => this.refreshStep(stepName));
187
196
  this.steps = this.stepsController.handles;
188
- this.exitController = new ExitController({
197
+ const exitParams = {
189
198
  close: () => this.exit('dismissed'),
190
199
  onChange: () => this.publishStep(),
191
- });
200
+ };
201
+ this.exitController = new ExitController(exitParams, this.logger);
192
202
  }
193
203
  /**
194
204
  * Разрешена ли навигация сейчас: тур показан на экране и владеет активным
195
205
  * шагом store
196
206
  */
197
207
  canNavigate() {
198
- return this.status === 'running' && this.activeStepStore.isActiveTour(this);
208
+ const isNavigable = this.status === 'running' && this.activeStepStore.isActiveTour(this);
209
+ if (!isNavigable) {
210
+ this.logger.debug('Навигация проигнорирована: тур неактивен');
211
+ }
212
+ return isNavigable;
199
213
  }
200
214
  /**
201
215
  * Переход к текущему шагу. Шаг с `waitForResume` ставит тур в ожидание
@@ -204,16 +218,23 @@ export class OnboardingTour {
204
218
  enterCurrentStep() {
205
219
  // Показывать нечего: у тура нет ни одного видимого (не-пропущенного) шага.
206
220
  if (this.navigator.total === 0) {
221
+ this.logger.warn('Нет ни одного видимого шага, тур не показан');
207
222
  this.status = 'idle';
208
223
  this.releaseStep();
209
224
  return;
210
225
  }
211
226
  const step = this.navigator.current;
212
227
  if (step.waitForResume) {
228
+ this.logger.debug('Ожидание resume', { step: step.name });
213
229
  this.status = 'waiting-resume';
214
230
  this.releaseStep();
215
231
  return;
216
232
  }
233
+ this.logger.debug('Показан шаг', {
234
+ step: step.name,
235
+ position: this.navigator.position,
236
+ total: this.navigator.total,
237
+ });
217
238
  this.status = 'running';
218
239
  this.publishStep();
219
240
  }
@@ -1,3 +1,4 @@
1
+ import { type Logger } from '../../../../services/Logger';
1
2
  import { type SaveStrategy, type TourProgress } from '../../types';
2
3
  /**
3
4
  * Обёртка над переданной SaveStrategy, изолирующая тур от исключений хранилища
@@ -9,8 +10,9 @@ import { type SaveStrategy, type TourProgress } from '../../types';
9
10
  */
10
11
  export declare class ProgressStorage {
11
12
  private readonly strategy;
13
+ private readonly logger;
12
14
  private saving;
13
- constructor(strategy: SaveStrategy);
15
+ constructor(strategy: SaveStrategy, logger: Logger);
14
16
  load(): Promise<TourProgress | null>;
15
17
  save(state: TourProgress): Promise<void>;
16
18
  /**
@@ -15,8 +15,9 @@ const CLEARED_STATE = {
15
15
  * `load()` ждёт текущее сохранение, поэтому читает уже применённое состояние.
16
16
  */
17
17
  export class ProgressStorage {
18
- constructor(strategy) {
18
+ constructor(strategy, logger) {
19
19
  this.strategy = strategy;
20
+ this.logger = logger;
20
21
  this.saving = Promise.resolve();
21
22
  }
22
23
  async load() {
@@ -25,7 +26,9 @@ export class ProgressStorage {
25
26
  try {
26
27
  return await this.strategy.load();
27
28
  }
28
- catch {
29
+ catch (error) {
30
+ // Ошибка хранилища не должна ронять тур, но остаётся видимой в логах.
31
+ this.logger.warn('Ошибка чтения прогресса из хранилища', error);
29
32
  return null;
30
33
  }
31
34
  }
@@ -49,8 +52,9 @@ export class ProgressStorage {
49
52
  try {
50
53
  await write();
51
54
  }
52
- catch {
53
- // Ошибка хранилища не должна ронять тур;
55
+ catch (error) {
56
+ // Ошибка хранилища не должна ронять тур, но остаётся видимой в логах.
57
+ this.logger.warn('Ошибка записи прогресса в хранилище', error);
54
58
  }
55
59
  });
56
60
  return this.saving;
@@ -1,8 +1,7 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { CrossOutlineMd } from '../../../../../../icons/CrossOutlineMd';
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
2
  import { Typography } from '../../../../../Typography';
4
3
  import { StepFooter } from '../StepFooter';
5
- import { CloseButton, Content, PopoverBody, StepPopover } from './styles';
4
+ import { StepPopover } from './styles';
6
5
  import { useLogic } from './useLogic';
7
6
  /**
8
7
  * Шаг с подсветкой: popover рядом с hole.
@@ -15,5 +14,5 @@ export const HighlightStep = ({ activeStep, isClosing, geometry, }) => {
15
14
  if (!anchorEl) {
16
15
  return null;
17
16
  }
18
- return (_jsx(_Fragment, { children: shouldShowPopover && (_jsx(StepPopover, { isOpen: isOpen, disableAutoFocus: true, hasArrow: true, anchorEl: anchorEl, placement: step.position, children: _jsxs(PopoverBody, { ref: setPopoverNode, tabIndex: -1, children: [_jsx(CloseButton, { variant: "text", onClick: handleClose, "aria-label": "\u0417\u0430\u043A\u0440\u044B\u0442\u044C", children: _jsx(CrossOutlineMd, {}) }), _jsxs(Content, { children: [step.title && (_jsx(Typography, { component: "div", variant: "h5", children: step.title })), step.content && (_jsx(Typography, { component: "div", variant: "body1", color: "grey", colorIntensity: "900", children: step.content }))] }), _jsx(StepFooter, { activeStep: activeStep })] }) })) }));
17
+ return (_jsx(_Fragment, { children: shouldShowPopover && (_jsx(StepPopover, { variant: "light", isOpen: isOpen, disableAutoFocus: true, anchorEl: anchorEl, placement: step.position, onClose: handleClose, contentRef: setPopoverNode, title: step.title, action: _jsx(StepFooter, { activeStep: activeStep }), children: step.content && (_jsx(Typography, { component: "div", variant: "body1", color: "grey", colorIntensity: "900", children: step.content })) })) }));
19
18
  };
@@ -1,25 +1,9 @@
1
1
  /// <reference types="react" />
2
- /**
3
- * Popover шага, z-index над overlay, быстрое закрытие.
4
- */
5
- export declare const StepPopover: import("../../../../../styled").StyledComponent<import("../../../../../NewPopover").NewPopoverProps & {
2
+ export declare const StepPopover: import("../../../../../styled").StyledComponent<Omit<import("../../../../../NewPopover").NewPopoverProps, "title" | "hasArrow" | "disableMobileDrawer"> & {
3
+ variant?: import("../../../../../HintPopover").HintPopoverVariant | undefined;
4
+ title?: import("react").ReactNode;
5
+ action?: import("react").ReactNode;
6
+ contentRef?: import("react").Ref<HTMLDivElement> | undefined;
7
+ } & {
6
8
  theme?: import("@emotion/react").Theme | undefined;
7
9
  }, {}, {}>;
8
- export declare const PopoverBody: import("../../../../../styled").StyledComponent<{
9
- theme?: import("@emotion/react").Theme | undefined;
10
- as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
11
- }, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
12
- export declare const CloseButton: import("../../../../../styled").StyledComponent<Omit<import("../../../../..").WithoutEmotionSpecific<import("@mui/material").ButtonProps>, "color" | "component" | "size" | "variant" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "TouchRippleProps" | "touchRippleRef" | "disableFocusRipple"> & {
13
- variant?: "light" | "link" | "text" | "contained" | "outlined" | undefined;
14
- loading?: boolean | undefined;
15
- color?: "primary" | "success" | "warning" | "error" | "grey" | undefined;
16
- component?: import("react").ElementType | undefined;
17
- selected?: boolean | undefined;
18
- size?: "large" | "medium" | undefined;
19
- } & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, ""> & import("react").RefAttributes<HTMLButtonElement> & {
20
- theme?: import("@emotion/react").Theme | undefined;
21
- }, {}, {}>;
22
- export declare const Content: import("../../../../../styled").StyledComponent<{
23
- theme?: import("@emotion/react").Theme | undefined;
24
- as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
25
- }, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
@@ -1,56 +1,13 @@
1
- import { IconButton } from '../../../../../IconButton';
2
- import { NewPopover } from '../../../../../NewPopover';
1
+ import { HintPopover } from '../../../../../HintPopover';
3
2
  import { popoverClassnames } from '../../../../../NewPopover/constants';
4
3
  import { styled } from '../../../../../styled';
5
4
  import { ONBOARDING_Z_INDEX_OFFSET } from '../../../../constants';
6
- /**
7
- * Popover шага, z-index над overlay, быстрое закрытие.
8
- */
9
- export const StepPopover = styled(NewPopover) `
5
+ export const StepPopover = styled(HintPopover) `
10
6
  && {
11
7
  z-index: ${({ theme }) => theme.zIndex.tooltip + ONBOARDING_Z_INDEX_OFFSET.popover};
12
8
  }
13
-
14
- .${popoverClassnames.innerContainer} {
15
- border-radius: calc(${({ theme }) => theme.shape.medium} * 2);
16
- }
17
9
 
18
10
  .${popoverClassnames.animatedWrapper}:not(.${popoverClassnames.animatedWrapperOpen}) {
19
11
  animation-duration: ${({ theme }) => theme.transitions.duration.shortest}ms;
20
12
  }
21
13
  `;
22
- export const PopoverBody = styled.div `
23
- position: relative;
24
-
25
- width: 400px;
26
- padding: ${({ theme }) => theme.spacing(6)};
27
-
28
- border-radius: calc(${({ theme }) => theme.shape.medium} * 2);
29
-
30
- /* Фокус-трап ставит фокус на контейнер, браузерную рамку убираем. */
31
- &:focus {
32
- outline: none;
33
- }
34
- `;
35
- export const CloseButton = styled(IconButton) `
36
- position: absolute;
37
- top: ${({ theme }) => theme.spacing(6)};
38
- right: ${({ theme }) => theme.spacing(6)};
39
- `;
40
- export const Content = styled.div `
41
- display: flex;
42
- flex-direction: column;
43
- gap: ${({ theme }) => theme.spacing(4)};
44
-
45
- margin-bottom: ${({ theme }) => theme.spacing(6)};
46
-
47
- &:last-child {
48
- margin-bottom: 0;
49
- }
50
-
51
- /* Резерв справа под крестик — только у верхнего элемента (заголовок, а если
52
- его нет — текст) */
53
- & > *:first-of-type {
54
- padding-right: ${({ theme }) => theme.spacing(10)};
55
- }
56
- `;
@@ -0,0 +1,16 @@
1
+ import { type Logger } from '../../services/Logger';
2
+ /**
3
+ * Mock логгера для тестов
4
+ */
5
+ export declare class LoggerMock {
6
+ configure: Logger['configure'];
7
+ child: Logger['child'];
8
+ isLevelEnabled: Logger['isLevelEnabled'];
9
+ success: Logger['success'];
10
+ info: Logger['info'];
11
+ warn: Logger['warn'];
12
+ debug: Logger['debug'];
13
+ error: Logger['error'];
14
+ constructor();
15
+ }
16
+ export declare const createLoggerMock: () => Logger;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLoggerMock = exports.LoggerMock = void 0;
4
+ const vitest_1 = require("vitest");
5
+ /**
6
+ * Mock логгера для тестов
7
+ */
8
+ class LoggerMock {
9
+ constructor() {
10
+ this.configure = vitest_1.vi.fn(() => vitest_1.vi.fn());
11
+ this.child = vitest_1.vi.fn();
12
+ this.isLevelEnabled = vitest_1.vi.fn(() => false);
13
+ this.success = vitest_1.vi.fn();
14
+ this.info = vitest_1.vi.fn();
15
+ this.warn = vitest_1.vi.fn();
16
+ this.debug = vitest_1.vi.fn();
17
+ this.error = vitest_1.vi.fn();
18
+ vitest_1.vi.mocked(this.child).mockReturnValue(this);
19
+ }
20
+ }
21
+ exports.LoggerMock = LoggerMock;
22
+ const createLoggerMock = () => new LoggerMock();
23
+ exports.createLoggerMock = createLoggerMock;
@@ -0,0 +1 @@
1
+ export * from './LoggerMock';
@@ -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("./LoggerMock"), exports);
@@ -1,4 +1,5 @@
1
1
  import { type ReactElement, type ReactNode } from 'react';
2
+ import { type LogThreshold } from '../../services/Logger';
2
3
  import { type LanguageMap } from '../DatePicker/types';
3
4
  import { type PlaceholderImageProps } from '../placeholders/Placeholder/Image';
4
5
  import { type CaptureException } from '../services/ErrorService';
@@ -138,7 +139,12 @@ export type ConfigProviderProps = Partial<ConfigContextProps> & {
138
139
  * Переданный в компонент флаг напрямую имеет приоритет выше
139
140
  */
140
141
  nextFeatureFlags?: NextFeatureFlagsContextProps;
142
+ /**
143
+ * Порог логов ui-kit в консоли. По умолчанию: prod → `error`, dev → `warn`,
144
+ * test → `silent`. `silent` — выключить полностью.
145
+ */
146
+ logLevel?: LogThreshold;
141
147
  };
142
148
  export declare const ConfigContext: import("react").Context<ConfigContextProps>;
143
- export declare const ConfigProvider: ({ children, language, datePickerLanguageMap, captureException: captureExceptionProp, emptySymbol, imagesMap, techSup, hidePersonalData, hidePersonalDataClassname, hidePersonalInputDataClassname, components: propsComponents, nextFeatureFlags, }: Partial<ConfigProviderProps>) => import("react/jsx-runtime").JSX.Element;
149
+ export declare const ConfigProvider: ({ children, language, datePickerLanguageMap, captureException: captureExceptionProp, emptySymbol, imagesMap, techSup, hidePersonalData, hidePersonalDataClassname, hidePersonalInputDataClassname, components: propsComponents, nextFeatureFlags, logLevel, }: Partial<ConfigProviderProps>) => import("react/jsx-runtime").JSX.Element;
144
150
  export {};
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConfigProvider = exports.ConfigContext = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const Logger_1 = require("../../services/Logger");
6
7
  const constants_1 = require("../DatePicker/constants");
7
8
  const ErrorService_1 = require("../services/ErrorService");
8
9
  const NextFeatureFlagsContext_1 = require("./NextFeatureFlagsContext");
@@ -16,6 +17,10 @@ const techSupDefault = {
16
17
  phone: '',
17
18
  email: '',
18
19
  };
20
+ const defaultCaptureException = (error) => {
21
+ // biome-ignore lint/suspicious/noConsole: Ошибка для интеграторов
22
+ console.error(error);
23
+ };
19
24
  const defaultHidePersonalDataClassname = 'ym-hide-content sentry-mask';
20
25
  const defaultHidePersonalInputDataClassname = 'ym-disable-keys sentry-mask';
21
26
  exports.ConfigContext = (0, react_1.createContext)({
@@ -30,20 +35,21 @@ exports.ConfigContext = (0, react_1.createContext)({
30
35
  hidePersonalInputDataClassname: defaultHidePersonalInputDataClassname,
31
36
  hidePersonalData: true,
32
37
  });
33
- const ConfigProvider = ({ children, language = 'ru', datePickerLanguageMap = constants_1.russianMap, captureException: captureExceptionProp, emptySymbol = '—', imagesMap = imagesMapDefault, techSup = techSupDefault, hidePersonalData = true, hidePersonalDataClassname = defaultHidePersonalDataClassname, hidePersonalInputDataClassname = defaultHidePersonalInputDataClassname, components: propsComponents, nextFeatureFlags = {}, }) => {
34
- const captureException =
35
- // biome-ignore lint/suspicious/noConsole: Ошибка для интеграторов
36
- captureExceptionProp || ((error) => console.error(error));
37
- (0, react_1.useMemo)(() => {
38
+ const ConfigProvider = ({ children, language = 'ru', datePickerLanguageMap = constants_1.russianMap, captureException: captureExceptionProp, emptySymbol = '—', imagesMap = imagesMapDefault, techSup = techSupDefault, hidePersonalData = true, hidePersonalDataClassname = defaultHidePersonalDataClassname, hidePersonalInputDataClassname = defaultHidePersonalInputDataClassname, components: propsComponents, nextFeatureFlags = {}, logLevel, }) => {
39
+ const captureException = captureExceptionProp ?? defaultCaptureException;
40
+ (0, react_1.useEffect)(() => {
38
41
  ErrorService_1.ErrorService.getInstance().init(captureException);
39
42
  }, [captureException]);
40
43
  (0, react_1.useEffect)(() => {
41
- if (!captureException) {
44
+ return (0, Logger_1.configureLogger)({ level: logLevel });
45
+ }, [logLevel]);
46
+ (0, react_1.useEffect)(() => {
47
+ if (!captureExceptionProp) {
42
48
  // biome-ignore lint/suspicious/noConsole: Ошибка для интеграторов
43
49
  console.warn('ConfigProvider: Необходимо наличие captureException, связанного с сервисом мониторинга ошибок.\n' +
44
50
  'На данный момент все ошибки, отлавливаемые в ErrorBoundary будут выводиться только в консоль');
45
51
  }
46
- }, []);
52
+ }, [captureExceptionProp]);
47
53
  const components = {
48
54
  ...propsComponents,
49
55
  layout: {
@@ -5,7 +5,7 @@ export declare const useLogic: ({ profile, menuOrganization }: HeaderProps) => {
5
5
  isShowProfile: boolean;
6
6
  isMobile: boolean;
7
7
  isFocusedMode: boolean;
8
- mobileHeaderPriorityFeature: "menuOrg" | "profile";
8
+ mobileHeaderPriorityFeature: "profile" | "menuOrg";
9
9
  isLoading: boolean;
10
10
  collapsedIn: boolean;
11
11
  onToggleSidebar: (newValue?: boolean | undefined) => void;
@@ -0,0 +1,6 @@
1
+ import { type HintPopoverProps } from './types';
2
+ /**
3
+ * Поповер-подсказка, привязанная к элементу: подсвечивает возможность интерфейса.
4
+ * Отличие от `NewPopover`: увеличенное скругление, всегда со стрелкой и не сворачивается в BottomDrawer на mobile.
5
+ */
6
+ export declare const HintPopover: ({ variant, title, action, onClose, contentRef, children, className, ...restProps }: HintPopoverProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HintPopover = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const CrossOutlineMd_1 = require("../../icons/CrossOutlineMd");
6
+ const useViewportType_1 = require("../useViewportType");
7
+ const classNames_1 = require("../utils/classNames");
8
+ const cva_1 = require("../utils/cva");
9
+ const constants_1 = require("./constants");
10
+ const styles_1 = require("./styles");
11
+ const hintPopoverCva = (0, cva_1.cva)(constants_1.hintPopoverClassnames.root, {
12
+ variants: {
13
+ variant: {
14
+ light: '',
15
+ dark: constants_1.hintPopoverClassnames.dark,
16
+ },
17
+ },
18
+ });
19
+ /**
20
+ * Поповер-подсказка, привязанная к элементу: подсвечивает возможность интерфейса.
21
+ * Отличие от `NewPopover`: увеличенное скругление, всегда со стрелкой и не сворачивается в BottomDrawer на mobile.
22
+ */
23
+ const HintPopover = ({ variant = 'dark', title, action, onClose, contentRef, children, className, ...restProps }) => {
24
+ const { isMobile } = (0, useViewportType_1.useViewportType)();
25
+ return ((0, jsx_runtime_1.jsx)(styles_1.StyledHintPopover, { ...restProps, className: (0, classNames_1.classNames)(className, hintPopoverCva({ variant })), hasArrow: true, disableMobileDrawer: true, onClose: onClose, children: (0, jsx_runtime_1.jsxs)(styles_1.Body, { ref: contentRef, tabIndex: -1, children: [(0, jsx_runtime_1.jsx)(styles_1.CloseButton, { variant: "text", onClick: onClose, "aria-label": "\u0417\u0430\u043A\u0440\u044B\u0442\u044C", children: (0, jsx_runtime_1.jsx)(CrossOutlineMd_1.CrossOutlineMd, {}) }), title && ((0, jsx_runtime_1.jsx)(styles_1.Title, { component: "div", variant: isMobile ? 'h5' : 'h4', children: title })), children, action && (0, jsx_runtime_1.jsx)(styles_1.ActionRow, { children: action })] }) }));
26
+ };
27
+ exports.HintPopover = HintPopover;
@@ -0,0 +1,4 @@
1
+ export declare const hintPopoverClassnames: {
2
+ root: string;
3
+ dark: string;
4
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hintPopoverClassnames = void 0;
4
+ const createUIKitClassname_1 = require("../utils/createUIKitClassname");
5
+ exports.hintPopoverClassnames = {
6
+ root: (0, createUIKitClassname_1.createUIKitClassname)('hint-popover'),
7
+ dark: (0, createUIKitClassname_1.createUIKitClassname)('hint-popover_dark'),
8
+ };
@@ -0,0 +1,2 @@
1
+ export { HintPopover } from './HintPopover';
2
+ export type { HintPopoverProps, HintPopoverVariant } from './types';