aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/CollectionBaseView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/CollectionBaseView.tsx')
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx176
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