From 5bf4de5132363de0f6e780b5b89f91cc2dc5c6c3 Mon Sep 17 00:00:00 2001 From: Grant Kinney Date: Wed, 10 Jul 2024 15:24:08 -0500 Subject: [PATCH 01/21] Scaffolds tabs and tab blocks --- docs/reference-guides/core-blocks.md | 20 ++++++++++++++++ lib/blocks.php | 2 ++ packages/block-library/src/index.js | 8 +++++++ packages/block-library/src/tab/block.json | 18 ++++++++++++++ packages/block-library/src/tab/edit.js | 12 ++++++++++ packages/block-library/src/tab/index.js | 22 +++++++++++++++++ packages/block-library/src/tab/init.js | 6 +++++ packages/block-library/src/tab/save.js | 12 ++++++++++ packages/block-library/src/tabs/block.json | 28 ++++++++++++++++++++++ packages/block-library/src/tabs/edit.js | 12 ++++++++++ packages/block-library/src/tabs/index.js | 22 +++++++++++++++++ packages/block-library/src/tabs/init.js | 6 +++++ packages/block-library/src/tabs/save.js | 12 ++++++++++ 13 files changed, 180 insertions(+) create mode 100644 packages/block-library/src/tab/block.json create mode 100644 packages/block-library/src/tab/edit.js create mode 100644 packages/block-library/src/tab/index.js create mode 100644 packages/block-library/src/tab/init.js create mode 100644 packages/block-library/src/tab/save.js create mode 100644 packages/block-library/src/tabs/block.json create mode 100644 packages/block-library/src/tabs/edit.js create mode 100644 packages/block-library/src/tabs/index.js create mode 100644 packages/block-library/src/tabs/init.js create mode 100644 packages/block-library/src/tabs/save.js diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 72281a53c3dd1..dceeca2d93d4f 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -887,6 +887,16 @@ Add white space between blocks and customize its height. ([Source](https://githu - **Supports:** anchor, interactivity (clientNavigation), spacing (margin) - **Attributes:** height, width +## Tab + +Tab container for content (use within the Tabs block). ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/tab)) + +- **Name:** core/tab +- **Experimental:** true +- **Category:** design +- **Parent:** core/tabs +- **Supports:** ~~align~~, ~~html~~ + ## Table Create structured content in rows and columns to display information. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/table)) @@ -906,6 +916,16 @@ Summarize your post with a list of headings. Add HTML anchors to Heading blocks - **Supports:** color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** headings, onlyIncludeCurrentPage +## Tabs + +Organize content into tabs. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/tabs)) + +- **Name:** core/tabs +- **Experimental:** true +- **Category:** design +- **Allowed Blocks:** core/tab +- **Supports:** align (full, wide), layout (default, ~~allowJustification~~, ~~allowSwitching~~), shadow, spacing (margin, padding), ~~html~~ + ## Tag Cloud A cloud of popular keywords, each sized by how often it appears. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/tag-cloud)) diff --git a/lib/blocks.php b/lib/blocks.php index c3fdb26700c58..d5ef71fa7f87b 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -39,6 +39,8 @@ function gutenberg_reregister_core_block_types() { 'spacer', 'table', 'table-of-contents', + 'tab', + 'tabs', 'text-columns', 'verse', 'video', diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 56365c87a268f..2ebd1b0cb0ecc 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -114,6 +114,8 @@ import * as socialLinks from './social-links'; import * as spacer from './spacer'; import * as table from './table'; import * as tableOfContents from './table-of-contents'; +import * as tab from './tab'; +import * as tabs from './tabs'; import * as tagCloud from './tag-cloud'; import * as templatePart from './template-part'; import * as termDescription from './term-description'; @@ -232,6 +234,12 @@ const getAllBlocks = () => { queryTitle, postAuthorBiography, ]; + + if ( window.__experimentalEnableBlockExperiments ) { + blocks.push( tab ); + blocks.push( tabs ); + } + if ( window?.__experimentalEnableFormBlocks ) { blocks.push( form ); blocks.push( formInput ); diff --git a/packages/block-library/src/tab/block.json b/packages/block-library/src/tab/block.json new file mode 100644 index 0000000000000..ab363d0f70100 --- /dev/null +++ b/packages/block-library/src/tab/block.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "core/tab", + "title": "Tab", + "category": "design", + "description": "Tab container for content (use within the Tabs block).", + "textdomain": "default", + "__experimental": true, + "attributes": {}, + "parent": [ "core/tabs" ], + "supports": { + "align": false, + "html": false + }, + "editorScript": "file:./build/index.js", + "style": "file:./build/style.css" +} diff --git a/packages/block-library/src/tab/edit.js b/packages/block-library/src/tab/edit.js new file mode 100644 index 0000000000000..25fc9b40db6a1 --- /dev/null +++ b/packages/block-library/src/tab/edit.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { useBlockProps, InnerBlocks } from '@wordpress/block-editor'; + +export default function Edit() { + return ( +
+ +
+ ); +} diff --git a/packages/block-library/src/tab/index.js b/packages/block-library/src/tab/index.js new file mode 100644 index 0000000000000..724198cd0d54c --- /dev/null +++ b/packages/block-library/src/tab/index.js @@ -0,0 +1,22 @@ +/** + * Internal dependencies + */ +import initBlock from '../utils/init-block'; +import metadata from './block.json'; +import edit from './edit'; +import save from './save'; +// import icon from './icon'; + +// import './style.scss'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + // icon, + edit, + save, +}; + +export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/tab/init.js b/packages/block-library/src/tab/init.js new file mode 100644 index 0000000000000..79f0492c2cb2f --- /dev/null +++ b/packages/block-library/src/tab/init.js @@ -0,0 +1,6 @@ +/** + * Internal dependencies + */ +import { init } from './'; + +export default init(); diff --git a/packages/block-library/src/tab/save.js b/packages/block-library/src/tab/save.js new file mode 100644 index 0000000000000..000acdcd4a605 --- /dev/null +++ b/packages/block-library/src/tab/save.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; + +export default function save() { + return ( +
+ +
+ ); +} diff --git a/packages/block-library/src/tabs/block.json b/packages/block-library/src/tabs/block.json new file mode 100644 index 0000000000000..b3bc49af3ea06 --- /dev/null +++ b/packages/block-library/src/tabs/block.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "core/tabs", + "title": "Tabs", + "category": "design", + "description": "Organize content into tabs.", + "textdomain": "default", + "__experimental": true, + "allowedBlocks": [ "core/tab" ], + "attributes": {}, + "supports": { + "align": [ "wide", "full" ], + "html": false, + "layout": { + "default": { "type": "flex", "orientation": "horizontal" }, + "allowSwitching": false, + "allowJustification": false + }, + "shadow": true, + "spacing": { + "margin": true, + "padding": true + } + }, + "editorScript": "file:./build/index.js", + "style": "file:./build/style.css" +} diff --git a/packages/block-library/src/tabs/edit.js b/packages/block-library/src/tabs/edit.js new file mode 100644 index 0000000000000..25fc9b40db6a1 --- /dev/null +++ b/packages/block-library/src/tabs/edit.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { useBlockProps, InnerBlocks } from '@wordpress/block-editor'; + +export default function Edit() { + return ( +
+ +
+ ); +} diff --git a/packages/block-library/src/tabs/index.js b/packages/block-library/src/tabs/index.js new file mode 100644 index 0000000000000..724198cd0d54c --- /dev/null +++ b/packages/block-library/src/tabs/index.js @@ -0,0 +1,22 @@ +/** + * Internal dependencies + */ +import initBlock from '../utils/init-block'; +import metadata from './block.json'; +import edit from './edit'; +import save from './save'; +// import icon from './icon'; + +// import './style.scss'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + // icon, + edit, + save, +}; + +export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/tabs/init.js b/packages/block-library/src/tabs/init.js new file mode 100644 index 0000000000000..79f0492c2cb2f --- /dev/null +++ b/packages/block-library/src/tabs/init.js @@ -0,0 +1,6 @@ +/** + * Internal dependencies + */ +import { init } from './'; + +export default init(); diff --git a/packages/block-library/src/tabs/save.js b/packages/block-library/src/tabs/save.js new file mode 100644 index 0000000000000..000acdcd4a605 --- /dev/null +++ b/packages/block-library/src/tabs/save.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; + +export default function save() { + return ( +
+ +
+ ); +} From 89c8035ff4508c2d8a51a1b00b152902133bdd75 Mon Sep 17 00:00:00 2001 From: Grant Kinney Date: Wed, 17 Jul 2024 11:05:11 -0500 Subject: [PATCH 02/21] WIP Adds title attribute for tab blocks --- packages/block-library/src/tab/block.json | 13 +++++++--- packages/block-library/src/tab/edit.js | 20 ++++++++++++--- packages/block-library/src/tab/save.js | 9 +++++-- packages/block-library/src/tabs/style.scss | 29 ++++++++++++++++++++++ 4 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 packages/block-library/src/tabs/style.scss diff --git a/packages/block-library/src/tab/block.json b/packages/block-library/src/tab/block.json index ab363d0f70100..402b625724782 100644 --- a/packages/block-library/src/tab/block.json +++ b/packages/block-library/src/tab/block.json @@ -4,14 +4,19 @@ "name": "core/tab", "title": "Tab", "category": "design", - "description": "Tab container for content (use within the Tabs block).", + "description": "Single tab within a tabs block.", "textdomain": "default", "__experimental": true, - "attributes": {}, + "attributes": { + "title": { + "type": "string" + } + }, "parent": [ "core/tabs" ], "supports": { - "align": false, - "html": false + "html": false, + "inserter": false, + "reusable": false }, "editorScript": "file:./build/index.js", "style": "file:./build/style.css" diff --git a/packages/block-library/src/tab/edit.js b/packages/block-library/src/tab/edit.js index 25fc9b40db6a1..cb06dc2045fa6 100644 --- a/packages/block-library/src/tab/edit.js +++ b/packages/block-library/src/tab/edit.js @@ -1,12 +1,26 @@ /** * WordPress dependencies */ -import { useBlockProps, InnerBlocks } from '@wordpress/block-editor'; +import { InnerBlocks, RichText, useBlockProps } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; + +export default function Edit( { attributes, setAttributes } ) { + const { title } = attributes; -export default function Edit() { return (
- + + setAttributes( { title: newTitle } ) + } + placeholder={ __( 'Add text…' ) } + /> +
+ +
); } diff --git a/packages/block-library/src/tab/save.js b/packages/block-library/src/tab/save.js index 000acdcd4a605..e7fafd12668f9 100644 --- a/packages/block-library/src/tab/save.js +++ b/packages/block-library/src/tab/save.js @@ -3,10 +3,15 @@ */ import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; -export default function save() { +export default function save( { attributes } ) { + const { title } = attributes; + return (
- +
{ title }
+
+ +
); } diff --git a/packages/block-library/src/tabs/style.scss b/packages/block-library/src/tabs/style.scss new file mode 100644 index 0000000000000..bfdd07f54461f --- /dev/null +++ b/packages/block-library/src/tabs/style.scss @@ -0,0 +1,29 @@ +.wp-block-tabs { + .wp-block-tabs__tab-buttons { + display: flex; + border-bottom: 1px solid #ccc; + } + + .wp-block-tabs__tab-button { + background: none; + border: none; + padding: 10px 15px; + cursor: pointer; + + &.is-active { + border-bottom: 2px solid #007cba; + } + } + + .wp-block-tabs__content { + padding: 15px 0; + } +} + +.wp-block-tab { + display: none; + + &.is-active { + display: block; + } +} From f3074ba1b6852510b1212530bb2bf72eb79c36f8 Mon Sep 17 00:00:00 2001 From: Grant Kinney Date: Wed, 17 Jul 2024 22:02:35 -0500 Subject: [PATCH 03/21] WIP Show only active tab --- packages/block-library/src/tab/block.json | 1 + packages/block-library/src/tab/edit.js | 29 ++++++++------- packages/block-library/src/tab/save.js | 1 - packages/block-library/src/tabs/block.json | 15 ++++++-- packages/block-library/src/tabs/edit.js | 42 +++++++++++++++++++--- 5 files changed, 65 insertions(+), 23 deletions(-) diff --git a/packages/block-library/src/tab/block.json b/packages/block-library/src/tab/block.json index 402b625724782..802c014d5579a 100644 --- a/packages/block-library/src/tab/block.json +++ b/packages/block-library/src/tab/block.json @@ -18,6 +18,7 @@ "inserter": false, "reusable": false }, + "usesContext": [ "tabs/activeTab" ], "editorScript": "file:./build/index.js", "style": "file:./build/style.css" } diff --git a/packages/block-library/src/tab/edit.js b/packages/block-library/src/tab/edit.js index cb06dc2045fa6..45b75750e156a 100644 --- a/packages/block-library/src/tab/edit.js +++ b/packages/block-library/src/tab/edit.js @@ -4,23 +4,22 @@ import { InnerBlocks, RichText, useBlockProps } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; -export default function Edit( { attributes, setAttributes } ) { - const { title } = attributes; - +export default function Edit( { context } ) { + console.log( context ); + const isActive = context[ 'tabs/activeTab' ]; return ( -
- - setAttributes( { title: newTitle } ) - } - placeholder={ __( 'Add text…' ) } +
+ -
- -
); } diff --git a/packages/block-library/src/tab/save.js b/packages/block-library/src/tab/save.js index e7fafd12668f9..b78ba5bb38bdd 100644 --- a/packages/block-library/src/tab/save.js +++ b/packages/block-library/src/tab/save.js @@ -8,7 +8,6 @@ export default function save( { attributes } ) { return (
-
{ title }
diff --git a/packages/block-library/src/tabs/block.json b/packages/block-library/src/tabs/block.json index b3bc49af3ea06..154ba0e9e6ada 100644 --- a/packages/block-library/src/tabs/block.json +++ b/packages/block-library/src/tabs/block.json @@ -8,10 +8,19 @@ "textdomain": "default", "__experimental": true, "allowedBlocks": [ "core/tab" ], - "attributes": {}, + "attributes": { + "activeTab": { + "type": "number", + "default": 0 + } + }, + "providesContext": { + "tabs/activeTab": "activeTab" + }, "supports": { "align": [ "wide", "full" ], "html": false, + "interactivity": true, "layout": { "default": { "type": "flex", "orientation": "horizontal" }, "allowSwitching": false, @@ -23,6 +32,6 @@ "padding": true } }, - "editorScript": "file:./build/index.js", - "style": "file:./build/style.css" + "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 25fc9b40db6a1..481479b15f055 100644 --- a/packages/block-library/src/tabs/edit.js +++ b/packages/block-library/src/tabs/edit.js @@ -1,12 +1,46 @@ /** * WordPress dependencies */ -import { useBlockProps, InnerBlocks } from '@wordpress/block-editor'; +import { + InnerBlocks, + useBlockProps, + useInnerBlocksProps, + store as blockEditorStore, +} from '@wordpress/block-editor'; +import { useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; +import clsx from 'clsx'; + +export default function Edit( { attributes, clientId, setAttributes } ) { + const { activeTab } = attributes; + const innerBlocks = useSelect( + ( select ) => select( blockEditorStore ).getBlocks( clientId ), + [ clientId ] + ); + + const setActiveTab = ( index ) => { + setAttributes( { activeTab: index } ); + }; -export default function Edit() { return ( -
- +
+
+ { innerBlocks.map( ( block, index ) => ( + + ) ) } +
+
+ +
); } From ccebbe2773459dc232b4e736b34a7059aeca9782 Mon Sep 17 00:00:00 2001 From: Grant Kinney Date: Thu, 18 Jul 2024 21:01:34 -0500 Subject: [PATCH 04/21] Displays list of tabs and active tab content --- docs/reference-guides/core-blocks.md | 8 +- packages/block-library/src/style.scss | 2 + packages/block-library/src/tab/block.json | 3 +- packages/block-library/src/tab/edit.js | 34 +++---- packages/block-library/src/tab/index.js | 2 - packages/block-library/src/tab/style.scss | 8 ++ packages/block-library/src/tabs/block.json | 5 +- packages/block-library/src/tabs/edit.js | 105 ++++++++++++++++----- packages/block-library/src/tabs/index.js | 2 - packages/block-library/src/tabs/style.scss | 33 +------ 10 files changed, 119 insertions(+), 83 deletions(-) create mode 100644 packages/block-library/src/tab/style.scss diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index dceeca2d93d4f..2d191c938c4ff 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -889,13 +889,14 @@ Add white space between blocks and customize its height. ([Source](https://githu ## Tab -Tab container for content (use within the Tabs block). ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/tab)) +Single tab within a tabs block. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/tab)) - **Name:** core/tab - **Experimental:** true - **Category:** design - **Parent:** core/tabs -- **Supports:** ~~align~~, ~~html~~ +- **Supports:** ~~html~~, ~~inserter~~, ~~reusable~~ +- **Attributes:** title ## Table @@ -924,7 +925,8 @@ Organize content into tabs. ([Source](https://github.com/WordPress/gutenberg/tre - **Experimental:** true - **Category:** design - **Allowed Blocks:** core/tab -- **Supports:** align (full, wide), layout (default, ~~allowJustification~~, ~~allowSwitching~~), shadow, spacing (margin, padding), ~~html~~ +- **Supports:** align (full, wide), interactivity, layout (default, ~~allowJustification~~, ~~allowSwitching~~), shadow, spacing (margin, padding), ~~html~~ +- **Attributes:** activeTab ## Tag Cloud diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index 8f17cd7a50f55..e4f029c084a00 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -53,9 +53,11 @@ @import "./site-title/style.scss"; @import "./social-links/style.scss"; @import "./spacer/style.scss"; +@import "./tab/style.scss"; @import "./tag-cloud/style.scss"; @import "./table/style.scss"; @import "./table-of-contents/style.scss"; +@import "./tabs/style.scss"; @import "./term-description/style.scss"; @import "./text-columns/style.scss"; @import "./verse/style.scss"; diff --git a/packages/block-library/src/tab/block.json b/packages/block-library/src/tab/block.json index 802c014d5579a..fdabf177c2697 100644 --- a/packages/block-library/src/tab/block.json +++ b/packages/block-library/src/tab/block.json @@ -19,6 +19,5 @@ "reusable": false }, "usesContext": [ "tabs/activeTab" ], - "editorScript": "file:./build/index.js", - "style": "file:./build/style.css" + "style": "wp-block-tab" } diff --git a/packages/block-library/src/tab/edit.js b/packages/block-library/src/tab/edit.js index 45b75750e156a..27757e0dc2af4 100644 --- a/packages/block-library/src/tab/edit.js +++ b/packages/block-library/src/tab/edit.js @@ -1,25 +1,19 @@ /** * WordPress dependencies */ -import { InnerBlocks, RichText, useBlockProps } from '@wordpress/block-editor'; -import { __ } from '@wordpress/i18n'; +import { + InnerBlocks, + useBlockProps, + useInnerBlocksProps, +} from '@wordpress/block-editor'; -export default function Edit( { context } ) { - console.log( context ); - const isActive = context[ 'tabs/activeTab' ]; - return ( -
- -
- ); +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, + } ); + return
; } diff --git a/packages/block-library/src/tab/index.js b/packages/block-library/src/tab/index.js index 724198cd0d54c..966216c39af91 100644 --- a/packages/block-library/src/tab/index.js +++ b/packages/block-library/src/tab/index.js @@ -7,8 +7,6 @@ import edit from './edit'; import save from './save'; // import icon from './icon'; -// import './style.scss'; - const { name } = metadata; export { metadata, name }; diff --git a/packages/block-library/src/tab/style.scss b/packages/block-library/src/tab/style.scss new file mode 100644 index 0000000000000..a2051c5861ef6 --- /dev/null +++ b/packages/block-library/src/tab/style.scss @@ -0,0 +1,8 @@ +.wp-block-tab { + display: none; + + &.is-active { + display: block; + } +} + diff --git a/packages/block-library/src/tabs/block.json b/packages/block-library/src/tabs/block.json index 154ba0e9e6ada..92422411ce30e 100644 --- a/packages/block-library/src/tabs/block.json +++ b/packages/block-library/src/tabs/block.json @@ -10,8 +10,8 @@ "allowedBlocks": [ "core/tab" ], "attributes": { "activeTab": { - "type": "number", - "default": 0 + "type": "string", + "default": "" } }, "providesContext": { @@ -32,6 +32,5 @@ "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 481479b15f055..d0f0f583e8980 100644 --- a/packages/block-library/src/tabs/edit.js +++ b/packages/block-library/src/tabs/edit.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + /** * WordPress dependencies */ @@ -6,11 +11,16 @@ import { useBlockProps, useInnerBlocksProps, store as blockEditorStore, + RichText, } from '@wordpress/block-editor'; -import { useSelect } from '@wordpress/data'; -import { __ } from '@wordpress/i18n'; -import { useState } from '@wordpress/element'; -import clsx from 'clsx'; +import { Button } from '@wordpress/components'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { useEffect } from '@wordpress/element'; + +const initialTabsTemplate = [ + [ 'core/tab', { title: 'Tab 1' } ], + [ 'core/tab', { title: 'Tab 2' } ], +]; export default function Edit( { attributes, clientId, setAttributes } ) { const { activeTab } = attributes; @@ -19,28 +29,77 @@ export default function Edit( { attributes, clientId, setAttributes } ) { [ clientId ] ); - const setActiveTab = ( index ) => { - setAttributes( { activeTab: index } ); + const blockProps = useBlockProps(); + + const innerBlockProps = useInnerBlocksProps( + { + className: 'wp-block-tabs__tab-content', + }, + { + renderAppender: InnerBlocks.ButtonBlockAppender, + template: initialTabsTemplate, + } + ); + + const { __unstableMarkNextChangeAsNotPersistent, updateBlockAttributes } = + useDispatch( blockEditorStore ); + + const setActiveTab = ( tabId ) => { + __unstableMarkNextChangeAsNotPersistent(); + setAttributes( { activeTab: tabId } ); }; + useEffect( () => { + // Initialize the first tab as active when the component mounts. + if ( innerBlocks.length ) { + setActiveTab( innerBlocks[ 0 ].clientId ); + } + }, [] ); // eslint-disable-line react-hooks/exhaustive-deps -- only run effect once when component mounts. + + // if ( ! innerBlocks || innerBlocks.length === 0 ) { + // return null; + // } + return ( -
-
- { innerBlocks.map( ( block, index ) => ( - - ) ) } -
-
- -
+
+
    + { innerBlocks.map( ( block ) => { + const isActive = block.clientId === activeTab; + const tabIndex = isActive ? '0' : '-1'; + + // TODO: Add unique ids and aria attributes for accessibility. + // (Try the anchor generation functionality from the heading block?) + return ( +
  • + +
  • + ); + } ) } +
