gridstack 12.2.1 → 12.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -3
  3. package/dist/angular/README.md +2 -0
  4. package/dist/angular/esm2020/lib/base-widget.mjs +63 -7
  5. package/dist/angular/esm2020/lib/gridstack-item.component.mjs +33 -4
  6. package/dist/angular/esm2020/lib/gridstack.component.mjs +97 -16
  7. package/dist/angular/esm2020/lib/gridstack.module.mjs +25 -3
  8. package/dist/angular/esm2020/lib/types.mjs +2 -2
  9. package/dist/angular/fesm2015/gridstack-angular.mjs +191 -25
  10. package/dist/angular/fesm2015/gridstack-angular.mjs.map +1 -1
  11. package/dist/angular/fesm2020/gridstack-angular.mjs +192 -26
  12. package/dist/angular/fesm2020/gridstack-angular.mjs.map +1 -1
  13. package/dist/angular/lib/base-widget.d.ts +42 -5
  14. package/dist/angular/lib/gridstack-item.component.d.ts +49 -7
  15. package/dist/angular/lib/gridstack.component.d.ts +137 -22
  16. package/dist/angular/lib/gridstack.module.d.ts +23 -0
  17. package/dist/angular/lib/types.d.ts +32 -5
  18. package/dist/angular/package.json +1 -1
  19. package/dist/angular/src/base-widget.ts +68 -9
  20. package/dist/angular/src/gridstack-item.component.ts +49 -7
  21. package/dist/angular/src/gridstack.component.ts +154 -23
  22. package/dist/angular/src/gridstack.module.ts +24 -2
  23. package/dist/angular/src/types.ts +54 -0
  24. package/dist/dd-base-impl.d.ts +52 -3
  25. package/dist/dd-base-impl.js +41 -3
  26. package/dist/dd-base-impl.js.map +1 -1
  27. package/dist/dd-draggable.d.ts +2 -2
  28. package/dist/dd-draggable.js +4 -2
  29. package/dist/dd-draggable.js.map +1 -1
  30. package/dist/dd-droppable.d.ts +2 -2
  31. package/dist/dd-droppable.js +2 -2
  32. package/dist/dd-droppable.js.map +1 -1
  33. package/dist/dd-element.d.ts +2 -2
  34. package/dist/dd-element.js +2 -2
  35. package/dist/dd-element.js.map +1 -1
  36. package/dist/dd-gridstack.d.ts +54 -6
  37. package/dist/dd-gridstack.js +32 -2
  38. package/dist/dd-gridstack.js.map +1 -1
  39. package/dist/dd-manager.d.ts +29 -8
  40. package/dist/dd-manager.js +7 -3
  41. package/dist/dd-manager.js.map +1 -1
  42. package/dist/dd-resizable-handle.d.ts +2 -2
  43. package/dist/dd-resizable-handle.js +2 -2
  44. package/dist/dd-resizable-handle.js.map +1 -1
  45. package/dist/dd-resizable.d.ts +2 -2
  46. package/dist/dd-resizable.js +2 -2
  47. package/dist/dd-resizable.js.map +1 -1
  48. package/dist/dd-touch.d.ts +2 -2
  49. package/dist/dd-touch.js +2 -2
  50. package/dist/dd-touch.js.map +1 -1
  51. package/dist/gridstack-all.js +1 -1
  52. package/dist/gridstack-all.js.LICENSE.txt +2 -2
  53. package/dist/gridstack-all.js.map +1 -1
  54. package/dist/gridstack-engine.d.ts +235 -26
  55. package/dist/gridstack-engine.js +276 -30
  56. package/dist/gridstack-engine.js.map +1 -1
  57. package/dist/gridstack.css +2 -2
  58. package/dist/gridstack.d.ts +424 -82
  59. package/dist/gridstack.js +416 -79
  60. package/dist/gridstack.js.map +1 -1
  61. package/dist/src/gridstack.scss +2 -2
  62. package/dist/types.d.ts +132 -31
  63. package/dist/types.js +6 -3
  64. package/dist/types.js.map +1 -1
  65. package/dist/utils.d.ts +208 -29
  66. package/dist/utils.js +275 -61
  67. package/dist/utils.js.map +1 -1
  68. package/doc/API.md +6192 -0
  69. package/package.json +14 -3
  70. package/doc/CHANGES.md +0 -955
  71. package/doc/README.md +0 -642
