@wordpress/block-library 9.38.1-next.v.0 → 9.39.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 (273) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/button/index.cjs +3 -0
  3. package/build/button/index.cjs.map +2 -2
  4. package/build/comments-title/block.json +1 -3
  5. package/build/comments-title/deprecated.cjs +148 -24
  6. package/build/comments-title/deprecated.cjs.map +3 -3
  7. package/build/comments-title/edit.cjs +17 -31
  8. package/build/comments-title/edit.cjs.map +3 -3
  9. package/build/cover/edit/block-controls.cjs +10 -2
  10. package/build/cover/edit/block-controls.cjs.map +2 -2
  11. package/build/cover/edit/embed-video-url-input.cjs +6 -2
  12. package/build/cover/edit/embed-video-url-input.cjs.map +2 -2
  13. package/build/details/index.cjs +3 -0
  14. package/build/details/index.cjs.map +2 -2
  15. package/build/heading/index.cjs +3 -0
  16. package/build/heading/index.cjs.map +2 -2
  17. package/build/image/index.cjs +1 -1
  18. package/build/image/index.cjs.map +2 -2
  19. package/build/index.cjs +6 -0
  20. package/build/index.cjs.map +2 -2
  21. package/build/list-item/index.cjs +3 -0
  22. package/build/list-item/index.cjs.map +2 -2
  23. package/build/more/index.cjs +1 -1
  24. package/build/more/index.cjs.map +2 -2
  25. package/build/navigation/edit/index.cjs +23 -2
  26. package/build/navigation/edit/index.cjs.map +2 -2
  27. package/build/navigation-submenu/index.cjs +2 -2
  28. package/build/navigation-submenu/index.cjs.map +2 -2
  29. package/build/paragraph/index.cjs +1 -1
  30. package/build/paragraph/index.cjs.map +2 -2
  31. package/build/post-excerpt/edit.cjs +1 -1
  32. package/build/post-excerpt/edit.cjs.map +2 -2
  33. package/build/tab/add-tab-toolbar-control.cjs +31 -9
  34. package/build/tab/add-tab-toolbar-control.cjs.map +2 -2
  35. package/build/tab/block.json +18 -4
  36. package/build/tab/controls.cjs +4 -8
  37. package/build/tab/controls.cjs.map +3 -3
  38. package/build/tab/edit.cjs +46 -118
  39. package/build/tab/edit.cjs.map +3 -3
  40. package/build/tab/remove-tab-toolbar-control.cjs +91 -0
  41. package/build/tab/remove-tab-toolbar-control.cjs.map +7 -0
  42. package/build/tab/save.cjs +2 -2
  43. package/build/tab/save.cjs.map +2 -2
  44. package/build/tab-panels/block.json +70 -0
  45. package/build/tab-panels/edit.cjs +63 -0
  46. package/build/tab-panels/edit.cjs.map +7 -0
  47. package/build/tab-panels/icon.cjs +29 -0
  48. package/build/tab-panels/icon.cjs.map +7 -0
  49. package/build/tab-panels/index.cjs +58 -0
  50. package/build/tab-panels/index.cjs.map +7 -0
  51. package/build/tab-panels/save.cjs +33 -0
  52. package/build/tab-panels/save.cjs.map +7 -0
  53. package/build/tabs/block.json +61 -90
  54. package/build/tabs/controls.cjs +19 -221
  55. package/build/tabs/controls.cjs.map +3 -3
  56. package/build/tabs/deprecated.cjs +179 -0
  57. package/build/tabs/deprecated.cjs.map +7 -0
  58. package/build/tabs/edit.cjs +84 -62
  59. package/build/tabs/edit.cjs.map +3 -3
  60. package/build/tabs/index.cjs +3 -1
  61. package/build/tabs/index.cjs.map +3 -3
  62. package/build/tabs/save.cjs +6 -9
  63. package/build/tabs/save.cjs.map +2 -2
  64. package/build/tabs-menu/block.json +77 -0
  65. package/build/tabs-menu/edit.cjs +204 -0
  66. package/build/tabs-menu/edit.cjs.map +7 -0
  67. package/build/tabs-menu/icon.cjs +29 -0
  68. package/build/tabs-menu/icon.cjs.map +7 -0
  69. package/build/tabs-menu/index.cjs +58 -0
  70. package/build/tabs-menu/index.cjs.map +7 -0
  71. package/build/tabs-menu/save.cjs +35 -0
  72. package/build/tabs-menu/save.cjs.map +7 -0
  73. package/build/tabs-menu-item/block.json +98 -0
  74. package/build/tabs-menu-item/controls.cjs +247 -0
  75. package/build/tabs-menu-item/controls.cjs.map +7 -0
  76. package/build/tabs-menu-item/edit.cjs +272 -0
  77. package/build/tabs-menu-item/edit.cjs.map +7 -0
  78. package/build/tabs-menu-item/icon.cjs +29 -0
  79. package/build/tabs-menu-item/icon.cjs.map +7 -0
  80. package/build/tabs-menu-item/index.cjs +58 -0
  81. package/build/tabs-menu-item/index.cjs.map +7 -0
  82. package/build/tabs-menu-item/save.cjs +50 -0
  83. package/build/tabs-menu-item/save.cjs.map +7 -0
  84. package/build/template-part/edit/index.cjs +1 -1
  85. package/build/template-part/edit/index.cjs.map +2 -2
  86. package/build/utils/caption.cjs +4 -6
  87. package/build/utils/caption.cjs.map +3 -3
  88. package/build/video/edit.cjs +4 -2
  89. package/build/video/edit.cjs.map +2 -2
  90. package/build-module/button/index.mjs +3 -0
  91. package/build-module/button/index.mjs.map +2 -2
  92. package/build-module/comments-title/block.json +1 -3
  93. package/build-module/comments-title/deprecated.mjs +148 -24
  94. package/build-module/comments-title/deprecated.mjs.map +2 -2
  95. package/build-module/comments-title/edit.mjs +17 -32
  96. package/build-module/comments-title/edit.mjs.map +2 -2
  97. package/build-module/cover/edit/block-controls.mjs +11 -3
  98. package/build-module/cover/edit/block-controls.mjs.map +2 -2
  99. package/build-module/cover/edit/embed-video-url-input.mjs +6 -2
  100. package/build-module/cover/edit/embed-video-url-input.mjs.map +2 -2
  101. package/build-module/details/index.mjs +3 -0
  102. package/build-module/details/index.mjs.map +2 -2
  103. package/build-module/heading/index.mjs +3 -0
  104. package/build-module/heading/index.mjs.map +2 -2
  105. package/build-module/image/index.mjs +1 -1
  106. package/build-module/image/index.mjs.map +2 -2
  107. package/build-module/index.mjs +6 -0
  108. package/build-module/index.mjs.map +2 -2
  109. package/build-module/list-item/index.mjs +3 -0
  110. package/build-module/list-item/index.mjs.map +2 -2
  111. package/build-module/more/index.mjs +1 -1
  112. package/build-module/more/index.mjs.map +2 -2
  113. package/build-module/navigation/edit/index.mjs +23 -2
  114. package/build-module/navigation/edit/index.mjs.map +2 -2
  115. package/build-module/navigation-submenu/index.mjs +2 -2
  116. package/build-module/navigation-submenu/index.mjs.map +2 -2
  117. package/build-module/paragraph/index.mjs +1 -1
  118. package/build-module/paragraph/index.mjs.map +2 -2
  119. package/build-module/post-excerpt/edit.mjs +1 -1
  120. package/build-module/post-excerpt/edit.mjs.map +2 -2
  121. package/build-module/tab/add-tab-toolbar-control.mjs +32 -10
  122. package/build-module/tab/add-tab-toolbar-control.mjs.map +2 -2
  123. package/build-module/tab/block.json +18 -4
  124. package/build-module/tab/controls.mjs +4 -8
  125. package/build-module/tab/controls.mjs.map +2 -2
  126. package/build-module/tab/edit.mjs +48 -128
  127. package/build-module/tab/edit.mjs.map +2 -2
  128. package/build-module/tab/remove-tab-toolbar-control.mjs +73 -0
  129. package/build-module/tab/remove-tab-toolbar-control.mjs.map +7 -0
  130. package/build-module/tab/save.mjs +2 -2
  131. package/build-module/tab/save.mjs.map +2 -2
  132. package/build-module/tab-panels/block.json +70 -0
  133. package/build-module/tab-panels/edit.mjs +36 -0
  134. package/build-module/tab-panels/edit.mjs.map +7 -0
  135. package/build-module/tab-panels/icon.mjs +8 -0
  136. package/build-module/tab-panels/icon.mjs.map +7 -0
  137. package/build-module/tab-panels/index.mjs +20 -0
  138. package/build-module/tab-panels/index.mjs.map +7 -0
  139. package/build-module/tab-panels/save.mjs +12 -0
  140. package/build-module/tab-panels/save.mjs.map +7 -0
  141. package/build-module/tabs/block.json +61 -90
  142. package/build-module/tabs/controls.mjs +21 -228
  143. package/build-module/tabs/controls.mjs.map +2 -2
  144. package/build-module/tabs/deprecated.mjs +158 -0
  145. package/build-module/tabs/deprecated.mjs.map +7 -0
  146. package/build-module/tabs/edit.mjs +87 -64
  147. package/build-module/tabs/edit.mjs.map +2 -2
  148. package/build-module/tabs/index.mjs +3 -1
  149. package/build-module/tabs/index.mjs.map +2 -2
  150. package/build-module/tabs/save.mjs +7 -10
  151. package/build-module/tabs/save.mjs.map +2 -2
  152. package/build-module/tabs-menu/block.json +77 -0
  153. package/build-module/tabs-menu/edit.mjs +186 -0
  154. package/build-module/tabs-menu/edit.mjs.map +7 -0
  155. package/build-module/tabs-menu/icon.mjs +8 -0
  156. package/build-module/tabs-menu/icon.mjs.map +7 -0
  157. package/build-module/tabs-menu/index.mjs +20 -0
  158. package/build-module/tabs-menu/index.mjs.map +7 -0
  159. package/build-module/tabs-menu/save.mjs +14 -0
  160. package/build-module/tabs-menu/save.mjs.map +7 -0
  161. package/build-module/tabs-menu-item/block.json +98 -0
  162. package/build-module/tabs-menu-item/controls.mjs +227 -0
  163. package/build-module/tabs-menu-item/controls.mjs.map +7 -0
  164. package/build-module/tabs-menu-item/edit.mjs +253 -0
  165. package/build-module/tabs-menu-item/edit.mjs.map +7 -0
  166. package/build-module/tabs-menu-item/icon.mjs +8 -0
  167. package/build-module/tabs-menu-item/icon.mjs.map +7 -0
  168. package/build-module/tabs-menu-item/index.mjs +20 -0
  169. package/build-module/tabs-menu-item/index.mjs.map +7 -0
  170. package/build-module/tabs-menu-item/save.mjs +29 -0
  171. package/build-module/tabs-menu-item/save.mjs.map +7 -0
  172. package/build-module/template-part/edit/index.mjs +1 -1
  173. package/build-module/template-part/edit/index.mjs.map +2 -2
  174. package/build-module/utils/caption.mjs +1 -3
  175. package/build-module/utils/caption.mjs.map +2 -2
  176. package/build-module/video/edit.mjs +4 -2
  177. package/build-module/video/edit.mjs.map +2 -2
  178. package/build-style/editor-rtl.css +16 -21
  179. package/build-style/editor.css +16 -21
  180. package/build-style/gallery/style-rtl.css +1 -1
  181. package/build-style/gallery/style.css +1 -1
  182. package/build-style/style-rtl.css +42 -153
  183. package/build-style/style.css +42 -153
  184. package/build-style/tab/style-rtl.css +7 -1
  185. package/build-style/tab/style.css +7 -1
  186. package/build-style/tab-panels/style-rtl.css +4 -0
  187. package/build-style/tab-panels/style.css +4 -0
  188. package/build-style/tabs/style-rtl.css +1 -167
  189. package/build-style/tabs/style.css +1 -167
  190. package/build-style/tabs-menu/editor-rtl.css +4 -0
  191. package/build-style/tabs-menu/editor.css +4 -0
  192. package/build-style/tabs-menu/style-rtl.css +8 -0
  193. package/build-style/tabs-menu/style.css +8 -0
  194. package/build-style/tabs-menu-item/editor-rtl.css +16 -0
  195. package/build-style/tabs-menu-item/editor.css +16 -0
  196. package/build-style/tabs-menu-item/style-rtl.css +34 -0
  197. package/build-style/tabs-menu-item/style.css +34 -0
  198. package/package.json +37 -37
  199. package/src/button/index.js +4 -0
  200. package/src/comments-title/block.json +1 -3
  201. package/src/comments-title/deprecated.js +153 -23
  202. package/src/comments-title/edit.js +9 -25
  203. package/src/cover/edit/block-controls.js +14 -3
  204. package/src/cover/edit/embed-video-url-input.js +6 -2
  205. package/src/details/index.js +4 -0
  206. package/src/editor.scss +2 -1
  207. package/src/gallery/style.scss +1 -1
  208. package/src/heading/index.js +4 -0
  209. package/src/image/index.js +4 -1
  210. package/src/index.js +6 -0
  211. package/src/list-item/index.js +4 -0
  212. package/src/more/index.js +4 -1
  213. package/src/navigation/edit/index.js +28 -4
  214. package/src/navigation-submenu/index.js +6 -3
  215. package/src/paragraph/index.js +4 -1
  216. package/src/post-excerpt/edit.js +1 -1
  217. package/src/post-excerpt/index.php +39 -16
  218. package/src/style.scss +3 -0
  219. package/src/tab/add-tab-toolbar-control.js +36 -11
  220. package/src/tab/block.json +18 -4
  221. package/src/tab/controls.js +4 -5
  222. package/src/tab/edit.js +75 -150
  223. package/src/tab/index.php +5 -63
  224. package/src/tab/remove-tab-toolbar-control.js +103 -0
  225. package/src/tab/save.js +1 -3
  226. package/src/tab/style.scss +8 -1
  227. package/src/tab-panels/block.json +70 -0
  228. package/src/tab-panels/edit.js +44 -0
  229. package/src/tab-panels/icon.js +10 -0
  230. package/src/tab-panels/index.js +21 -0
  231. package/src/tab-panels/save.js +11 -0
  232. package/src/tab-panels/style.scss +4 -0
  233. package/src/tabs/block.json +61 -90
  234. package/src/tabs/controls.js +7 -221
  235. package/src/tabs/deprecated.js +214 -0
  236. package/src/tabs/edit.js +108 -68
  237. package/src/tabs/index.js +2 -0
  238. package/src/tabs/index.php +86 -191
  239. package/src/tabs/save.js +6 -13
  240. package/src/tabs/style.scss +1 -187
  241. package/src/tabs-menu/block.json +77 -0
  242. package/src/tabs-menu/edit.js +251 -0
  243. package/src/tabs-menu/editor.scss +6 -0
  244. package/src/tabs-menu/icon.js +10 -0
  245. package/src/tabs-menu/index.js +21 -0
  246. package/src/tabs-menu/index.php +74 -0
  247. package/src/tabs-menu/save.js +18 -0
  248. package/src/tabs-menu/style.scss +8 -0
  249. package/src/tabs-menu-item/block.json +98 -0
  250. package/src/tabs-menu-item/controls.js +262 -0
  251. package/src/tabs-menu-item/edit.js +322 -0
  252. package/src/tabs-menu-item/editor.scss +20 -0
  253. package/src/tabs-menu-item/icon.js +10 -0
  254. package/src/tabs-menu-item/index.js +21 -0
  255. package/src/tabs-menu-item/index.php +82 -0
  256. package/src/tabs-menu-item/save.js +44 -0
  257. package/src/tabs-menu-item/style.scss +42 -0
  258. package/src/template-part/edit/index.js +1 -3
  259. package/src/utils/caption.js +1 -7
  260. package/src/video/edit.js +4 -2
  261. package/build/tab/tabs-list.cjs +0 -132
  262. package/build/tab/tabs-list.cjs.map +0 -7
  263. package/build/tabs/style-engine.cjs +0 -119
  264. package/build/tabs/style-engine.cjs.map +0 -7
  265. package/build-module/tab/tabs-list.mjs +0 -101
  266. package/build-module/tab/tabs-list.mjs.map +0 -7
  267. package/build-module/tabs/style-engine.mjs +0 -101
  268. package/build-module/tabs/style-engine.mjs.map +0 -7
  269. package/build-style/tabs/editor-rtl.css +0 -26
  270. package/build-style/tabs/editor.css +0 -26
  271. package/src/tab/tabs-list.js +0 -122
  272. package/src/tabs/editor.scss +0 -30
  273. package/src/tabs/style-engine.js +0 -164
