@odoo/o-spreadsheet 16.3.0-alpha.1 → 16.3.0-alpha.2

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.
@@ -224,7 +224,6 @@ const MENU_ITEM_HEIGHT = 28;
224
224
  const MENU_SEPARATOR_BORDER_WIDTH = 1;
225
225
  const MENU_SEPARATOR_PADDING = 5;
226
226
  const MENU_SEPARATOR_HEIGHT = MENU_SEPARATOR_BORDER_WIDTH + 2 * MENU_SEPARATOR_PADDING;
227
- const FIGURE_BORDER_SIZE = 1;
228
227
  // Fonts
229
228
  const DEFAULT_FONT_WEIGHT = "400";
230
229
  const DEFAULT_FONT_SIZE = 10;
@@ -244,6 +243,8 @@ const DEFAULT_REVISION_ID = "START_REVISION";
244
243
  // Figure
245
244
  const DEFAULT_FIGURE_HEIGHT = 335;
246
245
  const DEFAULT_FIGURE_WIDTH = 536;
246
+ const FIGURE_BORDER_WIDTH = 1;
247
+ const MIN_FIG_SIZE = 80;
247
248
  // Chart
248
249
  const MAX_CHAR_LABEL = 20;
249
250
  const FIGURE_ID_SPLITTER = "??";
@@ -254,7 +255,6 @@ const DEFAULT_SCORECARD_BASELINE_MODE = "difference";
254
255
  const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#00A04A";
255
256
  const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#DC6965";
256
257
  const LINE_FILL_TRANSPARENCY = 0.4;
257
- const MIN_FIG_SIZE = 80;
258
258
  // session
259
259
  const DEBOUNCE_TIME = 200;
260
260
  const MESSAGE_VERSION = 1;
@@ -276,7 +276,8 @@ var ComponentsImportance;
276
276
  ComponentsImportance[ComponentsImportance["TopBarComposer"] = 21] = "TopBarComposer";
277
277
  ComponentsImportance[ComponentsImportance["IconPicker"] = 25] = "IconPicker";
278
278
  ComponentsImportance[ComponentsImportance["Popover"] = 30] = "Popover";
279
- ComponentsImportance[ComponentsImportance["ChartAnchor"] = 1000] = "ChartAnchor";
279
+ ComponentsImportance[ComponentsImportance["FigureAnchor"] = 1000] = "FigureAnchor";
280
+ ComponentsImportance[ComponentsImportance["FigureSnapLine"] = 1001] = "FigureSnapLine";
280
281
  })(ComponentsImportance || (ComponentsImportance = {}));
281
282
  const DEFAULT_SHEETVIEW_SIZE = 1000;
282
283
  const MAXIMAL_FREEZABLE_RATIO = 0.85;
@@ -3051,6 +3052,9 @@ function cellTextStyleToCss(style) {
3051
3052
  }
3052
3053
  return attributes;
3053
3054
  }
