@design-edito/tools 0.5.2 → 0.5.3

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 (129) hide show
  1. package/agnostic/html/deep-select/index.js +5 -3
  2. package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +8 -8
  3. package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +8 -8
  4. package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +2 -2
  5. package/agnostic/html/hyper-json/smart-tags/isolated/index.js +2 -2
  6. package/agnostic/html/index.d.ts +1 -1
  7. package/agnostic/html/index.js +1 -1
  8. package/agnostic/misc/index.d.ts +3 -3
  9. package/agnostic/misc/index.js +3 -3
  10. package/agnostic/numbers/index.d.ts +1 -1
  11. package/agnostic/numbers/index.js +1 -1
  12. package/agnostic/optim/index.d.ts +1 -1
  13. package/agnostic/optim/index.js +1 -1
  14. package/agnostic/strings/index.d.ts +1 -1
  15. package/agnostic/strings/index.js +1 -1
  16. package/agnostic/time/dates/format-date/index.d.ts +4 -2
  17. package/agnostic/time/dates/format-date/index.js +43 -94
  18. package/agnostic/time/dates/format-date/index.test.js +154 -0
  19. package/agnostic/time/dates/format-date/parts.d.ts +28 -0
  20. package/agnostic/time/dates/format-date/parts.js +136 -0
  21. package/agnostic/time/dates/format-date/types.d.ts +84 -0
  22. package/agnostic/time/dates/format-date/types.js +1 -0
  23. package/agnostic/time/duration/format-duration/index.d.ts +42 -0
  24. package/agnostic/time/duration/format-duration/index.js +94 -0
  25. package/agnostic/time/duration/format-duration/index.test.d.ts +1 -0
  26. package/agnostic/time/duration/format-duration/index.test.js +148 -0
  27. package/agnostic/time/duration/format-duration/parts.d.ts +32 -0
  28. package/agnostic/time/duration/format-duration/parts.js +107 -0
  29. package/agnostic/time/duration/format-duration/types.d.ts +93 -0
  30. package/agnostic/time/duration/format-duration/types.js +1 -0
  31. package/agnostic/time/duration/index.d.ts +6 -1
  32. package/agnostic/time/duration/index.js +2 -2
  33. package/agnostic/time/duration/index.test.d.ts +1 -0
  34. package/agnostic/time/duration/index.test.js +168 -0
  35. package/agnostic/time/index.d.ts +1 -1
  36. package/agnostic/time/index.js +1 -1
  37. package/components/BeforeAfter/index.controlled.d.ts +25 -25
  38. package/components/BeforeAfter/index.controlled.js +47 -53
  39. package/components/BeforeAfter/index.d.ts +20 -23
  40. package/components/BeforeAfter/index.js +48 -40
  41. package/components/Button/index.js +1 -1
  42. package/components/Clippable/index.d.ts +17 -26
  43. package/components/Clippable/index.js +21 -23
  44. package/components/Disclaimer/index.d.ts +29 -22
  45. package/components/Disclaimer/index.js +29 -24
  46. package/components/Drawer/index.d.ts +45 -25
  47. package/components/Drawer/index.js +61 -52
  48. package/components/EventListener/index.d.ts +4 -5
  49. package/components/Gallery/index.d.ts +14 -16
  50. package/components/Gallery/index.js +25 -30
  51. package/components/Iframe/index.js +2 -2
  52. package/components/Image/index.d.ts +5 -12
  53. package/components/Image/index.js +11 -32
  54. package/components/Input/index.d.ts +23 -19
  55. package/components/Input/index.js +30 -20
  56. package/components/JsonEditor/index.d.ts +177 -9
  57. package/components/JsonEditor/index.js +133 -77
  58. package/components/JsonEditor/types.d.ts +11 -0
  59. package/components/JsonEditor/types.js +1 -0
  60. package/components/JsonEditor/utils.d.ts +8 -0
  61. package/components/JsonEditor/utils.js +23 -0
  62. package/components/ListLoader/index.controlled.d.ts +5 -5
  63. package/components/ListLoader/index.controlled.js +4 -4
  64. package/components/ListLoader/index.d.ts +11 -11
  65. package/components/ListLoader/index.js +12 -12
  66. package/components/Overlayer/index.js +4 -4
  67. package/components/Paginator/index.d.ts +7 -12
  68. package/components/Paginator/index.js +7 -11
  69. package/components/ResizeObserver/index.d.ts +14 -3
  70. package/components/ResizeObserver/index.js +49 -24
  71. package/components/Scrllgngn/index.d.ts +17 -13
  72. package/components/Scrllgngn/index.js +25 -84
  73. package/components/Scrllgngn/utils.d.ts +52 -0
  74. package/components/Scrllgngn/utils.js +102 -0
  75. package/components/ScrollListener/index.d.ts +43 -28
  76. package/components/ScrollListener/index.js +59 -79
  77. package/components/ScrollListener/utils.d.ts +37 -16
  78. package/components/ScrollListener/utils.js +141 -28
  79. package/components/Select/index.d.ts +24 -20
  80. package/components/Select/index.js +30 -20
  81. package/components/Sequencer/index.controlled.d.ts +16 -26
  82. package/components/Sequencer/index.controlled.js +15 -12
  83. package/components/Sequencer/index.d.ts +25 -30
  84. package/components/Sequencer/index.js +20 -34
  85. package/components/Subtitles/index.d.ts +29 -5
  86. package/components/Subtitles/index.js +90 -12
  87. package/components/Textarea/index.d.ts +26 -20
  88. package/components/Textarea/index.js +40 -21
  89. package/components/Theatre/index.d.ts +35 -39
  90. package/components/Theatre/index.js +46 -58
  91. package/components/UIModule/index.d.ts +25 -29
  92. package/components/UIModule/index.js +81 -110
  93. package/components/Video/index.controlled.d.ts +51 -51
  94. package/components/Video/index.controlled.js +75 -72
  95. package/components/Video/index.d.ts +12 -14
  96. package/components/Video/index.js +41 -86
  97. package/components/Video/utils.d.ts +35 -3
  98. package/components/Video/utils.js +37 -14
  99. package/components/index.d.ts +1 -1
  100. package/components/index.js +1 -1
  101. package/components/utils/index.d.ts +22 -0
  102. package/components/utils/index.js +31 -0
  103. package/index.d.ts +1 -1
  104. package/index.js +1 -1
  105. package/node/@aws-s3/storage/file/index.d.ts +1 -1
  106. package/node/@aws-s3/storage/file/index.js +1 -1
  107. package/node/@google-cloud/storage/index.d.ts +1 -1
  108. package/node/@google-cloud/storage/index.js +1 -1
  109. package/node/images/index.d.ts +1 -1
  110. package/node/images/index.js +1 -1
  111. package/node/images/transform/operations/index.d.ts +1 -1
  112. package/node/images/transform/operations/index.js +1 -1
  113. package/node/index.d.ts +1 -1
  114. package/node/index.js +1 -1
  115. package/package.json +20 -5
  116. package/TODO.md +0 -269
  117. package/components/BeforeAfter/utils.d.ts +0 -4
  118. package/components/BeforeAfter/utils.js +0 -9
  119. package/components/Input/index.controlled.d.ts +0 -42
  120. package/components/Input/index.controlled.js +0 -54
  121. package/components/Select/index.controlled.d.ts +0 -43
  122. package/components/Select/index.controlled.js +0 -54
  123. package/components/Subtitles/types.d.ts +0 -24
  124. package/components/Subtitles/utils.d.ts +0 -32
  125. package/components/Subtitles/utils.js +0 -107
  126. package/components/Textarea/index.controlled.d.ts +0 -50
  127. package/components/Textarea/index.controlled.js +0 -75
  128. /package/{components/Subtitles/types.js → agnostic/time/dates/format-date/index.test.d.ts} +0 -0
  129. /package/components/ResizeObserver/{style.module.css → styles.module.css} +0 -0
