diff options
Diffstat (limited to 'src/client/views/nodes')
26 files changed, 147 insertions, 131 deletions
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], |