@gravity-ui/page-constructor 1.11.0 → 1.12.0-alpha.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 (40) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/build/cjs/blocks/Tabs/Tabs.js +2 -2
  3. package/build/cjs/components/Media/Video/Video.d.ts +1 -1
  4. package/build/cjs/components/Media/Video/Video.js +8 -7
  5. package/build/cjs/components/ReactPlayer/CustomBarControls.css +21 -0
  6. package/build/cjs/components/ReactPlayer/CustomBarControls.d.ts +5 -1
  7. package/build/cjs/components/ReactPlayer/CustomBarControls.js +22 -7
  8. package/build/cjs/components/ReactPlayer/ReactPlayer.css +12 -0
  9. package/build/cjs/components/ReactPlayer/ReactPlayer.js +39 -36
  10. package/build/cjs/components/ReactPlayer/i18n/en.json +4 -0
  11. package/build/cjs/components/ReactPlayer/i18n/index.d.ts +2 -0
  12. package/build/cjs/components/ReactPlayer/i18n/index.js +8 -0
  13. package/build/cjs/components/ReactPlayer/i18n/ru.json +4 -0
  14. package/build/cjs/icons/VideoControlPause.d.ts +2 -0
  15. package/build/cjs/icons/VideoControlPause.js +16 -0
  16. package/build/cjs/icons/VideoControlPlay.d.ts +2 -0
  17. package/build/cjs/icons/VideoControlPlay.js +12 -0
  18. package/build/cjs/models/constructor-items/common.d.ts +9 -29
  19. package/build/cjs/models/constructor-items/common.js +6 -1
  20. package/build/esm/blocks/Tabs/Tabs.js +2 -2
  21. package/build/esm/components/Media/Video/Video.d.ts +1 -1
  22. package/build/esm/components/Media/Video/Video.js +6 -5
  23. package/build/esm/components/ReactPlayer/CustomBarControls.css +21 -0
  24. package/build/esm/components/ReactPlayer/CustomBarControls.d.ts +5 -1
  25. package/build/esm/components/ReactPlayer/CustomBarControls.js +23 -8
  26. package/build/esm/components/ReactPlayer/ReactPlayer.css +12 -0
  27. package/build/esm/components/ReactPlayer/ReactPlayer.js +40 -37
  28. package/build/esm/components/ReactPlayer/i18n/en.json +4 -0
  29. package/build/esm/components/ReactPlayer/i18n/index.d.ts +2 -0
  30. package/build/esm/components/ReactPlayer/i18n/index.js +5 -0
  31. package/build/esm/components/ReactPlayer/i18n/ru.json +4 -0
  32. package/build/esm/icons/VideoControlPause.d.ts +2 -0
  33. package/build/esm/icons/VideoControlPause.js +11 -0
  34. package/build/esm/icons/VideoControlPlay.d.ts +2 -0
  35. package/build/esm/icons/VideoControlPlay.js +7 -0
  36. package/build/esm/models/constructor-items/common.d.ts +9 -29
  37. package/build/esm/models/constructor-items/common.js +5 -0
  38. package/package.json +1 -1
  39. package/server/models/constructor-items/common.d.ts +9 -29
  40. package/server/models/constructor-items/common.js +6 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.11.1](https://github.com/gravity-ui/page-constructor/compare/v1.11.0...v1.11.1) (2023-01-10)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **Tabs:** add caption to media ([#106](https://github.com/gravity-ui/page-constructor/issues/106)) ([e825a4c](https://github.com/gravity-ui/page-constructor/commit/e825a4cff8fe9157f5444b03d32d4aff6e7dfba1))
9
+
3
10
  ## [1.11.0](https://github.com/gravity-ui/page-constructor/compare/v1.10.8...v1.11.0) (2023-01-09)
4
11
 
5
12
  ### Features
@@ -47,8 +47,8 @@ const TabsBlock = ({ items, title, description, animated, tabsColSizes, centered
47
47
  }, className: b('col', { centered: centered }) },
48
48
  react_1.default.createElement("div", { ref: ref }, (activeTabData === null || activeTabData === void 0 ? void 0 : activeTabData.media) && (react_1.default.createElement(Media_1.default, Object.assign({}, (0, utils_1.getThemedValue)(activeTabData.media, theme), { key: activeTab, className: b('media'), playVideo: play, height: mediaHeight })))),
49
49
  imageProps && (react_1.default.createElement(react_1.Fragment, null,
50
- react_1.default.createElement(FullscreenImage_1.default, Object.assign({}, imageProps, { imageClassName: b('image') })),
51
- (activeTabData === null || activeTabData === void 0 ? void 0 : activeTabData.caption) && (react_1.default.createElement("p", { className: b('caption') }, activeTabData.caption))))));
50
+ react_1.default.createElement(FullscreenImage_1.default, Object.assign({}, imageProps, { imageClassName: b('image') })))),
51
+ (activeTabData === null || activeTabData === void 0 ? void 0 : activeTabData.caption) && react_1.default.createElement("p", { className: b('caption') }, activeTabData.caption)));
52
52
  return (react_1.default.createElement(AnimateBlock_1.default, { className: b(), onScroll: () => setPlay(true), animate: animated },
53
53
  react_1.default.createElement(BlockHeader_1.default, { title: title, description: description, className: b('block-title', { centered: centered }) }),
54
54
  react_1.default.createElement(grid_1.Row, null,
@@ -10,6 +10,6 @@ interface InnerVideoProps {
10
10
  setHasVideoFallback: React.Dispatch<boolean>;
11
11
  hasVideoFallback: boolean;
12
12
  }
13
- type VideoAllProps = VideoAdditionProps & MediaComponentVideoProps & InnerVideoProps;
13
+ export type VideoAllProps = VideoAdditionProps & MediaComponentVideoProps & InnerVideoProps;
14
14
  declare const Video: (props: VideoAllProps) => JSX.Element | null;
15
15
  export default Video;
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const react_1 = tslib_1.__importStar(require("react"));
5
5
  const models_1 = require("../../../models");
6
+ const utils_1 = require("../../../utils");
6
7
  const ReactPlayer_1 = tslib_1.__importDefault(require("../../ReactPlayer/ReactPlayer"));
7
- const utils_1 = require("./utils");
8
- const utils_2 = require("../../../utils");
9
- const b = (0, utils_2.block)('media-component-video');
8
+ const utils_2 = require("./utils");
9
+ const b = (0, utils_1.block)('media-component-video');
10
10
  const Video = (props) => {
11
11
  const { video, height, metrika, previewImg, playButton: commonPlayButton, customBarControlsClassName, videoClassName, playVideo, setHasVideoFallback, hasVideoFallback, } = props;
12
12
  const ref = (0, react_1.useRef)(null);
@@ -22,7 +22,7 @@ const Video = (props) => {
22
22
  videoRef.currentTime = start;
23
23
  videoRef.play().catch(() => setHasVideoFallback(true));
24
24
  }
25
- });
25
+ }, { passive: true });
26
26
  }
27
27
  if (playVideo) {
28
28
  ref.current.play().catch(() => setHasVideoFallback(true));
@@ -30,8 +30,8 @@ const Video = (props) => {
30
30
  }
31
31
  }, [playVideo, video, setHasVideoFallback]);
32
32
  const reactPlayerBlock = (0, react_1.useMemo)(() => {
33
- const { src, loop, controls, muted, autoplay = true, elapsedTime, playButton } = video;
34
- return (react_1.default.createElement(ReactPlayer_1.default, { className: b('react-player', videoClassName), src: src, previewImgUrl: previewImg, loop: Boolean(loop), controls: controls, muted: muted, autoplay: autoplay && playVideo, elapsedTime: elapsedTime, playButton: playButton || commonPlayButton, customBarControlsClassName: customBarControlsClassName, metrika: metrika, height: height }));
33
+ const { src, loop, controls, muted, autoplay = true, elapsedTime, playButton, ariaLabel, customControlsOptions, } = video;
34
+ return (react_1.default.createElement(ReactPlayer_1.default, { className: b('react-player', videoClassName), src: src, previewImgUrl: previewImg, loop: Boolean(loop), controls: controls, muted: muted, autoplay: autoplay && playVideo, elapsedTime: elapsedTime, playButton: playButton || commonPlayButton, customBarControlsClassName: customBarControlsClassName, metrika: metrika, height: height, ariaLabel: ariaLabel, customControlsOptions: customControlsOptions }));
35
35
  }, [
36
36
  video,
37
37
  height,
@@ -46,7 +46,8 @@ const Video = (props) => {
46
46
  return video.src.length && !hasVideoFallback ? (react_1.default.createElement("div", { className: b('wrap', videoClassName), style: { height } },
47
47
  react_1.default.createElement("video", { disablePictureInPicture: true, playsInline: true,
48
48
  // @ts-ignore
49
- pip: "false", className: b('item'), ref: ref, preload: "metadata", muted: true }, (0, utils_1.getVideoTypesWithPriority)(video.src).map(({ src, type }, index) => (react_1.default.createElement("source", { key: index, src: src, type: type })))))) : null;
49
+ // eslint-disable-next-line react/no-unknown-property
50
+ pip: "false", className: b('item'), ref: ref, preload: "metadata", muted: true, "aria-label": video.ariaLabel }, (0, utils_2.getVideoTypesWithPriority)(video.src).map(({ src, type }, index) => (react_1.default.createElement("source", { key: index, src: src, type: type })))))) : null;
50
51
  }, [video, videoClassName, hasVideoFallback, height]);