@@ -1,31 +1,29 @@
1
1
  import { type FunctionComponent } from 'react';
2
- import { type Props as DisclaimerProps } from '../Disclaimer/index.js';
3
2
  import { type Props as ControlledProps } from './index.controlled.js';
4
3
  /**
5
4
  * Props for the {@link Video} component.
6
5
  *
7
6
  * Extends all ControlledVideo props except play, mute, fullscreen, volume, playbackRate, and their associated event handlers
8
- * @property disclaimer - Props forwarded to the internal {@link Disclaimer} component.
9
- * While the disclaimer is active, `autoPlay` and `muted` are suppressed on the
10
- * underlying `<video>` element.
11
7
  * @property autoPlayWhenVisible - When `true`, triggers playback the first time the
12
- * component intersects the viewport, provided no disclaimer is active.
8
+ * component intersects the viewport.
13
9
  * @property autoPauseWhenHidden - When `true`, pauses playback whenever the component
14
10
  * leaves the viewport.
15
11
  * @property autoLoudWhenVisible - When `true`, unmutes the video the first time the
16
- * component intersects the viewport, provided no disclaimer is active.
12
+ * component intersects the viewport.
17
13
  * @property autoMuteWhenHidden - When `true`, mutes the video whenever the component
18
14
  * leaves the viewport.
19
- * @property currentTimeMs - When provided, forces the video's current time to the given value (in milliseconds). Giving the currentTimeMs prop puts the component in a controlled state for the current time, meaning that the parent component is responsible for updating the current time. In this mode, user interactions that would normally change the current time (play, pause, timeline click) will not have an effect unless the parent component updates the currentTimeMs prop accordingly.
15
+ * @property currentTimeMs - When provided, hands ownership of the current time
16
+ * (in milliseconds) to the parent, which is then responsible for updating it —
17
+ * typically to scrub the video from scroll position. A controlled time implies a
18
+ * stopped video, since a playing element would advance a value it does not own:
19
+ * `autoPlay`, `autoPlayWhenVisible` and the play button have no effect for as
20
+ * long as this prop is provided.
20
21
  * @property wrapperClassName - Optional additional class name(s) applied to the root wrapper element.
21
- * @property stateHandlers - Optional callbacks invoked whenever the corresponding
22
- * state changes. Useful for synchronizing external state with the internal video state.
23
22
  * @property className - Optional additional class name(s) applied to the root element.
24
23
  * @property children - React children rendered inside the `<video>` element itself
25
24
  * (e.g. fallback content).
26
25
  */
27
26
  export type Props = Omit<ControlledProps, 'play' | 'fullscreen' | 'volume' | 'mute' | 'playbackRate'> & {
28
- disclaimer?: DisclaimerProps;
29
27
  autoPlayWhenVisible?: boolean;
30
28
  autoPauseWhenHidden?: boolean;
31
29
  autoLoudWhenVisible?: boolean;
@@ -34,12 +32,12 @@ export type Props = Omit<ControlledProps, 'play' | 'fullscreen' | 'volume' | 'mu
34
32
  };
