{"spec_id":"lollipop-grouped","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// lollipop-grouped: Grouped Lollipop Chart\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Quarterly revenue ($M) by product line, across four sales regions.\nconst regions = [\"North America\", \"Europe\", \"Asia Pacific\", \"Latin America\"];\nconst productLines = [\"Hardware\", \"Software\", \"Services\"];\nconst revenueByProduct = [\n  [42.5, 31.8, 24.2, 15.6], // Hardware\n  [38.1, 35.4, 29.7, 12.3], // Software\n  [21.6, 19.2, 16.8, 9.1], // Services\n];\n\n// Emphasize the single top-performing lollipop with a larger dot + callout.\nconst top = { value: -Infinity, datasetIndex: -1, dataIndex: -1 };\nrevenueByProduct.forEach((series, datasetIndex) => {\n  series.forEach((value, dataIndex) => {\n    if (value > top.value) Object.assign(top, { value, datasetIndex, dataIndex });\n  });\n});\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Custom plugin: draws the lollipop dot on top of each thin stem bar ------\n// Chart.js has no native lollipop controller; drawing the marker with a plugin\n// keeps every dataset a plain \"bar\" so the category scale's built-in grouped\n// offset positions the stems (and therefore the dots) for us.\nconst lollipopDots = {\n  id: \"lollipopDots\",\n  afterDatasetsDraw(chart) {\n    const { ctx } = chart;\n    chart.data.datasets.forEach((dataset, datasetIndex) => {\n      const meta = chart.getDatasetMeta(datasetIndex);\n      meta.data.forEach((bar, dataIndex) => {\n        const isTop = datasetIndex === top.datasetIndex && dataIndex === top.dataIndex;\n        const radius = isTop ? 17 : 13;\n        const { x, y } = bar.getProps([\"x\", \"y\"], true);\n        ctx.save();\n        ctx.beginPath();\n        ctx.arc(x, y, radius, 0, Math.PI * 2);\n        ctx.fillStyle = dataset.backgroundColor;\n        ctx.fill();\n        ctx.lineWidth = 2;\n        ctx.strokeStyle = t.pageBg;\n        ctx.stroke();\n        ctx.restore();\n        if (isTop) {\n          ctx.save();\n          ctx.font = \"bold 16px sans-serif\";\n          ctx.fillStyle = t.ink;\n          ctx.textAlign = \"center\";\n          ctx.textBaseline = \"bottom\";\n          ctx.fillText(`Top: $${top.value}M`, x, y - radius - 8);\n          ctx.restore();\n        }\n      });\n    });\n  },\n};\n\n// --- Chart ---------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"bar\",\n  data: {\n    labels: regions,\n    datasets: productLines.map((label, i) => ({\n      label,\n      data: revenueByProduct[i],\n      backgroundColor: t.palette[i],\n      barThickness: 4,\n      borderRadius: 2,\n      borderSkipped: false,\n      categoryPercentage: 0.7,\n      barPercentage: 0.9,\n    })),\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: { top: 40, right: 20 } },\n    plugins: {\n      title: {\n        display: true,\n        text: \"lollipop-grouped · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 24, weight: \"500\" },\n        padding: { bottom: 24 },\n      },\n      legend: {\n        position: \"top\",\n        align: \"end\",\n        labels: {\n          color: t.ink,\n          font: { size: 17 },\n          boxWidth: 16,\n          boxHeight: 16,\n        },\n      },\n    },\n    scales: {\n      x: {\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { display: false },\n        border: { color: t.grid },\n        title: {\n          display: true,\n          text: \"Region\",\n          color: t.ink,\n          font: { size: 16 },\n        },\n      },\n      y: {\n        beginAtZero: true,\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n        border: { display: false },\n        title: {\n          display: true,\n          text: \"Quarterly Revenue ($M)\",\n          color: t.ink,\n          font: { size: 16 },\n        },\n      },\n    },\n  },\n  plugins: [lollipopDots],\n});\n"}