-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Open LinkUI popover after appending link from Navigation side block i…
…nserter The behavior when adding a link from the appender button on the OffCanvasEditor is to open a link control popover. This commit copies over several files from the OffCanvasEditor that was required for that to work in the ListView. The behavior is buggy, IMO, but it does match what is in trunk.
- Loading branch information
Showing
4 changed files
with
379 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 167 additions & 0 deletions
167
packages/block-editor/src/components/list-view/link-ui.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
// Note: this file is copied directly from packages/block-library/src/navigation-link/link-ui.js | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; | ||
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/page-list', | ||
'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 ) => { | ||
return ( | ||
<Button | ||
key={ `transform-${ item.name }` } | ||
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 ) { | ||
const { label, url, opensInNewTab, type, kind } = props.link; | ||
const link = { | ||
url, | ||
opensInNewTab, | ||
title: label && stripHTML( label ), | ||
}; | ||
|
||
return ( | ||
<Popover | ||
placement="bottom" | ||
onClose={ props.onClose } | ||
anchor={ props.anchor } | ||
shift | ||
> | ||
<LinkControl | ||
hasTextControl | ||
hasRichPreviews | ||
className={ props.className } | ||
value={ link } | ||
showInitialSuggestions={ true } | ||
withCreateSuggestion={ props.hasCreateSuggestion } | ||
noDirectEntry={ !! type } | ||
noURLSuggestion={ !! type } | ||
suggestionsQuery={ getSuggestionsQuery( type, kind ) } | ||
onChange={ props.onChange } | ||
onRemove={ props.onRemove } | ||
onCancel={ props.onCancel } | ||
renderControlBottom={ | ||
! url | ||
? () => ( | ||
<LinkControlTransforms | ||
clientId={ props.clientId } | ||
/> | ||
) | ||
: null | ||
} | ||
/> | ||
</Popover> | ||
); | ||
} |
99 changes: 99 additions & 0 deletions
99
packages/block-editor/src/components/list-view/update-attributes.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { escapeHTML } from '@wordpress/escape-html'; | ||
import { safeDecodeURI } from '@wordpress/url'; | ||
|
||
/** | ||
* @typedef {'post-type'|'custom'|'taxonomy'|'post-type-archive'} WPNavigationLinkKind | ||
*/ | ||
/** | ||
* Navigation Link Block Attributes | ||
* | ||
* @typedef {Object} WPNavigationLinkBlockAttributes | ||
* | ||
* @property {string} [label] Link text. | ||
* @property {WPNavigationLinkKind} [kind] Kind is used to differentiate between term and post ids to check post draft status. | ||
* @property {string} [type] The type such as post, page, tag, category and other custom types. | ||
* @property {string} [rel] The relationship of the linked URL. | ||
* @property {number} [id] A post or term id. | ||
* @property {boolean} [opensInNewTab] Sets link target to _blank when true. | ||
* @property {string} [url] Link href. | ||
* @property {string} [title] Link title attribute. | ||
*/ | ||
/** | ||
* Link Control onChange handler that updates block attributes when a setting is changed. | ||
* | ||
* @param {Object} updatedValue New block attributes to update. | ||
* @param {Function} setAttributes Block attribute update function. | ||
* @param {WPNavigationLinkBlockAttributes} blockAttributes Current block attributes. | ||
* | ||
*/ | ||
|
||
export const updateAttributes = ( | ||
updatedValue = {}, | ||
setAttributes, | ||
blockAttributes = {} | ||
) => { | ||
const { | ||
label: originalLabel = '', | ||
kind: originalKind = '', | ||
type: originalType = '', | ||
} = blockAttributes; | ||
|
||
const { | ||
title: newLabel = '', // the title of any provided Post. | ||
url: newUrl = '', | ||
opensInNewTab, | ||
id, | ||
kind: newKind = originalKind, | ||
type: newType = originalType, | ||
} = updatedValue; | ||
|
||
const newLabelWithoutHttp = newLabel.replace( /http(s?):\/\//gi, '' ); | ||
const newUrlWithoutHttp = newUrl.replace( /http(s?):\/\//gi, '' ); | ||
|
||
const useNewLabel = | ||
newLabel && | ||
newLabel !== originalLabel && | ||
// LinkControl without the title field relies | ||
// on the check below. Specifically, it assumes that | ||
// the URL is the same as a title. | ||
// This logic a) looks suspicious and b) should really | ||
// live in the LinkControl and not here. It's a great | ||
// candidate for future refactoring. | ||
newLabelWithoutHttp !== newUrlWithoutHttp; | ||
|
||
// Unfortunately this causes the escaping model to be inverted. | ||
// The escaped content is stored in the block attributes (and ultimately in the database), | ||
// and then the raw data is "recovered" when outputting into the DOM. | ||
// It would be preferable to store the **raw** data in the block attributes and escape it in JS. | ||
// Why? Because there isn't one way to escape data. Depending on the context, you need to do | ||
// different transforms. It doesn't make sense to me to choose one of them for the purposes of storage. | ||
// See also: | ||
// - https://github.com/WordPress/gutenberg/pull/41063 | ||
// - https://github.com/WordPress/gutenberg/pull/18617. | ||
const label = useNewLabel | ||
? escapeHTML( newLabel ) | ||
: originalLabel || escapeHTML( newUrlWithoutHttp ); | ||
|
||
// 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( '-', '_' ); | ||
|
||
const isBuiltInType = | ||
[ 'post', 'page', 'tag', 'category' ].indexOf( type ) > -1; | ||
|
||
const isCustomLink = | ||
( ! newKind && ! isBuiltInType ) || newKind === 'custom'; | ||
const kind = isCustomLink ? 'custom' : newKind; | ||
|
||
setAttributes( { | ||
// Passed `url` may already be encoded. To prevent double encoding, decodeURI is executed to revert to the original string. | ||
...( newUrl && { url: encodeURI( safeDecodeURI( newUrl ) ) } ), | ||
...( label && { label } ), | ||
...( undefined !== opensInNewTab && { opensInNewTab } ), | ||
...( id && Number.isInteger( id ) && { id } ), | ||
...( kind && { kind } ), | ||
...( type && type !== 'URL' && { type } ), | ||
} ); | ||
}; |
Oops, something went wrong.