Skip to content

Commit c647501

Browse files
committed
cellHeight:auto fix
- fix #1600 - big re-write on how `cellHeight()` works. you can now call it at any time (not just grid init options) including switching to 'auto' or other modes on the fly. - fix `cellHeight:auto` now keeps cell square as window resizes (regressing from 2.x TS conversion). `Utils.throttle()` works better too (guaranteed to be called last event) - new `cellHeight:initial` which makes the cell squares initially, but doesn't change as windows resizes (better performance) - new grid option `cellHeightThrottle` (100ms) to control throttle of auto sizing triggers - height too small with `cellHeight:auto` loading in 1 column. Now detect we load at 1 column and size accordingly (default 'auto' could make big 700x700 cells, so explicit px might still be wanted) - added demo/cell-height.html
1 parent fd117ba commit c647501

File tree

8 files changed

+154
-54
lines changed

8 files changed

+154
-54
lines changed

demo/cell-height.html

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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>cell height demo</title>
8+
9+
<link rel="stylesheet" href="demo.css"/>
10+
<script src="../dist/gridstack-h5.js"></script>
11+
<style type="text/css">
12+
.container {
13+
background-color: lightblue;
14+
width: 100%;
15+
height: 800px;
16+
}
17+
</style>
18+
19+
</head>
20+
<body>
21+
<div class="container">
22+
<h1>Cell Height grid options demo</h1>
23+
<p>sample showing the different cellHeight options and what happens when you resize the window</p>
24+
<div>
25+
<a class="btn btn-primary" onClick="setOpt('auto')" href="#">auto</a>
26+
<a class="btn btn-primary" onClick="setOpt('initial')" href="#">initial</a>
27+
<a class="btn btn-primary" onClick="setOpt(100)" href="#">100px</a>
28+
<a class="btn btn-primary" onClick="setOpt('10vh')" href="#">'10vh'</a>
29+
<a class="btn btn-primary" onClick="setOpt('10%')" href="#">'10%' of blue (broken)</a>
30+
<span>settings:</span><span id="info"></span>
31+
</div>
32+
<br>
33+
<div class="grid-stack"></div>
34+
</div>
35+
<script type="text/javascript">
36+
let opts = {
37+
cellHeight: 'auto', // see other possible values (best to do in here)
38+
cellHeightThrottle: 100,
39+
}
40+
let grid = GridStack.init(opts);
41+
let items = [
42+
{x:0, y:0, content:'0'},
43+
{x:1, y:0, w:2, content:'1'},
44+
{x:3, y:0, h:2, content:'2'},
45+
];
46+
grid.load(items);
47+
48+
let info = document.querySelector('#info');
49+
info.innerHTML = opts.cellHeight;
50+
setOpt = function(val) {
51+
grid.cellHeight(val);
52+
info.innerHTML = val;
53+
}
54+
</script>
55+
</body>
56+
</html>

demo/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ <h1>Demos</h1>
99
<ul>
1010
<li><a href="advance.html">Advance</a></li>
1111
<li><a href="anijs.html">AniJS</a></li>
12+
<li><a href="cell-height.html">Cell Height</a></li>
1213
<li><a href="column.html">Column</a></li>
1314
<li><a href="float.html">Float grid</a></li>
1415
<li><a href="knockout.html">Knockout.js</a></li>

demo/responsive.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ <h1>Responsive grid demo</h1>
2929

3030
<script type="text/javascript">
3131
let grid = GridStack.init({
32+
cellHeight: 70,
3233
disableOneColumnMode: true, // will manually do 1 column
3334
float: true });
3435
let text = document.querySelector('#column-text');

doc/CHANGES.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ Change log
4949
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
5050
## 3.2.0-dev
5151

