// app/components/ui/macbook-scroll.tsx import React, { useEffect, useRef, useState } from "react"; import { MotionValue, motion, useScroll, useTransform } from "motion/react"; import { cn } from "~/lib/utils"; import { Monitor, MonitorSpeaker, Sun, Moon, Search, Mic, SkipBack, Play, SkipForward, Volume, Volume1, Volume2, VolumeX, Globe, Command, ChevronUp, ChevronLeft, ChevronRight, ChevronDown, Grid3x3 } from "lucide-react"; interface MacbookScrollProps { src?: string; showGradient?: boolean; title?: string | React.ReactNode; badge?: React.ReactNode; className?: string; // Target landing - best practice approach landingTargetId?: string; landingOffset?: number; } export const MacbookScroll = ({ src, showGradient = true, title, badge, className, landingTargetId, landingOffset = 0 }: MacbookScrollProps) => { const ref = useRef(null); const [landingDistance, setLandingDistance] = useState(800); const { scrollYProgress } = useScroll({ target: ref, offset: ["start start", "end start"], }); // Responsive landing calculation - best practice useEffect(() => { const calculateLanding = () => { if (!landingTargetId) return; const target = document.getElementById(landingTargetId); const container = ref.current; if (target && container) { const containerRect = container.getBoundingClientRect(); const targetRect = target.getBoundingClientRect(); const scrollTop = window.pageYOffset || document.documentElement.scrollTop; // Calculate absolute positions const containerTop = containerRect.top + scrollTop; const targetTop = targetRect.top + scrollTop; // Distance from container to target const distance = targetTop - containerTop + landingOffset; // Mobile responsive adjustment - more aggressive const isMobile = window.innerWidth < 1024; // Changed from 768 to 1024 const finalDistance = isMobile ? distance * 2.5 : distance; // More reduction for mobile setLandingDistance(Math.max(finalDistance, 200)); } }; calculateLanding(); const handleResize = () => calculateLanding(); const handleLoad = () => calculateLanding(); window.addEventListener('resize', handleResize); window.addEventListener('load', handleLoad); return () => { window.removeEventListener('resize', handleResize); window.removeEventListener('load', handleLoad); }; }, [landingTargetId, landingOffset]); // Simple transforms const scaleX = useTransform(scrollYProgress, [0, 0.3], [1.2, 1.5]); const scaleY = useTransform(scrollYProgress, [0, 0.3], [0.6, 1.5]); const translate = useTransform(scrollYProgress, [0, 1], [0, landingDistance]); const rotate = useTransform(scrollYProgress, [0.1, 0.12, 0.3], [-28, -28, 0]); const textTransform = useTransform(scrollYProgress, [0, 0.3], [0, 100]); const textOpacity = useTransform(scrollYProgress, [0, 0.2], [1, 0]); return (
{/* Menggunakan CSS variables untuk responsive - best practice Remix */} {title || ( This Macbook is built with Tailwindcss.
No kidding.
)}
{/* Lid */} {/* Base - responsive sizing */}
{/* Keyboard bar */}
{showGradient && (
)} {badge &&
{badge}
}
); }; export const Lid = ({ scaleX, scaleY, rotate, translate, src, }: { scaleX: MotionValue; scaleY: MotionValue; rotate: MotionValue; translate: MotionValue; src?: string; }) => { return (
{src && ( screen content )}
); }; export const Trackpad = () => { return (
); }; export const Keypad = () => { return (
{/* First Row */}
esc F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12
{/* Second row */}
~ ` ! 1 @ 2 # 3 $ 4 % 5 ^ 6 & 7 * 8 ( 9 ) 0 _ - + = delete
{/* Third row */}
tab Q W E R T Y U I O P {`{`} {`[`} {`}`} {`]`} {`|`} {`\\`}
{/* Fourth Row */}
caps lock A S D F G H J K L {`:`} {`;`} {`"`} {`'`} return
{/* Fifth Row */}
shift Z X C V B N M {`<`} {`,`} {`>`} {`.`} {`?`} {`/`} shift
{/* Sixth Row */}
fn
control
option
command
command
option
); }; export const KBtn = ({ className, children, childrenClassName, backlit = true, }: { className?: string; children?: React.ReactNode; childrenClassName?: string; backlit?: boolean; }) => { return (
{children}
); }; export const SpeakerGrid = () => { return (
); }; export const OptionKey = ({ className }: { className: string }) => { return ( ); };