added gridstack

This commit is contained in:
Pit Friedrich
2026-07-25 21:56:47 +02:00
parent c2d29f0635
commit bcf604b7c5
7 changed files with 73 additions and 7 deletions
Binary file not shown.
+14 -3
View File
@@ -34,13 +34,15 @@ class GridStackLayout extends HTMLElement {
this.classList.add('grid-stack');
const options = JSON.parse(optionsJson);
this.grid = GridStack.init(options, this);
const grid = GridStack.init(options, this);
if (!grid) return;
this.grid = grid;
this.registerChildren();
this.restore();
this.grid.on('change added removed', () => this.schedulePersist());
this.grid.on('resizestop', () => window.dispatchEvent(new Event('resize')));
grid.on('change added removed', () => this.schedulePersist());
grid.on('resizestop', () => window.dispatchEvent(new Event('resize')));
}
/** Drops any saved layout for this grid and re-applies the positions
@@ -73,6 +75,15 @@ class GridStackLayout extends HTMLElement {
mutation.removedNodes.forEach((node) => {
if (!(node instanceof HTMLElement)) return;
if (!(node as any).gridstackNode) return;
// gridstack's own _sortDom() re-appends children (within this
// same element) to match layout order after every
// move/resize, which fires childList mutations too. A
// reordered node is back in the (still connected) DOM by the
// time this callback runs; only a genuinely removed node is
// disconnected. Without this check, removeWidget() here
// would fire on gridstack's own reorder, re-triggering
// _sortDom() and looping forever.
if (node.isConnected) return;
this.grid!.removeWidget(node, false);
});
}
@@ -27,7 +27,11 @@ public class GridStackItem extends Div {
getElement().setAttribute("gs-h", String.valueOf(h));
this.content.addClassName("grid-stack-item-content");
add(this.content);
// Attach the wrapper div directly through the element API — calling
// the overridden add(Component...) below would route it through
// `content.add(...)`, making the wrapper try to add itself as its
// own child.
getElement().appendChild(this.content.getElement());
this.content.add(content);
}
@@ -80,7 +84,7 @@ public class GridStackItem extends Div {
}
/** Snapshot of a widget's grid placement, as reported by the client after a
* drag/resize (see {@link GridStackLayout#onLayoutChange(String)}). */
* drag/resize. */
public record Position(String id, int x, int y, int w, int h) {
}
}
@@ -121,4 +121,32 @@ vaadin-app-layout::part(content) {
font-weight: 600;
color: var(--dialect-ink);
margin: 0 0 12px 0;
}
/* GridStackLayout (components/GridStackLayout.java): extend the same alias
layer used by .dialect-card rather than hardcoding new colors. */
.grid-stack-item-content {
background: var(--dialect-surface);
border-radius: var(--dialect-radius);
box-shadow: var(--dialect-shadow);
overflow: auto;
}
/* A Card placed inside a grid item should fill the item instead of
stacking its own surface/shadow on top of grid-stack-item-content's. */
.grid-stack-item-content > .dialect-card {
height: 100%;
box-shadow: none;
}
.grid-stack-item-content .ui-resizable-handle {
background: var(--dialect-primary);
opacity: 0.6;
border-radius: 2px;
}
.grid-stack-placeholder > .placeholder-content {
background: var(--dialect-bg);
border: 2px dashed var(--dialect-border);
border-radius: var(--dialect-radius);
}