@design-edito/tools 0.4.11 → 0.4.12

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 (53) hide show
  1. package/agnostic/arrays/index.d.ts +1 -1
  2. package/agnostic/arrays/index.js +1 -1
  3. package/agnostic/colors/index.d.ts +2 -2
  4. package/agnostic/colors/index.js +2 -2
  5. package/agnostic/css/index.d.ts +1 -1
  6. package/agnostic/css/index.js +1 -1
  7. package/agnostic/html/deep-select/index.d.ts +31 -0
  8. package/agnostic/html/deep-select/index.js +52 -0
  9. package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +14 -14
  10. package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +14 -14
  11. package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +2 -2
  12. package/agnostic/html/hyper-json/smart-tags/isolated/index.js +2 -2
  13. package/agnostic/html/index.d.ts +3 -1
  14. package/agnostic/html/index.js +3 -1
  15. package/agnostic/html/watch-selection/index.d.ts +41 -0
  16. package/agnostic/html/watch-selection/index.js +50 -0
  17. package/agnostic/index.d.ts +3 -3
  18. package/agnostic/index.js +3 -3
  19. package/agnostic/misc/index.d.ts +3 -3
  20. package/agnostic/misc/index.js +3 -3
  21. package/agnostic/misc/logs/index.d.ts +1 -1
  22. package/agnostic/misc/logs/index.js +1 -1
  23. package/agnostic/numbers/index.d.ts +2 -2
  24. package/agnostic/numbers/index.js +2 -2
  25. package/agnostic/objects/index.d.ts +3 -3
  26. package/agnostic/objects/index.js +3 -3
  27. package/agnostic/random/index.d.ts +1 -1
  28. package/agnostic/random/index.js +1 -1
  29. package/agnostic/sanitization/index.d.ts +1 -1
  30. package/agnostic/sanitization/index.js +1 -1
  31. package/agnostic/strings/index.d.ts +3 -2
  32. package/agnostic/strings/index.js +3 -2
  33. package/agnostic/strings/split-trim/index.d.ts +27 -0
  34. package/agnostic/strings/split-trim/index.js +36 -0
  35. package/components/Video/index.controlled.d.ts +153 -0
  36. package/components/Video/index.controlled.js +255 -0
  37. package/components/Video/index.d.ts +10 -114
  38. package/components/Video/index.js +140 -265
  39. package/components/Video/utils.d.ts +11 -10
  40. package/components/Video/utils.js +30 -37
  41. package/components/public-classnames.d.ts +1 -0
  42. package/components/public-classnames.js +1 -0
  43. package/index.d.ts +1 -1
  44. package/index.js +1 -1
  45. package/node/@google-cloud/storage/file/index.d.ts +2 -2
  46. package/node/@google-cloud/storage/file/index.js +2 -2
  47. package/node/ftps/file/index.d.ts +2 -2
  48. package/node/ftps/file/index.js +2 -2
  49. package/node/images/transform/operations/index.d.ts +1 -1
  50. package/node/images/transform/operations/index.js +1 -1
  51. package/node/index.d.ts +1 -1
  52. package/node/index.js +1 -1
  53. package/package.json +22 -1
@@ -1,297 +1,172 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { clss } from '../../agnostic/css/clss/index.js';
4
4
  import { Disclaimer } from '../Disclaimer/index.js';
5
5
  import { IntersectionObserverComponent } from '../IntersectionObserver/index.js';
6
- import { Subtitles } from '../Subtitles/index.js';
7
6
  import { mergeClassNames } from '../utils/index.js';
8
- import { video as publicClassName } from '../public-classnames.js';
9
- import { formatTime, secondsToMs, muteAttributeWorkaround, forcePlay, forcePause, forceLoud, forceMute, forceFullScreen, forceExitFullScreen, forceVolume, forceCurrentTime, getTimelineClickProgress, forcePlaybackRate } from './utils.js';
7
+ import { videoWrapper as publicClassName } from '../public-classnames.js';
8
+ import { muteAttributeWorkaround } from './utils.js';
10
9
  import cssModule from './styles.module.css';
10
+ import { ControlledVideo } from './index.controlled.js';
11
11
  /**
12
12
  * Full-featured video player component. Wraps a native `<video>` element with
13
13
  * playback controls, volume, playback rate, a timeline, optional subtitles,
14
14
  * an optional disclaimer gate, and viewport-driven auto-play/mute behaviours.
15
15
  *
16
- * ### Root element modifiers
17
- * The root `<figure>` receives the public class name defined by `video` and
18
- * the following BEM-style modifier classes:
19
- * - `--play-on` / `--play-off` — reflects current playback state.
20
- * - `--fullscreen-on` / `--fullscreen-off` — reflects fullscreen state.
21
- * - `--loud` / `--muted` — reflects mute state.
22
- *
23
- * ### Data attributes on the root element
24
- * - `data-play-on` — present (empty string) when playing.
25
- * - `data-play-off` — present (empty string) when paused.
26
- * - `data-fullscreen-on` — present (empty string) when in fullScreen.
27
- * - `data-fullscreen-off` — present (empty string) when not in fullScreen.
28
- * - `data-loud` — present (empty string) when unmuted.
29
- * - `data-muted` — present (empty string) when muted.
30
- * - `data-volume` — current volume as a `0–1` float.
31
- * - `data-volume-percent` — current volume as a `0–100` float.
32
- * - `data-playback-rate` — current playback rate (e.g. `1`, `1.5`).
33
- * - `data-current-time-ms` — current time in milliseconds, fixed to 2 decimals.
34
- * - `data-current-time-ratio` — current / total ratio, fixed to 8 decimals.
35
- * - `data-total-time-ms` — total duration in milliseconds.
36
- *
37
- * ### CSS custom properties on the root element
38
- * - `--video-current-time-ratio` — current / total ratio, fixed to 8 decimals.
39
- * Useful for driving progress-bar animations purely in CSS.
40
- *
41
16
  * @param props - Component properties.
42
17
  * @see {@link Props}
43
18
  * @returns A `<figure>` element containing the video, its controls, optional
44
19
  * subtitles, and an optional disclaimer overlay.
45
20
  */
