analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
303 lines (301 loc) • 8.82 kB
JavaScript
import {
bgClassToCssVar,
describeArc,
polarToCartesian
} from "./chunk-REYQHCVW.mjs";
import {
Text_default
} from "./chunk-IMCIR6TJ.mjs";
import {
cn
} from "./chunk-53ICLDGS.mjs";
// src/components/shared/ChartComponents.tsx
import { useState } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
var calculateYAxisTicks = (maxValue) => {
if (maxValue <= 0) return [0];
const niceMax = Math.ceil(maxValue / 10) * 10;
const step = niceMax / 4;
return [
niceMax,
Math.round(step * 3),
Math.round(step * 2),
Math.round(step),
0
];
};
var LegendItem = ({
color,
label
}) => /* @__PURE__ */ jsxs("div", { className: "flex flex-row items-center gap-2", children: [
/* @__PURE__ */ jsx("div", { className: cn("w-2 h-2 rounded-full", color) }),
/* @__PURE__ */ jsx(Text_default, { size: "xs", weight: "medium", className: "text-text-600", children: label })
] });
var DataBar = ({
label,
value,
maxValue,
colorClass,
chartHeight
}) => {
const percentage = maxValue === 0 ? 0 : value / maxValue * 100;
const barHeight = percentage / 100 * chartHeight;
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 flex-1", children: [
/* @__PURE__ */ jsx(
"div",
{
className: "w-full flex items-end justify-center",
style: { height: chartHeight },
children: /* @__PURE__ */ jsx(
"div",
{
className: cn(
"w-16 rounded-lg transition-all duration-300",
colorClass
),
style: { height: `${barHeight}px` }
}
)
}
),
/* @__PURE__ */ jsx(Text_default, { size: "xs", weight: "medium", className: "text-text-600 text-center", children: label })
] });
};
var GridLines = ({
ticks,
chartHeight
}) => /* @__PURE__ */ jsx(
"div",
{
className: "absolute inset-0 flex flex-col justify-between pointer-events-none",
style: { height: chartHeight },
children: ticks.map((tick, index) => /* @__PURE__ */ jsx(
"div",
{
className: "w-full border-t border-dashed border-border-200",
style: { marginTop: index === 0 ? 0 : void 0 }
},
`${tick}-${index}`
))
}
);
var YAxis = ({
ticks,
chartHeight
}) => /* @__PURE__ */ jsx(
"div",
{
className: "flex flex-col justify-between items-end pr-3",
style: { height: chartHeight },
"aria-hidden": "true",
children: ticks.map((tick, index) => /* @__PURE__ */ jsx(
Text_default,
{
size: "xs",
weight: "medium",
className: "text-text-500",
children: tick
},
`${tick}-${index}`
))
}
);
var PIE_RADIUS_RATIO = 0.44;
var PIE_LABEL_RADIUS_RATIO = 0.62;
var PIE_MIN_PERCENTAGE_FOR_LABEL = 5;
var PIE_WHOLE_THRESHOLD = 99.9999;
var SimplePieChart = ({
slices,
size = 130,
emptyText,
minPercentageForLabel = PIE_MIN_PERCENTAGE_FOR_LABEL,
labelRadiusRatio = PIE_LABEL_RADIUS_RATIO,
labelColor = "var(--color-text-950)",
labelFontWeight = 500,
labelTextShadow,
hoverOpacity = 0.4,
hoveredSlice: externalHovered,
onSliceHover
}) => {
const [internalHovered, setInternalHovered] = useState(null);
const isControlled = externalHovered !== void 0;
const hovered = isControlled ? externalHovered : internalHovered;
const handleSliceHover = (label) => {
if (!isControlled) {
setInternalHovered(label);
}
onSliceHover?.(label);
};
const total = slices.reduce((sum, s) => sum + s.value, 0);
const radius = size * PIE_RADIUS_RATIO;
const center = size / 2;
if (total === 0) {
return /* @__PURE__ */ jsxs(
"svg",
{
width: size,
height: size,
viewBox: `0 0 ${size} ${size}`,
"aria-hidden": !emptyText,
"aria-label": emptyText || void 0,
children: [
/* @__PURE__ */ jsx(
"circle",
{
cx: center,
cy: center,
r: radius,
className: "fill-background-200"
}
),
emptyText && /* @__PURE__ */ jsx(
"text",
{
x: center,
y: center,
textAnchor: "middle",
dominantBaseline: "central",
fill: "var(--color-text-400)",
style: {
fontSize: 12,
fontWeight: 500,
fontFamily: "Roboto, sans-serif"
},
children: emptyText
}
)
]
}
);
}
let cumAngle = 0;
const computed = slices.map((s) => {
const pct = s.value / total * 100;
const startAngle = cumAngle;
cumAngle += pct / 100 * 360;
const endAngle = cumAngle;
const midAngle = startAngle + (endAngle - startAngle) / 2;
return { ...s, pct, startAngle, endAngle, midAngle };
}).filter((s) => s.pct > 0);
return /* @__PURE__ */ jsx(
"svg",
{
width: size,
height: size,
viewBox: `0 0 ${size} ${size}`,
"aria-hidden": "true",
onMouseLeave: () => handleSliceHover(null),
children: computed.map((slice) => {
const sliceKey = slice.key ?? slice.label;
const isHovered = hovered === sliceKey;
const isWhole = slice.pct >= PIE_WHOLE_THRESHOLD;
const arcPath = isWhole ? void 0 : describeArc(
center,
center,
radius,
slice.startAngle,
slice.endAngle
);
const labelPos = polarToCartesian(
center,
center,
radius * labelRadiusRatio,
slice.midAngle
);
const fill = slice.color ?? bgClassToCssVar(slice.colorClass);
return /* @__PURE__ */ jsxs(
"g",
{
onMouseEnter: () => handleSliceHover(sliceKey),
className: "cursor-pointer",
children: [
isWhole ? /* @__PURE__ */ jsx("circle", { cx: center, cy: center, r: radius, fill }) : /* @__PURE__ */ jsx("path", { d: arcPath, fill }),
isHovered && (isWhole ? /* @__PURE__ */ jsx(
"circle",
{
cx: center,
cy: center,
r: radius,
fill: "white",
opacity: hoverOpacity,
style: { pointerEvents: "none" }
}
) : /* @__PURE__ */ jsx(
"path",
{
d: arcPath,
fill: "white",
opacity: hoverOpacity,
style: { pointerEvents: "none" }
}
)),
slice.pct >= minPercentageForLabel && /* @__PURE__ */ jsx(
"text",
{
x: labelPos.x,
y: labelPos.y,
textAnchor: "middle",
dominantBaseline: "central",
fill: labelColor,
style: {
fontSize: 14,
fontWeight: labelFontWeight,
fontFamily: "Roboto, sans-serif",
lineHeight: 1,
letterSpacing: 0,
pointerEvents: "none",
textShadow: labelTextShadow
},
children: `${Math.round(slice.pct)}%`
}
)
]
},
sliceKey
);
})
}
);
};
var LegendRow = ({
colorClass,
color,
label,
value,
displayValue
}) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
/* @__PURE__ */ jsx(
"div",
{
className: cn("w-2 h-2 rounded-full shrink-0", !color && colorClass),
style: color ? { backgroundColor: color } : void 0
}
),
/* @__PURE__ */ jsx(Text_default, { size: "sm", weight: "medium", className: "text-text-950 flex-1", children: label }),
/* @__PURE__ */ jsx(Text_default, { size: "sm", weight: "medium", className: "text-text-600 ml-3", children: displayValue ?? value })
] });
var LegendPieCard = ({ slices }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 p-4 bg-background border border-border-50 rounded-xl", children: [
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: slices.map((s) => /* @__PURE__ */ jsx(
LegendRow,
{
colorClass: s.colorClass,
color: s.color,
label: s.label,
value: s.value,
displayValue: s.displayValue
},
s.key ?? s.label
)) }),
/* @__PURE__ */ jsx(SimplePieChart, { slices })
] });
export {
calculateYAxisTicks,
LegendItem,
DataBar,
GridLines,
YAxis,
SimplePieChart,
LegendRow,
LegendPieCard
};
//# sourceMappingURL=chunk-Y4MZ2KAU.mjs.map