aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts4
-rw-r--r--src/client/views/DocComponent.tsx6
-rw-r--r--src/client/views/DocumentDecorations.tsx20
-rw-r--r--src/client/views/InkingStroke.tsx4
-rw-r--r--src/client/views/MainView.tsx73
-rw-r--r--src/client/views/TemplateMenu.tsx1
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx1
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx2
-rw-r--r--src/client/views/collections/CollectionView.tsx17
-rw-r--r--src/client/views/collections/TabDocView.tsx57
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx29
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx12
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx24
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx2
-rw-r--r--src/client/views/nodes/DocHolderBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx45
-rw-r--r--src/client/views/nodes/FieldView.tsx4
-rw-r--r--src/client/views/nodes/FilterBox.tsx1
-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/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/PresBox.tsx2
-rw-r--r--src/client/views/nodes/VideoBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx8
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx2
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx10
-rw-r--r--src/fields/documentSchemas.ts2
32 files changed, 228 insertions, 117 deletions
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/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/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 5fd9d5fe4..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, "", this.props.Document._isBackground ? "none" : "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,
@@ -191,7 +191,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
return (
<svg className="inkingStroke"
style={{
- pointerEvents: this.props.Document.isInkMask && !this.props.Document._isBackground ? "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..6947dd3cd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -60,6 +60,7 @@ import { TraceMobx } from '../../fields/util';
import { SelectionManager } from '../util/SelectionManager';
import { UndoManager } from '../util/UndoManager';
const _global = (window /* browser */ || global /* node */) as any;
+import Color = require('color');
@observer
export class MainView extends React.Component {
@@ -75,6 +76,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,33 +230,44 @@ 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";
+ defaultBackgroundColors = (doc: Opt<Doc>, renderDepth: number, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => {
+ let docColor = StrCast(doc?._backgroundColor, StrCast(doc?.backgroundColor));
+ if (!docColor) {
+ if (this.darkScheme) {
+ switch (doc?.type) {
+ case DocumentType.PRESELEMENT: docColor = "dimgrey"; break;
+ case DocumentType.PRES: docColor = "#3e3e3e"; break;
+ case DocumentType.FONTICON: docColor = "black"; break;
+ case DocumentType.RTF || DocumentType.LABEL || DocumentType.BUTTON: docColor = "#2d2d2d"; break;
+ case DocumentType.LINK:
+ case DocumentType.COL:
+ docColor = Doc.IsSystem(doc) ? "rgb(62,62,62)" : 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 = "black"; break;
+ }
+ } else {
+ switch (doc?.type) {
+ case DocumentType.PRESELEMENT: docColor = ""; break;
+ case DocumentType.FONTICON: docColor = "black"; break;
+ case DocumentType.RTF: docColor = "#f1efeb"; break;
+ case DocumentType.BUTTON:
+ case DocumentType.LABEL: docColor = "lightgray"; break;
+ case DocumentType.LINK:
+ case DocumentType.COL:
+ docColor = Doc.IsSystem(doc) ? "lightgrey" :
+ StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground :
+ Doc.UserDoc().activeCollectionBackground);
+ break;
+ //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "lightgray";
+ default: docColor = "white"; break;
+ }
}
}
+ if (!doc || layerProvider?.(doc) === false) {
+ return Color(docColor).fade(0.5).toString();
+ }
+ return docColor;
}
@computed get mainDocView() {
@@ -310,8 +323,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");
@@ -480,6 +493,7 @@ export class MainView extends React.Component {
fieldKey={"data"}
dropAction={"alias"}
annotationsKey={""}
+ parentActive={returnFalse}
backgroundColor={this.defaultBackgroundColors}
rootSelected={returnTrue}
bringToFront={emptyFunction}
@@ -564,6 +578,7 @@ export class MainView extends React.Component {
PanelHeight={this.getPHeight}
renderDepth={0}
focus={emptyFunction}
+ parentActive={returnFalse}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
@@ -588,6 +603,7 @@ export class MainView extends React.Component {
select={returnFalse}
rootSelected={returnFalse}
renderDepth={0}
+ parentActive={returnFalse}
addDocTab={returnFalse}
pinToPres={returnFalse}
ScreenToLocalTransform={Transform.Identity}
@@ -613,7 +629,7 @@ export class MainView extends React.Component {
<SettingsManager />
<GroupManager />
<GoogleAuthenticationManager />
- <DocumentDecorations />
+ <DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
{this.search}
<CollectionMenu />
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
@@ -659,6 +675,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/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..8c58a5679 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.backgroundColor?.(this.props.Document, this.props.renderDepth, this.props.layerProvider)),
color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)),
borderRadius: StrCast(this.layoutDoc._captionBorderRounding),
}}>
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 be31ca6e5..56c6978f1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -216,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 = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth, 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..4d620bc61 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 :
+ const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || Cast(this.props.Document.layers, listSpec("string"), []).includes("background") || this.collectionViewType === CollectionViewType.Linear ? undefined :
`${CurrentUserUtils.ActiveDashboard?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
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 6079b2eb6..76efe7ec9 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -29,6 +29,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
import { CollectionViewType } from './CollectionView';
import "./TabDocView.scss";
import React = require("react");
+import { List } from '../../../fields/List';
const _global = (window /* browser */ || global /* node */) as any;
interface TabDocViewProps {
@@ -66,6 +67,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 +109,13 @@ 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"));
+ (selected) => {
+ 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;
@@ -184,8 +211,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 });
}
@@ -303,6 +330,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}
@@ -353,6 +381,26 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
setView = action((view: DocumentView) => this._view = view);
active = () => this._isActive;
+
+ 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;
+ }
+ }
@computed get docView() {
TraceMobx();
return !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
@@ -363,6 +411,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}
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 19dc97399..77c29d6ef 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -73,6 +73,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)
};
@@ -235,7 +236,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);
@@ -274,7 +275,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);
@@ -390,8 +390,8 @@ 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, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => {
+ let clusterColor = this.props.backgroundColor?.(doc, this.props.renderDepth + 1, layerProvider);
const cluster = NumCast(doc?.cluster);
if (this.Document._useClusters) {
if (this._clusterSets.length <= cluster) {
@@ -402,8 +402,8 @@ 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));
}
}
return clusterColor;
@@ -861,7 +861,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;
@@ -976,11 +976,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;
+ 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,
@@ -988,6 +988,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,
@@ -1014,7 +1015,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
focus: this.focusDocument,
backgroundColor: this.getClusterColor,
backgroundHalo: this.backgroundHalo,
- parentActive: this.parentActive,
bringToFront: this.bringToFront,
addDocTab: this.addDocTab,
};
@@ -1122,7 +1122,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) {
@@ -1161,6 +1160,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}
@@ -1380,7 +1380,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
@@ -1491,6 +1491,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)}
@@ -1515,7 +1516,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();
@@ -1584,6 +1585,7 @@ interface CollectionFreeFormViewPannableContentsProps {
presPaths?: boolean;
progressivize?: boolean;
presPinView?: boolean;
+ isAnnotationOverlay: boolean | undefined;
}
@observer
@@ -1729,6 +1731,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 796f02deb..8ed198b4a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -357,7 +357,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";
@@ -365,8 +365,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;
@@ -447,7 +447,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);
@@ -555,7 +555,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();
@@ -693,7 +693,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/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c87239ee9..e6cb160dd 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;
@@ -206,6 +210,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
ScreenToLocalTransform={this.getTransform}
backgroundColor={this.props.backgroundColor}
opacity={this.opacity}
+ layerProvider={this.props.layerProvider}
NativeHeight={this.NativeHeight}
NativeWidth={this.NativeWidth}
PanelWidth={this.panelWidth}
@@ -244,17 +249,25 @@ 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";
+ const layer = this.props.layerProvider?.(this.Document);
+ if (layer === false && !this._contentView?.docView?.isSelected() && !SnappingManager.GetIsDragging()) return "none";
+ if (this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None) return "none";
+ if (layer === true) return "all";
+ return this.props.pointerEvents;
+ }
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 = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document, this.props.renderDepth, 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.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth, 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 +279,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 +293,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 d5b91f4a7..d963369f8 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -45,7 +45,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
}
private getTransform = () => this.props.ScreenToLocalTransform().
- translate(this.props.dontCenter?.includes("x") ? 0 : -this.centeringOffset, this.props.dontCenter?.includes("y") ? 0 : -this.centeringYOffset);
+ 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 Math.abs(this.centeringOffset) < 0.001 && this.nativeHeight ? (this.props.PanelHeight() - this.nativeHeight * this.nativeScaling) / 2 : 0; }
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index dd254ae93..b5e0df659 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -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.backgroundColor?.(containedDoc, this.props.renderDepth, 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.tsx b/src/client/views/nodes/DocumentView.tsx
index f6262310f..9b26094b3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -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;
@@ -57,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>;
@@ -91,7 +93,7 @@ export interface DocumentViewProps {
addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
pinToPres: (document: Doc) => void;
backgroundHalo?: () => boolean;
- backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
+ backgroundColor?: (doc: Opt<Doc>, renderDepth: number, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined;
forcedBackgroundColor?: (doc: Doc) => string | undefined;
opacity?: () => number | undefined;
ChromeHeight?: () => number;
@@ -313,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);
@@ -746,9 +748,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
@action
toggleBackground = () => {
- this.Document._isBackground = (this.Document._isBackground ? undefined : true);
- this.Document._overflow = this.Document._isBackground ? "visible" : undefined;
- if (this.Document._isBackground) {
+ const layers = Cast(this.Document.layers, listSpec("string"), []);
+ if (!layers.includes("background")) {
+ if (!layers.length) this.Document.layers = new List<string>(["background"]);
+ else layers.push("background");
+ }
+ else layers.splice(layers.indexOf("background"), 1);
+ this.Document._overflow = !layers.includes("background") ? "visible" : undefined;
+ if (!layers.includes("background")) {
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]();
@@ -920,7 +927,8 @@ 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; }
+ @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 }}>
@@ -935,6 +943,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}
@@ -952,7 +961,7 @@ 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}
@@ -1072,10 +1081,13 @@ 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";
+ const layer = this.props.layerProvider?.(this.Document);
+ if (layer === false && !this.isSelected() && !SnappingManager.GetIsDragging()) return "none";
+ if (this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None) return "none";
+ if (layer === true) return "all";
+ return undefined;
}
@undoBatch
@action
@@ -1096,11 +1108,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
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.Document.type === DocumentType.INK) &&
+ const isBackground = Cast(this.Document.layers, listSpec("string"), []).includes("background");
+ return (isBackground || this.isSelected(false)) &&
+ ((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.RTF || this.Document.type === DocumentType.IMG || this.Document.type === DocumentType.INK) &&
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" />
+ <FontAwesomeIcon icon={isBackground ? "unlock" : "lock"} style={{ color: isBackground ? "red" : undefined }} size="lg" />
</div>
: (null);
}
@@ -1110,7 +1123,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
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);
+ const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || this.props.backgroundColor?.(this.layoutDoc, this.props.renderDepth, 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;
@@ -1146,7 +1159,7 @@ 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,
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,
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d7ff051cf..b8f2d5d6f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -26,6 +26,7 @@ export interface FieldViewProps {
Document: Doc;
DataDoc?: Doc;
LibraryPath: Doc[];
+ layerProvider?: (doc: Doc, assign?: boolean) => boolean;
contentsActive?: (setActive: () => boolean) => void;
onClick?: () => ScriptField;
dropAction: dropActionType;
@@ -42,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;
+ backgroundColor?: (document: Opt<Doc>, renderDepth: number, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined;
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..24cd6f21f 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -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}
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 276c66bb1..8eb107274 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 = StrCast(this.layoutDoc._backgroundColor, StrCast(this.rootDoc.backgroundColor, this.props.backgroundColor?.(this.rootDoc, this.props.renderDepth, 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/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..f80eb8f79 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.backgroundColor?.(this.props.Document, this.props.renderDepth, this.props.layerProvider) }} >
<CollectionTreeView {...this.props}
ChromeHeight={returnZero}
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/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..ab97f9f7e 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -461,7 +461,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 +663,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 +671,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 +693,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/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 8fc8fb402..5017d21f1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1622,7 +1622,7 @@ 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);
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 7b4afeb69..574bda970 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';
@@ -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.backgroundColor?.(this.layoutDoc, this.props.renderDepth, 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/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