react-native-sortables
Version:
Powerful Sortable Components for Flexible Content Reordering in React Native
151 lines (147 loc) • 5.63 kB
JavaScript
;
import { useCallback, useRef } from 'react';
import { runOnUI } from 'react-native-reanimated';
import { useStableCallback } from '../../hooks';
import { setAnimatedTimeout, useAnimatedDebounce, useMutableValue } from '../../integrations/reanimated';
import { areValuesDifferent, resolveDimension } from '../../utils';
import { createProvider } from '../utils';
import { useCommonValuesContext } from './CommonValuesProvider';
import { useMultiZoneContext } from './MultiZoneProvider';
const {
MeasurementsProvider,
useMeasurementsContext
} = createProvider('Measurements')(({
itemsCount
}) => {
const {
activeItemDimensions,
activeItemKey,
containerHeight,
containerWidth,
controlledContainerDimensions,
controlledItemDimensions,
itemHeights,
itemWidths,
usesAbsoluteLayout
} = useCommonValuesContext();
const {
activeItemDimensions: multiZoneActiveItemDimensions
} = useMultiZoneContext() ?? {};
const measuredItemsCount = useMutableValue(0);
const previousItemDimensionsRef = useRef({});
const debounce = useAnimatedDebounce();
const handleItemMeasurement = useStableCallback((key, dimensions) => {
const prevDimensions = previousItemDimensionsRef.current[key];
const {
height: isHeightControlled,
width: isWidthControlled
} = controlledItemDimensions;
if (isWidthControlled && isHeightControlled) {
return;
}
const changedDimensions = {};
if (!isWidthControlled && areValuesDifferent(prevDimensions?.width, dimensions.width, 1)) {
changedDimensions.width = dimensions.width;
}
if (!isHeightControlled && areValuesDifferent(prevDimensions?.height, dimensions.height, 1)) {
changedDimensions.height = dimensions.height;
}
if (!Object.keys(changedDimensions).length) {
return;
}
previousItemDimensionsRef.current[key] = dimensions;
runOnUI(() => {
const resolvedWidth = resolveDimension(itemWidths.value, key);
const resolvedHeight = resolveDimension(itemHeights.value, key);
const isNewItem = resolvedWidth === null || resolvedHeight === null;
if (isNewItem) {
measuredItemsCount.value += 1;
}
if (itemWidths.value && typeof itemWidths.value === 'object') {
itemWidths.value[key] = dimensions.width;
}
if (itemHeights.value && typeof itemHeights.value === 'object') {
itemHeights.value[key] = dimensions.height;
}
if (activeItemKey.value === key) {
activeItemDimensions.value = dimensions;
if (multiZoneActiveItemDimensions) {
multiZoneActiveItemDimensions.value = dimensions;
}
}
// Update the array of item dimensions only after all items have been
// measured to reduce the number of times animated reactions are triggered
if (measuredItemsCount.value === itemsCount) {
const updateDimensions = () => {
if (!isWidthControlled) itemWidths.modify();
if (!isHeightControlled) itemHeights.modify();
};
if (isNewItem) {
// If measurements were triggered because of adding new items and all new
// items have been measured, update dimensions immediately to avoid
// unnecessary delays
updateDimensions();
} else {
// Otherwise, debounce the update if the number of items is not changed
// to reduce the number of updates if dimensions of items are changed
// many times within a short period of time
debounce(updateDimensions, 100);
}
}
})();
});
const removeItemMeasurements = useCallback(key => {
delete previousItemDimensionsRef.current[key];
const {
height: isHeightControlled,
width: isWidthControlled
} = controlledItemDimensions;
if (isWidthControlled && isHeightControlled) {
return;
}
runOnUI(() => {
if (itemWidths.value && typeof itemWidths.value === 'object') {
delete itemWidths.value[key];
}
if (itemHeights.value && typeof itemHeights.value === 'object') {
delete itemHeights.value[key];
}
measuredItemsCount.value -= 1;
})();
}, [controlledItemDimensions, itemHeights, itemWidths, measuredItemsCount]);
const handleContainerMeasurement = useCallback((width, height) => {
'worklet';
if (!controlledContainerDimensions.width) {
containerWidth.value = width;
}
if (!controlledContainerDimensions.height) {
containerHeight.value = height;
}
}, [controlledContainerDimensions, containerHeight, containerWidth]);
const applyControlledContainerDimensions = useCallback(dimensions => {
'worklet';
if (controlledContainerDimensions.width && dimensions.width !== undefined) {
containerWidth.value = dimensions.width;
}
if (controlledContainerDimensions.height && dimensions.height !== undefined) {
containerHeight.value = dimensions.height;
}
if (!usesAbsoluteLayout.value) {
// Add timeout for safety, to prevent too many layout recalculations
// in a short period of time (this may cause issues on low end devices)
setAnimatedTimeout(() => {
usesAbsoluteLayout.value = true;
}, 100);
}
}, [containerHeight, containerWidth, controlledContainerDimensions, usesAbsoluteLayout]);
return {
value: {
applyControlledContainerDimensions,
handleContainerMeasurement,
handleItemMeasurement,
removeItemMeasurements
}
};
});
export { MeasurementsProvider, useMeasurementsContext };
//# sourceMappingURL=MeasurementsProvider.js.map