@odoo/o-spreadsheet 18.2.0-alpha.2 → 18.2.0-alpha.4

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.2
6
- * @date 2025-01-15T08:06:32.137Z
7
- * @hash 4f96c47
5
+ * @version 18.2.0-alpha.4
6
+ * @date 2025-01-29T06:30:12.773Z
7
+ * @hash 6838c26
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -4389,15 +4389,18 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4389
4389
  let currentIndex;
4390
4390
  let currentVal;
4391
4391
  let currentType;
4392
+ const getValue = sortOrder === "desc"
4393
+ ? (i) => normalizeValue(getValueInData(data, rangeLength - i - 1))
4394
+ : (i) => normalizeValue(getValueInData(data, i));
4392
4395
  while (indexRight - indexLeft >= 0) {
4393
4396
  indexMedian = Math.floor((indexLeft + indexRight) / 2);
4394
4397
  currentIndex = indexMedian;
4395
- currentVal = normalizeValue(getValueInData(data, currentIndex));
4398
+ currentVal = getValue(currentIndex);
4396
4399
  currentType = typeof currentVal;
4397
4400
  // 1 - linear search to find value with the same type
4398
4401
  while (indexLeft < currentIndex && targetType !== currentType) {
4399
4402
  currentIndex--;
4400
- currentVal = normalizeValue(getValueInData(data, currentIndex));
4403
+ currentVal = getValue(currentIndex);
4401
4404
  currentType = typeof currentVal;
4402
4405
  }
4403
4406
  if (currentType !== targetType || currentVal === undefined || currentVal === null) {
@@ -4413,8 +4416,7 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4413
4416
  if (matchVal === undefined ||
4414
4417
  matchVal === null ||
4415
4418
  matchVal < currentVal ||
4416
- (matchVal === currentVal && sortOrder === "asc" && matchValIndex < currentIndex) ||
4417
- (matchVal === currentVal && sortOrder === "desc" && matchValIndex > currentIndex)) {
4419
+ (matchVal === currentVal && matchValIndex < currentIndex)) {
4418
4420
  matchVal = currentVal;
4419
4421
  matchValIndex = currentIndex;
4420
4422
  }
@@ -4422,15 +4424,13 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4422
4424
  else if (mode === "nextGreater" && currentVal >= _target) {
4423
4425
  if (matchVal === undefined ||
4424
4426
  matchVal > currentVal ||
4425
- (matchVal === currentVal && sortOrder === "asc" && matchValIndex < currentIndex) ||
4426
- (matchVal === currentVal && sortOrder === "desc" && matchValIndex > currentIndex)) {
4427
+ (matchVal === currentVal && matchValIndex < currentIndex)) {
4427
4428
  matchVal = currentVal;
4428
4429
  matchValIndex = currentIndex;
4429
4430
  }
4430
4431
  }
4431
4432
  // 3 - give new indexes for the Binary search
4432
- if ((sortOrder === "asc" && currentVal > _target) ||
4433
- (sortOrder === "desc" && currentVal <= _target)) {
4433
+ if (currentVal > _target || (mode === "strict" && currentVal === _target)) {
4434
4434
  indexRight = currentIndex - 1;
4435
4435
  }
4436
4436
  else {
@@ -4438,7 +4438,10 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4438
4438
  }
4439
4439
  }
4440
4440
  // note that valMinIndex could be 0
4441
- return matchValIndex !== undefined ? matchValIndex : -1;
4441
+ if (matchValIndex === undefined) {
4442
+ return -1;
4443
+ }
4444
+ return sortOrder === "desc" ? rangeLength - matchValIndex - 1 : matchValIndex;
4442
4445
  }
4443
4446
  /**
4444
4447
  * Perform a linear search and return the index of the match.
@@ -8003,6 +8006,7 @@ const AGGREGATORS_BY_FIELD_TYPE = {
8003
8006
  integer: NUMBER_CHAR_AGGREGATORS,
8004
8007
  char: NUMBER_CHAR_AGGREGATORS,
8005
8008
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8009
+ datetime: ["max", "min", "count_distinct", "count"],
8006
8010
  };
8007
8011
  const AGGREGATORS = {};
8008
8012
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -9808,13 +9812,13 @@ function toExcelDataset(getters, ds) {
9808
9812
  else if (ds.labelCell) {
9809
9813
  label = {
9810
9814
  reference: getters.getRangeString(ds.labelCell, "forceSheetReference", {
9811
- useFixedReference: true,
9815
+ useBoundedReference: true,
9812
9816
  }),
9813
9817
  };
9814
9818
  }
9815
9819
  return {
9816
9820
  label,
9817
- range: getters.getRangeString(dataRange, "forceSheetReference", { useFixedReference: true }),
9821
+ range: getters.getRangeString(dataRange, "forceSheetReference", { useBoundedReference: true }),
9818
9822
  backgroundColor: ds.backgroundColor,
9819
9823
  rightYAxis: ds.rightYAxis,
9820
9824
  };
@@ -9829,7 +9833,7 @@ function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
9829
9833
  zone.top = zone.top + 1;
9830
9834
  }
9831
9835
  const range = labelRange.clone({ zone });
9832
- return getters.getRangeString(range, "forceSheetReference", { useFixedReference: true });
9836
+ return getters.getRangeString(range, "forceSheetReference", { useBoundedReference: true });
9833
9837
  }
9834
9838
  /**
9835
9839
  * Transform a chart definition which supports dataSets (dataSets and LabelRange)
@@ -29579,11 +29583,14 @@ const templates = /* xml */ `
29579
29583
  </t>
29580
29584
  </templates>
29581
29585
  `;
29582
- const app = new App(Component, { templates, translateFn: _t });
29586
+ let app;
29583
29587
  function renderToString(templateName, context = {}) {
29584
29588
  return render(templateName, context).innerHTML;
29585
29589
  }
29586
29590
  function render(templateName, context = {}) {
29591
+ if (!app) {
29592
+ app = new App(Component, { templates, translateFn: _t });
29593
+ }
29587
29594
  const templateFn = app.getTemplate(templateName);
29588
29595
  const bdom = templateFn(context, {});
29589
29596
  const div = document.createElement("div");
@@ -29812,6 +29819,7 @@ var CHART_RUNTIME_HELPERS = /*#__PURE__*/Object.freeze({
29812
29819
  getBarChartLegend: getBarChartLegend,
29813
29820
  getBarChartScales: getBarChartScales,
29814
29821
  getBarChartTooltip: getBarChartTooltip,
29822
+ getChartColorsGenerator: getChartColorsGenerator,
29815
29823
  getChartLabelFormat: getChartLabelFormat,
29816
29824
  getChartLayout: getChartLayout,
29817
29825
  getChartShowValues: getChartShowValues,
@@ -32344,6 +32352,10 @@ class Popover extends Component {
32344
32352
  this.currentDisplayValue = newDisplay;
32345
32353
  if (!anchor)
32346
32354
  return;
32355
+ el.style.top = "";
32356
+ el.style.left = "";
32357
+ el.style["max-height"] = "";
32358
+ el.style["max-width"] = "";
32347
32359
  const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
32348
32360
  let elDims = {
32349
32361
  width: el.getBoundingClientRect().width,
@@ -36980,6 +36992,440 @@ class Section extends Component {
36980
36992
  };
36981
36993
  }
36982
36994
 
36995
+ /**
36996
+ * Start listening to pointer events and apply the given callbacks.
36997
+ *
36998
+ * @returns A function to remove the listeners.
36999
+ */
37000
+ function startDnd(onMouseMove, onMouseUp, onMouseDown = () => { }) {
37001
+ const removeListeners = () => {
37002
+ window.removeEventListener("pointerdown", onMouseDown);
37003
+ window.removeEventListener("pointerup", _onMouseUp);
37004
+ window.removeEventListener("dragstart", _onDragStart);
37005
+ window.removeEventListener("pointermove", onMouseMove);
37006
+ window.removeEventListener("wheel", onMouseMove);
37007
+ };
37008
+ const _onMouseUp = (ev) => {
37009
+ onMouseUp(ev);
37010
+ removeListeners();
37011
+ };
37012
+ function _onDragStart(ev) {
37013
+ ev.preventDefault();
37014
+ }
37015
+ window.addEventListener("pointerdown", onMouseDown);
37016
+ window.addEventListener("pointerup", _onMouseUp);
37017
+ window.addEventListener("dragstart", _onDragStart);
37018
+ window.addEventListener("pointermove", onMouseMove);
37019
+ // mouse wheel on window is by default a passive event.
37020
+ // preventDefault() is not allowed in passive event handler.
37021
+ // https://chromestatus.com/feature/6662647093133312
37022
+ window.addEventListener("wheel", onMouseMove, { passive: false });
37023
+ return removeListeners;
37024
+ }
37025
+ /**
37026
+ * Function to be used during a pointerdown event, this function allows to
37027
+ * perform actions related to the pointermove and pointerup events and adjusts the viewport
37028
+ * when the new position related to the pointermove event is outside of it.
37029
+ * Among inputs are two callback functions. First intended for actions performed during
37030
+ * the pointermove event, it receives as parameters the current position of the pointermove
37031
+ * (occurrence of the current column and the current row). Second intended for actions
37032
+ * performed during the pointerup event.
37033
+ */
37034
+ function dragAndDropBeyondTheViewport(env, cbMouseMove, cbMouseUp, only = false) {
37035
+ let timeOutId = null;
37036
+ let currentEv;
37037
+ let previousEv;
37038
+ let startingEv;
37039
+ let startingX;
37040
+ let startingY;
37041
+ const getters = env.model.getters;
37042
+ const sheetId = getters.getActiveSheetId();
37043
+ const position = gridOverlayPosition();
37044
+ let colIndex;
37045
+ let rowIndex;
37046
+ const onMouseDown = (ev) => {
37047
+ previousEv = ev;
37048
+ startingEv = ev;
37049
+ startingX = startingEv.clientX - position.left;
37050
+ startingY = startingEv.clientY - position.top;
37051
+ };
37052
+ const onMouseMove = (ev) => {
37053
+ currentEv = ev;
37054
+ if (timeOutId) {
37055
+ return;
37056
+ }
37057
+ const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates();
37058
+ let { top, left, bottom, right } = getters.getActiveMainViewport();
37059
+ let { scrollX, scrollY } = getters.getActiveSheetDOMScrollInfo();
37060
+ const { xSplit, ySplit } = getters.getPaneDivisions(sheetId);
37061
+ let canEdgeScroll = false;
37062
+ let timeoutDelay = MAX_DELAY;
37063
+ const x = currentEv.clientX - position.left;
37064
+ colIndex = getters.getColIndex(x);
37065
+ if (only !== "vertical") {
37066
+ const previousX = previousEv.clientX - position.left;
37067
+ const edgeScrollInfoX = getters.getEdgeScrollCol(x, previousX, startingX);
37068
+ if (edgeScrollInfoX.canEdgeScroll) {
37069
+ canEdgeScroll = true;
37070
+ timeoutDelay = Math.min(timeoutDelay, edgeScrollInfoX.delay);
37071
+ let newTarget;
37072
+ switch (edgeScrollInfoX.direction) {
37073
+ case "reset":
37074
+ colIndex = xSplit;
37075
+ newTarget = xSplit;
37076
+ break;
37077
+ case 1:
37078
+ colIndex = right;
37079
+ newTarget = left + 1;
37080
+ break;
37081
+ case -1:
37082
+ colIndex = left - 1;
37083
+ while (env.model.getters.isColHidden(sheetId, colIndex)) {
37084
+ colIndex--;
37085
+ }
37086
+ newTarget = colIndex;
37087
+ break;
37088
+ }
37089
+ scrollX = getters.getColDimensions(sheetId, newTarget).start - offsetCorrectionX;
37090
+ }
37091
+ }
37092
+ const y = currentEv.clientY - position.top;
37093
+ rowIndex = getters.getRowIndex(y);
37094
+ if (only !== "horizontal") {
37095
+ const previousY = previousEv.clientY - position.top;
37096
+ const edgeScrollInfoY = getters.getEdgeScrollRow(y, previousY, startingY);
37097
+ if (edgeScrollInfoY.canEdgeScroll) {
37098
+ canEdgeScroll = true;
37099
+ timeoutDelay = Math.min(timeoutDelay, edgeScrollInfoY.delay);
37100
+ let newTarget;
37101
+ switch (edgeScrollInfoY.direction) {
37102
+ case "reset":
37103
+ rowIndex = ySplit;
37104
+ newTarget = ySplit;
37105
+ break;
37106
+ case 1:
37107
+ rowIndex = bottom;
37108
+ newTarget = top + edgeScrollInfoY.direction;
37109
+ break;
37110
+ case -1:
37111
+ rowIndex = top - 1;
37112
+ while (env.model.getters.isRowHidden(sheetId, rowIndex)) {
37113
+ rowIndex--;
37114
+ }
37115
+ newTarget = rowIndex;
37116
+ break;
37117
+ }
37118
+ scrollY = env.model.getters.getRowDimensions(sheetId, newTarget).start - offsetCorrectionY;
37119
+ }
37120
+ }
37121
+ if (!canEdgeScroll) {
37122
+ if (rowIndex === -1) {
37123
+ rowIndex = y < 0 ? 0 : getters.getNumberRows(sheetId) - 1;
37124
+ }
37125
+ if (colIndex === -1 && x < 0) {
37126
+ colIndex = x < 0 ? 0 : getters.getNumberCols(sheetId) - 1;
37127
+ }
37128
+ }
37129
+ cbMouseMove(colIndex, rowIndex, currentEv);
37130
+ if (canEdgeScroll) {
37131
+ env.model.dispatch("SET_VIEWPORT_OFFSET", { offsetX: scrollX, offsetY: scrollY });
37132
+ timeOutId = setTimeout(() => {
37133
+ timeOutId = null;
37134
+ onMouseMove(currentEv);
37135
+ }, Math.round(timeoutDelay));
37136
+ }
37137
+ previousEv = currentEv;
37138
+ };
37139
+ const onMouseUp = () => {
37140
+ clearTimeout(timeOutId);
37141
+ cbMouseUp();
37142
+ };
37143
+ startDnd(onMouseMove, onMouseUp, onMouseDown);
37144
+ }
37145
+
37146
+ function useDragAndDropListItems() {
37147
+ let dndHelper;
37148
+ const previousCursor = document.body.style.cursor;
37149
+ let cleanupFns = [];
37150
+ const cleanUp = () => {
37151
+ dndHelper = undefined;
37152
+ document.body.style.cursor = previousCursor;
37153
+ cleanupFns.forEach((fn) => fn());
37154
+ cleanupFns = [];
37155
+ };
37156
+ const start = (direction, args) => {
37157
+ const onChange = () => {
37158
+ document.body.style.cursor = "move";
37159
+ if (!dndHelper)
37160
+ return;
37161
+ Object.assign(state.itemsStyle, dndHelper.getItemStyles());
37162
+ args.onChange?.();
37163
+ };
37164
+ state.cancel = () => {
37165
+ state.draggedItemId = undefined;
37166
+ state.itemsStyle = {};
37167
+ document.body.style.cursor = previousCursor;
37168
+ args.onCancel?.();
37169
+ cleanUp();
37170
+ };
37171
+ const onDragEnd = (itemId, indexAtEnd) => {
37172
+ state.draggedItemId = undefined;
37173
+ state.itemsStyle = {};
37174
+ document.body.style.cursor = previousCursor;
37175
+ args.onDragEnd?.(itemId, indexAtEnd);
37176
+ cleanUp();
37177
+ };
37178
+ document.body.style.cursor = "move";
37179
+ state.draggedItemId = args.draggedItemId;
37180
+ const container = direction === "horizontal"
37181
+ ? new HorizontalContainer(args.containerEl)
37182
+ : new VerticalContainer(args.containerEl);
37183
+ dndHelper = new DOMDndHelper({
37184
+ ...args,
37185
+ container,
37186
+ onChange,
37187
+ onDragEnd,
37188
+ onCancel: state.cancel,
37189
+ });
37190
+ const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
37191
+ cleanupFns.push(stopListening);
37192
+ const onScroll = dndHelper.onScroll.bind(dndHelper);
37193
+ args.containerEl.addEventListener("scroll", onScroll);
37194
+ cleanupFns.push(() => args.containerEl.removeEventListener("scroll", onScroll));
37195
+ cleanupFns.push(dndHelper.destroy.bind(dndHelper));
37196
+ };
37197
+ onWillUnmount(() => {
37198
+ cleanUp();
37199
+ });
37200
+ const state = useState({
37201
+ itemsStyle: {},
37202
+ draggedItemId: undefined,
37203
+ start,
37204
+ cancel: () => { },
37205
+ });
37206
+ return state;
37207
+ }
37208
+ class DOMDndHelper {
37209
+ draggedItemId;
37210
+ items;
37211
+ container;
37212
+ initialMousePosition;
37213
+ currentMousePosition;
37214
+ initialScroll;
37215
+ minPosition;
37216
+ maxPosition;
37217
+ edgeScrollIntervalId;
37218
+ onChange;
37219
+ onCancel;
37220
+ onDragEnd;
37221
+ /**
37222
+ * The dead zone is an area in which the pointermove events are ignored.
37223
+ *
37224
+ * This is useful when swapping the dragged item with a larger item. After the swap,
37225
+ * the mouse is still hovering on the item we just swapped with. In this case, we don't want
37226
+ * a mouse move to trigger another swap the other way around, so we create a dead zone. We will clear
37227
+ * the dead zone when the mouse leaves the swapped item.
37228
+ */
37229
+ deadZone;
37230
+ constructor(args) {
37231
+ this.items = args.items.map((item) => ({ ...item, positionAtStart: item.position }));
37232
+ this.draggedItemId = args.draggedItemId;
37233
+ this.container = args.container;
37234
+ this.onChange = args.onChange;
37235
+ this.onCancel = args.onCancel;
37236
+ this.onDragEnd = args.onDragEnd;
37237
+ this.initialMousePosition = args.initialMousePosition;
37238
+ this.currentMousePosition = args.initialMousePosition;
37239
+ this.initialScroll = this.container.scroll;
37240
+ this.minPosition = this.items[0].position;
37241
+ this.maxPosition =
37242
+ this.items[this.items.length - 1].position + this.items[this.items.length - 1].size;
37243
+ }
37244
+ getItemStyles() {
37245
+ const styles = {};
37246
+ for (let item of this.items) {
37247
+ styles[item.id] = this.getItemStyle(item.id);
37248
+ }
37249
+ return styles;
37250
+ }
37251
+ getItemStyle(itemId) {
37252
+ const position = this.container.cssPositionProperty;
37253
+ const style = {};
37254
+ style.position = "relative";
37255
+ style[position] = (this.getItemsPositions()[itemId] || 0) + "px";
37256
+ style.transition = `${position} 0.5s`;
37257
+ style["pointer-events"] = "none";
37258
+ if (this.draggedItemId === itemId) {
37259
+ style.transition = `${position} 0s`;
37260
+ style["z-index"] = "1000";
37261
+ }
37262
+ return cssPropertiesToCss(style);
37263
+ }
37264
+ onScroll() {
37265
+ this.moveDraggedItemToPosition(this.currentMousePosition + this.scrollOffset);
37266
+ }
37267
+ onMouseMove(ev) {
37268
+ if (ev.button > 1) {
37269
+ this.onCancel();
37270
+ return;
37271
+ }
37272
+ const mousePosition = this.container.getMousePosition(ev);
37273
+ this.currentMousePosition = mousePosition;
37274
+ if (mousePosition < this.container.start || mousePosition > this.container.end) {
37275
+ this.startEdgeScroll(mousePosition < this.container.start ? -1 : 1);
37276
+ return;
37277
+ }
37278
+ else {
37279
+ this.stopEdgeScroll();
37280
+ }
37281
+ this.moveDraggedItemToPosition(mousePosition + this.scrollOffset);
37282
+ }
37283
+ moveDraggedItemToPosition(position) {
37284
+ const hoveredItemIndex = this.getHoveredItemIndex(position, this.items);
37285
+ const draggedItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
37286
+ const draggedItem = this.items[draggedItemIndex];
37287
+ if (this.deadZone && this.isInZone(position, this.deadZone)) {
37288
+ this.onChange(this.getItemsPositions());
37289
+ return;
37290
+ }
37291
+ else if (this.isInZone(position, {
37292
+ start: draggedItem.position,
37293
+ end: draggedItem.position + draggedItem.size,
37294
+ })) {
37295
+ this.deadZone = undefined;
37296
+ }
37297
+ if (draggedItemIndex === hoveredItemIndex) {
37298
+ this.onChange(this.getItemsPositions());
37299
+ return;
37300
+ }
37301
+ const startIndex = Math.min(draggedItemIndex, hoveredItemIndex);
37302
+ const endIndex = Math.max(draggedItemIndex, hoveredItemIndex);
37303
+ const direction = Math.sign(hoveredItemIndex - draggedItemIndex);
37304
+ let draggedItemMoveSize = 0;
37305
+ for (let i = startIndex; i <= endIndex; i++) {
37306
+ if (i === draggedItemIndex) {
37307
+ continue;
37308
+ }
37309
+ this.items[i].position -= direction * draggedItem.size;
37310
+ draggedItemMoveSize += this.items[i].size;
37311
+ }
37312
+ draggedItem.position += direction * draggedItemMoveSize;
37313
+ this.items.sort((item1, item2) => item1.position - item2.position);
37314
+ this.deadZone =
37315
+ direction > 0
37316
+ ? { start: position, end: draggedItem.position }
37317
+ : { start: draggedItem.position + draggedItem.size, end: position };
37318
+ this.onChange(this.getItemsPositions());
37319
+ }
37320
+ onMouseUp(ev) {
37321
+ if (ev.button !== 0) {
37322
+ this.onCancel();
37323
+ }
37324
+ ev.stopPropagation();
37325
+ ev.preventDefault();
37326
+ const targetItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
37327
+ this.onDragEnd(this.draggedItemId, targetItemIndex);
37328
+ this.stopEdgeScroll();
37329
+ return false;
37330
+ }
37331
+ startEdgeScroll(direction) {
37332
+ if (this.edgeScrollIntervalId)
37333
+ return;
37334
+ this.edgeScrollIntervalId = window.setInterval(() => {
37335
+ const offset = direction * 3;
37336
+ this.container.scroll += offset;
37337
+ }, 5);
37338
+ }
37339
+ stopEdgeScroll() {
37340
+ window.clearInterval(this.edgeScrollIntervalId);
37341
+ this.edgeScrollIntervalId = undefined;
37342
+ }
37343
+ /**
37344
+ * Get the index of the item the given mouse position is inside.
37345
+ * If the mouse is outside the container, return the first or last item index.
37346
+ */
37347
+ getHoveredItemIndex(mousePosition, items) {
37348
+ if (mousePosition <= this.minPosition)
37349
+ return 0;
37350
+ if (mousePosition >= this.maxPosition)
37351
+ return items.length - 1;
37352
+ return items.findIndex((item) => item.position + item.size >= mousePosition);
37353
+ }
37354
+ getItemsPositions() {
37355
+ const positions = {};
37356
+ for (let item of this.items) {
37357
+ if (item.id !== this.draggedItemId) {
37358
+ positions[item.id] = item.position - item.positionAtStart;
37359
+ continue;
37360
+ }
37361
+ const mouseOffset = this.currentMousePosition - this.initialMousePosition;
37362
+ let start = mouseOffset + this.scrollOffset;
37363
+ start = Math.max(this.minPosition - item.positionAtStart, start);
37364
+ start = Math.min(this.maxPosition - item.positionAtStart - item.size, start);
37365
+ positions[item.id] = start;
37366
+ }
37367
+ return positions;
37368
+ }
37369
+ isInZone(position, zone) {
37370
+ return position >= zone.start && position <= zone.end;
37371
+ }
37372
+ get scrollOffset() {
37373
+ return this.container.scroll - this.initialScroll;
37374
+ }
37375
+ destroy() {
37376
+ this.stopEdgeScroll();
37377
+ }
37378
+ }
37379
+ class ContainerWrapper {
37380
+ el;
37381
+ constructor(el) {
37382
+ this.el = el;
37383
+ }
37384
+ get containerRect() {
37385
+ return this.el.getBoundingClientRect();
37386
+ }
37387
+ }
37388
+ class VerticalContainer extends ContainerWrapper {
37389
+ get start() {
37390
+ return this.containerRect.top;
37391
+ }
37392
+ get end() {
37393
+ return this.containerRect.bottom;
37394
+ }
37395
+ get cssPositionProperty() {
37396
+ return "top";
37397
+ }
37398
+ get scroll() {
37399
+ return this.el.scrollTop;
37400
+ }
37401
+ set scroll(scroll) {
37402
+ this.el.scrollTop = scroll;
37403
+ }
37404
+ getMousePosition(ev) {
37405
+ return ev.clientY;
37406
+ }
37407
+ }
37408
+ class HorizontalContainer extends ContainerWrapper {
37409
+ get start() {
37410
+ return this.containerRect.left;
37411
+ }
37412
+ get end() {
37413
+ return this.containerRect.right;
37414
+ }
37415
+ get cssPositionProperty() {
37416
+ return "left";
37417
+ }
37418
+ get scroll() {
37419
+ return this.el.scrollLeft;
37420
+ }
37421
+ set scroll(scroll) {
37422
+ this.el.scrollLeft = scroll;
37423
+ }
37424
+ getMousePosition(ev) {
37425
+ return ev.clientX;
37426
+ }
37427
+ }
37428
+
36983
37429
  const arrowMap = {
36984
37430
  ArrowDown: "down",
36985
37431
  ArrowLeft: "left",
@@ -37030,6 +37476,7 @@ class SelectionInputStore extends SpreadsheetStore {
37030
37476
  "changeRange",
37031
37477
  "reset",
37032
37478
  "confirm",
37479
+ "updateColors",
37033
37480
  ];
37034
37481
  ranges = [];
37035
37482
  focusedRangeIndex = null;
@@ -37149,6 +37596,9 @@ class SelectionInputStore extends SpreadsheetStore {
37149
37596
  this.removeRangeByIndex(index);
37150
37597
  }
37151
37598
  }
37599
+ updateColors(colors) {
37600
+ this.colors = colors;
37601
+ }
37152
37602
  confirm() {
37153
37603
  for (const range of this.selectionInputs) {
37154
37604
  if (range.xc === "") {
@@ -37359,6 +37809,9 @@ css /* scss */ `
37359
37809
  right: 7px;
37360
37810
  top: 4px;
37361
37811
  }
37812
+ .o-drag-handle {
37813
+ cursor: move;
37814
+ }
37362
37815
  }
37363
37816
  .o-button {
37364
37817
  height: 28px;
@@ -37390,13 +37843,17 @@ class SelectionInput extends Component {
37390
37843
  class: { type: String, optional: true },
37391
37844
  onSelectionChanged: { type: Function, optional: true },
37392
37845
  onSelectionConfirmed: { type: Function, optional: true },
37846
+ onSelectionReordered: { type: Function, optional: true },
37847
+ onSelectionRemoved: { type: Function, optional: true },
37393
37848
  colors: { type: Array, optional: true, default: [] },
37394
37849
  };
37395
37850
  state = useState({
37396
37851
  isMissing: false,
37397
37852
  mode: "select-range",
37398
37853
  });
37854
+ dragAndDrop = useDragAndDropListItems();
37399
37855
  focusedInput = useRef("focusedInput");
37856
+ selectionRef = useRef("o-selection");
37400
37857
  store;
37401
37858
  get ranges() {
37402
37859
  return this.store.selectionInputs;
@@ -37424,8 +37881,45 @@ class SelectionInput extends Component {
37424
37881
  nextProps.ranges.join() !== this.store.selectionInputValues.join()) {
37425
37882
  this.store.resetWithRanges(nextProps.ranges);
37426
37883
  }
37884
+ if (nextProps.colors?.join() !== this.props.colors?.join() &&
37885
+ nextProps.colors?.join() !== this.store.colors.join()) {
37886
+ this.store.updateColors(nextProps.colors || []);
37887
+ }
37427
37888
  });
37428
37889
  }
37890
+ startDragAndDrop(rangeId, event) {
37891
+ if (event.button !== 0 || event.target.tagName === "SELECT") {
37892
+ return;
37893
+ }
37894
+ const rects = this.getRangeElementsRects();
37895
+ const draggableIds = this.ranges.map((range) => range.id);
37896
+ const draggableItems = draggableIds.map((id, index) => ({
37897
+ id: id.toString(),
37898
+ size: rects[index].height,
37899
+ position: rects[index].y,
37900
+ }));
37901
+ this.dragAndDrop.start("vertical", {
37902
+ draggedItemId: rangeId.toString(),
37903
+ initialMousePosition: event.clientY,
37904
+ items: draggableItems,
37905
+ containerEl: this.selectionRef.el,
37906
+ onDragEnd: (dimensionName, finalIndex) => {
37907
+ const originalIndex = draggableIds.findIndex((id) => id === rangeId);
37908
+ if (originalIndex === finalIndex) {
37909
+ return;
37910
+ }
37911
+ const draggedItems = [...draggableIds];
37912
+ draggedItems.splice(originalIndex, 1);
37913
+ draggedItems.splice(finalIndex, 0, rangeId);
37914
+ this.props.onSelectionReordered?.(this.store.selectionInputs.map((range) => draggedItems.indexOf(range.id)));
37915
+ this.props.onSelectionConfirmed?.();
37916
+ this.store.confirm();
37917
+ },
37918
+ });
37919
+ }
37920
+ getRangeElementsRects() {
37921
+ return Array.from(this.selectionRef.el.children).map((el) => el.getBoundingClientRect());
37922
+ }
37429
37923
  getColor(range) {
37430
37924
  if (!range.color) {
37431
37925
  return "";
@@ -37467,8 +37961,8 @@ class SelectionInput extends Component {
37467
37961
  this.store.addEmptyRange();
37468
37962
  }
37469
37963
  removeInput(rangeId) {
37470
- this.store.removeRange(rangeId);
37471
- this.triggerChange();
37964
+ const index = this.store.selectionInputs.findIndex((range) => range.id === rangeId);
37965
+ this.props.onSelectionRemoved?.(index);
37472
37966
  this.props.onSelectionConfirmed?.();
37473
37967
  }
37474
37968
  onInputChanged(rangeId, ev) {
@@ -37499,6 +37993,8 @@ class ChartDataSeries extends Component {
37499
37993
  ranges: Array,
37500
37994
  hasSingleRange: { type: Boolean, optional: true },
37501
37995
  onSelectionChanged: Function,
37996
+ onSelectionReordered: { type: Function, optional: true },
37997
+ onSelectionRemoved: { type: Function, optional: true },
37502
37998
  onSelectionConfirmed: Function,
37503
37999
  };
37504
38000
  get ranges() {
@@ -37607,11 +38103,11 @@ class GenericChartConfigPanel extends Component {
37607
38103
  datasetDispatchResult: undefined,
37608
38104
  labelsDispatchResult: undefined,
37609
38105
  });
37610
- dataSeriesRanges = [];
38106
+ dataSets = [];
37611
38107
  labelRange;
37612
38108
  chartTerms = ChartTerms;
37613
38109
  setup() {
37614
- this.dataSeriesRanges = this.props.definition.dataSets;
38110
+ this.dataSets = this.props.definition.dataSets;
37615
38111
  this.labelRange = this.props.definition.labelRange;
37616
38112
  }
37617
38113
  get errorMessages() {
@@ -37656,22 +38152,46 @@ class GenericChartConfigPanel extends Component {
37656
38152
  * button "confirm" is clicked
37657
38153
  */
37658
38154
  onDataSeriesRangesChanged(ranges) {
37659
- this.dataSeriesRanges = ranges.map((dataRange, i) => ({
37660
- ...this.dataSeriesRanges?.[i],
38155
+ this.dataSets = ranges.map((dataRange, i) => ({
38156
+ ...this.dataSets?.[i],
37661
38157
  dataRange,
37662
38158
  }));
37663
38159
  this.state.datasetDispatchResult = this.props.canUpdateChart(this.props.figureId, {
37664
- dataSets: this.dataSeriesRanges,
38160
+ dataSets: this.dataSets,
38161
+ });
38162
+ }
38163
+ onDataSeriesReordered(indexes) {
38164
+ const colorGenerator = getChartColorsGenerator({ dataSets: this.dataSets }, this.dataSets.length);
38165
+ const colors = this.dataSets.map((ds) => colorGenerator.next());
38166
+ this.dataSets = indexes.map((i) => ({
38167
+ backgroundColor: colors[i],
38168
+ ...this.dataSets[i],
38169
+ }));
38170
+ this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
38171
+ dataSets: this.dataSets,
38172
+ });
38173
+ }
38174
+ onDataSeriesRemoved(index) {
38175
+ const colorGenerator = getChartColorsGenerator({ dataSets: this.dataSets }, this.dataSets.length);
38176
+ const colors = this.dataSets.map((ds) => colorGenerator.next());
38177
+ this.dataSets = this.dataSets
38178
+ .map((ds, i) => ({
38179
+ backgroundColor: colors[i],
38180
+ ...ds,
38181
+ }))
38182
+ .filter((_, i) => i !== index);
38183
+ this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
38184
+ dataSets: this.dataSets,
37665
38185
  });
37666
38186
  }
37667
38187
  onDataSeriesConfirmed() {
37668
- this.dataSeriesRanges = spreadRange(this.env.model.getters, this.dataSeriesRanges);
38188
+ this.dataSets = spreadRange(this.env.model.getters, this.dataSets);
37669
38189
  this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
37670
- dataSets: this.dataSeriesRanges,
38190
+ dataSets: this.dataSets,
37671
38191
  });
37672
38192
  }
37673
38193
  getDataSeriesRanges() {
37674
- return this.dataSeriesRanges;
38194
+ return this.dataSets;
37675
38195
  }
37676
38196
  /**
37677
38197
  * Change the local labelRange. The model should be updated when the
@@ -37703,7 +38223,7 @@ class GenericChartConfigPanel extends Component {
37703
38223
  const getters = this.env.model.getters;
37704
38224
  const sheetId = getters.getActiveSheetId();
37705
38225
  const labelRange = createValidRange(getters, sheetId, this.labelRange);
37706
- const dataSets = createDataSets(getters, this.dataSeriesRanges, sheetId, this.props.definition.dataSetsHaveTitle);
38226
+ const dataSets = createDataSets(getters, this.dataSets, sheetId, this.props.definition.dataSetsHaveTitle);
37707
38227
  if (dataSets.length) {
37708
38228
  return dataSets[0].dataRange.zone.top + 1;
37709
38229
  }
@@ -37805,157 +38325,6 @@ class BadgeSelection extends Component {
37805
38325
  };
37806
38326
  }
37807
38327
 
37808
- /**
37809
- * Start listening to pointer events and apply the given callbacks.
37810
- *
37811
- * @returns A function to remove the listeners.
37812
- */
37813
- function startDnd(onMouseMove, onMouseUp, onMouseDown = () => { }) {
37814
- const removeListeners = () => {
37815
- window.removeEventListener("pointerdown", onMouseDown);
37816
- window.removeEventListener("pointerup", _onMouseUp);
37817
- window.removeEventListener("dragstart", _onDragStart);
37818
- window.removeEventListener("pointermove", onMouseMove);
37819
- window.removeEventListener("wheel", onMouseMove);
37820
- };
37821
- const _onMouseUp = (ev) => {
37822
- onMouseUp(ev);
37823
- removeListeners();
37824
- };
37825
- function _onDragStart(ev) {
37826
- ev.preventDefault();
37827
- }
37828
- window.addEventListener("pointerdown", onMouseDown);
37829
- window.addEventListener("pointerup", _onMouseUp);
37830
- window.addEventListener("dragstart", _onDragStart);
37831
- window.addEventListener("pointermove", onMouseMove);
37832
- // mouse wheel on window is by default a passive event.
37833
- // preventDefault() is not allowed in passive event handler.
37834
- // https://chromestatus.com/feature/6662647093133312
37835
- window.addEventListener("wheel", onMouseMove, { passive: false });
37836
- return removeListeners;
37837
- }
37838
- /**
37839
- * Function to be used during a pointerdown event, this function allows to
37840
- * perform actions related to the pointermove and pointerup events and adjusts the viewport
37841
- * when the new position related to the pointermove event is outside of it.
37842
- * Among inputs are two callback functions. First intended for actions performed during
37843
- * the pointermove event, it receives as parameters the current position of the pointermove
37844
- * (occurrence of the current column and the current row). Second intended for actions
37845
- * performed during the pointerup event.
37846
- */
37847
- function dragAndDropBeyondTheViewport(env, cbMouseMove, cbMouseUp, only = false) {
37848
- let timeOutId = null;
37849
- let currentEv;
37850
- let previousEv;
37851
- let startingEv;
37852
- let startingX;
37853
- let startingY;
37854
- const getters = env.model.getters;
37855
- const sheetId = getters.getActiveSheetId();
37856
- const position = gridOverlayPosition();
37857
- let colIndex;
37858
- let rowIndex;
37859
- const onMouseDown = (ev) => {
37860
- previousEv = ev;
37861
- startingEv = ev;
37862
- startingX = startingEv.clientX - position.left;
37863
- startingY = startingEv.clientY - position.top;
37864
- };
37865
- const onMouseMove = (ev) => {
37866
- currentEv = ev;
37867
- if (timeOutId) {
37868
- return;
37869
- }
37870
- const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates();
37871
- let { top, left, bottom, right } = getters.getActiveMainViewport();
37872
- let { scrollX, scrollY } = getters.getActiveSheetDOMScrollInfo();
37873
- const { xSplit, ySplit } = getters.getPaneDivisions(sheetId);
37874
- let canEdgeScroll = false;
37875
- let timeoutDelay = MAX_DELAY;
37876
- const x = currentEv.clientX - position.left;
37877
- colIndex = getters.getColIndex(x);
37878
- if (only !== "vertical") {
37879
- const previousX = previousEv.clientX - position.left;
37880
- const edgeScrollInfoX = getters.getEdgeScrollCol(x, previousX, startingX);
37881
- if (edgeScrollInfoX.canEdgeScroll) {
37882
- canEdgeScroll = true;
37883
- timeoutDelay = Math.min(timeoutDelay, edgeScrollInfoX.delay);
37884
- let newTarget;
37885
- switch (edgeScrollInfoX.direction) {
37886
- case "reset":
37887
- colIndex = xSplit;
37888
- newTarget = xSplit;
37889
- break;
37890
- case 1:
37891
- colIndex = right;
37892
- newTarget = left + 1;
37893
- break;
37894
- case -1:
37895
- colIndex = left - 1;
37896
- while (env.model.getters.isColHidden(sheetId, colIndex)) {
37897
- colIndex--;
37898
- }
37899
- newTarget = colIndex;
37900
- break;
37901
- }
37902
- scrollX = getters.getColDimensions(sheetId, newTarget).start - offsetCorrectionX;
37903
- }
37904
- }
37905
- const y = currentEv.clientY - position.top;
37906
- rowIndex = getters.getRowIndex(y);
37907
- if (only !== "horizontal") {
37908
- const previousY = previousEv.clientY - position.top;
37909
- const edgeScrollInfoY = getters.getEdgeScrollRow(y, previousY, startingY);
37910
- if (edgeScrollInfoY.canEdgeScroll) {
37911
- canEdgeScroll = true;
37912
- timeoutDelay = Math.min(timeoutDelay, edgeScrollInfoY.delay);
37913
- let newTarget;
37914
- switch (edgeScrollInfoY.direction) {
37915
- case "reset":
37916
- rowIndex = ySplit;
37917
- newTarget = ySplit;
37918
- break;
37919
- case 1:
37920
- rowIndex = bottom;
37921
- newTarget = top + edgeScrollInfoY.direction;
37922
- break;
37923
- case -1:
37924
- rowIndex = top - 1;
37925
- while (env.model.getters.isRowHidden(sheetId, rowIndex)) {
37926
- rowIndex--;
37927
- }
37928
- newTarget = rowIndex;
37929
- break;
37930
- }
37931
- scrollY = env.model.getters.getRowDimensions(sheetId, newTarget).start - offsetCorrectionY;
37932
- }
37933
- }
37934
- if (!canEdgeScroll) {
37935
- if (rowIndex === -1) {
37936
- rowIndex = y < 0 ? 0 : getters.getNumberRows(sheetId) - 1;
37937
- }
37938
- if (colIndex === -1 && x < 0) {
37939
- colIndex = x < 0 ? 0 : getters.getNumberCols(sheetId) - 1;
37940
- }
37941
- }
37942
- cbMouseMove(colIndex, rowIndex, currentEv);
37943
- if (canEdgeScroll) {
37944
- env.model.dispatch("SET_VIEWPORT_OFFSET", { offsetX: scrollX, offsetY: scrollY });
37945
- timeOutId = setTimeout(() => {
37946
- timeOutId = null;
37947
- onMouseMove(currentEv);
37948
- }, Math.round(timeoutDelay));
37949
- }
37950
- previousEv = currentEv;
37951
- };
37952
- const onMouseUp = () => {
37953
- clearTimeout(timeOutId);
37954
- cbMouseUp();
37955
- };
37956
- startDnd(onMouseMove, onMouseUp, onMouseDown);
37957
- }
37958
-
37959
38328
  const LINE_VERTICAL_PADDING = 1;
37960
38329
  const PICKER_PADDING = 8;
37961
38330
  const ITEM_BORDER_WIDTH = 1;
@@ -39237,9 +39606,9 @@ class GeoChartConfigPanel extends GenericChartConfigPanel {
39237
39606
  return this.getDataSeriesRanges().slice(0, 1);
39238
39607
  }
39239
39608
  onDataSeriesConfirmed() {
39240
- this.dataSeriesRanges = spreadRange(this.env.model.getters, this.dataSeriesRanges).slice(0, 1);
39609
+ this.dataSets = spreadRange(this.env.model.getters, this.dataSets).slice(0, 1);
39241
39610
  this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
39242
- dataSets: this.dataSeriesRanges,
39611
+ dataSets: this.dataSets,
39243
39612
  });
39244
39613
  }
39245
39614
  getLabelRangeOptions() {
@@ -39725,14 +40094,21 @@ class ChartTypePicker extends Component {
39725
40094
  class MainChartPanelStore extends SpreadsheetStore {
39726
40095
  mutators = ["activatePanel", "changeChartType"];
39727
40096
  panel = "configuration";
39728
- creationContext = {};
40097
+ creationContexts = {};
39729
40098
  activatePanel(panel) {
39730
40099
  this.panel = panel;
39731
40100
  }
39732
40101
  changeChartType(figureId, newDisplayType) {
39733
- this.creationContext = {
39734
- ...this.creationContext,
39735
- ...this.getters.getContextCreationChart(figureId),
40102
+ const currentCreationContext = this.getters.getContextCreationChart(figureId);
40103
+ const savedCreationContext = this.creationContexts[figureId] || {};
40104
+ let newRanges = currentCreationContext?.range;
40105
+ if (newRanges?.every((range, i) => deepEquals(range, savedCreationContext.range?.[i]))) {
40106
+ newRanges = Object.assign([], savedCreationContext.range, currentCreationContext?.range);
40107
+ }
40108
+ this.creationContexts[figureId] = {
40109
+ ...savedCreationContext,
40110
+ ...currentCreationContext,
40111
+ range: newRanges,
39736
40112
  };
39737
40113
  const sheetId = this.getters.getFigureSheetId(figureId);
39738
40114
  if (!sheetId) {
@@ -39748,12 +40124,8 @@ class MainChartPanelStore extends SpreadsheetStore {
39748
40124
  getChartDefinitionFromContextCreation(figureId, newDisplayType) {
39749
40125
  const newChartInfo = chartSubtypeRegistry.get(newDisplayType);
39750
40126
  const ChartClass = chartRegistry.get(newChartInfo.chartType);
39751
- const contextCreation = {
39752
- ...this.creationContext,
39753
- ...this.getters.getContextCreationChart(figureId),
39754
- };
39755
40127
  return {
39756
- ...ChartClass.getChartDefinitionFromContextCreation(contextCreation),
40128
+ ...ChartClass.getChartDefinitionFromContextCreation(this.creationContexts[figureId]),
39757
40129
  ...newChartInfo.subtypeDefinition,
39758
40130
  };
39759
40131
  }
@@ -41097,289 +41469,6 @@ class IconPicker extends Component {
41097
41469
  }
41098
41470
  }
41099
41471
 
41100
- function useDragAndDropListItems() {
41101
- let dndHelper;
41102
- const previousCursor = document.body.style.cursor;
41103
- let cleanupFns = [];
41104
- const cleanUp = () => {
41105
- dndHelper = undefined;
41106
- document.body.style.cursor = previousCursor;
41107
- cleanupFns.forEach((fn) => fn());
41108
- cleanupFns = [];
41109
- };
41110
- const start = (direction, args) => {
41111
- const onChange = () => {
41112
- document.body.style.cursor = "move";
41113
- if (!dndHelper)
41114
- return;
41115
- Object.assign(state.itemsStyle, dndHelper.getItemStyles());
41116
- args.onChange?.();
41117
- };
41118
- state.cancel = () => {
41119
- state.draggedItemId = undefined;
41120
- state.itemsStyle = {};
41121
- document.body.style.cursor = previousCursor;
41122
- args.onCancel?.();
41123
- cleanUp();
41124
- };
41125
- const onDragEnd = (itemId, indexAtEnd) => {
41126
- state.draggedItemId = undefined;
41127
- state.itemsStyle = {};
41128
- document.body.style.cursor = previousCursor;
41129
- args.onDragEnd?.(itemId, indexAtEnd);
41130
- cleanUp();
41131
- };
41132
- document.body.style.cursor = "move";
41133
- state.draggedItemId = args.draggedItemId;
41134
- const container = direction === "horizontal"
41135
- ? new HorizontalContainer(args.containerEl)
41136
- : new VerticalContainer(args.containerEl);
41137
- dndHelper = new DOMDndHelper({
41138
- ...args,
41139
- container,
41140
- onChange,
41141
- onDragEnd,
41142
- onCancel: state.cancel,
41143
- });
41144
- const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
41145
- cleanupFns.push(stopListening);
41146
- const onScroll = dndHelper.onScroll.bind(dndHelper);
41147
- args.containerEl.addEventListener("scroll", onScroll);
41148
- cleanupFns.push(() => args.containerEl.removeEventListener("scroll", onScroll));
41149
- cleanupFns.push(dndHelper.destroy.bind(dndHelper));
41150
- };
41151
- onWillUnmount(() => {
41152
- cleanUp();
41153
- });
41154
- const state = useState({
41155
- itemsStyle: {},
41156
- draggedItemId: undefined,
41157
- start,
41158
- cancel: () => { },
41159
- });
41160
- return state;
41161
- }
41162
- class DOMDndHelper {
41163
- draggedItemId;
41164
- items;
41165
- container;
41166
- initialMousePosition;
41167
- currentMousePosition;
41168
- initialScroll;
41169
- minPosition;
41170
- maxPosition;
41171
- edgeScrollIntervalId;
41172
- onChange;
41173
- onCancel;
41174
- onDragEnd;
41175
- /**
41176
- * The dead zone is an area in which the pointermove events are ignored.
41177
- *
41178
- * This is useful when swapping the dragged item with a larger item. After the swap,
41179
- * the mouse is still hovering on the item we just swapped with. In this case, we don't want
41180
- * a mouse move to trigger another swap the other way around, so we create a dead zone. We will clear
41181
- * the dead zone when the mouse leaves the swapped item.
41182
- */
41183
- deadZone;
41184
- constructor(args) {
41185
- this.items = args.items.map((item) => ({ ...item, positionAtStart: item.position }));
41186
- this.draggedItemId = args.draggedItemId;
41187
- this.container = args.container;
41188
- this.onChange = args.onChange;
41189
- this.onCancel = args.onCancel;
41190
- this.onDragEnd = args.onDragEnd;
41191
- this.initialMousePosition = args.initialMousePosition;
41192
- this.currentMousePosition = args.initialMousePosition;
41193
- this.initialScroll = this.container.scroll;
41194
- this.minPosition = this.items[0].position;
41195
- this.maxPosition =
41196
- this.items[this.items.length - 1].position + this.items[this.items.length - 1].size;
41197
- }
41198
- getItemStyles() {
41199
- const styles = {};
41200
- for (let item of this.items) {
41201
- styles[item.id] = this.getItemStyle(item.id);
41202
- }
41203
- return styles;
41204
- }
41205
- getItemStyle(itemId) {
41206
- const position = this.container.cssPositionProperty;
41207
- const style = {};
41208
- style.position = "relative";
41209
- style[position] = (this.getItemsPositions()[itemId] || 0) + "px";
41210
- style.transition = `${position} 0.5s`;
41211
- style["pointer-events"] = "none";
41212
- if (this.draggedItemId === itemId) {
41213
- style.transition = `${position} 0s`;
41214
- style["z-index"] = "1000";
41215
- }
41216
- return cssPropertiesToCss(style);
41217
- }
41218
- onScroll() {
41219
- this.moveDraggedItemToPosition(this.currentMousePosition + this.scrollOffset);
41220
- }
41221
- onMouseMove(ev) {
41222
- if (ev.button > 1) {
41223
- this.onCancel();
41224
- return;
41225
- }
41226
- const mousePosition = this.container.getMousePosition(ev);
41227
- this.currentMousePosition = mousePosition;
41228
- if (mousePosition < this.container.start || mousePosition > this.container.end) {
41229
- this.startEdgeScroll(mousePosition < this.container.start ? -1 : 1);
41230
- return;
41231
- }
41232
- else {
41233
- this.stopEdgeScroll();
41234
- }
41235
- this.moveDraggedItemToPosition(mousePosition + this.scrollOffset);
41236
- }
41237
- moveDraggedItemToPosition(position) {
41238
- const hoveredItemIndex = this.getHoveredItemIndex(position, this.items);
41239
- const draggedItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
41240
- const draggedItem = this.items[draggedItemIndex];
41241
- if (this.deadZone && this.isInZone(position, this.deadZone)) {
41242
- this.onChange(this.getItemsPositions());
41243
- return;
41244
- }
41245
- else if (this.isInZone(position, {
41246
- start: draggedItem.position,
41247
- end: draggedItem.position + draggedItem.size,
41248
- })) {
41249
- this.deadZone = undefined;
41250
- }
41251
- if (draggedItemIndex === hoveredItemIndex) {
41252
- this.onChange(this.getItemsPositions());
41253
- return;
41254
- }
41255
- const startIndex = Math.min(draggedItemIndex, hoveredItemIndex);
41256
- const endIndex = Math.max(draggedItemIndex, hoveredItemIndex);
41257
- const direction = Math.sign(hoveredItemIndex - draggedItemIndex);
41258
- let draggedItemMoveSize = 0;
41259
- for (let i = startIndex; i <= endIndex; i++) {
41260
- if (i === draggedItemIndex) {
41261
- continue;
41262
- }
41263
- this.items[i].position -= direction * draggedItem.size;
41264
- draggedItemMoveSize += this.items[i].size;
41265
- }
41266
- draggedItem.position += direction * draggedItemMoveSize;
41267
- this.items.sort((item1, item2) => item1.position - item2.position);
41268
- this.deadZone =
41269
- direction > 0
41270
- ? { start: position, end: draggedItem.position }
41271
- : { start: draggedItem.position + draggedItem.size, end: position };
41272
- this.onChange(this.getItemsPositions());
41273
- }
41274
- onMouseUp(ev) {
41275
- if (ev.button !== 0) {
41276
- this.onCancel();
41277
- }
41278
- ev.stopPropagation();
41279
- ev.preventDefault();
41280
- const targetItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
41281
- this.onDragEnd(this.draggedItemId, targetItemIndex);
41282
- this.stopEdgeScroll();
41283
- return false;
41284
- }
41285
- startEdgeScroll(direction) {
41286
- if (this.edgeScrollIntervalId)
41287
- return;
41288
- this.edgeScrollIntervalId = window.setInterval(() => {
41289
- const offset = direction * 3;
41290
- this.container.scroll += offset;
41291
- }, 5);
41292
- }
41293
- stopEdgeScroll() {
41294
- window.clearInterval(this.edgeScrollIntervalId);
41295
- this.edgeScrollIntervalId = undefined;
41296
- }
41297
- /**
41298
- * Get the index of the item the given mouse position is inside.
41299
- * If the mouse is outside the container, return the first or last item index.
41300
- */
41301
- getHoveredItemIndex(mousePosition, items) {
41302
- if (mousePosition <= this.minPosition)
41303
- return 0;
41304
- if (mousePosition >= this.maxPosition)
41305
- return items.length - 1;
41306
- return items.findIndex((item) => item.position + item.size >= mousePosition);
41307
- }
41308
- getItemsPositions() {
41309
- const positions = {};
41310
- for (let item of this.items) {
41311
- if (item.id !== this.draggedItemId) {
41312
- positions[item.id] = item.position - item.positionAtStart;
41313
- continue;
41314
- }
41315
- const mouseOffset = this.currentMousePosition - this.initialMousePosition;
41316
- let start = mouseOffset + this.scrollOffset;
41317
- start = Math.max(this.minPosition - item.positionAtStart, start);
41318
- start = Math.min(this.maxPosition - item.positionAtStart - item.size, start);
41319
- positions[item.id] = start;
41320
- }
41321
- return positions;
41322
- }
41323
- isInZone(position, zone) {
41324
- return position >= zone.start && position <= zone.end;
41325
- }
41326
- get scrollOffset() {
41327
- return this.container.scroll - this.initialScroll;
41328
- }
41329
- destroy() {
41330
- this.stopEdgeScroll();
41331
- }
41332
- }
41333
- class ContainerWrapper {
41334
- el;
41335
- constructor(el) {
41336
- this.el = el;
41337
- }
41338
- get containerRect() {
41339
- return this.el.getBoundingClientRect();
41340
- }
41341
- }
41342
- class VerticalContainer extends ContainerWrapper {
41343
- get start() {
41344
- return this.containerRect.top;
41345
- }
41346
- get end() {
41347
- return this.containerRect.bottom;
41348
- }
41349
- get cssPositionProperty() {
41350
- return "top";
41351
- }
41352
- get scroll() {
41353
- return this.el.scrollTop;
41354
- }
41355
- set scroll(scroll) {
41356
- this.el.scrollTop = scroll;
41357
- }
41358
- getMousePosition(ev) {
41359
- return ev.clientY;
41360
- }
41361
- }
41362
- class HorizontalContainer extends ContainerWrapper {
41363
- get start() {
41364
- return this.containerRect.left;
41365
- }
41366
- get end() {
41367
- return this.containerRect.right;
41368
- }
41369
- get cssPositionProperty() {
41370
- return "left";
41371
- }
41372
- get scroll() {
41373
- return this.el.scrollLeft;
41374
- }
41375
- set scroll(scroll) {
41376
- this.el.scrollLeft = scroll;
41377
- }
41378
- getMousePosition(ev) {
41379
- return ev.clientX;
41380
- }
41381
- }
41382
-
41383
41472
  /**
41384
41473
  * Manages an event listener on a ref. Useful for hooks that want to manage
41385
41474
  * event listeners, especially more than one. Prefer using t-on directly in
@@ -46283,7 +46372,7 @@ pivotRegistry.add("SPREADSHEET", {
46283
46372
  onIterationEndEvaluation: (pivot) => pivot.markAsDirtyForEvaluation(),
46284
46373
  dateGranularities: [...dateGranularities],
46285
46374
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
46286
- isMeasureCandidate: (field) => !["datetime", "boolean"].includes(field.type),
46375
+ isMeasureCandidate: (field) => field.type !== "boolean",
46287
46376
  isGroupable: () => true,
46288
46377
  });
46289
46378
 
@@ -46501,8 +46590,11 @@ class PivotSidePanelStore extends SpreadsheetStore {
46501
46590
  };
46502
46591
  }
46503
46592
  getUnusedGranularities(fields, definition) {
46504
- const { columns, rows } = definition;
46505
- const dateFields = columns.concat(rows).filter((dimension) => {
46593
+ const { columns, rows, measures } = definition;
46594
+ const dateFields = columns
46595
+ .concat(rows)
46596
+ .concat(measures)
46597
+ .filter((dimension) => {
46506
46598
  const fieldType = fields[dimension.fieldName]?.type;
46507
46599
  return fieldType === "date" || fieldType === "datetime";
46508
46600
  });
@@ -48774,7 +48866,7 @@ class GridComposer extends Component {
48774
48866
  return;
48775
48867
  }
48776
48868
  const sheetId = this.env.model.getters.getActiveSheetId();
48777
- const zone = this.env.model.getters.getSelectedZone();
48869
+ const zone = positionToZone(this.env.model.getters.getSelection().anchor.cell);
48778
48870
  const rect = this.env.model.getters.getVisibleRect(zone);
48779
48871
  if (!deepEquals(rect, this.rect) || sheetId !== this.composerStore.currentEditedCell.sheetId) {
48780
48872
  this.isCellReferenceVisible = true;
@@ -50610,13 +50702,23 @@ class GridRenderer {
50610
50702
  drawLayer(renderingContext, layer) {
50611
50703
  switch (layer) {
50612
50704
  case "Background":
50613
- const boxes = this.getGridBoxes();
50614
- this.drawBackground(renderingContext, boxes);
50615
- this.drawOverflowingCellBackground(renderingContext, boxes);
50616
- this.drawCellBackground(renderingContext, boxes);
50617
- this.drawBorders(renderingContext, boxes);
50618
- this.drawTexts(renderingContext, boxes);
50619
- this.drawIcon(renderingContext, boxes);
50705
+ this.drawGlobalBackground(renderingContext);
50706
+ for (const zone of this.getters.getAllActiveViewportsZones()) {
50707
+ const { ctx } = renderingContext;
50708
+ ctx.save();
50709
+ ctx.beginPath();
50710
+ const rect = this.getters.getVisibleRect(zone);
50711
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
50712
+ ctx.clip();
50713
+ const boxes = this.getGridBoxes(zone);
50714
+ this.drawBackground(renderingContext, boxes);
50715
+ this.drawOverflowingCellBackground(renderingContext, boxes);
50716
+ this.drawCellBackground(renderingContext, boxes);
50717
+ this.drawBorders(renderingContext, boxes);
50718
+ this.drawTexts(renderingContext, boxes);
50719
+ this.drawIcon(renderingContext, boxes);
50720
+ ctx.restore();
50721
+ }
50620
50722
  this.drawFrozenPanes(renderingContext);
50621
50723
  break;
50622
50724
  case "Headers":
@@ -50627,12 +50729,15 @@ class GridRenderer {
50627
50729
  break;
50628
50730
  }
50629
50731
  }
50630
- drawBackground(renderingContext, boxes) {
50631
- const { ctx, thinLineWidth } = renderingContext;
50732
+ drawGlobalBackground(renderingContext) {
50733
+ const { ctx } = renderingContext;
50632
50734
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
50633
50735
  // white background
50634
50736
  ctx.fillStyle = "#ffffff";
50635
50737
  ctx.fillRect(0, 0, width + CANVAS_SHIFT, height + CANVAS_SHIFT);
50738
+ }
50739
+ drawBackground(renderingContext, boxes) {
50740
+ const { ctx, thinLineWidth } = renderingContext;
50636
50741
  const areGridLinesVisible = !this.getters.isDashboard() &&
50637
50742
  this.getters.getGridLinesVisibility(this.getters.getActiveSheetId());
50638
50743
  const inset = areGridLinesVisible ? 0.1 * thinLineWidth : 0;
@@ -51063,7 +51168,7 @@ class GridRenderer {
51063
51168
  const position = { sheetId, col, row };
51064
51169
  const cell = this.getters.getEvaluatedCell(position);
51065
51170
  const showFormula = this.getters.shouldShowFormulas();
51066
- const { x, y, width, height } = this.getters.getVisibleRect(zone);
51171
+ const { x, y, width, height } = this.getters.getRect(zone);
51067
51172
  const { verticalAlign } = this.getters.getCellStyle(position);
51068
51173
  let style = this.getters.getCellComputedStyle(position);
51069
51174
  if (this.fingerprints.isEnabled) {
@@ -51188,12 +51293,16 @@ class GridRenderer {
51188
51293
  }
51189
51294
  return box;
51190
51295
  }
51191
- getGridBoxes() {
51296
+ getGridBoxes(zone) {
51192
51297
  const boxes = [];
51193
- const visibleCols = this.getters.getSheetViewVisibleCols();
51298
+ const visibleCols = this.getters
51299
+ .getSheetViewVisibleCols()
51300
+ .filter((col) => col >= zone.left && col <= zone.right);
51194
51301
  const left = visibleCols[0];
51195
51302
  const right = visibleCols[visibleCols.length - 1];
51196
- const visibleRows = this.getters.getSheetViewVisibleRows();
51303
+ const visibleRows = this.getters
51304
+ .getSheetViewVisibleRows()
51305
+ .filter((row) => row >= zone.top && row <= zone.bottom);
51197
51306
  const top = visibleRows[0];
51198
51307
  const bottom = visibleRows[visibleRows.length - 1];
51199
51308
  const viewport = { left, right, top, bottom };
@@ -53425,7 +53534,7 @@ class CellPlugin extends CorePlugin {
53425
53534
  /*
53426
53535
  * Reconstructs the original formula string based on new dependencies
53427
53536
  */
53428
- getFormulaString(sheetId, tokens, dependencies, useFixedReference = false) {
53537
+ getFormulaString(sheetId, tokens, dependencies, useBoundedReference = false) {
53429
53538
  if (!dependencies.length) {
53430
53539
  return concat(tokens.map((token) => token.value));
53431
53540
  }
@@ -53433,7 +53542,7 @@ class CellPlugin extends CorePlugin {
53433
53542
  return concat(tokens.map((token) => {
53434
53543
  if (token.type === "REFERENCE") {
53435
53544
  const range = dependencies[rangeIndex++];
53436
- return this.getters.getRangeString(range, sheetId, { useFixedReference });
53545
+ return this.getters.getRangeString(range, sheetId, { useBoundedReference });
53437
53546
  }
53438
53547
  return token.value;
53439
53548
  }));
@@ -53713,7 +53822,7 @@ class FormulaCellWithDependencies {
53713
53822
  if (token.type === "REFERENCE") {
53714
53823
  const index = rangeIndex++;
53715
53824
  return this.getRangeString(this.compiledFormula.dependencies[index], this.sheetId, {
53716
- useFixedReference: true,
53825
+ useBoundedReference: true,
53717
53826
  });
53718
53827
  }
53719
53828
  return token.value;
@@ -54050,7 +54159,7 @@ class ConditionalFormatPlugin extends CorePlugin {
54050
54159
  if (data.sheets) {
54051
54160
  for (let sheet of data.sheets) {
54052
54161
  if (this.cfRules[sheet.id]) {
54053
- sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
54162
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useBoundedReference: true }));
54054
54163
  }
54055
54164
  }
54056
54165
  }
@@ -54119,9 +54228,9 @@ class ConditionalFormatPlugin extends CorePlugin {
54119
54228
  // ---------------------------------------------------------------------------
54120
54229
  // Private
54121
54230
  // ---------------------------------------------------------------------------
54122
- mapToConditionalFormat(sheetId, cf, { useFixedReference } = { useFixedReference: false }) {
54231
+ mapToConditionalFormat(sheetId, cf, { useBoundedReference } = { useBoundedReference: false }) {
54123
54232
  const ranges = cf.ranges.map((range) => {
54124
- return this.getters.getRangeString(range, sheetId, { useFixedReference });
54233
+ return this.getters.getRangeString(range, sheetId, { useBoundedReference });
54125
54234
  });
54126
54235
  if (cf.rule.type !== "DataBarRule") {
54127
54236
  return {
@@ -54136,7 +54245,7 @@ class ConditionalFormatPlugin extends CorePlugin {
54136
54245
  ...cf.rule,
54137
54246
  rangeValues: cf.rule.rangeValues &&
54138
54247
  this.getters.getRangeString(cf.rule.rangeValues, sheetId, {
54139
- useFixedReference,
54248
+ useBoundedReference,
54140
54249
  }),
54141
54250
  },
54142
54251
  ranges,
@@ -54577,10 +54686,20 @@ class DataValidationPlugin extends CorePlugin {
54577
54686
  for (const sheet of data.sheets) {
54578
54687
  sheet.dataValidationRules = [];
54579
54688
  for (const rule of this.rules[sheet.id]) {
54580
- sheet.dataValidationRules.push({
54581
- ...rule,
54582
- ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useFixedReference: true })),
54583
- });
54689
+ const excelRule = {
54690
+ ...deepCopy(rule),
54691
+ ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useBoundedReference: true })),
54692
+ };
54693
+ if (rule.criterion.type === "isValueInRange") {
54694
+ excelRule.criterion.values = rule.criterion.values.map((value) => {
54695
+ const range = this.getters.getRangeFromSheetXC(sheet.id, value);
54696
+ return this.getters.getRangeString(range, sheet.id, {
54697
+ useBoundedReference: true,
54698
+ useFixedReference: true,
54699
+ });
54700
+ });
54701
+ }
54702
+ sheet.dataValidationRules.push(excelRule);
54584
54703
  }
54585
54704
  }
54586
54705
  }
@@ -56001,9 +56120,10 @@ class RangeAdapter {
56001
56120
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
56002
56121
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
56003
56122
  * @param options
56123
+ * @param options.useBoundedReference if true, the range will be returned with bounded row and column
56004
56124
  * @param options.useFixedReference if true, the range will be returned with fixed row and column
56005
56125
  */
56006
- getRangeString(range, forSheetId, options = { useFixedReference: false }) {
56126
+ getRangeString(range, forSheetId, options = { useBoundedReference: false, useFixedReference: false }) {
56007
56127
  if (!range) {
56008
56128
  return CellErrorType.InvalidReference;
56009
56129
  }
@@ -56106,13 +56226,13 @@ class RangeAdapter {
56106
56226
  /**
56107
56227
  * Get a Xc string that represent a part of a range
56108
56228
  */
56109
- getRangePartString(range, part, options = { useFixedReference: false }) {
56110
- const colFixed = range.parts && range.parts[part]?.colFixed ? "$" : "";
56229
+ getRangePartString(range, part, options = { useBoundedReference: false, useFixedReference: false }) {
56230
+ const colFixed = range.parts[part]?.colFixed || options.useFixedReference ? "$" : "";
56111
56231
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
56112
- const rowFixed = range.parts && range.parts[part]?.rowFixed ? "$" : "";
56232
+ const rowFixed = range.parts[part]?.rowFixed || options.useFixedReference ? "$" : "";
56113
56233
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
56114
56234
  let str = "";
56115
- if (range.isFullCol && !options.useFixedReference) {
56235
+ if (range.isFullCol && !options.useBoundedReference) {
56116
56236
  if (part === 0 && range.unboundedZone.hasHeader) {
56117
56237
  str = colFixed + col + rowFixed + row;
56118
56238
  }
@@ -56120,7 +56240,7 @@ class RangeAdapter {
56120
56240
  str = colFixed + col;
56121
56241
  }
56122
56242
  }
56123
- else if (range.isFullRow && !options.useFixedReference) {
56243
+ else if (range.isFullRow && !options.useBoundedReference) {
56124
56244
  if (part === 0 && range.unboundedZone.hasHeader) {
56125
56245
  str = colFixed + col + rowFixed + row;
56126
56246
  }
@@ -63295,6 +63415,7 @@ class Session extends EventBus {
63295
63415
  waitingUndoRedoAck = false;
63296
63416
  isReplayingInitialRevisions = false;
63297
63417
  processedRevisions = new Set();
63418
+ lastRevisionMessage = undefined;
63298
63419
  uuidGenerator = new UuidGenerator();
63299
63420
  lastLocalOperation;
63300
63421
  /**
@@ -63395,7 +63516,10 @@ class Session extends EventBus {
63395
63516
  * Notify the server that the user client left the collaborative session
63396
63517
  */
63397
63518
  async leave(data) {
63398
- if (data && Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
63519
+ if (data &&
63520
+ Object.keys(this.clients).length === 1 &&
63521
+ this.lastRevisionMessage &&
63522
+ this.lastRevisionMessage?.type !== "SNAPSHOT_CREATED") {
63399
63523
  await this.snapshot(data());
63400
63524
  }
63401
63525
  delete this.clients[this.clientId];
@@ -63616,6 +63740,7 @@ class Session extends EventBus {
63616
63740
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
63617
63741
  this.serverRevisionId = message.nextRevisionId;
63618
63742
  this.processedRevisions.add(message.nextRevisionId);
63743
+ this.lastRevisionMessage = message;
63619
63744
  this.sendPendingMessage();
63620
63745
  break;
63621
63746
  }
@@ -66846,8 +66971,12 @@ class GridSelectionPlugin extends UIPlugin {
66846
66971
  },
66847
66972
  ];
66848
66973
  handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
66974
+ const selection = pasteTarget[0];
66975
+ const col = selection.left;
66976
+ const row = selection.top;
66977
+ this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
66849
66978
  const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
66850
- let currentIndex = cmd.base;
66979
+ let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
66851
66980
  const resizingGroups = {};
66852
66981
  for (const element of toRemove) {
66853
66982
  const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
@@ -67138,22 +67267,33 @@ class InternalViewport {
67138
67267
  }
67139
67268
  /**
67140
67269
  *
67141
- * @param zone
67142
- * @returns Computes the absolute coordinate of a given zone inside the viewport
67270
+ * Computes the visible coordinates & dimensions of a given zone inside the viewport
67271
+ *
67143
67272
  */
67144
- getRect(zone) {
67273
+ getVisibleRect(zone) {
67145
67274
  const targetZone = intersection(zone, this);
67146
67275
  if (targetZone) {
67147
67276
  const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
67148
67277
  const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
67149
67278
  const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
67150
67279
  const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
67151
- return {
67152
- x,
67153
- y,
67154
- width,
67155
- height,
67156
- };
67280
+ return { x, y, width, height };
67281
+ }
67282
+ return undefined;
67283
+ }
67284
+ /**
67285
+ *
67286
+ * @returns Computes the absolute coordinates & dimensions of a given zone inside the viewport
67287
+ *
67288
+ */
67289
+ getFullRect(zone) {
67290
+ const targetZone = intersection(zone, this);
67291
+ if (targetZone) {
67292
+ const x = this.getters.getColRowOffset("COL", this.left, zone.left) + this.offsetCorrectionX;
67293
+ const y = this.getters.getColRowOffset("ROW", this.top, zone.top) + this.offsetCorrectionY;
67294
+ const width = this.getters.getColRowOffset("COL", zone.left, zone.right + 1);
67295
+ const height = this.getters.getColRowOffset("ROW", zone.top, zone.bottom + 1);
67296
+ return { x, y, width, height };
67157
67297
  }
67158
67298
  return undefined;
67159
67299
  }
@@ -67323,6 +67463,8 @@ class SheetViewPlugin extends UIPlugin {
67323
67463
  "isPositionVisible",
67324
67464
  "getColDimensionsInViewport",
67325
67465
  "getRowDimensionsInViewport",
67466
+ "getAllActiveViewportsZones",
67467
+ "getRect",
67326
67468
  ];
67327
67469
  viewports = {};
67328
67470
  /**
@@ -67706,16 +67848,27 @@ class SheetViewPlugin extends UIPlugin {
67706
67848
  getVisibleRectWithoutHeaders(zone) {
67707
67849
  const sheetId = this.getters.getActiveSheetId();
67708
67850
  const viewportRects = this.getSubViewports(sheetId)
67709
- .map((viewport) => viewport.getRect(zone))
67851
+ .map((viewport) => viewport.getVisibleRect(zone))
67710
67852
  .filter(isDefined);
67711
67853
  if (viewportRects.length === 0) {
67712
67854
  return { x: 0, y: 0, width: 0, height: 0 };
67713
67855
  }
67714
- const x = Math.min(...viewportRects.map((rect) => rect.x));
67715
- const y = Math.min(...viewportRects.map((rect) => rect.y));
67716
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
67717
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
67718
- return { x, y, width, height };
67856
+ return this.recomposeRect(viewportRects);
67857
+ }
67858
+ /**
67859
+ * Computes the actual size and position (:Rect) of the zone on the canvas
67860
+ * regardless of the viewport dimensions.
67861
+ */
67862
+ getRect(zone) {
67863
+ const sheetId = this.getters.getActiveSheetId();
67864
+ const viewportRects = this.getSubViewports(sheetId)
67865
+ .map((viewport) => viewport.getFullRect(zone))
67866
+ .filter(isDefined);
67867
+ if (viewportRects.length === 0) {
67868
+ return { x: 0, y: 0, width: 0, height: 0 };
67869
+ }
67870
+ const rect = this.recomposeRect(viewportRects);
67871
+ return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
67719
67872
  }
67720
67873
  /**
67721
67874
  * Returns the position of the MainViewport relatively to the start of the grid (without headers)
@@ -67759,6 +67912,10 @@ class SheetViewPlugin extends UIPlugin {
67759
67912
  end: start + (isRowHidden ? 0 : size),
67760
67913
  };
67761
67914
  }
67915
+ getAllActiveViewportsZones() {
67916
+ const sheetId = this.getters.getActiveSheetId();
67917
+ return this.getSubViewports(sheetId);
67918
+ }
67762
67919
  // ---------------------------------------------------------------------------
67763
67920
  // Private
67764
67921
  // ---------------------------------------------------------------------------
@@ -67949,6 +68106,13 @@ class SheetViewPlugin extends UIPlugin {
67949
68106
  const height = this.sheetViewHeight + this.gridOffsetY;
67950
68107
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
67951
68108
  }
68109
+ recomposeRect(viewportRects) {
68110
+ const x = Math.min(...viewportRects.map((rect) => rect.x));
68111
+ const y = Math.min(...viewportRects.map((rect) => rect.y));
68112
+ const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
68113
+ const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
68114
+ return { x, y, width, height };
68115
+ }
67952
68116
  }
67953
68117
 
67954
68118
  class HeaderPositionsUIPlugin extends UIPlugin {
@@ -69790,6 +69954,13 @@ class BorderEditorWidget extends Component {
69790
69954
  currentStyle: DEFAULT_BORDER_DESC.style,
69791
69955
  currentPosition: undefined,
69792
69956
  });
69957
+ setup() {
69958
+ onWillUpdateProps((newProps) => {
69959
+ if (!newProps.showBorderEditor) {
69960
+ this.state.currentPosition = undefined;
69961
+ }
69962
+ });
69963
+ }
69793
69964
  get borderEditorAnchorRect() {
69794
69965
  const button = this.borderEditorButtonRef.el;
69795
69966
  const buttonRect = button.getBoundingClientRect();
@@ -75056,6 +75227,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
75056
75227
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
75057
75228
 
75058
75229
 
75059
- __info__.version = "18.2.0-alpha.2";
75060
- __info__.date = "2025-01-15T08:06:32.137Z";
75061
- __info__.hash = "4f96c47";
75230
+ __info__.version = "18.2.0-alpha.4";
75231
+ __info__.date = "2025-01-29T06:30:12.773Z";
75232
+ __info__.hash = "6838c26";