import React, { useCallback, useImperativeHandle, useRef, useState, } from "react"; import { LayoutRectangle } from "react-native"; import { rgbaArray } from "react-native-svg"; import { ChildFn, isChildFunction, isPromise } from "../helpers/common"; import { SpotlightTour, SpotlightTourContext, SpotlightTourCtx, TourStep, } from "./SpotlightTour.context"; import { TourOverlay, TourOverlayRef, } from "./tour-overlay/TourOverlay.component"; interface SpotlightTourProviderProps { children: React.ReactNode | ChildFn; overlayColor?: string | number | rgbaArray; overlayOpacity?: number | string; steps: TourStep[]; onClose?: () => void; onBackdropPress: () => void; } export const SpotlightTourProvider = React.forwardRef< SpotlightTour, SpotlightTourProviderProps >((props, ref) => { const { children, overlayColor, overlayOpacity, steps, onClose, onBackdropPress, } = props; const [current, setCurrent] = useState(); const [spot, setSpot] = useState(); const overlayRef = useRef(null); const renderStep = useCallback( (index: number) => { if (steps[index] !== undefined) { const beforeHook = steps[index].before?.(); const beforePromise = isPromise(beforeHook) ? beforeHook : Promise.resolve(); return Promise.all([beforePromise, overlayRef.current?.hideTip()]).then( () => setCurrent(index) ); } return Promise.resolve(); }, [steps] ); const changeSpot = (newSpot: LayoutRectangle) => { setSpot(newSpot); }; const start = (index?: number) => { renderStep(index ?? 0); }; const stop = () => { setCurrent(undefined); onClose?.(); }; const onBackdropTap = () => { onBackdropPress(); stop(); }; const next = () => { if (current !== undefined && current < steps.length - 1) { renderStep(current + 1); } }; const previous = () => { if (current !== undefined && current > 0) { renderStep(current - 1); } }; const goTo = (index: number) => { renderStep(index); }; const tour: SpotlightTourCtx = { changeSpot, current, goTo, next, onBackdropTap, previous, spot, start, steps, stop, }; useImperativeHandle(ref, () => ({ current, goTo, next, onBackdropTap, previous, start, stop, })); return ( {isChildFunction(children) ? ( {children} ) : ( <>{children} )} ); });