aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx2
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx28
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx1
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx10
-rw-r--r--src/client/views/collections/CollectionSubView.tsx1
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx9
-rw-r--r--src/client/views/collections/CollectionView.tsx19
-rw-r--r--src/client/views/collections/TabDocView.tsx192
-rw-r--r--src/client/views/collections/TreeView.scss2
-rw-r--r--src/client/views/collections/TreeView.tsx298
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx15
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx92
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx23
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx11
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx2
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx2
18 files changed, 464 insertions, 246 deletions
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index c5910b0be..5ef3de4bc 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -65,7 +65,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
</div>
<div className="collectionCarouselView-caption" key="caption"
style={{
- background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)),
+ background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props.renderDepth, "backgroundColor", this.props.layerProvider)),
color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)),
borderRadius: StrCast(this.layoutDoc._captionBorderRounding),
}}>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index abe8477e4..57b038c73 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -4,7 +4,7 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from "mo
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
import * as GoldenLayout from "../../../client/goldenLayout";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../fields/Doc";
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -172,7 +172,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
instance._goldenLayout.root.addChild(newContentItem);
} else if (instance._goldenLayout.root.contentItems[0].isStack) {
instance._goldenLayout.root.contentItems[0].addChild(docContentConfig);
- } else if (instance._goldenLayout.root.contentItems.length === 1 && instance._goldenLayout.root.contentItems[0].contentItems.length === 1 &&
+ } else if (
+ instance._goldenLayout.root.contentItems.length === 1 &&
+ instance._goldenLayout.root.contentItems[0].contentItems.length === 1 &&
instance._goldenLayout.root.contentItems[0].contentItems[0].contentItems.length === 0) {
instance._goldenLayout.root.contentItems[0].contentItems[0].addChild(docContentConfig);
}
@@ -369,16 +371,18 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
this.props.Document.dockingConfig = json;
- const sublists = DocListCast(this.props.Document[this.props.fieldKey]);
- const tabs = Cast(sublists[0], Doc, null);
- const other = Cast(sublists[1], Doc, null);
- const tabdocs = DocListCast(tabs.data);
- const otherdocs = DocListCast(other.data);
- Doc.GetProto(tabs).data = new List<Doc>(docs);
- const otherSet = new Set<Doc>();
- otherdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
- tabdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
- Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()));
+ setTimeout(async () => {
+ const sublists = DocListCast(this.props.Document[this.props.fieldKey]);
+ const tabs = Cast(sublists[0], Doc, null);
+ const other = Cast(sublists[1], Doc, null);
+ const tabdocs = await DocListCastAsync(tabs.data);
+ const otherdocs = await DocListCastAsync(other.data);
+ Doc.GetProto(tabs).data = new List<Doc>(docs);
+ const otherSet = new Set<Doc>();
+ otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+ tabdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+ Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()));
+ }, 0);
}
tabDestroyed = (tab: any) => {
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index b6ab3f0e0..b427e35c3 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -154,7 +154,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
PanelHeight={nested ? pair.layout[HeightSym] : this.dimension}
renderDepth={this.props.renderDepth + 1}
focus={emptyFunction}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 9f8468253..a1df9d214 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -402,6 +402,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
DataDoc={undefined}
fitToBox={true}
FreezeDimensions={true}
+ dontCenter={"y"}
focus={emptyFunction}
LibraryPath={this.props.LibraryPath}
renderDepth={this.props.renderDepth}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 97eacaeab..4b3393e14 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -192,7 +192,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
return <ContentFittingDocumentView
Document={doc}
DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
LayoutTemplate={this.props.ChildLayoutTemplate}
LayoutTemplateString={this.props.ChildLayoutString}
LibraryPath={this.props.LibraryPath}
@@ -202,7 +202,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
PanelHeight={height}
NativeWidth={this.props.childIgnoreNativeSize ? returnZero : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
NativeHeight={this.props.childIgnoreNativeSize ? returnZero : undefined}
- dontCenter={this.props.childIgnoreNativeSize ? true : false}
+ dontCenter={this.props.childIgnoreNativeSize ? "xy" : ""}
fitToBox={false}
dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.dontRegisterChildViews, this.props.dontRegisterView)}
rootSelected={this.rootSelected}
@@ -292,9 +292,9 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
});
const oldDocs = this.childDocs.length;
if (super.onInternalDrop(e, de)) {
- const newDocs = this.childDocs.slice().filter((d: Doc, ind: number) => ind >= oldDocs);
+ const droppedDocs = this.childDocs.slice().filter((d: Doc, ind: number) => ind >= oldDocs); // if the drop operation adds something to the end of the list, then use that as the new document (may be different than what was dropped e.g., in the case of a button which is dropped but which creates say, a note).
+ const newDocs = droppedDocs.length ? droppedDocs : de.complete.docDragData.droppedDocuments; // if nothing was added to the end of the list, then presumably the dropped documents were already in the list, but possibly got reordered so we use them.
- //de.complete.docDragData.droppedDocuments;
const docs = this.childDocList;
DragManager.docsBeingDragged = [];
if (docs && newDocs.length) {
@@ -410,7 +410,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
this.observer = new _global.ResizeObserver(action((entries: any) => {
if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
- Doc.Layout(doc)._height = Math.max(height * NumCast(doc[this.props.fieldKey + "-height"]), NumCast(doc[this.props.fieldKey + "-height"]));
+ Doc.Layout(doc)._height = Math.max(height, NumCast(doc[this.props.fieldKey + "-height"]));
}
}));
this.observer.observe(ref);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b27f64ff0..10459a497 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -46,6 +46,7 @@ export interface SubCollectionViewProps extends CollectionViewProps {
freezeChildDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
overrideDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
ignoreFields?: string[]; // used in TreeView to ignore specified fields (see LinkBox)
+ parentActive: (outsideReaction: boolean) => boolean;
isAnnotationOverlay?: boolean;
annotationsKey: string;
layoutEngine?: () => string;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index a7c8a7cdc..ad010dba5 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -26,6 +26,7 @@ import { TreeView } from "./TreeView";
import React = require("react");
import { DocumentManager } from '../../util/DocumentManager';
import { FormattedTextBoxComment } from '../nodes/formattedText/FormattedTextBoxComment';
+import { DocumentView } from '../nodes/DocumentView';
export type collectionTreeViewProps = {
treeViewHideTitle?: boolean;
@@ -158,7 +159,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
e.stopPropagation();
e.key === "Enter" && this.makeTextCollection(childDocs);
}}>
- <ContentFittingDocumentView
+ <DocumentView
Document={this.doc}
DataDoc={undefined}
LayoutTemplateString={FormattedTextBox.LayoutString("text")}
@@ -167,7 +168,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
rootSelected={returnTrue}
treeViewDoc={undefined}
//dontRegisterView={true}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
PanelWidth={this.rtfWidth}
PanelHeight={this.rtfOutlineHeight}
focus={this.props.focus}
@@ -203,7 +204,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc);
return TreeView.GetChildElements(this.treeChildren, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
- moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+ moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.styleProvider, this.props.ScreenToLocalTransform,
this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged, this.props.dontRegisterView || Cast(this.props.Document.dontRegisterChildViews, "boolean", null));
@@ -215,7 +216,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
render() {
TraceMobx();
if (!(this.doc instanceof Doc)) return (null);
- const background = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth);
+ const background = this.props.styleProvider?.(this.doc, this.props.renderDepth, "backgroundColor", this.props.layerProvider);
const paddingX = `${NumCast(this.doc._xPadding, 10)}px`;
const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`;
const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a27fa5a66..e2c6bcaf6 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -38,6 +38,7 @@ import { SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from "./CollectionTreeView";
import './CollectionView.scss';
+import { listSpec } from '../../../fields/Schema';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -75,6 +76,7 @@ export interface CollectionRenderProps {
removeDocument: (document: Doc | Doc[]) => boolean;
moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
active: () => boolean;
+ parentActive: (outsideReaction: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
PanelWidth: () => number;
PanelHeight: () => number;
@@ -116,7 +118,13 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
return viewField as any as CollectionViewType;
}
- active = (outsideReaction?: boolean) => (this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.props.Document.forceActive || this._isChildActive || this.props.renderDepth === 0) ? true : false;
+ active = (outsideReaction?: boolean) => (this.props.isSelected(outsideReaction) ||
+ this.props.rootSelected(outsideReaction) ||
+ this.props.Document.forceActive ||
+ this._isChildActive ||
+ this.props.renderDepth === 0) ?
+ true :
+ false
whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
@@ -152,6 +160,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
if (effectiveAcl === AclAddonly) {
added.map(doc => {
+ this.props.layerProvider?.(doc, true);
Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
doc.context = this.props.Document;
});
@@ -176,6 +185,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
doc._stayInCollection = undefined;
doc.context = this.props.Document;
});
+ added.map(doc => this.props.layerProvider?.(doc, true));
(targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added);
targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
}
@@ -382,15 +392,16 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
moveDocument: this.moveDocument,
active: this.active,
whenActiveChanged: this.whenActiveChanged,
+ parentActive: this.props.parentActive,
PanelWidth: this.bodyPanelWidth,
PanelHeight: this.props.PanelHeight,
ChildLayoutTemplate: this.childLayoutTemplate,
ChildLayoutString: this.childLayoutString,
};
- const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.props.Document._isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
- `${CurrentUserUtils.ActiveDashboard?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+ const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.collectionViewType === CollectionViewType.Linear ? undefined :
+ this.props.styleProvider?.(this.props.Document, this.props.renderDepth, "boxShadow", this.props.layerProvider);
return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
- style={{ pointerEvents: this.props.Document._isBackground ? "none" : undefined, boxShadow }}>
+ style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined, boxShadow }}>
{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 =>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 530595cd0..82b6d8605 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -3,14 +3,14 @@ import { Tooltip } from '@material-ui/core';
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
import { clamp } from 'lodash';
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
-import { DataSym, Doc, DocListCast, Opt, DocListCastAsync } from "../../../fields/Doc";
+import { DataSym, Doc, DocListCast, Opt, DocListCastAsync, StrListCast, WidthSym, HeightSym } from "../../../fields/Doc";
import { Id } from '../../../fields/FieldSymbols';
import { FieldId } from "../../../fields/RefField";
import { listSpec } from '../../../fields/Schema';
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types";
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
@@ -29,6 +29,10 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
import { CollectionViewType } from './CollectionView';
import "./TabDocView.scss";
import React = require("react");
+import { List } from '../../../fields/List';
+import { DocumentType } from '../../documents/DocumentTypes';
+import Color = require('color');
+import { InkTool } from '../../../fields/InkField';
const _global = (window /* browser */ || global /* node */) as any;
interface TabDocViewProps {
@@ -39,6 +43,7 @@ interface TabDocViewProps {
export class TabDocView extends React.Component<TabDocViewProps> {
_mainCont: HTMLDivElement | null = null;
_tabReaction: IReactionDisposer | undefined;
+ @observable _activated: boolean = false;
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@@ -52,6 +57,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
@action
init = (tab: any, doc: Opt<Doc>) => {
+ if (tab.contentItem === tab.header.parent.getActiveContentItem()) this._activated = true;
if (tab.DashDoc !== doc && doc && tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
tab._disposers = {} as { [name: string]: IReactionDisposer };
tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
@@ -66,6 +72,27 @@ export class TabDocView extends React.Component<TabDocViewProps> {
titleEle.size = e.currentTarget.value.length + 3;
Doc.GetProto(doc).title = e.currentTarget.value;
}));
+ if (tab.element[0].children[1].children.length === 1) {
+ const toggle = document.createElement("div");
+ toggle.style.width = "10px";
+ toggle.style.height = "calc(100% - 2px)";
+ toggle.style.left = "-2px";
+ toggle.style.bottom = "1px";
+ toggle.style.borderTopRightRadius = "7px";
+ toggle.style.position = "relative";
+ toggle.style.display = "inline-block";
+ toggle.style.background = "gray";
+ toggle.style.borderLeft = "solid 1px black";
+ toggle.onclick = (e: MouseEvent) => {
+ if (tab.contentItem === tab.header.parent.getActiveContentItem()) {
+ tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : "background";
+ }
+ };
+ tab.element[0].style.borderTopRightRadius = "8px";
+ tab.element[0].children[1].appendChild(toggle);
+ tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
+ ({ layer, color }) => toggle.style.background = !layer ? color : "dimgrey", { fireImmediately: true });
+ }
// shifts the focus to this tab when another tab is dragged over it
tab.element[0].onmouseenter = (e: MouseEvent) => {
if (SnappingManager.GetIsDragging() && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
@@ -87,8 +114,14 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
};
tab._disposers.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments().some(v => (v.topMost || v.props.treeViewDoc) && v.props.Document === doc),
- (selected) => selected && tab.contentItem !== tab.header.parent.getActiveContentItem() &&
- UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch"));
+ action((selected) => {
+ if (selected) this._activated = true;
+ const toggle = tab.element[0].children[1].children[0] as HTMLInputElement;
+ selected && tab.contentItem !== tab.header.parent.getActiveContentItem() &&
+ UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch");
+ toggle.style.fontWeight = selected ? "bold" : "";
+ toggle.style.textTransform = selected ? "uppercase" : "";
+ }));
//attach the selection doc buttons menu to the drag handle
const stack = tab.contentItem.parent;
@@ -148,14 +181,16 @@ export class TabDocView extends React.Component<TabDocViewProps> {
const sublists = DocListCast(dview[fieldKey]);
const tabs = Cast(sublists[0], Doc, null);
const tabdocs = await DocListCastAsync(tabs.data);
- if (!tabdocs?.includes(curPres)) {
- tabdocs?.push(curPres); // bcz: Argh! this is annoying. if multiple documents are pinned, this will get called multiple times before the presentation view is drawn. Thus it won't be in the tabdocs list and it will get created multple times. so need to explicilty add the presbox to the list of open tabs
- CollectionDockingView.AddSplit(curPres, "right");
- }
- PresBox.Instance?._selectedArray.clear();
- pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
- DocumentManager.Instance.jumpToDocument(doc, false, undefined);
- batch.end();
+ runInAction(() => {
+ if (!tabdocs?.includes(curPres)) {
+ tabdocs?.push(curPres); // bcz: Argh! this is annoying. if multiple documents are pinned, this will get called multiple times before the presentation view is drawn. Thus it won't be in the tabdocs list and it will get created multple times. so need to explicilty add the presbox to the list of open tabs
+ CollectionDockingView.AddSplit(curPres, "right");
+ }
+ PresBox.Instance?._selectedArray.clear();
+ pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
+ DocumentManager.Instance.jumpToDocument(doc, false, undefined);
+ batch.end();
+ });
}
}
@@ -182,8 +217,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
})).observe(this.props.glContainer._element[0]);
this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged();
- this._tabReaction = reaction(() => ({ selected: selected(), color: this.tabColor, title: this.tab?.titleElement[0] }),
- ({ selected, color, title }) => title && (title.style.backgroundColor = selected ? color : ""),
+ this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }),
+ ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""),
{ fireImmediately: true });
}
@@ -242,6 +277,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
@computed get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.ContentScaling()) / 2 : 0; }
@computed get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.ContentScaling()) / this._panelWidth * 100}% ` : undefined; }
@computed get layoutDoc() { return this._document && Doc.Layout(this._document); }
+ @computed static get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
// adds a tab to the layout based on the locaiton parameter which can be:
// close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab,
@@ -265,7 +301,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
}
- @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document, 0))); }
+ @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, TabDocView.styleProvider(this._document, 0, "backgroundColor"))); }
@computed get renderBounds() {
const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
const xbounds = bounds[2] - bounds[0];
@@ -301,6 +337,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
CollectionView={undefined}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
+ parentActive={returnFalse}
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}
@@ -322,7 +359,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
renderDepth={0}
whenActiveChanged={emptyFunction}
focus={emptyFunction}
- backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
+ styleProvider={TabDocView.miniStyleProvider}
addDocTab={this.addDocTab}
pinToPres={TabDocView.PinDoc}
docFilters={CollectionDockingView.Instance.docFilters}
@@ -337,7 +374,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
<Tooltip title={<div className="dash-tooltip">{"toggle minimap"}</div>}>
<div className="miniMap-hidden" onPointerDown={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })}
- style={{ background: CollectionDockingView.Instance.props.backgroundColor?.(this._document, 0) }} >
+ style={{ background: TabDocView.styleProvider(this._document, 0, "backgroundColor") }} >
<FontAwesomeIcon icon={"globe-asia"} size="lg" />
</div>
</Tooltip>
@@ -351,9 +388,123 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
setView = action((view: DocumentView) => this._view = view);
active = () => this._isActive;
+
+ //
+ // a preliminary semantic-"layering/grouping" mechanism for determining interactive properties of documents
+ // currently, the provider tests whether the docuemnt's layer field matches the activeLayer field of the tab.
+ // if it matches, then the document gets pointer events, otherwise it does not.
+ //
+ layerProvider = (doc: Doc, assign?: boolean) => {
+ if (doc.z) return true;
+ if (assign) {
+ const activeLayer = StrCast(this._document?.activeLayer);
+ if (activeLayer) {
+ const layers = Cast(doc.layers, listSpec("string"), []);
+ if (layers.length && !layers.includes(activeLayer)) layers.push(activeLayer);
+ else if (!layers.length) doc.layers = new List<string>([activeLayer]);
+ if (activeLayer === "red" || activeLayer === "green" || activeLayer === "blue") doc._backgroundColor = activeLayer;
+ }
+ return true;
+ } else {
+ if (Doc.AreProtosEqual(doc, this._document)) return true;
+ const layers = Cast(doc.layers, listSpec("string"), []);
+ if (!layers.length && !this._document?.activeLayer) return true;
+ if (layers.includes(StrCast(this._document?.activeLayer))) return true;
+ return false;
+ }
+ }
+
+ @undoBatch
+ @action
+ static toggleBackground = (doc: Doc) => {
+ const layers = StrListCast(doc.layers);
+ if (!layers.includes("background")) {
+ if (!layers.length) doc.layers = new List<string>(["background"]);
+ else layers.push("background");
+ }
+ else layers.splice(layers.indexOf("background"), 1);
+ doc._overflow = !layers.includes("background") ? "visible" : undefined;
+ if (!layers.includes("background")) {
+ //this.props.bringToFront(doc, true);
+ // const wid = this.Document[WidthSym](); // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
+ // const hgt = this.Document[HeightSym]();
+ // Doc.SetNativeWidth(this.props.Document[DataSym], wid);
+ // Doc.SetNativeHeight(this.props.Document[DataSym], hgt);
+ }
+ }
+ //
+ // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
+ //
+ public static styleProvider = (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean): any => {
+ switch (property) {
+ case "backgroundColor": {
+ if (Doc.UserDoc().renderStyle === "comic") return undefined;
+ let docColor = StrCast(doc?._backgroundColor, StrCast(doc?.backgroundColor));
+ if (!docColor) {
+ switch (doc?.type) {
+ case DocumentType.PRESELEMENT: docColor = TabDocView.darkScheme ? "dimgrey" : ""; break;
+ case DocumentType.PRES: docColor = TabDocView.darkScheme ? "#3e3e3e" : "black"; break;
+ case DocumentType.FONTICON: docColor = "black"; break;
+ case DocumentType.RTF: docColor = TabDocView.darkScheme ? "#2d2d2d" : "#f1efeb";
+ case DocumentType.LABEL:
+ case DocumentType.BUTTON: docColor = TabDocView.darkScheme ? "#2d2d2d" : "lightgray"; break;
+ case DocumentType.LINK:
+ case DocumentType.COL:
+ docColor = Doc.IsSystem(doc) ? (TabDocView.darkScheme ? "rgb(62,62,62)" : "lightgrey") :
+ StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
+ break;
+ //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
+ default: docColor = TabDocView.darkScheme ? "black" : "white"; break;
+ }
+ }
+ if (docColor && (!doc || layerProvider?.(doc) === false)) docColor = Color(docColor).fade(0.5).toString();
+ return docColor;
+ }
+ case "hidden": return (BoolCast(doc?.hidden) /* || layerProvider?.(doc) === false*/);
+ case "boxShadow": {
+ switch (doc?.type) {
+ case DocumentType.COL: return StrListCast(doc.layers).includes("background") ? undefined :
+ `${TabDocView.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+ default: return undefined;
+ }
+ }
+ case "docContents": return undefined;
+ default:
+ if (property.startsWith("pointerEvents")) {
+ const layer = doc && layerProvider?.(doc);
+ if (doc?.Opacity === 0 || doc?.type === DocumentType.INK || doc?.isInkMask) return "none";
+ if (layer === false && !property.includes(":selected") && !SnappingManager.GetIsDragging()) return "none";
+ if (doc?.type !== DocumentType.INK && layer === true) return "all";
+ return undefined;
+ }
+ if (property.startsWith("decorations")) {
+ const isBackground = StrListCast(doc?.layers).includes("background");
+ return doc && (isBackground || property.includes(":selected")) && renderDepth > 0 &&
+ ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
+ <div className="documentView-lock" onClick={() => TabDocView.toggleBackground(doc)}>
+ <FontAwesomeIcon icon={isBackground ? "unlock" : "lock"} style={{ color: isBackground ? "red" : undefined }} size="lg" />
+ </div>
+ : (null);
+ }
+ }
+ }
+ public static miniStyleProvider = (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean): any => {
+ if (doc) {
+ switch (property) {
+ case "docContents":
+ if (doc.type === DocumentType.COL) return null;
+ const background = doc.type === DocumentType.PDF ? "red" : doc.type === DocumentType.IMG ? "blue" : doc.type === DocumentType.RTF ? "orange" :
+ doc.type === DocumentType.VID ? "purple" : doc.type === DocumentType.WEB ? "yellow" : "gray";
+ return <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />;
+ default:
+ if (property.startsWith("pointerEvents")) return "none";
+ return TabDocView.styleProvider(doc, renderDepth, property, layerProvider);
+ }
+ }
+ }
@computed get docView() {
TraceMobx();
- return !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
+ return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
<><DocumentView key={this._document[Id]}
LibraryPath={emptyPath}
Document={this._document}
@@ -361,6 +512,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
bringToFront={emptyFunction}
rootSelected={returnTrue}
+ layerProvider={this.layerProvider}
addDocument={undefined}
removeDocument={undefined}
ContentScaling={this.ContentScaling}
@@ -373,7 +525,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
parentActive={this.active}
whenActiveChanged={emptyFunction}
focus={this.focusFunc}
- backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
+ styleProvider={TabDocView.styleProvider}
addDocTab={this.addDocTab}
pinToPres={TabDocView.PinDoc}
docFilters={CollectionDockingView.Instance.docFilters}
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 17c6b0750..580fec9d6 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -22,7 +22,7 @@
}
.treeView-container-active {
z-index: 100;
- position: relative;;
+ position: relative;
.formattedTextbox-sidebar {
background-color: #ffff001f !important;
height: 500px !important;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 925eb4be6..cb521ea75 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace } from "mobx";
import { observer } from "mobx-react";
import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
@@ -49,7 +49,7 @@ export interface TreeViewProps {
outdentDocument?: () => void;
ScreenToLocalTransform: () => Transform;
dontRegisterView?: boolean;
- backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
+ backgroundColor?: (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined;
outerXf: () => { translateX: number, translateY: number };
treeView: CollectionTreeView;
parentKey: string;
@@ -78,12 +78,12 @@ export class TreeView extends React.Component<TreeViewProps> {
private _openScript: (() => ScriptField) | undefined;
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
- private _dref = React.createRef<HTMLDivElement>();
private _tref = React.createRef<HTMLDivElement>();
private _docRef = React.createRef<DocumentView>();
private _uniqueId = Utils.GenerateGuid();
private _editMaxWidth: number | string = 0;
+ @observable _dref: ContentFittingDocumentView | undefined | null;
@computed get doc() { TraceMobx(); return this.props.document; }
get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive
@@ -111,9 +111,8 @@ export class TreeView extends React.Component<TreeViewProps> {
@computed get childDocs() { TraceMobx(); return this.childDocList(this.fieldKey); }
@computed get childLinks() { return this.childDocList("links"); }
@computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
- @computed get boundsOfCollectionDocument() {
- return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined :
- Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
+ @computed get isCollectionDoc() {
+ return !StrCast(this.props.document.type).includes(DocumentType.COL) || !DocListCast(this.props.document[this.fieldKey]).length ? false : true;
}
@undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right");
@@ -185,8 +184,12 @@ export class TreeView extends React.Component<TreeViewProps> {
}
public static makeTextBullet() {
- const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", "sidebarColor": "transparent", "sidebarViewType": CollectionViewType.Freeform, forceActive: true, _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 });
- Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
+ const bullet = Docs.Create.TextDocument("-text-", {
+ layout: CollectionView.LayoutString("data"),
+ title: "-title-", "sidebarColor": "transparent", "sidebarViewType": CollectionViewType.Freeform,
+ _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true,
+ x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10
+ });
Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
Doc.GetProto(bullet).data = new List<Doc>([]);
Doc.SetInPlace(bullet, "editTitle", "*", false);
@@ -269,31 +272,35 @@ export class TreeView extends React.Component<TreeViewProps> {
return false;
}
- refTransform = (ref: HTMLDivElement) => {
+ refTransform = (ref: HTMLDivElement | undefined | null) => {
+ if (!ref) return this.props.ScreenToLocalTransform();
const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
const outerXf = this.props.outerXf();
const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
}
- docTransform = () => this.refTransform(this._dref.current!);
- getTransform = () => this.refTransform(this._tref.current!);
+ docTransform = () => this.refTransform(this._dref?.ContentRef?.current);
+ getTransform = () => this.refTransform(this._tref.current);
docWidth = () => {
const layoutDoc = this.layoutDoc;
const aspect = Doc.NativeAspect(layoutDoc);
+ if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - 20, layoutDoc[WidthSym]());
if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - 20));
- return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
+ return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : Math.min(this.layoutDoc[WidthSym](), this.props.panelWidth() - 20);
}
docHeight = () => {
const layoutDoc = this.layoutDoc;
- const bounds = this.boundsOfCollectionDocument;
return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
const aspect = Doc.NativeAspect(layoutDoc);
if (aspect) return this.docWidth() / (aspect || 1);
- if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
- return layoutDoc._fitWidth ? (!Doc.NativeHeight(this.doc) ? NumCast(this.props.containingCollection._height) :
- Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) /
- (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containingCollection._height))
- )) : (layoutDoc[HeightSym]() || 50);
+ return layoutDoc._fitWidth ?
+ (!Doc.NativeHeight(this.doc) ?
+ NumCast(this.props.containingCollection._height)
+ :
+ Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containingCollection._height))
+ ))
+ :
+ (layoutDoc[HeightSym]() || 50);
})()));
}
@@ -350,6 +357,18 @@ export class TreeView extends React.Component<TreeViewProps> {
rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20);
rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20);
+ expandPanelHeight = () => {
+ if (this.layoutDoc._fitWidth) return this.docHeight();
+ const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
+ const docAspect = this.docWidth() / this.docHeight();
+ return (docAspect < aspect) ? this.docWidth() / aspect : this.docHeight();
+ }
+ expandPanelWidth = () => {
+ if (this.layoutDoc._fitWidth) return this.docWidth();
+ const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
+ const docAspect = this.docWidth() / this.docHeight();
+ return (docAspect > aspect) ? this.docHeight() * aspect : this.docWidth();
+ }
@computed get renderContent() {
TraceMobx();
@@ -376,47 +395,11 @@ export class TreeView extends React.Component<TreeViewProps> {
[...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
- return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
+ return <ul key={this.doc[Id] + this.doc.title}><div style={{ display: "inline-block" }} >
{this.expandedField}
</div></ul>;
} else {
- const layoutDoc = this.layoutDoc;
- const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
- const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
- return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.doc[Id]}>
- <ContentFittingDocumentView
- Document={this.doc}
- DataDoc={undefined}
- LibraryPath={emptyPath}
- dontRegisterView={this.props.dontRegisterView}
- renderDepth={this.props.renderDepth + 1}
- rootSelected={returnTrue}
- treeViewDoc={undefined}
- backgroundColor={this.props.backgroundColor}
- fitToBox={this.boundsOfCollectionDocument !== undefined}
- FreezeDimensions={true}
- NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
- NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
- PanelWidth={panelWidth}
- PanelHeight={panelHeight}
- focus={returnFalse}
- ScreenToLocalTransform={this.docTransform}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={this.props.containingCollection}
- ContainingCollectionView={undefined}
- addDocument={returnFalse}
- moveDocument={this.move}
- removeDocument={this.props.removeDoc}
- parentActive={this.props.active}
- whenActiveChanged={this.props.whenActiveChanged}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- bringToFront={returnFalse}
- ContentScaling={returnOne}
- />
- </div>;
+ return this.renderDocument(false);
}
}
@@ -440,7 +423,8 @@ export class TreeView extends React.Component<TreeViewProps> {
@computed get renderBullet() {
TraceMobx();
const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
- return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
+ return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} key={"bullet"}
+ title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
onClick={this.bulletClick}
style={this.outlineMode ? { opacity: NumCast(this.doc.opacity, 1) } : {
color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
@@ -481,6 +465,27 @@ export class TreeView extends React.Component<TreeViewProps> {
truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0);
onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
+
+ refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
+ ignoreEvent = (e: any) => {
+ if (this.props.active(true)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+ onKeyDown = (e: React.KeyboardEvent) => {
+ if (this.doc.treeViewHideHeader || this.outlineMode) {
+ e.stopPropagation();
+ e.preventDefault();
+ switch (e.key) {
+ case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+ return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
+ case "Backspace": return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
+ case "Enter": return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
+ }
+ }
+ }
+
/**
* Renders the EditableView title element for placement into the tree.
*/
@@ -488,7 +493,7 @@ export class TreeView extends React.Component<TreeViewProps> {
get renderTitle() {
TraceMobx();
const view = this.showTitleEditorControl ? this.editableView("title") :
- <DocumentView
+ <DocumentView key="title"
ref={this._docRef}
Document={this.doc}
DataDoc={undefined}
@@ -535,12 +540,81 @@ export class TreeView extends React.Component<TreeViewProps> {
</>;
}
- refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
+ renderBulletHeader = (contents: JSX.Element) => {
+ return <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} key="titleheader"
+ ref={this._header}
+ style={{ maxWidth: this._editMaxWidth }}
+ onClick={this.ignoreEvent}
+ onPointerDown={this.ignoreEvent}
+ onPointerEnter={this.onPointerEnter}
+ onPointerLeave={this.onPointerLeave}>
+ {contents}
+ </div>;
+ }
+
+ // renders the text version of a document as the header (e.g., useful for Slide views where the "")
+ @computed get renderTitleAsHeader() {
+ return <>
+ {this.renderBullet}
+ {this.renderTitle}
+ </>;
+ }
+
+ renderDocument = (asText: boolean) => {
+ const panelWidth = asText || StrCast(Doc.LayoutField(this.layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.expandPanelWidth;
+ const panelHeight = asText ? this.rtfOutlineHeight : StrCast(Doc.LayoutField(this.layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.expandPanelHeight;
+ return <ContentFittingDocumentView key={this.doc[Id]} ref={action((r: ContentFittingDocumentView | null) => this._dref = r)}
+ Document={this.doc}
+ DataDoc={undefined}
+ PanelWidth={panelWidth}
+ PanelHeight={panelHeight}
+ NativeWidth={!asText && this.layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
+ NativeHeight={!asText && this.layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
+ fitToBox={!asText && this.isCollectionDoc !== undefined}
+ LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined}
+ focus={asText ? this.refocus : returnFalse}
+ dontRegisterView={asText ? undefined : this.props.dontRegisterView}
+ ScreenToLocalTransform={this.docTransform}
+ LibraryPath={emptyPath}
+ renderDepth={this.props.renderDepth + 1}
+ rootSelected={returnTrue}
+ treeViewDoc={undefined}
+ styleProvider={this.props.backgroundColor}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.containingCollection}
+ ContainingCollectionView={undefined}
+ addDocument={this.props.addDocument}
+ moveDocument={this.move}
+ removeDocument={this.props.removeDoc}
+ parentActive={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ bringToFront={returnFalse}
+ ContentScaling={returnOne}
+ />;
+ }
+
+ @computed get renderDocumentAsHeader() {
+ return <>
+ {this.renderBullet}
+ {this.renderDocument(true)}
+ </>;
+ }
+
+ @computed get renderBorder() {
+ const sorting = this.doc[`${this.fieldKey}-sortAscending`];
+ return <div className={`treeView-border${this.outlineMode ? "outline" : ""}`}
+ style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+ {!this.treeViewOpen ? (null) : this.renderContent}
+ </div>;
+ }
render() {
TraceMobx();
- if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null;
- const sorting = this.doc[`${this.fieldKey}-sortAscending`];
+ if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return "<" + this.doc.title + ">";
if (this.showTitleEditorControl) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
let par: any = this._header?.current;
while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
@@ -551,90 +625,28 @@ export class TreeView extends React.Component<TreeViewProps> {
}
}
else this._editMaxWidth = "";
- const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc));
- return this.doc.treeViewHideHeader || this.outlineMode ?
- !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ?
- this.renderContent
- : <div className={`treeView-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
- onKeyDown={e => {
- e.stopPropagation();
- e.preventDefault();
- switch (e.key) {
- case "Backspace": return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
- case "Enter": return UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet");
- case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
- return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
- }
- }} >
- <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ alignItems: this.outlineMode ? "center" : undefined, maxWidth: this._editMaxWidth }}
- onClick={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
- onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
- {this.renderBullet}
- <div ref={this._dref} style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}>
- <ContentFittingDocumentView
- Document={this.doc}
- DataDoc={undefined}
- LayoutTemplateString={FormattedTextBox.LayoutString("text")}
- LibraryPath={emptyPath}
- renderDepth={this.props.renderDepth + 1}
- rootSelected={returnTrue}
- treeViewDoc={undefined}
- backgroundColor={this.props.backgroundColor}
- fitToBox={this.boundsOfCollectionDocument !== undefined}
- PanelWidth={this.rtfWidth}
- PanelHeight={this.rtfOutlineHeight}
- focus={this.refocus}
- ScreenToLocalTransform={this.docTransform}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={this.props.containingCollection}
- ContainingCollectionView={undefined}
- addDocument={this.props.addDocument}
- moveDocument={this.move}
- removeDocument={this.props.removeDoc}
- parentActive={this.props.active}
- whenActiveChanged={this.props.whenActiveChanged}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- bringToFront={returnFalse}
- ContentScaling={returnOne}
- />
- </div>
- </div>
-
- <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
- {!this.treeViewOpen ? (null) : this.renderContent}
- </div>
- </div> :
- <div className="treeView-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
- <li className="collection-child">
- <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
- if (this.props.active(true)) {
- e.stopPropagation();
- e.preventDefault();
- SelectionManager.DeselectAll();
- }
- }}
- onPointerDown={e => {
- if (this.props.active(true)) {
- e.stopPropagation();
- e.preventDefault();
- }
- }}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
- {this.renderBullet}
- {this.renderTitle}
- </div>
- <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
- {!this.treeViewOpen ? (null) : this.renderContent}
- </div>
- </li>
+
+ const hideTitle = this.doc.treeViewHideHeader || this.outlineMode;
+ return hideTitle && !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ?
+ this.renderContent
+ :
+ <div className={`treeView-container${this._dref?.docView?.contentsActive() ? "-active" : ""}`}
+ ref={this.createTreeDropTarget}
+ onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
+ onKeyDown={this.onKeyDown}>
+ {hideTitle ?
+ <li className="collection-child">
+ {this.renderBulletHeader(this.renderDocumentAsHeader)}
+ {this.renderBorder}
+ </li> :
+ <li className="collection-child">
+ {this.renderBulletHeader(this.renderTitleAsHeader)}
+ {this.renderBorder}
+ </li>
+ }
</div>;
}
-
public static GetChildElements(
childDocs: Doc[],
treeView: CollectionTreeView,
@@ -649,7 +661,7 @@ export class TreeView extends React.Component<TreeViewProps> {
dropAction: dropActionType,
addDocTab: (doc: Doc, where: string) => boolean,
pinToPres: (document: Doc) => void,
- backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number) => string | undefined),
+ backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined),
screenToLocalXf: () => Transform,
outerXf: () => { translateX: number, translateY: number },
active: (outsideReaction?: boolean) => boolean,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 4cf257640..c81bd068c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -40,6 +40,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
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);
@@ -60,6 +61,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
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]));
+ 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;
@@ -101,8 +103,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const top = rect.top, height = rect.height;
var el = el.parentNode;
while (el && el !== document.body) {
- if (el.hasOwnProperty("getBoundingClientRect")) {
- rect = el.getBoundingClientRect();
+ rect = el?.getBoundingClientRect();
+ if (rect?.width) {
if (top <= rect.bottom === false && getComputedStyle(el).overflow === "hidden") return rect.bottom;
// Check if the element is out of view due to a container scrolling
if ((top + height) <= rect.top && getComputedStyle(el).overflow === "hidden") return rect.top;
@@ -117,8 +119,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const left = rect.left, width = rect.width;
var el = el.parentNode;
while (el && el !== document.body) {
- if (el.hasOwnProperty("getBoundingClientRect")) {
- rect = el.getBoundingClientRect();
+ rect = el?.getBoundingClientRect();
+ if (rect?.width) {
if (left <= rect.right === false && getComputedStyle(el).overflow === "hidden") return rect.right;
// Check if the element is out of view due to a container scrolling
if ((left + width) <= rect.left && getComputedStyle(el).overflow === "hidden") return rect.left;
@@ -138,11 +140,14 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const adiv = (acont.length ? acont[0] : A.ContentDiv);
const bdiv = (bcont.length ? bcont[0] : B.ContentDiv);
for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
- for (let apdiv = bdiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
+ for (let bpdiv = bdiv; bpdiv; bpdiv = bpdiv.parentElement as any) if ((bpdiv as any).hidden) return;
const a = adiv.getBoundingClientRect();
const b = bdiv.getBoundingClientRect();
const atop = this.visibleY(adiv);
const btop = this.visibleY(bdiv);
+ if (!a.width || !b.width) return undefined;
+ const atop2 = this.visibleY(adiv);
+ const btop2 = this.visibleY(bdiv);
const aleft = this.visibleX(adiv);
const bleft = this.visibleX(bdiv);
const clipped = aleft !== a.left || atop !== a.top || bleft !== b.left || btop !== b.top;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index a50b41198..a05c25c9b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -17,6 +17,7 @@
position: absolute;
top: 0;
left: 0;
+ pointer-events: none;
}
.collectionfreeformview-viewdef {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1033050b9..0ba13192d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -47,6 +47,7 @@ import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
+import { isUndefined } from "lodash";
export const panZoomSchema = createSchema({
_panX: "number",
@@ -73,6 +74,7 @@ export type collectionFreeformViewProps = {
forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox)
viewDefDivClick?: ScriptField;
childPointerEvents?: boolean;
+ parentActive: (outsideReaction: boolean) => boolean;
scaleField?: string;
noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
};
@@ -87,7 +89,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
private _inkToTextStartY: number | undefined;
private _wordPalette: Map<string, string> = new Map<string, string>();
private _clusterDistance: number = 75;
- private _hitCluster = false;
+ private _hitCluster: number = -1;
private _layoutComputeReaction: IReactionDisposer | undefined;
private _boundsReaction: IReactionDisposer | undefined;
private _layoutPoolData = new ObservableMap<string, PoolData>();
@@ -225,6 +227,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
if (this.Document._currentFrame !== undefined) {
+ CollectionFreeFormDocumentView.setupKeyframes([d], this.Document._currentFrame, false);
const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.h, vals.w, this.Document.editScrollProgressivize ? vals.scroll : undefined, vals.opacity);
} else {
@@ -234,7 +237,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
layoutDoc._width = NumCast(layoutDoc._width, 300);
layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
- !d._isBackground && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+ !Cast(d, listSpec("string"), []).includes("background") && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
}
(docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -273,7 +276,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
- // if (this.props.Document._isBackground) return false;
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);
@@ -287,21 +289,23 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
pickCluster(probe: number[]) {
return this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => {
- const layoutDoc = Doc.Layout(cd);
- const cx = NumCast(cd.x) - this._clusterDistance;
- const cy = NumCast(cd.y) - this._clusterDistance;
- const cw = NumCast(layoutDoc._width) + 2 * this._clusterDistance;
- const ch = NumCast(layoutDoc._height) + 2 * this._clusterDistance;
- return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ?
- NumCast(cd.cluster) : cluster;
+ const grouping = this.props.Document._useClusters ? NumCast(cd.cluster, -1) : NumCast(cd.group, -1);
+ if (grouping !== -1) {
+ const layoutDoc = Doc.Layout(cd);
+ const cx = NumCast(cd.x) - this._clusterDistance;
+ const cy = NumCast(cd.y) - this._clusterDistance;
+ const cw = NumCast(layoutDoc._width) + 2 * this._clusterDistance;
+ const ch = NumCast(layoutDoc._height) + 2 * this._clusterDistance;
+ return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
+ }
+ return cluster;
}, -1);
}
- tryDragCluster(e: PointerEvent | TouchEvent) {
- const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
- if (ptsParent) {
- const cluster = this.pickCluster(this.getTransform().transformPoint(ptsParent.clientX, ptsParent.clientY));
- if (cluster !== -1) {
- const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
+ tryDragCluster(e: PointerEvent | TouchEvent, cluster: number) {
+ if (cluster !== -1) {
+ const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
+ if (ptsParent) {
+ const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === cluster);
const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
const de = new DragManager.DocumentDragData(eles);
de.moveDocument = this.props.moveDocument;
@@ -389,8 +393,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
}
- getClusterColor = (doc: Opt<Doc>) => {
- let clusterColor = this.props.backgroundColor?.(doc, this.props.renderDepth + 1);
+ getClusterColor = (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => {
+ let clusterColor = this.props.styleProvider?.(doc, this.props.renderDepth + 1, property, layerProvider);
+ if (property !== "backgroundColor") return clusterColor;
const cluster = NumCast(doc?.cluster);
if (this.Document._useClusters) {
if (this._clusterSets.length <= cluster) {
@@ -401,10 +406,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
clusterColor = colors[cluster % colors.length];
const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
// override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
- set && set.filter(s => !s._isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
- set && set.filter(s => s._isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
+ set && set.filter(s => !Cast(s.layers, listSpec("string"), []).includes("background")).map(s => clusterColor = StrCast(s.backgroundColor));
+ set && set.filter(s => Cast(s.layers, listSpec("string"), []).includes("background")).map(s => clusterColor = StrCast(s.backgroundColor));
}
- }
+ } else if (doc && NumCast(doc.group, -1) !== -1) clusterColor = "gray";
return clusterColor;
}
@@ -414,8 +419,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) {
return;
}
- this._hitCluster = this.props.Document._useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false;
- if (e.button === 0 && (!e.shiftKey || this._hitCluster) && !e.altKey && !e.ctrlKey && this.props.active(true)) {
+ this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
+ if (e.button === 0 && (!e.shiftKey || this._hitCluster !== -1) && !e.altKey && !e.ctrlKey && this.props.active(true)) {
// if (!this.props.Document.aliasOf && !this.props.ContainingCollectionView) {
// this.props.addDocTab(this.props.Document, "replace");
@@ -446,7 +451,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
// const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
const pt = me.changedTouches[0];
if (pt) {
- this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false;
+ this._hitCluster = this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY));
if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) {
this.removeMoveListeners();
this.addMoveListeners();
@@ -640,7 +645,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
if (!e.cancelBubble) {
if (Doc.GetSelectedTool() === InkTool.None) {
- if (this._hitCluster && this.tryDragCluster(e)) {
+ if (this.tryDragCluster(e, this._hitCluster)) {
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
document.removeEventListener("pointermove", this.onPointerMove);
@@ -660,7 +665,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const pt = myTouches[0];
if (pt) {
if (Doc.GetSelectedTool() === InkTool.None) {
- if (this._hitCluster && this.tryDragCluster(e)) {
+ if (this.tryDragCluster(e, this._hitCluster)) {
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
document.removeEventListener("pointermove", this.onPointerMove);
@@ -860,7 +865,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
bringToFront = action((doc: Doc, sendToBack?: boolean) => {
- if (sendToBack || doc._isBackground) {
+ if (sendToBack || Cast(doc.layers, listSpec("string"), []).includes("background")) {
doc.zIndex = 0;
} else if (doc.isInkMask) {
doc.zIndex = 5000;
@@ -975,11 +980,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
@computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
- @computed get backgroundActive() { return this.layoutDoc._isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
+ @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.active() || this.props.active()); }
onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
- backgroundHalo = () => BoolCast(this.Document._useClusters);
- parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.backgroundActive || this.layoutDoc._viewType === CollectionViewType.Pile ? true : false;
+ backgroundHalo = computedFn(function (doc: Doc) { return BoolCast(this.Document._useClusters) || (NumCast(doc.group, -1) !== -1); }).bind(this);
+ parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.props.parentActive?.(outsideReaction) || this.backgroundActive || this.layoutDoc._viewType === CollectionViewType.Pile ? true : false;
getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
return {
addDocument: this.props.addDocument,
@@ -987,6 +992,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
moveDocument: this.props.moveDocument,
pinToPres: this.props.pinToPres,
whenActiveChanged: this.props.whenActiveChanged,
+ parentActive: this.parentActive,
fitToBox: false,
DataDoc: childData,
Document: childLayout,
@@ -1011,9 +1017,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
docRangeFilters: this.docRangeFilters,
searchFilterDocs: this.searchFilterDocs,
focus: this.focusDocument,
- backgroundColor: this.getClusterColor,
+ styleProvider: this.getClusterColor,
backgroundHalo: this.backgroundHalo,
- parentActive: this.parentActive,
bringToFront: this.bringToFront,
addDocTab: this.addDocTab,
};
@@ -1121,7 +1126,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@computed get doInternalLayoutComputation() {
TraceMobx();
-
const newPool = new Map<string, PoolData>();
const engine = this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
switch (engine) {
@@ -1160,6 +1164,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
replica={entry[1].replica}
dataProvider={this.childDataProvider}
sizeProvider={this.childSizeProvider}
+ layerProvider={this.props.layerProvider}
pointerEvents={this.backgroundActive || this.props.childPointerEvents ?
"all" :
(this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined}
@@ -1379,7 +1384,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
};
const snappableDocs: Doc[] = []; // the set of documents in the visible viewport that we will try to snap to;
const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
- this.getActiveDocuments().filter(doc => !doc._isBackground && doc.z === undefined).map(doc => isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
+ this.getActiveDocuments().filter(doc => !Cast(doc.layers, listSpec("string"), []).includes("background") && doc.z === undefined).map(doc => isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
!snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => isDocInView(doc, selRect)); // if not, see if there are background docs to snap to
!snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => isDocInView(doc, otherBounds)); // if not, then why not snap to floating docs
@@ -1475,11 +1480,23 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
}} />;
}
+ trySelectCluster = (addToSel: boolean) => {
+ if (this._hitCluster !== -1) {
+ if (!addToSel) {
+ SelectionManager.DeselectAll();
+ }
+ const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === this._hitCluster);
+ this.selectDocuments(eles);
+ return true;
+ }
+ return false;
+ }
@computed get marqueeView() {
return <MarqueeView
{...this.props}
- nudge={this.isAnnotationOverlay ? undefined : this.nudge}
+ nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
+ trySelectCluster={this.trySelectCluster}
activeDocuments={this.getActiveDocuments}
selectDocuments={this.selectDocuments}
addDocument={this.addDocument}
@@ -1490,6 +1507,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
<div ref={this._marqueeRef}>
{this.layoutDoc["_backgroundGrid-show"] ? this.grid : (null)}
<CollectionFreeFormViewPannableContents
+ isAnnotationOverlay={this.isAnnotationOverlay}
centeringShiftX={this.centeringShiftX}
centeringShiftY={this.centeringShiftY}
presPaths={BoolCast(this.Document.presPathView)}
@@ -1514,7 +1532,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const wscale = nw ? this.props.PanelWidth() / nw : 1;
return wscale < hscale ? wscale : hscale;
}
- @computed get backgroundEvents() { return this.layoutDoc._isBackground && SnappingManager.GetIsDragging(); }
+ @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); }
render() {
TraceMobx();
@@ -1583,6 +1601,7 @@ interface CollectionFreeFormViewPannableContentsProps {
presPaths?: boolean;
progressivize?: boolean;
presPinView?: boolean;
+ isAnnotationOverlay: boolean | undefined;
}
@observer
@@ -1728,6 +1747,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
style={{
transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)`,
transition: this.props.transition,
+ width: this.props.isAnnotationOverlay ? undefined : 0, // if not an overlay, then this will be the size of the collection, but panning and zooming will move it outside the visible border of the collection and make it selectable. This problem shows up after zooming/panning on a background collection -- you can drag the collection by clicking on apparently empty space outside the collection
//willChange: "transform"
}}>
{this.props.children()}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 7040b5e56..0eb05500c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -34,6 +34,7 @@ interface MarqueeViewProps {
selectDocuments: (docs: Doc[]) => void;
addLiveTextDocument: (doc: Doc) => void;
isSelected: () => boolean;
+ trySelectCluster: (addToSel: boolean) => boolean;
nudge?: (x: number, y: number) => boolean;
setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
}
@@ -285,7 +286,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._downX = x;
this._downY = y;
const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
- if ([AclAdmin, AclEdit, AclAddonly].includes(effectiveAcl)) PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge);
+ if ([AclAdmin, AclEdit, AclAddonly].includes(effectiveAcl)) {
+ PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge);
+ }
this.clearSelection();
}
});
@@ -297,7 +300,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (Doc.GetSelectedTool() === InkTool.None) {
if (!(e.nativeEvent as any).marqueeHit) {
(e.nativeEvent as any).marqueeHit = true;
- !(e.nativeEvent as any).formattedHandled && this.setPreviewCursor(e.clientX, e.clientY, false);
+ if (!(e.nativeEvent as any).formattedHandled) {
+ if (!this.props.trySelectCluster(e.shiftKey)) {
+ this.setPreviewCursor(e.clientX, e.clientY, false);
+ } else e.stopPropagation();
+ }
}
}
// let the DocumentView stopPropagation of this event when it selects this document
@@ -355,7 +362,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.hideMarquee();
}
- getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, _isBackground?: boolean) => {
+ getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, layers: string[]) => {
const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
Doc.GetProto(doc).data = new List<Doc>(selected);
Doc.GetProto(doc).title = "nested freeform";
@@ -363,8 +370,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
newCollection.system = undefined;
- newCollection._isBackground = _isBackground;
- newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : _isBackground ? "cyan" : undefined;
+ newCollection.layers = new List<string>(layers);
+ newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : layers.includes("background") ? "cyan" : undefined;
newCollection._width = this.Bounds.width;
newCollection._height = this.Bounds.height;
newCollection.x = this.Bounds.left;
@@ -445,7 +452,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}));
this.props.removeDocument(selected);
}
- const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined);
+ const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, []);
this.props.addDocument(newCollection);
this.props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -553,7 +560,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
@action
background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
- const newCollection = this.getCollection([], undefined, true);
+ const newCollection = this.getCollection([], undefined, ["background"]);
this.props.addDocument(newCollection);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
@@ -691,7 +698,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
marqueeSelect(selectBackgrounds: boolean = true) {
const selRect = this.Bounds;
const selection: Doc[] = [];
- this.props.activeDocuments().filter(doc => !doc._isBackground && !doc.z).map(doc => {
+ this.props.activeDocuments().filter(doc => this.props.layerProvider?.(doc) !== false && !doc.z).map(doc => {
const layoutDoc = Doc.Layout(doc);
const x = NumCast(doc.x);
const y = NumCast(doc.y);
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index d31427829..f3e385746 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -1,12 +1,12 @@
import { action, computed, Lambda, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from "react";
-import { Doc, Opt, WidthSym } from '../../../../fields/Doc';
+import { Doc, Opt } from '../../../../fields/Doc';
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, returnFalse, returnZero, setupMoveUpEvents, OmitKeys } from '../../../../Utils';
+import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { SnappingManager } from '../../../util/SnappingManager';
@@ -165,15 +165,18 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
{...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
Document={layout}
DataDoc={layout.resolvedDataDoc as Doc}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
ContainingCollectionDoc={this.props.Document}
PanelWidth={width}
PanelHeight={height}
+ ContentScaling={returnOne}
+ FreezeDimensions={true}
+ fitToBox={true}
ScreenToLocalTransform={dxf}
onClick={this.onChildClickHandler}
renderDepth={this.props.renderDepth + 1}
parentActive={this.props.active}
- dontCenter={true}
+ dontCenter={"y"}
/>;
}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 6e16137b5..2e7cdf6d0 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -216,7 +216,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
return <ContentFittingDocumentView
Document={layout}
DataDoc={layout.resolvedDataDoc as Doc}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
LayoutTemplate={this.props.ChildLayoutTemplate}
LayoutTemplateString={this.props.ChildLayoutString}
LibraryPath={this.props.LibraryPath}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index c5e1816d9..4206a5171 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -216,7 +216,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
return <ContentFittingDocumentView
Document={layout}
DataDoc={layout.resolvedDataDoc as Doc}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
LayoutTemplate={this.props.ChildLayoutTemplate}
LayoutTemplateString={this.props.ChildLayoutString}
LibraryPath={this.props.LibraryPath}