Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export default function ActiveItemPortal({
}: ActiveItemPortalProps) {
const { isTeleported, measurePortalOutlet, teleport } =
usePortalContext() ?? {};
const updateTimeoutRef = useRef<ReturnType<typeof setTimeout>>(null);
const teleportEnabled = useMutableValue(false);
const isFirstUpdateRef = useRef(true);

const renderTeleportedItemCell = useCallback(
() => (
Expand Down Expand Up @@ -80,38 +80,42 @@ export default function ActiveItemPortal({
const teleportedItemId = `${commonValuesContext.containerId}-${itemKey}`;

const enableTeleport = useStableCallback(() => {
isFirstUpdateRef.current = true;
teleport?.(teleportedItemId, renderTeleportedItemCell());
onTeleport(true);
});

const disableTeleport = useCallback(() => {
if (updateTimeoutRef.current !== null) {
clearTimeout(updateTimeoutRef.current);
}
teleport?.(teleportedItemId, null);
onTeleport(false);
}, [teleport, teleportedItemId, onTeleport]);

useEffect(() => disableTeleport, [disableTeleport]);

useEffect(() => {
if (isTeleported?.(teleportedItemId)) {
// We have to delay the update in order not to schedule render via this
// useEffect at the same time as the enableTeleport render is scheduled.
// This may happen if the user changes the item style/content via the
// onDragStart callback (e.g. in collapsible items) when we want to
// render the view unchanged at first and change it a while later to
// properly trigger all layout transitions that the item has.
updateTimeoutRef.current = setTimeout(() => {
teleport?.(teleportedItemId, renderTeleportedItemCell());
});
const checkTeleported = () => isTeleported?.(teleportedItemId);
if (!checkTeleported()) return;

const update = () =>
teleport?.(teleportedItemId, renderTeleportedItemCell());

if (isFirstUpdateRef.current) {
isFirstUpdateRef.current = false;
// Needed for proper collapsible items behavior
setTimeout(update);
} else {
update();
}
}, [isTeleported, renderTeleportedItemCell, teleport, teleportedItemId]);

useAnimatedReaction(
() => activationAnimationProgress.value,
(progress, prevProgress) => {
if (prevProgress && progress > prevProgress && !teleportEnabled.value) {
if (
prevProgress !== null &&
progress > prevProgress &&
!teleportEnabled.value
) {
// We have to ensure that the portal outlet ref is measured before the
// teleported item is rendered within it because portal outlet position
// must be known to calculate the teleported item position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function removeFromPendingTimeouts(id: AnimatedTimeoutID): void {

export function setAnimatedTimeout<F extends AnyFunction>(
callback: F,
delay: number
delay = 0
): AnimatedTimeoutID {
let startTimestamp: number;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ const { DragProvider, useDragContext } = createProvider('Drag')<

activeAnimationProgress.value = 0;
activeItemDropped.value = false;
prevActiveItemKey.value = activeItemKey.value;
prevActiveItemKey.value = null;
activeItemKey.value = key;
activeItemPosition.value = position;
activeItemDimensions.value = dimensions;
Expand All @@ -305,23 +305,14 @@ const { DragProvider, useDragContext } = createProvider('Drag')<

updateLayer?.(LayerState.FOCUSED);

let touchedItemPosition = position;

// We need to update the custom handle measurements if the custom handle
// is used (touch position is relative to the handle in this case)
updateActiveHandleMeasurements?.(key);
if (activeHandleMeasurements?.value) {
const { pageX, pageY } = activeHandleMeasurements.value;
touchedItemPosition = {
x: pageX - containerMeasurements.pageX,
y: pageY - containerMeasurements.pageY
};
}

// Touch position relative to the top-left corner of the sortable
// container
const touchX = touchedItemPosition.x + touch.x;
const touchY = touchedItemPosition.y + touch.y;
const touchX = touch.absoluteX - containerMeasurements.pageX;
const touchY = touch.absoluteY - containerMeasurements.pageY;

touchPosition.value = { x: touchX, y: touchY };
dragStartTouchPosition.value = touchPosition.value;
Expand All @@ -341,24 +332,29 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
activationAnimationProgress.value = animate();

haptics.medium();
stableOnDragStart({
fromIndex: dragStartIndex.value,
indexToKey: indexToKey.value,
key,
keyToIndex: keyToIndex.value

// Use timeout to ensure that the callback is called after all animated
// reactions are computed in the library
setAnimatedTimeout(() => {
stableOnDragStart({
fromIndex: dragStartIndex.value,
indexToKey: indexToKey.value,
key,
keyToIndex: keyToIndex.value
});
});
},
[
activationAnimationDuration,
activeAnimationProgress,
activeContainerId,
activeHandleMeasurements,
activeItemDimensions,
activeItemDropped,
activationState,
activeItemKey,
activeItemPosition,
containerId,
containerRef,
dragStartIndex,
dragStartItemTouchOffset,
dragStartTouchPosition,
Expand All @@ -369,7 +365,6 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
indexToKey,
keyToIndex,
multiZoneActiveItemDimensions,
containerRef,
prevActiveItemKey,
stableOnDragStart,
touchPosition,
Expand Down Expand Up @@ -569,7 +564,6 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
});

setAnimatedTimeout(() => {
prevActiveItemKey.value = null;
activeItemDropped.value = true;
updateLayer?.(LayerState.IDLE);
stableOnActiveItemDropped({
Expand Down
Loading