diff --git a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js index 3eca9b9a018..92f54ff461d 100644 --- a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js +++ b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js @@ -3475,14 +3475,15 @@ createChart(chartData) { const canvas = this.canvas.el; const ctx = canvas.getContext("2d"); + // @ts-ignore this.chart = new window.Chart(ctx, chartData); } updateChartJs(chartRuntime) { const chartData = chartRuntime.chartJsConfig; if (chartData.data && chartData.data.datasets) { this.chart.data = chartData.data; - if (chartData.options?.title) { - this.chart.config.options.title = chartData.options.title; + if (chartData.options?.plugins?.title) { + this.chart.config.options.plugins.title = chartData.options.plugins.title; } if (chartData.options && "valueLabel" in chartData.options) { if (chartData.options?.valueLabel) { @@ -3492,12 +3493,13 @@ } } else { - this.chart.data.datasets = undefined; + this.chart.data.datasets = []; } - this.chart.config.options.tooltips = chartData.options?.tooltips; - this.chart.config.options.legend = chartData.options?.legend; + this.chart.config.options.plugins.tooltip = chartData.options.plugins.tooltip; + this.chart.config.options.plugins.legend = chartData.options.plugins.legend; this.chart.config.options.scales = chartData.options?.scales; - this.chart.update({ duration: 0 }); + // ? + this.chart.update("active"); } } ChartJsComponent.props = { @@ -16179,49 +16181,40 @@ * Get a default chart js configuration */ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) { - return { - type: chart.type, - options: { - // https://www.chartjs.org/docs/latest/general/responsive.html - responsive: true, - maintainAspectRatio: false, - layout: { - padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 }, + const options = { + // https://www.chartjs.org/docs/latest/general/responsive.html + responsive: true, + maintainAspectRatio: false, + layout: { + padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 }, + }, + elements: { + line: { + fill: false, // do not fill the area under line charts }, - elements: { - line: { - fill: false, // do not fill the area under line charts - }, - point: { - hitRadius: 15, // increased hit radius to display point tooltip when hovering nearby - }, + point: { + hitRadius: 15, // increased hit radius to display point tooltip when hovering nearby }, - animation: { - duration: 0, // general animation time - }, - hover: { - animationDuration: 10, // duration of animations when hovering an item - }, - responsiveAnimationDuration: 0, + }, + animation: false, + plugins: { title: { display: !!chart.title, - fontSize: 22, - fontStyle: "normal", text: _t(chart.title), - fontColor, + color: fontColor, + font: { size: 22, weight: "normal" }, }, legend: { // Disable default legend onClick (show/hide dataset), to allow us to set a global onClick on the chart container. // If we want to re-enable this in the future, we need to override the default onClick to stop the event propagation - onClick: undefined, + onClick: () => { }, }, - tooltips: { + tooltip: { callbacks: { - label: function (tooltipItem, data) { - let xLabel = data.datasets?.[tooltipItem.datasetIndex || 0]?.label; - const yLabel = tooltipItem.yLabel !== "" - ? tooltipItem.yLabel - : data.datasets?.[tooltipItem.datasetIndex || 0]?.data?.[tooltipItem.index || 0]; + label: function (tooltipItem) { + let xLabel = tooltipItem.label; + // tooltipItem.parsed.y can be an object or a number for pie charts + const yLabel = tooltipItem.parsed.y ?? tooltipItem.parsed; const yLabelStr = format && typeof yLabel === "number" ? formatValue(yLabel, { format, locale }) : yLabel?.toLocaleString() || ""; @@ -16230,10 +16223,15 @@ }, }, }, + }; + return { + type: chart.type, + options, data: { labels: labels.map(truncateLabel), datasets: [], }, + plugins: [], }; } function getChartLabelFormat(getters, range) { @@ -16430,7 +16428,7 @@ const fontColor = chartFontColor(chart.background); const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat); const legend = { - labels: { fontColor }, + labels: { color: fontColor }, }; if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") { legend.display = false; @@ -16438,42 +16436,35 @@ else { legend.position = chart.legendPosition; } - config.options.legend = { ...config.options?.legend, ...legend }; + config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend }; config.options.layout = { padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 }, }; config.options.scales = { - xAxes: [ - { - ticks: { - // x axis configuration - maxRotation: 60, - minRotation: 15, - padding: 5, - labelOffset: 2, - fontColor, + x: { + ticks: { + padding: 5, + color: fontColor, + }, + }, + y: { + position: chart.verticalAxisPosition, + beginAtZero: true, + ticks: { + color: fontColor, + callback: (value) => { + return localeFormat.format + ? formatValue(value, localeFormat) + : value?.toLocaleString() || value; }, }, - ], - yAxes: [ - { - position: chart.verticalAxisPosition, - ticks: { - fontColor, - // y axis configuration - beginAtZero: true, - callback: (value) => { - return localeFormat.format - ? formatValue(value, localeFormat) - : value?.toLocaleString() || value; - }, - }, - }, - ], + }, }; if (chart.stacked) { - config.options.scales.xAxes[0].stacked = true; - config.options.scales.yAxes[0].stacked = true; + // @ts-ignore chart.js type is broken + config.options.scales.x.stacked = true; + // @ts-ignore chart.js type is broken + config.options.scales.y.stacked = true; } return config; } @@ -16677,17 +16668,18 @@ padding: { left: 30, right: 30, top: chart.title ? 10 : 25, bottom: 25 }, }; config.options.needle = { - radiusPercentage: 2, - widthPercentage: 3.2, - lengthPercentage: 80, - color: "#000000", + width: 10, + borderColor: "#000000", + backgroundColor: "#000000", }; config.options.valueLabel = { display: false, - formatter: null, - color: "#FFFFFF", + font: { + size: 30, + color: "#FFFFFF", + }, backgroundColor: "#000000", - fontSize: 30, + borderColor: "#000000", borderRadius: 5, padding: { top: 5, @@ -16695,7 +16687,6 @@ bottom: 5, left: 5, }, - bottomMarginPercentage: 5, }; return config; } @@ -16737,16 +16728,12 @@ data.push(point.value); backgroundColor.push(point.color); }); - // There's a bug in gauge lib when the last element in `data` is 0 (i.e. when the range maximum is 0). - // The value wrongly fallbacks to 1 because 0 is falsy - // See https://github.com/haiiaaa/chartjs-gauge/pull/33 - // https://github.com/haiiaaa/chartjs-gauge/blob/2ea50541d754d710cb30c2502fa690ac5dc27afd/src/controllers/controller.gauge.js#L52 data.push(maxNeedleValue); backgroundColor.push(colors.upperColor); const dataRange = chart.dataRange; const deltaBeyondRangeLimit = needleCoverage / 30; let needleValue = minNeedleValue - deltaBeyondRangeLimit; // make needle value always at the minimum by default - let cellFormatter = null; + let cellFormatter = undefined; let displayValue = false; if (dataRange !== undefined) { const cell = getters.getEvaluatedCell({ @@ -16761,6 +16748,7 @@ // smaller and slightly larger than minRange and maxRange to mark the fact // that the needle is out of the range limits needleValue = clip(cell.value, minNeedleValue - deltaBeyondRangeLimit, maxNeedleValue + deltaBeyondRangeLimit); + // show the original value, not the clipped one cellFormatter = () => getters.getRangeFormattedValues(dataRange)[0]; displayValue = true; } @@ -16808,31 +16796,29 @@ }, }; /** - * Regex to test if a format string is a date format that can be translated into a moment time format + * Regex to test if a format string is a date format that can be translated into a luxon time format */ - const timeFormatMomentCompatible = /^((d|dd|m|mm|yyyy|yy|hh|h|ss|a)(-|:|\s|\/))*(d|dd|m|mm|yyyy|yy|hh|h|ss|a)$/i; + const timeFormatLuxonCompatible = /^((d|dd|m|mm|yyyy|yy|hh|h|ss|a)(-|:|\s|\/))*(d|dd|m|mm|yyyy|yy|hh|h|ss|a)$/i; /** Get the time options for the XAxis of ChartJS */ function getChartTimeOptions(labels, labelFormat, locale) { - const momentFormat = convertDateFormatForMoment(labelFormat); - const timeUnit = getBestTimeUnitForScale(labels, momentFormat, locale); + const luxonFormat = convertDateFormatForLuxon(labelFormat); + const timeUnit = getBestTimeUnitForScale(labels, luxonFormat, locale); const displayFormats = {}; if (timeUnit) { - displayFormats[timeUnit] = momentFormat; + displayFormats[timeUnit] = luxonFormat; } return { - parser: momentFormat, + parser: luxonFormat, displayFormats, - unit: timeUnit, + unit: timeUnit ?? false, }; } /** * Convert the given date format into a format that moment.js understands. * - * https://momentjs.com/docs/#/parsing/string-format/ + * https://github.com/moment/luxon/blob/master/docs/formatting.md#table-of-tokens */ - function convertDateFormatForMoment(format) { - format = format.replace(/y/g, "Y"); - format = format.replace(/d/g, "D"); + function convertDateFormatForLuxon(format) { // "m" before "h" === month, "m" after "h" === minute const indexH = format.indexOf("h"); if (indexH >= 0) { @@ -16903,6 +16889,8 @@ return "year"; } + // @ts-ignore + const Chart = window.Chart; class LineChart extends AbstractChart { dataSets; labelRange; @@ -17032,7 +17020,7 @@ return canBeDateChart(labelRange, getters) || canBeLinearChart(labelRange, getters); } function getChartAxisType(chart, getters) { - if (isDateChart(chart, getters)) { + if (isDateChart(chart, getters) && isLuxonTimeAdapterInstalled()) { return "time"; } if (isLinearChart(chart, getters)) { @@ -17055,7 +17043,7 @@ col: labelRange.zone.left, row: labelRange.zone.top, }).format; - return Boolean(labelFormat && timeFormatMomentCompatible.test(labelFormat)); + return Boolean(labelFormat && timeFormatLuxonCompatible.test(labelFormat)); } function canBeLinearChart(labelRange, getters) { if (!labelRange) { @@ -17075,10 +17063,11 @@ const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat); const legend = { labels: { - fontColor, + color: fontColor, generateLabels(chart) { + // color the legend labels with the dataset color, without any transparency const { data } = chart; - const labels = window.Chart.defaults.global.legend.labels.generateLabels(chart); + const labels = Chart.defaults.plugins.legend.labels.generateLabels(chart); for (const [index, label] of labels.entries()) { label.fillStyle = data.datasets[index].borderColor; } @@ -17092,41 +17081,33 @@ else { legend.position = chart.legendPosition; } - config.options.legend = { ...config.options?.legend, ...legend }; + Object.assign(config.options.plugins.legend || {}, legend); config.options.layout = { padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 }, }; config.options.scales = { - xAxes: [ - { - ticks: { - // x axis configuration - maxRotation: 60, - minRotation: 15, - padding: 5, - labelOffset: 2, - fontColor, + x: { + ticks: { + padding: 5, + color: fontColor, + }, + }, + y: { + position: chart.verticalAxisPosition, + beginAtZero: true, + ticks: { + color: fontColor, + callback: (value) => { + return localeFormat.format + ? formatValue(value, localeFormat) + : value?.toLocaleString() || value; }, }, - ], - yAxes: [ - { - position: chart.verticalAxisPosition, - ticks: { - fontColor, - // y axis configuration - beginAtZero: true, - callback: (value) => { - return localeFormat.format - ? formatValue(value, localeFormat) - : value?.toLocaleString() || value; - }, - }, - }, - ], + }, }; - if (chart.stacked) { - config.options.scales.yAxes[0].stacked = true; + if (chart.stacked && config.options?.scales?.y) { + // @ts-ignore chart.js type is wrong + config.options.scales.y.stacked = true; } return config; } @@ -17153,15 +17134,18 @@ const config = getLineConfiguration(chart, labels, localeFormat); const labelFormat = getChartLabelFormat(getters, chart.labelRange); if (axisType === "time") { - config.options.scales.xAxes[0].type = "time"; - config.options.scales.xAxes[0].time = getChartTimeOptions(labels, labelFormat, locale); - config.options.scales.xAxes[0].ticks.maxTicksLimit = 15; + const axis = { + type: "time", + time: getChartTimeOptions(labels, labelFormat, locale), + }; + Object.assign(config.options.scales.x, axis); + config.options.scales.x.ticks.maxTicksLimit = 15; } else if (axisType === "linear") { - config.options.scales.xAxes[0].type = "linear"; - config.options.scales.xAxes[0].ticks.callback = (value) => formatValue(value, localeFormat); - config.options.tooltips.callbacks.title = (tooltipItem) => { - return formatValue(tooltipItem[0]?.xLabel || "", localeFormat); + config.options.scales.x.type = "linear"; + config.options.scales.x.ticks.callback = (value) => formatValue(value, { format: labelFormat, locale }); + config.options.plugins.tooltip.callbacks.title = (tooltipItem) => { + return formatValue(tooltipItem[0]?.label || "", localeFormat); }; } const colors = new ChartColors(); @@ -17179,7 +17163,7 @@ const dataset = { label, data, - lineTension: 0, + tension: 0, borderColor: color, backgroundColor, pointBackgroundColor: color, @@ -17189,6 +17173,22 @@ } return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR }; } + let missingTimeAdapterAlreadyWarned = false; + function isLuxonTimeAdapterInstalled() { + // @ts-ignore + if (!window.Chart) { + return false; + } + // @ts-ignore + const adapter = new window.Chart._adapters._date({}); + // @ts-ignore + const isInstalled = adapter._id === "luxon"; + if (!isInstalled && !missingTimeAdapterAlreadyWarned) { + missingTimeAdapterAlreadyWarned = true; + console.warn("'chartjs-adapter-luxon' time adapter is not installed. Time scale axes are disabled."); + } + return isInstalled; + } class PieChart extends AbstractChart { dataSets; @@ -17292,7 +17292,7 @@ const fontColor = chartFontColor(chart.background); const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat); const legend = { - labels: { fontColor }, + labels: { color: fontColor }, }; if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") { legend.display = false; @@ -17300,12 +17300,12 @@ else { legend.position = chart.legendPosition; } - config.options.legend = { ...config.options?.legend, ...legend }; + Object.assign(config.options.plugins.legend || {}, legend); config.options.layout = { padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 }, }; - config.options.tooltips.callbacks.title = function (tooltipItems, data) { - return data.datasets[tooltipItems[0].datasetIndex].label; + config.options.plugins.tooltip.callbacks.title = function (tooltipItems) { + return tooltipItems[0].dataset.label; }; return config; } @@ -18069,6 +18069,278 @@ }; } + // @ts-ignore + if (window.Chart) { + // @ts-ignore + const DoughnutController = window.Chart?.DoughnutController; + /** + * Example : + * const chart = new Chart(ctx, { + * type: "gauge", + * data: { + * datasets: [ + * { + * borderWidth: 5, + * data: [10, 20, 10], + * value: 23, + * backgroundColor: [ + * "#dc3545", //red + * "#ffc107", //orange + * "#28a745", //green + * ], + * }, + * ], + * }, + * options: { + * plugins: { + * title: { + * display: true, + * text: "Custom Chart Title", + * padding: { + * top: 10, + * bottom: 30, + * }, + * font : { + * size : 30 + * } + * }, + * }, + * valueLabel: { + * display: true, + * format: (value) => { + * return value.toFixed(2) + "%"; + * }, + * font : { + * size : 35, + * family : "Arial", + * color : "#FC0", + * }, + * backgroundColor : "#ccc", + * borderColor : "#090", + * borderRadius : 50 + * }, + * needle: { + * display: true, + * width: 50, + * color: "#DC5", + * backgroundColor : "#ccc", + * }, + * }, + * }); + */ + class GaugeController extends DoughnutController { + static id = "gauge"; + static defaults = { + ...DoughnutController.defaults, + circumference: 180, + rotation: 270, + }; + static overrides = { + aspectRatio: 2, + plugins: { + legend: { + display: false, + }, + tooltip: { + enabled: false, + }, + }, + }; + get chartHeight() { + return Math.abs(this.chart.chartArea.top - this.chart.chartArea.bottom); + } + get chartWidth() { + return Math.abs(this.chart.chartArea.left - this.chart.chartArea.right); + } + get needleOptions() { + const { config } = this.chart; + const options = config.options; + // @ts-ignore + return options?.needle || {}; + } + get valueLabelOptions() { + const { config } = this.chart; + const options = config.options; + // @ts-ignore + return options?.valueLabel || {}; + } + get minValue() { + const dataset = this.getDataset(); + // @ts-ignore + return dataset.minValue || 0; + } + getValueAngleInPercent(value) { + const dataset = this.getDataset(); + const data = dataset.data || []; + const max = Math.max(...data); + const min = this.minValue; + return (value - min) / (max - min); + } + drawValueLabel(params) { + const { ctx } = this.chart; + if (this.valueLabelOptions.display === false) { + return; + } + // draw background rectangle + ctx.save(); + ctx.beginPath(); + ctx.fillStyle = params.valueLabel.backgroundColor; + ctx.strokeStyle = params.valueLabel.borderColor; + ctx.roundRect(params.valueLabel.rect.x, params.valueLabel.rect.y, params.valueLabel.rect.width, params.valueLabel.rect.height, params.valueLabel.borderRadius); + ctx.stroke(); + ctx.fill(); + // draw value text + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = params.valueLabel.font; + ctx.fillStyle = params.valueLabel.textColor; + ctx.fillText(params.valueLabel.valueText, params.valueLabel.textPosition.x, params.valueLabel.textPosition.y); + ctx.restore(); + } + drawNeedle(params) { + const { ctx } = this.chart; + if (this.needleOptions.display === false) { + return; + } + // translate & rotate next paths + ctx.save(); + ctx.translate(params.needle.position.x, params.needle.position.y); + ctx.rotate(-Math.PI / 2 + Math.PI * params.needle.percent); + // draw circle + ctx.beginPath(); + ctx.fillStyle = params.needle.backgroundColor; + ctx.strokeStyle = params.needle.borderColor; + ctx.ellipse(0, 0, params.needle.width / 2, params.needle.width / 2, 0, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.closePath(); + // draw needle + ctx.fillStyle = params.needle.backgroundColor; + ctx.strokeStyle = params.needle.borderColor; + ctx.beginPath(); + ctx.moveTo(-params.needle.width / 2, 0); + ctx.lineTo(0, -params.needle.height + 10); + ctx.lineTo(+params.needle.width / 2, 0); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + ctx.restore(); + } + computeRenderingParams() { + const { ctx, config } = this.chart; + const options = config.options; + options.layout = options.layout || {}; + options.layout.padding = options.layout.padding || {}; + const formatter = this.valueLabelOptions.formatter; + const fmt = typeof formatter === "function" ? formatter : (value) => value; + const dataset = this.getDataset(); + // @ts-ignore value is a custom property for gauge charts + const value = dataset.value || 0; + const valueText = fmt(value).toString(); + const percent = this.getValueAngleInPercent(value); + const fontFamily = this.valueLabelOptions.font?.family || "Arial"; + const optionsFontSize = this.valueLabelOptions.font?.size || 0; + const fontSize = optionsFontSize >= 1 ? optionsFontSize : 30; + const font = `${fontSize}px ${fontFamily}`; + const padding = { + right: 0, + left: 0, + top: 0, + bottom: 0, + ...this.valueLabelOptions.padding, + }; + padding.left = padding.left >= 0 ? padding.left : 10; + padding.right = padding.right >= 0 ? padding.right : 10; + padding.top = padding.top >= 0 ? padding.top : 10; + padding.bottom = padding.bottom >= 0 ? padding.bottom : 10; + const chartArea = this.chart.chartArea; + const offsetX = this.offsetX || 0; + const offsetY = this.offsetY || 0; + const centerX = (chartArea.left + chartArea.right) / 2; + const centerY = (chartArea.top + chartArea.bottom) / 2; + const center = { + x: centerX + offsetX, + y: centerY + offsetY, + }; + const textPosition = { + x: center.x, + y: center.y, + }; + ctx.save(); + ctx.font = font; + const metrics = ctx.measureText(valueText); + ctx.restore(); + const textHeight = metrics.fontBoundingBoxAscent - metrics.fontBoundingBoxDescent; + return { + needle: { + borderColor: this.needleOptions.borderColor || "#000", + backgroundColor: this.needleOptions.backgroundColor || "#000", + width: this.needleOptions.width || 10, + height: Math.min(this.chartHeight, this.chartWidth / 2), + position: { + x: textPosition.x, + y: textPosition.y, + }, + percent, + }, + valueLabel: { + textColor: this.valueLabelOptions.font?.color || "#FFF", + backgroundColor: this.valueLabelOptions.backgroundColor || "#000", + borderColor: this.valueLabelOptions.borderColor || "#000", + borderRadius: this.valueLabelOptions.borderRadius || 10, + rect: { + x: center.x - metrics.width / 2 - padding.left, + y: center.y + textHeight / 2 + padding.top, + width: metrics.width + padding.left + padding.right, + height: -(textHeight + 6 + padding.top + padding.bottom), + }, + valueText, + textPosition, + textHeight, + font, + }, + }; + } + draw() { + super.draw(); + const params = this.computeRenderingParams(); + this.drawNeedle(params); + this.drawValueLabel(params); + } + updateElements(elements, start, count, mode) { + super.updateElements(elements, start, count, mode); + const dataset = this.getDataset(); + const data = this.getDataset().data; + // @ts-ignore + const minValue = dataset.minValue; + // @ts-ignore + const rotation = this.chart.options.rotation || 0; + // @ts-ignore + const circumference = this.chart.options.circumference || 0; + for (let arcIndex = 0; arcIndex < data.length; arcIndex++) { + const previousValue = arcIndex === 0 ? minValue : data[arcIndex - 1]; + const startAngleInPercent = this.getValueAngleInPercent(previousValue); + const endAngleInPercent = this.getValueAngleInPercent(data[arcIndex]); + const startAngle = degreesToRadians(rotation + circumference * startAngleInPercent) - Math.PI / 2; + const endAngle = degreesToRadians(rotation + circumference * endAngleInPercent) - Math.PI / 2; + const arcCircumference = endAngle - startAngle; + const arc = elements[arcIndex]; + const propertiesUpdates = { + startAngle, + endAngle, + circumference: arcCircumference, + }; + this.updateElement(arc, arcIndex, propertiesUpdates, mode); + } + } + } + function degreesToRadians(degrees) { + return (degrees * Math.PI) / 180; + } + // @ts-ignore + window.Chart.register(GaugeController); + } + //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ @@ -36418,7 +36690,8 @@ if (!chart) { throw new Error(`No chart for the given id: ${figureId}`); } - this.history.update("charts", figureId, this.createRuntimeChart(chart)); + const runtime = this.createRuntimeChart(chart); + this.history.update("charts", figureId, runtime); } return this.charts[figureId]; } @@ -49506,7 +49779,14 @@ `; } - const TABLE_DEFAULT_STYLE = escapeXml /*xml*/ ``; + const TABLE_DEFAULT_ATTRS = [ + ["name", "TableStyleLight8"], + ["showFirstColumn", "0"], + ["showLastColumn", "0"], + ["showRowStripes", "0"], + ["showColumnStripes", "0"], + ]; + const TABLE_DEFAULT_STYLE = escapeXml /*xml*/ ``; function createTable(table, tableId, sheetData) { const tableAttributes = [ ["id", tableId], @@ -50625,9 +50905,9 @@ Object.defineProperty(exports, '__esModule', { value: true }); - __info__.version = '16.5.0-alpha.3'; - __info__.date = '2023-08-09T12:01:15.162Z'; - __info__.hash = 'b0e8fd5'; + __info__.version = '16.5.0-alpha.4'; + __info__.date = '2023-08-10T09:44:45.710Z'; + __info__.hash = 'c8db018'; })(this.o_spreadsheet = this.o_spreadsheet || {}, owl); diff --git a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml index 2691fa8d62e..bde7ae6acf1 100644 --- a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml +++ b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml @@ -3951,7 +3951,7 @@