gridstack 7.3.0 → 8.0.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 (196) hide show
  1. package/README.md +8 -2
  2. package/dist/dd-base-impl.d.ts +20 -20
  3. package/dist/dd-base-impl.js +31 -35
  4. package/dist/dd-base-impl.js.map +1 -1
  5. package/dist/dd-draggable.d.ts +28 -28
  6. package/dist/dd-draggable.js +336 -344
  7. package/dist/dd-draggable.js.map +1 -1
  8. package/dist/dd-droppable.d.ts +26 -26
  9. package/dist/dd-droppable.js +146 -148
  10. package/dist/dd-droppable.js.map +1 -1
  11. package/dist/dd-element.d.ts +27 -27
  12. package/dist/dd-element.js +90 -94
  13. package/dist/dd-element.js.map +1 -1
  14. package/dist/dd-gridstack.d.ts +34 -34
  15. package/dist/dd-gridstack.js +127 -124
  16. package/dist/dd-gridstack.js.map +1 -1
  17. package/dist/dd-manager.d.ts +22 -22
  18. package/dist/dd-manager.js +9 -13
  19. package/dist/dd-manager.js.map +1 -1
  20. package/dist/dd-resizable-handle.d.ts +14 -14
  21. package/dist/dd-resizable-handle.js +102 -105
  22. package/dist/dd-resizable-handle.js.map +1 -1
  23. package/dist/dd-resizable.d.ts +28 -28
  24. package/dist/dd-resizable.js +290 -296
  25. package/dist/dd-resizable.js.map +1 -1
  26. package/dist/dd-touch.d.ts +33 -33
  27. package/dist/dd-touch.js +173 -182
  28. package/dist/dd-touch.js.map +1 -1
  29. package/dist/es5/dd-base-impl.d.ts +20 -20
  30. package/dist/es5/dd-base-impl.js +40 -40
  31. package/dist/es5/dd-base-impl.js.map +1 -1
  32. package/dist/es5/dd-draggable.d.ts +28 -28
  33. package/dist/es5/dd-draggable.js +366 -367
  34. package/dist/es5/dd-draggable.js.map +1 -1
  35. package/dist/es5/dd-droppable.d.ts +26 -26
  36. package/dist/es5/dd-droppable.js +181 -179
  37. package/dist/es5/dd-droppable.js.map +1 -1
  38. package/dist/es5/dd-element.d.ts +27 -27
  39. package/dist/es5/dd-element.js +95 -95
  40. package/dist/es5/dd-element.js.map +1 -1
  41. package/dist/es5/dd-gridstack.d.ts +34 -34
  42. package/dist/es5/dd-gridstack.js +144 -144
  43. package/dist/es5/dd-gridstack.js.map +1 -1
  44. package/dist/es5/dd-manager.d.ts +22 -22
  45. package/dist/es5/dd-manager.js +16 -16
  46. package/dist/es5/dd-manager.js.map +1 -1
  47. package/dist/es5/dd-resizable-handle.d.ts +14 -14
  48. package/dist/es5/dd-resizable-handle.js +105 -106
  49. package/dist/es5/dd-resizable-handle.js.map +1 -1
  50. package/dist/es5/dd-resizable.d.ts +28 -28
  51. package/dist/es5/dd-resizable.js +317 -318
  52. package/dist/es5/dd-resizable.js.map +1 -1
  53. package/dist/es5/dd-touch.d.ts +33 -33
  54. package/dist/es5/dd-touch.js +185 -185
  55. package/dist/es5/dd-touch.js.map +1 -1
  56. package/dist/es5/gridstack-all.js +1 -1
  57. package/dist/es5/gridstack-all.js.LICENSE.txt +1 -1
  58. package/dist/es5/gridstack-all.js.map +1 -1
  59. package/dist/es5/gridstack-engine.d.ts +102 -102
  60. package/dist/es5/gridstack-engine.js +1000 -995
  61. package/dist/es5/gridstack-engine.js.map +1 -1
  62. package/dist/es5/gridstack-poly.js +1 -1
  63. package/dist/es5/gridstack.d.ts +389 -376
  64. package/dist/es5/gridstack.js +2252 -2233
  65. package/dist/es5/gridstack.js.map +1 -1
  66. package/dist/es5/types.d.ts +279 -284
  67. package/dist/es5/types.js +47 -35
  68. package/dist/es5/types.js.map +1 -1
  69. package/dist/es5/utils.d.ts +95 -93
  70. package/dist/es5/utils.js +591 -569
  71. package/dist/es5/utils.js.map +1 -1
  72. package/dist/gridstack-all.js +1 -1
  73. package/dist/gridstack-all.js.LICENSE.txt +1 -1
  74. package/dist/gridstack-all.js.map +1 -1
  75. package/dist/gridstack-engine.d.ts +102 -102
  76. package/dist/gridstack-engine.js +950 -954
  77. package/dist/gridstack-engine.js.map +1 -1
  78. package/dist/gridstack-extra.css +0 -390
  79. package/dist/gridstack-extra.min.css +1 -1
  80. package/dist/gridstack.css +3 -97
  81. package/dist/gridstack.d.ts +389 -376
  82. package/dist/gridstack.js +2155 -2157
  83. package/dist/gridstack.js.map +1 -1
  84. package/dist/gridstack.min.css +1 -1
  85. package/dist/ng/README.md +154 -0
  86. package/dist/ng/gridstack-item.component.d.ts +29 -0
  87. package/dist/ng/gridstack-item.component.js +65 -0
  88. package/dist/ng/gridstack-item.component.js.map +1 -0
  89. package/dist/ng/gridstack.component.d.ts +118 -0
  90. package/dist/ng/gridstack.component.js +245 -0
  91. package/dist/ng/gridstack.component.js.map +1 -0
  92. package/dist/src/gridstack-extra.scss +0 -2
  93. package/dist/src/gridstack.scss +6 -9
  94. package/dist/types.d.ts +279 -284
  95. package/dist/types.js +44 -35
  96. package/dist/types.js.map +1 -1
  97. package/dist/utils.d.ts +95 -93
  98. package/dist/utils.js +539 -527
  99. package/dist/utils.js.map +1 -1
  100. package/{dist → dist_save}/angular/gridstack-item.component.ts +5 -3
  101. package/{dist → dist_save}/angular/gridstack.component.ts +32 -18
  102. package/dist_save/dd-base-impl.d.ts +20 -0
  103. package/dist_save/dd-base-impl.js +36 -0
  104. package/dist_save/dd-base-impl.js.map +1 -0
  105. package/dist_save/dd-draggable.d.ts +28 -0
  106. package/dist_save/dd-draggable.js +343 -0
  107. package/dist_save/dd-draggable.js.map +1 -0
  108. package/dist_save/dd-droppable.d.ts +26 -0
  109. package/dist_save/dd-droppable.js +149 -0
  110. package/dist_save/dd-droppable.js.map +1 -0
  111. package/dist_save/dd-element.d.ts +27 -0
  112. package/dist_save/dd-element.js +95 -0
  113. package/dist_save/dd-element.js.map +1 -0
  114. package/dist_save/dd-gridstack.d.ts +34 -0
  115. package/dist_save/dd-gridstack.js +125 -0
  116. package/dist_save/dd-gridstack.js.map +1 -0
  117. package/dist_save/dd-manager.d.ts +22 -0
  118. package/dist_save/dd-manager.js +14 -0
  119. package/dist_save/dd-manager.js.map +1 -0
  120. package/dist_save/dd-resizable-handle.d.ts +14 -0
  121. package/dist_save/dd-resizable-handle.js +106 -0
  122. package/dist_save/dd-resizable-handle.js.map +1 -0
  123. package/dist_save/dd-resizable.d.ts +28 -0
  124. package/dist_save/dd-resizable.js +294 -0
  125. package/dist_save/dd-resizable.js.map +1 -0
  126. package/dist_save/dd-touch.d.ts +33 -0
  127. package/dist_save/dd-touch.js +183 -0
  128. package/dist_save/dd-touch.js.map +1 -0
  129. package/dist_save/es5/dd-base-impl.d.ts +20 -0
  130. package/dist_save/es5/dd-base-impl.js +41 -0
  131. package/dist_save/es5/dd-base-impl.js.map +1 -0
  132. package/dist_save/es5/dd-draggable.d.ts +28 -0
  133. package/dist_save/es5/dd-draggable.js +366 -0
  134. package/dist_save/es5/dd-draggable.js.map +1 -0
  135. package/dist_save/es5/dd-droppable.d.ts +26 -0
  136. package/dist_save/es5/dd-droppable.js +180 -0
  137. package/dist_save/es5/dd-droppable.js.map +1 -0
  138. package/dist_save/es5/dd-element.d.ts +27 -0
  139. package/dist_save/es5/dd-element.js +96 -0
  140. package/dist_save/es5/dd-element.js.map +1 -0
  141. package/dist_save/es5/dd-gridstack.d.ts +34 -0
  142. package/dist_save/es5/dd-gridstack.js +145 -0
  143. package/dist_save/es5/dd-gridstack.js.map +1 -0
  144. package/dist_save/es5/dd-manager.d.ts +22 -0
  145. package/dist_save/es5/dd-manager.js +17 -0
  146. package/dist_save/es5/dd-manager.js.map +1 -0
  147. package/dist_save/es5/dd-resizable-handle.d.ts +14 -0
  148. package/dist_save/es5/dd-resizable-handle.js +107 -0
  149. package/dist_save/es5/dd-resizable-handle.js.map +1 -0
  150. package/dist_save/es5/dd-resizable.d.ts +28 -0
  151. package/dist_save/es5/dd-resizable.js +316 -0
  152. package/dist_save/es5/dd-resizable.js.map +1 -0
  153. package/dist_save/es5/dd-touch.d.ts +33 -0
  154. package/dist_save/es5/dd-touch.js +186 -0
  155. package/dist_save/es5/dd-touch.js.map +1 -0
  156. package/dist_save/es5/gridstack-all.js +3 -0
  157. package/dist_save/es5/gridstack-all.js.LICENSE.txt +7 -0
  158. package/dist_save/es5/gridstack-all.js.map +1 -0
  159. package/dist_save/es5/gridstack-engine.d.ts +102 -0
  160. package/dist_save/es5/gridstack-engine.js +997 -0
  161. package/dist_save/es5/gridstack-engine.js.map +1 -0
  162. package/dist_save/es5/gridstack-poly.js +356 -0
  163. package/dist_save/es5/gridstack.d.ts +376 -0
  164. package/dist_save/es5/gridstack.js +2238 -0
  165. package/dist_save/es5/gridstack.js.map +1 -0
  166. package/dist_save/es5/types.d.ts +284 -0
  167. package/dist_save/es5/types.js +36 -0
  168. package/dist_save/es5/types.js.map +1 -0
  169. package/dist_save/es5/utils.d.ts +95 -0
  170. package/dist_save/es5/utils.js +590 -0
  171. package/dist_save/es5/utils.js.map +1 -0
  172. package/dist_save/gridstack-all.js +3 -0
  173. package/dist_save/gridstack-all.js.LICENSE.txt +7 -0
  174. package/dist_save/gridstack-all.js.map +1 -0
  175. package/dist_save/gridstack-engine.d.ts +102 -0
  176. package/dist_save/gridstack-engine.js +956 -0
  177. package/dist_save/gridstack-engine.js.map +1 -0
  178. package/dist_save/gridstack-extra.css +433 -0
  179. package/dist_save/gridstack-extra.min.css +1 -0
  180. package/dist_save/gridstack.css +226 -0
  181. package/dist_save/gridstack.d.ts +376 -0
  182. package/dist_save/gridstack.js +2162 -0
  183. package/dist_save/gridstack.js.map +1 -0
  184. package/dist_save/gridstack.min.css +1 -0
  185. package/dist_save/src/gridstack-extra.scss +27 -0
  186. package/dist_save/src/gridstack.scss +131 -0
  187. package/dist_save/types.d.ts +284 -0
  188. package/dist_save/types.js +36 -0
  189. package/dist_save/types.js.map +1 -0
  190. package/dist_save/utils.d.ts +95 -0
  191. package/dist_save/utils.js +548 -0
  192. package/dist_save/utils.js.map +1 -0
  193. package/doc/CHANGES.md +12 -0
  194. package/doc/README.md +15 -1
  195. package/package.json +25 -24
  196. /package/{dist → dist_save}/angular/README.md +0 -0
