Я создал блок «Панели с вкладками» ( содержимое с вкладками ), который по сути является просто компонентом InnerBlocks, который допускает только блок «Панель». Когда вы создаете панель, вы должны дать ей заголовок, который затем используется на панели, а также кнопка «Вкладка». Поэтому в моей функции рендеринга панелей с вкладками мне нужно извлечь заголовок из дочерних блоков панелей.
Есть несколько подходов, которые я мог бы использовать, например, просто использовать регулярное выражение в рендеринге с вкладками. php функция поиска дочерних элементов html для правильных атрибутов, но это не кажется лучшим подходом.
Я думаю, что самым простым решением было бы прослушать любые изменения в блоках Panel и сохранить изменяется (заголовок и идентификатор в этом случае) на родителя. Мой текущий подход основан на этом обсуждении , которое использует хуки для прослушивания изменений. Эта часть, кажется, работает нормально, но мне нужно где-то сохранить выходные данные, поэтому я сохраняю их как атрибуты в блоке с вкладками. Поначалу кажется, что это нормально работает, но использование метода setAttributes непосредственно в функции редактирования приводит к проблемам. Если на странице слишком много блоков с вкладками, то React выдает ошибку «слишком много отрисовок».
Где должна жить моя функция «setAttributes», или есть ли лучший подход для передачи данных от дочернего элемента к родитель? Я думал об использовании ловушки useDispatch в дочернем процессоре, но мне нужно проверить много событий (заголовок меняется, блок переупорядочен, блок удален и т. Д. c.)
Вот соответствующие файлы js и php. Есть некоторые пользовательские элементы, но вы должны иметь возможность их анализировать.
tabbed-panel. js
import { arraysMatch } from 'Components/utils.js'
const { InnerBlocks } = wp.blockEditor
const { createBlock } = wp.blocks
const { Button } = wp.components
const { useDispatch, useSelect } = wp.data
const { __ } = wp.i18n
export const tabbedPanels = {
name: 'my/tabbed-panels',
args: {
title: __('Tabbed Panels', '_ws'),
description: __('Tabbable panels of content.', '_ws'),
icon: 'table-row-after',
category: 'common',
supports: {
anchor: true
},
attributes: {
headings: {
type: 'array',
default: []
},
uids: {
type: 'array',
default: []
}
},
edit: props => {
const { setAttributes } = props
const { headings, uids } = props.attributes
const { insertBlock } = useDispatch('core/block-editor')
const { panelHeadings, panelUids, blockCount } = useSelect(select => {
const blocks = select('core/block-editor').getBlocks(props.clientId)
return {
panelHeadings: blocks.map(b => b.attributes.heading),
panelUids: blocks.map(b => b.clientId),
blockCount: select('core/block-editor').getBlockOrder(props.clientId).length
}
})
if (!arraysMatch(panelHeadings, headings)) {
setAttributes({ headings: panelHeadings })
}
if (!arraysMatch(panelUids, uids)) {
setAttributes({ uids: panelUids })
}
return (
<div className="block-row">
<InnerBlocks
allowedBlocks={ ['my/panel'] }
templateLock={ false }
renderAppender={ () => (
<Button
isSecondary
onClick={ e => {
insertBlock(createBlock('my/panel'), blockCount, props.clientId)
} }
>
{ __('Add Panel', '_ws') }
</Button>
) }
/>
</div>
)
},
save: props => {
return (
<InnerBlocks.Content />
)
}
}
}
tabbed-Panel-render. php
<?php
function block_tabbed_panels($atts, $content) {
$atts['className'] = 'wp-block-ws-tabbed-panels ' . ($atts['className'] ?? '');
$headings = $atts['headings'] ?? '';
$uids = $atts['uids'] ?? '';
ob_start(); ?>
<div class="tabs" role="tablist">
<?php
foreach ($headings as $i=>$heading) : ?>
<button
id="tab-<?= $uids[$i]; ?>"
class="tab"
role="tab"
aria-selected="false"
aria-controls="panel-<?= $uids[$i]; ?>"
tabindex="-1"
>
<?= $heading; ?>
</button>
<?php
endforeach; ?>
</div>
<div class="panels">
<?= $content; ?>
</div>
<?php
return ob_get_clean();
}
панель. js
import ComponentHooks from 'Components/component-hooks.js'
const { InnerBlocks, RichText } = wp.blockEditor
const { __ } = wp.i18n
export const panel = {
name: 'my/panel',
args: {
title: __('Panel', '_ws'),
description: __('Panel with associated tab.', '_ws'),
icon: 'format-aside',
category: 'common',
supports: {
customClassName: false,
html: false,
inserter: false,
reusable: false
},
attributes: {
heading: {
type: 'string'
},
uid: {
type: 'string'
}
},
edit: props => {
const { setAttributes } = props
const { heading } = props.attributes
return (
<>
<ComponentHooks
componentDidMount={ () => setAttributes({ uid: props.clientId }) }
/>
<RichText
label={ __('Tab Name', '_ws') }
placeholder={ __('Tab Name', '_ws') }
tagName="h4"
onChange={ newValue => setAttributes({ heading: newValue }) }
value={ heading }
/>
<InnerBlocks
templateLock={ false }
/>
</>
)
},
save: props => {
return (
<InnerBlocks.Content />
)
}
}
}
панель-рендер. php
<?php
function block_panel($atts, $content) {
$uid = $atts['uid'] ?? '';
ob_start(); ?>
<div
id="panel-<?= $uid ?>"
class="panel"
role="tabpanel"
aria-labelledby="tab-<?= $uid; ?>"
tabindex="0"
hidden="hidden"
>
<?= $content; ?>
</div>
<?php
return ob_get_clean();
}