소재지 ₍₍◝(・'ω'・)◟⁾⁾ 🐟️?看XM(^_−)☆哈先看看刚看过卡卡国看过了回来冷藏柜好极过估计
PNG %k25u25%fgd5n!Espo.loader.setContextId('lib!gridstack');
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("jquery"), require("jquery-ui"), require("jquery-ui-touch-punch"));
else if(typeof define === 'function' && define.amd)
define(["jquery", "jquery-ui", "jquery-ui-touch-punch"], factory);
else if(typeof exports === 'object')
exports["GridStack"] = factory(require("jquery"), require("jquery-ui"), require("jquery-ui-touch-punch"));
else
root["GridStack"] = factory(root["jquery"], root["jquery-ui"], root["jquery-ui-touch-punch"]);
})(self, function(__WEBPACK_EXTERNAL_MODULE__273__, __WEBPACK_EXTERNAL_MODULE__946__, __WEBPACK_EXTERNAL_MODULE__858__) {
return /******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ 21:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
/**
* gridstack-dd.ts 5.1.1
* 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__(334);
const gridstack_1 = __webpack_require__(270);
const utils_1 = __webpack_require__(593);
// 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;
};
/***/ }),
/***/ 334:
/***/ ((__unused_webpack_module, exports) => {
/**
* gridstack-ddi.ts 5.1.1
* 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;
/***/ }),
/***/ 62:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
/**
* gridstack-engine.ts 5.1.1
* Copyright (c) 2021-2022 Alain Dumesny - see GridStack root license
*/
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.GridStackEngine = void 0;
const utils_1 = __webpack_require__(593);
/**
* 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.maxRow = opts.maxRow;
this._float = opts.float;
this.nodes = opts.nodes || [];
this.onChange = opts.onChange;
}
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
return this.saveInitial(); // since begin update (which is called multiple times) won't do 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; }
/** sort the nodes array from first to last, or reverse. Called during collision/placement to force an order */
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;
}
/** returns a list of modified nodes from their original values */
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 callback with dirty nodes so DOM can be updated */
_notify(removedNodes) {
if (this.batchMode || !this.onChange)
return this;
let dirtyNodes = (removedNodes || []).concat(this.getDirtyNodes());
this.onChange(dirtyNodes);
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) {
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;
// make sure we are still valid size
let canMove = clone.moveNode(clonedNode, o) && 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) {
// first make sure w,h are set for caller
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);
// check if we will need to fix collision at our new location
let collides = this.collideAll(node, nn, 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;
/***/ }),
/***/ 572:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
/**
* index-jq.ts 5.1.1 - everything you need for a Grid that uses Jquery-ui drag&drop (original, full feature)
* 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 }));
__exportStar(__webpack_require__(699), exports);
__exportStar(__webpack_require__(593), exports);
__exportStar(__webpack_require__(62), exports);
__exportStar(__webpack_require__(334), exports);
__exportStar(__webpack_require__(270), exports);
__exportStar(__webpack_require__(906), exports);
// declare module 'gridstack'; for umd ?
/***/ }),
/***/ 270:
/***/ (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.1.1
* https://gridstackjs.com/
*
* Copyright (c) 2021-2022 Alain Dumesny
* see root license https://github.com/gridstack/gridstack.js/tree/master/LICENSE
*/
const gridstack_engine_1 = __webpack_require__(62);
const utils_1 = __webpack_require__(593);
const gridstack_ddi_1 = __webpack_require__(334);
// export all dependent file as well to make it easier for users to just import the main file
__exportStar(__webpack_require__(699), exports);
__exportStar(__webpack_require__(593), exports);
__exportStar(__webpack_require__(62), exports);
__exportStar(__webpack_require__(334), 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,
oneColumnSize: 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
*
*/
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;
}
// 'minWidth' legacy support in 5.1
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
let anyOpts = opts;
if (anyOpts.minWidth !== undefined) {
opts.oneColumnSize = opts.oneColumnSize || anyOpts.minWidth;
delete anyOpts.minWidth;
}
// 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.oneColumnSize) {
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();
let engineClass = this.opts.engineClass || GridStack.engineClass || gridstack_engine_1.GridStackEngine;
this.engine = new engineClass({
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 = ``;
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;
}
/** call this method to register your engine instead of the default one.
* See instead `GridStackOptions.engineClass` if you only need to
* replace just one instance.
*/
static registerEngine(engineClass) {
GridStack.engineClass = engineClass;
}
/** @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('', {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 = ``;
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('');
* 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.oneColumnSize;
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;
/***/ }),
/***/ 906:
/***/ (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.GridStackDDJQueryUI = exports.$ = void 0;
const gridstack_dd_1 = __webpack_require__(21);
// export jq symbols see
// https://stackoverflow.com/questions/35345760/importing-jqueryui-with-typescript-and-requirejs
// https://stackoverflow.com/questions/33998262/jquery-ui-and-webpack-how-to-manage-it-into-module
//
// Note: user should be able to bring their own by changing their webpack aliases like
// alias: {
// 'jquery': 'gridstack/dist/jq/jquery.js',
// 'jquery-ui': 'gridstack/dist/jq/jquery-ui.js',
// 'jquery.ui': 'gridstack/dist/jq/jquery-ui.js',
// 'jquery.ui.touch-punch': 'gridstack/dist/jq/jquery.ui.touch-punch.js',
// },
const $ = __webpack_require__(273); // compile this in... having issues TS/ES6 app would include instead
exports.$ = $;
__webpack_require__(946);
__webpack_require__(858); // include for touch mobile devices
// export our base class (what user should use) and all associated types
__exportStar(__webpack_require__(21), exports);
/**
* legacy Jquery-ui based drag'n'drop plugin.
*/
class GridStackDDJQueryUI extends gridstack_dd_1.GridStackDD {
resizable(el, opts, key, value) {
let $el = $(el);
if (opts === 'enable') {
$el.resizable().resizable(opts);
}
else if (opts === 'disable' || opts === 'destroy') {
if ($el.data('ui-resizable')) { // error to call destroy if not there
$el.resizable(opts);
}
}
else if (opts === 'option') {
$el.resizable(opts, key, value);
}
else {
const grid = el.gridstackNode.grid;
let handles = $el.data('gs-resize-handles') ? $el.data('gs-resize-handles') : grid.opts.resizable.handles;
$el.resizable(Object.assign(Object.assign(Object.assign({}, grid.opts.resizable), { handles: handles }), {
start: opts.start,
stop: opts.stop,
resize: opts.resize // || function() {}
}));
}
return this;
}
draggable(el, opts, key, value) {
let $el = $(el);
if (opts === 'enable') {
$el.draggable().draggable('enable');
}
else if (opts === 'disable' || opts === 'destroy') {
if ($el.data('ui-draggable')) { // error to call destroy if not there
$el.draggable(opts);
}
}
else if (opts === 'option') {
$el.draggable(opts, key, value);
}
else {
const grid = el.gridstackNode.grid;
$el.draggable(Object.assign(Object.assign({}, grid.opts.draggable), {
containment: (grid.opts._isNested && !grid.opts.dragOut) ?
$(grid.el).parent() : (grid.opts.draggable.containment || null),
start: opts.start,
stop: opts.stop,
drag: opts.drag // || function() {}
}));
}
return this;
}
dragIn(el, opts) {
let $el = $(el); // workaround Type 'string' is not assignable to type 'PlainObject' - see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/29312
$el.draggable(opts);
return this;
}
droppable(el, opts, key, value) {
let $el = $(el);
if (typeof opts.accept === 'function' && !opts._accept) {
// convert jquery event to generic element
opts._accept = opts.accept;
opts.accept = ($el) => opts._accept($el.get(0));
}
if (opts === 'disable' || opts === 'destroy') {
if ($el.data('ui-droppable')) { // error to call destroy if not there
$el.droppable(opts);
}
}
else {
$el.droppable(opts, key, value);
}
return this;
}
isDroppable(el) {
let $el = $(el);
return Boolean($el.data('ui-droppable'));
}
isDraggable(el) {
let $el = $(el);
return Boolean($el.data('ui-draggable'));
}
isResizable(el) {
let $el = $(el);
return Boolean($el.data('ui-resizable'));
}
on(el, name, callback) {
let $el = $(el);
$el.on(name, (event, ui) => { return callback(event, ui.draggable ? ui.draggable[0] : event.target, ui.helper ? ui.helper[0] : null); });
return this;
}
off(el, name) {
let $el = $(el);
$el.off(name);
return this;
}
}
exports.GridStackDDJQueryUI = GridStackDDJQueryUI;
// finally register ourself
gridstack_dd_1.GridStackDD.registerPlugin(GridStackDDJQueryUI);
/***/ }),
/***/ 699:
/***/ ((__unused_webpack_module, exports) => {
/**
* types.ts 5.1.1
* Copyright (c) 2021 Alain Dumesny - see GridStack root license
*/
Object.defineProperty(exports, "__esModule", ({ value: true }));
/***/ }),
/***/ 593:
/***/ ((__unused_webpack_module, exports) => {
/**
* utils.ts 5.1.1
* 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, doMinMax = false) {
a.x = b.x;
a.y = b.y;
a.w = b.w;
a.h = b.h;
if (doMinMax) {
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) {
// Espo
// fixes fast scroll issue
return;
// 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) {
// Espo
// fixes fast scroll issue
return;
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'];
/***/ }),
/***/ 273:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__273__;
/***/ }),
/***/ 946:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__946__;
/***/ }),
/***/ 858:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__858__;
/***/ })
/******/ });
/************************************************************************/
/******/ // 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;
/******/ }
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module is referenced by other modules so it can't be inlined
/******/ var __webpack_exports__ = __webpack_require__(572);
/******/ __webpack_exports__ = __webpack_exports__.GridStack;
/******/
/******/ return __webpack_exports__;
/******/ })()
;
});
Espo.loader.setContextId(null);