@design-edito/tools 0.5.13 → 0.5.15

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 (62) hide show
  1. package/agnostic/colors/index.d.ts +1 -1
  2. package/agnostic/colors/index.js +1 -1
  3. package/agnostic/css/index.d.ts +2 -2
  4. package/agnostic/css/index.js +2 -2
  5. package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +14 -14
  6. package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +14 -14
  7. package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +3 -3
  8. package/agnostic/html/hyper-json/smart-tags/isolated/index.js +3 -3
  9. package/agnostic/html/index.d.ts +2 -2
  10. package/agnostic/html/index.js +2 -2
  11. package/agnostic/index.d.ts +2 -2
  12. package/agnostic/index.js +2 -2
  13. package/agnostic/misc/index.d.ts +3 -3
  14. package/agnostic/misc/index.js +3 -3
  15. package/agnostic/numbers/index.d.ts +3 -3
  16. package/agnostic/numbers/index.js +3 -3
  17. package/agnostic/objects/index.d.ts +3 -3
  18. package/agnostic/objects/index.js +3 -3
  19. package/agnostic/optim/index.d.ts +1 -1
  20. package/agnostic/optim/index.js +1 -1
  21. package/agnostic/strings/index.d.ts +4 -4
  22. package/agnostic/strings/index.js +4 -4
  23. package/agnostic/time/dates/format-date/index.d.ts +19 -4
  24. package/agnostic/time/dates/format-date/index.js +24 -7
  25. package/agnostic/time/dates/format-date/index.test.js +20 -7
  26. package/agnostic/time/dates/format-date/parts.js +4 -4
  27. package/agnostic/time/dates/format-date/types.d.ts +9 -9
  28. package/agnostic/time/duration/format-duration/index.d.ts +18 -1
  29. package/agnostic/time/duration/format-duration/index.js +44 -7
  30. package/agnostic/time/duration/format-duration/index.test.js +27 -0
  31. package/agnostic/time/duration/format-duration/types.d.ts +21 -1
  32. package/agnostic/time/index.d.ts +1 -1
  33. package/agnostic/time/index.js +1 -1
  34. package/components/Video/index.controlled.d.ts +12 -0
  35. package/components/Video/index.controlled.js +8 -3
  36. package/components/Video/utils.d.ts +0 -29
  37. package/components/Video/utils.js +0 -51
  38. package/components/index.d.ts +2 -2
  39. package/components/index.js +2 -2
  40. package/node/@aws-s3/storage/directory/index.d.ts +1 -1
  41. package/node/@aws-s3/storage/directory/index.js +1 -1
  42. package/node/@aws-s3/storage/file/index.d.ts +1 -1
  43. package/node/@aws-s3/storage/file/index.js +1 -1
  44. package/node/@aws-s3/storage/index.d.ts +1 -1
  45. package/node/@aws-s3/storage/index.js +1 -1
  46. package/node/@google-cloud/storage/directory/index.d.ts +1 -1
  47. package/node/@google-cloud/storage/directory/index.js +1 -1
  48. package/node/@google-cloud/storage/file/index.d.ts +2 -2
  49. package/node/@google-cloud/storage/file/index.js +2 -2
  50. package/node/cloud-storage/operations/index.d.ts +3 -3
  51. package/node/cloud-storage/operations/index.js +3 -3
  52. package/node/files/index.d.ts +1 -1
  53. package/node/files/index.js +1 -1
  54. package/node/ftps/directory/index.d.ts +1 -1
  55. package/node/ftps/directory/index.js +1 -1
  56. package/node/ftps/file/index.d.ts +2 -2
  57. package/node/ftps/file/index.js +2 -2
  58. package/node/images/transform/operations/index.d.ts +4 -4
  59. package/node/images/transform/operations/index.js +4 -4
  60. package/node/sftp/directory/index.d.ts +1 -1
  61. package/node/sftp/directory/index.js +1 -1
  62. package/package.json +1 -1
@@ -19,14 +19,31 @@ import type { FormatDurationOptions } from './types.js';
19
19
  * - `m` / `mm` : Minutes
20
20
  * - `s` / `ss` : Seconds
21
21
  * - `ms` : Milliseconds, padded to three digits
22
+ * - `f` / `ff` : The millisecond part counted in frames, at `options.fps`
22
23
  *
23
24
  * @param duration - The duration to format, as a {@link Duration} or a number of
24
25
  * milliseconds.
25
26
  * @param format - The template string containing tokens.
