UNPKG

react-native-draggable-masonry-grid

Version:

React native flatlist based implementation of masonary grid with drag/drop functionality

203 lines (198 loc) 7.86 kB
"use strict"; import { useRef, useState } from 'react'; export function asyncWaitForMS(waitForMS) { return new Promise(res => setTimeout(res, waitForMS)); } /** * @param min * @param max * @returns {number} random integer between min(inclusive) and max(inclusive) */ export const generateRandomInteger = (min, max) => { return Math.floor(Math.random() * (max - min + 1)) + min; }; /** * * @param array * @param index * @returns new array with item at added at given index */ export function arrayAddItemAtIndex(array, index, item) { return [...array.slice(0, index), item, ...array.slice(index)]; } /** * * @param array * @param index * @returns new array with item removed at given index */ export function arrayRemoveItemAtIndex(array, index) { return [...array.slice(0, index), ...array.slice(index + 1)]; } /** * * @param array * @param indexFrom * @param indexTo * @returns new array with item that was in indexFrom moved to indexTo */ export function arrayMoveFromIndexToIndex(array, indexFrom, indexTo) { if (indexFrom < indexTo && array[indexFrom]) { return [...array.slice(0, indexFrom), ...array.slice(indexFrom + 1, indexTo + 1), array[indexFrom], ...array.slice(indexTo + 1)]; } else if (indexFrom > indexTo && array[indexFrom]) { return [...array.slice(0, indexTo), array[indexFrom], ...array.slice(indexTo, indexFrom), ...array.slice(indexFrom + 1)]; } else { return [...array]; } } export function getGridColumns(data, numberOfColumns, columnWidth) { const columnHeightsTracker = new Array(numberOfColumns); const gridDataInSequence = new Array(data.length); const columnsForGrid = new Array(numberOfColumns); for (let i = 0; i < numberOfColumns; i++) { columnHeightsTracker[i] = 0; columnsForGrid[i] = []; } if (!data.length) { return { columnsForGrid, gridDataInSequence, columnHeights: columnHeightsTracker }; } data.forEach((item, index) => { if (item.type === 'HEIGHT_EQUILIZER') { // adds empty space to compensate for unaligned extra height of last columns item const maxColumnHeight = Math.max(...columnHeightsTracker); for (let i = 0; i < columnHeightsTracker.length; i++) { const currentColumnHeight = columnHeightsTracker[i]; const height = maxColumnHeight - (currentColumnHeight ?? 0); const data = { columnIndex: i, height, isDraggable: false, index: columnsForGrid[i]?.length ?? 0, offsetY: columnHeightsTracker[i] ?? 0, originalIndex: index, position: { x: i * columnWidth, y: columnHeightsTracker[i] ?? 0 }, type: 'empty_space' }; columnsForGrid[i]?.push(data); gridDataInSequence[index] = data; columnHeightsTracker[i] = (columnHeightsTracker[i] ?? 0) + height; } } else { const minColumnHeight = Math.min(...columnHeightsTracker); // add to earliest column if height with 10px of minColumnHeight const columnIndexToAddTo = columnHeightsTracker.findIndex(height => height < minColumnHeight + 10); const data = { columnIndex: columnIndexToAddTo, height: Math.round(item.height), isDraggable: item.isDraggable, index: columnsForGrid[columnIndexToAddTo]?.length ?? 0, item: item.item, offsetY: columnHeightsTracker[columnIndexToAddTo] ?? 0, verticeoffsets: item.verticeoffsets, originalIndex: index, position: { x: columnIndexToAddTo * columnWidth, y: columnHeightsTracker[columnIndexToAddTo] ?? 0 }, type: 'item' }; columnsForGrid[columnIndexToAddTo]?.push(data); gridDataInSequence[index] = data; columnHeightsTracker[columnIndexToAddTo] = (columnHeightsTracker[columnIndexToAddTo] ?? 0) + data.height; } }); return { columnsForGrid, gridDataInSequence, columnHeights: columnHeightsTracker }; } export function getRawDataFromGridColumns(columnsForGrid, columnHeights) { const totalItems = columnsForGrid.reduce((length, column) => length += column.length, 0); const rawDataInSequence = new Array(totalItems); const columnHeightsTracker = new Array(...columnHeights); const columnIndexTrackers = columnsForGrid.map(column => column.length - 1); let lastItem = undefined; for (let i = totalItems - 1; i >= 0; i--) { const maxColumnHeight = Math.max(...columnHeightsTracker); const columnIndexToExtractFrom = columnHeightsTracker.findIndex(height => height === maxColumnHeight); // extract last item in the column const item = columnsForGrid[columnIndexToExtractFrom]?.[columnIndexTrackers[columnIndexToExtractFrom] ?? 0]; columnHeightsTracker[columnIndexToExtractFrom] = (columnHeightsTracker[columnIndexToExtractFrom] ?? 0) - (item?.height ?? 0); columnIndexTrackers[columnIndexToExtractFrom] = (columnIndexTrackers[columnIndexToExtractFrom] ?? 0) - 1; if (item?.type === 'empty_space') { if (lastItem?.type !== 'empty_space') { rawDataInSequence[i] = { type: 'HEIGHT_EQUILIZER' }; } } else if (item) { rawDataInSequence[i] = { height: item.height, isDraggable: item.isDraggable, item: item.item, type: 'ITEM' }; } lastItem = item; } return { rawDataInSequence: rawDataInSequence.filter(Boolean) }; } export const checkIfContainsCoordinates = (coordinates, vertices) => coordinates.x > vertices.topLeft.x && coordinates.y > vertices.topLeft.y && coordinates.x < vertices.bottomRight.x && coordinates.y < vertices.bottomRight.y; export const checkForOverlap = (verticesA, verticesB) => checkIfContainsCoordinates(verticesA.center, verticesB) || checkIfContainsCoordinates(verticesB.center, verticesA); export const getDistanceBetweenVertices = (coordinatesA, coordinatesB) => Math.sqrt(Math.pow(coordinatesA.x - coordinatesB.x, 2) + Math.pow(coordinatesA.y - coordinatesB.y, 2)); const getViewMeasurements = cardRef => new Promise(res => cardRef?.current?.measure((...values) => res(values))); export const getViewVertices = async (cardRef, options = {}) => { const { scale = 1, offsets } = options; const cornerScale = (scale - 1) / 2 + 1; const [_x, _y, width, height, pageX, pageY] = await getViewMeasurements(cardRef); return { topCenter: { x: Math.round(pageX + width / 2 + (offsets?.center?.x ?? 0)), y: Math.round(pageY * cornerScale + (offsets?.topLeft?.y ?? 0)) }, topLeft: { x: Math.round(pageX * cornerScale + (offsets?.topLeft?.x ?? 0)), y: Math.round(pageY * cornerScale + (offsets?.topLeft?.y ?? 0)) }, topRight: { x: Math.round((pageX + width) / cornerScale + (offsets?.topRight?.x ?? 0)), y: Math.round(pageY * cornerScale + (offsets?.topRight?.y ?? 0)) }, bottomCenter: { x: Math.round(pageX + width / 2 + (offsets?.center?.x ?? 0)), y: Math.round((pageY + height) / cornerScale + (offsets?.bottomRight?.y ?? 0)) }, bottomRight: { x: Math.round((pageX + width) / cornerScale + (offsets?.bottomRight?.x ?? 0)), y: Math.round((pageY + height) / cornerScale + (offsets?.bottomRight?.y ?? 0)) }, bottomLeft: { x: Math.round(pageX * cornerScale + (offsets?.bottomLeft?.x ?? 0)), y: Math.round((pageY + height) / cornerScale + (offsets?.bottomLeft?.y ?? 0)) }, center: { x: Math.round(pageX + width / 2 + (offsets?.center?.x ?? 0)), y: Math.round(pageY + height / 2 + (offsets?.center?.y ?? 0)) } }; }; export function useStateRef(initialValue) { const [state, setState] = useState(initialValue); const stateRef = useRef(initialValue); stateRef.current = state; return [stateRef, setState]; } //# sourceMappingURL=utils.js.map