import { action, computed, makeObservable, observable } from 'mobx'; import * as React from 'react'; import { returnFalse } from '../../ClientUtils'; import { DateField } from '../../fields/DateField'; import { Doc, DocListCast, FieldType, Opt } from '../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData, DocViews } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { RefField } from '../../fields/RefField'; import { toList } from '../../fields/Types'; import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util'; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PinProps } from './PinFuncs'; import { DocumentView } from './nodes/DocumentView'; import { FocusViewOptions } from './nodes/FocusViewOptions'; import { FieldViewProps } from './nodes/FieldView'; import { OpenWhere } from './nodes/OpenWhere'; // import { DocUtils } from '../documents/Documents'; /** * Shared interface among all viewBox'es (ie, react classes that render the contents of a Doc) * Many of these methods only make sense for specific viewBox'es, but they should be written to * be as general as possible */ export class ViewBoxInterface
extends ObservableReactComponent () {
class Component extends ObservableReactComponent () {
class Component extends ViewBoxInterface {
constructor(props: P) {
super(props);
makeObservable(this);
}
ScreenToLocalBoxXf = () => this._props.ScreenToLocalTransform();
get DocumentView() {
return this._props.DocumentView;
}
/**
* This is the document being rendered. In the case of a compound template, it
* may not be the actual document rendered and it also may not be the 'real' root document.
* Rather, it specifies the shared properties of all layouts of the document (eg, x,y,)
*/
get Document() {
return this._props.Document;
}
/**
* This is the document being rendered. It may be a template so it may or may no inherit from the data doc.
*/
@computed get layoutDoc() {
return Doc.Layout(this.Document);
}
/**
* This is the unique data repository for a dcoument that stores the intrinsic document data
*/
@computed get dataDoc() {
return this.Document.isTemplateForField || this.Document.isTemplateDoc ? this._props.TemplateDataDocument ?? this.Document[DocData] : this.Document[DocData];
}
/**
* this is the field key where the primary rendering data is stored for the layout doc (e.g., it's often the 'data' field for a collection, or the 'text' field for rich text)
*/
get fieldKey() {
return this._props.fieldKey;
}
}
return Component;
}
/**
* base class for annotatable views that render the interior contents of a DocumentView
* This does what ViewBoxBaseComponent does and additionally provides accessor for the
* field key where annotations are stored as well as add/move/remove methods for handing
* annotations.
* This also provides methods to determine when the contents should be interactive
* (respond to pointerEvents) such as when the DocumentView container is selected or a
* peer child of the container is selected
* Example views include: PDFBox, ImageBox, MapBox, etc
*/
export function ViewBoxAnnotatableComponent () {
class Component extends ViewBoxInterface {
@observable _annotationKeySuffix = () => 'annotations';
@observable _isAnyChildContentActive = false;
constructor(props: P) {
super(props);
makeObservable(this);
}
ScreenToLocalBoxXf = () => this._props.ScreenToLocalTransform();
get DocumentView() {
return this._props.DocumentView;
}
/**
* This is the document being rendered. In the case of a compound template, it
* may not be the actual document rendered and it also may not be the 'real' root document.
* Rather, it specifies the shared properties of all layouts of the document (eg, x,y,)
*/
@computed get Document() {
return this._props.Document;
}
/**
* This is the document being rendered. It may be a template so it may or may no inherit from the data doc.
*/
@computed get layoutDoc() {
return Doc.Layout(this.Document);
}
/**
* This is the unique data repository for a dcoument that stores the intrinsic document data
*/
@computed get dataDoc() {
return this.Document.isTemplateForField || this.Document.isTemplateDoc ? this._props.TemplateDataDocument ?? this.Document[DocData] : this.Document[DocData];
}
/**
* this is the field key where the primary rendering data is stored for the layout doc (e.g., it's often the 'data' field for a collection, or the 'text' field for rich text)
*/
@computed get fieldKey() {
return this._props.fieldKey;
}
/**
* this is field key where the list of annotations is stored
*/
@computed public get annotationKey() {
return this.fieldKey + (this._annotationKeySuffix() ? '_' + this._annotationKeySuffix() : '');
}
override removeDocument = (docIn: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean, dontAddToRemoved?: boolean): boolean => {
const effectiveAcl = GetEffectiveAcl(this.dataDoc);
const docs = toList(docIn).filter(fdoc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(fdoc) === AclAdmin);
// docs.forEach(doc => doc.annotationOn === this.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true));
const targetDataDoc = this.Document[DocData]; // this.dataDoc; // we want to write to the template, not the actual data doc
const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]);
const toRemove = value.filter(v => docs.includes(v));
if (toRemove.length !== 0) {
const recentlyClosed = this.Document !== Doc.MyRecentlyClosed ? Doc.MyRecentlyClosed : undefined;
toRemove.forEach(rdoc => {
// leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, rdoc, true);
rdoc.embedContainer = undefined;
if (recentlyClosed && !dontAddToRemoved && rdoc.type !== DocumentType.LOADING) {
Doc.AddDocToList(recentlyClosed, 'data', rdoc, undefined, true, true);
Doc.RemoveEmbedding(rdoc, rdoc);
}
});
if (targetDataDoc.isGroup && DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]).length < 2) {
Array.from(targetDataDoc[DocViews])[0]?.ComponentView?.promoteCollection?.();
} else {
this.isAnyChildContentActive() && this._props.select(false);
}
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.
moveDocument = (docs: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean, annotationKey?: string): boolean => {
if (Doc.AreProtosEqual(this._props.Document, targetCollection)) {
return true;
}
const first = toList(docs)[0];
if (!first?._dragOnlyWithinContainer && addDocument !== returnFalse) {
return this.removeDocument(docs, annotationKey, false, true) && addDocument(docs, annotationKey);
}
return false;
};
override addDocument = (docIn: Doc | Doc[], annotationKey?: string): boolean => {
const docs = toList(docIn);
if (this._props.filterAddDocument?.(docs) === false || docs.find(fdoc => Doc.AreProtosEqual(fdoc, this.Document) && Doc.LayoutField(fdoc) === Doc.LayoutField(this.Document))) {
return false;
}
const targetDataDoc = this.Document[DocData]; // this.dataDoc; // we want to write to the template, not the actual data doc
const effectiveAcl = GetEffectiveAcl(targetDataDoc);
if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
return false;
}
const added = docs;
if (added.length) {
if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) {
added.forEach(adoc => {
adoc._dragOnlyWithinContainer = undefined;
if (annotationKey ?? this._annotationKeySuffix()) adoc[DocData].annotationOn = this.Document;
else adoc[DocData].annotationOn = undefined;
Doc.SetContainer(adoc, this.Document);
inheritParentAcls(targetDataDoc, adoc, true);
});
const annoDocs = Doc.Get(targetDataDoc, annotationKey ?? this.annotationKey, true) as List