diff --git a/demo/two-h5.html b/demo/two-h5.html
new file mode 100644
index 000000000..b4f3c8fe9
--- /dev/null
+++ b/demo/two-h5.html
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+ Two grids demo
+
+
+
+
+
+
+
+
+
+
+
+
Two grids demo
+
+
+
+
+
+
+
+
+
diff --git a/src/dragdrop/dd-base-impl.ts b/src/dragdrop/dd-base-impl.ts
new file mode 100644
index 000000000..34b271559
--- /dev/null
+++ b/src/dragdrop/dd-base-impl.ts
@@ -0,0 +1,42 @@
+// dd-base-impl.ts 2.2.0-dev @preserve
+
+/**
+ * https://gridstackjs.com/
+ * (c) 2020 rhlin, Alain Dumesny
+ * gridstack.js may be freely distributed under the MIT license.
+*/
+export type EventCallback = (event: Event) => boolean|void;
+export abstract class DDBaseImplement {
+ disabled = false;
+ private eventRegister: {
+ [eventName: string]: EventCallback;
+ } = {};
+ on(event: string, callback: EventCallback): void {
+ this.eventRegister[event] = callback;
+ }
+ off(event: string) {
+ delete this.eventRegister[event];
+ }
+ enable(): void {
+ this.disabled = false;
+ }
+ disable(): void {
+ this.disabled = true;
+ }
+ destroy() {
+ this.eventRegister = undefined;
+ }
+ triggerEvent(eventName: string, event: Event): boolean|void {
+ if (this.disabled) { return; }
+ if (!this.eventRegister) {return; } // used when destroy before triggerEvent fire
+ if (this.eventRegister[eventName]) {
+ return this.eventRegister[eventName](event);
+ }
+ }
+}
+
+export interface HTMLElementExtendOpt {
+ el: HTMLElement;
+ option: T;
+ updateOption(T): void;
+}
diff --git a/src/dragdrop/dd-draggable.ts b/src/dragdrop/dd-draggable.ts
new file mode 100644
index 000000000..5805ee2d1
--- /dev/null
+++ b/src/dragdrop/dd-draggable.ts
@@ -0,0 +1,323 @@
+// dd-draggable.ts 2.2.0-dev @preserve
+
+/**
+ * https://gridstackjs.com/
+ * (c) 2020 rhlin, Alain Dumesny
+ * gridstack.js may be freely distributed under the MIT license.
+*/
+import { DDManager } from './dd-manager';
+import { DDUtils } from './dd-utils';
+import { DDBaseImplement, HTMLElementExtendOpt } from './dd-base-impl';
+
+export interface DDDraggableOpt {
+ appendTo?: string | HTMLElement;
+ containment?: string | HTMLElement; // TODO: not implemented yet
+ handle?: string;
+ revert?: string | boolean | unknown; // TODO: not implemented yet
+ scroll?: boolean; // nature support by HTML5 drag drop, can't be switch to off actually
+ helper?: string | ((event: Event) => HTMLElement);
+ basePosition?: 'fixed' | 'absolute';
+ start?: (event?, ui?) => void;
+ stop?: (event?, ui?) => void;
+ drag?: (event?, ui?) => void;
+};
+export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt {
+ static basePosition: 'fixed' | 'absolute' = 'absolute';
+ static dragEventListenerOption = DDUtils.isEventSupportPassiveOption ? { capture: true, passive: true } : true;
+ static originStyleProp = ['transition', 'pointerEvents', 'position',
+ 'left', 'top', 'opacity', 'zIndex', 'width', 'height', 'willChange'];
+ el: HTMLElement;
+ helper: HTMLElement;
+ option: DDDraggableOpt;
+ dragOffset: {
+ left: number;
+ top: number;
+ width: number;
+ height: number;
+ offsetLeft: number;
+ offsetTop: number;
+ };
+ dragElementOriginStyle: Array;
+ dragFollowTimer: number;
+ mouseDownElement: HTMLElement;
+ dragging = false;
+ paintTimer: number;
+ parentOriginStylePosition: string;
+ helperContainment: HTMLElement;
+
+ constructor(el: HTMLElement, option: DDDraggableOpt) {
+ super();
+ this.el = el;
+ this.option = option || {};
+ this.init();
+ }
+
+ on(event: 'drag' | 'dragstart' | 'dragstop', callback: (event: DragEvent) => void): void {
+ super.on(event, callback);
+ }
+
+ off(event: 'drag' | 'dragstart' | 'dragstop') {
+ super.off(event);
+ }
+
+ enable() {
+ super.enable();
+ this.el.draggable = true;
+ this.el.classList.remove('ui-draggable-disabled');
+ }
+
+ disable() {
+ super.disable();
+ this.el.draggable = false;
+ this.el.classList.add('ui-draggable-disabled');
+ }
+
+ updateOption(opts) {
+ Object.keys(opts).forEach(key => {
+ const value = opts[key];
+ this.option[key] = value;
+ });
+ }
+
+ protected init() {
+ this.el.draggable = true;
+ this.el.classList.add('ui-draggable');
+ this.el.addEventListener('mousedown', this.mouseDown);
+ this.el.addEventListener('dragstart', this.dragStart);
+ }
+
+ protected mouseDown = (event: MouseEvent) => {
+ this.mouseDownElement = event.target as HTMLElement;
+ }
+
+ protected dragStart = (event: DragEvent) => {
+ if (this.option.handle && !(
+ this.mouseDownElement
+ && this.mouseDownElement.matches(
+ `${this.option.handle}, ${this.option.handle} > *`
+ )
+ )) {
+ event.preventDefault();
+ return;
+ }
+ DDManager.dragElement = this;
+ this.helper = this.createHelper(event);
+ this.setupHelperContainmentStyle();
+ this.dragOffset = this.getDragOffset(event, this.el, this.helperContainment);
+ const ev = DDUtils.initEvent(event, { target: this.el, type: 'dragstart' });
+ if (this.helper !== this.el) {
+ this.setupDragFollowNodeNNotifyStart(ev);
+ } else {
+ this.dragFollowTimer = window.setTimeout(() => {
+ this.dragFollowTimer = undefined;
+ this.setupDragFollowNodeNNotifyStart(ev);
+ }, 0);
+ }
+ this.cancelDragGhost(event);
+ }
+
+ protected setupDragFollowNodeNNotifyStart(ev) {
+ this.setupHelperStyle();
+ document.addEventListener('dragover', this.drag, DDDraggable.dragEventListenerOption);
+ this.el.addEventListener('dragend', this.dragEnd);
+ if (this.option.start) {
+ this.option.start(ev, this.ui());
+ }
+ this.dragging = true;
+ this.helper.classList.add('ui-draggable-dragging');
+ this.triggerEvent('dragstart', ev);
+ }
+
+ protected drag = (event: DragEvent) => {
+ this.dragFollow(event);
+ const ev = DDUtils.initEvent(event, { target: this.el, type: 'drag' });
+ if (this.option.drag) {
+ this.option.drag(ev, this.ui());
+ }
+ this.triggerEvent('drag', ev);
+ }
+
+ protected dragEnd = (event: DragEvent) => {
+ if (this.dragFollowTimer) {
+ clearTimeout(this.dragFollowTimer);
+ this.dragFollowTimer = undefined;
+ return;
+ } else {
+ if (this.paintTimer) {
+ cancelAnimationFrame(this.paintTimer);
+ }
+ document.removeEventListener('dragover', this.drag, DDDraggable.dragEventListenerOption);
+ this.el.removeEventListener('dragend', this.dragEnd);
+ }
+ this.dragging = false;
+ this.helper.classList.remove('ui-draggable-dragging');
+ this.helperContainment.style.position = this.parentOriginStylePosition || null;
+ if (this.helper === this.el) {
+ this.removeHelperStyle();
+ } else {
+ this.helper.remove();
+ }
+ const ev = DDUtils.initEvent(event, { target: this.el, type: 'dragstop' });
+ if (this.option.stop) {
+ this.option.stop(ev, this.ui());
+ }
+ this.triggerEvent('dragstop', ev);
+ DDManager.dragElement = undefined;
+ this.helper = undefined;
+ this.mouseDownElement = undefined;
+ }
+
+ private createHelper(event: DragEvent) {
+ const helperIsFunction = (typeof this.option.helper) === 'function';
+ const helper = (helperIsFunction
+ ? (this.option.helper as ((event: Event) => HTMLElement)).apply(this.el, [event])
+ : (this.option.helper === "clone" ? DDUtils.clone(this.el) : this.el)
+ ) as HTMLElement;
+ if (!document.body.contains(helper)) {
+ DDUtils.appendTo(helper, (this.option.appendTo === "parent"
+ ? this.el.parentNode
+ : this.option.appendTo));
+ }
+ if (helper === this.el) {
+ this.dragElementOriginStyle = DDDraggable.originStyleProp.map(prop => this.el.style[prop]);
+ }
+ return helper;
+ }
+
+ private setupHelperStyle() {
+ this.helper.style.pointerEvents = 'none';
+ this.helper.style.width = this.dragOffset.width + 'px';
+ this.helper.style.height = this.dragOffset.height + 'px';
+ this.helper.style['willChange'] = 'left, top';
+ this.helper.style.transition = 'none'; // show up instantly
+ this.helper.style.position = this.option.basePosition || DDDraggable.basePosition;
+ this.helper.style.zIndex = '1000';
+ setTimeout(() => {
+ if (this.helper) {
+ this.helper.style.transition = null; // recover animation
+ }
+ }, 0);
+ }
+
+ private removeHelperStyle() {
+ DDDraggable.originStyleProp.forEach(prop => {
+ this.helper.style[prop] = this.dragElementOriginStyle[prop] || null;
+ });
+ this.dragElementOriginStyle = undefined;
+ }
+
+ private dragFollow = (event: DragEvent) => {
+ if (this.paintTimer) {
+ cancelAnimationFrame(this.paintTimer);
+ }
+ this.paintTimer = requestAnimationFrame(() => {
+ this.paintTimer = undefined;
+ const offset = this.dragOffset;
+ let containmentRect = { left: 0, top: 0 };
+ if (this.helper.style.position === 'absolute') {
+ const { left, top } = this.helperContainment.getBoundingClientRect();
+ containmentRect = { left, top };
+ }
+ this.helper.style.left = event.clientX + offset.offsetLeft - containmentRect.left + 'px';
+ this.helper.style.top = event.clientY + offset.offsetTop - containmentRect.top + 'px';
+ });
+ }
+
+ private setupHelperContainmentStyle() {
+ this.helperContainment = this.helper.parentElement;
+ if (this.option.basePosition !== 'fixed') {
+ this.parentOriginStylePosition = this.helperContainment.style.position;
+ if (window.getComputedStyle(this.helperContainment).position.match(/static/)) {
+ this.helperContainment.style.position = 'relative';
+ }
+ }
+ }
+
+ private cancelDragGhost(e: DragEvent) {
+ if (e.dataTransfer != null) {
+ e.dataTransfer.setData('text', '');
+ }
+ e.dataTransfer.effectAllowed = 'move';
+ if ('function' === typeof DataTransfer.prototype.setDragImage) {
+ e.dataTransfer.setDragImage(new Image(), 0, 0);
+ } else {
+ // ie
+ (e.target as HTMLElement).style.display = 'none';
+ setTimeout(() => {
+ (e.target as HTMLElement).style.display = '';
+ });
+ e.stopPropagation();
+ return;
+ }
+ e.stopPropagation();
+ }
+
+ private getDragOffset(event: DragEvent, el: HTMLElement, attachedParent: HTMLElement) {
+ // in case ancestor has transform/perspective css properties that change the viewpoint
+ const getViewPointFromParent = (parent) => {
+ if (!parent) { return null; }
+ const testEl = document.createElement('div');
+ DDUtils.addElStyles(testEl, {
+ opacity: '0',
+ position: 'fixed',
+ top: 0 + 'px',
+ left: 0 + 'px',
+ width: '1px',
+ height: '1px',
+ zIndex: '-999999',
+ });
+ parent.appendChild(testEl);
+ const testElPosition = testEl.getBoundingClientRect();
+ parent.removeChild(testEl);
+ return {
+ offsetX: testElPosition.left,
+ offsetY: testElPosition.top
+ };
+ }
+ const targetOffset = el.getBoundingClientRect();
+ const mousePositionXY = {
+ x: event.clientX,
+ y: event.clientY
+ };
+ const transformOffset = getViewPointFromParent(attachedParent);
+ return {
+ left: targetOffset.left,
+ top: targetOffset.top,
+ offsetLeft: - mousePositionXY.x + targetOffset.left - transformOffset.offsetX,
+ offsetTop: - mousePositionXY.y + targetOffset.top - transformOffset.offsetY,
+ width: targetOffset.width,
+ height: targetOffset.height
+ };
+ }
+ destroy() {
+ if (this.dragging) {
+ // Destroy while dragging should remove dragend listener and manually trigger
+ // dragend, otherwise dragEnd can't perform dragstop because eventRegistry is
+ // destroyed.
+ this.dragEnd({} as DragEvent);
+ }
+ this.el.draggable = false;
+ this.el.classList.remove('ui-draggable');
+ this.el.removeEventListener('dragstart', this.dragStart);
+ this.el = undefined;
+ this.helper = undefined;
+ this.option = undefined;
+ super.destroy();
+ }
+
+ ui = () => {
+ const containmentEl = this.el.parentElement;
+ const containmentRect = containmentEl.getBoundingClientRect();
+ const offset = this.helper.getBoundingClientRect();
+ return {
+ helper: [this.helper], //The object arr representing the helper that's being dragged.
+ position: {
+ top: offset.top - containmentRect.top,
+ left: offset.left - containmentRect.left
+ }, //Current CSS position of the helper as { top, left } object
+ offset: { top: offset.top, left: offset.left } // Current offset position of the helper as { top, left } object.
+ };
+ }
+}
+
+
diff --git a/src/dragdrop/dd-droppable.ts b/src/dragdrop/dd-droppable.ts
new file mode 100644
index 000000000..45670ba1d
--- /dev/null
+++ b/src/dragdrop/dd-droppable.ts
@@ -0,0 +1,162 @@
+// dd-droppable.ts 2.2.0-dev @preserve
+
+/**
+ * https://gridstackjs.com/
+ * (c) 2020 rhlin, Alain Dumesny
+ * gridstack.js may be freely distributed under the MIT license.
+*/
+import { DDDraggable } from './dd-draggable';
+import { DDManager } from './dd-manager';
+import { DDBaseImplement, HTMLElementExtendOpt } from './dd-base-impl';
+import { DDUtils } from './dd-utils';
+
+export interface DDDroppableOpt {
+ accept?: string | ((el: HTMLElement) => boolean);
+ drop?: (event: DragEvent, ui) => void;
+ over?: (event: DragEvent, ui) => void;
+ out?: (event: DragEvent, ui) => void;
+};
+export class DDDroppable extends DDBaseImplement implements HTMLElementExtendOpt {
+ accept: (el: HTMLElement) => boolean;
+ el: HTMLElement;
+ option: DDDroppableOpt;
+ private acceptable: boolean = null;
+ private style;
+ constructor(el: HTMLElement, opts: DDDroppableOpt) {
+ super();
+ this.el = el;
+ this.option = opts || {};
+ this.init();
+ }
+ on(event: 'drop' | 'dropover' | 'dropout', callback: (event: DragEvent) => void): void {
+ super.on(event, callback);
+ }
+ off(event: 'drop' | 'dropover' | 'dropout') {
+ super.off(event);
+ }
+ enable() {
+ if (!this.disabled) { return; }
+ super.enable();
+ this.el.classList.remove('ui-droppable-disabled');
+ this.el.addEventListener('dragenter', this.dragEnter);
+ }
+ disable() {
+ if (this.disabled) { return; }
+ super.disable();
+ this.el.classList.add('ui-droppable-disabled');
+ this.el.removeEventListener('dragenter', this.dragEnter);
+ }
+ updateOption(opts) {
+ Object.keys(opts).forEach(key => {
+ const value = opts[key];
+ this.option[key] = value;
+ });
+ this.setupAccept();
+ }
+
+ protected init() {
+ this.el.classList.add('ui-droppable');
+ this.el.addEventListener('dragenter', this.dragEnter);
+
+ this.setupAccept();
+ this.createStyleSheet();
+ }
+
+ protected dragEnter = (event: DragEvent) => {
+ this.el.removeEventListener('dragenter', this.dragEnter);
+ this.acceptable = this.canDrop();
+ if (this.acceptable) {
+ event.preventDefault();
+ const ev = DDUtils.initEvent(event, { target: this.el, type: 'dropover' });
+ if (this.option.over) {
+ this.option.over(ev, this.ui(DDManager.dragElement))
+ }
+ this.triggerEvent('dropover', ev);
+ this.el.addEventListener('dragover', this.dragOver);
+ this.el.addEventListener('drop', this.drop);
+ }
+ this.el.classList.add('ui-droppable-over');
+ this.el.addEventListener('dragleave', this.dragLeave);
+
+ }
+ protected dragOver = (event: DragEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ protected dragLeave = (event: DragEvent) => {
+ if (this.el.contains(event.relatedTarget as HTMLElement)) { return; };
+ this.el.removeEventListener('dragleave', this.dragLeave);
+ this.el.classList.remove('ui-droppable-over');
+ if (this.acceptable) {
+ event.preventDefault();
+ this.el.removeEventListener('dragover', this.dragOver);
+ this.el.removeEventListener('drop', this.drop);
+ const ev = DDUtils.initEvent(event, { target: this.el, type: 'dropout' });
+ if (this.option.out) {
+ this.option.out(ev, this.ui(DDManager.dragElement))
+ }
+ this.triggerEvent('dropout', ev);
+ }
+ this.el.addEventListener('dragenter', this.dragEnter);
+ }
+
+ protected drop = (event: DragEvent) => {
+ if (this.acceptable) {
+ event.preventDefault();
+ const ev = DDUtils.initEvent(event, { target: this.el, type: 'drop' });
+ if (this.option.drop) {
+ this.option.drop(ev, this.ui(DDManager.dragElement))
+ }
+ this.triggerEvent('drop', ev);
+ this.dragLeave({
+ ...ev,
+ relatedTarget: null,
+ preventDefault: () => {
+ // do nothing
+ }
+ });
+ }
+ }
+ private canDrop() {
+ return DDManager.dragElement && (!this.accept || this.accept(DDManager.dragElement.el));
+ }
+ private setupAccept() {
+ if (this.option.accept && typeof this.option.accept === 'string') {
+ this.accept = (el: HTMLElement) => {
+ return el.matches(this.option.accept as string)
+ }
+ } else {
+ this.accept = this.option.accept as ((el: HTMLElement) => boolean);
+ }
+ }
+
+ private createStyleSheet() {
+ const content = `.ui-droppable.ui-droppable-over > *:not(.ui-droppable) {pointer-events: none;}`;
+ this.style = document.createElement('style');
+ this.style.innerText = content;
+ this.el.appendChild(this.style);
+ }
+ private removeStyleSheet() {
+ this.el.removeChild(this.style);
+ }
+
+ destroy() {
+ this.el.classList.remove('ui-droppable');
+ if (this.disabled) {
+ this.el.classList.remove('ui-droppable-disabled');
+ this.el.removeEventListener('dragenter', this.dragEnter);
+ this.el.removeEventListener('dragover', this.dragOver);
+ this.el.removeEventListener('drop', this.drop);
+ this.el.removeEventListener('dragleave', this.dragLeave);
+ }
+ super.destroy();
+ }
+
+ ui(DDDraggable: DDDraggable) {
+ return {
+ draggable: DDDraggable.el,
+ ...DDDraggable.ui()
+ };
+ }
+}
+
diff --git a/src/dragdrop/dd-element.ts b/src/dragdrop/dd-element.ts
new file mode 100644
index 000000000..36eb41f85
--- /dev/null
+++ b/src/dragdrop/dd-element.ts
@@ -0,0 +1,93 @@
+// dd-elements.ts 2.2.0-dev @preserve
+
+/**
+ * https://gridstackjs.com/
+ * (c) 2020 rhlin, Alain Dumesny
+ * gridstack.js may be freely distributed under the MIT license.
+*/
+import { DDResizable, DDResizableOpt } from './dd-resizable';
+import { GridItemHTMLElement } from './../types';
+import { DDDraggable, DDDraggableOpt } from './dd-draggable';
+import { DDDroppable, DDDroppableOpt } from './dd-droppable';
+export interface DDElementHost extends GridItemHTMLElement {
+ ddElement?: DDElement;
+}
+export class DDElement {
+ static init(el) {
+ el.ddElement = new DDElement(el);
+ return el.ddElement;
+ }
+ el: DDElementHost;
+ ddDraggable?: DDDraggable;
+ ddDroppable?: DDDroppable;
+ ddResizable?: DDResizable;
+ constructor(el: DDElementHost) {
+ this.el = el;
+ }
+ on(eventName: string, callback: (event: MouseEvent) => void) {
+ if (this.ddDraggable && ['drag', 'dragstart', 'dragstop'].indexOf(eventName) > -1) {
+ this.ddDraggable.on(eventName as 'drag' | 'dragstart' | 'dragstop', callback);
+ return;
+ }
+ if (this.ddDroppable && ['drop', 'dropover', 'dropout'].indexOf(eventName) > -1) {
+ this.ddDroppable.on(eventName as 'drop' | 'dropover' | 'dropout', callback);
+ return;
+ }
+ if (this.ddResizable && ['resizestart', 'resize', 'resizestop'].indexOf(eventName) > -1) {
+ this.ddResizable.on(eventName as 'resizestart' | 'resize' | 'resizestop', callback);
+ return;
+ }
+ return;
+ }
+ off(eventName: string) {
+ if (this.ddDraggable && ['drag', 'dragstart', 'dragstop'].indexOf(eventName) > -1) {
+ this.ddDraggable.off(eventName as 'drag' | 'dragstart' | 'dragstop');
+ return;
+ }
+ if (this.ddDroppable && ['drop', 'dropover', 'dropout'].indexOf(eventName) > -1) {
+ this.ddDroppable.off(eventName as 'drop' | 'dropover' | 'dropout');
+ return;
+ }
+ if (this.ddResizable && ['resizestart', 'resize', 'resizestop'].indexOf(eventName) > -1) {
+ this.ddResizable.off(eventName as 'resizestart' | 'resize' | 'resizestop');
+ return;
+ }
+ return;
+ }
+ setupDraggable(opts: DDDraggableOpt) {
+ if (!this.ddDraggable) {
+ this.ddDraggable = new DDDraggable(this.el, opts);
+ } else {
+ this.ddDraggable.updateOption(opts);
+ }
+ }
+ setupResizable(opts: DDResizableOpt) {
+ if (!this.ddResizable) {
+ this.ddResizable = new DDResizable(this.el, opts);
+ } else {
+ this.ddResizable.updateOption(opts);
+ }
+ }
+ cleanDraggable() {
+ if (!this.ddDraggable) { return; }
+ this.ddDraggable.destroy();
+ this.ddDraggable = undefined;
+ }
+ setupDroppable(opts: DDDroppableOpt) {
+ if (!this.ddDroppable) {
+ this.ddDroppable = new DDDroppable(this.el, opts);
+ } else {
+ this.ddDroppable.updateOption(opts);
+ }
+ }
+ cleanDroppable() {
+ if (!this.ddDroppable) { return; }
+ this.ddDroppable.destroy();
+ this.ddDroppable = undefined;
+ }
+ cleanResizable() {
+ if (!this.cleanResizable) { return; }
+ this.ddResizable.destroy();
+ this.ddResizable = undefined;
+ }
+}
diff --git a/src/dragdrop/dd-manager.ts b/src/dragdrop/dd-manager.ts
new file mode 100644
index 000000000..702bd93a8
--- /dev/null
+++ b/src/dragdrop/dd-manager.ts
@@ -0,0 +1,11 @@
+// dd-manager.ts 2.2.0-dev @preserve
+
+/**
+ * https://gridstackjs.com/
+ * (c) 2020 rhlin, Alain Dumesny
+ * gridstack.js may be freely distributed under the MIT license.
+*/
+import { DDDraggable } from './dd-draggable';
+export class DDManager {
+ static dragElement: DDDraggable;
+}
diff --git a/src/dragdrop/dd-resizable-handle.ts b/src/dragdrop/dd-resizable-handle.ts
new file mode 100644
index 000000000..60fa65b6a
--- /dev/null
+++ b/src/dragdrop/dd-resizable-handle.ts
@@ -0,0 +1,106 @@
+// dd-resizable-handle.ts 2.2.0-dev @preserve
+
+/**
+ * https://gridstackjs.com/
+ * (c) 2020 rhlin, Alain Dumesny
+ * gridstack.js may be freely distributed under the MIT license.
+*/
+export interface DDResizableHandleOpt {
+ start?: (event) => void;
+ move?: (event) => void;
+ stop?: (event) => void;
+}
+export class DDResizableHandle {
+ static prefix = 'ui-resizable-';
+ el: HTMLElement;
+ host: HTMLElement;
+ option: DDResizableHandleOpt;
+ dir: string;
+ private mouseMoving = false;
+ private started = false;
+ private mouseDownEvent: MouseEvent;
+ constructor(host: HTMLElement, direction: string, option: DDResizableHandleOpt) {
+ this.host = host;
+ this.dir = direction;
+ this.option = option;
+ this.init();
+ }
+
+ init() {
+ const el = document.createElement('div');
+ el.classList.add('ui-resizable-handle');
+ el.classList.add(`${DDResizableHandle.prefix}${this.dir}`);
+ el.style.zIndex = '100';
+ el.style.userSelect = 'none';
+ this.el = el;
+ this.host.appendChild(this.el);
+ this.el.addEventListener('mousedown', this.mouseDown);
+ }
+
+ protected mouseDown = (event: MouseEvent) => {
+ this.mouseDownEvent = event;
+ setTimeout(() => {
+ document.addEventListener('mousemove', this.mouseMove, true);
+ document.addEventListener('mouseup', this.mouseUp);
+ setTimeout(() => {
+ if (!this.mouseMoving) {
+ document.removeEventListener('mousemove', this.mouseMove, true);
+ document.removeEventListener('mouseup', this.mouseUp);
+ this.mouseDownEvent = undefined;
+ }
+ }, 300);
+ }, 100);
+ }
+
+ protected mouseMove = (event: MouseEvent) => {
+ if (!this.started && !this.mouseMoving) {
+ if (this.hasMoved(event, this.mouseDownEvent)) {
+ this.mouseMoving = true;
+ this.triggerEvent('start', this.mouseDownEvent);
+ this.started = true;
+ }
+ }
+ if (this.started) {
+ this.triggerEvent('move', event);
+ }
+ }
+
+ protected mouseUp = (event: MouseEvent) => {
+ if (this.mouseMoving) {
+ this.triggerEvent('stop', event);
+ }
+ document.removeEventListener('mousemove', this.mouseMove, true);
+ document.removeEventListener('mouseup', this.mouseUp);
+ this.mouseMoving = false;
+ this.started = false;
+ this.mouseDownEvent = undefined;
+ }
+
+ private hasMoved(event: MouseEvent, oEvent: MouseEvent) {
+ const { clientX, clientY } = event;
+ const { clientX: oClientX, clientY: oClientY } = oEvent;
+ return (
+ Math.abs(clientX - oClientX) > 1
+ || Math.abs(clientY - oClientY) > 1
+ );
+ }
+
+ show() {
+ this.el.style.display = 'block';
+ }
+
+ hide() {
+ this.el.style.display = 'none';
+ }
+
+ destroy() {
+ this.host.removeChild(this.el);
+ }
+
+ triggerEvent(name: string, event: MouseEvent) {
+ if (this.option[name]) {
+ this.option[name](event);
+ }
+ }
+
+}
diff --git a/src/dragdrop/dd-resizable.ts b/src/dragdrop/dd-resizable.ts
new file mode 100644
index 000000000..d13a619e3
--- /dev/null
+++ b/src/dragdrop/dd-resizable.ts
@@ -0,0 +1,283 @@
+// dd-resizable.ts 2.2.0-dev @preserve
+
+/**
+ * https://gridstackjs.com/
+ * (c) 2020 rhlin, Alain Dumesny
+ * gridstack.js may be freely distributed under the MIT license.
+*/
+import { DDResizableHandle } from './dd-resizable-handle';
+import { DDBaseImplement, HTMLElementExtendOpt } from './dd-base-impl';
+import { DDUtils } from './dd-utils';
+export interface DDResizableOpt {
+ autoHide?: boolean;
+ handles?: string;
+ maxHeight?: number;
+ maxWidth?: number;
+ minHeight?: number;
+ minWidth?: number;
+ basePosition?: 'fixed' | 'absolute';
+ start?: (event: MouseEvent, ui) => void;
+ stop?: (event: MouseEvent, ui) => void;
+ resize?: (event: MouseEvent, ui) => void;
+}
+export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt {
+ static originStyleProp = ['width', 'height', 'position', 'left', 'top', 'opacity', 'zIndex'];
+ el: HTMLElement;
+ option: DDResizableOpt;
+ handlers: DDResizableHandle[];
+ helper: HTMLElement;
+ originalRect;
+ temporalRect;
+ private startEvent: MouseEvent;
+ private elOriginStyle;
+ private parentOriginStylePosition;
+ constructor(el: HTMLElement, opts: DDResizableOpt) {
+ super();
+ this.el = el;
+ this.option = opts || {};
+ this.init();
+ }
+ on(event: 'resizestart' | 'resize' | 'resizestop', callback: (event: DragEvent) => void): void {
+ super.on(event, callback);
+ }
+ off(event: 'resizestart' | 'resize' | 'resizestop') {
+ super.off(event);
+ }
+ enable() {
+ if (!this.disabled) { return; }
+ super.enable();
+ this.el.classList.remove('ui-resizable-disabled');
+ }
+ disable() {
+ if (this.disabled) { return; }
+ super.disable();
+ this.el.classList.add('ui-resizable-disabled');
+ }
+ updateOption(opts: DDResizableOpt) {
+ let updateHandles = false;
+ let updateAutoHide = false;
+ if (opts.handles !== this.option.handles) {
+ updateHandles = true;
+ }
+ if (opts.autoHide !== this.option.autoHide) {
+ updateAutoHide = true;
+ }
+ Object.keys(opts).forEach(key => {
+ const value = opts[key];
+ this.option[key] = value;
+ });
+ if (updateHandles) {
+ this.removeHandlers();
+ this.setupHandlers();
+ }
+ if (updateAutoHide) {
+ this.setupAutoHide();
+ }
+ }
+
+ protected init() {
+ this.el.classList.add('ui-resizable');
+ this.setupAutoHide();
+ this.setupHandlers();
+ }
+
+ protected setupAutoHide() {
+ if (this.option.autoHide) {
+ this.el.classList.add('ui-resizable-autohide');
+ // use mouseover/mouseout instead of mouseenter mouseleave to get better performance;
+ this.el.addEventListener('mouseover', this.showHandlers);
+ this.el.addEventListener('mouseout', this.hideHandlers);
+ } else {
+ this.el.classList.remove('ui-resizable-autohide');
+ this.el.removeEventListener('mouseover', this.showHandlers);
+ this.el.removeEventListener('mouseout', this.hideHandlers);
+ }
+ }
+
+ protected showHandlers = () => {
+ this.el.classList.remove('ui-resizable-autohide');
+ }
+
+ protected hideHandlers = () => {
+ this.el.classList.add('ui-resizable-autohide');
+ }
+
+ protected setupHandlers() {
+ let handlerDirection = this.option.handles || 'e,s,se';
+ if (handlerDirection === 'all') {
+ handlerDirection = 'n,e,s,w,se,sw,ne,nw';
+ }
+ this.handlers = handlerDirection.split(',')
+ .map(dir => dir.trim())
+ .map(dir => new DDResizableHandle(this.el, dir, {
+ start: (event: MouseEvent) => {
+ this.resizeStart(event);
+ },
+ stop: (event: MouseEvent) => {
+ this.resizeStop(event);
+ },
+ move: (event: MouseEvent) => {
+ this.resizing(event, dir);
+ }
+ }));
+ }
+
+ protected resizeStart(event: MouseEvent) {
+ this.originalRect = this.el.getBoundingClientRect();
+ this.startEvent = event;
+ this.setupHelper();
+ this.applyChange();
+ const ev = DDUtils.initEvent(event, { type: 'resizestart', target: this.el });
+ if (this.option.start) {
+ this.option.start(ev, this.ui());
+ }
+ this.el.classList.add('ui-resizable-resizing');
+ this.triggerEvent('resizestart', ev);
+ }
+
+ protected resizing(event: MouseEvent, dir: string) {
+ this.temporalRect = this.getChange(event, dir);
+ this.applyChange();
+ const ev = DDUtils.initEvent(event, { type: 'resize', target: this.el });
+ if (this.option.resize) {
+ this.option.resize(ev, this.ui());
+ }
+ this.triggerEvent('resize', ev);
+ }
+
+ protected resizeStop(event: MouseEvent) {
+ const ev = DDUtils.initEvent(event, { type: 'resizestop', target: this.el });
+ if (this.option.stop) {
+ this.option.stop(ev, this.ui());
+ }
+ this.el.classList.remove('ui-resizable-resizing');
+ this.triggerEvent('resizestop', ev);
+ this.cleanHelper();
+ this.startEvent = undefined;
+ this.originalRect = undefined;
+ this.temporalRect = undefined;
+ }
+
+ private setupHelper() {
+ this.elOriginStyle = DDResizable.originStyleProp.map(prop => this.el.style[prop]);
+ this.parentOriginStylePosition = this.el.parentElement.style.position;
+ if (window.getComputedStyle(this.el.parentElement).position.match(/static/)) {
+ this.el.parentElement.style.position = 'relative';
+ }
+ this.el.style.position = this.option.basePosition || 'absolute'; // or 'fixed'
+ this.el.style.opacity = '0.8';
+ this.el.style.zIndex = '1000';
+ }
+ private cleanHelper() {
+ DDResizable.originStyleProp.forEach(prop => {
+ this.el.style[prop] = this.elOriginStyle[prop] || null;
+ });
+ this.el.parentElement.style.position = this.parentOriginStylePosition || null;
+ }
+ private getChange(event: MouseEvent, dir: string) {
+ const oEvent = this.startEvent;
+ const newRect = {
+ width: this.originalRect.width,
+ height: this.originalRect.height,
+ left: this.originalRect.left,
+ top: this.originalRect.top
+ };
+ const offsetH = event.clientX - oEvent.clientX;
+ const offsetV = event.clientY - oEvent.clientY;
+
+ if (dir.indexOf('e') > -1) {
+ newRect.width += event.clientX - oEvent.clientX;
+ }
+ if (dir.indexOf('s') > -1) {
+ newRect.height += event.clientY - oEvent.clientY;
+ }
+ if (dir.indexOf('w') > -1) {
+ newRect.width -= offsetH;
+ newRect.left += offsetH;
+ }
+ if (dir.indexOf('n') > -1) {
+ newRect.height -= offsetV;
+ newRect.top += offsetV
+ }
+ const reshape = this.getReShapeSize(newRect.width, newRect.height);
+ if (newRect.width !== reshape.width) {
+ if (dir.indexOf('w') > -1) {
+ newRect.left += reshape.width - newRect.width;
+ }
+ newRect.width = reshape.width;
+ }
+ if (newRect.height !== reshape.height) {
+ if (dir.indexOf('n') > -1) {
+ newRect.top += reshape.height - newRect.height;
+ }
+ newRect.height = reshape.height;
+ }
+ return newRect;
+ }
+
+ private getReShapeSize(oWidth, oHeight) {
+ const maxWidth = this.option.maxWidth || oWidth;
+ const minWidth = this.option.minWidth || oWidth;
+ const maxHeight = this.option.maxHeight || oHeight;
+ const minHeight = this.option.minHeight || oHeight;
+ const width = Math.min(maxWidth, Math.max(minWidth, oWidth));
+ const height = Math.min(maxHeight, Math.max(minHeight, oHeight));
+ return { width, height };
+ }
+
+ private applyChange() {
+ let containmentRect = { left: 0, top: 0, width: 0, height: 0 };
+ if (this.el.style.position === 'absolute') {
+ const containmentEl = this.el.parentElement;
+ const { left, top } = containmentEl.getBoundingClientRect();
+ containmentRect = { left, top, width: 0, height: 0 };
+ }
+ Object.keys(this.temporalRect || this.originalRect).forEach(key => {
+ const value = this.temporalRect[key];
+ this.el.style[key] = value - containmentRect[key] + 'px';
+ });
+ }
+
+ protected removeHandlers() {
+ this.handlers.forEach(handle => handle.destroy());
+ this.handlers = undefined;
+ }
+
+ destroy() {
+ this.removeHandlers();
+ if (this.option.autoHide) {
+ this.el.removeEventListener('mouseover', this.showHandlers);
+ this.el.removeEventListener('mouseout', this.hideHandlers);
+ }
+ this.el.classList.remove('ui-resizable');
+ this.el = undefined;
+ super.destroy();
+ }
+
+ ui = () => {
+ const containmentEl = this.el.parentElement;
+ const containmentRect = containmentEl.getBoundingClientRect();
+ const rect = this.temporalRect || this.originalRect;
+ return {
+ element: [this.el], // The object representing the element to be resized
+ helper: [], // TODO: not support yet - The object representing the helper that's being resized
+ originalElement: [this.el],// we don't wrap here, so simplify as this.el //The object representing the original element before it is wrapped
+ originalPosition: {
+ left: this.originalRect.left - containmentRect.left,
+ top: this.originalRect.top - containmentRect.top
+ }, // The position represented as { left, top } before the resizable is resized
+ originalSize: {
+ width: this.originalRect.width,
+ height: this.originalRect.height
+ },// The size represented as { width, height } before the resizable is resized
+ position: {
+ left: rect.left - containmentRect.left,
+ top: rect.top - containmentRect.top
+ }, // The current position represented as { left, top }
+ size: {
+ width: rect.width,
+ height: rect.height
+ } // The current size represented as { width, height }
+ };
+ }
+}
diff --git a/src/dragdrop/dd-utils.ts b/src/dragdrop/dd-utils.ts
new file mode 100644
index 000000000..039674ae7
--- /dev/null
+++ b/src/dragdrop/dd-utils.ts
@@ -0,0 +1,91 @@
+// dd-utils.ts 2.2.0-dev @preserve
+
+/**
+ * https://gridstackjs.com/
+ * (c) 2020 rhlin, Alain Dumesny
+ * gridstack.js may be freely distributed under the MIT license.
+*/
+export class DDUtils {
+ static isEventSupportPassiveOption = ((() => {
+ let supportsPassive = false;
+ let passiveTest = () => {
+ // do nothing
+ };
+ document.addEventListener('test', passiveTest, {
+ get passive() {
+ supportsPassive = true;
+ return true;
+ }
+ });
+ document.removeEventListener('test', passiveTest);
+ return supportsPassive;
+ })());
+
+ static clone(el: HTMLElement): HTMLElement {
+ const node = el.cloneNode(true) as HTMLElement;
+ node.removeAttribute('id');
+ return node;
+ }
+
+ static appendTo(el: HTMLElement, parent: string | HTMLElement | Node) {
+ let parentNode: HTMLElement;
+ if (typeof parent === 'string') {
+ parentNode = document.querySelector(parent as string);
+ } else {
+ parentNode = parent as HTMLElement;
+ }
+ if (parentNode) {
+ parentNode.append(el);
+ }
+ }
+ static setPositionRelative(el) {
+ if (!(/^(?:r|a|f)/).test(window.getComputedStyle(el).position)) {
+ el.style.position = "relative";
+ }
+ }
+
+ static addElStyles(el: HTMLElement, styles: { [prop: string]: string | string[] }) {
+ if (styles instanceof Object) {
+ for (const s in styles) {
+ if (styles.hasOwnProperty(s)) {
+ if (Array.isArray(styles[s])) {
+ // support fallback value
+ (styles[s] as string[]).forEach(val => {
+ el.style[s] = val;
+ });
+ } else {
+ el.style[s] = styles[s];
+ }
+ }
+ }
+ }
+ }
+ static copyProps(dst, src, props) {
+ for (let i = 0; i < props.length; i++) {
+ const p = props[i];
+ dst[p] = src[p];
+ }
+ }
+
+ static initEvent(e: DragEvent | MouseEvent, info: { type: string; target?: EventTarget }) {
+ const kbdProps = 'altKey,ctrlKey,metaKey,shiftKey'.split(',');
+ const ptProps = 'pageX,pageY,clientX,clientY,screenX,screenY'.split(',');
+ const evt = { type: info.type };
+ const obj = {
+ button: 0,
+ which: 0,
+ buttons: 1,
+ bubbles: true,
+ cancelable: true,
+ originEvent: e,
+ target: info.target ? info.target : e.target
+ }
+ if (e instanceof DragEvent) {
+ Object.assign(obj, { dataTransfer: e.dataTransfer });
+ }
+ DDUtils.copyProps(evt, e, kbdProps);
+ DDUtils.copyProps(evt, e, ptProps);
+ DDUtils.copyProps(evt, obj, Object.keys(obj));
+ return evt as unknown as T;
+ }
+}
diff --git a/src/dragdrop/gridstack-dd-native.ts b/src/dragdrop/gridstack-dd-native.ts
new file mode 100644
index 000000000..413feb020
--- /dev/null
+++ b/src/dragdrop/gridstack-dd-native.ts
@@ -0,0 +1,138 @@
+// gridstack-dd-native.ts 2.2.0-dev @preserve
+
+/**
+ * https://gridstackjs.com/
+ * (c) 2020 rhlin, Alain Dumesny
+ * gridstack.js may be freely distributed under the MIT license.
+*/
+import { DDManager } from './dd-manager';
+import { DDElement } from './dd-element';
+
+import { GridStack, GridStackElement } from '../gridstack';
+import { GridStackDD, DDOpts, DDKey, DDDropOpt, DDCallback, DDValue } from '../gridstack-dd';
+import { GridItemHTMLElement, DDDragInOpt } from '../types';
+
+/**
+ * HTML 5 Native DragDrop based drag'n'drop plugin.
+ */
+export class GridStackDDNative extends GridStackDD {
+ public constructor(grid: GridStack) {
+ super(grid);
+ }
+
+ public resizable(el: GridItemHTMLElement, opts: DDOpts, key?: DDKey, value?: DDValue): GridStackDDNative {
+ let dEl = this.getGridStackDDElement(el);
+ if (opts === 'disable' || opts === 'enable') {
+ dEl.ddResizable[opts]();
+ } else if (opts === 'destroy') {
+ if (dEl.ddResizable) {
+ dEl.cleanResizable();
+ }
+ } else if (opts === 'option') {
+ dEl.setupResizable({ [key]: value });
+ } else {
+ let handles = dEl.el.getAttribute('gs-resize-handles') ? dEl.el.getAttribute('gs-resize-handles') : this.grid.opts.resizable.handles;
+ dEl.setupResizable({
+ ...this.grid.opts.resizable,
+ ...{ handles: handles },
+ ...{
+ start: opts.start,
+ stop: opts.stop,
+ resize: opts.resize
+ }
+ });
+ }
+ return this;
+ }
+
+ public draggable(el: GridItemHTMLElement, opts: DDOpts, key?: DDKey, value?: DDValue): GridStackDDNative {
+ const dEl = this.getGridStackDDElement(el);
+ if (opts === 'disable' || opts === 'enable') {
+ dEl.ddDraggable && dEl.ddDraggable[opts]();
+ } else if (opts === 'destroy') {
+ if (dEl.ddDraggable) { // error to call destroy if not there
+ dEl.cleanDraggable();
+ }
+ } else if (opts === 'option') {
+ dEl.setupDraggable({ [key]: value });
+ } else {
+ dEl.setupDraggable({
+ ...this.grid.opts.draggable,
+ ...{
+ containment: (this.grid.opts._isNested && !this.grid.opts.dragOut)
+ ? this.grid.el.parentElement
+ : (this.grid.opts.draggable.containment || null),
+ start: opts.start,
+ stop: opts.stop,
+ drag: opts.drag
+ }
+ });
+ }
+ return this;
+ }
+
+ public dragIn(el: GridStackElement, opts: DDDragInOpt): GridStackDDNative {
+ let dEl = this.getGridStackDDElement(el);
+ dEl.setupDraggable(opts);
+ return this;
+ }
+
+ public droppable(el: GridItemHTMLElement, opts: DDOpts | DDDropOpt, key?: DDKey, value?: DDValue): GridStackDDNative {
+ let dEl = this.getGridStackDDElement(el);
+ if (typeof opts.accept === 'function' && !opts._accept) {
+ opts._accept = opts.accept;
+ opts.accept = (el) => opts._accept(el);
+ }
+ if (opts === 'disable' || opts === 'enable') {
+ dEl.ddDroppable && dEl.ddDroppable[opts]();
+ } else if (opts === 'destroy') {
+ if (dEl.ddDroppable) { // error to call destroy if not there
+ dEl.cleanDroppable();
+ }
+ } else if (opts === 'option') {
+ dEl.setupDroppable({ [key]: value });
+ } else {
+ dEl.setupDroppable(opts);
+ }
+ return this;
+ }
+
+ public isDroppable(el: GridItemHTMLElement): boolean {
+ const dEl = this.getGridStackDDElement(el);
+ return !!(dEl.ddDroppable);
+ }
+
+ public isDraggable(el: GridStackElement): boolean {
+ const dEl = this.getGridStackDDElement(el);
+ return !!(dEl.ddDraggable);
+ }
+
+ public on(el: GridItemHTMLElement, name: string, callback: DDCallback): GridStackDDNative {
+ let dEl = this.getGridStackDDElement(el);
+ dEl.on(name, (event: Event) => {
+ callback(
+ event,
+ DDManager.dragElement ? DDManager.dragElement.el : event.target as GridItemHTMLElement,
+ DDManager.dragElement ? DDManager.dragElement.helper : null)
+ });
+ return this;
+ }
+
+ public off(el: GridItemHTMLElement, name: string): GridStackDD {
+ let dEl = this.getGridStackDDElement(el);
+ dEl.off(name);
+ return this;
+ }
+ private getGridStackDDElement(el: GridStackElement): DDElement {
+ let dEl;
+ if (typeof el === 'string') {
+ dEl = document.querySelector(el as string);
+ } else {
+ dEl = el;
+ }
+ return dEl.ddElement ? dEl.ddElement: DDElement.init(dEl);
+ }
+}
+
+// finally register ourself
+GridStackDD.registerPlugin(GridStackDDNative);
diff --git a/src/gridstack.ts b/src/gridstack.ts
index d1f67de11..f884b7453 100644
--- a/src/gridstack.ts
+++ b/src/gridstack.ts
@@ -17,9 +17,6 @@ export * from './utils';
export * from './gridstack-engine';
export * from './gridstack-dd';
-// TEMPORARY import the jquery-ui drag&drop since we don't have alternative yet and don't expect users to create their own yet
-export * from './jq/gridstack-dd-jqueryui';
-
export type GridStackElement = string | HTMLElement | GridItemHTMLElement;
export interface GridHTMLElement extends HTMLElement {
diff --git a/src/index-h5.ts b/src/index-h5.ts
new file mode 100644
index 000000000..1000242c2
--- /dev/null
+++ b/src/index-h5.ts
@@ -0,0 +1,13 @@
+// index.html5.ts 2.2.0-dev - everything you need for a Grid that uses HTML5 native drag&drop (work in progress) @preserve
+
+// import './gridstack-poly.js';
+
+export * from './types';
+export * from './utils';
+export * from './gridstack-engine';
+export * from './gridstack-dd';
+export * from './gridstack';
+
+export * from './dragdrop/gridstack-dd-native';
+
+// declare module 'gridstack'; for umd ?
diff --git a/src/index-jq.ts b/src/index-jq.ts
new file mode 100644
index 000000000..83ff13e2a
--- /dev/null
+++ b/src/index-jq.ts
@@ -0,0 +1,13 @@
+// index.jq.ts 2.2.0-dev - everything you need for a Grid that uses Jquery-ui drag&drop (original, full feature) @preserve
+
+// import './gridstack-poly.js';
+
+export * from './types';
+export * from './utils';
+export * from './gridstack-engine';
+export * from './gridstack-dd';
+export * from './gridstack';
+
+export * from './jq/gridstack-dd-jqueryui';
+
+// declare module 'gridstack'; for umd ?
diff --git a/src/index-static.ts b/src/index-static.ts
new file mode 100644
index 000000000..322d9e1ab
--- /dev/null
+++ b/src/index-static.ts
@@ -0,0 +1,11 @@
+// index.static.ts 2.2.0-dev - everything you need for a static Grid (non draggable) @preserve
+
+// import './gridstack-poly.js';
+
+export * from './types';
+export * from './utils';
+export * from './gridstack-engine';
+export * from './gridstack-dd';
+export * from './gridstack';
+
+// declare module 'gridstack'; for umd ?
diff --git a/src/utils.ts b/src/utils.ts
index a34d2d42c..4eecb6010 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -187,7 +187,7 @@ export class Utils {
return Utils.closestByClass(el, name);
}
- /** @internal */
+ /** delay calling the given function by certain amount of time */
static throttle(callback: () => void, delay: number): () => void {
let isWaiting = false;
diff --git a/webpack.config.js b/webpack.config.js
index e59de811f..742882531 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -2,10 +2,9 @@ const path = require('path');
module.exports = {
entry: {
- 'gridstack.all': './src/gridstack.ts',
- //'gridstack.all': './src/index-jq.ts',
- //'gridstack.html5': './src/index-html5.ts',
- //'gridstack.static': './src/index-static.ts'
+ 'gridstack.all': './src/index-jq.ts',
+ 'gridstack.h5': './src/index-h5.ts',
+ 'gridstack.static': './src/index-static.ts'
},
mode: 'production', // production vs development
devtool: 'source-map',