analytica-frontend-lib 1.1.10 → 1.1.11

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/dist/index.mjs CHANGED
@@ -6772,7 +6772,12 @@ var NotFound = ({
6772
6772
  var NotFound_default = NotFound;
6773
6773
 
6774
6774
  // src/components/VideoPlayer/VideoPlayer.tsx
6775
- import { useRef as useRef9, useState as useState13, useEffect as useEffect11, useCallback } from "react";
6775
+ import {
6776
+ useRef as useRef9,
6777
+ useState as useState13,
6778
+ useEffect as useEffect11,
6779
+ useCallback
6780
+ } from "react";
6776
6781
  import {
6777
6782
  Play as Play2,
6778
6783
  Pause,
@@ -6790,6 +6795,85 @@ var formatTime = (seconds) => {
6790
6795
  const secs = Math.floor(seconds % 60);
6791
6796
  return `${mins}:${secs.toString().padStart(2, "0")}`;
6792
6797
  };
6798
+ var ProgressBar2 = ({
6799
+ currentTime,
6800
+ duration,
6801
+ progressPercentage,
6802
+ onSeek
6803
+ }) => /* @__PURE__ */ jsx36("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsx36(
6804
+ "input",
6805
+ {
6806
+ type: "range",
6807
+ min: 0,
6808
+ max: duration || 100,
6809
+ value: currentTime,
6810
+ onChange: (e) => onSeek(parseFloat(e.target.value)),
6811
+ className: "w-full h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer slider:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500",
6812
+ "aria-label": "Video progress",
6813
+ style: {
6814
+ background: `linear-gradient(to right, var(--color-primary-700) ${progressPercentage}%, var(--color-secondary-300) ${progressPercentage}%)`
6815
+ }
6816
+ }
6817
+ ) });
6818
+ var VolumeControls = ({
6819
+ volume,
6820
+ isMuted,
6821
+ onVolumeChange,
6822
+ onToggleMute
6823
+ }) => /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
6824
+ /* @__PURE__ */ jsx36(
6825
+ IconButton_default,
6826
+ {
6827
+ icon: isMuted ? /* @__PURE__ */ jsx36(SpeakerSlash, { size: 24 }) : /* @__PURE__ */ jsx36(SpeakerHigh2, { size: 24 }),
6828
+ onClick: onToggleMute,
6829
+ "aria-label": isMuted ? "Unmute" : "Mute",
6830
+ className: "!bg-transparent !text-white hover:!bg-white/20"
6831
+ }
6832
+ ),
6833
+ /* @__PURE__ */ jsx36(
6834
+ "input",
6835
+ {
6836
+ type: "range",
6837
+ min: 0,
6838
+ max: 100,
6839
+ value: Math.round(volume * 100),
6840
+ onChange: (e) => onVolumeChange(parseInt(e.target.value)),
6841
+ className: "w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500",
6842
+ "aria-label": "Volume control",
6843
+ style: {
6844
+ background: `linear-gradient(to right, var(--color-primary-700) ${volume * 100}%, var(--color-secondary-300) ${volume * 100}%)`
6845
+ }
6846
+ }
6847
+ )
6848
+ ] });
6849
+ var SpeedMenu = ({
6850
+ showSpeedMenu,
6851
+ playbackRate,
6852
+ onToggleMenu,
6853
+ onSpeedChange
6854
+ }) => /* @__PURE__ */ jsxs30("div", { className: "relative", children: [
6855
+ /* @__PURE__ */ jsx36(
6856
+ IconButton_default,
6857
+ {
6858
+ icon: /* @__PURE__ */ jsx36(DotsThreeVertical2, { size: 24 }),
6859
+ onClick: onToggleMenu,
6860
+ "aria-label": "Playback speed",
6861
+ className: "!bg-transparent !text-white hover:!bg-white/20"
6862
+ }
6863
+ ),
6864
+ showSpeedMenu && /* @__PURE__ */ jsx36("div", { className: "absolute bottom-12 right-0 bg-black/90 rounded-lg p-2 min-w-20", children: [0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => /* @__PURE__ */ jsxs30(
6865
+ "button",
6866
+ {
6867
+ onClick: () => onSpeedChange(speed),
6868
+ className: `block w-full text-left px-3 py-1 text-sm rounded hover:bg-white/20 transition-colors ${playbackRate === speed ? "text-primary-400" : "text-white"}`,
6869
+ children: [
6870
+ speed,
6871
+ "x"
6872
+ ]
6873
+ },
6874
+ speed
6875
+ )) })
6876
+ ] });
6793
6877
  var VideoPlayer = ({
6794
6878
  src,
6795
6879
  poster,
@@ -6814,10 +6898,51 @@ var VideoPlayer = ({
6814
6898
  const [showControls, setShowControls] = useState13(true);
6815
6899
  const [hasCompleted, setHasCompleted] = useState13(false);
6816
6900
  const [showCaptions, setShowCaptions] = useState13(false);
6901
+ useEffect11(() => {
6902
+ setHasCompleted(false);
6903
+ }, [src]);
6817
6904
  const [playbackRate, setPlaybackRate] = useState13(1);
6818
6905
  const [showSpeedMenu, setShowSpeedMenu] = useState13(false);
6819
6906
  const lastSaveTimeRef = useRef9(0);
6820
6907
  const trackRef = useRef9(null);
6908
+ const controlsTimeoutRef = useRef9(null);
6909
+ const lastMousePositionRef = useRef9({ x: 0, y: 0 });
6910
+ const mouseMoveTimeoutRef = useRef9(null);
6911
+ const clearControlsTimeout = useCallback(() => {
6912
+ if (controlsTimeoutRef.current) {
6913
+ clearTimeout(controlsTimeoutRef.current);
6914
+ controlsTimeoutRef.current = null;
6915
+ }
6916
+ }, []);
6917
+ const clearMouseMoveTimeout = useCallback(() => {
6918
+ if (mouseMoveTimeoutRef.current) {
6919
+ clearTimeout(mouseMoveTimeoutRef.current);
6920
+ mouseMoveTimeoutRef.current = null;
6921
+ }
6922
+ }, []);
6923
+ const showControlsWithTimer = useCallback(() => {
6924
+ setShowControls(true);
6925
+ clearControlsTimeout();
6926
+ if (isPlaying) {
6927
+ const timeout = isFullscreen ? 2e3 : 3e3;
6928
+ controlsTimeoutRef.current = window.setTimeout(() => {
6929
+ setShowControls(false);
6930
+ }, timeout);
6931
+ }
6932
+ }, [isPlaying, isFullscreen, clearControlsTimeout]);
6933
+ const handleMouseMove = useCallback(
6934
+ (event) => {
6935
+ const currentX = event.clientX;
6936
+ const currentY = event.clientY;
6937
+ const lastPos = lastMousePositionRef.current;
6938
+ const hasMoved = Math.abs(currentX - lastPos.x) > 5 || Math.abs(currentY - lastPos.y) > 5;
6939
+ if (hasMoved) {
6940
+ lastMousePositionRef.current = { x: currentX, y: currentY };
6941
+ showControlsWithTimer();
6942
+ }
6943
+ },
6944
+ [showControlsWithTimer]
6945
+ );
6821
6946
  useEffect11(() => {
6822
6947
  if (videoRef.current) {
6823
6948
  videoRef.current.volume = volume;
@@ -6825,84 +6950,129 @@ var VideoPlayer = ({
6825
6950
  }
6826
6951
  }, [volume, isMuted]);
6827
6952
  useEffect11(() => {
6828
- if (!autoSave || !storageKey) return;
6829
- const raw = localStorage.getItem(`${storageKey}-${src}`);
6830
- const saved = raw !== null ? Number(raw) : NaN;
6831
- const hasValidSaved = Number.isFinite(saved) && saved >= 0;
6832
- const hasValidInitial = Number.isFinite(initialTime) && initialTime >= 0;
6833
- let start;
6834
- if (hasValidInitial) {
6835
- start = initialTime;
6836
- } else if (hasValidSaved) {
6837
- start = saved;
6953
+ const video = videoRef.current;
6954
+ if (!video) return;
6955
+ const onPlay = () => setIsPlaying(true);
6956
+ const onPause = () => setIsPlaying(false);
6957
+ const onEnded = () => setIsPlaying(false);
6958
+ video.addEventListener("play", onPlay);
6959
+ video.addEventListener("pause", onPause);
6960
+ video.addEventListener("ended", onEnded);
6961
+ return () => {
6962
+ video.removeEventListener("play", onPlay);
6963
+ video.removeEventListener("pause", onPause);
6964
+ video.removeEventListener("ended", onEnded);
6965
+ };
6966
+ }, []);
6967
+ useEffect11(() => {
6968
+ if (isPlaying) {
6969
+ showControlsWithTimer();
6838
6970
  } else {
6839
- start = void 0;
6971
+ clearControlsTimeout();
6972
+ setShowControls(true);
6840
6973
  }
6974
+ }, [isPlaying, showControlsWithTimer, clearControlsTimeout]);
6975
+ useEffect11(() => {
6976
+ const handleFullscreenChange = () => {
6977
+ const isCurrentlyFullscreen = !!document.fullscreenElement;
6978
+ setIsFullscreen(isCurrentlyFullscreen);
6979
+ if (isCurrentlyFullscreen) {
6980
+ showControlsWithTimer();
6981
+ }
6982
+ };
6983
+ document.addEventListener("fullscreenchange", handleFullscreenChange);
6984
+ return () => {
6985
+ document.removeEventListener("fullscreenchange", handleFullscreenChange);
6986
+ };
6987
+ }, [showControlsWithTimer]);
6988
+ const getInitialTime = useCallback(() => {
6989
+ if (!autoSave || !storageKey) {
6990
+ return Number.isFinite(initialTime) && initialTime >= 0 ? initialTime : void 0;
6991
+ }
6992
+ const saved = Number(localStorage.getItem(`${storageKey}-${src}`) || NaN);
6993
+ const hasValidInitial = Number.isFinite(initialTime) && initialTime >= 0;
6994
+ const hasValidSaved = Number.isFinite(saved) && saved >= 0;
6995
+ if (hasValidInitial) return initialTime;
6996
+ if (hasValidSaved) return saved;
6997
+ return void 0;
6998
+ }, [autoSave, storageKey, src, initialTime]);
6999
+ useEffect11(() => {
7000
+ const start = getInitialTime();
6841
7001
  if (start !== void 0 && videoRef.current) {
6842
7002
  videoRef.current.currentTime = start;
6843
7003
  setCurrentTime(start);
6844
7004
  }
6845
- }, [src, storageKey, autoSave, initialTime]);
6846
- const saveProgress = useCallback(() => {
6847
- if (!autoSave || !storageKey) return;
6848
- const now = Date.now();
6849
- if (now - lastSaveTimeRef.current > 5e3) {
6850
- localStorage.setItem(`${storageKey}-${src}`, currentTime.toString());
6851
- lastSaveTimeRef.current = now;
6852
- }
6853
- }, [autoSave, storageKey, src, currentTime]);
6854
- const togglePlayPause = useCallback(() => {
6855
- if (videoRef.current) {
6856
- if (isPlaying) {
6857
- videoRef.current.pause();
6858
- } else {
6859
- videoRef.current.play();
7005
+ }, [getInitialTime]);
7006
+ const saveProgress = useCallback(
7007
+ (time) => {
7008
+ if (!autoSave || !storageKey) return;
7009
+ const now = Date.now();
7010
+ if (now - lastSaveTimeRef.current > 5e3) {
7011
+ localStorage.setItem(`${storageKey}-${src}`, time.toString());
7012
+ lastSaveTimeRef.current = now;
6860
7013
  }
6861
- setIsPlaying(!isPlaying);
7014
+ },
7015
+ [autoSave, storageKey, src]
7016
+ );
7017
+ const togglePlayPause = useCallback(async () => {
7018
+ const video = videoRef.current;
7019
+ if (!video) return;
7020
+ if (!video.paused) {
7021
+ video.pause();
7022
+ return;
6862
7023
  }
6863
- }, [isPlaying]);
7024
+ try {
7025
+ await video.play();
7026
+ } catch {
7027
+ }
7028
+ }, []);
6864
7029
  const handleVolumeChange = useCallback(
6865
7030
  (newVolume) => {
6866
- if (videoRef.current) {
6867
- const volumeValue = newVolume / 100;
6868
- videoRef.current.volume = volumeValue;
6869
- setVolume(volumeValue);
6870
- if (volumeValue === 0) {
6871
- videoRef.current.muted = true;
6872
- setIsMuted(true);
6873
- } else if (isMuted) {
6874
- videoRef.current.muted = false;
6875
- setIsMuted(false);
6876
- }
7031
+ const video = videoRef.current;
7032
+ if (!video) return;
7033
+ const volumeValue = newVolume / 100;
7034
+ video.volume = volumeValue;
7035
+ setVolume(volumeValue);
7036
+ const shouldMute = volumeValue === 0;
7037
+ const shouldUnmute = volumeValue > 0 && isMuted;
7038
+ if (shouldMute) {
7039
+ video.muted = true;
7040
+ setIsMuted(true);
7041
+ } else if (shouldUnmute) {
7042
+ video.muted = false;
7043
+ setIsMuted(false);
6877
7044
  }
6878
7045
  },
6879
7046
  [isMuted]
6880
7047
  );
6881
7048
  const toggleMute = useCallback(() => {
6882
- if (videoRef.current) {
6883
- if (isMuted) {
6884
- const restoreVolume = volume > 0 ? volume : 0.5;
6885
- videoRef.current.volume = restoreVolume;
6886
- videoRef.current.muted = false;
6887
- setVolume(restoreVolume);
6888
- setIsMuted(false);
6889
- } else {
6890
- videoRef.current.muted = true;
6891
- setIsMuted(true);
6892
- }
7049
+ const video = videoRef.current;
7050
+ if (!video) return;
7051
+ if (isMuted) {
7052
+ const restoreVolume = volume > 0 ? volume : 0.5;
7053
+ video.volume = restoreVolume;
7054
+ video.muted = false;
7055
+ setVolume(restoreVolume);
7056
+ setIsMuted(false);
7057
+ } else {
7058
+ video.muted = true;
7059
+ setIsMuted(true);
6893
7060
  }
6894
7061
  }, [isMuted, volume]);
7062
+ const handleSeek = useCallback((newTime) => {
7063
+ const video = videoRef.current;
7064
+ if (video) {
7065
+ video.currentTime = newTime;
7066
+ }
7067
+ }, []);
6895
7068
  const toggleFullscreen = useCallback(() => {
6896
7069
  const container = videoRef.current?.parentElement;
6897
7070
  if (!container) return;
6898
- if (!isFullscreen) {
6899
- if (container.requestFullscreen) {
6900
- container.requestFullscreen();
6901
- }
6902
- } else if (document.exitFullscreen) {
7071
+ if (!isFullscreen && container.requestFullscreen) {
7072
+ container.requestFullscreen();
7073
+ } else if (isFullscreen && document.exitFullscreen) {
6903
7074
  document.exitFullscreen();
6904
7075
  }
6905
- setIsFullscreen(!isFullscreen);
6906
7076
  }, [isFullscreen]);
6907
7077
  const handleSpeedChange = useCallback((speed) => {
6908
7078
  if (videoRef.current) {
@@ -6920,29 +7090,28 @@ var VideoPlayer = ({
6920
7090
  setShowCaptions(newShowCaptions);
6921
7091
  trackRef.current.track.mode = newShowCaptions && subtitles ? "showing" : "hidden";
6922
7092
  }, [showCaptions, subtitles]);
6923
- const handleTimeUpdate = useCallback(() => {
6924
- if (videoRef.current) {
6925
- const current = videoRef.current.currentTime;
6926
- setCurrentTime(current);
6927
- saveProgress();
6928
- onTimeUpdate?.(current);
6929
- if (duration > 0) {
6930
- const progressPercent = current / duration * 100;
6931
- onProgress?.(progressPercent);
6932
- if (progressPercent >= 95 && !hasCompleted) {
6933
- setHasCompleted(true);
6934
- onVideoComplete?.();
6935
- }
7093
+ const checkVideoCompletion = useCallback(
7094
+ (progressPercent) => {
7095
+ if (progressPercent >= 95 && !hasCompleted) {
7096
+ setHasCompleted(true);
7097
+ onVideoComplete?.();
6936
7098
  }
7099
+ },
7100
+ [hasCompleted, onVideoComplete]
7101
+ );
7102
+ const handleTimeUpdate = useCallback(() => {
7103
+ const video = videoRef.current;
7104
+ if (!video) return;
7105
+ const current = video.currentTime;
7106
+ setCurrentTime(current);
7107
+ saveProgress(current);
7108
+ onTimeUpdate?.(current);
7109
+ if (duration > 0) {
7110
+ const progressPercent = current / duration * 100;
7111
+ onProgress?.(progressPercent);
7112
+ checkVideoCompletion(progressPercent);
6937
7113
  }
6938
- }, [
6939
- duration,
6940
- saveProgress,
6941
- onTimeUpdate,
6942
- onProgress,
6943
- onVideoComplete,
6944
- hasCompleted
6945
- ]);
7114
+ }, [duration, saveProgress, onTimeUpdate, onProgress, checkVideoCompletion]);
6946
7115
  const handleLoadedMetadata = useCallback(() => {
6947
7116
  if (videoRef.current) {
6948
7117
  setDuration(videoRef.current.duration);
@@ -6971,9 +7140,78 @@ var VideoPlayer = ({
6971
7140
  return () => {
6972
7141
  document.removeEventListener("visibilitychange", handleVisibilityChange);
6973
7142
  window.removeEventListener("blur", handleBlur);
7143
+ clearControlsTimeout();
7144
+ clearMouseMoveTimeout();
6974
7145
  };
6975
- }, [isPlaying]);
7146
+ }, [isPlaying, clearControlsTimeout, clearMouseMoveTimeout]);
6976
7147
  const progressPercentage = duration > 0 ? currentTime / duration * 100 : 0;
7148
+ const getTopControlsOpacity = useCallback(() => {
7149
+ if (isFullscreen) {
7150
+ return showControls ? "opacity-100" : "opacity-0";
7151
+ }
7152
+ return !isPlaying || showControls ? "opacity-100" : "opacity-0 group-hover:opacity-100";
7153
+ }, [isFullscreen, showControls, isPlaying]);
7154
+ const getBottomControlsOpacity = useCallback(() => {
7155
+ if (isFullscreen) {
7156
+ return showControls ? "opacity-100" : "opacity-0";
7157
+ }
7158
+ return !isPlaying || showControls ? "opacity-100" : "opacity-0 group-hover:opacity-100";
7159
+ }, [isFullscreen, showControls, isPlaying]);
7160
+ const handleVideoKeyDown = useCallback(
7161
+ (e) => {
7162
+ if (e.key) {
7163
+ e.stopPropagation();
7164
+ showControlsWithTimer();
7165
+ }
7166
+ switch (e.key) {
7167
+ case " ":
7168
+ case "Enter":
7169
+ e.preventDefault();
7170
+ togglePlayPause();
7171
+ break;
7172
+ case "ArrowLeft":
7173
+ e.preventDefault();
7174
+ if (videoRef.current) {
7175
+ videoRef.current.currentTime -= 10;
7176
+ }
7177
+ break;
7178
+ case "ArrowRight":
7179
+ e.preventDefault();
7180
+ if (videoRef.current) {
7181
+ videoRef.current.currentTime += 10;
7182
+ }
7183
+ break;
7184
+ case "ArrowUp":
7185
+ e.preventDefault();
7186
+ handleVolumeChange(Math.min(100, volume * 100 + 10));
7187
+ break;
7188
+ case "ArrowDown":
7189
+ e.preventDefault();
7190
+ handleVolumeChange(Math.max(0, volume * 100 - 10));
7191
+ break;
7192
+ case "m":
7193
+ case "M":
7194
+ e.preventDefault();
7195
+ toggleMute();
7196
+ break;
7197
+ case "f":
7198
+ case "F":
7199
+ e.preventDefault();
7200
+ toggleFullscreen();
7201
+ break;
7202
+ default:
7203
+ break;
7204
+ }
7205
+ },
7206
+ [
7207
+ showControlsWithTimer,
7208
+ togglePlayPause,
7209
+ handleVolumeChange,
7210
+ volume,
7211
+ toggleMute,
7212
+ toggleFullscreen
7213
+ ]
7214
+ );
6977
7215
  return /* @__PURE__ */ jsxs30("div", { className: cn("flex flex-col", className), children: [
6978
7216
  (title || subtitleText) && /* @__PURE__ */ jsx36("div", { className: "bg-subject-1 rounded-t-xl px-8 py-4 flex items-end justify-between min-h-20", children: /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6979
7217
  title && /* @__PURE__ */ jsx36(
@@ -7000,12 +7238,17 @@ var VideoPlayer = ({
7000
7238
  )
7001
7239
  ] }) }),
7002
7240
  /* @__PURE__ */ jsxs30(
7003
- "div",
7241
+ "section",
7004
7242
  {
7005
7243
  className: cn(
7006
7244
  "relative w-full bg-background overflow-hidden group",
7007
- title || subtitleText ? "rounded-b-xl" : "rounded-xl"
7245
+ title || subtitleText ? "rounded-b-xl" : "rounded-xl",
7246
+ // Hide cursor when controls are hidden and video is playing in fullscreen
7247
+ isFullscreen && isPlaying && !showControls ? "cursor-none" : "cursor-default"
7008
7248
  ),
7249
+ "aria-label": title ? `Video player: ${title}` : "Video player",
7250
+ onMouseMove: isFullscreen ? handleMouseMove : showControlsWithTimer,
7251
+ onMouseEnter: showControlsWithTimer,
7009
7252
  children: [
7010
7253
  /* @__PURE__ */ jsx36(
7011
7254
  "video",
@@ -7018,39 +7261,7 @@ var VideoPlayer = ({
7018
7261
  onTimeUpdate: handleTimeUpdate,
7019
7262
  onLoadedMetadata: handleLoadedMetadata,
7020
7263
  onClick: togglePlayPause,
7021
- onKeyDown: (e) => {
7022
- if (e.key) {
7023
- setShowControls(true);
7024
- }
7025
- if (e.key === " " || e.key === "Enter") {
7026
- e.preventDefault();
7027
- togglePlayPause();
7028
- }
7029
- if (e.key === "ArrowLeft" && videoRef.current) {
7030
- e.preventDefault();
7031
- videoRef.current.currentTime -= 10;
7032
- }
7033
- if (e.key === "ArrowRight" && videoRef.current) {
7034
- e.preventDefault();
7035
- videoRef.current.currentTime += 10;
7036
- }
7037
- if (e.key === "ArrowUp") {
7038
- e.preventDefault();
7039
- handleVolumeChange(Math.min(100, volume * 100 + 10));
7040
- }
7041
- if (e.key === "ArrowDown") {
7042
- e.preventDefault();
7043
- handleVolumeChange(Math.max(0, volume * 100 - 10));
7044
- }
7045
- if (e.key === "m" || e.key === "M") {
7046
- e.preventDefault();
7047
- toggleMute();
7048
- }
7049
- if (e.key === "f" || e.key === "F") {
7050
- e.preventDefault();
7051
- toggleFullscreen();
7052
- }
7053
- },
7264
+ onKeyDown: handleVideoKeyDown,
7054
7265
  tabIndex: 0,
7055
7266
  "aria-label": title ? `Video: ${title}` : "Video player",
7056
7267
  children: /* @__PURE__ */ jsx36(
@@ -7080,9 +7291,9 @@ var VideoPlayer = ({
7080
7291
  {
7081
7292
  className: cn(
7082
7293
  "absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity",
7083
- !isPlaying || showControls ? "opacity-100" : "opacity-0 group-hover:opacity-100"
7294
+ getTopControlsOpacity()
7084
7295
  ),
7085
- children: /* @__PURE__ */ jsx36("div", { className: "ml-auto block", children: /* @__PURE__ */ jsx36(
7296
+ children: /* @__PURE__ */ jsx36("div", { className: "flex justify-start", children: /* @__PURE__ */ jsx36(
7086
7297
  IconButton_default,
7087
7298
  {
7088
7299
  icon: isFullscreen ? /* @__PURE__ */ jsx36(ArrowsInSimple, { size: 24 }) : /* @__PURE__ */ jsx36(ArrowsOutSimple, { size: 24 }),
@@ -7098,29 +7309,18 @@ var VideoPlayer = ({
7098
7309
  {
7099
7310
  className: cn(
7100
7311
  "absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 to-transparent transition-opacity",
7101
- !isPlaying || showControls ? "opacity-100" : "opacity-0 group-hover:opacity-100"
7312
+ getBottomControlsOpacity()
7102
7313
  ),
7103
7314
  children: [
7104
- /* @__PURE__ */ jsx36("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsx36(
7105
- "input",
7315
+ /* @__PURE__ */ jsx36(
7316
+ ProgressBar2,
7106
7317
  {
7107
- type: "range",
7108
- min: 0,
7109
- max: duration || 100,
7110
- value: currentTime,
7111
- onChange: (e) => {
7112
- const newTime = parseFloat(e.target.value);
7113
- if (videoRef.current) {
7114
- videoRef.current.currentTime = newTime;
7115
- }
7116
- },
7117
- className: "w-full h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer slider:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500",
7118
- "aria-label": "Video progress",
7119
- style: {
7120
- background: `linear-gradient(to right, #2271C4 ${progressPercentage}%, #D5D4D4 ${progressPercentage}%)`
7121
- }
7318
+ currentTime,
7319
+ duration,
7320
+ progressPercentage,
7321
+ onSeek: handleSeek
7122
7322
  }
7123
- ) }),
7323
+ ),
7124
7324
  /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between px-4 pb-4", children: [
7125
7325
  /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-4", children: [
7126
7326
  /* @__PURE__ */ jsx36(
@@ -7132,32 +7332,15 @@ var VideoPlayer = ({
7132
7332
  className: "!bg-transparent !text-white hover:!bg-white/20"
7133
7333
  }
7134
7334
  ),
7135
- /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
7136
- /* @__PURE__ */ jsx36(
7137
- IconButton_default,
7138
- {
7139
- icon: isMuted ? /* @__PURE__ */ jsx36(SpeakerSlash, { size: 24 }) : /* @__PURE__ */ jsx36(SpeakerHigh2, { size: 24 }),
7140
- onClick: toggleMute,
7141
- "aria-label": isMuted ? "Unmute" : "Mute",
7142
- className: "!bg-transparent !text-white hover:!bg-white/20"
7143
- }
7144
- ),
7145
- /* @__PURE__ */ jsx36(
7146
- "input",
7147
- {
7148
- type: "range",
7149
- min: 0,
7150
- max: 100,
7151
- value: Math.round(volume * 100),
7152
- onChange: (e) => handleVolumeChange(parseInt(e.target.value)),
7153
- className: "w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500",
7154
- "aria-label": "Volume control",
7155
- style: {
7156
- background: `linear-gradient(to right, #2271C4 ${volume * 100}%, #D5D4D4 ${volume * 100}%)`
7157
- }
7158
- }
7159
- )
7160
- ] }),
7335
+ /* @__PURE__ */ jsx36(
7336
+ VolumeControls,
7337
+ {
7338
+ volume,
7339
+ isMuted,
7340
+ onVolumeChange: handleVolumeChange,
7341
+ onToggleMute: toggleMute
7342
+ }
7343
+ ),
7161
7344
  subtitles && /* @__PURE__ */ jsx36(
7162
7345
  IconButton_default,
7163
7346
  {
@@ -7176,29 +7359,15 @@ var VideoPlayer = ({
7176
7359
  formatTime(duration)
7177
7360
  ] })
7178
7361
  ] }),
7179
- /* @__PURE__ */ jsx36("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsxs30("div", { className: "relative", children: [
7180
- /* @__PURE__ */ jsx36(
7181
- IconButton_default,
7182
- {
7183
- icon: /* @__PURE__ */ jsx36(DotsThreeVertical2, { size: 24 }),
7184
- onClick: toggleSpeedMenu,
7185
- "aria-label": "Playback speed",
7186
- className: "!bg-transparent !text-white hover:!bg-white/20"
7187
- }
7188
- ),
7189
- showSpeedMenu && /* @__PURE__ */ jsx36("div", { className: "absolute bottom-12 right-0 bg-black/90 rounded-lg p-2 min-w-20", children: [0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => /* @__PURE__ */ jsxs30(
7190
- "button",
7191
- {
7192
- onClick: () => handleSpeedChange(speed),
7193
- className: `block w-full text-left px-3 py-1 text-sm rounded hover:bg-white/20 transition-colors ${playbackRate === speed ? "text-primary-400" : "text-white"}`,
7194
- children: [
7195
- speed,
7196
- "x"
7197
- ]
7198
- },
7199
- speed
7200
- )) })
7201
- ] }) })
7362
+ /* @__PURE__ */ jsx36("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsx36(
7363
+ SpeedMenu,
7364
+ {
7365
+ showSpeedMenu,
7366
+ playbackRate,
7367
+ onToggleMenu: toggleSpeedMenu,
7368
+ onSpeedChange: handleSpeedChange
7369
+ }
7370
+ ) })
7202
7371
  ] })
7203
7372
  ]
7204
7373
  }