Skip to content

Commit

Permalink
Display inserter popover in Nav Offcanvas experiment UI (WordPress#46013
Browse files Browse the repository at this point in the history
)

* Add new prop to control 4th arg to insertBlock

* Don’t select block on insertion from offcanvas appender

* Make appender

* Provide data on inserted block in Inserter block callback

* Extract updater function from Nav Link block

This will make this easier to refactor in future

* Extract standalone Link UI component from Nav Link block

* Rename Link UI props

* Remove need for replaceBlock prop and colocate with component

* Rename more props

* Remove deps on attributes in favour of generic updater prop

* Colocate transforms control with Link UI component

* Copy Link UI from Nav Link Block

* Experiment with including Link UI in offcanvas

* Select inserted link attributes to dynamically update UI

* Pass title and new tab props to Link UI

* Remove create suggestion and resulting dep on Core Data package

* Use WP version of escape html over lodash dep

* Update packages/block-editor/src/components/off-canvas-editor/link-ui.js

Co-authored-by: Dave Smith <getdavemail@gmail.com>

* Separately import to fix unit tests

* Fix lint

* Use the correct import!

* Close the link appender

Co-authored-by: Ben Dwyer <ben@scruffian.com>
  • Loading branch information
2 people authored and mpkelly committed Dec 7, 2022
1 parent 7f276b2 commit f76c2dd
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 6 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@wordpress/deprecated": "file:../deprecated",
"@wordpress/dom": "file:../dom",
"@wordpress/element": "file:../element",
"@wordpress/escape-html": "file:../escape-html",
"@wordpress/hooks": "file:../hooks",
"@wordpress/html-entities": "file:../html-entities",
"@wordpress/i18n": "file:../i18n",
Expand Down
12 changes: 10 additions & 2 deletions packages/block-editor/src/components/inserter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ export default compose( [
allowedBlockType,
directInsertBlock,
onSelectOrClose,
selectBlockOnInsert,
} = ownProps;

if ( ! hasSingleBlockType && ! directInsertBlock ) {
Expand Down Expand Up @@ -370,10 +371,17 @@ export default compose( [
blockToInsert = createBlock( allowedBlockType.name );
}

insertBlock( blockToInsert, getInsertionIndex(), rootClientId );
insertBlock(
blockToInsert,
getInsertionIndex(),
rootClientId,
selectBlockOnInsert
);

if ( onSelectOrClose ) {
onSelectOrClose();
onSelectOrClose( {
insertedBlockId: blockToInsert?.clientId,
} );
}

const message = sprintf(
Expand Down
65 changes: 61 additions & 4 deletions packages/block-editor/src/components/off-canvas-editor/appender.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { forwardRef } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { forwardRef, useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import Inserter from '../inserter';
import { LinkUI } from './link-ui';
import { updateAttributes } from './update-attributes';

export const Appender = forwardRef( ( props, ref ) => {
const [ insertedBlock, setInsertedBlock ] = useState();

const { hideInserter, clientId } = useSelect( ( select ) => {
const {
getTemplateLock,
Expand All @@ -27,18 +31,71 @@ export const Appender = forwardRef( ( props, ref ) => {
};
}, [] );

const { insertedBlockAttributes } = useSelect(
( select ) => {
const { getBlockAttributes } = select( blockEditorStore );

return {
insertedBlockAttributes: getBlockAttributes( insertedBlock ),
};
},
[ insertedBlock ]
);

const { updateBlockAttributes } = useDispatch( blockEditorStore );

const setAttributes =
( insertedBlockClientId ) => ( _updatedAttributes ) => {
updateBlockAttributes( insertedBlockClientId, _updatedAttributes );
};

let maybeLinkUI;

if ( insertedBlock ) {
const link = {
url: insertedBlockAttributes.url,
opensInNewTab: insertedBlockAttributes.opensInNewTab,
title: insertedBlockAttributes.label,
};
maybeLinkUI = (
<LinkUI
clientId={ insertedBlock }
value={ link }
linkAttributes={ {
type: insertedBlockAttributes.type,
url: insertedBlockAttributes.url,
kind: insertedBlockAttributes.kind,
} }
onClose={ () => setInsertedBlock( null ) }
hasCreateSuggestion={ false }
onChange={ ( updatedValue ) => {
updateAttributes(
updatedValue,
setAttributes( insertedBlock ),
insertedBlockAttributes
);
setInsertedBlock( null );
} }
/>
);
}

if ( hideInserter ) {
return null;
}

return (
<div className="offcanvas-editor__appender">
{ maybeLinkUI }
<Inserter
ref={ ref }
rootClientId={ clientId }
position="bottom right"
isAppender
__experimentalIsQuick
isAppender={ true }
selectBlockOnInsert={ false }
onSelectOrClose={ ( { insertedBlockId } ) => {
setInsertedBlock( insertedBlockId );
} }
{ ...props }
/>
</div>
Expand Down
160 changes: 160 additions & 0 deletions packages/block-editor/src/components/off-canvas-editor/link-ui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Note: this file is copied directly from packages/block-library/src/navigation-link/link-ui.js

/**
* WordPress dependencies
*/
import { Popover, Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { switchToBlockType } from '@wordpress/blocks';
import { useSelect, useDispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import LinkControl from '../link-control';
import BlockIcon from '../block-icon';

/**
* Given the Link block's type attribute, return the query params to give to
* /wp/v2/search.
*
* @param {string} type Link block's type attribute.
* @param {string} kind Link block's entity of kind (post-type|taxonomy)
* @return {{ type?: string, subtype?: string }} Search query params.
*/
export function getSuggestionsQuery( type, kind ) {
switch ( type ) {
case 'post':
case 'page':
return { type: 'post', subtype: type };
case 'category':
return { type: 'term', subtype: 'category' };
case 'tag':
return { type: 'term', subtype: 'post_tag' };
case 'post_format':
return { type: 'post-format' };
default:
if ( kind === 'taxonomy' ) {
return { type: 'term', subtype: type };
}
if ( kind === 'post-type' ) {
return { type: 'post', subtype: type };
}
return {};
}
}

/**
* Add transforms to Link Control
*
* @param {Object} props Component props.
* @param {string} props.clientId Block client ID.
*/
function LinkControlTransforms( { clientId } ) {
const { getBlock, blockTransforms } = useSelect(
( select ) => {
const {
getBlock: _getBlock,
getBlockRootClientId,
getBlockTransformItems,
} = select( blockEditorStore );

return {
getBlock: _getBlock,
blockTransforms: getBlockTransformItems(
_getBlock( clientId ),
getBlockRootClientId( clientId )
),
};
},
[ clientId ]
);

const { replaceBlock } = useDispatch( blockEditorStore );

const featuredBlocks = [
'core/site-logo',
'core/social-links',
'core/search',
];

const transforms = blockTransforms.filter( ( item ) => {
return featuredBlocks.includes( item?.name );
} );

if ( ! transforms?.length ) {
return null;
}

if ( ! clientId ) {
return null;
}

return (
<div className="link-control-transform">
<h3 className="link-control-transform__subheading">
{ __( 'Transform' ) }
</h3>
<div className="link-control-transform__items">
{ transforms.map( ( item, index ) => {
return (
<Button
key={ `transform-${ index }` }
onClick={ () =>
replaceBlock(
clientId,
switchToBlockType(
getBlock( clientId ),
item.name
)
)
}
className="link-control-transform__item"
>
<BlockIcon icon={ item.icon } />
{ item.title }
</Button>
);
} ) }
</div>
</div>
);
}

export function LinkUI( props ) {
return (
<Popover
placement="bottom"
onClose={ props?.onClose }
anchor={ props?.anchor }
shift
>
<LinkControl
hasTextControl
hasRichPreviews
className="wp-block-navigation-link__inline-link-input"
value={ props?.value }
showInitialSuggestions={ true }
withCreateSuggestion={ props?.hasCreateSuggestion }
noDirectEntry={ !! props?.linkAttributes?.type }
noURLSuggestion={ !! props?.linkAttributes?.type }
suggestionsQuery={ getSuggestionsQuery(
props?.linkAttributes?.type,
props?.linkAttributes?.kind
) }
onChange={ props.onChange }
onRemove={ props.onRemove }
renderControlBottom={
! props?.linkAttributes?.url
? () => (
<LinkControlTransforms
clientId={ props?.clientId }
/>
)
: null
}
/>
</Popover>
);
}
Loading

0 comments on commit f76c2dd

Please sign in to comment.