aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts11
-rw-r--r--src/client/util/DragManager.ts4
-rw-r--r--src/client/util/TooltipTextMenu.tsx2
-rw-r--r--src/client/views/ContextMenu.scss2
-rw-r--r--src/client/views/ContextMenu.tsx7
-rw-r--r--src/client/views/ContextMenuItem.tsx34
-rw-r--r--src/client/views/DocumentDecorations.tsx2
-rw-r--r--src/client/views/InkingControl.tsx4
-rw-r--r--src/client/views/MainOverlayTextBox.tsx7
-rw-r--r--src/client/views/MainView.tsx7
-rw-r--r--src/client/views/Templates.tsx4
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx1
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx7
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss1
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx25
-rw-r--r--src/client/views/collections/CollectionStackingView.scss5
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx128
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.scss6
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx49
-rw-r--r--src/client/views/collections/CollectionView.tsx17
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx17
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx149
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx1
-rw-r--r--src/client/views/nodes/DocumentView.tsx187
-rw-r--r--src/client/views/nodes/FieldView.tsx1
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx47
-rw-r--r--src/client/views/nodes/ImageBox.tsx26
-rw-r--r--src/server/authentication/models/current_user_utils.ts20
31 files changed, 396 insertions, 380 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4bc0df31f..ab61b915c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -26,7 +26,7 @@ import { OmitKeys } from "../../Utils";
import { ImageField, VideoField, AudioField, PdfField, WebField } from "../../new_fields/URLField";
import { HtmlField } from "../../new_fields/HtmlField";
import { List } from "../../new_fields/List";
-import { Cast } from "../../new_fields/Types";
+import { Cast, NumCast } from "../../new_fields/Types";
import { IconField } from "../../new_fields/IconField";
import { listSpec } from "../../new_fields/Schema";
import { DocServer } from "../DocServer";
@@ -36,6 +36,7 @@ import { DateField } from "../../new_fields/DateField";
import { UndoManager } from "../util/UndoManager";
import { RouteStore } from "../../server/RouteStore";
var requestImageSize = require('request-image-size');
+var path = require('path');
export interface DocumentOptions {
x?: number;
@@ -218,13 +219,15 @@ export namespace Docs {
}
export function ImageDocument(url: string, options: DocumentOptions = {}) {
- let inst = CreateInstance(imageProto, new ImageField(new URL(url)), options);
+ let inst = CreateInstance(imageProto, new ImageField(new URL(url)), { title: path.basename(url), ...options });
requestImageSize(window.origin + RouteStore.corsProxy + "/" + url)
.then((size: any) => {
+ let aspect = size.height / size.width;
if (!inst.proto!.nativeWidth) {
inst.proto!.nativeWidth = size.width;
}
- inst.proto!.nativeHeight = Number(inst.proto!.nativeWidth!) * size.height / size.width;
+ inst.proto!.nativeHeight = Number(inst.proto!.nativeWidth!) * aspect;
+ inst.proto!.height = NumCast(inst.proto!.width) * aspect;
})
.catch((err: any) => console.log(err));
return inst;
@@ -292,7 +295,7 @@ export namespace Docs {
return CreateInstance(webProto, new HtmlField(html), options);
}
export function KVPDocument(document: Doc, options: DocumentOptions = {}) {
- return CreateInstance(kvpProto, document, options);
+ return CreateInstance(kvpProto, document, { title: document.title + ".kvp", ...options });
}
export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, makePrototype: boolean = true) {
if (!makePrototype) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 7f75a95f0..8f0cce095 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -26,7 +26,7 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
// if (this.props.isSelected() || this.props.isTopMost) {
if (e.button === 0) {
e.stopPropagation();
- if (e.shiftKey) {
+ if (e.shiftKey && CollectionDockingView.Instance) {
CollectionDockingView.Instance.StartOtherDrag([await docFunc()], e);
} else {
document.addEventListener("pointermove", onRowMove);
@@ -264,7 +264,7 @@ export namespace DragManager {
if (dragData instanceof DocumentDragData) {
dragData.userDropAction = e.ctrlKey || e.altKey ? "alias" : undefined;
}
- if (e.shiftKey) {
+ if (e.shiftKey && CollectionDockingView.Instance) {
AbortDrag();
CollectionDockingView.Instance.StartOtherDrag(docs, {
pageX: e.pageX,
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index a1f80120f..5dd10f1bf 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -194,7 +194,7 @@ export class TooltipTextMenu {
if (DocumentManager.Instance.getDocumentView(f)) {
DocumentManager.Instance.getDocumentView(f)!.props.focus(f);
}
- else CollectionDockingView.Instance.AddRightSplit(f);
+ else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f);
}
}));
}
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index 61ae69179..74acba615 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -22,7 +22,7 @@
color: $light-color;
}
-.subMenu-cont {
+.contextMenu-subMenu-cont {
position: absolute;
display: flex;
z-index: 1000;
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 542d259d6..da374455e 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -81,6 +81,11 @@ export class ContextMenu extends React.Component {
return false;
}
+ @action
+ closeMenu = () => {
+ this.clearItems();
+ }
+
render() {
let style = this._yRelativeToTop ? { left: this._pageX, top: this._pageY, display: this._display } :
{ left: this._pageX, bottom: this._pageY, display: this._display };
@@ -95,7 +100,7 @@ export class ContextMenu extends React.Component {
<input className="contextMenu-item contextMenu-description" type="text" placeholder="Search . . ." value={this._searchString} onChange={this.onChange} />
</span>
{this._items.filter(prop => prop.description.toLowerCase().indexOf(this._searchString.toLowerCase()) !== -1).
- map(prop => <ContextMenuItem {...prop} key={prop.description} />)}
+ map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.closeMenu} />)}
</div>
);
}
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index b9239dac9..fcda0db89 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -8,11 +8,13 @@ export interface OriginalMenuProps {
description: string;
event: (e: React.MouseEvent<HTMLDivElement>) => void;
icon?: IconProp; //maybe should be optional (icon?)
+ closeMenu?: () => void;
}
export interface SubmenuProps {
description: string;
subitems: ContextMenuProps[];
+ closeMenu?: () => void;
}
export interface ContextMenuItemProps {
@@ -32,30 +34,36 @@ export class ContextMenuItem extends React.Component<ContextMenuProps> {
}
}
+ handleEvent = (e: React.MouseEvent<HTMLDivElement>) => {
+ if ("event" in this.props) {
+ this.props.event(e);
+ this.props.closeMenu && this.props.closeMenu();
+ }
+ }
+
render() {
if ("event" in this.props) {
return (
- <div className="contextMenu-item" onClick={this.props.event}>
+ <div className="contextMenu-item" onClick={this.handleEvent}>
<span className="icon-background">
{this.props.icon ? <FontAwesomeIcon icon={this.props.icon} size="sm" /> : <FontAwesomeIcon icon="circle" size="sm" />}
</span>
- <div className="contextMenu-description"> {this.props.description}</div>
+ <div className="contextMenu-description">
+ {this.props.description}
+ </div>
</div>
);
}
else {
- let submenu = null;
- if (this.overItem) {
- submenu = (
- <div className="subMenu-cont" style={{ marginLeft: "100.5%", left: "0px" }}>
- {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} />)}
- </div>
- );
- }
+ let submenu = !this.overItem ? (null) :
+ <div className="contextMenu-subMenu-cont" style={{ marginLeft: "100.5%", left: "0px" }}>
+ {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
+ </div>;
return (
- <div className="contextMenu-item" onMouseEnter={action(() => { this.overItem = true; })}
- onMouseLeave={action(() => this.overItem = false)}>
- <div className="contextMenu-description"> {this.props.description}</div>
+ <div className="contextMenu-item" onMouseEnter={action(() => { this.overItem = true; })} onMouseLeave={action(() => this.overItem = false)}>
+ <div className="contextMenu-description">
+ {this.props.description}
+ </div>
{submenu}
</div>
);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 319e91337..e06fd6119 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -251,7 +251,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
if (!this._removeIcon) {
if (selectedDocs.length === 1) {
- this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].props.toggleMinimized());
+ this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].toggleMinimized());
} else if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD &&
Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) {
let docViews = SelectionManager.ViewsSortedVertically();
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 17d4a1e49..d456f531f 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -35,9 +35,7 @@ export class InkingControl extends React.Component {
@action
switchColor = (color: ColorResult): void => {
this._selectedColor = color.hex;
- SelectionManager.SelectedDocuments().forEach(doc =>
- doc.props.ContainingCollectionView && Doc.SetOnPrototype(doc.props.Document, "backgroundColor", color.hex)
- );
+ SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = color.hex);
}
@action
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 9be408049..6e8e6f8ce 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -17,6 +17,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
@observable _textXf: () => Transform = () => Transform.Identity();
private _textFieldKey: string = "data";
private _textColor: string | null = null;
+ private _textHideOnLeave?: boolean;
private _textTargetDiv: HTMLDivElement | undefined;
private _textProxyDiv: React.RefObject<HTMLDivElement>;
@@ -39,9 +40,9 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
this._textFieldKey = textFieldKey!;
this._textXf = tx ? tx : () => Transform.Identity();
this._textTargetDiv = div;
+ this._textHideOnLeave = FormattedTextBox.InputBoxOverlay && FormattedTextBox.InputBoxOverlay.props.hideOnLeave;
if (div) {
- if (div.parentElement && div.parentElement instanceof HTMLDivElement && div.parentElement.id === "screenSpace") this._textXf = () => Transform.Identity();
- this._textColor = div.style.color;
+ this._textColor = (getComputedStyle(div) as any).color;
div.style.color = "transparent";
}
}
@@ -88,7 +89,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${textRect.left}px, ${textRect.top}px) scale(${1 / s},${1 / s})`, width: "auto", height: "auto" }} >
<div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
style={{ width: `${textRect.width * s}px`, height: `${textRect.height * s}px` }}>
- <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={FormattedTextBox.InputBoxOverlay.props.Document} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
+ <FormattedTextBox fieldKey={this._textFieldKey} hideOnLeave={this._textHideOnLeave} isOverlay={true} Document={FormattedTextBox.InputBoxOverlay.props.Document} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={emptyFunction} />
</div>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 438607157..a093ffdec 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -104,7 +104,9 @@ export class MainView extends React.Component {
}, false); // drag event handler
// click interactions for the context menu
document.addEventListener("pointerdown", action(function (e: PointerEvent) {
- if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
+
+ const targets = document.elementsFromPoint(e.x, e.y);
+ if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) {
ContextMenu.Instance.clearItems();
}
}), true);
@@ -163,7 +165,7 @@ export class MainView extends React.Component {
}
openNotifsCol = () => {
- if (this._notifsCol) {
+ if (this._notifsCol && CollectionDockingView.Instance) {
CollectionDockingView.Instance.AddRightSplit(this._notifsCol);
}
}
@@ -184,7 +186,6 @@ export class MainView extends React.Component {
let mainCont = this.mainContainer;
let content = !mainCont ? (null) :
<DocumentView Document={mainCont}
- toggleMinimized={emptyFunction}
addDocument={undefined}
addDocTab={emptyFunction}
removeDocument={undefined}
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 303f3f3b8..0cd367bcb 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -42,8 +42,8 @@ export namespace Templates {
export const Caption = new Template("Caption", TemplatePosition.OutterBottom,
`<div>
<div style="height:100%; width:100%;position:absolute;">{layout}</div>
- <div id="screenSpace" style="top: 100%; font-size:14px; background:yellow; width:100%; position:absolute">
- <FormattedTextBox {...props} fieldKey={"caption"} />
+ <div style="bottom: 0; font-size:14px; width:100%; position:absolute">
+ <FormattedTextBox {...props} fieldKey={"caption"} hideOnLeave={"true"} />
</div>
</div>` );
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 2bccde241..e89b8be0f 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -128,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 180a8be46..dcc1bd95d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -22,6 +22,7 @@ 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> {
@@ -415,7 +416,10 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@observable private _panelHeight = 0;
@observable private _document: Opt<Doc>;
get _stack(): any {
- return (this.props as any).glContainer.parent.parent;
+ 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);
@@ -466,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/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 1af13765b..10db6e0bb 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -64,6 +64,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@observable _columnsPercentage = 0;
@observable _keys: string[] = [];
@observable _newKeyName: string = "";
+ @observable previewScript: string = "";
@computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
@computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; }
@@ -346,7 +347,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@computed
get previewPanel() {
trace();
- return !this.previewDocument ? (null) : <CollectionSchemaPreview
+ return <CollectionSchemaPreview
Document={this.previewDocument}
width={this.previewWidth}
height={this.previewHeight}
@@ -357,8 +358,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
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() {
trace();
@@ -374,7 +381,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
}
interface CollectionSchemaPreviewProps {
- Document: Doc;
+ Document?: Doc;
width: () => number;
height: () => number;
CollectionView: CollectionView | CollectionPDFView | CollectionVideoView;
@@ -384,13 +391,14 @@ interface CollectionSchemaPreviewProps {
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
addDocTab: (document: Doc, where: string) => void;
+ setPreviewScript: (script: string) => void;
+ previewScript: string;
}
@observer
class CollectionSchemaPreview extends React.Component<CollectionSchemaPreviewProps>{
- @observable previewScript: string = "";
- 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 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()) {
@@ -404,15 +412,15 @@ class CollectionSchemaPreview extends React.Component<CollectionSchemaPreviewPro
get centeringOffset() { return (this.props.width() - this.nativeWidth * this.contentScaling()) / 2; }
@action
onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- this.previewScript = e.currentTarget.value;
+ this.props.setPreviewScript(e.currentTarget.value);
}
render() {
trace();
+ console.log(this.props.Document);
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}
- toggleMinimized={emptyFunction}
addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
ScreenToLocalTransform={this.getTransform}
ContentScaling={this.contentScaling}
@@ -423,9 +431,10 @@ class CollectionSchemaPreview extends React.Component<CollectionSchemaPreviewPro
whenActiveChanged={this.props.whenActiveChanged}
bringToFront={emptyFunction}
addDocTab={this.props.addDocTab}
+ toggleMinimized={emptyFunction}
/>
</div>)}
- <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
+ <input className="collectionSchemaView-input" value={this.props.previewScript} onChange={this.onPreviewScriptChange}
style={{ left: `calc(50% - ${Math.min(75, (this.props.Document ? this.PanelWidth() / 2 : 75))}px)` }} />
</div>);
}
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 6043a813d..1bb0b2674 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -8,8 +8,7 @@
width: 100%;
height: 100%;
position: absolute;
- overflow-y: scroll;
- min-width: 250px;
+ overflow-y: auto;
border-width: 0;
box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw;
border-color: $light-color-secondary;
@@ -31,7 +30,7 @@
align-items: center;
}
- .collectionStackingview-masonryGrid {
+ .collectionStackingView-masonryGrid {
display:grid;
width:100%;
height:100%;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a29648d5b..bf246d4ec 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,9 +4,9 @@ import { CollectionSubView, CollectionViewProps, SubCollectionViewProps } from "
import { Doc, WidthSym, HeightSym, DocListCast } from "../../../new_fields/Doc";
import { DocumentView } from "../nodes/DocumentView";
import { Transform } from "../../util/Transform";
-import { emptyFunction, returnOne } from "../../../Utils";
+import { emptyFunction, returnOne, Utils } from "../../../Utils";
import "./CollectionStackingView.scss";
-import { action, reaction } from "mobx";
+import { action, reaction, trace, computed } from "mobx";
import { StrCast, NumCast } from "../../../new_fields/Types";
import { Id } from "../../../new_fields/FieldSymbols";
@@ -14,21 +14,10 @@ import { Id } from "../../../new_fields/FieldSymbols";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
- getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform();
-
- constructor(props: SubCollectionViewProps) {
- super(props);
- // reaction(() => [this.props.PanelHeight() + this.props.PanelWidth(),
- // (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document[this.props.ContainingCollectionView.props.fieldKey])], () => {
- // if (this.props.ContainingCollectionView) {
- // let allItems = DocListCast(this.props.ContainingCollectionView.props.Document[this.props.ContainingCollectionView.props.fieldKey]);
- // for (let x = 0; x < allItems.length; x++) {
- // resizeGridItem(allItems[x]);
- // }
- // }
- // }
- // );
- }
+ _masonryGridRef: HTMLDivElement | null = null;
+ get gridGap() { return 10; }
+ get gridSize() { return 20; }
+ get itemWidth() { return NumCast(this.props.Document.itemWidth, 250); }
@action
moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => {
@@ -36,57 +25,76 @@ export class CollectionStackingView extends CollectionSubView(doc => 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.itemWidth);
+ }
+ createRef = (ele: HTMLDivElement | null) => {
+ this._masonryGridRef = ele;
+ this.createDropTarget(ele!);
+ }
+ @computed
+ get children() {
+ return this.childDocs.map(d => {
+ let colSpan = Math.ceil((this.itemWidth + this.gridGap) / (this.gridSize + this.gridGap));
+ let rowSpan = Math.ceil((this.itemWidth / d[WidthSym]() * d[HeightSym]() + this.gridGap) / (this.gridSize + this.gridGap));
+ let dref = React.createRef<HTMLDivElement>();
+ let dxf = () => this.getDocTransform(d, dref.current!);
+ 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="colletionStackingView-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.itemWidth / NumCast(d.nativeWidth, d[WidthSym]())}, ${this.itemWidth / 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} />
+ </div>);
+ })
+ }
render() {
- const docs = this.childDocs;
- let gridGap = 10;
- let gridSize = 20;
- let itemWidth = NumCast(this.props.Document.itemWidth, 250);
let leftMargin = 20;
let topMargin = 20;
- let itemCols = Math.ceil(itemWidth / (gridSize + gridGap));
- let cells = Math.floor((this.props.PanelWidth() - leftMargin) / (itemCols * (gridSize + gridGap)));
+ let itemCols = Math.ceil(this.itemWidth / (this.gridSize + this.gridGap));
+ let cells = Math.floor((this.props.PanelWidth() - leftMargin) / (itemCols * (this.gridSize + this.gridGap)));
return (
- <div className="collectionStackingView" ref={this.createDropTarget} onWheel={(e: React.WheelEvent) => e.stopPropagation()}>
- <div className="collectionStackingview-masonryGrid"
+ <div className="collectionStackingView" ref={this.createRef} onWheel={(e: React.WheelEvent) => e.stopPropagation()}>
+ <div className="collectionStackingView-masonryGrid"
style={{
padding: `${topMargin}px 0px 0px ${leftMargin}px`,
- width: `${cells * itemCols * (gridSize + gridGap) + leftMargin}`,
- margin: "auto", position: "relative",
- gridGap: gridGap,
- gridTemplateColumns: `repeat(auto-fill, minmax(${gridSize}px,1fr))`,
- gridAutoRows: `${gridSize}px`
+ width: `${cells * itemCols * (this.gridSize + this.gridGap) + leftMargin}`,
+ height: "auto",
+ marginLeft: "auto", marginRight: "auto", position: "relative",
+ gridGap: this.gridGap,
+ gridTemplateColumns: `repeat(auto-fill, minmax(${this.gridSize}px,1fr))`,
+ gridAutoRows: `${this.gridSize}px`
}}
>
- {docs.map(d => {
- let colSpan = Math.ceil((itemWidth + gridGap) / (gridSize + gridGap));
- let rowSpan = Math.ceil((itemWidth / d[WidthSym]() * d[HeightSym]() + gridGap) / (gridSize + gridGap));
- return (<div className="mycontent" id={StrCast(d.title, "")}
- key={d[Id]}
- style={{
- transformOrigin: "top left",
- gridRowEnd: `span ${rowSpan}`,
- gridColumnEnd: `span ${colSpan}`,
- transform: `scale(${itemWidth / NumCast(d.nativeWidth, 1)}, ${itemWidth / NumCast(d.nativeWidth, 1)})`
- }} >
- <DocumentView Document={d}
- addDocument={this.props.addDocument}
- removeDocument={this.props.removeDocument}
- moveDocument={this.moveDocument}
- ContainingCollectionView={this.props.CollectionView}
- isTopMost={false}
- ScreenToLocalTransform={this.getPreviewTransform}
- focus={emptyFunction}
- ContentScaling={returnOne}
- PanelWidth={d[WidthSym]}
- PanelHeight={d[HeightSym]}
- selectOnLoad={false}
- parentActive={this.props.active}
- addDocTab={this.props.addDocTab}
- bringToFront={emptyFunction}
- toggleMinimized={emptyFunction}
- whenActiveChanged={this.props.active} />
- </div>);
- })}
+ {this.children}
</div>
</div>
);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index c4e72b23a..fe9e12640 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -212,7 +212,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 2f0329fc4..bb3be0a73 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -48,7 +48,11 @@
.treeViewItem-openRight {
display: inline-block;
// display: inline;
- transform: translate(0px, -3px); //TODO Fix this
+ svg {
+ display:block;
+ padding:0px;
+ margin: 0px;
+ }
}
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8ad495762..2814c0502 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -9,7 +9,7 @@ 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/FieldSymbols';
import { ContextMenu } from '../ContextMenu';
@@ -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");
}
}
@@ -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/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 39c8af4ab..68eefab4c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,18 +1,19 @@
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faProjectDiagram, faSquare, faTh, faTree, faSignature, faThList } 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/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 { CollectionSchemaView } from "./CollectionSchemaView";
-import { CollectionTreeView } from "./CollectionTreeView";
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+import { CollectionSchemaView } from "./CollectionSchemaView";
import { CollectionStackingView } from './CollectionStackingView';
+import { CollectionTreeView } from "./CollectionTreeView";
export const COLLECTION_BORDER_WIDTH = 2;
library.add(faTh);
@@ -44,13 +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: "signature" });
+ 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: "th-list" });
- ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" });
- ContextMenu.Instance.addItem({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "th-list" });
+ 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/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 85556fb90..a43c5f241 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -100,17 +100,20 @@ 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 => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d1c4fb72d..1a1614ac7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -279,7 +279,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getDocumentViewProps(document: Doc): DocumentViewProps {
return {
Document: document,
- toggleMinimized: emptyFunction,
addDocument: this.props.addDocument,
removeDocument: this.props.removeDocument,
moveDocument: this.props.moveDocument,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 64d5301c9..14dbe1899 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -87,7 +87,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
navigator.clipboard.readText().then(text => {
let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
if (ns.length === 1 && text.startsWith("http")) {
- this.props.addDocument(Docs.ImageDocument(text, { width: 300, x: x, y: y }), false);// paste an image from its URL in the paste buffer
+ 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);
}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 66d91f9c2..411e8c059 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -30,10 +30,6 @@ const FreeformDocument = makeInterface(schema, positionSchema);
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
private _mainCont = React.createRef<HTMLDivElement>();
- private _downX: number = 0;
- private _downY: number = 0;
- private _doubleTap = false;
- private _lastTap: number = 0;
_bringToFrontDisposer?: IReactionDisposer;
@computed get transform() {
@@ -63,7 +59,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
panelWidth = () => this.props.PanelWidth();
panelHeight = () => this.props.PanelHeight();
- toggleMinimized = async () => this.toggleIcon(await DocListCastAsync(this.props.Document.maximizedDocs));
getTransform = (): Transform => this.props.ScreenToLocalTransform()
.translate(-this.X, -this.Y)
.scale(1 / this.contentScaling()).scale(1 / this.zoom)
@@ -71,7 +66,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@computed
get docView() {
return <DocumentView {...OmitKeys(this.props, ['zoomFade']).omit}
- toggleMinimized={this.toggleMinimized}
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
PanelWidth={this.panelWidth}
@@ -125,139 +119,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
},
2);
}
- @action
- public toggleIcon = async (maximizedDocs: Doc[] | undefined): Promise<void> => {
- SelectionManager.DeselectAll();
- let isMinimized: boolean | undefined;
- let minimizedDoc: Doc | undefined = this.props.Document;
- if (!maximizedDocs) {
- minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
- if (minimizedDoc) maximizedDocs = await DocListCastAsync(minimizedDoc.maximizedDocs);
- }
- if (minimizedDoc && maximizedDocs) {
- let minimizedTarget = minimizedDoc;
- if (!CollectionFreeFormDocumentView._undoBatch) {
- CollectionFreeFormDocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
- }
- maximizedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => {
- let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
- if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) {
- if (isMinimized === undefined) {
- isMinimized = BoolCast(maximizedDoc.isMinimized, false);
- }
- let minx = NumCast(minimizedTarget.x, undefined) + NumCast(minimizedTarget.width, undefined) / 2;
- let miny = NumCast(minimizedTarget.y, undefined) + NumCast(minimizedTarget.height, undefined) / 2;
- if (minx !== undefined && miny !== undefined) {
- let scrpt = this.props.ScreenToLocalTransform().inverse().transformPoint(minx, miny);
- maximizedDoc.willMaximize = isMinimized;
- maximizedDoc.isMinimized = false;
- maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]);
- }
- }
- });
- setTimeout(() => {
- CollectionFreeFormDocumentView._undoBatch && CollectionFreeFormDocumentView._undoBatch.end();
- CollectionFreeFormDocumentView._undoBatch = undefined;
- }, 500);
- }
- }
- static _undoBatch?: UndoManager.Batch = undefined;
- onPointerDown = (e: React.PointerEvent): void => {
- this._downX = e.clientX;
- this._downY = e.clientY;
- this._doubleTap = false;
- if (e.button === 0 && e.altKey) {
- e.stopPropagation(); // prevents panning from happening on collection if shift is pressed after a document drag has started
- } // allow pointer down to go through otherwise so that marquees can be drawn starting over a document
- if (Date.now() - this._lastTap < 300) {
- if (e.buttons === 1) {
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- }
- } else {
- this._lastTap = Date.now();
- }
- if (e.button === 0) {
- e.preventDefault(); // prevents Firefox from dragging images (we want to do that ourself)
- }
- }
- onPointerUp = (e: PointerEvent): void => {
- document.removeEventListener("pointerup", this.onPointerUp);
- if (Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2) {
- this._doubleTap = true;
- }
- }
- onClick = async (e: React.MouseEvent) => {
- e.stopPropagation();
- if (this._doubleTap) {
- this.props.addDocTab(this.props.Document, "inTab");
- SelectionManager.DeselectAll();
- this.props.Document.libraryBrush = false;
- }
- let altKey = e.altKey;
- let ctrlKey = e.ctrlKey;
- if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
- Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
- let isExpander = (e.target as any).id === "isExpander";
- if (BoolCast(this.props.Document.isButton, false) || isExpander) {
- SelectionManager.DeselectAll();
- let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs);
- let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
- let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
- let linkedToDocs = await DocListCastAsync(this.props.Document.linkedToDocs, []);
- let linkedFromDocs = await DocListCastAsync(this.props.Document.linkedFromDocs, []);
- let expandedDocs: Doc[] = [];
- expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs;
- expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
- expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
- // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
- if (expandedDocs.length) { // bcz: need a better way to associate behaviors with click events on widget-documents
- let expandedProtoDocs = expandedDocs.map(doc => Doc.GetProto(doc));
- let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace");
- let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
- if (altKey) {
- maxLocation = this.props.Document.maximizeLocation = (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace");
- if (!maxLocation || maxLocation === "inPlace") {
- let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView);
- let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized, false), false);
- expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false);
- let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView);
- if (!hasView) {
- this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(getDispDoc(maxDoc), false));
- }
- expandedProtoDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized);
- }
- }
- if (maxLocation && maxLocation !== "inPlace") {
- let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
- if (dataDocs) {
- expandedDocs.forEach(maxDoc =>
- (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
- this.props.addDocTab(getDispDoc(maxDoc), maxLocation)));
- }
- } else {
- this.toggleIcon(expandedProtoDocs);
- }
- }
- else if (linkedToDocs.length || linkedFromDocs.length) {
- let linkedFwdDocs = [
- linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : expandedDocs[0],
- linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : expandedDocs[0]];
-
- let linkedFwdPage = [
- linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : undefined,
- linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : undefined];
- if (!linkedFwdDocs.some(l => l instanceof Promise)) {
- let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab");
- DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0]);
- }
- }
- }
- }
- }
-
- onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; };
- onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; };
borderRounding = () => {
let br = NumCast(this.props.Document.borderRounding);
@@ -283,17 +144,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
return (
<div className="collectionFreeFormDocumentView-container" ref={this._mainCont}
- onPointerDown={this.onPointerDown}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} onPointerOver={this.onPointerEnter}
- onClick={this.onClick}
style={{
- outlineColor: "maroon",
- outlineStyle: "dashed",
- outlineWidth: BoolCast(this.props.Document.libraryBrush, false) ||
- BoolCast(this.props.Document.protoBrush, false) ?
- `${1 * this.getTransform().Scale}px`
- // "2px"
- : "0px",
opacity: zoomFade,
borderRadius: `${this.borderRounding()}px`,
transformOrigin: "left top",
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 52a9582d2..02396c3af 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,6 +44,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
isSelected: () => boolean,
select: (ctrl: boolean) => void,
layoutKey: string,
+ hideOnLeave?: boolean
}> {
@computed get layout(): string {
const layout = Cast(this.props.Document[this.props.layoutKey], "string");
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2fb794925..c7c142c3b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -2,11 +2,11 @@ import { library } from '@fortawesome/fontawesome-svg-core';
import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons';
import { action, computed, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc";
import { List } from "../../../new_fields/List";
import { ObjectField } from "../../../new_fields/ObjectField";
-import { createSchema, makeInterface, defaultSpec } from "../../../new_fields/Schema";
-import { BoolCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types";
+import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { BoolCast, Cast, FieldValue, StrCast, NumCast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { emptyFunction, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
@@ -16,7 +16,7 @@ import { DragManager, dropActionType } from "../../util/DragManager";
import { SearchUtil } from "../../util/SearchUtil";
import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionPDFView } from "../collections/CollectionPDFView";
import { CollectionVideoView } from "../collections/CollectionVideoView";
@@ -29,6 +29,7 @@ import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import React = require("react");
import { Id, Copy } from '../../../new_fields/FieldSymbols';
+import { ContextMenuProps } from '../ContextMenuItem';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
library.add(faTrash);
@@ -72,9 +73,9 @@ export interface DocumentViewProps {
selectOnLoad: boolean;
parentActive: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
- toggleMinimized: () => void;
bringToFront: (doc: Doc) => void;
addDocTab: (doc: Doc, where: string) => void;
+ whenClicked?: () => void;
}
const schema = createSchema({
@@ -82,7 +83,6 @@ const schema = createSchema({
nativeWidth: "number",
nativeHeight: "number",
backgroundColor: "string",
- test: defaultSpec("number", 5)
});
export const positionSchema = createSchema({
@@ -104,6 +104,9 @@ const Document = makeInterface(schema);
export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) {
private _downX: number = 0;
private _downY: number = 0;
+ private _lastTap: number = 0;
+ private _doubleTap = false;
+ private _hitExpander = false;
private _mainCont = React.createRef<HTMLDivElement>();
private _dropDisposer?: DragManager.DragDropDisposer;
@@ -183,24 +186,121 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
}
+ toggleMinimized = async () => {
+ let minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
+ if (minimizedDoc) {
+ let scrpt = this.props.ScreenToLocalTransform().inverse().transformPoint(
+ NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y));
+ this.collapseToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
+ }
+ }
- onClick = (e: React.MouseEvent): void => {
- if (CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
+ static _undoBatch?: UndoManager.Batch = undefined;
+ @action
+ public collapseToPoint = async (scrpt: number[], expandedDocs: Doc[] | undefined): Promise<void> => {
+ SelectionManager.DeselectAll();
+ if (expandedDocs) {
+ if (!DocumentView._undoBatch) {
+ DocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
+ }
+ let isMinimized: boolean | undefined;
+ expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => {
+ let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
+ if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) {
+ if (isMinimized === undefined) {
+ isMinimized = BoolCast(maximizedDoc.isMinimized, false);
+ }
+ maximizedDoc.willMaximize = isMinimized;
+ maximizedDoc.isMinimized = false;
+ maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]);
+ }
+ });
+ setTimeout(() => {
+ DocumentView._undoBatch && DocumentView._undoBatch.end();
+ DocumentView._undoBatch = undefined;
+ }, 500);
+ }
+ }
+ onClick = async (e: React.MouseEvent) => {
+ e.stopPropagation();
+ let altKey = e.altKey;
+ let ctrlKey = e.ctrlKey;
+ if (this._doubleTap && !this.props.isTopMost) {
+ this.props.addDocTab(this.props.Document, "inTab");
+ SelectionManager.DeselectAll();
+ this.props.Document.libraryBrush = false;
+ }
+ else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
SelectionManager.SelectDoc(this, e.ctrlKey);
+ let isExpander = (e.target as any).id === "isExpander";
+ if (BoolCast(this.props.Document.isButton, false) || isExpander) {
+ SelectionManager.DeselectAll();
+ let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs);
+ let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
+ let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
+ let linkedToDocs = await DocListCastAsync(this.props.Document.linkedToDocs, []);
+ let linkedFromDocs = await DocListCastAsync(this.props.Document.linkedFromDocs, []);
+ let expandedDocs: Doc[] = [];
+ expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs;
+ expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
+ expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
+ // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
+ if (expandedDocs.length) { // bcz: need a better way to associate behaviors with click events on widget-documents
+ let expandedProtoDocs = expandedDocs.map(doc => Doc.GetProto(doc));
+ let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace");
+ let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
+ if (altKey) {
+ maxLocation = this.props.Document.maximizeLocation = (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace");
+ if (!maxLocation || maxLocation === "inPlace") {
+ let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView);
+ let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized, false), false);
+ expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false);
+ let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView);
+ if (!hasView) {
+ this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(getDispDoc(maxDoc), false));
+ }
+ expandedProtoDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized);
+ }
+ }
+ if (maxLocation && maxLocation !== "inPlace" && CollectionDockingView.Instance) {
+ let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
+ if (dataDocs) {
+ expandedDocs.forEach(maxDoc =>
+ (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
+ this.props.addDocTab(getDispDoc(maxDoc), maxLocation)));
+ }
+ } else {
+ let scrpt = this.props.ScreenToLocalTransform().inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
+ this.collapseToPoint(scrpt, expandedProtoDocs);
+ }
+ }
+ else if (linkedToDocs.length || linkedFromDocs.length) {
+ let linkedFwdDocs = [
+ linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : expandedDocs[0],
+ linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : expandedDocs[0]];
+
+ let linkedFwdPage = [
+ linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : undefined,
+ linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : undefined];
+ if (!linkedFwdDocs.some(l => l instanceof Promise)) {
+ let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab");
+ DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0]);
+ }
+ }
+ }
}
}
- _hitExpander = false;
onPointerDown = (e: React.PointerEvent): void => {
this._downX = e.clientX;
this._downY = e.clientY;
this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
- if (e.shiftKey && e.buttons === 1) {
+ if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) {
CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e);
e.stopPropagation();
- } else if (this.active) {
- //e.stopPropagation(); // bcz: doing this will block click events from CollectionFreeFormDocumentView which are needed for iconifying,etc
+ } else {
+ if (this.active) e.stopPropagation(); // events stop at the lowest document that is active.
document.removeEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
@@ -208,7 +308,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
onPointerMove = (e: PointerEvent): void => {
- if (!e.cancelBubble) {
+ if (!e.cancelBubble && this.active) {
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
@@ -223,34 +323,26 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onPointerUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
+ this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2);
+ this._lastTap = Date.now();
}
- deleteClicked = (): void => {
- this.props.removeDocument && this.props.removeDocument(this.props.Document);
- }
- fieldsClicked = (e: React.MouseEvent): void => {
- let kvp = Docs.KVPDocument(this.props.Document, { title: this.props.Document.title + ".kvp", width: 300, height: 300 });
- CollectionDockingView.Instance.AddRightSplit(kvp);
- }
- makeButton = (e: React.MouseEvent): void => {
- let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+ deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }
+ fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.Document, { width: 300, height: 300 }), "onRight") };
+ makeBtnClicked = (): void => {
+ let doc = Doc.GetProto(this.props.Document);
doc.isButton = !BoolCast(doc.isButton, false);
if (StrCast(doc.layout).indexOf("Formatted") !== -1) { // only need to freeze the dimensions of text boxes since they don't have a native width and height naturally
if (doc.isButton && !doc.nativeWidth) {
doc.nativeWidth = this.props.Document[WidthSym]();
doc.nativeHeight = this.props.Document[HeightSym]();
} else {
-
doc.nativeWidth = doc.nativeHeight = undefined;
}
}
}
- fullScreenClicked = (e: React.MouseEvent): void => {
- const doc = Doc.MakeCopy(this.props.Document, false);
- if (doc) {
- CollectionDockingView.Instance.OpenFullScreen(doc);
- }
- ContextMenu.Instance.clearItems();
+ fullScreenClicked = (): void => {
+ CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeCopy(this.props.Document, false));
SelectionManager.DeselectAll();
}
@@ -307,6 +399,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.templates = this.templates;
}
+ freezeNativeDimensions = (e: React.MouseEvent): void => {
+ if (NumCast(this.props.Document.nativeWidth)) {
+ this.props.Document.proto!.nativeWidth = undefined;
+ this.props.Document.proto!.nativeHeight = undefined;
+
+ } else {
+ this.props.Document.proto!.nativeWidth = this.props.Document[WidthSym]();
+ this.props.Document.proto!.nativeHeight = this.props.Document[HeightSym]();
+ }
+ }
+
@action
onContextMenu = (e: React.MouseEvent): void => {
e.stopPropagation();
@@ -318,16 +421,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e.preventDefault();
const cm = ContextMenu.Instance;
- cm.addItem({ description: "Full Screen", event: this.fullScreenClicked, icon: "desktop" });
- cm.addItem({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "inTab"), icon: "folder" });
- cm.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document), icon: "caret-square-right" });
- cm.addItem({ description: "Fields", event: this.fieldsClicked, icon: "layer-group" });
+ let subitems: ContextMenuProps[] = [];
+ subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" });
+ subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "inTab"), icon: "folder" });
+ subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "onRight"), icon: "caret-square-right" });
+ subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
+ cm.addItem({ description: "Open...", subitems: subitems });
+ cm.addItem({ description: NumCast(this.props.Document.nativeWidth) ? "Unfreeze" : "Freeze", event: this.freezeNativeDimensions, icon: "edit" });
cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" });
- cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeButton, icon: "concierge-bell" });
+ cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" });
cm.addItem({
description: "Find aliases", event: async () => {
const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document);
- CollectionDockingView.Instance.AddRightSplit(Docs.SchemaDocument(["title"], aliases, {}));
+ this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), "onRight");
}, icon: "search"
});
cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" });
@@ -344,6 +450,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
+ onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; };
+ onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; };
+
isSelected = () => SelectionManager.IsSelected(this);
select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
@@ -360,6 +469,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
<div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
ref={this._mainCont}
style={{
+ outlineColor: "maroon",
+ outlineStyle: "dashed",
+ outlineWidth: BoolCast(this.props.Document.libraryBrush, false) ||
+ BoolCast(this.props.Document.protoBrush, false) ?
+ `${1 * this.props.ScreenToLocalTransform().Scale}px`
+ : "0px",
borderRadius: "inherit",
background: this.Document.backgroundColor || "",
width: nativeWidth,
@@ -367,6 +482,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
transform: `scale(${scaling}, ${scaling})`
}}
onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
+
+ onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
>
{this.contents}
</div>
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d3d765eed..7b642b299 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -102,7 +102,6 @@ export class FieldView extends React.Component<FieldViewProps> {
layoutKey={"layout"}
ContainingCollectionView={this.props.ContainingCollectionView}
parentActive={this.props.active}
- toggleMinimized={emptyFunction}
whenActiveChanged={this.props.whenActiveChanged}
bringToFront={emptyFunction} />
);
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 5afef221c..5d93edaac 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -50,8 +50,9 @@ library.add(faSmile);
// this will edit the document and assign the new value to that field.
//]
-export interface FormattedTextBoxOverlay {
+export interface FormattedTextBoxProps {
isOverlay?: boolean;
+ hideOnLeave?: boolean;
}
const richTextSchema = createSchema({
@@ -62,7 +63,7 @@ type RichTextDocument = makeInterface<[typeof richTextSchema]>;
const RichTextDocument = makeInterface(richTextSchema);
@observer
-export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTextBoxOverlay), RichTextDocument>(RichTextDocument) {
+export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
public static LayoutString(fieldStr: string = "data") {
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
@@ -239,7 +240,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (DocumentManager.Instance.getDocumentView(f)) {
DocumentManager.Instance.getDocumentView(f)!.props.focus(f);
} else {
- CollectionDockingView.Instance.AddRightSplit(f);
+ this.props.addDocTab && this.props.addDocTab(f, "onRight");
}
}
}));
@@ -273,30 +274,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
}
-
- //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE
- freezeNativeDimensions = (e: React.MouseEvent): void => {
- if (NumCast(this.props.Document.nativeWidth)) {
- this.props.Document.proto!.nativeWidth = undefined;
- this.props.Document.proto!.nativeHeight = undefined;
-
- } else {
- this.props.Document.proto!.nativeWidth = this.props.Document[WidthSym]();
- this.props.Document.proto!.nativeHeight = this.props.Document[HeightSym]();
- }
- }
- specificContextMenu = (e: React.MouseEvent): void => {
- if (!this._gotDown) {
- e.preventDefault();
- return;
- }
- ContextMenu.Instance.addItem({
- description: NumCast(this.props.Document.nativeWidth) ? "Unfreeze" : "Freeze",
- event: this.freezeNativeDimensions,
- icon: "edit"
- });
- }
-
onPointerWheel = (e: React.WheelEvent): void => {
if (this.props.isSelected()) {
e.stopPropagation();
@@ -357,6 +334,17 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._undoTyping = UndoManager.StartBatch("undoTyping");
}
}
+
+ @observable
+ _entered = false;
+ @action
+ onPointerEnter = (e: React.PointerEvent) => {
+ this._entered = true;
+ }
+ @action
+ onPointerLeave = (e: React.PointerEvent) => {
+ this._entered = false;
+ }
render() {
let style = this.props.isOverlay ? "scroll" : "hidden";
let rounded = NumCast(this.props.Document.borderRounding) < 0 ? "-rounded" : "";
@@ -364,6 +352,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return (
<div className={`formattedTextBox-cont-${style}`} ref={this._ref}
style={{
+ background: this.props.hideOnLeave ? "rgba(0,0,0,0.4)" : undefined,
+ opacity: this.props.hideOnLeave ? (this._entered || this.props.isSelected() || this.props.Document.libraryBrush ? 1 : 0.1) : 1,
+ color: this.props.hideOnLeave ? "white" : "initial",
pointerEvents: interactive ? "all" : "none",
}}
// onKeyDown={this.onKeyPress}
@@ -377,6 +368,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
onContextMenu={this.specificContextMenu}
// tfs: do we need this event handler
onWheel={this.onPointerWheel}
+ onPointerEnter={this.onPointerEnter}
+ onPointerLeave={this.onPointerLeave}
>
<div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: this.props.Document.isButton && !this.props.isSelected() ? "none" : "all" }} ref={this._proseRef} />
</div>
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d70068295..4c2b73b70 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -12,13 +12,14 @@ import React = require("react");
import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
import { DocComponent } from '../DocComponent';
import { positionSchema } from './DocumentView';
-import { FieldValue, Cast, StrCast, PromiseValue } from '../../../new_fields/Types';
+import { FieldValue, Cast, StrCast, PromiseValue, NumCast } from '../../../new_fields/Types';
import { ImageField } from '../../../new_fields/URLField';
import { List } from '../../../new_fields/List';
import { InkingControl } from '../InkingControl';
-import { Doc } from '../../../new_fields/Doc';
+import { Doc, WidthSym, HeightSym } from '../../../new_fields/Doc';
import { faImage } from '@fortawesome/free-solid-svg-icons';
import { library } from '@fortawesome/fontawesome-svg-core';
+import { ContextMenuItemProps, ContextMenuProps } from '../ContextMenuItem';
var path = require('path');
@@ -131,11 +132,20 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let field = Cast(this.Document[this.props.fieldKey], ImageField);
if (field) {
let url = field.url.href;
- ContextMenu.Instance.addItem({
- description: "Copy path", event: () => {
- Utils.CopyText(url);
- }, icon: "expand-arrows-alt"
+ let subitems: ContextMenuProps[] = [];
+ subitems.push({ description: "Copy path", event: () => Utils.CopyText(url), icon: "expand-arrows-alt" });
+ subitems.push({
+ description: "Rotate", event: action(() => {
+ this.props.Document.rotation = (NumCast(this.props.Document.rotation) + 90) % 360;
+ let nw = this.props.Document.nativeWidth;
+ this.props.Document.nativeWidth = this.props.Document.nativeHeight;
+ this.props.Document.nativeHeight = nw;
+ let w = this.props.Document.width;
+ this.props.Document.width = this.props.Document.height;
+ this.props.Document.height = w;
+ }), icon: "expand-arrows-alt"
});
+ ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: subitems });
}
}
@@ -199,6 +209,9 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => this.choosePath((p as ImageField).url));
// }
let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
+ let rotation = NumCast(this.props.Document.rotation, 0);
+ let aspect = (rotation % 180) ? this.props.Document[HeightSym]() / this.props.Document[WidthSym]() : 1;
+ let shift = (rotation % 180) ? (this.props.Document[HeightSym]() - this.props.Document[WidthSym]() / aspect) / 2 : 0;
return (
<div id={id} className={`imageBox-cont${interactive}`}
onPointerDown={this.onPointerDown}
@@ -206,6 +219,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
<img id={id}
key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={paths[Math.min(paths.length, this._photoIndex)]}
+ style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
// style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }}
width={nativeWidth}
ref={this._imgRef}
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index add347a88..e5b7a025b 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -1,21 +1,17 @@
-import { computed, observable, action, runInAction } from "mobx";
+import { action, computed, observable, runInAction } from "mobx";
import * as rp from 'request-promise';
+import { DocServer } from "../../../client/DocServer";
import { Docs } from "../../../client/documents/Documents";
-import { Attribute, AttributeGroup, Catalog, Schema, AggregateFunction } from "../../../client/northstar/model/idea/idea";
+import { Gateway, NorthstarSettings } from "../../../client/northstar/manager/Gateway";
+import { Attribute, AttributeGroup, Catalog, Schema } from "../../../client/northstar/model/idea/idea";
import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil";
-import { RouteStore } from "../../RouteStore";
-import { DocServer } from "../../../client/DocServer";
-import { Doc, Opt, Field } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
import { CollectionViewType } from "../../../client/views/collections/CollectionBaseView";
-import { CollectionTreeView } from "../../../client/views/collections/CollectionTreeView";
import { CollectionView } from "../../../client/views/collections/CollectionView";
-import { NorthstarSettings, Gateway } from "../../../client/northstar/manager/Gateway";
-import { AttributeTransformationModel } from "../../../client/northstar/core/attribute/AttributeTransformationModel";
-import { ColumnAttributeModel } from "../../../client/northstar/core/attribute/AttributeModel";
-import { HistogramOperation } from "../../../client/northstar/operations/HistogramOperation";
-import { Cast, PromiseValue } from "../../../new_fields/Types";
+import { Doc } from "../../../new_fields/Doc";
+import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
+import { Cast } from "../../../new_fields/Types";
+import { RouteStore } from "../../RouteStore";
export class CurrentUserUtils {
private static curr_email: string;