Skip to content

Commit

Permalink
nested grid column:'auto' option
Browse files Browse the repository at this point in the history
* partial fix #1009
* we now have a new column:'auto' option which is used for nested grids to column size themselves to match their container (same # of column)
This make sure outside and inside items stay the same size
* this will eventually be required when creating grids on the fly dynamically (as good default)
* updated nested.html to showcase this
  • Loading branch information
adumesny committed Dec 30, 2021
1 parent f280a1c commit 83e11d2
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 42 deletions.
35 changes: 19 additions & 16 deletions demo/nested.html
Expand Up @@ -9,11 +9,13 @@
<link rel="stylesheet" href="../dist/gridstack-extra.min.css"/>
<script src="../dist/gridstack-h5.js"></script>
<style type="text/css">
.grid-stack .grid-stack {
background: rgba(255, 255, 255, 0.3);
/* make nested grids have slightly darker bg */
.grid-stack.grid-stack-nested {
background: #e4e4c1;
}
.grid-stack .grid-stack .grid-stack-item-content {
background: lightpink;
/* make nested grid take almost all space (need some to tell them apart) so items inside can have similar to external size+margin */
.grid-stack > .grid-stack-item.grid-stack-nested > .grid-stack-item-content {
inset: 2px;
}
/* make nested grid take entire item content */
.grid-stack-item-content .grid-stack {
Expand All @@ -25,11 +27,12 @@
<body>
<div class="container-fluid">
<h1>Nested grids demo</h1>
<p>This example uses new v3.1 API to load the entire nested grid from JSON, and shows dragging between nested grid items (pink) vs dragging higher items (green)</p>
<p>Note: HTML5 release doesn't yet support 'dragOut:false' constrain so use JQ version if you need that (nested 2 case).</p>
<p>This example shows v5.x dragging between nested grids (dark yellow) and parent grid (bright yellow.)<br>
Uses v3.1 API to load the entire nested grid from JSON.<br>
Nested grids uses new <b>column:'auto'</b> to keep items same size during resize.</p>
<a class="btn btn-primary" onClick="addNested()" href="#">Add Widget</a>
<a class="btn btn-primary" onClick="addNewWidget('.nested1')" href="#">Add Widget Grid1</a>
<a class="btn btn-primary" onClick="addNewWidget('.nested2')" href="#">Add Widget Grid2</a>
<a class="btn btn-primary" onClick="addNewWidget('.sub1')" href="#">Add Widget Grid1</a>
<a class="btn btn-primary" onClick="addNewWidget('.sub2')" href="#">Add Widget Grid2</a>
<span>entire save/re-create:</span>
<a class="btn btn-primary" onClick="save()" href="#">Save</a>
<a class="btn btn-primary" onClick="destroy()" href="#">Destroy</a>
Expand All @@ -49,29 +52,29 @@ <h1>Nested grids demo</h1>
let count = 0;
[...sub1, ...sub2].forEach(d => d.content = String(count++));
let subOptions = {
cellHeight: 30,
column: 4, // make sure to include gridstack-extra.min.css
cellHeight: 50,
column: 'auto', // size to match container. make sure to include gridstack-extra.min.css
acceptWidgets: true, // will accept .grid-stack-item by default
minWidth: 300, // min to go 1 column mode (much smaller than default)
margin: 2
margin: 5,
};
let options = { // main grid options
cellHeight: 70,
cellHeight: 50,
margin: 5,
minRow: 2, // don't collapse when empty
acceptWidgets: true,
id: 'main',
children: [
{y:0, content: 'regular item'},
{x:1, w:4, h:4, subGrid: {children: sub1, dragOut: true, id: 'sub1', ...subOptions}},
{x:5, w:4, h:4, subGrid: {children: sub2, id: 'sub2', ...subOptions}},
{x:1, w:4, h:4, subGrid: {children: sub1, dragOut: true, class: 'sub1', ...subOptions}},
{x:5, w:3, h:4, subGrid: {children: sub2, class: 'sub2', ...subOptions}},
]
};

// create and load it all from JSON above
let grid = GridStack.addGrid(document.querySelector('.container-fluid'), options);

addNested = function() {
grid.addWidget({x:0, y:0, content:"new item"});
grid.addWidget({x:0, y:100, content:"new item"});
}

addNewWidget = function(selector) {
Expand Down
1 change: 1 addition & 0 deletions doc/CHANGES.md
Expand Up @@ -68,6 +68,7 @@ Change log

## 4.4.1-dev (TBD)
* add [#992](https://github.com/gridstack/gridstack.js/issues/992) support dragging into and out of nested grids from parents! Thank you [@arclogos132](https://github.com/arclogos132) for sponsoring it.
* add [#1910](https://github.com/gridstack/gridstack.js/pull/1910) new `column:'auto'` option to size nested grids to their parent grid item column count, keeping items the same size inside and outside. Thank you [@arclogos132](https://github.com/arclogos132) for also sponsoring it.
* fix [#1902](https://github.com/gridstack/gridstack.js/pull/1902) nested.html: dragging between sub-grids show items clipped
* fix [#1558](https://github.com/gridstack/gridstack.js/issues/1558) dragging between vertical grids causes too much growth, not follow mouse.

Expand Down
15 changes: 10 additions & 5 deletions doc/README.md
Expand Up @@ -34,7 +34,7 @@ gridstack.js API
- [`cellHeight(val: number, update = true)`](#cellheightval-number-update--true)
- [`cellWidth()`](#cellwidth)
- [`commit()`](#commit)
- [`column(column: number, layout: ColumnOptions = 'moveScale')`](#columncolumn-number-layout-columnoptions--movescale)
- [`column(column: number | 'auto', layout: ColumnOptions = 'moveScale')`](#columncolumn-number--auto-layout-columnoptions--movescale)
- [`destroy([removeDOM])`](#destroyremovedom)
- [`disable()`](#disable)
- [`enable()`](#enable)
Expand All @@ -43,6 +43,7 @@ gridstack.js API
- [`float(val?)`](#floatval)
- [`getCellHeight()`](#getcellheight)
- [`getCellFromPixel(position[, useOffset])`](#getcellfrompixelposition-useoffset)
- [`getColumn(): number`](#getcolumn-number)
- [`getGridItems(): GridItemHTMLElement[]`](#getgriditems-griditemhtmlelement)
- [`getMargin()`](#getmargin)
- [`isAreaEmpty(x, y, width, height)`](#isareaemptyx-y-width-height)
Expand Down Expand Up @@ -365,14 +366,14 @@ Gets current cell width (grid width / # of columns).

Ends batch updates. Updates DOM nodes. You must call it after `batchUpdate()`.

### `column(column: number, layout: ColumnOptions = 'moveScale')`
### `column(column: number | 'auto', layout: ColumnOptions = 'moveScale')`

set/get the number of columns in the grid. Will update existing widgets to conform to new number of columns,
set the number of columns in the grid. Will update existing widgets to conform to new number of columns,
as well as cache the original layout so you can revert back to previous positions without loss.
Requires `gridstack-extra.css` or `gridstack-extra.min.css` for [2-11],
Requires `gridstack-extra.css` (or minimized version) for [2-11],
else you will need to generate correct CSS (see https://github.com/gridstack/gridstack.js#change-grid-columns)

- `column` - Integer > 0 (default 12), if missing it will return the current count instead.
- `column` - Integer > 0 (default 12), or 'auto' for nested grids to size themselves to the parent grid container (to make su-items the same size inside and outside)
- `layout` - specify the type of re-layout that will happen (position, size, etc...).
Note: items will never be outside of the current column boundaries. default ('moveScale'). Ignored for 1 column.
Possible values: 'moveScale' | 'move' | 'scale' | 'none' | (column: number, oldColumn: number, nodes: GridStackNode[], oldNodes: GridStackNode[]) => void.
Expand Down Expand Up @@ -445,6 +446,10 @@ Parameters :

Returns an object with properties `x` and `y` i.e. the column and row in the grid.

### `getColumn(): number`

returns the number of columns in the grid.

### `getGridItems(): GridItemHTMLElement[]`

Return list of GridItem HTML elements (excluding temporary placeholder) in DOM order, wether they are node items yet or not (looks by class)
Expand Down
69 changes: 50 additions & 19 deletions src/gridstack.ts
Expand Up @@ -228,6 +228,8 @@ export class GridStack {
private _insertNotAppend: boolean;
/** @internal extra row added when dragging at the bottom of the grid */
private _extraDragRow = 0;
/** @internal true if nested grid should get column count from our width */
private _autoColumn?: boolean;

/**
* Construct a grid item from the given element and options
Expand All @@ -245,6 +247,11 @@ export class GridStack {
}
let rowAttr = Utils.toNumber(el.getAttribute('gs-row'));

// flag only valid in sub-grids (handled by parent, not here)
if (opts.column === 'auto') {
delete opts.column;
}

// elements attributes override any passed options (like CSS style) - merge the two together
let defaults: GridStackOptions = {...Utils.cloneDeep(GridDefaults),
column: Utils.toNumber(el.getAttribute('gs-column')) || 12,
Expand Down Expand Up @@ -276,7 +283,7 @@ export class GridStack {

// 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)
if (this.opts.column !== 1 && !this.opts.disableOneColumnMode && this._widthOrContainer() <= this.opts.minWidth) {
this._prevColumn = this.opts.column;
this._prevColumn = this.getColumn();
this.opts.column = 1;
}

Expand Down Expand Up @@ -315,7 +322,7 @@ export class GridStack {
this._setStaticClass();

this.engine = new GridStackEngine({
column: this.opts.column,
column: this.getColumn(),
float: this.opts.float,
maxRow: this.opts.maxRow,
onChange: (cbNodes) => {
Expand Down Expand Up @@ -344,7 +351,7 @@ export class GridStack {
elements.push({
el,
// if x,y are missing (autoPosition) add them to end of list - but keep their respective DOM order
i: (Number.isNaN(x) ? 1000 : x) + (Number.isNaN(y) ? 1000 : y) * this.opts.column
i: (Number.isNaN(x) ? 1000 : x) + (Number.isNaN(y) ? 1000 : y) * this.getColumn()
});
});
elements.sort((a, b) => a.i - b.i).forEach(e => this._prepareElement(e.el));
Expand Down Expand Up @@ -435,8 +442,17 @@ export class GridStack {

// check if nested grid definition is present
if (node.subGrid && !(node.subGrid as GridStack).el) { // see if there is a sub-grid to create too
// if column special case it set, remember that flag and set default
let autoColumn: boolean;
let ops = node.subGrid as GridStackOptions;
if (ops.column === 'auto') {
ops.column = node.w;
ops.disableOneColumnMode = true; // driven by parent
autoColumn = true;
}
let content = node.el.querySelector('.grid-stack-item-content') as HTMLElement;
node.subGrid = GridStack.addGrid(content, node.subGrid as GridStackOptions);
if (autoColumn) { node.subGrid._autoColumn = true; }
}

this._triggerAddEvent();
Expand Down Expand Up @@ -483,7 +499,13 @@ export class GridStack {
delete o.marginTop; delete o.marginRight; delete o.marginBottom; delete o.marginLeft;
}
if (o.rtl === (this.el.style.direction === 'rtl')) { o.rtl = 'auto' }
if (this._isAutoCellHeight) { o.cellHeight = 'auto' }
if (this._isAutoCellHeight) {
o.cellHeight = 'auto'
}
if (this._autoColumn) {
o.column = 'auto';
delete o.disableOneColumnMode;
}
Utils.removeInternalAndSame(o, GridDefaults);
o.children = list;
return o;
Expand All @@ -503,7 +525,7 @@ export class GridStack {
* see http://gridstackjs.com/demo/serialization.html
**/
public load(layout: GridStackWidget[], addAndRemove: boolean | ((g: GridStack, w: GridStackWidget, add: boolean) => GridItemHTMLElement) = true): GridStack {
let items = GridStack.Utils.sort([...layout], -1, this._prevColumn || this.opts.column); // make copy before we mod/sort
let items = GridStack.Utils.sort([...layout], -1, this._prevColumn || this.getColumn()); // make copy before we mod/sort
this._insertNotAppend = true; // since create in reverse order...

// if we're loading a layout into 1 column (_prevColumn is set only when going to 1) and items don't fit, make sure to save
Expand Down Expand Up @@ -633,7 +655,7 @@ export class GridStack {

/** Gets current cell width. */
public cellWidth(): number {
return this._widthOrContainer() / this.opts.column;
return this._widthOrContainer() / this.getColumn();
}
/** return our expected width (or parent) for 1 column check */
private _widthOrContainer(): number {
Expand Down Expand Up @@ -671,7 +693,7 @@ export class GridStack {
*/
public column(column: number, layout: ColumnOptions = 'moveScale'): GridStack {
if (column < 1 || this.opts.column === column) return this;
let oldColumn = this.opts.column;
let oldColumn = this.getColumn();

// if we go into 1 column mode (which happens if we're sized less than minW unless disableOneColumnMode is on)
// then remember the original columns so we can restore.
Expand Down Expand Up @@ -709,7 +731,7 @@ export class GridStack {
* get the number of columns in the grid (default 12)
*/
public getColumn(): number {
return this.opts.column;
return this.opts.column as number;
}

/** returns an array of grid HTML elements (no placeholder) - used to iterate through our children in DOM order */
Expand Down Expand Up @@ -783,7 +805,7 @@ export class GridStack {
let relativeLeft = position.left - containerPos.left;
let relativeTop = position.top - containerPos.top;

let columnWidth = (box.width / this.opts.column);
let columnWidth = (box.width / this.getColumn());
let rowHeight = (box.height / parseInt(this.el.getAttribute('gs-current-row')));

return {x: Math.floor(relativeLeft / columnWidth), y: Math.floor(relativeTop / rowHeight)};
Expand Down Expand Up @@ -1336,29 +1358,38 @@ export class GridStack {

/**
* called when we are being resized by the window - check if the one Column Mode needs to be turned on/off
* and remember the prev columns we used, as well as check for auto cell height (square)
* and remember the prev columns we used, or get our count from parent, as well as check for auto cell height (square)
*/
public onParentResize(): GridStack {
if (!this.el || !this.el.clientWidth) return; // return if we're gone or no size yet (will get called again)
let oneColumn = !this.opts.disableOneColumnMode && this.el.clientWidth <= this.opts.minWidth;
let changedOneColumn = false;
let changedColumn = false;

if ((this.opts.column === 1) !== oneColumn) {
changedOneColumn = true;
if (this.opts.animate) { this.setAnimation(false); } // 1 <-> 12 is too radical, turn off animation
this.column(oneColumn ? 1 : this._prevColumn);
if (this.opts.animate) { this.setAnimation(true); }
// see if we're nested and take our column count from our parent....
if (this._autoColumn && this.opts._isNested) {
if (this.opts.column !== this.opts._isNested.w) {
changedColumn = true;
this.column(this.opts._isNested.w, 'none');
}
} else {
// else check for 1 column in/out behavior
let oneColumn = !this.opts.disableOneColumnMode && this.el.clientWidth <= this.opts.minWidth;
if ((this.opts.column === 1) !== oneColumn) {
changedColumn = true;
if (this.opts.animate) { this.setAnimation(false); } // 1 <-> 12 is too radical, turn off animation
this.column(oneColumn ? 1 : this._prevColumn);
if (this.opts.animate) { this.setAnimation(true); }
}
}

// make the cells content square again
if (this._isAutoCellHeight) {
if (!changedOneColumn && this.opts.cellHeightThrottle) {
if (!changedColumn && this.opts.cellHeightThrottle) {
if (!this._cellHeightThrottle) {
this._cellHeightThrottle = Utils.throttle(() => this.cellHeight(), this.opts.cellHeightThrottle);
}
this._cellHeightThrottle();
} else {
// immediate update if we've changed to/from oneColumn or have no threshold
// immediate update if we've changed column count or have no threshold
this.cellHeight();
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/types.ts
Expand Up @@ -71,8 +71,11 @@ export interface GridStackOptions {
/** list of children item to create when calling load() or addGrid() */
children?: GridStackWidget[];

/** number of columns (default?: 12). Note: IF you change this, CSS also have to change. See https://github.com/gridstack/gridstack.js#change-grid-columns */
column?: number;
/** number of columns (default?: 12). Note: IF you change this, CSS also have to change. See https://github.com/gridstack/gridstack.js#change-grid-columns.
* Note: for nested grids, it is recommended to use 'auto' which will always match the container grid-item current width (in column) to keep inside and outside
* items always to same. flag is ignored for non nested grids.
*/
column?: number | 'auto';

/** additional class on top of '.grid-stack' (which is required for our CSS) to differentiate this instance.
Note: only used by addGrid(), else your element should have the needed class */
Expand Down

0 comments on commit 83e11d2

Please sign in to comment.