Skip to content

Commit c1896be

Browse files
committed
fix(dashboard): per-segment stacked bar animation and zero-value border-radius fix
- Freeze base at cumulative position so each bar segment starts from its own section's left edge - Animate x from cumulative pixel position via chart.scales.x.getPixelForValue for simultaneous per-segment growth - Use lastNonZeroIndex/firstNonZeroIndex for border-radius to handle zero-value segments
1 parent 4b48cd4 commit c1896be

1 file changed

Lines changed: 50 additions & 10 deletions

File tree

assets/src/js/frontend/dashboard/pages/instructor/home-charts.ts

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,17 +142,45 @@ const getCSSProperty = (element: HTMLElement, propertyName: string): string => {
142142
return getComputedStyle(element).getPropertyValue(propertyName).trim();
143143
};
144144

145-
const getAnimationConfig = (): { duration: 0 } | undefined => {
145+
const shouldReduceMotion = (): boolean => {
146146
const wrapper = document.querySelector('[data-tutor-theme]') || document.documentElement;
147147
const motion = wrapper.getAttribute('data-tutor-motion');
148+
return (
149+
motion === 'reduce' ||
150+
((!motion || motion === 'auto') && window.matchMedia('(prefers-reduced-motion: reduce)').matches)
151+
);
152+
};
148153

149-
if (motion === 'reduce') {
154+
const getAnimationConfig = (): { duration: 0 } | undefined => {
155+
if (shouldReduceMotion()) {
150156
return { duration: 0 };
151157
}
152-
if ((!motion || motion === 'auto') && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
158+
return undefined;
159+
};
160+
161+
const getCompletionChartAnimation = (): { duration: 0 } | Record<string, unknown> => {
162+
if (shouldReduceMotion()) {
153163
return { duration: 0 };
154164
}
155-
return undefined;
165+
166+
return {
167+
base: { duration: 0 },
168+
x: {
169+
from: (ctx: Record<string, unknown>) => {
170+
const chart = ctx.chart as {
171+
scales: { x: { getPixelForValue: (v: number) => number } };
172+
data: { datasets: Array<{ data: Array<number> }> };
173+
};
174+
const dataIndex = ctx.dataIndex as number;
175+
const datasetIndex = ctx.datasetIndex as number;
176+
let cumulative = 0;
177+
for (let i = 0; i < datasetIndex; i++) {
178+
cumulative += chart.data.datasets[i].data[dataIndex] || 0;
179+
}
180+
return chart.scales.x.getPixelForValue(cumulative);
181+
},
182+
},
183+
};
156184
};
157185

158186
const createChart = (canvas: HTMLCanvasElement, config: ChartConfiguration): void => {
@@ -792,6 +820,9 @@ export const courseCompletionChart = (data: CourseCompletionChartData) => ({
792820
const chartColors = [colors.enrolled, colors.completed, colors.in_progress, colors.inactive, colors.cancelled];
793821
const dataKeys: CourseCompletionChartDataKey[] = ['enrolled', 'completed', 'in_progress', 'inactive', 'cancelled'];
794822

823+
const lastNonZeroIndex = chartData.reduce((last, value, i) => (value > 0 ? i : last), -1);
824+
const firstNonZeroIndex = chartData.findIndex((value) => value > 0);
825+
795826
return {
796827
type: 'bar',
797828
data: {
@@ -801,20 +832,29 @@ export const courseCompletionChart = (data: CourseCompletionChartData) => ({
801832
backgroundColor: chartColors[index],
802833
barThickness: CHART_CONFIG.bar.thickness,
803834
borderRadius: {
804-
bottomLeft: index === 0 ? CHART_CONFIG.bar.borderRadius : 0,
805-
topLeft: index === 0 ? CHART_CONFIG.bar.borderRadius : 0,
806-
bottomRight: index === chartData.length - 1 ? CHART_CONFIG.bar.borderRadius : 0,
807-
topRight: index === chartData.length - 1 ? CHART_CONFIG.bar.borderRadius : 0,
835+
bottomLeft: index === (firstNonZeroIndex > -1 ? firstNonZeroIndex : 0) ? CHART_CONFIG.bar.borderRadius : 0,
836+
topLeft: index === (firstNonZeroIndex > -1 ? firstNonZeroIndex : 0) ? CHART_CONFIG.bar.borderRadius : 0,
837+
bottomRight:
838+
index === (lastNonZeroIndex > -1 ? lastNonZeroIndex : chartData.length - 1)
839+
? CHART_CONFIG.bar.borderRadius
840+
: 0,
841+
topRight:
842+
index === (lastNonZeroIndex > -1 ? lastNonZeroIndex : chartData.length - 1)
843+
? CHART_CONFIG.bar.borderRadius
844+
: 0,
808845
},
809846
borderSkipped: false,
810847
borderWidth: {
811-
right: index === chartData.length - 1 ? 0 : CHART_CONFIG.bar.borderWidth,
848+
right:
849+
index === (lastNonZeroIndex > -1 ? lastNonZeroIndex : chartData.length - 1)
850+
? 0
851+
: CHART_CONFIG.bar.borderWidth,
812852
},
813853
borderColor: getCSSProperty(this.$refs.canvas, '--tutor-surface-l1'),
814854
})),
815855
},
816856
options: {
817-
animation: getAnimationConfig(),
857+
animation: getCompletionChartAnimation(),
818858
indexAxis: 'y',
819859
responsive: CHART_CONFIG.common.responsive,
820860
maintainAspectRatio: false,

0 commit comments

Comments
 (0)