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

Display inserter popover in offcanvas UI #46013

Merged
merged 22 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0a42349
Add new prop to control 4th arg to insertBlock
getdave Nov 23, 2022
35267a3
Don’t select block on insertion from offcanvas appender
getdave Nov 23, 2022
ec85f0f
Make appender
getdave Nov 24, 2022
83afff5
Provide data on inserted block in Inserter block callback
getdave Nov 24, 2022
c12cf1e
Extract updater function from Nav Link block
getdave Nov 25, 2022
e317c95
Extract standalone Link UI component from Nav Link block
getdave Nov 25, 2022
c3345fc
Rename Link UI props
getdave Nov 25, 2022
61fe222
Remove need for replaceBlock prop and colocate with component
getdave Nov 25, 2022
54ae328
Rename more props
getdave Nov 25, 2022
23297bd
Remove deps on attributes in favour of generic updater prop
getdave Nov 25, 2022
dc69e51
Colocate transforms control with Link UI component
getdave Nov 25, 2022
7bef737
Copy Link UI from Nav Link Block
getdave Nov 25, 2022
53c2824
Experiment with including Link UI in offcanvas
getdave Nov 25, 2022
6c4fdce
Select inserted link attributes to dynamically update UI
getdave Nov 25, 2022
63322ce
Pass title and new tab props to Link UI
getdave Nov 28, 2022
c66d778
Remove create suggestion and resulting dep on Core Data package
getdave Nov 28, 2022
450931e
Use WP version of escape html over lodash dep
getdave Nov 28, 2022
0a71370
Update packages/block-editor/src/components/off-canvas-editor/link-ui.js
scruffian Nov 28, 2022
defbdba
Separately import to fix unit tests
getdave Nov 28, 2022
983f4e2
Fix lint
getdave Nov 28, 2022
383faa3
Use the correct import!
getdave Nov 28, 2022
13eb8e3
Close the link appender
scruffian Nov 29, 2022
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
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If undefined this will default to true meaning that the blocks will continue to be focused in other contexts.

);

if ( onSelectOrClose ) {
onSelectOrClose();
onSelectOrClose( {
insertedBlockId: blockToInsert?.clientId,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what provides us with access to information about the block that was just inserted.

} );
}

const message = sprintf(
Expand Down
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';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This holds a lot of complex logic so we must borrow it from the implementation in Navigation Link block


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 ]
);
Comment on lines +34 to +43
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the inserted block ID changes we need to refetch info about the block attributes so we can update the Link UI.


const { updateBlockAttributes } = useDispatch( blockEditorStore );

const setAttributes =
( insertedBlockClientId ) => ( _updatedAttributes ) => {
updateBlockAttributes( insertedBlockClientId, _updatedAttributes );
};
Comment on lines +47 to +50
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We provide a customised implementation of setAttributes to the updateAttributes method. This is because in this context we do not have access to the block's setAttributes method. Therefore we must dispatch attribute updates directly to the store.

The function is curried in order that we can partially apply the blockID in advance.


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

/**
scruffian marked this conversation as resolved.
Show resolved Hide resolved
* 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',
];
Comment on lines +76 to +80
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will track this in #46166


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