From 8a1be635352177ba05845851289d1a67b4060708 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <abdullah_ahmed@brown.edu>
Date: Thu, 18 Jul 2019 19:24:58 -0400
Subject: schema cols can be moved by dragging

---
 .../CollectionSchemaMovableTableHOC.tsx            | 346 +++++++++------------
 1 file changed, 139 insertions(+), 207 deletions(-)

(limited to 'src/client/views/collections/CollectionSchemaMovableTableHOC.tsx')

diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 44a134d31..3a61881a7 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -1,111 +1,12 @@
 import React = require("react");
-import { TableProps, ReactTableDefaults, Column, TableCellRenderer, ComponentPropsGetterR, ComponentPropsGetter0 } from "react-table";
-import { ComponentType, ComponentClass } from 'react';
-import { action } from "mobx";
+import { ReactTableDefaults, TableCellRenderer, ComponentPropsGetterR, ComponentPropsGetter0 } from "react-table";
 import "./CollectionSchemaView.scss";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faBars } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 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 } from "../../../new_fields/Types";
 
-library.add(faBars);
-
-// export interface MovableSchemaProps {
-//     ScreenToLocalTransform: () => Transform;
-//     addDoc: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
-//     moveDoc: DragManager.MoveFunction;
-//     columnsValues: string[];
-//     columnsList: Column<any>[];
-//     setColumnsOrder: (columns: string[]) => void;
-//     numImmovableColumns?: number;
-// }
-
-// export default function CollectionSchemaMovableHOC<Props extends Partial<TableProps>>(WrappedComponent: ComponentType<Props>): ComponentClass<Props & MovableSchemaProps> {
-//     return class CollectionSchemaMovableSchemaHOC extends React.Component<Props & MovableSchemaProps> {
-//         constructor(props: any) {
-//             super(props);
-//         }
-
-//         reorderColumns(toMove: string, relativeTo: string, before: boolean, columnsValues: string[], setColumnsOrder: (columns: string[]) => void) {
-//             let columns = [...columnsValues];
-//             let oldIndex = columns.indexOf(toMove);
-//             let relIndex = columns.indexOf(relativeTo);
-//             let newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex;
-
-//             if (oldIndex === newIndex) return;
-
-//             columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]);
-//             setColumnsOrder(columns);
-//         }
-
-//         createColumns(columnsValues: string[], columnsList: Column<any>[], setColumnsOrder: (columnsValues: string[]) => void, ScreenToLocalTransform: () => Transform): Column<any>[] {
-//             let immovableIndex = this.props.numImmovableColumns ? columnsList.length - this.props.numImmovableColumns! : columnsList.length;
-//             return columnsList.map((col, index) => {
-//                 if (index >= immovableIndex) {
-//                     return col;
-//                 } else {
-//                     return ({ ...col, Header: MovableColumn(col.Header, columnsValues[index], columnsValues, setColumnsOrder, this.reorderColumns, ScreenToLocalTransform) });
-//                 }
-//             });
-//         }
-
-//         render() {
-//             console.log("THIS IS THE RIGHT HOC");
-//             const { ScreenToLocalTransform, addDoc, moveDoc, columnsValues, columnsList, setColumnsOrder, getTrProps, ...props } = this.props;
-//             return (
-//                 <WrappedComponent {...props as Props} ></WrappedComponent>
-//             );
-//         }
-
-//     };
-// }
-// //TrComponent={MovableRow(ScreenToLocalTransform, addDoc, moveDoc)}
-// //columns={this.createColumns(columnsValues, columnsList, setColumnsOrder, ScreenToLocalTransform)}
-
-// export  function MovableSchemaHOC<Props extends Partial<TableProps>>(WrappedComponent: ComponentType<Props>): ComponentClass<Props & MovableSchemaProps> {
-//     return class MovableSchemaHOC extends React.Component<Props & MovableSchemaProps> {
-//         constructor(props: any) {
-//             super(props);
-//         }
-
-//         createColumns(columnsValues: string[], columnsList: Column<any>[], setColumnsOrder: (columnsValues: string[]) => void, ScreenToLocalTransform: () => Transform): Column<any>[] {
-//             let immovableIndex = this.props.numImmovableColumns ? columnsList.length - this.props.numImmovableColumns! : columnsList.length;
-//             return columnsList.map((col, index) => {
-//                 if (index >= immovableIndex) {
-//                     return col;
-//                 } else {
-//                     return ({ ...col, Header: MovableColumn(col.Header, columnsValues[index], columnsValues, setColumnsOrder, this.reorderColumns, ScreenToLocalTransform) });
-//                 }
-//             });
-//         }
-
-//         reorderColumns(toMove: string, relativeTo: string, before: boolean, columnsValues: string[], setColumnsOrder: (columns: string[]) => void) {
-//             let columns = [...columnsValues];
-//             let oldIndex = columns.indexOf(toMove);
-//             let relIndex = columns.indexOf(relativeTo);
-//             let newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex;
-
-//             if (oldIndex === newIndex) return;
-
-//             columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]);
-//             setColumnsOrder(columns);
-//         }
-
-//         render() {
-//             const { ScreenToLocalTransform, addDoc, moveDoc, columnsValues, columnsList, setColumnsOrder, getTrProps, ...props } = this.props;
-//             return (
-//                 <WrappedComponent {...props as Props} columns={this.createColumns(columnsValues, columnsList, setColumnsOrder, ScreenToLocalTransform)} TrComponent={MovableRow(ScreenToLocalTransform, addDoc, moveDoc)} ></WrappedComponent>
-//             );
-//         }
-//     };
-// }
-
-
-
 
 export interface MovableColumnProps {
     columnRenderer: TableCellRenderer;
@@ -115,147 +16,178 @@ export interface MovableColumnProps {
     ScreenToLocalTransform: () => Transform;
 }
 export class MovableColumn extends React.Component<MovableColumnProps> {
-    // private _ref: React.RefObject<HTMLDivElement> = React.createRef();
+    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
+    private _colDropDisposer?: DragManager.DragDropDisposer;
 
-    onDragStart = (e: React.DragEvent<HTMLDivElement>, ref: React.RefObject<HTMLDivElement>): void => {
-        console.log("drag start");
-        e.dataTransfer.setData("column", this.props.columnValue);
+    onPointerEnter = (e: React.PointerEvent): void => {
+        if (e.buttons === 1 && SelectionManager.GetIsDragging()) {
+            this._header!.current!.className = "collectionSchema-col-wrapper";
+            document.addEventListener("pointermove", this.onDragMove, true);
+        }
     }
-
-    onDragOver = (e: React.DragEvent<HTMLDivElement>,ref: React.RefObject<HTMLDivElement>): void => {
-        console.log("drag over");
+    onPointerLeave = (e: React.PointerEvent): void => {
+        this._header!.current!.className = "collectionSchema-col-wrapper";
+        document.removeEventListener("pointermove", this.onDragMove, true);
+    }
+    onDragMove = (e: PointerEvent): void => {
         let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-        let rect = ref.current!.getBoundingClientRect();
+        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];
-
-        ref.current!.className = "collectionSchema-column-header";
-        if (before) ref.current!.className += " col-before";
-        if (!before) ref.current!.className += " col-after";
-        // e.stopPropagation();
+        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();
     }
 
-    onDragLeave = (e: React.DragEvent<HTMLDivElement>, ref: React.RefObject<HTMLDivElement>): void => {
-        console.log("drag leave");
-        ref.current!.className = "collectionSchema-column-header";
-        e.stopPropagation();
+    createColDropTarget = (ele: HTMLDivElement) => {
+        this._colDropDisposer && this._colDropDisposer();
+        if (ele) {
+            this._colDropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.colDrop.bind(this) } });
+        }
     }
 
-    onDrop = (e: React.DragEvent<HTMLDivElement>,ref: React.RefObject<HTMLDivElement>): void => {
-        console.log("on drop");
-        // TODO: get column being dropped and before/after
-        let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-        let rect = ref.current!.getBoundingClientRect();
+    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;
+    }
 
-        this.props.reorderColumns(e.dataTransfer.getData("column"), this.props.columnValue, before, this.props.allColumns);
-        ref.current!.className = "collectionSchema-column-header";
+    setupDrag (ref: React.RefObject<HTMLElement>) {
+        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(ref.current!, dragData, e.x, e.y);
+        };
+        let onRowUp = (): void => {
+            document.removeEventListener("pointermove", onRowMove);
+            document.removeEventListener('pointerup', onRowUp);
+        };
+        let onItemDown = (e: React.PointerEvent) => {
+            if (e.button === 0) {
+                e.stopPropagation();
+                document.addEventListener("pointermove", onRowMove);
+                document.addEventListener("pointerup", onRowUp);
+            }
+        };
+        return onItemDown;
     }
 
