diff --git a/.plugin-data b/.plugin-data
index 1b94983..ff53658 100644
--- a/.plugin-data
+++ b/.plugin-data
@@ -1,4 +1,4 @@
{
- "version": "1.1.2",
+ "version": "1.1.3",
"slug": "blockparty-tabs"
}
diff --git a/.wordpress-org/blueprints/blueprint.json b/.wordpress-org/blueprints/blueprint.json
index 9e4b1c8..c3b6a21 100644
--- a/.wordpress-org/blueprints/blueprint.json
+++ b/.wordpress-org/blueprints/blueprint.json
@@ -11,7 +11,7 @@
"pluginData": {
"resource": "git:directory",
"url": "https://github.com/BeAPI/blockparty-tabs",
- "ref": "1.1.2",
+ "ref": "1.1.3",
"refType": "tag"
},
"options": {
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2bbebbe..ec6f75a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## 1.1.3 - 2026-04-27
+
+- Performance improvements
+
## 1.1.2 - 2026-04-24
- Fix css for nested tabs block
diff --git a/blockparty-tabs.php b/blockparty-tabs.php
index d7d5296..a9bbbab 100644
--- a/blockparty-tabs.php
+++ b/blockparty-tabs.php
@@ -4,7 +4,7 @@
* Description: Accessible Tabs block for WordPress gutenberg.
* Requires at least: 6.2
* Requires PHP: 8.1
- * Version: 1.1.2
+ * Version: 1.1.3
* Author: Be API Technical team
* Author URI: https://beapi.fr
* License: GPL-2.0-or-later
@@ -14,7 +14,7 @@
namespace Blockparty\Tabs;
-define( 'BLOCKPARTY_TABS_VERSION', '1.1.2' );
+define( 'BLOCKPARTY_TABS_VERSION', '1.1.3' );
define( 'BLOCKPARTY_TABS_URL', plugin_dir_url( __FILE__ ) );
define( 'BLOCKPARTY_TABS_DIR', plugin_dir_path( __FILE__ ) );
define( 'BLOCKPARTY_TABS_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
diff --git a/readme.txt b/readme.txt
index 28f8f03..8fb5c20 100644
--- a/readme.txt
+++ b/readme.txt
@@ -31,6 +31,10 @@ directory take precedence. For example, `/assets/screenshot-1.png` would win ove
== Changelog ==
+= 1.1.3 =
+
+* Performance improvements
+
= 1.1.2 =
* Fix css for nested tabs block
diff --git a/src/blockparty-tabs-nav-item/block.json b/src/blockparty-tabs-nav-item/block.json
index d49076e..40d356e 100644
--- a/src/blockparty-tabs-nav-item/block.json
+++ b/src/blockparty-tabs-nav-item/block.json
@@ -2,7 +2,7 @@
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "blockparty/tabs-nav-item",
- "version": "1.1.2",
+ "version": "1.1.3",
"title": "Tab",
"category": "widgets",
"description": "Accessible tabs block item",
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/block.json b/src/blockparty-tabs/block.json
index 151b963..d4e8aa2 100644
--- a/src/blockparty-tabs/block.json
+++ b/src/blockparty-tabs/block.json
@@ -2,7 +2,7 @@
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "blockparty/tabs",
- "version": "1.1.2",
+ "version": "1.1.3",
"title": "Tabs",
"category": "widgets",
"description": "Accessible tabs block",
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( {