Skip to content

Commit

Permalink
Dashboard: Add binary charts + allow user to rename chart feature lab…
Browse files Browse the repository at this point in the history
…el (#2094)
  • Loading branch information
Terdious authored Oct 7, 2024
1 parent 3728087 commit f0a3c12
Show file tree
Hide file tree
Showing 11 changed files with 1,242 additions and 318 deletions.
87 changes: 85 additions & 2 deletions front/src/components/boxs/chart/ApexChartComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { getApexChartBarOptions } from './ApexChartBarOptions';
import { getApexChartAreaOptions } from './ApexChartAreaOptions';
import { getApexChartLineOptions } from './ApexChartLineOptions';
import { getApexChartStepLineOptions } from './ApexChartStepLineOptions';
import { getApexChartTimelineOptions } from './ApexChartTimelineOptions';
import mergeArray from '../../../utils/mergeArray';

dayjs.extend(localizedFormat);
Expand Down Expand Up @@ -51,6 +52,56 @@ class ApexChartComponent extends Component {
}
};
}
addDateFormatterRangeBar(options) {
const createTooltipContent = (opts, startDate, endDate) => {
const w = opts.ctx.w;
const seriesName = w.config.series[opts.seriesIndex].name ? w.config.series[opts.seriesIndex].name : '';
const ylabel = w.globals.seriesX[opts.seriesIndex][opts.dataPointIndex];
const color = w.globals.colors[opts.seriesIndex];

return `<div class="apexcharts-tooltip-rangebar">
<div> <span class="series-name" style="color: ${color}">
${seriesName ? seriesName : ''}
</span></div>
<div> <span class="category">
${ylabel}:
</span> <span class="value start-value"></br>&nbsp;&nbsp;
${dictionnary.start_date}${startDate}
</span> <span class="value end-value"></br>&nbsp;&nbsp;
${dictionnary.end_date}${endDate}
</span></div>
</div>`;
};

let formatter_custom;
const dictionnary = this.props.dictionary.dashboard.boxes.chart;
if (this.props.interval <= 24 * 60) {
formatter_custom = opts => {
const startDate = dayjs(opts.y1)
.locale(this.props.user.language)
.format('LL - LTS');
const endDate = dayjs(opts.y2)
.locale(this.props.user.language)
.format('LL - LTS');

return createTooltipContent(opts, startDate, endDate);
};
} else {
formatter_custom = opts => {
const startDate = dayjs(opts.y1)
.locale(this.props.user.language)
.format('LL');
const endDate = dayjs(opts.y2)
.locale(this.props.user.language)
.format('LL');

return createTooltipContent(opts, startDate, endDate);
};
}
options.tooltip.custom = function(opts) {
return formatter_custom(opts);
};
}
getBarChartOptions = () => {
const options = getApexChartBarOptions({
displayAxes: this.props.display_axes,
Expand Down Expand Up @@ -123,9 +174,32 @@ class ApexChartComponent extends Component {
this.addDateFormatter(options);
return options;
};
getTimelineChartOptions = () => {
let height;
if (this.props.size === 'small' && !this.props.display_axes) {
height = 40;
} else if (this.props.size === 'big' && !this.props.display_axes) {
height = 80;
} else {
// 95 is the height display of the timeline chart when there is no additional height
height = 95 + this.props.additionalHeight;
}
const options = getApexChartTimelineOptions({
height,
colors: mergeArray(this.props.colors, DEFAULT_COLORS),
displayAxes: this.props.display_axes,
series: this.props.series,
locales: [fr, en, de],
defaultLocale: this.props.user.language
});
this.addDateFormatterRangeBar(options);
return options;
};
displayChart = () => {
let options;
if (this.props.chart_type === 'area') {
if (this.props.chart_type === 'timeline') {
options = this.getTimelineChartOptions();
} else if (this.props.chart_type === 'area') {
options = this.getAreaChartOptions();
} else if (this.props.chart_type === 'line') {
options = this.getLineChartOptions();
Expand All @@ -140,6 +214,7 @@ class ApexChartComponent extends Component {
this.chart.updateOptions(options);
} else {
this.chart = new ApexCharts(this.chartRef.current, options);

this.chart.render();
}
};
Expand All @@ -152,7 +227,15 @@ class ApexChartComponent extends Component {
const displayAxesDifferent = nextProps.display_axes !== this.props.display_axes;
const intervalDifferent = nextProps.interval !== this.props.interval;
const sizeDifferent = nextProps.size !== this.props.size;
if (seriesDifferent || chartTypeDifferent || displayAxesDifferent || intervalDifferent || sizeDifferent) {
const additionalHeightDifferent = nextProps.additionalHeight !== this.props.additionalHeight;
if (
seriesDifferent ||
chartTypeDifferent ||
displayAxesDifferent ||
intervalDifferent ||
sizeDifferent ||
additionalHeightDifferent
) {
this.displayChart();
}
}
Expand Down
215 changes: 215 additions & 0 deletions front/src/components/boxs/chart/ApexChartTimelineOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
const addYAxisStyles = () => {
const yAxisLabel = document.querySelectorAll('.apexcharts-yaxis-label');
let fontSize = '12px';
yAxisLabel.forEach(text => {
const title = text.querySelector('title');
if (title) {
const textContent = title.textContent;
let lines = textContent.split('\n');
let countLineBreak = (textContent.match(/\n/g) || []).length;
let marginDy;
if (countLineBreak === 2) {
marginDy = '-1.0em';
} else if (countLineBreak === 1) {
marginDy = '-0.4em';
} else if (countLineBreak === 0) {
marginDy = '0em';
}
text.innerHTML = '';
lines.forEach((line, index) => {
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttribute('x', text.getAttribute('x'));
tspan.setAttribute('dy', index === 0 ? marginDy : '1.2em');
tspan.setAttribute('font-size', fontSize);
tspan.textContent = line;
text.appendChild(tspan);
});
const newTitle = document.createElementNS('http://www.w3.org/2000/svg', 'title');
newTitle.textContent = textContent;
text.appendChild(newTitle);
}
});
};
const limitZoom = chartContext => {
const minZoomRange = 10000;
const globals = chartContext.w.globals;

const maxY = globals.maxY;
const minY = globals.minY;
const currentRange = maxY - minY;
if (currentRange < minZoomRange) {
chartContext.updateOptions(
{
xaxis: {
min: globals.minY,
max: globals.minY + minZoomRange
}
},
false,
false
);
}
};

const getApexChartTimelineOptions = ({ displayAxes, height, series, colors, locales, defaultLocale }) => {
const options = {
series,
chart: {
locales,
defaultLocale,
type: 'rangeBar',
fontFamily: 'inherit',
height,
parentHeightOffset: 0,
sparkline: {
enabled: !displayAxes
},
toolbar: {
show: false
},
animations: {
enabled: false
},
events: {
mounted: addYAxisStyles,
updated: addYAxisStyles,
zoomed: function(chartContext) {
limitZoom(chartContext);
}
}
},
grid: {
strokeDashArray: 4,
padding: {
left: 1
}
},
plotOptions: {
bar: {
horizontal: true,
barHeight: '50%',
rangeBarGroupRows: true
}
},
colors,
fill: {
type: 'solid'
},
xaxis: {
labels: {
padding: 0,
hideOverlappingLabels: true,
datetimeUTC: false,
trim: true,
datetimeFormatter: {
year: 'yyyy',
month: "MMM 'yy",
day: 'dd MMM',
hour: 'HH:mm',
minute: 'HH:mm:ss',
second: 'HH:mm:ss'
}
},
axisBorder: {
show: true
},
type: 'datetime',
min: Math.floor(Math.min(...series.flatMap(s => s.data.map(d => d.y[0])))),
max: Math.floor(Math.max(...series.flatMap(s => s.data.map(d => d.y[1]))))
},
yaxis: {
showAlways: true,
dataLabels: {
enabled: false,
textAnchor: 'start'
},
axisBorder: {
show: true
},
labels: {
align: 'left',
minWidth: 50,
maxWidth: 100,
margin: 5,
formatter: function(value) {
const nbLines = 3;
if (value.length > 15) {
let [deviceName, featureName] = value.split(' (');
if (featureName) {
featureName = featureName.replace(')', '');
}

let result = [];
let currentLine = '';

for (let i = 0; i < deviceName.length; i++) {
currentLine += deviceName[i].replace('-', ' ').replace('_', ' ');
if (currentLine.length >= 15) {
let lastSpaceIndex = currentLine.lastIndexOf(' ');
if (lastSpaceIndex > -1) {
result.push(currentLine.slice(0, lastSpaceIndex).trim());
currentLine = currentLine.slice(lastSpaceIndex + 1);
} else {
result.push(currentLine.trim());
currentLine = '';
}
}
}

if (currentLine.length > 0) {
result.push(currentLine.trim());
}
if (result.length > nbLines && !featureName) {
result = result.slice(0, nbLines);
result[nbLines - 1] += '...';
}
if (result.length > nbLines - 1 && featureName) {
result = result.slice(0, nbLines - 1);
result[nbLines - 2] += '...';
}
deviceName = result.join('\n');

if (featureName) {
return `${deviceName}\n(${featureName})`;
}

return deviceName;
}

return value;
},
offsetX: -15,
offsetY: 0
}
},
legend: {
show: displayAxes,
position: 'bottom',
itemMargin: {
horizontal: 20
}
},
tooltip: {
//theme: 'dark',
marker: {
show: true
},
onDatasetHover: {
highlightDataSeries: false
},
items: {
display: 'flex'
},
fillSeriesColor: false,
fixed: {
enabled: true,
position: 'topLeft',
offsetX: 0,
offsetY: -70
}
}
};
return options;
};

export { getApexChartTimelineOptions };
Loading

0 comments on commit f0a3c12

Please sign in to comment.