@design-edito/tools 0.5.17 → 0.5.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/agnostic/colors/index.d.ts +2 -2
  2. package/agnostic/colors/index.js +2 -2
  3. package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +11 -11
  4. package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +11 -11
  5. package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +6 -6
  6. package/agnostic/html/hyper-json/smart-tags/isolated/index.js +6 -6
  7. package/agnostic/html/index.d.ts +2 -2
  8. package/agnostic/html/index.js +2 -2
  9. package/agnostic/misc/index.d.ts +3 -3
  10. package/agnostic/misc/index.js +3 -3
  11. package/agnostic/misc/logs/index.d.ts +1 -1
  12. package/agnostic/misc/logs/index.js +1 -1
  13. package/agnostic/numbers/index.d.ts +3 -3
  14. package/agnostic/numbers/index.js +3 -3
  15. package/agnostic/objects/index.d.ts +1 -1
  16. package/agnostic/objects/index.js +1 -1
  17. package/agnostic/optim/index.d.ts +1 -1
  18. package/agnostic/optim/index.js +1 -1
  19. package/agnostic/strings/index.d.ts +3 -3
  20. package/agnostic/strings/index.js +3 -3
  21. package/agnostic/time/index.d.ts +2 -2
  22. package/agnostic/time/index.js +2 -2
  23. package/components/Image/index.d.ts +6 -0
  24. package/components/Image/index.js +3 -18
  25. package/components/Video/index.controlled.d.ts +5 -22
  26. package/components/Video/index.controlled.js +28 -42
  27. package/components/Video/index.d.ts +36 -37
  28. package/components/Video/index.js +83 -88
  29. package/components/Video/types.d.ts +42 -0
  30. package/components/Video/types.js +30 -0
  31. package/components/Video/utils.d.ts +38 -1
  32. package/components/Video/utils.js +23 -4
  33. package/components/index.d.ts +1 -1
  34. package/components/index.js +1 -1
  35. package/components/utils/index.d.ts +21 -0
  36. package/components/utils/index.js +40 -0
  37. package/components/utils/viewport-behaviours/index.d.ts +54 -0
  38. package/components/utils/viewport-behaviours/index.js +163 -0
  39. package/components/utils/viewport-behaviours/types.d.ts +109 -0
  40. package/components/utils/viewport-behaviours/types.js +14 -0
  41. package/components/utils/viewport-behaviours/utils.d.ts +47 -0
  42. package/components/utils/viewport-behaviours/utils.js +86 -0
  43. package/node/@aws-s3/storage/file/index.d.ts +1 -1
  44. package/node/@aws-s3/storage/file/index.js +1 -1
  45. package/node/cloud-storage/operations/index.d.ts +1 -1
  46. package/node/cloud-storage/operations/index.js +1 -1
  47. package/package.json +16 -1
@@ -3,10 +3,10 @@ export * as contrast from './contrast/index.js'
3
3
  export * as convert from './convert/index.js'
4
4
  export * as distance from './distance/index.js'
5
5
  export * as grayscale from './grayscale/index.js'
6
- export * as lerp from './lerp/index.js'
7
6
  export * as invert from './invert/index.js'
7
+ export * as lerp from './lerp/index.js'
8
+ export * as luminance from './luminance/index.js'
8
9
  export * as palette from './palette/index.js'
9
10
  export * as rotate from './rotate/index.js'
10
11
  export * as tidy from './tidy/index.js'
11
12
  export * as typechecks from './typechecks/index.js'
12
- export * as luminance from './luminance/index.js'
@@ -3,10 +3,10 @@ export * as contrast from './contrast/index.js'
3
3
  export * as convert from './convert/index.js'
4
4
  export * as distance from './distance/index.js'
5
5
  export * as grayscale from './grayscale/index.js'
6
- export * as lerp from './lerp/index.js'
7
6
  export * as invert from './invert/index.js'
7
+ export * as lerp from './lerp/index.js'
8
+ export * as luminance from './luminance/index.js'
8
9
  export * as palette from './palette/index.js'
9
10
  export * as rotate from './rotate/index.js'
10
11
  export * as tidy from './tidy/index.js'
11
12
  export * as typechecks from './typechecks/index.js'
12
- export * as luminance from './luminance/index.js'
@@ -1,21 +1,21 @@
1
1
  export * as add from './add/index.js'
2
2
  export * as addclass from './addclass/index.js'
