diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-slug.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-slug.js new file mode 100644 index 0000000000000..d6ffa1991333e --- /dev/null +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-slug.js @@ -0,0 +1,161 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { + safeDecodeURIComponent, + filterURLForDisplay, + cleanForSlug, +} from '@wordpress/url'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; +import { + __experimentalInputControl as InputControl, + __experimentalHStack as HStack, + __experimentalVStack as VStack, + __experimentalText as Text, + Dropdown, + Button, +} from '@wordpress/components'; +import { store as coreStore } from '@wordpress/core-data'; + +export const PERMALINK_POSTNAME_REGEX = /%(?:postname|pagename)%/; + +function getPostPermalink( record, isEditable ) { + if ( ! record?.permalink_template ) { + return; + } + const slug = record?.slug || record?.generated_slug; + const [ prefix, suffix ] = record.permalink_template.split( + PERMALINK_POSTNAME_REGEX + ); + const permalink = isEditable ? prefix + slug + suffix : record.link; + return filterURLForDisplay( safeDecodeURIComponent( permalink ) ); +} + +export default function PageSlug( { postType, postId } ) { + const { editEntityRecord } = useDispatch( coreStore ); + const { record, savedSlug } = useSelect( + ( select ) => { + const { getEntityRecord, getEditedEntityRecord } = + select( coreStore ); + const savedRecord = getEntityRecord( 'postType', postType, postId ); + return { + record: getEditedEntityRecord( 'postType', postType, postId ), + savedSlug: savedRecord?.slug || savedRecord?.generated_slug, + }; + }, + [ postType, postId ] + ); + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + const [ forceEmptyField, setForceEmptyField ] = useState( false ); + const isEditable = + PERMALINK_POSTNAME_REGEX.test( record?.permalink_template ) && + record?._links?.[ 'wp:action-publish' ]; + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + 'aria-label': __( 'Change slug' ), + placement: 'bottom-end', + } ), + [ popoverAnchor ] + ); + if ( ! record || ! isEditable ) { + return null; + } + const recordSlug = safeDecodeURIComponent( + record?.slug || record?.generated_slug + ); + const permaLink = getPostPermalink( record, isEditable ); + const onSlugChange = ( newValue ) => { + editEntityRecord( 'postType', postType, postId, { + slug: newValue, + } ); + }; + return ( + + + { __( 'URL' ) } + + { + if ( forceEmptyField ) { + onSlugChange( cleanForSlug( savedSlug ) ); + setForceEmptyField( false ); + } + } } + renderToggle={ ( { onToggle } ) => ( + + ) } + renderContent={ ( { onClose } ) => { + return ( + <> + + +
+ { + onSlugChange( newValue ); + // When we delete the field the permalink gets + // reverted to the original value. + // The forceEmptyField logic allows the user to have + // the field temporarily empty while typing. + if ( ! newValue ) { + if ( ! forceEmptyField ) { + setForceEmptyField( true ); + } + return; + } + if ( forceEmptyField ) { + setForceEmptyField( false ); + } + } } + onBlur={ ( event ) => { + onSlugChange( + cleanForSlug( + event.target.value || + savedSlug + ) + ); + if ( forceEmptyField ) { + setForceEmptyField( false ); + } + } } + /> + +
+ + ); + } } + /> +
+ ); +} diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-summary.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-summary.js index c4dafeab6cb37..25b69985bcbd6 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-summary.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-summary.js @@ -8,6 +8,7 @@ import { __experimentalVStack as VStack } from '@wordpress/components'; import PageStatus from './page-status'; import PublishDate from './publish-date'; import EditTemplate from './edit-template'; +import PageSlug from './page-slug'; export default function PageSummary( { status, @@ -32,6 +33,7 @@ export default function PageSummary( { postType={ postType } /> + ); } diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/style.scss b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/style.scss index 5501fe49e5876..e1a8e4acb7227 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/style.scss +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/style.scss @@ -85,3 +85,10 @@ min-width: 240px; } } + +.edit-site-page-panels-edit-slug__dropdown { + .components-popover__content { + min-width: 320px; + padding: $grid-unit-20; + } +}