@doyourjob/gravity-ui-page-constructor 5.31.282 → 5.31.284
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/build/cjs/blocks/Header/Header.css +18 -1
- package/build/cjs/blocks/Header/Header.js +21 -3
- package/build/cjs/blocks/Header/schema.d.ts +18 -0
- package/build/cjs/blocks/Header/schema.js +3 -0
- package/build/cjs/blocks/Header/useAdaptiveUnicornBackground.d.ts +13 -0
- package/build/cjs/blocks/Header/useAdaptiveUnicornBackground.js +206 -0
- package/build/cjs/blocks/HeaderSlider/schema.d.ts +9 -0
- package/build/cjs/components/Media/Media.js +3 -2
- package/build/cjs/components/TextNode/TextNode.css +2 -0
- package/build/cjs/components/TextNode/TextNode.d.ts +12 -0
- package/build/cjs/components/TextNode/TextNode.js +16 -0
- package/build/cjs/components/VideoBlock/VideoBlock.css +8 -0
- package/build/cjs/components/VideoBlock/VideoBlock.d.ts +1 -0
- package/build/cjs/components/VideoBlock/VideoBlock.js +5 -3
- package/build/cjs/models/constructor-items/blocks.d.ts +3 -0
- package/build/cjs/models/constructor-items/common.d.ts +1 -0
- package/build/esm/blocks/Header/Header.css +18 -1
- package/build/esm/blocks/Header/Header.js +21 -3
- package/build/esm/blocks/Header/schema.d.ts +18 -0
- package/build/esm/blocks/Header/schema.js +3 -0
- package/build/esm/blocks/Header/useAdaptiveUnicornBackground.d.ts +13 -0
- package/build/esm/blocks/Header/useAdaptiveUnicornBackground.js +202 -0
- package/build/esm/blocks/HeaderSlider/schema.d.ts +9 -0
- package/build/esm/components/Media/Media.js +3 -2
- package/build/esm/components/TextNode/TextNode.css +2 -0
- package/build/esm/components/TextNode/TextNode.d.ts +13 -0
- package/build/esm/components/TextNode/TextNode.js +14 -0
- package/build/esm/components/VideoBlock/VideoBlock.css +8 -0
- package/build/esm/components/VideoBlock/VideoBlock.d.ts +1 -0
- package/build/esm/components/VideoBlock/VideoBlock.js +5 -3
- package/build/esm/models/constructor-items/blocks.d.ts +3 -0
- package/build/esm/models/constructor-items/common.d.ts +1 -0
- package/package.json +1 -1
- package/schema/index.js +1 -1
- package/server/models/constructor-items/blocks.d.ts +3 -0
- package/server/models/constructor-items/common.d.ts +1 -0
- package/widget/index.js +1 -1
|
@@ -246,7 +246,7 @@ unpredictable css rules order in build */
|
|
|
246
246
|
z-index: 5;
|
|
247
247
|
}
|
|
248
248
|
@media (max-width: 769px) {
|
|
249
|
-
.pc-header-block_has-background .pc-header-block__background.pc-header-block__background_unicorn, .pc-header-block_has-background .pc-header-block__background.pc-header-block__background_media.pc-header-block__background_unicorn {
|
|
249
|
+
.pc-header-block_has-background .pc-header-block__background.pc-header-block__background_unicorn:not(.pc-header-block__background_unicorn-fallback), .pc-header-block_has-background .pc-header-block__background.pc-header-block__background_media.pc-header-block__background_unicorn:not(.pc-header-block__background_unicorn-fallback) {
|
|
250
250
|
display: none;
|
|
251
251
|
}
|
|
252
252
|
}
|
|
@@ -263,6 +263,23 @@ unpredictable css rules order in build */
|
|
|
263
263
|
width: 100%;
|
|
264
264
|
background-color: var(--g-color-base-background);
|
|
265
265
|
}
|
|
266
|
+
.pc-header-block__fallback {
|
|
267
|
+
position: absolute;
|
|
268
|
+
z-index: 1;
|
|
269
|
+
inset: 0;
|
|
270
|
+
pointer-events: none;
|
|
271
|
+
opacity: 0;
|
|
272
|
+
transition: opacity 0.3s ease;
|
|
273
|
+
}
|
|
274
|
+
.pc-header-block__fallback_visible {
|
|
275
|
+
opacity: 1;
|
|
276
|
+
}
|
|
277
|
+
.pc-header-block__fallback-image {
|
|
278
|
+
display: block;
|
|
279
|
+
width: 100%;
|
|
280
|
+
height: 100%;
|
|
281
|
+
object-fit: cover;
|
|
282
|
+
}
|
|
266
283
|
.pc-header-block__overtitle {
|
|
267
284
|
font-size: var(--g-text-body-3-font-size, var(--pc-text-body-3-font-size));
|
|
268
285
|
line-height: var(--g-text-body-3-line-height, var(--pc-text-body-3-line-height));
|
|
@@ -20,6 +20,7 @@ const HeaderStock_1 = tslib_1.__importDefault(require("./HeaderStock/HeaderStock
|
|
|
20
20
|
const HeaderTag_1 = tslib_1.__importDefault(require("./HeaderTag/HeaderTag"));
|
|
21
21
|
const HeaderTags_1 = tslib_1.__importDefault(require("./HeaderTags/HeaderTags"));
|
|
22
22
|
const SwitchingTtitle_1 = tslib_1.__importDefault(require("./SwitchingTitle/SwitchingTtitle"));
|
|
23
|
+
const useAdaptiveUnicornBackground_1 = require("./useAdaptiveUnicornBackground");
|
|
23
24
|
const utils_3 = require("./utils");
|
|
24
25
|
const b = (0, utils_2.block)('header-block');
|
|
25
26
|
const Background = ({ background, isMobile }) => {
|
|
@@ -31,7 +32,7 @@ const Background = ({ background, isMobile }) => {
|
|
|
31
32
|
const FullWidthBackground = ({ background }) => (react_1.default.createElement("div", { className: b('background', { ['full-width']: true }), style: { backgroundColor: background === null || background === void 0 ? void 0 : background.color } }));
|
|
32
33
|
// eslint-disable-next-line complexity
|
|
33
34
|
const HeaderBlock = (props) => {
|
|
34
|
-
const { title, switchingTitle, topTags, bottomTags, overtitle, description, buttons, stock, stockPrice, stockShares, image, video, width = 's', imageSize, offset = 'default', background, theme: textTheme = 'light', verticalOffset = 'm', verticalOffsetTop, verticalOffsetBottom, className, breadcrumbs, status, renderTitle, children, mediaView = 'full', backgroundEffect, headerSpace, backLink, unicorn, unicornSdkUrl, } = props;
|
|
35
|
+
const { title, switchingTitle, topTags, bottomTags, overtitle, description, buttons, stock, stockPrice, stockShares, image, video, width = 's', imageSize, offset = 'default', background, theme: textTheme = 'light', verticalOffset = 'm', verticalOffsetTop, verticalOffsetBottom, className, breadcrumbs, status, renderTitle, children, mediaView = 'full', backgroundEffect, headerSpace, backLink, unicorn, unicornSdkUrl, unicornFallbackImage, unicornFallbackImageMobile, forceMobileImage = true, } = props;
|
|
35
36
|
const isMobile = (0, react_1.useContext)(mobileContext_1.MobileContext);
|
|
36
37
|
const { backButton, blockTag } = (0, react_1.useContext)(headerContext_1.HeaderContext);
|
|
37
38
|
const theme = (0, theme_1.useTheme)();
|
|
@@ -43,6 +44,17 @@ const HeaderBlock = (props) => {
|
|
|
43
44
|
const imageThemed = (0, react_1.useMemo)(() => image && (0, utils_2.getThemedValue)(image, theme), [image, theme]);
|
|
44
45
|
const videoThemed = (0, react_1.useMemo)(() => video && (0, utils_2.getThemedValue)(video, theme), [theme, video]);
|
|
45
46
|
const fullWidth = (backgroundThemed === null || backgroundThemed === void 0 ? void 0 : backgroundThemed.fullWidth) || (backgroundThemed === null || backgroundThemed === void 0 ? void 0 : backgroundThemed.fullWidthMedia);
|
|
47
|
+
const fallbackImage = isMobile
|
|
48
|
+
? unicornFallbackImageMobile || unicornFallbackImage
|
|
49
|
+
: unicornFallbackImage || unicornFallbackImageMobile;
|
|
50
|
+
const forceFallbackOnMobile = isMobile && forceMobileImage && Boolean(fallbackImage);
|
|
51
|
+
const { backgroundRef, handleFallbackImageLoad, handleFallbackTransitionEnd, handleSceneLoad, isFallbackVisible, shouldMountScene, shouldPlayScene, showFallback, } = (0, useAdaptiveUnicornBackground_1.useAdaptiveUnicornBackground)({
|
|
52
|
+
enabled: Boolean(unicorn) && !forceFallbackOnMobile,
|
|
53
|
+
fallbackAvailable: Boolean(fallbackImage),
|
|
54
|
+
});
|
|
55
|
+
const showFallbackImage = forceFallbackOnMobile || showFallback;
|
|
56
|
+
const isFallbackImageVisible = forceFallbackOnMobile || isFallbackVisible;
|
|
57
|
+
const shouldRenderScene = !forceFallbackOnMobile && shouldMountScene;
|
|
46
58
|
const titleId = (0, uikit_1.useUniqId)();
|
|
47
59
|
const headerRef = (0, react_1.useRef)(null);
|
|
48
60
|
const hasBackground = Boolean(backgroundThemed || backgroundEffect);
|
|
@@ -72,8 +84,14 @@ const HeaderBlock = (props) => {
|
|
|
72
84
|
backgroundThemed && fullWidth && react_1.default.createElement(FullWidthBackground, { background: backgroundThemed }),
|
|
73
85
|
backgroundThemed && react_1.default.createElement(Background, { background: backgroundThemed, isMobile: isMobile }),
|
|
74
86
|
backgroundEffect && backgroundEffect.firstSrc && backgroundEffect.secondSrc && (react_1.default.createElement(components_1.BackgroundEffect, Object.assign({}, backgroundEffect, { attachRef: headerRef }))),
|
|
75
|
-
unicorn && (react_1.default.createElement("div", { className: b('background', {
|
|
76
|
-
|
|
87
|
+
unicorn && (react_1.default.createElement("div", { ref: backgroundRef, className: b('background', {
|
|
88
|
+
unicorn: true,
|
|
89
|
+
'unicorn-fallback': showFallbackImage,
|
|
90
|
+
}) },
|
|
91
|
+
showFallbackImage && fallbackImage && (react_1.default.createElement("picture", { className: b('fallback', { visible: isFallbackImageVisible }), onTransitionEnd: handleFallbackTransitionEnd },
|
|
92
|
+
unicornFallbackImageMobile && (react_1.default.createElement("source", { media: "(max-width: 768px)", srcSet: unicornFallbackImageMobile })),
|
|
93
|
+
react_1.default.createElement("img", { alt: "", "aria-hidden": "true", className: b('fallback-image'), onLoad: handleFallbackImageLoad, src: fallbackImage }))),
|
|
94
|
+
shouldRenderScene && (react_1.default.createElement(components_1.UnicornScene, { jsonFilePath: unicorn, onLoad: handleSceneLoad, play: shouldPlayScene, sdkUrl: unicornSdkUrl })))),
|
|
77
95
|
react_1.default.createElement(grid_1.Grid, { containerClass: b('container-fluid') },
|
|
78
96
|
react_1.default.createElement(Breadcrumbs_1.default, { breadcrumbs: breadcrumbs, theme: textTheme }),
|
|
79
97
|
react_1.default.createElement(BackButton_1.default, { backButton: backButtonItem, theme: textTheme }),
|
|
@@ -1078,6 +1078,15 @@ export declare const HeaderProperties: {
|
|
|
1078
1078
|
unicornSdkUrl: {
|
|
1079
1079
|
type: string;
|
|
1080
1080
|
};
|
|
1081
|
+
unicornFallbackImage: {
|
|
1082
|
+
type: string;
|
|
1083
|
+
};
|
|
1084
|
+
unicornFallbackImageMobile: {
|
|
1085
|
+
type: string;
|
|
1086
|
+
};
|
|
1087
|
+
forceMobileImage: {
|
|
1088
|
+
type: string;
|
|
1089
|
+
};
|
|
1081
1090
|
breadcrumbs: {
|
|
1082
1091
|
type: string;
|
|
1083
1092
|
additionalProperties: boolean;
|
|
@@ -1948,6 +1957,15 @@ export declare const HeaderBlock: {
|
|
|
1948
1957
|
unicornSdkUrl: {
|
|
1949
1958
|
type: string;
|
|
1950
1959
|
};
|
|
1960
|
+
unicornFallbackImage: {
|
|
1961
|
+
type: string;
|
|
1962
|
+
};
|
|
1963
|
+
unicornFallbackImageMobile: {
|
|
1964
|
+
type: string;
|
|
1965
|
+
};
|
|
1966
|
+
forceMobileImage: {
|
|
1967
|
+
type: string;
|
|
1968
|
+
};
|
|
1951
1969
|
breadcrumbs: {
|
|
1952
1970
|
type: string;
|
|
1953
1971
|
additionalProperties: boolean;
|
|
@@ -148,6 +148,9 @@ exports.HeaderProperties = {
|
|
|
148
148
|
},
|
|
149
149
|
unicorn: { type: 'string' },
|
|
150
150
|
unicornSdkUrl: { type: 'string' },
|
|
151
|
+
unicornFallbackImage: { type: 'string' },
|
|
152
|
+
unicornFallbackImageMobile: { type: 'string' },
|
|
153
|
+
forceMobileImage: { type: 'boolean' },
|
|
151
154
|
breadcrumbs: {
|
|
152
155
|
type: 'object',
|
|
153
156
|
additionalProperties: false,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function useAdaptiveUnicornBackground({ enabled, fallbackAvailable, }: {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
fallbackAvailable: boolean;
|
|
4
|
+
}): {
|
|
5
|
+
backgroundRef: import("react").RefObject<HTMLDivElement>;
|
|
6
|
+
handleFallbackImageLoad: () => void;
|
|
7
|
+
handleFallbackTransitionEnd: () => void;
|
|
8
|
+
handleSceneLoad: () => void;
|
|
9
|
+
isFallbackVisible: boolean;
|
|
10
|
+
shouldMountScene: boolean;
|
|
11
|
+
shouldPlayScene: boolean;
|
|
12
|
+
showFallback: boolean;
|
|
13
|
+
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAdaptiveUnicornBackground = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const REDUCED_MOTION_QUERY = '(prefers-reduced-motion: reduce)';
|
|
6
|
+
const LOW_DEVICE_MEMORY_GB = 4;
|
|
7
|
+
const LOW_HARDWARE_CONCURRENCY = 4;
|
|
8
|
+
const SLOW_EFFECTIVE_TYPES = new Set(['slow-2g', '2g']);
|
|
9
|
+
const FRAME_WARMUP_COUNT = 5;
|
|
10
|
+
const SLOW_FRAME_MS = 50;
|
|
11
|
+
const SLOW_FRAME_LIMIT = 8;
|
|
12
|
+
const LONG_TASK_MS = 100;
|
|
13
|
+
const LONG_TASK_LIMIT = 2;
|
|
14
|
+
const FALLBACK_FADE_TIMEOUT_MS = 500;
|
|
15
|
+
function prefersStaticBackground() {
|
|
16
|
+
var _a;
|
|
17
|
+
if (typeof window === 'undefined') {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const navigatorWithSignals = navigator;
|
|
21
|
+
const connection = navigatorWithSignals.connection;
|
|
22
|
+
return (((_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, REDUCED_MOTION_QUERY).matches) ||
|
|
23
|
+
(connection === null || connection === void 0 ? void 0 : connection.saveData) === true ||
|
|
24
|
+
((connection === null || connection === void 0 ? void 0 : connection.effectiveType) ? SLOW_EFFECTIVE_TYPES.has(connection.effectiveType) : false) ||
|
|
25
|
+
(navigatorWithSignals.deviceMemory
|
|
26
|
+
? navigatorWithSignals.deviceMemory <= LOW_DEVICE_MEMORY_GB
|
|
27
|
+
: false) ||
|
|
28
|
+
(navigator.hardwareConcurrency
|
|
29
|
+
? navigator.hardwareConcurrency <= LOW_HARDWARE_CONCURRENCY
|
|
30
|
+
: false));
|
|
31
|
+
}
|
|
32
|
+
function supportsLongTaskObserver() {
|
|
33
|
+
var _a;
|
|
34
|
+
return (typeof PerformanceObserver !== 'undefined' &&
|
|
35
|
+
((_a = PerformanceObserver.supportedEntryTypes) === null || _a === void 0 ? void 0 : _a.includes('longtask')));
|
|
36
|
+
}
|
|
37
|
+
function isFallbackShortcut(event) {
|
|
38
|
+
return (event.altKey && event.shiftKey && (event.code === 'KeyU' || event.key.toLowerCase() === 'u'));
|
|
39
|
+
}
|
|
40
|
+
function useAdaptiveUnicornBackground({ enabled, fallbackAvailable, }) {
|
|
41
|
+
const backgroundRef = (0, react_1.useRef)(null);
|
|
42
|
+
const [mode, setMode] = (0, react_1.useState)('checking');
|
|
43
|
+
const [isNearViewport, setIsNearViewport] = (0, react_1.useState)(false);
|
|
44
|
+
const [fallbackFadeStarted, setFallbackFadeStarted] = (0, react_1.useState)(false);
|
|
45
|
+
const [fallbackImageLoaded, setFallbackImageLoaded] = (0, react_1.useState)(false);
|
|
46
|
+
const [sceneLoaded, setSceneLoaded] = (0, react_1.useState)(false);
|
|
47
|
+
const degradeBackground = (0, react_1.useCallback)(() => {
|
|
48
|
+
setMode((currentMode) => {
|
|
49
|
+
if (!fallbackAvailable) {
|
|
50
|
+
return 'paused';
|
|
51
|
+
}
|
|
52
|
+
return currentMode === 'animation' && isNearViewport ? 'fallback-fading' : 'fallback';
|
|
53
|
+
});
|
|
54
|
+
}, [fallbackAvailable, isNearViewport]);
|
|
55
|
+
const finishFallbackFade = (0, react_1.useCallback)(() => {
|
|
56
|
+
setMode((currentMode) => (currentMode === 'fallback-fading' ? 'fallback' : currentMode));
|
|
57
|
+
}, []);
|
|
58
|
+
const handleFallbackImageLoad = (0, react_1.useCallback)(() => {
|
|
59
|
+
if (mode === 'fallback-fading') {
|
|
60
|
+
setFallbackImageLoaded(true);
|
|
61
|
+
}
|
|
62
|
+
}, [mode]);
|
|
63
|
+
const handleSceneLoad = (0, react_1.useCallback)(() => {
|
|
64
|
+
setSceneLoaded(true);
|
|
65
|
+
}, []);
|
|
66
|
+
(0, react_1.useEffect)(() => {
|
|
67
|
+
setSceneLoaded(false);
|
|
68
|
+
setIsNearViewport(false);
|
|
69
|
+
if (!enabled) {
|
|
70
|
+
setMode('paused');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (prefersStaticBackground()) {
|
|
74
|
+
setMode(fallbackAvailable ? 'fallback' : 'paused');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setMode('animation');
|
|
78
|
+
}, [enabled, fallbackAvailable]);
|
|
79
|
+
(0, react_1.useEffect)(() => {
|
|
80
|
+
if (!enabled) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const handleKeyDown = (event) => {
|
|
84
|
+
if (isFallbackShortcut(event)) {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
degradeBackground();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
90
|
+
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
91
|
+
}, [degradeBackground, enabled]);
|
|
92
|
+
(0, react_1.useEffect)(() => {
|
|
93
|
+
if (mode !== 'fallback-fading') {
|
|
94
|
+
setFallbackFadeStarted(false);
|
|
95
|
+
setFallbackImageLoaded(false);
|
|
96
|
+
}
|
|
97
|
+
return undefined;
|
|
98
|
+
}, [mode]);
|
|
99
|
+
(0, react_1.useEffect)(() => {
|
|
100
|
+
if (mode !== 'fallback-fading' || !fallbackImageLoaded) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
if (typeof window === 'undefined' || !window.requestAnimationFrame) {
|
|
104
|
+
setFallbackFadeStarted(true);
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const frameId = window.requestAnimationFrame(() => setFallbackFadeStarted(true));
|
|
108
|
+
return () => { var _a; return (_a = window.cancelAnimationFrame) === null || _a === void 0 ? void 0 : _a.call(window, frameId); };
|
|
109
|
+
}, [fallbackImageLoaded, mode]);
|
|
110
|
+
(0, react_1.useEffect)(() => {
|
|
111
|
+
if (mode !== 'fallback-fading' || !fallbackFadeStarted) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
const fallbackTimer = setTimeout(finishFallbackFade, FALLBACK_FADE_TIMEOUT_MS);
|
|
115
|
+
return () => clearTimeout(fallbackTimer);
|
|
116
|
+
}, [fallbackFadeStarted, finishFallbackFade, mode]);
|
|
117
|
+
(0, react_1.useEffect)(() => {
|
|
118
|
+
if (!enabled || mode !== 'animation') {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
const target = backgroundRef.current;
|
|
122
|
+
if (!target || typeof IntersectionObserver === 'undefined') {
|
|
123
|
+
setIsNearViewport(true);
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
127
|
+
setIsNearViewport(entry.isIntersecting);
|
|
128
|
+
if (!entry.isIntersecting) {
|
|
129
|
+
setSceneLoaded(false);
|
|
130
|
+
}
|
|
131
|
+
}, { rootMargin: '300px 0px', threshold: 0 });
|
|
132
|
+
observer.observe(target);
|
|
133
|
+
return () => observer.disconnect();
|
|
134
|
+
}, [enabled, mode]);
|
|
135
|
+
(0, react_1.useEffect)(() => {
|
|
136
|
+
if (mode !== 'animation' ||
|
|
137
|
+
!isNearViewport ||
|
|
138
|
+
!sceneLoaded ||
|
|
139
|
+
typeof window === 'undefined' ||
|
|
140
|
+
!window.requestAnimationFrame ||
|
|
141
|
+
!window.cancelAnimationFrame) {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
let frameId = 0;
|
|
145
|
+
let previousTime;
|
|
146
|
+
let measuredFrames = 0;
|
|
147
|
+
let slowFrames = 0;
|
|
148
|
+
let longTasks = 0;
|
|
149
|
+
let stopped = false;
|
|
150
|
+
let observer;
|
|
151
|
+
const degradeOnce = () => {
|
|
152
|
+
if (stopped) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
stopped = true;
|
|
156
|
+
degradeBackground();
|
|
157
|
+
};
|
|
158
|
+
const tick = (time) => {
|
|
159
|
+
if (stopped) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (previousTime !== undefined) {
|
|
163
|
+
measuredFrames += 1;
|
|
164
|
+
if (measuredFrames > FRAME_WARMUP_COUNT && time - previousTime > SLOW_FRAME_MS) {
|
|
165
|
+
slowFrames += 1;
|
|
166
|
+
if (slowFrames >= SLOW_FRAME_LIMIT) {
|
|
167
|
+
degradeOnce();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
previousTime = time;
|
|
173
|
+
frameId = window.requestAnimationFrame(tick);
|
|
174
|
+
};
|
|
175
|
+
frameId = window.requestAnimationFrame(tick);
|
|
176
|
+
if (supportsLongTaskObserver()) {
|
|
177
|
+
observer = new PerformanceObserver((entries) => {
|
|
178
|
+
entries.getEntries().forEach((entry) => {
|
|
179
|
+
if (entry.duration >= LONG_TASK_MS) {
|
|
180
|
+
longTasks += 1;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
if (longTasks >= LONG_TASK_LIMIT) {
|
|
184
|
+
degradeOnce();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
observer.observe({ entryTypes: ['longtask'] });
|
|
188
|
+
}
|
|
189
|
+
return () => {
|
|
190
|
+
stopped = true;
|
|
191
|
+
window.cancelAnimationFrame(frameId);
|
|
192
|
+
observer === null || observer === void 0 ? void 0 : observer.disconnect();
|
|
193
|
+
};
|
|
194
|
+
}, [degradeBackground, isNearViewport, mode, sceneLoaded]);
|
|
195
|
+
return {
|
|
196
|
+
backgroundRef,
|
|
197
|
+
handleFallbackImageLoad,
|
|
198
|
+
handleFallbackTransitionEnd: finishFallbackFade,
|
|
199
|
+
handleSceneLoad,
|
|
200
|
+
isFallbackVisible: mode === 'fallback' || (mode === 'fallback-fading' && fallbackFadeStarted),
|
|
201
|
+
shouldMountScene: (mode === 'animation' && isNearViewport) || mode === 'fallback-fading',
|
|
202
|
+
shouldPlayScene: (mode === 'animation' && isNearViewport) || mode === 'fallback-fading',
|
|
203
|
+
showFallback: (mode === 'fallback-fading' || mode === 'fallback') && fallbackAvailable,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
exports.useAdaptiveUnicornBackground = useAdaptiveUnicornBackground;
|
|
@@ -769,6 +769,15 @@ export declare const HeaderSliderBlock: {
|
|
|
769
769
|
unicornSdkUrl: {
|
|
770
770
|
type: string;
|
|
771
771
|
};
|
|
772
|
+
unicornFallbackImage: {
|
|
773
|
+
type: string;
|
|
774
|
+
};
|
|
775
|
+
unicornFallbackImageMobile: {
|
|
776
|
+
type: string;
|
|
777
|
+
};
|
|
778
|
+
forceMobileImage: {
|
|
779
|
+
type: string;
|
|
780
|
+
};
|
|
772
781
|
breadcrumbs: {
|
|
773
782
|
type: string;
|
|
774
783
|
additionalProperties: boolean;
|
|
@@ -14,7 +14,7 @@ const Image_1 = tslib_1.__importDefault(require("./Image/Image"));
|
|
|
14
14
|
const Video_1 = tslib_1.__importDefault(require("./Video/Video"));
|
|
15
15
|
const b = (0, utils_1.block)('Media');
|
|
16
16
|
const Media = (props) => {
|
|
17
|
-
const { animated, image, video, youtube, videoIframe, dataLens, color, height, previewImg, previewVideo, parallax = false, fullscreen, analyticsEvents, className, imageClassName, videoClassName, youtubeClassName, disableImageSliderForArrayInput, playVideo = true, isBackground, playButton, playButtonCorner, customBarControlsClassName, qa, ratio, autoplay, onImageLoad, iframe, margins, videoMicrodata, } = props;
|
|
17
|
+
const { animated, image, video, youtube, videoIframe, dataLens, color, height, previewImg, previewVideo, parallax = false, fullscreen, analyticsEvents, className, imageClassName, videoClassName, youtubeClassName, disableImageSliderForArrayInput, playVideo = true, isBackground, playButton, playButtonCorner, playButtonText, customBarControlsClassName, qa, ratio, autoplay, onImageLoad, iframe, margins, videoMicrodata, } = props;
|
|
18
18
|
const [hasVideoFallback, setHasVideoFallback] = (0, react_1.useState)(false);
|
|
19
19
|
const { microdata } = (0, react_1.useContext)(innerContext_1.InnerContext);
|
|
20
20
|
const qaAttributes = (0, utils_1.getQaAttrubutes)(qa, 'video');
|
|
@@ -46,7 +46,7 @@ const Media = (props) => {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
if (youtube || videoIframe) {
|
|
49
|
-
result = (react_1.default.createElement(VideoBlock_1.default, { className: b('youtube', youtubeClassName), record: youtube, videoIframe: videoIframe, attributes: { color: 'white', rel: '0' }, previewImg: previewImg, previewVideo: previewVideo, playButtonCorner: playButtonCorner, height: height, ratio: ratio, fullscreen: fullscreen, analyticsEvents: analyticsEvents, autoplay: autoplay, onImageLoad: onImageLoad }));
|
|
49
|
+
result = (react_1.default.createElement(VideoBlock_1.default, { className: b('youtube', youtubeClassName), record: youtube, videoIframe: videoIframe, attributes: { color: 'white', rel: '0' }, previewImg: previewImg, previewVideo: previewVideo, playButtonCorner: playButtonCorner, playButtonText: playButtonText, height: height, ratio: ratio, fullscreen: fullscreen, analyticsEvents: analyticsEvents, autoplay: autoplay, onImageLoad: onImageLoad }));
|
|
50
50
|
}
|
|
51
51
|
if (dataLens) {
|
|
52
52
|
result = react_1.default.createElement(DataLens_1.default, { dataLens: dataLens });
|
|
@@ -83,6 +83,7 @@ const Media = (props) => {
|
|
|
83
83
|
ratio,
|
|
84
84
|
youtubeClassName,
|
|
85
85
|
playButtonCorner,
|
|
86
|
+
playButtonText,
|
|
86
87
|
autoplay,
|
|
87
88
|
margins,
|
|
88
89
|
]);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface TextNodeProps {
|
|
3
|
+
children?: string;
|
|
4
|
+
tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div';
|
|
5
|
+
}
|
|
6
|
+
declare const TextNode: ({ children, tag }: TextNodeProps) => React.DetailedReactHTMLElement<{
|
|
7
|
+
dangerouslySetInnerHTML: {
|
|
8
|
+
__html: string;
|
|
9
|
+
};
|
|
10
|
+
className: string;
|
|
11
|
+
}, HTMLElement> | null;
|
|
12
|
+
export default TextNode;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const react_1 = tslib_1.__importDefault(require("react"));
|
|
5
|
+
const utils_1 = require("../../utils");
|
|
6
|
+
const b = (0, utils_1.block)('tag');
|
|
7
|
+
const TextNode = ({ children, tag = 'div' }) => {
|
|
8
|
+
if (!children) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return react_1.default.createElement(tag, {
|
|
12
|
+
dangerouslySetInnerHTML: { __html: children },
|
|
13
|
+
className: b(),
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
exports.default = TextNode;
|
|
@@ -57,6 +57,14 @@ unpredictable css rules order in build */
|
|
|
57
57
|
width: 44px;
|
|
58
58
|
height: 44px;
|
|
59
59
|
}
|
|
60
|
+
.pc-VideoBlock__button_text {
|
|
61
|
+
height: 61px;
|
|
62
|
+
width: auto;
|
|
63
|
+
border-radius: 16px;
|
|
64
|
+
font-size: 24px;
|
|
65
|
+
line-height: 35px;
|
|
66
|
+
padding: 0 28px;
|
|
67
|
+
}
|
|
60
68
|
.pc-VideoBlock__icon {
|
|
61
69
|
margin-left: 1px;
|
|
62
70
|
}
|
|
@@ -49,7 +49,7 @@ function getHeight(width, ratio) {
|
|
|
49
49
|
}
|
|
50
50
|
exports.getHeight = getHeight;
|
|
51
51
|
const VideoBlock = (props) => {
|
|
52
|
-
const { stream, record, videoIframe, attributes, className, id, previewImg, previewVideo, playButton, playButtonCorner, playButtonId, height, ratio, fullscreen, analyticsEvents, autoplay, onImageLoad, } = props;
|
|
52
|
+
const { stream, record, videoIframe, attributes, className, id, previewImg, previewVideo, playButton, playButtonCorner, playButtonText, playButtonId, height, ratio, fullscreen, analyticsEvents, autoplay, onImageLoad, } = props;
|
|
53
53
|
const handleAnalytics = (0, useAnalytics_1.useAnalytics)(common_1.DefaultEventNames.VideoPreview);
|
|
54
54
|
const src = videoIframe ? videoIframe : getYoutubeVideoSrc(stream, record);
|
|
55
55
|
const ref = (0, react_1.useRef)(null);
|
|
@@ -110,7 +110,9 @@ const VideoBlock = (props) => {
|
|
|
110
110
|
iframeContent,
|
|
111
111
|
previewImg && !hidePreview && !fullscreen && (react_1.default.createElement("div", { className: b('preview'), onClick: onPreviewClick, onKeyDown: onPreviewKeyDown, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, role: "button", tabIndex: 0, "aria-labelledby": playButton ? playButtonId : buttonId },
|
|
112
112
|
isHovered && previewVideo ? (react_1.default.createElement("video", { src: previewVideo, className: b('video'), autoPlay: true, muted: true, loop: true, playsInline: true })) : (react_1.default.createElement(Image_1.default, { src: previewImg, className: b('image'), containerClassName: b('image-wrapper'), onLoad: onImageLoad })),
|
|
113
|
-
playButton || (react_1.default.createElement("button", { title: "Play", id: buttonId, className: b('button', {
|
|
114
|
-
|
|
113
|
+
playButton || (react_1.default.createElement("button", { title: "Play", id: buttonId, className: b('button', {
|
|
114
|
+
corner: playButtonCorner,
|
|
115
|
+
text: Boolean(playButtonText),
|
|
116
|
+
}) }, playButtonText ? (react_1.default.createElement("div", { className: b('button-text') }, playButtonText)) : (react_1.default.createElement(uikit_1.Icon, { className: b('icon'), data: icons_1.PlayFill, size: playButtonCorner ? 16 : 24 }))))))));
|
|
115
117
|
};
|
|
116
118
|
exports.default = VideoBlock;
|
|
@@ -228,6 +228,9 @@ export interface HeaderBlockProps {
|
|
|
228
228
|
theme?: 'light' | 'dark';
|
|
229
229
|
unicorn?: string;
|
|
230
230
|
unicornSdkUrl?: string;
|
|
231
|
+
unicornFallbackImage?: string;
|
|
232
|
+
unicornFallbackImageMobile?: string;
|
|
233
|
+
forceMobileImage?: boolean;
|
|
231
234
|
verticalOffset?: '0' | 's' | 'm' | 'l' | 'xl';
|
|
232
235
|
verticalOffsetTop?: 's' | 'm' | 'l' | 'xl';
|
|
233
236
|
verticalOffsetBottom?: 's' | 'm' | 'l' | 'xl';
|
|
@@ -246,7 +246,7 @@ unpredictable css rules order in build */
|
|
|
246
246
|
z-index: 5;
|
|
247
247
|
}
|
|
248
248
|
@media (max-width: 769px) {
|
|
249
|
-
.pc-header-block_has-background .pc-header-block__background.pc-header-block__background_unicorn, .pc-header-block_has-background .pc-header-block__background.pc-header-block__background_media.pc-header-block__background_unicorn {
|
|
249
|
+
.pc-header-block_has-background .pc-header-block__background.pc-header-block__background_unicorn:not(.pc-header-block__background_unicorn-fallback), .pc-header-block_has-background .pc-header-block__background.pc-header-block__background_media.pc-header-block__background_unicorn:not(.pc-header-block__background_unicorn-fallback) {
|
|
250
250
|
display: none;
|
|
251
251
|
}
|
|
252
252
|
}
|
|
@@ -263,6 +263,23 @@ unpredictable css rules order in build */
|
|
|
263
263
|
width: 100%;
|
|
264
264
|
background-color: var(--g-color-base-background);
|
|
265
265
|
}
|
|
266
|
+
.pc-header-block__fallback {
|
|
267
|
+
position: absolute;
|
|
268
|
+
z-index: 1;
|
|
269
|
+
inset: 0;
|
|
270
|
+
pointer-events: none;
|
|
271
|
+
opacity: 0;
|
|
272
|
+
transition: opacity 0.3s ease;
|
|
273
|
+
}
|
|
274
|
+
.pc-header-block__fallback_visible {
|
|
275
|
+
opacity: 1;
|
|
276
|
+
}
|
|
277
|
+
.pc-header-block__fallback-image {
|
|
278
|
+
display: block;
|
|
279
|
+
width: 100%;
|
|
280
|
+
height: 100%;
|
|
281
|
+
object-fit: cover;
|
|
282
|
+
}
|
|
266
283
|
.pc-header-block__overtitle {
|
|
267
284
|
font-size: var(--g-text-body-3-font-size, var(--pc-text-body-3-font-size));
|
|
268
285
|
line-height: var(--g-text-body-3-line-height, var(--pc-text-body-3-line-height));
|
|
@@ -17,6 +17,7 @@ import HeaderStock from './HeaderStock/HeaderStock';
|
|
|
17
17
|
import HeaderTag from './HeaderTag/HeaderTag';
|
|
18
18
|
import HeaderTags from './HeaderTags/HeaderTags';
|
|
19
19
|
import SwitchingTitle from './SwitchingTitle/SwitchingTtitle';
|
|
20
|
+
import { useAdaptiveUnicornBackground } from './useAdaptiveUnicornBackground';
|
|
20
21
|
import { getImageSize, getTitleSizes, titleWithImageSizes } from './utils';
|
|
21
22
|
import './Header.css';
|
|
22
23
|
const b = block('header-block');
|
|
@@ -29,7 +30,7 @@ const Background = ({ background, isMobile }) => {
|
|
|
29
30
|
const FullWidthBackground = ({ background }) => (React.createElement("div", { className: b('background', { ['full-width']: true }), style: { backgroundColor: background === null || background === void 0 ? void 0 : background.color } }));
|
|
30
31
|
// eslint-disable-next-line complexity
|
|
31
32
|
export const HeaderBlock = (props) => {
|
|
32
|
-
const { title, switchingTitle, topTags, bottomTags, overtitle, description, buttons, stock, stockPrice, stockShares, image, video, width = 's', imageSize, offset = 'default', background, theme: textTheme = 'light', verticalOffset = 'm', verticalOffsetTop, verticalOffsetBottom, className, breadcrumbs, status, renderTitle, children, mediaView = 'full', backgroundEffect, headerSpace, backLink, unicorn, unicornSdkUrl, } = props;
|
|
33
|
+
const { title, switchingTitle, topTags, bottomTags, overtitle, description, buttons, stock, stockPrice, stockShares, image, video, width = 's', imageSize, offset = 'default', background, theme: textTheme = 'light', verticalOffset = 'm', verticalOffsetTop, verticalOffsetBottom, className, breadcrumbs, status, renderTitle, children, mediaView = 'full', backgroundEffect, headerSpace, backLink, unicorn, unicornSdkUrl, unicornFallbackImage, unicornFallbackImageMobile, forceMobileImage = true, } = props;
|
|
33
34
|
const isMobile = useContext(MobileContext);
|
|
34
35
|
const { backButton, blockTag } = useContext(HeaderContext);
|
|
35
36
|
const theme = useTheme();
|
|
@@ -41,6 +42,17 @@ export const HeaderBlock = (props) => {
|
|
|
41
42
|
const imageThemed = useMemo(() => image && getThemedValue(image, theme), [image, theme]);
|
|
42
43
|
const videoThemed = useMemo(() => video && getThemedValue(video, theme), [theme, video]);
|
|
43
44
|
const fullWidth = (backgroundThemed === null || backgroundThemed === void 0 ? void 0 : backgroundThemed.fullWidth) || (backgroundThemed === null || backgroundThemed === void 0 ? void 0 : backgroundThemed.fullWidthMedia);
|
|
45
|
+
const fallbackImage = isMobile
|
|
46
|
+
? unicornFallbackImageMobile || unicornFallbackImage
|
|
47
|
+
: unicornFallbackImage || unicornFallbackImageMobile;
|
|
48
|
+
const forceFallbackOnMobile = isMobile && forceMobileImage && Boolean(fallbackImage);
|
|
49
|
+
const { backgroundRef, handleFallbackImageLoad, handleFallbackTransitionEnd, handleSceneLoad, isFallbackVisible, shouldMountScene, shouldPlayScene, showFallback, } = useAdaptiveUnicornBackground({
|
|
50
|
+
enabled: Boolean(unicorn) && !forceFallbackOnMobile,
|
|
51
|
+
fallbackAvailable: Boolean(fallbackImage),
|
|
52
|
+
});
|
|
53
|
+
const showFallbackImage = forceFallbackOnMobile || showFallback;
|
|
54
|
+
const isFallbackImageVisible = forceFallbackOnMobile || isFallbackVisible;
|
|
55
|
+
const shouldRenderScene = !forceFallbackOnMobile && shouldMountScene;
|
|
44
56
|
const titleId = useUniqId();
|
|
45
57
|
const headerRef = useRef(null);
|
|
46
58
|
const hasBackground = Boolean(backgroundThemed || backgroundEffect);
|
|
@@ -70,8 +82,14 @@ export const HeaderBlock = (props) => {
|
|
|
70
82
|
backgroundThemed && fullWidth && React.createElement(FullWidthBackground, { background: backgroundThemed }),
|
|
71
83
|
backgroundThemed && React.createElement(Background, { background: backgroundThemed, isMobile: isMobile }),
|
|
72
84
|
backgroundEffect && backgroundEffect.firstSrc && backgroundEffect.secondSrc && (React.createElement(BackgroundEffect, Object.assign({}, backgroundEffect, { attachRef: headerRef }))),
|
|
73
|
-
unicorn && (React.createElement("div", { className: b('background', {
|
|
74
|
-
|
|
85
|
+
unicorn && (React.createElement("div", { ref: backgroundRef, className: b('background', {
|
|
86
|
+
unicorn: true,
|
|
87
|
+
'unicorn-fallback': showFallbackImage,
|
|
88
|
+
}) },
|
|
89
|
+
showFallbackImage && fallbackImage && (React.createElement("picture", { className: b('fallback', { visible: isFallbackImageVisible }), onTransitionEnd: handleFallbackTransitionEnd },
|
|
90
|
+
unicornFallbackImageMobile && (React.createElement("source", { media: "(max-width: 768px)", srcSet: unicornFallbackImageMobile })),
|
|
91
|
+
React.createElement("img", { alt: "", "aria-hidden": "true", className: b('fallback-image'), onLoad: handleFallbackImageLoad, src: fallbackImage }))),
|
|
92
|
+
shouldRenderScene && (React.createElement(UnicornScene, { jsonFilePath: unicorn, onLoad: handleSceneLoad, play: shouldPlayScene, sdkUrl: unicornSdkUrl })))),
|
|
75
93
|
React.createElement(Grid, { containerClass: b('container-fluid') },
|
|
76
94
|
React.createElement(Breadcrumbs, { breadcrumbs: breadcrumbs, theme: textTheme }),
|
|
77
95
|
React.createElement(BackButton, { backButton: backButtonItem, theme: textTheme }),
|
|
@@ -1078,6 +1078,15 @@ export declare const HeaderProperties: {
|
|
|
1078
1078
|
unicornSdkUrl: {
|
|
1079
1079
|
type: string;
|
|
1080
1080
|
};
|
|
1081
|
+
unicornFallbackImage: {
|
|
1082
|
+
type: string;
|
|
1083
|
+
};
|
|
1084
|
+
unicornFallbackImageMobile: {
|
|
1085
|
+
type: string;
|
|
1086
|
+
};
|
|
1087
|
+
forceMobileImage: {
|
|
1088
|
+
type: string;
|
|
1089
|
+
};
|
|
1081
1090
|
breadcrumbs: {
|
|
1082
1091
|
type: string;
|
|
1083
1092
|
additionalProperties: boolean;
|
|
@@ -1948,6 +1957,15 @@ export declare const HeaderBlock: {
|
|
|
1948
1957
|
unicornSdkUrl: {
|
|
1949
1958
|
type: string;
|
|
1950
1959
|
};
|
|
1960
|
+
unicornFallbackImage: {
|
|
1961
|
+
type: string;
|
|
1962
|
+
};
|
|
1963
|
+
unicornFallbackImageMobile: {
|
|
1964
|
+
type: string;
|
|
1965
|
+
};
|
|
1966
|
+
forceMobileImage: {
|
|
1967
|
+
type: string;
|
|
1968
|
+
};
|
|
1951
1969
|
breadcrumbs: {
|
|
1952
1970
|
type: string;
|
|
1953
1971
|
additionalProperties: boolean;
|
|
@@ -145,6 +145,9 @@ export const HeaderProperties = {
|
|
|
145
145
|
},
|
|
146
146
|
unicorn: { type: 'string' },
|
|
147
147
|
unicornSdkUrl: { type: 'string' },
|
|
148
|
+
unicornFallbackImage: { type: 'string' },
|
|
149
|
+
unicornFallbackImageMobile: { type: 'string' },
|
|
150
|
+
forceMobileImage: { type: 'boolean' },
|
|
148
151
|
breadcrumbs: {
|
|
149
152
|
type: 'object',
|
|
150
153
|
additionalProperties: false,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function useAdaptiveUnicornBackground({ enabled, fallbackAvailable, }: {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
fallbackAvailable: boolean;
|
|
4
|
+
}): {
|
|
5
|
+
backgroundRef: import("react").RefObject<HTMLDivElement>;
|
|
6
|
+
handleFallbackImageLoad: () => void;
|
|
7
|
+
handleFallbackTransitionEnd: () => void;
|
|
8
|
+
handleSceneLoad: () => void;
|
|
9
|
+
isFallbackVisible: boolean;
|
|
10
|
+
shouldMountScene: boolean;
|
|
11
|
+
shouldPlayScene: boolean;
|
|
12
|
+
showFallback: boolean;
|
|
13
|
+
};
|