Skip to content
This repository has been archived by the owner on Jan 23, 2025. It is now read-only.

MG-163 - Add snap to grid effect on layout #175

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
13 changes: 13 additions & 0 deletions ui/web/static/css/dashboards.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ SPDX-License-Identifier: Apache-2.0 */

.grid {
position: relative;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
grid-gap: 1rem;
padding: 1rem;
}

.grid-editable {
background-size: 10px 10px;
background-image: linear-gradient(to right, lightgrey 1px, transparent 1px),
linear-gradient(to bottom, lightgrey 1px, transparent 1px);
background-position: 5px 5px;
min-width: 100%;
min-height: 50vh;
}

.item {
Expand Down
2 changes: 2 additions & 0 deletions ui/web/static/js/charts.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Echart extends Chart {
};
this.Content = this.#generateContent();
}

#generateContent() {
return `
<div class="item-content" id="${this.ID}" style="width: ${this.Style.width}; height: ${this.Style.height};">
Expand Down Expand Up @@ -51,6 +52,7 @@ class AlarmCount extends Chart {
</div>
</div>
</div>
</div>
`;
}
}
Expand Down
173 changes: 111 additions & 62 deletions ui/web/static/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
const gridClass = ".grid";
var grid = initGrid(layout);

// Editable canvas is used to make the canvas editable allowing the user to add widgets and be able to move the
// widgets around the canvas
const gridSize = 20;
const previousSizes = new Map();
let isResizing = false;
let currentFinalizeResizeFunction = null;

function cancelEdit() {
grid._settings.dragEnabled = false;
Expand Down Expand Up @@ -185,34 +186,35 @@ function editableCanvas() {
return grid;
}

const previousSizes = new Map();

const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
const { target } = entry;
const previousSize = previousSizes.get(target) || {
width: target.clientWidth,
height: target.clientHeight,
};
let ltgrid = document.querySelector(".grid");
ltgrid.classList.add("grid-editable");
const contentEl = target.querySelector(".item-content");
const gridRightPosition = target.parentNode.getBoundingClientRect().right;
const widgetRightPosition = target.getBoundingClientRect().right;
const isOverflowing = widgetRightPosition > gridRightPosition;
target.style.maxWidth = 1300 + "px";
target.style.maxHeight = 1300 + "px";
if (isOverflowing) {
target.style.maxWidth = target.clientWidth + "px";
target.style.maxHeight = target.clientHeight + "px";
} else {
target.style.maxWidth = "none";
target.style.maxHeight = "none";
target.style.maxWidth = 1300 + "px";
target.style.maxHeight = 1300 + "px";
}

if (widgetRightPosition < gridRightPosition - 5) {
// Calculate the change in width and height
var widthChange = target.clientWidth - previousSize.width;
var heightChange = target.clientHeight - previousSize.height;
var itemContentWidth =
let widthChange = target.clientWidth - previousSize.width;
let heightChange = target.clientHeight - previousSize.height;
let itemContentWidth =
parseInt(window.getComputedStyle(contentEl).getPropertyValue("width")) + widthChange;
var itemContentHeight =
let itemContentHeight =
parseInt(window.getComputedStyle(contentEl).getPropertyValue("height")) + heightChange;

// Update the previous size for the next callback
Expand All @@ -221,65 +223,112 @@ const resizeObserver = new ResizeObserver((entries) => {
height: target.clientHeight,
});

target.style.width = target.clientWidth + "px";
target.style.height = target.clientHeight + "px";

contentEl.style.width = itemContentWidth + "px";
contentEl.style.height = itemContentHeight + "px";

// Resize apache echarts chart
const chart = echarts.getInstanceByDom(contentEl);
if (chart) {
chart.resize({
width: itemContentWidth,
height: itemContentHeight,
});
} else {
const cardDiv = target.querySelector(".widgetcard");
const h5Elem = cardDiv.querySelector("h5");
const cardBody = cardDiv.querySelector(".card-body");
const cardFooter = cardDiv.querySelector(".card-footer");

if (entry.contentBoxSize) {
// The standard makes contentBoxSize an array...
if (entry.contentBoxSize[0]) {
h5Elem.style.fontSize = Math.max(1, entry.contentBoxSize[0].inlineSize / 300) + "rem";
if (cardBody) {
cardBody.style.fontSize =
Math.max(1.5, entry.contentBoxSize[0].inlineSize / 300) + "rem";
}
if (cardFooter) {
cardFooter.style.fontSize =
Math.max(1, entry.contentBoxSize[0].inlineSize / 600) + "rem";
}
} else {
// ...but old versions of Firefox treat it as a single item
h5Elem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize / 300) + "rem";
if (cardBody) {
cardBody.style.fontSize =
Math.max(1.5, entry.contentBoxSize.inlineSize / 300) + "rem";
}
if (cardFooter) {
cardFooter.style.fontSize =
Math.max(1, entry.contentBoxSize.inlineSize / 600) + "rem";
}
}
} else {
h5Elem.style.fontSize = `${Math.max(1, entry.contentRect.width / 300)}rem`;
if (cardBody) {
cardBody.style.fontSize = `${Math.max(1.5, entry.contentRect.width / 300)}rem`;
}
if (cardFooter) {
cardFooter.style.fontSize = `${Math.max(1, entry.contentRect.width / 600)}rem`;
}
}
}
//Resize the widget content
resizeWidgetContent(target, entry, itemContentWidth, itemContentHeight);

grid.refreshItems();
grid.layout(true);
if (!isResizing) {
isResizing = true;

if (currentFinalizeResizeFunction) {
document.removeEventListener("mouseup", currentFinalizeResizeFunction);
currentFinalizeResizeFunction = null;
}
if (!currentFinalizeResizeFunction) {
currentFinalizeResizeFunction = function () {
snapToGrid(target, entry);
};
document.addEventListener("mouseup", currentFinalizeResizeFunction);
}
}
}
}
});