46
- export const Video = ({ sources, tracks, playBtnContent, pauseBtnContent, loudBtnContent, muteBtnContent, fullScreenBtnContent, subtitles, disclaimer, autoPlayWhenVisible, autoPauseWhenHidden, autoLoudWhenVisible, autoMuteWhenHidden, actionHandlers, stateHandlers, children, className, ...intrinsicVideoAttributes }) => {
21
+ export const Video = ({ loop, disclaimer, autoPlayWhenVisible, autoPauseWhenHidden, autoMuteWhenHidden, autoLoudWhenVisible, wrapperClassName, ...controlledProps }) => {
47
22
  // State & refs
48
- const [isPlaying, setIsPlaying] = useState(false);
49
- const [isFullScreen, setIsFullScreen] = useState(false);
50
- const [isLoud, setIsLoud] = useState(false);
51
- const [currentTime, setCurrentTime] = useState(0);
52
- const [totalTime, setTotalTime] = useState(0);
53
- const [volume, setVolume] = useState(0);
54
- const [playbackRate, setPlaybackRate] = useState(0);
55
- const [hasBeenAutoPlayed, setHasBeenAutoPlayed] = useState(false);
23
+ const [play, setPlay] = useState(false);
24
+ const [volume, setVolume] = useState(1);
25
+ const [mute, setMute] = useState(false);
26
+ const [playbackRate, setPlaybackRate] = useState(1);
27
+ const [fullscreen, setFullscreen] = useState(false);
56
28
  const [isDisclaimerOn, setIsDisclaimerOn] = useState(disclaimer?.isOn === true
57
29
  || disclaimer?.defaultIsOn === true
58
30
  || (disclaimer !== undefined && disclaimer.defaultIsOn === undefined));
59
- let shouldDisclaimerBeOn = isDisclaimerOn;
60
- if (disclaimer?.isOn === true) {
61
- shouldDisclaimerBeOn = true;
62
- }
63
- if (disclaimer?.isOn === false) {
64
- shouldDisclaimerBeOn = false;
65
- }
66
- const currentTimeMs = secondsToMs(currentTime);
67
- const totalTimeMs = useMemo(() => secondsToMs(totalTime), [totalTime]);
68
- const volumePercent = useMemo(() => volume * 100, [volume]);
69
- const videoRef = useRef(null);
70
- const $root = useRef(null);
71
- // Intrinsic event handlers
72
- const handleMetadataLoadEvent = useCallback((e) => {
73
- if (videoRef.current === null)
31
+ const hasBeenAutoPlayed = useRef(false);
32
+ const needsDisclaimer = useMemo(() => disclaimer !== undefined, [disclaimer]);
33
+ const shouldDisclaimerBeOn = useMemo(() => {
34
+ if (disclaimer?.isOn === false)
35
+ return false;
36
+ if (disclaimer?.isOn === true)
37
+ return true;
38
+ return isDisclaimerOn;
39
+ }, [disclaimer, isDisclaimerOn]);
40
+ const needsObserve = useMemo(() => autoLoudWhenVisible === true
41
+ || autoMuteWhenHidden === true
42
+ || autoPlayWhenVisible === true
43
+ || autoPauseWhenHidden === true, [
44
+ autoLoudWhenVisible,
45
+ autoMuteWhenHidden,
46
+ autoPlayWhenVisible,
47
+ autoPauseWhenHidden
48
+ ]);
49
+ // Intrisic event handlers
50
+ const handleOnPlayEvent = useCallback((e) => {
51
+ controlledProps.onPlay?.(e);
52
+ if (shouldDisclaimerBeOn) {
53
+ setPlay(false);
74
54
  return;
75
- const video = videoRef.current;
76
- muteAttributeWorkaround(video, intrinsicVideoAttributes.muted ?? false, setIsLoud);
77
- setTotalTime(video.duration);
78
- setPlaybackRate(video.playbackRate);
79
- setVolume(video.volume);
80
- setIsLoud(!video.muted);
81
- intrinsicVideoAttributes.onLoadedMetadata?.(e);
82
- }, [intrinsicVideoAttributes.onLoadedMetadata, intrinsicVideoAttributes.muted]);
83
- const handleVolumeChangeEvent = useCallback((e) => {
84
- const volume = e.currentTarget.volume;
85
- setVolume(volume);
86
- intrinsicVideoAttributes.onVolumeChange?.(e);
87
- }, [intrinsicVideoAttributes.onVolumeChange]);
88
- const handlePlayEvent = useCallback((e) => {
89
- setIsPlaying(true);
90
- setHasBeenAutoPlayed(true);
91
- intrinsicVideoAttributes.onPlay?.(e);
92
- }, [intrinsicVideoAttributes.onPlay]);
93
- const handlePauseEvent = useCallback((e) => {
94
- setIsPlaying(false);
95
- intrinsicVideoAttributes.onPause?.(e);
96
- }, [intrinsicVideoAttributes.onPause]);
97
- const handleRateChangeEvent = useCallback((e) => {
98
- const currentRate = e.currentTarget.playbackRate;
99
- setPlaybackRate(currentRate);
100
- intrinsicVideoAttributes.onRateChange?.(e);
101
- }, [intrinsicVideoAttributes.onRateChange]);
102
- const handleTimeUpdateEvent = useCallback((e) => {
103
- const currentTime = e.currentTarget.currentTime;
104
- setCurrentTime(currentTime);
105
- intrinsicVideoAttributes?.onTimeUpdate?.(e);
106
- }, [totalTime, intrinsicVideoAttributes.onTimeUpdate]);
107
- // User action handlers
108
- const handleVolumeRangeChange = useCallback((e) => {
109
- const targetVolume = Number(e.currentTarget.value);
110
- actionHandlers?.volumeRangeChange?.(e, targetVolume, volume, videoRef.current);
111
- forceVolume(videoRef.current, targetVolume, setVolume);
112
- }, [actionHandlers?.volumeRangeChange, volume]);
113
- const handleLoudButtonClick = useCallback((e) => {
114
- actionHandlers?.loudButtonClick?.(e, isLoud, videoRef.current);
115
- forceLoud(videoRef.current, setIsLoud);
116
- }, [actionHandlers?.loudButtonClick, isLoud]);
117
- const handleMuteButtonClick = useCallback((e) => {
118
- actionHandlers?.muteButtonClick?.(e, isLoud, videoRef.current);
119
- forceMute(videoRef.current, setIsLoud);
120
- }, [actionHandlers?.muteButtonClick, isLoud]);
121
- const handlePlayButtonClick = useCallback((e) => {
122
- actionHandlers?.playButtonClick?.(e, isPlaying, videoRef.current);
123
- void forcePlay(videoRef.current, shouldDisclaimerBeOn, setIsPlaying);
124
- }, [actionHandlers?.playButtonClick, isPlaying, shouldDisclaimerBeOn]);
125
- const handlePauseButtonClick = useCallback((e) => {
126
- actionHandlers?.pauseButtonClick?.(e, isPlaying, videoRef.current);
127
- forcePause(videoRef.current, setIsPlaying);
128
- }, [actionHandlers?.pauseButtonClick, isPlaying]);
129
- const handleFullScreenButtonClick = useCallback((e) => {
130
- actionHandlers?.fullscreenButtonClick?.(e, isFullScreen, videoRef.current);
131
- if (isFullScreen)
132
- void forceExitFullScreen(videoRef.current, setIsFullScreen);
133
- else
134
- void forceFullScreen(videoRef.current, shouldDisclaimerBeOn, setIsFullScreen);
135
- }, [isFullScreen, shouldDisclaimerBeOn]);
136
- const handleRateRangeChange = useCallback((e) => {
137
- const rate = Number(e.currentTarget.value);
138
- actionHandlers?.rateRangeChange?.(e, rate, playbackRate, videoRef.current);
139
- forcePlaybackRate(videoRef.current, rate, setPlaybackRate);
140
- }, [actionHandlers?.rateRangeChange, playbackRate]);
141
- const handleTimelineClick = useCallback((e) => {
142
- const progress = getTimelineClickProgress(e, e.currentTarget, videoRef.current);
143
- const time = progress * totalTime;
144
- actionHandlers?.timelineClick?.(e, time, currentTime, videoRef.current);
145
- forceCurrentTime(videoRef.current, time, setCurrentTime);
146
- }, [actionHandlers?.timelineClick, totalTime, currentTime]);
147
- // Disclaimer handlers
148
- const handleDisclaimerDismiss = useCallback(() => {
55
+ }
56
+ setPlay(true);
57
+ hasBeenAutoPlayed.current = true;
58
+ }, [controlledProps?.onPlay, shouldDisclaimerBeOn]);
59
+ const handleOnPauseEvent = useCallback((e) => {
60
+ setPlay(false);
61
+ controlledProps.onPause?.(e);
62
+ }, [controlledProps?.onPause]);
63
+ const handleOnVolumeChangeEvent = useCallback((e) => {
64
+ setMute(Boolean(e.currentTarget.muted));
65
+ setVolume(Number(e.currentTarget.volume));
66
+ controlledProps.onVolumeChange?.(e);
67
+ }, [controlledProps?.onVolumeChange]);
68
+ const handleOnRateChangeEvent = useCallback((e) => {
69
+ setPlaybackRate(Number(e.currentTarget.playbackRate));
70
+ controlledProps.onRateChange?.(e);
71
+ }, [controlledProps?.onRateChange]);
72
+ const handleOnFullscreenChangeEvent = useCallback((e) => {
73
+ if (document.fullscreenElement === null || shouldDisclaimerBeOn) {
74
+ setFullscreen(false);
75
+ }
76
+ }, [shouldDisclaimerBeOn]);
77
+ const handleOnLoadedMetadataEvent = useCallback((e) => {
78
+ muteAttributeWorkaround(e.currentTarget, controlledProps.muted ?? false);
79
+ controlledProps.onLoadedMetadata?.(e);
80
+ }, [controlledProps?.onLoadedMetadata, controlledProps?.muted]);
81
+ // User actions
82
+ const handlePlayButtonClick = useCallback((e, isPlaying, video) => {
83
+ controlledProps.actionHandlers?.playButtonClick?.(e, isPlaying, video);
84
+ setPlay(!shouldDisclaimerBeOn);
85
+ }, [controlledProps.actionHandlers, shouldDisclaimerBeOn]);
86
+ const handlePauseButtonClick = useCallback((e, isPlaying, video) => {
87
+ controlledProps.actionHandlers?.pauseButtonClick?.(e, isPlaying, video);
88
+ setPlay(false);
89
+ }, [controlledProps.actionHandlers]);
90
+ const handleLoudButtonClick = useCallback((e, isLoud, video) => {
91
+ controlledProps.actionHandlers?.loudButtonClick?.(e, isLoud, video);
92
+ setMute(false);
93
+ }, [controlledProps.actionHandlers]);
94
+ const handleMuteButtonClick = useCallback((e, isLoud, video) => {
95
+ controlledProps.actionHandlers?.muteButtonClick?.(e, isLoud, video);
96
+ setMute(true);
97
+ }, [controlledProps.actionHandlers]);
98
+ const handleRateRangeChange = useCallback((e, targetRate, currentRate, video) => {
99
+ controlledProps.actionHandlers?.rateRangeChange?.(e, targetRate, currentRate, video);
100
+ setPlaybackRate(targetRate);
101
+ }, [controlledProps.actionHandlers]);
102
+ const handleVolumeRangeChange = useCallback((e, targetVolume, currentVolume, video) => {
103
+ controlledProps.actionHandlers?.volumeRangeChange?.(e, targetVolume, currentVolume, video);
104
+ setVolume(targetVolume);
105
+ }, [controlledProps.actionHandlers]);
106
+ const handleFullscreenButtonClick = useCallback((e, isFullscreen, video) => {
107
+ controlledProps.actionHandlers?.fullscreenButtonClick?.(e, isFullscreen, video);
108
+ setFullscreen((prev) => shouldDisclaimerBeOn ? false : !isFullscreen);
109
+ }, [controlledProps.actionHandlers, shouldDisclaimerBeOn]);
110
+ // Intersection Observer + Disclaimer
111
+ const onIntersected = useCallback(({ ioEntry }) => {
112
+ if (ioEntry === undefined)
113
+ return;
114
+ const { isIntersecting = false } = ioEntry;
115
+ if (autoPauseWhenHidden === true && !isIntersecting) {
116
+ setPlay(false);
117
+ }
118
+ if (autoLoudWhenVisible === true && isIntersecting) {
119
+ setMute(false);
120
+ }
121
+ if (autoPlayWhenVisible === true && !shouldDisclaimerBeOn && !hasBeenAutoPlayed.current && isIntersecting) {
122
+ setPlay(true);
123
+ }
124
+ if (autoMuteWhenHidden === true && !shouldDisclaimerBeOn && !hasBeenAutoPlayed.current && !isIntersecting) {
125
+ setMute(true);
126
+ }
127
+ }, [autoPlayWhenVisible, autoPauseWhenHidden, autoMuteWhenHidden, autoLoudWhenVisible, shouldDisclaimerBeOn]);
128
+ const handleDiclaimerDismiss = useCallback((prevIsOn) => {
149
129
  setIsDisclaimerOn(false);
150
- if (intrinsicVideoAttributes.autoPlay === true
151
- && !hasBeenAutoPlayed)
152
- void forcePlay(videoRef.current, shouldDisclaimerBeOn, setIsPlaying);
153
- }, [disclaimer, intrinsicVideoAttributes.autoPlay, shouldDisclaimerBeOn]);
130
+ if (controlledProps.autoPlay === true && !hasBeenAutoPlayed.current) {
131
+ setPlay(true);
132
+ }
133
+ disclaimer?.actionHandlers?.dismissClick?.(prevIsOn);
134
+ }, [controlledProps.autoPlay, disclaimer?.actionHandlers?.dismissClick]);
154
135
  useEffect(() => {
155
- if (isDisclaimerOn !== shouldDisclaimerBeOn)
136
+ if (isDisclaimerOn !== shouldDisclaimerBeOn) {
156
137
  setIsDisclaimerOn(shouldDisclaimerBeOn);
157
- if (shouldDisclaimerBeOn) {
158
- forceMute(videoRef.current, setIsLoud);
159
- forcePause(videoRef.current, setIsPlaying);
160
- void forceExitFullScreen(videoRef.current, setIsFullScreen);
161
- }
162
- else if (intrinsicVideoAttributes.autoPlay === true) {
163
- if (videoRef.current === null)
164
- return;
165
- if (hasBeenAutoPlayed)
166
- return;
167
- if (videoRef.current.paused)
168
- void forcePlay(videoRef.current, shouldDisclaimerBeOn, setIsPlaying);
169
138
  }
170
- }, [
171
- shouldDisclaimerBeOn,
172
- intrinsicVideoAttributes,
173
- hasBeenAutoPlayed
174
- ]);
175
- useEffect(() => {
176
- stateHandlers?.isPlaying?.(isPlaying);
177
- }, [isPlaying, stateHandlers?.isPlaying]);
178
- useEffect(() => {
179
- stateHandlers?.isFullScreen?.(isFullScreen);
180
- }, [isFullScreen, stateHandlers?.isFullScreen]);
139
+ }, [isDisclaimerOn, shouldDisclaimerBeOn]);
181
140
  useEffect(() => {
182
- stateHandlers?.isLoud?.(isLoud);
183
- }, [isLoud, stateHandlers?.isLoud]);
184
- useEffect(() => {
185
- stateHandlers?.volume?.(volume);
186
- }, [volume, stateHandlers?.volume]);
187
- useEffect(() => {
188
- stateHandlers?.playbackRate?.(playbackRate);
189
- }, [playbackRate, stateHandlers?.playbackRate]);
190
- useEffect(() => {
191
- stateHandlers?.currentTime?.(currentTime);
192
- }, [currentTime, stateHandlers?.currentTime]);
193
- // Parsing sources & tracks props
194
- const parsedSources = useMemo(() => {
195
- if (sources === undefined)
196
- return [];
197
- if (typeof sources === 'string')
198
- return [{ src: sources }];
199
- if (Array.isArray(sources)) {
200
- if (sources.length === 0)
201
- return [];
202
- if (typeof sources[0] === 'string')
203
- return sources.map(src => ({ src }));
204
- return sources;
141
+ if (shouldDisclaimerBeOn) {
142
+ setMute(true);
143
+ setPlay(false);
144
+ setFullscreen(false);
205
145
  }
206
- return [];
207
- }, [sources]);
208
- const parsedTracks = useMemo(() => {
209
- if (tracks === undefined)
210
- return [];
211
- if (typeof tracks === 'string')
212
- return [{ src: tracks }];
213
- if (Array.isArray(tracks)) {
214
- if (tracks.length === 0)
215
- return [];
216
- if (typeof tracks[0] === 'string')
217
- return tracks.map(src => ({ src }));
218
- return tracks;
146
+ else if (controlledProps.autoPlay === true && !hasBeenAutoPlayed.current) {
147
+ setPlay(true);
219
148
  }
220
- return [];
221
- }, [tracks]);
222
- // Rendering
149
+ }, [shouldDisclaimerBeOn]);
150
+ // Render
223
151
  const c = clss(publicClassName, { cssModule });
224
- const rootClss = mergeClassNames(c(null, {
225
- 'play-on': isPlaying,
226
- 'play-off': !isPlaying,
227
- 'fullscreen-on': isFullScreen,
228
- 'fullscreen-off': !isFullScreen,
229
- 'loud': isLoud,
230
- 'muted': !isLoud
231
- }), className);
232
- const rootAttributes = {
233
- 'data-play-on': isPlaying ? '' : undefined,
234
- 'data-play-off': !isPlaying ? '' : undefined,
235
- 'data-fullscreen-on': isFullScreen ? '' : undefined,
236
- 'data-fullscreen-off': !isFullScreen ? '' : undefined,
237
- 'data-loud': isLoud ? '' : undefined,
238
- 'data-muted': !isLoud ? '' : undefined,
239
- 'data-volume': volume,
240
- 'data-volume-percent': volumePercent,
241
- 'data-playback-rate': playbackRate,
242
- 'data-current-time-ms': currentTimeMs.toFixed(2),
243
- 'data-current-time-ratio': (currentTime / totalTime).toFixed(8),
244
- 'data-total-time-ms': totalTimeMs
245
- };
246
- const rootStyles = {
247
- [`--${publicClassName}-elapsed-time-ratio`]: (currentTime / totalTime).toFixed(8)
248
- };
249
- const videoClss = c('video');
250
- const videoControlsClss = c('video-controls');
251
- const playBtnClss = c('play-btn');
252
- const pauseBtnClss = c('pause-btn');
253
- const loudBtnClss = c('loud-btn');
254
- const muteBtnClss = c('mute-btn');
255
- const volumePcntClss = c('volume-percent');
256
- const fullScreenBtnClss = c('fullscreen-btn');
257
- const volumeRangeClss = c('volume-range');
258
- const playbackRateRangeClss = c('playback-rate-range');
259
- const playbackRateClss = c('playback-rate');
260
- const timeControlsClss = c('time-controls');
261
- const currentTimeClss = c('current-time');
262
- const totalTimeClss = c('total-time');
263
- const timelineClss = c('timeline');
264
- const sensitiveContent = _jsxs(_Fragment, { children: [_jsxs("video", { ref: videoRef, className: videoClss, onVolumeChange: handleVolumeChangeEvent, onLoadedMetadata: handleMetadataLoadEvent, onPlay: handlePlayEvent, onPause: handlePauseEvent, onTimeUpdate: handleTimeUpdateEvent, onRateChange: handleRateChangeEvent, ...intrinsicVideoAttributes, autoPlay: shouldDisclaimerBeOn ? false : intrinsicVideoAttributes.autoPlay, muted: shouldDisclaimerBeOn ? true : intrinsicVideoAttributes.muted, children: [parsedSources.map((source, index) => typeof source === 'string'
265
- ? _jsx("source", { src: source }, index)
266
- : _jsx("source", { src: source.src, type: source.type }, index)), parsedTracks.map((track, index) => typeof track === 'string'
267
- ? _jsx("track", { src: track }, index)
268
- : _jsx("track", { src: track.src, kind: track.kind, srcLang: track.srclang, label: track.label, default: track.default }, index)), children] }), _jsxs("div", { className: videoControlsClss, children: [_jsx("button", { className: playBtnClss, onClick: handlePlayButtonClick, children: playBtnContent }), _jsx("button", { className: pauseBtnClss, onClick: handlePauseButtonClick, children: pauseBtnContent }), _jsx("button", { className: loudBtnClss, onClick: handleLoudButtonClick, children: loudBtnContent }), _jsx("button", { className: muteBtnClss, onClick: handleMuteButtonClick, children: muteBtnContent }), _jsx("input", { type: "range", className: volumeRangeClss, value: volumePercent, onChange: handleVolumeRangeChange, min: 0, max: 100, step: 1 }), _jsx("span", { className: volumePcntClss, children: volumePercent }), _jsx("button", { className: fullScreenBtnClss, onClick: handleFullScreenButtonClick, children: fullScreenBtnContent }), _jsx("input", { type: "range", className: playbackRateRangeClss, value: playbackRate, onChange: handleRateRangeChange, min: 0.25, max: 4, step: 0.25 }), _jsx("span", { className: playbackRateClss, children: playbackRate })] }), _jsxs("div", { className: timeControlsClss, children: [_jsx("span", { className: currentTimeClss, children: formatTime(currentTimeMs, 'mm:ss:ms') }), _jsx("span", { className: totalTimeClss, children: formatTime(totalTimeMs, 'mm:ss:ms') }), _jsx("div", { className: timelineClss, onClick: handleTimelineClick })] }), subtitles !== undefined && _jsx(Subtitles, { ...subtitles, timecodeMs: currentTimeMs })] });
269
- const disclaimedContent = disclaimer !== undefined
270
- ? _jsx(Disclaimer, { ...disclaimer, isOn: shouldDisclaimerBeOn, actionHandlers: {
271
- dismissClick: handleDisclaimerDismiss
152
+ const rootClss = mergeClassNames(c(null, {}), wrapperClassName);
153
+ const sensitiveContent = _jsx(ControlledVideo, { ...controlledProps, play: play, volume: volume, mute: mute, playbackRate: playbackRate, fullscreen: fullscreen, onPlay: handleOnPlayEvent, onPause: handleOnPauseEvent, onVolumeChange: handleOnVolumeChangeEvent, onRateChange: handleOnRateChangeEvent, onLoadedMetadata: handleOnLoadedMetadataEvent, onFullscreenChange: handleOnFullscreenChangeEvent, actionHandlers: {
154
+ ...controlledProps.actionHandlers,
155
+ playButtonClick: handlePlayButtonClick,
156
+ pauseButtonClick: handlePauseButtonClick,
157
+ loudButtonClick: handleLoudButtonClick,
158
+ muteButtonClick: handleMuteButtonClick,
159
+ volumeRangeChange: handleVolumeRangeChange,
160
+ rateRangeChange: handleRateRangeChange,
161
+ fullscreenButtonClick: handleFullscreenButtonClick
162
+ } });
163
+ const disclaimedContent = needsDisclaimer
164
+ ? _jsx(Disclaimer, { ...disclaimer, actionHandlers: {
165
+ ...disclaimer?.actionHandlers,
166
+ dismissClick: handleDiclaimerDismiss
272
167
  }, children: sensitiveContent })
273
168
  : sensitiveContent;
274
- const observedContent = autoLoudWhenVisible === true
275
- || autoMuteWhenHidden === true
276
- || autoPlayWhenVisible === true
277
- || autoPauseWhenHidden === true
278
- ? _jsx(IntersectionObserverComponent, { onIntersected: ({ ioEntry }) => {
279
- const { isIntersecting = false } = ioEntry ?? {};
280
- if (autoMuteWhenHidden === true && !isIntersecting)
281
- forceMute(videoRef.current, setIsLoud);
282
- if (autoPauseWhenHidden === true && !isIntersecting)
283
- forcePause(videoRef.current, setIsPlaying);
284
- if (autoPlayWhenVisible === true
285
- && !hasBeenAutoPlayed
286
- && !shouldDisclaimerBeOn
287
- && isIntersecting)
288
- void forcePlay(videoRef.current, shouldDisclaimerBeOn, setIsPlaying);
289
- if (autoLoudWhenVisible === true
290
- && !hasBeenAutoPlayed
291
- && !shouldDisclaimerBeOn
292
- && isIntersecting)
293
- forceLoud(videoRef.current, setIsLoud);
294
- }, children: disclaimedContent })
295
- : disclaimedContent;
296
- return _jsx("figure", { className: rootClss, ...rootAttributes, ref: $root, style: rootStyles, children: observedContent });
169
+ return (_jsx("div", { className: rootClss, children: needsObserve
170
+ ? _jsx(IntersectionObserverComponent, { onIntersected: onIntersected, children: disclaimedContent })
171
+ : disclaimedContent }));
297
172
  };
@@ -1,14 +1,15 @@
1
1
  import type { Dispatch, SetStateAction } from 'react';
2
- export declare const muteAttributeWorkaround: (video: HTMLVideoElement | null, shouldMute: boolean, setIsSoundOn: Dispatch<SetStateAction<boolean>>) => void;
3
- export declare const forceMute: (video: HTMLVideoElement | null, setIsSoundOn: Dispatch<SetStateAction<boolean>>) => void;
4
- export declare const forceLoud: (video: HTMLVideoElement | null, setIsSoundOn: Dispatch<SetStateAction<boolean>>) => void;
5
- export declare const forceVolume: (video: HTMLVideoElement | null, volumePercent: number, setVolume: Dispatch<SetStateAction<number>>) => void;
2
+ export declare const muteAttributeWorkaround: (video: HTMLVideoElement | null, shouldMute: boolean) => void;
3
+ export declare const forceMute: (video: HTMLVideoElement | null) => void;
4
+ export declare const forceLoud: (video: HTMLVideoElement | null) => void;
5
+ export declare const forceVolume: (video: HTMLVideoElement | null, volume: number) => void;
6
6
  export declare const forceCurrentTime: (video: HTMLVideoElement | null, time: number, setCurrentTime: Dispatch<SetStateAction<number>>) => void;
7
- export declare const forcePlay: (video: HTMLVideoElement | null, shouldDisclaimerBeOn: boolean, setIsPlaying: Dispatch<SetStateAction<boolean>>) => Promise<void>;
8
- export declare const forcePause: (video: HTMLVideoElement | null, setIsPlaying: Dispatch<SetStateAction<boolean>>) => void;
9
- export declare const forcePlaybackRate: (video: HTMLVideoElement | null, rate: number, setPlaybackRate: Dispatch<SetStateAction<number>>) => void;
10
- export declare const forceFullScreen: (video: HTMLVideoElement | null, shouldDisclaimerBeOn: boolean, setIsFullscreen: Dispatch<SetStateAction<boolean>>) => Promise<void>;
11
- export declare const forceExitFullScreen: (video: HTMLVideoElement | null, setIsFullscreen: Dispatch<SetStateAction<boolean>>) => Promise<void>;
7
+ export declare const forcePlay: (video: HTMLVideoElement | null) => Promise<boolean>;
8
+ export declare const forcePause: (video: HTMLVideoElement | null) => Promise<boolean>;
9
+ export declare const forcePlaybackRate: (video: HTMLVideoElement | null, rate: number) => void;
10
+ export declare const forceFullscreen: (video: HTMLVideoElement | null) => Promise<boolean>;
11
+ export declare const forceExitFullscreen: (video: HTMLVideoElement | null) => Promise<boolean>;
12
12
  export declare function secondsToMs(seconds: number): number;
13
+ export declare function msToSeconds(ms: number): number;
13
14
  export declare function formatTime(ms: number, format: string, fps?: number): string;
14
- export declare const getTimelineClickProgress: (event: React.MouseEvent<HTMLDivElement, MouseEvent>, timeline: HTMLDivElement | null, video: HTMLVideoElement | null) => number;
15
+ export declare const getTimelineClickProgress: (event: React.MouseEvent<HTMLDivElement>, timeline: HTMLDivElement | null, video: HTMLVideoElement | null) => number;
@@ -1,5 +1,5 @@
1
1
  /* Video element triggers */
2
- export const muteAttributeWorkaround = (video, shouldMute, setIsSoundOn) => {
2
+ export const muteAttributeWorkaround = (video, shouldMute) => {
3
3
  if (video === null)
4
4
  return;
5
5
  if (!shouldMute)
@@ -9,26 +9,21 @@ export const muteAttributeWorkaround = (video, shouldMute, setIsSoundOn) => {
9
9
  return;
10
10
  video.setAttribute('muted', '');
11
11
  video.load();
12
- setIsSoundOn(false);
13
12
  };
14
- export const forceMute = (video, setIsSoundOn) => {
13
+ export const forceMute = (video) => {
15
14
  if (video === null)
16
15
  return;
17
16
  video.muted = true;
18
- setIsSoundOn(false);
19
17
  };
20
- export const forceLoud = (video, setIsSoundOn) => {
18
+ export const forceLoud = (video) => {
21
19
  if (video === null)
22
20
  return;
23
21
  video.muted = false;
24
- setIsSoundOn(true);
25
22
  };
26
- export const forceVolume = (video, volumePercent, setVolume) => {
23
+ export const forceVolume = (video, volume) => {
27
24
  if (video === null)
28
25
  return;
29
- const volume = volumePercent / 100;
30
26
  video.volume = volume;
31
- setVolume(volume);
32
27
  };
33
28
  export const forceCurrentTime = (video, time, setCurrentTime) => {
34
29
  if (video === null)
@@ -36,73 +31,71 @@ export const forceCurrentTime = (video, time, setCurrentTime) => {
36
31
  video.currentTime = time;
37
32
  setCurrentTime(time);
38
33
  };
39
- export const forcePlay = async (video, shouldDisclaimerBeOn, setIsPlaying) => {
40
- if (shouldDisclaimerBeOn) {
41
- setIsPlaying(false);
42
- return;
43
- }
34
+ export const forcePlay = async (video) => {
44
35
  if (video === null)
45
- return;
36
+ return false;
37
+ if (!video.paused)
38
+ return true;
46
39
  try {
47
40
  await video.play();
48
- setIsPlaying(true);
41
+ return video.paused;
49
42
  }
50
43
  catch (e) {
51
44
  console.error(e);
52
45
  }
46
+ return false;
53
47
  };
54
- export const forcePause = (video, setIsPlaying) => {
55
- if (video === null) {
56
- setIsPlaying(false);
57
- return;
58
- }
48
+ export const forcePause = async (video) => {
49
+ if (video === null)
50
+ return false;
51
+ if (video.paused)
52
+ return true;
59
53
  try {
60
54
  video.pause();
61
- setIsPlaying(false);
55
+ return video.paused;
62
56
  }
63
57
  catch (e) {
64
58
  console.error(e);
65
59
  }
60
+ return false;
66
61
  };
67
- export const forcePlaybackRate = (video, rate, setPlaybackRate) => {
62
+ export const forcePlaybackRate = (video, rate) => {
68
63
  if (video === null)
69
64
  return;
70
65
  video.playbackRate = rate;
71
- setPlaybackRate(rate);
72
66
  };
73
- export const forceFullScreen = async (video, shouldDisclaimerBeOn, setIsFullscreen) => {
74
- if (shouldDisclaimerBeOn)
75
- return;
76
- if (video === null) {
77
- setIsFullscreen(false);
78
- return;
79
- }
67
+ export const forceFullscreen = async (video) => {
68
+ if (video === null)
69
+ return false;
80
70
  try {
81
71
  await video.requestFullscreen();
82
- setIsFullscreen(true);
72
+ return document.fullscreenElement === video;
83
73
  }
84
74
  catch (e) {
85
- setIsFullscreen(false);
86
75
  console.error(e);
87
76
  }
77
+ return false;
88
78
  };
89
- export const forceExitFullScreen = async (video, setIsFullscreen) => {
79
+ export const forceExitFullscreen = async (video) => {
90
80
  if (video === null || document.fullscreenElement !== video) {
91
- setIsFullscreen(false);
92
- return;
81
+ return false;
93
82
  }
94
83
  try {
95
84
  await document.exitFullscreen();
96
- setIsFullscreen(false);
85
+ return document.fullscreenElement !== video;
97
86
  }
98
87
  catch (e) {
99
88
  console.error(e);
100
89
  }
90
+ return false;
101
91
  };
102
92
  /* Time & formats */
103
93
  export function secondsToMs(seconds) {
104
94
  return seconds * 1000;
105
95
  }
96
+ export function msToSeconds(ms) {
97
+ return ms / 1000;
98
+ }
106
99
  export function formatTime(ms, format, fps = 25) {
107
100
  const totalSeconds = Math.floor(ms / 1000);
108
101
  const hours = Math.floor(totalSeconds / 3600);
@@ -16,3 +16,4 @@ export declare const subtitles = "dsed-subtitles";
16
16
  export declare const theatre = "dsed-theatre";
17
17
  export declare const uiModule = "dsed-ui-module";
18
18
  export declare const video = "dsed-video";
19
+ export declare const videoWrapper = "dsed-video-wrapper";
@@ -16,3 +16,4 @@ export const subtitles = 'dsed-subtitles';
16
16
  export const theatre = 'dsed-theatre';
17
17
  export const uiModule = 'dsed-ui-module';
18
18
  export const video = 'dsed-video';
19
+ export const videoWrapper = 'dsed-video-wrapper';
package/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * as components from './components/index.js'
2
1
  export * as agnostic from './agnostic/index.js'
2
+ export * as components from './components/index.js'
3
3
  export * as node from './node/index.js'
package/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export * as components from './components/index.js'
2
1
  export * as agnostic from './agnostic/index.js'
2
+ export * as components from './components/index.js'
3
3
  export * as node from './node/index.js'