aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorandrewdkim <adkim414@gmail.com>2019-11-12 17:30:02 -0500
committerandrewdkim <adkim414@gmail.com>2019-11-12 17:30:02 -0500
commitfb3a8258298589b911c73c18fdb086c8815e27c8 (patch)
treec97973e8cc2d828b9ce7486b421fdcf2985c49e2 /src/client/views/collections
parent4d4db3aecf54a501a981c239feacdfddfe71152c (diff)
parentecbe527575bab2cb5f1ced278039ec0a6fc50809 (diff)
merge from master
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionBaseView.scss12
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx179
-rw-r--r--src/client/views/collections/CollectionDockingView.scss3
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx83
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx367
-rw-r--r--src/client/views/collections/CollectionPDFView.scss11
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx37
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx9
-rw-r--r--src/client/views/collections/CollectionSchemaMovableTableHOC.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss27
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx449
-rw-r--r--src/client/views/collections/CollectionStackingView.scss110
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx220
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx164
-rw-r--r--src/client/views/collections/CollectionSubView.tsx88
-rw-r--r--src/client/views/collections/CollectionTreeView.scss8
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx197
-rw-r--r--src/client/views/collections/CollectionVideoView.scss51
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx115
-rw-r--r--src/client/views/collections/CollectionView.scss26
-rw-r--r--src/client/views/collections/CollectionView.tsx213
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx83
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx84
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx69
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx251
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss7
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx76
32 files changed, 1409 insertions, 1552 deletions
diff --git a/src/client/views/collections/CollectionBaseView.scss b/src/client/views/collections/CollectionBaseView.scss
deleted file mode 100644
index 583e6f6ca..000000000
--- a/src/client/views/collections/CollectionBaseView.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-@import "../globalCssVariables";
-#collectionBaseView {
- border-width: 0;
- border-color: $light-color-secondary;
- border-style: solid;
- border-radius: 0 0 $border-radius $border-radius;
- box-sizing: border-box;
- border-radius: inherit;
- width:100%;
- height:100%;
- overflow: auto;
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
deleted file mode 100644
index 0168c466f..000000000
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ /dev/null
@@ -1,179 +0,0 @@
-import { action, computed, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc } from '../../../new_fields/Doc';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
-import { listSpec } from '../../../new_fields/Schema';
-import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from '../../../new_fields/Types';
-import { DocumentManager } from '../../util/DocumentManager';
-import { SelectionManager } from '../../util/SelectionManager';
-import { ContextMenu } from '../ContextMenu';
-import { FieldViewProps } from '../nodes/FieldView';
-import './CollectionBaseView.scss';
-import { DateField } from '../../../new_fields/DateField';
-
-export enum CollectionViewType {
- Invalid,
- Freeform,
- Schema,
- Docking,
- Tree,
- Stacking,
- Masonry,
- Pivot,
-}
-
-export namespace CollectionViewType {
-
- const stringMapping = new Map<string, CollectionViewType>([
- ["invalid", CollectionViewType.Invalid],
- ["freeform", CollectionViewType.Freeform],
- ["schema", CollectionViewType.Schema],
- ["docking", CollectionViewType.Docking],
- ["tree", CollectionViewType.Tree],
- ["stacking", CollectionViewType.Stacking],
- ["masonry", CollectionViewType.Masonry],
- ["pivot", CollectionViewType.Pivot]
- ]);
-
- export const valueOf = (value: string) => {
- return stringMapping.get(value.toLowerCase());
- };
-
-}
-
-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 | (JSX.Element | null)[];
- className?: string;
- contentRef?: React.Ref<HTMLDivElement>;
-}
-
-@observer
-export class CollectionBaseView extends React.Component<CollectionViewProps> {
- @observable private static _safeMode = false;
- static InSafeMode() { return this._safeMode; }
- static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
- get collectionViewType(): CollectionViewType | undefined {
- let Document = this.props.Document;
- let viewField = Cast(Document.viewType, "number");
- if (CollectionBaseView._safeMode) {
- if (viewField === CollectionViewType.Freeform) {
- return CollectionViewType.Tree;
- }
- if (viewField === CollectionViewType.Invalid) {
- return CollectionViewType.Freeform;
- }
- }
- if (viewField !== undefined) {
- return viewField;
- } else {
- return CollectionViewType.Invalid;
- }
- }
-
- @computed get dataDoc() { return Doc.fieldExtensionDoc(this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
- @computed get dataField() { return this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; }
-
- active = (): boolean => {
- var isSelected = this.props.isSelected();
- return isSelected || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0;
- }
-
- //TODO should this be observable?
- private _isChildActive = false;
- whenActiveChanged = (isActive: boolean) => {
- this._isChildActive = isActive;
- this.props.whenActiveChanged(isActive);
- }
-
- @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
-
- @action.bound
- addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
- var curPage = NumCast(this.props.Document.curPage, -1);
- Doc.GetProto(doc).page = curPage;
- if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer
- Doc.GetProto(doc).annotationOn = this.props.Document;
- }
- let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
- let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
- const value = Cast(targetDataDoc[targetField], listSpec(Doc));
- if (value !== undefined) {
- if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) {
- value.push(doc);
- }
- } else {
- Doc.GetProto(targetDataDoc)[targetField] = new List([doc]);
- }
- Doc.GetProto(doc).lastOpened = new DateField;
- return true;
- }
-
- @action.bound
- removeDocument(doc: Doc): boolean {
- let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
- docView && SelectionManager.DeselectDoc(docView);
- //TODO This won't create the field if it doesn't already exist
- let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
- let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
- let value = Cast(targetDataDoc[targetField], listSpec(Doc), []);
- let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
- index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
- PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn =>
- annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined));
-
- if (index !== -1) {
- value.splice(index, 1);
-
- // SelectionManager.DeselectAll()
- ContextMenu.Instance.clearItems();
- 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, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
- if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
- return true;
- }
- return this.removeDocument(doc) ? addDocument(doc) : 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 id="collectionBaseView"
- style={{
- pointerEvents: this.props.Document.isBackground ? "none" : "all",
- boxShadow: this.props.Document.isBackground ? undefined : `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
- }}
- className={this.props.className || "collectionView-cont"}
- onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
- {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
- </div>
- );
- }
-
-}
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 6f5abd05b..12f54d69d 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -25,6 +25,9 @@
top: 0;
left: 0;
overflow: hidden;
+ .lm_content {
+ background: white;
+ }
.lm_controls>li {
opacity: 0.6;
transform: scale(1.2);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index b047e77a8..42d372f4a 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -3,13 +3,13 @@ import { faFile } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, computed, Lambda, observable, reaction } from "mobx";
+import { action, Lambda, observable, reaction, computed, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
import Measure from "react-measure";
import * as GoldenLayout from "../../../client/goldenLayout";
import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCast, Field, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
import { Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { FieldId } from "../../../new_fields/RefField";
@@ -31,12 +31,14 @@ import { SubCollectionViewProps } from "./CollectionSubView";
import React = require("react");
import { ButtonSelector } from './ParentDocumentSelector';
import { DocumentType } from '../../documents/DocumentTypes';
+import { ComputedField } from '../../../new_fields/ScriptField';
library.add(faFile);
const _global = (window /* browser */ || global /* node */) as any;
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
- public static Instance: CollectionDockingView;
+ @observable public static Instances: CollectionDockingView[] = [];
+ @computed public static get Instance() { return CollectionDockingView.Instances[0]; }
public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number) {
return {
type: 'react-component',
@@ -51,7 +53,11 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
};
}
- private _goldenLayout: any = null;
+ @computed public get initialized() {
+ return this._goldenLayout !== null;
+ }
+
+ @observable private _goldenLayout: any = null;
private _containerRef = React.createRef<HTMLDivElement>();
private _flush: boolean = false;
private _ignoreStateChange = "";
@@ -60,7 +66,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
constructor(props: SubCollectionViewProps) {
super(props);
- !CollectionDockingView.Instance && (CollectionDockingView.Instance = this);
+ runInAction(() => !CollectionDockingView.Instances ? CollectionDockingView.Instances = [this] : CollectionDockingView.Instances.push(this));
//Why is this here?
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
@@ -251,7 +257,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
var config = StrCast(this.props.Document.dockingConfig);
if (config) {
if (!this._goldenLayout) {
- this._goldenLayout = new GoldenLayout(JSON.parse(config));
+ runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
}
else {
if (config === JSON.stringify(this._goldenLayout.toConfig())) {
@@ -264,7 +270,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._goldenLayout.unbind('stackCreated', this.stackCreated);
} catch (e) { }
this._goldenLayout.destroy();
- this._goldenLayout = new GoldenLayout(JSON.parse(config));
+ runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
}
this._goldenLayout.on('itemDropped', this.itemDropped);
this._goldenLayout.on('tabCreated', this.tabCreated);
@@ -292,7 +298,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// Because this is in a set timeout, if this component unmounts right after mounting,
// we will leak a GoldenLayout, because we try to destroy it before we ever create it
setTimeout(() => this.setupGoldenLayout(), 1);
- DocListCast((CurrentUserUtils.UserDocument.workspaces as Doc).data).map(d => d.workspaceBrush = false);
+ let userDoc = CurrentUserUtils.UserDocument;
+ userDoc && DocListCast((userDoc.workspaces as Doc).data).map(d => d.workspaceBrush = false);
this.props.Document.workspaceBrush = true;
}
this._ignoreStateChange = "";
@@ -311,13 +318,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
} catch (e) {
}
- if (this._goldenLayout) this._goldenLayout.destroy();
- this._goldenLayout = null;
+ this._goldenLayout && this._goldenLayout.destroy();
+ runInAction(() => {
+ CollectionDockingView.Instances.splice(CollectionDockingView.Instances.indexOf(this), 1);
+ this._goldenLayout = null;
+ });
window.removeEventListener('resize', this.onResize);
- if (this.reactionDisposer) {
- this.reactionDisposer();
- }
+ this.reactionDisposer && this.reactionDisposer();
}
@action
onResize = (event: any) => {
@@ -453,8 +461,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
let theDoc = doc;
CollectionDockingView.Instance._removedDocs.push(theDoc);
- const recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc);
- if (recent) {
+ let userDoc = CurrentUserUtils.UserDocument;
+ let recent: Doc | undefined;
+ if (userDoc && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
Doc.AddDocToList(recent, "data", doc, undefined, true, true);
}
SelectionManager.DeselectAll();
@@ -486,12 +495,13 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
.off('click') //unbind the current click handler
.click(action(async function () {
//if (confirm('really close this?')) {
- const recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc);
+
stack.remove();
stack.contentItems.forEach(async (contentItem: any) => {
let doc = await DocServer.GetRefField(contentItem.config.props.documentId);
if (doc instanceof Doc) {
- if (recent) {
+ let recent: Doc | undefined;
+ if (CurrentUserUtils.UserDocument && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
Doc.AddDocToList(recent, "data", doc, undefined, true, true);
}
let theDoc = doc;
@@ -564,11 +574,14 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
//add this new doc to props.Document
let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
if (curPres) {
+ let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
+ Doc.GetProto(pinDoc).presentationTargetDoc = doc;
+ Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()');
const data = Cast(curPres.data, listSpec(Doc));
if (data) {
- data.push(doc);
+ data.push(pinDoc);
} else {
- curPres.data = new List([doc]);
+ curPres.data = new List([pinDoc]);
}
if (!DocumentManager.Instance.getDocumentView(curPres)) {
this.addDocTab(curPres, undefined, "onRight");
@@ -602,19 +615,20 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
}
- panelWidth = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth()));
- panelHeight = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), this.nativeHeight()));
+ get layoutDoc() { return this._document && Doc.Layout(this._document);}
+ panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc.width), NumCast(this.layoutDoc.nativeWidth)), this._panelWidth) : this._panelWidth;
+ panelHeight = () => this._panelHeight;
- nativeWidth = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0;
- nativeHeight = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0;
+ nativeWidth = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!.fitWidth ? NumCast(this.layoutDoc!.nativeWidth) || this._panelWidth : 0;
+ nativeHeight = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!.fitWidth ? NumCast(this.layoutDoc!.nativeHeight) || this._panelHeight : 0;
contentScaling = () => {
- if (this._document!.type === DocumentType.PDF) {
- if ((this._document && this._document.fitWidth) ||
- this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) {
- return this._panelWidth / NumCast(this._document!.nativeWidth);
+ if (this.layoutDoc!.type === DocumentType.PDF) {
+ if ((this.layoutDoc && this.layoutDoc.fitWidth) ||
+ this._panelHeight / NumCast(this.layoutDoc!.nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!.nativeWidth)) {
+ return this._panelWidth / NumCast(this.layoutDoc!.nativeWidth);
} else {
- return this._panelHeight / NumCast(this._document!.nativeHeight);
+ return this._panelHeight / NumCast(this.layoutDoc!.nativeHeight);
}
}
const nativeH = this.nativeHeight();
@@ -632,7 +646,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
return Transform.Identity();
}
- get previewPanelCenteringOffset() { return this.nativeWidth() && !BoolCast(this._document!.ignoreAspect) ? (this._panelWidth - this.nativeWidth() / this.ScreenToLocalTransform().Scale) / 2 : 0; }
+ get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string) => {
SelectionManager.DeselectAll();
@@ -647,7 +661,10 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
}
}
- docView(document: Doc) {
+
+ @computed get docView() {
+ if (!this._document) return (null);
+ const document = this._document;
let resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
return <DocumentView key={document[Id]}
Document={document}
@@ -674,13 +691,13 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
render() {
- return (!this._isActive || !this._document) ? (null) :
+ return (!this._isActive || !this.layoutDoc) ? (null) :
(<div className="collectionDockingView-content" ref={ref => this._mainCont = ref}
style={{
transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`,
- height: this._document && this._document.fitWidth ? undefined : "100%"
+ height: this.layoutDoc && this.layoutDoc.fitWidth ? undefined : "100%"
}}>
- {this.docView(this._document)}
+ {this.docView}
</div >);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
new file mode 100644
index 000000000..52ebfafd3
--- /dev/null
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -0,0 +1,367 @@
+import React = require("react");
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faPalette } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, observable } from "mobx";
+import { observer } from "mobx-react";
+import Measure from "react-measure";
+import { Doc } from "../../../new_fields/Doc";
+import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { StrCast } from "../../../new_fields/Types";
+import { numberRange } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { DragManager } from "../../util/DragManager";
+import { CompileScript } from "../../util/Scripting";
+import { SelectionManager } from "../../util/SelectionManager";
+import { Transform } from "../../util/Transform";
+import { undoBatch } from "../../util/UndoManager";
+import { anchorPoints, Flyout } from "../DocumentDecorations";
+import { EditableView } from "../EditableView";
+import { CollectionStackingView } from "./CollectionStackingView";
+import "./CollectionStackingView.scss";
+import { undo } from "prosemirror-history";
+
+library.add(faPalette);
+
+interface CMVFieldRowProps {
+ rows: () => number;
+ headings: () => object[];
+ heading: string;
+ headingObject: SchemaHeaderField | undefined;
+ docList: Doc[];
+ parent: CollectionStackingView;
+ type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined;
+ createDropTarget: (ele: HTMLDivElement) => void;
+ screenToLocalTransform: () => Transform;
+ setDocHeight: (key: string, thisHeight: number) => void;
+}
+
+@observer
+export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowProps> {
+ @observable private _background = "inherit";
+ @observable private _createAliasSelected: boolean = false;
+ @observable private _collapsed: boolean = false;
+ @observable private _headingsHack: number = 1;
+ @observable private _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
+ @observable private _color = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+
+ private _dropDisposer?: DragManager.DragDropDisposer;
+ private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
+ private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 };
+ private _contRef: React.RefObject<HTMLDivElement> = React.createRef();
+ private _sensitivity: number = 16;
+ private _counter: number = 0;
+
+
+ createRowDropRef = (ele: HTMLDivElement | null) => {
+ this._dropDisposer && this._dropDisposer();
+ if (ele) {
+ this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.rowDrop.bind(this) } });
+ }
+ }
+
+ getTrueHeight = () => {
+ if (this._collapsed) {
+ this.props.setDocHeight(this._heading, 20);
+ } else {
+ let rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; //+ 15 accounts for the group header
+ let transformScale = this.props.screenToLocalTransform().Scale;
+ let trueHeight = rawHeight * transformScale;
+ this.props.setDocHeight(this._heading, trueHeight);
+ }
+ }
+
+ @undoBatch
+ rowDrop = action((e: Event, de: DragManager.DropEvent) => {
+ this._createAliasSelected = false;
+ if (de.data instanceof DragManager.DocumentDragData) {
+ (this.props.parent.Document.dropConverter instanceof ScriptField) &&
+ this.props.parent.Document.dropConverter.script.run({ dragData: de.data });
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let castedValue = this.getValue(this._heading);
+ de.data.droppedDocuments.forEach(d => d[key] = castedValue);
+ this.props.parent.drop(e, de);
+ e.stopPropagation();
+ }
+ });
+
+ getValue = (value: string): any => {
+ let parsed = parseInt(value);
+ if (!isNaN(parsed)) return parsed;
+ if (value.toLowerCase().indexOf("true") > -1) return true;
+ if (value.toLowerCase().indexOf("false") > -1) return false;
+ return value;
+ }
+
+ @action
+ headingChanged = (value: string, shiftDown?: boolean) => {
+ this._createAliasSelected = false;
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let castedValue = this.getValue(value);
+ if (castedValue) {
+ if (this.props.parent.sectionHeaders) {
+ if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
+ return false;
+ }
+ }
+ this.props.docList.forEach(d => d[key] = castedValue);
+ if (this.props.headingObject) {
+ this.props.headingObject.setHeading(castedValue.toString());
+ this._heading = this.props.headingObject.heading;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @action
+ changeColumnColor = (color: string) => {
+ this._createAliasSelected = false;
+ if (this.props.headingObject) {
+ this.props.headingObject.setColor(color);
+ this._color = color;
+ }
+ }
+
+ pointerEnteredRow = action(() => SelectionManager.GetIsDragging() && (this._background = "#b4b4b4"));
+
+ @action
+ pointerLeaveRow = () => {
+ this._createAliasSelected = false;
+ this._background = "inherit";
+ document.removeEventListener("pointermove", this.startDrag);
+ }
+
+ @action
+ addDocument = (value: string, shiftDown?: boolean) => {
+ this._createAliasSelected = false;
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let newDoc = Docs.Create.TextDocument({ height: 18, width: 200, title: value });
+ newDoc[key] = this.getValue(this.props.heading);
+ return this.props.parent.props.addDocument(newDoc);
+ }
+
+ deleteRow = undoBatch(action(() => {
+ this._createAliasSelected = false;
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ this.props.docList.forEach(d => d[key] = undefined);
+ if (this.props.parent.sectionHeaders && this.props.headingObject) {
+ let index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
+ this.props.parent.sectionHeaders.splice(index, 1);
+ }
+ }));
+
+ @action
+ collapseSection = () => {
+ this._createAliasSelected = false;
+ if (this.props.headingObject) {
+ this._headingsHack++;
+ this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed);
+ this.toggleVisibility();
+ }
+ }
+
+ startDrag = (e: PointerEvent) => {
+ let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+ if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
+ let alias = Doc.MakeAlias(this.props.parent.props.Document);
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let value = this.getValue(this._heading);
+ value = typeof value === "string" ? `"${value}"` : value;
+ let script = `return doc.${key} === ${value}`;
+ let compiled = CompileScript(script, { params: { doc: Doc.name } });
+ if (compiled.compiled) {
+ let scriptField = new ScriptField(compiled);
+ alias.viewSpecScript = scriptField;
+ let dragData = new DragManager.DocumentDragData([alias]);
+ DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY);
+ }
+
+ e.stopPropagation();
+ document.removeEventListener("pointermove", this.startDrag);
+ document.removeEventListener("pointerup", this.pointerUp);
+ }
+ }
+
+ pointerUp = (e: PointerEvent) => {
+ e.stopPropagation();
+ e.preventDefault();
+
+ document.removeEventListener("pointermove", this.startDrag);
+ document.removeEventListener("pointerup", this.pointerUp);
+ }
+
+ @action
+ headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
+ e.stopPropagation();
+ e.preventDefault();
+
+ let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
+ this._startDragPosition = { x: dx, y: dy };
+
+ if (this._createAliasSelected) {
+ document.removeEventListener("pointermove", this.startDrag);
+ document.addEventListener("pointermove", this.startDrag);
+ document.removeEventListener("pointerup", this.pointerUp);
+ document.addEventListener("pointerup", this.pointerUp);
+ }
+ this._createAliasSelected = false;
+ }
+
+ renderColorPicker = () => {
+ let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+
+ let pink = PastelSchemaPalette.get("pink2");
+ let purple = PastelSchemaPalette.get("purple4");
+ let blue = PastelSchemaPalette.get("bluegreen1");
+ let yellow = PastelSchemaPalette.get("yellow4");
+ let red = PastelSchemaPalette.get("red2");
+ let green = PastelSchemaPalette.get("bluegreen7");
+ let cyan = PastelSchemaPalette.get("bluegreen5");
+ let orange = PastelSchemaPalette.get("orange1");
+ let gray = "#f1efeb";
+
+ return (
+ <div className="collectionStackingView-colorPicker">
+ <div className="colorOptions">
+ <div className={"colorPicker" + (selected === pink ? " active" : "")} style={{ backgroundColor: pink }} onClick={() => this.changeColumnColor(pink!)}></div>
+ <div className={"colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.changeColumnColor(purple!)}></div>
+ <div className={"colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.changeColumnColor(blue!)}></div>
+ <div className={"colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.changeColumnColor(yellow!)}></div>
+ <div className={"colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.changeColumnColor(red!)}></div>
+ <div className={"colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.changeColumnColor(gray)}></div>
+ <div className={"colorPicker" + (selected === green ? " active" : "")} style={{ backgroundColor: green }} onClick={() => this.changeColumnColor(green!)}></div>
+ <div className={"colorPicker" + (selected === cyan ? " active" : "")} style={{ backgroundColor: cyan }} onClick={() => this.changeColumnColor(cyan!)}></div>
+ <div className={"colorPicker" + (selected === orange ? " active" : "")} style={{ backgroundColor: orange }} onClick={() => this.changeColumnColor(orange!)}></div>
+ </div>
+ </div>
+ );
+ }
+
+ toggleAlias = action(() => this._createAliasSelected = true);
+ toggleVisibility = action(() => this._collapsed = !this._collapsed);
+
+ renderMenu = () => {
+ let selected = this._createAliasSelected;
+ return (<div className="collectionStackingView-optionPicker">
+ <div className="optionOptions">
+ <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
+ <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.deleteRow}>Delete</div>
+ </div>
+ </div>);
+ }
+
+ handleResize = (size: any) => {
+ if (++this._counter !== 1) {
+ this.getTrueHeight();
+ }
+ }
+
+
+ render() {
+ let rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap))));
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let heading = this._heading;
+ let style = this.props.parent;
+ let evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
+ let headerEditableViewProps = {
+ GetValue: () => evContents,
+ SetValue: this.headingChanged,
+ contents: evContents,
+ oneLine: true,
+ HeadingObject: this.props.headingObject,
+ HeadingsHack: this._headingsHack,
+ toggle: this.toggleVisibility,
+ color: this._color
+ };
+ let newEditableViewProps = {
+ GetValue: () => "",
+ SetValue: this.addDocument,
+ contents: "+ NEW",
+ HeadingObject: this.props.headingObject,
+ HeadingsHack: this._headingsHack,
+ toggle: this.toggleVisibility,
+ color: this._color
+ };
+ let headingView = this.props.parent.props.Document.miniHeaders ?
+ <div className="collectionStackingView-miniHeader" style={{ width: "100%" }}>
+ {<EditableView {...headerEditableViewProps} />}
+ </div> :
+ this.props.headingObject ?
+ <div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
+ <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
+ title={evContents === `NO ${key.toUpperCase()} VALUE` ?
+ `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
+ style={{
+ width: "100%",
+ background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "lightgrey",
+ color: "grey"
+ }}>
+ {<EditableView {...headerEditableViewProps} />}
+ {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
+ <div className="collectionStackingView-sectionColor">
+ <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
+ <button className="collectionStackingView-sectionColorButton">
+ <FontAwesomeIcon icon="palette" size="lg" />
+ </button>
+ </ Flyout >
+ </div>
+ }
+ <button className="collectionStackingView-sectionDelete" onClick={this.collapseSection}>
+ <FontAwesomeIcon icon={this._collapsed ? "chevron-down" : "chevron-up"} size="lg" />
+ </button>
+ {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
+ <div className="collectionStackingView-sectionOptions">
+ <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
+ <button className="collectionStackingView-sectionOptionButton">
+ <FontAwesomeIcon icon="ellipsis-v" size="lg" />
+ </button>
+ </Flyout>
+ </div>
+ }
+ </div>
+ </div > : (null);
+ const background = this._background; //to account for observables in Measure
+ const collapsed = this._collapsed;
+ let chromeStatus = this.props.parent.props.Document.chromeStatus;
+ return (
+ <Measure offset onResize={this.handleResize}>
+ {({ measureRef }) => {
+ return <div ref={measureRef}>
+ <div className="collectionStackingView-masonrySection"
+ key={heading = "empty"}
+ style={{ width: this.props.parent.NodeWidth, background }}
+ ref={this.createRowDropRef}
+ onPointerEnter={this.pointerEnteredRow}
+ onPointerLeave={this.pointerLeaveRow}
+ >
+ {headingView}
+ {collapsed ? (null) :
+ < div style={{ position: "relative" }}>
+ <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
+ ref={this._contRef}
+ style={{
+ padding: `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px`,
+ width: this.props.parent.NodeWidth,
+ gridGap: this.props.parent.gridGap,
+ gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""),
+ }}>
+ {this.props.parent.children(this.props.docList)}
+ {this.props.parent.columnDragger}
+ </div>
+ {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
+ <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
+ style={{ width: style.columnWidth / style.numGroupColumns }}>
+ <EditableView {...newEditableViewProps} />
+ </div> : null
+ }
+ </div>
+ }
+ </div >
+ </div>;
+ }}
+ </Measure>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
deleted file mode 100644
index 62ec8a5be..000000000
--- a/src/client/views/collections/CollectionPDFView.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-.collectionPdfView-cont {
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
- z-index: -1;
- overflow: hidden !important;
-}
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
deleted file mode 100644
index cc8142ec0..000000000
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { trace } from "mobx";
-import { observer } from "mobx-react";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { emptyFunction } from "../../../Utils";
-import { ContextMenu } from "../ContextMenu";
-import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView";
-import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import "./CollectionPDFView.scss";
-import React = require("react");
-
-
-@observer
-export class CollectionPDFView extends React.Component<FieldViewProps> {
- public static LayoutString(fieldKey: string = "data", fieldExt: string = "annotations") {
- return FieldView.LayoutString(CollectionPDFView, fieldKey, fieldExt);
- }
-
- onContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- ContextMenu.Instance.addItem({ description: "PDFOptions", event: emptyFunction, icon: "file-pdf" });
- }
- }
-
- subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
- return (<CollectionFreeFormView {...this.props} {...renderProps} CollectionView={this} chromeCollapsed={true} />);
- }
-
- render() {
- trace();
- return (
- <CollectionBaseView {...this.props} className={"collectionPdfView-cont"} onContextMenu={this.onContextMenu}>
- {this.subView}
- </CollectionBaseView>
- );
- }
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 179e44266..54a36f691 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -13,17 +13,13 @@ import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../globalCssVariables.s
import '../DocumentDecorations.scss';
import { EditableView } from "../EditableView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import { CollectionPDFView } from "./CollectionPDFView";
import "./CollectionSchemaView.scss";
-import { CollectionVideoView } from "./CollectionVideoView";
import { CollectionView } from "./CollectionView";
import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types";
import { Docs } from "../../documents/Documents";
-import { DocumentContentsView } from "../nodes/DocumentContentsView";
import { SelectionManager } from "../../util/SelectionManager";
import { library } from '@fortawesome/fontawesome-svg-core';
import { faExpand } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { KeyCodes } from "../../northstar/utils/KeyCodes";
import { undoBatch } from "../../util/UndoManager";
@@ -34,8 +30,8 @@ export interface CellProps {
row: number;
col: number;
rowProps: CellInfo;
- CollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
- ContainingCollection: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
+ CollectionView: Opt<CollectionView>;
+ ContainingCollection: Opt<CollectionView>;
Document: Doc;
fieldKey: string;
renderDepth: number;
@@ -148,7 +144,6 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
Document: this.props.rowProps.original,
DataDoc: this.props.rowProps.original,
fieldKey: this.props.rowProps.column.id as string,
- fieldExt: "",
ruleProvider: undefined,
ContainingCollectionView: this.props.CollectionView,
ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 39abc41ec..274c8b6d1 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -229,7 +229,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
<div className="collectionSchema-row-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
<ReactTableDefaults.TrComponent>
<div className="row-dragger">
- <div className="row-option" onClick={() => this.props.removeDoc(this.props.rowInfo.original)}><FontAwesomeIcon icon="trash" size="sm" /></div>
+ <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
<div className="row-option" style={{ cursor: "grab" }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
</div>
{children}
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index e0cedc210..cb95dcbbc 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -10,6 +10,7 @@
top: 0;
width: 100%;
height: 100%;
+ margin-top: 0;
transition: top 0.5s;
display: flex;
justify-content: space-between;
@@ -38,30 +39,6 @@
}
}
-.collectionSchemaView-previewRegion {
- position: relative;
- background: $light-color;
- height: auto !important;
-
- .collectionSchemaView-previewDoc {
- height: 100%;
- width: 100%;
- position: absolute;
- }
-
- .collectionSchemaView-input {
- position: absolute;
- max-width: 150px;
- width: 100%;
- bottom: 0px;
- }
-
- .documentView-node:first-child {
- position: relative;
- background: $light-color;
- }
-}
-
.ReactTable {
width: 100%;
background: white;
@@ -470,7 +447,7 @@ button.add-column {
overflow: visible;
}
-.sub {
+.reactTable-sub {
padding: 10px 30px;
background-color: rgb(252, 252, 252);
width: calc(100% - 50px);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 8d931f812..203c68463 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,39 +1,34 @@
import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCog, faPlus, faTable, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons';
+import { faCog, faPlus, faSortDown, faSortUp, faTable } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, trace, untracked } from "mobx";
+import { action, computed, observable, untracked } from "mobx";
import { observer } from "mobx-react";
-import ReactTable, { CellInfo, ComponentPropsGetterR, Column, RowInfo, ResizedChangeFunction, Resize } from "react-table";
+import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
import "react-table/react-table.css";
-import { emptyFunction, returnOne, returnEmptyString } from "../../../Utils";
import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
-import { Docs, DocumentOptions } from "../../documents/Documents";
+import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { ComputedField } from "../../../new_fields/ScriptField";
import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { Docs, DocumentOptions } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
import { Gateway } from "../../northstar/manager/Gateway";
-import { DragManager } from "../../util/DragManager";
-import { CompileScript, ts, Transformer } from "../../util/Scripting";
+import { CompileScript, Transformer, ts } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
+import { undoBatch } from "../../util/UndoManager";
import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
import { ContextMenu } from "../ContextMenu";
import '../DocumentDecorations.scss';
-import { DocumentView } from "../nodes/DocumentView";
-import { CollectionPDFView } from "./CollectionPDFView";
+import { CellProps, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
+import { CollectionSchemaAddColumnHeader, CollectionSchemaHeader } from "./CollectionSchemaHeaders";
+import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
-import { CollectionVideoView } from "./CollectionVideoView";
import { CollectionView } from "./CollectionView";
-import { undoBatch } from "../../util/UndoManager";
-import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders";
-import { CellProps, CollectionSchemaCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell } from "./CollectionSchemaCells";
-import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
-import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { DocumentType } from "../../documents/DocumentTypes";
-
+import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
library.add(faCog, faPlus, faSortUp, faSortDown);
library.add(faTable);
@@ -51,7 +46,7 @@ const columnTypes: Map<string, ColumnType> = new Map([
["title", ColumnType.String],
["x", ColumnType.Number], ["y", ColumnType.Number], ["width", ColumnType.Number], ["height", ColumnType.Number],
["nativeWidth", ColumnType.Number], ["nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
- ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["zIndex", ColumnType.Number]
+ ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
]);
@observer
@@ -75,20 +70,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
super.CreateDropTarget(ele);
}
- isFocused = (doc: Doc): boolean => {
- if (!this.props.isSelected()) return false;
- return doc === this._focusedTable;
- }
+ isFocused = (doc: Doc): boolean => this.props.isSelected() && doc === this._focusedTable;
- @action
- setFocused = (doc: Doc): void => {
- this._focusedTable = doc;
- }
+ @action setFocused = (doc: Doc) => this._focusedTable = doc;
- @action
- setPreviewDoc = (doc: Doc): void => {
- this.previewDoc = doc;
- }
+ @action setPreviewDoc = (doc: Doc) => this.previewDoc = doc;
+
+ @undoBatch
+ @action setPreviewScript = (script: string) => this.previewScript = script
//toggles preview side-panel of schema
@action
@@ -130,17 +119,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
}
- onWheel = (e: React.WheelEvent): void => {
- if (this.props.active()) {
- e.stopPropagation();
- }
- }
-
@computed
get previewDocument(): Doc | undefined {
- let selected = this.previewDoc;
- let pdc = selected ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
- return pdc;
+ return this.previewDoc ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(this.previewDoc[this.previewScript], Doc)) : this.previewDoc) : undefined;
}
getPreviewTransform = (): Transform => {
@@ -157,15 +138,17 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
get previewPanel() {
let layoutDoc = this.previewDocument ? Doc.expandTemplateLayout(this.previewDocument, this.props.DataDoc) : undefined;
return <div ref={this.createTarget}>
- <CollectionSchemaPreview
+ <ContentFittingDocumentView
Document={layoutDoc}
DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
+ fieldKey={this.props.fieldKey}
childDocs={this.childDocs}
renderDepth={this.props.renderDepth}
ruleProvider={this.props.Document.isRuleProvider && layoutDoc && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
- width={this.previewWidth}
- height={this.previewHeight}
+ PanelWidth={this.previewWidth}
+ PanelHeight={this.previewHeight}
getTransform={this.getPreviewTransform}
+ CollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document}
CollectionView={this.props.CollectionView}
moveDocument={this.props.moveDocument}
addDocument={this.props.addDocument}
@@ -180,63 +163,51 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
</div>;
}
- @undoBatch
- @action
- setPreviewScript = (script: string) => {
- this.previewScript = script;
- }
-
@computed
get schemaTable() {
- return (
- <SchemaTable
- Document={this.props.Document}
- PanelHeight={this.props.PanelHeight}
- PanelWidth={this.props.PanelWidth}
- childDocs={this.childDocs}
- CollectionView={this.props.CollectionView}
- ContainingCollectionView={this.props.ContainingCollectionView}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- fieldKey={this.props.fieldKey}
- renderDepth={this.props.renderDepth}
- moveDocument={this.props.moveDocument}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- active={this.props.active}
- onDrop={this.onDrop}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- isSelected={this.props.isSelected}
- isFocused={this.isFocused}
- setFocused={this.setFocused}
- setPreviewDoc={this.setPreviewDoc}
- deleteDocument={this.props.removeDocument}
- dataDoc={this.props.DataDoc}
- />
- );
+ return <SchemaTable
+ Document={this.props.Document}
+ PanelHeight={this.props.PanelHeight}
+ PanelWidth={this.props.PanelWidth}
+ childDocs={this.childDocs}
+ CollectionView={this.props.CollectionView}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ fieldKey={this.props.fieldKey}
+ renderDepth={this.props.renderDepth}
+ moveDocument={this.props.moveDocument}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ active={this.props.active}
+ onDrop={this.onDrop}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ isSelected={this.props.isSelected}
+ isFocused={this.isFocused}
+ setFocused={this.setFocused}
+ setPreviewDoc={this.setPreviewDoc}
+ deleteDocument={this.props.removeDocument}
+ addDocument={this.props.addDocument}
+ dataDoc={this.props.DataDoc}
+ />;
}
@computed
public get schemaToolbar() {
- return (
- <div className="collectionSchemaView-toolbar">
- <div className="collectionSchemaView-toolbar-item">
- <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />Show Preview</div>
- </div>
+ return <div className="collectionSchemaView-toolbar">
+ <div className="collectionSchemaView-toolbar-item">
+ <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />Show Preview</div>
</div>
- );
+ </div>;
}
render() {
- Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
- return (
- <div className="collectionSchemaView-container" style={{ height: "100%", marginTop: "0", }}>
- <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={this.onWheel} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
- {this.schemaTable}
- </div>
- {this.dividerDragger}
- {!this.previewWidth() ? (null) : this.previewPanel}
+ return <div className="collectionSchemaView-container">
+ <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={e => this.props.active() && e.stopPropagation()} onDrop={e => this.onDrop(e, {})} ref={this.createTarget}>
+ {this.schemaTable}
</div>
- );
+ {this.dividerDragger}
+ {!this.previewWidth() ? (null) : this.previewPanel}
+ </div>;
}
}
@@ -246,12 +217,13 @@ export interface SchemaTableProps {
PanelHeight: () => number;
PanelWidth: () => number;
childDocs?: Doc[];
- CollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
- ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
+ CollectionView: Opt<CollectionView>;
+ ContainingCollectionView: Opt<CollectionView>;
ContainingCollectionDoc: Opt<Doc>;
fieldKey: string;
renderDepth: number;
deleteDocument: (document: Doc) => boolean;
+ addDocument: (document: Doc) => boolean;
moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
active: () => boolean;
@@ -302,21 +274,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
this.props.Document.textwrappedSchemaRows = new List<string>(textWrappedRows);
}
- @computed get resized(): { "id": string, "value": number }[] {
+ @computed get resized(): { id: string, value: number }[] {
return this.columns.reduce((resized, shf) => {
- if (shf.width > -1) {
- resized.push({ "id": shf.heading, "value": shf.width });
- }
+ (shf.width > -1) && resized.push({ id: shf.heading, value: shf.width });
return resized;
- }, [] as { "id": string, "value": number }[]);
+ }, [] as { id: string, value: number }[]);
}
- @computed get sorted(): { id: string, desc: boolean }[] {
+ @computed get sorted(): SortingRule[] {
return this.columns.reduce((sorted, shf) => {
- if (shf.desc) {
- sorted.push({ "id": shf.heading, "desc": shf.desc });
- }
+ shf.desc && sorted.push({ id: shf.heading, desc: shf.desc });
return sorted;
- }, [] as { id: string, desc: boolean }[]);
+ }, [] as SortingRule[]);
}
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
@@ -326,11 +294,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
let tableIsFocused = this.props.isFocused(this.props.Document);
let focusedRow = this._focusedCell.row;
let focusedCol = this._focusedCell.col;
- let isEditable = !this._headerIsEditing;// && this.props.isSelected();
+ let isEditable = !this._headerIsEditing;
- let children = this.childDocs;
-
- if (children.reduce((found, doc) => found || doc.type === "collection", false)) {
+ if (this.childDocs.reduce((found, doc) => found || doc.type === "collection", false)) {
columns.push(
{
expander: true,
@@ -438,26 +404,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
}
- tableRemoveDoc = (document: Doc): boolean => {
-
- let children = this.childDocs;
- if (children.indexOf(document) !== -1) {
- children.splice(children.indexOf(document), 1);
- this.childDocs = children;
- return true;
- }
- return false;
- }
-
private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
- const that = this;
- if (!rowInfo) {
- return {};
- }
- return {
+ return !rowInfo ? {} : {
ScreenToLocalTransform: this.props.ScreenToLocalTransform,
addDoc: this.tableAddDoc,
- removeDoc: this.tableRemoveDoc,
+ removeDoc: this.props.deleteDocument,
rowInfo,
rowFocused: !this._headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document),
textWrapRow: this.toggleTextWrapRow,
@@ -466,14 +417,12 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
- if (!rowInfo) return {};
- if (!column) return {};
+ if (!rowInfo || column) return {};
let row = rowInfo.index;
//@ts-ignore
let col = this.columns.map(c => c.heading).indexOf(column!.id);
let isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document);
- let isEditing = this.props.isFocused(this.props.Document) && this._cellIsEditing;
// TODO: editing border doesn't work :(
return {
style: {
@@ -483,113 +432,68 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@action
- onExpandCollection = (collection: Doc): void => {
- this._openCollections.push(collection[Id]);
- }
-
- @action
onCloseCollection = (collection: Doc): void => {
let index = this._openCollections.findIndex(col => col === collection[Id]);
if (index > -1) this._openCollections.splice(index, 1);
}
- @action
- setCellIsEditing = (isEditing: boolean): void => {
- this._cellIsEditing = isEditing;
- }
-
- @action
- setHeaderIsEditing = (isEditing: boolean): void => {
- this._headerIsEditing = isEditing;
- }
+ @action onExpandCollection = (collection: Doc) => this._openCollections.push(collection[Id]);
+ @action setCellIsEditing = (isEditing: boolean) => this._cellIsEditing = isEditing;
+ @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing;
onPointerDown = (e: React.PointerEvent): void => {
this.props.setFocused(this.props.Document);
- if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
- if (this.props.isSelected()) e.stopPropagation();
- }
- }
-
- onWheel = (e: React.WheelEvent): void => {
- if (this.props.active()) {
+ if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey && this.props.isSelected()) {
e.stopPropagation();
}
}
+ @action
onKeyDown = (e: KeyboardEvent): void => {
if (!this._cellIsEditing && !this._headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected()) {
let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
- this.changeFocusedCellByDirection(direction);
+ this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
- let children = this.childDocs;
- const pdoc = FieldValue(children[this._focusedCell.row]);
+ const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
pdoc && this.props.setPreviewDoc(pdoc);
}
}
- @action
- changeFocusedCellByDirection = (direction: string): void => {
- let children = this.childDocs;
+ changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => {
switch (direction) {
- case "tab":
- if (this._focusedCell.col + 1 === this.columns.length && this._focusedCell.row + 1 === children.length) {
- this._focusedCell = { row: 0, col: 0 };
- } else if (this._focusedCell.col + 1 === this.columns.length) {
- this._focusedCell = { row: this._focusedCell.row + 1, col: 0 };
- } else {
- this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col + 1 };
- }
- break;
- case "right":
- this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col + 1 === this.columns.length ? this._focusedCell.col : this._focusedCell.col + 1 };
- break;
- case "left":
- this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col === 0 ? this._focusedCell.col : this._focusedCell.col - 1 };
- break;
- case "up":
- this._focusedCell = { row: this._focusedCell.row === 0 ? this._focusedCell.row : this._focusedCell.row - 1, col: this._focusedCell.col };
- break;
- case "down":
- this._focusedCell = { row: this._focusedCell.row + 1 === children.length ? this._focusedCell.row : this._focusedCell.row + 1, col: this._focusedCell.col };
- break;
+ case "tab": return { row: (curRow + 1 === this.childDocs.length ? 0 : curRow + 1), col: curCol + 1 === this.columns.length ? 0 : curCol + 1 };
+ case "right": return { row: curRow, col: curCol + 1 === this.columns.length ? curCol : curCol + 1 };
+ case "left": return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
+ case "up": return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
+ case "down": return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
}
+ return this._focusedCell;
}
@action
changeFocusedCellByIndex = (row: number, col: number): void => {
- this._focusedCell = { row: row, col: col };
+ if (this._focusedCell.row !== row || this._focusedCell.col !== col) {
+ this._focusedCell = { row: row, col: col };
+ }
this.props.setFocused(this.props.Document);
}
@undoBatch
createRow = () => {
- let children = this.childDocs;
-
- let newDoc = Docs.Create.TextDocument({ width: 100, height: 30 });
- let proto = Doc.GetProto(newDoc);
- proto.title = "";
- children.push(newDoc);
-
- this.childDocs = children;
+ let newDoc = Docs.Create.TextDocument({ title: "", width: 100, height: 30 });
+ this.props.addDocument(newDoc);
}
@undoBatch
@action
createColumn = () => {
let index = 0;
- let columns = this.columns;
- let found = columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1;
- if (!found) {
- columns.push(new SchemaHeaderField("New field", "#f1efeb"));
- this.columns = columns;
- return;
- }
+ let found = this.columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1;
while (found) {
index++;
- found = columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1;
+ found = this.columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1;
}
- columns.push(new SchemaHeaderField("New field (" + index + ")", "#f1efeb"));
- this.columns = columns;
+ this.columns.push(new SchemaHeaderField(`New field ${index ? "(" + index + ")" : ""}`, "#f1efeb"));
}
@undoBatch
@@ -682,9 +586,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@action
- setColumns = (columns: SchemaHeaderField[]) => {
- this.columns = columns;
- }
+ setColumns = (columns: SchemaHeaderField[]) => this.columns = columns
@undoBatch
reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
@@ -730,11 +632,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
let textWrapped = this.textWrappedRows;
let index = textWrapped.findIndex(id => doc[Id] === id);
- if (index > -1) {
- textWrapped.splice(index, 1);
- } else {
- textWrapped.push(doc[Id]);
- }
+ index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
this.textWrappedRows = textWrapped;
}
@@ -764,13 +662,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
expanded={expanded}
resized={this.resized}
onResizedChange={this.onResizedChange}
- SubComponent={hasCollectionChild ?
- row => {
- if (row.original.type === "collection") {
- return <div className="sub"><SchemaTable {...this.props} Document={row.original} childDocs={undefined} /></div>;
- }
- }
- : undefined}
+ SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== "collection") ? (null) :
+ <div className="reactTable-sub"><SchemaTable {...this.props} Document={row.original} childDocs={undefined} /></div>}
/>;
}
@@ -886,143 +779,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
render() {
- return (
- <div className="collectionSchemaView-table" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
- onDrop={(e: React.DragEvent) => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
- {this.reactTable}
- <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
- </div>
- );
- }
-}
-
-
-interface CollectionSchemaPreviewProps {
- Document?: Doc;
- DataDocument?: Doc;
- childDocs?: Doc[];
- renderDepth: number;
- fitToBox?: boolean;
- width: () => number;
- height: () => number;
- ruleProvider: Doc | undefined;
- showOverlays?: (doc: Doc) => { title?: string, caption?: string };
- CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView;
- onClick?: ScriptField;
- getTransform: () => Transform;
- addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
- moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean;
- removeDocument: (document: Doc) => boolean;
- active: () => boolean;
- whenActiveChanged: (isActive: boolean) => void;
- addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
- pinToPres: (document: Doc) => void;
- setPreviewScript: (script: string) => void;
- previewScript?: string;
-}
-
-@observer
-export class CollectionSchemaPreview extends React.Component<CollectionSchemaPreviewProps>{
- private dropDisposer?: DragManager.DragDropDisposer;
- _mainCont?: HTMLDivElement;
- private get nativeWidth() { return NumCast(this.props.Document!.nativeWidth, this.props.width()); }
- private get nativeHeight() { return NumCast(this.props.Document!.nativeHeight, this.props.height()); }
- private contentScaling = () => {
- let wscale = this.props.width() / (this.nativeWidth ? this.nativeWidth : this.props.width());
- if (wscale * this.nativeHeight > this.props.height()) {
- return this.props.height() / (this.nativeHeight ? this.nativeHeight : this.props.height());
- }
- return wscale;
- }
- protected createDropTarget = (ele: HTMLDivElement) => {
- }
- private createTarget = (ele: HTMLDivElement) => {
- this._mainCont = ele;
- this.dropDisposer && this.dropDisposer();
- if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
- }
- }
-
- @undoBatch
- @action
- drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.DocumentDragData) {
- this.props.childDocs && this.props.childDocs.map(otherdoc => {
- let target = Doc.GetProto(otherdoc);
- let layoutNative = Doc.MakeTitled("layoutNative");
- layoutNative.layout = ComputedField.MakeFunction("this.image_data[0]");
- target.layoutNative = layoutNative;
- target.layoutCUstom = target.layout = Doc.MakeDelegate(de.data.draggedDocuments[0]);
- });
- e.stopPropagation();
- }
- return true;
- }
- private PanelWidth = () => this.nativeWidth ? this.nativeWidth * this.contentScaling() : this.props.width();
- private PanelHeight = () => this.nativeHeight ? this.nativeHeight * this.contentScaling() : this.props.height();
- private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling());
- get centeringOffset() { return this.nativeWidth ? (this.props.width() - this.nativeWidth * this.contentScaling()) / 2 : 0; }
- @action
- onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- this.props.setPreviewScript(e.currentTarget.value);
- }
- @computed get borderRounding() {
- let br = StrCast(this.props.Document!.borderRounding);
- if (br.endsWith("%")) {
- let percent = Number(br.substr(0, br.length - 1)) / 100;
- let nativeDim = Math.min(NumCast(this.props.Document!.nativeWidth), NumCast(this.props.Document!.nativeHeight));
- let minDim = percent * (nativeDim ? nativeDim : Math.min(this.PanelWidth(), this.PanelHeight()));
- return minDim;
- }
- return undefined;
- }
-
-
- render() {
- let input = this.props.previewScript === undefined ? (null) :
- <div ref={this.createTarget}><input className="collectionSchemaView-input" value={this.props.previewScript} onChange={this.onPreviewScriptChange}
- style={{ left: `calc(50% - ${Math.min(75, (this.props.Document ? this.PanelWidth() / 2 : 75))}px)` }} /></div>;
- return (<div className="collectionSchemaView-previewRegion"
- style={{ width: this.props.width(), height: this.props.height() }}>
- {!this.props.Document || !this.props.width ? (null) : (
- <div className="collectionSchemaView-previewDoc"
- style={{
- transform: `translate(${this.centeringOffset}px, 0px)`,
- borderRadius: this.borderRounding,
- display: "inline",
- height: this.props.height(),
- width: this.props.width()
- }}>
- <DocumentView
- DataDoc={this.props.DataDocument}
- Document={this.props.Document}
- fitToBox={this.props.fitToBox}
- onClick={this.props.onClick}
- ruleProvider={this.props.ruleProvider}
- showOverlays={this.props.showOverlays}
- addDocument={this.props.addDocument}
- removeDocument={this.props.removeDocument}
- moveDocument={this.props.moveDocument}
- whenActiveChanged={this.props.whenActiveChanged}
- ContainingCollectionView={this.props.CollectionView}
- ContainingCollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- parentActive={this.props.active}
- ScreenToLocalTransform={this.getTransform}
- renderDepth={this.props.renderDepth + 1}
- ContentScaling={this.contentScaling}
- PanelWidth={this.PanelWidth}
- PanelHeight={this.PanelHeight}
- focus={emptyFunction}
- backgroundColor={returnEmptyString}
- bringToFront={emptyFunction}
- zoomToScale={emptyFunction}
- getScale={returnOne}
- />
- </div>)}
- {input}
- </div>);
+ return <div className="collectionSchemaView-table" onPointerDown={this.onPointerDown} onWheel={e => this.props.active() && e.stopPropagation()} onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
+ {this.reactTable}
+ <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
+ </div>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 01d4ea2b6..29178b909 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -1,12 +1,19 @@
@import "../globalCssVariables";
.collectionMasonryView {
- display:inline;
+ display: inline;
}
-.collectionStackingView{
+
+.collectionStackingView {
display: flex;
}
-.collectionStackingView, .collectionMasonryView{
+.collectionStackingMasonry-cont {
+ position:relative;
+ height:100%;
+ width:100%;
+}
+.collectionStackingView,
+.collectionMasonryView {
height: 100%;
width: 100%;
position: absolute;
@@ -14,6 +21,11 @@
overflow-y: auto;
flex-wrap: wrap;
transition: top .5s;
+ >div {
+ position: relative;
+ display: block;
+ }
+
.collectionSchemaView-previewDoc {
height: 100%;
position: absolute;
@@ -40,10 +52,12 @@
top: 0;
left: 0;
}
+
.collectionStackingView-masonrySingle {
height: 100%;
position: absolute;
}
+
.collectionStackingView-masonryGrid {
margin: auto;
height: max-content;
@@ -90,18 +104,64 @@
grid-column-end: span 1;
height: 100%;
margin: auto;
+ display: inline-grid;
}
-
+
.collectionStackingView-masonrySection {
margin: auto;
}
+ .collectionStackingView-collapseBar {
+ margin-left: 2px;
+ margin-right: 2px;
+ margin-top: 2px;
+ background: $main-accent;
+ height: 5px;
+
+ &.active {
+ margin-left: 0;
+ margin-right: 0;
+ background: red;
+ }
+ }
+ .collectionStackingView-miniHeader {
+ width: 100%;
+ .editableView-container-editing-oneLine {
+ min-height: 20px;
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+ }
+ span::before , span::after{
+ content: "";
+ width: 50%;
+ border-top: dashed gray 1px;
+ position: relative;
+ display: inline-block;
+ }
+ span::before {
+ margin-right: 10px;
+ }
+ span::after{
+ margin-left: 10px;
+ }
+ span {
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ overflow: visible;
+ width: 100%;
+ display: flex;
+ color:gray;
+ align-items: center;
+ }
+ }
.collectionStackingView-sectionHeader {
text-align: center;
margin-left: 2px;
margin-right: 2px;
margin-top: 10px;
- background: gray;
+ background: $main-accent;
// overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
.editableView-input {
@@ -132,7 +192,7 @@
.editableView-input:hover,
.editableView-container-editing:hover,
.editableView-container-editing-oneLine:hover {
- cursor: text
+ cursor: text;
}
.editableView-input {
@@ -182,11 +242,45 @@
}
}
- .collectionStackingView-sectionDelete {
+ .collectionStackingView-sectionOptions {
position: absolute;
right: 0;
top: 0;
height: 100%;
+
+ [class*="css"] {
+ max-width: 102px;
+ }
+
+ .collectionStackingView-sectionOptionButton {
+ height: 35px;
+ }
+
+ .collectionStackingView-optionPicker {
+
+ .optionOptions {
+ display: inline;
+ }
+
+ .optionPicker {
+ cursor: pointer;
+ height: 20px;
+ border-radius: 10px;
+ margin: 3px;
+ width:max-content;
+
+ &.active {
+ color: red;
+ }
+ }
+ }
+ }
+
+ .collectionStackingView-sectionDelete {
+ position: absolute;
+ right: 25px;
+ top: 0;
+ height: 100%;
}
}
@@ -288,6 +382,4 @@
.rc-switch-checked .rc-switch-inner {
left: 8px;
}
-
-
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 45de0fefa..15033e51a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,7 +1,7 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CursorProperty } from "csstype";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import Switch from 'rc-switch';
import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
@@ -16,13 +16,14 @@ import { DragManager } from "../../util/DragManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { EditableView } from "../EditableView";
-import { CollectionSchemaPreview } from "./CollectionSchemaView";
+import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
import "./CollectionStackingView.scss";
import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
import { CollectionSubView } from "./CollectionSubView";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { ScriptBox } from "../ScriptBox";
+import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -32,7 +33,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
_sectionFilterDisposer?: IReactionDisposer;
_docXfs: any[] = [];
_columnStart: number = 0;
- @observable private cursor: CursorProperty = "grab";
+ @observable _heightMap = new Map<string, number>();
+ @observable _cursor: CursorProperty = "grab";
@observable _scroll = 0; // used to force the document decoration to update when scrolling
@computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
@computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
@@ -42,18 +44,35 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); }
@computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
- @computed get showAddAGroup() { return (this.sectionFilter && this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.ContainingCollectionDoc.chromeStatus !== 'disabled')); }
+ @computed get showAddAGroup() { return (this.sectionFilter && (this.props.Document.chromeStatus !== 'view-mode' && this.props.Document.chromeStatus !== 'disabled')); }
@computed get columnWidth() {
return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin,
this.isStackingView ? Number.MAX_VALUE : NumCast(this.props.Document.columnWidth, 250));
}
+ @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
childDocHeight(child: Doc) { return this.getDocHeight(Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, child).layout); }
- get layoutDoc() {
- // if this document's layout field contains a document (ie, a rendering template), then we will use that
- // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string.
- return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
+ children(docs: Doc[]) {
+ this._docXfs.length = 0;
+ return docs.map((d, i) => {
+ let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
+ let layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d;
+ let width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+ let height = () => this.getDocHeight(layoutDoc);
+ let dref = React.createRef<HTMLDivElement>();
+ let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
+ this._docXfs.push({ dxf: dxf, width: width, height: height });
+ let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
+ let style = this.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+ return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
+ {this.getDisplayDoc(pair.layout || d, pair.data, dxf, width)}
+ </div>;
+ });
+ }
+ @action
+ setDocHeight = (key: string, sectionHeight: number) => {
+ this._heightMap.set(key, sectionHeight);
}
get Sections() {
@@ -86,19 +105,30 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
componentDidMount() {
- // is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported).
+ super.componentDidMount();
this._heightDisposer = reaction(() => {
- if (this.isStackingView && BoolCast(this.props.Document.autoHeight)) {
+ if (this.props.Document.autoHeight) {
let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]);
- return this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => Math.max(maxHght,
- (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin)
- ), 0);
+ if (this.isStackingView) {
+ let res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => {
+ let r1 = Math.max(maxHght,
+ (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => {
+ let val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap);
+ return val;
+ }, this.yMargin));
+ return r1;
+ }, 0);
+ return res;
+ } else {
+ let sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0);
+ return this.props.ContentScaling() * (sum + (this.Sections.size ? (this.props.Document.miniHeaders ? 20 : 85) : -15));
+ }
}
return -1;
},
(hgt: number) => {
let doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
- doc && (doc.height = hgt);
+ doc && hgt > 0 && (Doc.Layout(doc).height = hgt);
},
{ fireImmediately: true }
);
@@ -110,6 +140,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
);
}
componentWillUnmount() {
+ super.componentWillUnmount();
this._heightDisposer && this._heightDisposer();
this._sectionFilterDisposer && this._sectionFilterDisposer();
}
@@ -120,7 +151,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
createRef = (ele: HTMLDivElement | null) => {
this._masonryGridRef = ele;
- this.createDropTarget(ele!);
+ this.createDropTarget(ele!); //so the whole grid is the drop target?
}
overlays = (doc: Doc) => {
@@ -130,20 +161,24 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
@computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); }
- getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
- let height = () => this.getDocHeight(layoutDoc);
+ getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
+ let layoutDoc = Doc.Layout(doc);
+ let height = () => this.getDocHeight(doc);
let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
- return <CollectionSchemaPreview
- Document={layoutDoc}
+ return <ContentFittingDocumentView
+ Document={doc}
DataDocument={dataDoc}
+ fieldKey={this.props.fieldKey}
showOverlays={this.overlays}
renderDepth={this.props.renderDepth}
ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
fitToBox={this.props.fitToBox}
- onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler}
- width={width}
- height={height}
+ onClick={layoutDoc.isTemplateDoc ? this.onClickHandler : this.onChildClickHandler}
+ PanelWidth={width}
+ PanelHeight={height}
getTransform={finalDxf}
+ focus={this.props.focus}
+ CollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document}
CollectionView={this.props.CollectionView}
addDocument={this.props.addDocument}
moveDocument={this.props.moveDocument}
@@ -154,25 +189,27 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
pinToPres={this.props.pinToPres}
setPreviewScript={emptyFunction}
previewScript={undefined}>
- </CollectionSchemaPreview>;
+ </ContentFittingDocumentView>;
}
getDocHeight(d?: Doc) {
if (!d) return 0;
- let nw = NumCast(d.nativeWidth);
- let nh = NumCast(d.nativeHeight);
- if (!d.ignoreAspect && !d.fitWidth && nw && nh) {
+ let layoutDoc = Doc.Layout(d);
+ let nw = NumCast(layoutDoc.nativeWidth);
+ let nh = NumCast(layoutDoc.nativeHeight);
+ let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
+ if (!layoutDoc.ignoreAspect && !layoutDoc.fitWidth && nw && nh) {
let aspect = nw && nh ? nh / nw : 1;
- let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
- if (!(d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(d[WidthSym](), wid);
+ if (!(d.nativeWidth && !layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
return wid * aspect;
}
- return d.fitWidth ? Math.min(this.props.PanelHeight() - 2 * this.yMargin, d[HeightSym]()) : d[HeightSym]();
+ return layoutDoc.fitWidth ? !layoutDoc.nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin :
+ Math.min(wid * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc.nativeHeight)) / NumCast(layoutDoc.nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym]();
}
columnDividerDown = (e: React.PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- runInAction(() => this.cursor = "grabbing");
+ runInAction(() => this._cursor = "grabbing");
document.addEventListener("pointermove", this.onDividerMove);
document.addEventListener('pointerup', this.onDividerUp);
this._columnStart = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
@@ -182,18 +219,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
let dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
let delta = dragPos - this._columnStart;
this._columnStart = dragPos;
- this.layoutDoc.columnWidth = this.columnWidth + delta;
+ this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta);
}
@action
onDividerUp = (e: PointerEvent): void => {
- runInAction(() => this.cursor = "grab");
+ runInAction(() => this._cursor = "grab");
document.removeEventListener("pointermove", this.onDividerMove);
document.removeEventListener('pointerup', this.onDividerUp);
}
@computed get columnDragger() {
- return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this.cursor, left: `${this.columnWidth + this.xMargin}px` }} >
+ return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px` }} >
<FontAwesomeIcon icon={"arrows-alt-h"} />
</div>;
}
@@ -203,12 +240,14 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
drop = (e: Event, de: DragManager.DropEvent) => {
let where = [de.x, de.y];
let targInd = -1;
+ let plusOne = false;
if (de.data instanceof DragManager.DocumentDragData) {
this._docXfs.map((cd, i) => {
let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
targInd = i;
+ plusOne = (where[1] > (pos[1] + pos1[1]) / 2 ? 1 : 0) ? true : false;
}
});
}
@@ -220,14 +259,14 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
else targInd = docs.indexOf(this.filteredChildren[targInd]);
let srcInd = docs.indexOf(newDoc);
docs.splice(srcInd, 1);
- docs.splice(targInd > srcInd ? targInd - 1 : targInd, 0, newDoc);
+ docs.splice((targInd > srcInd ? targInd - 1 : targInd) + (plusOne ? 1 : 0), 0, newDoc);
}
}
return false;
}
@undoBatch
@action
- onDrop = (e: React.DragEvent): void => {
+ onDrop = async (e: React.DragEvent): Promise<void> => {
let where = [e.clientX, e.clientY];
let targInd = -1;
this._docXfs.map((cd, i) => {
@@ -279,55 +318,32 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
return this.props.ScreenToLocalTransform().
- translate(offset[0], offset[1]).
+ translate(offset[0], offset[1] + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)).
scale(NumCast(doc.width, 1) / this.columnWidth);
}
- masonryChildren(docs: Doc[]) {
- this._docXfs.length = 0;
- return docs.map((d, i) => {
- const pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
- if (!pair.layout || pair.data instanceof Promise) {
- return (null);
- }
- let dref = React.createRef<HTMLDivElement>();
- let width = () => (d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth);/// (uniqueHeadings.length + 1);
- let height = () => this.getDocHeight(pair.layout);
- let dxf = () => this.getDocTransform(pair.layout!, dref.current!);
- let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
- this._docXfs.push({ dxf: dxf, width: width, height: height });
- return !pair.layout ? (null) : <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
- {this.getDisplayDoc(pair.layout, pair.data, dxf, width)}
- </div>;
- });
- }
- @observable _headingsHack: number = 1;
- sectionMasonry(heading: SchemaHeaderField | undefined, docList: Doc[]) {
- let cols = Math.max(1, Math.min(docList.length,
- Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
- if (isNaN(cols)) {
- console.log("naN");
- cols = 1;
+ sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
+ let key = this.sectionFilter;
+ let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+ let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
+ if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
+ type = types[0];
}
- return <div key={heading ? heading.heading : "empty"} className="collectionStackingView-masonrySection">
- {!heading ? (null) :
- <div key={`${heading.heading}`} className="collectionStackingView-sectionHeader" style={{ background: heading.color }}
- onClick={action(() => this._headingsHack++ && heading.setCollapsed(!heading.collapsed))} >
- {heading.heading}
- </div>}
- {this._headingsHack && heading && heading.collapsed ? (null) :
- <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
- style={{
- padding: `${this.yMargin}px ${this.xMargin}px`,
- width: `${cols * (this.columnWidth + this.gridGap) + 2 * this.xMargin - this.gridGap}px`,
- gridGap: this.gridGap,
- gridTemplateColumns: numberRange(cols).reduce((list, i) => list + ` ${this.columnWidth}px`, ""),
- }}>
- {this.masonryChildren(docList)}
- {this.columnDragger}
- </div>
- }
- </div>;
+ let rows = () => !this.isStackingView ? 1 : Math.max(1, Math.min(docList.length,
+ Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
+ return <CollectionMasonryViewFieldRow
+ key={heading ? heading.heading : ""}
+ rows={rows}
+ headings={this.headings}
+ heading={heading ? heading.heading : ""}
+ headingObject={heading}
+ docList={docList}
+ parent={this}
+ type={type}
+ createDropTarget={this.createDropTarget}
+ screenToLocalTransform={this.props.ScreenToLocalTransform}
+ setDocHeight={this.setDocHeight}
+ />;
}
@action
@@ -347,7 +363,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
onToggle = (checked: Boolean) => {
- this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus = checked ? "collapsed" : "view-mode");
+ this.props.Document.chromeStatus = checked ? "collapsed" : "view-mode";
}
onContextMenu = (e: React.MouseEvent): void => {
@@ -372,33 +388,33 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
SetValue: this.addGroup,
contents: "+ ADD A GROUP"
};
- Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
if (this.sectionFilter) {
let entries = Array.from(this.Sections.entries());
sections = entries.sort(this.sortFunc);
}
return (
- <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
- ref={this.createRef}
- onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
- onDrop={this.onDrop.bind(this)}
- onContextMenu={this.onContextMenu}
- onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
- {sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]))}
- {!this.showAddAGroup ? (null) :
- <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
- style={{ width: this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
- <EditableView {...editableViewProps} />
- </div>}
- {this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.chromeStatus !== 'disabled' ? <Switch
- onChange={this.onToggle}
- onClick={this.onToggle}
- defaultChecked={this.props.ContainingCollectionDoc.chromeStatus !== 'view-mode'}
- checkedChildren="edit"
- unCheckedChildren="view"
- /> : null}
- </div>
+ <div className="collectionStackingMasonry-cont" >
+ <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
+ ref={this.createRef}
+ onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
+ onDrop={this.onDrop.bind(this)}
+ onContextMenu={this.onContextMenu}
+ onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
+ {sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]))}
+ {!this.showAddAGroup ? (null) :
+ <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
+ style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
+ <EditableView {...editableViewProps} />
+ </div>}
+ {this.props.Document.chromeStatus !== 'disabled' ? <Switch
+ onChange={this.onToggle}
+ onClick={this.onToggle}
+ defaultChecked={this.props.Document.chromeStatus !== 'view-mode'}
+ checkedChildren="edit"
+ unCheckedChildren="view"
+ /> : null}
+ </div> </div>
);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 240adf428..b9d334b10 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faPalette } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, trace } from "mobx";
+import { action, observable, trace, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
@@ -23,7 +23,6 @@ import "./CollectionStackingView.scss";
library.add(faPalette);
-
interface CSVFieldColumnProps {
cols: () => number;
headings: () => object[];
@@ -39,6 +38,7 @@ interface CSVFieldColumnProps {
@observer
export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
@observable private _background = "inherit";
+ @observable private _createAliasSelected: boolean = false;
private _dropRef: HTMLDivElement | null = null;
private dropDisposer?: DragManager.DragDropDisposer;
@@ -58,8 +58,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
}
@undoBatch
- @action
- columnDrop = (e: Event, de: DragManager.DropEvent) => {
+ columnDrop = action((e: Event, de: DragManager.DropEvent) => {
+ this._createAliasSelected = false;
if (de.data instanceof DragManager.DocumentDragData) {
let key = StrCast(this.props.parent.props.Document.sectionFilter);
let castedValue = this.getValue(this._heading);
@@ -72,29 +72,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
this.props.parent.drop(e, de);
e.stopPropagation();
}
- }
-
- children(docs: Doc[]) {
- let parent = this.props.parent;
- parent._docXfs.length = 0;
- return docs.map((d, i) => {
- const pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
- if (!pair.layout || pair.data instanceof Promise) {
- return (null);
- }
- let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, parent.columnWidth / parent.numGroupColumns);
- let height = () => parent.getDocHeight(pair.layout);
- let dref = React.createRef<HTMLDivElement>();
- let dxf = () => parent.getDocTransform(pair.layout!, dref.current!);
- parent._docXfs.push({ dxf: dxf, width: width, height: height });
- let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap);
- let style = parent.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : parent.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
- return <div className={`collectionStackingView-${parent.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
- {parent.getDisplayDoc(pair.layout, pair.data, dxf, width)}
- </div>;
- });
- }
-
+ });
getValue = (value: string): any => {
let parsed = parseInt(value);
if (!isNaN(parsed)) {
@@ -111,6 +89,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
headingChanged = (value: string, shiftDown?: boolean) => {
+ this._createAliasSelected = false;
let key = StrCast(this.props.parent.props.Document.sectionFilter);
let castedValue = this.getValue(value);
if (castedValue) {
@@ -131,6 +110,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
changeColumnColor = (color: string) => {
+ this._createAliasSelected = false;
if (this.props.headingObject) {
this.props.headingObject.setColor(color);
this._color = color;
@@ -140,18 +120,21 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
pointerEntered = () => {
if (SelectionManager.GetIsDragging()) {
+ this._createAliasSelected = false;
this._background = "#b4b4b4";
}
}
@action
pointerLeave = () => {
+ this._createAliasSelected = false;
this._background = "inherit";
document.removeEventListener("pointermove", this.startDrag);
}
@action
addDocument = (value: string, shiftDown?: boolean) => {
+ this._createAliasSelected = false;
let key = StrCast(this.props.parent.props.Document.sectionFilter);
let newDoc = Docs.Create.TextDocument({ height: 18, width: 200, documentText: "@@@" + value, title: value, autoHeight: true });
newDoc[key] = this.getValue(this.props.heading);
@@ -163,6 +146,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
deleteColumn = () => {
+ this._createAliasSelected = false;
let key = StrCast(this.props.parent.props.Document.sectionFilter);
this.props.docList.forEach(d => d[key] = undefined);
if (this.props.parent.sectionHeaders && this.props.headingObject) {
@@ -171,6 +155,16 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
}
}
+ @action
+ collapseSection = () => {
+ this._createAliasSelected = false;
+ if (this.props.headingObject) {
+ this._headingsHack++;
+ this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed);
+ this.toggleVisibility();
+ }
+ }
+
startDrag = (e: PointerEvent) => {
let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
@@ -204,10 +198,13 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
this._startDragPosition = { x: dx, y: dy };
- document.removeEventListener("pointermove", this.startDrag);
- document.addEventListener("pointermove", this.startDrag);
- document.removeEventListener("pointerup", this.pointerUp);
- document.addEventListener("pointerup", this.pointerUp);
+ if (this._createAliasSelected) {
+ document.removeEventListener("pointermove", this.startDrag);
+ document.addEventListener("pointermove", this.startDrag);
+ document.removeEventListener("pointerup", this.pointerUp);
+ document.addEventListener("pointerup", this.pointerUp);
+ }
+ runInAction(() => this._createAliasSelected = false);
}
renderColorPicker = () => {
@@ -240,6 +237,30 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
);
}
+ @action
+ toggleAlias = () => {
+ this._createAliasSelected = true;
+ }
+
+ renderMenu = () => {
+ let selected = this._createAliasSelected;
+ return (
+ <div className="collectionStackingView-optionPicker">
+ <div className="optionOptions">
+ <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
+ </div>
+ </div >
+ );
+ }
+
+ @observable private collapsed: boolean = false;
+
+ private toggleVisibility = action(() => {
+ this.collapsed = !this.collapsed;
+ });
+
+ @observable _headingsHack: number = 1;
+
render() {
let cols = this.props.cols();
let key = StrCast(this.props.parent.props.Document.sectionFilter);
@@ -254,20 +275,29 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
GetValue: () => evContents,
SetValue: this.headingChanged,
contents: evContents,
- oneLine: true
+ oneLine: true,
+ HeadingObject: this.props.headingObject,
+ HeadingsHack: this._headingsHack,
+ toggle: this.toggleVisibility,
+ color: this._color
};
let newEditableViewProps = {
GetValue: () => "",
SetValue: this.addDocument,
- contents: "+ NEW"
+ contents: "+ NEW",
+ HeadingObject: this.props.headingObject,
+ HeadingsHack: this._headingsHack,
+ toggle: this.toggleVisibility,
+ color: this._color
};
let headingView = this.props.headingObject ?
<div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
style={{
width: (style.columnWidth) /
((uniqueHeadings.length +
- ((this.props.parent.props.ContainingCollectionDoc && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'disabled') ? 1 : 0)) || 1)
+ ((this.props.parent.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)
}}>
+ <div className={"collectionStackingView-collapseBar" + (this.props.headingObject.collapsed === true ? " active" : "")} onClick={this.collapseSection}></div>
{/* the default bucket (no key value) has a tooltip that describes what it is.
Further, it does not have a color and cannot be deleted. */}
<div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
@@ -281,9 +311,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
<EditableView {...headerEditableViewProps} />
{evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
<div className="collectionStackingView-sectionColor">
- <Flyout anchorPoint={anchorPoints.TOP_CENTER} content={this.renderColorPicker()}>
+ <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
<button className="collectionStackingView-sectionColorButton">
- <FontAwesomeIcon icon="palette" size="sm" />
+ <FontAwesomeIcon icon="palette" size="lg" />
</button>
</ Flyout >
</div>
@@ -291,36 +321,50 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
{evContents === `NO ${key.toUpperCase()} VALUE` ?
(null) :
<button className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
- <FontAwesomeIcon icon="trash" />
+ <FontAwesomeIcon icon="trash" size="lg" />
</button>}
+ {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
+ <div className="collectionStackingView-sectionOptions">
+ <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
+ <button className="collectionStackingView-sectionOptionButton">
+ <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
+ </button>
+ </Flyout>
+ </div>
+ }
</div>
</div> : (null);
for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth / style.numGroupColumns}px `;
+ let chromeStatus = this.props.parent.props.Document.chromeStatus;
return (
- <div className="collectionStackingViewFieldColumn" key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((this.props.parent.props.ContainingCollectionDoc && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }}
+ <div className="collectionStackingViewFieldColumn" key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }}
ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
- {headingView}
- <div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`}
- style={{
- padding: singleColumn ? `${style.yMargin}px ${0}px ${style.yMargin}px ${0}px` : `${style.yMargin}px ${0}px`,
- margin: "auto",
- width: "max-content", //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
- height: 'max-content',
- position: "relative",
- gridGap: style.gridGap,
- gridTemplateColumns: singleColumn ? undefined : templatecols,
- gridAutoRows: singleColumn ? undefined : "0px"
- }}
- >
- {this.children(this.props.docList)}
- {singleColumn ? (null) : this.props.parent.columnDragger}
- </div>
- {(this.props.parent.props.ContainingCollectionDoc && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'disabled') ?
- <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
- style={{ width: style.columnWidth / style.numGroupColumns }}>
- <EditableView {...newEditableViewProps} />
- </div> : null}
- </div>
+ {this.props.parent.Document.hideHeadings ? (null) : headingView}
+ {
+ this.collapsed ? (null) :
+ <div>
+ <div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`}
+ style={{
+ padding: singleColumn ? `${style.yMargin}px ${0}px ${style.yMargin}px ${0}px` : `${style.yMargin}px ${0}px`,
+ margin: "auto",
+ width: "max-content", //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
+ height: 'max-content',
+ position: "relative",
+ gridGap: style.gridGap,
+ gridTemplateColumns: singleColumn ? undefined : templatecols,
+ gridAutoRows: singleColumn ? undefined : "0px"
+ }}>
+ {this.props.parent.children(this.props.docList)}
+ {singleColumn ? (null) : this.props.parent.columnDragger}
+ </div>
+ {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
+ <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
+ style={{ width: style.columnWidth / style.numGroupColumns }}>
+ <EditableView {...newEditableViewProps} />
+ </div> : null}
+ </div>
+ }
+ </div >
);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 069269b06..a2675c9a3 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -6,7 +6,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast } from "../../../new_fields/Types";
+import { Cast, StrCast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { RouteStore } from "../../../server/RouteStore";
import { Utils } from "../../../Utils";
@@ -17,61 +17,74 @@ import { DragManager } from "../../util/DragManager";
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { FieldViewProps } from "../nodes/FieldView";
import { FormattedTextBox, GoogleRef } from "../nodes/FormattedTextBox";
-import { CollectionPDFView } from "./CollectionPDFView";
-import { CollectionVideoView } from "./CollectionVideoView";
import { CollectionView } from "./CollectionView";
import React = require("react");
import { DocComponent } from "../DocComponent";
+var path = require('path');
+import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
+import { ImageUtils } from "../../util/Import & Export/ImageUtils";
export interface CollectionViewProps extends FieldViewProps {
- addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+ addDocument: (document: Doc) => boolean;
removeDocument: (document: Doc) => boolean;
moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
PanelWidth: () => number;
PanelHeight: () => number;
+ VisibleHeight?: () => number;
chromeCollapsed: boolean;
setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
+ fieldKey: string;
}
export interface SubCollectionViewProps extends CollectionViewProps {
- CollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
+ CollectionView: Opt<CollectionView>;
ruleProvider: Doc | undefined;
+ children?: never | (() => JSX.Element[]) | React.ReactNode;
+ isAnnotationOverlay?: boolean;
+ annotationsKey: string;
}
export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
class CollectionSubView extends DocComponent<SubCollectionViewProps, T>(schemaCtor) {
private dropDisposer?: DragManager.DragDropDisposer;
private _childLayoutDisposer?: IReactionDisposer;
-
- protected createDropTarget = (ele: HTMLDivElement) => {
+ protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
this.dropDisposer && this.dropDisposer();
if (ele) {
this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
}
}
- protected CreateDropTarget(ele: HTMLDivElement) {
+ protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view
this.createDropTarget(ele);
}
componentDidMount() {
this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)],
- async (args) => args[1] instanceof Doc &&
- this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc))));
+ async (args) => {
+ if (args[1] instanceof Doc) {
+ this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), "layoutFromParent"));
+ }
+ else if (!(args[1] instanceof Promise)) {
+ this.childDocs.filter(d => !d.isTemplateField).map(async doc => doc.layoutKey === "layoutFromParent" && (doc.layoutKey = "layout"));
+ }
+ });
}
componentWillUnmount() {
this._childLayoutDisposer && this._childLayoutDisposer();
}
- // The data field for rendeing this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc.
+ @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc) : Doc.GetProto(this.props.Document); }
+ @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
+
+ // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc.
// When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through
// to its children which may be templates.
- // The name of the data field comes from fieldExt if it's an extension, or fieldKey otherwise.
+ // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey'
@computed get dataField() {
- return Doc.fieldExtensionDoc(this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt)[this.props.fieldExt || this.props.fieldKey];
+ return this.props.annotationsKey ? (this.extensionDoc ? this.extensionDoc[this.props.annotationsKey] : undefined) : this.dataDoc[this.props.fieldKey];
}
-
get childLayoutPairs() {
return this.childDocs.map(cd => Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, cd)).filter(pair => pair.layout).map(pair => ({ layout: pair.layout!, data: pair.data! }));
}
@@ -119,11 +132,12 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
+ (this.props.Document.dropConverter instanceof ScriptField) &&
+ this.props.Document.dropConverter.script.run({ dragData: de.data });
if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) {
if (de.mods === "AltKey" && de.data.draggedDocuments.length) {
this.childDocs.map(doc =>
- Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc)
- );
+ Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, "layoutFromParent"));
e.stopPropagation();
return true;
}
@@ -134,11 +148,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
return moved || added;
}, false);
} else if (de.data.moveDocument) {
- let movedDocs = de.data.draggedDocuments;// de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments;
- // note that it's possible the drag function might create a drop document that's not the same as the
- // original dragged document. So we explicitly call addDocument() with a droppedDocument and
+ let movedDocs = de.data.draggedDocuments;
added = movedDocs.reduce((added: boolean, d, i) =>
- de.data.moveDocument(d, this.props.Document, (doc: Doc) => this.props.addDocument(de.data.droppedDocuments[i])) || added, false);
+ de.data.droppedDocuments[i] !== d ? this.props.addDocument(de.data.droppedDocuments[i]) :
+ de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false);
} else {
added = de.data.droppedDocuments.reduce((added: boolean, d) => {
let moved = this.props.addDocument(d);
@@ -157,7 +170,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
@action
- protected onDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
+ protected async onDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
if (e.ctrlKey) {
e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl
return;
@@ -179,14 +192,14 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
DocServer.GetRefField(docid).then(f => {
if (f instanceof Doc) {
if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
- (f instanceof Doc) && this.props.addDocument(f, false);
+ (f instanceof Doc) && this.props.addDocument(f);
}
});
} else {
this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, options));
}
} else if (text) {
- this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text }), false);
+ this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text }));
}
return;
}
@@ -197,7 +210,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
if (img) {
let split = img.split("src=\"")[1].split("\"")[0];
let doc = Docs.Create.ImageDocument(split, { ...options, width: 300 });
- this.props.addDocument(doc, false);
+ ImageUtils.ExtractExif(doc);
+ this.props.addDocument(doc);
return;
} else {
let path = window.location.origin + "/doc/";
@@ -206,12 +220,12 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
DocServer.GetRefField(docid).then(f => {
if (f instanceof Doc) {
if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
- (f instanceof Doc) && this.props.addDocument(f, false);
+ (f instanceof Doc) && this.props.addDocument(f);
}
});
} else {
let htmlDoc = Docs.Create.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text });
- this.props.addDocument(htmlDoc, false);
+ this.props.addDocument(htmlDoc);
}
return;
}
@@ -225,13 +239,22 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) {
let newBox = Docs.Create.TextDocument({ ...options, width: 400, height: 200, title: "Awaiting title from Google Docs..." });
let proto = newBox.proto!;
- proto.autoHeight = true;
- proto[GoogleRef] = matches[2];
+ const documentId = matches[2];
+ proto[GoogleRef] = documentId;
proto.data = "Please select this document and then click on its pull button to load its contents from from Google Docs...";
proto.backgroundColor = "#eeeeff";
this.props.addDocument(newBox);
+ // const parent = Docs.Create.StackingDocument([newBox], { title: `Google Doc Import (${documentId})` });
+ // CollectionDockingView.Instance.AddRightSplit(parent, undefined);
+ // proto.height = parent[HeightSym]();
return;
}
+ if ((matches = /(https:\/\/)?photos\.google\.com\/(u\/3\/)?album\/([^\\]+)/g.exec(text)) !== null) {
+ const albums = await GooglePhotos.Transactions.ListAlbums();
+ const albumId = matches[3];
+ const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId);
+ console.log(mediaItems);
+ }
let batch = UndoManager.StartBatch("collection view drop");
let promises: Promise<void>[] = [];
// tslint:disable-next-line:prefer-for-of
@@ -246,7 +269,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
let type = result["content-type"];
if (type) {
Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300 })
- .then(doc => doc && this.props.addDocument(doc, false));
+ .then(doc => doc && this.props.addDocument(doc));
}
});
promises.push(prom);
@@ -267,8 +290,11 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName };
- let path = Utils.prepend(file);
- Docs.Get.DocumentFromType(type, path, full).then(doc => doc && this.props.addDocument(doc));
+ let pathname = Utils.prepend(file.path);
+ Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
+ doc && (Doc.GetProto(doc).fileUpload = path.basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""));
+ doc && this.props.addDocument(doc);
+ });
}));
});
promises.push(prom);
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 197e57808..7d0c900a6 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -8,14 +8,14 @@
box-sizing: border-box;
height: 100%;
width:100%;
- position: absolute;
+ position: relative;
top:0;
- padding-top: 20px;
padding-left: 10px;
- padding-right: 0px;
+ padding-right: 10px;
background: $light-color-secondary;
font-size: 13px;
overflow: auto;
+ cursor: default;
ul {
list-style: none;
@@ -51,7 +51,7 @@
.editableView-container-editing {
display: block;
text-overflow: ellipsis;
- font-size: 24px;
+ font-size: 1vw;
white-space: nowrap;
}
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e5313f68c..8726726bb 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,14 +1,15 @@
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faAngleRight, faCamera, faExpand, faTrash, faBell, faCaretDown, faCaretRight, faArrowsAltH, faCaretSquareDown, faCaretSquareRight, faTrashAlt, faPlus, faMinus } from '@fortawesome/free-solid-svg-icons';
+import { faAngleRight, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faExpand, faMinus, faPlus, faTrash, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, trace, untracked } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym, Opt, Field } from '../../../new_fields/Doc';
+import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { Document, listSpec } from '../../../new_fields/Schema';
+import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types';
-import { emptyFunction, Utils } from '../../../Utils';
+import { emptyFunction, Utils, returnFalse } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from '../../util/DocumentManager';
@@ -17,18 +18,17 @@ import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from "../EditableView";
import { MainView } from '../MainView';
+import { KeyValueBox } from '../nodes/KeyValueBox';
import { Templates } from '../Templates';
-import { CollectionViewType } from './CollectionBaseView';
-import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionSchemaPreview } from './CollectionSchemaView';
+import { CollectionViewType } from './CollectionView';
+import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTreeView.scss";
import React = require("react");
-import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
-import { KeyValueBox } from '../nodes/KeyValueBox';
-import { ContextMenuProps } from '../ContextMenuItem';
+import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
export interface TreeViewProps {
@@ -82,56 +82,46 @@ class TreeView extends React.Component<TreeViewProps> {
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
- get defaultExpandedView() { return this.childDocs ? this.fieldKey : "fields"; }
+ get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, "fields"); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
- @computed get treeViewOpen() { return (BoolCast(this.props.document.treeViewOpen) && !this.props.preventTreeViewOpen) || this._overrideTreeViewOpen; }
set treeViewOpen(c: boolean) { if (this.props.preventTreeViewOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = c; }
+ @computed get treeViewOpen() { return (BoolCast(this.props.document.treeViewOpen) && !this.props.preventTreeViewOpen) || this._overrideTreeViewOpen; }
@computed get treeViewExpandedView() { return StrCast(this.props.document.treeViewExpandedView, this.defaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); }
- @computed get dataDoc() { return this.resolvedDataDoc ? this.resolvedDataDoc : this.props.document; }
+ @computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; }
@computed get fieldKey() {
- let splits = StrCast(this.props.document.layout).split("fieldKey={\"");
+ let splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\"");
return splits.length > 1 ? splits[1].split("\"")[0] : "data";
}
- @computed get childDocs() {
- let layout = this.props.document.layout instanceof Doc ? this.props.document.layout : undefined;
- return (this.props.dataDoc ? Cast(this.props.dataDoc[this.fieldKey], listSpec(Doc)) : undefined) ||
- (layout ? Cast(layout[this.fieldKey], listSpec(Doc)) : undefined) ||
- Cast(this.props.document[this.fieldKey], listSpec(Doc));
- }
- @computed get childLinks() {
- let layout = this.props.document.layout instanceof Doc ? this.props.document.layout : undefined;
- return (this.props.dataDoc ? Cast(this.props.dataDoc.links, listSpec(Doc)) : undefined) ||
- (layout instanceof Doc ? Cast(layout.links, listSpec(Doc)) : undefined) ||
- Cast(this.props.document.links, listSpec(Doc));
+ childDocList(field: string) {
+ let layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined;
+ return ((this.props.dataDoc ? Cast(this.props.dataDoc[field], listSpec(Doc)) : undefined) ||
+ (layout ? Cast(layout[field], listSpec(Doc)) : undefined) ||
+ Cast(this.props.document[field], listSpec(Doc))) as Doc[];
}
- @computed get resolvedDataDoc() {
- if (this.props.dataDoc === undefined && this.props.document.layout instanceof Doc) {
- // if there is no dataDoc (ie, we're not rendering a template layout), but this document
- // has a template layout document, then we will render the template layout but use
- // this document as the data document for the layout.
+ @computed get childDocs() { return this.childDocList(this.fieldKey); }
+ @computed get childLinks() { return this.childDocList("links"); }
+ @computed get templateDataDoc() {
+ if (this.props.dataDoc === undefined && Doc.LayoutField(this.props.document) !== "string") {
+ // if there is no dataDoc (ie, we're not rendering a template layout), but this document has a layout document (not a layout string),
+ // then we render the layout document as a template and use this document as the data context for the template layout.
return this.props.document;
}
return this.props.dataDoc;
}
@computed get boundsOfCollectionDocument() {
return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 ? undefined :
- Doc.ComputeContentBounds(DocListCast(this.props.document.data));
+ Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
}
- @undoBatch delete = () => this.props.deleteDoc(this.dataDoc);
- @undoBatch openRight = () => this.props.addDocTab(this.props.document, undefined, "onRight");
+ @undoBatch delete = () => this.props.deleteDoc(this.props.document);
+ @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight");
@undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
@undoBatch move = (doc: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => {
return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
}
- @undoBatch @action remove = (document: Document, key: string): boolean => {
- let children = Cast(this.dataDoc[key], listSpec(Doc), []);
- if (children.indexOf(document) !== -1) {
- children.splice(children.indexOf(document), 1);
- return true;
- }
- return false;
+ @undoBatch @action remove = (document: Document, key: string) => {
+ return Doc.RemoveDocFromList(this.dataDoc, key, document);
}
protected createTreeDropTarget = (ele: HTMLDivElement) => {
@@ -175,9 +165,9 @@ class TreeView extends React.Component<TreeViewProps> {
fontStyle={style}
fontSize={12}
GetValue={() => StrCast(this.props.document[key])}
- SetValue={undoBatch((value: string) => (Doc.GetProto(this.dataDoc)[key] = value) ? true : true)}
+ SetValue={undoBatch((value: string) => Doc.SetInPlace(this.props.document, key, value, false) || true)}
OnFillDown={undoBatch((value: string) => {
- Doc.GetProto(this.dataDoc)[key] = value;
+ Doc.SetInPlace(this.props.document, key, value, false);
let doc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined;
if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
TreeView.loadId = doc[Id];
@@ -188,17 +178,18 @@ class TreeView extends React.Component<TreeViewProps> {
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
+ if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking && this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
- ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" });
- ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" });
+ ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab"), icon: "folder" });
+ ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight"), icon: "caret-square-right" });
if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
- ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.dataDoc).map(view => view.props.focus(this.props.document, true)), icon: "camera" });
+ ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.dataDoc)), icon: "camera" });
}
ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
} else {
ContextMenu.Instance.addItem({ description: "Open as Workspace", event: () => MainView.Instance.openWorkspace(this.dataDoc), icon: "caret-square-right" });
ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
+ ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
}
ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
@@ -218,25 +209,22 @@ class TreeView extends React.Component<TreeViewProps> {
if (de.data instanceof DragManager.LinkDragData) {
let sourceDoc = de.data.linkSourceDocument;
let destDoc = this.props.document;
- DocUtils.MakeLink(sourceDoc, destDoc);
+ DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc });
e.stopPropagation();
}
if (de.data instanceof DragManager.DocumentDragData) {
e.stopPropagation();
if (de.data.draggedDocuments[0] === this.props.document) return true;
- let addDoc = (doc: Doc) => this.props.addDocument(doc, this.resolvedDataDoc, before);
+ let addDoc = (doc: Doc) => this.props.addDocument(doc, undefined, before);
if (inside) {
- let docList = Cast(this.dataDoc.data, listSpec(Doc));
- if (docList !== undefined) {
- addDoc = (doc: Doc) => { docList && docList.push(doc); return true; };
- }
+ addDoc = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) || addDoc(doc);
}
let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
return (de.data.dropAction || de.data.userDropAction) ?
- de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before) || added, false)
- : (de.data.moveDocument) ?
- movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, this.resolvedDataDoc, addDoc) || added, false)
- : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before), false);
+ de.data.droppedDocuments.reduce((added, d) => addDoc(d) || added, false)
+ : de.data.moveDocument ?
+ movedDocs.reduce((added, d) => de.data.moveDocument(d, undefined, addDoc) || added, false)
+ : de.data.droppedDocuments.reduce((added, d) => addDoc(d), false);
}
return false;
}
@@ -249,17 +237,22 @@ class TreeView extends React.Component<TreeViewProps> {
return finalXf;
}
docWidth = () => {
- let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth);
- if (aspect) return Math.min(this.props.document[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 5));
- return NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5;
+ let layoutDoc = Doc.Layout(this.props.document);
+ let aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth);
+ if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
+ return NumCast(layoutDoc.nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
}
docHeight = () => {
+ let layoutDoc = Doc.Layout(this.props.document);
let bounds = this.boundsOfCollectionDocument;
return Math.min(this.MAX_EMBED_HEIGHT, (() => {
- let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth);
+ let aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth, 1);
if (aspect) return this.docWidth() * aspect;
if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
- return NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50;
+ return layoutDoc.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) :
+ Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc.nativeHeight)) / NumCast(layoutDoc.nativeWidth,
+ NumCast(this.props.containingCollection.height)))) :
+ NumCast(layoutDoc.height) ? NumCast(layoutDoc.height) : 50;
})());
}
@@ -308,39 +301,40 @@ class TreeView extends React.Component<TreeViewProps> {
let docs = expandKey === "links" ? this.childLinks : this.childDocs;
return <ul key={expandKey + "more"}>
{!docs ? (null) :
- TreeView.GetChildElements(docs as Doc[], this.props.treeViewId, this.props.document.layout as Doc,
- this.resolvedDataDoc, expandKey, addDoc, remDoc, this.move,
+ TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
+ this.templateDataDoc, expandKey, addDoc, remDoc, this.move,
this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth, this.props.showHeaderFields, this.props.preventTreeViewOpen,
[...this.props.renderedIds, this.props.document[Id]])}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
return <ul><div ref={this._dref} style={{ display: "inline-block" }} key={this.props.document[Id] + this.props.document.title}>
- {this.dataDoc ? this.expandedField(this.dataDoc) : (null)}
+ {this.expandedField(this.props.document)}
</div></ul>;
} else {
- let layoutDoc = this.props.document;
+ let layoutDoc = Doc.Layout(this.props.document);
return <div ref={this._dref} style={{ display: "inline-block", height: this.docHeight() }} key={this.props.document[Id] + this.props.document.title}>
- <CollectionSchemaPreview
+ <ContentFittingDocumentView
Document={layoutDoc}
- DataDocument={this.resolvedDataDoc}
+ DataDocument={this.templateDataDoc}
+ fieldKey={this.fieldKey}
renderDepth={this.props.renderDepth}
showOverlays={this.noOverlays}
ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider}
fitToBox={this.boundsOfCollectionDocument !== undefined}
- width={this.docWidth}
- height={this.docHeight}
+ PanelWidth={this.docWidth}
+ PanelHeight={this.docHeight}
getTransform={this.docTransform}
+ CollectionDoc={this.props.containingCollection}
CollectionView={undefined}
- addDocument={emptyFunction as any}
+ addDocument={returnFalse}
moveDocument={this.props.moveDocument}
- removeDocument={emptyFunction as any}
+ removeDocument={returnFalse}
active={this.props.active}
- whenActiveChanged={emptyFunction as any}
+ whenActiveChanged={emptyFunction}
addDocTab={this.props.addDocTab}
pinToPres={this.props.pinToPres}
- setPreviewScript={emptyFunction}>
- </CollectionSchemaPreview>
+ setPreviewScript={emptyFunction} />
</div>;
}
}
@@ -364,7 +358,7 @@ class TreeView extends React.Component<TreeViewProps> {
onPointerDown={action(() => {
if (this.treeViewOpen) {
this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" :
- this.treeViewExpandedView === "fields" && this.props.document.layout ? "layout" :
+ this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" :
this.treeViewExpandedView === "layout" && this.props.document.links ? "links" :
this.childDocs ? this.fieldKey : "fields";
}
@@ -456,7 +450,7 @@ class TreeView extends React.Component<TreeViewProps> {
let rowWidth = () => panelWidth() - 20;
return docs.map((child, i) => {
- let pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child);
+ const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child);
if (!pair.layout || pair.data instanceof Promise) {
return (null);
}
@@ -475,11 +469,12 @@ class TreeView extends React.Component<TreeViewProps> {
let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false);
};
+ const childLayout = Doc.Layout(pair.layout);
let rowHeight = () => {
- let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0);
- return aspect ? Math.min(child[WidthSym](), rowWidth()) / aspect : child[HeightSym]();
+ let aspect = NumCast(childLayout.nativeWidth, 0) / NumCast(childLayout.nativeHeight, 0);
+ return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
};
- return <TreeView
+ return !(child instanceof Doc) ? (null) : <TreeView
document={pair.layout}
dataDoc={pair.data}
containingCollection={containingCollection}
@@ -512,9 +507,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
private treedropDisposer?: DragManager.DragDropDisposer;
private _mainEle?: HTMLDivElement;
- @observable static NotifsCol: Opt<Doc>;
-
- @computed get resolvedDataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
+ @computed get dataDoc() { return this.props.DataDoc || this.props.Document; }
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this.treedropDisposer && this.treedropDisposer();
@@ -524,6 +517,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
componentWillUnmount() {
+ super.componentWillUnmount();
this.treedropDisposer && this.treedropDisposer();
}
@@ -538,7 +532,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
- if (!e.isPropagationStopped() && this.props.Document.workspaceLibrary) {
+ if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.workspaces) {
ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" });
e.stopPropagation();
@@ -552,31 +546,10 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
outerXf = () => Utils.GetScreenTransform(this._mainEle!);
onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {});
- openNotifsCol = () => {
- if (CollectionTreeView.NotifsCol) {
- this.props.addDocTab(CollectionTreeView.NotifsCol, undefined, "onRight");
- }
- }
- @computed get renderNotifsButton() {
- const length = CollectionTreeView.NotifsCol ? DocListCast(CollectionTreeView.NotifsCol.data).length : 0;
- const notifsRef = React.createRef<HTMLDivElement>();
- const dragNotifs = action(() => CollectionTreeView.NotifsCol!);
- return <div id="toolbar" key="toolbar">
- <div ref={notifsRef}>
- <button className="toolbar-button round-button" title="Notifs"
- onClick={this.openNotifsCol} onPointerDown={CollectionTreeView.NotifsCol ? SetupDrag(notifsRef, dragNotifs) : emptyFunction}>
- <FontAwesomeIcon icon={faBell} size="sm" />
- </button>
- <div className="main-notifs-badge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}>
- {length}
- </div>
- </div>
- </div >;
- }
@computed get renderClearButton() {
return <div id="toolbar" key="toolbar">
- <button className="toolbar-button round-button" title="Notifs"
+ <button className="toolbar-button round-button" title="Empty"
onClick={undoBatch(action(() => Doc.GetProto(this.props.Document)[this.props.fieldKey] = undefined))}>
<FontAwesomeIcon icon={faTrash} size="sm" />
</button>
@@ -584,38 +557,36 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
render() {
- Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
return !this.childDocs ? (null) : (
<div id="body" className="collectionTreeView-dropTarget"
- style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray") }}
+ style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document.yMargin, 20)}px` }}
onContextMenu={this.onContextMenu}
onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
onDrop={this.onTreeDrop}
ref={this.createTreeDropTarget}>
<EditableView
- contents={this.resolvedDataDoc.title}
+ contents={this.dataDoc.title}
display={"block"}
maxHeight={72}
height={"auto"}
- GetValue={() => StrCast(this.resolvedDataDoc.title)}
- SetValue={undoBatch((value: string) => (Doc.GetProto(this.resolvedDataDoc).title = value) ? true : true)}
+ GetValue={() => StrCast(this.dataDoc.title)}
+ SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
OnFillDown={undoBatch((value: string) => {
- Doc.GetProto(this.props.Document).title = value;
+ Doc.SetInPlace(this.dataDoc, "title", value, false);
let doc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined;
if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
TreeView.loadId = doc[Id];
Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true, false, false, false);
})} />
- {this.props.Document.workspaceLibrary ? this.renderNotifsButton : (null)}
{this.props.Document.allowClear ? this.renderClearButton : (null)}
<ul className="no-indent" style={{ width: "max-content" }} >
{
TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove,
moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
- this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth, () => this.props.Document.chromeStatus !== "disabled",
+ this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth, () => !this.props.Document.hideHeaderFields,
BoolCast(this.props.Document.preventTreeViewOpen), [])
}
</ul>
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
deleted file mode 100644
index 509851ebb..000000000
--- a/src/client/views/collections/CollectionVideoView.scss
+++ /dev/null
@@ -1,51 +0,0 @@
-
-.collectionVideoView-cont{
- width: 100%;
- height: 100%;
- position: inherit;
- top: 0;
- left:0;
- z-index: -1;
- display:inline-table;
-}
-.collectionVideoView-time{
- color : white;
- top :25px;
- left : 25px;
- position: absolute;
- background-color: rgba(50, 50, 50, 0.2);
- transform-origin: left top;
-}
-.collectionVideoView-snapshot{
- color : white;
- top :25px;
- right : 25px;
- position: absolute;
- background-color: rgba(50, 50, 50, 0.2);
- transform-origin: left top;
-}
-.collectionVideoView-play {
- width: 25px;
- height: 20px;
- bottom: 25px;
- left : 25px;
- position: absolute;
- color : white;
- background-color: rgba(50, 50, 50, 0.2);
- border-radius: 4px;
- text-align: center;
- transform-origin: left bottom;
-}
-.collectionVideoView-full {
- width: 25px;
- height: 20px;
- bottom: 25px;
- right : 25px;
- position: absolute;
- color : white;
- background-color: rgba(50, 50, 50, 0.2);
- border-radius: 4px;
- text-align: center;
- transform-origin: right bottom;
-
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
deleted file mode 100644
index 5185d9d0e..000000000
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { action } from "mobx";
-import { observer } from "mobx-react";
-import { NumCast } from "../../../new_fields/Types";
-import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import { VideoBox } from "../nodes/VideoBox";
-import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView";
-import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import "./CollectionVideoView.scss";
-import React = require("react");
-import { InkingControl } from "../InkingControl";
-import { InkTool } from "../../../new_fields/InkField";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-
-
-@observer
-export class CollectionVideoView extends React.Component<FieldViewProps> {
- private _videoBox?: VideoBox;
-
- public static LayoutString(fieldKey: string = "data", fieldExt: string = "annotations") {
- return FieldView.LayoutString(CollectionVideoView, fieldKey, fieldExt);
- }
- private get uIButtons() {
- let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
- let curTime = NumCast(this.props.Document.curPage);
- return ([<div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling})` }}>
- <span>{"" + Math.round(curTime)}</span>
- <span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
- </div>,
- <div className="collectionVideoView-snapshot" key="time" onPointerDown={this.onSnapshot} style={{ transform: `scale(${scaling})` }}>
- <FontAwesomeIcon icon="camera" size="lg" />
- </div>,
- VideoBox._showControls ? (null) : [
- <div className="collectionVideoView-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling})` }}>
- <FontAwesomeIcon icon={this._videoBox && this._videoBox.Playing ? "pause" : "play"} size="lg" />
- </div>,
- <div className="collectionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling})` }}>
- F
- </div>
- ]]);
- }
-
- @action
- onPlayDown = () => {
- if (this._videoBox) {
- if (this._videoBox.Playing) {
- this._videoBox.Pause();
- } else {
- this._videoBox.Play();
- }
- }
- }
-
- @action
- onFullDown = (e: React.PointerEvent) => {
- if (this._videoBox) {
- this._videoBox.FullScreen();
- e.stopPropagation();
- e.preventDefault();
- }
- }
-
- @action
- onSnapshot = (e: React.PointerEvent) => {
- if (this._videoBox) {
- this._videoBox.Snapshot();
- e.stopPropagation();
- e.preventDefault();
- }
- }
-
- _isclick = 0;
- @action
- onResetDown = (e: React.PointerEvent) => {
- if (this._videoBox) {
- this._videoBox.Pause();
- e.stopPropagation();
- this._isclick = 0;
- document.addEventListener("pointermove", this.onPointerMove, true);
- document.addEventListener("pointerup", this.onPointerUp, true);
- InkingControl.Instance.switchTool(InkTool.Eraser);
- }
- }
-
- @action
- onPointerMove = (e: PointerEvent) => {
- this._isclick += Math.abs(e.movementX) + Math.abs(e.movementY);
- if (this._videoBox) {
- this._videoBox.Seek(Math.max(0, NumCast(this.props.Document.curPage, 0) + Math.sign(e.movementX) * 0.0333));
- }
- e.stopImmediatePropagation();
- }
- @action
- onPointerUp = (e: PointerEvent) => {
- document.removeEventListener("pointermove", this.onPointerMove, true);
- document.removeEventListener("pointerup", this.onPointerUp, true);
- InkingControl.Instance.switchTool(InkTool.None);
- this._isclick < 10 && (this.props.Document.curPage = 0);
- }
- setVideoBox = (videoBox: VideoBox) => { this._videoBox = videoBox; };
-
- private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
- let props = { ...this.props, ...renderProps };
- return (<>
- <CollectionFreeFormView {...props} setVideoBox={this.setVideoBox} CollectionView={this} chromeCollapsed={true} />
- {this.props.isSelected() ? this.uIButtons : (null)}
- </>);
- }
-
- render() {
- return (
- <CollectionBaseView {...this.props} className="collectionVideoView-cont" >
- {this.subView}
- </CollectionBaseView>);
- }
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
new file mode 100644
index 000000000..e4187e4d6
--- /dev/null
+++ b/src/client/views/collections/CollectionView.scss
@@ -0,0 +1,26 @@
+@import "../globalCssVariables";
+
+.collectionView {
+ border-width: 0;
+ border-color: $light-color-secondary;
+ border-style: solid;
+ border-radius: 0 0 $border-radius $border-radius;
+ box-sizing: border-box;
+ border-radius: inherit;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+}
+
+#google-tags {
+ transition: all 0.5s ease 0s;
+ width: 30px;
+ height: 30px;
+ position: absolute;
+ bottom: 15px;
+ left: 15px;
+ border: 2px solid black;
+ border-radius: 50%;
+ padding: 3px;
+ background: white;
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index d3072ff1e..8d5694bf0 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,38 +1,96 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faEye } from '@fortawesome/free-regular-svg-icons';
-import { faColumns, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree, faCopy } from '@fortawesome/free-solid-svg-icons';
+import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from "mobx-react";
import * as React from 'react';
-import { Doc } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
-import { StrCast } from '../../../new_fields/Types';
+import { StrCast, BoolCast, Cast } from '../../../new_fields/Types';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from '../ContextMenuItem';
-import { FieldView, FieldViewProps } from '../nodes/FieldView';
-import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from './CollectionBaseView';
import { CollectionDockingView } from "./CollectionDockingView";
+import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionSchemaView } from "./CollectionSchemaView";
import { CollectionStackingView } from './CollectionStackingView';
import { CollectionTreeView } from "./CollectionTreeView";
import { CollectionViewBaseChrome } from './CollectionViewChromes';
-import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
+import { ImageUtils } from '../../util/Import & Export/ImageUtils';
+import { CollectionLinearView } from '../CollectionLinearView';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { ImageField } from '../../../new_fields/URLField';
+import { DocListCast } from '../../../new_fields/Doc';
+import Lightbox from 'react-image-lightbox-with-rotate';
+import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
export const COLLECTION_BORDER_WIDTH = 2;
-
+import { DateField } from '../../../new_fields/DateField';
+import { Doc, } from '../../../new_fields/Doc';
+import { listSpec } from '../../../new_fields/Schema';
+import { DocumentManager } from '../../util/DocumentManager';
+import { SelectionManager } from '../../util/SelectionManager';
+import './CollectionView.scss';
+import { FieldViewProps, FieldView } from '../nodes/FieldView';
library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
+export enum CollectionViewType {
+ Invalid,
+ Freeform,
+ Schema,
+ Docking,
+ Tree,
+ Stacking,
+ Masonry,
+ Pivot,
+ Linear,
+}
+
+export namespace CollectionViewType {
+ const stringMapping = new Map<string, CollectionViewType>([
+ ["invalid", CollectionViewType.Invalid],
+ ["freeform", CollectionViewType.Freeform],
+ ["schema", CollectionViewType.Schema],
+ ["docking", CollectionViewType.Docking],
+ ["tree", CollectionViewType.Tree],
+ ["stacking", CollectionViewType.Stacking],
+ ["masonry", CollectionViewType.Masonry],
+ ["pivot", CollectionViewType.Pivot],
+ ["linear", CollectionViewType.Linear]
+ ]);
+
+ export const valueOf = (value: string) => stringMapping.get(value.toLowerCase());
+}
+
+export interface CollectionRenderProps {
+ addDocument: (document: Doc) => boolean;
+ removeDocument: (document: Doc) => boolean;
+ moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+ active: () => boolean;
+ whenActiveChanged: (isActive: boolean) => void;
+}
+
@observer
export class CollectionView extends React.Component<FieldViewProps> {
- @observable private _collapsed = true;
+ public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
private _reactionDisposer: IReactionDisposer | undefined;
+ private _isChildActive = false; //TODO should this be observable?
+ @observable private _isLightboxOpen = false;
+ @observable private _curLightboxImg = 0;
+ @observable private _collapsed = true;
+ @observable private static _safeMode = false;
+ public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
- public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); }
-
- constructor(props: any) {
- super(props);
+ get collectionViewType(): CollectionViewType | undefined {
+ let viewField = Cast(this.props.Document.viewType, "number");
+ if (CollectionView._safeMode) {
+ if (viewField === CollectionViewType.Freeform) {
+ return CollectionViewType.Tree;
+ }
+ if (viewField === CollectionViewType.Invalid) {
+ return CollectionViewType.Freeform;
+ }
+ }
+ return viewField === undefined ? CollectionViewType.Invalid : viewField;
}
componentDidMount = () => {
@@ -47,26 +105,73 @@ export class CollectionView extends React.Component<FieldViewProps> {
});
}
- componentWillUnmount = () => {
- this._reactionDisposer && this._reactionDisposer();
+ componentWillUnmount = () => this._reactionDisposer && this._reactionDisposer();
+
+ // bcz: Argh? What's the height of the collection chromes??
+ chromeHeight = () => (this.props.ChromeHeight ? this.props.ChromeHeight() : 0) + (this.props.Document.chromeStatus === "enabled" ? -60 : 0);
+
+ active = () => this.props.isSelected() || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0;
+
+ whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); };
+
+ @action.bound
+ addDocument(doc: Doc): boolean {
+ let targetDataDoc = Doc.GetProto(this.props.Document);
+ Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
+ let extension = Doc.fieldExtensionDoc(targetDataDoc, this.props.fieldKey); // set metadata about the field being rendered (ie, the set of documents) on an extension field for that field
+ extension && (extension.lastModified = new DateField(new Date(Date.now())));
+ Doc.GetProto(doc).lastOpened = new DateField;
+ return true;
+ }
+
+ @action.bound
+ removeDocument(doc: Doc): boolean {
+ let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
+ docView && SelectionManager.DeselectDoc(docView);
+ let value = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
+ index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
+
+ ContextMenu.Instance.clearItems();
+ if (index !== -1) {
+ value.splice(index, 1);
+ 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, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
+ if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
+ return true;
+ }
+ return this.removeDocument(doc) ? addDocument(doc) : false;
+ }
+
+ showIsTagged = () => {
+ const children = DocListCast(this.props.Document[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"} />;
}
private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
- let props = { ...this.props, ...renderProps };
- switch (this.isAnnotationOverlay ? CollectionViewType.Freeform : type) {
- case CollectionViewType.Schema: return (<CollectionSchemaView chromeCollapsed={this._collapsed} key="collview" {...props} CollectionView={this} />);
- // currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip
- case CollectionViewType.Docking: return (<CollectionDockingView chromeCollapsed={true} key="collview" {...props} CollectionView={this} />);
- case CollectionViewType.Tree: return (<CollectionTreeView chromeCollapsed={this._collapsed} key="collview" {...props} CollectionView={this} />);
- case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView chromeCollapsed={this._collapsed} key="collview" {...props} CollectionView={this} />); }
- case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView chromeCollapsed={this._collapsed} key="collview" {...props} CollectionView={this} />); }
- case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (<CollectionFreeFormView chromeCollapsed={this._collapsed} key="collview" {...props} CollectionView={this} />); }
+ let props = { ...this.props, ...renderProps, chromeCollapsed: this._collapsed, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
+ switch (type) {
+ case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
+ case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
+ case CollectionViewType.Tree: return (<CollectionTreeView key="collview" {...props} />);
+ case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); }
+ case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView key="collview" {...props} />); }
+ case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView key="collview" {...props} />); }
+ case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (<CollectionFreeFormView key="collview" {...props} />); }
case CollectionViewType.Freeform:
- default:
- this.props.Document.freeformLayoutEngine = undefined;
- return (<CollectionFreeFormView chromeCollapsed={this._collapsed} key="collview" {...props} CollectionView={this} />);
+ default: { this.props.Document.freeformLayoutEngine = undefined; return (<CollectionFreeFormView key="collview" {...props} />); }
}
- return (null);
}
@action
@@ -77,23 +182,18 @@ export class CollectionView extends React.Component<FieldViewProps> {
private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
// currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip
- if (this.isAnnotationOverlay || this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking) {
- return [(null), this.SubViewHelper(type, renderProps)];
- }
- return [
- <CollectionViewBaseChrome CollectionView={this} key="chrome" type={type} collapse={this.collapse} />,
- this.SubViewHelper(type, renderProps)
- ];
+ let chrome = this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) :
+ <CollectionViewBaseChrome CollectionView={this} key="chrome" type={type} collapse={this.collapse} />;
+ return [chrome, this.SubViewHelper(type, renderProps)];
}
- get isAnnotationOverlay() { return this.props.fieldExt ? true : false; }
onContextMenu = (e: React.MouseEvent): void => {
- if (!this.isAnnotationOverlay && !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 (!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
let existingVm = ContextMenu.Instance.findByDescription("View Modes...");
- let subItems: ContextMenuProps[] = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
+ let subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; }, icon: "signature" });
- if (CollectionBaseView.InSafeMode()) {
+ if (CollectionView._safeMode) {
ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" });
}
subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" });
@@ -113,20 +213,43 @@ export class CollectionView extends React.Component<FieldViewProps> {
break;
}
}
+ subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
!existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
let existing = ContextMenu.Instance.findByDescription("Layout...");
- let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
+ let layoutItems = existing && "subitems" in existing ? existing.subitems : [];
layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
!existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
+ ContextMenu.Instance.addItem({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
}
}
+ lightbox = (images: string[]) => {
+ return !this._isLightboxOpen ? (null) : (<Lightbox key="lightbox"
+ mainSrc={images[this._curLightboxImg]}
+ nextSrc={images[(this._curLightboxImg + 1) % images.length]}
+ prevSrc={images[(this._curLightboxImg + images.length - 1) % images.length]}
+ onCloseRequest={action(() => this._isLightboxOpen = false)}
+ onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
+ onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
+ }
render() {
- return (
- <CollectionBaseView {...this.props} onContextMenu={this.onContextMenu}>
- {this.SubView}
- </CollectionBaseView>
- );
+ const props: CollectionRenderProps = {
+ addDocument: this.addDocument,
+ removeDocument: this.removeDocument,
+ moveDocument: this.moveDocument,
+ active: this.active,
+ whenActiveChanged: this.whenActiveChanged,
+ };
+ return (<div className={"collectionView"}
+ style={{
+ pointerEvents: this.props.Document.isBackground ? "none" : "all",
+ boxShadow: this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined : `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
+ }}
+ onContextMenu={this.onContextMenu}>
+ {this.showIsTagged()}
+ {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
+ {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d => Cast(d.data, ImageField) ? Cast(d.data, ImageField)!.url.href : ""))}
+ </div>);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index cefa9eebc..fb87996c6 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -10,12 +10,11 @@ import { ScriptField } from "../../../new_fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { Utils, emptyFunction } from "../../../Utils";
import { DragManager } from "../../util/DragManager";
-import { CompileScript } from "../../util/Scripting";
import { undoBatch } from "../../util/UndoManager";
import { EditableView } from "../EditableView";
import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
import { DocLike } from "../MetadataEntryMenu";
-import { CollectionViewType } from "./CollectionBaseView";
+import { CollectionViewType } from "./CollectionView";
import { CollectionView } from "./CollectionView";
import "./CollectionViewChromes.scss";
import * as Autosuggest from 'react-autosuggest';
@@ -41,9 +40,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
//(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
_templateCommand = {
- title: "set template", script: "this.target.childLayout = this.source ? this.source[0] : undefined", params: ["target", "source"],
+ title: "set template", script: "setChildLayout(this.target, this.source && this.source.length ? this.source[0]:undefined)", params: ["target", "source"],
initialize: emptyFunction,
- immediate: (draggedDocs: Doc[]) => this.props.CollectionView.props.Document.childLayout = draggedDocs.length ? draggedDocs[0] : undefined
+ immediate: (draggedDocs: Doc[]) => Doc.setChildLayout(this.props.CollectionView.props.Document, draggedDocs.length ? draggedDocs[0] : undefined)
};
_contentCommand = {
// title: "set content", script: "getProto(this.target).data = aliasDocs(this.source.map(async p => await p));", params: ["target", "source"], // bcz: doesn't look like we can do async stuff in scripting...
@@ -242,6 +241,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />);
+ case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />);
default: return null;
}
}
@@ -373,7 +373,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
render() {
let collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled";
return (
- <div className="collectionViewChrome-cont" style={{ top: collapsed ? -70 : 0 }}>
+ <div className="collectionViewChrome-cont" style={{ top: collapsed ? -70 : 0, height: collapsed ? 0 : undefined }}>
<div className="collectionViewChrome">
<div className="collectionViewBaseChrome">
<button className="collectionViewBaseChrome-collapse"
@@ -397,6 +397,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking View</option>
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry View</option>
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="7">Pivot View</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="8">Linear View</option>
</select>
<div className="collectionViewBaseChrome-viewSpecs" style={{ display: collapsed ? "none" : "grid" }}>
<input className="collectionViewBaseChrome-viewSpecsInput"
@@ -477,12 +478,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
getKeySuggestions = async (value: string): Promise<string[]> => {
value = value.toLowerCase();
- let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
- = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
- if (typeof docs === "function") {
- docs = docs();
- }
- docs = await docs;
+ let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
if (docs instanceof Doc) {
return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
} else {
@@ -588,19 +584,9 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
if (textwrappedRows.length) {
this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>([]);
} else {
- let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
- = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
- if (typeof docs === "function") {
- docs = docs();
- }
- docs = await docs;
- if (docs instanceof Doc) {
- let allRows = [docs[Id]];
- this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
- } else {
- let allRows = docs.map(doc => doc[Id]);
- this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
- }
+ let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
+ let allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
+ this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
}
}
@@ -635,63 +621,14 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
@observer
export class CollectionTreeViewChrome extends React.Component<CollectionViewChromeProps> {
- @observable private _currentKey: string = "";
- @observable private suggestions: string[] = [];
@computed private get descending() { return Cast(this.props.CollectionView.props.Document.sortAscending, "boolean", null); }
- @computed get sectionFilter() { return StrCast(this.props.CollectionView.props.Document.sectionFilter); }
-
- getKeySuggestions = async (value: string): Promise<string[]> => {
- value = value.toLowerCase();
- let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
- = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
- if (typeof docs === "function") {
- docs = docs();
- }
- docs = await docs;
- if (docs instanceof Doc) {
- return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
- } else {
- const keys = new Set<string>();
- docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
- return Array.from(keys).filter(key => key.toLowerCase().startsWith(value));
- }
- }
-
- @action
- onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
- this._currentKey = newValue;
- }
-
- getSuggestionValue = (suggestion: string) => suggestion;
-
- renderSuggestion = (suggestion: string) => {
- return <p>{suggestion}</p>;
- }
-
- onSuggestionFetch = async ({ value }: { value: string }) => {
- const sugg = await this.getKeySuggestions(value);
- runInAction(() => {
- this.suggestions = sugg;
- });
- }
-
- @action
- onSuggestionClear = () => {
- this.suggestions = [];
- }
-
- setValue = (value: string) => {
- this.props.CollectionView.props.Document.sectionFilter = value;
- return true;
- }
@action toggleSort = () => {
if (this.props.CollectionView.props.Document.sortAscending) this.props.CollectionView.props.Document.sortAscending = undefined;
else if (this.props.CollectionView.props.Document.sortAscending === undefined) this.props.CollectionView.props.Document.sortAscending = false;
else this.props.CollectionView.props.Document.sortAscending = true;
}
- @action resetValue = () => { this._currentKey = this.sectionFilter; };
render() {
return (
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 7f2913214..8b6fa330c 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -7,7 +7,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { SearchUtil } from "../../util/SearchUtil";
import { CollectionDockingView } from "./CollectionDockingView";
import { NumCast } from "../../../new_fields/Types";
-import { CollectionViewType } from "./CollectionBaseView";
+import { CollectionViewType } from "./CollectionView";
import { DocumentButtonBar } from "../DocumentButtonBar";
import { DocumentManager } from "../../util/DocumentManager";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 886692172..48d330674 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -63,11 +63,12 @@ export function computePivotLayout(pivotDoc: Doc, childDocs: Doc[], childPairs:
fontSize: NumCast(pivotDoc.pivotFontSize, 10)
});
for (const doc of val) {
+ let layoutDoc = Doc.Layout(doc);
docMap.set(doc, {
x: x + xCount * pivotAxisWidth * 1.25,
y: -y,
width: pivotAxisWidth,
- height: doc.nativeWidth ? (NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth)) * pivotAxisWidth : pivotAxisWidth
+ height: layoutDoc.nativeWidth ? (NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth)) * pivotAxisWidth : pivotAxisWidth
});
xCount++;
if (xCount >= numCols) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
index cfd18ad35..75af11537 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -1,20 +1,18 @@
.collectionfreeformlinkview-linkLine {
stroke: black;
- transform: translate(10000px,10000px);
opacity: 0.8;
pointer-events: all;
stroke-width: 3px;
+ transition: opacity 0.5s ease-in;
}
.collectionfreeformlinkview-linkCircle {
stroke: rgb(0,0,0);
opacity: 0.5;
- transform: translate(10000px,10000px);
pointer-events: all;
cursor: pointer;
}
.collectionfreeformlinkview-linkText {
stroke: rgb(0,0,0);
opacity: 0.5;
- transform: translate(10000px,10000px);
pointer-events: all;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index df089eb00..837413842 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,63 +1,51 @@
import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc";
-import { BoolCast, NumCast, StrCast } from "../../../../new_fields/Types";
-import { InkingControl } from "../../InkingControl";
+import { Doc } from "../../../../new_fields/Doc";
+import { Utils } from '../../../../Utils';
+import { DocumentView } from "../../nodes/DocumentView";
import "./CollectionFreeFormLinkView.scss";
import React = require("react");
import v5 = require("uuid/v5");
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { observable, action } from "mobx";
export interface CollectionFreeFormLinkViewProps {
- A: Doc;
- B: Doc;
+ A: DocumentView;
+ B: DocumentView;
LinkDocs: Doc[];
- addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
- removeDocument: (document: Doc) => boolean;
}
@observer
export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
-
- onPointerDown = (e: React.PointerEvent) => {
- if (e.button === 0 && !InkingControl.Instance.selectedTool) {
- let a = this.props.A;
- let b = this.props.B;
- let x1 = NumCast(a.x) + (BoolCast(a.isMinimized) ? 5 : a[WidthSym]() / 2);
- let y1 = NumCast(a.y) + (BoolCast(a.isMinimized) ? 5 : a[HeightSym]() / 2);
- let x2 = NumCast(b.x) + (BoolCast(b.isMinimized) ? 5 : b[WidthSym]() / 2);
- let y2 = NumCast(b.y) + (BoolCast(b.isMinimized) ? 5 : b[HeightSym]() / 2);
- // this.props.LinkDocs.map(l => {
- // let width = l[WidthSym]();
- // l.x = (x1 + x2) / 2 - width / 2;
- // l.y = (y1 + y2) / 2 + 10;
- // if (!this.props.removeDocument(l)) this.props.addDocument(l, false);
- // });
- e.stopPropagation();
- e.preventDefault();
- }
+ @observable _alive: number = 0;
+ @observable _opacity: number = 1;
+ @action
+ componentDidMount() {
+ this._alive = 1;
+ setTimeout(this.rerender, 50);
+ setTimeout(action(() => this._opacity = 0.05), 50);
+ }
+ @action
+ componentWillUnmount() {
+ this._alive = 0;
}
+ rerender = action(() => {
+ if (this._alive) {
+ setTimeout(this.rerender, 50);
+ this._alive++;
+ }
+ });
+
render() {
- // let l = this.props.LinkDocs;
- let a = this.props.A;
- let b = this.props.B;
- let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / 2);
- let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / 2);
- let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2);
- let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / 2);
- let text = "";
- // let first = this.props.LinkDocs[0];
- // if (this.props.LinkDocs.length === 1) text += first.title + (first.linkDescription ? "(" + StrCast(first.linkDescription) + ")" : "");
- // else text = "-multiple-";
- return (
- <>
- <line key="linkLine" className="collectionfreeformlinkview-linkLine"
- x1={`${x1}`} y1={`${y1}`}
- x2={`${x2}`} y2={`${y2}`} />
- {/* <circle key="linkCircle" className="collectionfreeformlinkview-linkCircle"
- cx={(x1 + x2) / 2} cy={(y1 + y2) / 2} r={8} onPointerDown={this.onPointerDown} /> */}
- <text key="linkText" textAnchor="middle" className="collectionfreeformlinkview-linkText" x={`${(x1 + x2) / 2}`} y={`${(y1 + y2) / 2}`}>
- {text}
- </text>
- </>
- );
+ let y = this._alive;
+ let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
+ let b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect();
+ let pt1 = Utils.getNearestPointInPerimeter(a.left, a.top, a.width, a.height, b.left + b.width / 2, b.top + b.height / 2);
+ let pt2 = Utils.getNearestPointInPerimeter(b.left, b.top, b.width, b.height, a.left + a.width / 2, a.top + a.height / 2);
+ return (<line key="linkLine" className="collectionfreeformlinkview-linkLine"
+ style={{ opacity: this._opacity }}
+ x1={`${pt1[0]}`} y1={`${pt1[1]}`}
+ x2={`${pt2[0]}`} y2={`${pt2[1]}`} />);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
index 30e158603..cb5cef29c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
@@ -1,10 +1,9 @@
.collectionfreeformlinksview-svgCanvas{
- transform: translate(-10000px,-10000px);
position: absolute;
top: 0;
left: 0;
- width: 20000px;
- height: 20000px;
+ width: 100%;
+ height: 100%;
pointer-events: none;
}
.collectionfreeformlinksview-container {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index a81f5315a..e9191c176 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,19 +1,18 @@
-import { computed, IReactionDisposer, reaction } from "mobx";
+import { computed, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../new_fields/Doc";
import { Id } from "../../../../new_fields/FieldSymbols";
-import { List } from "../../../../new_fields/List";
-import { listSpec } from "../../../../new_fields/Schema";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
import { DocumentManager } from "../../../util/DocumentManager";
import { DocumentView } from "../../nodes/DocumentView";
-import { CollectionViewProps } from "../CollectionSubView";
import "./CollectionFreeFormLinksView.scss";
import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
import React = require("react");
+import { Utils } from "../../../../Utils";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { DocumentType } from "../../../documents/DocumentTypes";
@observer
-export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
+export class CollectionFreeFormLinksView extends React.Component {
_brushReactionDisposer?: IReactionDisposer;
componentDidMount() {
@@ -69,59 +68,33 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
// });
}
componentWillUnmount() {
- if (this._brushReactionDisposer) {
- this._brushReactionDisposer();
- }
+ this._brushReactionDisposer && this._brushReactionDisposer();
}
- documentAnchors(view: DocumentView) {
- let equalViews = [view];
- let containerDoc = FieldValue(Cast(view.props.Document.annotationOn, Doc));
- if (containerDoc) {
- equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.proto!);
- }
- if (view.props.ContainingCollectionDoc) {
- let collid = view.props.ContainingCollectionDoc[Id];
- DocListCast(this.props.Document[this.props.fieldKey]).
- filter(child =>
- child[Id] === collid).map(view =>
- DocumentManager.Instance.getDocumentViews(view).map(view =>
- equalViews.push(view)));
- }
- return equalViews.filter(sv => sv.props.ContainingCollectionDoc === this.props.Document);
- }
-
@computed
get uniqueConnections() {
let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
- let srcViews = this.documentAnchors(connection.a);
- let targetViews = this.documentAnchors(connection.b);
-
- let possiblePairs: { a: Doc, b: Doc, }[] = [];
- srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document })));
- possiblePairs.map(possiblePair => {
- if (!drawnPairs.reduce((found, drawnPair) => {
- let match1 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.a) && Doc.AreProtosEqual(possiblePair.b, drawnPair.b));
- let match2 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.b) && Doc.AreProtosEqual(possiblePair.b, drawnPair.a));
- let match = match1 || match2;
- if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) {
- drawnPair.l.push(connection.l);
- }
- return match || found;
- }, false)) {
- drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] });
+ if (!drawnPairs.reduce((found, drawnPair) => {
+ let match1 = (connection.a === drawnPair.a && connection.b === drawnPair.b);
+ let match2 = (connection.a === drawnPair.b && connection.b === drawnPair.a);
+ let match = match1 || match2;
+ if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) {
+ drawnPair.l.push(connection.l);
}
- });
+ return match || found;
+ }, false)) {
+ drawnPairs.push({ a: connection.a, b: connection.b, l: [connection.l] });
+ }
return drawnPairs;
- }, [] as { a: Doc, b: Doc, l: Doc[] }[]);
- return connections.map(c => <CollectionFreeFormLinkView key={c.l.reduce((p, l) => p + l[Id], "")} A={c.a} B={c.b} LinkDocs={c.l}
- removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />);
+ }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]);
+ return connections.filter(c => c.a.props.Document.type === DocumentType.LINK) // get rid of the filter to show links to documents in addition to document anchors
+ .map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
}
render() {
return (
<div className="collectionfreeformlinksview-container">
<svg className="collectionfreeformlinksview-svgCanvas">
- {this.uniqueConnections}
+ {SelectionManager.GetIsDragging() ? (null) : this.uniqueConnections}
</svg>
{this.props.children}
</div>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index c4311fa52..bb1a12f88 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -46,12 +46,6 @@
border-radius: inherit;
box-sizing: border-box;
position: absolute;
- overflow: hidden;
-
- .marqueeView {
- overflow: hidden;
- }
-
top: 0;
left: 0;
width: 100%;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c6e8d7cf7..2d7a557b5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,6 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faEye } from "@fortawesome/free-regular-svg-icons";
-import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
+import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
@@ -10,7 +10,7 @@ import { createSchema, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
-import { aggregateBounds, emptyFunction, intersectRect, returnEmptyString, returnOne, Utils } from "../../../../Utils";
+import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { DocServer } from "../../../DocServer";
import { Docs } from "../../../documents/Documents";
@@ -25,24 +25,20 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"
import { ContextMenu } from "../../ContextMenu";
import { ContextMenuProps } from "../../ContextMenuItem";
import { InkingCanvas } from "../../InkingCanvas";
-import { CollectionFreeFormDocumentView, positionSchema } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocumentContentsView } from "../../nodes/DocumentContentsView";
-import { documentSchema, DocumentViewProps } from "../../nodes/DocumentView";
+import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
+import { DocumentViewProps } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
-import PDFMenu from "../../pdf/PDFMenu";
import { CollectionSubView } from "../CollectionSubView";
import { computePivotLayout, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
-import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
-import v5 = require("uuid/v5");
+import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
import { Timeline } from "../../animationtimeline/Timeline";
-import { number } from "prop-types";
-library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard);
+library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
export const panZoomSchema = createSchema({
panX: "number",
@@ -54,6 +50,11 @@ export const panZoomSchema = createSchema({
isRuleProvider: "boolean",
fitToBox: "boolean",
panTransformType: "string",
+ scrollHeight: "number",
+ fitX: "number",
+ fitY: "number",
+ fitW: "number",
+ fitH: "number"
});
type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>;
@@ -71,16 +72,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; }
@computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; }
@computed get contentBounds() { return aggregateBounds(this.elements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)); }
- @computed get nativeWidth() { return this.fitToContent ? 0 : this.Document.nativeWidth || 0; }
+ @computed get nativeWidth() { return this.Document.fitToContent ? 0 : this.Document.nativeWidth || 0; }
@computed get nativeHeight() { return this.fitToContent ? 0 : this.Document.nativeHeight || 0; }
- private get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } // fieldExt will be "" or "annotation". should maybe generalize this, or make it more specific (ie, 'annotation' instead of 'fieldExt')
+ private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
private easing = () => this.props.Document.panTransformType === "Ease";
private panX = () => this.fitToContent ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document.panX || 0;
private panY = () => this.fitToContent ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document.panY || 0;
private zoomScaling = () => (1 / this.parentScaling) * (this.fitToContent ?
Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) :
- this.Document.scale || 1);
+ this.Document.scale || 1)
private centeringShiftX = () => !this.nativeWidth && !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling : 0; // shift so pan position is at center of window for non-overlay collections
private centeringShiftY = () => !this.nativeHeight && !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling : 0;// shift so pan position is at center of window for non-overlay collections
private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
@@ -97,10 +98,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
heading = !sorted.length ? Math.max(1, maxHeading) : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading);
}
!this.Document.isRuleProvider && (newBox.heading = heading);
- this.addDocument(newBox, false);
+ this.addDocument(newBox);
}
- private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
- let added = this.props.addDocument(newBox, false);
+ private addDocument = (newBox: Doc) => {
+ let added = this.props.addDocument(newBox);
added && this.bringToFront(newBox);
added && this.updateCluster(newBox);
return added;
@@ -109,20 +110,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true));
}
- public isCurrent(doc: Doc) { return !this.props.Document.isMinimized && (Math.abs(NumCast(doc.page, -1) - NumCast(this.Document.curPage, -1)) < 1.5 || NumCast(doc.page, -1) === -1); }
+ public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
public getActiveDocuments = () => {
return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
}
- @computed get fieldExtensionDoc() {
- return Doc.fieldExtensionDoc(this.props.DataDoc || this.props.Document, this.props.fieldKey);
- }
-
@action
- onDrop = (e: React.DragEvent): void => {
+ onDrop = (e: React.DragEvent): Promise<void> => {
var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
- super.onDrop(e, { x: pt[0], y: pt[1] });
+ return super.onDrop(e, { x: pt[0], y: pt[1] });
}
@undoBatch
@@ -135,21 +132,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (super.drop(e, de)) {
if (de.data instanceof DragManager.DocumentDragData) {
if (de.data.droppedDocuments.length) {
- let z = NumCast(de.data.droppedDocuments[0].z);
+ let firstDoc = de.data.droppedDocuments[0];
+ let z = NumCast(firstDoc.z);
let x = (z ? xpo : xp) - de.data.offset[0];
let y = (z ? ypo : yp) - de.data.offset[1];
- let dropX = NumCast(de.data.droppedDocuments[0].x);
- let dropY = NumCast(de.data.droppedDocuments[0].y);
+ let dropX = NumCast(firstDoc.x);
+ let dropY = NumCast(firstDoc.y);
de.data.droppedDocuments.forEach(action((d: Doc) => {
+ let layoutDoc = Doc.Layout(d);
d.x = x + NumCast(d.x) - dropX;
d.y = y + NumCast(d.y) - dropY;
- if (!NumCast(d.width)) {
- d.width = 300;
+ if (!NumCast(layoutDoc.width)) {
+ layoutDoc.width = 300;
}
- if (!NumCast(d.height)) {
- let nw = NumCast(d.nativeWidth);
- let nh = NumCast(d.nativeHeight);
- d.height = nw && nh ? nh / nw * NumCast(d.width) : 300;
+ if (!NumCast(layoutDoc.height)) {
+ let nw = NumCast(layoutDoc.nativeWidth);
+ let nh = NumCast(layoutDoc.nativeHeight);
+ layoutDoc.height = nw && nh ? nh / nw * NumCast(layoutDoc.width) : 300;
}
this.bringToFront(d);
}));
@@ -162,12 +161,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let dragDoc = de.data.dropDocument;
let x = xp - de.data.offset[0];
let y = yp - de.data.offset[1];
- let dropX = NumCast(de.data.dropDocument.x);
- let dropY = NumCast(de.data.dropDocument.y);
+ let dropX = NumCast(dragDoc.x);
+ let dropY = NumCast(dragDoc.y);
dragDoc.x = x + NumCast(dragDoc.x) - dropX;
dragDoc.y = y + NumCast(dragDoc.y) - dropY;
- de.data.targetContext = this.props.Document;
- dragDoc.targetContext = this.props.Document;
+ de.data.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
this.bringToFront(dragDoc);
}
}
@@ -177,11 +175,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
pickCluster(probe: number[]) {
return this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => {
+ let layoutDoc = Doc.Layout(cd);
let cx = NumCast(cd.x) - this._clusterDistance;
let cy = NumCast(cd.y) - this._clusterDistance;
- let cw = NumCast(cd.width) + 2 * this._clusterDistance;
- let ch = NumCast(cd.height) + 2 * this._clusterDistance;
- return !cd.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ?
+ let cw = NumCast(layoutDoc.width) + 2 * this._clusterDistance;
+ let ch = NumCast(layoutDoc.height) + 2 * this._clusterDistance;
+ return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ?
NumCast(cd.cluster) : cluster;
}, -1);
}
@@ -189,14 +188,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let cluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
if (cluster !== -1) {
let eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
-
- // hacky way to get a list of DocumentViews in the current view given a list of Documents in the current view
- let prevSelected = SelectionManager.SelectedDocuments();
- this.selectDocuments(eles);
- let clusterDocs = SelectionManager.SelectedDocuments();
- SelectionManager.DeselectAll();
- prevSelected.map(dv => SelectionManager.SelectDoc(dv, true));
-
+ let clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
let de = new DragManager.DocumentDragData(eles);
de.moveDocument = this.props.moveDocument;
const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0);
@@ -271,8 +263,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerDown = (e: React.PointerEvent): void => {
+ if (e.nativeEvent.cancelBubble) return;
this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false;
- if (e.button === 0 && !e.shiftKey && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1) && this.props.active()) {
+ if (e.button === 0 && !e.shiftKey && !e.altKey && !e.ctrlKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1) && this.props.active()) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointermove", this.onPointerMove);
@@ -289,7 +282,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerMove = (e: PointerEvent): void => {
- if (!e.cancelBubble && !this.isAnnotationOverlay) {
+ if (!e.cancelBubble) {
if (this._hitCluster && this.tryDragCluster(e)) {
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
@@ -302,20 +295,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let docs = this.childLayoutPairs.map(pair => pair.layout);
let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
if (!this.isAnnotationOverlay) {
- PDFMenu.Instance.fadeOut(true);
let minx = docs.length ? NumCast(docs[0].x) : 0;
- let maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
+ let maxx = docs.length ? NumCast(Doc.Layout(docs[0]).width) + minx : minx;
let miny = docs.length ? NumCast(docs[0].y) : 0;
- let maxy = docs.length ? NumCast(docs[0].height) + miny : miny;
+ let maxy = docs.length ? NumCast(Doc.Layout(docs[0]).height) + miny : miny;
let ranges = docs.filter(doc => doc).reduce((range, doc) => {
+ let layoutDoc = Doc.Layout(doc);
let x = NumCast(doc.x);
- let xe = x + NumCast(doc.width);
+ let xe = x + NumCast(layoutDoc.width);
let y = NumCast(doc.y);
- let ye = y + NumCast(doc.height);
+ let ye = y + NumCast(layoutDoc.height);
return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
[range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
}, [[minx, maxx], [miny, maxy]]);
- let ink = Cast(this.fieldExtensionDoc.ink, InkField);
+ let ink = this.extensionDoc && Cast(this.extensionDoc.ink, InkField);
if (ink && ink.inkData) {
ink.inkData.forEach((value: StrokeData, key: string) => {
let bounds = InkingCanvas.StrokeRect(value);
@@ -342,7 +335,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.props.Document.lockedPosition || this.isAnnotationOverlay) return;
+ if (this.props.Document.lockedPosition || this.props.Document.inOverlay) return;
if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
e.stopPropagation();
}
@@ -363,14 +356,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
@action
- setPan(panX: number, panY: number) {
- if (!this.props.Document.lockedPosition) {
- this.props.Document.panTransformType = "None";
+ setPan(panX: number, panY: number, panType: string = "None") {
+ if (!this.Document.lockedPosition || this.Document.inOverlay) {
+ this.Document.panTransformType = panType;
var scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
- const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.props.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY));
- this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
- this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
+ const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY));
+ this.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
+ this.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
}
}
@@ -407,21 +400,27 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
SelectionManager.DeselectAll();
if (this.props.Document.scrollHeight) {
let annotOn = Cast(doc.annotationOn, Doc) as Doc;
- let offset = annotOn && (NumCast(annotOn.height) / 2);
- this.props.Document.scrollY = NumCast(doc.y) - offset;
+ if (!annotOn) {
+ this.props.focus(doc);
+ } else {
+ let contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height);
+ let offset = annotOn && (contextHgt / 2 * 96 / 72);
+ this.props.Document.scrollY = NumCast(doc.y) - offset;
+ }
} else {
- const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
- const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
+ let layoutdoc = Doc.Layout(doc);
+ const newPanX = NumCast(doc.x) + NumCast(layoutdoc.width) / 2;
+ const newPanY = NumCast(doc.y) + NumCast(layoutdoc.height) / 2;
const newState = HistoryUtil.getState();
newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
HistoryUtil.pushState(newState);
let savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType };
- this.setPan(newPanX, newPanY);
- this.Document.panTransformType = "Ease";
+ this.setPan(newPanX, newPanY, "Ease");
+ Doc.BrushDoc(this.props.Document);
this.props.focus(this.props.Document);
- willZoom && this.setScaleToZoom(doc, scale);
+ willZoom && this.setScaleToZoom(layoutdoc, scale);
afterFocus && setTimeout(() => {
if (afterFocus && afterFocus()) {
@@ -447,11 +446,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
return {
+ ...this.props,
DataDoc: childData,
Document: childLayout,
- addDocument: this.props.addDocument,
- removeDocument: this.props.removeDocument,
- moveDocument: this.props.moveDocument,
+ layoutKey: undefined,
ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves
onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
@@ -464,37 +462,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
focus: this.focusDocument,
backgroundColor: this.getClusterColor,
parentActive: this.props.active,
- whenActiveChanged: this.props.whenActiveChanged,
- bringToFront: this.bringToFront,
- addDocTab: this.props.addDocTab,
- pinToPres: this.props.pinToPres,
- zoomToScale: this.zoomToScale,
- getScale: this.getScale
- };
- }
- getDocumentViewProps(layoutDoc: Doc): DocumentViewProps {
- return {
- DataDoc: this.props.DataDoc,
- Document: this.props.Document,
- addDocument: this.props.addDocument,
- removeDocument: this.props.removeDocument,
- moveDocument: this.props.moveDocument,
- ruleProvider: this.props.ruleProvider,
- onClick: this.props.onClick,
- ScreenToLocalTransform: this.getTransform,
- renderDepth: this.props.renderDepth,
- PanelWidth: layoutDoc[WidthSym],
- PanelHeight: layoutDoc[HeightSym],
- ContentScaling: returnOne,
- ContainingCollectionView: this.props.CollectionView,
- ContainingCollectionDoc: this.props.ContainingCollectionDoc,
- focus: this.focusDocument,
- backgroundColor: returnEmptyString,
- parentActive: this.props.active,
- whenActiveChanged: this.props.whenActiveChanged,
bringToFront: this.bringToFront,
- addDocTab: this.props.addDocTab,
- pinToPres: this.props.pinToPres,
zoomToScale: this.zoomToScale,
getScale: this.getScale
};
@@ -503,10 +471,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, transition?: string, state?: any } {
const script = this.Document.arrangeScript;
const result = script && script.script.run(params, console.log);
+ const layoutDoc = Doc.Layout(params.doc);
if (result && result.success) {
return { ...result, transition: "transform 1s" };
}
- return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(params.doc.width, "number"), height: Cast(params.doc.height, "number") };
+ return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(layoutDoc.width, "number"), height: Cast(layoutDoc.height, "number") };
}
viewDefsToJSX = (views: any[]) => {
@@ -623,7 +592,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// find rule colorations when rule providing is turned on by looking at each document to see if it has a coloring -- if so, use it's color as the rule for its associated heading.
this.Document.isRuleProvider && this.childLayoutPairs.map(pair =>
// iterate over the children of a displayed document (or if the displayed document is a template, iterate over the children of that template)
- DocListCast(pair.layout.layout instanceof Doc ? pair.layout.layout.data : pair.layout.data).map(heading => {
+ DocListCast(Doc.Layout(pair.layout).data).map(heading => {
let headingPair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, heading);
let headingLayout = headingPair.layout && (pair.layout.data_ext instanceof Doc) && (pair.layout.data_ext[`Layout[${headingPair.layout[Id]}]`] as Doc) || headingPair.layout;
if (headingLayout && NumCast(headingLayout.heading) > 0 && headingLayout.backgroundColor !== headingLayout.defaultBackgroundColor) {
@@ -634,16 +603,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
analyzeStrokes = async () => {
- let data = Cast(this.fieldExtensionDoc.ink, InkField);
- if (data) {
- CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.fieldExtensionDoc, ["inkAnalysis", "handwriting"], data.inkData);
+ const extensionDoc = this.extensionDoc;
+ let data = extensionDoc && Cast(extensionDoc.ink, InkField);
+ if (data && extensionDoc) {
+ CognitiveServices.Inking.Appliers.ConcatenateHandwriting(extensionDoc, ["inkAnalysis", "handwriting"], data.inkData);
}
}
onContextMenu = (e: React.MouseEvent) => {
let layoutItems: ContextMenuProps[] = [];
- if (this.childDocs.some(d => BoolCast(d.isTemplate))) {
+ if (this.childDocs.some(d => BoolCast(d.isTemplateDoc))) {
layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
}
this._timelineRef.current!.timelineContextMenu(e.nativeEvent);
@@ -673,7 +643,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (doc instanceof Doc) {
const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y);
doc.x = xx, doc.y = yy;
- this.props.addDocument && this.props.addDocument(doc, false);
+ this.props.addDocument && this.props.addDocument(doc);
}
}
}
@@ -702,58 +672,39 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
- private childViews = () => [
- <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
- ...this.views
- ]
+ private childViews = () => {
+ let children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
+ return [
+ ...children,
+ ...this.views,
+ ];
+ }
render() {
// update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
- this.props.Document.fitX = this.contentBounds && this.contentBounds.x;
- this.props.Document.fitY = this.contentBounds && this.contentBounds.y;
- this.props.Document.fitW = this.contentBounds && (this.contentBounds.r - this.contentBounds.x);
- this.props.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
- // if fieldExt is set, then children will be stored in the extension document for the fieldKey.
+ this.Document.fitX = this.contentBounds && this.contentBounds.x;
+ this.Document.fitY = this.contentBounds && this.contentBounds.y;
+ this.Document.fitW = this.contentBounds && (this.contentBounds.r - this.contentBounds.x);
+ this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
+ // if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey.
// otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document
- Doc.UpdateDocumentExtensionForField(this.props.DataDoc || this.props.Document, this.props.fieldKey);
- return (
+ return !this.extensionDoc ? (null) :
<div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}
- style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (NumCast(this.props.Document.scrollHeight) ? NumCast(this.props.Document.scrollHeight) : "100%") : this.props.PanelHeight() }}
+ style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu}>
- <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected}
- addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox} setPreviewCursor={this.props.setPreviewCursor}
- getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
+ <MarqueeView {...this.props} extensionDoc={this.extensionDoc} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument}
+ addLiveTextDocument={this.addLiveTextBox} getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
<CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
easing={this.easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
- <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
- <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={"ink"} >
+ {!this.extensionDoc ? (null) :
+ <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.extensionDoc} inkFieldKey={"ink"} >
{this.childViews}
- </InkingCanvas>
- </CollectionFreeFormLinksView>
+ </InkingCanvas>}
<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
</CollectionFreeFormViewPannableContents>
</MarqueeView>
<Timeline ref={this._timelineRef} {...this.props} />
{this.overlayViews}
- <CollectionFreeFormOverlayView {...this.props} {...this.getDocumentViewProps(this.props.Document)} />
- </div>
- );
- }
-}
-
-@observer
-class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
- render() {
- return <DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
- renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />;
- }
-}
-
-@observer
-class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
- render() {
- return !this.props.Document.backgroundLayout ? (null) :
- (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
- renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />);
+ </div>;
}
}
@@ -774,8 +725,8 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
const ceny = this.props.centeringShiftY();
const panx = -this.props.panX();
const pany = -this.props.panY();
- const zoom = this.props.zoomScaling();// needs to be a variable outside of the <Measure> otherwise, reactions won't fire
- return <div className={freeformclass} style={{ borderRadius: "inherit", transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+ const zoom = this.props.zoomScaling();
+ return <div className={freeformclass} style={{ borderRadius: "inherit", transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)` }}>
{this.props.children}
</div>;
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 9fc2e44fb..04f6ec2ad 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -6,6 +6,13 @@
width:100%;
height:100%;
}
+.marqueeView {
+ overflow: hidden;
+}
+
+.marqueeView:focus-within {
+ overflow: hidden;
+}
.marquee {
border-style: dashed;
box-sizing: border-box;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 82193aefa..44b6fe030 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -15,27 +15,28 @@ import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
import { InkingCanvas } from "../../InkingCanvas";
import { PreviewCursor } from "../../PreviewCursor";
-import { CollectionViewType } from "../CollectionBaseView";
+import { CollectionViewType } from "../CollectionView";
import { CollectionFreeFormView } from "./CollectionFreeFormView";
import "./MarqueeView.scss";
import React = require("react");
+import { SubCollectionViewProps } from "../CollectionSubView";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
getTransform: () => Transform;
- container: CollectionFreeFormView;
- addDocument: (doc: Doc, allowDuplicates: false) => boolean;
+ addDocument: (doc: Doc) => boolean;
activeDocuments: () => Doc[];
selectDocuments: (docs: Doc[]) => void;
removeDocument: (doc: Doc) => boolean;
addLiveTextDocument: (doc: Doc) => void;
isSelected: () => boolean;
- isAnnotationOverlay: boolean;
+ extensionDoc: Doc;
+ isAnnotationOverlay?: boolean;
setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
}
@observer
-export class MarqueeView extends React.Component<MarqueeViewProps>
+export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
{
private _mainCont = React.createRef<HTMLDivElement>();
@observable _lastX: number = 0;
@@ -83,7 +84,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
ns.map(line => {
let indent = line.search(/\S|$/);
let newBox = Docs.Create.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line });
- this.props.addDocument(newBox, false);
+ this.props.addDocument(newBox);
y += 40 * this.props.getTransform().Scale;
});
})();
@@ -92,7 +93,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
navigator.clipboard.readText().then(text => {
let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
if (ns.length === 1 && text.startsWith("http")) {
- this.props.addDocument(Docs.Create.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }), false);// paste an image from its URL in the paste buffer
+ this.props.addDocument(Docs.Create.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer
} else {
this.pasteTable(ns, x, y);
}
@@ -146,7 +147,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 });
- this.props.addDocument(newCol, false);
+ this.props.addDocument(newCol);
}
}
@action
@@ -187,13 +188,13 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@action
onPointerUp = (e: PointerEvent): void => {
- if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
+ if (!this.props.active()) this.props.selectDocuments([this.props.Document]);
if (this._visible) {
let mselect = this.marqueeSelect();
if (!e.shiftKey) {
- SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
+ SelectionManager.DeselectAll(mselect.length ? undefined : this.props.Document);
}
- this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
+ this.props.selectDocuments(mselect.length ? mselect : [this.props.Document]);
}
this.cleanupInteractions(true);
@@ -202,7 +203,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
}
- setPreviewCursor = (x: number, y: number, drag: boolean) => {
+ setPreviewCursor = action((x: number, y: number, drag: boolean) => {
if (drag) {
this._downX = this._lastX = x;
this._downY = this._lastY = y;
@@ -217,7 +218,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this._downY = y;
PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument);
}
- }
+ });
@action
onClick = (e: React.MouseEvent): void => {
@@ -246,13 +247,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
get ink() { // ink will be stored on the extension doc for the field (fieldKey) where the container's data is stored.
- let cprops = this.props.container.props;
- return Cast(Doc.fieldExtensionDoc(cprops.Document, cprops.fieldKey).ink, InkField);
+ return this.props.extensionDoc && Cast(this.props.extensionDoc.ink, InkField);
}
set ink(value: InkField | undefined) {
- let cprops = this.props.container.props;
- Doc.fieldExtensionDoc(cprops.Document, cprops.fieldKey).ink = value;
+ this.props.extensionDoc && (this.props.extensionDoc.ink = value);
}
@undoBatch
@@ -285,18 +284,18 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this.props.removeDocument(d);
d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
- d.page = -1;
+ d.displayTimecode = undefined;
return d;
});
}
let defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
"rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
- let colorPalette = Cast(this.props.container.props.Document.colorPalette, listSpec("string"));
- if (!colorPalette) this.props.container.props.Document.colorPalette = new List<string>(defaultPalette);
- let palette = Array.from(Cast(this.props.container.props.Document.colorPalette, listSpec("string")) as string[]);
+ let colorPalette = Cast(this.props.Document.colorPalette, listSpec("string"));
+ if (!colorPalette) this.props.Document.colorPalette = new List<string>(defaultPalette);
+ let palette = Array.from(Cast(this.props.Document.colorPalette, listSpec("string")) as string[]);
let usedPaletted = new Map<string, number>();
- [...this.props.activeDocuments(), this.props.container.props.Document].map(child => {
- let bg = StrCast(child.layout instanceof Doc ? child.layout.backgroundColor : child.backgroundColor);
+ [...this.props.activeDocuments(), this.props.Document].map(child => {
+ let bg = StrCast(Doc.Layout(child).backgroundColor);
if (palette.indexOf(bg) !== -1) {
palette.splice(palette.indexOf(bg), 1);
if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
@@ -350,7 +349,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
}
else {
- this.props.addDocument(newCollection, false);
+ this.props.addDocument(newCollection);
this.props.selectDocuments([newCollection]);
}
this.cleanupInteractions(false);
@@ -394,20 +393,22 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
let selRect = this.Bounds;
let selection: Doc[] = [];
this.props.activeDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => {
+ let layoutDoc = Doc.Layout(doc);
var x = NumCast(doc.x);
var y = NumCast(doc.y);
- var w = NumCast(doc.width);
- var h = NumCast(doc.height);
+ var w = NumCast(layoutDoc.width);
+ var h = NumCast(layoutDoc.height);
if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
selection.push(doc);
}
});
if (!selection.length && selectBackgrounds) {
this.props.activeDocuments().filter(doc => doc.z === undefined).map(doc => {
+ let layoutDoc = Doc.Layout(doc);
var x = NumCast(doc.x);
var y = NumCast(doc.y);
- var w = NumCast(doc.width);
- var h = NumCast(doc.height);
+ var w = NumCast(layoutDoc.width);
+ var h = NumCast(layoutDoc.height);
if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
selection.push(doc);
}
@@ -420,10 +421,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
let size = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
let otherBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
this.props.activeDocuments().filter(doc => doc.z !== undefined).map(doc => {
+ let layoutDoc = Doc.Layout(doc);
var x = NumCast(doc.x);
var y = NumCast(doc.y);
- var w = NumCast(doc.width);
- var h = NumCast(doc.height);
+ var w = NumCast(layoutDoc.width);
+ var h = NumCast(layoutDoc.height);
if (this.intersectRect({ left: x, top: y, width: w, height: h }, otherBounds)) {
selection.push(doc);
}
@@ -434,21 +436,17 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@computed
get marqueeDiv() {
+ let p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- return <div className="marquee" style={{ width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}`, zIndex: 2000 }} >
+ return <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}`, zIndex: 2000 }} >
<span className="marquee-legend" />
</div>;
}
render() {
- let p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
- return <div className="marqueeView" style={{ borderRadius: "inherit" }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
- <div style={{ position: "relative", transform: `translate(${p[0]}px, ${p[1]}px)` }} >
- {this._visible ? this.marqueeDiv : null}
- <div ref={this._mainCont} style={{ transform: `translate(${-p[0]}px, ${-p[1]}px)` }} >
- {this.props.children}
- </div>
- </div>
+ return <div className="marqueeView" onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} style={{ borderRadius: "inherit" }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
+ {this._visible ? this.marqueeDiv : null}
+ {this.props.children}
</div>;
}
} \ No newline at end of file