diff --git a/addons/survey/static/src/js/survey_session_chart.js b/addons/survey/static/src/js/survey_session_chart.js index 2198afcceee..a4e74555193 100644 --- a/addons/survey/static/src/js/survey_session_chart.js +++ b/addons/survey/static/src/js/survey_session_chart.js @@ -166,6 +166,89 @@ publicWidget.registry.SurveySessionChart = publicWidget.Widget.extend({ } } }, + plugins: [{ + /** + * The way it works is each label is an array of words. + * eg.: if we have a chart label: "this is an example of a label" + * The library will split it as: ["this is an example", "of a label"] + * Each value of the array represents a line of the label. + * So for this example above: it will be displayed as: + * "this is an examble
of a label", breaking the label in 2 parts and put on 2 lines visually. + * + * What we do here is rework the labels with our own algorithm to make them fit better in screen space + * based on breakpoints based on number of columns to display. + * So this example will become: ["this is an", "example of", "a label"] if we have a lot of labels to put in the chart. + * Which will be displayed as "this is an
example of
a label" + * Obviously, the more labels you have, the more columns, and less screen space is available. + * + * We also adapt the font size based on the width available in the chart. + * + * So we counterbalance multiple times: + * - Based on number of columns (i.e. number of survey.question.answer of your current survey.question), + * we split the words of every labels to make them display on more rows. + * - Based on the width of the chart (which is equivalent to screen width), + * we reduce the chart font to be able to fit more characters. + * - Based on the longest word present in the labels, we apply a certain ratio with the width of the chart + * to get a more accurate font size for the space available. + * + * @param {Object} chart + */ + beforeInit: function (chart) { + const nbrCol = chart.data.labels.length; + const minRatio = 0.4; + // Numbers of maximum characters per line to print based on the number of columns and default ratio for the font size + // Between 1 and 2 -> 25, 3 and 4 -> 20, 5 and 6 -> 15, ... + const charPerLineBreakpoints = [ + [1, 2, 25, minRatio], + [3, 4, 20, minRatio], + [5, 6, 15, 0.45], + [7, 8, 10, 0.65], + [9, null, 7, 0.7], + ]; + + let charPerLine; + let fontRatio; + charPerLineBreakpoints.forEach(([lowerBound, upperBound, value, ratio]) => { + if (nbrCol >= lowerBound && (upperBound === null || nbrCol <= upperBound)) { + charPerLine = value; + fontRatio = ratio; + } + }); + + // Adapt font size if the number of characters per line is under the maximum + if (charPerLine < 25) { + const allWords = chart.data.labels.reduce((accumulator, words) => accumulator.concat(' '.concat(words))); + const maxWordLength = Math.max(...allWords.split(' ').map((word) => word.length)); + fontRatio = maxWordLength > charPerLine ? minRatio : fontRatio; + chart.options.scales.xAxes[0].ticks.fontSize = Math.min(parseInt(chart.options.scales.xAxes[0].ticks.fontSize), chart.width * fontRatio / (nbrCol)); + } + + chart.data.labels.forEach(function (label, index, labelsList) { + // Split all the words of the label + const words = label.split(" "); + let resultLines = []; + let currentLine = []; + for (let i = 0; i < words.length; i++) { + // If the word we are adding exceed already the number of characters for the line, we add it anyway before passing to a new line + currentLine.push(words[i]); + + // Continue to add words in the line if there is enough space and if there is at least one more word to add + const nextWord = i+1 < words.length ? words[i+1] : null; + if (nextWord) { + const nextLength = currentLine.join(' ').length + nextWord.length; + if (nextLength <= charPerLine) { + continue; + } + } + // Add the constructed line and reset the variable for the next line + const newLabelLine = currentLine.join(' '); + resultLines.push(newLabelLine); + currentLine = []; + } + labelsList[index] = resultLines; + }); + }, + }], }; }, @@ -176,7 +259,7 @@ publicWidget.registry.SurveySessionChart = publicWidget.Widget.extend({ */ _extractChartLabels: function () { return this.questionStatistics.map(function (point) { - return point.text.length > 20 ? point.text.substring(0, 17) + '...' : point.text; + return point.text; }); },