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