@@ -6,160 +6,71 @@
6
6
  */
7
7
 
8
8
  /**
9
- * Build inline CSS custom properties for color settings.
9
+ * Extract tabs list from tab-panels innerblocks.
10
10
  *
11
- * @param array $attributes Block attributes.
11
+ * @param array $innerblocks Parsed inner blocks of tabs block.
12
12
  *
13
- * @return string Inline CSS string.
14
- */
15
- function block_core_tabs_generate_color_styles( array $attributes ): string {
16
- $tab_inactive = $attributes['customTabInactiveColor'] ?? '';
17
- $tab_hover = $attributes['customTabHoverColor'] ?? '';
18
- $tab_active = $attributes['customTabActiveColor'] ?? '';
19
- $tab_text = $attributes['customTabTextColor'] ?? '';
20
- $hover_text = $attributes['customTabHoverTextColor'] ?? '';
21
- $active_text = $attributes['customTabActiveTextColor'] ?? '';
22
-
23
- $styles = array(
24
- '--custom-tab-inactive-color' => $tab_inactive,
25
- '--custom-tab-hover-color' => $tab_hover,
26
- '--custom-tab-active-color' => $tab_active,
27
- '--custom-tab-text-color' => $tab_text,
28
- '--custom-tab-hover-text-color' => $hover_text,
29
- '--custom-tab-active-text-color' => $active_text,
30
- );
31
-
32
- $style_string = array_map(
33
- static function ( string $key, string $value ): string {
34
- return ! empty( $value ) ? $key . ': ' . $value . ';' : '';
35
- },
36
- array_keys( $styles ),
37
- $styles
38
- );
39
-
40
- return implode( ' ', array_filter( $style_string ) );
41
- }
42
-
43
- /**
44
- * Build inline CSS custom properties for gap settings.
45
- *
46
- * @param array $attributes Block attributes.
47
- * @param bool $is_vertical Whether the tabs are vertical.
48
- *
49
- * @return string Inline CSS string.
13
+ * @return array List of tabs with id, label, index.
50
14
  */
