// Original glass interaction; legacy slot adapted from shadcn/ui (MIT). "use client"; import { useEffect, useLayoutEffect, useImperativeHandle, useRef, useState, type ComponentProps, type CSSProperties, } from "react"; import { Toggle as Primitive } from "./_ynrlib/radix-ui.js"; export type ToggleProps = ComponentProps & { size?: "xs" | "sm" | "md" | "lg"; }; type Gesture = { id: number; x: number; y: number; start: number; position: number; travel: number; dragged: boolean; }; const clamp = (value: number) => Math.max(0, Math.min(1, value)); /** Keeps the existing pressed API. asChild retains the original slotted button. */ export function Toggle(props: ToggleProps) { if (props.asChild) { const { className = "", size: _size, ...rest } = props; return ( ); } return ; } function GlassToggle({ pressed, size = "lg", defaultPressed = false, onPressedChange, disabled = false, className = "", children, style, ref, asChild: _asChild, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onLostPointerCapture, onKeyDown, onKeyUp, onClick, onBlur, type = "button", ...props }: ToggleProps) { const [internalPressed, setInternalPressed] = useState(defaultPressed); const isPressed = pressed ?? internalPressed; const [phase, setPhase] = useState<"rest" | "held" | "travel">("rest"); const frame = useRef(null); const root = useRef(null); const gesture = useRef(null); const key = useRef(null); const settleTimer = useRef | undefined>( undefined, ); const ignoreClick = useRef(false); useImperativeHandle(ref, () => root.current!, []); function clearDrag() { if (frame.current !== null) cancelAnimationFrame(frame.current); frame.current = null; root.current?.style.removeProperty("--toggle-drag-progress"); root.current?.removeAttribute("data-dragging"); } function suppressClick() { ignoreClick.current = true; } function finish() { clearTimeout(settleTimer.current); clearDrag(); if (globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches) { setPhase("rest"); } else { setPhase("travel"); settleTimer.current = setTimeout(() => setPhase("rest"), 120); } } function cancel() { const id = gesture.current?.id; gesture.current = null; key.current = null; suppressClick(); finish(); if (id !== undefined && root.current?.hasPointerCapture(id)) { root.current.releasePointerCapture(id); } } function commit(next: boolean) { finish(); if (next === isPressed || disabled) return; if (pressed === undefined) setInternalPressed(next); onPressedChange?.(next); } function hold() { clearTimeout(settleTimer.current); setPhase("held"); } useEffect(() => { // External changes or disabling during a gesture abandon only the draft. if (gesture.current || key.current) cancel(); }, [disabled, isPressed, size]); useEffect(() => { function interrupt() { if (gesture.current || key.current) cancel(); } window.addEventListener("blur", interrupt); return () => { window.removeEventListener("blur", interrupt); clearTimeout(settleTimer.current); if (frame.current !== null) cancelAnimationFrame(frame.current); }; }, []); // React owns committed state; pointer movement only updates one frame-coalesced CSS value. useLayoutEffect(() => { if (!gesture.current) clearDrag(); }); const variables = { "--toggle-progress": isPressed ? 1 : 0, ...style, } as CSSProperties; return ( ); }