UNPKG

react-native-chart-kit

Version:

Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.

111 lines (110 loc) 4.28 kB
import { useCallback, useMemo, useRef } from "react"; import { resolveChartViewportWindowFromPanDelta } from "../../core/index"; import { isChartViewportEventInBounds } from "./bounds"; import { getChartViewportPanDeltaPoints, resolveChartViewportInteractionConfig } from "./config"; const isSameViewportWindow = (first, second) => first.startIndex === second.startIndex && first.endIndex === second.endIndex; export const useChartViewportPan = ({ dataLength, enabled, onViewportChange, plotBounds, preventBrowserSelection, viewportInteraction, viewportWindow }) => { const config = useMemo(() => resolveChartViewportInteractionConfig(viewportInteraction), [viewportInteraction]); const panStateRef = useRef(undefined); const canPan = enabled && config.pan && onViewportChange !== undefined && dataLength > viewportWindow.visibleCount && viewportWindow.visibleCount > 0 && plotBounds.width > 0; const emitViewportChange = useCallback((nextWindow) => { onViewportChange?.({ viewport: { startIndex: nextWindow.startIndex, endIndex: nextWindow.endIndex }, startIndex: nextWindow.startIndex, endIndex: nextWindow.endIndex, visibleCount: nextWindow.visibleCount, itemCount: nextWindow.itemCount, isWindowed: nextWindow.isWindowed, source: "mainPlot", interaction: "pan" }); }, [onViewportChange]); const shouldSetResponder = useCallback((event) => canPan && isChartViewportEventInBounds({ bounds: plotBounds, event }), [canPan, plotBounds]); const handleGrant = useCallback((event) => { if (!canPan) { return false; } preventBrowserSelection(event); panStateRef.current = { lastWindow: viewportWindow, startLocationX: event.nativeEvent.locationX, startWindow: viewportWindow }; config.onGestureStart?.({ interaction: "pan" }); return true; }, [canPan, config, preventBrowserSelection, viewportWindow]); const handleMove = useCallback((event) => { const panState = panStateRef.current; if (!canPan || !panState) { return false; } preventBrowserSelection(event); const deltaX = Math.abs(event.nativeEvent.locationX - panState.startLocationX); if (deltaX < config.minPanDistance) { return true; } const deltaPoints = getChartViewportPanDeltaPoints({ currentLocationX: event.nativeEvent.locationX, plotWidth: plotBounds.width, startLocationX: panState.startLocationX, visibleCount: panState.startWindow.visibleCount }); if (deltaPoints === 0) { return true; } const nextWindow = resolveChartViewportWindowFromPanDelta({ currentWindow: panState.startWindow, deltaPoints, itemCount: dataLength }); if (isSameViewportWindow(nextWindow, panState.lastWindow)) { return true; } panStateRef.current = { ...panState, lastWindow: nextWindow }; emitViewportChange(nextWindow); return true; }, [ canPan, config.minPanDistance, dataLength, emitViewportChange, plotBounds.width, preventBrowserSelection ]); const handleEnd = useCallback(() => { if (!panStateRef.current) { return false; } panStateRef.current = undefined; config.onGestureEnd?.({ interaction: "pan" }); return true; }, [config]); const isActive = useCallback(() => panStateRef.current !== undefined, []); const hasChanged = useCallback(() => { const panState = panStateRef.current; return panState ? !isSameViewportWindow(panState.startWindow, panState.lastWindow) : false; }, []); return { hasChanged, handleEnd, handleGrant, handleMove, isActive, isEnabled: canPan, shouldBlockTermination: canPan && config.lockParentScroll, shouldSetResponder }; };