3
3
  export * as and from './and/index.js'
4
- export * as at from './at/index.js'
5
4
  export * as append from './append/index.js'
6
- export * as clone from './clone/index.js'
5
+ export * as at from './at/index.js'
7
6
  export * as call from './call/index.js'
8
7
  export * as deleteproperties from './deleteproperties/index.js'
9
8
  export * as equals from './equals/index.js'
9
+ export * as clone from './clone/index.js'
10
10
  export * as getattribute from './getattribute/index.js'
11
11
  export * as getproperties from './getproperties/index.js'
12
12
  export * as getproperty from './getproperty/index.js'
13
13
  export * as hjparse from './hjparse/index.js'
14
14
  export * as hjstringify from './hjstringify/index.js'
15
+ export * as if from './if/index.js'
16
+ export * as length from './length/index.js'
15
17
  export * as initialize from './initialize/index.js'
16
18
  export * as join from './join/index.js'
17
- export * as length from './length/index.js'
18
- export * as if from './if/index.js'
19
19
  export * as map from './map/index.js'
20
20
  export * as negate from './negate/index.js'
21
21
  export * as notrailing from './notrailing/index.js'
@@ -23,30 +23,30 @@ export * as or from './or/index.js'
23
23
  export * as pickrandom from './pickrandom/index.js'
24
24
  export * as populate from './populate/index.js'
25
25
  export * as print from './print/index.js'
26
+ export * as push from './push/index.js'
26
27
  export * as pusheach from './pusheach/index.js'
27
28
  export * as recordtoarray from './recordtoarray/index.js'
29
+ export * as removeattribute from './removeattribute/index.js'
28
30
  export * as removeclass from './removeclass/index.js'
29
31
  export * as renameproperty from './renameproperty/index.js'
30
32
  export * as replace from './replace/index.js'
31
- export * as removeattribute from './removeattribute/index.js'
32
- export * as push from './push/index.js'
33
33
  export * as select from './select/index.js'
34
34
  export * as set from './set/index.js'
35
35
  export * as setattribute from './setattribute/index.js'
36
36
  export * as setproperty from './setproperty/index.js'
37
37
  export * as sorton from './sorton/index.js'
38
- export * as spread from './spread/index.js'
39
38
  export * as split from './split/index.js'
39
+ export * as spread from './spread/index.js'
40
+ export * as toboolean from './toboolean/index.js'
40
41
  export * as toarray from './toarray/index.js'
41
42
  export * as toelement from './toelement/index.js'
42
- export * as tonodelist from './tonodelist/index.js'
43
+ export * as toggleclass from './toggleclass/index.js'
43
44
  export * as tonull from './tonull/index.js'
44
- export * as toboolean from './toboolean/index.js'
45
45
  export * as tonumber from './tonumber/index.js'
46
+ export * as tonodelist from './tonodelist/index.js'
46
47
  export * as torecord from './torecord/index.js'
47
48
  export * as toref from './toref/index.js'
48
49
  export * as tostring from './tostring/index.js'
49
- export * as toggleclass from './toggleclass/index.js'
50
50
  export * as transformselected from './transformselected/index.js'
51
- export * as totext from './totext/index.js'
52
51
  export * as trim from './trim/index.js'
52
+ export * as totext from './totext/index.js'
@@ -1,21 +1,21 @@
1
1
  export * as add from './add/index.js'
2
2
  export * as addclass from './addclass/index.js'
3
3
  export * as and from './and/index.js'
4
- export * as at from './at/index.js'
5
4
  export * as append from './append/index.js'
6
- export * as clone from './clone/index.js'
5
+ export * as at from './at/index.js'
7
6
  export * as call from './call/index.js'
8
7
  export * as deleteproperties from './deleteproperties/index.js'
9
8
  export * as equals from './equals/index.js'
9
+ export * as clone from './clone/index.js'
10
10
  export * as getattribute from './getattribute/index.js'
11
11
  export * as getproperties from './getproperties/index.js'
12
12
  export * as getproperty from './getproperty/index.js'
13
13
  export * as hjparse from './hjparse/index.js'
14
14
  export * as hjstringify from './hjstringify/index.js'
15
+ export * as if from './if/index.js'
16
+ export * as length from './length/index.js'
15
17
  export * as initialize from './initialize/index.js'
16
18
  export * as join from './join/index.js'
17
- export * as length from './length/index.js'
18
- export * as if from './if/index.js'
19
19
  export * as map from './map/index.js'