52-
- TBD
52+
- big re-write on how `cellHeight()` works. you can now call it at any time (not just grid init options) including switching to 'auto' or other modes on the fly.
53+
- fix `cellHeight:auto` now keeps cell square as window resizes (regressing from 2.x TS conversion). `Utils.throttle()` works better too (guaranteed to be called last event)
54+
- new `cellHeight:initial` which makes the cell squares initially, but doesn't change as windows resizes (better performance)
55+
- new grid option `cellHeightThrottle` (100ms) to control throttle of auto sizing triggers
56+
- fix [1600](https://github.com/gridstack/gridstack.js/issues/1600) height too small with `cellHeight:auto` loading in 1 column. Now detect we load at 1 column and size accordingly (default 'auto' could make big 700x700 cells, so explicit px might still be wanted)
5357

5458
## 3.2.0 (2021-1-25)
5559

doc/README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,14 @@ gridstack.js API
7070
See [example](http://gridstack.github.io/gridstack.js/demo/mobile.html)
7171
- `animate` - turns animation on to smooth transitions (default: `true`)
7272
- `auto` - if `false` gridstack will not initialize existing items (default: `true`)
73-
- `cellHeight` - one cell height (default: `auto`). Can be:
74-
* an integer (px)
75-
* a string (ex: '100px', '10em', '10rem', '10%', `10vh')
76-
* 0 or null, in which case the library will not generate styles for rows. Everything must be defined in CSS files.
77-
* `'auto'` - height will be square cells initially.
73+
- `cellHeight`- one cell height (default?: 'auto'). Can be:
74+
* an integer (px)
75+
* a string (ex: '100px', '10em', '10rem'). Note: % doesn't right - see demo/cell-height.html
76+
* 0, in which case the library will not generate styles for rows. Everything must be defined in your own CSS files.
77+
* `auto` - height will be calculated for square cells (width / column) and updated live as you resize the window - also see `cellHeightThrottle`
78+
* `initial` - similar to 'auto' (start at square cells) but stay that size during window resizing.
79+
- `cellHeightThrottle`?: number - throttle time delay (in ms) used when cellHeight='auto' to improve performance vs usability (default?: 100).
80+
* A value of 0 will make it instant at a cost of re-creating the CSS file at ever window resize event!
7881
- `children`?: GridStackWidget[] - list of children item to create when calling load() or addGrid()
7982
- `column` - number of columns (default: `12`) which can change on the fly with `column(N)` as well. See [example](http://gridstackjs.com/demo/column.html)
8083
- `class`?: string - additional class on top of '.grid-stack' (which is required for our CSS) to differentiate this instance
@@ -284,8 +287,7 @@ re-layout grid items to reclaim any empty space.
284287

285288
### cellHeight(val: number, update = true)
286289

287-
Update current cell height. This method rebuilds an internal CSS stylesheet (unless optional update=false). Note: You can expect performance issues if
288-
call this method too often.
290+
Update current cell height (see - `cellHeight` options format). This method rebuilds an internal CSS stylesheet (unless optional update=false). Note: You can expect performance issues if call this method too often.
289291

290292
```js
291293
grid.cellHeight(grid.cellWidth() * 1.2);

src/gridstack.ts

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const GridDefaults: GridStackOptions = {
5252
handleClass: null,
5353
styleInHead: false,
5454
cellHeight: 'auto',
55+
cellHeightThrottle: 100,
5556
margin: 10,
5657
auto: true,
5758
minWidth: 768,
@@ -223,7 +224,9 @@ export class GridStack {
223224
/** @internal flag to keep cells square during resize */
224225
private _isAutoCellHeight: boolean;
225226
/** @internal track event binding to window resize so we can remove */
226-
private _windowResizeBind: () => GridStack;
227+
private _windowResizeBind: () => void;
228+
/** @internal limit auto cell resizing method */
229+
private _cellHeightThrottle: () => void;
227230
/** @internal true when loading items to insert first rather than append */
228231
private _insertNotAppend: boolean;
229232

@@ -274,10 +277,16 @@ export class GridStack {
274277

275278
this.opts = Utils.defaults(opts, defaults);
276279
opts = null; // make sure we use this.opts instead
277-
this.initMargin();
280+
this.initMargin(); // part of settings defaults...
281+
282+
// Now check if we're loading into 1 column mode FIRST so we don't do un-necessary work (like cellHeight = width / 12 then go 1 column)
283+
if (this.opts.column !== 1 && !this.opts.disableOneColumnMode && this._widthOrContainer() <= this.opts.minWidth) {
284+
this._prevColumn = this.opts.column;
285+
this.opts.column = 1;
286+
}
278287

279288
if (this.opts.rtl === 'auto') {
280-
this.opts.rtl = el.style.direction === 'rtl';
289+
this.opts.rtl = (el.style.direction === 'rtl');
281290
}
282291

283292
if (this.opts.rtl) {
@@ -293,11 +302,9 @@ export class GridStack {
293302
}
294303

295304
this._isAutoCellHeight = (this.opts.cellHeight === 'auto');
296-
if (this._isAutoCellHeight) {
305+
if (this._isAutoCellHeight || this.opts.cellHeight === 'initial') {
297306
// make the cell content square initially (will use resize event to keep it square)
298-
let marginDiff = - (this.opts.marginRight as number) - (this.opts.marginLeft as number)
299-
+ (this.opts.marginTop as number) + (this.opts.marginBottom as number);
300-
this.cellHeight(this.cellWidth() + marginDiff, false);
307+
this.cellHeight(undefined, false);
301308
} else {
302309
this.cellHeight(this.opts.cellHeight, false);
303310
}
@@ -352,7 +359,7 @@ export class GridStack {
352359
this._setupDragIn();
353360
this._setupRemoveDrop();
354361
this._setupAcceptWidget();
355-
this._updateWindowResizeEvent(); // finally this may size us down to 1 column
362+
this._updateWindowResizeEvent();
356363
}
357364

358365
/**
@@ -574,13 +581,33 @@ export class GridStack {
574581
* This method rebuilds an internal CSS style sheet.
575582
* Note: You can expect performance issues if call this method too often.
576583
*
577-
* @param val the cell height
584+
* @param val the cell height. If not passed (undefined), cells content will be made square (match width minus margin),
585+
* if pass 0 the CSS will be generated by the application instead.
578586
* @param update (Optional) if false, styles will not be updated
579587
*
580588
* @example
589+
* grid.cellHeight(100); // same as 100px
590+
* grid.cellHeight('70px');
581591
* grid.cellHeight(grid.cellWidth() * 1.2);
582592
*/
583-
public cellHeight(val: numberOrString, update = true): GridStack {
593+
public cellHeight(val?: numberOrString, update = true): GridStack {
594+
595+
// if not called internally, check if we're changing mode
596+
if (update && val !== undefined) {
597+
if (this._isAutoCellHeight !== (val === 'auto')) {
598+
this._isAutoCellHeight = (val === 'auto');
599+
this._updateWindowResizeEvent();
600+
}
601+
}
602+
if (val === 'initial' || val === 'auto') { val = undefined; }
603+
604+
// make item content be square
605+
if (val === undefined) {
606+
let marginDiff = - (this.opts.marginRight as number) - (this.opts.marginLeft as number)
607+
+ (this.opts.marginTop as number) + (this.opts.marginBottom as number);
608+
val = this.cellWidth() + marginDiff;
609+
}
610+
584611
let data = Utils.parseHeight(val);
585612
if (this.opts.cellHeightUnit === data.unit && this.opts.cellHeight === data.h) {
586613
return this;
@@ -595,12 +622,15 @@ export class GridStack {
595622
return this;
596623
}
597624

598-
/**
599-
* Gets current cell width.
600-
*/
625+
/** Gets current cell width. */
601626
public cellWidth(): number {
602-
// use parent width if we're 0 (no size yet)
603-
return (this.el.offsetWidth || this.el.parentElement.offsetWidth || window.innerWidth) / this.opts.column;
627+
return this._widthOrContainer() / this.opts.column;
628+
}
629+
/** return our expected width (or parent) for 1 column check */
630+
private _widthOrContainer(): number {
631+
// use `offsetWidth` or `clientWidth` (no scrollbar) ?
632+
// https://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively
633+
return (this.el.clientWidth || this.el.parentElement.clientWidth || window.innerWidth);
604634
}
605635

606636
/**
@@ -1307,40 +1337,42 @@ export class GridStack {
13071337
*/
13081338
public onParentResize(): GridStack {
13091339
if (!this.el || !this.el.clientWidth) return; // return if we're gone or no size yet (will get called again)
1310-
1311-
// make the cells content (minus margin) square again
1312-
if (this._isAutoCellHeight) {
1313-
Utils.throttle(() => {
1314-
let marginDiff = - (this.opts.marginRight as number) - (this.opts.marginLeft as number)
1315-
+ (this.opts.marginTop as number) + (this.opts.marginBottom as number);
1316-
this.cellHeight(this.cellWidth() + marginDiff);
1317-
}, 100);
1340+
let changedOneColumn = false;
1341+
let oneColumn = !this.opts.disableOneColumnMode && this.el.clientWidth <= this.opts.minWidth;
1342+
1343+
if (!this._oneColumnMode !== !oneColumn) { // use ! (negate) so we can check undefined == false vs true
1344+
this._oneColumnMode = oneColumn;
1345+
changedOneColumn = true;
1346+
if (this.opts.animate) { this.setAnimation(false); } // 1 <-> 12 is too radical, turn off animation
1347+
this.column(oneColumn ? 1 : this._prevColumn);
1348+
this._resizeNestedGrids(this.el);
1349+
if (this.opts.animate) { this.setAnimation(true); }
13181350
}
13191351

1320-
if (!this.opts.disableOneColumnMode && this.el.clientWidth <= this.opts.minWidth) {
1321-
if (this._oneColumnMode) return this;
1322-
this._oneColumnMode = true;
1323-
this.column(1);
1324-
this._resizeNestedGrids(this.el);
1325-
} else {
1326-
if (!this._oneColumnMode) return this;
1327-
delete this._oneColumnMode;
1328-
this.column(this._prevColumn);
1329-
this._resizeNestedGrids(this.el);
1352+
// make the cells content square again
1353+
if (this._isAutoCellHeight) {
1354+
if (!changedOneColumn && this.opts.cellHeightThrottle) {
1355+
if (!this._cellHeightThrottle) {
1356+
this._cellHeightThrottle = Utils.throttle(() => this.cellHeight(), this.opts.cellHeightThrottle);
1357+
}
1358+
this._cellHeightThrottle();
1359+
} else {
1360+
// immediate update if we've changed to/from oneColumn or have no threshold
1361+
this.cellHeight();
1362+
}
13301363
}
13311364

13321365
return this;
13331366
}
13341367

13351368
/** add or remove the window size event handler */
13361369
private _updateWindowResizeEvent(forceRemove = false): GridStack {
1337-
const workTodo = (this._isAutoCellHeight || !this.opts.disableOneColumnMode);
1338-
13391370
// only add event if we're not nested (parent will call us) and we're auto sizing cells or supporting oneColumn (i.e. doing work)
1340-
if (workTodo && !forceRemove && !this.opts._isNested && !this._windowResizeBind) {
1371+
const workTodo = (this._isAutoCellHeight || !this.opts.disableOneColumnMode) && !this.opts._isNested;
1372+
1373+
if (!forceRemove && workTodo && !this._windowResizeBind) {
13411374
this._windowResizeBind = this.onParentResize.bind(this); // so we can properly remove later
13421375
window.addEventListener('resize', this._windowResizeBind);
1343-
this.onParentResize(); // initially call it once...
13441376
} else if ((forceRemove || !workTodo) && this._windowResizeBind) {
13451377
window.removeEventListener('resize', this._windowResizeBind);
13461378
delete this._windowResizeBind; // remove link to us so we can free

src/types.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,20 @@ export interface GridStackOptions {
5252
auto?: boolean;
5353

5454
/**
55-
* one cell height (default?: 60). Can be:
55+
* one cell height (default?: 'auto'). Can be:
5656
* an integer (px)
57-
* a string (ex: '100px', '10em', '10rem', '10%')
58-
* 0 or null, in which case the library will not generate styles for rows. Everything must be defined in CSS files.
59-
* 'auto' - height will be calculated to match cell width (initial square grid).
57+
* a string (ex: '100px', '10em', '10rem'). Note: % doesn't right - see demo/cell-height.html
58+
* 0, in which case the library will not generate styles for rows. Everything must be defined in your own CSS files.
59+
* 'auto' - height will be calculated for square cells (width / column) and updated live as you resize the window - also see `cellHeightThrottle`
60+
* 'initial' - similar to 'auto' (start at square cells) but stay that size during window resizing.
6061
*/
6162
cellHeight?: numberOrString;
6263

64+
/** throttle time delay (in ms) used when cellHeight='auto' to improve performance vs usability (default?: 100).
65+
* A value of 0 will make it instant at a cost of re-creating the CSS file at ever window resize event!
66+
* */
67+
cellHeightThrottle?: number;
68+
6369
/** (internal) unit for cellHeight (default? 'px') which is set when a string cellHeight with a unit is passed (ex: '10rem') */
6470
cellHeightUnit?: string;
6571

src/utils.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -245,15 +245,13 @@ export class Utils {
245245
return null;
246246
}
247247

248-
/** delay calling the given function by certain amount of time */
249-
static throttle(callback: () => void, delay: number): () => void {
248+
/** delay calling the given function for given delay, preventing new calls from happening while waiting */
249+
static throttle(func: () => void, delay: number): () => void {
250250
let isWaiting = false;
251-
252251
return (...args) => {
253252
if (!isWaiting) {
254-
callback.apply(this, args);
255253
isWaiting = true;
256-
setTimeout(() => isWaiting = false, delay);
254+
setTimeout(() => { func(...args); isWaiting = false; }, delay);
257255
}
258256
}
259257
}

0 commit comments

Comments
 (0)