From a779e95e68e328521e09ae03482e5feefe12f652 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 17 Mar 2022 17:20:04 +0100 Subject: [PATCH 01/41] Use animated scroll handler in KeyboardAwareFlatList --- .../keyboard-aware-flat-list/index.android.js | 20 +++- .../keyboard-aware-flat-list/index.ios.js | 108 ++++++++++-------- 2 files changed, 78 insertions(+), 50 deletions(-) diff --git a/packages/components/src/mobile/keyboard-aware-flat-list/index.android.js b/packages/components/src/mobile/keyboard-aware-flat-list/index.android.js index bdfc2ef1fd847..ffdd97dd5acbb 100644 --- a/packages/components/src/mobile/keyboard-aware-flat-list/index.android.js +++ b/packages/components/src/mobile/keyboard-aware-flat-list/index.android.js @@ -2,17 +2,27 @@ * External dependencies */ import { FlatList } from 'react-native'; +import Animated, { useAnimatedScrollHandler } from 'react-native-reanimated'; /** * Internal dependencies */ import KeyboardAvoidingView from '../keyboard-avoiding-view'; -export const KeyboardAwareFlatList = ( props ) => ( - - - -); +const AnimatedFlatList = Animated.createAnimatedComponent( FlatList ); + +export const KeyboardAwareFlatList = ( { innerRef, onScroll, ...props } ) => { + const scrollHandler = useAnimatedScrollHandler( { onScroll } ); + return ( + + + + ); +}; KeyboardAwareFlatList.handleCaretVerticalPositionChange = () => { // no need to handle on Android, it is system managed diff --git a/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js b/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js index 6c954d451dc17..4bd8e6fd81437 100644 --- a/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js +++ b/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js @@ -4,6 +4,10 @@ import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import { FlatList } from 'react-native'; import { isEqual } from 'lodash'; +import Animated, { + useAnimatedScrollHandler, + useSharedValue, +} from 'react-native-reanimated'; /** * WordPress dependencies @@ -11,6 +15,9 @@ import { isEqual } from 'lodash'; import { memo } from '@wordpress/element'; const List = memo( FlatList, isEqual ); +const AnimatedKeyboardAwareScrollView = Animated.createAnimatedComponent( + KeyboardAwareScrollView +); export const KeyboardAwareFlatList = ( { extraScrollHeight, @@ -19,53 +26,64 @@ export const KeyboardAwareFlatList = ( { autoScroll, scrollViewStyle, inputAccessoryViewHeight, + onScroll, ...listProps -} ) => ( - { - this.scrollViewRef = ref; - innerRef( ref ); - } } - onKeyboardWillHide={ () => { - this.keyboardWillShowIndicator = false; - } } - onKeyboardDidHide={ () => { - setTimeout( () => { - if ( - ! this.keyboardWillShowIndicator && - this.latestContentOffsetY !== undefined && - ! shouldPreventAutomaticScroll() - ) { - // Reset the content position if keyboard is still closed. - if ( this.scrollViewRef ) { - this.scrollViewRef.scrollToPosition( - 0, - this.latestContentOffsetY, - true - ); +} ) => { + const latestContentOffsetY = useSharedValue( -1 ); + const scrollHandler = useAnimatedScrollHandler( { + onScroll: ( event ) => { + const { contentOffset } = event; + latestContentOffsetY.value = contentOffset.y; + onScroll( event ); + }, + } ); + return ( + { + this.scrollViewRef = ref; + innerRef( ref ); + } } + onKeyboardWillHide={ () => { + this.keyboardWillShowIndicator = false; + } } + onKeyboardDidHide={ () => { + setTimeout( () => { + if ( + ! this.keyboardWillShowIndicator && + latestContentOffsetY.value !== -1 && + ! shouldPreventAutomaticScroll() + ) { + // Reset the content position if keyboard is still closed. + if ( this.scrollViewRef ) { + this.scrollViewRef.scrollToPosition( + 0, + latestContentOffsetY.value, + true + ); + } } - } - }, 50 ); - } } - onKeyboardWillShow={ () => { - this.keyboardWillShowIndicator = true; - } } - scrollEnabled={ listProps.scrollEnabled } - onScroll={ ( event ) => { - this.latestContentOffsetY = event.nativeEvent.contentOffset.y; - } } - > - - -); + }, 50 ); + } } + onKeyboardWillShow={ () => { + this.keyboardWillShowIndicator = true; + } } + scrollEnabled={ listProps.scrollEnabled } + onScroll={ scrollHandler } + > + + + ); +}; KeyboardAwareFlatList.handleCaretVerticalPositionChange = ( scrollView, From b71b30819e2a85d90713af9cb82fdcfc55120905 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 17 Mar 2022 18:15:57 +0100 Subject: [PATCH 02/41] Add hook for scrolling the block list while dragging --- .../use-scroll-when-dragging.native.js | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js diff --git a/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js b/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js new file mode 100644 index 0000000000000..385f86d1cc288 --- /dev/null +++ b/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js @@ -0,0 +1,108 @@ +/** + * External dependencies + */ +import { Dimensions } from 'react-native'; +import { + useSharedValue, + useAnimatedRef, + scrollTo, + useAnimatedReaction, + withTiming, + withRepeat, + cancelAnimation, + Easing, +} from 'react-native-reanimated'; + +/** + * Internal dependencies + */ +import { useBlockListContext } from '../block-list/block-list-context'; + +const SCROLL_VELOCITY = 180; +const SCROLL_THRESHOLD_LOW_VEL = 300; +const SCROLL_THRESHOLD_HIGH_VEL = 200; +const SCROLL_VELOCITY_MULTIPLIER = 3; + +export default function useScrollWhenDragging() { + const { scrollRef } = useBlockListContext(); + const animatedScrollRef = useAnimatedRef(); + animatedScrollRef( scrollRef ); + + const windowHeight = Dimensions.get( 'window' ).height; + + const velocityY = useSharedValue( 0 ); + const offsetY = useSharedValue( 0 ); + + const scroll = { + offsetY: useSharedValue( 0 ), + maxOffsetY: useSharedValue( 0 ), + }; + + const animation = useSharedValue( 0 ); + + const scrollHandler = ( event ) => { + 'worklet'; + const { contentSize, contentOffset, layoutMeasurement } = event; + scroll.offsetY.value = contentOffset.y; + scroll.maxOffsetY.value = contentSize.height - layoutMeasurement.height; + }; + + const stopScrolling = () => { + 'worklet'; + velocityY.value = 0; + cancelAnimation( animation ); + }; + + const startScrolling = () => { + 'worklet'; + stopScrolling(); + offsetY.value = scroll.offsetY.value; + animation.value = 0; + animation.value = withRepeat( + withTiming( 1, { duration: 1000, easing: Easing.linear } ), + -1, + true + ); + }; + + const scrollOnDragOver = ( y ) => { + 'worklet'; + if ( y < SCROLL_THRESHOLD_HIGH_VEL ) { + velocityY.value = -SCROLL_VELOCITY * SCROLL_VELOCITY_MULTIPLIER; + } else if ( y < SCROLL_THRESHOLD_LOW_VEL ) { + velocityY.value = -SCROLL_VELOCITY; + } else if ( y > windowHeight - SCROLL_THRESHOLD_HIGH_VEL ) { + velocityY.value = SCROLL_VELOCITY * SCROLL_VELOCITY_MULTIPLIER; + } else if ( y > windowHeight - SCROLL_THRESHOLD_LOW_VEL ) { + velocityY.value = SCROLL_VELOCITY; + } else { + velocityY.value = 0; + } + }; + + useAnimatedReaction( + () => animation.value, + ( value, previous ) => { + const delta = Math.abs( value - previous ); + let newOffset = offsetY.value + delta * velocityY.value; + + if ( scroll.maxOffsetY.value !== 0 ) { + newOffset = Math.max( + 0, + Math.min( scroll.maxOffsetY.value, newOffset ) + ); + } else { + // Scroll values are empty until receiving the first scroll event. + // In that case, the max offset is unknown and we can't clamp the + // new offset value. + newOffset = Math.max( 0, newOffset ); + } + offsetY.value = newOffset; + + if ( velocityY.value !== 0 ) + scrollTo( animatedScrollRef, 0, offsetY.value, false ); + } + ); + + return [ startScrolling, scrollOnDragOver, stopScrolling, scrollHandler ]; +} From faea4cfdd0f945066f78e2c4a1b2b11a7b3593b3 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 18 Mar 2022 13:08:04 +0100 Subject: [PATCH 03/41] Improve scroll animation in useScrollWhenDragging --- .../use-scroll-when-dragging.native.js | 73 +++++++++++++------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js b/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js index 385f86d1cc288..cdf9a5e26421e 100644 --- a/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js +++ b/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js @@ -18,10 +18,9 @@ import { */ import { useBlockListContext } from '../block-list/block-list-context'; -const SCROLL_VELOCITY = 180; -const SCROLL_THRESHOLD_LOW_VEL = 300; -const SCROLL_THRESHOLD_HIGH_VEL = 200; -const SCROLL_VELOCITY_MULTIPLIER = 3; +const SCROLL_INACTIVE_DISTANCE_PX = 50; +const SCROLL_INTERVAL_MS = 1000; +const VELOCITY_MULTIPLIER = 5000; export default function useScrollWhenDragging() { const { scrollRef } = useBlockListContext(); @@ -32,14 +31,15 @@ export default function useScrollWhenDragging() { const velocityY = useSharedValue( 0 ); const offsetY = useSharedValue( 0 ); + const dragStartY = useSharedValue( 0 ); + const animationTimer = useSharedValue( 0 ); + const isAnimationTimerActive = useSharedValue( false ); + const isScrollActive = useSharedValue( false ); const scroll = { offsetY: useSharedValue( 0 ), maxOffsetY: useSharedValue( 0 ), }; - - const animation = useSharedValue( 0 ); - const scrollHandler = ( event ) => { 'worklet'; const { contentSize, contentOffset, layoutMeasurement } = event; @@ -47,41 +47,63 @@ export default function useScrollWhenDragging() { scroll.maxOffsetY.value = contentSize.height - layoutMeasurement.height; }; + const animateVelocity = ( value ) => { + 'worklet'; + if ( value === 0 ) { + velocityY.value = withTiming( 0, { duration: 150 } ); + } else { + velocityY.value = withTiming( value, { duration: 150 } ); + } + }; + const stopScrolling = () => { 'worklet'; - velocityY.value = 0; - cancelAnimation( animation ); + isAnimationTimerActive.value = false; + isScrollActive.value = false; + animateVelocity( 0 ); }; - const startScrolling = () => { + const startScrolling = ( y ) => { 'worklet'; stopScrolling(); offsetY.value = scroll.offsetY.value; - animation.value = 0; - animation.value = withRepeat( - withTiming( 1, { duration: 1000, easing: Easing.linear } ), + dragStartY.value = y; + + animationTimer.value = 0; + animationTimer.value = withRepeat( + withTiming( 1, { + duration: SCROLL_INTERVAL_MS, + easing: Easing.linear, + } ), -1, true ); + isAnimationTimerActive.value = true; }; const scrollOnDragOver = ( y ) => { 'worklet'; - if ( y < SCROLL_THRESHOLD_HIGH_VEL ) { - velocityY.value = -SCROLL_VELOCITY * SCROLL_VELOCITY_MULTIPLIER; - } else if ( y < SCROLL_THRESHOLD_LOW_VEL ) { - velocityY.value = -SCROLL_VELOCITY; - } else if ( y > windowHeight - SCROLL_THRESHOLD_HIGH_VEL ) { - velocityY.value = SCROLL_VELOCITY * SCROLL_VELOCITY_MULTIPLIER; - } else if ( y > windowHeight - SCROLL_THRESHOLD_LOW_VEL ) { - velocityY.value = SCROLL_VELOCITY; + const dragDistance = Math.max( + Math.abs( y - dragStartY.value ) - SCROLL_INACTIVE_DISTANCE_PX, + 0 + ); + const distancePercentage = dragDistance / windowHeight; + + if ( ! isScrollActive.value ) { + isScrollActive.value = dragDistance > 0; + } else if ( y > dragStartY.value ) { + // User is dragging downwards. + animateVelocity( VELOCITY_MULTIPLIER * distancePercentage ); + } else if ( y < dragStartY.value ) { + // User is dragging upwards. + animateVelocity( -VELOCITY_MULTIPLIER * distancePercentage ); } else { - velocityY.value = 0; + animateVelocity( 0 ); } }; useAnimatedReaction( - () => animation.value, + () => animationTimer.value, ( value, previous ) => { const delta = Math.abs( value - previous ); let newOffset = offsetY.value + delta * velocityY.value; @@ -99,8 +121,11 @@ export default function useScrollWhenDragging() { } offsetY.value = newOffset; - if ( velocityY.value !== 0 ) + if ( velocityY.value !== 0 ) { scrollTo( animatedScrollRef, 0, offsetY.value, false ); + } else if ( ! isAnimationTimerActive.value ) { + cancelAnimation( animationTimer ); + } } ); From 799f0777afa55d25e3bbe29578e35d87a71f46bd Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 18 Mar 2022 19:00:09 +0100 Subject: [PATCH 04/41] Add draggable chip component --- .../block-draggable/draggable-chip.native.js | 36 +++++++++++++++++++ .../block-draggable/style.native.scss | 8 +++++ 2 files changed, 44 insertions(+) create mode 100644 packages/block-editor/src/components/block-draggable/draggable-chip.native.js create mode 100644 packages/block-editor/src/components/block-draggable/style.native.scss diff --git a/packages/block-editor/src/components/block-draggable/draggable-chip.native.js b/packages/block-editor/src/components/block-draggable/draggable-chip.native.js new file mode 100644 index 0000000000000..216090c6b29bc --- /dev/null +++ b/packages/block-editor/src/components/block-draggable/draggable-chip.native.js @@ -0,0 +1,36 @@ +/** + * External dependencies + */ +import { View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { dragHandle } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import BlockIcon from '../block-icon'; +import styles from './style.scss'; + +const shadowStyle = { + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.25, + shadowRadius: 3.84, + + elevation: 5, +}; + +export default function BlockDraggableChip( { icon } ) { + return ( + + + + + ); +} diff --git a/packages/block-editor/src/components/block-draggable/style.native.scss b/packages/block-editor/src/components/block-draggable/style.native.scss new file mode 100644 index 0000000000000..4319707eaaee8 --- /dev/null +++ b/packages/block-editor/src/components/block-draggable/style.native.scss @@ -0,0 +1,8 @@ +.draggable-chip__container { + flex-direction: row; + padding: 16px; + background-color: #f7f7f7; + border-radius: 8px; + top: -50%; + left: -50%; +} From 6aaae219f0f9dc624913ae18df85ef1a5ab39fc9 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 18 Mar 2022 19:07:41 +0100 Subject: [PATCH 05/41] Add block draggable component --- .../block-draggable/index.native.js | 194 ++++++++++++++++++ .../src/components/block-list/block.native.js | 44 ++-- .../src/components/block-list/index.native.js | 8 +- 3 files changed, 225 insertions(+), 21 deletions(-) create mode 100644 packages/block-editor/src/components/block-draggable/index.native.js diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js new file mode 100644 index 0000000000000..d8a4030df5189 --- /dev/null +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -0,0 +1,194 @@ +/** + * External dependencies + */ +import Animated, { + useSharedValue, + useAnimatedStyle, + runOnJS, + withTiming, + withSpring, +} from 'react-native-reanimated'; + +/** + * WordPress dependencies + */ +import { getBlockType } from '@wordpress/blocks'; +import { Draggable } from '@wordpress/components'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { useEffect, createContext, useContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import useScrollWhenDragging from './use-scroll-when-dragging'; +import DraggableChip from './draggable-chip'; +import { store as blockEditorStore } from '../../store'; + +const Context = createContext( { dragHandler: () => null } ); +const { Provider } = Context; + +const BlockDraggableWrapper = ( { children } ) => { + const { startDraggingBlocks, stopDraggingBlocks } = useDispatch( + blockEditorStore + ); + + const translation = { + x: useSharedValue( 0 ), + y: useSharedValue( 0 ), + startX: useSharedValue( 0 ), + startY: useSharedValue( 0 ), + }; + const scale = useSharedValue( 0 ); + const isDragging = useSharedValue( false ); + + const [ + startScrolling, + scrollOnDragOver, + stopScrolling, + scrollHandler, + ] = useScrollWhenDragging(); + + const { blockIcon } = useSelect( ( select ) => { + const { getBlockName, getDraggedBlockClientIds } = select( + blockEditorStore + ); + const draggedBlockClientIds = getDraggedBlockClientIds(); + const blockName = getBlockName( draggedBlockClientIds[ 0 ] ); + + return { + blockIcon: getBlockType( blockName )?.icon, + }; + } ); + + // Stop dragging blocks if the block draggable is unmounted. + useEffect( () => { + return () => { + if ( isDragging.value ) { + stopDraggingBlocks(); + } + }; + }, [] ); + + const startDragging = ( clientIds, { absoluteX: x, absoluteY: y } ) => { + 'worklet'; + runOnJS( startDraggingBlocks )( clientIds ); + + translation.x.value = x; + translation.y.value = y; + + isDragging.value = true; + scale.value = withSpring( 1 ); + + startScrolling( y ); + }; + + const updateDragging = ( { absoluteX: x, absoluteY: y } ) => { + 'worklet'; + // Update scrolling velocity + scrollOnDragOver( y ); + + translation.x.value = x; + translation.y.value = y; + }; + + const stopDragging = () => { + 'worklet'; + scale.value = withSpring( 0, ( completed ) => { + if ( completed ) { + isDragging.value = false; + runOnJS( stopDraggingBlocks )(); + } + } ); + + stopScrolling(); + }; + + const dragStyles = useAnimatedStyle( () => { + return { + position: 'absolute', + top: 0, + left: 0, + transform: [ + { translateX: translation.x.value }, + { translateY: translation.y.value }, + { scaleX: scale.value }, + { scaleY: scale.value }, + ], + }; + } ); + + return ( + + { children( { onScroll: scrollHandler } ) } + + + + + ); +}; + +const BlockDraggable = ( { clientIds, children } ) => { + const { startDragging, updateDragging, stopDragging } = useContext( + Context + ); + const opacity = useSharedValue( 1 ); + + const startBlockDragging = ( event ) => { + 'worklet'; + startDragging( clientIds, event ); + opacity.value = withTiming( 0 ); + }; + + const stopBlockDragging = () => { + 'worklet'; + stopDragging(); + opacity.value = withTiming( 1 ); + }; + + const { isDraggable } = useSelect( + ( select ) => { + const { getBlockRootClientId, getTemplateLock } = select( + blockEditorStore + ); + const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); + const templateLock = rootClientId + ? getTemplateLock( rootClientId ) + : null; + + return { + isDraggable: 'all' !== templateLock, + }; + }, + [ clientIds ] + ); + + const blockStyles = useAnimatedStyle( () => { + return { + opacity: opacity.value, + }; + } ); + + if ( ! isDraggable ) { + return children( { isDraggable: false } ); + } + + return ( + + { children( { isDraggable: true } ) } + + ); +}; + +export { BlockDraggableWrapper }; +export default BlockDraggable; diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index 2ee69691ab5a0..aea1f11a81bba 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -30,6 +30,7 @@ import styles from './block.scss'; import BlockEdit from '../block-edit'; import BlockInvalidWarning from './block-invalid-warning'; import BlockMobileToolbar from '../block-mobile-toolbar'; +import BlockDraggable from '../block-draggable'; import { store as blockEditorStore } from '../../store'; const emptyArray = []; @@ -77,25 +78,30 @@ function BlockForType( { return ( - + + { () => ( + + ) } + + ); diff --git a/packages/block-editor/src/components/block-list/index.native.js b/packages/block-editor/src/components/block-list/index.native.js index f1dc31da0e91e..1f52d0fd7441f 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -31,6 +31,7 @@ import { BlockListConsumer, DEFAULT_BLOCK_LIST_CONTEXT, } from './block-list-context'; +import { BlockDraggableWrapper } from '../block-draggable'; import { store as blockEditorStore } from '../../store'; export const OnCaretVerticalPositionChange = createContext(); @@ -197,7 +198,9 @@ export class BlockList extends Component { scrollRef: this.scrollViewRef, } } > - { this.renderList() } + + { ( { onScroll } ) => this.renderList( { onScroll } ) } + ) : ( @@ -235,7 +238,7 @@ export class BlockList extends Component { contentResizeMode, blockWidth, } = this.props; - const { parentScrollRef } = extraProps; + const { parentScrollRef, onScroll } = extraProps; const { blockToolbar, @@ -310,6 +313,7 @@ export class BlockList extends Component { ListHeaderComponent={ header } ListEmptyComponent={ ! isReadOnly && this.renderEmptyList } ListFooterComponent={ this.renderBlockListFooter } + onScroll={ onScroll } /> { this.shouldShowInnerBlockAppender() && ( Date: Mon, 21 Mar 2022 11:09:49 +0100 Subject: [PATCH 06/41] Remove icon prop from draggable chip component --- .../block-draggable/draggable-chip.native.js | 19 +++++++++++++++++-- .../block-draggable/index.native.js | 15 +-------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/draggable-chip.native.js b/packages/block-editor/src/components/block-draggable/draggable-chip.native.js index 216090c6b29bc..920e8535966ea 100644 --- a/packages/block-editor/src/components/block-draggable/draggable-chip.native.js +++ b/packages/block-editor/src/components/block-draggable/draggable-chip.native.js @@ -7,12 +7,15 @@ import { View } from 'react-native'; * WordPress dependencies */ import { dragHandle } from '@wordpress/icons'; +import { useSelect } from '@wordpress/data'; +import { getBlockType } from '@wordpress/blocks'; /** * Internal dependencies */ import BlockIcon from '../block-icon'; import styles from './style.scss'; +import { store as blockEditorStore } from '../../store'; const shadowStyle = { shadowColor: '#000', @@ -26,11 +29,23 @@ const shadowStyle = { elevation: 5, }; -export default function BlockDraggableChip( { icon } ) { +export default function BlockDraggableChip() { + const { blockIcon } = useSelect( ( select ) => { + const { getBlockName, getDraggedBlockClientIds } = select( + blockEditorStore + ); + const draggedBlockClientIds = getDraggedBlockClientIds(); + const blockName = getBlockName( draggedBlockClientIds[ 0 ] ); + + return { + blockIcon: getBlockType( blockName )?.icon, + }; + } ); + return ( - + ); } diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index d8a4030df5189..42513fccd44c1 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -12,7 +12,6 @@ import Animated, { /** * WordPress dependencies */ -import { getBlockType } from '@wordpress/blocks'; import { Draggable } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect, createContext, useContext } from '@wordpress/element'; @@ -48,18 +47,6 @@ const BlockDraggableWrapper = ( { children } ) => { scrollHandler, ] = useScrollWhenDragging(); - const { blockIcon } = useSelect( ( select ) => { - const { getBlockName, getDraggedBlockClientIds } = select( - blockEditorStore - ); - const draggedBlockClientIds = getDraggedBlockClientIds(); - const blockName = getBlockName( draggedBlockClientIds[ 0 ] ); - - return { - blockIcon: getBlockType( blockName )?.icon, - }; - } ); - // Stop dragging blocks if the block draggable is unmounted. useEffect( () => { return () => { @@ -127,7 +114,7 @@ const BlockDraggableWrapper = ( { children } ) => { > { children( { onScroll: scrollHandler } ) } - + ); From 64708c59f1b203c8500ec4f24228b57087acf392 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 21 Mar 2022 13:17:25 +0100 Subject: [PATCH 07/41] Add draggable placeholder --- .../block-draggable/index.native.js | 71 +++++++++++++++---- .../block-draggable/style.native.scss | 13 +++- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 42513fccd44c1..6c9b99f46a4d9 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -1,12 +1,15 @@ /** * External dependencies */ +import { View } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, runOnJS, withTiming, - withSpring, + interpolate, + useAnimatedRef, + measure, } from 'react-native-reanimated'; /** @@ -22,10 +25,13 @@ import { useEffect, createContext, useContext } from '@wordpress/element'; import useScrollWhenDragging from './use-scroll-when-dragging'; import DraggableChip from './draggable-chip'; import { store as blockEditorStore } from '../../store'; +import styles from './style.scss'; const Context = createContext( { dragHandler: () => null } ); const { Provider } = Context; +const BLOCK_PLACEHOLDER_HEIGHT = 20; + const BlockDraggableWrapper = ( { children } ) => { const { startDraggingBlocks, stopDraggingBlocks } = useDispatch( blockEditorStore @@ -124,18 +130,27 @@ const BlockDraggable = ( { clientIds, children } ) => { const { startDragging, updateDragging, stopDragging } = useContext( Context ); - const opacity = useSharedValue( 1 ); + + const animatedContainerRef = useAnimatedRef(); + const container = { + height: useSharedValue( 0 ), + opacity: useSharedValue( 1 ), + }; const startBlockDragging = ( event ) => { 'worklet'; startDragging( clientIds, event ); - opacity.value = withTiming( 0 ); + + const containerLayout = measure( animatedContainerRef ); + container.height.value = containerLayout.height; + + container.opacity.value = withTiming( 0 ); }; const stopBlockDragging = () => { 'worklet'; stopDragging(); - opacity.value = withTiming( 1 ); + container.opacity.value = withTiming( 1 ); }; const { isDraggable } = useSelect( @@ -156,8 +171,29 @@ const BlockDraggable = ( { clientIds, children } ) => { ); const blockStyles = useAnimatedStyle( () => { + const height = interpolate( + container.opacity.value, + [ 0, 1 ], + [ BLOCK_PLACEHOLDER_HEIGHT, container.height.value ] + ); + return { - opacity: opacity.value, + opacity: container.opacity.value, + height: container.opacity.value === 1 ? 'auto' : height, + }; + } ); + + const placeholderStyles = useAnimatedStyle( () => { + const height = interpolate( + container.opacity.value, + [ 0, 1 ], + [ BLOCK_PLACEHOLDER_HEIGHT, container.height.value ] + ); + + return { + display: container.opacity.value === 1 ? 'none' : 'flex', + opacity: 1 - container.opacity.value, + height, }; } ); @@ -166,14 +202,23 @@ const BlockDraggable = ( { clientIds, children } ) => { } return ( - - { children( { isDraggable: true } ) } - + + + { children( { isDraggable: true } ) } + + + ); }; diff --git a/packages/block-editor/src/components/block-draggable/style.native.scss b/packages/block-editor/src/components/block-draggable/style.native.scss index 4319707eaaee8..37db1fdb5166f 100644 --- a/packages/block-editor/src/components/block-draggable/style.native.scss +++ b/packages/block-editor/src/components/block-draggable/style.native.scss @@ -3,6 +3,15 @@ padding: 16px; background-color: #f7f7f7; border-radius: 8px; - top: -50%; - left: -50%; +} + +.draggable-placeholder__container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 10; + background-color: $gray-lighten-30; + border-radius: 8px; } From 778b10ad49aed8886fbd630144d8edf7c4ca20f0 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 21 Mar 2022 13:18:39 +0100 Subject: [PATCH 08/41] Fix draggable chip location --- .../block-draggable/index.native.js | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 6c9b99f46a4d9..72c20c5b8a968 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -25,11 +25,13 @@ import { useEffect, createContext, useContext } from '@wordpress/element'; import useScrollWhenDragging from './use-scroll-when-dragging'; import DraggableChip from './draggable-chip'; import { store as blockEditorStore } from '../../store'; +import { useBlockListContext } from '../block-list/block-list-context'; import styles from './style.scss'; const Context = createContext( { dragHandler: () => null } ); const { Provider } = Context; +const CHIP_POSITION_PADDING = 32; const BLOCK_PLACEHOLDER_HEIGHT = 20; const BlockDraggableWrapper = ( { children } ) => { @@ -37,13 +39,23 @@ const BlockDraggableWrapper = ( { children } ) => { blockEditorStore ); - const translation = { + const { scrollRef } = useBlockListContext(); + const animatedScrollRef = useAnimatedRef(); + animatedScrollRef( scrollRef ); + + const scroll = { + x: useSharedValue( 0 ), + y: useSharedValue( 0 ), + }; + const chip = { x: useSharedValue( 0 ), y: useSharedValue( 0 ), startX: useSharedValue( 0 ), startY: useSharedValue( 0 ), + width: useSharedValue( 0 ), + height: useSharedValue( 0 ), + opacity: useSharedValue( 0 ), }; - const scale = useSharedValue( 0 ); const isDragging = useSharedValue( false ); const [ @@ -62,15 +74,25 @@ const BlockDraggableWrapper = ( { children } ) => { }; }, [] ); + const onChipLayout = ( { nativeEvent: { layout } } ) => { + chip.width.value = layout.width; + chip.height.value = layout.height; + }; + const startDragging = ( clientIds, { absoluteX: x, absoluteY: y } ) => { 'worklet'; runOnJS( startDraggingBlocks )( clientIds ); - translation.x.value = x; - translation.y.value = y; + const scrollLayout = measure( animatedScrollRef ); + scroll.x.value = scrollLayout.pageX; + scroll.y.value = scrollLayout.pageY; + + chip.x.value = x - scroll.x.value - chip.width.value / 2; + chip.y.value = + y - scroll.y.value - chip.height.value - CHIP_POSITION_PADDING; isDragging.value = true; - scale.value = withSpring( 1 ); + chip.opacity.value = withTiming( 1 ); startScrolling( y ); }; @@ -80,13 +102,14 @@ const BlockDraggableWrapper = ( { children } ) => { // Update scrolling velocity scrollOnDragOver( y ); - translation.x.value = x; - translation.y.value = y; + chip.x.value = x - scroll.x.value - chip.width.value / 2; + chip.y.value = + y - scroll.y.value - chip.height.value - CHIP_POSITION_PADDING; }; const stopDragging = () => { 'worklet'; - scale.value = withSpring( 0, ( completed ) => { + chip.opacity.value = withTiming( 0.2, ( completed ) => { if ( completed ) { isDragging.value = false; runOnJS( stopDraggingBlocks )(); @@ -96,16 +119,13 @@ const BlockDraggableWrapper = ( { children } ) => { stopScrolling(); }; - const dragStyles = useAnimatedStyle( () => { + const chipStyles = useAnimatedStyle( () => { return { position: 'absolute', - top: 0, - left: 0, + opacity: chip.opacity.value, transform: [ - { translateX: translation.x.value }, - { translateY: translation.y.value }, - { scaleX: scale.value }, - { scaleY: scale.value }, + { translateX: chip.x.value }, + { translateY: chip.y.value }, ], }; } ); @@ -119,7 +139,11 @@ const BlockDraggableWrapper = ( { children } ) => { } } > { children( { onScroll: scrollHandler } ) } - + From 3822b2770f407caf65497d12084afd588177e132 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 21 Mar 2022 16:53:25 +0100 Subject: [PATCH 09/41] Wrap BlockListItemCell with BlockDraggable --- .../block-list/block-list-item-cell.native.js | 7 ++- .../src/components/block-list/block.native.js | 44 ++++++++----------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block-list-item-cell.native.js b/packages/block-editor/src/components/block-list/block-list-item-cell.native.js index c399643a63399..cf29ae9b128ff 100644 --- a/packages/block-editor/src/components/block-list/block-list-item-cell.native.js +++ b/packages/block-editor/src/components/block-list/block-list-item-cell.native.js @@ -12,6 +12,7 @@ import { useEffect, useCallback } from '@wordpress/element'; * Internal dependencies */ import { useBlockListContext } from './block-list-context'; +import BlockDraggable from '../block-draggable'; function BlockListItemCell( { children, clientId, rootClientId } ) { const { blocksLayouts, updateBlocksLayouts } = useBlockListContext(); @@ -36,7 +37,11 @@ function BlockListItemCell( { children, clientId, rootClientId } ) { [ clientId, rootClientId, updateBlocksLayouts ] ); - return { children }; + return ( + + { () => { children } } + + ); } export default BlockListItemCell; diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index aea1f11a81bba..2ee69691ab5a0 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -30,7 +30,6 @@ import styles from './block.scss'; import BlockEdit from '../block-edit'; import BlockInvalidWarning from './block-invalid-warning'; import BlockMobileToolbar from '../block-mobile-toolbar'; -import BlockDraggable from '../block-draggable'; import { store as blockEditorStore } from '../../store'; const emptyArray = []; @@ -78,30 +77,25 @@ function BlockForType( { return ( - - { () => ( - - ) } - - + ); From 36880136620f87377048ce3b96875e2673d5d7f2 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 21 Mar 2022 16:55:27 +0100 Subject: [PATCH 10/41] Fix block draggable placeholder style --- .../src/components/block-draggable/style.native.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/style.native.scss b/packages/block-editor/src/components/block-draggable/style.native.scss index 37db1fdb5166f..4baede2d8b88e 100644 --- a/packages/block-editor/src/components/block-draggable/style.native.scss +++ b/packages/block-editor/src/components/block-draggable/style.native.scss @@ -8,8 +8,8 @@ .draggable-placeholder__container { position: absolute; top: 0; - left: 0; - right: 0; + left: $block-selected-margin; + right: $block-selected-margin; bottom: 0; z-index: 10; background-color: $gray-lighten-30; From 14a9d6b620ea6f85ab87f952e2bf2d489b4f8baa Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 21 Mar 2022 16:56:57 +0100 Subject: [PATCH 11/41] Animate scale property instead of opacity of draggable chip --- .../block-draggable/index.native.js | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 72c20c5b8a968..1fea9cbde55a2 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -54,7 +54,7 @@ const BlockDraggableWrapper = ( { children } ) => { startY: useSharedValue( 0 ), width: useSharedValue( 0 ), height: useSharedValue( 0 ), - opacity: useSharedValue( 0 ), + scale: useSharedValue( 0 ), }; const isDragging = useSharedValue( false ); @@ -87,12 +87,11 @@ const BlockDraggableWrapper = ( { children } ) => { scroll.x.value = scrollLayout.pageX; scroll.y.value = scrollLayout.pageY; - chip.x.value = x - scroll.x.value - chip.width.value / 2; - chip.y.value = - y - scroll.y.value - chip.height.value - CHIP_POSITION_PADDING; + chip.x.value = x - scroll.x.value; + chip.y.value = y - scroll.y.value; isDragging.value = true; - chip.opacity.value = withTiming( 1 ); + chip.scale.value = withTiming( 1 ); startScrolling( y ); }; @@ -102,14 +101,13 @@ const BlockDraggableWrapper = ( { children } ) => { // Update scrolling velocity scrollOnDragOver( y ); - chip.x.value = x - scroll.x.value - chip.width.value / 2; - chip.y.value = - y - scroll.y.value - chip.height.value - CHIP_POSITION_PADDING; + chip.x.value = x - scroll.x.value; + chip.y.value = y - scroll.y.value; }; const stopDragging = () => { 'worklet'; - chip.opacity.value = withTiming( 0.2, ( completed ) => { + chip.scale.value = withTiming( 0, ( completed ) => { if ( completed ) { isDragging.value = false; runOnJS( stopDraggingBlocks )(); @@ -122,10 +120,16 @@ const BlockDraggableWrapper = ( { children } ) => { const chipStyles = useAnimatedStyle( () => { return { position: 'absolute', - opacity: chip.opacity.value, transform: [ - { translateX: chip.x.value }, - { translateY: chip.y.value }, + { translateX: chip.x.value - chip.width.value / 2 }, + { + translateY: + chip.y.value - + chip.height.value - + CHIP_POSITION_PADDING, + }, + { scaleX: chip.scale.value }, + { scaleY: chip.scale.value }, ], }; } ); From 90838fd295443aece4dad663a041cd27448163ad Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 21 Mar 2022 17:00:54 +0100 Subject: [PATCH 12/41] Fix draggable placeholder container height calculation --- .../components/block-draggable/index.native.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 1fea9cbde55a2..d3df39bcb3f9c 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -159,18 +159,21 @@ const BlockDraggable = ( { clientIds, children } ) => { Context ); - const animatedContainerRef = useAnimatedRef(); const container = { height: useSharedValue( 0 ), opacity: useSharedValue( 1 ), }; + const startDraggingContainerHeight = useSharedValue( 0 ); + + const onContainerLayout = ( { nativeEvent: { layout } } ) => { + container.height.value = layout.height; + }; const startBlockDragging = ( event ) => { 'worklet'; startDragging( clientIds, event ); - const containerLayout = measure( animatedContainerRef ); - container.height.value = containerLayout.height; + startDraggingContainerHeight.value = container.height.value; container.opacity.value = withTiming( 0 ); }; @@ -202,12 +205,12 @@ const BlockDraggable = ( { clientIds, children } ) => { const height = interpolate( container.opacity.value, [ 0, 1 ], - [ BLOCK_PLACEHOLDER_HEIGHT, container.height.value ] + [ BLOCK_PLACEHOLDER_HEIGHT, startDraggingContainerHeight.value ] ); return { opacity: container.opacity.value, - height: container.opacity.value === 1 ? 'auto' : height, + height: startDraggingContainerHeight.value !== 0 ? height : 'auto', }; } ); @@ -215,7 +218,7 @@ const BlockDraggable = ( { clientIds, children } ) => { const height = interpolate( container.opacity.value, [ 0, 1 ], - [ BLOCK_PLACEHOLDER_HEIGHT, container.height.value ] + [ BLOCK_PLACEHOLDER_HEIGHT, startDraggingContainerHeight.value ] ); return { @@ -230,7 +233,7 @@ const BlockDraggable = ( { clientIds, children } ) => { } return ( - + Date: Tue, 22 Mar 2022 13:05:48 +0100 Subject: [PATCH 13/41] Fix BlockDraggable height animation --- .../block-draggable/index.native.js | 48 +++++++++---------- .../block-draggable/style.native.scss | 4 +- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index d3df39bcb3f9c..cfc99c29d13e7 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -1,7 +1,6 @@ /** * External dependencies */ -import { View } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, @@ -32,7 +31,7 @@ const Context = createContext( { dragHandler: () => null } ); const { Provider } = Context; const CHIP_POSITION_PADDING = 32; -const BLOCK_PLACEHOLDER_HEIGHT = 20; +const BLOCK_COLLAPSED_HEIGHT = 20; const BlockDraggableWrapper = ( { children } ) => { const { startDraggingBlocks, stopDraggingBlocks } = useDispatch( @@ -161,9 +160,9 @@ const BlockDraggable = ( { clientIds, children } ) => { const container = { height: useSharedValue( 0 ), - opacity: useSharedValue( 1 ), }; - const startDraggingContainerHeight = useSharedValue( 0 ); + const containerHeightBeforeDragging = useSharedValue( 0 ); + const collapseAnimation = useSharedValue( 0 ); const onContainerLayout = ( { nativeEvent: { layout } } ) => { container.height.value = layout.height; @@ -172,16 +171,14 @@ const BlockDraggable = ( { clientIds, children } ) => { const startBlockDragging = ( event ) => { 'worklet'; startDragging( clientIds, event ); - - startDraggingContainerHeight.value = container.height.value; - - container.opacity.value = withTiming( 0 ); + containerHeightBeforeDragging.value = container.height.value; + collapseAnimation.value = withTiming( 1 ); }; const stopBlockDragging = () => { 'worklet'; stopDragging(); - container.opacity.value = withTiming( 1 ); + collapseAnimation.value = withTiming( 0 ); }; const { isDraggable } = useSelect( @@ -201,30 +198,31 @@ const BlockDraggable = ( { clientIds, children } ) => { [ clientIds ] ); - const blockStyles = useAnimatedStyle( () => { + const containerStyles = useAnimatedStyle( () => { const height = interpolate( - container.opacity.value, + collapseAnimation.value, [ 0, 1 ], - [ BLOCK_PLACEHOLDER_HEIGHT, startDraggingContainerHeight.value ] + [ containerHeightBeforeDragging.value, BLOCK_COLLAPSED_HEIGHT ] ); + return { + height: + containerHeightBeforeDragging.value === 0 || + collapseAnimation.value === 0 + ? 'auto' + : height, + }; + } ); + const blockStyles = useAnimatedStyle( () => { return { - opacity: container.opacity.value, - height: startDraggingContainerHeight.value !== 0 ? height : 'auto', + opacity: 1 - collapseAnimation.value, }; } ); const placeholderStyles = useAnimatedStyle( () => { - const height = interpolate( - container.opacity.value, - [ 0, 1 ], - [ BLOCK_PLACEHOLDER_HEIGHT, startDraggingContainerHeight.value ] - ); - return { - display: container.opacity.value === 1 ? 'none' : 'flex', - opacity: 1 - container.opacity.value, - height, + display: collapseAnimation.value === 0 ? 'none' : 'flex', + opacity: collapseAnimation.value, }; } ); @@ -233,7 +231,7 @@ const BlockDraggable = ( { clientIds, children } ) => { } return ( - + { ] } pointerEvents="none" /> - + ); }; diff --git a/packages/block-editor/src/components/block-draggable/style.native.scss b/packages/block-editor/src/components/block-draggable/style.native.scss index 4baede2d8b88e..4ffac6b1dde83 100644 --- a/packages/block-editor/src/components/block-draggable/style.native.scss +++ b/packages/block-editor/src/components/block-draggable/style.native.scss @@ -8,8 +8,8 @@ .draggable-placeholder__container { position: absolute; top: 0; - left: $block-selected-margin; - right: $block-selected-margin; + left: $solid-border-space; + right: $solid-border-space; bottom: 0; z-index: 10; background-color: $gray-lighten-30; From ee179a94890dea8b6ab89eb9338d67b8f8d2ed5b Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 23 Mar 2022 15:39:08 +0100 Subject: [PATCH 14/41] Move draggable to BlockDraggableWrapper --- .../block-draggable/index.native.js | 113 +++++++++--------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index cfc99c29d13e7..040d7c98a428b 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -2,13 +2,14 @@ * External dependencies */ import Animated, { - useSharedValue, - useAnimatedStyle, - runOnJS, - withTiming, interpolate, - useAnimatedRef, measure, + runOnJS, + runOnUI, + useAnimatedRef, + useAnimatedStyle, + useSharedValue, + withTiming, } from 'react-native-reanimated'; /** @@ -16,7 +17,7 @@ import Animated, { */ import { Draggable } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useEffect, createContext, useContext } from '@wordpress/element'; +import { useEffect } from '@wordpress/element'; /** * Internal dependencies @@ -27,14 +28,11 @@ import { store as blockEditorStore } from '../../store'; import { useBlockListContext } from '../block-list/block-list-context'; import styles from './style.scss'; -const Context = createContext( { dragHandler: () => null } ); -const { Provider } = Context; - const CHIP_POSITION_PADDING = 32; const BLOCK_COLLAPSED_HEIGHT = 20; const BlockDraggableWrapper = ( { children } ) => { - const { startDraggingBlocks, stopDraggingBlocks } = useDispatch( + const { /*startDraggingBlocks,*/ stopDraggingBlocks } = useDispatch( blockEditorStore ); @@ -73,46 +71,51 @@ const BlockDraggableWrapper = ( { children } ) => { }; }, [] ); + const setDraggingBlockByPosition = () => { + // TODO: Get clientId from blocks layouts data by position + // startDraggingBlocks( [ clientIdFromBlocksLayouts ] ); + }; + const onChipLayout = ( { nativeEvent: { layout } } ) => { chip.width.value = layout.width; chip.height.value = layout.height; }; - const startDragging = ( clientIds, { absoluteX: x, absoluteY: y } ) => { + const startDragging = ( { absoluteX, absoluteY } ) => { 'worklet'; - runOnJS( startDraggingBlocks )( clientIds ); - const scrollLayout = measure( animatedScrollRef ); scroll.x.value = scrollLayout.pageX; scroll.y.value = scrollLayout.pageY; - chip.x.value = x - scroll.x.value; - chip.y.value = y - scroll.y.value; + const dragPosition = { + x: absoluteX - scroll.x.value, + y: absoluteY - scroll.y.value, + }; + chip.x.value = dragPosition.x; + chip.y.value = dragPosition.y; isDragging.value = true; - chip.scale.value = withTiming( 1 ); - startScrolling( y ); + chip.scale.value = withTiming( 1 ); + runOnJS( setDraggingBlockByPosition )( dragPosition ); + startScrolling( absoluteY ); }; - const updateDragging = ( { absoluteX: x, absoluteY: y } ) => { + const updateDragging = ( { absoluteX, absoluteY } ) => { 'worklet'; - // Update scrolling velocity - scrollOnDragOver( y ); + chip.x.value = absoluteX - scroll.x.value; + chip.y.value = absoluteY - scroll.y.value; - chip.x.value = x - scroll.x.value; - chip.y.value = y - scroll.y.value; + // Update scrolling velocity + scrollOnDragOver( absoluteY ); }; const stopDragging = () => { 'worklet'; - chip.scale.value = withTiming( 0, ( completed ) => { - if ( completed ) { - isDragging.value = false; - runOnJS( stopDraggingBlocks )(); - } - } ); + isDragging.value = false; + chip.scale.value = withTiming( 0 ); + runOnJS( stopDraggingBlocks )(); stopScrolling(); }; @@ -134,14 +137,14 @@ const BlockDraggableWrapper = ( { children } ) => { } ); return ( - - { children( { onScroll: scrollHandler } ) } + <> + + { children( { onScroll: scrollHandler } ) } + { > - + ); }; const BlockDraggable = ( { clientIds, children } ) => { - const { startDragging, updateDragging, stopDragging } = useContext( - Context - ); - const container = { height: useSharedValue( 0 ), }; @@ -168,36 +167,45 @@ const BlockDraggable = ( { clientIds, children } ) => { container.height.value = layout.height; }; - const startBlockDragging = ( event ) => { + const startBlockDragging = () => { 'worklet'; - startDragging( clientIds, event ); containerHeightBeforeDragging.value = container.height.value; collapseAnimation.value = withTiming( 1 ); }; const stopBlockDragging = () => { 'worklet'; - stopDragging(); collapseAnimation.value = withTiming( 0 ); }; - const { isDraggable } = useSelect( + const { isDraggable, isBeingDragged } = useSelect( ( select ) => { - const { getBlockRootClientId, getTemplateLock } = select( - blockEditorStore - ); + const { + getBlockRootClientId, + getTemplateLock, + isBlockBeingDragged, + } = select( blockEditorStore ); const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); const templateLock = rootClientId ? getTemplateLock( rootClientId ) : null; return { + isBeingDragged: isBlockBeingDragged( clientIds[ 0 ] ), isDraggable: 'all' !== templateLock, }; }, [ clientIds ] ); + useEffect( () => { + if ( isBeingDragged ) { + runOnUI( startBlockDragging )(); + } else { + runOnUI( stopBlockDragging )(); + } + }, [ isBeingDragged ] ); + const containerStyles = useAnimatedStyle( () => { const height = interpolate( collapseAnimation.value, @@ -232,14 +240,9 @@ const BlockDraggable = ( { clientIds, children } ) => { return ( - + { children( { isDraggable: true } ) } - + Date: Wed, 23 Mar 2022 19:21:03 +0100 Subject: [PATCH 15/41] Disable isDragging when long-press gesture ends --- packages/components/src/draggable/index.native.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/draggable/index.native.js b/packages/components/src/draggable/index.native.js index 016bd084180cd..9716b8230e1a6 100644 --- a/packages/components/src/draggable/index.native.js +++ b/packages/components/src/draggable/index.native.js @@ -45,6 +45,7 @@ export default function Draggable( { } ) .onEnd( () => { 'worklet'; + isDragging.value = false; if ( onDragEnd ) { onDragEnd(); } From 95fd2e1fa3c0af0800eace7e92607669fb762039 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 23 Mar 2022 19:22:24 +0100 Subject: [PATCH 16/41] Fix onLayout calculation in block list item cell --- .../components/block-list/block-list-item-cell.native.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block-list-item-cell.native.js b/packages/block-editor/src/components/block-list/block-list-item-cell.native.js index cf29ae9b128ff..1055abf4d1064 100644 --- a/packages/block-editor/src/components/block-list/block-list-item-cell.native.js +++ b/packages/block-editor/src/components/block-list/block-list-item-cell.native.js @@ -38,9 +38,11 @@ function BlockListItemCell( { children, clientId, rootClientId } ) { ); return ( - - { () => { children } } - + + + { () => children } + + ); } From 593644d0b6efc543f4dee76320f30d76b4a9b749 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 23 Mar 2022 19:22:56 +0100 Subject: [PATCH 17/41] Add findBlockLayoutByPosition helper --- .../block-list/block-list-context.native.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/block-editor/src/components/block-list/block-list-context.native.js b/packages/block-editor/src/components/block-list/block-list-context.native.js index 95385b480b3d1..30309c3c4ed62 100644 --- a/packages/block-editor/src/components/block-list/block-list-context.native.js +++ b/packages/block-editor/src/components/block-list/block-list-context.native.js @@ -7,12 +7,25 @@ export const DEFAULT_BLOCK_LIST_CONTEXT = { scrollRef: null, blocksLayouts: { current: {} }, findBlockLayoutByClientId, + findBlockLayoutByPosition, updateBlocksLayouts, }; const Context = createContext( DEFAULT_BLOCK_LIST_CONTEXT ); const { Provider, Consumer } = Context; +function findBlockLayoutByPosition( data, position ) { + // Only enabled for root level blocks + return Object.values( data ).find( ( block ) => { + return ( + position.x >= block.x && + position.x <= block.x + block.width && + position.y >= block.y && + position.y <= block.y + block.height + ); + } ); +} + /** * Finds a block's layout data by its client Id. * From a91007255542d1395997365823fb8cbf37e952d7 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 23 Mar 2022 19:31:42 +0100 Subject: [PATCH 18/41] Set up dragging block by position --- .../block-draggable/index.native.js | 82 ++++++++++++++++--- 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 040d7c98a428b..53ff7c7cd1db3 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -10,6 +10,8 @@ import Animated, { useAnimatedStyle, useSharedValue, withTiming, + scrollTo, + useAnimatedReaction, } from 'react-native-reanimated'; /** @@ -30,19 +32,26 @@ import styles from './style.scss'; const CHIP_POSITION_PADDING = 32; const BLOCK_COLLAPSED_HEIGHT = 20; +const EXTRA_OFFSET_WHEN_CLOSE_TO_TOP_EDGE = 80; +const SCROLL_ANIMATION_DURATION = 350; const BlockDraggableWrapper = ( { children } ) => { - const { /*startDraggingBlocks,*/ stopDraggingBlocks } = useDispatch( + const { startDraggingBlocks, stopDraggingBlocks } = useDispatch( blockEditorStore ); - const { scrollRef } = useBlockListContext(); + const { + blocksLayouts, + scrollRef, + findBlockLayoutByPosition, + } = useBlockListContext(); const animatedScrollRef = useAnimatedRef(); animatedScrollRef( scrollRef ); const scroll = { x: useSharedValue( 0 ), y: useSharedValue( 0 ), + offsetY: useSharedValue( 0 ), }; const chip = { x: useSharedValue( 0 ), @@ -54,14 +63,23 @@ const BlockDraggableWrapper = ( { children } ) => { scale: useSharedValue( 0 ), }; const isDragging = useSharedValue( false ); + const scrollAnimation = useSharedValue( 0 ); const [ startScrolling, scrollOnDragOver, stopScrolling, - scrollHandler, + draggingScrollHandler, ] = useScrollWhenDragging(); + const scrollHandler = ( event ) => { + 'worklet'; + const { contentOffset } = event; + scroll.offsetY.value = contentOffset.y; + + draggingScrollHandler( event ); + }; + // Stop dragging blocks if the block draggable is unmounted. useEffect( () => { return () => { @@ -71,11 +89,48 @@ const BlockDraggableWrapper = ( { children } ) => { }; }, [] ); - const setDraggingBlockByPosition = () => { - // TODO: Get clientId from blocks layouts data by position - // startDraggingBlocks( [ clientIdFromBlocksLayouts ] ); + const setupDraggingBlock = ( position ) => { + const blockLayout = findBlockLayoutByPosition( blocksLayouts.current, { + x: position.x, + y: position.y + scroll.offsetY.value, + } ); + + const foundClientId = blockLayout?.clientId; + if ( foundClientId ) { + startDraggingBlocks( [ foundClientId ] ); + + const isBlockOutOfScrollView = blockLayout.y < scroll.offsetY.value; + // If the dragging block is out of the scroll view, we have to + // scroll the block list to show the origin position of the block. + if ( isBlockOutOfScrollView ) { + scrollAnimation.value = scroll.offsetY.value; + const scrollOffsetTarget = Math.max( + 0, + scroll.offsetY.value - + ( scroll.offsetY.value - blockLayout.y ) - + EXTRA_OFFSET_WHEN_CLOSE_TO_TOP_EDGE + ); + scrollAnimation.value = withTiming( + scrollOffsetTarget, + { duration: SCROLL_ANIMATION_DURATION }, + () => startScrolling( position.y ) + ); + } else { + runOnUI( startScrolling )( position.y ); + } + } }; + // This hook is used for animating the scroll via a shared value. + useAnimatedReaction( + () => scrollAnimation.value, + ( value ) => { + if ( isDragging.value ) { + scrollTo( animatedScrollRef, 0, value, false ); + } + } + ); + const onChipLayout = ( { nativeEvent: { layout } } ) => { chip.width.value = layout.width; chip.height.value = layout.height; @@ -97,17 +152,21 @@ const BlockDraggableWrapper = ( { children } ) => { isDragging.value = true; chip.scale.value = withTiming( 1 ); - runOnJS( setDraggingBlockByPosition )( dragPosition ); - startScrolling( absoluteY ); + runOnJS( setupDraggingBlock )( dragPosition ); }; const updateDragging = ( { absoluteX, absoluteY } ) => { 'worklet'; - chip.x.value = absoluteX - scroll.x.value; - chip.y.value = absoluteY - scroll.y.value; + const dragPosition = { + x: absoluteX - scroll.x.value, + y: absoluteY - scroll.y.value, + }; + + chip.x.value = dragPosition.x; + chip.y.value = dragPosition.y; // Update scrolling velocity - scrollOnDragOver( absoluteY ); + scrollOnDragOver( dragPosition.y ); }; const stopDragging = () => { @@ -142,6 +201,7 @@ const BlockDraggableWrapper = ( { children } ) => { onDragStart={ startDragging } onDragOver={ updateDragging } onDragEnd={ stopDragging } + wrapperAnimatedStyles={ { flex: 1 } } > { children( { onScroll: scrollHandler } ) } From 0ddca86c9258b45637ae6bf7728e97ae6ce050eb Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 23 Mar 2022 19:32:21 +0100 Subject: [PATCH 19/41] Remove animate scroll velocity --- .../use-scroll-when-dragging.native.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js b/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js index cdf9a5e26421e..d38e15760e76c 100644 --- a/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js +++ b/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js @@ -47,20 +47,11 @@ export default function useScrollWhenDragging() { scroll.maxOffsetY.value = contentSize.height - layoutMeasurement.height; }; - const animateVelocity = ( value ) => { - 'worklet'; - if ( value === 0 ) { - velocityY.value = withTiming( 0, { duration: 150 } ); - } else { - velocityY.value = withTiming( value, { duration: 150 } ); - } - }; - const stopScrolling = () => { 'worklet'; isAnimationTimerActive.value = false; isScrollActive.value = false; - animateVelocity( 0 ); + velocityY.value = 0; }; const startScrolling = ( y ) => { @@ -93,12 +84,12 @@ export default function useScrollWhenDragging() { isScrollActive.value = dragDistance > 0; } else if ( y > dragStartY.value ) { // User is dragging downwards. - animateVelocity( VELOCITY_MULTIPLIER * distancePercentage ); + velocityY.value = VELOCITY_MULTIPLIER * distancePercentage; } else if ( y < dragStartY.value ) { // User is dragging upwards. - animateVelocity( -VELOCITY_MULTIPLIER * distancePercentage ); + velocityY.value = -VELOCITY_MULTIPLIER * distancePercentage; } else { - animateVelocity( 0 ); + velocityY.value = 0; } }; From 149255087b993aea4b92e1fd05b0d7fd6241af03 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 23 Mar 2022 19:36:20 +0100 Subject: [PATCH 20/41] Remove useScrollWhenDragging hook This hook will be introduced in a separate PR --- .../block-draggable/index.native.js | 24 +--- .../use-scroll-when-dragging.native.js | 124 ------------------ 2 files changed, 3 insertions(+), 145 deletions(-) delete mode 100644 packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 53ff7c7cd1db3..388c8e66a26c0 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -24,7 +24,6 @@ import { useEffect } from '@wordpress/element'; /** * Internal dependencies */ -import useScrollWhenDragging from './use-scroll-when-dragging'; import DraggableChip from './draggable-chip'; import { store as blockEditorStore } from '../../store'; import { useBlockListContext } from '../block-list/block-list-context'; @@ -65,19 +64,10 @@ const BlockDraggableWrapper = ( { children } ) => { const isDragging = useSharedValue( false ); const scrollAnimation = useSharedValue( 0 ); - const [ - startScrolling, - scrollOnDragOver, - stopScrolling, - draggingScrollHandler, - ] = useScrollWhenDragging(); - const scrollHandler = ( event ) => { 'worklet'; const { contentOffset } = event; scroll.offsetY.value = contentOffset.y; - - draggingScrollHandler( event ); }; // Stop dragging blocks if the block draggable is unmounted. @@ -110,13 +100,9 @@ const BlockDraggableWrapper = ( { children } ) => { ( scroll.offsetY.value - blockLayout.y ) - EXTRA_OFFSET_WHEN_CLOSE_TO_TOP_EDGE ); - scrollAnimation.value = withTiming( - scrollOffsetTarget, - { duration: SCROLL_ANIMATION_DURATION }, - () => startScrolling( position.y ) - ); - } else { - runOnUI( startScrolling )( position.y ); + scrollAnimation.value = withTiming( scrollOffsetTarget, { + duration: SCROLL_ANIMATION_DURATION, + } ); } } }; @@ -164,9 +150,6 @@ const BlockDraggableWrapper = ( { children } ) => { chip.x.value = dragPosition.x; chip.y.value = dragPosition.y; - - // Update scrolling velocity - scrollOnDragOver( dragPosition.y ); }; const stopDragging = () => { @@ -175,7 +158,6 @@ const BlockDraggableWrapper = ( { children } ) => { chip.scale.value = withTiming( 0 ); runOnJS( stopDraggingBlocks )(); - stopScrolling(); }; const chipStyles = useAnimatedStyle( () => { diff --git a/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js b/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js deleted file mode 100644 index d38e15760e76c..0000000000000 --- a/packages/block-editor/src/components/block-draggable/use-scroll-when-dragging.native.js +++ /dev/null @@ -1,124 +0,0 @@ -/** - * External dependencies - */ -import { Dimensions } from 'react-native'; -import { - useSharedValue, - useAnimatedRef, - scrollTo, - useAnimatedReaction, - withTiming, - withRepeat, - cancelAnimation, - Easing, -} from 'react-native-reanimated'; - -/** - * Internal dependencies - */ -import { useBlockListContext } from '../block-list/block-list-context'; - -const SCROLL_INACTIVE_DISTANCE_PX = 50; -const SCROLL_INTERVAL_MS = 1000; -const VELOCITY_MULTIPLIER = 5000; - -export default function useScrollWhenDragging() { - const { scrollRef } = useBlockListContext(); - const animatedScrollRef = useAnimatedRef(); - animatedScrollRef( scrollRef ); - - const windowHeight = Dimensions.get( 'window' ).height; - - const velocityY = useSharedValue( 0 ); - const offsetY = useSharedValue( 0 ); - const dragStartY = useSharedValue( 0 ); - const animationTimer = useSharedValue( 0 ); - const isAnimationTimerActive = useSharedValue( false ); - const isScrollActive = useSharedValue( false ); - - const scroll = { - offsetY: useSharedValue( 0 ), - maxOffsetY: useSharedValue( 0 ), - }; - const scrollHandler = ( event ) => { - 'worklet'; - const { contentSize, contentOffset, layoutMeasurement } = event; - scroll.offsetY.value = contentOffset.y; - scroll.maxOffsetY.value = contentSize.height - layoutMeasurement.height; - }; - - const stopScrolling = () => { - 'worklet'; - isAnimationTimerActive.value = false; - isScrollActive.value = false; - velocityY.value = 0; - }; - - const startScrolling = ( y ) => { - 'worklet'; - stopScrolling(); - offsetY.value = scroll.offsetY.value; - dragStartY.value = y; - - animationTimer.value = 0; - animationTimer.value = withRepeat( - withTiming( 1, { - duration: SCROLL_INTERVAL_MS, - easing: Easing.linear, - } ), - -1, - true - ); - isAnimationTimerActive.value = true; - }; - - const scrollOnDragOver = ( y ) => { - 'worklet'; - const dragDistance = Math.max( - Math.abs( y - dragStartY.value ) - SCROLL_INACTIVE_DISTANCE_PX, - 0 - ); - const distancePercentage = dragDistance / windowHeight; - - if ( ! isScrollActive.value ) { - isScrollActive.value = dragDistance > 0; - } else if ( y > dragStartY.value ) { - // User is dragging downwards. - velocityY.value = VELOCITY_MULTIPLIER * distancePercentage; - } else if ( y < dragStartY.value ) { - // User is dragging upwards. - velocityY.value = -VELOCITY_MULTIPLIER * distancePercentage; - } else { - velocityY.value = 0; - } - }; - - useAnimatedReaction( - () => animationTimer.value, - ( value, previous ) => { - const delta = Math.abs( value - previous ); - let newOffset = offsetY.value + delta * velocityY.value; - - if ( scroll.maxOffsetY.value !== 0 ) { - newOffset = Math.max( - 0, - Math.min( scroll.maxOffsetY.value, newOffset ) - ); - } else { - // Scroll values are empty until receiving the first scroll event. - // In that case, the max offset is unknown and we can't clamp the - // new offset value. - newOffset = Math.max( 0, newOffset ); - } - offsetY.value = newOffset; - - if ( velocityY.value !== 0 ) { - scrollTo( animatedScrollRef, 0, offsetY.value, false ); - } else if ( ! isAnimationTimerActive.value ) { - cancelAnimation( animationTimer ); - } - } - ); - - return [ startScrolling, scrollOnDragOver, stopScrolling, scrollHandler ]; -} From 2bd4e6696522340eede0690c2f413ab7b1ff3b51 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 24 Mar 2022 12:52:05 +0100 Subject: [PATCH 21/41] Remove react-native-reanimated mock --- test/native/setup.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/native/setup.js b/test/native/setup.js index 5fb9927752771..014b7f0ce667c 100644 --- a/test/native/setup.js +++ b/test/native/setup.js @@ -153,16 +153,6 @@ jest.mock( '@react-native-community/blur', () => () => 'BlurView', { virtual: true, } ); -jest.mock( 'react-native-reanimated', () => { - const Reanimated = require( 'react-native-reanimated/mock' ); - - // The mock for `call` immediately calls the callback which is incorrect - // So we override it with a no-op - Reanimated.default.call = () => {}; - - return Reanimated; -} ); - // Silence the warning: Animated: `useNativeDriver` is not supported because the // native animated module is missing. This was added per React Navigation docs. // https://reactnavigation.org/docs/testing/#mocking-native-modules From 9a2ae5407f7b2802a54c8f4bf10253c38d9901c3 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 24 Mar 2022 13:54:55 +0100 Subject: [PATCH 22/41] Rename CHIP_OFFSET_TO_TOUCH_POSITION constant --- .../src/components/block-draggable/index.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 388c8e66a26c0..c2d538e8bdd1a 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -29,7 +29,7 @@ import { store as blockEditorStore } from '../../store'; import { useBlockListContext } from '../block-list/block-list-context'; import styles from './style.scss'; -const CHIP_POSITION_PADDING = 32; +const CHIP_OFFSET_TO_TOUCH_POSITION = 32; const BLOCK_COLLAPSED_HEIGHT = 20; const EXTRA_OFFSET_WHEN_CLOSE_TO_TOP_EDGE = 80; const SCROLL_ANIMATION_DURATION = 350; @@ -169,7 +169,7 @@ const BlockDraggableWrapper = ( { children } ) => { translateY: chip.y.value - chip.height.value - - CHIP_POSITION_PADDING, + CHIP_OFFSET_TO_TOUCH_POSITION, }, { scaleX: chip.scale.value }, { scaleY: chip.scale.value }, From fd0883ed3f58a96dacefc24c9476314b2b366c9a Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 24 Mar 2022 13:55:52 +0100 Subject: [PATCH 23/41] Remove unused shared values of chip component --- .../block-editor/src/components/block-draggable/index.native.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index c2d538e8bdd1a..13533ce17979a 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -55,8 +55,6 @@ const BlockDraggableWrapper = ( { children } ) => { const chip = { x: useSharedValue( 0 ), y: useSharedValue( 0 ), - startX: useSharedValue( 0 ), - startY: useSharedValue( 0 ), width: useSharedValue( 0 ), height: useSharedValue( 0 ), scale: useSharedValue( 0 ), From 6bef4854c857842bc8674ff2ddb39d87e64a73a0 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 24 Mar 2022 19:21:50 +0100 Subject: [PATCH 24/41] Stop dragging when no block is found --- .../src/components/block-draggable/index.native.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 13533ce17979a..70382e3c48028 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -102,6 +102,9 @@ const BlockDraggableWrapper = ( { children } ) => { duration: SCROLL_ANIMATION_DURATION, } ); } + } else { + // We stop dragging If no block is found. + runOnUI( stopDragging )(); } }; From ad016ed2eff692c1f6abb05980bf72bff0ed25ad Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 24 Mar 2022 19:22:58 +0100 Subject: [PATCH 25/41] Fix drag position calculation --- .../block-draggable/index.native.js | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 70382e3c48028..c445ca33dc046 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -3,7 +3,6 @@ */ import Animated, { interpolate, - measure, runOnJS, runOnUI, useAnimatedRef, @@ -48,8 +47,6 @@ const BlockDraggableWrapper = ( { children } ) => { animatedScrollRef( scrollRef ); const scroll = { - x: useSharedValue( 0 ), - y: useSharedValue( 0 ), offsetY: useSharedValue( 0 ), }; const chip = { @@ -123,16 +120,9 @@ const BlockDraggableWrapper = ( { children } ) => { chip.height.value = layout.height; }; - const startDragging = ( { absoluteX, absoluteY } ) => { + const startDragging = ( { x, y } ) => { 'worklet'; - const scrollLayout = measure( animatedScrollRef ); - scroll.x.value = scrollLayout.pageX; - scroll.y.value = scrollLayout.pageY; - - const dragPosition = { - x: absoluteX - scroll.x.value, - y: absoluteY - scroll.y.value, - }; + const dragPosition = { x, y }; chip.x.value = dragPosition.x; chip.y.value = dragPosition.y; @@ -142,13 +132,9 @@ const BlockDraggableWrapper = ( { children } ) => { runOnJS( setupDraggingBlock )( dragPosition ); }; - const updateDragging = ( { absoluteX, absoluteY } ) => { + const updateDragging = ( { x, y } ) => { 'worklet'; - const dragPosition = { - x: absoluteX - scroll.x.value, - y: absoluteY - scroll.y.value, - }; - + const dragPosition = { x, y }; chip.x.value = dragPosition.x; chip.y.value = dragPosition.y; }; From 7fac2ec80f31f5347dd471c403212b371177e78c Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 25 Mar 2022 17:25:06 +0100 Subject: [PATCH 26/41] Update html text input styles --- .../mobile/html-text-input/style.android.scss | 16 +--------------- .../src/mobile/html-text-input/style.ios.scss | 16 +--------------- .../{style-common.native.scss => style.scss} | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 30 deletions(-) rename packages/components/src/mobile/html-text-input/{style-common.native.scss => style.scss} (54%) diff --git a/packages/components/src/mobile/html-text-input/style.android.scss b/packages/components/src/mobile/html-text-input/style.android.scss index 1dca01274d75b..e292901922dbc 100644 --- a/packages/components/src/mobile/html-text-input/style.android.scss +++ b/packages/components/src/mobile/html-text-input/style.android.scss @@ -1,21 +1,7 @@ -@import "./style-common.scss"; - -.htmlView { - font-family: $htmlFont; - padding-left: $padding; - padding-right: $padding; - padding-top: $padding; - padding-bottom: $padding + 16; -} +@import "./style.scss"; .htmlViewTitle { font-family: $htmlFont; padding-left: $padding; padding-right: $padding; - padding-top: $padding; - padding-bottom: $padding; -} - -.scrollView { - flex: 1; } diff --git a/packages/components/src/mobile/html-text-input/style.ios.scss b/packages/components/src/mobile/html-text-input/style.ios.scss index 97cf00a7512ff..cd269a6b9876f 100644 --- a/packages/components/src/mobile/html-text-input/style.ios.scss +++ b/packages/components/src/mobile/html-text-input/style.ios.scss @@ -1,17 +1,4 @@ -@import "./style-common.scss"; - -$title-height: 32; - -.htmlView { - font-family: $htmlFont; - padding-left: $padding; - padding-right: $padding; - padding-bottom: $title-height + $padding; -} - -.htmlViewDark { - color: $textColorDark; -} +@import "./style.scss"; .htmlViewTitle { font-family: $htmlFont; @@ -19,5 +6,4 @@ $title-height: 32; padding-right: $padding; padding-top: $padding; padding-bottom: $padding; - height: $title-height; } diff --git a/packages/components/src/mobile/html-text-input/style-common.native.scss b/packages/components/src/mobile/html-text-input/style.scss similarity index 54% rename from packages/components/src/mobile/html-text-input/style-common.native.scss rename to packages/components/src/mobile/html-text-input/style.scss index c1ac9f155d4c7..89b81e898ad4b 100644 --- a/packages/components/src/mobile/html-text-input/style-common.native.scss +++ b/packages/components/src/mobile/html-text-input/style.scss @@ -21,3 +21,19 @@ $textColorDark: $white; .placeholderDark { color: $gray-50; } + +.htmlView { + font-family: $htmlFont; + padding-left: $padding; + padding-right: $padding; + padding-top: $padding; + padding-bottom: $padding + 16; +} + +.htmlViewDark { + color: $textColorDark; +} + +.scrollView { + flex: 1; +} From 5c6e21acde9789052eef5992d8f7b671ae8c70dc Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 25 Mar 2022 17:25:42 +0100 Subject: [PATCH 27/41] Unify container component within html text input --- .../html-text-input/container.android.js | 23 ------ .../mobile/html-text-input/container.ios.js | 50 ------------- .../mobile/html-text-input/index.native.js | 74 +++++++++++-------- 3 files changed, 45 insertions(+), 102 deletions(-) delete mode 100644 packages/components/src/mobile/html-text-input/container.android.js delete mode 100644 packages/components/src/mobile/html-text-input/container.ios.js diff --git a/packages/components/src/mobile/html-text-input/container.android.js b/packages/components/src/mobile/html-text-input/container.android.js deleted file mode 100644 index 68d69783f3b9f..0000000000000 --- a/packages/components/src/mobile/html-text-input/container.android.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * External dependencies - */ -import { ScrollView } from 'react-native'; - -/** - * Internal dependencies - */ -import KeyboardAvoidingView from '../keyboard-avoiding-view'; -import styles from './style.android.scss'; - -const HTMLInputContainer = ( { children, parentHeight } ) => ( - - { children } - -); - -HTMLInputContainer.scrollEnabled = false; - -export default HTMLInputContainer; diff --git a/packages/components/src/mobile/html-text-input/container.ios.js b/packages/components/src/mobile/html-text-input/container.ios.js deleted file mode 100644 index b40214e1eaab0..0000000000000 --- a/packages/components/src/mobile/html-text-input/container.ios.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * External dependencies - */ -import { UIManager, PanResponder } from 'react-native'; - -/** - * WordPress dependencies - */ -import { Component } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import KeyboardAvoidingView from '../keyboard-avoiding-view'; -import styles from './style.ios.scss'; - -class HTMLInputContainer extends Component { - constructor() { - super( ...arguments ); - - this.panResponder = PanResponder.create( { - onStartShouldSetPanResponderCapture: () => true, - - onPanResponderMove: ( e, gestureState ) => { - if ( gestureState.dy > 100 && gestureState.dy < 110 ) { - // Keyboard.dismiss() and this.textInput.blur() are not working here - // They require to know the currentlyFocusedID under the hood but - // during this gesture there's no currentlyFocusedID. - UIManager.blur( e.target ); - } - }, - } ); - } - - render() { - return ( - - { this.props.children } - - ); - } -} - -HTMLInputContainer.scrollEnabled = true; - -export default HTMLInputContainer; diff --git a/packages/components/src/mobile/html-text-input/index.native.js b/packages/components/src/mobile/html-text-input/index.native.js index 438a1ca88ed4b..eab8dafcbd490 100644 --- a/packages/components/src/mobile/html-text-input/index.native.js +++ b/packages/components/src/mobile/html-text-input/index.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { TextInput } from 'react-native'; +import { ScrollView, TextInput } from 'react-native'; /** * WordPress dependencies @@ -20,7 +20,7 @@ import { /** * Internal dependencies */ -import HTMLInputContainer from './container'; +import KeyboardAvoidingView from '../keyboard-avoiding-view'; import styles from './style.scss'; export class HTMLTextInput extends Component { @@ -73,7 +73,13 @@ export class HTMLTextInput extends Component { } render() { - const { getStylesFromColorScheme, style } = this.props; + const { + editTitle, + getStylesFromColorScheme, + parentHeight, + style, + title, + } = this.props; const titleStyle = [ styles.htmlViewTitle, style?.text && { color: style.text }, @@ -90,32 +96,42 @@ export class HTMLTextInput extends Component { ...( style?.text && { color: style.text } ), }; return ( - - - - + + + + + + ); } } From 55e62f1783cf3f810dae15cc45767dad0a906a60 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 28 Mar 2022 12:36:50 +0200 Subject: [PATCH 28/41] Use only a single client id in block draggable --- .../src/components/block-draggable/index.native.js | 8 ++++---- .../components/block-list/block-list-item-cell.native.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index c445ca33dc046..792f77a664f2e 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -185,7 +185,7 @@ const BlockDraggableWrapper = ( { children } ) => { ); }; -const BlockDraggable = ( { clientIds, children } ) => { +const BlockDraggable = ( { clientId, children } ) => { const container = { height: useSharedValue( 0 ), }; @@ -214,17 +214,17 @@ const BlockDraggable = ( { clientIds, children } ) => { getTemplateLock, isBlockBeingDragged, } = select( blockEditorStore ); - const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); + const rootClientId = getBlockRootClientId( clientId ); const templateLock = rootClientId ? getTemplateLock( rootClientId ) : null; return { - isBeingDragged: isBlockBeingDragged( clientIds[ 0 ] ), + isBeingDragged: isBlockBeingDragged( clientId ), isDraggable: 'all' !== templateLock, }; }, - [ clientIds ] + [ clientId ] ); useEffect( () => { diff --git a/packages/block-editor/src/components/block-list/block-list-item-cell.native.js b/packages/block-editor/src/components/block-list/block-list-item-cell.native.js index 1055abf4d1064..5577b0705d686 100644 --- a/packages/block-editor/src/components/block-list/block-list-item-cell.native.js +++ b/packages/block-editor/src/components/block-list/block-list-item-cell.native.js @@ -39,7 +39,7 @@ function BlockListItemCell( { children, clientId, rootClientId } ) { return ( - + { () => children } From 258eaf3a7b925bf67c1a82e6844a866c5bed8de8 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 28 Mar 2022 12:37:28 +0200 Subject: [PATCH 29/41] Add documentation to block draggable components --- .../block-draggable/index.native.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 792f77a664f2e..fa57624c55f32 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -33,6 +33,22 @@ const BLOCK_COLLAPSED_HEIGHT = 20; const EXTRA_OFFSET_WHEN_CLOSE_TO_TOP_EDGE = 80; const SCROLL_ANIMATION_DURATION = 350; +/** + * Block draggable wrapper component + * + * This component handles all the interactions for dragging blocks. + * It relies on the block list and its context for dragging, hence it + * should be rendered between the `BlockListProvider` component and the + * block list rendering. It also requires listening to scroll events, + * therefore for this purpose, it returns the `onScroll` event handler + * that should be attached to the list that renders the blocks. + * + * + * @param {Object} props Component props. + * @param {JSX.Element} props.children Children to be rendered. + * + * @return {Function} Render function that passes `onScroll` event handler. + */ const BlockDraggableWrapper = ( { children } ) => { const { startDraggingBlocks, stopDraggingBlocks } = useDispatch( blockEditorStore @@ -185,6 +201,18 @@ const BlockDraggableWrapper = ( { children } ) => { ); }; +/** + * Block draggable component + * + * This component serves for animating the block when it is being dragged. + * Hence, It should be wrapped around the rendering of a block. + * + * @param {Object} props Component props. + * @param {JSX.Element} props.children Children to be rendered. + * @param {string[]} props.clientId Client id of the block. + * + * @return {Function} Render function which includes the parameter `isDraggable` to determine if the block can be dragged. + */ const BlockDraggable = ( { clientId, children } ) => { const container = { height: useSharedValue( 0 ), From 78da359415c5b4c004cb9f31735ad5a2f92f285a Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 28 Mar 2022 12:39:13 +0200 Subject: [PATCH 30/41] Add documentation to block draggable chip component --- .../src/components/block-draggable/draggable-chip.native.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/block-editor/src/components/block-draggable/draggable-chip.native.js b/packages/block-editor/src/components/block-draggable/draggable-chip.native.js index 920e8535966ea..011025f7db7e4 100644 --- a/packages/block-editor/src/components/block-draggable/draggable-chip.native.js +++ b/packages/block-editor/src/components/block-draggable/draggable-chip.native.js @@ -29,6 +29,11 @@ const shadowStyle = { elevation: 5, }; +/** + * Block draggable chip component + * + * @return {JSX.Element} Chip component. + */ export default function BlockDraggableChip() { const { blockIcon } = useSelect( ( select ) => { const { getBlockName, getDraggedBlockClientIds } = select( From 8cac26408dd97b8616a13c18c227d4a4673a7452 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 28 Mar 2022 12:45:47 +0200 Subject: [PATCH 31/41] Add documentation to findBlockLayoutByPosition --- .../components/block-list/block-list-context.native.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/block-editor/src/components/block-list/block-list-context.native.js b/packages/block-editor/src/components/block-list/block-list-context.native.js index 30309c3c4ed62..21f850ec5551c 100644 --- a/packages/block-editor/src/components/block-list/block-list-context.native.js +++ b/packages/block-editor/src/components/block-list/block-list-context.native.js @@ -14,6 +14,16 @@ export const DEFAULT_BLOCK_LIST_CONTEXT = { const Context = createContext( DEFAULT_BLOCK_LIST_CONTEXT ); const { Provider, Consumer } = Context; +/** + * Finds a block's layout data by position. + * + * @param {Object} data Blocks layouts object. + * @param {Object} position Position to use for finding the block. + * @param {number} position.x X coordinate. + * @param {number} position.y Y coordinate. + * + * @return {Object|undefined} Found block layout data that matches the provided position. If none is found, `undefined` will be returned. + */ function findBlockLayoutByPosition( data, position ) { // Only enabled for root level blocks return Object.values( data ).find( ( block ) => { From 1612b48c2efbeb0b5a8b2be230a5e7c8b6c87506 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 28 Mar 2022 12:52:26 +0200 Subject: [PATCH 32/41] Update scrollOffsetTarget calculation --- .../src/components/block-draggable/index.native.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index fa57624c55f32..64d7287b031d0 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -107,9 +107,7 @@ const BlockDraggableWrapper = ( { children } ) => { scrollAnimation.value = scroll.offsetY.value; const scrollOffsetTarget = Math.max( 0, - scroll.offsetY.value - - ( scroll.offsetY.value - blockLayout.y ) - - EXTRA_OFFSET_WHEN_CLOSE_TO_TOP_EDGE + blockLayout.y - EXTRA_OFFSET_WHEN_CLOSE_TO_TOP_EDGE ); scrollAnimation.value = withTiming( scrollOffsetTarget, { duration: SCROLL_ANIMATION_DURATION, From b7e800a4ad8ffa783b7df62e3db2a44e5475e1f9 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 28 Mar 2022 12:53:03 +0200 Subject: [PATCH 33/41] Fix typos in block draggable --- .../src/components/block-draggable/index.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 64d7287b031d0..538de449895bb 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -114,7 +114,7 @@ const BlockDraggableWrapper = ( { children } ) => { } ); } } else { - // We stop dragging If no block is found. + // We stop dragging if no block is found. runOnUI( stopDragging )(); } }; @@ -203,7 +203,7 @@ const BlockDraggableWrapper = ( { children } ) => { * Block draggable component * * This component serves for animating the block when it is being dragged. - * Hence, It should be wrapped around the rendering of a block. + * Hence, it should be wrapped around the rendering of a block. * * @param {Object} props Component props. * @param {JSX.Element} props.children Children to be rendered. From 8c79ad3d4f385cb438657c8740d000dc2fa09807 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 28 Mar 2022 12:56:16 +0200 Subject: [PATCH 34/41] Add draggable wrapper container style --- .../src/components/block-draggable/index.native.js | 4 +++- .../src/components/block-draggable/style.native.scss | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 538de449895bb..426b101f9534f 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -184,7 +184,9 @@ const BlockDraggableWrapper = ( { children } ) => { onDragStart={ startDragging } onDragOver={ updateDragging } onDragEnd={ stopDragging } - wrapperAnimatedStyles={ { flex: 1 } } + wrapperAnimatedStyles={ + styles[ 'draggable-wrapper__container' ] + } > { children( { onScroll: scrollHandler } ) } diff --git a/packages/block-editor/src/components/block-draggable/style.native.scss b/packages/block-editor/src/components/block-draggable/style.native.scss index 4ffac6b1dde83..6bf7812f29540 100644 --- a/packages/block-editor/src/components/block-draggable/style.native.scss +++ b/packages/block-editor/src/components/block-draggable/style.native.scss @@ -1,3 +1,7 @@ +.draggable-wrapper__container { + flex: 1; +} + .draggable-chip__container { flex-direction: row; padding: 16px; From e9ce4e3e52b681385d37669ce24448ea0868e869 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 28 Mar 2022 18:23:07 +0200 Subject: [PATCH 35/41] Add dark mode styles for draggable chip --- .../components/block-draggable/draggable-chip.native.js | 8 +++++++- .../src/components/block-draggable/style.native.scss | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/draggable-chip.native.js b/packages/block-editor/src/components/block-draggable/draggable-chip.native.js index 011025f7db7e4..2559b2089252b 100644 --- a/packages/block-editor/src/components/block-draggable/draggable-chip.native.js +++ b/packages/block-editor/src/components/block-draggable/draggable-chip.native.js @@ -9,6 +9,7 @@ import { View } from 'react-native'; import { dragHandle } from '@wordpress/icons'; import { useSelect } from '@wordpress/data'; import { getBlockType } from '@wordpress/blocks'; +import { usePreferredColorSchemeStyle } from '@wordpress/compose'; /** * Internal dependencies @@ -35,6 +36,11 @@ const shadowStyle = { * @return {JSX.Element} Chip component. */ export default function BlockDraggableChip() { + const containerStyle = usePreferredColorSchemeStyle( + styles[ 'draggable-chip__container' ], + styles[ 'draggable-chip__container--dark' ] + ); + const { blockIcon } = useSelect( ( select ) => { const { getBlockName, getDraggedBlockClientIds } = select( blockEditorStore @@ -48,7 +54,7 @@ export default function BlockDraggableChip() { } ); return ( - + diff --git a/packages/block-editor/src/components/block-draggable/style.native.scss b/packages/block-editor/src/components/block-draggable/style.native.scss index 6bf7812f29540..33892ad7b51b5 100644 --- a/packages/block-editor/src/components/block-draggable/style.native.scss +++ b/packages/block-editor/src/components/block-draggable/style.native.scss @@ -5,10 +5,14 @@ .draggable-chip__container { flex-direction: row; padding: 16px; - background-color: #f7f7f7; + background-color: $gray-0; border-radius: 8px; } +.draggable-chip__container--dark { + background-color: $app-background-dark-alt; +} + .draggable-placeholder__container { position: absolute; top: 0; From 4060e1ad86e6d2c9b880a7ed28509668c146f062 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 28 Mar 2022 18:28:25 +0200 Subject: [PATCH 36/41] Add dark mode styles for block draggable --- .../block-draggable/index.native.js | 28 +++++++++++-------- .../block-draggable/style.native.scss | 4 +++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 426b101f9534f..0e09f5fe91cae 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -19,6 +19,7 @@ import Animated, { import { Draggable } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect } from '@wordpress/element'; +import { usePreferredColorSchemeStyle } from '@wordpress/compose'; /** * Internal dependencies @@ -50,6 +51,11 @@ const SCROLL_ANIMATION_DURATION = 350; * @return {Function} Render function that passes `onScroll` event handler. */ const BlockDraggableWrapper = ( { children } ) => { + const wrapperStyles = usePreferredColorSchemeStyle( + styles[ 'draggable-wrapper__container' ], + styles[ 'draggable-wrapper__container--dark' ] + ); + const { startDraggingBlocks, stopDraggingBlocks } = useDispatch( blockEditorStore ); @@ -184,9 +190,7 @@ const BlockDraggableWrapper = ( { children } ) => { onDragStart={ startDragging } onDragOver={ updateDragging } onDragEnd={ stopDragging } - wrapperAnimatedStyles={ - styles[ 'draggable-wrapper__container' ] - } + wrapperAnimatedStyles={ wrapperStyles } > { children( { onScroll: scrollHandler } ) } @@ -284,12 +288,20 @@ const BlockDraggable = ( { clientId, children } ) => { }; } ); - const placeholderStyles = useAnimatedStyle( () => { + const placeholderDynamicStyles = useAnimatedStyle( () => { return { display: collapseAnimation.value === 0 ? 'none' : 'flex', opacity: collapseAnimation.value, }; } ); + const placeholderStaticStyles = usePreferredColorSchemeStyle( + styles[ 'draggable-placeholder__container' ], + styles[ 'draggable-placeholder__container--dark' ] + ); + const placeholderStyles = [ + placeholderStaticStyles, + placeholderDynamicStyles, + ]; if ( ! isDraggable ) { return children( { isDraggable: false } ); @@ -300,13 +312,7 @@ const BlockDraggable = ( { clientId, children } ) => { { children( { isDraggable: true } ) } - + ); }; diff --git a/packages/block-editor/src/components/block-draggable/style.native.scss b/packages/block-editor/src/components/block-draggable/style.native.scss index 33892ad7b51b5..cbd6454de86ac 100644 --- a/packages/block-editor/src/components/block-draggable/style.native.scss +++ b/packages/block-editor/src/components/block-draggable/style.native.scss @@ -23,3 +23,7 @@ background-color: $gray-lighten-30; border-radius: 8px; } + +.draggable-placeholder__container--dark { + background-color: $gray-darken-30; +} From d16ce157ebc7b6e916c9c3026da48a525a71ca1b Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Tue, 29 Mar 2022 10:23:32 +0200 Subject: [PATCH 37/41] Get container height from blocks layout data --- .../components/block-draggable/index.native.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 0e09f5fe91cae..cd4ae06a4d8c7 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -218,19 +218,22 @@ const BlockDraggableWrapper = ( { children } ) => { * @return {Function} Render function which includes the parameter `isDraggable` to determine if the block can be dragged. */ const BlockDraggable = ( { clientId, children } ) => { - const container = { - height: useSharedValue( 0 ), - }; + const { blocksLayouts, findBlockLayoutByClientId } = useBlockListContext(); + const containerHeightBeforeDragging = useSharedValue( 0 ); const collapseAnimation = useSharedValue( 0 ); - const onContainerLayout = ( { nativeEvent: { layout } } ) => { - container.height.value = layout.height; + const setContainerHeightBeforeDragging = () => { + const blockLayout = findBlockLayoutByClientId( + blocksLayouts.current, + clientId + ); + containerHeightBeforeDragging.value = blockLayout?.height ?? 0; }; const startBlockDragging = () => { 'worklet'; - containerHeightBeforeDragging.value = container.height.value; + runOnJS( setContainerHeightBeforeDragging )(); collapseAnimation.value = withTiming( 1 ); }; @@ -308,7 +311,7 @@ const BlockDraggable = ( { clientId, children } ) => { } return ( - + { children( { isDraggable: true } ) } From 842b09b296409c99705ed5a09972ce81da02a4f5 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Tue, 29 Mar 2022 10:52:12 +0200 Subject: [PATCH 38/41] Replace inline callback functions with useCallback hook --- .../keyboard-aware-flat-list/index.ios.js | 74 +++++++++++-------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js b/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js index 4bd8e6fd81437..44c5eab87f6dd 100644 --- a/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js +++ b/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js @@ -12,7 +12,7 @@ import Animated, { /** * WordPress dependencies */ -import { memo } from '@wordpress/element'; +import { memo, useCallback, useRef } from '@wordpress/element'; const List = memo( FlatList, isEqual ); const AnimatedKeyboardAwareScrollView = Animated.createAnimatedComponent( @@ -29,7 +29,11 @@ export const KeyboardAwareFlatList = ( { onScroll, ...listProps } ) => { + const scrollViewRef = useRef(); + const keyboardWillShowIndicator = useRef(); + const latestContentOffsetY = useSharedValue( -1 ); + const scrollHandler = useAnimatedScrollHandler( { onScroll: ( event ) => { const { contentOffset } = event; @@ -37,6 +41,42 @@ export const KeyboardAwareFlatList = ( { onScroll( event ); }, } ); + + const getRef = useCallback( + ( ref ) => { + scrollViewRef.current = ref; + innerRef( ref ); + }, + [ scrollViewRef, innerRef ] + ); + const onKeyboardWillHide = useCallback( () => { + keyboardWillShowIndicator.current = false; + }, [ keyboardWillShowIndicator ] ); + const onKeyboardDidHide = useCallback( () => { + setTimeout( () => { + if ( + ! keyboardWillShowIndicator.current && + latestContentOffsetY.value !== -1 && + ! shouldPreventAutomaticScroll() + ) { + // Reset the content position if keyboard is still closed. + scrollViewRef.current?.scrollToPosition( + 0, + latestContentOffsetY.value, + true + ); + } + }, 50 ); + }, [ + keyboardWillShowIndicator, + latestContentOffsetY, + shouldPreventAutomaticScroll, + scrollViewRef, + ] ); + const onKeyboardWillShow = useCallback( () => { + keyboardWillShowIndicator.current = true; + }, [ keyboardWillShowIndicator ] ); + return ( { - this.scrollViewRef = ref; - innerRef( ref ); - } } - onKeyboardWillHide={ () => { - this.keyboardWillShowIndicator = false; - } } - onKeyboardDidHide={ () => { - setTimeout( () => { - if ( - ! this.keyboardWillShowIndicator && - latestContentOffsetY.value !== -1 && - ! shouldPreventAutomaticScroll() - ) { - // Reset the content position if keyboard is still closed. - if ( this.scrollViewRef ) { - this.scrollViewRef.scrollToPosition( - 0, - latestContentOffsetY.value, - true - ); - } - } - }, 50 ); - } } - onKeyboardWillShow={ () => { - this.keyboardWillShowIndicator = true; - } } + ref={ getRef } + onKeyboardWillHide={ onKeyboardWillHide } + onKeyboardDidHide={ onKeyboardDidHide } + onKeyboardWillShow={ onKeyboardWillShow } scrollEnabled={ listProps.scrollEnabled } onScroll={ scrollHandler } > From bda7b248e6a583f497f75fd4578699c6db9a85f3 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Tue, 29 Mar 2022 12:38:51 +0200 Subject: [PATCH 39/41] Update collapse/expand animation when dragging a block --- .../block-draggable/index.native.js | 81 ++++++++++++------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index cd4ae06a4d8c7..5fe6615a76fa2 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -2,7 +2,6 @@ * External dependencies */ import Animated, { - interpolate, runOnJS, runOnUI, useAnimatedRef, @@ -11,6 +10,7 @@ import Animated, { withTiming, scrollTo, useAnimatedReaction, + Easing, } from 'react-native-reanimated'; /** @@ -33,6 +33,15 @@ const CHIP_OFFSET_TO_TOUCH_POSITION = 32; const BLOCK_COLLAPSED_HEIGHT = 20; const EXTRA_OFFSET_WHEN_CLOSE_TO_TOP_EDGE = 80; const SCROLL_ANIMATION_DURATION = 350; +const COLLAPSE_HEIGHT_ANIMATION_CONFIG = { + duration: 350, + easing: Easing.out( Easing.exp ), +}; +const EXPAND_HEIGHT_ANIMATION_CONFIG = { + duration: 350, + easing: Easing.in( Easing.exp ), +}; +const COLLAPSE_OPACITY_ANIMATION_CONFIG = { duration: 150 }; /** * Block draggable wrapper component @@ -220,26 +229,48 @@ const BlockDraggableWrapper = ( { children } ) => { const BlockDraggable = ( { clientId, children } ) => { const { blocksLayouts, findBlockLayoutByClientId } = useBlockListContext(); - const containerHeightBeforeDragging = useSharedValue( 0 ); - const collapseAnimation = useSharedValue( 0 ); + const collapseAnimation = { + opacity: useSharedValue( 0 ), + height: useSharedValue( 0 ), + initialHeight: useSharedValue( 0 ), + }; - const setContainerHeightBeforeDragging = () => { + const startBlockDragging = () => { const blockLayout = findBlockLayoutByClientId( blocksLayouts.current, clientId ); - containerHeightBeforeDragging.value = blockLayout?.height ?? 0; - }; - - const startBlockDragging = () => { - 'worklet'; - runOnJS( setContainerHeightBeforeDragging )(); - collapseAnimation.value = withTiming( 1 ); + if ( blockLayout?.height > 0 ) { + collapseAnimation.initialHeight.value = blockLayout.height; + collapseAnimation.height.value = blockLayout.height; + collapseAnimation.opacity.value = withTiming( + 1, + COLLAPSE_OPACITY_ANIMATION_CONFIG, + ( completed ) => { + if ( completed ) { + collapseAnimation.height.value = withTiming( + BLOCK_COLLAPSED_HEIGHT, + COLLAPSE_HEIGHT_ANIMATION_CONFIG + ); + } + } + ); + } }; const stopBlockDragging = () => { - 'worklet'; - collapseAnimation.value = withTiming( 0 ); + collapseAnimation.height.value = withTiming( + collapseAnimation.initialHeight.value, + EXPAND_HEIGHT_ANIMATION_CONFIG, + ( completed ) => { + if ( completed ) { + collapseAnimation.opacity.value = withTiming( + 0, + COLLAPSE_OPACITY_ANIMATION_CONFIG + ); + } + } + ); }; const { isDraggable, isBeingDragged } = useSelect( @@ -264,37 +295,31 @@ const BlockDraggable = ( { clientId, children } ) => { useEffect( () => { if ( isBeingDragged ) { - runOnUI( startBlockDragging )(); + startBlockDragging(); } else { - runOnUI( stopBlockDragging )(); + stopBlockDragging(); } }, [ isBeingDragged ] ); const containerStyles = useAnimatedStyle( () => { - const height = interpolate( - collapseAnimation.value, - [ 0, 1 ], - [ containerHeightBeforeDragging.value, BLOCK_COLLAPSED_HEIGHT ] - ); + const canAnimateHeight = + collapseAnimation.height.value !== 0 && + collapseAnimation.opacity.value !== 0; return { - height: - containerHeightBeforeDragging.value === 0 || - collapseAnimation.value === 0 - ? 'auto' - : height, + height: canAnimateHeight ? collapseAnimation.height.value : 'auto', }; } ); const blockStyles = useAnimatedStyle( () => { return { - opacity: 1 - collapseAnimation.value, + opacity: 1 - collapseAnimation.opacity.value, }; } ); const placeholderDynamicStyles = useAnimatedStyle( () => { return { - display: collapseAnimation.value === 0 ? 'none' : 'flex', - opacity: collapseAnimation.value, + display: collapseAnimation.opacity.value === 0 ? 'none' : 'flex', + opacity: collapseAnimation.opacity.value, }; } ); const placeholderStaticStyles = usePreferredColorSchemeStyle( From b886b66e0d9c18b0412f5da839de2ec665e048ca Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Tue, 29 Mar 2022 16:33:48 +0200 Subject: [PATCH 40/41] Force draggable chip to be displayed upfront --- .../src/components/block-draggable/index.native.js | 7 +++++-- .../src/components/block-draggable/style.native.scss | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index 5fe6615a76fa2..8524e4aa48f94 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -176,9 +176,8 @@ const BlockDraggableWrapper = ( { children } ) => { runOnJS( stopDraggingBlocks )(); }; - const chipStyles = useAnimatedStyle( () => { + const chipDynamicStyles = useAnimatedStyle( () => { return { - position: 'absolute', transform: [ { translateX: chip.x.value - chip.width.value / 2 }, { @@ -192,6 +191,10 @@ const BlockDraggableWrapper = ( { children } ) => { ], }; } ); + const chipStyles = [ + chipDynamicStyles, + styles[ 'draggable-chip__wrapper' ], + ]; return ( <> diff --git a/packages/block-editor/src/components/block-draggable/style.native.scss b/packages/block-editor/src/components/block-draggable/style.native.scss index cbd6454de86ac..b8133c966e9e4 100644 --- a/packages/block-editor/src/components/block-draggable/style.native.scss +++ b/packages/block-editor/src/components/block-draggable/style.native.scss @@ -2,6 +2,11 @@ flex: 1; } +.draggable-chip__wrapper { + position: absolute; + z-index: 10; +} + .draggable-chip__container { flex-direction: row; padding: 16px; From d66fb3d17ec2a4cf8bd2a4006f9604f6b96efe10 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Tue, 29 Mar 2022 16:43:34 +0200 Subject: [PATCH 41/41] Remove refs from dependencies arrays References can be omitted from the dependencies arrays since React guarantees that they are inmutable. --- .../mobile/keyboard-aware-flat-list/index.ios.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js b/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js index 44c5eab87f6dd..a8e84aaf1c2a4 100644 --- a/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js +++ b/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js @@ -47,11 +47,11 @@ export const KeyboardAwareFlatList = ( { scrollViewRef.current = ref; innerRef( ref ); }, - [ scrollViewRef, innerRef ] + [ innerRef ] ); const onKeyboardWillHide = useCallback( () => { keyboardWillShowIndicator.current = false; - }, [ keyboardWillShowIndicator ] ); + }, [] ); const onKeyboardDidHide = useCallback( () => { setTimeout( () => { if ( @@ -67,15 +67,10 @@ export const KeyboardAwareFlatList = ( { ); } }, 50 ); - }, [ - keyboardWillShowIndicator, - latestContentOffsetY, - shouldPreventAutomaticScroll, - scrollViewRef, - ] ); + }, [ latestContentOffsetY, shouldPreventAutomaticScroll ] ); const onKeyboardWillShow = useCallback( () => { keyboardWillShowIndicator.current = true; - }, [ keyboardWillShowIndicator ] ); + }, [] ); return (