51
52
  switch (video.type) {
52
53
  case models_1.MediaVideoType.Player:
@@ -4,6 +4,10 @@ unpredictable css rules order in build */
4
4
  position: absolute;
5
5
  bottom: 0;
6
6
  }
7
+ .pc-CustomBarControls__wrapper_type_with-play-pause-button {
8
+ width: 100%;
9
+ padding: 20px;
10
+ }
7
11
  .pc-CustomBarControls__button {
8
12
  margin: 12px;
9
13
  border-radius: 50%;
@@ -18,6 +22,23 @@ unpredictable css rules order in build */
18
22
  .pc-CustomBarControls__button:hover {
19
23
  background: #eff2f8;
20
24
  }
25
+ .pc-CustomBarControls__play-button {
26
+ opacity: 0.9;
27
+ background-color: transparent;
28
+ border: 0;
29
+ cursor: pointer;
30
+ transition: opacity 300ms ease 3s;
31
+ }
32
+ .pc-CustomBarControls__play-button:hover, .pc-CustomBarControls__play-button:focus {
33
+ opacity: 1;
34
+ }
35
+ .pc-CustomBarControls__play-button:focus {
36
+ outline: 1px solid var(--g-color-line-light);
37
+ border-radius: 4px;
38
+ }
39
+ .pc-CustomBarControls__play-button:focus:not(:focus-visible) {
40
+ outline: none;
41
+ }
21
42
  .pc-CustomBarControls__icon {
22
43
  margin: auto;
23
44
  }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ClassNameProps } from '../../models';
2
+ import { ClassNameProps, CustomControlsType } from '../../models';
3
3
  interface MuteConfigProps {
4
4
  isMuted: boolean;
5
5
  changeMute: (event: React.MouseEvent) => void;
@@ -7,6 +7,10 @@ interface MuteConfigProps {
7
7
  export interface CustomBarControlsProps extends ClassNameProps {
8
8
  mute?: MuteConfigProps;
9
9
  elapsedTimePercent?: number;
10
+ type?: CustomControlsType;
11
+ isPaused?: boolean;
12
+ onPlayClick?: () => void;
13
+ isStarted?: boolean;
10
14
  }
11
15
  declare const CustomBarControls: (props: CustomBarControlsProps) => JSX.Element;
12
16
  export default CustomBarControls;
@@ -2,20 +2,35 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const react_1 = tslib_1.__importStar(require("react"));
5
+ const uikit_1 = require("@gravity-ui/uikit");
6
+ const VideoControlPause_1 = require("../../icons/VideoControlPause");
7
+ const VideoControlPlay_1 = require("../../icons/VideoControlPlay");
8
+ const models_1 = require("../../models");
5
9
  const utils_1 = require("../../utils");
6
10
  const CircleProgress_1 = tslib_1.__importDefault(require("./CircleProgress"));
11
+ const i18n_1 = tslib_1.__importDefault(require("./i18n"));
7
12
  const b = (0, utils_1.block)('CustomBarControls');
13
+ const PLAY_PAUSE_ICON_SIZE = 24;
8
14
  const CustomBarControls = (props) => {
9
- const { mute, elapsedTimePercent = 0, className } = props;
10
- const renderMute = (0, react_1.useCallback)((elapsedTime, muteConfig) => {
11
- if (!muteConfig) {
15
+ const { mute, elapsedTimePercent = 0, className, type = models_1.CustomControlsType.WithMuteButton, isPaused, onPlayClick, isStarted = false, } = props;
16
+ const muteButton = (0, react_1.useMemo)(() => {
17
+ if (!mute || type === models_1.CustomControlsType.WithPlayPauseButton) {
18
+ // mute button is not provided for with-play-pause-button
12
19
  return null;
13
20
  }
14
- const { isMuted, changeMute } = muteConfig;
21
+ const { isMuted, changeMute } = mute;
15
22
  return (react_1.default.createElement("div", { className: b('button'), onClick: changeMute },
16
23
  react_1.default.createElement("div", { className: b('mute-button', { muted: isMuted }) }),
17
- !isMuted && react_1.default.createElement(CircleProgress_1.default, { elapsedTime: elapsedTime, strokeWidth: 5 })));
18
- }, []);
19
- return react_1.default.createElement("div", { className: b('wrapper', className) }, renderMute(elapsedTimePercent, mute));
24
+ !isMuted && react_1.default.createElement(CircleProgress_1.default, { elapsedTime: elapsedTimePercent, strokeWidth: 5 })));
25
+ }, [elapsedTimePercent, mute, type]);
26
+ const playPauseButton = (0, react_1.useMemo)(() => {
27
+ if (type !== models_1.CustomControlsType.WithPlayPauseButton || !isStarted) {
28
+ return null;
29
+ }
30
+ return (react_1.default.createElement("button", { onClick: onPlayClick, className: b('play-button'), "aria-label": (0, i18n_1.default)(isPaused ? 'play' : 'pause') }, isPaused ? (react_1.default.createElement(uikit_1.Icon, { data: VideoControlPlay_1.VideoControlPlay, size: PLAY_PAUSE_ICON_SIZE })) : (react_1.default.createElement(uikit_1.Icon, { data: VideoControlPause_1.VideoControlPause, size: PLAY_PAUSE_ICON_SIZE }))));
31
+ }, [isPaused, isStarted, onPlayClick, type]);
32
+ return (react_1.default.createElement("div", { className: b('wrapper', { type }, className) },
33
+ muteButton,
34
+ playPauseButton));
20
35
  };
21
36
  exports.default = CustomBarControls;
@@ -50,6 +50,18 @@ unpredictable css rules order in build */
50
50
  opacity: 1;
51
51
  transition: opacity 300ms ease 0s;
52
52
  }
