Skip to content

Commit

Permalink
Add chart auto resize
Browse files Browse the repository at this point in the history
  • Loading branch information
Fosol committed Aug 29, 2024
1 parent a192a8a commit 00c998c
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export const ReportSectionMediaAnalytics = ({ index }: IReportSectionMediaAnalyt
? chart.settings.datasetValue[0]
: '',
excludeEmptyValues: false,
isHorizontal: false,
isHorizontal: true,
showDataLabels: false,
width: 500,
options: { ...chart.settings?.options },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const ReportSectionMediaAnalytics = React.forwardRef<
? chart.settings.datasetValue[0]
: '',
excludeEmptyValues: false,
isHorizontal: false,
isHorizontal: true,
showDataLabels: false,
width: 500,
options: { ...chart.settings?.options },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ import React from 'react';
import { useModal } from 'tno-core';

import {
calcAutoSize,
determineBackgroundColor,
determineBorderColor,
generateChartOptions,
getSectionLabel,
IChartData,
IChartDataset,
shouldResizeChart,
} from '../utils';
import { IChartSectionProps } from './IChartSectionProps';

Expand Down Expand Up @@ -65,16 +67,19 @@ export const ChartViewer: React.FC<IChartViewerProps> = ({
const uid = `${section.id}_${sectionIndex}`;

React.useEffect(() => {
const axisColumnWidth =
data && chart.sectionSettings.width ? chart.sectionSettings.width / data.labels.length : 0;
if (data && chart.sectionSettings.autoResize && axisColumnWidth < minAxisColumnWidth) {
if (
data &&
chart.sectionSettings.autoResize &&
shouldResizeChart(data.labels.length, chart.sectionSettings, minAxisColumnWidth)
) {
toggleModal();
}
// Only update when values change in data or chart.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
chart.sectionSettings.autoResize,
chart.sectionSettings.width,
chart.sectionSettings.height,
data?.labels.length,
minAxisColumnWidth,
]);
Expand Down Expand Up @@ -135,17 +140,15 @@ export const ChartViewer: React.FC<IChartViewerProps> = ({
if (data) {
// Calculate a new size for the chart based on the number of labels on the axis.
// Handle the direction of the axis, and aspect ratio.
const isHorizontal =
chart.sectionSettings.isHorizontal ||
chart.sectionSettings.isHorizontal === undefined;
const size = data.labels.length * minAxisColumnWidth;
const height = isHorizontal ? chart.sectionSettings.height : size;
setFieldValue(`sections.${sectionIndex}.chartTemplates.${chartIndex}.sectionSettings`, {
...chart.sectionSettings,
width: isHorizontal ? size : chart.sectionSettings.width,
height: height,
aspectRatio: height ? undefined : chart.sectionSettings.aspectRatio,
});
const settings = calcAutoSize(
data.labels.length,
chart.sectionSettings,
minAxisColumnWidth,
);
setFieldValue(
`sections.${sectionIndex}.chartTemplates.${chartIndex}.sectionSettings`,
settings,
);
}
toggleModal();
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { IChartSectionSettingsModel } from 'tno-core';

/**
* Determines if the size of the chart will fit the axis labels.
* If not it will generate a new chart section settings.
* @param axisLabelCount The number of axis labels.
* @param chartSectionSettings The chart section settings.
* @param minAxisColumnWidth The minimum size of an axis column (defaults 30).
* @returns Updated chart section settings.
*/
export const calcAutoSize = (
axisLabelCount: number,
chartSectionSettings: IChartSectionSettingsModel,
minAxisColumnWidth: number = 30,
) => {
const size = chartSectionSettings.isHorizontal
? chartSectionSettings.width
: chartSectionSettings.height;
if (!size || !axisLabelCount) return chartSectionSettings;
const axisColumnWidth = size / axisLabelCount;
if (axisColumnWidth < minAxisColumnWidth) {
const newSize = minAxisColumnWidth * axisLabelCount;
const width = chartSectionSettings.isHorizontal ? newSize : chartSectionSettings.width;
const height = chartSectionSettings.isHorizontal ? chartSectionSettings.height : newSize;
return {
...chartSectionSettings,
width,
height,
aspectRatio: height ? undefined : chartSectionSettings.aspectRatio,
};
}
return chartSectionSettings;
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export const generateChartOptions = (
x: {
stacked: chartSectionSettings.stacked,
display: chartSectionSettings.xShowAxisLabels,
suggestedMin: chartSectionSettings.isHorizontal
suggestedMin: !chartSectionSettings.isHorizontal
? chartSectionSettings.scaleSuggestedMin
: undefined,
suggestedMax: chartSectionSettings.isHorizontal
suggestedMax: !chartSectionSettings.isHorizontal
? chartSectionSettings.scaleSuggestedMax ?? scaleCalcMax
: undefined,
title: {
Expand All @@ -46,10 +46,10 @@ export const generateChartOptions = (
y: {
stacked: chartSectionSettings.stacked,
display: chartSectionSettings.yShowAxisLabels,
suggestedMin: !chartSectionSettings.isHorizontal
suggestedMin: chartSectionSettings.isHorizontal
? chartSectionSettings.scaleSuggestedMin
: undefined,
suggestedMax: !chartSectionSettings.isHorizontal
suggestedMax: chartSectionSettings.isHorizontal
? chartSectionSettings.scaleSuggestedMax ?? scaleCalcMax
: undefined,
title: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ export const getChartData = (
};
});

console.debug(labels, groups, results);

return {
labels,
datasets: results,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './calcAutoSize';
export * from './calcAverageSentiment';
export * from './calcDataPoint';
export * from './convertToChart';
Expand Down Expand Up @@ -26,3 +27,4 @@ export * from './selectColorForDataset';
export * from './selectSentimentColorForDataset';
export * from './selectSentimentColorForValue';
export * from './separateDatasets';
export * from './shouldResizeChart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { IChartSectionSettingsModel } from 'tno-core';

/**
* Determines if the size of the chart will fit the axis labels.
* @param axisLabelCount The number of axis labels.
* @param chartSectionSettings The chart section settings.
* @param minAxisColumnWidth The minimum size of an axis column (defaults 30).
* @returns True if the chart should resize.
*/
export const shouldResizeChart = (
axisLabelCount: number,
chartSectionSettings: IChartSectionSettingsModel,
minAxisColumnWidth: number = 30,
) => {
const size = !chartSectionSettings.isHorizontal
? chartSectionSettings.height
: chartSectionSettings.width;
if (!size || !axisLabelCount) return false;
const axisColumnWidth = size / axisLabelCount;
return axisColumnWidth < minAxisColumnWidth;
};
8 changes: 4 additions & 4 deletions libs/net/template/Models/Charts/ChartDatasetModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ public class ChartDatasetModel
public double?[] Data { get; set; } = Array.Empty<double?>();

/// <summary>
/// get/set -
/// get/set - Either an array of colours, or a function.
/// </summary>
[JsonPropertyName("backgroundColor")]
public string[]? BackgroundColor { get; set; }
public object? BackgroundColor { get; set; }

/// <summary>
/// get/set -
/// get/set - Either an array of colours, or a function.
/// </summary>
[JsonPropertyName("borderColor")]
public string[]? BorderColor { get; set; }
public object? BorderColor { get; set; }

/// <summary>
/// get/set -
Expand Down
12 changes: 12 additions & 0 deletions libs/net/template/Models/Charts/Options/ChartOptionsModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,17 @@ public class ChartOptionsModel
/// </summary>
[JsonPropertyName("indexAxis")]
public string? IndexAxis { get; set; }

/// <summary>
/// get/set -
/// </summary>
[JsonPropertyName("aspectRatio")]
public string? AspectRatio { get; set; }

/// <summary>
/// get/set -
/// </summary>
[JsonPropertyName("maintainAspectRatio")]
public string? MaintainAspectRatio { get; set; }
#endregion
}
57 changes: 55 additions & 2 deletions libs/net/template/ReportEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,29 +190,43 @@ public async Task<string> GenerateBase64ImageAsync(
// Get the Chart JSON data.
var data = model.ChartData ?? (await this.GenerateJsonAsync(model, isPreview)).Json;
var dataJson = data.ToJson();
var dataModel = JsonSerializer.Deserialize<ChartDataModel>(dataJson, this.SerializerOptions);

// If chart settings require auto scale
if (model.ChartTemplate.SectionSettings.ScaleCalcMax.HasValue)
{
// Determine the maximum scale and add the auto max to it.
var dataModel = JsonSerializer.Deserialize<ChartDataModel>(dataJson, this.SerializerOptions);
var max = dataModel?.Datasets.Any() == true ? dataModel?.Datasets.Max(ds => ds.Data.Any() ? ds.Data.Max(v => v ?? 0) : 0) : null;
if (max.HasValue)
{
var suggestedMax = (int)max + model.ChartTemplate.SectionSettings.ScaleCalcMax.Value;
var sectionJsonText = model.ChartTemplate.SectionSettings.Options.ToJson();
if (sectionJsonText != "{}")
{
var chartOptions = JsonSerializer.Deserialize<ChartOptionsModel>(model.ChartTemplate.SectionSettings.Options);
if (chartOptions != null)
{
var suggestedMax = (int)max + model.ChartTemplate.SectionSettings.ScaleCalcMax.Value;
chartOptions.Scales.X.SuggestedMax = suggestedMax;
chartOptions.Scales.Y.SuggestedMax = suggestedMax;
model.ChartTemplate.SectionSettings.Options = JsonDocument.Parse(JsonSerializer.Serialize(chartOptions, this.SerializerOptions));
}
}
}
}
if (dataModel != null && model.ChartTemplate.SectionSettings.AutoResize == true && ShouldResize(dataModel, model.ChartTemplate.SectionSettings, 30))
{
// If the chart should be resized, calculate the new size and update the options.
UpdateChartSize(dataModel, model.ChartTemplate.SectionSettings, 30);
var sectionJsonText = model.ChartTemplate.SectionSettings.Options.ToJson();
if (sectionJsonText != "{}")
{
var chartOptions = JsonSerializer.Deserialize<ChartOptionsModel>(model.ChartTemplate.SectionSettings.Options);
if (chartOptions != null)
{
model.ChartTemplate.SectionSettings.Options = JsonDocument.Parse(JsonSerializer.Serialize(chartOptions, this.SerializerOptions));
}
}
}

var optionsJson = model.ChartTemplate.SectionSettings.Options != null ? JsonSerializer.Serialize(MergeChartOptions(model.ChartTemplate.Settings, model.ChartTemplate.SectionSettings)) : "{}";
// Modify the chart options based on section settings.
Expand All @@ -232,6 +246,45 @@ public async Task<string> GenerateBase64ImageAsync(
return await response.Content.ReadAsStringAsync();
}

/// <summary>
/// Determine if the chart should be resized, based on the number of axis labels.
/// </summary>
/// <param name="data"></param>
/// <param name="settings"></param>
/// <param name="minAxisColumnWidth"></param>
/// <returns></returns>
private bool ShouldResize(ChartDataModel data, API.Models.Settings.ChartSectionSettingsModel settings, int minAxisColumnWidth)
{
var axisLabelCount = data.Labels.Length;
var size = settings.IsHorizontal == false ? settings.Height : settings.Width;
if (size == null || size <= 0 || axisLabelCount <= 0) return false;
var currentAxisColumnWidth = size / axisLabelCount;
return currentAxisColumnWidth < minAxisColumnWidth;
}

/// <summary>
/// Resize the chart based on the number of axis labels.
/// </summary>
/// <param name="data"></param>
/// <param name="settings"></param>
/// <param name="minAxisColumnWidth"></param>
private void UpdateChartSize(ChartDataModel data, API.Models.Settings.ChartSectionSettingsModel settings, int minAxisColumnWidth)
{
var axisLabelCount = data.Labels.Length;
var size = settings.IsHorizontal == false ? settings.Height : settings.Width;
if (size == null || size <= 0 || axisLabelCount <= 0) return;
var currentAxisColumnWidth = size / axisLabelCount;
if (currentAxisColumnWidth < minAxisColumnWidth)
{
var newSize = minAxisColumnWidth * axisLabelCount;
var width = settings.IsHorizontal == false ? settings.Width : newSize;
var height = settings.IsHorizontal == false ? newSize : settings.Height;
settings.Width = width;
settings.Height = height;
settings.AspectRatio = height.HasValue ? null : settings.AspectRatio;
}
}

#region Content
/// <summary>
/// Generate the output of the report with the Razor engine.
Expand Down

0 comments on commit 00c998c

Please sign in to comment.