@wordpress/block-library 9.35.1-next.dc3f6d3c1.0 → 9.36.1-next.8b30e05b0.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.
Files changed (262) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/accordion/view.js +46 -4
  3. package/build/accordion/view.js.map +2 -2
  4. package/build/accordion-heading/block.json +1 -1
  5. package/build/accordion-heading/deprecated.js +1 -1
  6. package/build/accordion-heading/deprecated.js.map +2 -2
  7. package/build/accordion-panel/block.json +1 -1
  8. package/build/audio/index.js +12 -9
  9. package/build/audio/index.js.map +3 -3
  10. package/build/button/index.js +13 -10
  11. package/build/button/index.js.map +3 -3
  12. package/build/code/index.js +9 -6
  13. package/build/code/index.js.map +3 -3
  14. package/build/cover/edit/block-controls.js +37 -3
  15. package/build/cover/edit/block-controls.js.map +3 -3
  16. package/build/cover/edit/cover-placeholder.js +0 -1
  17. package/build/cover/edit/cover-placeholder.js.map +2 -2
  18. package/build/cover/edit/embed-video-url-input.js +83 -0
  19. package/build/cover/edit/embed-video-url-input.js.map +7 -0
  20. package/build/cover/edit/index.js +60 -0
  21. package/build/cover/edit/index.js.map +2 -2
  22. package/build/cover/embed-video-utils.js +151 -0
  23. package/build/cover/embed-video-utils.js.map +7 -0
  24. package/build/cover/index.js +10 -4
  25. package/build/cover/index.js.map +3 -3
  26. package/build/cover/save.js +12 -0
  27. package/build/cover/save.js.map +2 -2
  28. package/build/cover/shared.js +3 -0
  29. package/build/cover/shared.js.map +2 -2
  30. package/build/details/index.js +9 -6
  31. package/build/details/index.js.map +3 -3
  32. package/build/file/index.js +14 -14
  33. package/build/file/index.js.map +3 -3
  34. package/build/freeform/block.json +1 -1
  35. package/build/gallery/edit.js +0 -2
  36. package/build/gallery/edit.js.map +2 -2
  37. package/build/heading/index.js +9 -6
  38. package/build/heading/index.js.map +3 -3
  39. package/build/html/modal.js +127 -118
  40. package/build/html/modal.js.map +3 -3
  41. package/build/image/edit.js +0 -1
  42. package/build/image/edit.js.map +2 -2
  43. package/build/image/image.js +0 -1
  44. package/build/image/image.js.map +2 -2
  45. package/build/image/index.js +18 -18
  46. package/build/image/index.js.map +3 -3
  47. package/build/list-item/index.js +8 -6
  48. package/build/list-item/index.js.map +3 -3
  49. package/build/math/block.json +28 -1
  50. package/build/math/edit.js +4 -1
  51. package/build/math/edit.js.map +2 -2
  52. package/build/media-text/index.js +15 -8
  53. package/build/media-text/index.js.map +3 -3
  54. package/build/media-text/media-container.js +0 -2
  55. package/build/media-text/media-container.js.map +2 -2
  56. package/build/missing/block.json +1 -1
  57. package/build/missing/edit.js +2 -2
  58. package/build/missing/edit.js.map +1 -1
  59. package/build/more/index.js +9 -6
  60. package/build/more/index.js.map +3 -3
  61. package/build/navigation-link/edit.js +36 -11
  62. package/build/navigation-link/edit.js.map +2 -2
  63. package/build/navigation-link/index.js +11 -8
  64. package/build/navigation-link/index.js.map +3 -3
  65. package/build/navigation-submenu/index.js +11 -8
  66. package/build/navigation-submenu/index.js.map +3 -3
  67. package/build/paragraph/deprecated-attributes.js +68 -0
  68. package/build/paragraph/deprecated-attributes.js.map +7 -0
  69. package/build/paragraph/edit.js +2 -0
  70. package/build/paragraph/edit.js.map +3 -3
  71. package/build/paragraph/index.js +9 -6
  72. package/build/paragraph/index.js.map +3 -3
  73. package/build/pattern/block.json +1 -1
  74. package/build/preformatted/index.js +9 -6
  75. package/build/preformatted/index.js.map +3 -3
  76. package/build/pullquote/index.js +11 -11
  77. package/build/pullquote/index.js.map +3 -3
  78. package/build/search/index.js +14 -16
  79. package/build/search/index.js.map +3 -3
  80. package/build/social-link/index.js +11 -8
  81. package/build/social-link/index.js.map +3 -3
  82. package/build/template-part/edit/index.js +37 -7
  83. package/build/template-part/edit/index.js.map +2 -2
  84. package/build/template-part/edit/utils/hooks.js +2 -3
  85. package/build/template-part/edit/utils/hooks.js.map +2 -2
  86. package/build/term-count/index.js +1 -0
  87. package/build/term-count/index.js.map +2 -2
  88. package/build/term-name/index.js +1 -0
  89. package/build/term-name/index.js.map +2 -2
  90. package/build/verse/index.js +9 -6
  91. package/build/verse/index.js.map +3 -3
  92. package/build/video/index.js +12 -9
  93. package/build/video/index.js.map +3 -3
  94. package/build-module/accordion/view.js +46 -4
  95. package/build-module/accordion/view.js.map +2 -2
  96. package/build-module/accordion-heading/block.json +1 -1
  97. package/build-module/accordion-heading/deprecated.js +1 -1
  98. package/build-module/accordion-heading/deprecated.js.map +2 -2
  99. package/build-module/accordion-panel/block.json +1 -1
  100. package/build-module/audio/index.js +12 -9
  101. package/build-module/audio/index.js.map +2 -2
  102. package/build-module/button/index.js +13 -10
  103. package/build-module/button/index.js.map +2 -2
  104. package/build-module/code/index.js +9 -6
  105. package/build-module/code/index.js.map +2 -2
  106. package/build-module/cover/edit/block-controls.js +27 -3
  107. package/build-module/cover/edit/block-controls.js.map +2 -2
  108. package/build-module/cover/edit/cover-placeholder.js +0 -1
  109. package/build-module/cover/edit/cover-placeholder.js.map +2 -2
  110. package/build-module/cover/edit/embed-video-url-input.js +67 -0
  111. package/build-module/cover/edit/embed-video-url-input.js.map +7 -0
  112. package/build-module/cover/edit/index.js +61 -0
  113. package/build-module/cover/edit/index.js.map +2 -2
  114. package/build-module/cover/embed-video-utils.js +122 -0
  115. package/build-module/cover/embed-video-utils.js.map +7 -0
  116. package/build-module/cover/index.js +10 -4
  117. package/build-module/cover/index.js.map +2 -2
  118. package/build-module/cover/save.js +13 -0
  119. package/build-module/cover/save.js.map +2 -2
  120. package/build-module/cover/shared.js +2 -0
  121. package/build-module/cover/shared.js.map +2 -2
  122. package/build-module/details/index.js +9 -6
  123. package/build-module/details/index.js.map +2 -2
  124. package/build-module/file/index.js +14 -14
  125. package/build-module/file/index.js.map +2 -2
  126. package/build-module/freeform/block.json +1 -1
  127. package/build-module/gallery/edit.js +0 -2
  128. package/build-module/gallery/edit.js.map +2 -2
  129. package/build-module/heading/index.js +9 -6
  130. package/build-module/heading/index.js.map +2 -2
  131. package/build-module/html/modal.js +128 -119
  132. package/build-module/html/modal.js.map +2 -2
  133. package/build-module/image/edit.js +0 -1
  134. package/build-module/image/edit.js.map +2 -2
  135. package/build-module/image/image.js +0 -1
  136. package/build-module/image/image.js.map +2 -2
  137. package/build-module/image/index.js +18 -18
  138. package/build-module/image/index.js.map +2 -2
  139. package/build-module/list-item/index.js +8 -6
  140. package/build-module/list-item/index.js.map +2 -2
  141. package/build-module/math/block.json +28 -1
  142. package/build-module/math/edit.js +4 -1
  143. package/build-module/math/edit.js.map +2 -2
  144. package/build-module/media-text/index.js +15 -8
  145. package/build-module/media-text/index.js.map +2 -2
  146. package/build-module/media-text/media-container.js +0 -2
  147. package/build-module/media-text/media-container.js.map +2 -2
  148. package/build-module/missing/block.json +1 -1
  149. package/build-module/missing/edit.js +2 -2
  150. package/build-module/missing/edit.js.map +1 -1
  151. package/build-module/more/index.js +9 -6
  152. package/build-module/more/index.js.map +2 -2
  153. package/build-module/navigation-link/edit.js +37 -12
  154. package/build-module/navigation-link/edit.js.map +2 -2
  155. package/build-module/navigation-link/index.js +11 -8
  156. package/build-module/navigation-link/index.js.map +2 -2
  157. package/build-module/navigation-submenu/index.js +11 -8
  158. package/build-module/navigation-submenu/index.js.map +2 -2
  159. package/build-module/paragraph/deprecated-attributes.js +37 -0
  160. package/build-module/paragraph/deprecated-attributes.js.map +7 -0
  161. package/build-module/paragraph/edit.js +2 -0
  162. package/build-module/paragraph/edit.js.map +2 -2
  163. package/build-module/paragraph/index.js +9 -6
  164. package/build-module/paragraph/index.js.map +2 -2
  165. package/build-module/pattern/block.json +1 -1
  166. package/build-module/preformatted/index.js +9 -6
  167. package/build-module/preformatted/index.js.map +2 -2
  168. package/build-module/pullquote/index.js +11 -11
  169. package/build-module/pullquote/index.js.map +2 -2
  170. package/build-module/search/index.js +14 -16
  171. package/build-module/search/index.js.map +2 -2
  172. package/build-module/social-link/index.js +11 -8
  173. package/build-module/social-link/index.js.map +2 -2
  174. package/build-module/template-part/edit/index.js +37 -7
  175. package/build-module/template-part/edit/index.js.map +2 -2
  176. package/build-module/template-part/edit/utils/hooks.js +2 -3
  177. package/build-module/template-part/edit/utils/hooks.js.map +2 -2
  178. package/build-module/term-count/index.js +1 -0
  179. package/build-module/term-count/index.js.map +2 -2
  180. package/build-module/term-name/index.js +1 -0
  181. package/build-module/term-name/index.js.map +2 -2
  182. package/build-module/verse/index.js +9 -6
  183. package/build-module/verse/index.js.map +2 -2
  184. package/build-module/video/index.js +12 -9
  185. package/build-module/video/index.js.map +2 -2
  186. package/build-style/accordion/style-rtl.css +3 -0
  187. package/build-style/accordion/style.css +3 -0
  188. package/build-style/accordion-heading/style-rtl.css +1 -2
  189. package/build-style/accordion-heading/style.css +1 -2
  190. package/build-style/accordion-item/style-rtl.css +0 -7
  191. package/build-style/accordion-item/style.css +0 -7
  192. package/build-style/accordion-panel/style-rtl.css +1 -4
  193. package/build-style/accordion-panel/style.css +1 -4
  194. package/build-style/cover/style-rtl.css +47 -0
  195. package/build-style/cover/style.css +47 -0
  196. package/build-style/editor-rtl.css +11 -13
  197. package/build-style/editor.css +11 -13
  198. package/build-style/html/editor-rtl.css +11 -13
  199. package/build-style/html/editor.css +11 -13
  200. package/build-style/style-rtl.css +52 -12
  201. package/build-style/style.css +52 -12
  202. package/package.json +37 -37
  203. package/src/accordion/style.scss +4 -0
  204. package/src/accordion/view.js +60 -3
  205. package/src/accordion-heading/block.json +1 -1
  206. package/src/accordion-heading/deprecated.js +1 -1
  207. package/src/accordion-heading/style.scss +1 -9
  208. package/src/accordion-item/index.php +1 -0
  209. package/src/accordion-item/style.scss +2 -9
  210. package/src/accordion-panel/block.json +1 -1
  211. package/src/accordion-panel/style.scss +1 -5
  212. package/src/audio/index.js +13 -9
  213. package/src/breadcrumbs/index.php +71 -82
  214. package/src/button/index.js +14 -10
  215. package/src/code/index.js +10 -6
  216. package/src/cover/edit/block-controls.js +26 -2
  217. package/src/cover/edit/cover-placeholder.js +0 -1
  218. package/src/cover/edit/embed-video-url-input.js +74 -0
  219. package/src/cover/edit/index.js +81 -0
  220. package/src/cover/embed-video-utils.js +196 -0
  221. package/src/cover/index.js +11 -4
  222. package/src/cover/index.php +106 -0
  223. package/src/cover/save.js +14 -0
  224. package/src/cover/shared.js +1 -0
  225. package/src/cover/style.scss +47 -0
  226. package/src/details/index.js +10 -6
  227. package/src/file/index.js +15 -14
  228. package/src/freeform/block.json +1 -1
  229. package/src/gallery/edit.js +0 -2
  230. package/src/heading/index.js +10 -6
  231. package/src/html/editor.scss +10 -15
  232. package/src/html/modal.js +15 -10
  233. package/src/image/edit.js +0 -1
  234. package/src/image/image.js +0 -1
  235. package/src/image/index.js +19 -18
  236. package/src/image/index.php +1 -0
  237. package/src/list-item/index.js +9 -6
  238. package/src/math/block.json +28 -1
  239. package/src/math/edit.js +4 -1
  240. package/src/media-text/index.js +16 -8
  241. package/src/media-text/media-container.js +0 -2
  242. package/src/missing/block.json +1 -1
  243. package/src/missing/edit.js +2 -2
  244. package/src/more/index.js +10 -6
  245. package/src/navigation-link/edit.js +72 -26
  246. package/src/navigation-link/index.js +12 -8
  247. package/src/navigation-submenu/index.js +12 -8
  248. package/src/paragraph/deprecated-attributes.js +45 -0
  249. package/src/paragraph/edit.js +2 -0
  250. package/src/paragraph/index.js +10 -6
  251. package/src/pattern/block.json +1 -1
  252. package/src/preformatted/index.js +10 -6
  253. package/src/pullquote/index.js +12 -11
  254. package/src/search/index.js +14 -15
  255. package/src/social-link/index.js +12 -8
  256. package/src/style.scss +1 -0
  257. package/src/template-part/edit/index.js +44 -6
  258. package/src/template-part/edit/utils/hooks.js +2 -4
  259. package/src/term-count/index.js +1 -0
  260. package/src/term-name/index.js +1 -0
  261. package/src/verse/index.js +10 -6
  262. package/src/video/index.js +13 -9
