@wordpress/block-library 10.2.0 → 10.3.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 (308) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/build/accordion-panel/block.json +1 -0
  3. package/build/code/edit.cjs +3 -1
  4. package/build/code/edit.cjs.map +2 -2
  5. package/build/cover/block.json +1 -0
  6. package/build/cover/edit/block-controls.cjs +3 -1
  7. package/build/cover/edit/block-controls.cjs.map +2 -2
  8. package/build/cover/edit/index.cjs +75 -31
  9. package/build/cover/edit/index.cjs.map +2 -2
  10. package/build/embed/edit.cjs +1 -1
  11. package/build/embed/edit.cjs.map +2 -2
  12. package/build/embed/embed-placeholder.cjs +1 -1
  13. package/build/embed/embed-placeholder.cjs.map +2 -2
  14. package/build/gallery/dynamic-gallery.cjs +104 -61
  15. package/build/gallery/dynamic-gallery.cjs.map +3 -3
  16. package/build/gallery/use-dynamic-gallery.cjs +15 -8
  17. package/build/gallery/use-dynamic-gallery.cjs.map +2 -2
  18. package/build/heading/edit.cjs +1 -1
  19. package/build/heading/edit.cjs.map +2 -2
  20. package/build/html/modal.cjs +63 -54
  21. package/build/html/modal.cjs.map +3 -3
  22. package/build/icon/block.json +5 -3
  23. package/build/image/edit.cjs +1 -1
  24. package/build/image/edit.cjs.map +2 -2
  25. package/build/image/image.cjs +29 -5
  26. package/build/image/image.cjs.map +3 -3
  27. package/build/image/use-open-image-media-editor-modal.cjs +6 -2
  28. package/build/image/use-open-image-media-editor-modal.cjs.map +2 -2
  29. package/build/list-item/hooks/use-indent-list-item.cjs +10 -2
  30. package/build/list-item/hooks/use-indent-list-item.cjs.map +2 -2
  31. package/build/math/edit.cjs +39 -56
  32. package/build/math/edit.cjs.map +3 -3
  33. package/build/media-text/edit.cjs +2 -1
  34. package/build/media-text/edit.cjs.map +2 -2
  35. package/build/more/edit.cjs +2 -2
  36. package/build/more/edit.cjs.map +2 -2
  37. package/build/navigation/edit/index.cjs +30 -16
  38. package/build/navigation/edit/index.cjs.map +3 -3
  39. package/build/navigation/edit/use-layout-custom-properties.cjs +72 -0
  40. package/build/navigation/edit/use-layout-custom-properties.cjs.map +7 -0
  41. package/build/navigation/view.cjs +9 -6
  42. package/build/navigation/view.cjs.map +2 -2
  43. package/build/navigation-link/edit.cjs +3 -3
  44. package/build/navigation-link/edit.cjs.map +2 -2
  45. package/build/navigation-link/link-ui/index.cjs +2 -2
  46. package/build/navigation-link/link-ui/index.cjs.map +2 -2
  47. package/build/paragraph/block.json +0 -1
  48. package/build/paragraph/edit.cjs +5 -2
  49. package/build/paragraph/edit.cjs.map +3 -3
  50. package/build/paragraph/index.cjs +4 -1
  51. package/build/paragraph/index.cjs.map +2 -2
  52. package/build/playlist/context.cjs +2 -1
  53. package/build/playlist/context.cjs.map +2 -2
  54. package/build/playlist/edit.cjs +113 -26
  55. package/build/playlist/edit.cjs.map +2 -2
  56. package/build/playlist/utils.cjs +19 -4
  57. package/build/playlist/utils.cjs.map +2 -2
  58. package/build/playlist-track/edit.cjs +21 -6
  59. package/build/playlist-track/edit.cjs.map +2 -2
  60. package/build/post-content/edit.cjs +36 -15
  61. package/build/post-content/edit.cjs.map +2 -2
  62. package/build/post-featured-image/edit.cjs +3 -2
  63. package/build/post-featured-image/edit.cjs.map +2 -2
  64. package/build/post-terms/edit.cjs +2 -2
  65. package/build/post-terms/edit.cjs.map +2 -2
  66. package/build/post-title/edit.cjs +2 -2
  67. package/build/post-title/edit.cjs.map +2 -2
  68. package/build/preformatted/edit.cjs +3 -1
  69. package/build/preformatted/edit.cjs.map +2 -2
  70. package/build/pullquote/edit.cjs +5 -3
  71. package/build/pullquote/edit.cjs.map +2 -2
  72. package/build/query/edit/inspector-controls/index.cjs +4 -1
  73. package/build/query/edit/inspector-controls/index.cjs.map +2 -2
  74. package/build/query/edit/inspector-controls/offset-controls.cjs +1 -1
  75. package/build/query/edit/inspector-controls/offset-controls.cjs.map +2 -2
  76. package/build/read-more/edit.cjs +3 -1
  77. package/build/read-more/edit.cjs.map +2 -2
  78. package/build/site-tagline/edit.cjs +3 -1
  79. package/build/site-tagline/edit.cjs.map +2 -2
  80. package/build/site-title/edit.cjs +3 -1
  81. package/build/site-title/edit.cjs.map +2 -2
  82. package/build/social-links/edit.cjs +11 -4
  83. package/build/social-links/edit.cjs.map +2 -2
  84. package/build/tab-list/block.json +2 -1
  85. package/build/tab-list/edit.cjs +2 -0
  86. package/build/tab-list/edit.cjs.map +3 -3
  87. package/build/tab-list/tab-movers.cjs +87 -0
  88. package/build/tab-list/tab-movers.cjs.map +7 -0
  89. package/build/tab-panel/edit.cjs +1 -9
  90. package/build/tab-panel/edit.cjs.map +2 -2
  91. package/build/table/edit.cjs +1 -1
  92. package/build/table/edit.cjs.map +2 -2
  93. package/build/tabs/use-tab-actions.cjs +23 -1
  94. package/build/tabs/use-tab-actions.cjs.map +2 -2
  95. package/build/utils/caption.cjs +2 -2
  96. package/build/utils/caption.cjs.map +2 -2
  97. package/build/verse/edit.cjs +3 -1
  98. package/build/verse/edit.cjs.map +2 -2
  99. package/build/video/tracks-editor.cjs +0 -1
  100. package/build/video/tracks-editor.cjs.map +2 -2
  101. package/build-module/accordion-panel/block.json +1 -0
  102. package/build-module/code/edit.mjs +3 -1
  103. package/build-module/code/edit.mjs.map +2 -2
  104. package/build-module/cover/block.json +1 -0
  105. package/build-module/cover/edit/block-controls.mjs +3 -1
  106. package/build-module/cover/edit/block-controls.mjs.map +2 -2
  107. package/build-module/cover/edit/index.mjs +78 -34
  108. package/build-module/cover/edit/index.mjs.map +2 -2
  109. package/build-module/embed/edit.mjs +1 -1
  110. package/build-module/embed/edit.mjs.map +2 -2
  111. package/build-module/embed/embed-placeholder.mjs +1 -1
  112. package/build-module/embed/embed-placeholder.mjs.map +2 -2
  113. package/build-module/gallery/dynamic-gallery.mjs +104 -61
  114. package/build-module/gallery/dynamic-gallery.mjs.map +2 -2
  115. package/build-module/gallery/use-dynamic-gallery.mjs +16 -9
  116. package/build-module/gallery/use-dynamic-gallery.mjs.map +2 -2
  117. package/build-module/heading/edit.mjs +1 -1
  118. package/build-module/heading/edit.mjs.map +2 -2
  119. package/build-module/html/modal.mjs +68 -55
  120. package/build-module/html/modal.mjs.map +2 -2
  121. package/build-module/icon/block.json +5 -3
  122. package/build-module/image/edit.mjs +1 -1
  123. package/build-module/image/edit.mjs.map +2 -2
  124. package/build-module/image/image.mjs +19 -5
  125. package/build-module/image/image.mjs.map +2 -2
  126. package/build-module/image/use-open-image-media-editor-modal.mjs +6 -2
  127. package/build-module/image/use-open-image-media-editor-modal.mjs.map +2 -2
  128. package/build-module/list-item/hooks/use-indent-list-item.mjs +10 -2
  129. package/build-module/list-item/hooks/use-indent-list-item.mjs.map +2 -2
  130. package/build-module/math/edit.mjs +41 -60
  131. package/build-module/math/edit.mjs.map +2 -2
  132. package/build-module/media-text/edit.mjs +2 -1
  133. package/build-module/media-text/edit.mjs.map +2 -2
  134. package/build-module/more/edit.mjs +2 -2
  135. package/build-module/more/edit.mjs.map +2 -2
  136. package/build-module/navigation/edit/index.mjs +30 -16
  137. package/build-module/navigation/edit/index.mjs.map +2 -2
  138. package/build-module/navigation/edit/use-layout-custom-properties.mjs +51 -0
  139. package/build-module/navigation/edit/use-layout-custom-properties.mjs.map +7 -0
  140. package/build-module/navigation/view.mjs +9 -6
  141. package/build-module/navigation/view.mjs.map +2 -2
  142. package/build-module/navigation-link/edit.mjs +3 -3
  143. package/build-module/navigation-link/edit.mjs.map +2 -2
  144. package/build-module/navigation-link/link-ui/index.mjs +2 -2
  145. package/build-module/navigation-link/link-ui/index.mjs.map +2 -2
  146. package/build-module/paragraph/block.json +0 -1
  147. package/build-module/paragraph/edit.mjs +6 -2
  148. package/build-module/paragraph/edit.mjs.map +2 -2
  149. package/build-module/paragraph/index.mjs +4 -1
  150. package/build-module/paragraph/index.mjs.map +2 -2
  151. package/build-module/playlist/context.mjs +2 -1
  152. package/build-module/playlist/context.mjs.map +2 -2
  153. package/build-module/playlist/edit.mjs +113 -27
  154. package/build-module/playlist/edit.mjs.map +2 -2
  155. package/build-module/playlist/utils.mjs +19 -4
  156. package/build-module/playlist/utils.mjs.map +2 -2
  157. package/build-module/playlist-track/edit.mjs +21 -6
  158. package/build-module/playlist-track/edit.mjs.map +2 -2
  159. package/build-module/post-content/edit.mjs +37 -15
  160. package/build-module/post-content/edit.mjs.map +2 -2
  161. package/build-module/post-featured-image/edit.mjs +3 -2
  162. package/build-module/post-featured-image/edit.mjs.map +2 -2
  163. package/build-module/post-terms/edit.mjs +2 -2
  164. package/build-module/post-terms/edit.mjs.map +2 -2
  165. package/build-module/post-title/edit.mjs +2 -2
  166. package/build-module/post-title/edit.mjs.map +2 -2
  167. package/build-module/preformatted/edit.mjs +3 -1
  168. package/build-module/preformatted/edit.mjs.map +2 -2
  169. package/build-module/pullquote/edit.mjs +5 -3
  170. package/build-module/pullquote/edit.mjs.map +2 -2
  171. package/build-module/query/edit/inspector-controls/index.mjs +5 -2
  172. package/build-module/query/edit/inspector-controls/index.mjs.map +2 -2
  173. package/build-module/query/edit/inspector-controls/offset-controls.mjs +2 -2
  174. package/build-module/query/edit/inspector-controls/offset-controls.mjs.map +2 -2
  175. package/build-module/read-more/edit.mjs +3 -1
  176. package/build-module/read-more/edit.mjs.map +2 -2
  177. package/build-module/site-tagline/edit.mjs +3 -1
  178. package/build-module/site-tagline/edit.mjs.map +2 -2
  179. package/build-module/site-title/edit.mjs +3 -1
  180. package/build-module/site-title/edit.mjs.map +2 -2
  181. package/build-module/social-links/edit.mjs +11 -4
  182. package/build-module/social-links/edit.mjs.map +2 -2
  183. package/build-module/tab-list/block.json +2 -1
  184. package/build-module/tab-list/edit.mjs +2 -0
  185. package/build-module/tab-list/edit.mjs.map +2 -2
  186. package/build-module/tab-list/tab-movers.mjs +59 -0
  187. package/build-module/tab-list/tab-movers.mjs.map +7 -0
  188. package/build-module/tab-panel/edit.mjs +1 -9
  189. package/build-module/tab-panel/edit.mjs.map +2 -2
  190. package/build-module/table/edit.mjs +1 -1
  191. package/build-module/table/edit.mjs.map +2 -2
  192. package/build-module/tabs/use-tab-actions.mjs +23 -1
  193. package/build-module/tabs/use-tab-actions.mjs.map +2 -2
  194. package/build-module/utils/caption.mjs +2 -2
  195. package/build-module/utils/caption.mjs.map +2 -2
  196. package/build-module/verse/edit.mjs +3 -1
  197. package/build-module/verse/edit.mjs.map +2 -2
  198. package/build-module/video/tracks-editor.mjs +0 -1
  199. package/build-module/video/tracks-editor.mjs.map +2 -2
  200. package/build-style/cover/editor-rtl.css +3 -0
  201. package/build-style/cover/editor.css +3 -0
  202. package/build-style/editor-rtl.css +18 -41
  203. package/build-style/editor.css +18 -41
  204. package/build-style/icon/editor-rtl.css +1 -4
  205. package/build-style/icon/editor.css +1 -4
  206. package/build-style/image/editor-rtl.css +3 -32
  207. package/build-style/image/editor.css +3 -32
  208. package/build-style/playlist/editor-rtl.css +0 -5
  209. package/build-style/playlist/editor.css +0 -5
  210. package/build-style/playlist/style-rtl.css +44 -4
  211. package/build-style/playlist/style.css +44 -4
  212. package/build-style/pullquote/editor-rtl.css +4 -0
  213. package/build-style/pullquote/editor.css +4 -0
  214. package/build-style/style-rtl.css +44 -4
  215. package/build-style/style.css +44 -4
  216. package/build-style/tab-list/editor-rtl.css +101 -0
  217. package/build-style/tab-list/editor.css +101 -0
  218. package/package.json +43 -43
  219. package/src/accordion-panel/block.json +1 -0
  220. package/src/breadcrumbs/index.php +3 -1
  221. package/src/button/index.php +1 -1
  222. package/src/code/edit.js +7 -2
  223. package/src/cover/block.json +1 -0
  224. package/src/cover/edit/block-controls.js +5 -0
  225. package/src/cover/edit/index.js +100 -35
  226. package/src/cover/editor.scss +6 -0
  227. package/src/cover/index.php +9 -2
  228. package/src/editor.scss +1 -0
  229. package/src/embed/edit.js +3 -1
  230. package/src/embed/embed-placeholder.js +9 -7
  231. package/src/form/index.php +6 -4
  232. package/src/gallery/dynamic-gallery.js +133 -74
  233. package/src/gallery/use-dynamic-gallery.js +27 -12
  234. package/src/heading/edit.js +1 -1
  235. package/src/html/modal.js +13 -2
  236. package/src/icon/README.md +1 -3
  237. package/src/icon/block.json +5 -3
  238. package/src/icon/editor.scss +1 -4
  239. package/src/image/edit.js +1 -5
  240. package/src/image/editor.scss +6 -46
  241. package/src/image/image.js +37 -3
  242. package/src/image/test/use-open-image-media-editor-modal.js +37 -1
  243. package/src/image/use-open-image-media-editor-modal.js +8 -1
  244. package/src/latest-posts/index.php +3 -2
  245. package/src/list-item/hooks/use-indent-list-item.js +10 -1
  246. package/src/math/edit.js +42 -59
  247. package/src/media-text/edit.js +1 -0
  248. package/src/media-text/index.php +11 -2
  249. package/src/more/edit.js +7 -4
  250. package/src/navigation/edit/index.js +52 -36
  251. package/src/navigation/edit/test/layout-custom-properties.js +138 -0
  252. package/src/navigation/edit/use-layout-custom-properties.js +84 -0
  253. package/src/navigation/index.php +123 -12
  254. package/src/navigation/test/view.js +51 -0
  255. package/src/navigation/view.js +15 -14
  256. package/src/navigation-link/edit.js +11 -7
  257. package/src/navigation-link/index.php +64 -0
  258. package/src/navigation-link/link-ui/index.js +2 -1
  259. package/src/navigation-link/test/__snapshots__/hooks.js.snap +1 -1
  260. package/src/page-list/index.php +4 -3
  261. package/src/paragraph/README.md +0 -1
  262. package/src/paragraph/block.json +0 -1
  263. package/src/paragraph/edit.js +9 -2
  264. package/src/paragraph/index.js +4 -1
  265. package/src/pattern/index.php +5 -5
  266. package/src/playlist/context.js +1 -0
  267. package/src/playlist/edit.js +151 -47
  268. package/src/playlist/editor.scss +0 -7
  269. package/src/playlist/index.php +14 -9
  270. package/src/playlist/style.scss +3 -0
  271. package/src/playlist/test/edit-component.js +139 -0
  272. package/src/playlist/test/edit.js +30 -0
  273. package/src/playlist/utils.js +25 -4
  274. package/src/playlist-track/edit.js +22 -5
  275. package/src/playlist-track/index.php +8 -7
  276. package/src/playlist-track/test/edit.js +87 -3
  277. package/src/post-content/edit.js +45 -16
  278. package/src/post-excerpt/index.php +4 -1
  279. package/src/post-featured-image/edit.js +3 -2
  280. package/src/post-featured-image/index.php +4 -1
  281. package/src/post-terms/edit.js +7 -4
  282. package/src/post-title/edit.js +5 -3
  283. package/src/preformatted/edit.js +7 -2
  284. package/src/pullquote/edit.js +9 -4
  285. package/src/pullquote/editor.scss +6 -0
  286. package/src/query/edit/inspector-controls/index.js +5 -2
  287. package/src/query/edit/inspector-controls/offset-controls.js +2 -2
  288. package/src/read-more/edit.js +7 -2
  289. package/src/rss/index.php +9 -3
  290. package/src/site-tagline/edit.js +7 -2
  291. package/src/site-title/edit.js +7 -2
  292. package/src/social-link/index.php +19 -9
  293. package/src/social-links/edit.js +21 -12
  294. package/src/tab-list/block.json +2 -1
  295. package/src/tab-list/edit.js +2 -0
  296. package/src/tab-list/editor.scss +9 -0
  297. package/src/tab-list/tab-movers.js +72 -0
  298. package/src/tab-panel/edit.js +1 -9
  299. package/src/table/edit.js +1 -1
  300. package/src/table-of-contents/index.php +386 -1
  301. package/src/tabs/use-tab-actions.js +32 -3
  302. package/src/tag-cloud/index.php +3 -2
  303. package/src/template-part/index.php +2 -1
  304. package/src/utils/caption.js +7 -4
  305. package/src/utils/test/waveform-utils.js +63 -0
  306. package/src/verse/edit.js +7 -2
  307. package/src/video/index.php +2 -2
  308. package/src/video/tracks-editor.js +0 -1
