diff --git a/packages/block-editor/src/components/block-list/block-popover.js b/packages/block-editor/src/components/block-list/block-popover.js index d596d8c5b1f475..882ec29807a061 100644 --- a/packages/block-editor/src/components/block-list/block-popover.js +++ b/packages/block-editor/src/components/block-list/block-popover.js @@ -189,6 +189,7 @@ function BlockPopover( { ) } @@ -214,6 +215,7 @@ function BlockPopover( { position="top right" rootClientId={ rootClientId } clientId={ clientId } + __experimentalIsQuick /> ) } diff --git a/packages/block-editor/src/components/block-list/insertion-point.js b/packages/block-editor/src/components/block-list/insertion-point.js index 3d23ad7da4431b..c906261b2c70f2 100644 --- a/packages/block-editor/src/components/block-list/insertion-point.js +++ b/packages/block-editor/src/components/block-list/insertion-point.js @@ -183,6 +183,7 @@ export default function InsertionPoint( { diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js new file mode 100644 index 00000000000000..8905eece51430b --- /dev/null +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -0,0 +1,59 @@ +/** + * WordPress dependencies + */ +import { useMemo } from '@wordpress/element'; +import { parse } from '@wordpress/blocks'; +import { ENTER, SPACE } from '@wordpress/keycodes'; + +/** + * Internal dependencies + */ +import BlockPreview from '../block-preview'; + +function BlockPattern( { pattern, onClick } ) { + const { content, viewportWidth } = pattern; + const blocks = useMemo( () => parse( content ), [ content ] ); + + return ( +
onClick( pattern, blocks ) } + onKeyDown={ ( event ) => { + if ( ENTER === event.keyCode || SPACE === event.keyCode ) { + onClick( pattern, blocks ); + } + } } + tabIndex={ 0 } + aria-label={ pattern.title } + > + +
+ { pattern.title } +
+
+ ); +} + +function BlockPatternPlaceholder() { + return ( +
+ ); +} + +function BlockPatternList( { blockPatterns, shownPatterns, onClickPattern } ) { + return blockPatterns.map( ( pattern ) => { + const isShown = shownPatterns.includes( pattern ); + return isShown ? ( + + ) : ( + + ); + } ); +} + +export default BlockPatternList; diff --git a/packages/block-editor/src/components/block-patterns-list/style.scss b/packages/block-editor/src/components/block-patterns-list/style.scss new file mode 100644 index 00000000000000..9843f71702b56f --- /dev/null +++ b/packages/block-editor/src/components/block-patterns-list/style.scss @@ -0,0 +1,29 @@ +.block-editor-block-patterns-list__item { + border-radius: $radius-block-ui; + cursor: pointer; + margin-top: $grid-unit-20; + transition: all 0.05s ease-in-out; + position: relative; + border: $border-width solid transparent; + + &:hover { + border: $border-width solid $theme-color; + } + + &:focus { + box-shadow: inset 0 0 0 1px $white, 0 0 0 $border-width-focus $theme-color; + + // Windows High Contrast mode will show this outline, but not the box-shadow. + outline: 2px solid transparent; + } + + &.is-placeholder { + min-height: 100px; + } +} + +.block-editor-block-patterns-list__item-title { + padding: $grid-unit-05; + font-size: 12px; + text-align: center; +} diff --git a/packages/block-editor/src/components/button-block-appender/index.js b/packages/block-editor/src/components/button-block-appender/index.js index 6bdeb479dd1639..a0eb840348d8ef 100644 --- a/packages/block-editor/src/components/button-block-appender/index.js +++ b/packages/block-editor/src/components/button-block-appender/index.js @@ -31,6 +31,7 @@ function ButtonBlockAppender( position="bottom center" rootClientId={ rootClientId } __experimentalSelectBlockOnInsert={ selectBlockOnInsert } + __experimentalIsQuick renderToggle={ ( { onToggle, disabled, diff --git a/packages/block-editor/src/components/default-block-appender/index.js b/packages/block-editor/src/components/default-block-appender/index.js index 865f9a76f226e1..b012eb256c721a 100644 --- a/packages/block-editor/src/components/default-block-appender/index.js +++ b/packages/block-editor/src/components/default-block-appender/index.js @@ -65,6 +65,7 @@ export function DefaultBlockAppender( { rootClientId={ rootClientId } position="top right" isAppender + __experimentalIsQuick />
); diff --git a/packages/block-editor/src/components/inserter/block-patterns.js b/packages/block-editor/src/components/inserter/block-patterns-tab.js similarity index 66% rename from packages/block-editor/src/components/inserter/block-patterns.js rename to packages/block-editor/src/components/inserter/block-patterns-tab.js index 9fc7f67413469b..1cb8db684cb1eb 100644 --- a/packages/block-editor/src/components/inserter/block-patterns.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab.js @@ -7,65 +7,17 @@ import { fromPairs } from 'lodash'; * WordPress dependencies */ import { useMemo, useCallback } from '@wordpress/element'; -import { parse } from '@wordpress/blocks'; -import { ENTER, SPACE } from '@wordpress/keycodes'; import { __, _x } from '@wordpress/i18n'; /** * Internal dependencies */ -import BlockPreview from '../block-preview'; -import useAsyncList from './use-async-list'; import InserterPanel from './panel'; import { searchItems } from './search-items'; import InserterNoResults from './no-results'; import usePatternsState from './hooks/use-patterns-state'; - -function BlockPattern( { pattern, onClick } ) { - const { content, viewportWidth } = pattern; - const blocks = useMemo( () => parse( content ), [ content ] ); - - return ( -
onClick( pattern, blocks ) } - onKeyDown={ ( event ) => { - if ( ENTER === event.keyCode || SPACE === event.keyCode ) { - onClick( pattern, blocks ); - } - } } - tabIndex={ 0 } - aria-label={ pattern.title } - > - -
- { pattern.title } -
-
- ); -} - -function BlockPatternPlaceholder() { - return ( -
- ); -} - -function BlockPatternList( { patterns, shownPatterns, onClickPattern } ) { - return patterns.map( ( pattern ) => { - const isShown = shownPatterns.includes( pattern ); - return isShown ? ( - - ) : ( - - ); - } ); -} +import useAsyncList from './hooks/use-async-list'; +import BlockPatternList from '../block-patterns-list'; function BlockPatternsSearchResults( { filterValue, onInsert } ) { const [ patterns, , onClick ] = usePatternsState( onInsert ); @@ -81,7 +33,7 @@ function BlockPatternsSearchResults( { filterValue, onInsert } ) { @@ -147,7 +99,7 @@ function BlockPatternsPerCategories( { onInsert } ) { > @@ -159,7 +111,7 @@ function BlockPatternsPerCategories( { onInsert } ) { @@ -168,7 +120,7 @@ function BlockPatternsPerCategories( { onInsert } ) { ); } -function BlockPatterns( { onInsert, filterValue } ) { +function BlockPatternsTabs( { onInsert, filterValue } ) { return filterValue ? ( item.name.split( '/' )[ 0 ]; const MAX_SUGGESTED_ITEMS = 6; -export function InserterBlockList( { +export function BlockTypesTab( { rootClientId, onInsert, onHover, @@ -248,4 +248,4 @@ export function InserterBlockList( { ); } -export default compose( withSpokenMessages )( InserterBlockList ); +export default compose( withSpokenMessages )( BlockTypesTab ); diff --git a/packages/block-editor/src/components/inserter/use-async-list.js b/packages/block-editor/src/components/inserter/hooks/use-async-list.js similarity index 100% rename from packages/block-editor/src/components/inserter/use-async-list.js rename to packages/block-editor/src/components/inserter/hooks/use-async-list.js diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js index 929746ad967ba1..cb2fa41effa90d 100644 --- a/packages/block-editor/src/components/inserter/index.js +++ b/packages/block-editor/src/components/inserter/index.js @@ -2,6 +2,8 @@ * External dependencies */ import { size } from 'lodash'; +import classnames from 'classnames'; + /** * WordPress dependencies */ @@ -18,6 +20,7 @@ import { plus } from '@wordpress/icons'; * Internal dependencies */ import InserterMenu from './menu'; +import QuickInserter from './quick-inserter'; const defaultRenderToggle = ( { onToggle, @@ -116,8 +119,19 @@ class Inserter extends Component { isAppender, showInserterHelpPanel, __experimentalSelectBlockOnInsert: selectBlockOnInsert, + __experimentalIsQuick: isQuick, } = this.props; + if ( isQuick ) { + return ( + + ); + } return ( event.stopPropagation(); @@ -82,7 +81,7 @@ function InserterMenu( { <>
- - +
); @@ -119,28 +121,14 @@ function InserterMenu( {
{ showPatterns && ( - + { ( tab ) => { if ( tab.name === 'blocks' ) { return blocksTab; } return patternsTab; } } - + ) } { ! showPatterns && blocksTab }
diff --git a/packages/block-editor/src/components/inserter/quick-inserter.js b/packages/block-editor/src/components/inserter/quick-inserter.js new file mode 100644 index 00000000000000..dd1bee9fd9eb29 --- /dev/null +++ b/packages/block-editor/src/components/inserter/quick-inserter.js @@ -0,0 +1,137 @@ +/** + * WordPress dependencies + */ +import { useState, useMemo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import BlockTypesList from '../block-types-list'; +import BlockPatternsList from '../block-patterns-list'; +import InserterSearchForm from './search-form'; +import InserterPanel from './panel'; +import InserterNoResults from './no-results'; +import useInsertionPoint from './hooks/use-insertion-point'; +import usePatternsState from './hooks/use-patterns-state'; +import useBlockTypesState from './hooks/use-block-types-state'; +import { searchBlockItems, searchItems } from './search-items'; + +const SEARCH_THRESHOLD = 6; +const SHOWN_BLOCK_TYPES = 6; +const SHOWN_BLOCK_PATTERNS = 2; + +function QuickInserterList( { + blockTypes, + blockPatterns, + onSelectBlockType, + onSelectBlockPattern, + onHover, +} ) { + const showBlockTypes = useMemo( + () => blockTypes.slice( 0, SHOWN_BLOCK_TYPES ), + [ blockTypes ] + ); + const shownBlockPatterns = useMemo( + () => blockPatterns.slice( 0, SHOWN_BLOCK_PATTERNS ), + [ blockTypes ] + ); + return ( +
+ { ! showBlockTypes.length && ! shownBlockPatterns.length && ( + + ) } + + { !! showBlockTypes.length && ( + + + + ) } + + { !! shownBlockPatterns.length && ( + + + + ) } +
+ ); +} + +function QuickInserter( { + rootClientId, + clientId, + isAppender, + selectBlockOnInsert, +} ) { + const [ isFiltered, setIsFiltered ] = useState( false ); + const [ filterValue, setFilterValue ] = useState( '' ); + const [ + destinationRootClientId, + onInsertBlocks, + onToggleInsertionPoint, + ] = useInsertionPoint( { + rootClientId, + clientId, + isAppender, + selectBlockOnInsert, + } ); + const [ + blockTypes, + blockTypeCategories, + blockTypeCollections, + onSelectBlockType, + ] = useBlockTypesState( destinationRootClientId, onInsertBlocks ); + const [ patterns, onSelectBlockPattern ] = usePatternsState( + onInsertBlocks + ); + const showPatterns = ! destinationRootClientId && patterns.length; + const showSearch = + ( showPatterns && patterns.length > SEARCH_THRESHOLD ) || + blockTypes.length > SEARCH_THRESHOLD; + + const filteredBlockTypes = useMemo( () => { + return searchBlockItems( + blockTypes, + blockTypeCategories, + blockTypeCollections, + filterValue + ); + }, [ filterValue, blockTypes, blockTypeCategories, blockTypeCollections ] ); + + const filteredBlockPatterns = useMemo( + () => searchItems( patterns, filterValue ), + [ filterValue, patterns ] + ); + + return ( +
+ { showSearch && ( + { + setFilterValue( value ); + setIsFiltered( true ); + } } + /> + ) } + { ( ! showSearch || isFiltered ) && ( + + ) } +
+ ); +} + +export default QuickInserter; diff --git a/packages/block-editor/src/components/inserter/style.scss b/packages/block-editor/src/components/inserter/style.scss index 2725c904073ee7..7a1cfb08fbe65c 100644 --- a/packages/block-editor/src/components/inserter/style.scss +++ b/packages/block-editor/src/components/inserter/style.scss @@ -16,7 +16,7 @@ $block-inserter-tabs-height: 44px; } } -.block-editor-inserter__popover > .components-popover__content { +.block-editor-inserter__popover:not(.is-quick) > .components-popover__content { @include break-medium { overflow-y: visible; height: 100vh; @@ -247,32 +247,17 @@ $block-inserter-tabs-height: 44px; flex-shrink: 0; } -.block-editor-inserter__patterns-item { - border-radius: $radius-block-ui; - cursor: pointer; - margin-top: $grid-unit-20; - transition: all 0.05s ease-in-out; - position: relative; - border: $border-width solid transparent; - - &:hover { - border: $border-width solid $theme-color; - } +.block-editor-inserter__quick-inserter { + width: $block-inserter-width; +} - &:focus { - box-shadow: inset 0 0 0 1px $white, 0 0 0 $border-width-focus $theme-color; - // Windows High Contrast mode will show this outline, but not the box-shadow. - outline: 2px solid transparent; - } - - &.is-placeholder { - min-height: 100px; - } +.block-editor-inserter__quick-inserter__results { + padding-bottom: $grid-unit-20; } -.block-editor-inserter__patterns-item-title { - padding: $grid-unit-05; - font-size: 12px; - text-align: center; +.block-editor-inserter__popover.is-quick > .components-popover__content { + @include break-medium { + padding: 0; + } } diff --git a/packages/block-editor/src/components/inserter/tabs.js b/packages/block-editor/src/components/inserter/tabs.js new file mode 100644 index 00000000000000..11393b2a703728 --- /dev/null +++ b/packages/block-editor/src/components/inserter/tabs.js @@ -0,0 +1,29 @@ +/** + * WordPress dependencies + */ +import { TabPanel } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +function InserterTabs( { children } ) { + return ( + + { children } + + ); +} + +export default InserterTabs; diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index f7648056f036c2..1ddb4f39f2fdd0 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -18,6 +18,7 @@ @import "./components/block-mover/style.scss"; @import "./components/block-navigation/style.scss"; @import "./components/block-parent-selector/style.scss"; +@import "./components/block-patterns-list/style.scss"; @import "./components/block-preview/style.scss"; @import "./components/block-settings-menu/style.scss"; @import "./components/block-styles/style.scss";