Вопрос или проблема
Я написал пользовательский React-компонент “Gutenberg”, чтобы использовать его везде с React, просто используя <Gutenberg value={blocks} onChange={setBlocks} />
Была проблема с блоками, никакие пользовательские блоки не отображались в библиотеке блоков. Я решил эту проблему, используя встроенные скрипты, которые вы можете увидеть ниже
Сейчас у меня аналогичная проблема с шаблонами блоков, я не могу видеть никаких шаблонов, но они видны на обычной странице редактирования постов.
Вот мой код:
import React, {useState, useEffect, useRef} from 'react';
import {SlotFillProvider, Button, Popover} from '@wordpress/components';
import {
BlockEditorProvider,
BlockList,
// @ts-ignore
BlockTools,
BlockInspector,
WritingFlow,
ObserveTyping,
// @ts-ignore
__experimentalLibrary as BlockLibrary,
// @ts-ignore
__experimentalListView as ListView,
Inserter,
BlockToolbar,
// @ts-ignore
TabbedSidebar,
} from '@wordpress/block-editor';
import { registerCoreBlocks } from '@wordpress/block-library'; // Ensure core blocks are registered
// @ts-ignore
import { ShortcutProvider } from '@wordpress/keyboard-shortcuts';
// @ts-ignore
import { InterfaceSkeleton } from '@wordpress/interface';
import { redo as redoIcon, undo as undoIcon, drawerLeft, drawerRight, plus, closeSmall, listView } from '@wordpress/icons';
import { __, isRTL } from '@wordpress/i18n';
import {Icon} from "@components";
import {useAppContext} from "@hooks";
registerCoreBlocks();
interface GutenbergProps {
value: string;
onChange: (value: string) => void
toggleFullScreen?: () => void
isFullScreen?: boolean
label?: string
}
const Gutenberg: React.FC<GutenbergProps> = ({
value,
onChange,
toggleFullScreen = () => {},
isFullScreen = false,
label = __('Block Editor', 'levcharity-editor')
}) => {
// @ts-ignore
const [blocks, setBlocks] = useState(wp.blocks.parse(value || '<!-- wp:paragraph --> <p></p> <!-- /wp:paragraph -->'));
const [history, setHistory] = useState<{ past: any[], future: any[] }>({ past: [], future: [] });
const [isRightSidebarOpen, setIsRightSidebarOpen] = useState(false);
const [isListViewOpen, setIsListViewOpen] = useState(false);
const [isInserterOpen, setIsInserterOpen] = useState(false);
const [localValue, setLocalValue] = useState(value || '<!-- wp:paragraph --> <p></p> <!-- /wp:paragraph -->');
const [ dropZoneElement, setDropZoneElement ] = useState( null );
const {data} = useAppContext();
useEffect(() => {
if (value && localValue !== value) {
// @ts-ignore
setBlocks(wp.blocks.parse(value || '<!-- wp:paragraph --> <p></p> <!-- /wp:paragraph -->')); // Parse initial value into blocks
}
}, [value]);
const handleChange = (newBlocks: any[]) => {
setHistory({
past: [...history.past, blocks],
future: [],
});
setBlocks(newBlocks);
// @ts-ignore
setLocalValue(wp.blocks.serialize(newBlocks)); // Serialize blocks on change
// @ts-ignore
onChange(wp.blocks.serialize(newBlocks)); // Serialize blocks on change
};
const undo = () => {
if (history.past.length === 0) return;
const previous = history.past[history.past.length - 1];
const newPast = history.past.slice(0, history.past.length - 1);
setHistory({
past: newPast,
future: [blocks, ...history.future],
});
setBlocks(previous);
};
const redo = () => {
if (history.future.length === 0) return;
const next = history.future[0];
const newFuture = history.future.slice(1);
setHistory({
past: [...history.past, blocks],
future: newFuture,
});
setBlocks(next);
};
const canUndo = history.past.length > 0;
const canRedo = history.future.length > 0;
const toggleRightSidebar = () => {
setIsRightSidebarOpen(!isRightSidebarOpen);
};
const toggleListView = ()=> {
setIsListViewOpen(!isListViewOpen);
if (!isListViewOpen && isInserterOpen) {
setIsInserterOpen(false);
}
}
const toggleInserter = ()=> {
setIsInserterOpen(!isInserterOpen);
if (!isInserterOpen && setIsListViewOpen) {
setIsListViewOpen(false);
}
}
// @ts-ignore
// @ts-ignore
// @ts-ignore
return (
<div className={"levcharity-gutenberg-editor" + (isListViewOpen || isInserterOpen ? ' secondary-sidebar-open' : '') + (isRightSidebarOpen ? ' sidebar-open' : '')}>
<ShortcutProvider>
<SlotFillProvider>
<BlockEditorProvider
value={blocks}
onInput={handleChange}
onChange={handleChange}
settings={{
maxWidth: 1120,
}}
>
<InterfaceSkeleton
header={
<div className="block-editor-header">
<div className="left-side-buttons header-buttons">
<Button onClick={toggleInserter} variant={'primary'} isPressed={isInserterOpen} icon={ !isInserterOpen ? plus : closeSmall }></Button>
<div className={"history-buttons header-buttons"}>
<Button onClick={canUndo ? undo : undefined} disabled={!canUndo} icon={ ! isRTL() ? undoIcon : redoIcon }></Button>
<Button onClick={canRedo ? redo : undefined} disabled={!canRedo} icon={ ! isRTL() ? redoIcon : undoIcon }></Button>
</div>
<Button onClick={toggleListView} isPressed={isListViewOpen} icon={ listView }></Button>
</div>
<h2 className="header-heading levcharity_heading">{label}</h2>
<div className="right-side-buttons header-buttons">
<Button onClick={toggleRightSidebar} isPressed={isRightSidebarOpen} icon={ isRTL() ? drawerLeft : drawerRight }></Button>
{toggleFullScreen && <Button onClick={toggleFullScreen} isPressed={isFullScreen} icon={ !isFullScreen ? <Icon name={"FullscreenOutlined"} /> : <Icon name={"FullscreenExitOutlined"} /> }></Button>}
</div>
</div>
}
secondarySidebar={
<>
{isListViewOpen && <div className="editor-list-view-sidebar__list-view-container" style={{minWidth: 350, maxWidth: 350}}>
<div className="editor-list-view-sidebar__list-view-panel-content">
<ListView
dropZoneElement={dropZoneElement}
/>
</div>
</div>}
{isInserterOpen && <BlockLibrary rootClientId={null} onClose={() => setIsInserterOpen(false)}/>}
</>
}
sidebar={isRightSidebarOpen && <div style={{minWidth: 350, maxWidth: 350}}><BlockInspector/></div>}
content={
<div className="editor-body">
<BlockTools>
<div className="editor-styles-wrapper" style={{background: 'white'}}>
<WritingFlow>
<ObserveTyping>
{/* @ts-ignore */}
<BlockList blocks={blocks}/>
</ObserveTyping>
</WritingFlow>
</div>
</BlockTools>
</div>
}
/>
{/* @ts-ignore */}
<Popover.Slot />
</BlockEditorProvider>
</SlotFillProvider>
</ShortcutProvider>
</div>
);
}
export default Gutenberg;
class AssetsManager extends BaseManager
{
protected function addActions(): void
{
add_action('admin_enqueue_scripts', [ $this, 'gutenbergSettings' ], 9);
}
public function gutenbergSettings(): void
{
if (isset($_GET['page']) && $_GET['page'] === 'levcharity-editor' && apply_filters('levcharity_editor_require_gutenberg', false)) {
$post_id = Helper::getCurrentState('id');
$post = get_post($post_id);
if (Helper::getCurrentState('type') === 'post') {
$block_editor_context = new \WP_Block_Editor_Context( array( 'post' => $post_id ) );
} else {
$block_editor_context = new \WP_Block_Editor_Context( array( 'name' => 'core/edit-site' ) );
}
$custom_settings = array(
'siteUrl' => site_url(),
'postsPerPage' => get_option( 'posts_per_page' ),
'styles' => get_block_editor_theme_styles(),
'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(),
'supportsLayout' => wp_theme_has_theme_json(),
'supportsTemplatePartsMode' => ! wp_is_block_theme() && current_theme_supports( 'block-template-parts' ),
);
$custom_settings['__experimentalAdditionalBlockPatterns'] = \WP_Block_Patterns_Registry::get_instance()->get_all_registered( true );
$custom_settings['__experimentalAdditionalBlockPatternCategories'] = \WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered( true );
$editor_settings = get_block_editor_settings( $custom_settings, $block_editor_context );
wp_add_inline_script(
'wp-blocks',
'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');'
);
if (Helper::getCurrentState('type') === 'post') {
wp_add_inline_script(
'wp-blocks',
sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( get_block_categories( $post ) ) ),
'after'
);
} else {
wp_add_inline_script(
'wp-blocks',
sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( isset( $editor_settings['blockCategories'] ) ? $editor_settings['blockCategories'] : array() ) ),
'after'
);
}
$block_registry = \WP_Block_Type_Registry::get_instance();
foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
// Front-end and editor styles.
foreach ( $block_type->style_handles as $style_handle ) {
wp_enqueue_style( $style_handle );
}
// Front-end and editor scripts.
foreach ( $block_type->script_handles as $script_handle ) {
wp_enqueue_script( $script_handle );
}
// Editor styles.
foreach ( $block_type->editor_style_handles as $editor_style_handle ) {
wp_enqueue_style( $editor_style_handle );
}
// Editor scripts.
foreach ( $block_type->editor_script_handles as $editor_script_handle ) {
wp_enqueue_script( $editor_script_handle );
}
}
enqueue_editor_block_styles_assets();
$rest_path = rest_get_route_for_post( $post );
$preload_paths = array(
'/wp/v2/types?context=view',
'/wp/v2/taxonomies?context=view',
'/wp/v2/block-patterns',
add_query_arg(
array(
'context' => 'edit',
'per_page' => -1,
),
rest_get_route_for_post_type_items( 'wp_block' )
),
add_query_arg( 'context', 'edit', $rest_path ),
sprintf( '/wp/v2/types/%s?context=edit', $post->post_type ),
'/wp/v2/users/me',
array( rest_get_route_for_post_type_items( 'attachment' ), 'OPTIONS' ),
array( rest_get_route_for_post_type_items( 'page' ), 'OPTIONS' ),
array( rest_get_route_for_post_type_items( 'wp_block' ), 'OPTIONS' ),
array( rest_get_route_for_post_type_items( 'wp_template' ), 'OPTIONS' ),
sprintf( '%s/autosaves?context=edit', $rest_path ),
'/wp/v2/settings',
array( '/wp/v2/settings', 'OPTIONS' ),
'/wp/v2/global-styles/themes/' . get_stylesheet(),
'/wp/v2/themes?context=edit&status=active',
'/wp/v2/global-styles/' . \WP_Theme_JSON_Resolver::get_user_global_styles_post_id() . '?context=edit',
);
block_editor_rest_api_preload( $preload_paths, $block_editor_context );
}
}
}
.
Ответ или решение
Для решения проблемы, связанной с отображением шаблонов блоков в вашем настраиваемом компоненте Gutenberg, необходимо детально проанализировать предоставленный код, чтобы понять, почему ваши блоки паттернов не отображаются в вашей среде редактирования, в отличие от стандартной страницы редактирования постов.
Задействуйте подход FOREST:
Focused (Фокусированный): Ваша проблема касается отображения блоков паттернов исключительно в вашей кастомизированной версии редактора Gutenberg, несмотря на то, что они корректно отображаются в стандартной версии WordPress. Нужно сузить область исследования до работы с отображением паттернов.
Organized (Организованный): Начнем с упрощения понимания, как ваш код обрабатывает паттерны блоков.
Relevant (Актуальный): Мы используем только те детали, которые помогают решить проблему отображения паттернов блоков.
Engaging (Увлекательный): Прямое и информативное разъяснение, что делать далее для исправления проблемы.
Simple (Простой): Избегайте сложных технических терминов, сосредотачиваясь на конкретных шагах и принципах.
Trustworthy (Достоверный): Базируйтесь на пониманиях из документации WordPress и общих практиках.
Решение проблемы:
-
Загрузите корректно зарегистрированные шаблоны блоков:
Одним из часто overlooked моментов является корректная регистрация и инициализация библиотеки блоков паттернов. Убедитесь, что паттерны загружаются в нужный момент. Ваш PHP-код пытается регистрировать шаблоны блоков через\WP_Block_Patterns_Registry
. Проверьте, что он корректно выполняется во всех необходимых условиях, особенно для вашего настраиваемого интерфейса (например, проверьте условия вif
блоках, что они корректные для всех случаев использования). -
Проверьте окружение контекста:
ВызовWP_Block_Editor_Context
должен корректно устанавливать контекст среды редактирования. Убедитесь, что передаете правильные параметры при создании объекта контекста. -
Серверные блоки и их установка:
Убедитесь, что установлены все необходимые серверные блоки при помощиwp.blocks.unstable__bootstrapServerSideBlockDefinitions
. Это ключевое действие, так как без запущенной действующей конфигурации серверной части, блоки просто не будут распознаваться во фронтенде. -
Прелоадинг и маршрутизация REST API:
Проверьте корректность маршрутов REST API, которые используются для предварительной загрузки блоков и шаблонов блоков. Все ваши маршруты должны быть доступны и корректны, чтобы данные подгружались в редактор. -
Код редактора и пользовательские компоненты:
Когда вы используете кастомный Gutenberg компонент, важно убедиться, что функции, такие какBlockLibrary
, получают правильные данные о блоках и их паттернах. Возможно, потребуется встроить вызовы к вашей системе шаблонов блоков непосредственно в место, где библиотека блоков инициируется в React коде.
SEO элементы:
- Ключевые слова: Gutenberg, шаблоны блоков, редактор WordPress, подгрузка шаблонов, WP Block Editor Context.
- Оригинальность: Избегайте стандартных рекомендаций, сосредоточьтесь на архитектурных особенностях вашего конкретного случая.
- Читаемость: Разбивайте информацию на четкие шаги, используйте список для организации инструкций.
Советую также проверять лог ошибок в браузере на предмет отображения Javascript ошибок, которые могут указывать на застопорившееся место в коде. Следование вышеуказанным шагам позволит вам сосредоточиться на ключевых аспектах регистрации и отображения шаблонов блоков в вашем настраиваемом решении Gutenberg.