Skip to content

Commit

Permalink
Implement basic mechanics and UI
Browse files Browse the repository at this point in the history
  • Loading branch information
getdave committed Sep 24, 2021
1 parent cdbda96 commit cf01dc8
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 25 deletions.
64 changes: 55 additions & 9 deletions packages/block-editor/src/components/link-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -119,15 +120,20 @@ function LinkControl( {
noURLSuggestion = false,
createSuggestionButtonText,
hasRichPreviews = false,
hasTextControl = false,
} ) {
if ( withCreateSuggestion === undefined && createSuggestion ) {
withCreateSuggestion = true;
}

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(
Expand All @@ -152,17 +158,31 @@ 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();

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.
Expand All @@ -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();
};
Expand All @@ -196,6 +225,9 @@ function LinkControl( {

const showSettingsDrawer = !! settings?.length;

// Only show once a URL value has been committed.
const showTextControl = hasTextControl;

return (
<div
tabIndex={ -1 }
Expand All @@ -210,10 +242,24 @@ function LinkControl( {

{ ( isEditingLink || ! value ) && ! isCreatingPage && (
<>
<div className="block-editor-link-control__search-input-wrapper">
<div
className={ classnames( {
'block-editor-link-control__search-input-wrapper': true,
'has-text-control': showTextControl,
} ) }
>
{ showTextControl && (
<TextControl
className="block-editor-link-control__field block-editor-link-control__text-content"
label="Text"
value={ internalTextValue }
onChange={ setInternalTextValue }
/>
) }

<LinkControlSearchInput
currentLink={ value }
className="block-editor-link-control__search-input"
className="block-editor-link-control__field block-editor-link-control__search-input"
placeholder={ searchInputPlaceholder }
value={ currentInputValue }
withCreateSuggestion={ withCreateSuggestion }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ export default function LinkPreview( {
className="block-editor-link-control__search-item-title"
href={ value.url }
>
{ richData?.title || value?.title || displayURL }
{ richData?.title ||
value?.text ||
value?.title ||
displayURL }
</ExternalLink>
{ value?.url && (
<span className="block-editor-link-control__search-item-info">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ const LinkControlSearchInput = forwardRef(
return (
<div>
<URLInput
label={ 'URL' }
className={ className }
value={ value }
onChange={ onInputChange }
Expand Down
35 changes: 24 additions & 11 deletions packages/block-editor/src/components/link-control/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,34 @@ $preview-image-height: 140px;
position: relative;
}

// LinkControl popover.
.block-editor-link-control .block-editor-link-control__search-input {
// Specificity override.
&.block-editor-link-control__search-input input[type="text"] {
.block-editor-link-control__field {
margin: $grid-unit-20; // allow margin collapse for vertical spacing.

// Element wrapping the label and input.
> .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 {
Expand All @@ -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;
}
Expand Down
23 changes: 19 additions & 4 deletions packages/block-library/src/navigation-link/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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( '-', '_' );
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -654,6 +668,7 @@ export default function NavigationLinkEdit( {
} }
/>
<LinkControl
hasTextControl
className="wp-block-navigation-link__inline-link-input"
value={ link }
showInitialSuggestions={ true }
Expand Down

0 comments on commit cf01dc8

Please sign in to comment.