aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
diff options
context:
space:
mode:
authoryipstanley <stanley_yip@brown.edu>2020-02-29 14:18:43 -0500
committeryipstanley <stanley_yip@brown.edu>2020-02-29 14:18:43 -0500
commit2f6e27c67d1790d4350eede3003f0b614460f4d1 (patch)
treeef5e70925b8cdeb8229af849e33e6f3a4cceae7f /src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
parentf1fcbeea5fb103b7623e795e72aacd4dfacc6c70 (diff)
parent640f14da28d97600fb32d09023fc932e3a4052c4 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into pen
Diffstat (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx247
1 files changed, 151 insertions, 96 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 047a3a1cc..a73e601fd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -3,18 +3,20 @@ import { faEye } from "@fortawesome/free-regular-svg-icons";
import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload, faTextHeight } from "@fortawesome/free-solid-svg-icons";
import { action, computed, observable, ObservableMap, reaction, runInAction, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync, Field } from "../../../../new_fields/Doc";
+import { computedFn } from "mobx-utils";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from "../../../../new_fields/Doc";
import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
import { Id } from "../../../../new_fields/FieldSymbols";
import { InkTool, InkField, InkData } from "../../../../new_fields/InkField";
-import { createSchema, makeInterface } from "../../../../new_fields/Schema";
+import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, DateCast, NumCast, StrCast, ScriptCast, FieldValue } from "../../../../new_fields/Types";
+import { Cast, NumCast, ScriptCast, BoolCast, StrCast, FieldValue } from "../../../../new_fields/Types";
+import { TraceMobx } from "../../../../new_fields/util";
+import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
-import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils";
+import { aggregateBounds, intersectRect, returnOne, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
-import { Docs, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
+import { Docs } from "../../../documents/Documents";
import { DocumentManager } from "../../../util/DocumentManager";
import { DragManager } from "../../../util/DragManager";
import { HistoryUtil } from "../../../util/History";
@@ -32,15 +34,12 @@ import { FormattedTextBox } from "../../nodes/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
import PDFMenu from "../../pdf/PDFMenu";
import { CollectionSubView } from "../CollectionSubView";
-import { computePivotLayout, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
+import { computePivotLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
import "./CollectionFreeFormView.scss";
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
-import { computedFn } from "mobx-utils";
-import { TraceMobx } from "../../../../new_fields/util";
-import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { RichTextField } from "../../../../new_fields/RichTextField";
import { List } from "../../../../new_fields/List";
@@ -59,8 +58,8 @@ export const panZoomSchema = createSchema({
arrangeInit: ScriptField,
useClusters: "boolean",
fitToBox: "boolean",
- xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
- yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
+ _xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
+ _yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
panTransformType: "string",
scrollHeight: "number",
fitX: "number",
@@ -83,6 +82,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private _hitCluster = false;
private _layoutComputeReaction: IReactionDisposer | undefined;
private _layoutPoolData = new ObservableMap<string, any>();
+ private _cachedPool: Map<string, any> = new Map();
@observable private _pullCoords: number[] = [0, 0];
@observable private _pullDirection: string = "";
@@ -123,26 +123,27 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true));
}
- public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
+ public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
public getActiveDocuments = () => {
return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
}
@action
- onDrop = (e: React.DragEvent): Promise<void> => {
+ onExternalDrop = (e: React.DragEvent): Promise<void> => {
const pt = this.getTransform().transformPoint(e.pageX, e.pageY);
- return super.onDrop(e, { x: pt[0], y: pt[1] });
+ return super.onExternalDrop(e, { x: pt[0], y: pt[1] });
}
@undoBatch
@action
- drop = (e: Event, de: DragManager.DropEvent) => {
+ onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+ if (this.props.Document.isBackground) return false;
const xf = this.getTransform();
const xfo = this.getTransformOverlay();
const [xp, yp] = xf.transformPoint(de.x, de.y);
const [xpo, ypo] = xfo.transformPoint(de.x, de.y);
- if (super.drop(e, de)) {
+ if (super.onInternalDrop(e, de)) {
if (de.complete.docDragData) {
if (de.complete.docDragData.droppedDocuments.length) {
const firstDoc = de.complete.docDragData.droppedDocuments[0];
@@ -218,6 +219,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
@undoBatch
+ @action
updateClusters(useClusters: boolean) {
this.props.Document.useClusters = useClusters;
this._clusterSets.length = 0;
@@ -255,7 +257,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
docs.map(doc => this._clusterSets[doc.cluster = NumCast(docFirst.cluster)].push(doc));
}
childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.cluster === i) && this.updateCluster(child));
- childLayouts.map(child => Doc.GetProto(child).clusterStr = child.cluster?.toString());
}
}
@@ -291,16 +292,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
getClusterColor = (doc: Doc) => {
- let clusterColor = "";
+ let clusterColor = this.props.backgroundColor?.(doc);
const cluster = NumCast(doc.cluster);
if (this.Document.useClusters) {
if (this._clusterSets.length <= cluster) {
setTimeout(() => this.updateCluster(doc), 0);
} else {
// choose a cluster color from a palette
- const colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
+ const colors = ["#da42429e", "#31ea318c", "rgba(197, 87, 20, 0.55)", "#4a7ae2c4", "rgba(216, 9, 255, 0.5)", "#ff7601", "#1dffff", "yellow", "rgba(27, 130, 49, 0.55)", "rgba(0, 0, 0, 0.268)"];
clusterColor = colors[cluster % colors.length];
- const set = this._clusterSets[cluster] && this._clusterSets[cluster].filter(s => s.backgroundColor && (s.backgroundColor !== s.defaultBackgroundColor));
+ const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
// override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
set && set.filter(s => !s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
set && set.filter(s => s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
@@ -527,19 +528,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let x = this.Document._panX || 0;
let y = this.Document._panY || 0;
- const docs = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout);
+ const docs = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout);
const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
- if (!this.isAnnotationOverlay && docs.length) {
+ if (!this.isAnnotationOverlay && docs.length && this.childDataProvider(docs[0])) {
PDFMenu.Instance.fadeOut(true);
- const minx = this.childDataProvider(docs[0]).x;//docs.length ? NumCast(docs[0].x) : 0;
- const miny = this.childDataProvider(docs[0]).y;//docs.length ? NumCast(docs[0].y) : 0;
- const maxx = this.childDataProvider(docs[0]).width + minx;//docs.length ? NumCast(docs[0].width) + minx : minx;
- const maxy = this.childDataProvider(docs[0]).height + miny;//docs.length ? NumCast(docs[0].height) + miny : miny;
- const ranges = docs.filter(doc => doc).reduce((range, doc) => {
- const x = this.childDataProvider(doc).x;//NumCast(doc.x);
- const y = this.childDataProvider(doc).y;//NumCast(doc.y);
- const xe = this.childDataProvider(doc).width + x;//x + NumCast(layoutDoc.width);
- const ye = this.childDataProvider(doc).height + y; //y + NumCast(layoutDoc.height);
+ const minx = this.childDataProvider(docs[0]).x;
+ const miny = this.childDataProvider(docs[0]).y;
+ const maxx = this.childDataProvider(docs[0]).width + minx;
+ const maxy = this.childDataProvider(docs[0]).height + miny;
+ const ranges = docs.filter(doc => doc && this.childDataProvider(doc)).reduce((range, doc) => {
+ const x = this.childDataProvider(doc).x;
+ const y = this.childDataProvider(doc).y;
+ const xe = this.childDataProvider(doc).width + x;
+ const ye = this.childDataProvider(doc).height + y;
return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
[range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
}, [[minx, maxx], [miny, maxy]]);
@@ -844,7 +845,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
- setScaleToZoom = (doc: Doc, scale: number = 0.5) => {
+ setScaleToZoom = (doc: Doc, scale: number = 0.75) => {
this.Document.scale = scale * Math.min(this.props.PanelWidth() / NumCast(doc._width), this.props.PanelHeight() / NumCast(doc._height));
}
@@ -856,6 +857,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
@computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+ backgroundHalo = () => BoolCast(this.Document.useClusters);
getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
return {
@@ -875,6 +877,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
ContainingCollectionDoc: this.props.Document,
focus: this.focusDocument,
backgroundColor: this.getClusterColor,
+ backgroundHalo: this.backgroundHalo,
parentActive: this.props.active,
bringToFront: this.bringToFront,
zoomToScale: this.zoomToScale,
@@ -882,93 +885,159 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
};
}
- getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, transition?: string, state?: any } {
+ getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): PoolData {
const result = this.Document.arrangeScript?.script.run(params, console.log);
if (result?.success) {
return { ...result, transition: "transform 1s" };
}
const layoutDoc = Doc.Layout(params.doc);
- return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number") };
+ const { x, y, z, color, zIndex } = params.doc;
+ return {
+ x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
+ width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number")
+ };
}
- viewDefsToJSX = (views: any[]) => {
+ viewDefsToJSX = (views: ViewDefBounds[]) => {
return !Array.isArray(views) ? [] : views.filter(ele => this.viewDefToJSX(ele)).map(ele => this.viewDefToJSX(ele)!);
}
- private viewDefToJSX(viewDef: any): Opt<ViewDefResult> {
+ onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
+ (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload });
+ }
+ private viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> {
+ const { x, y, z } = viewDef;
+ const color = StrCast(viewDef.color);
+ const width = Cast(viewDef.width, "number");
+ const height = Cast(viewDef.height, "number");
+ const transform = `translate(${x}px, ${y}px)`;
if (viewDef.type === "text") {
const text = Cast(viewDef.text, "string"); // don't use NumCast, StrCast, etc since we want to test for undefined below
- const x = Cast(viewDef.x, "number");
- const y = Cast(viewDef.y, "number");
- const z = Cast(viewDef.z, "number");
- const width = Cast(viewDef.width, "number");
- const height = Cast(viewDef.height, "number");
const fontSize = Cast(viewDef.fontSize, "number");
- return [text, x, y, width, height].some(val => val === undefined) ? undefined :
+ return [text, x, y].some(val => val === undefined) ? undefined :
{
- ele: <div className="collectionFreeform-customText" key={(text || "") + x + y + z} style={{ width, height, fontSize, transform: `translate(${x}px, ${y}px)` }}>
+ ele: <div className="collectionFreeform-customText" key={(text || "") + x + y + z + color} style={{ width, height, color, fontSize, transform }}>
{text}
</div>,
- bounds: { x: x!, y: y!, z: z, width: width!, height: height! }
+ bounds: viewDef
+ };
+ } else if (viewDef.type === "div") {
+ return [x, y].some(val => val === undefined) ? undefined :
+ {
+ ele: <div className="collectionFreeform-customDiv" title={viewDef.payload?.join(" ")} key={"div" + x + y + z} onClick={e => this.onViewDefDivClick(e, viewDef)}
+ style={{ width, height, backgroundColor: color, transform }} />,
+ bounds: viewDef
};
}
}
childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc) {
- if (!doc) {
- console.log(doc);
- }
return this._layoutPoolData.get(doc[Id]);
}.bind(this));
- doPivotLayout(poolData: ObservableMap<string, any>) {
- return computePivotLayout(poolData, this.props.Document, this.childDocs,
- this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)), [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
+ doTimelineLayout(poolData: Map<string, PoolData>) {
+ return computeTimelineLayout(poolData, this.props.Document, this.childDocs, this.filterDocs,
+ this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
}
- doFreeformLayout(poolData: ObservableMap<string, any>) {
+ doPivotLayout(poolData: Map<string, PoolData>) {
+ return computePivotLayout(poolData, this.props.Document, this.childDocs, this.filterDocs,
+ this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
+ }
+
+ doFreeformLayout(poolData: Map<string, PoolData>) {
const layoutDocs = this.childLayoutPairs.map(pair => pair.layout);
const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log);
- let state = initResult && initResult.success ? initResult.result.scriptState : undefined;
+ const state = initResult && initResult.success ? initResult.result.scriptState : undefined;
const elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
- const data = poolData.get(pair.layout[Id]);
const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state });
- state = pos.state === undefined ? state : pos.state;
- if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.width !== data.width || pos.height !== data.height || pos.transition !== data.transition) {
- runInAction(() => poolData.set(pair.layout[Id], pos));
- }
+ poolData.set(pair.layout[Id], pos);
});
- return { elements: elements };
+ return elements;
}
- get doLayoutComputation() {
- let computedElementData: { elements: ViewDefResult[] };
- switch (this.Document._freeformLayoutEngine) {
- case "pivot": computedElementData = this.doPivotLayout(this._layoutPoolData); break;
- default: computedElementData = this.doFreeformLayout(this._layoutPoolData); break;
+ @computed get doInternalLayoutComputation() {
+ const newPool = new Map<string, any>();
+ switch (this.props.layoutEngine?.()) {
+ case "timeline": return { newPool, computedElementData: this.doTimelineLayout(newPool) };
+ case "pivot": return { newPool, computedElementData: this.doPivotLayout(newPool) };
+ }
+ return { newPool, computedElementData: this.doFreeformLayout(newPool) };
+ }
+
+ @computed get filterDocs() {
+ const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
+ const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
+ const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
+ for (let i = 0; i < docFilters.length; i += 3) {
+ const [key, value, modifiers] = docFilters.slice(i, i + 3);
+ if (!filterFacets[key]) {
+ filterFacets[key] = {};
+ }
+ filterFacets[key][value] = modifiers;
}
- this.childLayoutPairs.filter((pair, i) => this.isCurrent(pair.layout)).forEach(pair =>
- computedElementData.elements.push({
+ const filteredDocs = docFilters.length ? this.childDocs.filter(d => {
+ for (const facetKey of Object.keys(filterFacets)) {
+ const facet = filterFacets[facetKey];
+ const satisfiesFacet = Object.keys(facet).some(value =>
+ (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value));
+ if (!satisfiesFacet) {
+ return false;
+ }
+ }
+ return true;
+ }) : this.childDocs;
+ const rangeFilteredDocs = filteredDocs.filter(d => {
+ for (let i = 0; i < docRangeFilters.length; i += 3) {
+ const key = docRangeFilters[i];
+ const min = Number(docRangeFilters[i + 1]);
+ const max = Number(docRangeFilters[i + 2]);
+ const val = Cast(d[key], "number", null);
+ if (val !== undefined && (val < min || val > max)) {
+ return false;
+ }
+ }
+ return true;
+ });
+ return rangeFilteredDocs;
+ }
+ childLayoutDocFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
+ get doLayoutComputation() {
+ const { newPool, computedElementData } = this.doInternalLayoutComputation;
+ runInAction(() =>
+ Array.from(newPool.keys()).map(key => {
+ const lastPos = this._cachedPool.get(key); // last computed pos
+ const newPos = newPool.get(key);
+ if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex || newPos.width !== lastPos.width || newPos.height !== lastPos.height) {
+ this._layoutPoolData.set(key, newPos);
+ }
+ }));
+ this._cachedPool.clear();
+ Array.from(newPool.keys()).forEach(k => this._cachedPool.set(k, newPool.get(k)));
+ const elements: ViewDefResult[] = computedElementData.slice();
+ this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair =>
+ elements.push({
ele: <CollectionFreeFormDocumentView key={pair.layout[Id]} {...this.getChildDocumentViewProps(pair.layout, pair.data)}
dataProvider={this.childDataProvider}
+ LayoutDoc={this.childLayoutDocFunc}
jitterRotation={NumCast(this.props.Document.jitterRotation)}
- fitToBox={this.props.fitToBox || this.Document._freeformLayoutEngine === "pivot"} />,
+ fitToBox={this.props.fitToBox || this.props.layoutEngine !== undefined} />,
bounds: this.childDataProvider(pair.layout)
}));
- return computedElementData;
+ return elements;
}
componentDidMount() {
super.componentDidMount();
- this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation; },
- action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)),
+ this._layoutComputeReaction = reaction(() => this.doLayoutComputation,
+ (elements) => this._layoutElements = elements || [],
{ fireImmediately: true, name: "doLayout" });
}
componentWillUnmount() {
- this._layoutComputeReaction && this._layoutComputeReaction();
+ this._layoutComputeReaction?.();
}
@computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
elementFunc = () => this._layoutElements;
@@ -980,23 +1049,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
layoutDocsInGrid = () => {
UndoManager.RunInBatch(() => {
- const docs = DocListCast(this.Document[this.props.fieldKey]);
+ const docs = this.childLayoutPairs;
const startX = this.Document._panX || 0;
let x = startX;
let y = this.Document._panY || 0;
let i = 0;
- const width = Math.max(...docs.map(doc => NumCast(doc._width)));
- const height = Math.max(...docs.map(doc => NumCast(doc._height)));
- for (const doc of docs) {
- doc.x = x;
- doc.y = y;
+ const width = Math.max(...docs.map(doc => NumCast(doc.layout._width)));
+ const height = Math.max(...docs.map(doc => NumCast(doc.layout._height)));
+ docs.forEach(pair => {
+ pair.layout.x = x;
+ pair.layout.y = y;
x += width + 20;
if (++i === 6) {
i = 0;
x = startX;
y += height + 20;
}
- }
+ });
}, "arrange contents");
}
@@ -1044,6 +1113,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// }
onContextMenu = (e: React.MouseEvent) => {
+ if (this.props.children && this.props.annotationsKey) return;
const layoutItems: ContextMenuProps[] = [];
layoutItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
@@ -1081,19 +1151,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
});
- layoutItems.push({
- description: "Add Note ...",
- subitems: DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data).map((note, i) => ({
- description: (i + 1) + ": " + StrCast(note.title),
- event: (args: { x: number, y: number }) => this.addLiveTextBox(Docs.Create.TextDocument("", { _width: 200, _height: 100, x: this.getTransform().transformPoint(args.x, args.y)[0], y: this.getTransform().transformPoint(args.x, args.y)[1], _autoHeight: true, layout: note, title: StrCast(note.title) })),
- icon: "eye"
- })) as ContextMenuProps[],
- icon: "eye"
- });
ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" });
}
-
private childViews = () => {
const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
return [
@@ -1102,13 +1162,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
];
}
- // @observable private _palette?: JSX.Element;
-
children = () => {
const eles: JSX.Element[] = [];
eles.push(...this.childViews());
- // this._palette && (eles.push(this._palette));
- // this.currentStroke && (eles.push(this.currentStroke));
eles.push(<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />);
return eles;
}
@@ -1143,11 +1199,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
// if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey.
// otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document
- // let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale;
return <div className={"collectionfreeformview-container"}
ref={this.createDashEventsTarget}
onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
- onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu}
+ onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu}
style={{
pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
transform: this.contentScaling ? `scale(${this.contentScaling})` : "",
@@ -1155,7 +1210,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
width: this.contentScaling ? `${100 / this.contentScaling}%` : "",
height: this.contentScaling ? `${100 / this.contentScaling}%` : this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
}}>
- {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ?
+ {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
this.placeholder : this.marqueeView}
<CollectionFreeFormOverlayView elements={this.elementFunc} />
@@ -1182,7 +1237,7 @@ interface CollectionFreeFormOverlayViewProps {
@observer
class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps>{
render() {
- return this.props.elements().filter(ele => ele.bounds && ele.bounds.z).map(ele => ele.ele);
+ return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele);
}
}