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.tsx6
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx25
-rw-r--r--src/client/views/collections/CollectionPDFView.scss39
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss1
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx254
-rw-r--r--src/client/views/collections/CollectionStackingView.scss51
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx179
-rw-r--r--src/client/views/collections/CollectionSubView.tsx22
-rw-r--r--src/client/views/collections/CollectionTreeView.scss9
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx53
-rw-r--r--src/client/views/collections/CollectionVideoView.scss2
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx48
-rw-r--r--src/client/views/collections/CollectionView.tsx20
-rw-r--r--src/client/views/collections/ParentDocumentSelector.scss16
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx48
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx22
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx38
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx59
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx141
23 files changed, 785 insertions, 269 deletions
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 84ffbac36..734669893 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -7,8 +7,8 @@ import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Typ
import { Doc, FieldResult, Opt, DocListCast } from '../../../new_fields/Doc';
import { listSpec } from '../../../new_fields/Schema';
import { List } from '../../../new_fields/List';
-import { Id } from '../../../new_fields/RefField';
import { SelectionManager } from '../../util/SelectionManager';
+import { Id } from '../../../new_fields/FieldSymbols';
export enum CollectionViewType {
Invalid,
@@ -16,6 +16,7 @@ export enum CollectionViewType {
Schema,
Docking,
Tree,
+ Stacking
}
export interface CollectionRenderProps {
@@ -119,7 +120,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
// set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument?
if (!alreadyAdded && (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid)) {
let zoom = NumCast(this.props.Document.scale, 1);
- Doc.SetOnPrototype(doc, "zoomBasis", zoom);
+ // Doc.GetProto(doc).zoomBasis = zoom;
}
}
return true;
@@ -127,6 +128,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
@action.bound
removeDocument(doc: Doc): boolean {
+ SelectionManager.DeselectAll();
const props = this.props;
//TODO This won't create the field if it doesn't already exist
const value = Cast(props.Document[props.fieldKey], listSpec(Doc), []);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 06b262d79..dcc1bd95d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -6,7 +6,7 @@ import * as ReactDOM from 'react-dom';
import Measure from "react-measure";
import * as GoldenLayout from "../../../client/goldenLayout";
import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc";
-import { FieldId, Id } from "../../../new_fields/RefField";
+import { FieldId } from "../../../new_fields/RefField";
import { listSpec } from "../../../new_fields/Schema";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { emptyFunction, returnTrue, Utils } from "../../../Utils";
@@ -21,6 +21,8 @@ import React = require("react");
import { ParentDocSelector } from './ParentDocumentSelector';
import { DocumentManager } from '../../util/DocumentManager';
import { CollectionViewType } from './CollectionBaseView';
+import { Id } from '../../../new_fields/FieldSymbols';
+import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
@@ -75,7 +77,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@undoBatch
@action
- public CloseRightSplit(document: Doc): boolean {
+ public CloseRightSplit = (document: Doc): boolean => {
let retVal = false;
if (this._goldenLayout.root.contentItems[0].isRow) {
retVal = Array.from(this._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
@@ -118,7 +120,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@action
- public AddRightSplit(document: Doc, minimize: boolean = false) {
+ public AddRightSplit = (document: Doc, minimize: boolean = false) => {
let docs = Cast(this.props.Document.data, listSpec(Doc));
if (docs) {
docs.push(document);
@@ -155,7 +157,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return newContentItem;
}
@action
- public AddTab(stack: any, document: Doc) {
+ public AddTab = (stack: any, document: Doc) => {
let docs = Cast(this.props.Document.data, listSpec(Doc));
if (docs) {
docs.push(document);
@@ -331,7 +333,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
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);
+ const stack = tab.contentItem.parent;
+ ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, location) => CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv);
tab.reactComponents = [upDiv];
tab.element.append(upDiv);
counter.DashDocId = tab.contentItem.config.props.documentId;
@@ -412,10 +415,14 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@observable private _document: Opt<Doc>;
- _stack: any;
+ get _stack(): any {
+ let parent = (this.props as any).glContainer.parent.parent;
+ if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1)
+ return parent.parent.contentItems[1];
+ return parent;
+ }
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));
}
@@ -432,7 +439,7 @@ 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(1 / scale);
+ return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
}
return Transform.Identity();
}
@@ -444,7 +451,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
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; }
@@ -464,7 +470,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
<div className="collectionDockingView-content" ref={this._mainCont}
style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier}, ${this.scaleToFitMultiplier})` }}>
<DocumentView key={this._document[Id]} Document={this._document}
- toggleMinimized={emptyFunction}
bringToFront={emptyFunction}
addDocument={undefined}
removeDocument={undefined}
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
index f6fb79582..50201bae8 100644
--- a/src/client/views/collections/CollectionPDFView.scss
+++ b/src/client/views/collections/CollectionPDFView.scss
@@ -1,49 +1,56 @@
.collectionPdfView-buttonTray {
- top : 15px;
- left : 20px;
- position: relative;
+ top: 15px;
+ left: 20px;
+ position: relative;
transform-origin: left top;
position: absolute;
}
+
.collectionPdfView-thumb {
- width:25px;
- height:25px;
+ width: 25px;
+ height: 25px;
transform-origin: left top;
position: absolute;
background: darkgray;
}
+
.collectionPdfView-slider {
- width:25px;
- height:25px;
+ width: 25px;
+ height: 25px;
transform-origin: left top;
position: absolute;
background: lightgray;
}
-.collectionPdfView-cont{
+
+.collectionPdfView-cont {
width: 100%;
height: 100%;
- position: absolute;
+ position: absolute;
top: 0;
- left:0;
+ left: 0;
+ z-index: -1;
}
+
.collectionPdfView-cont-dragging {
span {
user-select: none;
}
}
+
.collectionPdfView-backward {
- color : white;
+ color: white;
font-size: 24px;
- top :0px;
- left : 0px;
+ top: 0px;
+ left: 0px;
position: absolute;
background-color: rgba(50, 50, 50, 0.2);
}
+
.collectionPdfView-forward {
- color : white;
+ color: white;
font-size: 24px;
- top :0px;
- left : 45px;
+ top: 0px;
+ left: 45px;
position: absolute;
background-color: rgba(50, 50, 50, 0.2);
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index a6614da21..5e51437a4 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -8,7 +8,7 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
import { emptyFunction } from "../../../Utils";
import { NumCast } from "../../../new_fields/Types";
-import { Id } from "../../../new_fields/RefField";
+import { Id } from "../../../new_fields/FieldSymbols";
@observer
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index df5c52d01..186e006f3 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -11,6 +11,7 @@
position: absolute;
width: 100%;
height: 100%;
+ overflow: hidden;
.collectionSchemaView-cellContents {
height: $MAX_ROW_HEIGHT;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f15da41ff..11d71d023 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, untracked, runInAction } from "mobx";
+import { action, computed, observable, untracked, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss';
@@ -20,15 +20,22 @@ 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, StrCast } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
import { listSpec } from "../../../new_fields/Schema";
import { List } from "../../../new_fields/List";
-import { Id } from "../../../new_fields/RefField";
+import { Id } from "../../../new_fields/FieldSymbols";
import { Gateway } from "../../northstar/manager/Gateway";
import { Docs } from "../../documents/Documents";
import { ContextMenu } from "../ContextMenu";
+import { CollectionView } from "./CollectionView";
+import { CollectionPDFView } from "./CollectionPDFView";
+import { CollectionVideoView } from "./CollectionVideoView";
+import { SelectionManager } from "../../util/SelectionManager";
+import { undoBatch } from "../../util/UndoManager";
+library.add(faCog);
+library.add(faPlus);
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -51,7 +58,7 @@ class KeyToggle extends React.Component<{ keyName: string, checked: boolean, tog
@observer
export class CollectionSchemaView extends CollectionSubView(doc => doc) {
private _mainCont?: HTMLDivElement;
- private _startSplitPercent = 0;
+ private _startPreviewWidth = 0;
private DIVIDER_WIDTH = 4;
@observable _columns: Array<string> = ["title", "data", "author"];
@@ -59,16 +66,18 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@observable _columnsPercentage = 0;
@observable _keys: string[] = [];
@observable _newKeyName: string = "";
+ @observable previewScript: string = "";
- @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage); }
+ @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
+ @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; }
+ @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); }
@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>,
+ Header: <p ref={ref} onPointerDown={SetupDrag(ref, () => this.onHeaderDrag(col), undefined, "copy")}>{col}</p>,
accessor: (doc: Doc) => doc ? doc[col] : 0,
id: col
};
@@ -76,6 +85,15 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
onHeaderDrag = (columnName: string) => {
+ let schemaDoc = Cast(this.props.Document.schemaDoc, Doc);
+ if (schemaDoc instanceof Doc) {
+ let columnDocs = DocListCast(schemaDoc.data);
+ if (columnDocs) {
+ let ddoc = columnDocs.find(doc => doc.title === columnName);
+ if (ddoc)
+ return ddoc;
+ }
+ }
return this.props.Document;
}
@@ -100,7 +118,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
let reference = React.createRef<HTMLDivElement>();
let onItemDown = (e: React.PointerEvent) =>
(this.props.CollectionView.props.isSelected() ?
- SetupDrag(reference, () => props.Document, this.props.moveDocument)(e) : undefined);
+ SetupDrag(reference, () => props.Document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e) : undefined);
let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => {
const res = run({ this: doc });
if (!res.success) return false;
@@ -115,22 +133,20 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
height={Number(MAX_ROW_HEIGHT)}
GetValue={() => {
let field = props.Document[props.fieldKey];
- if (field) {
- //TODO Types
- // return field.ToScriptString();
- return String(field);
+ if (Field.IsField(field)) {
+ return Field.toScriptString(field);
}
return "";
}}
SetValue={(value: string) => {
- let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
+ let script = CompileScript(value, { addReturn: true, params: { this: Doc.name } });
if (!script.compiled) {
return false;
}
return applyToDoc(props.Document, script.run);
}}
OnFillDown={async (value: string) => {
- let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
+ let script = CompileScript(value, { addReturn: true, params: { this: Doc.name } });
if (!script.compiled) {
return;
}
@@ -187,30 +203,31 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
//toggles preview side-panel of schema
@action
- toggleExpander = (event: React.ChangeEvent<HTMLInputElement>) => {
- this.props.Document.schemaSplitPercentage = this.splitPercentage === 0 ? 33 : 0;
+ toggleExpander = () => {
+ this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0;
}
+ onDividerDown = (e: React.PointerEvent) => {
+ this._startPreviewWidth = this.previewWidth();
+ e.stopPropagation();
+ e.preventDefault();
+ document.addEventListener("pointermove", this.onDividerMove);
+ document.addEventListener('pointerup', this.onDividerUp);
+ }
@action
onDividerMove = (e: PointerEvent): void => {
let nativeWidth = this._mainCont!.getBoundingClientRect();
- this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100));
+ this.props.Document.schemaPreviewWidth = Math.min(nativeWidth.right - nativeWidth.left - 40,
+ this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]);
}
@action
onDividerUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onDividerMove);
document.removeEventListener('pointerup', this.onDividerUp);
- if (this._startSplitPercent === this.splitPercentage) {
- this.props.Document.schemaSplitPercentage = this.splitPercentage === 0 ? 33 : 0;
+ if (this._startPreviewWidth === this.previewWidth()) {
+ this.toggleExpander();
}
}
- onDividerDown = (e: React.PointerEvent) => {
- this._startSplitPercent = this.splitPercentage;
- e.stopPropagation();
- e.preventDefault();
- document.addEventListener("pointermove", this.onDividerMove);
- document.addEventListener('pointerup', this.onDividerUp);
- }
onPointerDown = (e: React.PointerEvent): void => {
if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
@@ -237,16 +254,17 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
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 += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "0") + ",", "");
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 });
+ let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }, { dbDoc: self.props.Document });
if (schemaDoc) {
- self.props.CollectionView.props.addDocument(schemaDoc, false);
+ //self.props.CollectionView.props.addDocument(schemaDoc, false);
+ self.props.Document.schemaDoc = schemaDoc;
}
}
}
@@ -262,63 +280,16 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
this._newKeyName = e.currentTarget.value;
}
- @observable previewScript: string = "";
- @action
- onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- this.previewScript = e.currentTarget.value;
- }
-
@computed
get previewDocument(): Doc | undefined {
const children = DocListCast(this.props.Document[this.props.fieldKey]);
const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : 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; }
- get previewRegionWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * this.splitPercentage / 100; }
-
- private previewDocNativeWidth = () => Cast(this.previewDocument!.nativeWidth, "number", this.previewRegionWidth);
- private previewDocNativeHeight = () => Cast(this.previewDocument!.nativeHeight, "number", this.previewRegionHeight);
- private previewContentScaling = () => {
- let wscale = this.previewRegionWidth / (this.previewDocNativeWidth() ? this.previewDocNativeWidth() : this.previewRegionWidth);
- if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight) {
- return this.previewRegionHeight / (this.previewDocNativeHeight() ? this.previewDocNativeHeight() : this.previewRegionHeight);
- }
- return wscale;
- }
- private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling();
- private previewPanelHeight = () => this.previewDocNativeHeight() * this.previewContentScaling();
- get previewPanelCenteringOffset() { return (this.previewRegionWidth - this.previewDocNativeWidth() * this.previewContentScaling()) / 2; }
+
getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
- - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth - this.previewPanelCenteringOffset,
- - this.borderWidth).scale(1 / this.previewContentScaling())
+ - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth);
- @computed
- get previewPanel() {
- // let doc = CompileScript(this.previewScript, { this: selected }, true)();
- const previewDoc = this.previewDocument;
- 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}
- addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
- ScreenToLocalTransform={this.getPreviewTransform}
- ContentScaling={this.previewContentScaling}
- PanelWidth={this.previewPanelWidth} PanelHeight={this.previewPanelHeight}
- ContainingCollectionView={this.props.CollectionView}
- focus={emptyFunction}
- 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, (previewDoc ? this.previewPanelWidth() / 2 : 75))}px)` }} />
- </div>);
- }
get documentKeysCheckList() {
const docs = DocListCast(this.props.Document[this.props.fieldKey]);
@@ -344,7 +315,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
<div id="schema-options-header"><h5><b>Options</b></h5></div>
<div id="options-flyout-div">
<h6 className="schema-options-subHeader">Preview Window</h6>
- <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage !== 0} onChange={this.toggleExpander} /> Show Preview </div>
+ <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} /> Show Preview </div>
<h6 className="schema-options-subHeader" >Displayed Columns</h6>
<ul id="schema-col-checklist" >
{this.documentKeysCheckList}
@@ -359,31 +330,130 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
@computed
+ get reactTable() {
+ trace();
+ let previewWidth = this.previewWidth() + 2 * this.borderWidth + this.DIVIDER_WIDTH + 1;
+ return <ReactTable style={{ position: "relative", float: "left", width: `calc(100% - ${previewWidth}px` }} data={this.childDocs} page={0} pageSize={this.childDocs.length} showPagination={false}
+ columns={this.tableColumns}
+ column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
+ getTrProps={this.getTrProps}
+ />
+ }
+
+ @computed
get dividerDragger() {
- return this.splitPercentage === 0 ? (null) :
+ return this.previewWidth() === 0 ? (null) :
<div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
}
-
+ @computed
+ get previewPanel() {
+ trace();
+ return <CollectionSchemaPreview
+ Document={this.previewDocument}
+ width={this.previewWidth}
+ height={this.previewHeight}
+ getTransform={this.getPreviewTransform}
+ CollectionView={this.props.CollectionView}
+ addDocument={this.props.addDocument}
+ removeDocument={this.props.removeDocument}
+ active={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ addDocTab={this.props.addDocTab}
+ setPreviewScript={this.setPreviewScript}
+ previewScript={this.previewScript}
+ />
+ }
+ @action
+ setPreviewScript = (script: string) => {
+ this.previewScript = script;
+ }
render() {
- library.add(faCog);
- library.add(faPlus);
- const children = this.childDocs;
+ trace();
return (
<div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
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.tableColumns}
- column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
- getTrProps={this.getTrProps}
- />
- </div>
+ {this.reactTable}
{this.dividerDragger}
- {this.previewPanel}
+ {!this.previewWidth() ? (null) : this.previewPanel}
{this.tableOptionsPanel}
</div>
);
}
+}
+interface CollectionSchemaPreviewProps {
+ Document?: Doc;
+ width: () => number;
+ height: () => number;
+ CollectionView: CollectionView | CollectionPDFView | CollectionVideoView;
+ getTransform: () => Transform;
+ addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+ removeDocument: (document: Doc) => boolean;
+ active: () => boolean;
+ whenActiveChanged: (isActive: boolean) => void;
+ addDocTab: (document: Doc, where: string) => void;
+ setPreviewScript: (script: string) => void;
+ previewScript?: string;
+}
+
+@observer
+export class CollectionSchemaPreview extends React.Component<CollectionSchemaPreviewProps>{
+ 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;
+ }
+ private PanelWidth = () => this.nativeWidth * this.contentScaling();
+ private PanelHeight = () => this.nativeHeight * this.contentScaling();
+ private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling())
+ get centeringOffset() { return (this.props.width() - this.nativeWidth * this.contentScaling()) / 2; }
+ @action
+ onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.props.setPreviewScript(e.currentTarget.value);
+ }
+ @undoBatch
+ @action
+ public collapseToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => {
+ SelectionManager.DeselectAll();
+ if (expandedDocs) {
+ let isMinimized: boolean | undefined;
+ expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => {
+ if (isMinimized === undefined) {
+ isMinimized = BoolCast(maximizedDoc.isMinimized, false);
+ }
+ maximizedDoc.isMinimized = !isMinimized;
+ });
+ }
+ }
+ render() {
+ trace();
+ console.log(this.props.Document);
+ let input = this.props.previewScript === undefined ? (null) :
+ <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)` }} />;
+ return (<div className="collectionSchemaView-previewRegion" style={{ width: this.props.width() }}>
+ {!this.props.Document || !this.props.width ? (null) : (
+ <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.centeringOffset}px, 0px)` }}>
+ <DocumentView Document={this.props.Document} isTopMost={false} selectOnLoad={false}
+ addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
+ ScreenToLocalTransform={this.getTransform}
+ ContentScaling={this.contentScaling}
+ PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight}
+ ContainingCollectionView={this.props.CollectionView}
+ focus={emptyFunction}
+ parentActive={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ bringToFront={emptyFunction}
+ addDocTab={this.props.addDocTab}
+ collapseToPoint={this.collapseToPoint}
+ />
+ </div>)}
+ {input}
+ </div>);
+ }
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
new file mode 100644
index 000000000..4d84aaaa9
--- /dev/null
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -0,0 +1,51 @@
+@import "../globalCssVariables";
+
+.collectionStackingView {
+ top: 0;
+ left: 0;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ position: absolute;
+ overflow-y: auto;
+ border-width: 0;
+ box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw;
+ border-color: $light-color-secondary;
+ border-style: solid;
+ border-radius: 0 0 $border-radius $border-radius;
+ box-sizing: border-box;
+
+ .collectionStackingView-docView-container {
+ width: 45%;
+ margin: 5% 2.5%;
+ padding-left: 2.5%;
+ height: auto;
+ }
+
+ .collectionStackingView-flexCont {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ align-items: center;
+ }
+
+ .collectionStackingView-masonrySingle, .collectionStackingView-masonryGrid{
+ width:100%;
+ height:100%;
+ position: absolute;
+ }
+ .collectionStackingView-masonryGrid {
+ display:grid;
+ }
+
+ .collectionStackingView-description {
+ font-size: 100%;
+ margin-bottom: 1vw;
+ padding: 10px;
+ height: 2vw;
+ width: 100%;
+ font-family: $sans-serif;
+ background: $dark-color;
+ color: $light-color;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
new file mode 100644
index 000000000..da7ea50c6
--- /dev/null
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -0,0 +1,179 @@
+import React = require("react");
+import { action, computed, IReactionDisposer, reaction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { BoolCast, NumCast } from "../../../new_fields/Types";
+import { emptyFunction, returnOne, Utils } from "../../../Utils";
+import { SelectionManager } from "../../util/SelectionManager";
+import { undoBatch } from "../../util/UndoManager";
+import { DocumentView } from "../nodes/DocumentView";
+import { CollectionSchemaPreview } from "./CollectionSchemaView";
+import "./CollectionStackingView.scss";
+import { CollectionSubView } from "./CollectionSubView";
+
+@observer
+export class CollectionStackingView extends CollectionSubView(doc => doc) {
+ _masonryGridRef: HTMLDivElement | null = null;
+ _heightDisposer?: IReactionDisposer;
+ get gridGap() { return 10; }
+ get gridSize() { return 20; }
+ get singleColumn() { return BoolCast(this.props.Document.singleColumn, true); }
+ get columnWidth() { return this.singleColumn ? this.props.PanelWidth() - 4 * this.gridGap : NumCast(this.props.Document.columnWidth, 250); }
+
+ componentDidMount() {
+ this._heightDisposer = reaction(() => [this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized]), this.columnWidth, this.props.PanelHeight()],
+ () => {
+ if (this.singleColumn) {
+ this.props.Document.height = this.childDocs.filter(d => !d.isMinimized).reduce((height, d) => {
+ let hgt = d[HeightSym]();
+ let wid = d[WidthSym]();
+ let nw = NumCast(d.nativeWidth);
+ let nh = NumCast(d.nativeHeight);
+ if (nw && nh) hgt = nh / nw * Math.min(this.columnWidth, wid);
+ return height + hgt + 2 * this.gridGap;
+ }, this.gridGap * 2);
+ }
+ }, { fireImmediately: true });
+ }
+ componentWillUnmount() {
+ if (this._heightDisposer) this._heightDisposer();
+ }
+
+ @action
+ moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => {
+ this.props.removeDocument(doc);
+ addDocument(doc);
+ return true;
+ }
+ getDocTransform(doc: Doc, dref: HTMLDivElement) {
+ let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
+ 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]).scale(NumCast(doc.width, 1) / this.columnWidth);
+ }
+ createRef = (ele: HTMLDivElement | null) => {
+ this._masonryGridRef = ele;
+ this.createDropTarget(ele!);
+ }
+ @undoBatch
+ @action
+ public collapseToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => {
+ SelectionManager.DeselectAll();
+ if (expandedDocs) {
+ let isMinimized: boolean | undefined;
+ expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => {
+ if (isMinimized === undefined) {
+ isMinimized = BoolCast(maximizedDoc.isMinimized, false);
+ }
+ maximizedDoc.isMinimized = !isMinimized;
+ });
+ }
+ }
+
+ @computed
+ get singleColumnChildren() {
+ return this.childDocs.filter(d => !d.isMinimized).map((d, i) => {
+ let dref = React.createRef<HTMLDivElement>();
+ let script = undefined;
+ let colWidth = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth;
+ let margin = colWidth() < this.columnWidth ? "auto" : undefined;
+ let rowHeight = () => {
+ let hgt = d[HeightSym]();
+ let nw = NumCast(d.nativeWidth);
+ let nh = NumCast(d.nativeHeight);
+ if (nw && nh) hgt = nh / nw * colWidth();
+ return hgt;
+ }
+ let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]());
+ return <div className="collectionStackingView-masonryDoc"
+ key={d[Id]}
+ ref={dref}
+ style={{ marginTop: `${i ? 2 * this.gridGap : 0}px`, width: colWidth(), height: rowHeight(), marginLeft: margin, marginRight: margin }} >
+ <CollectionSchemaPreview
+ Document={d}
+ width={colWidth}
+ height={rowHeight}
+ getTransform={dxf}
+ CollectionView={this.props.CollectionView}
+ addDocument={this.props.addDocument}
+ removeDocument={this.props.removeDocument}
+ active={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ addDocTab={this.props.addDocTab}
+ setPreviewScript={emptyFunction}
+ previewScript={script}>
+ </CollectionSchemaPreview>
+ </div>;
+ });
+ }
+ @computed
+ get children() {
+ return this.childDocs.filter(d => !d.isMinimized).map(d => {
+ let dref = React.createRef<HTMLDivElement>();
+ let dxf = () => this.getDocTransform(d, dref.current!);
+ let colSpan = Math.ceil(Math.min(d[WidthSym](), this.columnWidth + this.gridGap) / (this.gridSize + this.gridGap));
+ let rowSpan = Math.ceil((this.columnWidth / d[WidthSym]() * d[HeightSym]() + this.gridGap) / (this.gridSize + this.gridGap));
+ let childFocus = (doc: Doc) => {
+ doc.libraryBrush = true;
+ this.props.focus(this.props.Document); // just focus on this collection, not the underlying document because the API doesn't support adding an offset to focus on and we can't pan zoom our contents to be centered.
+ }
+ return (<div className="collectionStackingView-masonryDoc"
+ key={d[Id]}
+ ref={dref}
+ style={{
+ width: NumCast(d.nativeWidth, d[WidthSym]()),
+ height: NumCast(d.nativeHeight, d[HeightSym]()),
+ transformOrigin: "top left",
+ gridRowEnd: `span ${rowSpan}`,
+ gridColumnEnd: `span ${colSpan}`,
+ transform: `scale(${this.columnWidth / NumCast(d.nativeWidth, d[WidthSym]())}, ${this.columnWidth / NumCast(d.nativeWidth, d[WidthSym]())})`
+ }} >
+ <DocumentView key={d[Id]} Document={d}
+ addDocument={this.props.addDocument}
+ removeDocument={this.props.removeDocument}
+ moveDocument={this.moveDocument}
+ ContainingCollectionView={this.props.CollectionView}
+ isTopMost={false}
+ ScreenToLocalTransform={dxf}
+ focus={childFocus}
+ ContentScaling={returnOne}
+ PanelWidth={d[WidthSym]}
+ PanelHeight={d[HeightSym]}
+ selectOnLoad={false}
+ parentActive={this.props.active}
+ addDocTab={this.props.addDocTab}
+ bringToFront={emptyFunction}
+ whenActiveChanged={this.props.whenActiveChanged}
+ collapseToPoint={this.collapseToPoint}
+ />
+ </div>);
+ })
+ }
+ render() {
+ let leftMargin = 2 * this.gridGap;
+ let topMargin = 2 * this.gridGap;
+ let itemCols = Math.ceil(this.columnWidth / (this.gridSize + this.gridGap));
+ let cells = Math.floor((this.props.PanelWidth() - leftMargin) / (itemCols * (this.gridSize + this.gridGap)));
+ return (
+ <div className="collectionStackingView" style={{ height: "100%" }}
+ ref={this.createRef} onWheel={(e: React.WheelEvent) => e.stopPropagation()}>
+ <div className={`collectionStackingView-masonry${this.singleColumn ? "Single" : "Grid"}`}
+ style={{
+ padding: `${topMargin}px 0px 0px ${leftMargin}px`,
+ width: this.singleColumn ? "100%" : `${cells * itemCols * (this.gridSize + this.gridGap) + leftMargin}`,
+ height: "100%",
+ overflow: "hidden",
+ marginRight: "auto",
+ position: "relative",
+ gridGap: this.gridGap,
+ gridTemplateColumns: this.singleColumn ? undefined : `repeat(auto-fill, minmax(${this.gridSize}px,1fr))`,
+ gridAutoRows: this.singleColumn ? undefined : `${this.gridSize}px`
+ }}
+ >
+ {this.singleColumn ? this.singleColumnChildren : this.children}
+ </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 864fdfa4b..be37efd3d 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -16,9 +16,8 @@ import { listSpec } from "../../../new_fields/Schema";
import { Cast, PromiseValue, FieldValue, ListSpec } from "../../../new_fields/Types";
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";
+import CursorField from "../../../new_fields/CursorField";
+import { DocumentManager } from "../../util/DocumentManager";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -72,7 +71,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.data.metadata.id === id)) > -1) {
cursors[ind].setPosition(pos);
} else {
- let entry = new CursorField({ metadata: { id: id, identifier: email }, position: pos });
+ let entry = new CursorField({ metadata: { id: id, identifier: email, timestamp: Date.now() }, position: pos });
cursors.push(entry);
}
}
@@ -131,7 +130,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
options.dropAction = "copy";
}
if (type.indexOf("html") !== -1) {
- if (path.includes('localhost')) {
+ if (path.includes(window.location.hostname)) {
let s = path.split('/');
let id = s[s.length - 1];
DocServer.GetRefField(id).then(field => {
@@ -155,6 +154,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
@action
protected onDrop(e: React.DragEvent, options: DocumentOptions): 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;
+ }
let html = e.dataTransfer.getData("text/html");
let text = e.dataTransfer.getData("text/plain");
@@ -164,6 +167,13 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
e.stopPropagation();
e.preventDefault();
+ if (html && html.indexOf(document.location.origin)) { // prosemirror text containing link to dash document
+ let start = html.indexOf(window.location.origin);
+ let path = html.substr(start, html.length - start);
+ let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.prepend("/doc/"), "").split("?")[0];
+ DocServer.GetRefField(docid).then(f => (f instanceof Doc) && this.props.addDocument(f, false));
+ return;
+ }
if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
let htmlDoc = Docs.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text });
this.props.addDocument(htmlDoc, false);
@@ -210,7 +220,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
let path = window.location.origin + file;
- let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 600, width: 300, title: dropFileName });
+ let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
docPromise.then(doc => doc && this.props.addDocument(doc));
}));
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 5f82137c6..458030b28 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -46,7 +46,14 @@
.docContainer:hover {
.treeViewItem-openRight {
- display: inline;
+ display: inline-block;
+ height:13px;
+ // display: inline;
+ svg {
+ display:block;
+ padding:0px;
+ margin: 0px;
+ }
}
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 72fa69cb1..48da52ffa 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -9,9 +9,9 @@ import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTreeView.scss";
import React = require("react");
import { Document, listSpec } from '../../../new_fields/Schema';
-import { Cast, StrCast, BoolCast, FieldValue } from '../../../new_fields/Types';
+import { Cast, StrCast, BoolCast, FieldValue, NumCast } from '../../../new_fields/Types';
import { Doc, DocListCast } from '../../../new_fields/Doc';
-import { Id } from '../../../new_fields/RefField';
+import { Id } from '../../../new_fields/FieldSymbols';
import { ContextMenu } from '../ContextMenu';
import { undoBatch } from '../../util/UndoManager';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
@@ -19,6 +19,7 @@ import { CollectionDockingView } from './CollectionDockingView';
import { DocumentManager } from '../../util/DocumentManager';
import { Docs } from '../../documents/Documents';
import { MainView } from '../MainView';
+import { CollectionViewType } from './CollectionBaseView';
export interface TreeViewProps {
@@ -26,6 +27,7 @@ export interface TreeViewProps {
deleteDoc: (doc: Doc) => void;
moveDocument: DragManager.MoveFunction;
dropAction: "alias" | "copy" | undefined;
+ addDocTab: (doc: Doc, where: string) => void;
}
export enum BulletType {
@@ -53,7 +55,7 @@ class TreeView extends React.Component<TreeViewProps> {
if (this.props.document.dockingConfig) {
MainView.Instance.openWorkspace(this.props.document);
} else {
- CollectionDockingView.Instance.AddRightSplit(this.props.document);
+ this.props.addDocTab(this.props.document, "openRight");
}
}
@@ -111,7 +113,7 @@ class TreeView extends React.Component<TreeViewProps> {
let editableView = (titleString: string) =>
(<EditableView
oneLine={!this._isOver ? true : false}
- display={"inline-block"}
+ display={"inline"}
contents={titleString}
height={36}
GetValue={() => StrCast(this.props.document.title)}
@@ -121,11 +123,11 @@ class TreeView extends React.Component<TreeViewProps> {
return true;
}}
/>);
- let dataDocs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []);
+ let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : [];
let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
<div className="treeViewItem-openRight" onPointerDown={this.onPointerDown} onClick={this.openRight}>
<FontAwesomeIcon icon="angle-right" size="lg" />
- <FontAwesomeIcon icon="angle-right" size="lg" />
+ {/* <FontAwesomeIcon icon="angle-right" size="lg" /> */}
</div>);
return (
<div className="docContainer" ref={reference} onPointerDown={onItemDown} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}
@@ -138,21 +140,19 @@ 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
+ 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
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,
- { title: this.props.document.title + ".kvp", width: 300, height: 300 }))
- });
- if (DocumentManager.Instance.getDocumentViews(this.props.document).length) {
- ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.KVPDocument(this.props.document, { width: 300, height: 300 }), "onRight"), icon: "layer-group" });
+ if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
+ ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab"), icon: "folder" });
+ ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight"), icon: "caret-square-right" });
+ if (DocumentManager.Instance.getDocumentViews(this.props.document).length) {
+ ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) });
+ }
+ ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
+ } else {
+ ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
}
- ContextMenu.Instance.addItem({
- description: "Delete", event: undoBatch(() => {
- this.props.deleteDoc(this.props.document);
- })
- });
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
e.stopPropagation();
}
@@ -180,7 +180,7 @@ class TreeView extends React.Component<TreeViewProps> {
{(key === "data") ? (null) :
<span className="collectionTreeView-keyHeader" style={{ display: "block", marginTop: "7px" }} key={key}>{key}</span>}
<div style={{ display: "block", marginTop: `${spacing}px` }}>
- {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
+ {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction, this.props.addDocTab)}
</div>
</ul >);
} else {
@@ -197,9 +197,9 @@ class TreeView extends React.Component<TreeViewProps> {
</li>
</div>;
}
- public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
+ public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void) {
return docs.filter(child => !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).map(child =>
- <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
+ <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} addDocTab={addDocTab} />);
}
}
@@ -213,11 +213,10 @@ 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
+ // 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.excludeFromLibrary) { // excludeFromLibrary means this is the user document
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)) });
+ ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.remove(this.props.Document)) });
}
}
render() {
@@ -225,7 +224,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
if (!this.childDocs) {
return (null);
}
- let childElements = TreeView.GetChildElements(this.childDocs, false, this.remove, this.props.moveDocument, dropAction);
+ let childElements = TreeView.GetChildElements(this.childDocs, false, this.remove, this.props.moveDocument, dropAction, this.props.addDocTab);
return (
<div id="body" className="collectionTreeView-dropTarget"
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
index db8b84832..9d2c23d3e 100644
--- a/src/client/views/collections/CollectionVideoView.scss
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -5,7 +5,7 @@
position: inherit;
top: 0;
left:0;
-
+ z-index: -1;
}
.collectionVideoView-time{
color : white;
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 9ab959f3c..7853544d5 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -1,4 +1,5 @@
import { action, observable, trace } from "mobx";
+import * as htmlToImage from "html-to-image";
import { observer } from "mobx-react";
import { ContextMenu } from "../ContextMenu";
import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView";
@@ -6,10 +7,14 @@ import React = require("react");
import "./CollectionVideoView.scss";
import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import { emptyFunction } from "../../../Utils";
-import { Id } from "../../../new_fields/RefField";
+import { emptyFunction, Utils } from "../../../Utils";
+import { Id } from "../../../new_fields/FieldSymbols";
import { VideoBox } from "../nodes/VideoBox";
-import { NumCast } from "../../../new_fields/Types";
+import { NumCast, Cast, StrCast } from "../../../new_fields/Types";
+import { VideoField } from "../../../new_fields/URLField";
+import { SearchBox } from "../SearchBox";
+import { DocServer } from "../../DocServer";
+import { Docs, DocUtils } from "../../documents/Documents";
@observer
@@ -67,6 +72,43 @@ export class CollectionVideoView 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
}
+
+ let field = Cast(this.props.Document[this.props.fieldKey], VideoField);
+ if (field) {
+ let url = field.url.href;
+ ContextMenu.Instance.addItem({
+ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt"
+ });
+ }
+ let width = NumCast(this.props.Document.width);
+ let height = NumCast(this.props.Document.height);
+ ContextMenu.Instance.addItem({
+ description: "Take Snapshot", event: async () => {
+ var canvas = document.createElement('canvas');
+ canvas.width = 640;
+ canvas.height = 640 * NumCast(this.props.Document.nativeHeight) / NumCast(this.props.Document.nativeWidth);
+ var ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
+ ctx && ctx.drawImage(this._videoBox!.player!, 0, 0, canvas.width, canvas.height);
+
+ //convert to desired file format
+ var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
+ // if you want to preview the captured image,
+
+ let filename = encodeURIComponent("snapshot" + this.props.Document.title + "_" + this.props.Document.curPage).replace(/\./g, "");
+ SearchBox.convertDataUri(dataUrl, filename).then((returnedFilename) => {
+ if (returnedFilename) {
+ let url = DocServer.prepend(returnedFilename);
+ let imageSummary = Docs.ImageDocument(url, {
+ x: NumCast(this.props.Document.x) + width, y: NumCast(this.props.Document.y),
+ width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-"
+ });
+ this.props.addDocument && this.props.addDocument(imageSummary, false);
+ DocUtils.MakeLink(imageSummary, this.props.Document);
+ }
+ });
+ },
+ icon: "expand-arrows-alt"
+ });
}
setVideoBox = (videoBox: VideoBox) => { this._videoBox = videoBox; };
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index b9ffc11a2..68eefab4c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,23 +1,27 @@
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faProjectDiagram, faSquare, faTh, faTree } from '@fortawesome/free-solid-svg-icons';
+import { faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
import { observer } from "mobx-react";
import * as React from 'react';
-import { Id } from '../../../new_fields/RefField';
+import { Id } from '../../../new_fields/FieldSymbols';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
import { undoBatch } from '../../util/UndoManager';
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 { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionSchemaView } from "./CollectionSchemaView";
+import { CollectionStackingView } from './CollectionStackingView';
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);
+library.add(faSignature);
+library.add(faThList);
@observer
export class CollectionView extends React.Component<FieldViewProps> {
@@ -29,6 +33,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
case CollectionViewType.Schema: return (<CollectionSchemaView {...props} CollectionView={this} />);
case CollectionViewType.Docking: return (<CollectionDockingView {...props} CollectionView={this} />);
case CollectionViewType.Tree: return (<CollectionTreeView {...props} CollectionView={this} />);
+ case CollectionViewType.Stacking: return (<CollectionStackingView {...props} CollectionView={this} />);
case CollectionViewType.Freeform:
default:
return (<CollectionFreeFormView {...props} CollectionView={this} />);
@@ -40,12 +45,15 @@ 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), icon: "project-diagram" });
+ let subItems: ContextMenuProps[] = [];
+ subItems.push({ description: "Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Freeform), icon: "signature" });
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" });
+ subItems.push({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema), icon: "th-list" });
+ subItems.push({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" });
+ subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "th-list" });
+ ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems });
}
}
diff --git a/src/client/views/collections/ParentDocumentSelector.scss b/src/client/views/collections/ParentDocumentSelector.scss
index f3c605f3e..2dd3e49f2 100644
--- a/src/client/views/collections/ParentDocumentSelector.scss
+++ b/src/client/views/collections/ParentDocumentSelector.scss
@@ -1,8 +1,22 @@
.PDS-flyout {
position: absolute;
z-index: 9999;
- background-color: #d3d3d3;
+ background-color: #eeeeee;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
min-width: 150px;
color: black;
+ top: 12px;
+
+ padding: 10px;
+ border-radius: 3px;
+
+ hr {
+ height: 1px;
+ margin: 0px;
+ background-color: gray;
+ border-top: 0px;
+ border-bottom: 0px;
+ border-right: 0px;
+ border-left: 0px;
+ }
} \ No newline at end of file
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 52f7914f3..f11af04a3 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -3,36 +3,64 @@ 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 { Id } from "../../../new_fields/FieldSymbols";
import { SearchUtil } from "../../util/SearchUtil";
import { CollectionDockingView } from "./CollectionDockingView";
+import { NumCast } from "../../../new_fields/Types";
+import { CollectionViewType } from "./CollectionBaseView";
+type SelectorProps = { Document: Doc, addDocTab(doc: Doc, location: string): void };
@observer
-export class SelectorContextMenu extends React.Component<{ Document: Doc }> {
- @observable private _docs: Doc[] = [];
+export class SelectorContextMenu extends React.Component<SelectorProps> {
+ @observable private _docs: { col: Doc, target: Doc }[] = [];
+ @observable private _otherDocs: { col: Doc, target: Doc }[] = [];
- constructor(props: { Document: Doc }) {
+ constructor(props: SelectorProps) {
super(props);
this.fetchDocuments();
}
async fetchDocuments() {
+ let aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document);
const docs = await SearchUtil.Search(`data_l:"${this.props.Document[Id]}"`, true);
- runInAction(() => this._docs = docs);
+ const map: Map<Doc, Doc> = new Map;
+ const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true)));
+ allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index])));
+ docs.forEach(doc => map.delete(doc));
+ runInAction(() => {
+ this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: this.props.Document }));
+ this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target }));
+ });
+ }
+
+ getOnClick({ col, target }: { col: Doc, target: Doc }) {
+ return () => {
+ col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col;
+ if (NumCast(col.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) {
+ const newPanX = NumCast(target.x) + NumCast(target.width) / NumCast(target.zoomBasis, 1) / 2;
+ const newPanY = NumCast(target.y) + NumCast(target.height) / NumCast(target.zoomBasis, 1) / 2;
+ col.panX = newPanX;
+ col.panY = newPanY;
+ }
+ this.props.addDocTab(col, "inTab");
+ };
}
render() {
return (
<>
- {this._docs.map(doc => <p><a onClick={() => CollectionDockingView.Instance.AddRightSplit(doc)}>{doc.title}</a></p>)}
+ <p>Contexts:</p>
+ {this._docs.map(doc => <p><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)}
+ {this._otherDocs.length ? <hr></hr> : null}
+ {this._otherDocs.map(doc => <p><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)}
</>
);
}
}
@observer
-export class ParentDocSelector extends React.Component<{ Document: Doc }> {
+export class ParentDocSelector extends React.Component<SelectorProps> {
@observable hover = false;
@action
@@ -49,13 +77,13 @@ export class ParentDocSelector extends React.Component<{ Document: Doc }> {
let flyout;
if (this.hover) {
flyout = (
- <div className="PDS-flyout">
- <SelectorContextMenu Document={this.props.Document} />
+ <div className="PDS-flyout" title=" ">
+ <SelectorContextMenu {...this.props} />
</div>
);
}
return (
- <span style={{ position: "relative", display: "inline-block" }}
+ <span style={{ position: "relative", display: "inline-block", paddingLeft: "5px", paddingRight: "5px" }}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}>
<p>^</p>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
index 737ffba7d..7a0fd2b31 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -10,3 +10,9 @@
transform: translate(10000px,10000px);
pointer-events: all;
}
+.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 63d2f7642..301b769af 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -40,18 +40,24 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
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 x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / NumCast(a.zoomBasis, 1) / 2);
+ let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / NumCast(a.zoomBasis, 1) / 2);
+ let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / NumCast(b.zoomBasis, 1) / 2);
+ let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / NumCast(b.zoomBasis, 1) / 2);
+ let text = "";
+ this.props.LinkDocs.map(l => text += StrCast(l.title) + "(" + StrCast(l.linkDescription) + "), ");
+ text = text.substr(0, text.length - 2);
return (
<>
- <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine"
- style={{ strokeWidth: `${l.length / 2}` }}
+ <line key="linkLine" className="collectionfreeformlinkview-linkLine"
+ style={{ strokeWidth: `${2 * l.length / 2}` }}
x1={`${x1}`} y1={`${y1}`}
x2={`${x2}`} y2={`${y2}`} />
- <circle key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkCircle"
- cx={(x1 + x2) / 2} cy={(y1 + y2) / 2} r={5} onPointerDown={this.onPointerDown} />
+ {/* <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>
</>
);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index d5ce4e1e7..a43c5f241 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -11,7 +11,7 @@ import { Doc, DocListCastAsync, DocListCast } from "../../../../new_fields/Doc";
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 { Id } from "../../../../new_fields/FieldSymbols";
@observer
export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
@@ -32,8 +32,8 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
let srcTarg = srcDoc;
let x1 = NumCast(srcDoc.x);
let x2 = NumCast(dstDoc.x);
- let x1w = NumCast(srcDoc.width, -1);
- let x2w = NumCast(dstDoc.width, -1);
+ let x1w = NumCast(srcDoc.width, -1) / NumCast(srcDoc.zoomBasis, 1);
+ let x2w = NumCast(dstDoc.width, -1) / NumCast(srcDoc.zoomBasis, 1);
if (x1w < 0 || x2w < 0 || i === j) { }
else {
let findBrush = (field: (Doc | Promise<Doc>)[]) => field.findIndex(brush => {
@@ -60,12 +60,12 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
}
};
}
+ if (dstTarg.brushingDocs === undefined) dstTarg.brushingDocs = new List<Doc>();
+ if (srcTarg.brushingDocs === undefined) srcTarg.brushingDocs = new List<Doc>();
let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc), []);
let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc), []);
- if (dstBrushDocs === undefined) dstTarg.brushingDocs = dstBrushDocs = new List<Doc>();
- else brushAction(dstBrushDocs);
- if (srcBrushDocs === undefined) srcTarg.brushingDocs = srcBrushDocs = new List<Doc>();
- else brushAction(srcBrushDocs);
+ brushAction(dstBrushDocs);
+ brushAction(srcBrushDocs);
}
});
});
@@ -100,21 +100,27 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
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 =>
- drawnPairs.reduce((found, drawnPair) => {
- let match = (possiblePair.a === drawnPair.a && possiblePair.b === drawnPair.b);
+ 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] })
- );
+ }, false)) {
+ console.log("A" + possiblePair.a[Id] + " B" + possiblePair.b[Id] + " L" + connection.l[Id]);
+ drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] })
+ }
+ });
return drawnPairs;
}, [] as { a: Doc, b: Doc, l: Doc[] }[]);
- return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l}
- removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />);
+ return connections.map(c => {
+ let x = c.l.reduce((p, l) => p + l[Id], "");
+ return <CollectionFreeFormLinkView key={x} A={c.a} B={c.b} LinkDocs={c.l}
+ removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />;
+ });
}
render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 642118d75..2838b7905 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -9,6 +9,7 @@ import CursorField from "../../../../new_fields/CursorField";
import { List } from "../../../../new_fields/List";
import { Cast } from "../../../../new_fields/Types";
import { listSpec } from "../../../../new_fields/Schema";
+import * as mobxUtils from 'mobx-utils';
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
@@ -23,7 +24,9 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
let cursors = Cast(doc.cursors, listSpec(CursorField));
- return (cursors || []).filter(cursor => cursor.data.metadata.id !== id);
+ const now = mobxUtils.now();
+ // const now = Date.now();
+ return (cursors || []).filter(cursor => cursor.data.metadata.id !== id && (now - cursor.data.metadata.timestamp) < 1000);
}
private crosshairs?: HTMLCanvasElement;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 063c9e2cf..e10ba9d7e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -25,6 +25,9 @@
height: 100%;
width: 100%;
}
+ >.jsx-parser {
+ z-index:0;
+ }
//nested freeform views
// .collectionfreeformview-container {
@@ -52,6 +55,10 @@
position: inherit;
height: 100%;
}
+
+ >.jsx-parser {
+ z-index:0;
+ }
.formattedTextBox-cont {
background: $light-color-secondary;
@@ -63,6 +70,7 @@
border-radius: $border-radius;
box-sizing: border-box;
position:absolute;
+ z-index: -1;
.marqueeView {
overflow: hidden;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9cb8443f4..9d19df540 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -22,9 +22,10 @@ import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Sc
import { Doc, WidthSym, HeightSym } from "../../../../new_fields/Doc";
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";
+import { Id } from "../../../../new_fields/FieldSymbols";
+import { DocServer } from "../../../DocServer";
export const panZoomSchema = createSchema({
panX: "number",
@@ -135,14 +136,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
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) / NumCast(docs[0].zoomBasis) + minx : minx;
+ let maxx = docs.length ? NumCast(docs[0].width) / NumCast(docs[0].zoomBasis, 1) + minx : minx;
let miny = docs.length ? NumCast(docs[0].y) : 0;
- let maxy = docs.length ? NumCast(docs[0].height) / NumCast(docs[0].zoomBasis) + miny : miny;
+ let maxy = docs.length ? NumCast(docs[0].height) / NumCast(docs[0].zoomBasis, 1) + miny : miny;
let ranges = docs.filter(doc => doc).reduce((range, doc) => {
let x = NumCast(doc.x);
- let xe = x + NumCast(doc.width) / NumCast(doc.zoomBasis);
+ let xe = x + NumCast(doc.width) / NumCast(doc.zoomBasis, 1);
let y = NumCast(doc.y);
- let ye = y + NumCast(doc.height) / NumCast(doc.zoomBasis);
+ let ye = y + NumCast(doc.height) / NumCast(doc.zoomBasis, 1);
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]]);
@@ -155,12 +156,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
});
}
- let panelwidth = this._pwidth / this.zoomScaling() / 2;
- let panelheight = this._pheight / this.zoomScaling() / 2;
- if (x - dx < ranges[0][0] - panelwidth) x = ranges[0][1] + panelwidth + dx;
- if (x - dx > ranges[0][1] + panelwidth) x = ranges[0][0] - panelwidth + dx;
- if (y - dy < ranges[1][0] - panelheight) y = ranges[1][1] + panelheight + dy;
- if (y - dy > ranges[1][1] + panelheight) y = ranges[1][0] - panelheight + dy;
+ let panelDim = this.props.ScreenToLocalTransform().transformDirection(this._pwidth / this.zoomScaling(),
+ this._pheight / this.zoomScaling());
+ let panelwidth = panelDim[0];
+ let panelheight = panelDim[1];
+ if (ranges[0][0] - dx > (this.panX() + panelwidth / 2)) x = ranges[0][1] + panelwidth / 2;
+ if (ranges[0][1] - dx < (this.panX() - panelwidth / 2)) x = ranges[0][0] - panelwidth / 2;
+ if (ranges[1][0] - dy > (this.panY() + panelheight / 2)) y = ranges[1][1] + panelheight / 2;
+ if (ranges[1][1] - dy < (this.panY() - panelheight / 2)) y = ranges[1][0] - panelheight / 2;
}
this.setPan(x - dx, y - dy);
this._lastX = e.pageX;
@@ -215,7 +218,6 @@ 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));
@@ -227,7 +229,24 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onDrop = (e: React.DragEvent): void => {
var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
- super.onDrop(e, { x: pt[0], y: pt[1] });
+ let html = e.dataTransfer.getData("text/html");
+ if (html && html.indexOf(document.location.origin)) { // prosemirror text containing link to dash document
+ e.stopPropagation();
+ e.preventDefault();
+ let start = html.indexOf(window.location.origin);
+ let path = html.substr(start, html.length - start);
+ let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.prepend("/doc/"), "").split("?")[0];
+ DocServer.GetRefField(docid).then(f => {
+ if (f instanceof Doc) {
+ f.x = pt[0];
+ f.y = pt[1];
+ (f instanceof Doc) && this.props.addDocument(f, false);
+ }
+ });
+ return;
+ } else {
+ super.onDrop(e, { x: pt[0], y: pt[1] });
+ }
}
onDragOver = (): void => {
@@ -243,7 +262,6 @@ 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;
@@ -265,22 +283,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
}
SelectionManager.DeselectAll();
- const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
- const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
+ const newPanX = NumCast(doc.x) + NumCast(doc.width) / NumCast(doc.zoomBasis, 1) / 2;
+ const newPanY = NumCast(doc.y) + NumCast(doc.height) / NumCast(doc.zoomBasis, 1) / 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.props.focus(this.props.Document);
- this.panDisposer = setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false
}
getDocumentViewProps(document: Doc): DocumentViewProps {
return {
Document: document,
- toggleMinimized: emptyFunction,
addDocument: this.props.addDocument,
removeDocument: this.props.removeDocument,
moveDocument: this.props.moveDocument,
@@ -305,7 +321,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let docviews = this.childDocs.reduce((prev, doc) => {
if (!(doc instanceof Doc)) return prev;
var page = NumCast(doc.page, -1);
- if (page === curPage || page === -1) {
+ if (Math.round(page) === Math.round(curPage) || page === -1) {
let minim = BoolCast(doc.isMinimized, false);
if (minim === undefined || !minim) {
prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getDocumentViewProps(doc)} />);
@@ -324,7 +340,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
}
- private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />];
+ private childViews = () => [
+ <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
+ ...this.views
+ ];
render() {
const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
const easing = () => this.props.Document.panTransformType === "Ease";
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 4587c2227..29734fa19 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,5 +1,5 @@
import * as htmlToImage from "html-to-image";
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace } from "mobx";
import { observer } from "mobx-react";
import { Docs } from "../../../documents/Documents";
import { SelectionManager } from "../../../util/SelectionManager";
@@ -17,9 +17,11 @@ 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 { SearchBox } from "../../SearchBox";
import { DocServer } from "../../../DocServer";
-import { Id } from "../../../../new_fields/RefField";
+import { Id } from "../../../../new_fields/FieldSymbols";
+import { CollectionView } from "../CollectionView";
+import { CollectionViewType } from "../CollectionBaseView";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -83,49 +85,62 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
});
})();
} 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
- // assumes that 1st row has header entry for each column
- // assumes subsequent rows have entries for each column header OR
- // any row that has only one column is a section header-- this header is then added as a column to subsequent rows until the next header
- // assumes each cell is a string or a number
e.preventDefault();
- (async () => {
- let text: string = await navigator.clipboard.readText();
+ navigator.clipboard.readText().then(text => {
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[] = [];
- let groupAttr: string | number = "";
- for (let i = 1; i < ns.length - 1; i++) {
- let values = ns[i].split("\t");
- if (values.length === 1 && columns.length > 1) {
- groupAttr = values[0];
- continue;
- }
- let doc = new Doc();
- 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.title = i.toString();
- docList.push(doc);
- }
- 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);
+ if (ns.length === 1 && text.startsWith("http")) {
+ this.props.addDocument(Docs.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }), false);// paste an image from its URL in the paste buffer
+ } else {
+ this.pasteTable(ns, x, y);
}
- })();
+ });
} else {
let newBox = Docs.TextDocument({ width: 200, height: 100, x: x, y: y, title: "-typed text-" });
this.props.addLiveTextDocument(newBox);
}
e.stopPropagation();
}
+ //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
+ // assumes that 1st row has header entry for each column
+ // assumes subsequent rows have entries for each column header OR
+ // any row that has only one column is a section header-- this header is then added as a column to subsequent rows until the next header
+ // assumes each cell is a string or a number
+ pasteTable(ns: string[], x: number, y: number) {
+ 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[] = [];
+ let groupAttr: string | number = "";
+ let rowProto = new Doc();
+ rowProto.title = rowProto.Id;
+ rowProto.width = 200;
+ rowProto.isPrototype = true;
+ for (let i = 1; i < ns.length - 1; i++) {
+ let values = ns[i].split("\t");
+ if (values.length === 1 && columns.length > 1) {
+ groupAttr = values[0];
+ continue;
+ }
+ let docDataProto = Doc.MakeDelegate(rowProto);
+ docDataProto.isPrototype = true;
+ columns.forEach((col, i) => docDataProto[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
+ if (groupAttr) {
+ docDataProto._group = groupAttr;
+ }
+ docDataProto.title = i.toString();
+ let doc = Doc.MakeDelegate(docDataProto);
+ doc.width = 200;
+ docList.push(doc);
+ }
+ 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);
+ }
+ }
@action
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
@@ -138,7 +153,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
document.addEventListener("pointerup", this.onPointerUp, true);
document.addEventListener("keydown", this.marqueeCommand, true);
if (e.altKey) {
- e.stopPropagation();
+ //e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
e.preventDefault();
}
// bcz: do we need this? it kills the context menu on the main collection if !altKey
@@ -225,9 +240,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this.cleanupInteractions(false);
e.stopPropagation();
}
- if (e.key === "c" || e.key === "s" || e.key === "e" || e.key === "p") {
+ if (e.key === "c" || e.key === "s" || e.key === "S" || e.key === "e" || e.key === "p") {
this._commandExecuted = true;
e.stopPropagation();
+ e.preventDefault();
(e as any).propagationIsStopped = true;
let bounds = this.Bounds;
let selected = this.marqueeSelect();
@@ -254,13 +270,32 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
width: bounds.width * zoomBasis,
height: bounds.height * zoomBasis,
ink: inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined,
- title: e.key === "s" ? "-summary-" : e.key === "p" ? "-summary-" : "a nested collection",
+ title: e.key === "s" || e.key === "S" ? "-summary-" : e.key === "p" ? "-summary-" : "a nested collection",
});
+ newCollection.zoomBasis = zoomBasis;
this.marqueeInkDelete(inkData);
- if (e.key === "s" || e.key === "p") {
-
- htmlToImage.toPng(this._mainCont.current!, { width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, quality: 1 }).then((dataUrl) => {
+ if (e.key === "s") {
+ 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: "#e2ad32" /* yellow */, title: "-summary-" });
+ newCollection.proto!.summaryDoc = summary;
+ selected = [newCollection];
+ newCollection.x = bounds.left + bounds.width;
+ summary.proto!.subBulletDocs = new List<Doc>(selected);
+ //summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight"
+ summary.templates = new List<string>([Templates.Bullet.Layout]);
+ let container = Docs.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, title: "-summary-" });
+ container.viewType = CollectionViewType.Stacking;
+ this.props.addLiveTextDocument(container);
+ // });
+ } else if (e.key === "S") {
+ await htmlToImage.toPng(this._mainCont.current!, { width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, quality: 0.2 }).then((dataUrl) => {
selected.map(d => {
this.props.removeDocument(d);
d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
@@ -268,15 +303,25 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
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")]);
+ let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+ SearchBox.convertDataUri(dataUrl, "icon" + summary[Id] + "_image").then((returnedFilename) => {
+ if (returnedFilename) {
+ let url = DocServer.prepend(returnedFilename);
+ let imageSummary = Docs.ImageDocument(url, {
+ x: bounds.left, y: bounds.top + 100 / zoomBasis,
+ width: 150, height: bounds.height / bounds.width * 150, title: "-summary image-"
+ });
+ summary.imageSummary = imageSummary;
+ this.props.addDocument(imageSummary, false);
+ }
+ })
newCollection.proto!.summaryDoc = summary;
selected = [newCollection];
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);
});
}
@@ -347,10 +392,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
render() {
- let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
+ 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 ? null : this.marqueeDiv}
+ {this._visible ? this.marqueeDiv : null}
<div ref={this._mainCont} style={{ transform: `translate(${-p[0]}px, ${-p[1]}px)` }} >
{this.props.children}
</div>