{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "inline-edit",
  "title": "Inline Edit",
  "description": "Inline Edit from the Get IT Done design system.",
  "dependencies": [
    "class-variance-authority@^0.7.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/recipes.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/inline-edit.tsx",
      "content": "'use client';\n\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { forwardRef, useId, useRef, useState, type InputHTMLAttributes, type KeyboardEvent } from 'react';\n\nimport { focusRingField, invalidRing } from '@/lib/recipes';\nimport { cn } from '@/lib/utils';\nimport { usePreset } from '@/components/ui/labels-context';\n\nconst inlineEditVariants = cva(\n  [\n    'typography-ui w-full min-w-0 border border-solid border-transparent bg-transparent text-foreground outline-none',\n    'transition-[color,background-color,border-color,box-shadow] duration-150 motion-reduce:transition-none',\n    'placeholder:text-muted-foreground',\n    'hover:border-hairline hover:bg-background',\n    'read-only:cursor-default read-only:hover:border-transparent read-only:hover:bg-transparent',\n    'focus-visible:bg-background',\n  ],\n  {\n    variants: {\n      // attribute is a field in a detail aside, cell fills a table cell row.\n      variant: {\n        attribute: 'h-control-sm rounded-input px-2',\n        cell: 'h-row rounded-none px-2',\n      },\n      // Product preset (DesignSystemProvider `preset`): `zero` pads with the control token and shows the input border on hover.\n      preset: { default: '', zero: 'px-control-px-sm hover:border-input' },\n    },\n    defaultVariants: { variant: 'attribute', preset: 'default' },\n  },\n);\n\nexport interface InlineEditProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'defaultValue' | 'onChange' | 'size'>, Omit<VariantProps<typeof inlineEditVariants>, 'preset'> {\n  // Accessible name of the field. Required because there is no visible label.\n  'aria-label': string;\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  // Called on Enter or blur with the changed draft. Throwing is not handled: use `invalid` and `errorMessage`.\n  onCommit?: (value: string) => void;\n  // Called when Escape reverts the draft.\n  onCancel?: () => void;\n  invalid?: boolean;\n  errorMessage?: string;\n}\n\n// Text that edits in place. Always a real input, so keyboard and assistive tech need no extra mode.\nconst InlineEdit = forwardRef<HTMLInputElement, InlineEditProps>(\n  ({ className, variant, value, defaultValue = '', onValueChange, onCommit, onCancel, invalid, errorMessage, readOnly, onBlur, onKeyDown, ...props }, ref) => {\n    const zero = usePreset() === 'zero';\n    const controlled = value !== undefined;\n    const [inner, setInner] = useState(defaultValue);\n    const draft = controlled ? value : inner;\n    const committed = useRef(draft);\n    const errorId = useId();\n    const hasError = Boolean(invalid || errorMessage);\n\n    const set = (next: string) => {\n      if (!controlled) setInner(next);\n      onValueChange?.(next);\n    };\n    const commit = () => {\n      if (draft !== committed.current) {\n        committed.current = draft;\n        onCommit?.(draft);\n      }\n    };\n\n    const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n      onKeyDown?.(event);\n      if (event.defaultPrevented || readOnly) return;\n      if (event.key === 'Enter') {\n        event.preventDefault();\n        commit();\n      } else if (event.key === 'Escape') {\n        event.preventDefault();\n        set(committed.current);\n        onCancel?.();\n      }\n    };\n\n    return (\n      <div className=\"flex w-full min-w-0 flex-col gap-1\">\n        <input\n          ref={ref}\n          type=\"text\"\n          value={draft}\n          readOnly={readOnly}\n          aria-invalid={hasError || undefined}\n          aria-describedby={errorMessage ? errorId : undefined}\n          data-variant={variant ?? 'attribute'}\n          onChange={(event) => set(event.target.value)}\n          onBlur={(event) => {\n            onBlur?.(event);\n            if (!readOnly) commit();\n          }}\n          onKeyDown={handleKeyDown}\n          className={cn(inlineEditVariants({ variant, preset: zero ? 'zero' : 'default' }), focusRingField, invalidRing, hasError && 'border-border-destructive', className)}\n          {...props}\n        />\n        {errorMessage ? (\n          <p id={errorId} className=\"typography-text-extra-small px-2 text-text-destructive\">\n            {errorMessage}\n          </p>\n        ) : null}\n      </div>\n    );\n  },\n);\nInlineEdit.displayName = 'InlineEdit';\n\nexport { InlineEdit, inlineEditVariants };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}