aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorusodhi <61431818+usodhi@users.noreply.github.com>2021-02-11 16:43:46 -0500
committerusodhi <61431818+usodhi@users.noreply.github.com>2021-02-11 16:43:46 -0500
commit546540013de0a7cb647f30f1fcb513ce52048b72 (patch)
tree12b78ea0e29fba23b8557864540984daf9680942 /src/client/views/collections
parent77b7c3927c454a829d7dbb2748ad322b146265a7 (diff)
parent890337b525ea460f9986562c047135bc5ca203a6 (diff)
merging
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx4
-rw-r--r--src/client/views/collections/CollectionMenu.scss15
-rw-r--r--src/client/views/collections/CollectionMenu.tsx179
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx4
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx53
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx2
-rw-r--r--src/client/views/collections/CollectionView.tsx41
-rw-r--r--src/client/views/collections/SchemaTable.tsx6
-rw-r--r--src/client/views/collections/TabDocView.tsx16
-rw-r--r--src/client/views/collections/TreeView.tsx10
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx45
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx29
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx137
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx6
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx4
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx4
16 files changed, 234 insertions, 321 deletions
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 756346356..ead0ce6c4 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -7,7 +7,7 @@ import { documentSchema } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { makeInterface } from '../../../fields/Schema';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnTrue, Utils } from '../../../Utils';
+import { emptyFunction, returnTrue, Utils, emptyPath, returnEmptyDoclist } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
import { Transform } from '../../util/Transform';
import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
@@ -152,6 +152,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
renderDepth={this.props.renderDepth + 1}
focus={emptyFunction}
styleProvider={this.props.styleProvider}
+ layerProvider={this.props.layerProvider}
+ docViewPath={returnEmptyDoclist}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index e36e5caa7..47adb6a1c 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -6,7 +6,7 @@
display: inline-flex;
width: 100%;
opacity: 0.9;
- z-index: 9001;
+ z-index: 901;
transition: top .5s;
background: #323232;
color: white;
@@ -30,7 +30,7 @@
height: 32px;
border-bottom: .5px solid rgb(180, 180, 180);
overflow: visible;
- z-index: 9001;
+ z-index: 901;
border: unset;
.collectionViewBaseChrome {
@@ -315,10 +315,17 @@
}
}
+.collectionMenu-webUrlButtons {
+ margin-left: 44;
+ background: lightGray;
+ width: 100%;
+ display: flex;
+}
+
.webBox-urlEditor {
position: relative;
opacity: 0.9;
- z-index: 9001;
+ z-index: 901;
transition: top .5s;
.urlEditor {
@@ -354,7 +361,7 @@
}
}
-.webpage-urlInput {
+.collectionMenu-urlInput {
padding: 12px 10px 11px 10px;
border: 0px;
color: black;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 5670d45f5..d6e4b01c4 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -5,7 +5,7 @@ import { Tooltip } from "@material-ui/core";
import { action, computed, Lambda, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { ColorState } from "react-color";
-import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt } from "../../../fields/Doc";
import { Document } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { InkTool } from "../../../fields/InkField";
@@ -15,7 +15,6 @@ import { RichTextField } from "../../../fields/RichTextField";
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { WebField } from "../../../fields/URLField";
import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils";
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
@@ -26,7 +25,7 @@ import { undoBatch } from "../../util/UndoManager";
import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
import { EditableView } from "../EditableView";
import { GestureOverlay } from "../GestureOverlay";
-import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
+import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, ActiveArrowStart, ActiveArrowEnd } from "../InkingStroke";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocumentView } from "../nodes/DocumentView";
import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
@@ -34,6 +33,7 @@ import { PresBox } from "../nodes/PresBox";
import "./CollectionMenu.scss";
import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { TabDocView } from "./TabDocView";
+import { LightboxView } from "../LightboxView";
@observer
export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -488,8 +488,14 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
@computed get lightboxButton() {
const targetDoc = this.selectedDoc;
- return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Lightbox of Images"}</div>} placement="top">
- <button className="antimodeMenu-button" onPointerDown={action(() => targetDoc._isLightboxOpen = true)}>
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Lightbox of Documents"}</div>} placement="top">
+ <button className="antimodeMenu-button" onPointerDown={action(() => {
+ const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
+ if (docs.length) {
+ LightboxView.LightboxDoc = docs[0];
+ LightboxView.LightboxFuture = docs.slice(1);
+ }
+ })}>
<FontAwesomeIcon className="documentdecorations-icon" icon="desktop" size="lg" />
</button>
</Tooltip>;
@@ -549,17 +555,13 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
@computed get dataField() {
return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
}
- @computed get childDocs() {
- return DocListCast(this.dataField);
- }
-
- @computed get selectedDocumentView() {
- return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
- }
+ @computed get childDocs() { return DocListCast(this.dataField); }
+ @computed get selectedDocumentView() { return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; }
@computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
@computed get isText() {
return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false;
}
+ @computed get webBoxUrl() { return this.selectedDocumentView?.ComponentView?.url?.(); }
@undoBatch
@action
@@ -591,20 +593,24 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
private _draw = ["∿", "⎯", "→", "↔︎", "ロ", "O"];
private _head = ["", "", "", "arrow", "", ""];
private _end = ["", "", "arrow", "arrow", "", ""];
- private _shape = ["", "line", "line", "line", "rectangle", "circle"];
+ private _shapePrims = ["", "line", "line", "line", "rectangle", "circle"];
private _title = ["pen", "line", "line with arrow", "line with double arrows", "square", "circle",];
private _faName = ["pen-fancy", "minus", "long-arrow-alt-right", "arrows-alt-h", "square", "circle"];
- @observable _shapesNum = this._shape.length;
- @observable _selected = this._shapesNum;
-
- @observable _keepMode = false;
-
+ @observable _selectedPrimitive = this._shapePrims.length;
+ @observable _keepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
@observable _colorBtn = false;
@observable _widthBtn = false;
@observable _fillBtn = false;
- @action
- clearKeep() { this._selected = this._shapesNum; }
+ @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; }
+ @action primCreated() {
+ if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke=
+ Doc.SetSelectedTool(InkTool.None);
+ this._selectedPrimitive = this._shapePrims.length;
+ SetActiveArrowStart("none");
+ SetActiveArrowEnd("none");
+ }
+ }
@action
changeColor = (color: string, type: string) => {
@@ -636,17 +642,17 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
@computed get drawButtons() {
const func = action((i: number, keep: boolean) => {
- this._keepMode = keep;
- if (this._selected !== i) {
- this._selected = i;
+ this._keepPrimitiveMode = keep;
+ if (this._selectedPrimitive !== i) {
+ this._selectedPrimitive = i;
Doc.SetSelectedTool(InkTool.Pen);
SetActiveArrowStart(this._head[i]);
SetActiveArrowEnd(this._end[i]);
SetActiveBezierApprox("300");
- GestureOverlay.Instance.InkShape = this._shape[i];
+ GestureOverlay.Instance.InkShape = this._shapePrims[i];
} else {
- this._selected = this._shapesNum;
+ this._selectedPrimitive = this._shapePrims.length;
Doc.SetSelectedTool(InkTool.None);
SetActiveArrowStart("");
SetActiveArrowEnd("");
@@ -660,7 +666,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
<button className="antimodeMenu-button"
onPointerDown={() => func(i, false)}
onDoubleClick={() => func(i, true)}
- style={{ backgroundColor: i === this._selected ? "525252" : "", fontSize: "20" }}>
+ style={{ backgroundColor: i === this._selectedPrimitive ? "525252" : "", fontSize: "20" }}>
<FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" />
</button>
</Tooltip>)}
@@ -729,120 +735,35 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</div>;
}
- onUrlDrop = (e: React.DragEvent) => {
+ onWebUrlDrop = (e: React.DragEvent) => {
const { dataTransfer } = e;
const html = dataTransfer.getData("text/html");
const uri = dataTransfer.getData("text/uri-list");
- const url = uri || html || this._url;
+ const url = uri || html || this.webBoxUrl || "";
const newurl = url.startsWith(window.location.origin) ?
- url.replace(window.location.origin, this._url.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url;
- this.submitURL(newurl);
+ url.replace(window.location.origin, this.webBoxUrl?.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url;
+ this.submitWebUrl(newurl);
e.stopPropagation();
}
- onUrlDragover = (e: React.DragEvent) => {
- e.preventDefault();
- }
-
- @computed get _url() {
- return this.selectedDoc?.data instanceof WebField ? Cast(this.selectedDoc.data, WebField, null)?.url.toString() : Field.toString(this.selectedDoc?.data as Field);
- }
-
- set _url(value) {
- if (this.selectedDoc) {
- Doc.GetProto(this.selectedDoc).data = new WebField(value);
- Doc.SetInPlace(this.selectedDoc, "title", value, true);
- const annots = Doc.GetProto(this.selectedDoc)["data-annotations-" + this.urlHash(value)];
- Doc.GetProto(this.selectedDoc)["data-annotations"] = annots instanceof ObjectField ? ObjectField.MakeCopy(annots) : new List<Doc>([]);
- }
- }
-
- @action
- submitURL = (url: string) => {
- if (!url.startsWith("http")) url = "http://" + url;
- try {
- const selectedDoc = this.selectedDoc;
- if (selectedDoc) {
- const URLy = new URL(url);
- const future = Cast(selectedDoc["data-future"], listSpec("string"), null);
- const history = Cast(selectedDoc["data-history"], listSpec("string"), null);
- const annos = DocListCast(selectedDoc["data-annotations"]);
- if (Field.toString(selectedDoc.data as Field) === Field.toString(new WebField(URLy))) {
- Doc.GetProto(selectedDoc).data = new WebField(new URL("http://cs.brown.edu/~avd"));
- setTimeout(action(() => Doc.GetProto(selectedDoc).data = new WebField(URLy)), 100);
- } else {
- if (url) {
- Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(this._url)] = new List<Doc>(annos);
- if (history === undefined) {
- selectedDoc["data-history"] = new List<string>([this._url]);
- } else {
- history.push(this._url);
- }
- this.props.docView.props.Document._scrollTop = 0;
- future && (future.length = 0);
- }
- this._url = url;
- }
- }
- } catch (e) {
- console.log("WebBox URL error:" + url);
- }
- }
-
- urlHash(s: string) {
- return s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0);
- }
-
- onValueKeyDown = async (e: React.KeyboardEvent) => {
- e.key === "Enter" && this.submitURL(this._keyInput.current!.value);
+ onWebUrlValueKeyDown = (e: React.KeyboardEvent) => {
+ e.key === "Enter" && this.submitWebUrl(this._keyInput.current!.value);
e.stopPropagation();
}
-
- @action
- forward = () => {
- const selectedDoc = this.selectedDoc;
- if (selectedDoc) {
- const future = Cast(selectedDoc["data-future"], listSpec("string"), null);
- const history = Cast(selectedDoc["data-history"], listSpec("string"), null);
- if (future?.length) {
- history?.push(this._url);
- Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(selectedDoc["data-annotations"]));
- const newurl = future.pop()!;
- Doc.GetProto(selectedDoc).data = new WebField(new URL(this._url = newurl));
- Doc.GetProto(selectedDoc)["data-annotations"] = new List<Doc>(DocListCast(selectedDoc["data-annotations-" + this.urlHash(newurl)]));
- }
- }
- }
-
- @action
- back = () => {
- const selectedDoc = this.selectedDoc;
- if (selectedDoc) {
- const future = Cast(selectedDoc["data-future"], listSpec("string"), null);
- const history = Cast(selectedDoc["data-history"], listSpec("string"), null);
- if (history?.length) {
- if (future === undefined) selectedDoc["data-future"] = new List<string>([this._url]);
- else future.push(this._url);
- Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(selectedDoc["data-annotations"]));
- const newurl = history.pop()!;
- Doc.GetProto(selectedDoc).data = new WebField(new URL(this._url = newurl));
- Doc.GetProto(selectedDoc)["data-annotations"] = new List<Doc>(DocListCast(selectedDoc["data-annotations-" + this.urlHash(newurl)]));
- }
- }
- }
+ submitWebUrl = (url: string) => this.selectedDocumentView?.ComponentView?.submitURL?.(url);
+ webUrlForward = () => this.selectedDocumentView?.ComponentView?.forward?.();
+ webUrlBack = () => this.selectedDocumentView?.ComponentView?.back?.();
private _keyInput = React.createRef<HTMLInputElement>();
@computed get urlEditor() {
return (
- <div className="webBox-buttons"
- onDrop={this.onUrlDrop}
- onDragOver={this.onUrlDragover} style={{ display: "flex" }}>
- <input className="webpage-urlInput" key={this._url}
+ <div className="collectionMenu-webUrlButtons" onDrop={this.onWebUrlDrop} onDragOver={e => e.preventDefault()} >
+ <input className="collectionMenu-urlInput" key={this.webBoxUrl}
placeholder="ENTER URL"
- defaultValue={this._url}
- onDrop={this.onUrlDrop}
- onDragOver={this.onUrlDragover}
- onKeyDown={this.onValueKeyDown}
+ defaultValue={this.webBoxUrl}
+ onDrop={this.onWebUrlDrop}
+ onDragOver={e => e.preventDefault()}
+ onKeyDown={this.onWebUrlValueKeyDown}
onClick={(e) => {
this._keyInput.current!.select();
e.stopPropagation();
@@ -850,13 +771,13 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
ref={this._keyInput}
/>
<div style={{ display: "flex", flexDirection: "row", justifyContent: "space-between", maxWidth: "250px", }}>
- <button className="submitUrl" onClick={() => this.submitURL(this._keyInput.current!.value)} onDragOver={this.onUrlDragover} onDrop={this.onUrlDrop}>
+ <button className="submitUrl" onClick={() => this.submitWebUrl(this._keyInput.current!.value)} onDragOver={e => e.stopPropagation()} onDrop={this.onWebUrlDrop}>
GO
</button>
- <button className="submitUrl" onClick={this.back}>
+ <button className="submitUrl" onClick={this.webUrlBack}>
<FontAwesomeIcon icon="caret-left" size="lg"></FontAwesomeIcon>
</button>
- <button className="submitUrl" onClick={this.forward}>
+ <button className="submitUrl" onClick={this.webUrlForward}>
<FontAwesomeIcon icon="caret-right" size="lg"></FontAwesomeIcon>
</button>
</div>
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index c39f8b255..66064e354 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -11,7 +11,7 @@ import { listSpec } from "../../../fields/Schema";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { Cast, NumCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
+import { emptyFunction, emptyPath, returnFalse, setupMoveUpEvents, returnEmptyDoclist } from "../../../Utils";
import { SelectionManager } from "../../util/SelectionManager";
import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
@@ -414,6 +414,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
docRangeFilters={this.docRangeFilters}
searchFilterDocs={this.searchFilterDocs}
styleProvider={DefaultStyleProvider}
+ layerProvider={undefined}
+ docViewPath={returnEmptyDoclist}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
moveDocument={this.props.moveDocument}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d8a8723cd..9c12d5020 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -80,13 +80,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
return docs.map((d, i) => {
const height = () => this.getDocHeight(d);
const width = () => this.getDocWidth(d);
- const dref = React.createRef<HTMLDivElement>();
- const dxf = () => this.getDocTransform(d, dref.current!);
- this._docXfs.push({ dxf, width, height });
const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
- return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
- {this.getDisplayDoc(d, dxf, width)}
+ return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} style={style} >
+ {this.getDisplayDoc(d, width)}
</div>;
});
}
@@ -171,8 +168,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
return this.props.addDocTab(doc, where);
}
-
- focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, didFocus?: boolean) => {
+ focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc) => {
Doc.BrushDoc(doc);
this.props.focus(this.props.Document, true); // bcz: want our containing collection to zoom
Doc.linkFollowHighlight(doc);
@@ -186,27 +182,32 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
afterFocus && setTimeout(afterFocus, 500);
}
- getDisplayDoc(doc: Doc, dxf: () => Transform, width: () => number) {
+ styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => {
+ if (property === StyleProp.Opacity && doc) {
+ if (this.props.childOpacity) {
+ return this.props.childOpacity();
+ }
+ if (this.Document._currentFrame !== undefined) {
+ return CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity;
+ }
+ }
+ return this.props.styleProvider?.(doc, props, property);
+ }
+ getDisplayDoc(doc: Doc, width: () => number) {
const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc;
const height = () => this.getDocHeight(doc);
- const styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => {
- if (property === StyleProp.Opacity && doc) {
- if (this.props.childOpacity) {
- return this.props.childOpacity();
- }
- if (this.Document._currentFrame !== undefined) {
- return CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity;
- }
- }
- return this.props.styleProvider?.(doc, props, property);
- };
- return <DocumentView
+
+ let dref: Opt<HTMLDivElement>;
+ const stackedDocTransform = () => this.getDocTransform(doc, dref);
+ return <DocumentView ref={r => dref = r?.ContentDiv ? r.ContentDiv : undefined}
Document={doc}
DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
renderDepth={this.props.renderDepth + 1}
PanelWidth={width}
PanelHeight={height}
- styleProvider={styleProvider}
+ styleProvider={this.styleProvider}
+ layerProvider={this.props.layerProvider}
+ docViewPath={this.props.docViewPath}
LayoutTemplate={this.props.childLayoutTemplate}
LayoutTemplateString={this.props.childLayoutString}
freezeDimensions={this.props.childFreezeDimensions}
@@ -218,7 +219,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
onClick={this.onChildClickHandler}
onDoubleClick={this.onChildDoubleClickHandler}
- ScreenToLocalTransform={dxf}
+ ScreenToLocalTransform={stackedDocTransform}
focus={this.focusDocument}
docFilters={this.docFilters}
docRangeFilters={this.docRangeFilters}
@@ -385,14 +386,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
/>;
}
- getDocTransform(doc: Doc, dref: HTMLDivElement) {
- if (!dref) return Transform.Identity();
+ getDocTransform(doc: Doc, dref?: HTMLDivElement) {
const y = this._scroll; // required for document decorations to update when the text box container is scrolled
const { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
- const outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
- const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
- const offsety = 0;
- return this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + offsety);
+ return new Transform(-translateX, -translateY, 1).scale(this.props.ScreenToLocalTransform().Scale);
}
forceAutoHeight = () => {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b9ad1680a..a1be6d8f2 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -165,7 +165,9 @@ export class
renderDepth={this.props.renderDepth + 1}
rootSelected={returnTrue}
//dontRegisterView={true}
+ docViewPath={this.props.docViewPath}
styleProvider={this.props.styleProvider}
+ layerProvider={this.props.layerProvider}
PanelWidth={this.rtfWidth}
PanelHeight={this.rtfOutlineHeight}
focus={this.props.focus}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 03d8606d7..9ae469930 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,17 +1,16 @@
import { action, computed, observable } from 'mobx';
import { observer } from "mobx-react";
import * as React from 'react';
-import Lightbox from 'react-image-lightbox-with-rotate';
import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
import { DateField } from '../../../fields/DateField';
-import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, Field } from '../../../fields/Doc';
+import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
-import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
-import { ImageField } from '../../../fields/URLField';
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
import { denormalizeEmail, distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
-import { returnFalse, Utils } from '../../../Utils';
+import { returnFalse } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -38,7 +37,6 @@ import { SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from "./CollectionTreeView";
import './CollectionView.scss';
-import { ScriptField } from '../../../fields/ScriptField';
export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');
@@ -85,8 +83,6 @@ export class CollectionView extends Touchable<CollectionViewProps> {
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
_isChildActive = false; //TODO should this be observable?
- get _isLightboxOpen() { return BoolCast(this.props.Document._isLightboxOpen); }
- set _isLightboxOpen(value) { this.props.Document._isLightboxOpen = value; }
@observable private _curLightboxImg = 0;
@observable private static _safeMode = false;
public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
@@ -280,7 +276,6 @@ export class CollectionView extends Touchable<CollectionViewProps> {
!Doc.UserDoc().noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
!Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
- subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
if (!Doc.IsSystem(this.props.Document) && !this.props.Document.annotationOn) {
const existingVm = ContextMenu.Instance.findByDescription(category);
@@ -342,27 +337,6 @@ export class CollectionView extends Touchable<CollectionViewProps> {
}
}
- lightbox = (images: { image: string, title: string, caption: string }[]) => {
- if (!images.length) return (null);
- const mainPath = path.extname(images[this._curLightboxImg].image);
- const nextPath = path.extname(images[(this._curLightboxImg + 1) % images.length].image);
- const prevPath = path.extname(images[(this._curLightboxImg + images.length - 1) % images.length].image);
- const main = images[this._curLightboxImg].image.replace(mainPath, "_o" + mainPath);
- const title = images[this._curLightboxImg].title;
- const caption = images[this._curLightboxImg].caption;
- const next = images[(this._curLightboxImg + 1) % images.length].image.replace(nextPath, "_o" + nextPath);
- const prev = images[(this._curLightboxImg + images.length - 1) % images.length].image.replace(prevPath, "_o" + prevPath);
- return !this._isLightboxOpen ? (null) : (<Lightbox key="lightbox"
- mainSrc={main}
- nextSrc={next}
- prevSrc={prev}
- imageTitle={title}
- imageCaption={caption}
- onCloseRequest={action(() => this._isLightboxOpen = false)}
- onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
- onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
- }
-
bodyPanelWidth = () => this.props.PanelWidth();
childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
@@ -389,13 +363,6 @@ export class CollectionView extends Touchable<CollectionViewProps> {
style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined }}>
{this.showIsTagged()}
{this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
- {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => Cast(d.data, ImageField, null)).map(d =>
- ({
- image: (Cast(d.data, ImageField)!.url.href.indexOf(window.location.origin) === -1) ?
- Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href,
- title: StrCast(d.title),
- caption: Field.toString(d.caption as Field)
- })))}
</div>);
}
}
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index d77f70607..d4b4cf333 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -15,7 +15,7 @@ import { ComputedField } from "../../../fields/ScriptField";
import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
import { ImageField } from "../../../fields/URLField";
import { GetEffectiveAcl } from "../../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../../Utils";
import { Docs, DocumentOptions } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { CompileScript, Transformer, ts } from "../../util/Scripting";
@@ -25,6 +25,7 @@ import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/globa
import { ContextMenu } from "../ContextMenu";
import '../DocumentDecorations.scss';
import { DocumentView } from "../nodes/DocumentView";
+import { DefaultStyleProvider } from "../StyleProvider";
import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders";
import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
@@ -570,6 +571,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
ref="overlay"><DocumentView
Document={this._showDoc}
DataDoc={this._showDataDoc}
+ styleProvider={DefaultStyleProvider}
+ layerProvider={undefined}
+ docViewPath={returnEmptyDoclist}
freezeDimensions={true}
focus={emptyFunction}
renderDepth={this.props.renderDepth}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index c66734556..e1e1c8656 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -12,7 +12,7 @@ import { FieldId } from "../../../fields/RefField";
import { listSpec } from '../../../fields/Schema';
import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { DocumentType } from '../../documents/DocumentTypes';
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -22,9 +22,10 @@ import { SelectionManager } from '../../util/SelectionManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { DocumentView, DocAfterFocusFunc, DocumentViewProps } from "../nodes/DocumentView";
+import { LightboxView } from '../LightboxView';
+import { DocAfterFocusFunc, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
import { FieldViewProps } from '../nodes/FieldView';
-import { PresBox, PresMovement, PinProps } from '../nodes/PresBox';
+import { PinProps, PresBox, PresMovement } from '../nodes/PresBox';
import { DefaultLayerProvider, DefaultStyleProvider, StyleLayers, StyleProp } from '../StyleProvider';
import { CollectionDockingView } from './CollectionDockingView';
import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
@@ -277,9 +278,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
+ case "lightbox": return runInAction(() => LightboxView.LightboxDoc = doc) ? true : false;
case "inPlace":
case "add":
- default: return CollectionDockingView.AddSplit(doc, locationParams, this.stack);
+ default:
+ return CollectionDockingView.AddSplit(doc, locationParams, this.stack);
}
}
@@ -311,6 +314,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
parentActive={returnFalse}
+ docViewPath={returnEmptyDoclist}
childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
noOverlay={true} // don't render overlay Docs since they won't scale
active={returnTrue}
@@ -331,6 +335,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
whenActiveChanged={emptyFunction}
focus={emptyFunction}
styleProvider={TabDocView.miniStyleProvider}
+ layerProvider={undefined}
addDocTab={this.addDocTab}
pinToPres={TabDocView.PinDoc}
docFilters={CollectionDockingView.Instance.docFilters}
@@ -351,7 +356,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
</Tooltip>
</>;
}
- focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, notFocused?: boolean) => {
+ focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc) => {
if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
}
@@ -405,6 +410,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
parentActive={this.active}
whenActiveChanged={emptyFunction}
focus={this.focusFunc}
+ docViewPath={returnEmptyDoclist}
bringToFront={emptyFunction}
pinToPres={TabDocView.PinDoc} />
{this._document._viewType !== CollectionViewType.Freeform ? (null) :
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 816778c59..0d89c7b43 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,7 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss';
import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
@@ -10,7 +9,7 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -21,17 +20,18 @@ import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
import { EditableView } from "../EditableView";
+import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss';
import { DocumentView, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
import { KeyValueBox } from '../nodes/KeyValueBox';
+import { SliderBox } from '../nodes/SliderBox';
import { StyleProp, testDocProps } from '../StyleProvider';
import { CollectionTreeView } from './CollectionTreeView';
import { CollectionView, CollectionViewType } from './CollectionView';
import "./TreeView.scss";
import React = require("react");
-import { SliderBox } from '../nodes/SliderBox';
export interface TreeViewProps {
document: Doc;
@@ -557,6 +557,8 @@ export class TreeView extends React.Component<TreeViewProps> {
Document={this.doc}
DataDoc={undefined}
styleProvider={this.titleStyleProvider}
+ layerProvider={undefined}
+ docViewPath={returnEmptyDoclist}
treeViewDoc={this.props.treeView.props.Document}
addDocument={undefined}
addDocTab={this.props.addDocTab}
@@ -647,6 +649,8 @@ export class TreeView extends React.Component<TreeViewProps> {
renderDepth={this.props.renderDepth + 1}
rootSelected={returnTrue}
styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
+ layerProvider={undefined}
+ docViewPath={this.props.treeView.props.docViewPath}
docFilters={returnEmptyFilter}
docRangeFilters={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index ae5688b48..51cb9387a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -41,44 +41,41 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
if (!linkDoc.linkAutoMove) return;
- const acont = A.props.Document.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
- const bcont = B.props.Document.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
- const adiv = (acont.length ? acont[0] : A.ContentDiv);
- const bdiv = (bcont.length ? bcont[0] : B.ContentDiv);
+ const acont = A.rootDoc.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
+ const bcont = B.rootDoc.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
+ const adiv = acont.length ? acont[0] : A.ContentDiv;
+ const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
const a = adiv.getBoundingClientRect();
const b = bdiv.getBoundingClientRect();
const { left: aleft, top: atop, width: awidth, height: aheight } = adiv.parentElement!.getBoundingClientRect();
const { left: bleft, top: btop, width: bwidth, height: bheight } = bdiv.parentElement!.getBoundingClientRect();
const apt = Utils.closestPtBetweenRectangles(aleft, atop, awidth, aheight, bleft, btop, bwidth, bheight, a.left + a.width / 2, a.top + a.height / 2);
const bpt = Utils.closestPtBetweenRectangles(bleft, btop, bwidth, bheight, aleft, atop, awidth, aheight, apt.point.x, apt.point.y);
- const afield = A.props.LayoutTemplateString?.indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
- const bfield = afield === "anchor1" ? "anchor2" : "anchor1";
// really hacky stuff to make the LinkAnchorBox display where we want it to:
- // if there's an element in the DOM with a classname containing the link's id and a data-targetids attribute containing the other end of the link,
+ // if there's an element in the DOM with a classname containing a link anchor's id,
// then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
// otherwise, we just use the computed nearest point on the document boundary to the target Document
- const linkEles = Array.from(window.document.getElementsByClassName(linkDoc[Id]));
- const targetAhyperlink = linkEles.find((ele: any) => ele.dataset.targetids?.includes((linkDoc[afield] as Doc)[Id]));
- const targetBhyperlink = linkEles.find((ele: any) => ele.dataset.targetids?.includes((linkDoc[bfield] as Doc)[Id]));
+ const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor1 as Doc)[Id])).lastElement();
+ const targetBhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor2 as Doc)[Id])).lastElement();
if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return;
- if (!targetBhyperlink) {
- A.rootDoc[afield + "_x"] = (apt.point.x - aleft) / awidth * 100;
- A.rootDoc[afield + "_y"] = (apt.point.y - atop) / aheight * 100;
+ if (!targetAhyperlink) {
+ linkDoc.anchor1_x = (apt.point.x - aleft) / awidth * 100;
+ linkDoc.anchor1_y = (apt.point.y - atop) / aheight * 100;
} else {
- const m = targetBhyperlink.getBoundingClientRect();
+ const m = targetAhyperlink.getBoundingClientRect();
const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- A.rootDoc[afield + "_x"] = Math.min(1, mp[0] / A.props.PanelWidth()) * 100;
- A.rootDoc[afield + "_y"] = Math.min(1, mp[1] / A.props.PanelHeight()) * 100;
+ linkDoc.anchor1_x = Math.min(1, mp[0] / A.props.PanelWidth()) * 100;
+ linkDoc.anchor1_y = Math.min(1, mp[1] / A.props.PanelHeight()) * 100;
}
- if (!targetAhyperlink) {
- B.rootDoc[bfield + "_x"] = (bpt.point.x - bleft) / bwidth * 100;
- B.rootDoc[bfield + "_y"] = (bpt.point.y - btop) / bheight * 100;
+ if (!targetBhyperlink) {
+ linkDoc.anchor2_x = (bpt.point.x - bleft) / bwidth * 100;
+ linkDoc.anchor2_y = (bpt.point.y - btop) / bheight * 100;
} else {
- const m = targetAhyperlink.getBoundingClientRect();
+ const m = targetBhyperlink.getBoundingClientRect();
const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- B.rootDoc[bfield + "_x"] = Math.min(1, mp[0] / B.props.PanelWidth()) * 100;
- B.rootDoc[bfield + "_y"] = Math.min(1, mp[1] / B.props.PanelHeight()) * 100;
+ linkDoc.anchor2_x = Math.min(1, mp[0] / B.props.PanelWidth()) * 100;
+ linkDoc.anchor2_y = Math.min(1, mp[1] / B.props.PanelHeight()) * 100;
}
}
@@ -162,8 +159,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
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 aActive = A.isSelected() || Doc.IsBrushed(A.props.Document);
- const bActive = B.isSelected() || Doc.IsBrushed(B.props.Document);
+ const aActive = A.isSelected() || Doc.IsBrushed(A.rootDoc);
+ const bActive = B.isSelected() || Doc.IsBrushed(B.rootDoc);
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);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 4dab8f15b..5e0b31754 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -3,32 +3,27 @@ import { observer } from "mobx-react";
import { Doc } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
import { Utils } from "../../../../Utils";
-import { DocumentType } from "../../../documents/DocumentTypes";
import { DocumentManager } from "../../../util/DocumentManager";
import { DocumentView } from "../../nodes/DocumentView";
import "./CollectionFreeFormLinksView.scss";
import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
import React = require("react");
+import { DocumentType } from "../../../documents/DocumentTypes";
@observer
export class CollectionFreeFormLinksView extends React.Component {
@computed get uniqueConnections() {
- const connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
- if (!drawnPairs.reduce((found, drawnPair) => {
- const match1 = (connection.a === drawnPair.a && connection.b === drawnPair.b);
- const match2 = (connection.a === drawnPair.b && connection.b === drawnPair.a);
- const match = match1 || match2;
- if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) {
- drawnPair.l.push(connection.l);
- }
- return match || found;
- }, false)) {
- drawnPairs.push({ a: connection.a, b: connection.b, l: [connection.l] });
- }
- return drawnPairs;
- }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]);
- return connections.filter(c => c.a.props.Document.type === DocumentType.LINK)
- .map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
+ const connections = DocumentManager.Instance.LinkedDocumentViews
+ .filter(c => c.a.props.Document.type === DocumentType.LINK || c.b.props.Document.type === DocumentType.LINK)
+ .reduce((drawnPairs, connection) => {
+ const matchingPairs = drawnPairs.filter(pair => connection.a === pair.a && connection.b === pair.b);
+ matchingPairs.forEach(drawnPair => drawnPair.l.add(connection.l));
+ if (!matchingPairs.length) drawnPairs.push({ a: connection.a, b: connection.b, l: new Set<Doc>([connection.l]) });
+ return drawnPairs;
+ }, [] as { a: DocumentView, b: DocumentView, l: Set<Doc> }[]);
+ const set = new Map<Doc, { a: DocumentView, b: DocumentView, l: Doc[] }>();
+ connections.map(c => !set.has(Array.from(c.l)[0]) && set.set(Array.from(c.l)[0], { a: c.a, b: c.b, l: Array.from(c.l) }));
+ return Array.from(set.values()).map(c => <CollectionFreeFormLinkView key={c.l[0][Id]} A={c.a} B={c.b} LinkDocs={c.l} />);
}
render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6c7512f7c..bba0807a4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -49,6 +49,7 @@ import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
import { StyleProp, StyleLayers } from "../../StyleProvider";
import { DocumentDecorations } from "../../DocumentDecorations";
import { FieldViewProps } from "../../nodes/FieldView";
+import { reset } from "colors";
export const panZoomSchema = createSchema({
_panX: "number",
@@ -271,12 +272,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@undoBatch
@action
- internalPdfAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData, xp: number, yp: number) {
+ internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData, xp: number, yp: number) {
const dragDoc = annoDragData.dropDocument!;
const dropPos = [NumCast(dragDoc.x), NumCast(dragDoc.y)];
dragDoc.x = xp - annoDragData.offset[0] + (NumCast(dragDoc.x) - dropPos[0]);
dragDoc.y = yp - annoDragData.offset[1] + (NumCast(dragDoc.y) - dropPos[1]);
- annoDragData.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
this.bringToFront(dragDoc);
return true;
}
@@ -303,7 +303,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
const [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
if (this.isAnnotationOverlay !== true && de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
- if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalPdfAnnoDrop(e, de.complete.annoDragData, xp, yp);
+ if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData, xp, yp);
if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp);
return false;
}
@@ -789,6 +789,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
+ if (this.Document._isGroup) return;
let deltaScale = deltaY > 0 ? (1 / 1.05) : 1.05;
if (deltaScale < 0) deltaScale = -deltaScale;
const [x, y] = this.getTransform().transformPoint(pointX, pointY);
@@ -815,10 +816,16 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
e.stopPropagation();
}
else if (this.props.active(true)) {
- e.stopPropagation();
- e.preventDefault();
- if (!e.ctrlKey && MarqueeView.DragMarquee) this.setPan(this.panX() + e.deltaX, this.panY() + e.deltaY, "None", true);
- else this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
+ if (!e.ctrlKey && MarqueeView.DragMarquee) {
+ this.setPan(this.panX() + e.deltaX, this.panY() + e.deltaY, "None", true);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ else if (!this.Document._isGroup) {
+ e.stopPropagation();
+ e.preventDefault();
+ this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
+ }
}
this.props.Document.targetScale = NumCast(this.props.Document[this.scaleFieldKey]);
@@ -876,6 +883,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
});
scaleAtPt(docpt: number[], scale: number) {
+ if (this.Document._isGroup) return;
const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
this.Document._viewTransition = "transform 500ms";
this.layoutDoc[this.scaleFieldKey] = scale;
@@ -886,7 +894,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.layoutDoc._panY = NumCast(this.layoutDoc._panY) - newpan[1];
}
- focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, didFocus?: boolean) => {
+ focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => {
const state = HistoryUtil.getState();
// TODO This technically isn't correct if type !== "doc", as
@@ -904,77 +912,68 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
SelectionManager.DeselectAll();
if (this.props.Document.scrollHeight) {
- const annotOn = Cast(doc.annotationOn, Doc) as Doc;
- let delay = 1000;
- if (!annotOn) {
- !dontCenter && this.props.focus(doc);
- afterFocus && setTimeout(afterFocus, delay);
- } else {
- const contextHgt = NumCast(annotOn._height);
- const curScroll = NumCast(this.props.Document._scrollTop);
- let scrollTo = curScroll;
- if (curScroll + contextHgt < NumCast(doc.y)) {
- scrollTo = NumCast(doc.y) + Math.max(NumCast(doc._height), 100) - contextHgt;
- } else if (curScroll > NumCast(doc.y)) {
- scrollTo = Math.max(0, NumCast(doc.y) - 50);
- }
- if (curScroll !== scrollTo || this.props.Document._viewTransition) {
- this.props.Document._scrollPreviewY = this.props.Document._scrollY = scrollTo;
- delay = Math.abs(scrollTo - curScroll) > 5 ? 1000 : 0;
- !dontCenter && this.props.focus(this.props.Document);
- afterFocus && setTimeout(afterFocus, delay);
- } else {
- !dontCenter && delay && this.props.focus(this.props.Document);
- afterFocus?.(!dontCenter && delay ? true : false);
- }
- }
-
+ this.props.focus(doc, undefined, undefined, afterFocus);
} else {
+ const xfToCollection = docTransform ?? Transform.Identity();
const layoutdoc = Doc.Layout(doc);
const savedState = { px: NumCast(this.Document._panX), py: NumCast(this.Document._panY), s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
-
const newState = HistoryUtil.getState();
- let newPanX = savedState.px;
- let newPanY = savedState.py;
- if (!layoutdoc.annotationOn) { // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection
- willZoom && this.setScaleToZoom(layoutdoc, scale);
- newPanX = (NumCast(doc.x) + doc[WidthSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeWidth(this.props.Document)) / 2 / this.zoomScaling() : 0);
- newPanY = (NumCast(doc.y) + doc[HeightSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeHeight(this.props.Document)) / 2 / this.zoomScaling() : 0);
- newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
+ const cantTransform = this.props.isAnnotationOverlay || this.rootDoc._isGroup;
+ const { px, py } = cantTransform ? savedState : this.setPanIntoView(layoutdoc, xfToCollection, willZoom ? scale || .75 : undefined);
+ if (!cantTransform) { // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection
+ newState.initializers![this.Document[Id]] = { panX: px, panY: py };
HistoryUtil.pushState(newState);
}
+ // focus on the document in the collection
+ const didMove = !doc.z && (px !== savedState.px || py !== savedState.py);
+ const focusSpeed = didMove ? (doc.focusSpeed !== undefined ? Number(doc.focusSpeed) : 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) this.setPan(px, py, `transform ${focusSpeed}ms`, 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
+ Doc.BrushDoc(this.rootDoc);
+ !doc.hidden && Doc.linkFollowHighlight(doc);
- if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) {
- // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
- if (!doc.z) this.setPan(newPanX, newPanY, doc.focusSpeed || doc.focusSpeed === 0 ? `transform ${doc.focusSpeed}ms` : "transform 500ms", 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
- }
- Doc.BrushDoc(this.props.Document);
-
- const newDidFocus = didFocus || (newPanX !== savedState.px || newPanY !== savedState.py);
-
- const newAfterFocus = (didFocus: boolean) => {
- afterFocus && setTimeout(() => {
- // @ts-ignore
- if (afterFocus?.(!dontCenter && (didFocus || (newPanX !== savedState.px || newPanY !== savedState.py)))) {
- this.Document._panX = savedState.px;
- this.Document._panY = savedState.py;
- this.Document[this.scaleFieldKey] = savedState.s;
- this.Document._viewTransition = savedState.pt;
- }
- doc.hidden && Doc.UnHighlightDoc(doc);
- }, newPanX !== savedState.px || newPanY !== savedState.py ? 500 : 0);
- return false;
+ // 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 = afterFocus ? await afterFocus(moved) : false;
+ if (resetView) {
+ this.Document._panX = savedState.px;
+ this.Document._panY = savedState.py;
+ this.Document[this.scaleFieldKey] = savedState.s;
+ this.Document._viewTransition = savedState.pt;
+ }
+ return resetView;
};
- this.props.focus(this.props.Document, undefined, undefined, newAfterFocus, undefined, newDidFocus);
- !doc.hidden && Doc.linkFollowHighlight(doc);
+ const xf = !cantTransform ? Transform.Identity() :
+ this.props.isAnnotationOverlay ?
+ new Transform(NumCast(this.rootDoc.x), NumCast(this.rootDoc.y), this.rootDoc[WidthSym]() / Doc.NativeWidth(this.rootDoc))
+ :
+ new Transform(NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]() / 2 - NumCast(this.rootDoc._panX),
+ NumCast(this.rootDoc.y) + this.rootDoc[HeightSym]() / 2 - NumCast(this.rootDoc._panY), 1);
+ this.props.focus(cantTransform ? doc : this.rootDoc, willZoom, scale, (didFocus: boolean) =>
+ new Promise<boolean>(res => setTimeout(async () => res(await endFocus(didMove || didFocus)), focusSpeed)), xf.transform(docTransform ?? Transform.Identity()));
}
-
}
- setScaleToZoom = (doc: Doc, scale: number = 0.75) => {
- const pw = this.isAnnotationOverlay ? Doc.NativeWidth(this.props.Document) : this.props.PanelWidth();
- const ph = this.isAnnotationOverlay ? Doc.NativeHeight(this.props.Document) : this.props.PanelHeight();
- pw && ph && (this.Document[this.scaleFieldKey] = scale * Math.min(pw / NumCast(doc._width), ph / NumCast(doc._height)));
+ setPanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
+ const pw = this.props.PanelWidth() / NumCast(this.layoutDoc._viewScale, 1);
+ const ph = this.props.PanelHeight() / NumCast(this.layoutDoc._viewScale, 1);
+ const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y));
+ const pt2 = xf.transformPoint(NumCast(doc.x) + doc[WidthSym](), NumCast(doc.y) + doc[HeightSym]());
+ const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1] };
+
+ if (scale) {
+ this.Document[this.scaleFieldKey] = scale * Math.min(this.props.PanelWidth() / Math.abs(pt2[0] - pt[0])), this.props.PanelHeight() / Math.abs(pt2[1] - pt[1]);
+ return { px: (bounds.left + bounds.right) / 2, py: (bounds.top + bounds.bot) / 2 };
+ } else {
+ const cx = NumCast(this.layoutDoc._panX);
+ const cy = NumCast(this.layoutDoc._panY);
+ const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
+ return {
+ px: cx + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
+ py: cy + Math.min(0, bounds.top - ph / 10 - screen.top) + Math.max(0, bounds.bot + ph / 10 - screen.bot)
+ };
+ }
}
onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
@@ -988,6 +987,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
pinToPres: this.props.pinToPres,
whenActiveChanged: this.props.whenActiveChanged,
parentActive: this.parentActive,
+ docViewPath: this.props.docViewPath,
DataDoc: childData,
Document: childLayout,
ContainingCollectionView: this.props.CollectionView,
@@ -1005,6 +1005,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
searchFilterDocs: this.searchFilterDocs,
focus: this.focusDocument,
styleProvider: this.getClusterColor,
+ layerProvider: this.props.layerProvider,
freezeDimensions: this.props.childFreezeDimensions,
dropAction: StrCast(this.props.Document.childDropAction) as dropActionType,
bringToFront: this.bringToFront,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 0edbfe7a5..f7fb2b83d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -643,7 +643,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
render() {
return <div className="marqueeView"
- style={{ overflow: (!this.props.ContainingCollectionView && this.props.isAnnotationOverlay) ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
+ style={{
+ overflow: (!this.props.ContainingCollectionView && this.props.isAnnotationOverlay) ? "visible" :
+ StrCast(this.props.Document._overflow),
+ cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand"
+ }}
onDragOver={e => e.preventDefault()}
onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
{this._visible ? this.marqueeDiv : null}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 85013b960..e51417f64 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -6,7 +6,7 @@ import { documentSchema } from '../../../../fields/documentSchemas';
import { List } from '../../../../fields/List';
import { makeInterface } from '../../../../fields/Schema';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse } from '../../../../Utils';
+import { returnFalse, emptyPath, returnEmptyDoclist } from '../../../../Utils';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
@@ -217,6 +217,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
Document={layout}
DataDoc={layout.resolvedDataDoc as Doc}
styleProvider={this.props.styleProvider}
+ layerProvider={undefined}
+ docViewPath={returnEmptyDoclist}
LayoutTemplate={this.props.childLayoutTemplate}
LayoutTemplateString={this.props.childLayoutString}
freezeDimensions={this.props.childFreezeDimensions}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 4f5c8af95..d61a2bb72 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -6,7 +6,7 @@ import { documentSchema } from '../../../../fields/documentSchemas';
import { List } from '../../../../fields/List';
import { makeInterface } from '../../../../fields/Schema';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse } from '../../../../Utils';
+import { returnFalse, emptyPath, returnEmptyDoclist } from '../../../../Utils';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
@@ -217,6 +217,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
Document={layout}
DataDoc={layout.resolvedDataDoc as Doc}
styleProvider={this.props.styleProvider}
+ layerProvider={undefined}
+ docViewPath={returnEmptyDoclist}
LayoutTemplate={this.props.childLayoutTemplate}
LayoutTemplateString={this.props.childLayoutString}
freezeDimensions={this.props.childFreezeDimensions}