manuthecoder-react-native-spotlight-tour
Version:
React Native library to implement a highly customizable app tour feature with an awesome spotlight effect
122 lines • 4.56 kB
JavaScript
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState, } from "react";
import { isChildFunction } from "../helpers/common";
import { SpotlightTourContext, ZERO_SPOT, } from "./SpotlightTour.context";
import { TourOverlay } from "./components/tour-overlay/TourOverlay.component";
/**
* React provider component to get access to the SpotlightTour context.
*/
export const SpotlightTourProvider = forwardRef((props, ref) => {
const { arrow, children, flip, motion = "bounce", nativeDriver = true, offset, onBackdropPress, onPause, onResume, onStop, overlayColor = "black", overlayOpacity = 0.45, placement, shape = "circle", shift, steps, } = props;
const [current, setCurrent] = useState();
const [spot, setSpot] = useState(ZERO_SPOT);
const [pausedAt, setPausedAt] = useState();
const overlay = useRef({
hideTooltip: () => Promise.resolve({ finished: false }),
});
const status = useMemo(() => {
if (current === undefined) {
return pausedAt !== undefined
? "paused"
: "idle";
}
return "running";
}, [current, pausedAt]);
const currentStep = useMemo(() => {
const step = current !== undefined
? steps[current]
: undefined;
return step ?? { render: () => _jsx(_Fragment, {}) };
}, [steps, current]);
const renderStep = useCallback((index) => {
const step = steps[index];
if (step !== undefined) {
Promise.all([
overlay.current.hideTooltip(),
Promise.resolve().then(step.before),
])
.then(() => setCurrent(index));
}
}, [steps]);
const changeSpot = useCallback((newSpot) => {
setSpot(newSpot);
}, []);
const start = useCallback(() => {
renderStep(0);
}, [renderStep]);
const stop = useCallback(() => {
setCurrent(prev => {
if (prev !== undefined) {
onStop?.({
index: prev,
isLast: prev === steps.length - 1,
});
}
return undefined;
});
setSpot(ZERO_SPOT);
}, [onStop]);
const next = useCallback(() => {
if (current !== undefined) {
current === steps.length - 1
? stop()
: renderStep(current + 1);
}
}, [stop, renderStep, current, steps.length]);
const previous = useCallback(() => {
if (current !== undefined && current > 0) {
renderStep(current - 1);
}
}, [renderStep, current]);
const goTo = useCallback((index) => {
renderStep(index);
}, [renderStep]);
const pause = useCallback(() => {
setCurrent(prev => {
if (prev !== undefined) {
setPausedAt(prev);
onPause?.({
index: prev,
isLast: prev === steps.length - 1,
});
}
return undefined;
});
}, [onPause, steps.length]);
const resume = useCallback(() => {
const index = pausedAt ?? 0;
const isLast = index === steps.length - 1;
goTo(index);
setPausedAt(undefined);
onResume?.({ index, isLast });
}, [goTo, onResume, pausedAt, steps.length]);
const tour = useMemo(() => ({
changeSpot,
current,
goTo,
next,
pause,
previous,
resume,
spot,
start,
status,
steps,
stop,
}), [changeSpot, current, goTo, next, previous, spot, start, steps, stop, pause]);
useImperativeHandle(ref, () => ({
current,
goTo,
next,
pause,
previous,
resume,
start,
status,
stop,
}));
return (_jsxs(SpotlightTourContext.Provider, { value: tour, children: [isChildFunction(children)
? _jsx(SpotlightTourContext.Consumer, { children: children })
: _jsx(_Fragment, { children: children }), _jsx(TourOverlay, { backdropOpacity: overlayOpacity, color: overlayColor, current: current, motion: motion, nativeDriver: nativeDriver, onBackdropPress: onBackdropPress, ref: overlay, shape: shape, spot: spot, tourStep: currentStep, arrow: arrow, flip: flip, offset: offset, placement: placement, shift: shift })] }));
});
//# sourceMappingURL=SpotlightTour.provider.js.map