diff options
Diffstat (limited to 'src/client/views/DocComponent.tsx')
-rw-r--r-- | src/client/views/DocComponent.tsx | 125 |
1 files changed, 76 insertions, 49 deletions
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 6aba4a042..0e5a4f013 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -2,22 +2,62 @@ import { action, computed, makeObservable, observable } from 'mobx'; import * as React from 'react'; import { returnFalse } from '../../Utils'; import { DateField } from '../../fields/DateField'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { Doc, DocListCast, Field, Opt } from '../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util'; import { DocumentType } from '../documents/DocumentTypes'; import { DocUtils } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; -import { Transform } from '../util/Transform'; import { ObservableReactComponent } from './ObservableReactComponent'; import { CollectionFreeFormView } from './collections/collectionFreeForm'; -import { DocumentView } from './nodes/DocumentView'; +import { FieldViewProps, FocusViewOptions } from './nodes/FieldView'; +import { DocumentView, OpenWhere } from './nodes/DocumentView'; +import { PinProps } from './nodes/trails'; +import { RefField } from '../../fields/RefField'; /** - * DocComponent returns a generic React base class used by Doc views (not the 'Box' views that render the contents of Doc views) - * (e.g.,CollectionFreeFormDocumentView, DocumentViewInternal) - * + * 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 interface ViewBoxInterface { + fieldKey?: string; + annotationKey?: string; + updateIcon?: () => void; // updates the icon representation of the document + getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) + restoreView?: (viewSpec: Doc) => boolean; + scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: FocusViewOptions) => Opt<number>; // returns the duration of the focus + brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms) + getView?: (doc: Doc, options: FocusViewOptions) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined + addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox + addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections) + select?: (ctrlKey: boolean, shiftKey: boolean) => void; + focus?: (textAnchor: Doc, options: FocusViewOptions) => Opt<number>; + isAnyChildContentActive?: () => boolean; // is any child content of the document active + onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected + getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) + setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) + playFrom?: (time: number, endTime?: number) => void; + Pause?: () => void; // pause a media document (eg, audio/video) + IsPlaying?: () => boolean; // is a media document playing + TogglePause?: (keep?: boolean) => void; // toggle media document playing state + setFocus?: () => void; // sets input focus to the componentView + setData?: (data: Field | Promise<RefField | undefined>) => boolean; + componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null; + dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void; + incrementalRendering?: () => void; + infoUI?: () => JSX.Element | null; + screenBounds?: () => Opt<{ left: number; top: number; right: number; bottom: number; center?: { X: number; Y: number } }>; + ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; + ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; + snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number }; + search?: (str: string, bwd?: boolean, clear?: boolean) => boolean; +} +/** + * DocComponent returns a React base class used by Doc views with accessors for unpacking he Document,layoutDoc, and dataDoc's + * (note: this should not be used for the 'Box' views that render the contents of Doc views) + * Example derived views: CollectionFreeFormDocumentView, DocumentView, DocumentViewInternal) * */ export interface DocComponentProps { Document: Doc; @@ -26,7 +66,7 @@ export interface DocComponentProps { } export function DocComponent<P extends DocComponentProps>() { class Component extends ObservableReactComponent<React.PropsWithChildren<P>> { - constructor(props: any) { + constructor(props: P) { super(props); makeObservable(this); } @@ -41,28 +81,25 @@ export function DocComponent<P extends DocComponentProps>() { } // This is the data part of a document -- ie, the data that is constant across all views of the document @computed get dataDoc() { - return this._props.Document[DocData]; + return this.Document[DocData]; } } return Component; } /** - * ViewBoxBaseComponent - base class for non-annotatable views that render the interior contents of a DocumentView - * (e.g. InkingStroke, ColorBox) + * base class for non-annotatable views that render the interior contents of a DocumentView. + * this unpacks the Document/layout/data docs as well as the fieldKey being rendered, + * and provides accessors for DocumentView and ScreenToLocalBoxXf + * Example views include: InkingStroke, FontIconBox, EquationBox, etc */ -export interface ViewBoxBaseProps { - Document: Doc; - TemplateDataDocument?: Doc; - DocumentView?: () => DocumentView; - fieldKey: string; - isSelected: () => boolean; - isContentActive: () => boolean | undefined; - ScreenToLocalTransform: () => Transform; - renderDepth: number; -} -export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { +export function ViewBoxBaseComponent<P extends FieldViewProps>() { class Component extends ObservableReactComponent<React.PropsWithChildren<P>> { + constructor(props: P) { + super(props); + makeObservable(this); + } + ScreenToLocalBoxXf = () => this._props.ScreenToLocalTransform(); get DocumentView() { @@ -89,36 +126,27 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { } /** - * DocAnnotatableComponent - base class for annotatable views that render the interior contents of a DocumentView - * (e.g., PdfBox, ImageBox) - * These views should be interactive (respond to pointerEvents) when their conatainer DocumentView is selected + * 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 interface ViewBoxAnnotatableProps { - Document: Doc; - TemplateDataDocument?: Doc; - DocumentView?: () => DocumentView; - fieldKey: string; - 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) - isContentActive: () => boolean | undefined; - select: (isCtrlPressed: boolean) => void; - whenChildContentsActiveChanged: (isActive: boolean) => void; - ScreenToLocalTransform: () => Transform; - isSelected: () => boolean; - renderDepth: number; - isAnnotationOverlay?: boolean; -} -export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() { +export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() { class Component extends ObservableReactComponent<React.PropsWithChildren<P>> { - constructor(props: any) { + @observable _annotationKeySuffix = () => 'annotations'; + @observable _isAnyChildContentActive = false; + + constructor(props: P) { super(props); makeObservable(this); } ScreenToLocalBoxXf = () => this._props.ScreenToLocalTransform(); - @observable _annotationKeySuffix = () => 'annotations'; - @observable _isAnyChildContentActive = false; - get DocumentView() { return this._props.DocumentView; } @@ -139,9 +167,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() @computed get fieldKey() { return this._props.fieldKey; } - - isAnyChildContentActive = () => this._isAnyChildContentActive; - @computed public get annotationKey() { return this.fieldKey + (this._annotationKeySuffix() ? '_' + this._annotationKeySuffix() : ''); } @@ -162,7 +187,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() toRemove.forEach(doc => { leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey); Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); - Doc.RemoveDocFromList(Doc.GetProto(doc), 'proto_embeddings', doc); + Doc.RemoveDocFromList(doc[DocData], 'proto_embeddings', doc); doc.embedContainer = undefined; if (recent) { doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true); @@ -210,8 +235,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) { added.forEach(doc => { doc._dragOnlyWithinContainer = undefined; - if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.Document; - else Doc.GetProto(doc).annotationOn = undefined; + if (annotationKey ?? this._annotationKeySuffix()) doc[DocData].annotationOn = this.Document; + else doc[DocData].annotationOn = undefined; Doc.SetContainer(doc, this.Document); inheritParentAcls(targetDataDoc, doc, true); }); @@ -225,6 +250,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() return true; }; + isAnyChildContentActive = () => this._isAnyChildContentActive; + whenChildContentsActiveChanged = action((isActive: boolean) => this._props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive))); } return Component; |