{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "corners",
  "title": "Corners",
  "description": "Four corner marks: a frame that doesn't close.",
  "dependencies": [
    "@radix-ui/react-slot"
  ],
  "registryDependencies": [
    "https://thedirectors.agency/ui/r/0nlytype.json"
  ],
  "files": [
    {
      "path": "registry/0nlytype/ui/corners.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype CornersProps = React.ComponentProps<\"div\"> & {\n  /** Put the corners on the child element instead of a new div. */\n  asChild?: boolean\n  /**\n   * viewfinder moves the frame onto the part you point at or focus; kagi sets two corners round a phrase, as 「 」 quote it;\n   * glide keeps no frame at rest and carries one between the controls of a group, at any depth, as you point or move focus.\n   */\n  variant?: \"viewfinder\" | \"kagi\" | \"glide\"\n}\n\n/** The direct child of root that holds node. */\nfunction partOf(root: HTMLElement, node: EventTarget | null) {\n  let el = node instanceof HTMLElement ? node : null\n  while (el && el.parentElement !== root) el = el.parentElement\n  return el\n}\n\nfunction aim(root: HTMLElement, part: HTMLElement) {\n  const r = root.getBoundingClientRect()\n  const p = part.getBoundingClientRect()\n  root.style.setProperty(\"--ot-aim-x\", `${p.left - r.left - root.clientLeft}px`)\n  root.style.setProperty(\"--ot-aim-y\", `${p.top - r.top - root.clientTop}px`)\n  root.style.setProperty(\"--ot-aim-w\", `${p.width}px`)\n  root.style.setProperty(\"--ot-aim-h\", `${p.height}px`)\n  root.dataset.aim = \"\"\n}\n\nfunction rest(root: HTMLElement) {\n  for (const k of [\"x\", \"y\", \"w\", \"h\"]) root.style.removeProperty(`--ot-aim-${k}`)\n  delete root.dataset.aim\n}\n\nconst CONTROL = 'a[href], button, input, select, textarea, summary, [role=\"button\"], [tabindex]:not([tabindex=\"-1\"])'\n\n/** glide: the control under node, at any depth inside root, if it can be used. */\nfunction controlOf(root: HTMLElement, node: EventTarget | null) {\n  const el = node instanceof Element ? node.closest<HTMLElement>(CONTROL) : null\n  return el && root.contains(el) && el !== root && !el.matches(\":disabled, [aria-disabled='true']\") ? el : null\n}\n\n/** glide: frame el. Arriving from rest, the marks appear where they land instead of travelling in from the corner. */\nfunction glideTo(root: HTMLElement, el: HTMLElement, by: \"pointer\" | \"focus\") {\n  const landing = !(\"aim\" in root.dataset)\n  if (landing) root.dataset.landing = \"\"\n  aim(root, el)\n  root.dataset.aim = by\n  if (landing) requestAnimationFrame(() => requestAnimationFrame(() => delete root.dataset.landing))\n}\n\n/**\n * Four corner marks: a frame that doesn't close. Tune it with the custom\n * properties --ot-corner (length), --ot-corner-inset and --ot-corner-colour.\n */\nfunction Corners({ className, asChild = false, variant, onPointerOver, onPointerLeave, onFocus, onBlur, ...props }: CornersProps) {\n  const Comp = asChild ? Slot : \"div\"\n  const finder =\n    variant === \"viewfinder\"\n      ? {\n          onPointerOver: (e: React.PointerEvent<HTMLDivElement>) => {\n            onPointerOver?.(e)\n            const part = partOf(e.currentTarget, e.target)\n            if (part) aim(e.currentTarget, part)\n          },\n          onPointerLeave: (e: React.PointerEvent<HTMLDivElement>) => {\n            onPointerLeave?.(e)\n            // A tap leaves at once; keep the frame where the finger put it. Focus inside keeps it too.\n            if (e.pointerType !== \"touch\" && !e.currentTarget.contains(document.activeElement)) rest(e.currentTarget)\n          },\n          onFocus: (e: React.FocusEvent<HTMLDivElement>) => {\n            onFocus?.(e)\n            const part = partOf(e.currentTarget, e.target)\n            if (part) aim(e.currentTarget, part)\n          },\n          onBlur: (e: React.FocusEvent<HTMLDivElement>) => {\n            onBlur?.(e)\n            if (!e.currentTarget.contains(e.relatedTarget as Node | null)) rest(e.currentTarget)\n          },\n        }\n      : variant === \"glide\"\n        ? {\n            // ponytail: it moves only when the pointer or focus does; a reflow under a still frame waits for the next move.\n            onPointerOver: (e: React.PointerEvent<HTMLDivElement>) => {\n              onPointerOver?.(e)\n              const el = controlOf(e.currentTarget, e.target)\n              if (el && e.pointerType !== \"touch\") glideTo(e.currentTarget, el, \"pointer\")\n            },\n            onPointerLeave: (e: React.PointerEvent<HTMLDivElement>) => {\n              onPointerLeave?.(e)\n              // The hand has gone: the frame goes back to where you are, or away.\n              const here = controlOf(e.currentTarget, document.activeElement)\n              if (here) glideTo(e.currentTarget, here, \"focus\")\n              else delete e.currentTarget.dataset.aim // it fades where it is; the last place stays for the fade\n            },\n            onFocus: (e: React.FocusEvent<HTMLDivElement>) => {\n              onFocus?.(e)\n              const el = controlOf(e.currentTarget, e.target)\n              if (el) glideTo(e.currentTarget, el, \"focus\")\n            },\n            onBlur: (e: React.FocusEvent<HTMLDivElement>) => {\n              onBlur?.(e)\n              if (!e.currentTarget.contains(e.relatedTarget as Node | null) && e.currentTarget.dataset.aim === \"focus\") delete e.currentTarget.dataset.aim\n            },\n          }\n        : { onPointerOver, onPointerLeave, onFocus, onBlur }\n  return <Comp data-slot=\"corners\" data-variant={variant} className={cn(\"ot-corners\", className)} {...finder} {...props} />\n}\n\nexport { Corners, type CornersProps }\n",
      "type": "registry:ui"
    }
  ],
  "meta": {
    "movement": "IV",
    "underneath": "native",
    "contract": "ot-corners"
  },
  "type": "registry:ui"
}
