@wordpress/block-library 9.41.1-next.v.202603102151.0 → 9.42.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.
- package/CHANGELOG.md +2 -0
- package/build/cover/edit/cover-placeholder.cjs +7 -0
- package/build/cover/edit/cover-placeholder.cjs.map +2 -2
- package/build/html/modal.cjs +151 -229
- package/build/html/modal.cjs.map +2 -2
- package/build/image/edit.cjs +7 -0
- package/build/image/edit.cjs.map +2 -2
- package/build/media-text/media-container.cjs +6 -0
- package/build/media-text/media-container.cjs.map +2 -2
- package/build/navigation/edit/index.cjs +5 -4
- package/build/navigation/edit/index.cjs.map +2 -2
- package/build/navigation-link/shared/use-link-preview.cjs +29 -0
- package/build/navigation-link/shared/use-link-preview.cjs.map +2 -2
- package/build/nextpage/block.json +0 -1
- package/build/playlist-track/block.json +0 -0
- package/build/post-date/block.json +1 -3
- package/build/post-date/deprecated.cjs +82 -6
- package/build/post-date/deprecated.cjs.map +3 -3
- package/build/post-date/edit.cjs +49 -62
- package/build/post-date/edit.cjs.map +3 -3
- package/build/site-logo/edit.cjs +1 -3
- package/build/site-logo/edit.cjs.map +2 -2
- package/build/site-title/index.cjs +5 -1
- package/build/site-title/index.cjs.map +2 -2
- package/build/tab/add-tab-toolbar-control.cjs +22 -5
- package/build/tab/add-tab-toolbar-control.cjs.map +2 -2
- package/build/tab/remove-tab-toolbar-control.cjs +19 -1
- package/build/tab/remove-tab-toolbar-control.cjs.map +2 -2
- package/build/tabs/edit.cjs +85 -7
- package/build/tabs/edit.cjs.map +2 -2
- package/build/tabs/index.cjs +12 -2
- package/build/tabs/index.cjs.map +2 -2
- package/build/tabs-menu/block.json +1 -6
- package/build/tabs-menu/edit.cjs +11 -151
- package/build/tabs-menu/edit.cjs.map +3 -3
- package/build/tabs-menu/save.cjs.map +2 -2
- package/build/tabs-menu-item/block.json +14 -11
- package/build/tabs-menu-item/controls.cjs +2 -133
- package/build/tabs-menu-item/controls.cjs.map +3 -3
- package/build/tabs-menu-item/edit.cjs +44 -56
- package/build/tabs-menu-item/edit.cjs.map +3 -3
- package/build/tabs-menu-item/save.cjs +0 -1
- package/build/tabs-menu-item/save.cjs.map +2 -2
- package/build/utils/media-control.cjs +72 -29
- package/build/utils/media-control.cjs.map +3 -3
- package/build-module/cover/edit/cover-placeholder.mjs +7 -0
- package/build-module/cover/edit/cover-placeholder.mjs.map +2 -2
- package/build-module/html/modal.mjs +151 -229
- package/build-module/html/modal.mjs.map +2 -2
- package/build-module/image/edit.mjs +7 -0
- package/build-module/image/edit.mjs.map +2 -2
- package/build-module/media-text/media-container.mjs +7 -1
- package/build-module/media-text/media-container.mjs.map +2 -2
- package/build-module/navigation/edit/index.mjs +5 -4
- package/build-module/navigation/edit/index.mjs.map +2 -2
- package/build-module/navigation-link/shared/use-link-preview.mjs +28 -0
- package/build-module/navigation-link/shared/use-link-preview.mjs.map +2 -2
- package/build-module/nextpage/block.json +0 -1
- package/build-module/playlist-track/block.json +0 -0
- package/build-module/post-date/block.json +1 -3
- package/build-module/post-date/deprecated.mjs +82 -6
- package/build-module/post-date/deprecated.mjs.map +2 -2
- package/build-module/post-date/edit.mjs +49 -63
- package/build-module/post-date/edit.mjs.map +2 -2
- package/build-module/site-logo/edit.mjs +1 -3
- package/build-module/site-logo/edit.mjs.map +2 -2
- package/build-module/site-title/index.mjs +5 -1
- package/build-module/site-title/index.mjs.map +2 -2
- package/build-module/tab/add-tab-toolbar-control.mjs +22 -5
- package/build-module/tab/add-tab-toolbar-control.mjs.map +2 -2
- package/build-module/tab/remove-tab-toolbar-control.mjs +19 -1
- package/build-module/tab/remove-tab-toolbar-control.mjs.map +2 -2
- package/build-module/tabs/edit.mjs +87 -9
- package/build-module/tabs/edit.mjs.map +2 -2
- package/build-module/tabs/index.mjs +12 -2
- package/build-module/tabs/index.mjs.map +2 -2
- package/build-module/tabs-menu/block.json +1 -6
- package/build-module/tabs-menu/edit.mjs +13 -162
- package/build-module/tabs-menu/edit.mjs.map +2 -2
- package/build-module/tabs-menu/save.mjs.map +2 -2
- package/build-module/tabs-menu-item/block.json +14 -11
- package/build-module/tabs-menu-item/controls.mjs +4 -143
- package/build-module/tabs-menu-item/controls.mjs.map +2 -2
- package/build-module/tabs-menu-item/edit.mjs +45 -57
- package/build-module/tabs-menu-item/edit.mjs.map +3 -3
- package/build-module/tabs-menu-item/save.mjs +0 -1
- package/build-module/tabs-menu-item/save.mjs.map +2 -2
- package/build-module/utils/media-control.mjs +73 -30
- package/build-module/utils/media-control.mjs.map +2 -2
- package/build-style/editor-rtl.css +45 -11
- package/build-style/editor.css +45 -11
- package/build-style/navigation/style-rtl.css +4 -0
- package/build-style/navigation/style.css +4 -0
- package/build-style/navigation-overlay-close/style-rtl.css +3 -3
- package/build-style/navigation-overlay-close/style.css +3 -3
- package/build-style/style-rtl.css +7 -3
- package/build-style/style.css +7 -3
- package/build-style/tabs-menu/editor-rtl.css +5 -3
- package/build-style/tabs-menu/editor.css +5 -3
- package/package.json +38 -38
- package/src/cover/edit/cover-placeholder.js +8 -0
- package/src/html/modal.js +6 -77
- package/src/image/edit.js +8 -0
- package/src/media-text/media-container.js +8 -1
- package/src/navigation/edit/index.js +6 -4
- package/src/navigation/index.php +24 -17
- package/src/navigation/style.scss +10 -0
- package/src/navigation-link/index.php +9 -9
- package/src/navigation-link/shared/test/use-link-preview.test.js +149 -0
- package/src/navigation-link/shared/use-link-preview.js +43 -1
- package/src/navigation-overlay-close/style.scss +3 -3
- package/src/navigation-submenu/index.php +17 -11
- package/src/nextpage/block.json +0 -1
- package/src/playlist-track/block.json +0 -0
- package/src/playlist-track/edit.js +0 -0
- package/src/playlist-track/index.js +0 -0
- package/src/playlist-track/index.php +0 -0
- package/src/playlist-track/init.js +0 -0
- package/src/playlist-track/style.scss +0 -0
- package/src/post-date/block.json +1 -3
- package/src/post-date/deprecated.js +86 -6
- package/src/post-date/edit.js +65 -82
- package/src/site-logo/edit.js +1 -3
- package/src/site-title/index.js +5 -1
- package/src/tab/add-tab-toolbar-control.js +48 -23
- package/src/tab/remove-tab-toolbar-control.js +30 -10
- package/src/tabs/edit.js +133 -10
- package/src/tabs/index.js +12 -2
- package/src/tabs-menu/block.json +1 -6
- package/src/tabs-menu/edit.js +13 -214
- package/src/tabs-menu/editor.scss +7 -3
- package/src/tabs-menu/index.php +42 -27
- package/src/tabs-menu/save.js +0 -4
- package/src/tabs-menu-item/block.json +14 -11
- package/src/tabs-menu-item/controls.js +4 -167
- package/src/tabs-menu-item/edit.js +60 -69
- package/src/tabs-menu-item/index.php +11 -23
- package/src/tabs-menu-item/save.js +0 -1
- package/src/utils/media-control.js +61 -21
- package/src/utils/media-control.scss +54 -18
package/src/tabs/edit.js
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
BlockContextProvider,
|
|
8
8
|
store as blockEditorStore,
|
|
9
9
|
} from '@wordpress/block-editor';
|
|
10
|
-
import { useSelect } from '@wordpress/data';
|
|
11
|
-
import { useMemo, useEffect } from '@wordpress/element';
|
|
10
|
+
import { useSelect, useDispatch } from '@wordpress/data';
|
|
11
|
+
import { useMemo, useEffect, useRef } from '@wordpress/element';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Internal dependencies
|
|
@@ -23,6 +23,10 @@ const TABS_TEMPLATE = [
|
|
|
23
23
|
remove: true,
|
|
24
24
|
},
|
|
25
25
|
},
|
|
26
|
+
[
|
|
27
|
+
[ 'core/tabs-menu-item', { anchor: 'tab-1-button' } ],
|
|
28
|
+
[ 'core/tabs-menu-item', { anchor: 'tab-2-button' } ],
|
|
29
|
+
],
|
|
26
30
|
],
|
|
27
31
|
[
|
|
28
32
|
'core/tab-panel',
|
|
@@ -40,6 +44,14 @@ const TABS_TEMPLATE = [
|
|
|
40
44
|
},
|
|
41
45
|
[ [ 'core/paragraph' ] ],
|
|
42
46
|
],
|
|
47
|
+
[
|
|
48
|
+
'core/tab',
|
|
49
|
+
{
|
|
50
|
+
anchor: 'tab-2',
|
|
51
|
+
label: 'Tab 2',
|
|
52
|
+
},
|
|
53
|
+
[ [ 'core/paragraph' ] ],
|
|
54
|
+
],
|
|
43
55
|
],
|
|
44
56
|
],
|
|
45
57
|
];
|
|
@@ -62,30 +74,141 @@ function Edit( {
|
|
|
62
74
|
}
|
|
63
75
|
}, [] ); // eslint-disable-line react-hooks/exhaustive-deps
|
|
64
76
|
|
|
77
|
+
const { removeBlock } = useDispatch( blockEditorStore );
|
|
78
|
+
|
|
65
79
|
/**
|
|
66
80
|
* Construct a list of core/tab blocks, used to create tabs-list context.
|
|
81
|
+
* Also select menu items with their anchors for anchor-based deletion sync.
|
|
67
82
|
*/
|
|
68
|
-
const tabs = useSelect(
|
|
83
|
+
const { tabs, menuItems } = useSelect(
|
|
69
84
|
( select ) => {
|
|
70
85
|
const { getBlocks } = select( blockEditorStore );
|
|
71
86
|
const innerBlocks = getBlocks( clientId );
|
|
72
87
|
|
|
73
|
-
// Find tab-panel block and extract tab data
|
|
88
|
+
// Find tab-panel block and extract tab data.
|
|
74
89
|
const tabPanel = innerBlocks.find(
|
|
75
90
|
( block ) => block.name === 'core/tab-panel'
|
|
76
91
|
);
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return tabPanel.innerBlocks.filter(
|
|
83
|
-
( block ) => block.name === 'core/tab'
|
|
93
|
+
// Find tabs-menu block and get its children with their anchors.
|
|
94
|
+
const tabsMenu = innerBlocks.find(
|
|
95
|
+
( block ) => block.name === 'core/tabs-menu'
|
|
84
96
|
);
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
tabs: tabPanel
|
|
100
|
+
? tabPanel.innerBlocks.filter(
|
|
101
|
+
( block ) => block.name === 'core/tab'
|
|
102
|
+
)
|
|
103
|
+
: [],
|
|
104
|
+
menuItems: tabsMenu
|
|
105
|
+
? getBlocks( tabsMenu.clientId )
|
|
106
|
+
.filter( ( b ) => b.name === 'core/tabs-menu-item' )
|
|
107
|
+
.map( ( b ) => ( {
|
|
108
|
+
clientId: b.clientId,
|
|
109
|
+
anchor: b.attributes.anchor ?? '',
|
|
110
|
+
} ) )
|
|
111
|
+
: [],
|
|
112
|
+
};
|
|
85
113
|
},
|
|
86
114
|
[ clientId ]
|
|
87
115
|
);
|
|
88
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Keep tabs and menu items in sync when either is deleted directly (e.g.
|
|
119
|
+
* via the Backspace key or List View).
|
|
120
|
+
*
|
|
121
|
+
* TODO: This effect only handles deletions. The two lists can get out of
|
|
122
|
+
* sync in other cases: if a user pastes a core/tab block into the tab-panel
|
|
123
|
+
* (or duplicates one), no corresponding tabs-menu-item is created; if a
|
|
124
|
+
* user drags and drops a tabs-menu-item, the tab panel is not copied with
|
|
125
|
+
* it. We should extend this effect to handle insertions, detecting when
|
|
126
|
+
* tabs.length > menuItems.length and inserting the missing menu
|
|
127
|
+
* item(s) at the correct index.
|
|
128
|
+
*/
|
|
129
|
+
const prevSyncStateRef = useRef( null );
|
|
130
|
+
useEffect( () => {
|
|
131
|
+
const currentTabs = tabs.map( ( tab ) => ( {
|
|
132
|
+
clientId: tab.clientId,
|
|
133
|
+
anchor: tab.attributes.anchor ?? '',
|
|
134
|
+
} ) );
|
|
135
|
+
|
|
136
|
+
if ( prevSyncStateRef.current === null ) {
|
|
137
|
+
prevSyncStateRef.current = {
|
|
138
|
+
tabs: currentTabs,
|
|
139
|
+
menuItems: [ ...menuItems ],
|
|
140
|
+
};
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const { tabs: prevTabs, menuItems: prevMenuItems } =
|
|
145
|
+
prevSyncStateRef.current;
|
|
146
|
+
|
|
147
|
+
const tabsRemoved = currentTabs.length < prevTabs.length;
|
|
148
|
+
const menuItemsRemoved = menuItems.length < prevMenuItems.length;
|
|
149
|
+
|
|
150
|
+
// Update snapshot to the current state.
|
|
151
|
+
// Snapshot is updated eagerly; post-removal mutations keep it consistent
|
|
152
|
+
// so the next effect invocation sees a stable baseline.
|
|
153
|
+
prevSyncStateRef.current = {
|
|
154
|
+
tabs: currentTabs,
|
|
155
|
+
menuItems: [ ...menuItems ],
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// Lists are in sync, nothing changed, or toolbar already removed both.
|
|
159
|
+
if (
|
|
160
|
+
( ! tabsRemoved && ! menuItemsRemoved ) ||
|
|
161
|
+
( tabsRemoved && menuItemsRemoved )
|
|
162
|
+
) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const currentTabIds = new Set( currentTabs.map( ( t ) => t.clientId ) );
|
|
167
|
+
const currentMenuItemIds = new Set(
|
|
168
|
+
menuItems.map( ( m ) => m.clientId )
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if ( tabsRemoved ) {
|
|
172
|
+
prevTabs.forEach( ( prevTab ) => {
|
|
173
|
+
if ( currentTabIds.has( prevTab.clientId ) ) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const expectedMenuAnchor = prevTab.anchor
|
|
177
|
+
? `${ prevTab.anchor }-button`
|
|
178
|
+
: null;
|
|
179
|
+
const menuItemToRemove = expectedMenuAnchor
|
|
180
|
+
? menuItems.find( ( m ) => m.anchor === expectedMenuAnchor )
|
|
181
|
+
: null;
|
|
182
|
+
if ( menuItemToRemove ) {
|
|
183
|
+
removeBlock( menuItemToRemove.clientId, false );
|
|
184
|
+
prevSyncStateRef.current.menuItems =
|
|
185
|
+
prevSyncStateRef.current.menuItems.filter(
|
|
186
|
+
( m ) => m.clientId !== menuItemToRemove.clientId
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
} );
|
|
190
|
+
} else {
|
|
191
|
+
prevMenuItems.forEach( ( prevItem ) => {
|
|
192
|
+
if ( currentMenuItemIds.has( prevItem.clientId ) ) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const expectedTabAnchor =
|
|
196
|
+
prevItem.anchor?.replace( /-button$/, '' ) ?? '';
|
|
197
|
+
const tabToRemove = tabs.find(
|
|
198
|
+
( tab ) =>
|
|
199
|
+
( tab.attributes.anchor ?? '' ) === expectedTabAnchor
|
|
200
|
+
);
|
|
201
|
+
if ( tabToRemove ) {
|
|
202
|
+
removeBlock( tabToRemove.clientId, false );
|
|
203
|
+
prevSyncStateRef.current.tabs =
|
|
204
|
+
prevSyncStateRef.current.tabs.filter(
|
|
205
|
+
( t ) => t.clientId !== tabToRemove.clientId
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
} );
|
|
209
|
+
}
|
|
210
|
+
}, [ tabs, menuItems, removeBlock ] );
|
|
211
|
+
|
|
89
212
|
/**
|
|
90
213
|
* Memoize context value to prevent unnecessary re-renders.
|
|
91
214
|
*/
|
package/src/tabs/index.js
CHANGED
|
@@ -22,13 +22,23 @@ export const settings = {
|
|
|
22
22
|
innerBlocks: [
|
|
23
23
|
{
|
|
24
24
|
name: 'core/tabs-menu',
|
|
25
|
-
innerBlocks: [
|
|
25
|
+
innerBlocks: [
|
|
26
|
+
{
|
|
27
|
+
name: 'core/tabs-menu-item',
|
|
28
|
+
attributes: { anchor: 'tab-1-button' },
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'core/tabs-menu-item',
|
|
32
|
+
attributes: { anchor: 'tab-2-button' },
|
|
33
|
+
},
|
|
34
|
+
],
|
|
26
35
|
},
|
|
27
36
|
{
|
|
28
37
|
name: 'core/tab-panel',
|
|
29
|
-
innerBlocks: [ 1, 2
|
|
38
|
+
innerBlocks: [ 1, 2 ].map( ( index ) => ( {
|
|
30
39
|
name: 'core/tab',
|
|
31
40
|
attributes: {
|
|
41
|
+
anchor: `tab-${ index }`,
|
|
32
42
|
label: sprintf(
|
|
33
43
|
/** translators: %s: tab index number */
|
|
34
44
|
__( 'Tab %s' ),
|
package/src/tabs-menu/block.json
CHANGED
|
@@ -10,12 +10,7 @@
|
|
|
10
10
|
"textdomain": "default",
|
|
11
11
|
"parent": [ "core/tabs" ],
|
|
12
12
|
"allowedBlocks": [ "core/tabs-menu-item" ],
|
|
13
|
-
"usesContext": [
|
|
14
|
-
"core/tabs-list",
|
|
15
|
-
"core/tabs-id",
|
|
16
|
-
"core/tabs-activeTabIndex",
|
|
17
|
-
"core/tabs-editorActiveTabIndex"
|
|
18
|
-
],
|
|
13
|
+
"usesContext": [ "core/tabs-list" ],
|
|
19
14
|
"attributes": {},
|
|
20
15
|
"supports": {
|
|
21
16
|
"html": false,
|
package/src/tabs-menu/edit.js
CHANGED
|
@@ -6,23 +6,12 @@ import clsx from 'clsx';
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies
|
|
8
8
|
*/
|
|
9
|
-
import { __ } from '@wordpress/i18n';
|
|
10
9
|
import {
|
|
11
10
|
useBlockProps,
|
|
12
11
|
useInnerBlocksProps,
|
|
13
|
-
BlockContextProvider,
|
|
14
|
-
__experimentalUseBlockPreview as useBlockPreview,
|
|
15
12
|
store as blockEditorStore,
|
|
16
|
-
useBlockEditContext,
|
|
17
13
|
} from '@wordpress/block-editor';
|
|
18
|
-
import { useSelect
|
|
19
|
-
import {
|
|
20
|
-
memo,
|
|
21
|
-
useMemo,
|
|
22
|
-
useState,
|
|
23
|
-
useEffect,
|
|
24
|
-
useCallback,
|
|
25
|
-
} from '@wordpress/element';
|
|
14
|
+
import { useSelect } from '@wordpress/data';
|
|
26
15
|
|
|
27
16
|
/**
|
|
28
17
|
* Internal dependencies
|
|
@@ -30,221 +19,31 @@ import {
|
|
|
30
19
|
import AddTabToolbarControl from '../tab/add-tab-toolbar-control';
|
|
31
20
|
import RemoveTabToolbarControl from '../tab/remove-tab-toolbar-control';
|
|
32
21
|
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
*
|
|
40
|
-
* @param {Object} props Component props.
|
|
41
|
-
* @param {Array} props.blocks The blocks to preview.
|
|
42
|
-
* @param {string} props.blockContextId The context ID for this block.
|
|
43
|
-
* @param {boolean} props.isHidden Whether the preview is hidden.
|
|
44
|
-
* @param {Function} props.setActiveBlockContextId Callback to set the active context ID.
|
|
45
|
-
*/
|
|
46
|
-
function TabsMenuItemPreview( {
|
|
47
|
-
blocks,
|
|
48
|
-
blockContextId,
|
|
49
|
-
isHidden,
|
|
50
|
-
setActiveBlockContextId,
|
|
51
|
-
} ) {
|
|
52
|
-
const blockPreviewProps = useBlockPreview( { blocks } );
|
|
53
|
-
|
|
54
|
-
const handleOnClick = () => {
|
|
55
|
-
setActiveBlockContextId( blockContextId );
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const style = {
|
|
59
|
-
display: isHidden ? 'none' : 'flex',
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<div
|
|
64
|
-
{ ...blockPreviewProps }
|
|
65
|
-
tabIndex={ 0 }
|
|
66
|
-
role="button"
|
|
67
|
-
onClick={ handleOnClick }
|
|
68
|
-
onKeyDown={ handleOnClick }
|
|
69
|
-
style={ style }
|
|
70
|
-
/>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const MemoizedTabsMenuItemPreview = memo( TabsMenuItemPreview );
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* The actual editable inner blocks for the active tab item.
|
|
78
|
-
*
|
|
79
|
-
* @param {Object} props Component props.
|
|
80
|
-
* @param {Object} props.wrapperProps Props to pass to the wrapper element.
|
|
81
|
-
* @param {Object} props.layout The layout object to pass to inner blocks.
|
|
82
|
-
*/
|
|
83
|
-
function TabsMenuItemTemplateBlocks( { wrapperProps = {}, layout } ) {
|
|
84
|
-
const innerBlocksProps = useInnerBlocksProps( wrapperProps, {
|
|
85
|
-
template: TABS_MENU_ITEM_TEMPLATE,
|
|
86
|
-
templateLock: 'all',
|
|
87
|
-
renderAppender: false,
|
|
88
|
-
layout,
|
|
89
|
-
} );
|
|
90
|
-
return innerBlocksProps.children;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function Edit( {
|
|
94
|
-
context,
|
|
95
|
-
clientId,
|
|
96
|
-
__unstableLayoutClassNames: layoutClassNames,
|
|
97
|
-
} ) {
|
|
98
|
-
// Get the layout from block edit context to pass to inner blocks.
|
|
99
|
-
// This ensures the correct orientation is used from the start.
|
|
100
|
-
const { layout } = useBlockEditContext();
|
|
101
|
-
|
|
102
|
-
const tabsId = context[ 'core/tabs-id' ] || null;
|
|
103
|
-
const tabsList = context[ 'core/tabs-list' ] || EMPTY_ARRAY;
|
|
104
|
-
const activeTabIndex = context[ 'core/tabs-activeTabIndex' ] ?? 0;
|
|
105
|
-
const editorActiveTabIndex = context[ 'core/tabs-editorActiveTabIndex' ];
|
|
106
|
-
|
|
107
|
-
// Memoize effectiveActiveIndex to ensure it updates when context changes
|
|
108
|
-
const effectiveActiveIndex = useMemo( () => {
|
|
109
|
-
return editorActiveTabIndex ?? activeTabIndex;
|
|
110
|
-
}, [ editorActiveTabIndex, activeTabIndex ] );
|
|
111
|
-
|
|
112
|
-
const { __unstableMarkNextChangeAsNotPersistent } =
|
|
113
|
-
useDispatch( blockEditorStore );
|
|
114
|
-
const { updateBlockAttributes } = useDispatch( blockEditorStore );
|
|
115
|
-
|
|
116
|
-
// Track which tab context is "active" for editing (shows real inner blocks)
|
|
117
|
-
const [ activeBlockContextId, setActiveBlockContextId ] = useState( null );
|
|
118
|
-
|
|
119
|
-
// Get the inner blocks (the single tabs-menu-item template)
|
|
120
|
-
const { blocks, tabsClientId } = useSelect(
|
|
121
|
-
( select ) => {
|
|
122
|
-
const { getBlocks, getBlockRootClientId } =
|
|
123
|
-
select( blockEditorStore );
|
|
124
|
-
return {
|
|
125
|
-
blocks: getBlocks( clientId ),
|
|
126
|
-
tabsClientId: getBlockRootClientId( clientId ),
|
|
127
|
-
};
|
|
128
|
-
},
|
|
22
|
+
function Edit( { clientId, __unstableLayoutClassNames: layoutClassNames } ) {
|
|
23
|
+
const { tabsClientId } = useSelect(
|
|
24
|
+
( select ) => ( {
|
|
25
|
+
tabsClientId:
|
|
26
|
+
select( blockEditorStore ).getBlockRootClientId( clientId ),
|
|
27
|
+
} ),
|
|
129
28
|
[ clientId ]
|
|
130
29
|
);
|
|
131
30
|
|
|
132
|
-
// Build block contexts for each tab
|
|
133
|
-
const blockContexts = useMemo( () => {
|
|
134
|
-
return tabsList.map( ( tab, index ) => ( {
|
|
135
|
-
'core/tabs-menu-item-index': index,
|
|
136
|
-
'core/tabs-menu-item-id': tab.id || `tab-${ index }`,
|
|
137
|
-
'core/tabs-menu-item-label': tab.label || '',
|
|
138
|
-
'core/tabs-menu-item-clientId': tab.clientId,
|
|
139
|
-
// Pass through parent context
|
|
140
|
-
'core/tabs-id': tabsId,
|
|
141
|
-
'core/tabs-list': tabsList,
|
|
142
|
-
'core/tabs-activeTabIndex': activeTabIndex,
|
|
143
|
-
'core/tabs-editorActiveTabIndex': editorActiveTabIndex,
|
|
144
|
-
} ) );
|
|
145
|
-
}, [ tabsList, tabsId, activeTabIndex, editorActiveTabIndex ] );
|
|
146
|
-
|
|
147
|
-
// Generate a unique ID for each block context
|
|
148
|
-
const getContextId = useCallback( ( blockContext ) => {
|
|
149
|
-
return `tab-context-${ blockContext[ 'core/tabs-menu-item-index' ] }`;
|
|
150
|
-
}, [] );
|
|
151
|
-
|
|
152
|
-
// Set the first tab as active by default
|
|
153
|
-
useEffect( () => {
|
|
154
|
-
if ( blockContexts.length > 0 && activeBlockContextId === null ) {
|
|
155
|
-
setActiveBlockContextId( getContextId( blockContexts[ 0 ] ) );
|
|
156
|
-
}
|
|
157
|
-
}, [ blockContexts, activeBlockContextId, getContextId ] );
|
|
158
|
-
|
|
159
|
-
// Update active context when editorActiveTabIndex changes
|
|
160
|
-
useEffect( () => {
|
|
161
|
-
if (
|
|
162
|
-
blockContexts.length > 0 &&
|
|
163
|
-
effectiveActiveIndex < blockContexts.length
|
|
164
|
-
) {
|
|
165
|
-
const newContextId = getContextId(
|
|
166
|
-
blockContexts[ effectiveActiveIndex ]
|
|
167
|
-
);
|
|
168
|
-
setActiveBlockContextId( ( prevId ) =>
|
|
169
|
-
prevId !== newContextId ? newContextId : prevId
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
}, [ effectiveActiveIndex, blockContexts, getContextId ] );
|
|
173
|
-
|
|
174
|
-
// Handle tab click to update parent tabs block's editorActiveTabIndex
|
|
175
|
-
const handleTabContextClick = useCallback(
|
|
176
|
-
( index ) => {
|
|
177
|
-
if ( tabsClientId && index !== effectiveActiveIndex ) {
|
|
178
|
-
__unstableMarkNextChangeAsNotPersistent();
|
|
179
|
-
updateBlockAttributes( tabsClientId, {
|
|
180
|
-
editorActiveTabIndex: index,
|
|
181
|
-
} );
|
|
182
|
-
}
|
|
183
|
-
},
|
|
184
|
-
[
|
|
185
|
-
tabsClientId,
|
|
186
|
-
effectiveActiveIndex,
|
|
187
|
-
updateBlockAttributes,
|
|
188
|
-
__unstableMarkNextChangeAsNotPersistent,
|
|
189
|
-
]
|
|
190
|
-
);
|
|
191
|
-
|
|
192
31
|
const blockProps = useBlockProps( {
|
|
193
32
|
className: clsx( layoutClassNames ),
|
|
194
33
|
role: 'tablist',
|
|
195
34
|
} );
|
|
196
35
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
<RemoveTabToolbarControl tabsClientId={ tabsClientId } />
|
|
203
|
-
<div { ...blockProps }>
|
|
204
|
-
<span className="tabs__tab-label tabs__tab-label--placeholder">
|
|
205
|
-
{ __( 'Add tabs to display menu' ) }
|
|
206
|
-
</span>
|
|
207
|
-
</div>
|
|
208
|
-
</>
|
|
209
|
-
);
|
|
210
|
-
}
|
|
36
|
+
const innerBlocksProps = useInnerBlocksProps( blockProps, {
|
|
37
|
+
allowedBlocks: [ 'core/tabs-menu-item' ],
|
|
38
|
+
orientation: 'horizontal',
|
|
39
|
+
renderAppender: false,
|
|
40
|
+
} );
|
|
211
41
|
|
|
212
42
|
return (
|
|
213
43
|
<>
|
|
214
44
|
<AddTabToolbarControl tabsClientId={ tabsClientId } />
|
|
215
45
|
<RemoveTabToolbarControl tabsClientId={ tabsClientId } />
|
|
216
|
-
<div { ...
|
|
217
|
-
{ blockContexts.map( ( blockContext, index ) => {
|
|
218
|
-
const contextId = getContextId( blockContext );
|
|
219
|
-
const isVisible = contextId === activeBlockContextId;
|
|
220
|
-
|
|
221
|
-
return (
|
|
222
|
-
<BlockContextProvider
|
|
223
|
-
key={ contextId }
|
|
224
|
-
value={ blockContext }
|
|
225
|
-
>
|
|
226
|
-
{ isVisible ? (
|
|
227
|
-
<TabsMenuItemTemplateBlocks
|
|
228
|
-
wrapperProps={ {
|
|
229
|
-
onClick: () =>
|
|
230
|
-
handleTabContextClick( index ),
|
|
231
|
-
} }
|
|
232
|
-
layout={ layout }
|
|
233
|
-
/>
|
|
234
|
-
) : null }
|
|
235
|
-
<MemoizedTabsMenuItemPreview
|
|
236
|
-
blocks={ blocks }
|
|
237
|
-
blockContextId={ contextId }
|
|
238
|
-
setActiveBlockContextId={ ( id ) => {
|
|
239
|
-
setActiveBlockContextId( id );
|
|
240
|
-
handleTabContextClick( index );
|
|
241
|
-
} }
|
|
242
|
-
isHidden={ isVisible }
|
|
243
|
-
/>
|
|
244
|
-
</BlockContextProvider>
|
|
245
|
-
);
|
|
246
|
-
} ) }
|
|
247
|
-
</div>
|
|
46
|
+
<div { ...innerBlocksProps } />
|
|
248
47
|
</>
|
|
249
48
|
);
|
|
250
49
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
.wp-block-tabs-menu {
|
|
2
|
-
.
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
// Allow the inner block list to be displayed in the flex layout, so the tab buttons appear as direct flex children in the editor.
|
|
3
|
+
> .block-editor-block-list__layout {
|
|
4
|
+
display: contents;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.block-editor-block-list__block:has(> .wp-block-tabs-menu-item) {
|
|
8
|
+
display: contents;
|
|
5
9
|
}
|
|
6
10
|
}
|
package/src/tabs-menu/index.php
CHANGED
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
/**
|
|
9
9
|
* Render callback for core/tabs-menu.
|
|
10
10
|
*
|
|
11
|
+
* Re-renders each tabs-menu-item inner block with per-item context (index, id,
|
|
12
|
+
* label) injected from the tabs-list, so the tabs-menu-item render callback
|
|
13
|
+
* can add the correct IAPI directives for each button.
|
|
14
|
+
*
|
|
11
15
|
* @since 7.0.0
|
|
12
16
|
*
|
|
13
17
|
* @param array $attributes Block attributes.
|
|
14
|
-
* @param string $content Block content (
|
|
18
|
+
* @param string $content Block content (rendered inner blocks from save.js).
|
|
15
19
|
* @param \WP_Block $block WP_Block instance.
|
|
16
20
|
*
|
|
17
21
|
* @return string Updated HTML.
|
|
@@ -20,44 +24,55 @@ function block_core_tabs_menu_render_callback( array $attributes, string $conten
|
|
|
20
24
|
$tabs_list = $block->context['core/tabs-list'] ?? array();
|
|
21
25
|
|
|
22
26
|
if ( empty( $tabs_list ) ) {
|
|
23
|
-
return
|
|
27
|
+
return $content;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// Re-render each tabs-menu-item with per-item context (index, id, label).
|
|
31
|
+
// Match by anchor so the correct tab is found even when the two lists
|
|
32
|
+
// are in different orders.
|
|
33
|
+
$buttons_html = '';
|
|
34
|
+
|
|
35
|
+
foreach ( $block->parsed_block['innerBlocks'] ?? array() as $parsed_menu_item ) {
|
|
36
|
+
if ( 'core/tabs-menu-item' !== ( $parsed_menu_item['blockName'] ?? '' ) ) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Find the tab anchor from the menu item anchor (e.g. "tab-1-button" → "tab-1").
|
|
41
|
+
$menu_item_anchor = $parsed_menu_item['attrs']['anchor'] ?? '';
|
|
42
|
+
$tab_anchor = preg_replace( '/-button$/', '', $menu_item_anchor );
|
|
43
|
+
|
|
44
|
+
// Find the matching tab in $tabs_list by id.
|
|
45
|
+
$tab = null;
|
|
46
|
+
$tab_index = 0;
|
|
47
|
+
foreach ( $tabs_list as $index => $candidate ) {
|
|
48
|
+
if ( ( $candidate['id'] ?? '' ) === $tab_anchor ) {
|
|
49
|
+
$tab = $candidate;
|
|
50
|
+
$tab_index = $index;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
32
54
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
55
|
+
// Skip menu items with no matching tab.
|
|
56
|
+
if ( null === $tab ) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
$item_context = array_merge(
|
|
38
61
|
$block->context,
|
|
39
62
|
array(
|
|
40
|
-
'core/tabs-menu-item-index' => $
|
|
63
|
+
'core/tabs-menu-item-index' => $tab_index,
|
|
41
64
|
'core/tabs-menu-item-id' => $tab['id'] ?? '',
|
|
42
65
|
'core/tabs-menu-item-label' => $tab['label'] ?? '',
|
|
43
66
|
)
|
|
44
67
|
);
|
|
45
68
|
|
|
46
|
-
|
|
47
|
-
$
|
|
48
|
-
|
|
49
|
-
// Render the block
|
|
50
|
-
$tabs_markup .= $tab_block->render();
|
|
69
|
+
$menu_item_block = new WP_Block( $parsed_menu_item, $item_context );
|
|
70
|
+
$buttons_html .= $menu_item_block->render();
|
|
51
71
|
}
|
|
52
72
|
|
|
53
|
-
//
|
|
54
|
-
$
|
|
55
|
-
|
|
56
|
-
$tabs_markup,
|
|
57
|
-
$content
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
return $content;
|
|
73
|
+
// Rebuild the wrapper using get_block_wrapper_attributes().
|
|
74
|
+
$wrapper_attributes = get_block_wrapper_attributes( array( 'role' => 'tablist' ) );
|
|
75
|
+
return sprintf( '<div %s>%s</div>', $wrapper_attributes, $buttons_html );
|
|
61
76
|
}
|
|
62
77
|
|
|
63
78
|
/**
|
package/src/tabs-menu/save.js
CHANGED
|
@@ -4,21 +4,25 @@
|
|
|
4
4
|
"apiVersion": 3,
|
|
5
5
|
"name": "core/tabs-menu-item",
|
|
6
6
|
"title": "Tab Menu Item",
|
|
7
|
-
"description": "A single tab button in the tabs menu.
|
|
7
|
+
"description": "A single tab button in the tabs menu.",
|
|
8
8
|
"version": "1.0.0",
|
|
9
9
|
"category": "design",
|
|
10
10
|
"textdomain": "default",
|
|
11
11
|
"parent": [ "core/tabs-menu" ],
|
|
12
12
|
"usesContext": [
|
|
13
|
-
"core/tabs-menu-item-index",
|
|
14
|
-
"core/tabs-menu-item-id",
|
|
15
|
-
"core/tabs-menu-item-label",
|
|
16
|
-
"core/tabs-menu-item-clientId",
|
|
17
13
|
"core/tabs-list",
|
|
18
14
|
"core/tabs-activeTabIndex",
|
|
19
|
-
"core/tabs-editorActiveTabIndex"
|
|
15
|
+
"core/tabs-editorActiveTabIndex",
|
|
16
|
+
"core/tabs-menu-item-index",
|
|
17
|
+
"core/tabs-menu-item-id",
|
|
18
|
+
"core/tabs-menu-item-label"
|
|
20
19
|
],
|
|
21
|
-
"attributes": {
|
|
20
|
+
"attributes": {
|
|
21
|
+
"anchor": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"default": ""
|
|
24
|
+
}
|
|
25
|
+
},
|
|
22
26
|
"supports": {
|
|
23
27
|
"html": false,
|
|
24
28
|
"reusable": false,
|
|
@@ -31,7 +35,6 @@
|
|
|
31
35
|
"text": true
|
|
32
36
|
}
|
|
33
37
|
},
|
|
34
|
-
"shadow": true,
|
|
35
38
|
"typography": {
|
|
36
39
|
"fontSize": true,
|
|
37
40
|
"__experimentalFontFamily": true,
|
|
@@ -40,9 +43,6 @@
|
|
|
40
43
|
"fontSize": true
|
|
41
44
|
}
|
|
42
45
|
},
|
|
43
|
-
"layout": {
|
|
44
|
-
"allowEditing": false
|
|
45
|
-
},
|
|
46
46
|
"spacing": {
|
|
47
47
|
"padding": true,
|
|
48
48
|
"__experimentalDefaultControls": {
|
|
@@ -54,6 +54,9 @@
|
|
|
54
54
|
"color": true,
|
|
55
55
|
"width": true,
|
|
56
56
|
"style": true
|
|
57
|
+
},
|
|
58
|
+
"layout": {
|
|
59
|
+
"allowEditing": false
|
|
57
60
|
}
|
|
58
61
|
},
|
|
59
62
|
"editorScript": "file:./index.js",
|