diff --git a/src/controls/bar/ControlsBar.jsx b/src/controls/bar/ControlsBar.jsx index 076aa5c..9bf8820 100644 --- a/src/controls/bar/ControlsBar.jsx +++ b/src/controls/bar/ControlsBar.jsx @@ -1,7 +1,6 @@ import React from "react"; import * as Tooltip from "@radix-ui/react-tooltip"; import clsx from "clsx"; -import { useVolumeControl } from "../volume/useVolumeControl.js"; import { useFullscreenControl } from "../fullscreen/useFullscreenControl.js"; import { useChannelInfo } from "../info/useChannelInfo.js"; import { useKeyboardControls } from "./useKeyboardControls.js"; @@ -22,9 +21,12 @@ export default function ControlsBar({ showControls, clickToPlayPause, onClickToPlayChange, + volume, + isMuted, + handleVolumeChange, + handleVolumeScroll, + handleMuteToggle, }) { - const { volume, isMuted, handleVolumeChange, handleMuteToggle } = - useVolumeControl(core); const { isFullscreen, handleFullscreenToggle } = useFullscreenControl(videoContainer); const { username, viewerCount, uptime } = useChannelInfo(); @@ -45,6 +47,7 @@ export default function ControlsBar({ >
event.stopPropagation()} className={clsx("controls-bar", !shouldShow && "controls-bar--hidden")} >
@@ -57,6 +60,7 @@ export default function ControlsBar({ volume={volume} isMuted={isMuted} onVolumeChange={handleVolumeChange} + onVolumeScroll={handleVolumeScroll} onMuteToggle={handleMuteToggle} />
diff --git a/src/controls/container/Container.jsx b/src/controls/container/Container.jsx index 31afbd7..14ec27a 100644 --- a/src/controls/container/Container.jsx +++ b/src/controls/container/Container.jsx @@ -3,6 +3,7 @@ import ControlsBar from "../bar/ControlsBar.jsx"; import { useControlsVisibility } from "./useControlsVisibility.js"; import { usePlaybackControl } from "../play/usePlaybackControl.js"; import { usePreferences } from "../usePreferences.js"; +import { useVolumeControl } from "../volume/useVolumeControl.js"; export default function Container({ core, videoContainer }) { const containerRef = useRef(null); @@ -13,6 +14,13 @@ export default function Container({ core, videoContainer }) { ); const { isPlaying, handlePlayPause } = usePlaybackControl(core); const { clickToPlayPause, setClickToPlayPause } = usePreferences(); + const { + volume, + isMuted, + handleVolumeChange, + handleVolumeScroll, + handleMuteToggle, + } = useVolumeControl(core); const handleContainerClick = (e) => { const isInControlsBar = barRef.current?.contains(e.target); @@ -29,6 +37,7 @@ export default function Container({ core, videoContainer }) { ref={containerRef} className="kickstiny-container" onClick={handleContainerClick} + onWheel={handleVolumeScroll} >
); diff --git a/src/controls/volume/VolumeControls.jsx b/src/controls/volume/VolumeControls.jsx index a211a54..456d3a0 100644 --- a/src/controls/volume/VolumeControls.jsx +++ b/src/controls/volume/VolumeControls.jsx @@ -9,6 +9,7 @@ export default function VolumeControls({ volume, isMuted, onVolumeChange, + onVolumeScroll, onMuteToggle, }) { const label = isMuted ? "Unmute" : "Mute"; @@ -36,6 +37,7 @@ export default function VolumeControls({ className="slider" value={[volume]} onValueChange={([value]) => onVolumeChange(value)} + onWheel={onVolumeScroll} min={0} max={100} step={1} diff --git a/src/controls/volume/useVolumeControl.js b/src/controls/volume/useVolumeControl.js index c8c048b..d5895bc 100644 --- a/src/controls/volume/useVolumeControl.js +++ b/src/controls/volume/useVolumeControl.js @@ -27,13 +27,32 @@ export function useVolumeControl(core) { const handleVolumeChange = useCallback( (newVolume) => { const clampedVolume = clampVolume(newVolume); + if (clampedVolume === volume) return; setVolume(clampedVolume); setSavedVolume(clampedVolume); core.setVolume(normalizeVolume(clampedVolume)); core.setMuted(clampedVolume === 0); setIsMuted(clampedVolume === 0); }, - [core, setSavedVolume], + [core, volume, setSavedVolume], + ); + + const handleVolumeScroll = useCallback( + (event) => { + event.preventDefault(); + + if (event.deltaY === 0) return; + + // Step 1 unit if trackpad, 5 units if mouse wheel + const isLikelyTrackpad = + !Number.isInteger(event.deltaY) || + (event.deltaMode === 0 && Math.abs(event.deltaY) < 10); + const stepSize = isLikelyTrackpad ? 1 : 5; + const direction = Math.sign(-event.deltaY); + + handleVolumeChange(volume + direction * stepSize); + }, + [volume, handleVolumeChange], ); const handleMuteToggle = useCallback(() => { @@ -82,6 +101,7 @@ export function useVolumeControl(core) { volume, isMuted, handleVolumeChange, + handleVolumeScroll, handleMuteToggle, }; }