@design-edito/tools 0.4.6 → 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.
- package/agnostic/arrays/index.d.ts +1 -1
- package/agnostic/arrays/index.js +1 -1
- package/agnostic/arrays/make/index.d.ts +1 -1
- package/agnostic/css/index.d.ts +2 -2
- package/agnostic/css/index.js +2 -2
- package/agnostic/css/scale/index.d.ts +46 -16
- package/agnostic/css/scale/index.js +67 -38
- package/agnostic/css/scale/index.test.js +81 -64
- package/agnostic/html/deep-select/index.d.ts +31 -0
- package/agnostic/html/deep-select/index.js +52 -0
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +13 -13
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +13 -13
- package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +1 -1
- package/agnostic/html/hyper-json/smart-tags/isolated/index.js +1 -1
- package/agnostic/html/index.d.ts +4 -2
- package/agnostic/html/index.js +4 -2
- package/agnostic/html/watch-selection/index.d.ts +41 -0
- package/agnostic/html/watch-selection/index.js +50 -0
- package/agnostic/index.d.ts +3 -3
- package/agnostic/index.js +3 -3
- package/agnostic/misc/index.d.ts +1 -1
- package/agnostic/misc/index.js +1 -1
- package/agnostic/numbers/index.d.ts +3 -3
- package/agnostic/numbers/index.js +3 -3
- package/agnostic/objects/index.d.ts +2 -2
- package/agnostic/objects/index.js +2 -2
- package/agnostic/optim/index.d.ts +1 -1
- package/agnostic/optim/index.js +1 -1
- package/agnostic/strings/index.d.ts +2 -1
- package/agnostic/strings/index.js +2 -1
- package/agnostic/strings/split-trim/index.d.ts +27 -0
- package/agnostic/strings/split-trim/index.js +36 -0
- package/components/Video/index.controlled.d.ts +153 -0
- package/components/Video/index.controlled.js +255 -0
- package/components/Video/index.d.ts +10 -114
- package/components/Video/index.js +140 -265
- package/components/Video/utils.d.ts +11 -10
- package/components/Video/utils.js +30 -37
- package/components/index.d.ts +2 -2
- package/components/index.js +2 -2
- package/components/public-classnames.d.ts +1 -0
- package/components/public-classnames.js +1 -0
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/node/@aws-s3/storage/directory/index.d.ts +1 -1
- package/node/@aws-s3/storage/directory/index.js +1 -1
- package/node/@aws-s3/storage/file/index.d.ts +1 -1
- package/node/@aws-s3/storage/file/index.js +1 -1
- package/node/@google-cloud/storage/file/index.d.ts +1 -1
- package/node/@google-cloud/storage/file/index.js +1 -1
- package/node/ftps/file/index.d.ts +1 -1
- package/node/ftps/file/index.js +1 -1
- package/node/images/transform/operations/index.d.ts +2 -2
- package/node/images/transform/operations/index.js +2 -2
- package/node/index.d.ts +3 -3
- package/node/index.js +3 -3
- package/node/sftp/file/index.d.ts +2 -2
- package/node/sftp/file/index.js +2 -2
- package/package.json +22 -1
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useRef, useState, useCallback, useEffect } from 'react';
|
|
3
|
+
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
|
+
import { mergeClassNames } from '../utils/index.js';
|
|
5
|
+
import cssModule from './styles.module.css';
|
|
6
|
+
import { video as publicClassName } from '../public-classnames.js';
|
|
7
|
+
import { Subtitles } from '../Subtitles/index.js';
|
|
8
|
+
import { forceExitFullscreen, forceFullscreen, forceLoud, forceMute, forcePause, forcePlay, forcePlaybackRate, forceVolume, formatTime, getTimelineClickProgress, msToSeconds, secondsToMs } from './utils.js';
|
|
9
|
+
/**
|
|
10
|
+
* Full-featured video player component. Wraps a native `<video>` element with
|
|
11
|
+
* playback controls, volume, playback rate, a timeline, optional subtitles
|
|
12
|
+
* and viewport-driven auto-play/mute behaviours.
|
|
13
|
+
*
|
|
14
|
+
* ### Root element modifiers
|
|
15
|
+
* The root `<figure>` receives the public class name defined by `video` and
|
|
16
|
+
* the following BEM-style modifier classes:
|
|
17
|
+
* - `--play-on` / `--play-off` — reflects current playback state.
|
|
18
|
+
* - `--fullscreen-on` / `--fullscreen-off` — reflects fullscreen state.
|
|
19
|
+
* - `--loud` / `--muted` — reflects mute state.
|
|
20
|
+
*
|
|
21
|
+
* ### Data attributes on the root element
|
|
22
|
+
* - `data-play-on` — present (empty string) when playing.
|
|
23
|
+
* - `data-play-off` — present (empty string) when paused.
|
|
24
|
+
* - `data-fullscreen-on` — present (empty string) when in fullscreen.
|
|
25
|
+
* - `data-fullscreen-off` — present (empty string) when not in fullscreen.
|
|
26
|
+
* - `data-loud` — present (empty string) when unmuted.
|
|
27
|
+
* - `data-muted` — present (empty string) when muted.
|
|
28
|
+
* - `data-volume` — current volume as a `0–1` float.
|
|
29
|
+
* - `data-volume-percent` — current volume as a `0–100` float.
|
|
30
|
+
* - `data-playback-rate` — current playback rate (e.g. `1`, `1.5`).
|
|
31
|
+
* - `data-current-time-ms` — current time in milliseconds, fixed to 2 decimals.
|
|
32
|
+
* - `data-current-time-ratio` — current / total ratio, fixed to 8 decimals.
|
|
33
|
+
* - `data-total-time-ms` — total duration in milliseconds.
|
|
34
|
+
*
|
|
35
|
+
* ### CSS custom properties on the root element
|
|
36
|
+
* - `--video-current-time-ratio` — current / total ratio, fixed to 8 decimals.
|
|
37
|
+
* Useful for driving progress-bar animations purely in CSS.
|
|
38
|
+
*
|
|
39
|
+
* @param props - Component properties.
|
|
40
|
+
* @see {@link Props}
|
|
41
|
+
* @returns A `<figure>` element containing the video, its controls, optional
|
|
42
|
+
* subtitles.
|
|
43
|
+
*/
|
|
44
|
+
export const ControlledVideo = ({ sources, tracks, subtitles, playBtnContent, pauseBtnContent, loudBtnContent, muteBtnContent, fullscreenBtnContent, play, fullscreen, mute, muted, volume = 1, playbackRate = 1, currentTimeMs: givenCurrentTimeMs, actionHandlers, stateHandlers, children, className, _modifiers, ...intrinsicVideoAttributes }) => {
|
|
45
|
+
const videoRef = useRef(null);
|
|
46
|
+
const [totalTime, setTotalTime] = useState(0);
|
|
47
|
+
const totalTimeMs = useMemo(() => secondsToMs(totalTime), [totalTime]);
|
|
48
|
+
const [currentTimeMs, setCurrentTimeMs] = useState(0);
|
|
49
|
+
const currentTime = useMemo(() => msToSeconds(currentTimeMs), [currentTimeMs]);
|
|
50
|
+
const isTimeControlled = useMemo(() => givenCurrentTimeMs !== undefined, [givenCurrentTimeMs]);
|
|
51
|
+
const volumePercent = useMemo(() => volume * 100, [volume]);
|
|
52
|
+
// Intrinsic event handler
|
|
53
|
+
const handleMetadataLoadEvent = useCallback((e) => {
|
|
54
|
+
if (videoRef.current === null)
|
|
55
|
+
return;
|
|
56
|
+
const video = videoRef.current;
|
|
57
|
+
setTotalTime(Number(video.duration));
|
|
58
|
+
intrinsicVideoAttributes.onLoadedMetadata?.(e);
|
|
59
|
+
}, [intrinsicVideoAttributes.onLoadedMetadata]);
|
|
60
|
+
const handleOnTimeUpdateEvent = useCallback((e) => {
|
|
61
|
+
const video = e.currentTarget;
|
|
62
|
+
const newTimeMs = secondsToMs(Number(video.currentTime));
|
|
63
|
+
setCurrentTimeMs(newTimeMs);
|
|
64
|
+
if (intrinsicVideoAttributes?.onTimeUpdate !== undefined) {
|
|
65
|
+
intrinsicVideoAttributes.onTimeUpdate(e);
|
|
66
|
+
}
|
|
67
|
+
}, [intrinsicVideoAttributes.onTimeUpdate]);
|
|
68
|
+
const handleOnPlayEvent = useCallback((e) => {
|
|
69
|
+
/* We must never play a video that has controlled time */
|
|
70
|
+
if (isTimeControlled) {
|
|
71
|
+
videoRef.current?.pause();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
intrinsicVideoAttributes.onPlay?.(e);
|
|
75
|
+
}, [isTimeControlled, intrinsicVideoAttributes.onPlay]);
|
|
76
|
+
// Custom action handlers
|
|
77
|
+
const handlePlayButtonClick = useCallback((e) => {
|
|
78
|
+
const isPlaying = videoRef.current !== null ? Boolean(videoRef.current.paused) : false;
|
|
79
|
+
actionHandlers?.playButtonClick?.(e, isPlaying, videoRef.current);
|
|
80
|
+
}, [actionHandlers?.playButtonClick]);
|
|
81
|
+
const handlePauseButtonClick = useCallback((e) => {
|
|
82
|
+
const isPlaying = videoRef.current !== null ? Boolean(videoRef.current.paused) : false;
|
|
83
|
+
actionHandlers?.pauseButtonClick?.(e, isPlaying, videoRef.current);
|
|
84
|
+
}, [actionHandlers?.pauseButtonClick]);
|
|
85
|
+
const handleLoudButtonClick = useCallback((e) => {
|
|
86
|
+
const isLoud = mute !== undefined ? !mute : false;
|
|
87
|
+
actionHandlers?.loudButtonClick?.(e, isLoud, videoRef.current);
|
|
88
|
+
}, [actionHandlers?.loudButtonClick, mute]);
|
|
89
|
+
const handleMuteButtonClick = useCallback((e) => {
|
|
90
|
+
const isLoud = mute !== undefined ? !mute : false;
|
|
91
|
+
actionHandlers?.muteButtonClick?.(e, isLoud, videoRef.current);
|
|
92
|
+
}, [actionHandlers?.muteButtonClick, mute]);
|
|
93
|
+
const handleFullscreenButtonClick = useCallback((e) => {
|
|
94
|
+
actionHandlers?.fullscreenButtonClick?.(e, fullscreen ?? false, videoRef.current);
|
|
95
|
+
}, [actionHandlers?.fullscreenButtonClick, fullscreen]);
|
|
96
|
+
const handleVolumeRangeChange = useCallback((e) => {
|
|
97
|
+
const targetVolume = Number(e.currentTarget.value) / 100;
|
|
98
|
+
actionHandlers?.volumeRangeChange?.(e, targetVolume, volume, videoRef.current);
|
|
99
|
+
}, [actionHandlers?.volumeRangeChange, volume]);
|
|
100
|
+
const handleRateRangeChange = useCallback((e) => {
|
|
101
|
+
actionHandlers?.rateRangeChange?.(e, Number(e.currentTarget.value), playbackRate, videoRef.current);
|
|
102
|
+
}, [actionHandlers?.rateRangeChange, playbackRate]);
|
|
103
|
+
const handleTimelineClick = useCallback((e) => {
|
|
104
|
+
if (videoRef.current === null)
|
|
105
|
+
return;
|
|
106
|
+
const progress = getTimelineClickProgress(e, e.currentTarget, videoRef.current);
|
|
107
|
+
const targetTime = progress * totalTime;
|
|
108
|
+
actionHandlers?.timelineClick?.(e, targetTime, currentTime, videoRef.current);
|
|
109
|
+
/* If we are given a currentTimeMs prop, we consider that the current time is controlled by the parent component, and we should not attempt to set it here on timeline click, as it would create conflicts. The parent comp should update the currentTimeMs prop itself */
|
|
110
|
+
if (!isTimeControlled) {
|
|
111
|
+
videoRef.current.currentTime = targetTime;
|
|
112
|
+
}
|
|
113
|
+
}, [actionHandlers?.timelineClick, totalTime, currentTime, isTimeControlled]);
|
|
114
|
+
// Rendering
|
|
115
|
+
const isPlaying = play ?? false;
|
|
116
|
+
const isLoud = mute ?? false;
|
|
117
|
+
const isFullscreen = fullscreen ?? false;
|
|
118
|
+
const c = clss(publicClassName, { cssModule });
|
|
119
|
+
const rootClss = mergeClassNames(c(null, {
|
|
120
|
+
'play-on': isPlaying,
|
|
121
|
+
'play-off': !isPlaying,
|
|
122
|
+
'fullscreen-on': isFullscreen,
|
|
123
|
+
'fullscreen-off': !isFullscreen,
|
|
124
|
+
'loud': isLoud,
|
|
125
|
+
'muted': !isLoud,
|
|
126
|
+
..._modifiers
|
|
127
|
+
}), className);
|
|
128
|
+
const appliedVolume = volume ?? 0;
|
|
129
|
+
const rootAttributes = {
|
|
130
|
+
'data-play-on': isPlaying ? '' : undefined,
|
|
131
|
+
'data-play-off': !isPlaying ? '' : undefined,
|
|
132
|
+
'data-fullscreen-on': isFullscreen ? '' : undefined,
|
|
133
|
+
'data-fullscreen-off': !isFullscreen ? '' : undefined,
|
|
134
|
+
'data-loud': isLoud ? '' : undefined,
|
|
135
|
+
'data-muted': !isLoud ? '' : undefined,
|
|
136
|
+
'data-volume': appliedVolume.toFixed(8),
|
|
137
|
+
'data-volume-percent': volumePercent,
|
|
138
|
+
'data-playback-rate': playbackRate,
|
|
139
|
+
'data-current-time-ms': currentTimeMs.toFixed(2),
|
|
140
|
+
'data-current-time-ratio': (currentTime / totalTime).toFixed(8),
|
|
141
|
+
'data-total-time-ms': totalTimeMs
|
|
142
|
+
};
|
|
143
|
+
const rootStyles = {
|
|
144
|
+
[`--${publicClassName}-current-time-ratio`]: (currentTime / totalTime).toFixed(8)
|
|
145
|
+
};
|
|
146
|
+
const parsedSources = useMemo(() => {
|
|
147
|
+
if (sources === undefined)
|
|
148
|
+
return [];
|
|
149
|
+
if (typeof sources === 'string')
|
|
150
|
+
return [{ src: sources }];
|
|
151
|
+
if (Array.isArray(sources)) {
|
|
152
|
+
if (sources.length === 0)
|
|
153
|
+
return [];
|
|
154
|
+
if (typeof sources[0] === 'string')
|
|
155
|
+
return sources.map(src => ({ src }));
|
|
156
|
+
return sources;
|
|
157
|
+
}
|
|
158
|
+
return [];
|
|
159
|
+
}, [sources]);
|
|
160
|
+
const parsedTracks = useMemo(() => {
|
|
161
|
+
if (tracks === undefined)
|
|
162
|
+
return [];
|
|
163
|
+
if (typeof tracks === 'string')
|
|
164
|
+
return [{ src: tracks }];
|
|
165
|
+
if (Array.isArray(tracks)) {
|
|
166
|
+
if (tracks.length === 0)
|
|
167
|
+
return [];
|
|
168
|
+
if (typeof tracks[0] === 'string')
|
|
169
|
+
return tracks.map(src => ({ src }));
|
|
170
|
+
return tracks;
|
|
171
|
+
}
|
|
172
|
+
return [];
|
|
173
|
+
}, [tracks]);
|
|
174
|
+
const videoClss = c('video');
|
|
175
|
+
const videoControlsClss = c('video-controls');
|
|
176
|
+
const playBtnClss = c('play-btn');
|
|
177
|
+
const pauseBtnClss = c('pause-btn');
|
|
178
|
+
const loudBtnClss = c('loud-btn');
|
|
179
|
+
const muteBtnClss = c('mute-btn');
|
|
180
|
+
const volumePcntClss = c('volume-percent');
|
|
181
|
+
const fullscreenBtnClss = c('fullscreen-btn');
|
|
182
|
+
const volumeRangeClss = c('volume-range');
|
|
183
|
+
const playbackRateRangeClss = c('playback-rate-range');
|
|
184
|
+
const playbackRateClss = c('playback-rate');
|
|
185
|
+
const timeControlsClss = c('time-controls');
|
|
186
|
+
const currentTimeClss = c('current-time');
|
|
187
|
+
const totalTimeClss = c('total-time');
|
|
188
|
+
const timelineClss = c('timeline');
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
forcePlaybackRate(videoRef.current, playbackRate);
|
|
191
|
+
}, [playbackRate]);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (fullscreen === true) {
|
|
194
|
+
void forceFullscreen(videoRef.current);
|
|
195
|
+
}
|
|
196
|
+
return () => {
|
|
197
|
+
void forceExitFullscreen(videoRef.current);
|
|
198
|
+
};
|
|
199
|
+
}, [fullscreen]);
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
if (isTimeControlled)
|
|
202
|
+
return;
|
|
203
|
+
if (play === true) {
|
|
204
|
+
void forcePlay(videoRef.current);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
void forcePause(videoRef.current);
|
|
208
|
+
}
|
|
209
|
+
}, [play, isTimeControlled]);
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
if (volume !== undefined) {
|
|
212
|
+
forceVolume(videoRef.current, volume);
|
|
213
|
+
}
|
|
214
|
+
}, [volume]);
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
if (givenCurrentTimeMs !== undefined && videoRef.current !== null) {
|
|
217
|
+
void forcePause(videoRef.current);
|
|
218
|
+
videoRef.current.currentTime = msToSeconds(givenCurrentTimeMs);
|
|
219
|
+
}
|
|
220
|
+
}, [givenCurrentTimeMs]);
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
if (mute === true) {
|
|
223
|
+
forceMute(videoRef.current);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
forceLoud(videoRef.current);
|
|
227
|
+
}
|
|
228
|
+
}, [mute]);
|
|
229
|
+
// State handlers
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
if (stateHandlers?.currentTime !== undefined) {
|
|
232
|
+
stateHandlers.currentTime(msToSeconds(currentTimeMs));
|
|
233
|
+
}
|
|
234
|
+
}, [currentTimeMs, stateHandlers?.currentTime]);
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
stateHandlers?.isPlaying?.(isPlaying);
|
|
237
|
+
}, [isPlaying, stateHandlers?.isPlaying]);
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
stateHandlers?.isFullscreen?.(isFullscreen);
|
|
240
|
+
}, [isFullscreen, stateHandlers?.isFullscreen]);
|
|
241
|
+
useEffect(() => {
|
|
242
|
+
stateHandlers?.isLoud?.(isLoud);
|
|
243
|
+
}, [isLoud, stateHandlers?.isLoud]);
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
stateHandlers?.volume?.(volume);
|
|
246
|
+
}, [volume, stateHandlers?.volume]);
|
|
247
|
+
useEffect(() => {
|
|
248
|
+
stateHandlers?.playbackRate?.(playbackRate);
|
|
249
|
+
}, [playbackRate, stateHandlers?.playbackRate]);
|
|
250
|
+
return _jsxs("figure", { className: rootClss, style: rootStyles, ...rootAttributes, children: [_jsxs("video", { ref: videoRef, className: videoClss, ...intrinsicVideoAttributes, onLoadedMetadata: handleMetadataLoadEvent, onPlay: handleOnPlayEvent, onTimeUpdate: handleOnTimeUpdateEvent, children: [parsedSources.map((source, index) => typeof source === 'string'
|
|
251
|
+
? _jsx("source", { src: source }, index)
|
|
252
|
+
: _jsx("source", { src: source.src, type: source.type }, index)), parsedTracks.map((track, index) => typeof track === 'string'
|
|
253
|
+
? _jsx("track", { src: track }, index)
|
|
254
|
+
: _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: Math.round(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 })] });
|
|
255
|
+
};
|
|
@@ -1,83 +1,10 @@
|
|
|
1
|
-
import { type FunctionComponent
|
|
1
|
+
import { type FunctionComponent } from 'react';
|
|
2
2
|
import { type Props as DisclaimerProps } from '../Disclaimer/index.js';
|
|
3
|
-
import { type Props as
|
|
4
|
-
import type { WithClassName } from '../utils/types.js';
|
|
5
|
-
/**
|
|
6
|
-
* Describes a single video source.
|
|
7
|
-
*
|
|
8
|
-
* @property src - URL of the video file.
|
|
9
|
-
* @property type - MIME type of the source (e.g. `'video/mp4'`).
|
|
10
|
-
*/
|
|
11
|
-
type SourceData = {
|
|
12
|
-
src?: string;
|
|
13
|
-
type?: string;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Describes a single text track (subtitles, captions, chapters, etc.).
|
|
17
|
-
*
|
|
18
|
-
* @property src - URL of the track file.
|
|
19
|
-
* @property kind - Track type, maps directly to the `<track>` `kind` attribute.
|
|
20
|
-
* @property srclang - Language of the track content (e.g. `'fr'`, `'en'`).
|
|
21
|
-
* @property label - Human-readable label shown in the browser's track selector.
|
|
22
|
-
* @property default - When `true`, marks this track as the default selection.
|
|
23
|
-
*/
|
|
24
|
-
type TrackData = {
|
|
25
|
-
src?: string;
|
|
26
|
-
kind?: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata';
|
|
27
|
-
srclang?: string;
|
|
28
|
-
label?: string;
|
|
29
|
-
default?: boolean;
|
|
30
|
-
};
|
|
31
|
-
type ActionHandlersProps = {
|
|
32
|
-
playButtonClick?: (e: React.MouseEvent<HTMLButtonElement>, isPlaying: boolean, video: HTMLVideoElement | null) => void;
|
|
33
|
-
pauseButtonClick?: (e: React.MouseEvent<HTMLButtonElement>, isPlaying: boolean, video: HTMLVideoElement | null) => void;
|
|
34
|
-
loudButtonClick?: (e: React.MouseEvent<HTMLButtonElement>, isLoud: boolean, video: HTMLVideoElement | null) => void;
|
|
35
|
-
muteButtonClick?: (e: React.MouseEvent<HTMLButtonElement>, isLoud: boolean, video: HTMLVideoElement | null) => void;
|
|
36
|
-
volumeRangeChange?: (e: React.ChangeEvent<HTMLInputElement>, targetRangeVolume: number, volume: number, video: HTMLVideoElement | null) => void;
|
|
37
|
-
fullscreenButtonClick?: (e: React.MouseEvent<HTMLButtonElement>, isFullscreen: boolean, video: HTMLVideoElement | null) => void;
|
|
38
|
-
rateRangeChange?: (e: React.ChangeEvent<HTMLInputElement>, targetRangeRate: number, rate: number, video: HTMLVideoElement | null) => void;
|
|
39
|
-
timelineClick?: (e: React.MouseEvent<HTMLDivElement>, targetTimeSec: number, timeSec: number, video: HTMLVideoElement | null) => void;
|
|
40
|
-
};
|
|
41
|
-
type StateHandlersProps = {
|
|
42
|
-
isPlaying?: (isPlaying: boolean) => void;
|
|
43
|
-
isFullScreen?: (isFullScreen: boolean) => void;
|
|
44
|
-
isLoud?: (isLoud: boolean) => void;
|
|
45
|
-
volume?: (volume: number) => void;
|
|
46
|
-
currentTime?: (currentTime: number) => void;
|
|
47
|
-
playbackRate?: (rate: number) => void;
|
|
48
|
-
};
|
|
49
|
-
/**
|
|
50
|
-
* - play?: boolean
|
|
51
|
-
* - mute?: boolean
|
|
52
|
-
* - fullScreen?: boolean
|
|
53
|
-
* - volume?: number
|
|
54
|
-
* - playbackRate?: number
|
|
55
|
-
* - currentTimeMs:? number
|
|
56
|
-
* - PLUS TARD =
|
|
57
|
-
* - onPlay, onLoud, etc...
|
|
58
|
-
*/
|
|
3
|
+
import { type Props as ControlledProps } from './index.controlled.js';
|
|
59
4
|
/**
|
|
60
5
|
* Props for the {@link Video} component.
|
|
61
6
|
*
|
|
62
|
-
* Extends all
|
|
63
|
-
* video attribute (`autoPlay`, `muted`, `loop`, `poster`, etc.) can be passed
|
|
64
|
-
* and will be forwarded to the underlying `<video>` element.
|
|
65
|
-
*
|
|
66
|
-
* @property sources - One or more video sources. Accepts:
|
|
67
|
-
* - a single URL string,
|
|
68
|
-
* - an array of URL strings,
|
|
69
|
-
* - an array of {@link SourceData} objects for fine-grained `type` control.
|
|
70
|
-
* @property tracks - One or more text tracks. Accepts:
|
|
71
|
-
* - a single URL string,
|
|
72
|
-
* - an array of URL strings,
|
|
73
|
-
* - an array of {@link TrackData} objects.
|
|
74
|
-
* @property playBtnContent - Custom content for the play button.
|
|
75
|
-
* @property pauseBtnContent - Custom content for the pause button.
|
|
76
|
-
* @property loudBtnContent - Custom content for the unmute button.
|
|
77
|
-
* @property muteBtnContent - Custom content for the mute button.
|
|
78
|
-
* @property fullScreenBtnContent - Custom content for the fullScreen button.
|
|
79
|
-
* @property subtitles - Props forwarded to the internal {@link Subtitles} component.
|
|
80
|
-
* `timecodeMs` is injected automatically from the current playback position.
|
|
7
|
+
* Extends all ControlledVideo props except play, mute, fullscreen, volume, playbackRate, and their associated event handlers
|
|
81
8
|
* @property disclaimer - Props forwarded to the internal {@link Disclaimer} component.
|
|
82
9
|
* While the disclaimer is active, `autoPlay` and `muted` are suppressed on the
|
|
83
10
|
* underlying `<video>` element.
|
|
@@ -89,61 +16,30 @@ type StateHandlersProps = {
|
|
|
89
16
|
* component intersects the viewport, provided no disclaimer is active.
|
|
90
17
|
* @property autoMuteWhenHidden - When `true`, mutes the video whenever the component
|
|
91
18
|
* leaves the viewport.
|
|
19
|
+
* @property currentTimeMs - When provided, forces the video's current time to the given value (in milliseconds). Giving the currentTimeMs prop puts the component in a controlled state for the current time, meaning that the parent component is responsible for updating the current time. In this mode, user interactions that would normally change the current time (play, pause, timeline click) will not have an effect unless the parent component updates the currentTimeMs prop accordingly.
|
|
20
|
+
* @property wrapperClassName - Optional additional class name(s) applied to the root wrapper element.
|
|
21
|
+
* @property stateHandlers - Optional callbacks invoked whenever the corresponding
|
|
22
|
+
* state changes. Useful for synchronizing external state with the internal video state.
|
|
92
23
|
* @property className - Optional additional class name(s) applied to the root element.
|
|
93
24
|
* @property children - React children rendered inside the `<video>` element itself
|
|
94
25
|
* (e.g. fallback content).
|
|
95
26
|
*/
|
|
96
|
-
export type Props =
|
|
97
|
-
sources?: string | string[] | SourceData[];
|
|
98
|
-
tracks?: string | string[] | TrackData[];
|
|
99
|
-
playBtnContent?: React.ReactNode;
|
|
100
|
-
pauseBtnContent?: React.ReactNode;
|
|
101
|
-
loudBtnContent?: React.ReactNode;
|
|
102
|
-
muteBtnContent?: React.ReactNode;
|
|
103
|
-
fullScreenBtnContent?: React.ReactNode;
|
|
104
|
-
subtitles?: SubsProps;
|
|
27
|
+
export type Props = Omit<ControlledProps, 'play' | 'fullscreen' | 'volume' | 'mute' | 'playbackRate'> & {
|
|
105
28
|
disclaimer?: DisclaimerProps;
|
|
106
29
|
autoPlayWhenVisible?: boolean;
|
|
107
30
|
autoPauseWhenHidden?: boolean;
|
|
108
31
|
autoLoudWhenVisible?: boolean;
|
|
109
32
|
autoMuteWhenHidden?: boolean;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}> & VideoHTMLAttributes<HTMLVideoElement>>;
|
|
33
|
+
wrapperClassName?: string;
|
|
34
|
+
};
|
|
113
35
|
/**
|
|
114
36
|
* Full-featured video player component. Wraps a native `<video>` element with
|
|
115
37
|
* playback controls, volume, playback rate, a timeline, optional subtitles,
|
|
116
38
|
* an optional disclaimer gate, and viewport-driven auto-play/mute behaviours.
|
|
117
39
|
*
|
|
118
|
-
* ### Root element modifiers
|
|
119
|
-
* The root `<figure>` receives the public class name defined by `video` and
|
|
120
|
-
* the following BEM-style modifier classes:
|
|
121
|
-
* - `--play-on` / `--play-off` — reflects current playback state.
|
|
122
|
-
* - `--fullscreen-on` / `--fullscreen-off` — reflects fullscreen state.
|
|
123
|
-
* - `--loud` / `--muted` — reflects mute state.
|
|
124
|
-
*
|
|
125
|
-
* ### Data attributes on the root element
|
|
126
|
-
* - `data-play-on` — present (empty string) when playing.
|
|
127
|
-
* - `data-play-off` — present (empty string) when paused.
|
|
128
|
-
* - `data-fullscreen-on` — present (empty string) when in fullScreen.
|
|
129
|
-
* - `data-fullscreen-off` — present (empty string) when not in fullScreen.
|
|
130
|
-
* - `data-loud` — present (empty string) when unmuted.
|
|
131
|
-
* - `data-muted` — present (empty string) when muted.
|
|
132
|
-
* - `data-volume` — current volume as a `0–1` float.
|
|
133
|
-
* - `data-volume-percent` — current volume as a `0–100` float.
|
|
134
|
-
* - `data-playback-rate` — current playback rate (e.g. `1`, `1.5`).
|
|
135
|
-
* - `data-current-time-ms` — current time in milliseconds, fixed to 2 decimals.
|
|
136
|
-
* - `data-current-time-ratio` — current / total ratio, fixed to 8 decimals.
|
|
137
|
-
* - `data-total-time-ms` — total duration in milliseconds.
|
|
138
|
-
*
|
|
139
|
-
* ### CSS custom properties on the root element
|
|
140
|
-
* - `--video-current-time-ratio` — current / total ratio, fixed to 8 decimals.
|
|
141
|
-
* Useful for driving progress-bar animations purely in CSS.
|
|
142
|
-
*
|
|
143
40
|
* @param props - Component properties.
|
|
144
41
|
* @see {@link Props}
|
|
145
42
|
* @returns A `<figure>` element containing the video, its controls, optional
|
|
146
43
|
* subtitles, and an optional disclaimer overlay.
|
|
147
44
|
*/
|
|
148
45
|
export declare const Video: FunctionComponent<Props>;
|
|
149
|
-
export {};
|