'use client' import { Fragment, useEffect, useId, useRef, useState } from 'react' import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react' import clsx from 'clsx' import { type MotionProps, type Variant, AnimatePresence, motion, } from 'framer-motion' import { useDebouncedCallback } from 'use-debounce' import { AppScreen } from './AppScreen' import { Eyebrow, FeatureDescription, FeatureTitle, MobileFeatureTitle, P, SectionHeader, } from '@/components/Texts' import { CircleBackground } from '@/components/CircleBackground' import { Container } from '@/components/Container' import connectorImg from '@/images/connector.png' import peersImg from '@/images/peers.png' import settingImg from '@/images/setting.png' import { PhoneFrame } from '@/components/PhoneFrame' interface CustomAnimationProps { isForwards: boolean changeCount: number } const features = [ { name: 'Mycelium Connector', description: "Start (and stop) your Mycelium connector to gain access to sites, apps, and workloads available exclusively on the Mycelium Network. View statistics around peers and traffic.", icon: DeviceUserIcon, screen: InviteScreen, }, { name: 'Mycelium Peers', description: 'Search and discover active peers on the Mycelium Network, or add your own.', icon: DeviceNotificationIcon, screen: StocksScreen, }, { name: 'Network Setting', description: 'Find version and network information and trigger light or dark mode.', icon: DeviceTouchIcon, screen: InvestScreen, }, ] function DeviceUserIcon(props: React.ComponentPropsWithoutRef<'svg'>) { return ( ) } function DeviceNotificationIcon(props: React.ComponentPropsWithoutRef<'svg'>) { return ( ) } function DeviceTouchIcon(props: React.ComponentPropsWithoutRef<'svg'>) { let id = useId() return ( ) } const maxZIndex = 2147483647 const bodyVariantBackwards: Variant = { opacity: 0.4, scale: 0.8, zIndex: 0, filter: 'blur(4px)', transition: { duration: 0.4 }, } const bodyAnimation: MotionProps = { initial: 'initial', animate: 'animate', exit: 'exit', variants: { initial: (custom: CustomAnimationProps) => ( custom.isForwards ? { y: '100%', zIndex: maxZIndex - custom.changeCount, transition: { duration: 0.4 }, } : bodyVariantBackwards ), animate: (custom: CustomAnimationProps) => ({ y: '0%', opacity: 1, scale: 1, zIndex: maxZIndex / 2 - custom.changeCount, filter: 'blur(0px)', transition: { duration: 0.4 }, }), exit: (custom: CustomAnimationProps) => ( custom.isForwards ? bodyVariantBackwards : { y: '100%', zIndex: maxZIndex - custom.changeCount, transition: { duration: 0.4 }, } ), }, } function InviteScreen() { return ( Mycelium Connector ) } function StocksScreen() { return ( Mycelium Peers ) } function InvestScreen() { return ( Mycelium Settings ) } function usePrevious(value: T) { const ref = useRef() useEffect(() => { ref.current = value }, [value]) return ref.current } function FeaturesDesktop() { let [changeCount, setChangeCount] = useState(0) let [selectedIndex, setSelectedIndex] = useState(0) let prevIndex = usePrevious(selectedIndex) let isForwards = prevIndex === undefined ? true : selectedIndex > prevIndex let onChange = useDebouncedCallback( (selectedIndex: number) => { setSelectedIndex(selectedIndex) setChangeCount((changeCount) => changeCount + 1) }, 100, { leading: true }, ) return ( {features.map((feature, featureIndex) => (
{featureIndex === selectedIndex && ( )}
{feature.name} {feature.description}
))}
{features.map((feature, featureIndex) => selectedIndex === featureIndex ? ( ) : null, )}
) } function FeaturesMobile() { let [activeIndex, setActiveIndex] = useState(0) let slideContainerRef = useRef>(null) let slideRefs = useRef>>([]) useEffect(() => { let observer = new window.IntersectionObserver( (entries) => { for (let entry of entries) { if (entry.isIntersecting && entry.target instanceof HTMLDivElement) { setActiveIndex(slideRefs.current.indexOf(entry.target)) break } } }, { root: slideContainerRef.current, threshold: 0.6, }, ) for (let slide of slideRefs.current) { if (slide) { observer.observe(slide) } } return () => { observer.disconnect() } }, [slideContainerRef, slideRefs]) return ( <>
{features.map((feature, featureIndex) => (
ref && (slideRefs.current[featureIndex] = ref)} className="w-full flex-none snap-center px-4 sm:px-6 transition-all duration-300 ease-in-out hover:scale-105" >
{feature.name} {feature.description}
))}
{features.map((_, featureIndex) => ( ))}
) } export function PrimaryFeatures() { return (
How It Works How Mycelium Operates

Mycelium, like its natural namesake, thrives on decentralization, efficiency, and security, making it a truly powerful force in the world of decentralized networks.

) }