import React, { useRef, useEffect, isValidElement, useMemo } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import introJS, { IntroJs } from 'intro.js'; import { IntroProps } from './xui-intro.d'; const XuiIntro: React.FC = ({ enabled, initialStep, options, steps, onChange = () => {}, onComplete = () => {}, onExit = () => {}, }) => { const introJs = useRef(null); const isConfigured = useRef(false); const isVisible = useRef(false); const defalutOptions = useMemo( () => ({ prevLabel: '上一步', nextLabel: '下一步', skipLabel: '跳过', doneLabel: '完成', showStepNumbers: false, overlayOpacity: 0.45, showBullets: false, highlightClass: 'xui-ant__intro--highlightClass', tooltipClass: 'xui-ant__intro--tooltipClass', exitOnOverlayClick: false, }), [], ); const configureIntroJs = () => { const sanitizedSteps = steps.map(step => { if (isValidElement(step.intro)) { return { ...step, intro: renderToStaticMarkup(step.intro), }; } return step; }); const introJsCurrent: IntroJs = introJs.current; introJsCurrent.setOptions({ ...defalutOptions, ...options, steps: sanitizedSteps }); isConfigured.current = true; }; const renderSteps = () => { if (enabled && steps.length > 0 && !isVisible.current) { const introJsCurrent: IntroJs = introJs.current; introJsCurrent.start(); isVisible.current = true; introJsCurrent.goToStepNumber(initialStep + 1); } else if (!enabled && isVisible.current) { isVisible.current = false; introJs.current.exit(); } }; const handleExit = () => { isVisible.current = false; onExit(); }; const handleChange = (element: any) => { if (!isVisible.current) { return; } if (onChange) { onChange(element); } }; const handleComplete = () => { if (onComplete) { onComplete(); } }; useEffect(() => { introJs.current = introJS(); const introJsCurrent: IntroJs = introJs.current; introJsCurrent.onexit(handleExit); introJsCurrent.onchange(handleChange); introJsCurrent.oncomplete(handleComplete); return () => { introJsCurrent.exit(); }; }, []); useEffect(() => { if (enabled) { configureIntroJs(); renderSteps(); } }, [enabled, steps, options]); return null; }; export default XuiIntro;