@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.
- package/_mocks/LoggerMock/LoggerMock.d.ts +16 -0
- package/_mocks/LoggerMock/LoggerMock.js +18 -0
- package/_mocks/LoggerMock/index.d.ts +1 -0
- package/_mocks/LoggerMock/index.js +1 -0
- package/components/ConfigProvider/ConfigProvider.d.ts +7 -1
- package/components/ConfigProvider/ConfigProvider.js +14 -8
- package/components/DashboardLayout/Header/useLogic/useLogic.d.ts +1 -1
- package/components/HintPopover/HintPopover.d.ts +6 -0
- package/components/HintPopover/HintPopover.js +23 -0
- package/components/HintPopover/constants.d.ts +4 -0
- package/components/HintPopover/constants.js +5 -0
- package/components/HintPopover/index.d.ts +2 -0
- package/components/HintPopover/index.js +1 -0
- package/components/HintPopover/public.d.ts +2 -0
- package/components/HintPopover/public.js +1 -0
- package/components/HintPopover/styles.d.ts +38 -0
- package/components/HintPopover/styles.js +69 -0
- package/components/HintPopover/types.d.ts +24 -0
- package/components/HintPopover/types.js +1 -0
- package/components/NewPopover/NewPopover.d.ts +1 -1
- package/components/NewPopover/NewPopover.js +5 -4
- package/components/NewPopover/constants.d.ts +11 -0
- package/components/NewPopover/constants.js +11 -0
- package/components/NewPopover/styles.js +35 -30
- package/components/NewPopover/types.d.ts +5 -0
- package/components/NewPopover/useLogic/useLogic.d.ts +4 -3
- package/components/NewPopover/useLogic/useLogic.js +17 -8
- package/components/NewPopover/useLogic/utils/getArrowPadding/getArrowPadding.d.ts +14 -0
- package/components/NewPopover/useLogic/utils/getArrowPadding/getArrowPadding.js +12 -0
- package/components/NewPopover/useLogic/utils/getArrowPadding/index.d.ts +1 -0
- package/components/NewPopover/useLogic/utils/getArrowPadding/index.js +1 -0
- package/components/NewPopover/useLogic/utils/getPopoverCssVars/getPopoverCssVars.d.ts +2 -0
- package/components/NewPopover/useLogic/utils/getPopoverCssVars/getPopoverCssVars.js +6 -0
- package/components/NewPopover/useLogic/utils/getPopoverCssVars/index.d.ts +1 -0
- package/components/NewPopover/useLogic/utils/getPopoverCssVars/index.js +1 -0
- package/components/NewPopover/useLogic/utils/resolveAnchorNode/resolveAnchorNode.d.ts +6 -0
- package/components/NewPopover/useLogic/utils/resolveAnchorNode/resolveAnchorNode.js +8 -0
- package/components/Onboarding/Onboarding.js +13 -3
- package/components/Onboarding/core/ExitController/ExitController.d.ts +3 -1
- package/components/Onboarding/core/ExitController/ExitController.js +4 -1
- package/components/Onboarding/core/OnboardingTour/OnboardingTour.d.ts +3 -1
- package/components/Onboarding/core/OnboardingTour/OnboardingTour.js +25 -4
- package/components/Onboarding/core/ProgressStorage/ProgressStorage.d.ts +3 -1
- package/components/Onboarding/core/ProgressStorage/ProgressStorage.js +8 -4
- package/components/Onboarding/ui/StepStrategy/steps/HighlightStep/HighlightStep.js +3 -4
- package/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.d.ts +6 -22
- package/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.js +2 -45
- package/node/_mocks/LoggerMock/LoggerMock.d.ts +16 -0
- package/node/_mocks/LoggerMock/LoggerMock.js +23 -0
- package/node/_mocks/LoggerMock/index.d.ts +1 -0
- package/node/_mocks/LoggerMock/index.js +17 -0
- package/node/components/ConfigProvider/ConfigProvider.d.ts +7 -1
- package/node/components/ConfigProvider/ConfigProvider.js +13 -7
- package/node/components/DashboardLayout/Header/useLogic/useLogic.d.ts +1 -1
- package/node/components/HintPopover/HintPopover.d.ts +6 -0
- package/node/components/HintPopover/HintPopover.js +27 -0
- package/node/components/HintPopover/constants.d.ts +4 -0
- package/node/components/HintPopover/constants.js +8 -0
- package/node/components/HintPopover/index.d.ts +2 -0
- package/node/components/HintPopover/index.js +5 -0
- package/node/components/HintPopover/public.d.ts +2 -0
- package/node/components/HintPopover/public.js +5 -0
- package/node/components/HintPopover/styles.d.ts +38 -0
- package/node/components/HintPopover/styles.js +72 -0
- package/node/components/HintPopover/types.d.ts +24 -0
- package/node/components/HintPopover/types.js +2 -0
- package/node/components/NewPopover/NewPopover.d.ts +1 -1
- package/node/components/NewPopover/NewPopover.js +5 -4
- package/node/components/NewPopover/constants.d.ts +11 -0
- package/node/components/NewPopover/constants.js +12 -1
- package/node/components/NewPopover/styles.js +34 -29
- package/node/components/NewPopover/types.d.ts +5 -0
- package/node/components/NewPopover/useLogic/useLogic.d.ts +4 -3
- package/node/components/NewPopover/useLogic/useLogic.js +18 -9
- package/node/components/NewPopover/useLogic/utils/getArrowPadding/getArrowPadding.d.ts +14 -0
- package/node/components/NewPopover/useLogic/utils/getArrowPadding/getArrowPadding.js +16 -0
- package/node/components/NewPopover/useLogic/utils/getArrowPadding/index.d.ts +1 -0
- package/node/components/NewPopover/useLogic/utils/getArrowPadding/index.js +17 -0
- package/node/components/NewPopover/useLogic/utils/getPopoverCssVars/getPopoverCssVars.d.ts +2 -0
- package/node/components/NewPopover/useLogic/utils/getPopoverCssVars/getPopoverCssVars.js +10 -0
- package/node/components/NewPopover/useLogic/utils/getPopoverCssVars/index.d.ts +1 -0
- package/node/components/NewPopover/useLogic/utils/getPopoverCssVars/index.js +5 -0
- package/node/components/NewPopover/useLogic/utils/resolveAnchorNode/resolveAnchorNode.d.ts +6 -0
- package/node/components/NewPopover/{utils → useLogic/utils/resolveAnchorNode}/resolveAnchorNode.js +2 -2
- package/node/components/Onboarding/Onboarding.js +13 -3
- package/node/components/Onboarding/core/ExitController/ExitController.d.ts +3 -1
- package/node/components/Onboarding/core/ExitController/ExitController.js +4 -1
- package/node/components/Onboarding/core/OnboardingTour/OnboardingTour.d.ts +3 -1
- package/node/components/Onboarding/core/OnboardingTour/OnboardingTour.js +25 -4
- package/node/components/Onboarding/core/ProgressStorage/ProgressStorage.d.ts +3 -1
- package/node/components/Onboarding/core/ProgressStorage/ProgressStorage.js +8 -4
- package/node/components/Onboarding/ui/StepStrategy/steps/HighlightStep/HighlightStep.js +1 -2
- package/node/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.d.ts +6 -22
- package/node/components/Onboarding/ui/StepStrategy/steps/HighlightStep/styles.js +3 -46
- package/node/services/Logger/Logger.d.ts +45 -0
- package/node/services/Logger/Logger.js +72 -0
- package/node/services/Logger/index.d.ts +10 -0
- package/node/services/Logger/index.js +17 -0
- package/package.json +1 -1
- package/services/Logger/Logger.d.ts +45 -0
- package/services/Logger/Logger.js +68 -0
- package/services/Logger/index.d.ts +10 -0
- package/services/Logger/index.js +12 -0
- package/components/NewPopover/utils/resolveAnchorNode.d.ts +0 -6
- package/components/NewPopover/utils/resolveAnchorNode.js +0 -8
- package/node/components/NewPopover/utils/resolveAnchorNode.d.ts +0 -6
- /package/components/NewPopover/{utils → useLogic/utils/resolveAnchorNode}/index.d.ts +0 -0
- /package/components/NewPopover/{utils → useLogic/utils/resolveAnchorNode}/index.js +0 -0
- /package/node/components/NewPopover/{utils → useLogic/utils/resolveAnchorNode}/index.d.ts +0 -0
- /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,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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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 {
|
|
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, {
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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,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
|
+
};
|