@videojs/react 10.0.0-beta.15 → 10.0.0-beta.17

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.
Files changed (137) hide show
  1. package/dist/default/index.js +7 -1
  2. package/dist/default/media/dash-video/index.js +3 -2
  3. package/dist/default/media/dash-video/index.js.map +1 -1
  4. package/dist/default/media/hls-video/index.js +3 -2
  5. package/dist/default/media/hls-video/index.js.map +1 -1
  6. package/dist/default/media/mux-audio/index.js +20 -0
  7. package/dist/default/media/mux-audio/index.js.map +1 -0
  8. package/dist/default/media/mux-video/index.js +3 -2
  9. package/dist/default/media/mux-video/index.js.map +1 -1
  10. package/dist/default/media/native-hls-video/index.js +3 -2
  11. package/dist/default/media/native-hls-video/index.js.map +1 -1
  12. package/dist/default/media/simple-hls-video/index.js +3 -2
  13. package/dist/default/media/simple-hls-video/index.js.map +1 -1
  14. package/dist/default/player/context.js +9 -1
  15. package/dist/default/player/context.js.map +1 -1
  16. package/dist/default/presets/audio/minimal-skin.css +23 -0
  17. package/dist/default/presets/audio/skin.css +23 -0
  18. package/dist/default/presets/audio/skin.js +65 -0
  19. package/dist/default/presets/audio/skin.js.map +1 -1
  20. package/dist/default/presets/video/minimal-skin.css +23 -0
  21. package/dist/default/presets/video/skin.css +23 -0
  22. package/dist/default/presets/video/skin.js +107 -1
  23. package/dist/default/presets/video/skin.js.map +1 -1
  24. package/dist/default/ui/captions-button/captions-button.js +2 -1
  25. package/dist/default/ui/captions-button/captions-button.js.map +1 -1
  26. package/dist/default/ui/create-media-button.js +7 -2
  27. package/dist/default/ui/create-media-button.js.map +1 -1
  28. package/dist/default/ui/fullscreen-button/fullscreen-button.js +2 -1
  29. package/dist/default/ui/fullscreen-button/fullscreen-button.js.map +1 -1
  30. package/dist/default/ui/gesture/media-gesture.js +42 -0
  31. package/dist/default/ui/gesture/media-gesture.js.map +1 -0
  32. package/dist/default/ui/gesture/use-doubletap-gesture.js +29 -0
  33. package/dist/default/ui/gesture/use-doubletap-gesture.js.map +1 -0
  34. package/dist/default/ui/gesture/use-tap-gesture.js +29 -0
  35. package/dist/default/ui/gesture/use-tap-gesture.js.map +1 -0
  36. package/dist/default/ui/hooks/use-slider.js +1 -1
  37. package/dist/default/ui/hooks/use-slider.js.map +1 -1
  38. package/dist/default/ui/hotkey/media-hotkey.js +41 -0
  39. package/dist/default/ui/hotkey/media-hotkey.js.map +1 -0
  40. package/dist/default/ui/hotkey/use-aria-key-shortcuts.js +16 -0
  41. package/dist/default/ui/hotkey/use-aria-key-shortcuts.js.map +1 -0
  42. package/dist/default/ui/hotkey/use-hotkey.js +32 -0
  43. package/dist/default/ui/hotkey/use-hotkey.js.map +1 -0
  44. package/dist/default/ui/mute-button/mute-button.js +2 -1
  45. package/dist/default/ui/mute-button/mute-button.js.map +1 -1
  46. package/dist/default/ui/pip-button/pip-button.js +2 -1
  47. package/dist/default/ui/pip-button/pip-button.js.map +1 -1
  48. package/dist/default/ui/play-button/play-button.js +2 -1
  49. package/dist/default/ui/play-button/play-button.js.map +1 -1
  50. package/dist/default/ui/time-slider/time-slider-root.js +2 -2
  51. package/dist/default/ui/time-slider/time-slider-root.js.map +1 -1
  52. package/dist/dev/index.d.ts +7 -1
  53. package/dist/dev/index.js +7 -1
  54. package/dist/dev/media/dash-video/index.d.ts +4 -1
  55. package/dist/dev/media/dash-video/index.d.ts.map +1 -1
  56. package/dist/dev/media/dash-video/index.js +3 -2
  57. package/dist/dev/media/dash-video/index.js.map +1 -1
  58. package/dist/dev/media/hls-video/index.d.ts +4 -1
  59. package/dist/dev/media/hls-video/index.d.ts.map +1 -1
  60. package/dist/dev/media/hls-video/index.js +3 -2
  61. package/dist/dev/media/hls-video/index.js.map +1 -1
  62. package/dist/dev/media/mux-audio/index.d.ts +13 -0
  63. package/dist/dev/media/mux-audio/index.d.ts.map +1 -0
  64. package/dist/dev/media/mux-audio/index.js +20 -0
  65. package/dist/dev/media/mux-audio/index.js.map +1 -0
  66. package/dist/dev/media/mux-video/index.d.ts +4 -1
  67. package/dist/dev/media/mux-video/index.d.ts.map +1 -1
  68. package/dist/dev/media/mux-video/index.js +3 -2
  69. package/dist/dev/media/mux-video/index.js.map +1 -1
  70. package/dist/dev/media/native-hls-video/index.d.ts +4 -1
  71. package/dist/dev/media/native-hls-video/index.d.ts.map +1 -1
  72. package/dist/dev/media/native-hls-video/index.js +3 -2
  73. package/dist/dev/media/native-hls-video/index.js.map +1 -1
  74. package/dist/dev/media/simple-hls-video/index.d.ts +4 -1
  75. package/dist/dev/media/simple-hls-video/index.d.ts.map +1 -1
  76. package/dist/dev/media/simple-hls-video/index.js +3 -2
  77. package/dist/dev/media/simple-hls-video/index.js.map +1 -1
  78. package/dist/dev/player/context.d.ts.map +1 -1
  79. package/dist/dev/player/context.js +9 -1
  80. package/dist/dev/player/context.js.map +1 -1
  81. package/dist/dev/presets/audio/minimal-skin.css +23 -0
  82. package/dist/dev/presets/audio/skin.css +23 -0
  83. package/dist/dev/presets/audio/skin.d.ts.map +1 -1
  84. package/dist/dev/presets/audio/skin.js +65 -0
  85. package/dist/dev/presets/audio/skin.js.map +1 -1
  86. package/dist/dev/presets/video/minimal-skin.css +23 -0
  87. package/dist/dev/presets/video/skin.css +23 -0
  88. package/dist/dev/presets/video/skin.d.ts.map +1 -1
  89. package/dist/dev/presets/video/skin.js +107 -1
  90. package/dist/dev/presets/video/skin.js.map +1 -1
  91. package/dist/dev/ui/captions-button/captions-button.d.ts.map +1 -1
  92. package/dist/dev/ui/captions-button/captions-button.js +2 -1
  93. package/dist/dev/ui/captions-button/captions-button.js.map +1 -1
  94. package/dist/dev/ui/create-media-button.js +7 -2
  95. package/dist/dev/ui/create-media-button.js.map +1 -1
  96. package/dist/dev/ui/fullscreen-button/fullscreen-button.d.ts.map +1 -1
  97. package/dist/dev/ui/fullscreen-button/fullscreen-button.js +2 -1
  98. package/dist/dev/ui/fullscreen-button/fullscreen-button.js.map +1 -1
  99. package/dist/dev/ui/gesture/media-gesture.d.ts +23 -0
  100. package/dist/dev/ui/gesture/media-gesture.d.ts.map +1 -0
  101. package/dist/dev/ui/gesture/media-gesture.js +42 -0
  102. package/dist/dev/ui/gesture/media-gesture.js.map +1 -0
  103. package/dist/dev/ui/gesture/use-doubletap-gesture.d.ts +14 -0
  104. package/dist/dev/ui/gesture/use-doubletap-gesture.d.ts.map +1 -0
  105. package/dist/dev/ui/gesture/use-doubletap-gesture.js +29 -0
  106. package/dist/dev/ui/gesture/use-doubletap-gesture.js.map +1 -0
  107. package/dist/dev/ui/gesture/use-tap-gesture.d.ts +14 -0
  108. package/dist/dev/ui/gesture/use-tap-gesture.d.ts.map +1 -0
  109. package/dist/dev/ui/gesture/use-tap-gesture.js +29 -0
  110. package/dist/dev/ui/gesture/use-tap-gesture.js.map +1 -0
  111. package/dist/dev/ui/hooks/use-slider.d.ts +1 -1
  112. package/dist/dev/ui/hooks/use-slider.js +1 -1
  113. package/dist/dev/ui/hooks/use-slider.js.map +1 -1
  114. package/dist/dev/ui/hotkey/media-hotkey.d.ts +21 -0
  115. package/dist/dev/ui/hotkey/media-hotkey.d.ts.map +1 -0
  116. package/dist/dev/ui/hotkey/media-hotkey.js +41 -0
  117. package/dist/dev/ui/hotkey/media-hotkey.js.map +1 -0
  118. package/dist/dev/ui/hotkey/use-aria-key-shortcuts.d.ts +5 -0
  119. package/dist/dev/ui/hotkey/use-aria-key-shortcuts.d.ts.map +1 -0
  120. package/dist/dev/ui/hotkey/use-aria-key-shortcuts.js +16 -0
  121. package/dist/dev/ui/hotkey/use-aria-key-shortcuts.js.map +1 -0
  122. package/dist/dev/ui/hotkey/use-hotkey.d.ts +12 -0
  123. package/dist/dev/ui/hotkey/use-hotkey.d.ts.map +1 -0
  124. package/dist/dev/ui/hotkey/use-hotkey.js +32 -0
  125. package/dist/dev/ui/hotkey/use-hotkey.js.map +1 -0
  126. package/dist/dev/ui/mute-button/mute-button.d.ts.map +1 -1
  127. package/dist/dev/ui/mute-button/mute-button.js +2 -1
  128. package/dist/dev/ui/mute-button/mute-button.js.map +1 -1
  129. package/dist/dev/ui/pip-button/pip-button.d.ts.map +1 -1
  130. package/dist/dev/ui/pip-button/pip-button.js +2 -1
  131. package/dist/dev/ui/pip-button/pip-button.js.map +1 -1
  132. package/dist/dev/ui/play-button/play-button.d.ts.map +1 -1
  133. package/dist/dev/ui/play-button/play-button.js +2 -1
  134. package/dist/dev/ui/play-button/play-button.js.map +1 -1
  135. package/dist/dev/ui/time-slider/time-slider-root.js +2 -2
  136. package/dist/dev/ui/time-slider/time-slider-root.js.map +1 -1
  137. package/package.json +11 -11
@@ -9,6 +9,8 @@ import { ControlsRoot } from "../../ui/controls/controls-root.js";
9
9
  import { ErrorDialogDescription } from "../../ui/error-dialog/error-dialog-description.js";
10
10
  import { ErrorDialogRoot } from "../../ui/error-dialog/error-dialog-root.js";
11
11
  import { FullscreenButton } from "../../ui/fullscreen-button/fullscreen-button.js";
12
+ import { MediaGesture } from "../../ui/gesture/media-gesture.js";
13
+ import { MediaHotkey } from "../../ui/hotkey/media-hotkey.js";
12
14
  import { MuteButton } from "../../ui/mute-button/mute-button.js";
13
15
  import { PiPButton } from "../../ui/pip-button/pip-button.js";
14
16
  import { PlayButton } from "../../ui/play-button/play-button.js";
@@ -268,7 +270,111 @@ function VideoSkin(props) {
268
270
  })
269
271
  ] })
270
272
  }),
