UNPKG

liquidify-react

Version:

🚀 Production-ready React component library inspired by Apple's new design language post-WWDC 2025. Built with Panda CSS and React, using Bun as runtime (npm compatible). Helps developers maintain design consistency for Apple platform apps. 47+ components

383 lines (382 loc) • 10.7 kB
import { jsxs as me, jsx as R } from "react/jsx-runtime"; import { useSpring as P, useMotionValue as N, useTransform as D, motion as fe } from "framer-motion"; import { useCallback as h, useState as Y, useEffect as F, useRef as ge, forwardRef as he, useMemo as _e } from "react"; import { d as j, u as q, s as K, b as be, e as Me, c as U, f as ve, h as ye, i as we, w as Ee, m as Ce } from "./cva-CMV13orU.mjs"; import { c as T } from "./cx-DSqNu2Ai.mjs"; const Se = (e, o, n) => { const i = (t) => ({ [e]: "__ignore__", ...o, ...be(t) }); return { recipeFn: (t, a = !0) => { const m = Me({ conditions: { shift: ye, finalize: ve, breakpoints: { keys: ["base"] } }, utility: { toHash: (c, l) => l(c.join(":")), transform: (c, l) => (we(e, n, t, c), l === "__ignore__" ? { className: e } : (l = Ee(l), { className: `${e}--${c}_${l}` })) } }), u = i(t); if (a) { const c = j( n, u ); return T(m(u), U(c)); } return m(u); }, getVariantProps: i, __getCompoundVariantCss__: (t) => j(n, i(t)) }; }, Pe = (e, o) => { if (e && !o) return e; if (!e && o) return o; const n = (...t) => T(e(...t), o(...t)), i = q(e.variantKeys, o.variantKeys), s = i.reduce((t, a) => (t[a] = q(e.variantMap[a], o.variantMap[a]), t), {}); return Object.assign(n, { __recipe__: !0, __name__: `${e.__name__} ${o.__name__}`, raw: (t) => t, variantKeys: i, variantMap: s, splitVariantProps(t) { return K(t, i); } }); }, L = /* @__PURE__ */ Se( "liquid-button", { variant: "primary", size: "md" }, [] ), B = { variant: ["primary", "secondary", "ghost", "danger", "success", "warning"], size: ["sm", "md", "lg", "xl"] }, I = Object.keys(B), Re = /* @__PURE__ */ Object.assign(Ce(L.recipeFn), { __recipe__: !0, __name__: "button", __getCompoundVariantCss__: L.__getCompoundVariantCss__, raw: (e) => e, variantKeys: I, variantMap: B, merge(e) { return Pe(this, e); }, splitVariantProps(e) { return K(e, I); }, getVariantProps: L.getVariantProps }); function Ve(e = !0) { return e ? U({ _focusVisible: { outline: "none", boxShadow: "0 0 0 3px color-mix(in oklch, var(--colors-accent-dynamic) 35%, transparent)", borderColor: "token(colors.accent.dynamic)" } }) : ""; } function X(e = "hover") { return { hover: "liquid-flow", active: "liquid-pressed", focus: "liquid-flow" }[e]; } Object.assign(X, { gentle: X("hover"), interactive: "liquid-flow", smooth: "liquid-flow" }); const xe = (e = {}) => { const { stiffness: o = 350, // Enhanced for more Apple-like responsiveness damping: n = 22, // Optimized for smooth settle rippleColor: i = "rgba(255,255,255,0.3)", onTap: s } = e, t = Ne(), a = P(1, { stiffness: t ? 0 : o, damping: t ? 0 : n, mass: t ? 0 : 0.9, // Add mass for more realistic physics feel restDelta: 1e-3 // Fine-tuned settling threshold }), r = P(0, { stiffness: t ? 0 : o * 0.8, damping: t ? 0 : n * 1.1, mass: t ? 0 : 0.8 }), m = N(0), u = N(0), c = N(1), l = D(c, [0, 1], ["0%", "200%"]), M = D(c, [0, 1], [0, 1]), _ = h( (b) => { if (t || !s) return; const f = b.currentTarget.getBoundingClientRect(), v = "touches" in b ? b.touches[0]?.clientX ?? 0 : b.clientX, g = "touches" in b ? b.touches[0]?.clientY ?? 0 : b.clientY; m.set(v - f.left), u.set(g - f.top), c.set(1), c.set(0), s(); }, [t, s, m, u, c] ), E = h(() => { t || (a.set(1.025), r.set(-1.5)); }, [a, r, t]), C = h(() => { t || (a.set(1), r.set(0)); }, [a, r, t]), S = h(() => { t || (a.set(0.955), r.set(0.5)); }, [a, r, t]), d = h(() => { t || (a.set(1.025), r.set(-1.5)); }, [a, r, t]); return { scale: a, // Use in motion.div style={{ scale }} y: r, // Use in motion.div style={{ y }} ripple: { createRippleProps: () => ({ style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", background: `radial-gradient(circle, ${i} 0%, transparent 70%)`, scale: M, opacity: c, borderRadius: "inherit", pointerEvents: "none" }, initial: { scale: 0, opacity: 0 }, transition: { duration: 0.3, ease: "easeOut" } }), handleTap: _, // Motion values for direct use x: m, y: u, opacity: c, size: l, scale: M }, interactions: { onHoverStart: E, onHoverEnd: C, onPressStart: S, onPressEnd: d }, reducedMotion: t }; }, Ne = () => { const [e, o] = Y(!1); return F(() => { const n = window.matchMedia("(prefers-reduced-motion: reduce)"); o(n.matches); const i = (s) => o(s.matches); return n.addEventListener("change", i), () => n.removeEventListener("change", i); }, []), e; }, Le = () => { const [e, o] = Y(!1); return F(() => { const n = window.matchMedia("(prefers-reduced-motion: reduce)"); o(n.matches); const i = (s) => o(s.matches); return n.addEventListener("change", i), () => n.removeEventListener("change", i); }, []), e; }, Te = (e = {}) => { const { strength: o = 0.3, disabled: n = !1 } = e, i = ge(null), s = Le(), t = P(0, { stiffness: 150, damping: 15 }), a = P(0, { stiffness: 150, damping: 15 }), r = P(1, { stiffness: 300, damping: 20 }), m = h( (l) => { if (n || !i.current || s) return; const _ = i.current.getBoundingClientRect(), E = _.left + _.width / 2, C = _.top + _.height / 2, S = (l.clientX - E) * o, d = (l.clientY - C) * o; t.set(S), a.set(d), r.set(1.05); }, [n, o, t, a, r, s] ), u = h(() => { n || s || (t.set(0), a.set(0), r.set(1)); }, [n, t, a, r, s]), c = h(() => { n || s || r.set(1.02); }, [n, r, s]); return { ref: i, style: { x: t, y: a, scale: r }, handlers: { onMouseMove: m, onMouseLeave: u, onMouseEnter: c } }; }; function He(e) { switch (e) { case "compact": return "sm"; case "regular": return "md"; case "large": return "lg"; case "sm": case "md": case "lg": case "xl": return e; default: return "md"; } } function Q(e) { return e === "primary" || e === "secondary" || e === "ghost" || e === "danger" || e === "success" || e === "warning"; } function ze(e, o = "accent") { if (Q(e)) return e; switch (e) { case "filled": case "tinted": case "plain": return e; default: return "filled"; } } function V(e) { return e === "button"; } const Oe = he(function(o, n) { const { as: i, variant: s = "filled", tone: t = "accent", size: a = "regular", icon: r, iconPosition: m = "start", loading: u = !1, disabled: c = !1, className: l, children: M, "aria-label": _, onClick: E, role: C, tabIndex: S, magneticHover: d = !0, magneticStrength: H = 0.3, ...b } = o, f = i || "button", v = !!c || !!u, g = xe({ stiffness: 350, damping: 22, onTap: E ? () => { } : void 0 // Enable tap effects when clickable }), y = Te({ strength: H, disabled: v || !d }), G = h( (p) => { y.ref.current !== p && (y.ref.current = p), typeof n == "function" ? n(p) : n && "current" in n && (n.current = p); }, [n, y.ref] ); Q(s) && typeof process < "u" && process.env?.NODE_ENV !== "production" && console.warn( `[Button] Legacy variant value "${s}" is deprecated. Use ${s === "primary" ? 'variant="filled" tone="accent"' : s === "secondary" ? 'variant="tinted" tone="neutral"' : s === "ghost" ? 'variant="plain" tone="neutral"' : s === "danger" ? 'variant="filled" tone="destructive"' : s === "success" ? 'variant="filled" tone="accent"' : s === "warning" ? 'variant="tinted" tone="accent"' : ""}.` ); const W = ze(s, t), J = He(a), z = !M && !!r; z && !_ && typeof process < "u" && process.env?.NODE_ENV !== "production" && console.warn( "[Button] Icon-only buttons must include an accessible name via `aria-label`." ); const Z = { "aria-busy": u || void 0, "aria-disabled": !V(f) && v ? !0 : void 0, "data-loading": u ? "" : void 0, "data-icon-only": z ? "" : void 0, "data-tone": t }, A = (p) => { if (v) { p.preventDefault(), p.stopPropagation(); return; } E?.(p); }, ee = !V(f) && !C ? "button" : C, te = !V(f) && S == null ? v ? -1 : 0 : S, ne = _e( () => d ? fe(f) : f, [d, f] ), w = h( (p, $, k) => p || $ || k ? (x) => { p?.(x), $?.(x), k?.(x); } : void 0, [] ), { onMouseMove: O, onMouseEnter: se, onMouseLeave: oe, onMouseDown: re, onMouseUp: ae, onTouchStart: ie, onTouchEnd: ce, style: ue, ...le } = b, de = { // Magnetic hover handlers (if enabled) onMouseMove: d ? w(O, y.handlers.onMouseMove) : O, onMouseEnter: w( se, d ? y.handlers.onMouseEnter : void 0, g.interactions.onHoverStart ), onMouseLeave: w( oe, d ? y.handlers.onMouseLeave : void 0, g.interactions.onHoverEnd ), // Enhanced spring physics for press interactions onMouseDown: w( re, g.interactions.onPressStart ), onMouseUp: w( ae, g.interactions.onPressEnd ), onTouchStart: w( ie, g.interactions.onPressStart ), onTouchEnd: w( ce, g.interactions.onPressEnd ) }, pe = { ...ue, ...d ? y.style : {}, // Add enhanced spring physics to all buttons scale: g.scale, y: g.y }; return /* @__PURE__ */ me( ne, { ref: d ? G : n, style: pe, className: T( Re({ variant: W, tone: t, size: J }), Ve(!0), l ), disabled: V(f) ? v : void 0, onClick: A, role: ee, tabIndex: te, "aria-label": _, ...Z, ...le, ...de, children: [ r && m === "start" ? /* @__PURE__ */ R("span", { "aria-hidden": "true", className: "btn__icon btn__icon--start", children: r }) : null, M ? /* @__PURE__ */ R("span", { className: "btn__label", children: M }) : null, r && m === "end" ? /* @__PURE__ */ R("span", { "aria-hidden": "true", className: "btn__icon btn__icon--end", children: r }) : null, u ? /* @__PURE__ */ R("span", { "aria-hidden": "true", className: "btn__spinner" }) : null ] } ); }); Oe.displayName = "Button"; export { Oe as B }; //# sourceMappingURL=button-Cxh3vJQ6.mjs.map