Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First stab at in-toolbar link editing #24021

Closed
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
4 changes: 4 additions & 0 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ _Returns_

Undocumented declaration.

<a name="BlockToolbarLinkControl" href="#BlockToolbarLinkControl">#</a> **BlockToolbarLinkControl**

Undocumented declaration.

<a name="BlockVerticalAlignmentToolbar" href="#BlockVerticalAlignmentToolbar">#</a> **BlockVerticalAlignmentToolbar**

_Related_
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export default function computeDisplayUrl( url ) {
if ( ! url ) {
return '';
}

let urlData;
try {
urlData = new URL( url );
} catch ( e ) {
return url;
}
let displayUrl = '';

const siteHost = document.location.host;
if ( urlData.host && urlData.host !== siteHost ) {
displayUrl += urlData.host;
}
displayUrl += urlData.pathname;
if ( urlData.search ) {
displayUrl += urlData.search;
}
return displayUrl;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* WordPress dependencies
*/
import { createContext } from '@wordpress/element';

const ToolbarLinkControlContext = createContext( {} );
export default ToolbarLinkControlContext;
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* WordPress dependencies
*/
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useCallback, useMemo, useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import BlockToolbarInlineEdit from '../block-toolbar-inline-edit';
import SearchInput from './search-input';
import SettingsMenu from './settings-menu';
import computeDisplayUrl from './compute-display-url';
import ToolbarLinkControlContext from './context';

export default function ToolbarLinkControl( {
initialLink,
createSuggestion,
close,
onChange,
} ) {
const [ currentLink, setCurrentLink ] = useState( {
...initialLink,
url: computeDisplayUrl( initialLink.url ),
} );

const updateCurrentLink = useCallback(
( data ) => {
const newLink = {
...currentLink,
...data,
};
if ( currentLink.id && ! data.id ) {
delete newLink.id;
}
if ( 'url' in data ) {
newLink.url = computeDisplayUrl( data.url );
} else {
newLink.url = currentLink.url;
}
if ( data.title && ! currentLink.label ) {
newLink.label = data.title;
}
setCurrentLink( newLink );
onChange( newLink );
},
[ currentLink ]
);

const contextValue = useMemo(
() => ( {
createSuggestion,
currentLink,
updateCurrentLink,
} ),
[ createSuggestion, currentLink, updateCurrentLink ]
);

return (
<BlockToolbarInlineEdit.Fill onClose={ close }>
<ToolbarLinkControlContext.Provider value={ contextValue }>
<SearchInput />
<SettingsMenu />
<Button onClick={ close }>{ __( 'Done' ) }</Button>
</ToolbarLinkControlContext.Provider>
</BlockToolbarInlineEdit.Fill>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* WordPress dependencies
*/
import {
Popover,
__experimentalInputControl as InputControl,
Icon,
Spinner,
} from '@wordpress/components';
import { link as linkIcon } from '@wordpress/icons';
import { useContext, useEffect, useRef, useState } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import LinkControlSearchResults from '../link-control/search-results';
import LinkControlSearchInput from '../link-control/search-input';
import useCreatePage from '../link-control/use-create-page';
import ToolbarLinkControlContext from './context';

const renderSuggestions = ( suggestionsProps ) => (
<Popover focusOnMount={ false } position="bottom">
<LinkControlSearchResults { ...suggestionsProps } />
</Popover>
);

export default function SearchInput() {
const [ hasFocus, setHasFocus ] = useState( false );
const { createSuggestion, currentLink, updateCurrentLink } = useContext(
ToolbarLinkControlContext
);

const { createPage, isCreatingPage, errorMessage } = useCreatePage(
createSuggestion
);

const { createErrorNotice } = useDispatch( 'core/notices' );
useEffect( () => {
if ( errorMessage ) {
createErrorNotice( errorMessage, { type: 'snackbar' } );
}
}, [ errorMessage ] );

const searchInputRef = useRef();

return (
<LinkControlSearchInput
ref={ searchInputRef }
currentLink={ currentLink }
placeholder="Start typing"
renderSuggestions={ renderSuggestions }
showSuggestions={ hasFocus }
value={ currentLink.url }
onCreateSuggestion={ createPage }
onChange={ ( url ) => {
updateCurrentLink( { url } );
} }
onSelect={ ( link ) => {
updateCurrentLink( link );
} }
showInitialSuggestions={ false }
allowDirectEntry
withCreateSuggestion
renderControl={ ( controlProps, inputProps, { isLoading } ) => {
return (
<InputControl
{ ...controlProps }
{ ...inputProps }
className="toolbar-link-control__input-control"
onChange={ ( value, { event } ) => {
inputProps.onChange( event );
} }
onFocus={ () => {
setHasFocus( true );
} }
onBlur={ () => {
setHasFocus( false );
} }
prefix={
<div className="toolbar-link-control__affix-wrapper">
<Icon icon={ linkIcon } />
</div>
}
suffix={
<div className="toolbar-link-control__affix-wrapper">
{ ( isCreatingPage || isLoading ) && (
<Spinner />
) }
</div>
}
/>
);
} }
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* WordPress dependencies
*/
import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components';
import {
chevronDown as arrowDownIcon,
check as checkIcon,
} from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import { useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
import ToolbarLinkControlContext from './context';

export default function SettingsMenu() {
const { currentLink, updateCurrentLink } = useContext(
ToolbarLinkControlContext
);
const { opensInNewTab, rel } = currentLink;
return (
<DropdownMenu
popoverProps={ { position: 'bottom' } }
className="link-option"
contentClassName="link-options__popover"
icon={ arrowDownIcon }
toggleProps={ {
label: __( 'Link options' ),
} }
>
{ ( { onClose } ) => (
<>
<MenuGroup>
<MenuItem
icon={ opensInNewTab && checkIcon }
onClick={ () => {
updateCurrentLink( {
opensInNewTab: ! opensInNewTab,
} );
} }
>
{ __( 'Open in new tab' ) }
</MenuItem>
<MenuItem
icon={ rel === 'nofollow' && checkIcon }
onClick={ () => {
updateCurrentLink( {
rel:
currentLink.rel === 'nofollow'
? ''
: 'nofollow',
} );
} }
>
{ __( 'Add nofollow attribute' ) }
</MenuItem>
</MenuGroup>
<MenuGroup>
<MenuItem
onClick={ () => {
updateCurrentLink( {
url: '',
} );
onClose();
} }
>
{ __( 'Remove link' ) }
</MenuItem>
</MenuGroup>
</>
) }
</DropdownMenu>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

// Specificity hack
.block-editor-block-toolbar.block-editor-block-toolbar {

[data-experimental-toolbar-item]:focus .components-input-control__container {
//box-shadow: inset 0 0 0 2px var(--wp-admin-theme-color);
outline: var(--wp-admin-theme-color) auto 2px;
}

.toolbar-link-control__input-group {
flex-grow: 1;
margin: 0;
height: 45px;
min-height: 0;
}

.toolbar-link-control__input-wrapper {
display: flex;

.components-base-control {
display: flex;
height: 100%;
}

.components-base-control__field {
display: flex;
margin: 0;
}

.components-input-control__input {
min-width: 280px;
padding-left: 0;
}
}

.toolbar-link-control__input-control {
height: 45px;
border: 1px solid #ccc;
flex-grow: 1;
align-self: center;
font-size: 13px;
}

.toolbar-link-control__affix-wrapper {
width: $grid-unit-50;
text-align: center;

.components-spinner {
margin: 0;
}
}
}

.block-editor-link-control__search-results-wrapper.is-toolbar-results {
margin-top: 0;
}
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export { default as __experimentalBlockNavigationEditor } from './block-navigati
export { default as __experimentalBlockNavigationTree } from './block-navigation/tree';
export { default as __experimentalBlockVariationPicker } from './block-variation-picker';
export { default as BlockVerticalAlignmentToolbar } from './block-vertical-alignment-toolbar';
export { default as BlockToolbarLinkControl } from './block-toolbar-link-control';
export { default as ButtonBlockerAppender } from './button-block-appender';
export { default as ColorPalette } from './color-palette';
export { default as ColorPaletteControl } from './color-palette/control';
Expand Down
Loading