20
20
  export * as negate from './negate/index.js'
21
21
  export * as notrailing from './notrailing/index.js'
@@ -23,30 +23,30 @@ export * as or from './or/index.js'
23
23
  export * as pickrandom from './pickrandom/index.js'
24
24
  export * as populate from './populate/index.js'
25
25
  export * as print from './print/index.js'
26
+ export * as push from './push/index.js'
26
27
  export * as pusheach from './pusheach/index.js'
27
28
  export * as recordtoarray from './recordtoarray/index.js'
29
+ export * as removeattribute from './removeattribute/index.js'
28
30
  export * as removeclass from './removeclass/index.js'
29
31
  export * as renameproperty from './renameproperty/index.js'
30
32
  export * as replace from './replace/index.js'
31
- export * as removeattribute from './removeattribute/index.js'
32
- export * as push from './push/index.js'
33
33
  export * as select from './select/index.js'
34
34
  export * as set from './set/index.js'
35
35
  export * as setattribute from './setattribute/index.js'
36
36
  export * as setproperty from './setproperty/index.js'
37
37
  export * as sorton from './sorton/index.js'
38
- export * as spread from './spread/index.js'
39
38
  export * as split from './split/index.js'
39
+ export * as spread from './spread/index.js'
40
+ export * as toboolean from './toboolean/index.js'
40
41
  export * as toarray from './toarray/index.js'
41
42
  export * as toelement from './toelement/index.js'
42
- export * as tonodelist from './tonodelist/index.js'
43
+ export * as toggleclass from './toggleclass/index.js'
43
44
  export * as tonull from './tonull/index.js'
44
- export * as toboolean from './toboolean/index.js'
45
45
  export * as tonumber from './tonumber/index.js'
46
+ export * as tonodelist from './tonodelist/index.js'
46
47
  export * as torecord from './torecord/index.js'
47
48
  export * as toref from './toref/index.js'
48
49
  export * as tostring from './tostring/index.js'
49
- export * as toggleclass from './toggleclass/index.js'
50
50
  export * as transformselected from './transformselected/index.js'
51
- export * as totext from './totext/index.js'
52
51
  export * as trim from './trim/index.js'
52
+ export * as totext from './totext/index.js'
@@ -1,13 +1,13 @@
1
- export * as any from './any/index.js'
2
1
  export * as array from './array/index.js'
3
- export * as boolean from './boolean/index.js'
2
+ export * as any from './any/index.js'
4
3
  export * as element from './element/index.js'
5
- export * as guess from './guess/index.js'
6
- export * as nodelist from './nodelist/index.js'
7
4
  export * as get from './get/index.js'
5
+ export * as nodelist from './nodelist/index.js'
6
+ export * as guess from './guess/index.js'
8
7
  export * as null from './null/index.js'
8
+ export * as boolean from './boolean/index.js'
9
+ export * as number from './number/index.js'
9
10
  export * as record from './record/index.js'
10
11
  export * as string from './string/index.js'
11
- export * as text from './text/index.js'
12
12
  export * as ref from './ref/index.js'
13
- export * as number from './number/index.js'
13
+ export * as text from './text/index.js'
@@ -1,13 +1,13 @@
1
- export * as any from './any/index.js'
2
1
  export * as array from './array/index.js'
3
- export * as boolean from './boolean/index.js'
2
+ export * as any from './any/index.js'
4
3
  export * as element from './element/index.js'
5
- export * as guess from './guess/index.js'
6
- export * as nodelist from './nodelist/index.js'
7
4
  export * as get from './get/index.js'
5
+ export * as nodelist from './nodelist/index.js'
6
+ export * as guess from './guess/index.js'
8
7
  export * as null from './null/index.js'
8
+ export * as boolean from './boolean/index.js'
9
+ export * as number from './number/index.js'
9
10
  export * as record from './record/index.js'
10
11
  export * as string from './string/index.js'
11
- export * as text from './text/index.js'
12
12
  export * as ref from './ref/index.js'
13
- export * as number from './number/index.js'
13
+ export * as text from './text/index.js'
@@ -1,10 +1,10 @@
1
1
  export * as deepSelect from './deep-select/index.js'
2
2
  export * as getNodeAncestors from './get-node-ancestors/index.js'
3
3
  export * as getPositionInsideParent from './get-position-inside-parent/index.js'