@@ -32,6 +32,7 @@ import {
32
32
  attributesFromMedia,
33
33
  IMAGE_BACKGROUND_TYPE,
34
34
  VIDEO_BACKGROUND_TYPE,
35
+ EMBED_VIDEO_BACKGROUND_TYPE,
35
36
  dimRatioToClass,
36
37
  isContentPositionCenter,
37
38
  getPositionClassName,
@@ -48,6 +49,7 @@ import {
48
49
  DEFAULT_OVERLAY_COLOR,
49
50
  } from './color-utils';
50
51
  import { DEFAULT_MEDIA_SIZE_SLUG } from '../constants';
52
+ import { getIframeSrc, getBackgroundVideoSrc } from '../embed-video-utils';
51
53
 
52
54
  function getInnerBlocksTemplate( attributes ) {
53
55
  return [
@@ -319,10 +321,71 @@ function CoverEdit( {
319
321
  createErrorNotice( message, { type: 'snackbar' } );
320
322
  };
321
323
 
324
+ const onSelectEmbedUrl = ( embedUrl ) => {
325
+ // Only set a new dimRatio if there was no previous media selected
326
+ // to avoid resetting to 50 if it has been explicitly set to 100.
327
+ const newDimRatio =
328
+ originalUrl === undefined && dimRatio === 100 ? 50 : dimRatio;
329
+
330
+ // Set initial attributes with URL
331
+ setAttributes( {
332
+ url: embedUrl,
333
+ backgroundType: EMBED_VIDEO_BACKGROUND_TYPE,
334
+ dimRatio: newDimRatio,
335
+ id: undefined,
336
+ focalPoint: undefined,
337
+ hasParallax: undefined,
338
+ isRepeated: undefined,
339
+ useFeaturedImage: undefined,
340
+ } );
341
+ };
342
+
343
+ // Fetch embed preview for embed videos
344
+ const { embedPreview, isFetchingEmbed } = useSelect(
345
+ ( select ) => {
346
+ if ( backgroundType !== EMBED_VIDEO_BACKGROUND_TYPE || ! url ) {
347
+ return {
348
+ embedPreview: undefined,
349
+ isFetchingEmbed: false,
350
+ };
351
+ }
352
+
353
+ const { getEmbedPreview, isRequestingEmbedPreview } =
354
+ select( coreStore );
355
+
356
+ return {
357
+ embedPreview: getEmbedPreview( url ),
358
+ isFetchingEmbed: isRequestingEmbedPreview( url ),
359
+ };
360
+ },
361
+ [ url, backgroundType ]
362
+ );
363
+
364
+ // Compute embedSrc on-the-fly from embed preview for editor display
365
+ const embedSrc = useMemo( () => {
366
+ if (
367
+ backgroundType !== EMBED_VIDEO_BACKGROUND_TYPE ||
368
+ ! embedPreview?.html
369
+ ) {
370
+ return null;
371
+ }
372
+
373
+ // Extract iframe src from embed HTML
374
+ const iframeSrc = getIframeSrc( embedPreview.html );
375
+ if ( ! iframeSrc ) {
376
+ return null;
377
+ }
378
+
379
+ // Modify the src to add background video parameters (provider auto-detected)
380
+ return getBackgroundVideoSrc( iframeSrc );
381
+ }, [ embedPreview, backgroundType ] );
382
+
322
383
  const isUploadingMedia = isTemporaryMedia( id, url );
323
384
 
324
385
  const isImageBackground = IMAGE_BACKGROUND_TYPE === backgroundType;
325
386
  const isVideoBackground = VIDEO_BACKGROUND_TYPE === backgroundType;
387
+ const isEmbedVideoBackground =
388
+ EMBED_VIDEO_BACKGROUND_TYPE === backgroundType;
326
389
 
327
390
  const blockEditingMode = useBlockEditingMode();
328
391
  const hasNonContentControls = blockEditingMode === 'default';
@@ -449,6 +512,7 @@ function CoverEdit( {
449
512
  attributes={ attributes }
450
513
  setAttributes={ setAttributes }
451
514
  onSelectMedia={ onSelectMedia }
515
+ onSelectEmbedUrl={ onSelectEmbedUrl }
452
516
  currentSettings={ currentSettings }
453
517
  toggleUseFeaturedImage={ toggleUseFeaturedImage }
454
518
  onClearMedia={ onClearMedia }
@@ -600,6 +664,23 @@ function CoverEdit( {
600
664
  style={ mediaStyle }
601
665
  />
602
666
  ) }
667
+ { isEmbedVideoBackground && embedSrc && (
668
+ <div
669
+ ref={ mediaElement }
670
+ className="wp-block-cover__video-background wp-block-cover__embed-background"
671
+ style={ mediaStyle }
672
+ >
673
+ <iframe
674
+ src={ embedSrc }
675
+ title="Background video"
676
+ frameBorder="0"
677
+ allow="autoplay; fullscreen"
678
+ />
679
+ </div>
680
+ ) }
681
+ { isEmbedVideoBackground && ! embedSrc && isFetchingEmbed && (
682
+ <Spinner />
683
+ ) }
603
684
 
604
685
  { showOverlay && (
605
686
  <span
@@ -0,0 +1,196 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { getBlockVariations } from '@wordpress/blocks';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { matchesPatterns } from '../embed/util';
10
+
11
+ const DEFAULT_EMBED_BLOCK = 'core/embed';
12
+
13
+ // List of supported video providers for cover block backgrounds
14
+ const VIDEO_PROVIDERS = [
15
+ 'youtube',
16
+ 'vimeo',
17
+ 'videopress',
18
+ 'animoto',
19
+ 'tiktok',
20
+ 'wordpress-tv',
21
+ ];
22
+
23
+ /**
24
+ * Checks if a URL is a valid video embed URL from supported providers.
25
+ *
26
+ * @param {string} url The URL to validate.
27
+ * @return {boolean} True if the URL matches a supported video provider pattern.
28
+ */
29
+ export function isValidVideoEmbedUrl( url ) {
30
+ if ( ! url ) {
31
+ return false;
32
+ }
33
+
34
+ const embedBlock = findVideoEmbedProvider( url );
35
+ return embedBlock !== null;
36
+ }
37
+
38
+ /**
39
+ * Finds the embed provider for a given URL if it's a supported video provider.
40
+ *
41
+ * @param {string} url The URL to check.
42
+ * @return {string|null} The provider name slug (e.g., 'youtube') or null if not found.
43
+ */
44
+ export function getVideoEmbedProvider( url ) {
45
+ const embedBlock = findVideoEmbedProvider( url );
46
+ return embedBlock ? embedBlock.name : null;
47
+ }
48
+
49
+ /**
50
+ * Finds a matching video embed block variation for the given URL.
51
+ *
52
+ * @param {string} url The URL to match against provider patterns.
53
+ * @return {Object|null} The matching block variation or null if not found.
54
+ */
55
+ function findVideoEmbedProvider( url ) {
56
+ const embedVariations = getBlockVariations( DEFAULT_EMBED_BLOCK );
57
+
58
+ if ( ! embedVariations ) {
59
+ return null;
60
+ }
61
+
62
+ const matchingVariation = embedVariations.find( ( { patterns } ) =>
63
+ matchesPatterns( url, patterns )
64
+ );
65
+
66
+ if (
67
+ ! matchingVariation ||
68
+ ! VIDEO_PROVIDERS.includes( matchingVariation.name )
69
+ ) {
70
+ return null;
71
+ }
72
+
73
+ return matchingVariation;
74
+ }
75
+
76
+ /**
77
+ * Extracts iframe src from embed HTML.
78
+ *
79
+ * @param {string} html The embed HTML.
80
+ * @return {string|null} The iframe src URL or null if not found.
81
+ */
82
+ export function getIframeSrc( html ) {
83
+ if ( ! html ) {
84
+ return null;
85
+ }
86
+
87
+ const srcMatch = html.match( /src=["']([^"']+)["']/ );
88
+ return srcMatch ? srcMatch[ 1 ] : null;
89
+ }
90
+
91
+ /**
92
+ * Detects the video provider from an iframe src URL.
93
+ *
94
+ * @param {string} src The iframe src URL.
95
+ * @return {string|null} The provider name slug or null if not recognized.
96
+ */
97
+ export function detectProviderFromSrc( src ) {
98
+ if ( ! src ) {
99
+ return null;
100
+ }
101
+
102
+ const lowerSrc = src.toLowerCase();
103
+
104
+ if (
105
+ lowerSrc.includes( 'youtube.com' ) ||
106
+ lowerSrc.includes( 'youtu.be' )
107
+ ) {
108
+ return 'youtube';
109
+ }
110
+ if ( lowerSrc.includes( 'vimeo.com' ) ) {
111
+ return 'vimeo';
112
+ }
113
+ if ( lowerSrc.includes( 'videopress.com' ) ) {
114
+ return 'videopress';
115
+ }
116
+ if ( lowerSrc.includes( 'animoto.com' ) ) {
117
+ return 'animoto';
118
+ }
119
+ if ( lowerSrc.includes( 'tiktok.com' ) ) {
120
+ return 'tiktok';
121
+ }
122
+ if ( lowerSrc.includes( 'wordpress.tv' ) ) {
123
+ return 'wordpress-tv';
124
+ }
125
+
126
+ return null;
127
+ }
128
+
129
+ /**
130
+ * Modifies an iframe src URL to add background video parameters.
131
+ * Automatically detects the provider from the URL.
132
+ *
133
+ * @param {string} src The iframe src URL.
134
+ * @return {string} The modified URL.
135
+ */
136
+ export function getBackgroundVideoSrc( src ) {
137
+ if ( ! src ) {
138
+ return src;
139
+ }
140
+
141
+ try {
142
+ const url = new URL( src );
143
+
144
+ // Detect provider from the iframe src URL
145
+ const provider = detectProviderFromSrc( src );
146
+
147
+ // Add provider-specific parameters for background video behavior
148
+ switch ( provider ) {
149
+ case 'youtube':
150
+ // YouTube parameters for background video
151
+ url.searchParams.set( 'autoplay', '1' );
152
+ url.searchParams.set( 'mute', '1' );
153
+ url.searchParams.set( 'loop', '1' );
154
+ url.searchParams.set( 'controls', '0' );
155
+ url.searchParams.set( 'showinfo', '0' );
156
+ url.searchParams.set( 'modestbranding', '1' );
157
+ url.searchParams.set( 'playsinline', '1' );
158
+ url.searchParams.set( 'rel', '0' );
159
+ // For loop to work, we need the playlist parameter
160
+ const videoId = url.pathname.split( '/' ).pop();
161
+ if ( videoId ) {
162
+ url.searchParams.set( 'playlist', videoId );
163
+ }
164
+ break;
165
+
166
+ case 'vimeo':
167
+ // Vimeo parameters for background video
168
+ url.searchParams.set( 'autoplay', '1' );
169
+ url.searchParams.set( 'muted', '1' );
170
+ url.searchParams.set( 'loop', '1' );
171
+ url.searchParams.set( 'background', '1' );
172
+ url.searchParams.set( 'controls', '0' );
173
+ break;
174
+
175
+ case 'videopress':
176
+ case 'wordpress-tv':
177
+ // VideoPress parameters
178
+ url.searchParams.set( 'autoplay', '1' );
179
+ url.searchParams.set( 'loop', '1' );
180
+ url.searchParams.set( 'muted', '1' );
181
+ break;
182
+
183
+ default:
184
+ // Generic parameters that might work for other providers
185
+ url.searchParams.set( 'autoplay', '1' );
186
+ url.searchParams.set( 'muted', '1' );
187
+ url.searchParams.set( 'loop', '1' );
188
+ break;
189
+ }
190
+
191
+ return url.toString();
192
+ } catch ( error ) {
193
+ // If URL parsing fails, return original src
194
+ return src;
195
+ }
196
+ }
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
5
  import { cover as icon } from '@wordpress/icons';
6
+ import { privateApis as blocksPrivateApis } from '@wordpress/blocks';
6
7
 
7
8
  /**
8
9
  * Internal dependencies
@@ -14,6 +15,9 @@ import metadata from './block.json';
14
15
  import save from './save';
15
16
  import transforms from './transforms';
16
17
  import variations from './variations';
18
+ import { unlock } from '../lock-unlock';
19
+
20
+ const { fieldsKey, formKey } = unlock( blocksPrivateApis );
17
21
 
18
22
  const { name } = metadata;
19
23
 
@@ -53,15 +57,15 @@ export const settings = {
53
57
  };
54
58
 
55
59
  if ( window.__experimentalContentOnlyPatternInsertion ) {
56
- settings.fields = [
60
+ settings[ fieldsKey ] = [
57
61
  {
62
+ id: 'background',
58
63
  label: __( 'Background' ),
59
- type: 'Media',
60
- shownByDefault: true,
64
+ type: 'media',
61
65
  mapping: {
62
66
  type: 'backgroundType',
63
67
  id: 'id',
64
- src: 'url',
68
+ url: 'url',
65
69
  alt: 'alt',
66
70
  featuredImage: 'useFeaturedImage',
67
71
  },
@@ -73,6 +77,9 @@ if ( window.__experimentalContentOnlyPatternInsertion ) {
73
77
  },
74
78
  },
75
79
  ];
80
+ settings[ formKey ] = {
81
+ fields: [ 'content' ],
82
+ };
76
83
  }
77
84
 
78
85
  export const init = () => initBlock( { name, metadata, settings } );
@@ -16,6 +16,112 @@
16
16
  * @return string Returns the cover block markup, if useFeaturedImage is true.
17
17
  */
18
18
  function render_block_core_cover( $attributes, $content ) {
19
+ // Handle embed video background.
20
+ if (
21
+ isset( $attributes['backgroundType'] ) &&
22
+ 'embed-video' === $attributes['backgroundType'] &&
23
+ isset( $attributes['url'] ) &&
24
+ ! empty( $attributes['url'] ) &&
25
+ is_string( $attributes['url'] )
26
+ ) {
27
+ $url = $attributes['url'];
28
+
29
+ // Use WordPress's native oEmbed processing (includes caching).
30
+ $oembed_html = wp_oembed_get( $url );
31
+
32
+ if ( $oembed_html ) {
33
+ // Extract iframe src from the oEmbed HTML.
34
+ preg_match( '/src=["\']([^"\']+)["\']/', $oembed_html, $src_matches );
35
+ if ( ! empty( $src_matches[1] ) ) {
36
+ $iframe_src = $src_matches[1];
37
+
38
+ // Detect provider from iframe src URL.
39
+ $lower_src = strtolower( $iframe_src );
40
+ $provider = null;
41
+
42
+ if ( strpos( $lower_src, 'youtube.com' ) !== false || strpos( $lower_src, 'youtu.be' ) !== false ) {
43
+ $provider = 'youtube';
44
+ } elseif ( strpos( $lower_src, 'vimeo.com' ) !== false ) {
45
+ $provider = 'vimeo';
46
+ } elseif ( strpos( $lower_src, 'videopress.com' ) !== false ) {
47
+ $provider = 'videopress';
48
+ } elseif ( strpos( $lower_src, 'wordpress.tv' ) !== false ) {
49
+ $provider = 'wordpress-tv';
50
+ }
51
+
52
+ // Modify iframe src to add background video parameters based on provider.
53
+ $parsed_url = wp_parse_url( $iframe_src );
54
+ if ( $parsed_url && isset( $parsed_url['host'] ) ) {
55
+ // Parse existing query parameters.
56
+ $query_params = array();
57
+ if ( isset( $parsed_url['query'] ) ) {
58
+ parse_str( $parsed_url['query'], $query_params );
59
+ }
60
+
61
+ // Add background video parameters based on provider.
62
+ if ( 'youtube' === $provider ) {
63
+ $query_params['autoplay'] = '1';
64
+ $query_params['mute'] = '1';
65
+ $query_params['loop'] = '1';
66
+ $query_params['controls'] = '0';
67
+ $query_params['modestbranding'] = '1';
68
+ $query_params['playsinline'] = '1';
69
+ } elseif ( 'vimeo' === $provider ) {
70
+ $query_params['autoplay'] = '1';
71
+ $query_params['muted'] = '1';
72
+ $query_params['loop'] = '1';
73
+ $query_params['background'] = '1';
74
+ $query_params['controls'] = '0';
75
+ $query_params['transparent'] = '0';
76
+ } elseif ( 'videopress' === $provider || 'wordpress-tv' === $provider ) {
77
+ $query_params['autoplay'] = '1';
78
+ $query_params['loop'] = '1';
79
+ $query_params['muted'] = '1';
80
+ }
81
+
82
+ // Rebuild the URL with new parameters.
83
+ $iframe_src = $parsed_url['scheme'] . '://' . $parsed_url['host'];
84
+ if ( isset( $parsed_url['path'] ) ) {
85
+ $iframe_src .= $parsed_url['path'];
86
+ }
87
+ if ( ! empty( $query_params ) ) {
88
+ $iframe_src .= '?' . http_build_query( $query_params );
89
+ }
90
+ }
91
+
92
+ // Build the iframe HTML that will replace the figure.
93
+ $iframe_html = sprintf(
94
+ '<div class="wp-block-cover__video-background wp-block-cover__embed-background"><iframe src="%s" title="Background video" frameborder="0" allow="autoplay; fullscreen"></iframe></div>',
95
+ esc_url( $iframe_src )
96
+ );
97
+
98
+ // Use the HTML API to find and replace the figure.wp-block-embed element.
99
+ $processor = new WP_HTML_Tag_Processor( $content );
100
+
101
+ if ( $processor->next_tag(
102
+ array(
103
+ 'tag_name' => 'FIGURE',
104
+ 'class_name' => 'wp-block-embed',
105
+ )
106
+ ) ) {
107
+ // Use regex with PREG_OFFSET_CAPTURE to find the position of the figure element.
108
+ // This follows the same pattern used for featured image insertion below.
109
+ $figure_pattern = '/<figure\s+[^>]*\bwp-block-embed\b[^>]*>.*?<\/figure>/is';
110
+ if ( 1 === preg_match( $figure_pattern, $content, $matches, PREG_OFFSET_CAPTURE ) ) {
111
+ $figure_start = $matches[0][1];
112
+ $figure_length = strlen( $matches[0][0] );
113
+ $figure_end = $figure_start + $figure_length;
114
+
115
+ // Replace the figure element with the iframe HTML.
116
+ $content = substr( $content, 0, $figure_start ) . $iframe_html . substr( $content, $figure_end );
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ return $content;
123
+ }
124
+
19
125
  if ( 'image' !== $attributes['backgroundType'] || false === $attributes['useFeaturedImage'] ) {
20
126
  return $content;
21
127
  }
package/src/cover/save.js CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  import {
20
20
  IMAGE_BACKGROUND_TYPE,
21
21
  VIDEO_BACKGROUND_TYPE,
22
+ EMBED_VIDEO_BACKGROUND_TYPE,
22
23
  dimRatioToClass,
23
24
  isContentPositionCenter,
24
25
  getPositionClassName,
@@ -60,6 +61,8 @@ export default function save( { attributes } ) {
60
61
 
61
62
  const isImageBackground = IMAGE_BACKGROUND_TYPE === backgroundType;
62
63
  const isVideoBackground = VIDEO_BACKGROUND_TYPE === backgroundType;
64
+ const isEmbedVideoBackground =
65
+ EMBED_VIDEO_BACKGROUND_TYPE === backgroundType;
63
66
 
64
67
  const isImgElement = ! ( hasParallax || isRepeated );
65
68
 
@@ -144,6 +147,17 @@ export default function save( { attributes } ) {
144
147
  data-object-position={ objectPosition }
145
148
  />
146
149
  ) }
150
+ { isEmbedVideoBackground && url && (
151
+ <figure
152
+ className={ clsx(
153
+ 'wp-block-cover__video-background',
154
+ 'wp-block-cover__embed-background',
155
+ 'wp-block-embed'
156
+ ) }
157
+ >
158
+ <div className="wp-block-embed__wrapper">{ url }</div>
159
+ </figure>
160
+ ) }
147
161
 
148
162
  { /* The `wp-block-cover__background` needs to be immediately before
149
163
  the `wp-block-cover__inner-container`, so the exclusion CSS selector
@@ -18,6 +18,7 @@ const POSITION_CLASSNAMES = {
18
18
 
19
19
  export const IMAGE_BACKGROUND_TYPE = 'image';
20
20
  export const VIDEO_BACKGROUND_TYPE = 'video';
21
+ export const EMBED_VIDEO_BACKGROUND_TYPE = 'embed-video';
21
22
  export const COVER_MIN_HEIGHT = 50;
22
23
  export const COVER_MAX_HEIGHT = 1000;
23
24
  export const COVER_DEFAULT_HEIGHT = 300;
@@ -182,6 +182,49 @@
182
182
  border: none;
183
183
  box-shadow: none;
184
184
  }
185
+
186
+ .wp-block-cover__embed-background {
187
+ position: absolute;
188
+ top: 0;
189
+ left: 0;
190
+ right: 0;
191
+ bottom: 0;
192
+ margin: 0;
193
+ padding: 0;
194
+ width: 100%;
195
+ height: 100%;
196
+ max-width: none;
197
+ max-height: none;
198
+ outline: none;
199
+ border: none;
200
+ box-shadow: none;
201
+ pointer-events: none; // Prevent interaction with the iframe
202
+
203
+ .wp-block-embed__wrapper {
204
+ position: absolute;
205
+ top: 0;
206
+ left: 0;
207
+ right: 0;
208
+ bottom: 0;
209
+ margin: 0;
210
+ padding: 0;
211
+ width: 100%;
212
+ height: 100%;
213
+ }
214
+
215
+ iframe,
216
+ .wp-block-embed__wrapper iframe {
217
+ position: absolute;
218
+ top: 50%;
219
+ left: 50%;
220
+ width: 100vw;
221
+ height: 100vh;
222
+ min-width: 100%;
223
+ min-height: 100%;
224
+ transform: translate(-50%, -50%);
225
+ pointer-events: none; // Prevent interaction with the iframe
226
+ }
227
+ }
185
228
  }
186
229
 
187
230
  .wp-block-cover-image,
@@ -308,6 +351,10 @@ body:not(.editor-styles-wrapper) .wp-block-cover:not(
308
351
  z-index: z-index(".wp-block-cover__video-background");
309
352
  }
310
353
 
354
+ .wp-block-cover__embed-background {
355
+ z-index: z-index(".wp-block-cover__video-background");
356
+ }
357
+
311
358
  .wp-block-cover__image-background {
312
359
  z-index: z-index(".wp-block-cover__image-background");
313
360
  }
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { details as icon } from '@wordpress/icons';
5
5
  import { __, sprintf } from '@wordpress/i18n';
6
+ import { privateApis as blocksPrivateApis } from '@wordpress/blocks';
6
7
 
7
8
  /**
8
9
  * Internal dependencies
@@ -12,6 +13,9 @@ import metadata from './block.json';
12
13
  import edit from './edit';
13
14
  import save from './save';
14
15
  import transforms from './transforms';
16
+ import { unlock } from '../lock-unlock';
17
+
18
+ const { fieldsKey, formKey } = unlock( blocksPrivateApis );
15
19
 
16
20
  const { name } = metadata;
17
21
  export { metadata, name };
@@ -62,16 +66,16 @@ export const settings = {
62
66
  };
63
67
 
64
68
  if ( window.__experimentalContentOnlyPatternInsertion ) {
65
- settings.fields = [
69
+ settings[ fieldsKey ] = [
66
70
  {
71
+ id: 'summary',
67
72
  label: __( 'Summary' ),
68
- type: 'RichText',
69
- shownByDefault: true,
70
- mapping: {
71
- value: 'summary',
72
- },
73
+ type: 'richtext',
73
74
  },
74
75
  ];
76
+ settings[ formKey ] = {
77
+ fields: [ 'summary' ],
78
+ };
75
79
  }
76
80
 
77
81
  export const init = () => initBlock( { name, metadata, settings } );
package/src/file/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { _x, __ } from '@wordpress/i18n';
5
5
  import { file as icon } from '@wordpress/icons';
6
+ import { privateApis as blocksPrivateApis } from '@wordpress/blocks';
6
7
 
7
8
  /**
8
9
  * Internal dependencies
@@ -13,6 +14,9 @@ import edit from './edit';
13
14
  import metadata from './block.json';
14
15
  import save from './save';
15
16
  import transforms from './transforms';
17
+ import { unlock } from '../lock-unlock';
18
+
19
+ const { fieldsKey, formKey } = unlock( blocksPrivateApis );
16
20
 
17
21
  const { name } = metadata;
18
22
 
@@ -33,14 +37,14 @@ export const settings = {
33
37
  };
34
38
 
35
39
  if ( window.__experimentalContentOnlyPatternInsertion ) {
36
- settings.fields = [
40
+ settings[ fieldsKey ] = [
37
41
  {
42
+ id: 'file',
38
43
  label: __( 'File' ),
39
- type: 'Media',
40
- shownByDefault: true,
44
+ type: 'media',
41
45
  mapping: {
42
46
  id: 'id',
43
- src: 'href',
47
+ url: 'href',
44
48
  },
45
49
  args: {
46
50
  allowedTypes: [],
@@ -48,22 +52,19 @@ if ( window.__experimentalContentOnlyPatternInsertion ) {
48
52
  },
49
53
  },
50
54
  {
55
+ id: 'fileName',
51
56
  label: __( 'Filename' ),
52
- type: 'RichText',
53
- shownByDefault: false,
54
- mapping: {
55
- value: 'fileName',
56
- },
57
+ type: 'richtext',
57
58
  },
58
59
  {
60
+ id: 'downloadButtonText',
59
61
  label: __( 'Button Text' ),
60
- type: 'RichText',
61
- shownByDefault: false,
62
- mapping: {
63
- value: 'downloadButtonText',
64
- },
62
+ type: 'richtext',
65
63
  },
66
64
  ];
65
+ settings[ formKey ] = {
66
+ fields: [ 'file' ],
67
+ };
67
68
  }
68
69
 
69
70
  export const init = () => initBlock( { name, metadata, settings } );
@@ -19,7 +19,7 @@
19
19
  "lock": false,
20
20
  "reusable": false,
21
21
  "renaming": false,
22
- "blockVisibility": false
22
+ "visibility": false
23
23
  },
24
24
  "editorStyle": "wp-block-freeform-editor"
25
25
  }