'use client'; import * as React from 'react'; import { useEffect, useState } from 'react'; import { ScrollFade } from '@repo/ui'; import type { ToolcraftTimelineKeyframeEasing } from '../../state/types'; import { findTimelineEasingPresetName, getEasingInputValue, parseToolcraftTimelineKeyframeEasing, timelineEasingPresetCategories, timelineEasingPresets, } from './timeline-easing-model'; import { TimelineEasingEditor } from './timeline-easing-editor'; import { TimelineEasingPresetIcon, TimelineEasingStepPresetIcon } from './timeline-easing-icons'; import { useTimelineEasingPopoverWidth } from './timeline-easing-popover-layout'; function cn(...classNames: Array): string { return classNames.filter(Boolean).join(' '); } const selectedItemSurfaceClassName = 'bg-[color:color-mix(in_oklab,var(--link)_12%,transparent)]'; const selectedItemBorderClassName = 'border-[color:color-mix(in_oklab,var(--border)_10%,transparent)]'; const timelineEasingPresetButtonBaseClassName = 'inline-flex items-center gap-2 rounded-lg border p-1 text-left font-mono text-[11px] leading-[14px] transition-[background-color,border-color,color,transform] duration-150 ease-out hover:bg-[color:color-mix(in_oklab,var(--foreground)_6%,transparent)] hover:border-[color:color-mix(in_oklab,var(--border)_14%,transparent)] active:scale-[0.985]'; function getTimelineEasingPresetButtonClassName(isActive: boolean): string { return cn( timelineEasingPresetButtonBaseClassName, isActive && selectedItemBorderClassName, isActive && selectedItemSurfaceClassName, isActive ? 'text-[color:var(--foreground)]' : 'border-[color:color-mix(in_oklab,var(--border)_8%,transparent)] text-[color:var(--muted-foreground)]', ); } export function TimelineEasingPopoverContent({ easing, onChange, }: { easing: ToolcraftTimelineKeyframeEasing; onChange: (easing: ToolcraftTimelineKeyframeEasing) => void; }): React.JSX.Element { const [inputValue, setInputValue] = useState(getEasingInputValue(easing)); const [inputError, setInputError] = useState(null); const [inputEditing, setInputEditing] = useState(false); const activePresetName = findTimelineEasingPresetName(easing); const committedInputValue = getEasingInputValue(easing); const isStep = easing.type === 'step'; const popoverWidth = useTimelineEasingPopoverWidth(); useEffect(() => { if (inputEditing) { return; } setInputValue(committedInputValue); setInputError(null); }, [committedInputValue, inputEditing]); const commitInputValue = ( value = inputValue, { revertOnInvalid = false }: { revertOnInvalid?: boolean } = {}, ): void => { const nextEasing = parseToolcraftTimelineKeyframeEasing(value, easing); if (!nextEasing) { if (revertOnInvalid) { setInputValue(committedInputValue); setInputError(null); return; } setInputError('Use cubic-bezier(x1, y1, x2, y2) or step.'); return; } setInputError(null); setInputValue(getEasingInputValue(nextEasing)); if (getEasingInputValue(nextEasing) === committedInputValue) { return; } onChange(nextEasing); }; return (
); }