/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-e634e55 | Cube Dev Team */
//#region src/tasty/keyframes/index.ts
const KEYFRAMES_KEY = "@keyframes";
/**
* Pattern to extract animation names from CSS animation property values.
* Animation name is typically the first identifier in the shorthand.
* Handles: "fadeIn 300ms ease-in", "pulse 1s infinite", etc.
*
* CSS animation shorthand order (all optional except name):
* animation: name | duration | timing | delay | iteration | direction | fill-mode | play-state
*
* Animation names must:
* - Start with a letter, underscore, or hyphen (but not a digit or CSS keyword)
* - Not be CSS keywords: none, initial, inherit, unset, revert
*/
const CSS_KEYWORDS = new Set([
	"none",
	"initial",
	"inherit",
	"unset",
	"revert",
	"auto",
	"normal",
	"running",
	"paused"
]);
/**
* Pattern to match animation name at the start of an animation value.
* Must start with letter, underscore, or hyphen (not digit).
*/
const ANIMATION_NAME_PATTERN = /^([a-zA-Z_-][a-zA-Z0-9_-]*)/;
/**
* Check if styles object has local @keyframes definition.
* Fast path: single property lookup.
*/
function hasLocalKeyframes(styles) {
	return KEYFRAMES_KEY in styles;
}
/**
* Extract local @keyframes from styles object.
* Returns null if no local keyframes (fast path).
*/
function extractLocalKeyframes(styles) {
	const keyframes = styles[KEYFRAMES_KEY];
	if (!keyframes || typeof keyframes !== "object") return null;
	return keyframes;
}
/**
* Merge local and global keyframes.
* Local keyframes take priority over global.
* Returns null if no keyframes exist (fast path).
*/
function mergeKeyframes(local, global) {
	if (!local && !global) return null;
	if (!local) return global;
	if (!global) return local;
	return {
		...global,
		...local
	};
}
/**
* Extract animation name from a single animation value.
* Returns null if no valid name found.
*
* Examples:
* - "fadeIn 300ms ease-in" → "fadeIn"
* - "1s pulse infinite" → "pulse" (name can be anywhere)
* - "none" → null (CSS keyword)
* - "300ms ease-in" → null (no name, just duration/timing)
*/
function extractAnimationNameFromValue(value) {
	const trimmed = value.trim();
	if (!trimmed) return null;
	const parts = trimmed.split(/\s+/);
	for (const part of parts) {
		if (CSS_KEYWORDS.has(part.toLowerCase())) continue;
		if (/^-?[\d.]+m?s$/i.test(part)) continue;
		if (part === "infinite" || /^\d+$/.test(part)) continue;
		if ([
			"normal",
			"reverse",
			"alternate",
			"alternate-reverse"
		].includes(part.toLowerCase())) continue;
		if ([
			"forwards",
			"backwards",
			"both"
		].includes(part.toLowerCase())) continue;
		if (["running", "paused"].includes(part.toLowerCase())) continue;
		if (/^(ease|linear|ease-in|ease-out|ease-in-out|step-start|step-end)$/i.test(part)) continue;
		if (/^(cubic-bezier|steps)\(/i.test(part)) continue;
		const match = ANIMATION_NAME_PATTERN.exec(part);
		if (match) return match[1];
	}
	return null;
}
/**
* Extract all animation names from an animation property value.
* Handles multiple animations separated by commas.
*
* Example: "fadeIn 300ms, slideIn 500ms ease-out" → ["fadeIn", "slideIn"]
*/
function extractAnimationNamesFromAnimationValue(value) {
	const names = [];
	const animations = value.split(",");
	for (const animation of animations) {
		const name = extractAnimationNameFromValue(animation);
		if (name && !names.includes(name)) names.push(name);
	}
	return names;
}
/**
* Extract animation names from a style value (handles mappings and arrays).
*/
function extractAnimationNamesFromStyleValue(value, names) {
	if (typeof value === "string") for (const name of extractAnimationNamesFromAnimationValue(value)) names.add(name);
	else if (Array.isArray(value)) for (const v of value) extractAnimationNamesFromStyleValue(v, names);
	else if (value && typeof value === "object") for (const v of Object.values(value)) extractAnimationNamesFromStyleValue(v, names);
}
/**
* Extract all animation names referenced in styles.
* Scans 'animation' and 'animationName' properties including in state mappings.
* Returns empty set if no animation properties found (fast path).
*/
function extractAnimationNamesFromStyles(styles) {
	const names = /* @__PURE__ */ new Set();
	if ("animation" in styles) extractAnimationNamesFromStyleValue(styles.animation, names);
	if ("animationName" in styles) extractAnimationNamesFromStyleValue(styles.animationName, names);
	for (const [key, value] of Object.entries(styles)) {
		if (key === "$" || key === KEYFRAMES_KEY) continue;
		if ((key.startsWith("&") || key.startsWith(".") || /^[A-Z]/.test(key)) && value && typeof value === "object") {
			const nestedNames = extractAnimationNamesFromStyles(value);
			for (const name of nestedNames) names.add(name);
		}
	}
	return names;
}
/**
* Replace animation names in CSS declarations with injected names.
* Optimized to avoid regex creation - uses simple string replacement.
*
* @param declarations CSS declarations string
* @param nameMap Map from original name to injected name (only contains names that differ)
* @returns Updated declarations string
*/
function replaceAnimationNames(declarations, nameMap) {
	if (!declarations.includes("animation")) return declarations;
	const parts = declarations.split(";");
	let modified = false;
	for (let i = 0; i < parts.length; i++) {
		const part = parts[i];
		const colonIdx = part.indexOf(":");
		if (colonIdx === -1) continue;
		const prop = part.slice(0, colonIdx).trim().toLowerCase();
		if (prop === "animation" || prop === "animation-name") {
			const prefix = part.slice(0, colonIdx + 1);
			let value = part.slice(colonIdx + 1);
			for (const [original, injected] of nameMap) {
				const newValue = replaceWord(value, original, injected);
				if (newValue !== value) {
					value = newValue;
					modified = true;
				}
			}
			parts[i] = prefix + value;
		}
	}
	return modified ? parts.join(";") : declarations;
}
/**
* Replace a word in a string (word boundary aware, no regex).
*/
function replaceWord(str, word, replacement) {
	let result = str;
	let idx = 0;
	while ((idx = result.indexOf(word, idx)) !== -1) {
		const before = idx === 0 ? " " : result[idx - 1];
		const after = idx + word.length >= result.length ? " " : result[idx + word.length];
		const isWordBoundaryBefore = !/[a-zA-Z0-9_-]/.test(before);
		const isWordBoundaryAfter = !/[a-zA-Z0-9_-]/.test(after);
		if (isWordBoundaryBefore && isWordBoundaryAfter) {
			result = result.slice(0, idx) + replacement + result.slice(idx + word.length);
			idx += replacement.length;
		} else idx += word.length;
	}
	return result;
}
/**
* Filter keyframes to only those that are actually used.
* Returns null if no keyframes are used (fast path).
*/
function filterUsedKeyframes(keyframes, usedNames) {
	if (!keyframes || usedNames.size === 0) return null;
	const used = {};
	let hasAny = false;
	for (const name of usedNames) if (keyframes[name]) {
		used[name] = keyframes[name];
		hasAny = true;
	}
	return hasAny ? used : null;
}

//#endregion
export { extractAnimationNamesFromStyles, extractLocalKeyframes, filterUsedKeyframes, hasLocalKeyframes, mergeKeyframes, replaceAnimationNames };
//# sourceMappingURL=index.js.map