@wordpress/block-library 10.1.1-next.v.202607070741.0 → 10.2.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 (371) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/build/accordion/block.json +1 -0
  3. package/build/button/edit.cjs +18 -8
  4. package/build/button/edit.cjs.map +2 -2
  5. package/build/cover/block.json +11 -0
  6. package/build/cover/edit/block-controls.cjs +11 -4
  7. package/build/cover/edit/block-controls.cjs.map +2 -2
  8. package/build/cover/edit/embed-video-url-input.cjs +5 -3
  9. package/build/cover/edit/embed-video-url-input.cjs.map +3 -3
  10. package/build/cover/edit/index.cjs +1 -1
  11. package/build/cover/edit/index.cjs.map +2 -2
  12. package/build/cover/embed-video-utils.cjs +17 -7
  13. package/build/cover/embed-video-utils.cjs.map +2 -2
  14. package/build/details/transforms.cjs +1 -3
  15. package/build/details/transforms.cjs.map +2 -2
  16. package/build/embed/embed-placeholder.cjs +0 -1
  17. package/build/embed/embed-placeholder.cjs.map +2 -2
  18. package/build/file/edit.cjs +4 -3
  19. package/build/file/edit.cjs.map +2 -2
  20. package/build/file/transforms.cjs +25 -60
  21. package/build/file/transforms.cjs.map +2 -2
  22. package/build/gallery/block.json +4 -2
  23. package/build/gallery/edit.cjs +1 -1
  24. package/build/gallery/edit.cjs.map +3 -3
  25. package/build/gallery/gap-styles.cjs +49 -13
  26. package/build/gallery/gap-styles.cjs.map +3 -3
  27. package/build/group/transforms.cjs +1 -1
  28. package/build/group/transforms.cjs.map +2 -2
  29. package/build/icon/components/custom-inserter/icon-grid.cjs +25 -12
  30. package/build/icon/components/custom-inserter/icon-grid.cjs.map +2 -2
  31. package/build/icon/components/custom-inserter/index.cjs +100 -31
  32. package/build/icon/components/custom-inserter/index.cjs.map +3 -3
  33. package/build/icon/edit.cjs +11 -12
  34. package/build/icon/edit.cjs.map +2 -2
  35. package/build/image/block.json +1 -1
  36. package/build/image/edit.cjs +0 -34
  37. package/build/image/edit.cjs.map +2 -2
  38. package/build/image/image.cjs +4 -23
  39. package/build/image/image.cjs.map +3 -3
  40. package/build/image/transforms.cjs +78 -0
  41. package/build/image/transforms.cjs.map +3 -3
  42. package/build/index.cjs +18 -8
  43. package/build/index.cjs.map +2 -2
  44. package/build/latest-posts/edit.cjs +5 -8
  45. package/build/latest-posts/edit.cjs.map +2 -2
  46. package/build/list-item/edit.cjs +1 -1
  47. package/build/list-item/edit.cjs.map +2 -2
  48. package/build/list-item/hooks/use-enter.cjs +14 -17
  49. package/build/list-item/hooks/use-enter.cjs.map +3 -3
  50. package/build/list-item/hooks/use-space.cjs +3 -2
  51. package/build/list-item/hooks/use-space.cjs.map +3 -3
  52. package/build/paragraph/block.json +1 -0
  53. package/build/paragraph/use-enter.cjs +3 -7
  54. package/build/paragraph/use-enter.cjs.map +3 -3
  55. package/build/playlist/block.json +29 -10
  56. package/build/playlist/edit.cjs +216 -14
  57. package/build/playlist/edit.cjs.map +3 -3
  58. package/build/playlist/index.cjs +1 -1
  59. package/build/playlist/index.cjs.map +2 -2
  60. package/build/playlist/utils.cjs +19 -5
  61. package/build/playlist/utils.cjs.map +2 -2
  62. package/build/playlist/view.cjs +83 -13
  63. package/build/playlist/view.cjs.map +2 -2
  64. package/build/playlist-track/block.json +4 -2
  65. package/build/playlist-track/edit.cjs +94 -64
  66. package/build/playlist-track/edit.cjs.map +2 -2
  67. package/build/post-content/block.json +1 -0
  68. package/build/post-template/block.json +2 -1
  69. package/build/post-template/edit.cjs +10 -1
  70. package/build/post-template/edit.cjs.map +2 -2
  71. package/build/pullquote/block.json +3 -1
  72. package/build/query/block.json +3 -2
  73. package/build/query/edit/inspector-controls/index.cjs +38 -4
  74. package/build/query/edit/inspector-controls/index.cjs.map +2 -2
  75. package/build/query/edit/inspector-controls/offset-controls.cjs +0 -1
  76. package/build/query/edit/inspector-controls/offset-controls.cjs.map +2 -2
  77. package/build/query/edit/inspector-controls/pages-control.cjs +0 -1
  78. package/build/query/edit/inspector-controls/pages-control.cjs.map +2 -2
  79. package/build/query/edit/query-content.cjs +10 -3
  80. package/build/query/edit/query-content.cjs.map +2 -2
  81. package/build/query/index.cjs +2 -1
  82. package/build/query/index.cjs.map +2 -2
  83. package/build/quote/block.json +3 -1
  84. package/build/quote/transforms.cjs +1 -3
  85. package/build/quote/transforms.cjs.map +2 -2
  86. package/build/rss/edit.cjs +0 -1
  87. package/build/rss/edit.cjs.map +2 -2
  88. package/build/tab-list/block.json +2 -2
  89. package/build/tab-list/edit.cjs +65 -11
  90. package/build/tab-list/edit.cjs.map +3 -3
  91. package/build/tab-panel/block.json +1 -6
  92. package/build/tab-panel/edit.cjs +15 -15
  93. package/build/tab-panel/edit.cjs.map +2 -2
  94. package/build/tab-panels/block.json +0 -1
  95. package/build/tabs/block.json +0 -5
  96. package/build/tabs/edit.cjs +4 -39
  97. package/build/tabs/edit.cjs.map +3 -3
  98. package/build/tabs/use-tab-list-items-sync.cjs +19 -1
  99. package/build/tabs/use-tab-list-items-sync.cjs.map +2 -2
  100. package/build/term-name/block.json +1 -3
  101. package/build/term-name/deprecated.cjs +105 -0
  102. package/build/term-name/deprecated.cjs.map +7 -0
  103. package/build/term-name/edit.cjs +11 -37
  104. package/build/term-name/edit.cjs.map +3 -3
  105. package/build/term-name/index.cjs +3 -1
  106. package/build/term-name/index.cjs.map +3 -3
  107. package/build/utils/waveform-player.cjs +138 -22
  108. package/build/utils/waveform-player.cjs.map +2 -2
  109. package/build/utils/waveform-utils.cjs +315 -17
  110. package/build/utils/waveform-utils.cjs.map +2 -2
  111. package/build/video/edit.cjs +2 -11
  112. package/build/video/edit.cjs.map +3 -3
  113. package/build-module/accordion/block.json +1 -0
  114. package/build-module/button/edit.mjs +19 -14
  115. package/build-module/button/edit.mjs.map +2 -2
  116. package/build-module/cover/block.json +11 -0
  117. package/build-module/cover/edit/block-controls.mjs +11 -4
  118. package/build-module/cover/edit/block-controls.mjs.map +2 -2
  119. package/build-module/cover/edit/embed-video-url-input.mjs +5 -4
  120. package/build-module/cover/edit/embed-video-url-input.mjs.map +2 -2
  121. package/build-module/cover/edit/index.mjs +1 -1
  122. package/build-module/cover/edit/index.mjs.map +2 -2
  123. package/build-module/cover/embed-video-utils.mjs +16 -7
  124. package/build-module/cover/embed-video-utils.mjs.map +2 -2
  125. package/build-module/details/transforms.mjs +2 -7
  126. package/build-module/details/transforms.mjs.map +2 -2
  127. package/build-module/embed/embed-placeholder.mjs +0 -1
  128. package/build-module/embed/embed-placeholder.mjs.map +2 -2
  129. package/build-module/file/edit.mjs +4 -3
  130. package/build-module/file/edit.mjs.map +2 -2
  131. package/build-module/file/transforms.mjs +25 -60
  132. package/build-module/file/transforms.mjs.map +2 -2
  133. package/build-module/gallery/block.json +4 -2
  134. package/build-module/gallery/edit.mjs +3 -3
  135. package/build-module/gallery/edit.mjs.map +2 -2
  136. package/build-module/gallery/gap-styles.mjs +53 -14
  137. package/build-module/gallery/gap-styles.mjs.map +2 -2
  138. package/build-module/group/transforms.mjs +2 -5
  139. package/build-module/group/transforms.mjs.map +2 -2
  140. package/build-module/icon/components/custom-inserter/icon-grid.mjs +25 -12
  141. package/build-module/icon/components/custom-inserter/icon-grid.mjs.map +2 -2
  142. package/build-module/icon/components/custom-inserter/index.mjs +102 -33
  143. package/build-module/icon/components/custom-inserter/index.mjs.map +2 -2
  144. package/build-module/icon/edit.mjs +11 -12
  145. package/build-module/icon/edit.mjs.map +2 -2
  146. package/build-module/image/block.json +1 -1
  147. package/build-module/image/edit.mjs +0 -34
  148. package/build-module/image/edit.mjs.map +2 -2
  149. package/build-module/image/image.mjs +4 -13
  150. package/build-module/image/image.mjs.map +2 -2
  151. package/build-module/image/transforms.mjs +78 -0
  152. package/build-module/image/transforms.mjs.map +2 -2
  153. package/build-module/index.mjs +18 -8
  154. package/build-module/index.mjs.map +2 -2
  155. package/build-module/latest-posts/edit.mjs +5 -8
  156. package/build-module/latest-posts/edit.mjs.map +2 -2
  157. package/build-module/list-item/edit.mjs +1 -1
  158. package/build-module/list-item/edit.mjs.map +2 -2
  159. package/build-module/list-item/hooks/use-enter.mjs +15 -21
  160. package/build-module/list-item/hooks/use-enter.mjs.map +2 -2
  161. package/build-module/list-item/hooks/use-space.mjs +4 -6
  162. package/build-module/list-item/hooks/use-space.mjs.map +2 -2
  163. package/build-module/paragraph/block.json +1 -0
  164. package/build-module/paragraph/use-enter.mjs +4 -11
  165. package/build-module/paragraph/use-enter.mjs.map +2 -2
  166. package/build-module/playlist/block.json +29 -10
  167. package/build-module/playlist/edit.mjs +220 -16
  168. package/build-module/playlist/edit.mjs.map +2 -2
  169. package/build-module/playlist/index.mjs +1 -1
  170. package/build-module/playlist/index.mjs.map +2 -2
  171. package/build-module/playlist/utils.mjs +17 -4
  172. package/build-module/playlist/utils.mjs.map +2 -2
  173. package/build-module/playlist/view.mjs +89 -14
  174. package/build-module/playlist/view.mjs.map +2 -2
  175. package/build-module/playlist-track/block.json +4 -2
  176. package/build-module/playlist-track/edit.mjs +96 -65
  177. package/build-module/playlist-track/edit.mjs.map +2 -2
  178. package/build-module/post-content/block.json +1 -0
  179. package/build-module/post-template/block.json +2 -1
  180. package/build-module/post-template/edit.mjs +10 -1
  181. package/build-module/post-template/edit.mjs.map +2 -2
  182. package/build-module/pullquote/block.json +3 -1
  183. package/build-module/query/block.json +3 -2
  184. package/build-module/query/edit/inspector-controls/index.mjs +40 -5
  185. package/build-module/query/edit/inspector-controls/index.mjs.map +2 -2
  186. package/build-module/query/edit/inspector-controls/offset-controls.mjs +0 -1
  187. package/build-module/query/edit/inspector-controls/offset-controls.mjs.map +2 -2
  188. package/build-module/query/edit/inspector-controls/pages-control.mjs +0 -1
  189. package/build-module/query/edit/inspector-controls/pages-control.mjs.map +2 -2
  190. package/build-module/query/edit/query-content.mjs +10 -3
  191. package/build-module/query/edit/query-content.mjs.map +2 -2
  192. package/build-module/query/index.mjs +2 -1
  193. package/build-module/query/index.mjs.map +2 -2
  194. package/build-module/quote/block.json +3 -1
  195. package/build-module/quote/transforms.mjs +2 -4
  196. package/build-module/quote/transforms.mjs.map +2 -2
  197. package/build-module/rss/edit.mjs +0 -1
  198. package/build-module/rss/edit.mjs.map +2 -2
  199. package/build-module/tab-list/block.json +2 -2
  200. package/build-module/tab-list/edit.mjs +72 -13
  201. package/build-module/tab-list/edit.mjs.map +2 -2
  202. package/build-module/tab-panel/block.json +1 -6
  203. package/build-module/tab-panel/edit.mjs +16 -16
  204. package/build-module/tab-panel/edit.mjs.map +2 -2
  205. package/build-module/tab-panels/block.json +0 -1
  206. package/build-module/tabs/block.json +0 -5
  207. package/build-module/tabs/edit.mjs +5 -45
  208. package/build-module/tabs/edit.mjs.map +2 -2
  209. package/build-module/tabs/use-tab-list-items-sync.mjs +19 -1
  210. package/build-module/tabs/use-tab-list-items-sync.mjs.map +2 -2
  211. package/build-module/term-name/block.json +1 -3
  212. package/build-module/term-name/deprecated.mjs +74 -0
  213. package/build-module/term-name/deprecated.mjs.map +7 -0
  214. package/build-module/term-name/edit.mjs +11 -28
  215. package/build-module/term-name/edit.mjs.map +2 -2
  216. package/build-module/term-name/index.mjs +3 -1
  217. package/build-module/term-name/index.mjs.map +2 -2
  218. package/build-module/utils/waveform-player.mjs +144 -23
  219. package/build-module/utils/waveform-player.mjs.map +2 -2
  220. package/build-module/utils/waveform-utils.mjs +310 -16
  221. package/build-module/utils/waveform-utils.mjs.map +2 -2
  222. package/build-module/video/edit.mjs +2 -11
  223. package/build-module/video/edit.mjs.map +2 -2
  224. package/build-style/editor-rtl.css +46 -21
  225. package/build-style/editor.css +46 -21
  226. package/build-style/icon/editor-rtl.css +80 -7
  227. package/build-style/icon/editor.css +80 -7
  228. package/build-style/more/editor-rtl.css +2 -2
  229. package/build-style/more/editor.css +2 -2
  230. package/build-style/navigation/editor-rtl.css +3 -4
  231. package/build-style/navigation/editor.css +3 -4
  232. package/build-style/navigation-link/editor-rtl.css +1 -1
  233. package/build-style/navigation-link/editor.css +1 -1
  234. package/build-style/nextpage/editor-rtl.css +2 -2
  235. package/build-style/nextpage/editor.css +2 -2
  236. package/build-style/playlist/editor-rtl.css +6 -0
  237. package/build-style/playlist/editor.css +6 -0
  238. package/build-style/playlist/style-rtl.css +37 -1
  239. package/build-style/playlist/style.css +37 -1
  240. package/build-style/playlist-track/style-rtl.css +10 -0
  241. package/build-style/playlist-track/style.css +10 -0
  242. package/build-style/style-rtl.css +47 -1
  243. package/build-style/style.css +47 -1
  244. package/build-style/video/editor-rtl.css +1 -1
  245. package/build-style/video/editor.css +1 -1
  246. package/build-types/table-of-contents/list.d.ts +1 -1
  247. package/build-types/table-of-contents/list.d.ts.map +1 -1
  248. package/package.json +46 -44
  249. package/src/accordion/README.md +1 -0
  250. package/src/accordion/block.json +1 -0
  251. package/src/button/edit.js +21 -15
  252. package/src/cover/README.md +60 -0
  253. package/src/cover/block.json +11 -0
  254. package/src/cover/edit/block-controls.js +21 -11
  255. package/src/cover/edit/embed-video-url-input.js +5 -4
  256. package/src/cover/edit/index.js +9 -11
  257. package/src/cover/embed-video-utils.js +38 -13
  258. package/src/details/transforms.js +2 -7
  259. package/src/embed/embed-placeholder.js +0 -1
  260. package/src/file/edit.js +8 -8
  261. package/src/file/transforms.js +28 -60
  262. package/src/gallery/README.md +1 -1
  263. package/src/gallery/block.json +4 -2
  264. package/src/gallery/edit.js +3 -3
  265. package/src/gallery/gap-styles.js +79 -20
  266. package/src/gallery/index.php +103 -43
  267. package/src/group/transforms.js +2 -5
  268. package/src/icon/components/custom-inserter/icon-grid.js +40 -11
  269. package/src/icon/components/custom-inserter/index.js +98 -25
  270. package/src/icon/edit.js +11 -17
  271. package/src/icon/editor.scss +33 -8
  272. package/src/image/README.md +1 -1
  273. package/src/image/block.json +1 -1
  274. package/src/image/edit.js +0 -52
  275. package/src/image/editor.scss +2 -0
  276. package/src/image/image.js +19 -24
  277. package/src/image/transforms.js +128 -0
  278. package/src/index.js +19 -7
  279. package/src/latest-posts/edit.js +5 -8
  280. package/src/latest-posts/index.php +46 -16
  281. package/src/list-item/edit.js +1 -1
  282. package/src/list-item/hooks/use-enter.js +15 -22
  283. package/src/list-item/hooks/use-space.js +4 -6
  284. package/src/media-text/editor.scss +1 -0
  285. package/src/more/editor.scss +1 -1
  286. package/src/navigation/editor.scss +1 -2
  287. package/src/navigation-link/editor.scss +1 -1
  288. package/src/navigation-link/index.php +6 -3
  289. package/src/navigation-submenu/index.php +6 -3
  290. package/src/nextpage/editor.scss +1 -1
  291. package/src/paragraph/README.md +1 -0
  292. package/src/paragraph/block.json +1 -0
  293. package/src/paragraph/use-enter.js +4 -11
  294. package/src/playlist/README.md +17 -18
  295. package/src/playlist/block.json +29 -10
  296. package/src/playlist/edit.js +247 -15
  297. package/src/playlist/editor.scss +6 -0
  298. package/src/playlist/index.js +1 -1
  299. package/src/playlist/index.php +58 -14
  300. package/src/playlist/style.scss +51 -1
  301. package/src/playlist/test/edit.js +13 -2
  302. package/src/playlist/utils.js +25 -7
  303. package/src/playlist/view.js +97 -16
  304. package/src/playlist-track/README.md +3 -3
  305. package/src/playlist-track/block.json +4 -2
  306. package/src/playlist-track/edit.js +94 -70
  307. package/src/playlist-track/index.php +20 -6
  308. package/src/playlist-track/style.scss +10 -0
  309. package/src/playlist-track/test/edit.js +169 -0
  310. package/src/post-content/README.md +1 -0
  311. package/src/post-content/block.json +1 -0
  312. package/src/post-template/README.md +1 -0
  313. package/src/post-template/block.json +2 -1
  314. package/src/post-template/edit.js +9 -0
  315. package/src/pullquote/README.md +1 -0
  316. package/src/pullquote/block.json +3 -1
  317. package/src/query/README.md +2 -1
  318. package/src/query/block.json +3 -2
  319. package/src/query/edit/inspector-controls/index.js +43 -3
  320. package/src/query/edit/inspector-controls/offset-controls.js +0 -1
  321. package/src/query/edit/inspector-controls/pages-control.js +0 -1
  322. package/src/query/edit/query-content.js +19 -3
  323. package/src/query/index.js +1 -0
  324. package/src/quote/README.md +1 -0
  325. package/src/quote/block.json +3 -1
  326. package/src/quote/transforms.js +2 -4
  327. package/src/rss/edit.js +0 -1
  328. package/src/rss/index.php +1 -1
  329. package/src/tab-list/README.md +4 -5
  330. package/src/tab-list/block.json +2 -2
  331. package/src/tab-list/edit.js +90 -28
  332. package/src/tab-list/index.php +9 -4
  333. package/src/tab-panel/README.md +4 -6
  334. package/src/tab-panel/block.json +1 -6
  335. package/src/tab-panel/edit.js +19 -24
  336. package/src/tab-panels/README.md +5 -3
  337. package/src/tab-panels/block.json +0 -1
  338. package/src/tabs/README.md +18 -9
  339. package/src/tabs/block.json +0 -5
  340. package/src/tabs/edit.js +7 -64
  341. package/src/tabs/use-tab-list-items-sync.js +24 -4
  342. package/src/template-part/index.php +1 -16
  343. package/src/term-name/README.md +1 -1
  344. package/src/term-name/block.json +1 -3
  345. package/src/term-name/deprecated.js +74 -0
  346. package/src/term-name/edit.js +2 -18
  347. package/src/term-name/index.js +2 -0
  348. package/src/utils/media-control.scss +1 -5
  349. package/src/utils/test/waveform-player.js +297 -34
  350. package/src/utils/test/waveform-utils.js +410 -5
  351. package/src/utils/waveform-player.js +189 -59
  352. package/src/utils/waveform-utils.js +443 -40
  353. package/src/video/edit.js +0 -8
  354. package/src/video/editor.scss +1 -1
  355. package/build/image/animated-gif-convert-control.cjs +0 -105
  356. package/build/image/animated-gif-convert-control.cjs.map +0 -7
  357. package/build/utils/gif-conversion-attributes.cjs +0 -40
  358. package/build/utils/gif-conversion-attributes.cjs.map +0 -7
  359. package/build/video/gif-restore-control.cjs +0 -73
  360. package/build/video/gif-restore-control.cjs.map +0 -7
  361. package/build-module/image/animated-gif-convert-control.mjs +0 -87
  362. package/build-module/image/animated-gif-convert-control.mjs.map +0 -7
  363. package/build-module/utils/gif-conversion-attributes.mjs +0 -15
  364. package/build-module/utils/gif-conversion-attributes.mjs.map +0 -7
  365. package/build-module/video/gif-restore-control.mjs +0 -55
  366. package/build-module/video/gif-restore-control.mjs.map +0 -7
  367. package/src/image/animated-gif-convert-control.js +0 -135
  368. package/src/image/test/animated-gif-convert-control.js +0 -276
  369. package/src/utils/gif-conversion-attributes.js +0 -30
  370. package/src/utils/test/gif-conversion-attributes.js +0 -60
  371. package/src/video/gif-restore-control.js +0 -92
