@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
@@ -0,0 +1,251 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { __ } from '@wordpress/i18n';
10
+ import {
11
+ useBlockProps,
12
+ useInnerBlocksProps,
13
+ BlockContextProvider,
14
+ __experimentalUseBlockPreview as useBlockPreview,
15
+ store as blockEditorStore,
16
+ useBlockEditContext,
17
+ } from '@wordpress/block-editor';
18
+ import { useSelect, useDispatch } from '@wordpress/data';
19
+ import {
20
+ memo,
21
+ useMemo,
22
+ useState,
23
+ useEffect,
24
+ useCallback,
25
+ } from '@wordpress/element';
26
+
27
+ /**
28
+ * Internal dependencies
29
+ */
30
+ import AddTabToolbarControl from '../tab/add-tab-toolbar-control';
31
+ import RemoveTabToolbarControl from '../tab/remove-tab-toolbar-control';
32
+
33
+ const TABS_MENU_ITEM_TEMPLATE = [ [ 'core/tabs-menu-item', {} ] ];
34
+
35
+ /**
36
+ * Preview component for non-active tab menu items.
37
+ * Uses useBlockPreview to cache the rendering.
38
+ *
39
+ * @param {Object} props Component props.
40
+ * @param {Array} props.blocks The blocks to preview.
41
+ * @param {string} props.blockContextId The context ID for this block.
42
+ * @param {boolean} props.isHidden Whether the preview is hidden.
43
+ * @param {Function} props.setActiveBlockContextId Callback to set the active context ID.
44
+ */
45
+ function TabsMenuItemPreview( {
46
+ blocks,
47
+ blockContextId,
48
+ isHidden,
49
+ setActiveBlockContextId,
50
+ } ) {
51
+ const blockPreviewProps = useBlockPreview( { blocks } );
52
+
53
+ const handleOnClick = () => {
54
+ setActiveBlockContextId( blockContextId );
55
+ };
56
+
57
+ const style = {
58
+ display: isHidden ? 'none' : 'flex',
59
+ };
60
+
61
+ return (
62
+ <div
63
+ { ...blockPreviewProps }
64
+ tabIndex={ 0 }
65
+ role="button"
66
+ onClick={ handleOnClick }
67
+ onKeyDown={ handleOnClick }
68
+ style={ style }
69
+ />
70
+ );
71
+ }
72
+
73
+ const MemoizedTabsMenuItemPreview = memo( TabsMenuItemPreview );
74
+
75
+ /**
76
+ * The actual editable inner blocks for the active tab item.
77
+ *
78
+ * @param {Object} props Component props.
79
+ * @param {Object} props.wrapperProps Props to pass to the wrapper element.
80
+ * @param {Object} props.layout The layout object to pass to inner blocks.
81
+ */
82
+ function TabsMenuItemTemplateBlocks( { wrapperProps = {}, layout } ) {
83
+ const innerBlocksProps = useInnerBlocksProps( wrapperProps, {
84
+ template: TABS_MENU_ITEM_TEMPLATE,
85
+ templateLock: 'all',
86
+ renderAppender: false,
87
+ layout,
88
+ } );
89
+ return innerBlocksProps.children;
90
+ }
91
+
92
+ function Edit( {
93
+ context,
94
+ clientId,
95
+ __unstableLayoutClassNames: layoutClassNames,
96
+ } ) {
97
+ // Get the layout from block edit context to pass to inner blocks.
98
+ // This ensures the correct orientation is used from the start.
99
+ const { layout } = useBlockEditContext();
100
+
101
+ const tabsId = context[ 'core/tabs-id' ] || null;
102
+ const tabsList = context[ 'core/tabs-list' ] || [];
103
+ const activeTabIndex = context[ 'core/tabs-activeTabIndex' ] ?? 0;
104
+ const editorActiveTabIndex = context[ 'core/tabs-editorActiveTabIndex' ];
105
+
106
+ // Memoize effectiveActiveIndex to ensure it updates when context changes
107
+ const effectiveActiveIndex = useMemo( () => {
108
+ return editorActiveTabIndex ?? activeTabIndex;
109
+ }, [ editorActiveTabIndex, activeTabIndex ] );
110
+
111
+ const { __unstableMarkNextChangeAsNotPersistent } =
112
+ useDispatch( blockEditorStore );
113
+ const { updateBlockAttributes } = useDispatch( blockEditorStore );
114
+
115
+ // Track which tab context is "active" for editing (shows real inner blocks)
116
+ const [ activeBlockContextId, setActiveBlockContextId ] = useState( null );
117
+
118
+ // Get the inner blocks (the single tabs-menu-item template)
119
+ const { blocks, tabsClientId } = useSelect(
120
+ ( select ) => {
121
+ const { getBlocks, getBlockRootClientId } =
122
+ select( blockEditorStore );
123
+ return {
124
+ blocks: getBlocks( clientId ),
125
+ tabsClientId: getBlockRootClientId( clientId ),
126
+ };
127
+ },
128
+ [ clientId ]
129
+ );
130
+
131
+ // Build block contexts for each tab
132
+ const blockContexts = useMemo( () => {
133
+ return tabsList.map( ( tab, index ) => ( {
134
+ 'core/tabs-menu-item-index': index,
135
+ 'core/tabs-menu-item-id': tab.id || `tab-${ index }`,
136
+ 'core/tabs-menu-item-label': tab.label || '',
137
+ 'core/tabs-menu-item-clientId': tab.clientId,
138
+ // Pass through parent context
139
+ 'core/tabs-id': tabsId,
140
+ 'core/tabs-list': tabsList,
141
+ 'core/tabs-activeTabIndex': activeTabIndex,
142
+ 'core/tabs-editorActiveTabIndex': editorActiveTabIndex,
143
+ } ) );
144
+ }, [ tabsList, tabsId, activeTabIndex, editorActiveTabIndex ] );
145
+
146
+ // Generate a unique ID for each block context
147
+ const getContextId = useCallback( ( blockContext ) => {
148
+ return `tab-context-${ blockContext[ 'core/tabs-menu-item-index' ] }`;
149
+ }, [] );
150
+
151
+ // Set the first tab as active by default
152
+ useEffect( () => {
153
+ if ( blockContexts.length > 0 && activeBlockContextId === null ) {
154
+ setActiveBlockContextId( getContextId( blockContexts[ 0 ] ) );
155
+ }
156
+ }, [ blockContexts, activeBlockContextId, getContextId ] );
157
+
158
+ // Update active context when editorActiveTabIndex changes
159
+ useEffect( () => {
160
+ if (
161
+ blockContexts.length > 0 &&
162
+ effectiveActiveIndex < blockContexts.length
163
+ ) {
164
+ const newContextId = getContextId(
165
+ blockContexts[ effectiveActiveIndex ]
166
+ );
167
+ setActiveBlockContextId( ( prevId ) =>
168
+ prevId !== newContextId ? newContextId : prevId
169
+ );
170
+ }
171
+ }, [ effectiveActiveIndex, blockContexts, getContextId ] );
172
+
173
+ // Handle tab click to update parent tabs block's editorActiveTabIndex
174
+ const handleTabContextClick = useCallback(
175
+ ( index ) => {
176
+ if ( tabsClientId && index !== effectiveActiveIndex ) {
177
+ __unstableMarkNextChangeAsNotPersistent();
178
+ updateBlockAttributes( tabsClientId, {
179
+ editorActiveTabIndex: index,
180
+ } );
181
+ }
182
+ },
183
+ [
184
+ tabsClientId,
185
+ effectiveActiveIndex,
186
+ updateBlockAttributes,
187
+ __unstableMarkNextChangeAsNotPersistent,
188
+ ]
189
+ );
190
+
191
+ const blockProps = useBlockProps( {
192
+ className: clsx( layoutClassNames ),
193
+ role: 'tablist',
194
+ } );
195
+
196
+ // If no tabs exist yet, show placeholder
197
+ if ( tabsList.length === 0 ) {
198
+ return (
199
+ <>
200
+ <AddTabToolbarControl tabsClientId={ tabsClientId } />
201
+ <RemoveTabToolbarControl tabsClientId={ tabsClientId } />
202
+ <div { ...blockProps }>
203
+ <span className="tabs__tab-label tabs__tab-label--placeholder">
204
+ { __( 'Add tabs to display menu' ) }
205
+ </span>
206
+ </div>
207
+ </>
208
+ );
209
+ }
210
+
211
+ return (
212
+ <>
213
+ <AddTabToolbarControl tabsClientId={ tabsClientId } />
214
+ <RemoveTabToolbarControl tabsClientId={ tabsClientId } />
215
+ <div { ...blockProps }>
216
+ { blockContexts.map( ( blockContext, index ) => {
217
+ const contextId = getContextId( blockContext );
218
+ const isVisible = contextId === activeBlockContextId;
219
+
220
+ return (
221
+ <BlockContextProvider
222
+ key={ contextId }
223
+ value={ blockContext }
224
+ >
225
+ { isVisible ? (
226
+ <TabsMenuItemTemplateBlocks
227
+ wrapperProps={ {
228
+ onClick: () =>
229
+ handleTabContextClick( index ),
230
+ } }
231
+ layout={ layout }
232
+ />
233
+ ) : null }
234
+ <MemoizedTabsMenuItemPreview
235
+ blocks={ blocks }
236
+ blockContextId={ contextId }
237
+ setActiveBlockContextId={ ( id ) => {
238
+ setActiveBlockContextId( id );
239
+ handleTabContextClick( index );
240
+ } }
241
+ isHidden={ isVisible }
242
+ />
243
+ </BlockContextProvider>
244
+ );
245
+ } ) }
246
+ </div>
247
+ </>
248
+ );
249
+ }
250
+
251
+ export default Edit;
@@ -0,0 +1,6 @@
1
+ .wp-block-tabs-menu {
2
+ .tabs__tab-label--placeholder {
3
+ opacity: 0.5;
4
+ font-style: italic;
5
+ }
6
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { SVG, Path } from '@wordpress/primitives';
5
+
6
+ export default (
7
+ <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
8
+ <Path d="M4 5.5H20V7H4V5.5ZM4 11.5H12V13H4V11.5ZM20 11.5H14V13H20V11.5ZM4 17.5H20V19H4V17.5Z" />
9
+ </SVG>
10
+ );
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import initBlock from '../utils/init-block';
5
+ import edit from './edit';
6
+ import save from './save';
7
+ import icon from './icon';
8
+
9
+ import metadata from './block.json';
10
+
11
+ const { name } = metadata;
12
+
13
+ export { metadata, name };
14
+
15
+ export const settings = {
16
+ icon,
17
+ edit,
18
+ save,
19
+ };
20
+
21
+ export const init = () => initBlock( { name, metadata, settings } );
@@ -0,0 +1,74 @@
1
+ <?php
2
+ /**
3
+ * Tabs Menu Block
4
+ *
5
+ * @package WordPress
6
+ */
7
+
8
+ /**
9
+ * Render callback for core/tabs-menu.
10
+ *
11
+ * @param array $attributes Block attributes.
12
+ * @param string $content Block content (contains the tabs-menu-item template).
13
+ * @param \WP_Block $block WP_Block instance.
14
+ *
15
+ * @return string Updated HTML.
16
+ */
17
+ function block_core_tabs_menu_render_callback( array $attributes, string $content, \WP_Block $block ): string {
18
+ $tabs_list = $block->context['core/tabs-list'] ?? array();
19
+
20
+ if ( empty( $tabs_list ) ) {
21
+ return '';
22
+ }
23
+
24
+ // Get the first inner block as template (tabs-menu-item)
25
+ $inner_blocks = $block->parsed_block['innerBlocks'] ?? array();
26
+ if ( empty( $inner_blocks ) ) {
27
+ return '';
28
+ }
29
+ $template_block = $inner_blocks[0];
30
+
31
+ // Build rendered tab items
32
+ $tabs_markup = '';
33
+ foreach ( $tabs_list as $index => $tab ) {
34
+ // Create context for this specific tab
35
+ $tab_context = array_merge(
36
+ $block->context,
37
+ array(
38
+ 'core/tabs-menu-item-index' => $index,
39
+ 'core/tabs-menu-item-id' => $tab['id'] ?? '',
40
+ 'core/tabs-menu-item-label' => $tab['label'] ?? '',
41
+ )
42
+ );
43
+
44
+ // Create new WP_Block instance with template and context
45
+ $tab_block = new WP_Block( $template_block, $tab_context );
46
+
47
+ // Render the block
48
+ $tabs_markup .= $tab_block->render();
49
+ }
50
+
51
+ // Find the template block and replace it in $content with $tabs_markup
52
+ $content = preg_replace(
53
+ '/<a\b[^>]*\bwp-block-tabs-menu-item__template\b[^>]*>.*?<\/a>/si',
54
+ $tabs_markup,
55
+ $content
56
+ );
57
+
58
+ return $content;
59
+ }
60
+
61
+ /**
62
+ * Registers the `core/tabs-menu` block on the server.
63
+ *
64
+ * @since 6.9.0
65
+ */
66
+ function register_block_core_tabs_menu() {
67
+ register_block_type_from_metadata(
68
+ __DIR__ . '/tabs-menu',
69
+ array(
70
+ 'render_callback' => 'block_core_tabs_menu_render_callback',
71
+ )
72
+ );
73
+ }
74
+ add_action( 'init', 'register_block_core_tabs_menu' );
@@ -0,0 +1,18 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+
5
+ /**
6
+ * WordPress dependencies
7
+ */
8
+ import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
9
+
10
+ export default function save() {
11
+ const blockProps = useBlockProps.save( {
12
+ role: 'tablist',
13
+ } );
14
+
15
+ const innerBlocksProps = useInnerBlocksProps.save( blockProps );
16
+
17
+ return <div { ...innerBlocksProps } />;
18
+ }
@@ -0,0 +1,8 @@
1
+ .wp-block-tabs-menu {
2
+ display: flex;
3
+ align-items: flex-end;
4
+ min-width: fit-content;
5
+ border-bottom-width: 1px; // A default border divider between the tabs-menu and the tab-panels
6
+ border-bottom-style: solid;
7
+ border-bottom-color: #000;
8
+ }
@@ -0,0 +1,98 @@
1
+ {
2
+ "$schema": "https://schemas.wp.org/trunk/block.json",
3
+ "__experimental": true,
4
+ "apiVersion": 3,
5
+ "name": "core/tabs-menu-item",
6
+ "title": "Tab Menu Item",
7
+ "description": "A single tab button in the tabs menu. Used as a template for styling all tab buttons.",
8
+ "version": "1.0.0",
9
+ "category": "design",
10
+ "textdomain": "default",
11
+ "parent": [
12
+ "core/tabs-menu"
13
+ ],
14
+ "usesContext": [
15
+ "core/tabs-menu-item-index",
16
+ "core/tabs-menu-item-id",
17
+ "core/tabs-menu-item-label",
18
+ "core/tabs-menu-item-clientId",
19
+ "core/tabs-list",
20
+ "core/tabs-activeTabIndex",
21
+ "core/tabs-editorActiveTabIndex"
22
+ ],
23
+ "attributes": {
24
+ "activeBackgroundColor": {
25
+ "type": "string"
26
+ },
27
+ "customActiveBackgroundColor": {
28
+ "type": "string"
29
+ },
30
+ "activeTextColor": {
31
+ "type": "string"
32
+ },
33
+ "customActiveTextColor": {
34
+ "type": "string"
35
+ },
36
+ "hoverBackgroundColor": {
37
+ "type": "string"
38
+ },
39
+ "customHoverBackgroundColor": {
40
+ "type": "string"
41
+ },
42
+ "hoverTextColor": {
43
+ "type": "string"
44
+ },
45
+ "customHoverTextColor": {
46
+ "type": "string"
47
+ }
48
+ },
49
+ "supports": {
50
+ "html": false,
51
+ "reusable": false,
52
+ "lock": false,
53
+ "color": {
54
+ "background": true,
55
+ "text": true,
56
+ "__experimentalDefaultControls": {
57
+ "background": true,
58
+ "text": true
59
+ }
60
+ },
61
+ "shadow": true,
62
+ "typography": {
63
+ "fontSize": true,
64
+ "__experimentalFontFamily": true,
65
+ "textAlign": true,
66
+ "__experimentalDefaultControls": {
67
+ "fontSize": true
68
+ }
69
+ },
70
+ "layout": {
71
+ "default": {
72
+ "type": "flex",
73
+ "orientation": "vertical",
74
+ "flexWrap": "nowrap"
75
+ },
76
+ "allowVerticalAlignment": true,
77
+ "allowJustification": true,
78
+ "allowSwitching": false,
79
+ "allowOrientation": false,
80
+ "allowWrap": false
81
+ },
82
+ "spacing": {
83
+ "padding": true,
84
+ "__experimentalDefaultControls": {
85
+ "padding": true
86
+ }
87
+ },
88
+ "__experimentalBorder": {
89
+ "radius": true,
90
+ "color": true,
91
+ "width": true,
92
+ "style": true
93
+ }
94
+ },
95
+ "editorScript": "file:./index.js",
96
+ "editorStyle": "file:./editor.css",
97
+ "style": "file:./style-index.css"
98
+ }