[IMP] spreadsheet: update o_spreadsheet to latest version

### Contains the following commits:

https://github.com/odoo/o-spreadsheet/commit/59f25bb4 [REL] 16.3.0-alpha.2
https://github.com/odoo/o-spreadsheet/commit/d0189bd0 [FIX] config: bump owl version
https://github.com/odoo/o-spreadsheet/commit/115e1d2b [FIX] tests: fix component tests and their organization
https://github.com/odoo/o-spreadsheet/commit/e42786ad [FIX] Top_bar: Opening a menu should close the composer Task: 3076218
https://github.com/odoo/o-spreadsheet/commit/45f4c5c3 [FIX] range: show ref error after removing col/row
https://github.com/odoo/o-spreadsheet/commit/c55d601d [IMP] figure: add snap to figure drag & drop
https://github.com/odoo/o-spreadsheet/commit/7cbde524 [FIX] side panel: onSidePanelClose prop never call on X symbol click
https://github.com/odoo/o-spreadsheet/commit/308e5d6c [IMP] figure_chart: Double click on chart open the sidepanel
https://github.com/odoo/o-spreadsheet/commit/9781b7e3 [FIX] composer: fixed composer placeholder issues

closes odoo/odoo#116338

Signed-off-by: Rémi Rahir (rar) <rar@odoo.com>
This commit is contained in:
Lucas Lefèvre
2023-03-24 07:45:07 +01:00
parent c0ae3e70e1
commit b6c4cbe07d
2 changed files with 451 additions and 125 deletions
@@ -225,7 +225,6 @@
const MENU_SEPARATOR_BORDER_WIDTH = 1;
const MENU_SEPARATOR_PADDING = 5;
const MENU_SEPARATOR_HEIGHT = MENU_SEPARATOR_BORDER_WIDTH + 2 * MENU_SEPARATOR_PADDING;
const FIGURE_BORDER_SIZE = 1;
// Fonts
const DEFAULT_FONT_WEIGHT = "400";
const DEFAULT_FONT_SIZE = 10;
@@ -245,6 +244,8 @@
// Figure
const DEFAULT_FIGURE_HEIGHT = 335;
const DEFAULT_FIGURE_WIDTH = 536;
const FIGURE_BORDER_WIDTH = 1;
const MIN_FIG_SIZE = 80;
// Chart
const MAX_CHAR_LABEL = 20;
const FIGURE_ID_SPLITTER = "??";
@@ -255,7 +256,6 @@
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#00A04A";
const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#DC6965";
const LINE_FILL_TRANSPARENCY = 0.4;
const MIN_FIG_SIZE = 80;
// session
const DEBOUNCE_TIME = 200;
const MESSAGE_VERSION = 1;
@@ -277,7 +277,8 @@
ComponentsImportance[ComponentsImportance["TopBarComposer"] = 21] = "TopBarComposer";
ComponentsImportance[ComponentsImportance["IconPicker"] = 25] = "IconPicker";
ComponentsImportance[ComponentsImportance["Popover"] = 30] = "Popover";
ComponentsImportance[ComponentsImportance["ChartAnchor"] = 1000] = "ChartAnchor";
ComponentsImportance[ComponentsImportance["FigureAnchor"] = 1000] = "FigureAnchor";
ComponentsImportance[ComponentsImportance["FigureSnapLine"] = 1001] = "FigureSnapLine";
})(ComponentsImportance || (ComponentsImportance = {}));
const DEFAULT_SHEETVIEW_SIZE = 1000;
const MAXIMAL_FREEZABLE_RATIO = 0.85;
@@ -3052,6 +3053,9 @@
}
return attributes;
}
/**
* Transform CSS properties into a CSS string.
*/
function cssPropertiesToCss(attributes) {
const str = Object.entries(attributes)
.filter(([attName, attValue]) => attValue !== undefined)
@@ -5072,6 +5076,10 @@
function rectIntersection(rect1, rect2) {
return zoneToRect(intersection(rectToZone(rect1), rectToZone(rect2)));
}
/** Compute the union of the rectangles, ie. the smallest rectangle that contain them all */
function rectUnion(...rects) {
return zoneToRect(union(...rects.map(rectToZone)));
}
function rectToZone(rect) {
return {
left: rect.x,
@@ -7801,6 +7809,10 @@
}
`;
class ChartFigure extends owl.Component {
onDoubleClick() {
this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figure.id });
this.env.openSidePanel("ChartPanel");
}
get chartType() {
return this.env.model.getters.getChartType(this.props.figure.id);
}
@@ -18838,7 +18850,7 @@
box-sizing: content-box;
.o-fig-anchor {
z-index: ${ComponentsImportance.ChartAnchor};
z-index: ${ComponentsImportance.FigureAnchor};
position: absolute;
width: ${ANCHOR_SIZE}px;
height: ${ANCHOR_SIZE}px;
@@ -19705,13 +19717,6 @@
pointer-events: none;
}
}
.o-topbar-composer .o-composer:empty:not(:focus)::before {
/* svg free of use from https://uxwing.com/formula-fx-icon/ */
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");
position: relative;
top: 20%;
}
`;
class Composer extends owl.Component {
constructor() {
@@ -20439,6 +20444,266 @@
gridPosition: { type: Object, optional: true },
};
/**
* Transform a figure with coordinates from the model, to coordinates as they are shown on the screen,
* taking into account the scroll position of the active sheet and the frozen panes.
*/
function internalFigureToScreen(getters, fig) {
return { ...fig, ...internalToScreenCoordinates(getters, { x: fig.x, y: fig.y }) };
}
/**
* Transform a figure with coordinates as they are shown on the screen, to coordinates as they are in the model,
* taking into account the scroll position of the active sheet and the frozen panes.
*
* Note that this isn't exactly the reverse operation as internalFigureToScreen, because the figure will always be on top
* of the frozen panes.
*/
function screenFigureToInternal(getters, fig) {
return { ...fig, ...screenCoordinatesToInternal(getters, { x: fig.x, y: fig.y }) };
}
function internalToScreenCoordinates(getters, { x, y }) {
const { x: viewportX, y: viewportY } = getters.getMainViewportCoordinates();
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
x = x < viewportX ? x : x - scrollX;
y = y < viewportY ? y : y - scrollY;
return { x, y };
}
function screenCoordinatesToInternal(getters, { x, y }) {
const { x: viewportX, y: viewportY } = getters.getMainViewportCoordinates();
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
x = viewportX && x < viewportX ? x : x + scrollX;
y = viewportY && y < viewportY ? y : y + scrollY;
return { x, y };
}
function dragFigureForMove({ x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, initialFigure, { x: viewportX, y: viewportY }, { scrollX, scrollY }) {
const minX = viewportX ? 0 : -scrollX;
const minY = viewportY ? 0 : -scrollY;
let deltaX = mouseX - mouseInitialX;
const newX = Math.max(initialFigure.x + deltaX, minX);
let deltaY = mouseY - mouseInitialY;
const newY = Math.max(initialFigure.y + deltaY, minY);
return { ...initialFigure, x: newX, y: newY };
}
function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, keepRatio, minFigSize) {
let { x, y, width, height } = initialFigure;
if (keepRatio && dirX != 0 && dirY != 0) {
const deltaX = Math.min(dirX * (mouseInitialX - mouseX), initialFigure.width - minFigSize);
const deltaY = Math.min(dirY * (mouseInitialY - mouseY), initialFigure.height - minFigSize);
const fraction = Math.min(deltaX / initialFigure.width, deltaY / initialFigure.height);
width = initialFigure.width * (1 - fraction);
height = initialFigure.height * (1 - fraction);
if (dirX < 0) {
x = initialFigure.x + initialFigure.width * fraction;
}
if (dirY < 0) {
y = initialFigure.y + initialFigure.height * fraction;
}
}
else {
const deltaX = Math.max(dirX * (mouseX - mouseInitialX), minFigSize - initialFigure.width);
const deltaY = Math.max(dirY * (mouseY - mouseInitialY), minFigSize - initialFigure.height);
width = initialFigure.width + deltaX;
height = initialFigure.height + deltaY;
if (dirX < 0) {
x = initialFigure.x - deltaX;
}
if (dirY < 0) {
y = initialFigure.y - deltaY;
}
}
return { ...initialFigure, x, y, width, height };
}
const SNAP_MARGIN = 5;
/**
* Try to snap the given figure to other figures when moving the figure, and return the snapped
* figure and the possible snap lines, if any were found
*/
function snapForMove(getters, figureToSnap, otherFigures) {
const snappedFigure = { ...figureToSnap };
const verticalSnapLine = getSnapLine(getters, snappedFigure, ["hCenter", "right", "left"], otherFigures, ["hCenter", "right", "left"]);
const horizontalSnapLine = getSnapLine(getters, snappedFigure, ["vCenter", "bottom", "top"], otherFigures, ["vCenter", "bottom", "top"]);
const { y: viewportY, x: viewportX } = getters.getMainViewportCoordinates();
const { scrollY, scrollX } = getters.getActiveSheetScrollInfo();
// If the snap cause the figure to change pane, we need to also apply the scroll as an offset
if (horizontalSnapLine) {
snappedFigure.y -= horizontalSnapLine.snapOffset;
const isBaseFigFrozenY = figureToSnap.y < viewportY;
const isSnappedFrozenY = snappedFigure.y < viewportY;
if (isBaseFigFrozenY && !isSnappedFrozenY)
snappedFigure.y += scrollY;
else if (!isBaseFigFrozenY && isSnappedFrozenY)
snappedFigure.y -= scrollY;
}
if (verticalSnapLine) {
snappedFigure.x -= verticalSnapLine.snapOffset;
const isBaseFigFrozenX = figureToSnap.x < viewportX;
const isSnappedFrozenX = snappedFigure.x < viewportX;
if (isBaseFigFrozenX && !isSnappedFrozenX)
snappedFigure.x += scrollX;
else if (!isBaseFigFrozenX && isSnappedFrozenX)
snappedFigure.x -= scrollX;
}
return { snappedFigure, verticalSnapLine, horizontalSnapLine };
}
/**
* Try to snap the given figure to the other figures when resizing the figure, and return the snapped
* figure and the possible snap lines, if any were found
*/
function snapForResize(getters, resizeDirX, resizeDirY, figureToSnap, otherFigures) {
const snappedFigure = { ...figureToSnap };
// Vertical snap line
const verticalSnapLine = getSnapLine(getters, snappedFigure, [resizeDirX === -1 ? "left" : "right"], otherFigures, ["right", "left"]);
if (verticalSnapLine) {
if (resizeDirX === 1) {
snappedFigure.width -= verticalSnapLine.snapOffset;
}
else if (resizeDirX === -1) {
snappedFigure.x -= verticalSnapLine.snapOffset;
snappedFigure.width += verticalSnapLine.snapOffset;
}
}
// Horizontal snap line
const horizontalSnapLine = getSnapLine(getters, snappedFigure, [resizeDirY === -1 ? "top" : "bottom"], otherFigures, ["bottom", "top"]);
if (horizontalSnapLine) {
if (resizeDirY === 1) {
snappedFigure.height -= horizontalSnapLine.snapOffset;
}
else if (resizeDirY === -1) {
snappedFigure.y -= horizontalSnapLine.snapOffset;
snappedFigure.height += horizontalSnapLine.snapOffset;
}
}
snappedFigure.x = Math.round(snappedFigure.x);
snappedFigure.y = Math.round(snappedFigure.y);
snappedFigure.height = Math.round(snappedFigure.height);
snappedFigure.width = Math.round(snappedFigure.width);
return { snappedFigure, verticalSnapLine, horizontalSnapLine };
}
/**
* Get the position of snap axes for the given figure
*
* @param figure the figure
* @param axesTypes the list of axis types to return the positions of
*/
function getVisibleAxes(getters, figure, axesTypes) {
const axes = axesTypes.map((axisType) => getAxis(figure, axisType));
return axes
.filter((axis) => isAxisVisible(getters, figure, axis))
.map((axis) => getAxisScreenPosition(getters, figure, axis));
}
/**
* We need two positions for the figure axis :
* - the position (core) of the axis in the figure. This is used to know whether or not the axis is
* displayed, or is hidden by the scroll/the frozen panes
* - the position in the screen, which is used to find snap matches. We cannot use the core position for this,
* because figures partially in frozen panes aren't displayed at their actual coordinates
*/
function getAxisScreenPosition(getters, figure, figureAxis) {
const screenFigure = internalFigureToScreen(getters, figure);
return getAxis(screenFigure, figureAxis.axisType);
}
function isAxisVisible(getters, figure, axis) {
const { x: mainViewportX, y: mainViewportY } = getters.getMainViewportCoordinates();
const axisStartEndPositions = [];
switch (axis.axisType) {
case "top":
case "bottom":
case "vCenter":
if (figure.y < mainViewportY)
return true;
axisStartEndPositions.push({ x: figure.x, y: axis.position });
axisStartEndPositions.push({ x: figure.x + figure.width, y: axis.position });
break;
case "left":
case "right":
case "hCenter":
if (figure.x < mainViewportX)
return true;
axisStartEndPositions.push({ x: axis.position, y: figure.y });
axisStartEndPositions.push({ x: axis.position, y: figure.y + figure.height });
break;
}
return axisStartEndPositions.some(getters.isPositionVisible);
}
/**
* Get a snap line for the given figure, if the figure can snap to any other figure
*
* @param figureToSnap figure to get the snap line for
* @param figAxesTypes figure axes of the given figure to be considered to find a snap line
* @param otherFigures figures to match against the snapped figure to find a snap line
* @param otherAxesTypes figure axes of the other figures to be considered to find a snap line
*/
function getSnapLine(getters, figureToSnap, figAxesTypes, otherFigures, otherAxesTypes) {
const axesOfFigure = getVisibleAxes(getters, figureToSnap, figAxesTypes);
let closestMatch = undefined;
for (const otherFigure of otherFigures) {
const axesOfOtherFig = getVisibleAxes(getters, otherFigure, otherAxesTypes);
for (const axisOfFigure of axesOfFigure) {
for (const axisOfOtherFig of axesOfOtherFig) {
if (!canSnap(axisOfFigure.position, axisOfOtherFig.position))
continue;
const snapOffset = axisOfFigure.position - axisOfOtherFig.position;
if (closestMatch && snapOffset === closestMatch.snapOffset) {
closestMatch.matchedFigIds.push(otherFigure.id);
}
else if (!closestMatch || Math.abs(snapOffset) <= Math.abs(closestMatch.snapOffset)) {
closestMatch = {
matchedFigIds: [otherFigure.id],
snapOffset,
snappedAxisType: axisOfFigure.axisType,
position: axisOfOtherFig.position,
};
}
}
}
}
return closestMatch;
}
/** Check if two axes are close enough to snap */
function canSnap(axisPosition1, axisPosition2) {
return Math.abs(axisPosition1 - axisPosition2) <= SNAP_MARGIN;
}
function getAxis(fig, axisType) {
let position = 0;
switch (axisType) {
case "top":
position = fig.y;
break;
case "bottom":
position = fig.y + fig.height - FIGURE_BORDER_WIDTH;
break;
case "vCenter":
position = fig.y + Math.floor(fig.height / 2) - FIGURE_BORDER_WIDTH;
break;
case "left":
position = fig.x;
break;
case "right":
position = fig.x + fig.width - FIGURE_BORDER_WIDTH;
break;
case "hCenter":
position = fig.x + Math.floor(fig.width / 2) - FIGURE_BORDER_WIDTH;
break;
}
return { position, axisType: axisType };
}
css /*SCSS*/ `
.o-figure-snap-line {
position: relative;
z-index: ${ComponentsImportance.FigureSnapLine};
&.vertical {
width: 0px;
border-left: 1px dashed black;
}
&.horizontal {
border-top: 1px dashed black;
height: 0px;
}
}
`;
/**
* Each figure ⭐ is positioned inside a container `div` placed and sized
* according to the split pane the figure is part of, or a separate container for the figure
@@ -20503,11 +20768,9 @@
constructor() {
super(...arguments);
this.dnd = owl.useState({
figId: undefined,
x: 0,
y: 0,
width: 0,
height: 0,
draggedFigure: undefined,
horizontalSnap: undefined,
verticalSnap: undefined,
});
}
setup() {
@@ -20523,9 +20786,11 @@
});
}
getVisibleFigures() {
var _a;
const visibleFigures = this.env.model.getters.getVisibleFigures();
if (this.dnd.figId && !visibleFigures.some((figure) => figure.id === this.dnd.figId)) {
visibleFigures.push(this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), this.dnd.figId));
if (this.dnd.draggedFigure &&
!visibleFigures.some((figure) => { var _a; return figure.id === ((_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id); })) {
visibleFigures.push(this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), (_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id));
}
return visibleFigures;
}
@@ -20548,7 +20813,7 @@
});
}
}
if (this.dnd.figId) {
if (this.dnd.draggedFigure) {
containers.push({
type: "dnd",
figures: [this.getDndFigure()],
@@ -20559,18 +20824,24 @@
return containers;
}
getContainerStyle(container) {
return this.rectToCss(this.getContainerRect(container));
}
rectToCss(rect) {
return cssPropertiesToCss({
left: `${rect.x}px`,
top: `${rect.y}px`,
width: `${rect.width}px`,
height: `${rect.height}px`,
});
}
getContainerRect(container) {
const { width: viewWidth, height: viewHeight } = this.env.model.getters.getMainViewportRect();
const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
const left = ["bottomRight", "topRight"].includes(container) ? viewportX : 0;
const width = viewWidth - left;
const top = ["bottomRight", "bottomLeft"].includes(container) ? viewportY : 0;
const height = viewHeight - top;
return cssPropertiesToCss({
left: `${left}px`,
top: `${top}px`,
width: `${width}px`,
height: `${height}px`,
});
const x = ["bottomRight", "topRight"].includes(container) ? viewportX : 0;
const width = viewWidth - x;
const y = ["bottomRight", "bottomLeft"].includes(container) ? viewportY : 0;
const height = viewHeight - y;
return { x, y, width, height };
}
getInverseViewportPositionStyle(container) {
const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
@@ -20583,8 +20854,9 @@
});
}
getFigureContainer(figure) {
var _a;
const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
if (figure.id === this.dnd.figId) {
if (figure.id === ((_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id)) {
return "dnd";
}
else if (figure.x < viewportX && figure.y < viewportY) {
@@ -20610,133 +20882,137 @@
return;
}
const sheetId = this.env.model.getters.getActiveSheetId();
const mouseInitialX = ev.clientX;
const mouseInitialY = ev.clientY;
const { x: dndInitialX, y: dndInitialY } = this.internalToScreenCoordinates(figure);
this.dnd.x = dndInitialX;
this.dnd.y = dndInitialY;
this.dnd.width = figure.width;
this.dnd.height = figure.height;
const initialMousePosition = { x: ev.clientX, y: ev.clientY };
const { x, y } = internalFigureToScreen(this.env.model.getters, figure);
const initialFig = { ...figure, x, y };
const onMouseMove = (ev) => {
const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
const minX = viewportX ? 0 : -scrollX;
const minY = viewportY ? 0 : -scrollY;
this.dnd.figId = figure.id;
const newX = ev.clientX;
let deltaX = newX - mouseInitialX;
this.dnd.x = Math.max(dndInitialX + deltaX, minX);
const newY = ev.clientY;
let deltaY = newY - mouseInitialY;
this.dnd.y = Math.max(dndInitialY + deltaY, minY);
const getters = this.env.model.getters;
const currentMousePosition = { x: ev.clientX, y: ev.clientY };
const draggedFigure = dragFigureForMove(currentMousePosition, initialMousePosition, initialFig, this.env.model.getters.getMainViewportCoordinates(), getters.getActiveSheetScrollInfo());
const otherFigures = this.getOtherFigures(figure.id);
const internalDragged = screenFigureToInternal(getters, draggedFigure);
const snapResult = snapForMove(getters, internalDragged, otherFigures);
this.dnd.draggedFigure = internalFigureToScreen(getters, snapResult.snappedFigure);
this.dnd.horizontalSnap = this.getSnap(snapResult.horizontalSnapLine);
this.dnd.verticalSnap = this.getSnap(snapResult.verticalSnapLine);
};
const onMouseUp = (ev) => {
if (!this.dnd.figId) {
if (!this.dnd.draggedFigure) {
return;
}
let { x, y } = this.screenCoordinatesToInternal(this.dnd);
this.dnd.figId = undefined;
let { x, y } = screenFigureToInternal(this.env.model.getters, this.dnd.draggedFigure);
this.dnd.draggedFigure = undefined;
this.dnd.horizontalSnap = undefined;
this.dnd.verticalSnap = undefined;
this.env.model.dispatch("UPDATE_FIGURE", { sheetId, id: figure.id, x, y });
};
startDnd(onMouseMove, onMouseUp);
}
/**
* Initialize the resize of a figure with mouse movements
*
* @param dirX X direction of the resize. -1 : resize from the left border of the figure, 0 : no resize in X, 1 :
* resize from the right border of the figure
* @param dirY Y direction of the resize. -1 : resize from the top border of the figure, 0 : no resize in Y, 1 :
* resize from the bottom border of the figure
* @param ev Mouse Event
*/
startResize(figure, dirX, dirY, ev) {
ev.stopPropagation();
const initialX = ev.clientX;
const initialY = ev.clientY;
const { x: dndInitialX, y: dndInitialY } = this.internalToScreenCoordinates(figure);
this.dnd.x = dndInitialX;
this.dnd.y = dndInitialY;
this.dnd.width = figure.width;
this.dnd.height = figure.height;
const initialMousePosition = { x: ev.clientX, y: ev.clientY };
const { x, y } = internalFigureToScreen(this.env.model.getters, figure);
const initialFig = { ...figure, x, y };
const keepRatio = figureRegistry.get(figure.tag).keepRatio || false;
const minFigSize = figureRegistry.get(figure.tag).minFigSize || MIN_FIG_SIZE;
let onMouseMove;
if (keepRatio && dirX != 0 && dirY != 0) {
onMouseMove = (ev) => {
this.dnd.figId = figure.id;
const deltaX = Math.min(dirX * (initialX - ev.clientX), figure.width - minFigSize);
const deltaY = Math.min(dirY * (initialY - ev.clientY), figure.height - minFigSize);
const fraction = Math.min(deltaX / figure.width, deltaY / figure.height);
this.dnd.width = figure.width * (1 - fraction);
this.dnd.height = figure.height * (1 - fraction);
if (dirX < 0) {
this.dnd.x = dndInitialX + figure.width * fraction;
}
if (dirY < 0) {
this.dnd.y = dndInitialY + figure.height * fraction;
}
};
}
else {
onMouseMove = (ev) => {
this.dnd.figId = figure.id;
const deltaX = Math.max(dirX * (ev.clientX - initialX), minFigSize - figure.width);
const deltaY = Math.max(dirY * (ev.clientY - initialY), minFigSize - figure.height);
this.dnd.width = figure.width + deltaX;
this.dnd.height = figure.height + deltaY;
if (dirX < 0) {
this.dnd.x = dndInitialX - deltaX;
}
if (dirY < 0) {
this.dnd.y = dndInitialY - deltaY;
}
};
}
const onMouseMove = (ev) => {
const currentMousePosition = { x: ev.clientX, y: ev.clientY };
const draggedFigure = dragFigureForResize(initialFig, dirX, dirY, currentMousePosition, initialMousePosition, keepRatio, minFigSize);
const otherFigures = this.getOtherFigures(figure.id);
const snapResult = snapForResize(this.env.model.getters, dirX, dirY, draggedFigure, otherFigures);
this.dnd.draggedFigure = snapResult.snappedFigure;
this.dnd.horizontalSnap = this.getSnap(snapResult.horizontalSnapLine);
this.dnd.verticalSnap = this.getSnap(snapResult.verticalSnapLine);
};
const onMouseUp = (ev) => {
if (!this.dnd.figId) {
if (!this.dnd.draggedFigure) {
return;
}
this.dnd.figId = undefined;
let { x, y } = this.screenCoordinatesToInternal(this.dnd);
let { x, y } = screenFigureToInternal(this.env.model.getters, this.dnd.draggedFigure);
const update = { x, y };
if (dirX) {
update.width = this.dnd.width;
update.width = this.dnd.draggedFigure.width;
}
if (dirY) {
update.height = this.dnd.height;
update.height = this.dnd.draggedFigure.height;
}
this.env.model.dispatch("UPDATE_FIGURE", {
sheetId: this.env.model.getters.getActiveSheetId(),
id: figure.id,
...update,
});
this.dnd.draggedFigure = undefined;
this.dnd.horizontalSnap = undefined;
this.dnd.verticalSnap = undefined;
};
startDnd(onMouseMove, onMouseUp);
}
getOtherFigures(figId) {
return this.getVisibleFigures().filter((f) => f.id !== figId);
}
getDndFigure() {
const figure = this.getVisibleFigures().find((fig) => fig.id === this.dnd.figId);
const figure = this.getVisibleFigures().find((fig) => { var _a; return fig.id === ((_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id); });
if (!figure)
throw new Error("Dnd figure not found");
return {
...figure,
x: this.dnd.x,
y: this.dnd.y,
width: this.dnd.width,
height: this.dnd.height,
...this.dnd.draggedFigure,
};
}
getFigureStyle(figure) {
if (figure.id !== this.dnd.figId)
var _a;
if (figure.id !== ((_a = this.dnd.draggedFigure) === null || _a === void 0 ? void 0 : _a.id))
return "";
return cssPropertiesToCss({
opacity: "0.9",
cursor: "grabbing",
});
}
internalToScreenCoordinates({ x, y }) {
const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
x = x < viewportX ? x : x - scrollX;
y = y < viewportY ? y : y - scrollY;
return { x, y };
getSnap(snapLine) {
if (!snapLine || !this.dnd.draggedFigure)
return undefined;
const figureVisibleRects = snapLine.matchedFigIds
.map((id) => this.getVisibleFigures().find((fig) => fig.id === id))
.filter(isDefined$1)
.map((fig) => {
const figOnSCreen = internalFigureToScreen(this.env.model.getters, fig);
const container = this.getFigureContainer(fig);
return rectIntersection(figOnSCreen, this.getContainerRect(container));
})
.filter(isDefined$1);
const containerRect = rectUnion(this.dnd.draggedFigure, ...figureVisibleRects);
return {
line: snapLine,
containerStyle: this.rectToCss(containerRect),
lineStyle: this.getSnapLineStyle(snapLine, containerRect),
};
}
screenCoordinatesToInternal({ x, y }) {
const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
x = viewportX && x < viewportX ? x : x + scrollX;
y = viewportY && y < viewportY ? y : y + scrollY;
return { x, y };
getSnapLineStyle(snapLine, containerRect) {
if (!snapLine)
return "";
if (["top", "vCenter", "bottom"].includes(snapLine.snappedAxisType)) {
return cssPropertiesToCss({
top: `${snapLine.position - containerRect.y}px`,
left: `0px`,
width: `100%`,
});
}
else {
return cssPropertiesToCss({
top: `0px`,
left: `${snapLine.position - containerRect.x}px`,
height: `100%`,
});
}
}
}
FiguresContainer.template = "o-spreadsheet-FiguresContainer";
@@ -30242,6 +30518,7 @@
}
else if (range.zone[start] >= min && range.zone[end] <= max) {
changeType = "REMOVE";
newRange = range.clone({ ...this.getInvalidRange() });
}
else if (range.zone[start] <= max && range.zone[end] >= max) {
const toRemove = max - range.zone[start] + 1;
@@ -30307,8 +30584,10 @@
return { changeType: "NONE" };
}
const invalidSheetName = this.getters.getSheetName(cmd.sheetId);
const sheetId = "";
range = range.clone({ sheetId, invalidSheetName });
range = range.clone({
...this.getInvalidRange(),
invalidSheetName,
});
return { changeType: "REMOVE", range };
}, cmd.sheetId);
break;
@@ -30568,6 +30847,15 @@
}
return str;
}
getInvalidRange() {
return {
parts: [],
prefixSheet: false,
zone: { left: -1, top: -1, right: -1, bottom: -1 },
sheetId: "",
invalidXc: INCORRECT_RANGE_STRING,
};
}
}
RangeAdapter.getters = [
"getRangeString",
@@ -33464,6 +33752,20 @@
}
return result;
}
isPositionVisible(position) {
const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo();
const { x: mainViewportX, y: mainViewportY } = this.getters.getMainViewportCoordinates();
const { width, height } = this.getters.getSheetViewDimension();
if (position.x >= mainViewportX &&
(position.x < mainViewportX + scrollX || position.x > width + scrollX + mainViewportX)) {
return false;
}
if (position.y >= mainViewportY &&
(position.y < mainViewportY + scrollY || position.y > height + scrollY + mainViewportY)) {
return false;
}
return true;
}
getFrozenSheetViewRatio(sheetId) {
const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
const offsetCorrectionX = this.getters.getColDimensions(sheetId, xSplit).start;
@@ -33492,6 +33794,7 @@
"getSheetViewVisibleCols",
"getSheetViewVisibleRows",
"getFrozenSheetViewRatio",
"isPositionVisible",
];
/**
@@ -40591,6 +40894,11 @@
margin-top: -1px;
border: 1px solid;
z-index: ${ComponentsImportance.TopBarComposer};
.o-composer:empty:not(:focus)::before {
/* svg free of use from https://uxwing.com/formula-fx-icon/ */
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");
}
}
`;
class TopBarComposer extends owl.Component {
@@ -40997,6 +41305,7 @@
this.state.menuState.parentMenu = menu;
this.isSelectingMenu = true;
this.openedEl = ev.target;
this.env.model.dispatch("STOP_EDITION");
}
closeMenus() {
this.state.activeTool = "";
@@ -41370,8 +41679,10 @@
this.sidePanel.isOpen = true;
}
closeSidePanel() {
var _a, _b;
this.sidePanel.isOpen = false;
this.focusGrid();
(_b = (_a = this.sidePanel.panelProps) === null || _a === void 0 ? void 0 : _a.onCloseSidePanel) === null || _b === void 0 ? void 0 : _b.call(_a);
}
toggleSidePanel(panel, panelProps) {
if (this.sidePanel.isOpen && panel === this.sidePanel.component) {
@@ -43810,7 +44121,7 @@
if (currentPosition <= position && position < currentPosition + header.size) {
return {
index: parseInt(headerIndex),
offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_SIZE),
offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_WIDTH),
};
}
else {
@@ -43819,7 +44130,7 @@
}
return {
index: headers.length - 1,
offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_SIZE),
offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_WIDTH),
};
}
function createChartDrawing(figure, sheet, chartRelId) {
@@ -45080,9 +45391,9 @@
Object.defineProperty(exports, '__esModule', { value: true });
__info__.version = '16.3.0-alpha.1';
__info__.date = '2023-03-16T12:15:48.320Z';
__info__.hash = 'f41d64a';
__info__.version = '16.3.0-alpha.2';
__info__.date = '2023-03-23T11:47:46.254Z';
__info__.hash = '59f25bb';
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
@@ -520,6 +520,7 @@
t-on-contextmenu.prevent.stop="onContextMenu"
t-ref="figure"
t-att-style="props.style"
t-att-data-id="props.figure.id"
tabindex="0"
t-on-keydown="(ev) => this.onKeyDown(ev)"
t-on-keyup.stop="">
@@ -592,7 +593,7 @@
</t>
<t t-name="o-spreadsheet-ChartFigure" owl="1">
<div class="o-chart-container w-100 h-100">
<div class="o-chart-container w-100 h-100" t-on-dblclick="onDoubleClick">
<t
t-component="chartComponent"
figure="this.props.figure"
@@ -624,6 +625,20 @@
</div>
</t>
</div>
<div
class="o-figure-container position-absolute pe-none overflow-hidden"
t-if="dnd.horizontalSnap"
t-att-style="dnd.horizontalSnap.containerStyle"
t-att-data-id="'HorizontalSnapContainer'">
<div class="o-figure-snap-line horizontal" t-att-style="dnd.horizontalSnap.lineStyle"/>
</div>
<div
class="o-figure-container position-absolute pe-none overflow-hidden"
t-if="dnd.verticalSnap"
t-att-style="dnd.verticalSnap.containerStyle"
t-att-data-id="'VerticalSnapContainer'">
<div class="o-figure-snap-line vertical" t-att-style="dnd.verticalSnap.lineStyle"/>
</div>
</t>
<t t-name="o-spreadsheet-ImageFigure" owl="1">
@@ -1780,7 +1795,7 @@
'o-invalid': isInvalid || !range.isValidRange,
'text-decoration-underline': range.isFocused and state.mode === 'select-range'
}"
t-ref="{{range.isFocused ? 'focused' : 'unfocused'}}Input"
t-ref="{{range.isFocused ? 'focusedInput' : 'unfocusedInput' + range_index}}"
/>
<button
class="o-btn o-remove-selection"
@@ -3535,7 +3550,7 @@
</t>
</odoo>
<!--
__info__.version = '16.3.0-alpha.1';
__info__.date = '2023-03-16T12:15:52.711Z';
__info__.hash = 'f41d64a';
__info__.version = '16.3.0-alpha.2';
__info__.date = '2023-03-23T11:47:49.776Z';
__info__.hash = '59f25bb';
-->