diff options
Diffstat (limited to 'src/client/views')
| -rw-r--r-- | src/client/views/DocumentDecorations.scss | 6 | ||||
| -rw-r--r-- | src/client/views/DocumentDecorations.tsx | 105 | ||||
| -rw-r--r-- | src/client/views/InkingCanvas.scss | 17 | ||||
| -rw-r--r-- | src/client/views/InkingCanvas.tsx | 54 | ||||
| -rw-r--r-- | src/client/views/InkingStroke.tsx | 9 | ||||
| -rw-r--r-- | src/client/views/Main.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionBaseView.tsx | 23 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 12 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 28 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionViewBase.tsx | 13 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 22 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 45 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentContentsView.tsx | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 22 | ||||
| -rw-r--r-- | src/client/views/nodes/FieldView.tsx | 36 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValuePair.tsx | 10 |
16 files changed, 260 insertions, 156 deletions
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 11595aa01..7a43f3087 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -32,6 +32,12 @@ } } +.documentDecorations-background { + background:lightblue; + position: absolute; + opacity: 0.1; +} + // position: absolute; // display: grid; // z-index: 1000; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 47098c3b5..d0699e1ab 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,14 +1,14 @@ -import { observable, computed, action } from "mobx"; -import React = require("react"); -import { SelectionManager } from "../util/SelectionManager"; +import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; -import './DocumentDecorations.scss' -import { KeyStore } from '../../fields/KeyStore' +import { KeyStore } from '../../fields/KeyStore'; +import { ListField } from "../../fields/ListField"; import { NumberField } from "../../fields/NumberField"; -import { props } from "bluebird"; import { DragManager } from "../util/DragManager"; +import { SelectionManager } from "../util/SelectionManager"; +import { CollectionView } from "./collections/CollectionView"; +import './DocumentDecorations.scss'; import { LinkMenu } from "./nodes/LinkMenu"; -import { ListField } from "../../fields/ListField"; +import React = require("react"); const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -22,6 +22,7 @@ export class DocumentDecorations extends React.Component { private _resizeBorderWidth = 16; private _linkButton = React.createRef<HTMLDivElement>(); @observable private _hidden = false; + @observable private _dragging = false; constructor(props: Readonly<{}>) { super(props) @@ -50,6 +51,46 @@ export class DocumentDecorations extends React.Component { public get Hidden() { return this._hidden; } public set Hidden(value: boolean) { this._hidden = value; } + _lastDrag: number[] = [0, 0]; + onBackgroundDown = (e: React.PointerEvent): void => { + document.removeEventListener("pointermove", this.onBackgroundMove); + document.addEventListener("pointermove", this.onBackgroundMove); + document.removeEventListener("pointerup", this.onBackgroundUp); + document.addEventListener("pointerup", this.onBackgroundUp); + this._lastDrag = [e.clientX, e.clientY] + e.stopPropagation(); + e.preventDefault(); + } + + @action + onBackgroundMove = (e: PointerEvent): void => { + let dragDocView = SelectionManager.SelectedDocuments()[0]; + const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); + dragData.aliasOnDrop = false; + dragData.xOffset = e.x - left; + dragData.yOffset = e.y - top; + let move = SelectionManager.SelectedDocuments()[0].props.moveDocument; + dragData.moveDocument = move; + this._dragging = true; + document.removeEventListener("pointermove", this.onBackgroundMove); + document.removeEventListener("pointerup", this.onBackgroundUp); + DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => (docView as any)._mainCont!.current!), dragData, { + handlers: { + dragComplete: action(() => this._dragging = false), + }, + hideSource: true + }) + e.stopPropagation(); + } + + onBackgroundUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onBackgroundMove); + document.removeEventListener("pointerup", this.onBackgroundUp); + e.stopPropagation(); + e.preventDefault(); + } + onPointerDown = (e: React.PointerEvent): void => { e.stopPropagation(); if (e.button === 0) { @@ -191,7 +232,6 @@ export class DocumentDecorations extends React.Component { // buttonOnPointerUp = (e: React.PointerEvent): void => { // e.stopPropagation(); // } - render() { var bounds = this.Bounds; if (this.Hidden) { @@ -218,25 +258,36 @@ export class DocumentDecorations extends React.Component { </Flyout>); } return ( - <div id="documentDecorations-container" style={{ - width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px", - height: (bounds.b - bounds.y + this._resizeBorderWidth + 30) + "px", - left: bounds.x - this._resizeBorderWidth / 2, - top: bounds.y - this._resizeBorderWidth / 2, - }}> - <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-leftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-centerCont"></div> - <div id="documentDecorations-rightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - - <div title="View Links" className="linkFlyout" ref={this._linkButton}>{linkButton}</div> - - </div > + <div className="documentDecorations"> + <div className="documentDecorations-background" style={{ + width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px", + height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px", + left: bounds.x - this._resizeBorderWidth / 2, + top: bounds.y - this._resizeBorderWidth / 2, + pointerEvents: this._dragging ? "none" : "all", + zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0, + }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation() }} > + </div> + <div id="documentDecorations-container" style={{ + width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px", + height: (bounds.b - bounds.y + this._resizeBorderWidth + 30) + "px", + left: bounds.x - this._resizeBorderWidth / 2, + top: bounds.y - this._resizeBorderWidth / 2, + }}> + <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-leftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-centerCont"></div> + <div id="documentDecorations-rightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + + <div title="View Links" className="linkFlyout" ref={this._linkButton}>{linkButton}</div> + + </div > + </div> ) } }
\ No newline at end of file diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss index 214c70280..35c8ee942 100644 --- a/src/client/views/InkingCanvas.scss +++ b/src/client/views/InkingCanvas.scss @@ -2,18 +2,19 @@ .inkingCanvas-paths-ink, .inkingCanvas-paths-markers, .inkingCanvas-noSelect, .inkingCanvas-canSelect { position: absolute; - top: -50000px; - left: -50000px; - width: 100000px; - height: 100000px; - .inkingCanvas-children { - transform: translate(50000px, 50000px); - pointer-events: none; - } + width: 8192px; + height: 8192px; cursor:"crosshair"; pointer-events: auto; } +.inkingCanvas-canSelect, +.inkingCanvas-noSelect { + top:-50000px; + left:-50000px; + width: 100000px; + height: 100000px; +} .inkingCanvas-noSelect { pointer-events: none; cursor: "arrow"; diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 123ff679b..cad4b74b1 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -1,4 +1,4 @@ -import { action, computed, trace } from "mobx"; +import { action, computed, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../fields/Document"; import { FieldWaiting } from "../../fields/Field"; @@ -14,26 +14,40 @@ import React = require("react"); interface InkCanvasProps { getScreenTransform: () => Transform; Document: Document; + children: () => JSX.Element[]; } @observer export class InkingCanvas extends React.Component<InkCanvasProps> { - static InkOffset: number = 50000; + maxCanvasDim = 8192 / 2; // 1/2 of the maximum canvas dimension for Chrome + @observable inkMidX: number = 0; + @observable inkMidY: number = 0; private _currentStrokeId: string = ""; public static IntersectStrokeRect(stroke: StrokeData, selRect: { left: number, top: number, width: number, height: number }): boolean { - return stroke.pathData.reduce((inside, val) => inside || - (selRect.left < val.x - InkingCanvas.InkOffset && selRect.left + selRect.width > val.x - InkingCanvas.InkOffset && - selRect.top < val.y - InkingCanvas.InkOffset && selRect.top + selRect.height > val.y - InkingCanvas.InkOffset) + return stroke.pathData.reduce((inside: boolean, val) => inside || + (selRect.left < val.x && selRect.left + selRect.width > val.x && + selRect.top < val.y && selRect.top + selRect.height > val.y) , false); } + componentDidMount() { + this.props.Document.GetTAsync(KeyStore.Ink, InkField, ink => runInAction(() => { + if (ink) { + let bounds = Array.from(ink.Data).reduce(([mix, max, miy, may], [id, strokeData]) => + strokeData.pathData.reduce(([mix, max, miy, may], p) => + [Math.min(mix, p.x), Math.max(max, p.x), Math.min(miy, p.y), Math.max(may, p.y)], + [mix, max, miy, may]), + [Number.MAX_VALUE, Number.MIN_VALUE, Number.MAX_VALUE, Number.MIN_VALUE]); + this.inkMidX = (bounds[0] + bounds[1]) / 2; + this.inkMidY = (bounds[2] + bounds[3]) / 2; + } + })); + } + @computed get inkData(): StrokeMap { let map = this.props.Document.GetT(KeyStore.Ink, InkField); - if (!map || map === FieldWaiting) { - return new Map; - } - return new Map(map.Data); + return !map || map === FieldWaiting ? new Map : new Map(map.Data); } set inkData(value: StrokeMap) { @@ -63,9 +77,15 @@ export class InkingCanvas extends React.Component<InkCanvasProps> { } } + @action onPointerUp = (e: PointerEvent): void => { document.removeEventListener("pointermove", this.onPointerMove, true); document.removeEventListener("pointerup", this.onPointerUp, true); + let coord = this.relativeCoordinatesForEvent(e.clientX, e.clientY); + if (Math.abs(coord.x - this.inkMidX) > 500 || Math.abs(coord.y - this.inkMidY) > 500) { + this.inkMidX = coord.x; + this.inkMidY = coord.y; + } e.stopPropagation(); e.preventDefault(); } @@ -87,8 +107,6 @@ export class InkingCanvas extends React.Component<InkCanvasProps> { relativeCoordinatesForEvent = (ex: number, ey: number): { x: number, y: number } => { let [x, y] = this.props.getScreenTransform().transformPoint(ex, ey); - x += InkingCanvas.InkOffset; - y += InkingCanvas.InkOffset; return { x, y }; } @@ -101,30 +119,32 @@ export class InkingCanvas extends React.Component<InkCanvasProps> { @computed get drawnPaths() { - // parse data from server let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1) let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => { if (strokeData.page == -1 || strokeData.page == curPage) paths.push(<InkingStroke key={id} id={id} line={strokeData.pathData} + offsetX={this.maxCanvasDim - this.inkMidX} + offsetY={this.maxCanvasDim - this.inkMidY} color={strokeData.color} width={strokeData.width} tool={strokeData.tool} deleteCallback={this.removeLine} />) return paths; }, [] as JSX.Element[]); - return [<svg className={`inkingCanvas-paths-markers`} key="Markers" > + return [<svg className={`inkingCanvas-paths-markers`} key="Markers" + style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} > {paths.filter(path => path.props.tool == InkTool.Highlighter)} </svg>, - <svg className={`inkingCanvas-paths-ink`} key="Pens" > + <svg className={`inkingCanvas-paths-ink`} key="Pens" + style={{ left: `-${this.inkMidX - this.maxCanvasDim}px`, top: `-${this.inkMidY - this.maxCanvasDim}px` }}> {paths.filter(path => path.props.tool != InkTool.Highlighter)} </svg>]; } render() { let svgCanvasStyle = InkingControl.Instance.selectedTool != InkTool.None ? "canSelect" : "noSelect"; - return ( <div className="inkingCanvas" > - <svg className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} /> - {(this.props.children as any)() /* bcz: is there a better way to know that children is a function? */} + <div className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} /> + {this.props.children()} {this.drawnPaths} </div > ) diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 52111c711..615f8af7e 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -6,6 +6,8 @@ import React = require("react"); interface StrokeProps { + offsetX: number; + offsetY: number; id: string; line: Array<{ x: number, y: number }>; color: string; @@ -28,7 +30,7 @@ export class InkingStroke extends React.Component<StrokeProps> { } parseData = (line: Array<{ x: number, y: number }>): string => { - return !line.length ? "" : "M " + line.map(p => p.x + " " + p.y).join(" L "); + return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L "); } createStyle() { @@ -43,14 +45,13 @@ export class InkingStroke extends React.Component<StrokeProps> { } } - render() { let pathStyle = this.createStyle(); let pathData = this.parseData(this.props.line); + let pointerEvents: any = InkingControl.Instance.selectedTool == InkTool.Eraser ? "all" : "none"; return ( - <path className={(this._strokeTool === InkTool.Highlighter) ? "highlight" : ""} - d={pathData} style={{ ...pathStyle, pointerEvents: "all" }} strokeLinejoin="round" strokeLinecap="round" + <path d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round" onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} /> ) } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index bcbe6b987..8f67c006d 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -6,7 +6,7 @@ import { Document } from '../../fields/Document'; import { KeyStore } from '../../fields/KeyStore'; import "./Main.scss"; import { MessageStore } from '../../server/Message'; -import { Utils, returnTrue } from '../../Utils'; +import { Utils, returnTrue, emptyFunction } from '../../Utils'; import * as request from 'request' import * as rp from 'request-promise' import { Documents } from '../documents/Documents'; @@ -217,7 +217,8 @@ export class Main extends React.Component { focusDocument = (doc: Document) => { } noScaling = () => 1; - get content() { + @computed + get mainContent() { return !this.mainContainer ? (null) : <DocumentView Document={this.mainContainer} addDocument={undefined} @@ -230,6 +231,7 @@ export class Main extends React.Component { selectOnLoad={false} focus={this.focusDocument} parentActive={returnTrue} + onActiveChanged={emptyFunction} ContainingCollectionView={undefined} /> } @@ -311,17 +313,17 @@ export class Main extends React.Component { } return ( <div id="main-div"> + <DocumentDecorations /> <Measure onResize={(r: any) => runInAction(() => { this.pwidth = r.entry.width; this.pheight = r.entry.height; })}> {({ measureRef }) => <div ref={measureRef} id="mainContent-div"> - {this.content} + {this.mainContent} </div> } </Measure> - <DocumentDecorations /> <ContextMenu /> {this.nodesMenu} {this.miscButtons} diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index c9f640c4a..301467d99 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -21,8 +21,9 @@ export enum CollectionViewType { export interface CollectionRenderProps { addDocument: (document: Document, allowDuplicates?: boolean) => boolean; removeDocument: (document: Document) => boolean; - moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean; + moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; active: () => boolean; + onActiveChanged: (isActive: boolean) => void; } export interface CollectionViewProps extends FieldViewProps { @@ -49,9 +50,15 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { active = (): boolean => { var isSelected = this.props.isSelected(); - var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); var topMost = this.props.isTopMost; - return isSelected || childSelected || topMost; + return isSelected || this._isChildActive || topMost; + } + + //TODO should this be observable? + private _isChildActive = false; + onActiveChanged = (isActive: boolean) => { + this._isChildActive = isActive; + this.props.onActiveChanged(isActive); } createsCycle(documentToAdd: Document, containerDocument: Document): boolean { @@ -121,7 +128,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { if (index !== -1) { value.splice(index, 1) - SelectionManager.DeselectAll() + // SelectionManager.DeselectAll() ContextMenu.Instance.clearItems() return true; } @@ -129,13 +136,12 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { } @action.bound - moveDocument(doc: Document, targetCollection: Document, addDocument: (doc: Document) => void): boolean { + moveDocument(doc: Document, targetCollection: Document, addDocument: (doc: Document) => boolean): boolean { if (this.props.Document === targetCollection) { return false; } if (this.removeDocument(doc)) { - addDocument(doc); - return true; + return addDocument(doc); } return false; } @@ -145,7 +151,8 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { addDocument: this.addDocument, removeDocument: this.removeDocument, moveDocument: this.moveDocument, - active: this.active + active: this.active, + onActiveChanged: this.onActiveChanged, } return ( <div className={this.props.className || "collectionView-cont"} onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}> diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index bb34340fb..1b0ad0bee 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -8,7 +8,7 @@ import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/KeyStore"; import Measure from "react-measure"; import { FieldId, Opt, Field } from "../../../fields/Field"; -import { Utils, returnTrue } from "../../../Utils"; +import { Utils, returnTrue, emptyFunction } from "../../../Utils"; import { Server } from "../../Server"; import { undoBatch } from "../../util/UndoManager"; import { DocumentView } from "../nodes/DocumentView"; @@ -47,9 +47,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp (window as any).React = React; (window as any).ReactDOM = ReactDOM; } - public StartOtherDrag(dragDoc: Document, e: any) { - this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener. - onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 }) + public StartOtherDrag(dragDocs: Document[], e: any) { + dragDocs.map(dragDoc => + this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener. + onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 })); } @action @@ -199,7 +200,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp let tab = (e.target as any).parentElement as HTMLElement; Server.GetField(docid, action((f: Opt<Field>) => { if (f instanceof Document) - DragManager.StartDocumentDrag(tab, new DragManager.DocumentDragData(f as Document), + DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f as Document]), { handlers: { dragComplete: action(() => { }), @@ -319,6 +320,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { isTopMost={true} selectOnLoad={false} parentActive={returnTrue} + onActiveChanged={emptyFunction} focus={(doc: Document) => { }} ContainingCollectionView={undefined} /> </div> diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 0eacd38b0..78a813a99 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -83,7 +83,8 @@ export class CollectionSchemaView extends CollectionViewBase { selectOnLoad: false, ScreenToLocalTransform: Transform.Identity, focus: emptyFunction, - active: returnFalse + active: returnFalse, + onActiveChanged: emptyFunction, } let contents = ( <FieldView {...props} /> @@ -299,18 +300,19 @@ export class CollectionSchemaView extends CollectionViewBase { <Measure onResize={this.setScaling}> {({ measureRef }) => <div className="collectionSchemaView-content" ref={measureRef}> - {doc instanceof Document ? <DocumentView Document={doc} - addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} - isTopMost={false} - selectOnLoad={false} - ScreenToLocalTransform={this.getPreviewTransform} - ContentScaling={this.getContentScaling} - PanelWidth={this.getPanelWidth} - PanelHeight={this.getPanelHeight} - ContainingCollectionView={undefined} - focus={this.focusDocument} - parentActive={this.props.active} - /> : null} + {doc instanceof Document ? + <DocumentView Document={doc} + addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} + isTopMost={false} + selectOnLoad={false} + ScreenToLocalTransform={this.getPreviewTransform} + ContentScaling={this.getContentScaling} + PanelWidth={this.getPanelWidth} + PanelHeight={this.getPanelHeight} + ContainingCollectionView={undefined} + focus={this.focusDocument} + parentActive={this.props.active} + onActiveChanged={this.props.onActiveChanged} /> : null} <input value={this.previewScript} onChange={this.onPreviewScriptChange} style={{ position: 'absolute', bottom: '0px' }} /> </div> diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index 4a0b2312e..71a639137 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -22,7 +22,7 @@ import { FieldViewProps } from "../nodes/FieldView"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Document, allowDuplicates?: boolean) => boolean; removeDocument: (document: Document) => boolean; - moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean; + moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; } export interface SubCollectionViewProps extends CollectionViewProps { @@ -72,11 +72,14 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps> if (de.data instanceof DragManager.DocumentDragData) { if (de.data.aliasOnDrop) { [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key => - de.data.draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocument.SetNumber(key, f.Data) : null)); + de.data.draggedDocuments.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocument.SetNumber(key, f.Data) : null)); } - let added = this.props.addDocument(de.data.droppedDocument, false); - if (added && de.data.moveDocument && !de.data.aliasOnDrop) { - de.data.moveDocument(this.props.Document, this.props.addDocument); + let added = false; + if (de.data.aliasOnDrop) { + added = de.data.droppedDocuments.reduce((added, d) => added || this.props.addDocument(d), false); + } else if (de.data.moveDocument) { + const move = de.data.moveDocument; + added = de.data.droppedDocuments.reduce((added, d) => added || move(d, this.props.Document, this.props.addDocument), false) } e.stopPropagation(); return added; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 144d121db..b04438ede 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -86,13 +86,20 @@ export class CollectionFreeFormView extends CollectionViewBase { let screenX = de.x - (de.data.xOffset as number || 0); let screenY = de.y - (de.data.yOffset as number || 0); const [x, y] = this.getTransform().transformPoint(screenX, screenY); - de.data.droppedDocument.SetNumber(KeyStore.X, x); - de.data.droppedDocument.SetNumber(KeyStore.Y, y); - if (!de.data.droppedDocument.GetNumber(KeyStore.Width, 0)) { - de.data.droppedDocument.SetNumber(KeyStore.Width, 300); - de.data.droppedDocument.SetNumber(KeyStore.Height, 300); - } - this.bringToFront(de.data.droppedDocument); + let dragDoc = de.data.draggedDocuments[0]; + let dragX = dragDoc.GetNumber(KeyStore.X, 0); + let dragY = dragDoc.GetNumber(KeyStore.Y, 0); + de.data.draggedDocuments.map(d => { + let docX = d.GetNumber(KeyStore.X, 0); + let docY = d.GetNumber(KeyStore.Y, 0); + d.SetNumber(KeyStore.X, x + (docX - dragX)); + d.SetNumber(KeyStore.Y, y + (docY - dragY)); + if (!d.GetNumber(KeyStore.Width, 0)) { + d.SetNumber(KeyStore.Width, 300); + d.SetNumber(KeyStore.Height, 300); + } + this.bringToFront(d); + }) } return true; } @@ -247,6 +254,7 @@ export class CollectionFreeFormView extends CollectionViewBase { ContainingCollectionView: undefined, focus: this.focusDocument, parentActive: this.props.active, + onActiveChanged: this.props.active, } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 20132a4b1..e2239c8be 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -102,7 +102,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps> if (e.key == "Backspace" || e.key == "Delete") { this.marqueeSelect().map(d => this.props.removeDocument(d)); let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField); - if (ink && ink != FieldWaiting && ink.Data) { + if (ink && ink != FieldWaiting) { this.marqueeInkDelete(ink.Data); } this.cleanupInteractions(); @@ -118,22 +118,21 @@ export class MarqueeView extends React.Component<MarqueeViewProps> return d; }); let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField); - if (ink && ink != FieldWaiting && ink.Data) { - //setTimeout(() => { - let newCollection = Documents.FreeformDocument(selected, { - x: bounds.left, - y: bounds.top, - panx: 0, - pany: 0, - width: bounds.width, - height: bounds.height, - backgroundColor: "Transparent", - ink: this.marqueeInkSelect(ink.Data), - title: "a nested collection" - }); - this.props.addDocument(newCollection, false); - this.marqueeInkDelete(ink.Data); - } + let inkData = ink && ink != FieldWaiting ? ink.Data : undefined; + //setTimeout(() => { + let newCollection = Documents.FreeformDocument(selected, { + x: bounds.left, + y: bounds.top, + panx: 0, + pany: 0, + width: bounds.width, + height: bounds.height, + backgroundColor: "Transparent", + ink: inkData ? this.marqueeInkSelect(inkData) : undefined, + title: "a nested collection" + }); + this.props.addDocument(newCollection, false); + this.marqueeInkDelete(inkData); // }, 100); this.cleanupInteractions(); } @@ -159,15 +158,17 @@ export class MarqueeView extends React.Component<MarqueeViewProps> } @action - marqueeInkDelete(ink: Map<any, any>, ) { + marqueeInkDelete(ink?: Map<any, any>) { // bcz: this appears to work but when you restart all the deleted strokes come back -- InkField isn't observing its changes so they aren't written to the DB. // ink.forEach((value: StrokeData, key: string, map: any) => // InkingCanvas.IntersectStrokeRect(value, this.Bounds) && ink.delete(key)); - let idata = new Map(); - ink.forEach((value: StrokeData, key: string, map: any) => - !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value)); - this.props.container.props.Document.SetDataOnPrototype(KeyStore.Ink, idata, InkField); + if (ink) { + let idata = new Map(); + ink.forEach((value: StrokeData, key: string, map: any) => + !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value)); + this.props.container.props.Document.SetDataOnPrototype(KeyStore.Ink, idata, InkField); + } } marqueeSelect() { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 2574b374a..1c9155dce 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -54,7 +54,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & { ScreenToLocalTransform, addDocument, removeDocument, - parentActive: active + onActiveChanged, + parentActive: active, } = this.props; let bindings: JsxBindings = { props: { @@ -65,6 +66,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & { selectOnLoad, ScreenToLocalTransform, active, + onActiveChanged, addDocument, removeDocument, focus, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 545344024..6c05f6924 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -36,6 +36,7 @@ export interface DocumentViewProps { focus: (doc: Document) => void; selectOnLoad: boolean; parentActive: () => boolean; + onActiveChanged: (isActive: boolean) => void; } export interface JsxArgs extends DocumentViewProps { Keys: { [name: string]: Key } @@ -99,7 +100,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { if (this.props.isTopMost) { this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey); } - else CollectionDockingView.Instance.StartOtherDrag(this.props.Document, e); + else CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e); e.stopPropagation(); } else { if (this.active && !e.isDefaultPrevented()) { @@ -118,9 +119,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { if (this._mainCont.current) { this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }); } - runInAction(() => { - DocumentManager.Instance.DocumentViews.push(this); - }) + runInAction(() => DocumentManager.Instance.DocumentViews.push(this)) } componentDidUpdate() { @@ -136,25 +135,18 @@ export class DocumentView extends React.Component<DocumentViewProps> { if (this.dropDisposer) { this.dropDisposer(); } - runInAction(() => { - DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1); - - }) + runInAction(() => DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1)) } startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) { if (this._mainCont.current) { const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - let dragData = new DragManager.DocumentDragData(this.props.Document); + let dragData = new DragManager.DocumentDragData([this.props.Document]); dragData.aliasOnDrop = dropAliasOfDraggedDoc; dragData.xOffset = x - left; dragData.yOffset = y - top; - dragData.moveDocument = (targetCollection: Document, addDocument: (document: Document) => void) => { - if (this.props.moveDocument) { - this.props.moveDocument(this.props.Document, targetCollection, addDocument) - } - } - DragManager.StartDocumentDrag(this._mainCont.current, dragData, { + dragData.moveDocument = this.props.moveDocument; + DragManager.StartDocumentDrag([this._mainCont.current], dragData, { handlers: { dragComplete: action(() => { }), }, diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 57af55f9f..4c6062a2f 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -36,9 +36,10 @@ export interface FieldViewProps { selectOnLoad: boolean; addDocument?: (document: Document, allowDuplicates?: boolean) => boolean; removeDocument?: (document: Document) => boolean; - moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean; + moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; ScreenToLocalTransform: () => Transform; active: () => boolean; + onActiveChanged: (isActive: boolean) => void; focus: (doc: Document) => void; } @@ -74,21 +75,24 @@ export class FieldView extends React.Component<FieldViewProps> { return <AudioBox {...this.props} /> } else if (field instanceof Document) { - return (<DocumentContentsView Document={field} - addDocument={undefined} - removeDocument={undefined} - ScreenToLocalTransform={Transform.Identity} - ContentScaling={() => 1} - PanelWidth={() => 100} - PanelHeight={() => 100} - isTopMost={true} //TODO Why is this top most? - selectOnLoad={false} - focus={() => { }} - isSelected={() => false} - select={() => false} - layoutKey={KeyStore.Layout} - ContainingCollectionView={undefined} - parentActive={this.props.active} />) + return ( + <DocumentContentsView Document={field} + addDocument={undefined} + removeDocument={undefined} + ScreenToLocalTransform={Transform.Identity} + ContentScaling={() => 1} + PanelWidth={() => 100} + PanelHeight={() => 100} + isTopMost={true} //TODO Why is this top most? + selectOnLoad={false} + focus={() => { }} + isSelected={() => false} + select={() => false} + layoutKey={KeyStore.Layout} + ContainingCollectionView={undefined} + parentActive={this.props.active} + onActiveChanged={this.props.onActiveChanged} /> + ) } else if (field instanceof ListField) { return (<div> diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index e178cbc8d..a676844dc 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -12,6 +12,7 @@ import { Server } from "../../Server" import { EditableView } from "../EditableView"; import { CompileScript, ToField } from "../../util/Scripting"; import { Transform } from '../../util/Transform'; +import { returnFalse, emptyFunction } from '../../../Utils'; // Represents one row in a key value plane @@ -46,13 +47,14 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> { let props: FieldViewProps = { Document: this.props.doc, fieldKey: this.key, - isSelected: () => false, - select: () => { }, + isSelected: returnFalse, + select: emptyFunction, isTopMost: false, selectOnLoad: false, - active: () => false, + active: returnFalse, + onActiveChanged: emptyFunction, ScreenToLocalTransform: Transform.Identity, - focus: () => { }, + focus: emptyFunction, } let contents = ( <FieldView {...props} /> |
