@ldelia/react-media 0.8.8 → 0.10.0

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.
@@ -1,4 +1,3 @@
1
- /// <reference types="youtube" />
2
1
  export type InnerYouTubePlayerInterface = YT.Player;
3
2
  declare const dispatchOnPlayingHandlers: unique symbol;
4
3
  declare const dispatchOnFinishHandlers: unique symbol;
@@ -2,6 +2,13 @@ import { ReproductionBuilder } from './ReproductionBuilder';
2
2
  import { PlayAlongPlayer } from './Player/PlayAlongPlayer';
3
3
  import { YouTubePlayer } from './Player/YouTubePlayer';
4
4
  type Player = PlayAlongPlayer | YouTubePlayer;
5
+ export declare const REPRODUCTION_STATES: {
6
+ STOPPED: number;
7
+ COUNTING_IN: number;
8
+ PLAYING: number;
9
+ PAUSED: number;
10
+ };
11
+ type ReproductionState = (typeof REPRODUCTION_STATES)[keyof typeof REPRODUCTION_STATES];
5
12
  type Handler = (args: object) => void;
6
13
  declare const dispatchOnReadyHandlers: unique symbol;
7
14
  declare const dispatchOnSongStartHandlers: unique symbol;
@@ -12,11 +19,13 @@ declare const dispatchOnPausedHandlers: unique symbol;
12
19
  declare const dispatchOnFinishHandlers: unique symbol;
13
20
  declare const dispatchOnErrorHandlers: unique symbol;
