azure-devops-ui
Version:
React components for building web UI in Azure DevOps
169 lines (168 loc) • 7.78 kB
JavaScript
import "../../CommonImports";
import "../../Core/core.css";
import "./DropdownList.css";
import "./List.css";
import "./ListDropIndicator.css";
import * as React from "react";
import { getPointByEventType } from '../../Util';
import { DragDropEffect } from '../../Utilities/DragDrop';
import { cellFromEvent } from "./List";
import { ListDropIndicator } from "./ListDropIndicator";
import { ListDropIndicatorPosition } from "./ListDropIndicator.Props";
/**
* A behavior that turns a list into the target of a drag and drop operation. Note - this
* behavior should only be used if your list is intended to _only_ be a drop target. Please use
* the ListDragDropBehavior instead if the list is also a drag source, as that is the only way
* to get keyboard drag and drop support within your list.
*/
export class ListDropTargetBehavior {
constructor(options) {
this.initialize = (props, dragDroppableUI, eventDispatch) => {
this.dragDroppableUI = dragDroppableUI;
this.eventDispatch = eventDispatch;
this.eventDispatch.addEventListener("dragenter", this.onDragEnter);
this.eventDispatch.addEventListener("dragexit", this.onDragExit);
this.eventDispatch.addEventListener("dragover", this.onDragOver);
this.eventDispatch.addEventListener("drop", this.onDrop);
this.itemProvider = props.itemProvider;
this.indicatorName = this.options.isTree ? "tree-drop-indicator" : "drop-indicator";
};
this.onDragEnter = (event) => {
if (!this.handlesType(event)) {
return;
}
if (this.options.onDragEnter) {
this.options.onDragEnter(event);
}
else {
event.detail.dataTransfer.dropEffect = DragDropEffect.move;
}
};
this.onDragExit = (event) => {
if (!this.handlesType(event)) {
return;
}
if (this.options.onDragExit) {
this.options.onDragExit(event);
}
this.dragDroppableUI.removeOverlay(this.indicatorName);
};
this.onDragOver = (event) => {
if (!this.handlesType(event)) {
return;
}
let index = this.calculateIndex(event);
const dragIndex = event.detail.dataTransfer.secondaryData.index;
const listId = event.detail.dataTransfer.secondaryData.sourceId;
let resultFromDragOver;
if (index >= 0 && (index !== dragIndex || listId !== this.options.id || this.options.isTree)) {
if (this.options.onDragOver) {
resultFromDragOver = this.options.onDragOver(event, {
index: this.listIndicatorPosition === ListDropIndicatorPosition.bottom ? index + 1 : index
});
if (typeof (resultFromDragOver) === "number") {
index = resultFromDragOver;
}
}
else {
event.detail.dataTransfer.dropEffect = DragDropEffect.move;
}
}
else {
event.detail.dataTransfer.dropEffect = DragDropEffect.none;
}
if (event.detail.dataTransfer.dropEffect === DragDropEffect.none) {
this.dragDroppableUI.removeOverlay(this.indicatorName);
}
else {
if (typeof (resultFromDragOver) === "number") {
this.listIndicatorPosition = ListDropIndicatorPosition.bottom;
}
this.dragDroppableUI.addOverlay(this.indicatorName, index, this.renderDropIndicator);
}
};
this.onDrop = (event) => {
if (!this.handlesType(event)) {
return;
}
const index = this.calculateIndex(event);
const dragIndex = event.detail.dataTransfer.secondaryData.index;
const listId = event.detail.dataTransfer.secondaryData.sourceId;
if (index >= 0 && (index !== dragIndex || listId !== this.options.id) && this.options.onDrop) {
this.options.onDrop(event, { index: this.listIndicatorPosition === ListDropIndicatorPosition.bottom ? index + 1 : index });
}
this.dragDroppableUI.removeOverlay(this.indicatorName);
};
this.renderDropIndicator = (props) => {
return this.options.isTree ? (React.createElement("div", { className: "bolt-list-tree-drop-target flex-grow" })) : (React.createElement(ListDropIndicator, { position: this.listIndicatorPosition }));
};
this.options = options;
}
componentDidUpdate(props) {
this.itemProvider = props.itemProvider;
}
componentWillUnmount() {
var _a, _b, _c;
(_a = this.eventDispatch) === null || _a === void 0 ? void 0 : _a.removeEventListener("dragenter", this.onDragEnter);
(_b = this.eventDispatch) === null || _b === void 0 ? void 0 : _b.removeEventListener("dragexit", this.onDragExit);
(_c = this.eventDispatch) === null || _c === void 0 ? void 0 : _c.removeEventListener("dragover", this.onDragOver);
}
showIndicator(index, position) {
if (position) {
this.listIndicatorPosition = position;
}
this.dragDroppableUI.addOverlay(this.indicatorName, index, this.renderDropIndicator);
}
hideIndicator() {
this.dragDroppableUI.removeOverlay(this.indicatorName);
}
setListIndicatorPosition(listIndicatorPosition) {
this.listIndicatorPosition = listIndicatorPosition;
}
calculateIndex(event) {
const cell = cellFromEvent(event);
let index = cell.rowIndex;
if (this.options.isTree) {
return index;
}
if (cell.rowElement && event.detail.dataTransfer.secondaryData) {
const dragIndex = event.detail.dataTransfer.secondaryData.index;
const listId = event.detail.dataTransfer.secondaryData.sourceId;
const nativeEvent = event.detail.nativeEvent;
const rowRect = cell.rowElement.getBoundingClientRect();
const point = getPointByEventType(nativeEvent);
const topHalfOfRow = point ? point.y < rowRect.height / 2 + rowRect.top : index < dragIndex;
if (this.options.id !== listId) {
this.listIndicatorPosition = ListDropIndicatorPosition.top;
if (!topHalfOfRow) {
index++;
}
if (index >= this.itemProvider.length) {
this.listIndicatorPosition = ListDropIndicatorPosition.bottom;
index--;
}
}
else {
if (index < dragIndex) {
this.listIndicatorPosition = ListDropIndicatorPosition.top;
if (!topHalfOfRow) {
index++;
}
}
else if (index > dragIndex) {
this.listIndicatorPosition = ListDropIndicatorPosition.bottom;
if (topHalfOfRow) {
index--;
}
}
// No-op if index === dragIndex
}
}
return index;
}
handlesType(event) {
var _a;
const type = ((_a = event.detail.dataTransfer) === null || _a === void 0 ? void 0 : _a.type) || "";
return this.options.allowedTypes.indexOf(type) !== -1;
}
}