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 ( + <> +