import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faPalette } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, observable, computed } from "mobx"; import { observer } from "mobx-react"; import Measure from "react-measure"; import { Doc } from "../../../new_fields/Doc"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { ScriptField } from "../../../new_fields/ScriptField"; import { StrCast } from "../../../new_fields/Types"; import { numberRange } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { CompileScript } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { anchorPoints, Flyout } from "../DocumentDecorations"; import { EditableView } from "../EditableView"; import { CollectionStackingView } from "./CollectionStackingView"; import "./CollectionStackingView.scss"; library.add(faPalette); interface CMVFieldRowProps { rows: () => number; headings: () => object[]; heading: string; headingObject: SchemaHeaderField | undefined; docList: Doc[]; parent: CollectionStackingView; type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined; createDropTarget: (ele: HTMLDivElement) => void; screenToLocalTransform: () => Transform; setDocHeight: (key: string, thisHeight: number) => void; } @observer export class CollectionMasonryViewFieldRow extends React.Component { @observable private _background = "inherit"; @observable private _createAliasSelected: boolean = false; @observable private _collapsed: boolean = false; @observable private _headingsHack: number = 1; @observable private _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading; @observable private _color = this.props.headingObject ? this.props.headingObject.color : "#f1efeb"; private _dropDisposer?: DragManager.DragDropDisposer; private _headerRef: React.RefObject = React.createRef(); private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 }; private _contRef: React.RefObject = React.createRef(); private _sensitivity: number = 16; private _counter: number = 0; createRowDropRef = (ele: HTMLDivElement | null) => { this._dropDisposer && this._dropDisposer(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this)); } } getTrueHeight = () => { if (this._collapsed) { this.props.setDocHeight(this._heading, 20); } else { const rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; //+ 15 accounts for the group header const transformScale = this.props.screenToLocalTransform().Scale; const trueHeight = rawHeight * transformScale; this.props.setDocHeight(this._heading, trueHeight); } } @undoBatch rowDrop = action((e: Event, de: DragManager.DropEvent) => { this._createAliasSelected = false; if (de.complete.docDragData) { (this.props.parent.Document.dropConverter instanceof ScriptField) && this.props.parent.Document.dropConverter.script.run({ dragData: de.complete.docDragData }); const key = StrCast(this.props.parent.props.Document.sectionFilter); const castedValue = this.getValue(this._heading); de.complete.docDragData.droppedDocuments.forEach(d => d[key] = castedValue); this.props.parent.drop(e, de); e.stopPropagation(); } }); getValue = (value: string): any => { const parsed = parseInt(value); if (!isNaN(parsed)) return parsed; if (value.toLowerCase().indexOf("true") > -1) return true; if (value.toLowerCase().indexOf("false") > -1) return false; return value; } @action headingChanged = (value: string, shiftDown?: boolean) => { this._createAliasSelected = false; const key = StrCast(this.props.parent.props.Document.sectionFilter); const castedValue = this.getValue(value); if (castedValue) { if (this.props.parent.sectionHeaders) { if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) { return false; } } this.props.docList.forEach(d => d[key] = castedValue); if (this.props.headingObject) { this.props.headingObject.setHeading(castedValue.toString()); this._heading = this.props.headingObject.heading; } return true; } return false; } @action changeColumnColor = (color: string) => { this._createAliasSelected = false; if (this.props.headingObject) { this.props.headingObject.setColor(color); this._color = color; } } pointerEnteredRow = action(() => SelectionManager.GetIsDragging() && (this._background = "#b4b4b4")); @action pointerLeaveRow = () => { this._createAliasSelected = false; this._background = "inherit"; document.removeEventListener("pointermove", this.startDrag); } @action addDocument = (value: string, shiftDown?: boolean) => { this._createAliasSelected = false; const key = StrCast(this.props.parent.props.Document.sectionFilter); const newDoc = Docs.Create.TextDocument("", { _height: 18, _width: 200, title: value }); newDoc[key] = this.getValue(this.props.heading); return this.props.parent.props.addDocument(newDoc); } deleteRow = undoBatch(action(() => { this._createAliasSelected = false; const key = StrCast(this.props.parent.props.Document.sectionFilter); this.props.docList.forEach(d => d[key] = undefined); if (this.props.parent.sectionHeaders && this.props.headingObject) { const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject); this.props.parent.sectionHeaders.splice(index, 1); } })); @action collapseSection = () => { this._createAliasSelected = false; if (this.props.headingObject) { this._headingsHack++; this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed); this.toggleVisibility(); } } startDrag = (e: PointerEvent) => { const [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) { const alias = Doc.MakeAlias(this.props.parent.props.Document); const key = StrCast(this.props.parent.props.Document.sectionFilter); let value = this.getValue(this._heading); value = typeof value === "string" ? `"${value}"` : value; const script = `return doc.${key} === ${value}`; const compiled = CompileScript(script, { params: { doc: Doc.name } }); if (compiled.compiled) { alias.viewSpecScript = new ScriptField(compiled); DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY); } e.stopPropagation(); document.removeEventListener("pointermove", this.startDrag); document.removeEventListener("pointerup", this.pointerUp); } } pointerUp = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); document.removeEventListener("pointermove", this.startDrag); document.removeEventListener("pointerup", this.pointerUp); } @action headerDown = (e: React.PointerEvent) => { e.stopPropagation(); e.preventDefault(); const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY); this._startDragPosition = { x: dx, y: dy }; if (this._createAliasSelected) { document.removeEventListener("pointermove", this.startDrag); document.addEventListener("pointermove", this.startDrag); document.removeEventListener("pointerup", this.pointerUp); document.addEventListener("pointerup", this.pointerUp); } this._createAliasSelected = false; } renderColorPicker = () => { const selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb"; const pink = PastelSchemaPalette.get("pink2"); const purple = PastelSchemaPalette.get("purple4"); const blue = PastelSchemaPalette.get("bluegreen1"); const yellow = PastelSchemaPalette.get("yellow4"); const red = PastelSchemaPalette.get("red2"); const green = PastelSchemaPalette.get("bluegreen7"); const cyan = PastelSchemaPalette.get("bluegreen5"); const orange = PastelSchemaPalette.get("orange1"); const gray = "#f1efeb"; return (
this.changeColumnColor(pink!)}>
this.changeColumnColor(purple!)}>
this.changeColumnColor(blue!)}>
this.changeColumnColor(yellow!)}>
this.changeColumnColor(red!)}>
this.changeColumnColor(gray)}>
this.changeColumnColor(green!)}>
this.changeColumnColor(cyan!)}>
this.changeColumnColor(orange!)}>
); } toggleAlias = action(() => this._createAliasSelected = true); toggleVisibility = action(() => this._collapsed = !this._collapsed); renderMenu = () => { const selected = this._createAliasSelected; return (
Create Alias
Delete
); } handleResize = (size: any) => { if (++this._counter !== 1) { this.getTrueHeight(); } } @computed get contentLayout() { const rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap)))); const style = this.props.parent; const collapsed = this._collapsed; const chromeStatus = this.props.parent.props.Document._chromeStatus; const newEditableViewProps = { GetValue: () => "", SetValue: this.addDocument, contents: "+ NEW", HeadingObject: this.props.headingObject, HeadingsHack: this._headingsHack, toggle: this.toggleVisibility, color: this._color }; return collapsed ? (null) :
list + ` ${this.props.parent.columnWidth}px`, ""), }}> {this.props.parent.children(this.props.docList)} {this.props.parent.columnDragger}
{(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
: null }
; } @computed get headingView() { const heading = this._heading; const key = StrCast(this.props.parent.props.Document.sectionFilter); const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`; const headerEditableViewProps = { GetValue: () => evContents, SetValue: this.headingChanged, contents: evContents, oneLine: true, HeadingObject: this.props.headingObject, HeadingsHack: this._headingsHack, toggle: this.toggleVisibility, color: this._color }; return this.props.parent.props.Document.miniHeaders ?
: !this.props.headingObject ? (null) :
{evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
} {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
}
; } render() { const background = this._background; //to account for observables in Measure const contentlayout = this.contentLayout; const headingview = this.headingView; return {({ measureRef }) => { return
{headingview} {contentlayout}
; }}
; } }