react-native-swappable-grid 1.0.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.
Files changed (44) hide show
  1. package/README.md +284 -0
  2. package/lib/commonjs/ChildWrapper.js +320 -0
  3. package/lib/commonjs/ChildWrapper.js.map +1 -0
  4. package/lib/commonjs/SwappableGrid.js +378 -0
  5. package/lib/commonjs/SwappableGrid.js.map +1 -0
  6. package/lib/commonjs/index.js +14 -0
  7. package/lib/commonjs/index.js.map +1 -0
  8. package/lib/commonjs/utils/helpers/computerMinHeight.js +11 -0
  9. package/lib/commonjs/utils/helpers/computerMinHeight.js.map +1 -0
  10. package/lib/commonjs/utils/helpers/gestures/PanWithLongPress.js +249 -0
  11. package/lib/commonjs/utils/helpers/gestures/PanWithLongPress.js.map +1 -0
  12. package/lib/commonjs/utils/helpers/indexCalculations.js +73 -0
  13. package/lib/commonjs/utils/helpers/indexCalculations.js.map +1 -0
  14. package/lib/commonjs/utils/useGridLayout.js +205 -0
  15. package/lib/commonjs/utils/useGridLayout.js.map +1 -0
  16. package/lib/module/ChildWrapper.js +313 -0
  17. package/lib/module/ChildWrapper.js.map +1 -0
  18. package/lib/module/SwappableGrid.js +370 -0
  19. package/lib/module/SwappableGrid.js.map +1 -0
  20. package/lib/module/index.js +2 -0
  21. package/lib/module/index.js.map +1 -0
  22. package/lib/module/utils/helpers/computerMinHeight.js +5 -0
  23. package/lib/module/utils/helpers/computerMinHeight.js.map +1 -0
  24. package/lib/module/utils/helpers/gestures/PanWithLongPress.js +242 -0
  25. package/lib/module/utils/helpers/gestures/PanWithLongPress.js.map +1 -0
  26. package/lib/module/utils/helpers/indexCalculations.js +64 -0
  27. package/lib/module/utils/helpers/indexCalculations.js.map +1 -0
  28. package/lib/module/utils/useGridLayout.js +199 -0
  29. package/lib/module/utils/useGridLayout.js.map +1 -0
  30. package/lib/typescript/ChildWrapper.d.ts +23 -0
  31. package/lib/typescript/SwappableGrid.d.ts +85 -0
  32. package/lib/typescript/index.d.ts +2 -0
  33. package/lib/typescript/utils/helpers/computerMinHeight.d.ts +1 -0
  34. package/lib/typescript/utils/helpers/gestures/PanWithLongPress.d.ts +40 -0
  35. package/lib/typescript/utils/helpers/indexCalculations.d.ts +28 -0
  36. package/lib/typescript/utils/useGridLayout.d.ts +46 -0
  37. package/package.json +68 -0
  38. package/src/ChildWrapper.tsx +376 -0
  39. package/src/SwappableGrid.tsx +492 -0
  40. package/src/index.ts +2 -0
  41. package/src/utils/helpers/computerMinHeight.ts +9 -0
  42. package/src/utils/helpers/gestures/PanWithLongPress.ts +304 -0
  43. package/src/utils/helpers/indexCalculations.ts +91 -0
  44. package/src/utils/useGridLayout.ts +236 -0