+ +
); } diff --git a/packages/block-library/src/tabs/index.js b/packages/block-library/src/tabs/index.js index 724198cd0d54c..966216c39af91 100644 --- a/packages/block-library/src/tabs/index.js +++ b/packages/block-library/src/tabs/index.js @@ -7,8 +7,6 @@ import edit from './edit'; import save from './save'; // import icon from './icon'; -// import './style.scss'; - const { name } = metadata; export { metadata, name }; diff --git a/packages/block-library/src/tabs/style.scss b/packages/block-library/src/tabs/style.scss index bfdd07f54461f..84ef2377c87af 100644 --- a/packages/block-library/src/tabs/style.scss +++ b/packages/block-library/src/tabs/style.scss @@ -1,29 +1,6 @@ -.wp-block-tabs { - .wp-block-tabs__tab-buttons { - display: flex; - border-bottom: 1px solid #ccc; - } - - .wp-block-tabs__tab-button { - background: none; - border: none; - padding: 10px 15px; - cursor: pointer; - - &.is-active { - border-bottom: 2px solid #007cba; - } - } - - .wp-block-tabs__content { - padding: 15px 0; - } -} - -.wp-block-tab { - display: none; - - &.is-active { - display: block; - } +.wp-block-tabs__list { + display: flex; + gap: 10px; + list-style: none; + padding: 0; } From aa0a7108b1c7333fc44a88fce3c07d3a818adf15 Mon Sep 17 00:00:00 2001 From: Grant Kinney Date: Mon, 22 Jul 2024 12:29:07 -0500 Subject: [PATCH 05/21] Removes button component from block and initial styles for tab labels --- docs/reference-guides/core-blocks.md | 2 +- packages/block-library/src/tabs/block.json | 4 ++++ packages/block-library/src/tabs/edit.js | 26 ++++++++++++++++------ packages/block-library/src/tabs/style.scss | 22 ++++++++++++++++++ 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 2d191c938c4ff..141da0f13aae8 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -925,7 +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), interactivity, layout (default, ~~allowJustification~~, ~~allowSwitching~~), shadow, spacing (margin, padding), ~~html~~ +- **Supports:** align (full, wide), color (text, ~~background~~), interactivity, layout (default, ~~allowJustification~~, ~~allowSwitching~~), shadow, spacing (margin, padding), ~~html~~ - **Attributes:** activeTab ## Tag Cloud diff --git a/packages/block-library/src/tabs/block.json b/packages/block-library/src/tabs/block.json index 92422411ce30e..49e2b2e551c93 100644 --- a/packages/block-library/src/tabs/block.json +++ b/packages/block-library/src/tabs/block.json @@ -19,6 +19,10 @@ }, "supports": { "align": [ "wide", "full" ], + "color": { + "background": false, + "text": true + }, "html": false, "interactivity": true, "layout": { diff --git a/packages/block-library/src/tabs/edit.js b/packages/block-library/src/tabs/edit.js index d0f0f583e8980..2bc9d781be195 100644 --- a/packages/block-library/src/tabs/edit.js +++ b/packages/block-library/src/tabs/edit.js @@ -13,15 +13,27 @@ import { store as blockEditorStore, RichText, } from '@wordpress/block-editor'; -import { Button } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; import { useEffect } from '@wordpress/element'; -const initialTabsTemplate = [ +const TABS_TEMPLATE = [ [ 'core/tab', { title: 'Tab 1' } ], [ 'core/tab', { title: 'Tab 2' } ], ]; +const ALLOWED_FORMATS = [ + 'core/bold', + 'core/code', + 'core/image', + 'core/italic', + 'core/keyboard', + 'core/language', + 'core/strikethrough', + 'core/subscript', + 'core/superscript', + 'core/text-color', +]; + export default function Edit( { attributes, clientId, setAttributes } ) { const { activeTab } = attributes; const innerBlocks = useSelect( @@ -33,11 +45,11 @@ export default function Edit( { attributes, clientId, setAttributes } ) { const innerBlockProps = useInnerBlocksProps( { - className: 'wp-block-tabs__tab-content', + className: 'wp-block-tabs__content', }, { renderAppender: InnerBlocks.ButtonBlockAppender, - template: initialTabsTemplate, + template: TABS_TEMPLATE, } ); @@ -75,16 +87,16 @@ export default function Edit( { attributes, clientId, setAttributes } ) { className="wp-block-tabs__list-item" role="presentation" > - + ); } ) } diff --git a/packages/block-library/src/tabs/style.scss b/packages/block-library/src/tabs/style.scss index 84ef2377c87af..b4e7b4197e64e 100644 --- a/packages/block-library/src/tabs/style.scss +++ b/packages/block-library/src/tabs/style.scss @@ -2,5 +2,27 @@ display: flex; gap: 10px; list-style: none; + margin: 0; padding: 0; } + +.wp-block-tabs__tab { + box-shadow: none; + border: 0; + border-radius: 0; + background: none; + font-size: var(--wp--preset--font-size--normal, inherit); + margin: 0; + opacity: 0.5; + outline: none; + padding: $grid-unit; + + &.is-active { + border-bottom: currentColor 2px solid; + opacity: 1; + } +} + +.wp-block-tabs__content { + border-top: var(--wp--preset--color--accent) 2px solid; +} From 4a42b99319ce76714ac9f1451dafef70a7674838 Mon Sep 17 00:00:00 2001 From: Grant Kinney Date: Mon, 22 Jul 2024 20:26:03 -0500 Subject: [PATCH 06/21] Adds basic tab inserting and management --- docs/reference-guides/core-blocks.md | 7 +- packages/block-library/src/editor.scss | 1 + packages/block-library/src/tab/block.json | 9 +- packages/block-library/src/tab/edit.js | 16 ++- packages/block-library/src/tab/save.js | 8 +- packages/block-library/src/tab/style.scss | 5 + packages/block-library/src/tabs/block.json | 17 +-- packages/block-library/src/tabs/edit.js | 114 ++++++++++++++------ packages/block-library/src/tabs/editor.scss | 5 + 9 files changed, 118 insertions(+), 64 deletions(-) create mode 100644 packages/block-library/src/tabs/editor.scss diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 141da0f13aae8..832ebe10aa3b0 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 52f3aa64287fa..896f7fe4ab672 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 "./tag-cloud/editor.scss"; @import "./template-part/editor.scss"; @import "./text-columns/editor.scss"; diff --git a/packages/block-library/src/tab/block.json b/packages/block-library/src/tab/block.json index fdabf177c2697..ce258113e1b2c 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 27757e0dc2af4..ac8ac7b5982e2 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 b78ba5bb38bdd..000acdcd4a605 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 a2051c5861ef6..e96d32fb7e6bd 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 49e2b2e551c93..a421044e6d0db 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 2bc9d781be195..ff81724f94bf4 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 (