{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toggle-lever",
  "title": "Toggle Lever",
  "author": "Visant Labs <visantsupply@gmail.com>",
  "description": "Alavanca de painel sobre chapa escovada: empurrar contra a mola até passar do centro e ela estala para o outro lado, com a lente piloto acendendo.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/visant/ui/toggle-lever.tsx",
      "content": "\"use client\";\n\n/*\n * TOGGLE: an over-center toggle switch mounted on a brushed plate. Push the\n * knurled bat handle against its spring; cross the center cam and it snaps to\n * the opposite stop, throwing its shadow and specular ridge across the bezel.\n *\n * One normalized position u ∈ [−1, 1] drives the whole piece: bat foreshortening,\n * perspective taper, cast shadow, specular glint, and the pilot lens.\n */\nimport * as React from \"react\";\nimport { motion, useMotionValue, useMotionValueEvent, useReducedMotion, useSpring, useTransform } from \"motion/react\";\n\nconst GRAIN = `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E\")`;\nconst clamp = (v: number, a: number, b: number) => Math.max(a, Math.min(b, v));\n\nexport function ToggleLever() {\n  const reduce = useReducedMotion();\n  const uid = React.useId().replace(/:/g, \"\");\n  const [armed, setArmed] = React.useState(false);\n  const raw = useMotionValue(-1); // born in SAFE (-1): dragging right throws it over the cam into LIVE (+1)\n  const sprung = useSpring(raw, { stiffness: 560, damping: 28 });\n  const u = reduce ? raw : sprung;\n  const drag = React.useRef<{ x: number; from: number; moved: boolean } | null>(null);\n\n  useMotionValueEvent(raw, \"change\", (v) => setArmed(v >= 0));\n\n  const tipX = useTransform(u, (v) => v * 32);\n  const tipY = useTransform(u, (v) => -4 - (1 - Math.abs(v)) * 8);\n  const shadowX = useTransform(u, (v) => 100 - v * 24);\n  const glintX = useTransform(u, (v) => -3.2 + v * 2.8);\n  const pilotOp = useTransform(u, [-0.15, 0.45], [0.22, 1]);\n  const shaft = useTransform(u, (v) => {\n    const tx = 100 + v * 32;\n    const ty = 80 - (1 - Math.abs(v)) * 8;\n    const nx = -ty + 84;\n    const ny = tx - 100;\n    const len = Math.hypot(nx, ny) || 1;\n    const ux = nx / len;\n    const uy = ny / len;\n    return `M ${(100 - ux * 3.8).toFixed(1)} ${(84 - uy * 3.8).toFixed(1)} L ${(100 + ux * 3.8).toFixed(1)} ${(84 + uy * 3.8).toFixed(1)} L ${(tx + ux * 7.8).toFixed(1)} ${(ty + uy * 7.8).toFixed(1)} L ${(tx - ux * 7.8).toFixed(1)} ${(ty - uy * 7.8).toFixed(1)} Z`;\n  });\n  const ridge = useTransform(u, (v) => {\n    const tx = 100 + v * 32;\n    const ty = 80 - (1 - Math.abs(v)) * 8 - 2.2;\n    return `M 100 82.5 L ${tx.toFixed(1)} ${ty.toFixed(1)}`;\n  });\n\n  const snapTo = (next: 1 | -1) => {\n    if (reduce) raw.jump(next);\n    else raw.set(next);\n    navigator.vibrate?.(10);\n  };\n\n  return (\n    <div\n      role=\"switch\"\n      tabIndex={0}\n      aria-checked={armed}\n      aria-label=\"Chave geral de produção\"\n      onKeyDown={(e) => {\n        if (e.key === \" \" || e.key === \"Enter\") { e.preventDefault(); snapTo(armed ? -1 : 1); }\n        if (e.key === \"ArrowRight\") { e.preventDefault(); snapTo(1); }\n        if (e.key === \"ArrowLeft\") { e.preventDefault(); snapTo(-1); }\n      }}\n      onPointerDown={(e) => {\n        e.currentTarget.setPointerCapture(e.pointerId);\n        drag.current = { x: e.clientX, from: raw.get(), moved: false };\n      }}\n      onPointerMove={(e) => {\n        const d = drag.current;\n        if (!d) return;\n        const dx = e.clientX - d.x;\n        if (Math.abs(dx) > 3) d.moved = true;\n        const lin = clamp(d.from + dx / 44, -1, 1);\n        const cam = lin + 0.18 * Math.sin(lin * Math.PI);\n        if (reduce) raw.jump(clamp(cam, -1, 1));\n        else raw.set(clamp(cam, -1, 1));\n      }}\n      onPointerUp={() => {\n        const d = drag.current;\n        drag.current = null;\n        if (!d) return;\n        snapTo(!d.moved ? (armed ? -1 : 1) : raw.get() >= 0 ? 1 : -1);\n      }}\n      onPointerCancel={() => { drag.current = null; snapTo(raw.get() >= 0 ? 1 : -1); }}\n      className=\"relative isolate h-full w-full cursor-grab touch-none select-none overflow-hidden rounded-[var(--r-surface)] bg-muted outline-none focus-visible:[box-shadow:var(--focus)] active:cursor-grabbing\"\n    >\n      <svg aria-hidden viewBox=\"0 0 200 160\" preserveAspectRatio=\"xMidYMid meet\" className=\"absolute inset-0 h-full w-full\">\n        <defs>\n          <linearGradient id={`plate-${uid}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n            <stop offset=\"0\" style={{ stopColor: \"var(--card)\" }} />\n            <stop offset=\"1\" style={{ stopColor: \"var(--background)\" }} />\n          </linearGradient>\n          <linearGradient id={`shaft-${uid}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n            <stop offset=\"0\" style={{ stopColor: \"var(--foreground)\" }} />\n            <stop offset=\"0.55\" style={{ stopColor: \"var(--muted-foreground)\" }} />\n            <stop offset=\"1\" style={{ stopColor: \"var(--background)\" }} />\n          </linearGradient>\n          <radialGradient id={`cap-${uid}`} cx=\"0.32\" cy=\"0.28\" r=\"0.72\">\n            <stop offset=\"0\" style={{ stopColor: \"var(--foreground)\" }} />\n            <stop offset=\"0.55\" style={{ stopColor: \"var(--muted-foreground)\" }} />\n            <stop offset=\"0.88\" style={{ stopColor: \"var(--muted-foreground)\" }} />\n            <stop offset=\"1\" style={{ stopColor: \"var(--background)\" }} />\n          </radialGradient>\n        </defs>\n        {/* Recessed brushed plate with top highlight and engraved throw track */}\n        <rect x=\"22\" y=\"20\" width=\"156\" height=\"120\" rx=\"14\" fill={`url(#plate-${uid})`} className=\"stroke-border\" strokeWidth=\"1\" />\n        <line x1=\"34\" y1=\"21\" x2=\"166\" y2=\"21\" strokeWidth=\"1\" className=\"stroke-foreground opacity-20\" />\n        <rect x=\"58\" y=\"75\" width=\"84\" height=\"18\" rx=\"9\" className=\"fill-background stroke-border\" strokeWidth=\"1\" />\n        {[-1, 1].map((sx) =>\n          [-1, 1].map((sy) => (\n            <g key={`${sx}${sy}`} transform={`translate(${100 + sx * 64} ${80 + sy * 46})`}>\n              <circle r=\"3.2\" className=\"fill-background stroke-border\" strokeWidth=\"0.8\" />\n              <line x1=\"-2\" y1=\"-1\" x2=\"2\" y2=\"1\" strokeWidth=\"0.8\" className=\"stroke-muted-foreground\" />\n            </g>\n          )),\n        )}\n        {/* Pilot jewel indicator */}\n        <circle cx=\"100\" cy=\"39\" r=\"5.5\" className=\"fill-background stroke-border\" strokeWidth=\"1\" />\n        <motion.circle cx=\"100\" cy=\"39\" r=\"3.4\" style={{ opacity: pilotOp }} className=\"fill-[var(--chart-2)]\" />\n        {/* Hex mounting nut and recessed socket */}\n        <polygon points=\"100,59 121,70 121,96 100,107 79,96 79,70\" className=\"fill-card stroke-muted-foreground\" strokeWidth=\"1\" />\n        <circle cx=\"100\" cy=\"83\" r=\"14.5\" className=\"fill-background stroke-border\" strokeWidth=\"1\" />\n        <circle cx=\"100\" cy=\"83\" r=\"7.5\" className=\"fill-muted stroke-muted-foreground\" strokeWidth=\"0.7\" />\n        {/* Cast shadow across the plate */}\n        <motion.ellipse cx={shadowX} cy=\"93\" rx=\"22\" ry=\"7.5\" className=\"fill-background opacity-80 blur-[2.5px]\" />\n        {/* Conical bat shaft + specular highlight ridge */}\n        <motion.path d={shaft} fill={`url(#shaft-${uid})`} className=\"stroke-background\" strokeWidth=\"0.9\" />\n        <motion.path d={ridge} strokeWidth=\"1.8\" strokeLinecap=\"round\" className=\"stroke-foreground opacity-80\" />\n        {/* Machined bat tip with knurled rings and crisp catchlight */}\n        <motion.g style={{ x: tipX, y: tipY }}>\n          <circle cx=\"100\" cy=\"84\" r=\"12\" fill={`url(#cap-${uid})`} className=\"stroke-background\" strokeWidth=\"1\" />\n          <circle cx=\"100\" cy=\"84\" r=\"9.2\" fill=\"none\" strokeWidth=\"0.75\" strokeDasharray=\"1.8 1.8\" className=\"stroke-background opacity-55\" />\n          <circle cx=\"100\" cy=\"84\" r=\"5.5\" fill=\"none\" strokeWidth=\"0.7\" className=\"stroke-foreground opacity-45\" />\n          <motion.circle cx=\"100\" cy=\"80.2\" r=\"2.2\" style={{ x: glintX }} className=\"fill-foreground opacity-90\" />\n        </motion.g>\n      </svg>\n      <div aria-hidden className=\"pointer-events-none absolute inset-x-9 top-1/2 flex -translate-y-1/2 justify-between font-mono text-[10px] uppercase tracking-[0.14em]\">\n        <span className={armed ? \"text-muted-foreground opacity-45\" : \"text-foreground\"}>safe</span>\n        <span className={armed ? \"text-[var(--chart-2)]\" : \"text-muted-foreground opacity-45\"}>live</span>\n      </div>\n      <div aria-hidden className=\"pointer-events-none absolute inset-0 opacity-[0.24] mix-blend-overlay\" style={{ backgroundImage: GRAIN }} />\n    </div>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "meta": {
    "uso": {
      "quando": "Ligar e desligar algo que pede um gesto firme: energia, modo, armar",
      "evite": "Interruptor discreto numa lista de preferências: glass-switch; painel de aparelho antigo: vintage-toggle"
    }
  },
  "categories": [
    "controles",
    "alavanca",
    "interruptor",
    "ligar",
    "metal",
    "painel"
  ],
  "type": "registry:ui"
}