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

lazyLoad support to delay load widget content until visible #2826

Merged
merged 1 commit into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ <h1>Demos</h1>
<!-- <li><a href="esmodule.html">ES Module test</a></li> -->
<li><a href="float.html">Float grid</a></li>
<li><a href="knockout.html">Knockout.js</a></li>
<li><a href="lazy_load.html">Lazy Load</a></li>
<li><a href="mobile.html">Mobile touch</a></li>
<li><a href="nested.html">Nested grids</a></li>
<li><a href="nested_advanced.html">Nested Advanced grids</a></li>
Expand Down
36 changes: 36 additions & 0 deletions demo/lazy_load.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Lazy loading demo</title>

<link rel="stylesheet" href="demo.css"/>
<script src="../dist/gridstack-all.js"></script>

</head>
<body>
<div>
<h1>Lazy loading demo</h1>
<p>New V11 GridStackWidget.lazyLoad feature. open console and see widget content (or angular components) created as they become visible.</p>
<div style="height: 300px; overflow-y: auto">
<div class="grid-stack"></div>
</div>
</div>
<script type="text/javascript">
// print when widgets are created
GridStack.renderCB = function(el, w) {
el.textContent = w.content;
console.log('created node id ', w.id);
};
let children = [];
for (let y = 1; y <= 5; y++) children.push({x:0, y, id:y, content: String(y)});
let grid = GridStack.init({
cellHeight: 200,
children,
lazyLoad: true, // delay creation until visible
});
</script>
</body>
</html>
1 change: 1 addition & 0 deletions doc/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Change log
* 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)
big overall to how we do sidepanel drag&drop helper. see release notes.
* 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.
* feat: Lazy loading of widget content until visible (`GridStackOptions.lazyLoad` and `GridStackWidget.lazyLoad`)

## 10.3.1 (2024-07-21)
* fix: [#2734](https://github.com/gridstack/gridstack.js/bug/2734) rotate() JS error
Expand Down
4 changes: 3 additions & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ gridstack.js API
- `handle` - draggable handle selector (default: `'.grid-stack-item-content'`)
- `handleClass` - draggable handle class (e.g. `'grid-stack-item-content'`). If set `handle` is ignored (default: `null`)
- `itemClass` - widget class (default: `'grid-stack-item'`)
- `lazyLoad?`: boolean - true when widgets are only created when they scroll into view (visible). also overridable per widget in `GridStackWidget`
- `margin` - gap size around grid item and content (default: `10`). Can be:
* an integer (px)
* a string (ex: '2em', '20px', '2rem')
Expand Down Expand Up @@ -155,7 +156,7 @@ most of the above options are also available as HTML attributes using the `gs-`
Extras:
- `gs-current-row` - (internal) current rows amount. Set by the library only. Can be used by the CSS rules.

## Item Options
## Item Options - GridStackWidget

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

Expand All @@ -174,6 +175,7 @@ You need to add `noResize` and `noMove` attributes to completely lock the widget
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)
- `subGrid`?: GridStackOptions - optional nested grid options and list of children
- `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.
- `lazyLoad?`: boolean - true when widgets are only created when they scroll into view (visible). also optin on entire grid.

## Item attributes

Expand Down
3 changes: 2 additions & 1 deletion src/gridstack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,13 @@ export class GridStack {

let el: GridItemHTMLElement;
let node: GridStackNode = w;
node.grid = this;
if (node?.el) {
el = node.el; // re-use element stored in the node
} else if (GridStack.addRemoveCB) {
el = GridStack.addRemoveCB(this.el, w, true, false);
} else {
el = Utils.createWidgetDivs(this.opts.itemClass, w);
el = Utils.createWidgetDivs(this.opts.itemClass, node);
}

if (!el) return;
Expand Down
7 changes: 7 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ export interface GridStackOptions {
/** additional widget class (default?: 'grid-stack-item') */
itemClass?: string;

/** true when widgets are only created when they scroll into view (visible) */
lazyLoad?: boolean;

/**
* gap between grid item and content (default?: 10). This will set all 4 sides and support the CSS formats below
* an integer (px)
Expand Down Expand Up @@ -331,6 +334,8 @@ export interface GridStackWidget extends GridStackPosition {
id?: string;
/** html to append inside as content */
content?: string;
/** true when widgets are only created when they scroll into view (visible) */
lazyLoad?: boolean;
/** local (vs grid) override - see GridStackOptions.
* 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) */
sizeToContent?: boolean | number;
Expand Down Expand Up @@ -447,4 +452,6 @@ export interface GridStackNode extends GridStackWidget {
_removeDOM?: boolean;
/** @internal had drag&drop been initialized */
_initDD?: boolean;
/** @internal allow delay creation when visible */
_visibleObservable?: IntersectionObserver;
}
22 changes: 16 additions & 6 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,29 @@ export class Utils {
return els;
}

/** create the default grid item divs */
static createWidgetDivs(itemClass?: string, w?: GridStackWidget): HTMLElement {
/** create the default grid item divs, and content possibly lazy loaded calling GridStack.renderCB */
static createWidgetDivs(itemClass: string, n: GridStackNode): HTMLElement {
const el = Utils.createDiv(['grid-stack-item', itemClass]);
Utils.createDiv(['grid-stack-item-content'], el, w);
const cont = Utils.createDiv(['grid-stack-item-content'], el);

const lazyLoad = n.lazyLoad || n.grid?.opts?.lazyLoad && n.lazyLoad !== false;
if (lazyLoad) {
n._visibleObservable = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) {
n._visibleObservable.disconnect();
delete n._visibleObservable;
GridStack.renderCB(cont, n)
}});
window.setTimeout(() => n._visibleObservable.observe(el)); // wait until callee sets position attributes
} else GridStack.renderCB(cont, n);

return el;
}

/** create a div (possibly 2) with the given classes */
static createDiv(classes: string[], parent?: HTMLElement, w?: GridStackWidget): HTMLElement {
/** create a div with the given classes */
static createDiv(classes: string[], parent?: HTMLElement): HTMLElement {
const el = document.createElement('div');
classes.forEach(c => {if (c) el.classList.add(c)});
parent?.appendChild(el);
if (w) GridStack.renderCB(el, w);
return el;
}

Expand Down