Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
2 changes: 2 additions & 0 deletions includes/blocks/subtitle-block/block.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "newspack-block-theme/article-subtitle",
"title": "Article Subtitle",
"category": "newspack",
Expand Down
22 changes: 11 additions & 11 deletions includes/blocks/subtitle-block/class-subtitle-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ final class Subtitle_Block {
*/
public static function init() {
\add_action( 'init', [ __CLASS__, 'register_block_and_post_meta' ] );
\add_action( 'enqueue_block_editor_assets', [ __CLASS__, 'enqueue_block_editor_assets' ] );
\add_action( 'enqueue_block_assets', [ __CLASS__, 'enqueue_block_assets' ] );
}

/**
* Register the block.
*/
public static function register_block_and_post_meta() {
register_block_type_from_metadata(
__DIR__ . '/block.json',
__DIR__,
[
'render_callback' => [ __CLASS__, 'render_block' ],
]
Expand All @@ -52,27 +52,27 @@ public static function register_block_and_post_meta() {
public static function render_block() {
$post_subtitle = get_post_meta( get_the_ID(), self::POST_META_NAME, true );
$wrapper_attributes = get_block_wrapper_attributes();
return sprintf( '<p %1$s>%2$s</p>', $wrapper_attributes, $post_subtitle );
return sprintf( '<p %1$s>%2$s</p>', $wrapper_attributes, esc_html( $post_subtitle ) );
}

/**
* Enqueue block editor ad suppression assets for any post type considered
* "viewable".
* Enqueue block editor subtitle assets for the appropriate editor context.
*/
public static function enqueue_block_editor_assets() {
public static function enqueue_block_assets() {
if ( ! \wp_should_load_block_editor_scripts_and_styles() ) {
return;
}

$script_data = [
'post_meta_name' => self::POST_META_NAME,
];

global $pagenow;
if ( $pagenow === 'site-editor.php' ) {
$handle = 'newspack-block-theme-subtitle-block-site-editor';
\wp_enqueue_script( $handle, \get_theme_file_uri( 'dist/subtitle-block-site-editor.js' ), [], NEWSPACK_BLOCK_THEME_VERSION, true );
\wp_enqueue_script( $handle, \get_theme_file_uri( 'dist/subtitle-block-site-editor.js' ), [ 'wp-block-editor' ], NEWSPACK_BLOCK_THEME_VERSION, true );
\wp_localize_script( $handle, 'newspack_block_theme_subtitle_block', $script_data );
}

$post_type = \get_current_screen()->post_type;
if ( $post_type === 'post' ) {
} elseif ( \get_current_screen() && \get_current_screen()->post_type === 'post' ) {
$handle = 'newspack-block-theme-subtitle-block-post-editor';
\wp_enqueue_script( $handle, \get_theme_file_uri( 'dist/subtitle-block-post-editor.js' ), [], NEWSPACK_BLOCK_THEME_VERSION, true );
\wp_localize_script( $handle, 'newspack_block_theme_subtitle_block', $script_data );
Expand Down
87 changes: 67 additions & 20 deletions includes/blocks/subtitle-block/post-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,36 @@
*/
import { registerPlugin } from '@wordpress/plugins';
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
import { useEffect, useCallback, useRef } from '@wordpress/element';

const META_FIELD_NAME = newspack_block_theme_subtitle_block.post_meta_name;

const SUBTITLE_ID = 'newspack-post-subtitle-element';
const SUBTITLE_STYLE_ID = 'newspack-post-subtitle-element-style';

const appendSubtitleToTitleDOMElement = ( subtitle, callback ) => {
const titleWrapperEl = document.querySelector( '.edit-post-visual-editor__post-title-wrapper' );
/**
* Get the correct document for the editor canvas.
* In iframe mode, the editor content is inside an iframe with name="editor-canvas".
* In non-iframe mode, falls back to the admin document.
*/
const getEditorCanvas = () => {
const iframe = document.querySelector( 'iframe[name="editor-canvas"]' );
if ( iframe?.contentDocument ) {
return iframe.contentDocument;
}
return document;
};

const appendSubtitleToTitleDOMElement = ( subtitle, editorDoc, callback ) => {
const titleWrapperEl = editorDoc.querySelector( '.edit-post-visual-editor__post-title-wrapper' );

if ( titleWrapperEl && typeof subtitle === 'string' ) {
let subtitleEl = document.getElementById( SUBTITLE_ID );
let subtitleEl = editorDoc.getElementById( SUBTITLE_ID );
const titleParent = titleWrapperEl.parentNode;

if ( ! document.getElementById( SUBTITLE_STYLE_ID ) ) {
const style = document.createElement( 'style' );
if ( ! editorDoc.getElementById( SUBTITLE_STYLE_ID ) ) {
const style = editorDoc.createElement( 'style' );
style.id = SUBTITLE_STYLE_ID;
style.innerHTML = `
#${ SUBTITLE_ID } {
font-style: italic;
Expand All @@ -33,19 +47,22 @@ const appendSubtitleToTitleDOMElement = ( subtitle, callback ) => {
padding-right: var(--wp--preset--spacing--30);
}
`;
document.head.appendChild( style );
editorDoc.head.appendChild( style );
}

if ( ! subtitleEl ) {
subtitleEl = document.createElement( 'div' );
subtitleEl = editorDoc.createElement( 'div' );
subtitleEl.setAttribute( 'contenteditable', 'plaintext-only' );
subtitleEl.addEventListener( 'input', () => {
callback( subtitleEl.innerHTML );
callback( subtitleEl.textContent );
} );
subtitleEl.id = SUBTITLE_ID;
titleParent.insertBefore( subtitleEl, titleWrapperEl.nextSibling );
}
subtitleEl.innerHTML = subtitle;
// Only update textContent if it differs, to avoid frustrating fast typists.
if ( subtitleEl.textContent !== subtitle ) {
subtitleEl.textContent = subtitle;
}
}
};

Expand All @@ -56,17 +73,47 @@ const appendSubtitleToTitleDOMElement = ( subtitle, callback ) => {
*/
const NewspackSubtitlePanel = () => {
const subtitle = useSelect( select => select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ META_FIELD_NAME ] );
const dispatch = useDispatch();
const saveSubtitle = updatedSubtitle => {
dispatch( 'core/editor' ).editPost( {
meta: {
[ META_FIELD_NAME ]: updatedSubtitle,
},
} );
};
const { editPost } = useDispatch( 'core/editor' );
const saveSubtitle = useCallback(
updatedSubtitle => {
editPost( {
meta: {
[ META_FIELD_NAME ]: updatedSubtitle,
},
} );
},
[ editPost ]
);
// Mount effect: poll for canvas, then create element.
const timeoutRef = useRef();
useEffect( () => {
appendSubtitleToTitleDOMElement( subtitle, saveSubtitle );
}, [] );
let retryCount = 0;
const maxRetries = 50; // 5 seconds at 100ms intervals.
const tryAppend = () => {
const editorDoc = getEditorCanvas();
const titleWrapperEl = editorDoc.querySelector( '.edit-post-visual-editor__post-title-wrapper' );
if ( titleWrapperEl ) {
appendSubtitleToTitleDOMElement( subtitle, editorDoc, saveSubtitle );
} else if ( retryCount < maxRetries ) {
retryCount++;
timeoutRef.current = setTimeout( tryAppend, 100 );
}
};
tryAppend();
return () => {
clearTimeout( timeoutRef.current );
};
}, [] ); // eslint-disable-line react-hooks/exhaustive-deps

// Sync effect: update element when subtitle changes.
// Extracted from appendSubtitleToTitleDOMElement() above.
useEffect( () => {
const editorDoc = getEditorCanvas();
const subtitleEl = editorDoc.getElementById( SUBTITLE_ID );
if ( subtitleEl && subtitleEl.textContent !== subtitle ) {
subtitleEl.textContent = subtitle;
}
}, [ subtitle ] );
};

registerPlugin( 'plugin-document-setting-panel-newspack-subtitle', {
Expand Down
5 changes: 4 additions & 1 deletion includes/blocks/subtitle-block/site-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
* WordPress dependencies
*/
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
import { Icon, listView } from '@wordpress/icons';
import { useEntityProp } from '@wordpress/core-data';

import metadata from './block.json';

const EditComponent = ( { context: { postType, postId } } ) => {
const blockProps = useBlockProps();
const [ postMeta = {} ] = useEntityProp( 'postType', postType, 'meta', postId );
return postMeta[ newspack_block_theme_subtitle_block.post_meta_name ] || __( 'Article subtitle', 'newspack-block-theme' );
const subtitle = postMeta[ newspack_block_theme_subtitle_block.post_meta_name ] || __( 'Article subtitle', 'newspack-block-theme' );
return <p { ...blockProps }>{ subtitle }</p>;
};

const blockData = {
Expand Down
7 changes: 5 additions & 2 deletions includes/class-core.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ final class Core {
public static function init() {
\add_action( 'after_setup_theme', [ __CLASS__, 'theme_support' ] );
\add_action( 'wp_enqueue_scripts', [ __CLASS__, 'theme_styles' ] );
\add_action( 'enqueue_block_editor_assets', [ __CLASS__, 'editor_scripts' ] );
\add_action( 'enqueue_block_assets', [ __CLASS__, 'enqueue_block_assets' ] );
\add_filter( 'body_class', [ __CLASS__, 'body_class' ] );
\add_filter( 'block_type_metadata', [ __CLASS__, 'block_variations' ] );
}
Expand Down Expand Up @@ -78,7 +78,10 @@ public static function theme_styles() {
/**
* Enqueue editor scripts.
*/
public static function editor_scripts() {
public static function enqueue_block_assets() {
if ( ! wp_should_load_block_editor_scripts_and_styles() ) {
return;
}
// Enqueue editor JavaScript.
wp_enqueue_script( 'editor-script', get_theme_file_uri( '/dist/editor.js' ), array( 'wp-blocks', 'wp-dom' ), wp_get_theme()->get( 'Version' ), true );
}
Expand Down
Loading