diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 9ae7738510c726..f53825a6164b09 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -63,6 +63,10 @@ import { } from './components/block-list/use-block-props/use-block-refs'; import { LinkPicker } from './components/link-picker'; import useRemoteUrlData from './components/link-control/use-rich-url-data'; +import { + isHashLink, + isRelativePath, +} from './components/link-control/is-url-like'; /** * Private @wordpress/block-editor APIs. @@ -122,4 +126,6 @@ lock( privateApis, { useBlockElementRef, LinkPicker, useRemoteUrlData, + isHashLink, + isRelativePath, } ); diff --git a/packages/block-library/src/navigation-link/shared/controls.js b/packages/block-library/src/navigation-link/shared/controls.js index ea5d40245be7f7..06976097dc4991 100644 --- a/packages/block-library/src/navigation-link/shared/controls.js +++ b/packages/block-library/src/navigation-link/shared/controls.js @@ -2,17 +2,23 @@ * WordPress dependencies */ import { + Button, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, + __experimentalHStack as HStack, CheckboxControl, TextControl, TextareaControl, } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; -import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { + privateApis as blockEditorPrivateApis, + store as blockEditorStore, +} from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; +import { external } from '@wordpress/icons'; /** * Internal dependencies @@ -25,7 +31,9 @@ import { useLinkPreview } from './use-link-preview'; import { useIsInvalidLink } from './use-is-invalid-link'; import { unlock } from '../../lock-unlock'; -const { LinkPicker } = unlock( blockEditorPrivateApis ); +const { LinkPicker, isHashLink, isRelativePath } = unlock( + blockEditorPrivateApis +); /** * Get a human-readable entity type name. @@ -139,6 +147,17 @@ export function Controls( { attributes, setAttributes, clientId } ) { [ entityRecord?.featured_media ] ); + const onNavigateToEntityRecord = useSelect( + ( select ) => + select( blockEditorStore ).getSettings().onNavigateToEntityRecord, + [] + ); + + const homeUrl = useSelect( ( select ) => { + return select( coreStore ).getEntityRecord( 'root', '__unstableBase' ) + ?.home; + }, [] ); + const preview = useLinkPreview( { url, title: linkTitle, @@ -149,6 +168,21 @@ export function Controls( { attributes, setAttributes, clientId } ) { isEntityAvailable: isBoundEntityAvailable, } ); + // Check if URL is viewable (not hash link or other relative path like ./ or ../) + const isViewableUrl = + url && + ( ! isHashLink( url ) || + ( isRelativePath( url ) && ! url.startsWith( '/' ) ) ); + + // Construct full URL for viewing (prepend home URL for absolute paths starting with /) + const viewUrl = + isViewableUrl && url.startsWith( '/' ) && homeUrl ? homeUrl + url : url; + + const entityTypeName = getEntityTypeName( + attributes.type, + attributes.kind + ); + return ( + { url && ( + + { hasUrlBinding && + isBoundEntityAvailable && + entityRecord?.id && + attributes.kind === 'post-type' && + onNavigateToEntityRecord && ( + + ) } + { isViewableUrl && ( + + ) } + + ) } + !! opensInNewTab } label={ __( 'Open in new tab' ) }