{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "motion",
  "title": "Motion",
  "description": "The easing curves, durations and reduced-motion check every component animates with, plus hooks for size morphs and sliding highlights.",
  "files": [
    {
      "path": "lib/motion.ts",
      "content": "import * as React from \"react\"\n\nconst easeOut = \"cubic-bezier(0.23, 1, 0.32, 1)\"\nconst easeInOut = \"cubic-bezier(0.77, 0, 0.175, 1)\"\nconst easeSpring =\n  \"linear(0, 0.015 2%, 0.0532 4%, 0.1065 6%, 0.1686 8%, 0.2351 10%, 0.3363 13%, 0.4332 16%, 0.5495 20%, 0.648 24%, 0.7287 28%, 0.807 33%, 0.8646 38%, 0.9128 44%, 0.9447 50%, 0.968 57%, 0.9834 65%, 0.9924 74%, 0.9975 85%, 1)\"\n\nconst duration = {\n  press: 100,\n  release: 200,\n  hover: 150,\n  enter: 200,\n  exit: 150,\n  morph: 300,\n} as const\n\nfunction prefersReducedMotion() {\n  return (\n    typeof window === \"undefined\" ||\n    typeof window.matchMedia !== \"function\" ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  )\n}\n\ntype SizeAxis = \"width\" | \"height\"\n\ntype SizeMorphOptions = {\n  axis: SizeAxis\n  enabled?: boolean\n  duration?: number\n  easing?: string\n}\n\nfunction readSize(element: HTMLElement, axis: SizeAxis) {\n  const value = parseFloat(getComputedStyle(element)[axis])\n  if (Number.isFinite(value)) {\n    return value\n  }\n  const rect = element.getBoundingClientRect()\n  return axis === \"width\" ? rect.width : rect.height\n}\n\nfunction attachSizeMorph(\n  element: HTMLElement,\n  axis: SizeAxis,\n  time: number,\n  easing: string\n) {\n  if (\n    typeof MutationObserver === \"undefined\" ||\n    typeof element.animate !== \"function\"\n  ) {\n    return undefined\n  }\n\n  let settled = readSize(element, axis)\n  let animation: Animation | null = null\n\n  const morph = () => {\n    const from = animation ? readSize(element, axis) : settled\n    animation?.cancel()\n    animation = null\n    const to = readSize(element, axis)\n    settled = to\n\n    if (\n      Math.abs(from - to) < 0.5 ||\n      prefersReducedMotion() ||\n      !element.isConnected ||\n      element.getClientRects().length === 0\n    ) {\n      element.removeAttribute(\"data-morphing\")\n      return\n    }\n\n    element.setAttribute(\"data-morphing\", \"\")\n    const running = element.animate(\n      [{ [axis]: `${from}px` }, { [axis]: `${to}px` }],\n      { duration: time, easing }\n    )\n    animation = running\n    running.onfinish = () => {\n      if (animation === running) {\n        animation = null\n        element.removeAttribute(\"data-morphing\")\n        settled = readSize(element, axis)\n      }\n    }\n  }\n\n  const mutations = new MutationObserver(morph)\n  mutations.observe(element, {\n    childList: true,\n    subtree: true,\n    characterData: true,\n  })\n\n  const resize =\n    typeof ResizeObserver === \"undefined\"\n      ? null\n      : new ResizeObserver(() => {\n          if (!animation) {\n            settled = readSize(element, axis)\n          }\n        })\n  resize?.observe(element)\n\n  return () => {\n    mutations.disconnect()\n    resize?.disconnect()\n    animation?.cancel()\n    element.removeAttribute(\"data-morphing\")\n  }\n}\n\nfunction useSizeMorph<T extends HTMLElement>({\n  axis,\n  enabled = true,\n  duration: time = duration.morph,\n  easing = easeOut,\n}: SizeMorphOptions): React.RefCallback<T> {\n  return React.useCallback(\n    (element: T | null) => {\n      if (!element || !enabled) {\n        return undefined\n      }\n      return attachSizeMorph(element, axis, time, easing)\n    },\n    [axis, enabled, time, easing]\n  )\n}\n\nfunction useSlidingHighlight(\n  barRef: React.RefObject<HTMLElement | null>,\n  highlightRef: React.RefObject<HTMLElement | null>,\n  selector: string,\n  attribute = \"data-popup-open\"\n) {\n  React.useLayoutEffect(() => {\n    const bar = barRef.current\n    const highlight = highlightRef.current\n    if (!bar || !highlight) {\n      return\n    }\n\n    let current: HTMLElement | null = null\n\n    const place = (trigger: HTMLElement, instant: boolean) => {\n      if (instant || prefersReducedMotion()) {\n        highlight.setAttribute(\"data-instant\", \"\")\n      } else {\n        highlight.removeAttribute(\"data-instant\")\n      }\n      const frame = bar.getBoundingClientRect()\n      const box = trigger.getBoundingClientRect()\n      const scale = bar.offsetWidth > 0 ? frame.width / bar.offsetWidth : 1\n      highlight.style.left = \"0px\"\n      highlight.style.width = `${box.width / scale}px`\n      highlight.style.height = `${box.height / scale}px`\n      highlight.style.transform = `translate(${(box.left - frame.left) / scale - bar.clientLeft}px, ${(box.top - frame.top) / scale - bar.clientTop}px)`\n    }\n\n    const sync = () => {\n      const trigger = bar.querySelector<HTMLElement>(selector)\n      if (trigger === current) {\n        if (trigger) {\n          place(trigger, true)\n        }\n        return\n      }\n      const appearing = current === null\n      current = trigger\n      if (!trigger) {\n        highlight.removeAttribute(\"data-visible\")\n        return\n      }\n      place(trigger, appearing)\n      if (appearing) {\n        void highlight.offsetWidth\n      }\n      highlight.setAttribute(\"data-visible\", \"\")\n    }\n\n    sync()\n    const mutations = new MutationObserver(sync)\n    mutations.observe(bar, {\n      subtree: true,\n      childList: true,\n      attributes: true,\n      attributeFilter: [attribute],\n    })\n    const resize =\n      typeof ResizeObserver === \"undefined\"\n        ? null\n        : new ResizeObserver(() => {\n            if (current) {\n              place(current, true)\n            }\n          })\n    resize?.observe(bar)\n    const onScroll = () => {\n      if (current) {\n        place(current, true)\n      }\n    }\n    bar.addEventListener(\"scroll\", onScroll, { capture: true, passive: true })\n\n    return () => {\n      mutations.disconnect()\n      resize?.disconnect()\n      bar.removeEventListener(\"scroll\", onScroll, { capture: true })\n    }\n  }, [barRef, highlightRef, selector, attribute])\n}\n\nexport {\n  duration,\n  easeInOut,\n  easeOut,\n  easeSpring,\n  prefersReducedMotion,\n  useSizeMorph,\n  useSlidingHighlight,\n}\nexport type { SizeMorphOptions }\n",
      "type": "registry:lib"
    }
  ],
  "type": "registry:lib"
}