Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RNMobile] Add inserter block search #29169

Merged
merged 19 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 );
Comment on lines +148 to +152
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor thing, but I wonder if instead of using a let here we could used two const variables with names that showed how they were different.


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( '' );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiousity, why default to empty string instead of undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, I was thinking the same when I looked back at this PR 😆 . After reviewing the changes as they are now I can't find an explicit reason. I believe as some point while I was drafting the changes I was treating undefined differently. That's not the case now.

I might update the onChange function definition to use '' as the default value in a later change. That way the value remains a String type but can be called with undefined

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);
}