4
- export * as placeholders from './placeholders/index.js'
5
4
  export * as insertNode from './insert-node/index.js'
5
+ export * as hyperJson from './hyper-json/index.js'
6
6
  export * as replaceInElement from './replace-in-element/index.js'
7
7
  export * as selectorToElement from './selector-to-element/index.js'
8
+ export * as placeholders from './placeholders/index.js'
8
9
  export * as stringToNodes from './string-to-nodes/index.js'
9
10
  export * as watchSelection from './watch-selection/index.js'
10
- export * as hyperJson from './hyper-json/index.js'
@@ -1,10 +1,10 @@
1
1
  export * as deepSelect from './deep-select/index.js'
2
2
  export * as getNodeAncestors from './get-node-ancestors/index.js'
3
3
  export * as getPositionInsideParent from './get-position-inside-parent/index.js'
4
- export * as placeholders from './placeholders/index.js'
5
4
  export * as insertNode from './insert-node/index.js'
5
+ export * as hyperJson from './hyper-json/index.js'
6
6
  export * as replaceInElement from './replace-in-element/index.js'
7
7
  export * as selectorToElement from './selector-to-element/index.js'
8
+ export * as placeholders from './placeholders/index.js'
8
9
  export * as stringToNodes from './string-to-nodes/index.js'
9
10
  export * as watchSelection from './watch-selection/index.js'
10
- export * as hyperJson from './hyper-json/index.js'
@@ -1,12 +1,12 @@
1
1
  export * as assert from './assert/index.js'
2
2
  export * as cast from './cast/index.js'
3
- export * as connection from './connection/index.js'
4
3
  export * as crawler from './crawler/index.js'
5
4
  export * as crossenv from './crossenv/index.js'
5
+ export * as connection from './connection/index.js'
6
6
  export * as isConstructorFunction from './is-constructor-function/index.js'
7
+ export * as dataSize from './data-size/index.js'
8
+ export * as isNullish from './is-nullish/index.js'
7
9
  export * as logs from './logs/index.js'
8
10
  export * as loremIpsum from './lorem-ipsum/index.js'
9
11
  export * as normalizeExtension from './normalize-extension/index.js'
10
12
  export * as outcome from './outcome/index.js'
11
- export * as dataSize from './data-size/index.js'
12
- export * as isNullish from './is-nullish/index.js'
@@ -1,12 +1,12 @@
1
1
  export * as assert from './assert/index.js'
2
2
  export * as cast from './cast/index.js'
3
- export * as connection from './connection/index.js'
4
3
  export * as crawler from './crawler/index.js'
5
4
  export * as crossenv from './crossenv/index.js'
5
+ export * as connection from './connection/index.js'
6
6
  export * as isConstructorFunction from './is-constructor-function/index.js'
7
+ export * as dataSize from './data-size/index.js'
8
+ export * as isNullish from './is-nullish/index.js'
7
9
  export * as logs from './logs/index.js'
8
10
  export * as loremIpsum from './lorem-ipsum/index.js'
9
11
  export * as normalizeExtension from './normalize-extension/index.js'
10
12
  export * as outcome from './outcome/index.js'
11
- export * as dataSize from './data-size/index.js'
12
- export * as isNullish from './is-nullish/index.js'
@@ -1,3 +1,3 @@
1
- export * as logger from './logger/index.js'
2
1
  export * as makeTextBlock from './make-text-block/index.js'
2
+ export * as logger from './logger/index.js'
3
3
  export * as styles from './styles/index.js'
@@ -1,3 +1,3 @@
1
- export * as logger from './logger/index.js'
2
1
  export * as makeTextBlock from './make-text-block/index.js'
2
+ export * as logger from './logger/index.js'
3
3
  export * as styles from './styles/index.js'
@@ -1,6 +1,6 @@
1
- export * as clamp from './clamp/index.js'
2
1
  export * as absoluteModulo from './absolute-modulo/index.js'
3
- export * as geometricProgressions from './geometric-progressions/index.js'
4
2
  export * as approximateRational from './approximate-rational/index.js'
5
- export * as round from './round/index.js'
3
+ export * as clamp from './clamp/index.js'
4
+ export * as geometricProgressions from './geometric-progressions/index.js'
6
5
  export * as interpolate from './interpolate/index.js'
6
+ export * as round from './round/index.js'
@@ -1,6 +1,6 @@
1
- export * as clamp from './clamp/index.js'
2
1
  export * as absoluteModulo from './absolute-modulo/index.js'
