UNPKG

@recruitler/drag-drop

Version:

A forked version of Angular/'s drag-drop CDK.

750 lines 105 kB
/** * @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 { _getShadowRoot } from '@angular/cdk/platform'; import { Subject, Subscription, interval, animationFrameScheduler } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { isPointerNearClientRect, isInsideClientRect } from './dom/client-rect'; import { ParentPositionTracker } from './dom/parent-position-tracker'; import { SingleAxisSortStrategy } from './sorting/single-axis-sort-strategy'; /** * Proximity, as a ratio to width/height, at which a * dragged item will affect the drop container. */ 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. */ const SCROLL_PROXIMITY_THRESHOLD = 0.05; /** * Reference to a drop list. Used to manipulate or dispose of the container. */ export class DropListRef { _dragDropRegistry; _ngZone; _viewportRuler; /** Element that the drop list is attached to. */ element; /** Whether starting a dragging sequence from this container is disabled. */ disabled = false; /** Whether sorting items within the list is disabled. */ sortingDisabled = false; /** Locks the position of the draggable elements inside the container along the specified axis. */ lockAxis; /** * Whether auto-scrolling the view when the user * moves their pointer close to the edges is disabled. */ autoScrollDisabled = false; /** Number of pixels to scroll for each frame when auto-scrolling an element. */ autoScrollStep = 2; // R2M start nestEnabled = true; nestThreshold = 0.5; dropGutterSize = 0; // R2M end /** * Function that is used to determine whether an item * is allowed to be moved into a drop container. */ enterPredicate = () => true; /** Function that is used to determine whether an item can be sorted into a particular index. */ sortPredicate = () => true; /** Emits right before dragging has started. */ beforeStarted = new Subject(); /** * Emits when the user has moved a new drag item into this container. */ entered = new Subject(); /** * Emits when the user removes an item from the container * by dragging it into another container. */ exited = new Subject(); // R2M start /** * Emits when the user has moved a new drag item into this container. */ nested = new Subject(); // R2M end /** Emits when the user drops an item inside the container. */ dropped = new Subject(); /** Emits as the user is swapping items while actively dragging. */ sorted = new Subject(); /** Emits when a dragging sequence is started in a list connected to the current one. */ receivingStarted = new Subject(); /** Emits when a dragging sequence is stopped from a list connected to the current one. */ receivingStopped = new Subject(); /** Arbitrary data that can be attached to the drop list. */ data; /** Whether an item in the list is being dragged. */ _isDragging = false; /** Keeps track of the positions of any parent scrollable elements. */ _parentPositions; /** Strategy being used to sort items within the list. */ _sortStrategy; /** Cached `ClientRect` of the drop list. */ _clientRect; /** Draggable items in the container. */ _draggables = []; /** Drop lists that are connected to the current one. */ _siblings = []; /** Connected siblings that currently have a dragged item. */ _activeSiblings = new Set(); /** Subscription to the window being scrolled. */ _viewportScrollSubscription = Subscription.EMPTY; /** Vertical direction in which the list is currently scrolling. */ _verticalScrollDirection = 0 /* AutoScrollVerticalDirection.NONE */; /** Horizontal direction in which the list is currently scrolling. */ _horizontalScrollDirection = 0 /* AutoScrollHorizontalDirection.NONE */; /** Node that is being auto-scrolled. */ _scrollNode; /** Used to signal to the current auto-scroll sequence when to stop. */ _stopScrollTimers = new Subject(); /** Shadow root of the current element. Necessary for `elementFromPoint` to resolve correctly. */ _cachedShadowRoot = null; /** Reference to the document. */ _document; /** Elements that can be scrolled while the user is dragging. */ _scrollableElements; /** Initial value for the element's `scroll-snap-type` style. */ _initialScrollSnap; constructor(element, _dragDropRegistry, _document, _ngZone, _viewportRuler) { this._dragDropRegistry = _dragDropRegistry; this._ngZone = _ngZone; this._viewportRuler = _viewportRuler; this.element = coerceElement(element); this._document = _document; this.withScrollableParents([this.element]); _dragDropRegistry.registerDropContainer(this); this._parentPositions = new ParentPositionTracker(_document); this._sortStrategy = new SingleAxisSortStrategy(this.element, _dragDropRegistry); this._sortStrategy.withSortPredicate((index, item) => this.sortPredicate(index, item, this)); } /** Removes the drop list functionality from the DOM element. */ dispose() { this._stopScrolling(); this._stopScrollTimers.complete(); this._viewportScrollSubscription.unsubscribe(); this.beforeStarted.complete(); this.entered.complete(); // R2M start this.nested.complete(); // R2M end this.exited.complete(); this.dropped.complete(); this.sorted.complete(); this.receivingStarted.complete(); this.receivingStopped.complete(); this._activeSiblings.clear(); this._scrollNode = null; this._parentPositions.clear(); this._dragDropRegistry.removeDropContainer(this); } /** Whether an item from this list is currently being dragged. */ isDragging() { return this._isDragging; } /** Starts dragging an item. */ start() { this._draggingStarted(); this._notifyReceivingSiblings(); } /** * Attempts to move 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. * @param index Index at which the item entered. If omitted, the container will try to figure it * out automatically. */ enter(item, pointerX, pointerY, index) { this._draggingStarted(); // If sorting is disabled, we want the item to return to its starting // position if the user is returning it to its initial container. if (index == null && this.sortingDisabled) { index = this._draggables.indexOf(item); } this._sortStrategy.enter(item, pointerX, pointerY, index); // Note that this usually happens inside `_draggingStarted` as well, but the dimensions // can change when the sort strategy moves the item around inside `enter`. this._cacheParentPositions(); // Notify siblings at the end so that the item has been inserted into the `activeDraggables`. this._notifyReceivingSiblings(); 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. */ exit(item) { this._reset(); this.exited.next({ item, container: this }); } // R2M start _unnestIfNecessary(item) { if (item.nestInfo && item.nestInfo.nestIndex !== -1) { let targetItem = this._draggables[item.nestInfo.nestIndex]; this._sortStrategy.unnest(item, this._sortStrategy.getItemIndex(targetItem)); item.nestInfo = null; } } // R2M end /** * Drops an item into this container. * @param item Item being dropped into the container. * @param currentIndex Index at which the item should be inserted. * @param previousIndex Index of the item when dragging started. * @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. * @param event Event that triggered the dropping sequence. * * @breaking-change 15.0.0 `previousIndex` and `event` parameters to become required. */ drop(item, currentIndex, previousIndex, previousContainer, isPointerOverContainer, distance, dropPoint, event = {}, nestInfo // added by R2M for nesting item ) { this._reset(); this.dropped.next({ item, currentIndex, previousIndex, container: this, previousContainer, isPointerOverContainer, distance, dropPoint, event, nestInfo }); } /** * Sets the draggable items that are a part of this list. * @param items Items that are a part of this list. */ withItems(items) { const previousItems = this._draggables; this._draggables = items; items.forEach((item) => item._withDropContainer(this)); if (this.isDragging()) { const draggedItems = previousItems.filter((item) => item.isDragging()); // If all of the items being dragged were removed // from the list, abort the current drag sequence. if (draggedItems.every((item) => items.indexOf(item) === -1)) { this._reset(); } else { this._sortStrategy.withItems(this._draggables); } } return this; } /** Sets the layout direction of the drop list. */ withDirection(direction) { this._sortStrategy.direction = direction; return 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. * @param connectedTo Other containers that the current containers should be connected to. */ connectedTo(connectedTo) { this._siblings = connectedTo.slice(); return this; } /** * Sets the orientation of the container. * @param orientation New orientation for the container. */ withOrientation(orientation) { // TODO(crisbeto): eventually we should be constructing the new sort strategy here based on // the new orientation. For now we can assume that it'll always be `SingleAxisSortStrategy`. this._sortStrategy.orientation = orientation; return this; } /** * Sets which parent elements are can be scrolled while the user is dragging. * @param elements Elements that can be scrolled. */ withScrollableParents(elements) { const element = coerceElement(this.element); // We always allow the current element to be scrollable // so we need to ensure that it's in the array. this._scrollableElements = elements.indexOf(element) === -1 ? [element, ...elements] : elements.slice(); return this; } /** Gets the scrollable parents that are registered with this drop container. */ getScrollableParents() { return this._scrollableElements; } /** * Figures out the index of an item in the container. * @param item Item whose index should be determined. */ getItemIndex(item) { return this._isDragging ? this._sortStrategy.getItemIndex(item) : this._draggables.indexOf(item); } /** * Whether the list is able to receive the item that * is currently being dragged inside a connected drop list. */ isReceiving() { return this._activeSiblings.size > 0; } /** * create DropList inside an element of a list item if nessary, * nest DragRef item into this created DropList * @param item * @param nestIndex */ _nestItem(item, nestIndex) { let targetItem = this._draggables[nestIndex]; this._sortStrategy.nest(item, this._sortStrategy.getItemIndex(targetItem), item.nestInfo ? item.nestInfo.nestIndex : -1); this._animatePlaceholder(item); this.nested.next({ item, container: this, nestIndex: nestIndex, }); // R2M end } _isInGutter(item, pointerX, pointerY) { let currentIndex = this._sortStrategy.getItemIndex(item); let itemRects = this._sortStrategy.getItemBoundaries(); let index = itemRects.findIndex((rect, index) => { return index !== currentIndex && Math.floor(rect.top) <= pointerY && Math.floor(rect.bottom) >= pointerY; }); if (this._siblings.indexOf(this) < this.dropGutterSize && index == -1) { const firstDragRect = itemRects[0]; if (firstDragRect) { if (pointerY > firstDragRect.top) return true; } } return false; } /** * Checks if an item is inside name field of leaf node of the list * returns the index of list into which DragRef item will be nested * @param item Item to be nested. * @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. */ _getNestIndex(item, pointerX, pointerY, previewRect, pointerDelta) { // threshold value of left position of pointer for nesting item. // threshold * left + (1 - threshold) * right < pointer's left ===> NESTING!!!! let thresholdNest = 0.5; // Don't nest the item if nesting is disabled or it's out of range. if (!this.nestEnabled || !this._clientRect || !isPointerNearClientRect(this._clientRect, DROP_PROXIMITY_THRESHOLD, pointerX, pointerY)) { return -1; } let currentIndex = this._sortStrategy.getItemIndex(item); // let isVertical = (this._sortStrategy as SingleAxisSortStrategy<DragRef>).orientation === 'vertical'; let isVertical = true; let itemRects = this._sortStrategy.getItemBoundaries(); let index = isVertical ? itemRects.findIndex((rect, index) => { return index !== currentIndex && Math.floor(rect.top) <= pointerY && Math.floor(rect.bottom) >= pointerY; }) : itemRects.findIndex((rect, index) => { return index !== currentIndex && rect.left < pointerX && rect.right > pointerX; }); if (index == -1) return -1; if (isVertical && previewRect.x > itemRects[index].left) { return this._draggables.indexOf(this._sortStrategy.getActiveItem(index)); } if (!isVertical && pointerX < itemRects[index].left * thresholdNest + itemRects[index].right * (1 - thresholdNest)) { return index; } return -1; } /** * 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. */ _sortItem(item, pointerX, pointerY, pointerDelta) { // Don't sort the item if sorting is disabled or it's out of range. if (this.sortingDisabled || !this._clientRect || !isPointerNearClientRect(this._clientRect, DROP_PROXIMITY_THRESHOLD, pointerX, pointerY)) { return; } const result = this._sortStrategy.sort(item, pointerX, pointerY, pointerDelta); if (result) { this._animatePlaceholder(item); this.sorted.next({ previousIndex: result.previousIndex, currentIndex: result.currentIndex, container: this, item, }); } } _animatePlaceholder(item) { const afterRect = item.getPlaceholderElement().getBoundingClientRect(); if (item.animPlaceholder) { item.animPlaceholder.style.left = `${afterRect.x + window.scrollX}px`; item.animPlaceholder.style.top = `${afterRect.y + window.scrollY}px`; item.animPlaceholder.style.width = `${afterRect.width}px`; item.animPlaceholder.style.height = `${afterRect.height}px`; } } /** * Returns clientRect of this DropListRef */ getClientRect() { return this._clientRect; } /** * 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. */ _startScrollingIfNecessary(pointerX, pointerY) { if (this.autoScrollDisabled) { return; } let scrollNode; let verticalScrollDirection = 0 /* AutoScrollVerticalDirection.NONE */; let horizontalScrollDirection = 0 /* AutoScrollHorizontalDirection.NONE */; // Check whether we should start scrolling any of the parent containers. this._parentPositions.positions.forEach((position, element) => { // We have special handling for the `document` below. Also this would be // nicer with a for...of loop, but it requires changing a compiler flag. if (element === this._document || !position.clientRect || scrollNode) { return; } if (isPointerNearClientRect(position.clientRect, DROP_PROXIMITY_THRESHOLD, pointerX, pointerY)) { [verticalScrollDirection, horizontalScrollDirection] = getElementScrollDirections(element, position.clientRect, pointerX, pointerY); if (verticalScrollDirection || horizontalScrollDirection) { scrollNode = element; } } }); // Otherwise check if we can start scrolling the viewport. if (!verticalScrollDirection && !horizontalScrollDirection) { const { width, height } = this._viewportRuler.getViewportSize(); 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) { this._ngZone.runOutsideAngular(this._startScrollInterval); } else { this._stopScrolling(); } } } /** Stops any currently-running auto-scroll sequences. */ _stopScrolling() { this._stopScrollTimers.next(); } /** Starts the dragging sequence within the list. */ _draggingStarted() { const styles = coerceElement(this.element).style; this.beforeStarted.next(); this._isDragging = true; // We need to disable scroll snapping while the user is dragging, because it breaks automatic // scrolling. The browser seems to round the value based on the snapping points which means // that we can't increment/decrement the scroll position. this._initialScrollSnap = styles.msScrollSnapType || styles.scrollSnapType || ''; styles.scrollSnapType = styles.msScrollSnapType = 'none'; this._sortStrategy.start(this._draggables); this._cacheParentPositions(); this._viewportScrollSubscription.unsubscribe(); this._listenToScrollEvents(); } /** Caches the positions of the configured scrollable parents. */ _cacheParentPositions() { const element = coerceElement(this.element); this._parentPositions.cache(this._scrollableElements); // The list element is always in the `scrollableElements` // so we can take advantage of the cached `ClientRect`. this._clientRect = this._parentPositions.positions.get(element).clientRect; } /** Resets the container to its initial state. */ _reset() { this._isDragging = false; const styles = coerceElement(this.element).style; styles.scrollSnapType = styles.msScrollSnapType = this._initialScrollSnap; this._siblings.forEach((sibling) => sibling._stopReceiving(this)); this._sortStrategy.reset(); this._stopScrolling(); this._viewportScrollSubscription.unsubscribe(); this._parentPositions.clear(); } /** Starts the interval that'll auto-scroll the element. */ _startScrollInterval = () => { this._stopScrolling(); interval(0, animationFrameScheduler) .pipe(takeUntil(this._stopScrollTimers)) .subscribe(() => { const node = this._scrollNode; const scrollStep = this.autoScrollStep; if (this._verticalScrollDirection === 1 /* AutoScrollVerticalDirection.UP */) { node.scrollBy(0, -scrollStep); } else if (this._verticalScrollDirection === 2 /* AutoScrollVerticalDirection.DOWN */) { node.scrollBy(0, scrollStep); } if (this._horizontalScrollDirection === 1 /* AutoScrollHorizontalDirection.LEFT */) { node.scrollBy(-scrollStep, 0); } else if (this._horizontalScrollDirection === 2 /* AutoScrollHorizontalDirection.RIGHT */) { node.scrollBy(scrollStep, 0); } }); }; /** * 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. */ _isOverContainer(x, y) { return (this._clientRect != null && 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. */ _getSiblingContainerFromPosition(item, x, y) { // R2M start let siblingContainers = this._siblings.filter((sibling) => sibling._canReceive(item, x, y)); let topContainer = this._siblings[0]; let distMin = -99999; let nearestContainer = undefined; // find the nearest parent of this container for (let container of siblingContainers) { let top = container._clientRect?.top ? container._clientRect.top : -99999; if (top > distMin) { distMin = top; nearestContainer = container; } } return nearestContainer; // R2M end } /** * 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. */ _canReceive(item, x, y) { if (!this._clientRect || !isInsideClientRect(this._clientRect, x, y) || !this.enterPredicate(item, this)) { return false; } const elementFromPoint = this._getShadowRoot().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; } 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. */ _startReceiving(sibling, items) { const activeSiblings = this._activeSiblings; if (!activeSiblings.has(sibling) && items.every((item) => { // Note that we have to add an exception to the `enterPredicate` for items that started off // in this drop list. The drag ref has logic that allows an item to return to its initial // container, if it has left the initial container and none of the connected containers // allow it to enter. See `DragRef._updateActiveDropContainer` for more context. return (this.enterPredicate(item, this) || this._draggables.indexOf(item) > -1); })) { activeSiblings.add(sibling); this._cacheParentPositions(); this._listenToScrollEvents(); this.receivingStarted.next({ initiator: sibling, receiver: this, items, }); } } /** * Called by a connected drop list when dragging has stopped. * @param sibling Sibling whose dragging has stopped. */ _stopReceiving(sibling) { this._activeSiblings.delete(sibling); this._viewportScrollSubscription.unsubscribe(); this.receivingStopped.next({ initiator: sibling, receiver: this }); } /** * Starts listening to scroll events on the viewport. * Used for updating the internal state of the list. */ _listenToScrollEvents() { this._viewportScrollSubscription = this._dragDropRegistry .scrolled(this._getShadowRoot()) .subscribe((event) => { if (this.isDragging()) { const scrollDifference = this._parentPositions.handleScroll(event); if (scrollDifference) { this._sortStrategy.updateOnScroll(scrollDifference.top, scrollDifference.left); } } else if (this.isReceiving()) { this._cacheParentPositions(); } }); } /** * Lazily resolves and returns the shadow root of the element. We do this in a function, rather * than saving it in property directly on init, because we want to resolve it as late as possible * in order to ensure that the element has been moved into the shadow DOM. Doing it inside the * constructor might be too early if the element is inside of something like `ngFor` or `ngIf`. */ _getShadowRoot() { if (!this._cachedShadowRoot) { const shadowRoot = _getShadowRoot(coerceElement(this.element)); this._cachedShadowRoot = (shadowRoot || this._document); } return this._cachedShadowRoot; } /** Notifies any siblings that may potentially receive the item. */ _notifyReceivingSiblings() { const draggedItems = this._sortStrategy .getActiveItemsSnapshot() .filter((item) => item.isDragging()); this._siblings.forEach((sibling) => sibling._startReceiving(this, draggedItems)); } } /** * 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. */ function getVerticalScrollDirection(clientRect, pointerY) { const { top, bottom, height } = clientRect; const yThreshold = height * SCROLL_PROXIMITY_THRESHOLD; if (pointerY >= top - yThreshold && pointerY <= top + yThreshold) { return 1 /* AutoScrollVerticalDirection.UP */; } else if (pointerY >= bottom - yThreshold && pointerY <= bottom + yThreshold) { return 2 /* AutoScrollVerticalDirection.DOWN */; } return 0 /* AutoScrollVerticalDirection.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. */ function getHorizontalScrollDirection(clientRect, pointerX) { const { left, right, width } = clientRect; const xThreshold = width * SCROLL_PROXIMITY_THRESHOLD; if (pointerX >= left - xThreshold && pointerX <= left + xThreshold) { return 1 /* AutoScrollHorizontalDirection.LEFT */; } else if (pointerX >= right - xThreshold && pointerX <= right + xThreshold) { return 2 /* AutoScrollHorizontalDirection.RIGHT */; } return 0 /* AutoScrollHorizontalDirection.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. */ function getElementScrollDirections(element, clientRect, pointerX, pointerY) { const computedVertical = getVerticalScrollDirection(clientRect, pointerY); const computedHorizontal = getHorizontalScrollDirection(clientRect, pointerX); let verticalScrollDirection = 0 /* AutoScrollVerticalDirection.NONE */; let horizontalScrollDirection = 0 /* AutoScrollHorizontalDirection.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) { const scrollTop = element.scrollTop; if (computedVertical === 1 /* AutoScrollVerticalDirection.UP */) { if (scrollTop > 0) { verticalScrollDirection = 1 /* AutoScrollVerticalDirection.UP */; } } else if (element.scrollHeight - scrollTop > element.clientHeight) { verticalScrollDirection = 2 /* AutoScrollVerticalDirection.DOWN */; } } if (computedHorizontal) { const scrollLeft = element.scrollLeft; if (computedHorizontal === 1 /* AutoScrollHorizontalDirection.LEFT */) { if (scrollLeft > 0) { horizontalScrollDirection = 1 /* AutoScrollHorizontalDirection.LEFT */; } } else if (element.scrollWidth - scrollLeft > element.clientWidth) { horizontalScrollDirection = 2 /* AutoScrollHorizontalDirection.RIGHT */; } } return [verticalScrollDirection, horizontalScrollDirection]; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcC1saXN0LXJlZi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL3NvcnRhYmxlL3NyYy9saWIvZHJhZy1kcm9wL2Ryb3AtbGlzdC1yZWYudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBSUgsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBRXRELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUN2RCxPQUFPLEVBQUUsT0FBTyxFQUFFLFlBQVksRUFBRSxRQUFRLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDaEYsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRzNDLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hGLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBR3RFLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBSTdFOzs7R0FHRztBQUNILE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxDQUFDO0FBRXRDOzs7R0FHRztBQUNILE1BQU0sMEJBQTBCLEdBQUcsSUFBSSxDQUFDO0FBdUJ4Qzs7R0FFRztBQUNILE1BQU0sT0FBTyxXQUFXO0lBOEpaO0lBRUE7SUFDQTtJQWhLVixpREFBaUQ7SUFDakQsT0FBTyxDQUF3QztJQUUvQyw0RUFBNEU7SUFDNUUsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUUxQix5REFBeUQ7SUFDekQsZUFBZSxHQUFZLEtBQUssQ0FBQztJQUVqQyxrR0FBa0c7SUFDbEcsUUFBUSxDQUFZO0lBRXBCOzs7T0FHRztJQUNILGtCQUFrQixHQUFZLEtBQUssQ0FBQztJQUVwQyxnRkFBZ0Y7SUFDaEYsY0FBYyxHQUFXLENBQUMsQ0FBQztJQUUzQixZQUFZO0lBQ1osV0FBVyxHQUFZ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