{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "resource-manager",
  "title": "Resource Manager",
  "description": "List, create, inspect, edit, archive, and delete domain records.",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "button",
    "https://uaiblocks.dev/r/uai-theme.json",
    "https://uaiblocks.dev/r/uai-utils.json",
    "https://uaiblocks.dev/r/confirmation-dialog.json",
    "https://uaiblocks.dev/r/data-table-toolbar.json",
    "https://uaiblocks.dev/r/description-list.json",
    "https://uaiblocks.dev/r/empty-state.json"
  ],
  "files": [
    {
      "path": "src/registry/uai/blocks/resource-manager.tsx",
      "content": "\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport {\n  type ComponentProps,\n  createContext,\n  type KeyboardEvent,\n  useContext,\n  useId,\n  useState,\n} from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  ConfirmationDialog,\n  type ConfirmationDialogProps,\n  type ConfirmationDialogVariant,\n} from \"@/components/ui/uai/confirmation-dialog\";\nimport {\n  DataTableToolbar,\n  type DataTableToolbarProps,\n  type DataTableToolbarVariant,\n} from \"@/components/ui/uai/data-table-toolbar\";\nimport {\n  DescriptionList,\n  type DescriptionListProps,\n  type DescriptionListVariant,\n} from \"@/components/ui/uai/description-list\";\nimport {\n  EmptyState,\n  type EmptyStateProps,\n  type EmptyStateVariant,\n} from \"@/components/ui/uai/empty-state\";\nimport { cn } from \"@/lib/uai-utils\";\n\nexport const RESOURCE_MANAGER_VARIANTS = [\"split\", \"stacked\", \"compact\"] as const;\nexport type ResourceManagerVariant = (typeof RESOURCE_MANAGER_VARIANTS)[number];\nexport type ResourceManagerRecordTone = \"neutral\" | \"success\" | \"warning\" | \"danger\";\nexport type ResourceManagerProps = Omit<ComponentProps<\"section\">, \"defaultValue\"> & {\n  variant?: ResourceManagerVariant;\n  /** The inspected record. An empty string means nothing is selected. */\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n};\n\ntype ManagerContext = {\n  id: string;\n  variant: ResourceManagerVariant;\n  value: string;\n  select: (value: string) => void;\n};\nconst Context = createContext<ManagerContext | null>(null);\nfunction useManager(part: string) {\n  const context = useContext(Context);\n  if (!context) throw new Error(`${part} must be used within ResourceManager`);\n  return context;\n}\n\nconst toolbarVariants: Record<ResourceManagerVariant, DataTableToolbarVariant> = {\n  split: \"toolbar\",\n  stacked: \"stacked\",\n  compact: \"compact\",\n};\nconst detailsVariants: Record<ResourceManagerVariant, DescriptionListVariant> = {\n  split: \"inline\",\n  stacked: \"grid\",\n  compact: \"stacked\",\n};\nconst emptyVariants: Record<ResourceManagerVariant, EmptyStateVariant> = {\n  split: \"card\",\n  stacked: \"card\",\n  compact: \"compact\",\n};\nconst dialogVariants: Record<ResourceManagerVariant, ConfirmationDialogVariant> = {\n  split: \"centered\",\n  stacked: \"sheet\",\n  compact: \"compact\",\n};\nconst recordStatusVariants = cva(\n  \"inline-flex h-5 items-center rounded-full px-2 text-[11.5px]/4 font-medium whitespace-nowrap\",\n  {\n    variants: {\n      tone: {\n        neutral: \"bg-muted text-muted-foreground\",\n        success: \"bg-success/14 text-success-ink\",\n        warning: \"bg-warning/14 text-warning-ink\",\n        danger: \"bg-destructive/14 text-destructive-ink\",\n      },\n    },\n  },\n);\n\nconst resourceManagerVariants = cva(\n  \"grid min-w-0 content-start text-[13px]/[18px] text-foreground\",\n  { variants: { variant: { split: \"gap-4\", stacked: \"gap-4\", compact: \"gap-2.5\" } } },\n);\n\n/** Record management surface: a list of domain records beside an inspector. */\nexport function ResourceManager({\n  variant = \"split\",\n  value,\n  defaultValue = \"\",\n  onValueChange,\n  className,\n  children,\n  ...props\n}: ResourceManagerProps) {\n  const id = useId();\n  const [internal, setInternal] = useState(defaultValue);\n  const current = value ?? internal;\n  return (\n    <Context.Provider\n      value={{\n        id,\n        variant,\n        value: current,\n        select: (next) => {\n          if (next === current) return;\n          if (value === undefined) setInternal(next);\n          onValueChange?.(next);\n        },\n      }}\n    >\n      <section\n        aria-labelledby={`${id}-title`}\n        data-slot=\"resource-manager\"\n        className={cn(resourceManagerVariants({ variant }), className)}\n        {...props}\n        data-variant={variant}\n      >\n        {children}\n      </section>\n    </Context.Provider>\n  );\n}\n\nexport function ResourceManagerHeader({ className, ...props }: ComponentProps<\"div\">) {\n  useManager(\"ResourceManagerHeader\");\n  return (\n    <div\n      data-slot=\"resource-manager-header\"\n      className={cn(\"flex min-w-0 flex-wrap items-end justify-between gap-3\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function ResourceManagerHeading({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"resource-manager-heading\"\n      className={cn(\"grid min-w-0 flex-[1_1_240px] gap-1\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function ResourceManagerTitle({ className, ...props }: ComponentProps<\"h2\">) {\n  const context = useManager(\"ResourceManagerTitle\");\n  return (\n    <h2\n      data-slot=\"resource-manager-title\"\n      className={cn(\n        \"m-0 font-semibold tracking-[-0.015em] wrap-anywhere\",\n        context.variant === \"compact\" ? \"text-[15px]/5\" : \"text-[18px]/6\",\n        className,\n      )}\n      {...props}\n      id={`${context.id}-title`}\n    />\n  );\n}\n\nexport function ResourceManagerDescription({ className, ...props }: ComponentProps<\"p\">) {\n  return (\n    <p\n      data-slot=\"resource-manager-description\"\n      className={cn(\"m-0 text-muted-foreground tabular-nums\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function ResourceManagerActions({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"resource-manager-actions\"\n      className={cn(\"flex flex-wrap items-center gap-1.5\", className)}\n      {...props}\n    />\n  );\n}\n\nconst resourceManagerActionVariants = cva(\n  [\n    \"cursor-pointer gap-1.5 rounded-full border-0 py-0\",\n    \"transition-[filter,box-shadow,scale] duration-[120ms,120ms,140ms] ease-[ease-out,ease-out,cubic-bezier(0.23,1,0.32,1)]\",\n    \"enabled:active:scale-97 focus-visible:ring-0 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\",\n    \"disabled:cursor-not-allowed disabled:opacity-50\",\n    \"motion-reduce:transition-none motion-reduce:enabled:active:scale-100\",\n  ],\n  {\n    variants: {\n      emphasis: {\n        primary: \"bg-primary text-primary-foreground hover:bg-primary enabled:hover:brightness-108\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary enabled:hover:shadow-[inset_0_0_0_999px_color-mix(in_oklab,var(--foreground)_9%,transparent)]\",\n      },\n      compact: {\n        true: \"h-[26px] px-[11px] text-[12px]/4 has-[>svg]:px-[11px]\",\n        false: \"h-[30px] px-[13px] text-[12.5px]/4 has-[>svg]:px-[13px]\",\n      },\n    },\n  },\n);\n\nexport function ResourceManagerAction({\n  emphasis = \"secondary\",\n  type = \"button\",\n  className,\n  ...props\n}: ComponentProps<\"button\"> & { emphasis?: \"primary\" | \"secondary\" }) {\n  const context = useManager(\"ResourceManagerAction\");\n  return (\n    <Button\n      data-slot=\"resource-manager-action\"\n      variant={emphasis === \"primary\" ? \"default\" : \"secondary\"}\n      className={cn(\n        resourceManagerActionVariants({ emphasis, compact: context.variant === \"compact\" }),\n        className,\n      )}\n      {...props}\n      type={type}\n      data-emphasis={emphasis}\n    />\n  );\n}\n\n/** Search, column, and bulk controls. Compose Data Table Toolbar parts inside it. */\nexport function ResourceManagerToolbar(props: Omit<DataTableToolbarProps, \"variant\">) {\n  const context = useManager(\"ResourceManagerToolbar\");\n  return (\n    <DataTableToolbar\n      data-slot=\"resource-manager-toolbar\"\n      {...props}\n      variant={toolbarVariants[context.variant]}\n    />\n  );\n}\n\n/** Places the record list and inspector side by side, stacking when space runs out. */\nexport function ResourceManagerBody({ className, ...props }: ComponentProps<\"div\">) {\n  const context = useManager(\"ResourceManagerBody\");\n  return (\n    <div\n      data-slot=\"resource-manager-body\"\n      className={cn(\n        \"min-w-0 flex-wrap items-start\",\n        context.variant === \"split\" ? \"flex\" : \"grid\",\n        context.variant === \"compact\" ? \"gap-2\" : \"gap-3\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/** The record list. Arrow keys, Home, and End move between records. */\nexport function ResourceManagerList({\n  \"aria-label\": label = \"Records\",\n  className,\n  onKeyDown,\n  ...props\n}: ComponentProps<\"ul\">) {\n  const context = useManager(\"ResourceManagerList\");\n  const compact = context.variant === \"compact\";\n  const move = (event: KeyboardEvent<HTMLUListElement>) => {\n    const records = Array.from(\n      event.currentTarget.querySelectorAll<HTMLButtonElement>(\"[data-record]:not(:disabled)\"),\n    );\n    const index = records.indexOf(document.activeElement as HTMLButtonElement);\n    if (index === -1) return;\n    const target = {\n      ArrowDown: records[Math.min(index + 1, records.length - 1)],\n      ArrowUp: records[Math.max(index - 1, 0)],\n      Home: records[0],\n      End: records[records.length - 1],\n    }[event.key];\n    if (!target) return;\n    event.preventDefault();\n    target.focus();\n  };\n  return (\n    <ul\n      aria-label={label}\n      data-slot=\"resource-manager-list\"\n      className={cn(\n        \"m-0 grid min-w-0 flex-[999_1_320px] list-none border bg-card shadow-[0_1px_2px_oklch(0_0_0/0.04)]\",\n        compact ? \"gap-0.5 rounded-xl p-1\" : \"gap-1 rounded-[14px] p-1.5\",\n        className,\n      )}\n      {...props}\n      onKeyDown={(event) => {\n        onKeyDown?.(event);\n        if (!event.defaultPrevented) move(event);\n      }}\n    />\n  );\n}\n\nexport function ResourceManagerRecord({\n  value,\n  children,\n  onClick,\n  className,\n  ...props\n}: Omit<ComponentProps<\"button\">, \"value\"> & { value: string }) {\n  const context = useManager(\"ResourceManagerRecord\");\n  const selected = context.value === value;\n  const compact = context.variant === \"compact\";\n  return (\n    <li\n      className={cn(\n        \"min-w-0 animate-in fade-in-0 slide-in-from-bottom-1 duration-240 ease-out-quint fill-mode-both motion-reduce:animate-none\",\n        \"nth-2:[animation-delay:40ms] nth-3:[animation-delay:80ms] nth-4:[animation-delay:120ms] nth-5:[animation-delay:160ms] nth-[n+6]:[animation-delay:200ms]\",\n      )}\n    >\n      <button\n        data-slot=\"resource-manager-record\"\n        className={cn(\n          \"flex w-full cursor-pointer flex-wrap items-center gap-x-3 gap-y-0.5 border-0 text-left text-inherit\",\n          \"[transition:background-color_120ms_ease-out,box-shadow_120ms_ease-out] motion-reduce:transition-none\",\n          \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\",\n          compact ? \"min-h-8 rounded-lg px-2 py-1\" : \"min-h-11 rounded-[10px] px-3 py-2\",\n          selected\n            ? \"bg-accent\"\n            : \"bg-transparent hover:shadow-[inset_0_0_0_999px_color-mix(in_oklab,var(--foreground)_4%,transparent)]\",\n          className,\n        )}\n        {...props}\n        type=\"button\"\n        data-record=\"\"\n        aria-current={selected ? \"true\" : undefined}\n        onClick={(event) => {\n          onClick?.(event);\n          if (!event.defaultPrevented) context.select(value);\n        }}\n      >\n        {children}\n      </button>\n    </li>\n  );\n}\n\nexport function ResourceManagerRecordTitle({ className, ...props }: ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"resource-manager-record-title\"\n      className={cn(\"min-w-0 flex-[1_1_160px] font-medium wrap-anywhere\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function ResourceManagerRecordMeta({ className, ...props }: ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"resource-manager-record-meta\"\n      className={cn(\"text-[12px] text-subtle-foreground tabular-nums\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function ResourceManagerRecordStatus({\n  tone = \"neutral\",\n  className,\n  ...props\n}: ComponentProps<\"span\"> & { tone?: ResourceManagerRecordTone }) {\n  return (\n    <span\n      data-slot=\"resource-manager-record-status\"\n      className={cn(recordStatusVariants({ tone }), className)}\n      {...props}\n      data-tone={tone}\n    />\n  );\n}\n\nconst inspectorVariants = cva(\n  [\n    \"grid min-w-0 flex-[1_1_280px] content-start shadow-[0_1px_2px_oklch(0_0_0/0.04)]\",\n    \"animate-in fade-in-0 slide-in-from-bottom-1 duration-240 ease-out-quint fill-mode-both motion-reduce:animate-none\",\n  ],\n  {\n    variants: {\n      variant: {\n        split: \"gap-3 rounded-[14px] border bg-card p-4\",\n        stacked:\n          \"gap-3 rounded-[14px] border-0 bg-[color-mix(in_oklab,var(--muted)_70%,var(--card))] p-4\",\n        compact: \"gap-2 rounded-xl border bg-card p-3\",\n      },\n    },\n  },\n);\n\n/** Details for the selected record. Labelled by its title. */\nexport function ResourceManagerInspector({ className, ...props }: ComponentProps<\"section\">) {\n  const context = useManager(\"ResourceManagerInspector\");\n  return (\n    <section\n      // Remount per record so the enter fade replays when the selection changes.\n      key={context.value}\n      aria-labelledby={`${context.id}-inspector-title`}\n      data-slot=\"resource-manager-inspector\"\n      className={cn(inspectorVariants({ variant: context.variant }), className)}\n      {...props}\n    />\n  );\n}\n\nexport function ResourceManagerInspectorHeader({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"resource-manager-inspector-header\"\n      className={cn(\"flex min-w-0 flex-wrap items-center justify-between gap-2\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function ResourceManagerInspectorTitle({ className, ...props }: ComponentProps<\"h3\">) {\n  const context = useManager(\"ResourceManagerInspectorTitle\");\n  return (\n    <h3\n      data-slot=\"resource-manager-inspector-title\"\n      className={cn(\"m-0 text-[15px]/5 font-medium tracking-[-0.01em] wrap-anywhere\", className)}\n      {...props}\n      id={`${context.id}-inspector-title`}\n    />\n  );\n}\n\n/** Record facts. Compose Description List parts inside it. */\nexport function ResourceManagerDetails(props: Omit<DescriptionListProps, \"variant\">) {\n  const context = useManager(\"ResourceManagerDetails\");\n  return (\n    <DescriptionList\n      data-slot=\"resource-manager-details\"\n      {...props}\n      variant={detailsVariants[context.variant]}\n    />\n  );\n}\n\n/** Edit form for the selected record. Saving stays with the consumer. */\nexport function ResourceManagerForm({ className, ...props }: ComponentProps<\"form\">) {\n  const context = useManager(\"ResourceManagerForm\");\n  return (\n    <form\n      data-slot=\"resource-manager-form\"\n      className={cn(\n        \"m-0 grid min-w-0\",\n        context.variant === \"compact\" ? \"gap-2\" : \"gap-3\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/** Shown when no record matches. Compose Empty State parts inside it. */\nexport function ResourceManagerEmpty(props: Omit<EmptyStateProps, \"variant\">) {\n  const context = useManager(\"ResourceManagerEmpty\");\n  return (\n    <EmptyState\n      data-slot=\"resource-manager-empty\"\n      {...props}\n      variant={emptyVariants[context.variant]}\n    />\n  );\n}\n\n/** Destructive confirmation. Compose Confirmation Dialog parts inside it. */\nexport function ResourceManagerDelete(props: Omit<ConfirmationDialogProps, \"variant\">) {\n  const context = useManager(\"ResourceManagerDelete\");\n  return (\n    <ConfirmationDialog\n      data-slot=\"resource-manager-delete\"\n      {...props}\n      variant={dialogVariants[context.variant]}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/uai/resource-manager.tsx"
    }
  ],
  "categories": [
    "operations",
    "data",
    "crud"
  ],
  "type": "registry:block"
}