@stream-io/video-react-sdk 1.28.2 → 1.29.1
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.
- package/CHANGELOG.md +20 -0
- package/dist/index.cjs.js +118 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +118 -3
- package/dist/index.es.js.map +1 -1
- package/dist/src/core/components/CallLayout/SpeakerLayout.d.ts +6 -1
- package/dist/src/hooks/index.d.ts +1 -0
- package/dist/src/hooks/useDragToScroll.d.ts +18 -0
- package/package.json +5 -5
- package/src/core/components/CallLayout/SpeakerLayout.tsx +11 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useDragToScroll.ts +162 -0
|
@@ -57,8 +57,13 @@ export type SpeakerLayoutProps = {
|
|
|
57
57
|
* Whether the layout is muted. Defaults to `false`.
|
|
58
58
|
*/
|
|
59
59
|
muted?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Whether to enable drag-to-scroll functionality on the participants bar.
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
64
|
+
enableDragToScroll?: boolean;
|
|
60
65
|
} & Pick<ParticipantViewProps, 'VideoPlaceholder' | 'PictureInPicturePlaceholder'>;
|
|
61
66
|
export declare const SpeakerLayout: {
|
|
62
|
-
({ ParticipantViewUIBar, ParticipantViewUISpotlight, VideoPlaceholder, PictureInPicturePlaceholder, participantsBarPosition, participantsBarLimit, mirrorLocalParticipantVideo, excludeLocalParticipant, filterParticipants, pageArrowsVisible, muted, }: SpeakerLayoutProps): import("react/jsx-runtime").JSX.Element | null;
|
|
67
|
+
({ ParticipantViewUIBar, ParticipantViewUISpotlight, VideoPlaceholder, PictureInPicturePlaceholder, participantsBarPosition, participantsBarLimit, mirrorLocalParticipantVideo, excludeLocalParticipant, filterParticipants, pageArrowsVisible, muted, enableDragToScroll, }: SpeakerLayoutProps): import("react/jsx-runtime").JSX.Element | null;
|
|
63
68
|
displayName: string;
|
|
64
69
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface DragToScrollOptions {
|
|
2
|
+
decay?: number;
|
|
3
|
+
minVelocity?: number;
|
|
4
|
+
dragThreshold?: number;
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Enables drag-to-scroll functionality with momentum scrolling on a scrollable element.
|
|
9
|
+
*
|
|
10
|
+
* This hook allows users to click and drag to scroll an element, with momentum scrolling
|
|
11
|
+
* that continues after the drag ends. The drag only activates after moving beyond a threshold
|
|
12
|
+
* distance, which prevents accidental drags from clicks.
|
|
13
|
+
*
|
|
14
|
+
* @param element - The HTML element to enable drag to scroll on.
|
|
15
|
+
* @param options - Options for customizing the drag-to-scroll behavior.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useDragToScroll(element: HTMLElement | null, options?: DragToScrollOptions): void;
|
|
18
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-react-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.1",
|
|
4
4
|
"main": "./dist/index.cjs.js",
|
|
5
5
|
"module": "./dist/index.es.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@floating-ui/react": "^0.27.6",
|
|
34
|
-
"@stream-io/video-client": "1.
|
|
34
|
+
"@stream-io/video-client": "1.39.1",
|
|
35
35
|
"@stream-io/video-filters-web": "0.6.0",
|
|
36
|
-
"@stream-io/video-react-bindings": "1.12.
|
|
36
|
+
"@stream-io/video-react-bindings": "1.12.4",
|
|
37
37
|
"chart.js": "^4.4.4",
|
|
38
38
|
"clsx": "^2.0.0",
|
|
39
39
|
"react-chartjs-2": "^5.3.0"
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"@rollup/plugin-json": "^6.1.0",
|
|
47
47
|
"@rollup/plugin-replace": "^6.0.2",
|
|
48
48
|
"@rollup/plugin-typescript": "^12.1.4",
|
|
49
|
-
"@stream-io/audio-filters-web": "^0.
|
|
50
|
-
"@stream-io/video-styling": "^1.9.
|
|
49
|
+
"@stream-io/audio-filters-web": "^0.7.0",
|
|
50
|
+
"@stream-io/video-styling": "^1.9.1",
|
|
51
51
|
"@types/react": "~19.1.17",
|
|
52
52
|
"@types/react-dom": "~19.1.11",
|
|
53
53
|
"react": "19.1.0",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '../ParticipantView';
|
|
11
11
|
import { IconButton } from '../../../components';
|
|
12
12
|
import {
|
|
13
|
+
useDragToScroll,
|
|
13
14
|
useHorizontalScrollPosition,
|
|
14
15
|
useVerticalScrollPosition,
|
|
15
16
|
} from '../../../hooks';
|
|
@@ -88,6 +89,12 @@ export type SpeakerLayoutProps = {
|
|
|
88
89
|
* Whether the layout is muted. Defaults to `false`.
|
|
89
90
|
*/
|
|
90
91
|
muted?: boolean;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Whether to enable drag-to-scroll functionality on the participants bar.
|
|
95
|
+
* @default false
|
|
96
|
+
*/
|
|
97
|
+
enableDragToScroll?: boolean;
|
|
91
98
|
} & Pick<
|
|
92
99
|
ParticipantViewProps,
|
|
93
100
|
'VideoPlaceholder' | 'PictureInPicturePlaceholder'
|
|
@@ -109,6 +116,7 @@ export const SpeakerLayout = ({
|
|
|
109
116
|
filterParticipants,
|
|
110
117
|
pageArrowsVisible = true,
|
|
111
118
|
muted,
|
|
119
|
+
enableDragToScroll = false,
|
|
112
120
|
}: SpeakerLayoutProps) => {
|
|
113
121
|
const call = useCall();
|
|
114
122
|
const { useParticipants } = useCallStateHooks();
|
|
@@ -146,6 +154,9 @@ export const SpeakerLayout = ({
|
|
|
146
154
|
|
|
147
155
|
const isOneOnOneCall = allParticipants.length === 2;
|
|
148
156
|
useSpeakerLayoutSortPreset(call, isOneOnOneCall);
|
|
157
|
+
useDragToScroll(participantsBarWrapperElement, {
|
|
158
|
+
enabled: enableDragToScroll,
|
|
159
|
+
});
|
|
149
160
|
|
|
150
161
|
let participantsWithAppliedLimit = otherParticipants;
|
|
151
162
|
|
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
interface DragScrollState {
|
|
4
|
+
isDragging: boolean;
|
|
5
|
+
isPointerActive: boolean;
|
|
6
|
+
prevX: number;
|
|
7
|
+
prevY: number;
|
|
8
|
+
velocityX: number;
|
|
9
|
+
velocityY: number;
|
|
10
|
+
rafId: number;
|
|
11
|
+
startX: number;
|
|
12
|
+
startY: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface DragToScrollOptions {
|
|
16
|
+
decay?: number;
|
|
17
|
+
minVelocity?: number;
|
|
18
|
+
dragThreshold?: number;
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Enables drag-to-scroll functionality with momentum scrolling on a scrollable element.
|
|
24
|
+
*
|
|
25
|
+
* This hook allows users to click and drag to scroll an element, with momentum scrolling
|
|
26
|
+
* that continues after the drag ends. The drag only activates after moving beyond a threshold
|
|
27
|
+
* distance, which prevents accidental drags from clicks.
|
|
28
|
+
*
|
|
29
|
+
* @param element - The HTML element to enable drag to scroll on.
|
|
30
|
+
* @param options - Options for customizing the drag-to-scroll behavior.
|
|
31
|
+
*/
|
|
32
|
+
export function useDragToScroll(
|
|
33
|
+
element: HTMLElement | null,
|
|
34
|
+
options: DragToScrollOptions = {},
|
|
35
|
+
) {
|
|
36
|
+
const stateRef = useRef<DragScrollState>({
|
|
37
|
+
isDragging: false,
|
|
38
|
+
isPointerActive: false,
|
|
39
|
+
prevX: 0,
|
|
40
|
+
prevY: 0,
|
|
41
|
+
velocityX: 0,
|
|
42
|
+
velocityY: 0,
|
|
43
|
+
rafId: 0,
|
|
44
|
+
startX: 0,
|
|
45
|
+
startY: 0,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (!element || !options.enabled) return;
|
|
50
|
+
|
|
51
|
+
const { decay = 0.95, minVelocity = 0.5, dragThreshold = 5 } = options;
|
|
52
|
+
|
|
53
|
+
const state = stateRef.current;
|
|
54
|
+
|
|
55
|
+
const stopMomentum = () => {
|
|
56
|
+
if (state.rafId) {
|
|
57
|
+
cancelAnimationFrame(state.rafId);
|
|
58
|
+
state.rafId = 0;
|
|
59
|
+
}
|
|
60
|
+
state.velocityX = 0;
|
|
61
|
+
state.velocityY = 0;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const momentumStep = () => {
|
|
65
|
+
state.velocityX *= decay;
|
|
66
|
+
state.velocityY *= decay;
|
|
67
|
+
|
|
68
|
+
element.scrollLeft -= state.velocityX;
|
|
69
|
+
element.scrollTop -= state.velocityY;
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
Math.abs(state.velocityX) < minVelocity &&
|
|
73
|
+
Math.abs(state.velocityY) < minVelocity
|
|
74
|
+
) {
|
|
75
|
+
state.rafId = 0;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
state.rafId = requestAnimationFrame(momentumStep);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const onPointerDown = (e: PointerEvent) => {
|
|
83
|
+
if (e.pointerType !== 'mouse') return;
|
|
84
|
+
|
|
85
|
+
stopMomentum();
|
|
86
|
+
|
|
87
|
+
state.isDragging = false;
|
|
88
|
+
state.isPointerActive = true;
|
|
89
|
+
|
|
90
|
+
state.prevX = e.clientX;
|
|
91
|
+
state.prevY = e.clientY;
|
|
92
|
+
state.startX = e.clientX;
|
|
93
|
+
state.startY = e.clientY;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const onPointerMove = (e: PointerEvent) => {
|
|
97
|
+
if (e.pointerType !== 'mouse') return;
|
|
98
|
+
|
|
99
|
+
if (!state.isPointerActive) return;
|
|
100
|
+
|
|
101
|
+
const dx = e.clientX - state.startX;
|
|
102
|
+
const dy = e.clientY - state.startY;
|
|
103
|
+
|
|
104
|
+
if (!state.isDragging && Math.hypot(dx, dy) > dragThreshold) {
|
|
105
|
+
state.isDragging = true;
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!state.isDragging) return;
|
|
110
|
+
|
|
111
|
+
const moveDx = e.clientX - state.prevX;
|
|
112
|
+
const moveDy = e.clientY - state.prevY;
|
|
113
|
+
|
|
114
|
+
element.scrollLeft -= moveDx;
|
|
115
|
+
element.scrollTop -= moveDy;
|
|
116
|
+
|
|
117
|
+
state.velocityX = moveDx;
|
|
118
|
+
state.velocityY = moveDy;
|
|
119
|
+
|
|
120
|
+
state.prevX = e.clientX;
|
|
121
|
+
state.prevY = e.clientY;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const onPointerUpOrCancel = () => {
|
|
125
|
+
const wasDragging = state.isDragging;
|
|
126
|
+
|
|
127
|
+
state.isDragging = false;
|
|
128
|
+
state.isPointerActive = false;
|
|
129
|
+
|
|
130
|
+
state.prevX = 0;
|
|
131
|
+
state.prevY = 0;
|
|
132
|
+
state.startX = 0;
|
|
133
|
+
state.startY = 0;
|
|
134
|
+
|
|
135
|
+
if (!wasDragging) {
|
|
136
|
+
stopMomentum();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (Math.hypot(state.velocityX, state.velocityY) < minVelocity) {
|
|
141
|
+
stopMomentum();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
state.rafId = requestAnimationFrame(momentumStep);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
element.addEventListener('pointerdown', onPointerDown);
|
|
149
|
+
element.addEventListener('pointermove', onPointerMove);
|
|
150
|
+
window.addEventListener('pointerup', onPointerUpOrCancel);
|
|
151
|
+
window.addEventListener('pointercancel', onPointerUpOrCancel);
|
|
152
|
+
|
|
153
|
+
return () => {
|
|
154
|
+
element.removeEventListener('pointerdown', onPointerDown);
|
|
155
|
+
element.removeEventListener('pointermove', onPointerMove);
|
|
156
|
+
window.removeEventListener('pointerup', onPointerUpOrCancel);
|
|
157
|
+
window.removeEventListener('pointercancel', onPointerUpOrCancel);
|
|
158
|
+
|
|
159
|
+
stopMomentum();
|
|
160
|
+
};
|
|
161
|
+
}, [element, options]);
|
|
162
|
+
}
|