aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json2
-rw-r--r--src/client/DocServer.ts2
-rw-r--r--src/client/documents/DocumentTypes.ts22
-rw-r--r--src/client/documents/Documents.ts44
-rw-r--r--src/client/util/DictationManager.ts3
-rw-r--r--src/client/util/DragManager.ts5
-rw-r--r--src/client/util/RichTextSchema.tsx16
-rw-r--r--src/client/util/TooltipTextMenu.tsx49
-rw-r--r--src/client/views/ContextMenu.tsx50
-rw-r--r--src/client/views/DocumentDecorations.scss17
-rw-r--r--src/client/views/DocumentDecorations.tsx28
-rw-r--r--src/client/views/GlobalKeyHandler.ts4
-rw-r--r--src/client/views/InkingCanvas.scss3
-rw-r--r--src/client/views/InkingCanvas.tsx20
-rw-r--r--src/client/views/Main.scss13
-rw-r--r--src/client/views/MainOverlayTextBox.tsx7
-rw-r--r--src/client/views/MainView.tsx93
-rw-r--r--src/client/views/MetadataEntryMenu.scss12
-rw-r--r--src/client/views/MetadataEntryMenu.tsx52
-rw-r--r--src/client/views/ScriptBox.tsx30
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx4
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx48
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss48
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx22
-rw-r--r--src/client/views/collections/CollectionStackingView.scss26
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx169
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx75
-rw-r--r--src/client/views/collections/CollectionSubView.tsx7
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx75
-rw-r--r--src/client/views/collections/CollectionView.tsx26
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss32
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx295
-rw-r--r--src/client/views/collections/KeyRestrictionRow.tsx6
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx188
-rw-r--r--src/client/views/nodes/ButtonBox.scss4
-rw-r--r--src/client/views/nodes/ButtonBox.tsx42
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx18
-rw-r--r--src/client/views/nodes/DocumentView.tsx184
-rw-r--r--src/client/views/nodes/DragBox.scss13
-rw-r--r--src/client/views/nodes/DragBox.tsx101
-rw-r--r--src/client/views/nodes/FieldView.tsx8
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss8
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx92
-rw-r--r--src/client/views/nodes/ImageBox.scss44
-rw-r--r--src/client/views/nodes/ImageBox.tsx35
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx1
-rw-r--r--src/client/views/nodes/LinkMenu.tsx3
-rw-r--r--src/client/views/nodes/LinkMenuItem.tsx2
-rw-r--r--src/client/views/nodes/PDFBox.tsx2
-rw-r--r--src/client/views/nodes/PresBox.tsx (renamed from src/client/views/presentationview/PresentationView.tsx)501
-rw-r--r--src/client/views/nodes/WebBox.scss62
-rw-r--r--src/client/views/nodes/WebBox.tsx82
-rw-r--r--src/client/views/pdf/Annotation.tsx6
-rw-r--r--src/client/views/pdf/PDFViewer.tsx1
-rw-r--r--src/client/views/pdf/Page.tsx13
-rw-r--r--src/client/views/presentationview/PresentationElement.tsx3
-rw-r--r--src/client/views/presentationview/PresentationList.tsx5
-rw-r--r--src/client/views/presentationview/PresentationView.scss6
-rw-r--r--src/client/views/search/FilterBox.tsx2
-rw-r--r--src/client/views/search/IconBar.tsx1
-rw-r--r--src/client/views/search/IconButton.tsx2
-rw-r--r--src/client/views/search/SearchBox.scss5
-rw-r--r--src/client/views/search/SearchBox.tsx20
-rw-r--r--src/client/views/search/SearchItem.tsx3
-rw-r--r--src/new_fields/Doc.ts100
-rw-r--r--src/new_fields/ListSpec.ts0
-rw-r--r--src/new_fields/PresField.ts6
-rw-r--r--src/new_fields/SchemaHeaderField.ts14
-rw-r--r--src/new_fields/Types.ts4
-rw-r--r--src/new_fields/util.ts3
-rw-r--r--src/scraping/buxton/source/.Bill_Notes_NewO.docx.icloudbin0 -> 172 bytes
-rw-r--r--src/scraping/buxton/source/.Bill_Notes_OLPC.docx.icloudbin0 -> 172 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_NewO.docxbin2264571 -> 0 bytes
-rw-r--r--src/scraping/buxton/source/Bill_Notes_OLPC.docxbin6721592 -> 0 bytes
-rw-r--r--src/server/authentication/models/current_user_utils.ts25
-rw-r--r--views/stylesheets/authentication.css6
79 files changed, 1750 insertions, 1182 deletions
diff --git a/package.json b/package.json
index 7a629d813..e6a0a54b9 100644
--- a/package.json
+++ b/package.json
@@ -151,7 +151,7 @@
"js-datepicker": "^4.6.6",
"jsonwebtoken": "^8.5.0",
"jsx-to-string": "^1.4.0",
- "lodash": "^4.17.11",
+ "lodash": "^4.17.15",
"mobile-detect": "^1.4.3",
"mobx": "^5.9.0",
"mobx-react": "^5.3.5",
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 47aff2bb6..56d9147ae 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -34,6 +34,8 @@ export namespace DocServer {
LivePlayground = 3,
}
+ export let AclsMode = WriteMode.Default;
+
const fieldWriteModes: { [field: string]: WriteMode } = {};
const docsWithUpdates: { [field: string]: Set<Doc> } = {};
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
new file mode 100644
index 000000000..1578e49fe
--- /dev/null
+++ b/src/client/documents/DocumentTypes.ts
@@ -0,0 +1,22 @@
+export enum DocumentType {
+ NONE = "none",
+ TEXT = "text",
+ HIST = "histogram",
+ IMG = "image",
+ WEB = "web",
+ COL = "collection",
+ KVP = "kvp",
+ VID = "video",
+ AUDIO = "audio",
+ PDF = "pdf",
+ ICON = "icon",
+ IMPORT = "import",
+ LINK = "link",
+ LINKDOC = "linkdoc",
+ BUTTON = "button",
+ TEMPLATE = "template",
+ EXTENSION = "extension",
+ YOUTUBE = "youtube",
+ DRAGBOX = "dragbox",
+ PRES = "presentation",
+} \ No newline at end of file
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7dd853156..47df17329 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,24 +1,3 @@
-export enum DocumentType {
- NONE = "none",
- TEXT = "text",
- HIST = "histogram",
- IMG = "image",
- WEB = "web",
- COL = "collection",
- KVP = "kvp",
- VID = "video",
- AUDIO = "audio",
- PDF = "pdf",
- ICON = "icon",
- IMPORT = "import",
- LINK = "link",
- LINKDOC = "linkdoc",
- BUTTON = "button",
- TEMPLATE = "template",
- EXTENSION = "extension",
- YOUTUBE = "youtube",
-}
-
import { HistogramField } from "../northstar/dash-fields/HistogramField";
import { HistogramBox } from "../northstar/dash-nodes/HistogramBox";
import { HistogramOperation } from "../northstar/operations/HistogramOperation";
@@ -60,9 +39,14 @@ import { DocumentManager } from "../util/DocumentManager";
import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox";
import { Scripting, CompileScript } from "../util/Scripting";
import { ButtonBox } from "../views/nodes/ButtonBox";
+import { DragBox } from "../views/nodes/DragBox";
import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField";
+import { PresBox } from "../views/nodes/PresBox";
import { ComputedField } from "../../new_fields/ScriptField";
import { ProxyField } from "../../new_fields/Proxy";
+import { DocumentType } from "./DocumentTypes";
+//import { PresBox } from "../views/nodes/PresBox";
+//import { PresField } from "../../new_fields/PresField";
var requestImageSize = require('../util/request-image-size');
var path = require('path');
@@ -177,6 +161,14 @@ export namespace Docs {
}],
[DocumentType.BUTTON, {
layout: { view: ButtonBox },
+ }],
+ [DocumentType.PRES, {
+ layout: { view: PresBox },
+ options: {}
+ }],
+ [DocumentType.DRAGBOX, {
+ layout: { view: DragBox },
+ options: { width: 40, height: 40 },
}]
]);
@@ -298,7 +290,7 @@ export namespace Docs {
const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys);
if (!("author" in protoProps)) {
- protoProps.author = CurrentUserUtils.email;
+ protoProps.author = Doc.CurrentUserEmail;
}
if (!("creationDate" in protoProps)) {
@@ -346,6 +338,9 @@ export namespace Docs {
.catch((err: any) => console.log(err));
return inst;
}
+ export function PresDocument(initial: List<Doc> = new List(), options: DocumentOptions = {}) {
+ return InstanceFromProto(Prototypes.get(DocumentType.PRES), initial, options);
+ }
export function VideoDocument(url: string, options: DocumentOptions = {}) {
return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(new URL(url)), options);
@@ -442,6 +437,11 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}) });
}
+
+ export function DragboxDocument(options?: DocumentOptions) {
+ return InstanceFromProto(Prototypes.get(DocumentType.DRAGBOX), undefined, { ...(options || {}) });
+ }
+
export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
}
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 9c61fe125..b00846756 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -2,9 +2,10 @@ import { SelectionManager } from "./SelectionManager";
import { DocumentView } from "../views/nodes/DocumentView";
import { UndoManager } from "./UndoManager";
import * as interpreter from "words-to-numbers";
+import { DocumentType } from "../documents/DocumentTypes";
import { Doc } from "../../new_fields/Doc";
import { List } from "../../new_fields/List";
-import { Docs, DocumentType } from "../documents/Documents";
+import { Docs } from "../documents/Documents";
import { CollectionViewType } from "../views/collections/CollectionBaseView";
import { Cast, CastCtor } from "../../new_fields/Types";
import { listSpec } from "../../new_fields/Schema";
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index a7aaaed7c..894b366ef 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -9,8 +9,6 @@ import { DocumentManager } from "./DocumentManager";
import { LinkManager } from "./LinkManager";
import { SelectionManager } from "./SelectionManager";
import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
-import { DocumentDecorations } from "../views/DocumentDecorations";
-import { NumberLiteralType } from "typescript";
export type dropActionType = "alias" | "copy" | undefined;
export function SetupDrag(
@@ -211,6 +209,7 @@ export namespace DragManager {
dropAction: dropActionType;
userDropAction: dropActionType;
moveDocument?: MoveFunction;
+ applyAsTemplate?: boolean;
[id: string]: any;
}
@@ -490,7 +489,7 @@ export namespace DragManager {
x: e.x,
y: e.y,
data: dragData,
- mods: e.altKey ? "AltKey" : e.ctrlKey ? "CtrlKey" : ""
+ mods: e.altKey ? "AltKey" : e.ctrlKey ? "CtrlKey" : e.metaKey ? "MetaKey" : ""
}
})
);
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index ce9e29b26..6d2abfaa2 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,11 +1,6 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType, Slice, Mark, Node } from "prosemirror-model";
-import { joinUp, lift, setBlockType, toggleMark, wrapIn, selectNodeForward, deleteSelection } from 'prosemirror-commands';
-import { redo, undo } from 'prosemirror-history';
-import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list';
-import { EditorState, Transaction, NodeSelection, TextSelection, Selection, } from "prosemirror-state";
-import { EditorView, } from "prosemirror-view";
-import { View } from '@react-pdf/renderer';
+import { DOMOutputSpecArray, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model";
+import { bulletList, listItem, orderedList } from 'prosemirror-schema-list';
+import { TextSelection } from "prosemirror-state";
const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -110,6 +105,7 @@ export const nodes: { [index: string]: NodeSpec } = {
// }
// }]
},
+
// :: NodeSpec An inline image (`<img>`) node. Supports `src`,
// `alt`, and `href` attributes. The latter two default to the empty
// string.
@@ -188,6 +184,7 @@ export const nodes: { [index: string]: NodeSpec } = {
// parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }],
// toDOM() { return ulDOM }
},
+
//bullet_list: {
// content: 'list_item+',
// group: 'block',
@@ -199,7 +196,8 @@ export const nodes: { [index: string]: NodeSpec } = {
list_item: {
...listItem,
content: 'paragraph block*'
- }
+ },
+
};
const emDOM: DOMOutputSpecArray = ["em", 0];
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index d33a52d7f..4672dd246 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1,32 +1,25 @@
-import { action, observable, observe } from "mobx";
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faTag, faPlus, faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons';
-import { Dropdown, MenuItem, icons, } from "prosemirror-menu"; //no import css
-import { EditorState, NodeSelection, TextSelection, Transaction } from "prosemirror-state";
-import { EditorView } from "prosemirror-view";
-import { schema } from "./RichTextSchema";
-import { Schema, NodeType, MarkType, Mark, ResolvedPos } from "prosemirror-model";
-import { Node as ProsNode } from "prosemirror-model";
-import "./TooltipTextMenu.scss";
-const { toggleMark, setBlockType } = require("prosemirror-commands");
import { library } from '@fortawesome/fontawesome-svg-core';
-import { wrapInList, liftListItem, } from 'prosemirror-schema-list';
import { faListUl } from '@fortawesome/free-solid-svg-icons';
-import { FieldViewProps } from "../views/nodes/FieldView";
-const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js");
-import { DragManager } from "./DragManager";
-import { Doc, Opt, Field } from "../../new_fields/Doc";
+import { action, observable } from "mobx";
+import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css
+import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model";
+import { liftListItem, wrapInList } from 'prosemirror-schema-list';
+import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
+import { EditorView } from "prosemirror-view";
+import { Doc, Field, Opt } from "../../new_fields/Doc";
+import { Id } from "../../new_fields/FieldSymbols";
+import { Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
+import { FieldViewProps } from "../views/nodes/FieldView";
+import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
import { DocumentManager } from "./DocumentManager";
-import { Id } from "../../new_fields/FieldSymbols";
-import { FormattedTextBoxProps, FormattedTextBox } from "../views/nodes/FormattedTextBox";
-import { typeAlias } from "babel-types";
-import React, { Children } from "react";
-import ReactDOM from "react-dom";
-import { Utils } from "../../Utils";
+import { DragManager } from "./DragManager";
import { LinkManager } from "./LinkManager";
-import { bool } from "prop-types";
+import { schema } from "./RichTextSchema";
+import "./TooltipTextMenu.scss";
+const { toggleMark, setBlockType } = require("prosemirror-commands");
+const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js");
//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipTextMenu {
@@ -67,6 +60,8 @@ export class TooltipTextMenu {
@observable
private _storedMarks: Mark<any>[] | null | undefined;
+ public HackToFixTextSelectionGlitch: boolean = false;
+
constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) {
this.view = view;
@@ -177,6 +172,7 @@ export class TooltipTextMenu {
this.listTypeToIcon = new Map();
this.listTypeToIcon.set(schema.nodes.bullet_list, ":");
this.listTypeToIcon.set(schema.nodes.ordered_list, "1)");
+ // this.listTypeToIcon.set(schema.nodes.bullet_list, "⬜");
this.listTypes = Array.from(this.listTypeToIcon.keys());
//custom tools
@@ -191,8 +187,6 @@ export class TooltipTextMenu {
this.update(view, undefined);
- //view.dom.parentNode!.parentNode!.insertBefore(this.tooltip, view.dom.parentNode);
-
// add tooltip to outerdiv to circumvent scaling problem
const outer_div = this.editorProps.outer_div;
outer_div && outer_div(this.wrapper);
@@ -855,7 +849,8 @@ export class TooltipTextMenu {
this.updateFontSizeDropdown("Various");
}
}
- this.view.dispatch(this.view.state.tr.setStoredMarks(this._activeMarks));
+ !this.HackToFixTextSelectionGlitch &&
+ this.view.dispatch(this.view.state.tr.setStoredMarks(this._activeMarks)); // bcz: what's the purpose of this line? It messes up text selection without the Hack.
this.update_mark_doms();
}
@@ -969,7 +964,7 @@ export class TooltipTextMenu {
});
}
}
- if (!ref_node.isLeaf) {
+ if (!ref_node.isLeaf && ref_node.childCount > 0) {
ref_node = ref_node.child(0);
}
return ref_node;
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 1bf6e383d..760736501 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -1,6 +1,6 @@
import React = require("react");
import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem";
-import { observable, action, computed } from "mobx";
+import { observable, action, computed, runInAction, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import "./ContextMenu.scss";
import { library } from '@fortawesome/fontawesome-svg-core';
@@ -27,6 +27,13 @@ export class ContextMenu extends React.Component {
@observable private _width: number = 0;
@observable private _height: number = 0;
+ @observable private _mouseX: number = -1;
+ @observable private _mouseY: number = -1;
+ @observable private _shouldDisplay: boolean = false;
+ @observable private _mouseDown: boolean = false;
+
+ private _reactionDisposer?: IReactionDisposer;
+
constructor(props: Readonly<{}>) {
super(props);
@@ -34,6 +41,40 @@ export class ContextMenu extends React.Component {
}
@action
+ onPointerDown = (e: PointerEvent) => {
+ this._mouseDown = true;
+ this._mouseX = e.clientX;
+ this._mouseY = e.clientY;
+ }
+ @action
+ onPointerUp = (e: PointerEvent) => {
+ this._mouseDown = false;
+ let curX = e.clientX;
+ let curY = e.clientY;
+ if (this._mouseX !== curX || this._mouseY !== curY) {
+ this._shouldDisplay = false;
+ }
+
+ this._shouldDisplay && (this._display = true);
+ }
+ componentWillUnmount() {
+ document.removeEventListener("pointerdown", this.onPointerDown);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ this._reactionDisposer && this._reactionDisposer();
+ }
+
+ @action
+ componentDidMount = () => {
+ document.addEventListener("pointerdown", this.onPointerDown);
+ document.addEventListener("pointerup", this.onPointerUp);
+
+ this._reactionDisposer = reaction(
+ () => this._shouldDisplay,
+ () => this._shouldDisplay && !this._mouseDown && runInAction(() => this._display = true)
+ );
+ }
+
+ @action
clearItems() {
this._items = [];
}
@@ -83,22 +124,21 @@ export class ContextMenu extends React.Component {
}
@action
- displayMenu(x: number, y: number) {
+ displayMenu = (x: number, y: number) => {
//maxX and maxY will change if the UI/font size changes, but will work for any amount
//of items added to the menu
this._pageX = x;
this._pageY = y;
-
this._searchString = "";
-
- this._display = true;
+ this._shouldDisplay = true;
}
@action
closeMenu = () => {
this.clearItems();
this._display = false;
+ this._shouldDisplay = false;
}
@computed get filteredItems(): (OriginalMenuProps | string[])[] {
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ef7159370..ac8497bd0 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -24,13 +24,13 @@ $linkGap : 3px;
.documentDecorations-resizer {
pointer-events: auto;
background: $alt-accent;
- opacity: 0.8;
+ opacity: 1;
}
.documentDecorations-radius {
pointer-events: auto;
background: black;
- opacity: 0.8;
+ opacity: 1;
transform: translate(10px, 10px);
grid-row: 4;
}
@@ -92,27 +92,30 @@ $linkGap : 3px;
.title {
background: $alt-accent;
+ opacity: 1;
grid-column-start: 3;
grid-column-end: 4;
pointer-events: auto;
overflow: hidden;
+ text-align: center;
}
}
.documentDecorations-closeButton {
background: $alt-accent;
- opacity: 0.8;
+ opacity: 1;
grid-column-start: 4;
grid-column-end: 6;
pointer-events: all;
text-align: center;
cursor: pointer;
+ padding-right: 10px;
}
.documentDecorations-minimizeButton {
background: $alt-accent;
- opacity: 0.8;
+ opacity: 1;
grid-column-start: 1;
grid-column-end: 3;
pointer-events: all;
@@ -121,6 +124,7 @@ $linkGap : 3px;
position: absolute;
left: 0px;
top: 0px;
+ padding-top: 5px;
width: $MINIMIZED_ICON_SIZE;
height: $MINIMIZED_ICON_SIZE;
}
@@ -219,6 +223,11 @@ $linkGap : 3px;
margin-top: 3px;
}
+.documentdecorations-times {
+ margin-top: 3px;
+ padding-right: 3px;
+}
+
.templating-button,
.docDecs-tagButton {
width: 20px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7645af054..d5d59a8ee 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,5 +1,5 @@
import { library, IconProp } from '@fortawesome/fontawesome-svg-core';
-import { faLink, faTag, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faStopCircle, faCloudUploadAlt, faSyncAlt } from '@fortawesome/free-solid-svg-icons';
+import { faLink, faTag, faTimes, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faStopCircle, faCloudUploadAlt, faSyncAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
@@ -36,6 +36,7 @@ export const Flyout = higflyout.default;
library.add(faLink);
library.add(faTag);
+library.add(faTimes);
library.add(faArrowAltCircleDown);
library.add(faArrowAltCircleUp);
library.add(faStopCircle);
@@ -74,6 +75,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@observable private _opacity = 1;
@observable private _removeIcon = false;
@observable public Interacting = false;
+ @observable private _isMoving = false;
@observable public pushIcon: IconProp = "arrow-alt-circle-up";
@observable public pullIcon: IconProp = "arrow-alt-circle-down";
@@ -267,10 +269,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
@undoBatch
@action
- onCloseUp = (e: PointerEvent): void => {
+ onCloseUp = async (e: PointerEvent) => {
e.stopPropagation();
if (e.button === 0) {
- SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document));
+ const recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc);
+ SelectionManager.SelectedDocuments().map(dv => {
+ recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
+ dv.props.removeDocument && dv.props.removeDocument(dv.props.Document);
+ });
SelectionManager.DeselectAll();
document.removeEventListener("pointermove", this.onCloseMove);
document.removeEventListener("pointerup", this.onCloseUp);
@@ -400,9 +406,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
document.addEventListener("pointermove", this.onRadiusMove);
document.addEventListener("pointerup", this.onRadiusUp);
}
+ if (!this._isMoving) {
+ SelectionManager.SelectedDocuments().map(dv => dv.props.Document.layout instanceof Doc ? dv.props.Document.layout : dv.props.Document.isTemplate ? dv.props.Document : Doc.GetProto(dv.props.Document)).
+ map(d => d.borderRounding = "0%");
+ }
}
onRadiusMove = (e: PointerEvent): void => {
+ this._isMoving = true;
let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1]));
SelectionManager.SelectedDocuments().map(dv => dv.props.Document.layout instanceof Doc ? dv.props.Document.layout : dv.props.Document.isTemplate ? dv.props.Document : Doc.GetProto(dv.props.Document)).
map(d => d.borderRounding = `${Math.min(100, dist)}%`);
@@ -415,6 +426,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
e.preventDefault();
this._isPointerDown = false;
this._resizeUndo && this._resizeUndo.end();
+ this._isMoving = false;
document.removeEventListener("pointermove", this.onRadiusMove);
document.removeEventListener("pointerup", this.onRadiusUp);
}
@@ -577,7 +589,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
break;
}
- runInAction(() => FormattedTextBox.InputBoxOverlay = undefined);
+ if (!this._resizing) runInAction(() => FormattedTextBox.InputBoxOverlay = undefined);
SelectionManager.SelectedDocuments().forEach(element => {
if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
let doc = PositionDocument(element.props.Document);
@@ -591,12 +603,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
doc.x = (doc.x || 0) + dX * (actualdW - width);
doc.y = (doc.y || 0) + dY * (actualdH - height);
let proto = doc.isTemplate ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here...
- let fixedAspect = e.ctrlKey || (!BoolCast(proto.ignoreAspect) && nwidth && nheight);
+ let fixedAspect = e.ctrlKey || (!BoolCast(doc.ignoreAspect) && nwidth && nheight);
if (fixedAspect && (!nwidth || !nheight)) {
proto.nativeWidth = nwidth = doc.width || 0;
proto.nativeHeight = nheight = doc.height || 0;
}
- if (nwidth > 0 && nheight > 0 && !BoolCast(proto.ignoreAspect)) {
+ if (nwidth > 0 && nheight > 0 && !BoolCast(doc.ignoreAspect)) {
if (Math.abs(dW) > Math.abs(dH)) {
if (!fixedAspect) {
Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true);
@@ -856,7 +868,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{this._edtingTitle ?
<input ref={this.keyinput} className="title" type="text" name="dynbox" value={this._title} onBlur={this.titleBlur} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> :
<div className="title" onPointerDown={this.onTitleDown} ><span>{`${this.selectionTitle}`}</span></div>}
- <div className="documentDecorations-closeButton" title="Close Document" onPointerDown={this.onCloseDown}>X</div>
+ <div className="documentDecorations-closeButton" title="Close Document" onPointerDown={this.onCloseDown}>
+ <FontAwesomeIcon className="documentdecorations-times" icon={faTimes} size="lg" />
+ </div>
<div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index e773014e3..8a7295c65 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -194,6 +194,10 @@ export default class KeyManager {
};
});
+ async printClipboard() {
+ let text: string = await navigator.clipboard.readText();
+ }
+
private ctrl_shift = action((keyname: string) => {
let stopPropagation = true;
let preventDefault = true;
diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss
index d95398f17..5437b26d6 100644
--- a/src/client/views/InkingCanvas.scss
+++ b/src/client/views/InkingCanvas.scss
@@ -21,8 +21,7 @@
width: 8192px;
height: 8192px;
cursor: "crosshair";
- pointer-events: auto;
-
+ pointer-events: all;
}
.inkingCanvas-canSelect,
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 1c221e3df..1cfa8d644 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -165,14 +165,18 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
}
return paths;
}, [] as JSX.Element[]);
- return [<svg className={`inkingCanvas-paths-ink`} key="Pens"
- style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} >
- {paths.filter(path => path.props.tool !== InkTool.Highlighter)}
- </svg>,
- <svg className={`inkingCanvas-paths-markers`} key="Markers"
- style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }}>
- {paths.filter(path => path.props.tool === InkTool.Highlighter)}
- </svg>];
+ let markerPaths = paths.filter(path => path.props.tool === InkTool.Highlighter);
+ let penPaths = paths.filter(path => path.props.tool !== InkTool.Highlighter);
+ return [!penPaths.length ? (null) :
+ <svg className={`inkingCanvas-paths-ink`} key="Pens"
+ style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} >
+ {penPaths}
+ </svg>,
+ !markerPaths.length ? (null) :
+ <svg className={`inkingCanvas-paths-markers`} key="Markers"
+ style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }}>
+ {markerPaths}
+ </svg>];
}
render() {
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index f76abaff3..bc0975c86 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -140,6 +140,8 @@ button:hover {
// font-size: 8px;
// user-select: none;
// }
+ margin-top: -2.55px;
+ margin-left: -2.55px;
}
// add nodes menu. Note that the + button is actually an input label, not an actual button.
@@ -295,4 +297,15 @@ ul#add-options-list {
z-index: 999;
transition: 0.5s all ease;
pointer-events: none;
+}
+
+.webpage-input {
+ display: none;
+ height: 60px;
+ width: 600px;
+ position: absolute;
+
+ .url-input {
+ width: 80%;
+ }
} \ No newline at end of file
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 72eb956e3..9fe435bc5 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -130,21 +130,24 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
render() {
this.TextDoc; this.TextDataDoc;
if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) {
+ let wid = FormattedTextBox.InputBoxOverlay.props.Document.width; // need to force overlay to render when underlying text box is resized (eg, w/ DocDecorations)
let textRect = this._textTargetDiv.getBoundingClientRect();
let s = this._textXf().Scale;
let location = this._textBottom ? textRect.bottom : textRect.top;
let hgt = this._textAutoHeight || this._textBottom ? "auto" : this._textTargetDiv.clientHeight;
return <div ref={this._setouterdiv} className="mainOverlayTextBox-unscaled_div" style={{ transform: `translate(${textRect.left}px, ${location}px)` }} >
- <div className="mainOverlayTextBox-textInput" style={{ transform: `scale(${1 / s},${1 / s})`, width: "auto", height: "0px" }} >
+ <div className="mainOverlayTextBox-textInput" style={{ transform: `scale(${1 / s})`, width: "auto", height: "0px" }} >
<div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
style={{ width: `${textRect.width * s}px`, height: "0px" }}>
<div style={{ height: hgt, width: "100%", position: "absolute", bottom: this._textBottom ? "0px" : undefined }}>
<FormattedTextBox color={`${this._textColor}`} fieldKey={this.TextFieldKey} fieldExt="" hideOnLeave={this._textHideOnLeave} isOverlay={true}
Document={FormattedTextBox.InputBoxOverlay.props.Document}
DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc}
+ onClick={undefined}
isSelected={returnTrue} select={emptyFunction} renderDepth={0} selectOnLoad={true}
ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ContentScaling={returnOne}
- ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} outer_div={(tooltip: HTMLElement) => { this._tooltip = tooltip; this.updateTooltip(); }} />
+ ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction}
+ pinToPres={returnZero} addDocTab={this.addDocTab} outer_div={(tooltip: HTMLElement) => { this._tooltip = tooltip; this.updateTooltip(); }} />
</div>
</div>
</div>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 77f0e3d60..e85374f6a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,29 +1,32 @@
import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowDown, faCloudUploadAlt, faArrowUp, faClone, faCheck, faPlay, faPause, faCaretUp, faLongArrowAltRight, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faPortrait, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt, faCat, faBolt } from '@fortawesome/free-solid-svg-icons';
+import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, configure, observable, runInAction, reaction, trace, autorun } from 'mobx';
+import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
import { SketchPicker } from 'react-color';
import Measure from 'react-measure';
import { Doc, DocListCast, Opt, HeightSym } from '../../new_fields/Doc';
+import { List } from '../../new_fields/List';
import { Id } from '../../new_fields/FieldSymbols';
import { InkTool } from '../../new_fields/InkField';
-import { List } from '../../new_fields/List';
import { listSpec } from '../../new_fields/Schema';
-import { Cast, FieldValue, NumCast, BoolCast, StrCast } from '../../new_fields/Types';
+import { BoolCast, Cast, FieldValue, StrCast } from '../../new_fields/Types';
import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
import { RouteStore } from '../../server/RouteStore';
import { emptyFunction, returnOne, returnTrue, Utils, returnEmptyString, PostToServer } from '../../Utils';
import { DocServer } from '../DocServer';
import { Docs } from '../documents/Documents';
+import { ClientUtils } from '../util/ClientUtils';
+import { DictationManager } from '../util/DictationManager';
import { SetupDrag } from '../util/DragManager';
import { HistoryUtil } from '../util/History';
import { Transform } from '../util/Transform';
-import { UndoManager } from '../util/UndoManager';
+import { UndoManager, undoBatch } from '../util/UndoManager';
import { CollectionBaseView } from './collections/CollectionBaseView';
import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionTreeView } from './collections/CollectionTreeView';
import { ContextMenu } from './ContextMenu';
import { DocumentDecorations } from './DocumentDecorations';
import KeyManager from './GlobalKeyHandler';
@@ -33,21 +36,15 @@ import { MainOverlayTextBox } from './MainOverlayTextBox';
import { DocumentView } from './nodes/DocumentView';
import { OverlayView } from './OverlayView';
import PDFMenu from './pdf/PDFMenu';
-import { PresentationView } from './presentationview/PresentationView';
import { PreviewCursor } from './PreviewCursor';
import { FilterBox } from './search/FilterBox';
-import { CollectionTreeView } from './collections/CollectionTreeView';
-import { ClientUtils } from '../util/ClientUtils';
-import { SchemaHeaderField, RandomPastel } from '../../new_fields/SchemaHeaderField';
-import { DictationManager } from '../util/DictationManager';
-import { docs_v1 } from 'googleapis';
-import { GoogleApiClientUtils } from '../apis/google_docs/GoogleApiClientUtils';
+import PresModeMenu from './presentationview/PresentationModeMenu';
+import { PresBox } from './nodes/PresBox';
@observer
export class MainView extends React.Component {
public static Instance: MainView;
@observable addMenuToggle = React.createRef<HTMLInputElement>();
- @observable private _workspacesShown: boolean = false;
@observable public pwidth: number = 0;
@observable public pheight: number = 0;
@@ -84,9 +81,6 @@ export class MainView extends React.Component {
public isPointerDown = false;
private set mainContainer(doc: Opt<Doc>) {
if (doc) {
- if (!("presentationView" in doc)) {
- doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]);
- }
CurrentUserUtils.UserDocument.activeWorkspace = doc;
}
}
@@ -151,6 +145,7 @@ export class MainView extends React.Component {
componentWillUnMount() {
window.removeEventListener("keydown", KeyManager.Instance.handle);
+ //close presentation
window.removeEventListener("pointerdown", this.globalPointerDown);
window.removeEventListener("pointerup", this.globalPointerUp);
}
@@ -176,7 +171,7 @@ export class MainView extends React.Component {
library.add(faCat);
library.add(faFilePdf);
library.add(faObjectGroup);
- library.add(faTable);
+ library.add(faTv);
library.add(faGlobeAsia);
library.add(faUndoAlt);
library.add(faRedoAlt);
@@ -314,7 +309,6 @@ export class MainView extends React.Component {
@computed get dockingContent() {
let flyoutWidth = this.flyoutWidth;
let mainCont = this.mainContainer;
- let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, listSpec(Doc))) : undefined;
return <Measure offset onResize={this.onResize}>
{({ measureRef }) =>
<div ref={measureRef} id="mainContent-div" style={{ width: `calc(100% - ${flyoutWidth}px`, transform: `translate(${flyoutWidth}px, 0px)` }} onDrop={this.onDrop}>
@@ -323,6 +317,8 @@ export class MainView extends React.Component {
DataDoc={undefined}
addDocument={undefined}
addDocTab={emptyFunction}
+ pinToPres={emptyFunction}
+ onClick={undefined}
removeDocument={undefined}
ScreenToLocalTransform={Transform.Identity}
ContentScaling={returnOne}
@@ -339,7 +335,6 @@ export class MainView extends React.Component {
zoomToScale={emptyFunction}
getScale={returnOne}
/>}
- {castRes ? <PresentationView Documents={castRes} key="presentation" /> : null}
</div>
}
</Measure>;
@@ -386,7 +381,9 @@ export class MainView extends React.Component {
DataDoc={undefined}
addDocument={undefined}
addDocTab={this.addDocTabFunc}
+ pinToPres={emptyFunction}
removeDocument={undefined}
+ onClick={undefined}
ScreenToLocalTransform={Transform.Identity}
ContentScaling={returnOne}
PanelWidth={this.flyoutWidthFunc}
@@ -436,56 +433,41 @@ export class MainView extends React.Component {
}
}
-
@observable private _colorPickerDisplay = false;
/* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
nodesMenu() {
let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
- // let addDockingNode = action(() => Docs.Create.StandardCollectionDockingDocument([{ doc: addColNode(), initialWidth: 200 }], { width: 200, height: 200, title: "a nested docking freeform collection" }));
- let addSchemaNode = action(() => Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], [], { width: 200, height: 200, title: "a schema collection" }));
- //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" }));
- // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" }));
let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" }));
- let addTreeNode = action(() => CurrentUserUtils.UserDocument);
+ let addPresNode = action(() => Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" }));
+ let addWebNode = action(() => Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" }));
+ let addDragboxNode = action(() => Docs.Create.DragboxDocument({ width: 40, height: 40, title: "drag collection" }));
let addImageNode = action(() => Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" }));
let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 }));
- let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
- let addYoutubeSearcher = action(() => Docs.Create.YoutubeDocument(youtubeurl, { width: 600, height: 600, title: "youtube search" }));
+ // let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
+ // let addYoutubeSearcher = action(() => Docs.Create.YoutubeDocument(youtubeurl, { width: 600, height: 600, title: "youtube search" }));
let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
[React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
+ [React.createRef<HTMLDivElement>(), "tv", "Add Presentation Trail", addPresNode],
+ [React.createRef<HTMLDivElement>(), "globe-asia", "Add Website", addWebNode],
[React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument],
- // [React.createRef<HTMLDivElement>(), "clone", "Add Docking Frame", addDockingNode],
- [React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode],
- [React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher]
+ [React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode],
+ [React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode
+ //[React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher],
];
if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
- const setWriteMode = (mode: DocServer.WriteMode) => {
- console.log(DocServer.WriteMode[mode]);
- const mode1 = mode;
- const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
- DocServer.setFieldWriteMode("x", mode1);
- DocServer.setFieldWriteMode("y", mode1);
- DocServer.setFieldWriteMode("width", mode1);
- DocServer.setFieldWriteMode("height", mode1);
-
- DocServer.setFieldWriteMode("panX", mode2);
- DocServer.setFieldWriteMode("panY", mode2);
- DocServer.setFieldWriteMode("scale", mode2);
- DocServer.setFieldWriteMode("viewType", mode2);
- };
return < div id="add-nodes-menu" style={{ left: this.flyoutWidth + 20, bottom: 20 }} >
+
<input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
- <label htmlFor="add-menu-toggle" style={{ marginTop: 2 }} title="Add Node"><p>+</p></label>
+ <label htmlFor="add-menu-toggle" style={{ marginTop: 2 }} title="Close Menu"><p>+</p></label>
<div id="add-options-content">
<ul id="add-options-list">
<li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li>
- <li key="presentation"><button className="add-button round-button" title="Open Presentation View" onClick={() => PresentationView.Instance.toggle(undefined)}><FontAwesomeIcon icon="table" size="sm" /></button></li>
<li key="undo"><button className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li>
<li key="redo"><button className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li>
{btns.map(btn =>
@@ -494,13 +476,6 @@ export class MainView extends React.Component {
<FontAwesomeIcon icon={btn[1]} size="sm" />
</button>
</div></li>)}
- <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>
- {ClientUtils.RELEASE ? [] : [
- <li key="test"><button className="add-button round-button" title="Default" onClick={() => setWriteMode(DocServer.WriteMode.Default)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>,
- <li key="test1"><button className="add-button round-button" title="Playground" onClick={() => setWriteMode(DocServer.WriteMode.Playground)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>,
- <li key="test2"><button className="add-button round-button" title="Live Playground" onClick={() => setWriteMode(DocServer.WriteMode.LivePlayground)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>,
- <li key="test3"><button className="add-button round-button" title="Live Readonly" onClick={() => setWriteMode(DocServer.WriteMode.LiveReadonly)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>
- ]}
<li key="color"><button className="add-button round-button" title="Select Color" style={{ zIndex: 1000 }} onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
<div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
<SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
@@ -537,9 +512,8 @@ export class MainView extends React.Component {
}
@observable isSearchVisible = false;
- @action
+ @action.bound
toggleSearch = () => {
- // console.log("search toggling")
this.isSearchVisible = !this.isSearchVisible;
}
@@ -568,12 +542,21 @@ export class MainView extends React.Component {
);
}
+ @computed get miniPresentation() {
+ let next = () => PresBox.CurrentPresentation.next();
+ let back = () => PresBox.CurrentPresentation.back();
+ let startOrResetPres = () => PresBox.CurrentPresentation.startOrResetPres();
+ let closePresMode = action(() => { PresBox.CurrentPresentation.presMode = false; this.addDocTabFunc(PresBox.CurrentPresentation.props.Document); });
+ return !PresBox.CurrentPresentation || !PresBox.CurrentPresentation.presMode ? (null) : <PresModeMenu next={next} back={back} presStatus={PresBox.CurrentPresentation.presStatus} startOrResetPres={startOrResetPres} closePresMode={closePresMode} > </PresModeMenu>;
+ }
+
render() {
return (
<div id="main-div">
{this.dictationOverlay}
<DocumentDecorations />
{this.mainContent}
+ {this.miniPresentation}
<PreviewCursor />
<ContextMenu />
{this.nodesMenu()}
diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss
index bcfc9a82d..e28c4d0e0 100644
--- a/src/client/views/MetadataEntryMenu.scss
+++ b/src/client/views/MetadataEntryMenu.scss
@@ -1,8 +1,20 @@
.metadataEntry-outerDiv {
display: flex;
+ flex-direction: column;
width: 300px;
}
+.metadataEntry-keys {
+ max-height: 80;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+}
+.metadataEntry-inputArea {
+ display:flex;
+ flex-direction: row;
+}
+
.react-autosuggest__container {
position: relative;
}
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 36c240dd8..4a45eede9 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -1,11 +1,12 @@
import * as React from 'react';
import "./MetadataEntryMenu.scss";
import { observer } from 'mobx-react';
-import { observable, action, runInAction, trace } from 'mobx';
+import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx';
import { KeyValueBox } from './nodes/KeyValueBox';
import { Doc, Field } from '../../new_fields/Doc';
import * as Autosuggest from 'react-autosuggest';
import { undoBatch } from '../util/UndoManager';
+import { emptyFunction } from '../../Utils';
export type DocLike = Doc | Doc[] | Promise<Doc> | Promise<Doc[]>;
export interface MetadataEntryProps {
@@ -18,7 +19,8 @@ export interface MetadataEntryProps {
export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
@observable private _currentKey: string = "";
@observable private _currentValue: string = "";
- @observable private suggestions: string[] = [];
+ @observable _allSuggestions: string[] = [];
+ _suggestionDispser: IReactionDisposer | undefined;
private userModified = false;
private autosuggestRef = React.createRef<Autosuggest>();
@@ -140,35 +142,39 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
getSuggestionValue = (suggestion: string) => suggestion;
renderSuggestion = (suggestion: string) => {
- return <p>{suggestion}</p>;
+ return (null);
}
+ componentDidMount() {
- onSuggestionFetch = async ({ value }: { value: string }) => {
- const sugg = await this.getKeySuggestions(value);
- runInAction(() => {
- this.suggestions = sugg;
- });
+ this._suggestionDispser = reaction(() => this._currentKey,
+ () => this.getKeySuggestions(this._currentKey).then(action((s: string[]) => this._allSuggestions = s)),
+ { fireImmediately: true });
}
-
- @action
- onSuggestionClear = () => {
- this.suggestions = [];
+ componentWillUnmount() {
+ this._suggestionDispser && this._suggestionDispser();
}
render() {
return (
<div className="metadataEntry-outerDiv">
- Key:
- <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
- getSuggestionValue={this.getSuggestionValue}
- suggestions={this.suggestions}
- alwaysRenderSuggestions
- renderSuggestion={this.renderSuggestion}
- onSuggestionsFetchRequested={this.onSuggestionFetch}
- onSuggestionsClearRequested={this.onSuggestionClear}
- ref={this.autosuggestRef} />
- Value:
- <input className="metadataEntry-input" value={this._currentValue} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
+ <div className="metadataEntry-inputArea">
+ Key:
+ <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
+ getSuggestionValue={this.getSuggestionValue}
+ suggestions={[]}
+ alwaysRenderSuggestions={false}
+ renderSuggestion={this.renderSuggestion}
+ onSuggestionsFetchRequested={emptyFunction}
+ onSuggestionsClearRequested={emptyFunction}
+ ref={this.autosuggestRef} />
+ Value:
+ <input className="metadataEntry-input" value={this._currentValue} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
+ </div>
+ <div className="metadataEntry-keys" >
+ <ul>
+ {this._allSuggestions.map(s => <li key={s} onClick={action(() => { this._currentKey = s; this.previewValue(); })} >{s}</li>)}
+ </ul>
+ </div>
</div>
);
}
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index d073945e5..2b862a81e 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -5,7 +5,11 @@ import { observable, action } from "mobx";
import "./ScriptBox.scss";
import { OverlayView } from "./OverlayView";
import { DocumentIconContainer } from "./nodes/DocumentIcon";
-import { Opt } from "../../new_fields/Doc";
+import { Opt, Doc } from "../../new_fields/Doc";
+import { emptyFunction } from "../../Utils";
+import { ScriptCast } from "../../new_fields/Types";
+import { CompileScript } from "../util/Scripting";
+import { ScriptField } from "../../new_fields/ScriptField";
export interface ScriptBoxProps {
onSave: (text: string, onError: (error: string) => void) => void;
@@ -62,4 +66,26 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
</div>
);
}
-} \ No newline at end of file
+ public static EditClickScript(doc: Doc, fieldKey: string) {
+ let overlayDisposer: () => void = emptyFunction;
+ const script = ScriptCast(doc[fieldKey]);
+ let originalText: string | undefined = undefined;
+ if (script) originalText = script.script.originalScript;
+ // tslint:disable-next-line: no-unnecessary-callback-wrapper
+ let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
+ const script = CompileScript(text, {
+ params: { this: Doc.name },
+ typecheck: false,
+ editable: true,
+ transformer: DocumentIconContainer.getTransformer()
+ });
+ if (!script.compiled) {
+ onError(script.errors.map(error => error.messageText).join("\n"));
+ return;
+ }
+ doc[fieldKey] = new ScriptField(script);
+ overlayDisposer();
+ }} showDocumentIcons />;
+ overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${doc.title || ""} OnClick` });
+ }
+}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index cad87ebcc..b6ed6aaa0 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -11,6 +11,7 @@ import { SelectionManager } from '../../util/SelectionManager';
import { ContextMenu } from '../ContextMenu';
import { FieldViewProps } from '../nodes/FieldView';
import './CollectionBaseView.scss';
+import { DateField } from '../../../new_fields/DateField';
export enum CollectionViewType {
Invalid,
@@ -83,7 +84,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
active = (): boolean => {
var isSelected = this.props.isSelected();
- return isSelected || this._isChildActive || this.props.renderDepth === 0 || BoolCast(this.props.Document.excludeFromLibrary);
+ return isSelected || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0 || BoolCast(this.props.Document.excludeFromLibrary);
}
//TODO should this be observable?
@@ -113,6 +114,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
} else {
Doc.GetProto(targetDataDoc)[targetField] = new List([doc]);
}
+ Doc.GetProto(doc).lastOpened = new DateField;
return true;
}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 77b698a07..dc0cbda0b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -28,6 +28,9 @@ import { library } from '@fortawesome/fontawesome-svg-core';
import { faFile, faUnlockAlt } from '@fortawesome/free-solid-svg-icons';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
import { Docs } from '../../documents/Documents';
+import { DateField } from '../../../new_fields/DateField';
+import { List } from '../../../new_fields/List';
+import { DocumentType } from '../../documents/DocumentTypes';
library.add(faFile);
@observer
@@ -161,6 +164,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.stateChanged();
}
+ public Has = (document: Doc) => {
+ let docs = Cast(this.props.Document.data, listSpec(Doc));
+ if (!docs) {
+ return false;
+ }
+ return docs.includes(document);
+ }
+
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@@ -204,6 +215,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
@action
public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
+ Doc.GetProto(document).lastOpened = new DateField;
let docs = Cast(this.props.Document.data, listSpec(Doc));
if (docs) {
docs.push(document);
@@ -389,7 +401,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
const stack = tab.contentItem.parent;
// shifts the focus to this tab when another tab is dragged over it
tab.element[0].onmouseenter = (e: any) => {
- if (!this._isPointerDown) return;
+ if (!this._isPointerDown || !SelectionManager.GetIsDragging()) return;
var activeContentItem = tab.header.parent.getActiveContentItem();
if (tab.contentItem !== activeContentItem) {
tab.header.parent.setActiveContentItem(tab.contentItem);
@@ -533,6 +545,27 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}));
}
+ /**
+ * Adds a document to the presentation view
+ **/
+ @undoBatch
+ @action
+ public PinDoc(doc: Doc) {
+ //add this new doc to props.Document
+ let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
+ if (curPres) {
+ const data = Cast(curPres.data, listSpec(Doc));
+ if (data) {
+ data.push(doc);
+ } else {
+ curPres.data = new List([doc]);
+ }
+ if (!DocumentManager.Instance.getDocumentView(curPres)) {
+ this.addDocTab(curPres, undefined, "onRight");
+ }
+ }
+ }
+
componentDidMount() {
this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
this.props.glContainer.on("tab", this.onActiveContentItemChanged);
@@ -552,11 +585,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
}
- panelWidth = () => Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth()));
- panelHeight = () => Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), NumCast(this._document!.nativeHeight, this._panelHeight)));
+ panelWidth = () => this._document!.ignoreAspect ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth()));
+ panelHeight = () => this._document!.ignoreAspect ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), NumCast(this._document!.nativeHeight, this._panelHeight)));
- nativeWidth = () => !BoolCast(this._document!.ignoreAspect) ? NumCast(this._document!.nativeWidth, this._panelWidth) : 0;
- nativeHeight = () => !BoolCast(this._document!.ignoreAspect) ? NumCast(this._document!.nativeHeight, this._panelHeight) : 0;
+ nativeWidth = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0;
+ nativeHeight = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0;
contentScaling = () => {
const nativeH = this.nativeHeight();
@@ -567,7 +600,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
ScreenToLocalTransform = () => {
- if (this._mainCont && this._mainCont!.children) {
+ if (this._mainCont && this._mainCont.children) {
let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement);
scale = Utils.GetScreenTransform(this._mainCont).scale;
return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
@@ -581,6 +614,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
MainView.Instance.openWorkspace(doc);
} else if (location === "onRight") {
CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
+ } else if (location === "close") {
+ CollectionDockingView.Instance.CloseRightSplit(doc);
} else {
CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
}
@@ -607,6 +642,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
focus={emptyFunction}
backgroundColor={returnEmptyString}
addDocTab={this.addDocTab}
+ pinToPres={this.PinDoc}
ContainingCollectionView={undefined}
zoomToScale={emptyFunction}
getScale={returnOne} />;
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 7e3061354..551b485e7 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -40,6 +40,7 @@ export interface CellProps {
fieldKey: string;
renderDepth: number;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+ pinToPres: (document: Doc) => void;
moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
isFocused: boolean;
changeFocusedCellByIndex: (row: number, col: number) => void;
@@ -160,6 +161,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
PanelHeight: returnZero,
PanelWidth: returnZero,
addDocTab: this.props.addDocTab,
+ pinToPres: this.props.pinToPres,
ContentScaling: returnOne
};
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 01744fb34..e0cedc210 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -22,30 +22,6 @@
overflow: scroll;
}
- .collectionSchemaView-previewRegion {
- position: relative;
- background: $light-color;
- height: 100%;
-
- .collectionSchemaView-previewDoc {
- height: 100%;
- width: 100%;
- position: absolute;
- }
-
- .collectionSchemaView-input {
- position: absolute;
- max-width: 150px;
- width: 100%;
- bottom: 0px;
- }
-
- .documentView-node:first-child {
- position: relative;
- background: $light-color;
- }
- }
-
.collectionSchemaView-dividerDragger {
position: relative;
height: 100%;
@@ -62,6 +38,30 @@
}
}
+.collectionSchemaView-previewRegion {
+ position: relative;
+ background: $light-color;
+ height: auto !important;
+
+ .collectionSchemaView-previewDoc {
+ height: 100%;
+ width: 100%;
+ position: absolute;
+ }
+
+ .collectionSchemaView-input {
+ position: absolute;
+ max-width: 150px;
+ width: 100%;
+ bottom: 0px;
+ }
+
+ .documentView-node:first-child {
+ position: relative;
+ background: $light-color;
+ }
+}
+
.ReactTable {
width: 100%;
background: white;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 897796174..4008dea51 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -30,7 +30,7 @@ import { undoBatch } from "../../util/UndoManager";
import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders";
import { CellProps, CollectionSchemaCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell } from "./CollectionSchemaCells";
import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
-import { ComputedField } from "../../../new_fields/ScriptField";
+import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
@@ -171,6 +171,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
active={this.props.active}
whenActiveChanged={this.props.whenActiveChanged}
addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
setPreviewScript={this.setPreviewScript}
previewScript={this.previewScript}
/>
@@ -200,6 +201,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
active={this.props.active}
onDrop={this.onDrop}
addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
isSelected={this.props.isSelected}
isFocused={this.isFocused}
setFocused={this.setFocused}
@@ -251,6 +253,7 @@ export interface SchemaTableProps {
active: () => boolean;
onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+ pinToPres: (document: Doc) => void;
isSelected: () => boolean;
isFocused: (document: Doc) => boolean;
setFocused: (document: Doc) => void;
@@ -377,6 +380,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
fieldKey: this.props.fieldKey,
renderDepth: this.props.renderDepth,
addDocTab: this.props.addDocTab,
+ pinToPres: this.props.pinToPres,
moveDocument: this.props.moveDocument,
setIsEditing: this.setCellIsEditing,
isEditable: isEditable,
@@ -899,6 +903,7 @@ interface CollectionSchemaPreviewProps {
height: () => number;
showOverlays?: (doc: Doc) => { title?: string, caption?: string };
CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView;
+ onClick?: ScriptField;
getTransform: () => Transform;
addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean;
@@ -906,6 +911,7 @@ interface CollectionSchemaPreviewProps {
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+ pinToPres: (document: Doc) => void;
setPreviewScript: (script: string) => void;
previewScript?: string;
}
@@ -988,23 +994,25 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
DataDoc={this.props.DataDocument}
Document={this.props.Document}
fitToBox={this.props.fitToBox}
- renderDepth={this.props.renderDepth + 1}
- selectOnLoad={false}
+ onClick={this.props.onClick}
showOverlays={this.props.showOverlays}
addDocument={this.props.addDocument}
removeDocument={this.props.removeDocument}
moveDocument={this.props.moveDocument}
+ whenActiveChanged={this.props.whenActiveChanged}
+ ContainingCollectionView={this.props.CollectionView}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ parentActive={this.props.active}
ScreenToLocalTransform={this.getTransform}
+ renderDepth={this.props.renderDepth + 1}
+ selectOnLoad={false}
ContentScaling={this.contentScaling}
PanelWidth={this.PanelWidth}
PanelHeight={this.PanelHeight}
- ContainingCollectionView={this.props.CollectionView}
focus={emptyFunction}
backgroundColor={returnEmptyString}
- parentActive={this.props.active}
- whenActiveChanged={this.props.whenActiveChanged}
bringToFront={emptyFunction}
- addDocTab={this.props.addDocTab}
zoomToScale={emptyFunction}
getScale={returnOne}
/>
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 271ad2d58..01d4ea2b6 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -1,10 +1,15 @@
@import "../globalCssVariables";
-.collectionStackingView {
+.collectionMasonryView {
+ display:inline;
+}
+.collectionStackingView{
+ display: flex;
+}
+.collectionStackingView, .collectionMasonryView{
height: 100%;
width: 100%;
position: absolute;
- display: flex;
top: 0;
overflow-y: auto;
flex-wrap: wrap;
@@ -31,14 +36,20 @@
.collectionStackingView-masonrySingle,
.collectionStackingView-masonryGrid {
width: 100%;
- height: 100%;
- position: absolute;
display: grid;
top: 0;
left: 0;
- width: 100%;
+ }
+ .collectionStackingView-masonrySingle {
+ height: 100%;
position: absolute;
}
+ .collectionStackingView-masonryGrid {
+ margin: auto;
+ height: max-content;
+ position: relative;
+ grid-auto-rows: 0px;
+ }
.collectionStackingView-masonrySingle {
width: 100%;
@@ -80,12 +91,17 @@
height: 100%;
margin: auto;
}
+
+ .collectionStackingView-masonrySection {
+ margin: auto;
+ }
.collectionStackingView-sectionHeader {
text-align: center;
margin-left: 2px;
margin-right: 2px;
margin-top: 10px;
+ background: gray;
// overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
.editableView-input {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index b87be5d68..c74c60d8f 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -9,9 +9,9 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction } from "../../../Utils";
-import { DocumentType } from "../../documents/Documents";
+import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types";
+import { emptyFunction, Utils, numberRange } from "../../../Utils";
+import { DocumentType } from "../../documents/DocumentTypes";
import { DragManager } from "../../util/DragManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
@@ -20,24 +20,35 @@ import { CollectionSchemaPreview } from "./CollectionSchemaView";
import "./CollectionStackingView.scss";
import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
import { CollectionSubView } from "./CollectionSubView";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { ScriptBox } from "../ScriptBox";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
_masonryGridRef: HTMLDivElement | null = null;
_draggerRef = React.createRef<HTMLDivElement>();
_heightDisposer?: IReactionDisposer;
+ _childLayoutDisposer?: IReactionDisposer;
_sectionFilterDisposer?: IReactionDisposer;
_docXfs: any[] = [];
_columnStart: number = 0;
@observable private cursor: CursorProperty = "grab";
- get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
+ @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
+ @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
+ @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); }
@computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); }
@computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); }
@computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); }
- @computed get singleColumn() { return BoolCast(this.props.Document.singleColumn, true); }
- @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); }
- @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); }
- @computed get sectionFilter() { return this.singleColumn ? StrCast(this.props.Document.sectionFilter) : ""; }
+ @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
+ @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
+ @computed get showAddAGroup() { return (this.sectionFilter && (this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled')); }
+ @computed get columnWidth() {
+ return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin,
+ this.isStackingView ? Number.MAX_VALUE : NumCast(this.props.Document.columnWidth, 250));
+ }
+
+ childDocHeight(child: Doc) { return this.getDocHeight(Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, child).layout); }
get layoutDoc() {
// if this document's layout field contains a document (ie, a rendering template), then we will use that
@@ -45,14 +56,14 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
}
-
get Sections() {
- if (!this.sectionFilter) return new Map<SchemaHeaderField, Doc[]>();
+ if (!this.sectionFilter || this.sectionHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
if (this.sectionHeaders === undefined) {
- this.props.Document.sectionHeaders = new List<SchemaHeaderField>();
+ setTimeout(() => this.props.Document.sectionHeaders = new List<SchemaHeaderField>(), 0);
+ return new Map<SchemaHeaderField, Doc[]>();
}
- const sectionHeaders = this.sectionHeaders!;
+ const sectionHeaders = this.sectionHeaders;
let fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
this.filteredChildren.map(d => {
let sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object;
@@ -75,18 +86,26 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
componentDidMount() {
- // is there any reason this needs to exist? -syip
- this._heightDisposer = reaction(() => [this.props.Document.autoHeight, this.yMargin, this.props.Document[WidthSym](), this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])],
- () => {
- if (this.singleColumn && BoolCast(this.props.Document.autoHeight)) {
- let hgt = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => {
- let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
- return height + this.getDocHeight(pair.layout) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap);
- }, this.yMargin);
- (this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc)
- .height = hgt * (this.props as any).ContentScaling();
- }
- }, { fireImmediately: true });
+ this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)],
+ async (args) => args[1] instanceof Doc &&
+ this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined)));
+
+ // is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported).
+ this._heightDisposer = reaction(() => {
+ if (this.isStackingView && BoolCast(this.props.Document.autoHeight)) {
+ let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]);
+ return this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => Math.max(maxHght,
+ (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin)
+ ), 0);
+ }
+ return -1;
+ },
+ (hgt: number) => {
+ let doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
+ doc && (doc.height = hgt);
+ },
+ { fireImmediately: true }
+ );
// reset section headers when a new filter is inputted
this._sectionFilterDisposer = reaction(
@@ -95,6 +114,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
);
}
componentWillUnmount() {
+ this._childLayoutDisposer && this._childLayoutDisposer();
this._heightDisposer && this._heightDisposer();
this._sectionFilterDisposer && this._sectionFilterDisposer();
}
@@ -109,9 +129,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
overlays = (doc: Doc) => {
- return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: "title", caption: "caption" } : {};
+ return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: StrCast(this.props.Document.showTitles), caption: StrCast(this.props.Document.showCaptions) } : {};
}
+ @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+ @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : ScriptCast(this.Document.onChildClick); }
+
getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
let height = () => this.getDocHeight(layoutDoc);
let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
@@ -120,7 +143,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
DataDocument={dataDoc}
showOverlays={this.overlays}
renderDepth={this.props.renderDepth}
- fitToBox={true}
+ fitToBox={this.props.fitToBox}
+ onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler}
width={width}
height={height}
getTransform={finalDxf}
@@ -131,16 +155,19 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
active={this.props.active}
whenActiveChanged={this.props.whenActiveChanged}
addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
setPreviewScript={emptyFunction}
previewScript={undefined}>
</CollectionSchemaPreview>;
}
- getDocHeight(d: Doc, columnScale: number = 1) {
+ getDocHeight(d?: Doc) {
+ if (!d) return 0;
let nw = NumCast(d.nativeWidth);
let nh = NumCast(d.nativeHeight);
- if (!BoolCast(d.ignoreAspect) && nw && nh) {
+ if (!d.ignoreAspect && nw && nh) {
let aspect = nw && nh ? nh / nw : 1;
- let wid = Math.min(d[WidthSym](), this.columnWidth / columnScale);
+ let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
+ if (!(d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(d[WidthSym](), wid);
return wid * aspect;
}
return d[HeightSym]();
@@ -226,14 +253,14 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
});
}
headings = () => Array.from(this.Sections.keys());
- section = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
+ sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
let key = this.sectionFilter;
let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
type = types[0];
}
- let cols = () => this.singleColumn ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
+ let cols = () => this.isStackingView ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
return <CollectionStackingViewFieldColumn
key={heading ? heading.heading : ""}
@@ -249,6 +276,55 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
/>;
}
+ 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);
+ }
+ masonryChildren(docs: Doc[]) {
+ this._docXfs.length = 0;
+ return docs.map((d, i) => {
+ let dref = React.createRef<HTMLDivElement>();
+ let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
+ let width = () => (d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth);/// (uniqueHeadings.length + 1);
+ let height = () => this.getDocHeight(layoutDoc);
+ let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
+ let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
+ this._docXfs.push({ dxf: dxf, width: width, height: height });
+ return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
+ {this.getDisplayDoc(layoutDoc, d, dxf, width)}
+ </div>;
+ });
+ }
+
+ @observable _headingsHack: number = 1;
+ sectionMasonry(heading: SchemaHeaderField | undefined, docList: Doc[]) {
+ let cols = Math.max(1, Math.min(docList.length,
+ Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
+ return <div key={heading ? heading.heading : "empty"} className="collectionStackingView-masonrySection">
+ {!heading ? (null) :
+ <div key={`${heading.heading}`} className="collectionStackingView-sectionHeader" style={{ background: heading.color }}
+ onClick={action(() => this._headingsHack++ && heading.setCollapsed(!heading.collapsed))} >
+ {heading.heading}
+ </div>}
+ {this._headingsHack && heading && heading.collapsed ? (null) :
+ <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
+ style={{
+ padding: `${this.yMargin}px ${this.xMargin}px`,
+ width: `${cols * (this.columnWidth + this.gridGap) + 2 * this.xMargin - this.gridGap}px`,
+ gridGap: this.gridGap,
+ gridTemplateColumns: numberRange(cols).reduce((list, i) => list + ` ${this.columnWidth}px`, ""),
+ }}>
+ {this.masonryChildren(docList)}
+ {this.columnDragger}
+ </div>
+ }
+ </div>;
+ }
+
@action
addGroup = (value: string) => {
if (value && this.sectionHeaders) {
@@ -266,11 +342,22 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
onToggle = (checked: Boolean) => {
- this.props.CollectionView.props.Document.chromeSatus = checked ? "collapsed" : "view-mode";
+ this.props.CollectionView.props.Document.chromeStatus = checked ? "collapsed" : "view-mode";
+ }
+
+ onContextMenu = (e: React.MouseEvent): void => {
+ // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
+ if (!e.isPropagationStopped()) {
+ let subItems: ContextMenuProps[] = [];
+ subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
+ subItems.push({ description: `${this.props.Document.showTitles ? "Hide Titles" : "Show Titles"}`, event: () => this.props.Document.showTitles = !this.props.Document.showTitles ? "title" : "", icon: "plus" });
+ subItems.push({ description: `${this.props.Document.showCaptions ? "Hide Captions" : "Show Captions"}`, event: () => this.props.Document.showCaptions = !this.props.Document.showCaptions ? "caption" : "", icon: "plus" });
+ subItems.push({ description: "Edit onChildClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onChildClick") });
+ ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" });
+ }
}
render() {
- let headings = Array.from(this.Sections.keys());
let editableViewProps = {
GetValue: () => "",
SetValue: this.addGroup,
@@ -278,18 +365,16 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
};
Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
- // let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
+ let sections = (this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc) : [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]]);
return (
- <div className="collectionStackingView"
- ref={this.createRef} onDrop={this.onDrop.bind(this)} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
- {this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc).
- map((section: [SchemaHeaderField, Doc[]]) => this.section(section[0], section[1])) :
- this.section(undefined, this.filteredChildren)}
- {(this.sectionFilter && (this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled')) ?
+ <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
+ ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
+ {sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]))}
+ {!this.showAddAGroup ? (null) :
<div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
- style={{ width: (this.columnWidth / (headings.length + ((this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? 1 : 0))) - 10, marginTop: 10 }}>
+ style={{ width: this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
<EditableView {...editableViewProps} />
- </div> : null}
+ </div>}
{this.props.CollectionView.props.Document.chromeStatus !== 'disabled' ? <Switch
onChange={this.onToggle}
onClick={this.onToggle}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index df03da376..cc8476548 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -1,28 +1,25 @@
import React = require("react");
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faPalette } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { number } from "prop-types";
import { Doc, WidthSym } from "../../../new_fields/Doc";
-import { CollectionStackingView } from "./CollectionStackingView";
import { Id } from "../../../new_fields/FieldSymbols";
+import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { NumCast, StrCast } from "../../../new_fields/Types";
import { Utils } from "../../../Utils";
-import { NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { EditableView } from "../EditableView";
-import { action, observable, computed } from "mobx";
-import { undoBatch } from "../../util/UndoManager";
-import { DragManager } from "../../util/DragManager";
-import { DocumentManager } from "../../util/DocumentManager";
-import { SelectionManager } from "../../util/SelectionManager";
-import "./CollectionStackingView.scss";
import { Docs } from "../../documents/Documents";
-import { SchemaHeaderField, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { ScriptField } from "../../../new_fields/ScriptField";
+import { DragManager } from "../../util/DragManager";
import { CompileScript } from "../../util/Scripting";
-import { RichTextField } from "../../../new_fields/RichTextField";
+import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
-import { Flyout, anchorPoints } from "../DocumentDecorations";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faPalette } from '@fortawesome/free-solid-svg-icons';
+import { undoBatch } from "../../util/UndoManager";
+import { anchorPoints, Flyout } from "../DocumentDecorations";
+import { EditableView } from "../EditableView";
+import { CollectionStackingView } from "./CollectionStackingView";
+import "./CollectionStackingView.scss";
library.add(faPalette);
@@ -81,38 +78,20 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
let parent = this.props.parent;
parent._docXfs.length = 0;
return docs.map((d, i) => {
- let headings = this.props.headings();
- let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
- let pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
- let width = () => (d.nativeWidth && !BoolCast(d.ignoreAspect) ? Math.min(pair.layout[WidthSym](), parent.columnWidth / (uniqueHeadings.length + 1)) : parent.columnWidth / (uniqueHeadings.length + 1));/// (uniqueHeadings.length + 1);
- let height = () => parent.getDocHeight(pair.layout, uniqueHeadings.length + 1);// / (d.nativeWidth && !BoolCast(d.ignoreAspect) ? uniqueHeadings.length + 1 : 1);
+ const pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
+ if (!pair.layout || pair.data instanceof Promise) {
+ return (null);
+ }
+ let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, parent.columnWidth / parent.numGroupColumns);
+ let height = () => parent.getDocHeight(pair!.layout);
let dref = React.createRef<HTMLDivElement>();
- // if (uniqueHeadings.length > 0) {
- let dxf = () => this.getDocTransform(pair.layout, dref.current!);
+ let dxf = () => this.getDocTransform(pair!.layout, dref.current!);
this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height });
- // }
- // else {
- // //have to add the height of all previous single column sections or the doc decorations will be in the wrong place.
- // let dxf = () => this.getDocTransform(layoutDoc, i, width());
- // this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height });
- // }
- let rowHgtPcnt = height();
let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap);
- let style = parent.singleColumn ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : parent.gridGap, height: `${rowHgtPcnt}` } : { gridRowEnd: `span ${rowSpan}` };
- return <div className={`collectionStackingView-${parent.singleColumn ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
+ let style = parent.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : parent.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+ return <div className={`collectionStackingView-${parent.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
{this.props.parent.getDisplayDoc(pair.layout, pair.data, dxf, width)}
</div>;
- // } else {
- // let dref = React.createRef<HTMLDivElement>();
- // let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
- // this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height });
- // let rowHgtPcnt = height();
- // let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap);
- // let divStyle = parent.singleColumn ? { width: width(), marginTop: i === 0 ? 0 : parent.gridGap, height: `${rowHgtPcnt}` } : { gridRowEnd: `span ${rowSpan}` };
- // return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={divStyle} >
- // {this.props.parent.getDisplayDoc(layoutDoc, d, dxf, width)}
- // </div>;
- // }
});
}
@@ -278,7 +257,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
let headings = this.props.headings();
let heading = this._heading;
let style = this.props.parent;
- let singleColumn = style.singleColumn;
+ let singleColumn = style.isStackingView;
let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
let evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
let headerEditableViewProps = {
@@ -326,9 +305,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
</button>}
</div>
</div> : (null);
- for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth}px `;
+ for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth / style.numGroupColumns}px `;
return (
- <div key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }}
+ <div className="collectionStackingViewFieldColumn" key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }}
ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
{headingView}
<div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`}
@@ -348,7 +327,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
</div>
{(this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ?
<div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
- style={{ width: style.columnWidth / (uniqueHeadings.length + 1) }}>
+ style={{ width: style.columnWidth / style.numGroupColumns }}>
<EditableView {...newEditableViewProps} />
</div> : null}
</div>
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 70c0632d1..3deec22b5 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -11,7 +11,8 @@ import { CurrentUserUtils } from "../../../server/authentication/models/current_
import { RouteStore } from "../../../server/RouteStore";
import { Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
-import { Docs, DocumentOptions, DocumentType } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { Docs, DocumentOptions } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { DocComponent } from "../DocComponent";
@@ -82,7 +83,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
let ind;
let doc = this.props.Document;
let id = CurrentUserUtils.id;
- let email = CurrentUserUtils.email;
+ let email = Doc.CurrentUserEmail;
let pos = { x: position[0], y: position[1] };
if (id && email) {
const proto = Doc.GetProto(doc);
@@ -112,7 +113,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
- if (de.data instanceof DragManager.DocumentDragData) {
+ if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) {
if (de.mods === "AltKey" && de.data.draggedDocuments.length) {
this.childDocs.map(doc =>
Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, undefined)
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 24bd24d11..ebd385743 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -9,7 +9,8 @@ import { List } from '../../../new_fields/List';
import { Document, listSpec } from '../../../new_fields/Schema';
import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types';
import { emptyFunction, Utils } from '../../../Utils';
-import { Docs, DocUtils, DocumentType } from '../../documents/Documents';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
import { SelectionManager } from '../../util/SelectionManager';
@@ -25,7 +26,7 @@ import { CollectionSchemaPreview } from './CollectionSchemaView';
import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTreeView.scss";
import React = require("react");
-import { ComputedField } from '../../../new_fields/ScriptField';
+import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
import { KeyValueBox } from '../nodes/KeyValueBox';
@@ -38,6 +39,7 @@ export interface TreeViewProps {
moveDocument: DragManager.MoveFunction;
dropAction: "alias" | "copy" | undefined;
addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
+ pinToPres: (document: Doc) => void;
panelWidth: () => number;
panelHeight: () => number;
addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
@@ -252,16 +254,16 @@ class TreeView extends React.Component<TreeViewProps> {
doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
let rows: JSX.Element[] = [];
- for (let key of Object.keys(ids).sort()) {
+ for (let key of Object.keys(ids).slice().sort()) {
let contents = doc[key];
let contentElement: JSX.Element[] | JSX.Element = [];
if (contents instanceof Doc || Cast(contents, listSpec(Doc))) {
let remDoc = (doc: Doc) => this.remove(doc, key);
- let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before);
+ let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, !BoolCast(this.props.document.stackingHeadersSortDescending));
contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
DocListCast(contents), this.props.treeViewId, doc, undefined, key, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth);
+ this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth);
} else {
contentElement = <EditableView
key="editableView"
@@ -286,13 +288,13 @@ class TreeView extends React.Component<TreeViewProps> {
const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined;
if (expandKey !== undefined) {
let remDoc = (doc: Doc) => this.remove(doc, expandKey);
- let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before);
+ let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, !BoolCast(this.props.document.stackingHeadersSortDescending));
let docs = expandKey === "links" ? this.childLinks : this.childDocs;
return <ul key={expandKey + "more"}>
{!docs ? (null) :
TreeView.GetChildElements(docs as Doc[], this.props.treeViewId, this.props.document.layout as Doc,
this.resolvedDataDoc, expandKey, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform,
+ this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
@@ -318,6 +320,7 @@ class TreeView extends React.Component<TreeViewProps> {
active={this.props.active}
whenActiveChanged={emptyFunction as any}
addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
setPreviewScript={emptyFunction}>
</CollectionSchemaPreview>
</div>;
@@ -394,27 +397,66 @@ class TreeView extends React.Component<TreeViewProps> {
move: DragManager.MoveFunction,
dropAction: dropActionType,
addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void,
+ pinToPres: (document: Doc) => void,
screenToLocalXf: () => Transform,
outerXf: () => { translateX: number, translateY: number },
active: () => boolean,
panelWidth: () => number,
renderDepth: number
) {
- let docList = docs.filter(child => !child.excludeFromLibrary);
+ let viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
+ if (viewSpecScript) {
+ let script = viewSpecScript.script;
+ docs = docs.filter(d => {
+ let res = script.run({ doc: d });
+ if (res.success) {
+ return res.result;
+ }
+ else {
+ console.log(res.error);
+ }
+ });
+ }
+
+ let descending = BoolCast(containingCollection.stackingHeadersSortDescending);
+ docs.slice().sort(function (a, b): 1 | -1 {
+ let descA = descending ? b : a;
+ let descB = descending ? a : b;
+ let first = descA[String(containingCollection.sectionFilter)];
+ let second = descB[String(containingCollection.sectionFilter)];
+ // TODO find better way to sort how to sort..................
+ if (typeof first === 'number' && typeof second === 'number') {
+ return (first - second) > 0 ? 1 : -1;
+ }
+ if (typeof first === 'string' && typeof second === 'string') {
+ return first > second ? 1 : -1;
+ }
+ if (typeof first === 'boolean' && typeof second === 'boolean') {
+ // if (first === second) { // bugfixing?: otherwise, the list "flickers" because the list is resorted during every load
+ // return Number(descA.x) > Number(descB.x) ? 1 : -1;
+ // }
+ return first > second ? 1 : -1;
+ }
+ return descending ? 1 : -1;
+ });
+
let rowWidth = () => panelWidth() - 20;
- return docList.map((child, i) => {
+ return docs.map((child, i) => {
let pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child);
+ if (!pair.layout || pair.data instanceof Promise) {
+ return (null);
+ }
let indent = i === 0 ? undefined : () => {
- if (StrCast(docList[i - 1].layout).indexOf("CollectionView") !== -1) {
- let fieldKeysub = StrCast(docList[i - 1].layout).split("fieldKey")[1];
+ if (StrCast(docs[i - 1].layout).indexOf("CollectionView") !== -1) {
+ let fieldKeysub = StrCast(docs[i - 1].layout).split("fieldKey")[1];
let fieldKey = fieldKeysub.split("\"")[1];
- Doc.AddDocToList(docList[i - 1], fieldKey, child);
+ Doc.AddDocToList(docs[i - 1], fieldKey, child);
remove(child);
}
};
let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
- return add(doc, relativeTo ? relativeTo : docList[i], before !== undefined ? before : false);
+ return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false);
};
let rowHeight = () => {
let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0);
@@ -435,6 +477,7 @@ class TreeView extends React.Component<TreeViewProps> {
moveDocument={move}
dropAction={dropAction}
addDocTab={addDocTab}
+ pinToPres={pinToPres}
ScreenToLocalTransform={screenToLocalXf}
outerXf={outerXf}
parentKey={key}
@@ -518,7 +561,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
render() {
Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
- let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
+ let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, !BoolCast(this.props.Document.stackingHeadersSortDescending));
let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
return !this.childDocs ? (null) : (
<div id="body" className="collectionTreeView-dropTarget"
@@ -538,14 +581,14 @@ export class CollectionTreeView extends CollectionSubView(Document) {
let doc = this.props.Document.detailedLayout instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.detailedLayout)) : undefined;
if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
TreeView.loadId = doc[Id];
- Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true);
+ Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true, !BoolCast(this.props.Document.stackingHeadersSortDescending));
}} />
{this.props.Document.workspaceLibrary ? this.renderNotifsButton : (null)}
{this.props.Document.allowClear ? this.renderClearButton : (null)}
<ul className="no-indent" style={{ width: "max-content" }} >
{
TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove,
- moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth)
+ moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth)
}
</ul>
</div >
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7a402798e..3a8253720 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -30,6 +30,10 @@ export class CollectionView extends React.Component<FieldViewProps> {
public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); }
+ constructor(props: any) {
+ super(props);
+ }
+
componentDidMount = () => {
this._reactionDisposer = reaction(() => StrCast(this.props.Document.chromeStatus),
() => {
@@ -65,7 +69,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
@action
private collapse = (value: boolean) => {
this._collapsed = value;
- this.props.Document.chromeStatus = value ? "collapsed" : "visible";
+ this.props.Document.chromeStatus = value ? "collapsed" : "enabled";
}
private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
@@ -86,12 +90,7 @@ 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
let subItems: ContextMenuProps[] = [];
- subItems.push({
- description: "Freeform", event: () => {
- this.props.Document.viewType = CollectionViewType.Freeform;
- delete this.props.Document.usePivotLayout;
- }, icon: "signature"
- });
+ subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; delete this.props.Document.usePivotLayout; }, icon: "signature" });
if (CollectionBaseView.InSafeMode()) {
ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" });
}
@@ -107,10 +106,15 @@ export class CollectionView extends React.Component<FieldViewProps> {
}
}
ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
- ContextMenu.Instance.addItem({ description: "Apply Template", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
- ContextMenu.Instance.addItem({
- description: this.props.Document.chromeStatus !== "disabled" ? "Hide Chrome" : "Show Chrome", event: () => this.props.Document.chromeStatus = (this.props.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram"
- });
+ let existing = ContextMenu.Instance.findByDescription("Layout...");
+ let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
+ layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
+ !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
+
+ let makes = ContextMenu.Instance.findByDescription("Make...");
+ let makeItems: ContextMenuProps[] = makes && "subitems" in makes ? makes.subitems : [];
+ makeItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
+ !makes && ContextMenu.Instance.addItem({ description: "Make...", subitems: makeItems, icon: "hand-point-right" });
}
}
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 793cb7a8b..f39bd877a 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -7,7 +7,6 @@
z-index: 9001;
transition: top .5s;
background: lightgrey;
- padding: 10px;
.collectionViewChrome {
display: grid;
@@ -42,6 +41,14 @@
transform-origin: top left;
// margin-top: 10px;
}
+ .collectionViewBaseChrome-template {
+ margin-left: 10px;
+ display: grid;
+ background: rgb(238, 238, 238);
+ color:grey;
+ margin-top:auto;
+ margin-bottom:auto;
+ }
.collectionViewBaseChrome-viewSpecs {
margin-left: 10px;
@@ -97,7 +104,7 @@
.collectionViewBaseChrome-viewSpecsMenu-lastRow {
display: grid;
- grid-template-columns: 1fr 1fr;
+ grid-template-columns: 1fr 1fr 1fr;
grid-gap: 10px;
margin: 10px;
}
@@ -106,17 +113,20 @@
}
- .collectionStackingViewChrome-cont {
+ .collectionStackingViewChrome-cont,
+ .collectionTreeViewChrome-cont {
display: flex;
justify-content: space-between;
}
- .collectionStackingViewChrome-sort {
+ .collectionStackingViewChrome-sort,
+ .collectionTreeViewChrome-sort {
display: flex;
align-items: center;
justify-content: space-between;
- .collectionStackingViewChrome-sortIcon {
+ .collectionStackingViewChrome-sortIcon,
+ .collectionTreeViewChrome-sortIcon {
transition: transform .5s;
margin-left: 10px;
}
@@ -127,18 +137,21 @@
}
- .collectionStackingViewChrome-sectionFilter-cont {
+ .collectionStackingViewChrome-sectionFilter-cont,
+ .collectionTreeViewChrome-sectionFilter-cont {
justify-self: right;
display: flex;
font-size: 75%;
letter-spacing: 2px;
- .collectionStackingViewChrome-sectionFilter-label {
+ .collectionStackingViewChrome-sectionFilter-label,
+ .collectionTreeViewChrome-sectionFilter-label {
vertical-align: center;
padding: 10px;
}
- .collectionStackingViewChrome-sectionFilter {
+ .collectionStackingViewChrome-sectionFilter,
+ .collectionTreeViewChrome-sectionFilter {
color: white;
width: 100px;
text-align: center;
@@ -165,7 +178,8 @@
}
}
- .collectionStackingViewChrome-sectionFilter:hover {
+ .collectionStackingViewChrome-sectionFilter:hover,
+ .collectionTreeViewChrome-sectionFilter:hover {
cursor: text;
}
}
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 52c47e7e8..25b152d4e 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -1,26 +1,24 @@
-import * as React from "react";
-import { CollectionView } from "./CollectionView";
-import "./CollectionViewChromes.scss";
-import { CollectionViewType } from "./CollectionBaseView";
-import { undoBatch } from "../../util/UndoManager";
-import { action, observable, runInAction, computed, IObservable, IObservableValue, reaction, autorun } from "mobx";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
+import * as React from "react";
import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { DocLike } from "../MetadataEntryMenu";
-import * as Autosuggest from 'react-autosuggest';
-import { EditableView } from "../EditableView";
-import { StrCast, NumCast, BoolCast, Cast } from "../../../new_fields/Types";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
+import { listSpec } from "../../../new_fields/Schema";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { Utils } from "../../../Utils";
-import KeyRestrictionRow from "./KeyRestrictionRow";
+import { DragManager } from "../../util/DragManager";
import { CompileScript } from "../../util/Scripting";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { CollectionSchemaView } from "./CollectionSchemaView";
+import { undoBatch } from "../../util/UndoManager";
+import { EditableView } from "../EditableView";
import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
-import { listSpec } from "../../../new_fields/Schema";
-import { List } from "../../../new_fields/List";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { threadId } from "worker_threads";
+import { DocLike } from "../MetadataEntryMenu";
+import { CollectionViewType } from "./CollectionBaseView";
+import { CollectionView } from "./CollectionView";
+import "./CollectionViewChromes.scss";
+import KeyRestrictionRow from "./KeyRestrictionRow";
const datepicker = require('js-datepicker');
interface CollectionViewChromeProps {
@@ -29,31 +27,73 @@ interface CollectionViewChromeProps {
collapse?: (value: boolean) => any;
}
+interface Filter {
+ key: string;
+ value: string;
+ contains: boolean;
+}
+
let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
@observer
export class CollectionViewBaseChrome extends React.Component<CollectionViewChromeProps> {
+ //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
+
@observable private _viewSpecsOpen: boolean = false;
@observable private _dateWithinValue: string = "";
@observable private _dateValue: Date | string = "";
@observable private _keyRestrictions: [JSX.Element, string][] = [];
- @observable private _collapsed: boolean = false;
@computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); }
private _picker: any;
- private _datePickerElGuid = Utils.GenerateGuid();
+
+ getFilters = (script: string) => {
+ let re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g;
+ let arr: any[] = re.exec(script);
+ let toReturn: Filter[] = [];
+ if (arr !== null) {
+ let filter: Filter = {
+ key: arr[2],
+ value: arr[3],
+ contains: (arr[1] === "!") ? false : true,
+ };
+ toReturn.push(filter);
+ script = script.replace(arr[0], "");
+ if (re.exec(script) !== null) {
+ toReturn.push(...this.getFilters(script));
+ }
+ else { return toReturn; }
+ }
+ return toReturn;
+ }
+
+ addKeyRestrictions = (fields: Filter[]) => {
+
+ if (fields.length !== 0) {
+ for (let i = 0; i < fields.length; i++) {
+ this._keyRestrictions.push([<KeyRestrictionRow field={fields[i].key} value={fields[i].value} key={Utils.GenerateGuid()} contains={fields[i].contains} script={(value: string) => runInAction(() => this._keyRestrictions[i][1] = value)} />, ""]);
+
+ }
+ if (this._keyRestrictions.length === 1) {
+ this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]);
+ }
+ }
+ else {
+ this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[0][1] = value)} />, ""]);
+ this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={false} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]);
+ }
+ }
componentDidMount = () => {
- setTimeout(() => this._picker = datepicker("#" + this._datePickerElGuid, {
- disabler: (date: Date) => date > new Date(),
- onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
- dateSelected: new Date()
- }), 1000);
- runInAction(() => {
- this._keyRestrictions.push([<KeyRestrictionRow key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[0][1] = value)} />, ""]);
- this._keyRestrictions.push([<KeyRestrictionRow key={Utils.GenerateGuid()} contains={false} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]);
+ let fields: Filter[] = [];
+ if (this.filterValue) {
+ let string = this.filterValue.script.originalScript;
+ fields = this.getFilters(string);
+ }
+ runInAction(() => {
+ this.addKeyRestrictions(fields);
// chrome status is one of disabled, collapsed, or visible. this determines initial state from document
let chromeStatus = this.props.CollectionView.props.Document.chromeStatus;
if (chromeStatus) {
@@ -61,7 +101,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
throw new Error("how did you get here, if chrome status is 'disabled' on a collection, a chrome shouldn't even be instantiated!");
}
else if (chromeStatus === "collapsed") {
- this._collapsed = true;
if (this.props.collapse) {
this.props.collapse(true);
}
@@ -113,17 +152,17 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
@action
addKeyRestriction = (e: React.MouseEvent) => {
let index = this._keyRestrictions.length;
- this._keyRestrictions.push([<KeyRestrictionRow key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]);
+ this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]);
this.openViewSpecs(e);
}
- @action
+ @action.bound
applyFilter = (e: React.MouseEvent) => {
+
this.openViewSpecs(e);
- let keyRestrictionScript = `${this._keyRestrictions.map(i => i[1])
- .reduce((acc: string, value: string, i: number) => value ? `${acc} && ${value}` : acc)}`;
+ let keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")";
let yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0;
let monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0;
let weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0;
@@ -143,9 +182,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
}
}
let fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ?
- `return ${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} ${keyRestrictionScript}` :
- `return ${keyRestrictionScript} ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` :
+ `return ${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` :
+ `return (${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` :
"return true";
+
let compiled = CompileScript(fullScript, { params: { doc: Doc.name }, typecheck: false });
if (compiled.compiled) {
this.props.CollectionView.props.Document.viewSpecScript = new ScriptField(compiled);
@@ -163,9 +203,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
@action
toggleCollapse = () => {
- this._collapsed = !this._collapsed;
+ this.props.CollectionView.props.Document.chromeStatus = this.props.CollectionView.props.Document.chromeStatus === "enabled" ? "collapsed" : "enabled";
if (this.props.collapse) {
- this.props.collapse(this._collapsed);
+ this.props.collapse(this.props.CollectionView.props.Document.chromeStatus !== "enabled");
}
}
@@ -182,6 +222,12 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
CollectionView={this.props.CollectionView}
type={this.props.type}
/>);
+ case CollectionViewType.Tree: return (
+ <CollectionTreeViewChrome
+ key="collchrome"
+ CollectionView={this.props.CollectionView}
+ type={this.props.type}
+ />);
default:
return null;
}
@@ -217,17 +263,59 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
})} />);
}
+ @action.bound
+ clearFilter = () => {
+ let compiled = CompileScript("return true", { params: { doc: Doc.name }, typecheck: false });
+ if (compiled.compiled) {
+ this.props.CollectionView.props.Document.viewSpecScript = new ScriptField(compiled);
+ }
+
+ this._keyRestrictions = [];
+ this.addKeyRestrictions([]);
+ }
+
+ private dropDisposer?: DragManager.DragDropDisposer;
+ protected createDropTarget = (ele: HTMLDivElement) => {
+ this.dropDisposer && this.dropDisposer();
+ if (ele) {
+ this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ }
+ }
+
+ @undoBatch
+ @action
+ protected drop(e: Event, de: DragManager.DropEvent): boolean {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ if (de.data.draggedDocuments.length) {
+ this.props.CollectionView.props.Document.childLayout = de.data.draggedDocuments[0];
+ e.stopPropagation();
+ return true;
+ }
+ }
+ return true;
+ }
+
+ datePickerRef = (node: HTMLInputElement) => {
+ if (node) {
+ this._picker = datepicker("#" + node.id, {
+ disabler: (date: Date) => date > new Date(),
+ onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
+ dateSelected: new Date()
+ });
+ }
+ }
render() {
+ let collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled";
return (
- <div className="collectionViewChrome-cont" style={{ top: this._collapsed ? -70 : 0 }}>
+ <div className="collectionViewChrome-cont" style={{ top: collapsed ? -70 : 0 }}>
<div className="collectionViewChrome">
<div className="collectionViewBaseChrome">
<button className="collectionViewBaseChrome-collapse"
style={{
- top: this._collapsed ? 70 : 10,
- transform: `rotate(${this._collapsed ? 180 : 0}deg) scale(${this._collapsed ? 0.5 : 1}) translate(${this._collapsed ? "-100%, -100%" : "0, 0"})`,
- opacity: (this._collapsed && !this.props.CollectionView.props.isSelected()) ? 0 : 0.9,
- left: (this._collapsed ? 0 : "unset"),
+ top: collapsed ? 70 : 10,
+ transform: `rotate(${collapsed ? 180 : 0}deg) scale(${collapsed ? 0.5 : 1}) translate(${collapsed ? "-100%, -100%" : "0, 0"})`,
+ opacity: (collapsed && !this.props.CollectionView.props.isSelected()) ? 0 : 0.9,
+ left: (collapsed ? 0 : "unset"),
}}
title="Collapse collection chrome" onClick={this.toggleCollapse}>
<FontAwesomeIcon icon="caret-up" size="2x" />
@@ -243,12 +331,13 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking View</option>
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry View</option>
</select>
- <div className="collectionViewBaseChrome-viewSpecs" style={{ display: this._collapsed ? "none" : "grid" }}>
+ <div className="collectionViewBaseChrome-viewSpecs" style={{ display: collapsed ? "none" : "grid" }}>
<input className="collectionViewBaseChrome-viewSpecsInput"
placeholder="FILTER DOCUMENTS"
- value={this.filterValue ? this.filterValue.script.originalScript : ""}
+ value={this.filterValue ? this.filterValue.script.originalScript === "return true" ? "" : this.filterValue.script.originalScript : ""}
onChange={(e) => { }}
- onPointerDown={this.openViewSpecs} />
+ onPointerDown={this.openViewSpecs}
+ id="viewSpecsInput" />
{this.getPivotInput()}
<div className="collectionViewBaseChrome-viewSpecsMenu"
onPointerDown={this.openViewSpecs}
@@ -275,7 +364,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
<option value="1y">1 year of</option>
</select>
<input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
- id={this._datePickerElGuid}
+ id={Utils.GenerateGuid()}
+ ref={this.datePickerRef}
value={this._dateValue instanceof Date ? this._dateValue.toLocaleDateString() : this._dateValue}
onChange={(e) => runInAction(() => this._dateValue = e.target.value)}
onPointerDown={this.openDatePicker}
@@ -288,9 +378,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
<button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.applyFilter}>
APPLY FILTER
</button>
+ <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.clearFilter}>
+ CLEAR
+ </button>
</div>
</div>
</div>
+ <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{}}>
+ TEMPLATE
+ </div>
</div>
{this.subChrome()}
</div>
@@ -468,4 +564,109 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
</div >
);
}
-} \ No newline at end of file
+}
+
+@observer
+export class CollectionTreeViewChrome extends React.Component<CollectionViewChromeProps> {
+ @observable private _currentKey: string = "";
+ @observable private suggestions: string[] = [];
+
+ @computed private get descending() { return BoolCast(this.props.CollectionView.props.Document.stackingHeadersSortDescending); }
+ @computed get sectionFilter() { return StrCast(this.props.CollectionView.props.Document.sectionFilter); }
+
+ getKeySuggestions = async (value: string): Promise<string[]> => {
+ value = value.toLowerCase();
+ let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
+ = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
+ if (typeof docs === "function") {
+ docs = docs();
+ }
+ docs = await docs;
+ if (docs instanceof Doc) {
+ return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
+ } else {
+ const keys = new Set<string>();
+ docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+ return Array.from(keys).filter(key => key.toLowerCase().startsWith(value));
+ }
+ }
+
+ @action
+ onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
+ this._currentKey = newValue;
+ }
+
+ getSuggestionValue = (suggestion: string) => suggestion;
+
+ renderSuggestion = (suggestion: string) => {
+ return <p>{suggestion}</p>;
+ }
+
+ onSuggestionFetch = async ({ value }: { value: string }) => {
+ const sugg = await this.getKeySuggestions(value);
+ runInAction(() => {
+ this.suggestions = sugg;
+ });
+ }
+
+ @action
+ onSuggestionClear = () => {
+ this.suggestions = [];
+ }
+
+ setValue = (value: string) => {
+ this.props.CollectionView.props.Document.sectionFilter = value;
+ return true;
+ }
+
+ @action toggleSort = () => { this.props.CollectionView.props.Document.stackingHeadersSortDescending = !this.props.CollectionView.props.Document.stackingHeadersSortDescending; };
+ @action resetValue = () => { this._currentKey = this.sectionFilter; };
+
+ render() {
+ return (
+ <div className="collectionTreeViewChrome-cont">
+ <button className="collectionTreeViewChrome-sort" onClick={this.toggleSort}>
+ <div className="collectionTreeViewChrome-sortLabel">
+ Sort
+ </div>
+ <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.descending ? "180" : "0"}deg)` }}>
+ <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
+ </div>
+ </button>
+ <div className="collectionTreeViewChrome-sectionFilter-cont">
+ <div className="collectionTreeViewChrome-sectionFilter-label">
+ GROUP ITEMS BY:
+ </div>
+ <div className="collectionTreeViewChrome-sectionFilter">
+ <EditableView
+ GetValue={() => this.sectionFilter}
+ autosuggestProps={
+ {
+ resetValue: this.resetValue,
+ value: this._currentKey,
+ onChange: this.onKeyChange,
+ autosuggestProps: {
+ inputProps:
+ {
+ value: this._currentKey,
+ onChange: this.onKeyChange
+ },
+ getSuggestionValue: this.getSuggestionValue,
+ suggestions: this.suggestions,
+ alwaysRenderSuggestions: true,
+ renderSuggestion: this.renderSuggestion,
+ onSuggestionsFetchRequested: this.onSuggestionFetch,
+ onSuggestionsClearRequested: this.onSuggestionClear
+ }
+ }}
+ oneLine
+ SetValue={this.setValue}
+ contents={this.sectionFilter ? this.sectionFilter : "N/A"}
+ />
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx
index 1b59547d8..e35b7d7d3 100644
--- a/src/client/views/collections/KeyRestrictionRow.tsx
+++ b/src/client/views/collections/KeyRestrictionRow.tsx
@@ -7,12 +7,14 @@ import { Doc } from "../../../new_fields/Doc";
interface IKeyRestrictionProps {
contains: boolean;
script: (value: string) => void;
+ field: string;
+ value: string;
}
@observer
export default class KeyRestrictionRow extends React.Component<IKeyRestrictionProps> {
- @observable private _key = "";
- @observable private _value = "";
+ @observable private _key = this.props.field;
+ @observable private _value = this.props.value;
@observable private _contains = this.props.contains;
render() {
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index c3e55d825..17111af58 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -50,10 +50,10 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
render() {
return (
<>
- <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>)}
+ <p key="contexts">Contexts:</p>
+ {this._docs.map(doc => <p key={doc.col[Id] + doc.target[Id]}><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)}
+ {this._otherDocs.length ? <hr key="hr" /> : null}
+ {this._otherDocs.map(doc => <p key="p"><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)}
</>
);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 3193f5624..b8148852d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -55,7 +55,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
ctx.stroke();
// ctx.font = "10px Arial";
- // ctx.fillText(CurrentUserUtils.email[0].toUpperCase(), 10, 10);
+ // ctx.fillText(Doc.CurrentUserEmail[0].toUpperCase(), 10, 10);
}
}
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ba8dcff98..3be6aa3d3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,7 +1,7 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faEye } from "@fortawesome/free-regular-svg-icons";
import { faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload, faChalkboard, faBraille } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast, FieldResult, Field, Opt } from "../../../../new_fields/Doc";
import { Id } from "../../../../new_fields/FieldSymbols";
@@ -36,9 +36,8 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso
import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
-import { DocumentType, Docs } from "../../../documents/Documents";
-import { RouteStore } from "../../../../server/RouteStore";
-import { string, number, elementType } from "prop-types";
+import { Docs } from "../../../documents/Documents";
+import { DocumentType } from "../../../documents/DocumentTypes";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard);
@@ -79,7 +78,6 @@ export namespace PivotView {
let collection = target.Document;
const field = StrCast(collection.pivotField) || "title";
const width = NumCast(collection.pivotWidth) || 200;
-
const groups = new Map<FieldResult<Field>, Doc[]>();
for (const doc of target.childDocs) {
@@ -92,14 +90,11 @@ export namespace PivotView {
} else {
groups.set(val, [doc]);
}
-
}
let minSize = Infinity;
- groups.forEach((val, key) => {
- minSize = Math.min(minSize, val.length);
- });
+ groups.forEach((val, key) => minSize = Math.min(minSize, val.length));
const numCols = NumCast(collection.pivotNumColumns) || Math.ceil(Math.sqrt(minSize));
const fontSize = NumCast(collection.pivotFontSize);
@@ -136,42 +131,36 @@ export namespace PivotView {
});
let elements = target.viewDefsToJSX(groupNames);
- let curPage = FieldValue(target.Document.curPage, -1);
-
- let docViews = target.childDocs.filter(doc => doc instanceof Doc).reduce((prev, doc) => {
- var page = NumCast(doc.page, -1);
- if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) {
- let minim = BoolCast(doc.isMinimized);
- if (minim === undefined || !minim) {
- let defaultPosition = (): ViewDefBounds => {
- return {
- x: NumCast(doc.x),
- y: NumCast(doc.y),
- z: NumCast(doc.z),
- width: NumCast(doc.width),
- height: NumCast(doc.height)
- };
+ let docViews = target.childDocs.reduce((prev, doc) => {
+ let minim = BoolCast(doc.isMinimized);
+ if (minim === undefined || !minim) {
+ let defaultPosition = (): ViewDefBounds => {
+ return {
+ x: NumCast(doc.x),
+ y: NumCast(doc.y),
+ z: NumCast(doc.z),
+ width: NumCast(doc.width),
+ height: NumCast(doc.height)
};
- const pos = docMap.get(doc) || defaultPosition();
- prev.push({
- ele: (
- <CollectionFreeFormDocumentView
- key={doc[Id]}
- x={pos.x}
- y={pos.y}
- width={pos.width}
- height={pos.height}
- {...target.getChildDocumentViewProps(doc)}
- />),
- bounds: {
- x: pos.x,
- y: pos.y,
- z: pos.z,
- width: NumCast(pos.width),
- height: NumCast(pos.height)
- }
- });
- }
+ };
+ const pos = docMap.get(doc) || defaultPosition();
+ prev.push({
+ ele: <CollectionFreeFormDocumentView
+ key={doc[Id]}
+ x={pos.x}
+ y={pos.y}
+ width={pos.width}
+ height={pos.height}
+ {...target.getChildDocumentViewProps(doc)}
+ />,
+ bounds: {
+ x: pos.x,
+ y: pos.y,
+ z: pos.z,
+ width: NumCast(pos.width),
+ height: NumCast(pos.height)
+ }
+ });
}
return prev;
}, elements);
@@ -194,6 +183,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private get _pwidth() { return this.props.PanelWidth(); }
private get _pheight() { return this.props.PanelHeight(); }
private inkKey = "ink";
+ private _childLayoutDisposer?: IReactionDisposer;
+
+ componentDidMount() {
+ this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)],
+ async (args) => args[1] instanceof Doc &&
+ this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined)));
+ }
+ componentWillUnmount() {
+ this._childLayoutDisposer && this._childLayoutDisposer();
+ }
get parentScaling() {
return (this.props as any).ContentScaling && this.fitToBox && !this.isAnnotationOverlay ? (this.props as any).ContentScaling() : 1;
@@ -623,19 +622,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getScale = () => this.Document.scale ? this.Document.scale : 1;
- getChildDocumentViewProps(childDocLayout: Doc): DocumentViewProps {
- let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, childDocLayout);
+ getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
return {
- DataDoc: pair.data,
- Document: pair.layout,
+ DataDoc: childData,
+ Document: childLayout,
addDocument: this.props.addDocument,
removeDocument: this.props.removeDocument,
moveDocument: this.props.moveDocument,
- ScreenToLocalTransform: pair.layout.z ? this.getTransformOverlay : this.getTransform,
+ onClick: this.props.onClick,
+ ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
renderDepth: this.props.renderDepth + 1,
- selectOnLoad: pair.layout[Id] === this._selectOnLoaded,
- PanelWidth: pair.layout[WidthSym],
- PanelHeight: pair.layout[HeightSym],
+ selectOnLoad: childLayout[Id] === this._selectOnLoaded,
+ PanelWidth: childLayout[WidthSym],
+ PanelHeight: childLayout[HeightSym],
ContentScaling: returnOne,
ContainingCollectionView: this.props.CollectionView,
focus: this.focusDocument,
@@ -644,6 +643,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
whenActiveChanged: this.props.whenActiveChanged,
bringToFront: this.bringToFront,
addDocTab: this.props.addDocTab,
+ pinToPres: this.props.pinToPres,
zoomToScale: this.zoomToScale,
getScale: this.getScale
};
@@ -655,6 +655,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
addDocument: this.props.addDocument,
removeDocument: this.props.removeDocument,
moveDocument: this.props.moveDocument,
+ onClick: this.props.onClick,
ScreenToLocalTransform: this.getTransform,
renderDepth: this.props.renderDepth,
selectOnLoad: layoutDoc[Id] === this._selectOnLoaded,
@@ -668,6 +669,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
whenActiveChanged: this.props.whenActiveChanged,
bringToFront: this.bringToFront,
addDocTab: this.props.addDocTab,
+ pinToPres: this.props.pinToPres,
zoomToScale: this.zoomToScale,
getScale: this.getScale
};
@@ -718,6 +720,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed.struct
get elements() {
+ if (this.Document.usePivotLayout) return PivotView.elements(this);
let curPage = FieldValue(this.Document.curPage, -1);
const initScript = this.Document.arrangeInit;
const script = this.Document.arrangeScript;
@@ -741,12 +744,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) :
{ x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), z: Cast(doc.z, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") };
state = pos.state === undefined ? state : pos.state;
- prev.push({
- ele: <CollectionFreeFormDocumentView key={doc[Id]}
- x={script ? pos.x : undefined} y={script ? pos.y : undefined}
- width={script ? pos.width : undefined} height={script ? pos.height : undefined} {...this.getChildDocumentViewProps(doc)} />,
- bounds: (pos.x !== undefined && pos.y !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } : undefined
- });
+ let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, doc);
+ if (pair.layout && !(pair.data instanceof Promise)) {
+ prev.push({
+ ele: <CollectionFreeFormDocumentView key={doc[Id]}
+ x={script ? pos.x : undefined} y={script ? pos.y : undefined}
+ width={script ? pos.width : undefined} height={script ? pos.height : undefined} {...this.getChildDocumentViewProps(pair.layout, pair.data)} />,
+ bounds: (pos.x !== undefined && pos.y !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } : undefined
+ });
+ }
}
}
return prev;
@@ -761,7 +767,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed.struct
get views() {
- let source = this.Document.usePivotLayout === true ? PivotView.elements(this) : this.elements;
+ let source = this.elements;
return source.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele);
}
@computed.struct
@@ -812,17 +818,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
onContextMenu = (e: React.MouseEvent) => {
let layoutItems: ContextMenuProps[] = [];
- layoutItems.push({
- description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`,
- event: this.fitToContainer,
- icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt"
- });
- layoutItems.push({
- description: "reset view", event: () => {
- this.props.Document.panX = this.props.Document.panY = 0;
- this.props.Document.scale = 1;
- }, icon: "compress-arrows-alt"
- });
+ layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, event: this.fitToContainer, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
+ layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
layoutItems.push({
description: `${this.props.Document.useClusters ? "Uncluster" : "Use Clusters"}`,
event: async () => {
@@ -837,50 +834,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
event: async () => this.props.Document.clusterOverridesDefaultBackground = !this.props.Document.clusterOverridesDefaultBackground,
icon: !this.props.Document.useClusters ? "chalkboard" : "chalkboard"
});
- layoutItems.push({
- description: "Arrange contents in grid",
- event: this.arrangeContents,
- icon: "table"
- });
- ContextMenu.Instance.addItem({
- description: "Layout...",
- subitems: layoutItems,
- icon: "compass"
- });
- ContextMenu.Instance.addItem({
- description: "Analyze Strokes",
- event: this.analyzeStrokes,
- icon: "paint-brush"
- });
- ContextMenu.Instance.addItem({
- description: "Import document", icon: "upload", event: () => {
- const input = document.createElement("input");
- input.type = "file";
- input.accept = ".zip";
- input.onchange = async _e => {
- const files = input.files;
- if (!files) return;
- const file = files[0];
- let formData = new FormData();
- formData.append('file', file);
- formData.append('remap', "true");
- const upload = Utils.prepend("/uploadDoc");
- const response = await fetch(upload, { method: "POST", body: formData });
- const json = await response.json();
- if (json === "error") {
- return;
- }
- const doc = await DocServer.GetRefField(json);
- if (!doc || !(doc instanceof Doc)) {
- return;
- }
- const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY);
- doc.x = x, doc.y = y;
- this.addDocument(doc, false);
- };
- input.click();
- }
- });
+ layoutItems.push({ description: "Arrange contents in grid", event: this.arrangeContents, icon: "table" });
+ ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
+
+ let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
+ let analyzers: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
+ analyzers.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
+ !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" });
}
diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/ButtonBox.scss
index 92beafa15..5ed435505 100644
--- a/src/client/views/nodes/ButtonBox.scss
+++ b/src/client/views/nodes/ButtonBox.scss
@@ -3,10 +3,14 @@
height: 100%;
pointer-events: all;
border-radius: inherit;
+ display:table;
}
.buttonBox-mainButton {
width: 100%;
height: 100%;
border-radius: inherit;
+ display:table-cell;
+ vertical-align: middle;
+ text-align: center;
} \ No newline at end of file
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index 640795789..8b6f11aac 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -15,6 +15,7 @@ import { Doc } from '../../../new_fields/Doc';
import './ButtonBox.scss';
import { observer } from 'mobx-react';
import { DocumentIconContainer } from './DocumentIcon';
+import { StrCast } from '../../../new_fields/Types';
library.add(faEdit as any);
@@ -30,47 +31,10 @@ const ButtonDocument = makeInterface(ButtonSchema);
export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(ButtonDocument) {
public static LayoutString() { return FieldView.LayoutString(ButtonBox); }
- onClick = (e: React.MouseEvent) => {
- const onClick = this.Document.onClick;
- if (!onClick) {
- return;
- }
- e.stopPropagation();
- e.preventDefault();
- onClick.script.run({ this: this.props.Document });
- }
-
- onContextMenu = () => {
- ContextMenu.Instance.addItem({
- description: "Edit OnClick script", icon: "edit", event: () => {
- let overlayDisposer: () => void = emptyFunction;
- const script = this.Document.onClick;
- let originalText: string | undefined = undefined;
- if (script) originalText = script.script.originalScript;
- // tslint:disable-next-line: no-unnecessary-callback-wrapper
- let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
- const script = CompileScript(text, {
- params: { this: Doc.name },
- typecheck: false,
- editable: true,
- transformer: DocumentIconContainer.getTransformer()
- });
- if (!script.compiled) {
- onError(script.errors.map(error => error.messageText).join("\n"));
- return;
- }
- this.Document.onClick = new ScriptField(script);
- overlayDisposer();
- }} showDocumentIcons />;
- overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnClick` });
- }
- });
- }
-
render() {
return (
- <div className="buttonBox-outerDiv" onContextMenu={this.onContextMenu}>
- <button className="buttonBox-mainButton" onClick={this.onClick}>{this.Document.text || this.Document.title || "Button"}</button>
+ <div className="buttonBox-outerDiv" >
+ <div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} >{this.Document.text || this.Document.title}</div>
</div>
);
}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 396233551..d77662355 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -11,7 +11,9 @@ import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
import { FormattedTextBox } from "./FormattedTextBox";
import { ImageBox } from "./ImageBox";
+import { DragBox } from "./DragBox";
import { ButtonBox } from "./ButtonBox";
+import { PresBox } from "./PresBox";
import { IconBox } from "./IconBox";
import { KeyValueBox } from "./KeyValueBox";
import { PDFBox } from "./PDFBox";
@@ -27,7 +29,7 @@ import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
import { List } from "../../../new_fields/List";
import { Doc } from "../../../new_fields/Doc";
import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
-import { CollectionViewType } from "../collections/CollectionBaseView";
+import { ScriptField } from "../../../new_fields/ScriptField";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -48,6 +50,7 @@ const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
export class DocumentContentsView extends React.Component<DocumentViewProps & {
isSelected: () => boolean,
select: (ctrl: boolean) => void,
+ onClick?: ScriptField,
layoutKey: string,
hideOnLeave?: boolean
}> {
@@ -65,7 +68,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
}
get dataDoc() {
- if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+ if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) {
// if there is no dataDoc (ie, we're not rendering a template layout), but this document
// has a template layout document, then we will render the template layout but use
// this document as the data document for the layout.
@@ -80,7 +83,12 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
}
CreateBindings(): JsxBindings {
- return { props: { ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, Document: this.layoutDoc, DataDoc: this.dataDoc } };
+ let list = {
+ ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit,
+ Document: this.layoutDoc,
+ DataDoc: this.dataDoc
+ };
+ return { props: list };
}
@computed get templates(): List<string> {
@@ -99,10 +107,12 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
if (this.props.renderDepth > 7) return (null);
if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
return <ObserverJsxParser
- components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, YoutubeBox }}
+ blacklistedAttrs={[]}
+ components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox }}
bindings={this.CreateBindings()}
jsx={this.finalLayout}
showWarnings={true}
+
onError={(test: any) => { console.log(test); }}
/>;
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c8eab85c2..fd53262d7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,6 +1,6 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import * as fa from '@fortawesome/free-solid-svg-icons';
-import { action, computed, IReactionDisposer, reaction, runInAction, trace } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction, trace, observable } from "mobx";
import { observer } from "mobx-react";
import * as rp from "request-promise";
import { Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
@@ -8,13 +8,15 @@ import { Copy, Id } from '../../../new_fields/FieldSymbols';
import { List } from "../../../new_fields/List";
import { ObjectField } from "../../../new_fields/ObjectField";
import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, Cast, FieldValue, NumCast, StrCast, PromiseValue } from "../../../new_fields/Types";
+import { ScriptField } from '../../../new_fields/ScriptField';
+import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { RouteStore } from '../../../server/RouteStore';
import { emptyFunction, returnTrue, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from "../../documents/Documents";
import { ClientUtils } from '../../util/ClientUtils';
+import { DictationManager } from '../../util/DictationManager';
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
import { LinkManager } from '../../util/LinkManager';
@@ -29,17 +31,16 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from "../DocComponent";
import { EditableView } from '../EditableView';
+import { MainView } from '../MainView';
import { OverlayView } from '../OverlayView';
-import { PresentationView } from "../presentationview/PresentationView";
+import { ScriptBox } from '../ScriptBox';
import { ScriptingRepl } from '../ScriptingRepl';
import { Template } from "./../Templates";
import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import { FormattedTextBox } from './FormattedTextBox';
import React = require("react");
-import { DictationManager } from '../../util/DictationManager';
-import { MainView } from '../MainView';
-import requestPromise = require('request-promise');
+import { PresBox } from './PresBox';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
library.add(fa.faTrash);
@@ -80,6 +81,7 @@ export interface DocumentViewProps {
Document: Doc;
DataDoc?: Doc;
fitToBox?: boolean;
+ onClick?: ScriptField;
addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
removeDocument?: (doc: Doc) => boolean;
moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
@@ -95,6 +97,7 @@ export interface DocumentViewProps {
whenActiveChanged: (isActive: boolean) => void;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
+ pinToPres: (document: Doc) => void;
collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
zoomToScale: (scale: number) => void;
backgroundColor: (doc: Doc) => string | undefined;
@@ -109,7 +112,8 @@ const schema = createSchema({
nativeHeight: "number",
backgroundColor: "string",
opacity: "number",
- hidden: "boolean"
+ hidden: "boolean",
+ onClick: ScriptField,
});
export const positionSchema = createSchema({
@@ -135,8 +139,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _lastTap: number = 0;
private _doubleTap = false;
private _hitExpander = false;
+ private _hitTemplateDrag = false;
private _mainCont = React.createRef<HTMLDivElement>();
private _dropDisposer?: DragManager.DragDropDisposer;
+ _animateToIconDisposer?: IReactionDisposer;
+ _reactionDisposer?: IReactionDisposer;
public get ContentDiv() { return this._mainCont.current; }
@computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
@@ -151,8 +158,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
set templates(templates: List<string>) { this.props.Document.templates = templates; }
screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
- _animateToIconDisposer?: IReactionDisposer;
- _reactionDisposer?: IReactionDisposer;
@action
componentDidMount() {
if (this._mainCont.current) {
@@ -204,9 +209,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@action
componentDidUpdate() {
- if (this._dropDisposer) {
- this._dropDisposer();
- }
+ this._dropDisposer && this._dropDisposer();
if (this._mainCont.current) {
this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
handlers: { drop: this.drop.bind(this) }
@@ -215,9 +218,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@action
componentWillUnmount() {
- if (this._reactionDisposer) this._reactionDisposer();
- if (this._animateToIconDisposer) this._animateToIconDisposer();
- if (this._dropDisposer) this._dropDisposer();
+ this._reactionDisposer && this._reactionDisposer();
+ this._animateToIconDisposer && this._animateToIconDisposer();
+ this._dropDisposer && this._dropDisposer();
DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
}
@@ -226,7 +229,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
get dataDoc() {
- if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+ if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) {
// if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
// has a template layout document, then we will render the template layout but use
// this document as the data document for the layout.
@@ -234,7 +237,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
}
- startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) {
+ startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean, applyAsTemplate?: boolean) {
if (this._mainCont.current) {
let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
let alldataConnected = [this.dataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
@@ -245,6 +248,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
dragData.xOffset = xoff;
dragData.yOffset = yoff;
dragData.moveDocument = this.props.moveDocument;
+ dragData.applyAsTemplate = applyAsTemplate;
DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
handlers: {
dragComplete: action(emptyFunction)
@@ -292,6 +296,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onClick = async (e: React.MouseEvent) => {
if (e.nativeEvent.cancelBubble) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing.
e.stopPropagation();
+ if (this.onClickHandler && this.onClickHandler.script) {
+ this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
+ e.preventDefault();
+ return;
+ }
let altKey = e.altKey;
let ctrlKey = e.ctrlKey;
if (this._doubleTap && this.props.renderDepth) {
@@ -370,14 +379,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
}
+
+
onPointerDown = (e: React.PointerEvent): void => {
+ if (e.nativeEvent.cancelBubble) return;
this._downX = e.clientX;
this._downY = e.clientY;
this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
- // if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) {
- // CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]);
- // e.stopPropagation();
- // } else {
+ this._hitTemplateDrag = false;
+ for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
+ if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
+ this._hitTemplateDrag = true;
+ }
+ }
if (this.active) e.stopPropagation(); // events stop at the lowest document that is active.
document.removeEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointermove", this.onPointerMove);
@@ -391,7 +405,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander);
+ this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander, this._hitTemplateDrag);
}
}
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -449,6 +463,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
DocUtils.MakeLink(annotationDoc, targetDoc, this.props.ContainingCollectionView!.props.Document, `Link from ${StrCast(annotationDoc.title)}`);
}
+ if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) {
+ Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, this.props.DataDoc);
+ e.stopPropagation();
+ }
if (de.data instanceof DragManager.LinkDragData) {
let sourceDoc = de.data.linkSourceDocument;
let destDoc = this.props.Document;
@@ -563,36 +581,31 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" });
subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
- cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
- cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" });
- cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
- cm.addItem({ description: "Transcribe Speech", event: this.listen, icon: "microphone" });
- let makes: ContextMenuProps[] = [];
- makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
- makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" });
+ let existingMake = ContextMenu.Instance.findByDescription("Make...");
+ let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
+ makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
+ makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Into Button", event: this.makeBtnClicked, icon: "concierge-bell" });
+ makes.push({ description: "OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") });
makes.push({
- description: "Make Portal", event: () => {
+ description: "Into Portal", event: () => {
let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
- //Doc.GetProto(this.props.Document).subBulletDocs = new List<Doc>([portal]);
- //summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight"
- //Doc.GetProto(this.props.Document).templates = new List<string>([Templates.Bullet.Layout]);
- //let coll = Docs.Create.StackingDocument([this.props.Document, portal], { x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y), width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".cont" });
- //this.props.addDocument && this.props.addDocument(coll);
- //this.props.removeDocument && this.props.removeDocument(this.props.Document);
DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
this.makeBtnClicked();
-
}, icon: "window-restore"
});
- cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" });
- if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) {
- cm.addItem({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
- }
- cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
+ !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" });
let existing = ContextMenu.Instance.findByDescription("Layout...");
let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
+
+ layoutItems.push({ description: `${this.props.Document.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.props.Document.chromeStatus = (this.props.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+ layoutItems.push({ description: `${this.props.Document.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.props.Document.autoHeight = !this.props.Document.autoHeight, icon: "plus" });
+ layoutItems.push({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
+ layoutItems.push({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
+ if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) {
+ layoutItems.push({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
+ }
!existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
if (!ClientUtils.RELEASE) {
let copies: ContextMenuProps[] = [];
@@ -600,6 +613,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
copies.push({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" });
cm.addItem({ description: "Copy...", subitems: copies, icon: "copy" });
}
+ let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
+ let analyzers: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
+ analyzers.push({ description: "Transcribe Speech", event: this.listen, icon: "microphone" });
+ !existingAnalyze && cm.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" });
+ cm.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin" }); //I think this should work... and it does! A miracle!
+ cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
cm.addItem({
description: "Download document", icon: "download", event: () => {
const a = document.createElement("a");
@@ -609,13 +628,43 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
a.click();
}
});
+
+ cm.addItem({
+ description: "Import document", icon: "upload", event: () => {
+ const input = document.createElement("input");
+ input.type = "file";
+ input.accept = ".zip";
+ input.onchange = async _e => {
+ const files = input.files;
+ if (!files) return;
+ const file = files[0];
+ let formData = new FormData();
+ formData.append('file', file);
+ formData.append('remap', "true");
+ const upload = Utils.prepend("/uploadDoc");
+ const response = await fetch(upload, { method: "POST", body: formData });
+ const json = await response.json();
+ if (json === "error") {
+ return;
+ }
+ const doc = await DocServer.GetRefField(json);
+ if (!doc || !(doc instanceof Doc)) {
+ return;
+ }
+ const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY);
+ doc.x = x, doc.y = y;
+ this.props.addDocument && this.props.addDocument(doc, false);
+ };
+ input.click();
+ }
+ });
cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
type User = { email: string, userDocumentId: string };
let usersMenu: ContextMenuProps[] = [];
try {
let stuff = await rp.get(Utils.prepend(RouteStore.getUsers));
const users: User[] = JSON.parse(stuff);
- usersMenu = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({
+ usersMenu = users.filter(({ email }) => email !== Doc.CurrentUserEmail).map(({ email, userDocumentId }) => ({
description: email, event: async () => {
const userDocument = await Cast(DocServer.GetRefField(userDocumentId), Doc);
if (!userDocument) {
@@ -638,6 +687,30 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
runInAction(() => {
cm.addItem({ description: "Share...", subitems: usersMenu, icon: "share" });
+ if (!ClientUtils.RELEASE) {
+ let setWriteMode = (mode: DocServer.WriteMode) => {
+ DocServer.AclsMode = mode;
+ const mode1 = mode;
+ const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
+ DocServer.setFieldWriteMode("x", mode1);
+ DocServer.setFieldWriteMode("y", mode1);
+ DocServer.setFieldWriteMode("width", mode1);
+ DocServer.setFieldWriteMode("height", mode1);
+
+ DocServer.setFieldWriteMode("panX", mode2);
+ DocServer.setFieldWriteMode("panY", mode2);
+ DocServer.setFieldWriteMode("scale", mode2);
+ DocServer.setFieldWriteMode("viewType", mode2);
+ };
+ let aclsMenu: ContextMenuProps[] = [];
+ aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" });
+ aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" });
+ aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" });
+ aclsMenu.push({ description: "Live Readonly (no write/read others)", event: () => setWriteMode(DocServer.WriteMode.LiveReadonly), icon: DocServer.AclsMode === DocServer.WriteMode.LiveReadonly ? "check" : "exclamation" });
+ cm.addItem({ description: "Collaboration ACLs...", subitems: aclsMenu, icon: "share" });
+ cm.addItem({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" });
+ }
+
if (!this.topMost) {
// DocumentViews should stop propagation of this event
e.stopPropagation();
@@ -654,13 +727,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
isSelected = () => SelectionManager.IsSelected(this);
@action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
-
@computed get nativeWidth() { return this.Document.nativeWidth || 0; }
@computed get nativeHeight() { return this.Document.nativeHeight || 0; }
+ @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; }
@computed get contents() {
return (<DocumentContentsView {...this.props}
ChromeHeight={this.chromeHeight}
- isSelected={this.isSelected} select={this.select}
+ isSelected={this.isSelected}
+ select={this.select}
+ onClick={this.onClickHandler}
selectOnLoad={this.props.selectOnLoad}
layoutKey={"layout"}
fitToBox={BoolCast(this.props.Document.fitToBox) ? true : this.props.fitToBox}
@@ -679,6 +754,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
}
+
render() {
let backgroundColor = this.layoutDoc.isBackground || (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground && this.layoutDoc.backgroundColor === this.layoutDoc.defaultBackgroundColor) ?
this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) :
@@ -697,7 +773,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined;
- let brushDegree = Doc.IsBrushedDegree(this.layoutDoc);
+ let brushDegree = Doc.IsBrushedDegree(this.props.Document);
+ let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding);
+ let localScale = this.props.ScreenToLocalTransform().Scale * brushDegree;
return (
<div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
ref={this._mainCont}
@@ -706,14 +784,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
color: foregroundColor,
outlineColor: ["transparent", "maroon", "maroon"][brushDegree],
outlineStyle: ["none", "dashed", "solid"][brushDegree],
- outlineWidth: brushDegree && !StrCast(Doc.GetProto(this.props.Document).borderRounding) ?
- `${brushDegree * this.props.ScreenToLocalTransform().Scale}px` : "0px",
- marginLeft: brushDegree && StrCast(Doc.GetProto(this.props.Document).borderRounding) ?
- `${-brushDegree * this.props.ScreenToLocalTransform().Scale}px` : undefined,
- marginTop: brushDegree && StrCast(Doc.GetProto(this.props.Document).borderRounding) ?
- `${-brushDegree * this.props.ScreenToLocalTransform().Scale}px` : undefined,
- border: brushDegree && StrCast(Doc.GetProto(this.props.Document).borderRounding) ?
- `${["none", "dashed", "solid"][brushDegree]} ${["transparent", "maroon", "maroon"][brushDegree]} ${this.props.ScreenToLocalTransform().Scale}px` : undefined,
+ outlineWidth: brushDegree && !borderRounding ? `${localScale}px` : "0px",
+ border: brushDegree && borderRounding ? `${["none", "dashed", "solid"][brushDegree]} ${["transparent", "maroon", "maroon"][brushDegree]} ${localScale}px` : undefined,
borderRadius: "inherit",
background: backgroundColor,
width: nativeWidth,
@@ -743,13 +815,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
height={72}
fontSize={12}
GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle!])}
- SetValue={(value: string) => (Doc.GetProto(this.layoutDoc)[showTitle!] = value) ? true : true}
+ SetValue={(value: string) => ((this.layoutDoc.isTemplate ? this.layoutDoc : Doc.GetProto(this.layoutDoc))[showTitle!] = value) ? true : true}
/>
</div>
}
{!showCaption ? (null) :
<div style={{ position: "absolute", bottom: 0, transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
- <FormattedTextBox {...this.props} DataDoc={this.dataDoc} active={returnTrue} isSelected={this.isSelected} focus={emptyFunction} select={this.select} selectOnLoad={this.props.selectOnLoad} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} />
+ <FormattedTextBox {...this.props} onClick={this.onClickHandler} DataDoc={this.dataDoc} active={returnTrue} isSelected={this.isSelected} focus={emptyFunction} select={this.select} selectOnLoad={this.props.selectOnLoad} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} />
</div>
}
</div>
diff --git a/src/client/views/nodes/DragBox.scss b/src/client/views/nodes/DragBox.scss
new file mode 100644
index 000000000..fbb9b9c1c
--- /dev/null
+++ b/src/client/views/nodes/DragBox.scss
@@ -0,0 +1,13 @@
+.dragBox-outerDiv {
+ width: 100%;
+ height: 100%;
+ pointer-events: all;
+ border-radius: inherit;
+ background: black;
+ border-radius: 100%;
+ svg {
+ margin:18%;
+ width:65% !important;
+ height:65%;
+ }
+}
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
new file mode 100644
index 000000000..1f2c88086
--- /dev/null
+++ b/src/client/views/nodes/DragBox.tsx
@@ -0,0 +1,101 @@
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faEdit } from '@fortawesome/free-regular-svg-icons';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc } from '../../../new_fields/Doc';
+import { createSchema, makeInterface } from '../../../new_fields/Schema';
+import { ScriptField } from '../../../new_fields/ScriptField';
+import { emptyFunction } from '../../../Utils';
+import { CompileScript } from '../../util/Scripting';
+import { ContextMenu } from '../ContextMenu';
+import { DocComponent } from '../DocComponent';
+import { OverlayView } from '../OverlayView';
+import { ScriptBox } from '../ScriptBox';
+import { DocumentIconContainer } from './DocumentIcon';
+import './DragBox.scss';
+import { FieldView, FieldViewProps } from './FieldView';
+import { DragManager } from '../../util/DragManager';
+import { Docs } from '../../documents/Documents';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+
+library.add(faEdit as any);
+
+const DragSchema = createSchema({
+ onDragStart: ScriptField,
+ text: "string"
+});
+
+type DragDocument = makeInterface<[typeof DragSchema]>;
+const DragDocument = makeInterface(DragSchema);
+@observer
+export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocument) {
+ _downX: number = 0;
+ _downY: number = 0;
+ public static LayoutString() { return FieldView.LayoutString(DragBox); }
+ _mainCont = React.createRef<HTMLDivElement>();
+ onDragStart = (e: React.PointerEvent) => {
+ if (!e.ctrlKey && !e.altKey && !e.shiftKey && !this.props.isSelected() && e.button === 0) {
+ document.removeEventListener("pointermove", this.onDragMove);
+ document.addEventListener("pointermove", this.onDragMove);
+ document.removeEventListener("pointerup", this.onDragUp);
+ document.addEventListener("pointerup", this.onDragUp);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+
+ onDragMove = (e: MouseEvent) => {
+ if (!e.cancelBubble && !this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 5 || Math.abs(this._downY - e.clientY) > 5)) {
+ document.removeEventListener("pointermove", this.onDragMove);
+ document.removeEventListener("pointerup", this.onDragUp);
+ const onDragStart = this.Document.onDragStart;
+ e.stopPropagation();
+ e.preventDefault();
+ let res = onDragStart ? onDragStart.script.run({ this: this.props.Document }) : undefined;
+ let doc = res !== undefined && res.success ?
+ res.result as Doc :
+ Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
+ doc && DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc], [undefined]), e.clientX, e.clientY);
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ onDragUp = (e: MouseEvent) => {
+ document.removeEventListener("pointermove", this.onDragMove);
+ document.removeEventListener("pointerup", this.onDragUp);
+ }
+
+ onContextMenu = () => {
+ ContextMenu.Instance.addItem({
+ description: "Edit OnClick script", icon: "edit", event: () => {
+ let overlayDisposer: () => void = emptyFunction;
+ const script = this.Document.onDragStart;
+ let originalText: string | undefined = undefined;
+ if (script) originalText = script.script.originalScript;
+ // tslint:disable-next-line: no-unnecessary-callback-wrapper
+ let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
+ const script = CompileScript(text, {
+ params: { this: Doc.name },
+ typecheck: false,
+ editable: true,
+ transformer: DocumentIconContainer.getTransformer()
+ });
+ if (!script.compiled) {
+ onError(script.errors.map(error => error.messageText).join("\n"));
+ return;
+ }
+ this.Document.onClick = new ScriptField(script);
+ overlayDisposer();
+ }} showDocumentIcons />;
+ overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnDragStart` });
+ }
+ });
+ }
+
+ render() {
+ return (<div className="dragBox-outerDiv" onContextMenu={this.onContextMenu} onPointerDown={this.onDragStart} ref={this._mainCont}>
+ <FontAwesomeIcon className="dragBox-icon" icon="folder" size="lg" color="white" />
+ </div>);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index da54ecc3a..f0f1b3b73 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -17,7 +17,7 @@ import { IconBox } from "./IconBox";
import { ImageBox } from "./ImageBox";
import { PDFBox } from "./PDFBox";
import { VideoBox } from "./VideoBox";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { ScriptField } from "../../../new_fields/ScriptField";
//
// these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -32,12 +32,14 @@ export interface FieldViewProps {
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
DataDoc?: Doc;
+ onClick?: ScriptField;
isSelected: () => boolean;
select: (isCtrlPressed: boolean) => void;
renderDepth: number;
selectOnLoad: boolean;
addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+ pinToPres: (document: Doc) => void;
removeDocument?: (document: Doc) => boolean;
moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
@@ -56,6 +58,7 @@ export interface FieldViewProps {
export class FieldView extends React.Component<FieldViewProps> {
public static LayoutString(fieldType: { name: string }, fieldStr: string = "data", fieldExt: string = "") {
return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} fieldExt={"${fieldExt}"} />`;
+ //"<ImageBox {...props} />"
}
@computed
@@ -77,6 +80,9 @@ export class FieldView extends React.Component<FieldViewProps> {
else if (field instanceof ImageField) {
return <ImageBox {...this.props} leaveNativeSize={true} />;
}
+ // else if (field instaceof PresBox) {
+ // return <PresBox {...this.props} />;
+ // }
else if (field instanceof IconField) {
return <IconBox {...this.props} />;
}
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 247f7d1ea..1b537cc52 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -33,12 +33,12 @@
}
.formattedTextBox-inner-rounded {
- height: calc(100% - 25px);
- width: calc(100% - 40px);
+ height: 70%;
+ width: 85%;
position: absolute;
overflow: auto;
- top: 15;
- left: 20;
+ top: 15%;
+ left: 10%;
}
.formattedTextBox-inner-rounded div,
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index e06be7079..2705fac14 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,20 +1,21 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faEdit, faSmile, faTextHeight, faUpload } from '@fortawesome/free-solid-svg-icons';
-import { action, IReactionDisposer, observable, reaction, runInAction, computed, Lambda, trace, untracked, autorun } from "mobx";
+import { action, computed, IReactionDisposer, Lambda, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap } from "prosemirror-commands";
import { history } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
-import { Node as ProsNode } from "prosemirror-model";
-import { EditorState, Plugin, Transaction, Selection } from "prosemirror-state";
-import { NodeType, Slice, Node, Fragment } from 'prosemirror-model';
+import { Fragment, Node, Node as ProsNode, NodeType, Slice } from "prosemirror-model";
+import { EditorState, Plugin, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
-import { Doc, Opt, DocListCast } from "../../../new_fields/Doc";
-import { Id, Copy } from '../../../new_fields/FieldSymbols';
+import { DateField } from '../../../new_fields/DateField';
+import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Copy, Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { RichTextField, ToPlainText, FromPlainText } from "../../../new_fields/RichTextField";
-import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
import { BoolCast, Cast, NumCast, StrCast, DateCast } from "../../../new_fields/Types";
+import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { Utils } from '../../../Utils';
import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
@@ -26,18 +27,14 @@ import { SelectionManager } from "../../util/SelectionManager";
import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
import { TooltipTextMenu } from "../../util/TooltipTextMenu";
import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { ContextMenu } from "../../views/ContextMenu";
-import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from "../DocComponent";
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from "./FieldView";
import "./FormattedTextBox.scss";
import React = require("react");
-import { DateField } from '../../../new_fields/DateField';
-import { Utils } from '../../../Utils';
-import { MainOverlayTextBox } from '../MainOverlayTextBox';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils';
import { DocumentDecorations } from '../DocumentDecorations';
+import { MainOverlayTextBox } from '../MainOverlayTextBox';
library.add(faEdit);
library.add(faSmile, faTextHeight, faUpload);
@@ -74,7 +71,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
public static Instance: FormattedTextBox;
private _ref: React.RefObject<HTMLDivElement>;
- private _outerdiv?: (dominus: HTMLElement) => void;
private _proseRef?: HTMLDivElement;
private _editorView: Opt<EditorView>;
private static _toolTipTextMenu: TooltipTextMenu | undefined = undefined;
@@ -83,6 +79,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
private _reactionDisposer: Opt<IReactionDisposer>;
private _searchReactionDisposer?: Lambda;
private _textReactionDisposer: Opt<IReactionDisposer>;
+ private _heightReactionDisposer: Opt<IReactionDisposer>;
private _proxyReactionDisposer: Opt<IReactionDisposer>;
private pullReactionDisposer: Opt<IReactionDisposer>;
private pushReactionDisposer: Opt<IReactionDisposer>;
@@ -133,13 +130,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
constructor(props: FieldViewProps) {
super(props);
- //if (this.props.firstinstance) {
FormattedTextBox.Instance = this;
- //}
- if (this.props.outer_div) {
- this._outerdiv = this.props.outer_div;
- }
-
this._ref = React.createRef();
if (this.props.isOverlay) {
DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined);
@@ -182,7 +173,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
dispatchTransaction = (tx: Transaction) => {
if (this._editorView) {
const state = this._editorView.state.apply(tx);
+ FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = true);
this._editorView.updateState(state);
+ FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = false);
if (state.selection.empty && FormattedTextBox._toolTipTextMenu) {
const marks = tx.storedMarks;
if (marks) { FormattedTextBox._toolTipTextMenu.mark_key_pressed(marks); }
@@ -215,9 +208,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
tokens.forEach((word) => {
if (terms.includes(word) && this._editorView) {
this._editorView.dispatch(this._editorView.state.tr.addMark(start, start + word.length, mark).removeStoredMark(mark));
- // else {
- // this._editorView.state.tr.addMark(start, start + word.length, mark).removeStoredMark(mark);
- // }
}
start += word.length + 1;
});
@@ -271,25 +261,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this.props.Document.layout = de.data.draggedDocuments[0];
de.data.draggedDocuments[0].isTemplate = true;
e.stopPropagation();
- // let ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
- // if (!ldocs) {
- // this.dataDoc.subBulletDocs = new List<Doc>([]);
- // }
- // ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
- // if (!ldocs) return;
- // if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) {
- // ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.dataDoc.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 }));
- // this.props.addDocument && this.props.addDocument(ldocs[0] as Doc);
- // this.props.Document.templates = new List<string>([Templates.Bullet.Layout]);
- // this.props.Document.isBullet = true;
- // }
- // let stackDoc = (ldocs[0] as Doc);
- // if (de.data.moveDocument) {
- // de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => {
- // Cast(stackDoc.data, listSpec(Doc))!.push(doc);
- // return true;
- // });
- // }
}
}
}
@@ -370,6 +341,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
);
+ this._heightReactionDisposer = reaction(
+ () => this.props.Document[WidthSym](),
+ () => this.tryUpdateHeight()
+ );
+
this._textReactionDisposer = reaction(
() => this.extensionDoc,
() => {
@@ -579,7 +555,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
dispatchTransaction: this.dispatchTransaction,
nodeViews: {
image(node, view, getPos) { return new ImageResizeView(node, view, getPos); },
- star(node, view, getPos) { return new SummarizedView(node, view, getPos); }
+ star(node, view, getPos) { return new SummarizedView(node, view, getPos); },
},
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
@@ -593,8 +569,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (this.props.selectOnLoad) {
if (!this.props.isOverlay) this.props.select(false);
else this._editorView!.focus();
- this.tryUpdateHeight();
}
+ this.tryUpdateHeight();
}
componentWillUnmount() {
@@ -604,9 +580,14 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._textReactionDisposer && this._textReactionDisposer();
this.pushReactionDisposer && this.pushReactionDisposer();
this.pullReactionDisposer && this.pullReactionDisposer();
+ this._heightReactionDisposer && this._heightReactionDisposer();
+ this._searchReactionDisposer && this._searchReactionDisposer();
}
onPointerDown = (e: React.PointerEvent): void => {
+ if (this.props.onClick && e.button === 0) {
+ e.preventDefault();
+ }
if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
e.stopPropagation();
if (FormattedTextBox._toolTipTextMenu && FormattedTextBox._toolTipTextMenu.tooltip) {
@@ -752,15 +733,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
@action
tryUpdateHeight() {
- if (this.props.isOverlay && this.props.Document.autoHeight) {
- let self = this;
+ if (this.props.Document.autoHeight && this._ref.current!.scrollHeight !== 0) {
+ console.log("DT = " + this.props.Document.title + " " + this._ref.current!.clientHeight + " " + this._ref.current!.scrollHeight + " " + this._ref.current!.textContent);
let xf = this._ref.current!.getBoundingClientRect();
- let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, xf.height);
- let nh = NumCast(this.dataDoc.nativeHeight, 0);
+ let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, this._ref.current!.textContent === "" ? 35 : this._ref.current!.scrollHeight);
+ let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
let dh = NumCast(this.props.Document.height, 0);
let sh = scrBounds.height;
const ChromeHeight = MainOverlayTextBox.Instance.ChromeHeight;
- this.props.Document.height = (nh ? dh / nh * sh : sh) + (ChromeHeight ? ChromeHeight() : 0);
+ this.props.Document.height = Math.max(10, (nh ? dh / nh * sh : sh) + (ChromeHeight ? ChromeHeight() : 0));
this.dataDoc.nativeHeight = nh ? sh : undefined;
}
}
@@ -775,12 +756,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
specificContextMenu = (e: React.MouseEvent): void => {
- let subitems: ContextMenuProps[] = [];
- subitems.push({
- description: BoolCast(this.props.Document.autoHeight) ? "Manual Height" : "Auto Height",
- event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight)), icon: "expand-arrows-alt"
- });
- ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems, icon: "text-height" });
+ // let subitems: ContextMenuProps[] = [];
+ // subitems.push({
+ // description: BoolCast(this.props.Document.autoHeight) ? "Manual Height" : "Auto Height",
+ // event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight)), icon: "expand-arrows-alt"
+ // });
+ // ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems, icon: "text-height" });
}
@@ -794,8 +775,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return (
<div className={`formattedTextBox-cont-${style}`} ref={this._ref}
style={{
+ overflowY: this.props.Document.autoHeight ? "hidden" : "auto",
height: this.props.height ? this.props.height : undefined,
- background: this.props.hideOnLeave ? "rgba(0,0,0,0.4)" : undefined,
+ background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : undefined,
opacity: this.props.hideOnLeave ? (this._entered || this.props.isSelected() || Doc.IsBrushed(this.props.Document) ? 1 : 0.1) : 1,
color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "inherit",
pointerEvents: interactive,
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 697f19f0d..b1afa3f7d 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -56,4 +56,46 @@
left: 5%;
top: 15%;
}
-} \ No newline at end of file
+}
+
+#cf {
+ position:relative;
+ width:100%;
+ margin:0 auto;
+ display:flex;
+ align-items: center;
+ height:100%;
+ .imageBox-fadeBlocker {
+ width:100%;
+ height:100%;
+ background: black;
+ display:flex;
+ flex-direction: row;
+ align-items: center;
+ z-index: 1;
+ .imageBox-fadeaway {
+ object-fit: contain;
+ width:100%;
+ height:100%;
+ }
+ }
+ }
+
+ #cf img {
+ position:absolute;
+ left:0;
+ }
+
+ .imageBox-fadeBlocker {
+ -webkit-transition: opacity 1s ease-in-out;
+ -moz-transition: opacity 1s ease-in-out;
+ -o-transition: opacity 1s ease-in-out;
+ transition: opacity 1s ease-in-out;
+ }
+ .imageBox-fadeBlocker:hover {
+ -webkit-transition: opacity 1s ease-in-out;
+ -moz-transition: opacity 1s ease-in-out;
+ -o-transition: opacity 1s ease-in-out;
+ transition: opacity 1s ease-in-out;
+ opacity:0;
+ } \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ca0f637eb..6fc94a140 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -65,7 +65,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
private dropDisposer?: DragManager.DragDropDisposer;
- @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : this.props.Document; }
+ @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
protected createDropTarget = (ele: HTMLDivElement) => {
@@ -89,13 +89,10 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.data instanceof DragManager.DocumentDragData) {
de.data.droppedDocuments.forEach(action((drop: Doc) => {
- if (de.mods === "CtrlKey") {
- Doc.ApplyTemplateTo(drop, this.props.Document, this.props.DataDoc);
- e.stopPropagation();
- } else if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) {
+ if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) {
Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url);
e.stopPropagation();
- } else if (de.mods === "CtrlKey") {
+ } else if (de.mods === "MetaKey") {
if (this.extensionDoc !== this.dataDoc) {
let layout = StrCast(drop.backgroundLayout);
if (layout.indexOf(ImageBox.name) !== -1) {
@@ -321,7 +318,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let rotation = NumCast(this.dataDoc.rotation) % 180;
let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
let aspect = realsize.height / realsize.width;
- if (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1) {
+ if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1)) {
setTimeout(action(() => {
layoutdoc.height = layoutdoc[WidthSym]() * aspect;
layoutdoc.nativeHeight = realsize.height;
@@ -404,6 +401,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1;
let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
let srcpath = paths[Math.min(paths.length - 1, this.Document.curPage || 0)];
+ let fadepath = paths[Math.min(paths.length - 1, 1)];
if (!this.props.Document.ignoreAspect && !this.props.leaveNativeSize) this.resize(srcpath, this.props.Document);
@@ -411,13 +409,22 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
<div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
onPointerDown={this.onPointerDown}
onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
- <img id={id}
- key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
- src={srcpath}
- style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
- width={nativeWidth}
- ref={this._imgRef}
- onError={this.onError} />
+ <div id="cf">
+ <img id={id}
+ key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
+ src={srcpath}
+ style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
+ width={nativeWidth}
+ ref={this._imgRef}
+ onError={this.onError} />
+ {fadepath === srcpath ? (null) : <div className="imageBox-fadeBlocker"> <img className="imageBox-fadeaway"
+ key={"fadeaway" + this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
+ src={fadepath}
+ style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
+ width={nativeWidth}
+ ref={this._imgRef}
+ onError={this.onError} /></div>}
+ </div>
{paths.length > 1 ? this.dots(paths) : (null)}
<div className="imageBox-audioBackground"
onPointerDown={this.audioDown}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 534a42efc..8001b24a7 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -68,6 +68,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
PanelWidth: returnZero,
PanelHeight: returnZero,
addDocTab: returnZero,
+ pinToPres: returnZero,
ContentScaling: returnOne
};
let contents = <FieldView {...props} />;
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index 1a4af04f8..1908889e9 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -12,9 +12,6 @@ import { library } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
library.add(faTrash);
-import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { DocumentType } from "../../documents/Documents";
interface Props {
docView: DocumentView;
diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx
index a119eb39b..90b335933 100644
--- a/src/client/views/nodes/LinkMenuItem.tsx
+++ b/src/client/views/nodes/LinkMenuItem.tsx
@@ -52,7 +52,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
}
if (this.props.destinationDoc === self.props.linkDoc.anchor2 && targetContext) {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, async document => dockingFunc(document), undefined, targetContext!);
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, async document => dockingFunc(document), undefined, targetContext);
}
else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) {
DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!));
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6450cb826..18f82ff47 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -198,7 +198,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
<PDFViewer pdf={this._pdf} url={pdfUrl.url.pathname} active={this.props.active} scrollTo={this.scrollTo} loaded={this.loaded} panY={NumCast(this.props.Document.panY)}
Document={this.props.Document} DataDoc={this.props.DataDoc}
addDocTab={this.props.addDocTab} setPanY={this.setPanY}
- addDocument={this.props.addDocument}
+ pinToPres={this.props.pinToPres} addDocument={this.props.addDocument}
fieldKey={this.props.fieldKey} fieldExtensionDoc={this.fieldExtensionDoc} />
{this.settingsPanel()}
</div>);
diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/nodes/PresBox.tsx
index bea70f00b..112d39c32 100644
--- a/src/client/views/presentationview/PresentationView.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1,23 +1,22 @@
-import { observer } from "mobx-react";
import React = require("react");
-import { observable, action, runInAction, reaction, autorun } from "mobx";
-import "./PresentationView.scss";
-import { DocumentManager } from "../../util/DocumentManager";
-import { Utils } from "../../../Utils";
-import { Doc, DocListCast, DocListCastAsync, WidthSym } from "../../../new_fields/Doc";
-import { listSpec } from "../../../new_fields/Schema";
-import { Cast, NumCast, FieldValue, PromiseValue, StrCast, BoolCast } from "../../../new_fields/Types";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import PresentationElement, { buttonIndex } from "./PresentationElement";
import { library } from '@fortawesome/fontawesome-svg-core';
+import { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, faTimes } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faArrowRight, faArrowLeft, faPlay, faStop, faPlus, faTimes, faMinus, faEdit, faEye } from '@fortawesome/free-solid-svg-icons';
-import { Docs } from "../../documents/Documents";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import PresentationViewList from "./PresentationList";
-import PresModeMenu from "./PresentationModeMenu";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
+import { listSpec } from "../../../new_fields/Schema";
+import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { Utils } from "../../../Utils";
+import { DocumentManager } from "../../util/DocumentManager";
+import { undoBatch } from "../../util/UndoManager";
+import PresentationElement, { buttonIndex } from "../presentationview/PresentationElement";
+import PresentationViewList from "../presentationview/PresentationList";
+import "../presentationview/PresentationView.scss";
+import { FieldView, FieldViewProps } from './FieldView';
+import { ContextMenu } from "../ContextMenu";
library.add(faArrowLeft);
library.add(faArrowRight);
@@ -27,19 +26,24 @@ library.add(faPlus);
library.add(faTimes);
library.add(faMinus);
library.add(faEdit);
-library.add(faEye);
export interface PresViewProps {
Documents: List<Doc>;
}
-const expandedWidth = 400;
-const presMinWidth = 300;
+const expandedWidth = 450;
@observer
-export class PresentationView extends React.Component<PresViewProps> {
- public static Instance: PresentationView;
+export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
+
+
+ public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(PresBox, fieldKey); }
+
+ public static Instance: PresBox;
+
+ //Keeping track of the doc for the current presentation -- bcz: keeping a list of current presentations shouldn't be needed. Let users create them, store them, as they see fit.
+ @computed get curPresentation() { return this.props.Document; }
//Mapping from presentation ids to a list of doc that represent a group
@observable groupMappings: Map<String, Doc[]> = new Map();
@@ -52,9 +56,6 @@ export class PresentationView extends React.Component<PresViewProps> {
//back-up so that presentation stays the way it's when refreshed
@observable presGroupBackUp: Doc = new Doc();
@observable presButtonBackUp: Doc = new Doc();
-
- //Keeping track of the doc for the current presentation
- @observable curPresentation: Doc = new Doc();
//Mapping of guids to presentations.
@observable presentationsMapping: Map<String, Doc> = new Map();
//Mapping of presentations to guid, so that select option values can be given.
@@ -66,17 +67,18 @@ export class PresentationView extends React.Component<PresViewProps> {
//Variable that holds reference to title input, so that new presentations get titles assigned.
@observable titleInputElement: HTMLInputElement | undefined;
@observable PresTitleChangeOpen: boolean = false;
- @observable presMode: boolean = false;
-
@observable opacity = 1;
@observable persistOpacity = true;
@observable labelOpacity = 0;
+ @observable presMode = false;
+
+ @observable public static CurrentPresentation: PresBox;
//initilize class variables
- constructor(props: PresViewProps) {
+ constructor(props: FieldViewProps) {
super(props);
- PresentationView.Instance = this;
+ runInAction(() => PresBox.CurrentPresentation = this);
}
@action
@@ -88,33 +90,10 @@ export class PresentationView extends React.Component<PresViewProps> {
}
}
- //The first lifecycle function that gets called to set up the current presentation.
- async componentWillMount() {
-
- this.props.Documents.forEach(async (doc, index: number) => {
-
- //For each presentation received from mainContainer, a mapping is created.
- let curDoc: Doc = await doc;
- let newGuid = Utils.GenerateGuid();
- this.presentationsKeyMapping.set(curDoc, newGuid);
- this.presentationsMapping.set(newGuid, curDoc);
-
- //The Presentation at first index gets set as default start presentation
- if (index === 0) {
- runInAction(() => this.currentSelectedPresValue = newGuid);
- runInAction(() => this.curPresentation = curDoc);
- }
- });
- }
-
- //Second lifecycle function that gets called when component mounts. It makes sure to
+ //Second lifecycle function that gets called when component mounts. It makes sure toS
//get the back-up information from previous session for the current presentation.
async componentDidMount() {
- let docAtZero = await this.props.Documents[0];
- runInAction(() => this.curPresentation = docAtZero);
-
this.setPresentationBackUps();
-
}
@@ -207,7 +186,6 @@ export class PresentationView extends React.Component<PresViewProps> {
//observable means render is re-called every time variable is changed
@observable
collapsed: boolean = false;
- closePresentation = action(() => this.curPresentation.width = 0);
next = async () => {
const current = NumCast(this.curPresentation.selectedDoc);
//asking to get document at current index
@@ -368,16 +346,11 @@ export class PresentationView extends React.Component<PresViewProps> {
//checking if curDoc has navigation open
let curDocButtons = this.presElementsMappings.get(curDoc)!.selected;
if (curDocButtons[buttonIndex.Navigate]) {
- this.jumpToTabOrRight(curDocButtons, curDoc);
+ DocumentManager.Instance.jumpToDocument(curDoc, false);
} else if (curDocButtons[buttonIndex.Show]) {
let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]);
- if (curDocButtons[buttonIndex.OpenRight]) {
- //awaiting jump so that new scale can be found, since jumping is async
- await DocumentManager.Instance.jumpToDocument(curDoc, true);
- } else {
- await DocumentManager.Instance.jumpToDocument(curDoc, true, undefined, doc => CollectionDockingView.Instance.AddTab(undefined, doc, undefined));
- }
-
+ //awaiting jump so that new scale can be found, since jumping is async
+ await DocumentManager.Instance.jumpToDocument(curDoc, true);
let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc);
curDoc.viewScale = newScale;
@@ -390,15 +363,9 @@ export class PresentationView extends React.Component<PresViewProps> {
return;
}
let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]);
- let curDocButtons = this.presElementsMappings.get(docToJump)!.selected;
-
- if (curDocButtons[buttonIndex.OpenRight]) {
- //awaiting jump so that new scale can be found, since jumping is async
- await DocumentManager.Instance.jumpToDocument(docToJump, willZoom);
- } else {
- await DocumentManager.Instance.jumpToDocument(docToJump, willZoom, undefined, doc => CollectionDockingView.Instance.AddTab(undefined, doc, undefined));
- }
+ //awaiting jump so that new scale can be found, since jumping is async
+ await DocumentManager.Instance.jumpToDocument(docToJump, willZoom);
let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc);
curDoc.viewScale = newScale;
//saving the scale that user was on
@@ -409,18 +376,6 @@ export class PresentationView extends React.Component<PresViewProps> {
}
/**
- * This function checks if right option is clicked on a presentation element, if not it does open it as a tab
- * with help of CollectionDockingView.
- */
- jumpToTabOrRight = (curDocButtons: boolean[], curDoc: Doc) => {
- if (curDocButtons[buttonIndex.OpenRight]) {
- DocumentManager.Instance.jumpToDocument(curDoc, false);
- } else {
- DocumentManager.Instance.jumpToDocument(curDoc, false, undefined, doc => CollectionDockingView.Instance.AddTab(undefined, doc, undefined));
- }
- }
-
- /**
* Async function that supposedly return the doc that is located at given index.
*/
getDocAtIndex = async (index: number) => {
@@ -464,6 +419,22 @@ export class PresentationView extends React.Component<PresViewProps> {
}
}
+ //removing it from the backUp of selected Buttons
+ // let castedList = Cast(this.presButtonBackUp.selectedButtonDocs, listSpec(Doc));
+ // if (castedList) {
+ // castedList.forEach(async (doc, indexOfDoc) => {
+ // let curDoc = await doc;
+ // let curDocId = StrCast(curDoc.docId);
+ // if (curDocId === removedDoc[Id]) {
+ // if (castedList) {
+ // castedList.splice(indexOfDoc, 1);
+ // return;
+ // }
+ // }
+ // });
+
+ // }
+ //removing it from the backUp of selected Buttons
let castedList = Cast(this.presButtonBackUp.selectedButtonDocs, listSpec(Doc));
if (castedList) {
@@ -501,16 +472,13 @@ export class PresentationView extends React.Component<PresViewProps> {
}
}
- /**
- * An alternative remove method that removes a doc from presentation by its actual
- * reference.
- */
public removeDocByRef = (doc: Doc) => {
let indexOfDoc = this.childrenDocs.indexOf(doc);
const value = FieldValue(Cast(this.curPresentation.data, listSpec(Doc)));
if (value) {
value.splice(indexOfDoc, 1)[0];
}
+ //this.RemoveDoc(indexOfDoc, true);
if (indexOfDoc !== - 1) {
return true;
}
@@ -561,23 +529,6 @@ export class PresentationView extends React.Component<PresViewProps> {
runInAction(() => this.groupMappings = new Map());
}
- /**
- * Adds a document to the presentation view
- **/
- @undoBatch
- @action
- public PinDoc(doc: Doc) {
- //add this new doc to props.Document
- const data = Cast(this.curPresentation.data, listSpec(Doc));
- if (data) {
- data.push(doc);
- } else {
- this.curPresentation.data = new List([doc]);
- }
-
- this.toggle(true);
- }
-
//Function that sets the store of the children docs.
@action
setChildrenDocs = (docList: Doc[]) => {
@@ -596,40 +547,18 @@ export class PresentationView extends React.Component<PresViewProps> {
//The function that starts or resets presentaton functionally, depending on status flag.
@action
- startOrResetPres = async () => {
+ startOrResetPres = () => {
if (this.presStatus) {
this.resetPresentation();
} else {
this.presStatus = true;
- let startIndex = await this.findStartDocument();
- this.startPresentation(startIndex);
+ this.startPresentation(0);
const current = NumCast(this.curPresentation.selectedDoc);
- this.gotoDocument(startIndex, current);
+ this.gotoDocument(0, current);
}
this.curPresentation.presStatus = this.presStatus;
}
- /**
- * This method is called to find the start document of presentation. So
- * that when user presses on play, the correct presentation element will be
- * selected.
- */
- findStartDocument = async () => {
- let docAtZero = await this.getDocAtIndex(0);
- if (docAtZero === undefined) {
- return 0;
- }
- let docAtZeroPresId = StrCast(docAtZero.presentId);
-
- if (this.groupMappings.has(docAtZeroPresId)) {
- let group = this.groupMappings.get(docAtZeroPresId)!;
- let lastDoc = group[group.length - 1];
- return this.childrenDocs.indexOf(lastDoc);
- } else {
- return 0;
- }
- }
-
//The function that resets the presentation by removing every action done by it. It also
//stops the presentaton.
@action
@@ -675,71 +604,15 @@ export class PresentationView extends React.Component<PresViewProps> {
}
- /**
- * The function that is called to add a new presentation to the presentationView.
- * It sets up te mappings and local copies of it. Resets the groupings and presentation.
- * Makes the new presentation current selected, and retrieve the back-Ups if present.
- */
- @action
- addNewPresentation = (presTitle: string) => {
- //creating a new presentation doc
- let newPresentationDoc = Docs.Create.TreeDocument([], { title: presTitle });
- this.props.Documents.push(newPresentationDoc);
-
- //setting that new doc as current
- this.curPresentation = newPresentationDoc;
-
- //storing the doc in local copies for easier access
- let newGuid = Utils.GenerateGuid();
- this.presentationsMapping.set(newGuid, newPresentationDoc);
- this.presentationsKeyMapping.set(newPresentationDoc, newGuid);
-
- //resetting the previous presentation's actions so that new presentation can be loaded.
- this.resetGroupIds();
- this.resetPresentation();
- this.presElementsMappings = new Map();
- this.currentSelectedPresValue = newGuid;
- this.setPresentationBackUps();
-
- }
-
- /**
- * The function that is called to change the current selected presentation.
- * Changes the presentation, also resetting groupings and presentation in process.
- * Plus retrieving the backUps for the newly selected presentation.
- */
- @action
- getSelectedPresentation = (e: React.ChangeEvent<HTMLSelectElement>) => {
- //get the guid of the selected presentation
- let selectedGuid = e.target.value;
- //set that as current presentation
- this.curPresentation = this.presentationsMapping.get(selectedGuid)!;
-
- //reset current Presentations local things so that new one can be loaded
- this.resetGroupIds();
- this.resetPresentation();
- this.presElementsMappings = new Map();
- this.currentSelectedPresValue = selectedGuid;
- this.setPresentationBackUps();
-
-
- }
/**
* The function that is called to render either select for presentations, or title inputting.
*/
renderSelectOrPresSelection = () => {
- let presentationList = DocListCast(this.props.Documents);
if (this.PresTitleInputOpen || this.PresTitleChangeOpen) {
return <input ref={(e) => this.titleInputElement = e!} type="text" className="presentationView-title" placeholder="Enter Name!" onKeyDown={this.submitPresentationTitle} />;
} else {
- return <select value={this.currentSelectedPresValue} id="pres_selector" className="presentationView-title" onChange={this.getSelectedPresentation}>
- {presentationList.map((doc: Doc, index: number) => {
- let mappedGuid = this.presentationsKeyMapping.get(doc);
- let docGuid: string = mappedGuid ? mappedGuid.toString() : "";
- return <option key={docGuid} value={docGuid}>{StrCast(doc.title)}</option>;
- })}
- </select>;
+ return (null);
}
}
@@ -752,87 +625,12 @@ export class PresentationView extends React.Component<PresViewProps> {
if (e.keyCode === 13) {
let presTitle = this.titleInputElement!.value;
this.titleInputElement!.value = "";
- if (this.PresTitleInputOpen) {
- if (presTitle === "") {
- presTitle = "Presentation";
- }
- this.PresTitleInputOpen = false;
- this.addNewPresentation(presTitle);
- } else if (this.PresTitleChangeOpen) {
+ if (this.PresTitleChangeOpen) {
this.PresTitleChangeOpen = false;
this.changePresentationTitle(presTitle);
}
}
}
-
- /**
- * The function that is called to remove a presentation from all its copies, and the main Container's
- * list. Sets up the next presentation as current.
- */
- @action
- removePresentation = async () => {
- if (this.presentationsMapping.size !== 1) {
- let presentationList = Cast(this.props.Documents, listSpec(Doc));
- let batch = UndoManager.StartBatch("presRemoval");
-
- //getting the presentation that will be removed
- let removedDoc = this.presentationsMapping.get(this.currentSelectedPresValue!);
- //that presentation is removed
- presentationList!.splice(presentationList!.indexOf(removedDoc!), 1);
-
- //its mappings are removed from local copies
- this.presentationsKeyMapping.delete(removedDoc!);
- this.presentationsMapping.delete(this.currentSelectedPresValue!);
-
- //the next presentation is set as current
- let remainingPresentations = this.presentationsMapping.values();
- let nextDoc = remainingPresentations.next().value;
- this.curPresentation = nextDoc;
-
-
- //Storing these for being able to undo changes
- let curGuid = this.currentSelectedPresValue!;
- let curPresStatus = this.presStatus;
-
- //resetting the groups and presentation actions so that next presentation gets loaded
- this.resetGroupIds();
- this.resetPresentation();
- this.currentSelectedPresValue = this.presentationsKeyMapping.get(nextDoc)!.toString();
- this.setPresentationBackUps();
-
- //Storing for undo
- let currentGroups = this.groupMappings;
- let curPresElemMapping = this.presElementsMappings;
-
- //Event to undo actions that are not related to doc directly, aka. local things
- UndoManager.AddEvent({
- undo: action(() => {
- this.curPresentation = removedDoc!;
- this.presentationsMapping.set(curGuid, removedDoc!);
- this.presentationsKeyMapping.set(removedDoc!, curGuid);
- this.currentSelectedPresValue = curGuid;
-
- this.presStatus = curPresStatus;
- this.groupMappings = currentGroups;
- this.presElementsMappings = curPresElemMapping;
- this.setPresentationBackUps();
-
- }),
- redo: action(() => {
- this.curPresentation = nextDoc;
- this.presStatus = false;
- this.presentationsKeyMapping.delete(removedDoc!);
- this.presentationsMapping.delete(curGuid);
- this.currentSelectedPresValue = this.presentationsKeyMapping.get(nextDoc)!.toString();
- this.setPresentationBackUps();
-
- }),
- });
-
- batch.end();
- }
- }
-
/**
* The function that is called to change title of presentation to what user entered.
*/
@@ -844,151 +642,60 @@ export class PresentationView extends React.Component<PresViewProps> {
this.curPresentation.title = newTitle;
}
- /**
- * On pointer down element that is catched on resizer of te
- * presentation view. Sets up the event listeners to change the size with
- * mouse move.
- */
- _downsize = 0;
- onPointerDown = (e: React.PointerEvent) => {
- this._downsize = e.clientX;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointerup", this.onPointerUp);
- e.stopPropagation();
- e.preventDefault();
- }
- /**
- * Changes the size of the presentation view, with mouse move.
- * Minimum size is set to 300, so that every button is visible.
- */
- @action
- onPointerMove = (e: PointerEvent) => {
-
- this.curPresentation.width = Math.max(window.innerWidth - e.clientX, presMinWidth);
- }
-
- /**
- * The method that is called on pointer up event. It checks if the button is just
- * clicked so that presentation view will be closed. The way it's done is to check
- * for minimal pixel change like 4, and accept it as it's just a click on top of the dragger.
- */
- @action
- onPointerUp = (e: PointerEvent) => {
- if (Math.abs(e.clientX - this._downsize) < 4) {
- let presWidth = NumCast(this.curPresentation.width);
- if (presWidth - presMinWidth !== 0) {
- this.curPresentation.width = 0;
- }
- if (presWidth === 0) {
- this.curPresentation.width = presMinWidth;
- }
- }
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- }
-
- /**
- * This function is a setter that opens up the
- * presentation mode, by setting it's render flag
- * to true. It also closes the presentation view.
- */
- @action
- openPresMode = () => {
- if (!this.presMode) {
- this.curPresentation.width = 0;
- this.presMode = true;
- }
- }
-
- /**
- * This function closes the presentation mode by setting its
- * render flag to false. It also opens up the presentation view.
- * By setting it to it's minimum size.
- */
- @action
- closePresMode = () => {
- if (this.presMode) {
- this.presMode = false;
- this.curPresentation.width = presMinWidth;
- }
-
+ addPressElem = (keyDoc: Doc, elem: PresentationElement) => {
+ this.presElementsMappings.set(keyDoc, elem);
}
- /**
- * Function that is called to render the presentation mode, depending on its flag.
- */
- renderPresMode = () => {
- if (this.presMode) {
- return <PresModeMenu next={this.next} back={this.back} startOrResetPres={this.startOrResetPres} presStatus={this.presStatus} closePresMode={this.closePresMode} />;
- } else {
- return (null);
- }
+ minimize = undoBatch(action(() => {
+ this.presMode = true;
+ this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close");
+ }));
+ specificContextMenu = (e: React.MouseEvent): void => {
+ ContextMenu.Instance.addItem({ description: "Make Current Presentation", event: action(() => Doc.UserDoc().curPresentation = this.props.Document), icon: "asterisk" });
}
render() {
- let width = NumCast(this.curPresentation.width);
-
+ let width = "100%"; //NumCast(this.curPresentation.width)
return (
- <div>
- <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflowY: "scroll", overflowX: "hidden", opacity: this.opacity, transition: "0.7s opacity ease" }}>
- <div className="presentationView-heading">
- {this.renderSelectOrPresSelection()}
- <button title="Close Presentation" className='presentation-icon' onClick={this.closePresentation}><FontAwesomeIcon icon={"times"} /></button>
- <button title="Open Presentation Mode" className="presentation-icon" style={{ marginRight: 10 }} onClick={this.openPresMode}><FontAwesomeIcon icon={"eye"} /></button>
- <button title="Add Presentation" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
- runInAction(() => { if (this.PresTitleChangeOpen) { this.PresTitleChangeOpen = false; } });
- runInAction(() => this.PresTitleInputOpen ? this.PresTitleInputOpen = false : this.PresTitleInputOpen = true);
- }}><FontAwesomeIcon icon={"plus"} /></button>
- <button title="Remove Presentation" className='presentation-icon' style={{ marginRight: 10 }} onClick={this.removePresentation}><FontAwesomeIcon icon={"minus"} /></button>
- <button title="Change Presentation Title" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
- runInAction(() => { if (this.PresTitleInputOpen) { this.PresTitleInputOpen = false; } });
- runInAction(() => this.PresTitleChangeOpen ? this.PresTitleChangeOpen = false : this.PresTitleChangeOpen = true);
- }}><FontAwesomeIcon icon={"edit"} /></button>
- </div>
- <div className="presentation-buttons">
- <button title="Back" className="presentation-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
- {this.renderPlayPauseButton()}
- <button title="Next" className="presentation-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
- </div>
-
- <PresentationViewList
- mainDocument={this.curPresentation}
- deleteDocument={this.RemoveDoc}
- gotoDocument={this.gotoDocument}
- groupMappings={this.groupMappings}
- PresElementsMappings={this.presElementsMappings}
- setChildrenDocs={this.setChildrenDocs}
- presStatus={this.presStatus}
- presButtonBackUp={this.presButtonBackUp}
- presGroupBackUp={this.presGroupBackUp}
- removeDocByRef={this.removeDocByRef}
- clearElemMap={() => this.presElementsMappings.clear()}
- />
- <input
- type="checkbox"
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
- this.persistOpacity = e.target.checked;
- this.opacity = this.persistOpacity ? 1 : 0.4;
- })}
- checked={this.persistOpacity}
- style={{ position: "absolute", bottom: 5, left: 5 }}
- onPointerEnter={action(() => this.labelOpacity = 1)}
- onPointerLeave={action(() => this.labelOpacity = 0)}
- />
- <p style={{ position: "absolute", bottom: 1, left: 22, opacity: this.labelOpacity, transition: "0.7s opacity ease" }}>opacity {this.persistOpacity ? "persistent" : "on focus"}</p>
- </div>
- <div className="mainView-libraryHandle"
- style={{ cursor: "ew-resize", right: `${width - 10}px`, backgroundColor: "white", opacity: this.opacity, transition: "0.7s opacity ease" }}
- onPointerDown={this.onPointerDown}>
- <span title="library View Dragger" style={{ width: "100%", height: "100%", position: "absolute" }} />
+ <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onContextMenu={this.specificContextMenu}
+ onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))}
+ style={{ width: width, opacity: this.opacity, }}>
+ <div className="presentation-buttons">
+ <button title="Back" className="presentation-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
+ {this.renderPlayPauseButton()}
+ <button title="Next" className="presentation-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
+ <button title="Minimize" className="presentation-button" onClick={this.minimize}><FontAwesomeIcon icon={"eye"} /></button>
</div>
- {this.renderPresMode()}
-
+ <input
+ type="checkbox"
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+ this.persistOpacity = e.target.checked;
+ this.opacity = this.persistOpacity ? 1 : 0.4;
+ })}
+ checked={this.persistOpacity}
+ style={{ position: "absolute", bottom: 5, left: 5 }}
+ onPointerEnter={action(() => this.labelOpacity = 1)}
+ onPointerLeave={action(() => this.labelOpacity = 0)}
+ />
+ <p style={{ position: "absolute", bottom: 1, left: 22, opacity: this.labelOpacity, transition: "0.7s opacity ease" }}>opacity {this.persistOpacity ? "persistent" : "on focus"}</p>
+ <PresentationViewList
+ mainDocument={this.curPresentation}
+ deleteDocument={this.RemoveDoc}
+ gotoDocument={this.gotoDocument}
+ groupMappings={this.groupMappings}
+ PresElementsMappings={this.presElementsMappings}
+ setChildrenDocs={this.setChildrenDocs}
+ presStatus={this.presStatus}
+ presButtonBackUp={this.presButtonBackUp}
+ presGroupBackUp={this.presGroupBackUp}
+ removeDocByRef={this.removeDocByRef}
+ clearElemMap={() => this.presElementsMappings.clear()}
+ />
</div>
);
}
-}
+
+
+} \ No newline at end of file
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index eb09b0693..07774263c 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -1,16 +1,18 @@
-
-.webBox-cont, .webBox-cont-interactive{
+.webBox-cont,
+.webBox-cont-interactive {
padding: 0vw;
position: absolute;
top: 0;
- left:0;
+ left: 0;
width: 100%;
height: 100%;
overflow: auto;
- pointer-events: none ;
+ pointer-events: none;
}
+
.webBox-cont-interactive {
pointer-events: all;
+
span {
user-select: text !important;
}
@@ -18,8 +20,8 @@
#webBox-htmlSpan {
position: absolute;
- top:0;
- left:0;
+ top: 0;
+ left: 0;
}
.webBox-overlay {
@@ -29,8 +31,52 @@
}
.webBox-button {
- padding : 0vw;
+ padding: 0vw;
border: none;
- width : 100%;
+ width: 100%;
+ height: 100%;
+}
+
+.webView-urlEditor {
+ position: relative;
+ opacity: 0.9;
+ z-index: 9001;
+ transition: top .5s;
+ background: lightgrey;
+ padding: 10px;
+
+
+ .urlEditor {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ padding-bottom: 10px;
+ overflow: hidden;
+
+ .editorBase {
+ display: flex;
+
+ .editor-collapse {
+ transition: all .5s, opacity 0.3s;
+ position: absolute;
+ width: 40px;
+ transform-origin: top left;
+ }
+ }
+
+ button:hover {
+ transform: scale(1);
+ }
+ }
+}
+
+.webpage-urlInput {
+ padding: 12px 10px 11px 10px;
+ border: 0px;
+ color: grey;
+ letter-spacing: 2px;
+ outline-color: black;
+ background: rgb(238, 238, 238);
+ width: 100%;
+ margin-right: 10px;
height: 100%;
} \ No newline at end of file
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c8749b7cd..9b66b2431 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,19 +1,25 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, observable } from "mobx";
import { observer } from "mobx-react";
+import { FieldResult } from "../../../new_fields/Doc";
import { HtmlField } from "../../../new_fields/HtmlField";
+import { InkTool } from "../../../new_fields/InkField";
+import { Cast, NumCast } from "../../../new_fields/Types";
import { WebField } from "../../../new_fields/URLField";
+import { Utils } from "../../../Utils";
import { DocumentDecorations } from "../DocumentDecorations";
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
+import { KeyValueBox } from "./KeyValueBox";
import "./WebBox.scss";
import React = require("react");
-import { InkTool } from "../../../new_fields/InkField";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
-import { Utils } from "../../../Utils";
@observer
export class WebBox extends React.Component<FieldViewProps> {
public static LayoutString() { return FieldView.LayoutString(WebBox); }
+ @observable private collapsed: boolean = true;
+ @observable private url: string = "";
componentWillMount() {
@@ -28,6 +34,71 @@ export class WebBox extends React.Component<FieldViewProps> {
this.props.Document.height = NumCast(this.props.Document.width) / youtubeaspect;
}
}
+
+ this.setURL();
+ }
+
+ @action
+ onURLChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.url = e.target.value;
+ }
+
+ @action
+ submitURL = () => {
+ const script = KeyValueBox.CompileKVPScript(`new WebField("${this.url}")`);
+ if (!script) return;
+ KeyValueBox.ApplyKVPScript(this.props.Document, "data", script);
+ }
+
+ @action
+ setURL() {
+ let urlField: FieldResult<WebField> = Cast(this.props.Document.data, WebField);
+ if (urlField) this.url = urlField.url.toString();
+ else this.url = "";
+ }
+
+ onValueKeyDown = async (e: React.KeyboardEvent) => {
+ if (e.key === "Enter") {
+ e.stopPropagation();
+ this.submitURL();
+ }
+ }
+
+ urlEditor() {
+ return (
+ <div className="webView-urlEditor" style={{ top: this.collapsed ? -70 : 0 }}>
+ <div className="urlEditor">
+ <div className="editorBase">
+ <button className="editor-collapse"
+ style={{
+ top: this.collapsed ? 70 : 10,
+ transform: `rotate(${this.collapsed ? 180 : 0}deg) scale(${this.collapsed ? 0.5 : 1}) translate(${this.collapsed ? "-100%, -100%" : "0, 0"})`,
+ opacity: (this.collapsed && !this.props.isSelected()) ? 0 : 0.9,
+ left: (this.collapsed ? 0 : "unset"),
+ }}
+ title="Collapse Url Editor" onClick={this.toggleCollapse}>
+ <FontAwesomeIcon icon="caret-up" size="2x" />
+ </button>
+ <div style={{ marginLeft: 54, width: "100%", display: this.collapsed ? "none" : "flex" }}>
+ <input className="webpage-urlInput"
+ placeholder="ENTER URL"
+ value={this.url}
+ onChange={this.onURLChange}
+ onKeyDown={this.onValueKeyDown}
+ />
+ <button className="submitUrl" onClick={this.submitURL}>
+ SUBMIT URL
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ @action
+ toggleCollapse = () => {
+ this.collapsed = !this.collapsed;
}
_ignore = 0;
@@ -53,12 +124,13 @@ export class WebBox extends React.Component<FieldViewProps> {
if (field instanceof HtmlField) {
view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
} else if (field instanceof WebField) {
- view = <iframe src={Utils.CorsProxy(field.url.href)} style={{ position: "absolute", width: "100%", height: "100%" }} />;
+ view = <iframe src={Utils.CorsProxy(field.url.href)} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
} else {
- view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%" }} />;
+ view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
}
let content =
<div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
+ {this.urlEditor()}
{view}
</div>;
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 7ba7b6d14..6f77a0a5b 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -6,10 +6,10 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
import { DocumentManager } from "../../util/DocumentManager";
-import { PresentationView } from "../presentationview/PresentationView";
import PDFMenu from "./PDFMenu";
import "./Annotation.scss";
import { scale } from "./PDFViewer";
+import { PresBox } from "../nodes/PresBox";
interface IAnnotationProps {
anno: Doc;
@@ -18,6 +18,7 @@ interface IAnnotationProps {
fieldExtensionDoc: Doc;
scrollTo?: (n: number) => void;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+ pinToPres: (document: Doc) => void;
}
export default class Annotation extends React.Component<IAnnotationProps> {
@@ -37,6 +38,7 @@ interface IRegionAnnotationProps {
fieldExtensionDoc: Doc;
scrollTo?: (n: number) => void;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+ pinToPres: (document: Doc) => void;
document: Doc;
}
@@ -81,7 +83,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
pinToPres = () => {
let group = FieldValue(Cast(this.props.document.group, Doc));
- group && PresentationView.Instance.PinDoc(group);
+ group && this.props.pinToPres(group);
}
@action
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 08674720d..258e218f0 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -36,6 +36,7 @@ interface IViewerProps {
active: () => boolean;
setPanY?: (n: number) => void;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+ pinToPres: (document: Doc) => void;
addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 7ca9d2d7d..0de1777e6 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -19,8 +19,8 @@ interface IPageProps {
numPages: number;
page: number;
pageLoaded: (page: Pdfjs.PDFPageViewport) => void;
- fieldExtensionDoc: Doc,
- Document: Doc,
+ fieldExtensionDoc: Doc;
+ Document: Doc;
renderAnnotations: (annotations: Doc[], removeOld: boolean) => void;
sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
createAnnotation: (div: HTMLDivElement, page: number) => void;
@@ -112,7 +112,7 @@ export default class Page extends React.Component<IPageProps> {
if (!BoolCast(annotationDoc.linkedToDoc)) {
let annotations = await DocListCastAsync(annotationDoc.annotations);
annotations && annotations.forEach(anno => anno.target = targetDoc);
- DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`)
+ DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`);
}
}
},
@@ -151,6 +151,9 @@ export default class Page extends React.Component<IPageProps> {
PDFMenu.Instance.fadeOut(true);
if (e.target && (e.target as any).parentElement === this._textLayer.current) {
e.stopPropagation();
+ if (!e.ctrlKey) {
+ this.props.sendAnnotations([], -1);
+ }
}
else {
// set marquee x and y positions to the spatially transformed position
@@ -161,14 +164,12 @@ export default class Page extends React.Component<IPageProps> {
}
this._marqueeing = true;
this._marquee.current && (this._marquee.current.style.opacity = "0.2");
+ this.props.sendAnnotations([], -1);
}
document.removeEventListener("pointermove", this.onSelectStart);
document.addEventListener("pointermove", this.onSelectStart);
document.removeEventListener("pointerup", this.onSelectEnd);
document.addEventListener("pointerup", this.onSelectEnd);
- if (!e.ctrlKey) {
- this.props.sendAnnotations([], -1);
- }
}
}
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index d98b66324..912970a50 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -15,7 +15,7 @@ import { SelectionManager } from "../../util/SelectionManager";
import { ContextMenu } from "../ContextMenu";
import { Transform } from "../../util/Transform";
import { DocumentView } from "../nodes/DocumentView";
-import { DocumentType } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
import React = require("react");
@@ -839,6 +839,7 @@ export default class PresentationElement extends React.Component<PresentationEle
removeDocument={returnFalse}
ScreenToLocalTransform={Transform.Identity}
addDocTab={returnFalse}
+ pinToPres={returnFalse}
renderDepth={1}
PanelWidth={() => 350}
PanelHeight={() => 90}
diff --git a/src/client/views/presentationview/PresentationList.tsx b/src/client/views/presentationview/PresentationList.tsx
index e853c4070..288ade042 100644
--- a/src/client/views/presentationview/PresentationList.tsx
+++ b/src/client/views/presentationview/PresentationList.tsx
@@ -6,10 +6,7 @@ import { Utils } from "../../../Utils";
import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
import { NumCast, StrCast } from "../../../new_fields/Types";
import { Id } from "../../../new_fields/FieldSymbols";
-import PresentationElement, { buttonIndex } from "./PresentationElement";
-import "../../../new_fields/Doc";
-
-
+import PresentationElement from "./PresentationElement";
interface PresListProps {
diff --git a/src/client/views/presentationview/PresentationView.scss b/src/client/views/presentationview/PresentationView.scss
index 65b09c833..b0968132b 100644
--- a/src/client/views/presentationview/PresentationView.scss
+++ b/src/client/views/presentationview/PresentationView.scss
@@ -7,7 +7,9 @@
top: 0;
bottom: 0;
letter-spacing: 2px;
-
+ overflow: hidden;
+ transition: 0.7s opacity ease;
+ pointer-events: all;
}
.presentationView-item {
@@ -93,7 +95,7 @@
.presentation-button {
margin-right: 2.5%;
margin-left: 2.5%;
- width: 25%;
+ width: 20%;
}
.presentation-buttons {
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index 3e8582d61..c13d1d276 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -6,7 +6,7 @@ import { faTimes, faCheckCircle, faObjectGroup } from '@fortawesome/free-solid-s
import { library } from '@fortawesome/fontawesome-svg-core';
import { Doc } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
-import { DocumentType } from '../../documents/Documents';
+import { DocumentType } from "../../documents/DocumentTypes";
import { Cast, StrCast } from '../../../new_fields/Types';
import * as _ from "lodash";
import { IconBar } from './IconBar';
diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx
index 4712b0abc..c9924222f 100644
--- a/src/client/views/search/IconBar.tsx
+++ b/src/client/views/search/IconBar.tsx
@@ -4,7 +4,6 @@ import { observable, action } from 'mobx';
// import "./SearchBox.scss";
import "./IconBar.scss";
import "./IconButton.scss";
-import { DocumentType } from '../../documents/Documents';
import { faSearch, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia, faBan, faTimesCircle, faCheckCircle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
diff --git a/src/client/views/search/IconButton.tsx b/src/client/views/search/IconButton.tsx
index 5d23f6eeb..d2cfe7fad 100644
--- a/src/client/views/search/IconButton.tsx
+++ b/src/client/views/search/IconButton.tsx
@@ -6,7 +6,7 @@ import "./IconButton.scss";
import { faSearch, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia, faBan, faVideo, faCaretDown } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { library, icon } from '@fortawesome/fontawesome-svg-core';
-import { DocumentType } from '../../documents/Documents';
+import { DocumentType } from "../../documents/DocumentTypes";
import '../globalCssVariables.scss';
import * as _ from "lodash";
import { IconBar } from './IconBar';
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index fcdc79220..5ed33a596 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -37,6 +37,11 @@
margin-left: 2px;
margin-right: 2px
}
+
+ &.searchBox-close {
+ color: $light-color;
+ max-height: 32px;
+ }
}
}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 2214ac8af..2ad69daca 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -4,6 +4,8 @@ import { observable, action, runInAction, flow, computed } from 'mobx';
import "./SearchBox.scss";
import "./FilterBox.scss";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faTimes } from '@fortawesome/free-solid-svg-icons';
+import { library } from '@fortawesome/fontawesome-svg-core';
import { SetupDrag } from '../../util/DragManager';
import { Docs } from '../../documents/Documents';
import { NumCast, Cast } from '../../../new_fields/Types';
@@ -14,8 +16,12 @@ import { Id } from '../../../new_fields/FieldSymbols';
import { SearchUtil } from '../../util/SearchUtil';
import { RouteStore } from '../../../server/RouteStore';
import { FilterBox } from './FilterBox';
+import { ReadStream } from 'fs';
+import * as $ from 'jquery';
+import { MainView } from '../MainView';
import { Utils } from '../../../Utils';
+library.add(faTimes);
@observer
export class SearchBox extends React.Component {
@@ -29,6 +35,7 @@ export class SearchBox extends React.Component {
@observable private _visibleElements: JSX.Element[] = [];
private resultsRef = React.createRef<HTMLDivElement>();
+ public inputRef = React.createRef<HTMLInputElement>();
private _isSearch: ("search" | "placeholder" | undefined)[] = [];
private _numTotalResults = -1;
@@ -46,6 +53,15 @@ export class SearchBox extends React.Component {
this.resultsScrolled = this.resultsScrolled.bind(this);
}
+ componentDidMount = () => {
+ if (this.inputRef.current) {
+ this.inputRef.current.focus();
+ runInAction(() => {
+ this._searchbarOpen = true;
+ });
+ }
+ }
+
@action
getViews = async (doc: Doc) => {
const results = await SearchUtil.GetViewsOfDocument(doc);
@@ -321,11 +337,12 @@ export class SearchBox extends React.Component {
<span className="searchBox-barChild searchBox-collection" onPointerDown={SetupDrag(this.collectionRef, this.startDragCollection)} ref={this.collectionRef} title="Drag Results as Collection">
<FontAwesomeIcon icon="object-group" size="lg" />
</span>
- <input value={this._searchString} onChange={this.onChange} type="text" placeholder="Search..."
+ <input value={this._searchString} onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this.inputRef}
className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter}
style={{ width: this._searchbarOpen ? "500px" : "100px" }} />
<button className="searchBox-barChild searchBox-submit" onClick={this.submitSearch} onPointerDown={FilterBox.Instance.stopProp}>Submit</button>
<button className="searchBox-barChild searchBox-filter" onClick={FilterBox.Instance.openFilter} onPointerDown={FilterBox.Instance.stopProp}>Filter</button>
+ <button className="searchBox-barChild searchBox-close" title={"Close Search Bar"} onPointerDown={MainView.Instance.toggleSearch}><FontAwesomeIcon icon={faTimes} size="lg" /></button>
</div>
<div className="searchBox-results" onScroll={this.resultsScrolled} style={{
display: this._resultsOpen ? "flex" : "none",
@@ -336,5 +353,4 @@ export class SearchBox extends React.Component {
</div>
);
}
-
} \ No newline at end of file
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 8201aa374..41fc49c2e 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -11,7 +11,7 @@ import { RichTextField } from "../../../new_fields/RichTextField";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
-import { DocumentType } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, SetupDrag } from "../../util/DragManager";
import { LinkManager } from "../../util/LinkManager";
@@ -203,6 +203,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
removeDocument={returnFalse}
ScreenToLocalTransform={Transform.Identity}
addDocTab={returnFalse}
+ pinToPres={returnFalse}
renderDepth={1}
PanelWidth={returnXDimension}
PanelHeight={returnYDimension}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 543ee46cc..ca05dfa45 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -1,19 +1,18 @@
-import { observable, action, runInAction } from "mobx";
-import { serializable, primitive, map, alias, list, PropSchema, custom } from "serializr";
-import { autoObject, SerializationHelper, Deserializable, afterDocDeserialize } from "../client/util/SerializationHelper";
+import { observable, ObservableMap, runInAction } from "mobx";
+import { alias, map, serializable } from "serializr";
import { DocServer } from "../client/DocServer";
-import { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util";
-import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, BoolCast, StrCast } from "./Types";
-import { listSpec } from "./Schema";
-import { ObjectField } from "./ObjectField";
-import { RefField, FieldId } from "./RefField";
-import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols";
-import { scriptingGlobal, CompileScript, Scripting } from "../client/util/Scripting";
+import { DocumentType } from "../client/documents/DocumentTypes";
+import { CompileScript, Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper";
+import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, Update } from "./FieldSymbols";
import { List } from "./List";
-import { DocumentType } from "../client/documents/Documents";
-import { ComputedField, ScriptField } from "./ScriptField";
+import { ObjectField } from "./ObjectField";
import { PrefetchProxy, ProxyField } from "./Proxy";
-import { CurrentUserUtils } from "../server/authentication/models/current_user_utils";
+import { FieldId, RefField } from "./RefField";
+import { listSpec } from "./Schema";
+import { ComputedField } from "./ScriptField";
+import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast, ToConstructor } from "./Types";
+import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -72,6 +71,7 @@ export const HeightSym = Symbol("Height");
export const UpdatingFromServer = Symbol("UpdatingFromServer");
const CachedUpdates = Symbol("Cached updates");
+
function fetchProto(doc: Doc) {
const proto = doc.proto;
if (proto instanceof Promise) {
@@ -151,10 +151,10 @@ export class Doc extends RefField {
}
private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
-
+ public static CurrentUserEmail: string = "";
public async [HandleUpdate](diff: any) {
const set = diff.$set;
- const sameAuthor = this.author === CurrentUserUtils.email;
+ const sameAuthor = this.author === Doc.CurrentUserEmail;
if (set) {
for (const key in set) {
if (!key.startsWith("fields.")) {
@@ -310,6 +310,10 @@ export namespace Doc {
export function GetProto(doc: Doc) {
return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc);
}
+ export function GetDataDoc(doc: Doc): Doc {
+ let proto = Doc.GetProto(doc);
+ return proto === doc ? proto : Doc.GetDataDoc(proto);
+ }
export function allKeys(doc: Doc): string[] {
const results: Set<string> = new Set;
@@ -323,14 +327,12 @@ export namespace Doc {
return Array.from(results);
}
- export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean) {
+ export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
if (target[key] === undefined) {
- console.log("target key undefined");
Doc.GetProto(target)[key] = new List<Doc>();
}
let list = Cast(target[key], listSpec(Doc));
if (list) {
- console.log("has list");
if (allowDuplicates !== true) {
let pind = list.reduce((l, d, i) => d instanceof Doc && Doc.AreProtosEqual(d, doc) ? i : l, -1);
if (pind !== -1) {
@@ -338,15 +340,18 @@ export namespace Doc {
}
}
if (first) {
- console.log("is first");
list.splice(0, 0, doc);
}
else {
- console.log("not first");
let ind = relativeTo ? list.indexOf(relativeTo) : -1;
- if (ind === -1) list.push(doc);
- else list.splice(before ? ind : ind + 1, 0, doc);
- console.log("index", ind);
+ if (ind === -1) {
+ if (reversed) list.splice(0, 0, doc);
+ else list.push(doc);
+ }
+ else {
+ if (reversed) list.splice(before ? (list.length - ind) + 1 : list.length - ind, 0, doc);
+ else list.splice(before ? ind : ind + 1, 0, doc);
+ }
}
}
return true;
@@ -384,7 +389,7 @@ export namespace Doc {
docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends.
docExtensionForField.type = DocumentType.EXTENSION;
let proto: Doc | undefined = doc;
- while (proto && !Doc.IsPrototype(proto)) {
+ while (proto && !Doc.IsPrototype(proto) && proto.proto) {
proto = proto.proto;
}
(proto ? proto : doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField);
@@ -441,21 +446,24 @@ export namespace Doc {
if (expandedTemplateLayout instanceof Doc) {
return expandedTemplateLayout;
}
+ if (expandedTemplateLayout instanceof Promise) {
+ return undefined;
+ }
let expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]";
expandedTemplateLayout = dataDoc[expandedLayoutFieldKey];
if (expandedTemplateLayout instanceof Doc) {
return expandedTemplateLayout;
}
if (expandedTemplateLayout === undefined) {
- setTimeout(() =>
- dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]"), 0);
+ setTimeout(() => dataDoc[expandedLayoutFieldKey] === undefined &&
+ (dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]")), 0);
}
- return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending.
+ return undefined; // use the templateLayout when it's not a template or the expandedTemplate is pending.
}
export function GetLayoutDataDocPair(doc: Doc, dataDoc: Doc | undefined, fieldKey: string, childDocLayout: Doc) {
- let layoutDoc = childDocLayout;
- let resolvedDataDoc = !doc.isTemplate && dataDoc !== doc ? dataDoc : undefined;
+ let layoutDoc: Doc | undefined = childDocLayout;
+ let resolvedDataDoc = !doc.isTemplate && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) {
Doc.UpdateDocumentExtensionForField(resolvedDataDoc, fieldKey);
let fieldExtensionDoc = Doc.resolvedFieldDataDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)), "dummy");
@@ -503,7 +511,8 @@ export namespace Doc {
let _applyCount: number = 0;
export function ApplyTemplate(templateDoc: Doc) {
if (!templateDoc) return undefined;
- let otherdoc = new Doc();
+ let datadoc = new Doc();
+ let otherdoc = Doc.MakeDelegate(datadoc);
otherdoc.width = templateDoc[WidthSym]();
otherdoc.height = templateDoc[HeightSym]();
otherdoc.title = templateDoc.title + "(..." + _applyCount++ + ")";
@@ -511,6 +520,8 @@ export namespace Doc {
otherdoc.miniLayout = StrCast(templateDoc.miniLayout);
otherdoc.detailedLayout = otherdoc.layout;
otherdoc.type = DocumentType.TEMPLATE;
+ !templateDoc.nativeWidth && (otherdoc.nativeWidth = 0);
+ !templateDoc.nativeHeight && (otherdoc.nativeHeight = 0);
return otherdoc;
}
export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) {
@@ -519,6 +530,7 @@ export namespace Doc {
target.nativeHeight = Doc.GetProto(target).nativeHeight = undefined;
target.width = templateDoc.width;
target.height = templateDoc.height;
+ target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
Doc.GetProto(target).type = DocumentType.TEMPLATE;
if (targetData && targetData.layout === target) {
targetData.layout = temp;
@@ -529,6 +541,8 @@ export namespace Doc {
target.miniLayout = StrCast(templateDoc.miniLayout);
target.detailedLayout = target.layout;
}
+ !templateDoc.nativeWidth && (target.nativeWidth = 0);
+ !templateDoc.nativeHeight && (target.nativeHeight = 0);
}
export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) {
@@ -585,24 +599,28 @@ export namespace Doc {
});
}
- export class DocBrush {
- @observable BrushedDoc: Doc[] = [];
+
+ export class DocData {
+ @observable _user_doc: Doc = undefined!;
+ @observable BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
}
- const manager = new DocBrush();
+ const manager = new DocData();
+ export function UserDoc(): Doc { return manager._user_doc; }
+ export function SetUserDoc(doc: Doc) { manager._user_doc = doc; }
export function IsBrushed(doc: Doc) {
- return manager.BrushedDoc.some(d => Doc.AreProtosEqual(d, doc));
+ return manager.BrushedDoc.has(doc) || manager.BrushedDoc.has(Doc.GetDataDoc(doc));
}
export function IsBrushedDegree(doc: Doc) {
- return manager.BrushedDoc.some(d => d === doc) ? 2 : Doc.IsBrushed(doc) ? 1 : 0;
+ return manager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 2 : manager.BrushedDoc.has(doc) ? 1 : 0;
}
export function BrushDoc(doc: Doc) {
- if (manager.BrushedDoc.indexOf(doc) === -1) runInAction(() => manager.BrushedDoc.push(doc));
+ manager.BrushedDoc.set(doc, true);
+ manager.BrushedDoc.set(Doc.GetDataDoc(doc), true);
}
export function UnBrushDoc(doc: Doc) {
- let index = manager.BrushedDoc.indexOf(doc);
- if (index !== -1) runInAction(() => manager.BrushedDoc.splice(index, 1));
+ manager.BrushedDoc.delete(doc);
+ manager.BrushedDoc.delete(Doc.GetDataDoc(doc));
}
}
-Scripting.addGlobal(function renameAlias(doc: any, n: any) {
- return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`;
-}); \ No newline at end of file
+Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`; });
+Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); \ No newline at end of file
diff --git a/src/new_fields/ListSpec.ts b/src/new_fields/ListSpec.ts
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/new_fields/ListSpec.ts
diff --git a/src/new_fields/PresField.ts b/src/new_fields/PresField.ts
new file mode 100644
index 000000000..f236a04fd
--- /dev/null
+++ b/src/new_fields/PresField.ts
@@ -0,0 +1,6 @@
+//insert code here
+import { ObjectField } from "./ObjectField";
+
+export abstract class PresField extends ObjectField {
+
+} \ No newline at end of file
diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts
index 23605cfb0..7494c9bd1 100644
--- a/src/new_fields/SchemaHeaderField.ts
+++ b/src/new_fields/SchemaHeaderField.ts
@@ -1,8 +1,8 @@
import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, createSimpleSchema, primitive } from "serializr";
+import { serializable, primitive } from "serializr";
import { ObjectField } from "./ObjectField";
import { Copy, ToScriptString, OnUpdate } from "./FieldSymbols";
-import { scriptingGlobal, Scripting } from "../client/util/Scripting";
+import { scriptingGlobal } from "../client/util/Scripting";
import { ColumnType } from "../client/views/collections/CollectionSchemaView";
export const PastelSchemaPalette = new Map<string, string>([
@@ -53,9 +53,11 @@ export class SchemaHeaderField extends ObjectField {
@serializable(primitive())
width: number;
@serializable(primitive())
+ collapsed: boolean | undefined;
+ @serializable(primitive())
desc: boolean | undefined; // boolean determines sort order, undefined when no sort
- constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean) {
+ constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) {
super();
this.heading = heading;
@@ -63,6 +65,7 @@ export class SchemaHeaderField extends ObjectField {
this.type = type ? type : 0;
this.width = width ? width : -1;
this.desc = desc;
+ this.collapsed = collapsed;
}
setHeading(heading: string) {
@@ -90,6 +93,11 @@ export class SchemaHeaderField extends ObjectField {
this[OnUpdate]();
}
+ setCollapsed(collapsed: boolean | undefined) {
+ this.collapsed = collapsed;
+ this[OnUpdate]();
+ }
+
[Copy]() {
return new SchemaHeaderField(this.heading, this.color, this.type);
}
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 09cbff25e..0ca35fab2 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -2,6 +2,7 @@ import { Field, Opt, FieldResult, Doc } from "./Doc";
import { List } from "./List";
import { RefField } from "./RefField";
import { DateField } from "./DateField";
+import { ScriptField } from "./ScriptField";
export type ToType<T extends InterfaceValue> =
T extends "string" ? string :
@@ -86,6 +87,9 @@ export function BoolCast(field: FieldResult, defaultVal: boolean | null = false)
export function DateCast(field: FieldResult) {
return Cast(field, DateField, null);
}
+export function ScriptCast(field: FieldResult) {
+ return Cast(field, ScriptField, null);
+}
type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index c546e2aac..04194509c 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -7,7 +7,6 @@ import { ObjectField } from "./ObjectField";
import { action } from "mobx";
import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
import { DocServer } from "../client/DocServer";
-import { CurrentUserUtils } from "../server/authentication/models/current_user_utils";
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
@@ -61,7 +60,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
}
const writeMode = DocServer.getFieldWriteMode(prop as string);
const fromServer = target[UpdatingFromServer];
- const sameAuthor = fromServer || (receiver.author === CurrentUserUtils.email);
+ const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail);
const writeToDoc = sameAuthor || (writeMode !== DocServer.WriteMode.LiveReadonly);
const writeToServer = sameAuthor || (writeMode === DocServer.WriteMode.Default);
if (writeToDoc) {
diff --git a/src/scraping/buxton/source/.Bill_Notes_NewO.docx.icloud b/src/scraping/buxton/source/.Bill_Notes_NewO.docx.icloud
new file mode 100644
index 000000000..f71886d8c
--- /dev/null
+++ b/src/scraping/buxton/source/.Bill_Notes_NewO.docx.icloud
Binary files differ
diff --git a/src/scraping/buxton/source/.Bill_Notes_OLPC.docx.icloud b/src/scraping/buxton/source/.Bill_Notes_OLPC.docx.icloud
new file mode 100644
index 000000000..30ddb3091
--- /dev/null
+++ b/src/scraping/buxton/source/.Bill_Notes_OLPC.docx.icloud
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_NewO.docx b/src/scraping/buxton/source/Bill_Notes_NewO.docx
deleted file mode 100644
index a514926d2..000000000
--- a/src/scraping/buxton/source/Bill_Notes_NewO.docx
+++ /dev/null
Binary files differ
diff --git a/src/scraping/buxton/source/Bill_Notes_OLPC.docx b/src/scraping/buxton/source/Bill_Notes_OLPC.docx
deleted file mode 100644
index 7a636e2d6..000000000
--- a/src/scraping/buxton/source/Bill_Notes_OLPC.docx
+++ /dev/null
Binary files differ
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 91d7ba87d..508655605 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -10,20 +10,17 @@ import { CollectionView } from "../../../client/views/collections/CollectionView
import { Doc } from "../../../new_fields/Doc";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
-import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
-import { RouteStore } from "../../RouteStore";
+import { Cast, StrCast } from "../../../new_fields/Types";
import { Utils } from "../../../Utils";
+import { RouteStore } from "../../RouteStore";
export class CurrentUserUtils {
- private static curr_email: string;
private static curr_id: string;
- @observable private static user_document: Doc;
//TODO tfs: these should be temporary...
private static mainDocId: string | undefined;
- public static get email() { return this.curr_email; }
public static get id() { return this.curr_id; }
- @computed public static get UserDocument() { return this.user_document; }
+ @computed public static get UserDocument() { return Doc.UserDoc(); }
public static get MainDocId() { return this.mainDocId; }
public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
@@ -32,7 +29,7 @@ export class CurrentUserUtils {
doc.viewType = CollectionViewType.Tree;
doc.dropAction = "alias";
doc.layout = CollectionView.LayoutString();
- doc.title = this.email;
+ doc.title = Doc.CurrentUserEmail
this.updateUserDocument(doc);
doc.data = new List<Doc>();
doc.gridGap = 5;
@@ -41,8 +38,6 @@ export class CurrentUserUtils {
doc.boxShadow = "0 0";
doc.excludeFromLibrary = true;
doc.optionalRightCollection = Docs.Create.StackingDocument([], { title: "New mobile uploads" });
- // doc.library = Docs.Create.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` });
- // (doc.library as Doc).excludeFromLibrary = true;
return doc;
}
@@ -60,6 +55,12 @@ export class CurrentUserUtils {
recentlyClosed.boxShadow = "0 0";
doc.recentlyClosed = recentlyClosed;
}
+ if (doc.curPresentation === undefined) {
+ const curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation" });
+ curPresentation.excludeFromLibrary = true;
+ curPresentation.boxShadow = "0 0";
+ doc.curPresentation = curPresentation;
+ }
if (doc.sidebar === undefined) {
const sidebar = Docs.Create.StackingDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Sidebar" });
sidebar.excludeFromLibrary = true;
@@ -87,15 +88,15 @@ export class CurrentUserUtils {
public static async loadUserDocument({ id, email }: { id: string, email: string }) {
this.curr_id = id;
- this.curr_email = email;
+ Doc.CurrentUserEmail = email;
await rp.get(Utils.prepend(RouteStore.getUserDocumentId)).then(id => {
if (id) {
return DocServer.GetRefField(id).then(async field => {
if (field instanceof Doc) {
await this.updateUserDocument(field);
- runInAction(() => this.user_document = field);
+ runInAction(() => Doc.SetUserDoc(field));
} else {
- runInAction(() => this.user_document = this.createUserDocument(id));
+ runInAction(() => Doc.SetUserDoc(this.createUserDocument(id)));
}
});
} else {
diff --git a/views/stylesheets/authentication.css b/views/stylesheets/authentication.css
index dea0474e4..36bb880af 100644
--- a/views/stylesheets/authentication.css
+++ b/views/stylesheets/authentication.css
@@ -69,10 +69,10 @@ body {
.workspaceId {
list-style-type: none;
- font-family: Arial, Helvetica, sans-serif;
+ font-family: Arial, Helvetica, sans-serif;
margin-left: -20px;
cursor: grab;
- padding-bottom: 15px;
+ padding-bottom: 15px;
}
.workspaceId:hover {
@@ -106,7 +106,7 @@ body {
}
.overlay {
- border: 2px solid yellow;
+ border: 2px gray;
text-align: center;
position: absolute;
margin: auto;