3
- export * as geometricProgressions from './geometric-progressions/index.js'
4
2
  export * as approximateRational from './approximate-rational/index.js'
5
- export * as round from './round/index.js'
3
+ export * as clamp from './clamp/index.js'
4
+ export * as geometricProgressions from './geometric-progressions/index.js'
6
5
  export * as interpolate from './interpolate/index.js'
6
+ export * as round from './round/index.js'
@@ -4,6 +4,6 @@ export * as flattenGetters from './flatten-getters/index.js'
4
4
  export * as isRecord from './is-record/index.js'
5
5
  export * as recordFormat from './record-format/index.js'
6
6
  export * as isObject from './is-object/index.js'
7
+ export * as recordMap from './record-map/index.js'
7
8
  export * as sortKeys from './sort-keys/index.js'
8
9
  export * as validation from './validation/index.js'
9
- export * as recordMap from './record-map/index.js'
@@ -4,6 +4,6 @@ export * as flattenGetters from './flatten-getters/index.js'
4
4
  export * as isRecord from './is-record/index.js'
5
5
  export * as recordFormat from './record-format/index.js'
6
6
  export * as isObject from './is-object/index.js'
7
+ export * as recordMap from './record-map/index.js'
7
8
  export * as sortKeys from './sort-keys/index.js'
8
9
  export * as validation from './validation/index.js'
9
- export * as recordMap from './record-map/index.js'
@@ -1,2 +1,2 @@
1
- export * as throttleDebounce from './throttle-debounce/index.js'
2
1
  export * as memoize from './memoize/index.js'
2
+ export * as throttleDebounce from './throttle-debounce/index.js'
@@ -1,2 +1,2 @@
1
- export * as throttleDebounce from './throttle-debounce/index.js'
2
1
  export * as memoize from './memoize/index.js'
2
+ export * as throttleDebounce from './throttle-debounce/index.js'
@@ -1,8 +1,8 @@
1
- export * as matches from './matches/index.js'
1
+ export * as charCodes from './char-codes/index.js'
2
2
  export * as normalizeIndent from './normalize-indent/index.js'
3
3
  export * as parseTable from './parse-table/index.js'
4
- export * as splitTrim from './split-trim/index.js'
4
+ export * as matches from './matches/index.js'
5
5
  export * as replaceAll from './replace-all/index.js'
6
- export * as charCodes from './char-codes/index.js'
6
+ export * as splitTrim from './split-trim/index.js'
7
7
  export * as toAlphanum from './to-alphanum/index.js'
8
8
  export * as trim from './trim/index.js'
@@ -1,8 +1,8 @@
1
- export * as matches from './matches/index.js'
1
+ export * as charCodes from './char-codes/index.js'
2
2
  export * as normalizeIndent from './normalize-indent/index.js'
3
3
  export * as parseTable from './parse-table/index.js'
4
- export * as splitTrim from './split-trim/index.js'
4
+ export * as matches from './matches/index.js'
5
5
  export * as replaceAll from './replace-all/index.js'
6
- export * as charCodes from './char-codes/index.js'
6
+ export * as splitTrim from './split-trim/index.js'
7
7
  export * as toAlphanum from './to-alphanum/index.js'
8
8
  export * as trim from './trim/index.js'
@@ -1,5 +1,5 @@
1
1
  export * as duration from './duration/index.js'
2
- export * as dates from './dates/index.js'
3
2
  export * as timeout from './timeout/index.js'
4
- export * as wait from './wait/index.js'
3
+ export * as dates from './dates/index.js'
5
4
  export * as transitions from './transitions/index.js'
5
+ export * as wait from './wait/index.js'
@@ -1,5 +1,5 @@
1
1
  export * as duration from './duration/index.js'
2
- export * as dates from './dates/index.js'
3
2
  export * as timeout from './timeout/index.js'
4
- export * as wait from './wait/index.js'
3
+ export * as dates from './dates/index.js'
5
4
  export * as transitions from './transitions/index.js'
5
+ export * as wait from './wait/index.js'
@@ -7,6 +7,12 @@ import type { WithClassName } from '../utils/types.js';
7
7
  * @property type - MIME type hint for the source (e.g. `'image/webp'`).
8
8
  * @property media - Media condition under which this source is selected (e.g. `'(max-width: 768px)'`).