@@ -80,6 +80,66 @@ function block_core_navigation_link_build_css_colors( $context, $attributes, $is
80
80
  return $colors;
81
81
  }
82
82
 
83
+ /**
84
+ * Build an array with CSS classes and inline styles defining the font sizes
85
+ * which will be applied to the navigation markup in the front-end.
86
+ *
87
+ * This function is no longer used internally and is kept only for backward
88
+ * compatibility with third-party code that may call it directly.
89
+ *
90
+ * @since 5.9.0
91
+ * @deprecated 7.0.0
92
+ *
93
+ * @param array $context Navigation block context.
94
+ * @return array Font size CSS classes and inline styles.
95
+ */
96
+ function block_core_navigation_link_build_css_font_sizes( $context ) {
97
+ _deprecated_function( __FUNCTION__, '7.0.0' );
98
+
99
+ // CSS classes.
100
+ $font_sizes = array(
101
+ 'css_classes' => array(),
102
+ 'inline_styles' => '',
103
+ );
104
+
105
+ $has_named_font_size = array_key_exists( 'fontSize', $context );
106
+ $has_custom_font_size = isset( $context['style']['typography']['fontSize'] );
107
+
108
+ if ( $has_named_font_size ) {
109
+ // Add the font size class.
110
+ $font_sizes['css_classes'][] = sprintf( 'has-%s-font-size', $context['fontSize'] );
111
+ } elseif ( $has_custom_font_size ) {
112
+ // Add the custom font size inline style.
113
+ $font_sizes['inline_styles'] = sprintf(
114
+ 'font-size: %s;',
115
+ wp_get_typography_font_size_value(
116
+ array(
117
+ 'size' => $context['style']['typography']['fontSize'],
118
+ )
119
+ )
120
+ );
121
+ }
122
+
123
+ return $font_sizes;
124
+ }
125
+
126
+ /**
127
+ * Returns the top-level submenu SVG chevron icon.
128
+ *
129
+ * @since 5.9.0
130
+ * @deprecated 7.0.0 Use block_core_shared_navigation_render_submenu_icon() instead.
131
+ *
132
+ * @return string
133
+ */
134
+ function block_core_navigation_link_render_submenu_icon() {
135
+ _deprecated_function(
136
+ __FUNCTION__,
137
+ '7.0.0',
138
+ 'block_core_shared_navigation_render_submenu_icon()'
139
+ );
140
+ return block_core_shared_navigation_render_submenu_icon();
141
+ }
142
+
83
143
  /**
84
144
  * Decodes a url if it's encoded, returning the same url if not.
85
145
  *
@@ -90,6 +150,10 @@ function block_core_navigation_link_build_css_colors( $context, $attributes, $is
90
150
  * @return string $url Returns the decoded url.
91
151
  */
