angular-gridster2
Version:
Angular gridster 2
1,143 lines (1,135 loc) • 155 kB
JavaScript
import { NgStyle } from '@angular/common';
import * as i0 from '@angular/core';
import { Component, ViewEncapsulation, Input, EventEmitter, ElementRef, Renderer2, ChangeDetectorRef, NgZone, Inject, Output, HostBinding, NgModule } from '@angular/core';
import { Subject, debounceTime, takeUntil, switchMap, timer } from 'rxjs';
var GridType;
(function (GridType) {
GridType["Fit"] = "fit";
GridType["ScrollVertical"] = "scrollVertical";
GridType["ScrollHorizontal"] = "scrollHorizontal";
GridType["Fixed"] = "fixed";
GridType["VerticalFixed"] = "verticalFixed";
GridType["HorizontalFixed"] = "horizontalFixed";
})(GridType || (GridType = {}));
var DisplayGrid;
(function (DisplayGrid) {
DisplayGrid["Always"] = "always";
DisplayGrid["OnDragAndResize"] = "onDrag&Resize";
DisplayGrid["None"] = "none";
})(DisplayGrid || (DisplayGrid = {}));
var CompactType;
(function (CompactType) {
CompactType["None"] = "none";
CompactType["CompactUp"] = "compactUp";
CompactType["CompactLeft"] = "compactLeft";
CompactType["CompactUpAndLeft"] = "compactUp&Left";
CompactType["CompactLeftAndUp"] = "compactLeft&Up";
CompactType["CompactRight"] = "compactRight";
CompactType["CompactUpAndRight"] = "compactUp&Right";
CompactType["CompactRightAndUp"] = "compactRight&Up";
CompactType["CompactDown"] = "compactDown";
CompactType["CompactDownAndLeft"] = "compactDown&Left";
CompactType["CompactLeftAndDown"] = "compactLeft&Down";
CompactType["CompactDownAndRight"] = "compactDown&Right";
CompactType["CompactRightAndDown"] = "compactRight&Down";
})(CompactType || (CompactType = {}));
var DirTypes;
(function (DirTypes) {
DirTypes["LTR"] = "ltr";
DirTypes["RTL"] = "rtl";
})(DirTypes || (DirTypes = {}));
class GridsterCompact {
gridster;
constructor(gridster) {
this.gridster = gridster;
}
destroy() {
this.gridster = null;
}
checkCompact() {
if (this.gridster.$options.compactType !== CompactType.None) {
if (this.gridster.$options.compactType === CompactType.CompactUp) {
this.checkCompactMovement('y', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactLeft) {
this.checkCompactMovement('x', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactUpAndLeft) {
this.checkCompactMovement('y', -1);
this.checkCompactMovement('x', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactLeftAndUp) {
this.checkCompactMovement('x', -1);
this.checkCompactMovement('y', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactRight) {
this.checkCompactMovement('x', 1);
}
else if (this.gridster.$options.compactType === CompactType.CompactUpAndRight) {
this.checkCompactMovement('y', -1);
this.checkCompactMovement('x', 1);
}
else if (this.gridster.$options.compactType === CompactType.CompactRightAndUp) {
this.checkCompactMovement('x', 1);
this.checkCompactMovement('y', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactDown) {
this.checkCompactMovement('y', 1);
}
else if (this.gridster.$options.compactType === CompactType.CompactDownAndLeft) {
this.checkCompactMovement('y', 1);
this.checkCompactMovement('x', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactDownAndRight) {
this.checkCompactMovement('y', 1);
this.checkCompactMovement('x', 1);
}
else if (this.gridster.$options.compactType === CompactType.CompactLeftAndDown) {
this.checkCompactMovement('x', -1);
this.checkCompactMovement('y', 1);
}
else if (this.gridster.$options.compactType === CompactType.CompactRightAndDown) {
this.checkCompactMovement('x', 1);
this.checkCompactMovement('y', 1);
}
}
}
checkCompactItem(item) {
if (this.gridster.$options.compactType !== CompactType.None) {
if (this.gridster.$options.compactType === CompactType.CompactUp) {
this.moveTillCollision(item, 'y', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactLeft) {
this.moveTillCollision(item, 'x', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactUpAndLeft) {
this.moveTillCollision(item, 'y', -1);
this.moveTillCollision(item, 'x', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactLeftAndUp) {
this.moveTillCollision(item, 'x', -1);
this.moveTillCollision(item, 'y', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactUpAndRight) {
this.moveTillCollision(item, 'y', -1);
this.moveTillCollision(item, 'x', 1);
}
else if (this.gridster.$options.compactType === CompactType.CompactDown) {
this.moveTillCollision(item, 'y', 1);
}
else if (this.gridster.$options.compactType === CompactType.CompactDownAndLeft) {
this.moveTillCollision(item, 'y', 1);
this.moveTillCollision(item, 'x', -1);
}
else if (this.gridster.$options.compactType === CompactType.CompactLeftAndDown) {
this.moveTillCollision(item, 'x', -1);
this.moveTillCollision(item, 'y', 1);
}
else if (this.gridster.$options.compactType === CompactType.CompactDownAndRight) {
this.moveTillCollision(item, 'y', 1);
this.moveTillCollision(item, 'x', 1);
}
else if (this.gridster.$options.compactType === CompactType.CompactRightAndDown) {
this.moveTillCollision(item, 'x', 1);
this.moveTillCollision(item, 'y', 1);
}
}
}
checkCompactMovement(direction, delta) {
let widgetMoved = false;
this.gridster.grid.forEach((widget) => {
if (widget.$item.compactEnabled !== false) {
const moved = this.moveTillCollision(widget.$item, direction, delta);
if (moved) {
widgetMoved = true;
widget.item[direction] = widget.$item[direction];
widget.itemChanged();
}
}
});
if (widgetMoved) {
this.checkCompact();
}
}
moveTillCollision(item, direction, delta) {
item[direction] += delta;
if (this.gridster.checkCollision(item)) {
item[direction] -= delta;
return false;
}
else {
this.moveTillCollision(item, direction, delta);
return true;
}
}
}
const GridsterConfigService = {
gridType: GridType.Fit, // 'fit' will fit the items in the container without scroll;
scale: 1, // scale param to zoom in/zoom out
// 'scrollVertical' will fit on width and height of the items will be the same as the width
// 'scrollHorizontal' will fit on height and width of the items will be the same as the height
// 'fixed' will set the rows and columns dimensions based on fixedColWidth and fixedRowHeight options
// 'verticalFixed' will set the rows to fixedRowHeight and columns width will fit the space available
// 'horizontalFixed' will set the columns to fixedColWidth and rows height will fit the space available
fixedColWidth: 250, // fixed col width for gridType: 'fixed'
fixedRowHeight: 250, // fixed row height for gridType: 'fixed'
keepFixedHeightInMobile: false, // keep the height from fixed gridType in mobile layout
keepFixedWidthInMobile: false, // keep the width from fixed gridType in mobile layout
setGridSize: false, // sets grid size depending on content
compactType: CompactType.None, // compact items: 'none' | 'compactUp' | 'compactLeft' | 'compactUp&Left' | 'compactLeft&Up'
mobileBreakpoint: 640, // if the screen is not wider that this, remove the grid layout and stack the items
useBodyForBreakpoint: false, // whether to use the body width to determine the mobile breakpoint. Uses the element width when false.
allowMultiLayer: false,
defaultLayerIndex: 0,
maxLayerIndex: 2,
baseLayerIndex: 1,
minCols: 1, // minimum amount of columns in the grid
maxCols: 100, // maximum amount of columns in the grid
minRows: 1, // minimum amount of rows in the grid
maxRows: 100, // maximum amount of rows in the grid
defaultItemCols: 1, // default width of an item in columns
defaultItemRows: 1, // default height of an item in rows
maxItemCols: 50, // max item number of cols
maxItemRows: 50, // max item number of rows
minItemCols: 1, // min item number of columns
minItemRows: 1, // min item number of rows
minItemArea: 1, // min item area: cols * rows
maxItemArea: 2500, // max item area: cols * rows
addEmptyRowsCount: 0, // add a number of extra empty rows at the end
rowHeightRatio: 1, // row height ratio from column width
margin: 10, // margin between grid items
outerMargin: true, // if margins will apply to the sides of the container
outerMarginTop: null, // override outer margin for grid
outerMarginRight: null, // override outer margin for grid
outerMarginBottom: null, // override outer margin for grid
outerMarginLeft: null, // override outer margin for grid
useTransformPositioning: true, // toggle between transform or top/left positioning of items
scrollSensitivity: 10, // margin of the dashboard where to start scrolling
scrollSpeed: 20, // how much to scroll each mouse move when in the scrollSensitivity zone
initCallback: undefined, // callback to call after grid has initialized. Arguments: gridsterComponent
destroyCallback: undefined, // callback to call after grid has destroyed. Arguments: gridsterComponent
gridSizeChangedCallback: undefined, // callback to call after grid has changed size. Arguments: gridsterComponent
itemChangeCallback: undefined, // callback to call for each item when is changes x, y, rows, cols.
// Arguments: gridsterItem, gridsterItemComponent
itemResizeCallback: undefined, // callback to call for each item when width/height changes.
// Arguments: gridsterItem, gridsterItemComponent
itemInitCallback: undefined, // callback to call for each item when is initialized.
// Arguments: gridsterItem, gridsterItemComponent
itemRemovedCallback: undefined, // callback to call for each item when is initialized.
// Arguments: gridsterItem, gridsterItemComponent
itemValidateCallback: undefined, // callback to call to validate item position/size. Return true if valid.
// Arguments: gridsterItem
enableEmptyCellClick: false, // enable empty cell click events
enableEmptyCellContextMenu: false, // enable empty cell context menu (right click) events
enableEmptyCellDrop: false, // enable empty cell drop events
enableEmptyCellDrag: false, // enable empty cell drag events
enableOccupiedCellDrop: false, // enable occupied cell drop events
emptyCellClickCallback: undefined, // empty cell click callback
emptyCellContextMenuCallback: undefined, // empty cell context menu (right click) callback
emptyCellDropCallback: undefined, // empty cell drag drop callback. HTML5 Drag & Drop
emptyCellDragCallback: undefined, // empty cell drag and create item like excel cell selection
emptyCellDragMaxCols: 50, // limit empty cell drag max cols
emptyCellDragMaxRows: 50, // limit empty cell drag max rows
// Arguments: event, gridsterItem{x, y, rows: defaultItemRows, cols: defaultItemCols}
ignoreMarginInRow: false, // ignore the gap between rows for items which span multiple rows (see #162, #224)
draggable: {
delayStart: 0, // milliseconds to delay the start of drag, useful for touch interaction
enabled: false, // enable/disable draggable items
ignoreContentClass: 'gridster-item-content', // default content class to ignore the drag event from
ignoreContent: false, // if true drag will start only from elements from `dragHandleClass`
dragHandleClass: 'drag-handler', // drag event only from this class. If `ignoreContent` is true.
stop: undefined, // callback when dragging an item stops. Accepts Promise return to cancel/approve drag.
start: undefined, // callback when dragging an item starts.
// Arguments: item, gridsterItem, event
dropOverItems: false, // enable drop items on top other item
dropOverItemsCallback: undefined // callback on drop over another item
// Arguments: source, target, gridComponent
},
resizable: {
delayStart: 0, // milliseconds to delay the start of resize, useful for touch interaction
enabled: false, // enable/disable resizable items
handles: {
s: true,
e: true,
n: true,
w: true,
se: true,
ne: true,
sw: true,
nw: true
}, // resizable edges of an item
stop: undefined, // callback when resizing an item stops. Accepts Promise return to cancel/approve resize.
start: undefined // callback when resizing an item starts.
// Arguments: item, gridsterItem, event
},
swap: true, // allow items to switch position if drop on top of another
swapWhileDragging: false, // allow items to switch position while dragging
pushItems: false, // push items when resizing and dragging
disablePushOnDrag: false, // disable push on drag
disablePushOnResize: false, // disable push on resize
pushDirections: { north: true, east: true, south: true, west: true }, // control the directions items are pushed
pushResizeItems: false, // on resize of item will shrink adjacent items
displayGrid: DisplayGrid.OnDragAndResize, // display background grid of rows and columns
disableWindowResize: false, // disable the window on resize listener. This will stop grid to recalculate on window resize.
disableWarnings: false, // disable console log warnings about misplacement of grid items
scrollToNewItems: false, // scroll to new items placed in a scrollable view
disableScrollHorizontal: false, // disable horizontal scrolling
disableScrollVertical: false, // disable vertical scrolling
enableBoundaryControl: false, // enable boundary control while dragging items
disableAutoPositionOnConflict: false, // disable auto-position of items on conflict state,
dirType: DirTypes.LTR // page direction, rtl=right to left ltr= left to right, if you use rtl language set dirType to rtl
};
class GridsterUtils {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static merge(obj1, obj2, properties) {
for (const p in obj2) {
if (obj2[p] !== void 0 && properties.hasOwnProperty(p)) {
if (typeof obj2[p] === 'object') {
// create an empty object for the property if obj1 does not already have one.
if (!(p in obj1)) {
obj1[p] = {};
}
obj1[p] = GridsterUtils.merge(obj1[p], obj2[p], properties[p]);
}
else {
obj1[p] = obj2[p];
}
}
}
return obj1;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static checkTouchEvent(e) {
if (e.clientX === undefined && e.touches) {
if (e.touches && e.touches.length) {
e.clientX = e.touches[0].clientX;
e.clientY = e.touches[0].clientY;
}
else if (e.changedTouches && e.changedTouches.length) {
e.clientX = e.changedTouches[0].clientX;
e.clientY = e.changedTouches[0].clientY;
}
}
}
static checkContentClassForEvent(gridster, e) {
if (gridster.$options.draggable.ignoreContent) {
if (!GridsterUtils.checkDragHandleClass(e.target, e.currentTarget, gridster.$options.draggable.dragHandleClass, gridster.$options.draggable.ignoreContentClass)) {
return true;
}
}
else {
if (GridsterUtils.checkContentClass(e.target, e.currentTarget, gridster.$options.draggable.ignoreContentClass)) {
return true;
}
}
return false;
}
static checkContentClassForEmptyCellClickEvent(gridster, e) {
return (GridsterUtils.checkContentClass(e.target, e.currentTarget, gridster.$options.draggable.ignoreContentClass) ||
GridsterUtils.checkContentClass(e.target, e.currentTarget, gridster.$options.draggable.dragHandleClass));
}
static checkDragHandleClass(target, current, dragHandleClass, ignoreContentClass) {
if (!target || target === current) {
return false;
}
if (target.hasAttribute('class')) {
const classnames = target.getAttribute('class').split(' ');
if (classnames.indexOf(dragHandleClass) > -1) {
return true;
}
if (classnames.indexOf(ignoreContentClass) > -1) {
return false;
}
}
return GridsterUtils.checkDragHandleClass(target.parentNode, current, dragHandleClass, ignoreContentClass);
}
static checkContentClass(target, current, contentClass) {
if (!target || target === current) {
return false;
}
if (target.hasAttribute('class') &&
target.getAttribute('class').split(' ').indexOf(contentClass) > -1) {
return true;
}
else {
return GridsterUtils.checkContentClass(target.parentNode, current, contentClass);
}
}
static compareItems(a, b) {
if (a.y > b.y) {
return -1;
}
else if (a.y < b.y) {
return 1;
}
else if (a.x > b.x) {
return -1;
}
else {
return 1;
}
}
}
class GridsterEmptyCell {
gridster;
initialItem;
removeEmptyCellClickListenerFn;
removeEmptyCellTouchendListenerFn;
removeEmptyCellContextMenuListenerFn;
removeEmptyCellDropListenerFn;
removeEmptyCellMousedownListenerFn;
removeEmptyCellTouchstartListenerFn;
removeWindowMousemoveListenerFn;
removeWindowTouchmoveListenerFn;
removeWindowMouseupListenerFn;
removeWindowTouchendListenerFn;
removeEmptyCellDragoverListenerFn;
removeDocumentDragendListenerFn;
constructor(gridster) {
this.gridster = gridster;
}
destroy() {
if (this.gridster.previewStyle) {
this.gridster.previewStyle();
}
this.gridster.movingItem = null;
this.initialItem = this.gridster = null;
if (this.removeDocumentDragendListenerFn) {
this.removeDocumentDragendListenerFn();
this.removeDocumentDragendListenerFn = null;
}
}
updateOptions() {
if (this.gridster.$options.enableEmptyCellClick &&
!this.removeEmptyCellClickListenerFn &&
this.gridster.options.emptyCellClickCallback) {
this.removeEmptyCellClickListenerFn = this.gridster.renderer.listen(this.gridster.el, 'click', this.emptyCellClickCb);
this.removeEmptyCellTouchendListenerFn = this.gridster.renderer.listen(this.gridster.el, 'touchend', this.emptyCellClickCb);
}
else if (!this.gridster.$options.enableEmptyCellClick &&
this.removeEmptyCellClickListenerFn &&
this.removeEmptyCellTouchendListenerFn) {
this.removeEmptyCellClickListenerFn();
this.removeEmptyCellTouchendListenerFn();
this.removeEmptyCellClickListenerFn = null;
this.removeEmptyCellTouchendListenerFn = null;
}
if (this.gridster.$options.enableEmptyCellContextMenu &&
!this.removeEmptyCellContextMenuListenerFn &&
this.gridster.options.emptyCellContextMenuCallback) {
this.removeEmptyCellContextMenuListenerFn = this.gridster.renderer.listen(this.gridster.el, 'contextmenu', this.emptyCellContextMenuCb);
}
else if (!this.gridster.$options.enableEmptyCellContextMenu &&
this.removeEmptyCellContextMenuListenerFn) {
this.removeEmptyCellContextMenuListenerFn();
this.removeEmptyCellContextMenuListenerFn = null;
}
if (this.gridster.$options.enableEmptyCellDrop &&
!this.removeEmptyCellDropListenerFn &&
this.gridster.options.emptyCellDropCallback) {
this.removeEmptyCellDropListenerFn = this.gridster.renderer.listen(this.gridster.el, 'drop', this.emptyCellDragDrop);
this.gridster.zone.runOutsideAngular(() => {
this.removeEmptyCellDragoverListenerFn = this.gridster.renderer.listen(this.gridster.el, 'dragover', this.emptyCellDragOver);
});
this.removeDocumentDragendListenerFn = this.gridster.renderer.listen('document', 'dragend', () => {
this.gridster.movingItem = null;
this.gridster.previewStyle();
});
}
else if (!this.gridster.$options.enableEmptyCellDrop &&
this.removeEmptyCellDropListenerFn &&
this.removeEmptyCellDragoverListenerFn &&
this.removeDocumentDragendListenerFn) {
this.removeEmptyCellDropListenerFn();
this.removeEmptyCellDragoverListenerFn();
this.removeDocumentDragendListenerFn();
this.removeEmptyCellDragoverListenerFn = null;
this.removeEmptyCellDropListenerFn = null;
this.removeDocumentDragendListenerFn = null;
}
if (this.gridster.$options.enableEmptyCellDrag &&
!this.removeEmptyCellMousedownListenerFn &&
this.gridster.options.emptyCellDragCallback) {
this.removeEmptyCellMousedownListenerFn = this.gridster.renderer.listen(this.gridster.el, 'mousedown', this.emptyCellMouseDown);
this.removeEmptyCellTouchstartListenerFn = this.gridster.renderer.listen(this.gridster.el, 'touchstart', this.emptyCellMouseDown);
}
else if (!this.gridster.$options.enableEmptyCellDrag &&
this.removeEmptyCellMousedownListenerFn &&
this.removeEmptyCellTouchstartListenerFn) {
this.removeEmptyCellMousedownListenerFn();
this.removeEmptyCellTouchstartListenerFn();
this.removeEmptyCellMousedownListenerFn = null;
this.removeEmptyCellTouchstartListenerFn = null;
}
}
emptyCellClickCb = (e) => {
if (!this.gridster ||
this.gridster.movingItem ||
GridsterUtils.checkContentClassForEmptyCellClickEvent(this.gridster, e)) {
return;
}
const item = this.getValidItemFromEvent(e);
if (!item) {
return;
}
if (this.gridster.options.emptyCellClickCallback) {
this.gridster.options.emptyCellClickCallback(e, item);
}
this.gridster.cdRef.markForCheck();
};
emptyCellContextMenuCb = (e) => {
if (this.gridster.movingItem ||
GridsterUtils.checkContentClassForEmptyCellClickEvent(this.gridster, e)) {
return;
}
e.preventDefault();
e.stopPropagation();
const item = this.getValidItemFromEvent(e);
if (!item) {
return;
}
if (this.gridster.options.emptyCellContextMenuCallback) {
this.gridster.options.emptyCellContextMenuCallback(e, item);
}
this.gridster.cdRef.markForCheck();
};
emptyCellDragDrop = (e) => {
const item = this.getValidItemFromEvent(e);
if (!item) {
return;
}
if (this.gridster.options.emptyCellDropCallback) {
this.gridster.options.emptyCellDropCallback(e, item);
}
this.gridster.cdRef.markForCheck();
};
emptyCellDragOver = (e) => {
e.preventDefault();
e.stopPropagation();
const item = this.getValidItemFromEvent(e);
if (item) {
if (e.dataTransfer) {
e.dataTransfer.dropEffect = 'move';
}
this.gridster.movingItem = item;
}
else {
if (e.dataTransfer) {
e.dataTransfer.dropEffect = 'none';
}
this.gridster.movingItem = null;
}
this.gridster.previewStyle();
};
emptyCellMouseDown = (e) => {
if (GridsterUtils.checkContentClassForEmptyCellClickEvent(this.gridster, e)) {
return;
}
e.preventDefault();
e.stopPropagation();
const item = this.getValidItemFromEvent(e);
const leftMouseButtonCode = 1;
if (!item ||
(e.buttons !== leftMouseButtonCode && !(e instanceof TouchEvent))) {
return;
}
this.initialItem = item;
this.gridster.movingItem = item;
this.gridster.previewStyle();
this.gridster.zone.runOutsideAngular(() => {
this.removeWindowMousemoveListenerFn = this.gridster.renderer.listen('window', 'mousemove', this.emptyCellMouseMove);
this.removeWindowTouchmoveListenerFn = this.gridster.renderer.listen('window', 'touchmove', this.emptyCellMouseMove);
});
this.removeWindowMouseupListenerFn = this.gridster.renderer.listen('window', 'mouseup', this.emptyCellMouseUp);
this.removeWindowTouchendListenerFn = this.gridster.renderer.listen('window', 'touchend', this.emptyCellMouseUp);
};
emptyCellMouseMove = (e) => {
e.preventDefault();
e.stopPropagation();
const item = this.getValidItemFromEvent(e, this.initialItem);
if (!item) {
return;
}
this.gridster.movingItem = item;
this.gridster.previewStyle();
};
emptyCellMouseUp = (e) => {
this.removeWindowMousemoveListenerFn();
this.removeWindowTouchmoveListenerFn();
this.removeWindowMouseupListenerFn();
this.removeWindowTouchendListenerFn();
const item = this.getValidItemFromEvent(e, this.initialItem);
if (item) {
this.gridster.movingItem = item;
}
if (this.gridster.options.emptyCellDragCallback &&
this.gridster.movingItem) {
this.gridster.options.emptyCellDragCallback(e, this.gridster.movingItem);
}
setTimeout(() => {
this.initialItem = null;
if (this.gridster) {
this.gridster.movingItem = null;
this.gridster.previewStyle();
}
});
this.gridster.cdRef.markForCheck();
};
getPixelsX(e, rect) {
const scale = this.gridster.options.scale;
if (scale) {
return ((e.clientX - rect.left) / scale +
this.gridster.el.scrollLeft -
this.gridster.gridRenderer.getLeftMargin());
}
return (e.clientX +
this.gridster.el.scrollLeft -
rect.left -
this.gridster.gridRenderer.getLeftMargin());
}
getPixelsY(e, rect) {
const scale = this.gridster.options.scale;
if (scale) {
return ((e.clientY - rect.top) / scale +
this.gridster.el.scrollTop -
this.gridster.gridRenderer.getTopMargin());
}
return (e.clientY +
this.gridster.el.scrollTop -
rect.top -
this.gridster.gridRenderer.getTopMargin());
}
getValidItemFromEvent(e, oldItem) {
e.preventDefault();
e.stopPropagation();
GridsterUtils.checkTouchEvent(e);
const rect = this.gridster.el.getBoundingClientRect();
const x = this.getPixelsX(e, rect);
const y = this.getPixelsY(e, rect);
const item = {
x: this.gridster.pixelsToPositionX(x, Math.floor, true),
y: this.gridster.pixelsToPositionY(y, Math.floor, true),
cols: this.gridster.$options.defaultItemCols,
rows: this.gridster.$options.defaultItemRows
};
if (oldItem) {
item.cols = Math.min(Math.abs(oldItem.x - item.x) + 1, this.gridster.$options.emptyCellDragMaxCols);
item.rows = Math.min(Math.abs(oldItem.y - item.y) + 1, this.gridster.$options.emptyCellDragMaxRows);
if (oldItem.x < item.x) {
item.x = oldItem.x;
}
else if (oldItem.x - item.x >
this.gridster.$options.emptyCellDragMaxCols - 1) {
item.x = this.gridster.movingItem ? this.gridster.movingItem.x : 0;
}
if (oldItem.y < item.y) {
item.y = oldItem.y;
}
else if (oldItem.y - item.y >
this.gridster.$options.emptyCellDragMaxRows - 1) {
item.y = this.gridster.movingItem ? this.gridster.movingItem.y : 0;
}
}
if (!this.gridster.$options.enableOccupiedCellDrop &&
this.gridster.checkCollision(item)) {
return;
}
return item;
}
}
class GridsterRenderer {
gridster;
/**
* Caches the last grid column styles.
* This improves the grid responsiveness by caching and reusing the last style object instead of creating a new one.
*/
lastGridColumnStyles = {};
/**
* Caches the last grid column styles.
* This improves the grid responsiveness by caching and reusing the last style object instead of creating a new one.
*/
lastGridRowStyles = {};
constructor(gridster) {
this.gridster = gridster;
}
destroy() {
this.gridster = null;
}
updateItem(el, item, renderer) {
if (this.gridster.mobile) {
this.clearCellPosition(renderer, el);
if (this.gridster.$options.keepFixedHeightInMobile) {
renderer.setStyle(el, 'height', (item.rows - 1) * this.gridster.$options.margin +
item.rows * this.gridster.$options.fixedRowHeight +
'px');
}
else {
renderer.setStyle(el, 'height', (item.rows * this.gridster.curWidth) / item.cols + 'px');
}
if (this.gridster.$options.keepFixedWidthInMobile) {
renderer.setStyle(el, 'width', this.gridster.$options.fixedColWidth + 'px');
}
else {
renderer.setStyle(el, 'width', '');
}
renderer.setStyle(el, 'margin-bottom', this.gridster.$options.margin + 'px');
renderer.setStyle(el, DirTypes.LTR ? 'margin-right' : 'margin-left', '');
}
else {
const x = Math.round(this.gridster.curColWidth * item.x);
const y = Math.round(this.gridster.curRowHeight * item.y);
const width = this.gridster.curColWidth * item.cols - this.gridster.$options.margin;
const height = this.gridster.curRowHeight * item.rows - this.gridster.$options.margin;
// set the cell style
this.setCellPosition(renderer, el, x, y);
renderer.setStyle(el, 'width', width + 'px');
renderer.setStyle(el, 'height', height + 'px');
let marginBottom = null;
let marginRight = null;
if (this.gridster.$options.outerMargin) {
if (this.gridster.rows === item.rows + item.y) {
if (this.gridster.$options.outerMarginBottom !== null) {
marginBottom = this.gridster.$options.outerMarginBottom + 'px';
}
else {
marginBottom = this.gridster.$options.margin + 'px';
}
}
if (this.gridster.columns === item.cols + item.x) {
if (this.gridster.$options.outerMarginBottom !== null) {
marginRight = this.gridster.$options.outerMarginRight + 'px';
}
else {
marginRight = this.gridster.$options.margin + 'px';
}
}
}
renderer.setStyle(el, 'margin-bottom', marginBottom);
renderer.setStyle(el, DirTypes.LTR ? 'margin-right' : 'margin-left', marginRight);
}
}
updateGridster() {
let addClass = '';
let removeClass1 = '';
let removeClass2 = '';
let removeClass3 = '';
if (this.gridster.$options.gridType === GridType.Fit) {
addClass = GridType.Fit;
removeClass1 = GridType.ScrollVertical;
removeClass2 = GridType.ScrollHorizontal;
removeClass3 = GridType.Fixed;
}
else if (this.gridster.$options.gridType === GridType.ScrollVertical) {
this.gridster.curRowHeight =
this.gridster.curColWidth * this.gridster.$options.rowHeightRatio;
addClass = GridType.ScrollVertical;
removeClass1 = GridType.Fit;
removeClass2 = GridType.ScrollHorizontal;
removeClass3 = GridType.Fixed;
}
else if (this.gridster.$options.gridType === GridType.ScrollHorizontal) {
const widthRatio = this.gridster.$options.rowHeightRatio;
const calWidthRatio = widthRatio >= 1 ? widthRatio : widthRatio + 1;
this.gridster.curColWidth = this.gridster.curRowHeight * calWidthRatio;
addClass = GridType.ScrollHorizontal;
removeClass1 = GridType.Fit;
removeClass2 = GridType.ScrollVertical;
removeClass3 = GridType.Fixed;
}
else if (this.gridster.$options.gridType === GridType.Fixed) {
this.gridster.curColWidth =
this.gridster.$options.fixedColWidth +
(this.gridster.$options.ignoreMarginInRow
? 0
: this.gridster.$options.margin);
this.gridster.curRowHeight =
this.gridster.$options.fixedRowHeight +
(this.gridster.$options.ignoreMarginInRow
? 0
: this.gridster.$options.margin);
addClass = GridType.Fixed;
removeClass1 = GridType.Fit;
removeClass2 = GridType.ScrollVertical;
removeClass3 = GridType.ScrollHorizontal;
}
else if (this.gridster.$options.gridType === GridType.VerticalFixed) {
this.gridster.curRowHeight =
this.gridster.$options.fixedRowHeight +
(this.gridster.$options.ignoreMarginInRow
? 0
: this.gridster.$options.margin);
addClass = GridType.ScrollVertical;
removeClass1 = GridType.Fit;
removeClass2 = GridType.ScrollHorizontal;
removeClass3 = GridType.Fixed;
}
else if (this.gridster.$options.gridType === GridType.HorizontalFixed) {
this.gridster.curColWidth =
this.gridster.$options.fixedColWidth +
(this.gridster.$options.ignoreMarginInRow
? 0
: this.gridster.$options.margin);
addClass = GridType.ScrollHorizontal;
removeClass1 = GridType.Fit;
removeClass2 = GridType.ScrollVertical;
removeClass3 = GridType.Fixed;
}
if (this.gridster.mobile ||
(this.gridster.$options.setGridSize &&
this.gridster.$options.gridType !== GridType.Fit)) {
this.gridster.renderer.removeClass(this.gridster.el, addClass);
}
else {
this.gridster.renderer.addClass(this.gridster.el, addClass);
}
this.gridster.renderer.removeClass(this.gridster.el, removeClass1);
this.gridster.renderer.removeClass(this.gridster.el, removeClass2);
this.gridster.renderer.removeClass(this.gridster.el, removeClass3);
}
getGridColumnStyle(i) {
// generates the new style
const newPos = {
left: this.gridster.curColWidth * i,
width: this.gridster.curColWidth - this.gridster.$options.margin,
height: this.gridster.gridRows.length * this.gridster.curRowHeight -
this.gridster.$options.margin,
style: {}
};
newPos.style = {
...this.getLeftPosition(newPos.left),
width: newPos.width + 'px',
height: newPos.height + 'px'
};
// use the last cached style if it has same values as the generated one
const last = this.lastGridColumnStyles[i];
if (last &&
last.left === newPos.left &&
last.width === newPos.width &&
last.height === newPos.height) {
return last.style;
}
// cache and set new style
this.lastGridColumnStyles[i] = newPos;
return newPos.style;
}
getGridRowStyle(i) {
// generates the new style
const newPos = {
top: this.gridster.curRowHeight * i,
width: this.gridster.gridColumns.length * this.gridster.curColWidth +
this.gridster.$options.margin,
height: this.gridster.curRowHeight - this.gridster.$options.margin,
style: {}
};
newPos.style = {
...this.getTopPosition(newPos.top),
width: newPos.width + 'px',
height: newPos.height + 'px'
};
// use the last cached style if it has same values as the generated one
const last = this.lastGridRowStyles[i];
if (last &&
last.top === newPos.top &&
last.width === newPos.width &&
last.height === newPos.height) {
return last.style;
}
// cache and set new style
this.lastGridRowStyles[i] = newPos;
return newPos.style;
}
getLeftPosition(d) {
const dPosition = this.gridster.$options.dirType === DirTypes.RTL ? -d : d;
if (this.gridster.$options.useTransformPositioning) {
return {
transform: 'translateX(' + dPosition + 'px)'
};
}
else {
return {
left: this.getLeftMargin() + dPosition + 'px'
};
}
}
getTopPosition(d) {
if (this.gridster.$options.useTransformPositioning) {
return {
transform: 'translateY(' + d + 'px)'
};
}
else {
return {
top: this.getTopMargin() + d + 'px'
};
}
}
clearCellPosition(renderer, el) {
if (this.gridster.$options.useTransformPositioning) {
renderer.setStyle(el, 'transform', '');
}
else {
renderer.setStyle(el, 'top', '');
renderer.setStyle(el, 'left', '');
}
}
setCellPosition(renderer, el, x, y) {
const xPosition = this.gridster.$options.dirType === DirTypes.RTL ? -x : x;
if (this.gridster.$options.useTransformPositioning) {
const transform = 'translate3d(' + xPosition + 'px, ' + y + 'px, 0)';
renderer.setStyle(el, 'transform', transform);
}
else {
renderer.setStyle(el, 'left', this.getLeftMargin() + xPosition + 'px');
renderer.setStyle(el, 'top', this.getTopMargin() + y + 'px');
}
}
getLeftMargin() {
if (this.gridster.$options.outerMargin) {
if (this.gridster.$options.outerMarginLeft !== null) {
return this.gridster.$options.outerMarginLeft;
}
else {
return this.gridster.$options.margin;
}
}
else {
return 0;
}
}
getTopMargin() {
if (this.gridster.$options.outerMargin) {
if (this.gridster.$options.outerMarginTop !== null) {
return this.gridster.$options.outerMarginTop;
}
else {
return this.gridster.$options.margin;
}
}
else {
return 0;
}
}
}
class GridsterPreviewComponent {
renderer;
previewStyle$;
gridRenderer;
el;
sub;
constructor(el, renderer) {
this.renderer = renderer;
this.el = el.nativeElement;
}
ngOnInit() {
this.sub = this.previewStyle$.subscribe(options => this.previewStyle(options));
}
ngOnDestroy() {
if (this.sub) {
this.sub.unsubscribe();
}
}
previewStyle(item) {
if (item) {
this.renderer.setStyle(this.el, 'display', 'block');
this.gridRenderer.updateItem(this.el, item, this.renderer);
}
else {
this.renderer.setStyle(this.el, 'display', '');
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: GridsterPreviewComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.1", type: GridsterPreviewComponent, isStandalone: true, selector: "gridster-preview", inputs: { previewStyle$: "previewStyle$", gridRenderer: "gridRenderer" }, ngImport: i0, template: '', isInline: true, styles: ["gridster-preview{position:absolute;display:none;background:#00000026}\n"], encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: GridsterPreviewComponent, decorators: [{
type: Component,
args: [{ selector: 'gridster-preview', template: '', encapsulation: ViewEncapsulation.None, standalone: true, styles: ["gridster-preview{position:absolute;display:none;background:#00000026}\n"] }]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { previewStyle$: [{
type: Input
}], gridRenderer: [{
type: Input
}] } });
class GridsterComponent {
renderer;
cdRef;
zone;
options;
movingItem;
el;
$options;
mobile;
curWidth;
curHeight;
grid;
columns = 0;
rows = 0;
curColWidth;
curRowHeight;
gridColumns = [];
gridRows = [];
windowResize;
dragInProgress;
emptyCell;
compact;
gridRenderer;
previewStyle$ = new EventEmitter();
calculateLayout$ = new Subject();
resize$ = new Subject();
destroy$ = new Subject();
constructor(el, renderer, cdRef, zone) {
this.renderer = renderer;
this.cdRef = cdRef;
this.zone = zone;
this.el = el.nativeElement;
this.$options = JSON.parse(JSON.stringify(GridsterConfigService));
this.mobile = false;
this.curWidth = 0;
this.curHeight = 0;
this.grid = [];
this.curColWidth = 0;
this.curRowHeight = 0;
this.dragInProgress = false;
this.emptyCell = new GridsterEmptyCell(this);
this.compact = new GridsterCompact(this);
this.gridRenderer = new GridsterRenderer(this);
}
// ------ Function for swapWhileDragging option
// identical to checkCollision() except that here we add boundaries.
static checkCollisionTwoItemsForSwaping(item, item2) {
// if the cols or rows of the items are 1 , doesnt make any sense to set a boundary. Only if the item is bigger we set a boundary
const horizontalBoundaryItem1 = item.cols === 1 ? 0 : 1;
const horizontalBoundaryItem2 = item2.cols === 1 ? 0 : 1;
const verticalBoundaryItem1 = item.rows === 1 ? 0 : 1;
const verticalBoundaryItem2 = item2.rows === 1 ? 0 : 1;
return (item.x + horizontalBoundaryItem1 < item2.x + item2.cols &&
item.x + item.cols > item2.x + horizontalBoundaryItem2 &&
item.y + verticalBoundaryItem1 < item2.y + item2.rows &&
item.y + item.rows > item2.y + verticalBoundaryItem2);
}
checkCollisionTwoItems(item, item2) {
const collision = item.x < item2.x + item2.cols &&
item.x + item.cols > item2.x &&
item.y < item2.y + item2.rows &&
item.y + item.rows > item2.y;
if (!collision) {
return false;
}
if (!this.$options.allowMultiLayer) {
return true;
}
const defaultLayerIndex = this.$options.defaultLayerIndex;
const layerIndex = item.layerIndex === undefined ? defaultLayerIndex : item.layerIndex;
const layerIndex2 = item2.layerIndex === undefined ? defaultLayerIndex : item2.layerIndex;
return layerIndex === layerIndex2;
}
ngOnInit() {
if (this.options.initCallback) {
this.options.initCallback(this);
}
this.calculateLayout$
.pipe(debounceTime(0), takeUntil(this.destroy$))
.subscribe(() => this.calculateLayout());
this.resize$
.pipe(
// Cancel previously scheduled DOM timer if `calculateLayout()` has been called
// within this time range.
switchMap(() => timer(100)), takeUntil(this.destroy$))
.subscribe(() => this.resize());
}
ngOnChanges(changes) {
if (changes.options) {
this.setOptions();
this.options.api = {
optionsChanged: this.optionsChanged,
resize: this.onResize,
getNextPossiblePosition: this.getNextPossiblePosition,
getFirstPossiblePosition: this.getFirstPossiblePosition,
getLastPossiblePosition: this.getLastPossiblePosition,
getItemComponent: (item) => this.getItemComponent(item)
};
this.columns = this.$options.minCols;
this.rows = this.$options.minRows + this.$options.addEmptyRowsCount;
this.setGridSize();
this.calculateLayout();
}
}
resize() {
let height;
let width;
if (this.$options.gridType === 'fit' && !this.mobile) {
width = this.el.offsetWidth;
height = this.el.offsetHeight;
}
else {
width = this.el.clientWidth;
height = this.el.clientHeight;
}
if ((width !== this.curWidth || height !== this.curHeight) &&
this.checkIfToResize()) {
this.onResize();
}
}
setOptions() {
this.$options = GridsterUtils.merge(this.$options, this.options, this.$options);
if (!this.$options.disableWindowResize && !this.windowResize) {
this.windowResize = this.renderer.listen('window', 'resize', this.onResize);
}
else if (this.$options.disableWindowResize && this.windowResize) {
this.windowResize();
this.windowResize = null;
}
this.emptyCell.updateOptions();
}
optionsChanged = () => {
this.setOptions();
let widgetsIndex = this.grid.length - 1;
let widget;
for (; widgetsIndex >= 0; widgetsIndex--) {
widget = this.grid[widgetsIndex];
widget.updateOptions();
}
this.calculateLayout();
};
ngOnDestroy() {
this.destroy$.next();
this.previewStyle$.complete();
if (this.windowResize) {
this.windowResize();
}
if (this.options && this.options.destroyCallback) {
this.options.destroyCallback(this);
}
if (this.options && this.options.api) {
this.options.api.resize = undefined;
this.options.api.optionsChanged = undefined;
this.options.api.getNextPossiblePosition = undefined;
this.options.api = undefined;
}
this.emptyCell.destroy();
this.emptyCell = null;
this.compact.destroy();
this.compact = null;
}
onResize = () => {
if (this.el.clientWidth) {
if (this.options.setGridSize) {
// reset width/height so the size is recalculated afterwards
this.renderer.setStyle(this.el, 'width', '');
this.renderer.setStyle(this.el, 'height', '');
}
this.setGridSize();
this.calculateLayout();
}
};
checkIfToResize() {
const clientWidth = this.el.clientWidth;
const offsetWidth = this.el.offsetWidth;
const scrollWidth = this.el.scrollWidth;
const clientHeight = this.el.clientHeight;
const offsetHeight = this.el.offsetHeight;
const scrollHeight = this.el.scrollHeight;
const verticalScrollPresent = clientWidth < offsetWidth &&
scrollHeight > offsetHeight &&
scrollHeight - offsetHeight < offsetWidth - clientWidth;
const horizontalScrollPresent = clientHeight < offsetHeight &&
scrollWidth > offsetWidth &&
scrollWidth - offsetWidth < offsetHeight