Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/block-editor/src/private-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -122,4 +126,6 @@ lock( privateApis, {
useBlockElementRef,
LinkPicker,
useRemoteUrlData,
isHashLink,
isRelativePath,
} );
90 changes: 88 additions & 2 deletions packages/block-library/src/navigation-link/shared/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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 (
<ToolsPanel
label={ __( 'Settings' ) }
Expand Down Expand Up @@ -198,6 +232,58 @@ export function Controls( { attributes, setAttributes, clientId } ) {
/>
</ToolsPanelItem>

{ url && (
<HStack
className="navigation-link-to__actions"
alignment="left"
justify="left"
style={ { gridColumn: '1 / -1' } }
>
{ hasUrlBinding &&
isBoundEntityAvailable &&
entityRecord?.id &&
attributes.kind === 'post-type' &&
onNavigateToEntityRecord && (
<Button
size="compact"
variant="secondary"
onClick={ () => {
onNavigateToEntityRecord( {
postId: entityRecord.id,
postType: attributes.type,
} );
} }
__next40pxDefaultSize
>
{ sprintf(
/* translators: %s: entity type (e.g., "page", "post", "category") */
__( 'Edit %s' ),
entityTypeName
) }
</Button>
) }
{ isViewableUrl && (
<Button
size="compact"
variant="secondary"
href={ viewUrl }
target="_blank"
icon={ external }
iconPosition="right"
__next40pxDefaultSize
>
{ sprintf(
/* translators: %s: entity type (e.g., "page", "post", "category") or "site" for external links */
__( 'View %s' ),
entityTypeName !== 'item'
? entityTypeName
: __( 'site' )
) }
</Button>
) }
</HStack>
) }

<ToolsPanelItem
hasValue={ () => !! opensInNewTab }
label={ __( 'Open in new tab' ) }
Expand Down
Loading