import React = require("react"); import { ReactTableDefaults, TableCellRenderer, ComponentPropsGetterR, ComponentPropsGetter0, RowInfo } from "react-table"; import "./CollectionSchemaView.scss"; import { Transform } from "../../util/Transform"; import { Doc } from "../../../new_fields/Doc"; import { DragManager, SetupDrag } from "../../util/DragManager"; import { SelectionManager } from "../../util/SelectionManager"; import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { ContextMenu } from "../ContextMenu"; import { action } from "mobx"; import { library } from '@fortawesome/fontawesome-svg-core'; import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { DocumentManager } from "../../util/DocumentManager"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; library.add(faGripVertical, faTrash); export interface MovableColumnProps { columnRenderer: TableCellRenderer; columnValue: SchemaHeaderField; allColumns: SchemaHeaderField[]; reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void; ScreenToLocalTransform: () => Transform; } export class MovableColumn extends React.Component { private _header?: React.RefObject = React.createRef(); private _colDropDisposer?: DragManager.DragDropDisposer; private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 }; private _sensitivity: number = 16; private _dragRef: React.RefObject = React.createRef(); onPointerEnter = (e: React.PointerEvent): void => { if (e.buttons === 1 && SelectionManager.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); document.removeEventListener("pointermove", this.onPointerMove); } onDragMove = (e: PointerEvent): void => { let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top); let 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 && this._colDropDisposer(); if (ele) { this._colDropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.colDrop.bind(this) } }); } } colDrop = (e: Event, de: DragManager.DropEvent) => { document.removeEventListener("pointermove", this.onDragMove, true); let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top); let before = x[0] < bounds[0]; if (de.data instanceof DragManager.ColumnDragData) { this.props.reorderColumns(de.data.colKey, this.props.columnValue, before, this.props.allColumns); return true; } return false; } onPointerMove = (e: PointerEvent) => { let onRowMove = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); let dragData = new DragManager.ColumnDragData(this.props.columnValue); DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y); }; let onRowUp = (): void => { document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); }; if (e.buttons === 1) { let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y); 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; let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY); this._startDragPosition = { x: dx, y: dy }; document.addEventListener("pointermove", this.onPointerMove); } render() { let reference = React.createRef(); return (
this.onPointerDown(e, reference)} onPointerUp={this.onPointerUp}> {this.props.columnRenderer}
); } } export interface MovableRowProps { rowInfo: RowInfo; ScreenToLocalTransform: () => Transform; addDoc: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; removeDoc: (doc: Doc) => boolean; rowFocused: boolean; textWrapRow: (doc: Doc) => void; rowWrapped: boolean; } export class MovableRow extends React.Component { private _header?: React.RefObject = React.createRef(); private _rowDropDisposer?: DragManager.DragDropDisposer; onPointerEnter = (e: React.PointerEvent): void => { if (e.buttons === 1 && SelectionManager.GetIsDragging()) { this._header!.current!.className = "collectionSchema-row-wrapper"; document.addEventListener("pointermove", this.onDragMove, true); } } onPointerLeave = (e: React.PointerEvent): void => { this._header!.current!.className = "collectionSchema-row-wrapper"; document.removeEventListener("pointermove", this.onDragMove, true); } onDragMove = (e: PointerEvent): void => { let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; this._header!.current!.className = "collectionSchema-row-wrapper"; if (before) this._header!.current!.className += " row-above"; if (!before) this._header!.current!.className += " row-below"; e.stopPropagation(); } createRowDropTarget = (ele: HTMLDivElement) => { this._rowDropDisposer && this._rowDropDisposer(); if (ele) { this._rowDropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.rowDrop.bind(this) } }); } } rowDrop = (e: Event, de: DragManager.DropEvent) => { const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc)); if (!rowDoc) return false; let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; if (de.data instanceof DragManager.DocumentDragData) { e.stopPropagation(); if (de.data.draggedDocuments[0] === rowDoc) return true; let addDocument = (doc: Doc) => this.props.addDoc(doc, rowDoc, before); let movedDocs = de.data.draggedDocuments; return (de.data.dropAction || de.data.userDropAction) ? de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false) : (de.data.moveDocument) ? movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, rowDoc, addDocument) || added, false) : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false); } return false; } onRowContextMenu = (e: React.MouseEvent): void => { let description = this.props.rowWrapped ? "Unwrap text on row" : "Text wrap row"; ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: "file-pdf" }); } @undoBatch @action move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => { let targetView = DocumentManager.Instance.getDocumentView(target); if (targetView && targetView.props.ContainingCollectionDoc) { return doc !== target && doc !== targetView.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc); } return doc !== target && this.props.removeDoc(doc) && addDoc(doc); } render() { const { children = null, rowInfo } = this.props; if (!rowInfo) { return {children}; } const { original } = rowInfo; const doc = FieldValue(Cast(original, Doc)); if (!doc) return <>; let reference = React.createRef(); let onItemDown = SetupDrag(reference, () => doc, this.move); let className = "collectionSchema-row"; if (this.props.rowFocused) className += " row-focused"; if (this.props.rowWrapped) className += " row-wrapped"; return (
this.props.removeDoc(this.props.rowInfo.original)}>
{children}
); } }