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,
};
}