diff --git a/.plugin-data b/.plugin-data
index dca7deb..c4ee859 100644
--- a/.plugin-data
+++ b/.plugin-data
@@ -1,4 +1,4 @@
{
- "version": "1.0.5",
+ "version": "1.0.6",
"slug": "blockparty-tabs"
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0eeec90..fe7011a 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.0.6 - 2026-02-11
+
+- Update view and edit script to allow to use nested tabs block (tabs inside tab panel).
+
## 1.0.5 - 2025-11-10
- Update block icons
diff --git a/blockparty-tabs.php b/blockparty-tabs.php
index 0b48737..fbcefff 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.0.5
+ * Version: 1.0.6
* 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.0.5' );
+define( 'BLOCKPARTY_TABS_VERSION', '1.0.6' );
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/package.json b/package.json
index e1dfa1f..82bdcf0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "blockparty-tabs",
- "version": "1.0.5",
+ "version": "1.0.6",
"description": "Accessible tabs block for WordPress",
"author": "Be API Technical team",
"license": "GPL-2.0-or-later",
diff --git a/readme.txt b/readme.txt
index 843065a..c6148a1 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.0.6 =
+
+* Update view and edit script to allow to use nested tabs block (tabs inside tab panel).
+
= 1.0.5 =
* Update block icons
diff --git a/src/blockparty-tabs-nav-item/block.json b/src/blockparty-tabs-nav-item/block.json
index 4e32968..dc0bbb8 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.0.5",
+ "version": "1.0.6",
"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 2dc11e9..e044d51 100644
--- a/src/blockparty-tabs-nav-item/edit.js
+++ b/src/blockparty-tabs-nav-item/edit.js
@@ -5,6 +5,7 @@ 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,
@@ -47,6 +48,7 @@ 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 ]
+ );
+
+ const { updateBlockAttributes } = useDispatch( 'core/block-editor' );
+
+ // When this block is the active tab (closest to selection or selected), sync tabsActive.
+ useEffect( () => {
+ if ( ! selectedBlockClientId || ! tabsBlockId ) {
+ return;
+ }
+ const isActiveTab =
+ closestTabItemId === clientId || selectedBlockClientId === clientId;
+ if ( ! isActiveTab ) {
+ return;
+ }
+ updateBlockAttributes( tabsBlockId, { tabsActive: myIndex } );
+ }, [
+ selectedBlockClientId,
+ closestTabItemId,
+ clientId,
+ tabsBlockId,
+ myIndex,
+ updateBlockAttributes,
+ ] );
+
+ return null;
+}
diff --git a/src/blockparty-tabs/TabsFocus.js b/src/blockparty-tabs/TabsFocus.js
deleted file mode 100644
index ddd229d..0000000
--- a/src/blockparty-tabs/TabsFocus.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { withSelect } from '@wordpress/data';
-import { compose } from '@wordpress/compose';
-
-const TabsFocus = ( { activeTab, setAttributes } ) => {
- if ( activeTab >= 0 ) {
- setAttributes( { tabsActive: activeTab } );
- }
- return null;
-};
-
-export default compose( [
- withSelect( ( select, ownProps ) => {
- const {
- getBlockName,
- getBlockIndex,
- getSelectedBlockClientId,
- getBlockParentsByBlockName,
- hasSelectedInnerBlock,
- } = select( 'core/block-editor' );
- if ( ! hasSelectedInnerBlock( ownProps.clientId, true ) ) {
- return {
- activeTab: -1,
- setAttributes: ownProps.setAttributes,
- };
- }
- const selected = getSelectedBlockClientId();
- const name = getBlockName( selected );
- let currentIndex = getBlockIndex( selected );
- if (
- 'blockparty/tabs-panel-item' !== name &&
- 'blockparty/tabs-nav-item' !== name
- ) {
- const parents = getBlockParentsByBlockName( selected, [
- 'blockparty/tabs-nav-item',
- 'blockparty/tabs-panel-item',
- ] );
- currentIndex = getBlockIndex( parents[ 0 ] );
- }
- return {
- activeTab: currentIndex,
- setAttributes: ownProps.setAttributes,
- };
- } ),
-] )( TabsFocus );
diff --git a/src/blockparty-tabs/block.json b/src/blockparty-tabs/block.json
index b6e6f31..c5830d4 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.0.5",
+ "version": "1.0.6",
"title": "Tabs",
"category": "widgets",
"description": "Accessible tabs block",
diff --git a/src/blockparty-tabs/edit.js b/src/blockparty-tabs/edit.js
index fd56104..edea034 100644
--- a/src/blockparty-tabs/edit.js
+++ b/src/blockparty-tabs/edit.js
@@ -9,7 +9,6 @@ import {
} from '@wordpress/block-editor';
import { PanelBody, PanelRow, TextControl } from '@wordpress/components';
import { select } from '@wordpress/data';
-import TabsFocus from './TabsFocus';
import {
heading,
justifyRight,
@@ -119,7 +118,6 @@ export default function Edit( { attributes, setAttributes, clientId } ) {
-
>
);
diff --git a/src/blockparty-tabs/script.js b/src/blockparty-tabs/script.js
index 38f8623..8d02185 100644
--- a/src/blockparty-tabs/script.js
+++ b/src/blockparty-tabs/script.js
@@ -18,18 +18,17 @@ class TabsAutomatic {
this.firstTab = null;
this.lastTab = null;
- this.tabs = Array.from(
- this.tablistNode.querySelectorAll( '[role=tab]' )
+ this.tabnav = this.tablistNode.querySelector(
+ '.wp-block-blockparty-tabs-nav'
);
+ this.tabs = Array.from( this.tabnav.querySelectorAll( 'a[role=tab]' ) );
+ this.tabpanelsNode = this.tabnav.nextElementSibling;
+
this.tabpanels = [];
for ( let i = 0; i < this.tabs.length; i += 1 ) {
const tab = this.tabs[ i ];
- const tabpanel = this.tablistNode
- .closest( '.wp-block-blockparty-tabs' )
- .querySelectorAll( '.wp-block-blockparty-tabs-panels > *' )[
- i
- ];
+ const tabpanel = this.tabpanelsNode.children[ i ];
tab.tabIndex = -1;
tab.setAttribute( 'aria-selected', 'false' );