Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[RNMobile] Drag & drop blocks feature #40424

Merged
merged 36 commits into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a51e773
[Mobile] - Drag & drop blocks - Fetch and share blocks layout size an…
Mar 17, 2022
20dc6ff
Merge branch 'trunk' into rnmobile/feature/drag-and-drop
fluiddot Mar 17, 2022
0793b47
Merge branch 'trunk' into rnmobile/feature/drag-and-drop
Mar 18, 2022
4e5f5f7
[Mobile] - Draggable component (#39551)
Mar 22, 2022
76f8096
[RNMobile] `BlockDraggable` component (#39617)
fluiddot Mar 30, 2022
f8e4e0e
[RNMobile] Add `useScrollWhenDragging` hook (#39705)
fluiddot Mar 30, 2022
0b3940e
[RNMobile] Prevent draggable gesture when any text input is focused (…
fluiddot Mar 31, 2022
dfd29e2
[Mobile] Adds useBlockDropZone and DroppingInsertionPoint (#39891)
Mar 31, 2022
a908ece
[RNMobile] Add `useOnBlockDrop` hook to update blocks order when drop…
fluiddot Apr 7, 2022
22ff474
[RNMobile] Update drag & drop animations (#40104)
fluiddot Apr 8, 2022
2eb5f32
Merge branch 'trunk' into rnmobile/feature/drag-and-drop
fluiddot Apr 18, 2022
2ff70a4
[RNMobile] Disable text editing when long-pressing a block in favor o…
fluiddot Apr 19, 2022
951103c
Merge branch 'trunk' into rnmobile/feature/drag-and-drop
fluiddot Apr 20, 2022
3996c65
[RNMobile] Refactor draggable logic and introduce `DraggableTrigger` …
fluiddot Apr 20, 2022
48da045
[RNMobile] Update animation of drag & drop chip component (#40409)
fluiddot Apr 20, 2022
8547f80
Mobile - DroppingInsertionPoint - Update indicator position for selec…
Apr 22, 2022
a4f52e3
[RNMobile] Add haptic feedback to drag & drop feature (#40423)
fluiddot Apr 22, 2022
1320c03
[RNMobile] Fix issues related to editing text and dragging gesture (#…
fluiddot Apr 27, 2022
d147f60
Mobile - Draggable - Disable multiple touches (#40519)
Apr 27, 2022
fa3d10b
Mobile - DroppingInsertionPoint - Hide indicator when it overflows ou…
Apr 28, 2022
d0f14ba
Mobile - Disable long press event in media blocks (#40651)
Apr 28, 2022
4c671e4
Merge branch 'trunk' into rnmobile/feature/drag-and-drop
Apr 28, 2022
bc82fb7
Mobile - DraggingInsertionPoint - Prevent crash when accessing a null…
Apr 29, 2022
3929ae8
Mobile - Draggable - Add onTouchesCancelled to handle manually ending…
May 3, 2022
adcd420
[RNMobile] Fix Android crash when closing the editor while dragging (…
fluiddot May 5, 2022
3714b15
Merge branch 'trunk' into rnmobile/feature/drag-and-drop
May 5, 2022
0c34e55
Mobile - AztecView - Trigger notifyInputChange on focus/blur (#40788)
May 6, 2022
1d87dc6
[RNMobile] Disable dragging when a screen reader is enabled (#40852)
fluiddot May 6, 2022
27f2ba7
[RNMobile] Allow dragging from block toolbar (#40886)
fluiddot May 9, 2022
4806774
Update Podfile.lock
fluiddot May 10, 2022
8760a37
Update react-native-editor changelog
fluiddot May 10, 2022
f7a9e51
Merge branch 'trunk' into rnmobile/feature/drag-and-drop
fluiddot May 10, 2022
decf119
Fix text block query locator in Android E2E tests
fluiddot May 11, 2022
7292638
Fix Cover block E2E test
fluiddot May 11, 2022
fd9a3bf
Fix Spacer block E2E test
fluiddot May 11, 2022
73e22b5
Merge branch 'trunk' into rnmobile/feature/drag-and-drop
fluiddot May 11, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

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

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

/**
* WordPress dependencies
*/
import { dragHandle } from '@wordpress/icons';
import { usePreferredColorSchemeStyle } from '@wordpress/compose';

/**
* 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,
};

/**
* Block draggable chip component
*
* @param {Object} props Component props.
* @param {Object} [props.icon] Block icon.
* @return {JSX.Element} Chip component.
*/
export default function BlockDraggableChip( { icon } ) {
const containerStyle = usePreferredColorSchemeStyle(
styles[ 'draggable-chip__container' ],
styles[ 'draggable-chip__container--dark' ]
);

return (
<View style={ [ containerStyle, shadowStyle ] }>
<BlockIcon icon={ dragHandle } />
{ icon && <BlockIcon icon={ icon } /> }
</View>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/**
* External dependencies
*/
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
useAnimatedReaction,
runOnJS,
} from 'react-native-reanimated';
import {
useSafeAreaInsets,
useSafeAreaFrame,
} from 'react-native-safe-area-context';

/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { generateHapticFeedback } from '@wordpress/react-native-bridge';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { useBlockListContext } from '../block-list/block-list-context';
import styles from './dropping-insertion-point.scss';

/**
* Dropping zone indicator component.
*
* This component shows where a block can be dropped when it's being dragged.
*
* @param {Object} props Component props.
* @param {Object} props.scroll Scroll offset object.
* @param {Object} props.currentYPosition Current Y coordinate position when dragging.
* @param {import('react-native-reanimated').SharedValue} props.isDragging Whether or not dragging has started.
* @param {import('react-native-reanimated').SharedValue} props.targetBlockIndex Current block target index.
*
* @return {JSX.Element} The component to be rendered.
*/
export default function DroppingInsertionPoint( {
scroll,
currentYPosition,
isDragging,
targetBlockIndex,
} ) {
const {
getBlockOrder,
isBlockBeingDragged,
isDraggingBlocks,
getPreviousBlockClientId,
getNextBlockClientId,
} = useSelect( blockEditorStore );

const { blocksLayouts, findBlockLayoutByClientId } = useBlockListContext();
const { top, bottom } = useSafeAreaInsets();
const { height } = useSafeAreaFrame();
const safeAreaOffset = top + bottom;
const maxHeight =
height -
( safeAreaOffset + styles[ 'dropping-insertion-point' ].height );

const blockYPosition = useSharedValue( 0 );
const opacity = useSharedValue( 0 );

useAnimatedReaction(
() => isDragging.value,
( value ) => {
if ( ! value ) {
opacity.value = 0;
blockYPosition.value = 0;
}
}
);

function getSelectedBlockIndicatorPosition( positions ) {
const currentYPositionWithScroll =
currentYPosition.value + scroll.offsetY.value;
const midpoint = ( positions.top + positions.bottom ) / 2;

return midpoint < currentYPositionWithScroll
? positions.bottom
: positions.top;
}

function setIndicatorPosition( index ) {
const insertionPointIndex = index;
const order = getBlockOrder();
const isDraggingAnyBlocks = isDraggingBlocks();

if (
! isDraggingAnyBlocks ||
insertionPointIndex === null ||
! order.length
) {
return;
}

let previousClientId = order[ insertionPointIndex - 1 ];
let nextClientId = order[ insertionPointIndex ];

while ( isBlockBeingDragged( previousClientId ) ) {
previousClientId = getPreviousBlockClientId( previousClientId );
}

while ( isBlockBeingDragged( nextClientId ) ) {
nextClientId = getNextBlockClientId( nextClientId );
}

const previousElement = previousClientId
? findBlockLayoutByClientId(
blocksLayouts.current,
previousClientId
)
: null;
const nextElement = nextClientId
? findBlockLayoutByClientId( blocksLayouts.current, nextClientId )
: null;

const previousElementPosition = previousElement
? previousElement.y + previousElement.height
: 0;
const nextElementPosition = nextElement ? nextElement.y : 0;

const elementsPositions = {
top: Math.floor(
previousElement ? previousElementPosition : nextElementPosition
),
bottom: Math.floor(
nextElement ? nextElementPosition : previousElementPosition
),
};

const nextPosition =
elementsPositions.top !== elementsPositions.bottom
? getSelectedBlockIndicatorPosition( elementsPositions )
: elementsPositions.top;

if ( nextPosition && blockYPosition.value !== nextPosition ) {
opacity.value = 0;
blockYPosition.value = nextPosition;
opacity.value = withTiming( 1 );
generateHapticFeedback();
}
}

useAnimatedReaction(
() => targetBlockIndex.value,
( value, previous ) => {
if ( value !== previous ) {
runOnJS( setIndicatorPosition )( value );
}
}
);

const animatedStyles = useAnimatedStyle( () => {
const translationY = blockYPosition.value - scroll.offsetY.value;
// Prevents overflowing behind the header/footer
const shouldHideIndicator =
translationY < 0 || translationY > maxHeight;

return {
opacity: shouldHideIndicator ? 0 : opacity.value,
transform: [
{
translateY: translationY,
},
],
};
} );

const insertionPointStyles = [
styles[ 'dropping-insertion-point' ],
animatedStyles,
];

return (
<Animated.View pointerEvents="none" style={ insertionPointStyles } />
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.dropping-insertion-point {
position: absolute;
left: $dashed-border-space;
right: $dashed-border-space;
height: 3;
background-color: $blue-wordpress;
z-index: 1;
}
Loading
Loading