diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index 5b333a683cdb33..fb6b532c3c70c3 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -2,11 +2,12 @@ * External dependencies */ import { noop } from 'lodash'; +import classnames from 'classnames'; /** * WordPress dependencies */ -import { Button, Spinner, Notice } from '@wordpress/components'; +import { Button, Spinner, Notice, TextControl } from '@wordpress/components'; import { keyboardReturn } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; import { useRef, useState, useEffect } from '@wordpress/element'; @@ -119,6 +120,7 @@ function LinkControl( { noURLSuggestion = false, createSuggestionButtonText, hasRichPreviews = false, + hasTextControl = false, } ) { if ( withCreateSuggestion === undefined && createSuggestion ) { withCreateSuggestion = true; @@ -126,8 +128,12 @@ function LinkControl( { const isMounting = useRef( true ); const wrapperNode = useRef(); + const [ internalInputValue, setInternalInputValue ] = useState( - ( value && value.url ) || '' + value?.url || '' + ); + const [ internalTextValue, setInternalTextValue ] = useState( + value?.text || '' ); const currentInputValue = propInputValue || internalInputValue; const [ isEditingLink, setIsEditingLink ] = useState( @@ -152,10 +158,14 @@ function LinkControl( { return; } + const linkURLInputIndex = 1; + + // When editing, the 2nd focusable element is the Link URL input. + const whichFocusTarget = isEditingLink ? linkURLInputIndex : 0; // When switching between editable and non editable LinkControl - // move focus to the first element to avoid focus loss. + // move focus to the most appropriate element to avoid focus loss. const nextFocusTarget = - focus.focusable.find( wrapperNode.current )[ 0 ] || + focus.focusable.find( wrapperNode.current )[ whichFocusTarget ] || wrapperNode.current; nextFocusTarget.focus(); @@ -163,6 +173,16 @@ function LinkControl( { isEndingEditWithFocus.current = false; }, [ isEditingLink ] ); + /** + * If the value's `text` property changes then sync this + * back up with state. + */ + useEffect( () => { + if ( value?.text && value.text !== internalTextValue ) { + setInternalTextValue( value.text ); + } + }, [ value ] ); + /** * Cancels editing state and marks that focus may need to be restored after * the next render, if focus was within the wrapper when editing finished. @@ -180,13 +200,22 @@ function LinkControl( { ); const handleSelectSuggestion = ( updatedValue ) => { - onChange( updatedValue ); + onChange( { + ...updatedValue, + text: internalTextValue, + } ); stopEditing(); }; const handleSubmitButton = () => { - if ( currentInputValue !== value?.url ) { - onChange( { url: currentInputValue } ); + if ( + currentInputValue !== value?.url || + internalTextValue !== value?.text + ) { + onChange( { + url: currentInputValue, + text: internalTextValue, + } ); } stopEditing(); }; @@ -196,6 +225,9 @@ function LinkControl( { const showSettingsDrawer = !! settings?.length; + // Only show once a URL value has been committed. + const showTextControl = hasTextControl; + return (
-
+
+ { showTextControl && ( + + ) } + - { richData?.title || value?.title || displayURL } + { richData?.title || + value?.text || + value?.title || + displayURL } { value?.url && ( diff --git a/packages/block-editor/src/components/link-control/search-input.js b/packages/block-editor/src/components/link-control/search-input.js index 3a90bfb75344b6..eb0272e1847007 100644 --- a/packages/block-editor/src/components/link-control/search-input.js +++ b/packages/block-editor/src/components/link-control/search-input.js @@ -120,6 +120,7 @@ const LinkControlSearchInput = forwardRef( return (
.components-base-control__field { + display: flex; + align-items: center; + margin: 0; + } + + .components-base-control__label { + margin-right: $grid-unit-20; + margin-bottom: 0; + } + + input[type="text"], + // Specificity overide of URLInput defaults. + &.block-editor-url-input input[type="text"].block-editor-url-input__input { @include input-control; width: calc(100% - #{$grid-unit-20*2}); display: block; padding: 11px $grid-unit-20; padding-right: ( $button-size * $block-editor-link-control-number-of-actions ); // width of reset and submit buttons - margin: $grid-unit-20; + margin: 0; position: relative; border: 1px solid $gray-300; border-radius: $radius-block-ui; } - - .components-base-control__field { - margin-bottom: 0; - } } .block-editor-link-control__search-error { @@ -61,17 +71,20 @@ $preview-image-height: 140px; * when suggestions are rendered. * * Compensate for: - * - Input margin ($grid-unit-20) * - Border (1px) * - Vertically, for the difference in height between the input (40px) and * the icon buttons. * - Horizontally, pad to the minimum of: default input padding, or the * equivalent of the vertical padding. */ - top: $grid-unit-20 + 1px + ( ( 40px - $button-size ) * 0.5 ); + top: 1px + ( ( 40px - $button-size ) * 0.5 ); right: $grid-unit-20 + 1px + min($grid-unit-10, ( 40px - $button-size ) * 0.5); } +.block-editor-link-control__search-input-wrapper.has-text-control .block-editor-link-control__search-actions { + top: 59px; // TODO: figure this out in variables! +} + .components-button .block-editor-link-control__search-submit .has-icon { margin: -1px; } diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index f390210bf19330..7f44ad03b0f95f 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -229,21 +229,32 @@ export const updateNavigationLinkBlockAttributes = ( const { title = '', url = '', + text = '', opensInNewTab, id, kind: newKind = originalKind, type: newType = originalType, } = updatedValue; - const normalizedTitle = title.replace( /http(s?):\/\//gi, '' ); const normalizedURL = url.replace( /http(s?):\/\//gi, '' ); const escapeTitle = title !== '' && normalizedTitle !== normalizedURL && originalLabel !== title; - const label = escapeTitle - ? escape( title ) - : originalLabel || escape( normalizedURL ); + + let label; + + if ( text ) { + label = text; + } else if ( text === '' ) { + // If the user deliberately cleared out the link text + // then reset to default to the URL. + label = escape( normalizedURL ); + } else { + label = escapeTitle + ? escape( title ) + : originalLabel || escape( normalizedURL ); + } // In https://github.com/WordPress/gutenberg/pull/24670 we decided to use "tag" in favor of "post_tag" const type = newType === 'post_tag' ? 'tag' : newType.replace( '-', '_' ); @@ -286,10 +297,13 @@ export default function NavigationLinkEdit( { title, kind, } = attributes; + const link = { url, opensInNewTab, + text: label, }; + const { showSubmenuIcon } = context; const { saveEntityRecord } = useDispatch( coreStore ); const { insertBlock } = useDispatch( blockEditorStore ); @@ -654,6 +668,7 @@ export default function NavigationLinkEdit( { } } />