@comet/site-nextjs 9.0.0-canary-20251209132220 → 9.0.0-canary-20251209140438

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/lib/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export { sitePreviewRoute } from './sitePreview/appRouter/sitePreviewRoute';
11
11
  export { legacyPagesRouterSitePreviewApiHandler } from './sitePreview/pagesRouter/legacyPagesRouterSitePreviewApiHandler';
12
12
  export { legacyPagesRouterPreviewParams, previewParams, type SitePreviewParams } from './sitePreview/previewUtils';
13
13
  export { SitePreviewProvider } from './sitePreview/SitePreviewProvider';
14
- export { AdminMessageType, type BlockLoader, type BlockLoaderDependencies, type BlockLoaderOptions, BlockPreviewProvider, BlocksBlock, calculateInheritAspectRatio, convertPreviewDataToHeaders, type CookieApi, CookieApiProvider, CookieSafe, createFetchInMemoryCache, createFetchWithPreviewHeaders, createGraphQLFetch, createPersistedQueryGraphQLFetch, DamFileDownloadLinkBlock, EmailLinkBlock, ErrorHandlerBoundary, ErrorHandlerProvider, ExternalLinkBlock, generateImageUrl, getMaxDimensionsFromArea, gql, type GraphQLFetch, hasRichTextBlockContent, type IAdminContentScopeMessage, type IAdminGraphQLApiUrlMessage, type IAdminHoverComponentMessage, type IAdminShowOnlyVisibleMessage, IFrameBridgeProvider, type IFrameHoverComponentMessage, type IFrameLocationMessage, type IFrameMessage, IFrameMessageType, type IFrameOpenLinkMessage, type IFrameSelectComponentMessage, type ImageDimensions, type IReadyIFrameMessage, isWithPreviewPropsData, ListBlock, OneOfBlock, OptionalBlock, parseAspectRatio, persistedQueryRoute, PhoneLinkBlock, Preview, type PreviewData, PreviewSkeleton, type PropsWithData, recursivelyLoadBlockData, sendSitePreviewIFrameMessage,
14
+ export { AdminMessageType, type BlockLoader, type BlockLoaderDependencies, type BlockLoaderOptions, BlockPreviewProvider, BlocksBlock, calculateInheritAspectRatio, convertPreviewDataToHeaders, type CookieApi, CookieApiProvider, CookieSafe, createFetchInMemoryCache, createFetchWithPreviewHeaders, createGraphQLFetch, createPersistedQueryGraphQLFetch, DamFileDownloadLinkBlock, EmailLinkBlock, ErrorHandlerBoundary, ErrorHandlerProvider, ExternalLinkBlock, generateImageUrl, getMaxDimensionsFromArea, gql, type GraphQLFetch, hasRichTextBlockContent, type IAdminContentScopeMessage, type IAdminGraphQLApiUrlMessage, type IAdminHoverComponentMessage, type IAdminShowOnlyVisibleMessage, IFrameBridgeProvider, type IFrameHoverComponentMessage, type IFrameLocationMessage, type IFrameMessage, IFrameMessageType, type IFrameOpenLinkMessage, type IFrameSelectComponentMessage, type ImageDimensions, type IReadyIFrameMessage, isWithPreviewPropsData, ListBlock, OneOfBlock, OptionalBlock, parseAspectRatio, persistedQueryRoute, PhoneLinkBlock, type PlayPauseButtonProps, Preview, type PreviewData, PreviewSkeleton, type PropsWithData, recursivelyLoadBlockData, sendSitePreviewIFrameMessage,
15
15
  /** @deprecated Use PreviewData instead. */
16
16
  type PreviewData as SitePreviewData, SitePreviewIFrameMessageType, type SupportedBlocks, SvgImageBlock, useBlockPreviewFetch, useCookieApi, useCookieBotCookieApi, useIFrameBridge, useIsElementInViewport, useLocalStorageCookieApi, useOneTrustCookieApi, usePreview, type VideoPreviewImageProps, webpackPersistedQueriesLoader, withPreview, type WithPreviewProps, } from '@comet/site-react';
17
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,oCAAoC,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC5G,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC5E,OAAO,EAAE,sCAAsC,EAAE,MAAM,kEAAkE,CAAC;AAC1H,OAAO,EAAE,8BAA8B,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACnH,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EACH,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,oBAAoB,EACpB,WAAW,EACX,2BAA2B,EAC3B,2BAA2B,EAC3B,KAAK,SAAS,EACd,iBAAiB,EACjB,UAAU,EACV,wBAAwB,EACxB,6BAA6B,EAC7B,kBAAkB,EAClB,gCAAgC,EAChC,wBAAwB,EACxB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,EACxB,GAAG,EACH,KAAK,YAAY,EACjB,uBAAuB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,sBAAsB,EACtB,SAAS,EACT,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,OAAO,EACP,KAAK,WAAW,EAChB,eAAe,EACf,KAAK,aAAa,EAClB,wBAAwB,EACxB,4BAA4B;AAC5B,2CAA2C;AAC3C,KAAK,WAAW,IAAI,eAAe,EACnC,4BAA4B,EAC5B,KAAK,eAAe,EACpB,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,eAAe,EACf,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,UAAU,EACV,KAAK,sBAAsB,EAC3B,6BAA6B,EAC7B,WAAW,EACX,KAAK,gBAAgB,GACxB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,oCAAoC,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC5G,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC5E,OAAO,EAAE,sCAAsC,EAAE,MAAM,kEAAkE,CAAC;AAC1H,OAAO,EAAE,8BAA8B,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACnH,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EACH,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,oBAAoB,EACpB,WAAW,EACX,2BAA2B,EAC3B,2BAA2B,EAC3B,KAAK,SAAS,EACd,iBAAiB,EACjB,UAAU,EACV,wBAAwB,EACxB,6BAA6B,EAC7B,kBAAkB,EAClB,gCAAgC,EAChC,wBAAwB,EACxB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,EACxB,GAAG,EACH,KAAK,YAAY,EACjB,uBAAuB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,sBAAsB,EACtB,SAAS,EACT,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,KAAK,oBAAoB,EACzB,OAAO,EACP,KAAK,WAAW,EAChB,eAAe,EACf,KAAK,aAAa,EAClB,wBAAwB,EACxB,4BAA4B;AAC5B,2CAA2C;AAC3C,KAAK,WAAW,IAAI,eAAe,EACnC,4BAA4B,EAC5B,KAAK,eAAe,EACpB,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,eAAe,EACf,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,UAAU,EACV,KAAK,sBAAsB,EAC3B,6BAA6B,EAC7B,WAAW,EACX,KAAK,gBAAgB,GACxB,MAAM,mBAAmB,CAAC"}
@@ -1,10 +1,11 @@
1
1
  "use client";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
3
  import clsx from "clsx";
4
- import { useState, useRef } from "react";
4
+ import { useState, useCallback } from "react";
5
5
  import { withPreview } from "../iframebridge/withPreview.js";
6
6
  import { PreviewSkeleton } from "../previewskeleton/PreviewSkeleton.js";
7
7
  import styles from "./DamVideoBlock.module.scss.js";
8
+ import { PlayPauseButton } from "./helpers/PlayPauseButton.js";
8
9
  import { useIsElementInViewport } from "./helpers/useIsElementInViewport.js";
9
10
  const DamVideoBlock = withPreview(
10
11
  ({
@@ -14,24 +15,45 @@ const DamVideoBlock = withPreview(
14
15
  renderPreviewImage,
15
16
  fill,
16
17
  previewImageIcon,
17
- playButtonAriaLabel
18
+ playButtonAriaLabel,
19
+ pauseButtonAriaLabel,
20
+ playPauseButton: PlayPauseButtonComponent
18
21
  }) => {
19
22
  var _a;
20
23
  if (damFile === void 0) {
21
24
  return /* @__PURE__ */ jsx(PreviewSkeleton, { type: "media", hasContent: false, aspectRatio });
22
25
  }
23
26
  const [showPreviewImage, setShowPreviewImage] = useState(true);
27
+ const [isPlaying, setIsPlaying] = useState(autoplay ?? false);
28
+ const [isHandledManually, setIsHandledManually] = useState(false);
24
29
  const hasPreviewImage = Boolean(previewImage && previewImage.damFile);
25
- const videoRef = useRef(null);
26
- useIsElementInViewport(videoRef, (inView) => {
27
- if (autoplay && videoRef.current) {
28
- if (inView) {
29
- videoRef.current.play();
30
- } else {
31
- videoRef.current.pause();
30
+ const [videoElement, setVideoElement] = useState(null);
31
+ const videoRef = setVideoElement;
32
+ const handleInView = useCallback(
33
+ (inView) => {
34
+ if (!isHandledManually && videoElement) {
35
+ if (inView && autoplay) {
36
+ videoElement.play();
37
+ setIsPlaying(true);
38
+ } else {
39
+ videoElement.pause();
40
+ setIsPlaying(false);
41
+ }
32
42
  }
43
+ },
44
+ [autoplay, isHandledManually, videoElement]
45
+ );
46
+ useIsElementInViewport({ current: videoElement }, handleInView);
47
+ const handlePlayPauseClick = () => {
48
+ if (isPlaying) {
49
+ setIsPlaying(false);
50
+ setIsHandledManually(true);
51
+ videoElement == null ? void 0 : videoElement.pause();
52
+ } else {
53
+ setIsPlaying(true);
54
+ videoElement == null ? void 0 : videoElement.play();
33
55
  }
34
- });
56
+ };
35
57
  return /* @__PURE__ */ jsx(Fragment, { children: hasPreviewImage && showPreviewImage ? renderPreviewImage({
36
58
  onPlay: () => setShowPreviewImage(false),
37
59
  image: previewImage,
@@ -40,25 +62,36 @@ const DamVideoBlock = withPreview(
40
62
  fill,
41
63
  icon: previewImageIcon,
42
64
  playButtonAriaLabel
43
- }) : /* @__PURE__ */ jsxs(
44
- "video",
45
- {
46
- autoPlay: autoplay || hasPreviewImage && !showPreviewImage,
47
- controls: showControls,
48
- loop,
49
- playsInline: true,
50
- muted: autoplay,
51
- ref: videoRef,
52
- className: clsx(styles.video, fill && styles.fill),
53
- style: !fill ? { "--aspect-ratio": aspectRatio.replace("x", " / ") } : void 0,
54
- children: [
55
- (_a = damFile.captions) == null ? void 0 : _a.map((caption) => {
56
- return /* @__PURE__ */ jsx("track", { src: caption.fileUrl, kind: "captions", srcLang: caption.language }, caption.id);
57
- }),
58
- /* @__PURE__ */ jsx("source", { src: damFile.fileUrl, type: damFile.mimetype })
59
- ]
60
- }
61
- ) });
65
+ }) : /* @__PURE__ */ jsxs("div", { className: styles.root, children: [
66
+ /* @__PURE__ */ jsxs(
67
+ "video",
68
+ {
69
+ autoPlay: autoplay || hasPreviewImage && !showPreviewImage,
70
+ controls: showControls,
71
+ loop,
72
+ playsInline: true,
73
+ muted: autoplay,
74
+ ref: videoRef,
75
+ className: clsx(styles.video, fill && styles.fill),
76
+ style: !fill ? { "--aspect-ratio": aspectRatio.replace("x", " / ") } : void 0,
77
+ children: [
78
+ (_a = damFile.captions) == null ? void 0 : _a.map((caption) => {
79
+ return /* @__PURE__ */ jsx("track", { src: caption.fileUrl, kind: "captions", srcLang: caption.language }, caption.id);
80
+ }),
81
+ /* @__PURE__ */ jsx("source", { src: damFile.fileUrl, type: damFile.mimetype })
82
+ ]
83
+ }
84
+ ),
85
+ !showControls && (PlayPauseButtonComponent ? /* @__PURE__ */ jsx(PlayPauseButtonComponent, { isPlaying, onClick: handlePlayPauseClick }) : /* @__PURE__ */ jsx(
86
+ PlayPauseButton,
87
+ {
88
+ isPlaying,
89
+ onClick: handlePlayPauseClick,
90
+ ariaLabelPlay: playButtonAriaLabel,
91
+ ariaLabelPause: pauseButtonAriaLabel
92
+ }
93
+ ))
94
+ ] }) });
62
95
  },
63
96
  { label: "Video" }
64
97
  );
@@ -1,11 +1,14 @@
1
- const video = "_video_17b8y_1";
2
- const fill = "_fill_17b8y_7";
1
+ const root = "_root_8r6od_1";
2
+ const video = "_video_8r6od_5";
3
+ const fill = "_fill_8r6od_11";
3
4
  const styles = {
5
+ root,
4
6
  video,
5
7
  fill
6
8
  };
7
9
  export {
8
10
  styles as default,
9
11
  fill,
12
+ root,
10
13
  video
11
14
  };
@@ -1,9 +1,10 @@
1
1
  "use client";
2
- import { jsx, Fragment } from "react/jsx-runtime";
2
+ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
3
  import clsx from "clsx";
4
- import { useState, useRef } from "react";
4
+ import { useState, useRef, useCallback } from "react";
5
5
  import { withPreview } from "../iframebridge/withPreview.js";
6
6
  import { PreviewSkeleton } from "../previewskeleton/PreviewSkeleton.js";
7
+ import { PlayPauseButton } from "./helpers/PlayPauseButton.js";
7
8
  import { useIsElementInViewport } from "./helpers/useIsElementInViewport.js";
8
9
  import styles from "./VimeoVideoBlock.module.scss.js";
9
10
  function parseVimeoIdentifier(vimeoIdentifier) {
@@ -26,20 +27,40 @@ const VimeoVideoBlock = withPreview(
26
27
  renderPreviewImage,
27
28
  fill,
28
29
  previewImageIcon,
29
- playButtonAriaLabel
30
+ playButtonAriaLabel,
31
+ pauseButtonAriaLabel,
32
+ playPauseButton: PlayPauseButtonComponent
30
33
  }) => {
31
34
  const [showPreviewImage, setShowPreviewImage] = useState(true);
32
35
  const hasPreviewImage = !!(previewImage && previewImage.damFile);
33
36
  const inViewRef = useRef(null);
34
- const iframeRef = useRef(null);
35
- const handleVisibilityChange = (isVisible) => {
36
- var _a, _b;
37
- (_b = (_a = iframeRef.current) == null ? void 0 : _a.contentWindow) == null ? void 0 : _b.postMessage(
38
- JSON.stringify({ method: isVisible && autoplay ? "play" : "pause" }),
39
- "https://player.vimeo.com"
40
- );
41
- };
42
- useIsElementInViewport(inViewRef, handleVisibilityChange);
37
+ const [iframeElement, setIframeElement] = useState(null);
38
+ const iframeRef = setIframeElement;
39
+ const [isPlaying, setIsPlaying] = useState(autoplay ?? false);
40
+ const [isHandledManually, setIsHandledManually] = useState(false);
41
+ const pauseVimeoVideo = useCallback(() => {
42
+ var _a;
43
+ (_a = iframeElement == null ? void 0 : iframeElement.contentWindow) == null ? void 0 : _a.postMessage(JSON.stringify({ method: "pause" }), "https://player.vimeo.com");
44
+ }, [iframeElement]);
45
+ const playVimeoVideo = useCallback(() => {
46
+ var _a;
47
+ (_a = iframeElement == null ? void 0 : iframeElement.contentWindow) == null ? void 0 : _a.postMessage(JSON.stringify({ method: "play" }), "https://player.vimeo.com");
48
+ }, [iframeElement]);
49
+ const handleInView = useCallback(
50
+ (isVisible) => {
51
+ if (!isHandledManually) {
52
+ if (isVisible && autoplay) {
53
+ playVimeoVideo();
54
+ setIsPlaying(true);
55
+ } else {
56
+ pauseVimeoVideo();
57
+ setIsPlaying(false);
58
+ }
59
+ }
60
+ },
61
+ [autoplay, isHandledManually, playVimeoVideo, pauseVimeoVideo]
62
+ );
63
+ useIsElementInViewport(inViewRef, handleInView);
43
64
  if (!vimeoIdentifier) {
44
65
  return /* @__PURE__ */ jsx(PreviewSkeleton, { type: "media", hasContent: false, aspectRatio });
45
66
  }
@@ -53,6 +74,16 @@ const VimeoVideoBlock = withPreview(
53
74
  const vimeoBaseUrl = "https://player.vimeo.com/video/";
54
75
  const vimeoUrl = new URL(`${vimeoBaseUrl}${identifier ?? ""}`);
55
76
  vimeoUrl.search = searchParams.toString();
77
+ const handlePlayPauseClick = () => {
78
+ if (isPlaying) {
79
+ setIsPlaying(false);
80
+ setIsHandledManually(true);
81
+ pauseVimeoVideo();
82
+ } else {
83
+ setIsPlaying(true);
84
+ playVimeoVideo();
85
+ }
86
+ };
56
87
  return /* @__PURE__ */ jsx(Fragment, { children: hasPreviewImage && showPreviewImage ? renderPreviewImage({
57
88
  onPlay: () => setShowPreviewImage(false),
58
89
  image: previewImage,
@@ -61,13 +92,24 @@ const VimeoVideoBlock = withPreview(
61
92
  fill,
62
93
  icon: previewImageIcon,
63
94
  playButtonAriaLabel
64
- }) : /* @__PURE__ */ jsx(
95
+ }) : /* @__PURE__ */ jsxs(
65
96
  "div",
66
97
  {
67
98
  ref: inViewRef,
68
99
  className: clsx(styles.videoContainer, fill && styles.fill),
69
100
  style: !fill ? { "--aspect-ratio": aspectRatio.replace("x", "/") } : void 0,
70
- children: /* @__PURE__ */ jsx("iframe", { ref: iframeRef, className: styles.vimeoContainer, src: vimeoUrl.toString(), allow: "autoplay", allowFullScreen: true })
101
+ children: [
102
+ /* @__PURE__ */ jsx("iframe", { ref: iframeRef, className: styles.vimeoContainer, src: vimeoUrl.toString(), allow: "autoplay", allowFullScreen: true }),
103
+ !showControls && (PlayPauseButtonComponent ? /* @__PURE__ */ jsx(PlayPauseButtonComponent, { isPlaying, onClick: handlePlayPauseClick }) : /* @__PURE__ */ jsx(
104
+ PlayPauseButton,
105
+ {
106
+ isPlaying,
107
+ onClick: handlePlayPauseClick,
108
+ ariaLabelPlay: playButtonAriaLabel,
109
+ ariaLabelPause: pauseButtonAriaLabel
110
+ }
111
+ ))
112
+ ]
71
113
  }
72
114
  ) });
73
115
  },
@@ -1,9 +1,10 @@
1
1
  "use client";
2
- import { jsx, Fragment } from "react/jsx-runtime";
2
+ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
3
  import clsx from "clsx";
4
- import { useState, useRef } from "react";
4
+ import { useState, useRef, useCallback } from "react";
5
5
  import { withPreview } from "../iframebridge/withPreview.js";
6
6
  import { PreviewSkeleton } from "../previewskeleton/PreviewSkeleton.js";
7
+ import { PlayPauseButton } from "./helpers/PlayPauseButton.js";
7
8
  import { useIsElementInViewport } from "./helpers/useIsElementInViewport.js";
8
9
  import styles from "./YouTubeVideoBlock.module.scss.js";
9
10
  const EXPECTED_YT_ID_LENGTH = 11;
@@ -21,29 +22,40 @@ const YouTubeVideoBlock = withPreview(
21
22
  renderPreviewImage,
22
23
  fill,
23
24
  previewImageIcon,
24
- playButtonAriaLabel
25
+ playButtonAriaLabel,
26
+ pauseButtonAriaLabel,
27
+ playPauseButton: PlayPauseButtonComponent
25
28
  }) => {
26
29
  const [showPreviewImage, setShowPreviewImage] = useState(true);
30
+ const [isPlaying, setIsPlaying] = useState(autoplay ?? false);
31
+ const [isHandledManually, setIsHandledManually] = useState(false);
27
32
  const hasPreviewImage = !!(previewImage && previewImage.damFile);
28
- const iframeRef = useRef(null);
33
+ const [iframeElement, setIframeElement] = useState(null);
34
+ const iframeRef = setIframeElement;
29
35
  const inViewRef = useRef(null);
30
- const pauseYouTubeVideo = () => {
31
- var _a, _b;
32
- (_b = (_a = iframeRef.current) == null ? void 0 : _a.contentWindow) == null ? void 0 : _b.postMessage(`{"event":"command","func":"pauseVideo","args":""}`, "https://www.youtube-nocookie.com");
33
- };
34
- const playYouTubeVideo = () => {
35
- var _a, _b;
36
- (_b = (_a = iframeRef.current) == null ? void 0 : _a.contentWindow) == null ? void 0 : _b.postMessage(`{"event":"command","func":"playVideo","args":""}`, "https://www.youtube-nocookie.com");
37
- };
38
- useIsElementInViewport(inViewRef, (inView) => {
39
- if (autoplay) {
40
- if (inView) {
41
- playYouTubeVideo();
42
- } else {
43
- pauseYouTubeVideo();
36
+ const pauseYouTubeVideo = useCallback(() => {
37
+ var _a;
38
+ (_a = iframeElement == null ? void 0 : iframeElement.contentWindow) == null ? void 0 : _a.postMessage(`{"event":"command","func":"pauseVideo","args":""}`, "https://www.youtube-nocookie.com");
39
+ }, [iframeElement]);
40
+ const playYouTubeVideo = useCallback(() => {
41
+ var _a;
42
+ (_a = iframeElement == null ? void 0 : iframeElement.contentWindow) == null ? void 0 : _a.postMessage(`{"event":"command","func":"playVideo","args":""}`, "https://www.youtube-nocookie.com");
43
+ }, [iframeElement]);
44
+ const handleInView = useCallback(
45
+ (inView) => {
46
+ if (!isHandledManually) {
47
+ if (inView && autoplay) {
48
+ playYouTubeVideo();
49
+ setIsPlaying(true);
50
+ } else {
51
+ pauseYouTubeVideo();
52
+ setIsPlaying(false);
53
+ }
44
54
  }
45
- }
46
- });
55
+ },
56
+ [autoplay, isHandledManually, playYouTubeVideo, pauseYouTubeVideo]
57
+ );
58
+ useIsElementInViewport(inViewRef, handleInView);
47
59
  if (!youtubeIdentifier) {
48
60
  return /* @__PURE__ */ jsx(PreviewSkeleton, { type: "media", hasContent: false, aspectRatio });
49
61
  }
@@ -52,7 +64,7 @@ const YouTubeVideoBlock = withPreview(
52
64
  searchParams.append("modestbranding", "1");
53
65
  searchParams.append("rel", "0");
54
66
  searchParams.append("enablejsapi", "1");
55
- if (hasPreviewImage && !showPreviewImage) searchParams.append("autoplay", "1");
67
+ if (hasPreviewImage && !showPreviewImage || !hasPreviewImage) searchParams.append("autoplay", "1");
56
68
  if (autoplay) searchParams.append("mute", "1");
57
69
  if (showControls !== void 0) searchParams.append("controls", Number(showControls).toString());
58
70
  if (loop !== void 0) searchParams.append("loop", Number(loop).toString());
@@ -60,6 +72,16 @@ const YouTubeVideoBlock = withPreview(
60
72
  const youtubeBaseUrl = "https://www.youtube-nocookie.com/embed/";
61
73
  const youtubeUrl = new URL(`${youtubeBaseUrl}${identifier ?? ""}`);
62
74
  youtubeUrl.search = searchParams.toString();
75
+ const handlePlayPauseClick = () => {
76
+ if (isPlaying) {
77
+ setIsPlaying(false);
78
+ setIsHandledManually(true);
79
+ pauseYouTubeVideo();
80
+ } else {
81
+ setIsPlaying(true);
82
+ playYouTubeVideo();
83
+ }
84
+ };
63
85
  return /* @__PURE__ */ jsx(Fragment, { children: hasPreviewImage && showPreviewImage ? renderPreviewImage({
64
86
  onPlay: () => setShowPreviewImage(false),
65
87
  image: previewImage,
@@ -68,22 +90,33 @@ const YouTubeVideoBlock = withPreview(
68
90
  fill,
69
91
  icon: previewImageIcon,
70
92
  playButtonAriaLabel
71
- }) : /* @__PURE__ */ jsx(
93
+ }) : /* @__PURE__ */ jsxs(
72
94
  "div",
73
95
  {
74
96
  ref: inViewRef,
75
97
  className: clsx(styles.videoContainer, fill && styles.fill),
76
98
  style: !fill ? { "--aspect-ratio": aspectRatio.replace("x", "/") } : void 0,
77
- children: /* @__PURE__ */ jsx(
78
- "iframe",
79
- {
80
- ref: iframeRef,
81
- className: styles.youtubeContainer,
82
- allow: "autoplay",
83
- referrerPolicy: "strict-origin-when-cross-origin",
84
- src: youtubeUrl.toString()
85
- }
86
- )
99
+ children: [
100
+ !showControls && (PlayPauseButtonComponent ? /* @__PURE__ */ jsx(PlayPauseButtonComponent, { isPlaying, onClick: handlePlayPauseClick }) : /* @__PURE__ */ jsx(
101
+ PlayPauseButton,
102
+ {
103
+ isPlaying,
104
+ onClick: handlePlayPauseClick,
105
+ ariaLabelPlay: playButtonAriaLabel,
106
+ ariaLabelPause: pauseButtonAriaLabel
107
+ }
108
+ )),
109
+ /* @__PURE__ */ jsx(
110
+ "iframe",
111
+ {
112
+ ref: iframeRef,
113
+ className: styles.youtubeContainer,
114
+ allow: "autoplay",
115
+ referrerPolicy: "strict-origin-when-cross-origin",
116
+ src: youtubeUrl.toString()
117
+ }
118
+ )
119
+ ]
87
120
  }
88
121
  ) });
89
122
  },
@@ -0,0 +1,9 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import clsx from "clsx";
3
+ import styles from "./PlayPauseButton.module.scss.js";
4
+ const PlayPauseButton = ({ isPlaying, onClick, ariaLabelPlay, ariaLabelPause }) => {
5
+ return /* @__PURE__ */ jsx("button", { className: clsx(styles.button), onClick, "aria-label": isPlaying ? ariaLabelPause ?? "Pause" : ariaLabelPlay ?? "Play", children: /* @__PURE__ */ jsx("div", { className: clsx(styles.animatedPlayPause, isPlaying && styles.animatedPlayPausePaused) }) });
6
+ };
7
+ export {
8
+ PlayPauseButton
9
+ };
@@ -0,0 +1,14 @@
1
+ const button = "_button_4r8gs_1";
2
+ const animatedPlayPause = "_animatedPlayPause_4r8gs_23";
3
+ const animatedPlayPausePaused = "_animatedPlayPausePaused_4r8gs_32";
4
+ const styles = {
5
+ button,
6
+ animatedPlayPause,
7
+ animatedPlayPausePaused
8
+ };
9
+ export {
10
+ animatedPlayPause,
11
+ animatedPlayPausePaused,
12
+ button,
13
+ styles as default
14
+ };
package/lib/style.css CHANGED
@@ -1 +1 @@
1
- ._previewElementContainer_nwhl6_1{display:contents}._childrenWrapper_qguhx_1{position:relative;z-index:1}._root_v4248_1{position:absolute;z-index:2;top:0;left:0;right:0;min-height:100vh;height:var(--height)}._root_1ya0k_1{position:absolute;cursor:pointer;outline:1px solid transparent;outline-offset:-1px}._root_1ya0k_1:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;opacity:.25}._root_1ya0k_1:hover{outline-color:#29b6f6;outline-style:solid}._root_1ya0k_1:hover:after{background-color:#29b6f6}._isHoveredInBlockList_1ya0k_24{outline-color:#29b6f6;outline-style:solid}._isHoveredInBlockList_1ya0k_24:after{background-color:#29b6f6}._showBlockOutlines_1ya0k_32:not(:hover){outline-color:#d9d9d9;outline-style:dashed}._blockIsSelected_1ya0k_37{outline-color:#29b6f6}._blockIsSelected_1ya0k_37 ._label_1ya0k_40{display:inline-block}._label_1ya0k_40{position:absolute;padding:2px;background-color:#57b0eb;line-height:16px;color:#fff;top:1px;right:1px;font-size:12px;display:none}._barSkeleton_zomv4_1{min-height:20px;margin-bottom:5px;margin-right:5px;background-color:var(--background-color);color:var(--color)}._rowsContainer_zomv4_9{width:100%;min-width:300px}._rowSkeleton_zomv4_14{margin-bottom:10px;min-height:20px;width:var(--width);background-color:var(--background-color);color:var(--color)}._imageContainer_zomv4_22{width:100%;aspect-ratio:var(--aspect-ratio);height:var(--height);background-color:var(--background-color);color:var(--color)}._video_17b8y_1{width:100%;object-fit:cover;aspect-ratio:var(--aspect-ratio)}._fill_17b8y_7{height:100%}._root_1k5a5_1{position:relative;width:100%}._fill_1k5a5_6{height:100%}._iconWrapper_1k5a5_10{position:absolute;top:0;right:0;left:0;bottom:0;display:flex;align-items:center;justify-content:center;background-color:#000;opacity:.5;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;padding:0;cursor:pointer}._iconWrapper_1k5a5_10:focus ._playIcon_1k5a5_26{outline:4px solid white;outline-offset:10px;border-radius:2px}._playIcon_1k5a5_26{width:64px;height:64px;box-sizing:border-box;border-style:solid;border-width:32px 0 32px 64px;border-color:transparent transparent transparent white}._videoContainer_1flq1_1{overflow:hidden;position:relative;aspect-ratio:var(--aspect-ratio)}._fill_1flq1_7{width:100%;height:100%}._vimeoContainer_1flq1_12{position:absolute;border:0;top:0;left:0;width:100%;height:100%}._videoContainer_bhmhr_1{overflow:hidden;position:relative;aspect-ratio:var(--aspect-ratio)}._fill_bhmhr_7{width:100%;height:100%}._youtubeContainer_bhmhr_12{position:absolute;border:0;top:0;left:0;width:100%;height:100%}._imageContainer_ecpdr_1{position:relative;width:100%;aspect-ratio:var(--aspect-ratio)}
1
+ ._previewElementContainer_nwhl6_1{display:contents}._childrenWrapper_qguhx_1{position:relative;z-index:1}._root_v4248_1{position:absolute;z-index:2;top:0;left:0;right:0;min-height:100vh;height:var(--height)}._root_1ya0k_1{position:absolute;cursor:pointer;outline:1px solid transparent;outline-offset:-1px}._root_1ya0k_1:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;opacity:.25}._root_1ya0k_1:hover{outline-color:#29b6f6;outline-style:solid}._root_1ya0k_1:hover:after{background-color:#29b6f6}._isHoveredInBlockList_1ya0k_24{outline-color:#29b6f6;outline-style:solid}._isHoveredInBlockList_1ya0k_24:after{background-color:#29b6f6}._showBlockOutlines_1ya0k_32:not(:hover){outline-color:#d9d9d9;outline-style:dashed}._blockIsSelected_1ya0k_37{outline-color:#29b6f6}._blockIsSelected_1ya0k_37 ._label_1ya0k_40{display:inline-block}._label_1ya0k_40{position:absolute;padding:2px;background-color:#57b0eb;line-height:16px;color:#fff;top:1px;right:1px;font-size:12px;display:none}._barSkeleton_zomv4_1{min-height:20px;margin-bottom:5px;margin-right:5px;background-color:var(--background-color);color:var(--color)}._rowsContainer_zomv4_9{width:100%;min-width:300px}._rowSkeleton_zomv4_14{margin-bottom:10px;min-height:20px;width:var(--width);background-color:var(--background-color);color:var(--color)}._imageContainer_zomv4_22{width:100%;aspect-ratio:var(--aspect-ratio);height:var(--height);background-color:var(--background-color);color:var(--color)}._root_8r6od_1{position:relative}._video_8r6od_5{width:100%;object-fit:cover;aspect-ratio:var(--aspect-ratio)}._fill_8r6od_11{height:100%}._button_4r8gs_1{position:absolute;left:8px;bottom:8px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;background-color:#000000b3;z-index:10}._button_4r8gs_1:hover,._button_4r8gs_1:active{background-color:#9e9e9e}._button_4r8gs_1:focus{border-radius:6px}._animatedPlayPause_4r8gs_23{box-sizing:border-box;height:12px;border-color:transparent transparent transparent #fff;cursor:pointer;border-style:solid;border-width:6px 0 6px 9px;transition:border-width .1s ease,border-style .1s ease}._animatedPlayPausePaused_4r8gs_32{border-style:double;border-width:0 0 0 9px}._root_1k5a5_1{position:relative;width:100%}._fill_1k5a5_6{height:100%}._iconWrapper_1k5a5_10{position:absolute;top:0;right:0;left:0;bottom:0;display:flex;align-items:center;justify-content:center;background-color:#000;opacity:.5;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;padding:0;cursor:pointer}._iconWrapper_1k5a5_10:focus ._playIcon_1k5a5_26{outline:4px solid white;outline-offset:10px;border-radius:2px}._playIcon_1k5a5_26{width:64px;height:64px;box-sizing:border-box;border-style:solid;border-width:32px 0 32px 64px;border-color:transparent transparent transparent white}._videoContainer_1flq1_1{overflow:hidden;position:relative;aspect-ratio:var(--aspect-ratio)}._fill_1flq1_7{width:100%;height:100%}._vimeoContainer_1flq1_12{position:absolute;border:0;top:0;left:0;width:100%;height:100%}._videoContainer_bhmhr_1{overflow:hidden;position:relative;aspect-ratio:var(--aspect-ratio)}._fill_bhmhr_7{width:100%;height:100%}._youtubeContainer_bhmhr_12{position:absolute;border:0;top:0;left:0;width:100%;height:100%}._imageContainer_ecpdr_1{position:relative;width:100%;aspect-ratio:var(--aspect-ratio)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comet/site-nextjs",
3
- "version": "9.0.0-canary-20251209132220",
3
+ "version": "9.0.0-canary-20251209140438",
4
4
  "description": "Comet Site Next.js package",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,13 +28,13 @@
28
28
  "dependencies": {
29
29
  "jose": "^5.10.0",
30
30
  "server-only": "^0.0.1",
31
- "@comet/site-react": "9.0.0-canary-20251209132220"
31
+ "@comet/site-react": "9.0.0-canary-20251209140438"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/jest": "^29.5.14",
35
35
  "@types/react": "^19.2.0",
36
36
  "@types/react-dom": "^19.2.0",
37
- "@vitejs/plugin-react": "^4.6.0",
37
+ "@vitejs/plugin-react": "^4.7.0",
38
38
  "chokidar-cli": "^3.0.0",
39
39
  "eslint": "^9.30.1",
40
40
  "jest": "^29.7.0",
@@ -45,7 +45,7 @@
45
45
  "prettier": "^3.6.2",
46
46
  "react": "^19.2.0",
47
47
  "react-dom": "^19.2.0",
48
- "rimraf": "^6.0.1",
48
+ "rimraf": "^6.1.2",
49
49
  "rollup": "^4.44.2",
50
50
  "rollup-plugin-preserve-directives": "^0.4.0",
51
51
  "sass": "^1.89.2",
@@ -53,8 +53,8 @@
53
53
  "typescript": "5.9.3",
54
54
  "vite": "^5.4.19",
55
55
  "vite-plugin-dts": "^4.5.4",
56
- "@comet/cli": "9.0.0-canary-20251209132220",
57
- "@comet/eslint-config": "9.0.0-canary-20251209132220"
56
+ "@comet/cli": "9.0.0-canary-20251209140438",
57
+ "@comet/eslint-config": "9.0.0-canary-20251209140438"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "next": "^16.0.0",
@@ -75,6 +75,7 @@
75
75
  "generate-block-types": "comet generate-block-types",
76
76
  "generate-block-types:watch": "chokidar -s \"block-meta.json\" -c \"pnpm run generate-block-types\"",
77
77
  "lint": "pnpm run generate-block-types && run-p lint:prettier lint:eslint lint:tsc",
78
+ "lint:ci": "pnpm run lint",
78
79
  "lint:eslint": "eslint --max-warnings 0 src/ **/*.json --no-warn-ignored",
79
80
  "lint:prettier": "pnpm exec prettier --check './**/*.{js,json,md,yml,yaml,css,scss}'",
80
81
  "lint:tsc": "tsc --noEmit",