import React = require('react'); import { action } from 'mobx'; import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import './CollectionSchemaView.scss'; export interface MovableColumnProps { columnRenderer: React.ReactNode; columnValue: SchemaHeaderField; allColumns: SchemaHeaderField[]; reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void; ScreenToLocalTransform: () => Transform; } export class MovableColumn extends React.Component { // The header of the column private _header?: React.RefObject = React.createRef(); // The container of the function that is responsible for moving the column over to a new plac private _colDropDisposer?: DragManager.DragDropDisposer; // initial column position private _startDragPosition: { x: number; y: number } = { x: 0, y: 0 }; // sensitivity to being dragged, in pixels private _sensitivity: number = 16; // Column reference ID private _dragRef: React.RefObject = React.createRef(); onPointerEnter = (e: React.PointerEvent): void => { // if the column is left-clicked and it is being dragged if (e.buttons === 1 && SnappingManager.GetIsDragging()) { this._header!.current!.className = 'collectionSchema-col-wrapper'; document.addEventListener('pointermove', this.onDragMove, true); } }; onPointerLeave = (e: React.PointerEvent): void => { this._header!.current!.className = 'collectionSchema-col-wrapper'; document.removeEventListener('pointermove', this.onDragMove, true); !e.buttons && document.removeEventListener('pointermove', this.onPointerMove); }; onDragMove = (e: PointerEvent): void => { // only take into account the horizonal direction when a column is dragged const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); const rect = this._header!.current!.getBoundingClientRect(); // Now store the point at the top center of the column when it was in its original position const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top); // to be compared with its new horizontal position const before = x[0] < bounds[0]; this._header!.current!.className = 'collectionSchema-col-wrapper'; if (before) this._header!.current!.className += ' col-before'; if (!before) this._header!.current!.className += ' col-after'; e.stopPropagation(); }; createColDropTarget = (ele: HTMLDivElement) => { this._colDropDisposer?.(); if (ele) { this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this)); } }; colDrop = (e: Event, de: DragManager.DropEvent) => { document.removeEventListener('pointermove', this.onDragMove, true); // we only care about whether the column is shifted to the side const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); // get the dimensions of the smallest rectangle that bounds the header const rect = this._header!.current!.getBoundingClientRect(); const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top); // get whether the column was dragged before or after where it is now const before = x[0] < bounds[0]; const colDragData = de.complete.columnDragData; // if there is colDragData, which happen when the drag is complete, reorder the columns according to the established variables if (colDragData) { e.stopPropagation(); this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns); return true; } return false; }; onPointerMove = (e: PointerEvent) => { const onRowMove = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); document.removeEventListener('pointermove', onRowMove); document.removeEventListener('pointerup', onRowUp); const dragData = new DragManager.ColumnDragData(this.props.columnValue); DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y); }; const onRowUp = (): void => { document.removeEventListener('pointermove', onRowMove); document.removeEventListener('pointerup', onRowUp); }; // if the left mouse button is the one being held if (e.buttons === 1) { const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y); // If the movemnt of the drag exceeds the sensitivity value if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) { document.removeEventListener('pointermove', this.onPointerMove); e.stopPropagation(); document.addEventListener('pointermove', onRowMove); document.addEventListener('pointerup', onRowUp); } } }; onPointerUp = (e: React.PointerEvent) => { document.removeEventListener('pointermove', this.onPointerMove); }; @action onPointerDown = (e: React.PointerEvent, ref: React.RefObject) => { this._dragRef = ref; const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY); // If the cell thing dragged is not being edited if (!(e.target as any)?.tagName.includes('INPUT')) { this._startDragPosition = { x: dx, y: dy }; document.addEventListener('pointermove', this.onPointerMove); } }; render() { const reference = React.createRef(); return (
this.onPointerDown(e, reference)} onPointerUp={this.onPointerUp}> {this.props.columnRenderer}
); } }