diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 7988bfe1d7bc53..b449cd539f91aa 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -895,8 +895,8 @@ Single tab within a tabs block. ([Source](https://github.com/WordPress/gutenberg - **Experimental:** true - **Category:** design - **Parent:** core/tabs -- **Supports:** ~~html~~, ~~inserter~~, ~~reusable~~ -- **Attributes:** title +- **Supports:** anchor, ~~html~~, ~~reusable~~ +- **Attributes:** isActive, label ## Table @@ -925,8 +925,7 @@ Organize content into tabs. ([Source](https://github.com/WordPress/gutenberg/tre - **Experimental:** true - **Category:** design - **Allowed Blocks:** core/tab -- **Supports:** align (full, wide), color (text, ~~background~~), interactivity, layout (default, ~~allowJustification~~, ~~allowSwitching~~), shadow, spacing (margin, padding), ~~html~~ -- **Attributes:** activeTab +- **Supports:** align (full, wide), color (text, ~~background~~), interactivity, spacing (margin, padding), ~~html~~ ## Tag Cloud diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss index c43137c632b73f..3f88c3f45575bf 100644 --- a/packages/block-library/src/editor.scss +++ b/packages/block-library/src/editor.scss @@ -44,6 +44,7 @@ @import "./social-links/editor.scss"; @import "./spacer/editor.scss"; @import "./table/editor.scss"; +@import "./tabs/editor.scss"; @import "./template-part/editor.scss"; @import "./text-columns/editor.scss"; @import "./video/editor.scss"; diff --git a/packages/block-library/src/tab/block.json b/packages/block-library/src/tab/block.json index fdabf177c2697b..ce258113e1b2c5 100644 --- a/packages/block-library/src/tab/block.json +++ b/packages/block-library/src/tab/block.json @@ -8,16 +8,19 @@ "textdomain": "default", "__experimental": true, "attributes": { - "title": { + "isActive": { + "type": "boolean", + "default": false + }, + "label": { "type": "string" } }, "parent": [ "core/tabs" ], "supports": { + "anchor": true, "html": false, - "inserter": false, "reusable": false }, - "usesContext": [ "tabs/activeTab" ], "style": "wp-block-tab" } diff --git a/packages/block-library/src/tab/edit.js b/packages/block-library/src/tab/edit.js index 27757e0dc2af4c..ac8ac7b5982e28 100644 --- a/packages/block-library/src/tab/edit.js +++ b/packages/block-library/src/tab/edit.js @@ -5,15 +5,25 @@ import { InnerBlocks, useBlockProps, useInnerBlocksProps, + store as blockEditorStore, } from '@wordpress/block-editor'; +import { useSelect } from '@wordpress/data'; + +export default function Edit( { attributes, clientId } ) { + const hasChildBlocks = useSelect( + ( select ) => + select( blockEditorStore ).getBlockOrder( clientId ).length > 0, + [ clientId ] + ); + const { isActive } = attributes; -export default function Edit( { clientId, context } ) { - const isActive = context[ 'tabs/activeTab' ] === clientId; const blockProps = useBlockProps( { className: isActive ? 'is-active' : '', } ); const innerBlocksProps = useInnerBlocksProps( blockProps, { - renderAppender: InnerBlocks.ButtonBlockAppender, + renderAppender: hasChildBlocks + ? undefined + : InnerBlocks.ButtonBlockAppender, } ); return
; } diff --git a/packages/block-library/src/tab/save.js b/packages/block-library/src/tab/save.js index b78ba5bb38bddf..000acdcd4a6055 100644 --- a/packages/block-library/src/tab/save.js +++ b/packages/block-library/src/tab/save.js @@ -3,14 +3,10 @@ */ import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; -export default function save( { attributes } ) { - const { title } = attributes; - +export default function save() { return (
-
- -
+
); } diff --git a/packages/block-library/src/tab/style.scss b/packages/block-library/src/tab/style.scss index a2051c5861ef6f..e96d32fb7e6bd3 100644 --- a/packages/block-library/src/tab/style.scss +++ b/packages/block-library/src/tab/style.scss @@ -1,8 +1,13 @@ .wp-block-tab { display: none; + padding: 1em 0; &.is-active { display: block; } + + > *:first-child { + margin-top: 0; + } } diff --git a/packages/block-library/src/tabs/block.json b/packages/block-library/src/tabs/block.json index 49e2b2e551c933..a421044e6d0dba 100644 --- a/packages/block-library/src/tabs/block.json +++ b/packages/block-library/src/tabs/block.json @@ -8,15 +8,7 @@ "textdomain": "default", "__experimental": true, "allowedBlocks": [ "core/tab" ], - "attributes": { - "activeTab": { - "type": "string", - "default": "" - } - }, - "providesContext": { - "tabs/activeTab": "activeTab" - }, + "attributes": {}, "supports": { "align": [ "wide", "full" ], "color": { @@ -25,16 +17,11 @@ }, "html": false, "interactivity": true, - "layout": { - "default": { "type": "flex", "orientation": "horizontal" }, - "allowSwitching": false, - "allowJustification": false - }, - "shadow": true, "spacing": { "margin": true, "padding": true } }, + "editorStyle": "wp-block-tabs-editor", "style": "wp-block-tabs" } diff --git a/packages/block-library/src/tabs/edit.js b/packages/block-library/src/tabs/edit.js index 2bc9d781be1954..ff81724f94bf45 100644 --- a/packages/block-library/src/tabs/edit.js +++ b/packages/block-library/src/tabs/edit.js @@ -7,18 +7,18 @@ import clsx from 'clsx'; * WordPress dependencies */ import { - InnerBlocks, useBlockProps, useInnerBlocksProps, store as blockEditorStore, RichText, } from '@wordpress/block-editor'; import { useDispatch, useSelect } from '@wordpress/data'; -import { useEffect } from '@wordpress/element'; +import { useCallback, useEffect } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; const TABS_TEMPLATE = [ - [ 'core/tab', { title: 'Tab 1' } ], - [ 'core/tab', { title: 'Tab 2' } ], + [ 'core/tab', { label: 'Tab 1' } ], + [ 'core/tab', { label: 'Tab 2' } ], ]; const ALLOWED_FORMATS = [ @@ -34,49 +34,96 @@ const ALLOWED_FORMATS = [ 'core/text-color', ]; -export default function Edit( { attributes, clientId, setAttributes } ) { - const { activeTab } = attributes; - const innerBlocks = useSelect( - ( select ) => select( blockEditorStore ).getBlocks( clientId ), - [ clientId ] - ); +export default function Edit( { clientId } ) { + const { innerTabs, selectedTabClientId } = useSelect( + ( select ) => { + const { + getBlocks, + getSelectedBlockClientId, + hasSelectedInnerBlock, + } = select( blockEditorStore ); + const innerBlocks = getBlocks( clientId ); + const selectedBlockClientId = getSelectedBlockClientId(); + let selectedTabId = null; - const blockProps = useBlockProps(); + // Find the first tab that is selected or has selected inner blocks so we can set it as active. + for ( const block of innerBlocks ) { + if ( + block.clientId === selectedBlockClientId || + hasSelectedInnerBlock( block.clientId, true ) + ) { + selectedTabId = block.clientId; + break; + } + } - const innerBlockProps = useInnerBlocksProps( - { - className: 'wp-block-tabs__content', + return { + innerTabs: innerBlocks, + selectedTabClientId: selectedTabId, + }; }, - { - renderAppender: InnerBlocks.ButtonBlockAppender, - template: TABS_TEMPLATE, - } + [ clientId ] ); const { __unstableMarkNextChangeAsNotPersistent, updateBlockAttributes } = useDispatch( blockEditorStore ); - const setActiveTab = ( tabId ) => { - __unstableMarkNextChangeAsNotPersistent(); - setAttributes( { activeTab: tabId } ); - }; + const setActiveTab = useCallback( + ( activeTabClientId ) => { + // Set each inner tab's `isActive` attribute. + innerTabs.forEach( ( block ) => { + __unstableMarkNextChangeAsNotPersistent(); + updateBlockAttributes( block.clientId, { + isActive: block.clientId === activeTabClientId, + } ); + } ); + }, + [ + innerTabs, + updateBlockAttributes, + __unstableMarkNextChangeAsNotPersistent, + ] + ); useEffect( () => { - // Initialize the first tab as active when the component mounts. - if ( innerBlocks.length ) { - setActiveTab( innerBlocks[ 0 ].clientId ); + if ( innerTabs?.length ) { + // Set the first tab as active when the editor is loaded + setActiveTab( innerTabs[ 0 ].clientId ); } - }, [] ); // eslint-disable-line react-hooks/exhaustive-deps -- only run effect once when component mounts. + }, [] ); // eslint-disable-line react-hooks/exhaustive-deps -- set first tab as active when the editor is loaded. - // if ( ! innerBlocks || innerBlocks.length === 0 ) { - // return null; - // } + useEffect( () => { + const hasActiveTab = + innerTabs && + innerTabs.some( ( block ) => block.attributes.isActive ); + + if ( selectedTabClientId ) { + // If an inner tab block is selected, or its inner blocks are selected, it becomes the active tab. + setActiveTab( selectedTabClientId ); + } else if ( ! hasActiveTab && innerTabs?.length ) { + // Otherwise, if there's no active tab, default to the first inner tab. + setActiveTab( innerTabs[ 0 ].clientId ); + } + }, [ innerTabs, selectedTabClientId, setActiveTab ] ); + + const blockProps = useBlockProps(); + const innerBlockProps = useInnerBlocksProps( + { + className: 'wp-block-tabs__content', + }, + { + __experimentalCaptureToolbars: true, + clientId, + orientation: 'horizontal', + template: TABS_TEMPLATE, + } + ); return (