99
1010import { GridStackDDI } from './gridstack-ddi' ;
1111import { GridItemHTMLElement , GridStackNode , GridStackElement , DDUIData , DDDragInOpt , GridStackPosition } from './types' ;
12- import { GridStack } from './gridstack' ;
12+ import { GridStack , MousePosition } from './gridstack' ;
1313import { Utils } from './utils' ;
1414
1515/** Drag&Drop drop options */
@@ -88,35 +88,40 @@ GridStack.prototype._setupAcceptWidget = function(): GridStack {
8888 return this ;
8989 }
9090
91- let onDrag = ( event , el : GridItemHTMLElement ) => {
91+ // vars shared across all methods
92+ let gridPos : MousePosition ;
93+ let cellHeight : number , cellWidth : number ;
94+
95+ let onDrag = ( event : DragEvent , el : GridItemHTMLElement , helper : GridItemHTMLElement ) => {
96+
9297 let node = el . gridstackNode ;
93- let pos = this . getCellFromPixel ( { left : event . pageX , top : event . pageY } , true ) ;
94- let x = Math . max ( 0 , pos . x ) ;
95- let y = Math . max ( 0 , pos . y ) ;
98+ helper = helper || el ;
99+ // let left = event.pageX - gridPos.left;
100+ // let top = event.pageY - gridPos.top;
101+ let rec = helper . getBoundingClientRect ( ) ;
102+ let left = rec . left - gridPos . left ;
103+ let top = rec . top - gridPos . top ;
104+ let ui : DDUIData = { position : { top, left} } ;
105+
96106 if ( ! node . _added ) {
97- node . x = x ;
98- node . y = y ;
107+ node . x = Math . max ( 0 , Math . round ( left / cellWidth ) ) ;
108+ node . y = Math . max ( 0 , Math . round ( top / cellHeight ) ) ;
99109 delete node . autoPosition ;
100110
101111 // don't accept *initial* location if doesn't fit #1419 (locked drop region, or can't grow), but maybe try if it will go somewhere
102112 if ( ! this . engine . willItFit ( node ) ) {
103113 node . autoPosition = true ; // ignore x,y and try for any slot...
104- if ( ! this . engine . willItFit ( node ) ) return ; // full grid or can't grow
114+ if ( ! this . engine . willItFit ( node ) ) {
115+ GridStackDD . get ( ) . off ( el , 'drag' ) ; // stop calling us
116+ return ; // full grid or can't grow
117+ }
105118 }
106- node . _added = true ;
107-
108- node . el = el ;
109- this . engine . cleanNodes ( )
110- . beginUpdate ( node )
111- . addNode ( node ) ;
112119
113- this . _writePosAttr ( this . placeholder , node . x , node . y , node . w , node . h ) ;
114- this . el . appendChild ( this . placeholder ) ;
115- node . el = this . placeholder ; // dom we update while dragging...
116-
117- this . _updateContainerHeight ( ) ;
118- } else if ( this . engine . moveNodeCheck ( node , { x, y} ) ) {
119- this . _updateContainerHeight ( ) ;
120+ // re-use the existing node dragging method
121+ this . _onStartMoving ( event , ui , node , cellWidth , cellHeight ) ;
122+ } else {
123+ // re-use the existing node dragging that does so much of the collision detection
124+ this . _dragOrResize ( event , ui , node , cellWidth , cellHeight ) ;
120125 }
121126 } ;
122127
@@ -142,14 +147,21 @@ GridStack.prototype._setupAcceptWidget = function(): GridStack {
142147 return canAccept ;
143148 }
144149 } )
145- . on ( this . el , 'dropover' , ( event , el : GridItemHTMLElement ) => {
150+ . on ( this . el , 'dropover' , ( event : Event , el : GridItemHTMLElement , helper : GridItemHTMLElement ) => {
151+
146152 // ignore drop enter on ourself, and prevent parent from receiving event
147153 let node = el . gridstackNode ;
148154 if ( node && node . grid === this ) {
149155 delete node . _added ; // reset this to track placeholder again in case we were over other grid #1484 (dropout doesn't always clear)
150156 return false ;
151157 }
152158
159+ // get grid screen coordinates and cell dimensions
160+ let box = this . el . getBoundingClientRect ( ) ;
161+ gridPos = { top : box . top + document . documentElement . scrollTop , left : box . left } ;
162+ cellWidth = this . cellWidth ( ) ;
163+ cellHeight = this . getCellHeight ( true ) ;
164+
153165 // load any element attributes if we don't have a node
154166 if ( ! node ) {
155167 node = this . _readAttr ( el ) ;
@@ -161,14 +173,16 @@ GridStack.prototype._setupAcceptWidget = function(): GridStack {
161173 }
162174
163175 // if not calculate the grid size based on element outer size
164- let w = node . w || Math . round ( el . offsetWidth / this . cellWidth ( ) ) || 1 ;
165- let h = node . h || Math . round ( el . offsetHeight / this . getCellHeight ( true ) ) || 1 ;
176+ helper = helper || el ;
177+ let w = node . w || Math . round ( helper . offsetWidth / cellWidth ) || 1 ;
178+ let h = node . h || Math . round ( helper . offsetHeight / cellHeight ) || 1 ;
166179
167- // copy the node original values (min/max/id/etc...) but override width/height/other flags which are this grid specific
180+ // COPY the node original values (min/max/id/etc...) but override width/height/other flags which are this grid specific
168181 let newNode = this . engine . prepareNode ( { ...node , ...{ w, h, _added : false , _temporary : true , _isOutOfGrid : true } } ) ;
169182 el . gridstackNode = newNode ;
170183 el . _gridstackNodeOrig = node ;
171184
185+ onDrag ( event as DragEvent , el , helper ) ; // make sure this is called at least once when going fast #1578
172186 GridStackDD . get ( ) . on ( el , 'drag' , onDrag ) ;
173187 return false ; // prevent parent from receiving msg (which may be grid as well)
174188 } )
@@ -358,114 +372,19 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid
358372
359373 /** called when item starts moving/resizing */
360374 let onStartMoving = ( event : Event , ui : DDUIData ) : void => {
361- let target = event . target as HTMLElement ;
362-
363375 // trigger any 'dragstart' / 'resizestart' manually
364376 if ( this . _gsEventHandler [ event . type ] ) {
365- this . _gsEventHandler [ event . type ] ( event , target ) ;
377+ this . _gsEventHandler [ event . type ] ( event , event . target ) ;
366378 }
367-
368- this . engine . cleanNodes ( )
369- . beginUpdate ( node ) ;
370-
371- this . _writePosAttr ( this . placeholder , node . x , node . y , node . w , node . h )
372- this . el . append ( this . placeholder ) ;
373-
374- node . el = this . placeholder ;
375- node . _lastUiPosition = ui . position ;
376- node . _prevYPix = ui . position . top ;
377- node . _moving = ( event . type === 'dragstart' ) ;
378- delete node . _lastTried ;
379-
380- // set the min/max resize info
381379 cellWidth = this . cellWidth ( ) ;
382380 cellHeight = this . getCellHeight ( true ) ; // force pixels for calculations
383- this . engine . cacheRects ( cellWidth , cellHeight , this . opts . marginTop , this . opts . marginRight , this . opts . marginBottom , this . opts . marginLeft ) ;
384- let dd = GridStackDD . get ( )
385- . resizable ( el , 'option' , 'minWidth' , cellWidth * ( node . minW || 1 ) )
386- . resizable ( el , 'option' , 'minHeight' , cellHeight * ( node . minH || 1 ) ) ;
387- if ( node . maxW ) { dd . resizable ( el , 'option' , 'maxWidth' , cellWidth * node . maxW ) ; }
388- if ( node . maxH ) { dd . resizable ( el , 'option' , 'maxHeight' , cellHeight * node . maxH ) ; }
381+
382+ this . _onStartMoving ( event , ui , node , cellWidth , cellHeight ) ;
389383 }
390384
391385 /** called when item is being dragged/resized */
392386 let dragOrResize = ( event : Event , ui : DDUIData ) : void => {
393- // calculate the place where we're landing by offsetting margin so actual edge crosses mid point
394- let left = ui . position . left + ( ui . position . left > node . _lastUiPosition . left ? - this . opts . marginRight : this . opts . marginLeft ) ;
395- let top = ui . position . top + ( ui . position . top > node . _lastUiPosition . top ? - this . opts . marginBottom : this . opts . marginTop ) ;
396- let x = Math . round ( left / cellWidth ) ;
397- let y = Math . round ( top / cellHeight ) ;
398- let w = node . w ;
399- let h = node . h ;
400- let resizing : boolean ;
401-
402- if ( event . type === 'drag' ) {
403- let distance = ui . position . top - node . _prevYPix ;
404- node . _prevYPix = ui . position . top ;
405- Utils . updateScrollPosition ( el , ui . position , distance ) ;
406- // if inTrash, outside of the bounds or added to another grid (#393) temporarily remove it from us
407- if ( el . dataset . inTrashZone || node . _added || this . engine . isOutside ( x , y , node ) ) {
408- if ( node . _temporaryRemoved ) return ;
409- if ( this . opts . removable === true ) {
410- this . _setupRemovingTimeout ( el ) ;
411- }
412-
413- x = node . _beforeDrag . x ;
414- y = node . _beforeDrag . y ;
415-
416- if ( this . placeholder . parentNode === this . el ) {
417- this . placeholder . remove ( ) ;
418- }
419- this . engine . removeNode ( node ) ;
420- this . _updateContainerHeight ( ) ;
421-
422- node . _temporaryRemoved = true ;
423- delete node . _added ; // no need for this now
424- } else {
425- if ( node . _removeTimeout ) this . _clearRemovingTimeout ( el ) ;
426-
427- if ( node . _temporaryRemoved ) {
428- this . engine . addNode ( node ) ;
429- this . _writePosAttr ( this . placeholder , x , y , w , h ) ;
430- this . el . appendChild ( this . placeholder ) ;
431- node . el = this . placeholder ;
432- delete node . _temporaryRemoved ;
433- }
434- }
435- if ( node . x === x && node . y === y ) return ; // skip same
436- // DON'T skip one we tried as we might have failed because of coverage <50% before
437- // if (node._lastTried && node._lastTried.x === x && node._lastTried.y === y) return;
438- } else if ( event . type === 'resize' ) {
439- if ( x < 0 ) return ;
440- // Scrolling page if needed
441- Utils . updateScrollResize ( event as MouseEvent , el , cellHeight ) ;
442- w = Math . round ( ui . size . width / cellWidth ) ;
443- h = Math . round ( ui . size . height / cellHeight ) ;
444- if ( node . w === w && node . h === h ) return ;
445- if ( node . _lastTried && node . _lastTried . w === w && node . _lastTried . h === h ) return ; // skip one we tried (but failed)
446- resizing = true ;
447- }
448-
449- node . _lastTried = { x, y, w, h} ; // set as last tried (will nuke if we go there)
450- let rect : GridStackPosition = { // screen pix of the dragged box
451- x : ui . position . left + this . opts . marginLeft ,
452- y : ui . position . top + this . opts . marginTop ,
453- w : ( ui . size ? ui . size . width : node . w * cellWidth ) - this . opts . marginLeft - this . opts . marginRight ,
454- h : ( ui . size ? ui . size . height : node . h * cellHeight ) - this . opts . marginTop - this . opts . marginBottom
455- } ;
456- if ( this . engine . moveNodeCheck ( node , { x, y, w, h, cellWidth, cellHeight, rect} ) ) {
457- node . _lastUiPosition = ui . position ;
458- this . engine . cacheRects ( cellWidth , cellHeight , this . opts . marginTop , this . opts . marginRight , this . opts . marginBottom , this . opts . marginLeft ) ;
459- delete node . _skipDown ;
460- if ( resizing && node . subGrid ) { ( node . subGrid as GridStack ) . onParentResize ( ) ; }
461- this . _updateContainerHeight ( ) ;
462-
463- let target = event . target as GridItemHTMLElement ;
464- this . _writePosAttr ( target , node . x , node . y , node . w , node . h ) ;
465- if ( this . _gsEventHandler [ event . type ] ) {
466- this . _gsEventHandler [ event . type ] ( event , target ) ;
467- }
468- }
387+ this . _dragOrResize ( event , ui , node , cellWidth , cellHeight ) ;
469388 }
470389
471390 /** called when the item stops moving/resizing */
@@ -548,6 +467,125 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid
548467 return this ;
549468}
550469
470+ /** @internal called when item is starting a drag/resize */
471+ GridStack . prototype . _onStartMoving = function ( event : Event , ui : DDUIData , node : GridStackNode , cellWidth : number , cellHeight : number ) : void {
472+ this . engine . cleanNodes ( )
473+ . beginUpdate ( node ) ;
474+
475+ this . _writePosAttr ( this . placeholder , node . x , node . y , node . w , node . h )
476+ this . el . append ( this . placeholder ) ;
477+
478+ node . el = this . placeholder ;
479+ node . _lastUiPosition = ui . position ;
480+ node . _prevYPix = ui . position . top ;
481+ node . _moving = ( event . type === 'dragstart' ) ;
482+ delete node . _lastTried ;
483+
484+ if ( event . type === 'dropover' && ! node . _added ) {
485+ node . _added = true ;
486+ this . engine . addNode ( node ) ;
487+ this . _writePosAttr ( this . placeholder , node . x , node . y , node . w , node . h ) ;
488+ node . _moving = true ; // lastly mark as moving object
489+ }
490+
491+ // set the min/max resize info
492+ this . engine . cacheRects ( cellWidth , cellHeight , this . opts . marginTop , this . opts . marginRight , this . opts . marginBottom , this . opts . marginLeft ) ;
493+ if ( event . type === 'resizestart' ) {
494+ let el = node . el ;
495+ let dd = GridStackDD . get ( )
496+ . resizable ( el , 'option' , 'minWidth' , cellWidth * ( node . minW || 1 ) )
497+ . resizable ( el , 'option' , 'minHeight' , cellHeight * ( node . minH || 1 ) ) ;
498+ if ( node . maxW ) { dd . resizable ( el , 'option' , 'maxWidth' , cellWidth * node . maxW ) ; }
499+ if ( node . maxH ) { dd . resizable ( el , 'option' , 'maxHeight' , cellHeight * node . maxH ) ; }
500+ }
501+ }
502+
503+ /** @internal called when item is being dragged/resized */
504+ GridStack . prototype . _dragOrResize = function ( event : Event , ui : DDUIData , node : GridStackNode , cellWidth : number , cellHeight : number ) : void {
505+ let el = node . el ;
506+ // calculate the place where we're landing by offsetting margin so actual edge crosses mid point
507+ let left = ui . position . left + ( ui . position . left > node . _lastUiPosition . left ? - this . opts . marginRight : this . opts . marginLeft ) ;
508+ let top = ui . position . top + ( ui . position . top > node . _lastUiPosition . top ? - this . opts . marginBottom : this . opts . marginTop ) ;
509+ let x = Math . round ( left / cellWidth ) ;
510+ let y = Math . round ( top / cellHeight ) ;
511+ if ( node . _isOutOfGrid ) {
512+ // items coming from outside are handled by 'dragout' event instead, so make coordinates fit
513+ x = Math . max ( 0 , x ) ;
514+ y = Math . max ( 0 , y ) ;
515+ }
516+ let w = node . w ;
517+ let h = node . h ;
518+ let resizing : boolean ;
519+
520+ if ( event . type === 'drag' ) {
521+ let distance = ui . position . top - node . _prevYPix ;
522+ node . _prevYPix = ui . position . top ;
523+ Utils . updateScrollPosition ( el , ui . position , distance ) ;
524+ // if inTrash, outside of the bounds or added to another grid (#393) temporarily remove it from us
525+ if ( el . dataset . inTrashZone || ( node . _added && ! node . _isOutOfGrid ) || this . engine . isOutside ( x , y , node ) ) {
526+ if ( node . _temporaryRemoved ) return ;
527+ if ( this . opts . removable === true ) {
528+ this . _setupRemovingTimeout ( el ) ;
529+ }
530+
531+ x = node . _beforeDrag . x ;
532+ y = node . _beforeDrag . y ;
533+
534+ if ( this . placeholder . parentNode === this . el ) {
535+ this . placeholder . remove ( ) ;
536+ }
537+ this . engine . removeNode ( node ) ;
538+ this . _updateContainerHeight ( ) ;
539+
540+ node . _temporaryRemoved = true ;
541+ delete node . _added ; // no need for this now
542+ } else {
543+ if ( node . _removeTimeout ) this . _clearRemovingTimeout ( el ) ;
544+
545+ if ( node . _temporaryRemoved ) {
546+ this . engine . addNode ( node ) ;
547+ this . _writePosAttr ( this . placeholder , x , y , w , h ) ;
548+ this . el . appendChild ( this . placeholder ) ;
549+ node . el = this . placeholder ;
550+ delete node . _temporaryRemoved ;
551+ }
552+ }
553+ if ( node . x === x && node . y === y ) return ; // skip same
554+ // DON'T skip one we tried as we might have failed because of coverage <50% before
555+ // if (node._lastTried && node._lastTried.x === x && node._lastTried.y === y) return;
556+ } else if ( event . type === 'resize' ) {
557+ if ( x < 0 ) return ;
558+ // Scrolling page if needed
559+ Utils . updateScrollResize ( event as MouseEvent , el , cellHeight ) ;
560+ w = Math . round ( ui . size . width / cellWidth ) ;
561+ h = Math . round ( ui . size . height / cellHeight ) ;
562+ if ( node . w === w && node . h === h ) return ;
563+ if ( node . _lastTried && node . _lastTried . w === w && node . _lastTried . h === h ) return ; // skip one we tried (but failed)
564+ resizing = true ;
565+ }
566+
567+ node . _lastTried = { x, y, w, h} ; // set as last tried (will nuke if we go there)
568+ let rect : GridStackPosition = { // screen pix of the dragged box
569+ x : ui . position . left + this . opts . marginLeft ,
570+ y : ui . position . top + this . opts . marginTop ,
571+ w : ( ui . size ? ui . size . width : node . w * cellWidth ) - this . opts . marginLeft - this . opts . marginRight ,
572+ h : ( ui . size ? ui . size . height : node . h * cellHeight ) - this . opts . marginTop - this . opts . marginBottom
573+ } ;
574+ if ( this . engine . moveNodeCheck ( node , { x, y, w, h, cellWidth, cellHeight, rect} ) ) {
575+ node . _lastUiPosition = ui . position ;
576+ this . engine . cacheRects ( cellWidth , cellHeight , this . opts . marginTop , this . opts . marginRight , this . opts . marginBottom , this . opts . marginLeft ) ;
577+ delete node . _skipDown ;
578+ if ( resizing && node . subGrid ) { ( node . subGrid as GridStack ) . onParentResize ( ) ; }
579+ this . _updateContainerHeight ( ) ;
580+
581+ let target = event . target as GridItemHTMLElement ;
582+ this . _writePosAttr ( target , node . x , node . y , node . w , node . h ) ;
583+ if ( this . _gsEventHandler [ event . type ] ) {
584+ this . _gsEventHandler [ event . type ] ( event , target ) ;
585+ }
586+ }
587+ }
588+
551589/**
552590 * Enables/Disables moving.
553591 * @param els widget or selector to modify.
0 commit comments