aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2023-11-02 02:12:19 -0400
committerSophie Zhang <sophie_zhang@brown.edu>2023-11-02 02:12:19 -0400
commita1d00a36ef1afa97198a825bd25ebb4c5e598848 (patch)
treee0c0454c99938562132794333a22e490e3e37cb9 /src/client/views/collections/collectionFreeForm
parent78d8261522c0079b0298613a856547a9ac96ef50 (diff)
parent84c15417f2247fc650a9f7b2c959479519bd3ebb (diff)
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx362
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx1
4 files changed, 148 insertions, 220 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index c1c01eacb..d93e44ab7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -42,6 +42,7 @@ export interface PoolData {
transition?: string;
highlight?: boolean;
replica: string;
+ pointerEvents?: string; // without this, toggling lockPosition of a group/collection in a freeform view won't update until something else invalidates the freeform view's documents forcing -- this is a problem with doLayoutComputation which makes a performance test to insure somethingChanged
pair: { layout: Doc; data?: Doc };
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index c90fdf013..250760bd5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -54,10 +54,14 @@
}
}
+ .presPathLabels {
+ pointer-events: none;
+ }
svg.presPaths {
position: absolute;
z-index: 100000;
overflow: visible;
+ pointer-events: none;
}
svg.presPaths-hidden {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index bfc61f601..0c3033579 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -16,17 +16,18 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro
import { ImageField } from '../../../../fields/URLField';
import { TraceMobx } from '../../../../fields/util';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, emptyFunction, intersectRect, lightOrDark, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { InteractionUtils } from '../../../util/InteractionUtils';
+import { FollowLinkScript } from '../../../util/LinkFollower';
import { ReplayMovements } from '../../../util/ReplayMovements';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SelectionManager } from '../../../util/SelectionManager';
-import { ColorScheme, freeformScrollMode } from '../../../util/SettingsManager';
+import { freeformScrollMode } from '../../../util/SettingsManager';
import { SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
@@ -51,7 +52,6 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
import React = require('react');
-import { FollowLinkScript } from '../../../util/LinkFollower';
export type collectionFreeformViewProps = {
NativeWidth?: () => number;
@@ -82,6 +82,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _lastY: number = 0;
private _downX: number = 0;
private _downY: number = 0;
+ private _downTime = 0;
private _inkToTextStartX: number | undefined;
private _inkToTextStartY: number | undefined;
private _wordPalette: Map<string, string> = new Map<string, string>();
@@ -92,7 +93,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _layoutPoolData = observable.map<string, PoolData>();
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 _brushtimer: any;
private _brushtimer1: any;
@@ -118,8 +118,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
@observable _panZoomTransition: 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 = false; // 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. could be used for performance improvement
@observable _showAnimTimeline = false;
@observable _clusterSets: Doc[][] = [];
@@ -128,11 +126,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _marqueeRef: HTMLDivElement | null = null;
@observable _marqueeViewRef = React.createRef<MarqueeView>();
@observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
- @observable _brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 }; // highlighted region of freeform canvas used by presentations to indicate a region
-
- constructor(props: any) {
- super(props);
- }
+ @observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined; // highlighted region of freeform canvas used by presentations to indicate a region
@computed get views() {
const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele);
@@ -156,12 +150,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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?.width && !e.bounds.z).map(e => e.bounds!),
- NumCast(this.layoutDoc._xPadding, 10),
- NumCast(this.layoutDoc._yPadding, 10)
- );
+ : aggregateBounds(
+ this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
+ NumCast(this.layoutDoc._xPadding, 10),
+ NumCast(this.layoutDoc._yPadding, 10)
+ );
}
@computed get nativeWidth() {
return this.props.NativeWidth?.() || (this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)));
@@ -317,6 +310,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
focus = (anchor: Doc, options: DocFocusOptions) => {
if (this._lightboxDoc) return;
+ if (anchor === this.rootDoc) {
+ if (options.willZoomCentered && options.zoomScale) {
+ this.fitContentOnce();
+ options.didMove = true;
+ }
+ }
if (anchor.type !== DocumentType.CONFIG && !DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor)) return;
const xfToCollection = options?.docTransform ?? Transform.Identity();
const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined };
@@ -462,9 +461,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return false;
};
- onExternalDrop = (e: React.DragEvent) => {
- return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY));
- };
+ onExternalDrop = (e: React.DragEvent) => (([x, y]) => super.onExternalDrop(e, { x, y }))(this.getTransform().transformPoint(e.pageX, e.pageY));
pickCluster(probe: number[]) {
return this.childLayoutPairs
@@ -641,6 +638,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
+ this._downTime = Date.now();
if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
if (
!this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
@@ -715,7 +713,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
points,
ActiveIsInkMask(),
{
- title: 'ink stroke',
+ title: ge.gesture.toString(),
x: B.x - (ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale) / 2,
y: B.y - (ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale) / 2,
_width: B.width + ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
@@ -733,9 +731,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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 => DocCast(s.proto)?.type === DocumentType.RTF && s.color);
- const sets = setDocs.map(sd => {
- return Cast(sd.text, RichTextField)?.Text as string;
- });
+ const sets = setDocs.map(sd => 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);
@@ -810,31 +806,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onClick = (e: React.MouseEvent) => {
if (this._lightboxDoc) this._lightboxDoc = undefined;
- if (this.onBrowseClickHandler()) {
- if (this.props.DocumentView?.()) {
- this.onBrowseClickHandler().script.run({ documentView: this.props.DocumentView(), clientX: e.clientX, clientY: e.clientY });
- }
- e.stopPropagation();
- e.preventDefault();
- } else if (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3) {
- if (e.shiftKey && (this.props.renderDepth === 0 || this.isContentActive())) {
- 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);
- }
+ if (Utils.isClick(e.pageX, e.pageY, this._downX, this._downY, this._downTime)) {
+ if (this.onBrowseClickHandler()) {
+ this.onBrowseClickHandler().script.run({ documentView: this.props.DocumentView?.(), clientX: e.clientX, clientY: e.clientY });
+ e.stopPropagation();
+ e.preventDefault();
+ } else if (this.isContentActive() && e.shiftKey) {
+ // 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();
e.preventDefault();
}
- this._lastTap = Date.now();
}
};
@action
scrollPan = (e: WheelEvent | { deltaX: number; deltaY: number }): void => {
PresBox.Instance?.pauseAutoPres();
- const dx = e.deltaX;
- const dy = e.deltaY;
- this.setPan(NumCast(this.Document[this.panXFieldKey]) - dx, NumCast(this.Document[this.panYFieldKey]) - dy, 0, true);
+ this.setPan(NumCast(this.Document[this.panXFieldKey]) - e.deltaX, NumCast(this.Document[this.panYFieldKey]) - e.deltaY, 0, true);
};
@action
@@ -1183,23 +1172,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
bringToFront = (doc: Doc, sendToBack?: boolean) => {
- if (sendToBack) {
- const docs = this.childLayoutPairs.map(pair => pair.layout).slice();
- docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- let zfirst = docs.length ? NumCast(docs[0].zIndex) : 0;
- doc.zIndex = zfirst - 1;
- } else if (doc.stroke_isInkMask) {
+ if (doc.stroke_isInkMask) {
doc.zIndex = 5000;
} else {
- const docs = this.childLayoutPairs.map(pair => pair.layout).slice();
- docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- let zlast = docs.length ? Math.max(docs.length, NumCast(docs.lastElement().zIndex)) : 1;
- if (docs.lastElement() !== doc) {
- if (zlast - docs.length > 100) {
- for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1;
- zlast = docs.length + 1;
+ // prettier-ignore
+ const docs = this.childLayoutPairs.map(pair => pair.layout)
+ .sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
+ if (sendToBack) {
+ const zfirst = docs.length ? NumCast(docs[0].zIndex) : 0;
+ doc.zIndex = zfirst - 1;
+ } else {
+ let zlast = docs.length ? Math.max(docs.length, NumCast(docs.lastElement().zIndex)) : 1;
+ if (docs.lastElement() !== doc) {
+ if (zlast - docs.length > 100) {
+ for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1;
+ zlast = docs.length + 1;
+ }
+ doc.zIndex = zlast + 1;
}
- doc.zIndex = zlast + 1;
}
}
};
@@ -1291,14 +1281,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this.addDocument?.(newDoc);
}
};
- @computed get _pointerEvents() {
+ @computed get childPointerEvents() {
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
const pointerEvents = DocumentView.Interacting
? 'none'
: this.props.childPointerEvents?.() ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
return pointerEvents;
}
- pointerEvents = () => this._pointerEvents;
+ childPointerEventsFunc = () => this.childPointerEvents;
childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
getChildDocView(entry: PoolData) {
const childLayout = entry.pair.layout;
@@ -1308,8 +1298,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
key={childLayout[Id] + (entry.replica || '')}
DataDoc={childData}
Document={childLayout}
+ isGroupActive={this.props.isGroupActive}
renderDepth={this.props.renderDepth + 1}
replica={entry.replica}
+ hideDecorations={BoolCast(childLayout._layout_isSvg && childLayout.type === DocumentType.LINK)}
suppressSetHeight={this.layoutEngine ? true : false}
renderCutoffProvider={this.renderCutoffProvider}
CollectionFreeFormView={this}
@@ -1327,7 +1319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- isDocumentActive={this.props.childDocumentsActive?.() || this.rootDoc._isGroup ? this.props.isDocumentActive : this.isContentActive}
+ isDocumentActive={childLayout.pointerEvents === 'none' ? returnFalse : this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
isContentActive={this.childContentsActive}
focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
addDocTab={this.addDocTab}
@@ -1344,7 +1336,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
bringToFront={this.bringToFront}
layout_showTitle={this.props.childlayout_showTitle}
dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
- pointerEvents={this.pointerEvents}
+ pointerEvents={this.childPointerEventsFunc}
//fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeView_FreezeChildDimensions)} // bcz: check this
/>
);
@@ -1412,6 +1404,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
height: _height,
transition: StrCast(childDocLayout.dataTransition),
pair: params.pair,
+ pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
replica: '',
};
}
@@ -1531,7 +1524,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
newPos.z !== lastPos.z ||
newPos.rotation !== lastPos.rotation ||
newPos.zIndex !== lastPos.zIndex ||
- newPos.transition !== lastPos.transition
+ newPos.transition !== lastPos.transition ||
+ newPos.pointerEvents !== lastPos.pointerEvents
) {
this._layoutPoolData.set(entry[0], newPos);
somethingChanged = true;
@@ -1625,17 +1619,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
this._disposers.active = reaction(
- () => this.isContentActive(),
- active => this.rootDoc[this.autoResetFieldKey] && !active && this.resetView()
- );
-
- this._disposers.fitContent = reaction(
- () => this.rootDoc.fitContentOnce,
- fitContentOnce => {
- if (fitContentOnce) this.fitContentOnce();
- this.rootDoc.fitContentOnce = undefined;
- },
- { fireImmediately: true, name: 'fitContent' }
+ () => this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions
+ active => !SnappingManager.GetIsDragging() && this.rootDoc[this.autoResetFieldKey] && active && this.resetView()
);
})
);
@@ -1826,16 +1811,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
!viewctrls && ContextMenu.Instance.addItem({ description: 'UI Controls...', subitems: viewCtrlItems, icon: 'eye' });
@@ -1871,23 +1851,38 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- setupDragLines = (snapToDraggedDoc: boolean = false) => {
+ dragEnding = () => {
+ this.GroupChildDrag = false;
+ SnappingManager.clearSnapLines();
+ };
+ @action
+ dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean, visited = new Set<Doc>()) => {
+ if (visited.has(this.rootDoc)) return;
+ visited.add(this.rootDoc);
+ showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document._isGroup));
+ if (this.rootDoc._isGroup && this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
+ this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.dragStarting(snapToDraggedDoc, false, visited);
+ }
const activeDocs = this.getActiveDocuments();
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 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
- !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 snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
+ activeDocs.forEach(
+ doc =>
+ doc._isGroup &&
+ SnappingManager.GetIsResizing() !== doc &&
+ !DragManager.docsBeingDragged.includes(doc) &&
+ (DocumentManager.Instance.getDocumentView(doc)?.ComponentView as CollectionFreeFormView)?.dragStarting(snapToDraggedDoc, false, visited)
+ );
const horizLines: number[] = [];
const vertLines: number[] = [];
const invXf = this.getTransform().inverse();
snappableDocs
- .filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc))
+ .filter(doc => !doc._isGroup && (snapToDraggedDoc || (SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))))
.forEach(doc => {
const { left, top, width, height } = docDims(doc);
const topLeftInScreen = invXf.transformPoint(left, top);
@@ -1896,7 +1891,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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);
+ SnappingManager.addSnapLines(horizLines, vertLines);
};
incrementalRendering = () => this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])).length !== 0;
@@ -1926,16 +1921,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
- showPresPaths = () => (CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.getPaths(this.rootDoc) : null);
-
brushedView = () => this._brushedView;
- gridColor = () => {
- const backColor = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box');
- return lightOrDark(backColor);
- };
+ gridColor = () =>
+ DashColor(lightOrDark(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor)))
+ .fade(0.6)
+ .toString();
@computed get marqueeView() {
TraceMobx();
- return (
+ return this._firstRender ? (
+ this.placeholder
+ ) : (
<MarqueeView
{...this.props}
ref={this._marqueeViewRef}
@@ -1960,7 +1955,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}>
{this.layoutDoc._freeform_backgroundGrid ? (
<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!!?
+ <CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render when taking snapshot of a dashboard and the background grid is on!!?
PanelWidth={this.props.PanelWidth}
PanelHeight={this.props.PanelHeight}
panX={this.panX}
@@ -1976,13 +1971,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
) : null}
<CollectionFreeFormViewPannableContents
+ rootDoc={this.rootDoc}
brushedView={this.brushedView}
isAnnotationOverlay={this.isAnnotationOverlay}
- isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
transform={this.contentTransform}
- zoomScaling={this.zoomScaling}
- presPaths={this.showPresPaths}
- presPinView={BoolCast(this.Document.config_pinView)}
transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.rootDoc._viewTransition, 'string', null))}
viewDefDivClick={this.props.viewDefDivClick}>
{this.children}
@@ -2007,12 +1999,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number) => {
this._brushtimer1 && clearTimeout(this._brushtimer1);
this._brushtimer && clearTimeout(this._brushtimer);
- this._brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 };
+ this._brushedView = undefined;
this._brushtimer1 = setTimeout(
action(() => {
- this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2, opacity: 1 };
+ this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2 };
this._brushtimer = setTimeout(
- action(() => (this._brushedView.opacity = 0)),
+ action(() => (this._brushedView = undefined)),
2500
);
}),
@@ -2044,7 +2036,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onClick={this.onClick}
onPointerDown={this.onPointerDown}
onPointerMove={this.onCursorMove}
- onDrop={this.onExternalDrop.bind(this)}
+ onDrop={this.onExternalDrop}
onDragOver={e => e.preventDefault()}
onContextMenu={this.onContextMenu}
style={{
@@ -2081,19 +2073,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
) : (
<>
- {this._firstRender ? this.placeholder : this.marqueeView}
+ {this.marqueeView}
{this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
-
- {/* // 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" />) //
- .concat((this._vLines ?? []).map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)) ?? []}
- </svg>
- </div>
-
- {this.GroupChildDrag ? <div className="collectionFreeForm-groupDropper" /> : null}
+ {!this.GroupChildDrag ? null : <div className="collectionFreeForm-groupDropper" />}
</>
)}
</div>
@@ -2116,119 +2098,52 @@ class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOv
}
interface CollectionFreeFormViewPannableContentsProps {
- transform: () => string;
- zoomScaling: () => number;
+ rootDoc: Doc;
viewDefDivClick?: ScriptField;
children?: React.ReactNode | undefined;
- //children: () => JSX.Element[];
transition?: string;
- presPaths: () => JSX.Element | null;
- presPinView?: boolean;
isAnnotationOverlay: boolean | undefined;
- isAnnotationOverlayScrollable: boolean | undefined;
- brushedView: () => { panX: number; panY: number; width: number; height: number; opacity: number };
+ transform: () => string;
+ brushedView: () => { panX: number; panY: number; width: number; height: number } | undefined;
}
@observer
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 ?? '';
- 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();
- if (doc) {
- switch (this._drag) {
- 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':
- 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':
- 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':
- 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':
- doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px';
- doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px';
- }
- return false;
- }
- return true;
- };
-
@computed get presPaths() {
- return !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>
- {this.props.presPaths()}
- </svg>
- </>
- );
+ return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.rootDoc) : null;
}
+ // rectangle highlight used when following trail/link to a region of a collection that isn't a document
+ showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) =>
+ !viewport ? null : (
+ <div
+ className="collectionFreeFormView-brushView"
+ style={{
+ transform: `translate(${viewport.panX}px, ${viewport.panY}px)`,
+ width: viewport.width,
+ height: viewport.height,
+ border: `orange solid ${viewport.width * 0.005}px`,
+ }}
+ />
+ );
render() {
- const brushedView = this.props.brushedView();
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;
+ target.scrollTop = target.scrollLeft = 0; // if collection is visible, scrolling messes things up since there are no scroll bars
}
}}
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}
- {
- <div
- className="collectionFreeFormView-brushView"
- style={{
- opacity: brushedView.opacity,
- transform: `translate(${brushedView.panX}px, ${brushedView.panY}px)`,
- width: brushedView.width,
- height: brushedView.height,
- border: `orange solid ${brushedView.width * 0.005}px`,
- }}
- />
- }
{this.presPaths}
+ {this.showViewport(this.props.brushedView())}
</div>
);
}
@@ -2250,9 +2165,9 @@ 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);
+ if (!this.props.zoomScaling()) return gridSpace;
+ const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace;
+ return divisions < 90 ? gridSpace : this.chooseGridSpace(gridSpace * 2);
};
render() {
const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc['_backgroundGrid-spacing'], 50));
@@ -2278,14 +2193,22 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor
ctx.clearRect(0, 0, w, h);
if (ctx) {
ctx.strokeStyle = strokeStyle;
+ ctx.fillStyle = 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);
+ if (this.props.zoomScaling() > 1) {
+ 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);
+ }
+ } else {
+ for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace)
+ for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
+ ctx.fillRect(Math.round(x), Math.round(y), 1, 1);
+ }
}
ctx.stroke();
}
@@ -2299,20 +2222,21 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor
export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY: number) {
const browseTransitionTime = 500;
SelectionManager.DeselectAll();
- DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
- if (!focused) {
- const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
- let containers = dv.props.docViewPath();
- let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
- for (var cont of containers) {
- parFfview = parFfview ?? cont.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ dv &&
+ DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
+ if (!focused) {
+ const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
+ let containers = dv.props.docViewPath();
+ let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ for (var cont of containers) {
+ parFfview = parFfview ?? cont.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ }
+ while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 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), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
+ Doc.linkFollowHighlight(dv?.props.Document, false);
}
- while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
- const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 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), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
- Doc.linkFollowHighlight(dv?.props.Document, false);
- }
- });
+ });
}
ScriptingGlobals.add(CollectionBrowseClick);
ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5c053fefc..a30ec5302 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -377,7 +377,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
newCollection._height = this.Bounds.height;
newCollection._isGroup = makeGroup;
newCollection._dragWhenActive = makeGroup;
- newCollection.forceActive = makeGroup;
newCollection.x = this.Bounds.left;
newCollection.y = this.Bounds.top;
newCollection.layout_fitWidth = true;