Skip to main content
Glama
index.tsx7.36 kB
'use client'; import { Link } from '@components/Link/Link'; import { useDevice, useScreenWidth } from '@intlayer/design-system/hooks'; import { cn } from '@utils/cn'; import { type MotionValue, motion, useMotionValueEvent, useScroll, useTransform, } from 'framer-motion'; import { useIntlayer } from 'next-intlayer'; import type { CSSProperties, FC, SVGProps } from 'react'; import { useRef, useState } from 'react'; import { PagesRoutes } from '@/Routes'; import { AngularLogo } from './Angular'; import { NextJSLogo } from './Nextjs'; import { NuxtLogo } from './Nuxt'; import { PreactLogo } from './Preact'; import { ReactLogo } from './Reactjs'; import { SolidLogo } from './Solid'; import { SvelteLogo } from './Svelte'; import { ViteLogo } from './Vitejs'; import { VuejsLogo } from './Vuejs'; const BASE_SCREEN_WIDTH = 1500; type LogoConfig = { Logo: FC<SVGProps<SVGSVGElement>>; route: string; initialPost: { scale: number; x: number; y: number; }; label: string; }; type LogoItemProps = LogoConfig & { animationProgress: MotionValue<number>; outputRange: [number, number]; label: string; logoClassName?: string; getXPosition: (value: number) => number; isMobile?: boolean; animationDelay: number; }; const LogoItem: FC<LogoItemProps> = ({ Logo, route, initialPost, animationProgress, outputRange, label, logoClassName, getXPosition, isMobile, animationDelay, }) => { const x = useTransform(animationProgress, outputRange, [ getXPosition(initialPost.x), 0, ]); const y = useTransform(animationProgress, outputRange, [initialPost.y, 0]); const scale = useTransform(animationProgress, outputRange, [ initialPost.scale, 1, ]); const isEnabled = useTransform(animationProgress, outputRange, [true, false]); const [isFloating, setIsFloating] = useState(isEnabled.get()); useMotionValueEvent(isEnabled, 'change', (latest) => { setIsFloating(latest); }); return ( <motion.div style={{ x, y, scale: isMobile ? 1 : scale, }} > <Link href={route} color="custom" label={label}> <Logo className={cn( 'size-14 transition-transform duration-200 hover:scale-110', isFloating && 'animate-float', logoClassName )} style={ { animationDelay: `${animationDelay}s`, } as CSSProperties } /> </Link> </motion.div> ); }; const logosRow1 = [ { Logo: ReactLogo, route: PagesRoutes.Doc_Environment_ViteAndReact, initialPost: { scale: 1.5, x: -200, y: -60, }, label: 'react', }, { Logo: PreactLogo, route: PagesRoutes.Doc_Environment_ViteAndPreact, initialPost: { scale: 1.4, x: 200, y: -60, }, label: 'preact', }, ] as const; const logosRow2 = [ { Logo: VuejsLogo, route: PagesRoutes.Doc_Environment_ViteAndVue, initialPost: { scale: 1.5, x: -350, y: -30, }, label: 'vue', }, { Logo: NuxtLogo, route: PagesRoutes.Doc_Environment_NuxtAndVue, initialPost: { scale: 1.3, x: 0, y: 60, }, label: 'nuxt', }, { Logo: SvelteLogo, route: PagesRoutes.Doc_Environment_ViteAndSvelte, initialPost: { scale: 1.3, x: 350, y: -30, }, label: 'svelte', }, ] as const; const logosRow3 = [ { Logo: NextJSLogo, route: PagesRoutes.Doc_Environment_NextJS_15, initialPost: { scale: 1.5, x: -250, y: 60, }, label: 'nextjs', }, { Logo: ViteLogo, route: PagesRoutes.Doc_Environment_ViteAndReact, initialPost: { scale: 1.5, x: 250, y: 60, }, label: 'vite', }, ] as const; const comingSoonData = [ { Logo: SolidLogo, route: PagesRoutes.Doc_Environment_ViteAndSolid, initialPost: { scale: 1.2, x: -80, y: 40, }, label: 'solid', }, { Logo: AngularLogo, route: PagesRoutes.Doc_Environment_Angular, initialPost: { scale: 1.2, x: 80, y: 40, }, label: 'angular', }, ] as const; export const AvailableTechnoSection: FC = () => { const { comingSoon, availableOn, icons } = useIntlayer( 'available-techno-section' ); const { isMobile } = useDevice(); const { screenWidth } = useScreenWidth(); const containerRef = useRef(null); const { scrollYProgress } = useScroll({ target: containerRef, offset: ['start end', 'end start'], }); // Animation progress that starts at 50% scroll const animationProgress = useTransform(scrollYProgress, [0.4, 1], [0, 1]); const getXPosition = (index: number) => index * (screenWidth / BASE_SCREEN_WIDTH); return ( <section ref={containerRef} className="z-10 flex w-full flex-col items-center justify-center" > <h2 className="mb-3 text-lg text-neutral-500">{availableOn}</h2> <div className="mt-4 flex flex-col items-center gap-6"> {/* Row 1 */} <motion.div className="flex justify-center gap-x-12"> {logosRow1.map((logoConfig, index) => ( <LogoItem key={logoConfig.label} {...logoConfig} animationProgress={animationProgress} outputRange={[0, 0.1]} animationDelay={index * 0.15} label={icons[logoConfig.label].label.value} getXPosition={getXPosition} isMobile={isMobile} /> ))} </motion.div> {/* Row 2 */} <motion.div className="flex justify-center gap-x-12"> {logosRow2.map((logoConfig, index) => ( <LogoItem key={logoConfig.label} {...logoConfig} animationProgress={animationProgress} outputRange={[0.05, 0.15]} animationDelay={index * 0.15} label={icons[logoConfig.label].label.value} getXPosition={getXPosition} isMobile={isMobile} /> ))} </motion.div> {/* Row 3 */} <motion.div className="flex justify-center gap-x-12"> {logosRow3.map((logoConfig, index) => ( <LogoItem key={logoConfig.label} {...logoConfig} animationProgress={animationProgress} outputRange={[0.1, 0.2]} animationDelay={index * 0.15} label={icons[logoConfig.label].label.value} getXPosition={getXPosition} isMobile={isMobile} /> ))} </motion.div> </div> <h2 className="mt-8 mb-3 text-lg text-neutral-500">{comingSoon}</h2> <motion.div className="mt-4 flex justify-center gap-x-12"> {comingSoonData.map((logoConfig, index) => ( <LogoItem key={logoConfig.label} {...logoConfig} animationProgress={animationProgress} outputRange={[0.15, 0.25]} animationDelay={index * 0.15} label={icons[logoConfig.label].label.value} logoClassName="grayscale-70" getXPosition={getXPosition} isMobile={isMobile} /> ))} </motion.div> </section> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server