aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2020-11-30 10:59:52 -0500
committerbobzel <zzzman@gmail.com>2020-11-30 10:59:52 -0500
commit228ea98b69e848dddf835debb2acf77bbd7ddfbb (patch)
tree62e2d3054ab565bc91997db7875d4022e1751564
parentaae3fe10d4f996dfc811977ce457ae33d5fe3bcc (diff)
parent5da87a5d161446308031dd4b221f1f7e3a127e72 (diff)
Merge branch 'master' into sally-changes
-rw-r--r--src/.DS_Storebin8196 -> 8196 bytes
-rw-r--r--src/client/documents/Documents.ts4
-rw-r--r--src/client/goldenLayout.js11
-rw-r--r--src/client/util/CurrentUserUtils.ts13
-rw-r--r--src/client/util/DocumentManager.ts1
-rw-r--r--src/client/util/DragManager.ts5
-rw-r--r--src/client/util/LinkManager.ts3
-rw-r--r--src/client/util/SharingManager.tsx4
-rw-r--r--src/client/views/ContextMenuItem.tsx2
-rw-r--r--src/client/views/DocComponent.tsx6
-rw-r--r--src/client/views/DocumentDecorations.tsx20
-rw-r--r--src/client/views/GestureOverlay.tsx2
-rw-r--r--src/client/views/GlobalKeyHandler.ts22
-rw-r--r--src/client/views/InkingStroke.tsx6
-rw-r--r--src/client/views/MainView.tsx58
-rw-r--r--src/client/views/OverlayView.tsx3
-rw-r--r--src/client/views/Palette.tsx2
-rw-r--r--src/client/views/PreviewCursor.tsx15
-rw-r--r--src/client/views/PropertiesView.tsx6
-rw-r--r--src/client/views/TemplateMenu.tsx1
-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
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx34
-rw-r--r--src/client/views/nodes/AudioBox.tsx3
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx22
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx66
-rw-r--r--src/client/views/nodes/DocHolderBox.tsx6
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.scss6
-rw-r--r--src/client/views/nodes/DocumentView.tsx80
-rw-r--r--src/client/views/nodes/FieldView.tsx5
-rw-r--r--src/client/views/nodes/FilterBox.tsx5
-rw-r--r--src/client/views/nodes/FontIconBox.tsx2
-rw-r--r--src/client/views/nodes/ImageBox.tsx3
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx2
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx1
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx2
-rw-r--r--src/client/views/nodes/LinkBox.tsx2
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx4
-rw-r--r--src/client/views/nodes/PresBox.tsx2
-rw-r--r--src/client/views/nodes/ScriptingBox.scss6
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx14
-rw-r--r--src/client/views/nodes/VideoBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx13
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx21
-rw-r--r--src/client/views/nodes/formattedText/RichTextSchema.tsx2
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts2
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx12
-rw-r--r--src/fields/Doc.ts1
-rw-r--r--src/fields/documentSchemas.ts2
-rw-r--r--src/mobile/AudioUpload.tsx2
-rw-r--r--src/mobile/MobileInterface.tsx2
70 files changed, 743 insertions, 482 deletions
diff --git a/src/.DS_Store b/src/.DS_Store
index eaed2932f..bdc161da2 100644
--- a/src/.DS_Store
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2d8a897a5..ae0cd8b92 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -138,7 +138,7 @@ export interface DocumentOptions {
isAnnotating?: boolean; // whether we web document is annotation mode where links can't be clicked to allow annotations to be created
opacity?: number;
defaultBackgroundColor?: string;
- _isBackground?: boolean;
+ _layers?: List<string>;
_raiseWhenDragged?: boolean; // whether a document is brought to front when dragged.
isLinkButton?: boolean;
_columnWidth?: number;
@@ -712,7 +712,7 @@ export namespace Docs {
const doc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, {
dontRegisterChildViews: true,
isLinkButton: true, treeViewHideTitle: true, backgroundColor: "lightBlue", // lightBlue is default color for linking dot and link documents text comment area
- treeViewExpandedView: "fields", removeDropProperties: new List(["_isBackground", "isLinkButton"]), ...options
+ treeViewExpandedView: "fields", removeDropProperties: new List(["_layers", "isLinkButton"]), ...options
}, id);
const linkDocProto = Doc.GetProto(doc);
linkDocProto.treeViewOpen = true;// setting this in the instance creator would set it on the view document.
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 293762709..97ce13ae1 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -3204,7 +3204,16 @@
* If this was the last content item, remove this node as well
*/
} else if (!(this instanceof lm.items.Root) && this.config.isClosable === true) {
- if (!this.parent.parent.isRoot || this.parent.contentItems.length > 1) this.parent.removeChild(this); // bcz: added test for last stack
+ const stack = this;
+ const rowOrCol = stack.parent;
+ const parRowOrCol = rowOrCol.parent;
+ const canDelete = rowOrCol && !rowOrCol.isRoot && (rowOrCol.contentItems.length > 1 || (parRowOrCol && parRowOrCol.contentItems.length > 1)); // bcz: added test for last stack
+ if (canDelete) {
+ rowOrCol.removeChild(stack);
+ if (rowOrCol.contentItems.length === 1 && parRowOrCol.contentItems.length === 1 && !parRowOrCol.isRoot) {
+ parRowOrCol.replaceChild(rowOrCol, rowOrCol.contentItems[0]);
+ }
+ }
}
},
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4f054269f..99dfbaf1c 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -378,10 +378,8 @@ export class CurrentUserUtils {
((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptySlide === undefined) {
- const textDoc = Docs.Create.TextDocument("Slide", { title: "Slide", _viewType: CollectionViewType.Tree, _fontSize: "20px", treeViewOutlineMode: true, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, _backgroundColor: "transparent", system: true, cloneFieldFilter: new List<string>(["system"]) });
- Doc.GetProto(textDoc).layout = CollectionView.LayoutString("data");
+ const textDoc = Docs.Create.TreeDocument([], { title: "Slide", _viewType: CollectionViewType.Tree, _fontSize: "20px", treeViewOutlineMode: true, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, _backgroundColor: "transparent", system: true, cloneFieldFilter: new List<string>(["system"]) });
Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text');
- Doc.GetProto(textDoc).data = new List<Doc>([]);
FormattedTextBox.SelectOnLoad = textDoc[Id];
doc.emptySlide = textDoc;
}
@@ -496,7 +494,7 @@ export class CurrentUserUtils {
activeInkPen,
backgroundColor,
_hideContextMenu: true,
- removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
+ removeDropProperties: new List<string>(["_stayInCollection"]),
_stayInCollection: true,
dragFactory,
clickFactory,
@@ -1052,7 +1050,12 @@ export class CurrentUserUtils {
Docs.newAccount = !(field instanceof Doc);
await Docs.Prototypes.initialize();
const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
- return this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
+ const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
+ (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
+ const a1 = await Cast(link?.anchor1, Doc, null);
+ const a2 = await Cast(link?.anchor2, Doc, null);
+ });
+ return updated;
});
} else {
throw new Error("There should be a user id! Why does Dash think there isn't one?");
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index a6816c7f9..eaccbdb63 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -9,7 +9,6 @@ import { CollectionView } from '../views/collections/CollectionView';
import { DocumentView } from '../views/nodes/DocumentView';
import { LinkManager } from './LinkManager';
import { Scripting } from './Scripting';
-import { SelectionManager } from './SelectionManager';
import { LinkDocPreview } from '../views/nodes/LinkDocPreview';
import { FormattedTextBoxComment } from '../views/nodes/formattedText/FormattedTextBoxComment';
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 86e2d339e..e3019d288 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -127,7 +127,6 @@ export namespace DragManager {
droppedDocuments: Doc[];
dragDivName?: string;
treeViewDoc?: Doc;
- dontHideOnDrop?: boolean;
offset: number[];
canEmbed?: boolean;
userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
@@ -347,6 +346,7 @@ export namespace DragManager {
dragDiv.appendChild(dragLabel);
DragManager.Root().appendChild(dragDiv);
}
+ dragDiv.hidden = false;
dragLabel.style.display = "";
const scaleXs: number[] = [];
const scaleYs: number[] = [];
@@ -546,13 +546,14 @@ export namespace DragManager {
function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any },
xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number, options?: DragOptions, finishDrag?: (e: DragCompleteEvent) => void) {
- const removed = dragData.dontHideOnDrop ? [] : dragEles.map(dragEle => {
+ const removed = dragEles.map(dragEle => {
const ret = { ele: dragEle, w: dragEle.style.width, h: dragEle.style.height, o: dragEle.style.overflow };
dragEle.style.width = "0";
dragEle.style.height = "0";
dragEle.style.overflow = "hidden";
return ret;
});
+ dragDiv.hidden = true;
const target = document.elementFromPoint(e.x, e.y);
removed.map(r => {
r.ele.style.width = r.w;
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 802b8ae7b..38e81cf99 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -34,7 +34,8 @@ export class LinkManager {
public getAllLinks(): Doc[] { return this.allLinks(); }
allLinks = computedFn(function allLinks(this: any): Doc[] {
- const lset = new Set<Doc>(DocListCast(Doc.LinkDBDoc().data));
+ const linkData = Doc.LinkDBDoc().data;
+ const lset = new Set<Doc>(DocListCast(linkData));
SharingManager.Instance.users.forEach(user => DocListCast(user.linkDatabase?.data).forEach(doc => lset.add(doc)));
return Array.from(lset);
}, true);
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 2b13d2a44..062852e36 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -134,6 +134,10 @@ export class SharingManager extends React.Component<{}> {
const linkDatabase = await DocServer.GetRefField(user.linkDatabaseId);
if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
await DocListCastAsync(linkDatabase.data);
+ (await DocListCastAsync(Cast(linkDatabase, Doc, null).data))?.forEach(async link => { // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
+ const a1 = await Cast(link?.anchor1, Doc, null);
+ const a2 = await Cast(link?.anchor2, Doc, null);
+ });
sharingDocs.push({ user, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.color) });
}
}
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index d3429cdfb..e63631161 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -46,7 +46,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`);
}
await this.props.event({ x: e.clientX, y: e.clientY });
- batch && batch.end();
+ batch?.end();
}
}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index a55f4adaf..d6116fd23 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -37,6 +37,7 @@ interface ViewBoxBaseProps {
DataDoc?: Doc;
ContainingCollectionDoc: Opt<Doc>;
fieldKey: string;
+ layerProvider?: (doc: Doc, assign?: boolean) => boolean;
isSelected: (outsideReaction?: boolean) => boolean;
renderDepth: number;
rootSelected: (outsideReaction?: boolean) => boolean;
@@ -58,7 +59,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps, T>(schemaCtor:
lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.ContainingCollectionDoc }).result;
- active = (outsideReaction?: boolean) => !this.props.Document._isBackground && (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0 || this.layoutDoc.forceActive);// && !Doc.SelectedTool(); // bcz: inking state shouldn't affect static tools
+ active = (outsideReaction?: boolean) => this.props.layerProvider?.(this.props.Document) !== false && (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0 || this.layoutDoc.forceActive);// && !Doc.SelectedTool(); // bcz: inking state shouldn't affect static tools
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
}
return Component;
@@ -70,6 +71,7 @@ export interface ViewBoxAnnotatableProps {
Document: Doc;
DataDoc?: Doc;
fieldKey: string;
+ layerProvider?: (doc: Doc) => boolean;
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
isSelected: (outsideReaction?: boolean) => boolean;
@@ -190,7 +192,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.props.Document._) &&
(this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0 || BoolCast((this.layoutDoc as any).forceActive)) ? true : false)
- annotationsActive = (outsideReaction?: boolean) => (Doc.GetSelectedTool() !== InkTool.None || (this.props.Document._isBackground && this.props.active()) ||
+ annotationsActive = (outsideReaction?: boolean) => (Doc.GetSelectedTool() !== InkTool.None || (this.props.layerProvider?.(this.props.Document) === false && this.props.active()) ||
(this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
}
return Component;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 63b99cd85..8d905bcac 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -28,7 +28,7 @@ import { InkStrokeProperties } from './InkStrokeProperties';
import { KeyManager } from './GlobalKeyHandler';
@observer
-export class DocumentDecorations extends React.Component<{}, { value: string }> {
+export class DocumentDecorations extends React.Component<{ boundsLeft: number, boundsTop: number }, { value: string }> {
static Instance: DocumentDecorations;
private _resizeHdlId = "";
private _keyinput = React.createRef<HTMLInputElement>();
@@ -54,7 +54,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@observable public pullIcon: IconProp = "arrow-alt-circle-down";
@observable public pullColor: string = "white";
- constructor(props: Readonly<{}>) {
+ constructor(props: any) {
super(props);
DocumentDecorations.Instance = this;
reaction(() => SelectionManager.SelectedDocuments().slice(), docs => this.titleBlur(false));
@@ -603,17 +603,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<span style={{ width: "100%", display: "inline-block", cursor: "move" }}>{`${this.selectionTitle}`}</span>
</div>;
- bounds.x = Math.max(0, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
- bounds.y = Math.max(0, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
+ const leftBounds = this.props.boundsLeft;
+ const topBounds = this.props.boundsTop;
+ bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
+ bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
const borderRadiusDraggerWidth = 15;
- bounds.r = Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth;
- bounds.b = Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight;
- if (bounds.x > bounds.r) {
- bounds.x = bounds.r - this._resizeBorderWidth;
- }
- if (bounds.y > bounds.b) {
- bounds.y = bounds.b - (this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight);
- }
+ bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
+ bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
const useRotation = seldoc.rootDoc.type === DocumentType.INK;
return (<div className="documentDecorations" style={{ background: darkScheme }} >
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index ffa089af1..35dedf016 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -901,7 +901,7 @@ export class GestureOverlay extends Touchable {
PanelWidth={this.return300}
PanelHeight={this.return300}
renderDepth={0}
- backgroundColor={returnEmptyString}
+ styleProvider={returnEmptyString}
focus={emptyFunction}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index fb360ee26..3f3d382e4 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -26,6 +26,7 @@ import { DocumentView } from "./nodes/DocumentView";
import { PDFMenu } from "./pdf/PDFMenu";
import { SnappingManager } from "../util/SnappingManager";
import { SearchBox } from "./search/SearchBox";
+import { random } from "lodash";
const modifiers = ["control", "meta", "shift", "alt"];
type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -56,7 +57,7 @@ export class KeyManager {
public handle = action(async (e: KeyboardEvent) => {
if (e.key?.toLowerCase() === "shift" && e.ctrlKey && e.altKey) KeyManager.Instance.ShiftPressed = true;
- if (!Doc.UserDoc().noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
+ //if (!Doc.UserDoc().noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
const keyname = e.key && e.key.toLowerCase();
this.handleGreedy(keyname);
@@ -85,7 +86,24 @@ export class KeyManager {
private unmodified = action((keyname: string, e: KeyboardEvent) => {
switch (keyname) {
- case "a": DragManager.CanEmbed = true;
+ case "u":
+ if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+ return { stopPropagation: false, preventDefault: false };
+ }
+
+ const ungroupings = SelectionManager.SelectedDocuments().slice();
+ UndoManager.RunInBatch(() => ungroupings.map(dv => dv.layoutDoc.group = undefined), "ungroup");
+ SelectionManager.DeselectAll();
+ break;
+ case "g":
+ if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+ return { stopPropagation: false, preventDefault: false };
+ }
+
+ const groupings = SelectionManager.SelectedDocuments().slice();
+ const randomGroup = random(0, 1000);
+ UndoManager.RunInBatch(() => groupings.map(dv => dv.layoutDoc.group = randomGroup), "group");
+ SelectionManager.DeselectAll();
break;
case " ":
// MarqueeView.DragMarquee = !MarqueeView.DragMarquee; // bcz: this needs a better disclosure UI
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 7e424d8f0..8ed6fbd85 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -120,7 +120,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const hpoints = InteractionUtils.CreatePolyline(data, left, top,
this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
- "none", "none", undefined, scaleX, scaleY, "", "visiblepainted", false, true);
+ "none", "none", undefined, scaleX, scaleY, "", this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted", false, true);
//points for adding
const apoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth,
@@ -178,7 +178,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
onPointerDown={(e) => this.onControlDown(e, pts.I)} pointerEvents="all" cursor="all-scroll" display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} />
</svg>);
const handleLines = handleLine.map((pts, i) =>
- <svg height="100" width="100" key={`line${i}`}>
+ <svg height="100" width="100" key={`line${i}`} >
<line x1={(pts.X1 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y1={(pts.Y1 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
x2={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="green" strokeWidth={String(Number(dotsize) / 2)}
display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} />
@@ -191,7 +191,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
return (
<svg className="inkingStroke"
style={{
- pointerEvents: this.props.Document.isInkMask ? "all" : "none",
+ pointerEvents: this.props.Document.isInkMask && this.props.layerProvider?.(this.props.Document) !== false ? "all" : "none",
transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
overflow: "visible",
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 452ce61ff..032d4d3bb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -16,7 +16,6 @@ import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, return
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { Docs } from '../documents/Documents';
-import { DocumentType } from '../documents/DocumentTypes';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
import { DocumentManager } from '../util/DocumentManager';
import { GroupManager } from '../util/GroupManager';
@@ -59,6 +58,7 @@ import { SearchBox } from './search/SearchBox';
import { TraceMobx } from '../../fields/util';
import { SelectionManager } from '../util/SelectionManager';
import { UndoManager } from '../util/UndoManager';
+import { TabDocView } from './collections/TabDocView';
const _global = (window /* browser */ || global /* node */) as any;
@observer
@@ -75,6 +75,7 @@ export class MainView extends React.Component {
@observable private _flyoutWidth: number = 0;
@computed private get topOffset() { return (CollectionMenu.Instance?.Pinned ? 35 : 0) + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); }
+ @computed private get leftOffset() { return this.menuPanelWidth() - 2; }
@computed private get userDoc() { return Doc.UserDoc(); }
@computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
@computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
@@ -228,35 +229,6 @@ export class MainView extends React.Component {
getPHeight = () => this._panelHeight;
getContentsHeight = () => this._panelHeight - Number(SEARCH_PANEL_HEIGHT.replace("px", ""));
- defaultBackgroundColors = (doc: Opt<Doc>, renderDepth: number) => {
- if (this.darkScheme) {
- switch (doc?.type) {
- case DocumentType.PRESELEMENT: return "dimgrey";
- case DocumentType.PRES: return "#3e3e3e";
- case DocumentType.FONTICON: return "black";
- case DocumentType.RTF || DocumentType.LABEL || DocumentType.BUTTON: return "#2d2d2d";
- case DocumentType.LINK:
- case DocumentType.COL:
- return Doc.IsSystem(doc) ? "rgb(62,62,62)" : StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
- //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
- default: return "black";
- }
- } else {
- switch (doc?.type) {
- case DocumentType.PRESELEMENT: return "";
- case DocumentType.FONTICON: return "black";
- case DocumentType.RTF: return "#f1efeb";
- case DocumentType.BUTTON:
- case DocumentType.LABEL: return "lightgray";
- case DocumentType.LINK:
- case DocumentType.COL:
- return Doc.IsSystem(doc) ? "lightgrey" : StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
- //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "lightgray";
- default: return "white";
- }
- }
- }
-
@computed get mainDocView() {
return <DocumentView
Document={this.mainContainer!}
@@ -267,7 +239,6 @@ export class MainView extends React.Component {
pinToPres={emptyFunction}
rootSelected={returnTrue}
onClick={undefined}
- backgroundColor={this.defaultBackgroundColors}
removeDocument={undefined}
ScreenToLocalTransform={Transform.Identity}
ContentScaling={returnOne}
@@ -310,8 +281,8 @@ export class MainView extends React.Component {
}
flyoutWidthFunc = () => this._flyoutWidth;
- sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1);
- mainContainerXf = () => this.sidebarScreenToLocal().translate(-58, 0);
+ sidebarScreenToLocal = () => new Transform(0, -this.topOffset, 1);
+ mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftOffset, 0);
addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
return where === "close" ? CollectionDockingView.CloseSplit(doc) :
doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : CollectionDockingView.AddSplit(doc, "right");
@@ -324,7 +295,7 @@ export class MainView extends React.Component {
<div className="mainView-libraryFlyout" style={{ minWidth: this._flyoutWidth, width: this._flyoutWidth }} >
<div className="mainView-contentArea" >
<DocumentView
- Document={this._sidebarContent}
+ Document={this._sidebarContent.proto || this._sidebarContent}
DataDoc={undefined}
LibraryPath={emptyPath}
addDocument={undefined}
@@ -339,7 +310,7 @@ export class MainView extends React.Component {
PanelHeight={this.getContentsHeight}
renderDepth={0}
focus={emptyFunction}
- backgroundColor={this.defaultBackgroundColors}
+ styleProvider={TabDocView.styleProvider}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
@@ -349,7 +320,6 @@ export class MainView extends React.Component {
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
relative={true}
- forcedBackgroundColor={() => this.darkScheme ? "rgb(62,62,62)" : "lightgrey"}
/>
</div>
{this.docButtons}
@@ -374,7 +344,7 @@ export class MainView extends React.Component {
PanelHeight={this.getContentsHeight}
renderDepth={0}
focus={emptyFunction}
- backgroundColor={this.defaultBackgroundColors}
+ styleProvider={TabDocView.styleProvider}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
@@ -426,7 +396,7 @@ export class MainView extends React.Component {
<div className="mainView-propertiesDragger" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
<FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? "white" : "black"} size="sm" />
</div>
- {this.propertiesWidth() < 10 ? (null) : <PropertiesView backgroundColor={this.defaultBackgroundColors} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
+ {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={TabDocView.styleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
</div>
</>;
}
@@ -480,7 +450,8 @@ export class MainView extends React.Component {
fieldKey={"data"}
dropAction={"alias"}
annotationsKey={""}
- backgroundColor={this.defaultBackgroundColors}
+ parentActive={returnFalse}
+ styleProvider={TabDocView.styleProvider}
rootSelected={returnTrue}
bringToFront={emptyFunction}
select={emptyFunction}
@@ -556,7 +527,7 @@ export class MainView extends React.Component {
pinToPres={emptyFunction}
rootSelected={returnTrue}
onClick={undefined}
- backgroundColor={this.defaultBackgroundColors}
+ styleProvider={TabDocView.styleProvider}
removeDocument={undefined}
ScreenToLocalTransform={Transform.Identity}
ContentScaling={returnOne}
@@ -564,6 +535,7 @@ export class MainView extends React.Component {
PanelHeight={this.getPHeight}
renderDepth={0}
focus={emptyFunction}
+ parentActive={returnFalse}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
@@ -588,6 +560,7 @@ export class MainView extends React.Component {
select={returnFalse}
rootSelected={returnFalse}
renderDepth={0}
+ parentActive={returnFalse}
addDocTab={returnFalse}
pinToPres={returnFalse}
ScreenToLocalTransform={Transform.Identity}
@@ -613,12 +586,12 @@ export class MainView extends React.Component {
<SettingsManager />
<GroupManager />
<GoogleAuthenticationManager />
- <DocumentDecorations />
+ <DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
{this.search}
<CollectionMenu />
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
{DocumentLinksButton.EditLink ? <LinkMenu docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
- {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} backgroundColor={this.defaultBackgroundColors}
+ {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} styleProvider={TabDocView.styleProvider}
linkDoc={LinkDocPreview.LinkInfo.linkDoc} linkSrc={LinkDocPreview.LinkInfo.linkSrc} href={LinkDocPreview.LinkInfo.href}
addDocTab={LinkDocPreview.LinkInfo.addDocTab} /> : (null)}
<GestureOverlay >
@@ -659,6 +632,7 @@ export class MainView extends React.Component {
ScreenToLocalTransform={Transform.Identity}
bringToFront={returnFalse}
active={returnFalse}
+ parentActive={returnFalse}
whenActiveChanged={returnFalse}
focus={returnFalse}
PanelWidth={() => 500}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 7d47abdce..37e5b55eb 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -15,6 +15,7 @@ import { ScriptingRepl } from './ScriptingRepl';
import { DragManager } from "../util/DragManager";
import { List } from "../../fields/List";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import { TabDocView } from "./collections/TabDocView";
export type OverlayDisposer = () => void;
@@ -195,7 +196,7 @@ export class OverlayView extends React.Component {
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
focus={emptyFunction}
- backgroundColor={returnEmptyString}
+ styleProvider={TabDocView.styleProvider}
addDocTab={returnFalse}
pinToPres={emptyFunction}
docFilters={returnEmptyFilter}
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 62e0fb379..536af2776 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -53,7 +53,7 @@ export default class Palette extends React.Component<PaletteProps> {
PanelHeight={() => window.screen.height}
renderDepth={0}
focus={emptyFunction}
- backgroundColor={returnEmptyString}
+ styleProvider={returnEmptyString}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index f5f115e01..d756227f6 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -2,20 +2,15 @@ import { action, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
-import "./PreviewCursor.scss";
-import { Docs, DocUtils } from '../documents/Documents';
import { Doc } from '../../fields/Doc';
-import { Transform } from "../util/Transform";
+import { Cast, NumCast } from '../../fields/Types';
import { DocServer } from '../DocServer';
+import { Docs, DocUtils } from '../documents/Documents';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import { Transform } from "../util/Transform";
import { undoBatch, UndoManager } from '../util/UndoManager';
-import { NumCast, Cast } from '../../fields/Types';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
-import * as rp from 'request-promise';
-import { Utils } from '../../Utils';
-import { Networking } from '../Network';
-import { Upload } from '../../server/SharedMediaTypes';
-import { basename } from 'path';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import "./PreviewCursor.scss";
@observer
export class PreviewCursor extends React.Component<{}> {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index fad2f5284..245e612b3 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -36,7 +36,7 @@ const _global = (window /* browser */ || global /* node */) as any;
interface PropertiesViewProps {
width: number;
height: number;
- backgroundColor: (doc: Opt<Doc>, renderDepth: number) => Opt<string>;
+ styleProvider?: (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
}
@observer
@@ -270,10 +270,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
renderDepth={1}
rootSelected={returnFalse}
treeViewDoc={undefined}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
fitToBox={true}
FreezeDimensions={true}
- dontCenter={true}
+ dontCenter={"y"}
NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
PanelWidth={panelWidth}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 94efff4ee..68f30d58c 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -139,6 +139,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
LibraryPath={emptyPath}
dropAction={undefined}
active={returnTrue}
+ parentActive={returnFalse}
ContentScaling={returnOne}
bringToFront={emptyFunction}
focus={emptyFunction}
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}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 5c9123876..b9e240ba2 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -174,14 +174,26 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
@undoBatch
@action
- showLink = () => {
- this.props.linkDoc.hidden = !this.props.linkDoc.hidden;
+ autoMove = (e: React.PointerEvent) => {
+ e.stopPropagation();
+ this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove;
}
- render() {
+ @undoBatch
+ @action
+ showLink = (e: React.PointerEvent) => {
+ e.stopPropagation();
+ this.props.linkDoc.linkDisplay = !this.props.linkDoc.linkDisplay;
+ }
- const eyeIcon = this.props.linkDoc.hidden ? "eye-slash" : "eye";
+ @undoBatch
+ @action
+ showAnchor = (e: React.PointerEvent) => {
+ e.stopPropagation();
+ this.props.linkDoc.hidden = !this.props.linkDoc.hidden;
+ }
+ render() {
let destinationIcon: FontAwesomeIconProps["icon"] = "question";
switch (this.props.destinationDoc.type) {
case DocumentType.IMG: destinationIcon = "image"; break;
@@ -243,9 +255,19 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
<div className="linkMenu-item-buttons" ref={this._buttonRef} >
- <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.hidden ? "Show link" : "Hide link"}</div></>}>
+ <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.hidden ? "Show Anchor" : "Hide Anchor"}</div></>}>
+ <div className="button" ref={this._editRef} onPointerDown={this.showAnchor}>
+ <FontAwesomeIcon className="fa-icon" icon={this.props.linkDoc.hidden ? "eye-slash" : "eye"} size="sm" /></div>
+ </Tooltip>
+
+ <Tooltip title={<><div className="dash-tooltip">{!this.props.linkDoc.linkDisplay ? "Show link" : "Hide link"}</div></>}>
<div className="button" ref={this._editRef} onPointerDown={this.showLink}>
- <FontAwesomeIcon className="fa-icon" icon={eyeIcon} size="sm" /></div>
+ <FontAwesomeIcon className="fa-icon" icon={!this.props.linkDoc.linkDisplay ? "eye-slash" : "eye"} size="sm" /></div>
+ </Tooltip>
+
+ <Tooltip title={<><div className="dash-tooltip">{!this.props.linkDoc.linkAutoMove ? "Auto move dot" : "Freeze dot position"}</div></>}>
+ <div className="button" ref={this._editRef} onPointerDown={this.autoMove}>
+ <FontAwesomeIcon className="fa-icon" icon={this.props.linkDoc.linkAutoMove ? "play" : "pause"} size="sm" /></div>
</Tooltip>
<Tooltip title={<><div className="dash-tooltip">Edit Link</div></>}>
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index cba65f663..6323a6100 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -623,9 +623,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
ContainingCollectionDoc={this.props.Document}
parentActive={returnTrue}
bringToFront={emptyFunction}
- backgroundColor={returnTransparent}
ContentScaling={returnOne}
- forcedBackgroundColor={returnTransparent}
+ styleProvider={(doc: Opt<Doc>, renderDepth: number, property: string) => property === "backgroundColor" ? "transparent" : undefined}
pointerEvents={"none"}
LayoutTemplate={undefined}
LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c87239ee9..84f08b217 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,4 @@
-import { computed, IReactionDisposer, observable, reaction, trace } from "mobx";
+import { computed, IReactionDisposer, observable, reaction, trace, action } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
@@ -18,10 +18,13 @@ import { DocumentType } from "../../documents/DocumentTypes";
import { Zoom, Fade, Flip, Rotate, Bounce, Roll, LightSpeed } from 'react-reveal';
import { PresBox, PresEffect } from "./PresBox";
import { InkingStroke } from "../InkingStroke";
+import { SnappingManager } from "../../util/SnappingManager";
+import { InkTool } from "../../../fields/InkField";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined;
+ layerProvider?: (doc: Doc, assign?: boolean) => boolean;
zIndex?: number;
highlight?: boolean;
jitterRotation: number;
@@ -33,6 +36,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, Document>(Document) {
@observable _animPos: number[] | undefined = undefined;
+ @observable _contentView: ContentFittingDocumentView | undefined | null;
random(min: number, max: number) { // min should not be equal to max
const mseed = Math.abs(this.X * this.Y);
const seed = (mseed * 9301 + 49297) % 233280;
@@ -204,8 +208,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
dragDivName={"collectionFreeFormDocumentView-container"}
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
opacity={this.opacity}
+ layerProvider={this.props.layerProvider}
NativeHeight={this.NativeHeight}
NativeWidth={this.NativeWidth}
PanelWidth={this.panelWidth}
@@ -244,17 +249,21 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
opacity = () => this.Opacity;
NativeWidth = () => this.nativeWidth;
NativeHeight = () => this.nativeHeight;
+ @computed get pointerEvents() {
+ if (this.props.pointerEvents === "none") return "none";
+ return this.props.styleProvider?.(this.Document, this.props.renderDepth, !this._contentView?.docView?.isSelected() ? "pointerEvents:selected" : "pointerEvents", this.props.layerProvider);
+ }
render() {
TraceMobx();
- const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document, this.props.renderDepth);
+ const backgroundColor = this.props.styleProvider?.(this.Document, this.props.renderDepth, "backgroundColor", this.props.layerProvider);
const borderRounding = StrCast(Doc.Layout(this.layoutDoc).borderRounding) || StrCast(this.layoutDoc.borderRounding) || StrCast(this.Document.borderRounding) || undefined;
return <div className="collectionFreeFormDocumentView-container"
style={{
boxShadow:
this.Opacity === 0 ? undefined : // if it's not visible, then no shadow
this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
- this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc._isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
- this.layoutDoc._isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
+ this.props.backgroundHalo?.(this.props.Document) && this.props.Document.type !== DocumentType.INK ? (`${this.props.styleProvider?.(this.props.Document, this.props.renderDepth, "backgroundColor", this.props.layerProvider)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(Cast(this.layoutDoc.layers, listSpec("string"), []).includes("background") ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ Cast(this.layoutDoc.layers, listSpec("string"), []).includes('background') ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: borderRounding,
outline: this.Highlight ? "orange solid 2px" : "",
@@ -266,7 +275,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
display: this.ZInd === -99 ? "none" : undefined,
// @ts-ignore
- pointerEvents: this.props.Document._isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents
+ pointerEvents: this.pointerEvents
}} >
{Doc.UserDoc().renderStyle !== "comic" ? (null) :
@@ -280,6 +289,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
{!this.props.fitToBox ?
<>{this.freeformNodeDiv}</>
: <ContentFittingDocumentView {...this.props}
+ ref={action((r: ContentFittingDocumentView | null) => this._contentView = r)}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
DataDoc={this.props.DataDoc}
ScreenToLocalTransform={this.getTransform}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 0c52b9044..a54479f55 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -1,87 +1,85 @@
import React = require("react");
-import { computed } from "mobx";
+import { computed, observable, action } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, StrCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, OmitKeys, returnVal } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnVal, returnOne } from "../../../Utils";
import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
import "./ContentFittingDocumentView.scss";
interface ContentFittingDocumentViewProps {
- dontCenter?: boolean;
+ dontCenter?: string; // "x" ,"y", "xy"
}
@observer
export class ContentFittingDocumentView extends React.Component<DocumentViewProps & ContentFittingDocumentViewProps> {
public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
- private get layoutDoc() {
+ public ContentRef = React.createRef<HTMLDivElement>();
+ @observable public docView: DocumentView | undefined | null;
+ @computed get layoutDoc() {
return this.props.LayoutTemplate?.() ||
(this.props.layoutKey && Doc.Layout(this.props.Document, Cast(this.props.Document[this.props.layoutKey], Doc, null))) ||
Doc.Layout(this.props.Document);
}
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
-
- nativeWidth = () => returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || this.props.PanelWidth());
- nativeHeight = () => returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || this.props.PanelHeight());
- @computed get scaling() {
- const wscale = this.props.PanelWidth() / this.nativeWidth();
- const hscale = this.props.PanelHeight() / this.nativeHeight();
- if (wscale * this.nativeHeight() > this.props.PanelHeight()) {
+ @computed get nativeWidth() { return !this.layoutDoc._fitWidth && returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.freezeDimensions)); }
+ @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 0); }
+ @computed get nativeScaling() {
+ if (!this.nativeWidth || !this.nativeHeight) return 1;
+ const wscale = this.props.PanelWidth() / this.nativeWidth;
+ const hscale = this.props.PanelHeight() / this.nativeHeight;
+ if (wscale * this.nativeHeight > this.props.PanelHeight()) {
return hscale || 1;
}
return wscale || 1;
}
- private contentScaling = () => this.scaling;
-
- private PanelWidth = () => this.panelWidth;
- private PanelHeight = () => this.panelHeight;
- @computed get panelWidth() { return this.nativeWidth() && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
+ @computed get panelWidth() { return this.nativeWidth ? this.nativeWidth * this.nativeScaling : this.props.PanelWidth(); }
@computed get panelHeight() {
- if (this.nativeHeight()) {
- if (!this.props.Document._fitWidth) return this.nativeHeight() * this.contentScaling();
- else return this.panelWidth / Doc.NativeAspect(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 1;
+ if (this.nativeHeight) {
+ if (this.props.Document._fitWidth) return Math.min(this.props.PanelHeight(), this.panelWidth / Doc.NativeAspect(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 1);
+ return Math.min(this.props.PanelHeight(), this.nativeHeight * this.nativeScaling);
}
return this.props.PanelHeight();
}
- @computed get childXf() { return this.props.DataDoc ? 1 : 1 / this.contentScaling(); } // this is intended to detect when a document is being rendered inside itself as part of a template, but not as a leaf node where nativeWidth & height would apply.
- private getTransform = () => this.props.dontCenter ?
- this.props.ScreenToLocalTransform().scale(this.childXf) :
- this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(this.childXf)
- private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
- private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; }
+ private getTransform = () => this.props.ScreenToLocalTransform().
+ translate(this.props.dontCenter?.includes("x") ? 0 : -this.centeringOffset, this.props.dontCenter?.includes("y") ? 0 : -this.centeringYOffset)
+ private get centeringOffset() { return this.nativeWidth && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth * this.nativeScaling) / 2 : 0; }
+ private get centeringYOffset() { return this.nativeWidth && Math.abs(this.centeringOffset) < 0.001 && this.nativeHeight ? (this.props.PanelHeight() - this.nativeHeight * this.nativeScaling) / 2 : 0; }
@computed get borderRounding() { return StrCast(this.props.Document?.borderRounding); }
+ PanelWidth = () => this.panelWidth;
+ PanelHeight = () => this.panelHeight;
+
render() {
TraceMobx();
return (<div className="contentFittingDocumentView">
{!this.props.Document || !this.props.PanelWidth ? (null) : (
- <div className="contentFittingDocumentView-previewDoc"
+ <div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef}
style={{
- transform: !this.props.dontCenter ? `translate(${this.centeringOffset}px, ${this.centeringYOffset}px)` : undefined,
+ transform: `translate(${this.props.dontCenter?.includes("x") ? 0 : this.centeringOffset}px, ${this.props.dontCenter?.includes("y") ? 0 : this.centeringYOffset}px)`,
borderRadius: this.borderRounding,
- height: Math.abs(this.centeringYOffset) > 0.001 ? `${100 * this.nativeHeight() / this.nativeWidth() * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
- width: Math.abs(this.centeringOffset) > 0.001 ? `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth()
+ height: Math.abs(this.centeringYOffset) > 0.001 && this.nativeWidth ? `${100 * this.nativeHeight / this.nativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
+ width: Math.abs(this.centeringOffset) > 0.001 ? `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth(),
}}>
<DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+ ref={action((r: DocumentView | null) => this.docView = r)}
Document={this.props.Document}
DataDoc={this.props.DataDoc}
LayoutTemplate={this.props.LayoutTemplate}
LayoutTemplateString={this.props.LayoutTemplateString}
LibraryPath={this.props.LibraryPath}
- // NativeWidth={this.nativeWidth}
- // NativeHeight={this.nativeHeight}
PanelWidth={this.PanelWidth}
PanelHeight={this.PanelHeight}
- ContentScaling={this.contentScaling}
+ ContentScaling={returnOne}
fitToBox={this.props.fitToBox}
layoutKey={this.props.layoutKey}
dropAction={this.props.dropAction}
onClick={this.props.onClick}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
addDocument={this.props.addDocument}
removeDocument={this.props.removeDocument}
moveDocument={this.props.moveDocument}
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index dd254ae93..58f4df823 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -125,7 +125,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
ContainingCollectionView={this as any} // bcz: hack! need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked. currently, the up selector allows only a containing collection to be selected
ContainingCollectionDoc={undefined}
fitToBox={true}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
LayoutTemplateString={layoutTemplate}
LayoutTemplate={this.layoutTemplateDoc}
rootSelected={this.props.isSelected}
@@ -154,7 +154,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
ContainingCollectionView={this as any} // bcz: hack! need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked. currently, the up selector allows only a containing collection to be selected
ContainingCollectionDoc={undefined}
fitToBox={true}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
ignoreAutoHeight={true}
LayoutTemplateString={layoutTemplate}
LayoutTemplate={this.layoutTemplateDoc}
@@ -184,7 +184,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
onContextMenu={this.specificContextMenu}
onPointerDown={this.onPointerDown} onClick={this.onClick}
style={{
- background: this.props.backgroundColor?.(containedDoc, this.props.renderDepth),
+ background: this.props.styleProvider?.(containedDoc, this.props.renderDepth, "backgroundColor", this.props.layerProvider),
border: `#00000021 solid ${this.xPad}px`,
borderTop: `#0000005e solid ${this.yPad}px`,
borderBottom: `#0000005e solid ${this.yPad}px`,
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index e8c3662aa..98a1b026d 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -139,7 +139,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
const list = {
- ...OmitKeys(this.props, ['parentActive', 'NativeWidth', 'NativeHeight'], "", (obj: any) => obj.active = this.props.parentActive).omit,
+ ...OmitKeys(this.props, ['NativeWidth', 'NativeHeight'], "", (obj: any) => obj.active = this.props.parentActive).omit,
RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc,
Document: this.layoutDoc,
DataDoc: this.dataDoc,
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index c31172e22..ad72250b6 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -54,6 +54,12 @@
}
}
+ .documentView-contentsView {
+ border-radius: inherit;
+ width: 100%;
+ height: 100%;
+ }
+
.documentView-anchorCont {
position: absolute;
top: 0;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 77f63b457..62a2b5bc5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,7 +1,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, StrListCast } from "../../../fields/Doc";
import { Document } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
@@ -12,7 +12,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { emptyFunction, OmitKeys, returnOne, returnTransparent, returnVal, Utils } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnOne, returnTransparent, returnVal, Utils, returnFalse, returnTrue } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
@@ -41,6 +41,7 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup';
import { RadialMenu } from './RadialMenu';
import { TaskCompletionBox } from './TaskCompletedBox';
import React = require("react");
+import { List } from '../../../fields/List';
export type DocAfterFocusFunc = (notFocused: boolean) => boolean;
export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void;
@@ -49,6 +50,7 @@ export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView>;
ContainingCollectionDoc: Opt<Doc>;
docFilters: () => string[];
+ contentsActive?: (setActive: () => boolean) => void;
docRangeFilters: () => string[];
searchFilterDocs: () => Doc[];
FreezeDimensions?: boolean;
@@ -56,6 +58,7 @@ export interface DocumentViewProps {
NativeHeight?: () => number;
Document: Doc;
DataDoc?: Doc;
+ layerProvider?: (doc: Doc, assign?: boolean) => boolean;
getView?: (view: DocumentView) => any;
LayoutTemplateString?: string;
LayoutTemplate?: () => Opt<Doc>;
@@ -89,9 +92,9 @@ export interface DocumentViewProps {
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
pinToPres: (document: Doc) => void;
- backgroundHalo?: () => boolean;
- backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
- forcedBackgroundColor?: (doc: Doc) => string | undefined;
+ backgroundHalo?: (doc: Doc) => boolean;
+ styleProvider?: (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
+ forceHideLinkButton?: () => boolean;
opacity?: () => number | undefined;
ChromeHeight?: () => number;
dontRegisterView?: boolean;
@@ -118,7 +121,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
- private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
+ private get active() { return this.isSelected(true) || this.props.parentActive(true); }
public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
public get ContentDiv() { return this._mainCont.current; }
public get LayoutFieldKey() { return this.props.layoutKey || Doc.LayoutFieldKey(this.layoutDoc); }
@@ -312,7 +315,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
let stopPropagate = true;
let preventDefault = true;
- !this.props.Document._isBackground && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+ !Cast(this.props.Document.layers, listSpec("string"), []).includes("background") && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
if (this._doubleTap && ((this.props.renderDepth && this.props.Document.type !== DocumentType.FONTICON) || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
if (this._timeout) {
clearTimeout(this._timeout);
@@ -742,19 +745,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.Document.isLinkButton = true;
}
- @undoBatch
- @action
- toggleBackground = () => {
- this.Document._isBackground = (this.Document._isBackground ? undefined : true);
- this.Document._overflow = this.Document._isBackground ? "visible" : undefined;
- if (this.Document._isBackground) {
- this.props.bringToFront(this.props.Document, 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);
- }
- }
@action
onCopy = () => {
@@ -811,8 +801,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const zorders = cm.findByDescription("ZOrder...");
const zorderItems: ContextMenuProps[] = zorders && "subitems" in zorders ? zorders.subitems : [];
- zorderItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" });
- zorderItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" });
+ zorderItems.push({ description: "Bring to Front", event: () => SelectionManager.SelectedDocuments().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: "expand-arrows-alt" });
+ zorderItems.push({ description: "Send to Back", event: () => SelectionManager.SelectedDocuments().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: this.toggleRaiseWhenDragged, icon: "expand-arrows-alt" });
!zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
@@ -918,11 +908,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
@computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, -20]; }
+ @observable contentsActive: () => boolean = returnFalse;
+ @action setContentsActive = (setActive: () => boolean) => this.contentsActive = setActive;
+ parentActive = (outsideReaction: boolean) => this.props.layerProvider?.(this.layoutDoc) === false ? this.props.parentActive(outsideReaction) : false;
@computed get contents() {
TraceMobx();
- return (<div className="documentView-contentsView" style={{ pointerEvents: this.props.contentsPointerEvents as any, borderRadius: "inherit", width: "100%", height: "100%" }}>
+ return (<div className="documentView-contentsView" style={{ pointerEvents: this.props.contentsPointerEvents as any }}>
<DocumentContentsView key={1}
docFilters={this.props.docFilters}
+ contentsActive={this.setContentsActive}
docRangeFilters={this.props.docRangeFilters}
searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={this.props.ContainingCollectionView}
@@ -931,6 +925,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
NativeHeight={this.NativeHeight}
Document={this.props.Document}
DataDoc={this.props.DataDoc}
+ layerProvider={this.props.layerProvider}
LayoutTemplateString={this.props.LayoutTemplateString}
LayoutTemplate={this.props.LayoutTemplate}
makeLink={this.makeLink}
@@ -948,12 +943,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
PanelHeight={this.props.PanelHeight}
ignoreAutoHeight={this.props.ignoreAutoHeight}
focus={this.props.focus}
- parentActive={this.props.parentActive}
+ parentActive={this.parentActive}
whenActiveChanged={this.props.whenActiveChanged}
bringToFront={this.props.bringToFront}
addDocTab={this.props.addDocTab}
pinToPres={this.props.pinToPres}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
ContentScaling={this.childScaling}
ChromeHeight={this.chromeHeight}
isSelected={this.isSelected}
@@ -963,7 +958,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
layoutKey={this.finalLayoutKey} />
{this.layoutDoc.hideAllLinks ? (null) : this.allAnchors}
{/* {this.allAnchors} */}
- {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || (!this.isSelected() && (this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton)) || this.props.dontRegisterView ? (null) :
+ {this.props.forceHideLinkButton?.() || (!this.isSelected() && (this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton)) || this.props.dontRegisterView ? (null) :
<DocumentLinksButton View={this} links={this.allLinks} Offset={this.linkOffset} />}
</div>
);
@@ -1008,7 +1003,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
PanelHeight={this.anchorPanelHeight}
ContentScaling={returnOne}
dontRegisterView={false}
- forcedBackgroundColor={returnTransparent}
+ forceHideLinkButton={returnTrue}
+ styleProvider={(doc: Opt<Doc>, renderDepth: number, property: string) => property === "backgroundColor" ? "transparent" : undefined}
removeDocument={this.hideLinkAnchor}
pointerEvents={"none"}
LayoutTemplate={undefined}
@@ -1068,10 +1064,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
{captionView}
</div>;
}
- @computed get ignorePointerEvents() {
- return this.props.pointerEvents === "none" ||
- (this.Document._isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
- (this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None);
+ @computed get pointerEvents() {
+ if (this.props.pointerEvents === "none") return "none";
+ return this.props.styleProvider?.(this.Document, this.props.renderDepth, this.isSelected() ? "pointerEvents:selected" : "pointerEvents", this.props.layerProvider);
}
@undoBatch
@action
@@ -1091,22 +1086,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}), 400);
});
- renderLock() {
- return (this.Document._isBackground !== undefined || this.isSelected(false)) &&
- ((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) &&
- this.props.renderDepth > 0 && !this.props.treeViewDoc ?
- <div className="documentView-lock" onClick={this.toggleBackground}>
- <FontAwesomeIcon icon={this.Document._isBackground ? "unlock" : "lock"} style={{ color: this.Document._isBackground ? "red" : undefined }} size="lg" />
- </div>
- : (null);
- }
render() {
TraceMobx();
if (!(this.props.Document instanceof Doc)) return (null);
if (GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate) return (null);
- if (this.props.Document.hidden) return (null);
- const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document, this.props.renderDepth);
+ if (this.props.styleProvider?.(this.layoutDoc, this.props.renderDepth, "hidden", this.props.layerProvider)) return null;
+ const backgroundColor = this.props.styleProvider?.(this.layoutDoc, this.props.renderDepth, "backgroundColor", this.props.layerProvider);
const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor;
@@ -1120,7 +1106,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear && this.props.Document.type !== DocumentType.INK;
highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]"; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way
const topmost = this.topMost ? "-topmost" : "";
- return <div className={`documentView-node${topmost}`}
+ return this.props.styleProvider?.(this.rootDoc, this.props.renderDepth, "docContents", this.props.layerProvider) ?? <div className={`documentView-node${topmost}`}
id={this.props.Document[Id]}
ref={this._mainCont} onKeyDown={this.onKeyDown}
onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
@@ -1142,12 +1128,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
transformOrigin: this._animateScalingTo ? "center center" : undefined,
transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform 0.5s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
- pointerEvents: this.ignorePointerEvents ? "none" : undefined,
+ pointerEvents: this.pointerEvents as any,
color: StrCast(this.layoutDoc.color, "inherit"),
outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
border: highlighting && borderRounding && highlightStyles[fullDegree] === "dashed" ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
boxShadow: highlighting && borderRounding && highlightStyles[fullDegree] !== "dashed" ? `0 0 0 ${localScale}px ${highlightColors[fullDegree]}` :
- this.Document.isLinkButton && !this.props.dontRegisterView && this.props.forcedBackgroundColor?.(this.Document) !== "transparent" ?
+ this.Document.isLinkButton && !this.props.dontRegisterView && !this.props.forceHideLinkButton?.() ?
StrCast(this.layoutDoc._linkButtonShadow, "lightblue 0em 0em 1em") :
this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" :
undefined,
@@ -1158,10 +1144,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}}>
{this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <>
{this.innards}
- <div className="documentView-contentBlocker" />
+ <div className="documentView-conentBlocker" />
</> :
this.innards}
- {this.renderLock()}
+ {!this.props.treeViewDoc && this.props.styleProvider?.(this.rootDoc, this.props.renderDepth, this.isSelected() ? "decorations:selected" : "decorations", this.props.layerProvider) || (null)}
</div>;
}
}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 79947c023..9df3e6b81 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -26,6 +26,8 @@ export interface FieldViewProps {
Document: Doc;
DataDoc?: Doc;
LibraryPath: Doc[];
+ layerProvider?: (doc: Doc, assign?: boolean) => boolean;
+ contentsActive?: (setActive: () => boolean) => void;
onClick?: () => ScriptField;
dropAction: dropActionType;
backgroundHalo?: () => boolean;
@@ -41,9 +43,10 @@ export interface FieldViewProps {
pinToPres: (document: Doc) => void;
removeDocument?: (document: Doc | Doc[]) => boolean;
moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
- backgroundColor?: (document: Opt<Doc>, renderDepth: number) => string | undefined;
+ styleProvider?: (document: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
ScreenToLocalTransform: () => Transform;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
+ parentActive: (outsideReaction: boolean) => boolean;
active: (outsideReaction?: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
LayoutTemplateString?: string;
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 6f01e5916..ab401213c 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -161,7 +161,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
}
render() {
- const facetCollection = this.props.Document.proto as Doc;
+ const facetCollection = this.props.Document as Doc;
const flyout = <div className="filterBox-flyout" style={{ width: `100%`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
{this._allFacets.map(facet => <label className="filterBox-flyout-facet" key={`${facet}`} onClick={e => this.facetClick(facet)}>
<input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey]).some(d => d.title === facet)} />
@@ -204,6 +204,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
select={returnFalse}
bringToFront={emptyFunction}
active={this.props.active}
+ parentActive={returnFalse}
whenActiveChanged={returnFalse}
treeViewHideTitle={true}
ContentScaling={returnOne}
@@ -213,7 +214,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
ignoreFields={this.ignoreFields}
annotationsKey={""}
dontRegisterView={true}
- backgroundColor={this.filterBackground}
+ styleProvider={this.filterBackground}
moveDocument={returnFalse}
removeDocument={returnFalse}
addDocument={returnFalse} />
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 276c66bb1..dfbd89c88 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -61,7 +61,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
render() {
const label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
const color = StrCast(this.layoutDoc.color, this._foregroundColor);
- const backgroundColor = StrCast(this.layoutDoc._backgroundColor, StrCast(this.rootDoc.backgroundColor, this.props.backgroundColor?.(this.rootDoc, this.props.renderDepth)));
+ const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props.renderDepth, "backgroundColor", this.props.layerProvider);
const shape = StrCast(this.layoutDoc.iconShape, label ? "round" : "circle");
const icon = StrCast(this.dataDoc.icon, "user") as any;
const presSize = shape === 'round' ? 25 : 30;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 88dc3b241..1c1a13061 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -161,7 +161,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
if (field) {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
- funcs.push({ description: "Make Background", event: () => { this.layoutDoc._isBackground = true; this.props.bringToFront?.(this.rootDoc); }, icon: "expand-arrows-alt" });
if (!Doc.UserDoc().noviceMode) {
funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
@@ -411,7 +410,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
transform: this.props.PanelWidth() ? undefined : `scale(${this.contentScaling})`,
width: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`,
height: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`,
- pointerEvents: this.layoutDoc._isBackground ? "none" : undefined,
+ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.contentScaling}px`
}} >
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index c5ff42a1a..4ac932c24 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -70,7 +70,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean): boolean {
const { script, type, onDelegate } = kvpScript;
//const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates
- const target = forceOnDelegate || onDelegate ? doc : Doc.GetProto(doc);
+ const target = forceOnDelegate || onDelegate ? doc : doc.proto || doc;
let field: Field;
if (type === "computed") {
field = new ComputedField(script);
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 2e2319447..5e1f8fcea 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -68,6 +68,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
dropAction: "alias",
bringToFront: emptyFunction,
renderDepth: 1,
+ parentActive: returnFalse,
active: returnFalse,
whenActiveChanged: emptyFunction,
ScreenToLocalTransform: Transform.Identity,
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index ec8c43ab4..7ce9abf27 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -92,7 +92,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
openLinkTargetOnRight = (e: React.MouseEvent) => {
const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
alias.isLinkButton = undefined;
- alias._isBackground = undefined;
+ alias.layers = undefined;
alias.layoutKey = "layout";
this.props.addDocTab(alias, "add:right");
}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 64ae1051b..913c07eb2 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -17,7 +17,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
render() {
return <div className={`linkBox-container${this.active() ? "-interactive" : ""}`}
- style={{ background: this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) }} >
+ style={{ background: this.props.styleProvider?.(this.props.Document, this.props.renderDepth, "backgroundColor", this.props.layerProvider) }} >
<CollectionTreeView {...this.props}
ChromeHeight={returnZero}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index d47942bd9..dfdca7ebb 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -16,7 +16,7 @@ interface Props {
linkDoc?: Doc;
linkSrc?: Doc;
href?: string;
- backgroundColor: (doc: Opt<Doc>, renderDepth: number) => string;
+ styleProvider?: (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
addDocTab: (document: Doc, where: string) => boolean;
location: number[];
}
@@ -112,7 +112,7 @@ export class LinkDocPreview extends React.Component<Props> {
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
ContentScaling={returnOne}
- backgroundColor={this.props.backgroundColor} />;
+ styleProvider={this.props.styleProvider} />;
}
render() {
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 683cb938a..455ee87e1 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -640,7 +640,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
panelHeight = () => this.props.PanelHeight() - 40;
- active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground) &&
+ active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && this.props.layerProvider?.(this.layoutDoc) !== false) &&
(this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
/**
diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss
index a937364a8..8d76f2b1d 100644
--- a/src/client/views/nodes/ScriptingBox.scss
+++ b/src/client/views/nodes/ScriptingBox.scss
@@ -213,12 +213,12 @@
.scriptingBox-button {
font-size: xx-small;
- width: 50%;
+ width: 33%;
resize: auto;
}
- .scriptingBox-button-third {
- width: 33%;
+ .scriptingBox-button-finish {
+ width: 25%;
}
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 1a5edc1d9..f1f2cd7d3 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -628,13 +628,13 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, Sc
// toolbar (with compile and apply buttons) for scripting UI
renderScriptingTools() {
- const buttonStyle = "scriptingBox-button" + (this.rootDoc.layoutKey === "layout_onClick" ? "third" : "");
+ const buttonStyle = "scriptingBox-button" + (StrCast(this.rootDoc.layoutKey).startsWith("layout_on") ? "-finish" : "");
return <div className="scriptingBox-toolbar">
- <button className={buttonStyle} style={{ width: "33%" }} onPointerDown={e => { this.onCompile(); e.stopPropagation(); }}>Compile</button>
- <button className={buttonStyle} style={{ width: "33%" }} onPointerDown={e => { this.onApply(); e.stopPropagation(); }}>Apply</button>
- <button className={buttonStyle} style={{ width: "33%" }} onPointerDown={e => { this.onSave(); e.stopPropagation(); }}>Save</button>
+ <button className={buttonStyle} onPointerDown={e => { this.onCompile(); e.stopPropagation(); }}>Compile</button>
+ <button className={buttonStyle} onPointerDown={e => { this.onApply(); e.stopPropagation(); }}>Apply</button>
+ <button className={buttonStyle} onPointerDown={e => { this.onSave(); e.stopPropagation(); }}>Save</button>
- {this.rootDoc.layoutKey !== "layout_onClick" ? (null) :
+ {!StrCast(this.rootDoc.layoutKey).startsWith("layout_on") ? (null) : // onClick, onChecked, etc need a Finish button to return to their default layout
<button className={buttonStyle} onPointerDown={e => { this.onFinish(); e.stopPropagation(); }}>Finish</button>}
</div>;
}
@@ -662,11 +662,11 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, Sc
// toolbar (with edit and run buttons and error message) for params UI
renderTools(toolSet: string, func: () => void) {
- const buttonStyle = "scriptingBox-button" + (this.rootDoc.layoutKey === "layout_onClick" ? "third" : "");
+ const buttonStyle = "scriptingBox-button" + (StrCast(this.rootDoc.layoutKey).startsWith("layout_on") ? "-finish" : "");
return <div className="scriptingBox-toolbar">
<button className={buttonStyle} onPointerDown={e => { this.onEdit(); e.stopPropagation(); }}>Edit</button>
<button className={buttonStyle} onPointerDown={e => { func(); e.stopPropagation(); }}>{toolSet}</button>
- {this.rootDoc.layoutKey !== "layout_onClick" ? (null) :
+ {!StrCast(this.rootDoc.layoutKey).startsWith("layout_on") ? (null) :
<button className={buttonStyle} onPointerDown={e => { this.onFinish(); e.stopPropagation(); }}>Finish</button>}
</div>;
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index bc69a3954..8ce76a347 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -393,7 +393,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
transform: this.props.PanelWidth() ? undefined : `scale(${this.contentScaling})`,
width: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`,
height: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`,
- pointerEvents: this.layoutDoc._isBackground ? "none" : undefined,
+ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.contentScaling}px`
}} >
<div className="videoBox-viewer" >
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 80e2d3ce2..9a1ae336f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -193,7 +193,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
this.layoutDoc._height = this.layoutDoc[WidthSym]() / youtubeaspect;
}
} // else it's an HTMLfield
- } else if (field?.url) {
+ } else if (field?.url && !this.dataDoc.text) {
const result = await WebRequest.get(Utils.CorsProxy(field.url.href));
if (result) {
this.dataDoc.text = htmlToText.fromString(result.content);
@@ -442,7 +442,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded}
// the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
- sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
+ // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
+ sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin"} />;
} else {
view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />;
}
@@ -461,7 +462,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
{view}
</div>
{!frozen ? (null) :
- <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc._isBackground ? undefined : "all" }}
+ <div className="webBox-overlay" style={{ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? undefined : "all" }}
onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
<div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
<div className="indicator" ref={this._iframeIndicatorRef}></div>
@@ -663,7 +664,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
transform: `scale(${scaling})`,
width: `${100 / scaling}% `,
height: `${100 / scaling}% `,
- pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
+ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined
}}
onContextMenu={this.specificContextMenu}>
<base target="_blank" />
@@ -671,7 +672,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
<div className={"webBox-outerContent"} ref={this._outerRef}
style={{
width: `${Math.max(100, 100 / scaling)}% `,
- pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc._isBackground ? "all" : "none"
+ pointerEvents: this.layoutDoc.isAnnotating && this.props.layerProvider?.(this.layoutDoc) !== false ? "all" : "none"
}}
onWheel={e => {
const target = this._outerRef.current;
@@ -693,7 +694,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
>
<div className={"webBox-innerContent"} style={{
height: NumCast(this.scrollHeight, 50),
- pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
+ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined
}}>
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
PanelHeight={this.props.PanelHeight}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 7cd92b8b9..67bfd435b 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -240,7 +240,7 @@ export class DashDocView extends React.Component<IDashDocView> {
PanelWidth={finalLayout[WidthSym]}
PanelHeight={finalLayout[HeightSym]}
focus={this.outerFocus}
- backgroundColor={returnEmptyString}
+ styleProvider={returnEmptyString}
parentActive={returnFalse}
whenActiveChanged={returnFalse}
bringToFront={emptyFunction}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index b75cc230f..b04f60500 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -113,6 +113,9 @@
.ProseMirror {
padding:10px;
}
+}
+.formattedTextBox-outer-selected {
+
.ProseMirror:hover {
background: unset;
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fe38939c5..5017d21f1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -810,7 +810,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
}
}
+
+ IsActive = () => {
+ return this.active();//this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
+ }
+
componentDidMount() {
+ this.props.contentsActive?.(this.IsActive);
this._cachedLinks = DocListCast(this.Document.links);
this._disposers.sidebarheight = reaction(
() => ({ annoHeight: NumCast(this.rootDoc[this.annotationKey + "-height"]), textHeight: NumCast(this.rootDoc[this.fieldKey + "-height"]) }),
@@ -1616,13 +1622,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const active = this.active();
const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
- const interactive = (Doc.GetSelectedTool() === InkTool.None || SnappingManager.GetIsDragging()) && !this.layoutDoc._isBackground;
+ const interactive = (Doc.GetSelectedTool() === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false);
if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(() => FormattedTextBoxComment.Hide());
const minimal = this.props.ignoreAutoHeight;
const margins = NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0);
const selPad = Math.min(margins, 10);
const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
- const selclass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
+ const selPaddingClass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
return (
<div className="formattedTextBox-cont" ref={this._boxRef}
style={{
@@ -1665,12 +1671,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
})}
onDoubleClick={this.onDoubleClick}
>
- <div className={`formattedTextBox-outer${selclass}`} ref={this._scrollRef}
- style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !active && !SnappingManager.GetIsDragging() ? "none" : undefined }}
+ <div className={`formattedTextBox-outer${selected ? "-selected" : ""}`} ref={this._scrollRef}
+ style={{
+ width: `calc(100% - ${this.sidebarWidthPercent})`,
+ pointerEvents: !active && !SnappingManager.GetIsDragging() ? "none" : undefined,
+ overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
+ }}
onScroll={this.onscrolled} onDrop={this.ondrop} >
- <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
+ <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selPaddingClass}`} ref={this.createDropTarget}
style={{
- overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${padding}px`,
pointerEvents: !active && !SnappingManager.GetIsDragging() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
}}
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 40c1d1cac..b5d984aac 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -148,7 +148,7 @@ export class DashDocView {
PanelWidth={finalLayout[WidthSym]}
PanelHeight={finalLayout[HeightSym]}
focus={this.outerFocus}
- backgroundColor={returnEmptyString}
+ styleProvider={returnEmptyString}
parentActive={returnFalse}
whenActiveChanged={returnFalse}
bringToFront={emptyFunction}
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index cca7ea013..ea239e4d3 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -41,7 +41,7 @@ export const marks: { [index: string]: MarkSpec } = {
return node.attrs.docref && node.attrs.title ?
["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, href: node.attrs.allLinks[0].href, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
//node.attrs.allLinks.length === 1 ?
- ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, "data-targethrefs": targethrefs, title: `${node.attrs.title}`, href: node.attrs.allLinks[0].href, style: `text-decoration: ${linkids === " " ? "underline" : undefined}` }, 0];
+ ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, "data-targethrefs": targethrefs, title: `${node.attrs.title}`, href: node.attrs.allLinks[0]?.href, style: `text-decoration: ${linkids === " " ? "underline" : undefined}` }, 0];
// ["div", { class: "prosemirror-anchor" },
// ["span", { class: "prosemirror-linkBtn" },
// ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 7b4afeb69..899897a6d 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -1,12 +1,12 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, reaction, runInAction, observable, trace } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DataSym, DocListCast } from "../../../fields/Doc";
+import { Doc, DataSym } from "../../../fields/Doc";
import { documentSchema } from '../../../fields/documentSchemas';
import { Id } from "../../../fields/FieldSymbols";
-import { createSchema, makeInterface, listSpec } from '../../../fields/Schema';
-import { Cast, NumCast, BoolCast, ScriptCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, returnZero, numberRange, setupMoveUpEvents } from "../../../Utils";
+import { createSchema, makeInterface } from '../../../fields/Schema';
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, setupMoveUpEvents } from "../../../Utils";
import { Transform } from "../../util/Transform";
import { ViewBoxBaseComponent } from '../DocComponent';
import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
@@ -88,7 +88,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
LibraryPath={emptyPath}
fitToBox={true}
- backgroundColor={this.props.backgroundColor}
+ styleProvider={this.props.styleProvider}
rootSelected={returnTrue}
addDocument={returnFalse}
removeDocument={returnFalse}
@@ -312,7 +312,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
<div className="presItem-number">
{`${this.indexInPres + 1}.`}
</div>}
- {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`} style={{ backgroundColor: this.props.backgroundColor?.(this.layoutDoc, this.props.renderDepth) }}>
+ {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`} style={{ backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props.renderDepth, "backgroundColor", this.props.layerProvider) }}>
<div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 185) : toolbarWidth - 95, cursor: isSelected ? 'text' : 'grab' }}>
<EditableView
ref={this._titleRef}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 4c3c45d92..7b01d60e5 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -76,6 +76,7 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> { return Cast(field, Doc); }
+export function StrListCast(field: FieldResult) { return Cast(field, listSpec("string"), []) as string[]; }
export function DocListCast(field: FieldResult) { return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; }
export function DocListCastOrNull(field: FieldResult) { return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined; }
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index e0404d9d3..979238767 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -102,7 +102,7 @@ export const documentSchema = createSchema({
linkDisplay: "boolean", // whether a link connection should be shown between link anchor endpoints.
isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations
isLinkButton: "boolean", // whether document functions as a link follow button to follow the first link on the document when clicked
- _isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee)
+ layers: listSpec("string"), // which layers the document is part of
lockedPosition: "boolean", // whether the document can be moved (dragged)
_lockedTransform: "boolean",// whether a freeformview can pan/zoom
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index ebc8bc8a7..604c9a5ab 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -97,7 +97,7 @@ export class AudioUpload extends React.Component {
PanelHeight={() => 400}
renderDepth={0}
focus={emptyFunction}
- backgroundColor={() => "rgba(0,0,0,0)"}
+ styleProvider={() => "rgba(0,0,0,0)"}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index a42d85b56..ae88a2eaf 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -215,7 +215,7 @@ export class MobileInterface extends React.Component {
PanelHeight={this.returnHeight}
renderDepth={0}
focus={emptyFunction}
- backgroundColor={this.whitebackground}
+ styleProvider={this.whitebackground}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}