@tachui/modifiers
Version:
Essential styling modifiers for tachUI framework
787 lines (786 loc) • 30.6 kB
JavaScript
var T = Object.defineProperty;
var M = (v, a, i) => a in v ? T(v, a, { enumerable: !0, configurable: !0, writable: !0, value: i }) : v[a] = i;
var g = (v, a, i) => M(v, typeof a != "symbol" ? a + "" : a, i);
import { isSignal as u, isComputed as m, createEffect as y, getThemeSignal as $ } from "@tachui/core/reactive";
import { ModifierPriority as E } from "@tachui/core/modifiers/types";
import { shouldExpandForInfinity as P, dimensionToCSS as S, isInfinity as C } from "@tachui/core/constants/layout";
class L {
constructor(a) {
this.properties = a;
}
/**
* Helper to resolve reactive properties
*/
resolveReactiveProps(a, i) {
const t = {};
for (const [e, n] of Object.entries(a))
u(n) || m(n), t[e] = n;
return t;
}
/**
* Apply a single style change to an element with reactive support
*/
applyStyleChange(a, i, t) {
if (a instanceof HTMLElement) {
const e = this.toCSSProperty(i);
if (u(t) || m(t))
y(() => {
const n = t(), s = String(n);
if (s.includes("!important")) {
const o = s.replace(/\s*!important\s*$/, "").trim();
a.style.setProperty(e, o, "important");
} else
a.style.setProperty(e, s);
});
else {
const n = String(t);
if (n.includes("!important")) {
const s = n.replace(/\s*!important\s*$/, "").trim();
a.style.setProperty(e, s, "important");
} else
a.style.setProperty(e, n);
}
}
}
/**
* Convert camelCase property to CSS kebab-case
*/
toCSSProperty(a) {
return a.replace(/([A-Z])/g, "-$1").toLowerCase();
}
/**
* Convert value to CSS value string
*/
toCSSValue(a) {
return typeof a == "number" ? `${a}px` : String(a);
}
/**
* Convert value to CSS value string with property-specific handling
*/
toCSSValueForProperty(a, i) {
return typeof i == "number" ? [
"opacity",
"z-index",
"line-height",
"flex-grow",
"flex-shrink",
"order",
"column-count",
"font-weight"
].includes(a) ? String(i) : `${i}px` : ([
"filter",
// CSS filter strings should not be processed
"transform",
// CSS transform strings
"clip-path"
// CSS clip-path strings
].includes(a), String(i));
}
/**
* Apply multiple CSS properties to an element with reactive support
*/
applyStyles(a, i) {
if (a instanceof HTMLElement || a.style) {
const t = (a instanceof HTMLElement, a.style);
for (const [e, n] of Object.entries(i))
if (n !== void 0) {
const s = this.toCSSProperty(e);
if (u(n) || m(n))
y(() => {
const o = n(), r = this.toCSSValueForProperty(
s,
o
);
if (t.setProperty)
if (typeof r == "string" && r.includes("!important")) {
const l = r.replace(/\s*!important\s*$/, "").trim();
t.setProperty(s, l, "important");
} else
t.setProperty(s, r);
else
t[s] = r;
});
else {
const o = this.toCSSValueForProperty(s, n);
if (t.setProperty)
if (typeof o == "string" && o.includes("!important")) {
const r = o.replace(/\s*!important\s*$/, "").trim();
t.setProperty(s, r, "important");
} else
t.setProperty(s, o);
else
t[s] = o;
}
}
}
}
/**
* Add CSS classes to an element
*/
addClasses(a, i) {
a instanceof HTMLElement && a.classList.add(...i);
}
/**
* Remove CSS classes from an element
*/
removeClasses(a, i) {
a instanceof HTMLElement && a.classList.remove(...i);
}
/**
* Create a style computation context
*/
createStyleContext(a, i, t) {
return {
componentId: a,
element: i,
modifiers: t,
signals: /* @__PURE__ */ new Set(),
cleanup: []
};
}
}
class V extends L {
constructor() {
super(...arguments);
g(this, "type", "layout");
g(this, "priority", E.LAYOUT);
}
apply(i, t) {
if (!i.element || !t.element) return;
const e = this.createStyleContext(
t.componentId,
t.element,
[]
), n = this.computeLayoutStyles(
this.properties,
e
);
this.applyStyles(t.element, n);
const s = this.properties;
s.offset && t.element instanceof HTMLElement && this.applyOffsetTransform(t.element, s.offset), s.aspectRatio && t.element instanceof HTMLElement && this.applyAspectRatio(t.element, s.aspectRatio), s.scaleEffect && t.element instanceof HTMLElement && this.applyScaleTransform(t.element, s.scaleEffect), s.position && t.element instanceof HTMLElement && this.applyAbsolutePosition(t.element, s.position), s.zIndex !== void 0 && t.element instanceof HTMLElement && this.applyZIndex(t.element, s.zIndex);
}
applyOffsetTransform(i, t) {
const { x: e, y: n } = t;
if (u(e) || m(e) || u(n) || m(n))
y(() => {
const s = u(e) || m(e) ? e() : e ?? 0, o = u(n) || m(n) ? n() : n ?? 0, r = this.toCSSValue(s), l = this.toCSSValue(o), c = `translate(${r}, ${l})`, p = (i.style.transform || "").split(" ").filter((h) => h && !h.startsWith("translate(")).join(" "), d = p ? `${p} ${c}` : c;
i.style.transform = d;
});
else {
const s = e ?? 0, o = n ?? 0, r = this.toCSSValue(s), l = this.toCSSValue(o), c = `translate(${r}, ${l})`, p = (i.style.transform || "").split(" ").filter((h) => h && !h.startsWith("translate(")).join(" "), d = p ? `${p} ${c}` : c;
i.style.transform = d;
}
}
applyAspectRatio(i, t) {
const { ratio: e, contentMode: n } = t;
e !== void 0 && (u(e) || m(e) ? y(() => {
const s = typeof e == "function" ? e() : e;
i.style.aspectRatio = String(s);
}) : i.style.aspectRatio = String(e), n === "fill" ? i.style.objectFit = "cover" : i.style.objectFit = "contain");
}
// Phase 3 - Epic: Butternut Transform Methods
applyScaleTransform(i, t) {
const { x: e, y: n, anchor: s } = t, o = e ?? 1, r = n ?? o;
if (u(o) || m(o) || u(r) || m(r))
y(() => {
const l = u(o) || m(o) ? o() : o, c = u(r) || m(r) ? r() : r, f = `scale(${l}, ${c})`;
i.style.transformOrigin = this.getTransformOrigin(
s || "center"
);
const d = (i.style.transform || "").replace(/\s*scale\([^)]*\)\s*/g, " ").replace(/\s+/g, " ").trim(), h = d ? `${d} ${f}` : f;
i.style.transform = h;
});
else {
const l = `scale(${o}, ${r})`;
i.style.transformOrigin = this.getTransformOrigin(
s || "center"
);
const f = (i.style.transform || "").replace(/\s*scale\([^)]*\)\s*/g, " ").replace(/\s+/g, " ").trim(), p = f ? `${f} ${l}` : l;
i.style.transform = p;
}
}
applyAbsolutePosition(i, t) {
const { x: e, y: n } = t;
if (i.style.position = "absolute", u(e) || m(e) || u(n) || m(n))
y(() => {
const s = u(e) || m(e) ? e() : e ?? 0, o = u(n) || m(n) ? n() : n ?? 0;
i.style.left = this.toCSSValue(s), i.style.top = this.toCSSValue(o);
});
else {
const s = e ?? 0, o = n ?? 0;
i.style.left = this.toCSSValue(s), i.style.top = this.toCSSValue(o);
}
}
applyZIndex(i, t) {
u(t) || m(t) ? y(() => {
const e = t();
i.style.zIndex = String(e);
}) : i.style.zIndex = String(t);
}
getTransformOrigin(i) {
return {
center: "center center",
top: "center top",
topLeading: "left top",
topTrailing: "right top",
bottom: "center bottom",
bottomLeading: "left bottom",
bottomTrailing: "right bottom",
leading: "left center",
trailing: "right center"
}[i] || "center center";
}
computeLayoutStyles(i, t) {
const e = {};
if (i.frame) {
const n = i.frame, s = P(n);
if (Object.assign(e, s.cssProps), n.width !== void 0) {
const o = S(n.width);
o !== void 0 && !C(n.width) && !s.expandWidth && (e.width = o);
}
if (n.height !== void 0) {
const o = S(n.height);
o !== void 0 && !C(n.height) && !s.expandHeight && (e.height = o);
}
if (n.minWidth !== void 0) {
const o = S(n.minWidth);
o !== void 0 && (e.minWidth = o);
}
if (n.maxWidth !== void 0 && !C(n.maxWidth)) {
const o = S(n.maxWidth);
o !== void 0 && (e.maxWidth = o);
} else C(n.maxWidth) && (e.maxWidth = "none", e.flexGrow = "1 !important", e.flexShrink = "1 !important", e.flexBasis = "0% !important", e.alignSelf = "stretch !important");
if (n.minHeight !== void 0) {
const o = S(n.minHeight);
o !== void 0 && (e.minHeight = o);
}
if (n.maxHeight !== void 0 && !C(n.maxHeight)) {
const o = S(n.maxHeight);
o !== void 0 && (e.maxHeight = o);
} else C(n.maxHeight) && (e.maxHeight = "none", e.flexGrow = "1 !important", e.flexShrink = "1 !important", e.flexBasis = "0% !important", e.alignSelf = "stretch !important");
}
if (i.padding !== void 0)
if (typeof i.padding == "number")
e.padding = this.toCSSValue(i.padding);
else {
const n = i.padding;
n.top !== void 0 && (e.paddingTop = this.toCSSValue(n.top)), n.right !== void 0 && (e.paddingRight = this.toCSSValue(n.right)), n.bottom !== void 0 && (e.paddingBottom = this.toCSSValue(n.bottom)), n.left !== void 0 && (e.paddingLeft = this.toCSSValue(n.left));
}
if (i.margin !== void 0)
if (typeof i.margin == "number")
e.margin = this.toCSSValue(i.margin);
else {
const n = i.margin;
n.top !== void 0 && (e.marginTop = this.toCSSValue(n.top)), n.right !== void 0 && (e.marginRight = this.toCSSValue(n.right)), n.bottom !== void 0 && (e.marginBottom = this.toCSSValue(n.bottom)), n.left !== void 0 && (e.marginLeft = this.toCSSValue(n.left));
}
if (i.alignment)
switch (i.alignment) {
case "leading":
e.textAlign = "left", e.alignItems = "flex-start";
break;
case "center":
e.textAlign = "center", e.alignItems = "center";
break;
case "trailing":
e.textAlign = "right", e.alignItems = "flex-end";
break;
case "top":
e.alignItems = "flex-start";
break;
case "bottom":
e.alignItems = "flex-end";
break;
}
if (i.layoutPriority !== void 0) {
const n = Number(i.layoutPriority);
n > 0 ? (e.flexShrink = "0", e.flexGrow = String(Math.max(1, n / 10)), e.zIndex = String(n), e.gridRowEnd = `span ${String(Math.min(10, Math.max(1, Math.ceil(n / 10))))}`, e.gridColumnEnd = `span ${String(Math.min(10, Math.max(1, Math.ceil(n / 10))))}`) : n === 0 ? (e.flexShrink = "1", e.flexGrow = "1") : (e.flexShrink = String(Math.abs(n)), e.flexGrow = "0", e.zIndex = String(n)), e && typeof e == "object" && "setProperty" in e && e.setProperty("--layout-priority", String(n));
}
if (i.offset, i.aspectRatio) {
const { ratio: n, contentMode: s } = i.aspectRatio;
n !== void 0 && (e.aspectRatio = typeof n == "number" ? String(n) : n, s === "fill" ? e.objectFit = "cover" : e.objectFit = "contain");
}
if (i.fixedSize) {
const { horizontal: n, vertical: s } = i.fixedSize;
n && (e.flexShrink = "0", e.width = "max-content"), s && (e.flexShrink = "0", e.height = "max-content");
}
return e;
}
}
class I extends L {
constructor() {
super(...arguments);
g(this, "type", "appearance");
g(this, "priority", E.APPEARANCE);
}
apply(i, t) {
if (!i.element || !t.element)
return;
const e = this.createStyleContext(
t.componentId,
t.element,
[]
), n = this.resolveReactiveProps(
this.properties,
e
);
this.applyAssetBasedStyles(t.element, n);
const s = this.computeAppearanceStyles(n);
this.applyStyles(t.element, s);
}
/**
* Apply Asset-based styles with theme reactivity
*/
applyAssetBasedStyles(i, t) {
const e = $();
t.foregroundColor && this.isAsset(t.foregroundColor) && y(() => {
e();
const n = t.foregroundColor.resolve();
this.applyStyleChange(i, "color", n);
}), t.backgroundColor && this.isAsset(t.backgroundColor) && y(() => {
e();
const n = t.backgroundColor.resolve();
this.applyStyleChange(i, "backgroundColor", n);
}), t.border?.color && this.isAsset(t.border.color) && y(() => {
e();
const n = t.border.color.resolve();
this.applyStyleChange(i, "borderColor", n);
});
}
/**
* Check if a value is an Asset object (including Asset proxies)
*/
isAsset(i) {
return i != null && typeof i == "object" && "resolve" in i && typeof i.resolve == "function";
}
computeAppearanceStyles(i) {
const t = {};
if (i.foregroundColor && !this.isAsset(i.foregroundColor) && (t.color = i.foregroundColor), i.backgroundColor && !this.isAsset(i.backgroundColor) && (t.backgroundColor = i.backgroundColor), i.opacity !== void 0 && (t.opacity = i.opacity), i.font) {
const n = i.font;
n.family && (typeof n.family == "object" && n.family !== null && "resolve" in n.family ? t.fontFamily = n.family.resolve() : t.fontFamily = n.family), n.size && (t.fontSize = this.toCSSValue(n.size)), n.weight && (t.fontWeight = String(n.weight)), n.style && (t.fontStyle = n.style);
}
if (i.cornerRadius !== void 0 && (t.borderRadius = this.toCSSValue(i.cornerRadius)), i.border) {
const n = i.border;
n.width !== void 0 && (t.borderWidth = this.toCSSValue(n.width)), n.color && !this.isAsset(n.color) && (t.borderColor = n.color), n.style && (t.borderStyle = n.style);
}
if (i.shadow) {
const n = i.shadow, s = n.x || 0, o = n.y || 0, r = n.radius || 0, l = n.color || "rgba(0,0,0,0.25)";
t.boxShadow = `${s}px ${o}px ${r}px ${l}`;
}
if (i.clipped && (t.overflow = "hidden"), i.clipShape) {
const { shape: n, parameters: s } = i.clipShape;
switch (n) {
case "circle":
t.clipPath = "circle(50%)";
break;
case "ellipse": {
const o = s?.radiusX || "50%", r = s?.radiusY || "50%";
t.clipPath = `ellipse(${o} ${r} at center)`;
break;
}
case "rect": {
const o = s?.inset || 0;
t.clipPath = `inset(${o}px)`;
break;
}
case "polygon": {
const o = s?.points || "0% 0%, 100% 0%, 100% 100%, 0% 100%";
t.clipPath = `polygon(${o})`;
break;
}
}
}
const e = [];
return i.blur !== void 0 && e.push(`blur(${i.blur}px)`), i.brightness !== void 0 && e.push(`brightness(${i.brightness})`), i.contrast !== void 0 && e.push(`contrast(${i.contrast})`), i.saturation !== void 0 && e.push(`saturate(${i.saturation})`), i.hueRotation !== void 0 && e.push(`hue-rotate(${i.hueRotation}deg)`), i.grayscale !== void 0 && e.push(`grayscale(${i.grayscale})`), i.colorInvert !== void 0 && e.push(`invert(${i.colorInvert})`), e.length > 0 && (t.filter = e.join(" ")), t;
}
}
class R extends L {
constructor() {
super(...arguments);
g(this, "type", "interaction");
g(this, "priority", E.INTERACTION);
}
apply(i, t) {
if (!t.element) return;
const e = this.properties;
e.onTap && t.element.addEventListener("click", e.onTap), e.onHover && (t.element.addEventListener("mouseenter", () => e.onHover(!0)), t.element.addEventListener("mouseleave", () => e.onHover(!1))), e.onMouseEnter && t.element.addEventListener("mouseenter", e.onMouseEnter), e.onMouseLeave && t.element.addEventListener("mouseleave", e.onMouseLeave), e.onMouseDown && t.element.addEventListener("mousedown", e.onMouseDown), e.onMouseUp && t.element.addEventListener("mouseup", e.onMouseUp), e.onDragStart && t.element.addEventListener("dragstart", e.onDragStart), e.onDragOver && t.element.addEventListener("dragover", e.onDragOver), e.onDragLeave && t.element.addEventListener("dragleave", e.onDragLeave), e.onDrop && t.element.addEventListener("drop", e.onDrop), e.onDoubleClick && t.element.addEventListener("dblclick", e.onDoubleClick), e.onContextMenu && t.element.addEventListener("contextmenu", e.onContextMenu), e.onFocus && (t.element.addEventListener("focus", () => e.onFocus(!0)), t.element.addEventListener("blur", () => e.onFocus(!1))), e.onBlur && t.element.addEventListener("blur", () => e.onBlur(!1)), e.onKeyPress && t.element.addEventListener("keypress", e.onKeyPress), e.onKeyDown && t.element.addEventListener("keydown", e.onKeyDown), e.onKeyUp && t.element.addEventListener("keyup", e.onKeyUp), e.onScroll && t.element.addEventListener("scroll", e.onScroll, {
passive: !0
}), e.onWheel && t.element.addEventListener("wheel", e.onWheel, {
passive: !1
}), e.onInput && t.element.addEventListener("input", e.onInput), e.onChange && t.element.addEventListener("change", (n) => {
const s = n.target, o = s.value || s.textContent || "";
e.onChange(o, n);
}), e.onCopy && t.element.addEventListener("copy", e.onCopy), e.onCut && t.element.addEventListener("cut", e.onCut), e.onPaste && t.element.addEventListener("paste", e.onPaste), e.onSelect && t.element.addEventListener("select", e.onSelect), e.disabled !== void 0 && t.element instanceof HTMLElement && (e.disabled ? (t.element.setAttribute("disabled", "true"), t.element.style.pointerEvents = "none", t.element.style.opacity = "0.6") : (t.element.removeAttribute("disabled"), t.element.style.pointerEvents = "", t.element.style.opacity = "")), e.draggable !== void 0 && t.element instanceof HTMLElement && (t.element.draggable = e.draggable), e.accessibilityLabel && t.element.setAttribute("aria-label", e.accessibilityLabel), e.accessibilityHint && t.element.setAttribute("aria-describedby", e.accessibilityHint), e.onLongPressGesture && this.setupLongPressGesture(t.element, e.onLongPressGesture), e.keyboardShortcut && this.setupKeyboardShortcut(t.element, e.keyboardShortcut), e.focused !== void 0 && this.setupFocusManagement(t.element, e.focused), e.focusable && this.setupFocusable(t.element, e.focusable), e.onContinuousHover && this.setupContinuousHover(t.element, e.onContinuousHover), e.allowsHitTesting !== void 0 && this.setupHitTesting(t.element, e.allowsHitTesting);
}
// Phase 4 Advanced Gesture Methods
/**
* Setup long press gesture with timing and distance constraints
*/
setupLongPressGesture(i, t) {
const e = t.minimumDuration ?? 500, n = t.maximumDistance ?? 10;
let s, o = null, r = !1;
const l = () => {
s && (clearTimeout(s), s = void 0), r && t.onPressingChanged && t.onPressingChanged(!1), r = !1, o = null;
}, c = (h) => {
const b = h;
o = { x: b.clientX, y: b.clientY }, r = !0, t.onPressingChanged && t.onPressingChanged(!0), s = window.setTimeout(() => {
r && o && (t.perform(), l());
}, e);
}, f = (h) => {
const b = h;
if (!o || !r) return;
Math.sqrt(
Math.pow(b.clientX - o.x, 2) + Math.pow(b.clientY - o.y, 2)
) > n && l();
}, p = () => {
l();
}, d = () => {
l();
};
i.addEventListener("pointerdown", c), i.addEventListener("pointermove", f), i.addEventListener("pointerup", p), i.addEventListener(
"pointercancel",
d
), i._longPressCleanup = l;
}
/**
* Setup keyboard shortcut handling
*/
setupKeyboardShortcut(i, t) {
const e = t.modifiers ?? [], n = (s) => {
const o = {
cmd: e.includes("cmd") || e.includes("meta"),
ctrl: e.includes("ctrl"),
shift: e.includes("shift"),
alt: e.includes("alt")
}, r = {
cmd: s.metaKey || s.ctrlKey,
// Handle both Mac (meta) and PC (ctrl)
ctrl: s.ctrlKey,
shift: s.shiftKey,
alt: s.altKey
}, l = s.key.toLowerCase() === t.key.toLowerCase(), c = Object.entries(o).every(
([f, p]) => p === r[f]
);
l && c && (s.preventDefault(), t.action());
};
document.addEventListener("keydown", n), i._keyboardShortcutCleanup = () => {
document.removeEventListener("keydown", n);
};
}
/**
* Setup focus management with reactive binding
*/
setupFocusManagement(i, t) {
if (!(i instanceof HTMLElement)) return;
const e = i;
e.hasAttribute("tabindex") || e.setAttribute("tabindex", "0"), u(t) || m(t) ? y(() => {
t() ? e.focus() : e.blur();
}) : t && e.focus();
}
/**
* Setup focusable behavior
*/
setupFocusable(i, t) {
if (!(i instanceof HTMLElement)) return;
const e = i;
t.isFocusable === !1 ? e.setAttribute("tabindex", "-1") : e.hasAttribute("tabindex") || e.setAttribute("tabindex", "0"), t.interactions?.includes("activate") && e.addEventListener("keydown", (n) => {
(n.key === "Enter" || n.key === " ") && (n.preventDefault(), e.click());
}), t.interactions?.includes("edit") && (e.setAttribute("role", "textbox"), e.setAttribute("contenteditable", "true"));
}
/**
* Setup continuous hover tracking with coordinates
*/
setupContinuousHover(i, t) {
const e = t.coordinateSpace ?? "local", n = (o) => {
const r = o;
let l, c;
if (e === "local") {
const f = i.getBoundingClientRect();
l = r.clientX - f.left, c = r.clientY - f.top;
} else
l = r.clientX, c = r.clientY;
t.perform({ x: l, y: c });
}, s = () => {
t.perform(null);
};
i.addEventListener("mousemove", n), i.addEventListener("mouseleave", s), i._continuousHoverCleanup = () => {
i.removeEventListener("mousemove", n), i.removeEventListener(
"mouseleave",
s
);
};
}
/**
* Setup hit testing control
*/
setupHitTesting(i, t) {
i instanceof HTMLElement && (i.style.pointerEvents = t ? "" : "none");
}
}
class O extends L {
constructor() {
super(...arguments);
g(this, "type", "animation");
g(this, "priority", E.ANIMATION);
}
apply(i, t) {
if (!t.element) return;
const e = this.properties;
if (e.transition) {
const n = e.transition, s = n.property || "all", o = n.duration || 300, r = n.easing || "ease", l = n.delay || 0;
t.element instanceof HTMLElement && (t.element.style.transition = `${s} ${o}ms ${r} ${l}ms`);
}
if (e.animation && t.element instanceof HTMLElement) {
const n = e.animation;
if (n.keyframes) {
const s = `tachui-animation-${t.componentId}-${Date.now()}`, o = this.createKeyframeRule(
s,
n.keyframes
);
this.addKeyframesToStylesheet(o);
const r = n.duration || 1e3, l = n.easing || "ease", c = n.iterations || 1, f = n.direction || "normal";
t.element.style.animation = `${s} ${r}ms ${l} ${c} ${f}`;
}
}
if (e.transform && t.element instanceof HTMLElement && (u(e.transform) || m(e.transform) ? y(() => {
const n = e.transform();
t.element instanceof HTMLElement && (t.element.style.transform = n);
}) : t.element.style.transform = e.transform), e.rotationEffect && t.element instanceof HTMLElement) {
const { angle: n, anchor: s } = e.rotationEffect, r = {
center: "50% 50%",
top: "50% 0%",
topLeading: "0% 0%",
topTrailing: "100% 0%",
bottom: "50% 100%",
bottomLeading: "0% 100%",
bottomTrailing: "100% 100%",
leading: "0% 50%",
trailing: "100% 50%"
}[s || "center"] || "50% 50%", l = `rotate(${n}deg)`;
if (u(n) || m(n))
y(() => {
const f = `rotate(${typeof n == "function" ? n() : n}deg)`;
if (t.element instanceof HTMLElement) {
t.element.style.transformOrigin = r;
const d = (t.element.style.transform || "").split(" ").filter((b) => b && !b.startsWith("rotate(")).join(" "), h = d ? `${d} ${f}` : f;
t.element.style.transform = h;
}
});
else if (t.element instanceof HTMLElement) {
t.element.style.transformOrigin = r;
const f = (t.element.style.transform || "").split(" ").filter((d) => d && !d.startsWith("rotate(")).join(" "), p = f ? `${f} ${l}` : l;
t.element.style.transform = p;
}
}
e.overlay && t.element instanceof HTMLElement && this.applyOverlay(t.element, e.overlay, t);
}
applyOverlay(i, t, e) {
const { content: n, alignment: s = "center" } = t;
(i.style.position === "" || i.style.position === "static") && (i.style.position = "relative");
const o = document.createElement("div");
o.style.position = "absolute", o.style.pointerEvents = "none";
const r = this.getOverlayAlignment(s);
if (Object.assign(o.style, r), typeof n == "function") {
const l = n();
if (l && typeof l.render == "function") {
const c = l.render();
c.element && o.appendChild(c.element);
}
} else if (n && typeof n.render == "function") {
const l = n.render();
l.element && o.appendChild(l.element);
} else n instanceof HTMLElement && o.appendChild(n);
i.appendChild(o);
}
getOverlayAlignment(i) {
const t = {
center: {
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)"
},
top: {
top: "0",
left: "50%",
transform: "translateX(-50%)"
},
bottom: {
bottom: "0",
left: "50%",
transform: "translateX(-50%)"
},
leading: {
top: "50%",
left: "0",
transform: "translateY(-50%)"
},
trailing: {
top: "50%",
right: "0",
transform: "translateY(-50%)"
},
topLeading: {
top: "0",
left: "0"
},
topTrailing: {
top: "0",
right: "0"
},
bottomLeading: {
bottom: "0",
left: "0"
},
bottomTrailing: {
bottom: "0",
right: "0"
}
};
return t[i] || t.center;
}
createKeyframeRule(i, t) {
let e = `@keyframes ${i} {
`;
for (const [n, s] of Object.entries(t)) {
e += ` ${n} {
`;
for (const [o, r] of Object.entries(s)) {
const l = this.toCSSProperty(o);
e += ` ${l}: ${r};
`;
}
e += ` }
`;
}
return e += "}", e;
}
addKeyframesToStylesheet(i) {
let t = document.querySelector(
"#tachui-animations"
);
t || (t = document.createElement("style"), t.id = "tachui-animations", document.head.appendChild(t)), t.appendChild(document.createTextNode(i));
}
}
class D extends L {
constructor() {
super(...arguments);
g(this, "type", "lifecycle");
g(this, "priority", E.CUSTOM);
g(this, "activeAbortController");
}
apply(i, t) {
if (!t.element) return;
const e = this.properties;
this.activeAbortController && this.activeAbortController.abort(), (e.onAppear || e.onDisappear) && this.setupLifecycleObserver(t.element, e), e.task && this.setupTask(t, e.task), e.refreshable && this.setupRefreshable(t.element, e.refreshable);
}
setupLifecycleObserver(i, t) {
const e = new IntersectionObserver(
(n) => {
n.forEach((s) => {
s.isIntersecting && t.onAppear ? t.onAppear() : !s.isIntersecting && t.onDisappear && t.onDisappear();
});
},
{
threshold: 0.1,
// Trigger when 10% of element is visible
rootMargin: "10px"
// Add some margin for better UX
}
);
e.observe(i), this.addCleanup(() => {
e.disconnect();
});
}
setupTask(i, t) {
if (!t) return;
this.activeAbortController = new AbortController();
const { signal: e } = this.activeAbortController;
(async () => {
try {
if (e.aborted) return;
const s = t.operation();
s instanceof Promise && await s;
} catch (s) {
if (e.aborted) return;
console.error("TachUI Task Error:", s);
}
})(), this.addCleanup(() => {
this.activeAbortController && this.activeAbortController.abort();
});
}
setupRefreshable(i, t) {
if (!t) return;
let e = !1, n = 0, s = 0;
const o = 70, r = document.createElement("div");
if (r.style.cssText = `
position: absolute;
top: -50px;
left: 50%;
transform: translateX(-50%);
width: 30px;
height: 30px;
border: 2px solid #ccc;
border-top: 2px solid #007AFF;
border-radius: 50%;
animation: tachui-spin 1s linear infinite;
opacity: 0;
transition: opacity 0.3s ease;
z-index: 1000;
`, !document.querySelector("#tachui-refresh-styles")) {
const d = document.createElement("style");
d.id = "tachui-refresh-styles", d.textContent = `
@keyframes tachui-spin {
0% { transform: translateX(-50%) rotate(0deg); }
100% { transform: translateX(-50%) rotate(360deg); }
}
`, document.head.appendChild(d);
}
const l = i.parentElement || i;
l instanceof HTMLElement && (l.style.position = "relative", l.appendChild(r));
const c = (d) => {
e || (s = d.touches[0].clientY);
}, f = (d) => {
if (e) return;
const h = d.touches[0].clientY;
if (n = Math.max(0, h - s), n > 20) {
d.preventDefault();
const b = Math.min(n / o, 1);
r.style.opacity = String(b * 0.8), r.style.top = `${-50 + b * 20}px`;
}
}, p = async () => {
if (e || n < o) {
r.style.opacity = "0", r.style.top = "-50px", n = 0;
return;
}
e = !0, r.style.opacity = "1", r.style.top = "10px";
try {
await t.onRefresh();
} catch (d) {
console.error("TachUI Refresh Error:", d);
} finally {
e = !1, r.style.opacity = "0", r.style.top = "-50px", n = 0;
}
};
i.addEventListener("touchstart", c, {
passive: !1
}), i.addEventListener("touchmove", f, {
passive: !1
}), i.addEventListener("touchend", p), this.addCleanup(() => {
i.removeEventListener(
"touchstart",
c
), i.removeEventListener("touchmove", f), i.removeEventListener("touchend", p), r.parentElement && r.parentElement.removeChild(r);
});
}
addCleanup(i) {
this.properties._cleanupFunctions || (this.properties._cleanupFunctions = []), this.properties._cleanupFunctions.push(i);
}
}
export {
I as A,
L as B,
R as I,
V as L,
O as a,
D as b
};
//# sourceMappingURL=base-CkGf4b9G.js.map