35
33
  /**
36
34
  * Full-featured video player component. Wraps a native `<video>` element with
37
- * playback controls, volume, playback rate, a timeline, optional subtitles,
38
- * an optional disclaimer gate, and viewport-driven auto-play/mute behaviours.
35
+ * playback controls, volume, playback rate, a timeline, optional subtitles, and
36
+ * viewport-driven auto-play/mute behaviours.
39
37
  *
40
38
  * @param props - Component properties.
41
39
  * @see {@link Props}
42
- * @returns A `<figure>` element containing the video, its controls, optional
43
- * subtitles, and an optional disclaimer overlay.
40
+ * @returns A `<figure>` element containing the video, its controls and optional
41
+ * subtitles.
44
42
  */
45
43
  export declare const Video: FunctionComponent<Props>;
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { clss } from '../../agnostic/css/clss/index.js';
4
- import { Disclaimer } from '../Disclaimer/index.js';
5
4
  import { IntersectionObserverComponent } from '../IntersectionObserver/index.js';
6
5
  import { mergeClassNames } from '../utils/index.js';
7
6
  import { videoWrapper as publicClassName } from '../public-classnames.js';
@@ -10,33 +9,27 @@ import cssModule from './styles.module.css';
10
9
  import { ControlledVideo } from './index.controlled.js';
11
10
  /**
12
11
  * Full-featured video player component. Wraps a native `<video>` element with
13
- * playback controls, volume, playback rate, a timeline, optional subtitles,
14
- * an optional disclaimer gate, and viewport-driven auto-play/mute behaviours.
12
+ * playback controls, volume, playback rate, a timeline, optional subtitles, and
13
+ * viewport-driven auto-play/mute behaviours.
15
14
  *
16
15
  * @param props - Component properties.
17
16
  * @see {@link Props}
18
- * @returns A `<figure>` element containing the video, its controls, optional
19
- * subtitles, and an optional disclaimer overlay.
17
+ * @returns A `<figure>` element containing the video, its controls and optional
18
+ * subtitles.
20
19
  */
