%PDF- %PDF-
Direktori : /var/www/projetos/suporte.iigd.com.br/public/lib/ |
Current File : /var/www/projetos/suporte.iigd.com.br/public/lib/gridstack.js |
/******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ 102: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { /** * gridstack-dd.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GridStackDD = void 0; /* eslint-disable @typescript-eslint/no-unused-vars */ const gridstack_ddi_1 = __webpack_require__(91); const gridstack_1 = __webpack_require__(88); const utils_1 = __webpack_require__(90); // TEST let count = 0; /** * Base class implementing common Grid drag'n'drop functionality, with domain specific subclass (h5 vs jq subclasses) */ class GridStackDD extends gridstack_ddi_1.GridStackDDI { /** override to cast to correct type */ static get() { return gridstack_ddi_1.GridStackDDI.get(); } /** removes any drag&drop present (called during destroy) */ remove(el) { this.draggable(el, 'destroy').resizable(el, 'destroy'); if (el.gridstackNode) { delete el.gridstackNode._initDD; // reset our DD init flag } return this; } } exports.GridStackDD = GridStackDD; /******************************************************************************** * GridStack code that is doing drag&drop extracted here so main class is smaller * for static grid that don't do any of this work anyway. Saves about 10k. * https://www.typescriptlang.org/docs/handbook/declaration-merging.html * https://www.typescriptlang.org/docs/handbook/mixins.html ********************************************************************************/ /** @internal called to add drag over to support widgets being added externally */ gridstack_1.GridStack.prototype._setupAcceptWidget = function () { // check if we need to disable things if (this.opts.staticGrid || (!this.opts.acceptWidgets && !this.opts.removable)) { GridStackDD.get().droppable(this.el, 'destroy'); return this; } // vars shared across all methods let cellHeight, cellWidth; let onDrag = (event, el, helper) => { let node = el.gridstackNode; if (!node) return; helper = helper || el; let parent = this.el.getBoundingClientRect(); let { top, left } = helper.getBoundingClientRect(); left -= parent.left; top -= parent.top; let ui = { position: { top, left } }; if (node._temporaryRemoved) { node.x = Math.max(0, Math.round(left / cellWidth)); node.y = Math.max(0, Math.round(top / cellHeight)); delete node.autoPosition; this.engine.nodeBoundFix(node); // 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 if (!this.engine.willItFit(node)) { node.autoPosition = true; // ignore x,y and try for any slot... if (!this.engine.willItFit(node)) { GridStackDD.get().off(el, 'drag'); // stop calling us return; // full grid or can't grow } if (node._willFitPos) { // use the auto position instead #1687 utils_1.Utils.copyPos(node, node._willFitPos); delete node._willFitPos; } } // re-use the existing node dragging method this._onStartMoving(helper, event, ui, node, cellWidth, cellHeight); } else { // re-use the existing node dragging that does so much of the collision detection this._dragOrResize(helper, event, ui, node, cellWidth, cellHeight); } }; GridStackDD.get() .droppable(this.el, { accept: (el) => { let node = el.gridstackNode; // set accept drop to true on ourself (which we ignore) so we don't get "can't drop" icon in HTML5 mode while moving if ((node === null || node === void 0 ? void 0 : node.grid) === this) return true; if (!this.opts.acceptWidgets) return false; // prevent deeper nesting until rest of 992 can be fixed if (node === null || node === void 0 ? void 0 : node.subGrid) return false; // check for accept method or class matching let canAccept = true; if (typeof this.opts.acceptWidgets === 'function') { canAccept = this.opts.acceptWidgets(el); } else { let selector = (this.opts.acceptWidgets === true ? '.grid-stack-item' : this.opts.acceptWidgets); canAccept = el.matches(selector); } // finally check to make sure we actually have space left #1571 if (canAccept && node && this.opts.maxRow) { let n = { w: node.w, h: node.h, minW: node.minW, minH: node.minH }; // only width/height matters and autoPosition canAccept = this.engine.willItFit(n); } return canAccept; } }) /** * entering our grid area */ .on(this.el, 'dropover', (event, el, helper) => { // TEST console.log(`over ${this.el.gridstack.opts.id} ${count++}`); let node = el.gridstackNode; // ignore drop enter on ourself (unless we temporarily removed) which happens on a simple drag of our item if ((node === null || node === void 0 ? void 0 : node.grid) === this && !node._temporaryRemoved) { // delete node._added; // reset this to track placeholder again in case we were over other grid #1484 (dropout doesn't always clear) return false; // prevent parent from receiving msg (which may be a grid as well) } // fix #1578 when dragging fast, we may not get a leave on the previous grid so force one now if ((node === null || node === void 0 ? void 0 : node.grid) && node.grid !== this && !node._temporaryRemoved) { // TEST console.log('dropover without leave'); let otherGrid = node.grid; otherGrid._leave(el, helper); } // cache cell dimensions (which don't change), position can animate if we removed an item in otherGrid that affects us... cellWidth = this.cellWidth(); cellHeight = this.getCellHeight(true); // load any element attributes if we don't have a node if (!node) { // @ts-ignore private read only on ourself node = this._readAttr(el); } if (!node.grid) { node._isExternal = true; el.gridstackNode = node; } // calculate the grid size based on element outer size helper = helper || el; let w = node.w || Math.round(helper.offsetWidth / cellWidth) || 1; let h = node.h || Math.round(helper.offsetHeight / cellHeight) || 1; // if the item came from another grid, make a copy and save the original info in case we go back there if (node.grid && node.grid !== this) { // copy the node original values (min/max/id/etc...) but override width/height/other flags which are this grid specific // TEST console.log('dropover cloning node'); if (!el._gridstackNodeOrig) el._gridstackNodeOrig = node; // shouldn't have multiple nested! el.gridstackNode = node = Object.assign(Object.assign({}, node), { w, h, grid: this }); this.engine.cleanupNode(node) .nodeBoundFix(node); // restore some internal fields we need after clearing them all node._initDD = node._isExternal = // DOM needs to be re-parented on a drop node._temporaryRemoved = true; // so it can be inserted onDrag below } else { node.w = w; node.h = h; node._temporaryRemoved = true; // so we can insert it } // clear any marked for complete removal (Note: don't check _isAboutToRemove as that is cleared above - just do it) _itemRemoving(node.el, false); GridStackDD.get().on(el, 'drag', onDrag); // make sure this is called at least once when going fast #1578 onDrag(event, el, helper); return false; // prevent parent from receiving msg (which may be a grid as well) }) /** * Leaving our grid area... */ .on(this.el, 'dropout', (event, el, helper) => { // TEST console.log(`out ${this.el.gridstack.opts.id} ${count++}`); let node = el.gridstackNode; if (!node) return false; // fix #1578 when dragging fast, we might get leave after other grid gets enter (which calls us to clean) // so skip this one if we're not the active grid really.. if (!node.grid || node.grid === this) { this._leave(el, helper); } return false; // prevent parent from receiving msg (which may be grid as well) }) /** * end - releasing the mouse */ .on(this.el, 'drop', (event, el, helper) => { let node = el.gridstackNode; // ignore drop on ourself from ourself that didn't come from the outside - dragend will handle the simple move instead if ((node === null || node === void 0 ? void 0 : node.grid) === this && !node._isExternal) return false; let wasAdded = !!this.placeholder.parentElement; // skip items not actually added to us because of constrains, but do cleanup #1419 this.placeholder.remove(); // notify previous grid of removal // TEST console.log('drop delete _gridstackNodeOrig') let origNode = el._gridstackNodeOrig; delete el._gridstackNodeOrig; if (wasAdded && origNode && origNode.grid && origNode.grid !== this) { let oGrid = origNode.grid; oGrid.engine.removedNodes.push(origNode); oGrid._triggerRemoveEvent(); } if (!node) return false; // use existing placeholder node as it's already in our list with drop location if (wasAdded) { this.engine.cleanupNode(node); // removes all internal _xyz values node.grid = this; } GridStackDD.get().off(el, 'drag'); // if we made a copy ('helper' which is temp) of the original node then insert a copy, else we move the original node (#1102) // as the helper will be nuked by jquery-ui otherwise if (helper !== el) { helper.remove(); el.gridstackNode = origNode; // original item (left behind) is re-stored to pre dragging as the node now has drop info if (wasAdded) { el = el.cloneNode(true); } } else { el.remove(); // reduce flicker as we change depth here, and size further down GridStackDD.get().remove(el); } if (!wasAdded) return false; el.gridstackNode = node; node.el = el; // @ts-ignore utils_1.Utils.copyPos(node, this._readAttr(this.placeholder)); // placeholder values as moving VERY fast can throw things off #1578 utils_1.Utils.removePositioningStyles(el); // @ts-ignore this._writeAttr(el, node); this.el.appendChild(el); // @ts-ignore this._updateContainerHeight(); this.engine.addedNodes.push(node); // @ts-ignore this._triggerAddEvent(); // @ts-ignore this._triggerChangeEvent(); this.engine.endUpdate(); if (this._gsEventHandler['dropped']) { this._gsEventHandler['dropped'](Object.assign(Object.assign({}, event), { type: 'dropped' }), origNode && origNode.grid ? origNode : undefined, node); } // wait till we return out of the drag callback to set the new drag&resize handler or they may get messed up window.setTimeout(() => { // IFF we are still there (some application will use as placeholder and insert their real widget instead and better call makeWidget()) if (node.el && node.el.parentElement) { this._prepareDragDropByNode(node); } else { this.engine.removeNode(node); } }); return false; // prevent parent from receiving msg (which may be grid as well) }); return this; }; /** @internal mark item for removal */ function _itemRemoving(el, remove) { let node = el ? el.gridstackNode : undefined; if (!node || !node.grid) return; remove ? node._isAboutToRemove = true : delete node._isAboutToRemove; remove ? el.classList.add('grid-stack-item-removing') : el.classList.remove('grid-stack-item-removing'); } /** @internal called to setup a trash drop zone if the user specifies it */ gridstack_1.GridStack.prototype._setupRemoveDrop = function () { if (!this.opts.staticGrid && typeof this.opts.removable === 'string') { let trashEl = document.querySelector(this.opts.removable); if (!trashEl) return this; // only register ONE drop-over/dropout callback for the 'trash', and it will // update the passed in item and parent grid because the 'trash' is a shared resource anyway, // and Native DD only has 1 event CB (having a list and technically a per grid removableOptions complicates things greatly) if (!GridStackDD.get().isDroppable(trashEl)) { GridStackDD.get().droppable(trashEl, this.opts.removableOptions) .on(trashEl, 'dropover', (event, el) => _itemRemoving(el, true)) .on(trashEl, 'dropout', (event, el) => _itemRemoving(el, false)); } } return this; }; /** * call to setup dragging in from the outside (say toolbar), by specifying the class selection and options. * Called during GridStack.init() as options, but can also be called directly (last param are cached) in case the toolbar * is dynamically create and needs to change later. **/ gridstack_1.GridStack.setupDragIn = function (_dragIn, _dragInOptions) { let dragIn; let dragInOptions; const dragInDefaultOptions = { revert: 'invalid', handle: '.grid-stack-item-content', scroll: false, appendTo: 'body' }; // cache in the passed in values (form grid init?) so they don't have to resend them each time if (_dragIn) { dragIn = _dragIn; dragInOptions = Object.assign(Object.assign({}, dragInDefaultOptions), (_dragInOptions || {})); } if (typeof dragIn !== 'string') return; let dd = GridStackDD.get(); utils_1.Utils.getElements(dragIn).forEach(el => { if (!dd.isDraggable(el)) dd.dragIn(el, dragInOptions); }); }; /** @internal prepares the element for drag&drop **/ gridstack_1.GridStack.prototype._prepareDragDropByNode = function (node) { let el = node.el; let dd = GridStackDD.get(); // check for disabled grid first if (this.opts.staticGrid || ((node.noMove || this.opts.disableDrag) && (node.noResize || this.opts.disableResize))) { if (node._initDD) { dd.remove(el); // nukes everything instead of just disable, will add some styles back next delete node._initDD; } el.classList.add('ui-draggable-disabled', 'ui-resizable-disabled'); // add styles one might depend on #1435 return this; } if (!node._initDD) { // variables used/cashed between the 3 start/move/end methods, in addition to node passed above let cellWidth; let cellHeight; /** called when item starts moving/resizing */ let onStartMoving = (event, ui) => { // trigger any 'dragstart' / 'resizestart' manually if (this._gsEventHandler[event.type]) { this._gsEventHandler[event.type](event, event.target); } cellWidth = this.cellWidth(); cellHeight = this.getCellHeight(true); // force pixels for calculations this._onStartMoving(el, event, ui, node, cellWidth, cellHeight); }; /** called when item is being dragged/resized */ let dragOrResize = (event, ui) => { this._dragOrResize(el, event, ui, node, cellWidth, cellHeight); }; /** called when the item stops moving/resizing */ let onEndMoving = (event) => { this.placeholder.remove(); delete node._moving; delete node._lastTried; // if the item has moved to another grid, we're done here let target = event.target; if (!target.gridstackNode || target.gridstackNode.grid !== this) return; node.el = target; if (node._isAboutToRemove) { let gridToNotify = el.gridstackNode.grid; if (gridToNotify._gsEventHandler[event.type]) { gridToNotify._gsEventHandler[event.type](event, target); } dd.remove(el); gridToNotify.engine.removedNodes.push(node); gridToNotify._triggerRemoveEvent(); // break circular links and remove DOM delete el.gridstackNode; delete node.el; el.remove(); } else { if (!node._temporaryRemoved) { // move to new placeholder location utils_1.Utils.removePositioningStyles(target); // @ts-ignore this._writePosAttr(target, node); } else { // got removed - restore item back to before dragging position utils_1.Utils.removePositioningStyles(target); utils_1.Utils.copyPos(node, node._orig); // @ts-ignore this._writePosAttr(target, node); this.engine.addNode(node); } if (this._gsEventHandler[event.type]) { this._gsEventHandler[event.type](event, target); } } // @ts-ignore this._extraDragRow = 0; // @ts-ignore this._updateContainerHeight(); // @ts-ignore this._triggerChangeEvent(); this.engine.endUpdate(); }; dd.draggable(el, { start: onStartMoving, stop: onEndMoving, drag: dragOrResize }).resizable(el, { start: onStartMoving, stop: onEndMoving, resize: dragOrResize }); node._initDD = true; // we've set DD support now } // finally fine tune move vs resize by disabling any part... if (node.noMove || this.opts.disableDrag) { dd.draggable(el, 'disable'); el.classList.add('ui-draggable-disabled'); } else { dd.draggable(el, 'enable'); el.classList.remove('ui-draggable-disabled'); } if (node.noResize || this.opts.disableResize) { dd.resizable(el, 'disable'); el.classList.add('ui-resizable-disabled'); } else { dd.resizable(el, 'enable'); el.classList.remove('ui-resizable-disabled'); } return this; }; /** @internal called when item is starting a drag/resize */ gridstack_1.GridStack.prototype._onStartMoving = function (el, event, ui, node, cellWidth, cellHeight) { this.engine.cleanNodes() .beginUpdate(node); // @ts-ignore this._writePosAttr(this.placeholder, node); this.el.appendChild(this.placeholder); // TEST console.log('_onStartMoving placeholder') node.el = this.placeholder; node._lastUiPosition = ui.position; node._prevYPix = ui.position.top; 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) delete node._lastTried; if (event.type === 'dropover' && node._temporaryRemoved) { // TEST console.log('engine.addNode x=' + node.x); this.engine.addNode(node); // will add, fix collisions, update attr and clear _temporaryRemoved node._moving = true; // AFTER, mark as moving object (wanted fix location before) } // set the min/max resize info this.engine.cacheRects(cellWidth, cellHeight, this.opts.marginTop, this.opts.marginRight, this.opts.marginBottom, this.opts.marginLeft); if (event.type === 'resizestart') { let dd = GridStackDD.get() .resizable(el, 'option', 'minWidth', cellWidth * (node.minW || 1)) .resizable(el, 'option', 'minHeight', cellHeight * (node.minH || 1)); if (node.maxW) { dd.resizable(el, 'option', 'maxWidth', cellWidth * node.maxW); } if (node.maxH) { dd.resizable(el, 'option', 'maxHeight', cellHeight * node.maxH); } } }; /** @internal called when item leaving our area by either cursor dropout event * or shape is outside our boundaries. remove it from us, and mark temporary if this was * our item to start with else restore prev node values from prev grid it came from. **/ gridstack_1.GridStack.prototype._leave = function (el, helper) { let node = el.gridstackNode; if (!node) return; GridStackDD.get().off(el, 'drag'); // no need to track while being outside // this gets called when cursor leaves and shape is outside, so only do this once if (node._temporaryRemoved) return; node._temporaryRemoved = true; this.engine.removeNode(node); // remove placeholder as well, otherwise it's a sign node is not in our list, which is a bigger issue node.el = node._isExternal && helper ? helper : el; // point back to real item being dragged if (this.opts.removable === true) { // boolean vs a class string // item leaving us and we are supposed to remove on leave (no need to drag onto trash) mark it so _itemRemoving(el, true); } // finally if item originally came from another grid, but left us, restore things back to prev info if (el._gridstackNodeOrig) { // TEST console.log('leave delete _gridstackNodeOrig') el.gridstackNode = el._gridstackNodeOrig; delete el._gridstackNodeOrig; } else if (node._isExternal) { // item came from outside (like a toolbar) so nuke any node info delete node.el; delete el.gridstackNode; // and restore all nodes back to original this.engine.restoreInitial(); } }; /** @internal called when item is being dragged/resized */ gridstack_1.GridStack.prototype._dragOrResize = function (el, event, ui, node, cellWidth, cellHeight) { 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) let resizing; let mLeft = this.opts.marginLeft, mRight = this.opts.marginRight, mTop = this.opts.marginTop, mBottom = this.opts.marginBottom; // if margins (which are used to pass mid point by) are large relative to cell height/width, reduce them down #1855 let mHeight = Math.round(cellHeight * 0.1), mWidth = Math.round(cellWidth * 0.1); mLeft = Math.min(mLeft, mWidth); mRight = Math.min(mRight, mWidth); mTop = Math.min(mTop, mHeight); mBottom = Math.min(mBottom, mHeight); if (event.type === 'drag') { if (node._temporaryRemoved) return; // handled by dropover let distance = ui.position.top - node._prevYPix; node._prevYPix = ui.position.top; utils_1.Utils.updateScrollPosition(el, ui.position, distance); // get new position taking into account the margin in the direction we are moving! (need to pass mid point by margin) let left = ui.position.left + (ui.position.left > node._lastUiPosition.left ? -mRight : mLeft); let top = ui.position.top + (ui.position.top > node._lastUiPosition.top ? -mBottom : mTop); p.x = Math.round(left / cellWidth); p.y = Math.round(top / cellHeight); // @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 let prev = this._extraDragRow; if (this.engine.collide(node, p)) { let row = this.getRow(); let extra = Math.max(0, (p.y + node.h) - row); if (this.opts.maxRow && row + extra > this.opts.maxRow) { extra = Math.max(0, this.opts.maxRow - row); } // @ts-ignore this._extraDragRow = extra; // @ts-ignore } else this._extraDragRow = 0; // @ts-ignore if (this._extraDragRow !== prev) this._updateContainerHeight(); if (node.x === p.x && node.y === p.y) return; // skip same // DON'T skip one we tried as we might have failed because of coverage <50% before // if (node._lastTried && node._lastTried.x === x && node._lastTried.y === y) return; } else if (event.type === 'resize') { if (p.x < 0) return; // Scrolling page if needed utils_1.Utils.updateScrollResize(event, el, cellHeight); // get new size p.w = Math.round((ui.size.width - mLeft) / cellWidth); p.h = Math.round((ui.size.height - mTop) / cellHeight); if (node.w === p.w && node.h === p.h) return; if (node._lastTried && node._lastTried.w === p.w && node._lastTried.h === p.h) return; // skip one we tried (but failed) // if we size on left/top side this might move us, so get possible new position as well let left = ui.position.left + mLeft; let top = ui.position.top + mTop; p.x = Math.round(left / cellWidth); p.y = Math.round(top / cellHeight); resizing = true; } node._lastTried = p; // set as last tried (will nuke if we go there) let rect = { x: ui.position.left + mLeft, y: ui.position.top + mTop, w: (ui.size ? ui.size.width : node.w * cellWidth) - mLeft - mRight, h: (ui.size ? ui.size.height : node.h * cellHeight) - mTop - mBottom }; if (this.engine.moveNodeCheck(node, Object.assign(Object.assign({}, p), { cellWidth, cellHeight, rect, resizing }))) { node._lastUiPosition = ui.position; this.engine.cacheRects(cellWidth, cellHeight, mTop, mRight, mBottom, mLeft); delete node._skipDown; if (resizing && node.subGrid) { node.subGrid.onParentResize(); } // @ts-ignore this._extraDragRow = 0; // @ts-ignore this._updateContainerHeight(); let target = event.target; // @ts-ignore this._writePosAttr(target, node); if (this._gsEventHandler[event.type]) { this._gsEventHandler[event.type](event, target); } } }; /** * Enables/Disables moving. * @param els widget or selector to modify. * @param val if true widget will be draggable. */ gridstack_1.GridStack.prototype.movable = function (els, val) { if (this.opts.staticGrid) return this; // can't move a static grid! gridstack_1.GridStack.getElements(els).forEach(el => { let node = el.gridstackNode; if (!node) return; if (val) delete node.noMove; else node.noMove = true; this._prepareDragDropByNode(node); // init DD if need be, and adjust }); return this; }; /** * Enables/Disables resizing. * @param els widget or selector to modify * @param val if true widget will be resizable. */ gridstack_1.GridStack.prototype.resizable = function (els, val) { if (this.opts.staticGrid) return this; // can't resize a static grid! gridstack_1.GridStack.getElements(els).forEach(el => { let node = el.gridstackNode; if (!node) return; if (val) delete node.noResize; else node.noResize = true; this._prepareDragDropByNode(node); // init DD if need be, and adjust }); return this; }; /** * Temporarily disables widgets moving/resizing. * If you want a more permanent way (which freezes up resources) use `setStatic(true)` instead. * Note: no-op for static grid * This is a shortcut for: * @example * grid.enableMove(false); * grid.enableResize(false); */ gridstack_1.GridStack.prototype.disable = function () { if (this.opts.staticGrid) return; this.enableMove(false); this.enableResize(false); // @ts-ignore this._triggerEvent('disable'); return this; }; /** * Re-enables widgets moving/resizing - see disable(). * Note: no-op for static grid. * This is a shortcut for: * @example * grid.enableMove(true); * grid.enableResize(true); */ gridstack_1.GridStack.prototype.enable = function () { if (this.opts.staticGrid) return; this.enableMove(true); this.enableResize(true); // @ts-ignore this._triggerEvent('enable'); return this; }; /** Enables/disables widget moving. No-op for static grids. */ gridstack_1.GridStack.prototype.enableMove = function (doEnable) { if (this.opts.staticGrid) return this; // can't move a static grid! this.opts.disableDrag = !doEnable; // FIRST before we update children as grid overrides #1658 this.engine.nodes.forEach(n => this.movable(n.el, doEnable)); return this; }; /** Enables/disables widget resizing. No-op for static grids. */ gridstack_1.GridStack.prototype.enableResize = function (doEnable) { if (this.opts.staticGrid) return this; // can't size a static grid! this.opts.disableResize = !doEnable; // FIRST before we update children as grid overrides #1658 this.engine.nodes.forEach(n => this.resizable(n.el, doEnable)); return this; }; //# sourceMappingURL=gridstack-dd.js.map /***/ }), /***/ 91: /***/ ((__unused_webpack_module, exports) => { /** * gridstack-ddi.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GridStackDDI = void 0; /** * Abstract Partial Interface API for drag'n'drop plugin - look at GridStackDD and HTML5 / Jquery implementation versions */ class GridStackDDI { /** call this method to register your plugin instead of the default no-op one */ static registerPlugin(pluginClass) { GridStackDDI.ddi = new pluginClass(); return GridStackDDI.ddi; } /** get the current registered plugin to use */ static get() { return GridStackDDI.ddi || GridStackDDI.registerPlugin(GridStackDDI); } /** removes any drag&drop present (called during destroy) */ /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ remove(el) { return this; // no-op for static grids } } exports.GridStackDDI = GridStackDDI; //# sourceMappingURL=gridstack-ddi.js.map /***/ }), /***/ 89: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { /** * gridstack-engine.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GridStackEngine = void 0; const utils_1 = __webpack_require__(90); /** * Defines the GridStack engine that does most no DOM grid manipulation. * See GridStack methods and vars for descriptions. * * NOTE: values should not be modified directly - call the main GridStack API instead */ class GridStackEngine { constructor(opts = {}) { this.addedNodes = []; this.removedNodes = []; this.column = opts.column || 12; this.onChange = opts.onChange; this._float = opts.float; this.maxRow = opts.maxRow; this.nodes = opts.nodes || []; } batchUpdate() { if (this.batchMode) return this; this.batchMode = true; this._prevFloat = this._float; this._float = true; // let things go anywhere for now... commit() will restore and possibly reposition this.saveInitial(); // since begin update (which is called multiple times) won't do this return this; } commit() { if (!this.batchMode) return this; this.batchMode = false; this._float = this._prevFloat; delete this._prevFloat; return this._packNodes() ._notify(); } // use entire row for hitting area (will use bottom reverse sorted first) if we not actively moving DOWN and didn't already skip _useEntireRowArea(node, nn) { return !this.float && !this._hasLocked && (!node._moving || node._skipDown || nn.y <= node.y); } /** @internal fix collision on given 'node', going to given new location 'nn', with optional 'collide' node already found. * return true if we moved. */ _fixCollisions(node, nn = node, collide, opt = {}) { this._sortNodes(-1); // from last to first, so recursive collision move items in the right order collide = collide || this.collide(node, nn); // REAL area collide for swap and skip if none... if (!collide) return false; // swap check: if we're actively moving in gravity mode, see if we collide with an object the same size if (node._moving && !opt.nested && !this.float) { if (this.swap(node, collide)) return true; } // during while() collisions MAKE SURE to check entire row so larger items don't leap frog small ones (push them all down starting last in grid) let area = nn; if (this._useEntireRowArea(node, nn)) { area = { x: 0, w: this.column, y: nn.y, h: nn.h }; collide = this.collide(node, area, opt.skip); // force new hit } let didMove = false; let newOpt = { nested: true, pack: false }; while (collide = collide || this.collide(node, area, opt.skip)) { // could collide with more than 1 item... so repeat for each let moved; // if colliding with a locked item OR moving down with top gravity (and collide could move up) -> skip past the collide, // but remember that skip down so we only do this once (and push others otherwise). if (collide.locked || node._moving && !node._skipDown && nn.y > node.y && !this.float && // can take space we had, or before where we're going (!this.collide(collide, Object.assign(Object.assign({}, collide), { y: node.y }), node) || !this.collide(collide, Object.assign(Object.assign({}, collide), { y: nn.y - collide.h }), node))) { node._skipDown = (node._skipDown || nn.y > node.y); moved = this.moveNode(node, Object.assign(Object.assign(Object.assign({}, nn), { y: collide.y + collide.h }), newOpt)); if (collide.locked && moved) { utils_1.Utils.copyPos(nn, node); // moving after lock become our new desired location } else if (!collide.locked && moved && opt.pack) { // we moved after and will pack: do it now and keep the original drop location, but past the old collide to see what else we might push way this._packNodes(); nn.y = collide.y + collide.h; utils_1.Utils.copyPos(node, nn); } didMove = didMove || moved; } else { // move collide down *after* where we will be, ignoring where we are now (don't collide with us) moved = this.moveNode(collide, Object.assign(Object.assign(Object.assign({}, collide), { y: nn.y + nn.h, skip: node }), newOpt)); } if (!moved) { return didMove; } // break inf loop if we couldn't move after all (ex: maxRow, fixed) collide = undefined; } return didMove; } /** return the nodes that intercept the given node. Optionally a different area can be used, as well as a second node to skip */ collide(skip, area = skip, skip2) { return this.nodes.find(n => n !== skip && n !== skip2 && utils_1.Utils.isIntercepted(n, area)); } collideAll(skip, area = skip, skip2) { return this.nodes.filter(n => n !== skip && n !== skip2 && utils_1.Utils.isIntercepted(n, area)); } /** does a pixel coverage collision, returning the node that has the most coverage that is >50% mid line */ collideCoverage(node, o, collides) { if (!o.rect || !node._rect) return; let r0 = node._rect; // where started let r = Object.assign({}, o.rect); // where we are // update dragged rect to show where it's coming from (above or below, etc...) if (r.y > r0.y) { r.h += r.y - r0.y; r.y = r0.y; } else { r.h += r0.y - r.y; } if (r.x > r0.x) { r.w += r.x - r0.x; r.x = r0.x; } else { r.w += r0.x - r.x; } let collide; collides.forEach(n => { if (n.locked || !n._rect) return; let r2 = n._rect; // overlapping target let yOver = Number.MAX_VALUE, xOver = Number.MAX_VALUE, overMax = 0.5; // need >50% // depending on which side we started from, compute the overlap % of coverage // (ex: from above/below we only compute the max horizontal line coverage) if (r0.y < r2.y) { // from above yOver = ((r.y + r.h) - r2.y) / r2.h; } else if (r0.y + r0.h > r2.y + r2.h) { // from below yOver = ((r2.y + r2.h) - r.y) / r2.h; } if (r0.x < r2.x) { // from the left xOver = ((r.x + r.w) - r2.x) / r2.w; } else if (r0.x + r0.w > r2.x + r2.w) { // from the right xOver = ((r2.x + r2.w) - r.x) / r2.w; } let over = Math.min(xOver, yOver); if (over > overMax) { overMax = over; collide = n; } }); return collide; } /** called to cache the nodes pixel rectangles used for collision detection during drag */ cacheRects(w, h, top, right, bottom, left) { this.nodes.forEach(n => n._rect = { y: n.y * h + top, x: n.x * w + left, w: n.w * w - left - right, h: n.h * h - top - bottom }); return this; } /** called to possibly swap between 2 nodes (same size or column, not locked, touching), returning true if successful */ swap(a, b) { if (!b || b.locked || !a || a.locked) return false; function _doSwap() { let x = b.x, y = b.y; b.x = a.x; b.y = a.y; // b -> a position if (a.h != b.h) { a.x = x; a.y = b.y + b.h; // a -> goes after b } else if (a.w != b.w) { a.x = b.x + b.w; a.y = y; // a -> goes after b } else { a.x = x; a.y = y; // a -> old b position } a._dirty = b._dirty = true; return true; } let touching; // remember if we called it (vs undefined) // same size and same row or column, and touching if (a.w === b.w && a.h === b.h && (a.x === b.x || a.y === b.y) && (touching = utils_1.Utils.isTouching(a, b))) return _doSwap(); if (touching === false) return; // IFF ran test and fail, bail out // check for taking same columns (but different height) and touching if (a.w === b.w && a.x === b.x && (touching || (touching = utils_1.Utils.isTouching(a, b)))) { if (b.y < a.y) { let t = a; a = b; b = t; } // swap a <-> b vars so a is first return _doSwap(); } if (touching === false) return; // check if taking same row (but different width) and touching if (a.h === b.h && a.y === b.y && (touching || (touching = utils_1.Utils.isTouching(a, b)))) { if (b.x < a.x) { let t = a; a = b; b = t; } // swap a <-> b vars so a is first return _doSwap(); } return false; } isAreaEmpty(x, y, w, h) { let nn = { x: x || 0, y: y || 0, w: w || 1, h: h || 1 }; return !this.collide(nn); } /** re-layout grid items to reclaim any empty space */ compact() { if (this.nodes.length === 0) return this; this.batchUpdate() ._sortNodes(); let copyNodes = this.nodes; this.nodes = []; // pretend we have no nodes to conflict layout to start with... copyNodes.forEach(node => { if (!node.locked) { node.autoPosition = true; } this.addNode(node, false); // 'false' for add event trigger node._dirty = true; // will force attr update }); return this.commit(); } /** enable/disable floating widgets (default: `false`) See [example](http://gridstackjs.com/demo/float.html) */ set float(val) { if (this._float === val) return; this._float = val || false; if (!val) { this._packNodes()._notify(); } } /** float getter method */ get float() { return this._float || false; } /** @internal */ _sortNodes(dir) { this.nodes = utils_1.Utils.sort(this.nodes, dir, this.column); return this; } /** @internal called to top gravity pack the items back OR revert back to original Y positions when floating */ _packNodes() { if (this.batchMode) { return this; } this._sortNodes(); // first to last if (this.float) { // restore original Y pos this.nodes.forEach(n => { if (n._updating || n._orig === undefined || n.y === n._orig.y) return; let newY = n.y; while (newY > n._orig.y) { --newY; let collide = this.collide(n, { x: n.x, y: newY, w: n.w, h: n.h }); if (!collide) { n._dirty = true; n.y = newY; } } }); } else { // top gravity pack this.nodes.forEach((n, i) => { if (n.locked) return; while (n.y > 0) { let newY = i === 0 ? 0 : n.y - 1; let canBeMoved = i === 0 || !this.collide(n, { x: n.x, y: newY, w: n.w, h: n.h }); if (!canBeMoved) break; // Note: must be dirty (from last position) for GridStack::OnChange CB to update positions // and move items back. The user 'change' CB should detect changes from the original // starting position instead. n._dirty = (n.y !== newY); n.y = newY; } }); } return this; } /** * given a random node, makes sure it's coordinates/values are valid in the current grid * @param node to adjust * @param resizing if out of bound, resize down or move into the grid to fit ? */ prepareNode(node, resizing) { node = node || {}; node._id = node._id || GridStackEngine._idSeq++; // if we're missing position, have the grid position us automatically (before we set them to 0,0) if (node.x === undefined || node.y === undefined || node.x === null || node.y === null) { node.autoPosition = true; } // assign defaults for missing required fields let defaults = { x: 0, y: 0, w: 1, h: 1 }; utils_1.Utils.defaults(node, defaults); if (!node.autoPosition) { delete node.autoPosition; } if (!node.noResize) { delete node.noResize; } if (!node.noMove) { delete node.noMove; } // check for NaN (in case messed up strings were passed. can't do parseInt() || defaults.x above as 0 is valid #) if (typeof node.x == 'string') { node.x = Number(node.x); } if (typeof node.y == 'string') { node.y = Number(node.y); } if (typeof node.w == 'string') { node.w = Number(node.w); } if (typeof node.h == 'string') { node.h = Number(node.h); } if (isNaN(node.x)) { node.x = defaults.x; node.autoPosition = true; } if (isNaN(node.y)) { node.y = defaults.y; node.autoPosition = true; } if (isNaN(node.w)) { node.w = defaults.w; } if (isNaN(node.h)) { node.h = defaults.h; } return this.nodeBoundFix(node, resizing); } /** part2 of preparing a node to fit inside our grid - checks for x,y from grid dimensions */ nodeBoundFix(node, resizing) { let before = node._orig || utils_1.Utils.copyPos({}, node); if (node.maxW) { node.w = Math.min(node.w, node.maxW); } if (node.maxH) { node.h = Math.min(node.h, node.maxH); } if (node.minW && node.minW <= this.column) { node.w = Math.max(node.w, node.minW); } if (node.minH) { node.h = Math.max(node.h, node.minH); } if (node.w > this.column) { // if user loaded a larger than allowed widget for current # of columns, // remember it's full width so we can restore back (1 -> 12 column) #1655 // IFF we're not in the middle of column resizing! if (this.column < 12 && !this._inColumnResize) { node.w = Math.min(12, node.w); this.cacheOneLayout(node, 12); } node.w = this.column; } else if (node.w < 1) { node.w = 1; } if (this.maxRow && node.h > this.maxRow) { node.h = this.maxRow; } else if (node.h < 1) { node.h = 1; } if (node.x < 0) { node.x = 0; } if (node.y < 0) { node.y = 0; } if (node.x + node.w > this.column) { if (resizing) { node.w = this.column - node.x; } else { node.x = this.column - node.w; } } if (this.maxRow && node.y + node.h > this.maxRow) { if (resizing) { node.h = this.maxRow - node.y; } else { node.y = this.maxRow - node.h; } } if (!utils_1.Utils.samePos(node, before)) { node._dirty = true; } return node; } getDirtyNodes(verify) { // compare original x,y,w,h instead as _dirty can be a temporary state if (verify) { return this.nodes.filter(n => n._dirty && !utils_1.Utils.samePos(n, n._orig)); } return this.nodes.filter(n => n._dirty); } /** @internal call this to call onChange CB with dirty nodes */ _notify(nodes, removeDOM = true) { if (this.batchMode) return this; nodes = (nodes === undefined ? [] : (Array.isArray(nodes) ? nodes : [nodes])); let dirtyNodes = nodes.concat(this.getDirtyNodes()); this.onChange && this.onChange(dirtyNodes, removeDOM); return this; } /** @internal remove dirty and last tried info */ cleanNodes() { if (this.batchMode) return this; this.nodes.forEach(n => { delete n._dirty; delete n._lastTried; }); return this; } /** @internal called to save initial position/size to track real dirty state. * Note: should be called right after we call change event (so next API is can detect changes) * as well as right before we start move/resize/enter (so we can restore items to prev values) */ saveInitial() { this.nodes.forEach(n => { n._orig = utils_1.Utils.copyPos({}, n); delete n._dirty; }); this._hasLocked = this.nodes.some(n => n.locked); return this; } /** @internal restore all the nodes back to initial values (called when we leave) */ restoreInitial() { this.nodes.forEach(n => { if (utils_1.Utils.samePos(n, n._orig)) return; utils_1.Utils.copyPos(n, n._orig); n._dirty = true; }); this._notify(); return this; } /** call to add the given node to our list, fixing collision and re-packing */ addNode(node, triggerAddEvent = false) { let dup = this.nodes.find(n => n._id === node._id); if (dup) return dup; // prevent inserting twice! return it instead. // skip prepareNode if we're in middle of column resize (not new) but do check for bounds! node = this._inColumnResize ? this.nodeBoundFix(node) : this.prepareNode(node); delete node._temporaryRemoved; delete node._removeDOM; if (node.autoPosition) { this._sortNodes(); for (let i = 0;; ++i) { let x = i % this.column; let y = Math.floor(i / this.column); if (x + node.w > this.column) { continue; } let box = { x, y, w: node.w, h: node.h }; if (!this.nodes.find(n => utils_1.Utils.isIntercepted(box, n))) { node.x = x; node.y = y; delete node.autoPosition; // found our slot break; } } } this.nodes.push(node); if (triggerAddEvent) { this.addedNodes.push(node); } this._fixCollisions(node); if (!this.batchMode) { this._packNodes()._notify(); } return node; } removeNode(node, removeDOM = true, triggerEvent = false) { if (!this.nodes.find(n => n === node)) { // TEST console.log(`Error: GridStackEngine.removeNode() node._id=${node._id} not found!`) return this; } if (triggerEvent) { // we wait until final drop to manually track removed items (rather than during drag) this.removedNodes.push(node); } if (removeDOM) node._removeDOM = true; // let CB remove actual HTML (used to set _id to null, but then we loose layout info) // don't use 'faster' .splice(findIndex(),1) in case node isn't in our list, or in multiple times. this.nodes = this.nodes.filter(n => n !== node); return this._packNodes() ._notify(node); } removeAll(removeDOM = true) { delete this._layouts; if (this.nodes.length === 0) return this; removeDOM && this.nodes.forEach(n => n._removeDOM = true); // let CB remove actual HTML (used to set _id to null, but then we loose layout info) this.removedNodes = this.nodes; this.nodes = []; return this._notify(this.removedNodes); } /** checks if item can be moved (layout constrain) vs moveNode(), returning true if was able to move. * In more complicated cases (maxRow) it will attempt at moving the item and fixing * others in a clone first, then apply those changes if still within specs. */ moveNodeCheck(node, o) { // if (node.locked) return false; if (!this.changedPosConstrain(node, o)) return false; o.pack = true; // simpler case: move item directly... if (!this.maxRow /* && !this._hasLocked*/) { return this.moveNode(node, o); } // complex case: create a clone with NO maxRow (will check for out of bounds at the end) let clonedNode; let clone = new GridStackEngine({ column: this.column, float: this.float, nodes: this.nodes.map(n => { if (n === node) { clonedNode = Object.assign({}, n); return clonedNode; } return Object.assign({}, n); }) }); if (!clonedNode) return false; let canMove = clone.moveNode(clonedNode, o); // if maxRow make sure we are still valid size if (this.maxRow && canMove) { canMove = (clone.getRow() <= this.maxRow); // turns out we can't grow, then see if we can swap instead (ex: full grid) if we're not resizing if (!canMove && !o.resizing) { let collide = this.collide(node, o); if (collide && this.swap(node, collide)) { this._notify(); return true; } } } if (!canMove) return false; // if clone was able to move, copy those mods over to us now instead of caller trying to do this all over! // Note: we can't use the list directly as elements and other parts point to actual node, so copy content clone.nodes.filter(n => n._dirty).forEach(c => { let n = this.nodes.find(a => a._id === c._id); if (!n) return; utils_1.Utils.copyPos(n, c); n._dirty = true; }); this._notify(); return true; } /** return true if can fit in grid height constrain only (always true if no maxRow) */ willItFit(node) { delete node._willFitPos; if (!this.maxRow) return true; // create a clone with NO maxRow and check if still within size let clone = new GridStackEngine({ column: this.column, float: this.float, nodes: this.nodes.map(n => { return Object.assign({}, n); }) }); let n = Object.assign({}, node); // clone node so we don't mod any settings on it but have full autoPosition and min/max as well! #1687 this.cleanupNode(n); delete n.el; delete n._id; delete n.content; delete n.grid; clone.addNode(n); if (clone.getRow() <= this.maxRow) { node._willFitPos = utils_1.Utils.copyPos({}, n); return true; } return false; } /** true if x,y or w,h are different after clamping to min/max */ changedPosConstrain(node, p) { // make sure w,h are set p.w = p.w || node.w; p.h = p.h || node.h; if (node.x !== p.x || node.y !== p.y) return true; // check constrained w,h if (node.maxW) { p.w = Math.min(p.w, node.maxW); } if (node.maxH) { p.h = Math.min(p.h, node.maxH); } if (node.minW) { p.w = Math.max(p.w, node.minW); } if (node.minH) { p.h = Math.max(p.h, node.minH); } return (node.w !== p.w || node.h !== p.h); } /** return true if the passed in node was actually moved (checks for no-op and locked) */ moveNode(node, o) { if (!node || /*node.locked ||*/ !o) return false; if (o.pack === undefined) o.pack = true; // constrain the passed in values and check if we're still changing our node if (typeof o.x !== 'number') { o.x = node.x; } if (typeof o.y !== 'number') { o.y = node.y; } if (typeof o.w !== 'number') { o.w = node.w; } if (typeof o.h !== 'number') { o.h = node.h; } let resizing = (node.w !== o.w || node.h !== o.h); let nn = utils_1.Utils.copyPos({}, node, true); // get min/max out first, then opt positions next utils_1.Utils.copyPos(nn, o); nn = this.nodeBoundFix(nn, resizing); utils_1.Utils.copyPos(o, nn); if (utils_1.Utils.samePos(node, o)) return false; let prevPos = utils_1.Utils.copyPos({}, node); // during while() collisions make sure to check entire row so larger items don't leap frog small ones (push them all down) let area = nn; // if (this._useEntireRowArea(node, nn)) { // area = {x: 0, w: this.column, y: nn.y, h: nn.h}; // } // check if we will need to fix collision at our new location let collides = this.collideAll(node, area, o.skip); let needToMove = true; if (collides.length) { // now check to make sure we actually collided over 50% surface area while dragging let collide = node._moving && !o.nested ? this.collideCoverage(node, o, collides) : collides[0]; if (collide) { needToMove = !this._fixCollisions(node, nn, collide, o); // check if already moved... } else { needToMove = false; // we didn't cover >50% for a move, skip... } } // now move (to the original ask vs the collision version which might differ) and repack things if (needToMove) { node._dirty = true; utils_1.Utils.copyPos(node, nn); } if (o.pack) { this._packNodes() ._notify(); } return !utils_1.Utils.samePos(node, prevPos); // pack might have moved things back } getRow() { return this.nodes.reduce((row, n) => Math.max(row, n.y + n.h), 0); } beginUpdate(node) { if (!node._updating) { node._updating = true; delete node._skipDown; if (!this.batchMode) this.saveInitial(); } return this; } endUpdate() { let n = this.nodes.find(n => n._updating); if (n) { delete n._updating; delete n._skipDown; } return this; } /** saves a copy of the largest column layout (eg 12 even when rendering oneColumnMode, so we don't loose orig layout), * returning a list of widgets for serialization */ save(saveElement = true) { var _a; // use the highest layout for any saved info so we can have full detail on reload #1849 let len = (_a = this._layouts) === null || _a === void 0 ? void 0 : _a.length; let layout = len && this.column !== (len - 1) ? this._layouts[len - 1] : null; let list = []; this._sortNodes(); this.nodes.forEach(n => { let wl = layout === null || layout === void 0 ? void 0 : layout.find(l => l._id === n._id); let w = Object.assign({}, n); // use layout info instead if set if (wl) { w.x = wl.x; w.y = wl.y; w.w = wl.w; } // delete internals for (let key in w) { if (key[0] === '_' || w[key] === null || w[key] === undefined) delete w[key]; } delete w.grid; if (!saveElement) delete w.el; // delete default values (will be re-created on read) if (!w.autoPosition) delete w.autoPosition; if (!w.noResize) delete w.noResize; if (!w.noMove) delete w.noMove; if (!w.locked) delete w.locked; list.push(w); }); return list; } /** @internal called whenever a node is added or moved - updates the cached layouts */ layoutsNodesChange(nodes) { if (!this._layouts || this._inColumnResize) return this; // remove smaller layouts - we will re-generate those on the fly... larger ones need to update this._layouts.forEach((layout, column) => { if (!layout || column === this.column) return this; if (column < this.column) { this._layouts[column] = undefined; } else { // we save the original x,y,w (h isn't cached) to see what actually changed to propagate better. // NOTE: we don't need to check against out of bound scaling/moving as that will be done when using those cache values. #1785 let ratio = column / this.column; nodes.forEach(node => { if (!node._orig) return; // didn't change (newly added ?) let n = layout.find(l => l._id === node._id); if (!n) return; // no cache for new nodes. Will use those values. // Y changed, push down same amount // TODO: detect doing item 'swaps' will help instead of move (especially in 1 column mode) if (node.y !== node._orig.y) { n.y += (node.y - node._orig.y); } // X changed, scale from new position if (node.x !== node._orig.x) { n.x = Math.round(node.x * ratio); } // width changed, scale from new width if (node.w !== node._orig.w) { n.w = Math.round(node.w * ratio); } // ...height always carries over from cache }); } }); return this; } /** * @internal Called to scale the widget width & position up/down based on the column change. * Note we store previous layouts (especially original ones) to make it possible to go * from say 12 -> 1 -> 12 and get back to where we were. * * @param prevColumn previous number of columns * @param column new column number * @param nodes different sorted list (ex: DOM order) instead of current list * @param layout specify the type of re-layout that will happen (position, size, etc...). * Note: items will never be outside of the current column boundaries. default (moveScale). Ignored for 1 column */ updateNodeWidths(prevColumn, column, nodes, layout = 'moveScale') { var _a; if (!this.nodes.length || !column || prevColumn === column) return this; // cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data this.cacheLayout(this.nodes, prevColumn); this.batchUpdate(); // do this EARLY as it will call saveInitial() so we can detect where we started for _dirty and collision let newNodes = []; // if we're going to 1 column and using DOM order rather than default sorting, then generate that layout let domOrder = false; if (column === 1 && (nodes === null || nodes === void 0 ? void 0 : nodes.length)) { domOrder = true; let top = 0; nodes.forEach(n => { n.x = 0; n.w = 1; n.y = Math.max(n.y, top); top = n.y + n.h; }); newNodes = nodes; nodes = []; } else { nodes = utils_1.Utils.sort(this.nodes, -1, prevColumn); // current column reverse sorting so we can insert last to front (limit collision) } // see if we have cached previous layout IFF we are going up in size (restore) otherwise always // generate next size down from where we are (looks more natural as you gradually size down). let cacheNodes = []; if (column > prevColumn) { cacheNodes = this._layouts[column] || []; // ...if not, start with the largest layout (if not already there) as down-scaling is more accurate // by pretending we came from that larger column by assigning those values as starting point let lastIndex = this._layouts.length - 1; if (!cacheNodes.length && prevColumn !== lastIndex && ((_a = this._layouts[lastIndex]) === null || _a === void 0 ? void 0 : _a.length)) { prevColumn = lastIndex; this._layouts[lastIndex].forEach(cacheNode => { let n = nodes.find(n => n._id === cacheNode._id); if (n) { // still current, use cache info positions n.x = cacheNode.x; n.y = cacheNode.y; n.w = cacheNode.w; } }); } } // if we found cache re-use those nodes that are still current cacheNodes.forEach(cacheNode => { let j = nodes.findIndex(n => n._id === cacheNode._id); if (j !== -1) { // still current, use cache info positions nodes[j].x = cacheNode.x; nodes[j].y = cacheNode.y; nodes[j].w = cacheNode.w; newNodes.push(nodes[j]); nodes.splice(j, 1); } }); // ...and add any extra non-cached ones if (nodes.length) { if (typeof layout === 'function') { layout(column, prevColumn, newNodes, nodes); } else if (!domOrder) { let ratio = column / prevColumn; let move = (layout === 'move' || layout === 'moveScale'); let scale = (layout === 'scale' || layout === 'moveScale'); nodes.forEach(node => { // NOTE: x + w could be outside of the grid, but addNode() below will handle that node.x = (column === 1 ? 0 : (move ? Math.round(node.x * ratio) : Math.min(node.x, column - 1))); node.w = ((column === 1 || prevColumn === 1) ? 1 : scale ? (Math.round(node.w * ratio) || 1) : (Math.min(node.w, column))); newNodes.push(node); }); nodes = []; } } // finally re-layout them in reverse order (to get correct placement) newNodes = utils_1.Utils.sort(newNodes, -1, column); this._inColumnResize = true; // prevent cache update this.nodes = []; // pretend we have no nodes to start with (add() will use same structures) to simplify layout newNodes.forEach(node => { this.addNode(node, false); // 'false' for add event trigger delete node._orig; // make sure the commit doesn't try to restore things back to original }); this.commit(); delete this._inColumnResize; return this; } /** * call to cache the given layout internally to the given location so we can restore back when column changes size * @param nodes list of nodes * @param column corresponding column index to save it under * @param clear if true, will force other caches to be removed (default false) */ cacheLayout(nodes, column, clear = false) { let copy = []; nodes.forEach((n, i) => { n._id = n._id || GridStackEngine._idSeq++; // make sure we have an id in case this is new layout, else re-use id already set copy[i] = { x: n.x, y: n.y, w: n.w, _id: n._id }; // only thing we change is x,y,w and id to find it back }); this._layouts = clear ? [] : this._layouts || []; // use array to find larger quick this._layouts[column] = copy; return this; } /** * call to cache the given node layout internally to the given location so we can restore back when column changes size * @param node single node to cache * @param column corresponding column index to save it under */ cacheOneLayout(n, column) { n._id = n._id || GridStackEngine._idSeq++; let layout = { x: n.x, y: n.y, w: n.w, _id: n._id }; this._layouts = this._layouts || []; this._layouts[column] = this._layouts[column] || []; let index = this._layouts[column].findIndex(l => l._id === n._id); index === -1 ? this._layouts[column].push(layout) : this._layouts[column][index] = layout; return this; } /** called to remove all internal values but the _id */ cleanupNode(node) { for (let prop in node) { if (prop[0] === '_' && prop !== '_id') delete node[prop]; } return this; } } exports.GridStackEngine = GridStackEngine; /** @internal unique global internal _id counter NOT starting at 0 */ GridStackEngine._idSeq = 1; //# sourceMappingURL=gridstack-engine.js.map /***/ }), /***/ 88: /***/ (function(__unused_webpack_module, exports, __webpack_require__) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GridStack = void 0; /*! * GridStack 5.0 * https://gridstackjs.com/ * * Copyright (c) 2021 Alain Dumesny * see root license https://github.com/gridstack/gridstack.js/tree/master/LICENSE */ const gridstack_engine_1 = __webpack_require__(89); const utils_1 = __webpack_require__(90); const gridstack_ddi_1 = __webpack_require__(91); // export all dependent file as well to make it easier for users to just import the main file __exportStar(__webpack_require__(92), exports); __exportStar(__webpack_require__(90), exports); __exportStar(__webpack_require__(89), exports); __exportStar(__webpack_require__(91), exports); // default values for grid options - used during init and when saving out const GridDefaults = { column: 12, minRow: 0, maxRow: 0, itemClass: 'grid-stack-item', placeholderClass: 'grid-stack-placeholder', placeholderText: '', handle: '.grid-stack-item-content', handleClass: null, styleInHead: false, cellHeight: 'auto', cellHeightThrottle: 100, margin: 10, auto: true, minWidth: 768, float: false, staticGrid: false, animate: true, alwaysShowResizeHandle: false, resizable: { autoHide: true, handles: 'se' }, draggable: { handle: '.grid-stack-item-content', scroll: false, appendTo: 'body' }, disableDrag: false, disableResize: false, rtl: 'auto', removable: false, removableOptions: { accept: '.grid-stack-item' }, marginUnit: 'px', cellHeightUnit: 'px', disableOneColumnMode: false, oneColumnModeDomSort: false }; /** * Main gridstack class - you will need to call `GridStack.init()` first to initialize your grid. * Note: your grid elements MUST have the following classes for the CSS layout to work: * @example * <div class="grid-stack"> * <div class="grid-stack-item"> * <div class="grid-stack-item-content">Item 1</div> * </div> * </div> */ class GridStack { /** * Construct a grid item from the given element and options * @param el * @param opts */ constructor(el, opts = {}) { /** @internal */ this._gsEventHandler = {}; /** @internal extra row added when dragging at the bottom of the grid */ this._extraDragRow = 0; this.el = el; // exposed HTML element to the user opts = opts || {}; // handles null/undefined/0 // if row property exists, replace minRow and maxRow instead if (opts.row) { opts.minRow = opts.maxRow = opts.row; delete opts.row; } let rowAttr = utils_1.Utils.toNumber(el.getAttribute('gs-row')); // flag only valid in sub-grids (handled by parent, not here) if (opts.column === 'auto') { delete opts.column; } // elements attributes override any passed options (like CSS style) - merge the two together let defaults = Object.assign(Object.assign({}, utils_1.Utils.cloneDeep(GridDefaults)), { column: utils_1.Utils.toNumber(el.getAttribute('gs-column')) || 12, minRow: rowAttr ? rowAttr : utils_1.Utils.toNumber(el.getAttribute('gs-min-row')) || 0, maxRow: rowAttr ? rowAttr : utils_1.Utils.toNumber(el.getAttribute('gs-max-row')) || 0, staticGrid: utils_1.Utils.toBool(el.getAttribute('gs-static')) || false, _styleSheetClass: 'grid-stack-instance-' + (Math.random() * 10000).toFixed(0), alwaysShowResizeHandle: opts.alwaysShowResizeHandle || false, resizable: { autoHide: !(opts.alwaysShowResizeHandle || false), handles: 'se' }, draggable: { handle: (opts.handleClass ? '.' + opts.handleClass : (opts.handle ? opts.handle : '')) || '.grid-stack-item-content', scroll: false, appendTo: 'body' }, removableOptions: { accept: '.' + (opts.itemClass || 'grid-stack-item') } }); if (el.getAttribute('gs-animate')) { // default to true, but if set to false use that instead defaults.animate = utils_1.Utils.toBool(el.getAttribute('gs-animate')); } this.opts = utils_1.Utils.defaults(opts, defaults); opts = null; // make sure we use this.opts instead this.initMargin(); // part of settings defaults... // Now check if we're loading into 1 column mode FIRST so we don't do un-necessary work (like cellHeight = width / 12 then go 1 column) if (this.opts.column !== 1 && !this.opts.disableOneColumnMode && this._widthOrContainer() <= this.opts.minWidth) { this._prevColumn = this.getColumn(); this.opts.column = 1; } if (this.opts.rtl === 'auto') { this.opts.rtl = (el.style.direction === 'rtl'); } if (this.opts.rtl) { this.el.classList.add('grid-stack-rtl'); } // check if we're been nested, and if so update our style and keep pointer around (used during save) let parentGridItemEl = utils_1.Utils.closestByClass(this.el, GridDefaults.itemClass); if (parentGridItemEl && parentGridItemEl.gridstackNode) { this.opts._isNested = parentGridItemEl.gridstackNode; this.opts._isNested.subGrid = this; parentGridItemEl.classList.add('grid-stack-nested'); this.el.classList.add('grid-stack-nested'); } this._isAutoCellHeight = (this.opts.cellHeight === 'auto'); if (this._isAutoCellHeight || this.opts.cellHeight === 'initial') { // make the cell content square initially (will use resize/column event to keep it square) this.cellHeight(undefined, false); } else { // append unit if any are set if (typeof this.opts.cellHeight == 'number' && this.opts.cellHeightUnit && this.opts.cellHeightUnit !== GridDefaults.cellHeightUnit) { this.opts.cellHeight = this.opts.cellHeight + this.opts.cellHeightUnit; delete this.opts.cellHeightUnit; } this.cellHeight(this.opts.cellHeight, false); } this.el.classList.add(this.opts._styleSheetClass); this._setStaticClass(); this.engine = new gridstack_engine_1.GridStackEngine({ column: this.getColumn(), float: this.opts.float, maxRow: this.opts.maxRow, onChange: (cbNodes) => { let maxH = 0; this.engine.nodes.forEach(n => { maxH = Math.max(maxH, n.y + n.h); }); cbNodes.forEach(n => { let el = n.el; if (!el) return; if (n._removeDOM) { if (el) el.remove(); delete n._removeDOM; } else { this._writePosAttr(el, n); } }); this._updateStyles(false, maxH); // false = don't recreate, just append if need be } }); if (this.opts.auto) { this.batchUpdate(); // prevent in between re-layout #1535 TODO: this only set float=true, need to prevent collision check... let elements = []; this.getGridItems().forEach(el => { let x = parseInt(el.getAttribute('gs-x')); let y = parseInt(el.getAttribute('gs-y')); elements.push({ el, // if x,y are missing (autoPosition) add them to end of list - but keep their respective DOM order i: (Number.isNaN(x) ? 1000 : x) + (Number.isNaN(y) ? 1000 : y) * this.getColumn() }); }); elements.sort((a, b) => a.i - b.i).forEach(e => this._prepareElement(e.el)); this.commit(); } this.setAnimation(this.opts.animate); this._updateStyles(); if (this.opts.column != 12) { this.el.classList.add('grid-stack-' + this.opts.column); } // legacy support to appear 'per grid` options when really global. if (this.opts.dragIn) GridStack.setupDragIn(this.opts.dragIn, this.opts.dragInOptions); delete this.opts.dragIn; delete this.opts.dragInOptions; this._setupRemoveDrop(); this._setupAcceptWidget(); this._updateWindowResizeEvent(); } /** * initializing the HTML element, or selector string, into a grid will return the grid. Calling it again will * simply return the existing instance (ignore any passed options). There is also an initAll() version that support * multiple grids initialization at once. Or you can use addGrid() to create the entire grid from JSON. * @param options grid options (optional) * @param elOrString element or CSS selector (first one used) to convert to a grid (default to '.grid-stack' class selector) * * @example * let grid = GridStack.init(); * * Note: the HTMLElement (of type GridHTMLElement) will store a `gridstack: GridStack` value that can be retrieve later * let grid = document.querySelector('.grid-stack').gridstack; */ static init(options = {}, elOrString = '.grid-stack') { let el = GridStack.getGridElement(elOrString); if (!el) { if (typeof elOrString === 'string') { console.error('GridStack.initAll() no grid was found with selector "' + elOrString + '" - element missing or wrong selector ?' + '\nNote: ".grid-stack" is required for proper CSS styling and drag/drop, and is the default selector.'); } else { console.error('GridStack.init() no grid element was passed.'); } return null; } if (!el.gridstack) { el.gridstack = new GridStack(el, utils_1.Utils.cloneDeep(options)); } return el.gridstack; } /** * Will initialize a list of elements (given a selector) and return an array of grids. * @param options grid options (optional) * @param selector elements selector to convert to grids (default to '.grid-stack' class selector) * * @example * let grids = GridStack.initAll(); * grids.forEach(...) */ static initAll(options = {}, selector = '.grid-stack') { let grids = []; GridStack.getGridElements(selector).forEach(el => { if (!el.gridstack) { el.gridstack = new GridStack(el, utils_1.Utils.cloneDeep(options)); delete options.dragIn; delete options.dragInOptions; // only need to be done once (really a static global thing, not per grid) } grids.push(el.gridstack); }); if (grids.length === 0) { console.error('GridStack.initAll() no grid was found with selector "' + selector + '" - element missing or wrong selector ?' + '\nNote: ".grid-stack" is required for proper CSS styling and drag/drop, and is the default selector.'); } return grids; } /** * call to create a grid with the given options, including loading any children from JSON structure. This will call GridStack.init(), then * grid.load() on any passed children (recursively). Great alternative to calling init() if you want entire grid to come from * JSON serialized data, including options. * @param parent HTML element parent to the grid * @param opt grids options used to initialize the grid, and list of children */ static addGrid(parent, opt = {}) { if (!parent) return null; // create the grid element, but check if the passed 'parent' already has grid styling and should be used instead let el = parent; if (!parent.classList.contains('grid-stack')) { let doc = document.implementation.createHTMLDocument(''); // IE needs a param doc.body.innerHTML = `<div class="grid-stack ${opt.class || ''}"></div>`; el = doc.body.children[0]; parent.appendChild(el); } // create grid class and load any children let grid = GridStack.init(opt, el); if (grid.opts.children) { let children = grid.opts.children; delete grid.opts.children; grid.load(children); } return grid; } /** @internal create placeholder DIV as needed */ get placeholder() { if (!this._placeholder) { let placeholderChild = document.createElement('div'); // child so padding match item-content placeholderChild.className = 'placeholder-content'; if (this.opts.placeholderText) { placeholderChild.innerHTML = this.opts.placeholderText; } this._placeholder = document.createElement('div'); this._placeholder.classList.add(this.opts.placeholderClass, GridDefaults.itemClass, this.opts.itemClass); this.placeholder.appendChild(placeholderChild); } return this._placeholder; } /** * add a new widget and returns it. * * Widget will be always placed even if result height is more than actual grid height. * You need to use `willItFit()` before calling addWidget for additional check. * See also `makeWidget()`. * * @example * let grid = GridStack.init(); * grid.addWidget({w: 3, content: 'hello'}); * grid.addWidget('<div class="grid-stack-item"><div class="grid-stack-item-content">hello</div></div>', {w: 3}); * * @param el GridStackWidget (which can have content string as well), html element, or string definition to add * @param options widget position/size options (optional, and ignore if first param is already option) - see GridStackWidget */ addWidget(els, options) { // support legacy call for now ? if (arguments.length > 2) { console.warn('gridstack.ts: `addWidget(el, x, y, width...)` is deprecated. Use `addWidget({x, y, w, content, ...})`. It will be removed soon'); // eslint-disable-next-line prefer-rest-params let a = arguments, i = 1, opt = { x: a[i++], y: a[i++], w: a[i++], h: a[i++], autoPosition: a[i++], minW: a[i++], maxW: a[i++], minH: a[i++], maxH: a[i++], id: a[i++] }; return this.addWidget(els, opt); } function isGridStackWidget(w) { return w.x !== undefined || w.y !== undefined || w.w !== undefined || w.h !== undefined || w.content !== undefined ? true : false; } let el; if (typeof els === 'string') { let doc = document.implementation.createHTMLDocument(''); // IE needs a param doc.body.innerHTML = els; el = doc.body.children[0]; } else if (arguments.length === 0 || arguments.length === 1 && isGridStackWidget(els)) { let content = els ? els.content || '' : ''; options = els; let doc = document.implementation.createHTMLDocument(''); // IE needs a param doc.body.innerHTML = `<div class="grid-stack-item ${this.opts.itemClass || ''}"><div class="grid-stack-item-content">${content}</div></div>`; el = doc.body.children[0]; } else { el = els; } // Tempting to initialize the passed in opt with default and valid values, but this break knockout demos // as the actual value are filled in when _prepareElement() calls el.getAttribute('gs-xyz) before adding the node. // So make sure we load any DOM attributes that are not specified in passed in options (which override) let domAttr = this._readAttr(el); options = utils_1.Utils.cloneDeep(options) || {}; // make a copy before we modify in case caller re-uses it utils_1.Utils.defaults(options, domAttr); let node = this.engine.prepareNode(options); this._writeAttr(el, options); if (this._insertNotAppend) { this.el.prepend(el); } else { this.el.appendChild(el); } // similar to makeWidget() that doesn't read attr again and worse re-create a new node and loose any _id this._prepareElement(el, true, options); this._updateContainerHeight(); // check if nested grid definition is present if (node.subGrid && !node.subGrid.el) { // see if there is a sub-grid to create too // if column special case it set, remember that flag and set default let autoColumn; let ops = node.subGrid; if (ops.column === 'auto') { ops.column = node.w; ops.disableOneColumnMode = true; // driven by parent autoColumn = true; } let content = node.el.querySelector('.grid-stack-item-content'); node.subGrid = GridStack.addGrid(content, node.subGrid); if (autoColumn) { node.subGrid._autoColumn = true; } } this._triggerAddEvent(); this._triggerChangeEvent(); return el; } /** /** * saves the current layout returning a list of widgets for serialization which might include any nested grids. * @param saveContent if true (default) the latest html inside .grid-stack-content will be saved to GridStackWidget.content field, else it will * be removed. * @param saveGridOpt if true (default false), save the grid options itself, so you can call the new GridStack.addGrid() * to recreate everything from scratch. GridStackOptions.children would then contain the widget list instead. * @returns list of widgets or full grid option, including .children list of widgets */ save(saveContent = true, saveGridOpt = false) { // return copied nodes we can modify at will... let list = this.engine.save(saveContent); // check for HTML content and nested grids list.forEach(n => { if (saveContent && n.el && !n.subGrid) { // sub-grid are saved differently, not plain content let sub = n.el.querySelector('.grid-stack-item-content'); n.content = sub ? sub.innerHTML : undefined; if (!n.content) delete n.content; } else { if (!saveContent) { delete n.content; } // check for nested grid if (n.subGrid) { n.subGrid = n.subGrid.save(saveContent, true); } } delete n.el; }); // check if save entire grid options (needed for recursive) + children... if (saveGridOpt) { let o = utils_1.Utils.cloneDeep(this.opts); // delete default values that will be recreated on launch if (o.marginBottom === o.marginTop && o.marginRight === o.marginLeft && o.marginTop === o.marginRight) { o.margin = o.marginTop; delete o.marginTop; delete o.marginRight; delete o.marginBottom; delete o.marginLeft; } if (o.rtl === (this.el.style.direction === 'rtl')) { o.rtl = 'auto'; } if (this._isAutoCellHeight) { o.cellHeight = 'auto'; } if (this._autoColumn) { o.column = 'auto'; delete o.disableOneColumnMode; } utils_1.Utils.removeInternalAndSame(o, GridDefaults); o.children = list; return o; } return list; } /** * load the widgets from a list. This will call update() on each (matching by id) or add/remove widgets that are not there. * * @param layout list of widgets definition to update/create * @param addAndRemove boolean (default true) or callback method can be passed to control if and how missing widgets can be added/removed, giving * the user control of insertion. * * @example * see http://gridstackjs.com/demo/serialization.html **/ load(layout, addAndRemove = true) { let items = GridStack.Utils.sort([...layout], -1, this._prevColumn || this.getColumn()); // make copy before we mod/sort this._insertNotAppend = true; // since create in reverse order... // if we're loading a layout into 1 column (_prevColumn is set only when going to 1) and items don't fit, make sure to save // the original wanted layout so we can scale back up correctly #1471 if (this._prevColumn && this._prevColumn !== this.opts.column && items.some(n => (n.x + n.w) > this.opts.column)) { this._ignoreLayoutsNodeChange = true; // skip layout update this.engine.cacheLayout(items, this._prevColumn, true); } let removed = []; this.batchUpdate(); // see if any items are missing from new layout and need to be removed first if (addAndRemove) { let copyNodes = [...this.engine.nodes]; // don't loop through array you modify copyNodes.forEach(n => { let item = items.find(w => n.id === w.id); if (!item) { if (typeof (addAndRemove) === 'function') { addAndRemove(this, n, false); } else { removed.push(n); // batch keep track this.removeWidget(n.el, true, false); } } }); } // now add/update the widgets items.forEach(w => { let item = (w.id || w.id === 0) ? this.engine.nodes.find(n => n.id === w.id) : undefined; if (item) { this.update(item.el, w); if (w.subGrid && w.subGrid.children) { // update any sub grid as well let sub = item.el.querySelector('.grid-stack'); if (sub && sub.gridstack) { sub.gridstack.load(w.subGrid.children); // TODO: support updating grid options ? this._insertNotAppend = true; // got reset by above call } } } else if (addAndRemove) { if (typeof (addAndRemove) === 'function') { w = addAndRemove(this, w, true).gridstackNode; } else { w = this.addWidget(w).gridstackNode; } } }); this.engine.removedNodes = removed; this.commit(); // after commit, clear that flag delete this._ignoreLayoutsNodeChange; delete this._insertNotAppend; return this; } /** * Initializes batch updates. You will see no changes until `commit()` method is called. */ batchUpdate() { this.engine.batchUpdate(); return this; } /** * Gets current cell height. */ getCellHeight(forcePixel = false) { if (this.opts.cellHeight && this.opts.cellHeight !== 'auto' && (!forcePixel || !this.opts.cellHeightUnit || this.opts.cellHeightUnit === 'px')) { return this.opts.cellHeight; } // else get first cell height let el = this.el.querySelector('.' + this.opts.itemClass); if (el) { let height = utils_1.Utils.toNumber(el.getAttribute('gs-h')); return Math.round(el.offsetHeight / height); } // else do entire grid and # of rows (but doesn't work if min-height is the actual constrain) let rows = parseInt(this.el.getAttribute('gs-current-row')); return rows ? Math.round(this.el.getBoundingClientRect().height / rows) : this.opts.cellHeight; } /** * Update current cell height - see `GridStackOptions.cellHeight` for format. * This method rebuilds an internal CSS style sheet. * Note: You can expect performance issues if call this method too often. * * @param val the cell height. If not passed (undefined), cells content will be made square (match width minus margin), * if pass 0 the CSS will be generated by the application instead. * @param update (Optional) if false, styles will not be updated * * @example * grid.cellHeight(100); // same as 100px * grid.cellHeight('70px'); * grid.cellHeight(grid.cellWidth() * 1.2); */ cellHeight(val, update = true) { // if not called internally, check if we're changing mode if (update && val !== undefined) { if (this._isAutoCellHeight !== (val === 'auto')) { this._isAutoCellHeight = (val === 'auto'); this._updateWindowResizeEvent(); } } if (val === 'initial' || val === 'auto') { val = undefined; } // make item content be square if (val === undefined) { let marginDiff = -this.opts.marginRight - this.opts.marginLeft + this.opts.marginTop + this.opts.marginBottom; val = this.cellWidth() + marginDiff; } let data = utils_1.Utils.parseHeight(val); if (this.opts.cellHeightUnit === data.unit && this.opts.cellHeight === data.h) { return this; } this.opts.cellHeightUnit = data.unit; this.opts.cellHeight = data.h; if (update) { this._updateStyles(true, this.getRow()); // true = force re-create, for that # of rows } return this; } /** Gets current cell width. */ cellWidth() { return this._widthOrContainer() / this.getColumn(); } /** return our expected width (or parent) for 1 column check */ _widthOrContainer() { // use `offsetWidth` or `clientWidth` (no scrollbar) ? // https://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively return (this.el.clientWidth || this.el.parentElement.clientWidth || window.innerWidth); } /** * Finishes batch updates. Updates DOM nodes. You must call it after batchUpdate. */ commit() { this.engine.commit(); this._triggerRemoveEvent(); this._triggerAddEvent(); this._triggerChangeEvent(); return this; } /** re-layout grid items to reclaim any empty space */ compact() { this.engine.compact(); this._triggerChangeEvent(); return this; } /** * set the number of columns in the grid. Will update existing widgets to conform to new number of columns, * as well as cache the original layout so you can revert back to previous positions without loss. * Requires `gridstack-extra.css` or `gridstack-extra.min.css` for [2-11], * else you will need to generate correct CSS (see https://github.com/gridstack/gridstack.js#change-grid-columns) * @param column - Integer > 0 (default 12). * @param layout specify the type of re-layout that will happen (position, size, etc...). * Note: items will never be outside of the current column boundaries. default (moveScale). Ignored for 1 column */ column(column, layout = 'moveScale') { if (column < 1 || this.opts.column === column) return this; let oldColumn = this.getColumn(); // if we go into 1 column mode (which happens if we're sized less than minW unless disableOneColumnMode is on) // then remember the original columns so we can restore. if (column === 1) { this._prevColumn = oldColumn; } else { delete this._prevColumn; } this.el.classList.remove('grid-stack-' + oldColumn); this.el.classList.add('grid-stack-' + column); this.opts.column = this.engine.column = column; // update the items now - see if the dom order nodes should be passed instead (else default to current list) let domNodes; if (column === 1 && this.opts.oneColumnModeDomSort) { domNodes = []; this.getGridItems().forEach(el => { if (el.gridstackNode) { domNodes.push(el.gridstackNode); } }); if (!domNodes.length) { domNodes = undefined; } } this.engine.updateNodeWidths(oldColumn, column, domNodes, layout); if (this._isAutoCellHeight) this.cellHeight(); // and trigger our event last... this._ignoreLayoutsNodeChange = true; // skip layout update this._triggerChangeEvent(); delete this._ignoreLayoutsNodeChange; return this; } /** * get the number of columns in the grid (default 12) */ getColumn() { return this.opts.column; } /** returns an array of grid HTML elements (no placeholder) - used to iterate through our children in DOM order */ getGridItems() { return Array.from(this.el.children) .filter((el) => el.matches('.' + this.opts.itemClass) && !el.matches('.' + this.opts.placeholderClass)); } /** * Destroys a grid instance. DO NOT CALL any methods or access any vars after this as it will free up members. * @param removeDOM if `false` grid and items HTML elements will not be removed from the DOM (Optional. Default `true`). */ destroy(removeDOM = true) { if (!this.el) return; // prevent multiple calls this._updateWindowResizeEvent(true); this.setStatic(true, false); // permanently removes DD but don't set CSS class (we're going away) this.setAnimation(false); if (!removeDOM) { this.removeAll(removeDOM); this.el.classList.remove(this.opts._styleSheetClass); } else { this.el.parentNode.removeChild(this.el); } this._removeStylesheet(); this.el.removeAttribute('gs-current-row'); delete this.opts._isNested; delete this.opts; delete this._placeholder; delete this.engine; delete this.el.gridstack; // remove circular dependency that would prevent a freeing delete this.el; return this; } /** * enable/disable floating widgets (default: `false`) See [example](http://gridstackjs.com/demo/float.html) */ float(val) { this.engine.float = val; this._triggerChangeEvent(); return this; } /** * get the current float mode */ getFloat() { return this.engine.float; } /** * Get the position of the cell under a pixel on screen. * @param position the position of the pixel to resolve in * absolute coordinates, as an object with top and left properties * @param useDocRelative if true, value will be based on document position vs parent position (Optional. Default false). * Useful when grid is within `position: relative` element * * Returns an object with properties `x` and `y` i.e. the column and row in the grid. */ getCellFromPixel(position, useDocRelative = false) { let box = this.el.getBoundingClientRect(); // console.log(`getBoundingClientRect left: ${box.left} top: ${box.top} w: ${box.w} h: ${box.h}`) let containerPos; if (useDocRelative) { containerPos = { top: box.top + document.documentElement.scrollTop, left: box.left }; // console.log(`getCellFromPixel scrollTop: ${document.documentElement.scrollTop}`) } else { containerPos = { top: this.el.offsetTop, left: this.el.offsetLeft }; // console.log(`getCellFromPixel offsetTop: ${containerPos.left} offsetLeft: ${containerPos.top}`) } let relativeLeft = position.left - containerPos.left; let relativeTop = position.top - containerPos.top; let columnWidth = (box.width / this.getColumn()); let rowHeight = (box.height / parseInt(this.el.getAttribute('gs-current-row'))); return { x: Math.floor(relativeLeft / columnWidth), y: Math.floor(relativeTop / rowHeight) }; } /** returns the current number of rows, which will be at least `minRow` if set */ getRow() { return Math.max(this.engine.getRow(), this.opts.minRow); } /** * Checks if specified area is empty. * @param x the position x. * @param y the position y. * @param w the width of to check * @param h the height of to check */ isAreaEmpty(x, y, w, h) { return this.engine.isAreaEmpty(x, y, w, h); } /** * If you add elements to your grid by hand, you have to tell gridstack afterwards to make them widgets. * If you want gridstack to add the elements for you, use `addWidget()` instead. * Makes the given element a widget and returns it. * @param els widget or single selector to convert. * * @example * let grid = GridStack.init(); * grid.el.appendChild('<div id="gsi-1" gs-w="3"></div>'); * grid.makeWidget('#gsi-1'); */ makeWidget(els) { let el = GridStack.getElement(els); this._prepareElement(el, true); this._updateContainerHeight(); this._triggerAddEvent(); this._triggerChangeEvent(); return el; } /** * Event handler that extracts our CustomEvent data out automatically for receiving custom * notifications (see doc for supported events) * @param name of the event (see possible values) or list of names space separated * @param callback function called with event and optional second/third param * (see README documentation for each signature). * * @example * grid.on('added', function(e, items) { log('added ', items)} ); * or * grid.on('added removed change', function(e, items) { log(e.type, items)} ); * * Note: in some cases it is the same as calling native handler and parsing the event. * grid.el.addEventListener('added', function(event) { log('added ', event.detail)} ); * */ on(name, callback) { // check for array of names being passed instead if (name.indexOf(' ') !== -1) { let names = name.split(' '); names.forEach(name => this.on(name, callback)); return this; } if (name === 'change' || name === 'added' || name === 'removed' || name === 'enable' || name === 'disable') { // native CustomEvent handlers - cash the generic handlers so we can easily remove let noData = (name === 'enable' || name === 'disable'); if (noData) { this._gsEventHandler[name] = (event) => callback(event); } else { this._gsEventHandler[name] = (event) => callback(event, event.detail); } this.el.addEventListener(name, this._gsEventHandler[name]); } else if (name === 'drag' || name === 'dragstart' || name === 'dragstop' || name === 'resizestart' || name === 'resize' || name === 'resizestop' || name === 'dropped') { // drag&drop stop events NEED to be call them AFTER we update node attributes so handle them ourself. // do same for start event to make it easier... this._gsEventHandler[name] = callback; } else { console.log('GridStack.on(' + name + ') event not supported, but you can still use $(".grid-stack").on(...) while jquery-ui is still used internally.'); } return this; } /** * unsubscribe from the 'on' event below * @param name of the event (see possible values) */ off(name) { // check for array of names being passed instead if (name.indexOf(' ') !== -1) { let names = name.split(' '); names.forEach(name => this.off(name)); return this; } if (name === 'change' || name === 'added' || name === 'removed' || name === 'enable' || name === 'disable') { // remove native CustomEvent handlers if (this._gsEventHandler[name]) { this.el.removeEventListener(name, this._gsEventHandler[name]); } } delete this._gsEventHandler[name]; return this; } /** * Removes widget from the grid. * @param el widget or selector to modify * @param removeDOM if `false` DOM element won't be removed from the tree (Default? true). * @param triggerEvent if `false` (quiet mode) element will not be added to removed list and no 'removed' callbacks will be called (Default? true). */ removeWidget(els, removeDOM = true, triggerEvent = true) { GridStack.getElements(els).forEach(el => { if (el.parentElement !== this.el) return; // not our child! let node = el.gridstackNode; // For Meteor support: https://github.com/gridstack/gridstack.js/pull/272 if (!node) { node = this.engine.nodes.find(n => el === n.el); } if (!node) return; // remove our DOM data (circular link) and drag&drop permanently delete el.gridstackNode; gridstack_ddi_1.GridStackDDI.get().remove(el); this.engine.removeNode(node, removeDOM, triggerEvent); if (removeDOM && el.parentElement) { el.remove(); // in batch mode engine.removeNode doesn't call back to remove DOM } }); if (triggerEvent) { this._triggerRemoveEvent(); this._triggerChangeEvent(); } return this; } /** * Removes all widgets from the grid. * @param removeDOM if `false` DOM elements won't be removed from the tree (Default? `true`). */ removeAll(removeDOM = true) { // always remove our DOM data (circular link) before list gets emptied and drag&drop permanently this.engine.nodes.forEach(n => { delete n.el.gridstackNode; gridstack_ddi_1.GridStackDDI.get().remove(n.el); }); this.engine.removeAll(removeDOM); this._triggerRemoveEvent(); return this; } /** * Toggle the grid animation state. Toggles the `grid-stack-animate` class. * @param doAnimate if true the grid will animate. */ setAnimation(doAnimate) { if (doAnimate) { this.el.classList.add('grid-stack-animate'); } else { this.el.classList.remove('grid-stack-animate'); } return this; } /** * Toggle the grid static state, which permanently removes/add Drag&Drop support, unlike disable()/enable() that just turns it off/on. * Also toggle the grid-stack-static class. * @param val if true the grid become static. */ setStatic(val, updateClass = true) { if (this.opts.staticGrid === val) return this; this.opts.staticGrid = val; this._setupRemoveDrop(); this._setupAcceptWidget(); this.engine.nodes.forEach(n => this._prepareDragDropByNode(n)); // either delete or init Drag&drop if (updateClass) { this._setStaticClass(); } return this; } /** * 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. * @param els widget or selector of objects to modify (note: setting the same x,y for multiple items will be indeterministic and likely unwanted) * @param opt new widget options (x,y,w,h, etc..). Only those set will be updated. */ update(els, opt) { // support legacy call for now ? if (arguments.length > 2) { console.warn('gridstack.ts: `update(el, x, y, w, h)` is deprecated. Use `update(el, {x, w, content, ...})`. It will be removed soon'); // eslint-disable-next-line prefer-rest-params let a = arguments, i = 1; opt = { x: a[i++], y: a[i++], w: a[i++], h: a[i++] }; return this.update(els, opt); } GridStack.getElements(els).forEach(el => { if (!el || !el.gridstackNode) return; let n = el.gridstackNode; let w = utils_1.Utils.cloneDeep(opt); // make a copy we can modify in case they re-use it or multiple items delete w.autoPosition; // move/resize widget if anything changed let keys = ['x', 'y', 'w', 'h']; let m; if (keys.some(k => w[k] !== undefined && w[k] !== n[k])) { m = {}; keys.forEach(k => { m[k] = (w[k] !== undefined) ? w[k] : n[k]; delete w[k]; }); } // for a move as well IFF there is any min/max fields set if (!m && (w.minW || w.minH || w.maxW || w.maxH)) { m = {}; // will use node position but validate values } // check for content changing if (w.content) { let sub = el.querySelector('.grid-stack-item-content'); if (sub && sub.innerHTML !== w.content) { sub.innerHTML = w.content; } delete w.content; } // any remaining fields are assigned, but check for dragging changes, resize constrain let changed = false; let ddChanged = false; for (const key in w) { if (key[0] !== '_' && n[key] !== w[key]) { n[key] = w[key]; changed = true; ddChanged = ddChanged || (!this.opts.staticGrid && (key === 'noResize' || key === 'noMove' || key === 'locked')); } } // finally move the widget if (m) { this.engine.cleanNodes() .beginUpdate(n) .moveNode(n, m); this._updateContainerHeight(); this._triggerChangeEvent(); this.engine.endUpdate(); } if (changed) { // move will only update x,y,w,h so update the rest too this._writeAttr(el, n); } if (ddChanged) { this._prepareDragDropByNode(n); } }); return this; } /** * 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). * @param value margin value */ margin(value) { let isMultiValue = (typeof value === 'string' && value.split(' ').length > 1); // check if we can skip re-creating our CSS file... won't check if multi values (too much hassle) if (!isMultiValue) { let data = utils_1.Utils.parseHeight(value); if (this.opts.marginUnit === data.unit && this.opts.margin === data.h) return; } // re-use existing margin handling this.opts.margin = value; this.opts.marginTop = this.opts.marginBottom = this.opts.marginLeft = this.opts.marginRight = undefined; this.initMargin(); this._updateStyles(true); // true = force re-create return this; } /** returns current margin number value (undefined if 4 sides don't match) */ getMargin() { return this.opts.margin; } /** * Returns true if the height of the grid will be less than the vertical * constraint. Always returns true if grid doesn't have height constraint. * @param node contains x,y,w,h,auto-position options * * @example * if (grid.willItFit(newWidget)) { * grid.addWidget(newWidget); * } else { * alert('Not enough free space to place the widget'); * } */ willItFit(node) { // support legacy call for now if (arguments.length > 1) { console.warn('gridstack.ts: `willItFit(x,y,w,h,autoPosition)` is deprecated. Use `willItFit({x, y,...})`. It will be removed soon'); // eslint-disable-next-line prefer-rest-params let a = arguments, i = 0, w = { x: a[i++], y: a[i++], w: a[i++], h: a[i++], autoPosition: a[i++] }; return this.willItFit(w); } return this.engine.willItFit(node); } /** @internal */ _triggerChangeEvent() { if (this.engine.batchMode) return this; let elements = this.engine.getDirtyNodes(true); // verify they really changed if (elements && elements.length) { if (!this._ignoreLayoutsNodeChange) { this.engine.layoutsNodesChange(elements); } this._triggerEvent('change', elements); } this.engine.saveInitial(); // we called, now reset initial values & dirty flags return this; } /** @internal */ _triggerAddEvent() { if (this.engine.batchMode) return this; if (this.engine.addedNodes && this.engine.addedNodes.length > 0) { if (!this._ignoreLayoutsNodeChange) { this.engine.layoutsNodesChange(this.engine.addedNodes); } // prevent added nodes from also triggering 'change' event (which is called next) this.engine.addedNodes.forEach(n => { delete n._dirty; }); this._triggerEvent('added', this.engine.addedNodes); this.engine.addedNodes = []; } return this; } /** @internal */ _triggerRemoveEvent() { if (this.engine.batchMode) return this; if (this.engine.removedNodes && this.engine.removedNodes.length > 0) { this._triggerEvent('removed', this.engine.removedNodes); this.engine.removedNodes = []; } return this; } /** @internal */ _triggerEvent(name, data) { let event = data ? new CustomEvent(name, { bubbles: false, detail: data }) : new Event(name); this.el.dispatchEvent(event); return this; } /** @internal called to delete the current dynamic style sheet used for our layout */ _removeStylesheet() { if (this._styles) { utils_1.Utils.removeStylesheet(this._styles._id); delete this._styles; } return this; } /** @internal updated/create the CSS styles for row based layout and initial margin setting */ _updateStyles(forceUpdate = false, maxH) { // call to delete existing one if we change cellHeight / margin if (forceUpdate) { this._removeStylesheet(); } this._updateContainerHeight(); // if user is telling us they will handle the CSS themselves by setting heights to 0. Do we need this opts really ?? if (this.opts.cellHeight === 0) { return this; } let cellHeight = this.opts.cellHeight; let cellHeightUnit = this.opts.cellHeightUnit; let prefix = `.${this.opts._styleSheetClass} > .${this.opts.itemClass}`; // create one as needed if (!this._styles) { let id = 'gridstack-style-' + (Math.random() * 100000).toFixed(); // insert style to parent (instead of 'head' by default) to support WebComponent let styleLocation = this.opts.styleInHead ? undefined : this.el.parentNode; this._styles = utils_1.Utils.createStylesheet(id, styleLocation); if (!this._styles) return this; this._styles._id = id; this._styles._max = 0; // these are done once only utils_1.Utils.addCSSRule(this._styles, prefix, `min-height: ${cellHeight}${cellHeightUnit}`); // content margins let top = this.opts.marginTop + this.opts.marginUnit; let bottom = this.opts.marginBottom + this.opts.marginUnit; let right = this.opts.marginRight + this.opts.marginUnit; let left = this.opts.marginLeft + this.opts.marginUnit; let content = `${prefix} > .grid-stack-item-content`; let placeholder = `.${this.opts._styleSheetClass} > .grid-stack-placeholder > .placeholder-content`; utils_1.Utils.addCSSRule(this._styles, content, `top: ${top}; right: ${right}; bottom: ${bottom}; left: ${left};`); utils_1.Utils.addCSSRule(this._styles, placeholder, `top: ${top}; right: ${right}; bottom: ${bottom}; left: ${left};`); // resize handles offset (to match margin) utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-ne`, `right: ${right}`); utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-e`, `right: ${right}`); utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-se`, `right: ${right}; bottom: ${bottom}`); utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-nw`, `left: ${left}`); utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-w`, `left: ${left}`); utils_1.Utils.addCSSRule(this._styles, `${prefix} > .ui-resizable-sw`, `left: ${left}; bottom: ${bottom}`); } // now update the height specific fields maxH = maxH || this._styles._max; if (maxH > this._styles._max) { let getHeight = (rows) => (cellHeight * rows) + cellHeightUnit; for (let i = this._styles._max + 1; i <= maxH; i++) { // start at 1 let h = getHeight(i); utils_1.Utils.addCSSRule(this._styles, `${prefix}[gs-y="${i - 1}"]`, `top: ${getHeight(i - 1)}`); // start at 0 utils_1.Utils.addCSSRule(this._styles, `${prefix}[gs-h="${i}"]`, `height: ${h}`); utils_1.Utils.addCSSRule(this._styles, `${prefix}[gs-min-h="${i}"]`, `min-height: ${h}`); utils_1.Utils.addCSSRule(this._styles, `${prefix}[gs-max-h="${i}"]`, `max-height: ${h}`); } this._styles._max = maxH; } return this; } /** @internal */ _updateContainerHeight() { if (!this.engine || this.engine.batchMode) return this; let row = this.getRow() + this._extraDragRow; // checks for minRow already // check for css min height // Note: we don't handle %,rem correctly so comment out, beside we don't need need to create un-necessary // rows as the CSS will make us bigger than our set height if needed... not sure why we had this. // let cssMinHeight = parseInt(getComputedStyle(this.el)['min-height']); // if (cssMinHeight > 0) { // let minRow = Math.round(cssMinHeight / this.getCellHeight(true)); // if (row < minRow) { // row = minRow; // } // } this.el.setAttribute('gs-current-row', String(row)); if (row === 0) { this.el.style.removeProperty('height'); return this; } let cellHeight = this.opts.cellHeight; let unit = this.opts.cellHeightUnit; if (!cellHeight) return this; this.el.style.height = row * cellHeight + unit; return this; } /** @internal */ _prepareElement(el, triggerAddEvent = false, node) { if (!node) { el.classList.add(this.opts.itemClass); node = this._readAttr(el); } el.gridstackNode = node; node.el = el; node.grid = this; let copy = Object.assign({}, node); node = this.engine.addNode(node, triggerAddEvent); // write node attr back in case there was collision or we have to fix bad values during addNode() if (!utils_1.Utils.same(node, copy)) { this._writeAttr(el, node); } this._prepareDragDropByNode(node); return this; } /** @internal call to write position x,y,w,h attributes back to element */ _writePosAttr(el, n) { if (n.x !== undefined && n.x !== null) { el.setAttribute('gs-x', String(n.x)); } if (n.y !== undefined && n.y !== null) { el.setAttribute('gs-y', String(n.y)); } if (n.w) { el.setAttribute('gs-w', String(n.w)); } if (n.h) { el.setAttribute('gs-h', String(n.h)); } return this; } /** @internal call to write any default attributes back to element */ _writeAttr(el, node) { if (!node) return this; this._writePosAttr(el, node); let attrs /*: GridStackWidget but strings */ = { autoPosition: 'gs-auto-position', minW: 'gs-min-w', minH: 'gs-min-h', maxW: 'gs-max-w', maxH: 'gs-max-h', noResize: 'gs-no-resize', noMove: 'gs-no-move', locked: 'gs-locked', id: 'gs-id', resizeHandles: 'gs-resize-handles' }; for (const key in attrs) { if (node[key]) { // 0 is valid for x,y only but done above already and not in list anyway el.setAttribute(attrs[key], String(node[key])); } else { el.removeAttribute(attrs[key]); } } return this; } /** @internal call to read any default attributes from element */ _readAttr(el) { let node = {}; node.x = utils_1.Utils.toNumber(el.getAttribute('gs-x')); node.y = utils_1.Utils.toNumber(el.getAttribute('gs-y')); node.w = utils_1.Utils.toNumber(el.getAttribute('gs-w')); node.h = utils_1.Utils.toNumber(el.getAttribute('gs-h')); node.maxW = utils_1.Utils.toNumber(el.getAttribute('gs-max-w')); node.minW = utils_1.Utils.toNumber(el.getAttribute('gs-min-w')); node.maxH = utils_1.Utils.toNumber(el.getAttribute('gs-max-h')); node.minH = utils_1.Utils.toNumber(el.getAttribute('gs-min-h')); node.autoPosition = utils_1.Utils.toBool(el.getAttribute('gs-auto-position')); node.noResize = utils_1.Utils.toBool(el.getAttribute('gs-no-resize')); node.noMove = utils_1.Utils.toBool(el.getAttribute('gs-no-move')); node.locked = utils_1.Utils.toBool(el.getAttribute('gs-locked')); node.resizeHandles = el.getAttribute('gs-resize-handles'); node.id = el.getAttribute('gs-id'); // remove any key not found (null or false which is default) for (const key in node) { if (!node.hasOwnProperty(key)) return; if (!node[key] && node[key] !== 0) { // 0 can be valid value (x,y only really) delete node[key]; } } return node; } /** @internal */ _setStaticClass() { let classes = ['grid-stack-static']; if (this.opts.staticGrid) { this.el.classList.add(...classes); this.el.setAttribute('gs-static', 'true'); } else { this.el.classList.remove(...classes); this.el.removeAttribute('gs-static'); } return this; } /** * called when we are being resized by the window - check if the one Column Mode needs to be turned on/off * and remember the prev columns we used, or get our count from parent, as well as check for auto cell height (square) */ onParentResize() { if (!this.el || !this.el.clientWidth) return; // return if we're gone or no size yet (will get called again) let changedColumn = false; // see if we're nested and take our column count from our parent.... if (this._autoColumn && this.opts._isNested) { if (this.opts.column !== this.opts._isNested.w) { changedColumn = true; this.column(this.opts._isNested.w, 'none'); } } else { // else check for 1 column in/out behavior let oneColumn = !this.opts.disableOneColumnMode && this.el.clientWidth <= this.opts.minWidth; if ((this.opts.column === 1) !== oneColumn) { changedColumn = true; if (this.opts.animate) { this.setAnimation(false); } // 1 <-> 12 is too radical, turn off animation this.column(oneColumn ? 1 : this._prevColumn); if (this.opts.animate) { this.setAnimation(true); } } } // make the cells content square again if (this._isAutoCellHeight) { if (!changedColumn && this.opts.cellHeightThrottle) { if (!this._cellHeightThrottle) { this._cellHeightThrottle = utils_1.Utils.throttle(() => this.cellHeight(), this.opts.cellHeightThrottle); } this._cellHeightThrottle(); } else { // immediate update if we've changed column count or have no threshold this.cellHeight(); } } // finally update any nested grids this.engine.nodes.forEach(n => { if (n.subGrid) { n.subGrid.onParentResize(); } }); return this; } /** add or remove the window size event handler */ _updateWindowResizeEvent(forceRemove = false) { // 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) const workTodo = (this._isAutoCellHeight || !this.opts.disableOneColumnMode) && !this.opts._isNested; if (!forceRemove && workTodo && !this._windowResizeBind) { this._windowResizeBind = this.onParentResize.bind(this); // so we can properly remove later window.addEventListener('resize', this._windowResizeBind); } else if ((forceRemove || !workTodo) && this._windowResizeBind) { window.removeEventListener('resize', this._windowResizeBind); delete this._windowResizeBind; // remove link to us so we can free } return this; } /** @internal convert a potential selector into actual element */ static getElement(els = '.grid-stack-item') { return utils_1.Utils.getElement(els); } /** @internal */ static getElements(els = '.grid-stack-item') { return utils_1.Utils.getElements(els); } /** @internal */ static getGridElement(els) { return GridStack.getElement(els); } /** @internal */ static getGridElements(els) { return utils_1.Utils.getElements(els); } /** @internal initialize margin top/bottom/left/right and units */ initMargin() { let data; let margin = 0; // support passing multiple values like CSS (ex: '5px 10px 0 20px') let margins = []; if (typeof this.opts.margin === 'string') { margins = this.opts.margin.split(' '); } if (margins.length === 2) { // top/bot, left/right like CSS this.opts.marginTop = this.opts.marginBottom = margins[0]; this.opts.marginLeft = this.opts.marginRight = margins[1]; } else if (margins.length === 4) { // Clockwise like CSS this.opts.marginTop = margins[0]; this.opts.marginRight = margins[1]; this.opts.marginBottom = margins[2]; this.opts.marginLeft = margins[3]; } else { data = utils_1.Utils.parseHeight(this.opts.margin); this.opts.marginUnit = data.unit; margin = this.opts.margin = data.h; } // see if top/bottom/left/right need to be set as well if (this.opts.marginTop === undefined) { this.opts.marginTop = margin; } else { data = utils_1.Utils.parseHeight(this.opts.marginTop); this.opts.marginTop = data.h; delete this.opts.margin; } if (this.opts.marginBottom === undefined) { this.opts.marginBottom = margin; } else { data = utils_1.Utils.parseHeight(this.opts.marginBottom); this.opts.marginBottom = data.h; delete this.opts.margin; } if (this.opts.marginRight === undefined) { this.opts.marginRight = margin; } else { data = utils_1.Utils.parseHeight(this.opts.marginRight); this.opts.marginRight = data.h; delete this.opts.margin; } if (this.opts.marginLeft === undefined) { this.opts.marginLeft = margin; } else { data = utils_1.Utils.parseHeight(this.opts.marginLeft); this.opts.marginLeft = data.h; delete this.opts.margin; } this.opts.marginUnit = data.unit; // in case side were spelled out, use those units instead... if (this.opts.marginTop === this.opts.marginBottom && this.opts.marginLeft === this.opts.marginRight && this.opts.marginTop === this.opts.marginRight) { this.opts.margin = this.opts.marginTop; // makes it easier to check for no-ops in setMargin() } return this; } /* * drag&drop empty stubs that will be implemented in gridstack-dd.ts for non static grid * so we don't incur the load unless needed. * NOTE: had to make those methods public in order to define them else as * GridStack.prototype._setupAcceptWidget = function() * maybe there is a better way ???? */ /* eslint-disable @typescript-eslint/no-unused-vars */ /** * call to setup dragging in from the outside (say toolbar), by specifying the class selection and options. * Called during GridStack.init() as options, but can also be called directly (last param are cached) in case the toolbar * is dynamically create and needs to change later. * @param dragIn string selector (ex: '.sidebar .grid-stack-item') * @param dragInOptions options - see DDDragInOpt. (default: {revert: 'invalid', handle: '.grid-stack-item-content', scroll: false, appendTo: 'body'} **/ static setupDragIn(dragIn, dragInOptions) { } /** * 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. * IF you are looking to prevent an item from moving (due to being pushed around by another during collision) use locked property instead. * @param els widget or selector to modify. * @param val if true widget will be draggable. */ movable(els, val) { return this; } /** * 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. * @param els widget or selector to modify * @param val if true widget will be resizable. */ resizable(els, val) { return this; } /** * Temporarily disables widgets moving/resizing. * If you want a more permanent way (which freezes up resources) use `setStatic(true)` instead. * Note: no-op for static grid * This is a shortcut for: * @example * grid.enableMove(false); * grid.enableResize(false); */ disable() { return this; } /** * Re-enables widgets moving/resizing - see disable(). * Note: no-op for static grid. * This is a shortcut for: * @example * grid.enableMove(true); * grid.enableResize(true); */ enable() { return this; } /** * Enables/disables widget moving. No-op for static grids. */ enableMove(doEnable) { return this; } /** * Enables/disables widget resizing. No-op for static grids. */ enableResize(doEnable) { return this; } /** @internal called to add drag over support to support widgets */ _setupAcceptWidget() { return this; } /** @internal called to setup a trash drop zone if the user specifies it */ _setupRemoveDrop() { return this; } /** @internal prepares the element for drag&drop **/ _prepareDragDropByNode(node) { return this; } /** @internal handles actual drag/resize start **/ _onStartMoving(el, event, ui, node, cellWidth, cellHeight) { return; } /** @internal handles actual drag/resize **/ _dragOrResize(el, event, ui, node, cellWidth, cellHeight) { return; } /** @internal called when a node leaves our area (mouse out or shape outside) **/ _leave(el, helper) { return; } } exports.GridStack = GridStack; /** scoping so users can call GridStack.Utils.sort() for example */ GridStack.Utils = utils_1.Utils; /** scoping so users can call new GridStack.Engine(12) for example */ GridStack.Engine = gridstack_engine_1.GridStackEngine; //# sourceMappingURL=gridstack.js.map /***/ }), /***/ 98: /***/ ((__unused_webpack_module, exports) => { /** * dd-base-impl.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DDBaseImplement = void 0; class DDBaseImplement { constructor() { /** @internal */ this._disabled = false; /** @internal */ this._eventRegister = {}; } /** returns the enable state, but you have to call enable()/disable() to change (as other things need to happen) */ get disabled() { return this._disabled; } on(event, callback) { this._eventRegister[event] = callback; } off(event) { delete this._eventRegister[event]; } enable() { this._disabled = false; } disable() { this._disabled = true; } destroy() { delete this._eventRegister; } triggerEvent(eventName, event) { if (!this.disabled && this._eventRegister && this._eventRegister[eventName]) return this._eventRegister[eventName](event); } } exports.DDBaseImplement = DDBaseImplement; //# sourceMappingURL=dd-base-impl.js.map /***/ }), /***/ 100: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { /** * dd-draggable.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DDDraggable = void 0; const dd_manager_1 = __webpack_require__(94); const dd_utils_1 = __webpack_require__(99); const dd_base_impl_1 = __webpack_require__(98); class DDDraggable extends dd_base_impl_1.DDBaseImplement { constructor(el, option = {}) { super(); /** @internal */ this.dragging = false; /** @internal TODO: set to public as called by DDDroppable! */ this.ui = () => { const containmentEl = this.el.parentElement; const containmentRect = containmentEl.getBoundingClientRect(); const offset = this.helper.getBoundingClientRect(); return { position: { top: offset.top - containmentRect.top, left: offset.left - containmentRect.left } /* not used by GridStack for now... helper: [this.helper], //The object arr representing the helper that's being dragged. offset: { top: offset.top, left: offset.left } // Current offset position of the helper as { top, left } object. */ }; }; this.el = el; this.option = option; // get the element that is actually supposed to be dragged by let className = option.handle.substring(1); this.dragEl = el.classList.contains(className) ? el : el.querySelector(option.handle) || el; // create var event binding so we can easily remove and still look like TS methods (unlike anonymous functions) this._dragStart = this._dragStart.bind(this); this._drag = this._drag.bind(this); this._dragEnd = this._dragEnd.bind(this); this.enable(); } on(event, callback) { super.on(event, callback); } off(event) { super.off(event); } enable() { super.enable(); this.dragEl.draggable = true; this.dragEl.addEventListener('dragstart', this._dragStart); this.el.classList.remove('ui-draggable-disabled'); this.el.classList.add('ui-draggable'); } disable(forDestroy = false) { super.disable(); this.dragEl.removeAttribute('draggable'); this.dragEl.removeEventListener('dragstart', this._dragStart); this.el.classList.remove('ui-draggable'); if (!forDestroy) this.el.classList.add('ui-draggable-disabled'); } destroy() { if (this.dragging) { // Destroy while dragging should remove dragend listener and manually trigger // dragend, otherwise dragEnd can't perform dragstop because eventRegistry is // destroyed. this._dragEnd({}); } this.disable(true); delete this.el; delete this.helper; delete this.option; super.destroy(); } updateOption(opts) { Object.keys(opts).forEach(key => this.option[key] = opts[key]); return this; } /** @internal */ _dragStart(event) { dd_manager_1.DDManager.dragElement = this; this.helper = this._createHelper(event); this._setupHelperContainmentStyle(); this.dragOffset = this._getDragOffset(event, this.el, this.helperContainment); const ev = dd_utils_1.DDUtils.initEvent(event, { target: this.el, type: 'dragstart' }); if (this.helper !== this.el) { this._setupDragFollowNodeNotifyStart(ev); // immediately set external helper initial position to avoid flickering behavior and unnecessary looping in `_packNodes()` this._dragFollow(event); } else { this.dragFollowTimer = window.setTimeout(() => { delete this.dragFollowTimer; this._setupDragFollowNodeNotifyStart(ev); }, 0); } this._cancelDragGhost(event); } /** @internal */ _setupDragFollowNodeNotifyStart(ev) { this._setupHelperStyle(); document.addEventListener('dragover', this._drag, DDDraggable.dragEventListenerOption); this.dragEl.addEventListener('dragend', this._dragEnd); if (this.option.start) { this.option.start(ev, this.ui()); } this.dragging = true; this.helper.classList.add('ui-draggable-dragging'); this.triggerEvent('dragstart', ev); return this; } /** @internal */ _drag(event) { // Safari: prevent default to allow drop to happen instead of reverting back (with animation) and delaying dragend #1541 // https://stackoverflow.com/questions/61760755/how-to-fire-dragend-event-immediately event.preventDefault(); this._dragFollow(event); const ev = dd_utils_1.DDUtils.initEvent(event, { target: this.el, type: 'drag' }); if (this.option.drag) { this.option.drag(ev, this.ui()); } this.triggerEvent('drag', ev); } /** @internal */ _dragEnd(event) { if (this.dragFollowTimer) { clearTimeout(this.dragFollowTimer); delete this.dragFollowTimer; return; } else { if (this.paintTimer) { cancelAnimationFrame(this.paintTimer); } document.removeEventListener('dragover', this._drag, DDDraggable.dragEventListenerOption); this.dragEl.removeEventListener('dragend', this._dragEnd); } this.dragging = false; this.helper.classList.remove('ui-draggable-dragging'); this.helperContainment.style.position = this.parentOriginStylePosition || null; if (this.helper === this.el) { this._removeHelperStyle(); } else { this.helper.remove(); } const ev = dd_utils_1.DDUtils.initEvent(event, { target: this.el, type: 'dragstop' }); if (this.option.stop) { this.option.stop(ev); // Note: ui() not used by gridstack so don't pass } this.triggerEvent('dragstop', ev); delete dd_manager_1.DDManager.dragElement; delete this.helper; } /** @internal create a clone copy (or user defined method) of the original drag item if set */ _createHelper(event) { let helper = this.el; if (typeof this.option.helper === 'function') { helper = this.option.helper(event); } else if (this.option.helper === 'clone') { helper = dd_utils_1.DDUtils.clone(this.el); } if (!document.body.contains(helper)) { dd_utils_1.DDUtils.appendTo(helper, this.option.appendTo === 'parent' ? this.el.parentNode : this.option.appendTo); } if (helper === this.el) { this.dragElementOriginStyle = DDDraggable.originStyleProp.map(prop => this.el.style[prop]); } return helper; } /** @internal */ _setupHelperStyle() { // TODO: set all at once with style.cssText += ... ? https://stackoverflow.com/questions/3968593 const rec = this.helper.getBoundingClientRect(); const style = this.helper.style; style.pointerEvents = 'none'; style['min-width'] = 0; // since we no longer relative to our parent and we don't resize anyway (normally 100/#column %) style.width = this.dragOffset.width + 'px'; style.height = this.dragOffset.height + 'px'; style.willChange = 'left, top'; style.position = 'fixed'; // let us drag between grids by not clipping as parent .grid-stack is position: 'relative' style.left = rec.left + 'px'; style.top = rec.top + 'px'; style.transition = 'none'; // show up instantly setTimeout(() => { if (this.helper) { style.transition = null; // recover animation } }, 0); return this; } /** @internal */ _removeHelperStyle() { // don't bother restoring styles if we're gonna remove anyway... let node = this.helper ? this.helper.gridstackNode : undefined; if (this.dragElementOriginStyle && (!node || !node._isAboutToRemove)) { DDDraggable.originStyleProp.forEach(prop => { this.helper.style[prop] = this.dragElementOriginStyle[prop] || null; }); // show up instantly otherwise we animate to off the grid when switching back to 'absolute' from 'fixed' this.helper.style.transition = 'none'; setTimeout(() => { if (this.helper) { this.helper.style.transition = this.dragElementOriginStyle['transition']; // recover animation } }, 0); } delete this.dragElementOriginStyle; return this; } /** @internal */ _dragFollow(event) { if (this.paintTimer) { cancelAnimationFrame(this.paintTimer); } this.paintTimer = requestAnimationFrame(() => { delete this.paintTimer; const offset = this.dragOffset; let containmentRect = { left: 0, top: 0 }; if (this.helper.style.position === 'absolute') { const { left, top } = this.helperContainment.getBoundingClientRect(); containmentRect = { left, top }; } this.helper.style.left = event.clientX + offset.offsetLeft - containmentRect.left + 'px'; this.helper.style.top = event.clientY + offset.offsetTop - containmentRect.top + 'px'; }); } /** @internal */ _setupHelperContainmentStyle() { this.helperContainment = this.helper.parentElement; if (this.helper.style.position !== 'fixed') { this.parentOriginStylePosition = this.helperContainment.style.position; if (window.getComputedStyle(this.helperContainment).position.match(/static/)) { this.helperContainment.style.position = 'relative'; } } return this; } /** @internal prevent the default ghost image to be created (which has wrong as we move the helper/element instead * (legacy jquery UI code updates the top/left of the item). * TODO: maybe use mouse event instead of HTML5 drag as we have to work around it anyway, or change code to not update * the actual grid-item but move the ghost image around (and special case jq version) ? **/ _cancelDragGhost(e) { /* doesn't seem to do anything... let t = e.dataTransfer; t.effectAllowed = 'none'; t.dropEffect = 'none'; t.setData('text', ''); */ // NOTE: according to spec (and required by Safari see #1540) the image has to be visible in the browser (in dom and not hidden) so make it a 1px div let img = document.createElement('div'); img.style.width = '1px'; img.style.height = '1px'; img.style.position = 'fixed'; // prevent unwanted scrollbar document.body.appendChild(img); e.dataTransfer.setDragImage(img, 0, 0); setTimeout(() => document.body.removeChild(img)); // nuke once drag had a chance to grab this 'image' e.stopPropagation(); return this; } /** @internal */ _getDragOffset(event, el, parent) { // in case ancestor has transform/perspective css properties that change the viewpoint let xformOffsetX = 0; let xformOffsetY = 0; if (parent) { const testEl = document.createElement('div'); dd_utils_1.DDUtils.addElStyles(testEl, { opacity: '0', position: 'fixed', top: 0 + 'px', left: 0 + 'px', width: '1px', height: '1px', zIndex: '-999999', }); parent.appendChild(testEl); const testElPosition = testEl.getBoundingClientRect(); parent.removeChild(testEl); xformOffsetX = testElPosition.left; xformOffsetY = testElPosition.top; // TODO: scale ? } const targetOffset = el.getBoundingClientRect(); return { left: targetOffset.left, top: targetOffset.top, offsetLeft: -event.clientX + targetOffset.left - xformOffsetX, offsetTop: -event.clientY + targetOffset.top - xformOffsetY, width: targetOffset.width, height: targetOffset.height }; } } exports.DDDraggable = DDDraggable; /** @internal #1541 can't have {passive: true} on Safari as otherwise it reverts animate back to old location on drop */ DDDraggable.dragEventListenerOption = true; // DDUtils.isEventSupportPassiveOption ? { capture: true, passive: true } : true; /** @internal */ DDDraggable.originStyleProp = ['transition', 'pointerEvents', 'position', 'left', 'top', 'opacity', 'zIndex', 'width', 'height', 'willChange', 'min-width']; //# sourceMappingURL=dd-draggable.js.map /***/ }), /***/ 101: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { /** * dd-droppable.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DDDroppable = void 0; const dd_manager_1 = __webpack_require__(94); const dd_base_impl_1 = __webpack_require__(98); const dd_utils_1 = __webpack_require__(99); // TEST let count = 0; class DDDroppable extends dd_base_impl_1.DDBaseImplement { constructor(el, opts = {}) { super(); this.el = el; this.option = opts; // create var event binding so we can easily remove and still look like TS methods (unlike anonymous functions) this._dragEnter = this._dragEnter.bind(this); this._dragOver = this._dragOver.bind(this); this._dragLeave = this._dragLeave.bind(this); this._drop = this._drop.bind(this); this.el.classList.add('ui-droppable'); this.el.addEventListener('dragenter', this._dragEnter); this._setupAccept(); } on(event, callback) { super.on(event, callback); } off(event) { super.off(event); } enable() { if (!this.disabled) return; super.enable(); this.el.classList.remove('ui-droppable-disabled'); this.el.addEventListener('dragenter', this._dragEnter); } disable(forDestroy = false) { if (this.disabled) return; super.disable(); if (!forDestroy) this.el.classList.add('ui-droppable-disabled'); this.el.removeEventListener('dragenter', this._dragEnter); } destroy() { this._removeLeaveCallbacks(); this.disable(true); this.el.classList.remove('ui-droppable'); this.el.classList.remove('ui-droppable-disabled'); super.destroy(); } updateOption(opts) { Object.keys(opts).forEach(key => this.option[key] = opts[key]); this._setupAccept(); return this; } /** @internal called when the cursor enters our area - prepare for a possible drop and track leaving */ _dragEnter(event) { // TEST console.log(`${count++} Enter ${(this.el as GridHTMLElement).gridstack.opts.id}`); if (!this._canDrop()) return; event.preventDefault(); event.stopPropagation(); // ignore multiple 'dragenter' as we go over existing items if (this.moving) return; this.moving = true; const ev = dd_utils_1.DDUtils.initEvent(event, { target: this.el, type: 'dropover' }); if (this.option.over) { this.option.over(ev, this._ui(dd_manager_1.DDManager.dragElement)); } this.triggerEvent('dropover', ev); this.el.addEventListener('dragover', this._dragOver); this.el.addEventListener('drop', this._drop); this.el.addEventListener('dragleave', this._dragLeave); // Update: removed that as it causes nested grids to no receive dragenter events when parent drags and sets this for #992. not seeing cursor flicker (chrome). // this.el.classList.add('ui-droppable-over'); // make sure when we enter this, that the last one gets a leave to correctly cleanup as we don't always do if (DDDroppable.lastActive && DDDroppable.lastActive !== this) { DDDroppable.lastActive._dragLeave(event, true); } DDDroppable.lastActive = this; } /** @internal called when an moving to drop item is being dragged over - do nothing but eat the event */ _dragOver(event) { event.preventDefault(); event.stopPropagation(); } /** @internal called when the item is leaving our area, stop tracking if we had moving item */ _dragLeave(event, forceLeave) { var _a; // TEST console.log(`${count++} Leave ${(this.el as GridHTMLElement).gridstack.opts.id}`); event.preventDefault(); event.stopPropagation(); // ignore leave events on our children (we get them when starting to drag our items) // but exclude nested grids since we would still be leaving ourself, // but don't handle leave if we're dragging a nested grid around if (!forceLeave) { let onChild = dd_utils_1.DDUtils.inside(event, this.el); let drag = dd_manager_1.DDManager.dragElement.el; if (onChild && !((_a = drag.gridstackNode) === null || _a === void 0 ? void 0 : _a.subGrid)) { // dragging a nested grid ? let nestedEl = this.el.gridstack.engine.nodes.filter(n => n.subGrid).map(n => n.subGrid.el); onChild = !nestedEl.some(el => dd_utils_1.DDUtils.inside(event, el)); } if (onChild) return; } if (this.moving) { const ev = dd_utils_1.DDUtils.initEvent(event, { target: this.el, type: 'dropout' }); if (this.option.out) { this.option.out(ev, this._ui(dd_manager_1.DDManager.dragElement)); } this.triggerEvent('dropout', ev); } this._removeLeaveCallbacks(); if (DDDroppable.lastActive === this) { delete DDDroppable.lastActive; } } /** @internal item is being dropped on us - call the client drop event */ _drop(event) { if (!this.moving) return; // should not have received event... event.preventDefault(); const ev = dd_utils_1.DDUtils.initEvent(event, { target: this.el, type: 'drop' }); if (this.option.drop) { this.option.drop(ev, this._ui(dd_manager_1.DDManager.dragElement)); } this.triggerEvent('drop', ev); this._removeLeaveCallbacks(); } /** @internal called to remove callbacks when leaving or dropping */ _removeLeaveCallbacks() { if (!this.moving) { return; } delete this.moving; this.el.removeEventListener('dragover', this._dragOver); this.el.removeEventListener('drop', this._drop); this.el.removeEventListener('dragleave', this._dragLeave); // Update: removed that as it causes nested grids to no receive dragenter events when parent drags and sets this for #992. not seeing cursor flicker (chrome). // this.el.classList.remove('ui-droppable-over'); } /** @internal */ _canDrop() { return dd_manager_1.DDManager.dragElement && (!this.accept || this.accept(dd_manager_1.DDManager.dragElement.el)); } /** @internal */ _setupAccept() { if (this.option.accept && typeof this.option.accept === 'string') { this.accept = (el) => { return el.matches(this.option.accept); }; } else { this.accept = this.option.accept; } return this; } /** @internal */ _ui(drag) { return Object.assign({ draggable: drag.el }, drag.ui()); } } exports.DDDroppable = DDDroppable; //# sourceMappingURL=dd-droppable.js.map /***/ }), /***/ 95: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { /** * dd-elements.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DDElement = void 0; const dd_resizable_1 = __webpack_require__(96); const dd_draggable_1 = __webpack_require__(100); const dd_droppable_1 = __webpack_require__(101); class DDElement { constructor(el) { this.el = el; } static init(el) { if (!el.ddElement) { el.ddElement = new DDElement(el); } return el.ddElement; } on(eventName, callback) { if (this.ddDraggable && ['drag', 'dragstart', 'dragstop'].indexOf(eventName) > -1) { this.ddDraggable.on(eventName, callback); } else if (this.ddDroppable && ['drop', 'dropover', 'dropout'].indexOf(eventName) > -1) { this.ddDroppable.on(eventName, callback); } else if (this.ddResizable && ['resizestart', 'resize', 'resizestop'].indexOf(eventName) > -1) { this.ddResizable.on(eventName, callback); } return this; } off(eventName) { if (this.ddDraggable && ['drag', 'dragstart', 'dragstop'].indexOf(eventName) > -1) { this.ddDraggable.off(eventName); } else if (this.ddDroppable && ['drop', 'dropover', 'dropout'].indexOf(eventName) > -1) { this.ddDroppable.off(eventName); } else if (this.ddResizable && ['resizestart', 'resize', 'resizestop'].indexOf(eventName) > -1) { this.ddResizable.off(eventName); } return this; } setupDraggable(opts) { if (!this.ddDraggable) { this.ddDraggable = new dd_draggable_1.DDDraggable(this.el, opts); } else { this.ddDraggable.updateOption(opts); } return this; } cleanDraggable() { if (this.ddDraggable) { this.ddDraggable.destroy(); delete this.ddDraggable; } return this; } setupResizable(opts) { if (!this.ddResizable) { this.ddResizable = new dd_resizable_1.DDResizable(this.el, opts); } else { this.ddResizable.updateOption(opts); } return this; } cleanResizable() { if (this.ddResizable) { this.ddResizable.destroy(); delete this.ddResizable; } return this; } setupDroppable(opts) { if (!this.ddDroppable) { this.ddDroppable = new dd_droppable_1.DDDroppable(this.el, opts); } else { this.ddDroppable.updateOption(opts); } return this; } cleanDroppable() { if (this.ddDroppable) { this.ddDroppable.destroy(); delete this.ddDroppable; } return this; } } exports.DDElement = DDElement; //# sourceMappingURL=dd-element.js.map /***/ }), /***/ 94: /***/ ((__unused_webpack_module, exports) => { /** * dd-manager.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DDManager = void 0; class DDManager { } exports.DDManager = DDManager; //# sourceMappingURL=dd-manager.js.map /***/ }), /***/ 97: /***/ ((__unused_webpack_module, exports) => { /** * dd-resizable-handle.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DDResizableHandle = void 0; class DDResizableHandle { constructor(host, direction, option) { /** @internal true after we've moved enough pixels to start a resize */ this.moving = false; this.host = host; this.dir = direction; this.option = option; // create var event binding so we can easily remove and still look like TS methods (unlike anonymous functions) this._mouseDown = this._mouseDown.bind(this); this._mouseMove = this._mouseMove.bind(this); this._mouseUp = this._mouseUp.bind(this); this._init(); } /** @internal */ _init() { const el = document.createElement('div'); el.classList.add('ui-resizable-handle'); el.classList.add(`${DDResizableHandle.prefix}${this.dir}`); el.style.zIndex = '100'; el.style.userSelect = 'none'; this.el = el; this.host.appendChild(this.el); this.el.addEventListener('mousedown', this._mouseDown); return this; } /** call this when resize handle needs to be removed and cleaned up */ destroy() { if (this.moving) this._mouseUp(this.mouseDownEvent); this.el.removeEventListener('mousedown', this._mouseDown); this.host.removeChild(this.el); delete this.el; delete this.host; return this; } /** @internal called on mouse down on us: capture move on the entire document (mouse might not stay on us) until we release the mouse */ _mouseDown(e) { e.preventDefault(); this.mouseDownEvent = e; document.addEventListener('mousemove', this._mouseMove, true); // capture, not bubble document.addEventListener('mouseup', this._mouseUp); } /** @internal */ _mouseMove(e) { let s = this.mouseDownEvent; // don't start unless we've moved at least 3 pixels if (!this.moving && Math.abs(e.x - s.x) + Math.abs(e.y - s.y) > 2) { this.moving = true; this._triggerEvent('start', this.mouseDownEvent); } else if (this.moving) { this._triggerEvent('move', e); } } /** @internal */ _mouseUp(e) { if (this.moving) { this._triggerEvent('stop', e); } document.removeEventListener('mousemove', this._mouseMove, true); document.removeEventListener('mouseup', this._mouseUp); delete this.moving; delete this.mouseDownEvent; } /** @internal */ _triggerEvent(name, event) { if (this.option[name]) this.option[name](event); return this; } } exports.DDResizableHandle = DDResizableHandle; /** @internal */ DDResizableHandle.prefix = 'ui-resizable-'; //# sourceMappingURL=dd-resizable-handle.js.map /***/ }), /***/ 96: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { /** * dd-resizable.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DDResizable = void 0; const dd_resizable_handle_1 = __webpack_require__(97); const dd_base_impl_1 = __webpack_require__(98); const dd_utils_1 = __webpack_require__(99); const utils_1 = __webpack_require__(90); class DDResizable extends dd_base_impl_1.DDBaseImplement { constructor(el, opts = {}) { super(); /** @internal */ this._showHandlers = () => { this.el.classList.remove('ui-resizable-autohide'); }; /** @internal */ this._hideHandlers = () => { this.el.classList.add('ui-resizable-autohide'); }; /** @internal */ this._ui = () => { const containmentEl = this.el.parentElement; const containmentRect = containmentEl.getBoundingClientRect(); const newRect = { width: this.originalRect.width, height: this.originalRect.height + this.scrolled, left: this.originalRect.left, top: this.originalRect.top - this.scrolled }; const rect = this.temporalRect || newRect; return { position: { left: rect.left - containmentRect.left, top: rect.top - containmentRect.top }, size: { width: rect.width, height: rect.height } /* Gridstack ONLY needs position set above... keep around in case. element: [this.el], // The object representing the element to be resized helper: [], // TODO: not support yet - The object representing the helper that's being resized originalElement: [this.el],// we don't wrap here, so simplify as this.el //The object representing the original element before it is wrapped originalPosition: { // The position represented as { left, top } before the resizable is resized left: this.originalRect.left - containmentRect.left, top: this.originalRect.top - containmentRect.top }, originalSize: { // The size represented as { width, height } before the resizable is resized width: this.originalRect.width, height: this.originalRect.height } */ }; }; this.el = el; this.option = opts; this.enable(); this._setupAutoHide(); this._setupHandlers(); } on(event, callback) { super.on(event, callback); } off(event) { super.off(event); } enable() { super.enable(); this.el.classList.add('ui-resizable'); this.el.classList.remove('ui-resizable-disabled'); } disable() { super.disable(); this.el.classList.add('ui-resizable-disabled'); this.el.classList.remove('ui-resizable'); } destroy() { this._removeHandlers(); if (this.option.autoHide) { this.el.removeEventListener('mouseover', this._showHandlers); this.el.removeEventListener('mouseout', this._hideHandlers); } this.el.classList.remove('ui-resizable'); delete this.el; super.destroy(); } updateOption(opts) { let updateHandles = (opts.handles && opts.handles !== this.option.handles); let updateAutoHide = (opts.autoHide && opts.autoHide !== this.option.autoHide); Object.keys(opts).forEach(key => this.option[key] = opts[key]); if (updateHandles) { this._removeHandlers(); this._setupHandlers(); } if (updateAutoHide) { this._setupAutoHide(); } return this; } /** @internal */ _setupAutoHide() { if (this.option.autoHide) { this.el.classList.add('ui-resizable-autohide'); // use mouseover/mouseout instead of mouseenter/mouseleave to get better performance; this.el.addEventListener('mouseover', this._showHandlers); this.el.addEventListener('mouseout', this._hideHandlers); } else { this.el.classList.remove('ui-resizable-autohide'); this.el.removeEventListener('mouseover', this._showHandlers); this.el.removeEventListener('mouseout', this._hideHandlers); } return this; } /** @internal */ _setupHandlers() { let handlerDirection = this.option.handles || 'e,s,se'; if (handlerDirection === 'all') { handlerDirection = 'n,e,s,w,se,sw,ne,nw'; } this.handlers = handlerDirection.split(',') .map(dir => dir.trim()) .map(dir => new dd_resizable_handle_1.DDResizableHandle(this.el, dir, { start: (event) => { this._resizeStart(event); }, stop: (event) => { this._resizeStop(event); }, move: (event) => { this._resizing(event, dir); } })); return this; } /** @internal */ _resizeStart(event) { this.originalRect = this.el.getBoundingClientRect(); this.scrollEl = utils_1.Utils.getScrollElement(this.el); this.scrollY = this.scrollEl.scrollTop; this.scrolled = 0; this.startEvent = event; this._setupHelper(); this._applyChange(); const ev = dd_utils_1.DDUtils.initEvent(event, { type: 'resizestart', target: this.el }); if (this.option.start) { this.option.start(ev, this._ui()); } this.el.classList.add('ui-resizable-resizing'); this.triggerEvent('resizestart', ev); return this; } /** @internal */ _resizing(event, dir) { this.scrolled = this.scrollEl.scrollTop - this.scrollY; this.temporalRect = this._getChange(event, dir); this._applyChange(); const ev = dd_utils_1.DDUtils.initEvent(event, { type: 'resize', target: this.el }); if (this.option.resize) { this.option.resize(ev, this._ui()); } this.triggerEvent('resize', ev); return this; } /** @internal */ _resizeStop(event) { const ev = dd_utils_1.DDUtils.initEvent(event, { type: 'resizestop', target: this.el }); if (this.option.stop) { this.option.stop(ev); // Note: ui() not used by gridstack so don't pass } this.el.classList.remove('ui-resizable-resizing'); this.triggerEvent('resizestop', ev); this._cleanHelper(); delete this.startEvent; delete this.originalRect; delete this.temporalRect; delete this.scrollY; delete this.scrolled; return this; } /** @internal */ _setupHelper() { this.elOriginStyleVal = DDResizable._originStyleProp.map(prop => this.el.style[prop]); this.parentOriginStylePosition = this.el.parentElement.style.position; if (window.getComputedStyle(this.el.parentElement).position.match(/static/)) { this.el.parentElement.style.position = 'relative'; } this.el.style.position = 'absolute'; this.el.style.opacity = '0.8'; return this; } /** @internal */ _cleanHelper() { DDResizable._originStyleProp.forEach((prop, i) => { this.el.style[prop] = this.elOriginStyleVal[i] || null; }); this.el.parentElement.style.position = this.parentOriginStylePosition || null; return this; } /** @internal */ _getChange(event, dir) { const oEvent = this.startEvent; const newRect = { width: this.originalRect.width, height: this.originalRect.height + this.scrolled, left: this.originalRect.left, top: this.originalRect.top - this.scrolled }; const offsetX = event.clientX - oEvent.clientX; const offsetY = event.clientY - oEvent.clientY; if (dir.indexOf('e') > -1) { newRect.width += offsetX; } else if (dir.indexOf('w') > -1) { newRect.width -= offsetX; newRect.left += offsetX; } if (dir.indexOf('s') > -1) { newRect.height += offsetY; } else if (dir.indexOf('n') > -1) { newRect.height -= offsetY; newRect.top += offsetY; } const constrain = this._constrainSize(newRect.width, newRect.height); if (Math.round(newRect.width) !== Math.round(constrain.width)) { // round to ignore slight round-off errors if (dir.indexOf('w') > -1) { newRect.left += newRect.width - constrain.width; } newRect.width = constrain.width; } if (Math.round(newRect.height) !== Math.round(constrain.height)) { if (dir.indexOf('n') > -1) { newRect.top += newRect.height - constrain.height; } newRect.height = constrain.height; } return newRect; } /** @internal constrain the size to the set min/max values */ _constrainSize(oWidth, oHeight) { const maxWidth = this.option.maxWidth || Number.MAX_SAFE_INTEGER; const minWidth = this.option.minWidth || oWidth; const maxHeight = this.option.maxHeight || Number.MAX_SAFE_INTEGER; const minHeight = this.option.minHeight || oHeight; const width = Math.min(maxWidth, Math.max(minWidth, oWidth)); const height = Math.min(maxHeight, Math.max(minHeight, oHeight)); return { width, height }; } /** @internal */ _applyChange() { let containmentRect = { left: 0, top: 0, width: 0, height: 0 }; if (this.el.style.position === 'absolute') { const containmentEl = this.el.parentElement; const { left, top } = containmentEl.getBoundingClientRect(); containmentRect = { left, top, width: 0, height: 0 }; } if (!this.temporalRect) return this; Object.keys(this.temporalRect).forEach(key => { const value = this.temporalRect[key]; this.el.style[key] = value - containmentRect[key] + 'px'; }); return this; } /** @internal */ _removeHandlers() { this.handlers.forEach(handle => handle.destroy()); delete this.handlers; return this; } } exports.DDResizable = DDResizable; /** @internal */ DDResizable._originStyleProp = ['width', 'height', 'position', 'left', 'top', 'opacity', 'zIndex']; //# sourceMappingURL=dd-resizable.js.map /***/ }), /***/ 99: /***/ ((__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DDUtils = void 0; /** * dd-utils.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ class DDUtils { static clone(el) { const node = el.cloneNode(true); node.removeAttribute('id'); return node; } static appendTo(el, parent) { let parentNode; if (typeof parent === 'string') { parentNode = document.querySelector(parent); } else { parentNode = parent; } if (parentNode) { parentNode.appendChild(el); } } static setPositionRelative(el) { if (!(/^(?:r|a|f)/).test(window.getComputedStyle(el).position)) { el.style.position = "relative"; } } static addElStyles(el, styles) { if (styles instanceof Object) { for (const s in styles) { if (styles.hasOwnProperty(s)) { if (Array.isArray(styles[s])) { // support fallback value styles[s].forEach(val => { el.style[s] = val; }); } else { el.style[s] = styles[s]; } } } } } static initEvent(e, info) { const evt = { type: info.type }; const obj = { button: 0, which: 0, buttons: 1, bubbles: true, cancelable: true, target: info.target ? info.target : e.target }; // don't check for `instanceof DragEvent` as Safari use MouseEvent #1540 if (e.dataTransfer) { evt['dataTransfer'] = e.dataTransfer; // workaround 'readonly' field. } ['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].forEach(p => evt[p] = e[p]); // keys ['pageX', 'pageY', 'clientX', 'clientY', 'screenX', 'screenY'].forEach(p => evt[p] = e[p]); // point info return Object.assign(Object.assign({}, evt), obj); } /** returns true if event is inside the given element rectangle */ // Note: Safari Mac has null event.relatedTarget which causes #1684 so check if DragEvent is inside the coordinates instead // this.el.contains(event.relatedTarget as HTMLElement) static inside(e, el) { // srcElement, toElement, target: all set to placeholder when leaving simple grid, so we can't use that (Chrome) let target = e.relatedTarget || e.fromElement; if (!target) { const { bottom, left, right, top } = el.getBoundingClientRect(); return (e.x < right && e.x > left && e.y < bottom && e.y > top); } return el.contains(target); } } exports.DDUtils = DDUtils; DDUtils.isEventSupportPassiveOption = ((() => { let supportsPassive = false; let passiveTest = () => { // do nothing }; document.addEventListener('test', passiveTest, { get passive() { supportsPassive = true; return true; } }); document.removeEventListener('test', passiveTest); return supportsPassive; })()); //# sourceMappingURL=dd-utils.js.map /***/ }), /***/ 93: /***/ (function(__unused_webpack_module, exports, __webpack_require__) { /** * gridstack-dd-native.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GridStackDDNative = void 0; const dd_manager_1 = __webpack_require__(94); const dd_element_1 = __webpack_require__(95); const gridstack_dd_1 = __webpack_require__(102); const utils_1 = __webpack_require__(90); // export our base class (what user should use) and all associated types __exportStar(__webpack_require__(102), exports); /** * HTML 5 Native DragDrop based drag'n'drop plugin. */ class GridStackDDNative extends gridstack_dd_1.GridStackDD { resizable(el, opts, key, value) { this._getDDElements(el).forEach(dEl => { if (opts === 'disable' || opts === 'enable') { dEl.ddResizable && dEl.ddResizable[opts](); // can't create DD as it requires options for setupResizable() } else if (opts === 'destroy') { dEl.ddResizable && dEl.cleanResizable(); } else if (opts === 'option') { dEl.setupResizable({ [key]: value }); } else { const grid = dEl.el.gridstackNode.grid; let handles = dEl.el.getAttribute('gs-resize-handles') ? dEl.el.getAttribute('gs-resize-handles') : grid.opts.resizable.handles; dEl.setupResizable(Object.assign(Object.assign(Object.assign({}, grid.opts.resizable), { handles: handles }), { start: opts.start, stop: opts.stop, resize: opts.resize })); } }); return this; } draggable(el, opts, key, value) { this._getDDElements(el).forEach(dEl => { if (opts === 'disable' || opts === 'enable') { dEl.ddDraggable && dEl.ddDraggable[opts](); // can't create DD as it requires options for setupDraggable() } else if (opts === 'destroy') { dEl.ddDraggable && dEl.cleanDraggable(); } else if (opts === 'option') { dEl.setupDraggable({ [key]: value }); } else { const grid = dEl.el.gridstackNode.grid; dEl.setupDraggable(Object.assign(Object.assign({}, grid.opts.draggable), { containment: (grid.opts._isNested && !grid.opts.dragOut) ? grid.el.parentElement : (grid.opts.draggable.containment || null), start: opts.start, stop: opts.stop, drag: opts.drag })); } }); return this; } dragIn(el, opts) { this._getDDElements(el).forEach(dEl => dEl.setupDraggable(opts)); return this; } droppable(el, opts, key, value) { if (typeof opts.accept === 'function' && !opts._accept) { opts._accept = opts.accept; opts.accept = (el) => opts._accept(el); } this._getDDElements(el).forEach(dEl => { if (opts === 'disable' || opts === 'enable') { dEl.ddDroppable && dEl.ddDroppable[opts](); } else if (opts === 'destroy') { if (dEl.ddDroppable) { // error to call destroy if not there dEl.cleanDroppable(); } } else if (opts === 'option') { dEl.setupDroppable({ [key]: value }); } else { dEl.setupDroppable(opts); } }); return this; } /** true if element is droppable */ isDroppable(el) { return !!(el && el.ddElement && el.ddElement.ddDroppable && !el.ddElement.ddDroppable.disabled); } /** true if element is draggable */ isDraggable(el) { return !!(el && el.ddElement && el.ddElement.ddDraggable && !el.ddElement.ddDraggable.disabled); } /** true if element is draggable */ isResizable(el) { return !!(el && el.ddElement && el.ddElement.ddResizable && !el.ddElement.ddResizable.disabled); } on(el, name, callback) { this._getDDElements(el).forEach(dEl => dEl.on(name, (event) => { callback(event, dd_manager_1.DDManager.dragElement ? dd_manager_1.DDManager.dragElement.el : event.target, dd_manager_1.DDManager.dragElement ? dd_manager_1.DDManager.dragElement.helper : null); })); return this; } off(el, name) { this._getDDElements(el).forEach(dEl => dEl.off(name)); return this; } /** @internal returns a list of DD elements, creating them on the fly by default */ _getDDElements(els, create = true) { let hosts = utils_1.Utils.getElements(els); if (!hosts.length) return []; let list = hosts.map(e => e.ddElement || (create ? dd_element_1.DDElement.init(e) : null)); if (!create) { list.filter(d => d); } // remove nulls return list; } } exports.GridStackDDNative = GridStackDDNative; // finally register ourself gridstack_dd_1.GridStackDD.registerPlugin(GridStackDDNative); //# sourceMappingURL=gridstack-dd-native.js.map /***/ }), /***/ 92: /***/ ((__unused_webpack_module, exports) => { /** * types.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); //# sourceMappingURL=types.js.map /***/ }), /***/ 90: /***/ ((__unused_webpack_module, exports) => { /** * utils.ts 5.0 * Copyright (c) 2021 Alain Dumesny - see GridStack root license */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Utils = exports.obsoleteAttr = exports.obsoleteOptsDel = exports.obsoleteOpts = exports.obsolete = void 0; /** checks for obsolete method names */ // eslint-disable-next-line function obsolete(self, f, oldName, newName, rev) { let wrapper = (...args) => { console.warn('gridstack.js: Function `' + oldName + '` is deprecated in ' + rev + ' and has been replaced ' + 'with `' + newName + '`. It will be **completely** removed in v1.0'); return f.apply(self, args); }; wrapper.prototype = f.prototype; return wrapper; } exports.obsolete = obsolete; /** checks for obsolete grid options (can be used for any fields, but msg is about options) */ function obsoleteOpts(opts, oldName, newName, rev) { if (opts[oldName] !== undefined) { opts[newName] = opts[oldName]; console.warn('gridstack.js: Option `' + oldName + '` is deprecated in ' + rev + ' and has been replaced with `' + newName + '`. It will be **completely** removed in v1.0'); } } exports.obsoleteOpts = obsoleteOpts; /** checks for obsolete grid options which are gone */ function obsoleteOptsDel(opts, oldName, rev, info) { if (opts[oldName] !== undefined) { console.warn('gridstack.js: Option `' + oldName + '` is deprecated in ' + rev + info); } } exports.obsoleteOptsDel = obsoleteOptsDel; /** checks for obsolete Jquery element attributes */ function obsoleteAttr(el, oldName, newName, rev) { let oldAttr = el.getAttribute(oldName); if (oldAttr !== null) { el.setAttribute(newName, oldAttr); console.warn('gridstack.js: attribute `' + oldName + '`=' + oldAttr + ' is deprecated on this object in ' + rev + ' and has been replaced with `' + newName + '`. It will be **completely** removed in v1.0'); } } exports.obsoleteAttr = obsoleteAttr; /** * Utility methods */ class Utils { /** convert a potential selector into actual list of html elements */ static getElements(els) { if (typeof els === 'string') { let list = document.querySelectorAll(els); if (!list.length && els[0] !== '.' && els[0] !== '#') { list = document.querySelectorAll('.' + els); if (!list.length) { list = document.querySelectorAll('#' + els); } } return Array.from(list); } return [els]; } /** convert a potential selector into actual single element */ static getElement(els) { if (typeof els === 'string') { if (!els.length) return null; if (els[0] === '#') { return document.getElementById(els.substring(1)); } if (els[0] === '.' || els[0] === '[') { return document.querySelector(els); } // if we start with a digit, assume it's an id (error calling querySelector('#1')) as class are not valid CSS if (!isNaN(+els[0])) { // start with digit return document.getElementById(els); } // finally try string, then id then class let el = document.querySelector(els); if (!el) { el = document.getElementById(els); } if (!el) { el = document.querySelector('.' + els); } return el; } return els; } /** returns true if a and b overlap */ static isIntercepted(a, b) { return !(a.y >= b.y + b.h || a.y + a.h <= b.y || a.x + a.w <= b.x || a.x >= b.x + b.w); } /** returns true if a and b touch edges or corners */ static isTouching(a, b) { return Utils.isIntercepted(a, { x: b.x - 0.5, y: b.y - 0.5, w: b.w + 1, h: b.h + 1 }); } /** * Sorts array of nodes * @param nodes array to sort * @param dir 1 for asc, -1 for desc (optional) * @param width width of the grid. If undefined the width will be calculated automatically (optional). **/ static sort(nodes, dir, column) { column = column || nodes.reduce((col, n) => Math.max(n.x + n.w, col), 0) || 12; if (dir === -1) return nodes.sort((a, b) => (b.x + b.y * column) - (a.x + a.y * column)); else return nodes.sort((b, a) => (b.x + b.y * column) - (a.x + a.y * column)); } /** * creates a style sheet with style id under given parent * @param id will set the 'gs-style-id' attribute to that id * @param parent to insert the stylesheet as first child, * if none supplied it will be appended to the document head instead. */ static createStylesheet(id, parent) { let style = document.createElement('style'); style.setAttribute('type', 'text/css'); style.setAttribute('gs-style-id', id); // eslint-disable-next-line @typescript-eslint/no-explicit-any if (style.styleSheet) { // TODO: only CSSImportRule have that and different beast ?? // eslint-disable-next-line @typescript-eslint/no-explicit-any style.styleSheet.cssText = ''; } else { style.appendChild(document.createTextNode('')); // WebKit hack } if (!parent) { // default to head parent = document.getElementsByTagName('head')[0]; parent.appendChild(style); } else { parent.insertBefore(style, parent.firstChild); } return style.sheet; } /** removed the given stylesheet id */ static removeStylesheet(id) { let el = document.querySelector('STYLE[gs-style-id=' + id + ']'); if (el && el.parentNode) el.remove(); } /** inserts a CSS rule */ static addCSSRule(sheet, selector, rules) { if (typeof sheet.addRule === 'function') { sheet.addRule(selector, rules); } else if (typeof sheet.insertRule === 'function') { sheet.insertRule(`${selector}{${rules}}`); } } // eslint-disable-next-line @typescript-eslint/no-explicit-any static toBool(v) { if (typeof v === 'boolean') { return v; } if (typeof v === 'string') { v = v.toLowerCase(); return !(v === '' || v === 'no' || v === 'false' || v === '0'); } return Boolean(v); } static toNumber(value) { return (value === null || value.length === 0) ? undefined : Number(value); } static parseHeight(val) { let h; let unit = 'px'; if (typeof val === 'string') { let match = val.match(/^(-[0-9]+\.[0-9]+|[0-9]*\.[0-9]+|-[0-9]+|[0-9]+)(px|em|rem|vh|vw|%)?$/); if (!match) { throw new Error('Invalid height'); } unit = match[2] || 'px'; h = parseFloat(match[1]); } else { h = val; } return { h, unit }; } /** copies unset fields in target to use the given default sources values */ // eslint-disable-next-line static defaults(target, ...sources) { sources.forEach(source => { for (const key in source) { if (!source.hasOwnProperty(key)) return; if (target[key] === null || target[key] === undefined) { target[key] = source[key]; } else if (typeof source[key] === 'object' && typeof target[key] === 'object') { // property is an object, recursively add it's field over... #1373 this.defaults(target[key], source[key]); } } }); return target; } /** given 2 objects return true if they have the same values. Checks for Object {} having same fields and values (just 1 level down) */ static same(a, b) { if (typeof a !== 'object') return a == b; if (typeof a !== typeof b) return false; // else we have object, check just 1 level deep for being same things... if (Object.keys(a).length !== Object.keys(b).length) return false; for (const key in a) { if (a[key] !== b[key]) return false; } return true; } /** copies over b size & position (GridStackPosition), and possibly min/max as well */ static copyPos(a, b, minMax = false) { a.x = b.x; a.y = b.y; a.w = b.w; a.h = b.h; if (!minMax) return a; if (b.minW) a.minW = b.minW; if (b.minH) a.minH = b.minH; if (b.maxW) a.maxW = b.maxW; if (b.maxH) a.maxH = b.maxH; return a; } /** true if a and b has same size & position */ static samePos(a, b) { return a && b && a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h; } /** removes field from the first object if same as the second objects (like diffing) and internal '_' for saving */ static removeInternalAndSame(a, b) { if (typeof a !== 'object' || typeof b !== 'object') return; for (let key in a) { let val = a[key]; if (key[0] === '_' || val === b[key]) { delete a[key]; } else if (val && typeof val === 'object' && b[key] !== undefined) { for (let i in val) { if (val[i] === b[key][i] || i[0] === '_') { delete val[i]; } } if (!Object.keys(val).length) { delete a[key]; } } } } /** return the closest parent (or itself) matching the given class */ static closestByClass(el, name) { while (el) { if (el.classList.contains(name)) return el; el = el.parentElement; } return null; } /** delay calling the given function for given delay, preventing new calls from happening while waiting */ static throttle(func, delay) { let isWaiting = false; return (...args) => { if (!isWaiting) { isWaiting = true; setTimeout(() => { func(...args); isWaiting = false; }, delay); } }; } static removePositioningStyles(el) { let style = el.style; if (style.position) { style.removeProperty('position'); } if (style.left) { style.removeProperty('left'); } if (style.top) { style.removeProperty('top'); } if (style.width) { style.removeProperty('width'); } if (style.height) { style.removeProperty('height'); } } /** @internal returns the passed element if scrollable, else the closest parent that will, up to the entire document scrolling element */ static getScrollElement(el) { if (!el) return document.scrollingElement || document.documentElement; // IE support const style = getComputedStyle(el); const overflowRegex = /(auto|scroll)/; if (overflowRegex.test(style.overflow + style.overflowY)) { return el; } else { return this.getScrollElement(el.parentElement); } } /** @internal */ static updateScrollPosition(el, position, distance) { // is widget in view? let rect = el.getBoundingClientRect(); let innerHeightOrClientHeight = (window.innerHeight || document.documentElement.clientHeight); if (rect.top < 0 || rect.bottom > innerHeightOrClientHeight) { // set scrollTop of first parent that scrolls // if parent is larger than el, set as low as possible // to get entire widget on screen let offsetDiffDown = rect.bottom - innerHeightOrClientHeight; let offsetDiffUp = rect.top; let scrollEl = this.getScrollElement(el); if (scrollEl !== null) { let prevScroll = scrollEl.scrollTop; if (rect.top < 0 && distance < 0) { // moving up if (el.offsetHeight > innerHeightOrClientHeight) { scrollEl.scrollTop += distance; } else { scrollEl.scrollTop += Math.abs(offsetDiffUp) > Math.abs(distance) ? distance : offsetDiffUp; } } else if (distance > 0) { // moving down if (el.offsetHeight > innerHeightOrClientHeight) { scrollEl.scrollTop += distance; } else { scrollEl.scrollTop += offsetDiffDown > distance ? distance : offsetDiffDown; } } // move widget y by amount scrolled position.top += scrollEl.scrollTop - prevScroll; } } } /** * @internal Function used to scroll the page. * * @param event `MouseEvent` that triggers the resize * @param el `HTMLElement` that's being resized * @param distance Distance from the V edges to start scrolling */ static updateScrollResize(event, el, distance) { const scrollEl = this.getScrollElement(el); const height = scrollEl.clientHeight; // #1727 event.clientY is relative to viewport, so must compare this against position of scrollEl getBoundingClientRect().top // #1745 Special situation if scrollEl is document 'html': here browser spec states that // clientHeight is height of viewport, but getBoundingClientRect() is rectangle of html element; // this discrepancy arises because in reality scrollbar is attached to viewport, not html element itself. const offsetTop = (scrollEl === this.getScrollElement()) ? 0 : scrollEl.getBoundingClientRect().top; const pointerPosY = event.clientY - offsetTop; const top = pointerPosY < distance; const bottom = pointerPosY > height - distance; if (top) { // This also can be done with a timeout to keep scrolling while the mouse is // in the scrolling zone. (will have smoother behavior) scrollEl.scrollBy({ behavior: 'smooth', top: pointerPosY - distance }); } else if (bottom) { scrollEl.scrollBy({ behavior: 'smooth', top: distance - (height - pointerPosY) }); } } /** single level clone, returning a new object with same top fields. This will share sub objects and arrays */ static clone(obj) { if (obj === null || obj === undefined || typeof (obj) !== 'object') { return obj; } // return Object.assign({}, obj); if (obj instanceof Array) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return [...obj]; } return Object.assign({}, obj); } /** * Recursive clone version that returns a full copy, checking for nested objects and arrays ONLY. * Note: this will use as-is any key starting with double __ (and not copy inside) some lib have circular dependencies. */ static cloneDeep(obj) { // return JSON.parse(JSON.stringify(obj)); // doesn't work with date format ? const ret = Utils.clone(obj); for (const key in ret) { // NOTE: we don't support function/circular dependencies so skip those properties for now... if (ret.hasOwnProperty(key) && typeof (ret[key]) === 'object' && key.substring(0, 2) !== '__' && !skipFields.find(k => k === key)) { ret[key] = Utils.cloneDeep(obj[key]); } } return ret; } } exports.Utils = Utils; // list of fields we will skip during cloneDeep (nested objects, other internal) const skipFields = ['_isNested', 'el', 'grid', 'subGrid', 'engine']; //# sourceMappingURL=utils.js.map /***/ }), /***/ 103: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); // extracted by mini-css-extract-plugin /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /* webpack/runtime/compat get default export */ /******/ (() => { /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = (module) => { /******/ var getter = module && module.__esModule ? /******/ () => (module['default']) : /******/ () => (module); /******/ __webpack_require__.d(getter, { a: getter }); /******/ return getter; /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter functions for harmony exports /******/ __webpack_require__.d = (exports, definition) => { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /******/ /* webpack/runtime/make namespace object */ /******/ (() => { /******/ // define __esModule on exports /******/ __webpack_require__.r = (exports) => { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = {}; __webpack_require__.r(__webpack_exports__); /* harmony import */ var gridstack__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(88); /* harmony import */ var gridstack__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(gridstack__WEBPACK_IMPORTED_MODULE_0__); /** * --------------------------------------------------------------------- * * GLPI - Gestionnaire Libre de Parc Informatique * * http://glpi-project.org * * @copyright 2015-2024 Teclib' and contributors. * @copyright 2003-2014 by the INDEPNET Development Team. * @licence https://www.gnu.org/licenses/gpl-3.0.html * * --------------------------------------------------------------------- * * LICENSE * * This file is part of GLPI. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * * --------------------------------------------------------------------- */ // Gridstack lib window.GridStack = gridstack__WEBPACK_IMPORTED_MODULE_0__.GridStack; __webpack_require__(93); __webpack_require__(103); /******/ })() ; //# sourceMappingURL=gridstack.js.map