Skip to content

Commit e0403fd

Browse files
authored
Merge pull request #2826 from adumesny/master
lazyLoad support to delay load widget content until visible
2 parents 6f2fce4 + cf96cc5 commit e0403fd

File tree

7 files changed

+66
-8
lines changed

7 files changed

+66
-8
lines changed

demo/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ <h1>Demos</h1>
1515
<!-- <li><a href="esmodule.html">ES Module test</a></li> -->
1616
<li><a href="float.html">Float grid</a></li>
1717
<li><a href="knockout.html">Knockout.js</a></li>
18+
<li><a href="lazy_load.html">Lazy Load</a></li>
1819
<li><a href="mobile.html">Mobile touch</a></li>
1920
<li><a href="nested.html">Nested grids</a></li>
2021
<li><a href="nested_advanced.html">Nested Advanced grids</a></li>

demo/lazy_load.html

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<title>Lazy loading demo</title>
8+
9+
<link rel="stylesheet" href="demo.css"/>
10+
<script src="../dist/gridstack-all.js"></script>
11+
12+
</head>
13+
<body>
14+
<div>
15+
<h1>Lazy loading demo</h1>
16+
<p>New V11 GridStackWidget.lazyLoad feature. open console and see widget content (or angular components) created as they become visible.</p>
17+
<div style="height: 300px; overflow-y: auto">
18+
<div class="grid-stack"></div>
19+
</div>
20+
</div>
21+
<script type="text/javascript">
22+
// print when widgets are created
23+
GridStack.renderCB = function(el, w) {
24+
el.textContent = w.content;
25+
console.log('created node id ', w.id);
26+
};
27+
let children = [];
28+
for (let y = 1; y <= 5; y++) children.push({x:0, y, id:y, content: String(y)});
29+
let grid = GridStack.init({
30+
cellHeight: 200,
31+
children,
32+
lazyLoad: true, // delay creation until visible
33+
});
34+
</script>
35+
</body>
36+
</html>

