@stream-io/video-react-sdk 1.28.2 → 1.29.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.
- package/CHANGELOG.md +12 -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 +4 -4
- package/src/core/components/CallLayout/SpeakerLayout.tsx +11 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useDragToScroll.ts +162 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.29.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-1.28.2...@stream-io/video-react-sdk-1.29.0) (2025-12-18)
|
|
6
|
+
|
|
7
|
+
### Dependency Updates
|
|
8
|
+
|
|
9
|
+
- `@stream-io/audio-filters-web` updated to version `0.7.0`
|
|
10
|
+
- `@stream-io/video-client` updated to version `1.39.0`
|
|
11
|
+
- `@stream-io/video-react-bindings` updated to version `1.12.3`
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
- **react:** Drag scroll on the participants list in the default layouts ([#2042](https://github.com/GetStream/stream-video-js/issues/2042)) ([b0f3f37](https://github.com/GetStream/stream-video-js/commit/b0f3f37ef45967625dca81af04ee5eb44df9d485))
|
|
16
|
+
|
|
5
17
|
## [1.28.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-1.28.1...@stream-io/video-react-sdk-1.28.2) (2025-12-11)
|
|
6
18
|
|
|
7
19
|
### Dependency Updates
|
package/dist/index.cjs.js
CHANGED
|
@@ -594,6 +594,118 @@ const useModeration = (options) => {
|
|
|
594
594
|
react.useEffect(() => disableBlur, [disableBlur]);
|
|
595
595
|
};
|
|
596
596
|
|
|
597
|
+
/**
|
|
598
|
+
* Enables drag-to-scroll functionality with momentum scrolling on a scrollable element.
|
|
599
|
+
*
|
|
600
|
+
* This hook allows users to click and drag to scroll an element, with momentum scrolling
|
|
601
|
+
* that continues after the drag ends. The drag only activates after moving beyond a threshold
|
|
602
|
+
* distance, which prevents accidental drags from clicks.
|
|
603
|
+
*
|
|
604
|
+
* @param element - The HTML element to enable drag to scroll on.
|
|
605
|
+
* @param options - Options for customizing the drag-to-scroll behavior.
|
|
606
|
+
*/
|
|
607
|
+
function useDragToScroll(element, options = {}) {
|
|
608
|
+
const stateRef = react.useRef({
|
|
609
|
+
isDragging: false,
|
|
610
|
+
isPointerActive: false,
|
|
611
|
+
prevX: 0,
|
|
612
|
+
prevY: 0,
|
|
613
|
+
velocityX: 0,
|
|
614
|
+
velocityY: 0,
|
|
615
|
+
rafId: 0,
|
|
616
|
+
startX: 0,
|
|
617
|
+
startY: 0,
|
|
618
|
+
});
|
|
619
|
+
react.useEffect(() => {
|
|
620
|
+
if (!element || !options.enabled)
|
|
621
|
+
return;
|
|
622
|
+
const { decay = 0.95, minVelocity = 0.5, dragThreshold = 5 } = options;
|
|
623
|
+
const state = stateRef.current;
|
|
624
|
+
const stopMomentum = () => {
|
|
625
|
+
if (state.rafId) {
|
|
626
|
+
cancelAnimationFrame(state.rafId);
|
|
627
|
+
state.rafId = 0;
|
|
628
|
+
}
|
|
629
|
+
state.velocityX = 0;
|
|
630
|
+
state.velocityY = 0;
|
|
631
|
+
};
|
|
632
|
+
const momentumStep = () => {
|
|
633
|
+
state.velocityX *= decay;
|
|
634
|
+
state.velocityY *= decay;
|
|
635
|
+
element.scrollLeft -= state.velocityX;
|
|
636
|
+
element.scrollTop -= state.velocityY;
|
|
637
|
+
if (Math.abs(state.velocityX) < minVelocity &&
|
|
638
|
+
Math.abs(state.velocityY) < minVelocity) {
|
|
639
|
+
state.rafId = 0;
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
state.rafId = requestAnimationFrame(momentumStep);
|
|
643
|
+
};
|
|
644
|
+
const onPointerDown = (e) => {
|
|
645
|
+
if (e.pointerType !== 'mouse')
|
|
646
|
+
return;
|
|
647
|
+
stopMomentum();
|
|
648
|
+
state.isDragging = false;
|
|
649
|
+
state.isPointerActive = true;
|
|
650
|
+
state.prevX = e.clientX;
|
|
651
|
+
state.prevY = e.clientY;
|
|
652
|
+
state.startX = e.clientX;
|
|
653
|
+
state.startY = e.clientY;
|
|
654
|
+
};
|
|
655
|
+
const onPointerMove = (e) => {
|
|
656
|
+
if (e.pointerType !== 'mouse')
|
|
657
|
+
return;
|
|
658
|
+
if (!state.isPointerActive)
|
|
659
|
+
return;
|
|
660
|
+
const dx = e.clientX - state.startX;
|
|
661
|
+
const dy = e.clientY - state.startY;
|
|
662
|
+
if (!state.isDragging && Math.hypot(dx, dy) > dragThreshold) {
|
|
663
|
+
state.isDragging = true;
|
|
664
|
+
e.preventDefault();
|
|
665
|
+
}
|
|
666
|
+
if (!state.isDragging)
|
|
667
|
+
return;
|
|
668
|
+
const moveDx = e.clientX - state.prevX;
|
|
669
|
+
const moveDy = e.clientY - state.prevY;
|
|
670
|
+
element.scrollLeft -= moveDx;
|
|
671
|
+
element.scrollTop -= moveDy;
|
|
672
|
+
state.velocityX = moveDx;
|
|
673
|
+
state.velocityY = moveDy;
|
|
674
|
+
state.prevX = e.clientX;
|
|
675
|
+
state.prevY = e.clientY;
|
|
676
|
+
};
|
|
677
|
+
const onPointerUpOrCancel = () => {
|
|
678
|
+
const wasDragging = state.isDragging;
|
|
679
|
+
state.isDragging = false;
|
|
680
|
+
state.isPointerActive = false;
|
|
681
|
+
state.prevX = 0;
|
|
682
|
+
state.prevY = 0;
|
|
683
|
+
state.startX = 0;
|
|
684
|
+
state.startY = 0;
|
|
685
|
+
if (!wasDragging) {
|
|
686
|
+
stopMomentum();
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (Math.hypot(state.velocityX, state.velocityY) < minVelocity) {
|
|
690
|
+
stopMomentum();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
state.rafId = requestAnimationFrame(momentumStep);
|
|
694
|
+
};
|
|
695
|
+
element.addEventListener('pointerdown', onPointerDown);
|
|
696
|
+
element.addEventListener('pointermove', onPointerMove);
|
|
697
|
+
window.addEventListener('pointerup', onPointerUpOrCancel);
|
|
698
|
+
window.addEventListener('pointercancel', onPointerUpOrCancel);
|
|
699
|
+
return () => {
|
|
700
|
+
element.removeEventListener('pointerdown', onPointerDown);
|
|
701
|
+
element.removeEventListener('pointermove', onPointerMove);
|
|
702
|
+
window.removeEventListener('pointerup', onPointerUpOrCancel);
|
|
703
|
+
window.removeEventListener('pointercancel', onPointerUpOrCancel);
|
|
704
|
+
stopMomentum();
|
|
705
|
+
};
|
|
706
|
+
}, [element, options]);
|
|
707
|
+
}
|
|
708
|
+
|
|
597
709
|
exports.MenuVisualType = void 0;
|
|
598
710
|
(function (MenuVisualType) {
|
|
599
711
|
MenuVisualType["PORTAL"] = "portal";
|
|
@@ -1452,7 +1564,7 @@ const SpeakerTest = (props) => {
|
|
|
1452
1564
|
const audioElementRef = react.useRef(null);
|
|
1453
1565
|
const [isPlaying, setIsPlaying] = react.useState(false);
|
|
1454
1566
|
const { t } = videoReactBindings.useI18n();
|
|
1455
|
-
const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.
|
|
1567
|
+
const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.29.0"}/assets/piano.mp3`, } = props;
|
|
1456
1568
|
// Update audio output device when selection changes
|
|
1457
1569
|
react.useEffect(() => {
|
|
1458
1570
|
const audio = audioElementRef.current;
|
|
@@ -2945,7 +3057,7 @@ hostElement, limit) => {
|
|
|
2945
3057
|
};
|
|
2946
3058
|
|
|
2947
3059
|
const DefaultParticipantViewUIBar = () => (jsxRuntime.jsx(DefaultParticipantViewUI, { menuPlacement: "top-end" }));
|
|
2948
|
-
const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, ParticipantViewUISpotlight = DefaultParticipantViewUI, VideoPlaceholder, PictureInPicturePlaceholder, participantsBarPosition = 'bottom', participantsBarLimit, mirrorLocalParticipantVideo = true, excludeLocalParticipant = false, filterParticipants, pageArrowsVisible = true, muted, }) => {
|
|
3060
|
+
const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, ParticipantViewUISpotlight = DefaultParticipantViewUI, VideoPlaceholder, PictureInPicturePlaceholder, participantsBarPosition = 'bottom', participantsBarLimit, mirrorLocalParticipantVideo = true, excludeLocalParticipant = false, filterParticipants, pageArrowsVisible = true, muted, enableDragToScroll = false, }) => {
|
|
2949
3061
|
const call = videoReactBindings.useCall();
|
|
2950
3062
|
const { useParticipants } = videoReactBindings.useCallStateHooks();
|
|
2951
3063
|
const allParticipants = useParticipants();
|
|
@@ -2966,6 +3078,9 @@ const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, Par
|
|
|
2966
3078
|
}, [participantsBarWrapperElement, call]);
|
|
2967
3079
|
const isOneOnOneCall = allParticipants.length === 2;
|
|
2968
3080
|
useSpeakerLayoutSortPreset(call, isOneOnOneCall);
|
|
3081
|
+
useDragToScroll(participantsBarWrapperElement, {
|
|
3082
|
+
enabled: enableDragToScroll,
|
|
3083
|
+
});
|
|
2969
3084
|
let participantsWithAppliedLimit = otherParticipants;
|
|
2970
3085
|
const hardLimitToApply = isVertical
|
|
2971
3086
|
? hardLimit.vertical
|
|
@@ -3118,7 +3233,7 @@ const checkCanJoinEarly = (startsAt, joinAheadTimeSeconds) => {
|
|
|
3118
3233
|
return Date.now() >= +startsAt - (joinAheadTimeSeconds ?? 0) * 1000;
|
|
3119
3234
|
};
|
|
3120
3235
|
|
|
3121
|
-
const [major, minor, patch] = ("1.
|
|
3236
|
+
const [major, minor, patch] = ("1.29.0").split('.');
|
|
3122
3237
|
videoClient.setSdkInfo({
|
|
3123
3238
|
type: videoClient.SfuModels.SdkType.REACT,
|
|
3124
3239
|
major,
|