@@ -17,6 +17,9 @@ import {
1717} from "./gsapAnimationConstants" ;
1818import { buildTweenSummary } from "./gsapAnimationHelpers" ;
1919import { EaseCurveSection } from "./EaseCurveSection" ;
20+ import { ArcPathControls } from "./ArcPathControls" ;
21+ import type { ArcPathSegment } from "@hyperframes/core/gsap-parser" ;
22+ import { P } from "./panelTokens" ;
2023const BOOLEAN_PROPS = new Set ( [ "visibility" ] ) ;
2124const STRING_PROPS = new Set ( [ "filter" , "clipPath" ] ) ;
2225
@@ -97,11 +100,18 @@ function PropertyRow({
97100 < button
98101 type = "button"
99102 onClick = { ( ) => onCommit ( isVisible ? "hidden" : "visible" ) }
100- className = { `flex-shrink-0 w-7 h-4 rounded-full transition-colors relative ${ isVisible ? "bg-emerald-500/30" : "bg-neutral-700" } ` }
103+ className = { `flex-shrink-0 rounded-full transition-all duration-150 relative` }
104+ style = { { width : 28 , height : 16 , background : isVisible ? P . accent : P . borderInput } }
101105 title = { isVisible ? "Visible — click to hide" : "Hidden — click to show" }
102106 >
103107 < span
104- className = { `absolute top-0.5 h-3 w-3 rounded-full transition-transform ${ isVisible ? "bg-emerald-400 translate-x-3.5" : "bg-neutral-500 translate-x-0.5" } ` }
108+ className = "absolute top-[2px] left-0 rounded-full transition-transform duration-150"
109+ style = { {
110+ width : 12 ,
111+ height : 12 ,
112+ background : isVisible ? P . white : P . textMuted ,
113+ transform : isVisible ? "translateX(14px)" : "translateX(2px)" ,
114+ } }
105115 />
106116 </ button >
107117 </ div >
@@ -241,6 +251,15 @@ interface AnimationCardProps {
241251 onRemoveFromProperty ?: ( animationId : string , property : string ) => void ;
242252 onLivePreview ?: ( property : string , value : number | string ) => void ;
243253 onLivePreviewEnd ?: ( ) => void ;
254+ onSetArcPath ?: (
255+ animationId : string ,
256+ config : { enabled : boolean ; autoRotate ?: boolean | number ; segments ?: ArcPathSegment [ ] } ,
257+ ) => void ;
258+ onUpdateArcSegment ?: (
259+ animationId : string ,
260+ segmentIndex : number ,
261+ update : Partial < ArcPathSegment > ,
262+ ) => void ;
244263}
245264
246265// fallow-ignore-next-line complexity
@@ -257,6 +276,8 @@ export const AnimationCard = memo(function AnimationCard({
257276 onRemoveFromProperty,
258277 onLivePreview,
259278 onLivePreviewEnd,
279+ onSetArcPath,
280+ onUpdateArcSegment,
260281} : AnimationCardProps ) {
261282 const [ expanded , setExpanded ] = useState ( defaultExpanded ) ;
262283 const [ addingProp , setAddingProp ] = useState ( false ) ;
@@ -329,7 +350,7 @@ export const AnimationCard = memo(function AnimationCard({
329350 const [ copied , setCopied ] = useState ( false ) ;
330351
331352 const methodLabel = METHOD_LABELS [ animation . method ] ?? animation . method ;
332- const easeName = animation . ease ?? "none" ;
353+ const easeName = animation . ease ?? animation . keyframes ?. easeEach ?? "none" ;
333354 const easeLabel = easeName . startsWith ( "custom(" )
334355 ? "Custom curve"
335356 : ( EASE_LABELS [ easeName ] ?? easeName ) ;
@@ -348,7 +369,7 @@ export const AnimationCard = memo(function AnimationCard({
348369 className = "flex w-full items-center gap-2 py-1.5"
349370 >
350371 < span
351- className = "rounded bg-emerald-500 /10 px-1.5 py-0.5 text-[10px] font-semibold text-emerald-400 "
372+ className = "rounded bg-panel-accent /10 px-1.5 py-0.5 text-[10px] font-semibold text-panel-accent "
352373 title = { METHOD_TOOLTIPS [ animation . method ] }
353374 >
354375 { methodLabel }
@@ -420,13 +441,13 @@ export const AnimationCard = memo(function AnimationCard({
420441 < >
421442 < SelectField
422443 label = "Speed"
423- value = {
424- animation . ease ?. startsWith ( "custom(" ) ? "custom" : ( animation . ease ?? "none" )
425- }
444+ value = { easeName . startsWith ( "custom(" ) ? "custom" : easeName }
426445 options = { [ ...SUPPORTED_EASES , "custom" ] }
427446 onChange = { ( next ) => {
428447 if ( next === "custom" ) {
429- const points = controlPointsForGsapEase ( animation . ease ?? "power2.out" ) ;
448+ const points = controlPointsForGsapEase (
449+ easeName !== "none" ? easeName : "power2.out" ,
450+ ) ;
430451 const path = `M0,0 C${ points . x1 } ,${ points . y1 } ${ points . x2 } ,${ points . y2 } 1,1` ;
431452 onUpdateMeta ( animation . id , { ease : `custom(${ path } )` } ) ;
432453 } else {
@@ -435,7 +456,7 @@ export const AnimationCard = memo(function AnimationCard({
435456 } }
436457 />
437458 < EaseCurveSection
438- ease = { animation . ease ?? "none" }
459+ ease = { easeName }
439460 duration = { animation . duration }
440461 onCustomEaseCommit = { ( customEase ) =>
441462 onUpdateMeta ( animation . id , { ease : customEase } )
@@ -477,7 +498,7 @@ export const AnimationCard = memo(function AnimationCard({
477498 ) }
478499
479500 { animation . method === "fromTo" && Object . keys ( animation . properties ) . length > 0 && (
480- < p className = "text-[9px] font-semibold uppercase tracking-wider text-emerald-400 /70" >
501+ < p className = "text-[9px] font-semibold uppercase tracking-wider text-panel-accent /70" >
481502 To
482503 </ p >
483504 ) }
@@ -500,6 +521,39 @@ export const AnimationCard = memo(function AnimationCard({
500521 </ div >
501522 ) }
502523
524+ { onSetArcPath &&
525+ ( animation . properties . x != null ||
526+ animation . properties . y != null ||
527+ animation . keyframes ) && (
528+ < div className = "border-t border-neutral-800 pt-3" >
529+ < ArcPathControls
530+ arcPath = {
531+ animation . arcPath ?? { enabled : false , autoRotate : false , segments : [ ] }
532+ }
533+ segmentCount = { Math . max (
534+ animation . properties . x != null || animation . properties . y != null ? 1 : 0 ,
535+ ( animation . keyframes ?. keyframes ?. length ?? 0 ) - 1 ,
536+ ) }
537+ onToggle = { ( enabled ) =>
538+ onSetArcPath ( animation . id , {
539+ enabled,
540+ segments : animation . arcPath ?. segments ,
541+ } )
542+ }
543+ onUpdateSegment = { ( index , update ) =>
544+ onUpdateArcSegment ?.( animation . id , index , update )
545+ }
546+ onToggleAutoRotate = { ( autoRotate ) =>
547+ onSetArcPath ( animation . id , {
548+ enabled : true ,
549+ autoRotate,
550+ segments : animation . arcPath ?. segments ,
551+ } )
552+ }
553+ />
554+ </ div >
555+ ) }
556+
503557 < div className = "flex items-center gap-2 pt-1" >
504558 < AddPropertyTrigger
505559 adding = { addingProp }
0 commit comments