Skip to content

Commit ef6e6cd

Browse files
authored
feat: Active item dropped callback, more props in drag start callback (#381)
## Description This PR adds more props to the `onDragStart` callback and implement the new `onActiveItemDropped` callback that is called when the active item drop animation finishes.
1 parent c0b3c03 commit ef6e6cd

File tree

7 files changed

+104
-23
lines changed

7 files changed

+104
-23
lines changed

example/app/src/examples/SortableFlex/features/CallbacksExample.tsx

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
/* eslint-disable no-console */
33
import { useCallback, useMemo, useState } from 'react';
44
import { useSharedValue } from 'react-native-reanimated';
5-
import Sortable, {
6-
type DragEndCallback,
7-
type DragMoveCallback,
8-
type DragStartCallback,
9-
type OrderChangeCallback,
10-
type SortableFlexDragEndCallback
5+
import type {
6+
ActiveItemDroppedCallback,
7+
DragEndCallback,
8+
DragMoveCallback,
9+
DragStartCallback,
10+
OrderChangeCallback,
11+
SortableFlexDragEndCallback
1112
} from 'react-native-sortables';
13+
import Sortable from 'react-native-sortables';
1214

1315
import type { SwitchOptions } from '@/components';
1416
import {
@@ -67,6 +69,13 @@ export default function CallbacksExample() {
6769
[text]
6870
);
6971

72+
const onActiveItemDroppedJS = useCallback<ActiveItemDroppedCallback>(
73+
params => {
74+
text.value = formatCallbackResult('onActiveItemDropped', params);
75+
},
76+
[text]
77+
);
78+
7079
/* Callbacks executed on the UI thread */
7180

7281
const onDragStartUI = useCallback<DragStartCallback>(
@@ -101,12 +110,21 @@ export default function CallbacksExample() {
101110
[text]
102111
);
103112

113+
const onActiveItemDroppedUI = useCallback<ActiveItemDroppedCallback>(
114+
params => {
115+
'worklet';
116+
text.value = formatCallbackResult('onActiveItemDropped', params);
117+
},
118+
[text]
119+
);
120+
104121
const options = useMemo(
105122
() => ({
106123
onDragStart: sw(onDragStartJS, onDragStartUI),
107124
onDragEnd: sw(onDragEndJS, onDragEndUI),
108125
onOrderChange: sw(onOrderChangeJS, onOrderChangeUI),
109-
onDragMove: sw(onDragMoveJS, onDragMoveUI)
126+
onDragMove: sw(onDragMoveJS, onDragMoveUI),
127+
onActiveItemDropped: sw(onActiveItemDroppedJS, onActiveItemDroppedUI)
110128
}),
111129
[
112130
onDragEndJS,
@@ -116,7 +134,9 @@ export default function CallbacksExample() {
116134
onDragStartJS,
117135
onDragStartUI,
118136
onOrderChangeJS,
119-
onOrderChangeUI
137+
onOrderChangeUI,
138+
onActiveItemDroppedJS,
139+
onActiveItemDroppedUI
120140
]
121141
);
122142

example/app/src/examples/SortableGrid/features/CallbacksExample.tsx

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
/* eslint-disable no-console */
33
import { useCallback, useMemo, useState } from 'react';
44
import { useSharedValue } from 'react-native-reanimated';
5-
import Sortable, {
6-
type DragEndCallback,
7-
type DragMoveCallback,
8-
type DragStartCallback,
9-
type OrderChangeCallback,
10-
type SortableGridDragEndCallback,
11-
type SortableGridRenderItem
5+
import type {
6+
ActiveItemDroppedCallback,
7+
DragEndCallback,
8+
DragMoveCallback,
9+
DragStartCallback,
10+
OrderChangeCallback,
11+
SortableGridDragEndCallback,
12+
SortableGridRenderItem
1213
} from 'react-native-sortables';
14+
import Sortable from 'react-native-sortables';
1315

1416
import type { SwitchOptions } from '@/components';
1517
import {
@@ -68,6 +70,13 @@ export default function CallbacksExample() {
6870
[text]
6971
);
7072

73+
const onActiveItemDroppedJS = useCallback<ActiveItemDroppedCallback>(
74+
params => {
75+
text.value = formatCallbackResult('onActiveItemDropped', params);
76+
},
77+
[text]
78+
);
79+
7180
/* Callbacks executed on the UI thread */
7281

7382
const onDragStartUI = useCallback<DragStartCallback>(
@@ -102,6 +111,14 @@ export default function CallbacksExample() {
102111
[text]
103112
);
104113

114+
const onActiveItemDroppedUI = useCallback<ActiveItemDroppedCallback>(
115+
params => {
116+
'worklet';
117+
text.value = formatCallbackResult('onActiveItemDropped', params);
118+
},
119+
[text]
120+
);
121+
105122
const renderItem = useCallback<SortableGridRenderItem<(typeof DATA)[number]>>(
106123
({ item }) => <GridCard>{item}</GridCard>,
107124
[]
@@ -112,7 +129,8 @@ export default function CallbacksExample() {
112129
onDragStart: sw(onDragStartJS, onDragStartUI),
113130
onDragEnd: sw(onDragEndJS, onDragEndUI),
114131
onOrderChange: sw(onOrderChangeJS, onOrderChangeUI),
115-
onDragMove: sw(onDragMoveJS, onDragMoveUI)
132+
onDragMove: sw(onDragMoveJS, onDragMoveUI),
133+
onActiveItemDropped: sw(onActiveItemDroppedJS, onActiveItemDroppedUI)
116134
}),
117135
[
118136
onDragEndJS,
@@ -122,7 +140,9 @@ export default function CallbacksExample() {
122140
onDragStartJS,
123141
onDragStartUI,
124142
onOrderChangeJS,
125-
onOrderChangeUI
143+
onOrderChangeUI,
144+
onActiveItemDroppedJS,
145+
onActiveItemDroppedUI
126146
]
127147
);
128148

packages/react-native-sortables/src/constants/props.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export const DEFAULT_SHARED_PROPS = {
5959
itemExiting: IS_WEB ? null : SortableItemExiting,
6060
itemsLayout: IS_WEB ? null : LinearTransition,
6161
itemsLayoutTransitionMode: 'all',
62+
onActiveItemDropped: undefined,
6263
onDragMove: undefined,
6364
onDragStart: undefined,
6465
onOrderChange: undefined,

packages/react-native-sortables/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { PortalProvider } from './providers';
1212
export type { CustomHandleProps, SortableLayerProps } from './components';
1313
export * from './constants/layoutAnimations';
1414
export type {
15+
ActiveItemDroppedCallback,
1516
DragEndCallback,
1617
DragMoveCallback,
1718
DragStartCallback,

packages/react-native-sortables/src/providers/SharedProvider.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export default function SharedProvider({
5757
debug,
5858
hapticsEnabled,
5959
itemKeys,
60+
onActiveItemDropped,
6061
onDragEnd,
6162
onDragMove,
6263
onDragStart,
@@ -103,6 +104,7 @@ export default function SharedProvider({
103104
<DragProvider
104105
hapticsEnabled={hapticsEnabled}
105106
overDrag={overDrag}
107+
onActiveItemDropped={onActiveItemDropped}
106108
onDragEnd={onDragEnd}
107109
onDragMove={onDragMove}
108110
onDragStart={onDragStart}

packages/react-native-sortables/src/providers/shared/DragProvider.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
5151
DragContextType
5252
>(({
5353
hapticsEnabled,
54+
onActiveItemDropped,
5455
onDragEnd: stableOnDragEnd,
5556
onDragMove,
5657
onDragStart,
@@ -115,6 +116,7 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
115116
const stableOnDragStart = useStableCallbackValue(onDragStart);
116117
const stableOnDragMove = useStableCallbackValue(onDragMove);
117118
const stableOnOrderChange = useStableCallbackValue(onOrderChange);
119+
const stableOnActiveItemDropped = useStableCallbackValue(onActiveItemDropped);
118120

119121
// ACTIVE ITEM POSITION UPDATER
120122
useAnimatedReaction(
@@ -317,7 +319,9 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
317319
haptics.medium();
318320
stableOnDragStart({
319321
fromIndex: dragStartIndex.value,
320-
key
322+
indexToKey: indexToKey.value,
323+
key,
324+
keyToIndex: keyToIndex.value
321325
});
322326
},
323327
[
@@ -334,6 +338,7 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
334338
inactiveAnimationProgress,
335339
inactiveItemOpacity,
336340
inactiveItemScale,
341+
indexToKey,
337342
itemDimensions,
338343
itemPositions,
339344
keyToIndex,
@@ -471,18 +476,21 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
471476
activeHandleMeasurements.value = null;
472477
}
473478

479+
const fromIndex = dragStartIndex.value;
480+
const toIndex = keyToIndex.value[key]!;
481+
474482
if (activeItemKey.value !== null) {
475483
prevActiveItemKey.value = activeItemKey.value;
476484
activeItemKey.value = null;
477485
updateLayer?.(LayerState.INTERMEDIATE);
478486
haptics.medium();
479487

480488
stableOnDragEnd({
481-
fromIndex: dragStartIndex.value,
489+
fromIndex,
482490
indexToKey: indexToKey.value,
483491
key,
484492
keyToIndex: keyToIndex.value,
485-
toIndex: keyToIndex.value[key]!
493+
toIndex
486494
});
487495
}
488496

@@ -499,6 +507,7 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
499507
prevActiveItemKey.value = null;
500508
activeItemDropped.value = true;
501509
updateLayer?.(LayerState.IDLE);
510+
stableOnActiveItemDropped({ fromIndex, key, toIndex });
502511
}, dropAnimationDuration.value);
503512
},
504513
[
@@ -519,6 +528,7 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
519528
inactiveAnimationProgress,
520529
indexToKey,
521530
keyToIndex,
531+
stableOnActiveItemDropped,
522532
stableOnDragEnd,
523533
touchPosition,
524534
touchStartTouch,

packages/react-native-sortables/src/types/props/shared.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ export type DragStartParams = {
131131
key: string;
132132
/** Original index of the dragged item */
133133
fromIndex: number;
134+
/** Array mapping indices to item keys */
135+
indexToKey: Array<string>;
136+
/** Object mapping item keys to their indices */
137+
keyToIndex: Record<string, number>;
134138
};
135139

136140
/** Parameters provided when an item is being dragged. */
@@ -159,18 +163,28 @@ export type DragEndParams = {
159163

160164
/** Parameters provided when items change their positions during dragging. */
161165
export type OrderChangeParams = {
162-
/** Original index of the moved item */
166+
/** Unique identifier of the moved item */
167+
key: string;
168+
/** Previous index of the moved item */
163169
fromIndex: number;
164170
/** New index of the moved item */
165171
toIndex: number;
166-
/** Unique identifier of the moved item */
167-
key: string;
168172
/** Array mapping indices to item keys */
169173
indexToKey: Array<string>;
170174
/** Object mapping item keys to their indices */
171175
keyToIndex: Record<string, number>;
172176
};
173177

178+
/** Parameters provided when the active item is dropped */
179+
export type ActiveItemDroppedParams = {
180+
/** Unique identifier of the dropped item */
181+
key: string;
182+
/** Original index of the dropped item */
183+
fromIndex: number;
184+
/** Final index where the item was dropped */
185+
toIndex: number;
186+
};
187+
174188
/**
175189
* Callback function called when the item drag starts
176190
* @param params - Parameters for the drag start event
@@ -195,6 +209,14 @@ export type DragEndCallback = (params: DragEndParams) => void;
195209
*/
196210
export type OrderChangeCallback = (params: OrderChangeParams) => void;
197211

212+
/**
213+
* Callback function called when the active item is dropped
214+
* @param params - Parameters for the active item dropped event
215+
*/
216+
export type ActiveItemDroppedCallback = (
217+
params: ActiveItemDroppedParams
218+
) => void;
219+
198220
export type SortableCallbacks = {
199221
/** Called when an item starts being dragged
200222
* @param params Parameters for the drag start event
@@ -218,6 +240,11 @@ export type SortableCallbacks = {
218240
* it's called frequently during dragging. Use `onDragEnd` instead.
219241
*/
220242
onOrderChange?: OrderChangeCallback;
243+
244+
/** Called once when the active item is dropped.
245+
* @param params Parameters for the active item dropped event
246+
*/
247+
onActiveItemDropped?: ActiveItemDroppedCallback;
221248
};
222249

223250
export type Overflow = 'hidden' | 'visible';

0 commit comments

Comments
 (0)