text: /**
   * @fileoverview added by tsickle
   * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
   */
  /**
   * @license
   * Copyright Google LLC All Rights Reserved.
   *
   * Use of this source code is governed by an MIT-style license that can be
   * found in the LICENSE file at https://angular.io/license
   */
  import { coerceElement } from '@angular/cdk/coercion';
  import { _supportsShadowDom } from '@angular/cdk/platform';
  import { Subject, Subscription, interval, animationFrameScheduler } from 'rxjs';
  import { takeUntil } from 'rxjs/operators';
  import { moveItemInArray } from './drag-utils';
  /**
   * Counter used to generate unique ids for drop refs.
   * @type {?}
   */
  let _uniqueIdCounter = 0;
  /**
   * Proximity, as a ratio to width/height, at which a
   * dragged item will affect the drop container.
   * @type {?}
   */
  const DROP_PROXIMITY_THRESHOLD = 0.05;
  /**
   * Proximity, as a ratio to width/height at which to start auto-scrolling the drop list or the
   * viewport. The value comes from trying it out manually until it feels right.
   * @type {?}
   */
  const SCROLL_PROXIMITY_THRESHOLD = 0.05;
  /**
   * Number of pixels to scroll for each frame when auto-scrolling an element.
   * The value comes from trying it out manually until it feels right.
   * @type {?}
   */
  const AUTO_SCROLL_STEP = 2;
  /**
   * Entry in the position cache for draggable items.
   * \@docs-private
   * @record
   */
  function CachedItemPosition() { }
  if (false) {
      /**
       * Instance of the drag item.
       * @type {?}
       */
      CachedItemPosition.prototype.drag;
      /**
       * Dimensions of the item.
       * @type {?}
       */
      CachedItemPosition.prototype.clientRect;
      /**
       * Amount by which the item has been moved since dragging started.
       * @type {?}
       */
      CachedItemPosition.prototype.offset;
  }
  /**
   * Object holding the scroll position of something.
   * @record
   */
  function ScrollPosition() { }
  if (false) {
      /** @type {?} */
      ScrollPosition.prototype.top;
      /** @type {?} */
      ScrollPosition.prototype.left;
  }
  /** @enum {number} */
  const AutoScrollVerticalDirection = {
      NONE: 0, UP: 1, DOWN: 2,
  };
  /** @enum {number} */
  const AutoScrollHorizontalDirection = {
      NONE: 0, LEFT: 1, RIGHT: 2,
  };
  /**
   * Internal compile-time-only representation of a `DropListRef`.
   * Used to avoid circular import issues between the `DropListRef` and the `DragRef`.
   * \@docs-private
   * @record
   */
  export function DropListRefInternal() { }
  /**
   * Reference to a drop list. Used to manipulate or dispose of the container.
   * \@docs-private
   * @template T
   */
  export class DropListRef {
      /**
       * @param {?} element
       * @param {?} _dragDropRegistry
       * @param {?} _document
       * @param {?=} _ngZone
       * @param {?=} _viewportRuler
       */
      constructor(element, _dragDropRegistry, _document, _ngZone, _viewportRuler) {
          this._dragDropRegistry = _dragDropRegistry;
          this._ngZone = _ngZone;
          this._viewportRuler = _viewportRuler;
          /**
           * Unique ID for the drop list.
           * @deprecated No longer being used. To be removed.
           * \@breaking-change 8.0.0
           */
          this.id = `cdk-drop-list-ref-${_uniqueIdCounter++}`;
          /**
           * Whether starting a dragging sequence from this container is disabled.
           */
          this.disabled = false;
          /**
           * Whether sorting items within the list is disabled.
           */
          this.sortingDisabled = false;
          /**
           * Whether auto-scrolling the view when the user
           * moves their pointer close to the edges is disabled.
           */
          this.autoScrollDisabled = false;
          /**
           * Function that is used to determine whether an item
           * is allowed to be moved into a drop container.
           */
          this.enterPredicate = (/**
           * @return {?}
           */
          () => true);
          /**
           * Emits right before dragging has started.
           */
          this.beforeStarted = new Subject();
          /**
           * Emits when the user has moved a new drag item into this container.
           */
          this.entered = new Subject();
          /**
           * Emits when the user removes an item from the container
           * by dragging it into another container.
           */
          this.exited = new Subject();
          /**
           * Emits when the user drops an item inside the container.
           */
          this.dropped = new Subject();
          /**
           * Emits as the user is swapping items while actively dragging.
           */
          this.sorted = new Subject();
          /**
           * Whether an item in the list is being dragged.
           */
          this._isDragging = false;
          /**
           * Cache of the dimensions of all the items inside the container.
           */
          this._itemPositions = [];
          /**
           * Keeps track of the container's scroll position.
           */
          this._scrollPosition = { top: 0, left: 0 };
          /**
           * Keeps track of the scroll position of the viewport.
           */
          this._viewportScrollPosition = { top: 0, left: 0 };
          /**
           * Keeps track of the item that was last swapped with the dragged item, as
           * well as what direction the pointer was moving in when the swap occured.
           */
          this._previousSwap = { drag: (/** @type {?} */ (null)), delta: 0 };
          /**
           * Drop lists that are connected to the current one.
           */
          this._siblings = [];
          /**
           * Direction in which the list is oriented.
           */
          this._orientation = 'vertical';
          /**
           * Connected siblings that currently have a dragged item.
           */
          this._activeSiblings = new Set();
          /**
           * Layout direction of the drop list.
           */
          this._direction = 'ltr';
          /**
           * Subscription to the window being scrolled.
           */
          this._viewportScrollSubscription = Subscription.EMPTY;
          /**
           * Vertical direction in which the list is currently scrolling.
           */
          this._verticalScrollDirection = 0 /* NONE */;
          /**
           * Horizontal direction in which the list is currently scrolling.
           */
          this._horizontalScrollDirection = 0 /* NONE */;
          /**
           * Used to signal to the current auto-scroll sequence when to stop.
           */
          this._stopScrollTimers = new Subject();
          /**
           * Handles the container being scrolled. Has to be an arrow function to preserve the context.
           */
          this._handleScroll = (/**
           * @return {?}
           */
          () => {
              if (!this.isDragging()) {
                  return;
              }
              /** @type {?} */
              const element = coerceElement(this.element);
              this._updateAfterScroll(this._scrollPosition, element.scrollTop, element.scrollLeft);
          });
          /**
           * Starts the interval that'll auto-scroll the element.
           */
          this._startScrollInterval = (/**
           * @return {?}
           */
          () => {
              this._stopScrolling();
              interval(0, animationFrameScheduler)
                  .pipe(takeUntil(this._stopScrollTimers))
                  .subscribe((/**
               * @return {?}
               */
              () => {
                  /** @type {?} */
                  const node = this._scrollNode;
                  if (this._verticalScrollDirection === 1 /* UP */) {
                      incrementVerticalScroll(node, -AUTO_SCROLL_STEP);
                  }
                  else if (this._verticalScrollDirection === 2 /* DOWN */) {
                      incrementVerticalScroll(node, AUTO_SCROLL_STEP);
                  }
                  if (this._horizontalScrollDirection === 1 /* LEFT */) {
                      incrementHorizontalScroll(node, -AUTO_SCROLL_STEP);
                  }
                  else if (this._horizontalScrollDirection === 2 /* RIGHT */) {
                      incrementHorizontalScroll(node, AUTO_SCROLL_STEP);
                  }
              }));
          });
          /** @type {?} */
          const nativeNode = this.element = coerceElement(element);
          this._shadowRoot = getShadowRoot(nativeNode) || _document;
          _dragDropRegistry.registerDropContainer(this);
      }
      /**
       * Removes the drop list functionality from the DOM element.
       * @return {?}
       */
      dispose() {
          this._stopScrolling();
          this._stopScrollTimers.complete();
          this._removeListeners();
          this.beforeStarted.complete();
          this.entered.complete();
          this.exited.complete();
          this.dropped.complete();
          this.sorted.complete();
          this._activeSiblings.clear();
          this._scrollNode = (/** @type {?} */ (null));
          this._dragDropRegistry.removeDropContainer(this);
      }
      /**
       * Whether an item from this list is currently being dragged.
       * @return {?}
       */
      isDragging() {
          return this._isDragging;
      }
      /**
       * Starts dragging an item.
       * @return {?}
       */
      start() {
          /** @type {?} */
          const element = coerceElement(this.element);
          this.beforeStarted.next();
          this._isDragging = true;
          this._cacheItems();
          this._siblings.forEach((/**
           * @param {?} sibling
           * @return {?}
           */
          sibling => sibling._startReceiving(this)));
          this._removeListeners();
          // @breaking-change 9.0.0 Remove check for _ngZone once it's marked as a required param.
          if (this._ngZone) {
              this._ngZone.runOutsideAngular((/**
               * @return {?}
               */
              () => element.addEventListener('scroll', this._handleScroll)));
          }
          else {
              element.addEventListener('scroll', this._handleScroll);
          }
          // @breaking-change 9.0.0 Remove check for _viewportRuler once it's marked as a required param.
          if (this._viewportRuler) {
              this._listenToScrollEvents();
          }
      }
      /**
       * Emits an event to indicate that the user moved an item into the container.
       * @param {?} item Item that was moved into the container.
       * @param {?} pointerX Position of the item along the X axis.
       * @param {?} pointerY Position of the item along the Y axis.
       * @return {?}
       */
      enter(item, pointerX, pointerY) {
          this.start();
          // If sorting is disabled, we want the item to return to its starting
          // position if the user is returning it to its initial container.
          /** @type {?} */
          let newIndex = this.sortingDisabled ? this._draggables.indexOf(item) : -1;
          if (newIndex === -1) {
              // We use the coordinates of where the item entered the drop
              // zone to figure out at which index it should be inserted.
              newIndex = this._getItemIndexFromPointerPosition(item, pointerX, pointerY);
          }
          /** @type {?} */
          const activeDraggables = this._activeDraggables;
          /** @type {?} */
          const currentIndex = activeDraggables.indexOf(item);
          /** @type {?} */
          const placeholder = item.getPlaceholderElement();
          /** @type {?} */
          let newPositionReference = activeDraggables[newIndex];
          // If the item at the new position is the same as the item that is being dragged,
          // it means that we're trying to restore the item to its initial position. In this
          // case we should use the next item from the list as the reference.
          if (newPositionReference === item) {
              newPositionReference = activeDraggables[newIndex + 1];
          }
          // Since the item may be in the `activeDraggables` already (e.g. if the user dragged it
          // into another container and back again), we have to ensure that it isn't duplicated.
          if (currentIndex > -1) {
              activeDraggables.splice(currentIndex, 1);
          }
          // Don't use items that are being dragged as a reference, because
          // their element has been moved down to the bottom of the body.
          if (newPositionReference && !this._dragDropRegistry.isDragging(newPositionReference)) {
              /** @type {?} */
              const element = newPositionReference.getRootElement();
              (/** @type {?} */ (element.parentElement)).insertBefore(placeholder, element);
              activeDraggables.splice(newIndex, 0, item);
          }
          else {
              coerceElement(this.element).appendChild(placeholder);
              activeDraggables.push(item);
          }
          // The transform needs to be cleared so it doesn't throw off the measurements.
          placeholder.style.transform = '';
          // Note that the positions were already cached when we called `start` above,
          // but we need to refresh them since the amount of items has changed.
          this._cacheItemPositions();
          this.entered.next({ item, container: this, currentIndex: this.getItemIndex(item) });
      }
      /**
       * Removes an item from the container after it was dragged into another container by the user.
       * @param {?} item Item that was dragged out.
       * @return {?}
       */
      exit(item) {
          this._reset();
          this.exited.next({ item, container: this });
      }
      /**
       * Drops an item into this container.
       * \@breaking-change 9.0.0 `distance` parameter to become required.
       * @param {?} item Item being dropped into the container.
       * @param {?} currentIndex Index at which the item should be inserted.
       * @param {?} previousContainer Container from which the item got dragged in.
       * @param {?} isPointerOverContainer Whether the user's pointer was over the
       *    container when the item was dropped.
       * @param {?=} distance Distance the user has dragged since the start of the dragging sequence.
       * @return {?}
       */
      drop(item, currentIndex, previousContainer, isPointerOverContainer, distance = { x: 0, y: 0 }) {
          this._reset();
          this.dropped.next({
              item,
              currentIndex,
              previousIndex: previousContainer.getItemIndex(item),
              container: this,
              previousContainer,
              isPointerOverContainer,
              distance
          });
      }
      /**
       * Sets the draggable items that are a part of this list.
       * @template THIS
       * @this {THIS}
       * @param {?} items Items that are a part of this list.
       * @return {THIS}
       */
      withItems(items) {
          (/** @type {?} */ (this))._draggables = items;
          items.forEach((/**
           * @param {?} item
           * @return {?}
           */
          item => item._withDropContainer((/** @type {?} */ (this)))));
          if ((/** @type {?} */ (this)).isDragging()) {
              (/** @type {?} */ (this))._cacheItems();
          }
          return (/** @type {?} */ (this));
      }
      /**
       * Sets the layout direction of the drop list.
       * @template THIS
       * @this {THIS}
       * @param {?} direction
       * @return {THIS}
       */
      withDirection(direction) {
          (/** @type {?} */ (this))._direction = direction;
          return (/** @type {?} */ (this));
      }
      /**
       * Sets the containers that are connected to this one. When two or more containers are
       * connected, the user will be allowed to transfer items between them.
       * @template THIS
       * @this {THIS}
       * @param {?} connectedTo Other containers that the current containers should be connected to.
       * @return {THIS}
       */
      connectedTo(connectedTo) {
          (/** @type {?} */ (this))._siblings = connectedTo.slice();
          return (/** @type {?} */ (this));
      }
      /**
       * Sets the orientation of the container.
       * @template THIS
       * @this {THIS}
       * @param {?} orientation New orientation for the container.
       * @return {THIS}
       */
      withOrientation(orientation) {
          (/** @type {?} */ (this))._orientation = orientation;
          return (/** @type {?} */ (this));
      }
      /**
       * Figures out the index of an item in the container.
       * @param {?} item Item whose index should be determined.
       * @return {?}
       */
      getItemIndex(item) {
          if (!this._isDragging) {
              return this._draggables.indexOf(item);
          }
          // Items are sorted always by top/left in the cache, however they flow differently in RTL.
          // The rest of the logic still stands no matter what orientation we're in, however
          // we need to invert the array when determining the index.
          /** @type {?} */
          const items = this._orientation === 'horizontal' && this._direction === 'rtl' ?
              this._itemPositions.slice().reverse() : this._itemPositions;
          return findIndex(items, (/**
           * @param {?} currentItem
           * @return {?}
           */
          currentItem => currentItem.drag === item));
      }
      /**
       * Whether the list is able to receive the item that
       * is currently being dragged inside a connected drop list.
       * @return {?}
       */
      isReceiving() {
          return this._activeSiblings.size > 0;
      }
      /**
       * Sorts an item inside the container based on its position.
       * @param {?} item Item to be sorted.
       * @param {?} pointerX Position of the item along the X axis.
       * @param {?} pointerY Position of the item along the Y axis.
       * @param {?} pointerDelta Direction in which the pointer is moving along each axis.
       * @return {?}
       */
      _sortItem(item, pointerX, pointerY, pointerDelta) {
          // Don't sort the item if sorting is disabled or it's out of range.
          if (this.sortingDisabled || !this._isPointerNearDropContainer(pointerX, pointerY)) {
              return;
          }
          /** @type {?} */
          const siblings = this._itemPositions;
          /** @type {?} */
          const newIndex = this._getItemIndexFromPointerPosition(item, pointerX, pointerY, pointerDelta);
          if (newIndex === -1 && siblings.length > 0) {
              return;
          }
          /** @type {?} */
          const isHorizontal = this._orientation === 'horizontal';
          /** @type {?} */
          const currentIndex = findIndex(siblings, (/**
           * @param {?} currentItem
           * @return {?}
           */
          currentItem => currentItem.drag === item));
          /** @type {?} */
          const siblingAtNewPosition = siblings[newIndex];
          /** @type {?} */
          const currentPosition = siblings[currentIndex].clientRect;
          /** @type {?} */
          const newPosition = siblingAtNewPosition.clientRect;
          /** @type {?} */
          const delta = currentIndex > newIndex ? 1 : -1;
          this._previousSwap.drag = siblingAtNewPosition.drag;
          this._previousSwap.delta = isHorizontal ? pointerDelta.x : pointerDelta.y;
          // How many pixels the item's placeholder should be offset.
          /** @type {?} */
          const itemOffset = this._getItemOffsetPx(currentPosition, newPosition, delta);
          // How many pixels all the other items should be offset.
          /** @type {?} */
          const siblingOffset = this._getSiblingOffsetPx(currentIndex, siblings, delta);
          // Save the previous order of the items before moving the item to its new index.
          // We use this to check whether an item has been moved as a result of the sorting.
          /** @type {?} */
          const oldOrder = siblings.slice();
          // Shuffle the array in place.
          moveItemInArray(siblings, currentIndex, newIndex);
          this.sorted.next({
              previousIndex: currentIndex,
              currentIndex: newIndex,
              container: this,
              item
          });
          siblings.forEach((/**
           * @param {?} sibling
           * @param {?} index
           * @return {?}
           */
          (sibling, index) => {
              // Don't do anything if the position hasn't changed.
              if (oldOrder[index] === sibling) {
                  return;
              }
              /** @type {?} */
              const isDraggedItem = sibling.drag === item;
              /** @type {?} */
              const offset = isDraggedItem ? itemOffset : siblingOffset;
              /** @type {?} */
              const elementToOffset = isDraggedItem ? item.getPlaceholderElement() :
                  sibling.drag.getRootElement();
              // Update the offset to reflect the new position.
              sibling.offset += offset;
              // Since we're moving the items with a `transform`, we need to adjust their cached
              // client rects to reflect their new position, as well as swap their positions in the cache.
              // Note that we shouldn't use `getBoundingClientRect` here to update the cache, because the
              // elements may be mid-animation which will give us a wrong result.
              if (isHorizontal) {
                  // Round the transforms since some browsers will
                  // blur the elements, for sub-pixel transforms.
                  elementToOffset.style.transform = `translate3d(${Math.round(sibling.offset)}px, 0, 0)`;
                  adjustClientRect(sibling.clientRect, 0, offset);
              }
              else {
                  elementToOffset.style.transform = `translate3d(0, ${Math.round(sibling.offset)}px, 0)`;
                  adjustClientRect(sibling.clientRect, offset, 0);
              }
          }));
      }
      /**
       * Checks whether the user's pointer is close to the edges of either the
       * viewport or the drop list and starts the auto-scroll sequence.
       * @param {?} pointerX User's pointer position along the x axis.
       * @param {?} pointerY User's pointer position along the y axis.
       * @return {?}
       */
      _startScrollingIfNecessary(pointerX, pointerY) {
          if (this.autoScrollDisabled) {
              return;
          }
          /** @type {?} */
          let scrollNode;
          /** @type {?} */
          let verticalScrollDirection = 0 /* NONE */;
          /** @type {?} */
          let horizontalScrollDirection = 0 /* NONE */;
          // Check whether we should start scrolling the container.
          if (this._isPointerNearDropContainer(pointerX, pointerY)) {
              /** @type {?} */
              const element = coerceElement(this.element);
              [verticalScrollDirection, horizontalScrollDirection] =
                  getElementScrollDirections(element, this._clientRect, pointerX, pointerY);
              if (verticalScrollDirection || horizontalScrollDirection) {
                  scrollNode = element;
              }
          }
          // @breaking-change 9.0.0 Remove null check for _viewportRuler once it's a required parameter.
          // Otherwise check if we can start scrolling the viewport.
          if (this._viewportRuler && !verticalScrollDirection && !horizontalScrollDirection) {
              const { width, height } = this._viewportRuler.getViewportSize();
              /** @type {?} */
              const clientRect = { width, height, top: 0, right: width, bottom: height, left: 0 };
              verticalScrollDirection = getVerticalScrollDirection(clientRect, pointerY);
              horizontalScrollDirection = getHorizontalScrollDirection(clientRect, pointerX);
              scrollNode = window;
          }
          if (scrollNode && (verticalScrollDirection !== this._verticalScrollDirection ||
              horizontalScrollDirection !== this._horizontalScrollDirection ||
              scrollNode !== this._scrollNode)) {
              this._verticalScrollDirection = verticalScrollDirection;
              this._horizontalScrollDirection = horizontalScrollDirection;
              this._scrollNode = scrollNode;
              if ((verticalScrollDirection || horizontalScrollDirection) && scrollNode) {
                  // @breaking-change 9.0.0 Remove null check for `_ngZone` once it is made required.
                  if (this._ngZone) {
                      this._ngZone.runOutsideAngular(this._startScrollInterval);
                  }
                  else {
                      this._startScrollInterval();
                  }
              }
              else {
                  this._stopScrolling();
              }
          }
      }
      /**
       * Stops any currently-running auto-scroll sequences.
       * @return {?}
       */
      _stopScrolling() {
          this._stopScrollTimers.next();
      }
      /**
       * Caches the position of the drop list.
       * @private
       * @return {?}
       */
      _cacheOwnPosition() {
          /** @type {?} */
          const element = coerceElement(this.element);
          this._clientRect = getMutableClientRect(element);
          this._scrollPosition = { top: element.scrollTop, left: element.scrollLeft };
      }
      /**
       * Refreshes the position cache of the items and sibling containers.
       * @private
       * @return {?}
       */
      _cacheItemPositions() {
          /** @type {?} */
          const isHorizontal = this._orientation === 'horizontal';
          this._itemPositions = this._activeDraggables.map((/**
           * @param {?} drag
           * @return {?}
           */
          drag => {
              /** @type {?} */
              const elementToMeasure = this._dragDropRegistry.isDragging(drag) ?
                  // If the element is being dragged, we have to measure the
                  // placeholder, because the element is hidden.
                  drag.getPlaceholderElement() :
                  drag.getRootElement();
              return { drag, offset: 0, clientRect: getMutableClientRect(elementToMeasure) };
          })).sort((/**
           * @param {?} a
           * @param {?} b
           * @return {?}
           */
          (a, b) => {
              return isHorizontal ? a.clientRect.left - b.clientRect.left :
                  a.clientRect.top - b.clientRect.top;
          }));
      }
      /**
       * Resets the container to its initial state.
       * @private
       * @return {?}
       */
      _reset() {
          this._isDragging = false;
          // TODO(crisbeto): may have to wait for the animations to finish.
          this._activeDraggables.forEach((/**
           * @param {?} item
           * @return {?}
           */
          item => item.getRootElement().style.transform = ''));
          this._siblings.forEach((/**
           * @param {?} sibling
           * @return {?}
           */
          sibling => sibling._stopReceiving(this)));
          this._activeDraggables = [];
          this._itemPositions = [];
          this._previousSwap.drag = null;
          this._previousSwap.delta = 0;
          this._stopScrolling();
          this._removeListeners();
      }
      /**
       * Gets the offset in pixels by which the items that aren't being dragged should be moved.
       * @private
       * @param {?} currentIndex Index of the item currently being dragged.
       * @param {?} siblings All of the items in the list.
       * @param {?} delta Direction in which the user is moving.
       * @return {?}
       */
      _getSiblingOffsetPx(currentIndex, siblings, delta) {
          /** @type {?} */
          const isHorizontal = this._orientation === 'horizontal';
          /** @type {?} */
          const currentPosition = siblings[currentIndex].clientRect;
          /** @type {?} */
          const immediateSibling = siblings[currentIndex + delta * -1];
          /** @type {?} */
          let siblingOffset = currentPosition[isHorizontal ? 'width' : 'height'] * delta;
          if (immediateSibling) {
              /** @type {?} */
              const start = isHorizontal ? 'left' : 'top';
              /** @type {?} */
              const end = isHorizontal ? 'right' : 'bottom';
              // Get the spacing between the start of the current item and the end of the one immediately
              // after it in the direction in which the user is dragging, or vice versa. We add it to the
              // offset in order to push the element to where it will be when it's inline and is influenced
              // by the `margin` of its siblings.
              if (delta === -1) {
                  siblingOffset -= immediateSibling.clientRect[start] - currentPosition[end];
              }
              else {
                  siblingOffset += currentPosition[start] - immediateSibling.clientRect[end];
              }
          }
          return siblingOffset;
      }
      /**
       * Checks whether the pointer coordinates are close to the drop container.
       * @private
       * @param {?} pointerX Coordinates along the X axis.
       * @param {?} pointerY Coordinates along the Y axis.
       * @return {?}
       */
      _isPointerNearDropContainer(pointerX, pointerY) {
          const { top, right, bottom, left, width, height } = this._clientRect;
          /** @type {?} */
          const xThreshold = width * DROP_PROXIMITY_THRESHOLD;
          /** @type {?} */
          const yThreshold = height * DROP_PROXIMITY_THRESHOLD;
          return pointerY > top - yThreshold && pointerY < bottom + yThreshold &&
              pointerX > left - xThreshold && pointerX < right + xThreshold;
      }
      /**
       * Gets the offset in pixels by which the item that is being dragged should be moved.
       * @private
       * @param {?} currentPosition Current position of the item.
       * @param {?} newPosition Position of the item where the current item should be moved.
       * @param {?} delta Direction in which the user is moving.
       * @return {?}
       */
      _getItemOffsetPx(currentPosition, newPosition, delta) {
          /** @type {?} */
          const isHorizontal = this._orientation === 'horizontal';
          /** @type {?} */
          let itemOffset = isHorizontal ? newPosition.left - currentPosition.left :
              newPosition.top - currentPosition.top;
          // Account for differences in the item width/height.
          if (delta === -1) {
              itemOffset += isHorizontal ? newPosition.width - currentPosition.width :
                  newPosition.height - currentPosition.height;
          }
          return itemOffset;
      }
      /**
       * Gets the index of an item in the drop container, based on the position of the user's pointer.
       * @private
       * @param {?} item Item that is being sorted.
       * @param {?} pointerX Position of the user's pointer along the X axis.
       * @param {?} pointerY Position of the user's pointer along the Y axis.
       * @param {?=} delta Direction in which the user is moving their pointer.
       * @return {?}
       */
      _getItemIndexFromPointerPosition(item, pointerX, pointerY, delta) {
          /** @type {?} */
          const isHorizontal = this._orientation === 'horizontal';
          return findIndex(this._itemPositions, (/**
           * @param {?} __0
           * @param {?} _
           * @param {?} array
           * @return {?}
           */
          ({ drag, clientRect }, _, array) => {
              if (drag === item) {
                  // If there's only one item left in the container, it must be
                  // the dragged item itself so we use it as a reference.
                  return array.length < 2;
              }
              if (delta) {
                  /** @type {?} */
                  const direction = isHorizontal ? delta.x : delta.y;
                  // If the user is still hovering over the same item as last time, and they didn't change
                  // the direction in which they're dragging, we don't consider it a direction swap.
                  if (drag === this._previousSwap.drag && direction === this._previousSwap.delta) {
                      return false;
                  }
              }
              return isHorizontal ?
                  // Round these down since most browsers report client rects with
                  // sub-pixel precision, whereas the pointer coordinates are rounded to pixels.
                  pointerX >= Math.floor(clientRect.left) && pointerX <= Math.floor(clientRect.right) :
                  pointerY >= Math.floor(clientRect.top) && pointerY <= Math.floor(clientRect.bottom);
          }));
      }
      /**
       * Caches the current items in the list and their positions.
       * @private
       * @return {?}
       */
      _cacheItems() {
          this._activeDraggables = this._draggables.slice();
          this._cacheItemPositions();
          this._cacheOwnPosition();
      }
      /**
       * Updates the internal state of the container after a scroll event has happened.
       * @private
       * @param {?} scrollPosition Object that is keeping track of the scroll position.
       * @param {?} newTop New top scroll position.
       * @param {?} newLeft New left scroll position.
       * @param {?=} extraClientRect Extra `ClientRect` object that should be updated, in addition to the
       *  ones of the drag items. Useful when the viewport has been scrolled and we also need to update
       *  the `ClientRect` of the list.
       * @return {?}
       */
      _updateAfterScroll(scrollPosition, newTop, newLeft, extraClientRect) {
          /** @type {?} */
          const topDifference = scrollPosition.top - newTop;
          /** @type {?} */
          const leftDifference = scrollPosition.left - newLeft;
          if (extraClientRect) {
              adjustClientRect(extraClientRect, topDifference, leftDifference);
          }
          // Since we know the amount that the user has scrolled we can shift all of the client rectangles
          // ourselves. This is cheaper than re-measuring everything and we can avoid inconsistent
          // behavior where we might be measuring the element before its position has changed.
          this._itemPositions.forEach((/**
           * @param {?} __0
           * @return {?}
           */
          ({ clientRect }) => {
              adjustClientRect(clientRect, topDifference, leftDifference);
          }));
          // We need two loops for this, because we want all of the cached
          // positions to be up-to-date before we re-sort the item.
          this._itemPositions.forEach((/**
           * @param {?} __0
           * @return {?}
           */
          ({ drag }) => {
              if (this._dragDropRegistry.isDragging(drag)) {
                  // We need to re-sort the item manually, because the pointer move
                  // events won't be dispatched while the user is scrolling.
                  drag._sortFromLastPointerPosition();
              }
          }));
          scrollPosition.top = newTop;
          scrollPosition.left = newLeft;
      }
      /**
       * Removes the event listeners associated with this drop list.
       * @private
       * @return {?}
       */
      _removeListeners() {
          coerceElement(this.element).removeEventListener('scroll', this._handleScroll);
          this._viewportScrollSubscription.unsubscribe();
      }
      /**
       * Checks whether the user's pointer is positioned over the container.
       * @param {?} x Pointer position along the X axis.
       * @param {?} y Pointer position along the Y axis.
       * @return {?}
       */
      _isOverContainer(x, y) {
          return isInsideClientRect(this._clientRect, x, y);
      }
      /**
       * Figures out whether an item should be moved into a sibling
       * drop container, based on its current position.
       * @param {?} item Drag item that is being moved.
       * @param {?} x Position of the item along the X axis.
       * @param {?} y Position of the item along the Y axis.
       * @return {?}
       */
      _getSiblingContainerFromPosition(item, x, y) {
          return this._siblings.find((/**
           * @param {?} sibling
           * @return {?}
           */
          sibling => sibling._canReceive(item, x, y)));
      }
      /**
       * Checks whether the drop list can receive the passed-in item.
       * @param {?} item Item that is being dragged into the list.
       * @param {?} x Position of the item along the X axis.
       * @param {?} y Position of the item along the Y axis.
       * @return {?}
       */
      _canReceive(item, x, y) {
          if (!this.enterPredicate(item, this) || !isInsideClientRect(this._clientRect, x, y)) {
              return false;
          }
          /** @type {?} */
          const elementFromPoint = (/** @type {?} */ (this._shadowRoot.elementFromPoint(x, y)));
          // If there's no element at the pointer position, then
          // the client rect is probably scrolled out of the view.
          if (!elementFromPoint) {
              return false;
          }
          /** @type {?} */
          const nativeElement = coerceElement(this.element);
          // The `ClientRect`, that we're using to find the container over which the user is
          // hovering, doesn't give us any information on whether the element has been scrolled
          // out of the view or whether it's overlapping with other containers. This means that
          // we could end up transferring the item into a container that's invisible or is positioned
          // below another one. We use the result from `elementFromPoint` to get the top-most element
          // at the pointer position and to find whether it's one of the intersecting drop containers.
          return elementFromPoint === nativeElement || nativeElement.contains(elementFromPoint);
      }
      /**
       * Called by one of the connected drop lists when a dragging sequence has started.
       * @param {?} sibling Sibling in which dragging has started.
       * @return {?}
       */
      _startReceiving(sibling) {
          /** @type {?} */
          const activeSiblings = this._activeSiblings;
          if (!activeSiblings.has(sibling)) {
              activeSiblings.add(sibling);
              this._cacheOwnPosition();
              this._listenToScrollEvents();
          }
      }
      /**
       * Called by a connected drop list when dragging has stopped.
       * @param {?} sibling Sibling whose dragging has stopped.
       * @return {?}
       */
      _stopReceiving(sibling) {
          this._activeSiblings.delete(sibling);
          this._viewportScrollSubscription.unsubscribe();
      }
      /**
       * Starts listening to scroll events on the viewport.
       * Used for updating the internal state of the list.
       * @private
       * @return {?}
       */
      _listenToScrollEvents() {
          this._viewportScrollPosition = (/** @type {?} */ (this._viewportRuler)).getViewportScrollPosition();
          this._viewportScrollSubscription = this._dragDropRegistry.scroll.subscribe((/**
           * @return {?}
           */
          () => {
              if (this.isDragging()) {
                  /** @type {?} */
                  const newPosition = (/** @type {?} */ (this._viewportRuler)).getViewportScrollPosition();
                  this._updateAfterScroll(this._viewportScrollPosition, newPosition.top, newPosition.left, this._clientRect);
              }
              else if (this.isReceiving()) {
                  this._cacheOwnPosition();
              }
          }));
      }
  }
  if (false) {
      /**
       * Element that the drop list is attached to.
       * @type {?}
       */
      DropListRef.prototype.element;
      /**
       * Unique ID for the drop list.
       * @deprecated No longer being used. To be removed.
       * \@breaking-change 8.0.0
       * @type {?}
       */
      DropListRef.prototype.id;
      /**
       * Whether starting a dragging sequence from this container is disabled.
       * @type {?}
       */
      DropListRef.prototype.disabled;
      /**
       * Whether sorting items within the list is disabled.
       * @type {?}
       */
      DropListRef.prototype.sortingDisabled;
      /**
       * Locks the position of the draggable elements inside the container along the specified axis.
       * @type {?}
       */
      DropListRef.prototype.lockAxis;
      /**
       * Whether auto-scrolling the view when the user
       * moves their pointer close to the edges is disabled.
       * @type {?}
       */
      DropListRef.prototype.autoScrollDisabled;
      /**
       * Function that is used to determine whether an item
       * is allowed to be moved into a drop container.
       * @type {?}
       */
      DropListRef.prototype.enterPredicate;
      /**
       * Emits right before dragging has started.
       * @type {?}
       */
      DropListRef.prototype.beforeStarted;
      /**
       * Emits when the user has moved a new drag item into this container.
       * @type {?}
       */
      DropListRef.prototype.entered;
      /**
       * Emits when the user removes an item from the container
       * by dragging it into another container.
       * @type {?}
       */
      DropListRef.prototype.exited;
      /**
       * Emits when the user drops an item inside the container.
       * @type {?}
       */
      DropListRef.prototype.dropped;
      /**
       * Emits as the user is swapping items while actively dragging.
       * @type {?}
       */
      DropListRef.prototype.sorted;
      /**
       * Arbitrary data that can be attached to the drop list.
       * @type {?}
       */
      DropListRef.prototype.data;
      /**
       * Whether an item in the list is being dragged.
       * @type {?}
       * @private
       */
      DropListRef.prototype._isDragging;
      /**
       * Cache of the dimensions of all the items inside the container.
       * @type {?}
       * @private
       */
      DropListRef.prototype._itemPositions;
      /**
       * Keeps track of the container's scroll position.
       * @type {?}
       * @private
       */
      DropListRef.prototype._scrollPosition;
      /**
       * Keeps track of the scroll position of the viewport.
       * @type {?}
       * @private
       */
      DropListRef.prototype._viewportScrollPosition;
      /**
       * Cached `ClientRect` of the drop list.
       * @type {?}
       * @private
       */
      DropListRef.prototype._clientRect;
      /**
       * Draggable items that are currently active inside the container. Includes the items
       * from `_draggables`, as well as any items that have been dragged in, but haven't
       * been dropped yet.
       * @type {?}
       * @private
       */
      DropListRef.prototype._activeDraggables;
      /**
       * Keeps track of the item that was last swapped with the dragged item, as
       * well as what direction the pointer was moving in when the swap occured.
       * @type {?}
       * @private
       */
      DropListRef.prototype._previousSwap;
      /**
       * Draggable items in the container.
       * @type {?}
       * @private
       */
      DropListRef.prototype._draggables;
      /**
       * Drop lists that are connected to the current one.
       * @type {?}
       * @private
       */
      DropListRef.prototype._siblings;
      /**
       * Direction in which the list is oriented.
       * @type {?}
       * @private
       */
      DropListRef.prototype._orientation;
      /**
       * Connected siblings that currently have a dragged item.
       * @type {?}
       * @private
       */
      DropListRef.prototype._activeSiblings;
      /**
       * Layout direction of the drop list.
       * @type {?}
       * @private
       */
      DropListRef.prototype._direction;
      /**
       * Subscription to the window being scrolled.
       * @type {?}
       * @private
       */
      DropListRef.prototype._viewportScrollSubscription;
      /**
       * Vertical direction in which the list is currently scrolling.
       * @type {?}
       * @private
       */
      DropListRef.prototype._verticalScrollDirection;
      /**
       * Horizontal direction in which the list is currently scrolling.
       * @type {?}
       * @private
       */
      DropListRef.prototype._horizontalScrollDirection;
      /**
       * Node that is being auto-scrolled.
       * @type {?}
       * @private
       */
      DropListRef.prototype._scrollNode;
      /**
       * Used to signal to the current auto-scroll sequence when to stop.
       * @type {?}
       * @private
       */
      DropListRef.prototype._stopScrollTimers;
      /**
       * Shadow root of the current element. Necessary for `elementFromPoint` to resolve correctly.
       * @type {?}
       * @private
       */
      DropListRef.prototype._shadowRoot;
      /**
       * Handles the container being scrolled. Has to be an arrow function to preserve the context.
       * @type {?}
       * @private
       */
      DropListRef.prototype._handleScroll;
      /**
       * Starts the interval that'll auto-scroll the element.
       * @type {?}
       * @private
       */
      DropListRef.prototype._startScrollInterval;
      /**
       * @type {?}
       * @private
       */
      DropListRef.prototype._dragDropRegistry;
      /**
       * @deprecated _ngZone and _viewportRuler parameters to be made required.
       * \@breaking-change 9.0.0
       * @type {?}
       * @private
       */
      DropListRef.prototype._ngZone;
      /**
       * @type {?}
       * @private
       */
      DropListRef.prototype._viewportRuler;
  }
  /**
   * Updates the top/left positions of a `ClientRect`, as well as their bottom/right counterparts.
   * @param {?} clientRect `ClientRect` that should be updated.
   * @param {?} top Amount to add to the `top` position.
   * @param {?} left Amount to add to the `left` position.
   * @return {?}
   */
  function adjustClientRect(clientRect, top, left) {
      clientRect.top += top;
      clientRect.bottom = clientRect.top + clientRect.height;
      clientRect.left += left;
      clientRect.right = clientRect.left + clientRect.width;
  }
  /**
   * Finds the index of an item that matches a predicate function. Used as an equivalent
   * of `Array.prototype.findIndex` which isn't part of the standard Google typings.
   * @template T
   * @param {?} array Array in which to look for matches.
   * @param {?} predicate Function used to determine whether an item is a match.
   * @return {?}
   */
  function findIndex(array, predicate) {
      for (let i = 0; i < array.length; i++) {
          if (predicate(array[i], i, array)) {
              return i;
          }
      }
      return -1;
  }
  /**
   * Checks whether some coordinates are within a `ClientRect`.
   * @param {?} clientRect ClientRect that is being checked.
   * @param {?} x Coordinates along the X axis.
   * @param {?} y Coordinates along the Y axis.
   * @return {?}
   */
  function isInsideClientRect(clientRect, x, y) {
      const { top, bottom, left, right } = clientRect;
      return y >= top && y <= bottom && x >= left && x <= right;
  }
  /**
   * Gets a mutable version of an element's bounding `ClientRect`.
   * @param {?} element
   * @return {?}
   */
  function getMutableClientRect(element) {
      /** @type {?} */
      const clientRect = element.getBoundingClientRect();
      // We need to clone the `clientRect` here, because all the values on it are readonly
      // and we need to be able to update them. Also we can't use a spread here, because
      // the values on a `ClientRect` aren't own properties. See:
      // https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect#Notes
      return {
          top: clientRect.top,
          right: clientRect.right,
          bottom: clientRect.bottom,
          left: clientRect.left,
          width: clientRect.width,
          height: clientRect.height
      };
  }
  /**
   * Increments the vertical scroll position of a node.
   * @param {?} node Node whose scroll position should change.
   * @param {?} amount Amount of pixels that the `node` should be scrolled.
   * @return {?}
   */
  function incrementVerticalScroll(node, amount) {
      if (node === window) {
          ((/** @type {?} */ (node))).scrollBy(0, amount);
      }
      else {
          // Ideally we could use `Element.scrollBy` here as well, but IE and Edge don't support it.
          ((/** @type {?} */ (node))).scrollTop += amount;
      }
  }
  /**
   * Increments the horizontal scroll position of a node.
   * @param {?} node Node whose scroll position should change.
   * @param {?} amount Amount of pixels that the `node` should be scrolled.
   * @return {?}
   */
  function incrementHorizontalScroll(node, amount) {
      if (node === window) {
          ((/** @type {?} */ (node))).scrollBy(amount, 0);
      }
      else {
          // Ideally we could use `Element.scrollBy` here as well, but IE and Edge don't support it.
          ((/** @type {?} */ (node))).scrollLeft += amount;
      }
  }
  /**
   * Gets whether the vertical auto-scroll direction of a node.
   * @param {?} clientRect Dimensions of the node.
   * @param {?} pointerY Position of the user's pointer along the y axis.
   * @return {?}
   */
  function getVerticalScrollDirection(clientRect, pointerY) {
      const { top, bottom, height } = clientRect;
      /** @type {?} */
      const yThreshold = height * SCROLL_PROXIMITY_THRESHOLD;
      if (pointerY >= top - yThreshold && pointerY <= top + yThreshold) {
          return 1 /* UP */;
      }
      else if (pointerY >= bottom - yThreshold && pointerY <= bottom + yThreshold) {
          return 2 /* DOWN */;
      }
      return 0 /* NONE */;
  }
  /**
   * Gets whether the horizontal auto-scroll direction of a node.
   * @param {?} clientRect Dimensions of the node.
   * @param {?} pointerX Position of the user's pointer along the x axis.
   * @return {?}
   */
  function getHorizontalScrollDirection(clientRect, pointerX) {
      const { left, right, width } = clientRect;
      /** @type {?} */
      const xThreshold = width * SCROLL_PROXIMITY_THRESHOLD;
      if (pointerX >= left - xThreshold && pointerX <= left + xThreshold) {
          return 1 /* LEFT */;
      }
      else if (pointerX >= right - xThreshold && pointerX <= right + xThreshold) {
          return 2 /* RIGHT */;
      }
      return 0 /* NONE */;
  }
  /**
   * Gets the directions in which an element node should be scrolled,
   * assuming that the user's pointer is already within it scrollable region.
   * @param {?} element Element for which we should calculate the scroll direction.
   * @param {?} clientRect Bounding client rectangle of the element.
   * @param {?} pointerX Position of the user's pointer along the x axis.
   * @param {?} pointerY Position of the user's pointer along the y axis.
   * @return {?}
   */
  function getElementScrollDirections(element, clientRect, pointerX, pointerY) {
      /** @type {?} */
      const computedVertical = getVerticalScrollDirection(clientRect, pointerY);
      /** @type {?} */
      const computedHorizontal = getHorizontalScrollDirection(clientRect, pointerX);
      /** @type {?} */
      let verticalScrollDirection = 0 /* NONE */;
      /** @type {?} */
      let horizontalScrollDirection = 0 /* NONE */;
      // Note that we here we do some extra checks for whether the element is actually scrollable in
      // a certain direction and we only assign the scroll direction if it is. We do this so that we
      // can allow other elements to be scrolled, if the current element can't be scrolled anymore.
      // This allows us to handle cases where the scroll regions of two scrollable elements overlap.
      if (computedVertical) {
          /** @type {?} */
          const scrollTop = element.scrollTop;
          if (computedVertical === 1 /* UP */) {
              if (scrollTop > 0) {
                  verticalScrollDirection = 1 /* UP */;
              }
          }
          else if (element.scrollHeight - scrollTop > element.clientHeight) {
              verticalScrollDirection = 2 /* DOWN */;
          }
      }
      if (computedHorizontal) {
          /** @type {?} */
          const scrollLeft = element.scrollLeft;
          if (computedHorizontal === 1 /* LEFT */) {
              if (scrollLeft > 0) {
                  horizontalScrollDirection = 1 /* LEFT */;
              }
          }
          else if (element.scrollWidth - scrollLeft > element.clientWidth) {
              horizontalScrollDirection = 2 /* RIGHT */;
          }
      }
      return [verticalScrollDirection, horizontalScrollDirection];
  }
  /**
   * Gets the shadow root of an element, if any.
   * @param {?} element
   * @return {?}
   */
  function getShadowRoot(element) {
      if (_supportsShadowDom()) {
          /** @type {?} */
          const rootNode = element.getRootNode ? element.getRootNode() : null;
          if (rootNode instanceof ShadowRoot) {
              return rootNode;
          }
      }
      return null;
  }
  //# sourceMappingURL=drop-list-ref.js.map