UNPKG

@wordpress/block-library

Version:
115 lines (100 loc) 3.19 kB
/** * WordPress dependencies */ import { useBlockProps, useInnerBlocksProps, BlockContextProvider, store as blockEditorStore, } from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; /** * Internal dependencies */ import Controls from './controls'; import useTabListSync from './use-tab-list-sync'; const EMPTY_ARRAY = []; /** * Only the two structural child blocks are specified here — without inner * block entries for core/tab-list or core/tab-panels. * * If inner blocks were included in this template, `synchronizeBlocksWithTemplate` * (called whenever templateLock === 'all') would recurse into the containers and * truncate them to the template count, causing data loss when a saved block with * more than two tabs is re-opened in the editor. * * Initial tab/panel creation is delegated to the tab-panels template in * tab-panels/edit.js (templateLock: false, applied only when empty). */ const TABS_TEMPLATE = [ [ 'core/tab-list' ], [ 'core/tab-panels' ] ]; function Edit( { clientId, attributes, setAttributes } ) { const { anchor, activeTabIndex, editorActiveTabIndex } = attributes; const { tabPanels, tabPanelsClientId, tabs, tabListClientId } = useSelect( ( select ) => { const { getBlocks } = select( blockEditorStore ); const innerBlocks = getBlocks( clientId ); const tabPanelBlocks = innerBlocks.find( ( block ) => block.name === 'core/tab-panels' ); const tabList = innerBlocks.find( ( block ) => block.name === 'core/tab-list' ); return { tabPanels: tabPanelBlocks?.innerBlocks ?? EMPTY_ARRAY, tabPanelsClientId: tabPanelBlocks?.clientId ?? null, tabs: tabList?.innerBlocks ?? EMPTY_ARRAY, tabListClientId: tabList?.clientId ?? null, }; }, [ clientId ] ); useTabListSync( { tabPanels, tabs, tabPanelsClientId, tabListClientId, } ); /** * Memoize context value to prevent unnecessary re-renders. */ const contextValue = useMemo( () => { /** * Compute tabs list from innerblocks to provide via context. * This traverses the tab-panel block to find all tab blocks * and extracts their label and anchor for the tab-list to consume. */ const tabList = tabPanels.map( ( tab, index ) => ( { id: tab.attributes.anchor || `tab-${ index }`, label: tab.attributes.label || '', clientId: tab.clientId, index, } ) ); return { 'core/tabs-list': tabList, 'core/tabs-id': anchor, 'core/tabs-activeTabIndex': activeTabIndex, 'core/tabs-editorActiveTabIndex': editorActiveTabIndex, }; }, [ tabPanels, anchor, activeTabIndex, editorActiveTabIndex ] ); const blockProps = useBlockProps(); const innerBlockProps = useInnerBlocksProps( blockProps, { __experimentalCaptureToolbars: true, template: TABS_TEMPLATE, templateLock: 'all', renderAppender: false, } ); return ( <BlockContextProvider value={ contextValue }> <div { ...innerBlockProps }> <Controls clientId={ clientId } attributes={ attributes } setAttributes={ setAttributes } /> { innerBlockProps.children } </div> </BlockContextProvider> ); } export default Edit;