diff --git a/packages/base-styles/_mixins.scss b/packages/base-styles/_mixins.scss
index bbae5daa274121..74974a84254ec5 100644
--- a/packages/base-styles/_mixins.scss
+++ b/packages/base-styles/_mixins.scss
@@ -667,23 +667,6 @@
}
}
-@mixin inserter-toggle() {
- // Basic look
- background: colors.$gray-900;
- color: colors.$white;
- padding: 0;
-
- // TODO: Consider passing size="small" to the Inserter toggle instead.
- // Special dimensions for this button.
- min-width: variables.$button-size-small;
- height: variables.$button-size-small;
-
- &:hover {
- color: colors.$white;
- background: var(--wp-admin-theme-color);
- }
-}
-
@mixin selected-block-outline($widthRatio: 1) {
outline-color: var(--wp-admin-theme-color);
outline-style: solid;
diff --git a/packages/block-editor/src/components/block-tools/style.scss b/packages/block-editor/src/components/block-tools/style.scss
index 2cdf3e7096ce39..8d3f5b9e2cd12e 100644
--- a/packages/block-editor/src/components/block-tools/style.scss
+++ b/packages/block-editor/src/components/block-tools/style.scss
@@ -76,7 +76,20 @@
.block-editor-block-list__empty-block-inserter,
.block-editor-block-list__insertion-point-inserter {
.block-editor-inserter__toggle.components-button.has-icon {
- @include inserter-toggle;
+ // Basic look
+ background: $gray-900;
+ color: $white;
+ padding: 0;
+
+ // TODO: Consider passing size="small" to the Inserter toggle instead.
+ // Special dimensions for this button.
+ min-width: $button-size-small;
+ height: $button-size-small;
+
+ &:hover {
+ color: $white;
+ background: var(--wp-admin-theme-color);
+ }
}
}
diff --git a/packages/block-editor/src/components/default-block-appender/content.scss b/packages/block-editor/src/components/default-block-appender/content.scss
index 211202d40218d0..c94a8f21c17345 100644
--- a/packages/block-editor/src/components/default-block-appender/content.scss
+++ b/packages/block-editor/src/components/default-block-appender/content.scss
@@ -1,5 +1,4 @@
@use "@wordpress/base-styles/colors" as *;
-@use "@wordpress/base-styles/mixins" as *;
@use "@wordpress/base-styles/variables" as *;
@use "@wordpress/base-styles/z-index" as *;
@@ -42,7 +41,20 @@
}
.block-editor-inserter__toggle.components-button.has-icon {
- @include inserter-toggle;
+ // Basic look
+ background: $gray-900;
+ color: $white;
+ padding: 0;
+
+ // TODO: Consider passing size="small" to the Inserter toggle instead.
+ // Special dimensions for this button.
+ min-width: $button-size-small;
+ height: $button-size-small;
+
+ &:hover {
+ color: $white;
+ background: var(--wp-admin-theme-color);
+ }
}
}
@@ -91,16 +103,26 @@
// treating them the same, one or both can be retired.
.block-editor-inserter__toggle.components-button.has-icon,
.block-list-appender__toggle {
- @include inserter-toggle;
flex-direction: row;
box-shadow: none;
+ height: $button-size-small;
width: $button-size-small;
+ min-width: $button-size-small;
// Hide by default, then we unhide it when the selected block is a direct ancestor.
display: none;
// Important to override styles form Button component.
padding: 0 !important;
+
+ // Basic look
+ background: $gray-900;
+ color: $white;
+
+ &:hover {
+ color: $white;
+ background: var(--wp-admin-theme-color);
+ }
}
// This content only shows up inside the empty appender.
diff --git a/packages/block-library/src/navigation-link/link-ui/dialog-wrapper.js b/packages/block-library/src/navigation-link/link-ui/dialog-wrapper.js
index e37166a1dfc7c2..c470ac78de037a 100644
--- a/packages/block-library/src/navigation-link/link-ui/dialog-wrapper.js
+++ b/packages/block-library/src/navigation-link/link-ui/dialog-wrapper.js
@@ -64,7 +64,12 @@ function DialogWrapper( { className, title, description, onBack, children } ) {
{ description }
-
+ { onBack && (
+
+ ) }
{ children }
diff --git a/packages/block-library/src/navigation-link/link-ui/page-creator.js b/packages/block-library/src/navigation-link/link-ui/page-creator.js
index 30d1799f190979..6fea65ac4ce887 100644
--- a/packages/block-library/src/navigation-link/link-ui/page-creator.js
+++ b/packages/block-library/src/navigation-link/link-ui/page-creator.js
@@ -26,15 +26,19 @@ import DialogWrapper from './dialog-wrapper';
*
* @param {Object} props Component props.
* @param {string} props.postType The post type to create.
- * @param {Function} props.onBack Callback when user wants to go back.
+ * @param {Function} [props.onBack] Callback when user wants to go back. Shows a back button when provided.
+ * @param {Function} props.onClose Callback to close the creator without going back.
* @param {Function} props.onPageCreated Callback when page is successfully created.
* @param {string} [props.initialTitle] Initial title to pre-fill the form.
+ * @param {number} [props.menuOrder] Menu order for the new page.
*/
export function LinkUIPageCreator( {
postType,
onBack,
+ onClose,
onPageCreated,
initialTitle = '',
+ menuOrder,
} ) {
const [ title, setTitle ] = useState( initialTitle );
const [ shouldPublish, setShouldPublish ] = useState( true );
@@ -74,6 +78,9 @@ export function LinkUIPageCreator( {
{
title,
status: shouldPublish ? 'publish' : 'draft',
+ ...( menuOrder !== undefined && {
+ menu_order: menuOrder,
+ } ),
},
{ throwOnError: true }
);
@@ -117,6 +124,8 @@ export function LinkUIPageCreator( {
const isSubmitDisabled = isSaving || ! isTitleValid;
+ const handleDismiss = onBack || onClose;
+
return (
diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js
index cd665bb4434972..2189f5f93e9618 100644
--- a/packages/block-library/src/page-list/edit.js
+++ b/packages/block-library/src/page-list/edit.js
@@ -39,11 +39,15 @@ import {
ConvertToLinksModal,
} from './convert-to-links-modal';
import { useToolsPanelDropdownMenuProps } from '../utils/hooks';
+import PageCreatorAppender, {
+ PageCreatorContext,
+} from './page-creator-appender';
// We only show the edit option when page count is <= MAX_PAGE_COUNT
// Performance of Navigation Links is not good past this value.
const MAX_PAGE_COUNT = 100;
const NOOP = () => {};
+
function BlockContent( {
blockProps,
innerBlocksProps,
@@ -51,6 +55,7 @@ function BlockContent( {
blockList,
pages,
parentPageID,
+ showAppender,
} ) {
if ( ! hasResolvedPages ) {
return (
@@ -111,7 +116,18 @@ function BlockContent( {
}
if ( pages.length > 0 ) {
- return ;
+ return (
+
+
+ { innerBlocksProps.children }
+
+ { showAppender && (
+
+ ) }
+
+ );
}
}
@@ -259,12 +275,14 @@ export default function PageListEdit( {
parentClientId,
hasDraggedChild,
isChildOfNavigation,
+ isSelected,
} = useSelect(
( select ) => {
const {
getBlockParentsByBlockName,
hasSelectedInnerBlock,
hasDraggedInnerBlock,
+ getSelectedBlockClientId,
} = select( blockEditorStore );
const blockParents = getBlockParentsByBlockName(
clientId,
@@ -282,6 +300,7 @@ export default function PageListEdit( {
hasSelectedChild: hasSelectedInnerBlock( clientId, true ),
hasDraggedChild: hasDraggedInnerBlock( clientId, true ),
parentClientId: navigationBlockParents[ 0 ],
+ isSelected: getSelectedBlockClientId() === clientId,
};
},
[ clientId ]
@@ -294,23 +313,49 @@ export default function PageListEdit( {
parentPageID,
} );
- const innerBlocksProps = useInnerBlocksProps( blockProps, {
- renderAppender: false,
- __unstableDisableDropZone: true,
- templateLock: isChildOfNavigation ? false : 'all',
- onInput: NOOP,
- onChange: NOOP,
- value: blockList,
- } );
+ const showAppender =
+ ! isChildOfNavigation && ( isSelected || hasSelectedChild );
+
+ const nextMenuOrder = useMemo( () => {
+ if ( ! pages?.length ) {
+ return 0;
+ }
+ return (
+ pages.reduce(
+ ( max, page ) => Math.max( max, page.menu_order ),
+ 0
+ ) + 1
+ );
+ }, [ pages ] );
+
+ const pageCreatorContextValue = useMemo(
+ () => ( {
+ menuOrder: nextMenuOrder,
+ } ),
+ [ nextMenuOrder ]
+ );
+
+ const innerBlocksProps = useInnerBlocksProps(
+ {},
+ {
+ renderAppender: false,
+ __unstableDisableDropZone: true,
+ templateLock: isChildOfNavigation ? false : 'all',
+ onInput: NOOP,
+ onChange: NOOP,
+ value: blockList,
+ }
+ );
const { selectBlock } = useDispatch( blockEditorStore );
useEffect( () => {
- if ( hasSelectedChild || hasDraggedChild ) {
+ if ( isChildOfNavigation && ( hasSelectedChild || hasDraggedChild ) ) {
openModal();
selectBlock( parentClientId );
}
}, [
+ isChildOfNavigation,
hasSelectedChild,
hasDraggedChild,
parentClientId,
@@ -396,14 +441,17 @@ export default function PageListEdit( {
) }
>
) }
-
+
+
+
>
);
}
diff --git a/packages/block-library/src/page-list/editor.scss b/packages/block-library/src/page-list/editor.scss
index 2813dc1675b8ea..2dbca253908a08 100644
--- a/packages/block-library/src/page-list/editor.scss
+++ b/packages/block-library/src/page-list/editor.scss
@@ -1,4 +1,5 @@
@use "@wordpress/base-styles/breakpoints" as *;
+@use "@wordpress/base-styles/colors" as *;
@use "@wordpress/base-styles/mixins" as *;
@use "@wordpress/base-styles/variables" as *;
@@ -27,6 +28,13 @@
// Make links unclickable in the editor.
.wp-block-pages-list__item__link {
pointer-events: none;
+
+ // RawHTML wraps page titles in a , which is block-level.
+ // Firefox renders list markers on a separate line from block content.
+ // Force inline display so bullets align with titles across all browsers.
+ > div {
+ display: inline;
+ }
}
// Modal that shows conversion option.
@@ -61,3 +69,13 @@
.wp-block-page-list__loading-indicator-container {
padding: $grid-unit-10 $grid-unit-15;
}
+
+// Page creator appender container and button.
+.wp-block-page-list__appender-container {
+ list-style: none;
+ margin-top: $grid-unit-20;
+}
+
+.wp-block-page-list__appender-toggle.components-button {
+ @include inserter-toggle();
+}
diff --git a/packages/block-library/src/page-list/page-creator-appender.js b/packages/block-library/src/page-list/page-creator-appender.js
new file mode 100644
index 00000000000000..bd0cf720a12056
--- /dev/null
+++ b/packages/block-library/src/page-list/page-creator-appender.js
@@ -0,0 +1,60 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ createContext,
+ useContext,
+ useState,
+ useRef,
+} from '@wordpress/element';
+import { Button, Popover } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+import { plus } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import { LinkUIPageCreator } from '../navigation-link/link-ui/page-creator';
+
+export const PageCreatorContext = createContext( null );
+
+export default function PageCreatorAppender() {
+ const [ showCreator, setShowCreator ] = useState( false );
+ const ref = useRef();
+ const contextData = useContext( PageCreatorContext );
+
+ const closeCreator = () => {
+ setShowCreator( false );
+ // Return focus to the appender button after closing
+ ref.current?.focus();
+ };
+
+ return (
+ <>
+