11"use client" ;
22
3- import { useState } from "react" ;
3+ import { useState , useRef , useEffect } from "react" ;
44import { cn } from "@/lib/utils" ;
55import { Copy , Check , RotateCw } from "lucide-react" ;
66import { motion } from "framer-motion" ;
@@ -25,6 +25,38 @@ export function DemoTabs({
2525 const [ isRefreshing , setIsRefreshing ] = useState ( false ) ;
2626 const [ isDemoing , setIsDemoing ] = useState ( false ) ;
2727
28+ // Clip-path animation state
29+ const [ clipPath , setClipPath ] = useState ( "inset(0 100% 0 0 round 9999px)" ) ;
30+ const containerRef = useRef < HTMLDivElement > ( null ) ;
31+ const buttonRefs = useRef < ( HTMLButtonElement | null ) [ ] > ( [ ] ) ;
32+
33+ useEffect ( ( ) => {
34+ const updateClipPath = ( ) => {
35+ if ( ! containerRef . current ) return ;
36+
37+ const modeIndex = ( Object . keys ( modes ) as DemoMode [ ] ) . indexOf ( mode ) ;
38+ const activeEl = buttonRefs . current [ modeIndex ] ;
39+
40+ if ( activeEl ) {
41+ const containerRect = containerRef . current . getBoundingClientRect ( ) ;
42+ const activeRect = activeEl . getBoundingClientRect ( ) ;
43+
44+ // Calculate relative positions
45+ const left = activeRect . left - containerRect . left ;
46+ const right = containerRect . width - ( left + activeRect . width ) ;
47+
48+ setClipPath ( `inset(0 ${ right } px 0 ${ left } px round 9999px)` ) ;
49+ }
50+ } ;
51+
52+ // Initial update and on mode change
53+ updateClipPath ( ) ;
54+
55+ // Update on resize
56+ window . addEventListener ( 'resize' , updateClipPath ) ;
57+ return ( ) => window . removeEventListener ( 'resize' , updateClipPath ) ;
58+ } , [ mode ] ) ;
59+
2860 const handleRefresh = ( ) => {
2961 setIsRefreshing ( true ) ;
3062 setIsDemoing ( true ) ;
@@ -71,38 +103,6 @@ export function DemoTabs({
71103 setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
72104 } ;
73105
74- return (
75- const [ clipPath , setClipPath ] = useState ( "inset(0 100% 0 0 round 9999px)" ) ;
76- const containerRef = useRef < HTMLDivElement > ( null ) ;
77- const buttonRefs = useRef < ( HTMLButtonElement | null ) [ ] > ( [ ] ) ;
78-
79- useEffect ( ( ) => {
80- const updateClipPath = ( ) => {
81- if ( ! containerRef . current ) return ;
82-
83- const modeIndex = ( Object . keys ( modes ) as DemoMode [ ] ) . indexOf ( mode ) ;
84- const activeEl = buttonRefs . current [ modeIndex ] ;
85-
86- if ( activeEl ) {
87- const containerRect = containerRef . current . getBoundingClientRect ( ) ;
88- const activeRect = activeEl . getBoundingClientRect ( ) ;
89-
90- // Calculate relative positions
91- const left = activeRect . left - containerRect . left ;
92- const right = containerRect . width - ( left + activeRect . width ) ;
93-
94- setClipPath ( `inset(0 ${ right } px 0 ${ left } px round 9999px)` ) ;
95- }
96- } ;
97-
98- // Initial update and on mode change
99- updateClipPath ( ) ;
100-
101- // Update on resize
102- window . addEventListener ( 'resize' , updateClipPath ) ;
103- return ( ) => window . removeEventListener ( 'resize' , updateClipPath ) ;
104- } , [ mode ] ) ;
105-
106106 return (
107107 < div className = "w-full max-w-5xl mx-auto" >
108108 < div className = "grid grid-cols-1 lg:grid-cols-2 divide-y lg:divide-y-0 lg:divide-x divide-neutral-200 dark:divide-neutral-800 border border-neutral-200 dark:border-neutral-800 rounded-xl bg-white dark:bg-neutral-950 shadow-sm overflow-hidden" >
@@ -218,7 +218,7 @@ export function DemoTabs({
218218 { ( Object . keys ( modes ) as DemoMode [ ] ) . map ( ( key ) => (
219219 < button
220220 key = { `active-${ key } ` }
221- className = "px-4 py-2 rounded-full text-xs font-medium whitespace-nowrap text-white shadow-sm ring-1 ring-white/10 "
221+ className = "px-4 py-2 rounded-full text-xs font-medium whitespace-nowrap text-white"
222222 aria-hidden = "true"
223223 tabIndex = { - 1 }
224224 >
0 commit comments