+
     render() {
-        let ref: React.RefObject<HTMLDivElement> =  React.createRef();
+        let reference = React.createRef<HTMLDivElement>();
+        let onItemDown = this.setupDrag(reference);
+
         return (
-             <div className="collectionSchema-column-header" ref={ref} draggable={true} 
-             onPointerDown={() => console.log("pointer down")} onPointerEnter={() => console.log("pointer enter")} onPointerOut={() => console.log("pointer exit")}
-                 onDragStart={e => this.onDragStart(e, ref)} onDragOver={e => this.onDragOver(e, ref)} onDragLeave={e => this.onDragLeave(e, ref)} onDrop={e => this.onDrop(e, ref)}>
-                 {this.props.columnRenderer}
+            <div className="collectionSchema-col" ref={this.createColDropTarget}>
+                <div className="collectionSchema-col-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+                    <div className="col-dragger" ref={reference} onPointerDown={onItemDown}>
+                        {this.props.columnRenderer}
+                    </div>
+                </div>
             </div>
         );
     }
 }
 
-// export function MovableColumn(columnRenderer: TableCellRenderer, columnValue: string, allColumns: string[],
-//     reorderColumns: (toMove: string, relativeTo: string, before: boolean, columns: string[]) => void,
-//     ScreenToLocalTransform: () => Transform) {
-//     return ;
-// }
+export interface MovableRowProps {
+    ScreenToLocalTransform: () => Transform;
+    addDoc: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
+    moveDoc: DragManager.MoveFunction;
+}
 
