aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx17
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx108
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss89
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx97
-rw-r--r--src/client/views/collections/CollectionSubView.tsx8
-rw-r--r--src/client/views/collections/CollectionTreeView.scss17
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx28
-rw-r--r--src/client/views/collections/CollectionVideoView.scss2
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx52
-rw-r--r--src/client/views/collections/CollectionView.tsx37
-rw-r--r--src/client/views/collections/ParentDocumentSelector.scss8
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx66
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx92
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx119
15 files changed, 489 insertions, 253 deletions
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 645296d27..84ffbac36 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from 'mobx';
+import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ContextMenu } from '../ContextMenu';
@@ -36,9 +36,20 @@ export interface CollectionViewProps extends FieldViewProps {
@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 {
@@ -82,12 +93,12 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
}
return false;
}
- @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; }
+ @computed get isAnnotationOverlay() { return this.props.fieldKey === "annotations"; }
@action.bound
addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
let props = this.props;
- var curPage = Cast(props.Document.curPage, "number", -1);
+ var curPage = NumCast(props.Document.curPage, -1);
Doc.SetOnPrototype(doc, "page", curPage);
if (curPage >= 0) {
Doc.SetOnPrototype(doc, "annotationOn", props.Document);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index efcee9c02..06b262d79 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,11 +1,11 @@
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, observable, reaction } from "mobx";
+import { action, observable, reaction, Lambda } 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 { Doc, Field, Opt } from "../../../new_fields/Doc";
+import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc";
import { FieldId, Id } from "../../../new_fields/RefField";
import { listSpec } from "../../../new_fields/Schema";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
@@ -18,6 +18,9 @@ import { DocumentView } from "../nodes/DocumentView";
import "./CollectionDockingView.scss";
import { SubCollectionViewProps } from "./CollectionSubView";
import React = require("react");
+import { ParentDocSelector } from './ParentDocumentSelector';
+import { DocumentManager } from '../../util/DocumentManager';
+import { CollectionViewType } from './CollectionBaseView';
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
@@ -72,32 +75,40 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@undoBatch
@action
- public CloseRightSplit(document: Doc) {
+ public CloseRightSplit(document: Doc): boolean {
+ let retVal = false;
if (this._goldenLayout.root.contentItems[0].isRow) {
- this._goldenLayout.root.contentItems[0].contentItems.map((child: any, i: number) => {
+ retVal = Array.from(this._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
- child.contentItems[0].config.props.documentId == document[Id]) {
+ Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)!.Document, document)) {
child.contentItems[0].remove();
this.layoutChanged(document);
- this.stateChanged();
- } else
- child.contentItems.map((tab: any, j: number) => {
- if (tab.config.component === "DocumentFrameRenderer" && tab.config.props.documentId === document[Id]) {
+ return true;
+ } else {
+ Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
+ if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) {
child.contentItems[j].remove();
child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0);
let docs = Cast(this.props.Document.data, listSpec(Doc));
docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1);
- this.stateChanged();
+ return true;
}
+ return false;
});
- })
+ }
+ return false;
+ });
+ }
+ if (retVal) {
+ this.stateChanged();
}
+ return retVal;
}
@action
layoutChanged(removed?: Doc) {
this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
- this._goldenLayout.emit('sbcreteChanged');
+ this._goldenLayout.emit('stateChanged');
this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
if (removed) CollectionDockingView.Instance._removedDocs.push(removed);
this.stateChanged();
@@ -143,6 +154,17 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return newContentItem;
}
+ @action
+ public AddTab(stack: any, document: Doc) {
+ let docs = Cast(this.props.Document.data, listSpec(Doc));
+ if (docs) {
+ docs.push(document);
+ }
+ let docContentConfig = CollectionDockingView.makeDocumentConfig(document);
+ var newContentItem = stack.layoutManager.createContentItem(docContentConfig, this._goldenLayout);
+ stack.addChild(newContentItem.contentItems[0], undefined);
+ this.layoutChanged();
+ }
setupGoldenLayout() {
var config = StrCast(this.props.Document.dockingConfig);
@@ -157,6 +179,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
try {
this._goldenLayout.unbind('itemDropped', this.itemDropped);
this._goldenLayout.unbind('tabCreated', this.tabCreated);
+ this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
this._goldenLayout.unbind('stackCreated', this.stackCreated);
} catch (e) { }
this._goldenLayout.destroy();
@@ -164,6 +187,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
this._goldenLayout.on('itemDropped', this.itemDropped);
this._goldenLayout.on('tabCreated', this.tabCreated);
+ this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
this._goldenLayout.on('stackCreated', this.stackCreated);
this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer);
this._goldenLayout.container = this._containerRef.current;
@@ -177,12 +201,15 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._goldenLayout.init();
}
}
+ reactionDisposer?: Lambda;
componentDidMount: () => void = () => {
if (this._containerRef.current) {
- reaction(
+ this.reactionDisposer = reaction(
() => StrCast(this.props.Document.dockingConfig),
() => {
if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) {
+ // 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);
}
this._ignoreStateChange = "";
@@ -196,12 +223,17 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._goldenLayout.unbind('itemDropped', this.itemDropped);
this._goldenLayout.unbind('tabCreated', this.tabCreated);
this._goldenLayout.unbind('stackCreated', this.stackCreated);
+ this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
} catch (e) {
}
if (this._goldenLayout) this._goldenLayout.destroy();
this._goldenLayout = null;
window.removeEventListener('resize', this.onResize);
+
+ if (this.reactionDisposer) {
+ this.reactionDisposer();
+ }
}
@action
onResize = (event: any) => {
@@ -238,6 +270,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
let y = e.clientY;
let docid = (e.target as any).DashDocId;
let tab = (e.target as any).parentElement as HTMLElement;
+ let glTab = (e.target as any).Tab;
+ if (glTab && glTab.contentItem && glTab.contentItem.parent) {
+ glTab.contentItem.parent.setActiveContentItem(glTab.contentItem);
+ }
DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
if (f instanceof Doc) {
DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y,
@@ -292,21 +328,23 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
DocServer.GetRefField(tab.contentItem.config.props.documentId).then(async doc => {
if (doc instanceof Doc) {
- let counter: any = this.htmlToElement(`<div class="messageCounter">0</div>`);
+ let counter: any = this.htmlToElement(`<span class="messageCounter">0</div>`);
tab.element.append(counter);
+ let upDiv = document.createElement("span");
+ ReactDOM.render(<ParentDocSelector Document={doc} />, upDiv);
+ tab.reactComponents = [upDiv];
+ tab.element.append(upDiv);
counter.DashDocId = tab.contentItem.config.props.documentId;
tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title],
() => {
- const lf = Cast(doc.linkedFromDocs, listSpec(Doc), []);
- const lt = Cast(doc.linkedToDocs, listSpec(Doc), []);
- let count = (lf ? lf.length : 0) + (lt ? lt.length : 0);
- counter.innerHTML = count;
+ counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length;
tab.titleElement[0].textContent = doc.title;
}, { fireImmediately: true });
tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
}
});
}
+ tab.titleElement[0].Tab = tab;
tab.closeElement.off('click') //unbind the current click handler
.click(async function () {
if (tab.reactionDisposer) {
@@ -320,6 +358,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
tab.contentItem.remove();
});
}
+
+ tabDestroyed = (tab: any) => {
+ if (tab.reactComponents) {
+ for (const ele of tab.reactComponents) {
+ ReactDOM.unmountComponentAtNode(ele);
+ }
+ }
+ }
_removedDocs: Doc[] = [];
stackCreated = (stack: any) => {
@@ -366,9 +412,10 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@observable private _document: Opt<Doc>;
-
+ _stack: any;
constructor(props: any) {
super(props);
+ this._stack = (this.props as any).glContainer.parent.parent;
DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc));
}
@@ -385,19 +432,37 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
if (this._mainCont.current && this._mainCont.current.children) {
let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current.children[0].firstChild as HTMLElement);
scale = Utils.GetScreenTransform(this._mainCont.current).scale;
- return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling());
+ return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / scale);
}
return Transform.Identity();
}
+ get scaleToFitMultiplier() {
+ let docWidth = NumCast(this._document!.width);
+ let docHeight = NumCast(this._document!.height);
+ if (NumCast(this._document!.nativeWidth) || !docWidth || !this._panelWidth || !this._panelHeight) return 1;
+ if (StrCast(this._document!.layout).indexOf("Collection") === -1 ||
+ NumCast(this._document!.viewType) !== CollectionViewType.Freeform) return 1;
+ let scaling = Math.max(1, this._panelWidth / docWidth * docHeight > this._panelHeight ?
+ this._panelHeight / docHeight : this._panelWidth / docWidth);
+ console.log("Scaling = " + scaling);
+ return scaling;
+ }
get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; }
+ addDocTab = (doc: Doc, location: string) => {
+ if (location === "onRight") {
+ CollectionDockingView.Instance.AddRightSplit(doc);
+ } else {
+ CollectionDockingView.Instance.AddTab(this._stack, doc);
+ }
+ }
get content() {
if (!this._document) {
return (null);
}
return (
<div className="collectionDockingView-content" ref={this._mainCont}
- style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
+ style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier}, ${this.scaleToFitMultiplier})` }}>
<DocumentView key={this._document[Id]} Document={this._document}
toggleMinimized={emptyFunction}
bringToFront={emptyFunction}
@@ -412,6 +477,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
focus={emptyFunction}
+ addDocTab={this.addDocTab}
ContainingCollectionView={undefined} />
</div >);
}
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index b3762206a..a6614da21 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -61,7 +61,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
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 });
+ ContextMenu.Instance.addItem({ description: "PDFOptions", event: emptyFunction, icon: "file-pdf" });
}
}
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index cfdb3ab22..df5c52d01 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -4,7 +4,7 @@
.collectionSchemaView-container {
border-width: $COLLECTION_BORDER_WIDTH;
- border-color : $intermediate-color;
+ border-color: $intermediate-color;
border-style: solid;
border-radius: $border-radius;
box-sizing: border-box;
@@ -14,42 +14,52 @@
.collectionSchemaView-cellContents {
height: $MAX_ROW_HEIGHT;
+
+ img {
+ width: auto;
+ max-height: $MAX_ROW_HEIGHT;
+ }
}
-
+
.collectionSchemaView-previewRegion {
position: relative;
background: $light-color;
float: left;
height: 100%;
+
.collectionSchemaView-previewDoc {
height: 100%;
- width: 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;
}
}
+
.collectionSchemaView-previewHandle {
position: absolute;
height: 15px;
- width: 15px;
- z-index: 20;
- right: 0;
- top: 20px;
- background: Black ;
+ width: 15px;
+ z-index: 20;
+ right: 0;
+ top: 20px;
+ background: Black;
}
- .collectionSchemaView-dividerDragger{
- position: relative;
- background: black;
- float: left;
+
+ .collectionSchemaView-dividerDragger {
+ position: relative;
+ background: black;
+ float: left;
height: 37px;
width: 20px;
z-index: 20;
@@ -57,6 +67,7 @@
top: 0;
background: $main-accent;
}
+
.collectionSchemaView-columnsHandle {
position: absolute;
height: 37px;
@@ -66,6 +77,7 @@
bottom: 0;
background: $main-accent;
}
+
.collectionSchemaView-colDividerDragger {
position: relative;
box-sizing: border-box;
@@ -74,6 +86,7 @@
float: top;
width: 100%;
}
+
.collectionSchemaView-dividerDragger {
position: relative;
box-sizing: border-box;
@@ -82,11 +95,13 @@
float: left;
height: 100%;
}
+
.collectionSchemaView-tableContainer {
position: relative;
float: left;
height: 100%;
}
+
.ReactTable {
// position: absolute; // display: inline-block;
// overflow: auto;
@@ -95,6 +110,7 @@
background: $light-color;
box-sizing: border-box;
border: none !important;
+
.rt-table {
overflow-y: auto;
overflow-x: auto;
@@ -103,42 +119,50 @@
direction: ltr; // direction:rtl;
// display:block;
}
+
.rt-tbody {
//direction: ltr;
direction: rtl;
}
+
.rt-tr-group {
direction: ltr;
max-height: $MAX_ROW_HEIGHT;
}
+
.rt-td {
border-width: 1px;
border-right-color: $intermediate-color;
+
.imageBox-cont {
position: relative;
max-height: 100%;
}
+
.imageBox-cont img {
object-fit: contain;
max-width: 100%;
height: 100%;
}
- .videobox-cont {
+
+ .videoBox-cont {
object-fit: contain;
width: auto;
height: 100%;
}
}
}
+
.ReactTable .rt-thead.-header {
background: $intermediate-color;
color: $light-color;
- text-transform: uppercase;
+ // text-transform: uppercase;
letter-spacing: 2px;
font-size: 12px;
height: 30px;
padding-top: 4px;
}
+
.ReactTable .rt-th,
.ReactTable .rt-td {
max-height: $MAX_ROW_HEIGHT;
@@ -146,32 +170,36 @@
font-size: 13px;
text-align: center;
}
+
.ReactTable .rt-tbody .rt-tr-group:last-child {
border-bottom: $intermediate-color;
border-bottom-style: solid;
border-bottom-width: 1;
}
+
.documentView-node-topmost {
- text-align:left;
+ text-align: left;
transform-origin: center top;
display: inline-block;
}
+
.documentView-node:first-child {
background: $light-color;
}
}
+
//options menu styling
#schemaOptionsMenuBtn {
position: absolute;
height: 20px;
width: 20px;
border-radius: 50%;
- z-index: 21;
+ z-index: 21;
right: 4px;
- top: 4px;
+ top: 4px;
pointer-events: auto;
- background-color:black;
- display:inline-block;
+ background-color: black;
+ display: inline-block;
padding: 0px;
font-size: 100%;
}
@@ -185,10 +213,12 @@ ul {
padding: 0px;
margin: 0px;
}
+
.schema-options-subHeader {
color: $intermediate-color;
margin-bottom: 5px;
}
+
#schemaOptionsMenuBtn:hover {
transform: scale(1.15);
}
@@ -198,15 +228,15 @@ ul {
font-size: 12px;
}
- #options-flyout-div {
+#options-flyout-div {
text-align: left;
- padding:0px;
+ padding: 0px;
z-index: 100;
font-family: $sans-serif;
padding-left: 5px;
- }
+}
- #schema-col-checklist {
+#schema-col-checklist {
overflow: scroll;
text-align: left;
//background-color: $light-color-secondary;
@@ -214,8 +244,8 @@ ul {
max-height: 175px;
font-family: $sans-serif;
font-size: 12px;
- }
-
+}
+
.Resizer {
box-sizing: border-box;
@@ -223,6 +253,7 @@ ul {
opacity: 0.5;
z-index: 1;
background-clip: padding-box;
+
&.horizontal {
height: 11px;
margin: -5px 0;
@@ -230,22 +261,26 @@ ul {
border-bottom: 5px solid rgba(255, 255, 255, 0);
cursor: row-resize;
width: 100%;
+
&:hover {
border-top: 5px solid rgba(0, 0, 0, 0.5);
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
}
}
+
&.vertical {
width: 11px;
margin: 0 -5px;
border-left: 5px solid rgba(255, 255, 255, 0);
border-right: 5px solid rgba(255, 255, 255, 0);
cursor: col-resize;
+
&:hover {
border-left: 5px solid rgba(0, 0, 0, 0.5);
border-right: 5px solid rgba(0, 0, 0, 0.5);
}
}
+
&:hover {
-webkit-transition: all 2s ease;
transition: all 2s ease;
@@ -266,10 +301,12 @@ ul {
-ms-flex-direction: column;
flex-direction: column;
}
+
header {
padding: 1rem;
background: #eee;
}
+
footer {
padding: 1rem;
background: #eee;
@@ -283,10 +320,12 @@ ul {
display: flex;
flex-direction: column;
}
+
header {
padding: 1rem;
background: #eee;
}
+
footer {
padding: 1rem;
background: #eee;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 18319dc77..f15da41ff 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -20,10 +20,13 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
import { listSpec } from "../../../new_fields/Schema";
import { List } from "../../../new_fields/List";
import { Id } from "../../../new_fields/RefField";
+import { Gateway } from "../../northstar/manager/Gateway";
+import { Docs } from "../../documents/Documents";
+import { ContextMenu } from "../ContextMenu";
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -61,10 +64,25 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@computed get columns() { return Cast(this.props.Document.schemaColumns, listSpec("string"), []); }
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
+ @computed get tableColumns() {
+ return this.columns.map(col => {
+ const ref = React.createRef<HTMLParagraphElement>();
+ return {
+ Header: <p ref={ref} onPointerDown={SetupDrag(ref, () => this.onHeaderDrag(col))}>{col}</p>,
+ accessor: (doc: Doc) => doc ? doc[col] : 0,
+ id: col
+ };
+ });
+ }
+
+ onHeaderDrag = (columnName: string) => {
+ return this.props.Document;
+ }
+
renderCell = (rowProps: CellInfo) => {
let props: FieldViewProps = {
- Document: rowProps.value[0],
- fieldKey: rowProps.value[1],
+ Document: rowProps.original,
+ fieldKey: rowProps.column.id as string,
ContainingCollectionView: this.props.CollectionView,
isSelected: returnFalse,
select: emptyFunction,
@@ -76,24 +94,24 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
whenActiveChanged: emptyFunction,
PanelHeight: returnZero,
PanelWidth: returnZero,
+ addDocTab: this.props.addDocTab,
};
- let contents = (
- <FieldView {...props} />
- );
+ let fieldContentView = <FieldView {...props} />;
let reference = React.createRef<HTMLDivElement>();
- let onItemDown = SetupDrag(reference, () => props.Document, this.props.moveDocument);
+ let onItemDown = (e: React.PointerEvent) =>
+ (this.props.CollectionView.props.isSelected() ?
+ SetupDrag(reference, () => props.Document, this.props.moveDocument)(e) : undefined);
let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => {
const res = run({ this: doc });
if (!res.success) return false;
- const field = res.result;
- doc[props.fieldKey] = field;
+ doc[props.fieldKey] = res.result;
return true;
};
return (
<div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document[Id]} ref={reference}>
<EditableView
display={"inline"}
- contents={contents}
+ contents={fieldContentView}
height={Number(MAX_ROW_HEIGHT)}
GetValue={() => {
let field = props.Document[props.fieldKey];
@@ -118,7 +136,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
const run = script.run;
//TODO This should be able to be refactored to compile the script once
- const val = await DocListCastAsync(this.props.Document[this.props.fieldKey])
+ const val = await DocListCastAsync(this.props.Document[this.props.fieldKey]);
val && val.forEach(doc => applyToDoc(doc, run));
}}>
</EditableView>
@@ -207,6 +225,32 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
}
+ 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: "Make DB", event: this.makeDB });
+ }
+ }
+
+ @action
+ makeDB = async () => {
+ let csv: string = this.columns.reduce((val, col) => val + col + ",", "");
+ csv = csv.substr(0, csv.length - 1) + "\n";
+ let self = this;
+ DocListCast(this.props.Document.data).map(doc => {
+ csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "") + ",", "");
+ csv = csv.substr(0, csv.length - 1) + "\n";
+ });
+ csv.substring(0, csv.length - 1);
+ let dbName = StrCast(this.props.Document.title);
+ let res = await Gateway.Instance.PostSchema(csv, dbName);
+ if (self.props.CollectionView.props.addDocument) {
+ let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName });
+ if (schemaDoc) {
+ self.props.CollectionView.props.addDocument(schemaDoc, false);
+ }
+ }
+ }
+
@action
addColumn = () => {
this.columns.push(this._newKeyName);
@@ -224,10 +268,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
this.previewScript = e.currentTarget.value;
}
+ @computed
get previewDocument(): Doc | undefined {
- const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ const children = DocListCast(this.props.Document[this.props.fieldKey]);
const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : undefined;
- return selected ? (this.previewScript ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
+ return selected ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
}
get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); }
get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; }
@@ -253,8 +298,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
get previewPanel() {
// let doc = CompileScript(this.previewScript, { this: selected }, true)();
const previewDoc = this.previewDocument;
- return !previewDoc || !this.previewRegionWidth ? (null) : (
- <div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
+ return (<div className="collectionSchemaView-previewRegion" style={{ width: `${Math.max(0, this.previewRegionWidth - 1)}px` }}>
+ {!previewDoc || !this.previewRegionWidth ? (null) : (
<div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
<DocumentView Document={previewDoc} isTopMost={false} selectOnLoad={false}
toggleMinimized={emptyFunction}
@@ -267,12 +312,12 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
parentActive={this.props.active}
whenActiveChanged={this.props.whenActiveChanged}
bringToFront={emptyFunction}
+ addDocTab={this.props.addDocTab}
/>
- </div>
- <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
- style={{ left: `calc(50% - ${Math.min(75, this.previewPanelWidth() / 2)}px)` }} />
- </div>
- );
+ </div>)}
+ <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
+ style={{ left: `calc(50% - ${Math.min(75, (previewDoc ? this.previewPanelWidth() / 2 : 75))}px)` }} />
+ </div>);
}
get documentKeysCheckList() {
@@ -319,20 +364,18 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
<div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
}
+
+
render() {
library.add(faCog);
library.add(faPlus);
- const children = this.children;
+ const children = this.childDocs;
return (
<div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
- onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
+ onDrop={(e: React.DragEvent) => this.onDrop(e, {})} onContextMenu={this.onContextMenu} ref={this.createTarget}>
<div className="collectionSchemaView-tableContainer" style={{ width: `${this.tableWidth}px` }}>
<ReactTable data={children} page={0} pageSize={children.length} showPagination={false}
- columns={this.columns.map(col => ({
- Header: col,
- accessor: (doc: Doc) => [doc, col],
- id: col
- }))}
+ columns={this.tableColumns}
column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
getTrProps={this.getTrProps}
/>
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a86d250bd..864fdfa4b 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -18,6 +18,7 @@ import { List } from "../../../new_fields/List";
import { DocServer } from "../../DocServer";
import { ObjectField } from "../../../new_fields/ObjectField";
import CursorField, { CursorPosition, CursorMetadata } from "../../../new_fields/CursorField";
+import { url } from "inspector";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -46,7 +47,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
this.createDropTarget(ele);
}
- get children() {
+ get childDocs() {
//TODO tfs: This might not be what we want?
//This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
return DocListCast(this.props.Document[this.props.fieldKey]);
@@ -168,6 +169,11 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
this.props.addDocument(htmlDoc, false);
return;
}
+ if (text && text.indexOf("www.youtube.com/watch") !== -1) {
+ const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/");
+ this.props.addDocument(Docs.WebDocument(url, { ...options, width: 300, height: 300 }));
+ return;
+ }
let batch = UndoManager.StartBatch("collection view drop");
let promises: Promise<void>[] = [];
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 411d67ff7..5f82137c6 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -29,23 +29,24 @@
}
.bullet {
- float:left;
+ float: left;
position: relative;
width: 15px;
display: block;
color: $intermediate-color;
margin-top: 3px;
- transform: scale(1.3,1.3);
+ transform: scale(1.3, 1.3);
}
.docContainer {
margin-left: 10px;
display: block;
- // width:100%;//width: max-content;
+ // width:100%;//width: max-content;
}
+
.docContainer:hover {
.treeViewItem-openRight {
- display:inline;
+ display: inline;
}
}
@@ -61,10 +62,12 @@
// margin-top: 3px;
display: inline;
}
+
.treeViewItem-openRight {
margin-left: 5px;
- display:none;
+ display: none;
}
+
.docContainer:hover {
.delete-button {
display: inline;
@@ -73,14 +76,16 @@
}
.coll-title {
- width:max-content;
+ width: max-content;
display: block;
font-size: 24px;
}
+
.collection-child {
margin-top: 10px;
margin-bottom: 10px;
}
+
.collectionTreeView-keyHeader {
font-style: italic;
font-size: 8pt;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d22418b2c..72fa69cb1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -14,12 +14,11 @@ import { Doc, DocListCast } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/RefField';
import { ContextMenu } from '../ContextMenu';
import { undoBatch } from '../../util/UndoManager';
-import { Main } from '../Main';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
import { CollectionDockingView } from './CollectionDockingView';
import { DocumentManager } from '../../util/DocumentManager';
-import { List } from '../../../new_fields/List';
import { Docs } from '../../documents/Documents';
+import { MainView } from '../MainView';
export interface TreeViewProps {
@@ -52,11 +51,11 @@ class TreeView extends React.Component<TreeViewProps> {
@undoBatch openRight = async () => {
if (this.props.document.dockingConfig) {
- Main.Instance.openWorkspace(this.props.document);
+ MainView.Instance.openWorkspace(this.props.document);
} else {
CollectionDockingView.Instance.AddRightSplit(this.props.document);
}
- };
+ }
get children() {
return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed? .filter(doc => FieldValue(doc));
@@ -112,7 +111,7 @@ class TreeView extends React.Component<TreeViewProps> {
let editableView = (titleString: string) =>
(<EditableView
oneLine={!this._isOver ? true : false}
- display={"block"}
+ display={"inline-block"}
contents={titleString}
height={36}
GetValue={() => StrCast(this.props.document.title)}
@@ -130,7 +129,7 @@ class TreeView extends React.Component<TreeViewProps> {
</div>);
return (
<div className="docContainer" ref={reference} onPointerDown={onItemDown} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}
- style={{ background: BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0" }}
+ style={{ background: BoolCast(this.props.document.protoBrush, false) ? "#06123232" : BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0" }}
onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
{editableView(StrCast(this.props.document.title))}
{openRight}
@@ -140,7 +139,7 @@ class TreeView extends React.Component<TreeViewProps> {
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
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
- ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) });
+ ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) });
ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.document) });
ContextMenu.Instance.addItem({
description: "Open Fields", event: () => CollectionDockingView.Instance.AddRightSplit(Docs.KVPDocument(this.props.document,
@@ -184,8 +183,9 @@ class TreeView extends React.Component<TreeViewProps> {
{TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
</div>
</ul >);
- } else
+ } else {
bulletType = BulletType.Collapsed;
+ }
}
});
return <div className="treeViewItem-container"
@@ -198,8 +198,8 @@ class TreeView extends React.Component<TreeViewProps> {
</div>;
}
public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
- return docs.filter(child => child instanceof Doc && !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child =>
- <TreeView document={child as Doc} key={(child as Doc)[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
+ return docs.filter(child => !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).map(child =>
+ <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
}
}
@@ -214,7 +214,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
onContextMenu = (e: React.MouseEvent): void => {
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
- ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => Main.Instance.createNewWorkspace()) });
+ ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => MainView.Instance.createNewWorkspace()) });
}
if (!ContextMenu.Instance.getItems().some(item => item.description === "Delete")) {
ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.remove(this.props.Document)) });
@@ -222,16 +222,16 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
render() {
let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType;
- if (!this.children) {
+ if (!this.childDocs) {
return (null);
}
- let childElements = TreeView.GetChildElements(this.children, false, this.remove, this.props.moveDocument, dropAction);
+ let childElements = TreeView.GetChildElements(this.childDocs, false, this.remove, this.props.moveDocument, dropAction);
return (
<div id="body" className="collectionTreeView-dropTarget"
style={{ borderRadius: "inherit" }}
onContextMenu={this.onContextMenu}
- onWheel={(e: React.WheelEvent) => e.stopPropagation()}
+ onWheel={(e: React.WheelEvent) => this.props.isSelected() && e.stopPropagation()}
onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
<div className="coll-title">
<EditableView
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
index ed56ad268..db8b84832 100644
--- a/src/client/views/collections/CollectionVideoView.scss
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -2,7 +2,7 @@
.collectionVideoView-cont{
width: 100%;
height: 100%;
- position: absolute;
+ position: inherit;
top: 0;
left:0;
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index cb3fd1ba4..9ab959f3c 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -9,27 +9,26 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
import { emptyFunction } from "../../../Utils";
import { Id } from "../../../new_fields/RefField";
import { VideoBox } from "../nodes/VideoBox";
+import { NumCast } from "../../../new_fields/Types";
@observer
export class CollectionVideoView extends React.Component<FieldViewProps> {
- private _videoBox: VideoBox | undefined = undefined;
- @observable _playTimer?: NodeJS.Timeout = undefined;
-
- @observable _currentTimecode: number = 0;
+ private _videoBox?: VideoBox;
public static LayoutString(fieldKey: string = "data") {
return FieldView.LayoutString(CollectionVideoView, fieldKey);
}
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}, ${scaling})` }}>
- <span>{"" + Math.round(this._currentTimecode)}</span>
- <span style={{ fontSize: 8 }}>{" " + Math.round((this._currentTimecode - Math.trunc(this._currentTimecode)) * 100)}</span>
+ <span>{"" + Math.round(curTime)}</span>
+ <span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
</div>,
<div className="collectionVideoView-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
- {this._playTimer ? "\"" : ">"}
+ {this._videoBox && this._videoBox.Playing ? "\"" : ">"}
</div>,
<div className="collectionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
F
@@ -38,36 +37,20 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
}
@action
- updateTimecode = () => {
- if (this._videoBox && this._videoBox.player) {
- this._currentTimecode = this._videoBox.player.currentTime;
- this.props.Document.curPage = Math.round(this._currentTimecode);
- }
- }
-
- componentDidMount() { this.updateTimecode(); }
-
- componentWillUnmount() { if (this._playTimer) clearInterval(this._playTimer); }
-
- @action
onPlayDown = () => {
if (this._videoBox && this._videoBox.player) {
- if (this._videoBox.player.paused) {
- this._videoBox.player.play();
- if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 1000);
+ if (this._videoBox.Playing) {
+ this._videoBox.Pause();
} else {
- this._videoBox.player.pause();
- if (this._playTimer) clearInterval(this._playTimer);
- this._playTimer = undefined;
-
+ this._videoBox.Play();
}
}
}
@action
onFullDown = (e: React.PointerEvent) => {
- if (this._videoBox && this._videoBox.player) {
- this._videoBox.player.requestFullscreen();
+ if (this._videoBox) {
+ this._videoBox.FullScreen();
e.stopPropagation();
e.preventDefault();
}
@@ -75,22 +58,18 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
@action
onResetDown = () => {
- if (this._videoBox && this._videoBox.player) {
- this._videoBox.player.pause();
- this._videoBox.player.currentTime = 0;
- if (this._playTimer) clearInterval(this._playTimer);
- this._playTimer = undefined;
- this.updateTimecode();
+ if (this._videoBox) {
+ this._videoBox.Pause();
+ this.props.Document.curPage = 0;
}
}
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: "VideoOptions", event: emptyFunction });
}
}
- setVideoBox = (player: VideoBox) => { this._videoBox = player; };
+ setVideoBox = (videoBox: VideoBox) => { this._videoBox = videoBox; };
private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
let props = { ...this.props, ...renderProps };
@@ -101,6 +80,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
}
render() {
+ trace();
return (
<CollectionBaseView {...this.props} className="collectionVideoView-cont" onContextMenu={this.onContextMenu}>
{this.subView}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8c1442d38..b9ffc11a2 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,17 +1,23 @@
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faProjectDiagram, faSquare, faTh, faTree } from '@fortawesome/free-solid-svg-icons';
+import { observer } from "mobx-react";
import * as React from 'react';
-import { FieldViewProps, FieldView } from '../nodes/FieldView';
-import { CollectionBaseView, CollectionViewType, CollectionRenderProps } from './CollectionBaseView';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { CollectionSchemaView } from './CollectionSchemaView';
-import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionTreeView } from './CollectionTreeView';
-import { ContextMenu } from '../ContextMenu';
+import { Id } from '../../../new_fields/RefField';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { observer } from 'mobx-react';
import { undoBatch } from '../../util/UndoManager';
-import { trace } from 'mobx';
-import { Id } from '../../../new_fields/RefField';
-import { Main } from '../Main';
+import { ContextMenu } from "../ContextMenu";
+import { FieldView, FieldViewProps } from '../nodes/FieldView';
+import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from './CollectionBaseView';
+import { CollectionDockingView } from "./CollectionDockingView";
+import { CollectionSchemaView } from "./CollectionSchemaView";
+import { CollectionTreeView } from "./CollectionTreeView";
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+export const COLLECTION_BORDER_WIDTH = 2;
+
+library.add(faTh);
+library.add(faTree);
+library.add(faSquare);
+library.add(faProjectDiagram);
@observer
export class CollectionView extends React.Component<FieldViewProps> {
@@ -34,9 +40,12 @@ export class CollectionView extends React.Component<FieldViewProps> {
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
- ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Freeform) });
- ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema) });
- ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree) });
+ ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Freeform), icon: "project-diagram" });
+ if (CollectionBaseView.InSafeMode()) {
+ ContextMenu.Instance.addItem({ description: "Test Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Invalid), icon: "project-diagram" });
+ }
+ ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema), icon: "project-diagram" });
+ ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" });
}
}
diff --git a/src/client/views/collections/ParentDocumentSelector.scss b/src/client/views/collections/ParentDocumentSelector.scss
new file mode 100644
index 000000000..f3c605f3e
--- /dev/null
+++ b/src/client/views/collections/ParentDocumentSelector.scss
@@ -0,0 +1,8 @@
+.PDS-flyout {
+ position: absolute;
+ z-index: 9999;
+ background-color: #d3d3d3;
+ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
+ min-width: 150px;
+ color: black;
+} \ No newline at end of file
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
new file mode 100644
index 000000000..52f7914f3
--- /dev/null
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -0,0 +1,66 @@
+import * as React from "react";
+import './ParentDocumentSelector.scss';
+import { Doc } from "../../../new_fields/Doc";
+import { observer } from "mobx-react";
+import { observable, action, runInAction } from "mobx";
+import { Id } from "../../../new_fields/RefField";
+import { SearchUtil } from "../../util/SearchUtil";
+import { CollectionDockingView } from "./CollectionDockingView";
+
+@observer
+export class SelectorContextMenu extends React.Component<{ Document: Doc }> {
+ @observable private _docs: Doc[] = [];
+
+ constructor(props: { Document: Doc }) {
+ super(props);
+
+ this.fetchDocuments();
+ }
+
+ async fetchDocuments() {
+ const docs = await SearchUtil.Search(`data_l:"${this.props.Document[Id]}"`, true);
+ runInAction(() => this._docs = docs);
+ }
+
+ render() {
+ return (
+ <>
+ {this._docs.map(doc => <p><a onClick={() => CollectionDockingView.Instance.AddRightSplit(doc)}>{doc.title}</a></p>)}
+ </>
+ );
+ }
+}
+
+@observer
+export class ParentDocSelector extends React.Component<{ Document: Doc }> {
+ @observable hover = false;
+
+ @action
+ onMouseLeave = () => {
+ this.hover = false;
+ }
+
+ @action
+ onMouseEnter = () => {
+ this.hover = true;
+ }
+
+ render() {
+ let flyout;
+ if (this.hover) {
+ flyout = (
+ <div className="PDS-flyout">
+ <SelectorContextMenu Document={this.props.Document} />
+ </div>
+ );
+ }
+ return (
+ <span style={{ position: "relative", display: "inline-block" }}
+ onMouseEnter={this.onMouseEnter}
+ onMouseLeave={this.onMouseLeave}>
+ <p>^</p>
+ {flyout}
+ </span>
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dc86f38b6..9cb8443f4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -20,10 +20,11 @@ import React = require("react");
import v5 = require("uuid/v5");
import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
import { Doc, WidthSym, HeightSym } from "../../../../new_fields/Doc";
-import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types";
+import { FieldValue, Cast, NumCast, BoolCast } from "../../../../new_fields/Types";
import { pageSchema } from "../../nodes/ImageBox";
import { Id } from "../../../../new_fields/RefField";
import { InkField, StrokeData } from "../../../../new_fields/InkField";
+import { HistoryUtil } from "../../../util/History";
export const panZoomSchema = createSchema({
panX: "number",
@@ -36,23 +37,22 @@ const PanZoomDocument = makeInterface(panZoomSchema, positionSchema, pageSchema)
@observer
export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
- public static RIGHT_BTN_DRAG = false;
private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
private _lastX: number = 0;
private _lastY: number = 0;
private get _pwidth() { return this.props.PanelWidth(); }
private get _pheight() { return this.props.PanelHeight(); }
- @computed get nativeWidth() { return NumCast(this.Document.nativeWidth, 0); }
- @computed get nativeHeight() { return NumCast(this.Document.nativeHeight, 0); }
+ @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
+ @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
+ public get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
- private get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; }
- private panX = () => FieldValue(this.Document.panX, 0);
- private panY = () => FieldValue(this.Document.panY, 0);
- private zoomScaling = () => FieldValue(this.Document.scale, 1);
+ private panX = () => this.Document.panX || 0;
+ private panY = () => this.Document.panY || 0;
+ private zoomScaling = () => this.Document.scale || 1;
private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections
private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections
- private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
+ private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
private addLiveTextBox = (newBox: Doc) => {
@@ -65,13 +65,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return true;
}
private selectDocuments = (docs: Doc[]) => {
- SelectionManager.DeselectAll;
+ SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).filter(dv => dv).map(dv =>
SelectionManager.SelectDoc(dv!, true));
}
public getActiveDocuments = () => {
const curPage = FieldValue(this.Document.curPage, -1);
- return this.children.filter(doc => {
+ return this.childDocs.filter(doc => {
var page = NumCast(doc.page, -1);
return page === curPage || page === -1;
});
@@ -89,7 +89,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let y = yp - de.data.yOffset / zoom;
let dropX = NumCast(de.data.droppedDocuments[0].x);
let dropY = NumCast(de.data.droppedDocuments[0].y);
- de.data.droppedDocuments.map(d => {
+ de.data.droppedDocuments.forEach(d => {
d.x = x + NumCast(d.x) - dropX;
d.y = y + NumCast(d.y) - dropY;
if (!NumCast(d.width)) {
@@ -111,11 +111,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerDown = (e: React.PointerEvent): void => {
- if ((CollectionFreeFormView.RIGHT_BTN_DRAG &&
- (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) ||
- (e.button === 0 && e.altKey)) && this.props.active())) ||
- (!CollectionFreeFormView.RIGHT_BTN_DRAG &&
- ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && this.props.active()))) {
+ if (e.button === 0 && !e.shiftKey && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1) && this.props.active()) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointermove", this.onPointerMove);
@@ -133,20 +129,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerMove = (e: PointerEvent): void => {
if (!e.cancelBubble) {
- let x = NumCast(this.props.Document.panX);
- let y = NumCast(this.props.Document.panY);
- let docs = this.children || [];
+ let x = this.Document.panX || 0;
+ let y = this.Document.panY || 0;
+ let docs = this.childDocs || [];
let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
if (!this.isAnnotationOverlay) {
let minx = docs.length ? NumCast(docs[0].x) : 0;
- let maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
+ let maxx = docs.length ? NumCast(docs[0].width) / NumCast(docs[0].zoomBasis) + 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(docs[0].height) / NumCast(docs[0].zoomBasis) + miny : miny;
let ranges = docs.filter(doc => doc).reduce((range, doc) => {
let x = NumCast(doc.x);
- let xe = x + NumCast(doc.width);
+ let xe = x + NumCast(doc.width) / NumCast(doc.zoomBasis);
let y = NumCast(doc.y);
- let ye = y + NumCast(doc.height);
+ let ye = y + NumCast(doc.height) / NumCast(doc.zoomBasis);
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]]);
@@ -179,7 +175,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// if (!this.props.active()) {
// return;
// }
- let childSelected = this.children.some(doc => {
+ let childSelected = this.childDocs.some(doc => {
var dv = DocumentManager.Instance.getDocumentView(doc);
return dv && SelectionManager.IsSelected(dv) ? true : false;
});
@@ -210,7 +206,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let [x, y] = this.getTransform().transformPoint(e.clientX, e.clientY);
let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
- let safeScale = Math.abs(localTransform.Scale);
+ let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
this.props.Document.scale = Math.abs(safeScale);
this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
e.stopPropagation();
@@ -219,6 +215,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
setPan(panX: number, panY: number) {
+ this.panDisposer && clearTimeout(this.panDisposer);
+ this.props.Document.panTransformType = "None";
var scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
@@ -236,7 +234,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
bringToFront = (doc: Doc) => {
- const docs = this.children;
+ const docs = this.childDocs;
docs.slice().sort((doc1, doc2) => {
if (doc1 === doc) return 1;
if (doc2 === doc) return -1;
@@ -245,16 +243,37 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
doc.zIndex = docs.length + 1;
}
+ panDisposer?: NodeJS.Timeout;
focusDocument = (doc: Doc) => {
+ const panX = this.Document.panX;
+ const panY = this.Document.panY;
+ const id = this.Document[Id];
+ const state = HistoryUtil.getState();
+ // TODO This technically isn't correct if type !== "doc", as
+ // currently nothing is done, but we should probably push a new state
+ if (state.type === "doc" && panX !== undefined && panY !== undefined) {
+ const init = state.initializers[id];
+ if (!init) {
+ state.initializers[id] = {
+ panX, panY
+ };
+ HistoryUtil.pushState(state);
+ } else if (init.panX !== panX || init.panY !== panY) {
+ init.panX = panX;
+ init.panY = panY;
+ HistoryUtil.pushState(state);
+ }
+ }
SelectionManager.DeselectAll();
+ const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
+ const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
+ const newState = HistoryUtil.getState();
+ newState.initializers[id] = { panX: newPanX, panY: newPanY };
+ HistoryUtil.pushState(newState);
+ this.setPan(newPanX, newPanY);
this.props.Document.panTransformType = "Ease";
- this.setPan(
- NumCast(doc.x) + NumCast(doc.width) / 2,
- NumCast(doc.y) + NumCast(doc.height) / 2);
this.props.focus(this.props.Document);
- if (this.props.Document.panTransformType === "Ease") {
- setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false
- }
+ this.panDisposer = setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false
}
@@ -276,17 +295,18 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
parentActive: this.props.active,
whenActiveChanged: this.props.whenActiveChanged,
bringToFront: this.bringToFront,
+ addDocTab: this.props.addDocTab,
};
}
@computed.struct
get views() {
let curPage = FieldValue(this.Document.curPage, -1);
- let docviews = this.children.reduce((prev, doc) => {
+ let docviews = this.childDocs.reduce((prev, doc) => {
if (!(doc instanceof Doc)) return prev;
var page = NumCast(doc.page, -1);
if (page === curPage || page === -1) {
- let minim = Cast(doc.isMinimized, "boolean");
+ let minim = BoolCast(doc.isMinimized, false);
if (minim === undefined || !minim) {
prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getDocumentViewProps(doc)} />);
}
@@ -350,7 +370,7 @@ class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps
isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
}
render() {
- return this.backgroundView;
+ return this.props.Document.backgroundLayout ? this.backgroundView : (null);
}
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 080c484f4..4587c2227 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -17,6 +17,9 @@ import { InkField, StrokeData } from "../../../../new_fields/InkField";
import { List } from "../../../../new_fields/List";
import { ImageField } from "../../../../new_fields/URLField";
import { Template, Templates } from "../../Templates";
+import { Gateway } from "../../../northstar/manager/Gateway";
+import { DocServer } from "../../../DocServer";
+import { Id } from "../../../../new_fields/RefField";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -60,7 +63,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
e.preventDefault();
(async () => {
let text: string = await navigator.clipboard.readText();
- let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != "");
+ let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
for (let i = 0; i < ns.length - 1; i++) {
while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") ||
ns[i].endsWith(";\r") || ns[i].endsWith(";") ||
@@ -77,9 +80,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
let newBox = Docs.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line });
this.props.addDocument(newBox, false);
y += 40 * this.props.getTransform().Scale;
- })
+ });
})();
- } else if (e.key === "t" && e.ctrlKey) {
+ } else if (e.key === "b" && e.ctrlKey) {
//heuristically converts pasted text into a table.
// assumes each entry is separated by a tab
// skips all rows until it gets to a row with more than one entry
@@ -90,9 +93,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
e.preventDefault();
(async () => {
let text: string = await navigator.clipboard.readText();
- let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != "");
- while (ns.length > 0 && ns[0].split("\t").length < 2)
+ let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
+ while (ns.length > 0 && ns[0].split("\t").length < 2) {
ns.splice(0, 1);
+ }
if (ns.length > 0) {
let columns = ns[0].split("\t");
let docList: Doc[] = [];
@@ -104,18 +108,15 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
continue;
}
let doc = new Doc();
- columns.forEach((col, i) => {
- console.log(values[i] + " " + Number(values[i]).toString());
- doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined);
- });
+ columns.forEach((col, i) => doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
if (groupAttr) {
- doc["_group"] = groupAttr;
+ doc._group = groupAttr;
}
doc.title = i.toString();
docList.push(doc);
}
- let newCol = Docs.SchemaDocument(docList, { x: x, y: y, title: "-dropped table-", width: 300, height: 100 });
- newCol.proto!.schemaColumns = new List<string>([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)]);
+ let newCol = Docs.SchemaDocument([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 });
+
this.props.addDocument(newCol, false);
}
})();
@@ -131,17 +132,18 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this._downY = this._lastY = e.pageY;
this._commandExecuted = false;
PreviewCursor.Visible = false;
- if ((CollectionFreeFormView.RIGHT_BTN_DRAG && e.button === 0 && !e.altKey && !e.metaKey && this.props.container.props.active()) ||
- (!CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && this.props.container.props.active())) {
+ if (e.button === 2 || (e.button === 0 && e.altKey)) {
+ if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
document.addEventListener("pointermove", this.onPointerMove, true);
document.addEventListener("pointerup", this.onPointerUp, true);
document.addEventListener("keydown", this.marqueeCommand, true);
- // bcz: do we need this? it kills the context menu on the main collection
+ if (e.altKey) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ // bcz: do we need this? it kills the context menu on the main collection if !altKey
// e.stopPropagation();
}
- if (e.altKey) {
- e.preventDefault();
- }
}
@action
@@ -207,11 +209,13 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@undoBatch
@action
marqueeCommand = async (e: KeyboardEvent) => {
- if (this._commandExecuted) {
+ if (this._commandExecuted || (e as any).propagationIsStopped) {
return;
}
if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") {
this._commandExecuted = true;
+ e.stopPropagation();
+ (e as any).propagationIsStopped = true;
this.marqueeSelect().map(d => this.props.removeDocument(d));
let ink = Cast(this.props.container.props.Document.ink, InkField);
if (ink) {
@@ -221,26 +225,21 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this.cleanupInteractions(false);
e.stopPropagation();
}
- if (e.key === "c" || e.key === "r" || e.key === "s" || e.key === "e" || e.key === "p") {
+ if (e.key === "c" || e.key === "s" || e.key === "e" || e.key === "p") {
this._commandExecuted = true;
e.stopPropagation();
+ (e as any).propagationIsStopped = true;
let bounds = this.Bounds;
- let selected = this.marqueeSelect().map(d => {
- if (e.key === "s") {
- let dCopy = Doc.MakeCopy(d);
- dCopy.x = NumCast(d.x) - bounds.left - bounds.width / 2;
- dCopy.y = NumCast(d.y) - bounds.top - bounds.height / 2;
- dCopy.page = -1;
- return dCopy;
- }
- else if (e.key !== "r") {
+ let selected = this.marqueeSelect();
+ if (e.key === "c") {
+ selected.map(d => {
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;
- }
- return d;
- });
+ return d;
+ });
+ }
let ink = Cast(this.props.container.props.Document.ink, InkField);
let inkData = ink ? ink.inkData : undefined;
let zoomBasis = NumCast(this.props.container.props.Document.scale, 1);
@@ -250,39 +249,36 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
panX: 0,
panY: 0,
borderRounding: e.key === "e" ? -1 : undefined,
+ backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white",
scale: zoomBasis,
width: bounds.width * zoomBasis,
height: bounds.height * zoomBasis,
ink: inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined,
- title: "a nested collection",
+ title: e.key === "s" ? "-summary-" : e.key === "p" ? "-summary-" : "a nested collection",
});
-
this.marqueeInkDelete(inkData);
- // SelectionManager.DeselectAll();
- if (e.key === "s" || e.key === "r" || e.key === "p") {
- e.preventDefault();
- let scrpt = this.props.getTransform().inverse().transformPoint(bounds.left, bounds.top);
- let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
- let dataUrl = await htmlToImage.toPng(this._mainCont.current!, { width: bounds.width, height: bounds.height, quality: 1 });
- summary.proto!.thumbnail = new ImageField(new URL(dataUrl));
+ if (e.key === "s" || e.key === "p") {
- summary.proto!.templates = new List<string>([Templates.ImageOverlay(Math.min(50, bounds.width), bounds.height * Math.min(50, bounds.width) / bounds.width, "thumbnail")]);
- if (e.key === "s" || e.key === "p") {
- summary.proto!.maximizeOnRight = true;
+ htmlToImage.toPng(this._mainCont.current!, { width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, quality: 1 }).then((dataUrl) => {
+ selected.map(d => {
+ 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;
+ return d;
+ });
+ let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
+ summary.proto!.thumbnail = new ImageField(new URL(dataUrl));
+ summary.proto!.templates = new List<string>([Templates.ImageOverlay(Math.min(50, bounds.width), bounds.height * Math.min(50, bounds.width) / bounds.width, "thumbnail")]);
newCollection.proto!.summaryDoc = summary;
selected = [newCollection];
- }
- summary.proto!.summarizedDocs = new List<Doc>(selected);
- //summary.proto!.isButton = true;
- selected.map(summarizedDoc => {
- let maxx = NumCast(summarizedDoc.x, undefined);
- let maxy = NumCast(summarizedDoc.y, undefined);
- let maxw = NumCast(summarizedDoc.width, undefined);
- let maxh = NumCast(summarizedDoc.height, undefined);
- summarizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), 0])
+ newCollection.x = bounds.left + bounds.width;
+ //this.props.addDocument(newCollection, false);
+ summary.proto!.summarizedDocs = new List<Doc>(selected);
+ summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight"
+ this.props.addLiveTextDocument(summary);
});
- this.props.addLiveTextDocument(summary);
}
else {
this.props.addDocument(newCollection, false);
@@ -290,20 +286,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this.props.selectDocuments([newCollection]);
}
this.cleanupInteractions(false);
- } else
- if (e.key === "s") {
- // this._commandExecuted = true;
- // e.stopPropagation();
- // e.preventDefault();
- // let bounds = this.Bounds;
- // let selected = this.marqueeSelect();
- // SelectionManager.DeselectAll();
- // let summary = Docs.TextDocument({ x: bounds.left + bounds.width + 25, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
- // this.props.addLiveTextDocument(summary);
- // selected.forEach(select => Doc.MakeLink(summary.proto!, select.proto!));
-
- // this.cleanupInteractions(false);
- }
+ }
}
@action
marqueeInkSelect(ink: Map<any, any>) {