@odoo/o-spreadsheet 18.2.0-alpha.1 → 18.2.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 18.2.0-alpha.1
6
- * @date 2025-01-14T11:35:51.135Z
7
- * @hash 702f816
5
+ * @version 18.2.0-alpha.3
6
+ * @date 2025-01-27T10:07:28.716Z
7
+ * @hash 63a13e5
8
8
  */
9
9
 
10
10
  'use strict';
@@ -4391,15 +4391,18 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4391
4391
  let currentIndex;
4392
4392
  let currentVal;
4393
4393
  let currentType;
4394
+ const getValue = sortOrder === "desc"
4395
+ ? (i) => normalizeValue(getValueInData(data, rangeLength - i - 1))
4396
+ : (i) => normalizeValue(getValueInData(data, i));
4394
4397
  while (indexRight - indexLeft >= 0) {
4395
4398
  indexMedian = Math.floor((indexLeft + indexRight) / 2);
4396
4399
  currentIndex = indexMedian;
4397
- currentVal = normalizeValue(getValueInData(data, currentIndex));
4400
+ currentVal = getValue(currentIndex);
4398
4401
  currentType = typeof currentVal;
4399
4402
  // 1 - linear search to find value with the same type
4400
4403
  while (indexLeft < currentIndex && targetType !== currentType) {
4401
4404
  currentIndex--;
4402
- currentVal = normalizeValue(getValueInData(data, currentIndex));
4405
+ currentVal = getValue(currentIndex);
4403
4406
  currentType = typeof currentVal;
4404
4407
  }
4405
4408
  if (currentType !== targetType || currentVal === undefined || currentVal === null) {
@@ -4415,8 +4418,7 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4415
4418
  if (matchVal === undefined ||
4416
4419
  matchVal === null ||
4417
4420
  matchVal < currentVal ||
4418
- (matchVal === currentVal && sortOrder === "asc" && matchValIndex < currentIndex) ||
4419
- (matchVal === currentVal && sortOrder === "desc" && matchValIndex > currentIndex)) {
4421
+ (matchVal === currentVal && matchValIndex < currentIndex)) {
4420
4422
  matchVal = currentVal;
4421
4423
  matchValIndex = currentIndex;
4422
4424
  }
@@ -4424,15 +4426,13 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4424
4426
  else if (mode === "nextGreater" && currentVal >= _target) {
4425
4427
  if (matchVal === undefined ||
4426
4428
  matchVal > currentVal ||
4427
- (matchVal === currentVal && sortOrder === "asc" && matchValIndex < currentIndex) ||
4428
- (matchVal === currentVal && sortOrder === "desc" && matchValIndex > currentIndex)) {
4429
+ (matchVal === currentVal && matchValIndex < currentIndex)) {
4429
4430
  matchVal = currentVal;
4430
4431
  matchValIndex = currentIndex;
4431
4432
  }
4432
4433
  }
4433
4434
  // 3 - give new indexes for the Binary search
4434
- if ((sortOrder === "asc" && currentVal > _target) ||
4435
- (sortOrder === "desc" && currentVal <= _target)) {
4435
+ if (currentVal > _target || (mode === "strict" && currentVal === _target)) {
4436
4436
  indexRight = currentIndex - 1;
4437
4437
  }
4438
4438
  else {
@@ -4440,7 +4440,10 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4440
4440
  }
4441
4441
  }
4442
4442
  // note that valMinIndex could be 0
4443
- return matchValIndex !== undefined ? matchValIndex : -1;
4443
+ if (matchValIndex === undefined) {
4444
+ return -1;
4445
+ }
4446
+ return sortOrder === "desc" ? rangeLength - matchValIndex - 1 : matchValIndex;
4444
4447
  }
4445
4448
  /**
4446
4449
  * Perform a linear search and return the index of the match.
@@ -8005,6 +8008,7 @@ const AGGREGATORS_BY_FIELD_TYPE = {
8005
8008
  integer: NUMBER_CHAR_AGGREGATORS,
8006
8009
  char: NUMBER_CHAR_AGGREGATORS,
8007
8010
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8011
+ datetime: ["max", "min", "count_distinct", "count"],
8008
8012
  };
8009
8013
  const AGGREGATORS = {};
8010
8014
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -9810,13 +9814,13 @@ function toExcelDataset(getters, ds) {
9810
9814
  else if (ds.labelCell) {
9811
9815
  label = {
9812
9816
  reference: getters.getRangeString(ds.labelCell, "forceSheetReference", {
9813
- useFixedReference: true,
9817
+ useBoundedReference: true,
9814
9818
  }),
9815
9819
  };
9816
9820
  }
9817
9821
  return {
9818
9822
  label,
9819
- range: getters.getRangeString(dataRange, "forceSheetReference", { useFixedReference: true }),
9823
+ range: getters.getRangeString(dataRange, "forceSheetReference", { useBoundedReference: true }),
9820
9824
  backgroundColor: ds.backgroundColor,
9821
9825
  rightYAxis: ds.rightYAxis,
9822
9826
  };
@@ -9831,7 +9835,7 @@ function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
9831
9835
  zone.top = zone.top + 1;
9832
9836
  }
9833
9837
  const range = labelRange.clone({ zone });
9834
- return getters.getRangeString(range, "forceSheetReference", { useFixedReference: true });
9838
+ return getters.getRangeString(range, "forceSheetReference", { useBoundedReference: true });
9835
9839
  }
9836
9840
  /**
9837
9841
  * Transform a chart definition which supports dataSets (dataSets and LabelRange)
@@ -29581,11 +29585,14 @@ const templates = /* xml */ `
29581
29585
  </t>
29582
29586
  </templates>
29583
29587
  `;
29584
- const app = new owl.App(owl.Component, { templates, translateFn: _t });
29588
+ let app;
29585
29589
  function renderToString(templateName, context = {}) {
29586
29590
  return render(templateName, context).innerHTML;
29587
29591
  }
29588
29592
  function render(templateName, context = {}) {
29593
+ if (!app) {
29594
+ app = new owl.App(owl.Component, { templates, translateFn: _t });
29595
+ }
29589
29596
  const templateFn = app.getTemplate(templateName);
29590
29597
  const bdom = templateFn(context, {});
29591
29598
  const div = document.createElement("div");
@@ -29814,6 +29821,7 @@ var CHART_RUNTIME_HELPERS = /*#__PURE__*/Object.freeze({
29814
29821
  getBarChartLegend: getBarChartLegend,
29815
29822
  getBarChartScales: getBarChartScales,
29816
29823
  getBarChartTooltip: getBarChartTooltip,
29824
+ getChartColorsGenerator: getChartColorsGenerator,
29817
29825
  getChartLabelFormat: getChartLabelFormat,
29818
29826
  getChartLayout: getChartLayout,
29819
29827
  getChartShowValues: getChartShowValues,
@@ -32346,6 +32354,10 @@ class Popover extends owl.Component {
32346
32354
  this.currentDisplayValue = newDisplay;
32347
32355
  if (!anchor)
32348
32356
  return;
32357
+ el.style.top = "";
32358
+ el.style.left = "";
32359
+ el.style["max-height"] = "";
32360
+ el.style["max-width"] = "";
32349
32361
  const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
32350
32362
  let elDims = {
32351
32363
  width: el.getBoundingClientRect().width,
@@ -36982,6 +36994,440 @@ class Section extends owl.Component {
36982
36994
  };
36983
36995
  }
36984
36996
 
36997
+ /**
36998
+ * Start listening to pointer events and apply the given callbacks.
36999
+ *
37000
+ * @returns A function to remove the listeners.
37001
+ */
37002
+ function startDnd(onMouseMove, onMouseUp, onMouseDown = () => { }) {
37003
+ const removeListeners = () => {
37004
+ window.removeEventListener("pointerdown", onMouseDown);
37005
+ window.removeEventListener("pointerup", _onMouseUp);
37006
+ window.removeEventListener("dragstart", _onDragStart);
37007
+ window.removeEventListener("pointermove", onMouseMove);
37008
+ window.removeEventListener("wheel", onMouseMove);
37009
+ };
37010
+ const _onMouseUp = (ev) => {
37011
+ onMouseUp(ev);
37012
+ removeListeners();
37013
+ };
37014
+ function _onDragStart(ev) {
37015
+ ev.preventDefault();
37016
+ }
37017
+ window.addEventListener("pointerdown", onMouseDown);
37018
+ window.addEventListener("pointerup", _onMouseUp);
37019
+ window.addEventListener("dragstart", _onDragStart);
37020
+ window.addEventListener("pointermove", onMouseMove);
37021
+ // mouse wheel on window is by default a passive event.
37022
+ // preventDefault() is not allowed in passive event handler.
37023
+ // https://chromestatus.com/feature/6662647093133312
37024
+ window.addEventListener("wheel", onMouseMove, { passive: false });
37025
+ return removeListeners;
37026
+ }
37027
+ /**
37028
+ * Function to be used during a pointerdown event, this function allows to
37029
+ * perform actions related to the pointermove and pointerup events and adjusts the viewport
37030
+ * when the new position related to the pointermove event is outside of it.
37031
+ * Among inputs are two callback functions. First intended for actions performed during
37032
+ * the pointermove event, it receives as parameters the current position of the pointermove
37033
+ * (occurrence of the current column and the current row). Second intended for actions
37034
+ * performed during the pointerup event.
37035
+ */
37036
+ function dragAndDropBeyondTheViewport(env, cbMouseMove, cbMouseUp, only = false) {
37037
+ let timeOutId = null;
37038
+ let currentEv;
37039
+ let previousEv;
37040
+ let startingEv;
37041
+ let startingX;
37042
+ let startingY;
37043
+ const getters = env.model.getters;
37044
+ const sheetId = getters.getActiveSheetId();
37045
+ const position = gridOverlayPosition();
37046
+ let colIndex;
37047
+ let rowIndex;
37048
+ const onMouseDown = (ev) => {
37049
+ previousEv = ev;
37050
+ startingEv = ev;
37051
+ startingX = startingEv.clientX - position.left;
37052
+ startingY = startingEv.clientY - position.top;
37053
+ };
37054
+ const onMouseMove = (ev) => {
37055
+ currentEv = ev;
37056
+ if (timeOutId) {
37057
+ return;
37058
+ }
37059
+ const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates();
37060
+ let { top, left, bottom, right } = getters.getActiveMainViewport();
37061
+ let { scrollX, scrollY } = getters.getActiveSheetDOMScrollInfo();
37062
+ const { xSplit, ySplit } = getters.getPaneDivisions(sheetId);
37063
+ let canEdgeScroll = false;
37064
+ let timeoutDelay = MAX_DELAY;
37065
+ const x = currentEv.clientX - position.left;
37066
+ colIndex = getters.getColIndex(x);
37067
+ if (only !== "vertical") {
37068
+ const previousX = previousEv.clientX - position.left;
37069
+ const edgeScrollInfoX = getters.getEdgeScrollCol(x, previousX, startingX);
37070
+ if (edgeScrollInfoX.canEdgeScroll) {
37071
+ canEdgeScroll = true;
37072
+ timeoutDelay = Math.min(timeoutDelay, edgeScrollInfoX.delay);
37073
+ let newTarget;
37074
+ switch (edgeScrollInfoX.direction) {
37075
+ case "reset":
37076
+ colIndex = xSplit;
37077
+ newTarget = xSplit;
37078
+ break;
37079
+ case 1:
37080
+ colIndex = right;
37081
+ newTarget = left + 1;
37082
+ break;
37083
+ case -1:
37084
+ colIndex = left - 1;
37085
+ while (env.model.getters.isColHidden(sheetId, colIndex)) {
37086
+ colIndex--;
37087
+ }
37088
+ newTarget = colIndex;
37089
+ break;
37090
+ }
37091
+ scrollX = getters.getColDimensions(sheetId, newTarget).start - offsetCorrectionX;
37092
+ }
37093
+ }
37094
+ const y = currentEv.clientY - position.top;
37095
+ rowIndex = getters.getRowIndex(y);
37096
+ if (only !== "horizontal") {
37097
+ const previousY = previousEv.clientY - position.top;
37098
+ const edgeScrollInfoY = getters.getEdgeScrollRow(y, previousY, startingY);
37099
+ if (edgeScrollInfoY.canEdgeScroll) {
37100
+ canEdgeScroll = true;
37101
+ timeoutDelay = Math.min(timeoutDelay, edgeScrollInfoY.delay);
37102
+ let newTarget;
37103
+ switch (edgeScrollInfoY.direction) {
37104
+ case "reset":
37105
+ rowIndex = ySplit;
37106
+ newTarget = ySplit;
37107
+ break;
37108
+ case 1:
37109
+ rowIndex = bottom;
37110
+ newTarget = top + edgeScrollInfoY.direction;
37111
+ break;
37112
+ case -1:
37113
+ rowIndex = top - 1;
37114
+ while (env.model.getters.isRowHidden(sheetId, rowIndex)) {
37115
+ rowIndex--;
37116
+ }
37117
+ newTarget = rowIndex;
37118
+ break;
37119
+ }
37120
+ scrollY = env.model.getters.getRowDimensions(sheetId, newTarget).start - offsetCorrectionY;
37121
+ }
37122
+ }
37123
+ if (!canEdgeScroll) {
37124
+ if (rowIndex === -1) {
37125
+ rowIndex = y < 0 ? 0 : getters.getNumberRows(sheetId) - 1;
37126
+ }
37127
+ if (colIndex === -1 && x < 0) {
37128
+ colIndex = x < 0 ? 0 : getters.getNumberCols(sheetId) - 1;
37129
+ }
37130
+ }
37131
+ cbMouseMove(colIndex, rowIndex, currentEv);
37132
+ if (canEdgeScroll) {
37133
+ env.model.dispatch("SET_VIEWPORT_OFFSET", { offsetX: scrollX, offsetY: scrollY });
37134
+ timeOutId = setTimeout(() => {
37135
+ timeOutId = null;
37136
+ onMouseMove(currentEv);
37137
+ }, Math.round(timeoutDelay));
37138
+ }
37139
+ previousEv = currentEv;
37140
+ };
37141
+ const onMouseUp = () => {
37142
+ clearTimeout(timeOutId);
37143
+ cbMouseUp();
37144
+ };
37145
+ startDnd(onMouseMove, onMouseUp, onMouseDown);
37146
+ }
37147
+
37148
+ function useDragAndDropListItems() {
37149
+ let dndHelper;
37150
+ const previousCursor = document.body.style.cursor;
37151
+ let cleanupFns = [];
37152
+ const cleanUp = () => {
37153
+ dndHelper = undefined;
37154
+ document.body.style.cursor = previousCursor;
37155
+ cleanupFns.forEach((fn) => fn());
37156
+ cleanupFns = [];
37157
+ };
37158
+ const start = (direction, args) => {
37159
+ const onChange = () => {
37160
+ document.body.style.cursor = "move";
37161
+ if (!dndHelper)
37162
+ return;
37163
+ Object.assign(state.itemsStyle, dndHelper.getItemStyles());
37164
+ args.onChange?.();
37165
+ };
37166
+ state.cancel = () => {
37167
+ state.draggedItemId = undefined;
37168
+ state.itemsStyle = {};
37169
+ document.body.style.cursor = previousCursor;
37170
+ args.onCancel?.();
37171
+ cleanUp();
37172
+ };
37173
+ const onDragEnd = (itemId, indexAtEnd) => {
37174
+ state.draggedItemId = undefined;
37175
+ state.itemsStyle = {};
37176
+ document.body.style.cursor = previousCursor;
37177
+ args.onDragEnd?.(itemId, indexAtEnd);
37178
+ cleanUp();
37179
+ };
37180
+ document.body.style.cursor = "move";
37181
+ state.draggedItemId = args.draggedItemId;
37182
+ const container = direction === "horizontal"
37183
+ ? new HorizontalContainer(args.containerEl)
37184
+ : new VerticalContainer(args.containerEl);
37185
+ dndHelper = new DOMDndHelper({
37186
+ ...args,
37187
+ container,
37188
+ onChange,
37189
+ onDragEnd,
37190
+ onCancel: state.cancel,
37191
+ });
37192
+ const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
37193
+ cleanupFns.push(stopListening);
37194
+ const onScroll = dndHelper.onScroll.bind(dndHelper);
37195
+ args.containerEl.addEventListener("scroll", onScroll);
37196
+ cleanupFns.push(() => args.containerEl.removeEventListener("scroll", onScroll));
37197
+ cleanupFns.push(dndHelper.destroy.bind(dndHelper));
37198
+ };
37199
+ owl.onWillUnmount(() => {
37200
+ cleanUp();
37201
+ });
37202
+ const state = owl.useState({
37203
+ itemsStyle: {},
37204
+ draggedItemId: undefined,
37205
+ start,
37206
+ cancel: () => { },
37207
+ });
37208
+ return state;
37209
+ }
37210
+ class DOMDndHelper {
37211
+ draggedItemId;
37212
+ items;
37213
+ container;
37214
+ initialMousePosition;
37215
+ currentMousePosition;
37216
+ initialScroll;
37217
+ minPosition;
37218
+ maxPosition;
37219
+ edgeScrollIntervalId;
37220
+ onChange;
37221
+ onCancel;
37222
+ onDragEnd;
37223
+ /**
37224
+ * The dead zone is an area in which the pointermove events are ignored.
37225
+ *
37226
+ * This is useful when swapping the dragged item with a larger item. After the swap,
37227
+ * the mouse is still hovering on the item we just swapped with. In this case, we don't want
37228
+ * a mouse move to trigger another swap the other way around, so we create a dead zone. We will clear
37229
+ * the dead zone when the mouse leaves the swapped item.
37230
+ */
37231
+ deadZone;
37232
+ constructor(args) {
37233
+ this.items = args.items.map((item) => ({ ...item, positionAtStart: item.position }));
37234
+ this.draggedItemId = args.draggedItemId;
37235
+ this.container = args.container;
37236
+ this.onChange = args.onChange;
37237
+ this.onCancel = args.onCancel;
37238
+ this.onDragEnd = args.onDragEnd;
37239
+ this.initialMousePosition = args.initialMousePosition;
37240
+ this.currentMousePosition = args.initialMousePosition;
37241
+ this.initialScroll = this.container.scroll;
37242
+ this.minPosition = this.items[0].position;
37243
+ this.maxPosition =
37244
+ this.items[this.items.length - 1].position + this.items[this.items.length - 1].size;
37245
+ }
37246
+ getItemStyles() {
37247
+ const styles = {};
37248
+ for (let item of this.items) {
37249
+ styles[item.id] = this.getItemStyle(item.id);
37250
+ }
37251
+ return styles;
37252
+ }
37253
+ getItemStyle(itemId) {
37254
+ const position = this.container.cssPositionProperty;
37255
+ const style = {};
37256
+ style.position = "relative";
37257
+ style[position] = (this.getItemsPositions()[itemId] || 0) + "px";
37258
+ style.transition = `${position} 0.5s`;
37259
+ style["pointer-events"] = "none";
37260
+ if (this.draggedItemId === itemId) {
37261
+ style.transition = `${position} 0s`;
37262
+ style["z-index"] = "1000";
37263
+ }
37264
+ return cssPropertiesToCss(style);
37265
+ }
37266
+ onScroll() {
37267
+ this.moveDraggedItemToPosition(this.currentMousePosition + this.scrollOffset);
37268
+ }
37269
+ onMouseMove(ev) {
37270
+ if (ev.button > 1) {
37271
+ this.onCancel();
37272
+ return;
37273
+ }
37274
+ const mousePosition = this.container.getMousePosition(ev);
37275
+ this.currentMousePosition = mousePosition;
37276
+ if (mousePosition < this.container.start || mousePosition > this.container.end) {
37277
+ this.startEdgeScroll(mousePosition < this.container.start ? -1 : 1);
37278
+ return;
37279
+ }
37280
+ else {
37281
+ this.stopEdgeScroll();
37282
+ }
37283
+ this.moveDraggedItemToPosition(mousePosition + this.scrollOffset);
37284
+ }
37285
+ moveDraggedItemToPosition(position) {
37286
+ const hoveredItemIndex = this.getHoveredItemIndex(position, this.items);
37287
+ const draggedItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
37288
+ const draggedItem = this.items[draggedItemIndex];
37289
+ if (this.deadZone && this.isInZone(position, this.deadZone)) {
37290
+ this.onChange(this.getItemsPositions());
37291
+ return;
37292
+ }
37293
+ else if (this.isInZone(position, {
37294
+ start: draggedItem.position,
37295
+ end: draggedItem.position + draggedItem.size,
37296
+ })) {
37297
+ this.deadZone = undefined;
37298
+ }
37299
+ if (draggedItemIndex === hoveredItemIndex) {
37300
+ this.onChange(this.getItemsPositions());
37301
+ return;
37302
+ }
37303
+ const startIndex = Math.min(draggedItemIndex, hoveredItemIndex);
37304
+ const endIndex = Math.max(draggedItemIndex, hoveredItemIndex);
37305
+ const direction = Math.sign(hoveredItemIndex - draggedItemIndex);
37306
+ let draggedItemMoveSize = 0;
37307
+ for (let i = startIndex; i <= endIndex; i++) {
37308
+ if (i === draggedItemIndex) {
37309
+ continue;
37310
+ }
37311
+ this.items[i].position -= direction * draggedItem.size;
37312
+ draggedItemMoveSize += this.items[i].size;
37313
+ }
37314
+ draggedItem.position += direction * draggedItemMoveSize;
37315
+ this.items.sort((item1, item2) => item1.position - item2.position);
37316
+ this.deadZone =
37317
+ direction > 0
37318
+ ? { start: position, end: draggedItem.position }
37319
+ : { start: draggedItem.position + draggedItem.size, end: position };
37320
+ this.onChange(this.getItemsPositions());
37321
+ }
37322
+ onMouseUp(ev) {
37323
+ if (ev.button !== 0) {
37324
+ this.onCancel();
37325
+ }
37326
+ ev.stopPropagation();
37327
+ ev.preventDefault();
37328
+ const targetItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
37329
+ this.onDragEnd(this.draggedItemId, targetItemIndex);
37330
+ this.stopEdgeScroll();
37331
+ return false;
37332
+ }
37333
+ startEdgeScroll(direction) {
37334
+ if (this.edgeScrollIntervalId)
37335
+ return;
37336
+ this.edgeScrollIntervalId = window.setInterval(() => {
37337
+ const offset = direction * 3;
37338
+ this.container.scroll += offset;
37339
+ }, 5);
37340
+ }
37341
+ stopEdgeScroll() {
37342
+ window.clearInterval(this.edgeScrollIntervalId);
37343
+ this.edgeScrollIntervalId = undefined;
37344
+ }
37345
+ /**
37346
+ * Get the index of the item the given mouse position is inside.
37347
+ * If the mouse is outside the container, return the first or last item index.
37348
+ */
37349
+ getHoveredItemIndex(mousePosition, items) {
37350
+ if (mousePosition <= this.minPosition)
37351
+ return 0;
37352
+ if (mousePosition >= this.maxPosition)
37353
+ return items.length - 1;
37354
+ return items.findIndex((item) => item.position + item.size >= mousePosition);
37355
+ }
37356
+ getItemsPositions() {
37357
+ const positions = {};
37358
+ for (let item of this.items) {
37359
+ if (item.id !== this.draggedItemId) {
37360
+ positions[item.id] = item.position - item.positionAtStart;
37361
+ continue;
37362
+ }
37363
+ const mouseOffset = this.currentMousePosition - this.initialMousePosition;
37364
+ let start = mouseOffset + this.scrollOffset;
37365
+ start = Math.max(this.minPosition - item.positionAtStart, start);
37366
+ start = Math.min(this.maxPosition - item.positionAtStart - item.size, start);
37367
+ positions[item.id] = start;
37368
+ }
37369
+ return positions;
37370
+ }
37371
+ isInZone(position, zone) {
37372
+ return position >= zone.start && position <= zone.end;
37373
+ }
37374
+ get scrollOffset() {
37375
+ return this.container.scroll - this.initialScroll;
37376
+ }
37377
+ destroy() {
37378
+ this.stopEdgeScroll();
37379
+ }
37380
+ }
37381
+ class ContainerWrapper {
37382
+ el;
37383
+ constructor(el) {
37384
+ this.el = el;
37385
+ }
37386
+ get containerRect() {
37387
+ return this.el.getBoundingClientRect();
37388
+ }
37389
+ }
37390
+ class VerticalContainer extends ContainerWrapper {
37391
+ get start() {
37392
+ return this.containerRect.top;
37393
+ }
37394
+ get end() {
37395
+ return this.containerRect.bottom;
37396
+ }
37397
+ get cssPositionProperty() {
37398
+ return "top";
37399
+ }
37400
+ get scroll() {
37401
+ return this.el.scrollTop;
37402
+ }
37403
+ set scroll(scroll) {
37404
+ this.el.scrollTop = scroll;
37405
+ }
37406
+ getMousePosition(ev) {
37407
+ return ev.clientY;
37408
+ }
37409
+ }
37410
+ class HorizontalContainer extends ContainerWrapper {
37411
+ get start() {
37412
+ return this.containerRect.left;
37413
+ }
37414
+ get end() {
37415
+ return this.containerRect.right;
37416
+ }
37417
+ get cssPositionProperty() {
37418
+ return "left";
37419
+ }
37420
+ get scroll() {
37421
+ return this.el.scrollLeft;
37422
+ }
37423
+ set scroll(scroll) {
37424
+ this.el.scrollLeft = scroll;
37425
+ }
37426
+ getMousePosition(ev) {
37427
+ return ev.clientX;
37428
+ }
37429
+ }
37430
+
36985
37431
  const arrowMap = {
36986
37432
  ArrowDown: "down",
36987
37433
  ArrowLeft: "left",
@@ -37032,6 +37478,7 @@ class SelectionInputStore extends SpreadsheetStore {
37032
37478
  "changeRange",
37033
37479
  "reset",
37034
37480
  "confirm",
37481
+ "updateColors",
37035
37482
  ];
37036
37483
  ranges = [];
37037
37484
  focusedRangeIndex = null;
@@ -37151,6 +37598,9 @@ class SelectionInputStore extends SpreadsheetStore {
37151
37598
  this.removeRangeByIndex(index);
37152
37599
  }
37153
37600
  }
37601
+ updateColors(colors) {
37602
+ this.colors = colors;
37603
+ }
37154
37604
  confirm() {
37155
37605
  for (const range of this.selectionInputs) {
37156
37606
  if (range.xc === "") {
@@ -37361,6 +37811,9 @@ css /* scss */ `
37361
37811
  right: 7px;
37362
37812
  top: 4px;
37363
37813
  }
37814
+ .o-drag-handle {
37815
+ cursor: move;
37816
+ }
37364
37817
  }
37365
37818
  .o-button {
37366
37819
  height: 28px;
@@ -37392,13 +37845,17 @@ class SelectionInput extends owl.Component {
37392
37845
  class: { type: String, optional: true },
37393
37846
  onSelectionChanged: { type: Function, optional: true },
37394
37847
  onSelectionConfirmed: { type: Function, optional: true },
37848
+ onSelectionReordered: { type: Function, optional: true },
37849
+ onSelectionRemoved: { type: Function, optional: true },
37395
37850
  colors: { type: Array, optional: true, default: [] },
37396
37851
  };
37397
37852
  state = owl.useState({
37398
37853
  isMissing: false,
37399
37854
  mode: "select-range",
37400
37855
  });
37856
+ dragAndDrop = useDragAndDropListItems();
37401
37857
  focusedInput = owl.useRef("focusedInput");
37858
+ selectionRef = owl.useRef("o-selection");
37402
37859
  store;
37403
37860
  get ranges() {
37404
37861
  return this.store.selectionInputs;
@@ -37426,8 +37883,45 @@ class SelectionInput extends owl.Component {
37426
37883
  nextProps.ranges.join() !== this.store.selectionInputValues.join()) {
37427
37884
  this.store.resetWithRanges(nextProps.ranges);
37428
37885
  }
37886
+ if (nextProps.colors?.join() !== this.props.colors?.join() &&
37887
+ nextProps.colors?.join() !== this.store.colors.join()) {
37888
+ this.store.updateColors(nextProps.colors || []);
37889
+ }
37890
+ });
37891
+ }
37892
+ startDragAndDrop(rangeId, event) {
37893
+ if (event.button !== 0 || event.target.tagName === "SELECT") {
37894
+ return;
37895
+ }
37896
+ const rects = this.getRangeElementsRects();
37897
+ const draggableIds = this.ranges.map((range) => range.id);
37898
+ const draggableItems = draggableIds.map((id, index) => ({
37899
+ id: id.toString(),
37900
+ size: rects[index].height,
37901
+ position: rects[index].y,
37902
+ }));
37903
+ this.dragAndDrop.start("vertical", {
37904
+ draggedItemId: rangeId.toString(),
37905
+ initialMousePosition: event.clientY,
37906
+ items: draggableItems,
37907
+ containerEl: this.selectionRef.el,
37908
+ onDragEnd: (dimensionName, finalIndex) => {
37909
+ const originalIndex = draggableIds.findIndex((id) => id === rangeId);
37910
+ if (originalIndex === finalIndex) {
37911
+ return;
37912
+ }
37913
+ const draggedItems = [...draggableIds];
37914
+ draggedItems.splice(originalIndex, 1);
37915
+ draggedItems.splice(finalIndex, 0, rangeId);
37916
+ this.props.onSelectionReordered?.(this.store.selectionInputs.map((range) => draggedItems.indexOf(range.id)));
37917
+ this.props.onSelectionConfirmed?.();
37918
+ this.store.confirm();
37919
+ },
37429
37920
  });
37430
37921
  }
37922
+ getRangeElementsRects() {
37923
+ return Array.from(this.selectionRef.el.children).map((el) => el.getBoundingClientRect());
37924
+ }
37431
37925
  getColor(range) {
37432
37926
  if (!range.color) {
37433
37927
  return "";
@@ -37469,8 +37963,8 @@ class SelectionInput extends owl.Component {
37469
37963
  this.store.addEmptyRange();
37470
37964
  }
37471
37965
  removeInput(rangeId) {
37472
- this.store.removeRange(rangeId);
37473
- this.triggerChange();
37966
+ const index = this.store.selectionInputs.findIndex((range) => range.id === rangeId);
37967
+ this.props.onSelectionRemoved?.(index);
37474
37968
  this.props.onSelectionConfirmed?.();
37475
37969
  }
37476
37970
  onInputChanged(rangeId, ev) {
@@ -37501,6 +37995,8 @@ class ChartDataSeries extends owl.Component {
37501
37995
  ranges: Array,
37502
37996
  hasSingleRange: { type: Boolean, optional: true },
37503
37997
  onSelectionChanged: Function,
37998
+ onSelectionReordered: { type: Function, optional: true },
37999
+ onSelectionRemoved: { type: Function, optional: true },
37504
38000
  onSelectionConfirmed: Function,
37505
38001
  };
37506
38002
  get ranges() {
@@ -37609,11 +38105,11 @@ class GenericChartConfigPanel extends owl.Component {
37609
38105
  datasetDispatchResult: undefined,
37610
38106
  labelsDispatchResult: undefined,
37611
38107
  });
37612
- dataSeriesRanges = [];
38108
+ dataSets = [];
37613
38109
  labelRange;
37614
38110
  chartTerms = ChartTerms;
37615
38111
  setup() {
37616
- this.dataSeriesRanges = this.props.definition.dataSets;
38112
+ this.dataSets = this.props.definition.dataSets;
37617
38113
  this.labelRange = this.props.definition.labelRange;
37618
38114
  }
37619
38115
  get errorMessages() {
@@ -37658,22 +38154,46 @@ class GenericChartConfigPanel extends owl.Component {
37658
38154
  * button "confirm" is clicked
37659
38155
  */
37660
38156
  onDataSeriesRangesChanged(ranges) {
37661
- this.dataSeriesRanges = ranges.map((dataRange, i) => ({
37662
- ...this.dataSeriesRanges?.[i],
38157
+ this.dataSets = ranges.map((dataRange, i) => ({
38158
+ ...this.dataSets?.[i],
37663
38159
  dataRange,
37664
38160
  }));
37665
38161
  this.state.datasetDispatchResult = this.props.canUpdateChart(this.props.figureId, {
37666
- dataSets: this.dataSeriesRanges,
38162
+ dataSets: this.dataSets,
38163
+ });
38164
+ }
38165
+ onDataSeriesReordered(indexes) {
38166
+ const colorGenerator = getChartColorsGenerator({ dataSets: this.dataSets }, this.dataSets.length);
38167
+ const colors = this.dataSets.map((ds) => colorGenerator.next());
38168
+ this.dataSets = indexes.map((i) => ({
38169
+ backgroundColor: colors[i],
38170
+ ...this.dataSets[i],
38171
+ }));
38172
+ this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
38173
+ dataSets: this.dataSets,
38174
+ });
38175
+ }
38176
+ onDataSeriesRemoved(index) {
38177
+ const colorGenerator = getChartColorsGenerator({ dataSets: this.dataSets }, this.dataSets.length);
38178
+ const colors = this.dataSets.map((ds) => colorGenerator.next());
38179
+ this.dataSets = this.dataSets
38180
+ .map((ds, i) => ({
38181
+ backgroundColor: colors[i],
38182
+ ...ds,
38183
+ }))
38184
+ .filter((_, i) => i !== index);
38185
+ this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
38186
+ dataSets: this.dataSets,
37667
38187
  });
37668
38188
  }
37669
38189
  onDataSeriesConfirmed() {
37670
- this.dataSeriesRanges = spreadRange(this.env.model.getters, this.dataSeriesRanges);
38190
+ this.dataSets = spreadRange(this.env.model.getters, this.dataSets);
37671
38191
  this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
37672
- dataSets: this.dataSeriesRanges,
38192
+ dataSets: this.dataSets,
37673
38193
  });
37674
38194
  }
37675
38195
  getDataSeriesRanges() {
37676
- return this.dataSeriesRanges;
38196
+ return this.dataSets;
37677
38197
  }
37678
38198
  /**
37679
38199
  * Change the local labelRange. The model should be updated when the
@@ -37705,7 +38225,7 @@ class GenericChartConfigPanel extends owl.Component {
37705
38225
  const getters = this.env.model.getters;
37706
38226
  const sheetId = getters.getActiveSheetId();
37707
38227
  const labelRange = createValidRange(getters, sheetId, this.labelRange);
37708
- const dataSets = createDataSets(getters, this.dataSeriesRanges, sheetId, this.props.definition.dataSetsHaveTitle);
38228
+ const dataSets = createDataSets(getters, this.dataSets, sheetId, this.props.definition.dataSetsHaveTitle);
37709
38229
  if (dataSets.length) {
37710
38230
  return dataSets[0].dataRange.zone.top + 1;
37711
38231
  }
@@ -37807,157 +38327,6 @@ class BadgeSelection extends owl.Component {
37807
38327
  };
37808
38328
  }
37809
38329
 
37810
- /**
37811
- * Start listening to pointer events and apply the given callbacks.
37812
- *
37813
- * @returns A function to remove the listeners.
37814
- */
37815
- function startDnd(onMouseMove, onMouseUp, onMouseDown = () => { }) {
37816
- const removeListeners = () => {
37817
- window.removeEventListener("pointerdown", onMouseDown);
37818
- window.removeEventListener("pointerup", _onMouseUp);
37819
- window.removeEventListener("dragstart", _onDragStart);
37820
- window.removeEventListener("pointermove", onMouseMove);
37821
- window.removeEventListener("wheel", onMouseMove);
37822
- };
37823
- const _onMouseUp = (ev) => {
37824
- onMouseUp(ev);
37825
- removeListeners();
37826
- };
37827
- function _onDragStart(ev) {
37828
- ev.preventDefault();
37829
- }
37830
- window.addEventListener("pointerdown", onMouseDown);
37831
- window.addEventListener("pointerup", _onMouseUp);
37832
- window.addEventListener("dragstart", _onDragStart);
37833
- window.addEventListener("pointermove", onMouseMove);
37834
- // mouse wheel on window is by default a passive event.
37835
- // preventDefault() is not allowed in passive event handler.
37836
- // https://chromestatus.com/feature/6662647093133312
37837
- window.addEventListener("wheel", onMouseMove, { passive: false });
37838
- return removeListeners;
37839
- }
37840
- /**
37841
- * Function to be used during a pointerdown event, this function allows to
37842
- * perform actions related to the pointermove and pointerup events and adjusts the viewport
37843
- * when the new position related to the pointermove event is outside of it.
37844
- * Among inputs are two callback functions. First intended for actions performed during
37845
- * the pointermove event, it receives as parameters the current position of the pointermove
37846
- * (occurrence of the current column and the current row). Second intended for actions
37847
- * performed during the pointerup event.
37848
- */
37849
- function dragAndDropBeyondTheViewport(env, cbMouseMove, cbMouseUp, only = false) {
37850
- let timeOutId = null;
37851
- let currentEv;
37852
- let previousEv;
37853
- let startingEv;
37854
- let startingX;
37855
- let startingY;
37856
- const getters = env.model.getters;
37857
- const sheetId = getters.getActiveSheetId();
37858
- const position = gridOverlayPosition();
37859
- let colIndex;
37860
- let rowIndex;
37861
- const onMouseDown = (ev) => {
37862
- previousEv = ev;
37863
- startingEv = ev;
37864
- startingX = startingEv.clientX - position.left;
37865
- startingY = startingEv.clientY - position.top;
37866
- };
37867
- const onMouseMove = (ev) => {
37868
- currentEv = ev;
37869
- if (timeOutId) {
37870
- return;
37871
- }
37872
- const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates();
37873
- let { top, left, bottom, right } = getters.getActiveMainViewport();
37874
- let { scrollX, scrollY } = getters.getActiveSheetDOMScrollInfo();
37875
- const { xSplit, ySplit } = getters.getPaneDivisions(sheetId);
37876
- let canEdgeScroll = false;
37877
- let timeoutDelay = MAX_DELAY;
37878
- const x = currentEv.clientX - position.left;
37879
- colIndex = getters.getColIndex(x);
37880
- if (only !== "vertical") {
37881
- const previousX = previousEv.clientX - position.left;
37882
- const edgeScrollInfoX = getters.getEdgeScrollCol(x, previousX, startingX);
37883
- if (edgeScrollInfoX.canEdgeScroll) {
37884
- canEdgeScroll = true;
37885
- timeoutDelay = Math.min(timeoutDelay, edgeScrollInfoX.delay);
37886
- let newTarget;
37887
- switch (edgeScrollInfoX.direction) {
37888
- case "reset":
37889
- colIndex = xSplit;
37890
- newTarget = xSplit;
37891
- break;
37892
- case 1:
37893
- colIndex = right;
37894
- newTarget = left + 1;
37895
- break;
37896
- case -1:
37897
- colIndex = left - 1;
37898
- while (env.model.getters.isColHidden(sheetId, colIndex)) {
37899
- colIndex--;
37900
- }
37901
- newTarget = colIndex;
37902
- break;
37903
- }
37904
- scrollX = getters.getColDimensions(sheetId, newTarget).start - offsetCorrectionX;
37905
- }
37906
- }
37907
- const y = currentEv.clientY - position.top;
37908
- rowIndex = getters.getRowIndex(y);
37909
- if (only !== "horizontal") {
37910
- const previousY = previousEv.clientY - position.top;
37911
- const edgeScrollInfoY = getters.getEdgeScrollRow(y, previousY, startingY);
37912
- if (edgeScrollInfoY.canEdgeScroll) {
37913
- canEdgeScroll = true;
37914
- timeoutDelay = Math.min(timeoutDelay, edgeScrollInfoY.delay);
37915
- let newTarget;
37916
- switch (edgeScrollInfoY.direction) {
37917
- case "reset":
37918
- rowIndex = ySplit;
37919
- newTarget = ySplit;
37920
- break;
37921
- case 1:
37922
- rowIndex = bottom;
37923
- newTarget = top + edgeScrollInfoY.direction;
37924
- break;
37925
- case -1:
37926
- rowIndex = top - 1;
37927
- while (env.model.getters.isRowHidden(sheetId, rowIndex)) {
37928
- rowIndex--;
37929
- }
37930
- newTarget = rowIndex;
37931
- break;
37932
- }
37933
- scrollY = env.model.getters.getRowDimensions(sheetId, newTarget).start - offsetCorrectionY;
37934
- }
37935
- }
37936
- if (!canEdgeScroll) {
37937
- if (rowIndex === -1) {
37938
- rowIndex = y < 0 ? 0 : getters.getNumberRows(sheetId) - 1;
37939
- }
37940
- if (colIndex === -1 && x < 0) {
37941
- colIndex = x < 0 ? 0 : getters.getNumberCols(sheetId) - 1;
37942
- }
37943
- }
37944
- cbMouseMove(colIndex, rowIndex, currentEv);
37945
- if (canEdgeScroll) {
37946
- env.model.dispatch("SET_VIEWPORT_OFFSET", { offsetX: scrollX, offsetY: scrollY });
37947
- timeOutId = setTimeout(() => {
37948
- timeOutId = null;
37949
- onMouseMove(currentEv);
37950
- }, Math.round(timeoutDelay));
37951
- }
37952
- previousEv = currentEv;
37953
- };
37954
- const onMouseUp = () => {
37955
- clearTimeout(timeOutId);
37956
- cbMouseUp();
37957
- };
37958
- startDnd(onMouseMove, onMouseUp, onMouseDown);
37959
- }
37960
-
37961
38330
  const LINE_VERTICAL_PADDING = 1;
37962
38331
  const PICKER_PADDING = 8;
37963
38332
  const ITEM_BORDER_WIDTH = 1;
@@ -39239,9 +39608,9 @@ class GeoChartConfigPanel extends GenericChartConfigPanel {
39239
39608
  return this.getDataSeriesRanges().slice(0, 1);
39240
39609
  }
39241
39610
  onDataSeriesConfirmed() {
39242
- this.dataSeriesRanges = spreadRange(this.env.model.getters, this.dataSeriesRanges).slice(0, 1);
39611
+ this.dataSets = spreadRange(this.env.model.getters, this.dataSets).slice(0, 1);
39243
39612
  this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
39244
- dataSets: this.dataSeriesRanges,
39613
+ dataSets: this.dataSets,
39245
39614
  });
39246
39615
  }
39247
39616
  getLabelRangeOptions() {
@@ -41099,289 +41468,6 @@ class IconPicker extends owl.Component {
41099
41468
  }
41100
41469
  }
41101
41470
 
41102
- function useDragAndDropListItems() {
41103
- let dndHelper;
41104
- const previousCursor = document.body.style.cursor;
41105
- let cleanupFns = [];
41106
- const cleanUp = () => {
41107
- dndHelper = undefined;
41108
- document.body.style.cursor = previousCursor;
41109
- cleanupFns.forEach((fn) => fn());
41110
- cleanupFns = [];
41111
- };
41112
- const start = (direction, args) => {
41113
- const onChange = () => {
41114
- document.body.style.cursor = "move";
41115
- if (!dndHelper)
41116
- return;
41117
- Object.assign(state.itemsStyle, dndHelper.getItemStyles());
41118
- args.onChange?.();
41119
- };
41120
- state.cancel = () => {
41121
- state.draggedItemId = undefined;
41122
- state.itemsStyle = {};
41123
- document.body.style.cursor = previousCursor;
41124
- args.onCancel?.();
41125
- cleanUp();
41126
- };
41127
- const onDragEnd = (itemId, indexAtEnd) => {
41128
- state.draggedItemId = undefined;
41129
- state.itemsStyle = {};
41130
- document.body.style.cursor = previousCursor;
41131
- args.onDragEnd?.(itemId, indexAtEnd);
41132
- cleanUp();
41133
- };
41134
- document.body.style.cursor = "move";
41135
- state.draggedItemId = args.draggedItemId;
41136
- const container = direction === "horizontal"
41137
- ? new HorizontalContainer(args.containerEl)
41138
- : new VerticalContainer(args.containerEl);
41139
- dndHelper = new DOMDndHelper({
41140
- ...args,
41141
- container,
41142
- onChange,
41143
- onDragEnd,
41144
- onCancel: state.cancel,
41145
- });
41146
- const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
41147
- cleanupFns.push(stopListening);
41148
- const onScroll = dndHelper.onScroll.bind(dndHelper);
41149
- args.containerEl.addEventListener("scroll", onScroll);
41150
- cleanupFns.push(() => args.containerEl.removeEventListener("scroll", onScroll));
41151
- cleanupFns.push(dndHelper.destroy.bind(dndHelper));
41152
- };
41153
- owl.onWillUnmount(() => {
41154
- cleanUp();
41155
- });
41156
- const state = owl.useState({
41157
- itemsStyle: {},
41158
- draggedItemId: undefined,
41159
- start,
41160
- cancel: () => { },
41161
- });
41162
- return state;
41163
- }
41164
- class DOMDndHelper {
41165
- draggedItemId;
41166
- items;
41167
- container;
41168
- initialMousePosition;
41169
- currentMousePosition;
41170
- initialScroll;
41171
- minPosition;
41172
- maxPosition;
41173
- edgeScrollIntervalId;
41174
- onChange;
41175
- onCancel;
41176
- onDragEnd;
41177
- /**
41178
- * The dead zone is an area in which the pointermove events are ignored.
41179
- *
41180
- * This is useful when swapping the dragged item with a larger item. After the swap,
41181
- * the mouse is still hovering on the item we just swapped with. In this case, we don't want
41182
- * a mouse move to trigger another swap the other way around, so we create a dead zone. We will clear
41183
- * the dead zone when the mouse leaves the swapped item.
41184
- */
41185
- deadZone;
41186
- constructor(args) {
41187
- this.items = args.items.map((item) => ({ ...item, positionAtStart: item.position }));
41188
- this.draggedItemId = args.draggedItemId;
41189
- this.container = args.container;
41190
- this.onChange = args.onChange;
41191
- this.onCancel = args.onCancel;
41192
- this.onDragEnd = args.onDragEnd;
41193
- this.initialMousePosition = args.initialMousePosition;
41194
- this.currentMousePosition = args.initialMousePosition;
41195
- this.initialScroll = this.container.scroll;
41196
- this.minPosition = this.items[0].position;
41197
- this.maxPosition =
41198
- this.items[this.items.length - 1].position + this.items[this.items.length - 1].size;
41199
- }
41200
- getItemStyles() {
41201
- const styles = {};
41202
- for (let item of this.items) {
41203
- styles[item.id] = this.getItemStyle(item.id);
41204
- }
41205
- return styles;
41206
- }
41207
- getItemStyle(itemId) {
41208
- const position = this.container.cssPositionProperty;
41209
- const style = {};
41210
- style.position = "relative";
41211
- style[position] = (this.getItemsPositions()[itemId] || 0) + "px";
41212
- style.transition = `${position} 0.5s`;
41213
- style["pointer-events"] = "none";
41214
- if (this.draggedItemId === itemId) {
41215
- style.transition = `${position} 0s`;
41216
- style["z-index"] = "1000";
41217
- }
41218
- return cssPropertiesToCss(style);
41219
- }
41220
- onScroll() {
41221
- this.moveDraggedItemToPosition(this.currentMousePosition + this.scrollOffset);
41222
- }
41223
- onMouseMove(ev) {
41224
- if (ev.button > 1) {
41225
- this.onCancel();
41226
- return;
41227
- }
41228
- const mousePosition = this.container.getMousePosition(ev);
41229
- this.currentMousePosition = mousePosition;
41230
- if (mousePosition < this.container.start || mousePosition > this.container.end) {
41231
- this.startEdgeScroll(mousePosition < this.container.start ? -1 : 1);
41232
- return;
41233
- }
41234
- else {
41235
- this.stopEdgeScroll();
41236
- }
41237
- this.moveDraggedItemToPosition(mousePosition + this.scrollOffset);
41238
- }
41239
- moveDraggedItemToPosition(position) {
41240
- const hoveredItemIndex = this.getHoveredItemIndex(position, this.items);
41241
- const draggedItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
41242
- const draggedItem = this.items[draggedItemIndex];
41243
- if (this.deadZone && this.isInZone(position, this.deadZone)) {
41244
- this.onChange(this.getItemsPositions());
41245
- return;
41246
- }
41247
- else if (this.isInZone(position, {
41248
- start: draggedItem.position,
41249
- end: draggedItem.position + draggedItem.size,
41250
- })) {
41251
- this.deadZone = undefined;
41252
- }
41253
- if (draggedItemIndex === hoveredItemIndex) {
41254
- this.onChange(this.getItemsPositions());
41255
- return;
41256
- }
41257
- const startIndex = Math.min(draggedItemIndex, hoveredItemIndex);
41258
- const endIndex = Math.max(draggedItemIndex, hoveredItemIndex);
41259
- const direction = Math.sign(hoveredItemIndex - draggedItemIndex);
41260
- let draggedItemMoveSize = 0;
41261
- for (let i = startIndex; i <= endIndex; i++) {
41262
- if (i === draggedItemIndex) {
41263
- continue;
41264
- }
41265
- this.items[i].position -= direction * draggedItem.size;
41266
- draggedItemMoveSize += this.items[i].size;
41267
- }
41268
- draggedItem.position += direction * draggedItemMoveSize;
41269
- this.items.sort((item1, item2) => item1.position - item2.position);
41270
- this.deadZone =
41271
- direction > 0
41272
- ? { start: position, end: draggedItem.position }
41273
- : { start: draggedItem.position + draggedItem.size, end: position };
41274
- this.onChange(this.getItemsPositions());
41275
- }
41276
- onMouseUp(ev) {
41277
- if (ev.button !== 0) {
41278
- this.onCancel();
41279
- }
41280
- ev.stopPropagation();
41281
- ev.preventDefault();
41282
- const targetItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
41283
- this.onDragEnd(this.draggedItemId, targetItemIndex);
41284
- this.stopEdgeScroll();
41285
- return false;
41286
- }
41287
- startEdgeScroll(direction) {
41288
- if (this.edgeScrollIntervalId)
41289
- return;
41290
- this.edgeScrollIntervalId = window.setInterval(() => {
41291
- const offset = direction * 3;
41292
- this.container.scroll += offset;
41293
- }, 5);
41294
- }
41295
- stopEdgeScroll() {
41296
- window.clearInterval(this.edgeScrollIntervalId);
41297
- this.edgeScrollIntervalId = undefined;
41298
- }
41299
- /**
41300
- * Get the index of the item the given mouse position is inside.
41301
- * If the mouse is outside the container, return the first or last item index.
41302
- */
41303
- getHoveredItemIndex(mousePosition, items) {
41304
- if (mousePosition <= this.minPosition)
41305
- return 0;
41306
- if (mousePosition >= this.maxPosition)
41307
- return items.length - 1;
41308
- return items.findIndex((item) => item.position + item.size >= mousePosition);
41309
- }
41310
- getItemsPositions() {
41311
- const positions = {};
41312
- for (let item of this.items) {
41313
- if (item.id !== this.draggedItemId) {
41314
- positions[item.id] = item.position - item.positionAtStart;
41315
- continue;
41316
- }
41317
- const mouseOffset = this.currentMousePosition - this.initialMousePosition;
41318
- let start = mouseOffset + this.scrollOffset;
41319
- start = Math.max(this.minPosition - item.positionAtStart, start);
41320
- start = Math.min(this.maxPosition - item.positionAtStart - item.size, start);
41321
- positions[item.id] = start;
41322
- }
41323
- return positions;
41324
- }
41325
- isInZone(position, zone) {
41326
- return position >= zone.start && position <= zone.end;
41327
- }
41328
- get scrollOffset() {
41329
- return this.container.scroll - this.initialScroll;
41330
- }
41331
- destroy() {
41332
- this.stopEdgeScroll();
41333
- }
41334
- }
41335
- class ContainerWrapper {
41336
- el;
41337
- constructor(el) {
41338
- this.el = el;
41339
- }
41340
- get containerRect() {
41341
- return this.el.getBoundingClientRect();
41342
- }
41343
- }
41344
- class VerticalContainer extends ContainerWrapper {
41345
- get start() {
41346
- return this.containerRect.top;
41347
- }
41348
- get end() {
41349
- return this.containerRect.bottom;
41350
- }
41351
- get cssPositionProperty() {
41352
- return "top";
41353
- }
41354
- get scroll() {
41355
- return this.el.scrollTop;
41356
- }
41357
- set scroll(scroll) {
41358
- this.el.scrollTop = scroll;
41359
- }
41360
- getMousePosition(ev) {
41361
- return ev.clientY;
41362
- }
41363
- }
41364
- class HorizontalContainer extends ContainerWrapper {
41365
- get start() {
41366
- return this.containerRect.left;
41367
- }
41368
- get end() {
41369
- return this.containerRect.right;
41370
- }
41371
- get cssPositionProperty() {
41372
- return "left";
41373
- }
41374
- get scroll() {
41375
- return this.el.scrollLeft;
41376
- }
41377
- set scroll(scroll) {
41378
- this.el.scrollLeft = scroll;
41379
- }
41380
- getMousePosition(ev) {
41381
- return ev.clientX;
41382
- }
41383
- }
41384
-
41385
41471
  /**
41386
41472
  * Manages an event listener on a ref. Useful for hooks that want to manage
41387
41473
  * event listeners, especially more than one. Prefer using t-on directly in
@@ -46285,7 +46371,7 @@ pivotRegistry.add("SPREADSHEET", {
46285
46371
  onIterationEndEvaluation: (pivot) => pivot.markAsDirtyForEvaluation(),
46286
46372
  dateGranularities: [...dateGranularities],
46287
46373
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
46288
- isMeasureCandidate: (field) => !["datetime", "boolean"].includes(field.type),
46374
+ isMeasureCandidate: (field) => field.type !== "boolean",
46289
46375
  isGroupable: () => true,
46290
46376
  });
46291
46377
 
@@ -46503,8 +46589,11 @@ class PivotSidePanelStore extends SpreadsheetStore {
46503
46589
  };
46504
46590
  }
46505
46591
  getUnusedGranularities(fields, definition) {
46506
- const { columns, rows } = definition;
46507
- const dateFields = columns.concat(rows).filter((dimension) => {
46592
+ const { columns, rows, measures } = definition;
46593
+ const dateFields = columns
46594
+ .concat(rows)
46595
+ .concat(measures)
46596
+ .filter((dimension) => {
46508
46597
  const fieldType = fields[dimension.fieldName]?.type;
46509
46598
  return fieldType === "date" || fieldType === "datetime";
46510
46599
  });
@@ -48776,7 +48865,7 @@ class GridComposer extends owl.Component {
48776
48865
  return;
48777
48866
  }
48778
48867
  const sheetId = this.env.model.getters.getActiveSheetId();
48779
- const zone = this.env.model.getters.getSelectedZone();
48868
+ const zone = positionToZone(this.env.model.getters.getSelection().anchor.cell);
48780
48869
  const rect = this.env.model.getters.getVisibleRect(zone);
48781
48870
  if (!deepEquals(rect, this.rect) || sheetId !== this.composerStore.currentEditedCell.sheetId) {
48782
48871
  this.isCellReferenceVisible = true;
@@ -50612,13 +50701,23 @@ class GridRenderer {
50612
50701
  drawLayer(renderingContext, layer) {
50613
50702
  switch (layer) {
50614
50703
  case "Background":
50615
- const boxes = this.getGridBoxes();
50616
- this.drawBackground(renderingContext, boxes);
50617
- this.drawOverflowingCellBackground(renderingContext, boxes);
50618
- this.drawCellBackground(renderingContext, boxes);
50619
- this.drawBorders(renderingContext, boxes);
50620
- this.drawTexts(renderingContext, boxes);
50621
- this.drawIcon(renderingContext, boxes);
50704
+ this.drawGlobalBackground(renderingContext);
50705
+ for (const zone of this.getters.getAllActiveViewportsZones()) {
50706
+ const { ctx } = renderingContext;
50707
+ ctx.save();
50708
+ ctx.beginPath();
50709
+ const rect = this.getters.getVisibleRect(zone);
50710
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
50711
+ ctx.clip();
50712
+ const boxes = this.getGridBoxes(zone);
50713
+ this.drawBackground(renderingContext, boxes);
50714
+ this.drawOverflowingCellBackground(renderingContext, boxes);
50715
+ this.drawCellBackground(renderingContext, boxes);
50716
+ this.drawBorders(renderingContext, boxes);
50717
+ this.drawTexts(renderingContext, boxes);
50718
+ this.drawIcon(renderingContext, boxes);
50719
+ ctx.restore();
50720
+ }
50622
50721
  this.drawFrozenPanes(renderingContext);
50623
50722
  break;
50624
50723
  case "Headers":
@@ -50629,12 +50728,15 @@ class GridRenderer {
50629
50728
  break;
50630
50729
  }
50631
50730
  }
50632
- drawBackground(renderingContext, boxes) {
50633
- const { ctx, thinLineWidth } = renderingContext;
50731
+ drawGlobalBackground(renderingContext) {
50732
+ const { ctx } = renderingContext;
50634
50733
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
50635
50734
  // white background
50636
50735
  ctx.fillStyle = "#ffffff";
50637
50736
  ctx.fillRect(0, 0, width + CANVAS_SHIFT, height + CANVAS_SHIFT);
50737
+ }
50738
+ drawBackground(renderingContext, boxes) {
50739
+ const { ctx, thinLineWidth } = renderingContext;
50638
50740
  const areGridLinesVisible = !this.getters.isDashboard() &&
50639
50741
  this.getters.getGridLinesVisibility(this.getters.getActiveSheetId());
50640
50742
  const inset = areGridLinesVisible ? 0.1 * thinLineWidth : 0;
@@ -51065,7 +51167,7 @@ class GridRenderer {
51065
51167
  const position = { sheetId, col, row };
51066
51168
  const cell = this.getters.getEvaluatedCell(position);
51067
51169
  const showFormula = this.getters.shouldShowFormulas();
51068
- const { x, y, width, height } = this.getters.getVisibleRect(zone);
51170
+ const { x, y, width, height } = this.getters.getRect(zone);
51069
51171
  const { verticalAlign } = this.getters.getCellStyle(position);
51070
51172
  let style = this.getters.getCellComputedStyle(position);
51071
51173
  if (this.fingerprints.isEnabled) {
@@ -51190,12 +51292,16 @@ class GridRenderer {
51190
51292
  }
51191
51293
  return box;
51192
51294
  }
51193
- getGridBoxes() {
51295
+ getGridBoxes(zone) {
51194
51296
  const boxes = [];
51195
- const visibleCols = this.getters.getSheetViewVisibleCols();
51297
+ const visibleCols = this.getters
51298
+ .getSheetViewVisibleCols()
51299
+ .filter((col) => col >= zone.left && col <= zone.right);
51196
51300
  const left = visibleCols[0];
51197
51301
  const right = visibleCols[visibleCols.length - 1];
51198
- const visibleRows = this.getters.getSheetViewVisibleRows();
51302
+ const visibleRows = this.getters
51303
+ .getSheetViewVisibleRows()
51304
+ .filter((row) => row >= zone.top && row <= zone.bottom);
51199
51305
  const top = visibleRows[0];
51200
51306
  const bottom = visibleRows[visibleRows.length - 1];
51201
51307
  const viewport = { left, right, top, bottom };
@@ -53427,7 +53533,7 @@ class CellPlugin extends CorePlugin {
53427
53533
  /*
53428
53534
  * Reconstructs the original formula string based on new dependencies
53429
53535
  */
53430
- getFormulaString(sheetId, tokens, dependencies, useFixedReference = false) {
53536
+ getFormulaString(sheetId, tokens, dependencies, useBoundedReference = false) {
53431
53537
  if (!dependencies.length) {
53432
53538
  return concat(tokens.map((token) => token.value));
53433
53539
  }
@@ -53435,7 +53541,7 @@ class CellPlugin extends CorePlugin {
53435
53541
  return concat(tokens.map((token) => {
53436
53542
  if (token.type === "REFERENCE") {
53437
53543
  const range = dependencies[rangeIndex++];
53438
- return this.getters.getRangeString(range, sheetId, { useFixedReference });
53544
+ return this.getters.getRangeString(range, sheetId, { useBoundedReference });
53439
53545
  }
53440
53546
  return token.value;
53441
53547
  }));
@@ -53715,7 +53821,7 @@ class FormulaCellWithDependencies {
53715
53821
  if (token.type === "REFERENCE") {
53716
53822
  const index = rangeIndex++;
53717
53823
  return this.getRangeString(this.compiledFormula.dependencies[index], this.sheetId, {
53718
- useFixedReference: true,
53824
+ useBoundedReference: true,
53719
53825
  });
53720
53826
  }
53721
53827
  return token.value;
@@ -54052,7 +54158,7 @@ class ConditionalFormatPlugin extends CorePlugin {
54052
54158
  if (data.sheets) {
54053
54159
  for (let sheet of data.sheets) {
54054
54160
  if (this.cfRules[sheet.id]) {
54055
- sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
54161
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useBoundedReference: true }));
54056
54162
  }
54057
54163
  }
54058
54164
  }
@@ -54121,9 +54227,9 @@ class ConditionalFormatPlugin extends CorePlugin {
54121
54227
  // ---------------------------------------------------------------------------
54122
54228
  // Private
54123
54229
  // ---------------------------------------------------------------------------
54124
- mapToConditionalFormat(sheetId, cf, { useFixedReference } = { useFixedReference: false }) {
54230
+ mapToConditionalFormat(sheetId, cf, { useBoundedReference } = { useBoundedReference: false }) {
54125
54231
  const ranges = cf.ranges.map((range) => {
54126
- return this.getters.getRangeString(range, sheetId, { useFixedReference });
54232
+ return this.getters.getRangeString(range, sheetId, { useBoundedReference });
54127
54233
  });
54128
54234
  if (cf.rule.type !== "DataBarRule") {
54129
54235
  return {
@@ -54138,7 +54244,7 @@ class ConditionalFormatPlugin extends CorePlugin {
54138
54244
  ...cf.rule,
54139
54245
  rangeValues: cf.rule.rangeValues &&
54140
54246
  this.getters.getRangeString(cf.rule.rangeValues, sheetId, {
54141
- useFixedReference,
54247
+ useBoundedReference,
54142
54248
  }),
54143
54249
  },
54144
54250
  ranges,
@@ -54581,7 +54687,7 @@ class DataValidationPlugin extends CorePlugin {
54581
54687
  for (const rule of this.rules[sheet.id]) {
54582
54688
  sheet.dataValidationRules.push({
54583
54689
  ...rule,
54584
- ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useFixedReference: true })),
54690
+ ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useBoundedReference: true })),
54585
54691
  });
54586
54692
  }
54587
54693
  }
@@ -56003,9 +56109,9 @@ class RangeAdapter {
56003
56109
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
56004
56110
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
56005
56111
  * @param options
56006
- * @param options.useFixedReference if true, the range will be returned with fixed row and column
56112
+ * @param options.useBoundedReference if true, the range will be returned with fixed row and column
56007
56113
  */
56008
- getRangeString(range, forSheetId, options = { useFixedReference: false }) {
56114
+ getRangeString(range, forSheetId, options = { useBoundedReference: false }) {
56009
56115
  if (!range) {
56010
56116
  return CellErrorType.InvalidReference;
56011
56117
  }
@@ -56108,13 +56214,13 @@ class RangeAdapter {
56108
56214
  /**
56109
56215
  * Get a Xc string that represent a part of a range
56110
56216
  */
56111
- getRangePartString(range, part, options = { useFixedReference: false }) {
56217
+ getRangePartString(range, part, options = { useBoundedReference: false }) {
56112
56218
  const colFixed = range.parts && range.parts[part]?.colFixed ? "$" : "";
56113
56219
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
56114
56220
  const rowFixed = range.parts && range.parts[part]?.rowFixed ? "$" : "";
56115
56221
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
56116
56222
  let str = "";
56117
- if (range.isFullCol && !options.useFixedReference) {
56223
+ if (range.isFullCol && !options.useBoundedReference) {
56118
56224
  if (part === 0 && range.unboundedZone.hasHeader) {
56119
56225
  str = colFixed + col + rowFixed + row;
56120
56226
  }
@@ -56122,7 +56228,7 @@ class RangeAdapter {
56122
56228
  str = colFixed + col;
56123
56229
  }
56124
56230
  }
56125
- else if (range.isFullRow && !options.useFixedReference) {
56231
+ else if (range.isFullRow && !options.useBoundedReference) {
56126
56232
  if (part === 0 && range.unboundedZone.hasHeader) {
56127
56233
  str = colFixed + col + rowFixed + row;
56128
56234
  }
@@ -63526,9 +63632,6 @@ class Session extends EventBus {
63526
63632
  }
63527
63633
  }
63528
63634
  this.acknowledge(message);
63529
- if (message.type === "REMOTE_REVISION" && message.clientId === this.clientId) {
63530
- return;
63531
- }
63532
63635
  this.trigger("collaborative-event-received");
63533
63636
  }
63534
63637
  onClientMoved(message) {
@@ -66851,8 +66954,12 @@ class GridSelectionPlugin extends UIPlugin {
66851
66954
  },
66852
66955
  ];
66853
66956
  handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
66957
+ const selection = pasteTarget[0];
66958
+ const col = selection.left;
66959
+ const row = selection.top;
66960
+ this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
66854
66961
  const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
66855
- let currentIndex = cmd.base;
66962
+ let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
66856
66963
  const resizingGroups = {};
66857
66964
  for (const element of toRemove) {
66858
66965
  const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
@@ -67143,22 +67250,33 @@ class InternalViewport {
67143
67250
  }
67144
67251
  /**
67145
67252
  *
67146
- * @param zone
67147
- * @returns Computes the absolute coordinate of a given zone inside the viewport
67253
+ * Computes the visible coordinates & dimensions of a given zone inside the viewport
67254
+ *
67148
67255
  */
67149
- getRect(zone) {
67256
+ getVisibleRect(zone) {
67150
67257
  const targetZone = intersection(zone, this);
67151
67258
  if (targetZone) {
67152
67259
  const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
67153
67260
  const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
67154
67261
  const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
67155
67262
  const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
67156
- return {
67157
- x,
67158
- y,
67159
- width,
67160
- height,
67161
- };
67263
+ return { x, y, width, height };
67264
+ }
67265
+ return undefined;
67266
+ }
67267
+ /**
67268
+ *
67269
+ * @returns Computes the absolute coordinates & dimensions of a given zone inside the viewport
67270
+ *
67271
+ */
67272
+ getFullRect(zone) {
67273
+ const targetZone = intersection(zone, this);
67274
+ if (targetZone) {
67275
+ const x = this.getters.getColRowOffset("COL", this.left, zone.left) + this.offsetCorrectionX;
67276
+ const y = this.getters.getColRowOffset("ROW", this.top, zone.top) + this.offsetCorrectionY;
67277
+ const width = this.getters.getColRowOffset("COL", zone.left, zone.right + 1);
67278
+ const height = this.getters.getColRowOffset("ROW", zone.top, zone.bottom + 1);
67279
+ return { x, y, width, height };
67162
67280
  }
67163
67281
  return undefined;
67164
67282
  }
@@ -67328,6 +67446,8 @@ class SheetViewPlugin extends UIPlugin {
67328
67446
  "isPositionVisible",
67329
67447
  "getColDimensionsInViewport",
67330
67448
  "getRowDimensionsInViewport",
67449
+ "getAllActiveViewportsZones",
67450
+ "getRect",
67331
67451
  ];
67332
67452
  viewports = {};
67333
67453
  /**
@@ -67711,16 +67831,27 @@ class SheetViewPlugin extends UIPlugin {
67711
67831
  getVisibleRectWithoutHeaders(zone) {
67712
67832
  const sheetId = this.getters.getActiveSheetId();
67713
67833
  const viewportRects = this.getSubViewports(sheetId)
67714
- .map((viewport) => viewport.getRect(zone))
67834
+ .map((viewport) => viewport.getVisibleRect(zone))
67715
67835
  .filter(isDefined);
67716
67836
  if (viewportRects.length === 0) {
67717
67837
  return { x: 0, y: 0, width: 0, height: 0 };
67718
67838
  }
67719
- const x = Math.min(...viewportRects.map((rect) => rect.x));
67720
- const y = Math.min(...viewportRects.map((rect) => rect.y));
67721
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
67722
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
67723
- return { x, y, width, height };
67839
+ return this.recomposeRect(viewportRects);
67840
+ }
67841
+ /**
67842
+ * Computes the actual size and position (:Rect) of the zone on the canvas
67843
+ * regardless of the viewport dimensions.
67844
+ */
67845
+ getRect(zone) {
67846
+ const sheetId = this.getters.getActiveSheetId();
67847
+ const viewportRects = this.getSubViewports(sheetId)
67848
+ .map((viewport) => viewport.getFullRect(zone))
67849
+ .filter(isDefined);
67850
+ if (viewportRects.length === 0) {
67851
+ return { x: 0, y: 0, width: 0, height: 0 };
67852
+ }
67853
+ const rect = this.recomposeRect(viewportRects);
67854
+ return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
67724
67855
  }
67725
67856
  /**
67726
67857
  * Returns the position of the MainViewport relatively to the start of the grid (without headers)
@@ -67764,6 +67895,10 @@ class SheetViewPlugin extends UIPlugin {
67764
67895
  end: start + (isRowHidden ? 0 : size),
67765
67896
  };
67766
67897
  }
67898
+ getAllActiveViewportsZones() {
67899
+ const sheetId = this.getters.getActiveSheetId();
67900
+ return this.getSubViewports(sheetId);
67901
+ }
67767
67902
  // ---------------------------------------------------------------------------
67768
67903
  // Private
67769
67904
  // ---------------------------------------------------------------------------
@@ -67954,6 +68089,13 @@ class SheetViewPlugin extends UIPlugin {
67954
68089
  const height = this.sheetViewHeight + this.gridOffsetY;
67955
68090
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
67956
68091
  }
68092
+ recomposeRect(viewportRects) {
68093
+ const x = Math.min(...viewportRects.map((rect) => rect.x));
68094
+ const y = Math.min(...viewportRects.map((rect) => rect.y));
68095
+ const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
68096
+ const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
68097
+ return { x, y, width, height };
68098
+ }
67957
68099
  }
67958
68100
 
67959
68101
  class HeaderPositionsUIPlugin extends UIPlugin {
@@ -69795,6 +69937,13 @@ class BorderEditorWidget extends owl.Component {
69795
69937
  currentStyle: DEFAULT_BORDER_DESC.style,
69796
69938
  currentPosition: undefined,
69797
69939
  });
69940
+ setup() {
69941
+ owl.onWillUpdateProps((newProps) => {
69942
+ if (!newProps.showBorderEditor) {
69943
+ this.state.currentPosition = undefined;
69944
+ }
69945
+ });
69946
+ }
69798
69947
  get borderEditorAnchorRect() {
69799
69948
  const button = this.borderEditorButtonRef.el;
69800
69949
  const buttonRect = button.getBoundingClientRect();
@@ -75106,6 +75255,6 @@ exports.tokenColors = tokenColors;
75106
75255
  exports.tokenize = tokenize;
75107
75256
 
75108
75257
 
75109
- __info__.version = "18.2.0-alpha.1";
75110
- __info__.date = "2025-01-14T11:35:51.135Z";
75111
- __info__.hash = "702f816";
75258
+ __info__.version = "18.2.0-alpha.3";
75259
+ __info__.date = "2025-01-27T10:07:28.716Z";
75260
+ __info__.hash = "63a13e5";