@design-edito/tools 0.5.38 → 0.5.39
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/@react/effects/index.d.ts +1 -0
- package/agnostic/@react/effects/index.js +1 -0
- package/agnostic/@react/effects/use-state-change-dispatch/index.d.ts +22 -0
- package/agnostic/@react/effects/use-state-change-dispatch/index.js +31 -0
- package/agnostic/@react/index.d.ts +1 -0
- package/agnostic/@react/index.js +1 -0
- package/agnostic/arrays/index.d.ts +2 -1
- package/agnostic/arrays/index.js +2 -1
- package/agnostic/arrays/integers-between/index.d.ts +8 -0
- package/agnostic/arrays/integers-between/index.js +11 -0
- package/agnostic/arrays/integers-between/index.test.d.ts +1 -0
- package/agnostic/arrays/integers-between/index.test.js +16 -0
- package/agnostic/colors/index.d.ts +1 -1
- package/agnostic/colors/index.js +1 -1
- package/agnostic/css/index.d.ts +1 -0
- package/agnostic/css/index.js +1 -0
- package/agnostic/css/merge-class-names/index.d.ts +8 -0
- package/agnostic/css/merge-class-names/index.js +23 -0
- package/agnostic/css/merge-class-names/index.test.d.ts +1 -0
- package/agnostic/css/merge-class-names/index.test.js +52 -0
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +9 -9
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +9 -9
- 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 +2 -2
- package/agnostic/html/index.js +2 -2
- package/agnostic/index.d.ts +1 -0
- package/agnostic/index.js +1 -0
- package/agnostic/misc/index.d.ts +1 -1
- package/agnostic/misc/index.js +1 -1
- package/agnostic/numbers/index.d.ts +1 -1
- package/agnostic/numbers/index.js +1 -1
- package/agnostic/objects/index.d.ts +1 -1
- package/agnostic/objects/index.js +1 -1
- package/agnostic/time/index.d.ts +1 -1
- package/agnostic/time/index.js +1 -1
- package/components/BeforeAfter/index.controlled.js +1 -1
- package/components/BeforeAfter/index.js +2 -2
- package/components/Button/index.js +1 -1
- package/components/Clippable/index.js +1 -1
- package/components/Disclaimer/index.d.ts +2 -2
- package/components/Disclaimer/index.js +4 -3
- package/components/Drawer/index.js +4 -3
- package/components/EventListener/index.js +1 -1
- package/components/Gallery/index.js +5 -4
- package/components/Iframe/index.js +1 -1
- package/components/Image/index.d.ts +1 -1
- package/components/Image/index.js +3 -2
- package/components/Input/index.js +1 -1
- package/components/IntersectionObserver/index.js +1 -1
- package/components/JsonEditor/index.js +1 -1
- package/components/JsonEditor/types.d.ts +0 -6
- package/components/JsonEditor/utils.d.ts +0 -5
- package/components/JsonEditor/utils.js +0 -5
- package/components/Lightbox/index.d.ts +12 -12
- package/components/Lightbox/index.js +37 -35
- package/components/Lightbox/store.d.ts +9 -43
- package/components/Lightbox/store.js +51 -73
- package/components/Lightbox/types.d.ts +16 -0
- package/components/Lightbox/types.js +1 -0
- package/components/ListLoader/index.controlled.js +19 -13
- package/components/ListLoader/index.js +19 -3
- package/components/Overlayer/index.js +1 -1
- package/components/Paginator/index.js +4 -3
- package/components/ResizeObserver/index.js +1 -1
- package/components/Scrllgngn/index.d.ts +4 -4
- package/components/Scrllgngn/index.js +4 -3
- package/components/Scrllgngn/types.d.ts +55 -0
- package/components/Scrllgngn/types.js +24 -0
- package/components/Scrllgngn/utils.d.ts +2 -38
- package/components/ScrollListener/index.d.ts +1 -6
- package/components/ScrollListener/index.js +10 -9
- package/components/ScrollListener/types.d.ts +0 -3
- package/components/Select/index.js +1 -1
- package/components/Sequencer/index.d.ts +2 -9
- package/components/Sequencer/index.js +26 -128
- package/components/Sequencer/types.d.ts +30 -0
- package/components/Sequencer/types.js +30 -0
- package/components/Sequencer/utils.d.ts +4 -0
- package/components/Sequencer/utils.js +25 -0
- package/components/ShadowRoot/index.js +1 -1
- package/components/Subtitles/index.js +3 -5
- package/components/Subtitles/utils.d.ts +0 -51
- package/components/Subtitles/utils.js +0 -41
- package/components/Textarea/index.js +1 -1
- package/components/UIModule/index.js +5 -4
- package/components/Video/index.controlled.d.ts +1 -7
- package/components/Video/index.controlled.js +13 -12
- package/components/Video/index.d.ts +0 -8
- package/components/Video/index.js +11 -15
- package/components/Video/types.d.ts +33 -0
- package/components/Video/utils.d.ts +1 -67
- package/components/Video/utils.js +25 -44
- package/components/index.d.ts +2 -2
- package/components/index.js +2 -2
- package/components/utils/index.d.ts +1 -44
- package/components/utils/index.js +1 -83
- package/components/utils/viewport-behaviours/index.d.ts +8 -14
- package/components/utils/viewport-behaviours/index.js +15 -37
- package/components/utils/viewport-behaviours/types.d.ts +3 -9
- package/components/utils/viewport-behaviours/types.js +3 -4
- 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/cloud-storage/operations/index.d.ts +1 -1
- package/node/cloud-storage/operations/index.js +1 -1
- package/node/encryption/@aes-256-gcm/index.d.ts +1 -1
- package/node/encryption/@aes-256-gcm/index.js +1 -1
- package/node/ftps/directory/index.d.ts +1 -1
- package/node/ftps/directory/index.js +1 -1
- package/node/images/index.d.ts +1 -1
- package/node/images/index.js +1 -1
- package/node/images/transform/operations/index.d.ts +3 -3
- package/node/images/transform/operations/index.js +3 -3
- package/node/index.d.ts +2 -2
- package/node/index.js +2 -2
- package/package.json +44 -1
- package/components/ListLoader/utils.d.ts +0 -8
- package/components/ListLoader/utils.js +0 -10
|
@@ -1,80 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Children, cloneElement, isValidElement, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useStateChangeDispatch } from '../../agnostic/@react/effects/use-state-change-dispatch/index.js';
|
|
3
4
|
import { absoluteModulo } from '../../agnostic/numbers/absolute-modulo/index.js';
|
|
4
5
|
import { clamp } from '../../agnostic/numbers/clamp/index.js';
|
|
5
6
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
6
|
-
import { mergeClassNames
|
|
7
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
7
8
|
import { useViewportBehaviours } from '../utils/viewport-behaviours/index.js';
|
|
8
9
|
import { sequencer as publicClassName } from '../public-classnames.js';
|
|
9
10
|
import cssModule from './styles.module.css';
|
|
10
|
-
|
|
11
|
-
* The attribute a child uses to say which steps it belongs to.
|
|
12
|
-
*
|
|
13
|
-
* **Written on the child rather than gathered in a prop**, and that is the whole
|
|
14
|
-
* difference with the `activateOnStep` this component used to take. A parallel array and
|
|
15
|
-
* a children list derive from one another: inserting a child in the middle means
|
|
16
|
-
* reindexing the array, and nothing signals the one time it is forgotten. Carried by the
|
|
17
|
-
* child, the attachment travels with it.
|
|
18
|
-
*
|
|
19
|
-
* It stays in the DOM once read — it costs nothing, it is worth having while debugging,
|
|
20
|
-
* and a stylesheet can select on it.
|
|
21
|
-
*/
|
|
22
|
-
const STEPS_ATTRIBUTE = 'data-steps';
|
|
23
|
-
/**
|
|
24
|
-
* The attribute a child uses to say that the sequence ends on it.
|
|
25
|
-
*
|
|
26
|
-
* It answers a shape the positional fallback could not: an article writes the steps in
|
|
27
|
-
* reading order, then — **after** the sequence proper — the children that belong to
|
|
28
|
-
* several steps at once and name them with {@link STEPS_ATTRIBUTE}. Those trailing
|
|
29
|
-
* children would each claim a step of their own in the count, and the sequence would run
|
|
30
|
-
* on into steps where nothing but them is lit.
|
|
31
|
-
*
|
|
32
|
-
* Marking the last step is what separates the two halves, and it is written where the
|
|
33
|
-
* separation is rather than recounted in a prop — the same reason `data-steps` lives on
|
|
34
|
-
* the child. A child after the mark that names no step therefore never lights up, which
|
|
35
|
-
* is the honest reading of « it is not part of the sequence ».
|
|
36
|
-
*
|
|
37
|
-
* Valueless, as an HTML boolean attribute: what matters is that it is there.
|
|
38
|
-
*/
|
|
39
|
-
const LAST_STEP_ATTRIBUTE = 'data-last-step';
|
|
40
|
-
/**
|
|
41
|
-
* `data-steps="2, 6"` into the steps it names.
|
|
42
|
-
*
|
|
43
|
-
* Anything unreadable gives back `null`, which the caller reads as « the attribute was
|
|
44
|
-
* not written » and falls back to the child's position. That is lm-link's rule applied
|
|
45
|
-
* here — a typo costs the default behaviour, never a broken one.
|
|
46
|
-
*/
|
|
47
|
-
function parseSteps(raw) {
|
|
48
|
-
if (typeof raw !== 'string')
|
|
49
|
-
return null;
|
|
50
|
-
const parts = raw.split(',').map(part => part.trim()).filter(part => part !== '');
|
|
51
|
-
if (parts.length === 0)
|
|
52
|
-
return null;
|
|
53
|
-
const steps = parts.map(Number);
|
|
54
|
-
if (steps.some(step => !Number.isInteger(step)))
|
|
55
|
-
return null;
|
|
56
|
-
return steps;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Whether a child carries {@link LAST_STEP_ATTRIBUTE}.
|
|
60
|
-
*
|
|
61
|
-
* Present is enough — `data-last-step` alone gives `''` through lm-html, and `true`
|
|
62
|
-
* written in JSX. Only an explicit denial is read as one, so that a consumer computing
|
|
63
|
-
* the attribute can write `false` without having to leave it out.
|
|
64
|
-
*/
|
|
65
|
-
function isLastStep(child) {
|
|
66
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- a React element's props are a record, whatever else they are
|
|
67
|
-
const raw = child.props[LAST_STEP_ATTRIBUTE];
|
|
68
|
-
if (raw === undefined || raw === null)
|
|
69
|
-
return false;
|
|
70
|
-
return raw !== false && raw !== 'false';
|
|
71
|
-
}
|
|
72
|
-
/** The steps a child answers to: what it declares, or where it sits. */
|
|
73
|
-
function stepsOf(child, elementIndex) {
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- a React element's props are a record, whatever else they are
|
|
75
|
-
const declared = parseSteps(child.props[STEPS_ATTRIBUTE]);
|
|
76
|
-
return declared ?? [elementIndex];
|
|
77
|
-
}
|
|
11
|
+
import { getChildActiveSteps, hasDataLastStepAttribute } from './utils.js';
|
|
78
12
|
/**
|
|
79
13
|
* A sequencer over its children, which it classifies and never moves.
|
|
80
14
|
*
|
|
@@ -118,7 +52,7 @@ function stepsOf(child, elementIndex) {
|
|
|
118
52
|
* **Nothing surrenders here.** The layer lets a reader's hand switch an instruction off
|
|
119
53
|
* for the rest of the mount, and this component has no control for a hand to touch — so
|
|
120
54
|
* `':force'` has nothing to override, and the single `'playback'` domain is a formality.
|
|
121
|
-
*
|
|
55
|
+
* @see {@link SequencerDomain}
|
|
122
56
|
*
|
|
123
57
|
* ### On the root
|
|
124
58
|
* `--playing`, `--at-start`, `--at-end`, `--ended`, plus `data-step`, `data-active-step`,
|
|
@@ -148,19 +82,14 @@ function stepsOf(child, elementIndex) {
|
|
|
148
82
|
* therefore `--is-first` at 1 and `--not-first` at 6, and the answer is the same on every
|
|
149
83
|
* lap without anything being reset.
|
|
150
84
|
*/
|
|
151
|
-
export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaultPlay, tempo = 60, loop, onStepChanged, onIsPlayingChanged, onIsEndedChanged, onLooped, onReachedFirstStep, onReachedLastStep,
|
|
85
|
+
export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaultPlay, tempo = 60, loop, onStepChanged, onIsPlayingChanged, onIsEndedChanged, onLooped, onReachedFirstStep, onReachedLastStep, visibilityThreshold, visibilityRoot, visibilityRootMargin, visibilityOnAfterMs, visibilityOffAfterMs, whenVisible, whenHidden, onVisibilityChanged, className, children }) => {
|
|
152
86
|
const [internalStep, setInternalStep] = useState(step ?? defaultStep ?? 0);
|
|
153
87
|
const [internalPlay, setInternalPlay] = useState(defaultPlay ?? false);
|
|
154
88
|
const [hasLapped, setHasLapped] = useState(0);
|
|
155
89
|
const rootRef = useRef(null);
|
|
156
|
-
// Children, split once: what takes part and what merely renders.
|
|
157
90
|
const childrenArr = Children.toArray(children);
|
|
158
91
|
const elements = childrenArr.filter(isValidElement);
|
|
159
|
-
|
|
160
|
-
// the prop, then the child that says the sequence ends on it, then the count of
|
|
161
|
-
// element children. The prop wins because it is the one a consumer writes knowing the
|
|
162
|
-
// whole, where the mark only knows where it sits.
|
|
163
|
-
const markedLast = elements.findIndex(isLastStep);
|
|
92
|
+
const markedLast = elements.findIndex(hasDataLastStepAttribute);
|
|
164
93
|
const impliedSteps = markedLast === -1 ? elements.length : markedLast + 1;
|
|
165
94
|
const stepsCount = Math.max(totalSteps ?? impliedSteps, 0);
|
|
166
95
|
// The two numbers
|
|
@@ -171,12 +100,8 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
171
100
|
: clamp(rawStep, 0, stepsCount - 1))
|
|
172
101
|
: 0;
|
|
173
102
|
const activeStep = stepMap?.[position] ?? position;
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
// the position clamps to it — which is also what keeps the whole thing derived: nothing
|
|
177
|
-
// has to be unset when `loop` or `step` changes under it, and `onReachedLastStep` stays
|
|
178
|
-
// the arrival that this is not. A sequence merely paused on the last step has not
|
|
179
|
-
// ended, and a consumer driving `step` says so by naming the position after the last.
|
|
103
|
+
// The counter runs one past the end so the last step gets its beat, and the position
|
|
104
|
+
// clamps to it. Ended is therefore leaving the last step, not arriving on it.
|
|
180
105
|
const isEnded = loop !== true && stepsCount > 0 && rawStep >= stepsCount;
|
|
181
106
|
const isPlaying = (play ?? internalPlay) && !isEnded;
|
|
182
107
|
useEffect(() => {
|
|
@@ -197,18 +122,8 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
197
122
|
onLooped?.();
|
|
198
123
|
}, [lap, loop]);
|
|
199
124
|
// Viewport behaviours
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
// wrap backwards and fire `onLooped` for a move nobody made. `jump-by` is left alone
|
|
203
|
-
// on purpose — stepping past the last position really is a wrap, and says so.
|
|
204
|
-
//
|
|
205
|
-
// **The lap offset is for looping sequences only, and adding it elsewhere was a bug.**
|
|
206
|
-
// A sequence that does not loop runs its counter one past the end and leaves it there,
|
|
207
|
-
// so `lap` reads `1` from that moment on — and `jump-start` landed on `1 * stepsCount`,
|
|
208
|
-
// which is precisely the ended position it was asked to leave. The symptom: a sequence
|
|
209
|
-
// with `whenVisible='jump-start'` played once, stopped on its last step, and stayed
|
|
210
|
-
// there however many times it was scrolled past. Nothing is lost by dropping the
|
|
211
|
-
// offset here: without `loop`, there is no wrap to misread and `onLooped` never fires.
|
|
125
|
+
// The lap offset only applies while looping: on a sequence that does not loop, `lap`
|
|
126
|
+
// reads `1` past the end, and adding it would land `jump-start` back on the end.
|
|
212
127
|
const jumpToPosition = (target) => {
|
|
213
128
|
if (step !== undefined || stepsCount <= 0)
|
|
214
129
|
return;
|
|
@@ -223,18 +138,12 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
223
138
|
return;
|
|
224
139
|
setInternalStep(current => current + offset);
|
|
225
140
|
};
|
|
226
|
-
// What each verb does. Rebuilt on every render — it closes over the setters — which is
|
|
227
|
-
// why the generic layer reads it through a ref rather than a dependency list.
|
|
228
|
-
//
|
|
229
141
|
// `play` and `pause` need no guard against a controlled `play`: the effective state
|
|
230
|
-
// reads the prop first, so the internal one they write is
|
|
142
|
+
// reads the prop first, so the internal one they write is never consulted.
|
|
231
143
|
const actions = {
|
|
232
|
-
play: {
|
|
233
|
-
pause: {
|
|
234
|
-
// A jump starts nothing on its own — it moves a counter, playing or not — so a gate
|
|
235
|
-
// has no reason to hold it back.
|
|
144
|
+
play: { domain: 'playback', run: () => setInternalPlay(true) },
|
|
145
|
+
pause: { domain: 'playback', run: () => setInternalPlay(false) },
|
|
236
146
|
'jump-to': {
|
|
237
|
-
kind: 'stop',
|
|
238
147
|
domain: 'playback',
|
|
239
148
|
run: arg => {
|
|
240
149
|
const target = Number(arg);
|
|
@@ -243,10 +152,9 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
243
152
|
jumpToPosition(target);
|
|
244
153
|
}
|
|
245
154
|
},
|
|
246
|
-
'jump-start': {
|
|
247
|
-
'jump-end': {
|
|
155
|
+
'jump-start': { domain: 'playback', run: () => jumpToPosition(0) },
|
|
156
|
+
'jump-end': { domain: 'playback', run: () => jumpToPosition(-1) },
|
|
248
157
|
'jump-by': {
|
|
249
|
-
kind: 'stop',
|
|
250
158
|
domain: 'playback',
|
|
251
159
|
run: arg => {
|
|
252
160
|
const offset = Number(arg);
|
|
@@ -256,8 +164,7 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
256
164
|
}
|
|
257
165
|
}
|
|
258
166
|
};
|
|
259
|
-
// The surrender half
|
|
260
|
-
// component's lack of controls changes: nothing here can be taken over by hand.
|
|
167
|
+
// The surrender half is dropped: this component has no control a hand could take over.
|
|
261
168
|
useViewportBehaviours(rootRef, {
|
|
262
169
|
visibilityThreshold,
|
|
263
170
|
visibilityRoot,
|
|
@@ -267,14 +174,14 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
267
174
|
whenVisible,
|
|
268
175
|
whenHidden,
|
|
269
176
|
onVisibilityChanged
|
|
270
|
-
}, actions
|
|
177
|
+
}, actions);
|
|
271
178
|
// State dispatch
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
179
|
+
useStateChangeDispatch(position, () => onStepChanged?.({ step: position, activeStep }));
|
|
180
|
+
useStateChangeDispatch(isPlaying, onIsPlayingChanged);
|
|
181
|
+
useStateChangeDispatch(isEnded, onIsEndedChanged);
|
|
182
|
+
useStateChangeDispatch(position === 0, atStart => { if (atStart)
|
|
276
183
|
onReachedFirstStep?.(); });
|
|
277
|
-
|
|
184
|
+
useStateChangeDispatch(stepsCount > 0 && position === stepsCount - 1, atEnd => { if (atEnd)
|
|
278
185
|
onReachedLastStep?.(); });
|
|
279
186
|
// Rendering
|
|
280
187
|
const c = clss(publicClassName, { cssModule });
|
|
@@ -284,8 +191,6 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
284
191
|
'at-end': stepsCount > 0 && position === stepsCount - 1,
|
|
285
192
|
ended: isEnded
|
|
286
193
|
}), className);
|
|
287
|
-
// Walked once per render: for each element, the positions of this lap at which it is
|
|
288
|
-
// active. Cheap, and it is what makes the three pairs answerable without memory.
|
|
289
194
|
const positionsOf = (steps) => {
|
|
290
195
|
const positions = [];
|
|
291
196
|
for (let i = 0; i < stepsCount; i += 1) {
|
|
@@ -294,14 +199,9 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
294
199
|
}
|
|
295
200
|
return positions;
|
|
296
201
|
};
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
// never wrapped at all — true of the division, false of the thing it is named after,
|
|
300
|
-
// and `onLooped` says the same by never firing without `loop`.
|
|
202
|
+
// A lap is a wrap, so there is none without `loop` — where the internal counter would
|
|
203
|
+
// report `1` past the end. Both numbers below are rebuilt from it rather than `rawStep`.
|
|
301
204
|
const publishedLap = loop === true ? lap : 0;
|
|
302
|
-
// Rebuilt from the published lap rather than read off `rawStep`, for that same reason:
|
|
303
|
-
// at the end of a non-looping sequence the internal counter sits one step past the last
|
|
304
|
-
// position, and a number meant to drive a transform must not overshoot the picture.
|
|
305
205
|
const rawStepProp = publishedLap * stepsCount + position;
|
|
306
206
|
const cssCustomProps = {
|
|
307
207
|
'--lm-sequencer-step': `${position}`,
|
|
@@ -316,7 +216,7 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
316
216
|
if (!isValidElement(child))
|
|
317
217
|
return child;
|
|
318
218
|
elementIndex += 1;
|
|
319
|
-
const steps =
|
|
219
|
+
const steps = getChildActiveSteps(child, elementIndex);
|
|
320
220
|
const positions = positionsOf(steps);
|
|
321
221
|
const isOn = steps.includes(activeStep);
|
|
322
222
|
const hasEarlier = positions.some(p => p < position);
|
|
@@ -331,9 +231,7 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
331
231
|
});
|
|
332
232
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- a React element's props are a record, whatever else they are
|
|
333
233
|
const ownClassName = child.props.className;
|
|
334
|
-
//
|
|
335
|
-
// touched here — its own is kept, and comes second so a consumer's class wins a
|
|
336
|
-
// shorthand conflict.
|
|
234
|
+
// The child's own class comes second, so a consumer wins a shorthand conflict.
|
|
337
235
|
const nextProps = {
|
|
338
236
|
key: childPos,
|
|
339
237
|
className: mergeClassNames(childClss, typeof ownClassName === 'string' ? ownClassName : undefined)
|
|
@@ -55,3 +55,33 @@ export type SequencerAction = Exclude<SequencerVerb, 'jump-to' | 'jump-by'> | `j
|
|
|
55
55
|
* there are none.
|
|
56
56
|
*/
|
|
57
57
|
export type SequencerDomain = 'playback';
|
|
58
|
+
/**
|
|
59
|
+
* The attribute a child uses to say which steps it belongs to.
|
|
60
|
+
*
|
|
61
|
+
* **Written on the child rather than gathered in a prop**, and that is the whole
|
|
62
|
+
* difference with the `activateOnStep` this component used to take. A parallel array and
|
|
63
|
+
* a children list derive from one another: inserting a child in the middle means
|
|
64
|
+
* reindexing the array, and nothing signals the one time it is forgotten. Carried by the
|
|
65
|
+
* child, the attachment travels with it.
|
|
66
|
+
*
|
|
67
|
+
* It stays in the DOM once read — it costs nothing, it is worth having while debugging,
|
|
68
|
+
* and a stylesheet can select on it.
|
|
69
|
+
*/
|
|
70
|
+
export declare const STEPS_ATTRIBUTE = "data-steps";
|
|
71
|
+
/**
|
|
72
|
+
* The attribute a child uses to say that the sequence ends on it.
|
|
73
|
+
*
|
|
74
|
+
* It answers a shape the positional fallback could not: an article writes the steps in
|
|
75
|
+
* reading order, then — **after** the sequence proper — the children that belong to
|
|
76
|
+
* several steps at once and name them with {@link STEPS_ATTRIBUTE}. Those trailing
|
|
77
|
+
* children would each claim a step of their own in the count, and the sequence would run
|
|
78
|
+
* on into steps where nothing but them is lit.
|
|
79
|
+
*
|
|
80
|
+
* Marking the last step is what separates the two halves, and it is written where the
|
|
81
|
+
* separation is rather than recounted in a prop — the same reason `data-steps` lives on
|
|
82
|
+
* the child. A child after the mark that names no step therefore never lights up, which
|
|
83
|
+
* is the honest reading of « it is not part of the sequence ».
|
|
84
|
+
*
|
|
85
|
+
* Valueless, as an HTML boolean attribute: what matters is that it is there.
|
|
86
|
+
*/
|
|
87
|
+
export declare const LAST_STEP_ATTRIBUTE = "data-last-step";
|
|
@@ -30,3 +30,33 @@ export const SEQUENCER_VERB_ARGUMENTS = {
|
|
|
30
30
|
'jump-to': (arg) => /^-?\d+$/v.test(arg),
|
|
31
31
|
'jump-by': (arg) => /^-?\d+$/v.test(arg)
|
|
32
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* The attribute a child uses to say which steps it belongs to.
|
|
35
|
+
*
|
|
36
|
+
* **Written on the child rather than gathered in a prop**, and that is the whole
|
|
37
|
+
* difference with the `activateOnStep` this component used to take. A parallel array and
|
|
38
|
+
* a children list derive from one another: inserting a child in the middle means
|
|
39
|
+
* reindexing the array, and nothing signals the one time it is forgotten. Carried by the
|
|
40
|
+
* child, the attachment travels with it.
|
|
41
|
+
*
|
|
42
|
+
* It stays in the DOM once read — it costs nothing, it is worth having while debugging,
|
|
43
|
+
* and a stylesheet can select on it.
|
|
44
|
+
*/
|
|
45
|
+
export const STEPS_ATTRIBUTE = 'data-steps';
|
|
46
|
+
/**
|
|
47
|
+
* The attribute a child uses to say that the sequence ends on it.
|
|
48
|
+
*
|
|
49
|
+
* It answers a shape the positional fallback could not: an article writes the steps in
|
|
50
|
+
* reading order, then — **after** the sequence proper — the children that belong to
|
|
51
|
+
* several steps at once and name them with {@link STEPS_ATTRIBUTE}. Those trailing
|
|
52
|
+
* children would each claim a step of their own in the count, and the sequence would run
|
|
53
|
+
* on into steps where nothing but them is lit.
|
|
54
|
+
*
|
|
55
|
+
* Marking the last step is what separates the two halves, and it is written where the
|
|
56
|
+
* separation is rather than recounted in a prop — the same reason `data-steps` lives on
|
|
57
|
+
* the child. A child after the mark that names no step therefore never lights up, which
|
|
58
|
+
* is the honest reading of « it is not part of the sequence ».
|
|
59
|
+
*
|
|
60
|
+
* Valueless, as an HTML boolean attribute: what matters is that it is there.
|
|
61
|
+
*/
|
|
62
|
+
export const LAST_STEP_ATTRIBUTE = 'data-last-step';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
export declare function parseDataStepsAttribute(raw: unknown): number[] | null;
|
|
3
|
+
export declare function hasDataLastStepAttribute(child: ReactElement): boolean;
|
|
4
|
+
export declare function getChildActiveSteps(child: ReactElement, elementIndex: number): number[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { LAST_STEP_ATTRIBUTE, STEPS_ATTRIBUTE } from './types.js';
|
|
2
|
+
export function parseDataStepsAttribute(raw) {
|
|
3
|
+
if (typeof raw !== 'string')
|
|
4
|
+
return null;
|
|
5
|
+
const parts = raw.split(',').map(part => part.trim()).filter(part => part !== '');
|
|
6
|
+
if (parts.length === 0)
|
|
7
|
+
return null;
|
|
8
|
+
const steps = parts.map(Number);
|
|
9
|
+
if (steps.some(step => !Number.isInteger(step)))
|
|
10
|
+
return null;
|
|
11
|
+
return steps;
|
|
12
|
+
}
|
|
13
|
+
export function hasDataLastStepAttribute(child) {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- a React element's props are a record, whatever else they are
|
|
15
|
+
const raw = child.props[LAST_STEP_ATTRIBUTE];
|
|
16
|
+
if (raw === undefined || raw === null)
|
|
17
|
+
return false;
|
|
18
|
+
return raw !== false && raw !== 'false';
|
|
19
|
+
}
|
|
20
|
+
export function getChildActiveSteps(child, elementIndex) {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- a React element's props are a record, whatever else they are
|
|
22
|
+
const props = child.props;
|
|
23
|
+
const declared = parseDataStepsAttribute(props[STEPS_ATTRIBUTE]);
|
|
24
|
+
return declared ?? [elementIndex];
|
|
25
|
+
}
|
|
@@ -2,8 +2,8 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { useRef, useEffect, useState } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
5
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
5
6
|
import { shadowRoot as publicClassName } from '../public-classnames.js';
|
|
6
|
-
import { mergeClassNames } from '../utils/index.js';
|
|
7
7
|
import cssModule from './styles.module.css';
|
|
8
8
|
/**
|
|
9
9
|
* Component that creates a Shadow Root on its host element and renders
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useStateChangeDispatch } from '../../agnostic/@react/effects/use-state-change-dispatch/index.js';
|
|
3
4
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
5
|
import { toError } from '../../agnostic/misc/cast/index.js';
|
|
5
6
|
import { parseSrt } from '../../agnostic/subtitles/parse-srt/index.js';
|
|
6
|
-
import { mergeClassNames
|
|
7
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
7
8
|
import { subtitles as publicClassName } from '../public-classnames.js';
|
|
8
9
|
import { lastElapsedPos as toLastElapsedPos, normalizeGroupBoundaries, toCurrentGroup, toSubGroups } from './utils.js';
|
|
9
10
|
import cssModule from './styles.module.css';
|
|
@@ -44,9 +45,6 @@ export const Subtitles = ({ cues, srtFileContent, src, subsGroups, timecodeMs, i
|
|
|
44
45
|
const [isLoading, setIsLoading] = useState(false);
|
|
45
46
|
const [loadError, setLoadError] = useState(null);
|
|
46
47
|
const [parsedSubs, setParsedSubs] = useState([]);
|
|
47
|
-
// The load handlers are read through refs so the effect below can depend on the
|
|
48
|
-
// source alone. Depending on their identity re-fetches on every parent render —
|
|
49
|
-
// and `Video` re-renders several times a second as the timecode moves.
|
|
50
48
|
const onLoadedRef = useRef(onLoaded);
|
|
51
49
|
const onLoadFailedRef = useRef(onLoadFailed);
|
|
52
50
|
useEffect(() => {
|
|
@@ -54,7 +52,7 @@ export const Subtitles = ({ cues, srtFileContent, src, subsGroups, timecodeMs, i
|
|
|
54
52
|
onLoadFailedRef.current = onLoadFailed;
|
|
55
53
|
});
|
|
56
54
|
// State dispatch
|
|
57
|
-
|
|
55
|
+
useStateChangeDispatch(parsedSubs, onParsed);
|
|
58
56
|
// Fx. dep. cues, srtFileContent, src - Load from whichever source is set
|
|
59
57
|
useEffect(() => {
|
|
60
58
|
setIsLoading(false);
|
|
@@ -1,60 +1,9 @@
|
|
|
1
1
|
import type { SrtCue } from '../../agnostic/subtitles/parse-srt/types.js';
|
|
2
|
-
/**
|
|
3
|
-
* The inclusive bounds of a subtitle group, as **positions** in the cue array.
|
|
4
|
-
*
|
|
5
|
-
* Positions rather than file ids: an SRT concatenated from several files, or edited
|
|
6
|
-
* by hand, numbers its cues however it likes, and nothing guarantees a run starting
|
|
7
|
-
* at 1. A consumer shouldn't have to know a file's numbering to split it.
|
|
8
|
-
*
|
|
9
|
-
* @property startPos - Position of the group's first cue.
|
|
10
|
-
* @property endPos - Position of its last cue.
|
|
11
|
-
*/
|
|
12
2
|
export type SubGroupBounds = {
|
|
13
3
|
startPos: number;
|
|
14
4
|
endPos: number;
|
|
15
5
|
};
|
|
16
|
-
/**
|
|
17
|
-
* Puts a consumer's group boundaries in a state the group builder can use.
|
|
18
|
-
*
|
|
19
|
-
* Out-of-range, duplicate and unordered boundaries all produce overlapping or
|
|
20
|
-
* inverted groups downstream — the same cue rendered twice, colliding keys. Rather
|
|
21
|
-
* than demand a sorted array and break quietly when it isn't, normalise here.
|
|
22
|
-
*
|
|
23
|
-
* @param boundaries - The positions a consumer asked to split on, in any order.
|
|
24
|
-
* @param lastPos - Position of the last cue.
|
|
25
|
-
* @returns The usable boundaries, ascending, without duplicates, and without the
|
|
26
|
-
* last position — which ends the final group anyway.
|
|
27
|
-
*/
|
|
28
6
|
export declare function normalizeGroupBoundaries(boundaries: number[] | undefined, lastPos: number): number[];
|
|
29
|
-
/**
|
|
30
|
-
* Cuts the cue array into groups.
|
|
31
|
-
*
|
|
32
|
-
* @param boundaries - Normalised boundaries, each the position of a group's last cue.
|
|
33
|
-
* See {@link normalizeGroupBoundaries}.
|
|
34
|
-
* @param lastPos - Position of the last cue.
|
|
35
|
-
* @returns One group per section, in order. A single group spanning everything when
|
|
36
|
-
* no boundary is given, and nothing at all when there is no cue to group.
|
|
37
|
-
*/
|
|
38
7
|
export declare function toSubGroups(boundaries: number[], lastPos: number): SubGroupBounds[];
|
|
39
|
-
/**
|
|
40
|
-
* Finds the last cue the playhead has already reached.
|
|
41
|
-
*
|
|
42
|
-
* @param cues - The parsed cues, in order.
|
|
43
|
-
* @param timecodeMs - Current media position, absent while nothing plays.
|
|
44
|
-
* @returns Its position, or `-1` when the playhead sits before the first cue. One
|
|
45
|
-
* number, computed once: `--prev` on a cue and the current group both read from it,
|
|
46
|
-
* and deriving them separately lets them disagree on a file with duplicate or
|
|
47
|
-
* out-of-order start times.
|
|
48
|
-
*/
|
|
49
8
|
export declare function lastElapsedPos(cues: SrtCue[], timecodeMs?: number): number;
|
|
50
|
-
/**
|
|
51
|
-
* Picks the group the reader is in.
|
|
52
|
-
*
|
|
53
|
-
* @param groups - The groups, in order.
|
|
54
|
-
* @param lastElapsedPos - Position of the last elapsed cue, `-1` before the first.
|
|
55
|
-
* @param isEnded - Whether playback is over.
|
|
56
|
-
* @returns The group holding that cue. Once playback has ended, the last group
|
|
57
|
-
* whatever the timecode says — which is what `isEnded` is for. The first group
|
|
58
|
-
* before anything has elapsed.
|
|
59
|
-
*/
|
|
60
9
|
export declare function toCurrentGroup(groups: SubGroupBounds[], lastElapsedPos: number, isEnded?: boolean): SubGroupBounds | undefined;
|
|
@@ -1,30 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Puts a consumer's group boundaries in a state the group builder can use.
|
|
3
|
-
*
|
|
4
|
-
* Out-of-range, duplicate and unordered boundaries all produce overlapping or
|
|
5
|
-
* inverted groups downstream — the same cue rendered twice, colliding keys. Rather
|
|
6
|
-
* than demand a sorted array and break quietly when it isn't, normalise here.
|
|
7
|
-
*
|
|
8
|
-
* @param boundaries - The positions a consumer asked to split on, in any order.
|
|
9
|
-
* @param lastPos - Position of the last cue.
|
|
10
|
-
* @returns The usable boundaries, ascending, without duplicates, and without the
|
|
11
|
-
* last position — which ends the final group anyway.
|
|
12
|
-
*/
|
|
13
1
|
export function normalizeGroupBoundaries(boundaries, lastPos) {
|
|
14
2
|
if (boundaries === undefined)
|
|
15
3
|
return [];
|
|
16
4
|
const usable = boundaries.filter(pos => Number.isInteger(pos) && pos >= 0 && pos < lastPos);
|
|
17
5
|
return [...new Set(usable)].sort((a, b) => a - b);
|
|
18
6
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Cuts the cue array into groups.
|
|
21
|
-
*
|
|
22
|
-
* @param boundaries - Normalised boundaries, each the position of a group's last cue.
|
|
23
|
-
* See {@link normalizeGroupBoundaries}.
|
|
24
|
-
* @param lastPos - Position of the last cue.
|
|
25
|
-
* @returns One group per section, in order. A single group spanning everything when
|
|
26
|
-
* no boundary is given, and nothing at all when there is no cue to group.
|
|
27
|
-
*/
|
|
28
7
|
export function toSubGroups(boundaries, lastPos) {
|
|
29
8
|
if (lastPos < 0)
|
|
30
9
|
return [];
|
|
@@ -37,16 +16,6 @@ export function toSubGroups(boundaries, lastPos) {
|
|
|
37
16
|
groups.push({ startPos, endPos: lastPos });
|
|
38
17
|
return groups;
|
|
39
18
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Finds the last cue the playhead has already reached.
|
|
42
|
-
*
|
|
43
|
-
* @param cues - The parsed cues, in order.
|
|
44
|
-
* @param timecodeMs - Current media position, absent while nothing plays.
|
|
45
|
-
* @returns Its position, or `-1` when the playhead sits before the first cue. One
|
|
46
|
-
* number, computed once: `--prev` on a cue and the current group both read from it,
|
|
47
|
-
* and deriving them separately lets them disagree on a file with duplicate or
|
|
48
|
-
* out-of-order start times.
|
|
49
|
-
*/
|
|
50
19
|
export function lastElapsedPos(cues, timecodeMs) {
|
|
51
20
|
if (timecodeMs === undefined)
|
|
52
21
|
return -1;
|
|
@@ -57,16 +26,6 @@ export function lastElapsedPos(cues, timecodeMs) {
|
|
|
57
26
|
});
|
|
58
27
|
return found;
|
|
59
28
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Picks the group the reader is in.
|
|
62
|
-
*
|
|
63
|
-
* @param groups - The groups, in order.
|
|
64
|
-
* @param lastElapsedPos - Position of the last elapsed cue, `-1` before the first.
|
|
65
|
-
* @param isEnded - Whether playback is over.
|
|
66
|
-
* @returns The group holding that cue. Once playback has ended, the last group
|
|
67
|
-
* whatever the timecode says — which is what `isEnded` is for. The first group
|
|
68
|
-
* before anything has elapsed.
|
|
69
|
-
*/
|
|
70
29
|
export function toCurrentGroup(groups, lastElapsedPos, isEnded) {
|
|
71
30
|
if (isEnded === true)
|
|
72
31
|
return groups[groups.length - 1];
|
|
@@ -3,7 +3,7 @@ import { useLayoutEffect, useRef, useState } from 'react';
|
|
|
3
3
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
4
|
import { isNotFalsy } from '../../agnostic/booleans/is-falsy/index.js';
|
|
5
5
|
import { randomHash } from '../../agnostic/random/uuid/index.js';
|
|
6
|
-
import { mergeClassNames } from '
|
|
6
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
7
7
|
import { textarea as publicClassName } from '../public-classnames.js';
|
|
8
8
|
import cssModule from './styles.module.css';
|
|
9
9
|
/**
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useStateChangeDispatch } from '../../agnostic/@react/effects/use-state-change-dispatch/index.js';
|
|
3
4
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
5
|
import { unknownToString } from '../../agnostic/errors/unknown-to-string/index.js';
|
|
5
6
|
import { isNonNullObject } from '../../agnostic/objects/is-object/index.js';
|
|
6
7
|
import { randomHash } from '../../agnostic/random/uuid/index.js';
|
|
7
|
-
import { mergeClassNames
|
|
8
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
8
9
|
import { uiModule as publicClassName } from '../public-classnames.js';
|
|
9
10
|
import cssModule from './styles.module.css';
|
|
10
11
|
/**
|
|
@@ -56,9 +57,9 @@ export const UIModule = ({ src, props, onIdGenerated, onIsLoadingChanged, onLoad
|
|
|
56
57
|
// the render it ran on. This is what `init` and `postInit` read instead.
|
|
57
58
|
const propsRef = useRef(props);
|
|
58
59
|
// State dispatch
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
useStateChangeDispatch(isLoading, onIsLoadingChanged);
|
|
61
|
+
useStateChangeDispatch(loadedModule, onLoadedModuleChanged);
|
|
62
|
+
useStateChangeDispatch(moduleTarget, onModuleTargetChanged);
|
|
62
63
|
// Fx. no dep. - keep the latest props reachable from the effects that run outside
|
|
63
64
|
// their own render, declared first so they read this render's value and not the last
|
|
64
65
|
useEffect(() => { propsRef.current = props; });
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { type FunctionComponent, type PropsWithChildren, type Ref, type RefObject, 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
|
-
import {
|
|
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
|
-
*/
|
|
4
|
+
import type { SourceData, TrackData } from './types.js';
|
|
11
5
|
/**
|
|
12
6
|
* Props for the ControlledVideo component.
|
|
13
7
|
*
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useRef, useState, useCallback, useEffect } from 'react';
|
|
3
|
+
import { useStateChangeDispatch } from '../../agnostic/@react/effects/use-state-change-dispatch/index.js';
|
|
3
4
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
5
|
import { formatDuration } from '../../agnostic/time/duration/format-duration/index.js';
|
|
5
|
-
import { mergeClassNames
|
|
6
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
6
7
|
import cssModule from './styles.module.css';
|
|
7
8
|
import { video as publicClassName } from '../public-classnames.js';
|
|
8
9
|
import { Subtitles } from '../Subtitles/index.js';
|
|
9
|
-
import { forceExitFullscreen, forceFullscreen, forceLoud, forceMute, forcePause, forcePlay, forcePlaybackRate, forceVolume, getTimelineClickProgress, msToSeconds, secondsToMs } from './utils.js';
|
|
10
|
+
import { forceExitFullscreen, forceFullscreen, forceLoud, forceMute, forcePause, forcePlay, forcePlaybackRate, forceVolume, getTimelineClickProgress, msToSeconds, parseSrcSetSourcesList, secondsToMs } from './utils.js';
|
|
10
11
|
/**
|
|
11
12
|
* Full-featured video player component. Wraps a native `<video>` element with
|
|
12
13
|
* playback controls, volume, playback rate, a timeline, optional subtitles
|
|
@@ -226,8 +227,8 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
|
|
|
226
227
|
'--lm-video-volume-ratio': `${volume}`,
|
|
227
228
|
'--lm-video-playback-rate': `${playbackRate}`
|
|
228
229
|
};
|
|
229
|
-
const parsedSources = useMemo(() =>
|
|
230
|
-
const parsedTracks = useMemo(() =>
|
|
230
|
+
const parsedSources = useMemo(() => parseSrcSetSourcesList(sources, 'src'), [sources]);
|
|
231
|
+
const parsedTracks = useMemo(() => parseSrcSetSourcesList(tracks, 'src'), [tracks]);
|
|
231
232
|
const videoClss = c('video');
|
|
232
233
|
const videoControlsClss = c('video-controls');
|
|
233
234
|
const playBtnClss = c('play-btn');
|
|
@@ -305,13 +306,13 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
|
|
|
305
306
|
return () => { document.removeEventListener('fullscreenchange', onDocumentFullscreenChange); };
|
|
306
307
|
}, [onFullscreenChange]);
|
|
307
308
|
// State handlers
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
309
|
+
useStateChangeDispatch(currentTimeMs, onCurrentTimeMsChanged);
|
|
310
|
+
useStateChangeDispatch(isElementPlaying, onIsPlayingChanged);
|
|
311
|
+
useStateChangeDispatch(isFullscreen, onIsFullscreenChanged);
|
|
312
|
+
useStateChangeDispatch(isLoud, onIsLoudChanged);
|
|
313
|
+
useStateChangeDispatch(subtitlesOn, onIsSubtitlesOnChanged);
|
|
314
|
+
useStateChangeDispatch(isEnded, onIsEndedChanged);
|
|
315
|
+
useStateChangeDispatch(volume, onVolumeChanged);
|
|
316
|
+
useStateChangeDispatch(playbackRate, onPlaybackRateChanged);
|
|
316
317
|
return _jsxs("figure", { ref: rootRef, className: rootClss, style: rootStyles, ...rootAttributes, children: [_jsxs("video", { ref: videoRef, className: videoClss, ...intrinsicVideoAttributes, autoPlay: isTimeControlled ? false : intrinsicVideoAttributes.autoPlay, onClick: handleVideoClick, onLoadedMetadata: handleMetadataLoadEvent, onTimeUpdate: handleOnTimeUpdateEvent, onEnded: handleEndedEvent, onPlay: handlePlayEvent, onPause: handlePauseEvent, children: [parsedSources.map((source, index) => _jsx("source", { src: source.src, type: source.type }, index)), parsedTracks.map((track, index) => _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 })] });
|
|
317
318
|
};
|