@skodaflow/web-library 8.4.0 → 8.5.0
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/cjs/components/carousel/Autoplay.js.map +1 -1
- package/cjs/components/carousel/Carousel.js +1 -1
- package/cjs/components/carousel/Carousel.js.map +1 -1
- package/cjs/components/carousel/Navigation.js +1 -1
- package/cjs/components/carousel/Navigation.js.map +1 -1
- package/cjs/components/carousel/Pagination.js +1 -1
- package/cjs/components/carousel/Pagination.js.map +1 -1
- package/cjs/components/header/Header.js +1 -1
- package/cjs/components/header/Header.js.map +1 -1
- package/cjs/components/header/HeaderNavigationHero.js +1 -1
- package/cjs/components/header/HeaderNavigationHero.js.map +1 -1
- package/cjs/components/header/HeaderNavigationMenu.js +1 -1
- package/cjs/components/header/HeaderNavigationMenu.js.map +1 -1
- package/cjs/components/hotspotCard/HotspotCard.js +1 -1
- package/cjs/components/hotspotCard/HotspotCard.js.map +1 -1
- package/cjs/components/icon/stroke/EmissionLabelIcon.js.map +1 -1
- package/cjs/components/image/Image.d.ts +38 -14
- package/cjs/components/image/Image.js +1 -1
- package/cjs/components/image/Image.js.map +1 -1
- package/cjs/components/index.d.ts +1 -0
- package/cjs/components/label/Label.js.map +1 -1
- package/cjs/components/overlay/Overlay.js +1 -1
- package/cjs/components/overlay/Overlay.js.map +1 -1
- package/cjs/components/video/Video.d.ts +9 -8
- package/cjs/components/video/Video.js +1 -1
- package/cjs/components/video/Video.js.map +1 -1
- package/cjs/components/video/index.d.ts +5 -1
- package/cjs/components/video/useVideoFullscreen.d.ts +13 -0
- package/cjs/components/video/useVideoFullscreen.js +2 -0
- package/cjs/components/video/useVideoFullscreen.js.map +1 -0
- package/cjs/components/video/useVideoPlayPause.d.ts +13 -0
- package/cjs/components/video/useVideoPlayPause.js +2 -0
- package/cjs/components/video/useVideoPlayPause.js.map +1 -0
- package/cjs/components/video/useVideoReady.d.ts +6 -0
- package/cjs/components/video/useVideoReady.js +2 -0
- package/cjs/components/video/useVideoReady.js.map +1 -0
- package/cjs/components/video/useVideoSoundControls.d.ts +17 -0
- package/cjs/components/video/useVideoSoundControls.js +2 -0
- package/cjs/components/video/useVideoSoundControls.js.map +1 -0
- package/cjs/components/video/useVideoTime.d.ts +22 -0
- package/cjs/components/video/useVideoTime.js +2 -0
- package/cjs/components/video/useVideoTime.js.map +1 -0
- package/cjs/components/videoPlayer/VideoPlayer.d.ts +125 -0
- package/cjs/components/videoPlayer/VideoPlayer.js +2 -0
- package/cjs/components/videoPlayer/VideoPlayer.js.map +1 -0
- package/cjs/components/videoPlayer/VideoPlayerFullscreenButton.d.ts +31 -0
- package/cjs/components/videoPlayer/VideoPlayerFullscreenButton.js +2 -0
- package/cjs/components/videoPlayer/VideoPlayerFullscreenButton.js.map +1 -0
- package/cjs/components/videoPlayer/VideoPlayerIconButton.d.ts +16 -0
- package/cjs/components/videoPlayer/VideoPlayerIconButton.js +2 -0
- package/cjs/components/videoPlayer/VideoPlayerIconButton.js.map +1 -0
- package/cjs/components/videoPlayer/VideoPlayerPlayPauseButton.d.ts +31 -0
- package/cjs/components/videoPlayer/VideoPlayerPlayPauseButton.js +2 -0
- package/cjs/components/videoPlayer/VideoPlayerPlayPauseButton.js.map +1 -0
- package/cjs/components/videoPlayer/VideoPlayerSkipButton.d.ts +23 -0
- package/cjs/components/videoPlayer/VideoPlayerSkipButton.js +2 -0
- package/cjs/components/videoPlayer/VideoPlayerSkipButton.js.map +1 -0
- package/cjs/components/videoPlayer/VideoPlayerSlider.d.ts +12 -0
- package/cjs/components/videoPlayer/VideoPlayerSlider.js +2 -0
- package/cjs/components/videoPlayer/VideoPlayerSlider.js.map +1 -0
- package/cjs/components/videoPlayer/VideoPlayerSoundButton.d.ts +31 -0
- package/cjs/components/videoPlayer/VideoPlayerSoundButton.js +2 -0
- package/cjs/components/videoPlayer/VideoPlayerSoundButton.js.map +1 -0
- package/cjs/components/videoPlayer/VideoPlayerTime.d.ts +17 -0
- package/cjs/components/videoPlayer/VideoPlayerTime.js +2 -0
- package/cjs/components/videoPlayer/VideoPlayerTime.js.map +1 -0
- package/cjs/components/videoPlayer/formatVideoTime.d.ts +5 -0
- package/cjs/components/videoPlayer/formatVideoTime.js +2 -0
- package/cjs/components/videoPlayer/formatVideoTime.js.map +1 -0
- package/cjs/components/videoPlayer/index.d.ts +10 -0
- package/cjs/components/videoPlayer/useVideoPlayerVisibility.d.ts +24 -0
- package/cjs/components/videoPlayer/useVideoPlayerVisibility.js +2 -0
- package/cjs/components/videoPlayer/useVideoPlayerVisibility.js.map +1 -0
- package/cjs/index.js +1 -1
- package/cjs/materialui/accordion/AccordionSummaryExpandWrapper.d.ts +5 -1
- package/cjs/materialui/accordion/AccordionSummaryExpandWrapper.js +1 -1
- package/cjs/materialui/accordion/AccordionSummaryExpandWrapper.js.map +1 -1
- package/cjs/materialui/alert/alertTitle.theme.js.map +1 -1
- package/cjs/materialui/card/Card.js.map +1 -1
- package/cjs/materialui/card/card.theme.js +1 -1
- package/cjs/materialui/card/card.theme.js.map +1 -1
- package/cjs/materialui/divider/divider.theme.js +1 -1
- package/cjs/materialui/divider/divider.theme.js.map +1 -1
- package/cjs/materialui/skeleton/skeleton.theme.js +1 -1
- package/cjs/materialui/skeleton/skeleton.theme.js.map +1 -1
- package/cjs/materialui/slider/slider.theme.js +1 -1
- package/cjs/materialui/slider/slider.theme.js.map +1 -1
- package/cjs/materialui/table/tablePagination.theme.js.map +1 -1
- package/cjs/materialui/tabs/Tab.d.ts +1 -0
- package/cjs/materialui/tabs/Tab.js +1 -1
- package/cjs/materialui/tabs/Tab.js.map +1 -1
- package/cjs/materialui/tabs/TabsScrollButton.d.ts +3 -1
- package/cjs/materialui/tabs/TabsScrollButton.js +1 -1
- package/cjs/materialui/tabs/TabsScrollButton.js.map +1 -1
- package/cjs/materialui/textfield/FormControl.js +1 -1
- package/cjs/materialui/textfield/FormControl.js.map +1 -1
- package/cjs/materialui/textfield/FormHelperText.js +1 -1
- package/cjs/materialui/textfield/FormHelperText.js.map +1 -1
- package/cjs/materialui/theme/sxConfig.js.map +1 -1
- package/cjs/materialui/theme/theme.extensions.d.ts +29 -0
- package/cjs/node_modules/swiper/modules/a11y.js +2 -0
- package/cjs/node_modules/swiper/modules/a11y.js.map +1 -0
- package/cjs/node_modules/swiper/modules/autoplay.js +1 -1
- package/cjs/node_modules/swiper/modules/autoplay.js.map +1 -1
- package/cjs/node_modules/swiper/modules/effect-fade.js +1 -1
- package/cjs/node_modules/swiper/modules/effect-fade.js.map +1 -1
- package/cjs/node_modules/swiper/modules/keyboard.js +1 -1
- package/cjs/node_modules/swiper/modules/keyboard.js.map +1 -1
- package/cjs/node_modules/swiper/modules/parallax.js +1 -1
- package/cjs/node_modules/swiper/modules/parallax.js.map +1 -1
- package/cjs/node_modules/swiper/shared/classes-to-selector.js +2 -0
- package/cjs/node_modules/swiper/shared/classes-to-selector.js.map +1 -0
- package/cjs/node_modules/swiper/shared/effect-init.js +1 -1
- package/cjs/node_modules/swiper/shared/effect-init.js.map +1 -1
- package/cjs/node_modules/swiper/shared/effect-target.js +1 -1
- package/cjs/node_modules/swiper/shared/effect-target.js.map +1 -1
- package/cjs/node_modules/swiper/shared/effect-virtual-transition-end.js +1 -1
- package/cjs/node_modules/swiper/shared/effect-virtual-transition-end.js.map +1 -1
- package/cjs/node_modules/swiper/shared/swiper-core.js +1 -1
- package/cjs/node_modules/swiper/shared/swiper-core.js.map +1 -1
- package/cjs/node_modules/swiper/shared/update-on-virtual-data.js +1 -1
- package/cjs/node_modules/swiper/shared/update-on-virtual-data.js.map +1 -1
- package/cjs/node_modules/swiper/shared/update-swiper.js +1 -1
- package/cjs/node_modules/swiper/shared/update-swiper.js.map +1 -1
- package/cjs/node_modules/swiper/shared/utils.js +1 -1
- package/cjs/node_modules/swiper/shared/utils.js.map +1 -1
- package/cjs/node_modules/swiper/swiper-react.js +1 -1
- package/cjs/node_modules/swiper/swiper-react.js.map +1 -1
- package/cjs/package.json +1 -0
- package/components/carousel/Autoplay.js.map +1 -1
- package/components/carousel/Carousel.js +1 -1
- package/components/carousel/Carousel.js.map +1 -1
- package/components/carousel/Navigation.js +1 -1
- package/components/carousel/Navigation.js.map +1 -1
- package/components/carousel/Pagination.js +1 -1
- package/components/carousel/Pagination.js.map +1 -1
- package/components/header/Header.js +1 -1
- package/components/header/Header.js.map +1 -1
- package/components/header/HeaderNavigationHero.js +1 -1
- package/components/header/HeaderNavigationHero.js.map +1 -1
- package/components/header/HeaderNavigationMenu.js +1 -1
- package/components/header/HeaderNavigationMenu.js.map +1 -1
- package/components/hotspotCard/HotspotCard.js +1 -1
- package/components/hotspotCard/HotspotCard.js.map +1 -1
- package/components/icon/stroke/EmissionLabelIcon.js.map +1 -1
- package/components/image/Image.d.ts +38 -14
- package/components/image/Image.js +1 -1
- package/components/image/Image.js.map +1 -1
- package/components/index.d.ts +1 -0
- package/components/label/Label.js.map +1 -1
- package/components/overlay/Overlay.js +1 -1
- package/components/overlay/Overlay.js.map +1 -1
- package/components/video/Video.d.ts +9 -8
- package/components/video/Video.js +1 -1
- package/components/video/Video.js.map +1 -1
- package/components/video/index.d.ts +5 -1
- package/components/video/useVideoFullscreen.d.ts +13 -0
- package/components/video/useVideoFullscreen.js +2 -0
- package/components/video/useVideoFullscreen.js.map +1 -0
- package/components/video/useVideoPlayPause.d.ts +13 -0
- package/components/video/useVideoPlayPause.js +2 -0
- package/components/video/useVideoPlayPause.js.map +1 -0
- package/components/video/useVideoReady.d.ts +6 -0
- package/components/video/useVideoReady.js +2 -0
- package/components/video/useVideoReady.js.map +1 -0
- package/components/video/useVideoSoundControls.d.ts +17 -0
- package/components/video/useVideoSoundControls.js +2 -0
- package/components/video/useVideoSoundControls.js.map +1 -0
- package/components/video/useVideoTime.d.ts +22 -0
- package/components/video/useVideoTime.js +2 -0
- package/components/video/useVideoTime.js.map +1 -0
- package/components/videoPlayer/VideoPlayer.d.ts +125 -0
- package/components/videoPlayer/VideoPlayer.js +2 -0
- package/components/videoPlayer/VideoPlayer.js.map +1 -0
- package/components/videoPlayer/VideoPlayerFullscreenButton.d.ts +31 -0
- package/components/videoPlayer/VideoPlayerFullscreenButton.js +2 -0
- package/components/videoPlayer/VideoPlayerFullscreenButton.js.map +1 -0
- package/components/videoPlayer/VideoPlayerIconButton.d.ts +16 -0
- package/components/videoPlayer/VideoPlayerIconButton.js +2 -0
- package/components/videoPlayer/VideoPlayerIconButton.js.map +1 -0
- package/components/videoPlayer/VideoPlayerPlayPauseButton.d.ts +31 -0
- package/components/videoPlayer/VideoPlayerPlayPauseButton.js +2 -0
- package/components/videoPlayer/VideoPlayerPlayPauseButton.js.map +1 -0
- package/components/videoPlayer/VideoPlayerSkipButton.d.ts +23 -0
- package/components/videoPlayer/VideoPlayerSkipButton.js +2 -0
- package/components/videoPlayer/VideoPlayerSkipButton.js.map +1 -0
- package/components/videoPlayer/VideoPlayerSlider.d.ts +12 -0
- package/components/videoPlayer/VideoPlayerSlider.js +2 -0
- package/components/videoPlayer/VideoPlayerSlider.js.map +1 -0
- package/components/videoPlayer/VideoPlayerSoundButton.d.ts +31 -0
- package/components/videoPlayer/VideoPlayerSoundButton.js +2 -0
- package/components/videoPlayer/VideoPlayerSoundButton.js.map +1 -0
- package/components/videoPlayer/VideoPlayerTime.d.ts +17 -0
- package/components/videoPlayer/VideoPlayerTime.js +2 -0
- package/components/videoPlayer/VideoPlayerTime.js.map +1 -0
- package/components/videoPlayer/formatVideoTime.d.ts +5 -0
- package/components/videoPlayer/formatVideoTime.js +2 -0
- package/components/videoPlayer/formatVideoTime.js.map +1 -0
- package/components/videoPlayer/index.d.ts +10 -0
- package/components/videoPlayer/useVideoPlayerVisibility.d.ts +24 -0
- package/components/videoPlayer/useVideoPlayerVisibility.js +2 -0
- package/components/videoPlayer/useVideoPlayerVisibility.js.map +1 -0
- package/index.js +1 -1
- package/materialui/accordion/AccordionSummaryExpandWrapper.d.ts +5 -1
- package/materialui/accordion/AccordionSummaryExpandWrapper.js +1 -1
- package/materialui/accordion/AccordionSummaryExpandWrapper.js.map +1 -1
- package/materialui/alert/alertTitle.theme.js.map +1 -1
- package/materialui/card/Card.js.map +1 -1
- package/materialui/card/card.theme.js +1 -1
- package/materialui/card/card.theme.js.map +1 -1
- package/materialui/divider/divider.theme.js +1 -1
- package/materialui/divider/divider.theme.js.map +1 -1
- package/materialui/skeleton/skeleton.theme.js +1 -1
- package/materialui/skeleton/skeleton.theme.js.map +1 -1
- package/materialui/slider/slider.theme.js +1 -1
- package/materialui/slider/slider.theme.js.map +1 -1
- package/materialui/table/tablePagination.theme.js.map +1 -1
- package/materialui/tabs/Tab.d.ts +1 -0
- package/materialui/tabs/Tab.js +1 -1
- package/materialui/tabs/Tab.js.map +1 -1
- package/materialui/tabs/TabsScrollButton.d.ts +3 -1
- package/materialui/tabs/TabsScrollButton.js +1 -1
- package/materialui/tabs/TabsScrollButton.js.map +1 -1
- package/materialui/textfield/FormControl.js +1 -1
- package/materialui/textfield/FormControl.js.map +1 -1
- package/materialui/textfield/FormHelperText.js +1 -1
- package/materialui/textfield/FormHelperText.js.map +1 -1
- package/materialui/theme/sxConfig.js.map +1 -1
- package/materialui/theme/theme.extensions.d.ts +29 -0
- package/package.json +1 -1
- package/cjs/components/video/hooks.d.ts +0 -43
- package/cjs/components/video/hooks.js +0 -2
- package/cjs/components/video/hooks.js.map +0 -1
- package/cjs/node_modules/swiper/shared/ssr-window.esm.js +0 -2
- package/cjs/node_modules/swiper/shared/ssr-window.esm.js.map +0 -1
- package/components/video/hooks.d.ts +0 -43
- package/components/video/hooks.js +0 -2
- package/components/video/hooks.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Video.js","sources":["../../../../../src/components/video/Video.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { Box, BoxProps } from '@mui/material';\nimport { FileMissingIcon } from '@/src/components/icon/stroke/FileMissingIcon';\nimport { sxOverride } from '@/src/utils/sxOverride';\n\nexport interface VideoProps extends BoxProps<'video'> {\n /** The URL of the video to embed. */\n src?: string;\n /** video type property, @default \"video/mp4\" */\n type?: string;\n
|
|
1
|
+
{"version":3,"file":"Video.js","sources":["../../../../../src/components/video/Video.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { Box, BoxProps } from '@mui/material';\nimport { FileMissingIcon } from '@/src/components/icon/stroke/FileMissingIcon';\nimport { sxOverride } from '@/src/utils/sxOverride';\n\nexport interface VideoProps extends BoxProps<'video'> {\n /** The URL of the video to embed. */\n src?: string;\n /** video type property, @default \"video/mp4\" */\n type?: string;\n /**\n * Content shown when the video fails to load\n * @default <FileMissingIcon /> centered inside the Box\n */\n errorFallback?: React.ReactNode;\n /**\n * @deprecated Use `errorFallback` instead\n */\n noVidErrorMessage?: React.ReactNode;\n /** Custom source Box props. */\n sourceProps?: BoxProps<'source'>;\n /** Video element autoPlay, @default true */\n autoPlay?: boolean;\n /** Video element muted, @default true */\n muted?: boolean;\n /** Video element native browser controls, @default false */\n controls?: boolean;\n /** Video element loop, @default false */\n loop?: boolean;\n}\n\n/**\n * Video player wrapping the native <video> element, rendering a fallback when the source fails to load\n */\nexport const Video: React.FC<VideoProps> = forwardRef(\n (\n {\n src,\n type = 'video/mp4',\n errorFallback,\n noVidErrorMessage,\n sourceProps,\n sx,\n autoPlay = true,\n muted = true,\n controls = false,\n loop = false,\n children,\n ...rest\n },\n ref,\n ) => {\n return (\n <Box\n component=\"video\"\n data-testid=\"Video\"\n ref={ref}\n autoPlay={autoPlay}\n muted={muted}\n controls={controls}\n loop={loop}\n sx={sxOverride(\n {\n display: 'block',\n maxWidth: '100%',\n // fill the container fullscreened by useVideoFullscreen, otherwise the video keeps its intrinsic size\n // leading * keeps stylis from prefixing the bare pseudo-class with the own class\n '*:fullscreen > &': {\n width: '100%',\n height: '100%',\n objectFit: 'contain',\n },\n },\n sx,\n )}\n {...rest}\n >\n {children ?? <Box component=\"source\" src={src} type={type} {...sourceProps} />}\n {errorFallback ?? noVidErrorMessage ?? (\n <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100%' }}>\n <FileMissingIcon />\n </Box>\n )}\n </Box>\n );\n },\n);\n"],"names":["Video","forwardRef","src","type","errorFallback","noVidErrorMessage","sourceProps","sx","autoPlay","muted","controls","loop","children","rest","ref","_jsxs","Box","component","sxOverride","display","maxWidth","width","height","objectFit","_jsx","justifyContent","alignItems","FileMissingIcon"],"mappings":"qMAkCaA,EAA8BC,EAAAA,WACzC,EAEIC,MACAC,OAAO,YACPC,gBACAC,oBACAC,cACAC,KACAC,YAAW,EACXC,SAAQ,EACRC,YAAW,EACXC,QAAO,EACPC,cACGC,GAELC,IAGEC,EAAAA,KAACC,EAAAA,IAAG,CACFC,UAAU,QAAO,cACL,QACZH,IAAKA,EACLN,SAAUA,EACVC,MAAOA,EACPC,SAAUA,EACVC,KAAMA,EACNJ,GAAIW,aACF,CACEC,QAAS,QACTC,SAAU,OAGV,mBAAoB,CAClBC,MAAO,OACPC,OAAQ,OACRC,UAAW,YAGfhB,MAEEM,EAAID,SAAA,CAEPA,GAAYY,EAAAA,IAACR,EAAAA,IAAG,CAACC,UAAU,SAASf,IAAKA,EAAKC,KAAMA,KAAUG,IAC9DF,GAAiBC,GAChBmB,EAAAA,IAACR,EAAAA,IAAG,CAACT,GAAI,CAAEY,QAAS,OAAQM,eAAgB,SAAUC,WAAY,SAAUL,MAAO,OAAQC,OAAQ,QAAQV,SACzGY,EAAAA,IAACG,EAAAA,gBAAe"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
interface VideoFullscreen {
|
|
3
|
+
/** Whether the container is currently in fullscreen mode */
|
|
4
|
+
isFullscreen: boolean;
|
|
5
|
+
/** Toggles fullscreen on/off */
|
|
6
|
+
handleFullscreenToggle: () => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Manages fullscreen state for a container element
|
|
10
|
+
* @param containerRef - A reference to the element to display in fullscreen.
|
|
11
|
+
*/
|
|
12
|
+
export declare const useVideoFullscreen: (containerRef: RefObject<HTMLElement>) => VideoFullscreen;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react");const n=()=>{const e=document;return document.fullscreenElement??e.webkitFullscreenElement??null},t=e=>e instanceof HTMLVideoElement?e:e?.querySelector("video")??null;exports.useVideoFullscreen=r=>{const[l,c]=e.useState(!1);e.useEffect(()=>{const e=t(r.current),l=()=>c(n()===r.current),s=()=>c(!0),u=()=>c(!1);return document.addEventListener("fullscreenchange",l),document.addEventListener("webkitfullscreenchange",l),e?.addEventListener("webkitbeginfullscreen",s),e?.addEventListener("webkitendfullscreen",u),()=>{document.removeEventListener("fullscreenchange",l),document.removeEventListener("webkitfullscreenchange",l),e?.removeEventListener("webkitbeginfullscreen",s),e?.removeEventListener("webkitendfullscreen",u)}},[r]);return{isFullscreen:l,handleFullscreenToggle:e.useCallback(()=>{const e=r?.current;if(e)if(n()===e){const e=document;document.exitFullscreen?document.exitFullscreen().catch(()=>{}):e.webkitExitFullscreen?.()}else e.requestFullscreen?e.requestFullscreen().catch(()=>{}):e.webkitRequestFullscreen?e.webkitRequestFullscreen():t(e)?.webkitEnterFullscreen?.()},[r])}};
|
|
2
|
+
//# sourceMappingURL=useVideoFullscreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVideoFullscreen.js","sources":["../../../../../src/components/video/useVideoFullscreen.ts"],"sourcesContent":["import { useCallback, useEffect, useState, type RefObject } from 'react';\n\ninterface VideoFullscreen {\n /** Whether the container is currently in fullscreen mode */\n isFullscreen: boolean;\n /** Toggles fullscreen on/off */\n handleFullscreenToggle: () => void;\n}\n\n/** Prefixed Fullscreen API of Safari before 16.4 */\ninterface WebkitDocument extends Document {\n webkitFullscreenElement?: Element | null;\n webkitExitFullscreen?: () => void;\n}\n\n/** Prefixed Fullscreen API of Safari before 16.4 */\ninterface WebkitElement extends HTMLElement {\n webkitRequestFullscreen?: () => void;\n}\n\n/** Native video fullscreen of iPhone Safari, which has no Fullscreen API on other elements */\ninterface WebkitVideoElement extends HTMLVideoElement {\n webkitEnterFullscreen?: () => void;\n}\n\n/**\n * Returns the element currently in fullscreen, including the prefixed Safari API\n */\nconst getFullscreenElement = (): Element | null => {\n const webkitDocument: WebkitDocument = document;\n return document.fullscreenElement ?? webkitDocument.webkitFullscreenElement ?? null;\n};\n\n/**\n * Returns the video inside the container, or the container itself when it is the video\n */\nconst getVideo = (container: HTMLElement | null): WebkitVideoElement | null =>\n container instanceof HTMLVideoElement ? container : (container?.querySelector('video') ?? null);\n\n/**\n * Manages fullscreen state for a container element\n * @param containerRef - A reference to the element to display in fullscreen.\n */\nexport const useVideoFullscreen = (containerRef: RefObject<HTMLElement>): VideoFullscreen => {\n const [isFullscreen, setIsFullscreen] = useState(false);\n\n useEffect(() => {\n const video = getVideo(containerRef.current);\n const handleFullscreenChange = () => setIsFullscreen(getFullscreenElement() === containerRef.current);\n const handleVideoFullscreenEnter = () => setIsFullscreen(true);\n const handleVideoFullscreenExit = () => setIsFullscreen(false);\n\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n document.addEventListener('webkitfullscreenchange', handleFullscreenChange);\n video?.addEventListener('webkitbeginfullscreen', handleVideoFullscreenEnter);\n video?.addEventListener('webkitendfullscreen', handleVideoFullscreenExit);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange);\n document.removeEventListener('webkitfullscreenchange', handleFullscreenChange);\n video?.removeEventListener('webkitbeginfullscreen', handleVideoFullscreenEnter);\n video?.removeEventListener('webkitendfullscreen', handleVideoFullscreenExit);\n };\n }, [containerRef]);\n\n const handleFullscreenToggle = useCallback(() => {\n const container: WebkitElement | null = containerRef?.current;\n if (!container) {\n return;\n }\n\n if (getFullscreenElement() === container) {\n const webkitDocument: WebkitDocument = document;\n if (document.exitFullscreen) {\n document.exitFullscreen().catch(() => {});\n } else {\n webkitDocument.webkitExitFullscreen?.();\n }\n } else if (container.requestFullscreen) {\n container.requestFullscreen().catch(() => {});\n } else if (container.webkitRequestFullscreen) {\n container.webkitRequestFullscreen();\n } else {\n // iPhone Safari shows the video in its native player, the custom controls are not visible there\n getVideo(container)?.webkitEnterFullscreen?.();\n }\n }, [containerRef]);\n\n return {\n isFullscreen,\n handleFullscreenToggle,\n };\n};\n"],"names":["getFullscreenElement","webkitDocument","document","fullscreenElement","webkitFullscreenElement","getVideo","container","HTMLVideoElement","querySelector","containerRef","isFullscreen","setIsFullscreen","useState","useEffect","video","current","handleFullscreenChange","handleVideoFullscreenEnter","handleVideoFullscreenExit","addEventListener","removeEventListener","handleFullscreenToggle","useCallback","exitFullscreen","catch","webkitExitFullscreen","requestFullscreen","webkitRequestFullscreen","webkitEnterFullscreen"],"mappings":"oCA4BA,MAAMA,EAAuB,KAC3B,MAAMC,EAAiCC,SACvC,OAAOA,SAASC,mBAAqBF,EAAeG,yBAA2B,MAM3EC,EAAYC,GAChBA,aAAqBC,iBAAmBD,EAAaA,GAAWE,cAAc,UAAY,gCAMzDC,IACjC,MAAOC,EAAcC,GAAmBC,EAAAA,UAAS,GAEjDC,EAAAA,UAAU,KACR,MAAMC,EAAQT,EAASI,EAAaM,SAC9BC,EAAyB,IAAML,EAAgBX,MAA2BS,EAAaM,SACvFE,EAA6B,IAAMN,GAAgB,GACnDO,EAA4B,IAAMP,GAAgB,GAOxD,OALAT,SAASiB,iBAAiB,mBAAoBH,GAC9Cd,SAASiB,iBAAiB,yBAA0BH,GACpDF,GAAOK,iBAAiB,wBAAyBF,GACjDH,GAAOK,iBAAiB,sBAAuBD,GAExC,KACLhB,SAASkB,oBAAoB,mBAAoBJ,GACjDd,SAASkB,oBAAoB,yBAA0BJ,GACvDF,GAAOM,oBAAoB,wBAAyBH,GACpDH,GAAOM,oBAAoB,sBAAuBF,KAEnD,CAACT,IAyBJ,MAAO,CACLC,eACAW,uBAzB6BC,EAAAA,YAAY,KACzC,MAAMhB,EAAkCG,GAAcM,QACtD,GAAKT,EAIL,GAAIN,MAA2BM,EAAW,CACxC,MAAML,EAAiCC,SACnCA,SAASqB,eACXrB,SAASqB,iBAAiBC,MAAM,QAEhCvB,EAAewB,wBAEnB,MAAWnB,EAAUoB,kBACnBpB,EAAUoB,oBAAoBF,MAAM,QAC3BlB,EAAUqB,wBACnBrB,EAAUqB,0BAGVtB,EAASC,IAAYsB,2BAEtB,CAACnB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
interface PlayPause {
|
|
3
|
+
/** Whether the video is currently playing */
|
|
4
|
+
isPlaying: boolean;
|
|
5
|
+
/** Toggles play/pause */
|
|
6
|
+
handlePlayPause: () => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Manages play/pause state for a video element
|
|
10
|
+
* @param videoRef - A reference to the video element.
|
|
11
|
+
*/
|
|
12
|
+
export declare const useVideoPlayPause: (videoRef: RefObject<HTMLVideoElement>) => PlayPause;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react");exports.useVideoPlayPause=t=>{const[r,a]=e.useState(!t.current?.paused);e.useEffect(()=>{const e=t?.current;e&&void 0===r&&a(!e?.paused);const s=()=>{(!e.autoplay||!e.ended)&&a(!0)},n=()=>a(!1);if(e)return e.addEventListener("pause",n),a(!e.paused),e.addEventListener("play",s),()=>{e.removeEventListener("play",s),e.removeEventListener("pause",n)}},[t]);const s=e.useCallback(()=>{t?.current&&(r?t.current.pause():t.current.play().catch(()=>{}),a(e=>!e))},[r,t]);return{isPlaying:r,handlePlayPause:s}};
|
|
2
|
+
//# sourceMappingURL=useVideoPlayPause.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVideoPlayPause.js","sources":["../../../../../src/components/video/useVideoPlayPause.ts"],"sourcesContent":["import { useCallback, useEffect, useState, type RefObject } from 'react';\n\ninterface PlayPause {\n /** Whether the video is currently playing */\n isPlaying: boolean;\n /** Toggles play/pause */\n handlePlayPause: () => void;\n}\n\n/**\n * Manages play/pause state for a video element\n * @param videoRef - A reference to the video element.\n */\nexport const useVideoPlayPause = (videoRef: RefObject<HTMLVideoElement>): PlayPause => {\n const [isPlaying, setIsPlaying] = useState(!videoRef.current?.paused);\n\n useEffect(() => {\n const video = videoRef?.current;\n\n if (video && isPlaying === undefined) {\n setIsPlaying(!video?.paused);\n }\n\n const handlePlay = () => {\n // prevent playing state when autoplay triggers play event on re-entering viewport after video ended\n !(video.autoplay && video.ended) && setIsPlaying(true);\n };\n const handlePause = () => setIsPlaying(false);\n\n if (video) {\n video.addEventListener('pause', handlePause);\n\n // Set initial state (not enough without event listeners due to autoPlay and paused race conditions)\n setIsPlaying(!video.paused);\n\n video.addEventListener('play', handlePlay);\n\n return () => {\n video.removeEventListener('play', handlePlay);\n video.removeEventListener('pause', handlePause);\n };\n }\n }, [videoRef]);\n\n const handlePlayPause = useCallback(() => {\n if (videoRef?.current) {\n if (isPlaying) {\n videoRef.current.pause();\n } else {\n videoRef.current.play().catch(() => {});\n }\n setIsPlaying((prevIsPlaying) => !prevIsPlaying);\n }\n }, [isPlaying, videoRef]);\n\n return {\n isPlaying,\n handlePlayPause,\n };\n};\n"],"names":["videoRef","isPlaying","setIsPlaying","useState","current","paused","useEffect","video","undefined","handlePlay","autoplay","ended","handlePause","addEventListener","removeEventListener","handlePlayPause","useCallback","pause","play","catch","prevIsPlaying"],"mappings":"8DAakCA,IAChC,MAAOC,EAAWC,GAAgBC,EAAAA,UAAUH,EAASI,SAASC,QAE9DC,EAAAA,UAAU,KACR,MAAMC,EAAQP,GAAUI,QAEpBG,QAAuBC,IAAdP,GACXC,GAAcK,GAAOF,QAGvB,MAAMI,EAAa,OAEfF,EAAMG,WAAYH,EAAMI,QAAUT,GAAa,IAE7CU,EAAc,IAAMV,GAAa,GAEvC,GAAIK,EAQF,OAPAA,EAAMM,iBAAiB,QAASD,GAGhCV,GAAcK,EAAMF,QAEpBE,EAAMM,iBAAiB,OAAQJ,GAExB,KACLF,EAAMO,oBAAoB,OAAQL,GAClCF,EAAMO,oBAAoB,QAASF,KAGtC,CAACZ,IAEJ,MAAMe,EAAkBC,EAAAA,YAAY,KAC9BhB,GAAUI,UACRH,EACFD,EAASI,QAAQa,QAEjBjB,EAASI,QAAQc,OAAOC,MAAM,QAEhCjB,EAAckB,IAAmBA,KAElC,CAACnB,EAAWD,IAEf,MAAO,CACLC,YACAc"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Reports whether the video has loaded its metadata without an error, false while loading or after a failure
|
|
4
|
+
* @param videoRef - A reference to the video element.
|
|
5
|
+
*/
|
|
6
|
+
export declare const useVideoReady: (videoRef: RefObject<HTMLVideoElement>) => boolean;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react");exports.useVideoReady=t=>{const[r,n]=e.useState(!1);return e.useEffect(()=>{const e=t?.current;if(!e)return;const r=()=>n(e.readyState>=HTMLMediaElement.HAVE_METADATA&&!e.error);return r(),e.addEventListener("loadedmetadata",r),e.addEventListener("emptied",r),e.addEventListener("error",r),()=>{e.removeEventListener("loadedmetadata",r),e.removeEventListener("emptied",r),e.removeEventListener("error",r)}},[t]),r};
|
|
2
|
+
//# sourceMappingURL=useVideoReady.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVideoReady.js","sources":["../../../../../src/components/video/useVideoReady.ts"],"sourcesContent":["import { useEffect, useState, type RefObject } from 'react';\n\n/**\n * Reports whether the video has loaded its metadata without an error, false while loading or after a failure\n * @param videoRef - A reference to the video element.\n */\nexport const useVideoReady = (videoRef: RefObject<HTMLVideoElement>): boolean => {\n // not ready on the server and before the first check, so the controls never flash over a broken video\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n const video = videoRef?.current;\n if (!video) {\n return;\n }\n // a failed <source> keeps readyState at HAVE_NOTHING, a failed src or a network error sets video.error\n const update = () => setReady(video.readyState >= HTMLMediaElement.HAVE_METADATA && !video.error);\n\n // the video may have loaded before hydration\n update();\n video.addEventListener('loadedmetadata', update);\n video.addEventListener('emptied', update);\n video.addEventListener('error', update);\n\n return () => {\n video.removeEventListener('loadedmetadata', update);\n video.removeEventListener('emptied', update);\n video.removeEventListener('error', update);\n };\n }, [videoRef]);\n\n return ready;\n};\n"],"names":["videoRef","ready","setReady","useState","useEffect","video","current","update","readyState","HTMLMediaElement","HAVE_METADATA","error","addEventListener","removeEventListener"],"mappings":"0DAM8BA,IAE5B,MAAOC,EAAOC,GAAYC,EAAAA,UAAS,GAuBnC,OArBAC,EAAAA,UAAU,KACR,MAAMC,EAAQL,GAAUM,QACxB,IAAKD,EACH,OAGF,MAAME,EAAS,IAAML,EAASG,EAAMG,YAAcC,iBAAiBC,gBAAkBL,EAAMM,OAQ3F,OALAJ,IACAF,EAAMO,iBAAiB,iBAAkBL,GACzCF,EAAMO,iBAAiB,UAAWL,GAClCF,EAAMO,iBAAiB,QAASL,GAEzB,KACLF,EAAMQ,oBAAoB,iBAAkBN,GAC5CF,EAAMQ,oBAAoB,UAAWN,GACrCF,EAAMQ,oBAAoB,QAASN,KAEpC,CAACP,IAEGC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
interface SoundControls {
|
|
3
|
+
/** Whether the video is muted */
|
|
4
|
+
isMuted: boolean;
|
|
5
|
+
/** Toggles mute on/off */
|
|
6
|
+
handleMuteToggle: () => void;
|
|
7
|
+
/** Current volume level, 0 to 1 */
|
|
8
|
+
volume: number;
|
|
9
|
+
/** Sets the volume level */
|
|
10
|
+
handleVolumeChange: (newVolume: number) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Manages mute and volume for a video element
|
|
14
|
+
* @param videoRef - A reference to the video element.
|
|
15
|
+
*/
|
|
16
|
+
export declare const useVideoSoundControls: (videoRef: RefObject<HTMLVideoElement>) => SoundControls;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react");exports.useVideoSoundControls=t=>{const[u,r]=e.useState(t.current?.muted),[s,n]=e.useState(1);e.useEffect(()=>{const e=t?.current;e&&(void 0===u&&r(e?.muted),t.current.muted=u,t.current.volume=s)},[u,s,t]);const o=e.useCallback(()=>{r(e=>!e)},[]),c=e.useCallback(e=>{n(e)},[]);return{isMuted:u,handleMuteToggle:o,volume:s,handleVolumeChange:c}};
|
|
2
|
+
//# sourceMappingURL=useVideoSoundControls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVideoSoundControls.js","sources":["../../../../../src/components/video/useVideoSoundControls.ts"],"sourcesContent":["import { useCallback, useEffect, useState, type RefObject } from 'react';\n\ninterface SoundControls {\n /** Whether the video is muted */\n isMuted: boolean;\n /** Toggles mute on/off */\n handleMuteToggle: () => void;\n /** Current volume level, 0 to 1 */\n volume: number;\n /** Sets the volume level */\n handleVolumeChange: (newVolume: number) => void;\n}\n\n/**\n * Manages mute and volume for a video element\n * @param videoRef - A reference to the video element.\n */\nexport const useVideoSoundControls = (videoRef: RefObject<HTMLVideoElement>): SoundControls => {\n const [isMuted, setIsMuted] = useState(videoRef.current?.muted);\n const [volume, setVolume] = useState(1);\n\n useEffect(() => {\n const video = videoRef?.current;\n if (video) {\n if (isMuted === undefined) {\n setIsMuted(video?.muted);\n }\n\n videoRef.current.muted = isMuted;\n videoRef.current.volume = volume;\n }\n }, [isMuted, volume, videoRef]);\n\n const handleMuteToggle = useCallback(() => {\n setIsMuted((prevMuted) => !prevMuted);\n }, []);\n\n const handleVolumeChange = useCallback((newVolume: number) => {\n setVolume(newVolume);\n }, []);\n\n return {\n isMuted,\n handleMuteToggle,\n volume,\n handleVolumeChange,\n };\n};\n"],"names":["videoRef","isMuted","setIsMuted","useState","current","muted","volume","setVolume","useEffect","video","undefined","handleMuteToggle","useCallback","prevMuted","handleVolumeChange","newVolume"],"mappings":"kEAiBsCA,IACpC,MAAOC,EAASC,GAAcC,EAAAA,SAASH,EAASI,SAASC,QAClDC,EAAQC,GAAaJ,EAAAA,SAAS,GAErCK,EAAAA,UAAU,KACR,MAAMC,EAAQT,GAAUI,QACpBK,SACcC,IAAZT,GACFC,EAAWO,GAAOJ,OAGpBL,EAASI,QAAQC,MAAQJ,EACzBD,EAASI,QAAQE,OAASA,IAE3B,CAACL,EAASK,EAAQN,IAErB,MAAMW,EAAmBC,EAAAA,YAAY,KACnCV,EAAYW,IAAeA,IAC1B,IAEGC,EAAqBF,cAAaG,IACtCR,EAAUQ,IACT,IAEH,MAAO,CACLd,UACAU,mBACAL,SACAQ"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
export interface VideoTimeOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Updates the current time every animation frame while playing, instead of the ~250ms `timeupdate` interval
|
|
5
|
+
*/
|
|
6
|
+
smooth?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface VideoTime {
|
|
9
|
+
/** Current playback time, in seconds */
|
|
10
|
+
currentTime: number;
|
|
11
|
+
/** Total video duration, in seconds */
|
|
12
|
+
duration: number;
|
|
13
|
+
/** Seeks to a specific time */
|
|
14
|
+
handleSeek: (newTime: number) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Synchronizes playback time and duration with component state
|
|
18
|
+
* @param videoRef - A reference to the video element.
|
|
19
|
+
* @param options - Additional options.
|
|
20
|
+
*/
|
|
21
|
+
export declare const useVideoTime: (videoRef: RefObject<HTMLVideoElement>, { smooth }?: VideoTimeOptions) => VideoTime;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react");exports.useVideoTime=(t,{smooth:r}={})=>{const[n,a]=e.useState(0),[u,i]=e.useState(t.current?.duration),s=e.useCallback(()=>{t.current&&a(t.current.currentTime)},[t]),c=e.useCallback(e=>{t.current&&(t.current.currentTime=e),a(e)},[t]),d=e.useCallback(()=>{t.current?.duration&&i(t.current.duration)},[t]);return e.useEffect(()=>{const e=t?.current;if(e)return e.addEventListener("loadedmetadata",d),e.addEventListener("timeupdate",s),()=>{e.removeEventListener("loadedmetadata",d),e.removeEventListener("timeupdate",s)}},[d,s,t]),e.useEffect(()=>{const e=t?.current;if(!r||!e)return;let n;const u=()=>{a(e.currentTime),n=requestAnimationFrame(u)},i=()=>{cancelAnimationFrame(n),n=requestAnimationFrame(u)},s=()=>cancelAnimationFrame(n);return e.paused||i(),e.addEventListener("play",i),e.addEventListener("pause",s),()=>{s(),e.removeEventListener("play",i),e.removeEventListener("pause",s)}},[r,t]),{currentTime:n,duration:u,handleSeek:c}};
|
|
2
|
+
//# sourceMappingURL=useVideoTime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVideoTime.js","sources":["../../../../../src/components/video/useVideoTime.ts"],"sourcesContent":["import { useCallback, useEffect, useState, type RefObject } from 'react';\n\nexport interface VideoTimeOptions {\n /**\n * Updates the current time every animation frame while playing, instead of the ~250ms `timeupdate` interval\n */\n smooth?: boolean;\n}\n\ninterface VideoTime {\n /** Current playback time, in seconds */\n currentTime: number;\n /** Total video duration, in seconds */\n duration: number;\n /** Seeks to a specific time */\n handleSeek: (newTime: number) => void;\n}\n\n/**\n * Synchronizes playback time and duration with component state\n * @param videoRef - A reference to the video element.\n * @param options - Additional options.\n */\nexport const useVideoTime = (videoRef: RefObject<HTMLVideoElement>, { smooth }: VideoTimeOptions = {}): VideoTime => {\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(videoRef.current?.duration);\n\n const handleTimeUpdate = useCallback(() => {\n if (videoRef.current) {\n setCurrentTime(videoRef.current.currentTime);\n }\n }, [videoRef]);\n\n const handleSeek = useCallback(\n (newTime: number) => {\n if (videoRef.current) {\n videoRef.current.currentTime = newTime;\n }\n setCurrentTime(newTime);\n },\n [videoRef],\n );\n\n const handleLoadedMetadata = useCallback(() => {\n if (videoRef.current?.duration) {\n setDuration(videoRef.current.duration);\n }\n }, [videoRef]);\n\n useEffect(() => {\n const video = videoRef?.current;\n if (video) {\n video.addEventListener('loadedmetadata', handleLoadedMetadata);\n video.addEventListener('timeupdate', handleTimeUpdate);\n\n return () => {\n video.removeEventListener('loadedmetadata', handleLoadedMetadata);\n video.removeEventListener('timeupdate', handleTimeUpdate);\n };\n }\n }, [handleLoadedMetadata, handleTimeUpdate, videoRef]);\n\n useEffect(() => {\n const video = videoRef?.current;\n if (!smooth || !video) {\n return;\n }\n let frame: number;\n const update = () => {\n setCurrentTime(video.currentTime);\n frame = requestAnimationFrame(update);\n };\n const start = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(update);\n };\n const stop = () => cancelAnimationFrame(frame);\n\n if (!video.paused) {\n start();\n }\n video.addEventListener('play', start);\n video.addEventListener('pause', stop);\n\n return () => {\n stop();\n video.removeEventListener('play', start);\n video.removeEventListener('pause', stop);\n };\n }, [smooth, videoRef]);\n\n return {\n currentTime,\n duration,\n handleSeek,\n };\n};\n"],"names":["videoRef","smooth","currentTime","setCurrentTime","useState","duration","setDuration","current","handleTimeUpdate","useCallback","handleSeek","newTime","handleLoadedMetadata","useEffect","video","addEventListener","removeEventListener","frame","update","requestAnimationFrame","start","cancelAnimationFrame","stop","paused"],"mappings":"yDAuB4B,CAACA,GAAyCC,UAA6B,CAAA,KACjG,MAAOC,EAAaC,GAAkBC,EAAAA,SAAS,IACxCC,EAAUC,GAAeF,EAAAA,SAASJ,EAASO,SAASF,UAErDG,EAAmBC,EAAAA,YAAY,KAC/BT,EAASO,SACXJ,EAAeH,EAASO,QAAQL,cAEjC,CAACF,IAEEU,EAAaD,cAChBE,IACKX,EAASO,UACXP,EAASO,QAAQL,YAAcS,GAEjCR,EAAeQ,IAEjB,CAACX,IAGGY,EAAuBH,EAAAA,YAAY,KACnCT,EAASO,SAASF,UACpBC,EAAYN,EAASO,QAAQF,WAE9B,CAACL,IA4CJ,OA1CAa,EAAAA,UAAU,KACR,MAAMC,EAAQd,GAAUO,QACxB,GAAIO,EAIF,OAHAA,EAAMC,iBAAiB,iBAAkBH,GACzCE,EAAMC,iBAAiB,aAAcP,GAE9B,KACLM,EAAME,oBAAoB,iBAAkBJ,GAC5CE,EAAME,oBAAoB,aAAcR,KAG3C,CAACI,EAAsBJ,EAAkBR,IAE5Ca,EAAAA,UAAU,KACR,MAAMC,EAAQd,GAAUO,QACxB,IAAKN,IAAWa,EACd,OAEF,IAAIG,EACJ,MAAMC,EAAS,KACbf,EAAeW,EAAMZ,aACrBe,EAAQE,sBAAsBD,IAE1BE,EAAQ,KACZC,qBAAqBJ,GACrBA,EAAQE,sBAAsBD,IAE1BI,EAAO,IAAMD,qBAAqBJ,GAQxC,OANKH,EAAMS,QACTH,IAEFN,EAAMC,iBAAiB,OAAQK,GAC/BN,EAAMC,iBAAiB,QAASO,GAEzB,KACLA,IACAR,EAAME,oBAAoB,OAAQI,GAClCN,EAAME,oBAAoB,QAASM,KAEpC,CAACrB,EAAQD,IAEL,CACLE,cACAG,WACAK"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React, { ElementType, type ReactNode } from 'react';
|
|
2
|
+
import { BoxProps } from '@mui/material';
|
|
3
|
+
import { OverlayProps } from '../overlay/Overlay';
|
|
4
|
+
import { VideoPlayerPlayPauseButtonProps } from './VideoPlayerPlayPauseButton';
|
|
5
|
+
import { VideoPlayerSkipButtonProps } from './VideoPlayerSkipButton';
|
|
6
|
+
import { VideoPlayerSoundButtonProps } from './VideoPlayerSoundButton';
|
|
7
|
+
import { VideoPlayerFullscreenButtonProps } from './VideoPlayerFullscreenButton';
|
|
8
|
+
import { VideoPlayerSliderProps } from './VideoPlayerSlider';
|
|
9
|
+
import { VideoPlayerTimeProps } from './VideoPlayerTime';
|
|
10
|
+
export interface VideoPlayerProps extends BoxProps<ElementType> {
|
|
11
|
+
/**
|
|
12
|
+
* Video component to be controlled
|
|
13
|
+
*/
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* Inactivity in ms after which the controls hide while the video is playing
|
|
17
|
+
* @default 3000
|
|
18
|
+
*/
|
|
19
|
+
hideDelay?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Inactivity in ms after which the controls hide while the video is playing in fullscreen
|
|
22
|
+
* @default 1000
|
|
23
|
+
*/
|
|
24
|
+
fullscreenHideDelay?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Keeps the controls always visible
|
|
27
|
+
*/
|
|
28
|
+
disableAutoHide?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Callback of the skip-previous button, the button is hidden when not provided
|
|
31
|
+
*/
|
|
32
|
+
onSkipPrevious?: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* Callback of the skip-next button, the button is hidden when not provided
|
|
35
|
+
*/
|
|
36
|
+
onSkipNext?: () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Hides the sound button
|
|
39
|
+
*/
|
|
40
|
+
disableSound?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Hides the play/pause button
|
|
43
|
+
*/
|
|
44
|
+
disablePlayPause?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Hides the current time and duration
|
|
47
|
+
*/
|
|
48
|
+
disableTime?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Hides the seek slider
|
|
51
|
+
*/
|
|
52
|
+
disableSlider?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Hides the fullscreen button and disables fullscreen on double click
|
|
55
|
+
*/
|
|
56
|
+
disableFullscreen?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* ReactNode that renders custom buttons between the playback and sound buttons. If multiple buttons are needed, wrap them in React.Fragment
|
|
59
|
+
*/
|
|
60
|
+
startButtons?: ReactNode;
|
|
61
|
+
/**
|
|
62
|
+
* ReactNode that renders custom buttons before the fullscreen button. If multiple buttons are needed, wrap them in React.Fragment
|
|
63
|
+
*/
|
|
64
|
+
endButtons?: ReactNode;
|
|
65
|
+
/**
|
|
66
|
+
* Properties passed to the Overlay component
|
|
67
|
+
*/
|
|
68
|
+
overlayProps?: Omit<OverlayProps, 'overlayContent' | 'wrapperProps' | 'children'>;
|
|
69
|
+
/**
|
|
70
|
+
* Properties passed to the VideoPlayerPlayPauseButton component
|
|
71
|
+
*/
|
|
72
|
+
playPauseButtonProps?: Omit<VideoPlayerPlayPauseButtonProps, 'videoRef'>;
|
|
73
|
+
/**
|
|
74
|
+
* Properties passed to the skip-previous VideoPlayerSkipButton component
|
|
75
|
+
*/
|
|
76
|
+
skipPreviousButtonProps?: Omit<VideoPlayerSkipButtonProps, 'direction'>;
|
|
77
|
+
/**
|
|
78
|
+
* Properties passed to the skip-next VideoPlayerSkipButton component
|
|
79
|
+
*/
|
|
80
|
+
skipNextButtonProps?: Omit<VideoPlayerSkipButtonProps, 'direction'>;
|
|
81
|
+
/**
|
|
82
|
+
* Properties passed to the VideoPlayerSoundButton component
|
|
83
|
+
*/
|
|
84
|
+
soundButtonProps?: Omit<VideoPlayerSoundButtonProps, 'videoRef'>;
|
|
85
|
+
/**
|
|
86
|
+
* Properties passed to the VideoPlayerFullscreenButton component
|
|
87
|
+
*/
|
|
88
|
+
fullscreenButtonProps?: Omit<VideoPlayerFullscreenButtonProps, 'containerRef'>;
|
|
89
|
+
/**
|
|
90
|
+
* Properties passed to the VideoPlayerSlider component
|
|
91
|
+
*/
|
|
92
|
+
sliderProps?: Omit<VideoPlayerSliderProps, 'videoRef'>;
|
|
93
|
+
/**
|
|
94
|
+
* Properties passed to the VideoPlayerTime component
|
|
95
|
+
*/
|
|
96
|
+
timeProps?: Omit<VideoPlayerTimeProps, 'videoRef'>;
|
|
97
|
+
/**
|
|
98
|
+
* Properties applied to the wrapper of playback buttons centered over the video on mobile
|
|
99
|
+
*/
|
|
100
|
+
centerWrapperProps?: BoxProps<ElementType>;
|
|
101
|
+
/**
|
|
102
|
+
* Properties applied to the bar at the bottom, wrapping the slider and the toolbar
|
|
103
|
+
*/
|
|
104
|
+
barProps?: BoxProps<ElementType>;
|
|
105
|
+
/**
|
|
106
|
+
* Properties applied to the toolbar below the slider
|
|
107
|
+
*/
|
|
108
|
+
toolbarProps?: BoxProps<ElementType>;
|
|
109
|
+
/**
|
|
110
|
+
* Properties applied to the wrapper of playback buttons in the toolbar on desktop
|
|
111
|
+
*/
|
|
112
|
+
playbackWrapperProps?: BoxProps<ElementType>;
|
|
113
|
+
/**
|
|
114
|
+
* Properties applied to the wrapper of buttons at the start of the toolbar
|
|
115
|
+
*/
|
|
116
|
+
startButtonsWrapperProps?: BoxProps<ElementType>;
|
|
117
|
+
/**
|
|
118
|
+
* Properties applied to the wrapper of buttons at the end of the toolbar, filling its free space
|
|
119
|
+
*/
|
|
120
|
+
endButtonsWrapperProps?: BoxProps<ElementType>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Video player controls with play/pause, sound, seek and fullscreen buttons over the Video component
|
|
124
|
+
*/
|
|
125
|
+
export declare const VideoPlayer: React.FC<VideoPlayerProps>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),r=require("react"),i=require("@mui/material"),t=require("../../utils/sxOverride/sxOverride.js"),s=require("../overlay/Overlay.js"),o=require("./VideoPlayerPlayPauseButton.js"),n=require("./VideoPlayerSkipButton.js"),a=require("./VideoPlayerSoundButton.js"),l=require("./VideoPlayerFullscreenButton.js"),d=require("./VideoPlayerSlider.js"),u=require("./VideoPlayerTime.js"),c=require("./useVideoPlayerVisibility.js"),x=require("../video/useVideoReady.js"),p=require("../video/useVideoFullscreen.js"),y=require("../icon/filled/ControlsPlayFilledIcon.js"),P=require("../icon/filled/ControlsPauseFilledIcon.js");const v=r.forwardRef(({children:v,hideDelay:m=3e3,fullscreenHideDelay:f=1e3,disableAutoHide:j,onSkipPrevious:V,onSkipNext:b,disableSound:k,disablePlayPause:B,disableTime:h,disableSlider:C,disableFullscreen:g,startButtons:q,endButtons:S,overlayProps:F,playPauseButtonProps:O,skipPreviousButtonProps:R,skipNextButtonProps:I,soundButtonProps:D,fullscreenButtonProps:T,sliderProps:w,timeProps:E,centerWrapperProps:z,barProps:W,toolbarProps:L,playbackWrapperProps:A,startButtonsWrapperProps:H,endButtonsWrapperProps:K,onClick:M,onDoubleClick:N,onPointerMove:G,onPointerDown:J,onPointerLeave:Q,onKeyDown:U,sx:X,...Y},Z)=>{const $=r.useRef(null),_=r.useRef(null),ee=r.useCallback(e=>{$.current=e,_.current=e?.querySelector("video")??null},[]),re=i.useForkRef(ee,Z),{isFullscreen:ie,handleFullscreenToggle:te}=p.useVideoFullscreen($),{visible:se,handleActivity:oe,handleLeave:ne}=c.useVideoPlayerVisibility(_,{hideDelay:ie?f:m,disabled:j}),ae=x.useVideoReady(_),le=({target:e,currentTarget:r})=>e===_.current||e instanceof Element&&e.parentElement===r,de=r.useRef(!1),{sx:ue,...ce}=F??{},{sx:xe,...pe}=z??{},{sx:ye,...Pe}=W??{},{sx:ve,...me}=L??{},{sx:fe,...je}=A??{},{sx:Ve,...be}=H??{},{sx:ke,...Be}=K??{},{sx:he,...Ce}=E??{},ge=r=>e.jsxs(e.Fragment,{children:[V&&e.jsx(n.VideoPlayerSkipButton,{direction:"previous",size:"medium",...R,onClick:e=>{R?.onClick?.(e),V()}}),!B&&e.jsx(o.VideoPlayerPlayPauseButton,{videoRef:_,size:"medium",...r&&{PlayIconComponent:y.ControlsPlayFilledIcon,PauseIconComponent:P.ControlsPauseFilledIcon},...O}),b&&e.jsx(n.VideoPlayerSkipButton,{direction:"next",size:"medium",...I,onClick:e=>{I?.onClick?.(e),b()}})]});return e.jsxs(s.Overlay,{"data-testid":"VideoPlayer.overlay","data-visible":se,overlayContent:v,wrapperProps:{ref:re,"data-testid":"VideoPlayer",sx:t.sxOverride({...!se&&{cursor:"none"}},X),onClick:e=>{M?.(e);const r=_.current;r&&ae&&le(e)&&!de.current&&(r.paused?r.play().catch(()=>{}):r.pause())},onDoubleClick:e=>{N?.(e),!g&&ae&&le(e)&&te()},onPointerMove:e=>{G?.(e),oe()},onPointerDown:e=>{J?.(e),de.current="touch"===e.pointerType&&!se,oe()},onKeyDown:e=>{U?.(e),oe()},onPointerLeave:e=>{Q?.(e),"mouse"===e.pointerType&&ne()},...Y},sx:t.sxOverride({transition:"opacity 0.3s ease-out",backgroundRepeat:"no-repeat",backgroundPosition:"bottom",backgroundSize:{xs:"100% 100%",md:"100% 136px"},...!se&&{opacity:"0",pointerEvents:"none","&:has(:focus-visible)":{opacity:"1",pointerEvents:"auto"}},...!ae&&{display:"none"}},ue),...ce,children:[e.jsx(i.Box,{"data-testid":"VideoPlayer.center",sx:t.sxOverride({position:"absolute",inset:"0",display:{xs:"flex",md:"none"},alignItems:"center",justifyContent:"center",gap:2,'&:has(+ * :is([data-testid="VideoPlayerSeekSlider"], [data-testid="VideoPlayer.start"] > :not([data-testid="VideoPlayer.playback"]), [data-testid="VideoPlayer.end"] > *))':{mb:4,"@media (orientation: portrait)":{"*:fullscreen &":{mb:0}}},pointerEvents:"none","& > *":{pointerEvents:"auto"}},xe),...pe,children:ge(!0)}),e.jsxs(i.Box,{"data-testid":"VideoPlayer.bar",sx:t.sxOverride({position:"absolute",insetInline:"0",bottom:"0",display:"flex",flexDirection:"column",alignItems:"stretch",justifyContent:"flex-start",px:{xs:2,md:3},pb:{xs:.5,md:1}},ye),...Pe,children:[!C&&e.jsx(d.VideoPlayerSlider,{videoRef:_,...w}),e.jsxs(i.Box,{"data-testid":"VideoPlayer.toolbar",sx:t.sxOverride({display:"flex",alignItems:"center",justifyContent:"flex-start",gap:{xs:.5,md:1}},ve),...me,children:[e.jsxs(i.Box,{"data-testid":"VideoPlayer.start",sx:t.sxOverride({display:"flex",alignItems:"center",justifyContent:"flex-start",gap:"inherit"},Ve),...be,children:[e.jsx(i.Box,{"data-testid":"VideoPlayer.playback",sx:t.sxOverride({display:{xs:"none",md:"contents"}},fe),...je,children:ge(!1)}),q,!k&&e.jsx(a.VideoPlayerSoundButton,{videoRef:_,...D}),!h&&e.jsx(u.VideoPlayerTime,{videoRef:_,sx:t.sxOverride({order:{xs:"-1",md:"0"}},he),...Ce})]}),e.jsxs(i.Box,{"data-testid":"VideoPlayer.end",sx:t.sxOverride({flex:"1",display:"flex",alignItems:"center",justifyContent:"flex-end",gap:"inherit"},ke),...Be,children:[S,!g&&e.jsx(l.VideoPlayerFullscreenButton,{containerRef:$,...T})]})]})]})]})});exports.VideoPlayer=v;
|
|
2
|
+
//# sourceMappingURL=VideoPlayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoPlayer.js","sources":["../../../../../src/components/videoPlayer/VideoPlayer.tsx"],"sourcesContent":["import React, {\n ElementType,\n forwardRef,\n type KeyboardEvent,\n type MouseEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n useCallback,\n useRef,\n} from 'react';\nimport { Box, BoxProps, useForkRef } from '@mui/material';\nimport { sxOverride } from '@/src/utils/sxOverride';\nimport { Overlay, OverlayProps } from '@/src/components/overlay/Overlay';\nimport { VideoPlayerPlayPauseButton, VideoPlayerPlayPauseButtonProps } from './VideoPlayerPlayPauseButton';\nimport { VideoPlayerSkipButton, VideoPlayerSkipButtonProps } from './VideoPlayerSkipButton';\nimport { VideoPlayerSoundButton, VideoPlayerSoundButtonProps } from './VideoPlayerSoundButton';\nimport { VideoPlayerFullscreenButton, VideoPlayerFullscreenButtonProps } from './VideoPlayerFullscreenButton';\nimport { VideoPlayerSlider, VideoPlayerSliderProps } from './VideoPlayerSlider';\nimport { VideoPlayerTime, VideoPlayerTimeProps } from './VideoPlayerTime';\nimport { useVideoPlayerVisibility } from './useVideoPlayerVisibility';\nimport { useVideoReady } from '@/src/components/video/useVideoReady';\nimport { useVideoFullscreen } from '@/src/components/video/useVideoFullscreen';\nimport { ControlsPlayFilledIcon } from '@/src/components/icon/filled/ControlsPlayFilledIcon';\nimport { ControlsPauseFilledIcon } from '@/src/components/icon/filled/ControlsPauseFilledIcon';\n\nexport interface VideoPlayerProps extends BoxProps<ElementType> {\n /**\n * Video component to be controlled\n */\n children: ReactNode;\n /**\n * Inactivity in ms after which the controls hide while the video is playing\n * @default 3000\n */\n hideDelay?: number;\n /**\n * Inactivity in ms after which the controls hide while the video is playing in fullscreen\n * @default 1000\n */\n fullscreenHideDelay?: number;\n /**\n * Keeps the controls always visible\n */\n disableAutoHide?: boolean;\n /**\n * Callback of the skip-previous button, the button is hidden when not provided\n */\n onSkipPrevious?: () => void;\n /**\n * Callback of the skip-next button, the button is hidden when not provided\n */\n onSkipNext?: () => void;\n /**\n * Hides the sound button\n */\n disableSound?: boolean;\n /**\n * Hides the play/pause button\n */\n disablePlayPause?: boolean;\n /**\n * Hides the current time and duration\n */\n disableTime?: boolean;\n /**\n * Hides the seek slider\n */\n disableSlider?: boolean;\n /**\n * Hides the fullscreen button and disables fullscreen on double click\n */\n disableFullscreen?: boolean;\n /**\n * ReactNode that renders custom buttons between the playback and sound buttons. If multiple buttons are needed, wrap them in React.Fragment\n */\n startButtons?: ReactNode;\n /**\n * ReactNode that renders custom buttons before the fullscreen button. If multiple buttons are needed, wrap them in React.Fragment\n */\n endButtons?: ReactNode;\n /**\n * Properties passed to the Overlay component\n */\n overlayProps?: Omit<OverlayProps, 'overlayContent' | 'wrapperProps' | 'children'>;\n /**\n * Properties passed to the VideoPlayerPlayPauseButton component\n */\n playPauseButtonProps?: Omit<VideoPlayerPlayPauseButtonProps, 'videoRef'>;\n /**\n * Properties passed to the skip-previous VideoPlayerSkipButton component\n */\n skipPreviousButtonProps?: Omit<VideoPlayerSkipButtonProps, 'direction'>;\n /**\n * Properties passed to the skip-next VideoPlayerSkipButton component\n */\n skipNextButtonProps?: Omit<VideoPlayerSkipButtonProps, 'direction'>;\n /**\n * Properties passed to the VideoPlayerSoundButton component\n */\n soundButtonProps?: Omit<VideoPlayerSoundButtonProps, 'videoRef'>;\n /**\n * Properties passed to the VideoPlayerFullscreenButton component\n */\n fullscreenButtonProps?: Omit<VideoPlayerFullscreenButtonProps, 'containerRef'>;\n /**\n * Properties passed to the VideoPlayerSlider component\n */\n sliderProps?: Omit<VideoPlayerSliderProps, 'videoRef'>;\n /**\n * Properties passed to the VideoPlayerTime component\n */\n timeProps?: Omit<VideoPlayerTimeProps, 'videoRef'>;\n /**\n * Properties applied to the wrapper of playback buttons centered over the video on mobile\n */\n centerWrapperProps?: BoxProps<ElementType>;\n /**\n * Properties applied to the bar at the bottom, wrapping the slider and the toolbar\n */\n barProps?: BoxProps<ElementType>;\n /**\n * Properties applied to the toolbar below the slider\n */\n toolbarProps?: BoxProps<ElementType>;\n /**\n * Properties applied to the wrapper of playback buttons in the toolbar on desktop\n */\n playbackWrapperProps?: BoxProps<ElementType>;\n /**\n * Properties applied to the wrapper of buttons at the start of the toolbar\n */\n startButtonsWrapperProps?: BoxProps<ElementType>;\n /**\n * Properties applied to the wrapper of buttons at the end of the toolbar, filling its free space\n */\n endButtonsWrapperProps?: BoxProps<ElementType>;\n}\n\n/**\n * Video player controls with play/pause, sound, seek and fullscreen buttons over the Video component\n */\nexport const VideoPlayer: React.FC<VideoPlayerProps> = forwardRef(\n (\n {\n children,\n // common inactivity before players hide their controls\n hideDelay = 3000,\n // shorter in fullscreen, where the controls cover the whole screen\n fullscreenHideDelay = 1000,\n disableAutoHide,\n onSkipPrevious,\n onSkipNext,\n disableSound,\n disablePlayPause,\n disableTime,\n disableSlider,\n disableFullscreen,\n startButtons,\n endButtons,\n overlayProps,\n playPauseButtonProps,\n skipPreviousButtonProps,\n skipNextButtonProps,\n soundButtonProps,\n fullscreenButtonProps,\n sliderProps,\n timeProps,\n centerWrapperProps,\n barProps,\n toolbarProps,\n playbackWrapperProps,\n startButtonsWrapperProps,\n endButtonsWrapperProps,\n onClick,\n onDoubleClick,\n onPointerMove,\n onPointerDown,\n onPointerLeave,\n onKeyDown,\n sx,\n ...rest\n },\n ref,\n ) => {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const videoRef = useRef<HTMLVideoElement | null>(null);\n\n // refs attach before any effect runs, so the parts subscribe to the video passed as children\n const handleContainerRef = useCallback((node: HTMLDivElement | null) => {\n containerRef.current = node;\n videoRef.current = node?.querySelector('video') ?? null;\n }, []);\n const handleRef = useForkRef(handleContainerRef, ref);\n\n const { isFullscreen, handleFullscreenToggle } = useVideoFullscreen(containerRef);\n\n const { visible, handleActivity, handleLeave } = useVideoPlayerVisibility(videoRef, {\n hideDelay: isFullscreen ? fullscreenHideDelay : hideDelay,\n disabled: disableAutoHide,\n });\n\n const ready = useVideoReady(videoRef);\n\n // only the video or the overlay above it react to clicks, not the controls\n const isVideoArea = ({ target, currentTarget }: MouseEvent<HTMLDivElement>) =>\n target === videoRef.current || (target instanceof Element && target.parentElement === currentTarget);\n\n // a touch tap on hidden controls only shows them, pointerdown already shows them before the click arrives\n const revealTapRef = useRef(false);\n\n const handleClick = (event: MouseEvent<HTMLDivElement>) => {\n onClick?.(event);\n const video = videoRef.current;\n if (!video || !ready || !isVideoArea(event) || revealTapRef.current) {\n return;\n }\n if (video.paused) {\n video.play().catch(() => {});\n } else {\n video.pause();\n }\n };\n\n const { sx: overlaySx, ...restOverlay } = overlayProps ?? {};\n const { sx: centerWrapperSx, ...restCenterWrapper } = centerWrapperProps ?? {};\n const { sx: barSx, ...restBar } = barProps ?? {};\n const { sx: toolbarSx, ...restToolbar } = toolbarProps ?? {};\n const { sx: playbackWrapperSx, ...restPlaybackWrapper } = playbackWrapperProps ?? {};\n const { sx: startButtonsWrapperSx, ...restStartButtonsWrapper } = startButtonsWrapperProps ?? {};\n const { sx: endButtonsWrapperSx, ...restEndButtonsWrapper } = endButtonsWrapperProps ?? {};\n const { sx: timeSx, ...restTime } = timeProps ?? {};\n\n // playback buttons keep the medium size on mobile, where they are centered over the video\n const renderPlaybackButtons = (centered: boolean) => (\n <>\n {onSkipPrevious && (\n <VideoPlayerSkipButton\n direction=\"previous\"\n size=\"medium\"\n {...skipPreviousButtonProps}\n onClick={(event: MouseEvent<HTMLButtonElement>) => {\n skipPreviousButtonProps?.onClick?.(event);\n onSkipPrevious();\n }}\n />\n )}\n {!disablePlayPause && (\n <VideoPlayerPlayPauseButton\n videoRef={videoRef}\n size=\"medium\"\n // the centered button over the video on mobile has filled icons\n {...(centered && {\n PlayIconComponent: ControlsPlayFilledIcon,\n PauseIconComponent: ControlsPauseFilledIcon,\n })}\n {...playPauseButtonProps}\n />\n )}\n {onSkipNext && (\n <VideoPlayerSkipButton\n direction=\"next\"\n size=\"medium\"\n {...skipNextButtonProps}\n onClick={(event: MouseEvent<HTMLButtonElement>) => {\n skipNextButtonProps?.onClick?.(event);\n onSkipNext();\n }}\n />\n )}\n </>\n );\n\n return (\n <Overlay\n data-testid=\"VideoPlayer.overlay\"\n data-visible={visible}\n overlayContent={children}\n wrapperProps={{\n ref: handleRef,\n 'data-testid': 'VideoPlayer',\n sx: sxOverride(\n {\n ...(!visible && {\n cursor: 'none',\n }),\n },\n sx,\n ),\n onClick: handleClick,\n onDoubleClick: (event: MouseEvent<HTMLDivElement>) => {\n onDoubleClick?.(event);\n if (!disableFullscreen && ready && isVideoArea(event)) {\n handleFullscreenToggle();\n }\n },\n onPointerMove: (event: ReactPointerEvent<HTMLDivElement>) => {\n onPointerMove?.(event);\n handleActivity();\n },\n onPointerDown: (event: ReactPointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event);\n revealTapRef.current = event.pointerType === 'touch' && !visible;\n handleActivity();\n },\n onKeyDown: (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n handleActivity();\n },\n onPointerLeave: (event: ReactPointerEvent<HTMLDivElement>) => {\n onPointerLeave?.(event);\n // touch fires pointerleave right after each tap, only a mouse can actually leave\n if (event.pointerType === 'mouse') {\n handleLeave();\n }\n },\n ...rest,\n }}\n sx={sxOverride(\n {\n transition: 'opacity 0.3s ease-out',\n backgroundRepeat: 'no-repeat',\n backgroundPosition: 'bottom',\n // twice the height of the bar (68px) on desktop, mobile keeps it over the whole video for centered buttons\n backgroundSize: { xs: '100% 100%', md: '100% 136px' },\n ...(!visible && {\n opacity: '0',\n // pointer reaches the video, any activity over it shows the controls again\n pointerEvents: 'none',\n // keyboard focus keeps the controls shown, focus after a mouse click is not visible\n '&:has(:focus-visible)': {\n opacity: '1',\n pointerEvents: 'auto',\n },\n }),\n // no controls over a loading or broken video, display none also keeps them out of keyboard focus\n ...(!ready && {\n display: 'none',\n }),\n },\n overlaySx,\n )}\n {...restOverlay}\n >\n <Box\n data-testid=\"VideoPlayer.center\"\n sx={sxOverride(\n {\n // on mobile playback buttons are centered over the video\n position: 'absolute',\n inset: '0',\n display: { xs: 'flex', md: 'none' },\n alignItems: 'center',\n justifyContent: 'center',\n gap: 2,\n // lifted above the bar only when it shows the slider or a toolbar item, the hidden playback wrapper does not count\n '&:has(+ * :is([data-testid=\"VideoPlayerSeekSlider\"], [data-testid=\"VideoPlayer.start\"] > :not([data-testid=\"VideoPlayer.playback\"]), [data-testid=\"VideoPlayer.end\"] > *))':\n {\n mb: 4,\n // portrait fullscreen letterboxes the video, the bar sits below it and nothing overlaps,\n // the fullscreen player is the viewport, so the media query matches its own orientation\n '@media (orientation: portrait)': {\n '*:fullscreen &': {\n mb: 0,\n },\n },\n },\n // keep the video clickable around the buttons\n pointerEvents: 'none',\n '& > *': {\n pointerEvents: 'auto',\n },\n },\n centerWrapperSx,\n )}\n {...restCenterWrapper}\n >\n {renderPlaybackButtons(true)}\n </Box>\n <Box\n data-testid=\"VideoPlayer.bar\"\n sx={sxOverride(\n {\n position: 'absolute',\n insetInline: '0',\n bottom: '0',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'stretch',\n justifyContent: 'flex-start',\n px: { xs: 2, md: 3 },\n // the slider has its own space above the track\n pb: { xs: 0.5, md: 1 },\n },\n barSx,\n )}\n {...restBar}\n >\n {!disableSlider && <VideoPlayerSlider videoRef={videoRef} {...sliderProps} />}\n <Box\n data-testid=\"VideoPlayer.toolbar\"\n sx={sxOverride(\n {\n // no padding, the toolbar collapses on mobile when nothing but centered playback buttons is in it\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-start',\n gap: { xs: 0.5, md: 1 },\n },\n toolbarSx,\n )}\n {...restToolbar}\n >\n <Box\n data-testid=\"VideoPlayer.start\"\n sx={sxOverride(\n { display: 'flex', alignItems: 'center', justifyContent: 'flex-start', gap: 'inherit' },\n startButtonsWrapperSx,\n )}\n {...restStartButtonsWrapper}\n >\n <Box\n data-testid=\"VideoPlayer.playback\"\n sx={sxOverride({ display: { xs: 'none', md: 'contents' } }, playbackWrapperSx)}\n {...restPlaybackWrapper}\n >\n {renderPlaybackButtons(false)}\n </Box>\n {startButtons}\n {!disableSound && <VideoPlayerSoundButton videoRef={videoRef} {...soundButtonProps} />}\n {!disableTime && (\n <VideoPlayerTime\n videoRef={videoRef}\n sx={sxOverride(\n {\n // on mobile the time comes first, before the start and sound buttons\n order: { xs: '-1', md: '0' },\n },\n timeSx,\n )}\n {...restTime}\n />\n )}\n </Box>\n <Box\n data-testid=\"VideoPlayer.end\"\n sx={sxOverride(\n {\n // pushes the end buttons to the right side, while also wrapping them\n flex: '1',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n gap: 'inherit',\n },\n endButtonsWrapperSx,\n )}\n {...restEndButtonsWrapper}\n >\n {endButtons}\n {!disableFullscreen && (\n <VideoPlayerFullscreenButton containerRef={containerRef} {...fullscreenButtonProps} />\n )}\n </Box>\n </Box>\n </Box>\n </Overlay>\n );\n },\n);\n"],"names":["VideoPlayer","forwardRef","children","hideDelay","fullscreenHideDelay","disableAutoHide","onSkipPrevious","onSkipNext","disableSound","disablePlayPause","disableTime","disableSlider","disableFullscreen","startButtons","endButtons","overlayProps","playPauseButtonProps","skipPreviousButtonProps","skipNextButtonProps","soundButtonProps","fullscreenButtonProps","sliderProps","timeProps","centerWrapperProps","barProps","toolbarProps","playbackWrapperProps","startButtonsWrapperProps","endButtonsWrapperProps","onClick","onDoubleClick","onPointerMove","onPointerDown","onPointerLeave","onKeyDown","sx","rest","ref","containerRef","useRef","videoRef","handleContainerRef","useCallback","node","current","querySelector","handleRef","useForkRef","isFullscreen","handleFullscreenToggle","useVideoFullscreen","visible","handleActivity","handleLeave","useVideoPlayerVisibility","disabled","ready","useVideoReady","isVideoArea","target","currentTarget","Element","parentElement","revealTapRef","overlaySx","restOverlay","centerWrapperSx","restCenterWrapper","barSx","restBar","toolbarSx","restToolbar","playbackWrapperSx","restPlaybackWrapper","startButtonsWrapperSx","restStartButtonsWrapper","endButtonsWrapperSx","restEndButtonsWrapper","timeSx","restTime","renderPlaybackButtons","centered","_jsxs","_Fragment","_jsx","VideoPlayerSkipButton","direction","size","event","VideoPlayerPlayPauseButton","PlayIconComponent","ControlsPlayFilledIcon","PauseIconComponent","ControlsPauseFilledIcon","Overlay","overlayContent","wrapperProps","sxOverride","cursor","video","paused","play","catch","pause","pointerType","transition","backgroundRepeat","backgroundPosition","backgroundSize","xs","md","opacity","pointerEvents","display","Box","position","inset","alignItems","justifyContent","gap","mb","insetInline","bottom","flexDirection","px","pb","VideoPlayerSlider","VideoPlayerSoundButton","VideoPlayerTime","order","flex","VideoPlayerFullscreenButton"],"mappings":"spBA6IaA,EAA0CC,EAAAA,WACrD,EAEIC,WAEAC,YAAY,IAEZC,sBAAsB,IACtBC,kBACAC,iBACAC,aACAC,eACAC,mBACAC,cACAC,gBACAC,oBACAC,eACAC,aACAC,eACAC,uBACAC,0BACAC,sBACAC,mBACAC,wBACAC,cACAC,YACAC,qBACAC,WACAC,eACAC,uBACAC,2BACAC,yBACAC,UACAC,gBACAC,gBACAC,gBACAC,iBACAC,YACAC,QACGC,GAELC,KAEA,MAAMC,EAAeC,EAAAA,OAA8B,MAC7CC,EAAWD,EAAAA,OAAgC,MAG3CE,GAAqBC,cAAaC,IACtCL,EAAaM,QAAUD,EACvBH,EAASI,QAAUD,GAAME,cAAc,UAAY,MAClD,IACGC,GAAYC,EAAAA,WAAWN,GAAoBJ,IAE3CW,aAAEA,GAAYC,uBAAEA,IAA2BC,EAAAA,mBAAmBZ,IAE9Da,QAAEA,GAAOC,eAAEA,GAAcC,YAAEA,IAAgBC,EAAAA,yBAAyBd,EAAU,CAClFrC,UAAW6C,GAAe5C,EAAsBD,EAChDoD,SAAUlD,IAGNmD,GAAQC,EAAAA,cAAcjB,GAGtBkB,GAAc,EAAGC,SAAQC,mBAC7BD,IAAWnB,EAASI,SAAYe,aAAkBE,SAAWF,EAAOG,gBAAkBF,EAGlFG,GAAexB,EAAAA,QAAO,IAepBJ,GAAI6B,MAAcC,IAAgBlD,GAAgB,CAAA,GAClDoB,GAAI+B,MAAoBC,IAAsB5C,GAAsB,CAAA,GACpEY,GAAIiC,MAAUC,IAAY7C,GAAY,CAAA,GACtCW,GAAImC,MAAcC,IAAgB9C,GAAgB,CAAA,GAClDU,GAAIqC,MAAsBC,IAAwB/C,GAAwB,CAAA,GAC1ES,GAAIuC,MAA0BC,IAA4BhD,GAA4B,CAAA,GACtFQ,GAAIyC,MAAwBC,IAA0BjD,GAA0B,CAAA,GAChFO,GAAI2C,MAAWC,IAAazD,GAAa,CAAA,EAG3C0D,GAAyBC,GAC7BC,EAAAA,KAAAC,EAAAA,SAAA,CAAAjF,SAAA,CACGI,GACC8E,EAAAA,IAACC,EAAAA,sBAAqB,CACpBC,UAAU,WACVC,KAAK,YACDtE,EACJY,QAAU2D,IACRvE,GAAyBY,UAAU2D,GACnClF,QAIJG,GACA2E,EAAAA,IAACK,EAAAA,2BAA0B,CACzBjD,SAAUA,EACV+C,KAAK,YAEAN,GAAY,CACfS,kBAAmBC,EAAAA,uBACnBC,mBAAoBC,EAAAA,4BAElB7E,IAGPT,GACC6E,EAAAA,IAACC,wBAAqB,CACpBC,UAAU,OACVC,KAAK,YACDrE,EACJW,QAAU2D,IACRtE,GAAqBW,UAAU2D,GAC/BjF,UAOV,OACE2E,EAAAA,KAACY,UAAO,CAAA,cACM,sBAAqB,eACnB3C,GACd4C,eAAgB7F,EAChB8F,aAAc,CACZ3D,IAAKS,GACL,cAAe,cACfX,GAAI8D,EAAAA,WACF,KACO9C,IAAW,CACd+C,OAAQ,SAGZ/D,GAEFN,QA9Ee2D,IACnB3D,IAAU2D,GACV,MAAMW,EAAQ3D,EAASI,QAClBuD,GAAU3C,IAAUE,GAAY8B,KAAUzB,GAAanB,UAGxDuD,EAAMC,OACRD,EAAME,OAAOC,MAAM,QAEnBH,EAAMI,UAsEJzE,cAAgB0D,IACd1D,IAAgB0D,IACX5E,GAAqB4C,IAASE,GAAY8B,IAC7CvC,MAGJlB,cAAgByD,IACdzD,IAAgByD,GAChBpC,MAEFpB,cAAgBwD,IACdxD,IAAgBwD,GAChBzB,GAAanB,QAAgC,UAAtB4C,EAAMgB,cAA4BrD,GACzDC,MAEFlB,UAAYsD,IACVtD,IAAYsD,GACZpC,MAEFnB,eAAiBuD,IACfvD,IAAiBuD,GAES,UAAtBA,EAAMgB,aACRnD,SAGDjB,GAELD,GAAI8D,EAAAA,WACF,CACEQ,WAAY,wBACZC,iBAAkB,YAClBC,mBAAoB,SAEpBC,eAAgB,CAAEC,GAAI,YAAaC,GAAI,kBAClC3D,IAAW,CACd4D,QAAS,IAETC,cAAe,OAEf,wBAAyB,CACvBD,QAAS,IACTC,cAAe,aAIdxD,IAAS,CACZyD,QAAS,SAGbjD,OAEEC,GAAW/D,SAAA,CAEfkF,EAAAA,IAAC8B,EAAAA,IAAG,CAAA,cACU,qBACZ/E,GAAI8D,EAAAA,WACF,CAEEkB,SAAU,WACVC,MAAO,IACPH,QAAS,CAAEJ,GAAI,OAAQC,GAAI,QAC3BO,WAAY,SACZC,eAAgB,SAChBC,IAAK,EAEL,6KACE,CACEC,GAAI,EAGJ,iCAAkC,CAChC,iBAAkB,CAChBA,GAAI,KAKZR,cAAe,OACf,QAAS,CACPA,cAAe,SAGnB9C,OAEEC,GAAiBjE,SAEpB8E,IAAsB,KAEzBE,EAAAA,KAACgC,EAAAA,IAAG,CAAA,cACU,kBACZ/E,GAAI8D,EAAAA,WACF,CACEkB,SAAU,WACVM,YAAa,IACbC,OAAQ,IACRT,QAAS,OACTU,cAAe,SACfN,WAAY,UACZC,eAAgB,aAChBM,GAAI,CAAEf,GAAI,EAAGC,GAAI,GAEjBe,GAAI,CAAEhB,GAAI,GAAKC,GAAI,IAErB1C,OAEEC,GAAOnE,SAAA,EAETS,GAAiByE,MAAC0C,EAAAA,kBAAiB,CAACtF,SAAUA,KAAcnB,IAC9D6D,OAACgC,EAAAA,IAAG,CAAA,cACU,sBACZ/E,GAAI8D,aACF,CAEEgB,QAAS,OACTI,WAAY,SACZC,eAAgB,aAChBC,IAAK,CAAEV,GAAI,GAAKC,GAAI,IAEtBxC,OAEEC,aAEJW,EAAAA,KAACgC,EAAAA,IAAG,CAAA,cACU,oBACZ/E,GAAI8D,EAAAA,WACF,CAAEgB,QAAS,OAAQI,WAAY,SAAUC,eAAgB,aAAcC,IAAK,WAC5E7C,OAEEC,GAAuBzE,SAAA,CAE3BkF,EAAAA,IAAC8B,EAAAA,IAAG,CAAA,cACU,uBACZ/E,GAAI8D,EAAAA,WAAW,CAAEgB,QAAS,CAAEJ,GAAI,OAAQC,GAAI,aAAgBtC,OACxDC,GAAmBvE,SAEtB8E,IAAsB,KAExBnE,GACCL,GAAgB4E,EAAAA,IAAC2C,EAAAA,uBAAsB,CAACvF,SAAUA,KAAcrB,KAChET,GACA0E,EAAAA,IAAC4C,EAAAA,iBACCxF,SAAUA,EACVL,GAAI8D,aACF,CAEEgC,MAAO,CAAEpB,GAAI,KAAMC,GAAI,MAEzBhC,OAEEC,QAIVG,EAAAA,KAACgC,EAAAA,mBACa,kBACZ/E,GAAI8D,EAAAA,WACF,CAEEiC,KAAM,IACNjB,QAAS,OACTI,WAAY,SACZC,eAAgB,WAChBC,IAAK,WAEP3C,OAEEC,GAAqB3E,SAAA,CAExBY,GACCF,GACAwE,EAAAA,IAAC+C,+BAA4B7F,aAAcA,KAAkBlB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React, { ComponentType, type RefObject } from 'react';
|
|
2
|
+
import { SvgIconProps } from '@mui/material';
|
|
3
|
+
import { VideoPlayerIconButtonProps } from './VideoPlayerIconButton';
|
|
4
|
+
export interface VideoPlayerFullscreenButtonProps extends Omit<VideoPlayerIconButtonProps, 'title'> {
|
|
5
|
+
/**
|
|
6
|
+
* Ref to the element shown in fullscreen
|
|
7
|
+
*/
|
|
8
|
+
containerRef: RefObject<HTMLElement>;
|
|
9
|
+
/**
|
|
10
|
+
* Custom component for the icon shown while not fullscreen
|
|
11
|
+
*/
|
|
12
|
+
EnterIconComponent?: ComponentType<SvgIconProps>;
|
|
13
|
+
/**
|
|
14
|
+
* Custom component for the icon shown while fullscreen
|
|
15
|
+
*/
|
|
16
|
+
ExitIconComponent?: ComponentType<SvgIconProps>;
|
|
17
|
+
/**
|
|
18
|
+
* Tooltip and aria-label while not fullscreen
|
|
19
|
+
* @default Enter fullscreen
|
|
20
|
+
*/
|
|
21
|
+
enterTitle?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Tooltip and aria-label while fullscreen
|
|
24
|
+
* @default Exit fullscreen
|
|
25
|
+
*/
|
|
26
|
+
exitTitle?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Button that toggles fullscreen of the container
|
|
30
|
+
*/
|
|
31
|
+
export declare const VideoPlayerFullscreenButton: React.FC<VideoPlayerFullscreenButtonProps>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),r=require("react"),n=require("../icon/stroke/FullscreenEnterIcon.js"),l=require("../icon/stroke/FullscreenExitIcon.js"),t=require("../video/useVideoFullscreen.js"),o=require("./VideoPlayerIconButton.js");const c=r.forwardRef(({containerRef:r,EnterIconComponent:c,ExitIconComponent:i,enterTitle:s="Enter fullscreen",exitTitle:u="Exit fullscreen",onClick:a,...d},F)=>{const{isFullscreen:x,handleFullscreenToggle:E}=t.useVideoFullscreen(r),I=x?i??l.FullscreenExitIcon:c??n.FullscreenEnterIcon;return e.jsx(o.VideoPlayerIconButton,{ref:F,"data-testid":"VideoPlayerFullscreen",title:x?u:s,onClick:e=>{a?.(e),E()},...d,children:e.jsx(I,{"data-testid":x?"VideoPlayerFullscreenExitIcon":"VideoPlayerFullscreenEnterIcon"})})});exports.VideoPlayerFullscreenButton=c;
|
|
2
|
+
//# sourceMappingURL=VideoPlayerFullscreenButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoPlayerFullscreenButton.js","sources":["../../../../../src/components/videoPlayer/VideoPlayerFullscreenButton.tsx"],"sourcesContent":["import React, { ComponentType, forwardRef, type MouseEvent, type RefObject } from 'react';\nimport { SvgIconProps } from '@mui/material';\nimport { FullscreenEnterIcon } from '@/src/components/icon/stroke/FullscreenEnterIcon';\nimport { FullscreenExitIcon } from '@/src/components/icon/stroke/FullscreenExitIcon';\nimport { useVideoFullscreen } from '@/src/components/video/useVideoFullscreen';\nimport { VideoPlayerIconButton, VideoPlayerIconButtonProps } from './VideoPlayerIconButton';\n\nexport interface VideoPlayerFullscreenButtonProps extends Omit<VideoPlayerIconButtonProps, 'title'> {\n /**\n * Ref to the element shown in fullscreen\n */\n containerRef: RefObject<HTMLElement>;\n /**\n * Custom component for the icon shown while not fullscreen\n */\n EnterIconComponent?: ComponentType<SvgIconProps>;\n /**\n * Custom component for the icon shown while fullscreen\n */\n ExitIconComponent?: ComponentType<SvgIconProps>;\n /**\n * Tooltip and aria-label while not fullscreen\n * @default Enter fullscreen\n */\n enterTitle?: string;\n /**\n * Tooltip and aria-label while fullscreen\n * @default Exit fullscreen\n */\n exitTitle?: string;\n}\n\n/**\n * Button that toggles fullscreen of the container\n */\nexport const VideoPlayerFullscreenButton: React.FC<VideoPlayerFullscreenButtonProps> = forwardRef(\n (\n {\n containerRef,\n EnterIconComponent,\n ExitIconComponent,\n enterTitle = 'Enter fullscreen',\n exitTitle = 'Exit fullscreen',\n onClick,\n ...rest\n },\n ref,\n ) => {\n const { isFullscreen, handleFullscreenToggle } = useVideoFullscreen(containerRef);\n const Icon = isFullscreen ? (ExitIconComponent ?? FullscreenExitIcon) : (EnterIconComponent ?? FullscreenEnterIcon);\n\n return (\n <VideoPlayerIconButton\n ref={ref}\n data-testid=\"VideoPlayerFullscreen\"\n title={isFullscreen ? exitTitle : enterTitle}\n onClick={(event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n handleFullscreenToggle();\n }}\n {...rest}\n >\n <Icon data-testid={isFullscreen ? 'VideoPlayerFullscreenExitIcon' : 'VideoPlayerFullscreenEnterIcon'} />\n </VideoPlayerIconButton>\n );\n },\n);\n"],"names":["VideoPlayerFullscreenButton","forwardRef","containerRef","EnterIconComponent","ExitIconComponent","enterTitle","exitTitle","onClick","rest","ref","isFullscreen","handleFullscreenToggle","useVideoFullscreen","Icon","FullscreenExitIcon","FullscreenEnterIcon","_jsx","VideoPlayerIconButton","title","event","children"],"mappings":"4PAmCO,MAAMA,EAA0EC,EAAAA,WACrF,EAEIC,eACAC,qBACAC,oBACAC,aAAa,mBACbC,YAAY,kBACZC,aACGC,GAELC,KAEA,MAAMC,aAAEA,EAAYC,uBAAEA,GAA2BC,EAAAA,mBAAmBV,GAC9DW,EAAOH,EAAgBN,GAAqBU,EAAAA,mBAAuBX,GAAsBY,EAAAA,oBAE/F,OACEC,EAAAA,IAACC,EAAAA,sBAAqB,CACpBR,IAAKA,EAAG,cACI,wBACZS,MAAOR,EAAeJ,EAAYD,EAClCE,QAAUY,IACRZ,IAAUY,GACVR,QAEEH,EAAIY,SAERJ,EAAAA,IAACH,EAAI,CAAA,cAAcH,EAAe,gCAAkC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { IconButtonProps } from '@mui/material';
|
|
3
|
+
export interface VideoPlayerIconButtonProps extends IconButtonProps {
|
|
4
|
+
/**
|
|
5
|
+
* Text shown in the tooltip and used as aria-label
|
|
6
|
+
*/
|
|
7
|
+
title: string;
|
|
8
|
+
/**
|
|
9
|
+
* Fixed size on all screens, small on mobile and medium on desktop when not set
|
|
10
|
+
*/
|
|
11
|
+
size?: IconButtonProps['size'];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* IconButton with a tooltip, base of the VideoPlayer buttons
|
|
15
|
+
*/
|
|
16
|
+
export declare const VideoPlayerIconButton: React.FC<VideoPlayerIconButtonProps>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),r=require("react"),t=require("@mui/material"),i=require("../../utils/sxOverride/sxOverride.js");const o=r.forwardRef(({title:r,size:o,sx:s,...a},d)=>e.jsx(t.Tooltip,{title:r,placement:"top","data-tooltip-mode":"dark",slotProps:{popper:{modifiers:[{name:"offset",options:{offset:[0,2]}}]}},children:e.jsx(t.IconButton,{ref:d,color:"white",size:o??"medium","aria-label":r,sx:o?s:i.sxOverride(e=>({[e.breakpoints.down("md")]:{width:"32px",height:"32px","& .MuiSvgIcon-root":{fontSize:"17px"}}}),s),...a})}));exports.VideoPlayerIconButton=o;
|
|
2
|
+
//# sourceMappingURL=VideoPlayerIconButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoPlayerIconButton.js","sources":["../../../../../src/components/videoPlayer/VideoPlayerIconButton.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { IconButton, IconButtonProps, Tooltip } from '@mui/material';\nimport { sxOverride } from '@/src/utils/sxOverride';\n\nexport interface VideoPlayerIconButtonProps extends IconButtonProps {\n /**\n * Text shown in the tooltip and used as aria-label\n */\n title: string;\n /**\n * Fixed size on all screens, small on mobile and medium on desktop when not set\n */\n size?: IconButtonProps['size'];\n}\n\n/**\n * IconButton with a tooltip, base of the VideoPlayer buttons\n */\nexport const VideoPlayerIconButton: React.FC<VideoPlayerIconButtonProps> = forwardRef(\n ({ title, size, sx, ...rest }, ref) => (\n <Tooltip\n title={title}\n placement=\"top\"\n // dark mode tooltip is white, keeps it white over the video regardless of the theme\n data-tooltip-mode=\"dark\"\n // tooltip closer to the button than the default offset\n slotProps={{ popper: { modifiers: [{ name: 'offset', options: { offset: [0, 2] } }] } }}\n >\n <IconButton\n ref={ref}\n color=\"white\"\n size={size ?? 'medium'}\n aria-label={title}\n sx={\n size\n ? sx\n : sxOverride(\n (theme) => ({\n // sizes of size=\"small\" on mobile, a media query instead of the prop keeps it right on the server\n [theme.breakpoints.down('md')]: {\n width: '32px',\n height: '32px',\n '& .MuiSvgIcon-root': {\n fontSize: '17px',\n },\n },\n }),\n sx,\n )\n }\n {...rest}\n />\n </Tooltip>\n ),\n);\n"],"names":["VideoPlayerIconButton","forwardRef","title","size","sx","rest","ref","_jsx","Tooltip","placement","slotProps","popper","modifiers","name","options","offset","IconButton","color","sxOverride","theme","breakpoints","down","width","height","fontSize"],"mappings":"gJAkBO,MAAMA,EAA8DC,EAAAA,WACzE,EAAGC,QAAOC,OAAMC,QAAOC,GAAQC,IAC7BC,EAAAA,IAACC,EAAAA,QAAO,CACNN,MAAOA,EACPO,UAAU,0BAEQ,OAElBC,UAAW,CAAEC,OAAQ,CAAEC,UAAW,CAAC,CAAEC,KAAM,SAAUC,QAAS,CAAEC,OAAQ,CAAC,EAAG,iBAE5ER,EAAAA,IAACS,EAAAA,YACCV,IAAKA,EACLW,MAAM,QACNd,KAAMA,GAAQ,sBACFD,EACZE,GACED,EACIC,EACAc,EAAAA,WACGC,IAAK,CAEJ,CAACA,EAAMC,YAAYC,KAAK,OAAQ,CAC9BC,MAAO,OACPC,OAAQ,OACR,qBAAsB,CACpBC,SAAU,WAIhBpB,MAGJC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React, { ComponentType, type RefObject } from 'react';
|
|
2
|
+
import { SvgIconProps } from '@mui/material';
|
|
3
|
+
import { VideoPlayerIconButtonProps } from './VideoPlayerIconButton';
|
|
4
|
+
export interface VideoPlayerPlayPauseButtonProps extends Omit<VideoPlayerIconButtonProps, 'title'> {
|
|
5
|
+
/**
|
|
6
|
+
* Ref to the controlled <video> element
|
|
7
|
+
*/
|
|
8
|
+
videoRef: RefObject<HTMLVideoElement>;
|
|
9
|
+
/**
|
|
10
|
+
* Custom component for the icon shown while the video is paused
|
|
11
|
+
*/
|
|
12
|
+
PlayIconComponent?: ComponentType<SvgIconProps>;
|
|
13
|
+
/**
|
|
14
|
+
* Custom component for the icon shown while the video is playing
|
|
15
|
+
*/
|
|
16
|
+
PauseIconComponent?: ComponentType<SvgIconProps>;
|
|
17
|
+
/**
|
|
18
|
+
* Tooltip and aria-label while the video is paused
|
|
19
|
+
* @default Play video
|
|
20
|
+
*/
|
|
21
|
+
playTitle?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Tooltip and aria-label while the video is playing
|
|
24
|
+
* @default Pause video
|
|
25
|
+
*/
|
|
26
|
+
pauseTitle?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Button that toggles play/pause of the video
|
|
30
|
+
*/
|
|
31
|
+
export declare const VideoPlayerPlayPauseButton: React.FC<VideoPlayerPlayPauseButtonProps>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),o=require("react"),r=require("../../utils/sxOverride/sxOverride.js"),i=require("../icon/stroke/ControlsPlayIcon.js"),s=require("../icon/stroke/ControlsPauseIcon.js"),a=require("../video/useVideoPlayPause.js"),t=require("./VideoPlayerIconButton.js");const n=o.forwardRef(({videoRef:o,PlayIconComponent:n,PauseIconComponent:l,playTitle:u="Play video",pauseTitle:P="Pause video",onClick:d,sx:c,...y},x)=>{const{isPlaying:I,handlePlayPause:p}=a.useVideoPlayPause(o),v=I?l??s.ControlsPauseIcon:n??i.ControlsPlayIcon;return e.jsx(t.VideoPlayerIconButton,{ref:x,"data-testid":"VideoPlayerPlayPause",title:I?P:u,onClick:e=>{d?.(e),p()},sx:r.sxOverride(e=>({[e.breakpoints.down("md")]:{width:"52px",height:"52px","& .MuiSvgIcon-root":{fontSize:"32px"}}}),c),...y,children:e.jsx(v,{"data-testid":I?"VideoPlayerPauseIcon":"VideoPlayerPlayIcon"})})});exports.VideoPlayerPlayPauseButton=n;
|
|
2
|
+
//# sourceMappingURL=VideoPlayerPlayPauseButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoPlayerPlayPauseButton.js","sources":["../../../../../src/components/videoPlayer/VideoPlayerPlayPauseButton.tsx"],"sourcesContent":["import React, { ComponentType, forwardRef, type MouseEvent, type RefObject } from 'react';\nimport { SvgIconProps } from '@mui/material';\nimport { sxOverride } from '@/src/utils/sxOverride';\nimport { ControlsPlayIcon } from '@/src/components/icon/stroke/ControlsPlayIcon';\nimport { ControlsPauseIcon } from '@/src/components/icon/stroke/ControlsPauseIcon';\nimport { useVideoPlayPause } from '@/src/components/video/useVideoPlayPause';\nimport { VideoPlayerIconButton, VideoPlayerIconButtonProps } from './VideoPlayerIconButton';\n\nexport interface VideoPlayerPlayPauseButtonProps extends Omit<VideoPlayerIconButtonProps, 'title'> {\n /**\n * Ref to the controlled <video> element\n */\n videoRef: RefObject<HTMLVideoElement>;\n /**\n * Custom component for the icon shown while the video is paused\n */\n PlayIconComponent?: ComponentType<SvgIconProps>;\n /**\n * Custom component for the icon shown while the video is playing\n */\n PauseIconComponent?: ComponentType<SvgIconProps>;\n /**\n * Tooltip and aria-label while the video is paused\n * @default Play video\n */\n playTitle?: string;\n /**\n * Tooltip and aria-label while the video is playing\n * @default Pause video\n */\n pauseTitle?: string;\n}\n\n/**\n * Button that toggles play/pause of the video\n */\nexport const VideoPlayerPlayPauseButton: React.FC<VideoPlayerPlayPauseButtonProps> = forwardRef(\n (\n {\n videoRef,\n PlayIconComponent,\n PauseIconComponent,\n playTitle = 'Play video',\n pauseTitle = 'Pause video',\n onClick,\n sx,\n ...rest\n },\n ref,\n ) => {\n const { isPlaying, handlePlayPause } = useVideoPlayPause(videoRef);\n const Icon = isPlaying ? (PauseIconComponent ?? ControlsPauseIcon) : (PlayIconComponent ?? ControlsPlayIcon);\n\n return (\n <VideoPlayerIconButton\n ref={ref}\n data-testid=\"VideoPlayerPlayPause\"\n title={isPlaying ? pauseTitle : playTitle}\n onClick={(event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n handlePlayPause();\n }}\n sx={sxOverride(\n (theme) => ({\n // larger button on mobile, where it is centered over the video\n [theme.breakpoints.down('md')]: {\n width: '52px',\n height: '52px',\n '& .MuiSvgIcon-root': {\n fontSize: '32px',\n },\n },\n }),\n sx,\n )}\n {...rest}\n >\n <Icon data-testid={isPlaying ? 'VideoPlayerPauseIcon' : 'VideoPlayerPlayIcon'} />\n </VideoPlayerIconButton>\n );\n },\n);\n"],"names":["VideoPlayerPlayPauseButton","forwardRef","videoRef","PlayIconComponent","PauseIconComponent","playTitle","pauseTitle","onClick","sx","rest","ref","isPlaying","handlePlayPause","useVideoPlayPause","Icon","ControlsPauseIcon","ControlsPlayIcon","_jsx","VideoPlayerIconButton","title","event","sxOverride","theme","breakpoints","down","width","height","fontSize","children"],"mappings":"ySAoCO,MAAMA,EAAwEC,EAAAA,WACnF,EAEIC,WACAC,oBACAC,qBACAC,YAAY,aACZC,aAAa,cACbC,UACAC,QACGC,GAELC,KAEA,MAAMC,UAAEA,EAASC,gBAAEA,GAAoBC,EAAAA,kBAAkBX,GACnDY,EAAOH,EAAaP,GAAsBW,EAAAA,kBAAsBZ,GAAqBa,EAAAA,iBAE3F,OACEC,EAAAA,IAACC,EAAAA,sBAAqB,CACpBR,IAAKA,EAAG,cACI,uBACZS,MAAOR,EAAYL,EAAaD,EAChCE,QAAUa,IACRb,IAAUa,GACVR,KAEFJ,GAAIa,aACDC,IAAK,CAEJ,CAACA,EAAMC,YAAYC,KAAK,OAAQ,CAC9BC,MAAO,OACPC,OAAQ,OACR,qBAAsB,CACpBC,SAAU,WAIhBnB,MAEEC,EAAImB,SAERX,EAAAA,IAACH,EAAI,CAAA,cAAcH,EAAY,uBAAyB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React, { ComponentType } from 'react';
|
|
2
|
+
import { SvgIconProps } from '@mui/material';
|
|
3
|
+
import { VideoPlayerIconButtonProps } from './VideoPlayerIconButton';
|
|
4
|
+
export interface VideoPlayerSkipButtonProps extends Omit<VideoPlayerIconButtonProps, 'title'> {
|
|
5
|
+
/**
|
|
6
|
+
* Tooltip and aria-label of the button
|
|
7
|
+
* @default Previous video or Next video based on direction
|
|
8
|
+
*/
|
|
9
|
+
title?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Direction of the skip
|
|
12
|
+
* @default next
|
|
13
|
+
*/
|
|
14
|
+
direction?: 'previous' | 'next';
|
|
15
|
+
/**
|
|
16
|
+
* Custom component to be used for the icon
|
|
17
|
+
*/
|
|
18
|
+
IconComponent?: ComponentType<SvgIconProps>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Button that skips to the previous or next video
|
|
22
|
+
*/
|
|
23
|
+
export declare const VideoPlayerSkipButton: React.FC<VideoPlayerSkipButtonProps>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),o=require("react"),r=require("../icon/stroke/ControlsSkipPreviousIcon.js"),i=require("../icon/stroke/ControlsSkipNextIcon.js"),t=require("./VideoPlayerIconButton.js");const n=o.forwardRef(({direction:o="next",IconComponent:n,title:s,...c},u)=>{const d="previous"===o,a=n??(d?r.ControlsSkipPreviousIcon:i.ControlsSkipNextIcon);return e.jsx(t.VideoPlayerIconButton,{ref:u,"data-testid":d?"VideoPlayerSkipPrevious":"VideoPlayerSkipNext",title:s??(d?"Previous video":"Next video"),...c,children:e.jsx(a,{"data-testid":d?"VideoPlayerSkipPreviousIcon":"VideoPlayerSkipNextIcon"})})});exports.VideoPlayerSkipButton=n;
|
|
2
|
+
//# sourceMappingURL=VideoPlayerSkipButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoPlayerSkipButton.js","sources":["../../../../../src/components/videoPlayer/VideoPlayerSkipButton.tsx"],"sourcesContent":["import React, { ComponentType, forwardRef } from 'react';\nimport { SvgIconProps } from '@mui/material';\nimport { ControlsSkipPreviousIcon } from '@/src/components/icon/stroke/ControlsSkipPreviousIcon';\nimport { ControlsSkipNextIcon } from '@/src/components/icon/stroke/ControlsSkipNextIcon';\nimport { VideoPlayerIconButton, VideoPlayerIconButtonProps } from './VideoPlayerIconButton';\n\nexport interface VideoPlayerSkipButtonProps extends Omit<VideoPlayerIconButtonProps, 'title'> {\n /**\n * Tooltip and aria-label of the button\n * @default Previous video or Next video based on direction\n */\n title?: string;\n /**\n * Direction of the skip\n * @default next\n */\n direction?: 'previous' | 'next';\n /**\n * Custom component to be used for the icon\n */\n IconComponent?: ComponentType<SvgIconProps>;\n}\n\n/**\n * Button that skips to the previous or next video\n */\nexport const VideoPlayerSkipButton: React.FC<VideoPlayerSkipButtonProps> = forwardRef(\n ({ direction = 'next', IconComponent, title, ...rest }, ref) => {\n const isPrevious = direction === 'previous';\n const Icon = IconComponent ?? (isPrevious ? ControlsSkipPreviousIcon : ControlsSkipNextIcon);\n\n return (\n <VideoPlayerIconButton\n ref={ref}\n data-testid={isPrevious ? 'VideoPlayerSkipPrevious' : 'VideoPlayerSkipNext'}\n title={title ?? (isPrevious ? 'Previous video' : 'Next video')}\n {...rest}\n >\n <Icon data-testid={isPrevious ? 'VideoPlayerSkipPreviousIcon' : 'VideoPlayerSkipNextIcon'} />\n </VideoPlayerIconButton>\n );\n },\n);\n"],"names":["VideoPlayerSkipButton","forwardRef","direction","IconComponent","title","rest","ref","isPrevious","Icon","ControlsSkipPreviousIcon","ControlsSkipNextIcon","_jsx","VideoPlayerIconButton","children"],"mappings":"6NA0BaA,EAA8DC,EAAAA,WACzE,EAAGC,YAAY,OAAQC,gBAAeC,WAAUC,GAAQC,KACtD,MAAMC,EAA2B,aAAdL,EACbM,EAAOL,IAAkBI,EAAaE,EAAAA,yBAA2BC,EAAAA,sBAEvE,OACEC,MAACC,EAAAA,sBAAqB,CACpBN,IAAKA,EAAG,cACKC,EAAa,0BAA4B,sBACtDH,MAAOA,IAAUG,EAAa,iBAAmB,iBAC7CF,EAAIQ,SAERF,EAAAA,IAACH,EAAI,CAAA,cAAcD,EAAa,8BAAgC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React, { type RefObject } from 'react';
|
|
2
|
+
import { SliderProps } from '@mui/material';
|
|
3
|
+
export interface VideoPlayerSliderProps extends SliderProps {
|
|
4
|
+
/**
|
|
5
|
+
* Ref to the controlled <video> element
|
|
6
|
+
*/
|
|
7
|
+
videoRef: RefObject<HTMLVideoElement>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Seek slider showing the playback progress of the video
|
|
11
|
+
*/
|
|
12
|
+
export declare const VideoPlayerSlider: React.FC<VideoPlayerSliderProps>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),r=require("react"),i=require("@mui/material"),a=require("../../utils/sxOverride/sxOverride.js"),o=require("../video/useVideoTime.js"),d=require("../video/useVideoPlayPause.js"),s=require("./formatVideoTime.js");const t=r.forwardRef(({videoRef:r,onChange:t,onKeyDown:u,sx:l,...n},m)=>{const{currentTime:x,duration:p,handleSeek:c}=o.useVideoTime(r,{smooth:!0}),{handlePlayPause:h}=d.useVideoPlayPause(r);return e.jsx(i.Slider,{ref:m,"data-testid":"VideoPlayerSeekSlider",value:x,min:0,max:p||0,valueLabelDisplay:"off",onChange:(e,r,i)=>{t?.(e,r,i),c(r)},"aria-label":"Seek video",getAriaValueText:e=>s.formatVideoTime(e,p),onKeyDown:e=>{u?.(e)," "===e.key&&(e.preventDefault(),h())},sx:a.sxOverride({height:"4px",py:"6px","@media (pointer: coarse)":{py:"6px"},"& .MuiSlider-rail":{color:"common.white",borderRadius:"16px"},"& .MuiSlider-track":{color:"brand.secondary",borderRadius:"16px",transition:"none"},"& .MuiSlider-thumb":{width:"16px",height:"16px",borderRadius:"50%"}},l),...n})});exports.VideoPlayerSlider=t;
|
|
2
|
+
//# sourceMappingURL=VideoPlayerSlider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoPlayerSlider.js","sources":["../../../../../src/components/videoPlayer/VideoPlayerSlider.tsx"],"sourcesContent":["import React, { forwardRef, type KeyboardEvent, type RefObject } from 'react';\nimport { Slider, SliderProps } from '@mui/material';\nimport { sxOverride } from '@/src/utils/sxOverride';\nimport { useVideoTime } from '@/src/components/video/useVideoTime';\nimport { useVideoPlayPause } from '@/src/components/video/useVideoPlayPause';\nimport { formatVideoTime } from './formatVideoTime';\n\nexport interface VideoPlayerSliderProps extends SliderProps {\n /**\n * Ref to the controlled <video> element\n */\n videoRef: RefObject<HTMLVideoElement>;\n}\n\n/**\n * Seek slider showing the playback progress of the video\n */\nexport const VideoPlayerSlider: React.FC<VideoPlayerSliderProps> = forwardRef(\n ({ videoRef, onChange, onKeyDown, sx, ...rest }, ref) => {\n const { currentTime, duration, handleSeek } = useVideoTime(videoRef, { smooth: true });\n const { handlePlayPause } = useVideoPlayPause(videoRef);\n\n const handleKeyDown = (event: KeyboardEvent<HTMLSpanElement>) => {\n onKeyDown?.(event);\n // space toggles playback like on the play button, keyboard users don't have to leave the slider\n if (event.key === ' ') {\n event.preventDefault();\n handlePlayPause();\n }\n };\n\n return (\n <Slider\n ref={ref}\n data-testid=\"VideoPlayerSeekSlider\"\n value={currentTime}\n min={0}\n max={duration || 0}\n valueLabelDisplay=\"off\"\n onChange={(event, newValue: number, activeThumb) => {\n onChange?.(event, newValue, activeThumb);\n handleSeek(newValue);\n }}\n aria-label=\"Seek video\"\n getAriaValueText={(value) => formatVideoTime(value, duration)}\n onKeyDown={handleKeyDown}\n sx={sxOverride(\n {\n height: '4px',\n // symmetric padding keeps the track centered and serves as its hitbox,\n // Figma sits the track directly on the toolbar, the space keeps icon buttons focus ring from overlapping the slider\n py: '6px',\n // the theme sets its padding again for touch devices, the plain py would lose to it there\n '@media (pointer: coarse)': {\n py: '6px',\n },\n '& .MuiSlider-rail': {\n color: 'common.white',\n borderRadius: '16px',\n },\n '& .MuiSlider-track': {\n color: 'brand.secondary',\n borderRadius: '16px',\n // updated every frame, the default width transition would only make it lag behind\n transition: 'none',\n },\n '& .MuiSlider-thumb': {\n width: '16px',\n height: '16px',\n borderRadius: '50%',\n },\n },\n sx,\n )}\n {...rest}\n />\n );\n },\n);\n"],"names":["VideoPlayerSlider","forwardRef","videoRef","onChange","onKeyDown","sx","rest","ref","currentTime","duration","handleSeek","useVideoTime","smooth","handlePlayPause","useVideoPlayPause","_jsx","Slider","value","min","max","valueLabelDisplay","event","newValue","activeThumb","getAriaValueText","formatVideoTime","key","preventDefault","sxOverride","height","py","color","borderRadius","transition","width"],"mappings":"yQAiBaA,EAAsDC,EAAAA,WACjE,EAAGC,WAAUC,WAAUC,YAAWC,QAAOC,GAAQC,KAC/C,MAAMC,YAAEA,EAAWC,SAAEA,EAAQC,WAAEA,GAAeC,EAAAA,aAAaT,EAAU,CAAEU,QAAQ,KACzEC,gBAAEA,GAAoBC,EAAAA,kBAAkBZ,GAW9C,OACEa,EAAAA,IAACC,SAAM,CACLT,IAAKA,EAAG,cACI,wBACZU,MAAOT,EACPU,IAAK,EACLC,IAAKV,GAAY,EACjBW,kBAAkB,MAClBjB,SAAU,CAACkB,EAAOC,EAAkBC,KAClCpB,IAAWkB,EAAOC,EAAUC,GAC5Bb,EAAWY,IACZ,aACU,aACXE,iBAAmBP,GAAUQ,EAAAA,gBAAgBR,EAAOR,GACpDL,UAvBmBiB,IACrBjB,IAAYiB,GAEM,MAAdA,EAAMK,MACRL,EAAMM,iBACNd,MAmBAR,GAAIuB,EAAAA,WACF,CACEC,OAAQ,MAGRC,GAAI,MAEJ,2BAA4B,CAC1BA,GAAI,OAEN,oBAAqB,CACnBC,MAAO,eACPC,aAAc,QAEhB,qBAAsB,CACpBD,MAAO,kBACPC,aAAc,OAEdC,WAAY,QAEd,qBAAsB,CACpBC,MAAO,OACPL,OAAQ,OACRG,aAAc,QAGlB3B,MAEEC"}
|