@nikelroot/video-player 0.1.5 → 0.1.7

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/README.md CHANGED
@@ -37,6 +37,8 @@ export function Example() {
37
37
  }
38
38
  ```
39
39
 
40
+ When `active` is `true`, pressing `F` toggles fullscreen.
41
+
40
42
  Build before publishing:
41
43
 
42
44
  ```bash
package/dist/index.cjs CHANGED
@@ -37,6 +37,9 @@ __export(index_exports, {
37
37
  module.exports = __toCommonJS(index_exports);
38
38
 
39
39
  // src/VideoPlayer.tsx
40
+ var import_react3 = require("react");
41
+
42
+ // src/controls.tsx
40
43
  var import_react2 = require("react");
41
44
 
42
45
  // src/styles.ts
@@ -286,6 +289,13 @@ var VideoPlayerStyles = import_styled_components.styled.div`
286
289
  }
287
290
  }
288
291
 
292
+ &:hover {
293
+ .video-right-bar {
294
+ background: rgba(0, 0, 0, 0.1);
295
+ opacity: 1;
296
+ }
297
+ }
298
+
289
299
  ${(props) => (props.$showControl || props.$alwaysShowSegments) && import_styled_components.css`
290
300
  .video-right-bar {
291
301
  background: rgba(0, 0, 0, 0.1);
@@ -420,6 +430,7 @@ var createThrottledNumberFn = (fn, waitMs) => {
420
430
  // src/controls.tsx
421
431
  var import_jsx_runtime = require("react/jsx-runtime");
422
432
  var SeekSlider = ({ playAction, duration, currentTime, seekSetTime }) => {
433
+ const ref = (0, import_react2.useRef)(null);
423
434
  const clickHandler = (e) => {
424
435
  e.preventDefault();
425
436
  e.stopPropagation();
@@ -428,10 +439,14 @@ var SeekSlider = ({ playAction, duration, currentTime, seekSetTime }) => {
428
439
  const safeCurrentTime = Math.max(0, Math.min(Number.isFinite(currentTime) ? currentTime : 0, safeDuration || Infinity));
429
440
  const completeChange = () => {
430
441
  void playAction();
442
+ if (ref.current) {
443
+ ref.current.blur();
444
+ }
431
445
  };
432
446
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SeekSliderStyled, { onClick: clickHandler, className: "seek-slider", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
433
447
  "input",
434
448
  {
449
+ ref,
435
450
  "aria-label": "Seek",
436
451
  className: "seek-range",
437
452
  disabled: safeDuration <= 0,
@@ -533,19 +548,6 @@ var VideoPlayerControls = (props) => {
533
548
  } = props;
534
549
  const [, isMobile] = useWindowWidth();
535
550
  if (variant === "none") return null;
536
- if (variant === "tiny") {
537
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPlayerControlStyles, { $tiny: true, $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "video-player-control-row", children: [
538
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "full" }),
539
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SeekSlider, { playAction, duration, currentTime, seekSetTime })
540
- ] }) });
541
- }
542
- if (variant === "fullscreen-only") {
543
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "video-player-control-row", children: [
544
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "full" }),
545
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: muted ? "mute" : "sound", label: muted ? "Unmute" : "Mute", onClick: () => onMutedChange(!muted) }),
546
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { disabled: !fullscreenAllowed, icon: "fullscreen", label: "Fullscreen", onClick: fullScreenAction })
547
- ] }) });
548
- }
549
551
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "video-player-control-row", children: [
550
552
  playing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: "pause", label: "Pause", onClick: pauseAction }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: "play", label: "Play", onClick: playAction }),
551
553
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: "back", label: "Back 15 seconds", onClick: () => seekPrevAction() }),
@@ -770,6 +772,10 @@ var assignRef = (ref, value) => {
770
772
  }
771
773
  ref.current = value;
772
774
  };