9
9
  * @property sizes - Sizes attribute forwarded to the `<source>` element.
10
+ *
11
+ * **A picture source is not a video source.** `Video` keeps a shape of its own: a
12
+ * `<source>` inside a `<video>` carries `src` and nothing else useful, where this one
13
+ * carries `srcSet`, `media` and `sizes`, and neither element accepts the other's
14
+ * attributes. What the two do share is how a list of them is read — see
15
+ * `parseSourceList` in `components/utils`, whose `stringKey` is exactly that difference.
10
16
  */
11
17
  type SourceData = {
12
18
  srcSet?: string;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo } from 'react';
3
3
  import { clss } from '../../agnostic/css/clss/index.js';
4
- import { mergeClassNames } from '../utils/index.js';
4
+ import { mergeClassNames, parseSourceList } from '../utils/index.js';
5
5
  import { image as publicClassName } from '../public-classnames.js';
6
6
  import cssModule from './styles.module.css';
7
7
  /**
@@ -19,27 +19,12 @@ import cssModule from './styles.module.css';
19
19
  */
20
20
  export const Image = ({ sources, className, ...intrinsicImgAttributes }) => {
21
21
  // State
22
- const parsedSources = useMemo(() => {
23
- if (sources === undefined)
24
- return [];
25
- if (typeof sources === 'string')
26
- return [{ srcSet: sources }];
27
- if (Array.isArray(sources)) {
28
- if (sources.length === 0)
29
- return [];
30
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- first element sampled just above; array is expected to be homogeneous
31
- if (typeof sources[0] === 'string')
32
- return sources.map(srcSet => ({ srcSet }));
33
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- first element was checked not to be a string just above; array is expected to be homogeneous
34
- return sources;
35
- }
36
- return [];
37
- }, [sources]);
22
+ const parsedSources = useMemo(() => parseSourceList(sources, 'srcSet'), [sources]);
38
23
  // Rendering
39
24
  const c = clss(publicClassName, { cssModule });
40
25
  const rootClss = mergeClassNames(c(), className);
41
26
  const pictureClss = c('picture');
42
27
  const imgClss = c('image');
43
- const pictureContent = _jsxs("picture", { className: pictureClss, children: [parsedSources.map((source, index) => _jsx("source", { srcSet: typeof source === 'string' ? source : source.srcSet, type: typeof source === 'string' ? undefined : source.type, media: typeof source === 'string' ? undefined : source.media, sizes: typeof source === 'string' ? undefined : source.sizes }, index)), _jsx("img", { className: imgClss, ...intrinsicImgAttributes })] });
28
+ const pictureContent = _jsxs("picture", { className: pictureClss, children: [parsedSources.map((source, index) => _jsx("source", { srcSet: source.srcSet, type: source.type, media: source.media, sizes: source.sizes }, index)), _jsx("img", { className: imgClss, ...intrinsicImgAttributes })] });
44
29
  return _jsx("figure", { className: rootClss, children: pictureContent });
45
30
  };
@@ -1,32 +1,13 @@
1
- import { type FunctionComponent, type PropsWithChildren, type Ref, type VideoHTMLAttributes } from 'react';
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 { type SourceData, type TrackData } from './utils.js';
4
5
  /**
5
6
  * Describes a single video source.
6
7
  *
7
8
  * @property src - URL of the video file.
8
9
  * @property type - MIME type of the source (e.g. `'video/mp4'`).
9
10
  */
10
- type SourceData = {
11
- src?: string;
12
- type?: string;
13
- };
14
- /**
15
- * Describes a single text track (subtitles, captions, chapters, etc.).
16
- *
17
- * @property src - URL of the track file.
18
- * @property kind - Track type, maps directly to the `<track>` `kind` attribute.
19
- * @property srclang - Language of the track content (e.g. `'fr'`, `'en'`).
20
- * @property label - Human-readable label shown in the browser's track selector.
21
- * @property default - When `true`, marks this track as the default selection.
22
- */
23
- type TrackData = {
24
- src?: string;
25
- kind?: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata';
26
- srclang?: string;
27
- label?: string;
28
- default?: boolean;
29
- };
30
11
  /**
31
12
  * Props for the ControlledVideo component.
32
13
  *
@@ -114,6 +95,8 @@ type TrackData = {
114
95
  */
