UNPKG

@tachui/modifiers

Version:

Essential styling modifiers for tachUI framework

843 lines (842 loc) 33.6 kB
var T = Object.defineProperty; var M = (v, l, n) => l in v ? T(v, l, { enumerable: !0, configurable: !0, writable: !0, value: n }) : v[l] = n; var y = (v, l, n) => M(v, typeof l != "symbol" ? l + "" : l, n); import { isSignal as u, isComputed as m, createEffect as g, getThemeSignal as w } from "@tachui/core/reactive"; import { ModifierPriority as E } from "@tachui/core/modifiers/types"; import { shouldExpandForInfinity as A, dimensionToCSS as S, isInfinity as C } from "@tachui/core/constants/layout"; class L { constructor(l) { this.properties = l; } /** * Helper to resolve reactive properties */ resolveReactiveProps(l, n) { const t = {}; for (const [e, i] of Object.entries(l)) u(i) || m(i), t[e] = i; return t; } /** * Apply a single style change to an element with reactive support */ applyStyleChange(l, n, t) { if (l instanceof HTMLElement) { const e = this.toCSSProperty(n); if (u(t) || m(t)) g(() => { const i = t(), s = String(i); if (s.includes("!important")) { const o = s.replace(/\s*!important\s*$/, "").trim(); l.style.setProperty(e, o, "important"); } else l.style.setProperty(e, s); }); else { const i = String(t); if (i.includes("!important")) { const s = i.replace(/\s*!important\s*$/, "").trim(); l.style.setProperty(e, s, "important"); } else l.style.setProperty(e, i); } } } /** * Convert camelCase property to CSS kebab-case */ toCSSProperty(l) { return l.replace(/([A-Z])/g, "-$1").toLowerCase(); } /** * Convert value to CSS value string */ toCSSValue(l) { return typeof l == "number" ? `${l}px` : String(l); } /** * Convert value to CSS value string with property-specific handling */ toCSSValueForProperty(l, n) { return typeof n == "number" ? [ "opacity", "z-index", "line-height", "flex-grow", "flex-shrink", "order", "column-count", "font-weight" ].includes(l) ? String(n) : `${n}px` : ([ "filter", // CSS filter strings should not be processed "transform", // CSS transform strings "clip-path" // CSS clip-path strings ].includes(l), String(n)); } /** * Apply multiple CSS properties to an element with reactive support */ applyStyles(l, n) { if (l instanceof HTMLElement || l.style) { const t = (l instanceof HTMLElement, l.style); for (const [e, i] of Object.entries(n)) if (i !== void 0) { const s = this.toCSSProperty(e); if (u(i) || m(i)) g(() => { const o = i(), r = this.toCSSValueForProperty( s, o ); if (t.setProperty) if (typeof r == "string" && r.includes("!important")) { const a = r.replace(/\s*!important\s*$/, "").trim(); t.setProperty(s, a, "important"); } else t.setProperty(s, r); else t[s] = r; }); else { const o = this.toCSSValueForProperty(s, i); 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(l, n) { l instanceof HTMLElement && l.classList.add(...n); } /** * Remove CSS classes from an element */ removeClasses(l, n) { l instanceof HTMLElement && l.classList.remove(...n); } /** * Create a style computation context */ createStyleContext(l, n, t) { return { componentId: l, element: n, modifiers: t, signals: /* @__PURE__ */ new Set(), cleanup: [] }; } } class R extends L { constructor() { super(...arguments); y(this, "type", "layout"); y(this, "priority", E.LAYOUT); } apply(n, t) { if (!n.element || !t.element) return; const e = this.createStyleContext( t.componentId, t.element, [] ), i = this.computeLayoutStyles( this.properties, e ); this.applyStyles(t.element, i); 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(n, t) { const { x: e, y: i } = t; if (u(e) || m(e) || u(i) || m(i)) g(() => { const s = u(e) || m(e) ? e() : e ?? 0, o = u(i) || m(i) ? i() : i ?? 0, r = this.toCSSValue(s), a = this.toCSSValue(o), c = `translate(${r}, ${a})`, h = (n.style.transform || "").split(" ").filter((p) => p && !p.startsWith("translate(")).join(" "), d = h ? `${h} ${c}` : c; n.style.transform = d; }); else { const s = e ?? 0, o = i ?? 0, r = this.toCSSValue(s), a = this.toCSSValue(o), c = `translate(${r}, ${a})`, h = (n.style.transform || "").split(" ").filter((p) => p && !p.startsWith("translate(")).join(" "), d = h ? `${h} ${c}` : c; n.style.transform = d; } } applyAspectRatio(n, t) { const { ratio: e, contentMode: i } = t; e !== void 0 && (u(e) || m(e) ? g(() => { const s = typeof e == "function" ? e() : e; n.style.aspectRatio = String(s); }) : n.style.aspectRatio = String(e), i === "fill" ? n.style.objectFit = "cover" : n.style.objectFit = "contain"); } // Phase 3 - Epic: Butternut Transform Methods applyScaleTransform(n, t) { const { x: e, y: i, anchor: s } = t, o = e ?? 1, r = i ?? o; if (u(o) || m(o) || u(r) || m(r)) g(() => { const a = u(o) || m(o) ? o() : o, c = u(r) || m(r) ? r() : r, f = `scale(${a}, ${c})`; n.style.transformOrigin = this.getTransformOrigin( s || "center" ); const d = (n.style.transform || "").replace(/\s*scale\([^)]*\)\s*/g, " ").replace(/\s+/g, " ").trim(), p = d ? `${d} ${f}` : f; n.style.transform = p; }); else { const a = `scale(${o}, ${r})`; n.style.transformOrigin = this.getTransformOrigin( s || "center" ); const f = (n.style.transform || "").replace(/\s*scale\([^)]*\)\s*/g, " ").replace(/\s+/g, " ").trim(), h = f ? `${f} ${a}` : a; n.style.transform = h; } } applyAbsolutePosition(n, t) { const { x: e, y: i } = t; if (n.style.position = "absolute", u(e) || m(e) || u(i) || m(i)) g(() => { const s = u(e) || m(e) ? e() : e ?? 0, o = u(i) || m(i) ? i() : i ?? 0; n.style.left = this.toCSSValue(s), n.style.top = this.toCSSValue(o); }); else { const s = e ?? 0, o = i ?? 0; n.style.left = this.toCSSValue(s), n.style.top = this.toCSSValue(o); } } applyZIndex(n, t) { u(t) || m(t) ? g(() => { const e = t(); n.style.zIndex = String(e); }) : n.style.zIndex = String(t); } getTransformOrigin(n) { 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" }[n] || "center center"; } computeLayoutStyles(n, t) { const e = {}; if (n.frame) { const i = n.frame, s = A(i); if (Object.assign(e, s.cssProps), i.width !== void 0) { const o = S(i.width); o !== void 0 && !C(i.width) && !s.expandWidth && (e.width = o); } if (i.height !== void 0) { const o = S(i.height); o !== void 0 && !C(i.height) && !s.expandHeight && (e.height = o); } if (i.minWidth !== void 0) { const o = S(i.minWidth); o !== void 0 && (e.minWidth = o); } if (i.maxWidth !== void 0 && !C(i.maxWidth)) { const o = S(i.maxWidth); o !== void 0 && (e.maxWidth = o); } else C(i.maxWidth) && (e.maxWidth = "none", e.flexGrow = "1 !important", e.flexShrink = "1 !important", e.flexBasis = "0% !important", e.alignSelf = "stretch !important"); if (i.minHeight !== void 0) { const o = S(i.minHeight); o !== void 0 && (e.minHeight = o); } if (i.maxHeight !== void 0 && !C(i.maxHeight)) { const o = S(i.maxHeight); o !== void 0 && (e.maxHeight = o); } else C(i.maxHeight) && (e.maxHeight = "none", e.flexGrow = "1 !important", e.flexShrink = "1 !important", e.flexBasis = "0% !important", e.alignSelf = "stretch !important"); } if (n.padding !== void 0) if (typeof n.padding == "number") e.padding = this.toCSSValue(n.padding); else { const i = n.padding; i.top !== void 0 && (e.paddingTop = this.toCSSValue(i.top)), i.right !== void 0 && (e.paddingRight = this.toCSSValue(i.right)), i.bottom !== void 0 && (e.paddingBottom = this.toCSSValue(i.bottom)), i.left !== void 0 && (e.paddingLeft = this.toCSSValue(i.left)); } if (n.margin !== void 0) if (typeof n.margin == "number") e.margin = this.toCSSValue(n.margin); else { const i = n.margin; i.top !== void 0 && (e.marginTop = this.toCSSValue(i.top)), i.right !== void 0 && (e.marginRight = this.toCSSValue(i.right)), i.bottom !== void 0 && (e.marginBottom = this.toCSSValue(i.bottom)), i.left !== void 0 && (e.marginLeft = this.toCSSValue(i.left)); } if (n.alignment) switch (n.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 (n.layoutPriority !== void 0) { const i = Number(n.layoutPriority); i > 0 ? (e.flexShrink = "0", e.flexGrow = String(Math.max(1, i / 10)), e.zIndex = String(i), e.gridRowEnd = `span ${String(Math.min(10, Math.max(1, Math.ceil(i / 10))))}`, e.gridColumnEnd = `span ${String(Math.min(10, Math.max(1, Math.ceil(i / 10))))}`) : i === 0 ? (e.flexShrink = "1", e.flexGrow = "1") : (e.flexShrink = String(Math.abs(i)), e.flexGrow = "0", e.zIndex = String(i)), e && typeof e == "object" && "setProperty" in e && e.setProperty("--layout-priority", String(i)); } if (n.offset, n.aspectRatio) { const { ratio: i, contentMode: s } = n.aspectRatio; i !== void 0 && (e.aspectRatio = typeof i == "number" ? String(i) : i, s === "fill" ? e.objectFit = "cover" : e.objectFit = "contain"); } if (n.fixedSize) { const { horizontal: i, vertical: s } = n.fixedSize; i && (e.flexShrink = "0", e.width = "max-content"), s && (e.flexShrink = "0", e.height = "max-content"); } return e; } } class x extends L { constructor() { super(...arguments); y(this, "type", "appearance"); y(this, "priority", E.APPEARANCE); } apply(n, t) { if (!n.element || !t.element) return; const e = this.createStyleContext( t.componentId, t.element, [] ), i = this.resolveReactiveProps( this.properties, e ); this.applyAssetBasedStyles(t.element, i); const s = this.computeAppearanceStyles(i); this.applyStyles(t.element, s), this.applyAttributes(t.element, i); } /** * Apply Asset-based styles with theme reactivity */ applyAssetBasedStyles(n, t) { const e = w(); t.foregroundColor && this.isAsset(t.foregroundColor) && g(() => { e(); const i = t.foregroundColor.resolve(); this.applyStyleChange(n, "color", i); }), t.backgroundColor && this.isAsset(t.backgroundColor) && g(() => { e(); const i = t.backgroundColor.resolve(); this.applyStyleChange(n, "backgroundColor", i); }), t.border?.color && this.isAsset(t.border.color) && g(() => { e(); const i = t.border.color.resolve(); this.applyStyleChange(n, "borderColor", i); }); } /** * Check if a value is an Asset object (including Asset proxies) */ isAsset(n) { return n != null && typeof n == "object" && "resolve" in n && typeof n.resolve == "function"; } computeAppearanceStyles(n) { const t = {}; if (n.foregroundColor && !this.isAsset(n.foregroundColor) && (t.color = n.foregroundColor), n.backgroundColor && !this.isAsset(n.backgroundColor) && (t.backgroundColor = n.backgroundColor), n.opacity !== void 0 && (t.opacity = n.opacity), n.font) { const i = n.font; i.family && (typeof i.family == "object" && i.family !== null && "resolve" in i.family ? t.fontFamily = i.family.resolve() : t.fontFamily = i.family), i.size && (t.fontSize = this.toCSSValue(i.size)), i.weight && (t.fontWeight = String(i.weight)), i.style && (t.fontStyle = i.style); } if (n.cornerRadius !== void 0 && (t.borderRadius = this.toCSSValue(n.cornerRadius)), n.border) { const i = n.border; i.width !== void 0 && (t.borderWidth = this.toCSSValue(i.width)), i.color && !this.isAsset(i.color) && (t.borderColor = i.color), i.style && (t.borderStyle = i.style); } if (n.shadow) { const i = n.shadow, s = i.x || 0, o = i.y || 0, r = i.radius || 0, a = i.color || "rgba(0,0,0,0.25)"; t.boxShadow = `${s}px ${o}px ${r}px ${a}`; } if (n.clipped && (t.overflow = "hidden"), n.clipShape) { const { shape: i, parameters: s } = n.clipShape; switch (i) { 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 n.blur !== void 0 && e.push(`blur(${n.blur}px)`), n.brightness !== void 0 && e.push(`brightness(${n.brightness})`), n.contrast !== void 0 && e.push(`contrast(${n.contrast})`), n.saturation !== void 0 && e.push(`saturate(${n.saturation})`), n.hueRotation !== void 0 && e.push(`hue-rotate(${n.hueRotation}deg)`), n.grayscale !== void 0 && e.push(`grayscale(${n.grayscale})`), n.colorInvert !== void 0 && e.push(`invert(${n.colorInvert})`), e.length > 0 && (t.filter = e.join(" ")), t; } /** * Apply HTML attributes (ARIA, role, data attributes, etc.) */ applyAttributes(n, t) { if (!n) return; const e = this.findComponentFromElement(n); t.role !== void 0 && (n.setAttribute("role", String(t.role)), e?.props && (e.props.role = String(t.role))), t["aria-label"] !== void 0 && (n.setAttribute("aria-label", String(t["aria-label"])), e?.props && (e.props["aria-label"] = String(t["aria-label"]))), t["aria-live"] !== void 0 && (n.setAttribute("aria-live", String(t["aria-live"])), e?.props && (e.props["aria-live"] = String(t["aria-live"]))), t["aria-describedby"] !== void 0 && (n.setAttribute( "aria-describedby", String(t["aria-describedby"]) ), e?.props && (e.props["aria-describedby"] = String(t["aria-describedby"]))), t["aria-modal"] !== void 0 && (n.setAttribute("aria-modal", String(t["aria-modal"])), e?.props && (e.props["aria-modal"] = String(t["aria-modal"]))), t["aria-hidden"] !== void 0 && (n.setAttribute("aria-hidden", String(t["aria-hidden"])), e?.props && (e.props["aria-hidden"] = String(t["aria-hidden"]))), t.navigationTitle !== void 0 && (n.setAttribute( "data-navigation-title", String(t.navigationTitle) ), e?.props && (e.props.navigationTitle = String(t.navigationTitle))), t.navigationBarHidden !== void 0 && (n.setAttribute( "data-navigation-bar-hidden", String(t.navigationBarHidden) ), e?.props && (e.props.navigationBarHidden = t.navigationBarHidden), t.navigationBarHidden && (n.setAttribute("aria-hidden", "true"), e?.props && (e.props["aria-hidden"] = "true"))), t.navigationBarItems !== void 0 && (n.setAttribute( "data-navigation-bar-items", JSON.stringify(t.navigationBarItems) ), e?.props && (e.props.navigationBarItems = t.navigationBarItems)); } findComponentFromElement(n) { return n._tachui_component || null; } } class I extends L { constructor() { super(...arguments); y(this, "type", "interaction"); y(this, "priority", E.INTERACTION); } apply(n, t) { if (!t.element) return; const e = this.properties; if (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.onTouchStart && t.element.addEventListener("touchstart", e.onTouchStart, { passive: !0 }), e.onTouchMove && t.element.addEventListener("touchmove", e.onTouchMove, { passive: !0 }), e.onTouchEnd && t.element.addEventListener("touchend", e.onTouchEnd, { passive: !0 }), e.onTouchCancel && t.element.addEventListener("touchcancel", e.onTouchCancel, { passive: !0 }), e.onSwipeLeft || e.onSwipeRight) { let i = 0, s = 0; t.element.addEventListener( "touchstart", (o) => { const a = o.touches[0]; i = a.clientX, s = a.clientY; }, { passive: !0 } ), t.element.addEventListener( "touchend", (o) => { const a = o.changedTouches[0], c = a.clientX - i, f = a.clientY - s; Math.abs(c) > Math.abs(f) && Math.abs(c) > 50 && (c < 0 && e.onSwipeLeft ? e.onSwipeLeft() : c > 0 && e.onSwipeRight && e.onSwipeRight()); }, { passive: !0 } ); } 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", (i) => { const s = i.target, o = s.value || s.textContent || ""; e.onChange(o, i); }), 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(n, t) { const e = t.minimumDuration ?? 500, i = t.maximumDistance ?? 10; let s, o = null, r = !1; const a = () => { s && (clearTimeout(s), s = void 0), r && t.onPressingChanged && t.onPressingChanged(!1), r = !1, o = null; }, c = (p) => { const b = p; o = { x: b.clientX, y: b.clientY }, r = !0, t.onPressingChanged && t.onPressingChanged(!0), s = window.setTimeout(() => { r && o && (t.perform(), a()); }, e); }, f = (p) => { const b = p; if (!o || !r) return; Math.sqrt( Math.pow(b.clientX - o.x, 2) + Math.pow(b.clientY - o.y, 2) ) > i && a(); }, h = () => { a(); }, d = () => { a(); }; n.addEventListener("pointerdown", c), n.addEventListener("pointermove", f), n.addEventListener("pointerup", h), n.addEventListener( "pointercancel", d ), n._longPressCleanup = a; } /** * Setup keyboard shortcut handling */ setupKeyboardShortcut(n, t) { const e = t.modifiers ?? [], i = (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 }, a = s.key.toLowerCase() === t.key.toLowerCase(), c = Object.entries(o).every( ([f, h]) => h === r[f] ); a && c && (s.preventDefault(), t.action()); }; document.addEventListener("keydown", i), n._keyboardShortcutCleanup = () => { document.removeEventListener("keydown", i); }; } /** * Setup focus management with reactive binding */ setupFocusManagement(n, t) { if (!(n instanceof HTMLElement)) return; const e = n; e.hasAttribute("tabindex") || e.setAttribute("tabindex", "0"), u(t) || m(t) ? g(() => { t() ? e.focus() : e.blur(); }) : t && e.focus(); } /** * Setup focusable behavior */ setupFocusable(n, t) { if (!(n instanceof HTMLElement)) return; const e = n; t.isFocusable === !1 ? e.setAttribute("tabindex", "-1") : e.hasAttribute("tabindex") || e.setAttribute("tabindex", "0"), t.interactions?.includes("activate") && e.addEventListener("keydown", (i) => { (i.key === "Enter" || i.key === " ") && (i.preventDefault(), e.click()); }), t.interactions?.includes("edit") && (e.setAttribute("role", "textbox"), e.setAttribute("contenteditable", "true")); } /** * Setup continuous hover tracking with coordinates */ setupContinuousHover(n, t) { const e = t.coordinateSpace ?? "local", i = (o) => { const r = o; let a, c; if (e === "local") { const f = n.getBoundingClientRect(); a = r.clientX - f.left, c = r.clientY - f.top; } else a = r.clientX, c = r.clientY; t.perform({ x: a, y: c }); }, s = () => { t.perform(null); }; n.addEventListener("mousemove", i), n.addEventListener("mouseleave", s), n._continuousHoverCleanup = () => { n.removeEventListener("mousemove", i), n.removeEventListener( "mouseleave", s ); }; } /** * Setup hit testing control */ setupHitTesting(n, t) { n instanceof HTMLElement && (n.style.pointerEvents = t ? "" : "none"); } } class O extends L { constructor() { super(...arguments); y(this, "type", "animation"); y(this, "priority", E.ANIMATION); } apply(n, t) { if (!t.element) return; const e = this.properties; if (e.transition) { const i = e.transition, s = i.property || "all", o = i.duration || 300, r = i.easing || "ease", a = i.delay || 0; t.element instanceof HTMLElement && (t.element.style.transition = `${s} ${o}ms ${r} ${a}ms`); } if (e.animation && t.element instanceof HTMLElement) { const i = e.animation; if (i.keyframes) { const s = `tachui-animation-${t.componentId}-${Date.now()}`, o = this.createKeyframeRule( s, i.keyframes ); this.addKeyframesToStylesheet(o); const r = i.duration || 1e3, a = i.easing || "ease", c = i.iterations || 1, f = i.direction || "normal"; t.element.style.animation = `${s} ${r}ms ${a} ${c} ${f}`; } } if (e.transform && t.element instanceof HTMLElement && (u(e.transform) || m(e.transform) ? g(() => { const i = e.transform(); t.element instanceof HTMLElement && (t.element.style.transform = i); }) : t.element.style.transform = e.transform), e.rotationEffect && t.element instanceof HTMLElement) { const { angle: i, 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%", a = `rotate(${i}deg)`; if (u(i) || m(i)) g(() => { const f = `rotate(${typeof i == "function" ? i() : i}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(" "), p = d ? `${d} ${f}` : f; t.element.style.transform = p; } }); 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(" "), h = f ? `${f} ${a}` : a; t.element.style.transform = h; } } e.overlay && t.element instanceof HTMLElement && this.applyOverlay(t.element, e.overlay, t); } applyOverlay(n, t, e) { const { content: i, alignment: s = "center" } = t; (n.style.position === "" || n.style.position === "static") && (n.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 i == "function") { const a = i(); if (a && typeof a.render == "function") { const c = a.render(); c.element && o.appendChild(c.element); } } else if (i && typeof i.render == "function") { const a = i.render(); a.element && o.appendChild(a.element); } else i instanceof HTMLElement && o.appendChild(i); n.appendChild(o); } getOverlayAlignment(n) { 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[n] || t.center; } createKeyframeRule(n, t) { let e = `@keyframes ${n} { `; for (const [i, s] of Object.entries(t)) { e += ` ${i} { `; for (const [o, r] of Object.entries(s)) { const a = this.toCSSProperty(o); e += ` ${a}: ${r}; `; } e += ` } `; } return e += "}", e; } addKeyframesToStylesheet(n) { let t = document.querySelector( "#tachui-animations" ); t || (t = document.createElement("style"), t.id = "tachui-animations", document.head.appendChild(t)), t.appendChild(document.createTextNode(n)); } } class D extends L { constructor() { super(...arguments); y(this, "type", "lifecycle"); y(this, "priority", E.CUSTOM); y(this, "activeAbortController"); // Cleanup helpers y(this, "cleanupFunctions", []); } apply(n, 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(n, t) { const e = new IntersectionObserver( (i) => { i.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(n), this.addCleanup(() => { e.disconnect(); }); } setupTask(n, 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(n, t) { if (!t) return; let e = !1, i = 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 a = n.parentElement || n; a instanceof HTMLElement && (a.style.position = "relative", a.appendChild(r)); const c = (d) => { e || (s = d.touches[0].clientY); }, f = (d) => { if (e) return; const p = d.touches[0].clientY; if (i = Math.max(0, p - s), i > 20) { const b = Math.min(1, i / o); r.style.opacity = String(b); } i > 0 && n.scrollTop === 0 && d.preventDefault(); }, h = async () => { if (!e) if (i > o) { e = !0, r.style.opacity = "1"; try { await t.onRefresh(); } catch (d) { console.error("Refresh error:", d); } finally { e = !1, r.style.opacity = "0", i = 0; } } else r.style.opacity = "0", i = 0; }; n.addEventListener("touchstart", c, { passive: !0 }), n.addEventListener("touchmove", f, { passive: !1 }), n.addEventListener("touchend", h, { passive: !0 }), this.addCleanup(() => { n.removeEventListener( "touchstart", c ), n.removeEventListener("touchmove", f), n.removeEventListener("touchend", h), r.parentElement && r.parentElement.removeChild(r); }); } addCleanup(n) { this.cleanupFunctions.push(n); } cleanup() { this.cleanupFunctions.forEach((n) => n()), this.cleanupFunctions = []; } } export { O as AnimationModifier, x as AppearanceModifier, L as BaseModifier, I as InteractionModifier, R as LayoutModifier, D as LifecycleModifier }; //# sourceMappingURL=base.js.map