271
- /* @__PURE__ */ jsx("div", { className: "media-overlay" })
273
+ /* @__PURE__ */ jsx("div", { className: "media-overlay" }),
274
+ /* @__PURE__ */ jsx(MediaHotkey, {
275
+ keys: "Space",
276
+ action: "togglePaused"
277
+ }),
278
+ /* @__PURE__ */ jsx(MediaHotkey, {
279
+ keys: "k",
280
+ action: "togglePaused"
281
+ }),
282
+ /* @__PURE__ */ jsx(MediaHotkey, {
283
+ keys: "m",
284
+ action: "toggleMuted"
285
+ }),
286
+ /* @__PURE__ */ jsx(MediaHotkey, {
287
+ keys: "f",
288
+ action: "toggleFullscreen"
289
+ }),
290
+ /* @__PURE__ */ jsx(MediaHotkey, {
291
+ keys: "c",
292
+ action: "toggleSubtitles"
293
+ }),
294
+ /* @__PURE__ */ jsx(MediaHotkey, {
295
+ keys: "i",
296
+ action: "togglePictureInPicture"
297
+ }),
298
+ /* @__PURE__ */ jsx(MediaHotkey, {
299
+ keys: "ArrowRight",
300
+ action: "seekStep",
301
+ value: 5
302
+ }),
303
+ /* @__PURE__ */ jsx(MediaHotkey, {
304
+ keys: "ArrowLeft",
305
+ action: "seekStep",
306
+ value: -5
307
+ }),
308
+ /* @__PURE__ */ jsx(MediaHotkey, {
309
+ keys: "l",
310
+ action: "seekStep",
311
+ value: 10
312
+ }),
313
+ /* @__PURE__ */ jsx(MediaHotkey, {
314
+ keys: "j",
315
+ action: "seekStep",
316
+ value: -10
317
+ }),
318
+ /* @__PURE__ */ jsx(MediaHotkey, {
319
+ keys: "ArrowUp",
320
+ action: "volumeStep",
321
+ value: .05
322
+ }),
323
+ /* @__PURE__ */ jsx(MediaHotkey, {
324
+ keys: "ArrowDown",
325
+ action: "volumeStep",
326
+ value: -.05
327
+ }),
328
+ /* @__PURE__ */ jsx(MediaHotkey, {
329
+ keys: "0-9",
330
+ action: "seekToPercent"
331
+ }),
332
+ /* @__PURE__ */ jsx(MediaHotkey, {
333
+ keys: "Home",
334
+ action: "seekToPercent",
335
+ value: 0
336
+ }),
337
+ /* @__PURE__ */ jsx(MediaHotkey, {
338
+ keys: "End",
339
+ action: "seekToPercent",
340
+ value: 100
341
+ }),
342
+ /* @__PURE__ */ jsx(MediaHotkey, {
343
+ keys: ">",
344
+ action: "speedUp"
345
+ }),
346
+ /* @__PURE__ */ jsx(MediaHotkey, {
347
+ keys: "<",
348
+ action: "speedDown"
349
+ }),
350
+ /* @__PURE__ */ jsx(MediaGesture, {
351
+ type: "tap",
352
+ action: "togglePaused",
353
+ pointer: "mouse",
354
+ region: "center"
355
+ }),
356
+ /* @__PURE__ */ jsx(MediaGesture, {
357
+ type: "tap",
358
+ action: "toggleControls",
359
+ pointer: "touch"
360
+ }),
361
+ /* @__PURE__ */ jsx(MediaGesture, {
362
+ type: "doubletap",
363
+ action: "seekStep",
364
+ value: -10,
365
+ region: "left"
366
+ }),
367
+ /* @__PURE__ */ jsx(MediaGesture, {
368
+ type: "doubletap",
369
+ action: "toggleFullscreen",
370
+ region: "center"
371
+ }),
372
+ /* @__PURE__ */ jsx(MediaGesture, {
373
+ type: "doubletap",
374
+ action: "seekStep",
375
+ value: 10,
376
+ region: "right"
377
+ })
272
378
  ]
273
379
  });
274
380
  }
