Skip to content

Commit

Permalink
[RNMobile] Add inserter block search (#29169)
Browse files Browse the repository at this point in the history
* Refactor to functional component

* Switch to useSelect hook

* Switch to useDispatch

Switch to useDispatch for insertDefaultBlock

Switch to useDispatch for onSelect

Switch to useDispatch for showInsertionPoint

Remove withDispatch and WithSelect

* Simplify clipboard flow

* Move menu to search results

* Move displaying items to a separate component

Pass items to search results as a prop

Move show/hide insertion point

Move block insertion to menu

Fix block insertion

* Add insserter search form

* Filter blocks by search query

* First pass at styling for search

Also re-org bottom sheet setup

* Simplify search form

* Add dev flag to enable search

* Move BottomSheetConsumer to the menu

* Add ebottom sheet padding for search form

* Only show search if there are enough items to filter

* Clean up styles

* fix lint error

Co-authored-by: Jason Johnston <[email protected]>
  • Loading branch information
jhnstn and jhnstn authored Mar 18, 2021
1 parent 23328a2 commit 0e4fcfd
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 64 deletions.
82 changes: 69 additions & 13 deletions packages/block-editor/src/components/inserter/menu.native.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
/**
* External dependencies
*/
import { View } from 'react-native';
/**
* WordPress dependencies
*/
import { useEffect, useCallback } from '@wordpress/element';
import { useEffect, useState, useCallback } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import {
createBlock,
rawHandler,
store as blocksStore,
} from '@wordpress/blocks';
import { getClipboard } from '@wordpress/components';
import {
BottomSheet,
BottomSheetConsumer,
getClipboard,
} from '@wordpress/components';

/**
* Internal dependencies
*/

import InserterSearchResults from './search-results';
import InserterSearchForm from './search-form';
import { store as blockEditorStore } from '../../store';
import { searchItems } from './search-items';

const MIN_ITEMS_FOR_SEARCH = 2;

function InserterMenu( {
onSelect,
Expand All @@ -26,6 +38,11 @@ function InserterMenu( {
shouldReplaceBlock,
insertionIndex,
} ) {
const [ filterValue, setFilterValue ] = useState( '' );
const [ searchFormHeight, setSearchFormHeight ] = useState( 0 );
// eslint-disable-next-line no-undef
const [ showSearchForm, setShowSearchForm ] = useState( __DEV__ );

const {
showInsertionPoint,
hideInsertionPoint,
Expand Down Expand Up @@ -70,6 +87,7 @@ function InserterMenu( {
const { getBlockType } = useSelect( ( select ) => select( blocksStore ) );

useEffect( () => {
// Show/Hide insertion point on Mount/Dismount
if ( shouldReplaceBlock ) {
const count = getBlockCount();
// Check if there is a rootClientId because that means it is a nested replaceable block
Expand All @@ -86,14 +104,19 @@ function InserterMenu( {
removeBlock( blockToReplace, false );
}
}
// Show/Hide insertion point on Mount/Dismount
showInsertionPoint( destinationRootClientId, insertionIndex );

// Show search form if there are enough items to filter.
if ( getItems()?.length < MIN_ITEMS_FOR_SEARCH ) {
setShowSearchForm( false );
}

return hideInsertionPoint;
}, [] );

const onClose = useCallback( () => {
// If should replace but didn't insert any block
// re-insert default block.
// if should replace but didn't insert any block
// re-insert default block
if ( shouldReplaceBlock ) {
insertDefaultBlock( {}, destinationRootClientId, insertionIndex );
}
Expand Down Expand Up @@ -122,10 +145,12 @@ function InserterMenu( {
*/
function getItems() {
// Filter out reusable blocks (they will be added in another tab)
const itemsToDisplay = items.filter(
let itemsToDisplay = items.filter(
( { name } ) => name !== 'core/block'
);

itemsToDisplay = searchItems( itemsToDisplay, filterValue );

const clipboard = getClipboard();
let clipboardBlock = rawHandler( { HTML: clipboard } )[ 0 ];

Expand Down Expand Up @@ -153,14 +178,45 @@ function InserterMenu( {
}

return (
<InserterSearchResults
<BottomSheet
isVisible={ true }
onClose={ onClose }
items={ getItems() }
onSelect={ ( item ) => {
onInsert( item );
onSelect( item );
} }
/>
hideHeader
hasNavigation
setMinHeightToMaxHeight={ showSearchForm }
>
<BottomSheetConsumer>
{ ( { listProps, safeAreaBottomInset } ) => (
<View>
{ showSearchForm && (
<InserterSearchForm
onChange={ ( value ) => {
setFilterValue( value );
} }
value={ filterValue }
onLayout={ ( event ) => {
const { height } = event.nativeEvent.layout;
setSearchFormHeight( height );
} }
/>
) }

<InserterSearchResults
items={ getItems() }
onSelect={ ( item ) => {
onInsert( item );
onSelect( item );
} }
{ ...{
listProps,
safeAreaBottomInset,
searchFormHeight,
} }
/>
</View>
) }
</BottomSheetConsumer>
</BottomSheet>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* External dependencies
*/
import { TextInput, View, TouchableHighlight } from 'react-native';

/**
* WordPress dependencies
*/
import { useState, useRef } from '@wordpress/element';
import { usePreferredColorSchemeStyle } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import { ToolbarButton } from '@wordpress/components';
import {
Icon,
cancelCircleFilled,
arrowLeft,
search as searchIcon,
} from '@wordpress/icons';

/**
* Internal dependencies
*/
import styles from './style.scss';

function InserterSearchForm( { value, onChange, onLayout = () => {} } ) {
const [ isActive, setIsActive ] = useState( false );

const inputRef = useRef();

const searchFormStyle = usePreferredColorSchemeStyle(
styles.searchForm,
styles.searchFormDark
);

const searchFormInputStyle = usePreferredColorSchemeStyle(
styles.searchFormInput,
styles.searchFormInputDark
);

const placeholderStyle = usePreferredColorSchemeStyle(
styles.searchFormPlaceholder,
styles.searchFormPlaceholderDark
);

return (
<TouchableHighlight accessible={ false } onLayout={ onLayout }>
<View style={ searchFormStyle }>
{ isActive ? (
<ToolbarButton
title={ __( 'Cancel search' ) }
icon={ arrowLeft }
onClick={ () => {
inputRef.current.blur();
onChange( '' );
setIsActive( false );
} }
/>
) : (
<ToolbarButton
title={ __( 'Search block' ) }
icon={ searchIcon }
onClick={ () => {
inputRef.current.focus();
setIsActive( true );
} }
/>
) }
<TextInput
ref={ inputRef }
style={ searchFormInputStyle }
placeholderTextColor={ placeholderStyle.color }
onChangeText={ onChange }
onFocus={ () => setIsActive( true ) }
value={ value }
placeholder={ __( 'Search blocks' ) }
/>

{ !! value && (
<ToolbarButton
title={ __( 'Clear search' ) }
icon={ <Icon icon={ cancelCircleFilled } /> }
onClick={ () => {
onChange( '' );
} }
/>
) }
</View>
</TouchableHighlight>
);
}

export default InserterSearchForm;
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ import {
* WordPress dependencies
*/
import { useState, useEffect } from '@wordpress/element';
import {
BottomSheet,
BottomSheetConsumer,
InserterButton,
} from '@wordpress/components';
import { BottomSheet, InserterButton } from '@wordpress/components';

/**
* Internal dependencies
Expand All @@ -26,7 +22,13 @@ import styles from './style.scss';

const MIN_COL_NUM = 3;

function InserterSearchResults( { items, onSelect, onClose } ) {
function InserterSearchResults( {
items,
onSelect,
listProps,
safeAreaBottomInset,
searchFormHeight = 0,
} ) {
const [ numberOfColumns, setNumberOfColumns ] = useState( MIN_COL_NUM );
const [ itemWidth, setItemWidth ] = useState();
const [ maxWidth, setMaxWidth ] = useState();
Expand Down Expand Up @@ -73,51 +75,41 @@ function InserterSearchResults( { items, onSelect, onClose } ) {
}

return (
<BottomSheet
isVisible={ true }
onClose={ onClose }
hideHeader
hasNavigation
>
<TouchableHighlight accessible={ false }>
<BottomSheetConsumer>
{ ( { listProps, safeAreaBottomInset } ) => (
<FlatList
onLayout={ onLayout }
key={ `InserterUI-${ numberOfColumns }` } //re-render when numberOfColumns changes
keyboardShouldPersistTaps="always"
numColumns={ numberOfColumns }
data={ items }
ItemSeparatorComponent={ () => (
<TouchableWithoutFeedback accessible={ false }>
<View style={ styles.rowSeparator } />
</TouchableWithoutFeedback>
) }
keyExtractor={ ( item ) => item.name }
renderItem={ ( { item } ) => (
<InserterButton
{ ...{
item,
itemWidth,
maxWidth,
onSelect,
} }
/>
) }
{ ...listProps }
contentContainerStyle={ [
...listProps.contentContainerStyle,
{
paddingBottom:
safeAreaBottomInset ||
styles.list.paddingBottom,
},
] }
/>
) }
</BottomSheetConsumer>
</TouchableHighlight>
</BottomSheet>
<TouchableHighlight accessible={ false }>
<FlatList
onLayout={ onLayout }
key={ `InserterUI-${ numberOfColumns }` } //re-render when numberOfColumns changes
keyboardShouldPersistTaps="always"
numColumns={ numberOfColumns }
data={ items }
initialNumToRender={ 3 }
ItemSeparatorComponent={ () => (
<TouchableWithoutFeedback accessible={ false }>
<View style={ styles.rowSeparator } />
</TouchableWithoutFeedback>
) }
keyExtractor={ ( item ) => item.name }
renderItem={ ( { item } ) => (
<InserterButton
{ ...{
item,
itemWidth,
maxWidth,
onSelect,
} }
/>
) }
{ ...listProps }
contentContainerStyle={ [
...listProps.contentContainerStyle,
{
paddingBottom:
( safeAreaBottomInset ||
styles.list.paddingBottom ) + searchFormHeight,
},
] }
/>
</TouchableHighlight>
);
}

Expand Down
31 changes: 31 additions & 0 deletions packages/block-editor/src/components/inserter/style.native.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,34 @@
.list {
padding-bottom: 20;
}

.searchForm {
height: 46px;
border-radius: 8px;
color: $gray-dark;
margin: $grid-unit-30;
background-color: $gray-light;
flex-direction: row;
justify-content: space-between;
}

.searchFormDark {
background-color: rgba($white, 0.07);
}

.searchFormInput {
color: $gray-dark;
flex: 2;
}

.searchFormInputDark {
color: $white;
}

.searchFormPlaceholder {
color: $gray;
}

.searchFormPlaceholderDark {
color: rgba($white, 0.8);
}

0 comments on commit 0e4fcfd

Please sign in to comment.