Compare commits
2 Commits
f9860fd701
...
f242bbd8a6
| Author | SHA1 | Date | |
|---|---|---|---|
| f242bbd8a6 | |||
| 85bb0c8c0b |
@@ -20,7 +20,12 @@ class GridStackLayout extends HTMLElement {
|
|||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
this.observer?.disconnect();
|
this.observer?.disconnect();
|
||||||
this.observer = undefined;
|
this.observer = undefined;
|
||||||
clearTimeout(this.persistTimer);
|
// Write out a still-pending debounced save instead of dropping it:
|
||||||
|
// a drag/resize followed straight away by navigating to another route
|
||||||
|
// detaches this element inside the debounce window, which used to lose
|
||||||
|
// the very last layout change. The server is not notified — its view is
|
||||||
|
// going away with us.
|
||||||
|
this.flushPersist();
|
||||||
this.grid?.destroy(false); // keep DOM — Flow owns the children
|
this.grid?.destroy(false); // keep DOM — Flow owns the children
|
||||||
this.grid = undefined;
|
this.grid = undefined;
|
||||||
}
|
}
|
||||||
@@ -99,16 +104,28 @@ class GridStackLayout extends HTMLElement {
|
|||||||
|
|
||||||
private schedulePersist() {
|
private schedulePersist() {
|
||||||
clearTimeout(this.persistTimer);
|
clearTimeout(this.persistTimer);
|
||||||
this.persistTimer = setTimeout(() => this.persist(), 150);
|
this.persistTimer = setTimeout(() => {
|
||||||
|
this.persistTimer = undefined;
|
||||||
|
this.persist();
|
||||||
|
}, 150);
|
||||||
}
|
}
|
||||||
|
|
||||||
private persist() {
|
private flushPersist() {
|
||||||
|
if (this.persistTimer === undefined) return;
|
||||||
|
clearTimeout(this.persistTimer);
|
||||||
|
this.persistTimer = undefined;
|
||||||
|
this.persist(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private persist(notifyServer = true) {
|
||||||
if (!this.grid) return;
|
if (!this.grid) return;
|
||||||
const nodes = this.grid.save(false) as GridStackNode[];
|
const nodes = this.grid.save(false) as GridStackNode[];
|
||||||
if (this.storageKey) {
|
if (this.storageKey) {
|
||||||
localStorage.setItem(STORAGE_PREFIX + this.storageKey, JSON.stringify(nodes));
|
localStorage.setItem(STORAGE_PREFIX + this.storageKey, JSON.stringify(nodes));
|
||||||
}
|
}
|
||||||
(this as any).$server?.onLayoutChange(JSON.stringify(nodes));
|
if (notifyServer) {
|
||||||
|
(this as any).$server?.onLayoutChange(JSON.stringify(nodes));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private restore() {
|
private restore() {
|
||||||
@@ -122,13 +139,22 @@ class GridStackLayout extends HTMLElement {
|
|||||||
} catch {
|
} catch {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!Array.isArray(nodes)) return;
|
||||||
|
|
||||||
for (const node of nodes) {
|
// Items dropped server-side since the layout was saved are skipped, so a
|
||||||
if (!node.id) continue;
|
// stale entry degrades gracefully instead of blocking the restore.
|
||||||
const el = this.querySelector(`:scope > [gs-id="${node.id}"]`) as HTMLElement | null;
|
const known = nodes.filter((node) => node.id
|
||||||
if (!el) continue; // item no longer present server-side — skip, degrade gracefully
|
&& this.querySelector(`:scope > [gs-id="${CSS.escape(String(node.id))}"]`));
|
||||||
this.grid.update(el, { x: node.x, y: node.y, w: node.w, h: node.h });
|
if (!known.length) return;
|
||||||
}
|
|
||||||
|
// load() rather than a per-item update() loop: it sorts the saved nodes,
|
||||||
|
// pulls them out of the engine before re-placing them, and does the whole
|
||||||
|
// thing in one batch. Updating item by item let a later item collide with
|
||||||
|
// one already put back and push it off its saved spot for good — the
|
||||||
|
// restored layout was then not the one the user left. load() also re-applies
|
||||||
|
// gridstack's w/h defaults, which save() omits when they are 1.
|
||||||
|
// `false` keeps add/remove off: Flow owns which children exist.
|
||||||
|
this.grid.load(known, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private intAttr(el: HTMLElement, name: string): number | undefined {
|
private intAttr(el: HTMLElement, name: string): number | undefined {
|
||||||
|
|||||||
@@ -179,18 +179,17 @@ public class GridStackLayout extends Component implements HasSize, HasStyle {
|
|||||||
String id = node.path("id").asString(null);
|
String id = node.path("id").asString(null);
|
||||||
if (id == null) continue;
|
if (id == null) continue;
|
||||||
|
|
||||||
findItem(id).ifPresent(item -> item.setPosition(
|
// gridstack omits w/h from its saved layout when they are 1, so a
|
||||||
node.path("x").asInt(),
|
// missing value means 1 — defaulting to 0 wrote a zero-sized widget
|
||||||
node.path("y").asInt(),
|
// back onto the item and lost its size on the next attach.
|
||||||
node.path("w").asInt(),
|
int x = node.path("x").asInt(0);
|
||||||
node.path("h").asInt()));
|
int y = node.path("y").asInt(0);
|
||||||
|
int w = node.path("w").asInt(1);
|
||||||
|
int h = node.path("h").asInt(1);
|
||||||
|
|
||||||
positions.add(new GridStackItem.Position(
|
findItem(id).ifPresent(item -> item.setPosition(x, y, w, h));
|
||||||
id,
|
|
||||||
node.path("x").asInt(),
|
positions.add(new GridStackItem.Position(id, x, y, w, h));
|
||||||
node.path("y").asInt(),
|
|
||||||
node.path("w").asInt(),
|
|
||||||
node.path("h").asInt()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEvent(new LayoutChangeEvent(this, true, positions));
|
fireEvent(new LayoutChangeEvent(this, true, positions));
|
||||||
|
|||||||
Reference in New Issue
Block a user