@gravity-ui/page-constructor 5.22.0 → 5.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/build/cjs/blocks/Header/schema.d.ts +9 -0
  2. package/build/cjs/blocks/HeaderSlider/schema.d.ts +3 -0
  3. package/build/cjs/blocks/Media/schema.d.ts +6 -0
  4. package/build/cjs/blocks/PromoFeaturesBlock/schema.d.ts +3 -0
  5. package/build/cjs/blocks/Tabs/schema.d.ts +3 -0
  6. package/build/cjs/components/ButtonTabs/ButtonTabs.js +3 -2
  7. package/build/cjs/components/Media/Media.js +4 -3
  8. package/build/cjs/components/VideoBlock/VideoBlock.d.ts +1 -0
  9. package/build/cjs/components/VideoBlock/VideoBlock.js +24 -37
  10. package/build/cjs/models/constructor-items/common.d.ts +4 -1
  11. package/build/cjs/schema/constants.d.ts +3 -0
  12. package/build/cjs/schema/validators/common.d.ts +3 -0
  13. package/build/cjs/schema/validators/common.js +3 -0
  14. package/build/cjs/sub-blocks/LayoutItem/schema.d.ts +3 -0
  15. package/build/cjs/sub-blocks/LayoutItem/utils.d.ts +1 -1
  16. package/build/cjs/sub-blocks/LayoutItem/utils.js +1 -1
  17. package/build/cjs/sub-blocks/MediaCard/schema.d.ts +3 -0
  18. package/build/esm/blocks/Header/schema.d.ts +9 -0
  19. package/build/esm/blocks/HeaderSlider/schema.d.ts +3 -0
  20. package/build/esm/blocks/Media/schema.d.ts +6 -0
  21. package/build/esm/blocks/PromoFeaturesBlock/schema.d.ts +3 -0
  22. package/build/esm/blocks/Tabs/schema.d.ts +3 -0
  23. package/build/esm/components/ButtonTabs/ButtonTabs.js +3 -2
  24. package/build/esm/components/Media/Media.js +5 -4
  25. package/build/esm/components/VideoBlock/VideoBlock.d.ts +1 -0
  26. package/build/esm/components/VideoBlock/VideoBlock.js +25 -38
  27. package/build/esm/models/constructor-items/common.d.ts +4 -1
  28. package/build/esm/schema/constants.d.ts +3 -0
  29. package/build/esm/schema/validators/common.d.ts +3 -0
  30. package/build/esm/schema/validators/common.js +3 -0
  31. package/build/esm/sub-blocks/LayoutItem/schema.d.ts +3 -0
  32. package/build/esm/sub-blocks/LayoutItem/utils.d.ts +1 -1
  33. package/build/esm/sub-blocks/LayoutItem/utils.js +1 -1
  34. package/build/esm/sub-blocks/MediaCard/schema.d.ts +3 -0
  35. package/package.json +1 -1
  36. package/server/models/constructor-items/common.d.ts +4 -1
  37. package/widget/index.js +1 -1
