react-native-drax 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/lib/module/SortableContainer.js +14 -4
- package/lib/module/SortableContainer.js.map +1 -1
- package/lib/module/hooks/useCallbackDispatch.js +10 -3
- package/lib/module/hooks/useCallbackDispatch.js.map +1 -1
- package/lib/module/hooks/useSortableList.js +166 -2
- package/lib/module/hooks/useSortableList.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/math.js +72 -0
- package/lib/module/math.js.map +1 -1
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/src/SortableContainer.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useCallbackDispatch.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useSortableList.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/math.d.ts +25 -1
- package/lib/typescript/src/math.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +13 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/SortableContainer.tsx +10 -2
- package/src/hooks/useCallbackDispatch.tsx +9 -2
- package/src/hooks/useSortableList.ts +197 -2
- package/src/index.ts +3 -2
- package/src/math.ts +82 -0
- package/src/types.ts +14 -0
|
@@ -33,7 +33,9 @@ import {
|
|
|
33
33
|
import { isDraggable } from './useSpatialIndex';
|
|
34
34
|
|
|
35
35
|
/** Style override to strip margins — hover is positioned via translateX/Y */
|
|
36
|
-
|
|
36
|
+
/** Styles to strip from the hover content — margins and absolute positioning
|
|
37
|
+
* are not needed since hover is positioned via translateX/Y. */
|
|
38
|
+
const hoverResetStyle = {
|
|
37
39
|
margin: 0,
|
|
38
40
|
marginHorizontal: 0,
|
|
39
41
|
marginVertical: 0,
|
|
@@ -41,6 +43,11 @@ const noMargin = {
|
|
|
41
43
|
marginBottom: 0,
|
|
42
44
|
marginLeft: 0,
|
|
43
45
|
marginRight: 0,
|
|
46
|
+
position: 'relative',
|
|
47
|
+
left: 0,
|
|
48
|
+
top: 0,
|
|
49
|
+
right: undefined,
|
|
50
|
+
bottom: undefined,
|
|
44
51
|
} as const;
|
|
45
52
|
|
|
46
53
|
interface CallbackDispatchDeps {
|
|
@@ -238,7 +245,7 @@ export const useCallbackDispatch = (deps: CallbackDispatchDeps) => {
|
|
|
238
245
|
<View style={[
|
|
239
246
|
viewStyle,
|
|
240
247
|
dims && { width: dims.width, height: dims.height },
|
|
241
|
-
|
|
248
|
+
hoverResetStyle,
|
|
242
249
|
]}>
|
|
243
250
|
{draggedEntry.props.children}
|
|
244
251
|
</View>
|
|
@@ -9,9 +9,11 @@ import {
|
|
|
9
9
|
defaultAutoScrollJumpRatio,
|
|
10
10
|
defaultListItemLongPressDelay,
|
|
11
11
|
} from '../params';
|
|
12
|
+
import { packGrid } from '../math';
|
|
12
13
|
import type {
|
|
13
14
|
DraxSnapbackTarget,
|
|
14
15
|
DraxViewMeasurements,
|
|
16
|
+
GridItemSpan,
|
|
15
17
|
Position,
|
|
16
18
|
SortableItemMeasurement,
|
|
17
19
|
SortableListHandle,
|
|
@@ -51,6 +53,7 @@ export const useSortableList = <T,>(
|
|
|
51
53
|
autoScrollBackThreshold = defaultAutoScrollBackThreshold,
|
|
52
54
|
autoScrollForwardThreshold = defaultAutoScrollForwardThreshold,
|
|
53
55
|
animationConfig = 'default',
|
|
56
|
+
getItemSpan,
|
|
54
57
|
inactiveItemStyle,
|
|
55
58
|
itemEntering,
|
|
56
59
|
itemExiting,
|
|
@@ -215,6 +218,108 @@ export const useSortableList = <T,>(
|
|
|
215
218
|
// Alias for internal use
|
|
216
219
|
const getMeasForOrigIdx = getMeasurementByOriginalIndex;
|
|
217
220
|
|
|
221
|
+
/** Get the span for an item at the given original data index */
|
|
222
|
+
const getSpanForOrigIdx = (origIdx: number): GridItemSpan => {
|
|
223
|
+
if (!getItemSpan) return { colSpan: 1, rowSpan: 1 };
|
|
224
|
+
const item = stableData[origIdx];
|
|
225
|
+
if (item === undefined) return { colSpan: 1, rowSpan: 1 };
|
|
226
|
+
return getItemSpan(item, origIdx);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Derive grid geometry (cell size + gaps) from current measurements.
|
|
231
|
+
* Only used when getItemSpan is provided and numColumns > 1.
|
|
232
|
+
*/
|
|
233
|
+
const deriveGridGeometry = (): {
|
|
234
|
+
cellWidth: number;
|
|
235
|
+
cellHeight: number;
|
|
236
|
+
colGap: number;
|
|
237
|
+
rowGap: number;
|
|
238
|
+
startX: number;
|
|
239
|
+
startY: number;
|
|
240
|
+
} | undefined => {
|
|
241
|
+
if (!getItemSpan || originalIndexes.length === 0) return undefined;
|
|
242
|
+
|
|
243
|
+
const firstOrigIdx = originalIndexes[0];
|
|
244
|
+
const startMeas = firstOrigIdx !== undefined ? getMeasForOrigIdx(firstOrigIdx) : undefined;
|
|
245
|
+
if (!startMeas) return undefined;
|
|
246
|
+
|
|
247
|
+
// Pack original order to know grid positions for gap derivation
|
|
248
|
+
const origPacking = packGrid(
|
|
249
|
+
originalIndexes.length,
|
|
250
|
+
numColumns,
|
|
251
|
+
(displayIdx) => getSpanForOrigIdx(originalIndexes[displayIdx]!),
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
// Find cell dimensions from measurements of items with span 1
|
|
255
|
+
let cellWidth: number | undefined;
|
|
256
|
+
let cellHeight: number | undefined;
|
|
257
|
+
|
|
258
|
+
for (let i = 0; i < originalIndexes.length; i++) {
|
|
259
|
+
const origIdx = originalIndexes[i]!;
|
|
260
|
+
const span = getSpanForOrigIdx(origIdx);
|
|
261
|
+
const meas = getMeasForOrigIdx(origIdx);
|
|
262
|
+
if (!meas) continue;
|
|
263
|
+
if (span.colSpan === 1 && cellWidth === undefined) cellWidth = meas.width;
|
|
264
|
+
if (span.rowSpan === 1 && cellHeight === undefined) cellHeight = meas.height;
|
|
265
|
+
if (cellWidth !== undefined && cellHeight !== undefined) break;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Fallback: derive from first item divided by its span
|
|
269
|
+
if (cellWidth === undefined || cellHeight === undefined) {
|
|
270
|
+
const firstSpan = getSpanForOrigIdx(firstOrigIdx!);
|
|
271
|
+
if (cellWidth === undefined) cellWidth = startMeas.width / firstSpan.colSpan;
|
|
272
|
+
if (cellHeight === undefined) cellHeight = startMeas.height / firstSpan.rowSpan;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Derive column gap from two items at different grid columns
|
|
276
|
+
let colGap = 0;
|
|
277
|
+
for (let i = 0; i < origPacking.positions.length && colGap === 0; i++) {
|
|
278
|
+
for (let j = i + 1; j < origPacking.positions.length; j++) {
|
|
279
|
+
const pi = origPacking.positions[i]!;
|
|
280
|
+
const pj = origPacking.positions[j]!;
|
|
281
|
+
if (pi.col !== pj.col) {
|
|
282
|
+
const mi = getMeasForOrigIdx(originalIndexes[i]!);
|
|
283
|
+
const mj = getMeasForOrigIdx(originalIndexes[j]!);
|
|
284
|
+
if (mi && mj) {
|
|
285
|
+
const colDiff = Math.abs(pj.col - pi.col);
|
|
286
|
+
const xDiff = Math.abs(mj.x - mi.x);
|
|
287
|
+
colGap = xDiff / colDiff - cellWidth;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Derive row gap from two items at different grid rows
|
|
295
|
+
let rowGap = 0;
|
|
296
|
+
for (let i = 0; i < origPacking.positions.length && rowGap === 0; i++) {
|
|
297
|
+
for (let j = i + 1; j < origPacking.positions.length; j++) {
|
|
298
|
+
const pi = origPacking.positions[i]!;
|
|
299
|
+
const pj = origPacking.positions[j]!;
|
|
300
|
+
if (pi.row !== pj.row) {
|
|
301
|
+
const mi = getMeasForOrigIdx(originalIndexes[i]!);
|
|
302
|
+
const mj = getMeasForOrigIdx(originalIndexes[j]!);
|
|
303
|
+
if (mi && mj) {
|
|
304
|
+
const rowDiff = Math.abs(pj.row - pi.row);
|
|
305
|
+
const yDiff = Math.abs(mj.y - mi.y);
|
|
306
|
+
rowGap = yDiff / rowDiff - cellHeight;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
cellWidth,
|
|
315
|
+
cellHeight,
|
|
316
|
+
colGap: Math.max(colGap, 0),
|
|
317
|
+
rowGap: Math.max(rowGap, 0),
|
|
318
|
+
startX: startMeas.x,
|
|
319
|
+
startY: startMeas.y,
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
|
|
218
323
|
// ── Shift application (merges ghost shifts for cross-container) ──
|
|
219
324
|
|
|
220
325
|
const applyShifts = (shifts: Record<string, Position> | undefined) => {
|
|
@@ -309,7 +414,26 @@ export const useSortableList = <T,>(
|
|
|
309
414
|
}
|
|
310
415
|
displaySlot++;
|
|
311
416
|
}
|
|
417
|
+
} else if (getItemSpan) {
|
|
418
|
+
// ── Mixed-size grid: bin-pack items into a 2D occupancy grid ──
|
|
419
|
+
const geo = deriveGridGeometry();
|
|
420
|
+
if (!geo) return undefined;
|
|
421
|
+
|
|
422
|
+
const packing = packGrid(
|
|
423
|
+
order.length,
|
|
424
|
+
numColumns,
|
|
425
|
+
(displayIdx) => getSpanForOrigIdx(order[displayIdx]!),
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
for (let i = 0; i < order.length; i++) {
|
|
429
|
+
const gp = packing.positions[i]!;
|
|
430
|
+
targetPositions.set(i, {
|
|
431
|
+
x: geo.startX + gp.col * (geo.cellWidth + geo.colGap),
|
|
432
|
+
y: geo.startY + gp.row * (geo.cellHeight + geo.rowGap),
|
|
433
|
+
});
|
|
434
|
+
}
|
|
312
435
|
} else {
|
|
436
|
+
// ── Uniform grid: col = i % numColumns ──
|
|
313
437
|
let cursorY = startMeas.y;
|
|
314
438
|
const colXPositions: number[] = [];
|
|
315
439
|
for (let c = 0; c < numColumns && c < originalIndexes.length; c++) {
|
|
@@ -652,8 +776,60 @@ export const useSortableList = <T,>(
|
|
|
652
776
|
cursor += size + gap;
|
|
653
777
|
}
|
|
654
778
|
return itemCount - 1;
|
|
779
|
+
} else if (getItemSpan) {
|
|
780
|
+
// ── Mixed-size grid: map finger to cell, then to display index ──
|
|
781
|
+
const geo = deriveGridGeometry();
|
|
782
|
+
if (!geo) return draggedDisplayIndexRef.current ?? 0;
|
|
783
|
+
|
|
784
|
+
// Pack original order (stable positions during drag)
|
|
785
|
+
const origPacking = packGrid(
|
|
786
|
+
itemCount,
|
|
787
|
+
numColumns,
|
|
788
|
+
(displayIdx) => getSpanForOrigIdx(originalIndexes[displayIdx]!),
|
|
789
|
+
);
|
|
790
|
+
|
|
791
|
+
// Find which grid cell the finger is in
|
|
792
|
+
const cellCol = Math.max(0, Math.min(
|
|
793
|
+
Math.floor((contentPos.x - geo.startX + geo.colGap / 2) / (geo.cellWidth + geo.colGap)),
|
|
794
|
+
numColumns - 1,
|
|
795
|
+
));
|
|
796
|
+
const cellRow = Math.max(0, Math.floor(
|
|
797
|
+
(contentPos.y - geo.startY + geo.rowGap / 2) / (geo.cellHeight + geo.rowGap),
|
|
798
|
+
));
|
|
799
|
+
|
|
800
|
+
// Build cell → display index map (all cells each item occupies)
|
|
801
|
+
const cellOwner = new Map<string, number>();
|
|
802
|
+
for (let i = 0; i < origPacking.positions.length && i < itemCount; i++) {
|
|
803
|
+
const pos = origPacking.positions[i]!;
|
|
804
|
+
const span = getSpanForOrigIdx(originalIndexes[i]!);
|
|
805
|
+
for (let r = 0; r < span.rowSpan; r++) {
|
|
806
|
+
for (let c = 0; c < span.colSpan; c++) {
|
|
807
|
+
cellOwner.set(`${pos.row + r},${pos.col + c}`, i);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Direct cell hit
|
|
813
|
+
const owner = cellOwner.get(`${cellRow},${cellCol}`);
|
|
814
|
+
if (owner !== undefined) return Math.min(owner, pending.length - 1);
|
|
815
|
+
|
|
816
|
+
// Empty cell — find nearest item by center distance
|
|
817
|
+
let minDist = Infinity;
|
|
818
|
+
let nearest = 0;
|
|
819
|
+
for (let i = 0; i < origPacking.positions.length && i < itemCount; i++) {
|
|
820
|
+
const meas = measurements[i];
|
|
821
|
+
if (!meas) continue;
|
|
822
|
+
const cx = meas.x + meas.width / 2;
|
|
823
|
+
const cy = meas.y + meas.height / 2;
|
|
824
|
+
const dist = Math.abs(contentPos.x - cx) + Math.abs(contentPos.y - cy);
|
|
825
|
+
if (dist < minDist) {
|
|
826
|
+
minDist = dist;
|
|
827
|
+
nearest = i;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return Math.min(nearest, pending.length - 1);
|
|
655
831
|
} else {
|
|
656
|
-
//
|
|
832
|
+
// ── Uniform grid — find row then column ──
|
|
657
833
|
const firstMeas = measurements[0];
|
|
658
834
|
if (!firstMeas) return 0;
|
|
659
835
|
let cursorY = firstMeas.y;
|
|
@@ -750,8 +926,26 @@ export const useSortableList = <T,>(
|
|
|
750
926
|
targetPos = horizontal
|
|
751
927
|
? { x: cursor, y: snapStartMeas.y }
|
|
752
928
|
: { x: snapStartMeas.x, y: cursor };
|
|
929
|
+
} else if (getItemSpan) {
|
|
930
|
+
// Mixed-size grid — pack items and find target position
|
|
931
|
+
const geo = deriveGridGeometry();
|
|
932
|
+
if (!geo) return DraxSnapbackTargetPreset.Default;
|
|
933
|
+
|
|
934
|
+
const packing = packGrid(
|
|
935
|
+
pending.length,
|
|
936
|
+
numColumns,
|
|
937
|
+
(di) => getSpanForOrigIdx(pending[di]!),
|
|
938
|
+
);
|
|
939
|
+
|
|
940
|
+
const gp = packing.positions[displayIdx];
|
|
941
|
+
if (!gp) return DraxSnapbackTargetPreset.Default;
|
|
942
|
+
|
|
943
|
+
targetPos = {
|
|
944
|
+
x: geo.startX + gp.col * (geo.cellWidth + geo.colGap),
|
|
945
|
+
y: geo.startY + gp.row * (geo.cellHeight + geo.rowGap),
|
|
946
|
+
};
|
|
753
947
|
} else {
|
|
754
|
-
//
|
|
948
|
+
// Uniform grid
|
|
755
949
|
let cursorY = snapStartMeas.y;
|
|
756
950
|
const targetRow = Math.floor(displayIdx / numColumns);
|
|
757
951
|
const targetCol = displayIdx % numColumns;
|
|
@@ -801,6 +995,7 @@ export const useSortableList = <T,>(
|
|
|
801
995
|
longPressDelay,
|
|
802
996
|
lockToMainAxis,
|
|
803
997
|
animationConfig,
|
|
998
|
+
getItemSpan,
|
|
804
999
|
inactiveItemStyle,
|
|
805
1000
|
itemEntering,
|
|
806
1001
|
itemExiting,
|
package/src/index.ts
CHANGED
|
@@ -25,8 +25,8 @@ export { useSortableList } from './hooks/useSortableList';
|
|
|
25
25
|
export { useSortableBoard } from './hooks/useSortableBoard';
|
|
26
26
|
|
|
27
27
|
// ── Public Utilities ─────────────────────────────────────────────────
|
|
28
|
-
export { snapToAlignment } from './math';
|
|
29
|
-
export type { SnapAlignment } from './math';
|
|
28
|
+
export { snapToAlignment, packGrid } from './math';
|
|
29
|
+
export type { SnapAlignment, GridPackResult } from './math';
|
|
30
30
|
|
|
31
31
|
// ── Public Types ─────────────────────────────────────────────────────
|
|
32
32
|
export type {
|
|
@@ -75,6 +75,7 @@ export type {
|
|
|
75
75
|
// Sortable types
|
|
76
76
|
UseSortableListOptions,
|
|
77
77
|
SortableListHandle,
|
|
78
|
+
GridItemSpan,
|
|
78
79
|
} from './types';
|
|
79
80
|
export type { SortableItemContextValue } from './SortableItemContext';
|
|
80
81
|
export type {
|
package/src/math.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
DraxViewMeasurements,
|
|
3
|
+
GridItemSpan,
|
|
3
4
|
HitTestResult,
|
|
4
5
|
Position,
|
|
5
6
|
SpatialEntry,
|
|
@@ -249,3 +250,84 @@ export const snapToAlignment = (
|
|
|
249
250
|
|
|
250
251
|
return { x: x + offset.x, y: y + offset.y };
|
|
251
252
|
};
|
|
253
|
+
|
|
254
|
+
// ─── Grid Packing ───────────────────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
/** Result of packing items into a grid */
|
|
257
|
+
export interface GridPackResult {
|
|
258
|
+
/** Grid position (row, col) for each item, in input order */
|
|
259
|
+
positions: { row: number; col: number }[];
|
|
260
|
+
/** Total number of rows in the packed grid */
|
|
261
|
+
totalRows: number;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Pack items into a grid with the given number of columns.
|
|
266
|
+
* Items are placed left-to-right, top-to-bottom, filling the first
|
|
267
|
+
* available position where the item's span fits.
|
|
268
|
+
*
|
|
269
|
+
* This is the same algorithm used by mobile home screens: scan cells
|
|
270
|
+
* in reading order and place each item at the first slot that can
|
|
271
|
+
* accommodate its colSpan × rowSpan.
|
|
272
|
+
*
|
|
273
|
+
* @param count Number of items to pack
|
|
274
|
+
* @param numColumns Number of columns in the grid
|
|
275
|
+
* @param getSpan Returns the span for the item at the given index
|
|
276
|
+
*/
|
|
277
|
+
export function packGrid(
|
|
278
|
+
count: number,
|
|
279
|
+
numColumns: number,
|
|
280
|
+
getSpan: (index: number) => GridItemSpan,
|
|
281
|
+
): GridPackResult {
|
|
282
|
+
// Dynamic 2D occupancy grid — rows are added as needed
|
|
283
|
+
const occupied: boolean[][] = [];
|
|
284
|
+
const positions: { row: number; col: number }[] = [];
|
|
285
|
+
let maxRow = 0;
|
|
286
|
+
|
|
287
|
+
function ensureRow(row: number) {
|
|
288
|
+
while (occupied.length <= row) {
|
|
289
|
+
occupied.push(new Array<boolean>(numColumns).fill(false));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function isAvailable(row: number, col: number, cs: number, rs: number): boolean {
|
|
294
|
+
if (col + cs > numColumns) return false;
|
|
295
|
+
for (let r = row; r < row + rs; r++) {
|
|
296
|
+
ensureRow(r);
|
|
297
|
+
for (let c = col; c < col + cs; c++) {
|
|
298
|
+
if (occupied[r]![c]) return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function markOccupied(row: number, col: number, cs: number, rs: number) {
|
|
305
|
+
for (let r = row; r < row + rs; r++) {
|
|
306
|
+
ensureRow(r);
|
|
307
|
+
for (let c = col; c < col + cs; c++) {
|
|
308
|
+
occupied[r]![c] = true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
maxRow = Math.max(maxRow, row + rs - 1);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
for (let i = 0; i < count; i++) {
|
|
315
|
+
const span = getSpan(i);
|
|
316
|
+
const cs = Math.max(1, Math.min(span.colSpan, numColumns));
|
|
317
|
+
const rs = Math.max(1, span.rowSpan);
|
|
318
|
+
let placed = false;
|
|
319
|
+
for (let r = 0; !placed; r++) {
|
|
320
|
+
ensureRow(r);
|
|
321
|
+
for (let c = 0; c <= numColumns - cs; c++) {
|
|
322
|
+
if (isAvailable(r, c, cs, rs)) {
|
|
323
|
+
markOccupied(r, c, cs, rs);
|
|
324
|
+
positions.push({ row: r, col: c });
|
|
325
|
+
placed = true;
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return { positions, totalRows: count > 0 ? maxRow + 1 : 0 };
|
|
333
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -39,6 +39,14 @@ export interface ViewDimensions {
|
|
|
39
39
|
height: number;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/** Grid span for a sortable item (columns and rows it occupies) */
|
|
43
|
+
export interface GridItemSpan {
|
|
44
|
+
/** Number of columns this item spans. @default 1 */
|
|
45
|
+
colSpan: number;
|
|
46
|
+
/** Number of rows this item spans. @default 1 */
|
|
47
|
+
rowSpan: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
/** Measurements of a Drax view for bounds checking purposes */
|
|
43
51
|
export interface DraxViewMeasurements extends Position, ViewDimensions {
|
|
44
52
|
/** 1 when DraxView auto-detected transform-based positioning
|
|
@@ -724,6 +732,10 @@ export interface UseSortableListOptions<T> {
|
|
|
724
732
|
autoScrollForwardThreshold?: number;
|
|
725
733
|
/** Animation config for item shift animations. @default 'default' */
|
|
726
734
|
animationConfig?: SortableAnimationConfig;
|
|
735
|
+
/** Returns the grid span for an item. Enables non-uniform grid layout
|
|
736
|
+
* where items can span multiple columns and/or rows.
|
|
737
|
+
* Only used when numColumns > 1. */
|
|
738
|
+
getItemSpan?: (item: T, index: number) => GridItemSpan;
|
|
727
739
|
/** Style applied to all non-dragged items while a drag is active.
|
|
728
740
|
* Use for dimming/scaling inactive items (e.g., `{ opacity: 0.5 }`). */
|
|
729
741
|
inactiveItemStyle?: ViewStyle;
|
|
@@ -762,6 +774,8 @@ export interface SortableListInternal<T> {
|
|
|
762
774
|
longPressDelay: number;
|
|
763
775
|
lockToMainAxis: boolean;
|
|
764
776
|
animationConfig: SortableAnimationConfig;
|
|
777
|
+
/** Returns the grid span for an item (non-uniform grid layout) */
|
|
778
|
+
getItemSpan?: (item: T, index: number) => GridItemSpan;
|
|
765
779
|
inactiveItemStyle?: ViewStyle;
|
|
766
780
|
itemEntering?: EntryOrExitLayoutType;
|
|
767
781
|
itemExiting?: EntryOrExitLayoutType;
|