775
+ var isEditableKeyTarget = (target) => {
776
+ if (!(target instanceof HTMLElement)) return false;
777
+ return target.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName);
778
+ };
773
779
  var VideoPlayerBase = (props, ref) => {
774
780
  const {
775
781
  controlsVariant = "none",
@@ -822,40 +828,40 @@ var VideoPlayerBase = (props, ref) => {
822
828
  ...videoProps
823
829
  } = props;
824
830
  const videoSrc = rawVideoSrc.replaceAll(" ", "%20").replaceAll("#", "%23");
825
- const messages = (0, import_react2.useMemo)(() => ({ ...defaultMessages, ...messagesProp }), [messagesProp]);
831
+ const messages = (0, import_react3.useMemo)(() => ({ ...defaultMessages, ...messagesProp }), [messagesProp]);
826
832
  const [, isMobile, mobileType] = useWindowWidth();
827
- const videoRef = (0, import_react2.useRef)(null);
828
- const hlsRef = (0, import_react2.useRef)(null);
829
- const containerRef = (0, import_react2.useRef)(null);
830
- const netRetryCount = (0, import_react2.useRef)(0);
831
- const mediaRecoverCount = (0, import_react2.useRef)(0);
832
- const fatalReloadCount = (0, import_react2.useRef)(0);
833
- const reloadTimer = (0, import_react2.useRef)(null);
834
- const stalledNudgeTimer = (0, import_react2.useRef)(null);
835
- const liveStabilityMode = (0, import_react2.useRef)(false);
836
- const liveStallEvents = (0, import_react2.useRef)([]);
837
- const controlTime = (0, import_react2.useRef)(null);
838
- const [playingState, setPlayingState] = (0, import_react2.useState)(!!defaultPlaying);
839
- const [mutedState, setMutedState] = (0, import_react2.useState)(!!defaultMuted);
840
- const [durationState, setDurationState] = (0, import_react2.useState)(0);
841
- const [currentTimeState, setCurrentTimeState] = (0, import_react2.useState)(0);
842
- const [isFullscreen, setIsFullscreen] = (0, import_react2.useState)(false);
843
- const [showControl, setControlVisible] = (0, import_react2.useState)(true);
844
- const [errorState, setErrorState] = (0, import_react2.useState)(null);
845
- const [reloadToken, setReloadToken] = (0, import_react2.useState)(0);
833
+ const videoRef = (0, import_react3.useRef)(null);
834
+ const hlsRef = (0, import_react3.useRef)(null);
835
+ const containerRef = (0, import_react3.useRef)(null);
836
+ const netRetryCount = (0, import_react3.useRef)(0);
837
+ const mediaRecoverCount = (0, import_react3.useRef)(0);
838
+ const fatalReloadCount = (0, import_react3.useRef)(0);
839
+ const reloadTimer = (0, import_react3.useRef)(null);
840
+ const stalledNudgeTimer = (0, import_react3.useRef)(null);
841
+ const liveStabilityMode = (0, import_react3.useRef)(false);
842
+ const liveStallEvents = (0, import_react3.useRef)([]);
843
+ const controlTime = (0, import_react3.useRef)(null);
844
+ const [playingState, setPlayingState] = (0, import_react3.useState)(!!defaultPlaying);
845
+ const [mutedState, setMutedState] = (0, import_react3.useState)(!!defaultMuted);
846
+ const [durationState, setDurationState] = (0, import_react3.useState)(0);
847
+ const [currentTimeState, setCurrentTimeState] = (0, import_react3.useState)(0);
848
+ const [isFullscreen, setIsFullscreen] = (0, import_react3.useState)(false);
849
+ const [showControl, setControlVisible] = (0, import_react3.useState)(true);
850
+ const [errorState, setErrorState] = (0, import_react3.useState)(null);
851
+ const [reloadToken, setReloadToken] = (0, import_react3.useState)(0);
846
852
  const playing = playingProp != null ? playingProp : playingState;
847
853
  const muted = mutedProp != null ? mutedProp : mutedState;
848
854
  const duration = durationProp != null ? durationProp : durationState;
849
855
  const currentTime = currentTimeProp != null ? currentTimeProp : currentTimeState;
850
856
  const fullscreenAllowed = active != null ? active : true;
851
- const setTimeD = (0, import_react2.useRef)(
857
+ const setTimeD = (0, import_react3.useRef)(
852
858
  createThrottledNumberFn((value) => {
853
859
  const v = videoRef.current;
854
860
  if (!v) return;
855
861
  v.currentTime = value;
856
862
  }, 30)
857
863
  );
858
- const clearTimers = (0, import_react2.useCallback)(() => {
864
+ const clearTimers = (0, import_react3.useCallback)(() => {
859
865
  if (reloadTimer.current) {
860
866
  clearTimeout(reloadTimer.current);
861
867
  reloadTimer.current = null;
@@ -869,14 +875,14 @@ var VideoPlayerBase = (props, ref) => {
869
875
  controlTime.current = null;
870
876
  }
871
877
  }, []);
872
- const setNextPlaying = (0, import_react2.useCallback)(
878
+ const setNextPlaying = (0, import_react3.useCallback)(
873
879
  (value) => {
874
880
  if (playingProp === void 0) setPlayingState(value);
875
881
  onPlayingChange == null ? void 0 : onPlayingChange(value);
876
882
  },
877
883
  [onPlayingChange, playingProp]
878
884
  );
879
- const setNextMuted = (0, import_react2.useCallback)(
885
+ const setNextMuted = (0, import_react3.useCallback)(
880
886
  (value) => {
881
887
  if (mutedProp === void 0) setMutedState(value);
882
888
  const el = videoRef.current;
@@ -885,10 +891,10 @@ var VideoPlayerBase = (props, ref) => {
885
891
  },
886
892
  [mutedProp, onMutedChange]
887
893
  );
888
- const clearErrorState = (0, import_react2.useCallback)(() => {
894
+ const clearErrorState = (0, import_react3.useCallback)(() => {
889
895
  setErrorState((prev) => prev ? null : prev);
890
896
  }, []);
891
- const reportError = (0, import_react2.useCallback)(
897
+ const reportError = (0, import_react3.useCallback)(
892
898
  (error, data, message = messages.defaultError) => {
893
899
  const details = formatPlaybackErrorDetails(error, data);
894
900
  const code = getPlaybackErrorCodeText(error);
@@ -905,27 +911,27 @@ var VideoPlayerBase = (props, ref) => {
905
911
  },
906
912
  [messages.defaultError, onPlaybackError, setNextPlaying]
907
913
  );
908
- const notifyActiveChange = (0, import_react2.useCallback)(
914
+ const notifyActiveChange = (0, import_react3.useCallback)(
909
915
  (video, reason) => {
910
916
  onActiveChange == null ? void 0 : onActiveChange({ video, reason });
911
917
  },
912
918
  [onActiveChange]
913
919
  );
914
- const onTime = (0, import_react2.useCallback)(() => {
920
+ const onTime = (0, import_react3.useCallback)(() => {
915
921
  const el = videoRef.current;
916
922
  if (!el) return;
917
923
  const nextTime = Number.isFinite(el.currentTime) ? el.currentTime : 0;
918
924
  if (currentTimeProp === void 0) setCurrentTimeState(nextTime);
919
925
  onTimeChange == null ? void 0 : onTimeChange(nextTime, el);
920
926
  }, [currentTimeProp, onTimeChange]);
921
- const onDur = (0, import_react2.useCallback)(() => {
927
+ const onDur = (0, import_react3.useCallback)(() => {
922
928
  const el = videoRef.current;
923
929
  if (!el) return;
924
930
  const nextDuration = Number.isFinite(el.duration) ? el.duration : 0;
925
931
  if (durationProp === void 0) setDurationState(nextDuration);
926
932
  onDurationChange == null ? void 0 : onDurationChange(nextDuration, el);
927
933
  }, [durationProp, onDurationChange]);
928
- const setVideoEl = (0, import_react2.useCallback)(
934
+ const setVideoEl = (0, import_react3.useCallback)(
929
935
  (el) => {
930
936
  const prev = videoRef.current;
931
937
  if (prev === el) return;
@@ -944,7 +950,7 @@ var VideoPlayerBase = (props, ref) => {
944
950
  },
945
951
  [externalVideoRef, muted, notifyActiveChange, onDur, onTime, onVideoRefChange]
946
952
  );
947
- const pauseOtherVideos = (0, import_react2.useCallback)(
953
+ const pauseOtherVideos = (0, import_react3.useCallback)(
948
954
  (el) => {
949
955
  if (!exclusivePlayback) return;
950
956
  if (typeof exclusivePlayback === "object") {
@@ -958,7 +964,7 @@ var VideoPlayerBase = (props, ref) => {
958
964
  },
959
965
  [exclusivePlayback]
960
966
  );
961
- const playAction = (0, import_react2.useCallback)(async () => {
967
+ const playAction = (0, import_react3.useCallback)(async () => {
962
968
  const el = videoRef.current;
963
969
  if (!el) return;
964
970
  pauseOtherVideos(el);
@@ -984,14 +990,14 @@ var VideoPlayerBase = (props, ref) => {
984
990
  }
985
991
  }
986
992
  }, [clearErrorState, messages.playFailed, notifyActiveChange, pauseOtherVideos, reportError, scrollTo, setNextPlaying]);
987
- const pauseAction = (0, import_react2.useCallback)(() => {
993
+ const pauseAction = (0, import_react3.useCallback)(() => {
988
994
  const el = videoRef.current;
989
995
  if (!el) return;
990
996
  el.pause();
991
997
  setNextPlaying(false);
992
998
  notifyActiveChange(el, "pause");
993
999
  }, [notifyActiveChange, setNextPlaying]);
994
- const seekSetTime = (0, import_react2.useCallback)(
1000
+ const seekSetTime = (0, import_react3.useCallback)(
995
1001
  (value) => {
996
1002
  const el = videoRef.current;
997
1003
  if (!el) return;
@@ -1002,7 +1008,7 @@ var VideoPlayerBase = (props, ref) => {
1002
1008
  },
1003
1009
  [currentTimeProp, duration, notifyActiveChange]
1004
1010
  );
1005
- const seekPrevAction = (0, import_react2.useCallback)(
1011
+ const seekPrevAction = (0, import_react3.useCallback)(
1006
1012
  (sec = 15) => {
1007
1013
  const el = videoRef.current;
1008
1014
  if (!el) return;
@@ -1010,7 +1016,7 @@ var VideoPlayerBase = (props, ref) => {
1010
1016
  },
1011
1017
  [seekSetTime]
1012
1018
  );
1013
- const seekNextAction = (0, import_react2.useCallback)(
1019
+ const seekNextAction = (0, import_react3.useCallback)(
1014
1020
  (sec = 15) => {
1015
1021
  const el = videoRef.current;
1016
1022
  if (!el) return;
@@ -1018,7 +1024,7 @@ var VideoPlayerBase = (props, ref) => {
1018
1024
  },
1019
1025
  [seekSetTime]
1020
1026
  );
1021
- const toggleAction = (0, import_react2.useCallback)(
1027
+ const toggleAction = (0, import_react3.useCallback)(
1022
1028
  (force) => {
1023
1029
  const el = videoRef.current;
1024
1030
  if (!el) return;
@@ -1030,12 +1036,12 @@ var VideoPlayerBase = (props, ref) => {
1030
1036
  },
1031
1037
  [pauseAction, playAction]
1032
1038
  );
1033
- const nudgeOnStall = (0, import_react2.useCallback)(() => {
1039
+ const nudgeOnStall = (0, import_react3.useCallback)(() => {
1034
1040
  const v = videoRef.current;
1035
1041
  if (!v) return;
1036
1042
  v.currentTime = Math.max(0, v.currentTime + 0.05);
1037
1043
  }, []);
1038
- const shouldSwitchLiveToStability = (0, import_react2.useCallback)(() => {
1044
+ const shouldSwitchLiveToStability = (0, import_react3.useCallback)(() => {
1039
1045
  if (!live || liveStabilityMode.current) return false;
1040
1046
  const now = Date.now();
1041
1047
  liveStallEvents.current = [...liveStallEvents.current.filter((ts) => now - ts <= LIVE_STALL_WINDOW_MS), now];
@@ -1044,7 +1050,7 @@ var VideoPlayerBase = (props, ref) => {
1044
1050
  liveStallEvents.current = [];
1045
1051
  return true;
1046
1052
  }, [live]);
1047
- const resolvedHlsConfig = (0, import_react2.useCallback)(() => {
1053
+ const resolvedHlsConfig = (0, import_react3.useCallback)(() => {
1048
1054
  const modeConfig = live ? liveHlsConfig : vodHlsConfig;
1049
1055
  const base = live ? confLiveStability : confVod;
1050
1056
  return withCredentialsConfig(
@@ -1056,7 +1062,7 @@ var VideoPlayerBase = (props, ref) => {
1056
1062
  hlsCredentials
1057
1063
  );
1058
1064
  }, [hlsConfig, hlsCredentials, live, liveHlsConfig, vodHlsConfig]);
1059
- const loadVideo = (0, import_react2.useCallback)(
1065
+ const loadVideo = (0, import_react3.useCallback)(
1060
1066
  async (startAt = initialTime) => {
1061
1067
  const startVideoRef = videoRef.current;
1062
1068
  if (!startVideoRef) return;
@@ -1193,14 +1199,14 @@ var VideoPlayerBase = (props, ref) => {
1193
1199
  videoSrc
1194
1200
  ]
1195
1201
  );
1196
- const loadVideoNative = (0, import_react2.useCallback)(() => {
1202
+ const loadVideoNative = (0, import_react3.useCallback)(() => {
1197
1203
  const video = videoRef.current;
1198
1204
  if (!video) return;
1199
1205
  video.src = videoSrc;
1200
1206
  video.currentTime = initialTime;
1201
1207
  if (autoPlay) void playAction();
1202
1208
  }, [autoPlay, initialTime, playAction, videoSrc]);
1203
- const loadVideoHls = (0, import_react2.useCallback)(async () => {
1209
+ const loadVideoHls = (0, import_react3.useCallback)(async () => {
1204
1210
  const video = videoRef.current;
1205
1211
  const canNativeHls = !!(video == null ? void 0 : video.canPlayType) && video.canPlayType("application/vnd.apple.mpegurl") !== "";
1206
1212
  let HlsCtor;
@@ -1221,7 +1227,7 @@ var VideoPlayerBase = (props, ref) => {
1221
1227
  }
1222
1228
  reportError(null, { type: "hls", src: videoSrc, reason: "HLS is not supported in this browser" }, messages.unsupported);
1223
1229
  }, [initialTime, loadVideo, loadVideoNative, messages.streamLoadFailed, messages.unsupported, reportError, videoSrc]);
1224
- const destroy = (0, import_react2.useCallback)(() => {
1230
+ const destroy = (0, import_react3.useCallback)(() => {
1225
1231
  clearTimers();
1226
1232
  if (hlsRef.current) {
1227
1233
  hlsRef.current.destroy();
@@ -1252,12 +1258,12 @@ var VideoPlayerBase = (props, ref) => {
1252
1258
  onVideoRefChange,
1253
1259
  setNextPlaying
1254
1260
  ]);
1255
- const videoClickHandler = (0, import_react2.useCallback)(() => {
1261
+ const videoClickHandler = (0, import_react3.useCallback)(() => {
1256
1262
  if (!handleClick) return;
1257
1263
  if (playOnClick) toggleAction();
1258
1264
  onVideoClick == null ? void 0 : onVideoClick();
1259
1265
  }, [handleClick, onVideoClick, playOnClick, toggleAction]);
1260
- const fullScreenAction = (0, import_react2.useCallback)(() => {
1266
+ const fullScreenAction = (0, import_react3.useCallback)(() => {
1261
1267
  if (typeof document === "undefined") return;
1262
1268
  const doc = document;
1263
1269
  const videoEl = videoRef.current;
@@ -1282,36 +1288,36 @@ var VideoPlayerBase = (props, ref) => {
1282
1288
  else if (videoEl.webkitEnterFullscreen) void videoEl.webkitEnterFullscreen();
1283
1289
  }
1284
1290
  }, [fullscreenAllowed]);
1285
- const videoDbClickHandler = (0, import_react2.useCallback)(() => {
1291
+ const videoDbClickHandler = (0, import_react3.useCallback)(() => {
1286
1292
  if (!handleClick) return;
1287
1293
  fullScreenAction();
1288
1294
  onVideoDoubleClick == null ? void 0 : onVideoDoubleClick();
1289
1295
  }, [fullScreenAction, handleClick, onVideoDoubleClick]);
1290
- const showClickHandler = (0, import_react2.useCallback)(() => {
1296
+ const showClickHandler = (0, import_react3.useCallback)(() => {
1291
1297
  setControlVisible((st) => !st);
1292
1298
  }, []);
1293
- const retryLoadHandler = (0, import_react2.useCallback)(() => {
1299
+ const retryLoadHandler = (0, import_react3.useCallback)(() => {
1294
1300
  clearErrorState();
1295
1301
  setReloadToken((value) => value + 1);
1296
1302
  }, [clearErrorState]);
1297
- const videoErrorHandler = (0, import_react2.useCallback)(() => {
1303
+ const videoErrorHandler = (0, import_react3.useCallback)(() => {
1298
1304
  const video = videoRef.current;
1299
1305
  const error = video == null ? void 0 : video.error;
1300
1306
  const src = (video == null ? void 0 : video.currentSrc) || videoSrc;
1301
1307
  if (hlsRef.current && src.startsWith("blob:")) return;
1302
1308
  reportError(error, { type: "native", src }, getNativeVideoErrorMessage(error, messages));
1303
1309
  }, [messages, reportError, videoSrc]);
1304
- const videoLoadedHandler = (0, import_react2.useCallback)(() => {
1310
+ const videoLoadedHandler = (0, import_react3.useCallback)(() => {
1305
1311
  clearErrorState();
1306
1312
  }, [clearErrorState]);
1307
- const mouseMoveHandler = (0, import_react2.useCallback)(() => {
1313
+ const mouseMoveHandler = (0, import_react3.useCallback)(() => {
1308
1314
  if (controlTime.current) clearTimeout(controlTime.current);
1309
1315
  setControlVisible(true);
1310
1316
  controlTime.current = setTimeout(() => {
1311
1317
  setControlVisible(false);
1312
1318
  }, 1e3 * 10);
1313
1319
  }, []);
1314
- (0, import_react2.useImperativeHandle)(
1320
+ (0, import_react3.useImperativeHandle)(
1315
1321
  ref,
1316
1322
  () => ({
1317
1323
  play: playAction,
@@ -1322,24 +1328,24 @@ var VideoPlayerBase = (props, ref) => {
1322
1328
  }),
1323
1329
  [fullScreenAction, pauseAction, playAction, seekSetTime]
1324
1330
  );
1325
- (0, import_react2.useEffect)(() => {
1331
+ (0, import_react3.useEffect)(() => {
1326
1332
  const el = videoRef.current;
1327
1333
  if (!el) return;
1328
1334
  el.muted = muted;
1329
1335
  }, [muted]);
1330
- (0, import_react2.useEffect)(() => {
1336
+ (0, import_react3.useEffect)(() => {
1331
1337
  if (playingProp === void 0) return;
1332
1338
  if (!canAttemptPlayback(videoRef.current)) return;
1333
1339
  if (playingProp) void playAction();
1334
1340
  else pauseAction();
1335
1341
  }, [pauseAction, playAction, playingProp]);
1336
- (0, import_react2.useEffect)(() => {
1342
+ (0, import_react3.useEffect)(() => {
1337
1343
  const el = videoRef.current;
1338
1344
  if (!el || currentTimeProp === void 0) return;
1339
1345
  if (Math.abs(el.currentTime - currentTimeProp) < 0.25) return;
1340
1346
  el.currentTime = clampTime(currentTimeProp, duration);
1341
1347
  }, [currentTimeProp, duration]);
1342
- (0, import_react2.useEffect)(() => {
1348
+ (0, import_react3.useEffect)(() => {
1343
1349
  if (!videoRef.current) return;
1344
1350
  clearErrorState();
1345
1351
  fatalReloadCount.current = 0;
@@ -1375,12 +1381,27 @@ var VideoPlayerBase = (props, ref) => {
1375
1381
  type,
1376
1382
  videoSrc
1377
1383
  ]);
1378
- (0, import_react2.useEffect)(() => {
1384
+ (0, import_react3.useEffect)(() => {
1379
1385
  if (!autoPlay) return;
1380
1386
  if (!canAttemptPlayback(videoRef.current)) return;
1381
1387
  void playAction();
1382
1388
  }, [autoPlay, playAction]);
1383
- (0, import_react2.useEffect)(() => {
1389
+ (0, import_react3.useEffect)(() => {
1390
+ if (typeof document === "undefined") return;
1391
+ const handleKeyDown = (event) => {
1392
+ if (event.code !== "KeyF") return;
1393
+ if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
1394
+ if (active !== true) return;
1395
+ if (isEditableKeyTarget(event.target)) return;
1396
+ event.preventDefault();
1397
+ fullScreenAction();
1398
+ };
1399
+ document.addEventListener("keydown", handleKeyDown);
1400
+ return () => {
1401
+ document.removeEventListener("keydown", handleKeyDown);
1402
+ };
1403
+ }, [active, fullScreenAction]);
1404
+ (0, import_react3.useEffect)(() => {
1384
1405
  if (typeof document === "undefined") return;
1385
1406
  const doc = document;
1386
1407
  const handleFsChange = () => {
@@ -1398,7 +1419,7 @@ var VideoPlayerBase = (props, ref) => {
1398
1419
  document.removeEventListener("MSFullscreenChange", handleFsChange);
1399
1420
  };
1400
1421
  }, []);
1401
- (0, import_react2.useEffect)(() => {
1422
+ (0, import_react3.useEffect)(() => {
1402
1423
  return () => {
1403
1424
  setTimeD.current.cancel();
1404
1425
  destroy();
@@ -1483,7 +1504,7 @@ var VideoPlayerBase = (props, ref) => {
1483
1504
  }
1484
1505
  );
1485
1506
  };
1486
- var VideoPlayer = (0, import_react2.memo)((0, import_react2.forwardRef)(VideoPlayerBase));
1507
+ var VideoPlayer = (0, import_react3.memo)((0, import_react3.forwardRef)(VideoPlayerBase));
1487
1508
  var VideoPlayer_default = VideoPlayer;
1488
1509
  // Annotate the CommonJS export names for ESM import in node:
1489
1510
  0 && (module.exports = {
package/dist/index.js CHANGED
@@ -6,10 +6,13 @@ import {
6
6
  useEffect,
7
7
  useImperativeHandle,
8
8
  useMemo as useMemo2,
9
- useRef,
9
+ useRef as useRef2,
10
10
  useState
11
11
  } from "react";
12
12
 
13
+ // src/controls.tsx
14
+ import { useRef } from "react";
15
+
13
16
  // src/styles.ts
14
17
  import { css, styled } from "styled-components";
15
18
  var VideoPlayerControlStyles = styled.div`
@@ -257,6 +260,13 @@ var VideoPlayerStyles = styled.div`
257
260
  }
258
261
  }
259
262
 
263
+ &:hover {
264
+ .video-right-bar {
265
+ background: rgba(0, 0, 0, 0.1);
266
+ opacity: 1;
267
+ }
268
+ }
269
+
260
270
  ${(props) => (props.$showControl || props.$alwaysShowSegments) && css`
261
271
  .video-right-bar {
262
272
  background: rgba(0, 0, 0, 0.1);
@@ -391,6 +401,7 @@ var createThrottledNumberFn = (fn, waitMs) => {
391
401
  // src/controls.tsx
392
402
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
393
403
  var SeekSlider = ({ playAction, duration, currentTime, seekSetTime }) => {
404
+ const ref = useRef(null);
394
405
  const clickHandler = (e) => {
395
406
  e.preventDefault();
396
407
  e.stopPropagation();
@@ -399,10 +410,14 @@ var SeekSlider = ({ playAction, duration, currentTime, seekSetTime }) => {
399
410
  const safeCurrentTime = Math.max(0, Math.min(Number.isFinite(currentTime) ? currentTime : 0, safeDuration || Infinity));
400
411
  const completeChange = () => {
401
412
  void playAction();
413
+ if (ref.current) {
414
+ ref.current.blur();
415
+ }
402
416
  };
403
417
  return /* @__PURE__ */ jsx(SeekSliderStyled, { onClick: clickHandler, className: "seek-slider", children: /* @__PURE__ */ jsx(
404
418
  "input",
405
419
  {
420
+ ref,
406
421
  "aria-label": "Seek",
407
422
  className: "seek-range",
408
423
  disabled: safeDuration <= 0,
@@ -504,19 +519,6 @@ var VideoPlayerControls = (props) => {
504
519
  } = props;
505
520
  const [, isMobile] = useWindowWidth();
506
521
  if (variant === "none") return null;
507
- if (variant === "tiny") {
508
- return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $tiny: true, $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs("div", { className: "video-player-control-row", children: [
509
- /* @__PURE__ */ jsx("div", { className: "full" }),
510
- /* @__PURE__ */ jsx(SeekSlider, { playAction, duration, currentTime, seekSetTime })
511
- ] }) });
512
- }
513
- if (variant === "fullscreen-only") {
514
- return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs("div", { className: "video-player-control-row", children: [
515
- /* @__PURE__ */ jsx("div", { className: "full" }),
516
- /* @__PURE__ */ jsx(ControlButton, { icon: muted ? "mute" : "sound", label: muted ? "Unmute" : "Mute", onClick: () => onMutedChange(!muted) }),
517
- /* @__PURE__ */ jsx(ControlButton, { disabled: !fullscreenAllowed, icon: "fullscreen", label: "Fullscreen", onClick: fullScreenAction })
518
- ] }) });
519
- }
520
522
  return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs("div", { className: "video-player-control-row", children: [
521
523
  playing ? /* @__PURE__ */ jsx(ControlButton, { icon: "pause", label: "Pause", onClick: pauseAction }) : /* @__PURE__ */ jsx(ControlButton, { icon: "play", label: "Play", onClick: playAction }),
522
524
  /* @__PURE__ */ jsx(ControlButton, { icon: "back", label: "Back 15 seconds", onClick: () => seekPrevAction() }),
@@ -741,6 +743,10 @@ var assignRef = (ref, value) => {
741
743
  }
742
744
  ref.current = value;
743
745
  };
746
+ var isEditableKeyTarget = (target) => {
747
+ if (!(target instanceof HTMLElement)) return false;
748
+ return target.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName);
749
+ };
744
750
  var VideoPlayerBase = (props, ref) => {
745
751
  const {
746
752
  controlsVariant = "none",
@@ -795,17 +801,17 @@ var VideoPlayerBase = (props, ref) => {
795
801
  const videoSrc = rawVideoSrc.replaceAll(" ", "%20").replaceAll("#", "%23");
796
802
  const messages = useMemo2(() => ({ ...defaultMessages, ...messagesProp }), [messagesProp]);
797
803
  const [, isMobile, mobileType] = useWindowWidth();
798
- const videoRef = useRef(null);
799
- const hlsRef = useRef(null);
800
- const containerRef = useRef(null);
801
- const netRetryCount = useRef(0);
802
- const mediaRecoverCount = useRef(0);
803
- const fatalReloadCount = useRef(0);
804
- const reloadTimer = useRef(null);
805
- const stalledNudgeTimer = useRef(null);
806
- const liveStabilityMode = useRef(false);
807
- const liveStallEvents = useRef([]);
808
- const controlTime = useRef(null);
804
+ const videoRef = useRef2(null);
805
+ const hlsRef = useRef2(null);
806
+ const containerRef = useRef2(null);
807
+ const netRetryCount = useRef2(0);
808
+ const mediaRecoverCount = useRef2(0);
809
+ const fatalReloadCount = useRef2(0);
810
+ const reloadTimer = useRef2(null);
811
+ const stalledNudgeTimer = useRef2(null);
812
+ const liveStabilityMode = useRef2(false);
813
+ const liveStallEvents = useRef2([]);
814
+ const controlTime = useRef2(null);
809
815
  const [playingState, setPlayingState] = useState(!!defaultPlaying);
810
816
  const [mutedState, setMutedState] = useState(!!defaultMuted);
811
817
  const [durationState, setDurationState] = useState(0);
@@ -819,7 +825,7 @@ var VideoPlayerBase = (props, ref) => {
819
825
  const duration = durationProp != null ? durationProp : durationState;
820
826
  const currentTime = currentTimeProp != null ? currentTimeProp : currentTimeState;
821
827
  const fullscreenAllowed = active != null ? active : true;
822
- const setTimeD = useRef(
828
+ const setTimeD = useRef2(
823
829
  createThrottledNumberFn((value) => {
824
830
  const v = videoRef.current;
825
831
  if (!v) return;
@@ -1351,6 +1357,21 @@ var VideoPlayerBase = (props, ref) => {
1351
1357
  if (!canAttemptPlayback(videoRef.current)) return;
1352
1358
  void playAction();
1353
1359
  }, [autoPlay, playAction]);
1360
+ useEffect(() => {
1361
+ if (typeof document === "undefined") return;
1362
+ const handleKeyDown = (event) => {
1363
+ if (event.code !== "KeyF") return;
1364
+ if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
1365
+ if (active !== true) return;
1366
+ if (isEditableKeyTarget(event.target)) return;
1367
+ event.preventDefault();
1368
+ fullScreenAction();
1369
+ };
1370
+ document.addEventListener("keydown", handleKeyDown);
1371
+ return () => {
1372
+ document.removeEventListener("keydown", handleKeyDown);
1373
+ };
1374
+ }, [active, fullScreenAction]);
1354
1375
  useEffect(() => {
1355
1376
  if (typeof document === "undefined") return;
1356
1377
  const doc = document;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nikelroot/video-player",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Standalone React video player with HLS support.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",