21
- export const Video = ({ loop, disclaimer, autoPlayWhenVisible, autoPauseWhenHidden, autoMuteWhenHidden, autoLoudWhenVisible, wrapperClassName, ...controlledProps }) => {
20
+ export const Video = ({ loop, autoPlayWhenVisible, autoPauseWhenHidden, autoMuteWhenHidden, autoLoudWhenVisible, wrapperClassName, onPlayButtonClicked, onPauseButtonClicked, onLoudButtonClicked, onMuteButtonClicked, onVolumeRangeChanged, onRateRangeChanged, onFullscreenButtonClicked, ...controlledProps }) => {
22
21
  // State & refs
23
22
  const [play, setPlay] = useState(false);
24
23
  const [volume, setVolume] = useState(1);
25
24
  const [mute, setMute] = useState(false);
26
25
  const [playbackRate, setPlaybackRate] = useState(1);
27
26
  const [fullscreen, setFullscreen] = useState(false);
28
- const [isDisclaimerOn, setIsDisclaimerOn] = useState(disclaimer?.isOn === true
29
- || disclaimer?.defaultIsOn === true
30
- || (disclaimer !== undefined && disclaimer.defaultIsOn === undefined));
31
27
  const hasBeenAutoPlayed = useRef(false);
32
- const needsDisclaimer = useMemo(() => disclaimer !== undefined, [disclaimer]);
33
- const shouldDisclaimerBeOn = useMemo(() => {
34
- if (disclaimer?.isOn === false)
35
- return false;
36
- if (disclaimer?.isOn === true)
37
- return true;
38
- return isDisclaimerOn;
39
- }, [disclaimer, isDisclaimerOn]);
28
+ // Several paths below ask for playback — the play button, autoPlayWhenVisible,
29
+ // autoPlay itself. None of them may win over a parent-owned time, so the
30
+ // invariant is applied where the state is forwarded rather than guarded at each
31
+ // of those call sites.
32
+ const isTimeControlled = controlledProps.currentTimeMs !== undefined;
40
33
  const needsObserve = useMemo(() => autoLoudWhenVisible === true
41
34
  || autoMuteWhenHidden === true
42
35
  || autoPlayWhenVisible === true
@@ -49,13 +42,9 @@ export const Video = ({ loop, disclaimer, autoPlayWhenVisible, autoPauseWhenHidd
49
42
  // Intrisic event handlers
50
43
  const handleOnPlayEvent = useCallback((e) => {
51
44
  controlledProps.onPlay?.(e);
52
- if (shouldDisclaimerBeOn) {
53
- setPlay(false);
54
- return;
55
- }
56
45
  setPlay(true);
57
46
  hasBeenAutoPlayed.current = true;
58
- }, [controlledProps.onPlay, shouldDisclaimerBeOn]);
47
+ }, [controlledProps.onPlay]);
59
48
  const handleOnPauseEvent = useCallback((e) => {
60
49
  setPlay(false);
61
50
  controlledProps.onPause?.(e);
@@ -69,45 +58,44 @@ export const Video = ({ loop, disclaimer, autoPlayWhenVisible, autoPauseWhenHidd
69
58
  setPlaybackRate(e.currentTarget.playbackRate);
70
59
  controlledProps.onRateChange?.(e);
71
60
  }, [controlledProps.onRateChange]);
72
- const handleOnFullscreenChangeEvent = useCallback((e) => {
73
- if (document.fullscreenElement === null || shouldDisclaimerBeOn) {
61
+ const handleFullscreenChange = useCallback((isFullscreen) => {
62
+ if (!isFullscreen)
74
63
  setFullscreen(false);
75
- }
76
- }, [shouldDisclaimerBeOn]);
64
+ }, []);
77
65
  const handleOnLoadedMetadataEvent = useCallback((e) => {
78
66
  muteAttributeWorkaround(e.currentTarget, controlledProps.muted ?? false);
79
67
  controlledProps.onLoadedMetadata?.(e);
80
68
  }, [controlledProps.onLoadedMetadata, controlledProps.muted]);
81
69
  // User actions
82
70
  const handlePlayButtonClick = useCallback((e, isPlaying, video) => {
83
- controlledProps.actionHandlers?.playButtonClick?.(e, isPlaying, video);
84
- setPlay(!shouldDisclaimerBeOn);
85
- }, [controlledProps.actionHandlers, shouldDisclaimerBeOn]);
71
+ onPlayButtonClicked?.(e, isPlaying, video);
72
+ setPlay(true);
73
+ }, [onPlayButtonClicked]);
86
74
  const handlePauseButtonClick = useCallback((e, isPlaying, video) => {
87
- controlledProps.actionHandlers?.pauseButtonClick?.(e, isPlaying, video);
75
+ onPauseButtonClicked?.(e, isPlaying, video);
88
76
  setPlay(false);
89
- }, [controlledProps.actionHandlers]);
77
+ }, [onPauseButtonClicked]);
90
78
  const handleLoudButtonClick = useCallback((e, isLoud, video) => {
91
- controlledProps.actionHandlers?.loudButtonClick?.(e, isLoud, video);
79
+ onLoudButtonClicked?.(e, isLoud, video);
92
80
  setMute(false);
93
- }, [controlledProps.actionHandlers]);
81
+ }, [onLoudButtonClicked]);
94
82
  const handleMuteButtonClick = useCallback((e, isLoud, video) => {
95
- controlledProps.actionHandlers?.muteButtonClick?.(e, isLoud, video);
83
+ onMuteButtonClicked?.(e, isLoud, video);
96
84
  setMute(true);
97
- }, [controlledProps.actionHandlers]);
85
+ }, [onMuteButtonClicked]);
98
86
  const handleRateRangeChange = useCallback((e, targetRate, currentRate, video) => {
99
- controlledProps.actionHandlers?.rateRangeChange?.(e, targetRate, currentRate, video);
87
+ onRateRangeChanged?.(e, targetRate, currentRate, video);
100
88
  setPlaybackRate(targetRate);
101
- }, [controlledProps.actionHandlers]);
89
+ }, [onRateRangeChanged]);
102
90
  const handleVolumeRangeChange = useCallback((e, targetVolume, currentVolume, video) => {
103
- controlledProps.actionHandlers?.volumeRangeChange?.(e, targetVolume, currentVolume, video);
91
+ onVolumeRangeChanged?.(e, targetVolume, currentVolume, video);
104
92
  setVolume(targetVolume);
105
- }, [controlledProps.actionHandlers]);
93
+ }, [onVolumeRangeChanged]);
106
94
  const handleFullscreenButtonClick = useCallback((e, isFullscreen, video) => {
107
- controlledProps.actionHandlers?.fullscreenButtonClick?.(e, isFullscreen, video);
108
- setFullscreen((prev) => shouldDisclaimerBeOn ? false : !isFullscreen);
109
- }, [controlledProps.actionHandlers, shouldDisclaimerBeOn]);
110
- // Intersection Observer + Disclaimer
95
+ onFullscreenButtonClicked?.(e, isFullscreen, video);
96
+ setFullscreen(!isFullscreen);
97
+ }, [onFullscreenButtonClicked]);
98
+ // Intersection Observer
111
99
  const onIntersected = useCallback(({ ioEntry }) => {
112
100
  if (ioEntry === undefined)
113
101
  return;
@@ -117,12 +105,10 @@ export const Video = ({ loop, disclaimer, autoPlayWhenVisible, autoPauseWhenHidd
117
105
  if (autoLoudWhenVisible === true && isIntersecting)
118
106
  setMute(false);
119
107
  if (autoPlayWhenVisible === true
120
- && !shouldDisclaimerBeOn
121
108
  && !hasBeenAutoPlayed.current
122
109
  && isIntersecting)
123
110
  setPlay(true);
124
111
  if (autoMuteWhenHidden === true
125
- && !shouldDisclaimerBeOn
126
112
  && !hasBeenAutoPlayed.current
127
113
  && !isIntersecting)
128
114
  setMute(true);
@@ -130,50 +116,19 @@ export const Video = ({ loop, disclaimer, autoPlayWhenVisible, autoPauseWhenHidd
130
116
  autoPlayWhenVisible,
131
117
  autoPauseWhenHidden,
132
118
  autoMuteWhenHidden,
133
- autoLoudWhenVisible,
134
- shouldDisclaimerBeOn
119
+ autoLoudWhenVisible
135
120
  ]);
136
- const handleDiclaimerDismiss = useCallback((prevIsOn) => {
137
- setIsDisclaimerOn(false);
138
- if (controlledProps.autoPlay === true && !hasBeenAutoPlayed.current) {
139
- setPlay(true);
140
- }
141
- disclaimer?.actionHandlers?.dismissClick?.(prevIsOn);
142
- }, [controlledProps.autoPlay, disclaimer?.actionHandlers?.dismissClick]);
121
+ // `autoPlay` is forwarded to the element, but the play state is owned here, so
122
+ // it has to be seeded once on mount for the controls to agree with the element.
143
123
  useEffect(() => {
144
- if (isDisclaimerOn !== shouldDisclaimerBeOn)
145
- setIsDisclaimerOn(shouldDisclaimerBeOn);
146
- }, [isDisclaimerOn, shouldDisclaimerBeOn]);
147
- useEffect(() => {
148
- if (shouldDisclaimerBeOn) {
149
- setMute(true);
150
- setPlay(false);
151
- setFullscreen(false);
152
- }
153
- else if (controlledProps.autoPlay === true && !hasBeenAutoPlayed.current) {
124
+ if (controlledProps.autoPlay === true && !hasBeenAutoPlayed.current)
154
125
  setPlay(true);
155
- }
156
- }, [shouldDisclaimerBeOn]);
126
+ }, []);
157
127
  // Render
158
128
  const c = clss(publicClassName, { cssModule });
159
- const rootClss = mergeClassNames(c(null, {}), wrapperClassName);
160
- const sensitiveContent = _jsx(ControlledVideo, { ...controlledProps, play: play, volume: volume, mute: mute, playbackRate: playbackRate, fullscreen: fullscreen, onPlay: handleOnPlayEvent, onPause: handleOnPauseEvent, onVolumeChange: handleOnVolumeChangeEvent, onRateChange: handleOnRateChangeEvent, onLoadedMetadata: handleOnLoadedMetadataEvent, onFullscreenChange: handleOnFullscreenChangeEvent, actionHandlers: {
161
- ...controlledProps.actionHandlers,
162
- playButtonClick: handlePlayButtonClick,
163
- pauseButtonClick: handlePauseButtonClick,
164
- loudButtonClick: handleLoudButtonClick,
165
- muteButtonClick: handleMuteButtonClick,
166
- volumeRangeChange: handleVolumeRangeChange,
167
- rateRangeChange: handleRateRangeChange,
168
- fullscreenButtonClick: handleFullscreenButtonClick
169
- } });
170
- const disclaimedContent = needsDisclaimer
171
- ? _jsx(Disclaimer, { ...disclaimer, actionHandlers: {
172
- ...disclaimer?.actionHandlers,
173
- dismissClick: handleDiclaimerDismiss
174
- }, children: sensitiveContent })
175
- : sensitiveContent;
176
- return (_jsx("div", { className: rootClss, children: needsObserve
177
- ? _jsx(IntersectionObserverComponent, { onIntersected: onIntersected, children: disclaimedContent })
178
- : disclaimedContent }));
129
+ const rootClss = mergeClassNames(c(), wrapperClassName);
130
+ const videoContent = _jsx(ControlledVideo, { ...controlledProps, play: play && !isTimeControlled, volume: volume, mute: mute, playbackRate: playbackRate, fullscreen: fullscreen, onPlay: handleOnPlayEvent, onPause: handleOnPauseEvent, onVolumeChange: handleOnVolumeChangeEvent, onRateChange: handleOnRateChangeEvent, onLoadedMetadata: handleOnLoadedMetadataEvent, onFullscreenChange: handleFullscreenChange, onPlayButtonClicked: handlePlayButtonClick, onPauseButtonClicked: handlePauseButtonClick, onLoudButtonClicked: handleLoudButtonClick, onMuteButtonClicked: handleMuteButtonClick, onVolumeRangeChanged: handleVolumeRangeChange, onRateRangeChanged: handleRateRangeChange, onFullscreenButtonClicked: handleFullscreenButtonClick });
131
+ return _jsx("div", { className: rootClss, children: needsObserve
132
+ ? _jsx(IntersectionObserverComponent, { onIntersected: onIntersected, children: videoContent })
133
+ : videoContent });
179
134
  };
@@ -1,9 +1,7 @@
1
- import type { Dispatch, SetStateAction } from 'react';
2
1
  export declare const muteAttributeWorkaround: (video: HTMLVideoElement | null, shouldMute: boolean) => void;
3
2
  export declare const forceMute: (video: HTMLVideoElement | null) => void;
4
3
  export declare const forceLoud: (video: HTMLVideoElement | null) => void;
5
4
  export declare const forceVolume: (video: HTMLVideoElement | null, volume: number) => void;
6
- export declare const forceCurrentTime: (video: HTMLVideoElement | null, time: number, setCurrentTime: Dispatch<SetStateAction<number>>) => void;
7
5
  export declare const forcePlay: (video: HTMLVideoElement | null) => Promise<boolean>;
8
6
  export declare const forcePause: (video: HTMLVideoElement | null) => boolean;
9
7
  export declare const forcePlaybackRate: (video: HTMLVideoElement | null, rate: number) => void;
@@ -11,5 +9,39 @@ export declare const forceFullscreen: (video: HTMLVideoElement | null) => Promis
11
9
  export declare const forceExitFullscreen: (video: HTMLVideoElement | null) => Promise<boolean>;
12
10
  export declare function secondsToMs(seconds: number): number;
13
11
  export declare function msToSeconds(ms: number): number;
12
+ /**
13
+ * Renders a duration into a caller-defined pattern.
14
+ *
15
+ * The pattern is a free-form string in which the following tokens are replaced,
16
+ * longest first, so that `mm` is consumed before `m`:
17
+ *
18
+ * - `hh`, `mm`, `ss` — hours, minutes and seconds, zero-padded to two digits.
19
+ * - `ms` — the remaining milliseconds, zero-padded to three digits.
20
+ * - `frame` — the remaining milliseconds expressed in frames, zero-padded to two
21
+ * digits.
22
+ * - `h`, `m`, `s`, `f` — the same four values, unpadded.
23
+ *
24
+ * @param ms - The duration, in milliseconds.
25
+ * @param format - The pattern to fill, e.g. `'mm:ss:ms'`.
26
+ * @param fps - Frame rate used to derive the `frame` and `f` tokens. Defaults to `25`.
27
+ * @returns The formatted duration.
28
+ *
29
+ * @remarks
30
+ * Every field is a remainder of the one above it, never a total: at one hour and
31
+ * a half, `mm` is `30`, not `90`. A pattern that omits `hh` therefore loses the
32
+ * hours rather than folding them into the minutes.
33
+ *
34
+ * Since `h`, `m`, `s` and `f` are tokens on their own, **every** occurrence of
35
+ * those letters is substituted, wherever it sits: `'mm min ss'` renders as
36
+ * `'01 1in 01'`. Separate the fields with punctuation. This is what
37
+ * `agnostic/time/dates/format-date` avoids by delimiting its tokens with
38
+ * `{{…}}` — worth aligning on the day this one is made public.
39
+ */
14
40
  export declare function formatTime(ms: number, format: string, fps?: number): string;
15
- export declare const getTimelineClickProgress: (event: React.MouseEvent<HTMLDivElement>, timeline: HTMLDivElement | null, video: HTMLVideoElement | null) => number;
41
+ /**
42
+ * Where along the timeline a click landed.
43
+ *
44
+ * @param event - The click on the timeline element.
45
+ * @returns A ratio between `0` and `1`, clamped to the element's own bounds.
46
+ */
47
+ export declare const getTimelineClickProgress: (event: React.MouseEvent<HTMLDivElement>) => number;
@@ -28,13 +28,6 @@ export const forceVolume = (video, volume) => {
28
28
  // eslint-disable-next-line no-param-reassign
29
29
  video.volume = volume;
30
30
  };
31
- export const forceCurrentTime = (video, time, setCurrentTime) => {
32
- if (video === null)
33
- return;
34
- // eslint-disable-next-line no-param-reassign
35
- video.currentTime = time;
36
- setCurrentTime(time);
37
- };
38
31
  export const forcePlay = async (video) => {
39
32
  if (video === null)
40
33
  return false;
@@ -105,6 +98,34 @@ export function secondsToMs(seconds) {
105
98
  export function msToSeconds(ms) {
106
99
  return ms / 1000;
107
100
  }
101
+ /**
102
+ * Renders a duration into a caller-defined pattern.
103
+ *
104
+ * The pattern is a free-form string in which the following tokens are replaced,
105
+ * longest first, so that `mm` is consumed before `m`:
106
+ *
107
+ * - `hh`, `mm`, `ss` — hours, minutes and seconds, zero-padded to two digits.
108
+ * - `ms` — the remaining milliseconds, zero-padded to three digits.
109
+ * - `frame` — the remaining milliseconds expressed in frames, zero-padded to two
110
+ * digits.
111
+ * - `h`, `m`, `s`, `f` — the same four values, unpadded.
112
+ *
113
+ * @param ms - The duration, in milliseconds.
114
+ * @param format - The pattern to fill, e.g. `'mm:ss:ms'`.
115
+ * @param fps - Frame rate used to derive the `frame` and `f` tokens. Defaults to `25`.
116
+ * @returns The formatted duration.
117
+ *
118
+ * @remarks
119
+ * Every field is a remainder of the one above it, never a total: at one hour and
120
+ * a half, `mm` is `30`, not `90`. A pattern that omits `hh` therefore loses the
121
+ * hours rather than folding them into the minutes.
122
+ *
123
+ * Since `h`, `m`, `s` and `f` are tokens on their own, **every** occurrence of
124
+ * those letters is substituted, wherever it sits: `'mm min ss'` renders as
125
+ * `'01 1in 01'`. Separate the fields with punctuation. This is what
126
+ * `agnostic/time/dates/format-date` avoids by delimiting its tokens with
127
+ * `{{…}}` — worth aligning on the day this one is made public.
128
+ */
108
129
  export function formatTime(ms, format, fps = 25) {
109
130
  const totalSeconds = Math.floor(ms / 1000);
110
131
  const hours = Math.floor(totalSeconds / 3600);
@@ -123,17 +144,19 @@ export function formatTime(ms, format, fps = 25) {
123
144
  s: seconds,
124
145
  f: frames
125
146
  };
126
- const result = Object
147
+ return Object
127
148
  .keys(tokens)
128
149
  .sort((a, b) => b.length - a.length)
129
150
  .reduce((acc, t) => acc.replace(new RegExp(t, 'gv'), String(tokens[t])), format);
130
- return result;
131
151
  }
132
- export const getTimelineClickProgress = (event, timeline, video) => {
133
- if (video === null || timeline === null)
134
- return 0;
152
+ /**
153
+ * Where along the timeline a click landed.
154
+ *
155
+ * @param event - The click on the timeline element.
156
+ * @returns A ratio between `0` and `1`, clamped to the element's own bounds.
157
+ */
158
+ export const getTimelineClickProgress = (event) => {
135
159
  const timelineRect = event.currentTarget.getBoundingClientRect();
136
160
  const position = event.clientX - timelineRect.left;
137
- const progress = Math.min(1, Math.max(0, position / timelineRect.width));
138
- return progress;
161
+ return Math.min(1, Math.max(0, position / timelineRect.width));
139
162
  };
@@ -1,7 +1,7 @@
1
1
  export * as beforeAfter from './BeforeAfter/index.js'
2
2
  export * as button from './Button/index.js'
3
- export * as disclaimer from './Disclaimer/index.js'
4
3
  export * as clippable from './Clippable/index.js'
4
+ export * as disclaimer from './Disclaimer/index.js'
5
5
  export * as drawer from './Drawer/index.js'
6
6
  export * as eventListener from './EventListener/index.js'
7
7
  export * as gallery from './Gallery/index.js'
@@ -1,7 +1,7 @@
1
1
  export * as beforeAfter from './BeforeAfter/index.js'
2
2
  export * as button from './Button/index.js'
3
- export * as disclaimer from './Disclaimer/index.js'
4
3
  export * as clippable from './Clippable/index.js'
4
+ export * as disclaimer from './Disclaimer/index.js'
5
5
  export * as drawer from './Drawer/index.js'
6
6
  export * as eventListener from './EventListener/index.js'
7
7
  export * as gallery from './Gallery/index.js'
@@ -1 +1,23 @@
1
1
  export declare function mergeClassNames(...names: Array<string | null | undefined | Array<string | null | undefined>>): string;
2
+ /**
3
+ * Calls `onChange` whenever `value` changes, and only then — never on mount.
4
+ *
5
+ * This is how a component reports one of its state items to its consumer:
6
+ * a bare `useEffect` on the value would also fire on the first render, telling
7
+ * the consumer something changed when nothing did.
8
+ *
9
+ * @template T - Type of the watched value.
10
+ * @param value - The value to watch.
11
+ * @param onChange - Called with the new value, once the change has happened.
12
+ * @param isEqual - Decides whether the value actually changed. Defaults to
13
+ * `Object.is`, which covers booleans, numbers and strings. Pass a custom
14
+ * comparison for values rebuilt on every render, such as an array or an object
15
+ * derived from state, whose identity always differs.
16
+ *
17
+ * @remarks
18
+ * `onChange` is deliberately left out of the effect's dependencies. A consumer
19
+ * passing an inline arrow creates a new function on every render, which would
20
+ * re-run the effect each time and defeat the guard. The handler is read when the
21
+ * effect fires, so it is never a stale one.
22
+ */
23
+ export declare function useChangeDispatch<T>(value: T, onChange?: (value: T) => void, isEqual?: (a: T, b: T) => boolean): void;
@@ -1,3 +1,4 @@
1
+ import { useEffect, useRef } from 'react';
1
2
  export function mergeClassNames(...names) {
2
3
  return names.map(name => {
3
4
  if (typeof name !== 'string' && !Array.isArray(name))
@@ -10,3 +11,33 @@ export function mergeClassNames(...names) {
10
11
  }).filter((name) => typeof name === 'string')
11
12
  .join(' ');
12
13
  }
14
+ /**
15
+ * Calls `onChange` whenever `value` changes, and only then — never on mount.
16
+ *
17
+ * This is how a component reports one of its state items to its consumer:
18
+ * a bare `useEffect` on the value would also fire on the first render, telling
19
+ * the consumer something changed when nothing did.
20
+ *
21
+ * @template T - Type of the watched value.
22
+ * @param value - The value to watch.
23
+ * @param onChange - Called with the new value, once the change has happened.
24
+ * @param isEqual - Decides whether the value actually changed. Defaults to
25
+ * `Object.is`, which covers booleans, numbers and strings. Pass a custom
26
+ * comparison for values rebuilt on every render, such as an array or an object
27
+ * derived from state, whose identity always differs.
28
+ *
29
+ * @remarks
30
+ * `onChange` is deliberately left out of the effect's dependencies. A consumer
31
+ * passing an inline arrow creates a new function on every render, which would
32
+ * re-run the effect each time and defeat the guard. The handler is read when the
33
+ * effect fires, so it is never a stale one.
34
+ */
35
+ export function useChangeDispatch(value, onChange, isEqual = Object.is) {
36
+ const previousRef = useRef(value);
37
+ useEffect(() => {
38
+ if (isEqual(previousRef.current, value))
39
+ return;
40
+ previousRef.current = value;
41
+ onChange?.(value);
42
+ }, [value]);
43
+ }
package/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
+ export * as agnostic from './agnostic/index.js'
1
2
  export * as components from './components/index.js'
2
3
  export * as node from './node/index.js'
3
- export * as agnostic from './agnostic/index.js'
package/index.js CHANGED
@@ -1,3 +1,3 @@
1
+ export * as agnostic from './agnostic/index.js'
1
2
  export * as components from './components/index.js'
2
3
  export * as node from './node/index.js'
3
- export * as agnostic from './agnostic/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as copy from './copy/index.js'
2
- export * as exists from './exists/index.js'
3
2
  export * as download from './download/index.js'
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'
6
6
  export * as stat from './stat/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as copy from './copy/index.js'
2
- export * as exists from './exists/index.js'
3
2
  export * as download from './download/index.js'
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'
6
6
  export * as stat from './stat/index.js'
@@ -1,3 +1,3 @@
1
- export * as directory from './directory/index.js'
2
1
  export * as bucket from './bucket/index.js'
2
+ export * as directory from './directory/index.js'
3
3
  export * as file from './file/index.js'
@@ -1,3 +1,3 @@
1
- export * as directory from './directory/index.js'
2
1
  export * as bucket from './bucket/index.js'
2
+ export * as directory from './directory/index.js'
3
3
  export * as file from './file/index.js'
@@ -1,5 +1,5 @@
1
1
  export * as create from './create/index.js'
2
- export * as metadata from './metadata/index.js'
3
2
  export * as format from './format/index.js'
3
+ export * as metadata from './metadata/index.js'
4
4
  export * as transform from './transform/index.js'
5
5
  export * as utils from './utils/index.js'
@@ -1,5 +1,5 @@
1
1
  export * as create from './create/index.js'
2
- export * as metadata from './metadata/index.js'
3
2
  export * as format from './format/index.js'
3
+ export * as metadata from './metadata/index.js'
4
4
  export * as transform from './transform/index.js'
5
5
  export * as utils from './utils/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as blur from './blur/index.js'
2
- export * as extend from './extend/index.js'
3
2
  export * as brighten from './brighten/index.js'
3
+ export * as extend from './extend/index.js'
4
4
  export * as extract from './extract/index.js'
5
5
  export * as flatten from './flatten/index.js'
6
6
  export * as flip from './flip/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as blur from './blur/index.js'
2
- export * as extend from './extend/index.js'
3
2
  export * as brighten from './brighten/index.js'
3
+ export * as extend from './extend/index.js'
4
4
  export * as extract from './extract/index.js'
5
5
  export * as flatten from './flatten/index.js'
6
6
  export * as flip from './flip/index.js'
package/node/index.d.ts CHANGED
@@ -4,8 +4,8 @@ export * as express from './@express/index.js'
4
4
  export * as googleCloud from './@google-cloud/index.js'
5
5
  export * as cloudStorage from './cloud-storage/index.js'
6
6
  export * as encryption from './encryption/index.js'
7
- export * as ftps from './ftps/index.js'
8
7
  export * as files from './files/index.js'
8
+ export * as ftps from './ftps/index.js'
9
9
  export * as images from './images/index.js'
10
10
  export * as process from './process/index.js'
11
11
  export * as sftp from './sftp/index.js'
package/node/index.js CHANGED
@@ -4,8 +4,8 @@ export * as express from './@express/index.js'
4
4
  export * as googleCloud from './@google-cloud/index.js'
5
5
  export * as cloudStorage from './cloud-storage/index.js'
6
6
  export * as encryption from './encryption/index.js'
7
- export * as ftps from './ftps/index.js'
8
7
  export * as files from './files/index.js'
8
+ export * as ftps from './ftps/index.js'
9
9
  export * as images from './images/index.js'
10
10
  export * as process from './process/index.js'
11
11
  export * as sftp from './sftp/index.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@design-edito/tools",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "",
5
5
  "author": "Maxime Fabas",
6
6
  "license": "ISC",
@@ -1290,6 +1290,10 @@
1290
1290
  "./agnostic/time/dates/format-date/index.js": {
1291
1291
  "import": "./agnostic/time/dates/format-date/index.js"
1292
1292
  },
1293
+ "./agnostic/time/dates/format-date/types.js": {
1294
+ "import": "./agnostic/time/dates/format-date/types.js",
1295
+ "types": "./agnostic/time/dates/format-date/types.d.ts"
1296
+ },
1293
1297
  "./agnostic/time/dates/index.js": {
1294
1298
  "import": "./agnostic/time/dates/index.js"
1295
1299
  },
@@ -1308,6 +1312,17 @@
1308
1312
  "import": "./agnostic/time/duration/index.js",
1309
1313
  "types": "./agnostic/time/duration/index.d.ts"
1310
1314
  },
1315
+ "./agnostic/time/duration/format-duration": {
1316
+ "import": "./agnostic/time/duration/format-duration/index.js",
1317
+ "types": "./agnostic/time/duration/format-duration/index.d.ts"
1318
+ },
1319
+ "./agnostic/time/duration/format-duration/index.js": {
1320
+ "import": "./agnostic/time/duration/format-duration/index.js"
1321
+ },
1322
+ "./agnostic/time/duration/format-duration/types.js": {
1323
+ "import": "./agnostic/time/duration/format-duration/types.js",
1324
+ "types": "./agnostic/time/duration/format-duration/types.d.ts"
1325
+ },
1311
1326
  "./agnostic/time/duration/index.js": {
1312
1327
  "import": "./agnostic/time/duration/index.js"
1313
1328
  },
@@ -1451,6 +1466,10 @@
1451
1466
  "./components/JsonEditor/index.js": {
1452
1467
  "import": "./components/JsonEditor/index.js"
1453
1468
  },
1469
+ "./components/JsonEditor/types.js": {
1470
+ "import": "./components/JsonEditor/types.js",
1471
+ "types": "./components/JsonEditor/types.d.ts"
1472
+ },
1454
1473
  "./components/ListLoader": {
1455
1474
  "import": "./components/ListLoader/index.js",
1456
1475
  "types": "./components/ListLoader/index.d.ts"
@@ -1521,10 +1540,6 @@
1521
1540
  "./components/Subtitles/index.js": {
1522
1541
  "import": "./components/Subtitles/index.js"
1523
1542
  },
1524
- "./components/Subtitles/types.js": {
1525
- "import": "./components/Subtitles/types.js",
1526
- "types": "./components/Subtitles/types.d.ts"
1527
- },
1528
1543
  "./components/Textarea": {
1529
1544
  "import": "./components/Textarea/index.js",
1530
1545
  "types": "./components/Textarea/index.d.ts"