{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "lockout",
  "title": "Lockout",
  "description": "A lockout-tagout hasp: everyone working on something hangs their own padlock and tag, and it can't start until the last lock comes off. For freezes and holds.",
  "dependencies": [
    "@base-ui/react",
    "cn"
  ],
  "registryDependencies": [
    "https://bevel-ui.vercel.app/r/bevel.json"
  ],
  "files": [
    {
      "path": "registry/bevel/ui/lockout.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Button as ButtonPrimitive } from \"@base-ui/react/button\"\nimport { cn } from \"cn\"\n\ntype LockInfo = { owner: string }\n\ntype LockoutContextValue = {\n  locks: Record<string, LockInfo>\n  register: (id: string, info: LockInfo) => () => void\n  reasonId: string\n}\n\nconst LockoutContext = React.createContext<LockoutContextValue | null>(null)\n\nfunction useLockout() {\n  const context = React.useContext(LockoutContext)\n  if (!context) throw new Error(\"Lockout parts must be inside <Lockout>\")\n  return context\n}\n\n/**\n * A lockout hasp. Each person working on something hangs their own lock;\n * the action can't start until every lock is off. Give it an aria-label.\n */\nfunction Lockout({ className, children, ...props }: React.ComponentProps<\"div\">) {\n  const [locks, setLocks] = React.useState<Record<string, LockInfo>>({})\n  const reasonId = React.useId()\n  const register = React.useCallback((id: string, info: LockInfo) => {\n    setLocks((all) => ({ ...all, [id]: info }))\n    return () =>\n      setLocks((all) => {\n        const next = { ...all }\n        delete next[id]\n        return next\n      })\n  }, [])\n  const context = React.useMemo(() => ({ locks, register, reasonId }), [locks, register, reasonId])\n  return (\n    <LockoutContext value={context}>\n      <div\n        role=\"group\"\n        data-slot=\"lockout\"\n        data-locked={Object.keys(locks).length ? \"\" : undefined}\n        className={cn(\"flex w-full max-w-md flex-col gap-4\", className)}\n        {...props}\n      >\n        {children}\n      </div>\n    </LockoutContext>\n  )\n}\n\n/** The hasp the locks hang from. */\nfunction LockoutHasp({ className, children, ...props }: React.ComponentProps<\"ul\">) {\n  return (\n    <div className=\"flex flex-col\">\n      <span aria-hidden className=\"bevel-metal bevel-object relative z-10 h-4 rounded-full\" />\n      <ul\n        aria-label=\"Locks\"\n        data-slot=\"lockout-hasp\"\n        className={cn(\"-mt-1 flex min-h-36 flex-wrap justify-center gap-4 px-2\", className)}\n        {...props}\n      >\n        {children}\n      </ul>\n    </div>\n  )\n}\n\ntype LockoutLockProps = Omit<React.ComponentProps<\"li\">, \"id\"> & {\n  id: string\n  /** Whose lock it is. Shown on the tag. */\n  owner: string\n  /** Why it's locked. Shown on the tag. */\n  reason?: string\n  /** Only the owner can remove a lock: pass onRemove only for the current user's lock. */\n  onRemove?: () => void\n}\n\n/** A padlock with its owner's tag. */\nfunction LockoutLock({ className, id, owner, reason, onRemove, ...props }: LockoutLockProps) {\n  const { register } = useLockout()\n  React.useEffect(() => register(id, { owner }), [id, owner, register])\n  return (\n    <li data-slot=\"lockout-lock\" className={cn(\"flex w-28 flex-col items-center\", className)} {...props}>\n      <span aria-hidden className=\"h-6 w-8 rounded-t-full border-4 border-b-0 border-(--lockout-shackle)\" />\n      <span\n        aria-hidden\n        data-slot=\"lockout-lock-body\"\n        className=\"bevel-metal grid h-9 w-11 place-items-center rounded-(--bevel-radius-sm)\"\n      >\n        <span className=\"size-2 rounded-full bg-(--bevel-shade-strong)\" />\n      </span>\n      <span className=\"mt-1.5 flex w-full flex-col items-center gap-0.5 rounded-(--bevel-radius-xs) bg-(--lockout-tag) px-1.5 py-1 text-center text-(--lockout-tag-foreground) shadow-(--bevel-depth-sheet)\">\n        <span className=\"text-(length:--bevel-fine) font-black tracking-(--bevel-tracking-label) uppercase\">Danger</span>\n        <span className=\"text-xs font-semibold\">{owner}</span>\n        {reason && <span className=\"text-(length:--bevel-fine) leading-tight\">{reason}</span>}\n      </span>\n      {onRemove && (\n        <button\n          type=\"button\"\n          onClick={onRemove}\n          className=\"mt-1.5 h-7 rounded-(--bevel-radius-sm) px-2 text-xs font-medium underline underline-offset-2 outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n        >\n          Remove my lock\n        </button>\n      )}\n    </li>\n  )\n}\n\n/** The action the locks hold. Disabled while any lock remains, and says whose. */\nfunction LockoutAction({ className, children, ...props }: ButtonPrimitive.Props) {\n  const { locks, reasonId } = useLockout()\n  const owners = Object.values(locks).map((l) => l.owner)\n  const locked = owners.length > 0\n  return (\n    <div className=\"flex flex-col items-center gap-2\">\n      <ButtonPrimitive\n        data-slot=\"lockout-action\"\n        disabled={locked}\n        focusableWhenDisabled\n        aria-describedby={reasonId}\n        className={cn(\n          \"bevel-raised h-10 rounded-(--bevel-radius-sm) px-5 text-sm font-semibold outline-none [--bevel-material:var(--lockout-clear)]\",\n          \"transition-[translate,box-shadow] duration-(--bevel-duration-press) ease-(--bevel-ease-out) active:translate-y-0.5 active:bevel-pressed motion-reduce:transition-none\",\n          \"focus-visible:ring-3 focus-visible:ring-ring/60 data-disabled:cursor-not-allowed data-disabled:opacity-50\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n      </ButtonPrimitive>\n      <p id={reasonId} role=\"status\" className=\"text-center text-sm text-muted-foreground\">\n        {locked ? `Locked by ${owners.length}: ${owners.join(\", \")}` : \"No locks. Ready to start.\"}\n      </p>\n    </div>\n  )\n}\n\nexport { Lockout, LockoutHasp, LockoutLock, LockoutAction }\nexport type { LockoutLockProps }\n",
      "type": "registry:ui"
    }
  ],
  "css": {
    ":root": {
      "--lockout-hasp": "oklch(0.72 0.01 260)",
      "--lockout-hasp-edge": "oklch(0.48 0.01 260)",
      "--lockout-lock": "oklch(0.55 0.2 27)",
      "--lockout-lock-edge": "oklch(0.38 0.16 27)",
      "--lockout-shackle": "oklch(0.82 0.01 260)",
      "--lockout-tag": "oklch(0.86 0.15 90)",
      "--lockout-tag-foreground": "oklch(0.22 0.02 260)",
      "--lockout-clear": "oklch(0.6 0.15 150)"
    },
    "[data-slot=\"lockout\"]": {
      "--bevel-material": "var(--lockout-hasp)",
      "--bevel-material-edge": "var(--lockout-hasp-edge)"
    },
    "[data-slot=\"lockout-lock-body\"]": {
      "--bevel-material": "var(--lockout-lock)",
      "--bevel-material-edge": "var(--lockout-lock-edge)"
    }
  },
  "categories": [
    "coordination"
  ],
  "type": "registry:ui"
}