UNPKG

azure-devops-ui

Version:

React components for building web UI in Azure DevOps

164 lines (163 loc) 8 kB
import "../../CommonImports"; import "../../Core/core.css"; import "./DropdownList.css"; import "./List.css"; import "./ListDropIndicator.css"; import * as React from "react"; import { beginDragOperation, DragDropEffect, DragImage } from '../../Utilities/DragDrop'; import { cellFromEvent } from "./List"; // TODO: FF cleanup target ('dnd-behaviors-parent-autoscroll-enabled' document body class): const DragAndDropParentAutocrollClassName = 'dnd-behaviors-parent-autoscroll-enabled'; /** * A behavior that turns a list into the source of a drag and drop operation. Note - this * behavior should only be used if your list is intended to _only_ be a drag source. Please use * the ListDragDropBehavior instead if the list is also a drop target, as that is the only way * to get keyboard drag and drop support within your list. */ export class ListDragSourceBehavior { constructor(options) { this._scrollableParent = null; this._isScrollableParentCached = false; this.initialize = (props, dragDroppableUI, eventDispatch) => { this.dragDroppableUI = dragDroppableUI; this.eventDispatch = eventDispatch; this.eventDispatch.addEventListener("pointerdown", this.onPointerDown); this.eventDispatch.addEventListener("dragstart", this.onDragStart); this.eventDispatch.addEventListener("dragend", this.onDragEnd); this.eventDispatch.addEventListener("dragover", this.onDragging); this.itemProvider = props.itemProvider; this._scrollableParent = null; this._isScrollableParentCached = false; }; this.onDragging = (event) => { if (this.options.onDragging) { this.options.onDragging(event); return; } this.onDraggingDefault(); }; this.onDraggingDefault = () => { var _a, _b, _c; // TODO: FF cleanup target ('dnd-behaviors-parent-autoscroll-enabled' document body class): const isParentAutoscrollEnabled = document.body.classList.contains(DragAndDropParentAutocrollClassName); const scrollableElement = !isParentAutoscrollEnabled ? (_b = (_a = this.dragDroppableUI) === null || _a === void 0 ? void 0 : _a.currentElement) === null || _b === void 0 ? void 0 : _b.current : this.getScrollableContainer(); if (!scrollableElement) { return; } ; const { top, bottom } = scrollableElement.getBoundingClientRect(); const speedRate = 20; const edgeRate = 0.05; const edgeSize = scrollableElement.offsetHeight * edgeRate; const viewportY = (_c = this.operation) === null || _c === void 0 ? void 0 : _c.value.y; if (!viewportY) { return; } const isInBottomEdge = bottom - edgeSize < viewportY; const isInTopEdge = top + edgeSize > viewportY; const canScrollUp = scrollableElement.scrollTop > 0; const canScrollDown = scrollableElement.scrollTop + scrollableElement.offsetHeight < scrollableElement.scrollHeight; if (isInBottomEdge && canScrollDown) { scrollableElement.scrollTo({ top: scrollableElement.scrollTop + speedRate }); } else if (isInTopEdge && canScrollUp) { scrollableElement.scrollTo({ top: scrollableElement.scrollTop - speedRate }); } }; this.onDragEnd = (event) => { const index = cellFromEvent(event).rowIndex; if (index >= 0 && this.options.onDragEnd) { this.options.onDragEnd(event); } this.dragDroppableUI.removeOverlay("drag-source-item"); this.dragImageData = undefined; }; this.onDragStart = (event) => { if (event.detail.dataTransfer) { const index = cellFromEvent(event).rowIndex; if (index >= 0) { if (this.options.onDragStart) { this.options.onDragStart(event); } if (event.detail.dataTransfer.effectAllowed !== DragDropEffect.none) { this.dragDroppableUI.addOverlay("drag-source-item", index, this.renderDragSourceItemOverlay); if (this.dragImageData === undefined) { this.dragImageData = { image: this.options.renderDragImage(event) }; } } } } else { event.stopPropagation(); event.preventDefault(); } }; this.onPointerDown = (event) => { if (event.button === 0) { this.beginDrag(event); } }; this.renderDragSourceItemOverlay = (props) => { return (React.createElement(React.Fragment, null, React.createElement("div", { className: "bolt-list-drag-source-item flex-grow" }), this.operation && this.dragImageData && React.createElement(DragImage, { operation: this.operation }, this.dragImageData.image))); }; this.setDragImage = (image, xOffset, yOffset) => { this.dragImageData = { image: image, xOffset: xOffset, yOffset: yOffset }; }; this.options = options; } componentDidUpdate(props) { this.itemProvider = props.itemProvider; this._scrollableParent = null; this._isScrollableParentCached = false; } componentWillUnmount() { var _a, _b, _c, _d; (_a = this.eventDispatch) === null || _a === void 0 ? void 0 : _a.removeEventListener("pointerdown", this.onPointerDown); (_b = this.eventDispatch) === null || _b === void 0 ? void 0 : _b.removeEventListener("dragstart", this.onDragStart); (_c = this.eventDispatch) === null || _c === void 0 ? void 0 : _c.removeEventListener("dragend", this.onDragEnd); (_d = this.eventDispatch) === null || _d === void 0 ? void 0 : _d.removeEventListener("dragover", this.onDragging); } beginDrag(event) { const index = cellFromEvent(event).rowIndex; if (this.itemProvider && index >= 0) { const item = this.itemProvider.value[index]; this.operation = beginDragOperation(event, { data: item, dropEffect: DragDropEffect.none, secondaryData: { index: index, sourceId: this.options.id }, setDragImage: this.setDragImage, type: this.options.type }); } } getScrollableContainer() { var _a, _b, _c; if (this._isScrollableParentCached) { return this._scrollableParent; } this._isScrollableParentCached = true; const dragDroppableElement = (_b = (_a = this.dragDroppableUI) === null || _a === void 0 ? void 0 : _a.currentElement) === null || _b === void 0 ? void 0 : _b.current; if (!dragDroppableElement) { return null; } this._scrollableParent = this.findScrollableParent(dragDroppableElement); return (_c = this._scrollableParent) !== null && _c !== void 0 ? _c : dragDroppableElement; } findScrollableParent(element) { while (element) { const style = window.getComputedStyle(element); const overflowY = style.overflowY; if ((overflowY === 'auto' || overflowY === 'scroll') && element.scrollHeight > element.clientHeight) { return element; } element = element.parentElement; } return null; } }