UNPKG

@hugogo/angular-grid-layout-hugo

Version:

Grid Layout with draggable and resizable items for Angular

1,097 lines (1,089 loc) 106 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('rxjs/operators'), require('@angular/core'), require('@angular/common')) : typeof define === 'function' && define.amd ? define('@hugogo/angular-grid-layout-hugo', ['exports', 'rxjs', 'rxjs/operators', '@angular/core', '@angular/common'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.hugogo = global.hugogo || {}, global.hugogo['angular-grid-layout-hugo'] = {}), global.rxjs, global.rxjs.operators, global.ng.core, global.ng.common)); }(this, (function (exports, rxjs, operators, i0, common) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function () { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __param(paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); }; } function __metadata(metadataKey, metadataValue) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); } function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var __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]; }); function __exportStar(m, o) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } /** @deprecated */ function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } /** @deprecated */ function __spreadArrays() { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; } function __spreadArray(to, from) { for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) to[j] = from[i]; return to; } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } function __asyncGenerator(thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } } function __asyncDelegator(o) { var i, p; return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } } function __asyncValues(o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function (v) { resolve({ value: v, done: d }); }, reject); } } function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; } ; var __setModuleDefault = Object.create ? (function (o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function (o, v) { o["default"] = v; }; function __importStar(mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; } function __importDefault(mod) { return (mod && mod.__esModule) ? mod : { default: mod }; } function __classPrivateFieldGet(receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); } function __classPrivateFieldSet(receiver, privateMap, value) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to set private field on non-instance"); } privateMap.set(receiver, value); return value; } /** * IMPORTANT: * This utils are taken from the project: https://github.com/STRML/react-grid-layout. * The code should be as less modified as possible for easy maintenance. */ var DEBUG = false; /** * Return the bottom coordinate of the layout. * * @param {Array} layout Layout array. * @return {Number} Bottom coordinate. */ function bottom(layout) { var max = 0, bottomY; for (var i = 0, len = layout.length; i < len; i++) { bottomY = layout[i].y + layout[i].h; if (bottomY > max) { max = bottomY; } } return max; } function cloneLayout(layout) { var newLayout = Array(layout.length); for (var i = 0, len = layout.length; i < len; i++) { newLayout[i] = cloneLayoutItem(layout[i]); } return newLayout; } // Fast path to cloning, since this is monomorphic /** NOTE: This code has been modified from the original source */ function cloneLayoutItem(layoutItem) { var clonedLayoutItem = { w: layoutItem.w, h: layoutItem.h, x: layoutItem.x, y: layoutItem.y, id: layoutItem.id, moved: !!layoutItem.moved, static: !!layoutItem.static, }; if (layoutItem.minW !== undefined) { clonedLayoutItem.minW = layoutItem.minW; } if (layoutItem.maxW !== undefined) { clonedLayoutItem.maxW = layoutItem.maxW; } if (layoutItem.minH !== undefined) { clonedLayoutItem.minH = layoutItem.minH; } if (layoutItem.maxH !== undefined) { clonedLayoutItem.maxH = layoutItem.maxH; } // These can be null if (layoutItem.isDraggable !== undefined) { clonedLayoutItem.isDraggable = layoutItem.isDraggable; } if (layoutItem.isResizable !== undefined) { clonedLayoutItem.isResizable = layoutItem.isResizable; } return clonedLayoutItem; } /** * Given two layoutitems, check if they collide. */ function collides(l1, l2) { if (l1.id === l2.id) { return false; } // same element if (l1.x + l1.w <= l2.x) { return false; } // l1 is left of l2 if (l1.x >= l2.x + l2.w) { return false; } // l1 is right of l2 if (l1.y + l1.h <= l2.y) { return false; } // l1 is above l2 if (l1.y >= l2.y + l2.h) { return false; } // l1 is below l2 return true; // boxes overlap } /** * Given a layout, compact it. This involves going down each y coordinate and removing gaps * between items. * * @param {Array} layout Layout. * @param {Boolean} verticalCompact Whether or not to compact the layout * vertically. * @return {Array} Compacted Layout. */ function compact(layout, compactType, cols) { // Statics go in the compareWith array right away so items flow around them. var compareWith = getStatics(layout); // We go through the items by row and column. var sorted = sortLayoutItems(layout, compactType); // Holding for new items. var out = Array(layout.length); for (var i = 0, len = sorted.length; i < len; i++) { var l = cloneLayoutItem(sorted[i]); // Don't move static elements if (!l.static) { l = compactItem(compareWith, l, compactType, cols, sorted); // Add to comparison array. We only collide with items before this one. // Statics are already in this array. compareWith.push(l); } // Add to output array to make sure they still come out in the right order. out[layout.indexOf(sorted[i])] = l; // Clear moved flag, if it exists. l.moved = false; } return out; } var heightWidth = { x: 'w', y: 'h' }; /** * Before moving item down, it will check if the movement will cause collisions and move those items down before. */ function resolveCompactionCollision(layout, item, moveToCoord, axis) { var sizeProp = heightWidth[axis]; item[axis] += 1; var itemIndex = layout .map(function (layoutItem) { return layoutItem.id; }) .indexOf(item.id); // Go through each item we collide with. for (var i = itemIndex + 1; i < layout.length; i++) { var otherItem = layout[i]; // Ignore static items if (otherItem.static) { continue; } // Optimization: we can break early if we know we're past this el // We can do this b/c it's a sorted layout if (otherItem.y > item.y + item.h) { break; } if (collides(item, otherItem)) { resolveCompactionCollision(layout, otherItem, moveToCoord + item[sizeProp], axis); } } item[axis] = moveToCoord; } /** * Compact an item in the layout. */ function compactItem(compareWith, l, compactType, cols, fullLayout) { var compactV = compactType === 'vertical'; var compactH = compactType === 'horizontal'; if (compactV) { // Bottom 'y' possible is the bottom of the layout. // This allows you to do nice stuff like specify {y: Infinity} // This is here because the layout must be sorted in order to get the correct bottom `y`. l.y = Math.min(bottom(compareWith), l.y); // Move the element up as far as it can go without colliding. while (l.y > 0 && !getFirstCollision(compareWith, l)) { l.y--; } } else if (compactH) { l.y = Math.min(bottom(compareWith), l.y); // Move the element left as far as it can go without colliding. while (l.x > 0 && !getFirstCollision(compareWith, l)) { l.x--; } } // Move it down, and keep moving it down if it's colliding. var collides; while ((collides = getFirstCollision(compareWith, l))) { if (compactH) { resolveCompactionCollision(fullLayout, l, collides.x + collides.w, 'x'); } else { resolveCompactionCollision(fullLayout, l, collides.y + collides.h, 'y'); } // Since we can't grow without bounds horizontally, if we've overflown, let's move it down and try again. if (compactH && l.x + l.w > cols) { l.x = cols - l.w; l.y++; } } return l; } /** * Given a layout, make sure all elements fit within its bounds. * * @param {Array} layout Layout array. * @param {Number} bounds Number of columns. */ function correctBounds(layout, bounds) { var collidesWith = getStatics(layout); for (var i = 0, len = layout.length; i < len; i++) { var l = layout[i]; // Overflows right if (l.x + l.w > bounds.cols) { l.x = bounds.cols - l.w; } // Overflows left if (l.x < 0) { l.x = 0; l.w = bounds.cols; } if (!l.static) { collidesWith.push(l); } else { // If this is static and collides with other statics, we must move it down. // We have to do something nicer than just letting them overlap. while (getFirstCollision(collidesWith, l)) { l.y++; } } } return layout; } /** * Get a layout item by ID. Used so we can override later on if necessary. * * @param {Array} layout Layout array. * @param {String} id ID * @return {LayoutItem} Item at ID. */ function getLayoutItem(layout, id) { for (var i = 0, len = layout.length; i < len; i++) { if (layout[i].id === id) { return layout[i]; } } return null; } /** * Returns the first item this layout collides with. * It doesn't appear to matter which order we approach this from, although * perhaps that is the wrong thing to do. * * @param {Object} layoutItem Layout item. * @return {Object|undefined} A colliding layout item, or undefined. */ function getFirstCollision(layout, layoutItem) { for (var i = 0, len = layout.length; i < len; i++) { if (collides(layout[i], layoutItem)) { return layout[i]; } } return null; } function getAllCollisions(layout, layoutItem) { return layout.filter(function (l) { return collides(l, layoutItem); }); } /** * Get all static elements. * @param {Array} layout Array of layout objects. * @return {Array} Array of static layout items.. */ function getStatics(layout) { return layout.filter(function (l) { return l.static; }); } /** * Move an element. Responsible for doing cascading movements of other elements. * * @param {Array} layout Full layout to modify. * @param {LayoutItem} l element to move. * @param {Number} [x] X position in grid units. * @param {Number} [y] Y position in grid units. */ function moveElement(layout, l, x, y, isUserAction, preventCollision, compactType, cols) { // If this is static and not explicitly enabled as draggable, // no move is possible, so we can short-circuit this immediately. if (l.static && l.isDraggable !== true) { return layout; } // Short-circuit if nothing to do. if (l.y === y && l.x === x) { return layout; } log("Moving element " + l.id + " to [" + String(x) + "," + String(y) + "] from [" + l.x + "," + l.y + "]"); var oldX = l.x; var oldY = l.y; // This is quite a bit faster than extending the object if (typeof x === 'number') { l.x = x; } if (typeof y === 'number') { l.y = y; } l.moved = true; // If this collides with anything, move it. // When doing this comparison, we have to sort the items we compare with // to ensure, in the case of multiple collisions, that we're getting the // nearest collision. var sorted = sortLayoutItems(layout, compactType); var movingUp = compactType === 'vertical' && typeof y === 'number' ? oldY >= y : compactType === 'horizontal' && typeof x === 'number' ? oldX >= x : false; if (movingUp) { sorted = sorted.reverse(); } var collisions = getAllCollisions(sorted, l); // There was a collision; abort if (preventCollision && collisions.length) { log("Collision prevented on " + l.id + ", reverting."); l.x = oldX; l.y = oldY; l.moved = false; return layout; } // Move each item that collides away from this element. for (var i = 0, len = collisions.length; i < len; i++) { var collision = collisions[i]; log("Resolving collision between " + l.id + " at [" + l.x + "," + l.y + "] and " + collision.id + " at [" + collision.x + "," + collision.y + "]"); // Short circuit so we can't infinite loop if (collision.moved) { continue; } // Don't move static items - we have to move *this* element away if (collision.static) { layout = moveElementAwayFromCollision(layout, collision, l, isUserAction, compactType, cols); } else { layout = moveElementAwayFromCollision(layout, l, collision, isUserAction, compactType, cols); } } return layout; } /** * This is where the magic needs to happen - given a collision, move an element away from the collision. * We attempt to move it up if there's room, otherwise it goes below. * * @param {Array} layout Full layout to modify. * @param {LayoutItem} collidesWith Layout item we're colliding with. * @param {LayoutItem} itemToMove Layout item we're moving. */ function moveElementAwayFromCollision(layout, collidesWith, itemToMove, isUserAction, compactType, cols) { var compactH = compactType === 'horizontal'; // Compact vertically if not set to horizontal var compactV = compactType !== 'horizontal'; var preventCollision = collidesWith.static; // we're already colliding (not for static items) // If there is enough space above the collision to put this element, move it there. // We only do this on the main collision as this can get funky in cascades and cause // unwanted swapping behavior. if (isUserAction) { // Reset isUserAction flag because we're not in the main collision anymore. isUserAction = false; // Make a mock item so we don't modify the item here, only modify in moveElement. var fakeItem = { x: compactH ? Math.max(collidesWith.x - itemToMove.w, 0) : itemToMove.x, y: compactV ? Math.max(collidesWith.y - itemToMove.h, 0) : itemToMove.y, w: itemToMove.w, h: itemToMove.h, id: '-1', }; // No collision? If so, we can go up there; otherwise, we'll end up moving down as normal if (!getFirstCollision(layout, fakeItem)) { log("Doing reverse collision on " + itemToMove.id + " up to [" + fakeItem.x + "," + fakeItem.y + "]."); return moveElement(layout, itemToMove, compactH ? fakeItem.x : undefined, compactV ? fakeItem.y : undefined, isUserAction, preventCollision, compactType, cols); } } return moveElement(layout, itemToMove, compactH ? itemToMove.x + 1 : undefined, compactV ? itemToMove.y + 1 : undefined, isUserAction, preventCollision, compactType, cols); } /** * Helper to convert a number to a percentage string. * * @param {Number} num Any number * @return {String} That number as a percentage. */ function perc(num) { return num * 100 + '%'; } function setTransform(_a) { var top = _a.top, left = _a.left, width = _a.width, height = _a.height; // Replace unitless items with px var translate = "translate(" + left + "px," + top + "px)"; return { transform: translate, WebkitTransform: translate, MozTransform: translate, msTransform: translate, OTransform: translate, width: width + "px", height: height + "px", position: 'absolute', }; } function setTopLeft(_a) { var top = _a.top, left = _a.left, width = _a.width, height = _a.height; return { top: top + "px", left: left + "px", width: width + "px", height: height + "px", position: 'absolute', }; } /** * Get layout items sorted from top left to right and down. * * @return {Array} Array of layout objects. * @return {Array} Layout, sorted static items first. */ function sortLayoutItems(layout, compactType) { if (compactType === 'horizontal') { return sortLayoutItemsByColRow(layout); } else { return sortLayoutItemsByRowCol(layout); } } function sortLayoutItemsByRowCol(layout) { return [].concat(layout).sort(function (a, b) { if (a.y > b.y || (a.y === b.y && a.x > b.x)) { return 1; } else if (a.y === b.y && a.x === b.x) { // Without this, we can get different sort results in IE vs. Chrome/FF return 0; } return -1; }); } function sortLayoutItemsByColRow(layout) { return [].concat(layout).sort(function (a, b) { if (a.x > b.x || (a.x === b.x && a.y > b.y)) { return 1; } return -1; }); } /** * Validate a layout. Throws errors. * * @param {Array} layout Array of layout items. * @param {String} [contextName] Context name for errors. * @throw {Error} Validation error. */ function validateLayout(layout, contextName) { if (contextName === void 0) { contextName = 'Layout'; } var subProps = ['x', 'y', 'w', 'h']; if (!Array.isArray(layout)) { throw new Error(contextName + ' must be an array!'); } for (var i = 0, len = layout.length; i < len; i++) { var item = layout[i]; for (var j = 0; j < subProps.length; j++) { if (typeof item[subProps[j]] !== 'number') { throw new Error('ReactGridLayout: ' + contextName + '[' + i + '].' + subProps[j] + ' must be a number!'); } } if (item.id && typeof item.id !== 'string') { throw new Error('ReactGridLayout: ' + contextName + '[' + i + '].i must be a string!'); } if (item.static !== undefined && typeof item.static !== 'boolean') { throw new Error('ReactGridLayout: ' + contextName + '[' + i + '].static must be a boolean!'); } } } // Flow can't really figure this out, so we just use Object function autoBindHandlers(el, fns) { fns.forEach(function (key) { return (el[key] = el[key].bind(el)); }); } function log() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (!DEBUG) { return; } // eslint-disable-next-line no-console console.log.apply(console, __spread(args)); } var noop = function () { }; /** Cached result of whether the user's browser supports passive event listeners. */ var supportsPassiveEvents; /** * Checks whether the user's browser supports passive event listeners. * See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md */ function ktdSupportsPassiveEventListeners() { if (supportsPassiveEvents == null && typeof window !== 'undefined') { try { window.addEventListener('test', null, Object.defineProperty({}, 'passive', { get: function () { return supportsPassiveEvents = true; } })); } finally { supportsPassiveEvents = supportsPassiveEvents || false; } } return supportsPassiveEvents; } /** * Normalizes an `AddEventListener` object to something that can be passed * to `addEventListener` on any browser, no matter whether it supports the * `options` parameter. * @param options Object to be normalized. */ function ktdNormalizePassiveListenerOptions(options) { return ktdSupportsPassiveEventListeners() ? options : !!options.capture; } /** Options that can be used to bind a passive event listener. */ var passiveEventListenerOptions = ktdNormalizePassiveListenerOptions({ passive: true }); /** Options that can be used to bind an active event listener. */ var activeEventListenerOptions = ktdNormalizePassiveListenerOptions({ passive: false }); var isMobile = null; function ktdIsMobileOrTablet() { if (isMobile != null) { return isMobile; } // Generic match pattern to identify mobile or tablet devices var isMobileDevice = /Android|webOS|BlackBerry|Windows Phone|iPad|iPhone|iPod/i.test(navigator.userAgent); // Since IOS 13 is not safe to just check for the generic solution. See: https://stackoverflow.com/questions/58019463/how-to-detect-device-name-in-safari-on-ios-13-while-it-doesnt-show-the-correct var isIOSMobileDevice = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); isMobile = isMobileDevice || isIOSMobileDevice; return isMobile; } function ktdIsMouseEvent(event) { return event.clientX != null; } function ktdIsTouchEvent(event) { return event.touches != null && event.touches.length != null; } function ktdPointerClientX(event) { return ktdIsMouseEvent(event) ? event.clientX : event.touches[0].clientX; } function ktdPointerClientY(event) { return ktdIsMouseEvent(event) ? event.clientY : event.touches[0].clientY; } function ktdPointerClient(event) { return { clientX: ktdIsMouseEvent(event) ? event.clientX : event.touches[0].clientX, clientY: ktdIsMouseEvent(event) ? event.clientY : event.touches[0].clientY }; } /** * Emits when a mousedown or touchstart emits. Avoids conflicts between both events. * @param element, html element where to listen the events. * @param touchNumber number of the touch to track the event, default to the first one. */ function ktdMouseOrTouchDown(element, touchNumber) { if (touchNumber === void 0) { touchNumber = 1; } return rxjs.iif(function () { return ktdIsMobileOrTablet(); }, rxjs.fromEvent(element, 'touchstart', passiveEventListenerOptions).pipe(operators.filter(function (touchEvent) { return touchEvent.touches.length === touchNumber; })), rxjs.fromEvent(element, 'mousedown', activeEventListenerOptions).pipe(operators.filter(function (mouseEvent) { /** * 0 : Left mouse button * 1 : Wheel button or middle button (if present) * 2 : Right mouse button */ return mouseEvent.button === 0; // Mouse down to be only fired if is left click }))); } /** * Emits when a 'mousemove' or a 'touchmove' event gets fired. * @param element, html element where to listen the events. * @param touchNumber number of the touch to track the event, default to the first one. */ function ktdMouseOrTouchMove(element, touchNumber) { if (touchNumber === void 0) { touchNumber = 1; } return rxjs.iif(function () { return ktdIsMobileOrTablet(); }, rxjs.fromEvent(element, 'touchmove', activeEventListenerOptions).pipe(operators.filter(function (touchEvent) { return touchEvent.touches.length === touchNumber; })), rxjs.fromEvent(element, 'mousemove', activeEventListenerOptions)); } function ktdTouchEnd(element, touchNumber) { if (touchNumber === void 0) { touchNumber = 1; } return rxjs.merge(rxjs.fromEvent(element, 'touchend').pipe(operators.filter(function (touchEvent) { return touchEvent.touches.length === touchNumber - 1; })), rxjs.fromEvent(element, 'touchcancel').pipe(operators.filter(function (touchEvent) { return touchEvent.touches.length === touchNumber - 1; }))); } /** * Emits when a there is a 'mouseup' or the touch ends. * @param element, html element where to listen the events. * @param touchNumber number of the touch to track the event, default to the first one. */ function ktdMouseOrTouchEnd(element, touchNumber) { if (touchNumber === void 0) { touchNumber = 1; } return rxjs.iif(function () { return ktdIsMobileOrTablet(); }, ktdTouchEnd(element, touchNumber), rxjs.fromEvent(element, 'mouseup')); } /** Tracks items by id. This function is mean to be used in conjunction with the ngFor that renders the 'ktd-grid-items' */ function ktdTrackById(index, item) { return item.id; } /** * Call react-grid-layout utils 'compact()' function and return the compacted layout. * @param layout to be compacted. * @param compactType, type of compaction. * @param cols, number of columns of the grid. */ function ktdGridCompact(layout, compactType, cols) { return compact(layout, compactType, cols) // Prune react-grid-layout compact extra properties. .map(function (item) { return ({ id: item.id, x: item.x, y: item.y, w: item.w, h: item.h }); }); } function screenXPosToGridValue(screenXPos, cols, width) { return Math.round((screenXPos * cols) / width); } function screenYPosToGridValue(screenYPos, rowHeight, height) { return Math.round(screenYPos / rowHeight); } /** Returns a Dictionary where the key is the id and the value is the change applied to that item. If no changes Dictionary is empty. */ function ktdGetGridLayoutDiff(gridLayoutA, gridLayoutB) { var diff = {}; gridLayoutA.forEach(function (itemA) { var itemB = gridLayoutB.find(function (_itemB) { return _itemB.id === itemA.id; }); if (itemB != null) { var posChanged = itemA.x !== itemB.x || itemA.y !== itemB.y; var sizeChanged = itemA.w !== itemB.w || itemA.h !== itemB.h; var change = posChanged && sizeChanged ? 'moveresize' : posChanged ? 'move' : sizeChanged ? 'resize' : null; if (change) { diff[itemB.id] = { change: change }; } } }); return diff; } /** * Given the grid config & layout data and the current drag position & information, returns the corresponding layout and drag item position * @param gridItemId id of the grid item that is been dragged * @param config current grid configuration * @param compactionType type of compaction that will be performed * @param draggingData contains all the information about the drag */ function ktdGridItemDragging(gridItemId, config, compactionType, draggingData) { var pointerDownEvent = draggingData.pointerDownEvent, pointerDragEvent = draggingData.pointerDragEvent, gridElemClientRect = draggingData.gridElemClientRect, dragElemClientRect = draggingData.dragElemClientRect, scrollDifference = draggingData.scrollDifference; var draggingElemPrevItem = config.layout.find(function (item) { return item.id === gridItemId; }); var clientStartX = ktdPointerClientX(pointerDownEvent); var clientStartY = ktdPointerClientY(pointerDownEvent); var clientX = ktdPointerClientX(pointerDragEvent); var clientY = ktdPointerClientY(pointerDragEvent); var offsetX = clientStartX - dragElemClientRect.left; var offsetY = clientStartY - dragElemClientRect.top; // Grid element positions taking into account the possible scroll total difference from the beginning. var gridElementLeftPosition = gridElemClientRect.left + scrollDifference.left; var gridElementTopPosition = gridElemClientRect.top + scrollDifference.top; // Calculate position relative to the grid element. var gridRelXPos = clientX - gridElementLeftPosition - offsetX; var gridRelYPos = clientY - gridElementTopPosition - offsetY; // Get layout item position var layoutItem = Object.assign(Object.assign({}, draggingElemPrevItem), { x: screenXPosToGridValue(gridRelXPos, config.cols, gridElemClientRect.width), y: screenYPosToGridValue(gridRelYPos, config.rowHeight, gridElemClientRect.height) }); // Correct the values if they overflow, since 'moveElement' function doesn't do it layoutItem.x = Math.max(0, layoutItem.x); layoutItem.y = Math.max(0, layoutItem.y); if (layoutItem.x + layoutItem.w > config.cols) { layoutItem.x = Math.max(0, config.cols - layoutItem.w); } // Parse to LayoutItem array data in order to use 'react.grid-layout' utils var layoutItems = config.layout; var draggedLayoutItem = layoutItems.find(function (item) { return item.id === gridItemId; }); var newLayoutItems = moveElement(layoutItems, draggedLayoutItem, layoutItem.x, layoutItem.y, true, config.preventCollision, compactionType, config.cols); newLayoutItems = compact(newLayoutItems, compactionType, config.cols); return { layout: newLayoutItems, draggedItemPos: { top: gridRelYPos, left: gridRelXPos, width: dragElemClientRect.width, height: dragElemClientRect.height, } }; } /** * Given the grid config & layout data and the current drag position & information, returns the corresponding layout and drag item position * @param gridItemId id of the grid item that is been dragged * @param config current grid configuration * @param compactionType type of compaction that will be performed * @param draggingData contains all the information about the drag */ function ktdGridItemResizing(gridItemId, config, compactionType, draggingData) { var pointerDownEvent = draggingData.pointerDownEvent, pointerDragEvent = draggingData.pointerDragEvent, gridElemClientRect = draggingData.gridElemClientRect, dragElemClientRect = draggingData.dragElemClientRect, scrollDifference = draggingData.scrollDifference; var clientStartX = ktdPointerClientX(pointerDownEvent); var clientStartY = ktdPointerClientY(pointerDownEvent); var clientX = ktdPointerClientX(pointerDragEvent); var clientY = ktdPointerClientY(pointerDragEvent); // Get the difference between the mouseDown and the position 'right' of the resize element. var resizeElemOffsetX = dragElemClientRect.width - (clientStartX - dragElemClientRect.left); var resizeElemOffsetY = dragElemClientRect.height - (clientStartY - dragElemClientRect.top); var draggingElemPrevItem = config.layout.find(function (item) { return item.id === gridItemId; }); var width = clientX + resizeElemOffsetX - (dragElemClientRect.left + scrollDifference.left); var height = clientY + resizeElemOffsetY - (dragElemClientRect.top + scrollDifference.top); // Get layout item grid position var layoutItem = Object.assign(Object.assign({}, draggingElemPrevItem), { w: screenXPosToGridValue(width, config.cols, gridElemClientRect.width), h: screenYPosToGridValue(height, config.rowHeight, gridElemClientRect.height) }); layoutItem.w = Math.max(1, layoutItem.w); layoutItem.h = Math.max(1, layoutItem.h); if (layoutItem.x + layoutItem.w > config.cols) { layoutItem.w = Math.max(1, config.cols - layoutItem.x); } if (config.preventCollision) { var maxW = layoutItem.w; var maxH = layoutItem.h; var colliding = hasCollision(config.layout, layoutItem); var shrunkDimension = void 0; while (colliding) { shrunkDimension = getDimensionToShrink(layoutItem, shrunkDimension); layoutItem[shrunkDimension]--; colliding = hasCollision(config.layout, layoutItem); } if (shrunkDimension === 'w') { layoutItem.h = maxH; colliding = hasCollision(config.layout, layoutItem); while (colliding) { layoutItem.h--; colliding = hasCollision(config.layout, layoutItem); } } if (shrunkDimension === 'h') { layoutItem.w = maxW; colliding = hasCollision(config.layout, layoutItem); while (colliding) { layoutItem.w--; colliding = hasCollision(config.layout, layoutItem); } } } var newLayoutItems = config.layout.map(function (item) { return item.id === gridItemId ? layoutItem : item; }); return { layout: compact(newLayoutItems, compactionType, config.cols), draggedItemPos: { top: dragElemClientRect.top - gridElemClientRect.top, left: dragElemClientRect.left - gridElemClientRect.left, width: width, height: height, } }; } function hasCollision(layout, layoutItem) { return !!getFirstCollision(layout, layoutItem); } function getDimensionToShrink(layoutItem, lastShrunk) { if (layoutItem.h <= 1) { return 'w'; } if (layoutItem.w <= 1) { return 'h'; } return lastShrunk === 'w' ? 'h' : 'w'; } /** * Injection token that can be used to reference instances of `KtdGridDragHandle`. It serves as * alternative token to the actual `KtdGridDragHandle` class which could cause unnecessary * retention of the class and its directive metadata. */ var KTD_GRID_DRAG_HANDLE = new i0.InjectionToken('KtdGridDragHandle'); /** Handle that can be used to drag a KtdGridItem instance. */ // tslint:disable-next-line:directive-class-suffix var KtdGridDragHandle = /** @class */ (function () { function KtdGridDragHandle(element) { this.element = element; } return KtdGridDragHandle; }()); KtdGridDragHandle.decorators = [ { type: i0.Directive, args: [{ selector: '[ktdGridDragHandle]', host: { class: 'ktd-grid-drag-handle' }, providers: [{ provide: KTD_GRID_DRAG_HANDLE, useExisting: KtdGridDragHandle }], },] } ]; KtdGridDragHandle.ctorParameters = function () { return [ { type: i0.ElementRef }