import type { AnimationConfig, Keyframe, KeyframeProperties, TransformValue } from '@/types/animate'; import { DEFAULT_TRANSFORM } from './defaults'; function isIdentityTransform(t: TransformValue): boolean { return ( t.translateX === 0 && t.translateY === 0 && t.rotate === 0 && t.scaleX === 1 && t.scaleY === 1 && t.skewX === 0 && t.skewY === 0 ); } export function buildTransform(t: TransformValue): string { if (isIdentityTransform(t)) return ''; const parts: string[] = []; if (t.translateX !== 0 || t.translateY !== 0) parts.push(`translate(${t.translateX}px, ${t.translateY}px)`); if (t.rotate !== 0) parts.push(`rotate(${t.rotate}deg)`); if (t.scaleX !== 1 || t.scaleY !== 1) { parts.push(t.scaleX === t.scaleY ? `scale(${t.scaleX})` : `scale(${t.scaleX}, ${t.scaleY})`); } if (t.skewX !== 0 || t.skewY !== 0) parts.push(`skew(${t.skewX}deg, ${t.skewY}deg)`); return parts.join(' '); } function buildProperties(props: KeyframeProperties): string[] { const lines: string[] = []; if (props.transform) { const t = { ...DEFAULT_TRANSFORM, ...props.transform }; const val = buildTransform(t); lines.push(`transform: ${val || 'none'}`); } if (props.opacity !== undefined) lines.push(`opacity: ${props.opacity}`); if (props.backgroundColor && props.backgroundColor !== 'none') lines.push(`background-color: ${props.backgroundColor}`); if (props.borderRadius !== undefined && props.borderRadius !== 0) lines.push(`border-radius: ${props.borderRadius}px`); const filterParts: string[] = []; if (props.blur !== undefined && props.blur !== 0) filterParts.push(`blur(${props.blur}px)`); if (props.brightness !== undefined && props.brightness !== 1) filterParts.push(`brightness(${props.brightness})`); if (filterParts.length) lines.push(`filter: ${filterParts.join(' ')}`); return lines; } function buildIterationCount(count: number | 'infinite'): string { return count === 'infinite' ? 'infinite' : String(count); } export function buildAnimationShorthand(config: AnimationConfig): string { const iter = buildIterationCount(config.iterationCount); const delay = config.delay ? ` ${config.delay}ms` : ''; return `${config.name} ${config.duration}ms ${config.easing}${delay} ${iter} ${config.direction} ${config.fillMode}`; } export function buildKeyframesOnly(config: AnimationConfig): string { const sorted = [...config.keyframes].sort((a, b) => a.offset - b.offset); let out = `@keyframes ${config.name} {\n`; for (const kf of sorted) { const lines = buildProperties(kf.properties); if (lines.length === 0) { out += ` ${kf.offset}% { }\n`; } else { out += ` ${kf.offset}% {\n`; for (const line of lines) out += ` ${line};\n`; if (kf.easing) out += ` animation-timing-function: ${kf.easing};\n`; out += ` }\n`; } } out += `}\n`; return out; } export function buildCSS(config: AnimationConfig): string { const sorted = [...config.keyframes].sort((a, b) => a.offset - b.offset); let out = `@keyframes ${config.name} {\n`; for (const kf of sorted) { const lines = buildProperties(kf.properties); if (lines.length === 0) { out += ` ${kf.offset}% { }\n`; } else { out += ` ${kf.offset}% {\n`; for (const line of lines) out += ` ${line};\n`; if (kf.easing) out += ` animation-timing-function: ${kf.easing};\n`; out += ` }\n`; } } out += `}\n\n`; out += `.animated {\n`; out += ` animation: ${buildAnimationShorthand(config)};\n`; out += `}\n\n`; out += `/* Usage: add class="animated" to your element */`; return out; } export function buildTailwindCSS(config: AnimationConfig): string { const sorted = [...config.keyframes].sort((a, b) => a.offset - b.offset); let out = `/* In your globals.css */\n\n`; out += `@keyframes ${config.name} {\n`; for (const kf of sorted) { const lines = buildProperties(kf.properties); if (lines.length === 0) { out += ` ${kf.offset}% { }\n`; } else { out += ` ${kf.offset}% {\n`; for (const line of lines) out += ` ${line};\n`; if (kf.easing) out += ` animation-timing-function: ${kf.easing};\n`; out += ` }\n`; } } out += `}\n\n`; out += `@utility animate-${config.name} {\n`; out += ` animation: ${buildAnimationShorthand(config)};\n`; out += `}\n\n`; out += `/* Usage: className="animate-${config.name}" */`; return out; }