mirror of https://github.com/buster-so/buster.git
bar series updater
This commit is contained in:
parent
624452b8f2
commit
2221de1836
|
@ -33,6 +33,12 @@ export const barSeriesBuilder = ({
|
|||
|
||||
dataLabelOptions.stackTotal = {
|
||||
display: function (context) {
|
||||
// Reset the global rotation flag when processing the first data point
|
||||
if (context.dataIndex === 0 && context.datasetIndex === 0) {
|
||||
context.chart.$barDataLabelsGlobalRotation = false;
|
||||
context.chart.$barDataLabelsUpdateInProgress = false;
|
||||
}
|
||||
|
||||
const chart = context.chart;
|
||||
const shownDatasets = context.chart.data.datasets.filter(
|
||||
(dataset, index) =>
|
||||
|
@ -46,8 +52,18 @@ export const barSeriesBuilder = ({
|
|||
const chartLayout = context.chart.options.layout;
|
||||
const padding = { ...DEFAULT_CHART_LAYOUT.padding, top: 24 };
|
||||
context.chart.options.layout = { ...chartLayout, padding };
|
||||
//use setTimeout to ensure that the chart data label almost always overflows.
|
||||
requestAnimationFrame(() => {
|
||||
context.chart.update(); //this is hack because the chart data label almost always overflows
|
||||
if (!context.chart.$barDataLabelsUpdateInProgress) {
|
||||
context.chart.$barDataLabelsUpdateInProgress = true;
|
||||
console.log('updating');
|
||||
context.chart.update('none'); //this is hack because the chart data label almost always overflows
|
||||
|
||||
// Reset the flag after the update completes
|
||||
setTimeout(() => {
|
||||
context.chart.$barDataLabelsUpdateInProgress = false;
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
hasBeenDrawn = true;
|
||||
}
|
||||
|
@ -92,19 +108,14 @@ export const barSeriesBuilder = ({
|
|||
|
||||
declare module 'chart.js' {
|
||||
interface Chart {
|
||||
$barDataLabels: Record<
|
||||
number,
|
||||
Record<
|
||||
number,
|
||||
{
|
||||
formattedValue: string;
|
||||
rotation: number;
|
||||
}
|
||||
>
|
||||
>;
|
||||
$barDataLabels: Record<number, Record<number, string>>;
|
||||
$barDataLabelsGlobalRotation: boolean;
|
||||
$barDataLabelsUpdateInProgress: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
const TEXT_WIDTH_BUFFER = 4;
|
||||
|
||||
export const barBuilder = ({
|
||||
selectedDataset,
|
||||
colors,
|
||||
|
@ -133,8 +144,6 @@ export const barBuilder = ({
|
|||
const usePercentage = !!columnSetting?.showDataLabelsAsPercentage;
|
||||
const showLabels = !!columnSetting?.showDataLabels;
|
||||
|
||||
const textWidthBuffer = 4;
|
||||
|
||||
return {
|
||||
type: 'bar',
|
||||
label: yAxisItem.name,
|
||||
|
@ -150,38 +159,106 @@ export const barBuilder = ({
|
|||
labels: {
|
||||
barTotal: {
|
||||
display: (context) => {
|
||||
// Initialize the global rotation flag if it doesn't exist
|
||||
if (context.chart.$barDataLabelsGlobalRotation === undefined) {
|
||||
context.chart.$barDataLabelsGlobalRotation = false;
|
||||
context.chart.$barDataLabelsUpdateInProgress = false;
|
||||
}
|
||||
|
||||
// First dataset - analyze all data points to determine if any need rotation
|
||||
if (index === 0 && context.dataIndex === 0) {
|
||||
// Reset the rotation flag at the start of each render cycle
|
||||
context.chart.$barDataLabelsGlobalRotation = false;
|
||||
|
||||
// Analyze all datasets and datapoints in this first call
|
||||
const checkAllLabelsForRotation = () => {
|
||||
const datasets = context.chart.data.datasets;
|
||||
const needsGlobalRotation = datasets.some((dataset, datasetIndex) => {
|
||||
if (dataset.type !== 'bar') return false;
|
||||
|
||||
return Array.from({ length: dataset.data.length }).some((_, dataIndex) => {
|
||||
const value = dataset.data[dataIndex] as number;
|
||||
if (!value) return false;
|
||||
|
||||
// Get dimensions for this data point
|
||||
const meta = context.chart.getDatasetMeta(datasetIndex);
|
||||
if (!meta || !meta.data[dataIndex]) return false;
|
||||
|
||||
const barElement = meta.data[dataIndex] as BarElement;
|
||||
const { width: barWidth } = barElement.getProps(['width'], true);
|
||||
|
||||
// Only proceed if bar is visible and has reasonable width
|
||||
if (barWidth < 13) return false;
|
||||
|
||||
// Check if formatted value would need rotation
|
||||
const formattedValue = formatBarAndLineDataLabel(
|
||||
value,
|
||||
{ ...context, datasetIndex, dataIndex } as Context,
|
||||
false, // We're just checking width, not actual formatting
|
||||
columnLabelFormat
|
||||
);
|
||||
|
||||
const { width: textWidth } = context.chart.ctx.measureText(formattedValue);
|
||||
return textWidth > barWidth - TEXT_WIDTH_BUFFER;
|
||||
});
|
||||
});
|
||||
|
||||
if (needsGlobalRotation) {
|
||||
context.chart.$barDataLabelsGlobalRotation = true;
|
||||
|
||||
// Schedule update after all display calculations
|
||||
if (!context.chart.$barDataLabelsUpdateInProgress) {
|
||||
context.chart.$barDataLabelsUpdateInProgress = true;
|
||||
setTimeout(() => {
|
||||
context.chart.update('none');
|
||||
setTimeout(() => {
|
||||
context.chart.$barDataLabelsUpdateInProgress = false;
|
||||
}, 100);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Run rotation analysis immediately
|
||||
checkAllLabelsForRotation();
|
||||
}
|
||||
|
||||
const rawValue = context.dataset.data[context.dataIndex] as number;
|
||||
|
||||
if (!showLabels || !rawValue) return false;
|
||||
const { barWidth, barHeight } = getBarDimensions(context);
|
||||
|
||||
const { barWidth, barHeight } = getBarDimensions(context);
|
||||
if (barWidth < 13) return false;
|
||||
|
||||
const formattedValue = formatBarAndLineDataLabel(
|
||||
rawValue,
|
||||
context,
|
||||
usePercentage,
|
||||
columnLabelFormat
|
||||
);
|
||||
const { width: widthOfFormattedValue } = context.chart.ctx.measureText(formattedValue);
|
||||
const rotation = widthOfFormattedValue > barWidth - textWidthBuffer ? -90 : 0;
|
||||
|
||||
if (rotation === -90 && widthOfFormattedValue > barHeight - textWidthBuffer) {
|
||||
// Get text width for this specific label
|
||||
const { width: textWidth } = context.chart.ctx.measureText(formattedValue);
|
||||
|
||||
// Use the global rotation setting
|
||||
const rotation = context.chart.$barDataLabelsGlobalRotation ? -90 : 0;
|
||||
|
||||
// Check if this label can be displayed even with rotation
|
||||
if (rotation === -90 && textWidth > barHeight - TEXT_WIDTH_BUFFER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setBarDataLabelsManager(context, formattedValue, rotation);
|
||||
// Store only the formatted value, rotation is handled globally
|
||||
setBarDataLabelsManager(context, formattedValue);
|
||||
|
||||
if (barHeight < 16) return false;
|
||||
|
||||
return 'auto';
|
||||
},
|
||||
formatter: (_, context) => {
|
||||
const formattedValue = getBarDataLabelsManager(context).formattedValue;
|
||||
return formattedValue;
|
||||
return context.chart.$barDataLabels?.[context.datasetIndex]?.[context.dataIndex] || '';
|
||||
},
|
||||
rotation: (context) => {
|
||||
const { rotation } = getBarDataLabelsManager(context);
|
||||
return rotation;
|
||||
// Always use the global rotation setting
|
||||
return context.chart.$barDataLabelsGlobalRotation ? -90 : 0;
|
||||
},
|
||||
color: dataLabelFontColorContrast,
|
||||
borderWidth: 0,
|
||||
|
@ -200,17 +277,15 @@ export const barBuilder = ({
|
|||
} as ChartProps<'bar'>['data']['datasets'][number];
|
||||
};
|
||||
|
||||
const setBarDataLabelsManager = (context: Context, formattedValue: string, rotation: number) => {
|
||||
const setBarDataLabelsManager = (context: Context, formattedValue: string) => {
|
||||
const dataIndex = context.dataIndex;
|
||||
const datasetIndex = context.datasetIndex;
|
||||
|
||||
context.chart.$barDataLabels = {
|
||||
...context.chart.$barDataLabels,
|
||||
[datasetIndex]: {
|
||||
...context.chart.$barDataLabels?.[datasetIndex],
|
||||
[dataIndex]: {
|
||||
formattedValue,
|
||||
rotation
|
||||
}
|
||||
[dataIndex]: formattedValue
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -224,17 +299,6 @@ const getBarDimensions = (context: Context) => {
|
|||
return { barWidth, barHeight };
|
||||
};
|
||||
|
||||
const getBarDataLabelsManager = (context: Context) => {
|
||||
const dataIndex = context.dataIndex;
|
||||
const datasetIndex = context.datasetIndex;
|
||||
const values = context.chart.$barDataLabels?.[datasetIndex]?.[dataIndex];
|
||||
|
||||
return {
|
||||
formattedValue: values?.formattedValue,
|
||||
rotation: values?.rotation
|
||||
};
|
||||
};
|
||||
|
||||
export const barSeriesBuilder_labels = (props: LabelBuilderProps) => {
|
||||
const { dataset, columnLabelFormats } = props;
|
||||
|
||||
|
|
Loading…
Reference in New Issue