Current section
Files
Jump to
Current section
Files
priv/static/enguia.js
// assets/js/enguia/config.js
var parseConfig = (element) => {
const raw = element.dataset.motion;
if (!raw) return null;
try {
return JSON.parse(raw);
} catch (error) {
console.error("[Enguia] Failed to parse data-motion JSON:", error);
return null;
}
};
var buildOptions = (config) => ({
duration: config.duration || 300,
easing: config.easing || "ease",
delay: config.delay || 0,
fill: config.fill || "forwards",
iterations: config.repeat === "Infinity" ? Infinity : config.repeat || 1
});
// assets/js/enguia/observer.js
var _registry = /* @__PURE__ */ new Map();
var _observer = null;
var onIntersect = (entries) => entries.forEach((entry) => {
const record = _registry.get(entry.target);
if (!record) return;
const { hook, config } = record;
if (entry.isIntersecting) {
if (hook._animation?.playState === "running") return;
hook._animation?.cancel();
hook._animation = null;
hook._play();
if (!config.scroll_reveal) {
_observer.unobserve(entry.target);
_registry.delete(entry.target);
}
return;
}
if (config.scroll_reveal) {
hook._animation?.cancel();
hook._animation = null;
const firstKeyframe = config.keyframes?.[0];
if (firstKeyframe) Object.assign(hook.el.style, firstKeyframe);
}
});
var getObserver = () => {
if (!_observer)
_observer = new IntersectionObserver(onIntersect, { threshold: [0, 0.15] });
return _observer;
};
var observeElement = (element, hook, config) => {
_registry.set(element, { hook, config });
getObserver().observe(element);
};
var unobserveElement = (element) => {
if (!_registry.has(element)) return;
getObserver().unobserve(element);
_registry.delete(element);
};
// assets/js/enguia/text_effects.js
var playTypewriter = (hook) => {
const text = hook._originalText ?? hook.el.textContent;
const config = hook._config;
const charInterval = (config.duration || 2e3) / Math.max(text.length, 1);
hook.el.textContent = "";
let index = 0;
const typeNextChar = () => {
if (index >= text.length) return;
hook.el.textContent += text[index];
index++;
hook._typewriterTimer = setTimeout(typeNextChar, charInterval);
};
hook._typewriterTimer = setTimeout(typeNextChar, config.delay || 0);
};
var playSplitWords = (hook) => {
const config = hook._config;
const stagger = config.stagger || 80;
const words = (hook._originalText ?? hook.el.textContent).trim().split(/\s+/);
hook.el.innerHTML = words.map((word) => `<span style="display:inline-block">${word}</span>`).join(" ");
Array.from(hook.el.querySelectorAll("span")).forEach((span, index) => {
span.animate(config.keyframes || [], {
...buildOptions(config),
delay: (config.delay || 0) + index * stagger
});
});
};
var textEffects = {
typewriter: playTypewriter,
split_words: playSplitWords
};
// assets/js/enguia/triggers.js
var setupTrigger = {
mount: (hook) => hook._play(),
visible: (hook, config) => "IntersectionObserver" in window ? observeElement(hook.el, hook, config) : hook._play(),
hover: (hook) => {
hook._onMouseEnter = () => hook._play();
hook.el.addEventListener("mouseenter", hook._onMouseEnter);
},
click: (hook) => {
hook._onClick = () => hook._play();
hook.el.addEventListener("click", hook._onClick);
}
};
// assets/js/enguia/hook.js
var EnguiaHook = {
mounted() {
const config = parseConfig(this.el);
if (!config) return;
this._config = config;
this._animation = null;
this._typewriterTimer = null;
this._originalText = this.el.textContent;
const trigger = config.trigger || "mount";
const willChange = [];
config.keyframes?.forEach((kf) => {
if ("transform" in kf) willChange.push("transform");
if ("opacity" in kf) willChange.push("opacity");
});
if (willChange.length) this.el.style.willChange = [...new Set(willChange)].join(", ");
if (trigger === "visible" && config.keyframes?.[0]) {
Object.assign(this.el.style, config.keyframes[0]);
}
setupTrigger[trigger]?.(this, config);
},
destroyed() {
unobserveElement(this.el);
this._onMouseEnter && this.el.removeEventListener("mouseenter", this._onMouseEnter);
this._onClick && this.el.removeEventListener("click", this._onClick);
this._animation?.cancel();
this._animation = null;
clearTimeout(this._typewriterTimer);
this._typewriterTimer = null;
},
_play() {
if (!this._config) return;
const textEffect = textEffects[this._config.text_effect];
if (textEffect) return textEffect(this);
try {
this._animation = this.el.animate(
this._config.keyframes || [],
buildOptions(this._config)
);
} catch (error) {
console.error("[Enguia] Animation failed:", error);
return;
}
if (this._config.repeat !== "Infinity") {
this._animation.addEventListener("finish", () => {
try {
this._animation?.commitStyles();
} catch (_) {
}
this._animation?.cancel();
this.el.style.willChange = "auto";
this._animation = null;
});
}
}
};
var hook_default = EnguiaHook;
export {
hook_default as default
};