@trava/react-native-drax
Advanced tools
Comparing version 0.7.6 to 0.7.7
import React, { PropsWithChildren, ReactElement } from 'react'; | ||
import { DraxListProps } from './types'; | ||
export declare const DraxList: <T extends unknown>({ data, style, itemStyles, renderItemContent, renderItemHoverContent, onItemDragStart, onItemDragPositionChange, onItemDragEnd, onItemReorder, id: idProp, reorderable: reorderableProp, onChangeList, dummyItem, ...props }: React.PropsWithChildren<DraxListProps<T>>) => ReactElement | null; | ||
export declare const DraxList: <T extends unknown>({ data, style, itemStyles, renderItemContent, renderItemHoverContent, onItemDragStart, onItemDragPositionChange, onItemDragEnd, onItemReorder, id: idProp, reorderable: reorderableProp, onChangeList, lockDragging, ...props }: React.PropsWithChildren<DraxListProps<T>>) => ReactElement | null; |
@@ -30,2 +30,4 @@ "use strict"; | ||
const params_1 = require("./params"); | ||
const math_1 = require("./math"); | ||
const dummyId = math_1.generateRandomId(); | ||
const defaultStyles = react_native_1.StyleSheet.create({ | ||
@@ -39,4 +41,6 @@ draggingStyle: { opacity: 0 }, | ||
// @ts-ignore | ||
dummyItem, // RN added | ||
lockDragging, // RN added | ||
...props }) => { | ||
// Intuit the value for dummyItem (necessary for drags into other DraxLists) | ||
const dummyItem = onChangeList !== undefined; | ||
// Copy the value of the horizontal property for internal use. | ||
@@ -48,4 +52,2 @@ const { horizontal = false } = props; | ||
const { containerScrollPositionRef, // outer DraxScrollView's scroll position (for calculateSnapback to properly animate) | ||
dragExitedContainer, // if user's finger ends up outside container, then drop is not valid | ||
containerAutoScrollId, // outer DraxScrollView's autoscroll interval id (for us to clear) | ||
} = contextParent ?? {}; | ||
@@ -91,3 +93,3 @@ // RN added: Whether to make the dummy item visible (only want to show on hover from tile from different DraxList) | ||
// @ts-ignore | ||
return dummyItem ? [...data, { id: 100000 }] : data; // probably shouldn't hardcode id... | ||
return dummyItem ? [...data, { id: dummyId }] : data; | ||
}, [data, dummyItem]); | ||
@@ -97,3 +99,3 @@ // RN changed to reference dataUpdated. Get the item count for internal use. | ||
// RN added | ||
// Android only, bug fix: imperatively control scroll bar when last item is visibly moved | ||
// Flatlist component bug fix: imperatively control scroll bar depending on scrollTo state | ||
react_1.useEffect(() => { | ||
@@ -112,3 +114,3 @@ if (scrollTo) { | ||
react_1.useEffect(() => { | ||
// RN: reset state variable for android imperative scroll bux-fix | ||
// RN: reset state variable for Flatlist imperative scroll bug-fix | ||
setScrollTo(undefined); | ||
@@ -120,7 +122,4 @@ // RN: if # of items have changed, setShowDummy false (covers reset on success cases) | ||
const shifts = shiftsRef.current; | ||
// RN simplified, but might make sense to expand again | ||
if (itemMeasurements.length > itemCount) { | ||
itemMeasurements.splice(itemCount - itemMeasurements.length); | ||
registrations.splice(itemCount - registrations.length); | ||
shifts.splice(itemCount - shifts.length); | ||
} | ||
@@ -130,3 +129,17 @@ else { | ||
itemMeasurements.push(undefined); | ||
} | ||
} | ||
if (registrations.length > itemCount) { | ||
registrations.splice(itemCount - registrations.length); | ||
} | ||
else { | ||
while (registrations.length < itemCount) { | ||
registrations.push(undefined); | ||
} | ||
} | ||
if (shifts.length > itemCount) { | ||
shifts.splice(itemCount - shifts.length); | ||
} | ||
else { | ||
while (shifts.length < itemCount) { | ||
shifts.push({ | ||
@@ -140,3 +153,4 @@ targetValue: 0, | ||
// RN ADDED | ||
// update the dummyItemDimensions upon receiving a drag of a DraxView belonging to another DraxList. Dimensions should match foreign DraxView. | ||
// upon receiving a drag of a DraxView from another Draxlist, onMonitorDragEnter sets showDummy true, which triggers this effect upon rerender to update dummy item's dimensions to match currently dragged view. | ||
// when drag ends/exits, showDummy set to false, which triggers this effect to set dummy item's dimensions to 0. | ||
react_1.useEffect(() => { | ||
@@ -205,3 +219,4 @@ // if showDummy is false, set dimensions = 0 | ||
const itemSizes = itemMeasurementsRef.current; | ||
const lastItemPosition = horizontal ? itemSizes[itemCount - 1 - dummyItem].x : itemSizes[itemCount - 1 - dummyItem].y; | ||
const realItemCount = itemCount - (dummyItem ? 1 : 0); | ||
const lastItemPosition = horizontal ? itemSizes[realItemCount - 1].x : itemSizes[realItemCount - 1].y; | ||
// if the last item is visible, animation is different | ||
@@ -413,11 +428,2 @@ const lastItemVisible = scrollPosition + containerLength >= lastItemPosition; | ||
}, [horizontal]); | ||
// RN added: This is the cb fn assigned to onDragEnd of each Draxview (see renderItem). Added in order to prevent memory leak from outer ScrollView's autoscroll | ||
const handleDragEnd = react_1.useCallback(() => { | ||
// RN: if user's finger left vertical scrollview, we must clearInterval here onDragEnd | ||
// to prevent memory leak | ||
if (containerAutoScrollId) { | ||
clearInterval(containerAutoScrollId); | ||
} | ||
resetDraggedItem(); | ||
}, [containerAutoScrollId, resetDraggedItem]); | ||
// RN added: This is the cb fn assigned to onDragDrop of each Draxview (see renderItem) | ||
@@ -429,6 +435,2 @@ // If one of this DraxList's DraxViews is dropped in a different DraxList, we must animate this DraxList's shrinking | ||
// thus, shiftBeforeListShrinks only has effect on btw list move, executing while toList is animating | ||
// first, check if user's finger is within vertical ScrollView | ||
if (dragExitedContainer) { | ||
return; // if not, then not a valid drop | ||
} | ||
// if this item is dropped into same parent DraxList, no work necessary | ||
@@ -440,3 +442,3 @@ if (eventData.dragged.parentId === eventData.receiver.parentId) { | ||
resetDraggedItem(); | ||
}, [dragExitedContainer, shiftBeforeListShrinks, resetDraggedItem]); | ||
}, [shiftBeforeListShrinks, resetDraggedItem]); | ||
// RN added component: dummyItem is necessary to allow user to place a DraxView from a different DraxList at the end of this DraxList | ||
@@ -487,3 +489,3 @@ // Note: Size of dummyItem varies. If this DraxList is empty, DummyItem must take up width/height of day row/column, to allow user to drop foreign tile anywhere in this empty DraxList | ||
]); | ||
// RN edited this fn slightly to account for dummyItem and defined callbacks (e.g., handleDragEnd) | ||
// RN edited this fn slightly to account for dummyItem and defined callbacks (e.g., onDragDrop calls handleDragDrop) | ||
// this is the renderItem of the FlatList, rendering individual DraxViews (activity tiles) | ||
@@ -495,3 +497,3 @@ const renderItem = react_1.useCallback((info) => { | ||
const { style: itemStyle, draggingStyle = defaultStyles.draggingStyle, dragReleasedStyle = defaultStyles.dragReleasedStyle, ...otherStyleProps } = itemStyles ?? {}; | ||
return (react_1.default.createElement(DraxView_1.DraxView, Object.assign({ style: [itemStyle, { transform: getShiftTransform(originalIndex) }], draggable: !dummy, receptive: !dummy || showDummy, draggingStyle: draggingStyle, dragReleasedStyle: dragReleasedStyle }, otherStyleProps, { payload: { index, originalIndex }, onDragEnd: handleDragEnd, onDragDrop: (eventData) => handleDragDrop(eventData, originalIndex), onMeasure: (measurements) => { | ||
return (react_1.default.createElement(DraxView_1.DraxView, Object.assign({ style: [itemStyle, { transform: getShiftTransform(originalIndex) }], draggable: !lockDragging && !dummy, receptive: !dummy || showDummy, draggingStyle: draggingStyle, dragReleasedStyle: dragReleasedStyle }, otherStyleProps, { payload: { index, originalIndex }, onDragEnd: resetDraggedItem, onDragDrop: (eventData) => handleDragDrop(eventData, originalIndex), onMeasure: (measurements) => { | ||
itemMeasurementsRef.current[originalIndex] = measurements; | ||
@@ -513,3 +515,3 @@ }, registration: (registration) => { | ||
itemCount, | ||
handleDragEnd, | ||
resetDraggedItem, | ||
handleDragDrop, | ||
@@ -623,5 +625,3 @@ renderDummyItem, | ||
draggedParentId, // RN added this to ensure appropriate shifting occurs for 'foreign' DraxView vs. 'resident' DraxView | ||
width = 140, // RN updated to reflect default width of activity tile | ||
height = 90, // RN updated to reflect default height of activity tile | ||
} = draggedInfo; | ||
width = 50, height = 50, } = draggedInfo; | ||
const fromIndex = draggedPayload?.index; | ||
@@ -642,7 +642,7 @@ const toIndex = receiverPayload?.index; | ||
else { | ||
// items between dragged and received index should shift leftwards | ||
// moving right: items between dragged and received index should shift leftwards | ||
if (index > fromIndex && index <= toIndex) { | ||
newTargetValue = -offset; | ||
} | ||
// items between received index and dragged should shift rightwards | ||
// moving left: items between received index and dragged should shift rightwards | ||
else if (index < fromIndex && index >= toIndex) { | ||
@@ -710,2 +710,3 @@ newTargetValue = offset; | ||
} | ||
// Else, the dragged item belongs to another Draxlist OR is moving to the left | ||
else { | ||
@@ -723,3 +724,3 @@ // Target pos(toIndex) | ||
const scrollPosition = scrollPositionRef.current; | ||
// RN ADDED: if this DraxList is within another ScrollView, then we need that ScrollView's position too. | ||
// RN ADDED: if this DraxList is within another ScrollView, then we need that ScrollView's position too. Ideally, could replace with a recursive fn. | ||
const { x: containerScrollPositionX, y: containerScrollPositionY } = containerScrollPositionRef?.current ?? { x: 0, y: 0 }; | ||
@@ -743,8 +744,2 @@ // RN MODIFIED: subtract the outer ScrollView's x/y | ||
const { dragged, receiver, draggedDimensions } = eventData; | ||
// RN added: first, check if user's finger exited vertical ScrollView. If it has, then this is not a valid drop. | ||
if (dragExitedContainer) { | ||
setShowDummy(false); | ||
resetShifts(200); | ||
return undefined; | ||
} | ||
// RN added: if dragged item comes from other parent DraxList | ||
@@ -760,3 +755,3 @@ if (reorderable && dragged.parentId !== id) { | ||
}; | ||
// if user swipes an item into the list without hovering over, ensure shift occurs: | ||
// if user quickly swipes an item into the list without hovering over, ensure shift still occurs: | ||
if (shiftsRef.current[receiver.payload.index].targetValue === 0) { | ||
@@ -767,14 +762,9 @@ updateShifts(draggedInfo, receiver?.payload); | ||
const snapbackTarget = calculateSnapbackTarget(draggedInfo, receiver.payload); | ||
if (dataUpdated) { | ||
const newOriginalIndexes = originalIndexes.slice(); | ||
newOriginalIndexes.splice(receiver.payload.index, 0); | ||
setOriginalIndexes(newOriginalIndexes); | ||
} | ||
// RN edited -- important. | ||
// return value is supplied to Drax Registry method resetDrag (in DraxProvider), which animates tile to target | ||
// & calls callback once animation completes (which updates state) | ||
// return value is supplied to Drax Registry method resetDrag (in DraxProvider), which animates tile to snapbackTarget | ||
// & calls callback once snapback animation completes (which updates state) | ||
return { | ||
target: snapbackTarget, | ||
callback: () => { | ||
resetShifts(); // don't want to reset shifts until after snapback animation completes | ||
// onChangeList sets state on the data, which causes this component to rerender, which triggers effect to resetShifts | ||
onChangeList(dragged.payload.originalIndex, receiver.payload.originalIndex, dragged.parentId, id); | ||
@@ -871,3 +861,2 @@ }, | ||
onChangeList, | ||
dragExitedContainer, | ||
dataUpdated, | ||
@@ -895,5 +884,5 @@ hideDummy, | ||
const onMonitorDragOver = react_1.useCallback((eventData) => { | ||
const { dragged, receiver, monitorOffsetRatio, draggedDimensions, // RN added | ||
const { dragged, receiver, monitorOffsetRatio, draggedDimensions, // RN added to eventData in provider | ||
} = eventData; | ||
// RN added: if dragged item comes from other DraxList | ||
// RN added: if dragged item comes from other DraxList, call updateShifts | ||
if (reorderable && dragged.parentId !== id) { | ||
@@ -900,0 +889,0 @@ const draggedInfo = { |
@@ -30,3 +30,3 @@ "use strict"; | ||
const math_1 = require("./math"); | ||
const DraxProvider = ({ debug = false, clampDrag = false, // RN added: prevent user from dragging item outside of DraxProvider container | ||
const DraxProvider = ({ debug = false, clampDragToContainer = false, // RN added: prevent user from dragging item outside of DraxProvider container | ||
children, }) => { | ||
@@ -403,3 +403,3 @@ const { getViewState, getTrackingStatus, dispatch } = hooks_1.useDraxState(); | ||
const draggedData = dragged?.data ?? getAbsoluteViewData(id); | ||
if (containerHeight.current === undefined && clampDrag) { | ||
if (containerHeight.current === undefined && clampDragToContainer) { | ||
const containerInfo = getAbsoluteViewData(getContainerViewId()); // // RN added, so that we can clamp drag to outer container | ||
@@ -454,5 +454,5 @@ containerHeight.current = containerInfo?.absoluteMeasurements.height; | ||
}; | ||
// RN: updated to account for clampDrag | ||
// RN: updated to account for clampDragToContainer | ||
// Always update the drag position. | ||
updateDragPosition(clampDrag ? clampDragPosition : dragAbsolutePosition); | ||
updateDragPosition(clampDragToContainer ? clampDragPosition : dragAbsolutePosition); | ||
const draggedProtocol = dragged.data.protocol; | ||
@@ -638,3 +638,3 @@ // Prepare event data for dragged view. | ||
debug, | ||
clampDrag, | ||
clampDragToContainer, | ||
getContainerViewId, | ||
@@ -641,0 +641,0 @@ ]); |
import { PropsWithChildren } from 'react'; | ||
import { DraxScrollViewProps } from './types'; | ||
export declare const DraxScrollView: ({ children, onScroll: onScrollProp, onContentSizeChange: onContentSizeChangeProp, scrollEventThrottle, autoScrollIntervalLength, autoScrollJumpRatio, autoScrollBackThreshold, autoScrollForwardThreshold, id: idProp, isContainer, ...props }: PropsWithChildren<DraxScrollViewProps>) => JSX.Element | null; | ||
export declare const DraxScrollView: ({ children, onScroll: onScrollProp, onContentSizeChange: onContentSizeChangeProp, scrollEventThrottle, autoScrollIntervalLength, autoScrollJumpRatio, autoScrollBackThreshold, autoScrollForwardThreshold, id: idProp, ...props }: PropsWithChildren<DraxScrollViewProps>) => JSX.Element | null; |
@@ -30,4 +30,3 @@ "use strict"; | ||
const params_1 = require("./params"); | ||
const DraxScrollView = ({ children, onScroll: onScrollProp, onContentSizeChange: onContentSizeChangeProp, scrollEventThrottle = params_1.defaultScrollEventThrottle, autoScrollIntervalLength = params_1.defaultAutoScrollIntervalLength, autoScrollJumpRatio = params_1.defaultAutoScrollJumpRatio, autoScrollBackThreshold = params_1.defaultAutoScrollBackThreshold, autoScrollForwardThreshold = params_1.defaultAutoScrollForwardThreshold, id: idProp, isContainer, // RN added | ||
...props }) => { | ||
const DraxScrollView = ({ children, onScroll: onScrollProp, onContentSizeChange: onContentSizeChangeProp, scrollEventThrottle = params_1.defaultScrollEventThrottle, autoScrollIntervalLength = params_1.defaultAutoScrollIntervalLength, autoScrollJumpRatio = params_1.defaultAutoScrollJumpRatio, autoScrollBackThreshold = params_1.defaultAutoScrollBackThreshold, autoScrollForwardThreshold = params_1.defaultAutoScrollForwardThreshold, id: idProp, ...props }) => { | ||
// RN MAIN CHANGES TO THIS FILE: | ||
@@ -46,4 +45,2 @@ // 1. Exposed DraxScrollView's scrollposition/drag state via context so that DraxList's calculateSnapback accounted for outer scrollview | ||
const scrollPositionRef = react_1.useRef({ x: 0, y: 0 }); | ||
// RN added: Ensure user's finger hasn't left the vertical scroll view. If it has, disallow tile drops in DraxList(through context). | ||
const [exitedScroll, setExitedScroll] = react_1.useState(false); | ||
// Auto-scroll state. | ||
@@ -139,3 +136,2 @@ const autoScrollStateRef = react_1.useRef({ | ||
// console.log('\n', 'onMonitorDragStart: id', id, '\n') | ||
setExitedScroll(false); // DraxView we're monitoring hasn't left this scroll container | ||
return resetScroll(); | ||
@@ -146,3 +142,2 @@ }, [resetScroll]); | ||
// console.log('\n', 'onMonitorDragEnter: id', id, '\n') | ||
setExitedScroll(false); // DraxView we're monitoring hasn't left this scroll container | ||
return resetScroll(); | ||
@@ -153,4 +148,3 @@ }, [resetScroll]); | ||
// console.log('\n', 'onMonitorDragExit: id', id, '\n') | ||
setExitedScroll(true); // DraxView we're monitoring has left this scroll container | ||
// don't reset scroll here -- continue autoscrolling. Allow DraxView's onDragDrop to clear interval (in DraxList renderItem). | ||
return resetScroll(); | ||
}, []); | ||
@@ -225,7 +219,2 @@ const onMonitorDragEnd = react_1.useCallback((eventData) => { | ||
containerScrollPositionRef: scrollPositionRef, | ||
// RN added to ensure that a drag that ends outside the calendar grid altogether (e.g., in the Trip Name header) | ||
// results in a canceled drag rather than a successful drop in the top/bottom row. | ||
dragExitedContainer: exitedScroll, | ||
// RN added this so we can clear interval within DraxList on drag exiting container (to prevent memory leak) | ||
containerAutoScrollId: autoScrollIntervalRef.current, | ||
} }, | ||
@@ -232,0 +221,0 @@ react_1.default.createElement(react_native_1.ScrollView, Object.assign({}, props, { ref: setScrollViewRefs, onContentSizeChange: onContentSizeChange, onScroll: onScroll, scrollEventThrottle: scrollEventThrottle }), children)))) : null; |
@@ -473,3 +473,3 @@ /// <reference types="node" /> | ||
debug?: boolean; | ||
clampDrag?: boolean; | ||
clampDragToContainer?: boolean; | ||
multiDimensionalScroll?: boolean; | ||
@@ -476,0 +476,0 @@ } |
{ | ||
"name": "@trava/react-native-drax", | ||
"version": "0.7.6", | ||
"version": "0.7.7", | ||
"description": "A drag-and-drop system for React Native", | ||
@@ -5,0 +5,0 @@ "repository": { |
3633191
4306