Skip to content
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
39 changes: 11 additions & 28 deletions spec/gridstack-engine-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ describe('gridstack engine', function() {
expect(engine.float).toEqual(false);
expect(engine.maxRow).toEqual(undefined);
expect(engine.nodes).toEqual([]);
expect(engine.onChange).toEqual(undefined);
expect(engine.batchMode).toEqual(undefined);
expect((engine as any).onChange).toEqual(undefined);
});

it('should set params correctly.', function() {
Expand All @@ -37,8 +37,8 @@ describe('gridstack engine', function() {
expect(engine.float).toBe(true);
expect(engine.maxRow).toEqual(2);
expect(engine.nodes).toEqual(arr);
expect(engine.onChange).toEqual(fkt);
expect(engine.batchMode).toEqual(undefined);
expect((engine as any).onChange).toEqual(fkt);
});
});

Expand Down Expand Up @@ -80,42 +80,42 @@ describe('gridstack engine', function() {
it('should sort ascending with 12 columns.', function() {
w.column = 12;
w.nodes = [{x: 7, y: 0}, {x: 4, y: 4}, {x: 9, y: 0}, {x: 0, y: 1}];
e.prototype._sortNodes.call(w, 1);
engine.sortNodes.call(w, 1);
expect(w.nodes).toEqual([{x: 7, y: 0}, {x: 9, y: 0}, {x: 0, y: 1}, {x: 4, y: 4}]);
});

it('should sort descending with 12 columns.', function() {
w.column = 12;
w.nodes = [{x: 7, y: 0}, {x: 4, y: 4}, {x: 9, y: 0}, {x: 0, y: 1}];
e.prototype._sortNodes.call(w, -1);
engine.sortNodes.call(w, -1);
expect(w.nodes).toEqual([{x: 4, y: 4}, {x: 0, y: 1}, {x: 9, y: 0}, {x: 7, y: 0}]);
});

it('should sort ascending with 1 columns.', function() {
w.column = 1;
w.nodes = [{x: 7, y: 0}, {x: 4, y: 4}, {x: 9, y: 0}, {x: 0, y: 1}];
e.prototype._sortNodes.call(w, 1);
engine.sortNodes.call(w, 1);
expect(w.nodes).toEqual([{x: 0, y: 1}, {x: 7, y: 0}, {x: 4, y: 4}, {x: 9, y: 0}]);
});

it('should sort descending with 1 columns.', function() {
w.column = 1;
w.nodes = [{x: 7, y: 0}, {x: 4, y: 4}, {x: 9, y: 0}, {x: 0, y: 1}];
e.prototype._sortNodes.call(w, -1);
engine.sortNodes.call(w, -1);
expect(w.nodes).toEqual([{x: 9, y: 0}, {x: 4, y: 4}, {x: 7, y: 0}, {x: 0, y: 1}]);
});

it('should sort ascending without columns.', function() {
w.column = undefined;
w.nodes = [{x: 7, y: 0, w: 1}, {x: 4, y: 4, w: 1}, {x: 9, y: 0, w: 1}, {x: 0, y: 1, w: 1}];
e.prototype._sortNodes.call(w, 1);
engine.sortNodes.call(w, 1);
expect(w.nodes).toEqual([{x: 7, y: 0, w: 1}, {x: 9, y: 0, w: 1}, {x: 0, y: 1, w: 1}, {x: 4, y: 4, w: 1}]);
});

it('should sort descending without columns.', function() {
w.column = undefined;
w.nodes = [{x: 7, y: 0, w: 1}, {x: 4, y: 4, w: 1}, {x: 9, y: 0, w: 1}, {x: 0, y: 1, w: 1}];
e.prototype._sortNodes.call(w, -1);
engine.sortNodes.call(w, -1);
expect(w.nodes).toEqual([{x: 4, y: 4, w: 1}, {x: 0, y: 1, w: 1}, {x: 9, y: 0, w: 1}, {x: 7, y: 0, w: 1}]);
});

Expand Down Expand Up @@ -244,30 +244,13 @@ describe('gridstack engine', function() {

it('should by called with dirty nodes', function() {
(engine as any)._notify();
expect(spy.callback).toHaveBeenCalledWith([
engine.nodes[0],
engine.nodes[1]
], true);
expect(spy.callback).toHaveBeenCalledWith([engine.nodes[0], engine.nodes[1]]);
});

it('should by called with extra passed node to be removed', function() {
let n1 = {id: -1};
(engine as any)._notify(n1);
expect(spy.callback).toHaveBeenCalledWith([
n1,
engine.nodes[0],
engine.nodes[1]
], true);
});

it('should by called with extra passed node to be removed and should maintain false parameter', function() {
let n1 = {id: -1};
(engine as any)._notify(n1, false);
expect(spy.callback).toHaveBeenCalledWith([
n1,
engine.nodes[0],
engine.nodes[1]
], false);
(engine as any)._notify([n1]);
expect(spy.callback).toHaveBeenCalledWith([n1, engine.nodes[0], engine.nodes[1]]);
});
});

Expand Down
80 changes: 37 additions & 43 deletions src/gridstack-engine.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
/**
* gridstack-engine.ts 5.0.0-dev
* Copyright (c) 2021 Alain Dumesny - see GridStack root license
* Copyright (c) 2021-2022 Alain Dumesny - see GridStack root license
*/

import { Utils } from './utils';
import { GridStackNode, ColumnOptions, GridStackPosition, GridStackMoveOpts } from './types';

export type onChangeCB = (nodes: GridStackNode[], removeDOM?: boolean) => void;
/** options used for creations - similar to GridStackOptions */
/** callback to update the DOM attributes since this class is generic (no HTML or other info) for items that changed - see _notify() */
type OnChangeCB = (nodes: GridStackNode[]) => void;

/** options used during creation - similar to GridStackOptions */
export interface GridStackEngineOptions {
column?: number;
maxRow?: number;
float?: boolean;
nodes?: GridStackNode[];
onChange?: onChangeCB;
onChange?: OnChangeCB;
}

/**
Expand All @@ -26,38 +28,38 @@ export class GridStackEngine {
public column: number;
public maxRow: number;
public nodes: GridStackNode[];
public onChange: onChangeCB;
public addedNodes: GridStackNode[] = [];
public removedNodes: GridStackNode[] = [];
public batchMode: boolean;
/** @internal callback to update the DOM attributes */
protected onChange: OnChangeCB;
/** @internal */
private _float: boolean;
protected _float: boolean;
/** @internal */
private _prevFloat: boolean;
protected _prevFloat: boolean;
/** @internal cached layouts of difference column count so we can restore ack (eg 12 -> 1 -> 12) */
private _layouts?: Layout[][]; // maps column # to array of values nodes
protected _layouts?: GridStackNode[][]; // maps column # to array of values nodes
/** @internal true while we are resizing widgets during column resize to skip certain parts */
private _inColumnResize: boolean;
protected _inColumnResize: boolean;
/** @internal true if we have some items locked */
private _hasLocked: boolean;
protected _hasLocked: boolean;
/** @internal unique global internal _id counter NOT starting at 0 */
private static _idSeq = 1;
protected static _idSeq = 1;

public constructor(opts: GridStackEngineOptions = {}) {
this.column = opts.column || 12;
this.onChange = opts.onChange;
this._float = opts.float;
this.maxRow = opts.maxRow;
this._float = opts.float;
this.nodes = opts.nodes || [];
this.onChange = opts.onChange;
}

public batchUpdate(): GridStackEngine {
if (this.batchMode) return this;
this.batchMode = true;
this._prevFloat = this._float;
this._float = true; // let things go anywhere for now... commit() will restore and possibly reposition
this.saveInitial(); // since begin update (which is called multiple times) won't do this
return this;
return this.saveInitial(); // since begin update (which is called multiple times) won't do this
}

public commit(): GridStackEngine {
Expand All @@ -70,14 +72,14 @@ export class GridStackEngine {
}

// use entire row for hitting area (will use bottom reverse sorted first) if we not actively moving DOWN and didn't already skip
private _useEntireRowArea(node: GridStackNode, nn: GridStackPosition): boolean {
protected _useEntireRowArea(node: GridStackNode, nn: GridStackPosition): boolean {
return !this.float && !this._hasLocked && (!node._moving || node._skipDown || nn.y <= node.y);
}

/** @internal fix collision on given 'node', going to given new location 'nn', with optional 'collide' node already found.
* return true if we moved. */
private _fixCollisions(node: GridStackNode, nn = node, collide?: GridStackNode, opt: GridStackMoveOpts = {}): boolean {
this._sortNodes(-1); // from last to first, so recursive collision move items in the right order
protected _fixCollisions(node: GridStackNode, nn = node, collide?: GridStackNode, opt: GridStackMoveOpts = {}): boolean {
this.sortNodes(-1); // from last to first, so recursive collision move items in the right order

collide = collide || this.collide(node, nn); // REAL area collide for swap and skip if none...
if (!collide) return false;
Expand Down Expand Up @@ -240,7 +242,7 @@ export class GridStackEngine {
public compact(): GridStackEngine {
if (this.nodes.length === 0) return this;
this.batchUpdate()
._sortNodes();
.sortNodes();
let copyNodes = this.nodes;
this.nodes = []; // pretend we have no nodes to conflict layout to start with...
copyNodes.forEach(node => {
Expand All @@ -265,16 +267,16 @@ export class GridStackEngine {
/** float getter method */
public get float(): boolean { return this._float || false; }

/** @internal */
private _sortNodes(dir?: -1 | 1): GridStackEngine {
/** sort the nodes array from first to last, or reverse. Called during collision/placement to force an order */
public sortNodes(dir?: -1 | 1): GridStackEngine {
this.nodes = Utils.sort(this.nodes, dir, this.column);
return this;
}

/** @internal called to top gravity pack the items back OR revert back to original Y positions when floating */
private _packNodes(): GridStackEngine {
protected _packNodes(): GridStackEngine {
if (this.batchMode) { return this; }
this._sortNodes(); // first to last
this.sortNodes(); // first to last

if (this.float) {
// restore original Y pos
Expand Down Expand Up @@ -402,6 +404,7 @@ export class GridStackEngine {
return node;
}

/** returns a list of modified nodes from their original values */
public getDirtyNodes(verify?: boolean): GridStackNode[] {
// compare original x,y,w,h instead as _dirty can be a temporary state
if (verify) {
Expand All @@ -410,12 +413,11 @@ export class GridStackEngine {
return this.nodes.filter(n => n._dirty);
}

/** @internal call this to call onChange CB with dirty nodes */
private _notify(nodes?: GridStackNode | GridStackNode[], removeDOM = true): GridStackEngine {
if (this.batchMode) return this;
nodes = (nodes === undefined ? [] : (Array.isArray(nodes) ? nodes : [nodes]) );
let dirtyNodes = nodes.concat(this.getDirtyNodes());
this.onChange && this.onChange(dirtyNodes, removeDOM);
/** @internal call this to call onChange callback with dirty nodes so DOM can be updated */
protected _notify(removedNodes?: GridStackNode[]): GridStackEngine {
if (this.batchMode || !this.onChange) return this;
let dirtyNodes = (removedNodes || []).concat(this.getDirtyNodes());
this.onChange(dirtyNodes);
return this;
}

Expand Down Expand Up @@ -463,7 +465,7 @@ export class GridStackEngine {
delete node._removeDOM;

if (node.autoPosition) {
this._sortNodes();
this.sortNodes();

for (let i = 0;; ++i) {
let x = i % this.column;
Expand Down Expand Up @@ -501,7 +503,7 @@ export class GridStackEngine {
// don't use 'faster' .splice(findIndex(),1) in case node isn't in our list, or in multiple times.
this.nodes = this.nodes.filter(n => n !== node);
return this._packNodes()
._notify(node);
._notify([node]);
}

public removeAll(removeDOM = true): GridStackEngine {
Expand Down Expand Up @@ -676,7 +678,7 @@ export class GridStackEngine {
let len = this._layouts?.length;
let layout = len && this.column !== (len - 1) ? this._layouts[len - 1] : null;
let list: GridStackNode[] = [];
this._sortNodes();
this.sortNodes();
this.nodes.forEach(n => {
let wl = layout?.find(l => l._id === n._id);
let w: GridStackNode = {...n};
Expand Down Expand Up @@ -771,7 +773,7 @@ export class GridStackEngine {

// see if we have cached previous layout IFF we are going up in size (restore) otherwise always
// generate next size down from where we are (looks more natural as you gradually size down).
let cacheNodes: Layout[] = [];
let cacheNodes: GridStackNode[] = [];
if (column > prevColumn) {
cacheNodes = this._layouts[column] || [];
// ...if not, start with the largest layout (if not already there) as down-scaling is more accurate
Expand Down Expand Up @@ -842,7 +844,7 @@ export class GridStackEngine {
* @param clear if true, will force other caches to be removed (default false)
*/
public cacheLayout(nodes: GridStackNode[], column: number, clear = false): GridStackEngine {
let copy: Layout[] = [];
let copy: GridStackNode[] = [];
nodes.forEach((n, i) => {
n._id = n._id || GridStackEngine._idSeq++; // make sure we have an id in case this is new layout, else re-use id already set
copy[i] = {x: n.x, y: n.y, w: n.w, _id: n._id} // only thing we change is x,y,w and id to find it back
Expand All @@ -859,7 +861,7 @@ export class GridStackEngine {
*/
public cacheOneLayout(n: GridStackNode, column: number): GridStackEngine {
n._id = n._id || GridStackEngine._idSeq++;
let layout: Layout = {x: n.x, y: n.y, w: n.w, _id: n._id}
let layout: GridStackNode = {x: n.x, y: n.y, w: n.w, _id: n._id}
this._layouts = this._layouts || [];
this._layouts[column] = this._layouts[column] || [];
let index = this._layouts[column].findIndex(l => l._id === n._id);
Expand All @@ -876,11 +878,3 @@ export class GridStackEngine {
return this;
}
}

/** @internal class to store per column layout bare minimal info (subset of GridStackWidget) */
interface Layout {
x: number;
y: number;
w: number;
_id: number; // so we can find full node back
}
Loading