@@ -1,13 +1,20 @@
1
1
  /**
2
- * gridstack-engine.ts 12.2.1
3
- * Copyright (c) 2021-2024 Alain Dumesny - see GridStack root license
2
+ * gridstack-engine.ts 12.3.0
3
+ * Copyright (c) 2021-2025 Alain Dumesny - see GridStack root license
4
4
  */
5
5
  import { Utils } from './utils';
6
6
  /**
7
- * Defines the GridStack engine that does most no DOM grid manipulation.
8
- * See GridStack methods and vars for descriptions.
7
+ * Defines the GridStack engine that handles all grid layout calculations and node positioning.
8
+ * This is the core engine that performs grid manipulation without any DOM operations.
9
9
  *
10
- * NOTE: values should not be modified directly - call the main GridStack API instead
10
+ * The engine manages:
11
+ * - Node positioning and collision detection
12
+ * - Layout algorithms (compact, float, etc.)
13
+ * - Grid resizing and column changes
14
+ * - Widget movement and resizing logic
15
+ *
16
+ * NOTE: Values should not be modified directly - use the main GridStack API instead
17
+ * to ensure proper DOM updates and event triggers.
11
18
  */
12
19
  class GridStackEngine {
13
20
  constructor(opts = {}) {
@@ -22,6 +29,21 @@ class GridStackEngine {
22
29
  this.nodes = opts.nodes || [];
23
30
  this.onChange = opts.onChange;
24
31
  }
32
+ /**
33
+ * Enable/disable batch mode for multiple operations to optimize performance.
34
+ * When enabled, layout updates are deferred until batch mode is disabled.
35
+ *
36
+ * @param flag true to enable batch mode, false to disable and apply changes
37
+ * @param doPack if true (default), pack/compact nodes when disabling batch mode
38
+ * @returns the engine instance for chaining
39
+ *
40
+ * @example
41
+ * // Start batch mode for multiple operations
42
+ * engine.batchUpdate(true);
43
+ * engine.addNode(node1);
44
+ * engine.addNode(node2);
45
+ * engine.batchUpdate(false); // Apply all changes at once
46
+ */
25
47
  batchUpdate(flag = true, doPack = true) {
26
48
  if (!!this.batchMode === flag)
27
49
  return this;
@@ -101,12 +123,39 @@ class GridStackEngine {
101
123
  }
102
124
  return didMove;
103
125
  }
104
- /** return the nodes that intercept the given node. Optionally a different area can be used, as well as a second node to skip */
126
+ /**
127
+ * Return the first node that intercepts/collides with the given node or area.
128
+ * Used for collision detection during drag and drop operations.
129
+ *
130
+ * @param skip the node to skip in collision detection (usually the node being moved)
131
+ * @param area the area to check for collisions (defaults to skip node's area)
132
+ * @param skip2 optional second node to skip in collision detection
133
+ * @returns the first colliding node, or undefined if no collision
134
+ *
135
+ * @example
136
+ * const colliding = engine.collide(draggedNode, {x: 2, y: 1, w: 2, h: 1});
137
+ * if (colliding) {
138
+ * console.log('Would collide with:', colliding.id);
139
+ * }
140
+ */
105
141
  collide(skip, area = skip, skip2) {
106
142
  const skipId = skip._id;
107
143
  const skip2Id = skip2?._id;
108
144
  return this.nodes.find(n => n._id !== skipId && n._id !== skip2Id && Utils.isIntercepted(n, area));
109
145
  }
146
+ /**
147
+ * Return all nodes that intercept/collide with the given node or area.
148
+ * Similar to collide() but returns all colliding nodes instead of just the first.
149
+ *
150
+ * @param skip the node to skip in collision detection
151
+ * @param area the area to check for collisions (defaults to skip node's area)
152
+ * @param skip2 optional second node to skip in collision detection
153
+ * @returns array of all colliding nodes
154
+ *
155
+ * @example
156
+ * const allCollisions = engine.collideAll(draggedNode);
157
+ * console.log('Colliding with', allCollisions.length, 'nodes');
158
+ */
110
159
  collideAll(skip, area = skip, skip2) {
111
160
  const skipId = skip._id;
112
161
  const skip2Id = skip2?._id;
@@ -180,7 +229,20 @@ class GridStackEngine {
180
229
  return {collide, over: overMax};
181
230
  }
182
231
  */
183
- /** called to cache the nodes pixel rectangles used for collision detection during drag */
232
+ /**
233
+ * Cache the pixel rectangles for all nodes used for collision detection during drag operations.
234
+ * This optimization converts grid coordinates to pixel coordinates for faster collision detection.
235
+ *
236
+ * @param w width of a single grid cell in pixels
237
+ * @param h height of a single grid cell in pixels
238
+ * @param top top margin/padding in pixels
239
+ * @param right right margin/padding in pixels
240
+ * @param bottom bottom margin/padding in pixels
241
+ * @param left left margin/padding in pixels
242
+ * @returns the engine instance for chaining
243
+ *
244
+ * @internal This is typically called by GridStack during resize events
245
+ */
184
246
  cacheRects(w, h, top, right, bottom, left) {
185
247
  this.nodes.forEach(n => n._rect = {
186
248
  y: n.y * h + top,
@@ -190,7 +252,20 @@ class GridStackEngine {
190
252
  });
191
253
  return this;
192
254
  }
193
- /** called to possibly swap between 2 nodes (same size or column, not locked, touching), returning true if successful */
255
+ /**
256
+ * Attempt to swap the positions of two nodes if they meet swapping criteria.
257
+ * Nodes can swap if they are the same size or in the same column/row, not locked, and touching.
258
+ *
259
+ * @param a first node to swap
260
+ * @param b second node to swap
261
+ * @returns true if swap was successful, false if not possible, undefined if not applicable
262
+ *
263
+ * @example
264
+ * const swapped = engine.swap(nodeA, nodeB);
265
+ * if (swapped) {
266
+ * console.log('Nodes swapped successfully');
267
+ * }
268
+ */
194
269
  swap(a, b) {
195
270
  if (!b || b.locked || !a || a.locked)
196
271
  return false;
@@ -241,11 +316,41 @@ class GridStackEngine {
241
316
  }
242
317
  return false;
243
318
  }
319
+ /**
320
+ * Check if the specified rectangular area is empty (no nodes occupy any part of it).
321
+ *
322
+ * @param x the x coordinate (column) of the area to check
323
+ * @param y the y coordinate (row) of the area to check
324
+ * @param w the width in columns of the area to check
325
+ * @param h the height in rows of the area to check
326
+ * @returns true if the area is completely empty, false if any node overlaps
327
+ *
328
+ * @example
329
+ * if (engine.isAreaEmpty(2, 1, 3, 2)) {
330
+ * console.log('Area is available for placement');
331
+ * }
332
+ */
244
333
  isAreaEmpty(x, y, w, h) {
245
334
  const nn = { x: x || 0, y: y || 0, w: w || 1, h: h || 1 };
246
335
  return !this.collide(nn);
247
336
  }
248
- /** re-layout grid items to reclaim any empty space - optionally keeping the sort order exactly the same ('list' mode) vs truly finding an empty spaces */
337
+ /**
338
+ * Re-layout grid items to reclaim any empty space.
339
+ * This optimizes the grid layout by moving items to fill gaps.
340
+ *
341
+ * @param layout layout algorithm to use:
342
+ * - 'compact' (default): find truly empty spaces, may reorder items
343
+ * - 'list': keep the sort order exactly the same, move items up sequentially
344
+ * @param doSort if true (default), sort nodes by position before compacting
345
+ * @returns the engine instance for chaining
346
+ *
347
+ * @example
348
+ * // Compact to fill empty spaces
349
+ * engine.compact();
350
+ *
351
+ * // Compact preserving item order
352
+ * engine.compact('list');
353
+ */
249
354
  compact(layout = 'compact', doSort = true) {
250
355
  if (this.nodes.length === 0)
251
356
  return this;
@@ -274,7 +379,17 @@ class GridStackEngine {
274
379
  this.batchUpdate(false);
275
380
  return this;
276
381
  }
277
- /** enable/disable floating widgets (default: `false`) See [example](http://gridstackjs.com/demo/float.html) */
382
+ /**
383
+ * Enable/disable floating widgets (default: `false`).
384
+ * When floating is enabled, widgets can move up to fill empty spaces.
385
+ * See [example](http://gridstackjs.com/demo/float.html)
386
+ *
387
+ * @param val true to enable floating, false to disable
388
+ *
389
+ * @example
390
+ * engine.float = true; // Enable floating
391
+ * engine.float = false; // Disable floating (default)
392
+ */
278
393
  set float(val) {
279
394
  if (this._float === val)
280
395
  return;
@@ -283,9 +398,27 @@ class GridStackEngine {
283
398
  this._packNodes()._notify();
284
399
  }
285
400
  }
286
- /** float getter method */
401
+ /**
402
+ * Get the current floating mode setting.
403
+ *
404
+ * @returns true if floating is enabled, false otherwise
405
+ *
406
+ * @example
407
+ * const isFloating = engine.float;
408
+ * console.log('Floating enabled:', isFloating);
409
+ */
287
410
  get float() { return this._float || false; }
288
- /** sort the nodes array from first to last, or reverse. Called during collision/placement to force an order */
411
+ /**
412
+ * Sort the nodes array from first to last, or reverse.
413
+ * This is called during collision/placement operations to enforce a specific order.
414
+ *
415
+ * @param dir sort direction: 1 for ascending (first to last), -1 for descending (last to first)
416
+ * @returns the engine instance for chaining
417
+ *
418
+ * @example
419
+ * engine.sortNodes(); // Sort ascending (default)
420
+ * engine.sortNodes(-1); // Sort descending
421
+ */
289
422
  sortNodes(dir = 1) {
290
423
  this.nodes = Utils.sort(this.nodes, dir);
291
424
  return this;
@@ -333,9 +466,17 @@ class GridStackEngine {
333
466
  return this;
334
467
  }
335
468
  /**
336
- * given a random node, makes sure it's coordinates/values are valid in the current grid
337
- * @param node to adjust
338
- * @param resizing if out of bound, resize down or move into the grid to fit ?
469
+ * Prepare and validate a node's coordinates and values for the current grid.
470
+ * This ensures the node has valid position, size, and properties before being added to the grid.
471
+ *
472
+ * @param node the node to prepare and validate
473
+ * @param resizing if true, resize the node down if it's out of bounds; if false, move it to fit
474
+ * @returns the prepared node with valid coordinates
475
+ *
476
+ * @example
477
+ * const node = { w: 3, h: 2, content: 'Hello' };
478
+ * const prepared = engine.prepareNode(node);
479
+ * console.log('Node prepared at:', prepared.x, prepared.y);
339
480
  */
340
481
  prepareNode(node, resizing) {
341
482
  node._id = node._id ?? GridStackEngine._idSeq++;
@@ -394,7 +535,19 @@ class GridStackEngine {
394
535
  this.nodeBoundFix(node, resizing);
395
536
  return node;
396
537
  }
397
- /** part2 of preparing a node to fit inside our grid - checks for x,y,w from grid dimensions */
538
+ /**
539
+ * Part 2 of preparing a node to fit inside the grid - validates and fixes coordinates and dimensions.
540
+ * This ensures the node fits within grid boundaries and respects min/max constraints.
541
+ *
542
+ * @param node the node to validate and fix
543
+ * @param resizing if true, resize the node to fit; if false, move the node to fit
544
+ * @returns the engine instance for chaining
545
+ *
546
+ * @example
547
+ * // Fix a node that might be out of bounds
548
+ * engine.nodeBoundFix(node, true); // Resize to fit
549
+ * engine.nodeBoundFix(node, false); // Move to fit
550
+ */
398
551
  nodeBoundFix(node, resizing) {
399
552
  const before = node._orig || Utils.copyPos({}, node);
400
553
  if (node.maxW) {
@@ -413,7 +566,7 @@ class GridStackEngine {
413
566
  // remember it's position & width so we can restore back (1 -> 12 column) #1655 #1985
414
567
  // IFF we're not in the middle of column resizing!
415
568
  const saveOrig = (node.x || 0) + (node.w || 1) > this.column;
416
- if (saveOrig && this.column < this.defaultColumn && !this._inColumnResize && !this.skipCacheUpdate && node._id && this.findCacheLayout(node, this.defaultColumn) === -1) {
569
+ if (saveOrig && this.column < this.defaultColumn && !this._inColumnResize && !this.skipCacheUpdate && node._id != null && this.findCacheLayout(node, this.defaultColumn) === -1) {
417
570
  const copy = { ...node }; // need _id + positions
418
571
  if (copy.autoPosition || copy.x === undefined) {
419
572
  delete copy.x;
@@ -463,7 +616,20 @@ class GridStackEngine {
463
616
  }
464
617
  return this;
465
618
  }
466
- /** returns a list of modified nodes from their original values */
619
+ /**
620
+ * Returns a list of nodes that have been modified from their original values.
621
+ * This is used to track which nodes need DOM updates.
622
+ *
623
+ * @param verify if true, performs additional verification by comparing current vs original positions
624
+ * @returns array of nodes that have been modified
625
+ *
626
+ * @example
627
+ * const changed = engine.getDirtyNodes();
628
+ * console.log('Modified nodes:', changed.length);
629
+ *
630
+ * // Get verified dirty nodes
631
+ * const verified = engine.getDirtyNodes(true);
632
+ */
467
633
  getDirtyNodes(verify) {
468
634
  // compare original x,y,w,h instead as _dirty can be a temporary state
469
635
  if (verify) {
@@ -479,7 +645,14 @@ class GridStackEngine {
479
645
  this.onChange(dirtyNodes);
480
646
  return this;
481
647
  }
482
- /** @internal remove dirty and last tried info */
648
+ /**
649
+ * Clean all dirty and last tried information from nodes.
650
+ * This resets the dirty state tracking for all nodes.
651
+ *
652
+ * @returns the engine instance for chaining
653
+ *
654
+ * @internal
655
+ */
483
656
  cleanNodes() {
484
657
  if (this.batchMode)
485
658
  return this;
@@ -489,9 +662,16 @@ class GridStackEngine {
489
662
  });
490
663
  return this;
491
664
  }
492
- /** @internal called to save initial position/size to track real dirty state.
493
- * Note: should be called right after we call change event (so next API is can detect changes)
494
- * as well as right before we start move/resize/enter (so we can restore items to prev values) */
665
+ /**
666
+ * Save the initial position/size of all nodes to track real dirty state.
667
+ * This creates a snapshot of current positions that can be restored later.
668
+ *
669
+ * Note: Should be called right after change events and before move/resize operations.
670
+ *
671
+ * @returns the engine instance for chaining
672
+ *
673
+ * @internal
674
+ */
495
675
  saveInitial() {
496
676
  this.nodes.forEach(n => {
497
677
  n._orig = Utils.copyPos({}, n);
@@ -500,7 +680,14 @@ class GridStackEngine {
500
680
  this._hasLocked = this.nodes.some(n => n.locked);
501
681
  return this;
502
682
  }
503
- /** @internal restore all the nodes back to initial values (called when we leave) */
683
+ /**
684
+ * Restore all nodes back to their initial values.
685
+ * This is typically called when canceling an operation (e.g., Esc key during drag).
686
+ *
687
+ * @returns the engine instance for chaining
688
+ *
689
+ * @internal
690
+ */
504
691
  restoreInitial() {
505
692
  this.nodes.forEach(n => {
506
693
  if (!n._orig || Utils.samePos(n, n._orig))
@@ -511,9 +698,21 @@ class GridStackEngine {
511
698
  this._notify();
512
699
  return this;
513
700
  }
514
- /** find the first available empty spot for the given node width/height, updating the x,y attributes. return true if found.
515
- * optionally you can pass your own existing node list and column count, otherwise defaults to that engine data.
516
- * Optionally pass a widget to start search AFTER, meaning the order will remain the same but possibly have empty slots we skipped
701
+ /**
702
+ * Find the first available empty spot for the given node dimensions.
703
+ * Updates the node's x,y attributes with the found position.
704
+ *
705
+ * @param node the node to find a position for (w,h must be set)
706
+ * @param nodeList optional list of nodes to check against (defaults to engine nodes)
707
+ * @param column optional column count (defaults to engine column count)
708
+ * @param after optional node to start search after (maintains order)
709
+ * @returns true if an empty position was found and node was updated
710
+ *
711
+ * @example
712
+ * const node = { w: 2, h: 1 };
713
+ * if (engine.findEmptyPosition(node)) {
714
+ * console.log('Found position at:', node.x, node.y);
715
+ * }
517
716
  */
518
717
  findEmptyPosition(node, nodeList = this.nodes, column = this.column, after) {
519
718
  const start = after ? after.y * column + (after.x + after.w) : 0;
@@ -536,7 +735,19 @@ class GridStackEngine {
536
735
  }
537
736
  return found;
538
737
  }
539
- /** call to add the given node to our list, fixing collision and re-packing */
738
+ /**
739
+ * Add the given node to the grid, handling collision detection and re-packing.
740
+ * This is the main method for adding new widgets to the engine.
741
+ *
742
+ * @param node the node to add to the grid
743
+ * @param triggerAddEvent if true, adds node to addedNodes list for event triggering
744
+ * @param after optional node to place this node after (for ordering)
745
+ * @returns the added node (or existing node if duplicate)
746
+ *
747
+ * @example
748
+ * const node = { x: 0, y: 0, w: 2, h: 1, content: 'Hello' };
749
+ * const added = engine.addNode(node, true);
750
+ */
540
751
  addNode(node, triggerAddEvent = false, after) {
541
752
  const dup = this.nodes.find(n => n._id === node._id);
542
753
  if (dup)
@@ -561,6 +772,17 @@ class GridStackEngine {
561
772
  }
562
773
  return node;
563
774
  }
775
+ /**
776
+ * Remove the given node from the grid.
777
+ *
778
+ * @param node the node to remove
779
+ * @param removeDOM if true (default), marks node for DOM removal
780
+ * @param triggerEvent if true, adds node to removedNodes list for event triggering
781
+ * @returns the engine instance for chaining
782
+ *
783
+ * @example
784
+ * engine.removeNode(node, true, true);
785
+ */
564
786
  removeNode(node, removeDOM = true, triggerEvent = false) {
565
787
  if (!this.nodes.find(n => n._id === node._id)) {
566
788
  // TEST console.log(`Error: GridStackEngine.removeNode() node._id=${node._id} not found!`)
@@ -578,6 +800,16 @@ class GridStackEngine {
578
800
  this._notify([node]);
579
801
  return this;
580
802
  }
803
+ /**
804
+ * Remove all nodes from the grid.
805
+ *
806
+ * @param removeDOM if true (default), marks all nodes for DOM removal
807
+ * @param triggerEvent if true (default), triggers removal events
808
+ * @returns the engine instance for chaining
809
+ *
810
+ * @example
811
+ * engine.removeAll(); // Remove all nodes
812
+ */
581
813
  removeAll(removeDOM = true, triggerEvent = true) {
582
814
  delete this._layouts;
583
815
  if (!this.nodes.length)
@@ -588,9 +820,23 @@ class GridStackEngine {
588
820
  this.nodes = [];
589
821
  return this._notify(removedNodes);
590
822
  }
591
- /** checks if item can be moved (layout constrain) vs moveNode(), returning true if was able to move.
592
- * In more complicated cases (maxRow) it will attempt at moving the item and fixing
593
- * others in a clone first, then apply those changes if still within specs. */
823
+ /**
824
+ * Check if a node can be moved to a new position, considering layout constraints.
825
+ * This is a safer version of moveNode() that validates the move first.
826
+ *
827
+ * For complex cases (like maxRow constraints), it simulates the move in a clone first,
828
+ * then applies the changes only if they meet all specifications.
829
+ *
830
+ * @param node the node to move
831
+ * @param o move options including target position
832
+ * @returns true if the node was successfully moved
833
+ *
834
+ * @example
835
+ * const canMove = engine.moveNodeCheck(node, { x: 2, y: 1 });
836
+ * if (canMove) {
837
+ * console.log('Node moved successfully');
838
+ * }
839
+ */
594
840
  moveNodeCheck(node, o) {
595
841
  // if (node.locked) return false;
596
842
  if (!this.changedPosConstrain(node, o))