-export function MovableRow(ScreenToLocalTransform: () => Transform, addDoc: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean, moveDoc: DragManager.MoveFunction) {
-    return class MovableRow extends React.Component {
-        private _header?: React.RefObject<HTMLDivElement> = React.createRef();
-        private _treedropDisposer?: DragManager.DragDropDisposer;
+export class MovableRow extends React.Component<MovableRowProps> {
+    private _header?: React.RefObject<HTMLDivElement> = 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 = ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-            let rect = this._header!.current!.getBoundingClientRect();
-            let bounds = ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
-            let before = x[1] < bounds[1];
+    onPointerEnter = (e: React.PointerEvent): void => {
+        if (e.buttons === 1 && SelectionManager.GetIsDragging()) {
             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();
+            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();
+    }
 
-        createTreeDropTarget = (ele: HTMLDivElement) => {
-            this._treedropDisposer && this._treedropDisposer();
-            if (ele) {
-                this._treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } });
-            }
+    createRowDropTarget = (ele: HTMLDivElement) => {
+        this._rowDropDisposer && this._rowDropDisposer();
+        if (ele) {
+            this._rowDropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.rowDrop.bind(this) } });
         }
+    }
 
-        treeDrop = (e: Event, de: DragManager.DropEvent) => {
-            const { children = null, rowInfo } = this.props;
-            if (!rowInfo) return false;
+    rowDrop = (e: Event, de: DragManager.DropEvent) => {
+        const { children = null, rowInfo } = this.props;
+        if (!rowInfo) return false;
 
-            const { original } = rowInfo;
-            const rowDoc = FieldValue(Cast(original, Doc));
-            if (!rowDoc) return false;
+        const { original } = rowInfo;
+        const rowDoc = FieldValue(Cast(original, Doc));
+        if (!rowDoc) return false;
 
-            let x = ScreenToLocalTransform().transformPoint(de.x, de.y);
-            let rect = this._header!.current!.getBoundingClientRect();
-            let bounds = 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) => addDoc(doc, rowDoc, before);
-                let movedDocs = de.data.draggedDocuments; //(de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
-                return (de.data.dropAction || de.data.userDropAction) ?
-                    de.data.droppedDocuments.reduce((added: boolean, d) => 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) => addDoc(d, rowDoc, before), false);
-            }
-            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; //(de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
+            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;
+    }
 
-        render() {
-            const { children = null, rowInfo } = this.props;
-            if (!rowInfo) {
-                console.log("no rowinfo");
-                return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
-            }
+    render() {
+        const { children = null, rowInfo } = this.props;
+        if (!rowInfo) {
+            return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
+        }
 
-            const { original } = rowInfo;
-            const doc = FieldValue(Cast(original, Doc));
-            if (!doc) return <></>;
+        const { original } = rowInfo;
+        const doc = FieldValue(Cast(original, Doc));
+        if (!doc) return <></>;
 
-            let reference = React.createRef<HTMLDivElement>();
-            let onItemDown = SetupDrag(reference, () => doc, moveDoc);
+        let reference = React.createRef<HTMLDivElement>();
+        let onItemDown = SetupDrag(reference, () => doc, this.props.moveDoc);
 
-            return (
-                <div className="collectionSchema-row" ref={this.createTreeDropTarget}>
-                    <div className="collectionSchema-row-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                        <div className="row-dragger" ref={reference} onPointerDown={onItemDown}>
-                            <ReactTableDefaults.TrComponent>
-                                {children}
-                            </ReactTableDefaults.TrComponent>
-                        </div>
+        return (
+            <div className="collectionSchema-row" ref={this.createRowDropTarget}>
+                <div className="collectionSchema-row-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+                    <div className="row-dragger" ref={reference} onPointerDown={onItemDown}>
+                        <ReactTableDefaults.TrComponent>
+                            {children}
+                        </ReactTableDefaults.TrComponent>
                     </div>
                 </div>
-            );
-        }
-    };
-}
-
+            </div>
+        );
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

