diff --git a/README.md b/README.md index b9b9101e2..24633d688 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Join us on Slack: https://gridstackjs.troolee.com - [Touch devices support](#touch-devices-support) - [Migrating to v0.6.x](#migrating-to-v06x) - [Migrating to v1.0.0](#migrating-to-v100) + - [Migrating to v2.0.0](#migrating-to-v200) - [Changes](#changes) - [The Team](#the-team) @@ -323,6 +324,20 @@ Recommend looking at the [many samples](./demo) for more code examples. We're working on implementing HTML5 drag'n'drop through the plugin system. Right now it is still jquery-ui based. Because of that we are still bundling `jquery` (3.4.1) + `jquery-ui` (1.12.1 minimal drag|drop|resize) internally in `gridstack.all.js`. IFF your app needs to bring it's own version instead, you should **instead** include `gridstack-poly.min.js` (optional IE support) + `gridstack.min.js` + `gridstack.jQueryUI.min.js` + after you import your libs. +## Migrating to v2.0.0 + +make sure to read v1.0.0 migration first! + +v2.x is a Typescript rewrite of 1.x, using classes and overall code cleanup. You code might not need change from 1.x + +In general methods that used optional args as getting vs setting are not used in Typescript. +Also legacy methods that used to take tons of parameters will now take an object. + +``` +removed `addWidget(el, x, y, width, ...)` --> use the widget options version instead `addWidget(el, {x, y, with,...})` +`float()` to get value --> `getFloat()` +``` + Changes ===== diff --git a/demo/anijs.html b/demo/anijs.html index a4def831d..6acecfbe4 100644 --- a/demo/anijs.html +++ b/demo/anijs.html @@ -38,7 +38,7 @@

