{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "description-list",
  "title": "Description List",
  "description": "Description List from the Get IT Done design system.",
  "dependencies": [
    "class-variance-authority@^0.7.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/avatar.json",
    "https://zero-design.itdone.de/r/chip.json",
    "https://zero-design.itdone.de/r/empty-state.json",
    "https://zero-design.itdone.de/r/heading-tag.json",
    "https://zero-design.itdone.de/r/inline-edit.json",
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/logo-tile.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/description-list.tsx",
      "content": "'use client';\n\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '@/lib/utils';\nimport type { HeadingTag } from '@/components/ui/heading-tag';\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Chip } from '@/components/ui/chip';\nimport { EmptyState } from '@/components/ui/empty-state';\nimport { InlineEdit } from '@/components/ui/inline-edit';\nimport { useLabels } from '@/components/ui/labels-context';\nimport { LogoTile, type LogoTileColor } from '@/components/ui/logo-tile';\n\n// Figma: Attributes 10152:343821 (variant aside|sheet|compact, readonly) and SummaryList 10152:343061 (values|with-total). Both are <dl> lists with loading, empty and error states.\nexport type DescriptionListState = 'ready' | 'loading' | 'empty' | 'error';\n\ninterface StateProps {\n  state?: DescriptionListState;\n  // Replaces the default empty panel.\n  emptyState?: React.ReactNode;\n  // Replaces the default error panel.\n  errorState?: React.ReactNode;\n  // Number of placeholder rows while loading.\n  loadingRows?: number;\n}\n\nconst DescriptionListStatus = ({\n  state,\n  emptyState,\n  errorState,\n  loadingRows = 5,\n  label,\n}: StateProps & { label: string }): React.ReactElement | null => {\n  const l = useLabels();\n  if (state === 'loading') {\n    return (\n      <div role=\"status\" aria-busy=\"true\" aria-label={l.loadingItem(label)} className=\"flex flex-col gap-3\">\n        {Array.from({ length: loadingRows }, (_, i) => (\n          <div key={i} className=\"flex items-center gap-3\" aria-hidden=\"true\">\n            <span className=\"block h-3 w-16 rounded-item bg-overlay-skeleton-subtle motion-reduce:animate-none\" />\n            <span className=\"block h-3 w-32 rounded-item bg-overlay-skeleton-default motion-reduce:animate-none\" />\n          </div>\n        ))}\n      </div>\n    );\n  }\n  if (state === 'empty') {\n    return <>{emptyState ?? <EmptyState variant=\"first-use\" title={l.noItemsYet(label)} />}</>;\n  }\n  if (state === 'error') {\n    return (\n      <>\n        {errorState ?? (\n          <EmptyState\n            variant=\"error\"\n            title={l.itemNotLoaded(label)}\n            description={l.tryAgainInAMoment}\n            className=\"border-solid border-border-destructive [&_[data-slot=title]]:text-text-destructive\"\n          />\n        )}\n      </>\n    );\n  }\n  return null;\n};\n\nconst attributesVariants = cva('flex w-full flex-col', {\n  variants: {\n    variant: {\n      aside: 'gap-3',\n      sheet: 'gap-3',\n      compact: 'gap-2',\n    },\n  },\n  defaultVariants: { variant: 'aside' },\n});\n\n// Label column: a third of the row, so long labels wrap (break-words, hyphens-auto with lang) instead of running into the value. Variants differ in row gap only.\nconst labelWidth = { aside: 'w-1/3', sheet: 'w-1/3', compact: 'w-1/3' } as const;\n\ninterface AttributesContextValue {\n  variant: keyof typeof labelWidth;\n  divided: boolean;\n}\nconst AttributesContext = React.createContext<AttributesContextValue>({ variant: 'aside', divided: false });\n\nexport interface AttributesProps\n  extends Omit<React.HTMLAttributes<HTMLElement>, 'title'>,\n    VariantProps<typeof attributesVariants>,\n    StateProps {\n  // Section heading such as \"Details\".\n  title?: React.ReactNode;\n  // Heading element of the title, so the section fits the document outline. Default h3.\n  titleAs?: HeadingTag;\n  // Hairlines between rows (read-only description list).\n  divided?: boolean;\n  // Used in the loading and default empty/error copy, e.g. \"details\".\n  noun?: string;\n}\n\nconst Attributes = React.forwardRef<HTMLElement, AttributesProps>(\n  ({ className, variant = 'aside', title, titleAs: Title = 'h3', divided = false, state = 'ready', noun = 'details', emptyState, errorState, loadingRows, children, ...props }, ref) => {\n    const resolved = variant ?? 'aside';\n    const headingId = React.useId();\n    return (\n      <section\n        ref={ref}\n        aria-labelledby={title ? headingId : undefined}\n        className={cn('flex w-full flex-col gap-3', className)}\n        {...props}\n      >\n        {title ? (\n          <Title id={headingId} className=\"typography-ui-medium text-text-subtle\">\n            {title}\n          </Title>\n        ) : null}\n        {state === 'ready' ? (\n          <AttributesContext.Provider value={{ variant: resolved, divided }}>\n            <dl className={cn(attributesVariants({ variant: resolved }), divided && 'gap-0')}>{children}</dl>\n          </AttributesContext.Provider>\n        ) : (\n          <DescriptionListStatus state={state} label={noun} emptyState={emptyState} errorState={errorState} loadingRows={loadingRows} />\n        )}\n      </section>\n    );\n  },\n);\nAttributes.displayName = 'Attributes';\n\n// Figma _Attribute value Type=Text | Chip | Avatar | Logo | Input | Tags.\nexport type AttributeValueType = 'text' | 'chip' | 'avatar' | 'logo' | 'input' | 'tags';\n\nexport interface AttributeValueProps {\n  type?: AttributeValueType;\n  // Text, chip label, or the name next to an avatar or logo. Initial text of an input.\n  value?: string;\n  // Chips for `type=\"tags\"`.\n  tags?: string[];\n  // Image of an avatar.\n  src?: string;\n  // Initials of an avatar fallback or logo tile.\n  initials?: string;\n  // Project colour of a logo tile.\n  logoColor?: LogoTileColor;\n  // Accessible name of an input value. Defaults to the row label when it is a string.\n  inputLabel?: string;\n  onValueChange?: (value: string) => void;\n  onCommit?: (value: string) => void;\n}\n\nconst AttributeValue = ({ type = 'text', value, tags, src, initials, logoColor, inputLabel, onValueChange, onCommit }: AttributeValueProps): React.ReactElement | null => {\n  if (type === 'chip') {\n    return (\n      <Chip variant=\"outline\" size=\"small\">\n        {value}\n      </Chip>\n    );\n  }\n  if (type === 'avatar') {\n    return (\n      <span className=\"inline-flex items-center gap-2\">\n        <Avatar size=\"xs\">\n          {src ? <AvatarImage src={src} alt=\"\" /> : null}\n          <AvatarFallback>{initials}</AvatarFallback>\n        </Avatar>\n        <span className=\"truncate\">{value}</span>\n      </span>\n    );\n  }\n  if (type === 'logo') {\n    return (\n      <span className=\"inline-flex items-center gap-2\">\n        <LogoTile initials={initials ?? ''} color={logoColor} />\n        <span className=\"truncate\">{value}</span>\n      </span>\n    );\n  }\n  if (type === 'input') {\n    return <InlineEdit aria-label={inputLabel ?? ''} defaultValue={value} onValueChange={onValueChange} onCommit={onCommit} />;\n  }\n  if (type === 'tags') {\n    return (\n      <span className=\"flex flex-wrap items-center gap-1\">\n        {(tags ?? []).map((t) => (\n          <Chip key={t} variant=\"outline\" size=\"small\">\n            {t}\n          </Chip>\n        ))}\n      </span>\n    );\n  }\n  return <>{value}</>;\n};\nAttributeValue.displayName = 'AttributeValue';\n\nexport interface AttributeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>, AttributeValueProps {\n  label: React.ReactNode;\n  // Text, chip, avatar, input or tag list. Optional when `type` and `value`/`tags` describe a typed value.\n  children?: React.ReactNode;\n}\n\nconst Attribute = React.forwardRef<HTMLDivElement, AttributeProps>(\n  ({ className, label, children, type, value, tags, src, initials, logoColor, inputLabel, onValueChange, onCommit, ...props }, ref) => {\n    const { variant, divided } = React.useContext(AttributesContext);\n    const typed = children === undefined && (type !== undefined || value !== undefined || tags !== undefined);\n    return (\n      <div\n        ref={ref}\n        className={cn('flex items-center gap-3', divided && 'border-b border-solid border-border py-2', className)}\n        {...props}\n      >\n        <dt className={cn('typography-ui shrink-0 break-words hyphens-auto text-text-subtle', labelWidth[variant])}>{label}</dt>\n        <dd className=\"typography-ui m-0 min-w-0 flex-1 text-text-default\">\n          {typed ? (\n            <AttributeValue\n              type={type}\n              value={value}\n              tags={tags}\n              src={src}\n              initials={initials}\n              logoColor={logoColor}\n              inputLabel={inputLabel ?? (typeof label === 'string' ? label : undefined)}\n              onValueChange={onValueChange}\n              onCommit={onCommit}\n            />\n          ) : (\n            children\n          )}\n        </dd>\n      </div>\n    );\n  },\n);\nAttribute.displayName = 'Attribute';\n\nexport interface SummaryListProps extends Omit<React.HTMLAttributes<HTMLDListElement>, 'children'>, StateProps {\n  children?: React.ReactNode;\n  noun?: string;\n}\n\n// Key/value amounts, values right aligned. Add a SummaryListRow total for the with-total variant.\nconst SummaryList = React.forwardRef<HTMLDListElement, SummaryListProps>(\n  ({ className, state = 'ready', noun = 'amounts', emptyState, errorState, loadingRows = 4, children, ...props }, ref) =>\n    state === 'ready' ? (\n      <dl ref={ref} className={cn('flex w-full flex-col gap-2', className)} {...props}>\n        {children}\n      </dl>\n    ) : (\n      <DescriptionListStatus state={state} label={noun} emptyState={emptyState} errorState={errorState} loadingRows={loadingRows} />\n    ),\n);\nSummaryList.displayName = 'SummaryList';\n\nexport interface SummaryListRowProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  label: React.ReactNode;\n  value: React.ReactNode;\n  // Emphasised closing row with a hairline above.\n  total?: boolean;\n}\n\nconst SummaryListRow = React.forwardRef<HTMLDivElement, SummaryListRowProps>(({ className, label, value, total = false, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\n      'flex items-baseline justify-between gap-4',\n      total && 'mt-1 border-t border-solid border-border pt-3',\n      className,\n    )}\n    {...props}\n  >\n    <dt className={cn(total ? 'typography-ui-lg-semibold text-text-default' : 'typography-ui text-text-subtle')}>{label}</dt>\n    <dd className={cn('m-0 tabular-nums', total ? 'typography-ui-lg-semibold text-text-default' : 'typography-ui text-text-default')}>{value}</dd>\n  </div>\n));\nSummaryListRow.displayName = 'SummaryListRow';\n\nexport { Attribute, AttributeValue, Attributes, SummaryList, SummaryListRow, attributesVariants };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}