Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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