Repository navigation
Add page creation appender to Page List block within Navigation #75674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from 18 commits
289bf68
75494b4
6152589
54fd9e1
4bb5e6e
6c62055
8ef1c87
1586199
febb261
8d96563
8d8a4a6
bf7b821
79ca788
eefda2b
2fff403
2d1e301
63e4876
de495a8
6743c14
54c6537
579ce0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we get away without passing this param? It'll always be the last item.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The The calculation finds |
||
| } ) { | ||
| 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 ( | ||
| <DialogWrapper | ||
| className="link-ui-page-creator" | ||
|
|
@@ -154,7 +163,7 @@ export function LinkUIPageCreator( { | |
| <Button | ||
| __next40pxDefaultSize | ||
| variant="tertiary" | ||
| onClick={ onBack } | ||
| onClick={ handleDismiss } | ||
| disabled={ isSaving } | ||
| accessibleWhenDisabled | ||
| > | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,18 +39,23 @@ 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, | ||
| hasResolvedPages, | ||
| blockList, | ||
| pages, | ||
| parentPageID, | ||
| showAppender, | ||
| } ) { | ||
| if ( ! hasResolvedPages ) { | ||
| return ( | ||
|
|
@@ -111,7 +116,16 @@ function BlockContent( { | |
| } | ||
|
|
||
| if ( pages.length > 0 ) { | ||
| return <ul { ...innerBlocksProps }></ul>; | ||
| return ( | ||
| <ul { ...innerBlocksProps }> | ||
| { innerBlocksProps.children } | ||
| { showAppender && ( | ||
| <li className="wp-block-page-list__appender-container"> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this should be an
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 6743c14. The appender is now a <div {...blockProps}>
<ul {...innerBlocksProps}>
{innerBlocksProps.children}
</ul>
{showAppender && (
<div className="wp-block-page-list__appender-container">
<PageCreatorAppender />
</div>
)}
</div>
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had it like this before, but the problem was that sometimes it triggered a new block creation instead of opening the appender. Hopefully this time it doesn't! |
||
| <PageCreatorAppender /> | ||
| </li> | ||
| ) } | ||
| </ul> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -259,12 +273,14 @@ export default function PageListEdit( { | |
| parentClientId, | ||
| hasDraggedChild, | ||
| isChildOfNavigation, | ||
| isSelected, | ||
| } = useSelect( | ||
| ( select ) => { | ||
| const { | ||
| getBlockParentsByBlockName, | ||
| hasSelectedInnerBlock, | ||
| hasDraggedInnerBlock, | ||
| getSelectedBlockClientId, | ||
| } = select( blockEditorStore ); | ||
| const blockParents = getBlockParentsByBlockName( | ||
| clientId, | ||
|
|
@@ -282,6 +298,7 @@ export default function PageListEdit( { | |
| hasSelectedChild: hasSelectedInnerBlock( clientId, true ), | ||
| hasDraggedChild: hasDraggedInnerBlock( clientId, true ), | ||
| parentClientId: navigationBlockParents[ 0 ], | ||
| isSelected: getSelectedBlockClientId() === clientId, | ||
| }; | ||
| }, | ||
| [ clientId ] | ||
|
|
@@ -294,6 +311,28 @@ export default function PageListEdit( { | |
| parentPageID, | ||
| } ); | ||
|
|
||
| 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( blockProps, { | ||
| renderAppender: false, | ||
| __unstableDisableDropZone: true, | ||
|
|
@@ -306,11 +345,12 @@ export default function PageListEdit( { | |
| const { selectBlock } = useDispatch( blockEditorStore ); | ||
|
|
||
| useEffect( () => { | ||
| if ( hasSelectedChild || hasDraggedChild ) { | ||
| if ( isChildOfNavigation && ( hasSelectedChild || hasDraggedChild ) ) { | ||
| openModal(); | ||
| selectBlock( parentClientId ); | ||
| } | ||
| }, [ | ||
| isChildOfNavigation, | ||
| hasSelectedChild, | ||
| hasDraggedChild, | ||
| parentClientId, | ||
|
|
@@ -396,14 +436,17 @@ export default function PageListEdit( { | |
| ) } | ||
| </> | ||
| ) } | ||
| <BlockContent | ||
| blockProps={ blockProps } | ||
| innerBlocksProps={ innerBlocksProps } | ||
| hasResolvedPages={ hasResolvedPages } | ||
| blockList={ blockList } | ||
| pages={ pages } | ||
| parentPageID={ parentPageID } | ||
| /> | ||
| <PageCreatorContext.Provider value={ pageCreatorContextValue }> | ||
| <BlockContent | ||
| blockProps={ blockProps } | ||
| innerBlocksProps={ innerBlocksProps } | ||
| hasResolvedPages={ hasResolvedPages } | ||
| blockList={ blockList } | ||
| pages={ pages } | ||
| parentPageID={ parentPageID } | ||
| showAppender={ showAppender } | ||
| /> | ||
| </PageCreatorContext.Provider> | ||
| </> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <div>, 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,22 @@ | |
| .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 { | ||
| background: $gray-900; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once #76087 merges we'll be able to use a mixin here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| color: $white; | ||
| padding: 0; | ||
| width: $button-size-small; | ||
| height: $button-size-small; | ||
|
|
||
| &:hover { | ||
| color: $white; | ||
| background: var(--wp-admin-theme-color); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /** | ||
| * 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 ); | ||
|
|
||
| return ( | ||
| <> | ||
| <Button | ||
| size="small" | ||
| ref={ ref } | ||
| className="wp-block-page-list__appender-toggle" | ||
| icon={ plus } | ||
| onClick={ () => setShowCreator( true ) } | ||
| label={ __( 'Add page' ) } | ||
| showTooltip | ||
| /> | ||
| { showCreator && ( | ||
| <Popover | ||
| anchor={ ref.current } | ||
| placement="bottom-start" | ||
| shift | ||
| onClose={ () => setShowCreator( false ) } | ||
| > | ||
| <LinkUIPageCreator | ||
| postType="page" | ||
| onClose={ () => setShowCreator( false ) } | ||
| onPageCreated={ () => setShowCreator( false ) } | ||
| menuOrder={ contextData?.menuOrder } | ||
| /> | ||
| </Popover> | ||
| ) } | ||
| </> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot Is this necessary for this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is necessary. The
PageCreatorAppendercomponent reusesLinkUIPageCreatorbut only providesonClose, notonBack. Making the back button conditional allows the component to be used both in the Navigation Link UI (with back button) and in the standalone Page List context (without back button, just close via Popover).