{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "echarts-dot",
  "dependencies": [
    "echarts"
  ],
  "files": [
    {
      "path": "src/registry/ui/echarts-dot.tsx",
      "content": "import type * as echarts from \"echarts/core\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dots — map the resting/active variants onto ECharts symbols. Shared by every\n// ECharts chart that draws point markers.\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type DotVariant = \"none\" | \"default\" | \"border\" | \"colored-border\" | \"ping\";\n\n// Dot marker paint — structurally assignable to BOTH the series-level and the\n// per-datum itemStyle (the per-datum variant forbids callback color paints, so\n// the broader LineSeriesOption[\"itemStyle\"] cannot be reused for it).\nexport type DotItemStyleOption = {\n  color?: string | echarts.graphic.LinearGradient;\n  borderColor?: string | echarts.graphic.LinearGradient;\n  borderWidth?: number;\n  opacity?: number;\n};\n\nexport type DotStyle = { size: number; itemStyle: DotItemStyleOption };\n\n// Re-color a solid paint at a given alpha (hex #rgb/#rrggbb or rgb/rgba in, rgba\n// out; named colors pass through). Used for the translucent \"ping\" halo ring.\nfunction withAlpha(color: string, alpha: number): string {\n  if (color.startsWith(\"#\")) {\n    let hex = color.slice(1);\n    if (hex.length === 3)\n      hex = hex\n        .split(\"\")\n        .map((c) => c + c)\n        .join(\"\");\n    const n = parseInt(hex, 16);\n    return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`;\n  }\n  const m = color.match(/rgba?\\(([^)]+)\\)/);\n  if (m) {\n    const [r, g, b] = m[1].split(\",\").map((s) => parseFloat(s));\n    return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n  }\n  return color;\n}\n\nexport function dotItemStyle(\n  variant: DotVariant,\n  paint: string | echarts.graphic.LinearGradient,\n  background: string,\n): DotItemStyleOption {\n  switch (variant) {\n    case \"border\":\n      // Series-colored core with a thick background halo (Recharts r6 / sw5).\n      return { color: paint, borderColor: background, borderWidth: 2 };\n    case \"colored-border\":\n      // Background-filled core with a thin colored ring (Recharts r3 / sw1).\n      return { color: background, borderColor: paint, borderWidth: 1 };\n    case \"ping\": {\n      // Solid core wrapped in a wide, translucent same-color ring — a static\n      // \"ping\". The ring is the symbol's BORDER (a stroke ~1.25× the core\n      // radius), which fills out to a soft halo disc; a genuinely large symbol\n      // would silently fail to render on a category-axis line, so we stroke a\n      // small one instead.\n      const halo = typeof paint === \"string\" ? withAlpha(paint, 0.28) : paint;\n      return { color: paint, borderColor: halo, borderWidth: 10 };\n    }\n    case \"default\":\n      return { color: paint, borderWidth: 0 };\n    default:\n      return {};\n  }\n}\n\n// Sizes mirror the Recharts markers: default r3, border r6 (mostly halo), and\n// colored-border r3+ring. Flattening these to one size makes the hover ring read\n// LARGER than a haloed resting dot — the opposite of the Recharts twin.\nexport const DOT_SIZES: Record<DotVariant, number> = {\n  none: 0,\n  default: 6,\n  border: 8,\n  \"colored-border\": 6,\n  ping: 8,\n};\n\nexport function dotStyle(\n  variant: DotVariant,\n  paint: string | echarts.graphic.LinearGradient,\n  background: string,\n): DotStyle {\n  return { size: DOT_SIZES[variant], itemStyle: dotItemStyle(variant, paint, background) };\n}\n\n// The color the horizontal series gradient shows at position t ∈ [0, 1]. ECharts\n// paints a gradient itemStyle relative to each symbol's own bounding box — a full\n// rainbow inside every dot — while the Recharts dots clip a chart-wide gradient,\n// so each takes the gradient's color at its x-position. Sampling reproduces that.\nexport function sampleGradient(slots: string[], t: number): string {\n  if (slots.length <= 1) return slots[0] ?? \"rgba(120, 120, 120, 1)\";\n\n  const parse = (color: string) =>\n    color\n      .match(/rgba?\\(([^)]+)\\)/)?.[1]\n      .split(\",\")\n      .map(Number) ?? [120, 120, 120, 1];\n\n  const position = t * (slots.length - 1);\n  const index = Math.min(Math.floor(position), slots.length - 2);\n  const fraction = position - index;\n  const [r1, g1, b1, a1 = 1] = parse(slots[index]);\n  const [r2, g2, b2, a2 = 1] = parse(slots[index + 1]);\n  const lerp = (from: number, to: number) => from + (to - from) * fraction;\n\n  return `rgba(${Math.round(lerp(r1, r2))}, ${Math.round(lerp(g1, g2))}, ${Math.round(lerp(b1, b2))}, ${lerp(a1, a2).toFixed(3)})`;\n}\n",
      "type": "registry:component",
      "target": "components/evilcharts/ui/echarts-dot.tsx"
    }
  ],
  "type": "registry:component"
}