@ldelia/react-media 0.5.6 → 0.5.8

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,4 @@
1
- import React, { useContext, useEffect, useMemo, useRef } from 'react';
1
+ import React, { useContext, useEffect, useRef, useState } from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { ZoomContext } from '../ZoomContext/ZoomContext';
4
4
  import { pixelToSeconds, secondsToPixel } from '../utils/utils';
@@ -9,19 +9,13 @@ const OverlayCanvas = styled.canvas `
9
9
  width: 100%;
10
10
  height: 100%;
11
11
  color: cadetblue;
12
+ cursor: pointer;
12
13
  `;
13
14
  const RangeSelectorCanvas = ({ selectedRange, onChange, onRangeChange, }) => {
14
15
  const canvasRef = useRef(null);
15
16
  const zoomContextValue = useContext(ZoomContext);
16
- let isSelectingRange = false;
17
- let selectedRangeInPixels = useMemo(() => [], []);
18
- if (selectedRange.length === 2) {
19
- selectedRangeInPixels = [
20
- secondsToPixel(zoomContextValue, selectedRange[0]),
21
- secondsToPixel(zoomContextValue, selectedRange[1]),
22
- ];
23
- }
24
- let lastValidSelectedRangeInPixels = [];
17
+ const [selection, setSelection] = useState({ start: null, end: null });
18
+ const [isDragging, setIsDragging] = useState(false);
25
19
  const getMousePointerPixelPosition = (e) => {
26
20
  const canvas = canvasRef.current;
27
21
  let rect = canvas.getBoundingClientRect();
@@ -30,6 +24,7 @@ const RangeSelectorCanvas = ({ selectedRange, onChange, onRangeChange, }) => {
30
24
  const drawRect = (pixelX0, pixelX1) => {
31
25
  const canvas = canvasRef.current;
32
26
  const context = canvas.getContext('2d');
27
+ context.clearRect(0, 0, canvas.width, canvas.height);
33
28
  context.globalAlpha = 0.3;
34
29
  context.fillStyle = window
35
30
  .getComputedStyle(canvas)
@@ -37,91 +32,75 @@ const RangeSelectorCanvas = ({ selectedRange, onChange, onRangeChange, }) => {
37
32
  context.fillRect(pixelX0, 0, pixelX1 - pixelX0, context.canvas.height);
38
33
  context.globalAlpha = 1.0;
39
34
  };
40
- const onCanvasDoubleClick = (e) => {
41
- const x0 = getMousePointerPixelPosition(e);
42
- onChange(pixelToSeconds(zoomContextValue, x0));
43
- selectedRangeInPixels = lastValidSelectedRangeInPixels;
44
- };
45
- const isPixelNearSelectedRange = (x0) => {
46
- if (selectedRangeInPixels.length === 2) {
47
- const diff = Math.min(Math.abs(selectedRangeInPixels[0] - x0), Math.abs(selectedRangeInPixels[1] - x0));
48
- return diff <= zoomContextValue.pixelsInSecond / 2;
49
- }
50
- return false;
51
- };
52
- const onCanvasMouseDown = (e) => {
53
- const mouseCurrentPosition = getMousePointerPixelPosition(e);
54
- if (!isPixelNearSelectedRange(mouseCurrentPosition)) {
55
- // Keep track of the first position of the new range.
56
- selectedRangeInPixels = [mouseCurrentPosition, mouseCurrentPosition];
57
- }
58
- isSelectingRange = true;
35
+ // Handle mouse down (start of selection)
36
+ const handleMouseDown = (event) => {
37
+ const canvas = canvasRef.current;
38
+ if (!canvas)
39
+ return;
40
+ const pixel = getMousePointerPixelPosition(event);
41
+ const second = pixelToSeconds(zoomContextValue, pixel);
42
+ setSelection({ start: second, end: null });
43
+ setIsDragging(true);
59
44
  };
60
- const onCanvasMouseMove = (e) => {
45
+ // Handle mouse up (end of selection or single click)
46
+ const handleMouseUp = (event) => {
61
47
  const canvas = canvasRef.current;
62
- const context = canvas.getContext('2d');
63
- const mouseCurrentPosition = getMousePointerPixelPosition(e);
64
- if (selectedRangeInPixels.length === 2) {
65
- const diff = Math.min(Math.abs(selectedRangeInPixels[0] - mouseCurrentPosition), Math.abs(selectedRangeInPixels[1] - mouseCurrentPosition));
66
- // Change the mouse's cursor if it's near a selected range.
67
- canvas.style.cursor =
68
- diff <= zoomContextValue.pixelsInSecond / 2 ? 'col-resize' : 'default';
69
- if (!isSelectingRange)
70
- return;
71
- if (mouseCurrentPosition < selectedRangeInPixels[0]) {
72
- // The left side must be enlarged.
73
- selectedRangeInPixels[0] = mouseCurrentPosition;
74
- }
75
- else if (mouseCurrentPosition > selectedRangeInPixels[1]) {
76
- // The right side must be enlarged.
77
- selectedRangeInPixels[1] = mouseCurrentPosition;
78
- }
79
- else {
80
- const diffX0 = mouseCurrentPosition - selectedRangeInPixels[0];
81
- const diffX1 = selectedRangeInPixels[1] - mouseCurrentPosition;
82
- if (diffX0 < diffX1) {
83
- // The left side must be shrunk.
84
- selectedRangeInPixels[0] = mouseCurrentPosition;
48
+ if (!canvas)
49
+ return;
50
+ const pixel = getMousePointerPixelPosition(event);
51
+ const seconds = pixelToSeconds(zoomContextValue, pixel);
52
+ if (isDragging) {
53
+ setSelection(prevSelection => {
54
+ if (prevSelection.start === seconds) {
55
+ onChange(seconds);
56
+ return { start: null, end: null };
85
57
  }
86
58
  else {
87
- // The right side must be shrunk.
88
- selectedRangeInPixels[1] = mouseCurrentPosition;
59
+ const curatedSelection = seconds < prevSelection.start ? [seconds, prevSelection.start] : [prevSelection.start, seconds];
60
+ onRangeChange(curatedSelection);
61
+ return {
62
+ start: curatedSelection[0],
63
+ end: curatedSelection[1],
64
+ };
89
65
  }
90
- }
91
- context.clearRect(0, 0, canvas.width, canvas.height);
92
- drawRect(selectedRangeInPixels[0], selectedRangeInPixels[1]);
66
+ });
93
67
  }
68
+ setIsDragging(false);
94
69
  };
95
- const onCanvasMouseUp = (e) => {
96
- if (selectedRangeInPixels.length !== 2)
70
+ // Handle mouse move (for dragging)
71
+ const handleMouseMove = (event) => {
72
+ if (!isDragging)
73
+ return; // the mouse is moving over the canvas but the user has not clicked yet
74
+ const canvas = canvasRef.current;
75
+ if (!canvas)
97
76
  return;
98
- isSelectingRange = false;
99
- const mouseCurrentPosition = getMousePointerPixelPosition(e);
100
- const pixelRange = [
101
- Math.min(selectedRangeInPixels[0], mouseCurrentPosition),
102
- Math.max(selectedRangeInPixels[1], mouseCurrentPosition),
103
- ];
104
- if (pixelRange[1] - pixelRange[0] <= 1) {
105
- // It was just a click
106
- selectedRangeInPixels = lastValidSelectedRangeInPixels;
107
- }
108
- else {
109
- lastValidSelectedRangeInPixels = pixelRange;
110
- onRangeChange([
111
- pixelToSeconds(zoomContextValue, pixelRange[0]),
112
- pixelToSeconds(zoomContextValue, pixelRange[1]),
113
- ]);
114
- }
77
+ const pixel = getMousePointerPixelPosition(event);
78
+ const seconds = pixelToSeconds(zoomContextValue, pixel);
79
+ setSelection(prevSelection => {
80
+ return {
81
+ start: prevSelection.start,
82
+ end: seconds,
83
+ };
84
+ });
115
85
  };
116
86
  useEffect(() => {
117
87
  const canvas = canvasRef.current;
118
88
  // https://stackoverflow.com/questions/8696631/canvas-drawings-like-lines-are-blurry
119
89
  canvas.width = canvas.offsetWidth;
120
90
  canvas.height = canvas.offsetHeight;
121
- if (selectedRangeInPixels.length === 0)
91
+ if (selectedRange.length !== 2 || zoomContextValue.timelineWrapperWidth === 0)
92
+ return;
93
+ drawRect(secondsToPixel(zoomContextValue, selectedRange[0]), secondsToPixel(zoomContextValue, selectedRange[1]));
94
+ }, [selectedRange]);
95
+ useEffect(() => {
96
+ const canvas = canvasRef.current;
97
+ // https://stackoverflow.com/questions/8696631/canvas-drawings-like-lines-are-blurry
98
+ canvas.width = canvas.offsetWidth;
99
+ canvas.height = canvas.offsetHeight;
100
+ if (selection.start === null || selection.end === null)
122
101
  return;
123
- drawRect(selectedRangeInPixels[0], selectedRangeInPixels[1]);
124
- }, [selectedRangeInPixels]);
125
- return (React.createElement(OverlayCanvas, { ref: canvasRef, onDoubleClick: onCanvasDoubleClick, onMouseDown: onCanvasMouseDown, onMouseMove: onCanvasMouseMove, onMouseUp: onCanvasMouseUp, className: 'media-timeline-range-selector-canvas' }));
102
+ drawRect(secondsToPixel(zoomContextValue, selection.start), secondsToPixel(zoomContextValue, selection.end));
103
+ }, [selection, zoomContextValue]);
104
+ return (React.createElement(OverlayCanvas, { ref: canvasRef, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, className: 'media-timeline-range-selector-canvas' }));
126
105
  };
127
106
  export default React.memo(RangeSelectorCanvas);
@@ -30,7 +30,7 @@ export const TimelineValue = ({ canvasRef, value }) => {
30
30
  const postValueLineElement = postValueLineRef.current;
31
31
  const valueLineWidth = getComputedElementWidth(valueLineElement);
32
32
  const linePosition = secondsToPixel(zoomContextValue, value);
33
- const valueLinePositionBeginningConsideringWidth = linePosition - valueLineWidth / 2;
33
+ const valueLinePositionBeginningConsideringWidth = Math.max(linePosition - valueLineWidth / 2, 0);
34
34
  // configure preValueLineElement
35
35
  preValueLineElement.style.width = numberToPxString(valueLinePositionBeginningConsideringWidth);
36
36
  // configure valueLineElement
@@ -2,3 +2,4 @@ import { ReproductionWidgetProps } from '../components/reproduction-widget';
2
2
  declare const _default: import("@storybook/csf").ComponentAnnotations<import("@storybook/react/dist/types-a5624094").R, import("@storybook/csf").Args>;
3
3
  export default _default;
4
4
  export declare const Default: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-a5624094").R, ReproductionWidgetProps>;
5
+ export declare const PlayAlong: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/dist/types-a5624094").R, ReproductionWidgetProps>;
@@ -1,4 +1,4 @@
1
- import React, { useState, useCallback } from 'react';
1
+ import React, { useCallback, useState } from 'react';
2
2
  import { ReproductionWidget } from '../components/reproduction-widget';
3
3
  export default {
4
4
  title: 'ReproductionWidget',
@@ -12,7 +12,7 @@ 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) => { setReproductionTimestamp(new Date().getTime()); console.log("refresh"); };
16
16
  setReproduction(reproductionInstance);
17
17
  reproductionInstance.on('COUNTING_IN', (args) => { console.log("counting in", args); });
18
18
  reproductionInstance.on('PLAYING', refreshEvent);
@@ -40,7 +40,9 @@ const Template = (args) => {
40
40
  React.createElement("div", null,
41
41
  React.createElement("button", { onClick: handleStop, disabled: !reproduction || reproduction.isStopped() }, "Stop"),
42
42
  React.createElement("button", { onClick: handlePause, disabled: !reproduction || !reproduction.isPlaying() }, "Pause"),
43
- React.createElement("button", { onClick: handleResume, disabled: !reproduction || reproduction.isPlaying() }, "Resume"))));
43
+ React.createElement("button", { onClick: handleResume, disabled: !reproduction || reproduction.isPlaying() }, "Resume"),
44
+ "Current time: ", reproduction === null || reproduction === void 0 ? void 0 :
45
+ reproduction.getCurrentTime())));
44
46
  };
45
47
  export const Default = Template.bind({});
46
48
  Default.args = {
@@ -48,3 +50,9 @@ Default.args = {
48
50
  videoId: 'jFI-RBqXzhU',
49
51
  songTempo: 180,
50
52
  };
53
+ export const PlayAlong = Template.bind({});
54
+ PlayAlong.args = {
55
+ trainingMode: false,
56
+ songTempo: 180,
57
+ duration: 220,
58
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ldelia/react-media",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "A React components collection for media-related features.",
5
5
  "private": false,
6
6
  "keywords": [
@@ -114,20 +114,20 @@
114
114
  "@babel/preset-env": "^7.24.8",
115
115
  "@babel/preset-react": "^7.24.7",
116
116
  "@babel/preset-typescript": "^7.24.7",
117
- "@chromatic-com/storybook": "^1.5.0",
118
- "@storybook/addon-actions": "^8.2.7",
119
- "@storybook/addon-docs": "^8.2.7",
120
- "@storybook/addon-essentials": "^8.2.7",
121
- "@storybook/addon-interactions": "^8.2.7",
117
+ "@chromatic-com/storybook": "^3.2.4",
118
+ "@storybook/addon-actions": "^8.5.0",
119
+ "@storybook/addon-docs": "^8.5.0",
120
+ "@storybook/addon-essentials": "^8.5.0",
121
+ "@storybook/addon-interactions": "^8.5.0",
122
122
  "@storybook/addon-knobs": "^8.0.1",
123
- "@storybook/addon-links": "^8.2.7",
124
- "@storybook/addon-onboarding": "^8.2.7",
125
- "@storybook/blocks": "^8.2.7",
126
- "@storybook/node-logger": "^8.2.7",
127
- "@storybook/react": "^8.2.7",
123
+ "@storybook/addon-links": "^8.5.0",
124
+ "@storybook/addon-onboarding": "^8.5.0",
125
+ "@storybook/blocks": "^8.5.0",
126
+ "@storybook/node-logger": "^8.5.0",
127
+ "@storybook/react": "^8.5.0",
128
128
  "@storybook/react-docgen-typescript-plugin": "^1.0.6--canary.9.0c3f3b7.0",
129
- "@storybook/react-webpack5": "^8.2.7",
130
- "@storybook/test": "^8.2.7",
129
+ "@storybook/react-webpack5": "^8.5.0",
130
+ "@storybook/test": "^8.5.0",
131
131
  "@testing-library/jest-dom": "^6.4.6",
132
132
  "@testing-library/react": "^16.0.0",
133
133
  "@testing-library/user-event": "^14.5.2",
@@ -140,14 +140,14 @@
140
140
  "babel-loader": "^9.1.3",
141
141
  "eslint-config-prettier": "^9.1.0",
142
142
  "eslint-plugin-prettier": "^5.1.3",
143
- "eslint-plugin-storybook": "^0.8.0",
143
+ "eslint-plugin-storybook": "^0.11.2",
144
144
  "postcss-flexbugs-fixes": "^5.0.2",
145
145
  "postcss-normalize": "^10.0.1",
146
146
  "postcss-preset-env": "^9.5.14",
147
147
  "prettier": "^3.3.2",
148
148
  "prop-types": "^15.8.1",
149
149
  "react-docgen-typescript-plugin": "^1.0.8",
150
- "storybook": "^8.2.7",
150
+ "storybook": "^8.5.0",
151
151
  "ts-loader": "^9.5.1",
152
152
  "tsconfig-paths-webpack-plugin": "^4.1.0",
153
153
  "webpack": "^5.92.0"