92
152
  function block_core_navigation_link_maybe_urldecode( $url ) {
153
+ if ( ! is_string( $url ) ) {
154
+ return '';
155
+ }
156
+
93
157
  $is_url_encoded = false;
94
158
  $query = parse_url( $url, PHP_URL_QUERY );
95
159
  $query_params = wp_parse_args( $query );
@@ -248,11 +248,12 @@ function UnforwardedLinkUI( props, ref ) {
248
248
  setAddingPage( true );
249
249
  } }
250
250
  canAddPage={
251
+ blockEditingMode !== 'disabled' &&
251
252
  permissions?.canCreate &&
252
253
  type === 'page'
253
254
  }
254
255
  canAddBlock={
255
- blockEditingMode === 'default'
256
+ blockEditingMode !== 'disabled'
256
257
  }
257
258
  />
258
259
  );
@@ -1,4 +1,4 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
1
+ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2
2
 
3
3
  exports[`hooks enhanceNavigationLinkVariations enhances variations with icon and isActive functions 1`] = `
4
4
  {
@@ -9,7 +9,7 @@
9
9
  * Returns the submenu visibility value with backward compatibility
10
10
  * for the deprecated openSubmenusOnClick attribute.
11
11
  *
12
- * @since 6.9.0
12
+ * @since 7.0.0
13
13
  *
14
14
  * @param array $context Block context from parent Navigation block.
15
15
  * @return string The visibility mode: 'hover', 'click', or 'always'.
@@ -258,8 +258,9 @@ function render_block_core_page_list( $attributes, $content, $block ) {
258
258
  static $block_id = 0;
259
259
  ++$block_id;
260
260
 
261
- $parent_page_id = $attributes['parentPageID'];
262
- $is_nested = ! empty( $block->context['core/isInsideSubmenu'] );
261
+ $parent_page_id_attr = $attributes['parentPageID'] ?? null;
262
+ $parent_page_id = is_scalar( $parent_page_id_attr ) ? (int) $parent_page_id_attr : 0;
263
+ $is_nested = ! empty( $block->context['core/isInsideSubmenu'] );
263
264
 
264
265
  $all_pages = get_pages(
265
266
  array(
@@ -26,7 +26,6 @@ _Defined via the [`supports`](https://developer.wordpress.org/block-editor/refer
26
26
 
27
27
  - [`align`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#align): `"wide"`, `"full"`
28
28
  - [`splitting`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#splitting): `true`
29
- - [`editableRoot`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#editableroot): `true`
30
29
  - [`anchor`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#anchor): `true`
31
30
  - [`className`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#classname): `false`
32
31
  - [`color`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#color):
@@ -29,7 +29,6 @@
29
29
  "supports": {
30
30
  "align": [ "wide", "full" ],
31
31
  "splitting": true,
32
- "editableRoot": true,
33
32
  "anchor": true,
34
33
  "className": false,
35
34
  "__experimentalBorder": {
@@ -19,11 +19,14 @@ import {
19
19
  useBlockProps,
20
20
  useSettings,
21
21
  useBlockEditingMode,
22
+ privateApis as blockEditorPrivateApis,
22
23
  store as blockEditorStore,
23
24
  } from '@wordpress/block-editor';
24
25
  import { useSelect } from '@wordpress/data';
25
26
  import { getBlockSupport } from '@wordpress/blocks';
26
27
  import { formatLTR } from '@wordpress/icons';
28
+ import { useContext } from '@wordpress/element';
29
+
27
30
  /**
28
31
  * Internal dependencies
29
32
  */
@@ -31,6 +34,8 @@ import { useOnEnter } from './use-enter';
31
34
  import useDeprecatedAlign from './deprecated-attributes';
32
35
  import { unlock } from '../lock-unlock';
33
36
 
37
+ const { PrivateBlockContext } = unlock( blockEditorPrivateApis );
38
+
34
39
  function ParagraphRTLControl( { direction, setDirection } ) {
35
40
  return (
36
41
  isRTL() && (
@@ -131,6 +136,7 @@ function ParagraphBlock( {
131
136
  style: { direction },
132
137
  } );
133
138
  const blockEditingMode = useBlockEditingMode();
139
+ const { ariaLabel } = useContext( PrivateBlockContext );
134
140
 
135
141
  return (
136
142
  <>
@@ -164,11 +170,12 @@ function ParagraphBlock( {
164
170
  onReplace={ onReplace }
165
171
  onRemove={ onRemove }
166
172
  aria-label={
167
- RichText.isEmpty( content )
173
+ ariaLabel ??
174
+ ( RichText.isEmpty( content )
168
175
  ? __(
169
176
  'Empty block; start writing or type forward slash to choose a block'
170
177
  )
171
- : __( 'Block: Paragraph' )
178
+ : __( 'Block: Paragraph' ) )
172
179
  }
173
180
  data-empty={ RichText.isEmpty( content ) }
174
181
  placeholder={ placeholder || __( 'Type / to choose a block' ) }
@@ -16,7 +16,7 @@ import save from './save';
16
16
  import transforms from './transforms';
17
17
  import { unlock } from '../lock-unlock';
18
18
 
19
- const { fieldsKey, formKey } = unlock( blocksPrivateApis );
19
+ const { fieldsKey, formKey, editableRootKey } = unlock( blocksPrivateApis );
20
20
 
21
21
  const { name } = metadata;
22
22
 
@@ -24,6 +24,9 @@ export { metadata, name };
24
24
 
25
25
  export const settings = {
26
26
  icon,
27
+ // Opt into the editing host behaviour privately. It's a Symbol setting
28
+ // rather than a public `supports` key so it stays an internal detail.
29
+ [ editableRootKey ]: true,
27
30
  example: {
28
31
  attributes: {
29
32
  content: __(
@@ -33,18 +33,18 @@ function register_block_core_pattern() {
33
33
  function render_block_core_pattern( $attributes ) {
34
34
  static $seen_refs = array();
35
35
 
36
- if ( empty( $attributes['slug'] ) ) {
36
+ $slug = $attributes['slug'] ?? null;
37
+ if ( empty( $slug ) || ! is_string( $slug ) ) {
37
38
  return '';
38
39
  }
39
40
 
40
- $slug = $attributes['slug'];
41
41
  $registry = WP_Block_Patterns_Registry::get_instance();
42
42
 
43
43
  if ( ! $registry->is_registered( $slug ) ) {
44
44
  return '';
45
45
  }
46
46
 
47
- if ( isset( $seen_refs[ $attributes['slug'] ] ) ) {
47
+ if ( isset( $seen_refs[ $slug ] ) ) {
48
48
  // WP_DEBUG_DISPLAY must only be honored when WP_DEBUG. This precedent
49
49
  // is set in `wp_debug_mode()`.
50
50
  $is_debug = WP_DEBUG && WP_DEBUG_DISPLAY;
@@ -58,14 +58,14 @@ function render_block_core_pattern( $attributes ) {
58
58
  $pattern = $registry->get_registered( $slug );
59
59
  $content = $pattern['content'];
60
60
 
61
- $seen_refs[ $attributes['slug'] ] = true;
61
+ $seen_refs[ $slug ] = true;
62
62
 
63
63
  $content = do_blocks( $content );
64
64
 
65
65
  global $wp_embed;
66
66
  $content = $wp_embed->autoembed( $content );
67
67
 
68
- unset( $seen_refs[ $attributes['slug'] ] );
68
+ unset( $seen_refs[ $slug ] );
69
69
  return $content;
70
70
  }
71
71
 
@@ -6,4 +6,5 @@ import { createContext } from '@wordpress/element';
6
6
  export const PlaylistContext = createContext( {
7
7
  currentTrackClientId: null,
8
8
  setCurrentTrackClientId: () => {},
9
+ addTracks: undefined,
9
10
  } );
@@ -16,7 +16,6 @@ import {
16
16
  useInnerBlocksProps,
17
17
  BlockControls,
18
18
  InspectorControls,
19
- InnerBlocks,
20
19
  __experimentalColorGradientSettingsDropdown as ColorGradientSettingsDropdown,
21
20
  __experimentalUseMultipleOriginColorsAndGradients as useMultipleOriginColorsAndGradients,
22
21
  } from '@wordpress/block-editor';
@@ -32,6 +31,7 @@ import { store as noticesStore } from '@wordpress/notices';
32
31
  import { __, _x } from '@wordpress/i18n';
33
32
  import { playlist as icon } from '@wordpress/icons';
34
33
  import { createBlock } from '@wordpress/blocks';
34
+ import { createBlobURL } from '@wordpress/blob';
35
35
 
36
36
  /**
37
37
  * Internal dependencies
@@ -43,7 +43,10 @@ import { PlaylistContext } from './context';
43
43
  import { getTrackAttributes } from './utils';
44
44
 
45
45
  const ALLOWED_MEDIA_TYPES = [ 'audio' ];
46
+ const AUDIO_FILE_EXTENSION =
47
+ /\.(aac|aif|aiff|flac|m4a|m4b|mp3|oga|ogg|opus|wav|weba)$/i;
46
48
  const DEFAULT_WAVEFORM_STYLE = 'bars';
49
+ const FILE_LIST_OBJECT_NAME = '[object FileList]';
47
50
  const WAVEFORM_STYLE_OPTIONS = [
48
51
  { label: _x( 'Bars', 'waveform style option' ), value: 'bars' },
49
52
  { label: _x( 'Mirror', 'waveform style option' ), value: 'mirror' },
@@ -53,6 +56,23 @@ const WAVEFORM_STYLE_OPTIONS = [
53
56
  { label: _x( 'Seekbar', 'waveform style option' ), value: 'seekbar' },
54
57
  ];
55
58
 
59
+ function isFile( value ) {
60
+ return (
61
+ Object.prototype.toString.call( value ) === '[object File]' ||
62
+ ( typeof File !== 'undefined' && value instanceof File )
63
+ );
64
+ }
65
+
66
+ function isAudioFile( file ) {
67
+ return file.type
68
+ ? file.type.startsWith( 'audio/' )
69
+ : AUDIO_FILE_EXTENSION.test( file.name );
70
+ }
71
+
72
+ function getTrackIdentifier( track ) {
73
+ return track.id ?? track.src ?? track.blob;
74
+ }
75
+
56
76
  const PlaylistEdit = ( {
57
77
  attributes,
58
78
  setAttributes,
@@ -77,7 +97,7 @@ const PlaylistEdit = ( {
77
97
 
78
98
  const blockProps = useBlockProps();
79
99
  const waveformPanelId = `${ clientId }-waveform`;
80
- const { replaceInnerBlocks } = useDispatch( blockEditorStore );
100
+ const { replaceInnerBlocks, selectBlock } = useDispatch( blockEditorStore );
81
101
  const { createErrorNotice } = useDispatch( noticesStore );
82
102
  const dropdownMenuProps = useToolsPanelDropdownMenuProps();
83
103
  const colorGradientSettings = useMultipleOriginColorsAndGradients();
@@ -103,9 +123,12 @@ const PlaylistEdit = ( {
103
123
  const waveformBackgroundGradientValue = waveformBackgroundGradient;
104
124
  let waveformColorGradientChange;
105
125
  let waveformBackgroundColorGradientChange;
106
- function onUploadError( message ) {
107
- createErrorNotice( message, { type: 'snackbar' } );
108
- }
126
+ const onUploadError = useCallback(
127
+ ( message ) => {
128
+ createErrorNotice( message, { type: 'snackbar' } );
129
+ },
130
+ [ createErrorNotice ]
131
+ );
109
132
  const [ currentTrackClientId, setCurrentTrackClientId ] = useState( null );
110
133
 
111
134
  const { innerBlockTracks } = useSelect(
@@ -152,31 +175,112 @@ const PlaylistEdit = ( {
152
175
  }
153
176
  }, [ currentTrackClientId, setCurrentTrackClientId, validTracks ] );
154
177
 
155
- const playlistContext = useMemo(
156
- () => ( { currentTrackClientId, setCurrentTrackClientId } ),
157
- [ currentTrackClientId, setCurrentTrackClientId ]
158
- );
159
-
160
- const onSelectTracks = useCallback(
178
+ const createTrackBlocks = useCallback(
161
179
  ( media ) => {
162
180
  if ( ! media ) {
163
- return;
181
+ return [];
164
182
  }
165
183
 
166
- if ( ! Array.isArray( media ) ) {
167
- media = [ media ];
184
+ let mediaItems = [ media ];
185
+ if (
186
+ Object.prototype.toString.call( media ) ===
187
+ FILE_LIST_OBJECT_NAME
188
+ ) {
189
+ mediaItems = Array.from( media );
190
+ } else if ( Array.isArray( media ) ) {
191
+ mediaItems = media;
168
192
  }
193
+ let hasInvalidFile = false;
194
+
195
+ const blocks = mediaItems
196
+ .map( ( mediaItem ) => {
197
+ if ( isFile( mediaItem ) ) {
198
+ if ( ! isAudioFile( mediaItem ) ) {
199
+ hasInvalidFile = true;
200
+ return null;
201
+ }
169
202
 
170
- const trackList = media.map( getTrackAttributes );
203
+ return createBlock( 'core/playlist-track', {
204
+ blob: createBlobURL( mediaItem ),
205
+ title: mediaItem.name,
206
+ } );
207
+ }
208
+
209
+ const track = getTrackAttributes( mediaItem );
210
+
211
+ return track.src
212
+ ? createBlock( 'core/playlist-track', track )
213
+ : null;
214
+ } )
215
+ .filter( Boolean );
216
+
217
+ if ( hasInvalidFile ) {
218
+ onUploadError(
219
+ __( 'Only audio files can be added to a playlist.' )
220
+ );
221
+ }
222
+
223
+ return blocks;
224
+ },
225
+ [ onUploadError ]
226
+ );
227
+
228
+ const onSelectTracks = useCallback(
229
+ ( media ) => {
230
+ const newBlocks = createTrackBlocks( media );
231
+ if ( newBlocks.length === 0 ) {
232
+ return;
233
+ }
171
234
 
172
- const newBlocks = trackList.map( ( track ) =>
173
- createBlock( 'core/playlist-track', track )
174
- );
175
235
  setCurrentTrackClientId( newBlocks[ 0 ]?.clientId ?? null );
176
236
  // Replace the inner blocks with the new tracks.
177
237
  replaceInnerBlocks( clientId, newBlocks );
178
238
  },
179
- [ replaceInnerBlocks, clientId, setCurrentTrackClientId ]
239
+ [
240
+ clientId,
241
+ createTrackBlocks,
242
+ replaceInnerBlocks,
243
+ setCurrentTrackClientId,
244
+ ]
245
+ );
246
+
247
+ const onAddTracks = useCallback(
248
+ ( media ) => {
249
+ const existingIds = new Set(
250
+ validTracks
251
+ .map( ( block ) => getTrackIdentifier( block.attributes ) )
252
+ .filter( Boolean )
253
+ );
254
+ const newBlocks = createTrackBlocks( media ).filter(
255
+ ( block ) =>
256
+ ! existingIds.has( getTrackIdentifier( block.attributes ) )
257
+ );
258
+ if ( newBlocks.length === 0 ) {
259
+ return;
260
+ }
261
+
262
+ const nextBlocks = [ ...validTracks, ...newBlocks ];
263
+ setCurrentTrackClientId( newBlocks[ 0 ].clientId );
264
+ replaceInnerBlocks( clientId, nextBlocks );
265
+ selectBlock( newBlocks[ 0 ].clientId );
266
+ },
267
+ [
268
+ clientId,
269
+ createTrackBlocks,
270
+ replaceInnerBlocks,
271
+ selectBlock,
272
+ setCurrentTrackClientId,
273
+ validTracks,
274
+ ]
275
+ );
276
+
277
+ const playlistContext = useMemo(
278
+ () => ( {
279
+ currentTrackClientId,
280
+ setCurrentTrackClientId,
281
+ addTracks: onAddTracks,
282
+ } ),
283
+ [ currentTrackClientId, onAddTracks, setCurrentTrackClientId ]
180
284
  );
181
285
 
182
286
  // Get current track data by finding the track with matching client ID.
@@ -305,14 +409,6 @@ const PlaylistEdit = ( {
305
409
  } );
306
410
  }
307
411
 
308
- const hasSelectedChild = useSelect(
309
- ( select ) =>
310
- select( blockEditorStore ).hasSelectedInnerBlock( clientId ),
311
- [ clientId ]
312
- );
313
-
314
- const hasAnySelected = isSelected || hasSelectedChild;
315
-
316
412
  const colorSettings = [];
317
413
  if ( hasColors || hasGradients ) {
318
414
  colorSettings.push(
@@ -357,7 +453,7 @@ const PlaylistEdit = ( {
357
453
 
358
454
  const innerBlocksProps = useInnerBlocksProps( blockProps, {
359
455
  __experimentalAppenderTagName: 'li',
360
- renderAppender: hasAnySelected && InnerBlocks.ButtonBlockAppender,
456
+ renderAppender: false,
361
457
  } );
362
458
 
363
459
  if ( tracks.length === 0 ) {
@@ -377,6 +473,7 @@ const PlaylistEdit = ( {
377
473
  onSelect={ onSelectTracks }
378
474
  accept="audio/*"
379
475
  multiple
476
+ handleUpload={ false }
380
477
  allowedTypes={ ALLOWED_MEDIA_TYPES }
381
478
  onError={ onUploadError }
382
479
  />
@@ -388,13 +485,11 @@ const PlaylistEdit = ( {
388
485
  <>
389
486
  <BlockControls group="other">
390
487
  <MediaReplaceFlow
391
- name={ __( 'Edit' ) }
392
- onSelect={ onSelectTracks }
488
+ name={ __( 'Add' ) }
489
+ onSelect={ onAddTracks }
393
490
  accept="audio/*"
394
491
  multiple
395
- mediaIds={ tracks
396
- .filter( ( track ) => track.id )
397
- .map( ( track ) => track.id ) }
492
+ handleUpload={ false }
398
493
  allowedTypes={ ALLOWED_MEDIA_TYPES }
399
494
  onError={ onUploadError }
400
495
  />
@@ -585,6 +680,15 @@ const PlaylistEdit = ( {
585
680
  </ToolsPanel>
586
681
  </InspectorControls>
587
682
  <figure { ...blockProps }>
683
+ <MediaPlaceholder
684
+ onSelect={ onAddTracks }
685
+ accept="audio/*"
686
+ multiple
687
+ handleUpload={ false }
688
+ disableMediaButtons
689
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
690
+ onError={ onUploadError }
691
+ />
588
692
  <Disabled isDisabled={ ! isSelected }>
589
693
  <WaveformPlayer
590
694
  src={ currentTrackData?.src }
@@ -601,20 +705,20 @@ const PlaylistEdit = ( {
601
705
  showPlayButtonArtwork={ showPlayButtonArtwork === true }
602
706
  />
603
707
  </Disabled>
604
- { showTracklist && (
605
- <ol
606
- className={ clsx( 'wp-block-playlist__tracklist', {
607
- 'wp-block-playlist__tracklist-show-numbers':
608
- showNumbers,
609
- 'wp-block-playlist__tracklist-length-is-hidden':
610
- ! showTrackLength,
611
- } ) }
612
- >
613
- <PlaylistContext.Provider value={ playlistContext }>
614
- { innerBlocksProps.children }
615
- </PlaylistContext.Provider>
616
- </ol>
617
- ) }
708
+ <ol
709
+ className={ clsx( 'wp-block-playlist__tracklist', {
710
+ 'wp-block-playlist__tracklist-is-hidden':
711
+ ! showTracklist,
712
+ 'wp-block-playlist__tracklist-show-numbers':
713
+ showNumbers,
714
+ 'wp-block-playlist__tracklist-length-is-hidden':
715
+ ! showTrackLength,
716
+ } ) }
717
+ >
718
+ <PlaylistContext.Provider value={ playlistContext }>
719
+ { innerBlocksProps.children }
720
+ </PlaylistContext.Provider>
721
+ </ol>
618
722
  <Caption
619
723
  attributes={ attributes }
620
724
  setAttributes={ setAttributes }
@@ -1,10 +1,3 @@
1
- .wp-block-playlist {
2
- li.block-list-appender.block-list-appender {
3
- position: initial;
4
- margin-top: var(--wp--preset--spacing--30, 1em);
5
- }
6
- }
7
-
8
1
  .wp-block-playlist__waveform-color-controls {
9
2
  display: grid;
10
3
  grid-column: 1 / -1;
@@ -8,7 +8,7 @@
8
8
  /**
9
9
  * Renders the `core/playlist` block on server.
10
10
  *
11
- * @since 6.9.0
11
+ * @since 7.1.0
12
12
  *
13
13
  * @param array $attributes The block attributes.
14
14
  * @param string $content The block content.
@@ -37,13 +37,18 @@ function render_block_core_playlist( $attributes, $content, $block ) {
37
37
  $playlist_tracks[] = $track_id;
38
38
 
39
39
  // Extract track metadata from block attributes.
40
- $title = isset( $track_attributes['title'] ) && ! empty( $track_attributes['title'] ) ? $track_attributes['title'] : __( 'Unknown title' );
41
- $artist = $track_attributes['artist'] ?? '';
42
- $album = $track_attributes['album'] ?? '';
43
- $image = $track_attributes['image'] ?? '';
44
- $image_alt = $track_attributes['imageAlt'] ?? '';
45
- $url = $track_attributes['src'] ?? '';
46
- $aria_label = $title;
40
+ $track_title_attr = $track_attributes['title'] ?? null;
41
+ $track_artist = $track_attributes['artist'] ?? null;
42
+ $track_album = $track_attributes['album'] ?? null;
43
+ $track_image = $track_attributes['image'] ?? null;
44
+ $track_src = $track_attributes['src'] ?? null;
45
+ $title = ! empty( $track_title_attr ) && is_string( $track_title_attr ) ? $track_title_attr : __( 'Unknown title' );
46
+ $artist = is_string( $track_artist ) ? $track_artist : '';
47
+ $album = is_string( $track_album ) ? $track_album : '';
48
+ $image = is_string( $track_image ) ? $track_image : '';
49
+ $image_alt = $track_attributes['imageAlt'] ?? '';
50
+ $url = is_string( $track_src ) ? $track_src : '';
51
+ $aria_label = $title;
47
52
 
48
53
  if ( $title && $artist && $album ) {
49
54
  $aria_label = sprintf(
@@ -197,7 +202,7 @@ function render_block_core_playlist( $attributes, $content, $block ) {
197
202
  /**
198
203
  * Registers the `core/playlist` block on server.
199
204
  *
200
- * @since 6.9.0
205
+ * @since 7.1.0
201
206
  */
202
207
  function register_block_core_playlist() {
203
208
  register_block_type_from_metadata(
@@ -4,6 +4,9 @@
4
4
  $waveform-player-height: 100px;
5
5
 
6
6
  .wp-block-playlist {
7
+ // This block has customizable padding, border-box makes that more predictable.
8
+ box-sizing: border-box;
9
+
7
10
  // Main waveform player container.
8
11
  .wp-block-playlist__waveform-player {
9
12
  width: 100%;