-
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.
* Crudely fork web implementation to remove invariant violations * WIP - Better UI for testing. Reset Autocomplete on delete content * Use a synthetic event to bind onEnter to the autocompletion * Mobile - Move onEnter logic to native Rich Text * Simplify event conformance in native RichText * Remove native variant of block-editor autocomplete This adds the block completer * Use Platform variant to wrap text on native, leaving web unchanged * Extract autocomplete functions * Merge autocomplete variants, and create an autocomplete ui variant * WIP - add Popover library and begin experimenting * Move optional from this to function * WIP - Popover changes * Extract default useItems hook from autocomplete ui * Rename extracted ui for consistency * Remove Popover library. Add UI for the slash inserter selector * Remove lodash usage * Fix preventDefault on Android * Simplify customEditableOnKeyDown stubs * Rename autocompletion slot fill and move to mobile components * Reset scroll position when autocomplete items change * Adjust styles for BlurView (for iOS - needs Android changes) * Update autocompletion item container view styles for Android * Update autocomplete styles * Fix order of imports * Put feature behind __DEV__ flag * Remove mobile wrapper since it no longer uses the label attribute to render the block name * Add missing line break * Update active item styles * Revert "Put feature behind __DEV__ flag" This reverts commit 528a83f. * Add slash-inserter e2e test * Update light-mode active background color for slash complete option * Update placeholder text to hint the slash inserter * Slash inserter animation and active item color update * Styles cleanup * Pass isVisible to useCallback * Adjust autocompleter animation timing * Remove redundant text decoration * Adjust colors * Use shorter writing prompt This is taken from #29611 * Make test description more specific Co-authored-by: Enej Bajgoric <[email protected]> * Styles update * Mobile: Update default text placeholder, fix autocompleter position in Android * Use I with serif for italic format library icon This is to distinguish the character from the slash character to avoid confusion with the slash inserter (especially with the new writing prompt that says to type "/"). * Mobile - Add Slash inserter e2e tests * Unify e2e files * Revert placeholders change * Mark AutocompletionItemsSlot and AutocompletionItemsFill as __unstable * Update styles to BEM naming * Revert "Use I with serif for italic format library icon" This reverts commit c251037. * Fix slash inserter e2e * Fix e2e test on Android * Extract slash inserter assertion into separate function * Extract platform variants for BackgroundView to separate files Co-authored-by: Matthew Kevins <[email protected]> Co-authored-by: Matthew Kevins <[email protected]> Co-authored-by: Enej Bajgoric <[email protected]>
- Loading branch information
1 parent
a584b1e
commit 2db0d5c
Showing
17 changed files
with
736 additions
and
177 deletions.
There are no files selected for viewing
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
1 change: 0 additions & 1 deletion
1
packages/block-editor/src/components/autocomplete/index.native.js
This file was deleted.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import classnames from 'classnames'; | ||
import { map } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useLayoutEffect } from '@wordpress/element'; | ||
import { useAnchorRef } from '@wordpress/rich-text'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import getDefaultUseItems from './get-default-use-items'; | ||
import Button from '../button'; | ||
import Popover from '../popover'; | ||
|
||
export function getAutoCompleterUI( autocompleter ) { | ||
const useItems = autocompleter.useItems | ||
? autocompleter.useItems | ||
: getDefaultUseItems( autocompleter ); | ||
|
||
function AutocompleterUI( { | ||
filterValue, | ||
instanceId, | ||
listBoxId, | ||
className, | ||
selectedIndex, | ||
onChangeOptions, | ||
onSelect, | ||
onReset, | ||
value, | ||
contentRef, | ||
} ) { | ||
const [ items ] = useItems( filterValue ); | ||
const anchorRef = useAnchorRef( { ref: contentRef, value } ); | ||
|
||
useLayoutEffect( () => { | ||
onChangeOptions( items ); | ||
}, [ items ] ); | ||
|
||
if ( ! items.length > 0 ) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<Popover | ||
focusOnMount={ false } | ||
onClose={ onReset } | ||
position="top right" | ||
className="components-autocomplete__popover" | ||
anchorRef={ anchorRef } | ||
> | ||
<div | ||
id={ listBoxId } | ||
role="listbox" | ||
className="components-autocomplete__results" | ||
> | ||
{ map( items, ( option, index ) => ( | ||
<Button | ||
key={ option.key } | ||
id={ `components-autocomplete-item-${ instanceId }-${ option.key }` } | ||
role="option" | ||
aria-selected={ index === selectedIndex } | ||
disabled={ option.isDisabled } | ||
className={ classnames( | ||
'components-autocomplete__result', | ||
className, | ||
{ | ||
'is-selected': index === selectedIndex, | ||
} | ||
) } | ||
onClick={ () => onSelect( option ) } | ||
> | ||
{ option.label } | ||
</Button> | ||
) ) } | ||
</div> | ||
</Popover> | ||
); | ||
} | ||
|
||
return AutocompleterUI; | ||
} |
213 changes: 213 additions & 0 deletions
213
packages/components/src/autocomplete/autocompleter-ui.native.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,213 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { | ||
View, | ||
Animated, | ||
StyleSheet, | ||
Text, | ||
TouchableOpacity, | ||
ScrollView, | ||
} from 'react-native'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
useLayoutEffect, | ||
useEffect, | ||
useRef, | ||
useState, | ||
useCallback, | ||
} from '@wordpress/element'; | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
import { | ||
Icon, | ||
__unstableAutocompletionItemsFill as AutocompletionItemsFill, | ||
} from '@wordpress/components'; | ||
import { usePreferredColorSchemeStyle } from '@wordpress/compose'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import BackgroundView from './background-view'; | ||
import getDefaultUseItems from './get-default-use-items'; | ||
import styles from './style.scss'; | ||
|
||
const { compose: stylesCompose } = StyleSheet; | ||
|
||
export function getAutoCompleterUI( autocompleter ) { | ||
const useItems = autocompleter.useItems | ||
? autocompleter.useItems | ||
: getDefaultUseItems( autocompleter ); | ||
|
||
function AutocompleterUI( { | ||
filterValue, | ||
selectedIndex, | ||
onChangeOptions, | ||
onSelect, | ||
value, | ||
reset, | ||
} ) { | ||
const [ items ] = useItems( filterValue ); | ||
const scrollViewRef = useRef(); | ||
const animationValue = useRef( new Animated.Value( 0 ) ).current; | ||
const [ isVisible, setIsVisible ] = useState( false ); | ||
const { text } = value; | ||
|
||
useEffect( () => { | ||
if ( ! isVisible && text.length > 0 ) { | ||
setIsVisible( true ); | ||
} | ||
}, [ isVisible, text ] ); | ||
|
||
useLayoutEffect( () => { | ||
onChangeOptions( items ); | ||
scrollViewRef.current?.scrollTo( { x: 0, animated: false } ); | ||
|
||
if ( isVisible && text.length > 0 ) { | ||
startAnimation( true ); | ||
} else if ( isVisible && text.length === 0 ) { | ||
startAnimation( false ); | ||
} | ||
}, [ items, isVisible, text ] ); | ||
|
||
const activeItemStyles = usePreferredColorSchemeStyle( | ||
styles[ 'components-autocomplete__item-active' ], | ||
styles[ 'components-autocomplete__item-active-dark' ] | ||
); | ||
|
||
const iconStyles = usePreferredColorSchemeStyle( | ||
styles[ 'components-autocomplete__icon' ], | ||
styles[ 'components-autocomplete__icon-active-dark' ] | ||
); | ||
|
||
const activeIconStyles = usePreferredColorSchemeStyle( | ||
styles[ 'components-autocomplete__icon-active ' ], | ||
styles[ 'components-autocomplete__icon-active-dark' ] | ||
); | ||
|
||
const textStyles = usePreferredColorSchemeStyle( | ||
styles[ 'components-autocomplete__text' ], | ||
styles[ 'components-autocomplete__text-dark' ] | ||
); | ||
|
||
const activeTextStyles = usePreferredColorSchemeStyle( | ||
styles[ 'components-autocomplete__text-active' ], | ||
styles[ 'components-autocomplete__text-active-dark' ] | ||
); | ||
|
||
const startAnimation = useCallback( | ||
( show ) => { | ||
Animated.timing( animationValue, { | ||
toValue: show ? 1 : 0, | ||
duration: show ? 200 : 100, | ||
useNativeDriver: true, | ||
} ).start( ( { finished } ) => { | ||
if ( finished && ! show && isVisible ) { | ||
setIsVisible( false ); | ||
reset(); | ||
} | ||
} ); | ||
}, | ||
[ isVisible ] | ||
); | ||
|
||
const contentStyles = { | ||
transform: [ | ||
{ | ||
translateY: animationValue.interpolate( { | ||
inputRange: [ 0, 1 ], | ||
outputRange: [ | ||
styles[ 'components-autocomplete' ].height, | ||
0, | ||
], | ||
} ), | ||
}, | ||
], | ||
}; | ||
|
||
if ( ! items.length > 0 || ! isVisible ) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<AutocompletionItemsFill> | ||
<View style={ styles[ 'components-autocomplete' ] }> | ||
<Animated.View style={ contentStyles }> | ||
<BackgroundView> | ||
<ScrollView | ||
ref={ scrollViewRef } | ||
horizontal | ||
contentContainerStyle={ | ||
styles[ 'components-autocomplete__content' ] | ||
} | ||
showsHorizontalScrollIndicator={ false } | ||
keyboardShouldPersistTaps="always" | ||
accessibilityLabel={ | ||
// translators: Slash inserter autocomplete results | ||
__( 'Slash inserter results' ) | ||
} | ||
> | ||
{ items.map( ( option, index ) => { | ||
const isActive = index === selectedIndex; | ||
const itemStyle = stylesCompose( | ||
styles[ | ||
'components-autocomplete__item' | ||
], | ||
isActive && activeItemStyles | ||
); | ||
const textStyle = stylesCompose( | ||
textStyles, | ||
isActive && activeTextStyles | ||
); | ||
const iconStyle = stylesCompose( | ||
iconStyles, | ||
isActive && activeIconStyles | ||
); | ||
|
||
return ( | ||
<TouchableOpacity | ||
activeOpacity={ 0.5 } | ||
style={ itemStyle } | ||
key={ index } | ||
onPress={ () => onSelect( option ) } | ||
accessibilityLabel={ sprintf( | ||
// translators: %s: Block name e.g. "Image block" | ||
__( '%s block' ), | ||
option?.value?.title | ||
) } | ||
> | ||
<View | ||
style={ | ||
styles[ | ||
'components-autocomplete__icon' | ||
] | ||
} | ||
> | ||
<Icon | ||
icon={ | ||
option?.value?.icon?.src | ||
} | ||
size={ 24 } | ||
style={ iconStyle } | ||
/> | ||
</View> | ||
<Text style={ textStyle }> | ||
{ option?.value?.title } | ||
</Text> | ||
</TouchableOpacity> | ||
); | ||
} ) } | ||
</ScrollView> | ||
</BackgroundView> | ||
</Animated.View> | ||
</View> | ||
</AutocompletionItemsFill> | ||
); | ||
} | ||
|
||
return AutocompleterUI; | ||
} | ||
|
||
export default getAutoCompleterUI; |
25 changes: 25 additions & 0 deletions
25
packages/components/src/autocomplete/background-view.android.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,25 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { View } from 'react-native'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { usePreferredColorSchemeStyle } from '@wordpress/compose'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import styles from './style.scss'; | ||
|
||
const BackgroundView = ( { children } ) => { | ||
const backgroundStyles = usePreferredColorSchemeStyle( | ||
styles[ 'components-autocomplete__background' ], | ||
styles[ 'components-autocomplete__background-dark' ] | ||
); | ||
|
||
return <View style={ backgroundStyles }>{ children }</View>; | ||
}; | ||
|
||
export default BackgroundView; |
23 changes: 23 additions & 0 deletions
23
packages/components/src/autocomplete/background-view.ios.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,23 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { BlurView } from '@react-native-community/blur'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import styles from './style.scss'; | ||
|
||
const BackgroundView = ( { children } ) => { | ||
return ( | ||
<BlurView | ||
style={ styles[ 'components-autocomplete__background-blur' ] } | ||
blurType="prominent" | ||
blurAmount={ 10 } | ||
> | ||
{ children } | ||
</BlurView> | ||
); | ||
}; | ||
|
||
export default BackgroundView; |
Oops, something went wrong.