diff --git a/demo/demo.css b/demo/demo.css index 27f7aa2de..659cf803c 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -59,6 +59,16 @@ h1 { cursor: move; min-height: 25px; background-color: #16af91; + &.outside { + display: none; + width: fit-content; + position: absolute; + cursor: move; + z-index: 99999; + &:hover { + display: block; + } + } } .card-header:hover { background-color: #149b80; diff --git a/demo/index.html b/demo/index.html index ed3b6b780..abbc9d802 100644 --- a/demo/index.html +++ b/demo/index.html @@ -32,6 +32,7 @@

Demos

  • Size To Content
  • Static
  • Title drag
  • +
  • Title drag from outside grid stack item
  • Transform (scale+offset)
  • Two grids
  • Two grids Vertical
  • diff --git a/demo/title_drag_from_outside_gs_item.html b/demo/title_drag_from_outside_gs_item.html new file mode 100644 index 000000000..e13a755cc --- /dev/null +++ b/demo/title_drag_from_outside_gs_item.html @@ -0,0 +1,61 @@ + + + + + + + + Title area drag from outside GridStack item + + + + + + +
    +

    Title area drag from outside GridStack item

    +

    +
    - Drag here -
    +
    +
    +
    +
    Hover on the panel to toggle drag title (which is outside grid-stack-item) visibility. The + rest of the panel content doesn't drag.
    +
    +
    +
    +
    +
    - Drag here -
    +
    This panel has title inside grid item. Only title drags, rest of the panel content doesn't drag.
    +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/doc/README.md b/doc/README.md index e4c31a9ef..a4b22596e 100644 --- a/doc/README.md +++ b/doc/README.md @@ -148,6 +148,7 @@ v10.x supports a much richer responsive behavior, you can have breakpoints of wi - `scroll`?: boolean - default to 'true', enable or disable the scroll when an element is dragged on bottom or top of the grid. - `cancel`?: string - prevents dragging from starting on specified elements, listed as comma separated selectors (eg: '.no-drag'). default built in is 'input,textarea,button,select,option' - `helper`?: 'clone' | ((el: HTMLElement) => HTMLElement) - helper function when dragging side panel items that need to be cloned before dropping (ex: 'clone' or your own method) +- `dragElements`?: HTMLElement[] - references to the elements (outside the grid item) to be used for dragging grid item ## Grid attributes diff --git a/spec/gridstack-spec.ts b/spec/gridstack-spec.ts index bfbe3668c..5330b6bab 100644 --- a/spec/gridstack-spec.ts +++ b/spec/gridstack-spec.ts @@ -47,6 +47,20 @@ describe('gridstack >', function() { ''; // generic widget with no param let widgetHTML = '
    hello
    '; + // grid with one grid item which has item title outside grid + let gridstackExternalItemTitle = ` +
    +
    - Drag Here -
    +
    +
    +
    +
    Hover on the panel to toggle drag title (which is outside grid-stack-item) visibility. The + rest of the panel content doesn't drag.
    +
    +
    +
    +
    + `; describe('grid.init() / initAll() >', function() { beforeEach(function() { @@ -1986,4 +2000,63 @@ describe('gridstack >', function() { }); }); + describe("grid.moveExternal >", function () { + beforeEach(function () { + document.body.insertAdjacentHTML("afterbegin", gridstackExternalItemTitle); + }); + afterEach(function () { + document.body.removeChild(document.getElementById("gs-cont")); + }); + it("dragstart event not triggered on external element drag >", function () { + let externalTitle = document.querySelector(".title-header")!; + grid = GridStack.init(); + let test = 0; + grid.on("dragstart", () => { + test++; + }); + let mouseDownEvt = new MouseEvent("mousedown", { + bubbles: true, + cancelable: true, + clientX: externalTitle.getBoundingClientRect().x, + clientY: externalTitle.getBoundingClientRect().y, + }); + externalTitle?.dispatchEvent(mouseDownEvt); + let mousemove_event = new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + clientX: externalTitle.getBoundingClientRect().x + 5, + clientY: externalTitle.getBoundingClientRect().y + 5, + }); + document.dispatchEvent(mousemove_event); + expect(test).toBe(0); + }); + it("dragstart event triggered on external element drag >", function () { + let externalTitle = document.querySelector(".title-header")!; + grid = GridStack.init({ + draggable: { + dragElements: [externalTitle], + }, + }); + let test = 0; + grid.on("dragstart", () => { + test++; + }); + let mouseDownEvt = new MouseEvent("mousedown", { + bubbles: true, + cancelable: true, + clientX: externalTitle.getBoundingClientRect().x, + clientY: externalTitle.getBoundingClientRect().y, + }); + externalTitle?.dispatchEvent(mouseDownEvt); + let mousemove_event = new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + clientX: externalTitle.getBoundingClientRect().x + 5, + clientY: externalTitle.getBoundingClientRect().y + 5, + }); + document.dispatchEvent(mousemove_event); + expect(test).not.toBe(0); + }); + }); + }); diff --git a/src/dd-draggable.ts b/src/dd-draggable.ts index 8a0bdb91b..0ef14ecc1 100644 --- a/src/dd-draggable.ts +++ b/src/dd-draggable.ts @@ -69,6 +69,9 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt const handleName = option?.handle?.substring(1); const n = el.gridstackNode; this.dragEls = !handleName || el.classList.contains(handleName) ? [el] : (n?.subGrid ? [el.querySelector(option.handle) || el] : Array.from(el.querySelectorAll(option.handle))); + if(option?.dragElements?.length) { + this.dragEls = option.dragElements + } if (this.dragEls.length === 0) { this.dragEls = [el]; } diff --git a/src/types.ts b/src/types.ts index 4cc7e88b5..a5754fad7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -383,6 +383,8 @@ export interface DDDragOpt { cancel?: string; /** helper function when dropping: 'clone' or your own method */ helper?: 'clone' | ((el: HTMLElement) => HTMLElement); + /** references to the elements (outside the grid item) to be used for dragging grid item */ + dragElements?: HTMLElement[]; /** callbacks */ start?: (event: Event, ui: DDUIData) => void; stop?: (event: Event) => void; diff --git a/src/utils.ts b/src/utils.ts index 6aa104a71..ab46f54cf 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -434,7 +434,7 @@ export class Utils { */ static cloneDeep(obj: T): T { // list of fields we will skip during cloneDeep (nested objects, other internal) - const skipFields = ['parentGrid', 'el', 'grid', 'subGrid', 'engine']; + const skipFields = ['parentGrid', 'el', 'grid', 'subGrid', 'engine', 'dragElements']; // return JSON.parse(JSON.stringify(obj)); // doesn't work with date format ? const ret = Utils.clone(obj); for (const key in ret) {