@@ -17,9 +17,10 @@
17
17
  * @return string Returns the Playlist.
18
18
  */
19
19
  function render_block_core_playlist( $attributes, $content, $block ) {
20
- $playlist_id = wp_unique_id( 'playlist-' );
21
- $playlist_tracks = array();
22
- $tracks_data = array();
20
+ $playlist_id = wp_unique_id( 'playlist-' );
21
+ $playlist_tracks = array();
22
+ $tracks_data = array();
23
+ $show_play_button_artwork = ! empty( $attributes['showPlayButtonArtwork'] );
23
24
 
24
25
  // Parse inner blocks to extract track data.
25
26
  // This approach avoids duplicating track data in the HTML output.
@@ -40,12 +41,13 @@ function render_block_core_playlist( $attributes, $content, $block ) {
40
41
  $artist = $track_attributes['artist'] ?? '';
41
42
  $album = $track_attributes['album'] ?? '';
42
43
  $image = $track_attributes['image'] ?? '';
44
+ $image_alt = $track_attributes['imageAlt'] ?? '';
43
45
  $url = $track_attributes['src'] ?? '';
44
46
  $aria_label = $title;
45
47
 
46
48
  if ( $title && $artist && $album ) {
47
49
  $aria_label = sprintf(
48
- /* translators: %1$s: track title, %2$s artist name, %3$s: album name. */
50
+ /* translators: %1$s: track title, %2$s: artist name, %3$s: album name. */
49
51
  _x( '%1$s by %2$s from the album %3$s', 'track title, artist name, album name' ),
50
52
  $title,
51
53
  $artist,
@@ -62,6 +64,7 @@ function render_block_core_playlist( $attributes, $content, $block ) {
62
64
  'artist' => wp_strip_all_tags( $artist ),
63
65
  'album' => wp_strip_all_tags( $album ),
64
66
  'image' => esc_url( $image ),
67
+ 'imageAlt' => wp_strip_all_tags( $image_alt ),
65
68
  'ariaLabel' => wp_strip_all_tags( $aria_label ),
66
69
  );
67
70
  }
@@ -90,10 +93,50 @@ function render_block_core_playlist( $attributes, $content, $block ) {
90
93
  // Add waveform player container with translated button labels.
91
94
  $label_play = esc_attr__( 'Play' );
92
95
  $label_pause = esc_attr__( 'Pause' );
93
- $html = '<div class="wp-block-playlist__waveform-player"
96
+ $label_seek = esc_attr__( 'Seek' );
97
+ /* translators: %1$s: current audio time, %2$s: total audio duration. */
98
+ $label_seek_value = esc_attr_x(
99
+ '%1$s of %2$s',
100
+ 'audio current time of total duration'
101
+ );
102
+ $waveform_color_attribute = '';
103
+ $waveform_gradient_attribute = '';
104
+ $waveform_background_color_attribute = '';
105
+ $waveform_background_gradient_attribute = '';
106
+ if ( ! empty( $attributes['waveformColor'] ) ) {
107
+ $waveform_color_attribute = sprintf(
108
+ ' data-waveform-player-color="%s"',
109
+ esc_attr( $attributes['waveformColor'] )
110
+ );
111
+ }
112
+ if ( ! empty( $attributes['waveformGradient'] ) ) {
113
+ $waveform_gradient_attribute = sprintf(
114
+ ' data-waveform-player-gradient="%s"',
115
+ esc_attr( $attributes['waveformGradient'] )
116
+ );
117
+ }
118
+ if ( ! empty( $attributes['waveformBackgroundColor'] ) ) {
119
+ $waveform_background_color_attribute = sprintf(
120
+ ' data-waveform-player-background-color="%s"',
121
+ esc_attr( $attributes['waveformBackgroundColor'] )
122
+ );
123
+ }
124
+ if ( ! empty( $attributes['waveformBackgroundGradient'] ) ) {
125
+ $waveform_background_gradient_attribute = sprintf(
126
+ ' data-waveform-player-background-gradient="%s"',
127
+ esc_attr( $attributes['waveformBackgroundGradient'] )
128
+ );
129
+ }
130
+ $html = '<div class="wp-block-playlist__waveform-player"' .
131
+ $waveform_color_attribute .
132
+ $waveform_gradient_attribute .
133
+ $waveform_background_color_attribute .
134
+ $waveform_background_gradient_attribute . '
94
135
  data-wp-watch="callbacks.initWaveformPlayer"
95
136
  data-label-play="' . $label_play . '"
96
137
  data-label-pause="' . $label_pause . '"
138
+ data-label-seek="' . $label_seek . '"
139
+ data-label-seek-value="' . $label_seek_value . '"
97
140
  ></div>';
98
141
 
99
142
  // Add the waveform player container inside the figure.
@@ -106,20 +149,21 @@ function render_block_core_playlist( $attributes, $content, $block ) {
106
149
  $processor = new WP_HTML_Tag_Processor( $content );
107
150
  $processor->next_tag( 'figure' );
108
151
  $processor->set_attribute( 'data-wp-interactive', 'core/playlist' );
109
- // Extract the waveform style from the block style variation class.
110
- $waveform_style = 'bars';
111
- if ( ! empty( $attributes['className'] ) && preg_match( '/is-style-([\w-]+)/', $attributes['className'], $matches ) ) {
112
- $waveform_style = $matches[1];
113
- }
152
+
153
+ $waveform_style = $attributes['waveformStyle'] ?? 'bars';
114
154
 
115
155
  $processor->set_attribute(
116
156
  'data-wp-context',
117
157
  wp_json_encode(
118
158
  array(
119
- 'playlistId' => $playlist_id,
120
- 'currentId' => $playlist_tracks[0],
121
- 'tracks' => $playlist_tracks,
122
- 'waveformStyle' => $waveform_style,
159
+ 'playlistId' => $playlist_id,
160
+ 'currentId' => $playlist_tracks[0],
161
+ 'isPlaying' => false,
162
+ 'tracks' => $playlist_tracks,
163
+ 'waveformStyle' => $waveform_style,
164
+ 'showPlayButtonArtwork' => $show_play_button_artwork,
165
+ 'labelPauseTrack' => __( 'Pause' ),
166
+ 'labelSelectTrack' => __( 'Play' ),
123
167
  )
124
168
  )
125
169
  );
@@ -9,6 +9,11 @@ $waveform-player-height: 100px;
9
9
  width: 100%;
10
10
  margin: var(--wp--preset--spacing--20, 0.625em) 0;
11
11
  position: relative;
12
+
13
+ &:focus-visible {
14
+ outline: revert;
15
+ outline-offset: revert;
16
+ }
12
17
  }
13
18
 
14
19
  // Set the waveform track height and remove gap between button and waveform.
@@ -19,6 +24,7 @@ $waveform-player-height: 100px;
19
24
 
20
25
  // WaveformPlayer button styling.
21
26
  .waveform-btn {
27
+ border: none;
22
28
  border-radius: 0;
23
29
  border-end-start-radius: 0.125rem;
24
30
  border-start-start-radius: 0.125rem;
@@ -31,16 +37,60 @@ $waveform-player-height: 100px;
31
37
  &:hover:not(:disabled) {
32
38
  transform: none;
33
39
  }
40
+
41
+ svg {
42
+ width: 3rem;
43
+ height: 3rem;
44
+ }
45
+
46
+ .waveform-icon-pause svg {
47
+ // The pause icon's two full-height bars read larger than the
48
+ // play triangle at the same SVG box size, so optically scale it
49
+ // down to match the play icon's visual weight.
50
+ width: 2.625rem;
51
+ height: 2.625rem;
52
+ }
53
+ }
54
+
55
+ .waveform-player.has-play-button-artwork {
56
+ .waveform-btn {
57
+ background-image: var(--wp--playlist--play-button-artwork);
58
+ background-position: center;
59
+ background-size: cover;
60
+ color: #fff;
61
+
62
+ svg {
63
+ filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.45));
64
+ }
65
+
66
+ svg path {
67
+ // Override inline fills applied before artwork is added.
68
+ fill: #fff !important;
69
+ }
70
+ }
34
71
  }
35
72
 
36
73
  .waveform-track.waveform-align-bottom .waveform-btn {
37
74
  margin-bottom: 0;
38
75
  }
39
76
 
40
- .waveform-subtitle {
77
+ .waveform-btn:focus-visible,
78
+ .waveform-container:focus-visible {
79
+ outline: revert;
80
+ outline-offset: revert;
81
+ }
82
+
83
+ .waveform-artist {
41
84
  opacity: 0.7;
42
85
  }
43
86
 
87
+ .waveform-info,
88
+ .waveform-artist,
89
+ .waveform-title,
90
+ .waveform-time {
91
+ font-size: inherit;
92
+ }
93
+
44
94
  .wp-block-playlist__tracklist {
45
95
  margin: 0;
46
96
  padding-left: 0;
@@ -13,7 +13,10 @@ describe( 'Playlist block edit utilities', () => {
13
13
  artist: 'The Artist',
14
14
  album: 'Great Album',
15
15
  fileLength: '3:45',
16
- image: { src: 'https://example.com/cover.jpg' },
16
+ image: {
17
+ src: 'https://example.com/cover.jpg',
18
+ alt: 'A bright abstract track image',
19
+ },
17
20
  };
18
21
 
19
22
  const result = getTrackAttributes( media );
@@ -26,6 +29,7 @@ describe( 'Playlist block edit utilities', () => {
26
29
  album: 'Great Album',
27
30
  length: '3:45',
28
31
  image: 'https://example.com/cover.jpg',
32
+ imageAlt: 'A bright abstract track image',
29
33
  } );
30
34
  } );
31
35
 
@@ -38,6 +42,8 @@ describe( 'Playlist block edit utilities', () => {
38
42
  const result = getTrackAttributes( media );
39
43
 
40
44
  expect( result.id ).toBe( 'https://example.com/song.mp3' );
45
+ expect( result.image ).toBeUndefined();
46
+ expect( result.imageAlt ).toBeUndefined();
41
47
  } );
42
48
 
43
49
  it( 'should fall back to meta.artist when artist is not available', () => {
@@ -110,18 +116,23 @@ describe( 'Playlist block edit utilities', () => {
110
116
  const result = getTrackAttributes( media );
111
117
 
112
118
  expect( result.image ).toBe( '' );
119
+ expect( result.imageAlt ).toBe( '' );
113
120
  } );
114
121
 
115
122
  it( 'should include image URLs', () => {
116
123
  const media = {
117
124
  url: 'https://example.com/song.mp3',
118
125
  title: 'My Song',
119
- image: { src: 'https://example.com/cover.jpg' },
126
+ image: {
127
+ src: 'https://example.com/cover.jpg',
128
+ alt_text: 'A black and white portrait',
129
+ },
120
130
  };
121
131
 
122
132
  const result = getTrackAttributes( media );
123
133
 
124
134
  expect( result.image ).toBe( 'https://example.com/cover.jpg' );
135
+ expect( result.imageAlt ).toBe( 'A black and white portrait' );
125
136
  } );
126
137
  } );
127
138
  } );
@@ -3,6 +3,30 @@
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
+ /**
7
+ * Transform media library image data into track image attributes.
8
+ *
9
+ * @param {Object} image - Image object from the media library.
10
+ * @return {Object} Track image attributes for the playlist-track block.
11
+ */
12
+ export function getTrackImageAttributes( image ) {
13
+ const imageSrc = image?.src ?? image?.url;
14
+
15
+ // Prevent using the default media attachment icon as the track image.
16
+ if ( imageSrc?.endsWith( '/images/media/audio.svg' ) ) {
17
+ return {
18
+ image: '',
19
+ imageAlt: '',
20
+ };
21
+ }
22
+
23
+ return {
24
+ // Note: Image is not available when a new track is uploaded.
25
+ image: imageSrc,
26
+ imageAlt: imageSrc ? image?.alt || image?.alt_text || '' : undefined,
27
+ };
28
+ }
29
+
6
30
  /**
7
31
  * Transform media library data into track block attributes.
8
32
  *
@@ -25,12 +49,6 @@ export function getTrackAttributes( media ) {
25
49
  media?.media_details?.album ||
26
50
  __( 'Unknown album' ),
27
51
  length: media?.fileLength || media?.media_details?.length_formatted,
28
- // Prevent using the default media attachment icon as the track image.
29
- // Note: Image is not available when a new track is uploaded.
30
- image:
31
- media?.image?.src &&
32
- media?.image?.src.endsWith( '/images/media/audio.svg' )
33
- ? ''
34
- : media?.image?.src,
52
+ ...getTrackImageAttributes( media?.image ),
35
53
  };
36
54
  }
@@ -6,12 +6,18 @@ import { store, getContext, getElement } from '@wordpress/interactivity';
6
6
  /**
7
7
  * Internal dependencies
8
8
  */
9
- import { initWaveformPlayer, logPlayError } from '../utils/waveform-utils';
9
+ import {
10
+ initWaveformPlayer,
11
+ logPlayError,
12
+ setupPlayButtonArtwork,
13
+ updateSeekControlLabel,
14
+ } from '../utils/waveform-utils';
10
15
 
11
16
  /**
12
17
  * Store player state for each element.
13
18
  */
14
19
  const playerState = new WeakMap();
20
+ const playlistPlayerState = new Map();
15
21
 
16
22
  const { state } = store(
17
23
  'core/playlist',
@@ -22,10 +28,34 @@ const { state } = store(
22
28
  const { currentId, trackId } = getContext();
23
29
  return currentId === trackId;
24
30
  },
31
+ get isCurrentTrackPlaying() {
32
+ const { currentId, isPlaying, trackId } = getContext();
33
+ return currentId === trackId && !! isPlaying;
34
+ },
35
+ get trackButtonActionLabel() {
36
+ const { labelPauseTrack, labelSelectTrack } = getContext();
37
+ return state.isCurrentTrackPlaying
38
+ ? labelPauseTrack
39
+ : labelSelectTrack;
40
+ },
25
41
  },
26
42
  actions: {
27
43
  changeTrack() {
28
44
  const context = getContext();
45
+ if ( context.currentId === context.trackId ) {
46
+ const player = playlistPlayerState.get(
47
+ context.playlistId
48
+ )?.instance;
49
+ if ( player?.isPlaying ) {
50
+ context.isPlaying = false;
51
+ player.pause();
52
+ } else {
53
+ player?.play()?.catch( logPlayError );
54
+ }
55
+ return;
56
+ }
57
+
58
+ context.isPlaying = false;
29
59
  context.currentId = context.trackId;
30
60
  },
31
61
  },
@@ -74,27 +104,55 @@ const { state } = store(
74
104
  */
75
105
  function initPlayer( ref, track, shouldAutoPlay, context ) {
76
106
  const existing = playerState.get( ref );
107
+ const showPlayButtonArtwork = context.showPlayButtonArtwork === true;
108
+ const playerArtwork = showPlayButtonArtwork ? '' : track.image;
77
109
 
78
110
  // If a player already exists, load the new track without recreating.
79
111
  if ( existing?.instance ) {
80
- existing.instance
81
- .loadTrack( track.url, track.title, track.artist, {
82
- artwork: track.image,
83
- } )
84
- .then( () => {
85
- existing.url = track.url;
86
- if ( shouldAutoPlay ) {
87
- existing.instance.play()?.catch( logPlayError );
88
- }
89
- } )
90
- .catch( logPlayError );
91
- return;
112
+ const shouldRecreatePlayer =
113
+ !! existing.instance.artworkEl !== !! playerArtwork;
114
+
115
+ if ( shouldRecreatePlayer ) {
116
+ existing.destroy?.();
117
+ playerState.delete( ref );
118
+ } else {
119
+ playlistPlayerState.set( context.playlistId, existing );
120
+ existing.instance
121
+ .loadTrack( track.url, track.title, track.artist, {
122
+ artwork: playerArtwork,
123
+ artworkAlt: playerArtwork ? track.imageAlt : '',
124
+ } )
125
+ .then( () => {
126
+ existing.url = track.url;
127
+ if ( existing.instance.artworkEl ) {
128
+ existing.instance.artworkEl.alt = track.imageAlt || '';
129
+ }
130
+ // loadTrack() preserves the previous explicit seekLabel option.
131
+ updateSeekControlLabel(
132
+ existing.instance,
133
+ track.title || ref.dataset.labelSeek
134
+ );
135
+ if ( showPlayButtonArtwork ) {
136
+ setupPlayButtonArtwork(
137
+ existing.container,
138
+ track.image
139
+ );
140
+ }
141
+ if ( shouldAutoPlay ) {
142
+ existing.instance.play()?.catch( logPlayError );
143
+ }
144
+ } )
145
+ .catch( logPlayError );
146
+ return;
147
+ }
92
148
  }
93
149
 
94
150
  // Read translated labels from server-rendered data attributes.
95
151
  const labels = {
96
152
  play: ref.dataset.labelPlay,
97
153
  pause: ref.dataset.labelPause,
154
+ seek: ref.dataset.labelSeek,
155
+ seekValueText: ref.dataset.labelSeekValue,
98
156
  };
99
157
 
100
158
  // Initialize using the shared core.
@@ -103,9 +161,15 @@ function initPlayer( ref, track, shouldAutoPlay, context ) {
103
161
  title: track.title,
104
162
  artist: track.artist,
105
163
  image: track.image,
164
+ imageAlt: track.imageAlt,
165
+ waveformColor: ref.dataset.waveformPlayerColor,
166
+ waveformGradient: ref.dataset.waveformPlayerGradient,
167
+ backgroundColor: ref.dataset.waveformPlayerBackgroundColor,
168
+ backgroundGradient: ref.dataset.waveformPlayerBackgroundGradient,
106
169
  autoPlay: shouldAutoPlay,
107
170
  labels,
108
171
  waveformStyle: context.waveformStyle,
172
+ showPlayButtonArtwork,
109
173
  onEnded: () => {
110
174
  // Advance to next track (autoPlay handles playback).
111
175
  const currentIndex = context.tracks.findIndex(
@@ -117,11 +181,28 @@ function initPlayer( ref, track, shouldAutoPlay, context ) {
117
181
  }
118
182
  },
119
183
  } );
184
+ const setIsPlaying = ( isPlaying ) => {
185
+ context.isPlaying = isPlaying;
186
+ };
187
+ const onPlay = () => setIsPlaying( true );
188
+ const onPause = () => setIsPlaying( false );
189
+ player.container.addEventListener( 'waveformplayer:play', onPlay );
190
+ player.container.addEventListener( 'waveformplayer:pause', onPause );
191
+ player.container.addEventListener( 'waveformplayer:ended', onPause );
192
+ const destroy = () => {
193
+ player.container.removeEventListener( 'waveformplayer:play', onPlay );
194
+ player.container.removeEventListener( 'waveformplayer:pause', onPause );
195
+ player.container.removeEventListener( 'waveformplayer:ended', onPause );
196
+ player.destroy();
197
+ };
120
198
 
121
199
  // Store state for cleanup, including instance for loadTrack reuse.
122
- playerState.set( ref, {
200
+ const nextState = {
123
201
  url: track.url,
124
202
  instance: player.instance,
125
- destroy: player.destroy,
126
- } );
203
+ container: player.container,
204
+ destroy,
205
+ };
206
+ playerState.set( ref, nextState );
207
+ playlistPlayerState.set( context.playlistId, nextState );
127
208
  }
@@ -1,8 +1,6 @@
1
1
  # Playlist track
2
2
 
3
3
  <!-- START TOKEN(Autogenerated block API docs) -->
4
- <div class="callout callout-warning">This block is <strong>experimental</strong> and may change or be removed without notice.</div>
5
-
6
4
  Playlist track.
7
5
 
8
6
  - **Name:** `core/playlist-track`
@@ -29,6 +27,7 @@ _Defined via the [`attributes`](https://developer.wordpress.org/block-editor/ref
29
27
  | `album` | `string` | — | — |
30
28
  | `artist` | `string` | — | — |
31
29
  | `image` | `string` | — | — |
30
+ | `imageAlt` | `string` | — | — |
32
31
  | `length` | `string` | — | — |
33
32
  | `title` | `string` | — | — |
34
33
 
@@ -48,6 +47,7 @@ _Defined via the [`usesContext` and `providesContext`](https://developer.wordpre
48
47
  **Uses context:**
49
48
 
50
49
  - `showArtists`
50
+ - `showImages`
51
51
 
52
52
  ## Block Markup
53
53
 
@@ -56,7 +56,7 @@ This is a [**dynamic block**](https://developer.wordpress.org/block-editor/getti
56
56
  In post content, it is stored as a block comment:
57
57
 
58
58
  ```html
59
- <!-- wp:playlist-track {"type":"audio"} /-->
59
+ <!-- wp:core/playlist-track {"id":123,"src":"https://example.com/audio.mp3","album":"Example album","artist":"Example creator","image":"https://example.com/track-image.jpg","imageAlt":"Track image","length":"3:21","title":"Sample track"} /-->
60
60
  ```
61
61
 
62
62
  ## Source
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "$schema": "https://schemas.wp.org/trunk/block.json",
3
3
  "apiVersion": 3,
4
- "__experimental": true,
5
4
  "name": "core/playlist-track",
6
5
  "title": "Playlist track",
7
6
  "category": "media",
@@ -9,7 +8,7 @@
9
8
  "description": "Playlist track.",
10
9
  "keywords": [ "music", "sound" ],
11
10
  "textdomain": "default",
12
- "usesContext": [ "showArtists" ],
11
+ "usesContext": [ "showArtists", "showImages" ],
13
12
  "attributes": {
14
13
  "blob": {
15
14
  "type": "string",
@@ -34,6 +33,9 @@
34
33
  "image": {
35
34
  "type": "string"
36
35
  },
36
+ "imageAlt": {
37
+ "type": "string"
38
+ },
37
39
  "length": {
38
40
  "type": "string"
39
41
  },