@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.
Files changed (37) hide show
  1. package/build/cjs/blocks/Header/Header.css +18 -1
  2. package/build/cjs/blocks/Header/Header.js +21 -3
  3. package/build/cjs/blocks/Header/schema.d.ts +18 -0
  4. package/build/cjs/blocks/Header/schema.js +3 -0
  5. package/build/cjs/blocks/Header/useAdaptiveUnicornBackground.d.ts +13 -0
  6. package/build/cjs/blocks/Header/useAdaptiveUnicornBackground.js +206 -0
  7. package/build/cjs/blocks/HeaderSlider/schema.d.ts +9 -0
  8. package/build/cjs/components/Media/Media.js +3 -2
  9. package/build/cjs/components/TextNode/TextNode.css +2 -0
  10. package/build/cjs/components/TextNode/TextNode.d.ts +12 -0
  11. package/build/cjs/components/TextNode/TextNode.js +16 -0
  12. package/build/cjs/components/VideoBlock/VideoBlock.css +8 -0
  13. package/build/cjs/components/VideoBlock/VideoBlock.d.ts +1 -0
  14. package/build/cjs/components/VideoBlock/VideoBlock.js +5 -3
  15. package/build/cjs/models/constructor-items/blocks.d.ts +3 -0
  16. package/build/cjs/models/constructor-items/common.d.ts +1 -0
  17. package/build/esm/blocks/Header/Header.css +18 -1
  18. package/build/esm/blocks/Header/Header.js +21 -3
  19. package/build/esm/blocks/Header/schema.d.ts +18 -0
  20. package/build/esm/blocks/Header/schema.js +3 -0
  21. package/build/esm/blocks/Header/useAdaptiveUnicornBackground.d.ts +13 -0
  22. package/build/esm/blocks/Header/useAdaptiveUnicornBackground.js +202 -0
  23. package/build/esm/blocks/HeaderSlider/schema.d.ts +9 -0
  24. package/build/esm/components/Media/Media.js +3 -2
  25. package/build/esm/components/TextNode/TextNode.css +2 -0
  26. package/build/esm/components/TextNode/TextNode.d.ts +13 -0
  27. package/build/esm/components/TextNode/TextNode.js +14 -0
  28. package/build/esm/components/VideoBlock/VideoBlock.css +8 -0
  29. package/build/esm/components/VideoBlock/VideoBlock.d.ts +1 -0
  30. package/build/esm/components/VideoBlock/VideoBlock.js +5 -3
  31. package/build/esm/models/constructor-items/blocks.d.ts +3 -0
  32. package/build/esm/models/constructor-items/common.d.ts +1 -0
  33. package/package.json +1 -1
  34. package/schema/index.js +1 -1
  35. package/server/models/constructor-items/blocks.d.ts +3 -0
  36. package/server/models/constructor-items/common.d.ts +1 -0
  37. 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,2 @@
1
+ /* use this for style redefinitions to awoid problems with
2
+ unpredictable css rules order in build */
@@ -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
  }
@@ -17,6 +17,7 @@ export interface VideoBlockProps extends AnalyticsEventsBase {
17
17
  previewVideo?: string;
18
18
  playButton?: React.ReactNode;
19
19
  playButtonCorner?: boolean;
20
+ playButtonText?: string;
20
21
  playButtonId?: string;
21
22
  height?: number;
22
23
  ratio?: number | 'auto';
@@ -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', { corner: playButtonCorner }) },
110
- React.createElement(Icon, { className: b('icon'), data: PlayFill, size: playButtonCorner ? 16 : 24 })))))));
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';
@@ -196,6 +196,7 @@ export interface MediaComponentYoutubeProps {
196
196
  previewImg?: string;
197
197
  previewVideo?: string;
198
198
  playButtonCorner?: boolean;
199
+ playButtonText?: string;
199
200
  fullscreen?: boolean;
200
201
  }
201
202
  export interface MediaComponentImageProps {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doyourjob/gravity-ui-page-constructor",
3
- "version": "5.31.282",
3
+ "version": "5.31.284",
4
4
  "description": "Gravity UI Page Constructor",
5
5
  "license": "MIT",
6
6
  "repository": {