{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"inertia-drag-slider","type":"registry:block","title":"inertia drag slider","description":"\"collection slider is 42\" — flick it and it glides with momentum, rubber-bands at the ends.","author":"Motion Menu","categories":["role-gallery","motion-system","js"],"dependencies":["gsap"],"registryDependencies":[],"meta":{"number":"42","slug":"inertia-drag-slider","framework":"react","docs":"https://motion-menu-two.vercel.app/p/inertia-drag-slider.md"},"files":[{"path":"motion-menu/inertia-drag-slider.html","type":"registry:file","target":"motion-menu/inertia-drag-slider.html","content":"<article class=\"card\">\n        <header><span class=\"num\">42</span><h3>Inertia drag slider</h3><div class=\"tags\"><i>JS physics</i></div><a class=\"format-action\" data-format-link data-pattern-slug=\"inertia-drag-slider\" href=\"/r/inertia-drag-slider.json?framework=react\" title=\"Open the React registry output\">React ↗</a></header>\n        <div class=\"demo\">\n          <div class=\"drag-view\" id=\"drag-view\"><div class=\"drag-track\" id=\"drag-track\"></div></div>\n          <p class=\"demo-hint\">grab and throw it</p>\n        </div>\n        <p class=\"order\">Say: <b>\"collection slider is 42\"</b> — flick it and it glides with momentum, rubber-bands at the ends.</p>\n      </article>"},{"path":"motion-menu/inertia-drag-slider.css","type":"registry:file","target":"motion-menu/inertia-drag-slider.css","content":".card {\n    border: 1px solid var(--line); border-radius: 14px; background: var(--card);\n    overflow: hidden; display: flex; flex-direction: column;\n  }\n\n.card > header { display: flex; align-items: baseline; gap: .8rem; padding: 1.1rem 1.2rem .9rem; border-bottom: 1px solid var(--line); }\n\n.card .num { font-family: var(--mono); color: var(--acc); font-size: 1.05rem; }\n\n.card h3 { font-size: 1.02rem; font-weight: 600; letter-spacing: -0.01em; flex: 1; }\n\n.tags { display: flex; gap: .35rem; }\n\n.tags i {\n    font: 400 .62rem/1 var(--mono); font-style: normal; color: var(--dim);\n    border: 1px solid var(--line); border-radius: 99px; padding: .28em .6em; white-space: nowrap;\n  }\n\n.demo {\n    min-height: 250px; position: relative; display: flex; align-items: center; justify-content: center;\n    background: var(--bg2); overflow: hidden; flex: 1;\n  }\n\n.order { padding: .85rem 1.2rem; font-family: var(--mono); font-size: .72rem; color: var(--dim); border-top: 1px solid var(--line); }\n\n.order b { color: var(--txt); font-weight: 500; }\n\n.demo .btn.corner { position: absolute; top: 12px; right: 12px; padding: .55em 1em; font-size: .68rem; z-index: 5; }\n\n.demo-hint { position: absolute; bottom: 10px; left: 0; right: 0; text-align: center; font: 400 .65rem var(--mono); color: var(--dim); pointer-events: none; z-index: 4; }\n\n/* ---------- Section G · motion systems ---------- */\n  .drag-view { position: absolute; inset: 0; display: flex; align-items: center; overflow: hidden; cursor: grab; touch-action: pan-y; }\n\n.drag-view:active { cursor: grabbing; }\n\n.drag-track { display: flex; gap: 14px; padding: 0 24px; will-change: transform; }\n\n.drag-item { flex: 0 0 160px; height: 160px; border-radius: 12px; display: flex; align-items: flex-end; padding: 10px;\n    font: 500 .68rem var(--mono); color: rgba(0,0,0,.55); user-select: none; }\n\n.ai-card .num { color: var(--acc2) !important; }\n\n.demo:has(.hascheck input:checked) .hasbtn { border-color: var(--acc); color: var(--acc); pointer-events: auto; opacity: 1; }"},{"path":"components/motion-menu/inertia-drag-slider.tsx","type":"registry:component","content":"// @ts-nocheck — generated wrapper; inlined pattern JS is imperative DOM code, not authored TS.\n\"use client\";\n// inertia drag slider — Motion Menu\nimport { useEffect, useRef } from \"react\";\n\nconst MARKUP = \"<article class=\\\"card\\\">\\n        <header><span class=\\\"num\\\">42</span><h3>Inertia drag slider</h3><div class=\\\"tags\\\"><i>JS physics</i></div><a class=\\\"format-action\\\" data-format-link data-pattern-slug=\\\"inertia-drag-slider\\\" href=\\\"/r/inertia-drag-slider.json?framework=react\\\" title=\\\"Open the React registry output\\\">React \\u2197</a></header>\\n        <div class=\\\"demo\\\">\\n          <div class=\\\"drag-view\\\" id=\\\"drag-view\\\"><div class=\\\"drag-track\\\" id=\\\"drag-track\\\"></div></div>\\n          <p class=\\\"demo-hint\\\">grab and throw it</p>\\n        </div>\\n        <p class=\\\"order\\\">Say: <b>\\\"collection slider is 42\\\"</b> \\u2014 flick it and it glides with momentum, rubber-bands at the ends.</p>\\n      </article>\";\n\nexport function InertiaDragSlider() {\n  const hostRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    const host = hostRef.current;\n    if (!host) return;\n    const $ = (s: string) => host.querySelector(s) as HTMLElement | null;\n    let disposed = false;\n    const cleanup: Array<() => void> = [];\n\n    (async () => {\n      if (disposed) return;\n      try {\n        /* ---- 42 · inertia drag slider ---- */\n        {\n          const view = document.getElementById('drag-view');\n          const track = document.getElementById('drag-track');\n          const grads = [\n            'linear-gradient(150deg,#b7ff2e,#3a6600)', 'linear-gradient(150deg,#7a5cff,#241a66)',\n            'linear-gradient(150deg,#0ea5b7,#063a44)', 'linear-gradient(150deg,#ff6e9c,#66102e)',\n            'linear-gradient(150deg,#ffd76e,#664c06)', 'linear-gradient(150deg,#e8e8ee,#55555f)',\n          ];\n          grads.forEach((g, i) => {\n            const el = document.createElement('div');\n            el.className = 'drag-item';\n            el.style.background = g;\n            el.textContent = `0${i + 1}`;\n            track.appendChild(el);\n          });\n          const s = { x: 0, v: 0, dragging: false, lastX: 0, lastT: 0 };\n          const bounds = () => Math.min(0, view.clientWidth - track.scrollWidth - 48);\n          view.addEventListener('pointerdown', (e) => {\n            s.dragging = true; s.v = 0; s.lastX = e.clientX; s.lastT = performance.now();\n            view.setPointerCapture(e.pointerId);\n          });\n          view.addEventListener('pointermove', (e) => {\n            if (!s.dragging) return;\n            const now = performance.now();\n            const dx = e.clientX - s.lastX;\n            s.x += dx;\n            s.v = dx / Math.max(now - s.lastT, 1) * 1000;\n            s.lastX = e.clientX; s.lastT = now;\n          });\n          const end = () => (s.dragging = false);\n          view.addEventListener('pointerup', end);\n          view.addEventListener('pointercancel', end);\n          gsap.ticker.add((time, deltaMS) => {\n            const delta = Math.min(deltaMS / 1000, 0.05);\n            if (!s.dragging) {\n              s.x += s.v * delta;\n              s.v *= Math.pow(0.06, delta);\n              const min = bounds();\n              if (s.x > 0) { s.x += (0 - s.x) * 0.18; s.v = 0; }\n              if (s.x < min) { s.x += (min - s.x) * 0.18; s.v = 0; }\n            }\n            track.style.transform = `translateX(${s.x}px)`;\n          });\n        }\n      } catch (err: any) {\n        console.warn(\"[motion-menu] inertia-drag-slider failed to start:\", err);\n      }\n    })();\n\n    return () => {\n      disposed = true;\n      cleanup.forEach((fn) => { try { fn(); } catch (_) {} });\n    };\n  }, []);\n\n  return <div ref={hostRef} dangerouslySetInnerHTML={{ __html: MARKUP }} />;\n}\n\nexport default InertiaDragSlider;\n"}]}