26
- * @param options - How to handle the remainder and the month/year approximation.
27
+ * @param options - How to handle the remainder, the month/year approximation and
28
+ * the frame rate.
27
29
  * @returns Formatted duration string. An unknown token is left untouched, braces
28
30
  * included.
29
31
  * @see {@link getDurationParts} to reach the same parts as plain values.
32
+ * @see {@link formatDate} — the same template grammar, for a point in time rather
33
+ * than a length of it.
34
+ *
35
+ * @remarks
36
+ * This function and `formatDate` share one grammar: `{{…}}` delimiters, everything
37
+ * outside them literal, the bare token being the plain number and the doubled one
38
+ * that same number padded to two digits, `ms` padded to three, and an unknown token
39
+ * left as written. No token means two different kinds of thing across the two.
40
+ *
41
+ * @remarks
42
+ * **The template decides the breakdown, so nothing is ever lost for not having been
43
+ * asked for.** `'{{w}}w {{h}}h'` counts hours up to 167 because the week is there and
44
+ * the day is not; `'{{mm}}:{{ss}}'` on an hour-long video reads `62:05` rather than
45
+ * dropping the hour. The single-unit case follows from the same rule: `'{{s}}s'`
46
+ * carries the whole duration.
30
47
  *
31
48
  * @remarks
32
49
  * A negative duration yields negative parts, so every token renders a signed
@@ -1,8 +1,14 @@
1
1
  import { getDurationParts } from './parts.js';
2
2
  // Alternation is ordered, and the first branch that fits wins — so the longest
3
3
  // tokens come first. `m` ahead of `ms` or `mm` would swallow them.