@@ -1 +1 @@
1
- {"version":3,"file":"skin.js","names":["Popover.Root","Popover.Trigger","Popover.Popup","VolumeSlider.Root","VolumeSlider.Track","VolumeSlider.Fill","VolumeSlider.Thumb","ErrorDialog.Root","ErrorDialog.Popup","ErrorDialog.Title","ErrorDialog.Description","ErrorDialog.Close","Controls.Root","Tooltip.Provider","Tooltip.Root","Tooltip.Trigger","Tooltip.Popup","Time.Value","TimeSlider.Root","TimeSlider.Track","TimeSlider.Fill","TimeSlider.Buffer","TimeSlider.Thumb","Slider.Thumbnail","TimeSlider.Value"],"sources":["../../../../src/presets/video/skin.tsx"],"sourcesContent":["import {\n CaptionsOffIcon,\n CaptionsOnIcon,\n FullscreenEnterIcon,\n FullscreenExitIcon,\n PauseIcon,\n PipEnterIcon,\n PipExitIcon,\n PlayIcon,\n RestartIcon,\n SeekIcon,\n SpinnerIcon,\n VolumeHighIcon,\n VolumeLowIcon,\n VolumeOffIcon,\n} from '@videojs/icons/react';\nimport { isString } from '@videojs/utils/predicate';\nimport { cn } from '@videojs/utils/style';\nimport { type ComponentProps, forwardRef, type ReactNode } from 'react';\nimport { Container, usePlayer } from '@/player/context';\nimport { BufferingIndicator } from '@/ui/buffering-indicator';\nimport { CaptionsButton } from '@/ui/captions-button';\nimport { Controls } from '@/ui/controls';\nimport { ErrorDialog } from '@/ui/error-dialog';\nimport { FullscreenButton } from '@/ui/fullscreen-button';\nimport { MuteButton } from '@/ui/mute-button';\nimport { PiPButton } from '@/ui/pip-button';\nimport { PlayButton } from '@/ui/play-button';\nimport { PlaybackRateButton } from '@/ui/playback-rate-button';\nimport { Popover } from '@/ui/popover';\nimport { Poster } from '@/ui/poster';\nimport { SeekButton } from '@/ui/seek-button';\nimport { Slider } from '@/ui/slider';\nimport { Time } from '@/ui/time';\nimport { TimeSlider } from '@/ui/time-slider';\nimport { Tooltip } from '@/ui/tooltip';\nimport { VolumeSlider } from '@/ui/volume-slider';\nimport { isRenderProp } from '@/utils/use-render';\nimport type { BaseVideoSkinProps } from '../types';\n\nconst SEEK_TIME = 10;\n\nexport type VideoSkinProps = BaseVideoSkinProps;\n\nconst Button = forwardRef<HTMLButtonElement, ComponentProps<'button'>>(function Button({ className, ...props }, ref) {\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn('media-button media-button--subtle media-button--icon', className)}\n {...props}\n />\n );\n});\n\nfunction VolumePopover(): ReactNode {\n const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');\n\n const muteButton = (\n <MuteButton className=\"media-button--mute\" render={<Button />}>\n <VolumeOffIcon className=\"media-icon media-icon--volume-off\" />\n <VolumeLowIcon className=\"media-icon media-icon--volume-low\" />\n <VolumeHighIcon className=\"media-icon media-icon--volume-high\" />\n </MuteButton>\n );\n\n if (volumeUnsupported) return muteButton;\n\n return (\n <Popover.Root openOnHover delay={200} closeDelay={100} side=\"top\">\n <Popover.Trigger render={muteButton} />\n <Popover.Popup className=\"media-surface media-popover media-popover--volume\">\n <VolumeSlider.Root className=\"media-slider\" orientation=\"vertical\" thumbAlignment=\"edge\">\n <VolumeSlider.Track className=\"media-slider__track\">\n <VolumeSlider.Fill className=\"media-slider__fill\" />\n </VolumeSlider.Track>\n <VolumeSlider.Thumb className=\"media-slider__thumb media-slider__thumb--persistent\" />\n </VolumeSlider.Root>\n </Popover.Popup>\n </Popover.Root>\n );\n}\n\nexport function VideoSkin(props: VideoSkinProps): ReactNode {\n const { children, className, poster, ...rest } = props;\n\n return (\n <Container className={cn('media-default-skin media-default-skin--video', className)} {...rest}>\n {children}\n\n {poster && (\n <Poster src={isString(poster) ? poster : undefined} render={isRenderProp(poster) ? poster : undefined} />\n )}\n\n <BufferingIndicator\n render={(props) => (\n <div {...props} className=\"media-buffering-indicator\">\n <div className=\"media-surface\">\n <SpinnerIcon className=\"media-icon\" />\n </div>\n </div>\n )}\n />\n\n <ErrorDialog.Root>\n <ErrorDialog.Popup className=\"media-error\">\n <div className=\"media-error__dialog media-surface\">\n <div className=\"media-error__content\">\n <ErrorDialog.Title className=\"media-error__title\">Something went wrong.</ErrorDialog.Title>\n <ErrorDialog.Description className=\"media-error__description\" />\n </div>\n <div className=\"media-error__actions\">\n <ErrorDialog.Close className=\"media-button media-button--primary\">OK</ErrorDialog.Close>\n </div>\n </div>\n </ErrorDialog.Popup>\n </ErrorDialog.Root>\n\n <Controls.Root className=\"media-surface media-controls\">\n <Tooltip.Provider>\n <div className=\"media-button-group\">\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <PlayButton className=\"media-button--play\" render={<Button />}>\n <RestartIcon className=\"media-icon media-icon--restart\" />\n <PlayIcon className=\"media-icon media-icon--play\" />\n <PauseIcon className=\"media-icon media-icon--pause\" />\n </PlayButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\" />\n </Tooltip.Root>\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <SeekButton seconds={-SEEK_TIME} className=\"media-button--seek\" render={<Button />}>\n <span className=\"media-icon__container\">\n <SeekIcon className=\"media-icon media-icon--seek media-icon--flipped\" />\n <span className=\"media-icon__label\">{SEEK_TIME}</span>\n </span>\n </SeekButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\">Seek backward {SEEK_TIME} seconds</Tooltip.Popup>\n </Tooltip.Root>\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <SeekButton seconds={SEEK_TIME} className=\"media-button--seek\" render={<Button />}>\n <span className=\"media-icon__container\">\n <SeekIcon className=\"media-icon media-icon--seek\" />\n <span className=\"media-icon__label\">{SEEK_TIME}</span>\n </span>\n </SeekButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\">Seek forward {SEEK_TIME} seconds</Tooltip.Popup>\n </Tooltip.Root>\n </div>\n\n <div className=\"media-time-controls\">\n <Time.Value type=\"current\" className=\"media-time\" />\n <TimeSlider.Root className=\"media-slider\">\n <TimeSlider.Track className=\"media-slider__track\">\n <TimeSlider.Fill className=\"media-slider__fill\" />\n <TimeSlider.Buffer className=\"media-slider__buffer\" />\n </TimeSlider.Track>\n <TimeSlider.Thumb className=\"media-slider__thumb\" />\n\n <div className=\"media-surface media-preview media-slider__preview\">\n <Slider.Thumbnail className=\"media-preview__thumbnail\" />\n <TimeSlider.Value type=\"pointer\" className=\"media-time media-preview__time\" />\n <SpinnerIcon className=\"media-preview__spinner media-icon\" />\n </div>\n </TimeSlider.Root>\n <Time.Value type=\"duration\" className=\"media-time\" />\n </div>\n\n <div className=\"media-button-group\">\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={<PlaybackRateButton className=\"media-button--playback-rate\" render={<Button />} />}\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\">Toggle playback rate</Tooltip.Popup>\n </Tooltip.Root>\n\n <VolumePopover />\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <CaptionsButton className=\"media-button--captions\" render={<Button />}>\n <CaptionsOffIcon className=\"media-icon media-icon--captions-off\" />\n <CaptionsOnIcon className=\"media-icon media-icon--captions-on\" />\n </CaptionsButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\" />\n </Tooltip.Root>\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <PiPButton className=\"media-button--pip\" render={<Button />}>\n <PipEnterIcon className=\"media-icon media-icon--pip-enter\" />\n <PipExitIcon className=\"media-icon media-icon--pip-exit\" />\n </PiPButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\" />\n </Tooltip.Root>\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <FullscreenButton className=\"media-button--fullscreen\" render={<Button />}>\n <FullscreenEnterIcon className=\"media-icon media-icon--fullscreen-enter\" />\n <FullscreenExitIcon className=\"media-icon media-icon--fullscreen-exit\" />\n </FullscreenButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\" />\n </Tooltip.Root>\n </div>\n </Tooltip.Provider>\n </Controls.Root>\n\n <div className=\"media-overlay\" />\n </Container>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAM,YAAY;AAIlB,MAAM,SAAS,WAAwD,SAAS,OAAO,EAAE,WAAW,GAAG,SAAS,KAAK;AACnH,QACE,oBAAC,UAAD;EACO;EACL,MAAK;EACL,WAAW,GAAG,wDAAwD,UAAU;EAChF,GAAI;EACJ,CAAA;EAEJ;AAEF,SAAS,gBAA2B;CAClC,MAAM,oBAAoB,WAAW,MAAM,EAAE,uBAAuB,cAAc;CAElF,MAAM,aACJ,qBAAC,YAAD;EAAY,WAAU;EAAqB,QAAQ,oBAAC,QAAD,EAAU,CAAA;YAA7D;GACE,oBAAC,eAAD,EAAe,WAAU,qCAAsC,CAAA;GAC/D,oBAAC,eAAD,EAAe,WAAU,qCAAsC,CAAA;GAC/D,oBAAC,gBAAD,EAAgB,WAAU,sCAAuC,CAAA;GACtD;;AAGf,KAAI,kBAAmB,QAAO;AAE9B,QACE,qBAACA,aAAD;EAAc,aAAA;EAAY,OAAO;EAAK,YAAY;EAAK,MAAK;YAA5D,CACE,oBAACC,gBAAD,EAAiB,QAAQ,YAAc,CAAA,EACvC,oBAACC,cAAD;GAAe,WAAU;aACvB,qBAACC,kBAAD;IAAmB,WAAU;IAAe,aAAY;IAAW,gBAAe;cAAlF,CACE,oBAACC,aAAD;KAAoB,WAAU;eAC5B,oBAACC,YAAD,EAAmB,WAAU,sBAAuB,CAAA;KACjC,CAAA,EACrB,oBAACC,aAAD,EAAoB,WAAU,uDAAwD,CAAA,CACpE;;GACN,CAAA,CACH;;;AAInB,SAAgB,UAAU,OAAkC;CAC1D,MAAM,EAAE,UAAU,WAAW,QAAQ,GAAG,SAAS;AAEjD,QACE,qBAAC,WAAD;EAAW,WAAW,GAAG,gDAAgD,UAAU;EAAE,GAAI;YAAzF;GACG;GAEA,UACC,oBAAC,QAAD;IAAQ,KAAK,SAAS,OAAO,GAAG,SAAS,KAAA;IAAW,QAAQ,aAAa,OAAO,GAAG,SAAS,KAAA;IAAa,CAAA;GAG3G,oBAAC,oBAAD,EACE,SAAS,UACP,oBAAC,OAAD;IAAK,GAAI;IAAO,WAAU;cACxB,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,aAAD,EAAa,WAAU,cAAe,CAAA;KAClC,CAAA;IACF,CAAA,EAER,CAAA;GAEF,oBAACC,iBAAD,EAAA,UACE,oBAACC,kBAAD;IAAmB,WAAU;cAC3B,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAACC,kBAAD;OAAmB,WAAU;iBAAqB;OAAyC,CAAA,EAC3F,oBAACC,wBAAD,EAAyB,WAAU,4BAA6B,CAAA,CAC5D;SACN,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAACC,kBAAD;OAAmB,WAAU;iBAAqC;OAAsB,CAAA;MACpF,CAAA,CACF;;IACY,CAAA,EACH,CAAA;GAEnB,oBAACC,cAAD;IAAe,WAAU;cACvB,qBAACC,iBAAD,EAAA,UAAA;KACE,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,qBAACC,aAAD;QAAc,MAAK;kBAAnB,CACE,oBAACC,gBAAD,EACE,QACE,qBAAC,YAAD;SAAY,WAAU;SAAqB,QAAQ,oBAAC,QAAD,EAAU,CAAA;mBAA7D;UACE,oBAAC,aAAD,EAAa,WAAU,kCAAmC,CAAA;UAC1D,oBAAC,UAAD,EAAU,WAAU,+BAAgC,CAAA;UACpD,oBAAC,WAAD,EAAW,WAAU,gCAAiC,CAAA;UAC3C;YAEf,CAAA,EACF,oBAACC,cAAD,EAAe,WAAU,+BAAgC,CAAA,CAC5C;;OAEf,qBAACF,aAAD;QAAc,MAAK;kBAAnB,CACE,oBAACC,gBAAD,EACE,QACE,oBAAC,YAAD;SAAY,SAAS,CAAC;SAAW,WAAU;SAAqB,QAAQ,oBAAC,QAAD,EAAU,CAAA;mBAChF,qBAAC,QAAD;UAAM,WAAU;oBAAhB,CACE,oBAAC,UAAD,EAAU,WAAU,mDAAoD,CAAA,EACxE,oBAAC,QAAD;WAAM,WAAU;qBAAqB;WAAiB,CAAA,CACjD;;SACI,CAAA,EAEf,CAAA,EACF,qBAACC,cAAD;SAAe,WAAU;mBAAzB;UAAuD;UAAe;UAAU;UAAwB;WAC3F;;OAEf,qBAACF,aAAD;QAAc,MAAK;kBAAnB,CACE,oBAACC,gBAAD,EACE,QACE,oBAAC,YAAD;SAAY,SAAS;SAAW,WAAU;SAAqB,QAAQ,oBAAC,QAAD,EAAU,CAAA;mBAC/E,qBAAC,QAAD;UAAM,WAAU;oBAAhB,CACE,oBAAC,UAAD,EAAU,WAAU,+BAAgC,CAAA,EACpD,oBAAC,QAAD;WAAM,WAAU;qBAAqB;WAAiB,CAAA,CACjD;;SACI,CAAA,EAEf,CAAA,EACF,qBAACC,cAAD;SAAe,WAAU;mBAAzB;UAAuD;UAAc;UAAU;UAAwB;WAC1F;;OACX;;KAEN,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,oBAACC,OAAD;QAAY,MAAK;QAAU,WAAU;QAAe,CAAA;OACpD,qBAACC,gBAAD;QAAiB,WAAU;kBAA3B;SACE,qBAACC,aAAD;UAAkB,WAAU;oBAA5B,CACE,oBAACC,YAAD,EAAiB,WAAU,sBAAuB,CAAA,EAClD,oBAACC,cAAD,EAAmB,WAAU,wBAAyB,CAAA,CACrC;;SACnB,oBAACC,aAAD,EAAkB,WAAU,uBAAwB,CAAA;SAEpD,qBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,oBAACC,iBAAD,EAAkB,WAAU,4BAA6B,CAAA;WACzD,oBAACC,aAAD;YAAkB,MAAK;YAAU,WAAU;YAAmC,CAAA;WAC9E,oBAAC,aAAD,EAAa,WAAU,qCAAsC,CAAA;WACzD;;SACU;;OAClB,oBAACP,OAAD;QAAY,MAAK;QAAW,WAAU;QAAe,CAAA;OACjD;;KAEN,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,qBAACH,aAAD;QAAc,MAAK;kBAAnB,CACE,oBAACC,gBAAD,EACE,QAAQ,oBAAC,oBAAD;SAAoB,WAAU;SAA8B,QAAQ,oBAAC,QAAD,EAAU,CAAA;SAAI,CAAA,EAC1F,CAAA,EACF,oBAACC,cAAD;SAAe,WAAU;mBAA8B;SAAoC,CAAA,CAC9E;;OAEf,oBAAC,eAAD,EAAiB,CAAA;OAEjB,qBAACF,aAAD;QAAc,MAAK;kBAAnB,CACE,oBAACC,gBAAD,EACE,QACE,qBAAC,gBAAD;SAAgB,WAAU;SAAyB,QAAQ,oBAAC,QAAD,EAAU,CAAA;mBAArE,CACE,oBAAC,iBAAD,EAAiB,WAAU,uCAAwC,CAAA,EACnE,oBAAC,gBAAD,EAAgB,WAAU,sCAAuC,CAAA,CAClD;YAEnB,CAAA,EACF,oBAACC,cAAD,EAAe,WAAU,+BAAgC,CAAA,CAC5C;;OAEf,qBAACF,aAAD;QAAc,MAAK;kBAAnB,CACE,oBAACC,gBAAD,EACE,QACE,qBAAC,WAAD;SAAW,WAAU;SAAoB,QAAQ,oBAAC,QAAD,EAAU,CAAA;mBAA3D,CACE,oBAAC,cAAD,EAAc,WAAU,oCAAqC,CAAA,EAC7D,oBAAC,aAAD,EAAa,WAAU,mCAAoC,CAAA,CACjD;YAEd,CAAA,EACF,oBAACC,cAAD,EAAe,WAAU,+BAAgC,CAAA,CAC5C;;OAEf,qBAACF,aAAD;QAAc,MAAK;kBAAnB,CACE,oBAACC,gBAAD,EACE,QACE,qBAAC,kBAAD;SAAkB,WAAU;SAA2B,QAAQ,oBAAC,QAAD,EAAU,CAAA;mBAAzE,CACE,oBAAC,qBAAD,EAAqB,WAAU,2CAA4C,CAAA,EAC3E,oBAAC,oBAAD,EAAoB,WAAU,0CAA2C,CAAA,CACxD;YAErB,CAAA,EACF,oBAACC,cAAD,EAAe,WAAU,+BAAgC,CAAA,CAC5C;;OACX;;KACW,EAAA,CAAA;IACL,CAAA;GAEhB,oBAAC,OAAD,EAAK,WAAU,iBAAkB,CAAA;GACvB"}
1
+ {"version":3,"file":"skin.js","names":["Popover.Root","Popover.Trigger","Popover.Popup","VolumeSlider.Root","VolumeSlider.Track","VolumeSlider.Fill","VolumeSlider.Thumb","ErrorDialog.Root","ErrorDialog.Popup","ErrorDialog.Title","ErrorDialog.Description","ErrorDialog.Close","Controls.Root","Tooltip.Provider","Tooltip.Root","Tooltip.Trigger","Tooltip.Popup","Time.Value","TimeSlider.Root","TimeSlider.Track","TimeSlider.Fill","TimeSlider.Buffer","TimeSlider.Thumb","Slider.Thumbnail","TimeSlider.Value"],"sources":["../../../../src/presets/video/skin.tsx"],"sourcesContent":["import {\n CaptionsOffIcon,\n CaptionsOnIcon,\n FullscreenEnterIcon,\n FullscreenExitIcon,\n PauseIcon,\n PipEnterIcon,\n PipExitIcon,\n PlayIcon,\n RestartIcon,\n SeekIcon,\n SpinnerIcon,\n VolumeHighIcon,\n VolumeLowIcon,\n VolumeOffIcon,\n} from '@videojs/icons/react';\nimport { isString } from '@videojs/utils/predicate';\nimport { cn } from '@videojs/utils/style';\nimport { type ComponentProps, forwardRef, type ReactNode } from 'react';\nimport { Container, usePlayer } from '@/player/context';\nimport { BufferingIndicator } from '@/ui/buffering-indicator';\nimport { CaptionsButton } from '@/ui/captions-button';\nimport { Controls } from '@/ui/controls';\nimport { ErrorDialog } from '@/ui/error-dialog';\nimport { FullscreenButton } from '@/ui/fullscreen-button';\nimport { MediaGesture } from '@/ui/gesture/media-gesture';\nimport { MediaHotkey } from '@/ui/hotkey/media-hotkey';\nimport { MuteButton } from '@/ui/mute-button';\nimport { PiPButton } from '@/ui/pip-button';\nimport { PlayButton } from '@/ui/play-button';\nimport { PlaybackRateButton } from '@/ui/playback-rate-button';\nimport { Popover } from '@/ui/popover';\nimport { Poster } from '@/ui/poster';\nimport { SeekButton } from '@/ui/seek-button';\nimport { Slider } from '@/ui/slider';\nimport { Time } from '@/ui/time';\nimport { TimeSlider } from '@/ui/time-slider';\nimport { Tooltip } from '@/ui/tooltip';\nimport { VolumeSlider } from '@/ui/volume-slider';\nimport { isRenderProp } from '@/utils/use-render';\nimport type { BaseVideoSkinProps } from '../types';\n\nconst SEEK_TIME = 10;\n\nexport type VideoSkinProps = BaseVideoSkinProps;\n\nconst Button = forwardRef<HTMLButtonElement, ComponentProps<'button'>>(function Button({ className, ...props }, ref) {\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn('media-button media-button--subtle media-button--icon', className)}\n {...props}\n />\n );\n});\n\nfunction VolumePopover(): ReactNode {\n const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');\n\n const muteButton = (\n <MuteButton className=\"media-button--mute\" render={<Button />}>\n <VolumeOffIcon className=\"media-icon media-icon--volume-off\" />\n <VolumeLowIcon className=\"media-icon media-icon--volume-low\" />\n <VolumeHighIcon className=\"media-icon media-icon--volume-high\" />\n </MuteButton>\n );\n\n if (volumeUnsupported) return muteButton;\n\n return (\n <Popover.Root openOnHover delay={200} closeDelay={100} side=\"top\">\n <Popover.Trigger render={muteButton} />\n <Popover.Popup className=\"media-surface media-popover media-popover--volume\">\n <VolumeSlider.Root className=\"media-slider\" orientation=\"vertical\" thumbAlignment=\"edge\">\n <VolumeSlider.Track className=\"media-slider__track\">\n <VolumeSlider.Fill className=\"media-slider__fill\" />\n </VolumeSlider.Track>\n <VolumeSlider.Thumb className=\"media-slider__thumb media-slider__thumb--persistent\" />\n </VolumeSlider.Root>\n </Popover.Popup>\n </Popover.Root>\n );\n}\n\nexport function VideoSkin(props: VideoSkinProps): ReactNode {\n const { children, className, poster, ...rest } = props;\n\n return (\n <Container className={cn('media-default-skin media-default-skin--video', className)} {...rest}>\n {children}\n\n {poster && (\n <Poster src={isString(poster) ? poster : undefined} render={isRenderProp(poster) ? poster : undefined} />\n )}\n\n <BufferingIndicator\n render={(props) => (\n <div {...props} className=\"media-buffering-indicator\">\n <div className=\"media-surface\">\n <SpinnerIcon className=\"media-icon\" />\n </div>\n </div>\n )}\n />\n\n <ErrorDialog.Root>\n <ErrorDialog.Popup className=\"media-error\">\n <div className=\"media-error__dialog media-surface\">\n <div className=\"media-error__content\">\n <ErrorDialog.Title className=\"media-error__title\">Something went wrong.</ErrorDialog.Title>\n <ErrorDialog.Description className=\"media-error__description\" />\n </div>\n <div className=\"media-error__actions\">\n <ErrorDialog.Close className=\"media-button media-button--primary\">OK</ErrorDialog.Close>\n </div>\n </div>\n </ErrorDialog.Popup>\n </ErrorDialog.Root>\n\n <Controls.Root className=\"media-surface media-controls\">\n <Tooltip.Provider>\n <div className=\"media-button-group\">\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <PlayButton className=\"media-button--play\" render={<Button />}>\n <RestartIcon className=\"media-icon media-icon--restart\" />\n <PlayIcon className=\"media-icon media-icon--play\" />\n <PauseIcon className=\"media-icon media-icon--pause\" />\n </PlayButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\" />\n </Tooltip.Root>\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <SeekButton seconds={-SEEK_TIME} className=\"media-button--seek\" render={<Button />}>\n <span className=\"media-icon__container\">\n <SeekIcon className=\"media-icon media-icon--seek media-icon--flipped\" />\n <span className=\"media-icon__label\">{SEEK_TIME}</span>\n </span>\n </SeekButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\">Seek backward {SEEK_TIME} seconds</Tooltip.Popup>\n </Tooltip.Root>\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <SeekButton seconds={SEEK_TIME} className=\"media-button--seek\" render={<Button />}>\n <span className=\"media-icon__container\">\n <SeekIcon className=\"media-icon media-icon--seek\" />\n <span className=\"media-icon__label\">{SEEK_TIME}</span>\n </span>\n </SeekButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\">Seek forward {SEEK_TIME} seconds</Tooltip.Popup>\n </Tooltip.Root>\n </div>\n\n <div className=\"media-time-controls\">\n <Time.Value type=\"current\" className=\"media-time\" />\n <TimeSlider.Root className=\"media-slider\">\n <TimeSlider.Track className=\"media-slider__track\">\n <TimeSlider.Fill className=\"media-slider__fill\" />\n <TimeSlider.Buffer className=\"media-slider__buffer\" />\n </TimeSlider.Track>\n <TimeSlider.Thumb className=\"media-slider__thumb\" />\n\n <div className=\"media-surface media-preview media-slider__preview\">\n <Slider.Thumbnail className=\"media-preview__thumbnail\" />\n <TimeSlider.Value type=\"pointer\" className=\"media-time media-preview__time\" />\n <SpinnerIcon className=\"media-preview__spinner media-icon\" />\n </div>\n </TimeSlider.Root>\n <Time.Value type=\"duration\" className=\"media-time\" />\n </div>\n\n <div className=\"media-button-group\">\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={<PlaybackRateButton className=\"media-button--playback-rate\" render={<Button />} />}\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\">Toggle playback rate</Tooltip.Popup>\n </Tooltip.Root>\n\n <VolumePopover />\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <CaptionsButton className=\"media-button--captions\" render={<Button />}>\n <CaptionsOffIcon className=\"media-icon media-icon--captions-off\" />\n <CaptionsOnIcon className=\"media-icon media-icon--captions-on\" />\n </CaptionsButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\" />\n </Tooltip.Root>\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <PiPButton className=\"media-button--pip\" render={<Button />}>\n <PipEnterIcon className=\"media-icon media-icon--pip-enter\" />\n <PipExitIcon className=\"media-icon media-icon--pip-exit\" />\n </PiPButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\" />\n </Tooltip.Root>\n\n <Tooltip.Root side=\"top\">\n <Tooltip.Trigger\n render={\n <FullscreenButton className=\"media-button--fullscreen\" render={<Button />}>\n <FullscreenEnterIcon className=\"media-icon media-icon--fullscreen-enter\" />\n <FullscreenExitIcon className=\"media-icon media-icon--fullscreen-exit\" />\n </FullscreenButton>\n }\n />\n <Tooltip.Popup className=\"media-surface media-tooltip\" />\n </Tooltip.Root>\n </div>\n </Tooltip.Provider>\n </Controls.Root>\n\n <div className=\"media-overlay\" />\n\n {/* Hotkeys */}\n <MediaHotkey keys=\"Space\" action=\"togglePaused\" />\n <MediaHotkey keys=\"k\" action=\"togglePaused\" />\n <MediaHotkey keys=\"m\" action=\"toggleMuted\" />\n <MediaHotkey keys=\"f\" action=\"toggleFullscreen\" />\n <MediaHotkey keys=\"c\" action=\"toggleSubtitles\" />\n <MediaHotkey keys=\"i\" action=\"togglePictureInPicture\" />\n <MediaHotkey keys=\"ArrowRight\" action=\"seekStep\" value={5} />\n <MediaHotkey keys=\"ArrowLeft\" action=\"seekStep\" value={-5} />\n <MediaHotkey keys=\"l\" action=\"seekStep\" value={10} />\n <MediaHotkey keys=\"j\" action=\"seekStep\" value={-10} />\n <MediaHotkey keys=\"ArrowUp\" action=\"volumeStep\" value={0.05} />\n <MediaHotkey keys=\"ArrowDown\" action=\"volumeStep\" value={-0.05} />\n <MediaHotkey keys=\"0-9\" action=\"seekToPercent\" />\n <MediaHotkey keys=\"Home\" action=\"seekToPercent\" value={0} />\n <MediaHotkey keys=\"End\" action=\"seekToPercent\" value={100} />\n <MediaHotkey keys=\">\" action=\"speedUp\" />\n <MediaHotkey keys=\"<\" action=\"speedDown\" />\n\n {/* Gestures */}\n <MediaGesture type=\"tap\" action=\"togglePaused\" pointer=\"mouse\" region=\"center\" />\n <MediaGesture type=\"tap\" action=\"toggleControls\" pointer=\"touch\" />\n <MediaGesture type=\"doubletap\" action=\"seekStep\" value={-10} region=\"left\" />\n <MediaGesture type=\"doubletap\" action=\"toggleFullscreen\" region=\"center\" />\n <MediaGesture type=\"doubletap\" action=\"seekStep\" value={10} region=\"right\" />\n </Container>\n );\n}\n"],"mappings":"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"}
@@ -9,7 +9,8 @@ const CaptionsButton = createMediaButton({
9
9
  core: CaptionsButtonCore,
10
10
  stateAttrMap: CaptionsButtonDataAttrs,
11
11
  selector: selectTextTrack,
12
- action: (core, state) => core.toggle(state)
12
+ action: (core, state) => core.toggle(state),
13
+ hotkeyAction: "toggleSubtitles"
13
14
  });
14
15
  //#endregion
15
16
  export { CaptionsButton };
@@ -1 +1 @@
1
- {"version":3,"file":"captions-button.js","names":[],"sources":["../../../../src/ui/captions-button/captions-button.tsx"],"sourcesContent":["'use client';\n\nimport { CaptionsButtonCore, CaptionsButtonDataAttrs } from '@videojs/core';\nimport { selectTextTrack } from '@videojs/core/dom';\n\nimport type { UIComponentProps } from '../../utils/types';\nimport { createMediaButton } from '../create-media-button';\n\nexport interface CaptionsButtonProps\n extends UIComponentProps<'button', CaptionsButtonCore.State>,\n CaptionsButtonCore.Props {}\n\n/** A button that toggles captions. */\nexport const CaptionsButton = createMediaButton<CaptionsButtonCore, CaptionsButtonProps>({\n displayName: 'CaptionsButton',\n core: CaptionsButtonCore,\n stateAttrMap: CaptionsButtonDataAttrs,\n selector: selectTextTrack,\n action: (core, state) => core.toggle(state),\n});\n\nexport namespace CaptionsButton {\n export type Props = CaptionsButtonProps;\n export type State = CaptionsButtonCore.State;\n}\n"],"mappings":";;;;;;AAaA,MAAa,iBAAiB,kBAA2D;CACvF,aAAa;CACb,MAAM;CACN,cAAc;CACd,UAAU;CACV,SAAS,MAAM,UAAU,KAAK,OAAO,MAAM;CAC5C,CAAC"}
1
+ {"version":3,"file":"captions-button.js","names":[],"sources":["../../../../src/ui/captions-button/captions-button.tsx"],"sourcesContent":["'use client';\n\nimport { CaptionsButtonCore, CaptionsButtonDataAttrs } from '@videojs/core';\nimport { selectTextTrack } from '@videojs/core/dom';\n\nimport type { UIComponentProps } from '../../utils/types';\nimport { createMediaButton } from '../create-media-button';\n\nexport interface CaptionsButtonProps\n extends UIComponentProps<'button', CaptionsButtonCore.State>,\n CaptionsButtonCore.Props {}\n\n/** A button that toggles captions. */\nexport const CaptionsButton = createMediaButton<CaptionsButtonCore, CaptionsButtonProps>({\n displayName: 'CaptionsButton',\n core: CaptionsButtonCore,\n stateAttrMap: CaptionsButtonDataAttrs,\n selector: selectTextTrack,\n action: (core, state) => core.toggle(state),\n hotkeyAction: 'toggleSubtitles',\n});\n\nexport namespace CaptionsButton {\n export type Props = CaptionsButtonProps;\n export type State = CaptionsButtonCore.State;\n}\n"],"mappings":";;;;;;AAaA,MAAa,iBAAiB,kBAA2D;CACvF,aAAa;CACb,MAAM;CACN,cAAc;CACd,UAAU;CACV,SAAS,MAAM,UAAU,KAAK,OAAO,MAAM;CAC3C,cAAc;CACf,CAAC"}
@@ -2,13 +2,14 @@
2
2
  import { usePlayer } from "../player/context.js";
3
3
  import { renderElement } from "../utils/use-render.js";
4
4
  import { useButton } from "./hooks/use-button.js";
5
+ import { useAriaKeyShortcuts } from "./hotkey/use-aria-key-shortcuts.js";
5
6
  import { useOptionalTooltipContext } from "./tooltip/context.js";
6
7
  import "@videojs/core/dom";
7
8
  import { forwardRef, useLayoutEffect, useState } from "react";
8
9
  //#region src/ui/create-media-button.tsx
9
10
  /** Creates a media button React component from a core class and config. */
10
11
  function createMediaButton(config) {
11
- const { displayName, core: CoreClass, stateAttrMap, selector, action } = config;
12
+ const { displayName, core: CoreClass, stateAttrMap, selector, action, hotkeyAction } = config;
12
13
  const corePropKeys = new Set(Object.keys(CoreClass.defaultProps));
13
14
  const Component = forwardRef(function MediaButton(componentProps, forwardedRef) {
14
15
  const { render, className, style, ...rest } = componentProps;
@@ -18,6 +19,7 @@ function createMediaButton(config) {
18
19
  else elementProps[key] = rest[key];
19
20
  const tooltipCtx = useOptionalTooltipContext();
20
21
  const feature = usePlayer(selector);
22
+ const shortcuts = useAriaKeyShortcuts(hotkeyAction);
21
23
  const [core] = useState(() => new CoreClass());
22
24
  core.setProps(coreProps);
23
25
  const { getButtonProps, buttonRef } = useButton({
@@ -34,7 +36,10 @@ function createMediaButton(config) {
34
36
  return () => tooltipCtx.setContent(void 0);
35
37
  }, [tooltipCtx, label]);
36
38
  if (!feature || !state) return null;
37
- const attrs = core.getAttrs(state);
39
+ const attrs = {
40
+ ...core.getAttrs(state),
41
+ "aria-keyshortcuts": shortcuts
42
+ };
38
43
  return renderElement("button", {
39
44
  render,
40
45
  className,
@@ -1 +1 @@
1
- {"version":3,"file":"create-media-button.js","names":[],"sources":["../../../src/ui/create-media-button.tsx"],"sourcesContent":["'use client';\n\nimport type { InferComponentState, InferMediaState, MediaButtonComponent, StateAttrMap } from '@videojs/core';\nimport { logMissingFeature } from '@videojs/core/dom';\nimport type { Selector } from '@videojs/store';\nimport type { ForwardedRef, ForwardRefExoticComponent, RefAttributes } from 'react';\nimport { forwardRef, useLayoutEffect, useState } from 'react';\n\nimport { usePlayer } from '../player/context';\nimport type { renderElement as renderElementFn } from '../utils/use-render';\nimport { renderElement } from '../utils/use-render';\nimport { useButton } from './hooks/use-button';\nimport { useOptionalTooltipContext } from './tooltip/context';\n\ninterface MediaButtonConfig<Core extends Required<MediaButtonComponent>> {\n displayName: string;\n core: { new (): Core; defaultProps: Record<string, unknown> };\n stateAttrMap: StateAttrMap<InferComponentState<Core>>;\n selector: Selector<object, InferMediaState<Core> | undefined>;\n action: (core: Core, state: InferMediaState<Core>) => void;\n}\n\n/** Creates a media button React component from a core class and config. */\nexport function createMediaButton<Core extends Required<MediaButtonComponent>, Props extends object>(\n config: MediaButtonConfig<Core>\n): ForwardRefExoticComponent<Props & RefAttributes<HTMLButtonElement>> {\n const { displayName, core: CoreClass, stateAttrMap, selector, action } = config;\n\n // Props that exist in the core's defaultProps are routed to setProps; the rest go to the DOM element.\n const corePropKeys = new Set(Object.keys(CoreClass.defaultProps));\n\n const Component = forwardRef(function MediaButton(\n componentProps: Record<string, unknown>,\n forwardedRef: ForwardedRef<HTMLButtonElement>\n ) {\n const { render, className, style, ...rest } = componentProps;\n\n const coreProps: Record<string, unknown> = {};\n const elementProps: Record<string, unknown> = {};\n\n for (const key of Object.keys(rest)) {\n if (corePropKeys.has(key)) {\n coreProps[key] = rest[key];\n } else {\n elementProps[key] = rest[key];\n }\n }\n\n const tooltipCtx = useOptionalTooltipContext();\n const feature = usePlayer(selector);\n\n const [core] = useState(() => new CoreClass());\n core.setProps(coreProps);\n\n const { getButtonProps, buttonRef } = useButton({\n displayName,\n onActivate: () => action(core, feature!),\n isDisabled: () => !!coreProps.disabled || !feature,\n });\n\n // Derive state and label before the hooks boundary so the\n // useLayoutEffect below (called unconditionally) can reference them.\n type State = InferComponentState<Core>;\n if (feature) core.setMedia(feature);\n const state = feature ? (core.getState() as State) : null;\n const label = state ? core.getLabel(state) : undefined;\n\n // Forward label to tooltip popup content when inside a Tooltip.Root.\n useLayoutEffect(() => {\n if (!tooltipCtx) return;\n tooltipCtx.setContent(label);\n return () => tooltipCtx.setContent(undefined);\n }, [tooltipCtx, label]);\n\n if (!feature || !state) {\n if (__DEV__) logMissingFeature(displayName, selector.displayName ?? displayName);\n return null;\n }\n\n const attrs = core.getAttrs(state);\n\n return renderElement(\n 'button',\n { render, className, style } as renderElementFn.ComponentProps<InferComponentState<Core>>,\n {\n state,\n stateAttrMap,\n ref: [forwardedRef, buttonRef],\n props: [attrs, elementProps, getButtonProps()],\n }\n );\n });\n\n Component.displayName = displayName;\n\n return Component as unknown as ForwardRefExoticComponent<Props & RefAttributes<HTMLButtonElement>>;\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,kBACd,QACqE;CACrE,MAAM,EAAE,aAAa,MAAM,WAAW,cAAc,UAAU,WAAW;CAGzE,MAAM,eAAe,IAAI,IAAI,OAAO,KAAK,UAAU,aAAa,CAAC;CAEjE,MAAM,YAAY,WAAW,SAAS,YACpC,gBACA,cACA;EACA,MAAM,EAAE,QAAQ,WAAW,OAAO,GAAG,SAAS;EAE9C,MAAM,YAAqC,EAAE;EAC7C,MAAM,eAAwC,EAAE;AAEhD,OAAK,MAAM,OAAO,OAAO,KAAK,KAAK,CACjC,KAAI,aAAa,IAAI,IAAI,CACvB,WAAU,OAAO,KAAK;MAEtB,cAAa,OAAO,KAAK;EAI7B,MAAM,aAAa,2BAA2B;EAC9C,MAAM,UAAU,UAAU,SAAS;EAEnC,MAAM,CAAC,QAAQ,eAAe,IAAI,WAAW,CAAC;AAC9C,OAAK,SAAS,UAAU;EAExB,MAAM,EAAE,gBAAgB,cAAc,UAAU;GAC9C;GACA,kBAAkB,OAAO,MAAM,QAAS;GACxC,kBAAkB,CAAC,CAAC,UAAU,YAAY,CAAC;GAC5C,CAAC;AAKF,MAAI,QAAS,MAAK,SAAS,QAAQ;EACnC,MAAM,QAAQ,UAAW,KAAK,UAAU,GAAa;EACrD,MAAM,QAAQ,QAAQ,KAAK,SAAS,MAAM,GAAG,KAAA;AAG7C,wBAAsB;AACpB,OAAI,CAAC,WAAY;AACjB,cAAW,WAAW,MAAM;AAC5B,gBAAa,WAAW,WAAW,KAAA,EAAU;KAC5C,CAAC,YAAY,MAAM,CAAC;AAEvB,MAAI,CAAC,WAAW,CAAC,MAEf,QAAO;EAGT,MAAM,QAAQ,KAAK,SAAS,MAAM;AAElC,SAAO,cACL,UACA;GAAE;GAAQ;GAAW;GAAO,EAC5B;GACE;GACA;GACA,KAAK,CAAC,cAAc,UAAU;GAC9B,OAAO;IAAC;IAAO;IAAc,gBAAgB;IAAC;GAC/C,CACF;GACD;AAEF,WAAU,cAAc;AAExB,QAAO"}
1
+ {"version":3,"file":"create-media-button.js","names":[],"sources":["../../../src/ui/create-media-button.tsx"],"sourcesContent":["'use client';\n\nimport type { InferComponentState, InferMediaState, MediaButtonComponent, StateAttrMap } from '@videojs/core';\nimport { logMissingFeature } from '@videojs/core/dom';\nimport type { Selector } from '@videojs/store';\nimport type { ForwardedRef, ForwardRefExoticComponent, RefAttributes } from 'react';\nimport { forwardRef, useLayoutEffect, useState } from 'react';\n\nimport { usePlayer } from '../player/context';\nimport type { renderElement as renderElementFn } from '../utils/use-render';\nimport { renderElement } from '../utils/use-render';\nimport { useButton } from './hooks/use-button';\nimport { useAriaKeyShortcuts } from './hotkey/use-aria-key-shortcuts';\nimport { useOptionalTooltipContext } from './tooltip/context';\n\ninterface MediaButtonConfig<Core extends Required<MediaButtonComponent>> {\n displayName: string;\n core: { new (): Core; defaultProps: Record<string, unknown> };\n stateAttrMap: StateAttrMap<InferComponentState<Core>>;\n selector: Selector<object, InferMediaState<Core> | undefined>;\n action: (core: Core, state: InferMediaState<Core>) => void;\n hotkeyAction?: string;\n}\n\n/** Creates a media button React component from a core class and config. */\nexport function createMediaButton<Core extends Required<MediaButtonComponent>, Props extends object>(\n config: MediaButtonConfig<Core>\n): ForwardRefExoticComponent<Props & RefAttributes<HTMLButtonElement>> {\n const { displayName, core: CoreClass, stateAttrMap, selector, action, hotkeyAction } = config;\n\n // Props that exist in the core's defaultProps are routed to setProps; the rest go to the DOM element.\n const corePropKeys = new Set(Object.keys(CoreClass.defaultProps));\n\n const Component = forwardRef(function MediaButton(\n componentProps: Record<string, unknown>,\n forwardedRef: ForwardedRef<HTMLButtonElement>\n ) {\n const { render, className, style, ...rest } = componentProps;\n\n const coreProps: Record<string, unknown> = {};\n const elementProps: Record<string, unknown> = {};\n\n for (const key of Object.keys(rest)) {\n if (corePropKeys.has(key)) {\n coreProps[key] = rest[key];\n } else {\n elementProps[key] = rest[key];\n }\n }\n\n const tooltipCtx = useOptionalTooltipContext();\n const feature = usePlayer(selector);\n const shortcuts = useAriaKeyShortcuts(hotkeyAction);\n\n const [core] = useState(() => new CoreClass());\n core.setProps(coreProps);\n\n const { getButtonProps, buttonRef } = useButton({\n displayName,\n onActivate: () => action(core, feature!),\n isDisabled: () => !!coreProps.disabled || !feature,\n });\n\n // Derive state and label before the hooks boundary so the\n // useLayoutEffect below (called unconditionally) can reference them.\n type State = InferComponentState<Core>;\n if (feature) core.setMedia(feature);\n const state = feature ? (core.getState() as State) : null;\n const label = state ? core.getLabel(state) : undefined;\n\n // Forward label to tooltip popup content when inside a Tooltip.Root.\n useLayoutEffect(() => {\n if (!tooltipCtx) return;\n tooltipCtx.setContent(label);\n return () => tooltipCtx.setContent(undefined);\n }, [tooltipCtx, label]);\n\n if (!feature || !state) {\n if (__DEV__) logMissingFeature(displayName, selector.displayName ?? displayName);\n return null;\n }\n\n const attrs = { ...core.getAttrs(state), 'aria-keyshortcuts': shortcuts };\n\n return renderElement(\n 'button',\n { render, className, style } as renderElementFn.ComponentProps<InferComponentState<Core>>,\n {\n state,\n stateAttrMap,\n ref: [forwardedRef, buttonRef],\n props: [attrs, elementProps, getButtonProps()],\n }\n );\n });\n\n Component.displayName = displayName;\n\n return Component as unknown as ForwardRefExoticComponent<Props & RefAttributes<HTMLButtonElement>>;\n}\n"],"mappings":";;;;;;;;;;AAyBA,SAAgB,kBACd,QACqE;CACrE,MAAM,EAAE,aAAa,MAAM,WAAW,cAAc,UAAU,QAAQ,iBAAiB;CAGvF,MAAM,eAAe,IAAI,IAAI,OAAO,KAAK,UAAU,aAAa,CAAC;CAEjE,MAAM,YAAY,WAAW,SAAS,YACpC,gBACA,cACA;EACA,MAAM,EAAE,QAAQ,WAAW,OAAO,GAAG,SAAS;EAE9C,MAAM,YAAqC,EAAE;EAC7C,MAAM,eAAwC,EAAE;AAEhD,OAAK,MAAM,OAAO,OAAO,KAAK,KAAK,CACjC,KAAI,aAAa,IAAI,IAAI,CACvB,WAAU,OAAO,KAAK;MAEtB,cAAa,OAAO,KAAK;EAI7B,MAAM,aAAa,2BAA2B;EAC9C,MAAM,UAAU,UAAU,SAAS;EACnC,MAAM,YAAY,oBAAoB,aAAa;EAEnD,MAAM,CAAC,QAAQ,eAAe,IAAI,WAAW,CAAC;AAC9C,OAAK,SAAS,UAAU;EAExB,MAAM,EAAE,gBAAgB,cAAc,UAAU;GAC9C;GACA,kBAAkB,OAAO,MAAM,QAAS;GACxC,kBAAkB,CAAC,CAAC,UAAU,YAAY,CAAC;GAC5C,CAAC;AAKF,MAAI,QAAS,MAAK,SAAS,QAAQ;EACnC,MAAM,QAAQ,UAAW,KAAK,UAAU,GAAa;EACrD,MAAM,QAAQ,QAAQ,KAAK,SAAS,MAAM,GAAG,KAAA;AAG7C,wBAAsB;AACpB,OAAI,CAAC,WAAY;AACjB,cAAW,WAAW,MAAM;AAC5B,gBAAa,WAAW,WAAW,KAAA,EAAU;KAC5C,CAAC,YAAY,MAAM,CAAC;AAEvB,MAAI,CAAC,WAAW,CAAC,MAEf,QAAO;EAGT,MAAM,QAAQ;GAAE,GAAG,KAAK,SAAS,MAAM;GAAE,qBAAqB;GAAW;AAEzE,SAAO,cACL,UACA;GAAE;GAAQ;GAAW;GAAO,EAC5B;GACE;GACA;GACA,KAAK,CAAC,cAAc,UAAU;GAC9B,OAAO;IAAC;IAAO;IAAc,gBAAgB;IAAC;GAC/C,CACF;GACD;AAEF,WAAU,cAAc;AAExB,QAAO"}
@@ -9,7 +9,8 @@ const FullscreenButton = createMediaButton({
9
9
  core: FullscreenButtonCore,
10
10
  stateAttrMap: FullscreenButtonDataAttrs,
11
11
  selector: selectFullscreen,
12
- action: (core, state) => core.toggle(state)
12
+ action: (core, state) => core.toggle(state),
13
+ hotkeyAction: "toggleFullscreen"
13
14
  });
14
15
  //#endregion
15
16
  export { FullscreenButton };
@@ -1 +1 @@
1
- {"version":3,"file":"fullscreen-button.js","names":[],"sources":["../../../../src/ui/fullscreen-button/fullscreen-button.tsx"],"sourcesContent":["'use client';\n\nimport { FullscreenButtonCore, FullscreenButtonDataAttrs } from '@videojs/core';\nimport { selectFullscreen } from '@videojs/core/dom';\n\nimport type { UIComponentProps } from '../../utils/types';\nimport { createMediaButton } from '../create-media-button';\n\nexport interface FullscreenButtonProps\n extends UIComponentProps<'button', FullscreenButtonCore.State>,\n FullscreenButtonCore.Props {}\n\n/** A button that toggles fullscreen. */\nexport const FullscreenButton = createMediaButton<FullscreenButtonCore, FullscreenButtonProps>({\n displayName: 'FullscreenButton',\n core: FullscreenButtonCore,\n stateAttrMap: FullscreenButtonDataAttrs,\n selector: selectFullscreen,\n action: (core, state) => core.toggle(state),\n});\n\nexport namespace FullscreenButton {\n export type Props = FullscreenButtonProps;\n export type State = FullscreenButtonCore.State;\n}\n"],"mappings":";;;;;;AAaA,MAAa,mBAAmB,kBAA+D;CAC7F,aAAa;CACb,MAAM;CACN,cAAc;CACd,UAAU;CACV,SAAS,MAAM,UAAU,KAAK,OAAO,MAAM;CAC5C,CAAC"}
1
+ {"version":3,"file":"fullscreen-button.js","names":[],"sources":["../../../../src/ui/fullscreen-button/fullscreen-button.tsx"],"sourcesContent":["'use client';\n\nimport { FullscreenButtonCore, FullscreenButtonDataAttrs } from '@videojs/core';\nimport { selectFullscreen } from '@videojs/core/dom';\n\nimport type { UIComponentProps } from '../../utils/types';\nimport { createMediaButton } from '../create-media-button';\n\nexport interface FullscreenButtonProps\n extends UIComponentProps<'button', FullscreenButtonCore.State>,\n FullscreenButtonCore.Props {}\n\n/** A button that toggles fullscreen. */\nexport const FullscreenButton = createMediaButton<FullscreenButtonCore, FullscreenButtonProps>({\n displayName: 'FullscreenButton',\n core: FullscreenButtonCore,\n stateAttrMap: FullscreenButtonDataAttrs,\n selector: selectFullscreen,\n action: (core, state) => core.toggle(state),\n hotkeyAction: 'toggleFullscreen',\n});\n\nexport namespace FullscreenButton {\n export type Props = FullscreenButtonProps;\n export type State = FullscreenButtonCore.State;\n}\n"],"mappings":";;;;;;AAaA,MAAa,mBAAmB,kBAA+D;CAC7F,aAAa;CACb,MAAM;CACN,cAAc;CACd,UAAU;CACV,SAAS,MAAM,UAAU,KAAK,OAAO,MAAM;CAC3C,cAAc;CACf,CAAC"}
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { useContainer, usePlayer } from "../../player/context.js";
3
+ import { createDoubleTapGesture, createTapGesture, resolveGestureAction } from "@videojs/core/dom";
4
+ import { useEffect } from "react";
5
+ //#region src/ui/gesture/media-gesture.tsx
6
+ function MediaGesture({ type, action, value, pointer, region, disabled }) {
7
+ const store = usePlayer();
8
+ const container = useContainer();
9
+ useEffect(() => {
10
+ if (!container || !type || !action || disabled) return;
11
+ const resolver = resolveGestureAction(action);
12
+ if (!resolver) return;
13
+ const onActivate = (event) => {
14
+ resolver({
15
+ store,
16
+ value,
17
+ event
18
+ });
19
+ };
20
+ const options = {
21
+ pointer,
22
+ region,
23
+ action
24
+ };
25
+ if (type === "doubletap") return createDoubleTapGesture(container, onActivate, options);
26
+ return createTapGesture(container, onActivate, options);
27
+ }, [
28
+ container,
29
+ store,
30
+ type,
31
+ action,
32
+ value,
33
+ pointer,
34
+ region,
35
+ disabled
36
+ ]);
37
+ return null;
38
+ }
39
+ //#endregion
40
+ export { MediaGesture };
41
+
42
+ //# sourceMappingURL=media-gesture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-gesture.js","names":[],"sources":["../../../../src/ui/gesture/media-gesture.tsx"],"sourcesContent":["'use client';\n\nimport {\n type AnyPlayerStore,\n createDoubleTapGesture,\n createTapGesture,\n type GestureActionName,\n type GesturePointerType,\n type GestureRegion,\n resolveGestureAction,\n} from '@videojs/core/dom';\nimport type { ReactNode } from 'react';\nimport { useEffect } from 'react';\n\nimport { useContainer, usePlayer } from '../../player/context';\n\nexport interface MediaGestureProps {\n type: 'tap' | 'doubletap' | (string & {});\n action: GestureActionName | (string & {});\n value?: number;\n pointer?: GesturePointerType;\n region?: GestureRegion;\n disabled?: boolean;\n}\n\nexport function MediaGesture({ type, action, value, pointer, region, disabled }: MediaGestureProps): ReactNode {\n const store = usePlayer() as AnyPlayerStore;\n const container = useContainer();\n\n useEffect(() => {\n if (!container || !type || !action || disabled) return;\n\n const resolver = resolveGestureAction(action);\n if (!resolver) return;\n\n const onActivate = (event: PointerEvent) => {\n resolver({ store, value, event });\n };\n\n const options = { pointer, region, action };\n\n if (type === 'doubletap') {\n return createDoubleTapGesture(container, onActivate, options);\n }\n\n return createTapGesture(container, onActivate, options);\n }, [container, store, type, action, value, pointer, region, disabled]);\n\n return null;\n}\n"],"mappings":";;;;;AAyBA,SAAgB,aAAa,EAAE,MAAM,QAAQ,OAAO,SAAS,QAAQ,YAA0C;CAC7G,MAAM,QAAQ,WAAW;CACzB,MAAM,YAAY,cAAc;AAEhC,iBAAgB;AACd,MAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,UAAU,SAAU;EAEhD,MAAM,WAAW,qBAAqB,OAAO;AAC7C,MAAI,CAAC,SAAU;EAEf,MAAM,cAAc,UAAwB;AAC1C,YAAS;IAAE;IAAO;IAAO;IAAO,CAAC;;EAGnC,MAAM,UAAU;GAAE;GAAS;GAAQ;GAAQ;AAE3C,MAAI,SAAS,YACX,QAAO,uBAAuB,WAAW,YAAY,QAAQ;AAG/D,SAAO,iBAAiB,WAAW,YAAY,QAAQ;IACtD;EAAC;EAAW;EAAO;EAAM;EAAQ;EAAO;EAAS;EAAQ;EAAS,CAAC;AAEtE,QAAO"}
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { useContainer } from "../../player/context.js";
3
+ import { useLatestRef } from "../../utils/use-latest-ref.js";
4
+ import { createDoubleTapGesture } from "@videojs/core/dom";
5
+ import { useEffect } from "react";
6
+ //#region src/ui/gesture/use-doubletap-gesture.ts
7
+ function useDoubleTapGesture(onActivate, options) {
8
+ const { pointer, region, disabled = false, target } = options ?? {};
9
+ const contextContainer = useContainer();
10
+ const container = target?.current ?? contextContainer;
11
+ const onActivateRef = useLatestRef(onActivate);
12
+ useEffect(() => {
13
+ if (!container || disabled) return;
14
+ return createDoubleTapGesture(container, (event) => onActivateRef.current(event), {
15
+ pointer,
16
+ region
17
+ });
18
+ }, [
19
+ container,
20
+ disabled,
21
+ pointer,
22
+ region,
23
+ onActivateRef
24
+ ]);
25
+ }
26
+ //#endregion
27
+ export { useDoubleTapGesture };
28
+
29
+ //# sourceMappingURL=use-doubletap-gesture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-doubletap-gesture.js","names":[],"sources":["../../../../src/ui/gesture/use-doubletap-gesture.ts"],"sourcesContent":["'use client';\n\nimport { createDoubleTapGesture, type GesturePointerType, type GestureRegion } from '@videojs/core/dom';\nimport type { RefObject } from 'react';\nimport { useEffect } from 'react';\n\nimport { useContainer } from '../../player/context';\nimport { useLatestRef } from '../../utils/use-latest-ref';\n\nexport interface UseDoubleTapGestureOptions {\n pointer?: GesturePointerType;\n region?: GestureRegion;\n disabled?: boolean;\n target?: RefObject<HTMLElement | null>;\n}\n\nexport function useDoubleTapGesture(\n onActivate: (event: PointerEvent) => void,\n options?: UseDoubleTapGestureOptions\n): void {\n const { pointer, region, disabled = false, target } = options ?? {};\n const contextContainer = useContainer();\n const container = target?.current ?? contextContainer;\n const onActivateRef = useLatestRef(onActivate);\n\n useEffect(() => {\n if (!container || disabled) return;\n\n return createDoubleTapGesture(container, (event) => onActivateRef.current(event), { pointer, region });\n }, [container, disabled, pointer, region, onActivateRef]);\n}\n"],"mappings":";;;;;;AAgBA,SAAgB,oBACd,YACA,SACM;CACN,MAAM,EAAE,SAAS,QAAQ,WAAW,OAAO,WAAW,WAAW,EAAE;CACnE,MAAM,mBAAmB,cAAc;CACvC,MAAM,YAAY,QAAQ,WAAW;CACrC,MAAM,gBAAgB,aAAa,WAAW;AAE9C,iBAAgB;AACd,MAAI,CAAC,aAAa,SAAU;AAE5B,SAAO,uBAAuB,YAAY,UAAU,cAAc,QAAQ,MAAM,EAAE;GAAE;GAAS;GAAQ,CAAC;IACrG;EAAC;EAAW;EAAU;EAAS;EAAQ;EAAc,CAAC"}
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { useContainer } from "../../player/context.js";
3
+ import { useLatestRef } from "../../utils/use-latest-ref.js";
4
+ import { createTapGesture } from "@videojs/core/dom";
5
+ import { useEffect } from "react";
6
+ //#region src/ui/gesture/use-tap-gesture.ts
7
+ function useTapGesture(onActivate, options) {
8
+ const { pointer, region, disabled = false, target } = options ?? {};
9
+ const contextContainer = useContainer();
10
+ const container = target?.current ?? contextContainer;
11
+ const onActivateRef = useLatestRef(onActivate);
12
+ useEffect(() => {
13
+ if (!container || disabled) return;
14
+ return createTapGesture(container, (event) => onActivateRef.current(event), {
15
+ pointer,
16
+ region
17
+ });
18
+ }, [
19
+ container,
20
+ disabled,
21
+ pointer,
22
+ region,
23
+ onActivateRef
24
+ ]);
25
+ }
26
+ //#endregion
27
+ export { useTapGesture };
28
+
29
+ //# sourceMappingURL=use-tap-gesture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tap-gesture.js","names":[],"sources":["../../../../src/ui/gesture/use-tap-gesture.ts"],"sourcesContent":["'use client';\n\nimport { createTapGesture, type GesturePointerType, type GestureRegion } from '@videojs/core/dom';\nimport type { RefObject } from 'react';\nimport { useEffect } from 'react';\n\nimport { useContainer } from '../../player/context';\nimport { useLatestRef } from '../../utils/use-latest-ref';\n\nexport interface UseTapGestureOptions {\n pointer?: GesturePointerType;\n region?: GestureRegion;\n disabled?: boolean;\n target?: RefObject<HTMLElement | null>;\n}\n\nexport function useTapGesture(onActivate: (event: PointerEvent) => void, options?: UseTapGestureOptions): void {\n const { pointer, region, disabled = false, target } = options ?? {};\n const contextContainer = useContainer();\n const container = target?.current ?? contextContainer;\n const onActivateRef = useLatestRef(onActivate);\n\n useEffect(() => {\n if (!container || disabled) return;\n\n return createTapGesture(container, (event) => onActivateRef.current(event), { pointer, region });\n }, [container, disabled, pointer, region, onActivateRef]);\n}\n"],"mappings":";;;;;;AAgBA,SAAgB,cAAc,YAA2C,SAAsC;CAC7G,MAAM,EAAE,SAAS,QAAQ,WAAW,OAAO,WAAW,WAAW,EAAE;CACnE,MAAM,mBAAmB,cAAc;CACvC,MAAM,YAAY,QAAQ,WAAW;CACrC,MAAM,gBAAgB,aAAa,WAAW;AAE9C,iBAAgB;AACd,MAAI,CAAC,aAAa,SAAU;AAE5B,SAAO,iBAAiB,YAAY,UAAU,cAAc,QAAQ,MAAM,EAAE;GAAE;GAAS;GAAQ,CAAC;IAC/F;EAAC;EAAW;EAAU;EAAS;EAAQ;EAAc,CAAC"}
@@ -29,7 +29,7 @@ function useSlider(options) {
29
29
  getPercent: () => optionsRef.current.getPercent(),
30
30
  getStepPercent: () => optionsRef.current.getStepPercent(),
31
31
  getLargeStepPercent: () => optionsRef.current.getLargeStepPercent(),
32
- commitThrottle: optionsRef.current.commitThrottle,
32
+ changeThrottle: optionsRef.current.changeThrottle,
33
33
  adjustPercent: optionsRef.current.adjustPercent,
34
34
  onValueChange: (percent) => optionsRef.current.onValueChange?.(percent),
35
35
  onValueCommit: (percent) => optionsRef.current.onValueCommit?.(percent),
@@ -1 +1 @@
1
- {"version":3,"file":"use-slider.js","names":[],"sources":["../../../../src/ui/hooks/use-slider.ts"],"sourcesContent":["'use client';\n\nimport type { SliderInput, SliderState } from '@videojs/core';\nimport {\n createSlider,\n type SliderApi,\n type SliderOptions,\n type SliderRootProps,\n type SliderRootStyle,\n type SliderThumbProps,\n} from '@videojs/core/dom';\nimport { useSnapshot } from '@videojs/store/react';\nimport { isRTL } from '@videojs/utils/dom';\nimport { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport { useDestroy } from '../../utils/use-destroy';\nimport { useForceRender } from '../../utils/use-force-render';\nimport { useLatestRef } from '../../utils/use-latest-ref';\n\nexport interface UseSliderOptions<State extends SliderState = SliderState>\n extends Pick<\n SliderOptions,\n | 'getPercent'\n | 'getStepPercent'\n | 'getLargeStepPercent'\n | 'commitThrottle'\n | 'onValueChange'\n | 'onValueCommit'\n | 'onDragStart'\n | 'onDragEnd'\n > {\n computeState: (input: SliderInput) => State;\n orientation?: 'horizontal' | 'vertical' | undefined;\n disabled?: boolean | undefined;\n /** Adjust a raw 0–100 percent for thumb alignment. Called for fill and pointer percents. */\n adjustPercent?: ((rawPercent: number, thumbSize: number, trackSize: number) => number) | undefined;\n /** Compute CSS variable map from the (possibly alignment-adjusted) state. */\n getCSSVars: (state: State) => Record<string, string>;\n}\n\nexport interface UseSliderReturnValue<State extends SliderState = SliderState> {\n state: State;\n cssVars: Record<string, string>;\n rootRef: React.RefCallback<HTMLElement>;\n thumbRef: React.RefCallback<HTMLElement>;\n rootProps: SliderRootProps;\n rootStyle: SliderRootStyle;\n thumbProps: SliderThumbProps;\n}\n\n/**\n * Manages slider input lifecycle for React.\n *\n * Wraps `createSlider()` from `@videojs/core/dom` and subscribes to its\n * input state via `useSnapshot`. Returns split props for the root\n * (pointer events) and thumb (keyboard/focus) elements.\n */\nexport function useSlider<State extends SliderState = SliderState>(\n options: UseSliderOptions<State>\n): UseSliderReturnValue<State> {\n const optionsRef = useLatestRef(options);\n\n const rootElementRef = useRef<HTMLElement | null>(null);\n const thumbElementRef = useRef<HTMLElement | null>(null);\n const forceRender = useForceRender();\n\n // Lazy-init the slider handle. Stable across re-renders.\n const [slider] = useState<SliderApi>(() => {\n const stableOptions: SliderOptions = {\n getElement: () => rootElementRef.current!,\n getThumbElement: () => thumbElementRef.current,\n getOrientation: () => optionsRef.current.orientation ?? 'horizontal',\n isRTL: () => (rootElementRef.current ? isRTL(rootElementRef.current) : false),\n isDisabled: () => optionsRef.current.disabled ?? false,\n getPercent: () => optionsRef.current.getPercent(),\n getStepPercent: () => optionsRef.current.getStepPercent(),\n getLargeStepPercent: () => optionsRef.current.getLargeStepPercent(),\n commitThrottle: optionsRef.current.commitThrottle,\n adjustPercent: optionsRef.current.adjustPercent,\n onValueChange: (percent) => optionsRef.current.onValueChange?.(percent),\n onValueCommit: (percent) => optionsRef.current.onValueCommit?.(percent),\n onDragStart: () => optionsRef.current.onDragStart?.(),\n onDragEnd: () => optionsRef.current.onDragEnd?.(),\n };\n\n return createSlider(stableOptions);\n });\n\n useDestroy(slider);\n\n // Subscribe to slider input state.\n const input = useSnapshot(slider.input);\n\n // Compute derived state from input + caller-provided projection.\n const state = options.computeState(input);\n\n // Force a synchronous re-render after mount so edge thumb alignment\n // can read DOM measurements from the now-populated element refs.\n useLayoutEffect(() => {\n if (state.thumbAlignment === 'edge' && rootElementRef.current && thumbElementRef.current) {\n forceRender();\n }\n }, [state.thumbAlignment]);\n\n // Adjust CSS var percents for edge thumb alignment using live DOM measurements.\n const cssVars = options.getCSSVars(slider.adjustForAlignment(state));\n\n // Ref callbacks for root and thumb elements.\n const rootRef = useCallback((element: HTMLElement | null) => {\n rootElementRef.current = element;\n }, []);\n\n const thumbRef = useCallback((element: HTMLElement | null) => {\n thumbElementRef.current = element;\n }, []);\n\n return {\n state,\n cssVars,\n rootRef,\n thumbRef,\n rootProps: slider.rootProps,\n rootStyle: slider.rootStyle,\n thumbProps: slider.thumbProps,\n };\n}\n\nexport namespace useSlider {\n export type Options = UseSliderOptions;\n export type ReturnValue = UseSliderReturnValue;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwDA,SAAgB,UACd,SAC6B;CAC7B,MAAM,aAAa,aAAa,QAAQ;CAExC,MAAM,iBAAiB,OAA2B,KAAK;CACvD,MAAM,kBAAkB,OAA2B,KAAK;CACxD,MAAM,cAAc,gBAAgB;CAGpC,MAAM,CAAC,UAAU,eAA0B;AAkBzC,SAAO,aAjB8B;GACnC,kBAAkB,eAAe;GACjC,uBAAuB,gBAAgB;GACvC,sBAAsB,WAAW,QAAQ,eAAe;GACxD,aAAc,eAAe,UAAU,MAAM,eAAe,QAAQ,GAAG;GACvE,kBAAkB,WAAW,QAAQ,YAAY;GACjD,kBAAkB,WAAW,QAAQ,YAAY;GACjD,sBAAsB,WAAW,QAAQ,gBAAgB;GACzD,2BAA2B,WAAW,QAAQ,qBAAqB;GACnE,gBAAgB,WAAW,QAAQ;GACnC,eAAe,WAAW,QAAQ;GAClC,gBAAgB,YAAY,WAAW,QAAQ,gBAAgB,QAAQ;GACvE,gBAAgB,YAAY,WAAW,QAAQ,gBAAgB,QAAQ;GACvE,mBAAmB,WAAW,QAAQ,eAAe;GACrD,iBAAiB,WAAW,QAAQ,aAAa;GAClD,CAEiC;GAClC;AAEF,YAAW,OAAO;CAGlB,MAAM,QAAQ,YAAY,OAAO,MAAM;CAGvC,MAAM,QAAQ,QAAQ,aAAa,MAAM;AAIzC,uBAAsB;AACpB,MAAI,MAAM,mBAAmB,UAAU,eAAe,WAAW,gBAAgB,QAC/E,cAAa;IAEd,CAAC,MAAM,eAAe,CAAC;AAc1B,QAAO;EACL;EACA,SAbc,QAAQ,WAAW,OAAO,mBAAmB,MAAM,CAAC;EAclE,SAXc,aAAa,YAAgC;AAC3D,kBAAe,UAAU;KACxB,EAAE,CAAC;EAUJ,UARe,aAAa,YAAgC;AAC5D,mBAAgB,UAAU;KACzB,EAAE,CAAC;EAOJ,WAAW,OAAO;EAClB,WAAW,OAAO;EAClB,YAAY,OAAO;EACpB"}
1
+ {"version":3,"file":"use-slider.js","names":[],"sources":["../../../../src/ui/hooks/use-slider.ts"],"sourcesContent":["'use client';\n\nimport type { SliderInput, SliderState } from '@videojs/core';\nimport {\n createSlider,\n type SliderApi,\n type SliderOptions,\n type SliderRootProps,\n type SliderRootStyle,\n type SliderThumbProps,\n} from '@videojs/core/dom';\nimport { useSnapshot } from '@videojs/store/react';\nimport { isRTL } from '@videojs/utils/dom';\nimport { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport { useDestroy } from '../../utils/use-destroy';\nimport { useForceRender } from '../../utils/use-force-render';\nimport { useLatestRef } from '../../utils/use-latest-ref';\n\nexport interface UseSliderOptions<State extends SliderState = SliderState>\n extends Pick<\n SliderOptions,\n | 'getPercent'\n | 'getStepPercent'\n | 'getLargeStepPercent'\n | 'changeThrottle'\n | 'onValueChange'\n | 'onValueCommit'\n | 'onDragStart'\n | 'onDragEnd'\n > {\n computeState: (input: SliderInput) => State;\n orientation?: 'horizontal' | 'vertical' | undefined;\n disabled?: boolean | undefined;\n /** Adjust a raw 0–100 percent for thumb alignment. Called for fill and pointer percents. */\n adjustPercent?: ((rawPercent: number, thumbSize: number, trackSize: number) => number) | undefined;\n /** Compute CSS variable map from the (possibly alignment-adjusted) state. */\n getCSSVars: (state: State) => Record<string, string>;\n}\n\nexport interface UseSliderReturnValue<State extends SliderState = SliderState> {\n state: State;\n cssVars: Record<string, string>;\n rootRef: React.RefCallback<HTMLElement>;\n thumbRef: React.RefCallback<HTMLElement>;\n rootProps: SliderRootProps;\n rootStyle: SliderRootStyle;\n thumbProps: SliderThumbProps;\n}\n\n/**\n * Manages slider input lifecycle for React.\n *\n * Wraps `createSlider()` from `@videojs/core/dom` and subscribes to its\n * input state via `useSnapshot`. Returns split props for the root\n * (pointer events) and thumb (keyboard/focus) elements.\n */\nexport function useSlider<State extends SliderState = SliderState>(\n options: UseSliderOptions<State>\n): UseSliderReturnValue<State> {\n const optionsRef = useLatestRef(options);\n\n const rootElementRef = useRef<HTMLElement | null>(null);\n const thumbElementRef = useRef<HTMLElement | null>(null);\n const forceRender = useForceRender();\n\n // Lazy-init the slider handle. Stable across re-renders.\n const [slider] = useState<SliderApi>(() => {\n const stableOptions: SliderOptions = {\n getElement: () => rootElementRef.current!,\n getThumbElement: () => thumbElementRef.current,\n getOrientation: () => optionsRef.current.orientation ?? 'horizontal',\n isRTL: () => (rootElementRef.current ? isRTL(rootElementRef.current) : false),\n isDisabled: () => optionsRef.current.disabled ?? false,\n getPercent: () => optionsRef.current.getPercent(),\n getStepPercent: () => optionsRef.current.getStepPercent(),\n getLargeStepPercent: () => optionsRef.current.getLargeStepPercent(),\n changeThrottle: optionsRef.current.changeThrottle,\n adjustPercent: optionsRef.current.adjustPercent,\n onValueChange: (percent) => optionsRef.current.onValueChange?.(percent),\n onValueCommit: (percent) => optionsRef.current.onValueCommit?.(percent),\n onDragStart: () => optionsRef.current.onDragStart?.(),\n onDragEnd: () => optionsRef.current.onDragEnd?.(),\n };\n\n return createSlider(stableOptions);\n });\n\n useDestroy(slider);\n\n // Subscribe to slider input state.\n const input = useSnapshot(slider.input);\n\n // Compute derived state from input + caller-provided projection.\n const state = options.computeState(input);\n\n // Force a synchronous re-render after mount so edge thumb alignment\n // can read DOM measurements from the now-populated element refs.\n useLayoutEffect(() => {\n if (state.thumbAlignment === 'edge' && rootElementRef.current && thumbElementRef.current) {\n forceRender();\n }\n }, [state.thumbAlignment]);\n\n // Adjust CSS var percents for edge thumb alignment using live DOM measurements.\n const cssVars = options.getCSSVars(slider.adjustForAlignment(state));\n\n // Ref callbacks for root and thumb elements.\n const rootRef = useCallback((element: HTMLElement | null) => {\n rootElementRef.current = element;\n }, []);\n\n const thumbRef = useCallback((element: HTMLElement | null) => {\n thumbElementRef.current = element;\n }, []);\n\n return {\n state,\n cssVars,\n rootRef,\n thumbRef,\n rootProps: slider.rootProps,\n rootStyle: slider.rootStyle,\n thumbProps: slider.thumbProps,\n };\n}\n\nexport namespace useSlider {\n export type Options = UseSliderOptions;\n export type ReturnValue = UseSliderReturnValue;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwDA,SAAgB,UACd,SAC6B;CAC7B,MAAM,aAAa,aAAa,QAAQ;CAExC,MAAM,iBAAiB,OAA2B,KAAK;CACvD,MAAM,kBAAkB,OAA2B,KAAK;CACxD,MAAM,cAAc,gBAAgB;CAGpC,MAAM,CAAC,UAAU,eAA0B;AAkBzC,SAAO,aAjB8B;GACnC,kBAAkB,eAAe;GACjC,uBAAuB,gBAAgB;GACvC,sBAAsB,WAAW,QAAQ,eAAe;GACxD,aAAc,eAAe,UAAU,MAAM,eAAe,QAAQ,GAAG;GACvE,kBAAkB,WAAW,QAAQ,YAAY;GACjD,kBAAkB,WAAW,QAAQ,YAAY;GACjD,sBAAsB,WAAW,QAAQ,gBAAgB;GACzD,2BAA2B,WAAW,QAAQ,qBAAqB;GACnE,gBAAgB,WAAW,QAAQ;GACnC,eAAe,WAAW,QAAQ;GAClC,gBAAgB,YAAY,WAAW,QAAQ,gBAAgB,QAAQ;GACvE,gBAAgB,YAAY,WAAW,QAAQ,gBAAgB,QAAQ;GACvE,mBAAmB,WAAW,QAAQ,eAAe;GACrD,iBAAiB,WAAW,QAAQ,aAAa;GAClD,CAEiC;GAClC;AAEF,YAAW,OAAO;CAGlB,MAAM,QAAQ,YAAY,OAAO,MAAM;CAGvC,MAAM,QAAQ,QAAQ,aAAa,MAAM;AAIzC,uBAAsB;AACpB,MAAI,MAAM,mBAAmB,UAAU,eAAe,WAAW,gBAAgB,QAC/E,cAAa;IAEd,CAAC,MAAM,eAAe,CAAC;AAc1B,QAAO;EACL;EACA,SAbc,QAAQ,WAAW,OAAO,mBAAmB,MAAM,CAAC;EAclE,SAXc,aAAa,YAAgC;AAC3D,kBAAe,UAAU;KACxB,EAAE,CAAC;EAUJ,UARe,aAAa,YAAgC;AAC5D,mBAAgB,UAAU;KACzB,EAAE,CAAC;EAOJ,WAAW,OAAO;EAClB,WAAW,OAAO;EAClB,YAAY,OAAO;EACpB"}
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { useContainer, usePlayer } from "../../player/context.js";
3
+ import { createHotkey, isHotkeyToggleAction, resolveHotkeyAction } from "@videojs/core/dom";
4
+ import { useEffect } from "react";
5
+ //#region src/ui/hotkey/media-hotkey.tsx
6
+ function MediaHotkey({ keys, action, value, disabled, target }) {
7
+ const store = usePlayer();
8
+ const container = useContainer();
9
+ useEffect(() => {
10
+ if (!container || !keys || !action || disabled) return;
11
+ const resolver = resolveHotkeyAction(action);
12
+ if (!resolver) return;
13
+ return createHotkey(container, {
14
+ keys,
15
+ action,
16
+ target,
17
+ disabled,
18
+ repeatable: !isHotkeyToggleAction(action),
19
+ onActivate: (_event, key) => {
20
+ resolver({
21
+ store,
22
+ key,
23
+ value
24
+ });
25
+ }
26
+ });
27
+ }, [
28
+ container,
29
+ store,
30
+ keys,
31
+ action,
32
+ value,
33
+ disabled,
34
+ target
35
+ ]);
36
+ return null;
37
+ }
38
+ //#endregion
39
+ export { MediaHotkey };
40
+
41
+ //# sourceMappingURL=media-hotkey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-hotkey.js","names":[],"sources":["../../../../src/ui/hotkey/media-hotkey.tsx"],"sourcesContent":["'use client';\n\nimport {\n type AnyPlayerStore,\n createHotkey,\n type HotkeyActionName,\n isHotkeyToggleAction,\n resolveHotkeyAction,\n} from '@videojs/core/dom';\nimport type { ReactNode } from 'react';\nimport { useEffect } from 'react';\n\nimport { useContainer, usePlayer } from '../../player/context';\n\nexport interface MediaHotkeyProps {\n keys: string;\n action: HotkeyActionName | (string & {});\n value?: number;\n disabled?: boolean;\n target?: 'player' | 'document';\n}\n\nexport function MediaHotkey({ keys, action, value, disabled, target }: MediaHotkeyProps): ReactNode {\n const store = usePlayer() as AnyPlayerStore;\n const container = useContainer();\n\n useEffect(() => {\n if (!container || !keys || !action || disabled) return;\n\n const resolver = resolveHotkeyAction(action);\n if (!resolver) return;\n\n return createHotkey(container, {\n keys,\n action,\n target,\n disabled,\n repeatable: !isHotkeyToggleAction(action),\n onActivate: (_event, key) => {\n resolver({ store, key, value });\n },\n });\n }, [container, store, keys, action, value, disabled, target]);\n\n return null;\n}\n"],"mappings":";;;;;AAsBA,SAAgB,YAAY,EAAE,MAAM,QAAQ,OAAO,UAAU,UAAuC;CAClG,MAAM,QAAQ,WAAW;CACzB,MAAM,YAAY,cAAc;AAEhC,iBAAgB;AACd,MAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,UAAU,SAAU;EAEhD,MAAM,WAAW,oBAAoB,OAAO;AAC5C,MAAI,CAAC,SAAU;AAEf,SAAO,aAAa,WAAW;GAC7B;GACA;GACA;GACA;GACA,YAAY,CAAC,qBAAqB,OAAO;GACzC,aAAa,QAAQ,QAAQ;AAC3B,aAAS;KAAE;KAAO;KAAK;KAAO,CAAC;;GAElC,CAAC;IACD;EAAC;EAAW;EAAO;EAAM;EAAQ;EAAO;EAAU;EAAO,CAAC;AAE7D,QAAO"}
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { useContainer } from "../../player/context.js";
3
+ import { findHotkeyCoordinator } from "@videojs/core/dom";
4
+ import { useMemo } from "react";
5
+ //#region src/ui/hotkey/use-aria-key-shortcuts.ts
6
+ function useAriaKeyShortcuts(action) {
7
+ const container = useContainer();
8
+ return useMemo(() => {
9
+ if (!container || !action) return void 0;
10
+ return findHotkeyCoordinator(container)?.getAriaKeys(action);
11
+ }, [container, action]);
12
+ }
13
+ //#endregion
14
+ export { useAriaKeyShortcuts };
15
+
16
+ //# sourceMappingURL=use-aria-key-shortcuts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-aria-key-shortcuts.js","names":[],"sources":["../../../../src/ui/hotkey/use-aria-key-shortcuts.ts"],"sourcesContent":["'use client';\n\nimport { findHotkeyCoordinator } from '@videojs/core/dom';\nimport { useMemo } from 'react';\n\nimport { useContainer } from '../../player/context';\n\nexport function useAriaKeyShortcuts(action: string | undefined): string | undefined {\n const container = useContainer();\n return useMemo(() => {\n if (!container || !action) return undefined;\n return findHotkeyCoordinator(container)?.getAriaKeys(action);\n }, [container, action]);\n}\n"],"mappings":";;;;;AAOA,SAAgB,oBAAoB,QAAgD;CAClF,MAAM,YAAY,cAAc;AAChC,QAAO,cAAc;AACnB,MAAI,CAAC,aAAa,CAAC,OAAQ,QAAO,KAAA;AAClC,SAAO,sBAAsB,UAAU,EAAE,YAAY,OAAO;IAC3D,CAAC,WAAW,OAAO,CAAC"}
@@ -0,0 +1,32 @@
1
+ "use client";
2
+ import { useContainer } from "../../player/context.js";
3
+ import { useLatestRef } from "../../utils/use-latest-ref.js";
4
+ import { createHotkey } from "@videojs/core/dom";
5
+ import { useEffect } from "react";
6
+ //#region src/ui/hotkey/use-hotkey.ts
7
+ function useHotkey(options) {
8
+ const { keys, target = "player", repeatable = true, disabled = false } = options;
9
+ const container = useContainer();
10
+ const onActivateRef = useLatestRef(options.onActivate);
11
+ useEffect(() => {
12
+ if (!container || !keys || disabled) return;
13
+ return createHotkey(container, {
14
+ keys,
15
+ target,
16
+ repeatable,
17
+ disabled,
18
+ onActivate: (event, key) => onActivateRef.current(event, key)
19
+ });
20
+ }, [
21
+ container,
22
+ keys,
23
+ target,
24
+ repeatable,
25
+ disabled,
26
+ onActivateRef
27
+ ]);
28
+ }
29
+ //#endregion
30
+ export { useHotkey };
31
+
32
+ //# sourceMappingURL=use-hotkey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-hotkey.js","names":[],"sources":["../../../../src/ui/hotkey/use-hotkey.ts"],"sourcesContent":["'use client';\n\nimport { createHotkey } from '@videojs/core/dom';\nimport { useEffect } from 'react';\n\nimport { useContainer } from '../../player/context';\nimport { useLatestRef } from '../../utils/use-latest-ref';\n\nexport interface UseHotkeyOptions {\n keys: string;\n onActivate: (event: KeyboardEvent, key: string) => void;\n target?: 'player' | 'document';\n repeatable?: boolean;\n disabled?: boolean;\n}\n\nexport function useHotkey(options: UseHotkeyOptions): void {\n const { keys, target = 'player', repeatable = true, disabled = false } = options;\n const container = useContainer();\n const onActivateRef = useLatestRef(options.onActivate);\n\n useEffect(() => {\n if (!container || !keys || disabled) return;\n\n return createHotkey(container, {\n keys,\n target,\n repeatable,\n disabled,\n onActivate: (event, key) => onActivateRef.current(event, key),\n });\n }, [container, keys, target, repeatable, disabled, onActivateRef]);\n}\n"],"mappings":";;;;;;AAgBA,SAAgB,UAAU,SAAiC;CACzD,MAAM,EAAE,MAAM,SAAS,UAAU,aAAa,MAAM,WAAW,UAAU;CACzE,MAAM,YAAY,cAAc;CAChC,MAAM,gBAAgB,aAAa,QAAQ,WAAW;AAEtD,iBAAgB;AACd,MAAI,CAAC,aAAa,CAAC,QAAQ,SAAU;AAErC,SAAO,aAAa,WAAW;GAC7B;GACA;GACA;GACA;GACA,aAAa,OAAO,QAAQ,cAAc,QAAQ,OAAO,IAAI;GAC9D,CAAC;IACD;EAAC;EAAW;EAAM;EAAQ;EAAY;EAAU;EAAc,CAAC"}
@@ -9,7 +9,8 @@ const MuteButton = createMediaButton({
9
9
  core: MuteButtonCore,
10
10
  stateAttrMap: MuteButtonDataAttrs,
11
11
  selector: selectVolume,
12
- action: (core, state) => core.toggle(state)
12
+ action: (core, state) => core.toggle(state),
13
+ hotkeyAction: "toggleMuted"
13
14
  });
14
15
  //#endregion
15
16
  export { MuteButton };
@@ -1 +1 @@
1
- {"version":3,"file":"mute-button.js","names":[],"sources":["../../../../src/ui/mute-button/mute-button.tsx"],"sourcesContent":["'use client';\n\nimport { MuteButtonCore, MuteButtonDataAttrs } from '@videojs/core';\nimport { selectVolume } from '@videojs/core/dom';\n\nimport type { UIComponentProps } from '../../utils/types';\nimport { createMediaButton } from '../create-media-button';\n\nexport interface MuteButtonProps extends UIComponentProps<'button', MuteButtonCore.State>, MuteButtonCore.Props {}\n\n/** A button that toggles mute state. */\nexport const MuteButton = createMediaButton<MuteButtonCore, MuteButtonProps>({\n displayName: 'MuteButton',\n core: MuteButtonCore,\n stateAttrMap: MuteButtonDataAttrs,\n selector: selectVolume,\n action: (core, state) => core.toggle(state),\n});\n\nexport namespace MuteButton {\n export type Props = MuteButtonProps;\n export type State = MuteButtonCore.State;\n}\n"],"mappings":";;;;;;AAWA,MAAa,aAAa,kBAAmD;CAC3E,aAAa;CACb,MAAM;CACN,cAAc;CACd,UAAU;CACV,SAAS,MAAM,UAAU,KAAK,OAAO,MAAM;CAC5C,CAAC"}
1
+ {"version":3,"file":"mute-button.js","names":[],"sources":["../../../../src/ui/mute-button/mute-button.tsx"],"sourcesContent":["'use client';\n\nimport { MuteButtonCore, MuteButtonDataAttrs } from '@videojs/core';\nimport { selectVolume } from '@videojs/core/dom';\n\nimport type { UIComponentProps } from '../../utils/types';\nimport { createMediaButton } from '../create-media-button';\n\nexport interface MuteButtonProps extends UIComponentProps<'button', MuteButtonCore.State>, MuteButtonCore.Props {}\n\n/** A button that toggles mute state. */\nexport const MuteButton = createMediaButton<MuteButtonCore, MuteButtonProps>({\n displayName: 'MuteButton',\n core: MuteButtonCore,\n stateAttrMap: MuteButtonDataAttrs,\n selector: selectVolume,\n action: (core, state) => core.toggle(state),\n hotkeyAction: 'toggleMuted',\n});\n\nexport namespace MuteButton {\n export type Props = MuteButtonProps;\n export type State = MuteButtonCore.State;\n}\n"],"mappings":";;;;;;AAWA,MAAa,aAAa,kBAAmD;CAC3E,aAAa;CACb,MAAM;CACN,cAAc;CACd,UAAU;CACV,SAAS,MAAM,UAAU,KAAK,OAAO,MAAM;CAC3C,cAAc;CACf,CAAC"}
@@ -9,7 +9,8 @@ const PiPButton = createMediaButton({
9
9
  core: PiPButtonCore,
10
10
  stateAttrMap: PiPButtonDataAttrs,
11
11
  selector: selectPiP,
12
- action: (core, state) => core.toggle(state)
12
+ action: (core, state) => core.toggle(state),
13
+ hotkeyAction: "togglePictureInPicture"
13
14
  });
14
15
  //#endregion
15
16
  export { PiPButton };