@wordpress/block-library 9.29.1-next.f34ab90e9.0 → 9.30.1-next.6870dfe5b.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/accordion/edit.js +18 -1
- package/build/accordion/edit.js.map +1 -1
- package/build/accordion/index.js +0 -3
- package/build/accordion/index.js.map +1 -1
- package/build/accordion-content/edit.js +8 -8
- package/build/accordion-content/edit.js.map +1 -1
- package/build/accordion-content/index.js +2 -5
- package/build/accordion-content/index.js.map +1 -1
- package/build/accordion-header/index.js +0 -4
- package/build/accordion-header/index.js.map +1 -1
- package/build/accordion-panel/edit.js +5 -30
- package/build/accordion-panel/edit.js.map +1 -1
- package/build/accordion-panel/index.js +2 -5
- package/build/accordion-panel/index.js.map +1 -1
- package/build/accordion-panel/save.js +3 -29
- package/build/accordion-panel/save.js.map +1 -1
- package/build/audio/edit.js +3 -1
- package/build/audio/edit.js.map +1 -1
- package/build/gallery/index.js +2 -1
- package/build/gallery/index.js.map +1 -1
- package/build/group/variations.js +0 -12
- package/build/group/variations.js.map +1 -1
- package/build/image/image.js +1 -1
- package/build/image/image.js.map +1 -1
- package/build/navigation/constants.js +5 -1
- package/build/navigation/constants.js.map +1 -1
- package/build/navigation/edit/index.js +45 -1
- package/build/navigation/edit/index.js.map +1 -1
- package/build/navigation/edit/leaf-more-menu.js +0 -1
- package/build/navigation/edit/leaf-more-menu.js.map +1 -1
- package/build/navigation/edit/menu-inspector-controls.js +40 -5
- package/build/navigation/edit/menu-inspector-controls.js.map +1 -1
- package/build/navigation-link/block-inserter.js +69 -0
- package/build/navigation-link/block-inserter.js.map +1 -0
- package/build/navigation-link/dialog-wrapper.js +80 -0
- package/build/navigation-link/dialog-wrapper.js.map +1 -0
- package/build/navigation-link/link-ui.js +80 -116
- package/build/navigation-link/link-ui.js.map +1 -1
- package/build/navigation-link/page-creator.js +137 -0
- package/build/navigation-link/page-creator.js.map +1 -0
- package/build/query/edit/index.js.map +1 -1
- package/build/query/edit/query-content.js +7 -6
- package/build/query/edit/query-content.js.map +1 -1
- package/build/query/edit/query-placeholder.js +30 -9
- package/build/query/edit/query-placeholder.js.map +1 -1
- package/build/query/edit/query-toolbar.js +4 -2
- package/build/query/edit/query-toolbar.js.map +1 -1
- package/build/search/edit.js +22 -14
- package/build/search/edit.js.map +1 -1
- package/build/template-part/edit/placeholder.js +2 -1
- package/build/template-part/edit/placeholder.js.map +1 -1
- package/build/video/edit.js +3 -1
- package/build/video/edit.js.map +1 -1
- package/build-module/accordion/edit.js +20 -3
- package/build-module/accordion/edit.js.map +1 -1
- package/build-module/accordion/index.js +0 -3
- package/build-module/accordion/index.js.map +1 -1
- package/build-module/accordion-content/edit.js +8 -8
- package/build-module/accordion-content/edit.js.map +1 -1
- package/build-module/accordion-content/index.js +2 -5
- package/build-module/accordion-content/index.js.map +1 -1
- package/build-module/accordion-header/index.js +0 -4
- package/build-module/accordion-header/index.js.map +1 -1
- package/build-module/accordion-panel/edit.js +6 -29
- package/build-module/accordion-panel/edit.js.map +1 -1
- package/build-module/accordion-panel/index.js +2 -5
- package/build-module/accordion-panel/index.js.map +1 -1
- package/build-module/accordion-panel/save.js +4 -28
- package/build-module/accordion-panel/save.js.map +1 -1
- package/build-module/audio/edit.js +4 -2
- package/build-module/audio/edit.js.map +1 -1
- package/build-module/gallery/index.js +2 -1
- package/build-module/gallery/index.js.map +1 -1
- package/build-module/group/variations.js +0 -12
- package/build-module/group/variations.js.map +1 -1
- package/build-module/image/image.js +1 -1
- package/build-module/image/image.js.map +1 -1
- package/build-module/navigation/constants.js +5 -1
- package/build-module/navigation/constants.js.map +1 -1
- package/build-module/navigation/edit/index.js +50 -4
- package/build-module/navigation/edit/index.js.map +1 -1
- package/build-module/navigation/edit/leaf-more-menu.js +0 -1
- package/build-module/navigation/edit/leaf-more-menu.js.map +1 -1
- package/build-module/navigation/edit/menu-inspector-controls.js +40 -5
- package/build-module/navigation/edit/menu-inspector-controls.js.map +1 -1
- package/build-module/navigation-link/block-inserter.js +61 -0
- package/build-module/navigation-link/block-inserter.js.map +1 -0
- package/build-module/navigation-link/dialog-wrapper.js +75 -0
- package/build-module/navigation-link/dialog-wrapper.js.map +1 -0
- package/build-module/navigation-link/link-ui.js +85 -121
- package/build-module/navigation-link/link-ui.js.map +1 -1
- package/build-module/navigation-link/page-creator.js +130 -0
- package/build-module/navigation-link/page-creator.js.map +1 -0
- package/build-module/query/edit/index.js.map +1 -1
- package/build-module/query/edit/query-content.js +8 -7
- package/build-module/query/edit/query-content.js.map +1 -1
- package/build-module/query/edit/query-placeholder.js +30 -10
- package/build-module/query/edit/query-placeholder.js.map +1 -1
- package/build-module/query/edit/query-toolbar.js +4 -2
- package/build-module/query/edit/query-toolbar.js.map +1 -1
- package/build-module/search/edit.js +22 -14
- package/build-module/search/edit.js.map +1 -1
- package/build-module/template-part/edit/placeholder.js +2 -1
- package/build-module/template-part/edit/placeholder.js.map +1 -1
- package/build-module/video/edit.js +4 -2
- package/build-module/video/edit.js.map +1 -1
- package/build-style/accordion/style-rtl.css +8 -18
- package/build-style/accordion/style.css +8 -18
- package/build-style/editor-rtl.css +18 -0
- package/build-style/editor.css +18 -0
- package/build-style/form-input/style-rtl.css +4 -3
- package/build-style/form-input/style.css +4 -3
- package/build-style/navigation-link/editor-rtl.css +14 -0
- package/build-style/navigation-link/editor.css +14 -0
- package/build-style/navigation-link/style-rtl.css +1 -1
- package/build-style/navigation-link/style.css +1 -1
- package/build-style/post-comments-form/style-rtl.css +8 -5
- package/build-style/post-comments-form/style.css +8 -5
- package/build-style/query/editor-rtl.css +4 -0
- package/build-style/query/editor.css +4 -0
- package/build-style/search/style-rtl.css +11 -12
- package/build-style/search/style.css +11 -12
- package/build-style/style-rtl.css +32 -40
- package/build-style/style.css +32 -40
- package/package.json +35 -35
- package/src/accordion/block.json +0 -3
- package/src/accordion/edit.js +20 -0
- package/src/accordion/style.scss +12 -21
- package/src/accordion-content/block.json +2 -4
- package/src/accordion-content/edit.js +21 -27
- package/src/accordion-content/index.js +0 -1
- package/src/accordion-header/block.json +0 -3
- package/src/accordion-header/index.js +0 -1
- package/src/accordion-panel/block.json +2 -4
- package/src/accordion-panel/edit.js +11 -51
- package/src/accordion-panel/index.js +0 -1
- package/src/accordion-panel/save.js +4 -45
- package/src/audio/edit.js +6 -1
- package/src/cover/test/edit.js +1 -5
- package/src/form-input/style.scss +3 -2
- package/src/gallery/block.json +2 -1
- package/src/gallery/index.php +1 -1
- package/src/gallery/test/helpers.native.js +3 -3
- package/src/group/variations.js +0 -12
- package/src/image/image.js +2 -1
- package/src/navigation/constants.js +4 -0
- package/src/navigation/edit/index.js +50 -1
- package/src/navigation/edit/leaf-more-menu.js +0 -1
- package/src/navigation/edit/menu-inspector-controls.js +40 -5
- package/src/navigation-link/block-inserter.js +65 -0
- package/src/navigation-link/dialog-wrapper.js +74 -0
- package/src/navigation-link/editor.scss +17 -0
- package/src/navigation-link/link-ui.js +108 -158
- package/src/navigation-link/page-creator.js +157 -0
- package/src/navigation-link/style.scss +1 -1
- package/src/post-comments-form/style.scss +11 -11
- package/src/post-date/index.php +2 -1
- package/src/query/edit/index.js +1 -0
- package/src/query/edit/query-content.js +8 -4
- package/src/query/edit/query-placeholder.js +47 -17
- package/src/query/edit/query-toolbar.js +10 -2
- package/src/query/editor.scss +6 -1
- package/src/search/edit.js +44 -13
- package/src/search/index.php +16 -2
- package/src/search/style.scss +15 -16
- package/src/template-part/edit/placeholder.js +2 -1
- package/src/video/edit.js +6 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
import { useSelect } from '@wordpress/data';
|
|
6
|
+
import {
|
|
7
|
+
store as blockEditorStore,
|
|
8
|
+
privateApis as blockEditorPrivateApis,
|
|
9
|
+
} from '@wordpress/block-editor';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import DialogWrapper from './dialog-wrapper';
|
|
15
|
+
import { unlock } from '../lock-unlock';
|
|
16
|
+
|
|
17
|
+
const { PrivateQuickInserter: QuickInserter } = unlock(
|
|
18
|
+
blockEditorPrivateApis
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Component for inserting blocks within the Navigation Link UI.
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} props Component props.
|
|
25
|
+
* @param {string} props.clientId Client ID of the navigation link block.
|
|
26
|
+
* @param {Function} props.onBack Callback when user wants to go back.
|
|
27
|
+
* @param {Function} props.onBlockInsert Callback when a block is inserted.
|
|
28
|
+
*/
|
|
29
|
+
function LinkUIBlockInserter( { clientId, onBack, onBlockInsert } ) {
|
|
30
|
+
const { rootBlockClientId } = useSelect(
|
|
31
|
+
( select ) => {
|
|
32
|
+
const { getBlockRootClientId } = select( blockEditorStore );
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
rootBlockClientId: getBlockRootClientId( clientId ),
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
[ clientId ]
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if ( ! clientId ) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<DialogWrapper
|
|
47
|
+
className="link-ui-block-inserter"
|
|
48
|
+
title={ __( 'Add block' ) }
|
|
49
|
+
description={ __( 'Choose a block to add to your Navigation.' ) }
|
|
50
|
+
onBack={ onBack }
|
|
51
|
+
>
|
|
52
|
+
<QuickInserter
|
|
53
|
+
rootClientId={ rootBlockClientId }
|
|
54
|
+
clientId={ clientId }
|
|
55
|
+
isAppender={ false }
|
|
56
|
+
prioritizePatterns={ false }
|
|
57
|
+
selectBlockOnInsert={ ! onBlockInsert }
|
|
58
|
+
onSelect={ onBlockInsert ? onBlockInsert : undefined }
|
|
59
|
+
hasSearch={ false }
|
|
60
|
+
/>
|
|
61
|
+
</DialogWrapper>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default LinkUIBlockInserter;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { Button, VisuallyHidden } from '@wordpress/components';
|
|
5
|
+
import { __, isRTL } from '@wordpress/i18n';
|
|
6
|
+
import { chevronLeftSmall, chevronRightSmall } from '@wordpress/icons';
|
|
7
|
+
import { useInstanceId, useFocusOnMount } from '@wordpress/compose';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Shared BackButton component for consistent navigation across LinkUI sub-components.
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} props Component props.
|
|
13
|
+
* @param {string} props.className CSS class name for the button.
|
|
14
|
+
* @param {Function} props.onBack Callback when user wants to go back.
|
|
15
|
+
*/
|
|
16
|
+
function BackButton( { className, onBack } ) {
|
|
17
|
+
return (
|
|
18
|
+
<Button
|
|
19
|
+
className={ className }
|
|
20
|
+
icon={ isRTL() ? chevronRightSmall : chevronLeftSmall }
|
|
21
|
+
onClick={ ( e ) => {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
onBack();
|
|
24
|
+
} }
|
|
25
|
+
size="small"
|
|
26
|
+
>
|
|
27
|
+
{ __( 'Back' ) }
|
|
28
|
+
</Button>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Shared DialogWrapper component for consistent dialog structure across LinkUI sub-components.
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} props Component props.
|
|
36
|
+
* @param {string} props.className CSS class name for the dialog container.
|
|
37
|
+
* @param {string} props.title Dialog title for accessibility.
|
|
38
|
+
* @param {string} props.description Dialog description for accessibility.
|
|
39
|
+
* @param {Function} props.onBack Callback when user wants to go back.
|
|
40
|
+
* @param {Object} props.children Child components to render inside the dialog.
|
|
41
|
+
*/
|
|
42
|
+
function DialogWrapper( { className, title, description, onBack, children } ) {
|
|
43
|
+
const dialogTitleId = useInstanceId(
|
|
44
|
+
DialogWrapper,
|
|
45
|
+
'link-ui-dialog-title'
|
|
46
|
+
);
|
|
47
|
+
const dialogDescriptionId = useInstanceId(
|
|
48
|
+
DialogWrapper,
|
|
49
|
+
'link-ui-dialog-description'
|
|
50
|
+
);
|
|
51
|
+
const focusOnMountRef = useFocusOnMount( 'firstElement' );
|
|
52
|
+
const backButtonClassName = `${ className }__back`;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
className={ className }
|
|
57
|
+
role="dialog"
|
|
58
|
+
aria-labelledby={ dialogTitleId }
|
|
59
|
+
aria-describedby={ dialogDescriptionId }
|
|
60
|
+
ref={ focusOnMountRef }
|
|
61
|
+
>
|
|
62
|
+
<VisuallyHidden>
|
|
63
|
+
<h2 id={ dialogTitleId }>{ title }</h2>
|
|
64
|
+
<p id={ dialogDescriptionId }>{ description }</p>
|
|
65
|
+
</VisuallyHidden>
|
|
66
|
+
|
|
67
|
+
<BackButton className={ backButtonClassName } onBack={ onBack } />
|
|
68
|
+
|
|
69
|
+
{ children }
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default DialogWrapper;
|
|
@@ -122,3 +122,20 @@
|
|
|
122
122
|
gap: $grid-unit-10;
|
|
123
123
|
height: auto;
|
|
124
124
|
}
|
|
125
|
+
|
|
126
|
+
.link-ui-page-creator {
|
|
127
|
+
// Match LinkControl width constraints for consistent UI sizing
|
|
128
|
+
max-width: 350px;
|
|
129
|
+
min-width: auto;
|
|
130
|
+
width: 90vw;
|
|
131
|
+
padding-top: $grid-unit-10;
|
|
132
|
+
|
|
133
|
+
&__inner {
|
|
134
|
+
padding: $grid-unit-20;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
&__back {
|
|
138
|
+
margin-left: $grid-unit-10;
|
|
139
|
+
text-transform: uppercase;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -8,37 +8,24 @@ import {
|
|
|
8
8
|
VisuallyHidden,
|
|
9
9
|
__experimentalVStack as VStack,
|
|
10
10
|
} from '@wordpress/components';
|
|
11
|
-
import { __
|
|
11
|
+
import { __ } from '@wordpress/i18n';
|
|
12
|
+
import { LinkControl, useBlockEditingMode } from '@wordpress/block-editor';
|
|
12
13
|
import {
|
|
13
|
-
LinkControl,
|
|
14
|
-
store as blockEditorStore,
|
|
15
|
-
privateApis as blockEditorPrivateApis,
|
|
16
|
-
} from '@wordpress/block-editor';
|
|
17
|
-
import {
|
|
18
|
-
createInterpolateElement,
|
|
19
14
|
useMemo,
|
|
20
15
|
useState,
|
|
21
16
|
useRef,
|
|
22
17
|
useEffect,
|
|
23
18
|
forwardRef,
|
|
24
19
|
} from '@wordpress/element';
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
} from '@wordpress/core-data';
|
|
29
|
-
import { decodeEntities } from '@wordpress/html-entities';
|
|
30
|
-
import { useSelect, useDispatch } from '@wordpress/data';
|
|
31
|
-
import { chevronLeftSmall, chevronRightSmall, plus } from '@wordpress/icons';
|
|
32
|
-
import { useInstanceId, useFocusOnMount } from '@wordpress/compose';
|
|
20
|
+
import { useResourcePermissions } from '@wordpress/core-data';
|
|
21
|
+
import { plus } from '@wordpress/icons';
|
|
22
|
+
import { useInstanceId } from '@wordpress/compose';
|
|
33
23
|
|
|
34
24
|
/**
|
|
35
25
|
* Internal dependencies
|
|
36
26
|
*/
|
|
37
|
-
import {
|
|
38
|
-
|
|
39
|
-
const { PrivateQuickInserter: QuickInserter } = unlock(
|
|
40
|
-
blockEditorPrivateApis
|
|
41
|
-
);
|
|
27
|
+
import { LinkUIPageCreator } from './page-creator';
|
|
28
|
+
import LinkUIBlockInserter from './block-inserter';
|
|
42
29
|
|
|
43
30
|
/**
|
|
44
31
|
* Given the Link block's type attribute, return the query params to give to
|
|
@@ -78,110 +65,19 @@ export function getSuggestionsQuery( type, kind ) {
|
|
|
78
65
|
}
|
|
79
66
|
}
|
|
80
67
|
|
|
81
|
-
function LinkUIBlockInserter( { clientId, onBack } ) {
|
|
82
|
-
const { rootBlockClientId } = useSelect(
|
|
83
|
-
( select ) => {
|
|
84
|
-
const { getBlockRootClientId } = select( blockEditorStore );
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
rootBlockClientId: getBlockRootClientId( clientId ),
|
|
88
|
-
};
|
|
89
|
-
},
|
|
90
|
-
[ clientId ]
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const focusOnMountRef = useFocusOnMount( 'firstElement' );
|
|
94
|
-
|
|
95
|
-
const dialogTitleId = useInstanceId(
|
|
96
|
-
LinkControl,
|
|
97
|
-
`link-ui-block-inserter__title`
|
|
98
|
-
);
|
|
99
|
-
const dialogDescriptionId = useInstanceId(
|
|
100
|
-
LinkControl,
|
|
101
|
-
`link-ui-block-inserter__description`
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
if ( ! clientId ) {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<div
|
|
110
|
-
className="link-ui-block-inserter"
|
|
111
|
-
role="dialog"
|
|
112
|
-
aria-labelledby={ dialogTitleId }
|
|
113
|
-
aria-describedby={ dialogDescriptionId }
|
|
114
|
-
ref={ focusOnMountRef }
|
|
115
|
-
>
|
|
116
|
-
<VisuallyHidden>
|
|
117
|
-
<h2 id={ dialogTitleId }>{ __( 'Add block' ) }</h2>
|
|
118
|
-
|
|
119
|
-
<p id={ dialogDescriptionId }>
|
|
120
|
-
{ __( 'Choose a block to add to your Navigation.' ) }
|
|
121
|
-
</p>
|
|
122
|
-
</VisuallyHidden>
|
|
123
|
-
|
|
124
|
-
<Button
|
|
125
|
-
className="link-ui-block-inserter__back"
|
|
126
|
-
icon={ isRTL() ? chevronRightSmall : chevronLeftSmall }
|
|
127
|
-
onClick={ ( e ) => {
|
|
128
|
-
e.preventDefault();
|
|
129
|
-
onBack();
|
|
130
|
-
} }
|
|
131
|
-
size="small"
|
|
132
|
-
>
|
|
133
|
-
{ __( 'Back' ) }
|
|
134
|
-
</Button>
|
|
135
|
-
|
|
136
|
-
<QuickInserter
|
|
137
|
-
rootClientId={ rootBlockClientId }
|
|
138
|
-
clientId={ clientId }
|
|
139
|
-
isAppender={ false }
|
|
140
|
-
prioritizePatterns={ false }
|
|
141
|
-
selectBlockOnInsert
|
|
142
|
-
hasSearch={ false }
|
|
143
|
-
/>
|
|
144
|
-
</div>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
68
|
function UnforwardedLinkUI( props, ref ) {
|
|
149
69
|
const { label, url, opensInNewTab, type, kind } = props.link;
|
|
150
70
|
const postType = type || 'page';
|
|
151
71
|
|
|
152
72
|
const [ addingBlock, setAddingBlock ] = useState( false );
|
|
73
|
+
const [ addingPage, setAddingPage ] = useState( false );
|
|
153
74
|
const [ focusAddBlockButton, setFocusAddBlockButton ] = useState( false );
|
|
154
|
-
const
|
|
75
|
+
const [ focusAddPageButton, setFocusAddPageButton ] = useState( false );
|
|
155
76
|
const permissions = useResourcePermissions( {
|
|
156
77
|
kind: 'postType',
|
|
157
78
|
name: postType,
|
|
158
79
|
} );
|
|
159
80
|
|
|
160
|
-
async function handleCreate( pageTitle ) {
|
|
161
|
-
const page = await saveEntityRecord( 'postType', postType, {
|
|
162
|
-
title: pageTitle,
|
|
163
|
-
status: 'draft',
|
|
164
|
-
} );
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
id: page.id,
|
|
168
|
-
type: postType,
|
|
169
|
-
// Make `title` property consistent with that in `fetchLinkSuggestions` where the `rendered` title (containing HTML entities)
|
|
170
|
-
// is also being decoded. By being consistent in both locations we avoid having to branch in the rendering output code.
|
|
171
|
-
// Ideally in the future we will update both APIs to utilise the "raw" form of the title which is better suited to edit contexts.
|
|
172
|
-
// e.g.
|
|
173
|
-
// - title.raw = "Yes & No"
|
|
174
|
-
// - title.rendered = "Yes & No"
|
|
175
|
-
// - decodeEntities( title.rendered ) = "Yes & No"
|
|
176
|
-
// See:
|
|
177
|
-
// - https://github.com/WordPress/gutenberg/pull/41063
|
|
178
|
-
// - https://github.com/WordPress/gutenberg/blob/a1e1fdc0e6278457e9f4fc0b31ac6d2095f5450b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js#L212-L218
|
|
179
|
-
title: decodeEntities( page.title.rendered ),
|
|
180
|
-
url: page.link,
|
|
181
|
-
kind: 'post-type',
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
81
|
// Memoize link value to avoid overriding the LinkControl's internal state.
|
|
186
82
|
// This is a temporary fix. See https://github.com/WordPress/gutenberg/issues/50976#issuecomment-1568226407.
|
|
187
83
|
const link = useMemo(
|
|
@@ -193,15 +89,24 @@ function UnforwardedLinkUI( props, ref ) {
|
|
|
193
89
|
[ label, opensInNewTab, url ]
|
|
194
90
|
);
|
|
195
91
|
|
|
92
|
+
const handlePageCreated = ( pageLink ) => {
|
|
93
|
+
// Set the new page as the current link
|
|
94
|
+
props.onChange( pageLink );
|
|
95
|
+
// Return to main Link UI
|
|
96
|
+
setAddingPage( false );
|
|
97
|
+
};
|
|
98
|
+
|
|
196
99
|
const dialogTitleId = useInstanceId(
|
|
197
100
|
LinkUI,
|
|
198
|
-
|
|
101
|
+
'link-ui-link-control__title'
|
|
199
102
|
);
|
|
200
103
|
const dialogDescriptionId = useInstanceId(
|
|
201
104
|
LinkUI,
|
|
202
|
-
|
|
105
|
+
'link-ui-link-control__description'
|
|
203
106
|
);
|
|
204
107
|
|
|
108
|
+
const blockEditingMode = useBlockEditingMode();
|
|
109
|
+
|
|
205
110
|
return (
|
|
206
111
|
<Popover
|
|
207
112
|
ref={ ref }
|
|
@@ -210,7 +115,7 @@ function UnforwardedLinkUI( props, ref ) {
|
|
|
210
115
|
anchor={ props.anchor }
|
|
211
116
|
shift
|
|
212
117
|
>
|
|
213
|
-
{ ! addingBlock && (
|
|
118
|
+
{ ! addingBlock && ! addingPage && (
|
|
214
119
|
<div
|
|
215
120
|
role="dialog"
|
|
216
121
|
aria-labelledby={ dialogTitleId }
|
|
@@ -230,47 +135,41 @@ function UnforwardedLinkUI( props, ref ) {
|
|
|
230
135
|
hasRichPreviews
|
|
231
136
|
value={ link }
|
|
232
137
|
showInitialSuggestions
|
|
233
|
-
withCreateSuggestion={
|
|
234
|
-
createSuggestion={ handleCreate }
|
|
235
|
-
createSuggestionButtonText={ ( searchTerm ) => {
|
|
236
|
-
let format;
|
|
237
|
-
|
|
238
|
-
if ( type === 'post' ) {
|
|
239
|
-
/* translators: %s: search term. */
|
|
240
|
-
format = __(
|
|
241
|
-
'Create draft post: <mark>%s</mark>'
|
|
242
|
-
);
|
|
243
|
-
} else {
|
|
244
|
-
/* translators: %s: search term. */
|
|
245
|
-
format = __(
|
|
246
|
-
'Create draft page: <mark>%s</mark>'
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return createInterpolateElement(
|
|
251
|
-
sprintf( format, searchTerm ),
|
|
252
|
-
{
|
|
253
|
-
mark: <mark />,
|
|
254
|
-
}
|
|
255
|
-
);
|
|
256
|
-
} }
|
|
138
|
+
withCreateSuggestion={ false }
|
|
257
139
|
noDirectEntry={ !! type }
|
|
258
140
|
noURLSuggestion={ !! type }
|
|
259
141
|
suggestionsQuery={ getSuggestionsQuery( type, kind ) }
|
|
260
142
|
onChange={ props.onChange }
|
|
261
143
|
onRemove={ props.onRemove }
|
|
262
144
|
onCancel={ props.onCancel }
|
|
263
|
-
renderControlBottom={ () =>
|
|
264
|
-
|
|
145
|
+
renderControlBottom={ () => {
|
|
146
|
+
// Don't show the tools when there is submitted link (preview state).
|
|
147
|
+
if ( link?.url?.length ) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return (
|
|
265
152
|
<LinkUITools
|
|
266
153
|
focusAddBlockButton={ focusAddBlockButton }
|
|
154
|
+
focusAddPageButton={ focusAddPageButton }
|
|
267
155
|
setAddingBlock={ () => {
|
|
268
156
|
setAddingBlock( true );
|
|
269
157
|
setFocusAddBlockButton( false );
|
|
270
158
|
} }
|
|
159
|
+
setAddingPage={ () => {
|
|
160
|
+
setAddingPage( true );
|
|
161
|
+
setFocusAddPageButton( false );
|
|
162
|
+
} }
|
|
163
|
+
canAddPage={
|
|
164
|
+
permissions?.canCreate &&
|
|
165
|
+
type === 'page'
|
|
166
|
+
}
|
|
167
|
+
canAddBlock={
|
|
168
|
+
blockEditingMode === 'default'
|
|
169
|
+
}
|
|
271
170
|
/>
|
|
272
|
-
)
|
|
273
|
-
}
|
|
171
|
+
);
|
|
172
|
+
} }
|
|
274
173
|
/>
|
|
275
174
|
</div>
|
|
276
175
|
) }
|
|
@@ -281,7 +180,22 @@ function UnforwardedLinkUI( props, ref ) {
|
|
|
281
180
|
onBack={ () => {
|
|
282
181
|
setAddingBlock( false );
|
|
283
182
|
setFocusAddBlockButton( true );
|
|
183
|
+
setFocusAddPageButton( false );
|
|
184
|
+
} }
|
|
185
|
+
onBlockInsert={ props?.onBlockInsert }
|
|
186
|
+
/>
|
|
187
|
+
) }
|
|
188
|
+
|
|
189
|
+
{ addingPage && (
|
|
190
|
+
<LinkUIPageCreator
|
|
191
|
+
postType={ postType }
|
|
192
|
+
onBack={ () => {
|
|
193
|
+
setAddingPage( false );
|
|
194
|
+
setFocusAddPageButton( true );
|
|
195
|
+
setFocusAddBlockButton( false );
|
|
284
196
|
} }
|
|
197
|
+
onPageCreated={ handlePageCreated }
|
|
198
|
+
initialTitle={ link?.url || '' }
|
|
285
199
|
/>
|
|
286
200
|
) }
|
|
287
201
|
</Popover>
|
|
@@ -290,9 +204,17 @@ function UnforwardedLinkUI( props, ref ) {
|
|
|
290
204
|
|
|
291
205
|
export const LinkUI = forwardRef( UnforwardedLinkUI );
|
|
292
206
|
|
|
293
|
-
const LinkUITools = ( {
|
|
207
|
+
const LinkUITools = ( {
|
|
208
|
+
setAddingBlock,
|
|
209
|
+
setAddingPage,
|
|
210
|
+
focusAddBlockButton,
|
|
211
|
+
focusAddPageButton,
|
|
212
|
+
canAddPage,
|
|
213
|
+
canAddBlock,
|
|
214
|
+
} ) => {
|
|
294
215
|
const blockInserterAriaRole = 'listbox';
|
|
295
216
|
const addBlockButtonRef = useRef();
|
|
217
|
+
const addPageButtonRef = useRef();
|
|
296
218
|
|
|
297
219
|
// Focus the add block button when the popover is opened.
|
|
298
220
|
useEffect( () => {
|
|
@@ -301,20 +223,48 @@ const LinkUITools = ( { setAddingBlock, focusAddBlockButton } ) => {
|
|
|
301
223
|
}
|
|
302
224
|
}, [ focusAddBlockButton ] );
|
|
303
225
|
|
|
226
|
+
// Focus the add page button when the popover is opened.
|
|
227
|
+
useEffect( () => {
|
|
228
|
+
if ( focusAddPageButton ) {
|
|
229
|
+
addPageButtonRef.current?.focus();
|
|
230
|
+
}
|
|
231
|
+
}, [ focusAddPageButton ] );
|
|
232
|
+
|
|
233
|
+
// Don't render anything if neither button should be shown
|
|
234
|
+
if ( ! canAddPage && ! canAddBlock ) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
304
238
|
return (
|
|
305
|
-
<VStack className="link-ui-tools">
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
e
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
239
|
+
<VStack spacing={ 0 } className="link-ui-tools">
|
|
240
|
+
{ canAddPage && (
|
|
241
|
+
<Button
|
|
242
|
+
__next40pxDefaultSize
|
|
243
|
+
ref={ addPageButtonRef }
|
|
244
|
+
icon={ plus }
|
|
245
|
+
onClick={ ( e ) => {
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
setAddingPage( true );
|
|
248
|
+
} }
|
|
249
|
+
aria-haspopup={ blockInserterAriaRole }
|
|
250
|
+
>
|
|
251
|
+
{ __( 'Create page' ) }
|
|
252
|
+
</Button>
|
|
253
|
+
) }
|
|
254
|
+
{ canAddBlock && (
|
|
255
|
+
<Button
|
|
256
|
+
__next40pxDefaultSize
|
|
257
|
+
ref={ addBlockButtonRef }
|
|
258
|
+
icon={ plus }
|
|
259
|
+
onClick={ ( e ) => {
|
|
260
|
+
e.preventDefault();
|
|
261
|
+
setAddingBlock( true );
|
|
262
|
+
} }
|
|
263
|
+
aria-haspopup={ blockInserterAriaRole }
|
|
264
|
+
>
|
|
265
|
+
{ __( 'Add block' ) }
|
|
266
|
+
</Button>
|
|
267
|
+
) }
|
|
318
268
|
</VStack>
|
|
319
269
|
);
|
|
320
270
|
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
TextControl,
|
|
7
|
+
Notice,
|
|
8
|
+
CheckboxControl,
|
|
9
|
+
__experimentalVStack as VStack,
|
|
10
|
+
__experimentalHStack as HStack,
|
|
11
|
+
} from '@wordpress/components';
|
|
12
|
+
import { __ } from '@wordpress/i18n';
|
|
13
|
+
import { useSelect, useDispatch } from '@wordpress/data';
|
|
14
|
+
import { store as coreStore } from '@wordpress/core-data';
|
|
15
|
+
import { decodeEntities } from '@wordpress/html-entities';
|
|
16
|
+
import { useState } from '@wordpress/element';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Internal dependencies
|
|
20
|
+
*/
|
|
21
|
+
import DialogWrapper from './dialog-wrapper';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Component for creating new pages within the Navigation Link UI.
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} props Component props.
|
|
27
|
+
* @param {string} props.postType The post type to create.
|
|
28
|
+
* @param {Function} props.onBack Callback when user wants to go back.
|
|
29
|
+
* @param {Function} props.onPageCreated Callback when page is successfully created.
|
|
30
|
+
* @param {string} [props.initialTitle] Initial title to pre-fill the form.
|
|
31
|
+
*/
|
|
32
|
+
export function LinkUIPageCreator( {
|
|
33
|
+
postType,
|
|
34
|
+
onBack,
|
|
35
|
+
onPageCreated,
|
|
36
|
+
initialTitle = '',
|
|
37
|
+
} ) {
|
|
38
|
+
const [ title, setTitle ] = useState( initialTitle );
|
|
39
|
+
const [ shouldPublish, setShouldPublish ] = useState( false );
|
|
40
|
+
|
|
41
|
+
// Check if the title is valid for submission
|
|
42
|
+
const isTitleValid = title.trim().length > 0;
|
|
43
|
+
|
|
44
|
+
// Get the last created entity record (without ID) to track creation state
|
|
45
|
+
const { lastError, isSaving } = useSelect(
|
|
46
|
+
( select ) => ( {
|
|
47
|
+
lastError: select( coreStore ).getLastEntitySaveError(
|
|
48
|
+
'postType',
|
|
49
|
+
postType
|
|
50
|
+
),
|
|
51
|
+
isSaving: select( coreStore ).isSavingEntityRecord(
|
|
52
|
+
'postType',
|
|
53
|
+
postType
|
|
54
|
+
),
|
|
55
|
+
} ),
|
|
56
|
+
[ postType ]
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const { saveEntityRecord } = useDispatch( coreStore );
|
|
60
|
+
|
|
61
|
+
async function createPage( event ) {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
if ( isSaving || ! isTitleValid ) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const savedRecord = await saveEntityRecord(
|
|
69
|
+
'postType',
|
|
70
|
+
postType,
|
|
71
|
+
{
|
|
72
|
+
title,
|
|
73
|
+
status: shouldPublish ? 'publish' : 'draft',
|
|
74
|
+
},
|
|
75
|
+
{ throwOnError: true }
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if ( savedRecord ) {
|
|
79
|
+
// Create the page link object from the saved record
|
|
80
|
+
const pageLink = {
|
|
81
|
+
id: savedRecord.id,
|
|
82
|
+
type: postType,
|
|
83
|
+
title: decodeEntities( savedRecord.title.rendered ),
|
|
84
|
+
url: savedRecord.link,
|
|
85
|
+
kind: 'post-type',
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
onPageCreated( pageLink );
|
|
89
|
+
}
|
|
90
|
+
} catch ( error ) {
|
|
91
|
+
// Error handling is done via the data store selectors
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const isSubmitDisabled = isSaving || ! isTitleValid;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<DialogWrapper
|
|
99
|
+
className="link-ui-page-creator"
|
|
100
|
+
title={ __( 'Create page' ) }
|
|
101
|
+
description={ __( 'Create a new page to add to your Navigation.' ) }
|
|
102
|
+
onBack={ onBack }
|
|
103
|
+
>
|
|
104
|
+
<VStack className="link-ui-page-creator__inner" spacing={ 4 }>
|
|
105
|
+
<form onSubmit={ createPage }>
|
|
106
|
+
<VStack spacing={ 4 }>
|
|
107
|
+
<TextControl
|
|
108
|
+
__next40pxDefaultSize
|
|
109
|
+
__nextHasNoMarginBottom
|
|
110
|
+
label={ __( 'Title' ) }
|
|
111
|
+
onChange={ setTitle }
|
|
112
|
+
placeholder={ __( 'No title' ) }
|
|
113
|
+
value={ title }
|
|
114
|
+
/>
|
|
115
|
+
|
|
116
|
+
<CheckboxControl
|
|
117
|
+
__nextHasNoMarginBottom
|
|
118
|
+
label={ __( 'Publish immediately' ) }
|
|
119
|
+
help={ __(
|
|
120
|
+
'If unchecked, the page will be created as a draft.'
|
|
121
|
+
) }
|
|
122
|
+
checked={ shouldPublish }
|
|
123
|
+
onChange={ setShouldPublish }
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
{ lastError && (
|
|
127
|
+
<Notice status="error" isDismissible={ false }>
|
|
128
|
+
{ lastError.message }
|
|
129
|
+
</Notice>
|
|
130
|
+
) }
|
|
131
|
+
|
|
132
|
+
<HStack spacing={ 2 } justify="flex-end">
|
|
133
|
+
<Button
|
|
134
|
+
__next40pxDefaultSize
|
|
135
|
+
variant="tertiary"
|
|
136
|
+
onClick={ onBack }
|
|
137
|
+
disabled={ isSaving }
|
|
138
|
+
accessibleWhenDisabled
|
|
139
|
+
>
|
|
140
|
+
{ __( 'Cancel' ) }
|
|
141
|
+
</Button>
|
|
142
|
+
<Button
|
|
143
|
+
__next40pxDefaultSize
|
|
144
|
+
variant="primary"
|
|
145
|
+
type="submit"
|
|
146
|
+
isBusy={ isSaving }
|
|
147
|
+
aria-disabled={ isSubmitDisabled }
|
|
148
|
+
>
|
|
149
|
+
{ __( 'Create page' ) }
|
|
150
|
+
</Button>
|
|
151
|
+
</HStack>
|
|
152
|
+
</VStack>
|
|
153
|
+
</form>
|
|
154
|
+
</VStack>
|
|
155
|
+
</DialogWrapper>
|
|
156
|
+
);
|
|
157
|
+
}
|