diff options
Diffstat (limited to 'src')
36 files changed, 206 insertions, 271 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9406b374e..81fb204de 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1322,7 +1322,10 @@ export namespace DocUtils { export function LeavePushpin(doc: Doc) { if (doc.isPushpin) return undefined; const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null); - const hasContextAnchor = DocListCast(doc.links).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context)); + const hasContextAnchor = DocListCast(doc.links). + some(l => + (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || + (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context)); if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { const pushpin = Docs.Create.FontIconDocument({ title: "pushpin", label: "", annotationOn: Cast(doc.annotationOn, Doc, null), isPushpin: true, diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 38d0ecaa6..abf1af669 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -379,7 +379,12 @@ export namespace DragManager { if (docsBeingDragged.length) { const pdfBox = dragElement.getElementsByTagName("canvas"); const pdfBoxSrc = ele.getElementsByTagName("canvas"); - Array.from(pdfBox).map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0)); + Array.from(pdfBox).filter(pb => pb.width && pb.height).map((pb, i) => { + const context = pb.getContext('2d')!; + console.log(getComputedStyle(pb).width); + console.log(getComputedStyle(pdfBoxSrc[i]).width); + context.drawImage(pdfBoxSrc[i], 0, 0); + }); } [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele => ele.hasAttribute("style") && ((ele as any).style.pointerEvents = "none")); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 3f6715560..5c0fe6b97 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -10,6 +10,8 @@ import { ScriptField } from '../../fields/ScriptField'; import { GetEffectiveAcl, SharingPermissions, distributeAcls, denormalizeEmail } from '../../fields/util'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { DocUtils } from '../documents/Documents'; +import { returnFalse } from '../../Utils'; +import { UndoManager } from '../util/UndoManager'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) @@ -34,7 +36,7 @@ export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: D return Component; } -/// FieldViewBoxProps - a generic base class for field views that are not annotatable (e.g. AudioBox, FormattedTextBox) +/// FieldViewBoxProps - a generic base class for field views that are not annotatable (e.g. InkingStroke, ColorBox) interface ViewBoxBaseProps { Document: Doc; DataDoc?: Doc; @@ -74,22 +76,23 @@ export interface ViewBoxAnnotatableProps { Document: Doc; DataDoc?: Doc; fieldKey: string; - layerProvider?: (doc: Doc) => boolean; - active: () => boolean; + filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) + layerProvider?: (doc: Doc, assign?: boolean) => boolean; + isContentActive: () => boolean; select: (isCtrlPressed: boolean) => void; whenChildContentsActiveChanged: (isActive: boolean) => void; isSelected: (outsideReaction?: boolean) => boolean; rootSelected: (outsideReaction?: boolean) => boolean; renderDepth: number; + isAnnotationOverlay?: boolean; } -export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) { +export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T, _annotationKey: string = "annotations") { class Component extends Touchable<P> { - @observable _annotationKey: string = "annotations"; + @observable _annotationKey: string = _annotationKey; @observable _isAnyChildContentActive = false; //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @computed get Document(): T { return schemaCtor(this.props.Document); } - // This is the "The Document" -- it encapsulates, data, layout, and any templates @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info @@ -125,7 +128,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - @computed public get annotationKey() { return this.fieldKey + "-" + this._annotationKey; } + @computed public get annotationKey() { return this.fieldKey + (this._annotationKey ? "-" + this._annotationKey : ""); } @action.bound removeDocument(doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean): boolean { @@ -156,16 +159,28 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T return false; } - // if the moved document is already in this overlay collection nothing needs to be done. - // otherwise, if the document can be removed from where it was, it will then be added to this document's overlay collection. + // this is called with the document that was dragged and the collection to move it into. + // if the target collection is the same as this collection, then the move will be allowed. + // otherwise, the document being moved must be able to be removed from its container before + // moving it into the target. @action.bound - moveDocument(doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean, annotationKey?: string): boolean { - return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc, annotationKey, true) ? addDocument(doc) : false; + moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean, annotationKey?: string): boolean => { + if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { + return true; + } + const first = doc instanceof Doc ? doc : doc[0]; + if (!first?._stayInCollection && addDocument !== returnFalse) { + return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc)); + } + return false; } @action.bound addDocument(doc: Doc | Doc[], annotationKey?: string): boolean { const docs = doc instanceof Doc ? [doc] : doc; - docs.map(doc => doc.context = Doc.GetProto(doc).annotationOn = this.props.Document); + if (this.props.filterAddDocument?.(docs) === false || + docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document))) { + return false; + } const targetDataDoc = this.props.Document[DataSym]; const docList = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); const added = docs.filter(d => !docList.includes(d)); @@ -187,10 +202,21 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T } if (effectiveAcl === AclAddonly) { - added.map(doc => Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc)); + added.map(doc => { + this.props.layerProvider?.(doc, true); + Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); + doc.context = this.props.Document; + if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document; + }); } else { - added.map(doc => doc.context = this.props.Document); + added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => { // only make a pushpin if we have acl's to edit the document + this.props.layerProvider?.(doc, true); + DocUtils.LeavePushpin(doc); + doc._stayInCollection = undefined; + doc.context = this.props.Document; + if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document; + }); const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>; if (annoDocs) annoDocs.push(...added); else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added); @@ -202,10 +228,10 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T } whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive)); - active = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool === InkTool.None && + isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool === InkTool.None && (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.renderDepth === 0 || this.props.rootSelected(outsideReaction)) ? true : false) annotationsActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None || - (this.props.layerProvider?.(this.props.Document) === false && this.props.active()) || + (this.props.layerProvider?.(this.props.Document) !== false && this.props.isContentActive?.()) || (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.renderDepth === 0) ? true : false) } return Component; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index bb6a091cb..83949eba2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -485,7 +485,7 @@ export class MainView extends React.Component { rootSelected={returnTrue} bringToFront={emptyFunction} select={emptyFunction} - active={returnFalse} + isContentActive={returnFalse} isSelected={returnFalse} docViewPath={returnEmptyDoclist} moveDocument={this.moveButtonDoc} @@ -547,7 +547,7 @@ export class MainView extends React.Component { fieldKey="data" dropAction="move" isSelected={returnTrue} - active={returnTrue} + isContentActive={returnTrue} select={returnTrue} setHeight={returnFalse} addDocument={undefined} @@ -593,7 +593,7 @@ export class MainView extends React.Component { pinToPres={returnFalse} ScreenToLocalTransform={Transform.Identity} bringToFront={returnFalse} - active={returnFalse} + isContentActive={returnFalse} whenChildContentsActiveChanged={returnFalse} focus={returnFalse} docViewPath={returnEmptyDoclist} @@ -665,7 +665,7 @@ export class MainView extends React.Component { pinToPres={returnFalse} ScreenToLocalTransform={Transform.Identity} bringToFront={returnFalse} - active={returnFalse} + isContentActive={returnFalse} whenChildContentsActiveChanged={returnFalse} focus={returnFalse} PanelWidth={() => 500} diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index d3216bc94..4514e3ebb 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -98,7 +98,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { }; return !this.props.layoutDoc._showSidebar ? (null) : <div style={{ - position: "absolute", pointerEvents: this.props.active() ? "all" : undefined, top: 0, right: 0, + position: "absolute", pointerEvents: this.props.isContentActive() ? "all" : undefined, top: 0, right: 0, background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor), width: `${this.panelWidth()}px`, height: "100%" @@ -120,7 +120,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { scaleField={this.sidebarKey() + "-scale"} isAnnotationOverlay={false} select={emptyFunction} - active={this.props.annotationsActive} + isContentActive={this.props.annotationsActive} scaling={returnOne} whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} childHideDecorationTitle={returnTrue} diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 34b0e4d99..5491a81e6 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -141,7 +141,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> { onCheckedClick={this.scriptField} onChildClick={this.scriptField} dropAction={undefined} - active={returnTrue} + isContentActive={returnTrue} bringToFront={emptyFunction} focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 452c79f88..b2ae441d6 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -126,7 +126,7 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume } @computed get buttons() { - if (!this.props.active()) return null; + if (!this.props.isContentActive()) return null; return <div className="arrow-buttons" > <div key="back" className="carousel3DView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={(e) => this.onArrowClick(e, -1)} diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 92288c1f2..2d7569d45 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -208,7 +208,7 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap render() { const { childLayoutPairs } = this; - const { Document, fieldKey, active, google } = this.props; + const { Document, fieldKey, isContentActive: active, google } = this.props; const mapLoc = this.getLocation(this.rootDoc, `${fieldKey}-mapCenter`, false); let center = mapLoc; if (center === undefined) { diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index ba701b2a4..a94e706eb 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -281,7 +281,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""), }}> {this.props.parent.children(this.props.docList)} - {this.props.showHandle && this.props.parent.props.active() ? this.props.parent.columnDragger : (null)} + {this.props.showHandle && this.props.parent.props.isContentActive() ? this.props.parent.columnDragger : (null)} </div> </div>; } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index dc2880a48..d49403d8e 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -328,7 +328,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action onWheel(e: React.WheelEvent) { const scale = this.props.ScreenToLocalTransform().Scale; - this.props.active(true) && e.stopPropagation(); + this.props.isContentActive(true) && e.stopPropagation(); } @computed get renderMenuContent() { @@ -444,7 +444,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { renderDepth={this.props.renderDepth} moveDocument={this.props.moveDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} - active={this.props.active} + active={this.props.isContentActive} onDrop={this.onExternalDrop} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} @@ -533,11 +533,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { this.columns = columns; } - onZoomMenu = (e: React.WheelEvent) => this.props.active(true) && e.stopPropagation(); + onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation(); render() { TraceMobx(); - if (!this.props.active()) setTimeout(() => this.closeHeader(), 0); + if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0); const menuContent = this.renderMenuContent; const menu = <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} @@ -553,21 +553,21 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { return <div className={"collectionSchemaView" + (this.props.Document._searchDoc ? "-searchContainer" : "-container")} style={{ overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white", - pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined, + pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined, width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", }} > <div className="collectionSchemaView-tableContainer" style={{ width: `calc(100% - ${this.previewWidth()}px)` }} onContextMenu={this.onSpecificMenu} onPointerDown={this.onPointerDown} - onWheel={e => this.props.active(true) && e.stopPropagation()} + onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} onDrop={e => this.onExternalDrop(e, {})} ref={this.createTarget}> {this.schemaTable} </div> {this.dividerDragger} {!this.previewWidth() ? (null) : this.previewPanel} - {this._headerOpen && this.props.active() ? menu : null} + {this._headerOpen && this.props.isContentActive() ? menu : null} </div>; } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 304643d52..d6aca6f00 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -114,7 +114,7 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument onPointerDownTimeline = (e: React.PointerEvent): void => { const rect = this._timeline?.getBoundingClientRect(); const clientX = e.clientX; - if (rect && this.props.active()) { + if (rect && this.props.isContentActive()) { const wasPlaying = this.props.playing(); if (wasPlaying) this.props.Pause(); const wasSelecting = CollectionStackedTimeline.SelectingRegion === this; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 3087aeb81..4dab9d94a 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -533,14 +533,14 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"} ref={this.createRef} style={{ - overflowY: this.props.active() ? "auto" : "hidden", + overflowY: this.props.isContentActive() ? "auto" : "hidden", background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor), pointerEvents: this.backgroundEvents ? "all" : undefined }} onScroll={action(e => this._scroll = e.currentTarget.scrollTop)} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu} - onWheel={e => this.props.active(true) && e.stopPropagation()} > + onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} > {this.renderedSections} {!this.showAddAGroup ? (null) : <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 0702febae..f41043179 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -136,7 +136,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { } @computed get contents() { - return <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.active() ? undefined : "none" }} + return <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.isContentActive() ? undefined : "none" }} onClick={this.contentsDown}> <CollectionFreeFormView {...this.props} engineProps={{ pivotField: this.pivotField, docFilters: this.docFilters, docRangeFilters: this.docRangeFilters }} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 788cb01f6..f69a3a331 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -197,7 +197,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll @computed get fileSysMode() { return this.doc.treeViewType === "fileSystem"; } onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); whenChildContentsActiveChanged = (isActive: boolean) => { this.props.whenChildContentsActiveChanged(this._isChildActive = isActive); }; - active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive; + active = (outsideReaction: boolean | undefined) => this.props.isContentActive(outsideReaction) || this._isChildActive; panelWidth = () => this.props.PanelWidth() - 20; // bcz: 20 is the 10 + 10 for the left and right padding. @computed get treeChildren() { TraceMobx(); @@ -224,7 +224,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll const background = this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); const paddingX = `${NumCast(this.doc._xPadding, 15)}px`; const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`; - const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined; + const pointerEvents = !this.props.isContentActive() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined; return !this.treeChildren ? (null) : ( <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}> diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index f8c846f7f..28099a995 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -2,26 +2,23 @@ import { action, computed, observable } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app -import { DateField } from '../../../fields/DateField'; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, DocListCastAsync, Field } from '../../../fields/Doc'; +import { Doc, DocListCast } from '../../../fields/Doc'; +import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; -import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; +import { makeInterface } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; -import { Cast, ScriptCast, StrCast, DateCast } from '../../../fields/Types'; -import { denormalizeEmail, distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; -import { returnFalse } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; -import { BranchTask, BranchCreate } from '../../documents/Gitlike'; -import { DocumentType } from '../../documents/DocumentTypes'; +import { Cast, ScriptCast, StrCast } from '../../../fields/Types'; +import { TraceMobx } from '../../../fields/util'; +import { DocUtils } from '../../documents/Documents'; +import { BranchCreate, BranchTask } from '../../documents/Gitlike'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { InteractionUtils } from '../../util/InteractionUtils'; -import { UndoManager } from '../../util/UndoManager'; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; -import { Touchable } from '../Touchable'; import { CollectionCarousel3DView } from './CollectionCarousel3DView'; import { CollectionCarouselView } from './CollectionCarouselView'; import { CollectionDockingView } from "./CollectionDockingView"; @@ -64,7 +61,6 @@ export enum CollectionViewType { export interface CollectionViewProps extends FieldViewProps { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) layoutEngine?: () => string; - filterAddDocument?: (doc: Doc | Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; // property overrides for child documents @@ -81,19 +77,20 @@ export interface CollectionViewProps extends FieldViewProps { childClickScript?: ScriptField; childDoubleClickScript?: ScriptField; } + +type CollectionDocument = makeInterface<[typeof documentSchema]>; +const CollectionDocument = makeInterface(documentSchema); @observer -export class CollectionView extends Touchable<CollectionViewProps> { +export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument, "") { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } - _isChildActive = false; //TODO should this be observable? - @observable private _curLightboxImg = 0; @observable private static _safeMode = false; public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; } protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; get collectionViewType(): CollectionViewType | undefined { - const viewField = StrCast(this.props.Document._viewType); + const viewField = StrCast(this.layoutDoc._viewType); if (CollectionView._safeMode) { switch (viewField) { case CollectionViewType.Freeform: @@ -104,121 +101,24 @@ export class CollectionView extends Touchable<CollectionViewProps> { return viewField as any as CollectionViewType; } - active = (outsideReaction?: boolean) => { - return this.props.renderDepth === 0 || - this.props.isSelected(outsideReaction) || - this.props.rootSelected(outsideReaction) || - (this.props.layerProvider?.(this.props.Document) !== false && (this.props.Document.forceActive || this.props.Document._isGroup)) || - this._isChildActive ? - true : - false; - } - - whenChildContentsActiveChanged = (isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isChildActive = isActive); - - @action.bound - addDocument = (doc: Doc | Doc[]): boolean => { - if (this.props.filterAddDocument?.(doc) === false) { - return false; - } - - const docs = doc instanceof Doc ? [doc] : doc; - - if (docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document))) return false; - const targetDataDoc = this.props.Document[DataSym]; - const docList = DocListCast(targetDataDoc[this.props.fieldKey]); - const added = docs.filter(d => !docList.includes(d)); - const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); - - if (added.length) { - if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) { - return false; - } - else { - if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym])) { - added.forEach(d => { - for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true); - //else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true); - //else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); - } - }); - } - - if (effectiveAcl === AclAddonly) { - added.map(doc => { - this.props.layerProvider?.(doc, true);// assigns layer values to the newly added document... testing the utility of this - Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc); - doc.context = this.props.Document; - }); - } - else { - added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => { // only make a pushpin if we have acl's to edit the document - DocUtils.LeavePushpin(doc); - doc._stayInCollection = undefined; - doc.context = this.props.Document; - }); - added.map(doc => this.props.layerProvider?.(doc, true));// assigns layer values to the newly added document... testing the utility of this - (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added); - targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); - } - } - } - return true; - } - - @action.bound - removeDocument = (doc: any): boolean => { - const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); - const indocs = doc instanceof Doc ? [doc] : doc as Doc[]; - const docs = indocs.filter(doc => effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin); - if (docs.length) { - const targetDataDoc = this.props.Document[DataSym]; - const value = DocListCast(targetDataDoc[this.props.fieldKey]); - const toRemove = value.filter(v => docs.includes(v)); - if (toRemove.length !== 0) { - const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc; - toRemove.forEach(doc => { - const ind = (targetDataDoc[this.props.fieldKey] as List<Doc>).indexOf(doc); - if (ind !== -1) { - Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc); - doc.context = undefined; - recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); - } - }); - return true; - } - } - return false; - } - - // this is called with the document that was dragged and the collection to move it into. - // if the target collection is the same as this collection, then the move will be allowed. - // otherwise, the document being moved must be able to be removed from its container before - // moving it into the target. - @action.bound - moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { - if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { - return true; - } - const first = doc instanceof Doc ? doc : doc[0]; - if (!first?._stayInCollection && addDocument !== returnFalse) { - return UndoManager.RunInTempBatch(() => this.removeDocument(doc) && addDocument(doc)); - } - return false; - } - showIsTagged = () => { return (null); // this section would display an icon in the bototm right of a collection to indicate that all // photos had been processed through Google's content analysis API and Google's tags had been // assigned to the documents googlePhotosTags field. - // const children = DocListCast(this.props.Document[this.props.fieldKey]); + // const children = DocListCast(this.rootDoc[this.props.fieldKey]); // const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto); // const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags); // return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />; } + isContentActive = (outsideReaction?: boolean) => { + return this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.renderDepth === 0 || this.props.rootSelected(outsideReaction) || + (this.props.layerProvider?.(this.rootDoc) !== false && (this.rootDoc.forceActive || this.rootDoc._isGroup)) + ? + true : false; + } + screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()); private SubView = (type: CollectionViewType, props: SubCollectionViewProps) => { TraceMobx(); @@ -251,7 +151,6 @@ export class CollectionView extends Touchable<CollectionViewProps> { } subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" }); subItems.push({ description: "Tree", event: () => func(CollectionViewType.Tree), icon: "tree" }); - !Doc.UserDoc().noviceMode && subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking), icon: "ellipsis-v" }); subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" }); subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" }); subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" }); @@ -262,7 +161,7 @@ export class CollectionView extends Touchable<CollectionViewProps> { !Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" }); - if (!Doc.IsSystem(this.props.Document) && !this.props.Document.annotationOn) { + if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.annotationOn) { const existingVm = ContextMenu.Instance.findByDescription(category); const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : []; catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" }); @@ -272,9 +171,9 @@ export class CollectionView extends Touchable<CollectionViewProps> { onContextMenu = (e: React.MouseEvent): void => { const cm = ContextMenu.Instance; - if (cm && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 + if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 this.setupViewTypes("UI Controls...", vtype => { - const newRendition = Doc.MakeAlias(this.props.Document); + const newRendition = Doc.MakeAlias(this.rootDoc); newRendition._viewType = vtype; this.props.addDocTab(newRendition, "add:right"); return newRendition; @@ -282,34 +181,34 @@ export class CollectionView extends Touchable<CollectionViewProps> { const options = cm.findByDescription("Options..."); const optionItems = options && "subitems" in options ? options.subitems : []; - !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }) : null; - if (this.props.Document.childLayout instanceof Doc) { - optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "add:right"), icon: "project-diagram" }); + !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null; + if (this.rootDoc.childLayout instanceof Doc) { + optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, "add:right"), icon: "project-diagram" }); } - if (this.props.Document.childClickedOpenTemplateView instanceof Doc) { - optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" }); + if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) { + optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" }); } - !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" }); + !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" }); optionItems.push({ - description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.props.Document), "add:right"), icon: "project-diagram" + description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram" }); optionItems.push({ - description: "Pull Master", event: () => BranchTask(this.props.Document, "pull"), icon: "project-diagram" + description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram" }); optionItems.push({ - description: "Merge Branches", event: () => BranchTask(this.props.Document, "merge"), icon: "project-diagram" + description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram" }); !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" }); - if (!Doc.UserDoc().noviceMode && !this.props.Document.annotationOn) { + if (!Doc.UserDoc().noviceMode && !this.rootDoc.annotationOn) { const existingOnClick = cm.findByDescription("OnClick..."); const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }]; funcs.map(func => onClicks.push({ description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => { - const alias = Doc.MakeAlias(this.props.Document); + const alias = Doc.MakeAlias(this.rootDoc); DocUtils.makeCustomViewClicked(alias, undefined, func.key); this.props.addDocTab(alias, "add:right"); } @@ -318,15 +217,15 @@ export class CollectionView extends Touchable<CollectionViewProps> { onClicks.push({ description: `Set child ${childClick.title}`, icon: "edit", - event: () => Doc.GetProto(this.props.Document)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)), + event: () => Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)), })); - !Doc.IsSystem(this.props.Document) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); + !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); } if (!Doc.UserDoc().noviceMode) { const more = cm.findByDescription("More..."); const moreItems = more && "subitems" in more ? more.subitems : []; - moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) }); + moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) }); !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" }); } } @@ -334,27 +233,26 @@ export class CollectionView extends Touchable<CollectionViewProps> { bodyPanelWidth = () => this.props.PanelWidth(); - childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); - @computed get childLayoutString() { return StrCast(this.props.Document.childLayoutString); } + childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null); + @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); } render() { TraceMobx(); const props: SubCollectionViewProps = { ...this.props, addDocument: this.addDocument, - removeDocument: this.removeDocument, moveDocument: this.moveDocument, - active: this.active, - whenChildContentsActiveChanged: this.whenChildContentsActiveChanged, + removeDocument: this.removeDocument, + isContentActive: this.isContentActive, PanelWidth: this.bodyPanelWidth, PanelHeight: this.props.PanelHeight, + ScreenToLocalTransform: this.screenToLocalTransform, childLayoutTemplate: this.childLayoutTemplate, childLayoutString: this.childLayoutString, - ScreenToLocalTransform: this.screenToLocalTransform, CollectionView: this, }; return (<div className={"collectionView"} onContextMenu={this.onContextMenu} - style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined }}> + style={{ pointerEvents: this.props.layerProvider?.(this.rootDoc) === false ? "none" : undefined }}> {this.showIsTagged()} {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)} </div>); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index dcd874089..ec3f3090b 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -420,7 +420,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { 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 setHeight={returnFalse} - active={returnTrue} + isContentActive={returnTrue} select={emptyFunction} dropAction={undefined} isSelected={returnFalse} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index eca655099..b74bca5d3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -116,7 +116,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); } - @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.active() || this.props.active()); } + @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.isContentActive() || this.props.isContentActive()); } @computed get fitToContentVals() { return { bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 }, @@ -457,7 +457,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return; } this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)); - if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.active(true)) { + if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -482,7 +482,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const pt = me.changedTouches[0]; if (pt) { this._hitCluster = this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)); - if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) { + if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) { this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); @@ -645,7 +645,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (this.props.Document._isGroup) return; // groups don't pan when dragged -- instead let the event go through to allow the group itself to drag if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return; if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - if (this.props.active(true)) e.stopPropagation(); + if (this.props.isContentActive(true)) e.stopPropagation(); } else if (!e.cancelBubble) { if (CurrentUserUtils.SelectedTool === InkTool.None) { if (this.tryDragCluster(e, this._hitCluster)) { @@ -733,7 +733,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @action handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => { - if (!e.nativeEvent.cancelBubble && this.props.active(true)) { + if (!e.nativeEvent.cancelBubble && this.props.isContentActive(true)) { // const pt1: React.Touch | null = e.targetTouches.item(0); // const pt2: React.Touch | null = e.targetTouches.item(1); // // if (!pt1 || !pt2) return; @@ -817,7 +817,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } - else if (this.props.active(true) && !this.Document._isGroup) { + else if (this.props.isContentActive(true) && !this.Document._isGroup) { e.stopPropagation(); e.preventDefault(); this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc? @@ -1043,7 +1043,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P freezeDimensions={this.props.childFreezeDimensions} dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} bringToFront={this.bringToFront} - documentActive={() => this.props.active()} + isDocumentActive={() => this.props.isContentActive()} dontRegisterView={this.props.dontRegisterView} pointerEvents={this.backgroundActive || this.props.childPointerEvents ? "all" : (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined} @@ -1486,7 +1486,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P width: `${100 / (this.contentScaling || 1)}%`, height: this.isAnnotationOverlay && this.Document.scrollHeight ? this.Document.scrollHeight : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}> - {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ? + {this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ? this.placeholder : this.marqueeView} {this.props.noOverlay ? (null) : <CollectionFreeFormOverlayView elements={this.elementFunc} />} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index af391a078..d7a0d3f34 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -167,7 +167,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) { FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : ""; FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch"); - this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xMargin === 0, this.props.isAnnotationOverlay ? this.props.Document : undefined)); + this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xMargin === 0)); e.stopPropagation(); } } diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 35eed3bc2..b0030471d 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -295,7 +295,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { * Handles text document creation on double click. */ onPointerDown = (e: React.PointerEvent) => { - if (this.props.active(true)) { + if (this.props.isContentActive(true)) { setupMoveUpEvents(this, e, returnFalse, returnFalse, (e: PointerEvent, doubleTap?: boolean) => { if (doubleTap) { @@ -315,7 +315,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { render() { return ( <div className="collectionGridView-contents" ref={this.createDashEventsTarget} - style={{ pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined }} + style={{ pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined }} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onDrop={this.onExternalDrop} diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index d4d2bea76..970f9bd95 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -17,7 +17,7 @@ import { SnappingManager } from "../../util/SnappingManager"; import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; -import { ViewBoxAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; import "./AudioBox.scss"; import { FieldView, FieldViewProps } from './FieldView'; import { LinkDocPreview } from "./LinkDocPreview"; @@ -29,7 +29,7 @@ type AudioDocument = makeInterface<[typeof documentSchema]>; const AudioDocument = makeInterface(documentSchema); @observer -export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioDocument>(AudioDocument) { +export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, AudioDocument>(AudioDocument, "annotations") { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); } public static Enabled = false; static playheadWidth = 30; // width of playhead @@ -63,7 +63,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD @computed get pauseTime() { return this._pauseEnd - this._pauseStart; } // total time paused to update the correct time @computed get heightPercent() { return AudioBox.heightPercent; } - constructor(props: Readonly<FieldViewProps>) { + constructor(props: Readonly<ViewBoxAnnotatableProps & FieldViewProps>) { super(props); AudioBox.Instance = this; @@ -267,7 +267,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD // returns the html audio element @computed get audio() { - return <audio ref={this.setRef} className={`audiobox-control${this.active() ? "-interactive" : ""}`}> + return <audio ref={this.setRef} className={`audiobox-control${this.isContentActive() ? "-interactive" : ""}`}> <source src={this.path} type="audio/mpeg" /> Not supported. </audio>; @@ -337,7 +337,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD isChildActive={this.isActiveChild} Play={this.Play} Pause={this.Pause} - active={this.active} + isContentActive={this.isContentActive} playLink={this.playLink} PanelWidth={this.timelineWidth} PanelHeight={this.timelineHeight} @@ -345,7 +345,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD } render() { - const interactive = SnappingManager.GetIsDragging() || this.active() ? "-interactive" : ""; + const interactive = SnappingManager.GetIsDragging() || this.isContentActive() ? "-interactive" : ""; return <div className="audiobox-container" onContextMenu={this.specificContextMenu} onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined} @@ -370,7 +370,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD RECORD </button>} </div> : - <div className="audiobox-controls" style={{ pointerEvents: this._isAnyChildContentActive || this.active() ? "all" : "none" }} > + <div className="audiobox-controls" style={{ pointerEvents: this._isAnyChildContentActive || this.isContentActive() ? "all" : "none" }} > <div className="audiobox-dictation" /> <div className="audiobox-player" style={{ height: `${AudioBox.heightPercent}%` }} > <div className="audiobox-playhead" style={{ width: AudioBox.playheadWidth }} title={this.mediaState === "paused" ? "play" : "pause"} onClick={this.Play}> <FontAwesomeIcon style={{ width: "100%", position: "absolute", left: "0px", top: "5px", borderWidth: "thin", borderColor: "white" }} icon={this.mediaState === "paused" ? "play" : "pause"} size={"1x"} /></div> diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index d71bb1397..0a9d7e809 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -9,7 +9,7 @@ import { emptyFunction, OmitKeys, setupMoveUpEvents } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; -import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import "./ComparisonBox.scss"; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; @@ -21,7 +21,7 @@ type ComparisonDocument = makeInterface<[typeof comparisonSchema, typeof documen const ComparisonDocument = makeInterface(comparisonSchema, documentSchema); @observer -export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, ComparisonDocument>(ComparisonDocument) { +export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ComparisonDocument>(ComparisonDocument, "annotations") { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); } protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined]; @@ -102,7 +102,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, C }; return ( - <div className={`comparisonBox${this.active() || SnappingManager.GetIsDragging() ? "-interactive" : ""}` /* change className to easily disable/enable pointer events in CSS */}> + <div className={`comparisonBox${this.isContentActive() || SnappingManager.GetIsDragging() ? "-interactive" : ""}` /* change className to easily disable/enable pointer events in CSS */}> {displayBox("after", 1, this.props.PanelWidth() - 3)} <div className="clip-div" style={{ width: clipWidth, transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, "gray") }}> {displayBox("before", 0, 0)} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 553cfcdf7..b53827371 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -40,6 +40,7 @@ import React = require("react"); import { TraceMobx, GetEffectiveAcl } from "../../../fields/util"; import { ScriptField } from "../../../fields/ScriptField"; import XRegExp = require("xregexp"); +import { DocumentType } from "../../documents/DocumentTypes"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 19ed417c0..e4f1ac8ed 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -110,6 +110,7 @@ export interface DocumentViewSharedProps { whenChildContentsActiveChanged: (isActive: boolean) => void; rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected addDocTab: (doc: Doc, where: string) => boolean; + filterAddDocument?: (doc: []) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) addDocument?: (doc: Doc | Doc[]) => boolean; removeDocument?: (doc: Doc | Doc[]) => boolean; moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; @@ -130,7 +131,8 @@ export interface DocumentViewProps extends DocumentViewSharedProps { hideTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings treeViewDoc?: Doc; - documentActive?: () => boolean; // whether a document should handle pointer events + isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events + isContentActive?: () => boolean | undefined; // whether a document should handle pointer events contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents radialMenu?: String[]; LayoutTemplateString?: string; @@ -175,7 +177,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class private get topMost() { return this.props.renderDepth === 0; } - private get active() { return this.props.documentActive?.() || this.props.isSelected(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 Doc.LayoutFieldKey(this.layoutDoc); } @@ -279,7 +280,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps this._downX = touch.clientX; this._downY = touch.clientY; if (!e.nativeEvent.cancelBubble) { - if ((this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) e.stopPropagation(); + if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) e.stopPropagation(); this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); @@ -290,10 +291,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => { - if (e.cancelBubble && this.active) { + if (e.cancelBubble && this.props.isDocumentActive?.()) { this.removeMoveListeners(); } - else if (!e.cancelBubble && (this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { + else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { const touch = me.touchEvent.changedTouches.item(0); if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) { if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) { @@ -510,7 +511,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.layoutDoc.onDragStart) && // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) { - if ((this.active || this.layoutDoc.onDragStart) && + if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !e.ctrlKey && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { @@ -527,10 +528,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps onPointerMove = (e: PointerEvent): void => { if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) return; - if (e.cancelBubble && this.active) { + if (e.cancelBubble && this.props.isDocumentActive?.()) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } - else if (!e.cancelBubble && (this.active || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { + else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) { document.removeEventListener("pointermove", this.onPointerMove); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 97f53aac0..0fc7a752f 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -18,8 +18,8 @@ export interface FieldViewProps extends DocumentViewSharedProps { fieldKey: string; scrollOverflow?: boolean; // bcz: would like to think this can be avoided -- need to look at further - active: (outsideReaction?: boolean) => boolean; select: (isCtrlPressed: boolean) => void; + isContentActive: (outsideReaction?: boolean) => boolean; isSelected: (outsideReaction?: boolean) => boolean; scaling?: () => number; setHeight: (height: number) => void; diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index 39db6f46c..bf5a6b7d2 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -215,7 +215,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc isSelected={returnFalse} select={returnFalse} bringToFront={emptyFunction} - active={returnTrue} + isContentActive={returnTrue} whenChildContentsActiveChanged={returnFalse} treeViewHideTitle={true} focus={returnFalse} diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index efb9dcb4f..0f2e3e65d 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -20,7 +20,7 @@ import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../../views/ContextMenu"; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { ContextMenuProps } from '../ContextMenuItem'; -import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { StyleProp } from '../StyleProvider'; import { FaceRectangles } from './FaceRectangles'; @@ -46,7 +46,7 @@ const uploadIcons = { }; @observer -export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) { +export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ImageDocument>(ImageDocument, "annotations") { protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } private _imgRef: React.RefObject<HTMLImageElement> = React.createRef(); @@ -319,7 +319,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD } @action marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.active(true)) this._marqueeing = [e.clientX, e.clientY]; + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true)) this._marqueeing = [e.clientX, e.clientY]; } @action finishMarquee = () => { @@ -358,7 +358,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD focus={this.props.focus} isSelected={this.props.isSelected} select={emptyFunction} - active={this.annotationsActive} + isContentActive={this.annotationsActive} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}> {this.contentFunc} </CollectionFreeFormView> diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index e03293332..881cbf2bb 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -72,7 +72,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> { dropAction: "alias", bringToFront: emptyFunction, renderDepth: 1, - active: returnFalse, + isContentActive: returnFalse, whenChildContentsActiveChanged: emptyFunction, ScreenToLocalTransform: Transform.Identity, focus: emptyFunction, diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index cef294692..3eaa6e0f4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -16,7 +16,7 @@ import { undoBatch } from '../../util/UndoManager'; import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { ViewBoxAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; import { PDFViewer } from "../pdf/PDFViewer"; import { SidebarAnnos } from '../SidebarAnnos'; import { FieldView, FieldViewProps } from './FieldView'; @@ -28,7 +28,7 @@ type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, t const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @observer -export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocument>(PdfDocument) { +export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, PdfDocument>(PdfDocument, "annotations") { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); } private _searchString: string = ""; private _initialScrollTarget: Opt<Doc>; @@ -171,9 +171,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum </>; const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`; const curPage = this.Document._curPage || 1; - return !this.active() ? (null) : + return !this.isContentActive() ? (null) : <div className="pdfBox-ui" onKeyDown={e => [KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true} - onPointerDown={e => e.stopPropagation()} style={{ display: this.active() ? "flex" : "none" }}> + onPointerDown={e => e.stopPropagation()} style={{ display: this.isContentActive() ? "flex" : "none" }}> <div className="pdfBox-overlayCont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> <button className="pdfBox-overlayButton" title={searchTitle} /> <input className="pdfBox-searchBar" placeholder="Search" ref={this._searchRef} onChange={this.searchStringChanged} @@ -203,7 +203,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum {this._pageControls ? pageBtns : (null)} </div> <button className="pdfBox-sidebarBtn" title="Toggle Sidebar" - style={{ right: this.sidebarWidth() + 7, display: !this.active() ? "none" : undefined }} + style={{ right: this.sidebarWidth() + 7, display: !this.isContentActive() ? "none" : undefined }} onPointerDown={e => e.stopPropagation()} onClick={e => this.toggleSidebar()} > <FontAwesomeIcon icon={"chevron-left"} size="sm" /> </button> @@ -221,7 +221,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum } @computed get renderTitleBox() { - const classname = "pdfBox" + (this.active() ? "-interactive" : ""); + const classname = "pdfBox" + (this.isContentActive() ? "-interactive" : ""); return <div className={classname} > <div className="pdfBox-title-outer"> <strong className="pdfBox-title" >{this.props.Document.title}</strong> @@ -240,7 +240,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum <PDFViewer {...this.props} pdf={this._pdf!} url={this.pdfUrl!.url.pathname} - active={this.active} + isContentActive={this.isContentActive} anchorMenuClick={this._sidebarRef.current?.anchorMenuClick} loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined} setPdfViewer={this.setPdfViewer} @@ -259,7 +259,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum sidebarAddDocument={this.sidebarAddDocument} moveDocument={this.moveDocument} removeDocument={this.removeDocument} - active={this.active} + isContentActive={this.isContentActive} /> {this.settingsPanel()} </div>; diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 21ab0a2e0..57c88d411 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -713,8 +713,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isChildActive = isActive)); // For dragging documents into the presentation trail - addDocumentFilter = (doc: Doc | Doc[]) => { - const docs = doc instanceof Doc ? [doc] : doc; + addDocumentFilter = (docs: Doc[]) => { docs.forEach((doc, i) => { if (doc.presentationTargetDoc) return true; if (doc.type === DocumentType.LABEL) { diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 48fc0834f..b4e7c6243 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -19,7 +19,7 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline"; import { ContextMenu } from "../ContextMenu"; -import { ViewBoxAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; import { FieldView, FieldViewProps } from './FieldView'; import "./ScreenshotBox.scss"; import { VideoBox } from "./VideoBox"; @@ -33,7 +33,7 @@ type ScreenshotDocument = makeInterface<[typeof documentSchema]>; const ScreenshotDocument = makeInterface(documentSchema); @observer -export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) { +export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScreenshotDocument>(ScreenshotDocument, "annotations") { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); } private _videoRef = React.createRef<HTMLVideoElement>(); private _audioRec: any; @@ -159,7 +159,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps, S isSelected={this.props.isSelected} isAnnotationOverlay={true} select={emptyFunction} - active={returnFalse} + isContentActive={returnFalse} scaling={returnOne} whenChildContentsActiveChanged={emptyFunction} removeDocument={returnFalse} @@ -181,7 +181,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps, S isSelected={this.props.isSelected} isAnnotationOverlay={true} select={emptyFunction} - active={returnFalse} + isContentActive={returnFalse} scaling={returnOne} xMargin={25} yMargin={10} diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index f1f2cd7d3..0afc61f54 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -15,7 +15,7 @@ import { InteractionUtils } from "../../util/InteractionUtils"; import { CompileScript, Scripting, ScriptParam } from "../../util/Scripting"; import { ScriptManager } from "../../util/ScriptManager"; import { ContextMenu } from "../ContextMenu"; -import { ViewBoxAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import { OverlayView } from "../OverlayView"; @@ -29,7 +29,7 @@ type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentS const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); @observer -export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, ScriptingDocument>(ScriptingDocument) { +export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScriptingDocument>(ScriptingDocument, "annotations") { private dropDisposer?: DragManager.DragDropDisposer; protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 0b0f8afd1..f26a4fdbd 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -19,7 +19,7 @@ import { CollectionFreeFormView } from "../collections/collectionFreeForm/Collec import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; -import { ViewBoxAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { MarqueeAnnotator } from "../MarqueeAnnotator"; import { StyleProp } from "../StyleProvider"; @@ -32,7 +32,7 @@ type VideoDocument = makeInterface<[typeof documentSchema]>; const VideoDocument = makeInterface(documentSchema); @observer -export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) { +export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, VideoDocument>(VideoDocument, "annotations") { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); } static _youtubeIframeCounter: number = 0; static Instance: VideoBox; @@ -63,7 +63,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD private get transition() { return this._clicking ? "left 0.5s, width 0.5s, height 0.5s" : ""; } public get player(): HTMLVideoElement | null { return this._videoRef; } - constructor(props: Readonly<FieldViewProps>) { + constructor(props: Readonly<ViewBoxAnnotatableProps & FieldViewProps>) { super(props); VideoBox.Instance = this; } @@ -309,7 +309,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive"; const style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive; return !field ? <div key="loading">Loading</div> : - <div className="container" key="container" style={{ pointerEvents: this._isAnyChildContentActive || this.active() ? "all" : "none" }}> + <div className="container" key="container" style={{ pointerEvents: this._isAnyChildContentActive || this.isContentActive() ? "all" : "none" }}> <div className={`${style}`} style={{ width: "100%", height: "100%", left: "0px" }}> <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={{ height: "100%", width: "auto", display: "flex", margin: "auto" }} @@ -323,7 +323,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD Not supported. </video> {!this.audiopath || this.audiopath === field.url.href ? (null) : - <audio ref={this.setAudioRef} className={`audiobox-control${this.active() ? "-interactive" : ""}`}> + <audio ref={this.setAudioRef} className={`audiobox-control${this.isContentActive() ? "-interactive" : ""}`}> <source src={this.audiopath} type="audio/mpeg" /> Not supported. </audio>} @@ -420,7 +420,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD setupMoveUpEvents(this, e, action((e: PointerEvent) => { this._clicking = false; - if (this.active()) { + if (this.isContentActive()) { const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY); this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100)); } @@ -429,7 +429,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD () => { this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent; setTimeout(action(() => this._clicking = false), 500); - }, this.active(), this.active()); + }, this.isContentActive(), this.isContentActive()); }); onResetDown = (e: React.PointerEvent) => { @@ -526,7 +526,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD isChildActive={this.isActiveChild} Play={this.Play} Pause={this.Pause} - active={this.active} + isContentActive={this.isContentActive} playLink={this.playLink} PanelHeight={this.timelineHeight} /> @@ -538,7 +538,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD } marqueeDown = action((e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.active(true)) this._marqueeing = [e.clientX, e.clientY]; + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true)) this._marqueeing = [e.clientX, e.clientY]; }); finishMarquee = action(() => { @@ -572,7 +572,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD isAnnotationOverlay={true} annotationLayerHostsContent={true} select={emptyFunction} - active={this.annotationsActive} + isContentActive={this.annotationsActive} scaling={returnOne} docFilters={this.timelineDocFilter} PanelWidth={this.panelWidth} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index e7e28ba2b..8c446cf7a 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -21,7 +21,7 @@ import { undoBatch } from "../../util/UndoManager"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; -import { ViewBoxAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { LightboxView } from "../LightboxView"; import { MarqueeAnnotator } from "../MarqueeAnnotator"; @@ -39,7 +39,7 @@ type WebDocument = makeInterface<[typeof documentSchema]>; const WebDocument = makeInterface(documentSchema); @observer -export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) { +export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, WebDocument>(WebDocument, "annotations") { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); @@ -391,7 +391,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum @action onMarqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && this.active(true)) { + if (!e.altKey && e.button === 0 && this.isContentActive(true)) { this._marqueeing = [e.clientX, e.clientY]; this.props.select(false); } @@ -440,7 +440,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum sidebarWidth = () => !this.layoutDoc._showSidebar ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); @computed get content() { - return <div className={"webBox-cont" + (!this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.active() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")} + return <div className={"webBox-cont" + (!this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.isContentActive() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")} style={{ width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / (this.props.scaling?.() || 1)}%`, }}> {this.urlContent} </div>; @@ -464,7 +464,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum const inactiveLayer = this.props.layerProvider?.(this.layoutDoc) === false; const scale = this.props.scaling?.() || 1; return ( - <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.annotationsActive() ? "all" : this.active() || SnappingManager.GetIsDragging() ? undefined : "none" }} > + <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.annotationsActive() ? "all" : this.isContentActive() || SnappingManager.GetIsDragging() ? undefined : "none" }} > <div className={`webBox-container`} style={{ pointerEvents: inactiveLayer ? "none" : undefined }} onContextMenu={this.specificContextMenu}> @@ -493,7 +493,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum PanelHeight={this.panelHeight} dropAction={"alias"} select={emptyFunction} - active={this.active} + isContentActive={this.isContentActive} ContentScaling={returnOne} bringToFront={emptyFunction} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} @@ -524,7 +524,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum mainCont={this._mainCont.current} />} </div > <button className="webBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar" - style={{ right: this.sidebarWidth() + 7, display: !this.active() ? "none" : undefined }} + style={{ right: this.sidebarWidth() + 7, display: !this.isContentActive() ? "none" : undefined }} onPointerDown={e => e.stopPropagation()} onClick={e => this.toggleSidebar()} > <FontAwesomeIcon style={{ color: "white" }} icon={"chevron-left"} size="sm" /> </button> @@ -538,7 +538,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum sidebarAddDocument={this.sidebarAddDocument} moveDocument={this.moveDocument} removeDocument={this.removeDocument} - active={this.active} + isContentActive={this.isContentActive} /> </div>); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index bc746f157..28e58f6d8 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -782,7 +782,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp componentDidMount() { this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. - this.props.contentsActive?.(this.active); + this.props.contentsActive?.(this.isContentActive); this._cachedLinks = DocListCast(this.Document.links); this._disposers.breakupDictation = reaction(() => DocumentManager.Instance.RecordingEvent, this.breakupDictation); this._disposers.autoHeight = reaction(() => this.autoHeight, autoHeight => autoHeight && this.tryUpdateScrollHeight()); @@ -1192,7 +1192,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if ((e.nativeEvent as any).formattedHandled) { console.log("handled"); } - if (!(e.nativeEvent as any).formattedHandled && this.active(true)) { + if (!(e.nativeEvent as any).formattedHandled && this.isContentActive(true)) { const editor = this._editorView!; const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY }); !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0)))); @@ -1422,7 +1422,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const proseHeight = this.ProseRef?.scrollHeight || 0; const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation - const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; + const setScrollHeight = () => { + this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; + }; if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { setScrollHeight(); } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... @@ -1447,7 +1449,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get sidebarHandle() { TraceMobx(); const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length; - return (!annotated && !this.active()) ? (null) : <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} + return (!annotated && !this.isContentActive()) ? (null) : <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : "")) @@ -1467,7 +1469,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp scaleField={this.SidebarKey + "-scale"} isAnnotationOverlay={false} select={emptyFunction} - active={this.annotationsActive} + isContentActive={this.annotationsActive} scaling={this.sidebarContentScaling} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} removeDocument={this.sidebarRemDocument} @@ -1489,7 +1491,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp render() { TraceMobx(); const selected = this.props.isSelected(); - const active = this.active(); + const active = this.isContentActive(); const scale = this.props.hideOnLeave ? 1 : (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index b100701a3..35d4c497d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -158,7 +158,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu } copy = (e: ClipboardEvent) => { - if (this.active() && e.clipboardData) { + if (this.isContentActive() && e.clipboardData) { e.clipboardData.setData("text/plain", this._selectionText); e.preventDefault(); } @@ -375,10 +375,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu this._downX = e.clientX; this._downY = e.clientY; if ((this.Document._viewScale || 1) !== 1) return; - if ((e.button !== 0 || e.altKey) && this.active(true)) { + if ((e.button !== 0 || e.altKey) && this.isContentActive(true)) { this._setPreviewCursor?.(e.clientX, e.clientY, true); } - if (!e.altKey && e.button === 0 && this.props.active(true)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true)) { this.props.select(false); this._marqueeing = [e.clientX, e.clientY]; if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) { @@ -482,7 +482,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu @action onZoomWheel = (e: React.WheelEvent) => { - if (this.active(true)) { + if (this.isContentActive(true)) { e.stopPropagation(); if (e.ctrlKey) { const curScale = Number(this._pdfViewer.currentScaleValue); @@ -528,7 +528,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu PanelWidth={this.panelWidth} dropAction={"alias"} select={emptyFunction} - active={this.annotationsActive} + isContentActive={this.annotationsActive} ContentScaling={this.contentZoom} bringToFront={emptyFunction} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} @@ -542,7 +542,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu </div>; } @computed get pdfViewerDiv() { - return <div className={"pdfViewerDash-text" + (this._textSelecting && (this.props.isSelected() || this.props.active()) ? "-selected" : "")} ref={this._viewer} />; + return <div className={"pdfViewerDash-text" + (this._textSelecting && (this.props.isSelected() || this.props.isContentActive()) ? "-selected" : "")} ref={this._viewer} />; } @computed get contentScaling() { return this.props.ContentScaling?.() || 1; } @computed get standinViews() { diff --git a/src/fields/util.ts b/src/fields/util.ts index ea91cc057..6c9c9d45c 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -19,7 +19,7 @@ function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); } -const tracing = false; +const tracing = true; export function TraceMobx() { tracing && trace(); } |