diff options
Diffstat (limited to 'src/client/views/collections/CollectionBaseView.tsx')
-rw-r--r-- | src/client/views/collections/CollectionBaseView.tsx | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx new file mode 100644 index 000000000..31bdf213e --- /dev/null +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -0,0 +1,176 @@ +import { action, computed } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { ContextMenu } from '../ContextMenu'; +import { FieldViewProps } from '../nodes/FieldView'; +import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Types'; +import { Doc, FieldResult, Opt, DocListCast } from '../../../new_fields/Doc'; +import { listSpec } from '../../../new_fields/Schema'; +import { List } from '../../../new_fields/List'; +import { Id } from '../../../new_fields/RefField'; +import { SelectionManager } from '../../util/SelectionManager'; + +export enum CollectionViewType { + Invalid, + Freeform, + Schema, + Docking, + Tree, +} + +export interface CollectionRenderProps { + addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + removeDocument: (document: Doc) => boolean; + moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; + active: () => boolean; + whenActiveChanged: (isActive: boolean) => void; +} + +export interface CollectionViewProps extends FieldViewProps { + onContextMenu?: (e: React.MouseEvent) => void; + children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | JSX.Element[] | null; + className?: string; + contentRef?: React.Ref<HTMLDivElement>; +} + + +@observer +export class CollectionBaseView extends React.Component<CollectionViewProps> { + get collectionViewType(): CollectionViewType | undefined { + let Document = this.props.Document; + let viewField = Cast(Document.viewType, "number"); + if (viewField !== undefined) { + return viewField; + } else { + return CollectionViewType.Invalid; + } + } + + active = (): boolean => { + var isSelected = this.props.isSelected(); + var topMost = this.props.isTopMost; + return isSelected || this._isChildActive || topMost; + } + + //TODO should this be observable? + private _isChildActive = false; + whenActiveChanged = (isActive: boolean) => { + this._isChildActive = isActive; + this.props.whenActiveChanged(isActive); + } + + createsCycle(documentToAdd: Doc, containerDocument: Doc): boolean { + if (!(documentToAdd instanceof Doc)) { + return false; + } + let data = DocListCast(documentToAdd.data); + for (const doc of data) { + if (this.createsCycle(doc, containerDocument)) { + return true; + } + } + let annots = DocListCast(documentToAdd.annotations); + for (const annot of annots) { + if (this.createsCycle(annot, containerDocument)) { + return true; + } + } + for (let containerProto: Opt<Doc> = containerDocument; containerProto !== undefined; containerProto = FieldValue(containerProto.proto)) { + if (containerProto[Id] === documentToAdd[Id]) { + return true; + } + } + return false; + } + @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; } + + @action.bound + addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { + let props = this.props; + var curPage = NumCast(props.Document.curPage, -1); + Doc.SetOnPrototype(doc, "page", curPage); + if (curPage >= 0) { + Doc.SetOnPrototype(doc, "annotationOn", props.Document); + } + if (!this.createsCycle(doc, props.Document)) { + //TODO This won't create the field if it doesn't already exist + const value = Cast(props.Document[props.fieldKey], listSpec(Doc)); + let alreadyAdded = true; + if (value !== undefined) { + if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) { + alreadyAdded = false; + value.push(doc); + } + } else { + alreadyAdded = false; + Doc.SetOnPrototype(this.props.Document, this.props.fieldKey, new List([doc])); + } + // set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument? + if (!alreadyAdded && (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid)) { + let zoom = NumCast(this.props.Document.scale, 1); + Doc.SetOnPrototype(doc, "zoomBasis", zoom); + } + } + return true; + } + + @action.bound + removeDocument(doc: Doc): boolean { + const props = this.props; + //TODO This won't create the field if it doesn't already exist + const value = Cast(props.Document[props.fieldKey], listSpec(Doc), []); + let index = -1; + for (let i = 0; i < value.length; i++) { + let v = value[i]; + if (v instanceof Doc && v[Id] === doc[Id]) { + index = i; + break; + } + } + PromiseValue(Cast(doc.annotationOn, Doc)).then((annotationOn) => { + if (annotationOn === props.Document) { + doc.annotationOn = undefined; + } + }); + + if (index !== -1) { + value.splice(index, 1); + + // SelectionManager.DeselectAll() + ContextMenu.Instance.clearItems(); + return true; + } + return false; + } + + @action.bound + moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { + if (this.props.Document === targetCollection) { + return true; + } + if (this.removeDocument(doc)) { + SelectionManager.DeselectAll(); + return addDocument(doc); + } + return false; + } + + render() { + const props: CollectionRenderProps = { + addDocument: this.addDocument, + removeDocument: this.removeDocument, + moveDocument: this.moveDocument, + active: this.active, + whenActiveChanged: this.whenActiveChanged, + }; + const viewtype = this.collectionViewType; + return ( + <div className={this.props.className || "collectionView-cont"} + style={{ borderRadius: "inherit", pointerEvents: "all" }} + onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}> + {viewtype !== undefined ? this.props.children(viewtype, props) : (null)} + </div> + ); + } + +}
\ No newline at end of file |