package/dist/gridstack.js CHANGED
@@ -1,2158 +1,2156 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
- for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.GridStack = void 0;
14
- /*!
15
- * GridStack 7.3.0
16
- * https://gridstackjs.com/
17
- *
18
- * Copyright (c) 2021-2022 Alain Dumesny
19
- * see root license https://github.com/gridstack/gridstack.js/tree/master/LICENSE
20
- */
21
- const gridstack_engine_1 = require("./gridstack-engine");
22
- const utils_1 = require("./utils");
23
- const types_1 = require("./types");
24
- /*
25
- * and include D&D by default
26
- * TODO: while we could generate a gridstack-static.js at smaller size - saves about 31k (41k -> 72k)
27
- * I don't know how to generate the DD only code at the remaining 31k to delay load as code depends on Gridstack.ts
28
- * also it caused loading issues in prod - see https://github.com/gridstack/gridstack.js/issues/2039
29
- */
30
- const dd_gridstack_1 = require("./dd-gridstack");
31
- const dd_touch_1 = require("./dd-touch");
32
- const dd_manager_1 = require("./dd-manager");
33
- /** global instance */
34
- const dd = new dd_gridstack_1.DDGridStack;
35
- // export all dependent file as well to make it easier for users to just import the main file
36
- __exportStar(require("./types"), exports);
37
- __exportStar(require("./utils"), exports);
38
- __exportStar(require("./gridstack-engine"), exports);
39
- __exportStar(require("./dd-gridstack"), exports);
40
- /**
41
- * Main gridstack class - you will need to call `GridStack.init()` first to initialize your grid.
42
- * Note: your grid elements MUST have the following classes for the CSS layout to work:
43
- * @example
44
- * <div class="grid-stack">
45
- * <div class="grid-stack-item">
46
- * <div class="grid-stack-item-content">Item 1</div>
47
- * </div>
48
- * </div>
49
- */
50
- class GridStack {
51
- /**
52
- * Construct a grid item from the given element and options
53
- * @param el
54
- * @param opts
55
- */
56
- constructor(el, opts = {}) {
57
- var _a, _b;
58
- /** @internal */
59
- this._gsEventHandler = {};
60
- /** @internal extra row added when dragging at the bottom of the grid */
61
- this._extraDragRow = 0;
62
- this.el = el; // exposed HTML element to the user
63
- opts = opts || {}; // handles null/undefined/0
64
- if (!el.classList.contains('grid-stack')) {
65
- this.el.classList.add('grid-stack');
66
- }
67
- // if row property exists, replace minRow and maxRow instead
68
- if (opts.row) {
69
- opts.minRow = opts.maxRow = opts.row;
70
- delete opts.row;
71
- }
72
- let rowAttr = utils_1.Utils.toNumber(el.getAttribute('gs-row'));
73
- // flag only valid in sub-grids (handled by parent, not here)
74
- if (opts.column === 'auto') {
75
- delete opts.column;
76
- }
77
- // 'minWidth' legacy support in 5.1
78
- /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
79
- let anyOpts = opts;
80
- if (anyOpts.minWidth !== undefined) {
81
- opts.oneColumnSize = opts.oneColumnSize || anyOpts.minWidth;
82
- delete anyOpts.minWidth;
83
- }
84
- // save original setting so we can restore on save
85
- if (opts.alwaysShowResizeHandle !== undefined) {
86
- opts._alwaysShowResizeHandle = opts.alwaysShowResizeHandle;
87
- }
88
- // elements DOM attributes override any passed options (like CSS style) - merge the two together
89
- let defaults = Object.assign(Object.assign({}, utils_1.Utils.cloneDeep(types_1.gridDefaults)), { column: utils_1.Utils.toNumber(el.getAttribute('gs-column')) || types_1.gridDefaults.column, minRow: rowAttr ? rowAttr : utils_1.Utils.toNumber(el.getAttribute('gs-min-row')) || types_1.gridDefaults.minRow, maxRow: rowAttr ? rowAttr : utils_1.Utils.toNumber(el.getAttribute('gs-max-row')) || types_1.gridDefaults.maxRow, staticGrid: utils_1.Utils.toBool(el.getAttribute('gs-static')) || types_1.gridDefaults.staticGrid, draggable: {
90
- handle: (opts.handleClass ? '.' + opts.handleClass : (opts.handle ? opts.handle : '')) || types_1.gridDefaults.draggable.handle,
91
- }, removableOptions: {
92
- accept: opts.itemClass ? '.' + opts.itemClass : types_1.gridDefaults.removableOptions.accept,
93
- } });
94
- if (el.getAttribute('gs-animate')) { // default to true, but if set to false use that instead
95
- defaults.animate = utils_1.Utils.toBool(el.getAttribute('gs-animate'));
96
- }
97
- this.opts = utils_1.Utils.defaults(opts, defaults);
98
- opts = null; // make sure we use this.opts instead
99
- this._initMargin(); // part of settings defaults...
100
- // 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)
101
- if (this.opts.column !== 1 && !this.opts.disableOneColumnMode && this._widthOrContainer() <= this.opts.oneColumnSize) {
102
- this._prevColumn = this.getColumn();
103
- this.opts.column = 1;
104
- }
105
- if (this.opts.rtl === 'auto') {
106
- this.opts.rtl = (el.style.direction === 'rtl');
107
- }
108
- if (this.opts.rtl) {
109
- this.el.classList.add('grid-stack-rtl');
110
- }
111
- // check if we're been nested, and if so update our style and keep pointer around (used during save)
112
- let parentGridItem = (_a = utils_1.Utils.closestUpByClass(this.el, types_1.gridDefaults.itemClass)) === null || _a === void 0 ? void 0 : _a.gridstackNode;
113
- if (parentGridItem) {
114
- parentGridItem.subGrid = this;
115
- this.parentGridItem = parentGridItem;
116
- this.el.classList.add('grid-stack-nested');
117
- parentGridItem.el.classList.add('grid-stack-sub-grid');
118
- }
119
- this._isAutoCellHeight = (this.opts.cellHeight === 'auto');
120
- if (this._isAutoCellHeight || this.opts.cellHeight === 'initial') {
121
- // make the cell content square initially (will use resize/column event to keep it square)
122
- this.cellHeight(undefined, false);
123
- }
124
- else {
125
- // append unit if any are set
126
- if (typeof this.opts.cellHeight == 'number' && this.opts.cellHeightUnit && this.opts.cellHeightUnit !== types_1.gridDefaults.cellHeightUnit) {
127
- this.opts.cellHeight = this.opts.cellHeight + this.opts.cellHeightUnit;
128
- delete this.opts.cellHeightUnit;
129
- }
130
- this.cellHeight(this.opts.cellHeight, false);
131
- }
132
- // see if we need to adjust auto-hide
133
- if (this.opts.alwaysShowResizeHandle === 'mobile') {
134
- this.opts.alwaysShowResizeHandle = dd_touch_1.isTouch;
135
- }
136
- this._styleSheetClass = 'grid-stack-instance-' + gridstack_engine_1.GridStackEngine._idSeq++;
137
- this.el.classList.add(this._styleSheetClass);
138
- this._setStaticClass();
139
- let engineClass = this.opts.engineClass || GridStack.engineClass || gridstack_engine_1.GridStackEngine;
140
- this.engine = new engineClass({
141
- column: this.getColumn(),
142
- float: this.opts.float,
143
- maxRow: this.opts.maxRow,
144
- onChange: (cbNodes) => {
145
- let maxH = 0;
146
- this.engine.nodes.forEach(n => { maxH = Math.max(maxH, n.y + n.h); });
147
- cbNodes.forEach(n => {
148
- let el = n.el;
149
- if (!el)
150
- return;
151
- if (n._removeDOM) {
152
- if (el)
153
- el.remove();
154
- delete n._removeDOM;
155
- }
156
- else {
157
- this._writePosAttr(el, n);
158
- }
159
- });
160
- this._updateStyles(false, maxH); // false = don't recreate, just append if need be
161
- }
162
- });
163
- if (this.opts.auto) {
164
- this.batchUpdate(); // prevent in between re-layout #1535 TODO: this only set float=true, need to prevent collision check...
165
- this.getGridItems().forEach(el => this._prepareElement(el));
166
- this.batchUpdate(false);
167
- }
168
- // load any passed in children as well, which overrides any DOM layout done above
169
- if (this.opts.children) {
170
- let children = this.opts.children;
171
- delete this.opts.children;
172
- if (children.length)
173
- this.load(children); // don't load empty
174
- }
175
- this.setAnimation(this.opts.animate);
176
- this._updateStyles();
177
- if (this.opts.column != 12) {
178
- this.el.classList.add('grid-stack-' + this.opts.column);
179
- }
180
- // legacy support to appear 'per grid` options when really global.
181
- if (this.opts.dragIn)
182
- GridStack.setupDragIn(this.opts.dragIn, this.opts.dragInOptions);
183
- delete this.opts.dragIn;
184
- delete this.opts.dragInOptions;
185
- // dynamic grids require pausing during drag to detect over to nest vs push
186
- if (this.opts.subGridDynamic && !dd_manager_1.DDManager.pauseDrag)
187
- dd_manager_1.DDManager.pauseDrag = true;
188
- if (((_b = this.opts.draggable) === null || _b === void 0 ? void 0 : _b.pause) !== undefined)
189
- dd_manager_1.DDManager.pauseDrag = this.opts.draggable.pause;
190
- this._setupRemoveDrop();
191
- this._setupAcceptWidget();
192
- this._updateWindowResizeEvent();
193
- }
194
- /**
195
- * initializing the HTML element, or selector string, into a grid will return the grid. Calling it again will
196
- * simply return the existing instance (ignore any passed options). There is also an initAll() version that support
197
- * multiple grids initialization at once. Or you can use addGrid() to create the entire grid from JSON.
198
- * @param options grid options (optional)
199
- * @param elOrString element or CSS selector (first one used) to convert to a grid (default to '.grid-stack' class selector)
200
- *
201
- * @example
202
- * let grid = GridStack.init();
203
- *
204
- * Note: the HTMLElement (of type GridHTMLElement) will store a `gridstack: GridStack` value that can be retrieve later
205
- * let grid = document.querySelector('.grid-stack').gridstack;
206
- */
207
- static init(options = {}, elOrString = '.grid-stack') {
208
- let el = GridStack.getGridElement(elOrString);
209
- if (!el) {
210
- if (typeof elOrString === 'string') {
211
- console.error('GridStack.initAll() no grid was found with selector "' + elOrString + '" - element missing or wrong selector ?' +
212
- '\nNote: ".grid-stack" is required for proper CSS styling and drag/drop, and is the default selector.');
213
- }
214
- else {
215
- console.error('GridStack.init() no grid element was passed.');
216
- }
217
- return null;
218
- }
219
- if (!el.gridstack) {
220
- el.gridstack = new GridStack(el, utils_1.Utils.cloneDeep(options));
221
- }
222
- return el.gridstack;
223
- }
224
- /**
225
- * Will initialize a list of elements (given a selector) and return an array of grids.
226
- * @param options grid options (optional)
227
- * @param selector elements selector to convert to grids (default to '.grid-stack' class selector)
228
- *
229
- * @example
230
- * let grids = GridStack.initAll();
231
- * grids.forEach(...)
232
- */
233
- static initAll(options = {}, selector = '.grid-stack') {
234
- let grids = [];
235
- GridStack.getGridElements(selector).forEach(el => {
236
- if (!el.gridstack) {
237
- el.gridstack = new GridStack(el, utils_1.Utils.cloneDeep(options));
238
- delete options.dragIn;
239
- delete options.dragInOptions; // only need to be done once (really a static global thing, not per grid)
240
- }
241
- grids.push(el.gridstack);
242
- });
243
- if (grids.length === 0) {
244
- console.error('GridStack.initAll() no grid was found with selector "' + selector + '" - element missing or wrong selector ?' +
245
- '\nNote: ".grid-stack" is required for proper CSS styling and drag/drop, and is the default selector.');
246
- }
247
- return grids;
248
- }
249
- /**
250
- * call to create a grid with the given options, including loading any children from JSON structure. This will call GridStack.init(), then
251
- * grid.load() on any passed children (recursively). Great alternative to calling init() if you want entire grid to come from
252
- * JSON serialized data, including options.
253
- * @param parent HTML element parent to the grid
254
- * @param opt grids options used to initialize the grid, and list of children
255
- */
256
- static addGrid(parent, opt = {}) {
257
- if (!parent)
258
- return null;
259
- // create the grid element, but check if the passed 'parent' already has grid styling and should be used instead
260
- let el = parent;
261
- const parentIsGrid = parent.classList.contains('grid-stack');
262
- if (!parentIsGrid || opt.addRemoveCB) {
263
- if (opt.addRemoveCB) {
264
- el = opt.addRemoveCB(parent, opt, true, true);
265
- }
266
- else {
267
- let doc = document.implementation.createHTMLDocument(''); // IE needs a param
268
- doc.body.innerHTML = `<div class="grid-stack ${opt.class || ''}"></div>`;
269
- el = doc.body.children[0];
270
- parent.appendChild(el);
271
- }
272
- }
273
- // create grid class and load any children
274
- let grid = GridStack.init(opt, el);
275
- return grid;
276
- }
277
- /** call this method to register your engine instead of the default one.
278
- * See instead `GridStackOptions.engineClass` if you only need to
279
- * replace just one instance.
280
- */
281
- static registerEngine(engineClass) {
282
- GridStack.engineClass = engineClass;
283
- }
284
- /** @internal create placeholder DIV as needed */
285
- get placeholder() {
286
- if (!this._placeholder) {
287
- let placeholderChild = document.createElement('div'); // child so padding match item-content
288
- placeholderChild.className = 'placeholder-content';
289
- if (this.opts.placeholderText) {
290
- placeholderChild.innerHTML = this.opts.placeholderText;
291
- }
292
- this._placeholder = document.createElement('div');
293
- this._placeholder.classList.add(this.opts.placeholderClass, types_1.gridDefaults.itemClass, this.opts.itemClass);
294
- this.placeholder.appendChild(placeholderChild);
295
- }
296
- return this._placeholder;
297
- }
298
- /**
299
- * add a new widget and returns it.
300
- *
301
- * Widget will be always placed even if result height is more than actual grid height.
302
- * You need to use `willItFit()` before calling addWidget for additional check.
303
- * See also `makeWidget()`.
304
- *
305
- * @example
306
- * let grid = GridStack.init();
307
- * grid.addWidget({w: 3, content: 'hello'});
308
- * grid.addWidget('<div class="grid-stack-item"><div class="grid-stack-item-content">hello</div></div>', {w: 3});
309
- *
310
- * @param el GridStackWidget (which can have content string as well), html element, or string definition to add
311
- * @param options widget position/size options (optional, and ignore if first param is already option) - see GridStackWidget
312
- */
313
- addWidget(els, options) {
314
- function isGridStackWidget(w) {
315
- return w.el !== undefined || w.x !== undefined || w.y !== undefined || w.w !== undefined || w.h !== undefined || w.content !== undefined ? true : false;
316
- }
317
- let el;
318
- let node;
319
- if (typeof els === 'string') {
320
- let doc = document.implementation.createHTMLDocument(''); // IE needs a param
321
- doc.body.innerHTML = els;
322
- el = doc.body.children[0];
323
- }
324
- else if (arguments.length === 0 || arguments.length === 1 && isGridStackWidget(els)) {
325
- node = options = els;
326
- if (node === null || node === void 0 ? void 0 : node.el) {
327
- el = node.el; // re-use element stored in the node
328
- }
329
- else if (this.opts.addRemoveCB) {
330
- el = this.opts.addRemoveCB(this.el, options, true, false);
331
- }
332
- else {
333
- let content = (options === null || options === void 0 ? void 0 : options.content) || '';
334
- let doc = document.implementation.createHTMLDocument(''); // IE needs a param
335
- doc.body.innerHTML = `<div class="grid-stack-item ${this.opts.itemClass || ''}"><div class="grid-stack-item-content">${content}</div></div>`;
336
- el = doc.body.children[0];
337
- }
338
- }
339
- else {
340
- el = els;
341
- }
342
- if (!el)
343
- return;
344
- // Tempting to initialize the passed in opt with default and valid values, but this break knockout demos
345
- // as the actual value are filled in when _prepareElement() calls el.getAttribute('gs-xyz') before adding the node.
346
- // So make sure we load any DOM attributes that are not specified in passed in options (which override)
347
- let domAttr = this._readAttr(el);
348
- options = utils_1.Utils.cloneDeep(options) || {}; // make a copy before we modify in case caller re-uses it
349
- utils_1.Utils.defaults(options, domAttr);
350
- node = this.engine.prepareNode(options);
351
- this._writeAttr(el, options);
352
- if (this._insertNotAppend) {
353
- this.el.prepend(el);
354
- }
355
- else {
356
- this.el.appendChild(el);
357
- }
358
- // similar to makeWidget() that doesn't read attr again and worse re-create a new node and loose any _id
359
- this._prepareElement(el, true, options);
360
- this._updateContainerHeight();
361
- // see if there is a sub-grid to create
362
- if (node.subGrid) {
363
- this.makeSubGrid(node.el, undefined, undefined, false); //node.subGrid will be used as option in method, no need to pass
364
- }
365
- // if we're adding an item into 1 column (_prevColumn is set only when going to 1) make sure
366
- // we don't override the larger 12 column layout that was already saved. #1985
367
- if (this._prevColumn && this.opts.column === 1) {
368
- this._ignoreLayoutsNodeChange = true;
369
- }
370
- this._triggerAddEvent();
371
- this._triggerChangeEvent();
372
- delete this._ignoreLayoutsNodeChange;
373
- return el;
374
- }
375
- /**
376
- * Convert an existing gridItem element into a sub-grid with the given (optional) options, else inherit them
377
- * from the parent's subGrid options.
378
- * @param el gridItem element to convert
379
- * @param ops (optional) sub-grid options, else default to node, then parent settings, else defaults
380
- * @param nodeToAdd (optional) node to add to the newly created sub grid (used when dragging over existing regular item)
381
- * @returns newly created grid
382
- */
383
- makeSubGrid(el, ops, nodeToAdd, saveContent = true) {
384
- var _a, _b, _c;
385
- let node = el.gridstackNode;
386
- if (!node) {
387
- node = this.makeWidget(el).gridstackNode;
388
- }
389
- if ((_a = node.subGrid) === null || _a === void 0 ? void 0 : _a.el)
390
- return node.subGrid; // already done
391
- // find the template subGrid stored on a parent as fallback...
392
- let subGridTemplate; // eslint-disable-next-line @typescript-eslint/no-this-alias
393
- let grid = this;
394
- while (grid && !subGridTemplate) {
395
- subGridTemplate = (_b = grid.opts) === null || _b === void 0 ? void 0 : _b.subGrid;
396
- grid = (_c = grid.parentGridItem) === null || _c === void 0 ? void 0 : _c.grid;
397
- }
398
- //... and set the create options
399
- ops = utils_1.Utils.cloneDeep(Object.assign(Object.assign(Object.assign({}, (subGridTemplate || {})), { children: undefined }), (ops || node.subGrid)));
400
- node.subGrid = ops;
401
- // if column special case it set, remember that flag and set default
402
- let autoColumn;
403
- if (ops.column === 'auto') {
404
- autoColumn = true;
405
- ops.column = Math.max(node.w || 1, (nodeToAdd === null || nodeToAdd === void 0 ? void 0 : nodeToAdd.w) || 1);
406
- ops.disableOneColumnMode = true; // driven by parent
407
- }
408
- // if we're converting an existing full item, move over the content to be the first sub item in the new grid
409
- let content = node.el.querySelector('.grid-stack-item-content');
410
- let newItem;
411
- let newItemOpt;
412
- if (saveContent) {
413
- this._removeDD(node.el); // remove D&D since it's set on content div
414
- newItemOpt = Object.assign(Object.assign({}, node), { x: 0, y: 0 });
415
- utils_1.Utils.removeInternalForSave(newItemOpt);
416
- delete newItemOpt.subGrid;
417
- if (node.content) {
418
- newItemOpt.content = node.content;
419
- delete node.content;
420
- }
421
- if (this.opts.addRemoveCB) {
422
- newItem = this.opts.addRemoveCB(this.el, newItemOpt, true, false);
423
- }
424
- else {
425
- let doc = document.implementation.createHTMLDocument(''); // IE needs a param
426
- doc.body.innerHTML = `<div class="grid-stack-item"></div>`;
427
- newItem = doc.body.children[0];
428
- newItem.appendChild(content);
429
- doc.body.innerHTML = `<div class="grid-stack-item-content"></div>`;
430
- content = doc.body.children[0];
431
- node.el.appendChild(content);
432
- }
433
- this._prepareDragDropByNode(node); // ... and restore original D&D
434
- }
435
- // if we're adding an additional item, make the container large enough to have them both
436
- if (nodeToAdd) {
437
- let w = autoColumn ? ops.column : node.w;
438
- let h = node.h + nodeToAdd.h;
439
- let style = node.el.style;
440
- style.transition = 'none'; // show up instantly so we don't see scrollbar with nodeToAdd
441
- this.update(node.el, { w, h });
442
- setTimeout(() => style.transition = null); // recover animation
443
- }
444
- if (this.opts.addRemoveCB) {
445
- ops.addRemoveCB = ops.addRemoveCB || this.opts.addRemoveCB;
446
- }
447
- let subGrid = node.subGrid = GridStack.addGrid(content, ops);
448
- if (nodeToAdd === null || nodeToAdd === void 0 ? void 0 : nodeToAdd._moving)
449
- subGrid._isTemp = true; // prevent re-nesting as we add over
450
- if (autoColumn)
451
- subGrid._autoColumn = true;
452
- // add the original content back as a child of hte newly created grid
453
- if (saveContent) {
454
- subGrid.addWidget(newItem, newItemOpt);
455
- }
456
- // now add any additional node
457
- if (nodeToAdd) {
458
- if (nodeToAdd._moving) {
459
- // create an artificial event even for the just created grid to receive this item
460
- window.setTimeout(() => utils_1.Utils.simulateMouseEvent(nodeToAdd._event, 'mouseenter', subGrid.el), 0);
461
- }
462
- else {
463
- subGrid.addWidget(node.el, node);
464
- }
465
- }
466
- return subGrid;
467
- }
468
- /**
469
- * called when an item was converted into a nested grid to accommodate a dragged over item, but then item leaves - return back
470
- * to the original grid-item. Also called to remove empty sub-grids when last item is dragged out (since re-creating is simple)
471
- */
472
- removeAsSubGrid(nodeThatRemoved) {
473
- var _a;
474
- let pGrid = (_a = this.parentGridItem) === null || _a === void 0 ? void 0 : _a.grid;
475
- if (!pGrid)
476
- return;
477
- pGrid.batchUpdate();
478
- pGrid.removeWidget(this.parentGridItem.el, true, true);
479
- this.engine.nodes.forEach(n => {
480
- // migrate any children over and offsetting by our location
481
- n.x += this.parentGridItem.x;
482
- n.y += this.parentGridItem.y;
483
- pGrid.addWidget(n.el, n);
484
- });
485
- pGrid.batchUpdate(false);
486
- if (this.parentGridItem)
487
- delete this.parentGridItem.subGrid;
488
- delete this.parentGridItem;
489
- // create an artificial event for the original grid now that this one is gone (got a leave, but won't get enter)
490
- if (nodeThatRemoved) {
491
- window.setTimeout(() => utils_1.Utils.simulateMouseEvent(nodeThatRemoved._event, 'mouseenter', pGrid.el), 0);
492
- }
493
- }
494
- /**
495
- /**
496
- * saves the current layout returning a list of widgets for serialization which might include any nested grids.
497
- * @param saveContent if true (default) the latest html inside .grid-stack-content will be saved to GridStackWidget.content field, else it will
498
- * be removed.
499
- * @param saveGridOpt if true (default false), save the grid options itself, so you can call the new GridStack.addGrid()
500
- * to recreate everything from scratch. GridStackOptions.children would then contain the widget list instead.
501
- * @returns list of widgets or full grid option, including .children list of widgets
502
- */
503
- save(saveContent = true, saveGridOpt = false) {
504
- // return copied nodes we can modify at will...
505
- let list = this.engine.save(saveContent);
506
- // check for HTML content and nested grids
507
- list.forEach(n => {
508
- var _a;
509
- if (saveContent && n.el && !n.subGrid) { // sub-grid are saved differently, not plain content
510
- let sub = n.el.querySelector('.grid-stack-item-content');
511
- n.content = sub ? sub.innerHTML : undefined;
512
- if (!n.content)
513
- delete n.content;
514
- }
515
- else {
516
- if (!saveContent) {
517
- delete n.content;
518
- }
519
- // check for nested grid
520
- if ((_a = n.subGrid) === null || _a === void 0 ? void 0 : _a.el) {
521
- const listOrOpt = n.subGrid.save(saveContent, saveGridOpt);
522
- n.subGrid = (saveGridOpt ? listOrOpt : { children: listOrOpt });
523
- }
524
- }
525
- delete n.el;
526
- });
527
- // check if save entire grid options (needed for recursive) + children...
528
- if (saveGridOpt) {
529
- let o = utils_1.Utils.cloneDeep(this.opts);
530
- // delete default values that will be recreated on launch
531
- if (o.marginBottom === o.marginTop && o.marginRight === o.marginLeft && o.marginTop === o.marginRight) {
532
- o.margin = o.marginTop;
533
- delete o.marginTop;
534
- delete o.marginRight;
535
- delete o.marginBottom;
536
- delete o.marginLeft;
537
- }
538
- if (o.rtl === (this.el.style.direction === 'rtl')) {
539
- o.rtl = 'auto';
540
- }
541
- if (this._isAutoCellHeight) {
542
- o.cellHeight = 'auto';
543
- }
544
- if (this._autoColumn) {
545
- o.column = 'auto';
546
- delete o.disableOneColumnMode;
547
- }
548
- const origShow = o._alwaysShowResizeHandle;
549
- delete o._alwaysShowResizeHandle;
550
- if (origShow !== undefined) {
551
- o.alwaysShowResizeHandle = origShow;
552
- }
553
- else {
554
- delete o.alwaysShowResizeHandle;
555
- }
556
- utils_1.Utils.removeInternalAndSame(o, types_1.gridDefaults);
557
- o.children = list;
558
- return o;
559
- }
560
- return list;
561
- }
562
- /**
563
- * load the widgets from a list. This will call update() on each (matching by id) or add/remove widgets that are not there.
564
- *
565
- * @param layout list of widgets definition to update/create
566
- * @param addAndRemove boolean (default true) or callback method can be passed to control if and how missing widgets can be added/removed, giving
567
- * the user control of insertion.
568
- *
569
- * @example
570
- * see http://gridstackjs.com/demo/serialization.html
571
- **/
572
- load(layout, addRemove = this.opts.addRemoveCB || true) {
573
- let items = GridStack.Utils.sort([...layout], -1, this._prevColumn || this.getColumn()); // make copy before we mod/sort
574
- this._insertNotAppend = true; // since create in reverse order...
575
- // if we're loading a layout into for example 1 column (_prevColumn is set only when going to 1) and items don't fit, make sure to save
576
- // the original wanted layout so we can scale back up correctly #1471
577
- if (this._prevColumn && this._prevColumn !== this.opts.column && items.some(n => (n.x + n.w) > this.opts.column)) {
578
- this._ignoreLayoutsNodeChange = true; // skip layout update
579
- this.engine.cacheLayout(items, this._prevColumn, true);
580
- }
581
- // if given a different callback, temporally set it as global option to creating will use it
582
- const prevCB = this.opts.addRemoveCB;
583
- if (typeof (addRemove) === 'function')
584
- this.opts.addRemoveCB = addRemove;
585
- let removed = [];
586
- this.batchUpdate();
587
- // see if any items are missing from new layout and need to be removed first
588
- if (addRemove) {
589
- let copyNodes = [...this.engine.nodes]; // don't loop through array you modify
590
- copyNodes.forEach(n => {
591
- let item = items.find(w => n.id === w.id);
592
- if (!item) {
593
- if (this.opts.addRemoveCB)
594
- this.opts.addRemoveCB(this.el, n, false, false);
595
- removed.push(n); // batch keep track
596
- this.removeWidget(n.el, true, false);
597
- }
598
- });
599
- }
600
- // now add/update the widgets
601
- items.forEach(w => {
602
- let item = (w.id || w.id === 0) ? this.engine.nodes.find(n => n.id === w.id) : undefined;
603
- if (item) {
604
- this.update(item.el, w);
605
- if (w.subGrid && w.subGrid.children) { // update any sub grid as well
606
- let sub = item.el.querySelector('.grid-stack');
607
- if (sub && sub.gridstack) {
608
- sub.gridstack.load(w.subGrid.children); // TODO: support updating grid options ?
609
- this._insertNotAppend = true; // got reset by above call
610
- }
611
- }
612
- }
613
- else if (addRemove) {
614
- this.addWidget(w);
615
- }
616
- });
617
- this.engine.removedNodes = removed;
618
- this.batchUpdate(false);
619
- // after commit, clear that flag
620
- delete this._ignoreLayoutsNodeChange;
621
- delete this._insertNotAppend;
622
- prevCB ? this.opts.addRemoveCB = prevCB : delete this.opts.addRemoveCB;
623
- return this;
624
- }
625
- /**
626
- * use before calling a bunch of `addWidget()` to prevent un-necessary relayouts in between (more efficient)
627
- * and get a single event callback. You will see no changes until `batchUpdate(false)` is called.
628
- */
629
- batchUpdate(flag = true) {
630
- this.engine.batchUpdate(flag);
631
- if (!flag) {
632
- this._triggerRemoveEvent();
633
- this._triggerAddEvent();
634
- this._triggerChangeEvent();
635
- }
636
- return this;
637
- }
638
- /**
639
- * Gets current cell height.
640
- */
641
- getCellHeight(forcePixel = false) {
642
- if (this.opts.cellHeight && this.opts.cellHeight !== 'auto' &&
643
- (!forcePixel || !this.opts.cellHeightUnit || this.opts.cellHeightUnit === 'px')) {
644
- return this.opts.cellHeight;
645
- }
646
- // else get first cell height
647
- let el = this.el.querySelector('.' + this.opts.itemClass);
648
- if (el) {
649
- let height = utils_1.Utils.toNumber(el.getAttribute('gs-h'));
650
- return Math.round(el.offsetHeight / height);
651
- }
652
- // else do entire grid and # of rows (but doesn't work if min-height is the actual constrain)
653
- let rows = parseInt(this.el.getAttribute('gs-current-row'));
654
- return rows ? Math.round(this.el.getBoundingClientRect().height / rows) : this.opts.cellHeight;
655
- }
656
- /**
657
- * Update current cell height - see `GridStackOptions.cellHeight` for format.
658
- * This method rebuilds an internal CSS style sheet.
659
- * Note: You can expect performance issues if call this method too often.
660
- *
661
- * @param val the cell height. If not passed (undefined), cells content will be made square (match width minus margin),
662
- * if pass 0 the CSS will be generated by the application instead.
663
- * @param update (Optional) if false, styles will not be updated
664
- *
665
- * @example
666
- * grid.cellHeight(100); // same as 100px
667
- * grid.cellHeight('70px');
668
- * grid.cellHeight(grid.cellWidth() * 1.2);
669
- */
670
- cellHeight(val, update = true) {
671
- // if not called internally, check if we're changing mode
672
- if (update && val !== undefined) {
673
- if (this._isAutoCellHeight !== (val === 'auto')) {
674
- this._isAutoCellHeight = (val === 'auto');
675
- this._updateWindowResizeEvent();
676
- }
677
- }
678
- if (val === 'initial' || val === 'auto') {
679
- val = undefined;
680
- }
681
- // make item content be square
682
- if (val === undefined) {
683
- let marginDiff = -this.opts.marginRight - this.opts.marginLeft
684
- + this.opts.marginTop + this.opts.marginBottom;
685
- val = this.cellWidth() + marginDiff;
686
- }
687
- let data = utils_1.Utils.parseHeight(val);
688
- if (this.opts.cellHeightUnit === data.unit && this.opts.cellHeight === data.h) {
689
- return this;
690
- }
691
- this.opts.cellHeightUnit = data.unit;
692
- this.opts.cellHeight = data.h;
693
- if (update) {
694
- this._updateStyles(true); // true = force re-create for current # of rows
695
- }
696
- return this;
697
- }
698
- /** Gets current cell width. */
699
- cellWidth() {
700
- return this._widthOrContainer() / this.getColumn();
701
- }
702
- /** return our expected width (or parent) for 1 column check */
703
- _widthOrContainer() {
704
- // use `offsetWidth` or `clientWidth` (no scrollbar) ?
705
- // https://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively
706
- return (this.el.clientWidth || this.el.parentElement.clientWidth || window.innerWidth);
707
- }
708
- /** re-layout grid items to reclaim any empty space */
709
- compact() {
710
- this.engine.compact();
711
- this._triggerChangeEvent();
712
- return this;
713
- }
714
- /**
715
- * set the number of columns in the grid. Will update existing widgets to conform to new number of columns,
716
- * as well as cache the original layout so you can revert back to previous positions without loss.
717
- * Requires `gridstack-extra.css` or `gridstack-extra.min.css` for [2-11],
718
- * else you will need to generate correct CSS (see https://github.com/gridstack/gridstack.js#change-grid-columns)
719
- * @param column - Integer > 0 (default 12).
720
- * @param layout specify the type of re-layout that will happen (position, size, etc...).
721
- * Note: items will never be outside of the current column boundaries. default (moveScale). Ignored for 1 column
722
- */
723
- column(column, layout = 'moveScale') {
724
- if (column < 1 || this.opts.column === column)
725
- return this;
726
- let oldColumn = this.getColumn();
727
- // if we go into 1 column mode (which happens if we're sized less than minW unless disableOneColumnMode is on)
728
- // then remember the original columns so we can restore.
729
- if (column === 1) {
730
- this._prevColumn = oldColumn;
731
- }
732
- else {
733
- delete this._prevColumn;
734
- }
735
- this.el.classList.remove('grid-stack-' + oldColumn);
736
- this.el.classList.add('grid-stack-' + column);
737
- this.opts.column = this.engine.column = column;
738
- // update the items now - see if the dom order nodes should be passed instead (else default to current list)
739
- let domNodes;
740
- if (column === 1 && this.opts.oneColumnModeDomSort) {
741
- domNodes = [];
742
- this.getGridItems().forEach(el => {
743
- if (el.gridstackNode) {
744
- domNodes.push(el.gridstackNode);
745
- }
746
- });
747
- if (!domNodes.length) {
748
- domNodes = undefined;
749
- }
750
- }
751
- this.engine.updateNodeWidths(oldColumn, column, domNodes, layout);
752
- if (this._isAutoCellHeight)
753
- this.cellHeight();
754
- // and trigger our event last...
755
- this._ignoreLayoutsNodeChange = true; // skip layout update
756
- this._triggerChangeEvent();
757
- delete this._ignoreLayoutsNodeChange;
758
- return this;
759
- }
760
- /**
761
- * get the number of columns in the grid (default 12)
762
- */
763
- getColumn() {
764
- return this.opts.column;
765
- }
766
- /** returns an array of grid HTML elements (no placeholder) - used to iterate through our children in DOM order */
767
- getGridItems() {
768
- return Array.from(this.el.children)
769
- .filter((el) => el.matches('.' + this.opts.itemClass) && !el.matches('.' + this.opts.placeholderClass));
770
- }
771
- /**
772
- * Destroys a grid instance. DO NOT CALL any methods or access any vars after this as it will free up members.
773
- * @param removeDOM if `false` grid and items HTML elements will not be removed from the DOM (Optional. Default `true`).
774
- */
775
- destroy(removeDOM = true) {
776
- if (!this.el)
777
- return; // prevent multiple calls
778
- this._updateWindowResizeEvent(true);
779
- this.setStatic(true, false); // permanently removes DD but don't set CSS class (we're going away)
780
- this.setAnimation(false);
781
- if (!removeDOM) {
782
- this.removeAll(removeDOM);
783
- this.el.classList.remove(this._styleSheetClass);
784
- }
785
- else {
786
- this.el.parentNode.removeChild(this.el);
787
- }
788
- this._removeStylesheet();
789
- this.el.removeAttribute('gs-current-row');
790
- if (this.parentGridItem)
791
- delete this.parentGridItem.subGrid;
792
- delete this.parentGridItem;
793
- delete this.opts;
794
- delete this._placeholder;
795
- delete this.engine;
796
- delete this.el.gridstack; // remove circular dependency that would prevent a freeing
797
- delete this.el;
798
- return this;
799
- }
800
- /**
801
- * enable/disable floating widgets (default: `false`) See [example](http://gridstackjs.com/demo/float.html)
802
- */
803
- float(val) {
804
- if (this.opts.float !== val) {
805
- this.opts.float = this.engine.float = val;
806
- this._triggerChangeEvent();
807
- }
808
- return this;
809
- }
810
- /**
811
- * get the current float mode
812
- */
813
- getFloat() {
814
- return this.engine.float;
815
- }
816
- /**
817
- * Get the position of the cell under a pixel on screen.
818
- * @param position the position of the pixel to resolve in
819
- * absolute coordinates, as an object with top and left properties
820
- * @param useDocRelative if true, value will be based on document position vs parent position (Optional. Default false).
821
- * Useful when grid is within `position: relative` element
822
- *
823
- * Returns an object with properties `x` and `y` i.e. the column and row in the grid.
824
- */
825
- getCellFromPixel(position, useDocRelative = false) {
826
- let box = this.el.getBoundingClientRect();
827
- // console.log(`getBoundingClientRect left: ${box.left} top: ${box.top} w: ${box.w} h: ${box.h}`)
828
- let containerPos;
829
- if (useDocRelative) {
830
- containerPos = { top: box.top + document.documentElement.scrollTop, left: box.left };
831
- // console.log(`getCellFromPixel scrollTop: ${document.documentElement.scrollTop}`)
832
- }
833
- else {
834
- containerPos = { top: this.el.offsetTop, left: this.el.offsetLeft };
835
- // console.log(`getCellFromPixel offsetTop: ${containerPos.left} offsetLeft: ${containerPos.top}`)
836
- }
837
- let relativeLeft = position.left - containerPos.left;
838
- let relativeTop = position.top - containerPos.top;
839
- let columnWidth = (box.width / this.getColumn());
840
- let rowHeight = (box.height / parseInt(this.el.getAttribute('gs-current-row')));
841
- return { x: Math.floor(relativeLeft / columnWidth), y: Math.floor(relativeTop / rowHeight) };
842
- }
843
- /** returns the current number of rows, which will be at least `minRow` if set */
844
- getRow() {
845
- return Math.max(this.engine.getRow(), this.opts.minRow);
846
- }
847
- /**
848
- * Checks if specified area is empty.
849
- * @param x the position x.
850
- * @param y the position y.
851
- * @param w the width of to check
852
- * @param h the height of to check
853
- */
854
- isAreaEmpty(x, y, w, h) {
855
- return this.engine.isAreaEmpty(x, y, w, h);
856
- }
857
- /**
858
- * If you add elements to your grid by hand, you have to tell gridstack afterwards to make them widgets.
859
- * If you want gridstack to add the elements for you, use `addWidget()` instead.
860
- * Makes the given element a widget and returns it.
861
- * @param els widget or single selector to convert.
862
- *
863
- * @example
864
- * let grid = GridStack.init();
865
- * grid.el.appendChild('<div id="gsi-1" gs-w="3"></div>');
866
- * grid.makeWidget('#gsi-1');
867
- */
868
- makeWidget(els) {
869
- let el = GridStack.getElement(els);
870
- this._prepareElement(el, true);
871
- this._updateContainerHeight();
872
- this._triggerAddEvent();
873
- this._triggerChangeEvent();
874
- return el;
875
- }
876
- /**
877
- * Event handler that extracts our CustomEvent data out automatically for receiving custom
878
- * notifications (see doc for supported events)
879
- * @param name of the event (see possible values) or list of names space separated
880
- * @param callback function called with event and optional second/third param
881
- * (see README documentation for each signature).
882
- *
883
- * @example
884
- * grid.on('added', function(e, items) { log('added ', items)} );
885
- * or
886
- * grid.on('added removed change', function(e, items) { log(e.type, items)} );
887
- *
888
- * Note: in some cases it is the same as calling native handler and parsing the event.
889
- * grid.el.addEventListener('added', function(event) { log('added ', event.detail)} );
890
- *
891
- */
892
- on(name, callback) {
893
- // check for array of names being passed instead
894
- if (name.indexOf(' ') !== -1) {
895
- let names = name.split(' ');
896
- names.forEach(name => this.on(name, callback));
897
- return this;
898
- }
899
- if (name === 'change' || name === 'added' || name === 'removed' || name === 'enable' || name === 'disable') {
900
- // native CustomEvent handlers - cash the generic handlers so we can easily remove
901
- let noData = (name === 'enable' || name === 'disable');
902
- if (noData) {
903
- this._gsEventHandler[name] = (event) => callback(event);
904
- }
905
- else {
906
- this._gsEventHandler[name] = (event) => callback(event, event.detail);
907
- }
908
- this.el.addEventListener(name, this._gsEventHandler[name]);
909
- }
910
- else if (name === 'drag' || name === 'dragstart' || name === 'dragstop' || name === 'resizestart' || name === 'resize' || name === 'resizestop' || name === 'dropped') {
911
- // drag&drop stop events NEED to be call them AFTER we update node attributes so handle them ourself.
912
- // do same for start event to make it easier...
913
- this._gsEventHandler[name] = callback;
914
- }
915
- else {
916
- console.log('GridStack.on(' + name + ') event not supported, but you can still use $(".grid-stack").on(...) while jquery-ui is still used internally.');
917
- }
918
- return this;
919
- }
920
- /**
921
- * unsubscribe from the 'on' event below
922
- * @param name of the event (see possible values)
923
- */
924
- off(name) {
925
- // check for array of names being passed instead
926
- if (name.indexOf(' ') !== -1) {
927
- let names = name.split(' ');
928
- names.forEach(name => this.off(name));
929
- return this;
930
- }
931
- if (name === 'change' || name === 'added' || name === 'removed' || name === 'enable' || name === 'disable') {
932
- // remove native CustomEvent handlers
933
- if (this._gsEventHandler[name]) {
934
- this.el.removeEventListener(name, this._gsEventHandler[name]);
935
- }
936
- }
937
- delete this._gsEventHandler[name];
938
- return this;
939
- }
940
- /**
941
- * Removes widget from the grid.
942
- * @param el widget or selector to modify
943
- * @param removeDOM if `false` DOM element won't be removed from the tree (Default? true).
944
- * @param triggerEvent if `false` (quiet mode) element will not be added to removed list and no 'removed' callbacks will be called (Default? true).
945
- */
946
- removeWidget(els, removeDOM = true, triggerEvent = true) {
947
- GridStack.getElements(els).forEach(el => {
948
- if (el.parentElement && el.parentElement !== this.el)
949
- return; // not our child!
950
- let node = el.gridstackNode;
951
- // For Meteor support: https://github.com/gridstack/gridstack.js/pull/272
952
- if (!node) {
953
- node = this.engine.nodes.find(n => el === n.el);
954
- }
955
- if (!node)
956
- return;
957
- // remove our DOM data (circular link) and drag&drop permanently
958
- delete el.gridstackNode;
959
- this._removeDD(el);
960
- this.engine.removeNode(node, removeDOM, triggerEvent);
961
- if (removeDOM && el.parentElement) {
962
- el.remove(); // in batch mode engine.removeNode doesn't call back to remove DOM
963
- }
964
- });
965
- if (triggerEvent) {
966
- this._triggerRemoveEvent();
967
- this._triggerChangeEvent();
968
- }
969
- return this;
970
- }
971
- /**
972
- * Removes all widgets from the grid.
973
- * @param removeDOM if `false` DOM elements won't be removed from the tree (Default? `true`).
974
- */
975
- removeAll(removeDOM = true) {
976
- // always remove our DOM data (circular link) before list gets emptied and drag&drop permanently
977
- this.engine.nodes.forEach(n => {
978
- delete n.el.gridstackNode;
979
- this._removeDD(n.el);
980
- });
981
- this.engine.removeAll(removeDOM);
982
- this._triggerRemoveEvent();
983
- return this;
984
- }
985
- /**
986
- * Toggle the grid animation state. Toggles the `grid-stack-animate` class.
987
- * @param doAnimate if true the grid will animate.
988
- */
989
- setAnimation(doAnimate) {
990
- if (doAnimate) {
991
- this.el.classList.add('grid-stack-animate');
992
- }
993
- else {
994
- this.el.classList.remove('grid-stack-animate');
995
- }
996
- return this;
997
- }
998
- /**
999
- * Toggle the grid static state, which permanently removes/add Drag&Drop support, unlike disable()/enable() that just turns it off/on.
1000
- * Also toggle the grid-stack-static class.
1001
- * @param val if true the grid become static.
1002
- * @param updateClass true (default) if css class gets updated
1003
- * @param recurse true (default) if sub-grids also get updated
1004
- */
1005
- setStatic(val, updateClass = true, recurse = true) {
1006
- if (this.opts.staticGrid === val)
1007
- return this;
1008
- this.opts.staticGrid = val;
1009
- this._setupRemoveDrop();
1010
- this._setupAcceptWidget();
1011
- this.engine.nodes.forEach(n => {
1012
- this._prepareDragDropByNode(n); // either delete or init Drag&drop
1013
- if (n.subGrid && recurse)
1014
- n.subGrid.setStatic(val, updateClass, recurse);
1015
- });
1016
- if (updateClass) {
1017
- this._setStaticClass();
1018
- }
1019
- return this;
1020
- }
1021
- /**
1022
- * Updates widget position/size and other info. Note: if you need to call this on all nodes, use load() instead which will update what changed.
1023
- * @param els widget or selector of objects to modify (note: setting the same x,y for multiple items will be indeterministic and likely unwanted)
1024
- * @param opt new widget options (x,y,w,h, etc..). Only those set will be updated.
1025
- */
1026
- update(els, opt) {
1027
- // support legacy call for now ?
1028
- if (arguments.length > 2) {
1029
- console.warn('gridstack.ts: `update(el, x, y, w, h)` is deprecated. Use `update(el, {x, w, content, ...})`. It will be removed soon');
1030
- // eslint-disable-next-line prefer-rest-params
1031
- let a = arguments, i = 1;
1032
- opt = { x: a[i++], y: a[i++], w: a[i++], h: a[i++] };
1033
- return this.update(els, opt);
1034
- }
1035
- GridStack.getElements(els).forEach(el => {
1036
- if (!el || !el.gridstackNode)
1037
- return;
1038
- let n = el.gridstackNode;
1039
- let w = utils_1.Utils.cloneDeep(opt); // make a copy we can modify in case they re-use it or multiple items
1040
- delete w.autoPosition;
1041
- // move/resize widget if anything changed
1042
- let keys = ['x', 'y', 'w', 'h'];
1043
- let m;
1044
- if (keys.some(k => w[k] !== undefined && w[k] !== n[k])) {
1045
- m = {};
1046
- keys.forEach(k => {
1047
- m[k] = (w[k] !== undefined) ? w[k] : n[k];
1048
- delete w[k];
1049
- });
1050
- }
1051
- // for a move as well IFF there is any min/max fields set
1052
- if (!m && (w.minW || w.minH || w.maxW || w.maxH)) {
1053
- m = {}; // will use node position but validate values
1054
- }
1055
- // check for content changing
1056
- if (w.content) {
1057
- let sub = el.querySelector('.grid-stack-item-content');
1058
- if (sub && sub.innerHTML !== w.content) {
1059
- sub.innerHTML = w.content;
1060
- }
1061
- delete w.content;
1062
- }
1063
- // any remaining fields are assigned, but check for dragging changes, resize constrain
1064
- let changed = false;
1065
- let ddChanged = false;
1066
- for (const key in w) {
1067
- if (key[0] !== '_' && n[key] !== w[key]) {
1068
- n[key] = w[key];
1069
- changed = true;
1070
- ddChanged = ddChanged || (!this.opts.staticGrid && (key === 'noResize' || key === 'noMove' || key === 'locked'));
1071
- }
1072
- }
1073
- // finally move the widget
1074
- if (m) {
1075
- this.engine.cleanNodes()
1076
- .beginUpdate(n)
1077
- .moveNode(n, m);
1078
- this._updateContainerHeight();
1079
- this._triggerChangeEvent();
1080
- this.engine.endUpdate();
1081
- }
1082
- if (changed) { // move will only update x,y,w,h so update the rest too
1083
- this._writeAttr(el, n);
1084
- }
1085
- if (ddChanged) {
1086
- this._prepareDragDropByNode(n);
1087
- }
1088
- });
1089
- return this;
1090
- }
1091
- /**
1092
- * Updates the margins which will set all 4 sides at once - see `GridStackOptions.margin` for format options (CSS string format of 1,2,4 values or single number).
1093
- * @param value margin value
1094
- */
1095
- margin(value) {
1096
- let isMultiValue = (typeof value === 'string' && value.split(' ').length > 1);
1097
- // check if we can skip re-creating our CSS file... won't check if multi values (too much hassle)
1098
- if (!isMultiValue) {
1099
- let data = utils_1.Utils.parseHeight(value);
1100
- if (this.opts.marginUnit === data.unit && this.opts.margin === data.h)
1101
- return;
1102
- }
1103
- // re-use existing margin handling
1104
- this.opts.margin = value;
1105
- this.opts.marginTop = this.opts.marginBottom = this.opts.marginLeft = this.opts.marginRight = undefined;
1106
- this._initMargin();
1107
- this._updateStyles(true); // true = force re-create
1108
- return this;
1109
- }
1110
- /** returns current margin number value (undefined if 4 sides don't match) */
1111
- getMargin() { return this.opts.margin; }
1112
- /**
1113
- * Returns true if the height of the grid will be less than the vertical
1114
- * constraint. Always returns true if grid doesn't have height constraint.
1115
- * @param node contains x,y,w,h,auto-position options
1116
- *
1117
- * @example
1118
- * if (grid.willItFit(newWidget)) {
1119
- * grid.addWidget(newWidget);
1120
- * } else {
1121
- * alert('Not enough free space to place the widget');
1122
- * }
1123
- */
1124
- willItFit(node) {
1125
- // support legacy call for now
1126
- if (arguments.length > 1) {
1127
- console.warn('gridstack.ts: `willItFit(x,y,w,h,autoPosition)` is deprecated. Use `willItFit({x, y,...})`. It will be removed soon');
1128
- // eslint-disable-next-line prefer-rest-params
1129
- let a = arguments, i = 0, w = { x: a[i++], y: a[i++], w: a[i++], h: a[i++], autoPosition: a[i++] };
1130
- return this.willItFit(w);
1131
- }
1132
- return this.engine.willItFit(node);
1133
- }
1134
- /** @internal */
1135
- _triggerChangeEvent() {
1136
- if (this.engine.batchMode)
1137
- return this;
1138
- let elements = this.engine.getDirtyNodes(true); // verify they really changed
1139
- if (elements && elements.length) {
1140
- if (!this._ignoreLayoutsNodeChange) {
1141
- this.engine.layoutsNodesChange(elements);
1142
- }
1143
- this._triggerEvent('change', elements);
1144
- }
1145
- this.engine.saveInitial(); // we called, now reset initial values & dirty flags
1146
- return this;
1147
- }
1148
- /** @internal */
1149
- _triggerAddEvent() {
1150
- if (this.engine.batchMode)
1151
- return this;
1152
- if (this.engine.addedNodes && this.engine.addedNodes.length > 0) {
1153
- if (!this._ignoreLayoutsNodeChange) {
1154
- this.engine.layoutsNodesChange(this.engine.addedNodes);
1155
- }
1156
- // prevent added nodes from also triggering 'change' event (which is called next)
1157
- this.engine.addedNodes.forEach(n => { delete n._dirty; });
1158
- this._triggerEvent('added', this.engine.addedNodes);
1159
- this.engine.addedNodes = [];
1160
- }
1161
- return this;
1162
- }
1163
- /** @internal */
1164
- _triggerRemoveEvent() {
1165
- if (this.engine.batchMode)
1166
- return this;
1167
- if (this.engine.removedNodes && this.engine.removedNodes.length > 0) {
1168
- this._triggerEvent('removed', this.engine.removedNodes);
1169
- this.engine.removedNodes = [];
1170
- }
1171
- return this;
1172
- }
1173
- /** @internal */
1174
- _triggerEvent(type, data) {
1175
- let event = data ? new CustomEvent(type, { bubbles: false, detail: data }) : new Event(type);
1176
- this.el.dispatchEvent(event);
1177
- return this;
1178
- }
1179
- /** @internal called to delete the current dynamic style sheet used for our layout */
1180
- _removeStylesheet() {
1181
- if (this._styles) {
1182
- utils_1.Utils.removeStylesheet(this._styleSheetClass);
1183
- delete this._styles;
1184
- }
1185
- return this;
1186
- }
1187
- /** @internal updated/create the CSS styles for row based layout and initial margin setting */
1188
- _updateStyles(forceUpdate = false, maxH) {
1189
- // call to delete existing one if we change cellHeight / margin
1190
- if (forceUpdate) {
1191
- this._removeStylesheet();
1192
- }
1193
- if (!maxH)
1194
- maxH = this.getRow();
1195
- this._updateContainerHeight();
1196
- // if user is telling us they will handle the CSS themselves by setting heights to 0. Do we need this opts really ??
1197
- if (this.opts.cellHeight === 0) {
1198
- return this;
1199
- }
1200
- let cellHeight = this.opts.cellHeight;
1201
- let cellHeightUnit = this.opts.cellHeightUnit;
1202
- let prefix = `.${this._styleSheetClass} > .${this.opts.itemClass}`;
1203
- // create one as needed
1204
- if (!this._styles) {
1205
- // insert style to parent (instead of 'head' by default) to support WebComponent
1206
- let styleLocation = this.opts.styleInHead ? undefined : this.el.parentNode;
1207
- this._styles = utils_1.Utils.createStylesheet(this._styleSheetClass, styleLocation, {
1208
- nonce: this.opts.nonce,
1209
- });
1210
- if (!this._styles)
1211
- return this;
1212
- this._styles._max = 0;
1213
- // these are done once only
1214
- utils_1.Utils.addCSSRule(this._styles, prefix, `min-height: ${cellHeight}${cellHeightUnit}`);
1215
- // content margins
1216
- let top = this.opts.marginTop + this.opts.marginUnit;
1217
- let bottom = this.opts.marginBottom + this.opts.marginUnit;
1218
- let right = this.opts.marginRight + this.opts.marginUnit;
1219
- let left = this.opts.marginLeft + this.opts.marginUnit;
1220
- let content = `${prefix} > .grid-stack-item-content`;
1221
- let placeholder = `.${this._styleSheetClass} > .grid-stack-placeholder > .placeholder-content`;
1222
- utils_1.Utils.addCSSRule(this._styles, content, `top: ${top}; right: ${right}; bottom: ${bottom}; left: ${left};`);
1223
- utils_1.Utils.addCSSRule(this._styles, placeholder, `top: ${top}; right: ${right}; bottom: ${bottom}; left: ${left};`);
1224
- // resize handles offset (to match margin)
1225
- utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-ne`, `right: ${right}`);
1226
- utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-e`, `right: ${right}`);
1227
- utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-se`, `right: ${right}; bottom: ${bottom}`);
1228
- utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-nw`, `left: ${left}`);
1229
- utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-w`, `left: ${left}`);
1230
- utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-sw`, `left: ${left}; bottom: ${bottom}`);
1231
- }
1232
- // now update the height specific fields
1233
- maxH = maxH || this._styles._max;
1234
- if (maxH > this._styles._max) {
1235
- let getHeight = (rows) => (cellHeight * rows) + cellHeightUnit;
1236
- for (let i = this._styles._max + 1; i <= maxH; i++) { // start at 1
1237
- let h = getHeight(i);
1238
- utils_1.Utils.addCSSRule(this._styles, `${prefix}[gs-y="${i - 1}"]`, `top: ${getHeight(i - 1)}`); // start at 0
1239
- utils_1.Utils.addCSSRule(this._styles, `${prefix}[gs-h="${i}"]`, `height: ${h}`);
1240
- utils_1.Utils.addCSSRule(this._styles, `${prefix}[gs-min-h="${i}"]`, `min-height: ${h}`);
1241
- utils_1.Utils.addCSSRule(this._styles, `${prefix}[gs-max-h="${i}"]`, `max-height: ${h}`);
1242
- }
1243
- this._styles._max = maxH;
1244
- }
1245
- return this;
1246
- }
1247
- /** @internal */
1248
- _updateContainerHeight() {
1249
- if (!this.engine || this.engine.batchMode)
1250
- return this;
1251
- let row = this.getRow() + this._extraDragRow; // checks for minRow already
1252
- // check for css min height
1253
- // Note: we don't handle %,rem correctly so comment out, beside we don't need need to create un-necessary
1254
- // rows as the CSS will make us bigger than our set height if needed... not sure why we had this.
1255
- // let cssMinHeight = parseInt(getComputedStyle(this.el)['min-height']);
1256
- // if (cssMinHeight > 0) {
1257
- // let minRow = Math.round(cssMinHeight / this.getCellHeight(true));
1258
- // if (row < minRow) {
1259
- // row = minRow;
1260
- // }
1261
- // }
1262
- this.el.setAttribute('gs-current-row', String(row));
1263
- if (row === 0) {
1264
- this.el.style.removeProperty('min-height');
1265
- return this;
1266
- }
1267
- let cellHeight = this.opts.cellHeight;
1268
- let unit = this.opts.cellHeightUnit;
1269
- if (!cellHeight)
1270
- return this;
1271
- this.el.style.minHeight = row * cellHeight + unit;
1272
- return this;
1273
- }
1274
- /** @internal */
1275
- _prepareElement(el, triggerAddEvent = false, node) {
1276
- el.classList.add(this.opts.itemClass);
1277
- node = node || this._readAttr(el);
1278
- el.gridstackNode = node;
1279
- node.el = el;
1280
- node.grid = this;
1281
- let copy = Object.assign({}, node);
1282
- node = this.engine.addNode(node, triggerAddEvent);
1283
- // write node attr back in case there was collision or we have to fix bad values during addNode()
1284
- if (!utils_1.Utils.same(node, copy)) {
1285
- this._writeAttr(el, node);
1286
- }
1287
- this._prepareDragDropByNode(node);
1288
- return this;
1289
- }
1290
- /** @internal call to write position x,y,w,h attributes back to element */
1291
- _writePosAttr(el, n) {
1292
- if (n.x !== undefined && n.x !== null) {
1293
- el.setAttribute('gs-x', String(n.x));
1294
- }
1295
- if (n.y !== undefined && n.y !== null) {
1296
- el.setAttribute('gs-y', String(n.y));
1297
- }
1298
- if (n.w) {
1299
- el.setAttribute('gs-w', String(n.w));
1300
- }
1301
- if (n.h) {
1302
- el.setAttribute('gs-h', String(n.h));
1303
- }
1304
- return this;
1305
- }
1306
- /** @internal call to write any default attributes back to element */
1307
- _writeAttr(el, node) {
1308
- if (!node)
1309
- return this;
1310
- this._writePosAttr(el, node);
1311
- let attrs /*: GridStackWidget but strings */ = {
1312
- autoPosition: 'gs-auto-position',
1313
- minW: 'gs-min-w',
1314
- minH: 'gs-min-h',
1315
- maxW: 'gs-max-w',
1316
- maxH: 'gs-max-h',
1317
- noResize: 'gs-no-resize',
1318
- noMove: 'gs-no-move',
1319
- locked: 'gs-locked',
1320
- id: 'gs-id',
1321
- };
1322
- for (const key in attrs) {
1323
- if (node[key]) { // 0 is valid for x,y only but done above already and not in list anyway
1324
- el.setAttribute(attrs[key], String(node[key]));
1325
- }
1326
- else {
1327
- el.removeAttribute(attrs[key]);
1328
- }
1329
- }
1330
- return this;
1331
- }
1332
- /** @internal call to read any default attributes from element */
1333
- _readAttr(el) {
1334
- let node = {};
1335
- node.x = utils_1.Utils.toNumber(el.getAttribute('gs-x'));
1336
- node.y = utils_1.Utils.toNumber(el.getAttribute('gs-y'));
1337
- node.w = utils_1.Utils.toNumber(el.getAttribute('gs-w'));
1338
- node.h = utils_1.Utils.toNumber(el.getAttribute('gs-h'));
1339
- node.maxW = utils_1.Utils.toNumber(el.getAttribute('gs-max-w'));
1340
- node.minW = utils_1.Utils.toNumber(el.getAttribute('gs-min-w'));
1341
- node.maxH = utils_1.Utils.toNumber(el.getAttribute('gs-max-h'));
1342
- node.minH = utils_1.Utils.toNumber(el.getAttribute('gs-min-h'));
1343
- node.autoPosition = utils_1.Utils.toBool(el.getAttribute('gs-auto-position'));
1344
- node.noResize = utils_1.Utils.toBool(el.getAttribute('gs-no-resize'));
1345
- node.noMove = utils_1.Utils.toBool(el.getAttribute('gs-no-move'));
1346
- node.locked = utils_1.Utils.toBool(el.getAttribute('gs-locked'));
1347
- node.id = el.getAttribute('gs-id');
1348
- // remove any key not found (null or false which is default)
1349
- for (const key in node) {
1350
- if (!node.hasOwnProperty(key))
1351
- return;
1352
- if (!node[key] && node[key] !== 0) { // 0 can be valid value (x,y only really)
1353
- delete node[key];
1354
- }
1355
- }
1356
- return node;
1357
- }
1358
- /** @internal */
1359
- _setStaticClass() {
1360
- let classes = ['grid-stack-static'];
1361
- if (this.opts.staticGrid) {
1362
- this.el.classList.add(...classes);
1363
- this.el.setAttribute('gs-static', 'true');
1364
- }
1365
- else {
1366
- this.el.classList.remove(...classes);
1367
- this.el.removeAttribute('gs-static');
1368
- }
1369
- return this;
1370
- }
1371
- /**
1372
- * called when we are being resized by the window - check if the one Column Mode needs to be turned on/off
1373
- * and remember the prev columns we used, or get our count from parent, as well as check for auto cell height (square)
1374
- */
1375
- onParentResize() {
1376
- if (!this.el || !this.el.clientWidth)
1377
- return; // return if we're gone or no size yet (will get called again)
1378
- let changedColumn = false;
1379
- // see if we're nested and take our column count from our parent....
1380
- if (this._autoColumn && this.parentGridItem) {
1381
- if (this.opts.column !== this.parentGridItem.w) {
1382
- changedColumn = true;
1383
- this.column(this.parentGridItem.w, 'none');
1384
- }
1385
- }
1386
- else {
1387
- // else check for 1 column in/out behavior
1388
- let oneColumn = !this.opts.disableOneColumnMode && this.el.clientWidth <= this.opts.oneColumnSize;
1389
- if ((this.opts.column === 1) !== oneColumn) {
1390
- changedColumn = true;
1391
- if (this.opts.animate) {
1392
- this.setAnimation(false);
1393
- } // 1 <-> 12 is too radical, turn off animation
1394
- this.column(oneColumn ? 1 : this._prevColumn);
1395
- if (this.opts.animate) {
1396
- this.setAnimation(true);
1397
- }
1398
- }
1399
- }
1400
- // make the cells content square again
1401
- if (this._isAutoCellHeight) {
1402
- if (!changedColumn && this.opts.cellHeightThrottle) {
1403
- if (!this._cellHeightThrottle) {
1404
- this._cellHeightThrottle = utils_1.Utils.throttle(() => this.cellHeight(), this.opts.cellHeightThrottle);
1405
- }
1406
- this._cellHeightThrottle();
1407
- }
1408
- else {
1409
- // immediate update if we've changed column count or have no threshold
1410
- this.cellHeight();
1411
- }
1412
- }
1413
- // finally update any nested grids
1414
- this.engine.nodes.forEach(n => {
1415
- if (n.subGrid) {
1416
- n.subGrid.onParentResize();
1417
- }
1418
- });
1419
- return this;
1420
- }
1421
- /** add or remove the window size event handler */
1422
- _updateWindowResizeEvent(forceRemove = false) {
1423
- // 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)
1424
- const workTodo = (this._isAutoCellHeight || !this.opts.disableOneColumnMode) && !this.parentGridItem;
1425
- if (!forceRemove && workTodo && !this._windowResizeBind) {
1426
- this._windowResizeBind = this.onParentResize.bind(this); // so we can properly remove later
1427
- window.addEventListener('resize', this._windowResizeBind);
1428
- }
1429
- else if ((forceRemove || !workTodo) && this._windowResizeBind) {
1430
- window.removeEventListener('resize', this._windowResizeBind);
1431
- delete this._windowResizeBind; // remove link to us so we can free
1432
- }
1433
- return this;
1434
- }
1435
- /** @internal convert a potential selector into actual element */
1436
- static getElement(els = '.grid-stack-item') { return utils_1.Utils.getElement(els); }
1437
- /** @internal */
1438
- static getElements(els = '.grid-stack-item') { return utils_1.Utils.getElements(els); }
1439
- /** @internal */
1440
- static getGridElement(els) { return GridStack.getElement(els); }
1441
- /** @internal */
1442
- static getGridElements(els) { return utils_1.Utils.getElements(els); }
1443
- /** @internal initialize margin top/bottom/left/right and units */
1444
- _initMargin() {
1445
- let data;
1446
- let margin = 0;
1447
- // support passing multiple values like CSS (ex: '5px 10px 0 20px')
1448
- let margins = [];
1449
- if (typeof this.opts.margin === 'string') {
1450
- margins = this.opts.margin.split(' ');
1451
- }
1452
- if (margins.length === 2) { // top/bot, left/right like CSS
1453
- this.opts.marginTop = this.opts.marginBottom = margins[0];
1454
- this.opts.marginLeft = this.opts.marginRight = margins[1];
1455
- }
1456
- else if (margins.length === 4) { // Clockwise like CSS
1457
- this.opts.marginTop = margins[0];
1458
- this.opts.marginRight = margins[1];
1459
- this.opts.marginBottom = margins[2];
1460
- this.opts.marginLeft = margins[3];
1461
- }
1462
- else {
1463
- data = utils_1.Utils.parseHeight(this.opts.margin);
1464
- this.opts.marginUnit = data.unit;
1465
- margin = this.opts.margin = data.h;
1466
- }
1467
- // see if top/bottom/left/right need to be set as well
1468
- if (this.opts.marginTop === undefined) {
1469
- this.opts.marginTop = margin;
1470
- }
1471
- else {
1472
- data = utils_1.Utils.parseHeight(this.opts.marginTop);
1473
- this.opts.marginTop = data.h;
1474
- delete this.opts.margin;
1475
- }
1476
- if (this.opts.marginBottom === undefined) {
1477
- this.opts.marginBottom = margin;
1478
- }
1479
- else {
1480
- data = utils_1.Utils.parseHeight(this.opts.marginBottom);
1481
- this.opts.marginBottom = data.h;
1482
- delete this.opts.margin;
1483
- }
1484
- if (this.opts.marginRight === undefined) {
1485
- this.opts.marginRight = margin;
1486
- }
1487
- else {
1488
- data = utils_1.Utils.parseHeight(this.opts.marginRight);
1489
- this.opts.marginRight = data.h;
1490
- delete this.opts.margin;
1491
- }
1492
- if (this.opts.marginLeft === undefined) {
1493
- this.opts.marginLeft = margin;
1494
- }
1495
- else {
1496
- data = utils_1.Utils.parseHeight(this.opts.marginLeft);
1497
- this.opts.marginLeft = data.h;
1498
- delete this.opts.margin;
1499
- }
1500
- this.opts.marginUnit = data.unit; // in case side were spelled out, use those units instead...
1501
- if (this.opts.marginTop === this.opts.marginBottom && this.opts.marginLeft === this.opts.marginRight && this.opts.marginTop === this.opts.marginRight) {
1502
- this.opts.margin = this.opts.marginTop; // makes it easier to check for no-ops in setMargin()
1503
- }
1504
- return this;
1505
- }
1506
- /* ===========================================================================================
1507
- * drag&drop methods that used to be stubbed out and implemented in dd-gridstack.ts
1508
- * but caused loading issues in prod - see https://github.com/gridstack/gridstack.js/issues/2039
1509
- * ===========================================================================================
1510
- */
1511
- /** get the global (but static to this code) DD implementation */
1512
- static getDD() {
1513
- return dd;
1514
- }
1515
- /**
1516
- * call to setup dragging in from the outside (say toolbar), by specifying the class selection and options.
1517
- * Called during GridStack.init() as options, but can also be called directly (last param are used) in case the toolbar
1518
- * is dynamically create and needs to be set later.
1519
- * @param dragIn string selector (ex: '.sidebar .grid-stack-item')
1520
- * @param dragInOptions options - see DDDragInOpt. (default: {handle: '.grid-stack-item-content', appendTo: 'body'}
1521
- **/
1522
- static setupDragIn(dragIn, dragInOptions) {
1523
- if ((dragInOptions === null || dragInOptions === void 0 ? void 0 : dragInOptions.pause) !== undefined) {
1524
- dd_manager_1.DDManager.pauseDrag = dragInOptions.pause;
1525
- }
1526
- if (typeof dragIn === 'string') {
1527
- dragInOptions = Object.assign(Object.assign({}, types_1.dragInDefaultOptions), (dragInOptions || {}));
1528
- utils_1.Utils.getElements(dragIn).forEach(el => {
1529
- if (!dd.isDraggable(el))
1530
- dd.dragIn(el, dragInOptions);
1531
- });
1532
- }
1533
- }
1534
- /**
1535
- * Enables/Disables dragging by the user of specific grid element. If you want all items, and have it affect future items, use enableMove() instead. No-op for static grids.
1536
- * IF you are looking to prevent an item from moving (due to being pushed around by another during collision) use locked property instead.
1537
- * @param els widget or selector to modify.
1538
- * @param val if true widget will be draggable.
1539
- */
1540
- movable(els, val) {
1541
- if (this.opts.staticGrid)
1542
- return this; // can't move a static grid!
1543
- GridStack.getElements(els).forEach(el => {
1544
- let node = el.gridstackNode;
1545
- if (!node)
1546
- return;
1547
- if (val)
1548
- delete node.noMove;
1549
- else
1550
- node.noMove = true;
1551
- this._prepareDragDropByNode(node); // init DD if need be, and adjust
1552
- });
1553
- return this;
1554
- }
1555
- /**
1556
- * Enables/Disables user resizing of specific grid element. If you want all items, and have it affect future items, use enableResize() instead. No-op for static grids.
1557
- * @param els widget or selector to modify
1558
- * @param val if true widget will be resizable.
1559
- */
1560
- resizable(els, val) {
1561
- if (this.opts.staticGrid)
1562
- return this; // can't resize a static grid!
1563
- GridStack.getElements(els).forEach(el => {
1564
- let node = el.gridstackNode;
1565
- if (!node)
1566
- return;
1567
- if (val)
1568
- delete node.noResize;
1569
- else
1570
- node.noResize = true;
1571
- this._prepareDragDropByNode(node); // init DD if need be, and adjust
1572
- });
1573
- return this;
1574
- }
1575
- /**
1576
- * Temporarily disables widgets moving/resizing.
1577
- * If you want a more permanent way (which freezes up resources) use `setStatic(true)` instead.
1578
- * Note: no-op for static grid
1579
- * This is a shortcut for:
1580
- * @example
1581
- * grid.enableMove(false);
1582
- * grid.enableResize(false);
1583
- * @param recurse true (default) if sub-grids also get updated
1584
- */
1585
- disable(recurse = true) {
1586
- if (this.opts.staticGrid)
1587
- return;
1588
- this.enableMove(false, recurse);
1589
- this.enableResize(false, recurse); // @ts-ignore
1590
- this._triggerEvent('disable');
1591
- return this;
1592
- }
1593
- /**
1594
- * Re-enables widgets moving/resizing - see disable().
1595
- * Note: no-op for static grid.
1596
- * This is a shortcut for:
1597
- * @example
1598
- * grid.enableMove(true);
1599
- * grid.enableResize(true);
1600
- * @param recurse true (default) if sub-grids also get updated
1601
- */
1602
- enable(recurse = true) {
1603
- if (this.opts.staticGrid)
1604
- return;
1605
- this.enableMove(true, recurse);
1606
- this.enableResize(true, recurse); // @ts-ignore
1607
- this._triggerEvent('enable');
1608
- return this;
1609
- }
1610
- /**
1611
- * Enables/disables widget moving. No-op for static grids.
1612
- * @param recurse true (default) if sub-grids also get updated
1613
- */
1614
- enableMove(doEnable, recurse = true) {
1615
- if (this.opts.staticGrid)
1616
- return this; // can't move a static grid!
1617
- this.opts.disableDrag = !doEnable; // FIRST before we update children as grid overrides #1658
1618
- this.engine.nodes.forEach(n => {
1619
- this.movable(n.el, doEnable);
1620
- if (n.subGrid && recurse)
1621
- n.subGrid.enableMove(doEnable, recurse);
1622
- });
1623
- return this;
1624
- }
1625
- /**
1626
- * Enables/disables widget resizing. No-op for static grids.
1627
- * @param recurse true (default) if sub-grids also get updated
1628
- */
1629
- enableResize(doEnable, recurse = true) {
1630
- if (this.opts.staticGrid)
1631
- return this; // can't size a static grid!
1632
- this.opts.disableResize = !doEnable; // FIRST before we update children as grid overrides #1658
1633
- this.engine.nodes.forEach(n => {
1634
- this.resizable(n.el, doEnable);
1635
- if (n.subGrid && recurse)
1636
- n.subGrid.enableResize(doEnable, recurse);
1637
- });
1638
- return this;
1639
- }
1640
- /** @internal removes any drag&drop present (called during destroy) */
1641
- _removeDD(el) {
1642
- dd.draggable(el, 'destroy').resizable(el, 'destroy');
1643
- if (el.gridstackNode) {
1644
- delete el.gridstackNode._initDD; // reset our DD init flag
1645
- }
1646
- delete el.ddElement;
1647
- return this;
1648
- }
1649
- /** @internal called to add drag over to support widgets being added externally */
1650
- _setupAcceptWidget() {
1651
- // check if we need to disable things
1652
- if (this.opts.staticGrid || (!this.opts.acceptWidgets && !this.opts.removable)) {
1653
- dd.droppable(this.el, 'destroy');
1654
- return this;
1655
- }
1656
- // vars shared across all methods
1657
- let cellHeight, cellWidth;
1658
- let onDrag = (event, el, helper) => {
1659
- let node = el.gridstackNode;
1660
- if (!node)
1661
- return;
1662
- helper = helper || el;
1663
- let parent = this.el.getBoundingClientRect();
1664
- let { top, left } = helper.getBoundingClientRect();
1665
- left -= parent.left;
1666
- top -= parent.top;
1667
- let ui = { position: { top, left } };
1668
- if (node._temporaryRemoved) {
1669
- node.x = Math.max(0, Math.round(left / cellWidth));
1670
- node.y = Math.max(0, Math.round(top / cellHeight));
1671
- delete node.autoPosition;
1672
- this.engine.nodeBoundFix(node);
1673
- // don't accept *initial* location if doesn't fit #1419 (locked drop region, or can't grow), but maybe try if it will go somewhere
1674
- if (!this.engine.willItFit(node)) {
1675
- node.autoPosition = true; // ignore x,y and try for any slot...
1676
- if (!this.engine.willItFit(node)) {
1677
- dd.off(el, 'drag'); // stop calling us
1678
- return; // full grid or can't grow
1679
- }
1680
- if (node._willFitPos) {
1681
- // use the auto position instead #1687
1682
- utils_1.Utils.copyPos(node, node._willFitPos);
1683
- delete node._willFitPos;
1684
- }
1685
- }
1686
- // re-use the existing node dragging method
1687
- this._onStartMoving(helper, event, ui, node, cellWidth, cellHeight);
1688
- }
1689
- else {
1690
- // re-use the existing node dragging that does so much of the collision detection
1691
- this._dragOrResize(helper, event, ui, node, cellWidth, cellHeight);
1692
- }
1693
- };
1694
- dd.droppable(this.el, {
1695
- accept: (el) => {
1696
- let node = el.gridstackNode;
1697
- // set accept drop to true on ourself (which we ignore) so we don't get "can't drop" icon in HTML5 mode while moving
1698
- if ((node === null || node === void 0 ? void 0 : node.grid) === this)
1699
- return true;
1700
- if (!this.opts.acceptWidgets)
1701
- return false;
1702
- // check for accept method or class matching
1703
- let canAccept = true;
1704
- if (typeof this.opts.acceptWidgets === 'function') {
1705
- canAccept = this.opts.acceptWidgets(el);
1706
- }
1707
- else {
1708
- let selector = (this.opts.acceptWidgets === true ? '.grid-stack-item' : this.opts.acceptWidgets);
1709
- canAccept = el.matches(selector);
1710
- }
1711
- // finally check to make sure we actually have space left #1571
1712
- if (canAccept && node && this.opts.maxRow) {
1713
- let n = { w: node.w, h: node.h, minW: node.minW, minH: node.minH }; // only width/height matters and autoPosition
1714
- canAccept = this.engine.willItFit(n);
1715
- }
1716
- return canAccept;
1717
- }
1718
- })
1719
- /**
1720
- * entering our grid area
1721
- */
1722
- .on(this.el, 'dropover', (event, el, helper) => {
1723
- // console.log(`over ${this.el.gridstack.opts.id} ${count++}`); // TEST
1724
- let node = el.gridstackNode;
1725
- // ignore drop enter on ourself (unless we temporarily removed) which happens on a simple drag of our item
1726
- if ((node === null || node === void 0 ? void 0 : node.grid) === this && !node._temporaryRemoved) {
1727
- // delete node._added; // reset this to track placeholder again in case we were over other grid #1484 (dropout doesn't always clear)
1728
- return false; // prevent parent from receiving msg (which may be a grid as well)
1729
- }
1730
- // fix #1578 when dragging fast, we may not get a leave on the previous grid so force one now
1731
- if ((node === null || node === void 0 ? void 0 : node.grid) && node.grid !== this && !node._temporaryRemoved) {
1732
- // console.log('dropover without leave'); // TEST
1733
- let otherGrid = node.grid;
1734
- otherGrid._leave(el, helper);
1735
- }
1736
- // cache cell dimensions (which don't change), position can animate if we removed an item in otherGrid that affects us...
1737
- cellWidth = this.cellWidth();
1738
- cellHeight = this.getCellHeight(true);
1739
- // load any element attributes if we don't have a node
1740
- if (!node) { // @ts-ignore private read only on ourself
1741
- node = this._readAttr(el);
1742
- }
1743
- if (!node.grid) {
1744
- node._isExternal = true;
1745
- el.gridstackNode = node;
1746
- }
1747
- // calculate the grid size based on element outer size
1748
- helper = helper || el;
1749
- let w = node.w || Math.round(helper.offsetWidth / cellWidth) || 1;
1750
- let h = node.h || Math.round(helper.offsetHeight / cellHeight) || 1;
1751
- // if the item came from another grid, make a copy and save the original info in case we go back there
1752
- if (node.grid && node.grid !== this) {
1753
- // copy the node original values (min/max/id/etc...) but override width/height/other flags which are this grid specific
1754
- // console.log('dropover cloning node'); // TEST
1755
- if (!el._gridstackNodeOrig)
1756
- el._gridstackNodeOrig = node; // shouldn't have multiple nested!
1757
- el.gridstackNode = node = Object.assign(Object.assign({}, node), { w, h, grid: this });
1758
- this.engine.cleanupNode(node)
1759
- .nodeBoundFix(node);
1760
- // restore some internal fields we need after clearing them all
1761
- node._initDD =
1762
- node._isExternal = // DOM needs to be re-parented on a drop
1763
- node._temporaryRemoved = true; // so it can be inserted onDrag below
1764
- }
1765
- else {
1766
- node.w = w;
1767
- node.h = h;
1768
- node._temporaryRemoved = true; // so we can insert it
1769
- }
1770
- // clear any marked for complete removal (Note: don't check _isAboutToRemove as that is cleared above - just do it)
1771
- this._itemRemoving(node.el, false);
1772
- dd.on(el, 'drag', onDrag);
1773
- // make sure this is called at least once when going fast #1578
1774
- onDrag(event, el, helper);
1775
- return false; // prevent parent from receiving msg (which may be a grid as well)
1776
- })
1777
- /**
1778
- * Leaving our grid area...
1779
- */
1780
- .on(this.el, 'dropout', (event, el, helper) => {
1781
- // console.log(`out ${this.el.gridstack.opts.id} ${count++}`); // TEST
1782
- let node = el.gridstackNode;
1783
- if (!node)
1784
- return false;
1785
- // fix #1578 when dragging fast, we might get leave after other grid gets enter (which calls us to clean)
1786
- // so skip this one if we're not the active grid really..
1787
- if (!node.grid || node.grid === this) {
1788
- this._leave(el, helper);
1789
- // if we were created as temporary nested grid, go back to before state
1790
- if (this._isTemp) {
1791
- this.removeAsSubGrid(node);
1792
- }
1793
- }
1794
- return false; // prevent parent from receiving msg (which may be grid as well)
1795
- })
1796
- /**
1797
- * end - releasing the mouse
1798
- */
1799
- .on(this.el, 'drop', (event, el, helper) => {
1800
- var _a, _b;
1801
- let node = el.gridstackNode;
1802
- // ignore drop on ourself from ourself that didn't come from the outside - dragend will handle the simple move instead
1803
- if ((node === null || node === void 0 ? void 0 : node.grid) === this && !node._isExternal)
1804
- return false;
1805
- let wasAdded = !!this.placeholder.parentElement; // skip items not actually added to us because of constrains, but do cleanup #1419
1806
- this.placeholder.remove();
1807
- // notify previous grid of removal
1808
- // console.log('drop delete _gridstackNodeOrig') // TEST
1809
- let origNode = el._gridstackNodeOrig;
1810
- delete el._gridstackNodeOrig;
1811
- if (wasAdded && (origNode === null || origNode === void 0 ? void 0 : origNode.grid) && origNode.grid !== this) {
1812
- let oGrid = origNode.grid;
1813
- oGrid.engine.removedNodes.push(origNode);
1814
- oGrid._triggerRemoveEvent()._triggerChangeEvent();
1815
- // if it's an empty sub-grid that got auto-created, nuke it
1816
- if (oGrid.parentGridItem && !oGrid.engine.nodes.length && oGrid.opts.subGridDynamic) {
1817
- oGrid.removeAsSubGrid();
1818
- }
1819
- }
1820
- if (!node)
1821
- return false;
1822
- // use existing placeholder node as it's already in our list with drop location
1823
- if (wasAdded) {
1824
- this.engine.cleanupNode(node); // removes all internal _xyz values
1825
- node.grid = this;
1826
- }
1827
- dd.off(el, 'drag');
1828
- // if we made a copy ('helper' which is temp) of the original node then insert a copy, else we move the original node (#1102)
1829
- // as the helper will be nuked by jquery-ui otherwise. TODO: update old code path
1830
- if (helper !== el) {
1831
- helper.remove();
1832
- el.gridstackNode = origNode; // original item (left behind) is re-stored to pre dragging as the node now has drop info
1833
- if (wasAdded) {
1834
- el = el.cloneNode(true);
1835
- }
1836
- }
1837
- else {
1838
- el.remove(); // reduce flicker as we change depth here, and size further down
1839
- this._removeDD(el);
1840
- }
1841
- if (!wasAdded)
1842
- return false;
1843
- el.gridstackNode = node;
1844
- node.el = el;
1845
- let subGrid = (_b = (_a = node.subGrid) === null || _a === void 0 ? void 0 : _a.el) === null || _b === void 0 ? void 0 : _b.gridstack; // set when actual sub-grid present
1846
- // @ts-ignore
1847
- utils_1.Utils.copyPos(node, this._readAttr(this.placeholder)); // placeholder values as moving VERY fast can throw things off #1578
1848
- utils_1.Utils.removePositioningStyles(el); // @ts-ignore
1849
- this._writeAttr(el, node);
1850
- el.classList.add(types_1.gridDefaults.itemClass, this.opts.itemClass);
1851
- this.el.appendChild(el); // @ts-ignore // TODO: now would be ideal time to _removeHelperStyle() overriding floating styles (native only)
1852
- if (subGrid) {
1853
- subGrid.parentGridItem = node;
1854
- if (!subGrid.opts.styleInHead)
1855
- subGrid._updateStyles(true); // re-create sub-grid styles now that we've moved
1856
- }
1857
- this._updateContainerHeight();
1858
- this.engine.addedNodes.push(node); // @ts-ignore
1859
- this._triggerAddEvent(); // @ts-ignore
1860
- this._triggerChangeEvent();
1861
- this.engine.endUpdate();
1862
- if (this._gsEventHandler['dropped']) {
1863
- this._gsEventHandler['dropped'](Object.assign(Object.assign({}, event), { type: 'dropped' }), origNode && origNode.grid ? origNode : undefined, node);
1864
- }
1865
- // wait till we return out of the drag callback to set the new drag&resize handler or they may get messed up
1866
- window.setTimeout(() => {
1867
- // IFF we are still there (some application will use as placeholder and insert their real widget instead and better call makeWidget())
1868
- if (node.el && node.el.parentElement) {
1869
- this._prepareDragDropByNode(node);
1870
- }
1871
- else {
1872
- this.engine.removeNode(node);
1873
- }
1874
- delete node.grid._isTemp;
1875
- });
1876
- return false; // prevent parent from receiving msg (which may be grid as well)
1877
- });
1878
- return this;
1879
- }
1880
- /** @internal mark item for removal */
1881
- _itemRemoving(el, remove) {
1882
- let node = el ? el.gridstackNode : undefined;
1883
- if (!node || !node.grid)
1884
- return;
1885
- remove ? node._isAboutToRemove = true : delete node._isAboutToRemove;
1886
- remove ? el.classList.add('grid-stack-item-removing') : el.classList.remove('grid-stack-item-removing');
1887
- }
1888
- /** @internal called to setup a trash drop zone if the user specifies it */
1889
- _setupRemoveDrop() {
1890
- if (!this.opts.staticGrid && typeof this.opts.removable === 'string') {
1891
- let trashEl = document.querySelector(this.opts.removable);
1892
- if (!trashEl)
1893
- return this;
1894
- // only register ONE drop-over/dropout callback for the 'trash', and it will
1895
- // update the passed in item and parent grid because the 'trash' is a shared resource anyway,
1896
- // and Native DD only has 1 event CB (having a list and technically a per grid removableOptions complicates things greatly)
1897
- if (!dd.isDroppable(trashEl)) {
1898
- dd.droppable(trashEl, this.opts.removableOptions)
1899
- .on(trashEl, 'dropover', (event, el) => this._itemRemoving(el, true))
1900
- .on(trashEl, 'dropout', (event, el) => this._itemRemoving(el, false));
1901
- }
1902
- }
1903
- return this;
1904
- }
1905
- /** @internal prepares the element for drag&drop **/
1906
- _prepareDragDropByNode(node) {
1907
- let el = node.el;
1908
- const noMove = node.noMove || this.opts.disableDrag;
1909
- const noResize = node.noResize || this.opts.disableResize;
1910
- // check for disabled grid first
1911
- if (this.opts.staticGrid || (noMove && noResize)) {
1912
- if (node._initDD) {
1913
- this._removeDD(el); // nukes everything instead of just disable, will add some styles back next
1914
- delete node._initDD;
1915
- }
1916
- el.classList.add('ui-draggable-disabled', 'ui-resizable-disabled'); // add styles one might depend on #1435
1917
- return this;
1918
- }
1919
- if (!node._initDD) {
1920
- // variables used/cashed between the 3 start/move/end methods, in addition to node passed above
1921
- let cellWidth;
1922
- let cellHeight;
1923
- /** called when item starts moving/resizing */
1924
- let onStartMoving = (event, ui) => {
1925
- // trigger any 'dragstart' / 'resizestart' manually
1926
- if (this._gsEventHandler[event.type]) {
1927
- this._gsEventHandler[event.type](event, event.target);
1928
- }
1929
- cellWidth = this.cellWidth();
1930
- cellHeight = this.getCellHeight(true); // force pixels for calculations
1931
- this._onStartMoving(el, event, ui, node, cellWidth, cellHeight);
1932
- };
1933
- /** called when item is being dragged/resized */
1934
- let dragOrResize = (event, ui) => {
1935
- this._dragOrResize(el, event, ui, node, cellWidth, cellHeight);
1936
- };
1937
- /** called when the item stops moving/resizing */
1938
- let onEndMoving = (event) => {
1939
- this.placeholder.remove();
1940
- delete node._moving;
1941
- delete node._event;
1942
- delete node._lastTried;
1943
- // if the item has moved to another grid, we're done here
1944
- let target = event.target;
1945
- if (!target.gridstackNode || target.gridstackNode.grid !== this)
1946
- return;
1947
- node.el = target;
1948
- if (node._isAboutToRemove) {
1949
- let gridToNotify = el.gridstackNode.grid;
1950
- if (gridToNotify._gsEventHandler[event.type]) {
1951
- gridToNotify._gsEventHandler[event.type](event, target);
1952
- }
1953
- this._removeDD(el);
1954
- gridToNotify.engine.removedNodes.push(node);
1955
- gridToNotify._triggerRemoveEvent();
1956
- // break circular links and remove DOM
1957
- delete el.gridstackNode;
1958
- delete node.el;
1959
- el.remove();
1960
- }
1961
- else {
1962
- utils_1.Utils.removePositioningStyles(target);
1963
- if (node._temporaryRemoved) {
1964
- // got removed - restore item back to before dragging position
1965
- utils_1.Utils.copyPos(node, node._orig); // @ts-ignore
1966
- this._writePosAttr(target, node);
1967
- this.engine.addNode(node);
1968
- }
1969
- else {
1970
- // move to new placeholder location
1971
- this._writePosAttr(target, node);
1972
- }
1973
- if (this._gsEventHandler[event.type]) {
1974
- this._gsEventHandler[event.type](event, target);
1975
- }
1976
- }
1977
- // @ts-ignore
1978
- this._extraDragRow = 0; // @ts-ignore
1979
- this._updateContainerHeight(); // @ts-ignore
1980
- this._triggerChangeEvent();
1981
- this.engine.endUpdate();
1982
- };
1983
- dd.draggable(el, {
1984
- start: onStartMoving,
1985
- stop: onEndMoving,
1986
- drag: dragOrResize
1987
- }).resizable(el, {
1988
- start: onStartMoving,
1989
- stop: onEndMoving,
1990
- resize: dragOrResize
1991
- });
1992
- node._initDD = true; // we've set DD support now
1993
- }
1994
- // finally fine tune move vs resize by disabling any part...
1995
- dd.draggable(el, noMove ? 'disable' : 'enable')
1996
- .resizable(el, noResize ? 'disable' : 'enable');
1997
- return this;
1998
- }
1999
- /** @internal handles actual drag/resize start **/
2000
- _onStartMoving(el, event, ui, node, cellWidth, cellHeight) {
2001
- this.engine.cleanNodes()
2002
- .beginUpdate(node);
2003
- // @ts-ignore
2004
- this._writePosAttr(this.placeholder, node);
2005
- this.el.appendChild(this.placeholder);
2006
- // console.log('_onStartMoving placeholder') // TEST
2007
- node.el = this.placeholder;
2008
- node._lastUiPosition = ui.position;
2009
- node._prevYPix = ui.position.top;
2010
- node._moving = (event.type === 'dragstart'); // 'dropover' are not initially moving so they can go exactly where they enter (will push stuff out of the way)
2011
- delete node._lastTried;
2012
- if (event.type === 'dropover' && node._temporaryRemoved) {
2013
- // console.log('engine.addNode x=' + node.x); // TEST
2014
- this.engine.addNode(node); // will add, fix collisions, update attr and clear _temporaryRemoved
2015
- node._moving = true; // AFTER, mark as moving object (wanted fix location before)
2016
- }
2017
- // set the min/max resize info
2018
- this.engine.cacheRects(cellWidth, cellHeight, this.opts.marginTop, this.opts.marginRight, this.opts.marginBottom, this.opts.marginLeft);
2019
- if (event.type === 'resizestart') {
2020
- dd.resizable(el, 'option', 'minWidth', cellWidth * (node.minW || 1))
2021
- .resizable(el, 'option', 'minHeight', cellHeight * (node.minH || 1));
2022
- if (node.maxW) {
2023
- dd.resizable(el, 'option', 'maxWidth', cellWidth * node.maxW);
2024
- }
2025
- if (node.maxH) {
2026
- dd.resizable(el, 'option', 'maxHeight', cellHeight * node.maxH);
2027
- }
2028
- }
2029
- }
2030
- /** @internal handles actual drag/resize **/
2031
- _dragOrResize(el, event, ui, node, cellWidth, cellHeight) {
2032
- let p = Object.assign({}, node._orig); // could be undefined (_isExternal) which is ok (drag only set x,y and w,h will default to node value)
2033
- let resizing;
2034
- let mLeft = this.opts.marginLeft, mRight = this.opts.marginRight, mTop = this.opts.marginTop, mBottom = this.opts.marginBottom;
2035
- // if margins (which are used to pass mid point by) are large relative to cell height/width, reduce them down #1855
2036
- let mHeight = Math.round(cellHeight * 0.1), mWidth = Math.round(cellWidth * 0.1);
2037
- mLeft = Math.min(mLeft, mWidth);
2038
- mRight = Math.min(mRight, mWidth);
2039
- mTop = Math.min(mTop, mHeight);
2040
- mBottom = Math.min(mBottom, mHeight);
2041
- if (event.type === 'drag') {
2042
- if (node._temporaryRemoved)
2043
- return; // handled by dropover
2044
- let distance = ui.position.top - node._prevYPix;
2045
- node._prevYPix = ui.position.top;
2046
- if (this.opts.draggable.scroll !== false) {
2047
- utils_1.Utils.updateScrollPosition(el, ui.position, distance);
2048
- }
2049
- // get new position taking into account the margin in the direction we are moving! (need to pass mid point by margin)
2050
- let left = ui.position.left + (ui.position.left > node._lastUiPosition.left ? -mRight : mLeft);
2051
- let top = ui.position.top + (ui.position.top > node._lastUiPosition.top ? -mBottom : mTop);
2052
- p.x = Math.round(left / cellWidth);
2053
- p.y = Math.round(top / cellHeight);
2054
- // @ts-ignore// if we're at the bottom hitting something else, grow the grid so cursor doesn't leave when trying to place below others
2055
- let prev = this._extraDragRow;
2056
- if (this.engine.collide(node, p)) {
2057
- let row = this.getRow();
2058
- let extra = Math.max(0, (p.y + node.h) - row);
2059
- if (this.opts.maxRow && row + extra > this.opts.maxRow) {
2060
- extra = Math.max(0, this.opts.maxRow - row);
2061
- } // @ts-ignore
2062
- this._extraDragRow = extra; // @ts-ignore
2063
- }
2064
- else
2065
- this._extraDragRow = 0; // @ts-ignore
2066
- if (this._extraDragRow !== prev)
2067
- this._updateContainerHeight();
2068
- if (node.x === p.x && node.y === p.y)
2069
- return; // skip same
2070
- // DON'T skip one we tried as we might have failed because of coverage <50% before
2071
- // if (node._lastTried && node._lastTried.x === x && node._lastTried.y === y) return;
2072
- }
2073
- else if (event.type === 'resize') {
2074
- if (p.x < 0)
2075
- return;
2076
- // Scrolling page if needed
2077
- utils_1.Utils.updateScrollResize(event, el, cellHeight);
2078
- // get new size
2079
- p.w = Math.round((ui.size.width - mLeft) / cellWidth);
2080
- p.h = Math.round((ui.size.height - mTop) / cellHeight);
2081
- if (node.w === p.w && node.h === p.h)
2082
- return;
2083
- if (node._lastTried && node._lastTried.w === p.w && node._lastTried.h === p.h)
2084
- return; // skip one we tried (but failed)
2085
- // if we size on left/top side this might move us, so get possible new position as well
2086
- let left = ui.position.left + mLeft;
2087
- let top = ui.position.top + mTop;
2088
- p.x = Math.round(left / cellWidth);
2089
- p.y = Math.round(top / cellHeight);
2090
- resizing = true;
2091
- }
2092
- node._event = event;
2093
- node._lastTried = p; // set as last tried (will nuke if we go there)
2094
- let rect = {
2095
- x: ui.position.left + mLeft,
2096
- y: ui.position.top + mTop,
2097
- w: (ui.size ? ui.size.width : node.w * cellWidth) - mLeft - mRight,
2098
- h: (ui.size ? ui.size.height : node.h * cellHeight) - mTop - mBottom
2099
- };
2100
- if (this.engine.moveNodeCheck(node, Object.assign(Object.assign({}, p), { cellWidth, cellHeight, rect, resizing }))) {
2101
- node._lastUiPosition = ui.position;
2102
- this.engine.cacheRects(cellWidth, cellHeight, mTop, mRight, mBottom, mLeft);
2103
- delete node._skipDown;
2104
- if (resizing && node.subGrid) {
2105
- node.subGrid.onParentResize();
2106
- } // @ts-ignore
2107
- this._extraDragRow = 0; // @ts-ignore
2108
- this._updateContainerHeight();
2109
- let target = event.target; // @ts-ignore
2110
- this._writePosAttr(target, node);
2111
- if (this._gsEventHandler[event.type]) {
2112
- this._gsEventHandler[event.type](event, target);
2113
- }
2114
- }
2115
- }
2116
- /** @internal called when item leaving our area by either cursor dropout event
2117
- * or shape is outside our boundaries. remove it from us, and mark temporary if this was
2118
- * our item to start with else restore prev node values from prev grid it came from.
2119
- **/
2120
- _leave(el, helper) {
2121
- let node = el.gridstackNode;
2122
- if (!node)
2123
- return;
2124
- dd.off(el, 'drag'); // no need to track while being outside
2125
- // this gets called when cursor leaves and shape is outside, so only do this once
2126
- if (node._temporaryRemoved)
2127
- return;
2128
- node._temporaryRemoved = true;
2129
- this.engine.removeNode(node); // remove placeholder as well, otherwise it's a sign node is not in our list, which is a bigger issue
2130
- node.el = node._isExternal && helper ? helper : el; // point back to real item being dragged
2131
- if (this.opts.removable === true) { // boolean vs a class string
2132
- // item leaving us and we are supposed to remove on leave (no need to drag onto trash) mark it so
2133
- this._itemRemoving(el, true);
2134
- }
2135
- // finally if item originally came from another grid, but left us, restore things back to prev info
2136
- if (el._gridstackNodeOrig) {
2137
- // console.log('leave delete _gridstackNodeOrig') // TEST
2138
- el.gridstackNode = el._gridstackNodeOrig;
2139
- delete el._gridstackNodeOrig;
2140
- }
2141
- else if (node._isExternal) {
2142
- // item came from outside (like a toolbar) so nuke any node info
2143
- delete node.el;
2144
- delete el.gridstackNode;
2145
- // and restore all nodes back to original
2146
- this.engine.restoreInitial();
2147
- }
2148
- }
2149
- // legacy method removed
2150
- commit() { utils_1.obsolete(this, this.batchUpdate(false), 'commit', 'batchUpdate', '5.2'); return this; }
2151
- }
2152
- exports.GridStack = GridStack;
2153
- /** scoping so users can call GridStack.Utils.sort() for example */
2154
- GridStack.Utils = utils_1.Utils;
2155
- /** scoping so users can call new GridStack.Engine(12) for example */
2156
- GridStack.Engine = gridstack_engine_1.GridStackEngine;
2157
- GridStack.GDRev = '7.3.0';
1
+ /*!
2
+ * GridStack 8.0.0
3
+ * https://gridstackjs.com/
4
+ *
5
+ * Copyright (c) 2021-2022 Alain Dumesny
6
+ * see root license https://github.com/gridstack/gridstack.js/tree/master/LICENSE
7
+ */
8
+ import { GridStackEngine } from './gridstack-engine';
9
+ import { Utils, obsolete } from './utils';
10
+ import { gridDefaults, dragInDefaultOptions } from './types';
11
+ /*
12
+ * and include D&D by default
13
+ * TODO: while we could generate a gridstack-static.js at smaller size - saves about 31k (41k -> 72k)
14
+ * I don't know how to generate the DD only code at the remaining 31k to delay load as code depends on Gridstack.ts
15
+ * also it caused loading issues in prod - see https://github.com/gridstack/gridstack.js/issues/2039
16
+ */
17
+ import { DDGridStack } from './dd-gridstack';
18
+ import { isTouch } from './dd-touch';
19
+ import { DDManager } from './dd-manager';
20
+ /** global instance */
21
+ const dd = new DDGridStack;
22
+ // export all dependent file as well to make it easier for users to just import the main file
23
+ export * from './types';
24
+ export * from './utils';
25
+ export * from './gridstack-engine';
26
+ export * from './dd-gridstack';
27
+ /**
28
+ * Main gridstack class - you will need to call `GridStack.init()` first to initialize your grid.
29
+ * Note: your grid elements MUST have the following classes for the CSS layout to work:
30
+ * @example
31
+ * <div class="grid-stack">
32
+ * <div class="grid-stack-item">
33
+ * <div class="grid-stack-item-content">Item 1</div>
34
+ * </div>
35
+ * </div>
36
+ */
37
+ class GridStack {
38
+ /**
39
+ * initializing the HTML element, or selector string, into a grid will return the grid. Calling it again will
40
+ * simply return the existing instance (ignore any passed options). There is also an initAll() version that support
41
+ * multiple grids initialization at once. Or you can use addGrid() to create the entire grid from JSON.
42
+ * @param options grid options (optional)
43
+ * @param elOrString element or CSS selector (first one used) to convert to a grid (default to '.grid-stack' class selector)
44
+ *
45
+ * @example
46
+ * let grid = GridStack.init();
47
+ *
48
+ * Note: the HTMLElement (of type GridHTMLElement) will store a `gridstack: GridStack` value that can be retrieve later
49
+ * let grid = document.querySelector('.grid-stack').gridstack;
50
+ */
51
+ static init(options = {}, elOrString = '.grid-stack') {
52
+ let el = GridStack.getGridElement(elOrString);
53
+ if (!el) {
54
+ if (typeof elOrString === 'string') {
55
+ console.error('GridStack.initAll() no grid was found with selector "' + elOrString + '" - element missing or wrong selector ?' +
56
+ '\nNote: ".grid-stack" is required for proper CSS styling and drag/drop, and is the default selector.');
57
+ }
58
+ else {
59
+ console.error('GridStack.init() no grid element was passed.');
60
+ }
61
+ return null;
62
+ }
63
+ if (!el.gridstack) {
64
+ el.gridstack = new GridStack(el, Utils.cloneDeep(options));
65
+ }
66
+ return el.gridstack;
67
+ }
68
+ /**
69
+ * Will initialize a list of elements (given a selector) and return an array of grids.
70
+ * @param options grid options (optional)
71
+ * @param selector elements selector to convert to grids (default to '.grid-stack' class selector)
72
+ *
73
+ * @example
74
+ * let grids = GridStack.initAll();
75
+ * grids.forEach(...)
76
+ */
77
+ static initAll(options = {}, selector = '.grid-stack') {
78
+ let grids = [];
79
+ GridStack.getGridElements(selector).forEach(el => {
80
+ if (!el.gridstack) {
81
+ el.gridstack = new GridStack(el, Utils.cloneDeep(options));
82
+ delete options.dragIn;
83
+ delete options.dragInOptions; // only need to be done once (really a static global thing, not per grid)
84
+ }
85
+ grids.push(el.gridstack);
86
+ });
87
+ if (grids.length === 0) {
88
+ console.error('GridStack.initAll() no grid was found with selector "' + selector + '" - element missing or wrong selector ?' +
89
+ '\nNote: ".grid-stack" is required for proper CSS styling and drag/drop, and is the default selector.');
90
+ }
91
+ return grids;
92
+ }
93
+ /**
94
+ * call to create a grid with the given options, including loading any children from JSON structure. This will call GridStack.init(), then
95
+ * grid.load() on any passed children (recursively). Great alternative to calling init() if you want entire grid to come from
96
+ * JSON serialized data, including options.
97
+ * @param parent HTML element parent to the grid
98
+ * @param opt grids options used to initialize the grid, and list of children
99
+ */
100
+ static addGrid(parent, opt = {}) {
101
+ if (!parent)
102
+ return null;
103
+ let el = parent;
104
+ if (el.gridstack) {
105
+ // already a grid - set option and load data
106
+ const grid = el.gridstack;
107
+ if (opt)
108
+ grid.opts = { ...grid.opts, ...opt };
109
+ if (opt.children !== undefined)
110
+ grid.load(opt.children);
111
+ return grid;
112
+ }
113
+ // create the grid element, but check if the passed 'parent' already has grid styling and should be used instead
114
+ const parentIsGrid = parent.classList.contains('grid-stack');
115
+ if (!parentIsGrid || GridStack.addRemoveCB) {
116
+ if (GridStack.addRemoveCB) {
117
+ el = GridStack.addRemoveCB(parent, opt, true, true);
118
+ }
119
+ else {
120
+ let doc = document.implementation.createHTMLDocument(''); // IE needs a param
121
+ doc.body.innerHTML = `<div class="grid-stack ${opt.class || ''}"></div>`;
122
+ el = doc.body.children[0];
123
+ parent.appendChild(el);
124
+ }
125
+ }
126
+ // create grid class and load any children
127
+ let grid = GridStack.init(opt, el);
128
+ return grid;
129
+ }
130
+ /** call this method to register your engine instead of the default one.
131
+ * See instead `GridStackOptions.engineClass` if you only need to
132
+ * replace just one instance.
133
+ */
134
+ static registerEngine(engineClass) {
135
+ GridStack.engineClass = engineClass;
136
+ }
137
+ /** @internal create placeholder DIV as needed */
138
+ get placeholder() {
139
+ if (!this._placeholder) {
140
+ let placeholderChild = document.createElement('div'); // child so padding match item-content
141
+ placeholderChild.className = 'placeholder-content';
142
+ if (this.opts.placeholderText) {
143
+ placeholderChild.innerHTML = this.opts.placeholderText;
144
+ }
145
+ this._placeholder = document.createElement('div');
146
+ this._placeholder.classList.add(this.opts.placeholderClass, gridDefaults.itemClass, this.opts.itemClass);
147
+ this.placeholder.appendChild(placeholderChild);
148
+ }
149
+ return this._placeholder;
150
+ }
151
+ /**
152
+ * Construct a grid item from the given element and options
153
+ * @param el
154
+ * @param opts
155
+ */
156
+ constructor(el, opts = {}) {
157
+ /** @internal */
158
+ this._gsEventHandler = {};
159
+ /** @internal extra row added when dragging at the bottom of the grid */
160
+ this._extraDragRow = 0;
161
+ this.el = el; // exposed HTML element to the user
162
+ opts = opts || {}; // handles null/undefined/0
163
+ if (!el.classList.contains('grid-stack')) {
164
+ this.el.classList.add('grid-stack');
165
+ }
166
+ // if row property exists, replace minRow and maxRow instead
167
+ if (opts.row) {
168
+ opts.minRow = opts.maxRow = opts.row;
169
+ delete opts.row;
170
+ }
171
+ let rowAttr = Utils.toNumber(el.getAttribute('gs-row'));
172
+ // flag only valid in sub-grids (handled by parent, not here)
173
+ if (opts.column === 'auto') {
174
+ delete opts.column;
175
+ }
176
+ // 'minWidth' legacy support in 5.1
177
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
178
+ let anyOpts = opts;
179
+ if (anyOpts.minWidth !== undefined) {
180
+ opts.oneColumnSize = opts.oneColumnSize || anyOpts.minWidth;
181
+ delete anyOpts.minWidth;
182
+ }
183
+ // save original setting so we can restore on save
184
+ if (opts.alwaysShowResizeHandle !== undefined) {
185
+ opts._alwaysShowResizeHandle = opts.alwaysShowResizeHandle;
186
+ }
187
+ // elements DOM attributes override any passed options (like CSS style) - merge the two together
188
+ let defaults = { ...Utils.cloneDeep(gridDefaults),
189
+ column: Utils.toNumber(el.getAttribute('gs-column')) || gridDefaults.column,
190
+ minRow: rowAttr ? rowAttr : Utils.toNumber(el.getAttribute('gs-min-row')) || gridDefaults.minRow,
191
+ maxRow: rowAttr ? rowAttr : Utils.toNumber(el.getAttribute('gs-max-row')) || gridDefaults.maxRow,
192
+ staticGrid: Utils.toBool(el.getAttribute('gs-static')) || gridDefaults.staticGrid,
193
+ draggable: {
194
+ handle: (opts.handleClass ? '.' + opts.handleClass : (opts.handle ? opts.handle : '')) || gridDefaults.draggable.handle,
195
+ },
196
+ removableOptions: {
197
+ accept: opts.itemClass ? '.' + opts.itemClass : gridDefaults.removableOptions.accept,
198
+ },
199
+ };
200
+ if (el.getAttribute('gs-animate')) { // default to true, but if set to false use that instead
201
+ defaults.animate = Utils.toBool(el.getAttribute('gs-animate'));
202
+ }
203
+ this.opts = Utils.defaults(opts, defaults);
204
+ opts = null; // make sure we use this.opts instead
205
+ this._initMargin(); // part of settings defaults...
206
+ // 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)
207
+ if (this.opts.column !== 1 && !this.opts.disableOneColumnMode && this._widthOrContainer() <= this.opts.oneColumnSize) {
208
+ this._prevColumn = this.getColumn();
209
+ this.opts.column = 1;
210
+ }
211
+ if (this.opts.rtl === 'auto') {
212
+ this.opts.rtl = (el.style.direction === 'rtl');
213
+ }
214
+ if (this.opts.rtl) {
215
+ this.el.classList.add('grid-stack-rtl');
216
+ }
217
+ // check if we're been nested, and if so update our style and keep pointer around (used during save)
218
+ let parentGridItem = Utils.closestUpByClass(this.el, gridDefaults.itemClass)?.gridstackNode;
219
+ if (parentGridItem) {
220
+ parentGridItem.subGrid = this;
221
+ this.parentGridItem = parentGridItem;
222
+ this.el.classList.add('grid-stack-nested');
223
+ parentGridItem.el.classList.add('grid-stack-sub-grid');
224
+ }
225
+ this._isAutoCellHeight = (this.opts.cellHeight === 'auto');
226
+ if (this._isAutoCellHeight || this.opts.cellHeight === 'initial') {
227
+ // make the cell content square initially (will use resize/column event to keep it square)
228
+ this.cellHeight(undefined, false);
229
+ }
230
+ else {
231
+ // append unit if any are set
232
+ if (typeof this.opts.cellHeight == 'number' && this.opts.cellHeightUnit && this.opts.cellHeightUnit !== gridDefaults.cellHeightUnit) {
233
+ this.opts.cellHeight = this.opts.cellHeight + this.opts.cellHeightUnit;
234
+ delete this.opts.cellHeightUnit;
235
+ }
236
+ this.cellHeight(this.opts.cellHeight, false);
237
+ }
238
+ // see if we need to adjust auto-hide
239
+ if (this.opts.alwaysShowResizeHandle === 'mobile') {
240
+ this.opts.alwaysShowResizeHandle = isTouch;
241
+ }
242
+ this._styleSheetClass = 'grid-stack-instance-' + GridStackEngine._idSeq++;
243
+ this.el.classList.add(this._styleSheetClass);
244
+ this._setStaticClass();
245
+ let engineClass = this.opts.engineClass || GridStack.engineClass || GridStackEngine;
246
+ this.engine = new engineClass({
247
+ column: this.getColumn(),
248
+ float: this.opts.float,
249
+ maxRow: this.opts.maxRow,
250
+ onChange: (cbNodes) => {
251
+ let maxH = 0;
252
+ this.engine.nodes.forEach(n => { maxH = Math.max(maxH, n.y + n.h); });
253
+ cbNodes.forEach(n => {
254
+ let el = n.el;
255
+ if (!el)
256
+ return;
257
+ if (n._removeDOM) {
258
+ if (el)
259
+ el.remove();
260
+ delete n._removeDOM;
261
+ }
262
+ else {
263
+ this._writePosAttr(el, n);
264
+ }
265
+ });
266
+ this._updateStyles(false, maxH); // false = don't recreate, just append if need be
267
+ }
268
+ });
269
+ if (this.opts.auto) {
270
+ this.batchUpdate(); // prevent in between re-layout #1535 TODO: this only set float=true, need to prevent collision check...
271
+ this.getGridItems().forEach(el => this._prepareElement(el));
272
+ this.batchUpdate(false);
273
+ }
274
+ // load any passed in children as well, which overrides any DOM layout done above
275
+ if (this.opts.children) {
276
+ let children = this.opts.children;
277
+ delete this.opts.children;
278
+ if (children.length)
279
+ this.load(children); // don't load empty
280
+ }
281
+ this.setAnimation(this.opts.animate);
282
+ this._updateStyles();
283
+ if (this.opts.column != 12) {
284
+ this.el.classList.add('grid-stack-' + this.opts.column);
285
+ }
286
+ // legacy support to appear 'per grid` options when really global.
287
+ if (this.opts.dragIn)
288
+ GridStack.setupDragIn(this.opts.dragIn, this.opts.dragInOptions);
289
+ delete this.opts.dragIn;
290
+ delete this.opts.dragInOptions;
291
+ // dynamic grids require pausing during drag to detect over to nest vs push
292
+ if (this.opts.subGridDynamic && !DDManager.pauseDrag)
293
+ DDManager.pauseDrag = true;
294
+ if (this.opts.draggable?.pause !== undefined)
295
+ DDManager.pauseDrag = this.opts.draggable.pause;
296
+ this._setupRemoveDrop();
297
+ this._setupAcceptWidget();
298
+ this._updateWindowResizeEvent();
299
+ }
300
+ /**
301
+ * add a new widget and returns it.
302
+ *
303
+ * Widget will be always placed even if result height is more than actual grid height.
304
+ * You need to use `willItFit()` before calling addWidget for additional check.
305
+ * See also `makeWidget()`.
306
+ *
307
+ * @example
308
+ * let grid = GridStack.init();
309
+ * grid.addWidget({w: 3, content: 'hello'});
310
+ * grid.addWidget('<div class="grid-stack-item"><div class="grid-stack-item-content">hello</div></div>', {w: 3});
311
+ *
312
+ * @param el GridStackWidget (which can have content string as well), html element, or string definition to add
313
+ * @param options widget position/size options (optional, and ignore if first param is already option) - see GridStackWidget
314
+ */
315
+ addWidget(els, options) {
316
+ function isGridStackWidget(w) {
317
+ return w.el !== undefined || w.x !== undefined || w.y !== undefined || w.w !== undefined || w.h !== undefined || w.content !== undefined ? true : false;
318
+ }
319
+ let el;
320
+ let node;
321
+ if (typeof els === 'string') {
322
+ let doc = document.implementation.createHTMLDocument(''); // IE needs a param
323
+ doc.body.innerHTML = els;
324
+ el = doc.body.children[0];
325
+ }
326
+ else if (arguments.length === 0 || arguments.length === 1 && isGridStackWidget(els)) {
327
+ node = options = els;
328
+ if (node?.el) {
329
+ el = node.el; // re-use element stored in the node
330
+ }
331
+ else if (GridStack.addRemoveCB) {
332
+ el = GridStack.addRemoveCB(this.el, options, true, false);
333
+ }
334
+ else {
335
+ let content = options?.content || '';
336
+ let doc = document.implementation.createHTMLDocument(''); // IE needs a param
337
+ doc.body.innerHTML = `<div class="grid-stack-item ${this.opts.itemClass || ''}"><div class="grid-stack-item-content">${content}</div></div>`;
338
+ el = doc.body.children[0];
339
+ }
340
+ }
341
+ else {
342
+ el = els;
343
+ }
344
+ if (!el)
345
+ return;
346
+ // Tempting to initialize the passed in opt with default and valid values, but this break knockout demos
347
+ // as the actual value are filled in when _prepareElement() calls el.getAttribute('gs-xyz') before adding the node.
348
+ // So make sure we load any DOM attributes that are not specified in passed in options (which override)
349
+ let domAttr = this._readAttr(el);
350
+ options = Utils.cloneDeep(options) || {}; // make a copy before we modify in case caller re-uses it
351
+ Utils.defaults(options, domAttr);
352
+ node = this.engine.prepareNode(options);
353
+ this._writeAttr(el, options);
354
+ if (this._insertNotAppend) {
355
+ this.el.prepend(el);
356
+ }
357
+ else {
358
+ this.el.appendChild(el);
359
+ }
360
+ // similar to makeWidget() that doesn't read attr again and worse re-create a new node and loose any _id
361
+ this._prepareElement(el, true, options);
362
+ this._updateContainerHeight();
363
+ // see if there is a sub-grid to create
364
+ if (node.subGridOpts) {
365
+ this.makeSubGrid(node.el, node.subGridOpts, undefined, false); // node.subGrid will be used as option in method, no need to pass
366
+ }
367
+ // if we're adding an item into 1 column (_prevColumn is set only when going to 1) make sure
368
+ // we don't override the larger 12 column layout that was already saved. #1985
369
+ if (this._prevColumn && this.opts.column === 1) {
370
+ this._ignoreLayoutsNodeChange = true;
371
+ }
372
+ this._triggerAddEvent();
373
+ this._triggerChangeEvent();
374
+ delete this._ignoreLayoutsNodeChange;
375
+ return el;
376
+ }
377
+ /**
378
+ * Convert an existing gridItem element into a sub-grid with the given (optional) options, else inherit them
379
+ * from the parent's subGrid options.
380
+ * @param el gridItem element to convert
381
+ * @param ops (optional) sub-grid options, else default to node, then parent settings, else defaults
382
+ * @param nodeToAdd (optional) node to add to the newly created sub grid (used when dragging over existing regular item)
383
+ * @returns newly created grid
384
+ */
385
+ makeSubGrid(el, ops, nodeToAdd, saveContent = true) {
386
+ let node = el.gridstackNode;
387
+ if (!node) {
388
+ node = this.makeWidget(el).gridstackNode;
389
+ }
390
+ if (node.subGrid?.el)
391
+ return node.subGrid; // already done
392
+ // find the template subGrid stored on a parent as fallback...
393
+ let subGridTemplate; // eslint-disable-next-line @typescript-eslint/no-this-alias
394
+ let grid = this;
395
+ while (grid && !subGridTemplate) {
396
+ subGridTemplate = grid.opts?.subGridOpts;
397
+ grid = grid.parentGridItem?.grid;
398
+ }
399
+ //... and set the create options
400
+ ops = Utils.cloneDeep({ ...(subGridTemplate || {}), children: undefined, ...(ops || node.subGridOpts) });
401
+ node.subGridOpts = ops;
402
+ // if column special case it set, remember that flag and set default
403
+ let autoColumn;
404
+ if (ops.column === 'auto') {
405
+ autoColumn = true;
406
+ ops.column = Math.max(node.w || 1, nodeToAdd?.w || 1);
407
+ ops.disableOneColumnMode = true; // driven by parent
408
+ }
409
+ // if we're converting an existing full item, move over the content to be the first sub item in the new grid
410
+ let content = node.el.querySelector('.grid-stack-item-content');
411
+ let newItem;
412
+ let newItemOpt;
413
+ if (saveContent) {
414
+ this._removeDD(node.el); // remove D&D since it's set on content div
415
+ newItemOpt = { ...node, x: 0, y: 0 };
416
+ Utils.removeInternalForSave(newItemOpt);
417
+ delete newItemOpt.subGridOpts;
418
+ if (node.content) {
419
+ newItemOpt.content = node.content;
420
+ delete node.content;
421
+ }
422
+ if (GridStack.addRemoveCB) {
423
+ newItem = GridStack.addRemoveCB(this.el, newItemOpt, true, false);
424
+ }
425
+ else {
426
+ let doc = document.implementation.createHTMLDocument(''); // IE needs a param
427
+ doc.body.innerHTML = `<div class="grid-stack-item"></div>`;
428
+ newItem = doc.body.children[0];
429
+ newItem.appendChild(content);
430
+ doc.body.innerHTML = `<div class="grid-stack-item-content"></div>`;
431
+ content = doc.body.children[0];
432
+ node.el.appendChild(content);
433
+ }
434
+ this._prepareDragDropByNode(node); // ... and restore original D&D
435
+ }
436
+ // if we're adding an additional item, make the container large enough to have them both
437
+ if (nodeToAdd) {
438
+ let w = autoColumn ? ops.column : node.w;
439
+ let h = node.h + nodeToAdd.h;
440
+ let style = node.el.style;
441
+ style.transition = 'none'; // show up instantly so we don't see scrollbar with nodeToAdd
442
+ this.update(node.el, { w, h });
443
+ setTimeout(() => style.transition = null); // recover animation
444
+ }
445
+ let subGrid = node.subGrid = GridStack.addGrid(content, ops);
446
+ if (nodeToAdd?._moving)
447
+ subGrid._isTemp = true; // prevent re-nesting as we add over
448
+ if (autoColumn)
449
+ subGrid._autoColumn = true;
450
+ // add the original content back as a child of hte newly created grid
451
+ if (saveContent) {
452
+ subGrid.addWidget(newItem, newItemOpt);
453
+ }
454
+ // now add any additional node
455
+ if (nodeToAdd) {
456
+ if (nodeToAdd._moving) {
457
+ // create an artificial event even for the just created grid to receive this item
458
+ window.setTimeout(() => Utils.simulateMouseEvent(nodeToAdd._event, 'mouseenter', subGrid.el), 0);
459
+ }
460
+ else {
461
+ subGrid.addWidget(node.el, node);
462
+ }
463
+ }
464
+ return subGrid;
465
+ }
466
+ /**
467
+ * called when an item was converted into a nested grid to accommodate a dragged over item, but then item leaves - return back
468
+ * to the original grid-item. Also called to remove empty sub-grids when last item is dragged out (since re-creating is simple)
469
+ */
470
+ removeAsSubGrid(nodeThatRemoved) {
471
+ let pGrid = this.parentGridItem?.grid;
472
+ if (!pGrid)
473
+ return;
474
+ pGrid.batchUpdate();
475
+ pGrid.removeWidget(this.parentGridItem.el, true, true);
476
+ this.engine.nodes.forEach(n => {
477
+ // migrate any children over and offsetting by our location
478
+ n.x += this.parentGridItem.x;
479
+ n.y += this.parentGridItem.y;
480
+ pGrid.addWidget(n.el, n);
481
+ });
482
+ pGrid.batchUpdate(false);
483
+ if (this.parentGridItem)
484
+ delete this.parentGridItem.subGrid;
485
+ delete this.parentGridItem;
486
+ // create an artificial event for the original grid now that this one is gone (got a leave, but won't get enter)
487
+ if (nodeThatRemoved) {
488
+ window.setTimeout(() => Utils.simulateMouseEvent(nodeThatRemoved._event, 'mouseenter', pGrid.el), 0);
489
+ }
490
+ }
491
+ /**
492
+ * saves the current layout returning a list of widgets for serialization which might include any nested grids.
493
+ * @param saveContent if true (default) the latest html inside .grid-stack-content will be saved to GridStackWidget.content field, else it will
494
+ * be removed.
495
+ * @param saveGridOpt if true (default false), save the grid options itself, so you can call the new GridStack.addGrid()
496
+ * to recreate everything from scratch. GridStackOptions.children would then contain the widget list instead.
497
+ * @param saveCB callback for each node -> widget, so application can insert additional data to be saved into the widget data structure.
498
+ * @returns list of widgets or full grid option, including .children list of widgets
499
+ */
500
+ save(saveContent = true, saveGridOpt = false, saveCB = GridStack.saveCB) {
501
+ // return copied GridStackWidget (with optionally .el) we can modify at will...
502
+ let list = this.engine.save(saveContent, saveCB);
503
+ // check for HTML content and nested grids
504
+ list.forEach(n => {
505
+ if (saveContent && n.el && !n.subGrid && !saveCB) { // sub-grid are saved differently, not plain content
506
+ let sub = n.el.querySelector('.grid-stack-item-content');
507
+ n.content = sub ? sub.innerHTML : undefined;
508
+ if (!n.content)
509
+ delete n.content;
510
+ }
511
+ else {
512
+ if (!saveContent && !saveCB) {
513
+ delete n.content;
514
+ }
515
+ // check for nested grid
516
+ if (n.subGrid?.el) {
517
+ const listOrOpt = n.subGrid.save(saveContent, saveGridOpt, saveCB);
518
+ n.subGridOpts = (saveGridOpt ? listOrOpt : { children: listOrOpt });
519
+ delete n.subGrid;
520
+ }
521
+ }
522
+ delete n.el;
523
+ });
524
+ // check if save entire grid options (needed for recursive) + children...
525
+ if (saveGridOpt) {
526
+ let o = Utils.cloneDeep(this.opts);
527
+ // delete default values that will be recreated on launch
528
+ if (o.marginBottom === o.marginTop && o.marginRight === o.marginLeft && o.marginTop === o.marginRight) {
529
+ o.margin = o.marginTop;
530
+ delete o.marginTop;
531
+ delete o.marginRight;
532
+ delete o.marginBottom;
533
+ delete o.marginLeft;
534
+ }
535
+ if (o.rtl === (this.el.style.direction === 'rtl')) {
536
+ o.rtl = 'auto';
537
+ }
538
+ if (this._isAutoCellHeight) {
539
+ o.cellHeight = 'auto';
540
+ }
541
+ if (this._autoColumn) {
542
+ o.column = 'auto';
543
+ delete o.disableOneColumnMode;
544
+ }
545
+ const origShow = o._alwaysShowResizeHandle;
546
+ delete o._alwaysShowResizeHandle;
547
+ if (origShow !== undefined) {
548
+ o.alwaysShowResizeHandle = origShow;
549
+ }
550
+ else {
551
+ delete o.alwaysShowResizeHandle;
552
+ }
553
+ Utils.removeInternalAndSame(o, gridDefaults);
554
+ o.children = list;
555
+ return o;
556
+ }
557
+ return list;
558
+ }
559
+ /**
560
+ * load the widgets from a list. This will call update() on each (matching by id) or add/remove widgets that are not there.
561
+ *
562
+ * @param layout list of widgets definition to update/create
563
+ * @param addAndRemove boolean (default true) or callback method can be passed to control if and how missing widgets can be added/removed, giving
564
+ * the user control of insertion.
565
+ *
566
+ * @example
567
+ * see http://gridstackjs.com/demo/serialization.html
568
+ **/
569
+ load(layout, addRemove = GridStack.addRemoveCB || true) {
570
+ let items = GridStack.Utils.sort([...layout], -1, this._prevColumn || this.getColumn()); // make copy before we mod/sort
571
+ this._insertNotAppend = true; // since create in reverse order...
572
+ // if we're loading a layout into for example 1 column (_prevColumn is set only when going to 1) and items don't fit, make sure to save
573
+ // the original wanted layout so we can scale back up correctly #1471
574
+ if (this._prevColumn && this._prevColumn !== this.opts.column && items.some(n => (n.x + n.w) > this.opts.column)) {
575
+ this._ignoreLayoutsNodeChange = true; // skip layout update
576
+ this.engine.cacheLayout(items, this._prevColumn, true);
577
+ }
578
+ // if given a different callback, temporally set it as global option so creating will use it
579
+ const prevCB = GridStack.addRemoveCB;
580
+ if (typeof (addRemove) === 'function')
581
+ GridStack.addRemoveCB = addRemove;
582
+ let removed = [];
583
+ this.batchUpdate();
584
+ // see if any items are missing from new layout and need to be removed first
585
+ if (addRemove) {
586
+ let copyNodes = [...this.engine.nodes]; // don't loop through array you modify
587
+ copyNodes.forEach(n => {
588
+ let item = items.find(w => n.id === w.id);
589
+ if (!item) {
590
+ if (GridStack.addRemoveCB)
591
+ GridStack.addRemoveCB(this.el, n, false, false);
592
+ removed.push(n); // batch keep track
593
+ this.removeWidget(n.el, true, false);
594
+ }
595
+ });
596
+ }
597
+ // now add/update the widgets
598
+ items.forEach(w => {
599
+ let item = (w.id || w.id === 0) ? this.engine.nodes.find(n => n.id === w.id) : undefined;
600
+ if (item) {
601
+ this.update(item.el, w);
602
+ if (w.subGridOpts?.children) { // update any sub grid as well
603
+ let sub = item.el.querySelector('.grid-stack');
604
+ if (sub && sub.gridstack) {
605
+ sub.gridstack.load(w.subGridOpts.children); // TODO: support updating grid options ?
606
+ this._insertNotAppend = true; // got reset by above call
607
+ }
608
+ }
609
+ }
610
+ else if (addRemove) {
611
+ this.addWidget(w);
612
+ }
613
+ });
614
+ this.engine.removedNodes = removed;
615
+ this.batchUpdate(false);
616
+ // after commit, clear that flag
617
+ delete this._ignoreLayoutsNodeChange;
618
+ delete this._insertNotAppend;
619
+ prevCB ? GridStack.addRemoveCB = prevCB : delete GridStack.addRemoveCB;
620
+ return this;
621
+ }
622
+ /**
623
+ * use before calling a bunch of `addWidget()` to prevent un-necessary relayouts in between (more efficient)
624
+ * and get a single event callback. You will see no changes until `batchUpdate(false)` is called.
625
+ */
626
+ batchUpdate(flag = true) {
627
+ this.engine.batchUpdate(flag);
628
+ if (!flag) {
629
+ this._triggerRemoveEvent();
630
+ this._triggerAddEvent();
631
+ this._triggerChangeEvent();
632
+ }
633
+ return this;
634
+ }
635
+ /**
636
+ * Gets current cell height.
637
+ */
638
+ getCellHeight(forcePixel = false) {
639
+ if (this.opts.cellHeight && this.opts.cellHeight !== 'auto' &&
640
+ (!forcePixel || !this.opts.cellHeightUnit || this.opts.cellHeightUnit === 'px')) {
641
+ return this.opts.cellHeight;
642
+ }
643
+ // else get first cell height
644
+ let el = this.el.querySelector('.' + this.opts.itemClass);
645
+ if (el) {
646
+ let height = Utils.toNumber(el.getAttribute('gs-h'));
647
+ return Math.round(el.offsetHeight / height);
648
+ }
649
+ // else do entire grid and # of rows (but doesn't work if min-height is the actual constrain)
650
+ let rows = parseInt(this.el.getAttribute('gs-current-row'));
651
+ return rows ? Math.round(this.el.getBoundingClientRect().height / rows) : this.opts.cellHeight;
652
+ }
653
+ /**
654
+ * Update current cell height - see `GridStackOptions.cellHeight` for format.
655
+ * This method rebuilds an internal CSS style sheet.
656
+ * Note: You can expect performance issues if call this method too often.
657
+ *
658
+ * @param val the cell height. If not passed (undefined), cells content will be made square (match width minus margin),
659
+ * if pass 0 the CSS will be generated by the application instead.
660
+ * @param update (Optional) if false, styles will not be updated
661
+ *
662
+ * @example
663
+ * grid.cellHeight(100); // same as 100px
664
+ * grid.cellHeight('70px');
665
+ * grid.cellHeight(grid.cellWidth() * 1.2);
666
+ */
667
+ cellHeight(val, update = true) {
668
+ // if not called internally, check if we're changing mode
669
+ if (update && val !== undefined) {
670
+ if (this._isAutoCellHeight !== (val === 'auto')) {
671
+ this._isAutoCellHeight = (val === 'auto');
672
+ this._updateWindowResizeEvent();
673
+ }
674
+ }
675
+ if (val === 'initial' || val === 'auto') {
676
+ val = undefined;
677
+ }
678
+ // make item content be square
679
+ if (val === undefined) {
680
+ let marginDiff = -this.opts.marginRight - this.opts.marginLeft
681
+ + this.opts.marginTop + this.opts.marginBottom;
682
+ val = this.cellWidth() + marginDiff;
683
+ }
684
+ let data = Utils.parseHeight(val);
685
+ if (this.opts.cellHeightUnit === data.unit && this.opts.cellHeight === data.h) {
686
+ return this;
687
+ }
688
+ this.opts.cellHeightUnit = data.unit;
689
+ this.opts.cellHeight = data.h;
690
+ if (update) {
691
+ this._updateStyles(true); // true = force re-create for current # of rows
692
+ }
693
+ return this;
694
+ }
695
+ /** Gets current cell width. */
696
+ cellWidth() {
697
+ return this._widthOrContainer() / this.getColumn();
698
+ }
699
+ /** return our expected width (or parent) for 1 column check */
700
+ _widthOrContainer() {
701
+ // use `offsetWidth` or `clientWidth` (no scrollbar) ?
702
+ // https://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively
703
+ return (this.el.clientWidth || this.el.parentElement.clientWidth || window.innerWidth);
704
+ }
705
+ /** re-layout grid items to reclaim any empty space */
706
+ compact() {
707
+ this.engine.compact();
708
+ this._triggerChangeEvent();
709
+ return this;
710
+ }
711
+ /**
712
+ * set the number of columns in the grid. Will update existing widgets to conform to new number of columns,
713
+ * as well as cache the original layout so you can revert back to previous positions without loss.
714
+ * Requires `gridstack-extra.css` or `gridstack-extra.min.css` for [2-11],
715
+ * else you will need to generate correct CSS (see https://github.com/gridstack/gridstack.js#change-grid-columns)
716
+ * @param column - Integer > 0 (default 12).
717
+ * @param layout specify the type of re-layout that will happen (position, size, etc...).
718
+ * Note: items will never be outside of the current column boundaries. default (moveScale). Ignored for 1 column
719
+ */
720
+ column(column, layout = 'moveScale') {
721
+ if (column < 1 || this.opts.column === column)
722
+ return this;
723
+ let oldColumn = this.getColumn();
724
+ // if we go into 1 column mode (which happens if we're sized less than minW unless disableOneColumnMode is on)
725
+ // then remember the original columns so we can restore.
726
+ if (column === 1) {
727
+ this._prevColumn = oldColumn;
728
+ }
729
+ else {
730
+ delete this._prevColumn;
731
+ }
732
+ this.el.classList.remove('grid-stack-' + oldColumn);
733
+ this.el.classList.add('grid-stack-' + column);
734
+ this.opts.column = this.engine.column = column;
735
+ // update the items now - see if the dom order nodes should be passed instead (else default to current list)
736
+ let domNodes;
737
+ if (column === 1 && this.opts.oneColumnModeDomSort) {
738
+ domNodes = [];
739
+ this.getGridItems().forEach(el => {
740
+ if (el.gridstackNode) {
741
+ domNodes.push(el.gridstackNode);
742
+ }
743
+ });
744
+ if (!domNodes.length) {
745
+ domNodes = undefined;
746
+ }
747
+ }
748
+ this.engine.updateNodeWidths(oldColumn, column, domNodes, layout);
749
+ if (this._isAutoCellHeight)
750
+ this.cellHeight();
751
+ // and trigger our event last...
752
+ this._ignoreLayoutsNodeChange = true; // skip layout update
753
+ this._triggerChangeEvent();
754
+ delete this._ignoreLayoutsNodeChange;
755
+ return this;
756
+ }
757
+ /**
758
+ * get the number of columns in the grid (default 12)
759
+ */
760
+ getColumn() {
761
+ return this.opts.column;
762
+ }
763
+ /** returns an array of grid HTML elements (no placeholder) - used to iterate through our children in DOM order */
764
+ getGridItems() {
765
+ return Array.from(this.el.children)
766
+ .filter((el) => el.matches('.' + this.opts.itemClass) && !el.matches('.' + this.opts.placeholderClass));
767
+ }
768
+ /**
769
+ * Destroys a grid instance. DO NOT CALL any methods or access any vars after this as it will free up members.
770
+ * @param removeDOM if `false` grid and items HTML elements will not be removed from the DOM (Optional. Default `true`).
771
+ */
772
+ destroy(removeDOM = true) {
773
+ if (!this.el)
774
+ return; // prevent multiple calls
775
+ this._updateWindowResizeEvent(true);
776
+ this.setStatic(true, false); // permanently removes DD but don't set CSS class (we're going away)
777
+ this.setAnimation(false);
778
+ if (!removeDOM) {
779
+ this.removeAll(removeDOM);
780
+ this.el.classList.remove(this._styleSheetClass);
781
+ this.el.removeAttribute('gs-current-row');
782
+ }
783
+ else {
784
+ this.el.parentNode.removeChild(this.el);
785
+ }
786
+ this._removeStylesheet();
787
+ if (this.parentGridItem)
788
+ delete this.parentGridItem.subGrid;
789
+ delete this.parentGridItem;
790
+ delete this.opts;
791
+ delete this._placeholder;
792
+ delete this.engine;
793
+ delete this.el.gridstack; // remove circular dependency that would prevent a freeing
794
+ delete this.el;
795
+ return this;
796
+ }
797
+ /**
798
+ * enable/disable floating widgets (default: `false`) See [example](http://gridstackjs.com/demo/float.html)
799
+ */
800
+ float(val) {
801
+ if (this.opts.float !== val) {
802
+ this.opts.float = this.engine.float = val;
803
+ this._triggerChangeEvent();
804
+ }
805
+ return this;
806
+ }
807
+ /**
808
+ * get the current float mode
809
+ */
810
+ getFloat() {
811
+ return this.engine.float;
812
+ }
813
+ /**
814
+ * Get the position of the cell under a pixel on screen.
815
+ * @param position the position of the pixel to resolve in
816
+ * absolute coordinates, as an object with top and left properties
817
+ * @param useDocRelative if true, value will be based on document position vs parent position (Optional. Default false).
818
+ * Useful when grid is within `position: relative` element
819
+ *
820
+ * Returns an object with properties `x` and `y` i.e. the column and row in the grid.
821
+ */
822
+ getCellFromPixel(position, useDocRelative = false) {
823
+ let box = this.el.getBoundingClientRect();
824
+ // console.log(`getBoundingClientRect left: ${box.left} top: ${box.top} w: ${box.w} h: ${box.h}`)
825
+ let containerPos;
826
+ if (useDocRelative) {
827
+ containerPos = { top: box.top + document.documentElement.scrollTop, left: box.left };
828
+ // console.log(`getCellFromPixel scrollTop: ${document.documentElement.scrollTop}`)
829
+ }
830
+ else {
831
+ containerPos = { top: this.el.offsetTop, left: this.el.offsetLeft };
832
+ // console.log(`getCellFromPixel offsetTop: ${containerPos.left} offsetLeft: ${containerPos.top}`)
833
+ }
834
+ let relativeLeft = position.left - containerPos.left;
835
+ let relativeTop = position.top - containerPos.top;
836
+ let columnWidth = (box.width / this.getColumn());
837
+ let rowHeight = (box.height / parseInt(this.el.getAttribute('gs-current-row')));
838
+ return { x: Math.floor(relativeLeft / columnWidth), y: Math.floor(relativeTop / rowHeight) };
839
+ }
840
+ /** returns the current number of rows, which will be at least `minRow` if set */
841
+ getRow() {
842
+ return Math.max(this.engine.getRow(), this.opts.minRow);
843
+ }
844
+ /**
845
+ * Checks if specified area is empty.
846
+ * @param x the position x.
847
+ * @param y the position y.
848
+ * @param w the width of to check
849
+ * @param h the height of to check
850
+ */
851
+ isAreaEmpty(x, y, w, h) {
852
+ return this.engine.isAreaEmpty(x, y, w, h);
853
+ }
854
+ /**
855
+ * If you add elements to your grid by hand, you have to tell gridstack afterwards to make them widgets.
856
+ * If you want gridstack to add the elements for you, use `addWidget()` instead.
857
+ * Makes the given element a widget and returns it.
858
+ * @param els widget or single selector to convert.
859
+ *
860
+ * @example
861
+ * let grid = GridStack.init();
862
+ * grid.el.appendChild('<div id="gsi-1" gs-w="3"></div>');
863
+ * grid.makeWidget('#gsi-1');
864
+ */
865
+ makeWidget(els) {
866
+ let el = GridStack.getElement(els);
867
+ this._prepareElement(el, true);
868
+ this._updateContainerHeight();
869
+ this._triggerAddEvent();
870
+ this._triggerChangeEvent();
871
+ return el;
872
+ }
873
+ /**
874
+ * Event handler that extracts our CustomEvent data out automatically for receiving custom
875
+ * notifications (see doc for supported events)
876
+ * @param name of the event (see possible values) or list of names space separated
877
+ * @param callback function called with event and optional second/third param
878
+ * (see README documentation for each signature).
879
+ *
880
+ * @example
881
+ * grid.on('added', function(e, items) { log('added ', items)} );
882
+ * or
883
+ * grid.on('added removed change', function(e, items) { log(e.type, items)} );
884
+ *
885
+ * Note: in some cases it is the same as calling native handler and parsing the event.
886
+ * grid.el.addEventListener('added', function(event) { log('added ', event.detail)} );
887
+ *
888
+ */
889
+ on(name, callback) {
890
+ // check for array of names being passed instead
891
+ if (name.indexOf(' ') !== -1) {
892
+ let names = name.split(' ');
893
+ names.forEach(name => this.on(name, callback));
894
+ return this;
895
+ }
896
+ if (name === 'change' || name === 'added' || name === 'removed' || name === 'enable' || name === 'disable') {
897
+ // native CustomEvent handlers - cash the generic handlers so we can easily remove
898
+ let noData = (name === 'enable' || name === 'disable');
899
+ if (noData) {
900
+ this._gsEventHandler[name] = (event) => callback(event);
901
+ }
902
+ else {
903
+ this._gsEventHandler[name] = (event) => callback(event, event.detail);
904
+ }
905
+ this.el.addEventListener(name, this._gsEventHandler[name]);
906
+ }
907
+ else if (name === 'drag' || name === 'dragstart' || name === 'dragstop' || name === 'resizestart' || name === 'resize' || name === 'resizestop' || name === 'dropped') {
908
+ // drag&drop stop events NEED to be call them AFTER we update node attributes so handle them ourself.
909
+ // do same for start event to make it easier...
910
+ this._gsEventHandler[name] = callback;
911
+ }
912
+ else {
913
+ console.log('GridStack.on(' + name + ') event not supported, but you can still use $(".grid-stack").on(...) while jquery-ui is still used internally.');
914
+ }
915
+ return this;
916
+ }
917
+ /**
918
+ * unsubscribe from the 'on' event below
919
+ * @param name of the event (see possible values)
920
+ */
921
+ off(name) {
922
+ // check for array of names being passed instead
923
+ if (name.indexOf(' ') !== -1) {
924
+ let names = name.split(' ');
925
+ names.forEach(name => this.off(name));
926
+ return this;
927
+ }
928
+ if (name === 'change' || name === 'added' || name === 'removed' || name === 'enable' || name === 'disable') {
929
+ // remove native CustomEvent handlers
930
+ if (this._gsEventHandler[name]) {
931
+ this.el.removeEventListener(name, this._gsEventHandler[name]);
932
+ }
933
+ }
934
+ delete this._gsEventHandler[name];
935
+ return this;
936
+ }
937
+ /**
938
+ * Removes widget from the grid.
939
+ * @param el widget or selector to modify
940
+ * @param removeDOM if `false` DOM element won't be removed from the tree (Default? true).
941
+ * @param triggerEvent if `false` (quiet mode) element will not be added to removed list and no 'removed' callbacks will be called (Default? true).
942
+ */
943
+ removeWidget(els, removeDOM = true, triggerEvent = true) {
944
+ GridStack.getElements(els).forEach(el => {
945
+ if (el.parentElement && el.parentElement !== this.el)
946
+ return; // not our child!
947
+ let node = el.gridstackNode;
948
+ // For Meteor support: https://github.com/gridstack/gridstack.js/pull/272
949
+ if (!node) {
950
+ node = this.engine.nodes.find(n => el === n.el);
951
+ }
952
+ if (!node)
953
+ return;
954
+ // remove our DOM data (circular link) and drag&drop permanently
955
+ delete el.gridstackNode;
956
+ this._removeDD(el);
957
+ this.engine.removeNode(node, removeDOM, triggerEvent);
958
+ if (removeDOM && el.parentElement) {
959
+ el.remove(); // in batch mode engine.removeNode doesn't call back to remove DOM
960
+ }
961
+ });
962
+ if (triggerEvent) {
963
+ this._triggerRemoveEvent();
964
+ this._triggerChangeEvent();
965
+ }
966
+ return this;
967
+ }
968
+ /**
969
+ * Removes all widgets from the grid.
970
+ * @param removeDOM if `false` DOM elements won't be removed from the tree (Default? `true`).
971
+ */
972
+ removeAll(removeDOM = true) {
973
+ // always remove our DOM data (circular link) before list gets emptied and drag&drop permanently
974
+ this.engine.nodes.forEach(n => {
975
+ delete n.el.gridstackNode;
976
+ this._removeDD(n.el);
977
+ });
978
+ this.engine.removeAll(removeDOM);
979
+ this._triggerRemoveEvent();
980
+ return this;
981
+ }
982
+ /**
983
+ * Toggle the grid animation state. Toggles the `grid-stack-animate` class.
984
+ * @param doAnimate if true the grid will animate.
985
+ */
986
+ setAnimation(doAnimate) {
987
+ if (doAnimate) {
988
+ this.el.classList.add('grid-stack-animate');
989
+ }
990
+ else {
991
+ this.el.classList.remove('grid-stack-animate');
992
+ }
993
+ return this;
994
+ }
995
+ /**
996
+ * Toggle the grid static state, which permanently removes/add Drag&Drop support, unlike disable()/enable() that just turns it off/on.
997
+ * Also toggle the grid-stack-static class.
998
+ * @param val if true the grid become static.
999
+ * @param updateClass true (default) if css class gets updated
1000
+ * @param recurse true (default) if sub-grids also get updated
1001
+ */
1002
+ setStatic(val, updateClass = true, recurse = true) {
1003
+ if (this.opts.staticGrid === val)
1004
+ return this;
1005
+ this.opts.staticGrid = val;
1006
+ this._setupRemoveDrop();
1007
+ this._setupAcceptWidget();
1008
+ this.engine.nodes.forEach(n => {
1009
+ this._prepareDragDropByNode(n); // either delete or init Drag&drop
1010
+ if (n.subGrid && recurse)
1011
+ n.subGrid.setStatic(val, updateClass, recurse);
1012
+ });
1013
+ if (updateClass) {
1014
+ this._setStaticClass();
1015
+ }
1016
+ return this;
1017
+ }
1018
+ /**
1019
+ * Updates widget position/size and other info. Note: if you need to call this on all nodes, use load() instead which will update what changed.
1020
+ * @param els widget or selector of objects to modify (note: setting the same x,y for multiple items will be indeterministic and likely unwanted)
1021
+ * @param opt new widget options (x,y,w,h, etc..). Only those set will be updated.
1022
+ */
1023
+ update(els, opt) {
1024
+ // support legacy call for now ?
1025
+ if (arguments.length > 2) {
1026
+ console.warn('gridstack.ts: `update(el, x, y, w, h)` is deprecated. Use `update(el, {x, w, content, ...})`. It will be removed soon');
1027
+ // eslint-disable-next-line prefer-rest-params
1028
+ let a = arguments, i = 1;
1029
+ opt = { x: a[i++], y: a[i++], w: a[i++], h: a[i++] };
1030
+ return this.update(els, opt);
1031
+ }
1032
+ GridStack.getElements(els).forEach(el => {
1033
+ if (!el || !el.gridstackNode)
1034
+ return;
1035
+ let n = el.gridstackNode;
1036
+ let w = Utils.cloneDeep(opt); // make a copy we can modify in case they re-use it or multiple items
1037
+ delete w.autoPosition;
1038
+ // move/resize widget if anything changed
1039
+ let keys = ['x', 'y', 'w', 'h'];
1040
+ let m;
1041
+ if (keys.some(k => w[k] !== undefined && w[k] !== n[k])) {
1042
+ m = {};
1043
+ keys.forEach(k => {
1044
+ m[k] = (w[k] !== undefined) ? w[k] : n[k];
1045
+ delete w[k];
1046
+ });
1047
+ }
1048
+ // for a move as well IFF there is any min/max fields set
1049
+ if (!m && (w.minW || w.minH || w.maxW || w.maxH)) {
1050
+ m = {}; // will use node position but validate values
1051
+ }
1052
+ // check for content changing
1053
+ if (w.content) {
1054
+ let sub = el.querySelector('.grid-stack-item-content');
1055
+ if (sub && sub.innerHTML !== w.content) {
1056
+ sub.innerHTML = w.content;
1057
+ }
1058
+ delete w.content;
1059
+ }
1060
+ // any remaining fields are assigned, but check for dragging changes, resize constrain
1061
+ let changed = false;
1062
+ let ddChanged = false;
1063
+ for (const key in w) {
1064
+ if (key[0] !== '_' && n[key] !== w[key]) {
1065
+ n[key] = w[key];
1066
+ changed = true;
1067
+ ddChanged = ddChanged || (!this.opts.staticGrid && (key === 'noResize' || key === 'noMove' || key === 'locked'));
1068
+ }
1069
+ }
1070
+ Utils.sanitizeMinMax(n);
1071
+ // finally move the widget
1072
+ if (m) {
1073
+ this.engine.cleanNodes()
1074
+ .beginUpdate(n)
1075
+ .moveNode(n, m);
1076
+ this._updateContainerHeight();
1077
+ this._triggerChangeEvent();
1078
+ this.engine.endUpdate();
1079
+ }
1080
+ if (changed) { // move will only update x,y,w,h so update the rest too
1081
+ this._writeAttr(el, n);
1082
+ }
1083
+ if (ddChanged) {
1084
+ this._prepareDragDropByNode(n);
1085
+ }
1086
+ });
1087
+ return this;
1088
+ }
1089
+ /**
1090
+ * Updates the margins which will set all 4 sides at once - see `GridStackOptions.margin` for format options (CSS string format of 1,2,4 values or single number).
1091
+ * @param value margin value
1092
+ */
1093
+ margin(value) {
1094
+ let isMultiValue = (typeof value === 'string' && value.split(' ').length > 1);
1095
+ // check if we can skip re-creating our CSS file... won't check if multi values (too much hassle)
1096
+ if (!isMultiValue) {
1097
+ let data = Utils.parseHeight(value);
1098
+ if (this.opts.marginUnit === data.unit && this.opts.margin === data.h)
1099
+ return;
1100
+ }
1101
+ // re-use existing margin handling
1102
+ this.opts.margin = value;
1103
+ this.opts.marginTop = this.opts.marginBottom = this.opts.marginLeft = this.opts.marginRight = undefined;
1104
+ this._initMargin();
1105
+ this._updateStyles(true); // true = force re-create
1106
+ return this;
1107
+ }
1108
+ /** returns current margin number value (undefined if 4 sides don't match) */
1109
+ getMargin() { return this.opts.margin; }
1110
+ /**
1111
+ * Returns true if the height of the grid will be less than the vertical
1112
+ * constraint. Always returns true if grid doesn't have height constraint.
1113
+ * @param node contains x,y,w,h,auto-position options
1114
+ *
1115
+ * @example
1116
+ * if (grid.willItFit(newWidget)) {
1117
+ * grid.addWidget(newWidget);
1118
+ * } else {
1119
+ * alert('Not enough free space to place the widget');
1120
+ * }
1121
+ */
1122
+ willItFit(node) {
1123
+ // support legacy call for now
1124
+ if (arguments.length > 1) {
1125
+ console.warn('gridstack.ts: `willItFit(x,y,w,h,autoPosition)` is deprecated. Use `willItFit({x, y,...})`. It will be removed soon');
1126
+ // eslint-disable-next-line prefer-rest-params
1127
+ let a = arguments, i = 0, w = { x: a[i++], y: a[i++], w: a[i++], h: a[i++], autoPosition: a[i++] };
1128
+ return this.willItFit(w);
1129
+ }
1130
+ return this.engine.willItFit(node);
1131
+ }
1132
+ /** @internal */
1133
+ _triggerChangeEvent() {
1134
+ if (this.engine.batchMode)
1135
+ return this;
1136
+ let elements = this.engine.getDirtyNodes(true); // verify they really changed
1137
+ if (elements && elements.length) {
1138
+ if (!this._ignoreLayoutsNodeChange) {
1139
+ this.engine.layoutsNodesChange(elements);
1140
+ }
1141
+ this._triggerEvent('change', elements);
1142
+ }
1143
+ this.engine.saveInitial(); // we called, now reset initial values & dirty flags
1144
+ return this;
1145
+ }
1146
+ /** @internal */
1147
+ _triggerAddEvent() {
1148
+ if (this.engine.batchMode)
1149
+ return this;
1150
+ if (this.engine.addedNodes?.length) {
1151
+ if (!this._ignoreLayoutsNodeChange) {
1152
+ this.engine.layoutsNodesChange(this.engine.addedNodes);
1153
+ }
1154
+ // prevent added nodes from also triggering 'change' event (which is called next)
1155
+ this.engine.addedNodes.forEach(n => { delete n._dirty; });
1156
+ this._triggerEvent('added', this.engine.addedNodes);
1157
+ this.engine.addedNodes = [];
1158
+ }
1159
+ return this;
1160
+ }
1161
+ /** @internal */
1162
+ _triggerRemoveEvent() {
1163
+ if (this.engine.batchMode)
1164
+ return this;
1165
+ if (this.engine.removedNodes?.length) {
1166
+ this._triggerEvent('removed', this.engine.removedNodes);
1167
+ this.engine.removedNodes = [];
1168
+ }
1169
+ return this;
1170
+ }
1171
+ /** @internal */
1172
+ _triggerEvent(type, data) {
1173
+ let event = data ? new CustomEvent(type, { bubbles: false, detail: data }) : new Event(type);
1174
+ this.el.dispatchEvent(event);
1175
+ return this;
1176
+ }
1177
+ /** @internal called to delete the current dynamic style sheet used for our layout */
1178
+ _removeStylesheet() {
1179
+ if (this._styles) {
1180
+ Utils.removeStylesheet(this._styleSheetClass);
1181
+ delete this._styles;
1182
+ }
1183
+ return this;
1184
+ }
1185
+ /** @internal updated/create the CSS styles for row based layout and initial margin setting */
1186
+ _updateStyles(forceUpdate = false, maxH) {
1187
+ // call to delete existing one if we change cellHeight / margin
1188
+ if (forceUpdate) {
1189
+ this._removeStylesheet();
1190
+ }
1191
+ if (!maxH)
1192
+ maxH = this.getRow();
1193
+ this._updateContainerHeight();
1194
+ // if user is telling us they will handle the CSS themselves by setting heights to 0. Do we need this opts really ??
1195
+ if (this.opts.cellHeight === 0) {
1196
+ return this;
1197
+ }
1198
+ let cellHeight = this.opts.cellHeight;
1199
+ let cellHeightUnit = this.opts.cellHeightUnit;
1200
+ let prefix = `.${this._styleSheetClass} > .${this.opts.itemClass}`;
1201
+ // create one as needed
1202
+ if (!this._styles) {
1203
+ // insert style to parent (instead of 'head' by default) to support WebComponent
1204
+ let styleLocation = this.opts.styleInHead ? undefined : this.el.parentNode;
1205
+ this._styles = Utils.createStylesheet(this._styleSheetClass, styleLocation, {
1206
+ nonce: this.opts.nonce,
1207
+ });
1208
+ if (!this._styles)
1209
+ return this;
1210
+ this._styles._max = 0;
1211
+ // these are done once only
1212
+ Utils.addCSSRule(this._styles, prefix, `min-height: ${cellHeight}${cellHeightUnit}`);
1213
+ // content margins
1214
+ let top = this.opts.marginTop + this.opts.marginUnit;
1215
+ let bottom = this.opts.marginBottom + this.opts.marginUnit;
1216
+ let right = this.opts.marginRight + this.opts.marginUnit;
1217
+ let left = this.opts.marginLeft + this.opts.marginUnit;
1218
+ let content = `${prefix} > .grid-stack-item-content`;
1219
+ let placeholder = `.${this._styleSheetClass} > .grid-stack-placeholder > .placeholder-content`;
1220
+ Utils.addCSSRule(this._styles, content, `top: ${top}; right: ${right}; bottom: ${bottom}; left: ${left};`);
1221
+ Utils.addCSSRule(this._styles, placeholder, `top: ${top}; right: ${right}; bottom: ${bottom}; left: ${left};`);
1222
+ // resize handles offset (to match margin)
1223
+ Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-ne`, `right: ${right}`);
1224
+ Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-e`, `right: ${right}`);
1225
+ Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-se`, `right: ${right}; bottom: ${bottom}`);
1226
+ Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-nw`, `left: ${left}`);
1227
+ Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-w`, `left: ${left}`);
1228
+ Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-sw`, `left: ${left}; bottom: ${bottom}`);
1229
+ }
1230
+ // now update the height specific fields
1231
+ maxH = maxH || this._styles._max;
1232
+ if (maxH > this._styles._max) {
1233
+ let getHeight = (rows) => (cellHeight * rows) + cellHeightUnit;
1234
+ for (let i = this._styles._max + 1; i <= maxH; i++) { // start at 1
1235
+ let h = getHeight(i);
1236
+ Utils.addCSSRule(this._styles, `${prefix}[gs-y="${i - 1}"]`, `top: ${getHeight(i - 1)}`); // start at 0
1237
+ Utils.addCSSRule(this._styles, `${prefix}[gs-h="${i}"]`, `height: ${h}`);
1238
+ }
1239
+ this._styles._max = maxH;
1240
+ }
1241
+ return this;
1242
+ }
1243
+ /** @internal */
1244
+ _updateContainerHeight() {
1245
+ if (!this.engine || this.engine.batchMode)
1246
+ return this;
1247
+ let row = this.getRow() + this._extraDragRow; // checks for minRow already
1248
+ // check for css min height
1249
+ // Note: we don't handle %,rem correctly so comment out, beside we don't need need to create un-necessary
1250
+ // rows as the CSS will make us bigger than our set height if needed... not sure why we had this.
1251
+ // let cssMinHeight = parseInt(getComputedStyle(this.el)['min-height']);
1252
+ // if (cssMinHeight > 0) {
1253
+ // let minRow = Math.round(cssMinHeight / this.getCellHeight(true));
1254
+ // if (row < minRow) {
1255
+ // row = minRow;
1256
+ // }
1257
+ // }
1258
+ this.el.setAttribute('gs-current-row', String(row));
1259
+ if (row === 0) {
1260
+ this.el.style.removeProperty('min-height');
1261
+ return this;
1262
+ }
1263
+ let cellHeight = this.opts.cellHeight;
1264
+ let unit = this.opts.cellHeightUnit;
1265
+ if (!cellHeight)
1266
+ return this;
1267
+ this.el.style.minHeight = row * cellHeight + unit;
1268
+ return this;
1269
+ }
1270
+ /** @internal */
1271
+ _prepareElement(el, triggerAddEvent = false, node) {
1272
+ el.classList.add(this.opts.itemClass);
1273
+ node = node || this._readAttr(el);
1274
+ el.gridstackNode = node;
1275
+ node.el = el;
1276
+ node.grid = this;
1277
+ let copy = { ...node };
1278
+ node = this.engine.addNode(node, triggerAddEvent);
1279
+ // write node attr back in case there was collision or we have to fix bad values during addNode()
1280
+ if (!Utils.same(node, copy)) {
1281
+ this._writeAttr(el, node);
1282
+ }
1283
+ this._prepareDragDropByNode(node);
1284
+ return this;
1285
+ }
1286
+ /** @internal call to write position x,y,w,h attributes back to element */
1287
+ _writePosAttr(el, n) {
1288
+ if (n.x !== undefined && n.x !== null) {
1289
+ el.setAttribute('gs-x', String(n.x));
1290
+ }
1291
+ if (n.y !== undefined && n.y !== null) {
1292
+ el.setAttribute('gs-y', String(n.y));
1293
+ }
1294
+ n.w > 1 ? el.setAttribute('gs-w', String(n.w)) : el.removeAttribute('gs-w');
1295
+ n.h > 1 ? el.setAttribute('gs-h', String(n.h)) : el.removeAttribute('gs-h');
1296
+ return this;
1297
+ }
1298
+ /** @internal call to write any default attributes back to element */
1299
+ _writeAttr(el, node) {
1300
+ if (!node)
1301
+ return this;
1302
+ this._writePosAttr(el, node);
1303
+ let attrs /*: GridStackWidget but strings */ = {
1304
+ autoPosition: 'gs-auto-position',
1305
+ noResize: 'gs-no-resize',
1306
+ noMove: 'gs-no-move',
1307
+ locked: 'gs-locked',
1308
+ id: 'gs-id',
1309
+ };
1310
+ for (const key in attrs) {
1311
+ if (node[key]) { // 0 is valid for x,y only but done above already and not in list anyway
1312
+ el.setAttribute(attrs[key], String(node[key]));
1313
+ }
1314
+ else {
1315
+ el.removeAttribute(attrs[key]);
1316
+ }
1317
+ }
1318
+ return this;
1319
+ }
1320
+ /** @internal call to read any default attributes from element */
1321
+ _readAttr(el) {
1322
+ let n = {};
1323
+ n.x = Utils.toNumber(el.getAttribute('gs-x'));
1324
+ n.y = Utils.toNumber(el.getAttribute('gs-y'));
1325
+ n.w = Utils.toNumber(el.getAttribute('gs-w'));
1326
+ n.h = Utils.toNumber(el.getAttribute('gs-h'));
1327
+ if (!(n.w > 1))
1328
+ el.removeAttribute('gs-w');
1329
+ if (!(n.h > 1))
1330
+ el.removeAttribute('gs-h');
1331
+ n.autoPosition = Utils.toBool(el.getAttribute('gs-auto-position'));
1332
+ n.noResize = Utils.toBool(el.getAttribute('gs-no-resize'));
1333
+ n.noMove = Utils.toBool(el.getAttribute('gs-no-move'));
1334
+ n.locked = Utils.toBool(el.getAttribute('gs-locked'));
1335
+ n.id = el.getAttribute('gs-id');
1336
+ // read but never written out
1337
+ n.maxW = Utils.toNumber(el.getAttribute('gs-max-w'));
1338
+ if (n.maxW)
1339
+ el.removeAttribute('gs-max-w');
1340
+ n.minW = Utils.toNumber(el.getAttribute('gs-min-w'));
1341
+ if (n.minW)
1342
+ el.removeAttribute('gs-min-w');
1343
+ n.maxH = Utils.toNumber(el.getAttribute('gs-max-h'));
1344
+ if (n.maxH)
1345
+ el.removeAttribute('gs-max-h');
1346
+ n.minH = Utils.toNumber(el.getAttribute('gs-min-h'));
1347
+ if (n.minH)
1348
+ el.removeAttribute('gs-min-h');
1349
+ // remove any key not found (null or false which is default)
1350
+ for (const key in n) {
1351
+ if (!n.hasOwnProperty(key))
1352
+ return;
1353
+ if (!n[key] && n[key] !== 0) { // 0 can be valid value (x,y only really)
1354
+ delete n[key];
1355
+ }
1356
+ }
1357
+ return n;
1358
+ }
1359
+ /** @internal */
1360
+ _setStaticClass() {
1361
+ let classes = ['grid-stack-static'];
1362
+ if (this.opts.staticGrid) {
1363
+ this.el.classList.add(...classes);
1364
+ this.el.setAttribute('gs-static', 'true');
1365
+ }
1366
+ else {
1367
+ this.el.classList.remove(...classes);
1368
+ this.el.removeAttribute('gs-static');
1369
+ }
1370
+ return this;
1371
+ }
1372
+ /**
1373
+ * called when we are being resized by the window - check if the one Column Mode needs to be turned on/off
1374
+ * and remember the prev columns we used, or get our count from parent, as well as check for auto cell height (square)
1375
+ */
1376
+ onParentResize() {
1377
+ if (!this.el || !this.el.clientWidth)
1378
+ return; // return if we're gone or no size yet (will get called again)
1379
+ let changedColumn = false;
1380
+ // see if we're nested and take our column count from our parent....
1381
+ if (this._autoColumn && this.parentGridItem) {
1382
+ if (this.opts.column !== this.parentGridItem.w) {
1383
+ changedColumn = true;
1384
+ this.column(this.parentGridItem.w, 'none');
1385
+ }
1386
+ }
1387
+ else {
1388
+ // else check for 1 column in/out behavior
1389
+ let oneColumn = !this.opts.disableOneColumnMode && this.el.clientWidth <= this.opts.oneColumnSize;
1390
+ if ((this.opts.column === 1) !== oneColumn) {
1391
+ changedColumn = true;
1392
+ if (this.opts.animate) {
1393
+ this.setAnimation(false);
1394
+ } // 1 <-> 12 is too radical, turn off animation
1395
+ this.column(oneColumn ? 1 : this._prevColumn);
1396
+ if (this.opts.animate) {
1397
+ this.setAnimation(true);
1398
+ }
1399
+ }
1400
+ }
1401
+ // make the cells content square again
1402
+ if (this._isAutoCellHeight) {
1403
+ if (!changedColumn && this.opts.cellHeightThrottle) {
1404
+ if (!this._cellHeightThrottle) {
1405
+ this._cellHeightThrottle = Utils.throttle(() => this.cellHeight(), this.opts.cellHeightThrottle);
1406
+ }
1407
+ this._cellHeightThrottle();
1408
+ }
1409
+ else {
1410
+ // immediate update if we've changed column count or have no threshold
1411
+ this.cellHeight();
1412
+ }
1413
+ }
1414
+ // finally update any nested grids
1415
+ this.engine.nodes.forEach(n => {
1416
+ if (n.subGrid)
1417
+ n.subGrid.onParentResize();
1418
+ });
1419
+ return this;
1420
+ }
1421
+ /** add or remove the window size event handler */
1422
+ _updateWindowResizeEvent(forceRemove = false) {
1423
+ // 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)
1424
+ const workTodo = (this._isAutoCellHeight || !this.opts.disableOneColumnMode) && !this.parentGridItem;
1425
+ if (!forceRemove && workTodo && !this._windowResizeBind) {
1426
+ this._windowResizeBind = this.onParentResize.bind(this); // so we can properly remove later
1427
+ window.addEventListener('resize', this._windowResizeBind);
1428
+ }
1429
+ else if ((forceRemove || !workTodo) && this._windowResizeBind) {
1430
+ window.removeEventListener('resize', this._windowResizeBind);
1431
+ delete this._windowResizeBind; // remove link to us so we can free
1432
+ }
1433
+ return this;
1434
+ }
1435
+ /** @internal convert a potential selector into actual element */
1436
+ static getElement(els = '.grid-stack-item') { return Utils.getElement(els); }
1437
+ /** @internal */
1438
+ static getElements(els = '.grid-stack-item') { return Utils.getElements(els); }
1439
+ /** @internal */
1440
+ static getGridElement(els) { return GridStack.getElement(els); }
1441
+ /** @internal */
1442
+ static getGridElements(els) { return Utils.getElements(els); }
1443
+ /** @internal initialize margin top/bottom/left/right and units */
1444
+ _initMargin() {
1445
+ let data;
1446
+ let margin = 0;
1447
+ // support passing multiple values like CSS (ex: '5px 10px 0 20px')
1448
+ let margins = [];
1449
+ if (typeof this.opts.margin === 'string') {
1450
+ margins = this.opts.margin.split(' ');
1451
+ }
1452
+ if (margins.length === 2) { // top/bot, left/right like CSS
1453
+ this.opts.marginTop = this.opts.marginBottom = margins[0];
1454
+ this.opts.marginLeft = this.opts.marginRight = margins[1];
1455
+ }
1456
+ else if (margins.length === 4) { // Clockwise like CSS
1457
+ this.opts.marginTop = margins[0];
1458
+ this.opts.marginRight = margins[1];
1459
+ this.opts.marginBottom = margins[2];
1460
+ this.opts.marginLeft = margins[3];
1461
+ }
1462
+ else {
1463
+ data = Utils.parseHeight(this.opts.margin);
1464
+ this.opts.marginUnit = data.unit;
1465
+ margin = this.opts.margin = data.h;
1466
+ }
1467
+ // see if top/bottom/left/right need to be set as well
1468
+ if (this.opts.marginTop === undefined) {
1469
+ this.opts.marginTop = margin;
1470
+ }
1471
+ else {
1472
+ data = Utils.parseHeight(this.opts.marginTop);
1473
+ this.opts.marginTop = data.h;
1474
+ delete this.opts.margin;
1475
+ }
1476
+ if (this.opts.marginBottom === undefined) {
1477
+ this.opts.marginBottom = margin;
1478
+ }
1479
+ else {
1480
+ data = Utils.parseHeight(this.opts.marginBottom);
1481
+ this.opts.marginBottom = data.h;
1482
+ delete this.opts.margin;
1483
+ }
1484
+ if (this.opts.marginRight === undefined) {
1485
+ this.opts.marginRight = margin;
1486
+ }
1487
+ else {
1488
+ data = Utils.parseHeight(this.opts.marginRight);
1489
+ this.opts.marginRight = data.h;
1490
+ delete this.opts.margin;
1491
+ }
1492
+ if (this.opts.marginLeft === undefined) {
1493
+ this.opts.marginLeft = margin;
1494
+ }
1495
+ else {
1496
+ data = Utils.parseHeight(this.opts.marginLeft);
1497
+ this.opts.marginLeft = data.h;
1498
+ delete this.opts.margin;
1499
+ }
1500
+ this.opts.marginUnit = data.unit; // in case side were spelled out, use those units instead...
1501
+ if (this.opts.marginTop === this.opts.marginBottom && this.opts.marginLeft === this.opts.marginRight && this.opts.marginTop === this.opts.marginRight) {
1502
+ this.opts.margin = this.opts.marginTop; // makes it easier to check for no-ops in setMargin()
1503
+ }
1504
+ return this;
1505
+ }
1506
+ /* ===========================================================================================
1507
+ * drag&drop methods that used to be stubbed out and implemented in dd-gridstack.ts
1508
+ * but caused loading issues in prod - see https://github.com/gridstack/gridstack.js/issues/2039
1509
+ * ===========================================================================================
1510
+ */
1511
+ /** get the global (but static to this code) DD implementation */
1512
+ static getDD() {
1513
+ return dd;
1514
+ }
1515
+ /**
1516
+ * call to setup dragging in from the outside (say toolbar), by specifying the class selection and options.
1517
+ * Called during GridStack.init() as options, but can also be called directly (last param are used) in case the toolbar
1518
+ * is dynamically create and needs to be set later.
1519
+ * @param dragIn string selector (ex: '.sidebar .grid-stack-item')
1520
+ * @param dragInOptions options - see DDDragInOpt. (default: {handle: '.grid-stack-item-content', appendTo: 'body'}
1521
+ **/
1522
+ static setupDragIn(dragIn, dragInOptions) {
1523
+ if (dragInOptions?.pause !== undefined) {
1524
+ DDManager.pauseDrag = dragInOptions.pause;
1525
+ }
1526
+ if (typeof dragIn === 'string') {
1527
+ dragInOptions = { ...dragInDefaultOptions, ...(dragInOptions || {}) };
1528
+ Utils.getElements(dragIn).forEach(el => {
1529
+ if (!dd.isDraggable(el))
1530
+ dd.dragIn(el, dragInOptions);
1531
+ });
1532
+ }
1533
+ }
1534
+ /**
1535
+ * Enables/Disables dragging by the user of specific grid element. If you want all items, and have it affect future items, use enableMove() instead. No-op for static grids.
1536
+ * IF you are looking to prevent an item from moving (due to being pushed around by another during collision) use locked property instead.
1537
+ * @param els widget or selector to modify.
1538
+ * @param val if true widget will be draggable.
1539
+ */
1540
+ movable(els, val) {
1541
+ if (this.opts.staticGrid)
1542
+ return this; // can't move a static grid!
1543
+ GridStack.getElements(els).forEach(el => {
1544
+ let node = el.gridstackNode;
1545
+ if (!node)
1546
+ return;
1547
+ if (val)
1548
+ delete node.noMove;
1549
+ else
1550
+ node.noMove = true;
1551
+ this._prepareDragDropByNode(node); // init DD if need be, and adjust
1552
+ });
1553
+ return this;
1554
+ }
1555
+ /**
1556
+ * Enables/Disables user resizing of specific grid element. If you want all items, and have it affect future items, use enableResize() instead. No-op for static grids.
1557
+ * @param els widget or selector to modify
1558
+ * @param val if true widget will be resizable.
1559
+ */
1560
+ resizable(els, val) {
1561
+ if (this.opts.staticGrid)
1562
+ return this; // can't resize a static grid!
1563
+ GridStack.getElements(els).forEach(el => {
1564
+ let node = el.gridstackNode;
1565
+ if (!node)
1566
+ return;
1567
+ if (val)
1568
+ delete node.noResize;
1569
+ else
1570
+ node.noResize = true;
1571
+ this._prepareDragDropByNode(node); // init DD if need be, and adjust
1572
+ });
1573
+ return this;
1574
+ }
1575
+ /**
1576
+ * Temporarily disables widgets moving/resizing.
1577
+ * If you want a more permanent way (which freezes up resources) use `setStatic(true)` instead.
1578
+ * Note: no-op for static grid
1579
+ * This is a shortcut for:
1580
+ * @example
1581
+ * grid.enableMove(false);
1582
+ * grid.enableResize(false);
1583
+ * @param recurse true (default) if sub-grids also get updated
1584
+ */
1585
+ disable(recurse = true) {
1586
+ if (this.opts.staticGrid)
1587
+ return;
1588
+ this.enableMove(false, recurse);
1589
+ this.enableResize(false, recurse); // @ts-ignore
1590
+ this._triggerEvent('disable');
1591
+ return this;
1592
+ }
1593
+ /**
1594
+ * Re-enables widgets moving/resizing - see disable().
1595
+ * Note: no-op for static grid.
1596
+ * This is a shortcut for:
1597
+ * @example
1598
+ * grid.enableMove(true);
1599
+ * grid.enableResize(true);
1600
+ * @param recurse true (default) if sub-grids also get updated
1601
+ */
1602
+ enable(recurse = true) {
1603
+ if (this.opts.staticGrid)
1604
+ return;
1605
+ this.enableMove(true, recurse);
1606
+ this.enableResize(true, recurse); // @ts-ignore
1607
+ this._triggerEvent('enable');
1608
+ return this;
1609
+ }
1610
+ /**
1611
+ * Enables/disables widget moving. No-op for static grids.
1612
+ * @param recurse true (default) if sub-grids also get updated
1613
+ */
1614
+ enableMove(doEnable, recurse = true) {
1615
+ if (this.opts.staticGrid)
1616
+ return this; // can't move a static grid!
1617
+ this.opts.disableDrag = !doEnable; // FIRST before we update children as grid overrides #1658
1618
+ this.engine.nodes.forEach(n => {
1619
+ this.movable(n.el, doEnable);
1620
+ if (n.subGrid && recurse)
1621
+ n.subGrid.enableMove(doEnable, recurse);
1622
+ });
1623
+ return this;
1624
+ }
1625
+ /**
1626
+ * Enables/disables widget resizing. No-op for static grids.
1627
+ * @param recurse true (default) if sub-grids also get updated
1628
+ */
1629
+ enableResize(doEnable, recurse = true) {
1630
+ if (this.opts.staticGrid)
1631
+ return this; // can't size a static grid!
1632
+ this.opts.disableResize = !doEnable; // FIRST before we update children as grid overrides #1658
1633
+ this.engine.nodes.forEach(n => {
1634
+ this.resizable(n.el, doEnable);
1635
+ if (n.subGrid && recurse)
1636
+ n.subGrid.enableResize(doEnable, recurse);
1637
+ });
1638
+ return this;
1639
+ }
1640
+ /** @internal removes any drag&drop present (called during destroy) */
1641
+ _removeDD(el) {
1642
+ dd.draggable(el, 'destroy').resizable(el, 'destroy');
1643
+ if (el.gridstackNode) {
1644
+ delete el.gridstackNode._initDD; // reset our DD init flag
1645
+ }
1646
+ delete el.ddElement;
1647
+ return this;
1648
+ }
1649
+ /** @internal called to add drag over to support widgets being added externally */
1650
+ _setupAcceptWidget() {
1651
+ // check if we need to disable things
1652
+ if (this.opts.staticGrid || (!this.opts.acceptWidgets && !this.opts.removable)) {
1653
+ dd.droppable(this.el, 'destroy');
1654
+ return this;
1655
+ }
1656
+ // vars shared across all methods
1657
+ let cellHeight, cellWidth;
1658
+ let onDrag = (event, el, helper) => {
1659
+ let node = el.gridstackNode;
1660
+ if (!node)
1661
+ return;
1662
+ helper = helper || el;
1663
+ let parent = this.el.getBoundingClientRect();
1664
+ let { top, left } = helper.getBoundingClientRect();
1665
+ left -= parent.left;
1666
+ top -= parent.top;
1667
+ let ui = { position: { top, left } };
1668
+ if (node._temporaryRemoved) {
1669
+ node.x = Math.max(0, Math.round(left / cellWidth));
1670
+ node.y = Math.max(0, Math.round(top / cellHeight));
1671
+ delete node.autoPosition;
1672
+ this.engine.nodeBoundFix(node);
1673
+ // don't accept *initial* location if doesn't fit #1419 (locked drop region, or can't grow), but maybe try if it will go somewhere
1674
+ if (!this.engine.willItFit(node)) {
1675
+ node.autoPosition = true; // ignore x,y and try for any slot...
1676
+ if (!this.engine.willItFit(node)) {
1677
+ dd.off(el, 'drag'); // stop calling us
1678
+ return; // full grid or can't grow
1679
+ }
1680
+ if (node._willFitPos) {
1681
+ // use the auto position instead #1687
1682
+ Utils.copyPos(node, node._willFitPos);
1683
+ delete node._willFitPos;
1684
+ }
1685
+ }
1686
+ // re-use the existing node dragging method
1687
+ this._onStartMoving(helper, event, ui, node, cellWidth, cellHeight);
1688
+ }
1689
+ else {
1690
+ // re-use the existing node dragging that does so much of the collision detection
1691
+ this._dragOrResize(helper, event, ui, node, cellWidth, cellHeight);
1692
+ }
1693
+ };
1694
+ dd.droppable(this.el, {
1695
+ accept: (el) => {
1696
+ let node = el.gridstackNode;
1697
+ // set accept drop to true on ourself (which we ignore) so we don't get "can't drop" icon in HTML5 mode while moving
1698
+ if (node?.grid === this)
1699
+ return true;
1700
+ if (!this.opts.acceptWidgets)
1701
+ return false;
1702
+ // check for accept method or class matching
1703
+ let canAccept = true;
1704
+ if (typeof this.opts.acceptWidgets === 'function') {
1705
+ canAccept = this.opts.acceptWidgets(el);
1706
+ }
1707
+ else {
1708
+ let selector = (this.opts.acceptWidgets === true ? '.grid-stack-item' : this.opts.acceptWidgets);
1709
+ canAccept = el.matches(selector);
1710
+ }
1711
+ // finally check to make sure we actually have space left #1571
1712
+ if (canAccept && node && this.opts.maxRow) {
1713
+ let n = { w: node.w, h: node.h, minW: node.minW, minH: node.minH }; // only width/height matters and autoPosition
1714
+ canAccept = this.engine.willItFit(n);
1715
+ }
1716
+ return canAccept;
1717
+ }
1718
+ })
1719
+ /**
1720
+ * entering our grid area
1721
+ */
1722
+ .on(this.el, 'dropover', (event, el, helper) => {
1723
+ // console.log(`over ${this.el.gridstack.opts.id} ${count++}`); // TEST
1724
+ let node = el.gridstackNode;
1725
+ // ignore drop enter on ourself (unless we temporarily removed) which happens on a simple drag of our item
1726
+ if (node?.grid === this && !node._temporaryRemoved) {
1727
+ // delete node._added; // reset this to track placeholder again in case we were over other grid #1484 (dropout doesn't always clear)
1728
+ return false; // prevent parent from receiving msg (which may be a grid as well)
1729
+ }
1730
+ // fix #1578 when dragging fast, we may not get a leave on the previous grid so force one now
1731
+ if (node?.grid && node.grid !== this && !node._temporaryRemoved) {
1732
+ // console.log('dropover without leave'); // TEST
1733
+ let otherGrid = node.grid;
1734
+ otherGrid._leave(el, helper);
1735
+ }
1736
+ // cache cell dimensions (which don't change), position can animate if we removed an item in otherGrid that affects us...
1737
+ cellWidth = this.cellWidth();
1738
+ cellHeight = this.getCellHeight(true);
1739
+ // load any element attributes if we don't have a node
1740
+ if (!node) { // @ts-ignore private read only on ourself
1741
+ node = this._readAttr(el);
1742
+ }
1743
+ if (!node.grid) {
1744
+ node._isExternal = true;
1745
+ el.gridstackNode = node;
1746
+ }
1747
+ // calculate the grid size based on element outer size
1748
+ helper = helper || el;
1749
+ let w = node.w || Math.round(helper.offsetWidth / cellWidth) || 1;
1750
+ let h = node.h || Math.round(helper.offsetHeight / cellHeight) || 1;
1751
+ // if the item came from another grid, make a copy and save the original info in case we go back there
1752
+ if (node.grid && node.grid !== this) {
1753
+ // copy the node original values (min/max/id/etc...) but override width/height/other flags which are this grid specific
1754
+ // console.log('dropover cloning node'); // TEST
1755
+ if (!el._gridstackNodeOrig)
1756
+ el._gridstackNodeOrig = node; // shouldn't have multiple nested!
1757
+ el.gridstackNode = node = { ...node, w, h, grid: this };
1758
+ this.engine.cleanupNode(node)
1759
+ .nodeBoundFix(node);
1760
+ // restore some internal fields we need after clearing them all
1761
+ node._initDD =
1762
+ node._isExternal = // DOM needs to be re-parented on a drop
1763
+ node._temporaryRemoved = true; // so it can be inserted onDrag below
1764
+ }
1765
+ else {
1766
+ node.w = w;
1767
+ node.h = h;
1768
+ node._temporaryRemoved = true; // so we can insert it
1769
+ }
1770
+ // clear any marked for complete removal (Note: don't check _isAboutToRemove as that is cleared above - just do it)
1771
+ this._itemRemoving(node.el, false);
1772
+ dd.on(el, 'drag', onDrag);
1773
+ // make sure this is called at least once when going fast #1578
1774
+ onDrag(event, el, helper);
1775
+ return false; // prevent parent from receiving msg (which may be a grid as well)
1776
+ })
1777
+ /**
1778
+ * Leaving our grid area...
1779
+ */
1780
+ .on(this.el, 'dropout', (event, el, helper) => {
1781
+ // console.log(`out ${this.el.gridstack.opts.id} ${count++}`); // TEST
1782
+ let node = el.gridstackNode;
1783
+ if (!node)
1784
+ return false;
1785
+ // fix #1578 when dragging fast, we might get leave after other grid gets enter (which calls us to clean)
1786
+ // so skip this one if we're not the active grid really..
1787
+ if (!node.grid || node.grid === this) {
1788
+ this._leave(el, helper);
1789
+ // if we were created as temporary nested grid, go back to before state
1790
+ if (this._isTemp) {
1791
+ this.removeAsSubGrid(node);
1792
+ }
1793
+ }
1794
+ return false; // prevent parent from receiving msg (which may be grid as well)
1795
+ })
1796
+ /**
1797
+ * end - releasing the mouse
1798
+ */
1799
+ .on(this.el, 'drop', (event, el, helper) => {
1800
+ let node = el.gridstackNode;
1801
+ // ignore drop on ourself from ourself that didn't come from the outside - dragend will handle the simple move instead
1802
+ if (node?.grid === this && !node._isExternal)
1803
+ return false;
1804
+ let wasAdded = !!this.placeholder.parentElement; // skip items not actually added to us because of constrains, but do cleanup #1419
1805
+ this.placeholder.remove();
1806
+ // notify previous grid of removal
1807
+ // console.log('drop delete _gridstackNodeOrig') // TEST
1808
+ let origNode = el._gridstackNodeOrig;
1809
+ delete el._gridstackNodeOrig;
1810
+ if (wasAdded && origNode?.grid && origNode.grid !== this) {
1811
+ let oGrid = origNode.grid;
1812
+ oGrid.engine.removedNodes.push(origNode);
1813
+ oGrid._triggerRemoveEvent()._triggerChangeEvent();
1814
+ // if it's an empty sub-grid that got auto-created, nuke it
1815
+ if (oGrid.parentGridItem && !oGrid.engine.nodes.length && oGrid.opts.subGridDynamic) {
1816
+ oGrid.removeAsSubGrid();
1817
+ }
1818
+ }
1819
+ if (!node)
1820
+ return false;
1821
+ // use existing placeholder node as it's already in our list with drop location
1822
+ if (wasAdded) {
1823
+ this.engine.cleanupNode(node); // removes all internal _xyz values
1824
+ node.grid = this;
1825
+ }
1826
+ dd.off(el, 'drag');
1827
+ // if we made a copy ('helper' which is temp) of the original node then insert a copy, else we move the original node (#1102)
1828
+ // as the helper will be nuked by jquery-ui otherwise. TODO: update old code path
1829
+ if (helper !== el) {
1830
+ helper.remove();
1831
+ el.gridstackNode = origNode; // original item (left behind) is re-stored to pre dragging as the node now has drop info
1832
+ if (wasAdded) {
1833
+ el = el.cloneNode(true);
1834
+ }
1835
+ }
1836
+ else {
1837
+ el.remove(); // reduce flicker as we change depth here, and size further down
1838
+ this._removeDD(el);
1839
+ }
1840
+ if (!wasAdded)
1841
+ return false;
1842
+ el.gridstackNode = node;
1843
+ node.el = el;
1844
+ let subGrid = node.subGrid?.el?.gridstack; // set when actual sub-grid present
1845
+ // @ts-ignore
1846
+ Utils.copyPos(node, this._readAttr(this.placeholder)); // placeholder values as moving VERY fast can throw things off #1578
1847
+ Utils.removePositioningStyles(el); // @ts-ignore
1848
+ this._writeAttr(el, node);
1849
+ el.classList.add(gridDefaults.itemClass, this.opts.itemClass);
1850
+ this.el.appendChild(el); // @ts-ignore // TODO: now would be ideal time to _removeHelperStyle() overriding floating styles (native only)
1851
+ if (subGrid) {
1852
+ subGrid.parentGridItem = node;
1853
+ if (!subGrid.opts.styleInHead)
1854
+ subGrid._updateStyles(true); // re-create sub-grid styles now that we've moved
1855
+ }
1856
+ this._updateContainerHeight();
1857
+ this.engine.addedNodes.push(node); // @ts-ignore
1858
+ this._triggerAddEvent(); // @ts-ignore
1859
+ this._triggerChangeEvent();
1860
+ this.engine.endUpdate();
1861
+ if (this._gsEventHandler['dropped']) {
1862
+ this._gsEventHandler['dropped']({ ...event, type: 'dropped' }, origNode && origNode.grid ? origNode : undefined, node);
1863
+ }
1864
+ // wait till we return out of the drag callback to set the new drag&resize handler or they may get messed up
1865
+ window.setTimeout(() => {
1866
+ // IFF we are still there (some application will use as placeholder and insert their real widget instead and better call makeWidget())
1867
+ if (node.el && node.el.parentElement) {
1868
+ this._prepareDragDropByNode(node);
1869
+ }
1870
+ else {
1871
+ this.engine.removeNode(node);
1872
+ }
1873
+ delete node.grid._isTemp;
1874
+ });
1875
+ return false; // prevent parent from receiving msg (which may be grid as well)
1876
+ });
1877
+ return this;
1878
+ }
1879
+ /** @internal mark item for removal */
1880
+ _itemRemoving(el, remove) {
1881
+ let node = el ? el.gridstackNode : undefined;
1882
+ if (!node || !node.grid)
1883
+ return;
1884
+ remove ? node._isAboutToRemove = true : delete node._isAboutToRemove;
1885
+ remove ? el.classList.add('grid-stack-item-removing') : el.classList.remove('grid-stack-item-removing');
1886
+ }
1887
+ /** @internal called to setup a trash drop zone if the user specifies it */
1888
+ _setupRemoveDrop() {
1889
+ if (!this.opts.staticGrid && typeof this.opts.removable === 'string') {
1890
+ let trashEl = document.querySelector(this.opts.removable);
1891
+ if (!trashEl)
1892
+ return this;
1893
+ // only register ONE drop-over/dropout callback for the 'trash', and it will
1894
+ // update the passed in item and parent grid because the 'trash' is a shared resource anyway,
1895
+ // and Native DD only has 1 event CB (having a list and technically a per grid removableOptions complicates things greatly)
1896
+ if (!dd.isDroppable(trashEl)) {
1897
+ dd.droppable(trashEl, this.opts.removableOptions)
1898
+ .on(trashEl, 'dropover', (event, el) => this._itemRemoving(el, true))
1899
+ .on(trashEl, 'dropout', (event, el) => this._itemRemoving(el, false));
1900
+ }
1901
+ }
1902
+ return this;
1903
+ }
1904
+ /** @internal prepares the element for drag&drop **/
1905
+ _prepareDragDropByNode(node) {
1906
+ let el = node.el;
1907
+ const noMove = node.noMove || this.opts.disableDrag;
1908
+ const noResize = node.noResize || this.opts.disableResize;
1909
+ // check for disabled grid first
1910
+ if (this.opts.staticGrid || (noMove && noResize)) {
1911
+ if (node._initDD) {
1912
+ this._removeDD(el); // nukes everything instead of just disable, will add some styles back next
1913
+ delete node._initDD;
1914
+ }
1915
+ el.classList.add('ui-draggable-disabled', 'ui-resizable-disabled'); // add styles one might depend on #1435
1916
+ return this;
1917
+ }
1918
+ if (!node._initDD) {
1919
+ // variables used/cashed between the 3 start/move/end methods, in addition to node passed above
1920
+ let cellWidth;
1921
+ let cellHeight;
1922
+ /** called when item starts moving/resizing */
1923
+ let onStartMoving = (event, ui) => {
1924
+ // trigger any 'dragstart' / 'resizestart' manually
1925
+ if (this._gsEventHandler[event.type]) {
1926
+ this._gsEventHandler[event.type](event, event.target);
1927
+ }
1928
+ cellWidth = this.cellWidth();
1929
+ cellHeight = this.getCellHeight(true); // force pixels for calculations
1930
+ this._onStartMoving(el, event, ui, node, cellWidth, cellHeight);
1931
+ };
1932
+ /** called when item is being dragged/resized */
1933
+ let dragOrResize = (event, ui) => {
1934
+ this._dragOrResize(el, event, ui, node, cellWidth, cellHeight);
1935
+ };
1936
+ /** called when the item stops moving/resizing */
1937
+ let onEndMoving = (event) => {
1938
+ this.placeholder.remove();
1939
+ delete node._moving;
1940
+ delete node._event;
1941
+ delete node._lastTried;
1942
+ // if the item has moved to another grid, we're done here
1943
+ let target = event.target;
1944
+ if (!target.gridstackNode || target.gridstackNode.grid !== this)
1945
+ return;
1946
+ node.el = target;
1947
+ if (node._isAboutToRemove) {
1948
+ let gridToNotify = el.gridstackNode.grid;
1949
+ if (gridToNotify._gsEventHandler[event.type]) {
1950
+ gridToNotify._gsEventHandler[event.type](event, target);
1951
+ }
1952
+ this._removeDD(el);
1953
+ gridToNotify.engine.removedNodes.push(node);
1954
+ gridToNotify._triggerRemoveEvent();
1955
+ // break circular links and remove DOM
1956
+ delete el.gridstackNode;
1957
+ delete node.el;
1958
+ el.remove();
1959
+ }
1960
+ else {
1961
+ Utils.removePositioningStyles(target);
1962
+ if (node._temporaryRemoved) {
1963
+ // got removed - restore item back to before dragging position
1964
+ Utils.copyPos(node, node._orig); // @ts-ignore
1965
+ this._writePosAttr(target, node);
1966
+ this.engine.addNode(node);
1967
+ }
1968
+ else {
1969
+ // move to new placeholder location
1970
+ this._writePosAttr(target, node);
1971
+ }
1972
+ if (this._gsEventHandler[event.type]) {
1973
+ this._gsEventHandler[event.type](event, target);
1974
+ }
1975
+ }
1976
+ // @ts-ignore
1977
+ this._extraDragRow = 0; // @ts-ignore
1978
+ this._updateContainerHeight(); // @ts-ignore
1979
+ this._triggerChangeEvent();
1980
+ this.engine.endUpdate();
1981
+ };
1982
+ dd.draggable(el, {
1983
+ start: onStartMoving,
1984
+ stop: onEndMoving,
1985
+ drag: dragOrResize
1986
+ }).resizable(el, {
1987
+ start: onStartMoving,
1988
+ stop: onEndMoving,
1989
+ resize: dragOrResize
1990
+ });
1991
+ node._initDD = true; // we've set DD support now
1992
+ }
1993
+ // finally fine tune move vs resize by disabling any part...
1994
+ dd.draggable(el, noMove ? 'disable' : 'enable')
1995
+ .resizable(el, noResize ? 'disable' : 'enable');
1996
+ return this;
1997
+ }
1998
+ /** @internal handles actual drag/resize start **/
1999
+ _onStartMoving(el, event, ui, node, cellWidth, cellHeight) {
2000
+ this.engine.cleanNodes()
2001
+ .beginUpdate(node);
2002
+ // @ts-ignore
2003
+ this._writePosAttr(this.placeholder, node);
2004
+ this.el.appendChild(this.placeholder);
2005
+ // console.log('_onStartMoving placeholder') // TEST
2006
+ node.el = this.placeholder;
2007
+ node._lastUiPosition = ui.position;
2008
+ node._prevYPix = ui.position.top;
2009
+ node._moving = (event.type === 'dragstart'); // 'dropover' are not initially moving so they can go exactly where they enter (will push stuff out of the way)
2010
+ delete node._lastTried;
2011
+ if (event.type === 'dropover' && node._temporaryRemoved) {
2012
+ // console.log('engine.addNode x=' + node.x); // TEST
2013
+ this.engine.addNode(node); // will add, fix collisions, update attr and clear _temporaryRemoved
2014
+ node._moving = true; // AFTER, mark as moving object (wanted fix location before)
2015
+ }
2016
+ // set the min/max resize info
2017
+ this.engine.cacheRects(cellWidth, cellHeight, this.opts.marginTop, this.opts.marginRight, this.opts.marginBottom, this.opts.marginLeft);
2018
+ if (event.type === 'resizestart') {
2019
+ dd.resizable(el, 'option', 'minWidth', cellWidth * (node.minW || 1))
2020
+ .resizable(el, 'option', 'minHeight', cellHeight * (node.minH || 1));
2021
+ if (node.maxW) {
2022
+ dd.resizable(el, 'option', 'maxWidth', cellWidth * node.maxW);
2023
+ }
2024
+ if (node.maxH) {
2025
+ dd.resizable(el, 'option', 'maxHeight', cellHeight * node.maxH);
2026
+ }
2027
+ }
2028
+ }
2029
+ /** @internal handles actual drag/resize **/
2030
+ _dragOrResize(el, event, ui, node, cellWidth, cellHeight) {
2031
+ let p = { ...node._orig }; // could be undefined (_isExternal) which is ok (drag only set x,y and w,h will default to node value)
2032
+ let resizing;
2033
+ let mLeft = this.opts.marginLeft, mRight = this.opts.marginRight, mTop = this.opts.marginTop, mBottom = this.opts.marginBottom;
2034
+ // if margins (which are used to pass mid point by) are large relative to cell height/width, reduce them down #1855
2035
+ let mHeight = Math.round(cellHeight * 0.1), mWidth = Math.round(cellWidth * 0.1);
2036
+ mLeft = Math.min(mLeft, mWidth);
2037
+ mRight = Math.min(mRight, mWidth);
2038
+ mTop = Math.min(mTop, mHeight);
2039
+ mBottom = Math.min(mBottom, mHeight);
2040
+ if (event.type === 'drag') {
2041
+ if (node._temporaryRemoved)
2042
+ return; // handled by dropover
2043
+ let distance = ui.position.top - node._prevYPix;
2044
+ node._prevYPix = ui.position.top;
2045
+ if (this.opts.draggable.scroll !== false) {
2046
+ Utils.updateScrollPosition(el, ui.position, distance);
2047
+ }
2048
+ // get new position taking into account the margin in the direction we are moving! (need to pass mid point by margin)
2049
+ let left = ui.position.left + (ui.position.left > node._lastUiPosition.left ? -mRight : mLeft);
2050
+ let top = ui.position.top + (ui.position.top > node._lastUiPosition.top ? -mBottom : mTop);
2051
+ p.x = Math.round(left / cellWidth);
2052
+ p.y = Math.round(top / cellHeight);
2053
+ // @ts-ignore// if we're at the bottom hitting something else, grow the grid so cursor doesn't leave when trying to place below others
2054
+ let prev = this._extraDragRow;
2055
+ if (this.engine.collide(node, p)) {
2056
+ let row = this.getRow();
2057
+ let extra = Math.max(0, (p.y + node.h) - row);
2058
+ if (this.opts.maxRow && row + extra > this.opts.maxRow) {
2059
+ extra = Math.max(0, this.opts.maxRow - row);
2060
+ } // @ts-ignore
2061
+ this._extraDragRow = extra; // @ts-ignore
2062
+ }
2063
+ else
2064
+ this._extraDragRow = 0; // @ts-ignore
2065
+ if (this._extraDragRow !== prev)
2066
+ this._updateContainerHeight();
2067
+ if (node.x === p.x && node.y === p.y)
2068
+ return; // skip same
2069
+ // DON'T skip one we tried as we might have failed because of coverage <50% before
2070
+ // if (node._lastTried && node._lastTried.x === x && node._lastTried.y === y) return;
2071
+ }
2072
+ else if (event.type === 'resize') {
2073
+ if (p.x < 0)
2074
+ return;
2075
+ // Scrolling page if needed
2076
+ Utils.updateScrollResize(event, el, cellHeight);
2077
+ // get new size
2078
+ p.w = Math.round((ui.size.width - mLeft) / cellWidth);
2079
+ p.h = Math.round((ui.size.height - mTop) / cellHeight);
2080
+ if (node.w === p.w && node.h === p.h)
2081
+ return;
2082
+ if (node._lastTried && node._lastTried.w === p.w && node._lastTried.h === p.h)
2083
+ return; // skip one we tried (but failed)
2084
+ // if we size on left/top side this might move us, so get possible new position as well
2085
+ let left = ui.position.left + mLeft;
2086
+ let top = ui.position.top + mTop;
2087
+ p.x = Math.round(left / cellWidth);
2088
+ p.y = Math.round(top / cellHeight);
2089
+ resizing = true;
2090
+ }
2091
+ node._event = event;
2092
+ node._lastTried = p; // set as last tried (will nuke if we go there)
2093
+ let rect = {
2094
+ x: ui.position.left + mLeft,
2095
+ y: ui.position.top + mTop,
2096
+ w: (ui.size ? ui.size.width : node.w * cellWidth) - mLeft - mRight,
2097
+ h: (ui.size ? ui.size.height : node.h * cellHeight) - mTop - mBottom
2098
+ };
2099
+ if (this.engine.moveNodeCheck(node, { ...p, cellWidth, cellHeight, rect, resizing })) {
2100
+ node._lastUiPosition = ui.position;
2101
+ this.engine.cacheRects(cellWidth, cellHeight, mTop, mRight, mBottom, mLeft);
2102
+ delete node._skipDown;
2103
+ if (resizing && node.subGrid)
2104
+ node.subGrid.onParentResize();
2105
+ this._extraDragRow = 0; // @ts-ignore
2106
+ this._updateContainerHeight();
2107
+ let target = event.target; // @ts-ignore
2108
+ this._writePosAttr(target, node);
2109
+ if (this._gsEventHandler[event.type]) {
2110
+ this._gsEventHandler[event.type](event, target);
2111
+ }
2112
+ }
2113
+ }
2114
+ /** @internal called when item leaving our area by either cursor dropout event
2115
+ * or shape is outside our boundaries. remove it from us, and mark temporary if this was
2116
+ * our item to start with else restore prev node values from prev grid it came from.
2117
+ **/
2118
+ _leave(el, helper) {
2119
+ let node = el.gridstackNode;
2120
+ if (!node)
2121
+ return;
2122
+ dd.off(el, 'drag'); // no need to track while being outside
2123
+ // this gets called when cursor leaves and shape is outside, so only do this once
2124
+ if (node._temporaryRemoved)
2125
+ return;
2126
+ node._temporaryRemoved = true;
2127
+ this.engine.removeNode(node); // remove placeholder as well, otherwise it's a sign node is not in our list, which is a bigger issue
2128
+ node.el = node._isExternal && helper ? helper : el; // point back to real item being dragged
2129
+ if (this.opts.removable === true) { // boolean vs a class string
2130
+ // item leaving us and we are supposed to remove on leave (no need to drag onto trash) mark it so
2131
+ this._itemRemoving(el, true);
2132
+ }
2133
+ // finally if item originally came from another grid, but left us, restore things back to prev info
2134
+ if (el._gridstackNodeOrig) {
2135
+ // console.log('leave delete _gridstackNodeOrig') // TEST
2136
+ el.gridstackNode = el._gridstackNodeOrig;
2137
+ delete el._gridstackNodeOrig;
2138
+ }
2139
+ else if (node._isExternal) {
2140
+ // item came from outside (like a toolbar) so nuke any node info
2141
+ delete node.el;
2142
+ delete el.gridstackNode;
2143
+ // and restore all nodes back to original
2144
+ this.engine.restoreInitial();
2145
+ }
2146
+ }
2147
+ // legacy method removed
2148
+ commit() { obsolete(this, this.batchUpdate(false), 'commit', 'batchUpdate', '5.2'); return this; }
2149
+ }
2150
+ /** scoping so users can call GridStack.Utils.sort() for example */
2151
+ GridStack.Utils = Utils;
2152
+ /** scoping so users can call new GridStack.Engine(12) for example */
2153
+ GridStack.Engine = GridStackEngine;
2154
+ GridStack.GDRev = '8.0.0';
2155
+ export { GridStack };
2158
2156
  //# sourceMappingURL=gridstack.js.map