diff --git a/src/blockparty-tabs-nav-item/edit.js b/src/blockparty-tabs-nav-item/edit.js index 7d68c46..762e5aa 100644 --- a/src/blockparty-tabs-nav-item/edit.js +++ b/src/blockparty-tabs-nav-item/edit.js @@ -5,8 +5,6 @@ import { getBlockType } from '@wordpress/blocks'; import { select } from '@wordpress/data'; import ComposeBlockControls from './ComposeBlockControls'; import getSynchedID from '../blockparty-tabs/GetSynchedID'; -import SyncTabsActive from '../blockparty-tabs/SyncTabsActive'; - export default function Edit( { attributes, setAttributes, @@ -53,7 +51,6 @@ export default function Edit( { return ( <> - - { - const { - getBlockName, - getBlockIndex, - getSelectedBlockClientId, - getBlockParents, - getBlockRootClientId, - } = select( 'core/block-editor' ); - const selected = getSelectedBlockClientId(); - if ( ! selected ) { - return { - selectedBlockClientId: null, - closestTabItemId: null, - tabsBlockId: null, - myIndex: 0, - }; - } - // Find the tab item closest to the selection: the selected block if it - // is a tab item, otherwise the first tab item in the ancestor chain. - let closest = null; - if ( - TAB_ITEM_NAMES.indexOf( getBlockName( selected ) ) !== -1 - ) { - closest = selected; - } else { - const parents = getBlockParents( selected ); - for ( let i = 0; i < parents.length; i += 1 ) { - if ( - TAB_ITEM_NAMES.indexOf( - getBlockName( parents[ i ] ) - ) !== -1 - ) { - closest = parents[ i ]; - break; - } - } - } - // Parent chain: this block → tabs-nav or tabs-panels → blockparty/tabs. - const directParent = getBlockRootClientId( clientId ); - const tabsId = directParent - ? getBlockRootClientId( directParent ) - : null; - return { - selectedBlockClientId: selected, - closestTabItemId: closest, - tabsBlockId: tabsId, - myIndex: getBlockIndex( clientId ), - }; - }, - [ clientId ] - ); +/** + * Finds the tab item (nav or panel) closest to the current selection. + * + * @param {Function} selectStore Bound select from useSelect. + * @return {string|null} Closest tab item clientId, or null. + */ +function getClosestTabItemClientId( selectStore ) { + const { getBlockName, getSelectedBlockClientId, getBlockParents } = + selectStore( 'core/block-editor' ); + const selected = getSelectedBlockClientId(); + if ( ! selected ) { + return null; + } + if ( TAB_ITEM_NAMES.indexOf( getBlockName( selected ) ) !== -1 ) { + return selected; + } + const parents = getBlockParents( selected ); + for ( let i = 0; i < parents.length; i += 1 ) { + if ( TAB_ITEM_NAMES.indexOf( getBlockName( parents[ i ] ) ) !== -1 ) { + return parents[ i ]; + } + } + return null; +} + +/** + * Resolves the blockparty/tabs clientId that owns a tab item (same chain as legacy per-item sync). + * + * @param {Function} selectStore Bound select from useSelect. + * @param {string} tabItemId tabs-nav-item or tabs-panel-item clientId. + * @return {string|null} Owning blockparty/tabs clientId. + */ +function getOwningTabsBlockClientId( selectStore, tabItemId ) { + const { getBlockRootClientId } = selectStore( 'core/block-editor' ); + const directParent = getBlockRootClientId( tabItemId ); + return directParent ? getBlockRootClientId( directParent ) : null; +} + +/** + * Subscribes to selection and syncs this tabs block's tabsActive when appropriate. + * + * @param {string} tabsBlockClientId This blockparty/tabs block clientId. + * @return {void} + */ +export function useSyncTabsActiveForTabsBlock( tabsBlockClientId ) { + const { shouldSync, targetTabsActive } = useSelect( + ( selectStore ) => { + const closest = getClosestTabItemClientId( selectStore ); + if ( ! closest ) { + return { shouldSync: false, targetTabsActive: 0 }; + } + const owningTabsId = getOwningTabsBlockClientId( + selectStore, + closest + ); + if ( owningTabsId !== tabsBlockClientId ) { + return { shouldSync: false, targetTabsActive: 0 }; + } + const { getBlockIndex } = selectStore( 'core/block-editor' ); + return { + shouldSync: true, + targetTabsActive: getBlockIndex( closest ), + }; + }, + [ tabsBlockClientId ] + ); const { updateBlockAttributes } = useDispatch( 'core/block-editor' ); - // When this block is the active tab (closest to selection or selected), sync tabsActive. useEffect( () => { - if ( ! selectedBlockClientId || ! tabsBlockId ) { + if ( ! shouldSync ) { return; } - const isActiveTab = - closestTabItemId === clientId || selectedBlockClientId === clientId; - if ( ! isActiveTab ) { + const current = + select( 'core/block-editor' ).getBlockAttributes( + tabsBlockClientId + )?.tabsActive; + if ( current === targetTabsActive ) { return; } - updateBlockAttributes( tabsBlockId, { tabsActive: myIndex } ); + updateBlockAttributes( tabsBlockClientId, { + tabsActive: targetTabsActive, + } ); }, [ - selectedBlockClientId, - closestTabItemId, - clientId, - tabsBlockId, - myIndex, + shouldSync, + targetTabsActive, + tabsBlockClientId, updateBlockAttributes, ] ); - - return null; } diff --git a/src/blockparty-tabs/edit.js b/src/blockparty-tabs/edit.js index edea034..0da9a0e 100644 --- a/src/blockparty-tabs/edit.js +++ b/src/blockparty-tabs/edit.js @@ -19,6 +19,7 @@ import { justifySpaceBetween, } from '@wordpress/icons'; import './editor.scss'; +import { useSyncTabsActiveForTabsBlock } from './SyncTabsActive'; const DEFAULT_TABS_POSITIONS = [ { @@ -60,6 +61,7 @@ const setTabsIndex = ( setAttributes, clientId ) => { }; export default function Edit( { attributes, setAttributes, clientId } ) { + useSyncTabsActiveForTabsBlock( clientId ); setTabsIndex( setAttributes, clientId ); const { title, mode } = attributes; const blockProps = useBlockProps( {