@@ -167,6 +167,9 @@ export declare const HeaderBackgroundProps: {
167
167
  youtube: {
168
168
  type: string;
169
169
  };
170
+ videoIframe: {
171
+ type: string;
172
+ };
170
173
  parallax: {
171
174
  type: string;
172
175
  };
@@ -669,6 +672,9 @@ export declare const HeaderProperties: {
669
672
  youtube: {
670
673
  type: string;
671
674
  };
675
+ videoIframe: {
676
+ type: string;
677
+ };
672
678
  parallax: {
673
679
  type: string;
674
680
  };
@@ -1220,6 +1226,9 @@ export declare const HeaderBlock: {
1220
1226
  youtube: {
1221
1227
  type: string;
1222
1228
  };
1229
+ videoIframe: {
1230
+ type: string;
1231
+ };
1223
1232
  parallax: {
1224
1233
  type: string;
1225
1234
  };
@@ -371,6 +371,9 @@ export declare const HeaderSliderBlock: {
371
371
  youtube: {
372
372
  type: string;
373
373
  };
374
+ videoIframe: {
375
+ type: string;
376
+ };
374
377
  parallax: {
375
378
  type: string;
376
379
  };
@@ -161,6 +161,9 @@ export declare const Media: {
161
161
  youtube: {
162
162
  type: string;
163
163
  };
164
+ videoIframe: {
165
+ type: string;
166
+ };
164
167
  parallax: {
165
168
  type: string;
166
169
  };
@@ -784,6 +787,9 @@ export declare const MediaBlock: {
784
787
  youtube: {
785
788
  type: string;
786
789
  };
790
+ videoIframe: {
791
+ type: string;
792
+ };
787
793
  parallax: {
788
794
  type: string;
789
795
  };
@@ -177,6 +177,9 @@ export declare const PromoFeaturesItem: {
177
177
  youtube: {
178
178
  type: string;
179
179
  };
180
+ videoIframe: {
181
+ type: string;
182
+ };
180
183
  parallax: {
181
184
  type: string;
182
185
  };
@@ -172,6 +172,9 @@ export declare const tabsItem: {
172
172
  youtube: {
173
173
  type: string;
174
174
  };
175
+ videoIframe: {
176
+ type: string;
177
+ };
175
178
  parallax: {
176
179
  type: string;
177
180
  };
@@ -17,10 +17,11 @@ const ButtonTabs = ({ className, items, activeTab, onSelectTab, tabSize = 'l', q
17
17
  onSelectTab(tabId, e);
18
18
  }
19
19
  }, [onSelectTab]);
20
- return (react_1.default.createElement("div", { className: b(null, className), "data-qa": qa }, items.map(({ id, title }) => {
20
+ return (react_1.default.createElement("div", { className: b(null, className), role: "tablist", "data-qa": qa }, items.map(({ id, title }) => {
21
21
  const isActive = id === activeTabId;
22
22
  return (react_1.default.createElement(index_1.Button, { text: title, className: b('item', { active: isActive }), key: title, size: tabSize, onClick: handleClick(id), extraProps: {
23
- 'aria-current': isActive || undefined,
23
+ role: 'tab',
24
+ 'aria-selected': isActive,
24
25
  } }));
25
26
  })));
26
27
  };
@@ -12,7 +12,7 @@ const Image_1 = tslib_1.__importDefault(require("./Image/Image"));
12
12
  const Video_1 = tslib_1.__importDefault(require("./Video/Video"));
13
13
  const b = (0, utils_1.block)('Media');
14
14
  const Media = (props) => {
15
- const { image, video, youtube, dataLens, color, height, previewImg, parallax = false, fullscreen, analyticsEvents, className, imageClassName, videoClassName, youtubeClassName, disableImageSliderForArrayInput, playVideo = true, isBackground, playButton, customBarControlsClassName, qa, ratio, autoplay, onImageLoad, iframe, margins, } = props;
15
+ const { image, video, youtube, videoIframe, dataLens, color, height, previewImg, parallax = false, fullscreen, analyticsEvents, className, imageClassName, videoClassName, youtubeClassName, disableImageSliderForArrayInput, playVideo = true, isBackground, playButton, customBarControlsClassName, qa, ratio, autoplay, onImageLoad, iframe, margins, } = props;
16
16
  const [hasVideoFallback, setHasVideoFallback] = (0, react_1.useState)(false);
17
17
  const qaAttributes = (0, utils_1.getQaAttrubutes)(qa, 'video');
18
18
  const content = (0, react_1.useMemo)(() => {
@@ -42,8 +42,8 @@ const Media = (props) => {
42
42
  result.push(react_1.default.createElement(Video_1.default, Object.assign({}, videoProps, { qa: qaAttributes.video })));
43
43
  }
44
44
  }
45
- if (youtube) {
46
- result = (react_1.default.createElement(VideoBlock_1.default, { className: b('youtube', youtubeClassName), record: youtube, attributes: { color: 'white', rel: '0' }, previewImg: previewImg, height: height, fullscreen: fullscreen, analyticsEvents: analyticsEvents, autoplay: autoplay, onImageLoad: onImageLoad }));
45
+ if (youtube || videoIframe) {
46
+ result = (react_1.default.createElement(VideoBlock_1.default, { className: b('youtube', youtubeClassName), record: youtube, videoIframe: videoIframe, attributes: { color: 'white', rel: '0' }, previewImg: previewImg, height: height, fullscreen: fullscreen, analyticsEvents: analyticsEvents, autoplay: autoplay, onImageLoad: onImageLoad }));
47
47
  }
48
48
  if (dataLens) {
49
49
  result = react_1.default.createElement(DataLens_1.default, { dataLens: dataLens });
@@ -55,6 +55,7 @@ const Media = (props) => {
55
55
  }, [
56
56
  image,
57
57
  video,
58
+ videoIframe,
58
59
  youtube,
59
60
  dataLens,
60
61
  iframe,
@@ -9,6 +9,7 @@ export interface VideoBlockProps extends AnalyticsEventsBase {
9
9
  id?: string;
10
10
  stream?: string;
11
11
  record?: string;
12
+ videoIframe?: string;
12
13
  attributes?: Record<string, string>;
13
14
  className?: string;
14
15
  previewImg?: string;
@@ -24,8 +24,11 @@ exports.AUTOPLAY_ATTRIBUTES = {
24
24
  autoplay: 1,
25
25
  mute: 1,
26
26
  };
27
+ const NO_AUTOPLAY_ATTRIBUTES = {
28
+ autoplay: 0,
29
+ };
27
30
  const b = (0, utils_1.block)('VideoBlock');
28
- function getVideoSrc(stream, record) {
31
+ function getYoutubeVideoSrc(stream, record) {
29
32
  if (!stream && !record) {
30
33
  return null;
31
34
  }
@@ -44,22 +47,22 @@ function getHeight(width) {
44
47
  }
45
48
  exports.getHeight = getHeight;
46
49
  const VideoBlock = (props) => {
47
- const { stream, record, attributes, className, id, previewImg, playButton, height, fullscreen, analyticsEvents, autoplay, onImageLoad, } = props;
50
+ const { stream, record, videoIframe, attributes, className, id, previewImg, playButton, height, fullscreen, analyticsEvents, autoplay, onImageLoad, } = props;
48
51
  const handleAnalytics = (0, useAnalytics_1.useAnalytics)(common_1.DefaultEventNames.VideoPreview);
49
- const src = getVideoSrc(stream, record);
52
+ const src = videoIframe ? videoIframe : getYoutubeVideoSrc(stream, record);
50
53
  const ref = (0, react_1.useRef)(null);
51
- const iframeRef = (0, react_1.useRef)();
52
54
  const [hidePreview, setHidePreview] = (0, react_1.useState)(false);
53
- const norender = (!stream && !record) || !src;
54
55
  const [currentHeight, setCurrentHeight] = (0, react_1.useState)(height || undefined);
55
- const fullId = id || (0, uuid_1.v4)();
56
+ const fullId = (0, react_1.useMemo)(() => id || (0, uuid_1.v4)(), [id]);
57
+ const [isPlaying, setIsPlaying] = (0, react_1.useState)(!previewImg);
58
+ const iframeSrc = src && isPlaying
59
+ ? `${src}?${(0, utils_1.getPageSearchParams)(Object.assign(Object.assign({}, (attributes || {})), (autoplay ? exports.AUTOPLAY_ATTRIBUTES : NO_AUTOPLAY_ATTRIBUTES)))}`
60
+ : undefined;
56
61
  const onPreviewClick = (0, react_1.useCallback)(() => {
57
62
  handleAnalytics(analyticsEvents);
58
- if (iframeRef.current) {
59
- iframeRef.current.src = `${src}?${(0, utils_1.getPageSearchParams)(Object.assign(Object.assign({}, exports.AUTOPLAY_ATTRIBUTES), (attributes || {})))}`;
60
- }
63
+ setIsPlaying(true);
61
64
  setTimeout(() => setHidePreview(true), AUTOPLAY_DELAY);
62
- }, [handleAnalytics, analyticsEvents, src, attributes]);
65
+ }, [handleAnalytics, analyticsEvents]);
63
66
  (0, react_1.useEffect)(() => {
64
67
  const updateSize = (0, debounce_1.default)(() => {
65
68
  setCurrentHeight(ref.current ? Math.round(getHeight(ref.current.offsetWidth)) : undefined);
@@ -70,36 +73,20 @@ const VideoBlock = (props) => {
70
73
  window.removeEventListener('resize', updateSize);
71
74
  };
72
75
  }, [height]);
73
- (0, react_1.useEffect)(() => {
74
- if (norender) {
75
- return;
76
- }
77
- if (ref.current && !iframeRef.current) {
78
- const iframe = document.createElement('iframe');
79
- iframe.id = fullId;
80
- if (!previewImg) {
81
- iframe.src = `${src}?${(0, utils_1.getPageSearchParams)(Object.assign(Object.assign({}, (attributes || {})), (autoplay ? exports.AUTOPLAY_ATTRIBUTES : {})))}`;
82
- }
83
- iframe.width = '100%';
84
- iframe.height = '100%';
85
- iframe.title = (0, i18n_1.i18n)('iframe-title');
86
- iframe.frameBorder = '0';
87
- iframe.setAttribute('allowfullscreen', 'true');
88
- iframe.setAttribute('allow', 'autoplay');
89
- iframe.setAttribute('loading', 'lazy');
90
- ref.current.appendChild(iframe);
91
- iframeRef.current = iframe;
92
- }
93
- }, [stream, record, norender, src, fullId, attributes, iframeRef, previewImg, autoplay]);
76
+ const iframeContent = (0, react_1.useMemo)(() => {
77
+ return (react_1.default.createElement("iframe", { id: fullId, src: iframeSrc, width: "100%", height: "100%", title: (0, i18n_1.i18n)('iframe-title'), frameBorder: "0", allowFullScreen: true, allow: "autoplay; fullscreen; encrypted-media; accelerometer; gyroscope; picture-in-picture; clipboard-write; web-share; screen-wake-lock", loading: "lazy" }));
78
+ }, [fullId, iframeSrc]);
94
79
  (0, react_1.useEffect)(() => {
95
80
  setHidePreview(false);
96
- }, [src, setHidePreview]);
97
- if (norender) {
81
+ }, [src]);
82
+ if (!src) {
98
83
  return null;
99
84
  }
100
- return (react_1.default.createElement("div", { className: b(null, className), ref: ref, style: { height: currentHeight } }, previewImg && !hidePreview && !fullscreen && (react_1.default.createElement("div", { className: b('preview'), onClick: onPreviewClick },
101
- react_1.default.createElement(Image_1.default, { src: previewImg, className: b('image'), containerClassName: b('image-wrapper'), onLoad: onImageLoad }),
102
- playButton || (react_1.default.createElement("button", { title: "Play", className: b('button') },
103
- react_1.default.createElement(uikit_1.Icon, { className: b('icon'), data: icons_1.PlayFill, size: 24 })))))));
85
+ return (react_1.default.createElement("div", { className: b(null, className), style: { height: currentHeight }, ref: ref },
86
+ iframeContent,
87
+ previewImg && !hidePreview && !fullscreen && (react_1.default.createElement("div", { className: b('preview'), onClick: onPreviewClick },
88
+ react_1.default.createElement(Image_1.default, { src: previewImg, className: b('image'), containerClassName: b('image-wrapper'), onLoad: onImageLoad }),
89
+ playButton || (react_1.default.createElement("button", { title: "Play", className: b('button') },
90
+ react_1.default.createElement(uikit_1.Icon, { className: b('icon'), data: icons_1.PlayFill, size: 24 })))))));
104
91
  };
105
92
  exports.default = VideoBlock;
@@ -182,6 +182,9 @@ export interface MediaComponentVideoProps extends AnalyticsEventsBase {
182
182
  ratio?: number;
183
183
  previewImg?: string;
184
184
  }
185
+ export interface MediaComponentVideoIframeProps {
186
+ videoIframe: string;
187
+ }
185
188
  export interface MediaComponentYoutubeProps {
186
189
  youtube: string;
187
190
  previewImg?: string;
@@ -201,7 +204,7 @@ export interface MediaComponentIframeProps {
201
204
  iframe: IframeProps;
202
205
  margins?: boolean;
203
206
  }
204
- export interface MediaProps extends Animatable, Partial<MediaComponentDataLensProps>, Partial<MediaComponentYoutubeProps>, Partial<MediaComponentImageProps>, Partial<MediaComponentIframeProps>, Partial<MediaComponentVideoProps> {
207
+ export interface MediaProps extends Animatable, Partial<MediaComponentDataLensProps>, Partial<MediaComponentYoutubeProps>, Partial<MediaComponentVideoIframeProps>, Partial<MediaComponentImageProps>, Partial<MediaComponentIframeProps>, Partial<MediaComponentVideoProps> {
205
208
  color?: string;
206
209
  }
207
210
  export interface BackgroundMediaProps extends MediaProps, Animatable, QAProps {
@@ -1554,6 +1554,9 @@ export declare const cardSchemas: {
1554
1554
  youtube: {
1555
1555
  type: string;
1556
1556
  };
1557
+ videoIframe: {
1558
+ type: string;
1559
+ };
1557
1560
  parallax: {
1558
1561
  type: string;
1559
1562
  };
@@ -1134,6 +1134,9 @@ export declare const MediaProps: {
1134
1134
  youtube: {
1135
1135
  type: string;
1136
1136
  };
1137
+ videoIframe: {
1138
+ type: string;
1139
+ };
1137
1140
  parallax: {
1138
1141
  type: string;
1139
1142
  };
@@ -527,6 +527,9 @@ exports.MediaProps = {
527
527
  youtube: {
528
528
  type: 'string',
529
529
  },
530
+ videoIframe: {
531
+ type: 'string',
532
+ },
530
533
  parallax: {
531
534
  type: 'boolean',
532
535
  },
@@ -162,6 +162,9 @@ export declare const LayoutItem: {
162
162
  youtube: {
163
163
  type: string;
164
164
  };
165
+ videoIframe: {
166
+ type: string;
167
+ };
165
168
  parallax: {
166
169
  type: string;
167
170
  };
@@ -14,4 +14,4 @@ export declare const getLayoutItemLinks: (links: LayoutItemProps['content']['lin
14
14
  tabIndex?: number | undefined;
15
15
  }[] | undefined;
16
16
  export declare const hasFullscreen: ({ dataLens, image }: MediaProps) => boolean;
17
- export declare const showFullscreenIcon: ({ youtube }: MediaProps) => boolean;
17
+ export declare const showFullscreenIcon: ({ youtube, videoIframe }: MediaProps) => boolean;
@@ -8,5 +8,5 @@ const hasFullscreen = ({ dataLens, image }) => {
8
8
  return !(dataLens || Array.isArray(image));
9
9
  };
10
10
  exports.hasFullscreen = hasFullscreen;
11
- const showFullscreenIcon = ({ youtube }) => !youtube;
11
+ const showFullscreenIcon = ({ youtube, videoIframe }) => !(youtube || videoIframe);
12
12
  exports.showFullscreenIcon = showFullscreenIcon;
@@ -245,6 +245,9 @@ export declare const MediaCardBlock: {
245
245
  youtube: {
246
246
  type: string;
247
247
  };
248
+ videoIframe: {
249
+ type: string;
250
+ };
248
251
  parallax: {
249
252
  type: string;
250
253
  };
@@ -167,6 +167,9 @@ export declare const HeaderBackgroundProps: {
167
167
  youtube: {
168
168
  type: string;
169
169
  };
170
+ videoIframe: {
171
+ type: string;
172
+ };
170
173
  parallax: {
171
174
  type: string;
172
175
  };
@@ -669,6 +672,9 @@ export declare const HeaderProperties: {
669
672
  youtube: {
670
673
  type: string;
671
674
  };
675
+ videoIframe: {
676
+ type: string;
677
+ };
672
678
  parallax: {
673
679
  type: string;
674
680
  };
@@ -1220,6 +1226,9 @@ export declare const HeaderBlock: {
1220
1226
  youtube: {
1221
1227
  type: string;
1222
1228
  };
1229
+ videoIframe: {
1230
+ type: string;
1231
+ };
1223
1232
  parallax: {
1224
1233
  type: string;
1225
1234
  };
@@ -371,6 +371,9 @@ export declare const HeaderSliderBlock: {
371
371
  youtube: {
372
372
  type: string;
373
373
  };
374
+ videoIframe: {
375
+ type: string;
376
+ };
374
377
  parallax: {
375
378
  type: string;
376
379
  };
@@ -161,6 +161,9 @@ export declare const Media: {
161
161
  youtube: {
162
162
  type: string;
163
163
  };
164
+ videoIframe: {
165
+ type: string;
166
+ };
164
167
  parallax: {
165
168
  type: string;
166
169
  };
@@ -784,6 +787,9 @@ export declare const MediaBlock: {
784
787
  youtube: {
785
788
  type: string;
786
789
  };
790
+ videoIframe: {
791
+ type: string;
792
+ };
787
793
  parallax: {
788
794
  type: string;
789
795
  };
@@ -177,6 +177,9 @@ export declare const PromoFeaturesItem: {
177
177
  youtube: {
178
178
  type: string;
179
179
  };
180
+ videoIframe: {
181
+ type: string;
182
+ };
180
183
  parallax: {
181
184
  type: string;
182
185
  };
@@ -172,6 +172,9 @@ export declare const tabsItem: {
172
172
  youtube: {
173
173
  type: string;
174
174
  };
175
+ videoIframe: {
176
+ type: string;
177
+ };
175
178
  parallax: {
176
179
  type: string;
177
180
  };
@@ -15,10 +15,11 @@ const ButtonTabs = ({ className, items, activeTab, onSelectTab, tabSize = 'l', q
15
15
  onSelectTab(tabId, e);
16
16
  }
17
17
  }, [onSelectTab]);
18
- return (React.createElement("div", { className: b(null, className), "data-qa": qa }, items.map(({ id, title }) => {
18
+ return (React.createElement("div", { className: b(null, className), role: "tablist", "data-qa": qa }, items.map(({ id, title }) => {
19
19
  const isActive = id === activeTabId;
20
20
  return (React.createElement(Button, { text: title, className: b('item', { active: isActive }), key: title, size: tabSize, onClick: handleClick(id), extraProps: {
21
- 'aria-current': isActive || undefined,
21
+ role: 'tab',
22
+ 'aria-selected': isActive,
22
23
  } }));
23
24
  })));
24
25
  };
@@ -1,6 +1,6 @@
1
1
  import React, { useMemo, useState } from 'react';
2
2
  import { block, getQaAttrubutes } from '../../utils';
3
- import YoutubeBlock from '../VideoBlock/VideoBlock';
3
+ import IframeVideoBlock from '../VideoBlock/VideoBlock';
4
4
  import DataLens from './DataLens/DataLens';
5
5
  import FullscreenVideo from './FullscreenVideo/FullscreenVideo';
6
6
  import Iframe from './Iframe/Iframe';
@@ -9,7 +9,7 @@ import Video from './Video/Video';
9
9
  import './Media.css';
10
10
  const b = block('Media');
11
11
  export const Media = (props) => {
12
- const { image, video, youtube, dataLens, color, height, previewImg, parallax = false, fullscreen, analyticsEvents, className, imageClassName, videoClassName, youtubeClassName, disableImageSliderForArrayInput, playVideo = true, isBackground, playButton, customBarControlsClassName, qa, ratio, autoplay, onImageLoad, iframe, margins, } = props;
12
+ const { image, video, youtube, videoIframe, dataLens, color, height, previewImg, parallax = false, fullscreen, analyticsEvents, className, imageClassName, videoClassName, youtubeClassName, disableImageSliderForArrayInput, playVideo = true, isBackground, playButton, customBarControlsClassName, qa, ratio, autoplay, onImageLoad, iframe, margins, } = props;
13
13
  const [hasVideoFallback, setHasVideoFallback] = useState(false);
14
14
  const qaAttributes = getQaAttrubutes(qa, 'video');
15
15
  const content = useMemo(() => {
@@ -39,8 +39,8 @@ export const Media = (props) => {
39
39
  result.push(React.createElement(Video, Object.assign({}, videoProps, { qa: qaAttributes.video })));
40
40
  }
41
41
  }
42
- if (youtube) {
43
- result = (React.createElement(YoutubeBlock, { className: b('youtube', youtubeClassName), record: youtube, attributes: { color: 'white', rel: '0' }, previewImg: previewImg, height: height, fullscreen: fullscreen, analyticsEvents: analyticsEvents, autoplay: autoplay, onImageLoad: onImageLoad }));
42
+ if (youtube || videoIframe) {
43
+ result = (React.createElement(IframeVideoBlock, { className: b('youtube', youtubeClassName), record: youtube, videoIframe: videoIframe, attributes: { color: 'white', rel: '0' }, previewImg: previewImg, height: height, fullscreen: fullscreen, analyticsEvents: analyticsEvents, autoplay: autoplay, onImageLoad: onImageLoad }));
44
44
  }
45
45
  if (dataLens) {
46
46
  result = React.createElement(DataLens, { dataLens: dataLens });
@@ -52,6 +52,7 @@ export const Media = (props) => {
52
52
  }, [
53
53
  image,
54
54
  video,
55
+ videoIframe,
55
56
  youtube,
56
57
  dataLens,
57
58
  iframe,
@@ -10,6 +10,7 @@ export interface VideoBlockProps extends AnalyticsEventsBase {
10
10
  id?: string;
11
11
  stream?: string;
12
12
  record?: string;
13
+ videoIframe?: string;
13
14
  attributes?: Record<string, string>;
14
15
  className?: string;
15
16
  previewImg?: string;
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable jsx-a11y/no-static-element-interactions */
2
2
  /* eslint-disable jsx-a11y/click-events-have-key-events */
3
3
  // TODO fix in https://github.com/gravity-ui/page-constructor/issues/965
4
- import React, { useCallback, useEffect, useRef, useState } from 'react';
4
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
5
  import { PlayFill } from '@gravity-ui/icons';
6
6
  import { Icon } from '@gravity-ui/uikit';
7
7
  import debounce from 'lodash/debounce';
@@ -21,8 +21,11 @@ export const AUTOPLAY_ATTRIBUTES = {
21
21
  autoplay: 1,
22
22
  mute: 1,
23
23
  };
24
+ const NO_AUTOPLAY_ATTRIBUTES = {
25
+ autoplay: 0,
26
+ };
24
27
  const b = block('VideoBlock');
25
- function getVideoSrc(stream, record) {
28
+ function getYoutubeVideoSrc(stream, record) {
26
29
  if (!stream && !record) {
27
30
  return null;
28
31
  }
@@ -40,22 +43,22 @@ export function getHeight(width) {
40
43
  return (width / 16) * 9;
41
44
  }
42
45
  const VideoBlock = (props) => {
43
- const { stream, record, attributes, className, id, previewImg, playButton, height, fullscreen, analyticsEvents, autoplay, onImageLoad, } = props;
46
+ const { stream, record, videoIframe, attributes, className, id, previewImg, playButton, height, fullscreen, analyticsEvents, autoplay, onImageLoad, } = props;
44
47
  const handleAnalytics = useAnalytics(DefaultEventNames.VideoPreview);
45
- const src = getVideoSrc(stream, record);
48
+ const src = videoIframe ? videoIframe : getYoutubeVideoSrc(stream, record);
46
49
  const ref = useRef(null);
47
- const iframeRef = useRef();
48
50
  const [hidePreview, setHidePreview] = useState(false);
49
- const norender = (!stream && !record) || !src;
50
51
  const [currentHeight, setCurrentHeight] = useState(height || undefined);
51
- const fullId = id || uuidv4();
52
+ const fullId = useMemo(() => id || uuidv4(), [id]);
53
+ const [isPlaying, setIsPlaying] = useState(!previewImg);
54
+ const iframeSrc = src && isPlaying
55
+ ? `${src}?${getPageSearchParams(Object.assign(Object.assign({}, (attributes || {})), (autoplay ? AUTOPLAY_ATTRIBUTES : NO_AUTOPLAY_ATTRIBUTES)))}`
56
+ : undefined;
52
57
  const onPreviewClick = useCallback(() => {
53
58
  handleAnalytics(analyticsEvents);
54
- if (iframeRef.current) {
55
- iframeRef.current.src = `${src}?${getPageSearchParams(Object.assign(Object.assign({}, AUTOPLAY_ATTRIBUTES), (attributes || {})))}`;
56
- }
59
+ setIsPlaying(true);
57
60
  setTimeout(() => setHidePreview(true), AUTOPLAY_DELAY);
58
- }, [handleAnalytics, analyticsEvents, src, attributes]);
61
+ }, [handleAnalytics, analyticsEvents]);
59
62
  useEffect(() => {
60
63
  const updateSize = debounce(() => {
61
64
  setCurrentHeight(ref.current ? Math.round(getHeight(ref.current.offsetWidth)) : undefined);
@@ -66,36 +69,20 @@ const VideoBlock = (props) => {
66
69
  window.removeEventListener('resize', updateSize);
67
70
  };
68
71
  }, [height]);
69
- useEffect(() => {
70
- if (norender) {
71
- return;
72
- }
73
- if (ref.current && !iframeRef.current) {
74
- const iframe = document.createElement('iframe');
75
- iframe.id = fullId;
76
- if (!previewImg) {
77
- iframe.src = `${src}?${getPageSearchParams(Object.assign(Object.assign({}, (attributes || {})), (autoplay ? AUTOPLAY_ATTRIBUTES : {})))}`;
78
- }
79
- iframe.width = '100%';
80
- iframe.height = '100%';
81
- iframe.title = i18n('iframe-title');
82
- iframe.frameBorder = '0';
83
- iframe.setAttribute('allowfullscreen', 'true');
84
- iframe.setAttribute('allow', 'autoplay');
85
- iframe.setAttribute('loading', 'lazy');
86
- ref.current.appendChild(iframe);
87
- iframeRef.current = iframe;
88
- }
89
- }, [stream, record, norender, src, fullId, attributes, iframeRef, previewImg, autoplay]);
72
+ const iframeContent = useMemo(() => {
73
+ return (React.createElement("iframe", { id: fullId, src: iframeSrc, width: "100%", height: "100%", title: i18n('iframe-title'), frameBorder: "0", allowFullScreen: true, allow: "autoplay; fullscreen; encrypted-media; accelerometer; gyroscope; picture-in-picture; clipboard-write; web-share; screen-wake-lock", loading: "lazy" }));
74
+ }, [fullId, iframeSrc]);
90
75
  useEffect(() => {
91
76
  setHidePreview(false);
92
- }, [src, setHidePreview]);
93
- if (norender) {
77
+ }, [src]);
78
+ if (!src) {
94
79
  return null;
95
80
  }
96
- return (React.createElement("div", { className: b(null, className), ref: ref, style: { height: currentHeight } }, previewImg && !hidePreview && !fullscreen && (React.createElement("div", { className: b('preview'), onClick: onPreviewClick },
97
- React.createElement(Image, { src: previewImg, className: b('image'), containerClassName: b('image-wrapper'), onLoad: onImageLoad }),
98
- playButton || (React.createElement("button", { title: "Play", className: b('button') },
99
- React.createElement(Icon, { className: b('icon'), data: PlayFill, size: 24 })))))));
81
+ return (React.createElement("div", { className: b(null, className), style: { height: currentHeight }, ref: ref },
82
+ iframeContent,
83
+ previewImg && !hidePreview && !fullscreen && (React.createElement("div", { className: b('preview'), onClick: onPreviewClick },
84
+ React.createElement(Image, { src: previewImg, className: b('image'), containerClassName: b('image-wrapper'), onLoad: onImageLoad }),
85
+ playButton || (React.createElement("button", { title: "Play", className: b('button') },
86
+ React.createElement(Icon, { className: b('icon'), data: PlayFill, size: 24 })))))));
100
87
  };
101
88
  export default VideoBlock;
@@ -182,6 +182,9 @@ export interface MediaComponentVideoProps extends AnalyticsEventsBase {
182
182
  ratio?: number;
183
183
  previewImg?: string;
184
184
  }
185
+ export interface MediaComponentVideoIframeProps {
186
+ videoIframe: string;
187
+ }
185
188
  export interface MediaComponentYoutubeProps {
186
189
  youtube: string;
187
190
  previewImg?: string;
@@ -201,7 +204,7 @@ export interface MediaComponentIframeProps {
201
204
  iframe: IframeProps;
202
205
  margins?: boolean;
203
206
  }
204
- export interface MediaProps extends Animatable, Partial<MediaComponentDataLensProps>, Partial<MediaComponentYoutubeProps>, Partial<MediaComponentImageProps>, Partial<MediaComponentIframeProps>, Partial<MediaComponentVideoProps> {
207
+ export interface MediaProps extends Animatable, Partial<MediaComponentDataLensProps>, Partial<MediaComponentYoutubeProps>, Partial<MediaComponentVideoIframeProps>, Partial<MediaComponentImageProps>, Partial<MediaComponentIframeProps>, Partial<MediaComponentVideoProps> {
205
208
  color?: string;
206
209
  }
207
210
  export interface BackgroundMediaProps extends MediaProps, Animatable, QAProps {
@@ -1554,6 +1554,9 @@ export declare const cardSchemas: {
1554
1554
  youtube: {
1555
1555
  type: string;
1556
1556
  };
1557
+ videoIframe: {
1558
+ type: string;
1559
+ };
1557
1560
  parallax: {
1558
1561
  type: string;
1559
1562
  };
@@ -1134,6 +1134,9 @@ export declare const MediaProps: {
1134
1134
  youtube: {
1135
1135
  type: string;
1136
1136
  };
1137
+ videoIframe: {
1138
+ type: string;
1139
+ };
1137
1140
  parallax: {
1138
1141
  type: string;
1139
1142
  };
@@ -522,6 +522,9 @@ export const MediaProps = {
522
522
  youtube: {
523
523
  type: 'string',
524
524
  },
525
+ videoIframe: {
526
+ type: 'string',
527
+ },
525
528
  parallax: {
526
529
  type: 'boolean',
527
530
  },
@@ -162,6 +162,9 @@ export declare const LayoutItem: {
162
162
  youtube: {
163
163
  type: string;
164
164
  };
165
+ videoIframe: {
166
+ type: string;
167
+ };
165
168
  parallax: {
166
169
  type: string;
167
170
  };
@@ -14,4 +14,4 @@ export declare const getLayoutItemLinks: (links: LayoutItemProps['content']['lin
14
14
  tabIndex?: number | undefined;
15
15
  }[] | undefined;
16
16
  export declare const hasFullscreen: ({ dataLens, image }: MediaProps) => boolean;
17
- export declare const showFullscreenIcon: ({ youtube }: MediaProps) => boolean;
17
+ export declare const showFullscreenIcon: ({ youtube, videoIframe }: MediaProps) => boolean;