{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"squash-toggle","type":"registry:ui","title":"Squash Toggle","description":"Toggle switch whose thumb widens in anticipation while pressed, stretches and flattens as it travels, then bounces into place.","dependencies":["motion"],"registryDependencies":[],"files":[{"path":"registry/ui/squash-toggle.tsx","type":"registry:ui","content":"\"use client\";\n\nimport { useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport { animate, motion, useMotionValue, useReducedMotion, useTransform, useVelocity } from \"motion/react\";\n\nexport interface SquashToggleProps {\n  /** Controlled state. */\n  checked?: boolean;\n  /** Initial state when uncontrolled. */\n  defaultChecked?: boolean;\n  onChange?: (checked: boolean) => void;\n  /** Visible label (also the accessible name). */\n  label?: ReactNode;\n  /** Track color when on. */\n  color?: string;\n  /** Spring bounciness, 0 stiff to 1 wobbly. */\n  bounce?: number;\n  disabled?: boolean;\n  className?: string;\n}\n\nconst TRACK_W = 60;\nconst THUMB = 26;\nconst PAD = 3;\nconst TRAVEL = TRACK_W - THUMB - PAD * 2;\n\nexport function SquashToggle({\n  checked,\n  defaultChecked = false,\n  onChange,\n  label = \"Airplane mode\",\n  color = \"#22c55e\",\n  bounce = 0.6,\n  disabled = false,\n  className,\n}: SquashToggleProps) {\n  const id = useId();\n  const reduce = useReducedMotion();\n  const [inner, setInner] = useState(defaultChecked);\n  const [pressed, setPressed] = useState(false);\n  const on = checked ?? inner;\n  const first = useRef(true);\n\n  const x = useMotionValue(on ? TRAVEL : 0);\n  const v = useVelocity(x);\n  // Stretch along the direction of travel, flatten to keep the volume.\n  const scaleX = useTransform(v, (s) => 1 + Math.min(Math.abs(s) / 900, 0.55));\n  const scaleY = useTransform(v, (s) => 1 - Math.min(Math.abs(s) / 2200, 0.24));\n\n  useEffect(() => {\n    if (first.current) {\n      first.current = false;\n      return;\n    }\n    const to = on ? TRAVEL : 0;\n    if (reduce) x.set(to);\n    else animate(x, to, { type: \"spring\", stiffness: 520, damping: 34 - bounce * 26, velocity: on ? 400 : -400 });\n  }, [on, reduce, bounce, x]);\n\n  const toggle = () => {\n    if (disabled) return;\n    if (checked === undefined) setInner(!on);\n    onChange?.(!on);\n  };\n\n  return (\n    <div className={`inline-flex items-center gap-3 ${disabled ? \"opacity-50\" : \"\"} ${className ?? \"\"}`}>\n      <button\n        id={id}\n        type=\"button\"\n        role=\"switch\"\n        aria-checked={on}\n        disabled={disabled}\n        onClick={toggle}\n        onPointerDown={() => setPressed(true)}\n        onPointerUp={() => setPressed(false)}\n        onPointerLeave={() => setPressed(false)}\n        onKeyDown={(e) => e.key === \" \" && !e.repeat && setPressed(true)}\n        onKeyUp={() => setPressed(false)}\n        onBlur={() => setPressed(false)}\n        className=\"relative shrink-0 cursor-pointer rounded-full outline-none transition-[background-color,box-shadow] duration-300 focus-visible:ring-[3px] focus-visible:ring-ring/60 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed\"\n        style={{\n          width: TRACK_W,\n          height: THUMB + PAD * 2,\n          background: on ? color : \"color-mix(in oklab, var(--muted-foreground) 32%, transparent)\",\n          boxShadow: \"inset 0 1px 3px rgb(0 0 0 / .3)\",\n        }}\n      >\n        <motion.span\n          aria-hidden\n          className=\"absolute rounded-full bg-white shadow-[0_2px_4px_rgb(0_0_0/.3),0_0_0_.5px_rgb(0_0_0/.08)]\"\n          style={{ top: PAD, left: PAD, height: THUMB, x, scaleX, scaleY, originX: on ? 1 : 0 }}\n          // Anticipation: the thumb widens toward where it's about to go while held.\n          animate={{ width: pressed && !disabled ? THUMB + 7 : THUMB, marginLeft: pressed && on && !disabled ? -7 : 0 }}\n          initial={false}\n          transition={{ type: \"spring\", stiffness: 600, damping: 30 }}\n        />\n      </button>\n      {label && (\n        <label htmlFor={id} className={`select-none text-sm font-medium text-foreground ${disabled ? \"cursor-not-allowed\" : \"cursor-pointer\"}`}>\n          {label}\n        </label>\n      )}\n    </div>\n  );\n}\n"}]}