{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "versions",
  "title": "Versions",
  "description": "Versions from the Get IT Done design system.",
  "dependencies": [
    "lucide-react@^0.379.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/badge.json",
    "https://zero-design.itdone.de/r/button.json",
    "https://zero-design.itdone.de/r/data-state-panel.json",
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/skeleton.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/versions.tsx",
      "content": "'use client';\n\nimport { Download, History, RotateCcw } from 'lucide-react';\nimport * as React from 'react';\n\nimport { cn } from '@/lib/utils';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport { DataStatePanel } from '@/components/ui/data-state-panel';\nimport { intlLocaleOf, useLabels, useLocale, usePreset } from '@/components/ui/labels-context';\nimport { Skeleton } from '@/components/ui/skeleton';\n\nexport interface VersionItem {\n  id: string;\n  // Version number or tag, e.g. 3 or \"3.1\".\n  version: number | string;\n  author?: string;\n  date?: Date | string | number;\n  // Change note.\n  note?: string;\n  // Marks the live version. Only one should be current.\n  current?: boolean;\n  // File size text of this version, e.g. \"2.4 MB\". Shown in the `card` variant.\n  size?: string;\n}\n\n// Texts of the component. Precedence: this prop, then the provider labels, then English.\nexport interface VersionsLabels {\n  // Accessible name of the list.\n  title: string;\n  current: string;\n  restore: string;\n  restoring: string;\n  loading: string;\n  emptyTitle: string;\n  emptyDescription: string;\n  errorTitle: string;\n  errorDescription: string;\n  retry: string;\n  noNote: string;\n  version: (version: string | number) => string;\n  by: (name: string) => string;\n  restoreAria: (version: string | number) => string;\n  downloadAria: (version: string | number) => string;\n}\n\nexport interface VersionsProps extends Omit<React.HTMLAttributes<HTMLUListElement>, 'children'> {\n  items: VersionItem[];\n  // Shows a restore button on every non-current version.\n  onRestore?: (item: VersionItem) => void;\n  // Id of a version being restored right now. Its button shows a busy state.\n  restoringId?: string;\n  state?: 'default' | 'loading' | 'empty' | 'error';\n  onRetry?: () => void;\n  labels?: Partial<VersionsLabels>;\n  // Formats the date of a version. Defaults to Intl medium date in the provider locale.\n  formatDate?: (date: Date) => string;\n  // `list` is the flat history with the note on a second line. `card` is one bordered row per version on a single line (version, current, author, date, size, note), the current one with a brand border. Defaults to `card` in the zero preset, otherwise `list`.\n  variant?: 'list' | 'card';\n  // Shows a download icon button on every version.\n  onDownload?: (item: VersionItem) => void;\n}\n\n// Version history, newest first as given. The current version is marked, older ones can be restored.\nconst Versions = React.forwardRef<HTMLUListElement, VersionsProps>(\n  ({ className, items, onRestore, restoringId, state = 'default', onRetry, labels, formatDate, variant: variantProp, onDownload, 'aria-label': ariaLabel, ...props }, ref) => {\n    const l = useLabels();\n    const zero = usePreset() === 'zero';\n    const variant = variantProp ?? (zero ? 'card' : 'list');\n    const locale = intlLocaleOf(useLocale());\n    const t = {\n      title: labels?.title ?? l.versionsTitle,\n      current: labels?.current ?? l.currentVersion,\n      restore: labels?.restore ?? l.chatRestore,\n      restoring: labels?.restoring ?? l.versionRestoring,\n      loading: labels?.loading ?? l.loading,\n      emptyTitle: labels?.emptyTitle ?? l.noVersionsYet,\n      emptyDescription: labels?.emptyDescription,\n      errorTitle: labels?.errorTitle ?? l.couldNotLoad,\n      errorDescription: labels?.errorDescription,\n      retry: labels?.retry ?? l.tryAgain,\n      noNote: labels?.noNote ?? l.noVersionNote,\n      version: labels?.version ?? l.versionLabel,\n      by: labels?.by ?? l.versionBy,\n      restoreAria: labels?.restoreAria ?? l.restoreVersion,\n      downloadAria: labels?.downloadAria ?? l.downloadVersion,\n    };\n    const dateText = (date: Date) => (formatDate ? formatDate(date) : new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(date));\n    if (state === 'error') {\n      return <DataStatePanel kind=\"error\" title={t.errorTitle} description={t.errorDescription} onRetry={onRetry} retryText={t.retry} className={className} />;\n    }\n    if (state === 'empty' || (state === 'default' && items.length === 0)) {\n      return <DataStatePanel kind=\"empty\" title={t.emptyTitle} description={t.emptyDescription} icon={<History aria-hidden=\"true\" />} className={className} />;\n    }\n    if (state === 'loading') return <Skeleton variant=\"row-single\" rows={3} label={t.loading} className={className} />;\n    const card = variant === 'card';\n    return (\n      <ul ref={ref} aria-label={ariaLabel ?? t.title} className={cn('m-0 flex list-none flex-col p-0 text-text-default', card && 'gap-label', className)} {...props}>\n        {items.map((item) => {\n          const busy = restoringId === item.id;\n          const parsed = item.date === undefined ? undefined : new Date(item.date);\n          const date = parsed && !Number.isNaN(parsed.getTime()) ? parsed : undefined;\n          if (card) {\n            const parts = [\n              item.author ? t.by(item.author) : null,\n              date ? <time key=\"date\" dateTime={date.toISOString()}>{dateText(date)}</time> : null,\n              item.size ?? null,\n              item.note ?? null,\n            ].filter((part) => part !== null);\n            return (\n              <li\n                key={item.id}\n                aria-current={item.current ? 'true' : undefined}\n                data-current={item.current || undefined}\n                className={cn(\n                  'flex items-center gap-2 rounded-control border border-solid px-3 py-2',\n                  item.current ? 'border-border-brand bg-background-selected-default' : 'border-border-default bg-card',\n                )}\n              >\n                <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-x-2\">\n                  <b className=\"typography-ui-semibold\">{t.version(item.version)}</b>\n                  {item.current ? <Badge variant=\"info\">{t.current}</Badge> : null}\n                  <span className={cn('typography-ui min-w-0 truncate', item.current ? 'text-general-brand-on-container' : 'text-text-subtle')}>\n                    {parts.map((part, index) => (\n                      <React.Fragment key={index}>\n                        {index > 0 ? ' \\u00b7 ' : ''}\n                        {part}\n                      </React.Fragment>\n                    ))}\n                  </span>\n                </div>\n                {onRestore && !item.current ? (\n                  <Button\n                    type=\"button\"\n                    variant=\"outline\"\n                    size=\"sm\"\n                    icon={<RotateCcw aria-hidden=\"true\" />}\n                    onClick={() => onRestore(item)}\n                    disabled={busy}\n                    aria-busy={busy || undefined}\n                    aria-label={t.restoreAria(item.version)}\n                  >\n                    {busy ? t.restoring : t.restore}\n                  </Button>\n                ) : null}\n                {onDownload ? (\n                  <Button type=\"button\" variant=\"ghost\" size=\"icon-sm\" aria-label={t.downloadAria(item.version)} onClick={() => onDownload(item)}>\n                    <Download aria-hidden=\"true\" className=\"h-icon-sm w-icon-sm\" />\n                  </Button>\n                ) : null}\n              </li>\n            );\n          }\n          return (\n            <li\n              key={item.id}\n              aria-current={item.current ? 'true' : undefined}\n              data-current={item.current || undefined}\n              className={cn(\n                'grid grid-cols-[minmax(0,1fr)_auto] items-start gap-x-3 gap-y-1 border-0 border-b border-solid border-hairline px-3 py-3 last:border-b-0',\n                item.current && 'rounded-item bg-background-neutral-default [&_.text-text-subtle]:text-text-subtle-on-muted',\n              )}\n            >\n              <div className=\"min-w-0\">\n                <div className=\"flex flex-wrap items-center gap-2\">\n                  <b className=\"typography-ui-semibold\">{t.version(item.version)}</b>\n                  {item.current ? <Badge variant=\"info\">{t.current}</Badge> : null}\n                  {item.author ? <span className=\"typography-ui text-text-subtle\">{t.by(item.author)}</span> : null}\n                </div>\n                <p className={cn('typography-ui m-0 mt-1', item.note ? 'text-text-default' : 'text-text-subtle')}>{item.note ?? t.noNote}</p>\n              </div>\n              <div className=\"flex flex-col items-end gap-2\">\n                {date ? (\n                  <time dateTime={date.toISOString()} className=\"typography-text-extra-small text-text-subtle\">\n                    {dateText(date)}\n                  </time>\n                ) : null}\n                {onRestore && !item.current ? (\n                  <Button\n                    type=\"button\"\n                    variant=\"outline\"\n                    size=\"sm\"\n                    icon={<RotateCcw aria-hidden=\"true\" />}\n                    onClick={() => onRestore(item)}\n                    disabled={busy}\n                    aria-busy={busy || undefined}\n                    aria-label={t.restoreAria(item.version)}\n                  >\n                    {busy ? t.restoring : t.restore}\n                  </Button>\n                ) : null}\n              </div>\n            </li>\n          );\n        })}\n      </ul>\n    );\n  },\n);\nVersions.displayName = 'Versions';\n\nexport { Versions };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}