diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 0e0de9d4f8a507..6d5dedbeb74c4a 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -8,6 +8,51 @@ This page lists the blocks included in the block-library package. +## Accordion Group + +A group of headers and associated expandable content. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/accordion-group)) + +- **Name:** core/accordion-group +- **Experimental:** true +- **Category:** design +- **Allowed Blocks:** core/accordion-item +- **Supports:** align (full, wide), background (backgroundImage, backgroundSize), color (background, gradient, text), interactivity, layout, shadow, spacing (blockGap, margin, padding), ~~html~~ +- **Attributes:** allowedBlocks, autoclose, iconPosition + +## Accordion Header + +Accordion header. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/accordion-header)) + +- **Name:** core/accordion-header +- **Experimental:** true +- **Category:** design +- **Parent:** core/accordion-item +- **Supports:** anchor, border, color (background, gradient, text), interactivity, layout, shadow, spacing (margin, padding), typography (fontSize, textAlign), ~~align~~ +- **Attributes:** icon, iconPosition, level, levelOptions, openByDefault, textAlignment, title + +## Accordion + +A single accordion that displays a header and expandable content. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/accordion-item)) + +- **Name:** core/accordion-item +- **Experimental:** true +- **Category:** design +- **Parent:** core/accordion-group +- **Allowed Blocks:** core/accordion-header, core/accordion-panel +- **Supports:** align (full, wide), color (background, gradient, text), interactivity, layout, shadow, spacing (blockGap, margin) +- **Attributes:** openByDefault + +## Accordion Panel + +Accordion Panel ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/accordion-panel)) + +- **Name:** core/accordion-panel +- **Experimental:** true +- **Category:** design +- **Parent:** core/accordion-item +- **Supports:** border, color (background, gradient, text), interactivity, layout, shadow, spacing (blockGap, margin, padding), typography (fontSize, lineHeight) +- **Attributes:** allowedBlocks, isSelected, openByDefault, templateLock + ## Archives Display a date archive of your posts. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/archives)) diff --git a/lib/blocks.php b/lib/blocks.php index c3fdb26700c58c..e5f2d0620172b8 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -14,6 +14,8 @@ function gutenberg_reregister_core_block_types() { $blocks_dirs = array( __DIR__ . '/../build/block-library/blocks/' => array( 'block_folders' => array( + 'accordion-header', + 'accordion-panel', 'audio', 'button', 'buttons', @@ -45,6 +47,8 @@ function gutenberg_reregister_core_block_types() { 'embed', ), 'block_names' => array( + 'accordion-item.php' => 'core/accordion-item', + 'accordion-group.php' => 'core/accordion-group', 'archives.php' => 'core/archives', 'avatar.php' => 'core/avatar', 'block.php' => 'core/block', diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index 919be2e6e34a45..c6bd99a18bf4c7 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -28,15 +28,18 @@ function gutenberg_enable_experiments() { if ( gutenberg_is_experiment_enabled( 'gutenberg-full-page-client-side-navigation' ) ) { wp_add_inline_script( 'wp-block-library', 'window.__experimentalFullPageClientSideNavigation = true', 'before' ); } + if ( $gutenberg_experiments && array_key_exists( 'gutenberg-zoomed-out-patterns-tab', $gutenberg_experiments ) ) { + wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableZoomedOutPatternsTab = true', 'before' ); + } if ( $gutenberg_experiments && array_key_exists( 'gutenberg-quick-edit-dataviews', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalQuickEditDataViews = true', 'before' ); } + if ( $gutenberg_experiments && array_key_exists( 'gutenberg-block-bindings-ui', $gutenberg_experiments ) ) { + wp_add_inline_script( 'wp-block-editor', 'window.__experimentalBlockBindingsUI = true', 'before' ); + } if ( $gutenberg_experiments && array_key_exists( 'gutenberg-media-processing', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalMediaProcessing = true', 'before' ); } - if ( $gutenberg_experiments && array_key_exists( 'gutenberg-zoom-out-experiment', $gutenberg_experiments ) ) { - wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableZoomOutExperiment = true', 'before' ); - } } add_action( 'admin_init', 'gutenberg_enable_experiments' ); diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 1353ef24c77d89..10d8e34e100ca5 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -31,6 +31,7 @@ "{src,build,build-module}/*/init.js" ], "wpScriptModuleExports": { + "./accordion-group/view": "./build-module/accordion-group/view.js", "./file/view": "./build-module/file/view.js", "./image/view": "./build-module/image/view.js", "./navigation/view": "./build-module/navigation/view.js", diff --git a/packages/block-library/src/accordion-group/block.json b/packages/block-library/src/accordion-group/block.json new file mode 100644 index 00000000000000..572ad2a7afd30c --- /dev/null +++ b/packages/block-library/src/accordion-group/block.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "core/accordion-group", + "title": "Accordion Group", + "category": "design", + "description": "A group of headers and associated expandable content.", + "example": {}, + "__experimental": true, + "supports": { + "html": false, + "align": [ "wide", "full" ], + "background": { + "backgroundImage": true, + "backgroundSize": true, + "__experimentalDefaultControls": { + "backgroundImage": true + } + }, + "color": { + "background": true, + "gradient": true + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "spacing": { + "padding": true, + "margin": [ "top", "bottom" ], + "blockGap": true + }, + "shadow": true, + "layout": true, + "interactivity": true + }, + "attributes": { + "iconPosition": { + "type": "string", + "default": "right" + }, + "autoclose": { + "type": "boolean", + "default": false + }, + "allowedBlocks": { + "type": "array" + } + }, + "allowedBlocks": [ "core/accordion-item" ], + "textdomain": "default", + "style": "wp-block-accordion-group" +} diff --git a/packages/block-library/src/accordion-group/edit.js b/packages/block-library/src/accordion-group/edit.js new file mode 100644 index 00000000000000..96b1ba59622633 --- /dev/null +++ b/packages/block-library/src/accordion-group/edit.js @@ -0,0 +1,49 @@ +/** + * WordPress dependencies + */ +import { + useBlockProps, + useInnerBlocksProps, + InspectorControls, +} from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; +import { PanelBody, ToggleControl } from '@wordpress/components'; + +const ACCORDION_BLOCK_NAME = 'core/accordion-item'; +const ACCORDION_BLOCK = { + name: ACCORDION_BLOCK_NAME, +}; + +export default function Edit( { attributes: { autoclose }, setAttributes } ) { + const blockProps = useBlockProps(); + + const innerBlocksProps = useInnerBlocksProps( blockProps, { + template: [ [ ACCORDION_BLOCK_NAME ], [ ACCORDION_BLOCK_NAME ] ], + defaultBlock: ACCORDION_BLOCK, + directInsert: true, + } ); + + return ( + <> + + + { + setAttributes( { + autoclose: value, + } ); + } } + checked={ autoclose } + help={ __( + 'Automatically close accordions when a new one is opened.' + ) } + /> + + +
+ + ); +} diff --git a/packages/block-library/src/accordion-group/index.js b/packages/block-library/src/accordion-group/index.js new file mode 100644 index 00000000000000..37230fe7d30cd5 --- /dev/null +++ b/packages/block-library/src/accordion-group/index.js @@ -0,0 +1,49 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/components'; +/** + * Internal dependencies + */ +import edit from './edit'; +import save from './save'; +import metadata from './block.json'; +import initBlock from '../utils/init-block'; + +const icon = ( + + + + + + +); + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + icon, + example: {}, + edit, + save, +}; + +export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/accordion-group/index.php b/packages/block-library/src/accordion-group/index.php new file mode 100644 index 00000000000000..281d5b873f1e70 --- /dev/null +++ b/packages/block-library/src/accordion-group/index.php @@ -0,0 +1,59 @@ +next_tag( array( 'class_name' => 'wp-block-accordion-group' ) ) ) { + $p->set_attribute( 'data-wp-interactive', 'core/accordion' ); + $p->set_attribute( 'data-wp-context', '{ "autoclose": ' . $autoclose . ', "isOpen": [] }' ); + + // Only modify content if directives have been set. + $content = $p->get_updated_html(); + } + + return $content; +} + +/** + * Registers the `core/accordion-group` block on server. + * + * @since 6.6.0 + */ +function register_block_core_accordion_group() { + register_block_type_from_metadata( + __DIR__ . '/accordion-group', + array( + 'render_callback' => 'render_block_core_accordion_group', + ) + ); +} +add_action( 'init', 'register_block_core_accordion_group' ); diff --git a/packages/block-library/src/accordion-group/init.js b/packages/block-library/src/accordion-group/init.js new file mode 100644 index 00000000000000..79f0492c2cb2f8 --- /dev/null +++ b/packages/block-library/src/accordion-group/init.js @@ -0,0 +1,6 @@ +/** + * Internal dependencies + */ +import { init } from './'; + +export default init(); diff --git a/packages/block-library/src/accordion-group/save.js b/packages/block-library/src/accordion-group/save.js new file mode 100644 index 00000000000000..2b03d9bbcd0878 --- /dev/null +++ b/packages/block-library/src/accordion-group/save.js @@ -0,0 +1,23 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; +/** + * WordPress dependencies + */ +import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; + +export default function save( { attributes } ) { + const { iconPosition } = attributes; + const blockProps = useBlockProps.save(); + const className = clsx( + { + 'icon-position-left': iconPosition === 'left', + }, + blockProps.className + ); + + return ( +
+ ); +} diff --git a/packages/block-library/src/accordion-group/style.scss b/packages/block-library/src/accordion-group/style.scss new file mode 100644 index 00000000000000..b7aa3c9185cb51 --- /dev/null +++ b/packages/block-library/src/accordion-group/style.scss @@ -0,0 +1,94 @@ +.wp-block-accordion-item { + display: grid; + grid-template-rows: max-content 0fr; +} + +.wp-block-accordion-item.is-open { + grid-template-rows: max-content 1fr; +} + +.wp-block-accordion-item .accordion-item__heading { + color: inherit; + padding: 0; + margin: 0; +} + +.accordion-item__toggle { + font-family: inherit; + font-size: inherit; + font-weight: inherit; + line-height: inherit; + letter-spacing: inherit; + text-transform: inherit; + text-decoration: inherit; + word-spacing: inherit; + background: none; + border: none; + color: inherit; + padding: var(--wp--preset--spacing--20, 1em) 0; + cursor: pointer; + outline: none; + display: flex; + align-items: center; + text-align: inherit; + position: relative; + width: 100%; +} + +.accordion-item__toggle > span { + width: 100%; +} + +.is-layout-flow > .wp-block-accordion-panel, +.wp-block-accordion-panel { + overflow: hidden; + margin: 0; +} + +.accordion-panel__wrapper { + padding-bottom: var(--wp--preset--spacing--20, 1em); +} + +/* No icon block style */ +.is-style-no-icon .accordion-item__toggle-icon { + background-color: unset; +} + +.wp-block-accordion-header.icon-position-left .accordion-item__toggle { + /* stylelint-disable-next-line declaration-property-value-allowed-list -- This should be refactored to not use the row-reverse value. */ + flex-direction: row-reverse; +} + +.accordion-item__toggle:focus-visible { + outline: 2px solid -webkit-focus-ring-color; + outline-offset: 2px; +} + +/* Add transitions only for users who do not prefer reduced motion */ +@media (prefers-reduced-motion: no-preference) { + .wp-block-accordion-item .accordion-item__toggle-icon { + transition: transform 0.2s ease-in-out; + } + + .wp-block-accordion-item { + transition: grid-template-rows 0.3s ease-out; + } +} + +.is-open { + .accordion-item__toggle-icon.has-icon-plus { + transform: rotate(45deg); + } + .accordion-item__toggle-icon.has-icon-chevron { + transform: rotate(-180deg); + } + .accordion-item__toggle-icon.has-icon-circlePlus { + transform: rotate(45deg); + } + .accordion-item__toggle-icon.has-icon-caret { + transform: rotate(-180deg); + } + .accordion-item__toggle-icon.has-icon-chevronRight { + transform: rotate(90deg); + } +} diff --git a/packages/block-library/src/accordion-group/view.js b/packages/block-library/src/accordion-group/view.js new file mode 100644 index 00000000000000..cf1639710bdd33 --- /dev/null +++ b/packages/block-library/src/accordion-group/view.js @@ -0,0 +1,38 @@ +/** + * WordPress dependencies + */ +import { store, getContext } from '@wordpress/interactivity'; + +const { state } = store( 'core/accordion', { + state: { + get isOpen() { + const { isOpen, id } = getContext(); + return isOpen.includes( id ); + }, + }, + actions: { + toggle: () => { + const context = getContext(); + const { id, autoclose } = context; + + if ( autoclose ) { + context.isOpen = state.isOpen ? [] : [ id ]; + } else if ( state.isOpen ) { + context.isOpen = context.isOpen.filter( + ( item ) => item !== id + ); + } else { + context.isOpen.push( id ); + } + }, + }, + callbacks: { + initIsOpen: () => { + const context = getContext(); + const { id, openByDefault } = context; + if ( openByDefault ) { + context.isOpen.push( id ); + } + }, + }, +} ); diff --git a/packages/block-library/src/accordion-header/block.json b/packages/block-library/src/accordion-header/block.json new file mode 100644 index 00000000000000..6173e356dca9e0 --- /dev/null +++ b/packages/block-library/src/accordion-header/block.json @@ -0,0 +1,98 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "core/accordion-header", + "version": "0.1.0", + "title": "Accordion Header", + "category": "design", + "description": "Accordion header.", + "example": {}, + "__experimental": true, + "parent": [ "core/accordion-item" ], + "supports": { + "anchor": true, + "color": { + "background": true, + "gradient": true + }, + "align": false, + "border": true, + "interactivity": true, + "spacing": { + "padding": true, + "margin": [ "top", "bottom" ], + "__experimentalDefaultControls": { + "padding": true, + "margin": true + } + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "typography": { + "textAlign": true, + "fontSize": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true, + "fontFamily": true + } + }, + "shadow": true, + "layout": true + }, + "attributes": { + "openByDefault": { + "type": "boolean", + "default": false + }, + "title": { + "type": "rich-text", + "source": "rich-text", + "selector": "span" + }, + "level": { + "type": "number", + "default": 3 + }, + "levelOptions": { + "type": "array" + }, + "textAlignment": { + "type": "string", + "default": "left" + }, + "icon": { + "type": [ "string", "boolean" ], + "enum": [ + "plus", + "chevron", + "chevronRight", + "caret", + "circlePlus", + false + ], + "default": "plus" + }, + "iconPosition": { + "type": "string", + "enum": [ "left", "right" ], + "default": "right" + } + }, + "textdomain": "default" +} diff --git a/packages/block-library/src/accordion-header/edit.js b/packages/block-library/src/accordion-header/edit.js new file mode 100644 index 00000000000000..e8cc9cbc526489 --- /dev/null +++ b/packages/block-library/src/accordion-header/edit.js @@ -0,0 +1,185 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + useBlockProps, + __experimentalUseBorderProps as useBorderProps, + __experimentalUseColorProps as useColorProps, + __experimentalGetSpacingClassesAndStyles as useSpacingProps, + __experimentalGetShadowClassesAndStyles as useShadowProps, + BlockControls, + HeadingLevelDropdown, + RichText, + InspectorControls, +} from '@wordpress/block-editor'; +import { + PanelBody, + ToolbarGroup, + __experimentalToggleGroupControl as ToggleGroupControl, + __experimentalToggleGroupControlOption as ToggleGroupControlOption, + __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, +} from '@wordpress/components'; +/** + * Internal dependencies + */ +import { + caret, + chevron, + chevronRight, + circlePlus, + plus, +} from '../accordion-item/icons'; + +const ICONS = { + plus, + circlePlus, + chevron, + chevronRight, + caret, +}; + +export default function Edit( { attributes, setAttributes } ) { + const { level, title, textAlign, icon, iconPosition, levelOptions } = + attributes; + const TagName = 'h' + level; + + const blockProps = useBlockProps(); + const borderProps = useBorderProps( attributes ); + const colorProps = useColorProps( attributes ); + const spacingProps = useSpacingProps( attributes ); + const shadowProps = useShadowProps( attributes ); + + const Icon = ICONS[ icon ]; + + return ( + <> + + + + setAttributes( { level: newLevel } ) + } + /> + + + + + + setAttributes( { icon: value } ) + } + > + + + + + + + + { + setAttributes( { iconPosition: value } ); + } } + > + + + + + + + + + + ); +} diff --git a/packages/block-library/src/accordion-header/index.js b/packages/block-library/src/accordion-header/index.js new file mode 100644 index 00000000000000..5858f025c228dd --- /dev/null +++ b/packages/block-library/src/accordion-header/index.js @@ -0,0 +1,42 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/components'; +/** + * Internal dependencies + */ +import edit from './edit'; +import save from './save'; +import metadata from './block.json'; +import initBlock from '../utils/init-block'; + +const icon = ( + + + + +); + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + icon, + example: {}, + edit, + save, +}; + +export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/accordion-header/init.js b/packages/block-library/src/accordion-header/init.js new file mode 100644 index 00000000000000..79f0492c2cb2f8 --- /dev/null +++ b/packages/block-library/src/accordion-header/init.js @@ -0,0 +1,6 @@ +/** + * Internal dependencies + */ +import { init } from './'; + +export default init(); diff --git a/packages/block-library/src/accordion-header/save.js b/packages/block-library/src/accordion-header/save.js new file mode 100644 index 00000000000000..6ce0b6f3e49fb6 --- /dev/null +++ b/packages/block-library/src/accordion-header/save.js @@ -0,0 +1,89 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; +/** + * WordPress dependencies + */ +import { + useBlockProps, + __experimentalGetBorderClassesAndStyles as getBorderClassesAndStyles, + __experimentalGetColorClassesAndStyles as getColorClassesAndStyles, + __experimentalGetSpacingClassesAndStyles as getSpacingClassesAndStyles, + __experimentalGetShadowClassesAndStyles as getShadowClassesAndStyles, + RichText, +} from '@wordpress/block-editor'; +/** + * Internal dependencies + */ +import { + caret, + chevron, + chevronRight, + circlePlus, + plus, +} from '../accordion-item/icons'; + +const ICONS = { + plus, + circlePlus, + chevron, + chevronRight, + caret, +}; + +export default function save( { attributes } ) { + const { level, title, iconPosition, textAlign, icon } = attributes; + const TagName = 'h' + level; + + const blockProps = useBlockProps.save(); + const borderProps = getBorderClassesAndStyles( attributes ); + const colorProps = getColorClassesAndStyles( attributes ); + const spacingProps = getSpacingClassesAndStyles( attributes ); + const shadowProps = getShadowClassesAndStyles( attributes ); + + const Icon = ICONS[ icon ]; + + return ( + + + + ); +} diff --git a/packages/block-library/src/accordion-item/block.json b/packages/block-library/src/accordion-item/block.json new file mode 100644 index 00000000000000..8ac65ea0190bdf --- /dev/null +++ b/packages/block-library/src/accordion-item/block.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "core/accordion-item", + "version": "0.1.0", + "title": "Accordion", + "category": "design", + "description": "A single accordion that displays a header and expandable content.", + "example": {}, + "__experimental": true, + "parent": [ "core/accordion-group" ], + "allowedBlocks": [ "core/accordion-header", "core/accordion-panel" ], + "supports": { + "align": [ "wide", "full" ], + "color": { + "background": true, + "gradient": true + }, + "interactivity": true, + "spacing": { + "margin": [ "top", "bottom" ], + "blockGap": true + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "shadow": true, + "layout": true + }, + "attributes": { + "openByDefault": { + "type": "boolean", + "default": false + } + }, + "textdomain": "default" +} diff --git a/packages/block-library/src/accordion-item/edit.js b/packages/block-library/src/accordion-item/edit.js new file mode 100644 index 00000000000000..1c9aa6538de100 --- /dev/null +++ b/packages/block-library/src/accordion-item/edit.js @@ -0,0 +1,110 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + useBlockProps, + useInnerBlocksProps, + InspectorControls, + store as blockEditorStore, +} from '@wordpress/block-editor'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { useEffect } from '@wordpress/element'; +import { PanelBody, ToggleControl } from '@wordpress/components'; +/** + * External dependencies + */ +import clsx from 'clsx'; + +export default function Edit( { + attributes: { openByDefault }, + clientId, + setAttributes, +} ) { + const isSelected = useSelect( + ( select ) => { + const { isBlockSelected, hasSelectedInnerBlock } = + select( blockEditorStore ); + return ( + isBlockSelected( clientId ) || + hasSelectedInnerBlock( clientId, true ) + ); + }, + [ clientId ] + ); + + const getBlockOrder = useSelect( + ( select ) => select( blockEditorStore ).getBlockOrder, + [] + ); + + const contentBlockClientId = getBlockOrder( clientId )[ 1 ]; + const { updateBlockAttributes, __unstableMarkNextChangeAsNotPersistent } = + useDispatch( blockEditorStore ); + + useEffect( () => { + if ( contentBlockClientId ) { + __unstableMarkNextChangeAsNotPersistent(); + updateBlockAttributes( contentBlockClientId, { + isSelected, + } ); + } + }, [ + isSelected, + contentBlockClientId, + __unstableMarkNextChangeAsNotPersistent, + updateBlockAttributes, + ] ); + + const blockProps = useBlockProps(); + const innerBlocksProps = useInnerBlocksProps( + { + ...blockProps, + className: clsx( blockProps.className, { + 'is-open': openByDefault || isSelected, + } ), + }, + { + template: [ + [ 'core/accordion-header', {} ], + [ + 'core/accordion-panel', + { + isSelected: true, + openByDefault, + }, + ], + ], + templateLock: 'all', + directInsert: true, + } + ); + + return ( + <> + + + { + setAttributes( { + openByDefault: value, + } ); + if ( contentBlockClientId ) { + updateBlockAttributes( contentBlockClientId, { + openByDefault: value, + } ); + } + } } + checked={ openByDefault } + help={ __( + 'Accordion content will be displayed by default.' + ) } + /> + + +
+ + ); +} diff --git a/packages/block-library/src/accordion-item/icons.js b/packages/block-library/src/accordion-item/icons.js new file mode 100644 index 00000000000000..1a5ae0469c9882 --- /dev/null +++ b/packages/block-library/src/accordion-item/icons.js @@ -0,0 +1,121 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/components'; + +export const chevron = ( { width, height } ) => { + return ( + + + + ); +}; + +export const plus = ( { width, height } ) => { + return ( + + + + ); +}; + +export const circlePlus = ( { width, height } ) => { + return ( + + + + + ); +}; + +export const circleMinus = ( { width, height } ) => { + return ( + + + + + ); +}; + +export const caret = ( { width, height } ) => { + return ( + + + + ); +}; + +export const chevronRight = ( { width, height } ) => { + return ( + + + + ); +}; diff --git a/packages/block-library/src/accordion-item/index.js b/packages/block-library/src/accordion-item/index.js new file mode 100644 index 00000000000000..f54dd6be83bdc7 --- /dev/null +++ b/packages/block-library/src/accordion-item/index.js @@ -0,0 +1,54 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/components'; +/** + * Internal dependencies + */ +import edit from './edit'; +import save from './save'; +import metadata from './block.json'; +import initBlock from '../utils/init-block'; + +const icon = ( + + + + + + +); + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + icon, + example: {}, + edit, + save, +}; + +export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/accordion-item/index.php b/packages/block-library/src/accordion-item/index.php new file mode 100644 index 00000000000000..73dbacda63c1cb --- /dev/null +++ b/packages/block-library/src/accordion-item/index.php @@ -0,0 +1,73 @@ + function () { + $context = wp_interactivity_get_context(); + return $context['openByDefault']; + }, + ) + ); + + if ( $p->next_tag( array( 'class_name' => 'wp-block-accordion-item' ) ) ) { + $open_by_default = $attributes['openByDefault'] ? 'true' : 'false'; + $p->set_attribute( 'data-wp-context', '{ "id": "' . $unique_id . '", "openByDefault": ' . $open_by_default . ' }' ); + $p->set_attribute( 'data-wp-class--is-open', 'state.isOpen' ); + $p->set_attribute( 'data-wp-init', 'callbacks.initIsOpen' ); + + if ( $p->next_tag( array( 'class_name' => 'accordion-item__toggle' ) ) ) { + $p->set_attribute( 'data-wp-on--click', 'actions.toggle' ); + $p->set_attribute( 'id', $unique_id ); + $p->set_attribute( 'aria-controls', $unique_id . '-panel' ); + $p->set_attribute( 'data-wp-bind--aria-expanded', 'state.isOpen' ); + + if ( $p->next_tag( array( 'class_name' => 'wp-block-accordion-panel' ) ) ) { + $p->set_attribute( 'id', $unique_id . '-panel' ); + $p->set_attribute( 'aria-labelledby', $unique_id ); + $p->set_attribute( 'data-wp-bind--inert', '!state.isOpen' ); + + // Only modify content if all directives have been set. + $content = $p->get_updated_html(); + } + } + } + + return $content; +} + +/** + * Registers the `core/accordion-item` block on server. + * + * @since 6.6.0 + */ +function register_block_core_accordion_item() { + register_block_type_from_metadata( + __DIR__ . '/accordion-item', + array( + 'render_callback' => 'block_core_accordion_item_render', + ) + ); +} +add_action( 'init', 'register_block_core_accordion_item' ); diff --git a/packages/block-library/src/accordion-item/init.js b/packages/block-library/src/accordion-item/init.js new file mode 100644 index 00000000000000..79f0492c2cb2f8 --- /dev/null +++ b/packages/block-library/src/accordion-item/init.js @@ -0,0 +1,6 @@ +/** + * Internal dependencies + */ +import { init } from './'; + +export default init(); diff --git a/packages/block-library/src/accordion-item/save.js b/packages/block-library/src/accordion-item/save.js new file mode 100644 index 00000000000000..04d95466593c70 --- /dev/null +++ b/packages/block-library/src/accordion-item/save.js @@ -0,0 +1,25 @@ +/** + * WordPress dependencies + */ +import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; +/** + * External dependencies + */ +import clsx from 'clsx'; + +export default function save( { attributes } ) { + const { openByDefault } = attributes; + const blockProps = useBlockProps.save(); + const className = clsx( + { + 'is-open': openByDefault, + }, + blockProps.className + ); + const innerBlocksProps = useInnerBlocksProps.save( { + ...blockProps, + className, + } ); + + return
; +} diff --git a/packages/block-library/src/accordion-panel/block.json b/packages/block-library/src/accordion-panel/block.json new file mode 100644 index 00000000000000..57c14111398ab7 --- /dev/null +++ b/packages/block-library/src/accordion-panel/block.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "core/accordion-panel", + "version": "0.1.0", + "title": "Accordion Panel", + "category": "design", + "description": "Accordion Panel", + "example": {}, + "__experimental": true, + "parent": [ "core/accordion-item" ], + "supports": { + "color": { + "background": true, + "gradient": true + }, + "border": true, + "interactivity": true, + "spacing": { + "padding": true, + "margin": [ "top", "bottom" ], + "blockGap": true, + "__experimentalDefaultControls": { + "padding": true, + "blockGap": true + } + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "shadow": true, + "layout": true + }, + "attributes": { + "allowedBlocks": { + "type": "array" + }, + "templateLock": { + "type": [ "string", "boolean" ], + "enum": [ "all", "insert", "contentOnly", false ], + "default": false + }, + "openByDefault": { + "type": "boolean", + "default": false + }, + "isSelected": { + "type": "boolean", + "default": false + } + }, + "textdomain": "default" +} diff --git a/packages/block-library/src/accordion-panel/edit.js b/packages/block-library/src/accordion-panel/edit.js new file mode 100644 index 00000000000000..6f708b33cefb0c --- /dev/null +++ b/packages/block-library/src/accordion-panel/edit.js @@ -0,0 +1,61 @@ +/** + * WordPress dependencies + */ +import { + useBlockProps, + useInnerBlocksProps, + __experimentalUseBorderProps as useBorderProps, + __experimentalUseColorProps as useColorProps, + __experimentalGetSpacingClassesAndStyles as useSpacingProps, + __experimentalGetShadowClassesAndStyles as useShadowProps, +} from '@wordpress/block-editor'; +/** + * External dependencies + */ +import clsx from 'clsx'; + +export default function Edit( { attributes } ) { + const { allowedBlocks, templateLock, openByDefault, isSelected } = + attributes; + const borderProps = useBorderProps( attributes ); + const colorProps = useColorProps( attributes ); + const spacingProps = useSpacingProps( attributes ); + const shadowProps = useShadowProps( attributes ); + + const blockProps = useBlockProps(); + const innerBlocksProps = useInnerBlocksProps( + { + className: 'accordion-content__wrapper', + style: { + ...spacingProps.style, + }, + }, + { + allowedBlocks, + template: [ [ 'core/paragraph', {} ] ], + templateLock, + } + ); + + return ( +
+
+
+ ); +} diff --git a/packages/block-library/src/accordion-panel/index.js b/packages/block-library/src/accordion-panel/index.js new file mode 100644 index 00000000000000..cd00c2d9413f6f --- /dev/null +++ b/packages/block-library/src/accordion-panel/index.js @@ -0,0 +1,41 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/components'; +/** + * Internal dependencies + */ +import edit from './edit'; +import save from './save'; +import metadata from './block.json'; +import initBlock from '../utils/init-block'; + +const icon = ( + + + +); + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + icon, + example: {}, + edit, + save, +}; + +export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/accordion-panel/init.js b/packages/block-library/src/accordion-panel/init.js new file mode 100644 index 00000000000000..79f0492c2cb2f8 --- /dev/null +++ b/packages/block-library/src/accordion-panel/init.js @@ -0,0 +1,6 @@ +/** + * Internal dependencies + */ +import { init } from './'; + +export default init(); diff --git a/packages/block-library/src/accordion-panel/save.js b/packages/block-library/src/accordion-panel/save.js new file mode 100644 index 00000000000000..7ebbffc2cfd42e --- /dev/null +++ b/packages/block-library/src/accordion-panel/save.js @@ -0,0 +1,51 @@ +/** + * WordPress dependencies + */ +import { + InnerBlocks, + useBlockProps, + __experimentalGetBorderClassesAndStyles as getBorderClassesAndStyles, + __experimentalGetColorClassesAndStyles as getColorClassesAndStyles, + __experimentalGetSpacingClassesAndStyles as getSpacingClassesAndStyles, + __experimentalGetShadowClassesAndStyles as getShadowClassesAndStyles, +} from '@wordpress/block-editor'; +/** + * External dependencies + */ +import clsx from 'clsx'; + +export default function save( { attributes } ) { + const blockProps = useBlockProps.save(); + const borderProps = getBorderClassesAndStyles( attributes ); + const colorProps = getColorClassesAndStyles( attributes ); + const spacingProps = getSpacingClassesAndStyles( attributes ); + const shadowProps = getShadowClassesAndStyles( attributes ); + + return ( +
+
+ +
+
+ ); +} diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 56365c87a268fd..7acea9ef85abe7 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -20,6 +20,10 @@ import { // production build to make the final bundle smaller. // // See https://github.com/WordPress/gutenberg/pull/40655 for more context. +import * as accordionGroup from './accordion-group'; +import * as accordionItem from './accordion-item'; +import * as accordionHeader from './accordion-header'; +import * as accordionPanel from './accordion-panel'; import * as archives from './archives'; import * as avatar from './avatar'; import * as audio from './audio'; @@ -232,6 +236,14 @@ const getAllBlocks = () => { queryTitle, postAuthorBiography, ]; + + if ( window?.__experimentalEnableBlockExperiments ) { + blocks.push( accordionGroup ); + blocks.push( accordionItem ); + blocks.push( accordionHeader ); + blocks.push( accordionPanel ); + } + if ( window?.__experimentalEnableFormBlocks ) { blocks.push( form ); blocks.push( formInput ); diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index a8819c2084dc2e..bb3957eabef8dd 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -1,3 +1,4 @@ +@import "./accordion-group/style.scss"; @import "./archives/style.scss"; @import "./avatar/style.scss"; @import "./audio/style.scss";