3055
+ /**
3056
+ * Transform CSS properties into a CSS string.
3057
+ */
3054
3058
  function cssPropertiesToCss(attributes) {
3055
3059
  const str = Object.entries(attributes)
3056
3060
  .filter(([attName, attValue]) => attValue !== undefined)
@@ -5071,6 +5075,10 @@ function usePopoverContainer() {
5071
5075
  function rectIntersection(rect1, rect2) {
5072
5076
  return zoneToRect(intersection(rectToZone(rect1), rectToZone(rect2)));
5073
5077
  }
5078
+ /** Compute the union of the rectangles, ie. the smallest rectangle that contain them all */
5079
+ function rectUnion(...rects) {
5080
+ return zoneToRect(union(...rects.map(rectToZone)));
5081
+ }
5074
5082
  function rectToZone(rect) {
5075
5083
  return {
5076
5084
  left: rect.x,
@@ -7800,6 +7808,10 @@ css /* scss */ `
7800
7808
  }
7801
7809
  `;
7802
7810
  class ChartFigure extends Component {
7811
+ onDoubleClick() {
7812
+ this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figure.id });
7813
+ this.env.openSidePanel("ChartPanel");
7814
+ }
7803
7815
  get chartType() {
7804
7816
  return this.env.model.getters.getChartType(this.props.figure.id);
7805
7817
  }
@@ -18837,7 +18849,7 @@ css /*SCSS*/ `
18837
18849
  box-sizing: content-box;
18838
18850
 
18839
18851
  .o-fig-anchor {
18840
- z-index: ${ComponentsImportance.ChartAnchor};
18852
+ z-index: ${ComponentsImportance.FigureAnchor};
18841
18853
  position: absolute;
18842
18854
  width: ${ANCHOR_SIZE}px;
18843
18855
  height: ${ANCHOR_SIZE}px;
@@ -19704,13 +19716,6 @@ css /* scss */ `
19704
19716
  pointer-events: none;
19705
19717
  }
19706
19718
  }
19707
-
19708
- .o-topbar-composer .o-composer:empty:not(:focus)::before {
19709
- /* svg free of use from https://uxwing.com/formula-fx-icon/ */
19710
- content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'%3E%3Cpath d='m28 34-4 5v2h10l-6 40c-4 22-6 28-7 30-2 2-3 3-5 3-3 0-7-2-9-4H4c-2 2-4 4-4 7s4 6 8 6 9-2 15-8c8-7 13-17 18-39l7-35 13-1 3-6H49c4-23 7-27 11-27 2 0 5 2 8 6h4c1-1 4-4 4-7 0-2-3-6-9-6-5 0-13 4-20 10-6 7-9 14-11 24h-8zm41 16c4-5 7-7 8-7s2 1 5 9l3 12c-7 11-12 17-16 17l-3-1-2-1c-3 0-6 3-6 7s3 7 7 7c6 0 12-6 22-23l3 10c3 9 6 13 10 13 5 0 11-4 18-15l-3-4c-4 6-7 8-8 8-2 0-4-3-6-10l-5-15 8-10 6-4 3 1 3 2c2 0 6-3 6-7s-2-7-6-7c-6 0-11 5-21 20l-2-6c-3-9-5-14-9-14-5 0-12 6-18 15l3 3z' fill='%23BDBDBD' /%3E%3C/svg%3E");
19711
- position: relative;
19712
- top: 20%;
19713
- }
19714
19719
  `;
19715
19720
  class Composer extends Component {
19716
19721
  constructor() {
@@ -20438,6 +20443,266 @@ FilterIconsOverlay.props = {
20438
20443
  gridPosition: { type: Object, optional: true },
20439
20444
  };
20440
20445
 
20446
+ /**
20447
+ * Transform a figure with coordinates from the model, to coordinates as they are shown on the screen,
20448
+ * taking into account the scroll position of the active sheet and the frozen panes.
20449
+ */
20450
+ function internalFigureToScreen(getters, fig) {
20451
+ return { ...fig, ...internalToScreenCoordinates(getters, { x: fig.x, y: fig.y }) };
20452
+ }
20453
+ /**
20454
+ * Transform a figure with coordinates as they are shown on the screen, to coordinates as they are in the model,
20455
+ * taking into account the scroll position of the active sheet and the frozen panes.
20456
+ *
20457
+ * Note that this isn't exactly the reverse operation as internalFigureToScreen, because the figure will always be on top
20458
+ * of the frozen panes.
20459
+ */
20460
+ function screenFigureToInternal(getters, fig) {
20461
+ return { ...fig, ...screenCoordinatesToInternal(getters, { x: fig.x, y: fig.y }) };
20462
+ }
20463
+ function internalToScreenCoordinates(getters, { x, y }) {
20464
+ const { x: viewportX, y: viewportY } = getters.getMainViewportCoordinates();
20465
+ const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
20466
+ x = x < viewportX ? x : x - scrollX;
20467
+ y = y < viewportY ? y : y - scrollY;
20468
+ return { x, y };
20469
+ }
20470
+ function screenCoordinatesToInternal(getters, { x, y }) {
20471
+ const { x: viewportX, y: viewportY } = getters.getMainViewportCoordinates();
20472
+ const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
20473
+ x = viewportX && x < viewportX ? x : x + scrollX;
20474
+ y = viewportY && y < viewportY ? y : y + scrollY;
20475
+ return { x, y };
20476
+ }
20477
+
20478
+ function dragFigureForMove({ x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, initialFigure, { x: viewportX, y: viewportY }, { scrollX, scrollY }) {
20479
+ const minX = viewportX ? 0 : -scrollX;
20480
+ const minY = viewportY ? 0 : -scrollY;
20481
+ let deltaX = mouseX - mouseInitialX;
20482
+ const newX = Math.max(initialFigure.x + deltaX, minX);
20483
+ let deltaY = mouseY - mouseInitialY;
20484
+ const newY = Math.max(initialFigure.y + deltaY, minY);
20485
+ return { ...initialFigure, x: newX, y: newY };
20486
+ }
20487
+ function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, keepRatio, minFigSize) {
20488
+ let { x, y, width, height } = initialFigure;
20489
+ if (keepRatio && dirX != 0 && dirY != 0) {
20490
+ const deltaX = Math.min(dirX * (mouseInitialX - mouseX), initialFigure.width - minFigSize);
20491
+ const deltaY = Math.min(dirY * (mouseInitialY - mouseY), initialFigure.height - minFigSize);
20492
+ const fraction = Math.min(deltaX / initialFigure.width, deltaY / initialFigure.height);
20493
+ width = initialFigure.width * (1 - fraction);
20494
+ height = initialFigure.height * (1 - fraction);
20495
+ if (dirX < 0) {
20496
+ x = initialFigure.x + initialFigure.width * fraction;
20497
+ }
20498
+ if (dirY < 0) {
20499
+ y = initialFigure.y + initialFigure.height * fraction;
20500
+ }
20501
+ }
20502
+ else {
20503
+ const deltaX = Math.max(dirX * (mouseX - mouseInitialX), minFigSize - initialFigure.width);
20504
+ const deltaY = Math.max(dirY * (mouseY - mouseInitialY), minFigSize - initialFigure.height);
20505
+ width = initialFigure.width + deltaX;
20506
+ height = initialFigure.height + deltaY;
20507
+ if (dirX < 0) {
20508
+ x = initialFigure.x - deltaX;
20509
+ }
20510
+ if (dirY < 0) {
20511
+ y = initialFigure.y - deltaY;
20512
+ }
20513
+ }
20514
+ return { ...initialFigure, x, y, width, height };
20515
+ }
20516
+
20517
+ const SNAP_MARGIN = 5;
20518
+ /**
20519
+ * Try to snap the given figure to other figures when moving the figure, and return the snapped
20520
+ * figure and the possible snap lines, if any were found
20521
+ */
20522
+ function snapForMove(getters, figureToSnap, otherFigures) {
20523
+ const snappedFigure = { ...figureToSnap };
20524
+ const verticalSnapLine = getSnapLine(getters, snappedFigure, ["hCenter", "right", "left"], otherFigures, ["hCenter", "right", "left"]);
20525
+ const horizontalSnapLine = getSnapLine(getters, snappedFigure, ["vCenter", "bottom", "top"], otherFigures, ["vCenter", "bottom", "top"]);
20526
+ const { y: viewportY, x: viewportX } = getters.getMainViewportCoordinates();
20527
+ const { scrollY, scrollX } = getters.getActiveSheetScrollInfo();
20528
+ // If the snap cause the figure to change pane, we need to also apply the scroll as an offset
20529
+ if (horizontalSnapLine) {
20530
+ snappedFigure.y -= horizontalSnapLine.snapOffset;
20531
+ const isBaseFigFrozenY = figureToSnap.y < viewportY;
20532
+ const isSnappedFrozenY = snappedFigure.y < viewportY;
20533
+ if (isBaseFigFrozenY && !isSnappedFrozenY)
20534
+ snappedFigure.y += scrollY;
20535
+ else if (!isBaseFigFrozenY && isSnappedFrozenY)
20536
+ snappedFigure.y -= scrollY;
20537
+ }
20538
+ if (verticalSnapLine) {
20539
+ snappedFigure.x -= verticalSnapLine.snapOffset;
20540
+ const isBaseFigFrozenX = figureToSnap.x < viewportX;
20541
+ const isSnappedFrozenX = snappedFigure.x < viewportX;
20542
+ if (isBaseFigFrozenX && !isSnappedFrozenX)
20543
+ snappedFigure.x += scrollX;
20544
+ else if (!isBaseFigFrozenX && isSnappedFrozenX)
20545
+ snappedFigure.x -= scrollX;
20546
+ }
20547
+ return { snappedFigure, verticalSnapLine, horizontalSnapLine };
20548
+ }
20549
+ /**
20550
+ * Try to snap the given figure to the other figures when resizing the figure, and return the snapped
20551
+ * figure and the possible snap lines, if any were found
20552
+ */
20553
+ function snapForResize(getters, resizeDirX, resizeDirY, figureToSnap, otherFigures) {
20554
+ const snappedFigure = { ...figureToSnap };
20555
+ // Vertical snap line
20556
+ const verticalSnapLine = getSnapLine(getters, snappedFigure, [resizeDirX === -1 ? "left" : "right"], otherFigures, ["right", "left"]);
20557
+ if (verticalSnapLine) {
20558
+ if (resizeDirX === 1) {
20559
+ snappedFigure.width -= verticalSnapLine.snapOffset;
20560
+ }
20561
+ else if (resizeDirX === -1) {
20562
+ snappedFigure.x -= verticalSnapLine.snapOffset;
20563
+ snappedFigure.width += verticalSnapLine.snapOffset;
20564
+ }
20565
+ }
20566
+ // Horizontal snap line
20567
+ const horizontalSnapLine = getSnapLine(getters, snappedFigure, [resizeDirY === -1 ? "top" : "bottom"], otherFigures, ["bottom", "top"]);
20568
+ if (horizontalSnapLine) {
20569
+ if (resizeDirY === 1) {
20570
+ snappedFigure.height -= horizontalSnapLine.snapOffset;
20571
+ }
20572
+ else if (resizeDirY === -1) {
20573
+ snappedFigure.y -= horizontalSnapLine.snapOffset;
20574
+ snappedFigure.height += horizontalSnapLine.snapOffset;
20575
+ }
20576
+ }
20577
+ snappedFigure.x = Math.round(snappedFigure.x);
20578
+ snappedFigure.y = Math.round(snappedFigure.y);
20579
+ snappedFigure.height = Math.round(snappedFigure.height);
20580
+ snappedFigure.width = Math.round(snappedFigure.width);
20581
+ return { snappedFigure, verticalSnapLine, horizontalSnapLine };
20582
+ }
20583
+ /**
20584
+ * Get the position of snap axes for the given figure
20585
+ *
20586
+ * @param figure the figure
20587
+ * @param axesTypes the list of axis types to return the positions of
20588
+ */
20589
+ function getVisibleAxes(getters, figure, axesTypes) {
20590
+ const axes = axesTypes.map((axisType) => getAxis(figure, axisType));
20591
+ return axes
20592
+ .filter((axis) => isAxisVisible(getters, figure, axis))
20593
+ .map((axis) => getAxisScreenPosition(getters, figure, axis));
20594
+ }
20595
+ /**
20596
+ * We need two positions for the figure axis :
20597
+ * - the position (core) of the axis in the figure. This is used to know whether or not the axis is
20598
+ * displayed, or is hidden by the scroll/the frozen panes
20599
+ * - the position in the screen, which is used to find snap matches. We cannot use the core position for this,
20600
+ * because figures partially in frozen panes aren't displayed at their actual coordinates
20601
+ */
20602
+ function getAxisScreenPosition(getters, figure, figureAxis) {
20603
+ const screenFigure = internalFigureToScreen(getters, figure);
20604
+ return getAxis(screenFigure, figureAxis.axisType);
20605
+ }
20606
+ function isAxisVisible(getters, figure, axis) {
20607
+ const { x: mainViewportX, y: mainViewportY } = getters.getMainViewportCoordinates();
20608
+ const axisStartEndPositions = [];
20609
+ switch (axis.axisType) {
20610
+ case "top":
20611
+ case "bottom":
20612
+ case "vCenter":
20613
+ if (figure.y < mainViewportY)
20614
+ return true;
20615
+ axisStartEndPositions.push({ x: figure.x, y: axis.position });
20616
+ axisStartEndPositions.push({ x: figure.x + figure.width, y: axis.position });
20617
+ break;
20618
+ case "left":
20619
+ case "right":
20620
+ case "hCenter":
20621
+ if (figure.x < mainViewportX)
20622
+ return true;
20623
+ axisStartEndPositions.push({ x: axis.position, y: figure.y });
20624
+ axisStartEndPositions.push({ x: axis.position, y: figure.y + figure.height });
20625
+ break;
20626
+ }
20627
+ return axisStartEndPositions.some(getters.isPositionVisible);
20628
+ }
20629
+ /**
20630
+ * Get a snap line for the given figure, if the figure can snap to any other figure
20631
+ *
20632
+ * @param figureToSnap figure to get the snap line for
20633
+ * @param figAxesTypes figure axes of the given figure to be considered to find a snap line
20634
+ * @param otherFigures figures to match against the snapped figure to find a snap line
20635
+ * @param otherAxesTypes figure axes of the other figures to be considered to find a snap line
20636
+ */
20637
+ function getSnapLine(getters, figureToSnap, figAxesTypes, otherFigures, otherAxesTypes) {
20638
+ const axesOfFigure = getVisibleAxes(getters, figureToSnap, figAxesTypes);
20639
+ let closestMatch = undefined;
20640
+ for (const otherFigure of otherFigures) {
20641
+ const axesOfOtherFig = getVisibleAxes(getters, otherFigure, otherAxesTypes);
20642
+ for (const axisOfFigure of axesOfFigure) {
20643
+ for (const axisOfOtherFig of axesOfOtherFig) {
20644
+ if (!canSnap(axisOfFigure.position, axisOfOtherFig.position))
20645
+ continue;
20646
+ const snapOffset = axisOfFigure.position - axisOfOtherFig.position;
20647
+ if (closestMatch && snapOffset === closestMatch.snapOffset) {
20648
+ closestMatch.matchedFigIds.push(otherFigure.id);
20649
+ }
20650
+ else if (!closestMatch || Math.abs(snapOffset) <= Math.abs(closestMatch.snapOffset)) {
20651
+ closestMatch = {
20652
+ matchedFigIds: [otherFigure.id],
20653
+ snapOffset,
20654
+ snappedAxisType: axisOfFigure.axisType,
20655
+ position: axisOfOtherFig.position,
20656
+ };
20657
+ }
20658
+ }
20659
+ }
20660
+ }
20661
+ return closestMatch;
20662
+ }
20663
+ /** Check if two axes are close enough to snap */
20664
+ function canSnap(axisPosition1, axisPosition2) {
20665
+ return Math.abs(axisPosition1 - axisPosition2) <= SNAP_MARGIN;
20666
+ }
20667
+ function getAxis(fig, axisType) {
20668
+ let position = 0;
20669
+ switch (axisType) {
20670
+ case "top":
20671
+ position = fig.y;
20672
+ break;
20673
+ case "bottom":
20674
+ position = fig.y + fig.height - FIGURE_BORDER_WIDTH;
20675
+ break;
20676
+ case "vCenter":
20677
+ position = fig.y + Math.floor(fig.height / 2) - FIGURE_BORDER_WIDTH;
20678
+ break;
20679
+ case "left":
20680
+ position = fig.x;
20681
+ break;
20682
+ case "right":
20683
+ position = fig.x + fig.width - FIGURE_BORDER_WIDTH;
20684
+ break;
20685
+ case "hCenter":
20686
+ position = fig.x + Math.floor(fig.width / 2) - FIGURE_BORDER_WIDTH;
20687
+ break;
20688
+ }
20689
+ return { position, axisType: axisType };
20690
+ }
20691
+
20692
+ css /*SCSS*/ `
20693
+ .o-figure-snap-line {
20694
+ position: relative;
20695
+ z-index: ${ComponentsImportance.FigureSnapLine};
20696
+ &.vertical {
20697
+ width: 0px;
20698
+ border-left: 1px dashed black;
20699
+ }
20700
+ &.horizontal {
20701
+ border-top: 1px dashed black;
20702
+ height: 0px;
20703
+ }
20704
+ }
20705
+ `;
20441
20706
  /**
20442
20707
  * Each figure ⭐ is positioned inside a container `div` placed and sized
20443
20708
  * according to the split pane the figure is part of, or a separate container for the figure
@@ -20502,11 +20767,9 @@ class FiguresContainer extends Component {
20502
20767
  constructor() {
20503
20768
  super(...arguments);
20504
20769
  this.dnd = useState({
20505
- figId: undefined,
20506
- x: 0,
20507
- y: 0,
20508
- width: 0,
20509
- height: 0,
20770
+ draggedFigure: undefined,
20771
+ horizontalSnap: undefined,
20772
+ verticalSnap: undefined,
20510
20773
  });
20511
20774
  }
20512
20775
  setup() {
@@ -20522,9 +20785,11 @@ class FiguresContainer extends Component {
20522
20785
  });
20523
20786
  }
20524
20787
  getVisibleFigures() {
20788
+ var _a;
20525
20789
  const visibleFigures = this.env.model.getters.getVisibleFigures();
20526
- if (this.dnd.figId && !visibleFigures.some((figure) => figure.id === this.dnd.figId)) {
20527
- visibleFigures.push(this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), this.dnd.figId));
20790
+ if (this.dnd.draggedFigure &&
20791
+ !visibleFigures.some((figure) => { var _a; return figure.id === ((_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id); })) {
20792
+ visibleFigures.push(this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), (_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id));
20528
20793
  }
20529
20794
  return visibleFigures;
20530
20795
  }
@@ -20547,7 +20812,7 @@ class FiguresContainer extends Component {
20547
20812
  });
20548
20813
  }
20549
20814
  }
20550
- if (this.dnd.figId) {
20815
+ if (this.dnd.draggedFigure) {
20551
20816
  containers.push({
20552
20817
  type: "dnd",
20553
20818
  figures: [this.getDndFigure()],
@@ -20558,19 +20823,25 @@ class FiguresContainer extends Component {
20558
20823
  return containers;
20559
20824
  }
20560
20825
  getContainerStyle(container) {
20561
- const { width: viewWidth, height: viewHeight } = this.env.model.getters.getMainViewportRect();
20562
- const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
20563
- const left = ["bottomRight", "topRight"].includes(container) ? viewportX : 0;
20564
- const width = viewWidth - left;
20565
- const top = ["bottomRight", "bottomLeft"].includes(container) ? viewportY : 0;
20566
- const height = viewHeight - top;
20826
+ return this.rectToCss(this.getContainerRect(container));
20827
+ }
20828
+ rectToCss(rect) {
20567
20829
  return cssPropertiesToCss({
20568
- left: `${left}px`,
20569
- top: `${top}px`,
20570
- width: `${width}px`,
20571
- height: `${height}px`,
20830
+ left: `${rect.x}px`,
20831
+ top: `${rect.y}px`,
20832
+ width: `${rect.width}px`,
20833
+ height: `${rect.height}px`,
20572
20834
  });
20573
20835
  }
20836
+ getContainerRect(container) {
20837
+ const { width: viewWidth, height: viewHeight } = this.env.model.getters.getMainViewportRect();
20838
+ const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
20839
+ const x = ["bottomRight", "topRight"].includes(container) ? viewportX : 0;
20840
+ const width = viewWidth - x;
20841
+ const y = ["bottomRight", "bottomLeft"].includes(container) ? viewportY : 0;
20842
+ const height = viewHeight - y;
20843
+ return { x, y, width, height };
20844
+ }
20574
20845
  getInverseViewportPositionStyle(container) {
20575
20846
  const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
20576
20847
  const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
@@ -20582,8 +20853,9 @@ class FiguresContainer extends Component {
20582
20853
  });
20583
20854
  }
20584
20855
  getFigureContainer(figure) {
20856
+ var _a;
20585
20857
  const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
20586
- if (figure.id === this.dnd.figId) {
20858
+ if (figure.id === ((_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id)) {
20587
20859
  return "dnd";
20588
20860
  }
20589
20861
  else if (figure.x < viewportX && figure.y < viewportY) {
@@ -20609,133 +20881,137 @@ class FiguresContainer extends Component {
20609
20881
  return;
20610
20882
  }
20611
20883
  const sheetId = this.env.model.getters.getActiveSheetId();
20612
- const mouseInitialX = ev.clientX;
20613
- const mouseInitialY = ev.clientY;
20614
- const { x: dndInitialX, y: dndInitialY } = this.internalToScreenCoordinates(figure);
20615
- this.dnd.x = dndInitialX;
20616
- this.dnd.y = dndInitialY;
20617
- this.dnd.width = figure.width;
20618
- this.dnd.height = figure.height;
20884
+ const initialMousePosition = { x: ev.clientX, y: ev.clientY };
20885
+ const { x, y } = internalFigureToScreen(this.env.model.getters, figure);
20886
+ const initialFig = { ...figure, x, y };
20619
20887
  const onMouseMove = (ev) => {
20620
- const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
20621
- const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
20622
- const minX = viewportX ? 0 : -scrollX;
20623
- const minY = viewportY ? 0 : -scrollY;
20624
- this.dnd.figId = figure.id;
20625
- const newX = ev.clientX;
20626
- let deltaX = newX - mouseInitialX;
20627
- this.dnd.x = Math.max(dndInitialX + deltaX, minX);
20628
- const newY = ev.clientY;
20629
- let deltaY = newY - mouseInitialY;
20630
- this.dnd.y = Math.max(dndInitialY + deltaY, minY);
20888
+ const getters = this.env.model.getters;
20889
+ const currentMousePosition = { x: ev.clientX, y: ev.clientY };
20890
+ const draggedFigure = dragFigureForMove(currentMousePosition, initialMousePosition, initialFig, this.env.model.getters.getMainViewportCoordinates(), getters.getActiveSheetScrollInfo());
20891
+ const otherFigures = this.getOtherFigures(figure.id);
20892
+ const internalDragged = screenFigureToInternal(getters, draggedFigure);
20893
+ const snapResult = snapForMove(getters, internalDragged, otherFigures);
20894
+ this.dnd.draggedFigure = internalFigureToScreen(getters, snapResult.snappedFigure);
20895
+ this.dnd.horizontalSnap = this.getSnap(snapResult.horizontalSnapLine);
20896
+ this.dnd.verticalSnap = this.getSnap(snapResult.verticalSnapLine);
20631
20897
  };
20632
20898
  const onMouseUp = (ev) => {
20633
- if (!this.dnd.figId) {
20899
+ if (!this.dnd.draggedFigure) {
20634
20900
  return;
20635
20901
  }
20636
- let { x, y } = this.screenCoordinatesToInternal(this.dnd);
20637
- this.dnd.figId = undefined;
20902
+ let { x, y } = screenFigureToInternal(this.env.model.getters, this.dnd.draggedFigure);
20903
+ this.dnd.draggedFigure = undefined;
20904
+ this.dnd.horizontalSnap = undefined;
20905
+ this.dnd.verticalSnap = undefined;
20638
20906
  this.env.model.dispatch("UPDATE_FIGURE", { sheetId, id: figure.id, x, y });
20639
20907
  };
20640
20908
  startDnd(onMouseMove, onMouseUp);
20641
20909
  }
20910
+ /**
20911
+ * Initialize the resize of a figure with mouse movements
20912
+ *
20913
+ * @param dirX X direction of the resize. -1 : resize from the left border of the figure, 0 : no resize in X, 1 :
20914
+ * resize from the right border of the figure
20915
+ * @param dirY Y direction of the resize. -1 : resize from the top border of the figure, 0 : no resize in Y, 1 :
20916
+ * resize from the bottom border of the figure
20917
+ * @param ev Mouse Event
20918
+ */
20642
20919
  startResize(figure, dirX, dirY, ev) {
20643
20920
  ev.stopPropagation();
20644
- const initialX = ev.clientX;
20645
- const initialY = ev.clientY;
20646
- const { x: dndInitialX, y: dndInitialY } = this.internalToScreenCoordinates(figure);
20647
- this.dnd.x = dndInitialX;
20648
- this.dnd.y = dndInitialY;
20649
- this.dnd.width = figure.width;
20650
- this.dnd.height = figure.height;
20921
+ const initialMousePosition = { x: ev.clientX, y: ev.clientY };
20922
+ const { x, y } = internalFigureToScreen(this.env.model.getters, figure);
20923
+ const initialFig = { ...figure, x, y };
20651
20924
  const keepRatio = figureRegistry.get(figure.tag).keepRatio || false;
20652
20925
  const minFigSize = figureRegistry.get(figure.tag).minFigSize || MIN_FIG_SIZE;
20653
- let onMouseMove;
20654
- if (keepRatio && dirX != 0 && dirY != 0) {
20655
- onMouseMove = (ev) => {
20656
- this.dnd.figId = figure.id;
20657
- const deltaX = Math.min(dirX * (initialX - ev.clientX), figure.width - minFigSize);
20658
- const deltaY = Math.min(dirY * (initialY - ev.clientY), figure.height - minFigSize);
20659
- const fraction = Math.min(deltaX / figure.width, deltaY / figure.height);
20660
- this.dnd.width = figure.width * (1 - fraction);
20661
- this.dnd.height = figure.height * (1 - fraction);
20662
- if (dirX < 0) {
20663
- this.dnd.x = dndInitialX + figure.width * fraction;
20664
- }
20665
- if (dirY < 0) {
20666
- this.dnd.y = dndInitialY + figure.height * fraction;
20667
- }
20668
- };
20669
- }
20670
- else {
20671
- onMouseMove = (ev) => {
20672
- this.dnd.figId = figure.id;
20673
- const deltaX = Math.max(dirX * (ev.clientX - initialX), minFigSize - figure.width);
20674
- const deltaY = Math.max(dirY * (ev.clientY - initialY), minFigSize - figure.height);
20675
- this.dnd.width = figure.width + deltaX;
20676
- this.dnd.height = figure.height + deltaY;
20677
- if (dirX < 0) {
20678
- this.dnd.x = dndInitialX - deltaX;
20679
- }
20680
- if (dirY < 0) {
20681
- this.dnd.y = dndInitialY - deltaY;
20682
- }
20683
- };
20684
- }
20926
+ const onMouseMove = (ev) => {
20927
+ const currentMousePosition = { x: ev.clientX, y: ev.clientY };
20928
+ const draggedFigure = dragFigureForResize(initialFig, dirX, dirY, currentMousePosition, initialMousePosition, keepRatio, minFigSize);
20929
+ const otherFigures = this.getOtherFigures(figure.id);
20930
+ const snapResult = snapForResize(this.env.model.getters, dirX, dirY, draggedFigure, otherFigures);
20931
+ this.dnd.draggedFigure = snapResult.snappedFigure;
20932
+ this.dnd.horizontalSnap = this.getSnap(snapResult.horizontalSnapLine);
20933
+ this.dnd.verticalSnap = this.getSnap(snapResult.verticalSnapLine);
20934
+ };
20685
20935
  const onMouseUp = (ev) => {
20686
- if (!this.dnd.figId) {
20936
+ if (!this.dnd.draggedFigure) {
20687
20937
  return;
20688
20938
  }
20689
- this.dnd.figId = undefined;
20690
- let { x, y } = this.screenCoordinatesToInternal(this.dnd);
20939
+ let { x, y } = screenFigureToInternal(this.env.model.getters, this.dnd.draggedFigure);
20691
20940
  const update = { x, y };
20692
20941
  if (dirX) {
20693
- update.width = this.dnd.width;
20942
+ update.width = this.dnd.draggedFigure.width;
20694
20943
  }
20695
20944
  if (dirY) {
20696
- update.height = this.dnd.height;
20945
+ update.height = this.dnd.draggedFigure.height;
20697
20946
  }
20698
20947
  this.env.model.dispatch("UPDATE_FIGURE", {
20699
20948
  sheetId: this.env.model.getters.getActiveSheetId(),
20700
20949
  id: figure.id,
20701
20950
  ...update,
20702
20951
  });
20952
+ this.dnd.draggedFigure = undefined;
20953
+ this.dnd.horizontalSnap = undefined;
20954
+ this.dnd.verticalSnap = undefined;
20703
20955
  };
20704
20956
  startDnd(onMouseMove, onMouseUp);
20705
20957
  }
20958
+ getOtherFigures(figId) {
20959
+ return this.getVisibleFigures().filter((f) => f.id !== figId);
20960
+ }
20706
20961
  getDndFigure() {
20707
- const figure = this.getVisibleFigures().find((fig) => fig.id === this.dnd.figId);
20962
+ const figure = this.getVisibleFigures().find((fig) => { var _a; return fig.id === ((_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id); });
20708
20963
  if (!figure)
20709
20964
  throw new Error("Dnd figure not found");
20710
20965
  return {
20711
20966
  ...figure,
20712
- x: this.dnd.x,
20713
- y: this.dnd.y,
20714
- width: this.dnd.width,
20715
- height: this.dnd.height,
20967
+ ...this.dnd.draggedFigure,
20716
20968
  };
20717
20969
  }
20718
20970
  getFigureStyle(figure) {
20719
- if (figure.id !== this.dnd.figId)
20971
+ var _a;
20972
+ if (figure.id !== ((_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id))
20720
20973
  return "";
20721
20974
  return cssPropertiesToCss({
20722
20975
  opacity: "0.9",
20723
20976
  cursor: "grabbing",
20724
20977
  });
20725
20978
  }
20726
- internalToScreenCoordinates({ x, y }) {
20727
- const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
20728
- const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
20729
- x = x < viewportX ? x : x - scrollX;
20730
- y = y < viewportY ? y : y - scrollY;
20731
- return { x, y };
20979
+ getSnap(snapLine) {
20980
+ if (!snapLine || !this.dnd.draggedFigure)
20981
+ return undefined;
20982
+ const figureVisibleRects = snapLine.matchedFigIds
20983
+ .map((id) => this.getVisibleFigures().find((fig) => fig.id === id))
20984
+ .filter(isDefined$1)
20985
+ .map((fig) => {
20986
+ const figOnSCreen = internalFigureToScreen(this.env.model.getters, fig);
20987
+ const container = this.getFigureContainer(fig);
20988
+ return rectIntersection(figOnSCreen, this.getContainerRect(container));
20989
+ })
20990
+ .filter(isDefined$1);
20991
+ const containerRect = rectUnion(this.dnd.draggedFigure, ...figureVisibleRects);
20992
+ return {
20993
+ line: snapLine,
20994
+ containerStyle: this.rectToCss(containerRect),
20995
+ lineStyle: this.getSnapLineStyle(snapLine, containerRect),
20996
+ };
20732
20997
  }
20733
- screenCoordinatesToInternal({ x, y }) {
20734
- const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
20735
- const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
20736
- x = viewportX && x < viewportX ? x : x + scrollX;
20737
- y = viewportY && y < viewportY ? y : y + scrollY;
20738
- return { x, y };
20998
+ getSnapLineStyle(snapLine, containerRect) {
20999
+ if (!snapLine)
21000
+ return "";
21001
+ if (["top", "vCenter", "bottom"].includes(snapLine.snappedAxisType)) {
21002
+ return cssPropertiesToCss({
21003
+ top: `${snapLine.position - containerRect.y}px`,
21004
+ left: `0px`,
21005
+ width: `100%`,
21006
+ });
21007
+ }
21008
+ else {
21009
+ return cssPropertiesToCss({
21010
+ top: `0px`,
21011
+ left: `${snapLine.position - containerRect.x}px`,
21012
+ height: `100%`,
21013
+ });
21014
+ }
20739
21015
  }
20740
21016
  }
20741
21017
  FiguresContainer.template = "o-spreadsheet-FiguresContainer";
@@ -20882,6 +21158,9 @@ class GridOverlay extends Component {
20882
21158
  onMounted(() => {
20883
21159
  resizeObserver.observe(this.gridOverlayEl);
20884
21160
  });
21161
+ onWillUnmount(() => {
21162
+ resizeObserver.disconnect();
21163
+ });
20885
21164
  useTouchMove(this.gridOverlay, this.props.onGridMoved, () => {
20886
21165
  const { scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
20887
21166
  return scrollY > 0;
@@ -30238,6 +30517,7 @@ class RangeAdapter {
30238
30517
  }
30239
30518
  else if (range.zone[start] >= min && range.zone[end] <= max) {
30240
30519
  changeType = "REMOVE";
30520
+ newRange = range.clone({ ...this.getInvalidRange() });
30241
30521
  }
30242
30522
  else if (range.zone[start] <= max && range.zone[end] >= max) {
30243
30523
  const toRemove = max - range.zone[start] + 1;
@@ -30303,8 +30583,10 @@ class RangeAdapter {
30303
30583
  return { changeType: "NONE" };
30304
30584
  }
30305
30585
  const invalidSheetName = this.getters.getSheetName(cmd.sheetId);
30306
- const sheetId = "";
30307
- range = range.clone({ sheetId, invalidSheetName });
30586
+ range = range.clone({
30587
+ ...this.getInvalidRange(),
30588
+ invalidSheetName,
30589
+ });
30308
30590
  return { changeType: "REMOVE", range };
30309
30591
  }, cmd.sheetId);
30310
30592
  break;
@@ -30564,6 +30846,15 @@ class RangeAdapter {
30564
30846
  }
30565
30847
  return str;
30566
30848
  }
30849
+ getInvalidRange() {
30850
+ return {
30851
+ parts: [],
30852
+ prefixSheet: false,
30853
+ zone: { left: -1, top: -1, right: -1, bottom: -1 },
30854
+ sheetId: "",
30855
+ invalidXc: INCORRECT_RANGE_STRING,
30856
+ };
30857
+ }
30567
30858
  }
30568
30859
  RangeAdapter.getters = [
30569
30860
  "getRangeString",
@@ -33460,6 +33751,20 @@ class SheetViewPlugin extends UIPlugin {
33460
33751
  }
33461
33752
  return result;
33462
33753
  }
33754
+ isPositionVisible(position) {
33755
+ const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo();
33756
+ const { x: mainViewportX, y: mainViewportY } = this.getters.getMainViewportCoordinates();
33757
+ const { width, height } = this.getters.getSheetViewDimension();
33758
+ if (position.x >= mainViewportX &&
33759
+ (position.x < mainViewportX + scrollX || position.x > width + scrollX + mainViewportX)) {
33760
+ return false;
33761
+ }
33762
+ if (position.y >= mainViewportY &&
33763
+ (position.y < mainViewportY + scrollY || position.y > height + scrollY + mainViewportY)) {
33764
+ return false;
33765
+ }
33766
+ return true;
33767
+ }
33463
33768
  getFrozenSheetViewRatio(sheetId) {
33464
33769
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
33465
33770
  const offsetCorrectionX = this.getters.getColDimensions(sheetId, xSplit).start;
@@ -33488,6 +33793,7 @@ SheetViewPlugin.getters = [
33488
33793
  "getSheetViewVisibleCols",
33489
33794
  "getSheetViewVisibleRows",
33490
33795
  "getFrozenSheetViewRatio",
33796
+ "isPositionVisible",
33491
33797
  ];
33492
33798
 
33493
33799
  /**
@@ -40587,6 +40893,11 @@ css /* scss */ `
40587
40893
  margin-top: -1px;
40588
40894
  border: 1px solid;
40589
40895
  z-index: ${ComponentsImportance.TopBarComposer};
40896
+
40897
+ .o-composer:empty:not(:focus)::before {
40898
+ /* svg free of use from https://uxwing.com/formula-fx-icon/ */
40899
+ content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -10 121.8 142.9' width='24' height='24' focusable='false'%3E%3Cpath d='m28 34-4 5v2h10l-6 40c-4 22-6 28-7 30-2 2-3 3-5 3-3 0-7-2-9-4H4c-2 2-4 4-4 7s4 6 8 6 9-2 15-8c8-7 13-17 18-39l7-35 13-1 3-6H49c4-23 7-27 11-27 2 0 5 2 8 6h4c1-1 4-4 4-7 0-2-3-6-9-6-5 0-13 4-20 10-6 7-9 14-11 24h-8zm41 16c4-5 7-7 8-7s2 1 5 9l3 12c-7 11-12 17-16 17l-3-1-2-1c-3 0-6 3-6 7s3 7 7 7c6 0 12-6 22-23l3 10c3 9 6 13 10 13 5 0 11-4 18-15l-3-4c-4 6-7 8-8 8-2 0-4-3-6-10l-5-15 8-10 6-4 3 1 3 2c2 0 6-3 6-7s-2-7-6-7c-6 0-11 5-21 20l-2-6c-3-9-5-14-9-14-5 0-12 6-18 15l3 3z' fill='%23BDBDBD' /%3E%3C/svg%3E");
40900
+ }
40590
40901
  }