@@ -0,0 +1,378 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
10
+ var _reactNativeGestureHandler = require("react-native-gesture-handler");
11
+ var _computerMinHeight = _interopRequireDefault(require("./utils/helpers/computerMinHeight"));
12
+ var _useGridLayout = require("./utils/useGridLayout");
13
+ var _ChildWrapper = _interopRequireDefault(require("./ChildWrapper"));
14
+ var _indexCalculations = require("./utils/helpers/indexCalculations");
15
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
17
+ const AnimatedScrollView = _reactNativeReanimated.default.createAnimatedComponent(_reactNative.ScrollView);
18
+ const normalizeKey = k => String(k).replace(/^\.\$/, "");
19
+
20
+ /**
21
+ * Props for the SwappableGrid component
22
+ */
23
+
24
+ /**
25
+ * Ref methods for SwappableGrid component
26
+ */
27
+
28
+ /**
29
+ * SwappableGrid - A React Native component for creating a draggable, swappable grid layout.
30
+ *
31
+ * Features:
32
+ * - Drag and drop to reorder items
33
+ * - Long press to enter drag mode
34
+ * - Auto-scroll when dragging near edges
35
+ * - Optional wiggle animation during drag mode
36
+ * - Optional delete functionality with hold-to-delete or delete component
37
+ * - Support for trailing components (e.g., "Add" button)
38
+ * - Automatic column calculation based on container width
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * <SwappableGrid
43
+ * itemWidth={100}
44
+ * itemHeight={100}
45
+ * numColumns={3}
46
+ * onOrderChange={(keys) => console.log('New order:', keys)}
47
+ * >
48
+ * {items.map(item => (
49
+ * <View key={item.id}>{item.content}</View>
50
+ * ))}
51
+ * </SwappableGrid>
52
+ * ```
53
+ */
54
+ const SwappableGrid = /*#__PURE__*/(0, _react.forwardRef)(({
55
+ children,
56
+ itemWidth,
57
+ itemHeight,
58
+ gap = 8,
59
+ containerPadding = 8,
60
+ longPressMs = 300,
61
+ numColumns,
62
+ onDragEnd,
63
+ onOrderChange,
64
+ onDelete,
65
+ wiggle,
66
+ style,
67
+ dragSizeIncreaseFactor = 1.06,
68
+ scrollThreshold = 100,
69
+ scrollSpeed = 10,
70
+ trailingComponent,
71
+ deleteComponent,
72
+ deleteComponentStyle,
73
+ reverse = false
74
+ }, ref) => {
75
+ // MUST be Animated ref for scrollTo
76
+ const scrollViewRef = (0, _reactNativeReanimated.useAnimatedRef)();
77
+ const [showTrailingComponent, setShowTrailingComponent] = (0, _react.useState)(false);
78
+ const [showDeleteComponent, setShowDeleteComponent] = (0, _react.useState)(false);
79
+ const [isDragging, setIsDragging] = (0, _react.useState)(false);
80
+ const [currentNumColumns, setCurrentNumColumns] = (0, _react.useState)(numColumns || 1);
81
+ const touchEndTimeoutRef = _react.default.useRef(null);
82
+ const deleteComponentRef = _react.default.useRef(null);
83
+ (0, _react.useEffect)(() => {
84
+ setTimeout(() => {
85
+ setShowTrailingComponent(true);
86
+ setShowDeleteComponent(true);
87
+ }, 50); // This is kinda a hack that makes the trailing component render in the correct position because it lets the other components render first to make the position calculation correct.
88
+ }, []);
89
+ const handleOnOrderChange = order => {
90
+ if (onOrderChange) onOrderChange(order.map(key => normalizeKey(key)));
91
+ };
92
+
93
+ // Extract paddingBottom from style prop to allow dragging into padding area
94
+ const paddingBottom = _react.default.useMemo(() => {
95
+ if (!style) return 0;
96
+ const styleObj = _reactNative.StyleSheet.flatten(style);
97
+ return styleObj.paddingBottom || 0;
98
+ }, [style]);
99
+ const {
100
+ orderState,
101
+ composed,
102
+ dynamicNumColumns,
103
+ onLayoutContent: originalOnLayoutContent,
104
+ // layout of the inner content view (for width/cols)
105
+ onLayoutScrollView,
106
+ // layout of the scroll viewport (for height)
107
+ onScroll,
108
+ deleteItem,
109
+ childArray,
110
+ positions,
111
+ dragMode,
112
+ anyItemInDeleteMode,
113
+ order,
114
+ deleteComponentPosition
115
+ } = (0, _useGridLayout.useGridLayout)({
116
+ reverse,
117
+ children,
118
+ longPressMs,
119
+ itemWidth,
120
+ itemHeight,
121
+ gap,
122
+ containerPadding,
123
+ numColumns,
124
+ onDragEnd,
125
+ onOrderChange: handleOnOrderChange,
126
+ onDelete: onDelete ? key => onDelete(normalizeKey(key)) : undefined,
127
+ scrollViewRef,
128
+ scrollSpeed,
129
+ scrollThreshold,
130
+ contentPaddingBottom: paddingBottom
131
+ });
132
+
133
+ // Track numColumns changes for height calculation
134
+ const onLayoutContent = e => {
135
+ originalOnLayoutContent(e);
136
+ // Update currentNumColumns for height calculation
137
+ if (numColumns) {
138
+ setCurrentNumColumns(numColumns);
139
+ } else {
140
+ const possibleCols = Math.floor((e.nativeEvent.layout.width - containerPadding * 2 + gap) / (itemWidth + gap));
141
+ setCurrentNumColumns(Math.max(1, possibleCols));
142
+ }
143
+ };
144
+
145
+ // Track drag state to show/hide delete component
146
+ (0, _reactNativeReanimated.useAnimatedReaction)(() => dragMode.value, isDraggingValue => {
147
+ (0, _reactNativeReanimated.runOnJS)(setIsDragging)(isDraggingValue);
148
+ });
149
+
150
+ // Expose cancel delete mode function to parent
151
+ (0, _react.useImperativeHandle)(ref, () => ({
152
+ cancelDeleteMode: () => {
153
+ if (anyItemInDeleteMode.value) {
154
+ anyItemInDeleteMode.value = false;
155
+ }
156
+ }
157
+ }), [anyItemInDeleteMode]);
158
+ const trailingX = (0, _reactNativeReanimated.useDerivedValue)(() => {
159
+ const {
160
+ x
161
+ } = (0, _indexCalculations.indexToXY)({
162
+ index: order.value.length,
163
+ // AFTER last swappable
164
+ itemWidth,
165
+ itemHeight,
166
+ dynamicNumColumns,
167
+ containerPadding,
168
+ gap
169
+ });
170
+ return x;
171
+ });
172
+ const trailingY = (0, _reactNativeReanimated.useDerivedValue)(() => {
173
+ const {
174
+ y
175
+ } = (0, _indexCalculations.indexToXY)({
176
+ index: order.value.length,
177
+ itemWidth,
178
+ itemHeight,
179
+ dynamicNumColumns,
180
+ containerPadding,
181
+ gap
182
+ });
183
+ return y;
184
+ });
185
+ const trailingStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => ({
186
+ left: trailingX.value,
187
+ top: trailingY.value
188
+ }));
189
+
190
+ // Calculate default delete component position (bottom center)
191
+ const deleteComponentX = (0, _reactNativeReanimated.useDerivedValue)(() => {
192
+ if (deleteComponentStyle) {
193
+ // If custom style provided, position will be set by user
194
+ return 0;
195
+ }
196
+ // Default: center horizontally
197
+ const cols = dynamicNumColumns.value;
198
+ const totalWidth = cols * itemWidth + (cols - 1) * gap + containerPadding * 2;
199
+ return (totalWidth - itemWidth) / 2;
200
+ });
201
+ const deleteComponentY = (0, _reactNativeReanimated.useDerivedValue)(() => {
202
+ if (deleteComponentStyle) {
203
+ // If custom style provided, position will be set by user
204
+ return 0;
205
+ }
206
+ // Default: bottom of grid (after all items)
207
+ // Account for trailing component if it exists
208
+ const rows = Math.ceil(order.value.length / dynamicNumColumns.value);
209
+ const baseY = containerPadding + rows * (itemHeight + gap);
210
+
211
+ // If trailing component exists, add extra space so delete component appears below it
212
+ if (trailingComponent && showTrailingComponent) {
213
+ return baseY + (itemHeight + gap);
214
+ }
215
+ return baseY + gap;
216
+ });
217
+ const deleteComponentStyleAnimated = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
218
+ const baseStyle = {
219
+ position: "absolute",
220
+ width: itemWidth,
221
+ height: itemHeight
222
+ };
223
+
224
+ // Use custom position if provided, otherwise use default
225
+ if (deleteComponentStyle) {
226
+ return baseStyle;
227
+ }
228
+ return {
229
+ ...baseStyle,
230
+ left: deleteComponentX.value,
231
+ top: deleteComponentY.value
232
+ };
233
+ });
234
+
235
+ // Update delete component position for drop detection when default position changes
236
+ (0, _reactNativeReanimated.useDerivedValue)(() => {
237
+ if (deleteComponent && deleteComponentPosition && !deleteComponentStyle) {
238
+ // Only update if using default position (custom style uses onLayout)
239
+ deleteComponentPosition.value = {
240
+ x: deleteComponentX.value,
241
+ y: deleteComponentY.value,
242
+ width: itemWidth,
243
+ height: itemHeight
244
+ };
245
+ }
246
+ });
247
+ const showTrailing = !!(trailingComponent && showTrailingComponent);
248
+ const showDelete = !!(deleteComponent && showDeleteComponent);
249
+ // Make sure we calculate room for both trailing and delete components
250
+ // Trailing component is part of the grid, delete component is positioned below
251
+ const itemsCountForHeight = orderState.length + (showTrailing ? 1 : 0);
252
+
253
+ // Calculate minimum height needed (on JS thread since computeMinHeight is not a worklet)
254
+ const baseHeight = (0, _computerMinHeight.default)(itemsCountForHeight, currentNumColumns, itemHeight + gap, containerPadding);
255
+
256
+ // If delete component is shown and using default position, add extra space for it
257
+ let calculatedHeight = baseHeight;
258
+ if (showDelete && !deleteComponentStyle) {
259
+ // Account for trailing component when calculating rows (same logic as deleteComponentY)
260
+ const totalItems = orderState.length + (showTrailing ? 1 : 0);
261
+ const rows = Math.ceil(totalItems / currentNumColumns);
262
+ const baseY = containerPadding + rows * (itemHeight + gap);
263
+
264
+ // If trailing component exists, add extra space so delete component appears below it
265
+ let deleteComponentY = baseY;
266
+ if (showTrailing) {
267
+ deleteComponentY = baseY + (itemHeight + gap);
268
+ } else {
269
+ deleteComponentY = baseY + gap;
270
+ }
271
+ const deleteComponentBottom = deleteComponentY + itemHeight;
272
+ // Ensure container is tall enough to show the delete component
273
+ calculatedHeight = Math.max(baseHeight, deleteComponentBottom + containerPadding);
274
+ }
275
+ return /*#__PURE__*/_react.default.createElement(AnimatedScrollView, {
276
+ ref: scrollViewRef,
277
+ onScroll: onScroll,
278
+ onLayout: onLayoutScrollView // viewport height comes from the SCROLLVIEW
279
+ ,
280
+ scrollEventThrottle: 16,
281
+ contentContainerStyle: [style],
282
+ onTouchEnd: () => {
283
+ // Cancel delete mode when user touches outside items
284
+ // Add a small delay to avoid canceling when user taps on items
285
+ // (items might briefly activate, which would prevent cancellation)
286
+ if (touchEndTimeoutRef.current) {
287
+ clearTimeout(touchEndTimeoutRef.current);
288
+ }
289
+ touchEndTimeoutRef.current = setTimeout(() => {
290
+ // Only cancel if still in delete mode and not dragging
291
+ // This ensures we don't cancel when user is interacting with items
292
+ if (anyItemInDeleteMode.value && !dragMode.value) {
293
+ anyItemInDeleteMode.value = false;
294
+ }
295
+ }, 100); // Small delay to let item interactions complete
296
+ }
297
+ }, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
298
+ style: [styles.container, {
299
+ padding: containerPadding,
300
+ height: calculatedHeight
301
+ }],
302
+ onLayout: onLayoutContent
303
+ }, /*#__PURE__*/_react.default.createElement(_reactNativeGestureHandler.GestureDetector, {
304
+ gesture: composed
305
+ }, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
306
+ pointerEvents: "box-none",
307
+ style: _reactNative.StyleSheet.absoluteFill
308
+ }, orderState.map(key => {
309
+ const child = childArray.find(c => c.key && normalizeKey(c.key) === normalizeKey(key));
310
+ if (!child) return null;
311
+ return /*#__PURE__*/_react.default.createElement(_ChildWrapper.default, {
312
+ key: key,
313
+ position: positions[key],
314
+ itemWidth: itemWidth,
315
+ itemHeight: itemHeight,
316
+ dragMode: dragMode,
317
+ anyItemInDeleteMode: anyItemInDeleteMode,
318
+ wiggle: wiggle,
319
+ dragSizeIncreaseFactor: dragSizeIncreaseFactor,
320
+ disableHoldToDelete: !!deleteComponent,
321
+ onDelete: () => {
322
+ deleteItem(key);
323
+ if (onDelete) {
324
+ onDelete(normalizeKey(key));
325
+ }
326
+ }
327
+ }, child);
328
+ }))), trailingComponent && showTrailingComponent && /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
329
+ pointerEvents: "box-none",
330
+ collapsable: false,
331
+ style: [{
332
+ position: "absolute",
333
+ width: itemWidth,
334
+ height: itemHeight
335
+ }, trailingStyle // 👈 left/top from UI thread
336
+ ]
337
+ }, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
338
+ pointerEvents: "auto",
339
+ style: {
340
+ flex: 1
341
+ }
342
+ }, trailingComponent)), deleteComponent && showDeleteComponent && isDragging && /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
343
+ ref: deleteComponentRef,
344
+ pointerEvents: "box-none",
345
+ collapsable: false,
346
+ style: [deleteComponentStyleAnimated, deleteComponentStyle // User can override position with custom style
347
+ ],
348
+ onLayout: e => {
349
+ // Update position for drop detection
350
+ const {
351
+ x,
352
+ y,
353
+ width,
354
+ height
355
+ } = e.nativeEvent.layout;
356
+ if (deleteComponentPosition) {
357
+ deleteComponentPosition.value = {
358
+ x,
359
+ y,
360
+ width,
361
+ height
362
+ };
363
+ }
364
+ }
365
+ }, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
366
+ pointerEvents: "auto",
367
+ style: {
368
+ flex: 1
369
+ }
370
+ }, deleteComponent))));
371
+ });
372
+ const styles = _reactNative.StyleSheet.create({
373
+ container: {
374
+ width: "100%"
375
+ }
376
+ });
377
+ var _default = exports.default = SwappableGrid;
378
+ //# sourceMappingURL=SwappableGrid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_reactNativeGestureHandler","_computerMinHeight","_interopRequireDefault","_useGridLayout","_ChildWrapper","_indexCalculations","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","AnimatedScrollView","Animated","createAnimatedComponent","ScrollView","normalizeKey","k","String","replace","SwappableGrid","forwardRef","children","itemWidth","itemHeight","gap","containerPadding","longPressMs","numColumns","onDragEnd","onOrderChange","onDelete","wiggle","style","dragSizeIncreaseFactor","scrollThreshold","scrollSpeed","trailingComponent","deleteComponent","deleteComponentStyle","reverse","ref","scrollViewRef","useAnimatedRef","showTrailingComponent","setShowTrailingComponent","useState","showDeleteComponent","setShowDeleteComponent","isDragging","setIsDragging","currentNumColumns","setCurrentNumColumns","touchEndTimeoutRef","React","useRef","deleteComponentRef","useEffect","setTimeout","handleOnOrderChange","order","map","key","paddingBottom","useMemo","styleObj","StyleSheet","flatten","orderState","composed","dynamicNumColumns","onLayoutContent","originalOnLayoutContent","onLayoutScrollView","onScroll","deleteItem","childArray","positions","dragMode","anyItemInDeleteMode","deleteComponentPosition","useGridLayout","undefined","contentPaddingBottom","possibleCols","Math","floor","nativeEvent","layout","width","max","useAnimatedReaction","value","isDraggingValue","runOnJS","useImperativeHandle","cancelDeleteMode","trailingX","useDerivedValue","x","indexToXY","index","length","trailingY","y","trailingStyle","useAnimatedStyle","left","top","deleteComponentX","cols","totalWidth","deleteComponentY","rows","ceil","baseY","deleteComponentStyleAnimated","baseStyle","position","height","showTrailing","showDelete","itemsCountForHeight","baseHeight","computeMinHeight","calculatedHeight","totalItems","deleteComponentBottom","createElement","onLayout","scrollEventThrottle","contentContainerStyle","onTouchEnd","current","clearTimeout","View","styles","container","padding","GestureDetector","gesture","pointerEvents","absoluteFill","child","find","c","disableHoldToDelete","collapsable","flex","create","_default","exports"],"sources":["SwappableGrid.tsx"],"sourcesContent":["import React, {\n ReactNode,\n useEffect,\n useState,\n useImperativeHandle,\n forwardRef,\n} from \"react\";\nimport {\n View,\n StyleSheet,\n ScrollView,\n StyleProp,\n ViewStyle,\n LayoutChangeEvent,\n} from \"react-native\";\nimport Animated, {\n useAnimatedRef,\n useAnimatedStyle,\n useDerivedValue,\n useAnimatedReaction,\n runOnJS,\n} from \"react-native-reanimated\";\nimport { GestureDetector } from \"react-native-gesture-handler\";\nimport computeMinHeight from \"./utils/helpers/computerMinHeight\";\nimport { useGridLayout } from \"./utils/useGridLayout\";\nimport ChildWrapper from \"./ChildWrapper\";\nimport { indexToXY } from \"./utils/helpers/indexCalculations\";\n\nconst AnimatedScrollView = Animated.createAnimatedComponent(ScrollView);\n\nconst normalizeKey = (k: React.Key) => String(k).replace(/^\\.\\$/, \"\");\n\n/**\n * Props for the SwappableGrid component\n */\ntype SwappableGridProps = {\n /** The child components to render in the grid. Each child should have a unique key. */\n children: ReactNode;\n /** Width of each grid item in pixels */\n itemWidth: number;\n /** Height of each grid item in pixels */\n itemHeight: number;\n /** Gap between grid items in pixels. Defaults to 8. */\n gap?: number;\n /** Padding around the container in pixels. Defaults to 8. */\n containerPadding?: number;\n /** Duration in milliseconds to hold before drag starts. Defaults to 300. */\n longPressMs?: number;\n /** Number of columns in the grid. If not provided, will be calculated automatically based on container width. */\n numColumns?: number;\n /** Wiggle animation configuration when items are in drag mode or delete mode */\n wiggle?: {\n /** Duration of one wiggle cycle in milliseconds */\n duration: number;\n /** Rotation degrees for the wiggle animation */\n degrees: number;\n };\n /** Callback fired when drag ends, providing the ordered array of child nodes */\n onDragEnd?: (ordered: ChildNode[]) => void;\n /** Callback fired when the order changes, providing an array of keys in the new order */\n onOrderChange?: (keys: string[]) => void;\n /** Callback fired when an item is deleted, providing the key of the deleted item */\n onDelete?: (key: string) => void;\n /** Factor by which the dragged item scales up. Defaults to 1.06. */\n dragSizeIncreaseFactor?: number;\n /** Speed of auto-scrolling when dragging near edges. Defaults to 10. */\n scrollSpeed?: number;\n /** Distance from edge in pixels that triggers auto-scroll. Defaults to 100. */\n scrollThreshold?: number;\n /** Custom style for the ScrollView container */\n style?: StyleProp<ViewStyle>;\n /** Component to render after all grid items (e.g., an \"Add\" button) */\n trailingComponent?: ReactNode;\n /** Component to render as a delete target (shown when dragging). If provided, disables hold-to-delete feature. */\n deleteComponent?: ReactNode;\n /** Custom style for the delete component. If provided, allows custom positioning. */\n deleteComponentStyle?: StyleProp<ViewStyle>;\n /** If true, reverses the order of items (right-to-left, bottom-to-top). Defaults to false. */\n reverse?: boolean;\n};\n\n/**\n * Ref methods for SwappableGrid component\n */\nexport interface SwappableGridRef {\n /** Cancels the delete mode if any item is currently in delete mode */\n cancelDeleteMode: () => void;\n}\n\n/**\n * SwappableGrid - A React Native component for creating a draggable, swappable grid layout.\n *\n * Features:\n * - Drag and drop to reorder items\n * - Long press to enter drag mode\n * - Auto-scroll when dragging near edges\n * - Optional wiggle animation during drag mode\n * - Optional delete functionality with hold-to-delete or delete component\n * - Support for trailing components (e.g., \"Add\" button)\n * - Automatic column calculation based on container width\n *\n * @example\n * ```tsx\n * <SwappableGrid\n * itemWidth={100}\n * itemHeight={100}\n * numColumns={3}\n * onOrderChange={(keys) => console.log('New order:', keys)}\n * >\n * {items.map(item => (\n * <View key={item.id}>{item.content}</View>\n * ))}\n * </SwappableGrid>\n * ```\n */\nconst SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(\n (\n {\n children,\n itemWidth,\n itemHeight,\n gap = 8,\n containerPadding = 8,\n longPressMs = 300,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n wiggle,\n style,\n dragSizeIncreaseFactor = 1.06,\n scrollThreshold = 100,\n scrollSpeed = 10,\n trailingComponent,\n deleteComponent,\n deleteComponentStyle,\n reverse = false,\n },\n ref\n ) => {\n // MUST be Animated ref for scrollTo\n const scrollViewRef = useAnimatedRef<Animated.ScrollView>();\n const [showTrailingComponent, setShowTrailingComponent] =\n useState<boolean>(false);\n const [showDeleteComponent, setShowDeleteComponent] =\n useState<boolean>(false);\n const [isDragging, setIsDragging] = useState<boolean>(false);\n const [currentNumColumns, setCurrentNumColumns] = useState<number>(\n numColumns || 1\n );\n const touchEndTimeoutRef = React.useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n const deleteComponentRef = React.useRef<View>(null);\n\n useEffect(() => {\n setTimeout(() => {\n setShowTrailingComponent(true);\n setShowDeleteComponent(true);\n }, 50); // This is kinda a hack that makes the trailing component render in the correct position because it lets the other components render first to make the position calculation correct.\n }, []);\n\n const handleOnOrderChange = (order: string[]) => {\n if (onOrderChange) onOrderChange(order.map((key) => normalizeKey(key)));\n };\n\n // Extract paddingBottom from style prop to allow dragging into padding area\n const paddingBottom = React.useMemo(() => {\n if (!style) return 0;\n const styleObj = StyleSheet.flatten(style);\n return (styleObj.paddingBottom as number) || 0;\n }, [style]);\n\n const {\n orderState,\n composed,\n dynamicNumColumns,\n onLayoutContent: originalOnLayoutContent, // layout of the inner content view (for width/cols)\n onLayoutScrollView, // layout of the scroll viewport (for height)\n onScroll,\n deleteItem,\n childArray,\n positions,\n dragMode,\n anyItemInDeleteMode,\n order,\n deleteComponentPosition,\n } = useGridLayout({\n reverse,\n children,\n longPressMs,\n itemWidth,\n itemHeight,\n gap,\n containerPadding,\n numColumns,\n onDragEnd,\n onOrderChange: handleOnOrderChange,\n onDelete: onDelete ? (key) => onDelete(normalizeKey(key)) : undefined,\n scrollViewRef,\n scrollSpeed,\n scrollThreshold,\n contentPaddingBottom: paddingBottom,\n });\n\n // Track numColumns changes for height calculation\n const onLayoutContent = (e: LayoutChangeEvent) => {\n originalOnLayoutContent(e);\n // Update currentNumColumns for height calculation\n if (numColumns) {\n setCurrentNumColumns(numColumns);\n } else {\n const possibleCols = Math.floor(\n (e.nativeEvent.layout.width - containerPadding * 2 + gap) /\n (itemWidth + gap)\n );\n setCurrentNumColumns(Math.max(1, possibleCols));\n }\n };\n\n // Track drag state to show/hide delete component\n useAnimatedReaction(\n () => dragMode.value,\n (isDraggingValue) => {\n runOnJS(setIsDragging)(isDraggingValue);\n }\n );\n\n // Expose cancel delete mode function to parent\n useImperativeHandle(\n ref,\n () => ({\n cancelDeleteMode: () => {\n if (anyItemInDeleteMode.value) {\n anyItemInDeleteMode.value = false;\n }\n },\n }),\n [anyItemInDeleteMode]\n );\n\n const trailingX = useDerivedValue(() => {\n const { x } = indexToXY({\n index: order.value.length, // AFTER last swappable\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return x;\n });\n\n const trailingY = useDerivedValue(() => {\n const { y } = indexToXY({\n index: order.value.length,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return y;\n });\n\n const trailingStyle = useAnimatedStyle(() => ({\n left: trailingX.value,\n top: trailingY.value,\n }));\n\n // Calculate default delete component position (bottom center)\n const deleteComponentX = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: center horizontally\n const cols = dynamicNumColumns.value;\n const totalWidth =\n cols * itemWidth + (cols - 1) * gap + containerPadding * 2;\n return (totalWidth - itemWidth) / 2;\n });\n\n const deleteComponentY = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: bottom of grid (after all items)\n // Account for trailing component if it exists\n const rows = Math.ceil(order.value.length / dynamicNumColumns.value);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n if (trailingComponent && showTrailingComponent) {\n return baseY + (itemHeight + gap);\n }\n\n return baseY + gap;\n });\n\n const deleteComponentStyleAnimated = useAnimatedStyle(() => {\n const baseStyle: any = {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n };\n\n // Use custom position if provided, otherwise use default\n if (deleteComponentStyle) {\n return baseStyle;\n }\n\n return {\n ...baseStyle,\n left: deleteComponentX.value,\n top: deleteComponentY.value,\n };\n });\n\n // Update delete component position for drop detection when default position changes\n useDerivedValue(() => {\n if (deleteComponent && deleteComponentPosition && !deleteComponentStyle) {\n // Only update if using default position (custom style uses onLayout)\n deleteComponentPosition.value = {\n x: deleteComponentX.value,\n y: deleteComponentY.value,\n width: itemWidth,\n height: itemHeight,\n };\n }\n });\n\n const showTrailing = !!(trailingComponent && showTrailingComponent);\n const showDelete = !!(deleteComponent && showDeleteComponent);\n // Make sure we calculate room for both trailing and delete components\n // Trailing component is part of the grid, delete component is positioned below\n const itemsCountForHeight = orderState.length + (showTrailing ? 1 : 0);\n\n // Calculate minimum height needed (on JS thread since computeMinHeight is not a worklet)\n const baseHeight = computeMinHeight(\n itemsCountForHeight,\n currentNumColumns,\n itemHeight + gap,\n containerPadding\n );\n\n // If delete component is shown and using default position, add extra space for it\n let calculatedHeight = baseHeight;\n if (showDelete && !deleteComponentStyle) {\n // Account for trailing component when calculating rows (same logic as deleteComponentY)\n const totalItems = orderState.length + (showTrailing ? 1 : 0);\n const rows = Math.ceil(totalItems / currentNumColumns);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n let deleteComponentY = baseY;\n if (showTrailing) {\n deleteComponentY = baseY + (itemHeight + gap);\n } else {\n deleteComponentY = baseY + gap;\n }\n\n const deleteComponentBottom = deleteComponentY + itemHeight;\n // Ensure container is tall enough to show the delete component\n calculatedHeight = Math.max(\n baseHeight,\n deleteComponentBottom + containerPadding\n );\n }\n\n return (\n <AnimatedScrollView\n ref={scrollViewRef}\n onScroll={onScroll}\n onLayout={onLayoutScrollView} // viewport height comes from the SCROLLVIEW\n scrollEventThrottle={16}\n contentContainerStyle={[style]}\n onTouchEnd={() => {\n // Cancel delete mode when user touches outside items\n // Add a small delay to avoid canceling when user taps on items\n // (items might briefly activate, which would prevent cancellation)\n if (touchEndTimeoutRef.current) {\n clearTimeout(touchEndTimeoutRef.current);\n }\n touchEndTimeoutRef.current = setTimeout(() => {\n // Only cancel if still in delete mode and not dragging\n // This ensures we don't cancel when user is interacting with items\n if (anyItemInDeleteMode.value && !dragMode.value) {\n anyItemInDeleteMode.value = false;\n }\n }, 100); // Small delay to let item interactions complete\n }}\n >\n <View\n style={[\n styles.container,\n {\n padding: containerPadding,\n height: calculatedHeight,\n },\n ]}\n onLayout={onLayoutContent}\n >\n <GestureDetector gesture={composed}>\n <View pointerEvents=\"box-none\" style={StyleSheet.absoluteFill}>\n {orderState.map((key) => {\n const child = childArray.find(\n (c) => c.key && normalizeKey(c.key) === normalizeKey(key)\n );\n if (!child) return null;\n return (\n <ChildWrapper\n key={key}\n position={positions[key]}\n itemWidth={itemWidth}\n itemHeight={itemHeight}\n dragMode={dragMode}\n anyItemInDeleteMode={anyItemInDeleteMode}\n wiggle={wiggle}\n dragSizeIncreaseFactor={dragSizeIncreaseFactor}\n disableHoldToDelete={!!deleteComponent}\n onDelete={() => {\n deleteItem(key);\n if (onDelete) {\n onDelete(normalizeKey(key));\n }\n }}\n >\n {child}\n </ChildWrapper>\n );\n })}\n </View>\n </GestureDetector>\n\n {/* Trailing rendered OUTSIDE the GestureDetector */}\n {trailingComponent && showTrailingComponent && (\n <Animated.View\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n },\n trailingStyle, // 👈 left/top from UI thread\n ]}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {trailingComponent}\n </View>\n </Animated.View>\n )}\n\n {/* Delete component rendered OUTSIDE the GestureDetector - only show when dragging */}\n {deleteComponent && showDeleteComponent && isDragging && (\n <Animated.View\n ref={deleteComponentRef}\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n deleteComponentStyleAnimated,\n deleteComponentStyle, // User can override position with custom style\n ]}\n onLayout={(e) => {\n // Update position for drop detection\n const { x, y, width, height } = e.nativeEvent.layout;\n if (deleteComponentPosition) {\n deleteComponentPosition.value = { x, y, width, height };\n }\n }}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {deleteComponent}\n </View>\n </Animated.View>\n )}\n </View>\n </AnimatedScrollView>\n );\n }\n);\n\nconst styles = StyleSheet.create({\n container: {\n width: \"100%\",\n },\n});\n\nexport default SwappableGrid;\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AAOA,IAAAC,YAAA,GAAAD,OAAA;AAQA,IAAAE,sBAAA,GAAAH,uBAAA,CAAAC,OAAA;AAOA,IAAAG,0BAAA,GAAAH,OAAA;AACA,IAAAI,kBAAA,GAAAC,sBAAA,CAAAL,OAAA;AACA,IAAAM,cAAA,GAAAN,OAAA;AACA,IAAAO,aAAA,GAAAF,sBAAA,CAAAL,OAAA;AACA,IAAAQ,kBAAA,GAAAR,OAAA;AAA8D,SAAAK,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,SAAAV,wBAAAU,CAAA,EAAAG,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAd,uBAAA,YAAAA,CAAAU,CAAA,EAAAG,CAAA,SAAAA,CAAA,IAAAH,CAAA,IAAAA,CAAA,CAAAC,UAAA,SAAAD,CAAA,MAAAO,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAR,OAAA,EAAAF,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAS,CAAA,MAAAF,CAAA,GAAAJ,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAE,CAAA,CAAAI,GAAA,CAAAX,CAAA,UAAAO,CAAA,CAAAK,GAAA,CAAAZ,CAAA,GAAAO,CAAA,CAAAM,GAAA,CAAAb,CAAA,EAAAS,CAAA,gBAAAN,CAAA,IAAAH,CAAA,gBAAAG,CAAA,OAAAW,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAG,CAAA,OAAAK,CAAA,IAAAD,CAAA,GAAAS,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAG,CAAA,OAAAK,CAAA,CAAAI,GAAA,IAAAJ,CAAA,CAAAK,GAAA,IAAAN,CAAA,CAAAE,CAAA,EAAAN,CAAA,EAAAK,CAAA,IAAAC,CAAA,CAAAN,CAAA,IAAAH,CAAA,CAAAG,CAAA,WAAAM,CAAA,KAAAT,CAAA,EAAAG,CAAA;AAE9D,MAAMgB,kBAAkB,GAAGC,8BAAQ,CAACC,uBAAuB,CAACC,uBAAU,CAAC;AAEvE,MAAMC,YAAY,GAAIC,CAAY,IAAKC,MAAM,CAACD,CAAC,CAAC,CAACE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;;AAErE;AACA;AACA;;AA+CA;AACA;AACA;;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,aAAa,gBAAG,IAAAC,iBAAU,EAC9B,CACE;EACEC,QAAQ;EACRC,SAAS;EACTC,UAAU;EACVC,GAAG,GAAG,CAAC;EACPC,gBAAgB,GAAG,CAAC;EACpBC,WAAW,GAAG,GAAG;EACjBC,UAAU;EACVC,SAAS;EACTC,aAAa;EACbC,QAAQ;EACRC,MAAM;EACNC,KAAK;EACLC,sBAAsB,GAAG,IAAI;EAC7BC,eAAe,GAAG,GAAG;EACrBC,WAAW,GAAG,EAAE;EAChBC,iBAAiB;EACjBC,eAAe;EACfC,oBAAoB;EACpBC,OAAO,GAAG;AACZ,CAAC,EACDC,GAAG,KACA;EACH;EACA,MAAMC,aAAa,GAAG,IAAAC,qCAAc,EAAsB,CAAC;EAC3D,MAAM,CAACC,qBAAqB,EAAEC,wBAAwB,CAAC,GACrD,IAAAC,eAAQ,EAAU,KAAK,CAAC;EAC1B,MAAM,CAACC,mBAAmB,EAAEC,sBAAsB,CAAC,GACjD,IAAAF,eAAQ,EAAU,KAAK,CAAC;EAC1B,MAAM,CAACG,UAAU,EAAEC,aAAa,CAAC,GAAG,IAAAJ,eAAQ,EAAU,KAAK,CAAC;EAC5D,MAAM,CAACK,iBAAiB,EAAEC,oBAAoB,CAAC,GAAG,IAAAN,eAAQ,EACxDlB,UAAU,IAAI,CAChB,CAAC;EACD,MAAMyB,kBAAkB,GAAGC,cAAK,CAACC,MAAM,CAE7B,IAAI,CAAC;EACf,MAAMC,kBAAkB,GAAGF,cAAK,CAACC,MAAM,CAAO,IAAI,CAAC;EAEnD,IAAAE,gBAAS,EAAC,MAAM;IACdC,UAAU,CAAC,MAAM;MACfb,wBAAwB,CAAC,IAAI,CAAC;MAC9BG,sBAAsB,CAAC,IAAI,CAAC;IAC9B,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;EACV,CAAC,EAAE,EAAE,CAAC;EAEN,MAAMW,mBAAmB,GAAIC,KAAe,IAAK;IAC/C,IAAI9B,aAAa,EAAEA,aAAa,CAAC8B,KAAK,CAACC,GAAG,CAAEC,GAAG,IAAK9C,YAAY,CAAC8C,GAAG,CAAC,CAAC,CAAC;EACzE,CAAC;;EAED;EACA,MAAMC,aAAa,GAAGT,cAAK,CAACU,OAAO,CAAC,MAAM;IACxC,IAAI,CAAC/B,KAAK,EAAE,OAAO,CAAC;IACpB,MAAMgC,QAAQ,GAAGC,uBAAU,CAACC,OAAO,CAAClC,KAAK,CAAC;IAC1C,OAAQgC,QAAQ,CAACF,aAAa,IAAe,CAAC;EAChD,CAAC,EAAE,CAAC9B,KAAK,CAAC,CAAC;EAEX,MAAM;IACJmC,UAAU;IACVC,QAAQ;IACRC,iBAAiB;IACjBC,eAAe,EAAEC,uBAAuB;IAAE;IAC1CC,kBAAkB;IAAE;IACpBC,QAAQ;IACRC,UAAU;IACVC,UAAU;IACVC,SAAS;IACTC,QAAQ;IACRC,mBAAmB;IACnBnB,KAAK;IACLoB;EACF,CAAC,GAAG,IAAAC,4BAAa,EAAC;IAChBzC,OAAO;IACPlB,QAAQ;IACRK,WAAW;IACXJ,SAAS;IACTC,UAAU;IACVC,GAAG;IACHC,gBAAgB;IAChBE,UAAU;IACVC,SAAS;IACTC,aAAa,EAAE6B,mBAAmB;IAClC5B,QAAQ,EAAEA,QAAQ,GAAI+B,GAAG,IAAK/B,QAAQ,CAACf,YAAY,CAAC8C,GAAG,CAAC,CAAC,GAAGoB,SAAS;IACrExC,aAAa;IACbN,WAAW;IACXD,eAAe;IACfgD,oBAAoB,EAAEpB;EACxB,CAAC,CAAC;;EAEF;EACA,MAAMQ,eAAe,GAAI9E,CAAoB,IAAK;IAChD+E,uBAAuB,CAAC/E,CAAC,CAAC;IAC1B;IACA,IAAImC,UAAU,EAAE;MACdwB,oBAAoB,CAACxB,UAAU,CAAC;IAClC,CAAC,MAAM;MACL,MAAMwD,YAAY,GAAGC,IAAI,CAACC,KAAK,CAC7B,CAAC7F,CAAC,CAAC8F,WAAW,CAACC,MAAM,CAACC,KAAK,GAAG/D,gBAAgB,GAAG,CAAC,GAAGD,GAAG,KACrDF,SAAS,GAAGE,GAAG,CACpB,CAAC;MACD2B,oBAAoB,CAACiC,IAAI,CAACK,GAAG,CAAC,CAAC,EAAEN,YAAY,CAAC,CAAC;IACjD;EACF,CAAC;;EAED;EACA,IAAAO,0CAAmB,EACjB,MAAMb,QAAQ,CAACc,KAAK,EACnBC,eAAe,IAAK;IACnB,IAAAC,8BAAO,EAAC5C,aAAa,CAAC,CAAC2C,eAAe,CAAC;EACzC,CACF,CAAC;;EAED;EACA,IAAAE,0BAAmB,EACjBtD,GAAG,EACH,OAAO;IACLuD,gBAAgB,EAAEA,CAAA,KAAM;MACtB,IAAIjB,mBAAmB,CAACa,KAAK,EAAE;QAC7Bb,mBAAmB,CAACa,KAAK,GAAG,KAAK;MACnC;IACF;EACF,CAAC,CAAC,EACF,CAACb,mBAAmB,CACtB,CAAC;EAED,MAAMkB,SAAS,GAAG,IAAAC,sCAAe,EAAC,MAAM;IACtC,MAAM;MAAEC;IAAE,CAAC,GAAG,IAAAC,4BAAS,EAAC;MACtBC,KAAK,EAAEzC,KAAK,CAACgC,KAAK,CAACU,MAAM;MAAE;MAC3B/E,SAAS;MACTC,UAAU;MACV8C,iBAAiB;MACjB5C,gBAAgB;MAChBD;IACF,CAAC,CAAC;IACF,OAAO0E,CAAC;EACV,CAAC,CAAC;EAEF,MAAMI,SAAS,GAAG,IAAAL,sCAAe,EAAC,MAAM;IACtC,MAAM;MAAEM;IAAE,CAAC,GAAG,IAAAJ,4BAAS,EAAC;MACtBC,KAAK,EAAEzC,KAAK,CAACgC,KAAK,CAACU,MAAM;MACzB/E,SAAS;MACTC,UAAU;MACV8C,iBAAiB;MACjB5C,gBAAgB;MAChBD;IACF,CAAC,CAAC;IACF,OAAO+E,CAAC;EACV,CAAC,CAAC;EAEF,MAAMC,aAAa,GAAG,IAAAC,uCAAgB,EAAC,OAAO;IAC5CC,IAAI,EAAEV,SAAS,CAACL,KAAK;IACrBgB,GAAG,EAAEL,SAAS,CAACX;EACjB,CAAC,CAAC,CAAC;;EAEH;EACA,MAAMiB,gBAAgB,GAAG,IAAAX,sCAAe,EAAC,MAAM;IAC7C,IAAI3D,oBAAoB,EAAE;MACxB;MACA,OAAO,CAAC;IACV;IACA;IACA,MAAMuE,IAAI,GAAGxC,iBAAiB,CAACsB,KAAK;IACpC,MAAMmB,UAAU,GACdD,IAAI,GAAGvF,SAAS,GAAG,CAACuF,IAAI,GAAG,CAAC,IAAIrF,GAAG,GAAGC,gBAAgB,GAAG,CAAC;IAC5D,OAAO,CAACqF,UAAU,GAAGxF,SAAS,IAAI,CAAC;EACrC,CAAC,CAAC;EAEF,MAAMyF,gBAAgB,GAAG,IAAAd,sCAAe,EAAC,MAAM;IAC7C,IAAI3D,oBAAoB,EAAE;MACxB;MACA,OAAO,CAAC;IACV;IACA;IACA;IACA,MAAM0E,IAAI,GAAG5B,IAAI,CAAC6B,IAAI,CAACtD,KAAK,CAACgC,KAAK,CAACU,MAAM,GAAGhC,iBAAiB,CAACsB,KAAK,CAAC;IACpE,MAAMuB,KAAK,GAAGzF,gBAAgB,GAAGuF,IAAI,IAAIzF,UAAU,GAAGC,GAAG,CAAC;;IAE1D;IACA,IAAIY,iBAAiB,IAAIO,qBAAqB,EAAE;MAC9C,OAAOuE,KAAK,IAAI3F,UAAU,GAAGC,GAAG,CAAC;IACnC;IAEA,OAAO0F,KAAK,GAAG1F,GAAG;EACpB,CAAC,CAAC;EAEF,MAAM2F,4BAA4B,GAAG,IAAAV,uCAAgB,EAAC,MAAM;IAC1D,MAAMW,SAAc,GAAG;MACrBC,QAAQ,EAAE,UAAU;MACpB7B,KAAK,EAAElE,SAAS;MAChBgG,MAAM,EAAE/F;IACV,CAAC;;IAED;IACA,IAAIe,oBAAoB,EAAE;MACxB,OAAO8E,SAAS;IAClB;IAEA,OAAO;MACL,GAAGA,SAAS;MACZV,IAAI,EAAEE,gBAAgB,CAACjB,KAAK;MAC5BgB,GAAG,EAAEI,gBAAgB,CAACpB;IACxB,CAAC;EACH,CAAC,CAAC;;EAEF;EACA,IAAAM,sCAAe,EAAC,MAAM;IACpB,IAAI5D,eAAe,IAAI0C,uBAAuB,IAAI,CAACzC,oBAAoB,EAAE;MACvE;MACAyC,uBAAuB,CAACY,KAAK,GAAG;QAC9BO,CAAC,EAAEU,gBAAgB,CAACjB,KAAK;QACzBY,CAAC,EAAEQ,gBAAgB,CAACpB,KAAK;QACzBH,KAAK,EAAElE,SAAS;QAChBgG,MAAM,EAAE/F;MACV,CAAC;IACH;EACF,CAAC,CAAC;EAEF,MAAMgG,YAAY,GAAG,CAAC,EAAEnF,iBAAiB,IAAIO,qBAAqB,CAAC;EACnE,MAAM6E,UAAU,GAAG,CAAC,EAAEnF,eAAe,IAAIS,mBAAmB,CAAC;EAC7D;EACA;EACA,MAAM2E,mBAAmB,GAAGtD,UAAU,CAACkC,MAAM,IAAIkB,YAAY,GAAG,CAAC,GAAG,CAAC,CAAC;;EAEtE;EACA,MAAMG,UAAU,GAAG,IAAAC,0BAAgB,EACjCF,mBAAmB,EACnBvE,iBAAiB,EACjB3B,UAAU,GAAGC,GAAG,EAChBC,gBACF,CAAC;;EAED;EACA,IAAImG,gBAAgB,GAAGF,UAAU;EACjC,IAAIF,UAAU,IAAI,CAAClF,oBAAoB,EAAE;IACvC;IACA,MAAMuF,UAAU,GAAG1D,UAAU,CAACkC,MAAM,IAAIkB,YAAY,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAMP,IAAI,GAAG5B,IAAI,CAAC6B,IAAI,CAACY,UAAU,GAAG3E,iBAAiB,CAAC;IACtD,MAAMgE,KAAK,GAAGzF,gBAAgB,GAAGuF,IAAI,IAAIzF,UAAU,GAAGC,GAAG,CAAC;;IAE1D;IACA,IAAIuF,gBAAgB,GAAGG,KAAK;IAC5B,IAAIK,YAAY,EAAE;MAChBR,gBAAgB,GAAGG,KAAK,IAAI3F,UAAU,GAAGC,GAAG,CAAC;IAC/C,CAAC,MAAM;MACLuF,gBAAgB,GAAGG,KAAK,GAAG1F,GAAG;IAChC;IAEA,MAAMsG,qBAAqB,GAAGf,gBAAgB,GAAGxF,UAAU;IAC3D;IACAqG,gBAAgB,GAAGxC,IAAI,CAACK,GAAG,CACzBiC,UAAU,EACVI,qBAAqB,GAAGrG,gBAC1B,CAAC;EACH;EAEA,oBACE5C,MAAA,CAAAa,OAAA,CAAAqI,aAAA,CAACpH,kBAAkB;IACjB6B,GAAG,EAAEC,aAAc;IACnBgC,QAAQ,EAAEA,QAAS;IACnBuD,QAAQ,EAAExD,kBAAmB,CAAC;IAAA;IAC9ByD,mBAAmB,EAAE,EAAG;IACxBC,qBAAqB,EAAE,CAAClG,KAAK,CAAE;IAC/BmG,UAAU,EAAEA,CAAA,KAAM;MAChB;MACA;MACA;MACA,IAAI/E,kBAAkB,CAACgF,OAAO,EAAE;QAC9BC,YAAY,CAACjF,kBAAkB,CAACgF,OAAO,CAAC;MAC1C;MACAhF,kBAAkB,CAACgF,OAAO,GAAG3E,UAAU,CAAC,MAAM;QAC5C;QACA;QACA,IAAIqB,mBAAmB,CAACa,KAAK,IAAI,CAACd,QAAQ,CAACc,KAAK,EAAE;UAChDb,mBAAmB,CAACa,KAAK,GAAG,KAAK;QACnC;MACF,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACX;EAAE,gBAEF9G,MAAA,CAAAa,OAAA,CAAAqI,aAAA,CAAC/I,YAAA,CAAAsJ,IAAI;IACHtG,KAAK,EAAE,CACLuG,MAAM,CAACC,SAAS,EAChB;MACEC,OAAO,EAAEhH,gBAAgB;MACzB6F,MAAM,EAAEM;IACV,CAAC,CACD;IACFI,QAAQ,EAAE1D;EAAgB,gBAE1BzF,MAAA,CAAAa,OAAA,CAAAqI,aAAA,CAAC7I,0BAAA,CAAAwJ,eAAe;IAACC,OAAO,EAAEvE;EAAS,gBACjCvF,MAAA,CAAAa,OAAA,CAAAqI,aAAA,CAAC/I,YAAA,CAAAsJ,IAAI;IAACM,aAAa,EAAC,UAAU;IAAC5G,KAAK,EAAEiC,uBAAU,CAAC4E;EAAa,GAC3D1E,UAAU,CAACP,GAAG,CAAEC,GAAG,IAAK;IACvB,MAAMiF,KAAK,GAAGnE,UAAU,CAACoE,IAAI,CAC1BC,CAAC,IAAKA,CAAC,CAACnF,GAAG,IAAI9C,YAAY,CAACiI,CAAC,CAACnF,GAAG,CAAC,KAAK9C,YAAY,CAAC8C,GAAG,CAC1D,CAAC;IACD,IAAI,CAACiF,KAAK,EAAE,OAAO,IAAI;IACvB,oBACEjK,MAAA,CAAAa,OAAA,CAAAqI,aAAA,CAACzI,aAAA,CAAAI,OAAY;MACXmE,GAAG,EAAEA,GAAI;MACTwD,QAAQ,EAAEzC,SAAS,CAACf,GAAG,CAAE;MACzBvC,SAAS,EAAEA,SAAU;MACrBC,UAAU,EAAEA,UAAW;MACvBsD,QAAQ,EAAEA,QAAS;MACnBC,mBAAmB,EAAEA,mBAAoB;MACzC/C,MAAM,EAAEA,MAAO;MACfE,sBAAsB,EAAEA,sBAAuB;MAC/CgH,mBAAmB,EAAE,CAAC,CAAC5G,eAAgB;MACvCP,QAAQ,EAAEA,CAAA,KAAM;QACd4C,UAAU,CAACb,GAAG,CAAC;QACf,IAAI/B,QAAQ,EAAE;UACZA,QAAQ,CAACf,YAAY,CAAC8C,GAAG,CAAC,CAAC;QAC7B;MACF;IAAE,GAEDiF,KACW,CAAC;EAEnB,CAAC,CACG,CACS,CAAC,EAGjB1G,iBAAiB,IAAIO,qBAAqB,iBACzC9D,MAAA,CAAAa,OAAA,CAAAqI,aAAA,CAAC9I,sBAAA,CAAAS,OAAQ,CAAC4I,IAAI;IACZM,aAAa,EAAC,UAAU;IACxBM,WAAW,EAAE,KAAM;IACnBlH,KAAK,EAAE,CACL;MACEqF,QAAQ,EAAE,UAAU;MACpB7B,KAAK,EAAElE,SAAS;MAChBgG,MAAM,EAAE/F;IACV,CAAC,EACDiF,aAAa,CAAE;IAAA;EACf,gBAEF3H,MAAA,CAAAa,OAAA,CAAAqI,aAAA,CAAC/I,YAAA,CAAAsJ,IAAI;IAACM,aAAa,EAAC,MAAM;IAAC5G,KAAK,EAAE;MAAEmH,IAAI,EAAE;IAAE;EAAE,GAC3C/G,iBACG,CACO,CAChB,EAGAC,eAAe,IAAIS,mBAAmB,IAAIE,UAAU,iBACnDnE,MAAA,CAAAa,OAAA,CAAAqI,aAAA,CAAC9I,sBAAA,CAAAS,OAAQ,CAAC4I,IAAI;IACZ9F,GAAG,EAAEe,kBAAmB;IACxBqF,aAAa,EAAC,UAAU;IACxBM,WAAW,EAAE,KAAM;IACnBlH,KAAK,EAAE,CACLmF,4BAA4B,EAC5B7E,oBAAoB,CAAE;IAAA,CACtB;IACF0F,QAAQ,EAAGxI,CAAC,IAAK;MACf;MACA,MAAM;QAAE0G,CAAC;QAAEK,CAAC;QAAEf,KAAK;QAAE8B;MAAO,CAAC,GAAG9H,CAAC,CAAC8F,WAAW,CAACC,MAAM;MACpD,IAAIR,uBAAuB,EAAE;QAC3BA,uBAAuB,CAACY,KAAK,GAAG;UAAEO,CAAC;UAAEK,CAAC;UAAEf,KAAK;UAAE8B;QAAO,CAAC;MACzD;IACF;EAAE,gBAEFzI,MAAA,CAAAa,OAAA,CAAAqI,aAAA,CAAC/I,YAAA,CAAAsJ,IAAI;IAACM,aAAa,EAAC,MAAM;IAAC5G,KAAK,EAAE;MAAEmH,IAAI,EAAE;IAAE;EAAE,GAC3C9G,eACG,CACO,CAEb,CACY,CAAC;AAEzB,CACF,CAAC;AAED,MAAMkG,MAAM,GAAGtE,uBAAU,CAACmF,MAAM,CAAC;EAC/BZ,SAAS,EAAE;IACThD,KAAK,EAAE;EACT;AACF,CAAC,CAAC;AAAC,IAAA6D,QAAA,GAAAC,OAAA,CAAA5J,OAAA,GAEYyB,aAAa","ignoreList":[]}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "SwappableGrid", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _SwappableGrid.default;
10
+ }
11
+ });
12
+ var _SwappableGrid = _interopRequireDefault(require("./SwappableGrid"));
13
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_SwappableGrid","_interopRequireDefault","require","e","__esModule","default"],"sources":["index.ts"],"sourcesContent":["export { default as SwappableGrid } from \"./SwappableGrid\";\nexport type { SwappableGridRef } from \"./SwappableGrid\";\n"],"mappings":";;;;;;;;;;;AAAA,IAAAA,cAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA2D,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA","ignoreList":[]}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = computeMinHeight;
7
+ function computeMinHeight(count, numColumns, tileH, pad) {
8
+ const rows = Math.max(1, Math.ceil(count / numColumns));
9
+ return pad * 2 + rows * tileH;
10
+ }
11
+ //# sourceMappingURL=computerMinHeight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["computeMinHeight","count","numColumns","tileH","pad","rows","Math","max","ceil"],"sources":["computerMinHeight.ts"],"sourcesContent":["export default function computeMinHeight(\n count: number,\n numColumns: number,\n tileH: number,\n pad: number\n) {\n const rows = Math.max(1, Math.ceil(count / numColumns));\n return pad * 2 + rows * tileH;\n}\n"],"mappings":";;;;;;AAAe,SAASA,gBAAgBA,CACtCC,KAAa,EACbC,UAAkB,EAClBC,KAAa,EACbC,GAAW,EACX;EACA,MAAMC,IAAI,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC,EAAED,IAAI,CAACE,IAAI,CAACP,KAAK,GAAGC,UAAU,CAAC,CAAC;EACvD,OAAOE,GAAG,GAAG,CAAC,GAAGC,IAAI,GAAGF,KAAK;AAC/B","ignoreList":[]}