{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "dot-field", "title": "Dot Field", "description": "A dot grid for small counts. Count, density and deterministic stipple variants.", "dependencies": [ "class-variance-authority" ], "registryDependencies": [ "http://localhost:5173/r/liquid-theme.json" ], "files": [ { "path": "src/components/ui/dot-field.tsx", "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst field = cva(\"block overflow-visible\", {\n variants: {\n tone: {\n ink: \"text-ink dark:text-white\",\n current: \"text-current\",\n volt: \"text-volt\",\n flare: \"text-flare\",\n chalk: \"text-white\",\n },\n },\n defaultVariants: { tone: \"ink\" },\n})\n\nexport interface DotFieldProps\n extends React.ComponentProps<\"svg\">,\n VariantProps {\n rows?: number\n columns?: number\n /** Filled marks, counted column-major — the Hard Inquiries glyph reads left to right. */\n value?: number\n /**\n * `count` fills the first N marks. `density` fades every mark by its\n * distance from the value. `stipple` renders a fixed pseudo-random field,\n * which is how the foot-pressure map in the reference set is built.\n */\n variant?: \"count\" | \"density\" | \"stipple\"\n dotSize?: number\n spacing?: number\n label?: string\n}\n\n/** Deterministic hash so a `stipple` field is stable across renders. */\nfunction noise(i: number) {\n const s = Math.sin(i * 127.1) * 43758.5453\n return s - Math.floor(s)\n}\n\nexport function DotField({\n rows = 3,\n columns = 7,\n value = 0,\n variant = \"count\",\n dotSize = 3,\n spacing = 9,\n tone,\n label,\n className,\n ...props\n}: DotFieldProps) {\n const width = (columns - 1) * spacing + dotSize * 2\n const height = (rows - 1) * spacing + dotSize * 2\n const marks: React.ReactElement[] = []\n\n for (let c = 0; c < columns; c++) {\n for (let r = 0; r < rows; r++) {\n const index = c * rows + r\n let filled = false\n let opacity = 1\n\n if (variant === \"count\") {\n filled = index < value\n opacity = filled ? 1 : 0.28\n } else if (variant === \"density\") {\n filled = true\n opacity = index < value ? 1 : Math.max(0.12, 0.6 - (index - value) * 0.06)\n } else {\n const n = noise(index)\n filled = n > 0.42\n opacity = filled ? 0.35 + n * 0.65 : 0.15\n }\n\n marks.push(\n ,\n )\n }\n }\n\n return (\n \n {marks}\n \n )\n}\n", "type": "registry:ui" } ], "type": "registry:ui" }