53
+ .pc-ReactPlayer_started.pc-ReactPlayer_controls_custom.pc-ReactPlayer_hovered::before {
54
+ opacity: 1;
55
+ }
56
+ .pc-ReactPlayer_started.pc-ReactPlayer_controls_custom::before {
57
+ position: absolute;
58
+ width: 100%;
59
+ height: 100%;
60
+ content: "";
61
+ background: linear-gradient(180deg, rgba(0, 0, 0, 0) 65.36%, rgba(0, 0, 0, 0.35) 100%);
62
+ opacity: 0;
63
+ transition: opacity 300ms;
64
+ }
53
65
  .pc-ReactPlayer__custom-bar-controls {
54
66
  opacity: 0;
55
67
  transition: opacity 300ms ease 3s;
@@ -19,8 +19,9 @@ const FPS = 60;
19
19
  exports.ReactPlayerBlock = react_1.default.forwardRef((props, originRef) => {
20
20
  const isMobile = (0, react_1.useContext)(mobileContext_1.MobileContext);
21
21
  const { metrika } = (0, react_1.useContext)(metrikaContext_1.MetrikaContext);
22
- const { src, previewImgUrl, loop = false, controls = models_1.MediaVideoControlsType.Default, muted: initiallyMuted = false, elapsedTime, playButton, className, customBarControlsClassName, showPreview, onClickPreview, metrika: videoMetrika, height, } = props;
22
+ const { src, previewImgUrl, loop = false, controls = models_1.MediaVideoControlsType.Default, customControlsOptions = {}, muted: initiallyMuted = false, elapsedTime, playButton, className, customBarControlsClassName, showPreview, onClickPreview, metrika: videoMetrika, height, } = props;
23
23
  const { type = models_1.PlayButtonType.Default, theme = models_1.PlayButtonThemes.Blue, text, className: buttonClassName, } = playButton || {};
24
+ const { type: customControlsType = models_1.CustomControlsType.WithMuteButton } = customControlsOptions;
24
25
  const autoPlay = Boolean(!isMobile && !previewImgUrl && props.autoplay);
25
26
  const mute = initiallyMuted || autoPlay;
26
27
  const { playingVideoRef, setProps } = (0, react_1.useContext)(videoContext_1.VideoContext);
@@ -32,8 +33,8 @@ exports.ReactPlayerBlock = react_1.default.forwardRef((props, originRef) => {
32
33
  const [width, setWidth] = (0, react_1.useState)(0);
33
34
  const [muted, setMuted] = (0, react_1.useState)(mute);
34
35
  const [started, setStarted] = (0, react_1.useState)(autoPlay);
35
- const [paused, setPaused] = (0, react_1.useState)(false);
36
36
  const [ended, setEnded] = (0, react_1.useState)(false);
37
+ const [hovered, setHovered] = (0, react_1.useState)(false);
37
38
  (0, react_1.useImperativeHandle)(originRef, () => ({
38
39
  pause: () => setIsPlaying(false),
39
40
  }));
@@ -41,14 +42,14 @@ exports.ReactPlayerBlock = react_1.default.forwardRef((props, originRef) => {
41
42
  if (ref.current && !(playingVideoRef === null || playingVideoRef === void 0 ? void 0 : playingVideoRef.contains(ref.current))) {
42
43
  setMuted(true);
43
44
  }
44
- }, [playingVideoRef]);
45
+ }, [playingVideoRef, ref]);
45
46
  (0, react_1.useEffect)(() => {
46
47
  if (showPreview) {
47
48
  playerRef === null || playerRef === void 0 ? void 0 : playerRef.showPreview();
48
49
  }
49
50
  }, [showPreview, playerRef]);
50
51
  (0, react_1.useEffect)(() => {
51
- if (playerRef) {
52
+ if (playerRef && autoPlay) {
52
53
  setIsPlaying(autoPlay);
53
54
  }
54
55
  }, [autoPlay, playerRef]);
@@ -96,10 +97,6 @@ exports.ReactPlayerBlock = react_1.default.forwardRef((props, originRef) => {
96
97
  return (react_1.default.createElement("button", { className: b('button', { theme, text: Boolean(text) }, buttonClassName) }, playButtonContent));
97
98
  }, [type, theme, text, buttonClassName]);
98
99
  const changeMute = (0, react_1.useCallback)((isMuted) => {
99
- if (isMuted && playerRef) {
100
- playerRef.seekTo(0);
101
- setPlayedPercent(0);
102
- }
103
100
  if (metrika && videoMetrika) {
104
101
  const { play, stop, counterName } = videoMetrika;
105
102
  const goal = isMuted ? play : stop;
@@ -112,8 +109,7 @@ exports.ReactPlayerBlock = react_1.default.forwardRef((props, originRef) => {
112
109
  }
113
110
  // In order to the progress bar to update (equals 0) before displaying
114
111
  setTimeout(() => setMuted(!isMuted), 0);
115
- }, [playerRef, setProps, videoMetrika, metrika]);
116
- const handleClick = (0, react_1.useCallback)(() => changeMute(muted), [changeMute, muted]);
112
+ }, [metrika, videoMetrika, setProps]);
117
113
  const handleClickPreview = (0, react_1.useCallback)(() => {
118
114
  setIsPlaying(true);
119
115
  onClickPreview === null || onClickPreview === void 0 ? void 0 : onClickPreview();
@@ -125,13 +121,8 @@ exports.ReactPlayerBlock = react_1.default.forwardRef((props, originRef) => {
125
121
  }
126
122
  }, [onClickPreview, setIsPlaying, videoMetrika, metrika]);
127
123
  const onPause = (0, react_1.useCallback)(() => {
128
- // For support correct state for youtube
129
124
  setIsPlaying(false);
130
- if (controls === models_1.MediaVideoControlsType.Custom) {
131
- setPaused(true);
132
- setIsPlaying(true);
133
- }
134
- }, [controls, setIsPlaying, setPaused]);
125
+ }, []);
135
126
  const onStart = (0, react_1.useCallback)(() => {
136
127
  if (!autoPlay && !initiallyMuted) {
137
128
  setMuted(false);
@@ -140,21 +131,17 @@ exports.ReactPlayerBlock = react_1.default.forwardRef((props, originRef) => {
140
131
  const onPlay = (0, react_1.useCallback)(() => {
141
132
  setIsPlaying(true);
142
133
  if (controls === models_1.MediaVideoControlsType.Custom) {
143
- if (ended) {
144
- changeMute(false);
145
- }
146
- else if (paused) {
147
- changeMute(muted);
148
- }
149
- setEnded(false);
150
- setPaused(false);
134
+ changeMute(true);
151
135
  }
152
- }, [changeMute, controls, ended, muted, paused]);
136
+ }, [changeMute, controls]);
153
137
  const onProgress = (0, react_1.useCallback)((progress) => {
154
138
  setPlayedPercent(progress.played);
155
139
  if (progress.played === 1) {
156
140
  setMuted(true);
157
141
  }
142
+ else {
143
+ setEnded(false);
144
+ }
158
145
  }, []);
159
146
  const onEnded = (0, react_1.useCallback)(() => {
160
147
  // Youtube videos not muted after finishing playing and start again.
@@ -167,21 +154,37 @@ exports.ReactPlayerBlock = react_1.default.forwardRef((props, originRef) => {
167
154
  }
168
155
  setEnded(true);
169
156
  }, [loop, playerRef]);
170
- const renderCustomBarControls = (0, react_1.useCallback)((isMuted, elapsedTimePercent) => {
171
- if (controls !== models_1.MediaVideoControlsType.Custom || !isPlaying) {
172
- return null;
157
+ const onPlayClick = (0, react_1.useCallback)(() => {
158
+ if (isPlaying) {
159
+ onPause();
160
+ }
161
+ else {
162
+ onPlay();
163
+ }
164
+ }, [isPlaying, onPause, onPlay]);
165
+ const handleClick = (0, react_1.useCallback)(() => {
166
+ if (customControlsType === models_1.CustomControlsType.WithMuteButton) {
167
+ changeMute(muted);
168
+ }
169
+ else {
170
+ onPlayClick();
173
171
  }
174
- return (react_1.default.createElement(CustomBarControls_1.default, { className: b('custom-bar-controls', { muted: isMuted }, customBarControlsClassName), mute: {
175
- isMuted,
172
+ if (ended) {
173
+ playerRef === null || playerRef === void 0 ? void 0 : playerRef.seekTo(0);
174
+ onPlay();
175
+ }
176
+ }, [changeMute, customControlsType, ended, muted, onPlay, onPlayClick, playerRef]);
177
+ const onFocusIn = (0, react_1.useCallback)(() => setHovered(true), []);
178
+ const onFocusOut = (0, react_1.useCallback)(() => setHovered(false), []);
179
+ return (react_1.default.createElement("div", { className: b({ wrapper: !currentHeight, controls, started, hovered }, className), ref: ref, onClick: handleClick, onMouseEnter: onFocusIn, onMouseLeave: onFocusOut, onFocus: onFocusIn, onBlur: onFocusOut },
180
+ react_1.default.createElement(react_player_1.default, { className: b('player'), url: src, muted: muted, controls: controls === models_1.MediaVideoControlsType.Default, height: currentHeight || '100%', width: width || '100%', light: previewImgUrl, playing: isPlaying, playIcon: playIcon, progressInterval: FPS, onClickPreview: handleClickPreview, onStart: onStart, onReady: setPlayerRef, onPlay: onPlay, onPause: onPause, onProgress: onProgress, onEnded: onEnded }),
181
+ controls === models_1.MediaVideoControlsType.Custom && (react_1.default.createElement(CustomBarControls_1.default, { className: b('custom-bar-controls', { muted }, customBarControlsClassName), mute: {
182
+ isMuted: muted,
176
183
  changeMute: (event) => {
177
184
  event.stopPropagation();
178
- changeMute(isMuted);
185
+ changeMute(muted);
179
186
  },
180
- }, elapsedTimePercent: elapsedTimePercent }));
181
- }, [controls, isPlaying, customBarControlsClassName, changeMute]);
182
- return (react_1.default.createElement("div", { className: b({ wrapper: !currentHeight }, className), ref: ref, onClick: handleClick },
183
- react_1.default.createElement(react_player_1.default, { className: b('player'), url: src, muted: muted, controls: controls === models_1.MediaVideoControlsType.Default, height: currentHeight || '100%', width: width || '100%', light: previewImgUrl, playing: isPlaying, playIcon: playIcon, progressInterval: FPS, onClickPreview: handleClickPreview, onStart: onStart, onReady: setPlayerRef, onPlay: onPlay, onPause: onPause, onProgress: onProgress, onEnded: onEnded }),
184
- renderCustomBarControls(muted, playedPercent)));
187
+ }, elapsedTimePercent: playedPercent, type: customControlsType, isPaused: !isPlaying, onPlayClick: onPlayClick, isStarted: started }))));
185
188
  });
186
189
  function getHeight(width) {
187
190
  return (width / 16) * 9;
@@ -0,0 +1,4 @@
1
+ {
2
+ "play": "Play",
3
+ "pause": "Pause"
4
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: (key: string, params?: import("@gravity-ui/i18n").Params | undefined) => string;
2
+ export default _default;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const registerKeyset_1 = require("../../../utils/registerKeyset");
5
+ const en_json_1 = tslib_1.__importDefault(require("./en.json"));
6
+ const ru_json_1 = tslib_1.__importDefault(require("./ru.json"));
7
+ const COMPONENT = 'ReactPlayer';
8
+ exports.default = (0, registerKeyset_1.registerKeyset)({ en: en_json_1.default, ru: ru_json_1.default }, COMPONENT);
@@ -0,0 +1,4 @@
1
+ {
2
+ "play": "Воспроизвести",
3
+ "pause": "Остановить"
4
+ }
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const VideoControlPause: React.FC<React.SVGProps<SVGSVGElement>>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VideoControlPause = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const svg_1 = require("../utils/svg");
7
+ const VideoControlPause = (props) => {
8
+ return (react_1.default.createElement("svg", Object.assign({ width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, svg_1.a11yHiddenSvgProps, props),
9
+ react_1.default.createElement("g", { opacity: "0.9" },
10
+ react_1.default.createElement("mask", { id: "path-1-outside-1_1237_523", maskUnits: "userSpaceOnUse", x: "1", y: "0", width: "22", height: "24", fill: "black" },
11
+ react_1.default.createElement("rect", { fill: "white", x: "1", width: "22", height: "24" }),
12
+ react_1.default.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4 1C2.89543 1 2 1.89543 2 3V21C2 22.1046 2.89543 23 4 23H8C9.10457 23 10 22.1046 10 21V3C10 1.89543 9.10457 1 8 1H4ZM16 1C14.8954 1 14 1.89543 14 3V21C14 22.1046 14.8954 23 16 23H20C21.1046 23 22 22.1046 22 21V3C22 1.89543 21.1046 1 20 1H16Z" })),
13
+ react_1.default.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4 1C2.89543 1 2 1.89543 2 3V21C2 22.1046 2.89543 23 4 23H8C9.10457 23 10 22.1046 10 21V3C10 1.89543 9.10457 1 8 1H4ZM16 1C14.8954 1 14 1.89543 14 3V21C14 22.1046 14.8954 23 16 23H20C21.1046 23 22 22.1046 22 21V3C22 1.89543 21.1046 1 20 1H16Z", fill: "white" }),
14
+ react_1.default.createElement("path", { d: "M3 3C3 2.44772 3.44771 2 4 2V0C2.34315 0 1 1.34314 1 3H3ZM3 21V3H1V21H3ZM4 22C3.44772 22 3 21.5523 3 21H1C1 22.6569 2.34315 24 4 24V22ZM8 22H4V24H8V22ZM9 21C9 21.5523 8.55229 22 8 22V24C9.65685 24 11 22.6569 11 21H9ZM9 3V21H11V3H9ZM8 2C8.55228 2 9 2.44772 9 3H11C11 1.34315 9.65685 0 8 0V2ZM4 2H8V0H4V2ZM15 3C15 2.44772 15.4477 2 16 2V0C14.3431 0 13 1.34314 13 3H15ZM15 21V3H13V21H15ZM16 22C15.4477 22 15 21.5523 15 21H13C13 22.6569 14.3431 24 16 24V22ZM20 22H16V24H20V22ZM21 21C21 21.5523 20.5523 22 20 22V24C21.6569 24 23 22.6569 23 21H21ZM21 3V21H23V3H21ZM20 2C20.5523 2 21 2.44772 21 3H23C23 1.34315 21.6569 0 20 0V2ZM16 2H20V0H16V2Z", fill: "black", fillOpacity: "0.15", mask: "url(#path-1-outside-1_1237_523)" }))));
15
+ };
16
+ exports.VideoControlPause = VideoControlPause;
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const VideoControlPlay: React.FC<React.SVGProps<SVGSVGElement>>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VideoControlPlay = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const svg_1 = require("../utils/svg");
7
+ const VideoControlPlay = (props) => {
8
+ return (react_1.default.createElement("svg", Object.assign({ width: "24", height: "26", viewBox: "0 0 24 26", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, svg_1.a11yHiddenSvgProps, props),
9
+ react_1.default.createElement("path", { d: "M20.5028 15.6408C22.4991 14.4663 22.4991 11.5337 20.5028 10.3592L6.99778 2.41411C4.99944 1.23846 2.5 2.70595 2.5 5.05488L2.5 20.9451C2.5 23.2941 4.99943 24.7615 6.99777 23.5859L20.5028 15.6408Z", fill: "white" }),
10
+ react_1.default.createElement("path", { d: "M20.7563 16.0717C23.0812 14.7039 23.0812 11.2961 20.7563 9.92827L7.25131 1.98315C4.90651 0.603685 2 2.33458 2 5.05488L2 20.9451C2 23.6654 4.90651 25.3963 7.25131 24.0168L20.7563 16.0717Z", stroke: "black", strokeOpacity: "0.15" })));
11
+ };
12
+ exports.VideoControlPlay = VideoControlPlay;
@@ -29,6 +29,10 @@ export declare enum PlayButtonThemes {
29
29
  Blue = "blue",
30
30
  Grey = "grey"
31
31
  }
32
+ export declare enum CustomControlsType {
33
+ WithMuteButton = "with-mute-button",
34
+ WithPlayPauseButton = "with-play-pause-button"
35
+ }
32
36
  export declare enum MediaVideoType {
33
37
  Default = "default",
34
38
  Player = "player"
@@ -109,7 +113,9 @@ export interface MediaVideoProps {
109
113
  elapsedTime?: number;
110
114
  playButton?: PlayButtonProps;
111
115
  controls?: MediaVideoControlsType;
116
+ customControlsOptions?: CustomControlsOptions;
112
117
  metrika?: MetrikaVideo;
118
+ ariaLabel?: string;
113
119
  }
114
120
  export interface LinkProps extends Stylable {
115
121
  url: string;
@@ -146,22 +152,14 @@ export interface ButtonImageProps {
146
152
  position?: 'left' | 'right';
147
153
  alt?: string;
148
154
  }
155
+ export interface CustomControlsOptions {
156
+ type?: CustomControlsType;
157
+ }
149
158
  export interface PlayButtonProps extends ClassNameProps {
150
159
  type?: PlayButtonType;
151
160
  theme?: PlayButtonThemes;
152
161
  text?: string;
153
162
  }
154
- export interface MediaVideoProps {
155
- src: string[];
156
- type?: MediaVideoType;
157
- loop?: LoopProps | boolean;
158
- muted?: boolean;
159
- autoplay?: boolean;
160
- elapsedTime?: number;
161
- playButton?: PlayButtonProps;
162
- controls?: MediaVideoControlsType;
163
- metrika?: MetrikaVideo;
164
- }
165
163
  export type ThemedMediaVideoProps = ThemeSupporting<MediaVideoProps>;
166
164
  export interface MediaComponentVideoProps {
167
165
  video: MediaVideoProps;
@@ -309,24 +307,6 @@ export interface PriceDetailedProps extends CardBaseProps {
309
307
  foldable?: PriceFoldableDetailsProps;
310
308
  labelsDefaultText?: Record<PriceLabelColor, string>;
311
309
  }
312
- export interface PriceDetailedProps extends CardBaseProps {
313
- items: PriceItemProps[];
314
- description?: {
315
- titleSize?: TextSize;
316
- descriptionSize?: TextSize;
317
- titleColor?: PriceDescriptionColor;
318
- };
319
- details?: {
320
- titleSize?: TextSize;
321
- descriptionSize?: TextSize;
322
- };
323
- priceType?: PriceDetailsType;
324
- numberGroupItems?: 3 | 4 | 5;
325
- isCombined?: boolean;
326
- useMixedView?: boolean;
327
- foldable?: PriceFoldableDetailsProps;
328
- labelsDefaultText?: Record<PriceLabelColor, string>;
329
- }
330
310
  export interface AuthorProps {
331
311
  author: AuthorItem;
332
312
  className?: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MediaVideoControlsType = exports.MediaVideoType = exports.PlayButtonThemes = exports.PlayButtonType = exports.PreviewItemType = exports.PriceLabelColor = exports.PriceDetailsType = exports.AuthorType = void 0;
3
+ exports.MediaVideoControlsType = exports.MediaVideoType = exports.CustomControlsType = exports.PlayButtonThemes = exports.PlayButtonType = exports.PreviewItemType = exports.PriceLabelColor = exports.PriceDetailsType = exports.AuthorType = void 0;
4
4
  // enums
5
5
  var AuthorType;
6
6
  (function (AuthorType) {
@@ -35,6 +35,11 @@ var PlayButtonThemes;
35
35
  PlayButtonThemes["Blue"] = "blue";
36
36
  PlayButtonThemes["Grey"] = "grey";
37
37
  })(PlayButtonThemes = exports.PlayButtonThemes || (exports.PlayButtonThemes = {}));
38
+ var CustomControlsType;
39
+ (function (CustomControlsType) {
40
+ CustomControlsType["WithMuteButton"] = "with-mute-button";
41
+ CustomControlsType["WithPlayPauseButton"] = "with-play-pause-button";
42
+ })(CustomControlsType = exports.CustomControlsType || (exports.CustomControlsType = {}));
38
43
  var MediaVideoType;
39
44
  (function (MediaVideoType) {
40
45
  MediaVideoType["Default"] = "default";
@@ -44,8 +44,8 @@ export const TabsBlock = ({ items, title, description, animated, tabsColSizes, c
44
44
  }, className: b('col', { centered: centered }) },
45
45
  React.createElement("div", { ref: ref }, (activeTabData === null || activeTabData === void 0 ? void 0 : activeTabData.media) && (React.createElement(Media, Object.assign({}, getThemedValue(activeTabData.media, theme), { key: activeTab, className: b('media'), playVideo: play, height: mediaHeight })))),
46
46
  imageProps && (React.createElement(Fragment, null,
47
- React.createElement(FullScreenImage, Object.assign({}, imageProps, { imageClassName: b('image') })),
48
- (activeTabData === null || activeTabData === void 0 ? void 0 : activeTabData.caption) && (React.createElement("p", { className: b('caption') }, activeTabData.caption))))));
47
+ React.createElement(FullScreenImage, Object.assign({}, imageProps, { imageClassName: b('image') })))),
48
+ (activeTabData === null || activeTabData === void 0 ? void 0 : activeTabData.caption) && React.createElement("p", { className: b('caption') }, activeTabData.caption)));
49
49
  return (React.createElement(AnimateBlock, { className: b(), onScroll: () => setPlay(true), animate: animated },
50
50
  React.createElement(BlockHeader, { title: title, description: description, className: b('block-title', { centered: centered }) }),
51
51
  React.createElement(Row, null,
@@ -11,6 +11,6 @@ interface InnerVideoProps {
11
11
  setHasVideoFallback: React.Dispatch<boolean>;
12
12
  hasVideoFallback: boolean;
13
13
  }
14
- type VideoAllProps = VideoAdditionProps & MediaComponentVideoProps & InnerVideoProps;
14
+ export type VideoAllProps = VideoAdditionProps & MediaComponentVideoProps & InnerVideoProps;
15
15
  declare const Video: (props: VideoAllProps) => JSX.Element | null;
16
16
  export default Video;
@@ -1,8 +1,8 @@
1
1
  import React, { useEffect, useMemo, useRef } from 'react';
2
2
  import { MediaVideoType } from '../../../models';
3
+ import { block } from '../../../utils';
3
4
  import ReactPlayerBlock from '../../ReactPlayer/ReactPlayer';
4
5
  import { getVideoTypesWithPriority } from './utils';
5
- import { block } from '../../../utils';
6
6
  import './Video.css';
7
7
  const b = block('media-component-video');
8
8
  const Video = (props) => {
@@ -20,7 +20,7 @@ const Video = (props) => {
20
20
  videoRef.currentTime = start;
21
21
  videoRef.play().catch(() => setHasVideoFallback(true));
22
22
  }
23
- });
23
+ }, { passive: true });
24
24
  }
25
25
  if (playVideo) {
26
26
  ref.current.play().catch(() => setHasVideoFallback(true));
@@ -28,8 +28,8 @@ const Video = (props) => {
28
28
  }
29
29
  }, [playVideo, video, setHasVideoFallback]);
30
30
  const reactPlayerBlock = useMemo(() => {
31
- const { src, loop, controls, muted, autoplay = true, elapsedTime, playButton } = video;
32
- return (React.createElement(ReactPlayerBlock, { className: b('react-player', videoClassName), src: src, previewImgUrl: previewImg, loop: Boolean(loop), controls: controls, muted: muted, autoplay: autoplay && playVideo, elapsedTime: elapsedTime, playButton: playButton || commonPlayButton, customBarControlsClassName: customBarControlsClassName, metrika: metrika, height: height }));
31
+ const { src, loop, controls, muted, autoplay = true, elapsedTime, playButton, ariaLabel, customControlsOptions, } = video;
32
+ return (React.createElement(ReactPlayerBlock, { className: b('react-player', videoClassName), src: src, previewImgUrl: previewImg, loop: Boolean(loop), controls: controls, muted: muted, autoplay: autoplay && playVideo, elapsedTime: elapsedTime, playButton: playButton || commonPlayButton, customBarControlsClassName: customBarControlsClassName, metrika: metrika, height: height, ariaLabel: ariaLabel, customControlsOptions: customControlsOptions }));
33
33
  }, [
34
34
  video,
35
35
  height,
@@ -44,7 +44,8 @@ const Video = (props) => {
44
44
  return video.src.length && !hasVideoFallback ? (React.createElement("div", { className: b('wrap', videoClassName), style: { height } },
45
45
  React.createElement("video", { disablePictureInPicture: true, playsInline: true,
46
46
  // @ts-ignore
47
- pip: "false", className: b('item'), ref: ref, preload: "metadata", muted: true }, getVideoTypesWithPriority(video.src).map(({ src, type }, index) => (React.createElement("source", { key: index, src: src, type: type })))))) : null;
47
+ // eslint-disable-next-line react/no-unknown-property
48
+ pip: "false", className: b('item'), ref: ref, preload: "metadata", muted: true, "aria-label": video.ariaLabel }, getVideoTypesWithPriority(video.src).map(({ src, type }, index) => (React.createElement("source", { key: index, src: src, type: type })))))) : null;
48
49
  }, [video, videoClassName, hasVideoFallback, height]);
49
50
  switch (video.type) {
50
51
  case MediaVideoType.Player:
@@ -4,6 +4,10 @@ unpredictable css rules order in build */
4
4
  position: absolute;
5
5
  bottom: 0;
6
6
  }
7
+ .pc-CustomBarControls__wrapper_type_with-play-pause-button {
8
+ width: 100%;
9
+ padding: 20px;
10
+ }
7
11
  .pc-CustomBarControls__button {
8
12
  margin: 12px;
9
13
  border-radius: 50%;
@@ -18,6 +22,23 @@ unpredictable css rules order in build */
18
22
  .pc-CustomBarControls__button:hover {
19
23
  background: #eff2f8;
20
24
  }
25
+ .pc-CustomBarControls__play-button {
26
+ opacity: 0.9;
27
+ background-color: transparent;
28
+ border: 0;
29
+ cursor: pointer;
30
+ transition: opacity 300ms ease 3s;
31
+ }
32
+ .pc-CustomBarControls__play-button:hover, .pc-CustomBarControls__play-button:focus {
33
+ opacity: 1;
34
+ }
35
+ .pc-CustomBarControls__play-button:focus {
36
+ outline: 1px solid var(--g-color-line-light);
37
+ border-radius: 4px;
38
+ }
39
+ .pc-CustomBarControls__play-button:focus:not(:focus-visible) {
40
+ outline: none;
41
+ }
21
42
  .pc-CustomBarControls__icon {
22
43
  margin: auto;
23
44
  }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ClassNameProps } from '../../models';
2
+ import { ClassNameProps, CustomControlsType } from '../../models';
3
3
  import './CustomBarControls.css';
4
4
  interface MuteConfigProps {
5
5
  isMuted: boolean;
@@ -8,6 +8,10 @@ interface MuteConfigProps {
8
8
  export interface CustomBarControlsProps extends ClassNameProps {
9
9
  mute?: MuteConfigProps;
10
10
  elapsedTimePercent?: number;
11
+ type?: CustomControlsType;
12
+ isPaused?: boolean;
13
+ onPlayClick?: () => void;
14
+ isStarted?: boolean;
11
15
  }
12
16
  declare const CustomBarControls: (props: CustomBarControlsProps) => JSX.Element;
13
17
  export default CustomBarControls;
@@ -1,19 +1,34 @@
1
- import React, { useCallback } from 'react';
1
+ import React, { useMemo } from 'react';
2
+ import { Icon } from '@gravity-ui/uikit';
3
+ import { VideoControlPause } from '../../icons/VideoControlPause';
4
+ import { VideoControlPlay } from '../../icons/VideoControlPlay';
5
+ import { CustomControlsType } from '../../models';
2
6
  import { block } from '../../utils';
3
7
  import CircleProgress from './CircleProgress';
8
+ import i18n from './i18n';
4
9
  import './CustomBarControls.css';
5
10
  const b = block('CustomBarControls');
11
+ const PLAY_PAUSE_ICON_SIZE = 24;
6
12
  const CustomBarControls = (props) => {
7
- const { mute, elapsedTimePercent = 0, className } = props;
8
- const renderMute = useCallback((elapsedTime, muteConfig) => {
9
- if (!muteConfig) {
13
+ const { mute, elapsedTimePercent = 0, className, type = CustomControlsType.WithMuteButton, isPaused, onPlayClick, isStarted = false, } = props;
14
+ const muteButton = useMemo(() => {
15
+ if (!mute || type === CustomControlsType.WithPlayPauseButton) {
16
+ // mute button is not provided for with-play-pause-button
10
17
  return null;
11
18
  }
12
- const { isMuted, changeMute } = muteConfig;
19
+ const { isMuted, changeMute } = mute;
13
20
  return (React.createElement("div", { className: b('button'), onClick: changeMute },
14
21
  React.createElement("div", { className: b('mute-button', { muted: isMuted }) }),
15
- !isMuted && React.createElement(CircleProgress, { elapsedTime: elapsedTime, strokeWidth: 5 })));
16
- }, []);
17
- return React.createElement("div", { className: b('wrapper', className) }, renderMute(elapsedTimePercent, mute));
22
+ !isMuted && React.createElement(CircleProgress, { elapsedTime: elapsedTimePercent, strokeWidth: 5 })));
23
+ }, [elapsedTimePercent, mute, type]);
24
+ const playPauseButton = useMemo(() => {
25
+ if (type !== CustomControlsType.WithPlayPauseButton || !isStarted) {
26
+ return null;
27
+ }
28
+ return (React.createElement("button", { onClick: onPlayClick, className: b('play-button'), "aria-label": i18n(isPaused ? 'play' : 'pause') }, isPaused ? (React.createElement(Icon, { data: VideoControlPlay, size: PLAY_PAUSE_ICON_SIZE })) : (React.createElement(Icon, { data: VideoControlPause, size: PLAY_PAUSE_ICON_SIZE }))));
29
+ }, [isPaused, isStarted, onPlayClick, type]);
30
+ return (React.createElement("div", { className: b('wrapper', { type }, className) },
31
+ muteButton,
32
+ playPauseButton));
18
33
  };
19
34
  export default CustomBarControls;
@@ -50,6 +50,18 @@ unpredictable css rules order in build */
50
50
  opacity: 1;
51
51
  transition: opacity 300ms ease 0s;
52
52
  }
53
+ .pc-ReactPlayer_started.pc-ReactPlayer_controls_custom.pc-ReactPlayer_hovered::before {
54
+ opacity: 1;
55
+ }
56
+ .pc-ReactPlayer_started.pc-ReactPlayer_controls_custom::before {
57
+ position: absolute;
58
+ width: 100%;
59
+ height: 100%;
60
+ content: "";
61
+ background: linear-gradient(180deg, rgba(0, 0, 0, 0) 65.36%, rgba(0, 0, 0, 0.35) 100%);
62
+ opacity: 0;
63
+ transition: opacity 300ms;
64
+ }
53
65
  .pc-ReactPlayer__custom-bar-controls {
54
66
  opacity: 0;
55
67
  transition: opacity 300ms ease 3s;
@@ -3,7 +3,7 @@ import ReactPlayer from 'react-player';
3
3
  import React, { useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
4
4
  import { Icon } from '@gravity-ui/uikit';
5
5
  import { block } from '../../utils';
6
- import { PlayButtonThemes, PlayButtonType, MediaVideoControlsType, } from '../../models';
6
+ import { CustomControlsType, PlayButtonThemes, PlayButtonType, MediaVideoControlsType, } from '../../models';
7
7
  import CustomBarControls from './CustomBarControls';
8
8
  import { VideoContext } from '../../context/videoContext';
9
9
  import { MetrikaContext } from '../../context/metrikaContext';
@@ -16,8 +16,9 @@ const FPS = 60;
16
16
  export const ReactPlayerBlock = React.forwardRef((props, originRef) => {
17
17
  const isMobile = useContext(MobileContext);
18
18
  const { metrika } = useContext(MetrikaContext);
19
- const { src, previewImgUrl, loop = false, controls = MediaVideoControlsType.Default, muted: initiallyMuted = false, elapsedTime, playButton, className, customBarControlsClassName, showPreview, onClickPreview, metrika: videoMetrika, height, } = props;
19
+ const { src, previewImgUrl, loop = false, controls = MediaVideoControlsType.Default, customControlsOptions = {}, muted: initiallyMuted = false, elapsedTime, playButton, className, customBarControlsClassName, showPreview, onClickPreview, metrika: videoMetrika, height, } = props;
20
20
  const { type = PlayButtonType.Default, theme = PlayButtonThemes.Blue, text, className: buttonClassName, } = playButton || {};
21
+ const { type: customControlsType = CustomControlsType.WithMuteButton } = customControlsOptions;
21
22
  const autoPlay = Boolean(!isMobile && !previewImgUrl && props.autoplay);
22
23
  const mute = initiallyMuted || autoPlay;
23
24
  const { playingVideoRef, setProps } = useContext(VideoContext);
@@ -29,8 +30,8 @@ export const ReactPlayerBlock = React.forwardRef((props, originRef) => {
29
30
  const [width, setWidth] = useState(0);
30
31
  const [muted, setMuted] = useState(mute);
31
32
  const [started, setStarted] = useState(autoPlay);
32
- const [paused, setPaused] = useState(false);
33
33
  const [ended, setEnded] = useState(false);
34
+ const [hovered, setHovered] = useState(false);
34
35
  useImperativeHandle(originRef, () => ({
35
36
  pause: () => setIsPlaying(false),
36
37
  }));
@@ -38,14 +39,14 @@ export const ReactPlayerBlock = React.forwardRef((props, originRef) => {
38
39
  if (ref.current && !(playingVideoRef === null || playingVideoRef === void 0 ? void 0 : playingVideoRef.contains(ref.current))) {
39
40
  setMuted(true);
40
41
  }
41
- }, [playingVideoRef]);
42
+ }, [playingVideoRef, ref]);
42
43
  useEffect(() => {
43
44
  if (showPreview) {
44
45
  playerRef === null || playerRef === void 0 ? void 0 : playerRef.showPreview();
45
46
  }
46
47
  }, [showPreview, playerRef]);
47
48
  useEffect(() => {
48
- if (playerRef) {
49
+ if (playerRef && autoPlay) {
49
50
  setIsPlaying(autoPlay);
50
51
  }
51
52
  }, [autoPlay, playerRef]);
@@ -93,10 +94,6 @@ export const ReactPlayerBlock = React.forwardRef((props, originRef) => {
93
94
  return (React.createElement("button", { className: b('button', { theme, text: Boolean(text) }, buttonClassName) }, playButtonContent));
94
95
  }, [type, theme, text, buttonClassName]);
95
96
  const changeMute = useCallback((isMuted) => {
96
- if (isMuted && playerRef) {
97
- playerRef.seekTo(0);
98
- setPlayedPercent(0);
99
- }
100
97
  if (metrika && videoMetrika) {
101
98
  const { play, stop, counterName } = videoMetrika;
102
99
  const goal = isMuted ? play : stop;
@@ -109,8 +106,7 @@ export const ReactPlayerBlock = React.forwardRef((props, originRef) => {
109
106
  }
110
107
  // In order to the progress bar to update (equals 0) before displaying
111
108
  setTimeout(() => setMuted(!isMuted), 0);
112
- }, [playerRef, setProps, videoMetrika, metrika]);
113
- const handleClick = useCallback(() => changeMute(muted), [changeMute, muted]);
109
+ }, [metrika, videoMetrika, setProps]);
114
110
  const handleClickPreview = useCallback(() => {
115
111
  setIsPlaying(true);
116
112
  onClickPreview === null || onClickPreview === void 0 ? void 0 : onClickPreview();
@@ -122,13 +118,8 @@ export const ReactPlayerBlock = React.forwardRef((props, originRef) => {
122
118
  }
123
119
  }, [onClickPreview, setIsPlaying, videoMetrika, metrika]);
124
120
  const onPause = useCallback(() => {
125
- // For support correct state for youtube
126
121
  setIsPlaying(false);
127
- if (controls === MediaVideoControlsType.Custom) {
128
- setPaused(true);
129
- setIsPlaying(true);
130
- }
131
- }, [controls, setIsPlaying, setPaused]);
122
+ }, []);
132
123
  const onStart = useCallback(() => {
133
124
  if (!autoPlay && !initiallyMuted) {
134
125
  setMuted(false);
@@ -137,21 +128,17 @@ export const ReactPlayerBlock = React.forwardRef((props, originRef) => {
137
128
  const onPlay = useCallback(() => {
138
129
  setIsPlaying(true);
139
130
  if (controls === MediaVideoControlsType.Custom) {
140
- if (ended) {
141
- changeMute(false);
142
- }
143
- else if (paused) {
144
- changeMute(muted);
145
- }
146
- setEnded(false);
147
- setPaused(false);
131
+ changeMute(true);
148
132
  }
149
- }, [changeMute, controls, ended, muted, paused]);
133
+ }, [changeMute, controls]);
150
134
  const onProgress = useCallback((progress) => {
151
135
  setPlayedPercent(progress.played);
152
136
  if (progress.played === 1) {
153
137
  setMuted(true);
154
138
  }
139
+ else {
140
+ setEnded(false);
141
+ }
155
142
  }, []);
156
143
  const onEnded = useCallback(() => {
157
144
  // Youtube videos not muted after finishing playing and start again.
@@ -164,21 +151,37 @@ export const ReactPlayerBlock = React.forwardRef((props, originRef) => {
164
151
  }
165
152
  setEnded(true);
166
153
  }, [loop, playerRef]);
167
- const renderCustomBarControls = useCallback((isMuted, elapsedTimePercent) => {
168
- if (controls !== MediaVideoControlsType.Custom || !isPlaying) {
169
- return null;
154
+ const onPlayClick = useCallback(() => {
155
+ if (isPlaying) {
156
+ onPause();
157
+ }
158
+ else {
159
+ onPlay();
160
+ }
161
+ }, [isPlaying, onPause, onPlay]);
162
+ const handleClick = useCallback(() => {
163
+ if (customControlsType === CustomControlsType.WithMuteButton) {
164
+ changeMute(muted);
165
+ }
166
+ else {
167
+ onPlayClick();
170
168
  }
171
- return (React.createElement(CustomBarControls, { className: b('custom-bar-controls', { muted: isMuted }, customBarControlsClassName), mute: {
172
- isMuted,
169
+ if (ended) {
170
+ playerRef === null || playerRef === void 0 ? void 0 : playerRef.seekTo(0);
171
+ onPlay();
172
+ }
173
+ }, [changeMute, customControlsType, ended, muted, onPlay, onPlayClick, playerRef]);
174
+ const onFocusIn = useCallback(() => setHovered(true), []);
175
+ const onFocusOut = useCallback(() => setHovered(false), []);
176
+ return (React.createElement("div", { className: b({ wrapper: !currentHeight, controls, started, hovered }, className), ref: ref, onClick: handleClick, onMouseEnter: onFocusIn, onMouseLeave: onFocusOut, onFocus: onFocusIn, onBlur: onFocusOut },
177
+ React.createElement(ReactPlayer, { className: b('player'), url: src, muted: muted, controls: controls === MediaVideoControlsType.Default, height: currentHeight || '100%', width: width || '100%', light: previewImgUrl, playing: isPlaying, playIcon: playIcon, progressInterval: FPS, onClickPreview: handleClickPreview, onStart: onStart, onReady: setPlayerRef, onPlay: onPlay, onPause: onPause, onProgress: onProgress, onEnded: onEnded }),
178
+ controls === MediaVideoControlsType.Custom && (React.createElement(CustomBarControls, { className: b('custom-bar-controls', { muted }, customBarControlsClassName), mute: {
179
+ isMuted: muted,
173
180
  changeMute: (event) => {
174
181
  event.stopPropagation();
175
- changeMute(isMuted);
182
+ changeMute(muted);
176
183
  },
177
- }, elapsedTimePercent: elapsedTimePercent }));
178
- }, [controls, isPlaying, customBarControlsClassName, changeMute]);
179
- return (React.createElement("div", { className: b({ wrapper: !currentHeight }, className), ref: ref, onClick: handleClick },
180
- React.createElement(ReactPlayer, { className: b('player'), url: src, muted: muted, controls: controls === MediaVideoControlsType.Default, height: currentHeight || '100%', width: width || '100%', light: previewImgUrl, playing: isPlaying, playIcon: playIcon, progressInterval: FPS, onClickPreview: handleClickPreview, onStart: onStart, onReady: setPlayerRef, onPlay: onPlay, onPause: onPause, onProgress: onProgress, onEnded: onEnded }),
181
- renderCustomBarControls(muted, playedPercent)));
184
+ }, elapsedTimePercent: playedPercent, type: customControlsType, isPaused: !isPlaying, onPlayClick: onPlayClick, isStarted: started }))));
182
185
  });
183
186
  function getHeight(width) {
184
187
  return (width / 16) * 9;
@@ -0,0 +1,4 @@
1
+ {
2
+ "play": "Play",
3
+ "pause": "Pause"
4
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: (key: string, params?: import("@gravity-ui/i18n").Params | undefined) => string;
2
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { registerKeyset } from '../../../utils/registerKeyset';
2
+ import en from './en.json';
3
+ import ru from './ru.json';
4
+ const COMPONENT = 'ReactPlayer';
5
+ export default registerKeyset({ en, ru }, COMPONENT);
@@ -0,0 +1,4 @@
1
+ {
2
+ "play": "Воспроизвести",
3
+ "pause": "Остановить"
4
+ }
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const VideoControlPause: React.FC<React.SVGProps<SVGSVGElement>>;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { a11yHiddenSvgProps } from '../utils/svg';
3
+ export const VideoControlPause = (props) => {
4
+ return (React.createElement("svg", Object.assign({ width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, a11yHiddenSvgProps, props),
5
+ React.createElement("g", { opacity: "0.9" },
6
+ React.createElement("mask", { id: "path-1-outside-1_1237_523", maskUnits: "userSpaceOnUse", x: "1", y: "0", width: "22", height: "24", fill: "black" },
7
+ React.createElement("rect", { fill: "white", x: "1", width: "22", height: "24" }),
8
+ React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4 1C2.89543 1 2 1.89543 2 3V21C2 22.1046 2.89543 23 4 23H8C9.10457 23 10 22.1046 10 21V3C10 1.89543 9.10457 1 8 1H4ZM16 1C14.8954 1 14 1.89543 14 3V21C14 22.1046 14.8954 23 16 23H20C21.1046 23 22 22.1046 22 21V3C22 1.89543 21.1046 1 20 1H16Z" })),
9
+ React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4 1C2.89543 1 2 1.89543 2 3V21C2 22.1046 2.89543 23 4 23H8C9.10457 23 10 22.1046 10 21V3C10 1.89543 9.10457 1 8 1H4ZM16 1C14.8954 1 14 1.89543 14 3V21C14 22.1046 14.8954 23 16 23H20C21.1046 23 22 22.1046 22 21V3C22 1.89543 21.1046 1 20 1H16Z", fill: "white" }),
10
+ React.createElement("path", { d: "M3 3C3 2.44772 3.44771 2 4 2V0C2.34315 0 1 1.34314 1 3H3ZM3 21V3H1V21H3ZM4 22C3.44772 22 3 21.5523 3 21H1C1 22.6569 2.34315 24 4 24V22ZM8 22H4V24H8V22ZM9 21C9 21.5523 8.55229 22 8 22V24C9.65685 24 11 22.6569 11 21H9ZM9 3V21H11V3H9ZM8 2C8.55228 2 9 2.44772 9 3H11C11 1.34315 9.65685 0 8 0V2ZM4 2H8V0H4V2ZM15 3C15 2.44772 15.4477 2 16 2V0C14.3431 0 13 1.34314 13 3H15ZM15 21V3H13V21H15ZM16 22C15.4477 22 15 21.5523 15 21H13C13 22.6569 14.3431 24 16 24V22ZM20 22H16V24H20V22ZM21 21C21 21.5523 20.5523 22 20 22V24C21.6569 24 23 22.6569 23 21H21ZM21 3V21H23V3H21ZM20 2C20.5523 2 21 2.44772 21 3H23C23 1.34315 21.6569 0 20 0V2ZM16 2H20V0H16V2Z", fill: "black", fillOpacity: "0.15", mask: "url(#path-1-outside-1_1237_523)" }))));
11
+ };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const VideoControlPlay: React.FC<React.SVGProps<SVGSVGElement>>;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { a11yHiddenSvgProps } from '../utils/svg';
3
+ export const VideoControlPlay = (props) => {
4
+ return (React.createElement("svg", Object.assign({ width: "24", height: "26", viewBox: "0 0 24 26", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, a11yHiddenSvgProps, props),
5
+ React.createElement("path", { d: "M20.5028 15.6408C22.4991 14.4663 22.4991 11.5337 20.5028 10.3592L6.99778 2.41411C4.99944 1.23846 2.5 2.70595 2.5 5.05488L2.5 20.9451C2.5 23.2941 4.99943 24.7615 6.99777 23.5859L20.5028 15.6408Z", fill: "white" }),
6
+ React.createElement("path", { d: "M20.7563 16.0717C23.0812 14.7039 23.0812 11.2961 20.7563 9.92827L7.25131 1.98315C4.90651 0.603685 2 2.33458 2 5.05488L2 20.9451C2 23.6654 4.90651 25.3963 7.25131 24.0168L20.7563 16.0717Z", stroke: "black", strokeOpacity: "0.15" })));
7
+ };
@@ -29,6 +29,10 @@ export declare enum PlayButtonThemes {
29
29
  Blue = "blue",
30
30
  Grey = "grey"
31
31
  }
32
+ export declare enum CustomControlsType {
33
+ WithMuteButton = "with-mute-button",
34
+ WithPlayPauseButton = "with-play-pause-button"
35
+ }
32
36
  export declare enum MediaVideoType {
33
37
  Default = "default",
34
38
  Player = "player"
@@ -109,7 +113,9 @@ export interface MediaVideoProps {
109
113
  elapsedTime?: number;
110
114
  playButton?: PlayButtonProps;
111
115
  controls?: MediaVideoControlsType;
116
+ customControlsOptions?: CustomControlsOptions;
112
117
  metrika?: MetrikaVideo;
118
+ ariaLabel?: string;
113
119
  }
114
120
  export interface LinkProps extends Stylable {
115
121
  url: string;
@@ -146,22 +152,14 @@ export interface ButtonImageProps {
146
152
  position?: 'left' | 'right';
147
153
  alt?: string;
148
154
  }
155
+ export interface CustomControlsOptions {
156
+ type?: CustomControlsType;
157
+ }
149
158
  export interface PlayButtonProps extends ClassNameProps {
150
159
  type?: PlayButtonType;
151
160
  theme?: PlayButtonThemes;
152
161
  text?: string;
153
162
  }
154
- export interface MediaVideoProps {
155
- src: string[];
156
- type?: MediaVideoType;
157
- loop?: LoopProps | boolean;
158
- muted?: boolean;
159
- autoplay?: boolean;
160
- elapsedTime?: number;
161
- playButton?: PlayButtonProps;
162
- controls?: MediaVideoControlsType;
163
- metrika?: MetrikaVideo;
164
- }
165
163
  export type ThemedMediaVideoProps = ThemeSupporting<MediaVideoProps>;
166
164
  export interface MediaComponentVideoProps {
167
165
  video: MediaVideoProps;
@@ -309,24 +307,6 @@ export interface PriceDetailedProps extends CardBaseProps {
309
307
  foldable?: PriceFoldableDetailsProps;
310
308
  labelsDefaultText?: Record<PriceLabelColor, string>;
311
309
  }
312
- export interface PriceDetailedProps extends CardBaseProps {
313
- items: PriceItemProps[];
314
- description?: {
315
- titleSize?: TextSize;
316
- descriptionSize?: TextSize;
317
- titleColor?: PriceDescriptionColor;
318
- };
319
- details?: {
320
- titleSize?: TextSize;
321
- descriptionSize?: TextSize;
322
- };
323
- priceType?: PriceDetailsType;
324
- numberGroupItems?: 3 | 4 | 5;
325
- isCombined?: boolean;
326
- useMixedView?: boolean;
327
- foldable?: PriceFoldableDetailsProps;
328
- labelsDefaultText?: Record<PriceLabelColor, string>;
329
- }
330
310
  export interface AuthorProps {
331
311
  author: AuthorItem;
332
312
  className?: string;
@@ -32,6 +32,11 @@ export var PlayButtonThemes;
32
32
  PlayButtonThemes["Blue"] = "blue";
33
33
  PlayButtonThemes["Grey"] = "grey";
34
34
  })(PlayButtonThemes || (PlayButtonThemes = {}));
35
+ export var CustomControlsType;
36
+ (function (CustomControlsType) {
37
+ CustomControlsType["WithMuteButton"] = "with-mute-button";
38
+ CustomControlsType["WithPlayPauseButton"] = "with-play-pause-button";
39
+ })(CustomControlsType || (CustomControlsType = {}));
35
40
  export var MediaVideoType;
36
41
  (function (MediaVideoType) {
37
42
  MediaVideoType["Default"] = "default";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/page-constructor",
3
- "version": "1.11.0",
3
+ "version": "1.12.0-alpha.0",
4
4
  "description": "Gravity UI Page Constructor",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,6 +29,10 @@ export declare enum PlayButtonThemes {
29
29
  Blue = "blue",
30
30
  Grey = "grey"
31
31
  }
32
+ export declare enum CustomControlsType {
33
+ WithMuteButton = "with-mute-button",
34
+ WithPlayPauseButton = "with-play-pause-button"
35
+ }
32
36
  export declare enum MediaVideoType {
33
37
  Default = "default",
34
38
  Player = "player"
@@ -109,7 +113,9 @@ export interface MediaVideoProps {
109
113
  elapsedTime?: number;
110
114
  playButton?: PlayButtonProps;
111
115
  controls?: MediaVideoControlsType;
116
+ customControlsOptions?: CustomControlsOptions;
112
117
  metrika?: MetrikaVideo;
118
+ ariaLabel?: string;
113
119
  }
114
120
  export interface LinkProps extends Stylable {
115
121
  url: string;
@@ -146,22 +152,14 @@ export interface ButtonImageProps {
146
152
  position?: 'left' | 'right';
147
153
  alt?: string;
148
154
  }
155
+ export interface CustomControlsOptions {
156
+ type?: CustomControlsType;
157
+ }
149
158
  export interface PlayButtonProps extends ClassNameProps {
150
159
  type?: PlayButtonType;
151
160
  theme?: PlayButtonThemes;
152
161
  text?: string;
153
162
  }
154
- export interface MediaVideoProps {
155
- src: string[];
156
- type?: MediaVideoType;
157
- loop?: LoopProps | boolean;
158
- muted?: boolean;
159
- autoplay?: boolean;
160
- elapsedTime?: number;
161
- playButton?: PlayButtonProps;
162
- controls?: MediaVideoControlsType;
163
- metrika?: MetrikaVideo;
164
- }
165
163
  export type ThemedMediaVideoProps = ThemeSupporting<MediaVideoProps>;
166
164
  export interface MediaComponentVideoProps {
167
165
  video: MediaVideoProps;
@@ -309,24 +307,6 @@ export interface PriceDetailedProps extends CardBaseProps {
309
307
  foldable?: PriceFoldableDetailsProps;
310
308
  labelsDefaultText?: Record<PriceLabelColor, string>;
311
309
  }
312
- export interface PriceDetailedProps extends CardBaseProps {
313
- items: PriceItemProps[];
314
- description?: {
315
- titleSize?: TextSize;
316
- descriptionSize?: TextSize;
317
- titleColor?: PriceDescriptionColor;
318
- };
319
- details?: {
320
- titleSize?: TextSize;
321
- descriptionSize?: TextSize;
322
- };
323
- priceType?: PriceDetailsType;
324
- numberGroupItems?: 3 | 4 | 5;
325
- isCombined?: boolean;
326
- useMixedView?: boolean;
327
- foldable?: PriceFoldableDetailsProps;
328
- labelsDefaultText?: Record<PriceLabelColor, string>;
329
- }
330
310
  export interface AuthorProps {
331
311
  author: AuthorItem;
332
312
  className?: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MediaVideoControlsType = exports.MediaVideoType = exports.PlayButtonThemes = exports.PlayButtonType = exports.PreviewItemType = exports.PriceLabelColor = exports.PriceDetailsType = exports.AuthorType = void 0;
3
+ exports.MediaVideoControlsType = exports.MediaVideoType = exports.CustomControlsType = exports.PlayButtonThemes = exports.PlayButtonType = exports.PreviewItemType = exports.PriceLabelColor = exports.PriceDetailsType = exports.AuthorType = void 0;
4
4
  // enums
5
5
  var AuthorType;
6
6
  (function (AuthorType) {
@@ -35,6 +35,11 @@ var PlayButtonThemes;
35
35
  PlayButtonThemes["Blue"] = "blue";
36
36
  PlayButtonThemes["Grey"] = "grey";
37
37
  })(PlayButtonThemes = exports.PlayButtonThemes || (exports.PlayButtonThemes = {}));
38
+ var CustomControlsType;
39
+ (function (CustomControlsType) {
40
+ CustomControlsType["WithMuteButton"] = "with-mute-button";
41
+ CustomControlsType["WithPlayPauseButton"] = "with-play-pause-button";
42
+ })(CustomControlsType = exports.CustomControlsType || (exports.CustomControlsType = {}));
38
43
  var MediaVideoType;
39
44
  (function (MediaVideoType) {
40
45
  MediaVideoType["Default"] = "default";