14
21
  export declare class Reproduction {
15
- private player;
16
- private requiresCountingIn;
17
- private songTempo;
22
+ private readonly player;
23
+ private readonly requiresCountingIn;
24
+ private readonly songTempo;
18
25
  private state;
19
26
  private interval;
27
+ private loopInterval;
28
+ private loopRange;
20
29
  private countingInCounter;
21
30
  private [dispatchOnReadyHandlers];
22
31
  private [dispatchOnSongStartHandlers];
@@ -43,16 +52,19 @@ export declare class Reproduction {
43
52
  PAUSED: number;
44
53
  };
45
54
  static newBuilder(): ReproductionBuilder;
46
- on(eventName: keyof typeof Reproduction.EVENTS, handler: Handler): number | undefined;
55
+ on(eventName: keyof typeof Reproduction.EVENTS, handler: Handler): () => void;
56
+ off(eventName: keyof typeof Reproduction.EVENTS, handler: Handler): void;
47
57
  dispatch(eventName: keyof typeof Reproduction.EVENTS, args?: {}): void;
48
58
  start(): void;
49
59
  play(): void;
60
+ playLoop(from: number, to: number): void;
50
61
  pause(): void;
51
62
  stop(): void;
52
63
  isPlaying(): boolean;
53
64
  isStopped(): boolean;
54
65
  isPaused(): boolean;
55
66
  isCountingIn(): boolean;
67
+ getState(): ReproductionState;
56
68
  getPlayer(): Player;
57
69
  getTempo(): number;
58
70
  getCurrentTime(): number;
@@ -1,6 +1,6 @@
1
1
  import { PLAYER_EVENTS } from './Player/PlayerEvents';
2
2
  import { ReproductionBuilder } from './ReproductionBuilder';
3
- const STATES = {
3
+ export const REPRODUCTION_STATES = {
4
4
  STOPPED: 0,
5
5
  COUNTING_IN: 1,
6
6
  PLAYING: 2,
@@ -37,6 +37,8 @@ export class Reproduction {
37
37
  this.player = player;
38
38
  this.state = Reproduction.STATES.STOPPED;
39
39
  this.interval = null;
40
+ this.loopInterval = null;
41
+ this.loopRange = null;
40
42
  this.requiresCountingIn = requiresCountingIn;
41
43
  this.countingInCounter = 0;
42
44
  this.player.setVolume(volume);
@@ -47,6 +49,9 @@ export class Reproduction {
47
49
  this.player.on(PLAYER_EVENTS.FINISH, () => {
48
50
  this.state = Reproduction.STATES.STOPPED;
49
51
  clearInterval(this.interval);
52
+ clearInterval(this.loopInterval);
53
+ this.loopInterval = null;
54
+ this.loopRange = null;
50
55
  this.dispatch(Reproduction.EVENTS.FINISH);
51
56
  });
52
57
  this.player.on(PLAYER_EVENTS.ERROR, (error) => {
@@ -57,29 +62,75 @@ export class Reproduction {
57
62
  return EVENTS;
58
63
  }
59
64
  static get STATES() {
60
- return STATES;
65
+ return REPRODUCTION_STATES;
61
66
  }
62
67
  static newBuilder() {
63
68
  return new ReproductionBuilder();
64
69
  }
65
70
  on(eventName, handler) {
71
+ if (typeof handler !== 'function') {
72
+ throw new Error('Handler must be a function');
73
+ }
66
74
  switch (eventName) {
67
75
  case Reproduction.EVENTS.START:
68
- return this[dispatchOnSongStartHandlers].push(handler);
76
+ this[dispatchOnSongStartHandlers].push(handler);
77
+ break;
69
78
  case Reproduction.EVENTS.COUNTING_IN:
70
- return this[dispatchOnCountingInHandlers].push(handler);
79
+ this[dispatchOnCountingInHandlers].push(handler);
80
+ break;
71
81
  case Reproduction.EVENTS.PLAY:
72
- return this[dispatchOnPlayHandlers].push(handler);
82
+ this[dispatchOnPlayHandlers].push(handler);
83
+ break;
73
84
  case Reproduction.EVENTS.PLAYING:
74
- return this[dispatchOnPlayingHandlers].push(handler);
85
+ this[dispatchOnPlayingHandlers].push(handler);
86
+ break;
75
87
  case Reproduction.EVENTS.PAUSED:
76
- return this[dispatchOnPausedHandlers].push(handler);
88
+ this[dispatchOnPausedHandlers].push(handler);
89
+ break;
77
90
  case Reproduction.EVENTS.FINISH:
78
- return this[dispatchOnFinishHandlers].push(handler);
91
+ this[dispatchOnFinishHandlers].push(handler);
92
+ break;
79
93
  case Reproduction.EVENTS.ERROR:
80
- return this[dispatchOnErrorHandlers].push(handler);
94
+ this[dispatchOnErrorHandlers].push(handler);
95
+ break;
81
96
  default:
97
+ throw new Error(`Unknown event: ${eventName}`);
98
+ }
99
+ return () => this.off(eventName, handler);
100
+ }
101
+ off(eventName, handler) {
102
+ if (typeof handler !== 'function') {
103
+ throw new Error('Handler must be a function');
104
+ }
105
+ let handlers;
106
+ switch (eventName) {
107
+ case Reproduction.EVENTS.START:
108
+ handlers = this[dispatchOnSongStartHandlers];
109
+ break;
110
+ case Reproduction.EVENTS.COUNTING_IN:
111
+ handlers = this[dispatchOnCountingInHandlers];
112
+ break;
113
+ case Reproduction.EVENTS.PLAY:
114
+ handlers = this[dispatchOnPlayHandlers];
82
115
  break;
116
+ case Reproduction.EVENTS.PLAYING:
117
+ handlers = this[dispatchOnPlayingHandlers];
118
+ break;
119
+ case Reproduction.EVENTS.PAUSED:
120
+ handlers = this[dispatchOnPausedHandlers];
121
+ break;
122
+ case Reproduction.EVENTS.FINISH:
123
+ handlers = this[dispatchOnFinishHandlers];
124
+ break;
125
+ case Reproduction.EVENTS.ERROR:
126
+ handlers = this[dispatchOnErrorHandlers];
127
+ break;
128
+ default:
129
+ throw new Error(`Unknown event: ${eventName}`);
130
+ }
131
+ const index = handlers.indexOf(handler);
132
+ if (index > -1) {
133
+ handlers.splice(index, 1);
83
134
  }
84
135
  }
85
136
  dispatch(eventName, args = {}) {
@@ -129,24 +180,55 @@ export class Reproduction {
129
180
  }
130
181
  }
131
182
  play() {
183
+ clearInterval(this.interval);
132
184
  this.player.play();
133
- const intervalTimeout = 200;
185
+ const bpmInterval = this.getBPMInterval();
186
+ const tickInterval = Math.min(bpmInterval / 4, 50);
134
187
  this.interval = setInterval(() => {
135
188
  if (this.isPlaying()) {
136
189
  this.dispatch(Reproduction.EVENTS.PLAYING);
137
190
  }
138
- }, intervalTimeout);
191
+ }, tickInterval);
192
+ }
193
+ playLoop(from, to) {
194
+ if (!Number.isFinite(from) || !Number.isFinite(to)) {
195
+ return;
196
+ }
197
+ if (to <= from) {
198
+ return;
199
+ }
200
+ clearInterval(this.loopInterval);
201
+ this.loopInterval = null;
202
+ this.loopRange = { from, to };
203
+ this.seekTo(from);
204
+ this.play();
205
+ const loopCheckInterval = 100;
206
+ this.loopInterval = setInterval(() => {
207
+ if (!this.isPlaying() || !this.loopRange) {
208
+ return;
209
+ }
210
+ const currentTime = this.getCurrentTime();
211
+ if (currentTime >= this.loopRange.to) {
212
+ this.seekTo(this.loopRange.from);
213
+ }
214
+ }, loopCheckInterval);
139
215
  }
140
216
  pause() {
141
217
  this.state = Reproduction.STATES.PAUSED;
142
218
  this.player.pause();
143
219
  clearInterval(this.interval);
220
+ clearInterval(this.loopInterval);
221
+ this.loopInterval = null;
222
+ this.loopRange = null;
144
223
  this.dispatch(Reproduction.EVENTS.PAUSED);
145
224
  }
146
225
  stop() {
147
226
  this.state = Reproduction.STATES.STOPPED;
148
227
  this.player.stop();
149
228
  clearInterval(this.interval);
229
+ clearInterval(this.loopInterval);
230
+ this.loopInterval = null;
231
+ this.loopRange = null;
150
232
  this.dispatch(Reproduction.EVENTS.FINISH);
151
233
  }
152
234
  isPlaying() {
@@ -161,6 +243,9 @@ export class Reproduction {
161
243
  isCountingIn() {
162
244
  return this.state === Reproduction.STATES.COUNTING_IN;
163
245
  }
246
+ getState() {
247
+ return this.state;
248
+ }
164
249
  getPlayer() {
165
250
  return this.player;
166
251
  }
@@ -1,7 +1,8 @@
1
+ import { Meta } from '@storybook/react';
1
2
  import { ReproductionWidgetProps } from '../components/reproduction-widget';
2
- declare const _default: import("storybook/internal/csf").ComponentAnnotations<import("@storybook/react/dist/types-7abe74eb").R, import("storybook/internal/csf").Args>;
3
+ declare const _default: Meta;
3
4
  export default _default;
4
- export declare const Default: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, ReproductionWidgetProps>;
5
- export declare const WhisperingVideo: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, ReproductionWidgetProps>;
6
- export declare const PlayAlong: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, ReproductionWidgetProps>;
7
- export declare const InvalidVideo: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, ReproductionWidgetProps>;
5
+ export declare const Default: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, ReproductionWidgetProps>;
6
+ export declare const WhisperingVideo: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, ReproductionWidgetProps>;
7
+ export declare const PlayAlong: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, ReproductionWidgetProps>;
8
+ export declare const InvalidVideo: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, ReproductionWidgetProps>;
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useState } from 'react';
2
- import { ReproductionWidget } from '../components/reproduction-widget';
2
+ import { ReproductionWidget, } from '../components/reproduction-widget';
3
3
  export default {
4
4
  title: 'ReproductionWidget',
5
5
  component: ReproductionWidget,
@@ -12,13 +12,19 @@ const Template = (args) => {
12
12
  const [reproductionTimestamp, setReproductionTimestamp] = useState(0);
13
13
  // Handle initialization of reproduction
14
14
  const handleInit = useCallback((reproductionInstance) => {
15
- const refreshEvent = (args) => { setReproductionTimestamp(new Date().getTime()); };
15
+ const refreshEvent = (args) => {
16
+ setReproductionTimestamp(new Date().getTime());
17
+ };
16
18
  setReproduction(reproductionInstance);
17
- reproductionInstance.on('COUNTING_IN', (args) => { console.log("counting in", args); });
19
+ reproductionInstance.on('COUNTING_IN', (args) => {
20
+ console.log('counting in', args);
21
+ });
18
22
  reproductionInstance.on('PLAYING', refreshEvent);
19
23
  reproductionInstance.on('PAUSED', refreshEvent);
20
24
  reproductionInstance.on('FINISH', refreshEvent);
21
- reproductionInstance.on('ERROR', (args) => { console.error("Reproduction error", args); });
25
+ reproductionInstance.on('ERROR', (args) => {
26
+ console.error('Reproduction error', args);
27
+ });
22
28
  }, []);
23
29
  const handleStop = () => {
24
30
  if (reproduction) {
@@ -40,19 +46,27 @@ const Template = (args) => {
40
46
  reproduction.start();
41
47
  }
42
48
  };
49
+ const handleLoop = () => {
50
+ if (reproduction) {
51
+ reproduction.playLoop(10, 20);
52
+ }
53
+ };
43
54
  return (React.createElement("div", null,
44
55
  React.createElement(ReproductionWidget, Object.assign({}, args, { onInit: handleInit })),
45
56
  React.createElement("div", null,
46
57
  React.createElement("button", { onClick: handleStop, disabled: !reproduction || reproduction.isStopped() }, "Stop"),
47
58
  React.createElement("button", { onClick: handlePause, disabled: !reproduction || !reproduction.isPlaying() }, "Pause"),
48
- React.createElement("button", { onClick: handleResume, disabled: !reproduction || reproduction.isPlaying() || reproduction.getCurrentTime() === 0 }, "Resume"),
59
+ React.createElement("button", { onClick: handleResume, disabled: !reproduction ||
60
+ reproduction.isPlaying() ||
61
+ reproduction.getCurrentTime() === 0 }, "Resume"),
49
62
  React.createElement("button", { onClick: handleStart, disabled: !reproduction || reproduction.isPlaying() }, "Start"),
63
+ React.createElement("button", { onClick: handleLoop, disabled: !reproduction }, "Loop 10-20"),
50
64
  reproduction && (React.createElement("div", null,
51
65
  "Current time: ", reproduction === null || reproduction === void 0 ? void 0 :
52
66
  reproduction.getCurrentTime())),
53
- reproduction && (React.createElement("div", null,
67
+ reproduction && React.createElement("div", null,
54
68
  "Volume: ", reproduction === null || reproduction === void 0 ? void 0 :
55
- reproduction.getVolume())),
69
+ reproduction.getVolume()),
56
70
  React.createElement("button", { onClick: () => reproduction === null || reproduction === void 0 ? void 0 : reproduction.setVolume(reproduction.getVolume() - 10), disabled: !reproduction || reproduction.getVolume() <= 10 }, "Volume -10"),
57
71
  React.createElement("button", { onClick: () => reproduction === null || reproduction === void 0 ? void 0 : reproduction.setVolume(reproduction.getVolume() + 10), disabled: !reproduction || reproduction.getVolume() >= 100 }, "Volume +10"))));
58
72
  };
@@ -1,10 +1,11 @@
1
+ import type { Meta } from '@storybook/react';
1
2
  import { TimelineProps } from '../components/timeline';
2
3
  import './timeline.stories.custom.css';
3
- declare const _default: import("storybook/internal/csf").ComponentAnnotations<import("@storybook/react/dist/types-7abe74eb").R, import("storybook/internal/csf").Args>;
4
+ declare const _default: Meta;
4
5
  export default _default;
5
- export declare const Default: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, TimelineProps>;
6
- export declare const WithSelectedRange: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, TimelineProps>;
7
- export declare const WithCustomClassName: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, TimelineProps>;
8
- export declare const WithoutTimeBlocks: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, TimelineProps>;
9
- export declare const Minimalist: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, TimelineProps>;
10
- export declare const WithSelectedRangeAndMarkers: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-7abe74eb").R, TimelineProps>;
6
+ export declare const Default: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, TimelineProps>;
7
+ export declare const WithSelectedRange: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, TimelineProps>;
8
+ export declare const WithCustomClassName: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, TimelineProps>;
9
+ export declare const WithoutTimeBlocks: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, TimelineProps>;
10
+ export declare const Minimalist: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, TimelineProps>;
11
+ export declare const WithSelectedRangeAndMarkers: import("storybook/internal/csf").AnnotatedStoryFn<import("@storybook/react").ReactRenderer, TimelineProps>;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { Timeline } from '../components/timeline';
3
2
  import styled from 'styled-components';
3
+ import { Timeline } from '../components/timeline';
4
4
  import './timeline.stories.custom.css';
5
5
  const StyledTimeline = styled(Timeline) `
6
6
  .media-timeline-value-line {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ldelia/react-media",
3
- "version": "0.8.8",
3
+ "version": "0.10.0",
4
4
  "description": "A React components collection for media-related features.",
5
5
  "private": false,
6
6
  "keywords": [
@@ -101,7 +101,7 @@
101
101
  "styled-components": "^6.1.11",
102
102
  "tailwindcss": "^3.0.2",
103
103
  "terser-webpack-plugin": "^5.2.5",
104
- "typescript": "^4.9.5",
104
+ "typescript": "^5.3.3",
105
105
  "webpack": "^5.102.1",
106
106
  "webpack-dev-server": "^5.2.2",
107
107
  "webpack-manifest-plugin": "^5.0.1",
@@ -113,12 +113,12 @@
113
113
  "@babel/preset-env": "^7.24.8",
114
114
  "@babel/preset-react": "^7.24.7",
115
115
  "@babel/preset-typescript": "^7.24.7",
116
- "@chromatic-com/storybook": "^4.1.1",
117
- "@storybook/addon-docs": "^9.1.12",
118
- "@storybook/addon-links": "^9.1.12",
119
- "@storybook/addon-onboarding": "^9.1.12",
116
+ "@chromatic-com/storybook": "^5.0.1",
117
+ "@storybook/addon-docs": "^10.2.8",
118
+ "@storybook/addon-links": "^10.2.8",
119
+ "@storybook/addon-onboarding": "^10.2.8",
120
120
  "@storybook/react-docgen-typescript-plugin": "^1.0.6--canary.9.0c3f3b7.0",
121
- "@storybook/react-webpack5": "^9.1.12",
121
+ "@storybook/react-webpack5": "^10.2.8",
122
122
  "@testing-library/jest-dom": "^6.4.6",
123
123
  "@testing-library/react": "^16.0.0",
124
124
  "@testing-library/user-event": "^14.5.2",
@@ -131,7 +131,7 @@
131
131
  "babel-loader": "^9.1.3",
132
132
  "eslint-config-prettier": "^9.1.0",
133
133
  "eslint-plugin-prettier": "^5.1.3",
134
- "eslint-plugin-storybook": "^9.1.12",
134
+ "eslint-plugin-storybook": "^10.2.8",
135
135
  "jest-environment-jsdom": "^30.2.0",
136
136
  "postcss-flexbugs-fixes": "^5.0.2",
137
137
  "postcss-normalize": "^10.0.1",
@@ -139,10 +139,10 @@
139
139
  "prettier": "^3.3.2",
140
140
  "prop-types": "^15.8.1",
141
141
  "react-docgen-typescript-plugin": "^1.0.8",
142
- "storybook": "^9.1.12",
142
+ "storybook": "^10.2.8",
143
143
  "ts-loader": "^9.5.1",
144
144
  "tsconfig-paths-webpack-plugin": "^4.1.0",
145
- "webpack": "^5.92.0"
145
+ "webpack": "^5.105.2"
146
146
  },
147
147
  "jest": {
148
148
  "roots": [