4
- const tokenRegexp = /\{\{(YY|MM|ww|dd|hh|mm|ss|ms|Y|M|w|d|h|m|s)\}\}/gv;
5
- /** Which tokens read which unit, longest unit first. */
4
+ const tokenRegexp = /\{\{(YY|MM|ww|dd|hh|mm|ss|ms|ff|Y|M|w|d|h|m|s|f)\}\}/gv;
5
+ /**
6
+ * Which tokens read which unit, longest unit first.
7
+ *
8
+ * `f` and `ff` sit under `ms` rather than under a unit of their own: a frame is
9
+ * the millisecond part counted in another base, so a template asking for frames
10
+ * is a template asking the cascade to run down to milliseconds.
11
+ */
6
12
  const unitTokens = [
7
13
  ['Y', ['Y', 'YY']],
8
14
  ['M', ['M', 'MM']],
@@ -11,7 +17,7 @@ const unitTokens = [
11
17
  ['h', ['h', 'hh']],
12
18
  ['m', ['m', 'mm']],
13
19
  ['s', ['s', 'ss']],
14
- ['ms', ['ms']]
20
+ ['ms', ['ms', 'f', 'ff']]
15
21
  ];
16
22
  function pad(value, length) {
17
23
  return `${value}`.padStart(length, '0');
@@ -36,8 +42,22 @@ const renderers = {
36
42
  'mm': parts => pad(parts.m, 2),
37
43
  's': parts => `${parts.s}`,
38
44
  'ss': parts => pad(parts.s, 2),
39
- 'ms': parts => pad(parts.ms, 3)
45
+ 'ms': parts => pad(parts.ms, 3),
46
+ 'f': (parts, fps) => `${toFrames(parts.ms, fps)}`,
47
+ 'ff': (parts, fps) => pad(toFrames(parts.ms, fps), 2)
40
48
  };
49
+ /**
50
+ * The millisecond part counted in frames.
51
+ *
52
+ * Truncated rather than rounded: a frame is only reached once it has fully
53
+ * elapsed, so 39 ms at 25 fps is frame `0`, not frame `1`. The sign is carried
54
+ * around the truncation, so a negative part stays negative — the whole function
55
+ * keeps a duration's parts faithful rather than tidying them.
56
+ */
57
+ function toFrames(ms, fps) {
58
+ const sign = ms < 0 ? -1 : 1;
59
+ return Math.floor(Math.abs(ms) / 1000 * fps) * sign;
60
+ }
41
61
  /** The units a template asks for, read off the tokens it actually uses. */
42
62
  function unitsInFormat(format) {
43
63
  return unitTokens
@@ -63,14 +83,31 @@ function unitsInFormat(format) {
63
83
  * - `m` / `mm` : Minutes
64
84
  * - `s` / `ss` : Seconds
65
85
  * - `ms` : Milliseconds, padded to three digits
86
+ * - `f` / `ff` : The millisecond part counted in frames, at `options.fps`
66
87
  *
67
88
  * @param duration - The duration to format, as a {@link Duration} or a number of
68
89
  * milliseconds.
69
90
  * @param format - The template string containing tokens.
70
- * @param options - How to handle the remainder and the month/year approximation.
91
+ * @param options - How to handle the remainder, the month/year approximation and
92
+ * the frame rate.
71
93
  * @returns Formatted duration string. An unknown token is left untouched, braces
72
94
  * included.
73
95
  * @see {@link getDurationParts} to reach the same parts as plain values.
96
+ * @see {@link formatDate} — the same template grammar, for a point in time rather
97
+ * than a length of it.
98
+ *
99
+ * @remarks
100
+ * This function and `formatDate` share one grammar: `{{…}}` delimiters, everything
101
+ * outside them literal, the bare token being the plain number and the doubled one
102
+ * that same number padded to two digits, `ms` padded to three, and an unknown token
103
+ * left as written. No token means two different kinds of thing across the two.
104
+ *
105
+ * @remarks
106
+ * **The template decides the breakdown, so nothing is ever lost for not having been
107
+ * asked for.** `'{{w}}w {{h}}h'` counts hours up to 167 because the week is there and
108
+ * the day is not; `'{{mm}}:{{ss}}'` on an hour-long video reads `62:05` rather than
109
+ * dropping the hour. The single-unit case follows from the same rule: `'{{s}}s'`
110
+ * carries the whole duration.
74
111
  *
75
112
  * @remarks
76
113
  * A negative duration yields negative parts, so every token renders a signed
@@ -84,11 +121,11 @@ function unitsInFormat(format) {
84
121
  * // => "1:02:05"
85
122
  */
86
123
  export function formatDuration(duration, format, options = {}) {
87
- const { floorSmallestUnit = true, useApproximateMonthAndYear = true } = options;
124
+ const { floorSmallestUnit = true, useApproximateMonthAndYear = true, fps = 25 } = options;
88
125
  const parts = getDurationParts(duration, {
89
126
  units: unitsInFormat(format),
90
127
  floorSmallestUnit,
91
128
  useApproximateMonthAndYear
92
129
  });
93
- return format.replace(tokenRegexp, (_match, token) => renderers[token](parts));
130
+ return format.replace(tokenRegexp, (_match, token) => renderers[token](parts, fps));
94
131
  }
@@ -53,6 +53,33 @@ describe('formatDuration', () => {
53
53
  expect(formatDuration(seconds(-90), '{{m}}:{{ss}}')).toBe('-1:-30');
54
54
  });
55
55
  });
56
+ describe('frames', () => {
57
+ it('counts the sub-second remainder in frames, at 25 fps by default', () => {
58
+ expect(formatDuration(3725240, '{{mm}}:{{ss}}.{{ff}}')).toBe('62:05.06');
59
+ expect(formatDuration(3725240, '{{mm}}:{{ss}}.{{f}}')).toBe('62:05.6');
60
+ });
61
+ it('counts in the frame rate it is given', () => {
62
+ expect(formatDuration(3725240, '{{ss}}.{{ff}}', { fps: 50 })).toBe('3725.12');
63
+ });
64
+ // A frame is reached once it has fully elapsed, so the 40th millisecond is
65
+ // where frame 1 starts at 25 fps — not the 39th.
66
+ it('truncates rather than rounds to the frame', () => {
67
+ expect(formatDuration(39, '{{s}}.{{ff}}')).toBe('0.00');
68
+ expect(formatDuration(40, '{{s}}.{{ff}}')).toBe('0.01');
69
+ });
70
+ // Frames are milliseconds in another base, so they pull the cascade down to
71
+ // milliseconds exactly as `{{ms}}` would — without needing `{{ms}}` written.
72
+ it('runs the cascade down to milliseconds without a ms token', () => {
73
+ expect(formatDuration(3725240, '{{ss}}')).toBe('3725');
74
+ expect(formatDuration(3725240, '{{ss}}.{{ff}}')).toBe('3725.06');
75
+ });
76
+ it('puts the whole duration on frames when it is the only token', () => {
77
+ expect(formatDuration(seconds(2), '{{f}}')).toBe('50');
78
+ });
79
+ it('signs a frame count like every other part', () => {
80
+ expect(formatDuration(-240, '{{s}}.{{f}}')).toBe('0.-6');
81
+ });
82
+ });
56
83
  });
57
84
  describe('getDurationParts', () => {
58
85
  describe('breakdown', () => {
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export type DurationUnit = 'ms' | 's' | 'm' | 'h' | 'd' | 'w' | 'M' | 'Y';
6
6
  /** Every token `formatDuration` substitutes in a template. */
7
- export type DurationToken = 'Y' | 'YY' | 'M' | 'MM' | 'w' | 'ww' | 'd' | 'dd' | 'h' | 'hh' | 'm' | 'mm' | 's' | 'ss' | 'ms';
7
+ export type DurationToken = 'Y' | 'YY' | 'M' | 'MM' | 'w' | 'ww' | 'd' | 'dd' | 'h' | 'hh' | 'm' | 'mm' | 's' | 'ss' | 'ms' | 'f' | 'ff';
8
8
  /** Options for `getDurationParts`. */
9
9
  export type GetDurationPartsOptions = {
10
10
  /**
@@ -25,6 +25,16 @@ export type GetDurationPartsOptions = {
25
25
  * 365.2425 days and a month of exactly a twelfth of it.
26
26
  */
27
27
  useApproximateMonthAndYear?: boolean;
28
+ /**
29
+ * Frame rate the `f` and `ff` tokens count in. Defaults to `25`.
30
+ *
31
+ * Frames are a *rendering* of the millisecond part, not a unit of their own —
32
+ * they never appear in {@link DurationParts}, and a template asking for them
33
+ * asks for milliseconds under another base. So `'{{ss}}.{{ff}}'` reads the
34
+ * sub-second remainder, while `'{{f}}'` alone puts the whole duration on
35
+ * frames, exactly as `'{{ms}}'` alone puts it on milliseconds.
36
+ */
37
+ fps?: number;
28
38
  };
29
39
  /**
30
40
  * A duration's parts, keyed by unit token.
@@ -90,4 +100,14 @@ export type FormatDurationOptions = {
90
100
  * 365.2425 days and a month of exactly a twelfth of it.
91
101
  */
92
102
  useApproximateMonthAndYear?: boolean;
103
+ /**
104
+ * Frame rate the `f` and `ff` tokens count in. Defaults to `25`.
105
+ *
106
+ * Frames are a *rendering* of the millisecond part, not a unit of their own —
107
+ * they never appear in {@link DurationParts}, and a template asking for them
108
+ * asks for milliseconds under another base. So `'{{ss}}.{{ff}}'` reads the
109
+ * sub-second remainder, while `'{{f}}'` alone puts the whole duration on
110
+ * frames, exactly as `'{{ms}}'` alone puts it on milliseconds.
111
+ */
112
+ fps?: number;
93
113
  };
@@ -1,5 +1,5 @@
1
+ export * as timeout from './timeout/index.js'
1
2
  export * as dates from './dates/index.js'
2
3
  export * as duration from './duration/index.js'
3
- export * as timeout from './timeout/index.js'
4
4
  export * as transitions from './transitions/index.js'
5
5
  export * as wait from './wait/index.js'
@@ -1,5 +1,5 @@
1
+ export * as timeout from './timeout/index.js'
1
2
  export * as dates from './dates/index.js'
2
3
  export * as duration from './duration/index.js'
3
- export * as timeout from './timeout/index.js'
4
4
  export * as transitions from './transitions/index.js'
5
5
  export * as wait from './wait/index.js'
@@ -85,6 +85,17 @@ type TrackData = {
85
85
  * fullscreen on its own — pressing Escape, typically — with the new state. This is
86
86
  * the signal a parent needs to update its `fullscreen` prop, and it is distinct
87
87
  * from `onIsFullscreenChanged`, which merely echoes that prop back once changed.
88
+ * @property timeFormat - How the two clocks are written, as a {@link formatDuration}
89
+ * pattern. Tokens go in `{{…}}` and the rest is literal, so `'{{m}} min {{ss}}'` is a
90
+ * valid answer. Defaults to `'{{mm}}:{{ss}}:{{ms}}'` — milliseconds included, which is
91
+ * what a cutting room wants and almost never what an article does: `'{{mm}}:{{ss}}'` is
92
+ * the usual one. Frames (`{{f}}`, `{{ff}}`) are derived at 25 fps, not configurable
93
+ * here.
94
+ *
95
+ * The pattern decides how the duration is split: the units it names are the units it
96
+ * gets, each taking its whole part and handing the rest down. So `'{{mm}}:{{ss}}'` on
97
+ * an hour-long video reads `62:05` rather than dropping the hour — nothing is ever
98
+ * lost for not having been asked for.
88
99
  * @property className - Additional CSS class for the root element.
89
100
  * @property children - React content inserted into the <video> tag (fallback, etc).
90
101
  *
@@ -105,6 +116,7 @@ export type Props = PropsWithChildren<WithClassName<{
105
116
  mute?: boolean;
106
117
  playbackRate?: number;
107
118
  currentTimeMs?: number;
119
+ timeFormat?: string;
108
120
  onPlayButtonClicked?: (e: React.MouseEvent<HTMLButtonElement>, isPlaying: boolean, video: HTMLVideoElement | null) => void;
109
121
  onPauseButtonClicked?: (e: React.MouseEvent<HTMLButtonElement>, isPlaying: boolean, video: HTMLVideoElement | null) => void;
110
122
  onLoudButtonClicked?: (e: React.MouseEvent<HTMLButtonElement>, isLoud: boolean, video: HTMLVideoElement | null) => void;
@@ -1,11 +1,12 @@
1
1
  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
+ import { formatDuration } from '../../agnostic/time/duration/format-duration/index.js';
4
5
  import { mergeClassNames, useChangeDispatch } from '../utils/index.js';
5
6
  import cssModule from './styles.module.css';
6
7
  import { video as publicClassName } from '../public-classnames.js';
7
8
  import { Subtitles } from '../Subtitles/index.js';
8
- import { forceExitFullscreen, forceFullscreen, forceLoud, forceMute, forcePause, forcePlay, forcePlaybackRate, forceVolume, formatTime, getTimelineClickProgress, msToSeconds, secondsToMs } from './utils.js';
9
+ import { forceExitFullscreen, forceFullscreen, forceLoud, forceMute, forcePause, forcePlay, forcePlaybackRate, forceVolume, getTimelineClickProgress, msToSeconds, secondsToMs } from './utils.js';
9
10
  /**
10
11
  * Full-featured video player component. Wraps a native `<video>` element with
11
12
  * playback controls, volume, playback rate, a timeline, optional subtitles
@@ -49,7 +50,11 @@ import { forceExitFullscreen, forceFullscreen, forceLoud, forceMute, forcePause,
49
50
  * @returns A `<figure>` element containing the video, its controls, optional
50
51
  * subtitles.
51
52
  */
52
- export const ControlledVideo = ({ sources, tracks, subtitles, playBtnContent, pauseBtnContent, loudBtnContent, muteBtnContent, fullscreenBtnContent, play, fullscreen, mute, muted, volume = 1, playbackRate = 1, currentTimeMs: givenCurrentTimeMs, onPlayButtonClicked, onPauseButtonClicked, onLoudButtonClicked, onMuteButtonClicked, onVolumeRangeChanged, onRateRangeChanged, onFullscreenButtonClicked, onTimelineClicked, onIsPlayingChanged, onIsFullscreenChanged, onIsLoudChanged, onIsEndedChanged, onVolumeChanged, onPlaybackRateChanged, onCurrentTimeMsChanged, onFullscreenChange, children, className, ...intrinsicVideoAttributes }) => {
53
+ export const ControlledVideo = ({ sources, tracks, subtitles, playBtnContent, pauseBtnContent, loudBtnContent, muteBtnContent, fullscreenBtnContent, play, fullscreen, mute, muted, volume = 1, playbackRate = 1, currentTimeMs: givenCurrentTimeMs,
54
+ // Milliseconds by default, which is what this component has always rendered — a
55
+ // default that changes under a consumer is a change nobody asked for. An article
56
+ // 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 }) => {
53
58
  const videoRef = useRef(null);
54
59
  const [totalTime, setTotalTime] = useState(0);
55
60
  const totalTimeMs = useMemo(() => secondsToMs(totalTime), [totalTime]);
@@ -302,5 +307,5 @@ export const ControlledVideo = ({ sources, tracks, subtitles, playBtnContent, pa
302
307
  ? _jsx("source", { src: source }, index)
303
308
  : _jsx("source", { src: source.src, type: source.type }, index)), parsedTracks.map((track, index) => typeof track === 'string'
304
309
  ? _jsx("track", { src: track }, index)
305
- : _jsx("track", { src: track.src, kind: track.kind, srcLang: track.srclang, label: track.label, default: track.default }, index)), children] }), _jsxs("div", { className: videoControlsClss, children: [_jsx("button", { className: playBtnClss, onClick: handlePlayButtonClick, children: playBtnContent }), _jsx("button", { className: pauseBtnClss, onClick: handlePauseButtonClick, children: pauseBtnContent }), _jsx("button", { className: loudBtnClss, onClick: handleLoudButtonClick, children: loudBtnContent }), _jsx("button", { className: muteBtnClss, onClick: handleMuteButtonClick, children: muteBtnContent }), _jsx("input", { type: 'range', className: volumeRangeClss, value: volumePercent, onChange: handleVolumeRangeChange, min: 0, max: 100, step: 1 }), _jsx("span", { className: volumePcntClss, children: Math.round(volumePercent) }), _jsx("button", { className: fullscreenBtnClss, onClick: handleFullscreenButtonClick, children: fullscreenBtnContent }), _jsx("input", { type: 'range', className: playbackRateRangeClss, value: playbackRate, onChange: handleRateRangeChange, min: 0.25, max: 4, step: 0.25 }), _jsx("span", { className: playbackRateClss, children: playbackRate })] }), _jsxs("div", { className: timeControlsClss, children: [_jsx("span", { className: currentTimeClss, children: formatTime(currentTimeMs, 'mm:ss:ms') }), _jsx("span", { className: totalTimeClss, children: formatTime(totalTimeMs, 'mm:ss:ms') }), _jsx("div", { className: timelineClss, onClick: handleTimelineClick })] }), subtitles !== undefined && _jsx(Subtitles, { ...subtitles, timecodeMs: currentTimeMs, isEnded: isEnded })] });
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 })] });
306
311
  };
