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