115
96
  export type Props = PropsWithChildren<WithClassName<{
116
97
  rootRef?: Ref<HTMLElement>;
98
+ videoRef?: RefObject<HTMLVideoElement | null>;
99
+ togglePlayOnClick?: boolean;
117
100
  sources?: string | string[] | SourceData[];
118
101
  tracks?: string | string[] | TrackData[];
119
102
  subtitles?: SubsProps;
@@ -140,6 +123,7 @@ export type Props = PropsWithChildren<WithClassName<{
140
123
  onFullscreenButtonClicked?: (e: React.MouseEvent<HTMLButtonElement>, isFullscreen: boolean, video: HTMLVideoElement | null) => void;
141
124
  onSubtitlesButtonClicked?: (e: React.MouseEvent<HTMLButtonElement>, isSubtitlesOn: boolean, video: HTMLVideoElement | null) => void;
142
125
  onTimelineClicked?: (e: React.MouseEvent<HTMLDivElement>, targetTime: number, currentTime: number, video: HTMLVideoElement | null) => void;
126
+ onVideoClicked?: (e: React.MouseEvent<HTMLVideoElement>, isPlaying: boolean, video: HTMLVideoElement | null) => void;
143
127
  onIsPlayingChanged?: (isPlaying: boolean) => void;
144
128
  onIsFullscreenChanged?: (isFullscreen: boolean) => void;
145
129
  onIsLoudChanged?: (isLoud: boolean) => void;
@@ -194,4 +178,3 @@ export type Props = PropsWithChildren<WithClassName<{
194
178
  * subtitles.
195
179
  */
196
180
  export declare const ControlledVideo: FunctionComponent<Props>;
197
- export {};
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo, useRef, useState, useCallback, useEffect } from 'react';
3
3
  import { clss } from '../../agnostic/css/clss/index.js';
4
4
  import { formatDuration } from '../../agnostic/time/duration/format-duration/index.js';
5
- import { mergeClassNames, useChangeDispatch } from '../utils/index.js';
5
+ import { mergeClassNames, parseSourceList, useChangeDispatch } from '../utils/index.js';
6
6
  import cssModule from './styles.module.css';
7
7
  import { video as publicClassName } from '../public-classnames.js';
8
8
  import { Subtitles } from '../Subtitles/index.js';
@@ -57,8 +57,9 @@ subtitlesOn = true, mute, muted, volume = 1, playbackRate = 1, currentTimeMs: gi
57
57
  // Milliseconds by default, which is what this component has always rendered — a
58
58
  // default that changes under a consumer is a change nobody asked for. An article
59
59
  // usually wants `'{{mm}}:{{ss}}'` and says so.
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 }) => {
61
- const videoRef = useRef(null);
60
+ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked, onLoudButtonClicked, onMuteButtonClicked, onVolumeRangeChanged, onRateRangeChanged, onFullscreenButtonClicked, onSubtitlesButtonClicked, onTimelineClicked, onVideoClicked, togglePlayOnClick, onIsPlayingChanged, onIsFullscreenChanged, onIsLoudChanged, onIsSubtitlesOnChanged, onIsEndedChanged, onVolumeChanged, onPlaybackRateChanged, onCurrentTimeMsChanged, onFullscreenChange, children, className, rootRef, videoRef: givenVideoRef, ...intrinsicVideoAttributes }) => {
61
+ const ownVideoRef = useRef(null);
62
+ const videoRef = givenVideoRef ?? ownVideoRef;
62
63
  const [totalTime, setTotalTime] = useState(0);
63
64
  const totalTimeMs = useMemo(() => secondsToMs(totalTime), [totalTime]);
64
65
  const [internalCurrentTimeMs, setInternalCurrentTimeMs] = useState(0);
@@ -121,6 +122,20 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
121
122
  const wasPlaying = videoRef.current?.paused === false;
122
123
  onPlayButtonClicked?.(e, wasPlaying, videoRef.current);
123
124
  }, [onPlayButtonClicked]);
