{"spec_id":"gauge-activity-rings","library":"muix","language":"javascript","code":"// anyplot.ai\n// gauge-activity-rings: Activity Rings Progress Chart\n// Library: muix 7.29.1 | JavaScript 22.22.3\n// Quality: 89/100 | Created: 2026-06-14\n//# anyplot-orientation: square\n// anyplot.ai\n// gauge-activity-rings: Activity Rings Progress Chart\n// Library: MUI X Charts | React | Node 22\n// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.\n// Quality: pending | Created: 2026-06-14\n\nimport {\n  GaugeContainer,\n  GaugeValueArc,\n  GaugeReferenceArc,\n} from \"@mui/x-charts/Gauge\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst W = window.ANYPLOT_SIZE.width;  // 1200 CSS px (square mount)\nconst H = window.ANYPLOT_SIZE.height; // 1200 CSS px\n\n// Higher opacity in dark mode so tracks remain visible against near-black\nconst trackOpacity = window.ANYPLOT_THEME === \"dark\" ? 0.28 : 0.18;\n\n// Daily fitness activity — Move / Exercise / Stand rings (spec example data)\nconst rings = [\n  { label: \"Move\",     value: 420, goal: 600, unit: \"kcal\", color: t.palette[0] },\n  { label: \"Exercise\", value: 25,  goal: 30,  unit: \"min\",  color: t.palette[1] },\n  { label: \"Stand\",    value: 9,   goal: 12,  unit: \"hr\",   color: t.palette[2] },\n];\n\n// Concentric radii as % of maxRadius (≈ min(W,H)/2 = 600 px for full-circle gauge)\n// Outer → inner: 12% wide rings, 4% gaps between them\nconst RING_RADII = [\n  { inner: \"60%\", outer: \"72%\" }, // outer  (Move)\n  { inner: \"44%\", outer: \"56%\" }, // middle (Exercise)\n  { inner: \"28%\", outer: \"40%\" }, // inner  (Stand)\n];\n\n// Identify the lagging ring (lowest percentage) for visual emphasis\nconst ringPcts = rings.map((r) => Math.round((r.value / r.goal) * 100));\nconst laggingIndex = ringPcts.indexOf(Math.min(...ringPcts));\n\nexport default function Chart() {\n  return (\n    <Box sx={{ position: \"relative\", width: W, height: H, bgcolor: t.pageBg }}>\n      {/* Three concentric gauge rings stacked via absolute positioning */}\n      {rings.map((ring, i) => {\n        const pct = Math.min((ring.value / ring.goal) * 100, 100);\n        return (\n          <Box key={ring.label} sx={{ position: \"absolute\", inset: 0 }}>\n            <GaugeContainer\n              width={W}\n              height={H}\n              value={pct}\n              valueMin={0}\n              valueMax={100}\n              startAngle={0}\n              endAngle={360}\n              innerRadius={RING_RADII[i].inner}\n              outerRadius={RING_RADII[i].outer}\n              cornerRadius=\"50%\"\n              skipAnimation\n            >\n              {/* Full-circle background track */}\n              <GaugeReferenceArc style={{ fill: ring.color, opacity: trackOpacity }} />\n              {/* Colored progress arc */}\n              <GaugeValueArc style={{ fill: ring.color }} />\n            </GaugeContainer>\n          </Box>\n        );\n      })}\n\n      {/* Title */}\n      <Box\n        sx={{\n          position: \"absolute\",\n          top: 28,\n          left: 0,\n          right: 0,\n          textAlign: \"center\",\n          pointerEvents: \"none\",\n        }}\n      >\n        <Typography\n          sx={{ color: t.inkSoft, fontSize: 22, fontWeight: 400, letterSpacing: \"0.01em\" }}\n        >\n          gauge-activity-rings · javascript · muix · anyplot.ai\n        </Typography>\n      </Box>\n\n      {/* Center label — sits inside the innermost ring (< 28% radius = 168 px) */}\n      <Box\n        sx={{\n          position: \"absolute\",\n          top: \"50%\",\n          left: \"50%\",\n          transform: \"translate(-50%, -50%)\",\n          textAlign: \"center\",\n          pointerEvents: \"none\",\n        }}\n      >\n        <Typography\n          sx={{ color: t.ink, fontSize: 34, fontWeight: 700, lineHeight: 1.15 }}\n        >\n          Daily\n        </Typography>\n        <Typography\n          sx={{ color: t.ink, fontSize: 34, fontWeight: 700, lineHeight: 1.15 }}\n        >\n          Activity\n        </Typography>\n        <Typography sx={{ color: t.inkSoft, fontSize: 16, mt: 0.75 }}>\n          Jun 14, 2026\n        </Typography>\n      </Box>\n\n      {/* Bottom legend: percentage + name + value/goal */}\n      <Box\n        sx={{\n          position: \"absolute\",\n          bottom: 52,\n          left: 0,\n          right: 0,\n          display: \"flex\",\n          justifyContent: \"center\",\n          gap: \"64px\",\n          pointerEvents: \"none\",\n        }}\n      >\n        {rings.map((ring, i) => {\n          const pct = ringPcts[i];\n          const isLagging = i === laggingIndex;\n          return (\n            <Box key={ring.label} sx={{ textAlign: \"center\" }}>\n              <Typography\n                sx={{ color: ring.color, fontSize: 30, fontWeight: 800, lineHeight: 1 }}\n              >\n                {pct}%\n              </Typography>\n              <Typography\n                sx={{ color: t.ink, fontSize: 18, fontWeight: 600, mt: 0.5 }}\n              >\n                {ring.label}\n              </Typography>\n              <Typography sx={{ color: t.inkSoft, fontSize: 14 }}>\n                {ring.value} / {ring.goal} {ring.unit}\n              </Typography>\n              {isLagging && (\n                <Typography sx={{ color: t.amber, fontSize: 13, fontWeight: 700, mt: 0.5 }}>\n                  ↓ focus area\n                </Typography>\n              )}\n            </Box>\n          );\n        })}\n      </Box>\n    </Box>\n  );\n}\n"}