40591
40902
  `;
40592
40903
  class TopBarComposer extends Component {
@@ -40993,6 +41304,7 @@ class TopBar extends Component {
40993
41304
  this.state.menuState.parentMenu = menu;
40994
41305
  this.isSelectingMenu = true;
40995
41306
  this.openedEl = ev.target;
41307
+ this.env.model.dispatch("STOP_EDITION");
40996
41308
  }
40997
41309
  closeMenus() {
40998
41310
  this.state.activeTool = "";
@@ -41366,8 +41678,10 @@ class Spreadsheet extends Component {
41366
41678
  this.sidePanel.isOpen = true;
41367
41679
  }
41368
41680
  closeSidePanel() {
41681
+ var _a, _b;
41369
41682
  this.sidePanel.isOpen = false;
41370
41683
  this.focusGrid();
41684
+ (_b = (_a = this.sidePanel.panelProps) === null || _a === void 0 ? void 0 : _a.onCloseSidePanel) === null || _b === void 0 ? void 0 : _b.call(_a);
41371
41685
  }
41372
41686
  toggleSidePanel(panel, panelProps) {
41373
41687
  if (this.sidePanel.isOpen && panel === this.sidePanel.component) {
@@ -43806,7 +44120,7 @@ function figureCoordinates(headers, position) {
43806
44120
  if (currentPosition <= position && position < currentPosition + header.size) {
43807
44121
  return {
43808
44122
  index: parseInt(headerIndex),
43809
- offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_SIZE),
44123
+ offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_WIDTH),
43810
44124
  };
43811
44125
  }
43812
44126
  else {
@@ -43815,7 +44129,7 @@ function figureCoordinates(headers, position) {
43815
44129
  }
43816
44130
  return {
43817
44131
  index: headers.length - 1,
43818
- offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_SIZE),
44132
+ offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_WIDTH),
43819
44133
  };
43820
44134
  }
43821
44135
  function createChartDrawing(figure, sheet, chartRelId) {
@@ -45046,6 +45360,6 @@ function addFunction(functionName, functionDescription) {
45046
45360
  export { AbstractChart, CommandResult, CorePlugin, DATETIME_FORMAT, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, links, load, parse, readonlyAllowedCommands, registries, setTranslationMethod, tokenize };
45047
45361
 
45048
45362
 
45049
- __info__.version = '16.3.0-alpha.1';
45050
- __info__.date = '2023-03-16T12:12:26.034Z';
45051
- __info__.hash = 'f41d64a';
45363
+ __info__.version = '16.3.0-alpha.2';
45364
+ __info__.date = '2023-03-23T11:36:58.645Z';
45365
+ __info__.hash = '59f25bb';