aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
authorbrynnchernosky <56202540+brynnchernosky@users.noreply.github.com>2023-01-19 14:33:22 -0500
committerbrynnchernosky <56202540+brynnchernosky@users.noreply.github.com>2023-01-19 14:33:22 -0500
commit0ef7050b0792ce183c7d5cda637cb79b7a92b704 (patch)
treed1dca8f09ddc2954c2ce88439172aeded672c0b6 /src/client/views/collections/collectionFreeForm
parentceb338752aacc383c97a0e3a9b608365a1cf39b6 (diff)
parentd5f796b433d7e72130d4109a3775347ccb10c454 (diff)
Merge branch 'master' of github.com:brown-dash/Dash-Web into master
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss9
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx88
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss50
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx234
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx21
7 files changed, 202 insertions, 211 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 89cc22d07..7dd9cdb8b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -46,7 +46,7 @@ export interface PoolData {
export interface ViewDefResult {
ele: JSX.Element;
bounds?: ViewDefBounds;
- inkMask?: number; //sort elements into either the mask layer (which has a mixedBlendMode appropriate for transparent masks), or the regular documents layer; -1 = no mask, 0 = mask layer but stroke is transprent (hidden), >0 = mask layer and not hidden
+ inkMask?: number; //sort elements into either the mask layer (which has a mixedBlendMode appropriate for transparent masks), or the regular documents layer; -1 = no mask, 0 = mask layer but stroke is transparent (hidden, as in during a presentation when you want to smoothly animate it into being a mask), >0 = mask layer and not hidden
}
function toLabel(target: FieldResult<Field>) {
if (typeof target === 'number' || Number(target)) {
@@ -82,7 +82,7 @@ interface PivotColumn {
filters: string[];
}
-export function computerPassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
+export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
const docMap = new Map<string, PoolData>();
childPairs.forEach(({ layout, data }, i) => {
docMap.set(layout[Id], {
@@ -97,7 +97,7 @@ export function computerPassLayout(poolData: Map<string, PoolData>, pivotDoc: Do
return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
}
-export function computerStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
+export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel
const docMap = new Map<string, PoolData>();
const docSize = mustFit ? panelDim[0] * 0.33 : 75; // assume an icon sized at 75
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
index 858719a08..b44acfce8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -5,15 +5,8 @@
transition: opacity 0.5s ease-in;
fill: transparent;
}
-.collectionfreeformlinkview-linkCircle {
- stroke: rgb(0,0,0);
- opacity: 0.5;
- pointer-events: all;
- cursor: pointer;
-}
.collectionfreeformlinkview-linkText {
- stroke: rgb(0,0,0);
- opacity: 0.5;
+ stroke: rgb(0, 0, 0);
pointer-events: all;
cursor: move;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index bf9de6760..8919b1c01 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -3,7 +3,7 @@ import { observer } from 'mobx-react';
import { Doc, Field } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
-import { Cast, NumCast } from '../../../../fields/Types';
+import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
import { LinkManager } from '../../../util/LinkManager';
import { SelectionManager } from '../../../util/SelectionManager';
@@ -44,7 +44,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
this._start = Date.now();
this._timeout && clearTimeout(this._timeout);
this._timeout = setTimeout(this.timeout, 25);
- setTimeout(this.placeAnchors);
+ setTimeout(this.placeAnchors, 10); // when docs are dragged, their transforms will update before a render has been performed. placeanchors needs to come after a render to find things in the dom. a 0 timeout will still come before the render
}),
{ fireImmediately: true }
);
@@ -117,12 +117,16 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
return false;
},
emptyFunction,
- () => {
+ action(() => {
+ SelectionManager.DeselectAll();
+ SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
+ LinkManager.currentLink = this.props.LinkDocs[0];
+ this.toggleProperties();
// OverlayView.Instance.addElement(
// <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
// showLinks={action(() => { })}
// />, { x: 300, y: 300 });
- }
+ })
);
};
@@ -163,15 +167,16 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
@action
toggleProperties = () => {
- if (SettingsManager.propertiesWidth > 0) {
- SettingsManager.propertiesWidth = 0;
- } else {
+ if ((SettingsManager.propertiesWidth ?? 0) < 100) {
SettingsManager.propertiesWidth = 250;
}
};
+ @action
onClickLine = () => {
+ SelectionManager.DeselectAll();
SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
+ LinkManager.currentLink = this.props.LinkDocs[0];
this.toggleProperties();
};
@@ -199,15 +204,17 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const bclipped = bleft !== b.left || btop !== b.top;
if (aclipped && bclipped) return undefined;
const clipped = aclipped || bclipped;
+ const pt1inside = NumCast(LinkDocs[0].anchor1_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor1_y) % 100 !== 0;
+ const pt2inside = NumCast(LinkDocs[0].anchor2_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor2_y) % 100 !== 0;
const pt1 = [aleft + a.width / 2, atop + a.height / 2];
const pt2 = [bleft + b.width / 2, btop + b.width / 2];
- const pt1vec = [(bDocBounds.left + bDocBounds.right) / 2 - pt1[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt1[1]];
- const pt2vec = [(aDocBounds.left + aDocBounds.right) / 2 - pt2[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt2[1]];
+ const pt2vec = pt2inside ? [-0.7071, 0.7071] : [(bDocBounds.left + bDocBounds.right) / 2 - pt2[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt2[1]];
+ const pt1vec = pt1inside ? [-0.7071, 0.7071] : [(aDocBounds.left + aDocBounds.right) / 2 - pt1[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt1[1]];
const pt1len = Math.sqrt(pt1vec[0] * pt1vec[0] + pt1vec[1] * pt1vec[1]);
const pt2len = Math.sqrt(pt2vec[0] * pt2vec[0] + pt2vec[1] * pt2vec[1]);
const ptlen = Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) / 2;
- const pt1norm = clipped ? [0, 0] : [(pt1vec[0] / pt1len) * ptlen, (pt1vec[1] / pt1len) * ptlen];
- const pt2norm = clipped ? [0, 0] : [(pt2vec[0] / pt2len) * ptlen, (pt2vec[1] / pt2len) * ptlen];
+ const pt1norm = clipped ? [0, 0] : [-(pt1vec[0] / pt1len) * ptlen, -(pt1vec[1] / pt1len) * ptlen];
+ const pt2norm = clipped ? [0, 0] : [-(pt2vec[0] / pt2len) * ptlen, -(pt2vec[1] / pt2len) * ptlen];
const pt1normlen = Math.sqrt(pt1norm[0] * pt1norm[0] + pt1norm[1] * pt1norm[1]) || 1;
const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
@@ -217,51 +224,82 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].linkOffsetX);
const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].linkOffsetY);
- return { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13], pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13] };
+ return {
+ a,
+ b,
+ pt1norm,
+ pt2norm,
+ aActive,
+ bActive,
+ textX,
+ textY,
+ pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13],
+ pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13],
+ };
}
render() {
if (!this.renderData) return null;
+ const link = this.props.LinkDocs[0];
const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
- LinkManager.currentLink = this.props.LinkDocs[0];
- const linkRelationship = Field.toString(LinkManager.currentLink?.linkRelationship as any as Field); //get string representing relationship
+ const linkRelationship = Field.toString(link?.linkRelationship as any as Field); //get string representing relationship
const linkRelationshipList = Doc.UserDoc().linkRelationshipList as List<string>;
const linkColorList = Doc.UserDoc().linkColorList as List<string>;
const linkRelationshipSizes = Doc.UserDoc().linkRelationshipSizes as List<number>;
const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship);
+ const linkDescription = Field.toString(link.description as any as Field);
const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
//access stroke color using index of the relationship in the color list (default black)
- const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? 'black' : linkColorList[currRelationshipIndex];
+ const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? StrCast(link._backgroundColor, 'black') : linkColorList[currRelationshipIndex];
// const hexStroke = this.rgbToHex(stroke)
//calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has)
//thickness varies linearly from 3px to 12px for increasing link count
const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
- if (this.props.LinkDocs[0].displayArrow === undefined) {
- this.props.LinkDocs[0].displayArrow = false;
+ if (link.linkDisplayArrow === undefined) {
+ link.linkDisplayArrow = false;
}
- return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || (!this.props.LinkDocs[0].linkDisplay && !aActive && !bActive) ? null : (
+ return link.opacity === 0 || !a.width || !b.width || (!link.linkDisplay && !aActive && !bActive) ? null : (
<>
<defs>
- <marker id="arrowhead" markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
- <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
+ <marker id={`${link[Id] + 'arrowhead'}`} markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
+ <polygon points="0 0, 3 1.5, 0 3" fill={stroke} />
</marker>
+ <filter id="outline">
+ <feMorphology in="SourceAlpha" result="expanded" operator="dilate" radius="1" />
+ <feFlood floodColor={`${Colors.DARK_GRAY}`} />
+ <feComposite in2="expanded" operator="in" />
+ <feComposite in="SourceGraphic" />
+ </filter>
+ <filter x="0" y="0" width="1" height="1" id={`${link[Id] + 'background'}`}>
+ <feFlood floodColor={`${StrCast(link._backgroundColor, 'white')}`} result="bg" />
+ <feMerge>
+ <feMergeNode in="bg" />
+ <feMergeNode in="SourceGraphic" />
+ </feMerge>
+ </filter>
</defs>
<path
+ filter={LinkManager.currentLink === link ? 'url(#outline)' : ''}
+ fill="pink"
+ stroke="antiquewhite"
+ strokeWidth="4"
className="collectionfreeformlinkview-linkLine"
- style={{ pointerEvents: 'all', opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
+ style={{ pointerEvents: 'all', opacity: this._opacity, stroke, strokeWidth }}
onClick={this.onClickLine}
d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
- markerEnd={this.props.LinkDocs[0].displayArrow ? 'url(#arrowhead)' : ''}
+ markerEnd={link.linkDisplayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
/>
- {textX === undefined ? null : (
- <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
- {Field.toString(this.props.LinkDocs[0].description as any as Field)}
+ {textX === undefined || !linkDescription ? null : (
+ <text filter={`url(#${link[Id] + 'background'})`} className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
+ <tspan>&nbsp;</tspan>
+ <tspan dy="2">{linkDescription}</tspan>
+ <tspan dy="2">&nbsp;</tspan>
</text>
)}
</>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index b8344dc0c..420e6a318 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -2,13 +2,13 @@ import { computed } from 'mobx';
import { observer } from 'mobx-react';
import { Id } from '../../../../fields/FieldSymbols';
import { DocumentManager } from '../../../util/DocumentManager';
+import { LightboxView } from '../../LightboxView';
import './CollectionFreeFormLinksView.scss';
import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
import React = require('react');
-import { LightboxView } from '../../LightboxView';
@observer
-export class CollectionFreeFormLinksView extends React.Component<React.PropsWithChildren<{}>> {
+export class CollectionFreeFormLinksView extends React.Component {
@computed get uniqueConnections() {
return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews))
.filter(c => !LightboxView.LightboxDoc || (LightboxView.IsLightboxDocView(c.a.docViewPath) && LightboxView.IsLightboxDocView(c.b.docViewPath)))
@@ -19,7 +19,6 @@ export class CollectionFreeFormLinksView extends React.Component<React.PropsWith
return (
<div className="collectionfreeformlinksview-container">
<svg className="collectionfreeformlinksview-svgCanvas">{this.uniqueConnections}</svg>
- {this.props.children}
</div>
);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index d80fcdfc3..7a7ae3f40 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -96,37 +96,6 @@
border-color: #69a5db;
}
-.progressivizeButton {
- position: absolute;
- display: grid;
- grid-template-columns: auto 20px auto;
- transform: translate(-105%, 0);
- align-items: center;
- border: black solid 1px;
- border-radius: 3px;
- justify-content: center;
- width: 40;
- z-index: 30000;
- height: 20;
- overflow: hidden;
- background-color: #d5dce2;
- transition: all 1s;
-
- .progressivizeButton-prev:hover {
- color: #5a9edd;
- }
-
- .progressivizeButton-frame {
- justify-self: center;
- text-align: center;
- width: 15px;
- }
-
- .progressivizeButton-next:hover {
- color: #5a9edd;
- }
-}
-
.resizable {
background: rgba(0, 0, 0, 0.2);
width: 100px;
@@ -178,25 +147,6 @@
}
}
-.progressivizeMove-frame {
- width: 20px;
- border-radius: 2px;
- z-index: 100000;
- color: white;
- text-align: center;
- background-color: #5a9edd;
- transform: translate(-110%, 110%);
-}
-
-.progressivizeButton:hover {
- box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.5);
-
- .progressivizeButton-frame {
- background-color: #5a9edd;
- color: white;
- }
-}
-
.collectionFreeform-customText {
position: absolute;
text-align: center;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2f246e74f..dc0eb69f3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,4 +1,5 @@
import { Bezier } from 'bezier-js';
+import { Colors } from 'browndash-components';
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
@@ -37,7 +38,7 @@ import { GestureOverlay } from '../../GestureOverlay';
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
import { LightboxView } from '../../LightboxView';
import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
-import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, ViewAdjustment, ViewSpecPrefix } from '../../nodes/DocumentView';
import { FieldViewProps } from '../../nodes/FieldView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { PresBox } from '../../nodes/trails/PresBox';
@@ -47,7 +48,7 @@ import { StyleProp } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeView';
import { TabDocView } from '../TabDocView';
-import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
+import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
@@ -71,6 +72,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return 'CollectionFreeFormView(' + this.props.Document.title?.toString() + ')';
} // this makes mobx trace() statements more descriptive
+ @observable
+ public static ShowPresPaths = false;
+
private _lastNudge: any;
private _lastX: number = 0;
private _lastY: number = 0;
@@ -105,7 +109,7 @@ 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 _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 _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 = 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.
@@ -118,6 +122,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable ChildDrag: DocumentView | undefined; // 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);
+ this.props.setContentView?.(this);
+ }
+
@computed get views() {
const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele);
const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && (ele.inkMask === -1 || ele.inkMask === undefined)).map(ele => ele.ele);
@@ -173,6 +182,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this.getContainerTransform().translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
}
+ _keyTimer: NodeJS.Timeout | undefined;
changeKeyFrame = (back = false) => {
const currentFrame = Cast(this.Document._currentFrame, 'number', null);
if (currentFrame === undefined) {
@@ -180,14 +190,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
}
if (back) {
- CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice());
+ this._keyTimer = CollectionFreeFormDocumentView.gotoKeyframe(this._keyTimer, this.childDocs.slice());
this.Document._currentFrame = Math.max(0, (currentFrame || 0) - 1);
} else {
- CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0);
+ this._keyTimer = CollectionFreeFormDocumentView.updateKeyframe(this._keyTimer, this.childDocs, currentFrame || 0);
this.Document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame));
}
};
+ @observable _keyframeEditing = false;
+ @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);
onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
@@ -709,6 +722,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
pan = (e: PointerEvent | React.Touch | { clientX: number; clientY: number }): void => {
+ this.props.DocumentView?.().clearViewTransition();
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;
@@ -792,9 +806,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y },
});
const intersects = Array.from(new Set(rawIntersects as (number | string)[])); // convert to more manageable union array type
- if (intersects.length) {
- console.log();
- }
// 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
}
@@ -1008,7 +1019,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (this.layoutDoc._Transform || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
e.stopPropagation();
e.preventDefault();
- switch (Doc.UserDoc().freeformScrollMode) {
+ switch (!e.ctrlKey ? Doc.UserDoc().freeformScrollMode : freeformScrollMode.Pan) {
case freeformScrollMode.Pan:
// if shift is selected then zoom
if (e.ctrlKey) {
@@ -1021,8 +1032,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
} else if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
// things that can scroll vertically should do that instead of zooming
} else if (this.props.isContentActive(true) && !this.Document._isGroup) {
- const dx = e.deltaX;
- const dy = e.deltaY;
+ const dx = -e.deltaX;
+ const dy = -e.deltaY;
if (e.shiftKey) {
!this.props.isAnnotationOverlayScrollable && this.scrollPan({ deltaX: dy, deltaY: 0 });
} else {
@@ -1076,7 +1087,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) {
- this._viewTransition = panTime;
+ this._panZoomTransition = panTime;
const scale = this.getLocalTransform().inverse().Scale;
const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
const minPanX = NumCast(this.rootDoc._panXMin, 0);
@@ -1095,7 +1106,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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)),
+ action(() => (this._panZoomTransition = 0)),
nudgeTime
);
return true;
@@ -1124,7 +1135,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) {
if (this.Document._isGroup) return;
- this._viewTransition = transitionTime;
+ this._panZoomTransition = transitionTime;
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]);
@@ -1132,9 +1143,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const newpan = this.getTransform().transformDirection(scrDelta.x, scrDelta.y);
this.layoutDoc._panX = NumCast(this.layoutDoc._panX) - newpan[0];
this.layoutDoc._panY = NumCast(this.layoutDoc._panY) - newpan[1];
- return new Promise<number>(res => setTimeout(() => res(runInAction(() => (this._viewTransition = 0))), this._viewTransition)); // set transition to be smooth, then reset
+ return new Promise<number>(res => setTimeout(() => res(runInAction(() => (this._panZoomTransition = 0))), this._panZoomTransition)); // set transition to be smooth, then reset
}
+ _focusCount = 0;
focusDocument = (doc: Doc, options: DocFocusOptions) => {
const state = HistoryUtil.getState();
@@ -1154,14 +1166,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) {
// SelectionManager.DeselectAll();
// }
- if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined) {
+ if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined || doc.type === DocumentType.MARKER) {
this.props.focus(doc, options);
} else {
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 savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willPanZoom ? this.Document[this.scaleFieldKey] : undefined };
const newState = HistoryUtil.getState();
const cantTransform = (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);
+ const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willPanZoom) ? savedState : this.calculatePanIntoView(doc, xfToCollection, options?.willPanZoom ? options?.zoomScale || 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, panY };
@@ -1169,27 +1181,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
// focus on the document in the collection
const didMove = !cantTransform && !doc.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale);
- const focusSpeed = options?.instant ? 0 : didMove ? NumCast(doc.focusSpeed, 500) : 0;
+ const focusSpeed = options?.instant ? 0 : didMove ? options.zoomTime ?? 500 : 0;
// glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
if (didMove) {
- scale && (this.Document[this.scaleFieldKey] = scale);
+ options.zoomScale && scale && (this.Document[this.scaleFieldKey] = scale);
this.setPan(panX, panY, focusSpeed, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
}
+ const focusCount = ++this._focusCount;
const startTime = Date.now();
// focus on this collection within its parent view. the parent view after focusing determines whether to reset the view change within the collection
const endFocus = async (moved: boolean) => {
doc.hidden && Doc.UnHighlightDoc(doc);
const resetView = options?.afterFocus ? await options?.afterFocus(moved) : ViewAdjustment.doNothing;
if (resetView) {
- const restoreState = (!LightboxView.LightboxDoc || LightboxView.LightboxDoc === this.props.Document) && savedState;
+ const restoreState = savedState;
if (typeof restoreState !== 'boolean') {
this.Document._panX = restoreState.panX;
this.Document._panY = restoreState.panY;
this.Document[this.scaleFieldKey] = restoreState.scale;
}
}
- runInAction(() => (this._viewTransition = 0));
+ this._focusCount === focusCount && didMove && runInAction(() => (this._panZoomTransition = 0));
return resetView;
};
const xf = !cantTransform
@@ -1229,6 +1242,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const cx = NumCast(this.layoutDoc._panX) + (this.props.isAnnotationOverlay ? pw / 2 : 0);
const cy = NumCast(this.layoutDoc._panY) + (this.props.isAnnotationOverlay ? ph / 2 : 0);
const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
+ const maxYShift = Math.max(0, screen.bot - screen.top - (bounds.bot - bounds.top));
+ const phborder = bounds.top < screen.top || bounds.bot > screen.bot ? Math.min(ph / 10, maxYShift / 2) : 0;
if (screen.right - screen.left < bounds.right - bounds.left || screen.bot - screen.top < bounds.bot - bounds.top) {
return {
panX: (bounds.left + bounds.right) / 2,
@@ -1238,7 +1253,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
return {
panX: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panX) : cx) + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
- panY: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panY) : cy) + Math.min(0, bounds.top - ph / 10 - screen.top) + Math.max(0, bounds.bot + ph / 10 - screen.bot),
+ panY: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panY) : cy) + Math.min(0, bounds.top - phborder - screen.top) + Math.max(0, bounds.bot + phborder - screen.bot),
};
};
@@ -1267,7 +1282,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
pointerEvents = () => {
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
- const pointerEvents = this.props.isContentActive() === false ? 'none' : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === 'pass' && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.());
+ const pointerEvents =
+ this.props.isContentActive() === false ? 'none' : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.());
return pointerEvents;
};
getChildDocView(entry: PoolData) {
@@ -1309,6 +1325,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
docViewPath={this.props.docViewPath}
styleProvider={this.getClusterColor}
+ canEmbedOnDrag={this.props.childCanEmbedOnDrag}
dataProvider={this.childDataProvider}
sizeProvider={this.childSizeProvider}
dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
@@ -1322,18 +1339,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
/>
);
}
- addDocTab = action((doc: Doc, where: string) => {
- 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) {
- this.dataDoc[this.props.fieldKey] = doc instanceof Doc ? doc : new List<Doc>(doc as any as Doc[]);
- return true;
+ addDocTab = action((doc: Doc, where: OpenWhere) => {
+ switch (where) {
+ case OpenWhere.inParent:
+ return this.props.addDocument?.(doc) || false;
+ case OpenWhere.inParentFromScreen:
+ return (
+ this.props.addDocument?.(
+ (doc instanceof Doc ? [doc] : doc).map(doc => {
+ const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
+ doc.x = pt[0];
+ doc.y = pt[1];
+ return doc;
+ })
+ ) || false
+ );
+ case OpenWhere.inPlace:
+ if (this.layoutDoc.isInPlaceContainer) {
+ this.dataDoc[this.props.fieldKey] = new List<Doc>(doc instanceof Doc ? [doc] : doc);
+ return true;
+ }
}
return this.props.addDocTab(doc, where);
});
@@ -1351,7 +1376,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
z: Cast(z, 'number'),
color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.props.styleProvider?.(childDoc, this.props, StyleProp.BackgroundColor),
- opacity: Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
+ opacity: this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
zIndex: Cast(zIndex, 'number'),
width: Cast(childDocLayout._width, 'number'),
height: Cast(childDocLayout._height, 'number'),
@@ -1451,10 +1476,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const newPool = new Map<string, PoolData>();
// prettier-ignore
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 computePassLayout.name : return { newPool, computedElementData: this.doEngineLayout(newPool, computePassLayout) };
+ case computeTimelineLayout.name: return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
+ case computePivotLayout.name: return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
+ case computeStarburstLayout.name: return { newPool, computedElementData: this.doEngineLayout(newPool, computeStarburstLayout) };
}
return { newPool, computedElementData: this.doFreeformLayout(newPool) };
}
@@ -1494,7 +1519,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
elements.push({
ele: this.getChildDocView(entry[1]),
bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica),
- inkMask: BoolCast(entry[1].pair.layout.isInkMask) ? NumCast(entry[1].pair.layout.opacity) : -1,
+ inkMask: BoolCast(entry[1].pair.layout.isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
})
);
@@ -1524,11 +1549,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return 0;
};
+ @action
+ scrollFocus = (anchor: Doc, options: DocFocusOptions) => {
+ const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
+ return PresBox.restoreTargetDocView(
+ this.props.DocumentView?.(), //
+ { pinDocLayout: BoolCast(anchor.presPinDocLayout) },
+ anchor,
+ focusSpeed,
+ {
+ pannable: anchor.presPinData ? true : false,
+ }
+ )
+ ? focusSpeed
+ : undefined;
+ }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
+
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), presTransition: 500, annotationOn: this.rootDoc });
+ PresBox.pinDocView(anchor, { pinData: { pannable: true } }, this.rootDoc);
const proto = Doc.GetProto(anchor);
proto[ViewSpecPrefix + '_viewType'] = this.layoutDoc._viewType;
proto.docFilters = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List<string>([]);
@@ -1543,7 +1585,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
componentDidMount() {
super.componentDidMount?.();
- this.props.setContentView?.(this);
this.props.setBrushViewer?.(this.brushView);
setTimeout(
action(() => {
@@ -1566,12 +1607,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
r: cbounds.r - p[0] + c[0],
b: cbounds.b - p[1] + 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;
+ if (Number.isFinite(pbounds.r - pbounds.x) && Number.isFinite(pbounds.b - pbounds.y)) {
+ 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 }
@@ -1701,7 +1744,7 @@ 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, OpenWhere.inParentFromScreen);
this.props.ContainingCollectionView?.removeDocument(this.props.Document);
};
@@ -1839,6 +1882,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
DragManager.SetSnapLines(horizLines, vertLines);
};
+ incrementalRendering = () => this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])).length !== 0;
+
incrementalRender = action(() => {
if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) {
const unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
@@ -1864,6 +1909,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
+ showPresPaths = () => (CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.getPaths(this.rootDoc) : null);
+
@computed get marqueeView() {
TraceMobx();
return (
@@ -1910,10 +1957,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
transform={this.contentTransform}
zoomScaling={this.zoomScaling}
- presPaths={BoolCast(this.Document.presPathView)}
- progressivize={BoolCast(this.Document.editProgressivize)}
+ presPaths={this.showPresPaths}
presPinView={BoolCast(this.Document.presPinView)}
- transition={this._viewTransition ? `transform ${this._viewTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', null)}
+ transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', null)}
viewDefDivClick={this.props.viewDefDivClick}>
{this.children}
</CollectionFreeFormViewPannableContents>
@@ -1985,19 +2031,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
{this._firstRender ? this.placeholder : 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" />
- ))}
- {this._vLines?.map(l => (
- <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />
- ))}
- </svg>
- </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
@@ -2039,8 +2083,7 @@ interface CollectionFreeFormViewPannableContentsProps {
viewDefDivClick?: ScriptField;
children: () => JSX.Element[];
transition?: string;
- presPaths?: boolean;
- progressivize?: boolean;
+ presPaths: () => JSX.Element | null;
presPinView?: boolean;
isAnnotationOverlay: boolean | undefined;
isAnnotationOverlayScrollable: boolean | undefined;
@@ -2093,42 +2136,11 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
return true;
};
- // scale: NumCast(targetDoc._viewScale),
- @computed get zoomProgressivizeContainer() {
- const activeItem = PresBox.Instance.activeItem;
- // const targetDoc = PresBox.Instance.targetDoc;
- if (activeItem && activeItem.presPinView && activeItem.id) {
- const left = NumCast(activeItem.presPinViewX);
- const top = NumCast(activeItem.presPinViewY);
- const width = 100;
- const height = 100;
- 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>
- </div>
- );
- }
- }
-
- @computed get zoomProgressivize() {
- 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;
- }
-
@computed get presPaths() {
- const presPaths = 'presPaths' + (this.props.presPaths ? '' : '-hidden');
- return !PresBox.Instance || !this.props.presPaths ? null : (
+ return !this.props.presPaths() ? null : (
<>
- <div key="presorder">{PresBox.Instance.order}</div>
- <svg key="svg" className={presPaths}>
+ <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" />
@@ -2140,7 +2152,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
<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}
+ {this.props.presPaths()}
</svg>
</>
);
@@ -2180,8 +2192,6 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
/>
)}
{this.presPaths}
- {this.progressivize}
- {this.zoomProgressivize}
</div>
);
}
@@ -2250,7 +2260,7 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor
export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY: number) {
SelectionManager.DeselectAll();
dv.props.focus(dv.props.Document, {
- willZoom: true,
+ willPanZoom: true,
afterFocus: async didMove => {
if (!didMove) {
const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
@@ -2270,6 +2280,14 @@ ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
!readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
});
+ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) {
+ const selView = SelectionManager.Views();
+ if (readOnly) return selView[0].ComponentView?.getKeyFrameEditing?.() ? Colors.MEDIUM_BLUE : 'transparent';
+ runInAction(() => selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.()));
+});
ScriptingGlobals.add(function pinWithView(readOnly: boolean, pinDocContent: boolean) {
- !readOnly && SelectionManager.Views().forEach(view => TabDocView.PinDoc(view.rootDoc, { pinDocContent, pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()) }));
+ !readOnly &&
+ SelectionManager.Views().forEach(view =>
+ TabDocView.PinDoc(view.rootDoc, { currentFrame: Cast(view.rootDoc.currentFrame, 'number', null), pinDocContent, pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()) })
+ );
});
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a020b67cd..dcbadc5f3 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -17,8 +17,8 @@ import { SelectionManager } from '../../../util/SelectionManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
+import { OpenWhere } from '../../nodes/DocumentView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
-import { PresBox } from '../../nodes/trails/PresBox';
import { VideoBox } from '../../nodes/VideoBox';
import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
import { PreviewCursor } from '../../PreviewCursor';
@@ -120,7 +120,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const cm = ContextMenu.Instance;
const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
if (e.key === '?') {
- cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', useCors: true }), 'add:right'));
+ cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', useCors: true }), OpenWhere.addRight));
cm.displayMenu(this._downX, this._downY, undefined, true);
e.stopPropagation();
@@ -243,7 +243,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (!(e.nativeEvent as any).marqueeHit) {
(e.nativeEvent as any).marqueeHit = true;
// allow marquee if right click OR alt+left click OR in adding presentation slide & left key drag mode
- if (e.button === 2 || (e.button === 0 && e.altKey) || (PresBox.startMarquee && e.button === 0)) {
+ if (e.button === 2 || (e.button === 0 && e.altKey)) {
// if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
this.setPreviewCursor(e.clientX, e.clientY, true, false);
// (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
@@ -272,9 +272,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.cleanupInteractions(true); // stop listening for events if another lower-level handle (e.g. another Marquee) has stopPropagated this
}
e.altKey && e.preventDefault();
- if (PresBox.startMarquee) {
- e.stopPropagation();
- }
};
@action
@@ -295,11 +292,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
document.removeEventListener('pointerdown', hideMarquee, true);
document.removeEventListener('wheel', hideMarquee, true);
};
- if (PresBox.startMarquee) {
- this.pinWithView();
- PresBox.startMarquee = false;
- }
- if (!this._commandExecuted && Math.abs(this.Bounds.height * this.Bounds.width) > 100 && !PresBox.startMarquee) {
+ if (!this._commandExecuted && Math.abs(this.Bounds.height * this.Bounds.width) > 100) {
MarqueeOptionsMenu.Instance.createCollection = this.collection;
MarqueeOptionsMenu.Instance.delete = this.delete;
MarqueeOptionsMenu.Instance.summarize = this.summary;
@@ -393,7 +386,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
: ((doc: Doc) => {
Doc.GetProto(doc).data = new List<Doc>(selected);
Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
- !this.props.isAnnotationOverlay && Doc.AddDocToList(Doc.MyFileOrphans, undefined, Doc.GetProto(doc));
+ !this.props.isAnnotationOverlay && Doc.AddFileOrphan(Doc.GetProto(doc));
doc._panX = doc._panY = 0;
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
@@ -540,7 +533,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
d.y = NumCast(d.y) - this.Bounds.top;
return d;
});
- const summary = Docs.Create.TextDocument('', { backgroundColor: '#e2ad32', x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: 'overview' });
+ const summary = Docs.Create.TextDocument('', { backgroundColor: '#e2ad32', x: this.Bounds.left, y: this.Bounds.top, followLinkToggle: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: 'overview' });
const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
DocUtils.MakeLink({ doc: summary }, { doc: portal }, 'summary of:summarized by', '');
@@ -685,7 +678,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
className="marqueeView"
style={{
overflow: StrCast(this.props.Document._overflow),
- cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible || PresBox.startMarquee ? 'crosshair' : 'pointer',
+ cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible ? 'crosshair' : 'pointer',
}}
onDragOver={e => e.preventDefault()}
onScroll={e => (e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0)}