diff --git a/packages/customize-widgets/src/components/block-inspector-button/index.js b/packages/customize-widgets/src/components/block-inspector-button/index.js new file mode 100644 index 0000000000000..c68c2f46c7468 --- /dev/null +++ b/packages/customize-widgets/src/components/block-inspector-button/index.js @@ -0,0 +1,38 @@ +/** + * WordPress dependencies + */ +import { useMemo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { MenuItem } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { store as blockEditorStore } from '@wordpress/block-editor'; + +function BlockInspectorButton( { inspector, closeMenu, ...props } ) { + const selectedBlockClientId = useSelect( + ( select ) => select( blockEditorStore ).getSelectedBlockClientId(), + [] + ); + + const selectedBlock = useMemo( + () => document.getElementById( `block-${ selectedBlockClientId }` ), + [ selectedBlockClientId ] + ); + + return ( + { + // Open the inspector. + inspector.open( { + returnFocusWhenClose: selectedBlock, + } ); + // Then close the dropdown menu. + closeMenu(); + } } + { ...props } + > + { __( 'Show more settings' ) } + + ); +} + +export default BlockInspectorButton; diff --git a/packages/customize-widgets/src/components/block-inspector-button/style.scss b/packages/customize-widgets/src/components/block-inspector-button/style.scss new file mode 100644 index 0000000000000..b9a4f0f85d13a --- /dev/null +++ b/packages/customize-widgets/src/components/block-inspector-button/style.scss @@ -0,0 +1,21 @@ +#customize-theme-controls .customize-pane-child.accordion-section-content.customize-widgets-layout__inspector { + background: $white; + box-sizing: border-box; + + * { + box-sizing: inherit; + } + + .block-editor-block-inspector { + margin: -$grid-unit-15; + + // To override the style in block-editor/block-inspector. + h3 { + margin-bottom: 0; + } + } +} + +#customize-theme-controls .customize-pane-child.control-section-sidebar.is-sub-section-open { + transform: translateX(-100%); +} diff --git a/packages/customize-widgets/src/components/inspector/block-inspector-button.js b/packages/customize-widgets/src/components/inspector/block-inspector-button.js deleted file mode 100644 index bbf45103063f6..0000000000000 --- a/packages/customize-widgets/src/components/inspector/block-inspector-button.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { MenuItem } from '@wordpress/components'; - -function BlockInspectorButton( { isInspectorOpened = false, ...props } ) { - const label = isInspectorOpened - ? __( 'Hide more settings' ) - : __( 'Show more settings' ); - - return { label }; -} - -export default BlockInspectorButton; diff --git a/packages/customize-widgets/src/components/inspector/index.js b/packages/customize-widgets/src/components/inspector/index.js deleted file mode 100644 index 6a362ab9a338f..0000000000000 --- a/packages/customize-widgets/src/components/inspector/index.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies - */ -import { useEffect, useRef } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; -import { - BlockInspector, - store as blockEditorStore, -} from '@wordpress/block-editor'; -import { __ } from '@wordpress/i18n'; -import { useInstanceId } from '@wordpress/compose'; - -export { default as BlockInspectorButton } from './block-inspector-button'; - -function InspectorSectionMeta( { closeInspector, inspectorTitleId } ) { - return ( -
-
- -

- - { __( 'Customizing ▸ Widgets' ) } - - { __( 'Block Settings' ) } -

-
-
- ); -} - -export default function Inspector( { - isOpened, - isAnimating, - setInspectorOpenState, -} ) { - const selectedBlockClientId = useSelect( ( select ) => - select( blockEditorStore ).getSelectedBlockClientId() - ); - const selectedBlockRef = useRef(); - - useEffect( () => { - selectedBlockRef.current = document.querySelector( - `[data-block="${ selectedBlockClientId }"]` - ); - }, [ selectedBlockClientId ] ); - - const inspectorTitleId = useInstanceId( - Inspector, - 'customize-widgets-block-settings' - ); - - useEffect( () => { - const openedSidebarSection = document.querySelector( - '.control-section-sidebar.open' - ); - - if ( isOpened ) { - openedSidebarSection.classList.add( 'is-inspector-open' ); - } else { - openedSidebarSection.classList.remove( 'is-inspector-open' ); - } - - // In case the "transitionend" event for some reasons doesn't fire. - // (Like when it's set to "display: none", or when the transition property is removed.) - // See https://github.com/w3c/csswg-drafts/issues/3043 - const timer = setTimeout( () => { - setInspectorOpenState( 'TRANSITION_END' ); - }, 180 ); - - return () => { - openedSidebarSection.classList.remove( 'is-inspector-open' ); - clearTimeout( timer ); - }; - }, [ isOpened, setInspectorOpenState ] ); - - return ( -
{ - setInspectorOpenState( 'TRANSITION_END' ); - } } - > - { - setInspectorOpenState( 'CLOSE' ); - - // Wait a tick so that the block editor can transition back from being hidden. - window.requestAnimationFrame( () => { - selectedBlockRef.current?.focus(); - } ); - } } - inspectorTitleId={ inspectorTitleId } - /> - - -
- ); -} diff --git a/packages/customize-widgets/src/components/inspector/style.scss b/packages/customize-widgets/src/components/inspector/style.scss deleted file mode 100644 index 42b284a9b466a..0000000000000 --- a/packages/customize-widgets/src/components/inspector/style.scss +++ /dev/null @@ -1,25 +0,0 @@ -// To override the style in block-editor/block-inspector. -.block-editor-block-inspector h3 { - margin-bottom: 0; -} - -#customize-theme-controls .customize-pane-child.accordion-section-content.customize-widgets-layout__inspector { - background: $white; - box-sizing: border-box; - - * { - box-sizing: inherit; - } - - .block-editor-block-inspector { - margin: -$grid-unit-15; - } -} - -#customize-theme-controls .customize-pane-child.accordion-section-content.control-section-sidebar.open { - transition: 0.18s transform cubic-bezier(0.645, 0.045, 0.355, 1); /* easeInOutCubic */ - - &.is-inspector-open { - transform: translateX(-100%); - } -} diff --git a/packages/customize-widgets/src/components/sidebar-block-editor/index.js b/packages/customize-widgets/src/components/sidebar-block-editor/index.js index cddd59ce80096..1b9c0660bbc35 100644 --- a/packages/customize-widgets/src/components/sidebar-block-editor/index.js +++ b/packages/customize-widgets/src/components/sidebar-block-editor/index.js @@ -1,11 +1,12 @@ /** * WordPress dependencies */ -import { useReducer, createPortal, useMemo } from '@wordpress/element'; +import { createPortal, useMemo } from '@wordpress/element'; import { BlockEditorProvider, BlockList, BlockSelectionClearer, + BlockInspector, ObserveTyping, WritingFlow, BlockEditorKeyboardShortcuts, @@ -20,39 +21,13 @@ import { /** * Internal dependencies */ -import Inspector, { BlockInspectorButton } from '../inspector'; +import BlockInspectorButton from '../block-inspector-button'; import Header from '../header'; import useSidebarBlockEditor from './use-sidebar-block-editor'; import useInserter from '../inserter/use-inserter'; -const inspectorOpenStateReducer = ( state, action ) => { - switch ( action ) { - case 'OPEN': - return { - open: true, - busy: true, - }; - case 'TRANSITION_END': - return { - ...state, - busy: false, - }; - case 'CLOSE': - return { - open: false, - busy: true, - }; - default: - throw new Error( 'Unexpected action' ); - } -}; - -export default function SidebarBlockEditor( { sidebar, inserter } ) { +export default function SidebarBlockEditor( { sidebar, inserter, inspector } ) { const [ blocks, onInput, onChange ] = useSidebarBlockEditor( sidebar ); - const [ - { open: isInspectorOpened, busy: isInspectorAnimating }, - setInspectorOpenState, - ] = useReducer( inspectorOpenStateReducer, { open: false, busy: false } ); const parentContainer = document.getElementById( 'customize-theme-controls' ); @@ -69,52 +44,46 @@ export default function SidebarBlockEditor( { sidebar, inserter } ) { - + { createPortal( - , - parentContainer + // This is a temporary hack to prevent button component inside + // from submitting form when type="button" is not specified. +
event.preventDefault() }> + + , + inspector.contentContainer[ 0 ] ) } <__experimentalBlockSettingsMenuFirstItem> { ( { onClose } ) => ( { - // Open the inspector, - setInspectorOpenState( 'OPEN' ); - // Then close the dropdown menu. - onClose(); - } } + inspector={ inspector } + closeMenu={ onClose } /> ) } diff --git a/packages/customize-widgets/src/controls/inspector-section.js b/packages/customize-widgets/src/controls/inspector-section.js new file mode 100644 index 0000000000000..7c74c1085b214 --- /dev/null +++ b/packages/customize-widgets/src/controls/inspector-section.js @@ -0,0 +1,58 @@ +const { + wp: { customize }, +} = window; + +class InspectorSection extends customize.Section { + constructor( id, options ) { + super( id, options ); + + this.parentSection = options.parentSection; + + this.returnFocusWhenClose = null; + } + ready() { + this.contentContainer[ 0 ].classList.add( + 'customize-widgets-layout__inspector' + ); + } + onChangeExpanded( expanded, args ) { + super.onChangeExpanded( expanded, args ); + + if ( this.parentSection && ! args.unchanged ) { + if ( expanded ) { + this.parentSection.collapse( { + manualTransition: true, + } ); + } else { + this.parentSection.expand( { + manualTransition: true, + completeCallback: () => { + // Return focus after finishing the transition. + if ( + this.returnFocusWhenClose && + ! this.contentContainer[ 0 ].contains( + this.returnFocusWhenClose + ) + ) { + this.returnFocusWhenClose.focus(); + } + }, + } ); + } + } + } + open( { returnFocusWhenClose } = {} ) { + this.returnFocusWhenClose = returnFocusWhenClose; + + this.expand( { + allowMultiple: true, + } ); + } + close() { + this.collapse( { + allowMultiple: true, + } ); + } +} + +export default InspectorSection; diff --git a/packages/customize-widgets/src/controls/sidebar-control.js b/packages/customize-widgets/src/controls/sidebar-control.js index 32d3fe0823894..f10789bf15967 100644 --- a/packages/customize-widgets/src/controls/sidebar-control.js +++ b/packages/customize-widgets/src/controls/sidebar-control.js @@ -20,28 +20,36 @@ class SidebarControl extends customize.Control { ready() { this.inserter = new InserterOuterSection( inserterId, {} ); customize.section.add( this.inserter ); + + this.sectionInstance = customize.section( this.section() ); + + this.inspector = this.sectionInstance.inspector; + this.render(); } - onChangeExpanded() { - this.render(); - } - expanded() { - return customize.section( this.section() ).expanded(); + onChangeSectionExpanded( expanded, args ) { + if ( ! args.unchanged ) { + // Close the inserter when the section collapses. + if ( ! expanded ) { + this.inserter.close(); + } + + this.render(); + } } render() { - if ( this.expanded() ) { + if ( this.sectionInstance.expanded() ) { render( , this.container[ 0 ] ); - } else { + } else if ( ! this.sectionInstance.hasSubSectionOpened() ) { + // Don't unmount the node when the sub section (inspector) is opened. unmountComponentAtNode( this.container[ 0 ] ); - - // Close the inserter when the section collapses. - this.inserter.close(); } } } diff --git a/packages/customize-widgets/src/controls/sidebar-section.js b/packages/customize-widgets/src/controls/sidebar-section.js index 3cc165f4c3fbd..b91676c3d194b 100644 --- a/packages/customize-widgets/src/controls/sidebar-section.js +++ b/packages/customize-widgets/src/controls/sidebar-section.js @@ -1,14 +1,72 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import InspectorSection from './inspector-section'; + const { wp: { customize }, } = window; +const getInspectorSectionId = ( sidebarId ) => + `widgets-inspector-${ sidebarId }`; + class SidebarSection extends customize.Section { + ready() { + this.inspector = new InspectorSection( + getInspectorSectionId( this.id ), + { + title: __( 'Block Settings' ), + parentSection: this, + customizeAction: [ + __( 'Customizing' ), + __( 'Widgets' ), + this.params.title, + ].join( ' ▸ ' ), + } + ); + customize.section.add( this.inspector ); + } + hasSubSectionOpened() { + return this.inspector.expanded(); + } onChangeExpanded( expanded, args ) { - super.onChangeExpanded( expanded, args ); + if ( args.manualTransition ) { + if ( expanded ) { + this.contentContainer.addClass( [ 'busy', 'open' ] ); + this.contentContainer.removeClass( 'is-sub-section-open' ); + this.contentContainer + .closest( '.wp-full-overlay' ) + .addClass( 'section-open' ); + this.contentContainer.one( 'transitionend', () => { + this.contentContainer.removeClass( 'busy' ); + args.completeCallback?.(); + } ); + } else { + this.contentContainer.addClass( [ + 'busy', + 'is-sub-section-open', + ] ); + this.contentContainer + .closest( '.wp-full-overlay' ) + .addClass( 'section-open' ); + this.contentContainer.removeClass( 'open' ); + this.contentContainer.one( 'transitionend', () => { + this.contentContainer.removeClass( 'busy' ); + args.completeCallback?.(); + } ); + } + } else { + super.onChangeExpanded( expanded, args ); + } const controls = this.controls(); controls.forEach( ( control ) => { - control.onChangeExpanded( expanded, args ); + control.onChangeSectionExpanded( expanded, args ); } ); } } diff --git a/packages/customize-widgets/src/style.scss b/packages/customize-widgets/src/style.scss index 13b614322b10e..96954c0210097 100644 --- a/packages/customize-widgets/src/style.scss +++ b/packages/customize-widgets/src/style.scss @@ -1,3 +1,3 @@ -@import "./components/inspector/style.scss"; +@import "./components/block-inspector-button/style.scss"; @import "./components/header/style.scss"; @import "./components/inserter/style.scss";