{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pagination-dot-group",
  "title": "Pagination Dot Group",
  "description": "Pagination Dot Group from the Get IT Done design system.",
  "dependencies": [
    "class-variance-authority@^0.7.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/labels.json",
    "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/pagination-dot-group.tsx",
      "content": "'use client';\n\nimport { cva } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { focusRing } from '@/lib/recipes';\nimport { cn } from '@/lib/utils';\nimport { useLabels } from '@/components/ui/labels-context';\nimport type { LabelProp } from '@/components/ui/labels';\n\n// Figma Pagination dot group (1111:38): Size md|lg, Style Dot|Line, Framed True|False, Color Primary|Gray x light|dark bg.\n// md: dot 8, gap 12, frame padding 8. lg: dot 10, gap 16, frame padding 12. Line: 40px wide, 6px (md) or 8px (lg) high.\n// 6px and 10px have no utility in the preset, so they are derived from the space-micro token (2px x 3 and x 5).\nexport type PaginationDotTone = 'primary-light-bg' | 'primary-dark-bg' | 'gray-light-bg' | 'gray-dark-bg';\n\nconst toneFrame: Record<PaginationDotTone, string> = {\n  'primary-light-bg': 'bg-primary-700',\n  'primary-dark-bg': 'bg-background-brand-subtle',\n  'gray-light-bg': 'bg-background backdrop-blur-sm',\n  'gray-dark-bg': 'bg-static-black/90 backdrop-blur-sm',\n};\n\n// Current and idle indicator colours per tone. The two \"dark frame\" tones use static white for the current dot: Figma binds\n// semantic/background/default there, which turns dark in dark mode and would vanish on the dark frame.\nconst toneCurrent: Record<PaginationDotTone, string> = {\n  'primary-light-bg': 'bg-static-white',\n  'primary-dark-bg': 'bg-background-brand-bold-default',\n  'gray-light-bg': 'bg-background-inverse',\n  'gray-dark-bg': 'bg-static-white',\n};\n\nconst toneIdle: Record<PaginationDotTone, string> = {\n  'primary-light-bg': 'bg-general-brand-default',\n  'primary-dark-bg': 'bg-background-neutral-default',\n  'gray-light-bg': 'bg-muted-strong',\n  'gray-dark-bg': 'bg-icon-subtle',\n};\n\nconst groupVariants = cva('inline-flex items-center justify-center', {\n  variants: {\n    size: { md: 'gap-3', lg: 'gap-4' },\n    framed: { true: 'rounded-full', false: '' },\n  },\n  compoundVariants: [\n    { size: 'md', framed: true, className: 'p-2' },\n    { size: 'lg', framed: true, className: 'p-3' },\n  ],\n  defaultVariants: { size: 'md', framed: false },\n});\n\nconst indicatorVariants = cva('block shrink-0 rounded-full transition-colors motion-reduce:transition-none', {\n  variants: {\n    size: { md: '', lg: '' },\n    style: { dot: '', line: '' },\n  },\n  compoundVariants: [\n    { size: 'md', style: 'dot', className: 'h-2 w-2' },\n    { size: 'lg', style: 'dot', className: 'h-[calc(var(--space-micro)*5)] w-[calc(var(--space-micro)*5)]' },\n    { size: 'md', style: 'line', className: 'h-[calc(var(--space-micro)*3)] w-10' },\n    { size: 'lg', style: 'line', className: 'h-2 w-10' },\n  ],\n  defaultVariants: { size: 'md', style: 'dot' },\n});\n\nexport interface PaginationDotGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect'> {\n  // Number of pages or slides.\n  count: number;\n  // Zero based index of the current page.\n  current: number;\n  size?: 'md' | 'lg';\n  // Figma Style: round dots or lines of equal width. Only the colour marks the current one.\n  indicator?: 'dot' | 'line';\n  // Figma Framed: pill background around the indicators.\n  framed?: boolean;\n  // Figma Color. Default `gray-light-bg`.\n  tone?: PaginationDotTone;\n  // Makes the indicators buttons. Without it the group is a single read-only image.\n  onSelect?: (index: number) => void;\n  // Builds the accessible name of an indicator button.\n  getItemLabel?: (index: number, count: number) => string;\n  // Accessible name of the read-only group, from the 1 based page and the count. Wins over the provider text.\n  pageOfLabel?: LabelProp<[number, number]>;\n}\n\nconst PaginationDotGroup = React.forwardRef<HTMLDivElement, PaginationDotGroupProps>(\n  (\n    {\n      className,\n      count,\n      current,\n      size = 'md',\n      indicator = 'dot',\n      framed = false,\n      tone = 'gray-light-bg',\n      onSelect,\n      getItemLabel: getItemLabelProp,\n      pageOfLabel,\n      ...props\n    },\n    ref,\n  ) => {\n    const l = useLabels({ pageOf: pageOfLabel });\n    const getItemLabel = getItemLabelProp ?? ((i: number, n: number) => l.goToPage(i + 1, n));\n    const items = Array.from({ length: Math.max(0, count) });\n    const readOnly = !onSelect;\n    return (\n      <div\n        ref={ref}\n        role={readOnly ? 'img' : 'group'}\n        aria-label={readOnly ? l.pageOf(current + 1, count) : undefined}\n        data-tone={tone}\n        className={cn(groupVariants({ size, framed }), readOnly ? undefined : 'gap-0', framed && toneFrame[tone], className)}\n        {...props}\n      >\n        {items.map((_, i) => {\n          const isCurrent = i === current;\n          const mark = <span className={cn(indicatorVariants({ size, style: indicator }), isCurrent ? toneCurrent[tone] : toneIdle[tone])} />;\n          if (readOnly) {\n            return (\n              <span key={i} aria-hidden=\"true\" className=\"inline-flex\">\n                {mark}\n              </span>\n            );\n          }\n          return (\n            <button\n              key={i}\n              type=\"button\"\n              aria-label={getItemLabel(i, count)}\n              aria-current={isCurrent ? 'true' : undefined}\n              onClick={() => onSelect?.(i)}\n              // The visible indicator keeps its Figma size inside a 24px target (WCAG 2.5.8). The group gap is dropped, the target boxes carry the spacing.\n              className={cn('inline-flex h-6 min-w-target items-center justify-center rounded-full', ...focusRing)}\n            >\n              {mark}\n            </button>\n          );\n        })}\n      </div>\n    );\n  },\n);\nPaginationDotGroup.displayName = 'PaginationDotGroup';\n\nexport { PaginationDotGroup };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}