{"spec_id":"sparkline-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// sparkline-basic: Basic Sparkline\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 88/100 | Created: 2026-09-09\n//# anyplot-orientation: landscape\n// anyplot.ai\n// sparkline-basic: Basic Sparkline\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-09-09\n\nimport { SparkLineChart } from \"@mui/x-charts/SparkLineChart\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic fixed-seed LCG) -------------------------\n// The browser has no seeded RNG, so a tiny linear congruential generator\n// stands in for numpy's random.seed(42).\nfunction makeLcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\n\nfunction randomWalk(seed, length, start, drift, volatility) {\n  const rng = makeLcg(seed);\n  const values = [start];\n  for (let i = 1; i < length; i += 1) {\n    const step = drift + (rng() - 0.5) * volatility;\n    values.push(Math.max(0, values[i - 1] + step));\n  }\n  return values;\n}\n\nconst POINTS = 45;\n\n// Four dashboard KPI trend rows — one sparkline per metric, table-row style\nconst metrics = [\n  {\n    label: \"Monthly Revenue\",\n    prefix: \"$\",\n    unit: \"k\",\n    color: t.palette[0],\n    values: randomWalk(7, POINTS, 82, 0.9, 6),\n  },\n  {\n    label: \"Active Users\",\n    prefix: \"\",\n    unit: \"\",\n    color: t.palette[1],\n    values: randomWalk(19, POINTS, 3400, 12, 220),\n  },\n  {\n    label: \"Avg Response Time\",\n    prefix: \"\",\n    unit: \"ms\",\n    color: t.palette[2],\n    values: randomWalk(31, POINTS, 260, -1.1, 14),\n  },\n  {\n    label: \"Conversion Rate\",\n    prefix: \"\",\n    unit: \"%\",\n    color: t.palette[3],\n    values: randomWalk(53, POINTS, 4.2, 0.02, 0.35),\n  },\n];\n\nfunction formatValue(metric) {\n  const last = metric.values[metric.values.length - 1];\n  const decimals = last >= 100 ? 0 : last >= 10 ? 1 : 2;\n  return `${metric.prefix}${last.toFixed(decimals)}${metric.unit}`;\n}\n\nconst TITLE = \"sparkline-basic · javascript · muix · anyplot.ai\";\nconst TITLE_HEIGHT = 90;\nconst OUTER_PADDING = 40;\nconst ROW_GAP = 22;\nconst LABEL_COLUMN_WIDTH = 330;\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  const width = window.ANYPLOT_SIZE.width;\n  const height = window.ANYPLOT_SIZE.height;\n  const bodyHeight = height - TITLE_HEIGHT - OUTER_PADDING * 2;\n  const rowHeight = (bodyHeight - ROW_GAP * (metrics.length - 1)) / metrics.length;\n  const sparkWidth = width - OUTER_PADDING * 2 - LABEL_COLUMN_WIDTH - 32;\n  const sparkHeight = rowHeight - 12;\n\n  return (\n    <Box sx={{ width, height, display: \"flex\", flexDirection: \"column\" }}>\n      <Box\n        sx={{\n          height: TITLE_HEIGHT,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          flexShrink: 0,\n        }}\n      >\n        <Typography sx={{ color: t.ink, fontSize: 42, fontWeight: 600 }}>\n          {TITLE}\n        </Typography>\n      </Box>\n\n      <Box\n        sx={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          gap: `${ROW_GAP}px`,\n          padding: `0px ${OUTER_PADDING}px ${OUTER_PADDING}px`,\n          boxSizing: \"border-box\",\n        }}\n      >\n        {metrics.map((metric) => (\n          <Box\n            key={metric.label}\n            sx={{\n              height: rowHeight,\n              backgroundColor: t.elevatedBg,\n              borderRadius: \"10px\",\n              padding: \"0px 24px\",\n              boxSizing: \"border-box\",\n              display: \"flex\",\n              alignItems: \"center\",\n              gap: \"32px\",\n            }}\n          >\n            <Box sx={{ width: LABEL_COLUMN_WIDTH, flexShrink: 0 }}>\n              <Typography sx={{ color: t.inkSoft, fontSize: 15 }}>\n                {metric.label}\n              </Typography>\n              <Typography sx={{ color: t.ink, fontSize: 30, fontWeight: 600 }}>\n                {formatValue(metric)}\n              </Typography>\n            </Box>\n\n            <SparkLineChart\n              data={metric.values}\n              width={sparkWidth}\n              height={sparkHeight}\n              colors={[metric.color]}\n              curve=\"monotoneX\"\n              area\n              skipAnimation\n              margin={{ top: 10, bottom: 10, left: 4, right: 4 }}\n              sx={{\n                \"& .MuiLineElement-root\": {\n                  strokeWidth: 3.5,\n                  strokeLinecap: \"round\",\n                  strokeLinejoin: \"round\",\n                },\n                \"& .MuiAreaElement-root\": {\n                  fillOpacity: 0.18,\n                },\n              }}\n            />\n          </Box>\n        ))}\n      </Box>\n    </Box>\n  );\n}\n"}