125
+ // The picture as a play/pause surface. It reports and nothing more, like every other
126
+ // control here: the uncontrolled layer above owns the play state.
127
+ //
128
+ // The listener sits on the element and not on the `<figure>`, so the controls painted
129
+ // over it are not part of the surface — a click on pause would otherwise toggle twice
130
+ // and cancel itself. No `role` and no `tabIndex` either: this is a **second** way to
131
+ // do what the play button already does, and giving it a focus stop would put the same
132
+ // action twice in the tab order.
133
+ const handleVideoClick = useCallback((e) => {
134
+ if (togglePlayOnClick !== true)
135
+ return;
136
+ const wasPlaying = videoRef.current?.paused === false;
137
+ onVideoClicked?.(e, wasPlaying, videoRef.current);
138
+ }, [togglePlayOnClick, onVideoClicked]);
124
139
  const handlePauseButtonClick = useCallback((e) => {
125
140
  const wasPlaying = videoRef.current?.paused === false;
126
141
  onPauseButtonClicked?.(e, wasPlaying, videoRef.current);
@@ -170,7 +185,11 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
170
185
  // caché, c'est l'absence d'un état. Une feuille qui veut viser ce cas le reconnaît à
171
186
  // ce qu'aucun des deux modifieurs n'est là.
172
187
  'subtitles-on': subtitles !== undefined && subtitlesOn,
173
- 'subtitles-off': subtitles !== undefined && !subtitlesOn
188
+ 'subtitles-off': subtitles !== undefined && !subtitlesOn,
189
+ // A capability rather than a state, and it is here for the same reason the lightbox
190
+ // carries `--open-on-click`: a surface that answers a click has to be able to say so
191
+ // with a cursor.
192
+ 'toggle-play-on-click': togglePlayOnClick === true
174
193
  }), className);
175
194
  // Guarded: the duration is unknown until the metadata lands, and an unguarded
176
195
  // division would expose the string 'NaN' on every render until then.
@@ -190,7 +209,8 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
190
209
  'data-playback-rate': playbackRate,
191
210
  'data-current-time-ms': currentTimeMs.toFixed(2),
192
211
  'data-current-time-ratio': currentTimeRatio.toFixed(8),
193
- 'data-total-time-ms': totalTimeMs
212
+ 'data-total-time-ms': totalTimeMs,
213
+ 'data-toggle-play-on-click': togglePlayOnClick === true ? '' : undefined
194
214
  };
195
215
  const rootStyles = {
196
216
  '--lm-video-current-time': `${currentTimeMs}ms`,
@@ -201,38 +221,8 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
201
221
  '--lm-video-volume-ratio': `${volume}`,
202
222
  '--lm-video-playback-rate': `${playbackRate}`
203
223
  };
204
- const parsedSources = useMemo(() => {
205
- if (sources === undefined)
206
- return [];
207
- if (typeof sources === 'string')
208
- return [{ src: sources }];
209
- if (Array.isArray(sources)) {
210
- if (sources.length === 0)
211
- return [];
212
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- first element sampled just above; array is expected to be homogeneous
213
- if (typeof sources[0] === 'string')
214
- return sources.map(src => ({ src }));
215
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- first element was checked not to be a string just above; array is expected to be homogeneous
216
- return sources;
217
- }
218
- return [];
219
- }, [sources]);
220
- const parsedTracks = useMemo(() => {
221
- if (tracks === undefined)
222
- return [];
223
- if (typeof tracks === 'string')
224
- return [{ src: tracks }];
225
- if (Array.isArray(tracks)) {
226
- if (tracks.length === 0)
227
- return [];
228
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- first element sampled just above; array is expected to be homogeneous
229
- if (typeof tracks[0] === 'string')
230
- return tracks.map(src => ({ src }));
231
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- first element was checked not to be a string just above; array is expected to be homogeneous
232
- return tracks;
233
- }
234
- return [];
235
- }, [tracks]);
224
+ const parsedSources = useMemo(() => parseSourceList(sources, 'src'), [sources]);
225
+ const parsedTracks = useMemo(() => parseSourceList(tracks, 'src'), [tracks]);
236
226
  const videoClss = c('video');
237
227
  const videoControlsClss = c('video-controls');
238
228
  const playBtnClss = c('play-btn');
@@ -318,9 +308,5 @@ timeFormat = '{{mm}}:{{ss}}:{{ms}}', onPlayButtonClicked, onPauseButtonClicked,
318
308
  useChangeDispatch(isEnded, onIsEndedChanged);
319
309
  useChangeDispatch(volume, onVolumeChanged);
320
310
  useChangeDispatch(playbackRate, onPlaybackRateChanged);
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'
322
- ? _jsx("source", { src: source }, index)
323
- : _jsx("source", { src: source.src, type: source.type }, index)), parsedTracks.map((track, index) => typeof track === 'string'
324
- ? _jsx("track", { src: track }, index)
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
+ 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 })] });
326
312
  };