@@ -20,35 +20,6 @@ export declare const forceFullscreen: (video: HTMLVideoElement | null) => Promis
20
20
  export declare const forceExitFullscreen: (video: HTMLVideoElement | null) => Promise<boolean>;
21
21
  export declare function secondsToMs(seconds: number): number;
22
22
  export declare function msToSeconds(ms: number): number;
23
- /**
24
- * Renders a duration into a caller-defined pattern.
25
- *
26
- * The pattern is a free-form string in which the following tokens are replaced,
27
- * longest first, so that `mm` is consumed before `m`:
28
- *
29
- * - `hh`, `mm`, `ss` — hours, minutes and seconds, zero-padded to two digits.
30
- * - `ms` — the remaining milliseconds, zero-padded to three digits.
31
- * - `frame` — the remaining milliseconds expressed in frames, zero-padded to two
32
- * digits.
33
- * - `h`, `m`, `s`, `f` — the same four values, unpadded.
34
- *
35
- * @param ms - The duration, in milliseconds.
36
- * @param format - The pattern to fill, e.g. `'mm:ss:ms'`.
37
- * @param fps - Frame rate used to derive the `frame` and `f` tokens. Defaults to `25`.
38
- * @returns The formatted duration.
39
- *
40
- * @remarks
41
- * Every field is a remainder of the one above it, never a total: at one hour and
42
- * a half, `mm` is `30`, not `90`. A pattern that omits `hh` therefore loses the
43
- * hours rather than folding them into the minutes.
44
- *
45
- * Since `h`, `m`, `s` and `f` are tokens on their own, **every** occurrence of
46
- * those letters is substituted, wherever it sits: `'mm min ss'` renders as
47
- * `'01 1in 01'`. Separate the fields with punctuation. This is what
48
- * `agnostic/time/dates/format-date` avoids by delimiting its tokens with
49
- * `{{…}}` — worth aligning on the day this one is made public.
50
- */
51
- export declare function formatTime(ms: number, format: string, fps?: number): string;
52
23
  /**
53
24
  * Where along the timeline a click landed.
54
25
  *
@@ -107,57 +107,6 @@ export function secondsToMs(seconds) {
107
107
  export function msToSeconds(ms) {
108
108
  return ms / 1000;
109
109
  }
110
- /**
111
- * Renders a duration into a caller-defined pattern.
112
- *
113
- * The pattern is a free-form string in which the following tokens are replaced,
114
- * longest first, so that `mm` is consumed before `m`:
115
- *
116
- * - `hh`, `mm`, `ss` — hours, minutes and seconds, zero-padded to two digits.
117
- * - `ms` — the remaining milliseconds, zero-padded to three digits.
118
- * - `frame` — the remaining milliseconds expressed in frames, zero-padded to two
119
- * digits.
120
- * - `h`, `m`, `s`, `f` — the same four values, unpadded.
121
- *
122
- * @param ms - The duration, in milliseconds.
123
- * @param format - The pattern to fill, e.g. `'mm:ss:ms'`.
124
- * @param fps - Frame rate used to derive the `frame` and `f` tokens. Defaults to `25`.
125
- * @returns The formatted duration.
126
- *
127
- * @remarks
128
- * Every field is a remainder of the one above it, never a total: at one hour and
129
- * a half, `mm` is `30`, not `90`. A pattern that omits `hh` therefore loses the
130
- * hours rather than folding them into the minutes.
131
- *
132
- * Since `h`, `m`, `s` and `f` are tokens on their own, **every** occurrence of
133
- * those letters is substituted, wherever it sits: `'mm min ss'` renders as
134
- * `'01 1in 01'`. Separate the fields with punctuation. This is what
135
- * `agnostic/time/dates/format-date` avoids by delimiting its tokens with
136
- * `{{…}}` — worth aligning on the day this one is made public.
137
- */
138
- export function formatTime(ms, format, fps = 25) {
139
- const totalSeconds = Math.floor(ms / 1000);
140
- const hours = Math.floor(totalSeconds / 3600);
141
- const minutes = Math.floor((totalSeconds % 3600) / 60);
142
- const seconds = Math.floor(totalSeconds % 60);
143
- const frames = Math.floor(((ms % 1000) / 1000) * fps);
144
- const msRest = Math.floor(ms % 1000);
145
- const tokens = {
146
- hh: String(hours).padStart(2, '0'),
147
- mm: String(minutes).padStart(2, '0'),
148
- ss: String(seconds).padStart(2, '0'),
149
- frame: String(frames).padStart(2, '0'),
150
- ms: String(msRest).padStart(3, '0'),
151
- h: hours,
152
- m: minutes,
153
- s: seconds,
154
- f: frames
155
- };
156
- return Object
157
- .keys(tokens)
158
- .sort((a, b) => b.length - a.length)
159
- .reduce((acc, t) => acc.replace(new RegExp(t, 'gv'), String(tokens[t])), format);
160
- }
161
110
  /**
162
111
  * Where along the timeline a click landed.
163
112
  *
@@ -8,8 +8,8 @@ export * as gallery from './Gallery/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 intersectionObserver from './IntersectionObserver/index.js'
12
11
  export * as jsonEditor from './JsonEditor/index.js'
12
+ export * as intersectionObserver from './IntersectionObserver/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 select from './Select/index.js'
21
20
  export * as sequencer from './Sequencer/index.js'
21
+ export * as select from './Select/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'
@@ -8,8 +8,8 @@ export * as gallery from './Gallery/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 intersectionObserver from './IntersectionObserver/index.js'
12
11
  export * as jsonEditor from './JsonEditor/index.js'
12
+ export * as intersectionObserver from './IntersectionObserver/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 select from './Select/index.js'
21
20
  export * as sequencer from './Sequencer/index.js'
21
+ export * as select from './Select/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,4 +1,4 @@
1
1
  export * as copyDir from './copy-dir/index.js'
2
2
  export * as list from './list/index.js'
3
- export * as moveDir from './move-dir/index.js'
4
3
  export * as removeDir from './remove-dir/index.js'
4
+ export * as moveDir from './move-dir/index.js'
@@ -1,4 +1,4 @@
1
1
  export * as copyDir from './copy-dir/index.js'
2
2
  export * as list from './list/index.js'
3
- export * as moveDir from './move-dir/index.js'
4
3
  export * as removeDir from './remove-dir/index.js'
4
+ export * as moveDir from './move-dir/index.js'
@@ -1,5 +1,5 @@
1
- export * as copy from './copy/index.js'
2
1
  export * as download from './download/index.js'
2
+ export * as copy from './copy/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 copy from './copy/index.js'
2
1
  export * as download from './download/index.js'
2
+ export * as copy from './copy/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,2 +1,2 @@
1
- export * as directory from './directory/index.js'
2
1
  export * as file from './file/index.js'
2
+ export * as directory from './directory/index.js'
@@ -1,2 +1,2 @@
1
- export * as directory from './directory/index.js'
2
1
  export * as file from './file/index.js'
2
+ export * as directory from './directory/index.js'
@@ -1,4 +1,4 @@
1
1
  export * as copyDir from './copy-dir/index.js'
2
- export * as moveDir from './move-dir/index.js'
3
2
  export * as list from './list/index.js'
3
+ export * as moveDir from './move-dir/index.js'
4
4
  export * as removeDir from './remove-dir/index.js'
@@ -1,4 +1,4 @@
1
1
  export * as copyDir from './copy-dir/index.js'
2
- export * as moveDir from './move-dir/index.js'
3
2
  export * as list from './list/index.js'
3
+ export * as moveDir from './move-dir/index.js'
4
4
  export * as removeDir from './remove-dir/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 exists from './exists/index.js'
4
3
  export * as generateSignedUrl from './generate-signed-url/index.js'
4
+ export * as exists from './exists/index.js'
5
5
  export * as getPermissions from './get-permissions/index.js'
6
6
  export * as getMetadata from './get-metadata/index.js'
7
- export * as move from './move/index.js'
8
7
  export * as remove from './remove/index.js'
8
+ export * as move from './move/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 exists from './exists/index.js'
4
3
  export * as generateSignedUrl from './generate-signed-url/index.js'
4
+ export * as exists from './exists/index.js'
5
5
  export * as getPermissions from './get-permissions/index.js'
6
6
  export * as getMetadata from './get-metadata/index.js'
7
- export * as move from './move/index.js'
8
7
  export * as remove from './remove/index.js'
8
+ export * as move from './move/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 copyDir from './copy-dir/index.js'
2
- export * as downloadFile from './download-file/index.js'
3
2
  export * as copyFile from './copy-file/index.js'
3
+ export * as downloadFile from './download-file/index.js'
4
4
  export * as existsFile from './exists-file/index.js'
5
- export * as listDir from './list-dir/index.js'
6
5
  export * as moveDir from './move-dir/index.js'
6
+ export * as listDir from './list-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'
10
9
  export * as statFile from './stat-file/index.js'
11
10
  export * as uploadFile from './upload-file/index.js'
11
+ export * as removeFile from './remove-file/index.js'
@@ -1,11 +1,11 @@
1
1
  export * as copyDir from './copy-dir/index.js'
2
- export * as downloadFile from './download-file/index.js'
3
2
  export * as copyFile from './copy-file/index.js'
3
+ export * as downloadFile from './download-file/index.js'
4
4
  export * as existsFile from './exists-file/index.js'
5
- export * as listDir from './list-dir/index.js'
6
5
  export * as moveDir from './move-dir/index.js'
6
+ export * as listDir from './list-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'
10
9
  export * as statFile from './stat-file/index.js'
11
10
  export * as uploadFile from './upload-file/index.js'
11
+ export * as removeFile from './remove-file/index.js'
@@ -1,4 +1,4 @@
1
1
  export * as getSize from './get-size/index.js'
2
2
  export * as isInDirectory from './is-in-directory/index.js'
3
- export * as readWrite from './read-write/index.js'
4
3
  export * as subpaths from './subpaths/index.js'
4
+ export * as readWrite from './read-write/index.js'
@@ -1,4 +1,4 @@
1
1
  export * as getSize from './get-size/index.js'
2
2
  export * as isInDirectory from './is-in-directory/index.js'
3
- export * as readWrite from './read-write/index.js'
4
3
  export * as subpaths from './subpaths/index.js'
4
+ export * as readWrite from './read-write/index.js'
@@ -1,4 +1,4 @@
1
1
  export * as copyDir from './copy-dir/index.js'
2
2
  export * as list from './list/index.js'
3
- export * as moveDir from './move-dir/index.js'
4
3
  export * as removeDir from './remove-dir/index.js'
4
+ export * as moveDir from './move-dir/index.js'
@@ -1,4 +1,4 @@
1
1
  export * as copyDir from './copy-dir/index.js'
2
2
  export * as list from './list/index.js'
3
- export * as moveDir from './move-dir/index.js'
4
3
  export * as removeDir from './remove-dir/index.js'
4
+ export * as moveDir from './move-dir/index.js'
@@ -1,7 +1,7 @@
1
- export * as copy from './copy/index.js'
2
1
  export * as download from './download/index.js'
2
+ export * as copy from './copy/index.js'
3
3
  export * as exists from './exists/index.js'
4
- export * as move from './move/index.js'
5
4
  export * as remove from './remove/index.js'
6
5
  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,7 +1,7 @@
1
- export * as copy from './copy/index.js'
2
1
  export * as download from './download/index.js'
2
+ export * as copy from './copy/index.js'
3
3
  export * as exists from './exists/index.js'
4
- export * as move from './move/index.js'
5
4
  export * as remove from './remove/index.js'
6
5
  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'
3
2
  export * as extend from './extend/index.js'
4
3
  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'
9
8
  export * as level from './level/index.js'
10
- export * as normalize from './normalize/index.js'
11
9
  export * as lighten from './lighten/index.js'
10
+ export * as hue from './hue/index.js'
12
11
  export * as overlay from './overlay/index.js'
12
+ export * as normalize from './normalize/index.js'
13
13
  export * as resize from './resize/index.js'
14
- export * as rotate from './rotate/index.js'
15
14
  export * as saturate from './saturate/index.js'
15
+ export * as rotate from './rotate/index.js'