analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
496 lines (478 loc) • 17.7 kB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
var _chunkOSTHYDKMjs = require('./chunk-OSTHYDKM.js');
var _chunkYCIBW5F4js = require('./chunk-YCIBW5F4.js');
var _chunkANT66KVKjs = require('./chunk-ANT66KVK.js');
var _chunkTN3AYOMVjs = require('./chunk-TN3AYOMV.js');
// src/components/TimeChart/TimeChart.tsx
var _react = require('react');
var _jsxruntime = require('react/jsx-runtime');
var TIME_CHART_CATEGORY_KEY = /* @__PURE__ */ ((TIME_CHART_CATEGORY_KEY2) => {
TIME_CHART_CATEGORY_KEY2["ACTIVITIES"] = "activities";
TIME_CHART_CATEGORY_KEY2["CONTENT"] = "content";
TIME_CHART_CATEGORY_KEY2["SIMULATIONS"] = "simulations";
TIME_CHART_CATEGORY_KEY2["QUESTIONNAIRES"] = "questionnaires";
TIME_CHART_CATEGORY_KEY2["RECOMMENDED_LESSONS"] = "recommendedLessons";
return TIME_CHART_CATEGORY_KEY2;
})(TIME_CHART_CATEGORY_KEY || {});
var STUDENT_CATEGORIES = [
{
key: "activities" /* ACTIVITIES */,
label: "Atividades",
colorClass: "bg-success-700"
},
{
key: "simulations" /* SIMULATIONS */,
label: "Simulados",
colorClass: "bg-warning-300"
},
{
key: "questionnaires" /* QUESTIONNAIRES */,
label: "Question\xE1rios",
colorClass: "bg-indicator-info"
},
{
key: "content" /* CONTENT */,
label: "Videoaulas",
colorClass: "bg-success-300"
},
{
key: "recommendedLessons" /* RECOMMENDED_LESSONS */,
label: "Aulas recomendadas",
colorClass: "bg-indicator-positive"
}
];
var DEFAULT_CATEGORIES = [
{
key: "activities" /* ACTIVITIES */,
label: "Atividades",
colorClass: "bg-success-700"
},
{
key: "recommendedLessons" /* RECOMMENDED_LESSONS */,
label: "Aulas recomendadas",
colorClass: "bg-indicator-positive"
}
];
var calculateHourTicks = (maxHours) => {
if (maxHours <= 0) return [0];
const niceMax = Math.ceil(maxHours / 4) * 4;
const step = niceMax / 4;
return [niceMax, step * 3, step * 2, step, 0];
};
var getDayValue = (day, key) => Number(day[key]) || 0;
var LegendItem = ({ color, label }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-row items-center gap-2", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkTN3AYOMVjs.cn.call(void 0, "w-2 h-2 rounded-full", color) }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", weight: "medium", className: "text-text-600", children: label })
] });
var ChartCard = ({
title,
categories,
children,
className,
contentGapClassName = "gap-4",
...props
}) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"div",
{
className: _chunkTN3AYOMVjs.cn.call(void 0,
"flex flex-col p-5 bg-background border border-border-50 rounded-xl",
contentGapClassName,
className
),
...props,
children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-4", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_chunkANT66KVKjs.Text_default,
{
as: "h3",
size: "lg",
weight: "bold",
className: "text-text-950 tracking-[0.2px]",
children: title
}
),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex flex-row flex-wrap justify-between gap-x-6 gap-y-2", children: categories.map((cat) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, LegendItem, { color: cat.colorClass, label: cat.label }, cat.key)) })
] }),
children
]
}
);
var YAxis = ({
ticks,
chartHeight,
unitSuffix
}) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"div",
{
className: "flex flex-col justify-between items-end pr-3",
style: { height: chartHeight },
"aria-hidden": "true",
children: ticks.map((tick, index) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_chunkANT66KVKjs.Text_default,
{
size: "md",
weight: "normal",
className: "text-text-700",
children: tick === 0 ? "0" : `${tick}${unitSuffix}`
},
`${tick}-${index}`
))
}
);
var GridLines = ({
ticks,
chartHeight
}) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"div",
{
className: "absolute inset-0 flex flex-col justify-between pointer-events-none",
style: { height: chartHeight },
children: ticks.map((tick, index) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"div",
{
className: "w-full border-t border-dashed border-border-200"
},
`grid-${tick}-${index}`
))
}
);
var StackedBar = ({
day,
categories,
maxValue,
chartHeight,
unitSuffix
}) => {
const nonZeroCategories = categories.filter(
(cat) => getDayValue(day, cat.key) > 0
);
const tooltipContent = nonZeroCategories.length > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex flex-col gap-1", children: nonZeroCategories.map((cat) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"div",
{
className: "w-2 h-2 rounded-full shrink-0",
style: { background: _chunkYCIBW5F4js.bgClassToCssVar.call(void 0, cat.colorClass) }
}
),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkANT66KVKjs.Text_default, { as: "span", size: "xs", weight: "medium", color: "text-white", children: [
cat.label,
": ",
getDayValue(day, cat.key),
unitSuffix
] })
] }, cat.key)) }) : null;
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_chunkOSTHYDKMjs.Tooltip,
{
content: _nullishCoalesce(tooltipContent, () => ( "")),
disabled: !tooltipContent,
position: "top",
className: "flex-1",
contentClassName: "whitespace-normal",
children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col items-center gap-2 w-full cursor-pointer group/bar", children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"div",
{
className: "w-full flex flex-col-reverse items-center justify-start relative",
style: { height: chartHeight },
children: [
categories.map((cat) => {
const value = getDayValue(day, cat.key);
const segmentHeight = maxValue === 0 ? 0 : value / maxValue * chartHeight;
if (segmentHeight === 0) return null;
const isFirst = cat === nonZeroCategories[0];
const isLast = cat === nonZeroCategories[nonZeroCategories.length - 1];
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"div",
{
"data-testid": `bar-segment-${day.label}-${cat.key}`,
className: _chunkTN3AYOMVjs.cn.call(void 0,
"w-9",
cat.colorClass,
isFirst && "rounded-b",
isLast && "rounded-t",
isFirst && isLast && "rounded"
),
style: { height: `${segmentHeight}px` },
"aria-label": `${cat.label}: ${value}${unitSuffix}`
},
cat.key
);
}),
nonZeroCategories.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "absolute inset-0 bg-white/50 opacity-0 group-hover/bar:opacity-100 transition-opacity duration-200 rounded-md pointer-events-none z-20" })
]
}
),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_chunkANT66KVKjs.Text_default,
{
size: "md",
weight: "normal",
className: "text-text-700 text-center",
"data-testid": `day-label-${day.label}`,
children: day.label
}
)
] })
}
);
};
var PieChart = ({
categories,
totals,
size
}) => {
const [hoveredSlice, setHoveredSlice] = _react.useState.call(void 0, null);
const grandTotal = categories.reduce(
// istanbul ignore next -- defensive: parent always provides all keys
(sum, cat) => sum + (_nullishCoalesce(totals[cat.key], () => ( 0))),
0
);
if (grandTotal === 0) {
const radius2 = size * 0.4;
const center2 = size / 2;
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"svg",
{
width: size,
height: size,
viewBox: `0 0 ${size} ${size}`,
"aria-hidden": "true",
"data-testid": "pie-chart",
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"circle",
{
cx: center2,
cy: center2,
r: radius2,
className: "fill-background-300"
}
)
}
);
}
const radius = size * 0.4;
const center = size / 2;
let cumulativeAngle = 0;
const slices = categories.map((cat) => {
const value = _nullishCoalesce(totals[cat.key], () => ( 0));
const percentage = value / grandTotal * 100;
const angle = percentage / 100 * 360;
const startAngle = cumulativeAngle;
const endAngle = cumulativeAngle + angle;
const midAngle = startAngle + angle / 2;
cumulativeAngle = endAngle;
return { cat, percentage, startAngle, endAngle, midAngle };
}).filter((s) => s.percentage > 0);
const hoveredData = hoveredSlice ? slices.find((s) => s.cat.key === hoveredSlice) : null;
const tooltipPos = hoveredData ? _chunkYCIBW5F4js.polarToCartesian.call(void 0, center, center, radius * 0.6, hoveredData.midAngle) : { x: 0, y: 0 };
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "relative inline-block", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"svg",
{
width: size,
height: size,
viewBox: `0 0 ${size} ${size}`,
"aria-hidden": "true",
"data-testid": "pie-chart",
onMouseLeave: () => setHoveredSlice(null),
children: slices.map((slice) => {
const path = slice.percentage >= 99.99 ? void 0 : _chunkYCIBW5F4js.describeArc.call(void 0,
center,
center,
radius,
slice.startAngle,
slice.endAngle
);
const labelPos = _chunkYCIBW5F4js.polarToCartesian.call(void 0,
center,
center,
radius * 0.6,
slice.midAngle
);
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"g",
{
"data-testid": `pie-slice-${slice.cat.key}`,
onMouseEnter: () => setHoveredSlice(slice.cat.key),
className: "cursor-pointer",
children: [
slice.percentage >= 99.99 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"circle",
{
cx: center,
cy: center,
r: radius,
style: { fill: _chunkYCIBW5F4js.bgClassToCssVar.call(void 0, slice.cat.colorClass) }
}
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"path",
{
d: path,
style: { fill: _chunkYCIBW5F4js.bgClassToCssVar.call(void 0, slice.cat.colorClass) }
}
),
hoveredSlice === slice.cat.key && (slice.percentage >= 99.99 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"circle",
{
cx: center,
cy: center,
r: radius,
fill: "white",
opacity: 0.5,
style: { pointerEvents: "none" }
}
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"path",
{
d: path,
fill: "white",
opacity: 0.5,
style: { pointerEvents: "none" }
}
)),
slice.percentage >= 5 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"text",
{
x: labelPos.x,
y: labelPos.y,
textAnchor: "middle",
dominantBaseline: "central",
className: "fill-white text-xs font-bold",
style: { fontSize: "12px", pointerEvents: "none" },
children: `${Math.round(slice.percentage)}%`
}
)
]
},
slice.cat.key
);
})
}
),
hoveredData && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"div",
{
className: "absolute bg-background-900 text-white px-4 py-2 rounded-lg shadow-[0px_3px_10px_0px_rgba(38,38,38,0.2)] pointer-events-none z-10",
style: {
left: tooltipPos.x,
top: tooltipPos.y,
transform: "translate(-50%, calc(-100% - 8px))"
},
children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"div",
{
className: "w-2 h-2 rounded-full shrink-0",
style: {
background: _chunkYCIBW5F4js.bgClassToCssVar.call(void 0, hoveredData.cat.colorClass)
}
}
),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
_chunkANT66KVKjs.Text_default,
{
as: "span",
size: "xs",
weight: "bold",
color: "text-white",
className: "whitespace-nowrap",
children: [
hoveredData.cat.label,
": ",
Math.round(hoveredData.percentage),
"%"
]
}
)
] })
}
)
] });
};
var TimeChart = ({
data,
barChartTitle = "Dados de horas por semana",
pieChartTitle = "Dados de horas por item",
chartHeight = 180,
pieSize = 200,
unitSuffix = "h",
className,
...props
}) => {
const { categories, hoursByPeriod, hoursByItem } = data;
const periodTotals = hoursByPeriod.map(
(day) => categories.reduce((sum, cat) => sum + getDayValue(day, cat.key), 0)
);
const maxPeriodTotal = Math.max(...periodTotals, 0);
const yAxisTicks = calculateHourTicks(maxPeriodTotal);
const adjustedMax = yAxisTicks[0];
const categoryTotals = {};
if (hoursByItem) {
for (const cat of categories) {
categoryTotals[cat.key] = _nullishCoalesce(hoursByItem[cat.key], () => ( 0));
}
} else {
for (const cat of categories) {
categoryTotals[cat.key] = hoursByPeriod.reduce(
(sum, day) => sum + getDayValue(day, cat.key),
0
);
}
}
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"div",
{
className: _chunkTN3AYOMVjs.cn.call(void 0, "grid grid-cols-1 lg:grid-cols-2 gap-4", className),
"aria-label": "Gr\xE1ficos de dados de horas por semana e por categoria",
...props,
children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
ChartCard,
{
title: barChartTitle,
categories,
contentGapClassName: "gap-8",
children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-row", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
YAxis,
{
ticks: yAxisTicks,
chartHeight,
unitSuffix
}
),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-4" }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex-1 relative", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, GridLines, { ticks: yAxisTicks, chartHeight }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex flex-row flex-1 gap-2 relative z-10", children: hoursByPeriod.map((day, index) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
StackedBar,
{
day,
categories,
maxValue: adjustedMax,
chartHeight,
unitSuffix
},
`${day.label}-${index}`
)) })
] })
] })
}
),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, ChartCard, { title: pieChartTitle, categories, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
PieChart,
{
categories,
totals: categoryTotals,
size: pieSize
}
) }) })
]
}
);
};
var TimeChart_default = TimeChart;
exports.TIME_CHART_CATEGORY_KEY = TIME_CHART_CATEGORY_KEY; exports.STUDENT_CATEGORIES = STUDENT_CATEGORIES; exports.DEFAULT_CATEGORIES = DEFAULT_CATEGORIES; exports.calculateHourTicks = calculateHourTicks; exports.TimeChart = TimeChart; exports.TimeChart_default = TimeChart_default;
//# sourceMappingURL=chunk-IALYXAFQ.js.map