import { useTrackDesignInput } from "../../contexts/DesignPanelInputContext"; import { FlatRow, FlatSegmentedRow, FlatSelectRow } from "./propertyPanelFlatPrimitives"; import { KeyframeNavigation } from "./KeyframeNavigation"; import { formatPxMetricValue } from "./propertyPanelHelpers"; import { STUDIO_KEYFRAMES_ENABLED } from "./manualEditingAvailability"; import { resolveValueTier } from "./propertyPanelValueTier"; import { PropertyPanel3dTransform } from "./propertyPanel3dTransform"; import type { DomEditSelection } from "./domEditingTypes"; type KeyframeEntry = Array<{ percentage: number; tweenPercentage?: number; properties: Record; ease?: string; }> | null; interface GeometryRowsProps { element: DomEditSelection; displayX: number; displayY: number; displayW: number; displayH: number; displayR: number; manualOffsetEditingDisabled: boolean; manualSizeEditingDisabled: boolean; manualRotationEditingDisabled: boolean; commitManualOffset: (axis: "x" | "y", value: string) => void; commitManualSize: (dimension: "width" | "height", value: string) => void; commitManualRotation: (value: string) => void; gsapAnimId: string | null; navKeyframes: KeyframeEntry; currentPct: number; seekFromKfPct: (pct: number) => void; animIdForProp: (prop: string) => string; onCommitAnimatedProperty?: ( element: DomEditSelection, property: string, value: number, ) => Promise; onRemoveKeyframe?: (animId: string, pct: number) => void; onConvertToKeyframes?: (animId: string) => void; } function KeyframeGutter({ element, property, displayValue, gsapAnimId, navKeyframes, currentPct, seekFromKfPct, animIdForProp, onCommitAnimatedProperty, onRemoveKeyframe, onConvertToKeyframes, }: { property: string; displayValue: number; } & Pick< GeometryRowsProps, | "element" | "gsapAnimId" | "navKeyframes" | "currentPct" | "seekFromKfPct" | "animIdForProp" | "onCommitAnimatedProperty" | "onRemoveKeyframe" | "onConvertToKeyframes" >) { const track = useTrackDesignInput(); if (!STUDIO_KEYFRAMES_ENABLED || !gsapAnimId) return null; const hasKeyframesOnProp = Boolean(navKeyframes?.some((kf) => property in kf.properties)); return ( { if (!onCommitAnimatedProperty) return; track("button", `Add ${property} keyframe`); void onCommitAnimatedProperty(element, property, displayValue); }} onRemoveKeyframe={(pct, animationId) => { if (!onRemoveKeyframe) return; track("button", `Remove ${property} keyframe`); onRemoveKeyframe(animationId ?? animIdForProp(property), pct); }} onConvertToKeyframes={() => { if (!onConvertToKeyframes) return; track("button", `Convert ${property} to keyframes`); onConvertToKeyframes(animIdForProp(property)); }} /> ); } export function LayoutGeometryRows({ element, displayX, displayY, displayW, displayH, displayR, manualOffsetEditingDisabled, manualSizeEditingDisabled, manualRotationEditingDisabled, commitManualOffset, commitManualSize, commitManualRotation, gsapAnimId, navKeyframes, currentPct, seekFromKfPct, animIdForProp, onCommitAnimatedProperty, onRemoveKeyframe, onConvertToKeyframes, }: GeometryRowsProps) { const gutterProps = { element, gsapAnimId, navKeyframes, currentPct, seekFromKfPct, animIdForProp, onCommitAnimatedProperty, onRemoveKeyframe, onConvertToKeyframes, }; return ( <> commitManualOffset("x", next)} suffix={} /> commitManualOffset("y", next)} suffix={} /> commitManualSize("width", next)} suffix={} /> commitManualSize("height", next)} suffix={} /> commitManualRotation(next.replace("°", ""))} suffix={} /> ); } export function LayoutZIndexRow({ styles, onSetStyle, }: { styles: Record; onSetStyle: (prop: string, value: string) => void | Promise; }) { const zIndex = String(parseInt(styles["z-index"] || "auto", 10) || 0); return ( void onSetStyle("z-index", next)} /> ); } export function LayoutFlexBlock({ styles, onSetStyle, disabled, }: { styles: Record; onSetStyle: (prop: string, value: string) => void | Promise; disabled: boolean; }) { const isFlex = styles.display === "flex" || styles.display === "inline-flex"; if (!isFlex) return null; const direction = styles["flex-direction"] || "row"; return (
Flex
void onSetStyle("flex-direction", next)} /> void onSetStyle("justify-content", next)} /> void onSetStyle("align-items", next)} /> void onSetStyle("gap", next.endsWith("px") ? next : `${next}px`)} />
); } export function LayoutTransform3DBlock({ gsapRuntimeValues, gsapAnimId, resolveAnimIdForProp, gsapKeyframes, currentPct, elStart, elDuration, element, onCommitAnimatedProperty, onCommitAnimatedProperties, onSeekToTime, onRemoveKeyframe, onConvertToKeyframes, onLivePreviewProps, }: { gsapRuntimeValues: Record; gsapAnimId: string | null; resolveAnimIdForProp?: (prop: string) => string | null; gsapKeyframes: Array<{ percentage: number; properties: Record; ease?: string; }> | null; currentPct: number; elStart: number; elDuration: number; element: DomEditSelection; onCommitAnimatedProperty?: ( element: DomEditSelection, property: string, value: number, ) => Promise; onCommitAnimatedProperties?: ( element: DomEditSelection, props: Record, ) => Promise; onSeekToTime?: (time: number) => void; onRemoveKeyframe?: (animId: string, pct: number) => void; onConvertToKeyframes?: (animId: string, duration?: number) => void; onLivePreviewProps?: (element: DomEditSelection, props: Record) => void; }) { return (
3D Transform
); } interface FlatLayoutSectionProps extends Omit, Pick< Parameters[0], | "gsapRuntimeValues" | "resolveAnimIdForProp" | "gsapKeyframes" | "elStart" | "elDuration" | "onCommitAnimatedProperties" | "onSeekToTime" | "onLivePreviewProps" > { element: DomEditSelection; styles: Record; onSetStyle: (prop: string, value: string) => void | Promise; disabled: boolean; } export function FlatLayoutSection({ element, styles, onSetStyle, disabled, gsapRuntimeValues, resolveAnimIdForProp, gsapKeyframes, elStart, elDuration, onCommitAnimatedProperties, onSeekToTime, onLivePreviewProps, ...geometry }: FlatLayoutSectionProps) { return (
); }