doc/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ Change log
119119
* fix: [#2231](https://github.com/gridstack/gridstack.js/bug/2231),[#1840](https://github.com/gridstack/gridstack.js/bug/1840),[#2354](https://github.com/gridstack/gridstack.js/bug/2354)
120120
big overall to how we do sidepanel drag&drop helper. see release notes.
121121
* feat: [#2818](https://github.com/gridstack/gridstack.js/pull/2818) support for Angular Component hosting true sub-grids (that size according to parent) without requring them to be only child of grid-item-content.
122+
* feat: Lazy loading of widget content until visible (`GridStackOptions.lazyLoad` and `GridStackWidget.lazyLoad`)
122123

123124
## 10.3.1 (2024-07-21)
124125
* fix: [#2734](https://github.com/gridstack/gridstack.js/bug/2734) rotate() JS error

doc/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ gridstack.js API
106106
- `handle` - draggable handle selector (default: `'.grid-stack-item-content'`)
107107
- `handleClass` - draggable handle class (e.g. `'grid-stack-item-content'`). If set `handle` is ignored (default: `null`)
108108
- `itemClass` - widget class (default: `'grid-stack-item'`)
109+
- `lazyLoad?`: boolean - true when widgets are only created when they scroll into view (visible). also overridable per widget in `GridStackWidget`
109110
- `margin` - gap size around grid item and content (default: `10`). Can be:
110111
* an integer (px)
111112
* a string (ex: '2em', '20px', '2rem')
@@ -155,7 +156,7 @@ most of the above options are also available as HTML attributes using the `gs-`
155156
Extras:
156157
- `gs-current-row` - (internal) current rows amount. Set by the library only. Can be used by the CSS rules.
157158

158-
## Item Options
159+
## Item Options - GridStackWidget
159160

160161
options you can pass when calling `addWidget()`, `update()`, `load()` and many others
161162

@@ -174,6 +175,7 @@ You need to add `noResize` and `noMove` attributes to completely lock the widget
174175
Note: This also allow you to set a maximum h value (but user changeable during normal resizing) to prevent unlimited content from taking too much space (get scrollbar)
175176
- `subGrid`?: GridStackOptions - optional nested grid options and list of children
176177
- `subGridDynamic`?: boolean - enable/disable the creation of sub-grids on the fly by dragging items completely over others (nest) vs partially (push). Forces `DDDragOpt.pause=true` to accomplish that.
178+
- `lazyLoad?`: boolean - true when widgets are only created when they scroll into view (visible). also optin on entire grid.
177179

178180
## Item attributes
179181

src/gridstack.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,12 +461,13 @@ export class GridStack {
461461

462462
let el: GridItemHTMLElement;
463463
let node: GridStackNode = w;
464+
node.grid = this;
464465
if (node?.el) {
465466
el = node.el; // re-use element stored in the node
466467
} else if (GridStack.addRemoveCB) {
467468
el = GridStack.addRemoveCB(this.el, w, true, false);
468469
} else {
469-
el = Utils.createWidgetDivs(this.opts.itemClass, w);
470+
el = Utils.createWidgetDivs(this.opts.itemClass, node);
470471
}
471472

472473
if (!el) return;

src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ export interface GridStackOptions {
191191
/** additional widget class (default?: 'grid-stack-item') */
192192
itemClass?: string;
193193

194+
/** true when widgets are only created when they scroll into view (visible) */
195+
lazyLoad?: boolean;
196+
194197
/**
195198
* gap between grid item and content (default?: 10). This will set all 4 sides and support the CSS formats below
196199
* an integer (px)
@@ -331,6 +334,8 @@ export interface GridStackWidget extends GridStackPosition {
331334
id?: string;
332335
/** html to append inside as content */
333336
content?: string;
337+
/** true when widgets are only created when they scroll into view (visible) */
338+
lazyLoad?: boolean;
334339
/** local (vs grid) override - see GridStackOptions.
335340
* Note: This also allow you to set a maximum h value (but user changeable during normal resizing) to prevent unlimited content from taking too much space (get scrollbar) */
336341
sizeToContent?: boolean | number;
@@ -447,4 +452,6 @@ export interface GridStackNode extends GridStackWidget {
447452
_removeDOM?: boolean;
448453
/** @internal had drag&drop been initialized */
449454
_initDD?: boolean;
455+
/** @internal allow delay creation when visible */
456+
_visibleObservable?: IntersectionObserver;
450457
}

src/utils.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,29 @@ export class Utils {
110110
return els;
111111
}
112112

113-
/** create the default grid item divs */
114-
static createWidgetDivs(itemClass?: string, w?: GridStackWidget): HTMLElement {
113+
/** create the default grid item divs, and content possibly lazy loaded calling GridStack.renderCB */
114+
static createWidgetDivs(itemClass: string, n: GridStackNode): HTMLElement {
115115
const el = Utils.createDiv(['grid-stack-item', itemClass]);
116-
Utils.createDiv(['grid-stack-item-content'], el, w);
116+
const cont = Utils.createDiv(['grid-stack-item-content'], el);
117+
118+
const lazyLoad = n.lazyLoad || n.grid?.opts?.lazyLoad && n.lazyLoad !== false;
119+
if (lazyLoad) {
120+
n._visibleObservable = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) {
121+
n._visibleObservable.disconnect();
122+
delete n._visibleObservable;
123+
GridStack.renderCB(cont, n)
124+
}});
125+
window.setTimeout(() => n._visibleObservable.observe(el)); // wait until callee sets position attributes
126+
} else GridStack.renderCB(cont, n);
127+
117128
return el;
118129
}
119130

120-
/** create a div (possibly 2) with the given classes */
121-
static createDiv(classes: string[], parent?: HTMLElement, w?: GridStackWidget): HTMLElement {
131+
/** create a div with the given classes */
132+
static createDiv(classes: string[], parent?: HTMLElement): HTMLElement {
122133
const el = document.createElement('div');
123134
classes.forEach(c => {if (c) el.classList.add(c)});
124135
parent?.appendChild(el);
125-
if (w) GridStack.renderCB(el, w);
126136
return el;
127137
}
128138

0 commit comments

Comments
 (0)