-
@@ -60,10 +68,14 @@ exports[`URLPopover matches the snapshot when there are no settings 1`] = `
position="bottom center"
>
-
diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js
index b22d63da20cb9..a50b8601aa55d 100644
--- a/packages/block-library/src/image/edit.js
+++ b/packages/block-library/src/image/edit.js
@@ -3,6 +3,7 @@
*/
import classnames from 'classnames';
import {
+ find,
get,
isEmpty,
map,
@@ -17,7 +18,10 @@ import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob';
import {
Button,
ButtonGroup,
+ ExternalLink,
IconButton,
+ MenuItem,
+ NavigableMenu,
PanelBody,
Path,
Rect,
@@ -30,9 +34,16 @@ import {
ToggleControl,
Toolbar,
withNotices,
- ExternalLink,
} from '@wordpress/components';
import { compose } from '@wordpress/compose';
+import {
+ LEFT,
+ RIGHT,
+ UP,
+ DOWN,
+ BACKSPACE,
+ ENTER,
+} from '@wordpress/keycodes';
import { withSelect } from '@wordpress/data';
import {
BlockAlignmentToolbar,
@@ -40,9 +51,15 @@ import {
BlockIcon,
InspectorControls,
MediaPlaceholder,
+ URLPopover,
RichText,
} from '@wordpress/block-editor';
-import { Component } from '@wordpress/element';
+import {
+ Component,
+ useCallback,
+ useState,
+ useRef,
+} from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { getPath } from '@wordpress/url';
import { withViewportMatch } from '@wordpress/viewport';
@@ -95,6 +112,155 @@ const isTemporaryImage = ( id, url ) => ! id && isBlobURL( url );
*/
const isExternalImage = ( id, url ) => url && ! id && ! isBlobURL( url );
+const stopPropagation = ( event ) => {
+ event.stopPropagation();
+};
+
+const stopPropagationRelevantKeys = ( event ) => {
+ if ( [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( event.keyCode ) > -1 ) {
+ // Stop the key event from propagating up to ObserveTyping.startTypingInTextField.
+ event.stopPropagation();
+ }
+};
+
+const ImageURLInputUI = ( {
+ advancedOptions,
+ linkDestination,
+ mediaLinks,
+ onChangeUrl,
+ url,
+} ) => {
+ const [ isOpen, setIsOpen ] = useState( false );
+ const openLinkUI = useCallback( () => {
+ setIsOpen( true );
+ } );
+
+ const [ isEditingLink, setIsEditingLink ] = useState( false );
+ const [ urlInput, setUrlInput ] = useState( null );
+
+ const startEditLink = useCallback( () => {
+ if ( linkDestination === LINK_DESTINATION_MEDIA ||
+ linkDestination === LINK_DESTINATION_ATTACHMENT
+ ) {
+ setUrlInput( '' );
+ }
+ setIsEditingLink( true );
+ } );
+ const stopEditLink = useCallback( () => {
+ setIsEditingLink( false );
+ } );
+
+ const closeLinkUI = useCallback( () => {
+ setUrlInput( null );
+ stopEditLink();
+ setIsOpen( false );
+ } );
+
+ const autocompleteRef = useRef( null );
+
+ const onClickOutside = useCallback( () => {
+ return ( event ) => {
+ // The autocomplete suggestions list renders in a separate popover (in a portal),
+ // so onClickOutside fails to detect that a click on a suggestion occurred in the
+ // LinkContainer. Detect clicks on autocomplete suggestions using a ref here, and
+ // return to avoid the popover being closed.
+ const autocompleteElement = autocompleteRef.current;
+ if ( autocompleteElement && autocompleteElement.contains( event.target ) ) {
+ return;
+ }
+ setIsOpen( false );
+ setUrlInput( null );
+ stopEditLink();
+ };
+ } );
+
+ const onSubmitLinkChange = useCallback( () => {
+ return ( event ) => {
+ if ( urlInput ) {
+ onChangeUrl( urlInput );
+ }
+ stopEditLink();
+ setUrlInput( null );
+ event.preventDefault();
+ };
+ } );
+
+ const onLinkRemove = useCallback( () => {
+ closeLinkUI();
+ onChangeUrl( '' );
+ } );
+ const linkEditorValue = urlInput !== null ? urlInput : url;
+
+ const urlLabel = (
+ find( mediaLinks, [ 'linkDestination', linkDestination ] ) || {}
+ ).title;
+ return (
+ <>
+
+ { isOpen && (
+
advancedOptions }
+ additionalControls={ ! linkEditorValue && (
+
+ {
+ map( mediaLinks, ( link ) => (
+
+ ) )
+ }
+
+ ) }
+ >
+ { ( ! url || isEditingLink ) && (
+
+ ) }
+ { ( url && ! isEditingLink ) && (
+ <>
+
+
+ >
+ ) }
+
+ ) }
+ >
+ );
+};
+
class ImageEdit extends Component {
constructor( { attributes } ) {
super( ...arguments );
@@ -108,15 +274,15 @@ class ImageEdit extends Component {
this.updateWidth = this.updateWidth.bind( this );
this.updateHeight = this.updateHeight.bind( this );
this.updateDimensions = this.updateDimensions.bind( this );
- this.onSetCustomHref = this.onSetCustomHref.bind( this );
+ this.onSetHref = this.onSetHref.bind( this );
this.onSetLinkClass = this.onSetLinkClass.bind( this );
this.onSetLinkRel = this.onSetLinkRel.bind( this );
- this.onSetLinkDestination = this.onSetLinkDestination.bind( this );
this.onSetNewTab = this.onSetNewTab.bind( this );
this.getFilename = this.getFilename.bind( this );
this.toggleIsEditing = this.toggleIsEditing.bind( this );
this.onUploadError = this.onUploadError.bind( this );
this.onImageError = this.onImageError.bind( this );
+ this.getLinkDestinations = this.getLinkDestinations.bind( this );
this.state = {
captionFocused: false,
@@ -199,25 +365,6 @@ class ImageEdit extends Component {
} );
}
- onSetLinkDestination( value ) {
- let href;
-
- if ( value === LINK_DESTINATION_NONE ) {
- href = undefined;
- } else if ( value === LINK_DESTINATION_MEDIA ) {
- href = ( this.props.image && this.props.image.source_url ) || this.props.attributes.url;
- } else if ( value === LINK_DESTINATION_ATTACHMENT ) {
- href = this.props.image && this.props.image.link;
- } else {
- href = this.props.attributes.href;
- }
-
- this.props.setAttributes( {
- linkDestination: value,
- href,
- } );
- }
-
onSelectURL( newURL ) {
const { url } = this.props.attributes;
@@ -244,7 +391,28 @@ class ImageEdit extends Component {
}
}
- onSetCustomHref( value ) {
+ onSetHref( value ) {
+ const linkDestinations = this.getLinkDestinations();
+ const { attributes } = this.props;
+ const { linkDestination } = attributes;
+ let linkDestinationInput;
+ if ( ! value ) {
+ linkDestinationInput = LINK_DESTINATION_NONE;
+ } else {
+ linkDestinationInput = (
+ find( linkDestinations, ( destination ) => {
+ return destination.url === value;
+ } ) ||
+ { linkDestination: LINK_DESTINATION_CUSTOM }
+ ).linkDestination;
+ }
+ if ( linkDestination !== linkDestinationInput ) {
+ this.props.setAttributes( {
+ linkDestination: linkDestinationInput,
+ href: value,
+ } );
+ return;
+ }
this.props.setAttributes( { href: value } );
}
@@ -337,12 +505,21 @@ class ImageEdit extends Component {
}
}
- getLinkDestinationOptions() {
+ getLinkDestinations() {
return [
- { value: LINK_DESTINATION_NONE, label: __( 'None' ) },
- { value: LINK_DESTINATION_MEDIA, label: __( 'Media File' ) },
- { value: LINK_DESTINATION_ATTACHMENT, label: __( 'Attachment Page' ) },
- { value: LINK_DESTINATION_CUSTOM, label: __( 'Custom URL' ) },
+ {
+ linkDestination: LINK_DESTINATION_MEDIA,
+ title: __( 'Media File' ),
+ url: ( this.props.image && this.props.image.source_url ) ||
+ this.props.attributes.url,
+ icon,
+ },
+ {
+ linkDestination: LINK_DESTINATION_ATTACHMENT,
+ title: __( 'Attachment Page' ),
+ url: this.props.image && this.props.image.link,
+ icon:
,
+ },
];
}
@@ -400,15 +577,47 @@ class ImageEdit extends Component {
onChange={ this.updateAlignment }
/>
{ url && (
-
-
-
+ <>
+
+
+
+
+
+
+
+
+ >
+ }
+ />
+
+ >
) }
);
@@ -458,7 +667,7 @@ class ImageEdit extends Component {
} );
const isResizable = [ 'wide', 'full' ].indexOf( align ) === -1 && isLargeViewport;
- const isLinkURLInputReadOnly = linkDestination !== LINK_DESTINATION_CUSTOM;
+
const imageSizeOptions = this.getImageSizeOptions();
const getInspectorControls = ( imageWidth, imageHeight ) => (
@@ -539,39 +748,6 @@ class ImageEdit extends Component {
) }
-
-
- { linkDestination !== LINK_DESTINATION_NONE && (
- <>
-
-
-
-
- >
- ) }
-
);
diff --git a/packages/format-library/src/link/inline.js b/packages/format-library/src/link/inline.js
index c4b64e9e3c45c..564ae795fd484 100644
--- a/packages/format-library/src/link/inline.js
+++ b/packages/format-library/src/link/inline.js
@@ -1,22 +1,15 @@
-/**
- * External dependencies
- */
-import classnames from 'classnames';
-
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component, createRef, useMemo } from '@wordpress/element';
import {
- ExternalLink,
- IconButton,
ToggleControl,
withSpokenMessages,
} from '@wordpress/components';
import { LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } from '@wordpress/keycodes';
import { getRectangleFromRange } from '@wordpress/dom';
-import { prependHTTP, safeDecodeURI, filterURLForDisplay } from '@wordpress/url';
+import { prependHTTP } from '@wordpress/url';
import {
create,
insert,
@@ -25,7 +18,7 @@ import {
getTextContent,
slice,
} from '@wordpress/rich-text';
-import { URLInput, URLPopover } from '@wordpress/block-editor';
+import { URLPopover } from '@wordpress/block-editor';
/**
* Internal dependencies
@@ -38,45 +31,6 @@ function isShowingInput( props, state ) {
return props.addingLink || state.editLink;
}
-const LinkEditor = ( { value, onChangeInputValue, onKeyDown, submitLink, autocompleteRef } ) => (
- // Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
- /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
-
- /* eslint-enable jsx-a11y/no-noninteractive-element-interactions */
-);
-
-const LinkViewerUrl = ( { url } ) => {
- const prependedURL = prependHTTP( url );
- const linkClassName = classnames( 'editor-format-toolbar__link-container-value block-editor-format-toolbar__link-container-value', {
- 'has-invalid-link': ! isValidHref( prependedURL ),
- } );
-
- if ( ! url ) {
- return
;
- }
-
- return (
-
- { filterURLForDisplay( safeDecodeURI( url ) ) }
-
- );
-};
-
const URLPopoverAtLink = ( { isActive, addingLink, value, ...props } ) => {
const anchorRect = useMemo( () => {
const selection = window.getSelection();
@@ -111,21 +65,6 @@ const URLPopoverAtLink = ( { isActive, addingLink, value, ...props } ) => {
return
;
};
-const LinkViewer = ( { url, editLink } ) => {
- return (
- // Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
- /* eslint-disable jsx-a11y/no-static-element-interactions */
-
-
-
-
- /* eslint-enable jsx-a11y/no-static-element-interactions */
- );
-};
-
class InlineLinkUI extends Component {
constructor() {
super( ...arguments );
@@ -271,17 +210,22 @@ class InlineLinkUI extends Component {
) }
>
{ showInput ? (
-
) : (
-
) }