@@ -28,6 +28,7 @@ const activeCapturingEventOptions = normalizePassiveListenerOptions({
2828@Injectable ( { providedIn : 'root' } )
2929export class DragDropRegistry < I , C > implements OnDestroy {
3030 private _document : Document ;
31+ private _window : Window | null ;
3132
3233 /** Registered drop container instances. */
3334 private _dropInstances = new Set < C > ( ) ;
@@ -41,28 +42,36 @@ export class DragDropRegistry<I, C> implements OnDestroy {
4142 /** Keeps track of the event listeners that we've bound to the `document`. */
4243 private _globalListeners = new Map < string , {
4344 handler : ( event : Event ) => void ,
45+ // The target needs to be `| null` because we bind either to `window` or `document` which
46+ // aren't available during SSR. There's an injection token for the document, but not one for
47+ // window so we fall back to not binding events to it.
48+ target : EventTarget | null ,
4449 options ?: AddEventListenerOptions | boolean
4550 } > ( ) ;
4651
4752 /**
4853 * Emits the `touchmove` or `mousemove` events that are dispatched
4954 * while the user is dragging a drag item instance.
5055 */
51- readonly pointerMove : Subject < TouchEvent | MouseEvent > = new Subject < TouchEvent | MouseEvent > ( ) ;
56+ pointerMove : Subject < TouchEvent | MouseEvent > = new Subject < TouchEvent | MouseEvent > ( ) ;
5257
5358 /**
5459 * Emits the `touchend` or `mouseup` events that are dispatched
5560 * while the user is dragging a drag item instance.
5661 */
57- readonly pointerUp : Subject < TouchEvent | MouseEvent > = new Subject < TouchEvent | MouseEvent > ( ) ;
62+ pointerUp : Subject < TouchEvent | MouseEvent > = new Subject < TouchEvent | MouseEvent > ( ) ;
5863
5964 /** Emits when the viewport has been scrolled while the user is dragging an item. */
60- readonly scroll : Subject < Event > = new Subject < Event > ( ) ;
65+ scroll : Subject < Event > = new Subject < Event > ( ) ;
66+
67+ /** Emits when the page has been blurred while the user is dragging an item. */
68+ pageBlurred : Subject < void > = new Subject < void > ( ) ;
6169
6270 constructor (
6371 private _ngZone : NgZone ,
6472 @Inject ( DOCUMENT ) _document : any ) {
6573 this . _document = _document ;
74+ this . _window = ( typeof window !== 'undefined' && window . addEventListener ) ? window : null ;
6675 }
6776
6877 /** Adds a drop container to the registry. */
@@ -129,30 +138,40 @@ export class DragDropRegistry<I, C> implements OnDestroy {
129138 this . _globalListeners
130139 . set ( moveEvent , {
131140 handler : ( e : Event ) => this . pointerMove . next ( e as TouchEvent | MouseEvent ) ,
132- options : activeCapturingEventOptions
141+ options : activeCapturingEventOptions ,
142+ target : this . _document
133143 } )
134144 . set ( upEvent , {
135145 handler : ( e : Event ) => this . pointerUp . next ( e as TouchEvent | MouseEvent ) ,
136- options : true
146+ options : true ,
147+ target : this . _document
137148 } )
138149 . set ( 'scroll' , {
139150 handler : ( e : Event ) => this . scroll . next ( e ) ,
140151 // Use capturing so that we pick up scroll changes in any scrollable nodes that aren't
141152 // the document. See https://github.com/angular/components/issues/17144.
142- options : true
153+ options : true ,
154+ target : this . _document
143155 } )
144156 // Preventing the default action on `mousemove` isn't enough to disable text selection
145157 // on Safari so we need to prevent the selection event as well. Alternatively this can
146158 // be done by setting `user-select: none` on the `body`, however it has causes a style
147159 // recalculation which can be expensive on pages with a lot of elements.
148160 . set ( 'selectstart' , {
149161 handler : this . _preventDefaultWhileDragging ,
150- options : activeCapturingEventOptions
162+ options : activeCapturingEventOptions ,
163+ target : this . _document
164+ } )
165+ . set ( 'blur' , {
166+ handler : ( ) => this . pageBlurred . next ( ) ,
167+ target : this . _window // Note that this event can only be bound on the window, not document
151168 } ) ;
152169
153170 this . _ngZone . runOutsideAngular ( ( ) => {
154171 this . _globalListeners . forEach ( ( config , name ) => {
155- this . _document . addEventListener ( name , config . handler , config . options ) ;
172+ if ( config . target ) {
173+ config . target . addEventListener ( name , config . handler , config . options ) ;
174+ }
156175 } ) ;
157176 } ) ;
158177 }
@@ -178,6 +197,7 @@ export class DragDropRegistry<I, C> implements OnDestroy {
178197 this . _clearGlobalListeners ( ) ;
179198 this . pointerMove . complete ( ) ;
180199 this . pointerUp . complete ( ) ;
200+ this . pageBlurred . complete ( ) ;
181201 }
182202
183203 /**
@@ -193,7 +213,9 @@ export class DragDropRegistry<I, C> implements OnDestroy {
193213 /** Clears out the global event listeners from the `document`. */
194214 private _clearGlobalListeners ( ) {
195215 this . _globalListeners . forEach ( ( config , name ) => {
196- this . _document . removeEventListener ( name , config . handler , config . options ) ;
216+ if ( config . target ) {
217+ config . target . removeEventListener ( name , config . handler , config . options ) ;
218+ }
197219 } ) ;
198220
199221 this . _globalListeners . clear ( ) ;
0 commit comments