@cntrl-site/sdk-nextjs 0.13.0 → 0.14.1

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.
Binary file
@@ -14,27 +14,19 @@ const useItemAngle_1 = require("../useItemAngle");
14
14
  const sdk_1 = require("@cntrl-site/sdk");
15
15
  const useCntrlContext_1 = require("../../provider/useCntrlContext");
16
16
  const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
17
- const useCurrentLayout_1 = require("../../common/useCurrentLayout");
18
17
  const VimeoEmbedItem = ({ item, sectionId }) => {
19
18
  const id = (0, react_1.useId)();
20
19
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
21
- const layout = (0, useCurrentLayout_1.useCurrentLayout)();
22
20
  const { radius } = (0, useEmbedVideoItem_1.useEmbedVideoItem)(item, sectionId);
23
21
  const [iframeRef, setIframeRef] = (0, react_1.useState)(null);
24
22
  const vimeoPlayer = (0, react_1.useMemo)(() => iframeRef ? new player_1.default(iframeRef) : undefined, [iframeRef]);
25
- const isAutoPlayOnHover = (0, react_1.useMemo)(() => {
26
- const layoutHoverStates = item.state.hover[layout];
27
- if (!layoutHoverStates)
28
- return false;
29
- return layoutHoverStates.autoplay === true;
30
- }, [layout, item]);
31
23
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
32
- const { autoplay, controls, loop, muted, pictureInPicture, url } = item.commonParams;
24
+ const { play, controls, loop, muted, pictureInPicture, url } = item.commonParams;
33
25
  const getValidVimeoUrl = (url) => {
34
26
  const validURL = new URL(url);
35
27
  validURL.searchParams.append('controls', String(controls));
36
- validURL.searchParams.append('autoplay', String(autoplay));
37
- validURL.searchParams.append('muted', String(muted));
28
+ validURL.searchParams.append('autoplay', String(play === 'auto'));
29
+ validURL.searchParams.append('muted', String(play !== 'on-click' ? true : muted));
38
30
  validURL.searchParams.append('loop', String(loop));
39
31
  validURL.searchParams.append('pip', String(pictureInPicture));
40
32
  validURL.searchParams.append('title', '0');
@@ -48,11 +40,11 @@ const VimeoEmbedItem = ({ item, sectionId }) => {
48
40
  borderRadius: `${radius * 100}vw`,
49
41
  transform: `rotate(${angle}deg)`
50
42
  }, onMouseEnter: () => {
51
- if (!vimeoPlayer || !isAutoPlayOnHover)
43
+ if (!vimeoPlayer || play !== 'on-hover')
52
44
  return;
53
45
  vimeoPlayer.play();
54
46
  }, onMouseLeave: () => {
55
- if (!vimeoPlayer || !isAutoPlayOnHover)
47
+ if (!vimeoPlayer || play !== 'on-hover')
56
48
  return;
57
49
  vimeoPlayer.pause();
58
50
  }, children: (0, jsx_runtime_1.jsx)("iframe", { ref: setIframeRef, className: "embedVideo", src: validUrl || '', allow: "autoplay; fullscreen; picture-in-picture;", allowFullScreen: true }) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
@@ -14,33 +14,64 @@ const useItemAngle_1 = require("../useItemAngle");
14
14
  const sdk_1 = require("@cntrl-site/sdk");
15
15
  const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
16
16
  const useCntrlContext_1 = require("../../provider/useCntrlContext");
17
+ const useYouTubeIframeApi_1 = require("../../utils/Youtube/useYouTubeIframeApi");
17
18
  const YoutubeEmbedItem = ({ item, sectionId }) => {
18
19
  const id = (0, react_1.useId)();
19
20
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
20
- const { autoplay, controls, url } = item.commonParams;
21
+ const { play, controls, url } = item.commonParams;
21
22
  const { radius } = (0, useEmbedVideoItem_1.useEmbedVideoItem)(item, sectionId);
22
23
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
23
- const getValidYoutubeUrl = (url) => {
24
+ const YT = (0, useYouTubeIframeApi_1.useYouTubeIframeApi)();
25
+ const [div, setDiv] = (0, react_1.useState)(null);
26
+ const [player, setPlayer] = (0, react_1.useState)(undefined);
27
+ (0, react_1.useEffect)(() => {
24
28
  const newUrl = new URL(url);
25
- const id = (0, getValidYoutubeUrl_1.getYoutubeId)(newUrl);
26
- const validUrl = new URL(`https://www.youtube.com/embed/${id}`);
27
- validUrl.searchParams.append('controls', `${Number(controls)}`);
28
- validUrl.searchParams.append('autoplay', `${Number(autoplay)}`);
29
- validUrl.searchParams.append('mute', `${Number(autoplay)}`);
30
- return validUrl.href;
31
- };
32
- const validUrl = getValidYoutubeUrl(url);
33
- return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: item.link?.url, children: [(0, jsx_runtime_1.jsx)("div", { className: `embed-youtube-video-wrapper-${item.id}`, style: {
29
+ const videoId = (0, getValidYoutubeUrl_1.getYoutubeId)(newUrl);
30
+ if (!YT || !videoId || !div)
31
+ return;
32
+ const divRect = div.getBoundingClientRect();
33
+ const placeholder = document.createElement('div');
34
+ div.appendChild(placeholder);
35
+ const player = new YT.Player(placeholder, {
36
+ videoId,
37
+ playerVars: {
38
+ autoplay: play === 'auto' ? '1' : '0',
39
+ controls: controls ? '1' : '0'
40
+ },
41
+ events: {
42
+ onReady: (event) => {
43
+ setPlayer(event.target);
44
+ if (play !== 'on-click') {
45
+ player.mute();
46
+ }
47
+ }
48
+ }
49
+ });
50
+ return () => {
51
+ setPlayer(undefined);
52
+ player.destroy();
53
+ placeholder.parentElement?.removeChild(placeholder);
54
+ };
55
+ }, [YT, div]);
56
+ return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: item.link?.url, children: [(0, jsx_runtime_1.jsx)("div", { className: `embed-youtube-video-wrapper-${item.id}`, onMouseEnter: () => {
57
+ if (!player || play !== 'on-hover')
58
+ return;
59
+ player.playVideo();
60
+ }, onMouseLeave: () => {
61
+ if (!player || play !== 'on-hover')
62
+ return;
63
+ player.pauseVideo();
64
+ }, ref: setDiv, style: {
34
65
  borderRadius: `${radius * 100}vw`,
35
66
  transform: `rotate(${angle}deg)`
36
- }, children: (0, jsx_runtime_1.jsx)("iframe", { className: "embedYoutubeVideo", src: validUrl || '', allow: "accelerometer; autoplay; allowfullscreen;", allowFullScreen: true }) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
67
+ } }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
37
68
  .embed-youtube-video-wrapper-${item.id} {
38
69
  position: absolute;
39
70
  overflow: hidden;
40
71
  width: 100%;
41
72
  height: 100%;
42
73
  }
43
- .embedYoutubeVideo {
74
+ .embed-youtube-video-wrapper-${item.id} iframe {
44
75
  width: 100%;
45
76
  height: 100%;
46
77
  z-index: 1;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.captureStackTrace = exports.YouTubeIframeApiLoader = void 0;
4
+ const YT_IFRAME_API_URL = 'https://www.youtube.com/iframe_api';
5
+ class YouTubeIframeApiLoader {
6
+ static create() {
7
+ if (!YouTubeIframeApiLoader.instance) {
8
+ YouTubeIframeApiLoader.instance = new YouTubeIframeApiLoader();
9
+ }
10
+ return YouTubeIframeApiLoader.instance;
11
+ }
12
+ constructor() {
13
+ this.loadingPromise = this.loadApi();
14
+ }
15
+ async getApi() {
16
+ await this.loadingPromise;
17
+ if (!window.YT) {
18
+ throw new Error('YouTube IFrame API is not loaded');
19
+ }
20
+ return window.YT;
21
+ }
22
+ loadApi() {
23
+ return new Promise((resolve, reject) => {
24
+ const script = document.createElement('script');
25
+ script.src = YT_IFRAME_API_URL;
26
+ const loadError = new Error('YouTube IFrame API loading failed');
27
+ captureStackTrace(loadError);
28
+ script.addEventListener('abort', () => {
29
+ reject(loadError);
30
+ });
31
+ script.addEventListener('error', () => {
32
+ reject(loadError);
33
+ });
34
+ window.onYouTubeIframeAPIReady = () => {
35
+ window.onYouTubeIframeAPIReady = undefined;
36
+ resolve();
37
+ };
38
+ document.head.appendChild(script);
39
+ });
40
+ }
41
+ }
42
+ exports.YouTubeIframeApiLoader = YouTubeIframeApiLoader;
43
+ function captureStackTrace(error) {
44
+ if (typeof Error.captureStackTrace === 'function') {
45
+ Error.captureStackTrace(error);
46
+ return;
47
+ }
48
+ try {
49
+ throw error;
50
+ }
51
+ catch (errWithStack) { }
52
+ }
53
+ exports.captureStackTrace = captureStackTrace;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.YTPlayerState = void 0;
4
+ var YTPlayerState;
5
+ (function (YTPlayerState) {
6
+ YTPlayerState[YTPlayerState["Unstarted"] = -1] = "Unstarted";
7
+ YTPlayerState[YTPlayerState["Ended"] = 0] = "Ended";
8
+ YTPlayerState[YTPlayerState["Playing"] = 1] = "Playing";
9
+ YTPlayerState[YTPlayerState["Paused"] = 2] = "Paused";
10
+ YTPlayerState[YTPlayerState["Buffering"] = 3] = "Buffering";
11
+ YTPlayerState[YTPlayerState["VideoCued"] = 5] = "VideoCued";
12
+ })(YTPlayerState = exports.YTPlayerState || (exports.YTPlayerState = {}));
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useYouTubeIframeApi = void 0;
4
+ const react_1 = require("react");
5
+ const YouTubeIframeApiLoader_1 = require("./YouTubeIframeApiLoader");
6
+ function useYouTubeIframeApi() {
7
+ const [YT, setYT] = (0, react_1.useState)(undefined);
8
+ (0, react_1.useEffect)(() => {
9
+ const loader = YouTubeIframeApiLoader_1.YouTubeIframeApiLoader.create();
10
+ loader.getApi().then((YT) => setYT(YT));
11
+ }, []);
12
+ return YT;
13
+ }
14
+ exports.useYouTubeIframeApi = useYouTubeIframeApi;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "SDK for Next.js",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/index.ts",
@@ -21,7 +21,8 @@
21
21
  },
22
22
  "homepage": "https://github.com/cntrl-site/sdk-nextjs#readme",
23
23
  "dependencies": {
24
- "@cntrl-site/sdk": "^1.1.6",
24
+ "@types/vimeo__player": "^2.18.0",
25
+ "@cntrl-site/sdk": "^1.1.8",
25
26
  "@vimeo/player": "^2.20.1",
26
27
  "html-react-parser": "^3.0.1",
27
28
  "styled-jsx": "^5.0.2"
@@ -39,7 +40,6 @@
39
40
  "@types/jest": "^29.2.5",
40
41
  "@types/node": "^18.11.7",
41
42
  "@types/react": "^18.0.15",
42
- "@types/vimeo__player": "^2.18.0",
43
43
  "jest": "^29.3.1",
44
44
  "ts-jest": "^29.0.3",
45
45
  "typescript": "^4.9.5"
@@ -14,22 +14,16 @@ import { useCurrentLayout } from '../../common/useCurrentLayout';
14
14
  export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId }) => {
15
15
  const id = useId();
16
16
  const { layouts } = useCntrlContext();
17
- const layout = useCurrentLayout()
18
17
  const { radius } = useEmbedVideoItem(item, sectionId);
19
18
  const [iframeRef, setIframeRef] = useState<HTMLIFrameElement | null>(null);
20
19
  const vimeoPlayer = useMemo(() => iframeRef ? new Player(iframeRef) : undefined, [iframeRef]);
21
- const isAutoPlayOnHover = useMemo(() => {
22
- const layoutHoverStates = item.state.hover[layout];
23
- if (!layoutHoverStates) return false;
24
- return layoutHoverStates.autoplay === true;
25
- }, [layout, item]);
26
20
  const angle = useItemAngle(item, sectionId);
27
- const { autoplay, controls, loop, muted, pictureInPicture, url } = item.commonParams;
21
+ const { play, controls, loop, muted, pictureInPicture, url } = item.commonParams;
28
22
  const getValidVimeoUrl = (url: string): string => {
29
23
  const validURL = new URL(url);
30
24
  validURL.searchParams.append('controls', String(controls));
31
- validURL.searchParams.append('autoplay', String(autoplay));
32
- validURL.searchParams.append('muted', String(muted));
25
+ validURL.searchParams.append('autoplay', String(play === 'auto'));
26
+ validURL.searchParams.append('muted', String(play !== 'on-click' ? true : muted));
33
27
  validURL.searchParams.append('loop', String(loop));
34
28
  validURL.searchParams.append('pip', String(pictureInPicture));
35
29
  validURL.searchParams.append('title', '0');
@@ -49,11 +43,11 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
49
43
  transform: `rotate(${angle}deg)`
50
44
  }}
51
45
  onMouseEnter={() => {
52
- if (!vimeoPlayer || !isAutoPlayOnHover) return;
46
+ if (!vimeoPlayer || play !== 'on-hover') return;
53
47
  vimeoPlayer.play();
54
48
  }}
55
49
  onMouseLeave={() =>{
56
- if (!vimeoPlayer || !isAutoPlayOnHover) return;
50
+ if (!vimeoPlayer || play !== 'on-hover') return;
57
51
  vimeoPlayer.pause();
58
52
  }}
59
53
  >
@@ -1,4 +1,4 @@
1
- import { FC, useId } from 'react';
1
+ import { FC, useEffect, useId, useState } from 'react';
2
2
  import JSXStyle from 'styled-jsx/style';
3
3
  import { TYoutubeEmbedItem } from '@cntrl-site/core';
4
4
  import { ItemProps } from '../Item';
@@ -9,41 +9,65 @@ import { useItemAngle } from '../useItemAngle';
9
9
  import { ArticleItemType, getLayoutStyles } from '@cntrl-site/sdk';
10
10
  import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
11
11
  import { useCntrlContext } from '../../provider/useCntrlContext';
12
+ import { useYouTubeIframeApi } from '../../utils/Youtube/useYouTubeIframeApi';
13
+ import { YTPlayer } from '../../utils/Youtube/YoutubeIframeApi';
12
14
 
13
15
  export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, sectionId }) => {
14
16
  const id = useId();
15
17
  const { layouts } = useCntrlContext();
16
- const { autoplay, controls, url } = item.commonParams;
18
+ const { play, controls, url } = item.commonParams;
17
19
  const { radius } = useEmbedVideoItem(item, sectionId);
18
20
  const angle = useItemAngle(item, sectionId);
21
+ const YT = useYouTubeIframeApi();
22
+ const [div, setDiv] = useState<HTMLDivElement | null>(null);
23
+ const [player, setPlayer] = useState<YTPlayer | undefined>(undefined);
19
24
 
20
- const getValidYoutubeUrl = (url: string): string => {
25
+ useEffect(() => {
21
26
  const newUrl = new URL(url);
22
- const id = getYoutubeId(newUrl);
23
- const validUrl = new URL(`https://www.youtube.com/embed/${id}`);
24
- validUrl.searchParams.append('controls', `${ Number(controls) }`);
25
- validUrl.searchParams.append('autoplay', `${ Number(autoplay) }`);
26
- validUrl.searchParams.append('mute', `${ Number(autoplay) }`);
27
-
28
- return validUrl.href;
29
- }
30
- const validUrl = getValidYoutubeUrl(url);
27
+ const videoId = getYoutubeId(newUrl);
28
+ if (!YT || !videoId || !div) return;
29
+ const divRect = div.getBoundingClientRect();
30
+ const placeholder = document.createElement('div');
31
+ div.appendChild(placeholder);
32
+ const player = new YT.Player(placeholder, {
33
+ videoId,
34
+ playerVars: {
35
+ autoplay: play === 'auto' ? '1' : '0',
36
+ controls: controls ? '1' : '0'
37
+ },
38
+ events: {
39
+ onReady: (event) => {
40
+ setPlayer(event.target);
41
+ if (play !== 'on-click') {
42
+ player.mute();
43
+ }
44
+ }
45
+ }
46
+ });
47
+ return () => {
48
+ setPlayer(undefined);
49
+ player.destroy();
50
+ placeholder.parentElement?.removeChild(placeholder);
51
+ };
52
+ }, [YT, div]);
31
53
 
32
54
  return (
33
55
  <LinkWrapper url={item.link?.url}>
34
56
  <div className={`embed-youtube-video-wrapper-${item.id}`}
57
+ onMouseEnter={() => {
58
+ if (!player || play !== 'on-hover') return;
59
+ player.playVideo();
60
+ }}
61
+ onMouseLeave={() => {
62
+ if (!player || play !== 'on-hover') return;
63
+ player.pauseVideo();
64
+ }}
65
+ ref={setDiv}
35
66
  style={{
36
67
  borderRadius: `${radius * 100}vw`,
37
68
  transform: `rotate(${angle}deg)`
38
69
  }}
39
- >
40
- <iframe
41
- className="embedYoutubeVideo"
42
- src={validUrl || ''}
43
- allow="accelerometer; autoplay; allowfullscreen;"
44
- allowFullScreen
45
- />
46
- </div>
70
+ ></div>
47
71
  <JSXStyle id={id}>{`
48
72
  .embed-youtube-video-wrapper-${item.id} {
49
73
  position: absolute;
@@ -51,7 +75,7 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
51
75
  width: 100%;
52
76
  height: 100%;
53
77
  }
54
- .embedYoutubeVideo {
78
+ .embed-youtube-video-wrapper-${item.id} iframe {
55
79
  width: 100%;
56
80
  height: 100%;
57
81
  z-index: 1;
@@ -0,0 +1,58 @@
1
+ import { YT } from './YoutubeIframeApi';
2
+
3
+ const YT_IFRAME_API_URL = 'https://www.youtube.com/iframe_api';
4
+
5
+ export class YouTubeIframeApiLoader {
6
+ private static instance: YouTubeIframeApiLoader | undefined;
7
+
8
+ public static create(): YouTubeIframeApiLoader {
9
+ if (!YouTubeIframeApiLoader.instance) {
10
+ YouTubeIframeApiLoader.instance = new YouTubeIframeApiLoader();
11
+ }
12
+ return YouTubeIframeApiLoader.instance;
13
+ }
14
+
15
+ private loadingPromise: Promise<void>;
16
+
17
+ private constructor() {
18
+ this.loadingPromise = this.loadApi();
19
+ }
20
+
21
+ async getApi(): Promise<YT> {
22
+ await this.loadingPromise;
23
+ if (!window.YT) {
24
+ throw new Error('YouTube IFrame API is not loaded');
25
+ }
26
+ return window.YT;
27
+ }
28
+
29
+ private loadApi(): Promise<void> {
30
+ return new Promise<void>((resolve, reject) => {
31
+ const script = document.createElement('script');
32
+ script.src = YT_IFRAME_API_URL;
33
+ const loadError = new Error('YouTube IFrame API loading failed');
34
+ captureStackTrace(loadError);
35
+ script.addEventListener('abort', () => {
36
+ reject(loadError);
37
+ });
38
+ script.addEventListener('error', () => {
39
+ reject(loadError);
40
+ });
41
+ window.onYouTubeIframeAPIReady = () => {
42
+ window.onYouTubeIframeAPIReady = undefined;
43
+ resolve();
44
+ };
45
+ document.head.appendChild(script);
46
+ });
47
+ }
48
+ }
49
+
50
+ export function captureStackTrace(error: Error): void {
51
+ if (typeof Error.captureStackTrace === 'function') {
52
+ Error.captureStackTrace(error);
53
+ return;
54
+ }
55
+ try {
56
+ throw error;
57
+ } catch (errWithStack) {}
58
+ }
@@ -0,0 +1,101 @@
1
+ export interface YT {
2
+ Player: YTPlayerConstructor;
3
+ }
4
+
5
+ export interface YTPlayerConstructor {
6
+ new(container: string | Element, params: YTPlayerParams): YTPlayer;
7
+ }
8
+
9
+ export interface YTEvent<Data> {
10
+ target: YTPlayer;
11
+ data: Data;
12
+ }
13
+
14
+ type YTBool = 0 | 1 | '0' | '1';
15
+
16
+ export interface YTPlayerParams {
17
+ width?: string | number;
18
+ height?: string | number;
19
+ videoId?: string;
20
+ playerVars?: Partial<{
21
+ autoplay: YTBool;
22
+ cc_lang_pref: string;
23
+ cc_load_policy: YTBool;
24
+ color: string;
25
+ controls: YTBool;
26
+ disablekb: YTBool;
27
+ enablejsapi: YTBool;
28
+ end: number | string;
29
+ fs: YTBool;
30
+ hl: string;
31
+ iv_load_policy: 1 | 3 | '1' | '3';
32
+ list: string;
33
+ listType: 'playlist' | 'user_uploads';
34
+ loop: YTBool;
35
+ origin: string;
36
+ playlist: string;
37
+ playsinline: YTBool;
38
+ rel: YTBool;
39
+ start: number | string;
40
+ widget_referrer: string;
41
+ }>;
42
+ events?: Partial<{
43
+ onReady: (event: YTEvent<void>) => void;
44
+ onStateChange: (event: YTEvent<YTPlayerState>) => void;
45
+ }>
46
+ }
47
+
48
+ export enum YTPlayerState {
49
+ Unstarted = -1,
50
+ Ended = 0,
51
+ Playing = 1,
52
+ Paused = 2,
53
+ Buffering = 3,
54
+ VideoCued = 5
55
+ }
56
+
57
+ export interface YTPlayer {
58
+ loadVideoById(videoId: string, startSeconds?: number): void;
59
+ loadVideoById(params: {
60
+ videoId: string;
61
+ startSeconds?: number;
62
+ endSeconds?: number;
63
+ }): void;
64
+
65
+ loadVideoByUrl(mediaContentUrl: string, startSeconds?: number): void;
66
+ loadVideoByUrl(params: {
67
+ mediaContentUrl: string;
68
+ startSeconds?: number;
69
+ endSeconds?: number;
70
+ }): void;
71
+
72
+ playVideo(): void;
73
+ pauseVideo(): void;
74
+ stopVideo(): void;
75
+ seekTo(seconds: number, allowSeekAhead: boolean): void;
76
+
77
+ mute(): void;
78
+ unMute(): void;
79
+ isMuted(): void;
80
+ setVolume(volume: number): void;
81
+ getVolume(): void;
82
+ setSize(width: number, height: number): void;
83
+ setLoop(loopPlaylists: boolean): void;
84
+
85
+ getVideoLoadedFraction(): number;
86
+ getPlayerState(): YTPlayerState;
87
+
88
+ getDuration(): number; // in seconds
89
+ getVideoUrl(): string;
90
+ getVideoEmbedCode(): string;
91
+
92
+ getIframe(): HTMLIFrameElement | undefined;
93
+ destroy(): void;
94
+ }
95
+
96
+ declare global {
97
+ interface Window {
98
+ YT?: YT;
99
+ onYouTubeIframeAPIReady?: () => void;
100
+ }
101
+ }
@@ -0,0 +1,12 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { YT } from './YoutubeIframeApi';
3
+ import { YouTubeIframeApiLoader } from './YouTubeIframeApiLoader';
4
+
5
+ export function useYouTubeIframeApi(): YT | undefined {
6
+ const [YT, setYT] = useState<YT | undefined>(undefined);
7
+ useEffect(() => {
8
+ const loader = YouTubeIframeApiLoader.create();
9
+ loader.getApi().then((YT) => setYT(YT));
10
+ }, []);
11
+ return YT;
12
+ }