react-native-draggable-grid
Version:
A draggable grid for react native
345 lines • 14 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DraggableGrid = void 0;
var React = require("react");
var react_1 = require("react");
var react_native_1 = require("react-native");
var block_1 = require("./block");
var utils_1 = require("./utils");
var activeBlockOffset = { x: 0, y: 0 };
exports.DraggableGrid = function (props) {
var blockPositions = react_1.useState([])[0];
var orderMap = react_1.useState({})[0];
var itemMap = react_1.useState({})[0];
var items = react_1.useState([])[0];
var _a = react_1.useState(0), blockHeight = _a[0], setBlockHeight = _a[1];
var _b = react_1.useState(0), blockWidth = _b[0], setBlockWidth = _b[1];
var gridHeight = react_1.useState(new react_native_1.Animated.Value(0))[0];
var _c = react_1.useState(false), hadInitBlockSize = _c[0], setHadInitBlockSize = _c[1];
var dragStartAnimatedValue = react_1.useState(new react_native_1.Animated.Value(1))[0];
var _d = react_1.useState({
x: 0,
y: 0,
width: 0,
height: 0,
}), gridLayout = _d[0], setGridLayout = _d[1];
var _e = react_1.useState(), activeItemIndex = _e[0], setActiveItemIndex = _e[1];
var assessGridSize = function (event) {
if (!hadInitBlockSize) {
var blockWidth_1 = event.nativeEvent.layout.width / props.numColumns;
var blockHeight_1 = props.itemHeight || blockWidth_1;
setBlockWidth(blockWidth_1);
setBlockHeight(blockHeight_1);
setGridLayout(event.nativeEvent.layout);
setHadInitBlockSize(true);
}
};
var _f = react_1.useState(false), panResponderCapture = _f[0], setPanResponderCapture = _f[1];
var panResponder = react_native_1.PanResponder.create({
onStartShouldSetPanResponder: function () { return true; },
onStartShouldSetPanResponderCapture: function () { return false; },
onMoveShouldSetPanResponder: function () { return panResponderCapture; },
onMoveShouldSetPanResponderCapture: function () { return panResponderCapture; },
onShouldBlockNativeResponder: function () { return false; },
onPanResponderTerminationRequest: function () { return false; },
onPanResponderGrant: onStartDrag,
onPanResponderMove: onHandMove,
onPanResponderRelease: onHandRelease,
});
function initBlockPositions() {
items.forEach(function (_, index) {
blockPositions[index] = getBlockPositionByOrder(index);
});
}
function getBlockPositionByOrder(order) {
if (blockPositions[order]) {
return blockPositions[order];
}
var columnOnRow = order % props.numColumns;
var y = blockHeight * Math.floor(order / props.numColumns);
var x = columnOnRow * blockWidth;
return {
x: x,
y: y,
};
}
function resetGridHeight() {
var rowCount = Math.ceil(props.data.length / props.numColumns);
gridHeight.setValue(rowCount * blockHeight);
}
function onBlockPress(itemIndex) {
props.onItemPress && props.onItemPress(items[itemIndex].itemData);
}
function onStartDrag(_, gestureState) {
var activeItem = getActiveItem();
if (!activeItem)
return false;
props.onDragStart && props.onDragStart(activeItem.itemData);
var x0 = gestureState.x0, y0 = gestureState.y0, moveX = gestureState.moveX, moveY = gestureState.moveY;
var activeOrigin = blockPositions[orderMap[activeItem.key].order];
var x = activeOrigin.x + (react_native_1.I18nManager.isRTL ? x0 : -x0);
var y = activeOrigin.y - y0;
activeItem.currentPosition.setOffset({
x: x,
y: y,
});
activeBlockOffset = {
x: x,
y: y,
};
activeItem.currentPosition.setValue({
x: react_native_1.I18nManager.isRTL ? -moveX : moveX,
y: moveY,
});
}
function onHandMove(_, gestureState) {
var activeItem = getActiveItem();
if (!activeItem)
return false;
var moveXOriginal = gestureState.moveX, moveY = gestureState.moveY;
var moveX = react_native_1.I18nManager.isRTL ? -moveXOriginal : moveXOriginal;
props.onDragging && props.onDragging(gestureState);
var xChokeAmount = Math.max(0, activeBlockOffset.x + moveX - (gridLayout.width - blockWidth));
var xMinChokeAmount = Math.min(0, activeBlockOffset.x + moveX);
var dragPosition = {
x: moveX - xChokeAmount - xMinChokeAmount,
y: moveY,
};
var originPosition = blockPositions[orderMap[activeItem.key].order];
var dragPositionToActivePositionDistance = getDistance(dragPosition, originPosition);
activeItem.currentPosition.setValue(dragPosition);
var closetItemIndex = activeItemIndex;
var closetDistance = dragPositionToActivePositionDistance;
items.forEach(function (item, index) {
if (item.itemData.disabledReSorted)
return;
if (index != activeItemIndex) {
var dragPositionToItemPositionDistance = getDistance(dragPosition, blockPositions[orderMap[item.key].order]);
if (dragPositionToItemPositionDistance < closetDistance &&
dragPositionToItemPositionDistance < blockWidth) {
closetItemIndex = index;
closetDistance = dragPositionToItemPositionDistance;
}
}
});
if (activeItemIndex != closetItemIndex) {
var closetOrder = orderMap[items[closetItemIndex].key].order;
resetBlockPositionByOrder(orderMap[activeItem.key].order, closetOrder);
orderMap[activeItem.key].order = closetOrder;
props.onResetSort && props.onResetSort(getSortData());
}
}
function onHandRelease() {
var activeItem = getActiveItem();
if (!activeItem)
return false;
props.onDragRelease && props.onDragRelease(getSortData());
setPanResponderCapture(false);
activeItem.currentPosition.flattenOffset();
moveBlockToBlockOrderPosition(activeItem.key);
setActiveItemIndex(undefined);
}
function resetBlockPositionByOrder(activeItemOrder, insertedPositionOrder) {
var disabledReSortedItemCount = 0;
if (activeItemOrder > insertedPositionOrder) {
for (var i = activeItemOrder - 1; i >= insertedPositionOrder; i--) {
var key = getKeyByOrder(i);
var item = itemMap[key];
if (item && item.disabledReSorted) {
disabledReSortedItemCount++;
}
else {
orderMap[key].order += disabledReSortedItemCount + 1;
disabledReSortedItemCount = 0;
moveBlockToBlockOrderPosition(key);
}
}
}
else {
for (var i = activeItemOrder + 1; i <= insertedPositionOrder; i++) {
var key = getKeyByOrder(i);
var item = itemMap[key];
if (item && item.disabledReSorted) {
disabledReSortedItemCount++;
}
else {
orderMap[key].order -= disabledReSortedItemCount + 1;
disabledReSortedItemCount = 0;
moveBlockToBlockOrderPosition(key);
}
}
}
}
function moveBlockToBlockOrderPosition(itemKey) {
var itemIndex = utils_1.findIndex(items, function (item) { return "" + item.key === "" + itemKey; });
items[itemIndex].currentPosition.flattenOffset();
react_native_1.Animated.timing(items[itemIndex].currentPosition, {
toValue: blockPositions[orderMap[itemKey].order],
duration: 200,
useNativeDriver: false,
}).start();
}
function getKeyByOrder(order) {
return utils_1.findKey(orderMap, function (item) { return item.order === order; });
}
function getSortData() {
var sortData = [];
items.forEach(function (item) {
sortData[orderMap[item.key].order] = item.itemData;
});
return sortData;
}
function getDistance(startOffset, endOffset) {
var xDistance = startOffset.x + activeBlockOffset.x - endOffset.x;
var yDistance = startOffset.y + activeBlockOffset.y - endOffset.y;
return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
}
function setActiveBlock(itemIndex, item) {
if (item.disabledDrag)
return;
props.onDragItemActive && props.onDragItemActive(item);
setPanResponderCapture(true);
setActiveItemIndex(itemIndex);
}
function startDragStartAnimation() {
if (!props.dragStartAnimation) {
dragStartAnimatedValue.setValue(1);
react_native_1.Animated.timing(dragStartAnimatedValue, {
toValue: 1.1,
duration: 100,
useNativeDriver: false,
}).start();
}
}
function getBlockStyle(itemIndex) {
return [
{
justifyContent: 'center',
alignItems: 'center',
},
hadInitBlockSize && {
width: blockWidth,
height: blockHeight,
position: 'absolute',
top: items[itemIndex].currentPosition.getLayout().top,
left: react_native_1.I18nManager.isRTL && react_native_1.Platform.OS === 'web' ? undefined : items[itemIndex].currentPosition.getLayout().left,
right: react_native_1.I18nManager.isRTL && react_native_1.Platform.OS === 'web' ? items[itemIndex].currentPosition.getLayout().left : undefined,
},
];
}
function getDragStartAnimation(itemIndex) {
if (activeItemIndex != itemIndex) {
return;
}
var dragStartAnimation = props.dragStartAnimation || getDefaultDragStartAnimation();
return __assign({ zIndex: 3 }, dragStartAnimation);
}
function getActiveItem() {
if (activeItemIndex === undefined)
return false;
return items[activeItemIndex];
}
function getDefaultDragStartAnimation() {
return {
transform: [
{
scale: dragStartAnimatedValue,
},
],
shadowColor: '#000000',
shadowOpacity: 0.2,
shadowRadius: 6,
shadowOffset: {
width: 1,
height: 1,
},
};
}
function addItem(item, index) {
blockPositions.push(getBlockPositionByOrder(items.length));
orderMap[item.key] = {
order: index,
};
itemMap[item.key] = item;
items.push({
key: item.key,
itemData: item,
currentPosition: new react_native_1.Animated.ValueXY(getBlockPositionByOrder(index)),
});
}
function removeItem(item) {
var itemIndex = utils_1.findIndex(items, function (curItem) { return curItem.key === item.key; });
items.splice(itemIndex, 1);
blockPositions.pop();
delete orderMap[item.key];
}
function diffData() {
props.data.forEach(function (item, index) {
if (orderMap[item.key]) {
if (orderMap[item.key].order != index) {
orderMap[item.key].order = index;
moveBlockToBlockOrderPosition(item.key);
}
var currentItem = items.find(function (i) { return i.key === item.key; });
if (currentItem) {
currentItem.itemData = item;
}
itemMap[item.key] = item;
}
else {
addItem(item, index);
}
});
var deleteItems = utils_1.differenceBy(items, props.data, 'key');
deleteItems.forEach(function (item) {
removeItem(item);
});
}
react_1.useEffect(function () {
startDragStartAnimation();
}, [activeItemIndex]);
react_1.useEffect(function () {
if (hadInitBlockSize) {
initBlockPositions();
}
}, [gridLayout]);
react_1.useEffect(function () {
resetGridHeight();
});
if (hadInitBlockSize) {
diffData();
}
var itemList = items.map(function (item, itemIndex) {
return (<block_1.Block onPress={onBlockPress.bind(null, itemIndex)} onLongPress={setActiveBlock.bind(null, itemIndex, item.itemData)} panHandlers={panResponder.panHandlers} style={getBlockStyle(itemIndex)} dragStartAnimationStyle={getDragStartAnimation(itemIndex)} delayLongPress={props.delayLongPress || 300} key={item.key}>
{props.renderItem(item.itemData, orderMap[item.key].order)}
</block_1.Block>);
});
return (<react_native_1.Animated.View style={[
styles.draggableGrid,
props.style,
{
height: gridHeight,
},
]} onLayout={assessGridSize}>
{hadInitBlockSize && itemList}
</react_native_1.Animated.View>);
};
var styles = react_native_1.StyleSheet.create({
draggableGrid: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
},
});
//# sourceMappingURL=draggable-grid.js.map