Last active
February 3, 2024 14:38
-
-
Save andreilupu/be97e8e38e6caa29e1e9348b36125084 to your computer and use it in GitHub Desktop.
WordPress Post Title variation which prints a <li> instead of <h{n}>
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
// register a new post title variation. | |
// In a production environment I would register a new attribute called "isLiPostTitle" and use that instead of checking the className. | |
window.wp.blocks.registerBlockVariation( | |
'core/post-title', | |
{ | |
name: 'core/post-title-li', | |
title: 'Post title (li)', | |
attributes: { | |
className: 'is-li-title' | |
}, | |
} | |
); |
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
<?php | |
/** | |
* A function that imitates the render_callback of the Post Title block but with the output of <li> tag instead of <h{N}> | |
* Added to `render_block` that's why parameters are not the same. | |
* https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/post-title/index.php#L19-L59 | |
* | |
* @param $content | |
* @param $attrs | |
* @param $block | |
* @return string | |
*/ | |
function _custom_render_block_core_post_title( $content, $attrs, $block ) { | |
$attributes = $block->attributes; | |
if ( ! isset( $block->context['postId'] ) ) { | |
return ''; | |
} | |
/** | |
* The `$post` argument is intentionally omitted so that changes are reflected when previewing a post. | |
* See: https://github.com/WordPress/gutenberg/pull/37622#issuecomment-1000932816. | |
*/ | |
$title = get_the_title(); | |
if ( ! $title ) { | |
return ''; | |
} | |
$tag_name = 'h2'; | |
if ( isset( $attributes['level'] ) ) { | |
$tag_name = 'h' . $attributes['level']; | |
} | |
// @THIS! This is different from the original https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/post-title/index.php#L19-L59 | |
if ( isset( $attributes['className'] ) && $attributes['className'] === 'is-li-title' ) { | |
$tag_name = 'li'; | |
} else { | |
return $content; | |
} | |
if ( isset( $attributes['isLink'] ) && $attributes['isLink'] ) { | |
$rel = ! empty( $attributes['rel'] ) ? 'rel="' . esc_attr( $attributes['rel'] ) . '"' : ''; | |
$title = sprintf( '<a href="%1$s" target="%2$s" %3$s>%4$s</a>', esc_url( get_the_permalink( $block->context['postId'] ) ), esc_attr( $attributes['linkTarget'] ), $rel, $title ); | |
} | |
$classes = array(); | |
if ( isset( $attributes['textAlign'] ) ) { | |
$classes[] = 'has-text-align-' . $attributes['textAlign']; | |
} | |
if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) { | |
$classes[] = 'has-link-color'; | |
} | |
return sprintf( | |
'<%1$s %2$s>%3$s</%1$s>', | |
$tag_name, | |
implode( ' ', $classes ), | |
$title | |
); | |
} | |
add_filter('render_block', '_custom_render_block_core_post_title', 10, 3); |
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
import customPostTitleEdit from './4-PostTitleEdit.js'; | |
const { createHigherOrderComponent } = wp.compose; | |
const { InspectorControls } = wp.blockEditor; | |
const { PanelBody } = wp.components; | |
const withMyPluginControls = createHigherOrderComponent( ( BlockEdit ) => { | |
return ( props ) => { | |
const isLI = props.attributes['className'] === 'is-li-title'; | |
return ( | |
<> | |
{isLI && <PostTitleEdit { ...props } />} | |
{!isLI && <BlockEdit key="edit" { ...props } />} | |
<InspectorControls> | |
<PanelBody>My custom control</PanelBody> | |
</InspectorControls> | |
</> | |
); | |
}; | |
}, 'withMyPluginControls' ); | |
wp.hooks.addFilter( | |
'editor.BlockEdit', | |
'my-plugin/with-inspector-controls', | |
withMyPluginControls | |
); |
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
// A copy of Gutenberg's PostTitle Edit component https://github.com/WordPress/gutenberg/blob/408c1186af35e19ed040b8fa56765b1ac731969d/packages/block-library/src/post-title/edit.js#L28 | |
// This file would be the only one who needs webpack handling with @wordpress-scripts. (I could convert imports to window.wp but is out of scope for this excercise). | |
// At this point this is already a custom block just to replace a <h{n}> with a <li>. | |
import classnames from 'classnames'; | |
/** | |
* WordPress dependencies | |
*/ | |
import { useSelect } from '@wordpress/data'; | |
import { store as coreStore } from '@wordpress/core-data'; | |
import { | |
AlignmentControl, | |
BlockControls, | |
InspectorControls, | |
useBlockProps, | |
PlainText, | |
HeadingLevelDropdown, | |
useBlockEditingMode, | |
} from '@wordpress/block-editor'; | |
import { ToggleControl, TextControl, PanelBody } from '@wordpress/components'; | |
import { __ } from '@wordpress/i18n'; | |
import { createBlock, getDefaultBlockName } from '@wordpress/blocks'; | |
import { useEntityProp } from '@wordpress/core-data'; | |
/** | |
* Returns whether the current user can edit the given entity. | |
* | |
* @param {string} kind Entity kind. | |
* @param {string} name Entity name. | |
* @param {string} recordId Record's id. | |
*/ | |
export function useCanEditEntity( kind, name, recordId ) { | |
return useSelect( | |
( select ) => | |
select( coreStore ).canUserEditEntityRecord( kind, name, recordId ), | |
[ kind, name, recordId ] | |
); | |
} | |
export default function customPostTitleEdit( { | |
attributes: { level, textAlign, isLink, rel, linkTarget }, | |
setAttributes, | |
context: { postType, postId, queryId }, | |
insertBlocksAfter, | |
} ) { | |
const TagName = 'li'; | |
const isDescendentOfQueryLoop = Number.isFinite( queryId ); | |
/** | |
* Hack: useCanEditEntity may trigger an OPTIONS request to the REST API via the canUser resolver. | |
* However, when the Post Title is a descendant of a Query Loop block, the title cannot be edited. | |
* In order to avoid these unnecessary requests, we call the hook without | |
* the proper data, resulting in returning early without making them. | |
*/ | |
const userCanEdit = useCanEditEntity( | |
'postType', | |
! isDescendentOfQueryLoop && postType, | |
postId | |
); | |
const [ rawTitle = '', setTitle, fullTitle ] = useEntityProp( | |
'postType', | |
postType, | |
'title', | |
postId | |
); | |
const [ link ] = useEntityProp( 'postType', postType, 'link', postId ); | |
const onSplitAtEnd = () => { | |
insertBlocksAfter( createBlock( getDefaultBlockName() ) ); | |
}; | |
const blockProps = useBlockProps( { | |
className: classnames( { | |
[ `has-text-align-${ textAlign }` ]: textAlign, | |
} ), | |
} ); | |
const blockEditingMode = useBlockEditingMode(); | |
let titleElement = <TagName { ...blockProps }>{ __( 'Title' ) }</TagName>; | |
if ( postType && postId ) { | |
titleElement = userCanEdit ? ( | |
<PlainText | |
tagName={ TagName } | |
placeholder={ __( 'No Title' ) } | |
value={ rawTitle } | |
onChange={ setTitle } | |
__experimentalVersion={ 2 } | |
__unstableOnSplitAtEnd={ onSplitAtEnd } | |
{ ...blockProps } | |
/> | |
) : ( | |
<TagName | |
{ ...blockProps } | |
dangerouslySetInnerHTML={ { __html: fullTitle?.rendered } } | |
/> | |
); | |
} | |
if ( isLink && postType && postId ) { | |
titleElement = userCanEdit ? ( | |
<TagName { ...blockProps }> | |
<PlainText | |
tagName="a" | |
href={ link } | |
target={ linkTarget } | |
rel={ rel } | |
placeholder={ ! rawTitle.length ? __( 'No Title' ) : null } | |
value={ rawTitle } | |
onChange={ setTitle } | |
__experimentalVersion={ 2 } | |
__unstableOnSplitAtEnd={ onSplitAtEnd } | |
/> | |
</TagName> | |
) : ( | |
<TagName { ...blockProps }> | |
<a | |
href={ link } | |
target={ linkTarget } | |
rel={ rel } | |
onClick={ ( event ) => event.preventDefault() } | |
dangerouslySetInnerHTML={ { | |
__html: fullTitle?.rendered, | |
} } | |
/> | |
</TagName> | |
); | |
} | |
return ( | |
<> | |
{ blockEditingMode === 'default' && ( | |
<BlockControls group="block"> | |
<HeadingLevelDropdown | |
value={ level } | |
onChange={ ( newLevel ) => | |
setAttributes( { level: newLevel } ) | |
} | |
/> | |
<AlignmentControl | |
value={ textAlign } | |
onChange={ ( nextAlign ) => { | |
setAttributes( { textAlign: nextAlign } ); | |
} } | |
/> | |
</BlockControls> | |
) } | |
<InspectorControls> | |
<PanelBody title={ __( 'Settings' ) }> | |
<ToggleControl | |
__nextHasNoMarginBottom | |
label={ __( 'Make title a link' ) } | |
onChange={ () => setAttributes( { isLink: ! isLink } ) } | |
checked={ isLink } | |
/> | |
{ isLink && ( | |
<> | |
<ToggleControl | |
__nextHasNoMarginBottom | |
label={ __( 'Open in new tab' ) } | |
onChange={ ( value ) => | |
setAttributes( { | |
linkTarget: value ? '_blank' : '_self', | |
} ) | |
} | |
checked={ linkTarget === '_blank' } | |
/> | |
<TextControl | |
__nextHasNoMarginBottom | |
label={ __( 'Link rel' ) } | |
value={ rel } | |
onChange={ ( newRel ) => | |
setAttributes( { rel: newRel } ) | |
} | |
/> | |
</> | |
) } | |
</PanelBody> | |
</InspectorControls> | |
{ titleElement } | |
</> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment