-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add customize widgets inserter (#29549)
Co-authored-by: Robert Anderson <[email protected]>
- Loading branch information
1 parent
f1d9a0b
commit 53d1ebb
Showing
13 changed files
with
361 additions
and
22 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { createPortal } from '@wordpress/element'; | ||
import { __, _x } from '@wordpress/i18n'; | ||
import { Button, ToolbarItem } from '@wordpress/components'; | ||
import { NavigableToolbar } from '@wordpress/block-editor'; | ||
import { plus } from '@wordpress/icons'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import Inserter from '../inserter'; | ||
|
||
function Header( { inserter, isInserterOpened, setIsInserterOpened } ) { | ||
return ( | ||
<> | ||
<div className="customize-widgets-header"> | ||
<NavigableToolbar | ||
className="customize-widgets-header-toolbar" | ||
aria-label={ __( 'Document tools' ) } | ||
> | ||
<ToolbarItem | ||
as={ Button } | ||
className="customize-widgets-header-toolbar__inserter-toggle" | ||
isPressed={ isInserterOpened } | ||
isPrimary | ||
icon={ plus } | ||
label={ _x( | ||
'Add block', | ||
'Generic label for block inserter button' | ||
) } | ||
onClick={ () => { | ||
setIsInserterOpened( ( isOpen ) => ! isOpen ); | ||
} } | ||
/> | ||
</NavigableToolbar> | ||
</div> | ||
|
||
{ createPortal( | ||
<Inserter setIsOpened={ setIsInserterOpened } />, | ||
inserter.contentContainer[ 0 ] | ||
) } | ||
</> | ||
); | ||
} | ||
|
||
export default Header; |
21 changes: 21 additions & 0 deletions
21
packages/customize-widgets/src/components/header/style.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
.customize-widgets-header-toolbar { | ||
display: flex; | ||
border: none; | ||
|
||
.customize-widgets-header-toolbar__inserter-toggle.components-button.has-icon { | ||
border-radius: 2px; | ||
color: $white; | ||
padding: 0; | ||
min-width: $grid-unit-30; | ||
height: $grid-unit-30; | ||
margin-left: auto; | ||
|
||
&::before { | ||
content: none; | ||
} | ||
|
||
&.is-pressed { | ||
background: $gray-900; | ||
} | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
packages/customize-widgets/src/components/inserter/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { __experimentalLibrary as Library } from '@wordpress/block-editor'; | ||
import { Button } from '@wordpress/components'; | ||
import { useInstanceId } from '@wordpress/compose'; | ||
import { closeSmall } from '@wordpress/icons'; | ||
|
||
function Inserter( { setIsOpened } ) { | ||
const inserterTitleId = useInstanceId( | ||
Inserter, | ||
'customize-widget-layout__inserter-panel-title' | ||
); | ||
|
||
return ( | ||
<div | ||
className="customize-widgets-layout__inserter-panel" | ||
aria-labelledby={ inserterTitleId } | ||
> | ||
<div className="customize-widgets-layout__inserter-panel-header"> | ||
<h2 | ||
id={ inserterTitleId } | ||
className="customize-widgets-layout__inserter-panel-header-title" | ||
> | ||
{ __( 'Add a block' ) } | ||
</h2> | ||
<Button | ||
className="customize-widgets-layout__inserter-panel-header-close-button" | ||
icon={ closeSmall } | ||
onClick={ () => setIsOpened( false ) } | ||
aria-label={ __( 'Close inserter' ) } | ||
/> | ||
</div> | ||
<div className="customize-widgets-layout__inserter-panel-content"> | ||
<Library | ||
showInserterHelpPanel | ||
onSelect={ () => setIsOpened( false ) } | ||
/> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default Inserter; |
40 changes: 40 additions & 0 deletions
40
packages/customize-widgets/src/components/inserter/style.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#customize-sidebar-outer-content { | ||
// To override the default style of width: 100%, | ||
// so that the inspector won't be cropped. | ||
width: auto; | ||
min-width: 100%; | ||
} | ||
|
||
#customize-outer-theme-controls #sub-accordion-section-widgets-inserter { | ||
padding: 0; | ||
|
||
.customize-section-description-container { | ||
display: none; | ||
} | ||
} | ||
|
||
.customize-widgets-layout__inserter-panel { | ||
background: $white; | ||
} | ||
|
||
.customize-widgets-layout__inserter-panel-header { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
padding: $grid-unit-20; | ||
// Match the "wp-full-overlay-header" height in core, plus the 1px border. | ||
height: 46px; | ||
box-sizing: border-box; | ||
border-bottom: 1px solid $gray-300; | ||
|
||
.customize-widgets-layout__inserter-panel-header-title { | ||
margin: 0; | ||
} | ||
} | ||
|
||
.block-editor-inserter__quick-inserter { | ||
.block-editor-inserter__panel-content { | ||
// To fix quick inserter doesn't have background. | ||
background: $white; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
packages/customize-widgets/src/components/inserter/use-inserter.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useState, useEffect, useCallback } from '@wordpress/element'; | ||
|
||
export default function useInserter( inserter ) { | ||
const [ isInserterOpened, setIsInserterOpened ] = useState( | ||
() => inserter.isOpen | ||
); | ||
|
||
useEffect( () => { | ||
return inserter.subscribe( setIsInserterOpened ); | ||
}, [ inserter ] ); | ||
|
||
return [ | ||
isInserterOpened, | ||
useCallback( | ||
( updater ) => { | ||
let isOpen = updater; | ||
if ( typeof updater === 'function' ) { | ||
isOpen = updater( inserter.isOpen ); | ||
} | ||
|
||
if ( isOpen ) { | ||
inserter.open(); | ||
} else { | ||
inserter.close(); | ||
} | ||
}, | ||
[ inserter ] | ||
), | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
packages/customize-widgets/src/controls/inserter-outer-section.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { ESCAPE } from '@wordpress/keycodes'; | ||
import { focus } from '@wordpress/dom'; | ||
|
||
const { | ||
wp: { customize }, | ||
} = window; | ||
|
||
const OuterSection = customize.OuterSection; | ||
// Override the OuterSection class to handle multiple outer sections. | ||
// It closes all the other outer sections whenever one is opened. | ||
// The result is that at most one outer section can be opened at the same time. | ||
customize.OuterSection = class extends OuterSection { | ||
onChangeExpanded( expanded, args ) { | ||
if ( expanded ) { | ||
customize.section.each( ( section ) => { | ||
if ( | ||
section.params.type === 'outer' && | ||
section.id !== this.id | ||
) { | ||
if ( section.expanded() ) { | ||
section.collapse(); | ||
} | ||
} | ||
} ); | ||
} | ||
|
||
return super.onChangeExpanded( expanded, args ); | ||
} | ||
}; | ||
// Handle constructor so that "params.type" can be correctly pointed to "outer". | ||
customize.sectionConstructor.outer = customize.OuterSection; | ||
|
||
class InserterOuterSection extends customize.OuterSection { | ||
constructor( ...args ) { | ||
super( ...args ); | ||
|
||
// This is necessary since we're creating a new class which is not identical to the original OuterSection. | ||
// @See https://github.com/WordPress/wordpress-develop/blob/42b05c397c50d9dc244083eff52991413909d4bd/src/js/_enqueues/wp/customize/controls.js#L1427-L1436 | ||
this.params.type = 'outer'; | ||
|
||
this.activeElementBeforeExpanded = null; | ||
|
||
const ownerWindow = this.contentContainer[ 0 ].ownerDocument | ||
.defaultView; | ||
|
||
// Handle closing the inserter when pressing the Escape key. | ||
ownerWindow.addEventListener( | ||
'keydown', | ||
( event ) => { | ||
if ( | ||
this.isOpen && | ||
( event.keyCode === ESCAPE || event.code === 'Escape' ) | ||
) { | ||
event.stopPropagation(); | ||
|
||
this.close(); | ||
} | ||
}, | ||
// Use capture mode to make this run before other event listeners. | ||
true | ||
); | ||
} | ||
get isOpen() { | ||
return this.expanded(); | ||
} | ||
subscribe( handler ) { | ||
this.expanded.bind( handler ); | ||
return () => this.expanded.unbind( handler ); | ||
} | ||
open() { | ||
if ( ! this.isOpen ) { | ||
const contentContainer = this.contentContainer[ 0 ]; | ||
this.activeElementBeforeExpanded = | ||
contentContainer.ownerDocument.activeElement; | ||
|
||
this.expand( { | ||
completeCallback() { | ||
// We have to do this in a "completeCallback" or else the elements will not yet be visible/tabbable. | ||
// The first one should be the close button, | ||
// we want to skip it and choose the second one instead, which is the search box. | ||
const searchBox = focus.tabbable.find( | ||
contentContainer | ||
)[ 1 ]; | ||
if ( searchBox ) { | ||
searchBox.focus(); | ||
} | ||
}, | ||
} ); | ||
} | ||
} | ||
close() { | ||
if ( this.isOpen ) { | ||
const contentContainer = this.contentContainer[ 0 ]; | ||
const activeElement = contentContainer.ownerDocument.activeElement; | ||
|
||
this.collapse( { | ||
completeCallback() { | ||
// Return back the focus when closing the inserter. | ||
// Only do this if the active element which triggers the action is inside the inserter, | ||
// (the close button for instance). In that case the focus will be lost. | ||
// Otherwise, we don't hijack the focus when the user is focusing on other elements | ||
// (like the quick inserter). | ||
if ( contentContainer.contains( activeElement ) ) { | ||
// Return back the focus when closing the inserter. | ||
if ( this.activeElementBeforeExpanded ) { | ||
this.activeElementBeforeExpanded.focus(); | ||
} | ||
} | ||
}, | ||
} ); | ||
} | ||
} | ||
} | ||
|
||
export default InserterOuterSection; |
Oops, something went wrong.