Widget added

}); function addWidget() { - grid.addWidget('
', 0, 0, Math.floor(1 + 3 * Math.random()), Math.floor(1 + 3 * Math.random()), true); + grid.addWidget('
', {width: Math.floor(1 + 3 * Math.random()), height: Math.floor(1 + 3 * Math.random())}); }; var animationHelper = AniJS.getHelper(); diff --git a/demo/float.html b/demo/float.html index d1509e3af..edf7c094e 100644 --- a/demo/float.html +++ b/demo/float.html @@ -50,8 +50,8 @@

Float grid demo

}; toggleFloat = function() { - grid.float(! grid.float()); - document.querySelector('#float').innerHTML = 'float: ' + grid.float(); + grid.float(! grid.getFloat()); + document.querySelector('#float').innerHTML = 'float: ' + grid.getFloat(); }; addNewWidget(); diff --git a/demo/two.html b/demo/two.html index 8c58335ff..9967f10d9 100644 --- a/demo/two.html +++ b/demo/two.html @@ -135,8 +135,8 @@

Two grids demo

}); function toggleFloat(button, i) { - grids[i].float(! grids[i].float()); - button.innerHTML = 'float: ' + grids[i].float(); + grids[i].float(! grids[i].getFloat()); + button.innerHTML = 'float: ' + grids[i].getFloat(); } function compact(i) { diff --git a/doc/CHANGES.md b/doc/CHANGES.md index 698778586..bacab24d5 100644 --- a/doc/CHANGES.md +++ b/doc/CHANGES.md @@ -37,6 +37,7 @@ Change log - fix [1187](https://github.com/gridstack/gridstack.js/issues/1187) IE support for `CustomEvent` polyfill - thanks [@phil-blais](https://github.com/phil-blais) - fix [1204](https://github.com/gridstack/gridstack.js/issues/1204) destroy drag&drop when removing node(s) instead of just disabling it. - include SASS source files to npm package again [1193](https://github.com/gridstack/gridstack.js/pull/1193) - include SASS source files to npm package again [1193](https://github.com/gridstack/gridstack.js/pull/1193) +- fix [1217](https://github.com/gridstack/gridstack.js/issues/1217) If I set `cellHeight` to some `vh`, only first grid will take `vh`, rest will use `px` - add `getGridItems()` to return list of HTML grid items ## 1.1.0 (2020-02-29) diff --git a/doc/README.md b/doc/README.md index 3d78dbe30..dddaa1b60 100644 --- a/doc/README.md +++ b/doc/README.md @@ -22,7 +22,6 @@ gridstack.js API - [gsresizestop(event, ui)](#gsresizestopevent-ui) - [API](#api) - [addWidget(el, [options])](#addwidgetel-options) - - [addWidget(el, [x, y, width, height, autoPosition, minWidth, maxWidth, minHeight, maxHeight, id])](#addwidgetel-x-y-width-height-autoposition-minwidth-maxwidth-minheight-maxheight-id) - [batchUpdate()](#batchupdate) - [compact()](#compact) - [cellHeight()](#cellheight) @@ -239,30 +238,19 @@ grid.on('gsresizestop', function(event, element) { ### addWidget(el, [options]) -Creates new widget and returns it. Options is an object containing the fields x,y,width,height,etc... described below. - -### addWidget(el, [x, y, width, height, autoPosition, minWidth, maxWidth, minHeight, maxHeight, id]) - -Creates new widget and returns it. +Creates new widget and returns it. Options is an object containing the fields x,y,width,height,etc... Parameters: -- `el` - widget to add -- `x`, `y`, `width`, `height` - widget position/dimensions (optional) -- `autoPosition` - if `true` then `x`, `y` parameters will be ignored and widget will be places on the first available -position (optional) -- `minWidth` minimum width allowed during resize/creation (optional) -- `maxWidth` maximum width allowed during resize/creation (optional) -- `minHeight` minimum height allowed during resize/creation (optional) -- `maxHeight` maximum height allowed during resize/creation (optional) -- `id` value for `data-gs-id` (optional) +- `el` - html element or string definition to add +- `options` widget position/size options (optional) - see GridStackWidget Widget will be always placed even if result height is more than actual grid height. You need to use `willItFit` method before calling `addWidget` for additional check. ```js var grid = GridStack.init(); -grid.addWidget(el, 0, 0, 3, 2, true); +grid.addWidget('
hello
', {width: 3}); ``` ### batchUpdate() @@ -507,15 +495,14 @@ Returns `true` if the `height` of the grid will be less the vertical constraint. have `height` constraint. ```js -if (grid.willItFit(newNode.x, newNode.y, newNode.width, newNode.height, true)) { - grid.addWidget(newNode.el, newNode.x, newNode.y, newNode.width, newNode.height, true); +if (grid.willItFit(newNode.x, newNode.y, newNode.width, newNode.height, newNode.autoPosition)) { + grid.addWidget(newNode.el, newNode); } else { alert('Not enough free space to place the widget'); } ``` - ## Utils ### GridStack.Utils.sort(nodes[, dir[, width]]) diff --git a/spec/gridstack-spec.js b/spec/gridstack-spec.js index ffd7e5169..2473600d4 100644 --- a/spec/gridstack-spec.js +++ b/spec/gridstack-spec.js @@ -753,7 +753,8 @@ describe('gridstack', function() { }); it('should keep all widget options the same (autoPosition off', function() { var grid = GridStack.init({float: true});; - var widget = grid.addWidget(widgetHTML, 6, 7, 2, 3, false, 1, 4, 2, 5, 'coolWidget'); + var widget = grid.addWidget(widgetHTML, {x: 6, y:7, width:2, height:3, autoPosition:false, + mindWidth:1, maxWidth:4, mindHeight:2, maxHeight:5, id:'coolWidget'}); var $widget = $(widget); expect(parseInt($widget.attr('data-gs-x'), 10)).toBe(6); expect(parseInt($widget.attr('data-gs-y'), 10)).toBe(7); @@ -767,9 +768,9 @@ describe('gridstack', function() { expect($widget.attr('data-gs-id')).toBe('coolWidget'); // should move widget to top with float=false - expect(grid.float()).toBe(true); + expect(grid.getFloat()).toBe(true); grid.float(false); - expect(grid.float()).toBe(false); + expect(grid.getFloat()).toBe(false); expect(parseInt($widget.attr('data-gs-x'), 10)).toBe(6); expect(parseInt($widget.attr('data-gs-y'), 10)).toBe(4); // <--- from 7 to 4 below second original widget expect(parseInt($widget.attr('data-gs-width'), 10)).toBe(2); @@ -783,7 +784,7 @@ describe('gridstack', function() { // should not move again (no-op) grid.float(true); - expect(grid.float()).toBe(true); + expect(grid.getFloat()).toBe(true); expect(parseInt($widget.attr('data-gs-x'), 10)).toBe(6); expect(parseInt($widget.attr('data-gs-y'), 10)).toBe(4); expect(parseInt($widget.attr('data-gs-width'), 10)).toBe(2); @@ -806,7 +807,7 @@ describe('gridstack', function() { }); it('should change x, y coordinates for widgets.', function() { var grid = GridStack.init({float: true}); - var widget = grid.addWidget(widgetHTML, 9, 7, 2, 3, true); + var widget = grid.addWidget(widgetHTML, {x:9, y:7, width:2, height:3, autoPosition:true}); var $widget = $(widget); expect(parseInt($widget.attr('data-gs-x'), 10)).not.toBe(9); expect(parseInt($widget.attr('data-gs-y'), 10)).not.toBe(7); @@ -925,14 +926,14 @@ describe('gridstack', function() { it('should clear x position', function() { var grid = GridStack.init({float: true}); var widgetHTML = '
'; - var widget = grid.addWidget(widgetHTML, null, null, undefined); + var widget = grid.addWidget(widgetHTML, {x:null, y:null, width:undefined}); var $widget = $(widget); expect(parseInt($widget.attr('data-gs-x'), 10)).toBe(8); expect(parseInt($widget.attr('data-gs-y'), 10)).toBe(0); }); }); - describe('method float()', function() { + describe('method getFloat()', function() { beforeEach(function() { document.body.insertAdjacentHTML('afterbegin', gridstackHTML); }); @@ -941,15 +942,15 @@ describe('gridstack', function() { }); it('should match true/false only', function() { var grid = GridStack.init({float: true}); - expect(grid.float()).toBe(true); + expect(grid.getFloat()).toBe(true); grid.float(0); - expect(grid.float()).toBe(false); + expect(grid.getFloat()).toBe(false); grid.float(null); - expect(grid.float()).toBe(false); + expect(grid.getFloat()).toBe(false); grid.float(undefined); - expect(grid.float()).toBe(false); + expect(grid.getFloat()).toBe(false); grid.float(false); - expect(grid.float()).toBe(false); + expect(grid.getFloat()).toBe(false); }); }); diff --git a/spec/gridstack-tests.ts b/spec/gridstack-tests.ts deleted file mode 100644 index 94a7e00bb..000000000 --- a/spec/gridstack-tests.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { GridStack, GridstackOptions, MousePosition } from '../src/gridstack'; - -var options: GridstackOptions = { - float: true -}; - -var grid: GridStack = GridStack.init(options); -var gsFromElement: GridStack = GridStack.get(); -var gsFromElement2: GridStack = ($('.grid-stack').get(0) as any).gridstack; - -if (gsFromElement !== grid) throw Error('These should match!'); -if (gsFromElement2 !== grid) throw Error('These should match!'); - -var gridItem = '
Hello
' - -grid.addWidget(gridItem, {width: 2}); -grid.addWidget(gridItem, 1, 2, 3, 4, true); -grid.makeWidget(document.createElement('div')); -grid.batchUpdate(); -grid.cellHeight();; -grid.cellHeight(2); -grid.cellWidth(); -grid.getCellFromPixel({ left:20, top: 20 }); -grid.removeAll(false); diff --git a/src/gridstack-engine.ts b/src/gridstack-engine.ts index 294e77932..f6a6cf6ae 100644 --- a/src/gridstack-engine.ts +++ b/src/gridstack-engine.ts @@ -71,8 +71,16 @@ export class GridStackEngine { while (true) { let collisionNode = this.nodes.find( n => n !== node && Utils.isIntercepted(n, nn), {node: node, nn: nn}); if (!collisionNode) { return; } - this.moveNode(collisionNode, collisionNode.x, node.y + node.height, - collisionNode.width, collisionNode.height, true); + let moved; + if (collisionNode.locked) { + // if colliding with a locked item, move ourself instead + moved = this.moveNode(node, node.x, collisionNode.y + collisionNode.height, + node.width, node.height, true); + } else { + moved = this.moveNode(collisionNode, collisionNode.x, node.y + node.height, + collisionNode.width, collisionNode.height, true); + } + if (!moved) { return; } // break inf loop if we couldn't move after all (ex: maxRow, fixed) } } @@ -112,7 +120,7 @@ export class GridStackEngine { } /** float getter method */ - public get float(): boolean { return this._float; } + public get float(): boolean { return this._float || false; } private _sortNodes(dir?: -1 | 1) { this.nodes = Utils.sort(this.nodes, dir, this.column); @@ -180,13 +188,6 @@ export class GridStackEngine { let defaults = {width: 1, height: 1, x: 0, y: 0}; node = Utils.defaults(node, defaults); - // convert any strings over - /* TODO: check - node.x = parseInt(node.x); - node.y = parseInt(node.y); - node.width = parseInt(node.width); - node.height = parseInt(node.height); - */ node.autoPosition = node.autoPosition || false; node.noResize = node.noResize || false; node.noMove = node.noMove || false; @@ -197,19 +198,29 @@ export class GridStackEngine { if (Number.isNaN(node.width)) { node.width = defaults.width; } if (Number.isNaN(node.height)) { node.height = defaults.height; } + if (node.maxWidth) { node.width = Math.min(node.width, node.maxWidth); } + if (node.maxHeight) { node.height = Math.min(node.height, node.maxHeight); } + if (node.minWidth) { node.width = Math.max(node.width, node.minWidth); } + if (node.minHeight) { node.height = Math.max(node.height, node.minHeight); } + if (node.width > this.column) { node.width = this.column; } else if (node.width < 1) { node.width = 1; } - if (node.height < 1) { + if (this.maxRow && node.height > this.maxRow) { + node.height = this.maxRow; + } else if (node.height < 1) { node.height = 1; } if (node.x < 0) { node.x = 0; } + if (node.y < 0) { + node.y = 0; + } if (node.x + node.width > this.column) { if (resizing) { @@ -218,9 +229,12 @@ export class GridStackEngine { node.x = this.column - node.width; } } - - if (node.y < 0) { - node.y = 0; + if (this.maxRow && node.y + node.height > this.maxRow) { + if (resizing) { + node.height = this.maxRow - node.y; + } else { + node.y = this.maxRow - node.height; + } } return node; @@ -263,11 +277,6 @@ export class GridStackEngine { public addNode(node: GridStackNode, triggerAddEvent?: boolean) { node = this.prepareNode(node); - if (node.maxWidth) { node.width = Math.min(node.width, node.maxWidth); } - if (node.maxHeight) { node.height = Math.min(node.height, node.maxHeight); } - if (node.minWidth) { node.width = Math.max(node.width, node.minWidth); } - if (node.minHeight) { node.height = Math.max(node.height, node.minHeight); } - node._id = node._id || GridStackEngine._idSeq++; if (node.autoPosition) { @@ -393,34 +402,27 @@ export class GridStackEngine { } public moveNode(node: GridStackNode, x: number, y: number, width?: number, height?: number, noPack?: boolean): GridStackNode { + if (node.locked) { return null; } if (typeof x !== 'number') { x = node.x; } if (typeof y !== 'number') { y = node.y; } if (typeof width !== 'number') { width = node.width; } if (typeof height !== 'number') { height = node.height; } - if (node.maxWidth) { width = Math.min(width, node.maxWidth); } - if (node.maxHeight) { height = Math.min(height, node.maxHeight); } - if (node.minWidth) { width = Math.max(width, node.minWidth); } - if (node.minHeight) { height = Math.max(height, node.minHeight); } - - if (node.x === x && node.y === y && node.width === width && node.height === height) { - return node; + // constrain the passed in values and check if we're still changing our node + let resizing = (node.width !== width || node.height !== height); + let nn: GridStackNode = { x, y, width, height, + maxWidth: node.maxWidth, maxHeight: node.maxHeight, minWidth: node.minWidth, minHeight: node.minHeight}; + nn = this.prepareNode(nn, resizing); + if (node.x === nn.x && node.y === nn.y && node.width === nn.width && node.height === nn.height) { + return null; } - let resizing = node.width !== width; node._dirty = true; - node.x = x; - node.y = y; - node.width = width; - node.height = height; - - node._lastTriedX = x; - node._lastTriedY = y; - node._lastTriedWidth = width; - node._lastTriedHeight = height; - - node = this.prepareNode(node, resizing); + node.x = node._lastTriedX = nn.x; + node.y = node._lastTriedY = nn.y; + node.width = node._lastTriedWidth = nn.width; + node.height = node._lastTriedHeight = nn.height; this._fixCollisions(node); if (!noPack) { diff --git a/src/gridstack.ts b/src/gridstack.ts index 26ebbceea..891be845e 100644 --- a/src/gridstack.ts +++ b/src/gridstack.ts @@ -119,7 +119,7 @@ export class GridStack { let grids: GridStack[] = []; getGridElements(selector).forEach(el => { if (!el.gridstack) { - el.gridstack = new GridStack(el, options); + el.gridstack = new GridStack(el, Utils.clone(options)); } grids.push(el.gridstack); }); @@ -336,7 +336,7 @@ export class GridStack { /** - * Creates new widget and returns it. + * add a new widget and returns it. * * Widget will be always placed even if result height is more than actual grid height. * You need to use willItFit method before calling addWidget for additional check. @@ -344,44 +344,19 @@ export class GridStack { * * @example * let grid = GridStack.init(); - * grid.addWidget(el, {width: 3, autoPosition: true}); + * grid.addWidget('
hello
', {width: 3}); * - * @param el widget to add - * @param options widget position/size options (optional) + * @param el html element or string definition to add + * @param options widget position/size options (optional) - see GridStackWidget */ - public addWidget(el: GridStackElement, options? : GridstackWidget): HTMLElement; - - /** - * Creates new widget and returns it. - * Legacy: Spelled out version of the widgets options, recommend use new version instead. - * - * @example - * let grid = GridStack.init(); - * grid.addWidget(el, 0, 0, 3, 2, true); - * - * @param el widget to add - * @param x widget position x (optional) - * @param y widget position y (optional) - * @param width widget dimension width (optional) - * @param height widget dimension height (optional) - * @param autoPosition if true then x, y parameters will be ignored and widget will be places on the first available position (optional) - * @param minWidth minimum width allowed during resize/creation (optional) - * @param maxWidth maximum width allowed during resize/creation (optional) - * @param minHeight minimum height allowed during resize/creation (optional) - * @param maxHeight maximum height allowed during resize/creation (optional) - * @param id value for `data-gs-id` (optional) - */ - public addWidget(el: GridStackElement, x? : number | GridstackWidget, y?: number, width?: number, height?: number, autoPosition?: boolean, - minWidth?: number, maxWidth?: number, minHeight?: number, maxHeight?: number, id?: numberOrString): HTMLElement { - // new way of calling with an object - make sure all items have been properly initialized - if (x === undefined || typeof x === 'object') { - // Tempting to initialize the passed in opt with default and valid values, but this break knockout demos - // as the actual value are filled in when _prepareElement() calls el.attr('data-gs-xyz) before adding the node. - // opt = this.engine.prepareNode(opt); - x = (x || {}) as GridstackWidget; - } else { - // old legacy way of calling with items spelled out - call us back with single object instead (so we can properly initialized values) - return this.addWidget(el, { x, y, width, height, autoPosition, minWidth, maxWidth, minHeight, maxHeight, id }); + public addWidget(el: GridStackElement, options?: GridstackWidget): GridItemHTMLElement { + + // support legacy call for now ? + if (arguments.length > 2) { + console.warn('gridstack.ts: `addWidget(el, x, y, width...)` is deprecated. Use `addWidget(el, {x, y, width,...})`. It will be removed soon'); + let a = arguments, i = 1, + opt: GridstackWidget = { x:a[i++], y:a[i++], width:a[i++], height:a[i++], autoPosition:a[i++], minWidth:a[i++], maxWidth:a[i++], minHeight:a[i++], maxHeight:a[i++], id:a[i++] }; + return this.addWidget(el, opt); } if (typeof el === 'string') { @@ -390,9 +365,13 @@ export class GridStack { el = doc.body.children[0] as HTMLElement; } - this._writeAttr(el, x); - this.el.appendChild(el); + // Tempting to initialize the passed in opt with default and valid values, but this break knockout demos + // as the actual value are filled in when _prepareElement() calls el.getAttribute('data-gs-xyz) before adding the node. + if (options) { + this._writeAttr(el, options); + } + this.el.appendChild(el); return this.makeWidget(el); } @@ -595,6 +574,11 @@ export class GridStack { * enable/disable floating widgets (default: `false`) See [example](http://gridstackjs.com/demo/float.html) */ public float(val: boolean) { + if (val === undefined) { + // TODO: should we support and/or change signature ? figure this soon... + console.warn('gridstack.ts: getter `float()` is deprecated in 2.x and has been replaced by `getFloat()`. It will be **completely** removed soon'); + return this.getFloat(); + } this.engine.float = val; this._triggerChangeEvent(); } @@ -603,7 +587,7 @@ export class GridStack { * get the current float mode */ public getFloat(): boolean { - return this.engine.float || false; + return this.engine.float; } /** @@ -672,7 +656,7 @@ export class GridStack { * grid.el.appendChild('
'); * grid.makeWidget('gsi-1'); */ - public makeWidget(els: GridStackElement): HTMLElement { + public makeWidget(els: GridStackElement): GridItemHTMLElement { let el = getElement(els); this._prepareElement(el, true); this._updateContainerHeight(); @@ -1008,8 +992,8 @@ export class GridStack { * will be places on the first available position * * @example - * if (grid.willItFit(newNode.x, newNode.y, newNode.width, newNode.height, true)) { - * grid.addWidget(newNode.el, newNode.x, newNode.y, newNode.width, newNode.height, true); + * if (grid.willItFit(newNode.x, newNode.y, newNode.width, newNode.height, newNode.autoPosition)) { + * grid.addWidget(newNode.el, newNode); * } else { * alert('Not enough free space to place the widget'); * } @@ -1400,7 +1384,8 @@ export class GridStack { } /** call to write any default attributes back to element */ - private _writeAttr(el: HTMLElement, node: GridstackWidget = {}) { + private _writeAttr(el: HTMLElement, node: GridstackWidget) { + if (!node) return; this._writeAttrs(el, node.x, node.y, node.width, node.height); if (node.autoPosition) { diff --git a/src/types.ts b/src/types.ts index 578175d9f..79921ffd2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -203,7 +203,7 @@ export interface DDResizeOpt { /** Drag&Drop remove options */ export interface DDRemoveOpt { - /** class that be removed default?: '.' + opts.itemClass */ + /** class that can be removed (default?: '.' + opts.itemClass) */ accept?: string; }