@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
|
@@ -1,24 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
import clsx from 'clsx';
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
2
|
* WordPress dependencies
|
|
8
3
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
BlockControls,
|
|
12
|
-
store as blockEditorStore,
|
|
13
|
-
} from '@wordpress/block-editor';
|
|
14
|
-
import { ToolbarGroup, ToolbarItem, Button } from '@wordpress/components';
|
|
15
|
-
import {
|
|
16
|
-
chevronLeft,
|
|
17
|
-
chevronRight,
|
|
18
|
-
chevronUp,
|
|
19
|
-
chevronDown,
|
|
20
|
-
} from '@wordpress/icons';
|
|
21
|
-
import { useDispatch, useSelect } from '@wordpress/data';
|
|
4
|
+
import { BlockControls } from '@wordpress/block-editor';
|
|
22
5
|
|
|
23
6
|
/**
|
|
24
7
|
* Internal dependencies
|
|
@@ -26,157 +9,11 @@ import { useDispatch, useSelect } from '@wordpress/data';
|
|
|
26
9
|
import AddTabToolbarControl from '../tab/add-tab-toolbar-control';
|
|
27
10
|
import RemoveTabToolbarControl from '../tab/remove-tab-toolbar-control';
|
|
28
11
|
|
|
29
|
-
function
|
|
30
|
-
tabClientId,
|
|
31
|
-
tabIndex,
|
|
32
|
-
tabsCount,
|
|
33
|
-
tabsMenuClientId,
|
|
34
|
-
tabsClientId,
|
|
35
|
-
} ) {
|
|
36
|
-
const {
|
|
37
|
-
moveBlocksUp,
|
|
38
|
-
moveBlocksDown,
|
|
39
|
-
updateBlockAttributes,
|
|
40
|
-
__unstableMarkNextChangeAsNotPersistent,
|
|
41
|
-
} = useDispatch( blockEditorStore );
|
|
42
|
-
|
|
43
|
-
const { tabPanelClientId, orientation } = useSelect(
|
|
44
|
-
( select ) => {
|
|
45
|
-
const { getBlockRootClientId, getBlockAttributes } =
|
|
46
|
-
select( blockEditorStore );
|
|
47
|
-
// Get orientation directly from the tabs-menu block's layout attribute.
|
|
48
|
-
// This is more reliable than getBlockListSettings which is set asynchronously.
|
|
49
|
-
const tabsMenuAttributes = tabsMenuClientId
|
|
50
|
-
? getBlockAttributes( tabsMenuClientId )
|
|
51
|
-
: null;
|
|
52
|
-
return {
|
|
53
|
-
tabPanelClientId: getBlockRootClientId( tabClientId ),
|
|
54
|
-
orientation:
|
|
55
|
-
tabsMenuAttributes?.layout?.orientation || 'horizontal',
|
|
56
|
-
};
|
|
57
|
-
},
|
|
58
|
-
[ tabClientId, tabsMenuClientId ]
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
const isFirst = tabIndex === 0;
|
|
62
|
-
const isLast = tabIndex === tabsCount - 1;
|
|
63
|
-
const isHorizontal = orientation === 'horizontal';
|
|
64
|
-
|
|
65
|
-
// Icons and labels based on orientation (respects RTL for horizontal)
|
|
66
|
-
let upIcon, downIcon, upLabel, downLabel;
|
|
67
|
-
if ( isHorizontal ) {
|
|
68
|
-
if ( isRTL() ) {
|
|
69
|
-
upIcon = chevronRight;
|
|
70
|
-
downIcon = chevronLeft;
|
|
71
|
-
upLabel = __( 'Move tab right' );
|
|
72
|
-
downLabel = __( 'Move tab left' );
|
|
73
|
-
} else {
|
|
74
|
-
upIcon = chevronLeft;
|
|
75
|
-
downIcon = chevronRight;
|
|
76
|
-
upLabel = __( 'Move tab left' );
|
|
77
|
-
downLabel = __( 'Move tab right' );
|
|
78
|
-
}
|
|
79
|
-
} else {
|
|
80
|
-
upIcon = chevronUp;
|
|
81
|
-
downIcon = chevronDown;
|
|
82
|
-
upLabel = __( 'Move tab up' );
|
|
83
|
-
downLabel = __( 'Move tab down' );
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Handle moving tab and updating active index to follow the moved tab
|
|
87
|
-
const handleMoveUp = () => {
|
|
88
|
-
moveBlocksUp( [ tabClientId ], tabPanelClientId );
|
|
89
|
-
// Update editorActiveTabIndex to follow the moved tab
|
|
90
|
-
if ( tabsClientId ) {
|
|
91
|
-
__unstableMarkNextChangeAsNotPersistent();
|
|
92
|
-
updateBlockAttributes( tabsClientId, {
|
|
93
|
-
editorActiveTabIndex: tabIndex - 1,
|
|
94
|
-
} );
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const handleMoveDown = () => {
|
|
99
|
-
moveBlocksDown( [ tabClientId ], tabPanelClientId );
|
|
100
|
-
// Update editorActiveTabIndex to follow the moved tab
|
|
101
|
-
if ( tabsClientId ) {
|
|
102
|
-
__unstableMarkNextChangeAsNotPersistent();
|
|
103
|
-
updateBlockAttributes( tabsClientId, {
|
|
104
|
-
editorActiveTabIndex: tabIndex + 1,
|
|
105
|
-
} );
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
// Don't render if only one tab
|
|
110
|
-
if ( tabsCount <= 1 ) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return (
|
|
115
|
-
<BlockControls group="parent">
|
|
116
|
-
<ToolbarGroup
|
|
117
|
-
className={ clsx( 'block-editor-block-mover', {
|
|
118
|
-
'is-horizontal': isHorizontal,
|
|
119
|
-
} ) }
|
|
120
|
-
>
|
|
121
|
-
<div className="block-editor-block-mover__move-button-container">
|
|
122
|
-
<ToolbarItem>
|
|
123
|
-
{ ( itemProps ) => (
|
|
124
|
-
<Button
|
|
125
|
-
className={ clsx(
|
|
126
|
-
'block-editor-block-mover-button',
|
|
127
|
-
'is-up-button'
|
|
128
|
-
) }
|
|
129
|
-
icon={ upIcon }
|
|
130
|
-
label={ upLabel }
|
|
131
|
-
disabled={ isFirst }
|
|
132
|
-
accessibleWhenDisabled
|
|
133
|
-
onClick={ handleMoveUp }
|
|
134
|
-
__next40pxDefaultSize
|
|
135
|
-
{ ...itemProps }
|
|
136
|
-
/>
|
|
137
|
-
) }
|
|
138
|
-
</ToolbarItem>
|
|
139
|
-
<ToolbarItem>
|
|
140
|
-
{ ( itemProps ) => (
|
|
141
|
-
<Button
|
|
142
|
-
className={ clsx(
|
|
143
|
-
'block-editor-block-mover-button',
|
|
144
|
-
'is-down-button'
|
|
145
|
-
) }
|
|
146
|
-
icon={ downIcon }
|
|
147
|
-
label={ downLabel }
|
|
148
|
-
disabled={ isLast }
|
|
149
|
-
accessibleWhenDisabled
|
|
150
|
-
onClick={ handleMoveDown }
|
|
151
|
-
__next40pxDefaultSize
|
|
152
|
-
{ ...itemProps }
|
|
153
|
-
/>
|
|
154
|
-
) }
|
|
155
|
-
</ToolbarItem>
|
|
156
|
-
</div>
|
|
157
|
-
</ToolbarGroup>
|
|
158
|
-
</BlockControls>
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export default function Controls( {
|
|
163
|
-
tabsClientId,
|
|
164
|
-
tabClientId,
|
|
165
|
-
tabIndex,
|
|
166
|
-
tabsCount,
|
|
167
|
-
tabsMenuClientId,
|
|
168
|
-
} ) {
|
|
12
|
+
export default function Controls( { tabsClientId } ) {
|
|
169
13
|
return (
|
|
170
|
-
|
|
171
|
-
<TabBlockMover
|
|
172
|
-
tabClientId={ tabClientId }
|
|
173
|
-
tabIndex={ tabIndex }
|
|
174
|
-
tabsCount={ tabsCount }
|
|
175
|
-
tabsMenuClientId={ tabsMenuClientId }
|
|
176
|
-
tabsClientId={ tabsClientId }
|
|
177
|
-
/>
|
|
14
|
+
<BlockControls>
|
|
178
15
|
<AddTabToolbarControl tabsClientId={ tabsClientId } />
|
|
179
16
|
<RemoveTabToolbarControl tabsClientId={ tabsClientId } />
|
|
180
|
-
|
|
17
|
+
</BlockControls>
|
|
181
18
|
);
|
|
182
19
|
}
|
|
@@ -13,132 +13,127 @@ import {
|
|
|
13
13
|
RichText,
|
|
14
14
|
} from '@wordpress/block-editor';
|
|
15
15
|
import { useSelect, useDispatch } from '@wordpress/data';
|
|
16
|
-
import {
|
|
16
|
+
import { useMemo, useCallback } from '@wordpress/element';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Internal dependencies
|
|
20
20
|
*/
|
|
21
|
-
import slugFromLabel from '../tab/slug-from-label';
|
|
22
21
|
import Controls from './controls';
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
const EMPTY_ARRAY = [];
|
|
24
|
+
|
|
25
|
+
function Edit( {
|
|
26
|
+
attributes,
|
|
25
27
|
context,
|
|
26
28
|
clientId,
|
|
27
29
|
__unstableLayoutClassNames: layoutClassNames,
|
|
28
30
|
} ) {
|
|
29
|
-
|
|
30
|
-
const tabIndex = context[ 'core/tabs-menu-item-index' ] ?? 0;
|
|
31
|
-
const tabId = context[ 'core/tabs-menu-item-id' ] ?? '';
|
|
32
|
-
const tabLabel = context[ 'core/tabs-menu-item-label' ] ?? '';
|
|
33
|
-
const tabClientId = context[ 'core/tabs-menu-item-clientId' ] ?? '';
|
|
34
|
-
|
|
35
|
-
// Context from parent tabs block, memoized to prevent unnecessary re-renders.
|
|
36
|
-
const contextTabsList = context[ 'core/tabs-list' ];
|
|
37
|
-
const tabsList = useMemo(
|
|
38
|
-
() => contextTabsList || [],
|
|
39
|
-
[ contextTabsList ]
|
|
40
|
-
);
|
|
31
|
+
const tabsList = context[ 'core/tabs-list' ] || EMPTY_ARRAY;
|
|
41
32
|
const activeTabIndex = context[ 'core/tabs-activeTabIndex' ] ?? 0;
|
|
42
33
|
const editorActiveTabIndex = context[ 'core/tabs-editorActiveTabIndex' ];
|
|
43
34
|
|
|
44
|
-
// Memoize effectiveActiveIndex to ensure it updates when context changes
|
|
45
35
|
const effectiveActiveIndex = useMemo( () => {
|
|
46
36
|
return editorActiveTabIndex ?? activeTabIndex;
|
|
47
37
|
}, [ editorActiveTabIndex, activeTabIndex ] );
|
|
48
38
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const { __unstableMarkNextChangeAsNotPersistent } =
|
|
52
|
-
useDispatch( blockEditorStore );
|
|
53
|
-
|
|
54
|
-
// Get parent tabs clientId for updating editorActiveTabIndex
|
|
55
|
-
const { tabsClientId, tabsMenuClientId, selectedTabClientId } = useSelect(
|
|
39
|
+
const { menuItemIndex, tabsClientId, selectedTabClientId } = useSelect(
|
|
56
40
|
( select ) => {
|
|
57
41
|
const {
|
|
42
|
+
getBlockOrder,
|
|
58
43
|
getBlockRootClientId,
|
|
59
44
|
getSelectedBlockClientIds,
|
|
60
45
|
hasSelectedInnerBlock,
|
|
61
46
|
} = select( blockEditorStore );
|
|
62
|
-
|
|
47
|
+
|
|
63
48
|
const _tabsMenuClientId = getBlockRootClientId( clientId );
|
|
64
49
|
const _tabsClientId = _tabsMenuClientId
|
|
65
50
|
? getBlockRootClientId( _tabsMenuClientId )
|
|
66
51
|
: null;
|
|
67
52
|
|
|
68
|
-
const
|
|
53
|
+
const siblings = getBlockOrder( _tabsMenuClientId );
|
|
54
|
+
const _menuItemIndex = siblings.indexOf( clientId );
|
|
69
55
|
|
|
70
|
-
// Find
|
|
71
|
-
|
|
56
|
+
// Find which tab panel block is currently selected.
|
|
57
|
+
const selectedIds = getSelectedBlockClientIds();
|
|
58
|
+
let _selectedTabClientId = null;
|
|
72
59
|
for ( const tab of tabsList ) {
|
|
73
60
|
if (
|
|
74
61
|
selectedIds.includes( tab.clientId ) ||
|
|
75
62
|
hasSelectedInnerBlock( tab.clientId, true )
|
|
76
63
|
) {
|
|
77
|
-
|
|
64
|
+
_selectedTabClientId = tab.clientId;
|
|
78
65
|
break;
|
|
79
66
|
}
|
|
80
67
|
}
|
|
81
68
|
|
|
82
69
|
return {
|
|
70
|
+
menuItemIndex: _menuItemIndex,
|
|
83
71
|
tabsClientId: _tabsClientId,
|
|
84
|
-
|
|
85
|
-
selectedTabClientId: selectedTab,
|
|
72
|
+
selectedTabClientId: _selectedTabClientId,
|
|
86
73
|
};
|
|
87
74
|
},
|
|
88
75
|
[ clientId, tabsList ]
|
|
89
76
|
);
|
|
90
77
|
|
|
91
|
-
|
|
78
|
+
// Find the corresponding tab's anchor from this menu item's anchor
|
|
79
|
+
// attribute (e.g., "tab-1-button" → "tab-1"), then look it up in tabsList.
|
|
80
|
+
// Falls back to positional lookup when no anchor is set.
|
|
81
|
+
const tabAnchor = attributes.anchor?.replace( /-button$/, '' ) ?? '';
|
|
82
|
+
const tab =
|
|
83
|
+
( tabAnchor && tabsList.find( ( t ) => t.id === tabAnchor ) ) ||
|
|
84
|
+
tabsList[ menuItemIndex ] ||
|
|
85
|
+
{};
|
|
92
86
|
|
|
93
|
-
//
|
|
94
|
-
|
|
87
|
+
// tabListIndex is the tab's position in tabsList, used for active-state
|
|
88
|
+
// checks and click handling.
|
|
89
|
+
const tabListIndex = tab.index ?? menuItemIndex;
|
|
95
90
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
[ updateBlockAttributes, tabClientId, tabIndex ]
|
|
106
|
-
);
|
|
91
|
+
const tabId = tab.id || `tab-${ menuItemIndex }`;
|
|
92
|
+
const tabClientId = tab.clientId || '';
|
|
93
|
+
const label = tab.label || '';
|
|
94
|
+
|
|
95
|
+
const isActive = tabListIndex === effectiveActiveIndex;
|
|
96
|
+
const isSelected = tabClientId === selectedTabClientId;
|
|
97
|
+
|
|
98
|
+
const { __unstableMarkNextChangeAsNotPersistent, updateBlockAttributes } =
|
|
99
|
+
useDispatch( blockEditorStore );
|
|
107
100
|
|
|
108
|
-
// Update editor active tab index on parent tabs block when tab is clicked
|
|
109
101
|
const handleTabClick = useCallback(
|
|
110
102
|
( event ) => {
|
|
111
103
|
event.preventDefault();
|
|
112
|
-
|
|
113
|
-
// Update the parent tabs block's editorActiveTabIndex (ephemeral, not persisted)
|
|
114
|
-
if ( tabsClientId && tabIndex !== effectiveActiveIndex ) {
|
|
104
|
+
if ( tabsClientId && tabListIndex !== effectiveActiveIndex ) {
|
|
115
105
|
__unstableMarkNextChangeAsNotPersistent();
|
|
116
106
|
updateBlockAttributes( tabsClientId, {
|
|
117
|
-
editorActiveTabIndex:
|
|
107
|
+
editorActiveTabIndex: tabListIndex,
|
|
118
108
|
} );
|
|
119
109
|
}
|
|
120
110
|
},
|
|
121
111
|
[
|
|
122
112
|
tabsClientId,
|
|
123
|
-
|
|
113
|
+
tabListIndex,
|
|
124
114
|
effectiveActiveIndex,
|
|
125
115
|
updateBlockAttributes,
|
|
126
116
|
__unstableMarkNextChangeAsNotPersistent,
|
|
127
117
|
]
|
|
128
118
|
);
|
|
129
119
|
|
|
130
|
-
const
|
|
131
|
-
|
|
120
|
+
const handleLabelChange = useCallback(
|
|
121
|
+
( newLabel ) => {
|
|
122
|
+
if ( tabClientId ) {
|
|
123
|
+
updateBlockAttributes( tabClientId, { label: newLabel } );
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
[ tabClientId, updateBlockAttributes ]
|
|
127
|
+
);
|
|
132
128
|
|
|
133
|
-
// Use blockProps for core style engine support
|
|
134
129
|
const blockProps = useBlockProps( {
|
|
135
130
|
className: clsx( layoutClassNames, {
|
|
136
|
-
'is-active':
|
|
137
|
-
'is-selected':
|
|
131
|
+
'is-active': isActive,
|
|
132
|
+
'is-selected': isSelected,
|
|
138
133
|
} ),
|
|
139
|
-
'aria-controls':
|
|
140
|
-
'aria-selected':
|
|
141
|
-
id:
|
|
134
|
+
'aria-controls': tabId,
|
|
135
|
+
'aria-selected': isActive,
|
|
136
|
+
id: `${ tabId }--tab`,
|
|
142
137
|
role: 'tab',
|
|
143
138
|
tabIndex: -1,
|
|
144
139
|
onClick: handleTabClick,
|
|
@@ -146,26 +141,22 @@ export default function Edit( {
|
|
|
146
141
|
|
|
147
142
|
return (
|
|
148
143
|
<>
|
|
149
|
-
<Controls
|
|
150
|
-
|
|
151
|
-
tabClientId={ tabClientId }
|
|
152
|
-
tabIndex={ tabIndex }
|
|
153
|
-
tabsCount={ tabsList.length }
|
|
154
|
-
tabsMenuClientId={ tabsMenuClientId }
|
|
155
|
-
/>
|
|
156
|
-
<div { ...blockProps }>
|
|
144
|
+
<Controls tabsClientId={ tabsClientId } />
|
|
145
|
+
<button { ...blockProps } type="button">
|
|
157
146
|
<RichText
|
|
158
147
|
tagName="span"
|
|
159
148
|
withoutInteractiveFormatting
|
|
160
149
|
placeholder={ sprintf(
|
|
161
150
|
/* translators: %d is the tab index + 1 */
|
|
162
151
|
__( 'Tab title %d' ),
|
|
163
|
-
|
|
152
|
+
menuItemIndex + 1
|
|
164
153
|
) }
|
|
165
|
-
value={
|
|
154
|
+
value={ label }
|
|
166
155
|
onChange={ handleLabelChange }
|
|
167
156
|
/>
|
|
168
|
-
</
|
|
157
|
+
</button>
|
|
169
158
|
</>
|
|
170
159
|
);
|
|
171
160
|
}
|
|
161
|
+
|
|
162
|
+
export default Edit;
|
|
@@ -8,18 +8,19 @@
|
|
|
8
8
|
/**
|
|
9
9
|
* Render callback for core/tabs-menu-item.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* Injects the tab label and IAPI directives into the saved button HTML.
|
|
12
|
+
* Per-item context (index, id, label) is provided by the parent tabs-menu
|
|
13
|
+
* render callback before this is called.
|
|
12
14
|
*
|
|
13
15
|
* @since 7.0.0
|
|
14
16
|
*
|
|
15
17
|
* @param array $attributes Block attributes.
|
|
16
|
-
* @param string $content Block content.
|
|
18
|
+
* @param string $content Block content (styled button from save.js).
|
|
17
19
|
* @param \WP_Block $block WP_Block instance.
|
|
18
20
|
*
|
|
19
21
|
* @return string Updated HTML.
|
|
20
22
|
*/
|
|
21
23
|
function block_core_tabs_menu_item_render_callback( array $attributes, string $content, \WP_Block $block ): string {
|
|
22
|
-
// Get tab-specific context
|
|
23
24
|
$tab_index = $block->context['core/tabs-menu-item-index'] ?? 0;
|
|
24
25
|
$tab_id = $block->context['core/tabs-menu-item-id'] ?? '';
|
|
25
26
|
$tab_label = $block->context['core/tabs-menu-item-label'] ?? '';
|
|
@@ -28,42 +29,29 @@ function block_core_tabs_menu_item_render_callback( array $attributes, string $c
|
|
|
28
29
|
$tab_id = 'tab-' . $tab_index;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
//
|
|
32
|
+
// Add Interactivity API directives and tab-specific attributes to the button.
|
|
32
33
|
$tag_processor = new WP_HTML_Tag_Processor( $content );
|
|
33
34
|
|
|
34
35
|
if ( $tag_processor->next_tag() ) {
|
|
35
|
-
// Remove hidden attribute and template class (from save.js)
|
|
36
|
-
$tag_processor->remove_attribute( 'hidden' );
|
|
37
|
-
|
|
38
|
-
// Set tab-specific attributes
|
|
39
36
|
$tag_processor->set_attribute( 'id', 'tab__' . $tab_id );
|
|
40
37
|
$tag_processor->set_attribute( 'aria-controls', $tab_id );
|
|
41
|
-
|
|
42
|
-
// Add IAPI directives
|
|
43
38
|
$tag_processor->set_attribute( 'data-wp-on--click', 'actions.handleTabClick' );
|
|
44
39
|
$tag_processor->set_attribute( 'data-wp-on--keydown', 'actions.handleTabKeyDown' );
|
|
45
40
|
$tag_processor->set_attribute( 'data-wp-bind--aria-selected', 'state.isActiveTab' );
|
|
46
41
|
$tag_processor->set_attribute( 'data-wp-bind--tabindex', 'state.tabIndexAttribute' );
|
|
47
|
-
|
|
48
|
-
// Add context for this specific tab item
|
|
49
42
|
$tag_processor->set_attribute(
|
|
50
43
|
'data-wp-context',
|
|
51
44
|
wp_json_encode( array( 'tabIndex' => $tab_index ) )
|
|
52
45
|
);
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
'/(<button[^>]*>).*?(<\/button>)/s',
|
|
62
|
-
'$1' . '<span>' . wp_kses_post( $tab_label ) . '</span>' . '$2',
|
|
63
|
-
$output
|
|
48
|
+
// Inject the tab label into the button.
|
|
49
|
+
return preg_replace(
|
|
50
|
+
'/(<button\b[^>]*>).*?(<\/button>)/s',
|
|
51
|
+
'$1<span>' . wp_kses_post( $tab_label ) . '</span>$2',
|
|
52
|
+
$tag_processor->get_updated_html(),
|
|
53
|
+
1
|
|
64
54
|
);
|
|
65
|
-
|
|
66
|
-
return $output;
|
|
67
55
|
}
|
|
68
56
|
|
|
69
57
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import {
|
|
5
5
|
Button,
|
|
6
6
|
DropZone,
|
|
7
|
-
|
|
7
|
+
FlexBlock,
|
|
8
8
|
Spinner,
|
|
9
9
|
__experimentalItemGroup as ItemGroup,
|
|
10
10
|
__experimentalHStack as HStack,
|
|
@@ -14,36 +14,62 @@ import {
|
|
|
14
14
|
MediaReplaceFlow,
|
|
15
15
|
store as blockEditorStore,
|
|
16
16
|
} from '@wordpress/block-editor';
|
|
17
|
+
import { focus } from '@wordpress/dom';
|
|
18
|
+
import { useRef } from '@wordpress/element';
|
|
17
19
|
import { __ } from '@wordpress/i18n';
|
|
18
20
|
import { useSelect } from '@wordpress/data';
|
|
21
|
+
import { reset as resetIcon } from '@wordpress/icons';
|
|
22
|
+
import { getFilename } from '@wordpress/url';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Focuses the toggle button.
|
|
26
|
+
*
|
|
27
|
+
* @param {Object} containerRef - ref object containing current element
|
|
28
|
+
*/
|
|
29
|
+
const focusToggleButton = ( containerRef ) => {
|
|
30
|
+
// Use requestAnimationFrame to ensure DOM updates are complete.
|
|
31
|
+
window.requestAnimationFrame( () => {
|
|
32
|
+
const [ toggleButton ] = focus.tabbable.find( containerRef?.current );
|
|
33
|
+
if ( ! toggleButton ) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
toggleButton.focus();
|
|
38
|
+
} );
|
|
39
|
+
};
|
|
19
40
|
|
|
20
41
|
/**
|
|
21
42
|
* MediaControlPreview - Preview component showing media thumbnail and filename
|
|
22
43
|
*
|
|
23
44
|
* @param {Object} props
|
|
24
45
|
* @param {string} props.url Media URL for thumbnail
|
|
25
|
-
* @param {string} props.alt Alt text for image
|
|
26
46
|
* @param {string} props.filename Filename to display
|
|
27
47
|
* @param {Object} props.itemGroupProps Optional props to pass to ItemGroup
|
|
28
48
|
* @param {string} props.className Optional className for Truncate
|
|
49
|
+
* @param {string} props.label Optional label for accessibility
|
|
29
50
|
* @return {Element} Preview component
|
|
30
51
|
*/
|
|
31
52
|
export function MediaControlPreview( {
|
|
32
53
|
url,
|
|
33
|
-
alt,
|
|
34
54
|
filename,
|
|
35
55
|
itemGroupProps,
|
|
36
56
|
className,
|
|
57
|
+
label,
|
|
37
58
|
} ) {
|
|
38
59
|
return (
|
|
39
60
|
<ItemGroup { ...itemGroupProps } as="span">
|
|
40
|
-
<HStack justify="flex-start"
|
|
41
|
-
<
|
|
42
|
-
|
|
61
|
+
<HStack justify="flex-start">
|
|
62
|
+
<span
|
|
63
|
+
className="block-library-utils__media-control__inspector-image-indicator"
|
|
64
|
+
style={ {
|
|
65
|
+
backgroundImage: url ? `url(${ url })` : undefined,
|
|
66
|
+
} }
|
|
67
|
+
/>
|
|
68
|
+
<FlexBlock>
|
|
43
69
|
<Truncate numberOfLines={ 1 } className={ className }>
|
|
44
|
-
{ filename }
|
|
70
|
+
{ filename ?? label }
|
|
45
71
|
</Truncate>
|
|
46
|
-
</
|
|
72
|
+
</FlexBlock>
|
|
47
73
|
</HStack>
|
|
48
74
|
</ItemGroup>
|
|
49
75
|
);
|
|
@@ -55,7 +81,6 @@ export function MediaControlPreview( {
|
|
|
55
81
|
* @param {Object} props
|
|
56
82
|
* @param {number} props.mediaId Media attachment ID
|
|
57
83
|
* @param {string} props.mediaUrl Media URL
|
|
58
|
-
* @param {string} props.alt Alt text for preview
|
|
59
84
|
* @param {string} props.filename Filename to display
|
|
60
85
|
* @param {Array} props.allowedTypes Allowed media types
|
|
61
86
|
* @param {Function} props.onSelect Callback when media selected
|
|
@@ -69,7 +94,6 @@ export function MediaControlPreview( {
|
|
|
69
94
|
export function MediaControl( {
|
|
70
95
|
mediaId,
|
|
71
96
|
mediaUrl,
|
|
72
|
-
alt = '',
|
|
73
97
|
filename,
|
|
74
98
|
allowedTypes,
|
|
75
99
|
onSelect,
|
|
@@ -77,7 +101,7 @@ export function MediaControl( {
|
|
|
77
101
|
onError,
|
|
78
102
|
onReset,
|
|
79
103
|
isUploading = false,
|
|
80
|
-
emptyLabel = __( '
|
|
104
|
+
emptyLabel = __( 'Media' ),
|
|
81
105
|
} ) {
|
|
82
106
|
const { getSettings } = useSelect( blockEditorStore );
|
|
83
107
|
const onFilesDrop = ( filesList ) => {
|
|
@@ -95,10 +119,15 @@ export function MediaControl( {
|
|
|
95
119
|
multiple: false,
|
|
96
120
|
} );
|
|
97
121
|
};
|
|
122
|
+
const containerRef = useRef();
|
|
98
123
|
|
|
99
124
|
return (
|
|
100
|
-
<div
|
|
125
|
+
<div
|
|
126
|
+
ref={ containerRef }
|
|
127
|
+
className="block-library-utils__media-control"
|
|
128
|
+
>
|
|
101
129
|
<MediaReplaceFlow
|
|
130
|
+
className="block-library-utils__media-control__replace-flow"
|
|
102
131
|
mediaId={ mediaId }
|
|
103
132
|
mediaURL={ mediaUrl }
|
|
104
133
|
allowedTypes={ allowedTypes }
|
|
@@ -106,15 +135,14 @@ export function MediaControl( {
|
|
|
106
135
|
onSelectURL={ onSelectURL }
|
|
107
136
|
onError={ onError }
|
|
108
137
|
name={
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
)
|
|
138
|
+
<MediaControlPreview
|
|
139
|
+
url={ mediaUrl }
|
|
140
|
+
filename={ filename }
|
|
141
|
+
className="block-library-utils__media-control__inspector-media-replace-title"
|
|
142
|
+
label={
|
|
143
|
+
mediaUrl ? getFilename( filename ) : emptyLabel
|
|
144
|
+
}
|
|
145
|
+
/>
|
|
118
146
|
}
|
|
119
147
|
renderToggle={ ( props ) => (
|
|
120
148
|
<Button { ...props } __next40pxDefaultSize>
|
|
@@ -123,6 +151,18 @@ export function MediaControl( {
|
|
|
123
151
|
) }
|
|
124
152
|
onReset={ onReset }
|
|
125
153
|
/>
|
|
154
|
+
{ mediaUrl && onReset && (
|
|
155
|
+
<Button
|
|
156
|
+
label={ __( 'Reset' ) }
|
|
157
|
+
className="block-library-utils__media-control__reset"
|
|
158
|
+
size="small"
|
|
159
|
+
icon={ resetIcon }
|
|
160
|
+
onClick={ () => {
|
|
161
|
+
onReset();
|
|
162
|
+
focusToggleButton( containerRef );
|
|
163
|
+
} }
|
|
164
|
+
/>
|
|
165
|
+
) }
|
|
126
166
|
<DropZone onFilesDrop={ onFilesDrop } />
|
|
127
167
|
</div>
|
|
128
168
|
);
|