{"spec_id":"bar-pareto","library":"muix","language":"javascript","code":"// anyplot.ai\n// bar-pareto: Pareto Chart with Cumulative Line\n// Library: muix 9.16.0 | JavaScript 22.23.3\n// Quality: 87/100 | Updated: 2026-10-24\nimport { ChartsContainer } from \"@mui/x-charts/ChartsContainer\";\nimport { BarPlot } from \"@mui/x-charts/BarChart\";\nimport { LinePlot, MarkPlot } from \"@mui/x-charts/LineChart\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\nimport { ChartsGrid } from \"@mui/x-charts/ChartsGrid\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Constants\nconst PARETO_THRESHOLD = 80;\nconst PERCENT_MAX = 100;\nconst COUNT_PAD_SHARE = 0.18;\n\n// Columns: set these to your data's column names\nconst CATEGORY = \"Defect type\"; // role: category\nconst COUNT = \"Defect count\"; // role: count\n\n// Data: replace this block with your own records, one object per row\nconst data = [\n  { [CATEGORY]: \"Scratches\", [COUNT]: 412 },\n  { [CATEGORY]: \"Dents\", [COUNT]: 298 },\n  { [CATEGORY]: \"Cracks\", [COUNT]: 187 },\n  { [CATEGORY]: \"Warping\", [COUNT]: 143 },\n  { [CATEGORY]: \"Discoloration\", [COUNT]: 98 },\n  { [CATEGORY]: \"Missing Part\", [COUNT]: 76 },\n  { [CATEGORY]: \"Wrong Size\", [COUNT]: 54 },\n  { [CATEGORY]: \"Surface Flaw\", [COUNT]: 32 },\n];\n\n// Plot: reads only data and the constants above\nconst sorted = [...data].sort((a, b) => b[COUNT] - a[COUNT]);\nconst categories = sorted.map((d) => d[CATEGORY]);\nconst counts = sorted.map((d) => d[COUNT]);\nconst total = counts.reduce((a, b) => a + b, 0);\n\nlet running = 0;\nconst cumPercent = counts.map((c) => {\n  running += c;\n  return Number(((running / total) * PERCENT_MAX).toFixed(1));\n});\n\nconst countMax = Math.ceil(counts[0] * (1 + COUNT_PAD_SHARE));\nconst vitalCount = cumPercent.findIndex((p) => p >= PARETO_THRESHOLD) + 1;\nconst cumLabel = \"Cumulative share (%)\";\nconst TITLE = \"bar-pareto · javascript · muix · anyplot.ai\";\nconst SUBTITLE = `${COUNT}: ${total.toLocaleString(\"en-US\")} · ${CATEGORY}: ${categories.length} · Top ${vitalCount} reach ${cumPercent[vitalCount - 1]}%`;\n\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n  const HEADER_H = 104;\n  const chartH = height - HEADER_H;\n\n  return (\n    <Box\n      sx={{\n        width,\n        height,\n        bgcolor: t.pageBg,\n        display: \"flex\",\n        flexDirection: \"column\",\n        fontFamily:\n          '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif',\n        boxSizing: \"border-box\",\n      }}\n    >\n      <Box\n        sx={{\n          height: HEADER_H,\n          pt: \"14px\",\n          px: \"48px\",\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          boxSizing: \"border-box\",\n        }}\n      >\n        <Typography\n          sx={{ color: t.ink, fontSize: 22, fontWeight: 600, lineHeight: 1.25 }}\n        >\n          {TITLE}\n        </Typography>\n        <Typography sx={{ color: t.inkSoft, fontSize: 15, mt: \"4px\" }}>\n          {SUBTITLE}\n        </Typography>\n        <Box\n          sx={{\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            gap: \"28px\",\n            mt: \"6px\",\n          }}\n        >\n          <Box sx={{ display: \"flex\", alignItems: \"center\", gap: \"8px\" }}>\n            <Box\n              sx={{\n                width: 18,\n                height: 12,\n                bgcolor: t.palette[0],\n                borderRadius: \"2px\",\n              }}\n            />\n            <Typography sx={{ color: t.inkSoft, fontSize: 14 }}>\n              {COUNT}\n            </Typography>\n          </Box>\n          <Box sx={{ display: \"flex\", alignItems: \"center\", gap: \"8px\" }}>\n            <Box\n              sx={{\n                width: 22,\n                height: 3,\n                bgcolor: t.palette[1],\n                borderRadius: \"2px\",\n              }}\n            />\n            <Typography sx={{ color: t.inkSoft, fontSize: 14 }}>\n              {cumLabel}\n            </Typography>\n          </Box>\n          <Box sx={{ display: \"flex\", alignItems: \"center\", gap: \"8px\" }}>\n            <Box\n              sx={{\n                width: 22,\n                height: 0,\n                borderTop: `2.5px dashed ${t.amber}`,\n              }}\n            />\n            <Typography sx={{ color: t.inkSoft, fontSize: 14 }}>\n              {`${PARETO_THRESHOLD}% threshold`}\n            </Typography>\n          </Box>\n        </Box>\n      </Box>\n\n      <ChartsContainer\n        width={width}\n        height={chartH}\n        series={[\n          {\n            type: \"bar\",\n            id: \"defect-counts\",\n            yAxisId: \"counts\",\n            data: counts,\n            label: COUNT,\n            color: t.palette[0],\n          },\n          {\n            type: \"line\",\n            id: \"cum-line\",\n            yAxisId: \"percent\",\n            data: cumPercent,\n            label: cumLabel,\n            color: t.palette[1],\n            showMark: true,\n            shape: \"circle\",\n            curve: \"linear\",\n          },\n        ]}\n        xAxis={[\n          {\n            id: \"x\",\n            scaleType: \"band\",\n            data: categories,\n            categoryGapRatio: 0.35,\n            height: 68,\n            label: CATEGORY,\n            labelStyle: { fontSize: 16, fill: t.ink, fontWeight: 500 },\n            tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n          },\n        ]}\n        yAxis={[\n          {\n            id: \"counts\",\n            position: \"left\",\n            min: 0,\n            max: countMax,\n            width: 84,\n            label: COUNT,\n            labelStyle: { fontSize: 16, fill: t.ink, fontWeight: 500 },\n            tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n          },\n          {\n            id: \"percent\",\n            position: \"right\",\n            min: 0,\n            max: PERCENT_MAX,\n            width: 88,\n            valueFormatter: (v: number) => `${v}%`,\n            label: cumLabel,\n            labelStyle: { fontSize: 16, fill: t.ink, fontWeight: 500 },\n            tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n          },\n        ]}\n        margin={{ top: 20, right: 36, bottom: 24, left: 36 }}\n        skipAnimation\n        sx={{\n          \"& .MuiChartsAxis-line\": { stroke: t.inkSoft },\n          \"& .MuiChartsAxis-tick\": { stroke: t.inkSoft },\n          \"& .MuiChartsGrid-line\": { stroke: t.grid, strokeWidth: 1 },\n          \"& .MuiLineChart-line\": { strokeWidth: 3 },\n        }}\n      >\n        <ChartsGrid horizontal />\n        <BarPlot borderRadius={3} skipAnimation />\n        <LinePlot skipAnimation />\n        <MarkPlot skipAnimation />\n        <ChartsReferenceLine\n          y={PARETO_THRESHOLD}\n          axisId=\"percent\"\n          lineStyle={{\n            stroke: t.amber,\n            strokeDasharray: \"8 5\",\n            strokeWidth: 2.5,\n          }}\n        />\n        <ChartsXAxis axisId=\"x\" />\n        <ChartsYAxis axisId=\"counts\" />\n        <ChartsYAxis axisId=\"percent\" />\n      </ChartsContainer>\n    </Box>\n  );\n}\n"}