UNPKG

@gfazioli/mantine-onboarding-tour

Version:

A Mantine component enables you to create a onboarding-tour effect using overlays, popovers, and onboarding tours, which enhances element visibility and interactivity.

178 lines (175 loc) 5.75 kB
'use client'; import React, { useId, useState, useRef, useEffect, useMemo, cloneElement } from 'react'; import { useProps, useStyles, Popover, Transition, Overlay } from '@mantine/core'; import { useUncontrolled, useInViewport, useMergedRef, useDidUpdate } from '@mantine/hooks'; import { useScrollIntoView } from '../hooks/use-scroll-into-view/use-scroll-into-view.mjs'; import { OnboardingTourFocusRevealGroup } from '../OnboardingTourFocusRevealGroup/OnboardingTourFocusRevealGroup.mjs'; import { useOnboardingTourFocusRevealGroupContext } from '../OnboardingTourFocusRevealGroup/OnboardingTourFocusRevealGroup.context.mjs'; import classes from './OnboardingTourFocusReveal.module.css.mjs'; const defaultProps = { focusedMode: "none", withReveal: true, withOverlay: true, overlayProps: { blur: 2, backgroundOpacity: 0.5, color: "black" }, transitionProps: { transition: "fade", duration: 150 }, popoverProps: { position: "left", withArrow: true, arrowSize: 16, arrowRadius: 4, offset: -4, radius: "md", shadow: "xl" } }; function OnboardingTourFocusReveal(_props) { const ctx = useOnboardingTourFocusRevealGroupContext(); const props = useProps( "OnboardingTourFocusReveal", { ...defaultProps, ...ctx ? ctx : {} }, _props ); props.overlayProps = { ...defaultProps.overlayProps, ..._props.overlayProps }; props.transitionProps = { ...defaultProps.transitionProps, ..._props.transitionProps }; props.popoverProps = { ...defaultProps.popoverProps, ..._props.popoverProps }; props.defaultFocused = ctx && _props.defaultFocused === void 0 ? true : _props.defaultFocused; const { focused, defaultFocused, withOverlay, overlayProps, withReveal, revealProps, transitionProps, focusedMode, popoverContent, popoverProps, scrollableRef, onChange, onFocus, onBlur, onRevealFinish, classNames, style, styles, unstyled, vars, children, className } = props; const getStyles = useStyles({ name: "OnboardingTourFocusReveal", props, classes, className, style, classNames, styles, unstyled, vars }); const uuid = useId(); const [_focused, setFocused, controlled] = useUncontrolled({ value: focused, defaultValue: defaultFocused, finalValue: false, onChange }); const blur = () => { setFocused(false); _focused && onBlur?.(); }; const focus = () => { setFocused(true); !_focused && onFocus?.(); }; const [realFocused, setRealFocused] = useState(false); const fallbackRef = useRef(null); const { scrollIntoView, targetRef } = useScrollIntoView({ ...revealProps, onScrollFinish: onRevealFinish, scrollableRef: scrollableRef || fallbackRef }); const { ref: inViewportRef, inViewport } = useInViewport(); const mergedRef = useMergedRef(inViewportRef, targetRef); useEffect(() => { ctx?.setMeInViewport(uuid, inViewport); if (targetRef.current) { if (_focused || defaultFocused) { if (withReveal) { scrollIntoView({ alignment: "center" }); } } if (_focused && inViewport && !realFocused) { setRealFocused(true); setFocused(_focused && inViewport); } else if (!_focused && realFocused) { setRealFocused(false); } } }, [_focused, withReveal, inViewport]); useDidUpdate(() => { ctx?.setMeInViewport(uuid, inViewport); if (_focused && !inViewport) { blur(); setRealFocused(false); ctx?.setRequestOverlay(false); } if (inViewport && !controlled && defaultFocused === true) { focus(); setRealFocused(true); ctx?.setRequestOverlay(true); } }, [inViewport, defaultFocused]); const clonedChildren = useMemo(() => { const child = children; const newProps = { ...realFocused && { "data-onboarding-tour-focus-reveal-focused": true }, "data-onboarding-tour-focus-reveal-mode": focusedMode, ref: mergedRef, className: [realFocused ? classes.focused : "", className, child.props.className].filter(Boolean).join(" "), style: { position: "relative", ...child.props.style, zIndex: realFocused ? 201 : 0 } }; newProps["data-popover-dropdown"] = !!popoverContent; return /* @__PURE__ */ React.createElement(Popover, { opened: realFocused && !!popoverContent, withinPortal: false, ...popoverProps }, /* @__PURE__ */ React.createElement(Popover.Target, null, cloneElement(child, newProps)), /* @__PURE__ */ React.createElement(Popover.Dropdown, null, popoverContent)); }, [children, realFocused, _focused, inViewport, defaultFocused]); if (ctx) { return clonedChildren; } return /* @__PURE__ */ React.createElement(React.Fragment, null, withOverlay && /* @__PURE__ */ React.createElement( Transition, { transition: transitionProps?.transition, mounted: _focused && inViewport, duration: transitionProps?.duration, exitDuration: transitionProps?.exitDuration }, (transitionStyles) => /* @__PURE__ */ React.createElement( Overlay, { "data-onboarding-tour-focus-reveal-overlay": true, ...overlayProps, ...getStyles("overlay", { className: overlayProps?.className, style: [transitionStyles, overlayProps?.style] }) } ) ), clonedChildren); } OnboardingTourFocusReveal.classes = classes; OnboardingTourFocusReveal.displayName = "OnboardingTourFocusReveal"; OnboardingTourFocusReveal.Group = OnboardingTourFocusRevealGroup; export { OnboardingTourFocusReveal, defaultProps }; //# sourceMappingURL=OnboardingTourFocusReveal.mjs.map