@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
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
const REDUCED_MOTION_QUERY = '(prefers-reduced-motion: reduce)';
|
|
3
|
+
const LOW_DEVICE_MEMORY_GB = 4;
|
|
4
|
+
const LOW_HARDWARE_CONCURRENCY = 4;
|
|
5
|
+
const SLOW_EFFECTIVE_TYPES = new Set(['slow-2g', '2g']);
|
|
6
|
+
const FRAME_WARMUP_COUNT = 5;
|
|
7
|
+
const SLOW_FRAME_MS = 50;
|
|
8
|
+
const SLOW_FRAME_LIMIT = 8;
|
|
9
|
+
const LONG_TASK_MS = 100;
|
|
10
|
+
const LONG_TASK_LIMIT = 2;
|
|
11
|
+
const FALLBACK_FADE_TIMEOUT_MS = 500;
|
|
12
|
+
function prefersStaticBackground() {
|
|
13
|
+
var _a;
|
|
14
|
+
if (typeof window === 'undefined') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const navigatorWithSignals = navigator;
|
|
18
|
+
const connection = navigatorWithSignals.connection;
|
|
19
|
+
return (((_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, REDUCED_MOTION_QUERY).matches) ||
|
|
20
|
+
(connection === null || connection === void 0 ? void 0 : connection.saveData) === true ||
|
|
21
|
+
((connection === null || connection === void 0 ? void 0 : connection.effectiveType) ? SLOW_EFFECTIVE_TYPES.has(connection.effectiveType) : false) ||
|
|
22
|
+
(navigatorWithSignals.deviceMemory
|
|
23
|
+
? navigatorWithSignals.deviceMemory <= LOW_DEVICE_MEMORY_GB
|
|
24
|
+
: false) ||
|
|
25
|
+
(navigator.hardwareConcurrency
|
|
26
|
+
? navigator.hardwareConcurrency <= LOW_HARDWARE_CONCURRENCY
|
|
27
|
+
: false));
|
|
28
|
+
}
|
|
29
|
+
function supportsLongTaskObserver() {
|
|
30
|
+
var _a;
|
|
31
|
+
return (typeof PerformanceObserver !== 'undefined' &&
|
|
32
|
+
((_a = PerformanceObserver.supportedEntryTypes) === null || _a === void 0 ? void 0 : _a.includes('longtask')));
|
|
33
|
+
}
|
|
34
|
+
function isFallbackShortcut(event) {
|
|
35
|
+
return (event.altKey && event.shiftKey && (event.code === 'KeyU' || event.key.toLowerCase() === 'u'));
|
|
36
|
+
}
|
|
37
|
+
export function useAdaptiveUnicornBackground({ enabled, fallbackAvailable, }) {
|
|
38
|
+
const backgroundRef = useRef(null);
|
|
39
|
+
const [mode, setMode] = useState('checking');
|
|
40
|
+
const [isNearViewport, setIsNearViewport] = useState(false);
|
|
41
|
+
const [fallbackFadeStarted, setFallbackFadeStarted] = useState(false);
|
|
42
|
+
const [fallbackImageLoaded, setFallbackImageLoaded] = useState(false);
|
|
43
|
+
const [sceneLoaded, setSceneLoaded] = useState(false);
|
|
44
|
+
const degradeBackground = useCallback(() => {
|
|
45
|
+
setMode((currentMode) => {
|
|
46
|
+
if (!fallbackAvailable) {
|
|
47
|
+
return 'paused';
|
|
48
|
+
}
|
|
49
|
+
return currentMode === 'animation' && isNearViewport ? 'fallback-fading' : 'fallback';
|
|
50
|
+
});
|
|
51
|
+
}, [fallbackAvailable, isNearViewport]);
|
|
52
|
+
const finishFallbackFade = useCallback(() => {
|
|
53
|
+
setMode((currentMode) => (currentMode === 'fallback-fading' ? 'fallback' : currentMode));
|
|
54
|
+
}, []);
|
|
55
|
+
const handleFallbackImageLoad = useCallback(() => {
|
|
56
|
+
if (mode === 'fallback-fading') {
|
|
57
|
+
setFallbackImageLoaded(true);
|
|
58
|
+
}
|
|
59
|
+
}, [mode]);
|
|
60
|
+
const handleSceneLoad = useCallback(() => {
|
|
61
|
+
setSceneLoaded(true);
|
|
62
|
+
}, []);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
setSceneLoaded(false);
|
|
65
|
+
setIsNearViewport(false);
|
|
66
|
+
if (!enabled) {
|
|
67
|
+
setMode('paused');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (prefersStaticBackground()) {
|
|
71
|
+
setMode(fallbackAvailable ? 'fallback' : 'paused');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setMode('animation');
|
|
75
|
+
}, [enabled, fallbackAvailable]);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (!enabled) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
const handleKeyDown = (event) => {
|
|
81
|
+
if (isFallbackShortcut(event)) {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
degradeBackground();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
87
|
+
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
88
|
+
}, [degradeBackground, enabled]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (mode !== 'fallback-fading') {
|
|
91
|
+
setFallbackFadeStarted(false);
|
|
92
|
+
setFallbackImageLoaded(false);
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}, [mode]);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (mode !== 'fallback-fading' || !fallbackImageLoaded) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
if (typeof window === 'undefined' || !window.requestAnimationFrame) {
|
|
101
|
+
setFallbackFadeStarted(true);
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const frameId = window.requestAnimationFrame(() => setFallbackFadeStarted(true));
|
|
105
|
+
return () => { var _a; return (_a = window.cancelAnimationFrame) === null || _a === void 0 ? void 0 : _a.call(window, frameId); };
|
|
106
|
+
}, [fallbackImageLoaded, mode]);
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (mode !== 'fallback-fading' || !fallbackFadeStarted) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
const fallbackTimer = setTimeout(finishFallbackFade, FALLBACK_FADE_TIMEOUT_MS);
|
|
112
|
+
return () => clearTimeout(fallbackTimer);
|
|
113
|
+
}, [fallbackFadeStarted, finishFallbackFade, mode]);
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!enabled || mode !== 'animation') {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
const target = backgroundRef.current;
|
|
119
|
+
if (!target || typeof IntersectionObserver === 'undefined') {
|
|
120
|
+
setIsNearViewport(true);
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
124
|
+
setIsNearViewport(entry.isIntersecting);
|
|
125
|
+
if (!entry.isIntersecting) {
|
|
126
|
+
setSceneLoaded(false);
|
|
127
|
+
}
|
|
128
|
+
}, { rootMargin: '300px 0px', threshold: 0 });
|
|
129
|
+
observer.observe(target);
|
|
130
|
+
return () => observer.disconnect();
|
|
131
|
+
}, [enabled, mode]);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (mode !== 'animation' ||
|
|
134
|
+
!isNearViewport ||
|
|
135
|
+
!sceneLoaded ||
|
|
136
|
+
typeof window === 'undefined' ||
|
|
137
|
+
!window.requestAnimationFrame ||
|
|
138
|
+
!window.cancelAnimationFrame) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
let frameId = 0;
|
|
142
|
+
let previousTime;
|
|
143
|
+
let measuredFrames = 0;
|
|
144
|
+
let slowFrames = 0;
|
|
145
|
+
let longTasks = 0;
|
|
146
|
+
let stopped = false;
|
|
147
|
+
let observer;
|
|
148
|
+
const degradeOnce = () => {
|
|
149
|
+
if (stopped) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
stopped = true;
|
|
153
|
+
degradeBackground();
|
|
154
|
+
};
|
|
155
|
+
const tick = (time) => {
|
|
156
|
+
if (stopped) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (previousTime !== undefined) {
|
|
160
|
+
measuredFrames += 1;
|
|
161
|
+
if (measuredFrames > FRAME_WARMUP_COUNT && time - previousTime > SLOW_FRAME_MS) {
|
|
162
|
+
slowFrames += 1;
|
|
163
|
+
if (slowFrames >= SLOW_FRAME_LIMIT) {
|
|
164
|
+
degradeOnce();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
previousTime = time;
|
|
170
|
+
frameId = window.requestAnimationFrame(tick);
|
|
171
|
+
};
|
|
172
|
+
frameId = window.requestAnimationFrame(tick);
|
|
173
|
+
if (supportsLongTaskObserver()) {
|
|
174
|
+
observer = new PerformanceObserver((entries) => {
|
|
175
|
+
entries.getEntries().forEach((entry) => {
|
|
176
|
+
if (entry.duration >= LONG_TASK_MS) {
|
|
177
|
+
longTasks += 1;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
if (longTasks >= LONG_TASK_LIMIT) {
|
|
181
|
+
degradeOnce();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
observer.observe({ entryTypes: ['longtask'] });
|
|
185
|
+
}
|
|
186
|
+
return () => {
|
|
187
|
+
stopped = true;
|
|
188
|
+
window.cancelAnimationFrame(frameId);
|
|
189
|
+
observer === null || observer === void 0 ? void 0 : observer.disconnect();
|
|
190
|
+
};
|
|
191
|
+
}, [degradeBackground, isNearViewport, mode, sceneLoaded]);
|
|
192
|
+
return {
|
|
193
|
+
backgroundRef,
|
|
194
|
+
handleFallbackImageLoad,
|
|
195
|
+
handleFallbackTransitionEnd: finishFallbackFade,
|
|
196
|
+
handleSceneLoad,
|
|
197
|
+
isFallbackVisible: mode === 'fallback' || (mode === 'fallback-fading' && fallbackFadeStarted),
|
|
198
|
+
shouldMountScene: (mode === 'animation' && isNearViewport) || mode === 'fallback-fading',
|
|
199
|
+
shouldPlayScene: (mode === 'animation' && isNearViewport) || mode === 'fallback-fading',
|
|
200
|
+
showFallback: (mode === 'fallback-fading' || mode === 'fallback') && fallbackAvailable,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -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;
|
|
@@ -11,7 +11,7 @@ import Video from './Video/Video';
|
|
|
11
11
|
import './Media.css';
|
|
12
12
|
const b = block('Media');
|
|
13
13
|
export const Media = (props) => {
|
|
14
|
-
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;
|
|
14
|
+
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;
|
|
15
15
|
const [hasVideoFallback, setHasVideoFallback] = useState(false);
|
|
16
16
|
const { microdata } = useContext(InnerContext);
|
|
17
17
|
const qaAttributes = getQaAttrubutes(qa, 'video');
|
|
@@ -43,7 +43,7 @@ export const Media = (props) => {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
if (youtube || videoIframe) {
|
|
46
|
-
result = (React.createElement(IframeVideoBlock, { 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 }));
|
|
46
|
+
result = (React.createElement(IframeVideoBlock, { 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 }));
|
|
47
47
|
}
|
|
48
48
|
if (dataLens) {
|
|
49
49
|
result = React.createElement(DataLens, { dataLens: dataLens });
|
|
@@ -80,6 +80,7 @@ export const Media = (props) => {
|
|
|
80
80
|
ratio,
|
|
81
81
|
youtubeClassName,
|
|
82
82
|
playButtonCorner,
|
|
83
|
+
playButtonText,
|
|
83
84
|
autoplay,
|
|
84
85
|
margins,
|
|
85
86
|
]);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './TextNode.css';
|
|
3
|
+
export interface TextNodeProps {
|
|
4
|
+
children?: string;
|
|
5
|
+
tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div';
|
|
6
|
+
}
|
|
7
|
+
declare const TextNode: ({ children, tag }: TextNodeProps) => React.DetailedReactHTMLElement<{
|
|
8
|
+
dangerouslySetInnerHTML: {
|
|
9
|
+
__html: string;
|
|
10
|
+
};
|
|
11
|
+
className: string;
|
|
12
|
+
}, HTMLElement> | null;
|
|
13
|
+
export default TextNode;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { block } from '../../utils';
|
|
3
|
+
import './TextNode.css';
|
|
4
|
+
const b = block('tag');
|
|
5
|
+
const TextNode = ({ children, tag = 'div' }) => {
|
|
6
|
+
if (!children) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
return React.createElement(tag, {
|
|
10
|
+
dangerouslySetInnerHTML: { __html: children },
|
|
11
|
+
className: b(),
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
export 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
|
}
|
|
@@ -45,7 +45,7 @@ export function getHeight(width, ratio) {
|
|
|
45
45
|
return (width / 16) * 9;
|
|
46
46
|
}
|
|
47
47
|
const VideoBlock = (props) => {
|
|
48
|
-
const { stream, record, videoIframe, attributes, className, id, previewImg, previewVideo, playButton, playButtonCorner, playButtonId, height, ratio, fullscreen, analyticsEvents, autoplay, onImageLoad, } = props;
|
|
48
|
+
const { stream, record, videoIframe, attributes, className, id, previewImg, previewVideo, playButton, playButtonCorner, playButtonText, playButtonId, height, ratio, fullscreen, analyticsEvents, autoplay, onImageLoad, } = props;
|
|
49
49
|
const handleAnalytics = useAnalytics(DefaultEventNames.VideoPreview);
|
|
50
50
|
const src = videoIframe ? videoIframe : getYoutubeVideoSrc(stream, record);
|
|
51
51
|
const ref = useRef(null);
|
|
@@ -106,7 +106,9 @@ const VideoBlock = (props) => {
|
|
|
106
106
|
iframeContent,
|
|
107
107
|
previewImg && !hidePreview && !fullscreen && (React.createElement("div", { className: b('preview'), onClick: onPreviewClick, onKeyDown: onPreviewKeyDown, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, role: "button", tabIndex: 0, "aria-labelledby": playButton ? playButtonId : buttonId },
|
|
108
108
|
isHovered && previewVideo ? (React.createElement("video", { src: previewVideo, className: b('video'), autoPlay: true, muted: true, loop: true, playsInline: true })) : (React.createElement(Image, { src: previewImg, className: b('image'), containerClassName: b('image-wrapper'), onLoad: onImageLoad })),
|
|
109
|
-
playButton || (React.createElement("button", { title: "Play", id: buttonId, className: b('button', {
|
|
110
|
-
|
|
109
|
+
playButton || (React.createElement("button", { title: "Play", id: buttonId, className: b('button', {
|
|
110
|
+
corner: playButtonCorner,
|
|
111
|
+
text: Boolean(playButtonText),
|
|
112
|
+
}) }, playButtonText ? (React.createElement("div", { className: b('button-text') }, playButtonText)) : (React.createElement(Icon, { className: b('icon'), data: PlayFill, size: playButtonCorner ? 16 : 24 }))))))));
|
|
111
113
|
};
|
|
112
114
|
export 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';
|