Skip to content

Commit 41a90bc

Browse files
committed
Start working on reordering on drag end
1 parent e74d8af commit 41a90bc

10 files changed

Lines changed: 85 additions & 25 deletions

File tree

example/app/src/examples/SortableGrid/PlaygroundExample.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback } from 'react';
1+
import { useCallback, useState } from 'react';
22
import { StyleSheet, Text, View } from 'react-native';
33
import type { SortableGridRenderItem } from 'react-native-sortables';
44
import Sortable from 'react-native-sortables';
@@ -9,6 +9,8 @@ import { colors, radius, sizes, spacing, text } from '@/theme';
99
const DATA = Array.from({ length: 12 }, (_, index) => `Item ${index + 1}`);
1010

1111
export default function PlaygroundExample() {
12+
const [data, setData] = useState(DATA);
13+
1214
const renderItem = useCallback<SortableGridRenderItem<string>>(
1315
({ item }) => (
1416
<View style={styles.card}>
@@ -23,9 +25,23 @@ export default function PlaygroundExample() {
2325
<Sortable.Grid
2426
columnGap={10}
2527
columns={3}
26-
data={DATA}
28+
data={data}
2729
renderItem={renderItem}
30+
reorderOnDrag={false}
2831
rowGap={10}
32+
strategy='swap'
33+
onDragEnd={params => {
34+
console.log(
35+
'item',
36+
params.key,
37+
'moved from',
38+
params.fromIndex,
39+
'to',
40+
params.toIndex,
41+
`(${params.data[params.toIndex]})`
42+
);
43+
setData(params.data.filter(item => item !== params.key));
44+
}}
2945
/>
3046
</ScrollScreen>
3147
);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export default function DropIndicatorExample() {
3535
columns={COLUMNS}
3636
data={DATA}
3737
renderItem={renderItem}
38+
reorderOnDrag={false}
3839
rowGap={spacing.xs}
3940
showDropIndicator
4041
/>

packages/react-native-sortables/src/components/SortableFlex.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ const SortableFlexInner = memo(function SortableFlexInner({
101101
paddingRight,
102102
paddingTop,
103103
paddingVertical,
104+
reorderOnDrag,
104105
rowGap,
105106
showDropIndicator,
106107
strategy,
@@ -159,6 +160,7 @@ const SortableFlexInner = memo(function SortableFlexInner({
159160
itemEntering={itemEntering}
160161
itemExiting={itemExiting}
161162
overflow={overflow}
163+
reorderOnDrag={reorderOnDrag}
162164
showDropIndicator={showDropIndicator}
163165
strategy={strategy}
164166
styleProps={styleProps}
@@ -176,13 +178,15 @@ type SortableFlexComponentProps = Pick<
176178
| 'itemEntering'
177179
| 'itemExiting'
178180
| 'overflow'
181+
| 'reorderOnDrag'
179182
| 'showDropIndicator'
180183
| 'strategy'
181184
> & {
182185
styleProps: FlexStyleProps;
183186
};
184187

185188
function SortableFlexComponent({
189+
reorderOnDrag,
186190
strategy,
187191
styleProps,
188192
...rest
@@ -200,7 +204,7 @@ function SortableFlexComponent({
200204
width
201205
} = styleProps;
202206

203-
useOrderUpdater(strategy, FLEX_STRATEGIES);
207+
useOrderUpdater(strategy, FLEX_STRATEGIES, reorderOnDrag);
204208

205209
const baseContainerStyle: ViewStyle = {
206210
...styleProps,

packages/react-native-sortables/src/components/SortableGrid.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ const SortableGridInner = typedMemo(function SortableGridInner<I>({
109109
itemEntering,
110110
itemExiting,
111111
overflow,
112+
reorderOnDrag,
112113
rowGap: _rowGap,
113114
rowHeight,
114115
showDropIndicator,
@@ -156,6 +157,7 @@ const SortableGridInner = typedMemo(function SortableGridInner<I>({
156157
itemEntering={itemEntering}
157158
itemExiting={itemExiting}
158159
overflow={overflow}
160+
reorderOnDrag={reorderOnDrag}
159161
rowGap={rowGap}
160162
rowHeight={rowHeight}
161163
showDropIndicator={showDropIndicator}
@@ -176,6 +178,7 @@ type SortableGridComponentProps<I> = Pick<
176178
| 'itemEntering'
177179
| 'itemExiting'
178180
| 'overflow'
181+
| 'reorderOnDrag'
179182
| 'rowHeight'
180183
| 'showDropIndicator'
181184
| 'strategy'
@@ -188,6 +191,7 @@ function SortableGridComponent<I>({
188191
columnGap,
189192
groups,
190193
isVertical,
194+
reorderOnDrag,
191195
rowGap,
192196
rowHeight,
193197
strategy,
@@ -199,7 +203,7 @@ function SortableGridComponent<I>({
199203

200204
const isFirstRenderRef = useRef(true);
201205

202-
useOrderUpdater(strategy, GRID_STRATEGIES);
206+
useOrderUpdater(strategy, GRID_STRATEGIES, reorderOnDrag);
203207

204208
useLayoutEffect(() => {
205209
if (isFirstRenderRef.current) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const DEFAULT_SHARED_PROPS = {
5757
onOrderChange: undefined,
5858
overDrag: 'both',
5959
overflow: 'visible',
60+
reorderOnDrag: true,
6061
reorderTriggerOrigin: 'center',
6162
scrollableRef: undefined,
6263
showDropIndicator: false,

packages/react-native-sortables/src/providers/grid/GridLayoutProvider/updates/insert.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ function useInactiveIndexToKey() {
3535
}),
3636
({ excludedKey, fixedKeys, idxToKey }) => {
3737
if (excludedKey === null) {
38-
result.value = EMPTY_ARRAY;
3938
return;
4039
}
4140

packages/react-native-sortables/src/providers/grid/GridLayoutProvider/updates/swap.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ function useInactiveIndexToKey() {
3737
({ excludedKey, idxToKey, keyToIdx }) => {
3838
const excludedIndex = excludedKey ? keyToIdx[excludedKey] : undefined;
3939
if (excludedIndex === undefined) {
40-
result.value = EMPTY_ARRAY;
4140
return;
4241
}
4342

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1+
import { useCallback } from 'react';
12
import { useAnimatedReaction } from 'react-native-reanimated';
23

34
import { useDebugContext } from '../../../debug';
4-
import type { PredefinedStrategies, SortStrategyFactory } from '../../../types';
5+
import type {
6+
OrderUpdaterCallbackProps,
7+
PredefinedStrategies,
8+
SortStrategyFactory
9+
} from '../../../types';
510
import { error } from '../../../utils';
611
import { useCommonValuesContext } from '../CommonValuesProvider';
712
import { useDragContext } from '../DragProvider';
813

914
export default function useOrderUpdater<
1015
P extends PredefinedStrategies = PredefinedStrategies
11-
>(strategy: keyof P | SortStrategyFactory, predefinedStrategies: P) {
16+
>(
17+
strategy: keyof P | SortStrategyFactory,
18+
predefinedStrategies: P,
19+
reorderOnDrag: boolean
20+
) {
1221
const useStrategy =
1322
typeof strategy === 'string' ? predefinedStrategies[strategy] : strategy;
1423

@@ -25,27 +34,18 @@ export default function useOrderUpdater<
2534

2635
const updater = useStrategy();
2736

28-
useAnimatedReaction(
29-
() => ({
30-
activeKey: activeItemKey.value,
31-
dimensions: activeItemDimensions.value,
32-
position: triggerOriginPosition.value
33-
}),
34-
({ activeKey, dimensions, position }) => {
35-
if (!activeKey || !dimensions || !position) {
36-
if (debugCross) debugCross.set({ position: null });
37-
return;
38-
}
39-
37+
const handleOrderUpdate = useCallback(
38+
({
39+
activeKey,
40+
dimensions,
41+
position
42+
}: Omit<OrderUpdaterCallbackProps, 'activeIndex'>) => {
43+
'worklet';
4044
const activeIndex = keyToIndex.value[activeKey];
4145
if (activeIndex === undefined) {
4246
return;
4347
}
4448

45-
if (debugCross) {
46-
debugCross.set({ color: '#00007e', position });
47-
}
48-
4949
const newOrder = updater({
5050
activeIndex,
5151
activeKey,
@@ -61,6 +61,38 @@ export default function useOrderUpdater<
6161
newOrder
6262
);
6363
}
64+
},
65+
[handleOrderChange, keyToIndex, updater]
66+
);
67+
68+
useAnimatedReaction(
69+
() => ({
70+
activeKey: activeItemKey.value,
71+
dimensions: activeItemDimensions.value,
72+
position: triggerOriginPosition.value
73+
}),
74+
({ activeKey, dimensions, position }, prevProps) => {
75+
debugCross?.set({ color: '#00007e', position });
76+
77+
if (reorderOnDrag) {
78+
if (activeKey !== null && dimensions && position) {
79+
handleOrderUpdate({ activeKey, dimensions, position });
80+
} else {
81+
debugCross?.set({ position: null });
82+
}
83+
} else if (
84+
activeKey === null &&
85+
prevProps?.dimensions &&
86+
prevProps?.position &&
87+
prevProps?.activeKey !== null
88+
) {
89+
debugCross?.set({ position: null });
90+
handleOrderUpdate({
91+
activeKey: prevProps.activeKey,
92+
dimensions: prevProps.dimensions,
93+
position: prevProps.position
94+
});
95+
}
6496
}
6597
);
6698
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ export type SharedProps = Simplify<
327327
* @default 'visible'
328328
*/
329329
overflow: Overflow;
330+
/** Whether to reorder items during drag gesture or after the active item is dropped
331+
* @default true
332+
*/
333+
reorderOnDrag: boolean;
330334
/** Enables debug mode to show additional visual helpers and console logs.
331335
* @note This only works in development builds and has no effect in production.
332336
* @default false

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export type DebugContextType = {
218218

219219
// ORDER UPDATER
220220

221-
type OrderUpdaterCallbackProps = {
221+
export type OrderUpdaterCallbackProps = {
222222
activeKey: string;
223223
activeIndex: number;
224224
dimensions: Dimensions;

0 commit comments

Comments
 (0)