@design-edito/tools 0.5.15 → 0.5.17
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/colors/index.d.ts +3 -3
- package/agnostic/colors/index.js +3 -3
- package/agnostic/css/index.d.ts +2 -2
- package/agnostic/css/index.js +2 -2
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +14 -14
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +14 -14
- package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +5 -5
- package/agnostic/html/hyper-json/smart-tags/isolated/index.js +5 -5
- package/agnostic/html/index.d.ts +4 -4
- package/agnostic/html/index.js +4 -4
- package/agnostic/index.d.ts +2 -2
- package/agnostic/index.js +2 -2
- package/agnostic/misc/index.d.ts +5 -5
- package/agnostic/misc/index.js +5 -5
- package/agnostic/numbers/index.d.ts +2 -2
- package/agnostic/numbers/index.js +2 -2
- package/agnostic/objects/index.d.ts +3 -3
- package/agnostic/objects/index.js +3 -3
- package/agnostic/strings/index.d.ts +3 -3
- package/agnostic/strings/index.js +3 -3
- package/agnostic/time/index.d.ts +3 -3
- package/agnostic/time/index.js +3 -3
- package/components/IntersectionObserver/index.d.ts +28 -3
- package/components/IntersectionObserver/index.js +51 -20
- package/components/Video/index.controlled.d.ts +17 -1
- package/components/Video/index.controlled.js +20 -5
- package/components/Video/index.d.ts +13 -3
- package/components/Video/index.js +31 -12
- package/components/index.d.ts +3 -3
- package/components/index.js +3 -3
- package/components/public-classnames.d.ts +0 -1
- package/components/public-classnames.js +0 -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/@aws-s3/storage/index.d.ts +1 -1
- package/node/@aws-s3/storage/index.js +1 -1
- package/node/@google-cloud/storage/file/index.d.ts +3 -3
- package/node/@google-cloud/storage/file/index.js +3 -3
- package/node/cloud-storage/operations/index.d.ts +2 -2
- package/node/cloud-storage/operations/index.js +2 -2
- package/node/files/index.d.ts +1 -1
- package/node/files/index.js +1 -1
- package/node/ftps/directory/index.d.ts +1 -1
- package/node/ftps/directory/index.js +1 -1
- package/node/ftps/file/index.d.ts +2 -2
- package/node/ftps/file/index.js +2 -2
- package/node/images/transform/operations/index.d.ts +4 -4
- package/node/images/transform/operations/index.js +4 -4
- package/node/index.d.ts +1 -1
- package/node/index.js +1 -1
- package/node/sftp/directory/index.d.ts +1 -1
- package/node/sftp/directory/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FunctionComponent, type PropsWithChildren, type VideoHTMLAttributes } from 'react';
|
|
1
|
+
import { type FunctionComponent, type PropsWithChildren, type Ref, type VideoHTMLAttributes } from 'react';
|
|
2
2
|
import type { WithClassName } from '../utils/types.js';
|
|
3
3
|
import { type Props as SubsProps } from '../Subtitles/index.js';
|
|
4
4
|
/**
|
|
@@ -66,6 +66,8 @@ type TrackData = {
|
|
|
66
66
|
* the component reacts, with the target and current rates.
|
|
67
67
|
* @property onFullscreenButtonClicked - Called when the fullscreen button is
|
|
68
68
|
* clicked, before the component reacts, with the fullscreen state as it was.
|
|
69
|
+
* @property onSubtitlesButtonClicked - Called when the subtitles button is clicked,
|
|
70
|
+
* before the component reacts, with the subtitles state as it was.
|
|
69
71
|
* @property onTimelineClicked - Called when the timeline is clicked, before the
|
|
70
72
|
* component reacts, with the target and current times (in seconds). The component
|
|
71
73
|
* seeks to the target right after, unless the time is controlled.
|
|
@@ -77,6 +79,8 @@ type TrackData = {
|
|
|
77
79
|
* @property onIsEndedChanged - Called after playback reached the end, and again
|
|
78
80
|
* once it left it — a seek back or a new play. Never on mount.
|
|
79
81
|
* @property onIsLoudChanged - Called once the mute state has changed (`true` = unmuted).
|
|
82
|
+
* @property onIsSubtitlesOnChanged - Called once the subtitles have been shown or
|
|
83
|
+
* hidden. Never on mount.
|
|
80
84
|
* @property onVolumeChanged - Called once the volume has changed (`0` to `1`).
|
|
81
85
|
* @property onPlaybackRateChanged - Called once the playback rate has changed.
|
|
82
86
|
* @property onCurrentTimeMsChanged - Called once the current time has changed, in
|
|
@@ -96,12 +100,20 @@ type TrackData = {
|
|
|
96
100
|
* gets, each taking its whole part and handing the rest down. So `'{{mm}}:{{ss}}'` on
|
|
97
101
|
* an hour-long video reads `62:05` rather than dropping the hour — nothing is ever
|
|
98
102
|
* lost for not having been asked for.
|
|
103
|
+
* @property subtitlesBtnContent - Glyph for the subtitles button, like the five other
|
|
104
|
+
* button contents. The button renders only when `subtitles` is set: with no cues there
|
|
105
|
+
* is nothing to toggle, and unlike `Disclaimer`'s missing toggler this withholds no
|
|
106
|
+
* behaviour — a stylesheet has nothing it could do with it either.
|
|
107
|
+
* @property subtitlesOn - Whether the subtitles show. Controlled: the component never
|
|
108
|
+
* changes it, it reports a click through `onSubtitlesButtonClicked` and leaves the
|
|
109
|
+
* decision to the parent. `Video` holds it for you.
|
|
99
110
|
* @property className - Additional CSS class for the root element.
|
|
100
111
|
* @property children - React content inserted into the <video> tag (fallback, etc).
|
|
101
112
|
*
|
|
102
113
|
* Also inherits all standard HTML props for a <video> element.
|
|
103
114
|
*/
|
|
104
115
|
export type Props = PropsWithChildren<WithClassName<{
|
|
116
|
+
rootRef?: Ref<HTMLElement>;
|
|
105
117
|
sources?: string | string[] | SourceData[];
|
|
106
118
|
tracks?: string | string[] | TrackData[];
|
|
107
119
|
subtitles?: SubsProps;
|
|
@@ -110,7 +122,9 @@ export type Props = PropsWithChildren<WithClassName<{
|
|
|
110
122
|
loudBtnContent?: React.ReactNode;
|
|
111
123
|
muteBtnContent?: React.ReactNode;
|
|
112
124
|
fullscreenBtnContent?: React.ReactNode;
|
|
125
|
+
subtitlesBtnContent?: React.ReactNode;
|
|
113
126
|
play?: boolean;
|
|
127
|
+
subtitlesOn?: boolean;
|
|
114
128
|
fullscreen?: boolean;
|
|
115
129
|
volume?: number;
|
|
116
130
|
mute?: boolean;
|
|
@@ -124,10 +138,12 @@ export type Props = PropsWithChildren<WithClassName<{
|
|
|
124
138
|
onVolumeRangeChanged?: (e: React.ChangeEvent<HTMLInputElement>, targetVolume: number, currentVolume: number, video: HTMLVideoElement | null) => void;
|
|
125
139
|
onRateRangeChanged?: (e: React.ChangeEvent<HTMLInputElement>, targetRate: number, currentRate: number, video: HTMLVideoElement | null) => void;
|
|
126
140
|
onFullscreenButtonClicked?: (e: React.MouseEvent<HTMLButtonElement>, isFullscreen: boolean, video: HTMLVideoElement | null) => void;
|
|
141
|
+
onSubtitlesButtonClicked?: (e: React.MouseEvent<HTMLButtonElement>, isSubtitlesOn: boolean, video: HTMLVideoElement | null) => void;
|
|
127
142
|
onTimelineClicked?: (e: React.MouseEvent<HTMLDivElement>, targetTime: number, currentTime: number, video: HTMLVideoElement | null) => void;
|
|
128
143
|
onIsPlayingChanged?: (isPlaying: boolean) => void;
|
|
129
144
|
onIsFullscreenChanged?: (isFullscreen: boolean) => void;
|
|
130
145
|
onIsLoudChanged?: (isLoud: boolean) => void;
|
|
146
|
+
onIsSubtitlesOnChanged?: (isSubtitlesOn: boolean) => void;
|
|
131
147
|
onIsEndedChanged?: (isEnded: boolean) => void;
|
|
132
148
|
onVolumeChanged?: (volume: number) => void;
|
|
133
149
|
onPlaybackRateChanged?: (playbackRate: number) => void;
|
|
@@ -50,11 +50,14 @@ import { forceExitFullscreen, forceFullscreen, forceLoud, forceMute, forcePause,
|
|
|
50
50
|
* @returns A `<figure>` element containing the video, its controls, optional
|
|
51
51
|
* subtitles.
|
|
52
52
|
*/
|
|
53
|
-
export const ControlledVideo = ({ sources, tracks, subtitles, playBtnContent, pauseBtnContent, loudBtnContent, muteBtnContent, fullscreenBtnContent, play, fullscreen,
|
|
53
|
+
export const ControlledVideo = ({ sources, tracks, subtitles, playBtnContent, pauseBtnContent, loudBtnContent, muteBtnContent, fullscreenBtnContent, subtitlesBtnContent, play, fullscreen,
|
|
54
|
+
// Les sous-titres sont montrés tant que personne n'a dit le contraire : un article qui
|
|
55
|
+
// en fournit veut qu'on les lise.
|
|
56
|
+
subtitlesOn = true, mute, muted, volume = 1, playbackRate = 1, currentTimeMs: givenCurrentTimeMs,
|
|
54
57
|
// Milliseconds by default, which is what this component has always rendered — a
|
|
55
58
|
// default that changes under a consumer is a change nobody asked for. An article
|
|
56
59
|
// usually wants `'{{mm}}:{{ss}}'` and says so.
|
|
57
|
-
timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked, onLoudButtonClicked, onMuteButtonClicked, onVolumeRangeChanged, onRateRangeChanged, onFullscreenButtonClicked, onTimelineClicked, onIsPlayingChanged, onIsFullscreenChanged, onIsLoudChanged, onIsEndedChanged, onVolumeChanged, onPlaybackRateChanged, onCurrentTimeMsChanged, onFullscreenChange, children, className, ...intrinsicVideoAttributes }) => {
|
|
60
|
+
timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked, onLoudButtonClicked, onMuteButtonClicked, onVolumeRangeChanged, onRateRangeChanged, onFullscreenButtonClicked, onSubtitlesButtonClicked, onTimelineClicked, onIsPlayingChanged, onIsFullscreenChanged, onIsLoudChanged, onIsSubtitlesOnChanged, onIsEndedChanged, onVolumeChanged, onPlaybackRateChanged, onCurrentTimeMsChanged, onFullscreenChange, children, className, rootRef, ...intrinsicVideoAttributes }) => {
|
|
58
61
|
const videoRef = useRef(null);
|
|
59
62
|
const [totalTime, setTotalTime] = useState(0);
|
|
60
63
|
const totalTimeMs = useMemo(() => secondsToMs(totalTime), [totalTime]);
|
|
@@ -131,6 +134,9 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
|
|
|
131
134
|
const handleFullscreenButtonClick = useCallback((e) => {
|
|
132
135
|
onFullscreenButtonClicked?.(e, isFullscreen, videoRef.current);
|
|
133
136
|
}, [onFullscreenButtonClicked, isFullscreen]);
|
|
137
|
+
const handleSubtitlesButtonClick = useCallback((e) => {
|
|
138
|
+
onSubtitlesButtonClicked?.(e, subtitlesOn, videoRef.current);
|
|
139
|
+
}, [onSubtitlesButtonClicked, subtitlesOn]);
|
|
134
140
|
const handleVolumeRangeChange = useCallback((e) => {
|
|
135
141
|
const targetVolume = Number(e.currentTarget.value) / 100;
|
|
136
142
|
onVolumeRangeChanged?.(e, targetVolume, volume, videoRef.current);
|
|
@@ -159,7 +165,12 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
|
|
|
159
165
|
'fullscreen-on': isFullscreen,
|
|
160
166
|
'fullscreen-off': !isFullscreen,
|
|
161
167
|
'loud': isLoud,
|
|
162
|
-
'muted': !isLoud
|
|
168
|
+
'muted': !isLoud,
|
|
169
|
+
// Rien des deux quand aucun sous-titre n'est fourni : l'absence n'est pas un état
|
|
170
|
+
// caché, c'est l'absence d'un état. Une feuille qui veut viser ce cas le reconnaît à
|
|
171
|
+
// ce qu'aucun des deux modifieurs n'est là.
|
|
172
|
+
'subtitles-on': subtitles !== undefined && subtitlesOn,
|
|
173
|
+
'subtitles-off': subtitles !== undefined && !subtitlesOn
|
|
163
174
|
}), className);
|
|
164
175
|
// Guarded: the duration is unknown until the metadata lands, and an unguarded
|
|
165
176
|
// division would expose the string 'NaN' on every render until then.
|
|
@@ -172,6 +183,8 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
|
|
|
172
183
|
'data-fullscreen-off': !isFullscreen ? '' : undefined,
|
|
173
184
|
'data-loud': isLoud ? '' : undefined,
|
|
174
185
|
'data-muted': !isLoud ? '' : undefined,
|
|
186
|
+
'data-subtitles-on': subtitles !== undefined && subtitlesOn ? '' : undefined,
|
|
187
|
+
'data-subtitles-off': subtitles !== undefined && !subtitlesOn ? '' : undefined,
|
|
175
188
|
'data-volume': volume.toFixed(8),
|
|
176
189
|
'data-volume-percent': volumePercent,
|
|
177
190
|
'data-playback-rate': playbackRate,
|
|
@@ -228,6 +241,7 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
|
|
|
228
241
|
const muteBtnClss = c('mute-btn');
|
|
229
242
|
const volumePcntClss = c('volume-percent');
|
|
230
243
|
const fullscreenBtnClss = c('fullscreen-btn');
|
|
244
|
+
const subtitlesBtnClss = c('subtitles-btn');
|
|
231
245
|
const volumeRangeClss = c('volume-range');
|
|
232
246
|
const playbackRateRangeClss = c('playback-rate-range');
|
|
233
247
|
const playbackRateClss = c('playback-rate');
|
|
@@ -300,12 +314,13 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
|
|
|
300
314
|
useChangeDispatch(isElementPlaying, onIsPlayingChanged);
|
|
301
315
|
useChangeDispatch(isFullscreen, onIsFullscreenChanged);
|
|
302
316
|
useChangeDispatch(isLoud, onIsLoudChanged);
|
|
317
|
+
useChangeDispatch(subtitlesOn, onIsSubtitlesOnChanged);
|
|
303
318
|
useChangeDispatch(isEnded, onIsEndedChanged);
|
|
304
319
|
useChangeDispatch(volume, onVolumeChanged);
|
|
305
320
|
useChangeDispatch(playbackRate, onPlaybackRateChanged);
|
|
306
|
-
return _jsxs("figure", { className: rootClss, style: rootStyles, ...rootAttributes, children: [_jsxs("video", { ref: videoRef, className: videoClss, ...intrinsicVideoAttributes, autoPlay: isTimeControlled ? false : intrinsicVideoAttributes.autoPlay, onLoadedMetadata: handleMetadataLoadEvent, onTimeUpdate: handleOnTimeUpdateEvent, onEnded: handleEndedEvent, onPlay: handlePlayEvent, onPause: handlePauseEvent, children: [parsedSources.map((source, index) => typeof source === 'string'
|
|
321
|
+
return _jsxs("figure", { ref: rootRef, className: rootClss, style: rootStyles, ...rootAttributes, children: [_jsxs("video", { ref: videoRef, className: videoClss, ...intrinsicVideoAttributes, autoPlay: isTimeControlled ? false : intrinsicVideoAttributes.autoPlay, onLoadedMetadata: handleMetadataLoadEvent, onTimeUpdate: handleOnTimeUpdateEvent, onEnded: handleEndedEvent, onPlay: handlePlayEvent, onPause: handlePauseEvent, children: [parsedSources.map((source, index) => typeof source === 'string'
|
|
307
322
|
? _jsx("source", { src: source }, index)
|
|
308
323
|
: _jsx("source", { src: source.src, type: source.type }, index)), parsedTracks.map((track, index) => typeof track === 'string'
|
|
309
324
|
? _jsx("track", { src: track }, index)
|
|
310
|
-
: _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: formatDuration(currentTimeMs, timeFormat) }), _jsx("span", { className: totalTimeClss, children: formatDuration(totalTimeMs, timeFormat) }), _jsx("div", { className: timelineClss, onClick: handleTimelineClick })] }), subtitles !== undefined && _jsx(Subtitles, { ...subtitles, timecodeMs: currentTimeMs, isEnded: isEnded })] });
|
|
325
|
+
: _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 }), subtitles !== undefined && _jsx("button", { className: subtitlesBtnClss, onClick: handleSubtitlesButtonClick, children: subtitlesBtnContent }), _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: formatDuration(currentTimeMs, timeFormat) }), _jsx("span", { className: totalTimeClss, children: formatDuration(totalTimeMs, timeFormat) }), _jsx("div", { className: timelineClss, onClick: handleTimelineClick })] }), subtitles !== undefined && _jsx(Subtitles, { ...subtitles, timecodeMs: currentTimeMs, isEnded: isEnded })] });
|
|
311
326
|
};
|
|
@@ -30,12 +30,16 @@ import { type Props as ControlledProps } from './index.controlled.js';
|
|
|
30
30
|
* stopped video, since a playing element would advance a value it does not own:
|
|
31
31
|
* `autoPlay`, `autoPlayWhenVisible` and the play button have no effect for as
|
|
32
32
|
* long as this prop is provided.
|
|
33
|
-
* @property
|
|
33
|
+
* @property defaultSubtitlesOn - Whether the subtitles start shown. `true` by default —
|
|
34
|
+
* an article that supplies cues means them to be read. The reader's button takes it from
|
|
35
|
+
* there, so this is a starting point and not a setting: `default…` and never `initial…`,
|
|
36
|
+
* as everywhere else here.
|
|
34
37
|
* @property className - Optional additional class name(s) applied to the root element.
|
|
35
38
|
* @property children - React children rendered inside the `<video>` element itself
|
|
36
39
|
* (e.g. fallback content).
|
|
37
40
|
*/
|
|
38
|
-
export type Props = WithViewportObservation<Omit<ControlledProps, 'play' | 'fullscreen' | 'volume' | 'mute' | 'playbackRate'>> & {
|
|
41
|
+
export type Props = WithViewportObservation<Omit<ControlledProps, 'play' | 'fullscreen' | 'volume' | 'mute' | 'playbackRate' | 'subtitlesOn' | 'rootRef'>> & {
|
|
42
|
+
defaultSubtitlesOn?: boolean;
|
|
39
43
|
autoPlayWhenVisible?: boolean;
|
|
40
44
|
autoPlayOnceVisible?: boolean;
|
|
41
45
|
autoPauseWhenHidden?: boolean;
|
|
@@ -44,7 +48,6 @@ export type Props = WithViewportObservation<Omit<ControlledProps, 'play' | 'full
|
|
|
44
48
|
autoLoudOnceVisible?: boolean;
|
|
45
49
|
autoMuteWhenHidden?: boolean;
|
|
46
50
|
autoMuteOnceHidden?: boolean;
|
|
47
|
-
wrapperClassName?: string;
|
|
48
51
|
};
|
|
49
52
|
/**
|
|
50
53
|
* Full-featured video player component. Wraps a native `<video>` element with
|
|
@@ -73,6 +76,13 @@ export type Props = WithViewportObservation<Omit<ControlledProps, 'play' | 'full
|
|
|
73
76
|
* automatic play the component still owed. Setting both flavours of the same
|
|
74
77
|
* behaviour is the same as setting the `…When…` one alone.
|
|
75
78
|
*
|
|
79
|
+
* **`loop` reaches the element untouched**, like every other native media attribute
|
|
80
|
+
* this component does not drive itself. It was once destructured out of the props and
|
|
81
|
+
* never forwarded, which silently dropped it — so if a native attribute ever stops
|
|
82
|
+
* arriving, look at the destructuring here before looking at the caller. One
|
|
83
|
+
* consequence worth knowing: a looping element never fires `ended`, so the `--ended`
|
|
84
|
+
* modifier and the `isEnded` it feeds to `Subtitles` simply never come.
|
|
85
|
+
*
|
|
76
86
|
* Browsers refuse an unmuted `play()` outside a user gesture, so pairing an
|
|
77
87
|
* `autoLoud…` with an `autoPlay…` will usually have the playback rejected. The
|
|
78
88
|
* refusal is caught rather than ignored — the element is read back once the attempt
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { IntersectionObserverComponent } from '../IntersectionObserver/index.js';
|
|
5
|
-
import { mergeClassNames } from '../utils/index.js';
|
|
6
|
-
import { videoWrapper as publicClassName } from '../public-classnames.js';
|
|
3
|
+
import { useIntersectionObserver } from '../IntersectionObserver/index.js';
|
|
7
4
|
import { muteAttributeWorkaround, shouldRunAutoBehaviour } from './utils.js';
|
|
8
|
-
import cssModule from './styles.module.css';
|
|
9
5
|
import { ControlledVideo } from './index.controlled.js';
|
|
10
6
|
/**
|
|
11
7
|
* Full-featured video player component. Wraps a native `<video>` element with
|
|
@@ -34,6 +30,13 @@ import { ControlledVideo } from './index.controlled.js';
|
|
|
34
30
|
* automatic play the component still owed. Setting both flavours of the same
|
|
35
31
|
* behaviour is the same as setting the `…When…` one alone.
|
|
36
32
|
*
|
|
33
|
+
* **`loop` reaches the element untouched**, like every other native media attribute
|
|
34
|
+
* this component does not drive itself. It was once destructured out of the props and
|
|
35
|
+
* never forwarded, which silently dropped it — so if a native attribute ever stops
|
|
36
|
+
* arriving, look at the destructuring here before looking at the caller. One
|
|
37
|
+
* consequence worth knowing: a looping element never fires `ended`, so the `--ended`
|
|
38
|
+
* modifier and the `isEnded` it feeds to `Subtitles` simply never come.
|
|
39
|
+
*
|
|
37
40
|
* Browsers refuse an unmuted `play()` outside a user gesture, so pairing an
|
|
38
41
|
* `autoLoud…` with an `autoPlay…` will usually have the playback rejected. The
|
|
39
42
|
* refusal is caught rather than ignored — the element is read back once the attempt
|
|
@@ -41,13 +44,14 @@ import { ControlledVideo } from './index.controlled.js';
|
|
|
41
44
|
* The media still won't play, though: autoplay muted, and leave unmuting to the
|
|
42
45
|
* reader.
|
|
43
46
|
*/
|
|
44
|
-
export const Video = ({
|
|
47
|
+
export const Video = ({ defaultSubtitlesOn = true, autoPlayWhenVisible, autoPlayOnceVisible, autoPauseWhenHidden, autoPauseOnceHidden, autoMuteWhenHidden, autoMuteOnceHidden, autoLoudWhenVisible, autoLoudOnceVisible, threshold, root, rootMargin, onVisibilityChanged, onPlayButtonClicked, onPauseButtonClicked, onLoudButtonClicked, onMuteButtonClicked, onVolumeRangeChanged, onRateRangeChanged, onFullscreenButtonClicked, onSubtitlesButtonClicked, ...controlledProps }) => {
|
|
45
48
|
// State & refs
|
|
46
49
|
const [play, setPlay] = useState(false);
|
|
47
50
|
const [volume, setVolume] = useState(1);
|
|
48
51
|
const [mute, setMute] = useState(false);
|
|
49
52
|
const [playbackRate, setPlaybackRate] = useState(1);
|
|
50
53
|
const [fullscreen, setFullscreen] = useState(false);
|
|
54
|
+
const [subtitlesOn, setSubtitlesOn] = useState(defaultSubtitlesOn);
|
|
51
55
|
// One flag per `…Once…` behaviour, and each is armed by that behaviour's own
|
|
52
56
|
// automatic trigger. A single shared flag conflated four questions, and being set
|
|
53
57
|
// on any `play` event — a user click included — meant the first press cancelled
|
|
@@ -61,6 +65,7 @@ export const Video = ({ loop, autoPlayWhenVisible, autoPlayOnceVisible, autoPaus
|
|
|
61
65
|
// invariant is applied where the state is forwarded rather than guarded at each
|
|
62
66
|
// of those call sites.
|
|
63
67
|
const isTimeControlled = controlledProps.currentTimeMs !== undefined;
|
|
68
|
+
const rootRef = useRef(null);
|
|
64
69
|
const needsObserve = useMemo(() => onVisibilityChanged !== undefined
|
|
65
70
|
|| autoLoudWhenVisible === true
|
|
66
71
|
|| autoLoudOnceVisible === true
|
|
@@ -143,6 +148,14 @@ export const Video = ({ loop, autoPlayWhenVisible, autoPlayOnceVisible, autoPaus
|
|
|
143
148
|
onFullscreenButtonClicked?.(e, isFullscreen, video);
|
|
144
149
|
setFullscreen(!isFullscreen);
|
|
145
150
|
}, [onFullscreenButtonClicked]);
|
|
151
|
+
// Un seul bouton pour les deux sens, contrairement à lecture/pause et son/muet qui en
|
|
152
|
+
// ont un chacun : ceux-là existent en paire dans le DOM et la feuille en montre un,
|
|
153
|
+
// parce qu'ils portent deux glyphes opposés. Les sous-titres n'en portent qu'un, dont
|
|
154
|
+
// seul l'habillage change — inutile d'en rendre deux pour n'en montrer jamais qu'un.
|
|
155
|
+
const handleSubtitlesButtonClick = useCallback((e, isSubtitlesOn, video) => {
|
|
156
|
+
onSubtitlesButtonClicked?.(e, isSubtitlesOn, video);
|
|
157
|
+
setSubtitlesOn(!isSubtitlesOn);
|
|
158
|
+
}, [onSubtitlesButtonClicked]);
|
|
146
159
|
// Intersection Observer
|
|
147
160
|
const onIntersected = useCallback(({ ioEntry }) => {
|
|
148
161
|
if (ioEntry === undefined)
|
|
@@ -185,11 +198,17 @@ export const Video = ({ loop, autoPlayWhenVisible, autoPlayOnceVisible, autoPaus
|
|
|
185
198
|
if (controlledProps.autoPlay === true)
|
|
186
199
|
setPlay(true);
|
|
187
200
|
}, []);
|
|
201
|
+
// The observer watches the `<figure>` itself. `needsObserve` says whether anything
|
|
202
|
+
// asked for it — a hook cannot be skipped, so the condition is passed in rather than
|
|
203
|
+
// wrapped around the call, and no `IntersectionObserver` is created without a reason
|
|
204
|
+
// to create one.
|
|
205
|
+
useIntersectionObserver(rootRef, { threshold, root, rootMargin }, onIntersected, needsObserve);
|
|
188
206
|
// Render
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
207
|
+
//
|
|
208
|
+
// **The `<figure>` is the root, and there is nothing above it.** The observer runs on
|
|
209
|
+
// that element rather than on a box built to hold it: a wrapper would take the outer
|
|
210
|
+
// position — the one a consumer lays out — and leave `className` naming an inner
|
|
211
|
+
// element, which is the wrong way round. So no wrapper, and no second class-name prop
|
|
212
|
+
// to reach past it.
|
|
213
|
+
return _jsx(ControlledVideo, { ...controlledProps, rootRef: rootRef, play: play && !isTimeControlled, volume: volume, mute: mute, playbackRate: playbackRate, fullscreen: fullscreen, subtitlesOn: subtitlesOn, onPlay: handleOnPlayEvent, onPause: handleOnPauseEvent, onIsPlayingChanged: handleIsPlayingChanged, onVolumeChange: handleOnVolumeChangeEvent, onRateChange: handleOnRateChangeEvent, onLoadedMetadata: handleOnLoadedMetadataEvent, onFullscreenChange: handleFullscreenChange, onPlayButtonClicked: handlePlayButtonClick, onPauseButtonClicked: handlePauseButtonClick, onLoudButtonClicked: handleLoudButtonClick, onMuteButtonClicked: handleMuteButtonClick, onVolumeRangeChanged: handleVolumeRangeChange, onRateRangeChanged: handleRateRangeChange, onFullscreenButtonClicked: handleFullscreenButtonClick, onSubtitlesButtonClicked: handleSubtitlesButtonClick });
|
|
195
214
|
};
|
package/components/index.d.ts
CHANGED
|
@@ -3,13 +3,13 @@ export * as button from './Button/index.js'
|
|
|
3
3
|
export * as clippable from './Clippable/index.js'
|
|
4
4
|
export * as disclaimer from './Disclaimer/index.js'
|
|
5
5
|
export * as drawer from './Drawer/index.js'
|
|
6
|
-
export * as eventListener from './EventListener/index.js'
|
|
7
6
|
export * as gallery from './Gallery/index.js'
|
|
7
|
+
export * as eventListener from './EventListener/index.js'
|
|
8
8
|
export * as iframe from './Iframe/index.js'
|
|
9
9
|
export * as image from './Image/index.js'
|
|
10
10
|
export * as input from './Input/index.js'
|
|
11
|
-
export * as jsonEditor from './JsonEditor/index.js'
|
|
12
11
|
export * as intersectionObserver from './IntersectionObserver/index.js'
|
|
12
|
+
export * as jsonEditor from './JsonEditor/index.js'
|
|
13
13
|
export * as lightbox from './Lightbox/index.js'
|
|
14
14
|
export * as listLoader from './ListLoader/index.js'
|
|
15
15
|
export * as overlayer from './Overlayer/index.js'
|
|
@@ -17,8 +17,8 @@ export * as paginator from './Paginator/index.js'
|
|
|
17
17
|
export * as resizeObserver from './ResizeObserver/index.js'
|
|
18
18
|
export * as scrllgngn from './Scrllgngn/index.js'
|
|
19
19
|
export * as scrollListener from './ScrollListener/index.js'
|
|
20
|
-
export * as sequencer from './Sequencer/index.js'
|
|
21
20
|
export * as select from './Select/index.js'
|
|
21
|
+
export * as sequencer from './Sequencer/index.js'
|
|
22
22
|
export * as shadowRoot from './ShadowRoot/index.js'
|
|
23
23
|
export * as subtitles from './Subtitles/index.js'
|
|
24
24
|
export * as textarea from './Textarea/index.js'
|
package/components/index.js
CHANGED
|
@@ -3,13 +3,13 @@ export * as button from './Button/index.js'
|
|
|
3
3
|
export * as clippable from './Clippable/index.js'
|
|
4
4
|
export * as disclaimer from './Disclaimer/index.js'
|
|
5
5
|
export * as drawer from './Drawer/index.js'
|
|
6
|
-
export * as eventListener from './EventListener/index.js'
|
|
7
6
|
export * as gallery from './Gallery/index.js'
|
|
7
|
+
export * as eventListener from './EventListener/index.js'
|
|
8
8
|
export * as iframe from './Iframe/index.js'
|
|
9
9
|
export * as image from './Image/index.js'
|
|
10
10
|
export * as input from './Input/index.js'
|
|
11
|
-
export * as jsonEditor from './JsonEditor/index.js'
|
|
12
11
|
export * as intersectionObserver from './IntersectionObserver/index.js'
|
|
12
|
+
export * as jsonEditor from './JsonEditor/index.js'
|
|
13
13
|
export * as lightbox from './Lightbox/index.js'
|
|
14
14
|
export * as listLoader from './ListLoader/index.js'
|
|
15
15
|
export * as overlayer from './Overlayer/index.js'
|
|
@@ -17,8 +17,8 @@ export * as paginator from './Paginator/index.js'
|
|
|
17
17
|
export * as resizeObserver from './ResizeObserver/index.js'
|
|
18
18
|
export * as scrllgngn from './Scrllgngn/index.js'
|
|
19
19
|
export * as scrollListener from './ScrollListener/index.js'
|
|
20
|
-
export * as sequencer from './Sequencer/index.js'
|
|
21
20
|
export * as select from './Select/index.js'
|
|
21
|
+
export * as sequencer from './Sequencer/index.js'
|
|
22
22
|
export * as shadowRoot from './ShadowRoot/index.js'
|
|
23
23
|
export * as subtitles from './Subtitles/index.js'
|
|
24
24
|
export * as textarea from './Textarea/index.js'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * as download from './download/index.js'
|
|
2
1
|
export * as copy from './copy/index.js'
|
|
2
|
+
export * as download from './download/index.js'
|
|
3
3
|
export * as exists from './exists/index.js'
|
|
4
4
|
export * as move from './move/index.js'
|
|
5
5
|
export * as remove from './remove/index.js'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * as download from './download/index.js'
|
|
2
1
|
export * as copy from './copy/index.js'
|
|
2
|
+
export * as download from './download/index.js'
|
|
3
3
|
export * as exists from './exists/index.js'
|
|
4
4
|
export * as move from './move/index.js'
|
|
5
5
|
export * as remove from './remove/index.js'
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export * as copy from './copy/index.js'
|
|
2
2
|
export * as download from './download/index.js'
|
|
3
|
-
export * as generateSignedUrl from './generate-signed-url/index.js'
|
|
4
3
|
export * as exists from './exists/index.js'
|
|
5
|
-
export * as
|
|
4
|
+
export * as generateSignedUrl from './generate-signed-url/index.js'
|
|
6
5
|
export * as getMetadata from './get-metadata/index.js'
|
|
7
|
-
export * as
|
|
6
|
+
export * as getPermissions from './get-permissions/index.js'
|
|
8
7
|
export * as move from './move/index.js'
|
|
8
|
+
export * as remove from './remove/index.js'
|
|
9
9
|
export * as revokeSignedUrls from './revoke-signed-urls/index.js'
|
|
10
10
|
export * as stat from './stat/index.js'
|
|
11
11
|
export * as updateMetadata from './update-metadata/index.js'
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export * as copy from './copy/index.js'
|
|
2
2
|
export * as download from './download/index.js'
|
|
3
|
-
export * as generateSignedUrl from './generate-signed-url/index.js'
|
|
4
3
|
export * as exists from './exists/index.js'
|
|
5
|
-
export * as
|
|
4
|
+
export * as generateSignedUrl from './generate-signed-url/index.js'
|
|
6
5
|
export * as getMetadata from './get-metadata/index.js'
|
|
7
|
-
export * as
|
|
6
|
+
export * as getPermissions from './get-permissions/index.js'
|
|
8
7
|
export * as move from './move/index.js'
|
|
8
|
+
export * as remove from './remove/index.js'
|
|
9
9
|
export * as revokeSignedUrls from './revoke-signed-urls/index.js'
|
|
10
10
|
export * as stat from './stat/index.js'
|
|
11
11
|
export * as updateMetadata from './update-metadata/index.js'
|
|
@@ -2,10 +2,10 @@ export * as copyDir from './copy-dir/index.js'
|
|
|
2
2
|
export * as copyFile from './copy-file/index.js'
|
|
3
3
|
export * as downloadFile from './download-file/index.js'
|
|
4
4
|
export * as existsFile from './exists-file/index.js'
|
|
5
|
-
export * as moveDir from './move-dir/index.js'
|
|
6
5
|
export * as listDir from './list-dir/index.js'
|
|
6
|
+
export * as moveDir from './move-dir/index.js'
|
|
7
7
|
export * as moveFile from './move-file/index.js'
|
|
8
8
|
export * as removeDir from './remove-dir/index.js'
|
|
9
|
+
export * as removeFile from './remove-file/index.js'
|
|
9
10
|
export * as statFile from './stat-file/index.js'
|
|
10
11
|
export * as uploadFile from './upload-file/index.js'
|
|
11
|
-
export * as removeFile from './remove-file/index.js'
|
|
@@ -2,10 +2,10 @@ export * as copyDir from './copy-dir/index.js'
|
|
|
2
2
|
export * as copyFile from './copy-file/index.js'
|
|
3
3
|
export * as downloadFile from './download-file/index.js'
|
|
4
4
|
export * as existsFile from './exists-file/index.js'
|
|
5
|
-
export * as moveDir from './move-dir/index.js'
|
|
6
5
|
export * as listDir from './list-dir/index.js'
|
|
6
|
+
export * as moveDir from './move-dir/index.js'
|
|
7
7
|
export * as moveFile from './move-file/index.js'
|
|
8
8
|
export * as removeDir from './remove-dir/index.js'
|
|
9
|
+
export * as removeFile from './remove-file/index.js'
|
|
9
10
|
export * as statFile from './stat-file/index.js'
|
|
10
11
|
export * as uploadFile from './upload-file/index.js'
|
|
11
|
-
export * as removeFile from './remove-file/index.js'
|
package/node/files/index.d.ts
CHANGED
package/node/files/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * as download from './download/index.js'
|
|
2
1
|
export * as copy from './copy/index.js'
|
|
2
|
+
export * as download from './download/index.js'
|
|
3
3
|
export * as exists from './exists/index.js'
|
|
4
|
+
export * as move from './move/index.js'
|
|
4
5
|
export * as remove from './remove/index.js'
|
|
5
6
|
export * as stat from './stat/index.js'
|
|
6
|
-
export * as move from './move/index.js'
|
|
7
7
|
export * as upload from './upload/index.js'
|
package/node/ftps/file/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * as download from './download/index.js'
|
|
2
1
|
export * as copy from './copy/index.js'
|
|
2
|
+
export * as download from './download/index.js'
|
|
3
3
|
export * as exists from './exists/index.js'
|
|
4
|
+
export * as move from './move/index.js'
|
|
4
5
|
export * as remove from './remove/index.js'
|
|
5
6
|
export * as stat from './stat/index.js'
|
|
6
|
-
export * as move from './move/index.js'
|
|
7
7
|
export * as upload from './upload/index.js'
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export * as blur from './blur/index.js'
|
|
2
|
+
export * as brighten from './brighten/index.js'
|
|
2
3
|
export * as extend from './extend/index.js'
|
|
3
4
|
export * as extract from './extract/index.js'
|
|
4
|
-
export * as brighten from './brighten/index.js'
|
|
5
5
|
export * as flatten from './flatten/index.js'
|
|
6
6
|
export * as flip from './flip/index.js'
|
|
7
7
|
export * as flop from './flop/index.js'
|
|
8
|
+
export * as hue from './hue/index.js'
|
|
8
9
|
export * as level from './level/index.js'
|
|
9
10
|
export * as lighten from './lighten/index.js'
|
|
10
|
-
export * as hue from './hue/index.js'
|
|
11
|
-
export * as overlay from './overlay/index.js'
|
|
12
11
|
export * as normalize from './normalize/index.js'
|
|
12
|
+
export * as overlay from './overlay/index.js'
|
|
13
13
|
export * as resize from './resize/index.js'
|
|
14
|
-
export * as saturate from './saturate/index.js'
|
|
15
14
|
export * as rotate from './rotate/index.js'
|
|
15
|
+
export * as saturate from './saturate/index.js'
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export * as blur from './blur/index.js'
|
|
2
|
+
export * as brighten from './brighten/index.js'
|
|
2
3
|
export * as extend from './extend/index.js'
|
|
3
4
|
export * as extract from './extract/index.js'
|
|
4
|
-
export * as brighten from './brighten/index.js'
|
|
5
5
|
export * as flatten from './flatten/index.js'
|
|
6
6
|
export * as flip from './flip/index.js'
|
|
7
7
|
export * as flop from './flop/index.js'
|
|
8
|
+
export * as hue from './hue/index.js'
|
|
8
9
|
export * as level from './level/index.js'
|
|
9
10
|
export * as lighten from './lighten/index.js'
|
|
10
|
-
export * as hue from './hue/index.js'
|
|
11
|
-
export * as overlay from './overlay/index.js'
|
|
12
11
|
export * as normalize from './normalize/index.js'
|
|
12
|
+
export * as overlay from './overlay/index.js'
|
|
13
13
|
export * as resize from './resize/index.js'
|
|
14
|
-
export * as saturate from './saturate/index.js'
|
|
15
14
|
export * as rotate from './rotate/index.js'
|
|
15
|
+
export * as saturate from './saturate/index.js'
|
package/node/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export * as awsS3 from './@aws-s3/index.js'
|
|
2
2
|
export * as designEdito from './@design-edito/index.js'
|
|
3
3
|
export * as express from './@express/index.js'
|
|
4
|
-
export * as cloudStorage from './cloud-storage/index.js'
|
|
5
4
|
export * as googleCloud from './@google-cloud/index.js'
|
|
5
|
+
export * as cloudStorage from './cloud-storage/index.js'
|
|
6
6
|
export * as encryption from './encryption/index.js'
|
|
7
7
|
export * as files from './files/index.js'
|
|
8
8
|
export * as ftps from './ftps/index.js'
|
package/node/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export * as awsS3 from './@aws-s3/index.js'
|
|
2
2
|
export * as designEdito from './@design-edito/index.js'
|
|
3
3
|
export * as express from './@express/index.js'
|
|
4
|
-
export * as cloudStorage from './cloud-storage/index.js'
|
|
5
4
|
export * as googleCloud from './@google-cloud/index.js'
|
|
5
|
+
export * as cloudStorage from './cloud-storage/index.js'
|
|
6
6
|
export * as encryption from './encryption/index.js'
|
|
7
7
|
export * as files from './files/index.js'
|
|
8
8
|
export * as ftps from './ftps/index.js'
|