function snapToGrid(target, entry) {
const previousSize = previousSizes.get(target) || {
width: target.clientWidth,
height: target.clientHeight,
};
const contentEl = target.querySelector(".item-content");
const snapToGrid = (size) => Math.round(size / gridSize) * gridSize;
let snappedWidth = snapToGrid(target.clientWidth);
let snappedHeight = snapToGrid(target.clientHeight);

let widthChange = snappedWidth - previousSize.width;
let heightChange = snappedHeight - previousSize.height;
let itemContentWidth =
parseInt(window.getComputedStyle(contentEl).getPropertyValue("width")) + widthChange;
let itemContentHeight =
parseInt(window.getComputedStyle(contentEl).getPropertyValue("height")) + heightChange;

target.style.width = snappedWidth + "px";
target.style.height = snappedHeight + "px";

contentEl.style.width = itemContentWidth + "px";
contentEl.style.height = itemContentHeight + "px";

previousSizes.set(target, {
width: snappedWidth,
height: snappedHeight,
});

resizeWidgetContent(target, entry, itemContentWidth, itemContentHeight);
document.removeEventListener("mouseup", currentFinalizeResizeFunction);
let ltgrid = document.querySelector(".grid");
ltgrid.classList.remove("grid-editable");
isResizing = false;
}

function resizeWidgetContent(target, entry, itemContentWidth, itemContentHeight) {
const contentEl = target.querySelector(".item-content");
const chart = echarts.getInstanceByDom(contentEl);
if (chart) {
chart.resize({
width: itemContentWidth,
height: itemContentHeight,
});
} else {
const cardDiv = target.querySelector(".widgetcard");
const h5Elem = cardDiv.querySelector("h5");
const cardBody = cardDiv.querySelector(".card-body");
const cardFooter = cardDiv.querySelector(".card-footer");

if (target.contentBoxSize) {
// The standard makes contentBoxSize an array...
if (entry.contentBoxSize[0]) {
h5Elem.style.fontSize = Math.max(1, entry.contentBoxSize[0].inlineSize / 300) + "rem";
if (cardBody) {
cardBody.style.fontSize = Math.max(1.5, entry.contentBoxSize[0].inlineSize / 300) + "rem";
}
if (cardFooter) {
cardFooter.style.fontSize = Math.max(1, entry.contentBoxSize[0].inlineSize / 600) + "rem";
}
} else {
// ...but old versions of Firefox treat it as a single item
h5Elem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize / 300) + "rem";
if (cardBody) {
cardBody.style.fontSize = Math.max(1.5, entry.contentBoxSize.inlineSize / 300) + "rem";
}
if (cardFooter) {
cardFooter.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize / 600) + "rem";
}
}
} else {
h5Elem.style.fontSize = `${Math.max(1, entry.contentRect.width / 300)}rem`;
if (cardBody) {
cardBody.style.fontSize = `${Math.max(1.5, entry.contentRect.width / 300)}rem`;
}
if (cardFooter) {
cardFooter.style.fontSize = `${Math.max(1, entry.contentRect.width / 600)}rem`;
}
}
}
}
// No widget placeholder
function showNoWidgetPlaceholder() {
const noWidgetPlaceholder = document.querySelector(".no-widget-placeholder");
Expand Down
Loading