diff --git a/demo/advance-h5.html b/demo/advance-h5.html new file mode 100644 index 000000000..f21d5905a --- /dev/null +++ b/demo/advance-h5.html @@ -0,0 +1,96 @@ + + + + + + + + Advanced grid demo + + + + + + + + + + + + + +

Advanced Demo

+
+
+
+
+ +
+
+ Drop here to remove! +
+
+
+
+
+ +
+
+ Drag me in the dashboard! +
+
+
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/dragdrop/gridstack-dd-native.ts b/src/dragdrop/gridstack-dd-native.ts index 4ccc97a36..26af54821 100644 --- a/src/dragdrop/gridstack-dd-native.ts +++ b/src/dragdrop/gridstack-dd-native.ts @@ -6,11 +6,12 @@ * gridstack.js may be freely distributed under the MIT license. */ import { DDManager } from './dd-manager'; -import { DDElement } from './dd-element'; +import { DDElement, DDElementHost } from './dd-element'; import { GridStackElement } from '../gridstack'; import { GridStackDD, DDOpts, DDKey, DDDropOpt, DDCallback, DDValue } from '../gridstack-dd'; import { GridItemHTMLElement, DDDragInOpt } from '../types'; +import { Utils } from '../utils'; /** * HTML 5 Native DragDrop based drag'n'drop plugin. @@ -18,119 +19,117 @@ import { GridItemHTMLElement, DDDragInOpt } from '../types'; export class GridStackDDNative extends GridStackDD { 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 { - const grid = el.gridstackNode.grid; - let handles = dEl.el.getAttribute('gs-resize-handles') ? dEl.el.getAttribute('gs-resize-handles') : grid.opts.resizable.handles; - dEl.setupResizable({ - ...grid.opts.resizable, - ...{ handles: handles }, - ...{ - start: opts.start, - stop: opts.stop, - resize: opts.resize + this.getDDElements(el).forEach(dEl => { + 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 { + const grid = dEl.el.gridstackNode.grid; + let handles = dEl.el.getAttribute('gs-resize-handles') ? dEl.el.getAttribute('gs-resize-handles') : grid.opts.resizable.handles; + dEl.setupResizable({ + ...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 { - const grid = el.gridstackNode.grid; - dEl.setupDraggable({ - ...grid.opts.draggable, - ...{ - containment: (grid.opts._isNested && !grid.opts.dragOut) - ? grid.el.parentElement - : (grid.opts.draggable.containment || null), - start: opts.start, - stop: opts.stop, - drag: opts.drag + this.getDDElements(el).forEach(dEl => { + 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 { + const grid = dEl.el.gridstackNode.grid; + dEl.setupDraggable({ + ...grid.opts.draggable, + ...{ + containment: (grid.opts._isNested && !grid.opts.dragOut) + ? grid.el.parentElement + : (grid.opts.draggable.containment || null), + start: opts.start, + stop: opts.stop, + drag: opts.drag + } + }); + } + }); return this; } public dragIn(el: GridStackElement, opts: DDDragInOpt): GridStackDDNative { - let dEl = this.getGridStackDDElement(el); - dEl.setupDraggable(opts); + this.getDDElements(el).forEach(dEl => 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(); + this.getDDElements(el).forEach(dEl => { + if (opts === 'disable' || opts === 'enable') { + dEl.ddDroppable && dEl.ddDroppable[opts](); + } else if (opts === 'destroy') { + if (dEl.ddDroppable) { // error to call destroy if not there + dEl.cleanDroppable(); + } + } else if (opts === 'option') { + dEl.setupDroppable({ [key]: value }); + } else { + dEl.setupDroppable(opts); } - } else if (opts === 'option') { - dEl.setupDroppable({ [key]: value }); - } else { - dEl.setupDroppable(opts); - } + }); return this; } + /** true if at least one of them is droppable */ public isDroppable(el: GridItemHTMLElement): boolean { - const dEl = this.getGridStackDDElement(el); - return !!(dEl.ddDroppable); + return this.getDDElements(el).some(dEl => !!(dEl.ddDroppable)); } + /** true if at least one of them is draggable */ public isDraggable(el: GridStackElement): boolean { - const dEl = this.getGridStackDDElement(el); - return !!(dEl.ddDraggable); + return this.getDDElements(el).some(dEl => !!(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) - }); + this.getDDElements(el).forEach(dEl => + 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); + this.getDDElements(el).forEach(dEl => 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); + private getDDElements(els: GridStackElement): DDElement[] { + let list = Utils.getElements(els) as DDElementHost[]; + if (!list.length) { return []; } + return list.map(e => e.ddElement || DDElement.init(e)); } } diff --git a/src/gridstack.ts b/src/gridstack.ts index e5d70b281..7120662f2 100644 --- a/src/gridstack.ts +++ b/src/gridstack.ts @@ -8,7 +8,7 @@ import { GridStackEngine } from './gridstack-engine'; import { obsoleteOpts, obsoleteOptsDel, obsoleteAttr, obsolete, Utils, HeightData } from './utils'; -import { GridItemHTMLElement, GridStackWidget, GridStackNode, GridStackOptions, numberOrString, ColumnOptions, DDUIData } from './types'; +import { GridStackElement, GridItemHTMLElement, GridStackWidget, GridStackNode, GridStackOptions, numberOrString, ColumnOptions, DDUIData } from './types'; import { GridStackDD } from './gridstack-dd'; // export all dependent file as well to make it easier for users to just import the main file @@ -17,8 +17,6 @@ export * from './utils'; export * from './gridstack-engine'; export * from './gridstack-dd'; -export type GridStackElement = string | HTMLElement | GridItemHTMLElement; - export interface GridHTMLElement extends HTMLElement { gridstack?: GridStack; // grid's parent DOM element points back to grid class } @@ -1766,48 +1764,19 @@ export class GridStack { /** @internal convert a potential selector into actual element */ private static getElement(els: GridStackElement = '.grid-stack-item'): GridItemHTMLElement { - if (typeof els === 'string') { - if (!els.length) { return null} - if (els[0] === '#') { - return document.getElementById(els.substring(1)); - } - if (els[0] === '.') { - return document.querySelector(els); - } - - // if we start with a digit, assume it's an id (error calling querySelector('#1')) as class are not valid CSS - if(!isNaN(+els[0])) { // start with digit - return document.getElementById(els); - } - - // finally try string, then id then class - let el = document.querySelector(els); - if (!el) { el = document.getElementById(els) } - if (!el) { el = document.querySelector('.' + els) } - return el as GridItemHTMLElement; - } - return els; + return Utils.getElement(els); } - - /** @internal convert a potential selector into actual list of elements */ + /** @internal */ private static getElements(els: GridStackElement = '.grid-stack-item'): GridItemHTMLElement[] { - if (typeof els === 'string') { - let list = document.querySelectorAll(els); - if (!list.length && els[0] !== '.' && els[0] !== '#') { - list = document.querySelectorAll('.' + els); - if (!list.length) { list = document.querySelectorAll('#' + els) } - } - return Array.from(list) as GridItemHTMLElement[]; - } - return [els]; + return Utils.getElements(els); } /** @internal */ - private static getGridElement(els: string | HTMLElement = '.grid-stack'): GridHTMLElement { - return GridStack.getElement(els) as GridHTMLElement; + private static getGridElement(els: GridStackElement): GridHTMLElement { + return GridStack.getElement(els); } /** @internal */ - private static getGridElements(els: string | HTMLElement = '.grid-stack'): GridHTMLElement[] { - return GridStack.getElements(els) as GridHTMLElement[]; + private static getGridElements(els: string): GridHTMLElement[] { + return Utils.getElements(els); } /** @internal initialize margin top/bottom/left/right and units */ diff --git a/src/types.ts b/src/types.ts index 6710e766d..53c329e5c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -24,6 +24,8 @@ export interface GridItemHTMLElement extends HTMLElement { _gridstackNodeOrig?: GridStackNode; } +export type GridStackElement = string | HTMLElement | GridItemHTMLElement; + /** * Defines the options for a Grid */ diff --git a/src/utils.ts b/src/utils.ts index 4eecb6010..a96044d29 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,7 +6,7 @@ * gridstack.js may be freely distributed under the MIT license. */ -import { GridStackWidget, GridStackNode, GridStackOptions, numberOrString } from './types'; +import { GridStackElement, GridStackWidget, GridStackNode, GridStackOptions, numberOrString } from './types'; export interface HeightData { height: number; @@ -56,6 +56,44 @@ export function obsoleteAttr(el: HTMLElement, oldName: string, newName: string, */ export class Utils { + /** convert a potential selector into actual list of html elements */ + static getElements(els: GridStackElement): HTMLElement[] { + if (typeof els === 'string') { + let list = document.querySelectorAll(els); + if (!list.length && els[0] !== '.' && els[0] !== '#') { + list = document.querySelectorAll('.' + els); + if (!list.length) { list = document.querySelectorAll('#' + els) } + } + return Array.from(list) as HTMLElement[]; + } + return [els]; + } + + /** convert a potential selector into actual single element */ + static getElement(els: GridStackElement): HTMLElement { + if (typeof els === 'string') { + if (!els.length) { return null} + if (els[0] === '#') { + return document.getElementById(els.substring(1)); + } + if (els[0] === '.') { + return document.querySelector(els); + } + + // if we start with a digit, assume it's an id (error calling querySelector('#1')) as class are not valid CSS + if(!isNaN(+els[0])) { // start with digit + return document.getElementById(els); + } + + // finally try string, then id then class + let el = document.querySelector(els); + if (!el) { el = document.getElementById(els) } + if (!el) { el = document.querySelector('.' + els) } + return el as HTMLElement; + } + return els; + } + /** returns true if a and b overlap */ static isIntercepted(a: GridStackWidget, b: GridStackWidget): boolean { return !(a.x + a.width <= b.x || b.x + b.width <= a.x || a.y + a.height <= b.y || b.y + b.height <= a.y);