import React from 'react'; import { sendGAEvent } from '@next/third-parties/google'; import type { SxProps } from '@mui/joy/styles/types'; import { Box, Button, Step, stepClasses, StepIndicator, stepIndicatorClasses, Stepper, Typography } from '@mui/joy'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; import ArrowForwardRoundedIcon from '@mui/icons-material/ArrowForwardRounded'; import CheckRoundedIcon from '@mui/icons-material/CheckRounded'; import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'; import { AutoBlocksRenderer } from '~/modules/blocks/AutoBlocksRenderer'; import { AgiSquircleIcon } from '~/common/components/icons/AgiSquircleIcon'; import { ChatBeamIcon } from '~/common/components/icons/ChatBeamIcon'; import { ShortcutKey, useGlobalShortcuts } from '~/common/components/shortcuts/useGlobalShortcuts'; import { animationTextShadowLimey } from '~/common/util/animUtils'; import { hasGoogleAnalytics } from '~/common/components/GoogleAnalytics'; import { useIsMobile } from '~/common/components/useMatchMedia'; import { useUIContentScaling } from '~/common/state/store-ui'; // configuration const colorButtons = 'neutral' as const; const colorStepper = 'neutral' as const; // Steps - the top stepper interface ExplainerStep { stepDigits: string, stepName: string, } const stepSequenceSx: SxProps = { // width: '100%', [`& .${stepClasses.completed}::after`]: { bgcolor: `${colorStepper}.500`, }, [`& .${stepClasses.active} .${stepIndicatorClasses.root}`]: { borderColor: `${colorStepper}.500`, }, [`& .${stepClasses.root}:has(+ .${stepClasses.active})::after`]: { color: `${colorStepper}.500`, backgroundColor: 'transparent', backgroundImage: 'radial-gradient(currentColor 2px, transparent 2px)', backgroundSize: '7px 7px', backgroundPosition: 'center left', }, }; const buttonBaseSx: SxProps = { justifyContent: 'space-between', minHeight: '2.5rem', minWidth: 120, }; const buttonNextSx: SxProps = { ...buttonBaseSx, boxShadow: `0 8px 24px -4px rgb(var(--joy-palette-${colorButtons}-mainChannel) / 20%)`, minWidth: 180, }; function AllStepsStepper(props: { steps: ExplainerStep[], activeIndex: number, isMobile: boolean, onStepClicked: (stepIndex: number) => void, }) { return ( {props.steps.map(((step, stepIndex) => { const completed = props.activeIndex > stepIndex; const active = props.activeIndex === stepIndex; return ( props.onStepClicked(stepIndex)} sx={{ cursor: 'pointer' }} > {completed ? : active ? : undefined} } > {step.stepName} } >{step.stepDigits ?? null} ); }))} ); } // The Explainer - Carousel of pages export interface ExplainerPage extends ExplainerStep { titlePrefix?: string, titleSquircle?: boolean, titleSpark?: string, titleSuffix?: string, mdContent: string } export function ExplainerCarousel(props: { explainerId: string, steps: ExplainerPage[], footer?: React.ReactNode, noStepper?: boolean, onFinished: () => any, }) { // state const [stepIndex, setStepIndex] = React.useState(0); // external state const isMobile = useIsMobile(); const contentScaling = useUIContentScaling(); // derived state const { onFinished } = props; const isFirstPage = stepIndex === 0; const isLastPage = stepIndex === props.steps.length - 1; const activeStep = props.steps[stepIndex] ?? null; // handlers const mdText = activeStep?.mdContent ?? null; const handlePrevPage = React.useCallback(() => { setStepIndex(step => step > 0 ? step - 1 : step); }, []); const handleNextPage = React.useCallback(() => { if (isLastPage) { hasGoogleAnalytics && sendGAEvent('event', 'tutorial_complete', { tutorial_id: props.explainerId }); onFinished(); } else setStepIndex(step => step < props.steps.length - 1 ? step + 1 : step); }, [isLastPage, onFinished, props.explainerId, props.steps.length]); React.useEffect(() => { const recordTutorialBegun = () => { hasGoogleAnalytics && sendGAEvent('event', 'tutorial_begin', { tutorial_id: props.explainerId }); }; const timeoutId = setTimeout(recordTutorialBegun, 500); return () => clearTimeout(timeoutId); }, [props.explainerId]); useGlobalShortcuts('ExplainerCarousel', React.useMemo(() => [ { key: ShortcutKey.Left, action: handlePrevPage }, { key: ShortcutKey.Right, action: handleNextPage }, ], [handleNextPage, handlePrevPage])); // [effect] restart from 0 if steps change // React.useEffect(() => { // setStepIndex(0); // }, [props.steps]); return ( {/* Page Title */} {activeStep?.titlePrefix}{' '} {!!activeStep?.titleSquircle && } {!!activeStep?.titleSquircle && '-'} {!!activeStep?.titleSpark && {activeStep.titleSpark} }{activeStep?.titleSuffix} {/* Page Message */} {/* Main Card with the markdown body */} {!!mdText && ( )} {/* Advance Button */} {/* Back Button */} {/* All Steps */} {props.noStepper ? null : ( )} {/* Final words of wisdom (also perfect for centering the other components) */} {props.footer} ); }