51
- function block_core_tabs_generate_gap_styles( array $attributes, bool $is_vertical ): string {
52
- if ( empty( $attributes['style'] ) || ! is_array( $attributes['style'] ) ) {
53
- return '--wp--style--tabs-gap-default: 0.5em;';
54
- }
55
- if ( empty( $attributes['style']['spacing'] ) || ! is_array( $attributes['style']['spacing'] ) ) {
56
- return '--wp--style--tabs-gap-default: 0.5em;';
57
- }
58
- if ( ! array_key_exists( 'blockGap', $attributes['style']['spacing'] ) ) {
59
- return '--wp--style--tabs-gap-default: 0.5em;';
60
- }
61
-
62
- $block_gap = $attributes['style']['spacing']['blockGap'];
63
-
64
- if ( is_array( $block_gap ) ) {
65
- if ( array_key_exists( 'left', $block_gap ) && array_key_exists( 'top', $block_gap ) ) {
66
- $block_gap_horizontal = $block_gap['left'];
67
- $block_gap_vertical = $block_gap['top'];
68
- } elseif ( array_key_exists( 'left', $block_gap ) ) {
69
- $block_gap_horizontal = $block_gap['left'];
70
- $block_gap_vertical = '0.5em';
71
- } elseif ( array_key_exists( 'top', $block_gap ) ) {
72
- $block_gap_horizontal = '0.5em';
73
- $block_gap_vertical = $block_gap['top'];
74
- } else {
75
- return '--wp--style--tabs-gap-default: 0.5em;';
15
+ function block_core_tabs_generate_tabs_list( array $innerblocks = array() ): array {
16
+ $tabs_list = array();
17
+
18
+ // Find tab-panels block
19
+ foreach ( $innerblocks as $inner_block ) {
20
+ if ( 'core/tab-panels' === ( $inner_block['blockName'] ?? '' ) ) {
21
+ $tab_index = 0;
22
+ foreach ( $inner_block['innerBlocks'] ?? array() as $tab_block ) {
23
+ if ( 'core/tab' === ( $tab_block['blockName'] ?? '' ) ) {
24
+ $attrs = $tab_block['attrs'] ?? array();
25
+ $tab_label = $attrs['label'] ?? '';
26
+
27
+ // Try to get the ID from the rendered content
28
+ $tab_id = $attrs['anchor'] ?? '';
29
+ if ( empty( $tab_id ) && ! empty( $tab_block['innerHTML'] ) ) {
30
+ $tag_processor = new WP_HTML_Tag_Processor( $tab_block['innerHTML'] );
31
+ if ( $tag_processor->next_tag( array( 'class_name' => 'wp-block-tab' ) ) ) {
32
+ $tab_id = $tag_processor->get_attribute( 'id' ) ?? '';
33
+ }
34
+ }
35
+ if ( empty( $tab_id ) ) {
36
+ $tab_id = 'tab-' . $tab_index;
37
+ }
38
+
39
+ $tabs_list[] = array(
40
+ 'id' => $tab_id,
41
+ 'label' => esc_html( (string) $tab_label ),
42
+ 'index' => $tab_index,
43
+ );
44
+ ++$tab_index;
45
+ }
46
+ }
47
+ break;
76
48
  }
77
- } elseif ( is_string( $block_gap ) ) {
78
- return '--wp--style--tabs-gap-default: 0.5em;';
79
49
  }
80
50
 
81
- $block_gap_horizontal = preg_match( '/^var:preset\|spacing\|\d+$/', (string) $block_gap_horizontal )
82
- ? 'var(--wp--preset--spacing--' . substr( (string) $block_gap_horizontal, strrpos( (string) $block_gap_horizontal, '|' ) + 1 ) . ')'
83
- : (string) $block_gap_horizontal;
84
-
85
- $block_gap_vertical = preg_match( '/^var:preset\|spacing\|\d+$/', (string) $block_gap_vertical )
86
- ? 'var(--wp--preset--spacing--' . substr( (string) $block_gap_vertical, strrpos( (string) $block_gap_vertical, '|' ) + 1 ) . ')'
87
- : (string) $block_gap_vertical;
88
-
89
- $list_gap = $block_gap_horizontal;
90
- $block_gap = $block_gap_vertical;
91
-
92
- if ( $is_vertical ) {
93
- $list_gap = $block_gap_vertical;
94
- $block_gap = $block_gap_horizontal;
95
- }
96
-
97
- return wp_sprintf(
98
- '--wp--style--unstable-tabs-list-gap: %s; --wp--style--unstable-tabs-gap: %s;',
99
- $list_gap,
100
- $block_gap
101
- );
51
+ return $tabs_list;
102
52
  }
103
53
 
104
54
  /**
105
- * Extract tabs list from inner blocks for hydration.
55
+ * Filter to provide tabs list context to core/tabs and core/tabs-menu blocks.
56
+ * It is more performant to do this here, once, rather than in the tabs render and tabs context filters.
57
+ * In this way core/tabs is both a provider and a consumer of the core/tabs-list context.
106
58
  *
107
- * @param array $innerblocks Parsed inner blocks.
59
+ * @param array $context Default block context.
60
+ * @param array $parsed_block The block being rendered.
108
61
  *
109
- * @return array List of tabs with id, label, index.
62
+ * @return array Modified context.
110
63
  */
111
- function block_core_tabs_generate_tabs_list_from_innerblocks( array $innerblocks = array() ): array {
112
- $tab_index = 0;
113
-
114
- return array_map(
115
- static function ( array $tab ) use ( &$tab_index ): array {
116
- $attrs = $tab['attrs'] ?? array();
117
-
118
- $tag_processor = new WP_HTML_Tag_Processor( $tab['innerHTML'] ?? '' );
119
- $tag_processor->next_tag( array( 'class_name' => 'wp-block-tab' ) );
120
-
121
- $tab_id = $tag_processor->get_attribute( 'id' );
122
- $tab_label = $attrs['label'] ?? '';
123
-
124
- $attrs['id'] = $tab_id;
125
- $attrs['label'] = esc_html( (string) $tab_label );
126
-
127
- $tab_index++;
128
-
129
- return $attrs;
130
- },
131
- $innerblocks
132
- );
133
- }
134
-
135
- /**
136
- * Build inline CSS custom properties for border settings.
137
- *
138
- * @param array $attributes Block attributes.
139
- *
140
- * @return string Inline CSS string.
141
- */
142
- function block_core_tabs_generate_border_styles( array $attributes ): string {
143
- $radius = $attributes['style']['border']['radius'] ?? null;
144
-
145
- if ( empty( $radius ) ) {
146
- return '';
64
+ function block_core_tabs_provide_context( array $context, array $parsed_block ): array {
65
+ if ( 'core/tabs' === $parsed_block['blockName'] ) {
66
+ $tabs_list = block_core_tabs_generate_tabs_list( $parsed_block['innerBlocks'] ?? array() );
67
+ $context['core/tabs-list'] = $tabs_list;
68
+ $context['core/tabs-id'] = $parsed_block['attrs']['anchor'] ?? wp_unique_id( 'tabs_' ); // Generate a unique ID for each tabs instance. Used for 3rd party extensibility to identify the tabs instance.
147
69
  }
148
70
 
149
- if ( is_array( $radius ) ) {
150
- $radius_value = wp_sprintf(
151
- '%s %s %s %s',
152
- $radius['topLeft'] ?? '0',
153
- $radius['topRight'] ?? '0',
154
- $radius['bottomRight'] ?? '0',
155
- $radius['bottomLeft'] ?? '0'
156
- );
157
- } else {
158
- $radius_value = $radius;
159
- }
160
-
161
- return wp_sprintf( '--tab-border-radius: %s;', (string) $radius_value );
71
+ return $context;
162
72
  }
73
+ add_filter( 'render_block_context', 'block_core_tabs_provide_context', 10, 2 );
163
74
 
164
75
  /**
165
76
  * Render callback for core/tabs.
@@ -172,29 +83,37 @@ function block_core_tabs_generate_border_styles( array $attributes ): string {
172
83
  */
173
84
  function block_core_tabs_render_block_callback( array $attributes, string $content, \WP_Block $block ): string {
174
85
  $active_tab_index = $attributes['activeTabIndex'] ?? 0;
86
+ $tabs_list = $block->context['core/tabs-list'] ?? array();
87
+ $tabs_id = $block->context['core/tabs-id'] ?? null;
175
88
 
176
- $tabs_list = block_core_tabs_generate_tabs_list_from_innerblocks( $block->parsed_block['innerBlocks'] ?? array() );
177
-
178
- $tabs_id = wp_unique_id( 'tabs_' );
89
+ if ( empty( $tabs_id ) ) {
90
+ // If malformed tabs, return early to avoid errors.
91
+ return '';
92
+ }
179
93
 
180
- /**
181
- * Builds a client side state for just this tabs instance.
182
- * This allows 3rd party extensibility of tabs while retaining
183
- * client side state management per core/tabs instance, like context.
184
- */
185
- wp_interactivity_state(
186
- 'core/tabs/private',
187
- array(
188
- $tabs_id => $tabs_list,
189
- )
190
- );
94
+ $title = $attributes['metadata']['name'] ?? '';
95
+ if ( empty( $title ) ) {
96
+ $title = 'Tab Contents';
97
+ }
98
+ $title = wp_sprintf( '<h3 class="wp-block-tabs__title">%s</h3>', esc_html( $title ) );
191
99
 
192
- $is_vertical = 'vertical' === ( $attributes['orientation'] ?? 'horizontal' );
100
+ $is_vertical = false;
193
101
 
194
102
  $tag_processor = new WP_HTML_Tag_Processor( $content );
103
+
195
104
  $tag_processor->next_tag( array( 'class_name' => 'wp-block-tabs' ) );
196
- $tag_processor->add_class( $is_vertical ? 'is-vertical' : 'is-horizontal' );
197
105
  $tag_processor->set_attribute( 'data-wp-interactive', 'core/tabs/private' );
106
+
107
+ // Inspect inside the tabs-menu to see if its vertical or not.
108
+ $tag_processor->set_bookmark( 'core/tabs_wrapper' );
109
+ while ( $tag_processor->next_tag( array( 'class_name' => 'wp-block-tabs-menu' ) ) ) {
110
+ if ( $tag_processor->has_class( 'is-vertical' ) ) {
111
+ $is_vertical = true;
112
+ break;
113
+ }
114
+ }
115
+ $tag_processor->seek( 'core/tabs_wrapper' );
116
+
198
117
  $tag_processor->set_attribute(
199
118
  'data-wp-context',
200
119
  wp_json_encode(
@@ -208,48 +127,24 @@ function block_core_tabs_render_block_callback( array $attributes, string $conte
208
127
  $tag_processor->set_attribute( 'data-wp-init', 'callbacks.onTabsInit' );
209
128
  $tag_processor->set_attribute( 'data-wp-on--keydown', 'actions.handleTabKeyDown' );
210
129
 
211
- /**
212
- * Process style attribute.
213
- */
214
- $style = (string) $tag_processor->get_attribute( 'style' );
215
- $style .= block_core_tabs_generate_color_styles( $attributes );
216
- $style .= block_core_tabs_generate_gap_styles( $attributes, $is_vertical );
217
- $style .= block_core_tabs_generate_border_styles( $attributes );
218
- $tag_processor->set_attribute( 'style', $style );
219
-
220
- $updated_content = $tag_processor->get_updated_html();
130
+ $output = $tag_processor->get_updated_html();
221
131
 
222
- /**
223
- * Build the tabs list markup.
224
- * We're doing this manually instead of using <template/> to make it possible
225
- * for other blocks to extend the tabs list via HTML api.
226
- */
227
- $tabs_list_markup = array_map(
228
- static function ( array $tab ): string {
229
- return wp_sprintf(
230
- '<a id="tab__%1$s" class="tabs__tab-label" href="#%1$s" role="tab" aria-controls="%1$s" data-wp-on--click="actions.handleTabClick" data-wp-on--keydown="actions.handleTabKeyDown" data-wp-bind--aria-selected="state.isActiveTab" data-wp-bind--tabindex="state.tabIndexAttribute">%2$s</a>',
231
- $tab['id'],
232
- html_entity_decode( $tab['label'] )
233
- );
234
- },
235
- $tabs_list
236
- );
237
- $tabs_list_markup = implode( '', $tabs_list_markup );
132
+ // Insert the title after the first opening tag.
133
+ $output = preg_replace( '/^(<[^>]+>)/', '$1' . $title, $output );
238
134
 
239
135
  /**
240
- * Splice the tabs list into the content.
136
+ * Builds a client side state for just this tabs instance.
137
+ * This allows 3rd party extensibility of tabs while retaining
138
+ * client side state management per core/tabs instance, like context.
241
139
  */
242
- $content = preg_replace(
243
- '/<ul\s+class="tabs__list">\s*<\/ul>/i',
244
- '<div class="tabs__list" role="tablist">' . $tabs_list_markup . '</div>',
245
- (string) $updated_content
140
+ wp_interactivity_state(
141
+ 'core/tabs/private',
142
+ array(
143
+ $tabs_id => $tabs_list,
144
+ )
246
145
  );
247
146
 
248
- /**
249
- * In the event preg_replace fails, return the tabs content without the list spliced in.
250
- * This ensures the block content is still rendered, albeit without the tabs list.
251
- */
252
- return is_string( $content ) ? $content : (string) $updated_content;
147
+ return $output;
253
148
  }
254
149
 
255
150
  /**
package/src/tabs/save.js CHANGED
@@ -3,20 +3,13 @@
3
3
  */
4
4
  import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
5
5
 
6
- export default function Save( { attributes } ) {
7
- // eslint-disable-next-line react-compiler/react-compiler
8
- const blockProps = useBlockProps.save();
6
+ export default function save( { attributes } ) {
7
+ const { anchor } = attributes;
9
8
 
10
- // eslint-disable-next-line react-compiler/react-compiler
11
- const innerBlocksProps = useInnerBlocksProps.save( {} );
9
+ const tabsId = anchor;
12
10
 
13
- const title = attributes?.metadata?.name || 'Tab Contents';
11
+ const blockProps = useBlockProps.save();
12
+ const innerBlocksProps = useInnerBlocksProps.save( blockProps );
14
13
 
15
- return (
16
- <div { ...blockProps }>
17
- <h3 className="tabs__title">{ title }</h3>
18
- <ul className="tabs__list"></ul>
19
- { innerBlocksProps.children }
20
- </div>
21
- );
14
+ return <div { ...innerBlocksProps } id={ tabsId } />;
22
15
  }
@@ -1,192 +1,6 @@
1
1
  .wp-block-tabs {
2
- display: flex;
3
- flex-direction: row;
4
- flex-wrap: wrap;
5
- gap: var(--wp--style--unstable-tabs-gap, var(--wp--style--tabs-gap-default));
6
2
  box-sizing: border-box;
7
-
8
- --tab-bg: var(--custom-tab-inactive-color, transparent);
9
- --tab-bg-hover: var(--custom-tab-hover-color, #eaeaea);
10
- --tab-bg-active: var(--custom-tab-active-color, #000);
11
- --tab-text: var(--custom-tab-text-color, #000);
12
- --tab-text-hover: var(--custom-tab-hover-text-color, #000);
13
- --tab-text-active: var(--custom-tab-active-text-color, #fff);
14
- --tab-opacity: 0.5;
15
- --tab-opacity-hover: 1;
16
- --tab-opacity-active: 1;
17
- --tab-outline-width: 0;
18
- --tab-underline-width: 0;
19
- --tab-side-border-width: 0;
20
- --tab-border-color: var(--custom-tab-inactive-color, transparent);
21
- --tab-border-color-hover: var(--custom-tab-hover-color, #000);
22
- --tab-border-color-active: var(--custom-tab-active-color, #000);
23
- --tab-padding-block: var(--wp--preset--spacing--20, 0.5em);
24
- --tab-padding-inline: var(--wp--preset--spacing--30, 1em);
25
- --tab-border-radius: 0;
26
- --tabs-divider-color: var(--custom-tab-active-color, #000);
27
-
28
- .tabs__title {
3
+ .wp-block-tabs__title {
29
4
  display: none;
30
5
  }
31
-
32
- .tabs__list {
33
- display: flex;
34
- list-style: none;
35
- margin: 0;
36
- padding: 0;
37
- align-items: flex-end;
38
- flex-grow: 1;
39
- flex-basis: 100%;
40
- gap: var(--wp--style--unstable-tabs-list-gap, var(--wp--style--tabs-gap-default));
41
- min-width: 100px;
42
-
43
- button.tabs__tab-label {
44
- font-size: inherit;
45
- font-family: inherit;
46
- font-weight: inherit;
47
- line-height: inherit;
48
- letter-spacing: inherit;
49
- text-transform: inherit;
50
- text-align: inherit;
51
- }
52
-
53
- .tabs__tab-label {
54
- box-sizing: border-box;
55
- display: block;
56
- width: max-content;
57
- margin: 0;
58
- opacity: var(--tab-opacity);
59
- padding-block: var(--tab-padding-block);
60
- padding-inline: var(--tab-padding-inline);
61
- text-decoration: none;
62
- cursor: pointer;
63
- background-color: var(--tab-bg);
64
- color: var(--tab-text);
65
- border: var(--tab-outline-width) solid var(--tab-border-color);
66
- border-block-end: var(--tab-underline-width) solid var(--tab-border-color);
67
- border-inline-end: var(--tab-side-border-width) solid var(--tab-border-color);
68
- border-radius: var(--tab-border-radius);
69
- &:focus {
70
- outline: none;
71
- }
72
- &:focus-visible {
73
- outline: 2px solid var(--tab-border-color-active);
74
- outline-offset: 2px;
75
- }
76
- &:hover {
77
- opacity: var(--tab-opacity-hover);
78
- background-color: var(--tab-bg-hover);
79
- color: var(--tab-text-hover);
80
- border-color: var(--tab-border-color-hover);
81
- }
82
- &[aria-selected="true"] {
83
- opacity: var(--tab-opacity-active);
84
- background-color: var(--tab-bg-active);
85
- color: var(--tab-text-active);
86
- border-color: var(--tab-border-color-active);
87
- }
88
- }
89
- }
90
-
91
- &:not(.is-vertical) {
92
- .tabs__list {
93
- overflow-x: auto;
94
- border-block-end: 1px solid var(--tabs-divider-color);
95
- }
96
- }
97
-
98
- &.is-vertical {
99
- flex-wrap: nowrap;
100
- .tabs__list {
101
- flex-grow: 0;
102
- flex-direction: column;
103
- max-width: 30%;
104
- border-inline-end: 1px solid var(--tabs-divider-color);
105
- border-block-end: none;
106
- overflow-x: hidden;
107
- }
108
- .tabs__tab-label {
109
- max-width: 100%;
110
- text-align: end;
111
- }
112
- }
113
- }
114
-
115
- // Links style.
116
- .wp-block-tabs.is-style-links {
117
- --tabs-divider-color: var(--custom-tab-hover-color, #000);
118
- --tab-bg: transparent;
119
- --tab-bg-hover: transparent;
120
- --tab-bg-active: transparent;
121
- --tab-text: var(--custom-tab-text-color, #000);
122
- --tab-text-hover: var(--custom-tab-hover-text-color, #000);
123
- --tab-text-active: var(--custom-tab-active-text-color, #000);
124
- --tab-border-color: var(--custom-tab-inactive-color, transparent);
125
- --tab-border-color-hover: var(--custom-tab-hover-color, #000);
126
- --tab-border-color-active: var(--custom-tab-active-color, #000);
127
- --tab-underline-width: 2px;
128
-
129
- .tabs__tab-label {
130
- /* preserve extra bottom space for underline */
131
- padding-block-end: 0.5em;
132
- border-bottom: 2px solid var(--tab-border-color);
133
- &[aria-selected="true"] {
134
- background-color: inherit;
135
- color: var(--tab-text-active);
136
- border-color: var(--tab-border-color-active);
137
- }
138
- &:hover:not([aria-selected="true"]) {
139
- background-color: inherit;
140
- color: var(--tab-text-hover);
141
- border-color: var(--tab-border-color-hover);
142
- }
143
- }
144
-
145
- &.is-vertical {
146
- .tabs__list {
147
- border-block-end: none;
148
- border-inline-end: 1px solid var(--tabs-divider-color);
149
- }
150
- .tabs__tab-label {
151
- /* switch underline to side for vertical */
152
- border-block-end: 0;
153
- --tab-underline-width: 0;
154
- --tab-side-border-width: 2px;
155
- padding-block-end: 0;
156
- padding-inline-end: 0.5em;
157
- }
158
- }
159
- }
160
-
161
- // Button style.
162
- .wp-block-tabs.is-style-button {
163
- --tab-border-radius: 9999px;
164
- --tab-bg: var(--custom-tab-inactive-color, transparent);
165
- --tab-bg-hover: var(--custom-tab-hover-color, #000);
166
- --tab-bg-active: var(--custom-tab-active-color, #000);
167
- --tab-text: var(--custom-tab-text-color, #000);
168
- --tab-text-hover: var(--custom-tab-hover-text-color, #000);
169
- --tab-text-active: var(--custom-tab-active-text-color, #000);
170
- --tab-padding-block: 0.5em;
171
- --tab-padding-inline: 1em;
172
-
173
- .tabs__list {
174
- border-block-end: none;
175
- align-items: center;
176
- }
177
-
178
- .tabs__tab-label {
179
- border-width: 0;
180
- &:hover:not([aria-selected="true"]) {
181
- background-color: var(--tab-bg-hover);
182
- }
183
- }
184
-
185
- &.is-vertical {
186
- .tabs__list {
187
- border-block-end: none;
188
- border-inline-end: none;
189
- align-items: flex-end;
190
- }
191
- }
192
6
  }
@@ -0,0 +1,77 @@
1
+ {
2
+ "$schema": "https://schemas.wp.org/trunk/block.json",
3
+ "__experimental": true,
4
+ "apiVersion": 3,
5
+ "name": "core/tabs-menu",
6
+ "title": "Tabs Menu",
7
+ "description": "Display the tab buttons for a tabbed interface.",
8
+ "version": "1.0.0",
9
+ "category": "design",
10
+ "textdomain": "default",
11
+ "parent": [
12
+ "core/tabs"
13
+ ],
14
+ "allowedBlocks": [
15
+ "core/tabs-menu-item"
16
+ ],
17
+ "usesContext": [
18
+ "core/tabs-list",
19
+ "core/tabs-id",
20
+ "core/tabs-activeTabIndex",
21
+ "core/tabs-editorActiveTabIndex"
22
+ ],
23
+ "attributes": {},
24
+ "supports": {
25
+ "html": false,
26
+ "reusable": false,
27
+ "lock": false,
28
+ "dimensions": {
29
+ "aspectRatio": false,
30
+ "height": false,
31
+ "minHeight": false,
32
+ "width": false
33
+ },
34
+ "color": {
35
+ "background": true,
36
+ "text": true,
37
+ "__experimentalDefaultControls": {
38
+ "background": true,
39
+ "text": true
40
+ }
41
+ },
42
+ "typography": {
43
+ "fontSize": true,
44
+ "__experimentalFontFamily": true
45
+ },
46
+ "__experimentalBorder": {
47
+ "color": true,
48
+ "radius": true,
49
+ "style": true,
50
+ "width": true
51
+ },
52
+ "layout": {
53
+ "default": {
54
+ "type": "flex",
55
+ "flexWrap": "nowrap",
56
+ "orientation": "horizontal"
57
+ },
58
+ "allowSwitching": false,
59
+ "allowVerticalAlignment": true,
60
+ "allowJustification": true,
61
+ "allowOrientation": true
62
+ },
63
+ "spacing": {
64
+ "padding": true,
65
+ "margin": true,
66
+ "blockGap": true,
67
+ "__experimentalDefaultControls": {
68
+ "padding": true,
69
+ "margin": true,
70
+ "blockGap": true
71
+ }
72
+ }
73
+ },
74
+ "editorScript": "file:./index.js",
75
+ "editorStyle": "file:./editor.css",
76
+ "style": "file:./style-index.css"
77
+ }