aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx1637
1 files changed, 903 insertions, 734 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b9da4faa4..d33f0b5be 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,65 +1,64 @@
-import { Bezier } from "bezier-js";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { computedFn } from "mobx-utils";
-import { DateField } from "../../../../fields/DateField";
-import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField";
-import { List } from "../../../../fields/List";
-import { ObjectField } from "../../../../fields/ObjectField";
-import { RichTextField } from "../../../../fields/RichTextField";
-import { listSpec } from "../../../../fields/Schema";
-import { ScriptField } from "../../../../fields/ScriptField";
-import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
-import { ImageField } from "../../../../fields/URLField";
-import { TraceMobx } from "../../../../fields/util";
-import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
-import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils";
-import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
-import { DocServer } from "../../../DocServer";
-import { Docs, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { DragManager, dropActionType } from "../../../util/DragManager";
-import { HistoryUtil } from "../../../util/History";
-import { InteractionUtils } from "../../../util/InteractionUtils";
-import { LinkManager } from "../../../util/LinkManager";
-import { RecordingApi } from "../../../util/RecordingApi";
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
-import { SearchUtil } from "../../../util/SearchUtil";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { ColorScheme } from "../../../util/SettingsManager";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import { COLLECTION_BORDER_WIDTH } from "../../../views/global/globalCssVariables.scss";
-import { Timeline } from "../../animationtimeline/Timeline";
-import { ContextMenu } from "../../ContextMenu";
-import { GestureOverlay } from "../../GestureOverlay";
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from "../../InkingStroke";
-import { LightboxView } from "../../LightboxView";
-import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
-import { FieldViewProps } from "../../nodes/FieldView";
-import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PresBox } from "../../nodes/trails/PresBox";
-import { VideoBox } from "../../nodes/VideoBox";
-import { CreateImage } from "../../nodes/WebBoxRenderer";
-import { StyleProp } from "../../StyleProvider";
-import { CollectionDockingView } from "../CollectionDockingView";
-import { CollectionSubView } from "../CollectionSubView";
-import { TreeViewType } from "../CollectionTreeView";
-import { CollectionViewType } from "../CollectionView";
-import { TabDocView } from "../TabDocView";
-import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
-import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
-import "./CollectionFreeFormView.scss";
-import { MarqueeView } from "./MarqueeView";
-import React = require("react");
-import e = require("connect-flash");
-
+import { Bezier } from 'bezier-js';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
+import { DateField } from '../../../../fields/DateField';
+import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
+import { ObjectField } from '../../../../fields/ObjectField';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { listSpec } from '../../../../fields/Schema';
+import { ScriptField } from '../../../../fields/ScriptField';
+import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { TraceMobx } from '../../../../fields/util';
+import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
+import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
+import { DocServer } from '../../../DocServer';
+import { Docs, DocUtils } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { DragManager, dropActionType } from '../../../util/DragManager';
+import { HistoryUtil } from '../../../util/History';
+import { InteractionUtils } from '../../../util/InteractionUtils';
+import { LinkManager } from '../../../util/LinkManager';
+import { RecordingApi } from '../../../util/RecordingApi';
+import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
+import { SearchUtil } from '../../../util/SearchUtil';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { ColorScheme } from '../../../util/SettingsManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariables.scss';
+import { Timeline } from '../../animationtimeline/Timeline';
+import { ContextMenu } from '../../ContextMenu';
+import { GestureOverlay } from '../../GestureOverlay';
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { LightboxView } from '../../LightboxView';
+import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from '../../nodes/DocumentView';
+import { FieldViewProps } from '../../nodes/FieldView';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { PresBox } from '../../nodes/trails/PresBox';
+import { VideoBox } from '../../nodes/VideoBox';
+import { CreateImage } from '../../nodes/WebBoxRenderer';
+import { StyleProp } from '../../StyleProvider';
+import { CollectionDockingView } from '../CollectionDockingView';
+import { CollectionSubView } from '../CollectionSubView';
+import { TreeViewType } from '../CollectionTreeView';
+import { CollectionViewType } from '../CollectionView';
+import { TabDocView } from '../TabDocView';
+import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
+import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
+import './CollectionFreeFormView.scss';
+import { MarqueeView } from './MarqueeView';
+import React = require('react');
+import e = require('connect-flash');
export type collectionFreeformViewProps = {
annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -69,13 +68,15 @@ export type collectionFreeformViewProps = {
noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
engineProps?: any;
dontScaleFilter?: (doc: Doc) => boolean; // whether this collection should scale documents to fit their panel vs just scrolling them
- dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are transparent or not.
+ dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are transparent or not.
// However, this screws up interactions since only the top layer gets events. so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents.
};
@observer
export class CollectionFreeFormView extends CollectionSubView<Partial<collectionFreeformViewProps>>() {
- public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive
+ public get displayName() {
+ return 'CollectionFreeFormView(' + this.props.Document.title?.toString() + ')';
+ } // this makes mobx trace() statements more descriptive
private _lastNudge: any;
private _lastX: number = 0;
@@ -90,27 +91,33 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _disposers: { [name: string]: IReactionDisposer } = {};
private _renderCutoffData = observable.map<string, boolean>();
private _layoutPoolData = observable.map<string, PoolData>();
- private _layoutSizeData = observable.map<string, { width?: number, height?: number }>();
+ private _layoutSizeData = observable.map<string, { width?: number; height?: number }>();
private _cachedPool: Map<string, PoolData> = new Map();
private _lastTap = 0;
private _batch: UndoManager.Batch | undefined = undefined;
// private isWritingMode: boolean = true;
- // private writingModeDocs: Doc[] = [];
+ // private writingModeDocs: Doc[] = [];
- private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
- private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
- private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
+ private get isAnnotationOverlay() {
+ return this.props.isAnnotationOverlay;
+ }
+ private get scaleFieldKey() {
+ return this.props.scaleField || '_viewScale';
+ }
+ private get borderWidth() {
+ return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH;
+ }
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
- @observable _viewTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
+ @observable _viewTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
@observable _hLines: number[] | undefined;
@observable _vLines: number[] | undefined;
@observable _firstRender = true; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown.
@observable _pullCoords: number[] = [0, 0];
- @observable _pullDirection: string = "";
+ @observable _pullDirection: string = '';
@observable _showAnimTimeline = false;
- @observable _clusterSets: (Doc[])[] = [];
+ @observable _clusterSets: Doc[][] = [];
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeRef = React.createRef<HTMLDivElement>();
@@ -118,43 +125,57 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _keyframeEditing = false;
@observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
- @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
+ @computed get views() {
+ return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele);
+ }
@computed get fitToContentVals() {
return {
bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 },
- scale: !this.childDocs.length ? 1 :
- Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y),
- this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x))
+ scale: !this.childDocs.length ? 1 : Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)),
};
}
- @computed get fitContentsToBox() { return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay; }
- @computed get contentBounds() {
- const cb = Cast(this.rootDoc.contentBounds, listSpec("number"));
- return cb ? {x:cb[0], y:cb[1], r:cb[2], b: cb[3]} :
- this.props.contentBounds?.() ?? aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10));
+ @computed get fitContentsToBox() {
+ return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay;
+ }
+ @computed get contentBounds() {
+ const cb = Cast(this.rootDoc.contentBounds, listSpec('number'));
+ return cb
+ ? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
+ : this.props.contentBounds?.() ??
+ aggregateBounds(
+ this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!),
+ NumCast(this.layoutDoc._xPadding, 10),
+ NumCast(this.layoutDoc._yPadding, 10)
+ );
+ }
+ @computed get nativeWidth() {
+ return this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+ }
+ @computed get nativeHeight() {
+ return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
}
- @computed get nativeWidth() { return this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); }
- @computed get nativeHeight() { return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); }
@computed get cachedCenteringShiftX(): number {
const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
- return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
+ return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
}
@computed get cachedCenteringShiftY(): number {
const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
- return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling;// shift so pan position is at center of window for non-overlay collections
+ return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
}
@computed get cachedGetLocalTransform(): Transform {
- return Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
+ return Transform.Identity()
+ .scale(1 / this.zoomScaling())
+ .translate(this.panX(), this.panY());
}
@computed get cachedGetContainerTransform(): Transform {
return this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
}
@computed get cachedGetTransform(): Transform {
- return this.getContainerTransform().translate(- this.cachedCenteringShiftX, - this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
+ return this.getContainerTransform().translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
}
- changeKeyFrame = (back=false) => {
- const currentFrame = Cast(this.Document._currentFrame, "number", null);
+ changeKeyFrame = (back = false) => {
+ const currentFrame = Cast(this.Document._currentFrame, 'number', null);
if (currentFrame === undefined) {
this.Document._currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
@@ -167,8 +188,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.Document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame));
}
- }
- @action setKeyFrameEditing = (set: boolean) => this._keyframeEditing = set;
+ };
+ @action setKeyFrameEditing = (set: boolean) => (this._keyframeEditing = set);
getKeyFrameEditing = () => this._keyframeEditing;
onBrowseClickHandler = () => this.props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick);
onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
@@ -179,32 +200,35 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.layoutDoc._panX = vals.bounds.cx;
this.layoutDoc._panY = vals.bounds.cy;
this.layoutDoc._viewScale = vals.scale;
- }
- freeformData = (force?: boolean) => !this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined;
- reverseNativeScaling = () => this.fitContentsToBox ? true : false;
+ };
+ freeformData = (force?: boolean) => (!this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined);
+ reverseNativeScaling = () => (this.fitContentsToBox ? true : false);
// panx, pany, zoomscale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
- // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
+ // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1));
panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1));
zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1));
- contentTransform = () => !this.cachedCenteringShiftX && !this.cachedCenteringShiftY && this.zoomScaling() === 1 ? "" : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
+ contentTransform = () =>
+ !this.cachedCenteringShiftX && !this.cachedCenteringShiftY && this.zoomScaling() === 1
+ ? ''
+ : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
getTransform = () => this.cachedGetTransform.copy();
getLocalTransform = () => this.cachedGetLocalTransform.copy();
getContainerTransform = () => this.cachedGetContainerTransform.copy();
getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
isAnyChildContentActive = () => this.props.isAnyChildContentActive();
addLiveTextBox = (newBox: Doc) => {
- FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+ FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
this.addDocument(newBox);
- }
+ };
selectDocuments = (docs: Doc[]) => {
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectView(dv, true));
- }
+ };
addDocument = (newBox: Doc | Doc[]) => {
let retVal = false;
if (newBox instanceof Doc) {
- if (retVal = (this.props.addDocument?.(newBox) || false)) {
+ if ((retVal = this.props.addDocument?.(newBox) || false)) {
this.bringToFront(newBox);
this.updateCluster(newBox);
}
@@ -213,14 +237,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// bcz: deal with clusters
}
if (retVal) {
- const newBoxes = (newBox instanceof Doc) ? [newBox] : newBox;
+ const newBoxes = newBox instanceof Doc ? [newBox] : newBox;
for (const newBox of newBoxes) {
if (newBox.activeFrame !== undefined) {
const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field]);
CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field}-indexed`]);
CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field]);
delete newBox.activeFrame;
- CollectionFreeFormDocumentView.animFields.forEach((field, i) => field !== "opacity" && (newBox[field] = vals[i]));
+ CollectionFreeFormDocumentView.animFields.forEach((field, i) => field !== 'opacity' && (newBox[field] = vals[i]));
}
}
if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
@@ -228,13 +252,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
return retVal;
- }
+ };
isCurrent(doc: Doc) {
const dispTime = NumCast(doc._timecodeToShow, -1);
const endTime = NumCast(doc._timecodeToHide, dispTime + 1.5);
const curTime = NumCast(this.Document._currentTimecode, -1);
- return dispTime === -1 || ((curTime - dispTime) >= -1e-4 && curTime <= endTime);
+ return dispTime === -1 || (curTime - dispTime >= -1e-4 && curTime <= endTime);
}
@action
@@ -246,8 +270,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const z = NumCast(refDoc.z);
const x = (z ? xpo : xp) - docDragData.offset[0];
const y = (z ? ypo : yp) - docDragData.offset[1];
- const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- zsorted.forEach((doc, index) => doc.zIndex = doc.isInkMask ? 5000 : index + 1);
+ const zsorted = this.childLayoutPairs
+ .map(pair => pair.layout)
+ .slice()
+ .sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
+ zsorted.forEach((doc, index) => (doc.zIndex = doc.isInkMask ? 5000 : index + 1));
const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
const dropPos = this.Document._currentFrame !== undefined ? [dvals.x || 0, dvals.y || 0] : [NumCast(refDoc.x), NumCast(refDoc.y)];
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
@@ -267,8 +294,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
d._lastModified = new DateField();
const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
layoutDoc._width = NumCast(layoutDoc._width, 300);
- layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
- (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged(): d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+ layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? (nd[1] / nd[0]) * NumCast(layoutDoc._width) : 300);
+ (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
}
(docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -292,13 +319,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@undoBatch
internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) {
- if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) { // dragged document is a child of this collection
- if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.context !== this.props.Document) { // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document
- const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
+ if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) {
+ // dragged document is a child of this collection
+ if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.context !== this.props.Document) {
+ // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document
+ const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' });
this.props.addDocument?.(source);
- de.complete.linkDocument = DocUtils.MakeLink({ doc: linkDragData.linkSourceGetAnchor() }, { doc: source }, "annotated by:annotation of", ""); // TODODO this is where in text links get passed
+ de.complete.linkDocument = DocUtils.MakeLink({ doc: linkDragData.linkSourceGetAnchor() }, { doc: source }, 'annotated by:annotation of', ''); // TODODO this is where in text links get passed
}
- e.stopPropagation(); // do nothing if link is dropped into any freeform view parent of dragged document
+ e.stopPropagation(); // do nothing if link is dropped into any freeform view parent of dragged document
return true;
}
return false;
@@ -310,25 +339,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
else if (de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
else if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp);
return false;
- }
+ };
onExternalDrop = (e: React.DragEvent) => {
return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY));
- }
+ };
pickCluster(probe: number[]) {
- return this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => {
- const grouping = this.props.Document._useClusters ? NumCast(cd.cluster, -1) : NumCast(cd.group, -1);
- if (grouping !== -1) {
- const layoutDoc = Doc.Layout(cd);
- const cx = NumCast(cd.x) - this._clusterDistance;
- const cy = NumCast(cd.y) - this._clusterDistance;
- const cw = NumCast(layoutDoc._width) + 2 * this._clusterDistance;
- const ch = NumCast(layoutDoc._height) + 2 * this._clusterDistance;
- return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
- }
- return cluster;
- }, -1);
+ return this.childLayoutPairs
+ .map(pair => pair.layout)
+ .reduce((cluster, cd) => {
+ const grouping = this.props.Document._useClusters ? NumCast(cd.cluster, -1) : NumCast(cd.group, -1);
+ if (grouping !== -1) {
+ const layoutDoc = Doc.Layout(cd);
+ const cx = NumCast(cd.x) - this._clusterDistance;
+ const cy = NumCast(cd.y) - this._clusterDistance;
+ const cw = NumCast(layoutDoc._width) + 2 * this._clusterDistance;
+ const ch = NumCast(layoutDoc._height) + 2 * this._clusterDistance;
+ return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
+ }
+ return cluster;
+ }, -1);
}
tryDragCluster(e: PointerEvent | TouchEvent, cluster: number) {
@@ -338,10 +369,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === cluster);
const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
- const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? "alias" : undefined);
+ const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'alias' : undefined);
de.moveDocument = this.props.moveDocument;
de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
- DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, ptsParent.clientX, ptsParent.clientY, { hideSource: !de.dropAction });
+ DragManager.StartDocumentDrag(
+ clusterDocs.map(v => v.ContentDiv!),
+ de,
+ ptsParent.clientX,
+ ptsParent.clientY,
+ { hideSource: !de.dropAction }
+ );
return true;
}
}
@@ -364,12 +401,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const docFirst = docs[0];
docs.map(doc => this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)));
const preferredInd = NumCast(docFirst.cluster);
- docs.map(doc => doc.cluster = -1);
- docs.map(doc => this._clusterSets.map((set, i) => set.map(member => {
- if (docFirst.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
- docFirst.cluster = i;
- }
- })));
+ docs.map(doc => (doc.cluster = -1));
+ docs.map(doc =>
+ this._clusterSets.map((set, i) =>
+ set.map(member => {
+ if (docFirst.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+ docFirst.cluster = i;
+ }
+ })
+ )
+ );
if (docFirst.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
docFirst.cluster = preferredInd;
}
@@ -385,7 +426,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
});
} else if (this._clusterSets.length) {
for (let i = this._clusterSets.length; i <= NumCast(docFirst.cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
- docs.map(doc => this._clusterSets[doc.cluster = NumCast(docFirst.cluster)].push(doc));
+ 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));
}
@@ -399,11 +440,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
const preferredInd = NumCast(doc.cluster);
doc.cluster = -1;
- this._clusterSets.forEach((set, i) => set.forEach(member => {
- if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
- doc.cluster = i;
- }
- }));
+ this._clusterSets.forEach((set, i) =>
+ set.forEach(member => {
+ if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+ doc.cluster = i;
+ }
+ })
+ );
if (doc.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
doc.cluster = preferredInd;
}
@@ -423,7 +466,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
- let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
+ let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
if (property !== StyleProp.BackgroundColor) return styleProp;
const cluster = NumCast(doc?.cluster);
if (this.Document._useClusters) {
@@ -431,15 +474,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
setTimeout(() => doc && this.updateCluster(doc));
} else {
// choose a cluster color from a palette
- 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)"];
+ 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)'];
styleProp = colors[cluster % colors.length];
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?.map(s => styleProp = StrCast(s.backgroundColor));
+ set?.map(s => (styleProp = StrCast(s.backgroundColor)));
}
} //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
return styleProp;
- }
+ };
trySelectCluster = (addToSel: boolean) => {
if (this._hitCluster !== -1) {
@@ -449,30 +492,32 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return true;
}
return false;
- }
+ };
@action
onPenUp = (e: PointerEvent): void => {
if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- document.removeEventListener("pointerup", this.onPenUp);
- const currentCol = DocListCast(this.rootDoc.currentInkDoc)
+ document.removeEventListener('pointerup', this.onPenUp);
+ const currentCol = DocListCast(this.rootDoc.currentInkDoc);
const rootDocList = DocListCast(this.rootDoc.data);
currentCol.push(rootDocList[rootDocList.length - 1]);
console.log(currentCol);
this._batch?.end();
}
- }
+ };
@action
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
- if (!e.nativeEvent.cancelBubble &&
+ if (
+ !e.nativeEvent.cancelBubble &&
!this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
- !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+ !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)
+ ) {
switch (CurrentUserUtils.ActiveTool) {
case InkTool.Highlighter:
break;
@@ -482,23 +527,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case InkTool.Pen:
break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
case InkTool.Eraser:
- document.addEventListener("pointermove", this.onEraserMove);
- document.addEventListener("pointerup", this.onEraserUp);
- this._batch = UndoManager.StartBatch("collectionErase");
+ document.addEventListener('pointermove', this.onEraserMove);
+ document.addEventListener('pointerup', this.onEraserUp);
+ this._batch = UndoManager.StartBatch('collectionErase');
e.stopPropagation();
e.preventDefault();
break;
case InkTool.None:
if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
- document.addEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointerup", this.onPointerUp);
+ document.addEventListener('pointermove', this.onPointerMove);
+ document.addEventListener('pointerup', this.onPointerUp);
}
break;
}
}
}
- }
+ };
@action
handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
@@ -517,14 +562,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._lastY = pt.pageY;
e.preventDefault();
e.stopPropagation();
- }
- else {
+ } else {
e.preventDefault();
}
}
}
}
- }
+ };
public unprocessedDocs: Doc[] = [];
public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
@@ -534,8 +578,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case GestureUtils.Gestures.Stroke:
const points = ge.points;
const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
- { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
+ const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, {
+ title: 'ink stroke',
+ x: B.x - ActiveInkWidth() / 2,
+ y: B.y - ActiveInkWidth() / 2,
+ _width: B.width + ActiveInkWidth(),
+ _height: B.height + ActiveInkWidth(),
+ });
if (CurrentUserUtils.ActiveTool === InkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
@@ -561,7 +610,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
return pass;
});
- this.addDocument(Docs.Create.FreeformDocument(sel, { title: "nested collection", x: bounds.x, y: bounds.y, _width: bWidth, _height: bHeight, _panX: 0, _panY: 0 }));
+ this.addDocument(Docs.Create.FreeformDocument(sel, { title: 'nested collection', x: bounds.x, y: bounds.y, _width: bWidth, _height: bHeight, _panX: 0, _panY: 0 }));
sel.forEach(d => this.props.removeDocument?.(d));
e.stopPropagation();
break;
@@ -573,17 +622,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case GestureUtils.Gestures.EndBracket:
if (this._inkToTextStartX && this._inkToTextStartY) {
const end = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y)));
- const setDocs = this.getActiveDocuments().filter(s => s.proto?.type === "rtf" && s.color);
- const sets = setDocs.map((sd) => {
+ const setDocs = this.getActiveDocuments().filter(s => s.proto?.type === 'rtf' && s.color);
+ const sets = setDocs.map(sd => {
return Cast(sd.text, RichTextField)?.Text as string;
});
if (sets.length && sets[0]) {
this._wordPalette.clear();
const colors = setDocs.map(sd => FieldValue(sd.color) as string);
- sets.forEach((st: string, i: number) => st.split(",").forEach(word => this._wordPalette.set(word, colors[i])));
+ sets.forEach((st: string, i: number) => st.split(',').forEach(word => this._wordPalette.set(word, colors[i])));
}
const inks = this.getActiveDocuments().filter(doc => {
- if (doc.type === "ink") {
+ if (doc.type === 'ink') {
const l = NumCast(doc.x);
const r = l + doc[WidthSym]();
const t = NumCast(doc.y);
@@ -599,15 +648,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const d = Cast(i.data, InkField);
const x = NumCast(i.x);
const y = NumCast(i.y);
- const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
- const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
+ const left = Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
+ const top = Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
if (d) {
strokes.push(d.inkData.map(pd => ({ X: pd.X + x - left, Y: pd.Y + y - top })));
}
});
- CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
- const wordResults = results.filter((r: any) => r.category === "inkWord");
+ CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then(results => {
+ const wordResults = results.filter((r: any) => r.category === 'inkWord');
for (const word of wordResults) {
const indices: number[] = word.strokeIds;
indices.forEach(i => {
@@ -619,8 +668,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
inks[i].alternativeColors = new List<string>(uniqueColors);
if (this._wordPalette.has(word.recognizedText.toLowerCase())) {
inks[i].color = this._wordPalette.get(word.recognizedText.toLowerCase());
- }
- else if (word.alternates) {
+ } else if (word.alternates) {
for (const alt of word.alternates) {
if (this._wordPalette.has(alt.recognizedString.toLowerCase())) {
inks[i].color = this._wordPalette.get(alt.recognizedString.toLowerCase());
@@ -641,27 +689,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation();
}
}
- }
+ };
@action
onEraserUp = (e: PointerEvent): void => {
if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- document.removeEventListener("pointermove", this.onEraserMove);
- document.removeEventListener("pointerup", this.onEraserUp);
+ document.removeEventListener('pointermove', this.onEraserMove);
+ document.removeEventListener('pointerup', this.onEraserUp);
this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
this._deleteList = [];
this._batch?.end();
}
- }
+ };
@action
onPointerUp = (e: PointerEvent): void => {
if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener('pointermove', this.onPointerMove);
+ document.removeEventListener('pointerup', this.onPointerUp);
this.removeMoveListeners();
this.removeEndListeners();
}
- }
+ };
onClick = (e: React.MouseEvent) => {
if (this.onBrowseClickHandler()) {
@@ -670,10 +718,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
e.stopPropagation();
e.preventDefault();
- }
- else if ((Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) {
+ } else if (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3) {
if (e.shiftKey) {
- if (Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a shift + double click
+ if (Date.now() - this._lastTap < 300) {
+ // reset zoom of freeform view to 1-to-1 on a shift + double click
this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1);
}
e.stopPropagation();
@@ -681,15 +729,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
this._lastTap = Date.now();
}
- }
+ };
@action
- pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
+ pan = (e: PointerEvent | React.Touch | { clientX: number; clientY: number }): void => {
const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
this.setPan(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true);
this._lastX = e.clientX;
this._lastY = e.clientY;
- }
+ };
/**
* Erases strokes by intersecting them with an invisible "eraser stroke".
@@ -703,14 +751,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.getEraserIntersections({ X: this._lastX, Y: this._lastY }, currPoint).forEach(intersect => {
if (!this._deleteList.includes(intersect.inkView)) {
this._deleteList.push(intersect.inkView);
- SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || "1");
- SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || "black");
+ SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || '1');
+ SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black');
// create a new curve by appending all curves of the current segment together in order to render a single new stroke.
- !e.shiftKey && this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
- GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke,
- segment.reduce((data, curve) => [...data, ...curve.points
- .map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })
- ], [] as PointData[])));
+ !e.shiftKey &&
+ this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
+ GestureOverlay.Instance.dispatchGesture(
+ GestureUtils.Gestures.Stroke,
+ segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
+ )
+ );
// Lower ink opacity to give the user a visual indicator of deletion.
intersect.inkView.layoutDoc.opacity = 0.5;
}
@@ -720,7 +770,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
- }
+ };
@action
onPointerMove = (e: PointerEvent): void => {
@@ -730,45 +780,54 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (this.props.isContentActive(true)) e.stopPropagation();
} else if (!e.cancelBubble) {
if (this.tryDragCluster(e, this._hitCluster)) {
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- }
- else this.pan(e);
+ document.removeEventListener('pointermove', this.onPointerMove);
+ document.removeEventListener('pointerup', this.onPointerUp);
+ } else this.pan(e);
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
}
- }
+ };
/**
* Determines if the Eraser tool has intersected with an ink stroke in the current freeform collection.
* @returns an array of tuples containing the intersected ink DocumentView and the t-value where it was intersected
*/
- getEraserIntersections = (lastPoint: { X: number, Y: number }, currPoint: { X: number, Y: number }) => {
+ getEraserIntersections = (lastPoint: { X: number; Y: number }, currPoint: { X: number; Y: number }) => {
const eraserMin = { X: Math.min(lastPoint.X, currPoint.X), Y: Math.min(lastPoint.Y, currPoint.Y) };
const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) };
return this.childDocs
.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView))
.filter(inkView => inkView?.ComponentView instanceof InkingStroke)
.map(inkView => ({ inkViewBounds: inkView!.getBounds(), inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
- .filter(({ inkViewBounds }) => inkViewBounds && // bounding box of eraser segment and ink stroke overlap
- eraserMin.X <= inkViewBounds.right && eraserMin.Y <= inkViewBounds.bottom &&
- eraserMax.X >= inkViewBounds.left && eraserMax.Y >= inkViewBounds.top)
+ .filter(
+ ({ inkViewBounds }) =>
+ inkViewBounds && // bounding box of eraser segment and ink stroke overlap
+ eraserMin.X <= inkViewBounds.right &&
+ eraserMin.Y <= inkViewBounds.bottom &&
+ eraserMax.X >= inkViewBounds.left &&
+ eraserMax.Y >= inkViewBounds.top
+ )
.reduce((intersections, { inkStroke, inkView }) => {
const { inkData } = inkStroke.inkScaledData();
// Convert from screen space to ink space for the intersection.
const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint);
const currPointInkSpace = inkStroke.ptFromScreen(currPoint);
for (var i = 0; i < inkData.length - 3; i += 4) {
- const intersects = Array.from(new Set(InkField.Segment(inkData, i).intersects({ // compute all unique intersections
- p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
- p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y }
- }) as (number | string)[])); // convert to more manageable union array type
+ const intersects = Array.from(
+ new Set(
+ InkField.Segment(inkData, i).intersects({
+ // compute all unique intersections
+ p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
+ p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y },
+ }) as (number | string)[]
+ )
+ ); // convert to more manageable union array type
// return tuples of the inkingStroke intersected, and the t value of the intersection
- intersections.push(...intersects.map(t => ({ inkView, t: (+t) + Math.floor(i / 4) })));// convert string t's to numbers and add start of curve segment to convert from local t value to t value along complete curve
+ intersections.push(...intersects.map(t => ({ inkView, t: +t + Math.floor(i / 4) }))); // convert string t's to numbers and add start of curve segment to convert from local t value to t value along complete curve
}
return intersections;
- }, [] as { t: number, inkView: DocumentView }[]);
- }
+ }, [] as { t: number; inkView: DocumentView }[]);
+ };
/**
* Performs segmentation of the ink stroke - creates "segments" or subsections of the current ink stroke at points in which the
@@ -805,11 +864,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
segment.push(inkSegment);
}
}
- if (excludeT < startSegmentT || excludeT > (inkData.length / 4)) {
+ if (excludeT < startSegmentT || excludeT > inkData.length / 4) {
segment.length && segments.push(segment);
}
return segments;
- }
+ };
/**
* Determines all possible intersections of the current curve of the intersected ink stroke with all other curves of all
@@ -837,13 +896,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const otherCurve = new Bezier(otherCtrlPts.slice(j, j + 4).map(p => ({ x: p.X, y: p.Y })));
curve.intersects(otherCurve).forEach((val: string | number, i: number) => {
// Converting the Bezier.js Split type to a t-value number.
- const t = +val.toString().split("/")[0];
- if (i % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack! don't know why but intersection points are doubled from bezier.js (but not identical).
+ const t = +val.toString().split('/')[0];
+ if (i % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack! don't know why but intersection points are doubled from bezier.js (but not identical).
});
}
});
return tVals;
- }
+ };
handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
if (!e.cancelBubble) {
@@ -853,8 +912,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (this.tryDragCluster(e, this._hitCluster)) {
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener('pointermove', this.onPointerMove);
+ document.removeEventListener('pointerup', this.onPointerUp);
return;
}
// TODO: nda - this allows us to pan collections with finger -> only want to do this when collection is selected'
@@ -864,7 +923,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// e.stopPropagation();
e.preventDefault();
}
- }
+ };
handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
// pinch zooming
@@ -887,7 +946,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
// calculate the raw delta value
- const rawDelta = (dir * (d1 + d2));
+ const rawDelta = dir * (d1 + d2);
// this floors and ceils the delta value to prevent jitteriness
const delta = Math.sign(rawDelta) * Math.min(Math.abs(rawDelta), 8);
@@ -902,7 +961,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
// const transformed = this.getTransform().inverse().transformPoint(centerX, centerY);
- if (!this._pullDirection) { // if we are not bezel movement
+ if (!this._pullDirection) {
+ // if we are not bezel movement
this.pan({ clientX: centerX, clientY: centerY });
} else {
this._pullCoords = [centerX, centerY];
@@ -916,7 +976,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// e.stopPropagation();
e.preventDefault();
}
- }
+ };
@action
handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
@@ -934,21 +994,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._lastY = centerY;
const screenBox = this._mainCont?.getBoundingClientRect();
-
// determine if we are using a bezel movement
if (screenBox) {
- if ((screenBox.right - centerX) < 100) {
+ if (screenBox.right - centerX < 100) {
this._pullCoords = [centerX, centerY];
- this._pullDirection = "right";
+ this._pullDirection = 'right';
} else if (centerX - screenBox.left < 100) {
this._pullCoords = [centerX, centerY];
- this._pullDirection = "left";
+ this._pullDirection = 'left';
} else if (screenBox.bottom - centerY < 100) {
this._pullCoords = [centerX, centerY];
- this._pullDirection = "bottom";
+ this._pullDirection = 'bottom';
} else if (centerY - screenBox.top < 100) {
this._pullCoords = [centerX, centerY];
- this._pullDirection = "top";
+ this._pullDirection = 'top';
}
}
@@ -959,27 +1018,30 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation();
}
}
- }
+ };
cleanUpInteractions = () => {
switch (this._pullDirection) {
- case "left": case "right": case "top": case "bottom":
- CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: "New Collection" }), this._pullDirection);
+ case 'left':
+ case 'right':
+ case 'top':
+ case 'bottom':
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: 'New Collection' }), this._pullDirection);
}
- this._pullDirection = "";
+ this._pullDirection = '';
this._pullCoords = [0, 0];
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener('pointermove', this.onPointerMove);
+ document.removeEventListener('pointerup', this.onPointerUp);
this.removeMoveListeners();
this.removeEndListeners();
- }
+ };
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
if (this.Document._isGroup) return;
- let deltaScale = deltaY > 0 ? (1 / 1.05) : 1.05;
+ let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05;
if (deltaScale < 0) deltaScale = -deltaScale;
const [x, y] = this.getTransform().transformPoint(pointX, pointY);
const invTransform = this.getLocalTransform().inverse();
@@ -996,27 +1058,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
}
- }
+ };
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
- if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
+ if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline)
+ return;
+ if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
+ // things that can scroll vertically should do that instead of zooming
e.stopPropagation();
- }
- else if (this.props.isContentActive(true) && !this.Document._isGroup) {
+ } else if (this.props.isContentActive(true) && !this.Document._isGroup) {
e.stopPropagation();
e.preventDefault();
!this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
}
- }
+ };
@action
setPan(panX: number, panY: number, panTime: number = 0, clamp: boolean = false) {
// set the current respective FFview to the tab being panned.
- (Doc.UserDoc()?.presentationMode === 'recording') && RecordingApi.Instance.setRecordingFFView(this);
+ Doc.UserDoc()?.presentationMode === 'recording' && RecordingApi.Instance.setRecordingFFView(this);
// TODO: make this based off the specific recording FFView
- (Doc.UserDoc()?.presentationMode === 'none') && RecordingApi.Instance.setPlayFFView(this);
+ Doc.UserDoc()?.presentationMode === 'none' && RecordingApi.Instance.setPlayFFView(this);
if (Doc.UserDoc()?.presentationMode === 'watching') {
RecordingApi.Instance.pauseVideoAndMovements();
Doc.UserDoc().presentationMode = 'none';
@@ -1026,24 +1089,30 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!this.isAnnotationOverlay && clamp) {
// this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
- const measuredDocs = docs.map(doc => ({ pos: this.childPositionProviderUnmemoized(doc, ""), size: this.childSizeProviderUnmemoized(doc, "") }))
- .filter(({ pos, size }) => pos && size).map(({ pos, size }) => ({ pos: pos!, size: size! }));
+ const measuredDocs = docs
+ .map(doc => ({ pos: this.childPositionProviderUnmemoized(doc, ''), size: this.childSizeProviderUnmemoized(doc, '') }))
+ .filter(({ pos, size }) => pos && size)
+ .map(({ pos, size }) => ({ pos: pos!, size: size! }));
if (measuredDocs.length) {
- const ranges = measuredDocs.reduce(({ xrange, yrange }, { pos, size }) => // computes range of content
- ({
- xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
- yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) }
- })
- , {
+ const ranges = measuredDocs.reduce(
+ (
+ { xrange, yrange },
+ { pos, size } // computes range of content
+ ) => ({
+ xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
+ yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) },
+ }),
+ {
xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE },
- yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }
- });
+ yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE },
+ }
+ );
const panelDim = [this.props.PanelWidth() / this.zoomScaling(), this.props.PanelHeight() / this.zoomScaling()];
- if (ranges.xrange.min >= (panX + panelDim[0] / 2)) panX = ranges.xrange.max + panelDim[0] / 2; // snaps pan position of range of content goes out of bounds
- else if (ranges.xrange.max <= (panX - panelDim[0] / 2)) panX = ranges.xrange.min - panelDim[0] / 2;
- if (ranges.yrange.min >= (panY + panelDim[1] / 2)) panY = ranges.yrange.max + panelDim[1] / 2;
- else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
+ if (ranges.xrange.min >= panX + panelDim[0] / 2) panX = ranges.xrange.max + panelDim[0] / 2; // snaps pan position of range of content goes out of bounds
+ else if (ranges.xrange.max <= panX - panelDim[0] / 2) panX = ranges.xrange.min - panelDim[0] / 2;
+ if (ranges.yrange.min >= panY + panelDim[1] / 2) panY = ranges.yrange.max + panelDim[1] / 2;
+ else if (ranges.yrange.max <= panY - panelDim[1] / 2) panY = ranges.yrange.min - panelDim[1] / 2;
}
}
if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.Document)) {
@@ -1052,10 +1121,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
const minPanX = NumCast(this.rootDoc._panXMin, 0);
const minPanY = NumCast(this.rootDoc._panYMin, 0);
- const newPanX = Math.min(
- minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._panXMax, this.nativeWidth), Math.max(minPanX, panX));
- const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) :
- minPanY + (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, this.nativeHeight)), Math.max(minPanY, panY));
+ const newPanX = Math.min(minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._panXMax, this.nativeWidth), Math.max(minPanX, panX));
+ const newPanY = Math.min(this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : minPanY + (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, this.nativeHeight), Math.max(minPanY, panY));
!this.Document._verticalScroll && (this.Document._panX = this.isAnnotationOverlay ? newPanX : panX);
!this.Document._horizontalScroll && (this.Document._panY = this.isAnnotationOverlay ? newPanY : panY);
}
@@ -1063,16 +1130,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
nudge = (x: number, y: number, nudgeTime: number = 500) => {
- if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ||
- this.props.ContainingCollectionDoc._panX !== undefined) { // bcz: this isn't ideal, but want to try it out...
- this.setPan(NumCast(this.layoutDoc._panX) + this.props.PanelWidth() / 2 * x / this.zoomScaling(),
- NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), nudgeTime, true);
+ if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || this.props.ContainingCollectionDoc._panX !== undefined) {
+ // bcz: this isn't ideal, but want to try it out...
+ this.setPan(NumCast(this.layoutDoc._panX) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), NumCast(this.layoutDoc._panY) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(), nudgeTime, true);
this._lastNudge && clearTimeout(this._lastNudge);
- this._lastNudge = setTimeout(action(() => this._viewTransition = 0), nudgeTime);
+ this._lastNudge = setTimeout(
+ action(() => (this._viewTransition = 0)),
+ nudgeTime
+ );
return true;
}
return false;
- }
+ };
@action
bringToFront = (doc: Doc, sendToBack?: boolean) => {
@@ -1090,12 +1159,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
doc.zIndex = zlast + 1;
}
- }
+ };
@action
zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) {
if (this.Document._isGroup) return;
- setTimeout(action(() => this._viewTransition = 0), this._viewTransition = transitionTime); // set transition to be smooth, then reset
+ setTimeout(
+ action(() => (this._viewTransition = 0)),
+ (this._viewTransition = transitionTime)
+ ); // set transition to be smooth, then reset
const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
this.layoutDoc[this.scaleFieldKey] = scale;
const newScreenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
@@ -1110,7 +1182,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// TODO This technically isn't correct if type !== "doc", as
// currently nothing is done, but we should probably push a new state
- if (state.type === "doc" && this.Document._panX !== undefined && this.Document._panY !== undefined) {
+ if (state.type === 'doc' && this.Document._panX !== undefined && this.Document._panY !== undefined) {
const init = state.initializers![this.Document[Id]];
if (!init) {
state.initializers![this.Document[Id]] = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY) };
@@ -1130,9 +1202,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const xfToCollection = options?.docTransform ?? Transform.Identity();
const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoom ? this.Document[this.scaleFieldKey] : undefined };
const newState = HistoryUtil.getState();
- const cantTransform = /*this.props.isAnnotationOverlay ||*/ ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
- const { panX, panY, scale } = cantTransform ? savedState : this.calculatePanIntoView(doc, xfToCollection, options?.willZoom ? options?.scale || .75 : undefined);
- if (!cantTransform) { // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection
+ const cantTransform = /*this.props.isAnnotationOverlay ||*/ (this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc;
+ const { panX, panY, scale } = cantTransform ? savedState : this.calculatePanIntoView(doc, xfToCollection, options?.willZoom ? options?.scale || 0.75 : undefined);
+ if (!cantTransform) {
+ // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection
newState.initializers![this.Document[Id]] = { panX: panX, panY: panY };
HistoryUtil.pushState(newState);
}
@@ -1152,30 +1225,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const resetView = options?.afterFocus ? await options?.afterFocus(moved) : ViewAdjustment.doNothing;
if (resetView) {
const restoreState = (!LightboxView.LightboxDoc || LightboxView.LightboxDoc === this.props.Document) && savedState;
- if (typeof restoreState !== "boolean") {
+ if (typeof restoreState !== 'boolean') {
this.Document._panX = restoreState.panX;
this.Document._panY = restoreState.panY;
this.Document[this.scaleFieldKey] = restoreState.scale;
}
- runInAction(() => this._viewTransition = 0);
+ runInAction(() => (this._viewTransition = 0));
}
return resetView;
};
- const xf = !cantTransform ? Transform.Identity() :
- this.props.isAnnotationOverlay ?
- new Transform(NumCast(this.rootDoc.x), NumCast(this.rootDoc.y), this.rootDoc[WidthSym]() / Doc.NativeWidth(this.rootDoc))
- :
- new Transform(NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]() / 2 - NumCast(this.rootDoc._panX),
- NumCast(this.rootDoc.y) + this.rootDoc[HeightSym]() / 2 - NumCast(this.rootDoc._panY), 1);
+ const xf = !cantTransform
+ ? Transform.Identity()
+ : this.props.isAnnotationOverlay
+ ? new Transform(NumCast(this.rootDoc.x), NumCast(this.rootDoc.y), this.rootDoc[WidthSym]() / Doc.NativeWidth(this.rootDoc))
+ : new Transform(NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]() / 2 - NumCast(this.rootDoc._panX), NumCast(this.rootDoc.y) + this.rootDoc[HeightSym]() / 2 - NumCast(this.rootDoc._panY), 1);
this.props.focus(cantTransform ? doc : this.rootDoc, {
...options,
docTransform: xf,
- afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res =>
- setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime))))
+ afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime)))),
});
}
- }
+ };
calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
const layoutdoc = Doc.Layout(doc);
@@ -1185,11 +1256,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (scale) {
const maxZoom = 5; // sets the limit for how far we will zoom. this is useful for preventing small text boxes from filling the screen. So probably needs to be more sophisticated to consider more about the target and context
- const newScale = Math.min(maxZoom, 1 / (this.contentScaling || 1) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height)));
+ const newScale = Math.min(maxZoom, (1 / (this.contentScaling || 1)) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height)));
return {
panX: this.props.isAnnotationOverlay ? bounds.left - (Doc.NativeWidth(this.layoutDoc) / newScale - bounds.width) / 2 : (bounds.left + bounds.right) / 2,
panY: this.props.isAnnotationOverlay ? bounds.top - (Doc.NativeHeight(this.layoutDoc) / newScale - bounds.height) / 2 : (bounds.top + bounds.bot) / 2,
- scale: newScale
+ scale: newScale,
};
}
const pw = this.props.PanelWidth() / NumCast(this.layoutDoc._viewScale, 1);
@@ -1197,19 +1268,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const cx = NumCast(this.layoutDoc._panX);
const cy = NumCast(this.layoutDoc._panY);
const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
- if ((screen.right - screen.left) < (bounds.right - bounds.left) ||
- (screen.bot - screen.top) < (bounds.bot - bounds.top)) {
+ if (screen.right - screen.left < bounds.right - bounds.left || screen.bot - screen.top < bounds.bot - bounds.top) {
return {
panX: (bounds.left + bounds.right) / 2,
panY: (bounds.top + bounds.bot) / 2,
- scale: Math.min(this.props.PanelHeight() / (bounds.bot - bounds.top), this.props.PanelWidth() / (bounds.right - bounds.left)) / 1.1
+ scale: Math.min(this.props.PanelHeight() / (bounds.bot - bounds.top), this.props.PanelWidth() / (bounds.right - bounds.left)) / 1.1,
};
}
return {
panX: cx + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
panY: cy + Math.min(0, bounds.top - ph / 10 - screen.top) + Math.max(0, bounds.bot + ph / 10 - screen.bot),
};
- }
+ };
isContentActive = () => this.props.isSelected() || this.props.isContentActive();
@@ -1217,190 +1287,215 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
const docView = fieldProps.DocumentView?.();
- if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) {
+ if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._singleLine) && ['Tab', 'Enter'].includes(e.key)) {
e.stopPropagation?.();
- const below = !e.altKey && e.key !== "Tab";
+ const below = !e.altKey && e.key !== 'Tab';
const layoutKey = StrCast(docView.LayoutFieldKey);
const newDoc = Doc.MakeCopy(docView.rootDoc, true);
const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
if (below) newDoc.y = NumCast(docView.rootDoc.y) + NumCast(docView.rootDoc._height) + 10;
else newDoc.x = NumCast(docView.rootDoc.x) + NumCast(docView.rootDoc._width) + 10;
- if (layoutKey !== "layout" && docView.rootDoc[layoutKey] instanceof Doc) {
+ if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) {
newDoc[layoutKey] = docView.rootDoc[layoutKey];
}
Doc.GetProto(newDoc).text = undefined;
FormattedTextBox.SelectOnLoad = newDoc[Id];
return this.addDocument?.(newDoc);
}
- }
+ };
pointerEvents = () => {
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
- const pointerEvents = this.props.isContentActive() === false ? "none" :
- this.props.childPointerEvents ? "all" :
- (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : this.props.pointerEvents?.();
+ const pointerEvents = this.props.isContentActive() === false ? 'none' : this.props.childPointerEvents ? 'all' : this.props.viewDefDivClick || (engine === 'pass' && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.();
return pointerEvents;
- }
+ };
getChildDocView(entry: PoolData) {
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
- return <CollectionFreeFormDocumentView key={childLayout[Id] + (entry.replica || "")}
- DataDoc={childData}
- Document={childLayout}
- renderDepth={this.props.renderDepth + 1}
- replica={entry.replica}
- suppressSetHeight={this.layoutEngine ? true : false}
- renderCutoffProvider={this.renderCutoffProvider}
- ContainingCollectionView={this.props.CollectionView}
- ContainingCollectionDoc={this.props.Document}
- CollectionFreeFormView={this}
- LayoutTemplate={childLayout.z ? undefined : this.props.childLayoutTemplate}
- LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
- rootSelected={childData ? this.rootSelected : returnFalse}
- onClick={this.onChildClickHandler}
- onKey={this.onKeyDown}
- onDoubleClick={this.onChildDoubleClickHandler}
- onBrowseClick={this.onBrowseClickHandler}
- ScreenToLocalTransform={childLayout.z ? this.getContainerTransform : this.getTransform}
- PanelWidth={childLayout[WidthSym]}
- PanelHeight={childLayout[HeightSym]}
- docFilters={this.childDocFilters}
- docRangeFilters={this.childDocRangeFilters}
- searchFilterDocs={this.searchFilterDocs}
- isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
- isContentActive={emptyFunction}
- focus={this.focusDocument}
- addDocTab={this.addDocTab}
- addDocument={this.props.addDocument}
- removeDocument={this.props.removeDocument}
- moveDocument={this.props.moveDocument}
- pinToPres={this.props.pinToPres}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- docViewPath={this.props.docViewPath}
- styleProvider={this.getClusterColor}
- dataProvider={this.childDataProvider}
- sizeProvider={this.childSizeProvider}
- dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
- bringToFront={this.bringToFront}
- showTitle={this.props.childShowTitle}
- dontScaleFilter={this.props.dontScaleFilter}
- dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
- pointerEvents={this.pointerEvents}
- jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
- //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
- />;
+ return (
+ <CollectionFreeFormDocumentView
+ key={childLayout[Id] + (entry.replica || '')}
+ DataDoc={childData}
+ Document={childLayout}
+ renderDepth={this.props.renderDepth + 1}
+ replica={entry.replica}
+ suppressSetHeight={this.layoutEngine ? true : false}
+ renderCutoffProvider={this.renderCutoffProvider}
+ ContainingCollectionView={this.props.CollectionView}
+ ContainingCollectionDoc={this.props.Document}
+ CollectionFreeFormView={this}
+ LayoutTemplate={childLayout.z ? undefined : this.props.childLayoutTemplate}
+ LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
+ rootSelected={childData ? this.rootSelected : returnFalse}
+ onClick={this.onChildClickHandler}
+ onKey={this.onKeyDown}
+ onDoubleClick={this.onChildDoubleClickHandler}
+ onBrowseClick={this.onBrowseClickHandler}
+ ScreenToLocalTransform={childLayout.z ? this.getContainerTransform : this.getTransform}
+ PanelWidth={childLayout[WidthSym]}
+ PanelHeight={childLayout[HeightSym]}
+ docFilters={this.childDocFilters}
+ docRangeFilters={this.childDocRangeFilters}
+ searchFilterDocs={this.searchFilterDocs}
+ isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
+ isContentActive={emptyFunction}
+ focus={this.focusDocument}
+ addDocTab={this.addDocTab}
+ addDocument={this.props.addDocument}
+ removeDocument={this.props.removeDocument}
+ moveDocument={this.props.moveDocument}
+ pinToPres={this.props.pinToPres}
+ whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+ docViewPath={this.props.docViewPath}
+ styleProvider={this.getClusterColor}
+ dataProvider={this.childDataProvider}
+ sizeProvider={this.childSizeProvider}
+ dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+ bringToFront={this.bringToFront}
+ showTitle={this.props.childShowTitle}
+ dontScaleFilter={this.props.dontScaleFilter}
+ dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
+ pointerEvents={this.pointerEvents}
+ jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
+ //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
+ />
+ );
}
addDocTab = action((doc: Doc, where: string) => {
- if (where === "inParent") {
- ((doc instanceof Doc) ? [doc] : doc).forEach(doc => {
+ if (where === 'inParent') {
+ (doc instanceof Doc ? [doc] : doc).forEach(doc => {
const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
doc.x = pt[0];
doc.y = pt[1];
});
return this.props.addDocument?.(doc) || false;
}
- if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+ if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) {
this.dataDoc[this.props.fieldKey] = doc instanceof Doc ? doc : new List<Doc>(doc as any as Doc[]);
return true;
}
return this.props.addDocTab(doc, where);
});
- getCalculatedPositions(params: { pair: { layout: Doc, data?: Doc }, index: number, collection: Doc }): PoolData {
+ getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData {
const layoutDoc = Doc.Layout(params.pair.layout);
const { z, color, zIndex } = params.pair.layout;
- const { x, y, opacity } = this.Document._currentFrame === undefined ?
- { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) } :
- CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame));
+ const { x, y, opacity } =
+ this.Document._currentFrame === undefined
+ ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) }
+ : CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame));
return {
- x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
- transition: StrCast(layoutDoc.dataTransition), opacity: this._keyframeEditing ? 1 : Cast(opacity, "number", null),
- width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number"), pair: params.pair, replica: ""
+ x: NumCast(x),
+ y: NumCast(y),
+ z: Cast(z, 'number'),
+ color: StrCast(color),
+ zIndex: Cast(zIndex, 'number'),
+ transition: StrCast(layoutDoc.dataTransition),
+ opacity: this._keyframeEditing ? 1 : Cast(opacity, 'number', null),
+ width: Cast(layoutDoc._width, 'number'),
+ height: Cast(layoutDoc._height, 'number'),
+ pair: params.pair,
+ replica: '',
};
}
onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
(this.props.viewDefDivClick || ScriptCast(this.props.Document.onViewDefDivClick))?.script.run({ this: this.props.Document, payload });
e.stopPropagation();
- }
+ };
viewDefsToJSX = (views: ViewDefBounds[]) => {
return !Array.isArray(views) ? [] : views.filter(ele => this.viewDefToJSX(ele)).map(ele => this.viewDefToJSX(ele)!);
- }
+ };
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 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 fontSize = Cast(viewDef.fontSize, "string");
- return [text, x, y].some(val => val === undefined) ? undefined :
- {
- ele: <div className="collectionFreeform-customText" key={(text || "") + x + y + z + color} style={{ width, height, color, fontSize, transform }}>
- {text}
- </div>,
- 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 + viewDef.payload} onClick={e => this.onViewDefDivClick(e, viewDef)}
- style={{ width, height, backgroundColor: color, transform }} />,
- bounds: viewDef
- };
+ 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 fontSize = Cast(viewDef.fontSize, 'string');
+ return [text, x, y].some(val => val === undefined)
+ ? undefined
+ : {
+ ele: (
+ <div className="collectionFreeform-customText" key={(text || '') + x + y + z + color} style={{ width, height, color, fontSize, transform }}>
+ {text}
+ </div>
+ ),
+ 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 + viewDef.payload}
+ onClick={e => this.onViewDefDivClick(e, viewDef)}
+ style={{ width, height, backgroundColor: color, transform }}
+ />
+ ),
+ bounds: viewDef,
+ };
}
}
-
- renderCutoffProvider = computedFn(function renderCutoffProvider(this: any, doc: Doc) {
- return !this._renderCutoffData.get(doc[Id] + "");
- }.bind(this));
-
+ renderCutoffProvider = computedFn(
+ function renderCutoffProvider(this: any, doc: Doc) {
+ return !this._renderCutoffData.get(doc[Id] + '');
+ }.bind(this)
+ );
childPositionProviderUnmemoized = (doc: Doc, replica: string) => {
- return this._layoutPoolData.get(doc[Id] + (replica || ""));
- }
- childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc, replica: string) {
- return this._layoutPoolData.get(doc[Id] + (replica || ""));
- }.bind(this));
+ return this._layoutPoolData.get(doc[Id] + (replica || ''));
+ };
+ childDataProvider = computedFn(
+ function childDataProvider(this: any, doc: Doc, replica: string) {
+ return this._layoutPoolData.get(doc[Id] + (replica || ''));
+ }.bind(this)
+ );
childSizeProviderUnmemoized = (doc: Doc, replica: string) => {
- return this._layoutSizeData.get(doc[Id] + (replica || ""));
- }
- childSizeProvider = computedFn(function childSizeProvider(this: any, doc: Doc, replica: string) {
- return this._layoutSizeData.get(doc[Id] + (replica || ""));
- }.bind(this));
-
- doEngineLayout(poolData: Map<string, PoolData>,
- engine: (
- poolData: Map<string, PoolData>,
- pivotDoc: Doc,
- childPairs: { layout: Doc, data?: Doc }[],
- panelDim: number[],
- viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[]),
- engineProps: any) => ViewDefResult[]
+ return this._layoutSizeData.get(doc[Id] + (replica || ''));
+ };
+ childSizeProvider = computedFn(
+ function childSizeProvider(this: any, doc: Doc, replica: string) {
+ return this._layoutSizeData.get(doc[Id] + (replica || ''));
+ }.bind(this)
+ );
+
+ doEngineLayout(
+ poolData: Map<string, PoolData>,
+ engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) => ViewDefResult[]
) {
return engine(poolData, this.props.Document, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX, this.props.engineProps);
}
doFreeformLayout(poolData: Map<string, PoolData>) {
- this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) =>
- poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document })));
+ this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document })));
return [] as ViewDefResult[];
}
- @computed get layoutEngine() { return this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine); }
+ @computed get layoutEngine() {
+ return this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
+ }
@computed get doInternalLayoutComputation() {
TraceMobx();
const newPool = new Map<string, PoolData>();
switch (this.layoutEngine) {
- case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
- case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
- case "pivot": return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
- case "starburst": return { newPool, computedElementData: this.doEngineLayout(newPool, computerStarburstLayout) };
+ case 'pass':
+ return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
+ case 'timeline':
+ return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
+ case 'pivot':
+ return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
+ case 'starburst':
+ return { newPool, computedElementData: this.doEngineLayout(newPool, computerStarburstLayout) };
}
return { newPool, computedElementData: this.doFreeformLayout(newPool) };
}
@@ -1424,15 +1519,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._cachedPool.clear();
Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
const elements = computedElementData.slice();
- Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach((entry, i) =>
- elements.push({
- ele: this.getChildDocView(entry[1]),
- bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
- }));
+ Array.from(newPool.entries())
+ .filter(entry => this.isCurrent(entry[1].pair.layout))
+ .forEach((entry, i) =>
+ elements.push({
+ ele: this.getChildDocView(entry[1]),
+ bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica),
+ })
+ );
- if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) { // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
+ if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) {
+ // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, this.zoomScaling());
- else this.props.Document[this.scaleFieldKey] = Math.max(1,this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey]));
+ else this.props.Document[this.scaleFieldKey] = Math.max(1, this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey]));
}
this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
@@ -1453,60 +1552,69 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
return 0;
- }
+ };
getAnchor = () => {
if (this.props.Document.annotationOn) {
return this.rootDoc;
}
- const anchor = Docs.Create.TextanchorDocument({ title: "ViewSpec - " + StrCast(this.layoutDoc._viewType), annotationOn: this.rootDoc });
+ const anchor = Docs.Create.TextanchorDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._viewType), annotationOn: this.rootDoc });
const proto = Doc.GetProto(anchor);
- proto[ViewSpecPrefix + "_viewType"] = this.layoutDoc._viewType;
+ proto[ViewSpecPrefix + '_viewType'] = this.layoutDoc._viewType;
proto.docFilters = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List<string>([]);
- if (Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), []).push(anchor);
+ if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
+ Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
} else {
- this.dataDoc[this.props.fieldKey + "-annotations"] = new List<Doc>([anchor]);
+ this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
}
return anchor;
- }
+ };
@action
componentDidMount() {
super.componentDidMount?.();
this.props.setContentView?.(this);
- setTimeout(action(() => {
- this._firstRender = false;
- this._disposers.layoutComputation = reaction(() => this.doLayoutComputation,
- (elements) => this._layoutElements = elements || [],
- { fireImmediately: true, name: "doLayout" });
-
- this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
-
- this._disposers.groupBounds = reaction(() => {
- if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
- const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
- return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
- }
- return undefined;
- },
- (cbounds) => {
- if (cbounds) {
- const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
- const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
- const pbounds = {
- x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
- r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1]
- };
- this.layoutDoc._width = (pbounds.r - pbounds.x);
- this.layoutDoc._height = (pbounds.b - pbounds.y);
- this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
- this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
- this.layoutDoc.x = pbounds.x;
- this.layoutDoc.y = pbounds.y;
- }
- }, { fireImmediately: true });
- }));
+ setTimeout(
+ action(() => {
+ this._firstRender = false;
+ this._disposers.layoutComputation = reaction(
+ () => this.doLayoutComputation,
+ elements => (this._layoutElements = elements || []),
+ { fireImmediately: true, name: 'doLayout' }
+ );
+
+ this._marqueeRef.current?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
+
+ this._disposers.groupBounds = reaction(
+ () => {
+ if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
+ const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
+ return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
+ }
+ return undefined;
+ },
+ cbounds => {
+ if (cbounds) {
+ const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
+ const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
+ const pbounds = {
+ x: (cbounds.x - p[0]) * this.zoomScaling() + c[0],
+ y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
+ r: (cbounds.r - p[0]) * this.zoomScaling() + c[0],
+ b: (cbounds.b - p[1]) * this.zoomScaling() + c[1],
+ };
+ this.layoutDoc._width = pbounds.r - pbounds.x;
+ this.layoutDoc._height = pbounds.b - pbounds.y;
+ this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
+ this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
+ this.layoutDoc.x = pbounds.x;
+ this.layoutDoc.y = pbounds.y;
+ }
+ },
+ { fireImmediately: true }
+ );
+ })
+ );
}
static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
@@ -1516,15 +1624,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
if (oldDiv instanceof HTMLCanvasElement) {
- if (oldDiv.className === "collectionFreeFormView-grid") {
+ if (oldDiv.className === 'collectionFreeFormView-grid') {
const newCan = newDiv as HTMLCanvasElement;
const parEle = newCan.parentElement as HTMLElement;
parEle.removeChild(newCan);
- parEle.appendChild(document.createElement('div'))
+ parEle.appendChild(document.createElement('div'));
} else {
const canvas = oldDiv;
const img = document.createElement('img'); // create a Image Element
- img.src = canvas.toDataURL(); //image source
+ img.src = canvas.toDataURL(); //image source
img.style.width = canvas.style.width;
img.style.height = canvas.style.height;
const newCan = newDiv as HTMLCanvasElement;
@@ -1535,27 +1643,38 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
- updateIcon = () => CollectionFreeFormView.UpdateIcon(
- this.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
- this.props.docViewPath().lastElement().ContentDiv!,
- this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](),
- this.props.PanelWidth(), this.props.PanelHeight(), 0, 1, false, "",
- (iconFile, nativeWidth, nativeHeight) => {
- this.dataDoc.icon = new ImageField(iconFile);
- this.dataDoc["icon-nativeWidth"] = nativeWidth;
- this.dataDoc["icon-nativeHeight"] = nativeHeight;
- });
+ updateIcon = () =>
+ CollectionFreeFormView.UpdateIcon(
+ this.layoutDoc[Id] + '-icon' + new Date().getTime(),
+ this.props.docViewPath().lastElement().ContentDiv!,
+ this.layoutDoc[WidthSym](),
+ this.layoutDoc[HeightSym](),
+ this.props.PanelWidth(),
+ this.props.PanelHeight(),
+ 0,
+ 1,
+ false,
+ '',
+ (iconFile, nativeWidth, nativeHeight) => {
+ this.dataDoc.icon = new ImageField(iconFile);
+ this.dataDoc['icon-nativeWidth'] = nativeWidth;
+ this.dataDoc['icon-nativeHeight'] = nativeHeight;
+ }
+ );
public static UpdateIcon(
- filename:string, docViewContent:HTMLElement,
- width: number, height: number,
- panelWidth:number, panelHeight: number,
- scrollTop:number,
+ filename: string,
+ docViewContent: HTMLElement,
+ width: number,
+ height: number,
+ panelWidth: number,
+ panelHeight: number,
+ scrollTop: number,
realNativeHeight: number,
noSuffix: boolean,
- replaceRootFilename: string| undefined,
- cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any)
- {
+ replaceRootFilename: string | undefined,
+ cb: (iconFile: string, nativeWidth: number, nativeHeight: number) => any
+ ) {
const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
newDiv.style.width = width.toString();
newDiv.style.height = height.toString();
@@ -1563,15 +1682,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const htmlString = new XMLSerializer().serializeToString(newDiv);
const nativeWidth = width;
const nativeHeight = height;
- return CreateImage(
- Utils.prepend(""),
- document.styleSheets,
- htmlString,
- nativeWidth,
- nativeWidth * panelHeight / panelWidth,
- scrollTop * panelHeight / realNativeHeight
- ).then
- (async (data_url: any) => {
+ return CreateImage(Utils.prepend(''), document.styleSheets, htmlString, nativeWidth, (nativeWidth * panelHeight) / panelWidth, (scrollTop * panelHeight) / realNativeHeight)
+ .then(async (data_url: any) => {
const returnedFilename = await VideoBox.convertDataUri(data_url, filename, noSuffix, replaceRootFilename);
cb(returnedFilename as string, nativeWidth, nativeHeight);
})
@@ -1582,13 +1694,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
componentWillUnmount() {
Object.values(this._disposers).forEach(disposer => disposer?.());
- this._marqueeRef.current?.removeEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
+ this._marqueeRef.current?.removeEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
}
@action
onCursorMove = (e: React.PointerEvent) => {
// super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
- }
+ };
@action
onDragAutoScroll = (e: CustomEvent<React.DragEvent>) => {
@@ -1608,7 +1720,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
e.stopPropagation();
- }
+ };
@undoBatch
promoteCollection = () => {
@@ -1618,9 +1730,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
doc.x = scr?.[0];
doc.y = scr?.[1];
});
- this.props.addDocTab(childDocs as any as Doc, "inParent");
+ this.props.addDocTab(childDocs as any as Doc, 'inParent');
this.props.ContainingCollectionView?.removeDocument(this.props.Document);
- }
+ };
@undoBatch
layoutDocsInGrid = () => {
@@ -1629,73 +1741,88 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const height = Math.max(...docs.map(doc => NumCast(doc._height))) + 20;
const dim = Math.ceil(Math.sqrt(docs.length));
docs.forEach((doc, i) => {
- doc.x = NumCast(this.Document._panX) + (i % dim) * width - width * dim / 2;
- doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - height * dim / 2;
+ doc.x = NumCast(this.Document._panX) + (i % dim) * width - (width * dim) / 2;
+ doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - (height * dim) / 2;
});
- }
+ };
@undoBatch
- toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight)
+ toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight);
onContextMenu = (e: React.MouseEvent) => {
if (this.props.isAnnotationOverlay || this.props.Document.annotationOn || !ContextMenu.Instance) return;
- const appearance = ContextMenu.Instance.findByDescription("Appearance...");
- const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
- appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
- !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
- appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
- appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
+ const appearance = ContextMenu.Instance.findByDescription('Appearance...');
+ const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
+ appearanceItems.push({
+ description: 'Reset View',
+ event: () => {
+ this.props.Document._panX = this.props.Document._panY = 0;
+ this.props.Document[this.scaleFieldKey] = 1;
+ },
+ icon: 'compress-arrows-alt',
+ });
+ !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: 'Reset default note style', event: () => (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' });
+ appearanceItems.push({
+ description: `${this.fitContentsToBox ? 'Make Zoomable' : 'Scale to Window'}`,
+ event: () => (this.Document._fitContentsToBox = !this.fitContentsToBox),
+ icon: !this.fitContentsToBox ? 'expand-arrows-alt' : 'compress-arrows-alt',
+ });
+ appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinDocView: true, panelWidth: this.props.PanelWidth(), panelHeight: this.props.PanelHeight() }), icon: 'map-pin' });
//appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
- this.props.ContainingCollectionView &&
- appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
+ this.props.ContainingCollectionView && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
- this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: "Ink to text", event: () => this.transcribeStrokes(false), icon: "font" });
+ this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: () => this.transcribeStrokes(false), icon: 'font' });
// this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" });
- !Doc.noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
- !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
-
- const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
- const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
- !Doc.noviceMode ? viewCtrlItems.push({ description: (SnappingManager.GetShowSnapLines() ? "Hide" : "Show") + " Snap Lines", event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: "compress-arrows-alt" }) : null;
- !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
- !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
-
- const options = ContextMenu.Instance.findByDescription("Options...");
- const optionItems = options && "subitems" in options ? options.subitems : [];
- !this.props.isAnnotationOverlay && !Doc.noviceMode &&
- optionItems.push({ description: (this._showAnimTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this._showAnimTimeline = !this._showAnimTimeline), icon: "eye" });
- this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
+ !Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null;
+ !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
+
+ const viewctrls = ContextMenu.Instance.findByDescription('UI Controls...');
+ const viewCtrlItems = viewctrls && 'subitems' in viewctrls ? viewctrls.subitems : [];
+ !Doc.noviceMode
+ ? viewCtrlItems.push({ description: (SnappingManager.GetShowSnapLines() ? 'Hide' : 'Show') + ' Snap Lines', event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: 'compress-arrows-alt' })
+ : null;
+ !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._useClusters), icon: 'braille' }) : null;
+ !viewctrls && ContextMenu.Instance.addItem({ description: 'UI Controls...', subitems: viewCtrlItems, icon: 'eye' });
+
+ const options = ContextMenu.Instance.findByDescription('Options...');
+ const optionItems = options && 'subitems' in options ? options.subitems : [];
+ !this.props.isAnnotationOverlay &&
+ !Doc.noviceMode &&
+ optionItems.push({ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline', event: action(() => (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
+ this.props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' });
if (!Doc.noviceMode) {
- optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
+ optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
}
- !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
- const mores = ContextMenu.Instance.findByDescription("More...");
- const moreItems = mores && "subitems" in mores ? mores.subitems : [];
+ !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
+ const mores = ContextMenu.Instance.findByDescription('More...');
+ const moreItems = mores && 'subitems' in mores ? mores.subitems : [];
if (!Doc.noviceMode) {
e.persist();
- moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) });
- moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(e.clientX, e.clientY) });
+ moreItems.push({ description: 'Export collection', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
+ moreItems.push({ description: 'Import exported collection', icon: 'upload', event: ({ x, y }) => this.importDocument(e.clientX, e.clientY) });
}
- !mores && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "eye" });
- }
+ !mores && ContextMenu.Instance.addItem({ description: 'More...', subitems: moreItems, icon: 'eye' });
+ };
importDocument = (x: number, y: number) => {
- const input = document.createElement("input");
- input.type = "file";
- input.accept = ".zip";
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.zip';
input.onchange = _e => {
- input.files && Doc.importDocument(input.files[0]).then(doc => {
- if (doc instanceof Doc) {
- const [xx, yy] = this.getTransform().transformPoint(x, y);
- doc.x = xx, doc.y = yy;
- this.props.addDocument?.(doc);}
- });
+ input.files &&
+ Doc.importDocument(input.files[0]).then(doc => {
+ if (doc instanceof Doc) {
+ const [xx, yy] = this.getTransform().transformPoint(x, y);
+ (doc.x = xx), (doc.y = yy);
+ this.props.addDocument?.(doc);
+ }
+ });
};
input.click();
- }
+ };
@undoBatch
@action
@@ -1704,13 +1831,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!math) {
const text = StrCast(this.props.Document.transcription);
- const lines = text.split("\n");
+ const lines = text.split('\n');
const height = 30 + 15 * lines.length;
this.props.ContainingCollectionView?.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
}
}
- }
+ };
@action
setupDragLines = (snapToDraggedDoc: boolean = false) => {
@@ -1718,37 +1845,39 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const size = this.getTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
const docDims = (doc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
- const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => intersectRect(docDims(doc), rect);
+ const isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => intersectRect(docDims(doc), rect);
const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
- let snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
+ let snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
!snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect))); // if not, see if there are background docs to snap to
!snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z !== undefined && isDocInView(doc, otherBounds))); // if not, then why not snap to floating docs
const horizLines: number[] = [];
const vertLines: number[] = [];
const invXf = this.getTransform().inverse();
- snappableDocs.filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => {
- const { left, top, width, height } = docDims(doc);
- const topLeftInScreen = invXf.transformPoint(left, top);
- const docSize = invXf.transformDirection(width, height);
+ snappableDocs
+ .filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc))
+ .forEach(doc => {
+ const { left, top, width, height } = docDims(doc);
+ const topLeftInScreen = invXf.transformPoint(left, top);
+ const docSize = invXf.transformDirection(width, height);
- horizLines.push(topLeftInScreen[1], topLeftInScreen[1] + docSize[1] / 2, topLeftInScreen[1] + docSize[1]); // horiz center line
- vertLines.push(topLeftInScreen[0], topLeftInScreen[0] + docSize[0] / 2, topLeftInScreen[0] + docSize[0]);// right line
- });
+ horizLines.push(topLeftInScreen[1], topLeftInScreen[1] + docSize[1] / 2, topLeftInScreen[1] + docSize[1]); // horiz center line
+ vertLines.push(topLeftInScreen[0], topLeftInScreen[0] + docSize[0] / 2, topLeftInScreen[0] + docSize[0]); // right line
+ });
DragManager.SetSnapLines(horizLines, vertLines);
- }
+ };
onPointerOver = (e: React.PointerEvent) => {
e.stopPropagation();
- }
+ };
incrementalRender = action(() => {
if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) {
const unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
const loadIncrement = 5;
for (var i = 0; i < Math.min(unrendered.length, loadIncrement); i++) {
- this._renderCutoffData.set(unrendered[i][Id] + "", true);
+ this._renderCutoffData.set(unrendered[i][Id] + '', true);
}
}
this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
@@ -1756,64 +1885,67 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
children = () => {
this.incrementalRender();
- const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
- return [
- ...children,
- ...this.views,
- <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
- ];
- }
+ const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : [];
+ return [...children, ...this.views, <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />];
+ };
@computed get placeholder() {
- return <div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}>
- <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title?.toString()}</span>
- </div>;
+ return (
+ <div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}>
+ <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title?.toString()}</span>
+ </div>
+ );
}
@computed get marqueeView() {
TraceMobx();
- return <MarqueeView
- {...this.props}
- ref={this._marqueeViewRef}
- ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
- nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
- addDocTab={this.addDocTab}
- trySelectCluster={this.trySelectCluster}
- activeDocuments={this.getActiveDocuments}
- selectDocuments={this.selectDocuments}
- addDocument={this.addDocument}
- addLiveTextDocument={this.addLiveTextBox}
- getContainerTransform={this.getContainerTransform}
- getTransform={this.getTransform}
- isAnnotationOverlay={this.isAnnotationOverlay}>
- <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
- {this.layoutDoc._backgroundGridShow ?
- <div><CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!?
- PanelWidth={this.props.PanelWidth}
- PanelHeight={this.props.PanelHeight}
- panX={this.panX}
- panY={this.panY}
- zoomScaling={this.zoomScaling}
- layoutDoc={this.layoutDoc}
+ return (
+ <MarqueeView
+ {...this.props}
+ ref={this._marqueeViewRef}
+ ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
+ nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
+ addDocTab={this.addDocTab}
+ trySelectCluster={this.trySelectCluster}
+ activeDocuments={this.getActiveDocuments}
+ selectDocuments={this.selectDocuments}
+ addDocument={this.addDocument}
+ addLiveTextDocument={this.addLiveTextBox}
+ getContainerTransform={this.getContainerTransform}
+ getTransform={this.getTransform}
+ isAnnotationOverlay={this.isAnnotationOverlay}>
+ <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
+ {this.layoutDoc._backgroundGridShow ? (
+ <div>
+ <CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!?
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.props.PanelHeight}
+ panX={this.panX}
+ panY={this.panY}
+ zoomScaling={this.zoomScaling}
+ layoutDoc={this.layoutDoc}
+ isAnnotationOverlay={this.isAnnotationOverlay}
+ cachedCenteringShiftX={this.cachedCenteringShiftX}
+ cachedCenteringShiftY={this.cachedCenteringShiftY}
+ />
+ </div>
+ ) : null}
+ <CollectionFreeFormViewPannableContents
isAnnotationOverlay={this.isAnnotationOverlay}
- cachedCenteringShiftX={this.cachedCenteringShiftX}
- cachedCenteringShiftY={this.cachedCenteringShiftY}
- /></div> : (null)}
- <CollectionFreeFormViewPannableContents
- isAnnotationOverlay={this.isAnnotationOverlay}
- isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
- transform={this.contentTransform}
- zoomScaling={this.zoomScaling}
- presPaths={BoolCast(this.Document.presPathView)}
- progressivize={BoolCast(this.Document.editProgressivize)}
- presPinView={BoolCast(this.Document.presPinView)}
- transition={this._viewTransition ? `transform ${this._viewTransition}ms` : Cast(this.layoutDoc._viewTransition, "string", null)}
- viewDefDivClick={this.props.viewDefDivClick}>
- {this.children}
- </CollectionFreeFormViewPannableContents>
- </div>
- {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : (null)}
- </MarqueeView>;
+ isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
+ transform={this.contentTransform}
+ zoomScaling={this.zoomScaling}
+ presPaths={BoolCast(this.Document.presPathView)}
+ progressivize={BoolCast(this.Document.editProgressivize)}
+ presPinView={BoolCast(this.Document.presPinView)}
+ transition={this._viewTransition ? `transform ${this._viewTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', null)}
+ viewDefDivClick={this.props.viewDefDivClick}>
+ {this.children}
+ </CollectionFreeFormViewPannableContents>
+ </div>
+ {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : null}
+ </MarqueeView>
+ );
}
@computed get contentScaling() {
@@ -1826,66 +1958,83 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
private groupDropDisposer?: DragManager.DragDropDisposer;
- protected createGroupEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ protected createGroupEventsTarget = (ele: HTMLDivElement) => {
+ //used for stacking and masonry view
this.groupDropDisposer?.();
if (ele) {
this.groupDropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this));
}
- }
+ };
render() {
TraceMobx();
const clientRect = this._mainCont?.getBoundingClientRect();
- return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget}
- onPointerOver={this.onPointerOver}
- onWheel={this.onPointerWheel}
- onClick={this.onClick}
- onPointerDown={this.onPointerDown}
- onPointerMove={this.onCursorMove}
- onDrop={this.onExternalDrop.bind(this)}
- onDragOver={e => e.preventDefault()}
- onContextMenu={this.onContextMenu}
- style={{
- pointerEvents: this.props.Document.type === DocumentType.MARKER ? "none" : // bcz: ugh.. this is here to prevent markers, which render as freeform views, from grabbing events -- need a better approach.
- (SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement())) ? "all" : this.props.pointerEvents?.() as any,
- transform: `scale(${this.contentScaling || 1})`,
- width: `${100 / (this.contentScaling || 1)}%`,
- height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
- }}>
- {this._firstRender ?
- this.placeholder : this.marqueeView}
- {this.props.noOverlay ? (null) : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
-
-
- <div className={"pullpane-indicator"}
+ return (
+ <div
+ className={'collectionfreeformview-container'}
+ ref={this.createDashEventsTarget}
+ onPointerOver={this.onPointerOver}
+ onWheel={this.onPointerWheel}
+ onClick={this.onClick}
+ onPointerDown={this.onPointerDown}
+ onPointerMove={this.onCursorMove}
+ onDrop={this.onExternalDrop.bind(this)}
+ onDragOver={e => e.preventDefault()}
+ onContextMenu={this.onContextMenu}
style={{
- display: this._pullDirection ? "block" : "none",
- top: clientRect ? this._pullDirection === "bottom" ? this._pullCoords[1] - clientRect.y : 0 : "auto",
- left: clientRect ? this._pullDirection === "right" ? this._pullCoords[0] - clientRect.x : 0 : "auto",
- width: clientRect ? this._pullDirection === "left" ? this._pullCoords[0] - clientRect.left : this._pullDirection === "right" ? clientRect.right - this._pullCoords[0] : clientRect.width : 0,
- height: clientRect ? this._pullDirection === "top" ? this._pullCoords[1] - clientRect.top : this._pullDirection === "bottom" ? clientRect.bottom - this._pullCoords[1] : clientRect.height : 0,
-
+ pointerEvents:
+ this.props.Document.type === DocumentType.MARKER
+ ? 'none' // bcz: ugh.. this is here to prevent markers, which render as freeform views, from grabbing events -- need a better approach.
+ : SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement())
+ ? 'all'
+ : (this.props.pointerEvents?.() as any),
+ transform: `scale(${this.contentScaling || 1})`,
+ width: `${100 / (this.contentScaling || 1)}%`,
+ height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`, // : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
}}>
+ {this._firstRender ? this.placeholder : this.marqueeView}
+ {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
+
+ <div
+ className={'pullpane-indicator'}
+ style={{
+ display: this._pullDirection ? 'block' : 'none',
+ top: clientRect ? (this._pullDirection === 'bottom' ? this._pullCoords[1] - clientRect.y : 0) : 'auto',
+ left: clientRect ? (this._pullDirection === 'right' ? this._pullCoords[0] - clientRect.x : 0) : 'auto',
+ width: clientRect ? (this._pullDirection === 'left' ? this._pullCoords[0] - clientRect.left : this._pullDirection === 'right' ? clientRect.right - this._pullCoords[0] : clientRect.width) : 0,
+ height: clientRect ? (this._pullDirection === 'top' ? this._pullCoords[1] - clientRect.top : this._pullDirection === 'bottom' ? clientRect.bottom - this._pullCoords[1] : clientRect.height) : 0,
+ }}></div>
+ {
+ // uncomment to show snap lines
+ <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}>
+ <svg style={{ width: '100%', height: '100%' }}>
+ {this._hLines?.map(l => (
+ <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />
+ ))}
+ {this._vLines?.map(l => (
+ <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />
+ ))}
+ </svg>
+ </div>
+ }
+
+ {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ? (
+ <div
+ className="collectionFreeForm-groupDropper"
+ ref={this.createGroupEventsTarget}
+ style={{
+ width: this.ChildDrag ? '10000' : '100%',
+ height: this.ChildDrag ? '10000' : '100%',
+ left: this.ChildDrag ? '-5000' : 0,
+ top: this.ChildDrag ? '-5000' : 0,
+ position: 'absolute',
+ background: '#0009930',
+ pointerEvents: 'all',
+ }}
+ />
+ ) : null}
</div>
- {// uncomment to show snap lines
- <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
- <svg style={{ width: "100%", height: "100%" }}>
- {this._hLines?.map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />)}
- {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)}
- </svg>
- </div>}
-
- {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ?
- <div className="collectionFreeForm-groupDropper" ref={this.createGroupEventsTarget} style={{
- width: this.ChildDrag ? "10000" : "100%",
- height: this.ChildDrag ? "10000" : "100%",
- left: this.ChildDrag ? "-5000" : 0,
- top: this.ChildDrag ? "-5000" : 0,
- position: "absolute",
- background: "#0009930",
- pointerEvents: "all"
- }} /> : (null)}
- </div >;
+ );
}
}
@@ -1894,9 +2043,12 @@ interface CollectionFreeFormOverlayViewProps {
}
@observer
-class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps>{
+class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps> {
render() {
- return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele);
+ return this.props
+ .elements()
+ .filter(ele => ele.bounds?.z)
+ .map(ele => ele.ele);
}
}
@@ -1914,50 +2066,50 @@ interface CollectionFreeFormViewPannableContentsProps {
}
@observer
-class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps>{
+class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps> {
@observable _drag: string = '';
//Adds event listener so knows pointer is down and moving
onPointerDown = (e: React.PointerEvent): void => {
e.stopPropagation();
e.preventDefault();
- this._drag = (e.target as any)?.id ?? "";
+ this._drag = (e.target as any)?.id ?? '';
document.getElementById(this._drag) && setupMoveUpEvents(e.target, e, this.onPointerMove, emptyFunction, emptyFunction);
- }
+ };
//Adjusts the value in NodeStore
@action
onPointerMove = (e: PointerEvent) => {
const doc = document.getElementById('resizable');
- const toNumber = (original: number, delta: number) => original + (delta * this.props.zoomScaling());
+ const toNumber = (original: number, delta: number) => original + delta * this.props.zoomScaling();
if (doc) {
switch (this._drag) {
- case "resizer-br":
+ case 'resizer-br':
doc.style.width = toNumber(doc.offsetWidth, e.movementX) + 'px';
doc.style.height = toNumber(doc.offsetHeight, e.movementY) + 'px';
break;
- case "resizer-bl":
+ case 'resizer-bl':
doc.style.width = toNumber(doc.offsetWidth, -e.movementX) + 'px';
doc.style.height = toNumber(doc.offsetHeight, e.movementY) + 'px';
doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px';
break;
- case "resizer-tr":
+ case 'resizer-tr':
doc.style.width = toNumber(doc.offsetWidth, -e.movementX) + 'px';
doc.style.height = toNumber(doc.offsetHeight, -e.movementY) + 'px';
doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px';
- case "resizer-tl":
+ case 'resizer-tl':
doc.style.width = toNumber(doc.offsetWidth, -e.movementX) + 'px';
doc.style.height = toNumber(doc.offsetHeight, -e.movementY) + 'px';
doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px';
doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px';
- case "resizable":
+ case 'resizable':
doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px';
doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px';
}
return false;
}
return true;
- }
+ };
// scale: NumCast(targetDoc._viewScale),
@computed get zoomProgressivizeContainer() {
@@ -1968,66 +2120,73 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
const top = NumCast(activeItem.presPinViewY);
const width = 100;
const height = 100;
- return !this.props.presPinView ? (null) :
+ return !this.props.presPinView ? null : (
<div key="resizable" className="resizable" onPointerDown={this.onPointerDown} style={{ width, height, top, left, position: 'absolute' }}>
- <div className='resizers' key={'resizer' + activeItem.id}>
- <div className='resizer top-left' onPointerDown={this.onPointerDown} />
- <div className='resizer top-right' onPointerDown={this.onPointerDown} />
- <div className='resizer bottom-left' onPointerDown={this.onPointerDown} />
- <div className='resizer bottom-right' onPointerDown={this.onPointerDown} />
+ <div className="resizers" key={'resizer' + activeItem.id}>
+ <div className="resizer top-left" onPointerDown={this.onPointerDown} />
+ <div className="resizer top-right" onPointerDown={this.onPointerDown} />
+ <div className="resizer bottom-left" onPointerDown={this.onPointerDown} />
+ <div className="resizer bottom-right" onPointerDown={this.onPointerDown} />
</div>
- </div>;
+ </div>
+ );
}
}
@computed get zoomProgressivize() {
- return PresBox.Instance?.activeItem?.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : (null);
+ return PresBox.Instance?.activeItem?.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : null;
}
@computed get progressivize() {
- return PresBox.Instance && this.props.progressivize ? PresBox.Instance.progressivizeChildDocs : (null);
+ return PresBox.Instance && this.props.progressivize ? PresBox.Instance.progressivizeChildDocs : null;
}
@computed get presPaths() {
- const presPaths = "presPaths" + (this.props.presPaths ? "" : "-hidden");
- return !PresBox.Instance || !this.props.presPaths ? (null) : <>
- <div key="presorder">{PresBox.Instance.order}</div>
- <svg key="svg" className={presPaths}>
- <defs>
- <marker id="markerSquare" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
- <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="white" fillOpacity="0.8" />
- </marker>
- <marker id="markerSquareFilled" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
- <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="#69a6db" />
- </marker>
- <marker id="markerArrow" markerWidth="3" markerHeight="3" refX="2" refY="4" orient="auto" overflow="visible">
- <path d="M2,2 L2,6 L6,4 L2,2 Z" stroke="#69a6db" strokeLinejoin="round" strokeWidth="1" fill="white" fillOpacity="0.8" />
- </marker>
- </defs>
- {PresBox.Instance.paths}
- </svg>
- </>;
+ const presPaths = 'presPaths' + (this.props.presPaths ? '' : '-hidden');
+ return !PresBox.Instance || !this.props.presPaths ? null : (
+ <>
+ <div key="presorder">{PresBox.Instance.order}</div>
+ <svg key="svg" className={presPaths}>
+ <defs>
+ <marker id="markerSquare" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
+ <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="white" fillOpacity="0.8" />
+ </marker>
+ <marker id="markerSquareFilled" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
+ <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="#69a6db" />
+ </marker>
+ <marker id="markerArrow" markerWidth="3" markerHeight="3" refX="2" refY="4" orient="auto" overflow="visible">
+ <path d="M2,2 L2,6 L6,4 L2,2 Z" stroke="#69a6db" strokeLinejoin="round" strokeWidth="1" fill="white" fillOpacity="0.8" />
+ </marker>
+ </defs>
+ {PresBox.Instance.paths}
+ </svg>
+ </>
+ );
}
render() {
- return <div className={"collectionfreeformview" + (this.props.viewDefDivClick ? "-viewDef" : "-none")}
- onScroll={e => {
- const target = e.target as any;
- if (getComputedStyle(target)?.overflow === "visible") { // if collection is visible, then scrolling will mess things up since there are no scroll bars
- target.scrollTop = target.scrollLeft = 0;
- }
- }}
- style={{
- transform: this.props.transform(),
- transition: this.props.transition,
- width: this.props.isAnnotationOverlay ? undefined : 0, // if not an overlay, then this will be the size of the collection, but panning and zooming will move it outside the visible border of the collection and make it selectable. This problem shows up after zooming/panning on a background collection -- you can drag the collection by clicking on apparently empty space outside the collection
- //willChange: "transform"
- }}>
- {this.props.children()}
- {this.presPaths}
- {this.progressivize}
- {this.zoomProgressivize}
- </div>;
+ return (
+ <div
+ className={'collectionfreeformview' + (this.props.viewDefDivClick ? '-viewDef' : '-none')}
+ onScroll={e => {
+ const target = e.target as any;
+ if (getComputedStyle(target)?.overflow === 'visible') {
+ // if collection is visible, then scrolling will mess things up since there are no scroll bars
+ target.scrollTop = target.scrollLeft = 0;
+ }
+ }}
+ style={{
+ transform: this.props.transform(),
+ transition: this.props.transition,
+ width: this.props.isAnnotationOverlay ? undefined : 0, // if not an overlay, then this will be the size of the collection, but panning and zooming will move it outside the visible border of the collection and make it selectable. This problem shows up after zooming/panning on a background collection -- you can drag the collection by clicking on apparently empty space outside the collection
+ //willChange: "transform"
+ }}>
+ {this.props.children()}
+ {this.presPaths}
+ {this.progressivize}
+ {this.zoomProgressivize}
+ </div>
+ );
}
}
@@ -2044,63 +2203,73 @@ interface CollectionFreeFormViewBackgroundGridProps {
}
@observer
class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFormViewBackgroundGridProps> {
-
-
chooseGridSpace = (gridSpace: number): number => {
if (!this.props.zoomScaling()) return 50;
const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace + 3;
return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10);
- }
+ };
render() {
- const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc["_backgroundGrid-spacing"], 50));
- const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.props.panX() % gridSpace - gridSpace) * this.props.zoomScaling();
- const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.props.panY() % gridSpace - gridSpace) * this.props.zoomScaling();
+ const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc['_backgroundGrid-spacing'], 50));
+ const shiftX = (this.props.isAnnotationOverlay ? 0 : (-this.props.panX() % gridSpace) - gridSpace) * this.props.zoomScaling();
+ const shiftY = (this.props.isAnnotationOverlay ? 0 : (-this.props.panY() % gridSpace) - gridSpace) * this.props.zoomScaling();
const renderGridSpace = gridSpace * this.props.zoomScaling();
const w = this.props.PanelWidth() + 2 * renderGridSpace;
const h = this.props.PanelHeight() + 2 * renderGridSpace;
- const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "rgba(255,255,255,0.5)" : "rgba(0, 0,0,0.5)";
- return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
- ref={(el) => {
- const ctx = el?.getContext('2d');
- if (ctx) {
- const Cx = this.props.cachedCenteringShiftX % renderGridSpace;
- const Cy = this.props.cachedCenteringShiftY % renderGridSpace;
- ctx.lineWidth = Math.min(1, Math.max(0.5, this.props.zoomScaling()));
- ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
- ctx.clearRect(0, 0, w, h);
+ const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)';
+ return (
+ <canvas
+ className="collectionFreeFormView-grid"
+ width={w}
+ height={h}
+ style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
+ ref={el => {
+ const ctx = el?.getContext('2d');
if (ctx) {
- ctx.strokeStyle = strokeStyle;
- ctx.beginPath();
- for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
- ctx.moveTo(x, Cy - h);
- ctx.lineTo(x, Cy + h);
+ const Cx = this.props.cachedCenteringShiftX % renderGridSpace;
+ const Cy = this.props.cachedCenteringShiftY % renderGridSpace;
+ ctx.lineWidth = Math.min(1, Math.max(0.5, this.props.zoomScaling()));
+ ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
+ ctx.clearRect(0, 0, w, h);
+ if (ctx) {
+ ctx.strokeStyle = strokeStyle;
+ ctx.beginPath();
+ for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
+ ctx.moveTo(x, Cy - h);
+ ctx.lineTo(x, Cy + h);
+ }
+ for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
+ ctx.moveTo(Cx - w, y);
+ ctx.lineTo(Cx + w, y);
+ }
+ ctx.stroke();
}
- for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
- ctx.moveTo(Cx - w, y);
- ctx.lineTo(Cx + w, y);
- }
- ctx.stroke();
}
- }
- }} />;
+ }}
+ />
+ );
}
}
export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY: number) {
SelectionManager.DeselectAll();
dv.props.focus(dv.props.Document, {
- willZoom: true, afterFocus: async (didMove) => {
+ willZoom: true,
+ afterFocus: async didMove => {
if (!didMove) {
const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
const parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
- const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || "_viewScale"] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview
+ const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview
ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5);
}
return ViewAdjustment.doNothing;
- }
+ },
});
Doc.linkFollowHighlight(dv?.props.Document, false);
}
ScriptingGlobals.add(CollectionBrowseClick);
-ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(); });
-ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true); }); \ No newline at end of file
+ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
+ !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
+});
+ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
+ !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
+});